Why should I learn nvim?

As a professional, you are going to spend a lot of time on your computer, especially editing code. You want do it in the most fun and efficient way. nvim is fun: fun to learn, fun to write with, fun to improve at and most importantly - it is very very efficient.

You can use vim with any keyboard

vim gives you the option to use only the basic keyboard keys: the alphabetical keys, ctrl, alt, shift, escape, enter.
You don't need to find where is end, pageup/down and home each time you switch a keyboard/laptop.

If you can type on a keyboard, you can use vim the way you're used to.

Keep your hands on the keyboard

vim can be used without a mouse at all, and it's even recommended to use it this way, as it will "force" you to stay in the "flow" while programming without needing to move your hand over to the mouse and back to the keyboard.

vim is designed to be used with type touching.
The important binds are on/next to the keys your hands should always be on: asdf jkl;.
It isn't a must to touch type (I don't touch type 100%) but I recommend trying to switch anyways.

Disclaimer

You can use a mouse.
I configured a mouse to allow "guests" to show me stuff in my workstation.
I use a mouse to read code, personally I find it more convenient.
I use a mouse when showing someone in my workstation so they can follow along.

The right way to program

It's a strong saying but I'm convinced that nvim is the right way to program.

When you're using vim, you "talk" to the editor rather than "move" it.
Imagine, instead of asking your friend to pour you a glass of water, you would give instructions to your friend on how to pour you a glass of water, which would look like something like this:

  1. Open the bottle
  2. Grab the glass
  3. Grab the bottle
  4. Make an angle with the bottle towards the glass
  5. Put down the bottle
  6. Close the bottle

Wouldn't it be easier to just say "Hey, can you pour me glass of water?" Well, that how it feels to use other editors after you're using vim, vim lets you "talk" to the editor.

Feel "at home" everywhere

Most TUIs programs support vim binds which will make you feel fast in any program that you usually use, less, man, tmux and many more.

You can feel "at home" in browsers too, with the vimium extension

Edit files through ssh with ease

Lightweight and Native

nvim is lightweight, starts up fast, renders fast and a lot of the plugins tend to use a strong native binary to run the heavyweight tasks.
For example, to search for text across the project telescope.nvim runs ripgrep.

Rich plugin ecosystem

nvim has a huge community that develops plugins for everything you can and can't imagine.
nvim doesn't miss on any IDE feature that other IDEs have to offer.
nvim plugins are the first to "evolve" when a new tech is coming to the IDE world, the nvim community is the first to adapt it, e.g: ripgrep

You can take a look at the awesome nvim plugin list. I recommend to "watch" to get updates about new plugins.

Be comfortable

nvim is basically a framework for an editor, it can't do much without plugins but it gives the user the ability to configure the editor in a way they're most comfortable with.

Imagine sitting on a chair you built exactly to your size. The back rest covers all of your back, the head rest is exactly in the right height and has the perfect shape to support your head and neck. The arm rests match exactly the height of your desk, and the chair is at the perfect height for your legs.
This is how I feel when I code in nvim, I sit in a chair I build exactly for my needs, and I can keep improve the way I code because I can improve the way my editor works.


How should I start use nvim?

nvim has a steep learning curve - it takes time and patience to master, you will find that you need to invest time so you can start using nvim as your daily driver.

If you are a professional programmer I recommend to start using it in your personal projects and integrate it slowly to your professional workflow. I did it by having my previous editor (Sublime Text) open while nvim was open too.

I started by trying to do simple actions with nvim and moving to sublime when I needed to do complicated stuff that I struggled to do with nvim. After I got comfortable with basic functionality, I started trying to do more complex actions with nvim, sometimes I managed to yet sometimes I also failed and went back to sublime. Eventually I felt more comfortable with nvim and stuck with it all day long.

The bottom line is that it'll take time and effort but eventually it will be worth it, for me it sparked the old feeling of learning how to edit your code, and I still feel it every day.


Neovim Requires Time

Neovim requires a lot of time learning additional to the learning curve you will have to make bunch of configurations other than the nvim config itself, terminal, nerdfont, plugins, etc, it might be not for you.

If you are 100% dedicated to learn and use neovim. I strongly recommend to start backing up your work environment config with some kind of dotfiles framework. I use dotbot.

The vim Language

Buffers, Splits/Windows and Tabs

  • Buffers - buffer is an open file, which can be edited from multiple splits
  • Splits/Windows - splits aka windows are the panes which you can move through
  • Tabs - tab is a set of splits, like a tab in a terminal, people don't use it very often.
Binds
  • :e <file> - will open a new file (buffer)
  • :b <buffer> - will switch to an already opened file (buffer)
  • :vsplit - will open a vertical split
  • :split - will open a horizontal split
  • use :help tab-page to read more about tabs (they're not very necessary)

Keybinds

Often you'll see a keybind described as something like that: ggyG - which copies the entire buffer.
All the signs in the keybind are meant to be pressed as you type them.
To execute this bind you'll need to press:

  • gg - move to the top of the file
  • y - start a copy
  • shift+g - end the copy from the cursor to the end of the file

In vim you don't hold the keys in order to complete a key sequence as you type them, and you can configure the timeoutlen to make vim wait more/less between keystrokes.

Keybinds naming

You don't need to memorize keybinds as the letter stands for its action.

  • y - yank
  • p - paste

Each time where I introduce an action in this guide, I'll mark the letter I use to remember the bind.

Special Keys

  • <cmd> - is : which starts a cmd
  • <cr> - is enter
  • <Esc> - is Escape
  • <C-x> - is Ctrl+x
  • <M-x> - is alt+x
  • <M-X> - is alt+shift+x
  • <A-x> - is alt+x
  • <A-X> - is alt+shift+x
  • <leader> - is the leader key

You can type :help <key> to open a help split for that key, the key can be anything, it can be G and it can be <cr>

Leader

vim maps most of the keyboard by default, <leader> acts as a prefix for custom user binds.
Leader is remappable, the default mapping is , most vim users change it to <Space>

Help

There's a help page for everything, start using it as soon as possible, it's like man just better and for vim.

Modes

There are a lot of modes in vim, I'll cover the important ones.
To exit from each mode back to normal press Escape. I recommend remapping it to capslock, you're going to press it a lot and it will be much easier to use the pinky without moving your hands.

  • Normal - This is where you will be usually, you can move, copy in this mode, and much more
  • Insert (i) - Insert text to the buffer, you want to be in this mode only when you're actually inserting text, you don't want to move in this mode
  • Visual (v) - Select and copy/replace text
  • Visual Line (V) - Select and copy/replace text by lines
  • Command (:,/) - Inserting a command

How to map Capslock to Escape

  • GNOME (Ubuntu) - Install gnome-tweak-tool, start Tweaks then Keyboard & Mouse -> Additional Layout Options -> Caps Lock behavior.
  • macOS
  • Windows - Install AutoHotKey and add Capslock::Esc to an ahk script.

Basic Configuration

One of nvim's strengths is that it's highly configurable, you can customize everything.
Setup your editor in a way that'll be nice for you to look at and it won't be filled with useless buttons/stuff.

I recommend to create your own configuration but it takes time and effort.
We will talk about the advantages at the advanced configuration chapter but I recommend to use preconfigured configuration for now, unless you are a config nerd like me.


Terminal Emulator Setup

nvim can be run with a GUI frontend or how it was intended to, in a terminal emulator.
I recommend to use one of the modern cross-platform terminal emulators:

  • Alacritty - recommended with tmux/i3 for splits and sessions (I use Alacritty + tmux)
  • Kitty - I never used it but people say it's nice
  • Wezterm - Lua configured terminal, I don't like the design, especially the way cursor behaves in it, but it seems the community love it.

Nerdfont

It's highly recommended to install a nerdfont to support icons in your terminal, many plugins utilize it.
I use CascadiaCode, JetBrainsMono is nice too.

Keybinds limitations

Because of ascii terminals don't support Ctrl+Shift+<X> keybindings, here's a great comment that explains why.
Some terminals allow to use a workaround, but it's not a must (I don't use Ctrl+Shift+X binds).

