Neovim Tips & Tricks


This chapter compiles many of the best tips and tricks that the Neovim community has developed over the years to turbo-charge your Neovim workflow.

Filed In:keymap

Reorganizing lines in a buffer is a very common task. While a yank and paste sequence could be used for each line, there is a better way. The following keymaps allow a line or group of lines to be easily moved up or down using a single keystroke: -- single-line vim.keymap.set("n","<A-j>",":move .+1<CR>==") vim.keymap.set("n","<A-k>",":move .-2<CR>==") -- multi-line vim.keymap.set("v","<A-j>",":move '>+1<CR>gv=gv") vim.keymap.set("v","<A-k>",":move '<-2<CR>gv=gv") To see how they work, consider the following buffer: Initial Conditions a = [ "one", "three", "two", "four", "five", ] NORMAL Top 1:1 Note that the words are out of order, and we would like to fix that. We start by moving the cursor to the line that we want to move (i.e. the "target" line): Move to Target Line2j a = [ "one", "three", "two", "four", "five", ] NORMAL 38% 3:1 With this keymap, hitting A-J allows us to easily move the currently down one position, swapping its position with the line below it: Move Current Line<A-j> a = [ "one", "two", "three", "four", "five", ] NORMAL 50% 4:1 To get a better idea about what happened, consider the command that executed when we hit A-J: :move .+1<CR>== The :move command has a call signature: :m[ove] {address} and moves the current line (or selected lines) to the line below the specified address. In this case, the specified address is .+1, which we learned in the ranges chapter means "the line below the current line". The carriage return causes the preceding command to execute, which is follows by == , which formats (indents) the line after it is moved. Let's see how this works with a visual selection . First, let's move the cursor so that the selection spans three lines: Select Multiple Linesv2j a = [ "one", "two", "three", "four", "five", ] VISUAL 75% 6:1 Note that although the selection only touches the third line, the entire line is included in the block of lines to move. Now, move these lines up: Move Multiple Lines<A-k> a = [ "one", "three", "four", "five", "two", ] VISUAL 62% 5:1 3 lines indented The selected lines have each move up one line, and the line containing "two" (which has previously been above the selection) is now located below the selection.
Filed In:keymap

Neovim provides many ways to navigate within documents, and this tip creates a simple keymap that makes it quick and easy to jump between markdown headings, which can be a big speed boost in longer markdown documents. Create a markdown directory under ftplugin if it doesn't already exist, then add a file containing the following keymaps : vim.keymap.set("n","<A-j>","/^#\\+ <CR>") vim.keymap.set("n","<A-k>","?^#\\+ <CR>") These keymaps take advantage of the search operators / and ? to search forward and backward from the current cursor position, respectively. In each case, Neovim searches for the pattern ^#\\+, which matches any line that starts with one or more # characters followed by a space. While this is a fairly naive pattern that may produce some false matches, it is very simple to implement and works quite well for most markdown documents. As a quick demo, let's take the following Markdown file. Starting from the top, in steps 1 and 2 we hit <A-j> to step forward through the headings, then finally in step 3 we use <A-k> to jump back up to the previous heading: Start Before # Heading 1 Text ## Heading 2 Text ### Heading 3 NORMAL Top 1:1 1 Jump to Next Heading<A-j> # Heading 1 Text ## Heading 2 Text ### Heading 3 NORMAL 50% 5:1 2 Jump to Next Heading (Again)<A-j> # Heading 1 Text ## Heading 2 Text ### Heading 3 NORMAL 90% 9:1 3 Jump to Previous Heading<A-k> # Heading 1 Text ## Heading 2 Text ### Heading 3 NORMAL 50% 5:1 This keymap also supports counts , allowing you to navigate even faster by jumping multiple headings at a time. If you would like to see another way to implement the same function but using Treesitter, take a look at this tip.
Filed In:keymap

