Marcus Kazmierczak

Home mkaz.blog

Working With Vim

Neovim Configuration

Neovim uses ~/.config/nvim/init.lua for its configuration. Using Lua instead of vimscript gives you a real programming language to work with, making config easier to read and maintain.

I’m going to include several examples from my config to give you an idea of what you can do. I don’t recommend just copying and pasting mine or anyone’s config. Most config is just one person’s preferences, especially for shortcuts. The important part is to be able to read and understand what shortcuts and features they are implementing; and see if it applies to improving your workflow.

Your muscle memory may want settings or mappings to use a different set of keys; learn how it works and shape Neovim to fit you.

Options

In Lua, you set Neovim options using vim.opt. Here are some examples from my config:

vim.opt.tabstop     = 4
vim.opt.shiftwidth  = 4
vim.opt.expandtab   = true
vim.opt.wrap        = true
vim.opt.linebreak   = true
vim.opt.number      = true    -- show line numbers
vim.opt.showmode    = true    -- show insert/normal mode
vim.opt.scrolloff   = 2
vim.opt.backup      = false   -- no backups
vim.opt.mouse       = 'vi'    -- enable mouse support
vim.opt.visualbell  = false   -- shhhh
vim.opt.errorbells  = false   -- shhhh

For search behavior:

vim.opt.ignorecase = true
vim.opt.smartcase  = true

Setting ignorecase with smartcase means searches are case-insensitive unless you include a capital letter. This is a nice default.

You can also configure the characters shown when displaying whitespace:

vim.opt.listchars = 'tab:▸-,space:·,nbsp:␣,trail:•,eol:¶,precedes:«,extends:»'

Leader

The <Leader> is a special variable intended to be used for user definitions. Using a leader to prefix a command will help avoid conflicts with other default Vim commands, though this depends on what you set your leader variable to. By default the leader key is \ but it is common to map it to , which I do.

vim.g.mapleader = ','

Keymaps

You create keymaps in Lua using vim.api.nvim_set_keymap. I set up a shorthand and common option sets to keep things readable:

local opts = { noremap = true, silent = true }
local shh  = { silent = true }
local keymap = vim.api.nvim_set_keymap

The first argument is the mode: 'n' for normal, 'i' for insert, 'v' for visual and select, 'x' for just visual, 'c' for command-line, and 't' for terminal.

Setting noremap = true prevents recursive remapping, which can lead to errors if the right-hand side includes part of the left-hand side. It is a good best practice to use noremap by default. If the right-hand side relies on a mapping defined elsewhere, for example a plugin, leave out noremap (see the quote example below).

Setting silent = true suppresses the command from showing in the command line when executed.

Edit Config

A handy mapping to quickly open or reload your config:

keymap('n', '<Leader>re', ':edit $MYVIMRC<CR>', opts)
keymap('n', '<Leader>rs', ':source $MYVIMRC<CR>', opts)

Wrapped Line Navigation

By default, j and k move by actual lines, which skips over wrapped portions. Remapping to gj and gk moves by visual lines instead:

keymap('n', 'j', 'gj', opts)
keymap('n', 'k', 'gk', opts)

Add Semi-colon

A mapping for NORMAL mode to add a semi-colon to the end of the current line:

keymap('n', '<Leader>;', 'g_a;<Esc>', opts)

Here g_ moves to the last non-whitespace character on the line, a enters INSERT mode after the cursor, ; adds the semi-colon, and <Esc> returns to NORMAL mode. With this set, all I type is ,; to add a semi-colon at end of line.

Instant Quotes

A mapping to wrap a word in single or double quotes. This uses the vim-surround plugin, an example that needs to be recursive (no noremap):

keymap('n', "<Leader>'", "ysiw'", shh)
keymap('n', '<Leader>"', 'ysiw"', shh)

Clipboard Copy

Map <Leader>y to copy to the system clipboard instead of Vim’s internal register:

keymap('v', '<Leader>y',  '"+y', opts)
keymap('n', '<Leader>Y',  '"+yg_', opts)
keymap('n', '<Leader>y',  '"+y', opts)
keymap('n', '<Leader>yy', '"+yy', opts)

Bubble Lines

Move lines up and down using Alt+Arrow keys:

keymap('n', '<M-Up>',   ':m .-2<CR>', opts)
keymap('n', '<M-Down>', ':m  .+1<CR>', opts)

Buffer Navigation

Quick shortcuts for working with buffers:

keymap('n', '<Leader>n', ':enew<CR>',  opts)  -- new buffer
keymap('n', '<Tab>',     ':bnext<CR>', opts)  -- next buffer
keymap('n', '<S-Tab>',   ':bprev<CR>', opts)  -- previous buffer
keymap('n', '<Leader>3', ':b#<CR>',    opts)  -- recent buffer
keymap('n', '<Leader>a', ':only<CR>',  opts)  -- only buffer
keymap('n', 'Q',         ':bd!<CR>',   opts)  -- close buffer

Clear search highlighting with <Leader><Space>:

keymap('n', '<Leader><Space>', ':nohlsearch<CR>', opts)

Map F1 to Escape

Avoid accidental help screen when reaching for Escape:

keymap('i', '<F1>', '<ESC>', opts)
keymap('n', '<F1>', '<ESC>', opts)

Insert Date

Insert the current date with F5, works in both normal and insert mode:

keymap('n', '<F5>', 'i<C-R>=strftime("%b %e, %Y")<CR><Esc>', opts)
keymap('i', '<F5>', '<C-R>=strftime("%b %e, %Y")<CR>', opts)

Make me a Sandwich

My one great tip from my configuration, which I give credit to whoever I picked it up from years ago, this saves me so many times.

vim.cmd('ca w!! w !sudo tee >/dev/null "%"')

When you open a file and don’t have write permissions, you can call :w!! and it will auto sudo the file for you. Saves me practically every time I edit a system file. Sandwich reference

File Type Settings

Neovim supports per-filetype configuration using files in ~/.config/nvim/ftplugin/. Create a file named after the filetype (e.g., go.lua, php.lua) and Neovim will load it automatically when that filetype is detected. This is cleaner than autocmds for filetype-specific settings.

Using vim.cmd

Some things are still easier to express in vimscript. You can use vim.cmd() to run any ex command from Lua:

vim.cmd('colorscheme opencode')

This is useful for commands that don’t have a clean Lua equivalent, or for using command aliases like the sudo write trick above.