Options as Alt in macOS

To be able to use Alt binds in your terminal you need to configure your terminal emulator to do so.


How to Install nvim

I recommend to install the latest release, Nvim development is the nightly.
You can install nightly which is pretty stable and doesn't break often but you don't want to bother yourself with bugs as a beginner.


Preconfigured Configurations

There are several preconfigured configurations, these are the popular ones:

LunarVim

Personally I skipped using a preconfigured configuration, but I highly suggest to use it as a starting point. LunarVim is the easiest to start with, Installation link.

Make sure you read and edit the default configuration to your taste.
To open the configuration press Space, SHIFT+L, c.
To see the keybinds press Space, SHIFT+L, k.


How to Install Plugins

I'll recommend several plugins along the way, make sure you are not lazy to install them.

How to download a plugin

nvim plugins are git repositories, a package manager downloads and updates them, packer is the standard one and it's already installed in LunarVim.

In LunarVim's configuration you will find:

-- Additional Plugins
lvim.plugins = {
    {"folke/tokyonight.nvim"},
    {
      "folke/trouble.nvim",
      cmd = "TroubleToggle",
    },
}

lvim.plugins is passed to packer, folke/tokyonight.nvim is a short for github.com/folke/tokyonight.nvim.
Which means when a plugin tell you to install it by use { 'plugin_author/plugin_name', more_options }, just copy { 'plugin_author/plugin_name', more_options }.

Note: LunarVim runs :PackerInstall for you when the config changes.

Useful Packer Commands:

  • :PackerInstall - Install new added plugins.
  • :PackerStatus - List all installed pluigns.
  • :PackerSnapshot - Take a snapshot of your current plugins versions (useful before updating).
  • :PackerUpdate - Update all plugins.
  • :PackerClean - Remove unused plugins.

How to configure a plugin

Usually a plugin will provide a setup function which configures the plugin's behavior, most plugins won't activate if the setup function isn't called.

Usually the setup function receives a table, most plugins use the override standard - the keys in the table you pass will override the default, other keys keep their default value.
The defaults are usually in the plugin README and in the :help <plugin>

Note: I recommend adding the GitHub shorthand above the setup function as a comment, you can use open.nvim to access the plugin repo quickly.

How to install vim plugin

nvim supports vim plugins, the install of them is similar to nvim but the configuration is done by vim variables rather than setup function. The documentation of the plugin usually would be written in VimScript format as so:

let g:cool_plugin_variable = 1

The equivalent of that line in Lua is:

vim.g.cool_plugin_variable = 1

You can read more about vim variables at :help internal-variables.

Programming languages support

Some languages have plugins to support them specifically, usually the plugin takes care of the LSP setup (read below), so make sure you don't setup the LSP more than once. I recommend to skip the LSP setup of the plugin and set it up yourself or with mason.

auto-save.nvim

I hated using autosave in other text editors, but because vim has modes it knows exactly when a text was changed.
On InsertLeave which is triggered when you leave Insert mode, and on TextChanged which is triggered when text was changed in normal mode.

Autosave + autoformat = annoying behavior. Make sure you disable auto format, I wrote a simple plugin to format when I switch buffers.

How to disable autoformat in LunarVim:

lvim.format_on_save = false

Autosave + live config refresh = annoying behavior. Make sure you disable autosave in your config if you like live config refresh.
An example for how to do it in LunarVim:

local user_config_file = require("lvim.config"):get_user_config_path()
require("auto-save").setup {
  enabled = true,

  condition = function(buf)
    local fn = vim.fn
    local utils = require("auto-save.utils.data")

    if vim.api.nvim_buf_get_name(buf) == user_config_file then
      return false
    end

    if fn.getbufvar(buf, "&modifiable") == 1 and
        utils.not_in(fn.getbufvar(buf, "&filetype"), {}) then
      return true -- met condition(s), can save
    end
    return false -- can't save
  end,
}

Options

Options control the behavior of nvim, they can be set to boolean, number or string.
To set an option type :set <option>, to see the current value of an option use :set <option>?.
boolean options are enabled by :set <option> and disabled by :set no<option>.
E.g: :set relativenumber enables relativenumber and :set norelativenumber disables it.

Every option has a help tag for it, for example :h relativenumber
The options are listed in :h option-list.

In nvim you can access the options by vim.opt

Sensible Options

local opt = vim.opt

opt.number = true -- Enables line numbers
opt.relativenumber = true -- Enables relative line numbers
opt.autoindent = true -- Indent automatically
opt.cursorline = true -- Enables cursor line
opt.ignorecase = true -- Ignore case when searching
opt.splitright = true -- Split to the right on vertical
opt.splitbelow = true -- Split below when horizontal
opt.swapfile = false -- Don't use swap files (I use auto-save.nvim instead)
opt.wrap = true -- Wrap lines
opt.updatetime = 100 -- mainly for trld.nvim which utilize CursorHold autocmd
opt.formatoptions:append('cro') -- continue comments when going down a line, hit C-u to remove the added comment prefix
opt.sessionoptions:remove('options') -- don't save keymaps and local options
opt.foldlevelstart = 99 -- no auto folding

Key mapping

Key map can be set to specific modes.
The important ones:

  • n - Normal mode
  • i - Insert mode
  • x - Visual & Select mode
  • v - Visual mode
  • :h map-commands for the full mode list

To set a keymap you should use vim.keymap.set or even better a map function with default value for opts.

local function map(mode, lhs, rhs, desc, opts)
	opts = opts or { slient = true }
	opts.desc = desc
	vim.keymap.set(mode, lhs, rhs, opts)
end
  • mode - mode for which the keymap is added to, can be a single mode 'n', table of modes {'n', 'x'}, or empty '' for all modes.
  • lhs - The key you need to press to activate the key map
  • rhs - Can be a lua function to call or a string to let vim "type", for example nzz executes n and then zz which are binded to next and recenter.
  • desc - Description for the keymap.
  • opts - Table of options for the keymap, read more at :h map-arguments

Note: LunarVim keymaps are set different make to sure to read this too


Autocommands

An autocommand is a command that is executed automatically in response to some event, such as a file being read or written or a buffer change.

