Install and Set Up Which Key in Neovim/Nvim And Never Forget Your Key Mappings

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.

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")
Installing Which Key

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:

KeyMappingDescription
aAlphaAlpha
bTelescope buffersBuffers
eNvimTreeToggleExplorer
ww!Save
qq!Quit
cBdelete!Close Buffer
hnohlsearchNo Highlight
fTelescope find_filesFind files
FTelescope live_grepFind Text
Plua require(‘telescope’).extensions.projects.projects()Projects

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.

KeyMappingDescription
cPackerCompileCompile
iPackerInstallInstall
sPackerSyncSync
SPackerStatusStatus
uPackerUpdateUpdate

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.

KeyMappingDescription
jgs.next_hunk()Next Hunk
kgs.prev_hunk()Prev Hunk
lgs.blame_line()Blame
pgs.preview_hunkPreview Hunk
rgs.reset_hunk()Reset Hunk
Rgs.reset_bufferReset Buffer
sgs.stage_hunkStage Hunk
oTelescope git_statusOpen changed file
bTelescope git_brancheCheckout branch
cTelescope git_commitsCheckout 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

.

KeyMappingDescription
avim.lsp.buf.code_action()Code Action
wTelescope diagnosticsWorkspace Diagnostics
fvim.lsp.buf.format{async=true}Format
iLspInfoInfo
ILspInstallInfoInstaller Info
lvim.lsp.codelens.run()CodeLens Action
qvim.diagnostic.setloclist()Quickfix
rvim.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.

KeyMappingDescription
sSearchSearch
bTelescope git_branchesCheckout branch
cTelescope colorschemeColorscheme
hTelescope help_tagsFind Help
MTelescope man_pagesMan Pages
rTelescope oldfilesOpen Recent File
RTelescope registersRegisters
kTelescope keymapsKeymaps
CTelescope commandsCommands

Terminal:

You can map terminal commands with Which Key. Here are suggested key maps grouped under Terminal and can be accessed with <space> + t.

KeyMappingDescription
tTerminalTerminal
n_NODE_TOGGLE()Node
u_NCDU_TOGGLE()NCDU
tlua _HTOP_TOGGLE()Htop
p_PYTHON_TOGGLE()Python
fToggleTerm direction=floatFloat
hToggleTerm size=10 direction=horizontalHorizontal
vToggleTerm size=80 direction=verticalVertical

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:

Which Key UI

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:

Which Key UI

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:

Modified Which Key UI

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.

Share your love

Newsletter Updates

Stay updated with our latest guides and tutorials about Linux.

Leave a Reply

Your email address will not be published. Required fields are marked *