I like the Emacs idea that everything is a buffer: files, directories, shells, search results. What I don’t like is that every Emacs mode comes with its own keys.
In Neovim I try to get the first part without the second, and keep the Vim keys working the same way everywhere.
Why buffers
A buffer is just text. Anything that’s a buffer already works with everything I know: moving around, searching, yanking, editing, undo.
Anything that isn’t a buffer is usually a new window with its own keys. Install ten plugins like that and you end up with ten small tools on one screen, each with its own rules. It looks powerful, but it’s harder to use, because what you learn in one of them doesn’t help in the next.
One question before adding a plugin
Does this give me a buffer, or a new window with its own keys?
The plugins I like most remove things I have to remember, and oil.nvim is the best example. It shows a directory as a buffer. To move a file, I dd it, p it somewhere else and save with :w. To rename one, I edit the name. I didn’t have to learn anything new.
Built-in features often win for the same reason. The built-in nvim.difftool puts the changed files of git difftool -d in the quickfix list, which I already know how to use. diffview.nvim does the same job in its own window, with its own keys, so I kept the built-in one.
Signs that something doesn’t fit
- Keys that only work in one window. You have to know where you are before you know what a key does.
- Things you can’t find again. They don’t show up in
:lsand you can’t reopen them later. - Results that don’t become text. You can’t search them, filter them or run them again.
The last one is easy to miss. A fuzzy picker usually forgets your search when it closes. :grep doesn’t, because it stays in the command history, and q: opens that history as a buffer. I go up to the last search, change a word and run it again.
I only care about this for searches. To get back to a file, :ls is enough.
The quickfix list ties it together
Most of my searches end up in the quickfix list, and from there everything works together:
| Tool | What it does |
|---|---|
:grep with ripgrep |
fills the list |
:Cfilter |
narrows the list |
:cdo / :cfdo |
runs a command on every result, or on every file |
:colder / :cnewer |
moves between older and newer lists |
nvim.difftool |
uses a quickfix list for git changes |
Since it’s always the same kind of list, the same commands work on all of it.
The same goes for mappings
Mappings pile up too, and it’s easy not to notice because they don’t show up anywhere. I ask a similar question: does this mapping save me from something, or is it one more thing to remember?
Two kinds are usually not worth it:
- A shortcut for a command that already works everywhere.
<Space>gfor:grepsaves four keys, but:grepworks in any Vim or Neovim, with no setup. - Rebuilding something another tool already does. I had ten lines of Lua to open a file next to the current one. With oil,
-already does that.
For opening files I ended up with three ways, depending on what I know:
| When | How |
|---|---|
| I don’t know the name, or it’s next to the current file | - (oil) |
| I know the name, anywhere in the project | :E, a small command of mine (fuzzy match on rg --files) |
| I know the full path | :e |
I used to have four mappings for this and now I have two: - for oil, and <Space>r, which starts :E. The other two weren’t saving me anything.
Where the rule doesn’t apply
- The completion menu is a popup in every editor, and that’s fine.
- A quick picker that opens and closes in two seconds is fine too, as long as I don’t need that search again.
My own plugin, pathpick.nvim, copies the current file’s path in different forms. It does open a small popup, but each line is the exact text it will copy, so yy, / and visual mode work like in any other buffer.
So my rule is narrower than “no popups”: anything I want to come back to should live in a buffer.
My config today
Neovim 0.12 with its built-in plugin manager, vim.pack, and three plugins: oil.nvim, pathpick.nvim and treesitter (which only improves highlighting and adds no window or keys). Everything else is built in or a few lines of my own config: the quickfix list, :Cfilter, nvim.difftool and fuzzy matching. Nothing was wrong with the plugins I removed; they just did things in their own window that a buffer could already do.
Why not just use Emacs?
I tried it. Emacs puts everything in a buffer, and many keys work everywhere, but each mode adds its own keys on top and those don’t carry over to the next mode.
Vim has three main modes (Normal, Insert and Visual), and they work the same everywhere. You combine an action with a target: ci" changes inside quotes, daw deletes a word, >ip indents a paragraph. That works in a code file, in q: and in oil. When I learn a new action, it works on everything I already know. In Emacs, learning a new command usually just gave me one new command.
There’s also a practical reason. I work in the terminal all day, in foot with tmux (I wrote about my setup), and Vim was made for the terminal.
Recommended book
Practical Vim, by Drew Neil, is an amazing book. It teaches you how to think about using Vim, which is what you need to really grok it.