For example a good autocommand to highlight the text that was yanked.

-- Highlight on yank
vim.api.nvim_create_autocmd('TextYankPost', {
	pattern = '*',
	callback = function() vim.highlight.on_yank({timeout=350, higroup='Visual'}) end
})

Plugins can add custom autocommands too!
For all the builtin autocmds see :h autocmd-list.

Read :h nvim_create_autocmd for full explanation.


Shell Setup

Don't forget to set nvim as your default terminal editor like so:

export EDITOR='nvim'

Other good tools with vim binds

  • Vimium - Vim binds for your browser.
  • btop - Resource viewer that can be configured to use vim binds.

Plugin Tools

The preconfigured configuration handles the installation of LSP and Treesitter, so you don't need to worry about handling it, but it's recommended to familiarize yourself with both of these tools to understand how other plugins utilize them.

Backend Binaries

Some plugins utilize external binaries to make nvim fast, I recommend to instal them.

LSP

The idea behind the Language Server Protocol (LSP) is to standardize the protocol for how such servers and development tools communicate. This way, a single Language Server can be re-used in multiple development tools, which in turn can support multiple languages with minimal effort.

Which means nvim/sublime/vscode get the same Intellisense.
The servers run locally on your PC and are optimized to run fast.
nvim implements the fastest lsp client.

Server list by language

Treesitter

Tree-sitter is a parser generator tool and an incremental parsing library. It can build a concrete syntax tree for a source file and efficiently update the syntax tree as the source file is edited. Tree-sitter aims to be:

  • General enough to parse any programming language
  • Fast enough to parse on every keystroke in a text editor
  • Robust enough to provide useful results even in the presence of syntax errors
  • Dependency-free so that the runtime library (which is written in pure C) can be embedded in any application

Basically it provides a fast unified syntax query for different languages, this allows to build plugins that utilize the unified syntax query.

E.g: nvim-treesitter-textobjects which allows to do an action on a code context such as copy the function/class/argument/loop and more, we will elaborate on that later.

Note: make sure you to install treesitter parser for your language by adding it to the ensure_installed table or run :TSUpdate {lang}.

Movements and Operators

vim is built on actions that are mostly built on 2 types, an operator and a movement.

Basic binds

  • i - enter Insert mode
  • a - enter insert mode After the cursor
  • I - enter Insert mode at the beginning of the line
  • A - enter insert mode at the end of the line (same as a but for the entire line)
  • o - insert new line below
  • O - insert new line above
  • u - Undo, check out undotree
  • Ctrl-r - Redo
  • zz - Recenter the screen, there are more binds for screen actions but this is the most important one

Basic movements

Arrows

  • h - left
  • j - down
  • k - up
  • l - right

It takes some time getting used to it but it's worth it, I recommend disabling the arrow keys for movement and try the game hjkl in vim-be-good.

-- Disable arrows movement
map('', '<Up>', '<Nop>')
map('', '<Down>', '<Nop>')
map('', '<Left>', '<Nop>')
map('', '<Right>', '<Nop>')

Multiply Movement

You can multiply every movement by entering a number before the movement.
For example: 3j will jump 3 lines down

Words

  • w - jump Word forward
  • b - jump word Backwards
  • e - jump forward to the End of the word
  • ge - jump to the End of the previous word

Capital will change the behavior from word to WORD, read :help word and :help WORD to understand the differences

Generics

  • 0 - go to the beginning of the line
  • $ - go to the end of the line
  • <C-u> - go Up half a page
  • <C-d> - go Down half a page
  • % - jump to the pair of the bracket/quote/ifdef your cursor on
  • <C-o> - jump to the previous position you jumped from (you can do it multiple times)
  • <C-i> - jump to the next position you jumped to (you can do it multiple times)

Pair Movments

