Skip to content

Neovim Cheatsheet

Overview

Neovim is a hyperextensible Vim-based text editor that aims to aggressively refactor Vim in order to simplify maintenance, enable advanced UIs, and maximize extensibility.

Installation

Package Managers

bash
# Ubuntu/Debian
sudo apt install neovim

# macOS
brew install neovim

# Arch Linux
sudo pacman -S neovim

# Windows (Chocolatey)
choco install neovim

# From source
git clone https://github.com/neovim/neovim.git
cd neovim && make CMAKE_BUILD_TYPE=RelWithDebInfo
sudo make install

Basic Differences from Vim

Key Improvements

vim
" Built-in terminal emulator
:terminal
:term

" Asynchronous job control
:help job-control

" Better default settings
" No need for 'nocompatible'
" Sensible defaults enabled

" Lua scripting support
:lua print("Hello from Lua!")

" Better plugin architecture
" Native LSP support
" TreeSitter integration

Configuration

Configuration Files

bash
# Neovim config location
~/.config/nvim/init.vim     # Vimscript config
~/.config/nvim/init.lua     # Lua config

# Check config location
:echo stdpath('config')

Basic init.vim

vim
" Enable syntax highlighting
syntax enable

" Line numbers
set number
set relativenumber

" Indentation
set tabstop=4
set shiftwidth=4
set expandtab
set autoindent
set smartindent

" Search
set ignorecase
set smartcase
set incsearch
set hlsearch

" UI improvements
set termguicolors
set signcolumn=yes
set updatetime=300
set timeoutlen=500

" Mouse support
set mouse=a

" Clipboard
set clipboard+=unnamedplus

" Split behavior
set splitbelow
set splitright

" Leader key
let mapleader = " "

Basic init.lua

lua
-- Basic settings
vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.tabstop = 4
vim.opt.shiftwidth = 4
vim.opt.expandtab = true
vim.opt.autoindent = true
vim.opt.smartindent = true
vim.opt.ignorecase = true
vim.opt.smartcase = true
vim.opt.incsearch = true
vim.opt.hlsearch = true
vim.opt.termguicolors = true
vim.opt.signcolumn = "yes"
vim.opt.updatetime = 300
vim.opt.timeoutlen = 500
vim.opt.mouse = "a"
vim.opt.clipboard = "unnamedplus"
vim.opt.splitbelow = true
vim.opt.splitright = true

-- Leader key
vim.g.mapleader = " "

-- Key mappings
vim.keymap.set('n', '<leader>w', ':w<CR>')
vim.keymap.set('n', '<leader>q', ':q<CR>')
vim.keymap.set('n', '<C-h>', '<C-w>h')
vim.keymap.set('n', '<C-j>', '<C-w>j')
vim.keymap.set('n', '<C-k>', '<C-w>k')
vim.keymap.set('n', '<C-l>', '<C-w>l')

Plugin Management

Using vim-plug

vim
" Install vim-plug
curl -fLo ~/.local/share/nvim/site/autoload/plug.vim --create-dirs \
    https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

" Plugin configuration in init.vim
call plug#begin('~/.local/share/nvim/plugged')

" Essential plugins
Plug 'nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'}
Plug 'neovim/nvim-lspconfig'
Plug 'hrsh7th/nvim-cmp'
Plug 'hrsh7th/cmp-nvim-lsp'
Plug 'L3MON4D3/LuaSnip'
Plug 'nvim-telescope/telescope.nvim'
Plug 'nvim-lua/plenary.nvim'
Plug 'kyazdani42/nvim-tree.lua'
Plug 'kyazdani42/nvim-web-devicons'
Plug 'nvim-lualine/lualine.nvim'

call plug#end()

" Install plugins: :PlugInstall
" Update plugins: :PlugUpdate
" Clean plugins: :PlugClean

Using packer.nvim (Lua)

lua
-- Install packer.nvim
local install_path = vim.fn.stdpath('data')..'/site/pack/packer/start/packer.nvim'
if vim.fn.empty(vim.fn.glob(install_path)) > 0 then
  vim.fn.system({'git', 'clone', '--depth', '1', 'https://github.com/wbthomason/packer.nvim', install_path})
end

-- Plugin configuration
require('packer').startup(function(use)
  use 'wbthomason/packer.nvim'
  
  -- Essential plugins
  use {'nvim-treesitter/nvim-treesitter', run = ':TSUpdate'}
  use 'neovim/nvim-lspconfig'
  use 'hrsh7th/nvim-cmp'
  use 'hrsh7th/cmp-nvim-lsp'
  use 'L3MON4D3/LuaSnip'
  use {'nvim-telescope/telescope.nvim', requires = 'nvim-lua/plenary.nvim'}
  use {'kyazdani42/nvim-tree.lua', requires = 'kyazdani42/nvim-web-devicons'}
  use 'nvim-lualine/lualine.nvim'
end)