Vim's built-in J command can be used to join the current line with the next line, which can be a very handy tool for keeping text "clean" when editing. Let's take a quick look at the default behavior. Starting from the following buffer , execute J : Before Before Default Line one Line two Line three NORMAL Top 1:6 After After DefaultJ Line one Line two Line three NORMAL Top 1:9 Note that the second line has merged with the first, but the cursor jumped after joining the lines, which is a bit disorienting. We would prefer to join lines without having the cursor move, so in this tip we will develop a simple keymap that achieved this. For comparison, here is a quick demo of the buffer before and after the improved keymap: Before Before Improved Line one Line two Line three NORMAL Top 1:6 After After Improved<A-j> Line one Line two Line three NORMAL Top 1:6 Let's set this up, step by step. Since the default J moves the cursor, our goal is to have the cursor return to the original location, which we can achieve by setting a mark to record the cursor position before we execute J . We can use any lower-case letter, let's use z: Set Markmz Line one Line two Line three NORMAL Top 1:6 Now that we have set the mark , execute the join by executing J : Join LinesJ Line one Line two Line three NORMAL Top 1:9 As we saw before, the cursor jumped. Now, we simply jump back to the mark we previously set using `z : Jump to Mark`z Line one Line two Line three NORMAL Top 1:6 Now that we have compiled the steps required to get the behavior we want, let's combine the steps into the following keymap: vim.keymap.set("n","<A-j>","mzJ`z") Note that for demonstration purposes this mapping sets the alternate keymap to A-J. If you prefer this functionality over the default, then you can also override the default keymap directly by using J instead of <A-J>.
Filed In:keymap

We previously looked at how to create a keymap that makes it quick and easy to navigate between Markdown headings. Although that tip was simple to create, there is potential for false-positives. In this tip we take a look at how to implement the same keymap with Treesitter so that we can more-accurately target Markdown headings. Create a markdown directory under ftplugin if it doesn't already exist, then add a file containing the following keymaps : localts_utils=require("nvim-treesitter.ts_utils") localM={ -- define the query query=vim.treesitter.query.parse("markdown","((atx_heading) @header)"), } M.init=function() -- search the current buffer M.buffer=0 -- references to lines within the buffer M.first_line=0 M.current_line=vim.fn.line(".") M.previous_line=M.current_line-1 M.next_line=M.current_line+1 M.last_line=-1 -- default count M.count=1 ifvim.v.count>1then M.count=vim.v.count end -- list of captures M.captures={} -- get the parser M.parser=vim.treesitter.get_parser() -- parse the tree M.tree=M.parser:parse()[1] -- get the root of the resulting tree M.root=M.tree:root() end M.next_heading=function() M.init() -- populate captures with all matching nodes from the next line to -- the last line of the buffer for_,node,_,_in M.query:iter_captures(M.root,M.buffer,M.next_line,M.last_line) do table.insert(M.captures,node) end -- get the node at the specified index ts_utils.goto_node(M.captures[M.count]) end M.previous_heading=function() M.init() -- if we are already at the top of the buffer -- there are no previous headings ifM.current_line==M.first_line+1then return end -- populate captures with all matching nodes from the first line -- of the buffer to the previous line for_,node,_,_in M.query:iter_captures(M.root,M.buffer,M.first_line,M.previous_line) do table.insert(M.captures,node) end -- get the node at the specified index ts_utils.goto_node(M.captures[#M.captures-M.count+1]) end -- define the keymaps vim.keymap.set("n","<A-j>",M.next_heading) vim.keymap.set("n","<A-k>",M.previous_heading) This is a bit more complicated, but still fairly simple. We start by creating a lua module, followed by an initialization function that collects information about the buffer , cursor , and count . Finally, separate functions are created to implement jumping in the forward and reverse directions, respectively. Finally, we create the keymaps . As a quick demo, let's follow the same steps we did in the previous tip. Starting from the top, in steps 1 and 2 we hit <A-j> to step forward through the headings, then finally in step 3 we use <A-k> to jump back up to the previous heading: Start Before # Heading 1 Text ## Heading 2 Text ### Heading 3 NORMAL Top 1:1 1 Jump to Next Heading<A-j> # Heading 1 Text ## Heading 2 Text ### Heading 3 NORMAL 50% 5:1 2 Jump to Next Heading (Again)<A-j> # Heading 1 Text ## Heading 2 Text ### Heading 3 NORMAL 90% 9:1 3 Jump to Previous Heading<A-k> # Heading 1 Text ## Heading 2 Text ### Heading 3 NORMAL 50% 5:1