The standard way to jump forward to something is ] and backward is [.

Some examples

  • ]m / [m - Jump to Method
  • ]] / [[ - Jump to section
  • } / { - Jump to paragraph
  • ]c / [c - Jump to diff (Change)

I suggest to install vim-unimpaired which adds more pair movements.


Searching

Search across the file

These are not movements!

  • / - to start a forward search
  • ? - to start a backward search
  • n - go to the Next occurrence
  • N - go to the previous occurrence
  • * - forward search the current word under cursor
  • # - backward search the current word under cursor

I recommend to remap n and N to nzz and Nzz, zz centers the screen by the line you are on, these binds will go to the next/prev occurrence and center the screen.

map('n', 'n', 'nzz') -- Auto recenter after n
map('n', 'N', 'Nzz') -- Auto recenter after N

If you enabled opt.ignorecase you can add \C at the end of your search to re-enable case.

These are movements! You will need to enter a char after the quick search key.

  • f - jump to the next char, I remember it as Find
  • F - jump to the previous char, I remember it as Find
  • t - jump Till the next char (1 char before the occurrence)
  • T - jump Till the prev char (1 char after the occurrence)

These movements are very useful to manipulate text in the same line. This is the reason I enable opt.wrap in vim.

I highly recommend installing clever-f.vim it will help you get used to these awesome movements fast.


Operators

The main ones

  • y - Yank (copy)
  • d - Delete
  • c - Change, delete the text and enters insert mode
  • < - remove indentation (left)
  • > - add indentation (right)
  • = - auto indent
  • gc - Comment/Uncomment, done with Comment.nvim

Capital (shift) will apply the operation starting from the cursor to the end of the line, e.g: D deletes the line from the cursor to the end of the line.
Repeating the operator key will apply the operation for the entire line, e.g: yy yanks the entire current line.


Action

Action is an operator + movement
For example:

  • y3k - yank 2 lines above the cursor, including the current line.
  • ct, - change the text until ,, very useful to change function arguments and more.

This is why you must have relativenumber on, it will make your life much easier.

"Special" actions

  • Hit the operator twice to activate it on the current line. yy will yank the entire line you are on.
  • Shift+operator to activate the operator from the cursor the end of the line. C will change the line from the cursor to the end.

One char actions

Actions without movement

  • x - delete the char you are on.
  • X - delete 1 char before the cursor.
  • r - Replace 1 char

How should I remember all those binds?

Think of a key sequence as an English sentence.

For example: when I'm using y3j/y3<Enter> I'll think of it as yank 3 down, in my mind I "speak" with the editor rather than remembering which keys to press.


There are many more movements and operators but these are the ones I feel is the most important, it takes some time getting used to work with this method, but once you understand it, it'll stick well.

Copy, Paste and Visual Mode

The default behavior of vim doesn't copy to your OS clipboard, but instead copies to a register, which can also be the OS clipboard.

Copy and paste with specifying registers

To yank (copy) a text, you can copy the text from NORMAL mode with the y operator combined with a movement ,or you can enter visual mode with v and yank the text with y.

A text is copied to the registers when you change and delete text too.

To paste the yanked result you can use:

  • p - paste after the cursor
  • P - paste before the cursor
  • <M-p> (Alt+p) - paste in insert mode

Visual Modes

  • v - Normal Visual mode
  • V - Visual Line mode, selects text by lines
  • <C-v> - Visual Block mode, selects text by block

You can replace text by pasting new text after selecting the text to replace with visual, this action copies the replaced text to a register too.

Registers

To specify a destination for the copy use ".
For example to copy to register a I'll hit these keys: "ay<movement>".

Same for pasting: "ap.

Use _ - as the black hole register, when you want to delete a text without transferring it to a register.

There are many things you can do with registers, I suggest to briefly read :help registers.

Binds to delete/change/replace without yanking.

map('x', '<leader>p', '"_dP', 'Replace without yanking')

map('n', '<leader>d', '"_d', 'Delete without yanking') -- e.g <leader>dd deletes the current line without yanking it
map('n', '<leader>D', '"_D', 'Delete until EOL without yanking')

map('n', '<leader>c', '"_c', 'Change without yanking')
map('n', '<leader>C', '"_C', 'Change until EOL without yanking')

Using the OS clipboard

+ is the OS clipboard register, you can yank to it and you can paste from it.

I use these binds to use the OS clipboard without too many key presses:

map('', '<leader>y', '"+y', 'Yank to clipboard') -- E.g: <leader>yy will yank current line to os clipboard
map('', '<leader>Y', '"+y$', 'Yank until EOL to clipboard')

map('n', '<leader>p', '"+p', 'Paste after cursor from clipboard')
map('n', '<leader>P', '"+P', 'Paste before cursor from clipboard')

Plugins

Put/Yank Improvements

  • yanky.nvim to cycle fast in my copy history after pasting instead of using the registers.
  • peekup - dynamically interact with vim registers.
  • registers.nvim to see the registers before yanking from/to them.

Text Objects

Text objects are what made vim superior from other editors for me by any means.

Text objects are "movements" that only work after an operator.
A text object consists of a start and an end.
The default and the most used ones are inner/a + X.

Modifiers:

  • i - as Inner.
  • a - as A.

For example:

  • yiw - yank inner word, will yank the current word.
  • ciw - change inner word, will change the current word.
  • daw - delete a word, will delete the current word and the space before it, removes a word from a sentence.
  • ci' - change inner ', will change the inner text inside the next/current pair of ' aka a string.
  • ca( - delete a (, will delete the text inside and the ( of the next/current pair of (.

You can do these actions for every pair, (/{/'/".
You can do these actions for html tags(t), paragraphs(p), and more.

You can repeat the action when entering a number before, e.g: 3daw deletes the current word and the next 2.

Feel free to enter visual mode and test how it works.

You can read more about this at :help text-objects.


One of my favorite text objects is the paragraph, it makes editing code feel so natural.

If you want to change the order of boo and goo, you can you delete the goo paragraph with dap, go back a paragraph with { and paste the deleted paragraph with p.

def foo():
	result = boo()
	if result is not None:
		return result
	
	result = goo() # <--- cursor here
	if result is not None:
		return result
	
	return None

Treesitter Text Objects

nvim-treesitter-textobjects is an awesome/must have plugin that creates text-objects from the treesitter query, which means text-objects for an actual part of your code!

For the config I use, f is a function, so if I want to change the current content of the function I am on, I hit cif, change inner function, and you can do this on other code objects.

It changes the way you think about editing code, with code object movements rather than how to move the cursor and to where.
For example, change the 3rd argument in the next function.

def foo(a: int, b: Any, c: Tuple[int, Optional[str]]):
	pass # <--- cursor here

You can use [m to move up to the function signature, then ]a 3 times (unfortunately you can't do 3]a to do it yet), then you can press cia to keep the , but change the content of the argument or you can press cad to delete the entire argument.

In a standard editor you would need to go the signature manually or using reverse search, hold ctrl and hit the arrows until you reach c, hold ctrl+shift and hitting right arrow 9 times (depends on the editor, tested on sublime), that's tedious.

It changed the way I think about editing code, it allows me to think with elements from the code, go to next/prev function, yank inner function, delete a function, change inner argument, rather thinking how to move the cursor which relates to the code elements.


Config

My config (adds pair movement as well):

textobjects = {
	move = {
		enable = true,
		set_jumps = true, -- whether to set jumps in the jumplist
		goto_next_start = {
			["]m"] = "@function.outer",
			["gj"] = "@function.outer",
			["]]"] = "@class.outer",
			["]b"] = "@block.outer",
			["]a"] = "@parameter.inner",
		},
		goto_next_end = {
			["]M"] = "@function.outer",
			["gJ"] = "@function.outer",
			["]["] = "@class.outer",
			["]B"] = "@block.outer",
			["]A"] = "@parameter.inner",
		},
		goto_previous_start = {
			["[m"] = "@function.outer",
			["gk"] = "@function.outer",
			["[["] = "@class.outer",
			["[b"] = "@block.outer",
			["[a"] = "@parameter.inner",
		},
		goto_previous_end = {
			["[M"] = "@function.outer",
			["gK"] = "@function.outer",
			["[]"] = "@class.outer",
			["[B"] = "@block.outer",
			["[A"] = "@parameter.inner",
		},
	},
	select = {
		enable = true,
		lookahead = true,
		keymaps = {
			["af"] = "@function.outer",
			["if"] = "@function.inner",
			["ac"] = "@class.outer",
			["ic"] = "@class.inner",
			["ab"] = "@block.outer",
			["ib"] = "@block.inner",
			["al"] = "@loop.outer",
			["il"] = "@loop.inner",
			["a/"] = "@comment.outer",
			["i/"] = "@comment.outer", -- no inner for comment
			["aa"] = "@parameter.outer", -- parameter -> argument
			["ia"] = "@parameter.inner",
		},
	},
},

For LunarVim:

lvim.builtin.treesitter.textobjects.select = {
	enable = true,
	lookahead = true,
	keymaps = {
		["af"] = "@function.outer",
		["if"] = "@function.inner",
		["ac"] = "@class.outer",
		["ic"] = "@class.inner",
		["ab"] = "@block.outer",
		["ib"] = "@block.inner",
		["al"] = "@loop.outer",
		["il"] = "@loop.inner",
		["a/"] = "@comment.outer",
		["i/"] = "@comment.outer", -- no inner for comment
		["aa"] = "@parameter.outer", -- parameter -> argument
		["ia"] = "@parameter.inner",
	},
}
lvim.builtin.treesitter.textobjects.move = {
	enable = true,
	set_jumps = true, -- whether to set jumps in the jumplist
	goto_next_start = {
		["]m"] = "@function.outer",
		["gj"] = "@function.outer",
		["]]"] = "@class.outer",
		["]b"] = "@block.outer",
		["]a"] = "@parameter.inner",
	},
	goto_next_end = {
		["]M"] = "@function.outer",
		["gJ"] = "@function.outer",
		["]["] = "@class.outer",
		["]B"] = "@block.outer",
		["]A"] = "@parameter.inner",
	},
	goto_previous_start = {
		["[m"] = "@function.outer",
		["gk"] = "@function.outer",
		["[["] = "@class.outer",
		["[b"] = "@block.outer",
		["[a"] = "@parameter.inner",
	},
	goto_previous_end = {
		["[M"] = "@function.outer",
		["gK"] = "@function.outer",
		["[]"] = "@class.outer",
		["[B"] = "@block.outer",
		["[A"] = "@parameter.inner",
	},
}

Splits and Actual Tabs

Splits aka Windows

We already covered what splits are at Chapter 1, but we didn't cover how to control them.

  • :vsplit/:vs - Creates a vertical split.
  • :split - Creates a horizontal split.
  • :q - Close split.
  • <C-w> + h/j/k/l - Move split to the direction of the arrow.
  • <C-w>= - Equally resize all the splits.
  • <C-w>> - Increase width.
  • <C-w>< - Decrease width.
  • <C-w>+ - Increase height.
  • <C-w>- - Decrease height.

Configure

I recommend to map a quick way to move, create and close splits. (If you are using preconfigured configuration some binds might be set already)

My mappings:

map({'n', 't'}, '<C-h>', '<C-w>h')
map({'n', 't'}, '<C-j>', '<C-w>j')
map({'n', 't'}, '<C-k>', '<C-w>k')
map({'n', 't'}, '<C-l>', '<C-w>l')

map('n', '<M-e>', '<cmd>vsplit<cr>')
map('n', '<M-o>', '<cmd>split<cr>')

map('n', '<M-q>', '<cmd>q<cr>')

The default direction of splits in vim are out of today's standards, to fix it add this to your config:

vim.opt.splitright = true
vim.opt.splitbelow = true

Plugins

If you are using tmux I highly recommend to use Navigator.nvim to move in/out of vim to/from tmux panes seamlessly.


Actual Tabs

To achieve actual tabs (like in any other IDE) you need to use a bufferline/tabline plugin.

Your preconfigured configuration probably has one already but you can see the list here, I use bufferline.nvim.

Make sure to map keys to quickly switch, cycle and close tabs.

My config:

-- Tabline binds
map('n', '<C-q>', function() require('bufdelete').bufdelete(0, true) end) -- shift+Quit to close current tab
map('n', 'g1', function() require('bufferline').go_to_buffer(1, true) end)
map('n', 'g2', function() require('bufferline').go_to_buffer(2, true) end)
map('n', 'g3', function() require('bufferline').go_to_buffer(3, true) end)
map('n', 'g4', function() require('bufferline').go_to_buffer(4, true) end)
map('n', 'g5', function() require('bufferline').go_to_buffer(5, true) end)
map('n', 'g6', function() require('bufferline').go_to_buffer(6, true) end)
map('n', 'g7', function() require('bufferline').go_to_buffer(7, true) end)
map('n', 'g8', function() require('bufferline').go_to_buffer(8, true) end)
map('n', 'g9', function() require('bufferline').go_to_buffer(9, true) end)
map('n', 'g0', function() require('bufferline').go_to_buffer(10, true) end)
map('n', '<M-j>', '<cmd>BufferLineCyclePrev<CR>') -- Alt+j to move to left
map('n', '<M-k>', '<cmd>BufferLineCycleNext<CR>') -- Alt+k to move to right
map('n', '<M-J>', '<cmd>BufferLineMovePrev<CR>') -- Alt+Shift+j grab to with you to left
map('n', '<M-K>', '<cmd>BufferLineMoveNext<CR>') -- Alt+Shift+k grab to with you to right

Vim Goodies

This chapter will briefly cover few of many other vim features.

Quickfix list

The quickfix list is a list that has entries of file, location(row, col) and text.
There are many ways to populate the quickfix list, personally I populate it with :help make and telescope.nvim which we will cover later.

It's mainly used to store search results and compilation errors.

Commands

  • :copen - open the quickfix list.
  • :cclose - close the quickfix list.
  • :cnext - jump to the next entry in the qf list.
  • :cprev - jump to the prev entry in the qf list.

I recommend to install vim-unimpaired that adds ]q and [q to jump to the next/prev entry.

Substitute Command

The s command is used for search & replace in the current buffer you are editing.
The syntax resembles the sed cli tool.

The command is very powerful, you can learn more about it in :help :substitute or just google it.

Few examples:

  • :s/ofir/gal/ - find the first occurrence of ofir in the current line and replace it with gal
  • :s/ofir/gal/g - find all occurrences of ofir in the current line and replace them with gal
  • :%s/ofir/gal/ - find the first occurrence of ofir in each line of the current buffer and replace them with gal
  • :%s/ofir/gal/g - same as above but for all occurrences

Recommended keymap to rename the word under the cursor

-- My <F2> is binded to lsp rename
map('n', '<leader><F2>', '*:%s///g<left><left>') -- Rename current word with <leader>F2
map('x', '<F2>', '"hy:%s/<C-r>h//g<left><left>') -- Rename selected text in visual

Plugins

With text-case.nvim you can replace text and preserve the casing of the text.

When executing :Subs/ofir gal/amit tamari on this text:

OfirGal
ofirGal

It results this text:

AmitTamari
amitTamari

Also it provides lua API to change case, I created these user commands to change casing fast.

local api = vim.api
local textcase = require('textcase')
textcase.setup {
}

api.nvim_create_user_command('UpperCase', function() textcase.current_word('to_upper_case') end, {})
api.nvim_create_user_command('LowerCase', function() textcase.current_word('to_lower_case') end, {})
api.nvim_create_user_command('SnakeCase', function() textcase.current_word('to_snake_case') end, {})
api.nvim_create_user_command('ConstantCase', function() textcase.current_word('to_dash_case') end, {})
api.nvim_create_user_command('DashCase', function() textcase.current_word('to_constant_case') end, {})
api.nvim_create_user_command('DotCase', function() textcase.current_word('to_dot_case') end, {})
api.nvim_create_user_command('CamelCase', function() textcase.current_word('to_camel_case') end, {})
api.nvim_create_user_command('PascalCase', function() textcase.current_word('to_pascal_case') end, {})
api.nvim_create_user_command('TitleCase', function() textcase.current_word('to_title_case') end, {})
api.nvim_create_user_command('PathCase', function() textcase.current_word('to_path_case') end, {})
api.nvim_create_user_command('PhraseCase', function() textcase.current_word('to_phrase_case') end, {})

Global command

The g command is used to apply a "vim" command across the buffer by filtering lines with pattern.

For example:

  • :g/ofir/d - will delete all the lines with ofir

The command is very powerful, you can learn more about it in :help :global or just google search it.

Macros

You can record and replay macros (set of key presses) to apply similar changes fast.

To start recording press q{a-z}, you will see in the bottom left recording @{a-z} to stop recording press q again, this will save the macro to {a-z} so you can use it later.
To replay the macro press @{a-z}.

Record the macro once and use it multiple times on multiple lines.

Usage Example

I have a list of days of the week that I need to store inside a list, all the days need to be in lowercase.
I have a code with enum of days but I need to migrate it to python

enum Days {
	Sunday = 0,
	Monday = 1,
	Tuesday = 2,
	Wednesday = 3,
	Thursday = 4,
	Friday = 5,
	Saturday = 6,
};

Wanted result:

days = [
	'sunday',
	'monday',
	'tuesday',
	'wednesday',
	'thursday',
	'friday',
	'saturday',
]

First I'll grab all days from inside the enum to my python file, but I need to make it a list with only the day names.
I can record a macro that changes the casing of the first letter, deletes the number and adds ' to make the day string.

We are going to change the first day manually while recording the macro.

The macro: qa^guui'<Esc>ea'<Esc>ldt,jq.
Breakdown:

  1. qa - start a macro recording on a
  2. ^ - go to the start of the line
  3. guu - change the current char to lowercase
  4. i'<Esc> - enter insert mode, add ' and escape from insert mode
  5. e - go the the end of word
  6. a'<Esc> - enter insert mode after the word, add ' and escape from insert mode
  7. l - move right from the ' we just added
  8. dt, - delete the content until , (the = <num>)
  9. j - go down a line so we can repeat the macro
  10. q finish recording the macro.

After we recorded the macro on top of the first day we have 6 days left, we can press 6@a and that will repeat the macro 6 times and will change all the days.

Plugins

You can edit macros after recording them with vim-buffest

Dot to repeat

You can press . to repeat what you just did.
You can think of it like a macro that holds only the last action you did.

Usage Example

apple banana orange

I want to change all the fruits to fruit, but I realized it after I already changed apple to fruit by using ciwfruit<Esc> and I don't want to record a macro again.

After I change it I can just go to the next word (preferably with w) and press . to change the current word to fruit.

You can do cool stuff with when combining :help gn

External Command

You can run external terminal commands from vim, for example: !ls

Folds

vim supports folds, personally I never found this feature necessary, I set it to never fold automatically, but you can read more about folds here and make sure to check out nvim-ufo.

opt.foldlevelstart = 99 -- no auto folding

Advanced Config

You learned the basics of vim (and a little bit about nvim). From this chapter on I'll mostly talk about what takes nvim from being a good editor to a great editor, its plugin-rich ecosystem and configurability.

Personal Config vs Preconfigured Configuration

Preconfigured config

Preconfigured configurations are great, they provide a fast way to get a good config which is maintained by a community. But they add a "layer" of abstraction in the config, when installing new plugins that don't interact with builtin plugins everything works, but if the plugin has to interact with a builtin plugin it can become tricky. You have to read how your preconfigured configurations expose those options which sometimes confusing and inconvenient.

It's like trying to fix stuff in a rented house, you don't know exactly how everything works but you learn as you try, sometimes you just don't want to mess with that.

Personal config

When you build your own config, you build your own house, so you know exactly how the electricity works, how the plumbing works, and how everything is connected.

Your config = your codebase, configs of nvim are written in Lua, when you write your own config you write a mini project. You know each file, you know the "flow" of the config, you manage your own codebase from scratch rather than "diving" into the existing codebase of your preconfigured configuration.

Conclusion

I think that maintaining a personal config is the better option, it takes time and effort at first but it's worthwhile. Not everyone should write their own config but I suggest you try it.

Note: If you stick with a preconfigured configuration, some parts of the next chapters are irrelevant, I'll make sure to let you know you can skip them.


Why do I need to configure my editor using a programming language?

At first I didn't understand why do I need to learn and use Lua to configure my editor but soon enough I understood it's the right way to configure your editor.
It gives the ability to utilize your coding knowledge to improve your own experience in the editor.

A great basic example for that is my go to definition keybind.

goto_def = function()
	local ft = api.nvim_buf_get_option(0, 'filetype')
	if ft == 'man' then
		api.nvim_command(':Man ' .. vim.fn.expand('<cWORD>'))
	elseif ft == 'help' then
		api.nvim_command(':help ' .. vim.fn.expand('<cword>'))
	else
		require'telescope.builtin'.lsp_definitions()
	end
end

Which provides me a goto in man page, help page or just a regular code block, all in the same bind! When I'm browsing man I feel like I browse code.

In other editors it would be a value in a .json which a builtin/plugin function handles, so in order to achieve this kind of behavior I would have to write a plugin.


Basic Lua

Regardless of whether you use a preconfigured or a personal config, you need to learn a bit of Lua. Fortunately it's quite a simple language, I suggest you to learn it the same as you would with Python, for instance.

You can find Lua resources and the basic of Lua in nvim here. It's a long guide, I used it as a reference document to start with, afterwards I switched to the :help nvim_* method, which I'll cover later.

Lua modules, packages and tables

To understand how module and package work, you have to understand what's a table in Lua.

Lua table

Tables are the main (in fact, the only) data structuring mechanism in Lua, and a powerful one. We use tables to represent ordinary arrays, symbol tables, sets, records, queues, and other data structures, in a simple, uniform, and efficient way. As far as I know this is the reason Lua is so fast.

Table usage examples (click to expand)
local my_table = {
	ofir = "gal"
}

for key, value in pairs(my_table) do
	print("key: " .. key .. ", value: " .. value)
end
-- Output:
--   key: ofir, value: gal
print(my_table.ofir) -- Output: gal

-------------------------------

local my_array = { 5, 6, 7 }
for _, value in ipairs(my_array) do
	print(value)
end
-- Output:
--  5
--  6
--  7

-------------------------------

for index, value in ipairs(my_array) do
	print(index, value)
end
-- Output:
--  1       5
--  2       6
--  3       7

-------------------------------

local my_dict = {
	["ofir"] = "gal"
}

for key, value in pairs(my_dict) do
	print("key: " .. key .. ", value: " .. value)
end
-- Output:
--   key: ofir, value: gal
print(my_dict.ofir) -- Output: gal
print(my_dict["ofir"]) -- Output: gal
print(my_table["ofir"]) -- Output: gal

More examples here


Lua module

Module is a table of exported functions and vars.

Module example (click to expand)

module_example.lua:

local M = {} -- Initialize the table of the module

-- Example for private function
local function private_func(input)
	print("Foo: " .. input)
end

-- Example of exported function
M.public_func = function(input)
	private_func(input)
end

return M -- Return the exported functions table

Usage example:

require('module_example').public_func("ofir")

local mod = require('module_example')
mod.public_func("ofir")

Lua package

Lua package is a folder which has an init.lua which is a Lua module.

Package example (click to expand)

package_example/init.lua:

local M = {}
local submod = require('anothermodule')

M.boo = function()
	submod.goo("ofir gal")
end

M.goo = submod.goo -- Expose submod.goo through the package

return M

package_example/anothermodule.lua:

local M = {}

M.goo = function(input)
	print("submod.goo: ".. input)
end

return M

Usage example:

require('package_example').boo()
require('package_example').goo("amit tamari")
require('package_example.anothermodule').goo("direct call to submodule")
require('package_example/anothermodule').goo("another way to direct call to submodule")

How to create a personal config

Launch nvim in separate environments

The first thing you'll want to do is to start your config in a git repo, you can manage your config in a separate repo and use it as a git submodule in your dotfiles or make it a part of your dotfiles repo.

nvim loads its config from ~/.config/nvim by default, but we can set it to load from a different dir:

XDG_CONFIG_HOME=~/wip_config/ XDG_DATA_HOME=~/.local/share/wip_nvim XDG_STATE_HOME=~/.local/state/wip_nvim nvim

This line launches nvim, loads the config from ~/wip_config/nvim, saves the plugins in ~/.local/share/wip_nvim and the state at ~/.local/state/wip_nvim.

This creates a virtualenv-esque setup for nvim. I recommend to set an alias to launch your personal nvim (for example pnv) until your personal config is stable enough.

Folder structure

Your config is your codebase, so you have to maintain the order. One of the fundamentals for a clean config is the folder structure.

Find a structure which allows you to maintain a clean config that doesn't add overhead when editing/adding plugins.

Example of basic config folder structure

To understand how nvim loads the config I made a simple folder structure with notes:

📂 ~/wip_config/nvim
├── 🌑 init.lua # <---- Entry point
├── 📂 lua
│  ├── 🌑 module_example.lua # <---- Interpreted at `require('module_example')`
│  └── 📂 package_example
│     ├── 🌑 init.lua # <---- Interpreted at `require('package_example')`
│     └── 🌑 anothermodule.lua # <---- Interpreted at `require('package_example/anothermodule')`
├── 📂 vim  # <---- vimscript config files
└── 📁 pack # <---- `packer` compile dir, added it to .gitignore

This is my personal opinion for how to manage nvim config, feel free to scan through other dotfiles/nvim configs for inspiration.

📂 ~/wip_config/nvim
├── 🌑 init.lua # Entrypoint, requires lua/* modules by desired order
└── 📂 lua
   ├── 🌑 autocmds.lua    # Generic autocmds
   ├── 🌑 keymaps.lua     # Keymaps
   ├── 🌑 plugin_list.lua # Setup `packer`   (plugin list)
   ├── 🌑 settings.lua    # nvim settings    (vim.opt)
   ├── 🌑 ui.lua          # UI plugins setup (loaded before other plugins)
   ├── 🌑 usercmds.lua    # Generic usercmds
   ├── 🌑 utils.lua       # Utils functions  (can be a package too)
   └── 📂 plugins
      ├── 🌑 init.lua         # Loads all the submodules (plugins setup)
      ├── 🌑 autocomplete.lua # Autocomplete engine setup
      ├── 🌑 debug.lua        # Debug related plugins
      ├── 🌑 git.lua          # Git related plugins
      ├── 🌑 hydra.lua        # Hydras
      ├── 🌑 lsp.lua          # LSP configuration and related plugins
      ├── 🌑 misc.lua         # Miscellaneous plugins
      ├── 🌑 telescope.lua    # Telescope + extensions setup
      └── 🌑 treesitter.lua   # Treesitter + extensions setup

I suggest to recreate this folder structure and write the files while reading the guide.

Config Setup

Let's start with settings.lua, transfer your vim options (vim.opt.*) to settings.lua.
Now setup init.lua to require settings.lua like so:

require('settings')

Restart your nvim and check that your vim options are set.
Remember you can use print

After that I recommend to write keymaps.lua and autocmds.lua.

Installing the first plugin

Let's start with writing plugin_list.lua, in this file you manage your installed plugins.
There are several package managers, one that is well-known is packer.
TL;DR copy paste this into plugin_list.lua and add use '{github user}/{repo}' beneath the -- My plugins here comment.

If you reset your nvim :Packer commands won't exist, you must require it from init.lua like so:

require('plugin_list')

Make sure to execute :PackerInstall to install the added plugin.

After you installed your first plugin successfully it's time to set it up.

Create plugins/init.lua file and copy the code that requires all submodules from here and add require('plugins') in init.lua.

Now you can add each plugin setup to its corresponding file and restart nvim.


Lua usage in nvim & nvim lua api

nvim has an api for almost everything in lua, each lua function generates a help doc, which you can access by :help nvim_*.

For example, the docs for vim.api.nvim_buf_get_name can be accessed using :h nvim_buf_get_name

You can run lua from the command line, :lua print(vim.api.nvim_buf_get_name(0))

Important reference links from nanotee/nvim-lua-guide and :help tags:

UI

One of nvim's strengths is that it doesn't provide a real native UI, instead it provides a framework for creating a UI, this enables the community to develop different UI options through the use of various plugins.

If you are using a preconfigured configuration, probably the UI is already configured, but can be customized. Check your config's docs for more info.

Components

Colorscheme

Like in any code editor, there are a bunch of color schemes to choose from, I use my own scheme ofirkai.nvim. I recommend to choose a color scheme you are already familiar with.

Color schemes

Statusline and Winbar

Statusline is the bottom line right above the command line.
You can configure it to show whatever you like but usually it shows the current mode, file, file type and more.

You can set the status line to global or local.

  • global - one status line that shows information about the current active buffer.
  • local - status line for each window even if it's inactive, you can configure different designs for active and inactive.

Winbar is the same as statusline but for each window, it's located at the top of the window instead of the bottom, it's useful if you want information per window but a global status line too.

I personally use lualine.nvim, but I know a lot of people that prefer feline.nvim, you can find all statuslines here

How to set a global/local status line

vim.opt.laststatus = 3 -- Global line
vim.opt.laststatus = 2 -- The default, local

Check :h laststatus for all the options

Tabline

Tabline is the row of opened tabs, like in web browsers and other IDEs.

There isn't much to configure, but here are a couple of plugins that implement it:

File Explorer (File Tree)

There is a default file explorer for nvim but it's not widely used.

Recommended file tree plugins:

Code Navigation

LSP

To make nvim "smart" you need to install a LSP server and connect it to nvim's LSP client.

There are two options to do that:

  1. Using mason.nvim with mason-lspconfig.nvim and nvim-lspconfig, these 3 plugins will manage and install LSP servers, make sure you integrate it with your preconfigured configuration.
  2. Using nvim-lspconfig and manually maintain the LSP servers binaries (personally I do it with dotfiles), make sure you integrate it with your preconfigured configuration.

Telescope

Now we need a tool which can utilize the great capabilities that are provided with LSP.

One of them is telescope which is a framework for search, preview and pick.

Some of the features:

  • You can live-grep across the project with it, equivalent to CTRL+SHIFT+F in a "modern" IDE.
  • You can find a file, equivalent to CTRL+p in a "modern" IDE.
  • You can find all the definitions of a function with it (using LSP), equivalent to F12 in a "modern" IDE.
  • You can find all the references/implementations/type definitions with it (using LSP).
  • You can live grep a local/workspace symbols (functions), using LSP ofc.

Your preconfigured configuration probably has it installed and binded to keys already, make sure you know how to use it and configure it to your taste, it is very useful.

LSP Goodies

  • LSP Saga UI for LSP "hover" and rename and code actions (hints).
  • vim-illuminate highlight the symbol your cursor is on and let you jump to next/previous reference, very useful to know what "happened" to a variable.
  • LSP Signature shows the function signature while writing a function call.
  • fidget.nvim shows the LSP server progress in the bottom right corner.
  • lsp_lines.nvim shows the diagnostics in an intuitive way.
  • trld.nvim shows the current line diagnostic in the top right corner of the buffer.
  • nvim-navic shows the current context in the status/winbar line.
  • More plugins that integrate with LSP

Complete and Snippets Engines

If you are using a preconfigured configuration there is a high chance this is already configured and installed, you can skip this chapter.

Complete Engine

nvim doesn't provide a native completion menu, we need a menu with fuzzy finding, completion, scrolling and more.
For that a completion engine is for, the one that everybody use is nvim-cmp.
I recommend to start with the recommended setup and configure it more further.

Sources

Part of the configuration you pass sources this is where the complete menu get its entries from, you can extend the sources of your completions with other plugins.

The default ones are presented at the readme of nvim-cmp.
One of the sources you want to have is the complete engine source, I'll elaborate about that later.

Extra cool sources:

Formatting & Window

You can control how the menu is presented

Binds

You can go wild on this one, I recommend to stick with the default ones at first and think about that later.


Snippet Engine

Snippet engine has 2 parts, the part that translate snippet formats to actual snippet and the one that let you jump between the part of the snippet you need to complete.

There are multiple snippet engines personally I use nvim-snippy which requires to add cmp-snippy as a nvim-cmp source.

The snippet engine requires a snippets source too, I use a personal fork of vim-snippets to remove unwanted snippets.


My Complete and Snippet Engines Configuration

Git

As someone who used the git CLI for everything git has to offer I switched very quickly to use nvim for git management.
I'll cover some of the plugins I use to manage my git through nvim, I recommend to adapt some of them.


Fugitive

Provides the :Git/:G command which basically sends the command to the git CLI and provides an interactive window to work with.
E.g: :G show HEAD/:Git show HEAD

The "main" feature of it is the interactive status window which can be triggered with :G with no params which provide a convenient way to see the diffs, stage files and commit changes. nvim Screenshot


Gitsigns

Shows the current diff in a column next to the line numbers, lets you to stage/revert/preview/blame hunk's.


Git blame

Shows the current line blame in the status bar or in virtual text.


Git Tree (git log)

To see the git tree I use vim-flog which provides interactive git tree viewer.
You can hit enter on a commit to see the diff, you can perform interactive rebase on the commit when hitting ri and much more.
It so useful that I created ngh which is an alias to replace git hist, it opens a new nvim with a git tree. nvim Screenshot


Diffview

To see a big diff easily I use diffview.nvim. nvim Screenshot

I use it to see the history of a file too (<leader>gh in my keymap.lua). nvim Screenshot

I use it to solve conflicts too. nvim Screenshot


Hunk History

How many times did you tried git blame to understand why this code exist only to see the last commit just indent the code or made a minor change.
git-messenger.vim is comes to solve this by providing hunk history. nvim Screenshot

Debug

The same solution for LSP is exist for debugging, its called DAP, Debug Adapater Protocol.
To integrate it you need to install nvim-dap.

As any good nvim plugin nvim-dap is a framework and there is plugins that extends it.

Extensions:

Hydra

Hydra is a framework to create a submode inside nvim, a submode is called Hydra.

It allows to enter a body with a key bind or a lua command and from there it creates a custom keymapping "ontop" of your exist keys to allow you to do various actions.
You can create custom ones very easily too.

Hydras for example:

Example for a "custom" hydra of mine, which let met scroll through the functions in the buffer quickly:

local ts_move = require'nvim-treesitter.textobjects.move'
-- Move up/down functions
local curr = Hydra({
	hint = [[
 _j_ _J_ : up
 _k_ _K_ : down
  _<Esc>_
	]],
	config = {
		timeout = 4000,
		hint = {
			border = 'rounded'
		}
	},
	mode = {'n', 'x'},
	heads = {
		{ 'j', function ()
			ts_move.goto_next_start('@function.outer')
			center_screen()
		end },
		{ 'J', function ()
			ts_move.goto_next_end('@function.outer')
			center_screen()
		end },
		{ 'k', function ()
			ts_move.goto_previous_start('@function.outer')
			center_screen()
		end },
		{ 'K', function ()
			ts_move.goto_previous_end('@function.outer')
			center_screen()
		end },
		{ '<Esc>', nil,  { exit = true }}
	}
})
map({'n', 'x'}, 'gj', function ()
	ts_move.goto_next_start('@function.outer')
	center_screen()
	curr:activate()
end)
map('o', 'gj', function () ts_move.goto_next_start('@function.outer') end)

map({'n', 'x'}, 'gk', function ()
	ts_move.goto_previous_start('@function.outer')
	center_screen()
	curr:activate()
end)
map('o', 'gk', function () ts_move.goto_previous_start('@function.outer') end)

map({'n', 'x'}, 'gJ', function ()
	ts_move.goto_next_end('@function.outer')
	center_screen()
	curr:activate()
end)
map('o', 'gJ', function () ts_move.goto_next_end('@function.outer') end)

map({'n', 'x'}, 'gK', function ()
	ts_move.goto_previous_end('@function.outer')
	center_screen()
	curr:activate()
end)
map('o', 'gK', function () ts_move.goto_previous_end('@function.outer') end)

Misc

In this chapter I'll mention some misc plugins that I recommend to adapt.

Basic IDE Functionalities

nvim doesn't provide all the expected functionalities of an IDE, plugins are here to close that gap.

Session Manager

I use auto-session. Other Session Manager's

Comment

Adds a comment operation

Last Place

Remembers the last place you edited a buffer.

Guess Indent

Guess the indent of the file and override the indent settings.

Auto Pairs

Adds closures for your pairs, for example when you type ( it make it () and put the cursor in the middle.

Auto Save

Auto saves the file when a text has been edited, don't worry about losing your data.

Trailing whitespace

Be a good programmer and don't leave junk for git.

Multi cursor

If you are already good with multi cursor, take that skill with you to nvim, if you don't consider to use macros instead.

Terminal in nvim

Enables an integrated terminal inside nvim.

pasta

Fix's the issues with vanilla paste of nvim.

Extras

Surround

Surround parts of your code with []/()/{}, quickly

Undotree

Visualize your undo branches, never lose and redo/undo history again!

Unimpaired

Add pair mappings (][) that fills natural.

vim-repeat

Extends dot-repeat action.

Todo Comments

Highlight TODO comments, populate quickfix list with all the todo comments in a workspace

Debug print

Add debug prints for variables quickly.

leap.nvim

Add leap motion to nvim, like vimium, EasyMotion, hop and such.
If you liked clever-f you will love flit.nvim

vim-tmux-navigator

Make pane switching from/to nvim seamless, if you are using tmux its a must!

suda.vim

Write/read to/from files that requires sudo without executing nvim as sudo.

impatient.nvim

Improves startup time

Man pager

Consider make nvim your man pager, its so awesome to control man pages as you control your editor.
Add this to your .shellrc:

# nvim as man viewer
export MANPAGER='nvim +Man! .'
export MANWIDTH=999

More

There are many many more plugins for neovim, you can check out awesome-neovim, and you can also my list - packer.lua


Summary

Thanks for reading! I hope this guide helped you learn Neovim.
This guide doesn't cover everything, make sure you keep learning about your text editor and keep improving your skill.

If you found mistakes make sure to leave feedback at the issues section
If you liked the guide consider to leave a star.

To keep up with new plugins and updates I recommend to: