Neovim is a powerful and feature-rich terminal text editor that is a fork of the classic Vim editor. The scope of customizing the workflow with Neovim is limitless. Neovim allows you to customize key bindings to boost your productivity. However, mastering the key bindings for different groups can be challenging, especially for beginners. Thanks to Which Key, not only can you set up custom key bindings, but it also provides a dynamic floating dashboard to remind you of your key bindings, so you never forget them again.
In this blog post, I’ll show you how to install Which Key and set up your custom key bindings. I’ll also demonstrate how to customize Which Key for the best possible experience.
Table of Contents
Prerequisites
Before proceeding with the installation and usage of Nvim Tree in Neovim, make sure you have the following packages installed on your system:
Neovim
Which Key is a plugin for Neovim that allows you to set up custom key bindings. Therefore, you must have Neovim installed to use Which Key. I assume that you have already installed Neovim.
Packer
Packer is a plugin manager for Neovim. You need Packer to install the Which Key plugin in Neovim. So, make sure Packer is installed in Neovim before installing Which Key on your system. If you haven’t installed Packer yet, consider installing Packer and follow the rest of the tutorial.
Installing Which Key
Before installing Which Key in Neovim, let’s take a look at the project structure. If you have followed our guide to install Packer, you should already have a similar structure. Don’t worry; you can still follow this guide even if you have a different structure. Just be careful when editing the configuration files.
~/.config/
└── nvim
├── init.lua
└── lua
├── plugins.lua
├── whichkey.lua
├── .....
└── other configuration files
To install the Which Key plugin, open the plugins.lua
file using the following command:
nvim ~/.config/nvim/lua/plugins.lua
If your plugins file has a different name or is located in a different directory, open the file accordingly. After opening the plugins file, add the following two lines to install Which Key:
-- Install Which Key
use ("folke/which-key.nvim")
Once you’ve added these lines to the plugins.lua
file, save it using the :w
command to install the plugins.
Setting Keymaps
First, you need to choose a ladder key to create key combinations. To avoid duplicate key mappings, avoid using Alt
, Ctrl
, or Shift
as the ladder key. For this tutorial, I’ve set the ladder key to <Space>
. To use any key binding, you should use <ladder>
+ <key>
. For example, you can map the :w
command to <ladder> + w
to save a file. After mapping it, you can use Space
+ w
to save files.
To start creating key mappings, create a Lua configuration file for Which Key. Use the following command to create whichkey.lua
nvim ~/.config/nvim/lua/whichkey.lua
Then, add the following configuration to the file:
-- set leader key to space
vim.g.mapleader = " "
local keymap = vim.keymap -- for conciseness
local status_ok, which_key = pcall(require, "which-key")
if not status_ok then
return
end
local setup = {
plugins = {
marks = true, -- shows a list of your marks on ' and `
registers = true, -- shows your registers on " in NORMAL or <C-r> in INSERT mode
spelling = {
enabled = true, -- enabling this will show WhichKey when pressing z= to select spelling suggestions
suggestions = 20, -- how many suggestions should be shown in the list?
},
-- the presets plugin, adds help for a bunch of default keybindings in Neovim
-- No actual key bindings are created
presets = {
operators = false, -- adds help for operators like d, y, ... and registers them for motion / text object completion
motions = true, -- adds help for motions
text_objects = true, -- help for text objects triggered after entering an operator
windows = true, -- default bindings on <c-w>
nav = true, -- misc bindings to work with windows
z = true, -- bindings for folds, spelling and others prefixed with z
g = true, -- bindings for prefixed with g
},
},
-- add operators that will trigger motion and text object completion
-- to enable all native operators, set the preset / operators plugin above
-- operators = { gc = "Comments" },
key_labels = {
-- override the label used to display some keys. It doesn't effect WK in any other way.
-- For example:
-- ["<space>"] = "SPC",
-- ["<cr>"] = "RET",
-- ["<tab>"] = "TAB",
},
icons = {
breadcrumb = "»", -- symbol used in the command line area that shows your active key combo
separator = "➜", -- symbol used between a key and it's label
group = "+", -- symbol prepended to a group
},
popup_mappings = {
scroll_down = "<c-d>", -- binding to scroll down inside the popup
scroll_up = "<c-u>", -- binding to scroll up inside the popup
},
window = {
border = "rounded", -- none, single, double, shadow
position = "bottom", -- bottom, top
margin = { 1, 0, 1, 0 }, -- extra window margin [top, right, bottom, left]
padding = { 2, 2, 2, 2 }, -- extra window padding [top, right, bottom, left]
winblend = 0,
},
layout = {
height = { min = 4, max = 25 }, -- min and max height of the columns
width = { min = 20, max = 50 }, -- min and max width of the columns
spacing = 3, -- spacing between columns
align = "left", -- align columns left, center or right
},
ignore_missing = true, -- enable this to hide mappings for which you didn't specify a label
hidden = { "<silent>", "<cmd>", "<Cmd>", "<CR>", "call", "lua", "^:", "^ " }, -- hide mapping boilerplate
show_help = true, -- show help message on the command line when the popup is visible
triggers = "auto", -- automatically setup triggers
-- triggers = {"<leader>"} -- or specify a list manually
triggers_blacklist = {
-- list of mode / prefixes that should never be hooked by WhichKey
-- this is mostly relevant for key maps that start with a native binding
-- most people should not need to change this
i = { "j", "k" },
v = { "j", "k" },
},
}
local opts = {
mode = "n", -- NORMAL mode
prefix = "<leader>",
buffer = nil, -- Global mappings. Specify a buffer number for buffer local mappings
silent = true, -- use `silent` when creating keymaps
noremap = true, -- use `noremap` when creating keymaps
nowait = true, -- use `nowait` when creating keymaps
}
local mappings = {
["a"] = { "<cmd>Alpha<cr>", "Alpha" },
["b"] = {
"<cmd>lua require('telescope.builtin').buffers(require('telescope.themes').get_dropdown{previewer = false})<cr>",
"Buffers",
},
["e"] = { "<cmd>NvimTreeToggle<cr>", "Explorer" },
["w"] = { "<cmd>w!<CR>", "Save" },
["q"] = { "<cmd>q!<CR>", "Quit" },
["c"] = { "<cmd>Bdelete!<CR>", "Close Buffer" },
["h"] = { "<cmd>nohlsearch<CR>", "No Highlight" },
["f"] = {
"<cmd>lua require('telescope.builtin').find_files(require('telescope.themes').get_dropdown{previewer = false})<cr>",
"Find files",
},
["F"] = { "<cmd>Telescope live_grep theme=ivy<cr>", "Find Text" },
["P"] = { "<cmd>lua require('telescope').extensions.projects.projects()<cr>", "Projects" },
-- Packer
p = {
name = "Packer",
c = { "<cmd>PackerCompile<cr>", "Compile" },
i = { "<cmd>PackerInstall<cr>", "Install" },
s = { "<cmd>PackerSync<cr>", "Sync" },
S = { "<cmd>PackerStatus<cr>", "Status" },
u = { "<cmd>PackerUpdate<cr>", "Update" },
},
-- Git
g = {
name = "Git",
g = { "<cmd>lua _LAZYGIT_TOGGLE()<CR>", "Lazygit" },
j = { "<cmd>lua require 'gitsigns'.next_hunk()<cr>", "Next Hunk" },
k = { "<cmd>lua require 'gitsigns'.prev_hunk()<cr>", "Prev Hunk" },
l = { "<cmd>lua require 'gitsigns'.blame_line()<cr>", "Blame" },
p = { "<cmd>lua require 'gitsigns'.preview_hunk()<cr>", "Preview Hunk" },
r = { "<cmd>lua require 'gitsigns'.reset_hunk()<cr>", "Reset Hunk" },
R = { "<cmd>lua require 'gitsigns'.reset_buffer()<cr>", "Reset Buffer" },
s = { "<cmd>lua require 'gitsigns'.stage_hunk()<cr>", "Stage Hunk" },
u = {
"<cmd>lua require 'gitsigns'.undo_stage_hunk()<cr>",
"Undo Stage Hunk",
},
o = { "<cmd>Telescope git_status<cr>", "Open changed file" },
b = { "<cmd>Telescope git_branches<cr>", "Checkout branch" },
c = { "<cmd>Telescope git_commits<cr>", "Checkout commit" },
d = {
"<cmd>Gitsigns diffthis HEAD<cr>",
"Diff",
},
},
-- Language Server Protocol (LSP)
l = {
name = "LSP",
a = { "<cmd>lua vim.lsp.buf.code_action()<cr>", "Code Action" },
d = {
"<cmd>Telescope diagnostics bufnr=0<cr>",
"Document Diagnostics",
},
w = {
"<cmd>Telescope diagnostics<cr>",
"Workspace Diagnostics",
},
f = { "<cmd>lua vim.lsp.buf.format{async=true}<cr>", "Format" },
i = { "<cmd>LspInfo<cr>", "Info" },
I = { "<cmd>LspInstallInfo<cr>", "Installer Info" },
j = {
"<cmd>lua vim.lsp.diagnostic.goto_next()<CR>",
"Next Diagnostic",
},
k = {
"<cmd>lua vim.lsp.diagnostic.goto_prev()<cr>",
"Prev Diagnostic",
},
l = { "<cmd>lua vim.lsp.codelens.run()<cr>", "CodeLens Action" },
q = { "<cmd>lua vim.diagnostic.setloclist()<cr>", "Quickfix" },
r = { "<cmd>lua vim.lsp.buf.rename()<cr>", "Rename" },
s = { "<cmd>Telescope lsp_document_symbols<cr>", "Document Symbols" },
S = {
"<cmd>Telescope lsp_dynamic_workspace_symbols<cr>",
"Workspace Symbols",
},
},
--Telescope
s = {
name = "Search",
b = { "<cmd>Telescope git_branches<cr>", "Checkout branch" },
c = { "<cmd>Telescope colorscheme<cr>", "Colorscheme" },
h = { "<cmd>Telescope help_tags<cr>", "Find Help" },
M = { "<cmd>Telescope man_pages<cr>", "Man Pages" },
r = { "<cmd>Telescope oldfiles<cr>", "Open Recent File" },
R = { "<cmd>Telescope registers<cr>", "Registers" },
k = { "<cmd>Telescope keymaps<cr>", "Keymaps" },
C = { "<cmd>Telescope commands<cr>", "Commands" },
},
-- Terminal
t = {
name = "Terminal",
n = { "<cmd>lua _NODE_TOGGLE()<cr>", "Node" },
u = { "<cmd>lua _NCDU_TOGGLE()<cr>", "NCDU" },
t = { "<cmd>lua _HTOP_TOGGLE()<cr>", "Htop" },
p = { "<cmd>lua _PYTHON_TOGGLE()<cr>", "Python" },
f = { "<cmd>ToggleTerm direction=float<cr>", "Float" },
h = { "<cmd>ToggleTerm size=10 direction=horizontal<cr>", "Horizontal" },
v = { "<cmd>ToggleTerm size=80 direction=vertical<cr>", "Vertical" },
},
}
which_key.setup(setup)
which_key.register(mappings, opts)
Credit: Neovim-from-scratch
After adding these configurations, save and exit the nvim editor using the :wq
command.
Now, open the init.lua
file with the following command:
nvim ~/.config/nvim/init.lua
Add the following line to the init.lua
file to complete the installation:
require("whichkey")
After adding the line, save and quit the file.
Understanding the Key Maps in the Configuration File
In Which Key configuration, you can set both single key bindings and create key groups. Key groups contain multiple related keybindings. Group names are initiated with the +
symbol, and you can identify group names by inspecting the name.
Single Key Mappings
Here are some examples of single key mappings:
Key | Mapping | Description |
---|---|---|
a | Alpha | Alpha |
b | Telescope buffers | Buffers |
e | NvimTreeToggle | Explorer |
w | w! | Save |
q | q! | Quit |
c | Bdelete! | Close Buffer |
h | nohlsearch | No Highlight |
f | Telescope find_files | Find files |
F | Telescope live_grep | Find Text |
P | lua require(‘telescope’).extensions.projects.projects() | Projects |
Telescope
is a powerful fuzzy finder used to search files and strings. If you haven’t installed it, you can follow our guides to install it.NvimTree
is a terminal file explorer for Neovim. If you haven’t installed Nvim Tree, you can follow our guides to install it.
Group Key Mappings
Which Key supports group key mappings where multiple related key combinations are stored. You can create your own keymapping group. Here are some commonly used keymap groups:
Packer:
Packer is a plugin manager that provides options to install, sync, and update plugins. It is accessible through the command, and you can map the packer command to <ladder>
+ p
as the group key.
Key | Mapping | Description |
---|---|---|
c | PackerCompile | Compile |
i | PackerInstall | Install |
s | PackerSync | Sync |
S | PackerStatus | Status |
u | PackerUpdate | Update |
Git:
You can maintain your GitHub repository in Neovim. To increase your productivity, you are suggested to use the following key combinations within the <ladder>
+ g
group key.
Key | Mapping | Description |
---|---|---|
j | gs.next_hunk() | Next Hunk |
k | gs.prev_hunk() | Prev Hunk |
l | gs.blame_line() | Blame |
p | gs.preview_hunk | Preview Hunk |
r | gs.reset_hunk() | Reset Hunk |
R | gs.reset_buffer | Reset Buffer |
s | gs.stage_hunk | Stage Hunk |
o | Telescope git_status | Open changed file |
b | Telescope git_branche | Checkout branch |
c | Telescope git_commits | Checkout commit |
Language Server Protocol (LSP):
The Language Server Protocol (LSP) is an open standard protocol designed to facilitate communication between code editors or integrated development environments (IDEs) and language servers. You can create custom keymaps for LSP. Here are suggested key maps grouped under <space>
+ l
group key
.
Key | Mapping | Description |
---|---|---|
a | vim.lsp.buf.code_action() | Code Action |
w | Telescope diagnostics | Workspace Diagnostics |
f | vim.lsp.buf.format{async=true} | Format |
i | LspInfo | Info |
I | LspInstallInfo | Installer Info |
l | vim.lsp.codelens.run() | CodeLens Action |
q | vim.diagnostic.setloclist() | Quickfix |
r | vim.lsp.buf.rename() | Rename |
Telescope:
Telescope is a highly versatile fuzzy finder over lists. It is used for searching and can be combined with other modules for advanced actions. Here is a suggested keymapping for Telescope grouped under Search and can be accessed with <space>
+ s
.
Key | Mapping | Description |
---|---|---|
s | Search | Search |
b | Telescope git_branches | Checkout branch |
c | Telescope colorscheme | Colorscheme |
h | Telescope help_tags | Find Help |
M | Telescope man_pages | Man Pages |
r | Telescope oldfiles | Open Recent File |
R | Telescope registers | Registers |
k | Telescope keymaps | Keymaps |
C | Telescope commands | Commands |
Terminal:
You can map terminal commands with Which Key. Here are suggested key maps grouped under Terminal and can be accessed with <space>
+ t
.
Key | Mapping | Description |
---|---|---|
t | Terminal | Terminal |
n | _NODE_TOGGLE() | Node |
u | _NCDU_TOGGLE() | NCDU |
t | lua _HTOP_TOGGLE() | Htop |
p | _PYTHON_TOGGLE() | Python |
f | ToggleTerm direction=float | Float |
h | ToggleTerm size=10 direction=horizontal | Horizontal |
v | ToggleTerm size=80 direction=vertical | Vertical |
Using Which Key
Which Key allows you to use key mapping without remembering them. You can access key maps with ladder keys. For this blog post, I have used the Space
key as the ladder key. For example, open a file with Neovim and press the Space
key, and you will see the key maps in a floating dashboard in the terminal, like this:
From the key maps list, you can select key maps to execute the associated commands. Notice the +
symbol before the name of key maps, which represents the group key, and you can navigate further to the group.
For example, if I press l
, which represents Language Server Protocol (LSP), another window will pop up containing the key maps associated with LSP:
You can select any command from the list, and you will never forget your key maps.
Changing the Color of the Which Key Float
As you have noticed, the default look of the floating window of Which Key is kind of ugly. You can change the default color of the floating window to make it easier on your eyes. To change the custom color, you need to define colors for the floating window in Neovim.
Open the options.lua
file with Neovim:
nvim ~/.config/nvim/lua/options.lua
If the file does not exist, Neovim will create one. Add the following lines to the file to change the color:
-- appearance
vim.api.nvim_set_hl(0, 'FloatBorder', {bg='#3B4252', fg='#5E81AC'})
vim.api.nvim_set_hl(0, 'NormalFloat', {bg='#3B4252'})
vim.api.nvim_set_hl(0, 'TelescopeNormal', {bg='#3B4252'})
vim.api.nvim_set_hl(0, 'TelescopeBorder', {bg='#3B4252'})
Save and exit the file. Please make sure that you have added options.lua
to init.lua
. If you haven’t added it, open the init.lua
file:
nvim ~/.config/nvim/init.lua
and add the following line to the file:
require("options")
Save and exit the file.
Now, open a file with Neovim and open Which Key with the ladder Space
key. You will see the floating window like this:
Conclusion
Which Key is an excellent option for setting key maps and also allows you to see the key maps if you forget them. With Which Key, you won’t forget the key maps again. In this blog post, I have discussed installing and setting up the key maps for Which Key and changing the user interface.