Skip to main content
Theme

<- Back

Learning Neovim

Posted on 2024-10-25


I've been using Neovim as my primary editor for some time now and have grown quite adept at it. However, reaching this level of comfort required extensive research and learning. I thought it would be helpful to create a brief guide for those looking to transition to Neovim as their full-time editor. This guide assumes a basic understanding of Neovim. If you're entirely new to it, I recommend starting with the vimtutor command in the terminal to run through the introductory tutorial. Additionally, it's assumed you have a Language Server set up. If not, consider using Kickstart, or opt for an all-in-one configuration like LazyVim, NvChad, or AstroNvim.

I'll also recommend some plugins. While some users prefer to avoid motion-related plugins to maintain proficiency with Vim on remote machines, I find this concern less relevant for my needs. I rarely use Vim remotely, preferring to optimize my local Neovim experience instead. This preference extends to online coding platforms with Vim bindings, such as Leetcode or CodeSignal; however, I do not frequent these enough to justify limiting my use of motion plugins.

Navigating within a file

For navigating within a file, I typically use h, j, k, l if my target is nearby, or H, M, L (High, Middle, Low) for quick jumps to the top, middle, or bottom of the file. Additionally, I rely on the great plugin folke/flash.nvim for more precise navigation. Activated by pressing s or /, you simply type the first letter or two of your destination. The plugin then displays a unique character key next to each match. Pressing the corresponding key transports your cursor directly to the desired location. This is somewhat akin to the ggandor/leap.nvim plugin, but I find that flash.nvim offers several additional features that make it my preferred choice.

Navigating between files

I highly recommend nvim-telescope/telescope.nvim for this. This will enable features like VS Code's Ctrl/Cmd-P file search and the project-wide text search. These are my keybindings for it, inspired by the kickstart.nvim bindings:

-- Allows you to fuzzy search the current file
vim.keymap.set("n", "<leader>/", function()
    builtin.current_buffer_fuzzy_find(require("telescope.themes").get_dropdown({
        winblend = 10,
        previewer = false,
    }))
end, { desc = "[/] Fuzzily search in current buffer" })
-- Search files in current project. Similar to VS Code's Ctrl/Cmd-P
vim.keymap.set("n", "<leader>sf", builtin.find_files, { desc = "[S]earch [F]iles" })
-- Search the project for occurrences of the word your cursor is on
vim.keymap.set("n", "<leader>sw", builtin.grep_string, { desc = "[S]earch current [W]ord" })
-- Similar to the project-wide text search in VS Code
vim.keymap.set("n", "<leader>sg", builtin.live_grep, { desc = "[S]earch by [G]rep" })
-- Lists all open buffers (files)
vim.keymap.set("n", "<leader>sb", builtin.buffers, { desc = "[S]earch [B]uffers" })
-- Searches through all available help docs
vim.keymap.set("n", "<leader>sh", builtin.help_tags, { desc = "[S]earch [H]elp" })
-- Searches through your key mappings
vim.keymap.set("n", "<leader>sk", builtin.keymaps, { desc = "[S]earch [K]eymaps" })
-- Search/list all symbls in the project (variables, functions, etc)
vim.keymap.set("n", "<leader>ss", builtin.lsp_document_symbols, { desc = "[S]ocument [S]ymbols" })
-- Search/list where the current variable/function/etc is used/declared
vim.keymap.set("n", "<leader>lr", builtin.lsp_references, { desc = "[L]ist LSP [R]eferences" })
-- Basically git log in Telescope
vim.keymap.set("n", "<leader>sc", builtin.git_commits, { desc = "[S]earch [C]ommits" })
-- Search Neovim config files
vim.keymap.set("n", "<leader>sn", function()
    builtin.find_files({ cwd = vim.fn.stdpath("config") })
end, { desc = "[S]earch [N]eovim config" })

Sometimes you'll want to take a quick look at what a function does, dive deeper into a type declaration, or something similar. For this, I use the vim.lsp.buf.definition function, which I have mapped to gd ([g]o to [d]efinition). When I want to inspect a function's details, I'll put my cursor on the function name and hit gd which opens the file where this function is declared and puts the cursor directly on it. When I'm done looking, I use Vim's jump list functionality to return to my previous file with Ctrl+o. Ctrl+i goes forward in the jump list, so if you go back, move your cursor off the function name and decide you want to take another look, Ctrl+i will bring you back to the function declaration file without having to move your cursor back to the function call and hitting gd.

Test block manipulation

There are several ways I tend to work with blocks of text or code. If there's a block of text you want to delete, you can use dap (Delete Around Paragraph) with your cursor anywhere in the block. If you want to operate on something more granular, you can swap out the p with whatever you're trying to target, for example { or ". Using a (Around) will operate on the target as well, while using i (In) will operate within the target.

// Original
function SomeFunc(arg) {
    const foo = {
        val1: "1",
        val2: "2",
    }
    return foo;
}
// Using di{ with your cursor in between the foo object braces
function SomeFunc(arg) {
    const foo = {
    }
    return foo;
}
// Using di{ with your cursor inside the function
function SomeFunc(arg) {
}

Of course, running the above commands with c instead of d will do the exact same, except put you in insert mode immediately. Or, if you use y instead of d, it will yank the target instead. While this method works, it's not very precise. If you have a lot of nesting going on, it can be hard to target exactly what you want to. This is where the flash plugin comes in handy again. When you trigger its treesitter selection mode, it will put a letter next to all valid treesitter targets, and as soon as you hit that key, it will select that: flash plugin example In this screenshot, if I hit d the if case gets selected, if I hit f, the whole for loop gets selected, if I hit g the case block gets selected, etc. This makes granular selection very easy.

Additional tips and tricks