So Here's the thing. I had planned to write one blog a week, but due to some reasons, I couldn't do much research on the thing I wanted to write about. So instead of writing a half-assed blog about a topic I don't know much about, I am writing a blog on something that I do know some things about. And yes, that's Neovim. It's a text editor I have been using in the past, and have always come back to even after I thought I would never return to it at some point.
My Neovim Journey
Before going into the actual content, I would like to share my neovim journey first.
I first started using neovim three years back. I was in the second year of my college at the time and was learning linux. Due to some issues (both hardware and software), VS Code wasn't running properly and I was having to use Sublime text (which is a fantastic tool imo). However, I was just not content with using Sublime. Something felt wrong and so, I started searching for other editors. I also tried Brackets, Atom and Eclipse in the process and none of them suited my taste. That was when I discovered vim (yes vim, not neovim).
The baby steps
I started using vim and started learning the shortcuts and keybinds. Eventually, I also wrote a config file (init.vim.....Ah.....this brings back memories) and was pretty content with it. But something still felt lacking. I had started watching Primeagen and in one of his videos, I heard about neovim. I was curious, so I checked it out. I had heard that it's a fork of vim and has a lot of extra stuff (like native lsp support and all), but I wasn't too happy with it as it felt mostly same to me. However, all that changed when I discovered something new.
The world of linux ricing
As I mentioned earlier, I was learning linux and to no surprise, I came across linux ricing soon. At the time, I was using a ubuntu installation on my HP pavilion laptop, and I was pretty bored with the stock gnome look. I had tried to customize with some plugins and stuff, but I still didn't like it at the time. That was when I came across Distrotube channel and learnt about linux ricing. I started installing different window managers and bars and run launchers and started tinkering with config files. One of the first window managers I used was the awesome window manager. It's configured in lua, the same language used to configure neovim. That was when I learnt lua properly. However, I wasn't content with just my desktop looking good. I wanted my editor to match the theme and that was when I started using Lunar vim.
The first ever neovim distro
For people who don't know, Lunarvim is a distro for neovim. That is, it's a specific set of config made by @chrisatmachine. At the time, it was in it's very early stage and I got to see the exact configs without much abstractions. Being a nerd, I dug into it and within a month, figured out how things worked. Once that happened, I made my very first config file (custom one) and started daily driving it.
Making my own demon
We create our own demons
- Tony Stark
After I made my own config, I started daily driving it. However, I started to notice that my productivity had gone down instead of going up.
The reason was that, I was always updating my config. Everytime I stareted working, I would come across some issue and I would start tinkering with my config and I would spend a lot of time in the process. And this was not restricted to neovim. I would also make changes to my window manager config, my polybar config and rofi configs from time to time, but these were still rare. Neovim was something I used frequently, and I noticed a lot of issues with it (yeah I was a noob at the time, and still am xD) and started fixing them. This made me lose a lot of time in a day and it was starting to affect my productivity and work as well. So, I shifted to Nvchad.
The Chad config
Nvchad was the setup of my dreams. It looked so similar to VS Code, but was in vim and I loved it. I started daily driving it, and I used it for the longest ( I think 5-6 months). Again, being a nerd, I used to go through it's code and figure out how things worked and I would change some things here and there to see how it affected the setup. These 6 months, I learned the most about Neovim. I learnt about buffers, windows, tabs and the differences between those. I learnt how the keybindings actually worked (yeah earlier I mostly copy pasted, so I wasn't sure how things were actually working), I learned about neovim auto commands, and the list goes on.
There was another thing I leant from this, and that is actually what this blog is about. I learnt what I actually needed to have an usable setup.
Wrting a config from scratch
Yes, I made another config (I somehow managed to lose all the code for the config, though 😅) and this time, it helped increase my productivity by a lot. "But Samy, I thought you liked Nvchad. Then why did you bother writing your own config ?" is what you'd like to ask, and here's my answer:
- Nvchad was too much similar to vs code and was bloated with stuff that I didn't use. In fact, it reminded me of VS Code and for some reason, I wasn't able to focus on my work at one point (yeah that's kind of a lame reason)
- I think neovim is a personalized editor, and I would like something that actually catered to my needs (minimal, fast and reliable). So I went on and made my own custom config and I used it for the rest of the time I was using linux. Yes. I am on windows now, and I have another config at the moment.
Getting my very own laptop
"Wait I think you said you had your laptop!" is what careful readers would say. Yes, I had one, but I had stored up enough money to buy a gaming laptop (the former one being more of an office laptop, couldn't run games smoothly) and yes I like gaming. So I got myself a gaming laptop, and yeah I had to use windows in it because linux gaming sucked (it still does imo, but that's a conversation for later). I loved my new laptop but I hated windows and I cursed it for around 2-3 months as there were not many tools that I used to use which were available on windows. However, at some point, I realized that I could just make some if I wanted to use them. If you're interested, please let me know in the comments and I would love to share some stuff I did to get back my linux setup (or at least something close to it) on windows. I installed neovim and made my new setup.
Basic parts of a neovim config
Okay, that's enough of backstory. Let's get to the point now.
To have a basic setup, you need the following:
- A terminal and a neovim installation (duh)
- Some sensible settings (details below)
- Some sensible keybinds (details below)
- Treesitter
- A fuzzy finder (Telescope, for example)
- Your required plugins (this depends on the user, but I will share the ones I use below and some essential ones)
As you can see, I didn't mention a colorscheme and a file tree. This is because, I believe a file tree is not much important when you have a fuzzy finder, however, you can have one if you want (even I use one and I have included that under 6). So let's see how to configure each of these. I will discuss how I configured them and I will link documentations for each of them. I highly suggest reading the documentation for details. This blog will simply cover the surface so that it's simple to understand, however, to really understand how things work, reading the docs is a must.
A terminal and a neovim installation
This should be a no brainer. Neovim is a terminal text editor, and so, having a terminal is a must. There are some GUI apps to load neovim as well, and you are free to use them too. It's just that my experience with them have been a little lacking as sometimes, they tend to slow down a lot (In my case, it happened when I opened a lot of files and had a lot of plugins) or sometimes, they have certain configs diabled (for example, transparent background would sometimes be disabled for some reason, etc).
My choice of terminal for my current windows workflow is the OG Windows Terminal. I am daily driving my neovim in it for about 6 months now and I have yet to face any issue. Of course, I had to change some keybinds in it to make things work (For example, I disabled the Ctrl+V as it was conflicting with neovim's visual block mode).
To set up neovim in windows, first, install neovim. I used winget to do that:
$ winget install Neovim.Neovim
You can also use chocolatey or scoop or any other package manager you use. Once neovim is installed, you need to write your config in C:\Users\_username_\AppData\Local\nvim
folder. If the folder doesn't exist, you can create one. All your config will reside inside the init.lua
file within the folder. This is the folder structure I use:
├───lua
│ ├───config
│ │ ├───comments.lua
│ │ ├───context.lua
│ │ ├───git.lua
│ │ ├───harpoon.lua
│ │ ├───illuminate.lua
│ │ ├───init.lua
│ │ ├───lazy.lua
│ │ ├───lsp.lua
│ │ ├───lualine.lua
│ │ ├───telescope.lua
│ │ ├───treesitter.lua
│ │ ├───trouble.lua
│ │ └───undotree.lua
│ └───samy
│ │ ├───autocommands.lua
│ │ ├───git.lua
│ │ ├───init.lua
│ │ ├───keybinds.lua
│ │ └───settings.lua
└───undodir
Please ignore all the different files for now. As a general rule, this is what I follow:
Any plugin config goes into config/
.
Any of my own config goes into samy/
.
This helps me keep the code clean (it's not that clean, though 😅)
Some sensible settings
I shall divide this into some sub-sections to make things easier to understand.
Line numbers
There are three types for line numbers in neovim:
- No line numbers (default)
- Regular line numbers
- Relative line numbers I personally use a mixture of regular and relative line numbers. This is how I achieve it:
local opts = vim.opt
-- Line Numbers
opts.nu = true
opts.relativenumber = true
And this is how it looks:
Notice how the actual line number on the actual line is visible and the rest are numbered on the basis of the active line. This helps in navigating code within the file.
Let's say, I need to go to General settings portion. Instead of hitting j
multiple times, I can hit 13j
and go there directly. I know it's 13j
and not 25j
or 6j
because I can see the relative line number for the line is 13
!
Indentation
This should be straightforward. Indenting code is a part of our daily lives and let's be honest, indented code looks kinda sexy (Just kidding I don;t get turned on my code indentation). Personally, I like 4 space indenting as it tells me when I should made a modular function for the logic or when I am writing bad code (trust me, it's easier to spot bad code with a 4 space indenting). So these are the setting I use for specifying my indentation:
-- Indenting
opts.tabstop = 4
opts.softtabstop = 4
opts.shiftwidth = 4
opts.expandtab = true
opts.smartindent = true
Search and other general settings
Please note that everything I say under this section is a personal opinion. You might not agree with me, and that's fine, so feel free to change things here according to your taste.
I like my code wrapped, hence, this line:
-- General Settings
opts.wrap = false
I like to keep swapfile off. What this does is, it makes neovim to not store the buffers in case they are not closed properly. So, I add these lines:
opts.swapfile = false
opts.backup = false
The next two lines are related to a plugin called undotree, and I would recommend using this one (details further down):
opts.undodir = "C:\\Users\\samy3\\AppData\\Local\\nvim\\undodir"
opts.undofile = true
I like the current line to be highlighted and hence, I add this:
opts.cursorline = true
The following two lines configure highlights after search and incremental search:
-- Searching
opts.hlsearch = false
opts.incsearch = true
hlsearch = false
makes sure that the searches are not highlighted after the search is complete (this just looks weird, so I keep it off. Please try keeping it on and see for yourself if it looks good).
incsearch = true
ensures that the searches are highlighted while the search is happening. For example, when I hit /
and write sea
, all instances of sea
will be highlighted. This is not a default behavior and needs to be enabled (yeah this was a shock to me too).
Some sensible keybinds
I won't say much here. Keybinds and shortcuts are personal stuff, so I would simply share what I use and some tips to customize them.
These are what I use:
vim.g.mapleader = " "
local keymap = vim.keymap
-- keymap.set("n", "<leader>pv", vim.cmd.Ex)
-- VS Code style shifting
keymap.set("v", "J", ":m '>=1<CR>gv=gv")
keymap.set("v", "K", ":m '<-2<CR>gv=gv")
-- Keybindings to make life easy
keymap.set("i", "jj", "<Esc>")
keymap.set("n", "Y", "yg$")
keymap.set("n", "J", "mzJ`z")
keymap.set("n", "<C-d>", "<C-d>zz")
keymap.set("n", "<C-u>", "<C-u>zz")
keymap.set("n", "n", "nzzzv")
keymap.set("n", "N", "Nzzzv")
-- Copy paste Keybindings for various utilities
keymap.set("x", "<leader>p", "\"_dP")
keymap.set("n", "<leader>y", "\"+y")
keymap.set("v", "<leader>y", "\"+y")
keymap.set("n", "<leader>Y", "\"+Y")
keymap.set("n", "<leader>d", "\"_d")
keymap.set("v", "<leader>d", "\"_d")
-- More life enhancement Keybindings
keymap.set("n", "Q", "<nop>")
keymap.set("n", "<leader>f", function ()
vim.lsp.buf.format({ async = true })
end)
keymap.set("n", "<leader>x", "<cmd>!chmod +x %<CR>", { silent = true })
-- Error navigation
keymap.set("n", "<C-k>", "<cmd>cprev<CR>", { silent = true })
keymap.set("n", "<C-j>", "<cmd>cnext<CR>", { silent = true })
-- Buffer navigation
keymap.set("n", "<leader>bx", "<cmd>bdelete<CR>", { silent = true })
keymap.set("n", "<leader>bb", "<cmd>bnext<CR>", { silent = true })
keymap.set("n", "<leader>bB", "<cmd>bprev<CR>", { silent = true })
Disclaimer: Some of these are copied from Primeagen's config, so if you're a viewer and were wondering why they looked familiar, you know now.
Here's an explanation of what's going on:
-
vim.g.mapleader
sets the leader key. This is similar to a mod key, but for neovim. SUPER, META, ALT, CTRL and SHIFT are also available, but it's generally not a good idea to set these as leader keys. A lot of people use SPACE as leader key, and so do I. -
keymap.set
sets a keybind. To do so, all you need to do is specify the mode, the key (or skystorkes) you want to bind and the key or function or command you want to bind it to. We usen
for Normal Mode,i
for Insert mode,v
orx
for different visual modes andt
for terminal mode.
If you want to copy my keybinds, here's a summary of what you're going to have:
Mode | Key | Effect |
---|---|---|
Normal | Y | Yank till end of line |
Normal | J | Join the current and the next line and keep the cursor on the last line |
Normal | Ctrl + d | Scroll down half a page and keep the cursor at the center of the page |
Normal | Ctrl + u | Scroll up half a page and keep the cursor at the center of the page |
Normal | n | Go to next search result |
Normal | N | Go to previous search result |
Normal | Leader + y | Copy to clipboard |
Normal | Leader + y | Copy entire line to clipboard |
Normal | Leader + d | Delete and remove from yank buffer |
Normal | Leader + f | Format document using the active LSP |
Normal | Leader + x | Make current script executable (works only in linux) |
Normal | Ctrl + j | Go to next error |
Normal | Ctrl + k | Go to previous error |
Normal | Ctrl + bx | Close buffer |
Normal | Ctrl + bb | Go to next buffer |
Normal | Ctrl + bB | Go to previous buffer |
Insert1 | jj | Exit insert mode |
Visual | J | Move lines down |
Visual | K | Move lines up |
Visual | Leader + y | Copy selection to clipboard |
Visual | Leader + d | Delete selection and remove from yank buffer |
Visual | Leader + x | Paste without copying the selection |
Treesitter
Treesitter is what makes your neovim colorful. It uses the LSP and colorscheme and colors your variables, keywords and other stuff. To understand how treesitter works and configure treesitter please read the docs. Here is my configuration for treesitter:
require('nvim-treesitter.install').prefer_git = true
require 'nvim-treesitter.configs'.setup {
ensure_installed = { 'bash', 'c', 'diff', 'html', 'lua', 'luadoc', 'markdown', 'vim', 'vimdoc', 'javascript', 'rust', 'typescript', "comment" },
-- Autoinstall languages that are not installed
auto_install = true,
highlight = {
enable = true,
-- Some languages depend on vim's regex highlighting system (such as Ruby) for indent rules.
-- If you are experiencing weird indenting issues, add the language to
-- the list of additional_vim_regex_highlighting and disabled languages for indent.
additional_vim_regex_highlighting = { 'ruby' },
},
indent = { enable = true, disable = { 'ruby' } },
}
A fuzzy finder
I personally use Telescope as my fuzzy finder. Again, here's the docs for telescope and here's my config:
local builtin = require("telescope.builtin")
local keymap = vim.keymap
keymap.set("n", "<leader>pf", builtin.find_files, {})
keymap.set("n", "<leader>bl", builtin.buffers, {})
keymap.set("n", "<leader>pp", builtin.git_files, {})
keymap.set("n", "<leader>ps", function ()
builtin.grep_string({ search = vim.fn.input("Search: ") });
end)
Here's an explanation about what is going on:
Firstly, as soon as we install the telescope plugin and start it when our neovim session starts, we can call the find_files
, buffers
, git_files
, etc. anytime we want. There are some commands as well to trigger them and the docs will mention everything in detail. Here, we are triggering them using lua functions.
So builtin
contains all the functions and stuff within the telescope package and we simply call the individual functions (this is a higher level explanation for any library in neovim btw).
Now, here, we bind it to some keystokes:
Mode | Key | Effect |
---|---|---|
Normal | Leader + pf | Search within all the files (this includes files ignored by git, like node_modules) |
Normal | Leader + pp | Search within all the git files (this excludes files ignored by git, like node_modules) |
Normal | Leader + bl | List all the active buffers (this lets you navigate between opened buffers or files) |
Normal | Leader + ps | Project wide search for a specific term (Similar to what we have on VS Code) |
Now, you might be confused about two things:
- How do we even install this telescope or the treesitter mentioned above ?
- How are buffers and files related ?
The answer to the first is provided in the next section, where we discuss the required plugins, so please hold your horses.
The answer to the second one ? Let's understand that.
Bonus: Opening files (and folders) in neovim
For people who have experience with IDEs or even notepad, opening files is something simple. You click on a file (or double click in some cases) and it opens up in an editor for you to edit. However, as with everything else, neovim takes this a step ahead.
In neovim, there are these three concepts relating to opening files:
- Buffers
- Window and Panes
- Tabs
Let's get down to each of these at once, starting with windows. We will take an example of VS Code to make things easier to understand.
In VS Code, when you open a file, it opens in a tab. Think of the current VS Code session as the window (I'm 99.99% sure that is what people call it as well). The concept of window is the same in neovim. You start a session and a window appears. Now, if you've used split views in VS Code, you'd know that windows can be split between panes. What this does is, it opens the file again (side by side) in another tab.
In neovim, panes and windows work in a similar way. A window is a neovim window and a pane is a split of the window (vertical or horizontal). What is different, though is the content being shown (or at least how it's being shown).
You see, neovim opens the file only once and then just shows it to you side by side.
This is done with the help of Buffers. Neovim always opens a file or folder in a buffer and shows the buffer in separate panes. This way, less memory is used for doing the same task (This is why VS Code doesn't suit me anymore).
So let's make things clear. You hit enter on a file (or open it in some way) and neovim creates a buffer for the file and shows it in a window. You can split the window and create panes, but the same buffer will be shown instead of opening the file again. Tabs takes this one step further. A tab is like a superset for the window. When you open multiple tabs, you get multiple windows in the same neovim session. So you can have your different microservices open in different tabs on the same session and keep working without worrying about anything else.
Now that this is clear, let's see some essential plugins you'd need for your basic setup.
Your required plugins
The first thing you need is a plugin manager. This works in the same way any package manager works in an OS.
There are couple of popular ones, like Packer, Plug, Lazy, etc.
I use Lazy (because I am Lazy lol). It really lives up to it's name, and let's you install packages in the most lazy way. Simply mention the git repo of the package and it will take care of the rest. Of course, you need to write the configs, but there is no other hastle in the installation part at least. This also enabled lazy loading by default, which makes neovim load faster than is the packages were all loaded at once. Here is what you need to do to install Lazy:
-- Bootstrap lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
local lazyrepo = "https://github.com/folke/lazy.nvim.git"
local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
if vim.v.shell_error ~= 0 then
vim.api.nvim_echo({
{ "Failed to clone lazy.nvim:\n", "ErrorMsg" },
{ out, "WarningMsg" },
{ "\nPress any key to exit..." },
}, true, {})
vim.fn.getchar()
os.exit(1)
end
end
vim.opt.rtp:prepend(lazypath)
-- Make sure to setup `mapleader` and `maplocalleader` before
-- loading lazy.nvim so that mappings are correct.
-- This is also a good place to setup other settings (vim.opt)
vim.g.mapleader = " "
vim.g.maplocalleader = "\\"
-- Setup lazy.nvim
require("lazy").setup({
spec = {
-- All the plugins go here
},
-- Configure any other settings here. See the documentation for more details.
-- colorscheme that will be used when installing plugins.
install = { missing = true, colorscheme = { "catppuccin-mocha" } },
-- automatically check for plugin updates
checker = { enabled = true, notify = false },
}, {
ui = {
-- If you are using a Nerd Font: set icons to an empty table which will use the
-- default lazy.nvim defined Nerd Font icons, otherwise define a unicode icons table
icons = vim.g.have_nerd_font and {} or {
cmd = '⌘',
config = '🛠',
event = '📅',
ft = '📂',
init = '⚙',
keys = '🗝',
plugin = '🔌',
runtime = '💻',
require = '🌙',
source = '📄',
start = '🚀',
task = '📌',
lazy = '💤 ',
},
},
})
This is a boilerplate and you can get the same along with the docs from here.
To install a package, you need to provide the name in the spec
object and Lazy will take care of the rest. here are some examples for that:
- Using just a package name:
"nvim-lua/plenary.nvim",
- Using a name and
options
:
{
'nvim-telescope/telescope.nvim',
tag = '0.1.8',
dependencies = { 'nvim-lua/plenary.nvim' }
},
{
"nvim-treesitter/nvim-treesitter",
build = ":TSUpdate",
opts = {
ensure_installed = { 'bash', 'c', 'diff', 'html', 'lua', 'luadoc', 'markdown', 'vim', 'vimdoc', 'javascript', 'rust', 'typescript' },
-- Autoinstall languages that are not installed
auto_install = true,
highlight = {
enable = true,
-- Some languages depend on vim's regex highlighting system (such as Ruby) for indent rules.
-- If you are experiencing weird indenting issues, add the language to
-- the list of additional_vim_regex_highlighting and disabled languages for indent.
additional_vim_regex_highlighting = { 'ruby' },
},
indent = { enable = true, disable = { 'ruby' } },
},
},
As you can see, it's pretty simple to install packages and the same can be found on the docs for each package (well, at least mostly).
Alongside this, there are some other packages that any developer would like to have:
- Undotree
- Gitsigns
- LSP
- Comment
- File tree (optional)
- Colorscheme (optional)
I'll drop the docs and my configs for each of these here without much explanation as one good look at the docs will explain everything here. If something is difficult to understand, please feel free to put that up in the comments and I shall answer each of them:
Undotree
Docs: https://github.com/mbbill/undotree
Config:
vim.keymap.set("n", "<leader>u", vim.cmd.UndotreeToggle)
Gitsigns
Docs: https://github.com/lewis6991/gitsigns.nvim
Config:
[vim.keymap.set("n", "<leader>u", vim.cmd.UndotreeToggle)](<local neogit = require('neogit')
neogit.setup {}
require('gitsigns').setup {
signs = {
add = { text = '┃' },
change = { text = '┃' },
delete = { text = '_' },
topdelete = { text = '‾' },
changedelete = { text = '~' },
untracked = { text = '┆' },
},
signs_staged = {
add = { text = '┃' },
change = { text = '┃' },
delete = { text = '_' },
topdelete = { text = '‾' },
changedelete = { text = '~' },
untracked = { text = '┆' },
},
signs_staged_enable = true,
signcolumn = true, -- Toggle with `:Gitsigns toggle_signs`
numhl = false, -- Toggle with `:Gitsigns toggle_numhl`
linehl = false, -- Toggle with `:Gitsigns toggle_linehl`
word_diff = false, -- Toggle with `:Gitsigns toggle_word_diff`
watch_gitdir = {
follow_files = true
},
auto_attach = true,
attach_to_untracked = false,
current_line_blame = false, -- Toggle with `:Gitsigns toggle_current_line_blame`
current_line_blame_opts = {
virt_text = true,
virt_text_pos = 'eol', -- 'eol' | 'overlay' | 'right_align'
delay = 1000,
ignore_whitespace = false,
virt_text_priority = 100,
},
current_line_blame_formatter = '%3Cauthor%3E, <author_time:%R> - <summary>',
sign_priority = 6,
update_debounce = 100,
status_formatter = nil, -- Use default
max_file_length = 40000, -- Disable if file is longer than this (in lines)
preview_config = {
-- Options passed to nvim_open_win
border = 'single',
style = 'minimal',
relative = 'cursor',
row = 0,
col = 1
},
on_attach = function(bufnr)
local gitsigns = require('gitsigns')
local function map(mode, l, r, opts)
opts = opts or {}
opts.buffer = bufnr
vim.keymap.set(mode, l, r, opts)
end
-- Navigation
map('n', ']c', function()
if vim.wo.diff then
vim.cmd.normal({']c', bang = true})
else
gitsigns.nav_hunk('next')
end
end)
map('n', '[c', function()
if vim.wo.diff then
vim.cmd.normal({'[c', bang = true})
else
gitsigns.nav_hunk('prev')
end
end)
-- Actions
map('n', '<leader>hs', gitsigns.stage_hunk)
map('n', '<leader>hr', gitsigns.reset_hunk)
map('v', '<leader>hs', function() gitsigns.stage_hunk {vim.fn.line('.'), vim.fn.line('v')} end)
map('v', '<leader>hr', function() gitsigns.reset_hunk {vim.fn.line('.'), vim.fn.line('v')} end)
map('n', '<leader>hS', gitsigns.stage_buffer)
map('n', '<leader>hu', gitsigns.undo_stage_hunk)
map('n', '<leader>hR', gitsigns.reset_buffer)
map('n', '<leader>hp', gitsigns.preview_hunk)
map('n', '<leader>hb', function() gitsigns.blame_line{full=true} end)
map('n', '<leader>tb', gitsigns.toggle_current_line_blame)
map('n', '<leader>hd', gitsigns.diffthis)
map('n', '<leader>hD', function() gitsigns.diffthis('~') end)
map('n', '<leader>td', gitsigns.toggle_deleted)
-- Text object
map({'o', 'x'}, 'ih', ':<C-U>Gitsigns select_hunk<CR>')
end
}>
LSP
Docs: https://github.com/VonHeikemen/lsp-zero.nvim
Config:
local lsp_zero = require('lsp-zero')
local cmp_lsp = require("cmp_nvim_lsp")
lsp_zero.on_attach(function(client, bufnr)
-- see :help lsp-zero-keybindings
-- to learn the available actions
if client.name == "tsserver" then
client.server_capabilities.document_formatting = false
end
lsp_zero.default_keymaps({ buffer = bufnr })
end)
local capabilities = vim.tbl_deep_extend(
"force",
{},
vim.lsp.protocol.make_client_capabilities(),
cmp_lsp.default_capabilities()
)
-- to learn how to use mason.nvim
-- read this: https://github.com/VonHeikemen/lsp-zero.nvim/blob/v3.x/doc/md/guide/integrate-with-mason-nvim.md
require('mason').setup({})
require('mason-lspconfig').setup({
ensure_installed = {
"lua_ls",
"tsserver",
"eslint",
"rust_analyzer"
},
handlers = {
function(server_name)
local opts = {}
if server_name == "tsserver" then
opts.settings = {
implicitProjectConfiguration = {
checkJs = true
},
}
end
opts.capabilities = capabilities
require('lspconfig')[server_name].setup(opts)
end,
},
})
local cmp = require("cmp")
local luasnip = require('luasnip')
luasnip.config.setup {}
local cmp_mappings = cmp.mapping.preset.insert({
["<C-Space>"] = cmp.mapping.complete(),
["<C-k>"] = cmp.mapping.select_prev_item(),
["<C-j>"] = cmp.mapping.select_next_item(),
["<C-b>"] = cmp.mapping(cmp.mapping.scroll_docs(-1), { "i", "c" }),
["<C-f>"] = cmp.mapping(cmp.mapping.scroll_docs(1), { "i", "c" }),
["<C-y>"] = cmp.config.disable, -- Specify `cmp.config.disable` if you want to remove the default `<C-y>` mapping.
["<C-e>"] = cmp.mapping({
i = cmp.mapping.abort(),
c = cmp.mapping.close(),
}),
-- Accept currently selected item. If none selected, `select` first item.
-- Set `select` to `false` to only confirm explicitly selected items.
["<CR>"] = cmp.mapping.confirm({ select = true }),
["<Tab>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_next_item()
else
fallback()
end
end, { "i", "s" }),
["<S-Tab>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_prev_item()
else
fallback()
end
end, { "i", "s" }),
})
cmp.setup({
snippet = {
expand = function(args)
luasnip.lsp_expand(args.body)
end,
},
completion = { completeopt = 'menu,menuone,noinsert' },
mapping = cmp_mappings,
sources = {
{ name = 'nvim_lsp' },
{ name = 'luasnip' },
{ name = 'path' },
},
})
local status, prettier = pcall(require, "prettier")
if not status then
return
end
prettier.setup({
bin = "prettierd",
filetypes = {
"css",
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"json",
"scss",
"less",
},
})
Comment
This has two parts to it:
- Comments (To add comments) Docs: https://github.com/numToStr/Comment.nvim Config:
local status_ok, comment = pcall(require, "Comment")
if not status_ok then
return
end
comment.setup({
pre_hook = function(ctx)
local U = require("Comment.utils")
local location = nil
if ctx.ctype == U.ctype.block then
location = require("ts_context_commentstring.utils").get_cursor_location()
elseif ctx.cmotion == U.cmotion.v or ctx.cmotion == U.cmotion.V then
location = require("ts_context_commentstring.utils").get_visual_start_location()
end
return require("ts_context_commentstring.internal").calculate_commentstring({
key = ctx.ctype == U.ctype.line and "__default" or "__multiline",
location = location,
})
end,
})
- Treesitter Context (To give treesitter context of which language it is) Docs: https://github.com/nvim-treesitter/nvim-treesitter-context Config:
require 'treesitter-context'.setup {
enable = true, -- Enable this plugin (Can be enabled/disabled later via commands)
max_lines = 0, -- How many lines the window should span. Values <= 0 mean no limit.
min_window_height = 0, -- Minimum editor window height to enable context. Values <= 0 mean no limit.
line_numbers = true,
multiline_threshold = 20, -- Maximum number of lines to show for a single context
trim_scope = 'outer', -- Which context lines to discard if `max_lines` is exceeded. Choices: 'inner', 'outer'
mode = 'cursor', -- Line used to calculate context. Choices: 'cursor', 'topline'
-- Separator between context and content. Should be a single character string, like '-'.
-- When separator is set, the context will only show up when there are at least 2 lines above cursorline.
separator = nil,
zindex = 20, -- The Z-index of the context window
on_attach = nil, -- (fun(buf: integer): boolean) return false to disable attaching
}
File tree (optional)
This is a little subjective and varies for user to user. There are many good file tree plugins, some of the popular ones being nvim-tree
, neotree
, nerdtree
, etc.
personally, I hate file explorers (takes up a lot of space and shifts focus, and more importantly, reminds me of VS Code), so I use yazi.nvim
Docs: https://github.com/mikavilpas/yazi.nvim
Config:
This doesn't require any separate config. If you use yazi, the same config will be used here. Only the keybinds need to be configured while installing:
{
"mikavilpas/yazi.nvim",
event = "VeryLazy",
keys = {
-- 👇 in this section, choose your own keymappings!
{
"<leader>pv",
function()
require("yazi").yazi()
end,
desc = "Open the file manager",
},
{
-- Open in the current working directory
"<leader>cw",
function()
require("yazi").yazi(nil, vim.fn.getcwd())
end,
desc = "Open the file manager in nvim's working directory",
},
},
opts = {
-- if you want to open yazi instead of netrw, see below for more info
open_for_directories = false,
},
},
Colorscheme (optional)
This is again subjective, and depends on the user. I personally use catppuccin.
Docs: https://github.com/catppuccin/nvim
Config:
{
"catppuccin/nvim",
name = "catppuccin",
priority = 1000
},
Bonus: Some quality of life plugins
Here are some more plugins that I use personally:
- Harpoon
- Neogit
- TODO Domments
- Mini.nvim
- Noice.nvim
- Lualine
- Autopairs
- Indent Blankline
- nvim-ts-context-commentstring
- vim-css-color
- vim-closetag
- vim-illuminate
- goto-preview
- Trouble
- Codeium If you think these are a lot, then yes they are but this gives me the most basic neovim setup that I need. You can add more plugins and make it even more feature rich, and it's completely fine but I like my setup to be fast and reliable, so the fewer plugins I have, the less I need to worry about them going out of date xD
Conclusion
With that, we come to the end of this long blog. I hope this was informative (except foy my backstory) and you learnt somethigng from it. If you reached this far, then I'd like to thank you for your time and patience and I'd like it if you could drop a comment on anything related to the blog or my writing style or some detail that I covered or something I missed out on. I am looking to actively give back to this community that gave me so many resources to learn and upskill and I'd like to get some suggestions on that as well.
See you in the next blog this weekend.
Top comments (0)