# which-key.nvim **Repository Path**: neovim-lover/which-key.nvim ## Basic Information - **Project Name**: which-key.nvim - **Description**: 老牌插件,不用介绍了吧,哈哈 - **Primary Language**: Lua - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-04-13 - **Last Updated**: 2025-04-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 💥 Which Key **WhichKey** helps you remember your Neovim keymaps, by showing available keybindings in a popup as you type. ![image](https://github.com/user-attachments/assets/89277334-dcdc-4b0f-9fd4-02f27012f589) ![image](https://github.com/user-attachments/assets/f8d71a75-312e-4a42-add8-d153493b2633) ![image](https://github.com/user-attachments/assets/e4400a1d-7e71-4439-b6ff-6cbc40647a6f) ## ✨ Features - 🔍 **Key Binding Help**: show available keybindings in a popup as you type. - ⌨️ **Modes**: works in normal, insert, visual, operator pending, terminal and command mode. Every mode can be enabled/disabled. - 🛠️ **Customizable Layouts**: choose from `classic`, `modern`, and `helix` presets or customize the window. - 🔄 **Flexible Sorting**: sort by `local`, `order`, `group`, `alphanum`, `mod`, `lower`, `icase`, `desc`, or `manual`. - 🎨 **Formatting**: customizable key labels and descriptions - 🖼️ **Icons**: integrates with [mini.icons](https://github.com/echasnovski/mini.icons) and [nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons) - ⏱️ **Delay**: delay is independent of `timeoutlen` - 🌐 **Plugins**: built-in plugins for marks, registers, presets, and spelling suggestions - 🚀 **Operators, Motions, Text Objects**: help for operators, motions and text objects - 🐙 **Hydra Mode**: keep the popup open until you hit `` ## ⚡️ Requirements - **Neovim** >= 0.9.4 - for proper icons support: - [mini.icons](https://github.com/echasnovski/mini.icons) _(optional)_ - [nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons) _(optional)_ - a [Nerd Font](https://www.nerdfonts.com/) **_(optional)_** ## 📦 Installation Install the plugin with your package manager: ### [lazy.nvim](https://github.com/folke/lazy.nvim) ```lua { "folke/which-key.nvim", event = "VeryLazy", opts = { -- your configuration comes here -- or leave it empty to use the default settings -- refer to the configuration section below }, keys = { { "?", function() require("which-key").show({ global = false }) end, desc = "Buffer Local Keymaps (which-key)", }, }, } ``` ## ⚙️ Configuration > [!important] > Make sure to run `:checkhealth which-key` if something isn't working properly **WhichKey** is highly configurable. Expand to see the list of all the default options below.
Default Options ```lua ---@class wk.Opts local defaults = { ---@type false | "classic" | "modern" | "helix" preset = "classic", -- Delay before showing the popup. Can be a number or a function that returns a number. ---@type number | fun(ctx: { keys: string, mode: string, plugin?: string }):number delay = function(ctx) return ctx.plugin and 0 or 200 end, ---@param mapping wk.Mapping filter = function(mapping) -- example to exclude mappings without a description -- return mapping.desc and mapping.desc ~= "" return true end, --- You can add any mappings here, or use `require('which-key').add()` later ---@type wk.Spec spec = {}, -- show a warning when issues were detected with your mappings notify = true, -- Which-key automatically sets up triggers for your mappings. -- But you can disable this and setup the triggers manually. -- Check the docs for more info. ---@type wk.Spec triggers = { { "", mode = "nxso" }, }, -- Start hidden and wait for a key to be pressed before showing the popup -- Only used by enabled xo mapping modes. ---@param ctx { mode: string, operator: string } defer = function(ctx) return ctx.mode == "V" or ctx.mode == "" end, plugins = { marks = true, -- shows a list of your marks on ' and ` registers = true, -- shows your registers on " in NORMAL or in INSERT mode -- the presets plugin, adds help for a bunch of default keybindings in Neovim -- No actual key bindings are created 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? }, presets = { operators = true, -- adds help for operators like d, y, ... motions = true, -- adds help for motions text_objects = true, -- help for text objects triggered after entering an operator windows = true, -- default bindings on 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 }, }, ---@type wk.Win.opts win = { -- don't allow the popup to overlap with the cursor no_overlap = true, -- width = 1, -- height = { min = 4, max = 25 }, -- col = 0, -- row = math.huge, -- border = "none", padding = { 1, 2 }, -- extra window padding [top/bottom, right/left] title = true, title_pos = "center", zindex = 1000, -- Additional vim.wo and vim.bo options bo = {}, wo = { -- winblend = 10, -- value between 0-100 0 for fully opaque and 100 for fully transparent }, }, layout = { width = { min = 20 }, -- min and max width of the columns spacing = 3, -- spacing between columns }, keys = { scroll_down = "", -- binding to scroll down inside the popup scroll_up = "", -- binding to scroll up inside the popup }, ---@type (string|wk.Sorter)[] --- Mappings are sorted using configured sorters and natural sort of the keys --- Available sorters: --- * local: buffer-local mappings first --- * order: order of the items (Used by plugins like marks / registers) --- * group: groups last --- * alphanum: alpha-numerical first --- * mod: special modifier keys last --- * manual: the order the mappings were added --- * case: lower-case first sort = { "local", "order", "group", "alphanum", "mod" }, ---@type number|fun(node: wk.Node):boolean? expand = 0, -- expand groups when <= n mappings -- expand = function(node) -- return not node.desc -- expand all nodes without a description -- end, -- Functions/Lua Patterns for formatting the labels ---@type table replace = { key = { function(key) return require("which-key.view").format(key) end, -- { "", "SPC" }, }, desc = { { "%(?(.*)%)?", "%1" }, { "^%+", "" }, { "<[cC]md>", "" }, { "<[cC][rR]>", "" }, { "<[sS]ilent>", "" }, { "^lua%s+", "" }, { "^call%s+", "" }, { "^:%s*", "" }, }, }, 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 ellipsis = "…", -- set to false to disable all mapping icons, -- both those explicitly added in a mapping -- and those from rules mappings = true, --- See `lua/which-key/icons.lua` for more details --- Set to `false` to disable keymap icons from rules ---@type wk.IconRule[]|false rules = {}, -- use the highlights from mini.icons -- When `false`, it will use `WhichKeyIcon` instead colors = true, -- used by key format keys = { Up = " ", Down = " ", Left = " ", Right = " ", C = "󰘴 ", M = "󰘵 ", D = "󰘳 ", S = "󰘶 ", CR = "󰌑 ", Esc = "󱊷 ", ScrollWheelDown = "󱕐 ", ScrollWheelUp = "󱕑 ", NL = "󰌑 ", BS = "󰁮", Space = "󱁐 ", Tab = "󰌒 ", F1 = "󱊫", F2 = "󱊬", F3 = "󱊭", F4 = "󱊮", F5 = "󱊯", F6 = "󱊰", F7 = "󱊱", F8 = "󱊲", F9 = "󱊳", F10 = "󱊴", F11 = "󱊵", F12 = "󱊶", }, }, show_help = true, -- show a help message in the command line for using WhichKey show_keys = true, -- show the currently pressed key and its label as a message in the command line -- disable WhichKey for certain buf types and file types. disable = { ft = {}, bt = {}, }, debug = false, -- enable wk.log in the current directory } ```
## ⌨️ Mappings **WhichKey** automatically gets the descriptions of your keymaps from the `desc` attribute of the keymap. So for most use-cases, you don't need to do anything else. However, the **mapping spec** is still useful to configure group descriptions and mappings that don't really exist as a regular keymap. > [!WARNING] > The **mappings spec** changed in `v3`, so make sure to only use the new `add` method if > you updated your existing mappings. Mappings can be added as part of the config `opts.spec`, or can be added later using `require("which-key").add()`. `wk.add()` can be called multiple times from anywhere in your config files. A mapping has the following attributes: - **[1]**: (`string`) lhs **_(required)_** - **[2]**: (`string|fun()`) rhs **_(optional)_**: when present, it will create the mapping - **desc**: (`string|fun():string`) description **_(required for non-groups)_** - **group**: (`string|fun():string`) group name **_(optional)_** - **mode**: (`string|string[]`) mode **_(optional, defaults to `"n"`)_** - **cond**: (`boolean|fun():boolean`) condition to enable the mapping **_(optional)_** - **hidden**: (`boolean`) hide the mapping **_(optional)_** - **icon**: (`string|wk.Icon|fun():(wk.Icon|string)`) icon spec **_(optional)_** - **proxy**: (`string`) proxy to another mapping **_(optional)_** - **expand**: (`fun():wk.Spec`) nested mappings **_(optional)_** - any other option valid for `vim.keymap.set`. These are only used for creating mappings. When `desc`, `group`, or `icon` are functions, they are evaluated every time the popup is shown. The `expand` property allows to create dynamic mappings. Only functions as `rhs` are supported for dynamic mappings. Two examples are included in `which-key.extras`: - `require("which-key.extras").expand.buf`: creates numerical key to buffer mappings - `require("which-key.extras").expand.win`: creates numerical key to window mappings ```lua local wk = require("which-key") wk.add({ { "f", group = "file" }, -- group { "ff", "Telescope find_files", desc = "Find File", mode = "n" }, { "fb", function() print("hello") end, desc = "Foobar" }, { "fn", desc = "New File" }, { "f1", hidden = true }, -- hide this keymap { "w", proxy = "", group = "windows" }, -- proxy to window mappings { "b", group = "buffers", expand = function() return require("which-key.extras").expand.buf() end }, { -- Nested mappings are allowed and can be added in any order -- Most attributes can be inherited or overridden on any level -- There's no limit to the depth of nesting mode = { "n", "v" }, -- NORMAL and VISUAL mode { "q", "q", desc = "Quit" }, -- no need to specify mode since it's inherited { "w", "w", desc = "Write" }, } }) ``` ## 🎯 Triggers There's two ways that **which-key** can be triggered: - by a trigger keymap - by a `ModeChanged` event for visual and operator pending mode Both can be configured using `opts.triggers` and `opts.defer`. By default `opts.triggers` includes `{ "", mode = "nixsotc" }`, which will setup keymap triggers for every mode automatically and will trigger during `ModeChanged`. > [!NOTE] > Auto triggers will never be created for existing keymaps. > That includes every valid single key Neovim builtin mapping. > If you want to trigger on a builtin keymap, you have to add it manually. > > ```lua > triggers = { > { "", mode = "nixsotc" }, > { "a", mode = { "n", "v" } }, > } > ``` > [!TIP] > To manually setup triggers, you can set `opts.triggers` to: > > ```lua > triggers = { > { "", mode = { "n", "v" } }, > } > ``` For `ModeChanged` triggers, you can configure the `opts.defer` option. When it returns `true`, the popup will be shown only after an additional key is pressed. So `yaf`, would show which-key after pressing `ya`, but not after `y`. > [!TIP] > Defer some operators: > > ```lua > ---@param ctx { mode: string, operator: string } > defer = function(ctx) > if vim.list_contains({ "d", "y" }, ctx.operator) then > return true > end > return vim.list_contains({ "", "V" }, ctx.mode) > end, > ``` ## 🎨 Icons > [!note] > For full support, you need to install either [mini.icons](https://github.com/echasnovski/mini.icons) or [nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons) There's multiple ways to set icons for your keymaps: - if you use lazy.nvim, then some icons will be autodetected for keymaps belonging to certain plugins. - custom rules to decide what icon to use - in your mapping spec, you can specify what icon to use at any level, so at the node for `g` for example, to apply to all git keymaps. The `icon` attribute of a mapping can be a `string`, which will be used as the actual icon, or an `wk.Icon` object, which can have the following attributes: - `icon` (`string`): the icon to use **_(optional)_** - `hl` (`string`): the highlight group to use for the icon **_(optional)_** - `color` (`string`): the color to use for the icon **_(optional)_** valid colors are: `azure`, `blue`, `cyan`, `green`, `grey`, `orange`, `purple`, `red`, `yellow` - `cat` (`string`): the category of the icon **_(optional)_** valid categories are: `file`, `filetype`, `extension` - `name` (`string`): the name of the icon in the specified category **_(optional)_** > [!TIP] > If you'd rather not use icons, you can disable them > by setting `opts.icons.mappings` to `false`. ## 🚀 Usage When the **WhichKey** popup is open, you can use the following key bindings (they are also displayed at the bottom of the screen): - hit one of the keys to open a group or execute a key binding - `` to cancel and close the popup - `` go up one level - `` scroll down - `` scroll up ## 🐙 Hydra Mode Hydra mode is a special mode that keeps the popup open until you hit ``. ```lua -- Show hydra mode for changing windows require("which-key").show({ keys = "", loop = true, -- this will keep the popup open until you hit }) ``` ## 🔥 Plugins Four built-in plugins are included with **WhichKey**. ### Presets Built-in key binding help for `motions`, `text-objects`, `operators`, `windows`, `nav`, `z` and `g` and more. ### Marks Shows a list of your buffer local and global marks when you hit \` or ' ![image](https://github.com/user-attachments/assets/43fb0874-7f79-4521-aee9-03e2b0841758) ### Registers Shows a list of your buffer local and global registers when you hit " in _NORMAL_ mode, or `` in _INSERT_ mode. ![image](https://github.com/user-attachments/assets/d8077dcb-56fb-47b0-ad9e-1aba5db16950) ### Spelling When enabled, this plugin hooks into `z=` and replaces the full-screen spelling suggestions window by a list of suggestions within **WhichKey**. ![image](https://github.com/user-attachments/assets/102c7963-329a-40b9-b0a8-72c8656318b7) ## 🎨 Colors The table below shows all the highlight groups defined for **WhichKey** with their default link. | Highlight Group | Default Group | Description | | --- | --- | --- | | **WhichKey** | ***Function*** | | | **WhichKeyBorder** | ***FloatBorder*** | Border of the which-key window | | **WhichKeyDesc** | ***Identifier*** | description | | **WhichKeyGroup** | ***Keyword*** | group name | | **WhichKeyIcon** | ***@markup.link*** | icons | | **WhichKeyIconAzure** | ***Function*** | | | **WhichKeyIconBlue** | ***DiagnosticInfo*** | | | **WhichKeyIconCyan** | ***DiagnosticHint*** | | | **WhichKeyIconGreen** | ***DiagnosticOk*** | | | **WhichKeyIconGrey** | ***Normal*** | | | **WhichKeyIconOrange** | ***DiagnosticWarn*** | | | **WhichKeyIconPurple** | ***Constant*** | | | **WhichKeyIconRed** | ***DiagnosticError*** | | | **WhichKeyIconYellow** | ***DiagnosticWarn*** | | | **WhichKeyNormal** | ***NormalFloat*** | Normal in th which-key window | | **WhichKeySeparator** | ***Comment*** | the separator between the key and its description | | **WhichKeyTitle** | ***FloatTitle*** | Title of the which-key window | | **WhichKeyValue** | ***Comment*** | values by plugins (like marks, registers, etc) |