Language Server Protocol (LSP)

LSP Setup

lua
-- Basic LSP configuration
local lspconfig = require('lspconfig')

-- Python
lspconfig.pyright.setup{}

-- JavaScript/TypeScript
lspconfig.tsserver.setup{}

-- Rust
lspconfig.rust_analyzer.setup{}

-- Go
lspconfig.gopls.setup{}

-- C/C++
lspconfig.clangd.setup{}

-- Lua
lspconfig.lua_ls.setup{
  settings = {
    Lua = {
      runtime = {version = 'LuaJIT'},
      diagnostics = {globals = {'vim'}},
      workspace = {library = vim.api.nvim_get_runtime_file("", true)},
      telemetry = {enable = false},
    },
  },
}

LSP Key Mappings

lua
-- LSP key mappings
vim.keymap.set('n', 'gd', vim.lsp.buf.definition)
vim.keymap.set('n', 'gD', vim.lsp.buf.declaration)
vim.keymap.set('n', 'gr', vim.lsp.buf.references)
vim.keymap.set('n', 'gi', vim.lsp.buf.implementation)
vim.keymap.set('n', 'K', vim.lsp.buf.hover)
vim.keymap.set('n', '<C-k>', vim.lsp.buf.signature_help)
vim.keymap.set('n', '<leader>rn', vim.lsp.buf.rename)
vim.keymap.set('n', '<leader>ca', vim.lsp.buf.code_action)
vim.keymap.set('n', '<leader>f', vim.lsp.buf.format)
vim.keymap.set('n', '[d', vim.diagnostic.goto_prev)
vim.keymap.set('n', ']d', vim.diagnostic.goto_next)
vim.keymap.set('n', '<leader>e', vim.diagnostic.open_float)
vim.keymap.set('n', '<leader>q', vim.diagnostic.setloclist)

TreeSitter

TreeSitter Configuration

lua
require'nvim-treesitter.configs'.setup {
  -- Install parsers synchronously (only applied to `ensure_installed`)
  sync_install = false,
  
  -- Automatically install missing parsers when entering buffer
  auto_install = true,
  
  -- List of parsers to install
  ensure_installed = {
    "c", "lua", "vim", "vimdoc", "query",
    "python", "javascript", "typescript", "rust", "go"
  },
  
  highlight = {
    enable = true,
    additional_vim_regex_highlighting = false,
  },
  
  indent = {
    enable = true
  },
  
  incremental_selection = {
    enable = true,
    keymaps = {
      init_selection = "gnn",
      node_incremental = "grn",
      scope_incremental = "grc",
      node_decremental = "grm",
    },
  },
}

Autocompletion

nvim-cmp Setup

lua
local cmp = require'cmp'
local luasnip = require'luasnip'

cmp.setup({
  snippet = {
    expand = function(args)
      luasnip.lsp_expand(args.body)
    end,
  },
  
  mapping = cmp.mapping.preset.insert({
    ['<C-b>'] = cmp.mapping.scroll_docs(-4),
    ['<C-f>'] = cmp.mapping.scroll_docs(4),
    ['<C-Space>'] = cmp.mapping.complete(),
    ['<C-e>'] = cmp.mapping.abort(),
    ['<CR>'] = cmp.mapping.confirm({ select = true }),
    ['<Tab>'] = cmp.mapping(function(fallback)
      if cmp.visible() then
        cmp.select_next_item()
      elseif luasnip.expand_or_jumpable() then
        luasnip.expand_or_jump()
      else
        fallback()
      end
    end, { 'i', 's' }),
  }),
  
  sources = cmp.config.sources({
    { name = 'nvim_lsp' },
    { name = 'luasnip' },
  }, {
    { name = 'buffer' },
  })
})

Terminal Integration

Built-in Terminal

vim
" Open terminal
:terminal
:term

" Terminal in split
:split | terminal
:vsplit | terminal

" Terminal key mappings
tnoremap <Esc> <C-\><C-n>
tnoremap <C-h> <C-\><C-n><C-w>h
tnoremap <C-j> <C-\><C-n><C-w>j
tnoremap <C-k> <C-\><C-n><C-w>k
tnoremap <C-l> <C-\><C-n><C-w>l

Terminal Lua Configuration

lua
-- Terminal configuration
vim.keymap.set('t', '<Esc>', '<C-\\><C-n>')
vim.keymap.set('t', '<C-h>', '<C-\\><C-n><C-w>h')
vim.keymap.set('t', '<C-j>', '<C-\\><C-n><C-w>j')
vim.keymap.set('t', '<C-k>', '<C-\\><C-n><C-w>k')
vim.keymap.set('t', '<C-l>', '<C-\\><C-n><C-w>l')

-- Open terminal in split
vim.keymap.set('n', '<leader>th', ':split | terminal<CR>')
vim.keymap.set('n', '<leader>tv', ':vsplit | terminal<CR>')

