last updated: October 16, 2022
5 minute read
Five Interesting Things From my Neovim Config
Although I'm no expert in Neovim, I've found a few interesting solutions for the problems I've encountered when switching from VSCode to Neovim. For those new and old to Neovim, I hope I can provide some value as you experiment with your own config.
1. Absolute Imports in Lua
Importing another module into your Lua code can be a little confusing, to say the least. For example, if you search how to import a module from a parent directory, you might come across this stackoverflow post with the following accepted answer:
package.path = package.path .. ";../?.lua"
For context, Lua keeps a list of semicolon-separated directories to search when resolving a
require
, and stores it in a global variable called package.path
. To import a module from a
parent, the solution above suggests updating the package.path
to include the parent directory.
This works fine for importing directly from the parent directory, but you can imagine how poorly this scales.
As it turns out, Lua in Neovim helps us out by automatically adding its own runtime path (i.e.
~/.config/nvim/lua
) to the package.path
! This means that with a filetree like so:
.└── nvim/├── lua/│ ├── settings/│ │ └── barbar.lua│ └── shared/│ └── helpers.lua└── init.lua
We can import the shared helpers in helpers.lua
to our barbar.lua
using the following code:
-- barbar.lualocal helpers = require("shared.helpers")
No need to manually update your package.path
, and no need for relative imports either - just
include the entire path starting from ~/.config/nvim/lua
2. Sending Neovim Keys Though iTerm2
When I first switched from VSCode to Neovim, one of the greatest pain points was relearning all the
shortcuts I became so used to: cmd+s
, cmd+p
, cmd+z
etc. Unfortunately, as pointed out in this
stackoverflow answer, you can only remap the command
key
with MacVim, not Neovim. Luckily, there's another alternative: using iTerm2.
With iTerm2, you can set remaps with the command
key that send arbitrary key inputs to the
terminal. In other words, you can set up a remap like cmd+s
to send the key inputs :w
! A few
gotchas to keep in mind:
- The iTerm2 equivalent of
<cr>
is\n
. This is mostly useful when submitting a Neovim command, like:w\n
- To remap the
control
key, sayctrl+x
, use the form\<C-x>
You can add remaps by going to iTerm2 -> Preferences -> Keys and clicking the +
sign at the bottom
left. For the action
, select Send Text with "vim" Special Chars, and you should be good to go!
3. Shared Settings For a Barebones Config
When setting up a remote server, it can be a pain to get your Vim config up and running – especially if you use packages with their own installation processes (i.e. treesitter, coc, etc.). For remote systems where I mostly edit server config files, I've come to use a barebones Vim config with sensible defaults and zero external packages. Since nearly all of my options and a good chunk of my remaps can be shared between a barebones and full-blown config, I decided to take advantage of the overlap by sharing files between the two. Take a look at the following file structure:
.└── nvim/├── lua/│ ├── barebones/│ │ ├── init.lua│ │ ├── options.lua│ │ └── remaps.lua│ ├── settings/│ │ ├── plugins.lua│ │ ├── functions.lua│ │ ├── options.lua│ │ └── remaps.lua│ └── shared/│ ├── helpers.lua│ ├── options.lua│ └── remaps.lua└── init.lua
This file tree may look a bit weird, but it works great. Ignoring the barebones
directory, this is
just a normal Neovim config - an init.lua
at the root, and some settings to import. In other
words, by default, I use the feature-complete config's init.lua
and settings. In the barebones
directory, I added a second init.lua
, one which can be sourced with:
nvim -u ~/.config/nvim/lua/barebones/init.lua
Let's take a look at the two init.lua
s
-- nvim/init.lualocal h = require("shared.helpers")-- remap leader before importing remaps that use ith.map("", "<space>", "<nop>")h.let.mapleader = " "-- settings unique to the feature-complete configrequire("settings.plugins")require("settings.functions")require("settings.remaps")require("settings.options")require("shared.options")require("shared.remaps")
and
-- nvim/lua/barebones/init.lualocal h = require("shared.helpers")-- remap leader before importing remaps that use ith.map("", "<space>", "<nop>")h.let.mapleader = " "-- settings unique to the barbones configrequire("barebones.options")require("barebones.remaps")require("shared.options")require("shared.remaps")
Each config imports its own unique settings, along with the shared settings for both to use. It's a setup I've come to really enjoy working with.
4. Remaps For Hard-to-Reach Keys
I've always had trouble hitting the keys on the top row of the keyboard, especially the pinkey-keys
to the right of 0
: -
, _
, =
, +
. To make these keys a bit easier to use, I have two types of
solutions: snippets and native Neovim remaps.
For snippets, I use coc-snippets with the following
coc-settings.json
:
// ~/.config/nvim/coc-settings.json{"snippets.textmateSnippetsRoots": ["path/to/.config/nvim/snippets"]}
This sources custom snippets from the snippets
directory in the root of your config. I'm used to
writing VSCode-style snippets, so I use the textmate style with the following code:
// ~/.config/nvim/snippets/misc.code-snippets{"plus": {"prefix": "uu","body": ["+"]}}
This works wonders with using the +
key, but snippets don't quite work for the other hard-to-reach
keys. Since snippets are triggered by a prefix
like hh
, the prefix must be separated from any
characters before it with a space. Unfortunately, there are many situations where you wouldn't want
a space before the character. Say we want to create a snippet for =
, we can see that a trigger
prefix won't work for cases like prop={prop}
. Instead, for -
, _
, and =
, I remap the
control
key with a native Neovim remap:
inoremap <C-f> =inoremap <C-g> -inoremap <C-b> _
Personally, I've had great results with these two types of solutions, and I'd highly recommend trying them out.
5. Search by Whole Word, Case Sensitive, or Both
One of my favorite search-related features in VSCode was the ability to search by whole word, case, or both. Luckily, with the right keymappings, this isn't too hard to recreate in Neovim.
# case sensitivenoremap <leader>/c /\C<left><left># whole wordnoremap <leader>/w /\<\><left><left># case sensitive and whole wordnoremap <leader>cw /\<\>\C<left><left><left><left>
To replace multiple results in a single file, highlight your results with /
or one of the commands
above, type cgn
, enter the replacement, and press escape. Go to the next entry with n
as normal,
and do the same replacement with .
.
you might also like:
React Suspense in three different architectures
August 17, 2023
Unpacking React's most versatile API