Wezterm and Neovim keybindings in MacOS
I've been using Alacritty on Ubuntu for a long time, and it was pretty much all what I needed, because on Ubuntu you can swap the CTRL key with the SUPER key, and you're done. You can do most of your stuff in Neovim, and the OS in general, with just your left thumb, but when deciding to use a second laptop for non-work related stuff, and choosing to go with MacOS I got into troubles.
As Neovim (and Vim) don't allow mapping the CMD key (that's just possible for MacVim, I've never used MacVim as certainly isn't a fit for me, I use Vim in the terminal because I'm in the terminal itself, and that prevents me for using any other GUI editor/IDE) I didn't want to just give up and use the CTRL for most of the keybindings I use. So I wanted to give it a try and see how to successfully use the CMD key for that.
At first, I got something done by translating the keys pressed to HEX codes (https://github.com/wez/wezterm/issues/3731) but that didn't work well for other keys and involves you having to play with the HEX representation of every key, which is by far unintuitive. But knowing that in Wezterm you can get the name of the process spawn in which your pane is (although, subject to various restrictions) was enough to get a simpler version working.
You can first get the foreground process (pane:get_foreground_process_name()
), extract the part that matters (in MacOS it'll be something like this (/opt/homebrew/Cellar/neovim/0.9.4/bin/nvim
)) and check if it's "nvim" (or "vim");
pane:get_foreground_process_name():match(".*/([^/]+)$") == "nvim"
then, as a Wezterm action_callback
, you can perform an action in the current window and pane;
window:perform_action({ SendKey = { key = key, mods = mods } }, pane)
and that's all.
In my case, I wanted to move between windows with the arrow keys, something I do on Ubuntu with CTRL+Left, etc, can be mapped to CMD+Left by using Wezterm's LeftArrow
and the CMD
mod;
-- ~/.wezterm.lua
wezterm.action_callback(function(window, pane)
local is_nvim = pane:get_foreground_process_name():match(".*/([^/]+)$") == "nvim"
if not is_nvim then return end
window:perform_action({ SendKey = { key = "h", mods = "CTRL" } }, pane)
end)
so the whole key configuration becomes;
-- ~/.wezterm.lua
{
key = "LeftArrow",
mods = "CMD",
action = wezterm.action_callback(function(window, pane)
local is_nvim = pane:get_foreground_process_name():match(".*/([^/]+)$") == "nvim"
if not is_nvim then return end
window:perform_action({ SendKey = { key = "h", mods = "CTRL" } }, pane)
end)
}
(but notice for this we first need to create Vim's mapping, otherwise Vim will only receive the CTRL + h combination and will result in nothing - unless, of course, is mapped to something else)
vim.api.nvim_set_keymap('n', '<C-left>', '<C-w>h', { noremap = true })
And off you go, now you can map any combination of keys using any key supported by Wezterm to whatever can be received in Vim.
Having this wrapped into a tiny function, I have a couple of Telescope mappings that come in handy;
-- ~/.wezterm.lua
local function bind_keys_in_nvim(key, mods)
return function(window, pane)
local is_nvim = pane:get_foreground_process_name():match(".*/([^/]+)$") == "nvim"
if not is_nvim then return end
window:perform_action({ SendKey = { key = key, mods = mods } }, pane)
end
end
local config = {}
config.keys = {
...
{ key = "p", mods = "CMD", action = wezterm.action_callback(bind_keys_in_nvim("p", "CTRL")) },
{ key = "s", mods = "CMD", action = wezterm.action_callback(bind_keys_in_nvim("s", "CTRL")) },
{ key = "g", mods = "CMD", action = wezterm.action_callback(bind_keys_in_nvim("g", "CTRL")) },
}
return config
(I use FeiyouG/commander.nvim
to manage my mappings in Neovim);
-- ~/.config/nvim/lua/setup/general.lua
local commander = require("commander")
local telescope = {
{
description = "Search for files (respecting .gitignore)",
cmd = "<cmd>Telescope find_files<cr>",
keys = { "n", "<c-p>", { noremap = true } },
},
{
description = "Search across the whole project",
cmd = "<cmd>Telescope live_grep<cr>",
keys = { "n", "<c-s>", { noremap = true } },
},
{
description = "Search string under cursor on the whole project",
cmd = "<cmd>Telescope grep_string<cr>",
keys = { "n", "<c-g>", { noremap = true } },
},
}
commander.add(telescope, { category = "Telescope" })
require("telescope").load_extension("commander")
Now I can change environments without any extra effort (yeah, there's an extra effort I have to do while working in Vim in different laptops).