File Explorer

nvim-tree Setup

lua
require("nvim-tree").setup({
  sort_by = "case_sensitive",
  view = {
    width = 30,
  },
  renderer = {
    group_empty = true,
  },
  filters = {
    dotfiles = true,
  },
})

-- Key mappings
vim.keymap.set('n', '<leader>e', ':NvimTreeToggle<CR>')
vim.keymap.set('n', '<leader>r', ':NvimTreeRefresh<CR>')
vim.keymap.set('n', '<leader>n', ':NvimTreeFindFile<CR>')

Fuzzy Finder

Telescope Setup

lua
require('telescope').setup{
  defaults = {
    mappings = {
      i = {
        ["<C-n>"] = "move_selection_next",
        ["<C-p>"] = "move_selection_previous",
        ["<C-c>"] = "close",
        ["<Down>"] = "move_selection_next",
        ["<Up>"] = "move_selection_previous",
        ["<CR>"] = "select_default",
        ["<C-x>"] = "select_horizontal",
        ["<C-v>"] = "select_vertical",
        ["<C-t>"] = "select_tab",
      },
    },
  },
}

-- Key mappings
vim.keymap.set('n', '<leader>ff', '<cmd>Telescope find_files<cr>')
vim.keymap.set('n', '<leader>fg', '<cmd>Telescope live_grep<cr>')
vim.keymap.set('n', '<leader>fb', '<cmd>Telescope buffers<cr>')
vim.keymap.set('n', '<leader>fh', '<cmd>Telescope help_tags<cr>')

Status Line

Lualine Setup

lua
require('lualine').setup {
  options = {
    icons_enabled = true,
    theme = 'auto',
    component_separators = { left = '', right = ''},
    section_separators = { left = '', right = ''},
    disabled_filetypes = {
      statusline = {},
      winbar = {},
    },
    ignore_focus = {},
    always_divide_middle = true,
    globalstatus = false,
    refresh = {
      statusline = 1000,
      tabline = 1000,
      winbar = 1000,
    }
  },
  sections = {
    lualine_a = {'mode'},
    lualine_b = {'branch', 'diff', 'diagnostics'},
    lualine_c = {'filename'},
    lualine_x = {'encoding', 'fileformat', 'filetype'},
    lualine_y = {'progress'},
    lualine_z = {'location'}
  },
}

Advanced Features

Lua Scripting

lua
-- Create custom commands
vim.api.nvim_create_user_command('Hello', function()
  print('Hello from Neovim!')
end, {})

-- Create autocommands
vim.api.nvim_create_autocmd("BufWritePre", {
  pattern = "*.lua",
  callback = function()
    vim.lsp.buf.format()
  end,
})

-- Custom functions
local function toggle_number()
  if vim.opt.number:get() then
    vim.opt.number = false
    vim.opt.relativenumber = false
  else
    vim.opt.number = true
    vim.opt.relativenumber = true
  end
end

vim.keymap.set('n', '<leader>tn', toggle_number)

Health Check

vim
" Check Neovim health
:checkhealth

" Check specific provider
:checkhealth provider
:checkhealth nvim-treesitter
:checkhealth telescope

Migration from Vim

Key Differences

vim
" Neovim uses different paths
:echo stdpath('config')    " Config directory
:echo stdpath('data')      " Data directory
:echo stdpath('cache')     " Cache directory

" Better defaults
" No need for 'nocompatible'
" 'autoindent' is on by default
" 'autoread' is on by default
" 'background=dark' by default
" 'belloff=all' by default
" 'cscopeverbose' is on by default
" 'display=lastline' by default
" 'encoding=utf-8' by default

Compatibility

vim
" Most Vim plugins work with Neovim
" Vimscript is fully supported
" Can gradually migrate to Lua

" Check if running Neovim
if has('nvim')
  " Neovim-specific configuration
endif

Debugging

Built-in Debugging

vim
" Debug mode
nvim -D file.txt

" Verbose mode
nvim -V9 file.txt

" Check startup time
nvim --startuptime startup.log file.txt

" Profile startup
:profile start profile.log
:profile func *
:profile file *

Performance Tips

Optimization

lua
-- Disable unused providers
vim.g.loaded_python_provider = 0
vim.g.loaded_ruby_provider = 0
vim.g.loaded_perl_provider = 0
vim.g.loaded_node_provider = 0

-- Lazy load plugins
-- Use event-based loading
-- Minimize startup plugins

-- Optimize search
vim.opt.regexpengine = 1

-- Reduce updatetime for better experience
vim.opt.updatetime = 250

Troubleshooting

Common Issues

vim
" Plugin not working
:checkhealth
:PlugStatus

" LSP not working
:LspInfo
:LspLog

" TreeSitter issues
:TSInstallInfo
:TSUpdate

" Clear cache
rm -rf ~/.local/share/nvim
rm -rf ~/.cache/nvim

Resources