Showing preview only (274K chars total). Download the full file or copy to clipboard to get everything.
Repository: folke/which-key.nvim
Branch: main
Commit: 3aab2147e748
Files: 61
Total size: 257.7 KB
Directory structure:
gitextract_lgngfsi1/
├── .editorconfig
├── .github/
│ ├── .release-please-manifest.json
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ ├── config.yml
│ │ └── feature_request.yml
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── dependabot.yml
│ ├── release-please-config.json
│ └── workflows/
│ ├── ci.yml
│ ├── labeler.yml
│ ├── pr.yml
│ ├── stale.yml
│ └── update.yml
├── .gitignore
├── .lua-format
├── .markdownlint-cli2.yaml
├── .neoconf.json
├── CHANGELOG.md
├── LICENSE
├── NEWS.md
├── README.md
├── TODO.md
├── doc/
│ └── which-key.nvim.txt
├── lua/
│ └── which-key/
│ ├── buf.lua
│ ├── colors.lua
│ ├── config.lua
│ ├── docs.lua
│ ├── extras.lua
│ ├── health.lua
│ ├── icons.lua
│ ├── init.lua
│ ├── layout.lua
│ ├── mappings.lua
│ ├── migrate.lua
│ ├── node.lua
│ ├── plugins/
│ │ ├── init.lua
│ │ ├── marks.lua
│ │ ├── presets.lua
│ │ ├── registers.lua
│ │ └── spelling.lua
│ ├── presets.lua
│ ├── state.lua
│ ├── text.lua
│ ├── tree.lua
│ ├── triggers.lua
│ ├── types.lua
│ ├── util.lua
│ ├── view.lua
│ └── win.lua
├── plugin/
│ └── which-key.lua
├── scripts/
│ ├── docs
│ └── test
├── selene.toml
├── stylua.toml
├── tests/
│ ├── buf_spec.lua
│ ├── helpers.lua
│ ├── layout_spec.lua
│ ├── mappings_spec.lua
│ ├── minit.lua
│ └── util_spec.lua
└── vim.yml
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
root = true
[*]
insert_final_newline = true
indent_style = space
indent_size = 2
charset = utf-8
================================================
FILE: .github/.release-please-manifest.json
================================================
{
".": "3.17.0"
}
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: Bug Report
description: File a bug/issue
title: "bug: "
labels: [bug]
body:
- type: markdown
attributes:
value: |
**Before** reporting an issue, make sure to read the [documentation](https://github.com/folke/which-key.nvim)
and search [existing issues](https://github.com/folke/which-key.nvim/issues).
Usage questions such as ***"How do I...?"*** belong in [Discussions](https://github.com/folke/which-key.nvim/discussions) and will be closed.
- type: checkboxes
attributes:
label: Did you check docs and existing issues?
description: Make sure you checked all of the below before submitting an issue
options:
- label: I have read all the which-key.nvim docs
required: true
- label: I have updated the plugin to the latest version before submitting this issue
required: true
- label: I have searched the existing issues of which-key.nvim
required: true
- label: I have searched the existing issues of plugins related to this issue
required: true
- type: input
attributes:
label: "Neovim version (nvim -v)"
placeholder: "0.8.0 commit db1b0ee3b30f"
validations:
required: true
- type: input
attributes:
label: "Operating system/version"
placeholder: "MacOS 11.5"
validations:
required: true
- type: textarea
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is. Please include any related errors you see in Neovim.
validations:
required: true
- type: textarea
attributes:
label: Steps To Reproduce
description: Steps to reproduce the behavior.
placeholder: |
1.
2.
3.
validations:
required: true
- type: textarea
attributes:
label: Expected Behavior
description: A concise description of what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: Health
description: Attach the output of `:checkhealth which-key` here
render: log
- type: textarea
attributes:
label: Log
description: Please enable logging with `opts.debug = true` and attach the contents of `./wk.log` here
render: log
- type: textarea
attributes:
label: Repro
description: Minimal `init.lua` to reproduce this issue. Save as `repro.lua` and run with `nvim -u repro.lua`
value: |
vim.env.LAZY_STDPATH = ".repro"
load(vim.fn.system("curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua"))()
require("lazy.minit").repro({
spec = {
{ "folke/which-key.nvim", opts = {} },
-- add any other plugins here
},
})
render: lua
validations:
required: false
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: Ask a question
url: https://github.com/folke/which-key.nvim/discussions
about: Use Github discussions instead
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: Feature Request
description: Suggest a new feature
title: "feature: "
labels: [enhancement]
body:
- type: checkboxes
attributes:
label: Did you check the docs?
description: Make sure you read all the docs before submitting a feature request
options:
- label: I have read all the which-key.nvim docs
required: true
- type: textarea
validations:
required: true
attributes:
label: Is your feature request related to a problem? Please describe.
description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
- type: textarea
validations:
required: true
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
- type: textarea
validations:
required: true
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
- type: textarea
validations:
required: false
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here.
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
## Description
<!-- Describe the big picture of your changes to communicate to the maintainers
why we should accept this pull request. -->
## Related Issue(s)
<!--
If this PR fixes any issues, please link to the issue here.
- Fixes #<issue_number>
-->
## Screenshots
<!-- Add screenshots of the changes if applicable. -->
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
================================================
FILE: .github/release-please-config.json
================================================
{
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"packages": {
".": {
"release-type": "simple",
"extra-files": ["lua/which-key/config.lua"]
}
}
}
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
push:
branches: [main, master]
pull_request:
jobs:
ci:
uses: folke/github/.github/workflows/ci.yml@main
secrets: inherit
with:
plugin: which-key.nvim
repo: folke/which-key.nvim
================================================
FILE: .github/workflows/labeler.yml
================================================
name: "PR Labeler"
on:
- pull_request_target
jobs:
labeler:
uses: folke/github/.github/workflows/labeler.yml@main
secrets: inherit
================================================
FILE: .github/workflows/pr.yml
================================================
name: PR Title
on:
pull_request_target:
types:
- opened
- edited
- synchronize
- reopened
- ready_for_review
permissions:
pull-requests: read
jobs:
pr-title:
uses: folke/github/.github/workflows/pr.yml@main
secrets: inherit
================================================
FILE: .github/workflows/stale.yml
================================================
name: Stale Issues & PRs
on:
schedule:
- cron: "30 1 * * *"
jobs:
stale:
if: contains(fromJSON('["folke", "LazyVim"]'), github.repository_owner)
uses: folke/github/.github/workflows/stale.yml@main
secrets: inherit
================================================
FILE: .github/workflows/update.yml
================================================
name: Update Repo
on:
workflow_dispatch:
schedule:
# Run every hour
- cron: "0 * * * *"
jobs:
update:
if: contains(fromJSON('["folke", "LazyVim"]'), github.repository_owner)
uses: folke/github/.github/workflows/update.yml@main
secrets: inherit
================================================
FILE: .gitignore
================================================
*.log
/.repro
/.tests
/build
/debug
/doc/tags
foo.*
node_modules
tt.*
================================================
FILE: .lua-format
================================================
# https://github.com/Koihik/LuaFormatter/blob/master/docs/Style-Config.md
column_limit: 100
indent_width: 2
continuation_indent_width: 2
use_tab: false
chop_down_parameter: true
chop_down_table: true
chop_down_kv_table: true
single_quote_to_double_quote: true
spaces_inside_table_braces: true
align_parameter: true
keep_simple_control_block_one_line: true
extra_sep_at_table_end: true
================================================
FILE: .markdownlint-cli2.yaml
================================================
config:
MD013: false
MD033: false
================================================
FILE: .neoconf.json
================================================
{
"lspconfig": {
"sumneko_lua": {
"Lua.format.defaultConfig": {
"indent_style": "space",
"indent_size": "2",
"continuation_indent_size": "2"
},
"Lua.diagnostics.neededFileStatus": {
// "codestyle-check": "Any"
}
}
}
}
================================================
FILE: CHANGELOG.md
================================================
# Changelog
## [3.17.0](https://github.com/folke/which-key.nvim/compare/v3.16.0...v3.17.0) (2025-02-14)
### Features
* **mappings:** allow flagging a mapping as `real`. It will be hidden if there's no real keymap. ([bcfe1e4](https://github.com/folke/which-key.nvim/commit/bcfe1e4596dc0c6cc25a5b14b32f60a81d18c08d))
* **preset:** some extra win keymaps ([ff61f4f](https://github.com/folke/which-key.nvim/commit/ff61f4fe0d21de199c44a9d893395b5005e96270))
## [3.16.0](https://github.com/folke/which-key.nvim/compare/v3.15.0...v3.16.0) (2025-01-05)
### Features
* **icons:** add refactoring ([#923](https://github.com/folke/which-key.nvim/issues/923)) ([7b7e3d0](https://github.com/folke/which-key.nvim/commit/7b7e3d0788957592e12970bbb9e0e14c4cd143d4))
### Bug Fixes
* **mappings:** add missing keybinds for g; & g, ([#924](https://github.com/folke/which-key.nvim/issues/924)) ([57f4438](https://github.com/folke/which-key.nvim/commit/57f4438158e93be6856317e999758ed77f286dd9))
## [3.15.0](https://github.com/folke/which-key.nvim/compare/v3.14.1...v3.15.0) (2024-12-15)
### Features
* **icons:** icons for snacks and profiler ([d533d8a](https://github.com/folke/which-key.nvim/commit/d533d8a2e0da3444986500ecc2fb0528062d6003))
### Bug Fixes
* **config:** load early when executing WhichKey command. Fixes [#912](https://github.com/folke/which-key.nvim/issues/912) ([c890020](https://github.com/folke/which-key.nvim/commit/c8900201501ab6f0dcfabd55c70f6ba03ada3bd8))
## [3.14.1](https://github.com/folke/which-key.nvim/compare/v3.14.0...v3.14.1) (2024-11-28)
### Bug Fixes
* **state:** do proper redraw that works on nightly and on stable ([3a9b162](https://github.com/folke/which-key.nvim/commit/3a9b162026a4ad4b9ee7b09009b8bbe69ba19520))
## [3.14.0](https://github.com/folke/which-key.nvim/compare/v3.13.3...v3.14.0) (2024-11-28)
### Features
* **icons:** add grapple icon ([#838](https://github.com/folke/which-key.nvim/issues/838)) ([c21b71f](https://github.com/folke/which-key.nvim/commit/c21b71ff0bc4e516705811ec6916131e880cb882))
### Bug Fixes
* **state:** use redraw flush to prevent issues with selecting visual line etc. Fixes [#898](https://github.com/folke/which-key.nvim/issues/898) ([3974c2d](https://github.com/folke/which-key.nvim/commit/3974c2d21b117236ec4f5be4e61a9e4f02aa4c46))
* **triggers:** when in macro defer re-checking suspended for 100ms. Fixes [#864](https://github.com/folke/which-key.nvim/issues/864) ([f46556b](https://github.com/folke/which-key.nvim/commit/f46556b2b1bb7dbbd3b1086eaa24ca5db52b1986))
### Performance Improvements
* **state:** only redraw when waiting for a key longer than 200ms ([1c5aeba](https://github.com/folke/which-key.nvim/commit/1c5aeba42861a2cd446156ec8cbb7e7a5b5a9dfd))
* **tree:** small perf optims ([5610eb6](https://github.com/folke/which-key.nvim/commit/5610eb6bd7193e78d31eb399effacd2dfc25dedf))
## [3.13.3](https://github.com/folke/which-key.nvim/compare/v3.13.2...v3.13.3) (2024-09-18)
### Bug Fixes
* **config:** disable wk by default for terminal mode ([#825](https://github.com/folke/which-key.nvim/issues/825)) ([e7b415c](https://github.com/folke/which-key.nvim/commit/e7b415cc1d9ac9aee180ee5c8e46ca1484ebda78))
* **triggers:** never attach when macro is recording / executing. Fixes [#851](https://github.com/folke/which-key.nvim/issues/851). Fixes [#822](https://github.com/folke/which-key.nvim/issues/822). Fixes [#807](https://github.com/folke/which-key.nvim/issues/807) ([6b023b4](https://github.com/folke/which-key.nvim/commit/6b023b4c29ecc0aad06a51dd14bd2754b43bb0c8))
* **view:** display actual scroll up/down keys in help ([#821](https://github.com/folke/which-key.nvim/issues/821)) ([dafe27a](https://github.com/folke/which-key.nvim/commit/dafe27a06919bc5077db2ee97feec54d0932450e))
## [3.13.2](https://github.com/folke/which-key.nvim/compare/v3.13.1...v3.13.2) (2024-07-24)
### Bug Fixes
* **view:** fix epanded keys. Fixes [#795](https://github.com/folke/which-key.nvim/issues/795) ([f5e0cd5](https://github.com/folke/which-key.nvim/commit/f5e0cd5c7712ac63d8e6184785fb7bdac3b7b50d))
## [3.13.1](https://github.com/folke/which-key.nvim/compare/v3.13.0...v3.13.1) (2024-07-24)
### Bug Fixes
* **state:** better current buf/mode check ([711453b](https://github.com/folke/which-key.nvim/commit/711453bb945433362636e918a5238172ffd21e43))
* **state:** deal with the fact that ModeChanged doesn't always seems to trigger. Fixes [#787](https://github.com/folke/which-key.nvim/issues/787) ([388bd3f](https://github.com/folke/which-key.nvim/commit/388bd3f83de06d1a1758ea6a342cf3ae614401f1))
## [3.13.0](https://github.com/folke/which-key.nvim/compare/v3.12.1...v3.13.0) (2024-07-24)
### Features
* **debug:** add git info to log when using lazy ([550338d](https://github.com/folke/which-key.nvim/commit/550338dc292c014d83687afccb0afb06e3e769f2))
## [3.12.1](https://github.com/folke/which-key.nvim/compare/v3.12.0...v3.12.1) (2024-07-24)
### Bug Fixes
* **node:** dynamic mappings only support functions as rhs. Fixes [#790](https://github.com/folke/which-key.nvim/issues/790) ([ba91db7](https://github.com/folke/which-key.nvim/commit/ba91db72ffc745983f06ca4e7d969101287a9afe))
* **state:** use cached mode. Fixes [#787](https://github.com/folke/which-key.nvim/issues/787). Closes [#789](https://github.com/folke/which-key.nvim/issues/789) ([c1b062a](https://github.com/folke/which-key.nvim/commit/c1b062ae95c3ca3e6eb87c075da991523605d79b))
* **triggers:** check for existing keymaps in the correct buffer. Fixes [#783](https://github.com/folke/which-key.nvim/issues/783) ([977fa23](https://github.com/folke/which-key.nvim/commit/977fa23622425e3c8ae837b9f7c710d9c78bdeab))
* **triggers:** nil error ([dae3bd2](https://github.com/folke/which-key.nvim/commit/dae3bd271826887771a7fb6deec231d2eb344f02))
## [3.12.0](https://github.com/folke/which-key.nvim/compare/v3.11.1...v3.12.0) (2024-07-22)
### Features
* **api:** allow overriding delay. Closes [#778](https://github.com/folke/which-key.nvim/issues/778) ([e6fea48](https://github.com/folke/which-key.nvim/commit/e6fea4889c20f22a7c6267cf1f1d091bc96f8ca0))
### Bug Fixes
* dont expand nodes without children. Fixes [#782](https://github.com/folke/which-key.nvim/issues/782) ([53a1d2a](https://github.com/folke/which-key.nvim/commit/53a1d2a674df5fb667497fe3bbda625c39a2c0e1))
## [3.11.1](https://github.com/folke/which-key.nvim/compare/v3.11.0...v3.11.1) (2024-07-21)
### Bug Fixes
* **config:** keys can be any case. Fixes [#771](https://github.com/folke/which-key.nvim/issues/771) ([0a44d55](https://github.com/folke/which-key.nvim/commit/0a44d55d3bcdf75a134ec57c90aaec1055731014))
* **config:** normalize keys ([c96be9b](https://github.com/folke/which-key.nvim/commit/c96be9bd54ffbc2ec7fc818001cad712119d778c))
## [3.11.0](https://github.com/folke/which-key.nvim/compare/v3.10.0...v3.11.0) (2024-07-20)
### Features
* **icons:** icon for grug-far ([b2a2a0c](https://github.com/folke/which-key.nvim/commit/b2a2a0c9486211da23acdf18087f8203bfbca0e4))
* **state:** detect recursion by users mapping wk manually. Closes [#761](https://github.com/folke/which-key.nvim/issues/761) ([55fa07f](https://github.com/folke/which-key.nvim/commit/55fa07fbbd8a4c6d75399b1d1f9005d146cda22c))
* **view:** expand recursively. Closes [#767](https://github.com/folke/which-key.nvim/issues/767) ([5ae87af](https://github.com/folke/which-key.nvim/commit/5ae87af42914afe8b478ad6cdb3cb179fb73a62b))
### Bug Fixes
* **config:** disable wk by default for insert/command mode ([9d2b2e7](https://github.com/folke/which-key.nvim/commit/9d2b2e7059547c0481db2e93fd98547b26c7c05a))
* **config:** more checks in validate ([bdcc429](https://github.com/folke/which-key.nvim/commit/bdcc429afaecc5896b462b0b07c2b3a9e9c1c60f))
* **mappings:** preset descriptions should not override existing keymap descriptions. Fixes [#769](https://github.com/folke/which-key.nvim/issues/769) ([82d628f](https://github.com/folke/which-key.nvim/commit/82d628f4cfa397cf4bb233bd500e9cd9a018ded1))
* **state:** don't feed count in insert mode. Fixes [#770](https://github.com/folke/which-key.nvim/issues/770) ([63690ff](https://github.com/folke/which-key.nvim/commit/63690ff34a8921c2de44fad289e2e04ee324b031))
* **util:** better `<nop>` check. Fixes [#766](https://github.com/folke/which-key.nvim/issues/766) ([b7b3bd1](https://github.com/folke/which-key.nvim/commit/b7b3bd1b609524472f67e4a69d2bc14b80ea997f))
* **view:** dont set title when no border. Fixes [#764](https://github.com/folke/which-key.nvim/issues/764) ([6e61b09](https://github.com/folke/which-key.nvim/commit/6e61b0904e9c038b6c511c43591ae2d811b4975e))
### Performance Improvements
* prevent expanding nodes when not needed ([78cc92c](https://github.com/folke/which-key.nvim/commit/78cc92c6cb7da88df60227bc334a598a7e772e51))
## [3.10.0](https://github.com/folke/which-key.nvim/compare/v3.9.0...v3.10.0) (2024-07-18)
### Features
* **view:** expand all nodes by default when filter.global = false ([c168905](https://github.com/folke/which-key.nvim/commit/c168905d62d9b8859b261de69910dfb7e3438996))
### Bug Fixes
* **buf:** always use nowait. Fixes [#755](https://github.com/folke/which-key.nvim/issues/755) ([ae1a235](https://github.com/folke/which-key.nvim/commit/ae1a235c53233c58a2f7cc14e5cdd09346cf27ed))
* **buf:** early exit to determine if a trigger is safe to create. Fixes [#754](https://github.com/folke/which-key.nvim/issues/754) ([27e4716](https://github.com/folke/which-key.nvim/commit/27e47163165fee8e45b43d340db9335001403d2f))
* **icons:** added frontier pattern for `ai` ([#760](https://github.com/folke/which-key.nvim/issues/760)) ([6fe0657](https://github.com/folke/which-key.nvim/commit/6fe065716e08550328c471689e6f8c1e42a0effc))
* list_contains doesn't exists in Neovim < 0.10. Fixes [#758](https://github.com/folke/which-key.nvim/issues/758) ([7e4eae8](https://github.com/folke/which-key.nvim/commit/7e4eae8836e4ad28d478fedc421700b1138d1e0c))
* **node:** allow custom mappings to override proxy/plugin/expand mappings ([9820900](https://github.com/folke/which-key.nvim/commit/982090080fa11da06038cf8e71af90d3a4fbd05a))
* **node:** is_local check now also includes children ([fdd27f9](https://github.com/folke/which-key.nvim/commit/fdd27f9b6a991586943eb865275b279fb411ff0b))
* **registers:** don't try to get `+*` registers when no clipboard is available. Fixes [#754](https://github.com/folke/which-key.nvim/issues/754) ([ae4ec03](https://github.com/folke/which-key.nvim/commit/ae4ec030489d7ecda908e473aea096a7594f84e8))
* **state:** always honor defer. Fixes [#690](https://github.com/folke/which-key.nvim/issues/690) ([c512d13](https://github.com/folke/which-key.nvim/commit/c512d135531be81e17c85e254994cc755d3016c5))
* **state:** redraw cursor before getchar ([cf6cbf2](https://github.com/folke/which-key.nvim/commit/cf6cbf2fd8f0c6497f130d07f6c88a2833c15d80))
* **triggers:** prevent creating triggers for single upper-case alpha keys expect for Z. Fixes [#756](https://github.com/folke/which-key.nvim/issues/756) ([d19fa07](https://github.com/folke/which-key.nvim/commit/d19fa07b6e818ab55c34815784470a6d5f023524))
## [3.9.0](https://github.com/folke/which-key.nvim/compare/v3.8.0...v3.9.0) (2024-07-18)
### Features
* **config:** simplified config. Some options are now deprecated ([8ddf2da](https://github.com/folke/which-key.nvim/commit/8ddf2da5a6aa76f5b3cec976f1d61e7c7fea42b5))
* **view:** show and expand localleader mappings with filter.global = false ([ed5f762](https://github.com/folke/which-key.nvim/commit/ed5f7622771d0b5c0ac3a5e286ec6cd17b6be131))
### Bug Fixes
* **ui:** remove deprecated opts.layout.align option. (wasn't used). Closes [#752](https://github.com/folke/which-key.nvim/issues/752) ([db32ac6](https://github.com/folke/which-key.nvim/commit/db32ac67abb36789a43fe497ff7d0b8ab7e8109e))
## [3.8.0](https://github.com/folke/which-key.nvim/compare/v3.7.0...v3.8.0) (2024-07-17)
### Features
* **mappings:** added health check for invalid modes ([640724a](https://github.com/folke/which-key.nvim/commit/640724a541af75e6bbfe98f78cdebbec701d23a8))
### Bug Fixes
* **buf:** never create proxy/plugin mappings when a keymap exists. Fixes [#738](https://github.com/folke/which-key.nvim/issues/738) ([b4c4e36](https://github.com/folke/which-key.nvim/commit/b4c4e3648261399a97bfdc44bb8fa31b485fd3b9))
* **registers:** use x instead of v ([#742](https://github.com/folke/which-key.nvim/issues/742)) ([5c3b3e8](https://github.com/folke/which-key.nvim/commit/5c3b3e834852a44efb26725f9c08917145f2c0c6))
* **state:** schedule redraw. Fixes [#740](https://github.com/folke/which-key.nvim/issues/740) ([09f21a1](https://github.com/folke/which-key.nvim/commit/09f21a133104b66a5cede8fc0a8082b85b0eee9b))
* **triggers:** allow overriding keymaps with empty rhs or <Nop>. Fixes [#748](https://github.com/folke/which-key.nvim/issues/748) ([843a93f](https://github.com/folke/which-key.nvim/commit/843a93fac6bca58167aafa392e6f7fd5a77633c9))
* **triggers:** make sure no keymaps exists for triggers ([e8b454f](https://github.com/folke/which-key.nvim/commit/e8b454fb03e3cab398c894e5d462c84595ee57ca))
* **typo:** replace 'exras' for 'extras' in README. ([#745](https://github.com/folke/which-key.nvim/issues/745)) ([af48cdc](https://github.com/folke/which-key.nvim/commit/af48cdc4bb8f1982a6124bf6bb5570349f690822))
## [3.7.0](https://github.com/folke/which-key.nvim/compare/v3.6.0...v3.7.0) (2024-07-17)
### Features
* added `expand` property to create dynamic mappings. An example for `buf` and `win` is included ([02f6e6f](https://github.com/folke/which-key.nvim/commit/02f6e6f4951ff993ad1d5c699784e6847a6c7b4c))
* proxy mappings ([c3cfc2b](https://github.com/folke/which-key.nvim/commit/c3cfc2bdb03c1b87943a6d02485ad50b86567341))
* **state:** allow defering on certain operators. Closes [#733](https://github.com/folke/which-key.nvim/issues/733) ([984d930](https://github.com/folke/which-key.nvim/commit/984d930711341ac118e6712804e8e22e575ba9d3))
### Bug Fixes
* **buf:** create triggers for xo anyway. Fixes [#728](https://github.com/folke/which-key.nvim/issues/728) ([96b2e93](https://github.com/folke/which-key.nvim/commit/96b2e93979373744056c921f82b0c356e6f900de))
* **icons:** added frontier pattern for `git`. Fixes [#727](https://github.com/folke/which-key.nvim/issues/727) ([bb4e82b](https://github.com/folke/which-key.nvim/commit/bb4e82bdaff50a4a93867e4c90938d18e7615af6))
* **state:** dont popup when switching between v and V mode. Fixes [#729](https://github.com/folke/which-key.nvim/issues/729) ([8ddb527](https://github.com/folke/which-key.nvim/commit/8ddb527bcffc6957a59518f11c34a84d91e075f9))
* **view:** always show a group as a group ([96a9eb3](https://github.com/folke/which-key.nvim/commit/96a9eb3f0b3299dffc241cf0f9ee5cf0509e6cd2))
* **view:** empty icons ([e2cacc6](https://github.com/folke/which-key.nvim/commit/e2cacc6f1e4ba77e82e7a34e0dc6b2ad69cf075b))
## [3.6.0](https://github.com/folke/which-key.nvim/compare/v3.5.0...v3.6.0) (2024-07-16)
### Features
* added icons for <D mappings ([aaf71ab](https://github.com/folke/which-key.nvim/commit/aaf71ab078d86a48a26fafb5d451af609fd19c64))
* added option to disable all mapping icons. Fixes [#721](https://github.com/folke/which-key.nvim/issues/721) ([33f6ac0](https://github.com/folke/which-key.nvim/commit/33f6ac04bdbce855ce43eecacb4c421876e246d7))
* make which-key work without setup or calling add/register ([9ca5f4a](https://github.com/folke/which-key.nvim/commit/9ca5f4ab7cb541ef48dcaa4f03d3cd914a5e62fb))
* **presets:** added some missing mappings ([6e1b3f2](https://github.com/folke/which-key.nvim/commit/6e1b3f290a3f89ffca68148aa639c866c24e2b77))
* **state:** improve trigger/mode logic. Fixes [#715](https://github.com/folke/which-key.nvim/issues/715) ([3617e47](https://github.com/folke/which-key.nvim/commit/3617e47673d027989e9c3caa645edb6412c7fa30))
### Bug Fixes
* **config:** replacement for plug mappings ([495f9d9](https://github.com/folke/which-key.nvim/commit/495f9d953a86d630ef308f555ed452e332f417ee))
* **icons:** get icons from parent nodes when needed ([3f0a7ed](https://github.com/folke/which-key.nvim/commit/3f0a7ed4401b98764740cbe8e1b954ac6adeca1b))
* **icons:** use nerdfont symbol for BS. Fixes [#722](https://github.com/folke/which-key.nvim/issues/722) ([18c1ff5](https://github.com/folke/which-key.nvim/commit/18c1ff5ccb813d95c86f4ead6dac7e6cc5728f08))
* **mode:** never create triggers for xo mode ([15d3a70](https://github.com/folke/which-key.nvim/commit/15d3a70304607417b2dc1df3da4992d5b8ce077a))
* **presets:** motions in normal mode ([e2ffc26](https://github.com/folke/which-key.nvim/commit/e2ffc263fc05bf20f090ccaae7a06f88fd6e2fee))
* tmp fix for op mode ([91641e2](https://github.com/folke/which-key.nvim/commit/91641e2a3af116ffaf739302a65cdb2865fb2415))
* **view:** fix format for keymaps with 3+ keys ([#723](https://github.com/folke/which-key.nvim/issues/723)) ([0db7896](https://github.com/folke/which-key.nvim/commit/0db7896057d046576c829a87e2ff2de37c49e0fe))
## [3.5.0](https://github.com/folke/which-key.nvim/compare/v3.4.0...v3.5.0) (2024-07-15)
### Features
* **api:** using wk.show() always assumes you want to see the group, and not the actual mapping in case of overlap. Fixes [#714](https://github.com/folke/which-key.nvim/issues/714) ([f5067d2](https://github.com/folke/which-key.nvim/commit/f5067d2b244c19eca38b5b495b6eb3e361ac565d))
### Bug Fixes
* **state:** attach on BufNew as well. Fixes [#681](https://github.com/folke/which-key.nvim/issues/681) ([0f58176](https://github.com/folke/which-key.nvim/commit/0f581764dc2c89c0ac3d8363369152735ae265ab))
* **state:** make sure mode always exists even when not safe. See [#681](https://github.com/folke/which-key.nvim/issues/681) ([7915964](https://github.com/folke/which-key.nvim/commit/7915964e73c30ba5657e9a762c6570925dad421b))
### Performance Improvements
* **plugin:** only expand plugins when needed ([1fcfc72](https://github.com/folke/which-key.nvim/commit/1fcfc72374c705d68f0607a1dcbbbce13873b4e2))
* **view:** set buf/win opts with eventignore ([e81e55b](https://github.com/folke/which-key.nvim/commit/e81e55b647a781f306453734834eb543e1f43c20))
## [3.4.0](https://github.com/folke/which-key.nvim/compare/v3.3.0...v3.4.0) (2024-07-15)
### Features
* added icons for function keys ([9222280](https://github.com/folke/which-key.nvim/commit/9222280970e8a7d74b4e0f6dab06c2f7a54d668d))
* **mode:** allow certain modes to start hidden and only show after keypress. See [#690](https://github.com/folke/which-key.nvim/issues/690) ([b4fa48f](https://github.com/folke/which-key.nvim/commit/b4fa48f473796f5d9e3c9c31e6c9d7d509e51ca6))
* **presets:** added gw ([09b80a6](https://github.com/folke/which-key.nvim/commit/09b80a68085c1fc792b595a851f702bc071d6310))
* **presets:** better padding defaults for helix preset ([4c36b9b](https://github.com/folke/which-key.nvim/commit/4c36b9b8c722bcf51d038dcfba8b967f0ee818b8))
* **presets:** increase default height for helix ([df0ad20](https://github.com/folke/which-key.nvim/commit/df0ad205ebd661ef101666ae21a62b77b3024a83))
* simplified/documented/fixed mappings sorting. Closes [#694](https://github.com/folke/which-key.nvim/issues/694) ([eb73f7c](https://github.com/folke/which-key.nvim/commit/eb73f7c05785a83e07f1ea155b3b2833d8bbb532))
* **state:** skip mouse keys in debug ([5f85b77](https://github.com/folke/which-key.nvim/commit/5f85b770c386c9435eb8da5db3081aa19078211a))
* **ui:** show keys/help in an overlay and added scrolling hint ([50b2c43](https://github.com/folke/which-key.nvim/commit/50b2c43532e6ea5cca3ef4c2838d5a8bb535757f))
* **view:** get parent icon if possible ([b9de927](https://github.com/folke/which-key.nvim/commit/b9de9278bdc57adfa69a67d8a3309f07c83951d0))
### Bug Fixes
* **buf:** always detach " when executing keys. Fixes [#689](https://github.com/folke/which-key.nvim/issues/689) ([d36f722](https://github.com/folke/which-key.nvim/commit/d36f722f114dfdafc8098496e9b5dcbd9f8fc3e8))
* **config:** set expand=0 by default. Fixes [#693](https://github.com/folke/which-key.nvim/issues/693) ([89434aa](https://github.com/folke/which-key.nvim/commit/89434aa356abd4a694d2b89eccb203e0742bc0d7))
* **config:** warn when deprecated config options were used. Fixes [#696](https://github.com/folke/which-key.nvim/issues/696) ([81413ef](https://github.com/folke/which-key.nvim/commit/81413ef02dffbe6e4c73f418e4acc920e68b3aa7))
* **health:** move deprecated option check to health ([af7a30f](https://github.com/folke/which-key.nvim/commit/af7a30fa24ce0a13dba00cbd7b836330facf9f1a))
* **mappings:** allow creating keymaps without desc. Fixes [#695](https://github.com/folke/which-key.nvim/issues/695) ([c442aaa](https://github.com/folke/which-key.nvim/commit/c442aaa6aafe2742c2e92df7ee127df90099ce17))
* **plugins:** add existing keymaps to plugin view. Fixes [#681](https://github.com/folke/which-key.nvim/issues/681) ([26f6fd2](https://github.com/folke/which-key.nvim/commit/26f6fd258b66e9656bb86c7269c6497a9ce8a5fa))
* **presets:** don't override title setting for classic. See [#649](https://github.com/folke/which-key.nvim/issues/649) ([9a53c1f](https://github.com/folke/which-key.nvim/commit/9a53c1ff46421450b5563baab1599591d81de111))
* **presets:** shorter descriptions ([20600e4](https://github.com/folke/which-key.nvim/commit/20600e422277b383e8c921feec2111a281935217))
* **state:** always do full update on BufReadPost since buffer-local keymaps would be deleted. Fixes [#709](https://github.com/folke/which-key.nvim/issues/709) ([6068887](https://github.com/folke/which-key.nvim/commit/60688872f4ecc552a5e2bcbd01e7629a155f377f))
* **state:** don't show when coming from cmdline mode. Fixes [#692](https://github.com/folke/which-key.nvim/issues/692) ([8cba66b](https://github.com/folke/which-key.nvim/commit/8cba66b5a1a0ea8fe8dd5d3d55a42755924e47d8))
* **state:** honor timeoutlen and nowait. Fixes [#648](https://github.com/folke/which-key.nvim/issues/648). Closes [#697](https://github.com/folke/which-key.nvim/issues/697) ([80f20ee](https://github.com/folke/which-key.nvim/commit/80f20ee62311505fe6d675212f7b246900570450))
* **state:** properly disable which-key when recording macros. Fixes [#702](https://github.com/folke/which-key.nvim/issues/702) ([b506275](https://github.com/folke/which-key.nvim/commit/b506275acfb4383f678b9ba3aa8db88787c24680))
* **state:** scrolling ([dce9167](https://github.com/folke/which-key.nvim/commit/dce9167025a0801e4bab146a2856508a9af52ea2))
* **tree:** rawget for existing plugin node children ([c77cda8](https://github.com/folke/which-key.nvim/commit/c77cda8cd2f54965e4316699f1d124a2b3bf9d49))
* **util:** when no clipboard provider exists, use the " register as default. Fixes [#687](https://github.com/folke/which-key.nvim/issues/687) ([d077a3f](https://github.com/folke/which-key.nvim/commit/d077a3f36d4b4d29eccc7feb1ba8e78a421df920))
* **view:** disable footer on Neovim < 0.10 ([6d544a4](https://github.com/folke/which-key.nvim/commit/6d544a43a21a228482155d65c3ca18fd7038b422))
* **view:** ensure highlights get set for title padding ([#684](https://github.com/folke/which-key.nvim/issues/684)) ([2e4f7af](https://github.com/folke/which-key.nvim/commit/2e4f7afa4aa444483d8ade5989d524c7f4131368))
* **view:** hide existing title/footer when no trail ([4f589a1](https://github.com/folke/which-key.nvim/commit/4f589a1368e100a6e33aabd904f34716b75360f6))
* **view:** include group keymaps in expand results. See [#682](https://github.com/folke/which-key.nvim/issues/682) ([39e703c](https://github.com/folke/which-key.nvim/commit/39e703ceaa9a05dcc664e0ab0ea88c03c3b6bf90))
* **view:** overlap protection should keep at least 4 lines ([0d89475](https://github.com/folke/which-key.nvim/commit/0d89475f87756199efc2bc52537fc4d11b0f695a))
* **view:** padding & column spacing. Fixes [#704](https://github.com/folke/which-key.nvim/issues/704) ([11eec49](https://github.com/folke/which-key.nvim/commit/11eec49509490c023bf0272efef955f86f18c1d2))
* **view:** spacing when more than one box ([89568f3](https://github.com/folke/which-key.nvim/commit/89568f3438f1fbc6c340a8af05ea67feac494c46))
* **view:** special handling of `<NL>/<C-J>`. Fixes [#706](https://github.com/folke/which-key.nvim/issues/706) ([f8c91b2](https://github.com/folke/which-key.nvim/commit/f8c91b2b4a2d239d3b1d49f901a393e7326a5da8))
## [3.3.0](https://github.com/folke/which-key.nvim/compare/v3.2.0...v3.3.0) (2024-07-14)
### Features
* **expand:** allow expand to be a function. Closes [#670](https://github.com/folke/which-key.nvim/issues/670) ([dfaa10c](https://github.com/folke/which-key.nvim/commit/dfaa10cd24badb321a4667fb9135f242393e5680))
* **mappings:** mapping `desc` and `icon` can now be a function that is evaluated when which-key is show. Fixes [#666](https://github.com/folke/which-key.nvim/issues/666) ([c634af1](https://github.com/folke/which-key.nvim/commit/c634af1295512dc2062fbec38f563f5793de245c))
* **mappings:** opts.filter to exclude certain mappings from showing up in which-key. ([763ea00](https://github.com/folke/which-key.nvim/commit/763ea000cce9589124515ba34f6d9a6347a02891))
* **view:** add operator to trail in op mode ([5a6eaaa](https://github.com/folke/which-key.nvim/commit/5a6eaaa4ebc072625b9fc906943e3798028bd817))
* **view:** when in visual mode, propagate esc. See [#656](https://github.com/folke/which-key.nvim/issues/656) ([30ef44a](https://github.com/folke/which-key.nvim/commit/30ef44a13065a157f97d3fb5bbf23a5c23e513eb))
### Bug Fixes
* default preset ([38987d3](https://github.com/folke/which-key.nvim/commit/38987d3f18a8ffc5eaa404d746fd8ee4017b5f37))
* **mappings:** don't show `<SNR>` mappings ([d700244](https://github.com/folke/which-key.nvim/commit/d700244acc1f1474b34737e14a45df2aa3a324ba))
* **presets:** max 1 column in helix mode. Fixes [#665](https://github.com/folke/which-key.nvim/issues/665) ([b2a6910](https://github.com/folke/which-key.nvim/commit/b2a6910e9e97526f2327327d2751834049cbd334))
* **presets:** shorter descriptions ([9a73d6a](https://github.com/folke/which-key.nvim/commit/9a73d6a0b0d5f456a9768d434a83d6d4cdb83efa))
* **state:** cooldown till next tick when not safe to open which-key. Fixes [#672](https://github.com/folke/which-key.nvim/issues/672) ([bdf3b27](https://github.com/folke/which-key.nvim/commit/bdf3b272ea34ac137af3cb1ebcd5cf8c9745abbb))
* **util:** nt mode should map to n ([969afc9](https://github.com/folke/which-key.nvim/commit/969afc95d374bc0d6ce397d3d2357d8faa38041a))
* **view:** set nowrap for the which-key window ([6e1c098](https://github.com/folke/which-key.nvim/commit/6e1c0987024adf63ab91f281f8f9c355abf3f3d8))
* **view:** set winhl groups. Fixes [#661](https://github.com/folke/which-key.nvim/issues/661) ([baff8ea](https://github.com/folke/which-key.nvim/commit/baff8ea846cbb613dee79333aad7a1d2b912a5bc))
## [3.2.0](https://github.com/folke/which-key.nvim/compare/v3.1.0...v3.2.0) (2024-07-13)
### Features
* added `opts.debug` that writes to wk.log in the current directory ([c23df71](https://github.com/folke/which-key.nvim/commit/c23df711884d97963d0c17ed29f5d8c1064d4adc))
* hydra mode. will document later ([65f2e72](https://github.com/folke/which-key.nvim/commit/65f2e7236a3bc278dd163d7c98c9ea5d9ab6e42e))
* **icons:** add telescope icon ([#643](https://github.com/folke/which-key.nvim/issues/643)) ([fca3d9e](https://github.com/folke/which-key.nvim/commit/fca3d9eaef57ddb3ce438d208ebc32e23c9f290a))
### Bug Fixes
* layout stuff ([7423096](https://github.com/folke/which-key.nvim/commit/742309697cff6aa7f377b72e2f54d34afef09ee1))
* **mappings:** always use mapping even when it's creating a keymap. Fixes [#637](https://github.com/folke/which-key.nvim/issues/637) ([2d744cb](https://github.com/folke/which-key.nvim/commit/2d744cb824c0f310be420bf33688bc005f164f46))
* **mappings:** make replace_keycodes default to false in v1 spec ([6ec0a1e](https://github.com/folke/which-key.nvim/commit/6ec0a1ef89209680c799269227b4d0c28de1d877))
* **state:** dont start which-key during dot repeat. Fixes [#636](https://github.com/folke/which-key.nvim/issues/636) ([5971ecd](https://github.com/folke/which-key.nvim/commit/5971ecdf4465425d6bc6e2277101c6fc896cbe06))
* **state:** dont start which-key more than once during the same tick in xo mode. Fixes [#635](https://github.com/folke/which-key.nvim/issues/635) ([0218fce](https://github.com/folke/which-key.nvim/commit/0218fce1c3d54307217391215db28e63de9b8980))
* **state:** dont start wk when chars are pending. Fixes [#658](https://github.com/folke/which-key.nvim/issues/658). Fixes [#655](https://github.com/folke/which-key.nvim/issues/655). Fixes [#648](https://github.com/folke/which-key.nvim/issues/648) ([877ce16](https://github.com/folke/which-key.nvim/commit/877ce163d764bbe7c82a7fec5671c32188607754))
* **state:** only hide on focus lost when still hidden after 1s. Fixes [#638](https://github.com/folke/which-key.nvim/issues/638) ([649a51b](https://github.com/folke/which-key.nvim/commit/649a51bc81b09443c326d390e3d182e0cdf98c15))
* **types:** spec field types ([#645](https://github.com/folke/which-key.nvim/issues/645)) ([c6ffb1c](https://github.com/folke/which-key.nvim/commit/c6ffb1ce63959d5f1effe5924712f36eac1e940e))
* **util:** set local window opts for notify. Fixes [#641](https://github.com/folke/which-key.nvim/issues/641) ([63f2112](https://github.com/folke/which-key.nvim/commit/63f2112361a53b0cf68245868977773f210bb5cd))
* **view:** check for real overlap instead of just row overlap. See [#649](https://github.com/folke/which-key.nvim/issues/649) ([0427e91](https://github.com/folke/which-key.nvim/commit/0427e91dbbd9c37eb20e6fbc2386f890dc0d7e2a))
* **view:** disable folds. Fixes [#99](https://github.com/folke/which-key.nvim/issues/99) ([6860e3b](https://github.com/folke/which-key.nvim/commit/6860e3b681b40e3620049f714ae53a6bad594701))
## [3.1.0](https://github.com/folke/which-key.nvim/compare/v3.0.0...v3.1.0) (2024-07-12)
### Features
* allow disabling any trigger ([94b7951](https://github.com/folke/which-key.nvim/commit/94b795154fb213db6ed8aeba3d7f53cbce7c147c))
### Bug Fixes
* added support for vim.loop ([54db192](https://github.com/folke/which-key.nvim/commit/54db1928c17ac420e897a40f5ad560ee9f28b186))
* automatically do setup if setup wasn't called within 500ms. Fixes [#630](https://github.com/folke/which-key.nvim/issues/630) ([632ad41](https://github.com/folke/which-key.nvim/commit/632ad41b5fcf60fac897d0b6530a699eb980748d))
* **buf:** buffer-local mappings were broken (not keymaps). Fixes [#629](https://github.com/folke/which-key.nvim/issues/629) ([58d7f82](https://github.com/folke/which-key.nvim/commit/58d7f822ecc80ca4b43e9c14fd6ec962483e2168))
* **colors:** compat with older Neovim vesions. Fixes [#631](https://github.com/folke/which-key.nvim/issues/631) ([4516dc9](https://github.com/folke/which-key.nvim/commit/4516dc9422f571c9e189ff6696853d445a3058d6))
## [3.0.0](https://github.com/folke/which-key.nvim/compare/v2.1.0...v3.0.0) (2024-07-12)
### ⚠ BREAKING CHANGES
* v3 release
### Features
* added health check back with better wording on what actually gets checked ([97e6e41](https://github.com/folke/which-key.nvim/commit/97e6e4166134aad826454588ae764c7a54f5d298))
* added WhichKey command ([7c12ab9](https://github.com/folke/which-key.nvim/commit/7c12ab9c2569a7459932bc19a4e52ea5a48437b2))
* automatically use nowait based on delay and timeoutlen ([110ed72](https://github.com/folke/which-key.nvim/commit/110ed728bedd0182e4d11726194f7eb5db63e2fb))
* bring config back and create mappings when needed ([add7ab9](https://github.com/folke/which-key.nvim/commit/add7ab92163399c47f7149c96387d382e9d8996b))
* buffer-local sort & refactor API ([14309d0](https://github.com/folke/which-key.nvim/commit/14309d0446dcc6a24421c56e914e06b1fe2d4f41))
* close which-key on FocusLost ([aa99460](https://github.com/folke/which-key.nvim/commit/aa99460e117d0348c7f1f77ab669398c04fcba6b))
* config, and presets ([541989d](https://github.com/folke/which-key.nvim/commit/541989db167e04eb3db24ba57decab0326614f0f))
* expand groups with less than n mappings. Closes [#374](https://github.com/folke/which-key.nvim/issues/374). Fixes [#90](https://github.com/folke/which-key.nvim/issues/90). Closes [#208](https://github.com/folke/which-key.nvim/issues/208) ([5caf057](https://github.com/folke/which-key.nvim/commit/5caf057b3a204a94d53b4b0200ce915463b4a922))
* fancy key icons ([e4d0134](https://github.com/folke/which-key.nvim/commit/e4d01347434b31e8a90720463076bbbeebbef199))
* fix hidden and empty groups ([afc4aa9](https://github.com/folke/which-key.nvim/commit/afc4aa96ae5671f5d4d14f332789dec72dd5db02))
* **health:** duplicate mappings check ([4762e06](https://github.com/folke/which-key.nvim/commit/4762e06f9dc45b3470ab5b2efa0a4b3de6148298))
* **health:** icon providers & overlapping keys ([dcbf29a](https://github.com/folke/which-key.nvim/commit/dcbf29ae337bd4d621e326b6f1caad66cfe0770a))
* initial rewrite ([eb3ad2e](https://github.com/folke/which-key.nvim/commit/eb3ad2eb062392497d0fed3489e2582d4e5bc289))
* keep track of virtual mappings ([4537d3e](https://github.com/folke/which-key.nvim/commit/4537d3ea52b2b11b96ca2fdde2bb4573f0ca7c73))
* key/desc replacements ([cf34ffe](https://github.com/folke/which-key.nvim/commit/cf34ffe9384941dc833ed2a3bb2a3bf3aa050373))
* layout ([347288a](https://github.com/folke/which-key.nvim/commit/347288acd8398ae7c641bd6159261e98f9a6b929))
* manual sorting. Closes [#131](https://github.com/folke/which-key.nvim/issues/131), Closes [#362](https://github.com/folke/which-key.nvim/issues/362), Closes [#264](https://github.com/folke/which-key.nvim/issues/264) ([c2daf9d](https://github.com/folke/which-key.nvim/commit/c2daf9dcf48e8c8cca61cfc27b1731272b9bc2c6))
* **mappings:** added support for lazy.nvim style mappings ([6f7a945](https://github.com/folke/which-key.nvim/commit/6f7a945f1dc679ce2c35064e12e4dc531ebf2c3c))
* **mappings:** added support for setting custom icons from the spec ([951ae7a](https://github.com/folke/which-key.nvim/commit/951ae7a89d164f39f8aa49f51da424539370f6c4))
* new spec and migration recommendation for health ([41374bc](https://github.com/folke/which-key.nvim/commit/41374bcae462d897fa98c904a44127e258c0438c))
* option to disable icon colors ([79c8ac8](https://github.com/folke/which-key.nvim/commit/79c8ac87139dcb816072c1a5ca1800d9ce5d64aa))
* option to disable notify ([4cc46ff](https://github.com/folke/which-key.nvim/commit/4cc46ffa57b8a6ebf6ca7a07128d353f5569a802))
* play nice with macros ([1abc2bf](https://github.com/folke/which-key.nvim/commit/1abc2bf96472e7816252719de06d60e9b09035dc))
* plugins partially working again ([b925b31](https://github.com/folke/which-key.nvim/commit/b925b31bab1f91507d15a96f226f7f7423c4fced))
* **registers:** show non-printable with keytrans ([1832197](https://github.com/folke/which-key.nvim/commit/183219772d01e0ea744c0ff8bf656895f7d7c8d3))
* spec parser rewrite & proper typings ([07065fe](https://github.com/folke/which-key.nvim/commit/07065fe345bc9dd20aff11ab9a6a3b078aacd42e))
* state management ([e2ee1fa](https://github.com/folke/which-key.nvim/commit/e2ee1fae13f7a6c38652994dedb0cb34e2608918))
* state management ([e6beb88](https://github.com/folke/which-key.nvim/commit/e6beb8845e80558194c6027b7a985e1211e76878))
* title trail ([aef2e53](https://github.com/folke/which-key.nvim/commit/aef2e535c5b7c8f100b534a4b781a82e36f20e39))
* **ui:** added scrolling ([5f1ab35](https://github.com/folke/which-key.nvim/commit/5f1ab35d099a252f204e2806747980c192a9c265))
* **ui:** keymap icons ([21d7108](https://github.com/folke/which-key.nvim/commit/21d71081d86872189a3ce90b7c13593f15b78459))
* **ui:** sorters ([ffeea79](https://github.com/folke/which-key.nvim/commit/ffeea7933249d5ce33b2b3838171cc5299ef1893))
* update ui when new mappings become available ([a8f66f5](https://github.com/folke/which-key.nvim/commit/a8f66f5ebd9b94f409a88c4a77244167f6edd05f))
* v3 release ([da258a8](https://github.com/folke/which-key.nvim/commit/da258a89a700916ad0e6af1ad8f9889ff0308253))
* **view:** nerd font icons for cmd keys ([2787dbd](https://github.com/folke/which-key.nvim/commit/2787dbd158184af67ead5af5bcc0cbdb17856c31))
### Bug Fixes
* **api:** show view immediately when opened through the API ([b0e0af0](https://github.com/folke/which-key.nvim/commit/b0e0af0957a648735a43fae52ef34059721f7b42))
* autmatically blacklist all single key hooks except for z and g ([87c5a4b](https://github.com/folke/which-key.nvim/commit/87c5a4b1be1f882c8b27252464d777a76ea15839))
* **icons:** check that mini icons hl groups exist in the current colorscheme. If not use which-key default groups ([2336350](https://github.com/folke/which-key.nvim/commit/233635039bf828e341f5ca9b4b8444ac3c56b974))
* **icons:** proper icons check ([2eaed99](https://github.com/folke/which-key.nvim/commit/2eaed99585f08787d6b5060c89184973eb5aa276))
* **keys:** delete nop keymaps with a description ([ccf0276](https://github.com/folke/which-key.nvim/commit/ccf027625df6c4e22febfdd786c5e1f7521c2ccb))
* **layout:** display vs multibyte ellipsis ([0442a73](https://github.com/folke/which-key.nvim/commit/0442a7340cebe13cc5a5fd70dd6cdc989f9086fe))
* **layout:** empty columns ([600881a](https://github.com/folke/which-key.nvim/commit/600881a9b0cf8119819a97d8900d99fd7a406d36))
* op-mode, count and reg ([e4d54d1](https://github.com/folke/which-key.nvim/commit/e4d54d11cc247edd0ed4bde7a501caa8e119c1ff))
* pcall keymap.del ([e47ee13](https://github.com/folke/which-key.nvim/commit/e47ee139b6a082deab16e436cbd2711923e01625))
* plugin actions & spelling ([e7da411](https://github.com/folke/which-key.nvim/commit/e7da411b45415e8d0d6a5e14b9c1bd5207d09869))
* presets ([bcf52ba](https://github.com/folke/which-key.nvim/commit/bcf52ba08a57a90e85d4397245a0350c34f2b9d1))
* readme ([5fe6c91](https://github.com/folke/which-key.nvim/commit/5fe6c91e6f2d7d6dd1a8473ac0cd9bbe311512d9))
* respect mappings with `<esc>` and close on cursor moved ([22deda5](https://github.com/folke/which-key.nvim/commit/22deda5458b15a10b02b516c68dd409cbaeb53f4))
* set check debounce to 50 ([754bcc7](https://github.com/folke/which-key.nvim/commit/754bcc7be77b9f9ecac02598121eb97a243b7efa))
* **state:** dont return or autocmd will cancel ([9a77986](https://github.com/folke/which-key.nvim/commit/9a779869ef557ff6fa84a8b0b478a0f84781c67e))
* **state:** keyboard interrupts ([1ed9182](https://github.com/folke/which-key.nvim/commit/1ed91823d47f34ce5c52da9ca14e202606caf215))
* **state:** make sure the buffer mode exists when changing modes ([df64366](https://github.com/folke/which-key.nvim/commit/df64366d8633ac13ba2da7134cc6bbe242a97237))
* stuff ([f67eb19](https://github.com/folke/which-key.nvim/commit/f67eb192ca6d579add84086d4d1b4ce6ce8732ac))
* **tree:** check for which_key_ignore in existing keymaps ([f17d78b](https://github.com/folke/which-key.nvim/commit/f17d78bdf8a0afce5bec97c70e68203a6cddf2b7))
* **ui:** box height ([528fc43](https://github.com/folke/which-key.nvim/commit/528fc43b87cfc29bbc1dddc17051a99cdfdf9ad2))
* **ui:** make sure the which-key window never overlaps the user's cursor position ([1bb30a7](https://github.com/folke/which-key.nvim/commit/1bb30a7a6901aa842f31c96af7009ef645b29edd))
* **ui:** scroll and topline=1 on refresh ([28b648d](https://github.com/folke/which-key.nvim/commit/28b648daeabfd2aad8496ffc7a2096bf7d2441b5))
* which_key_ignore ([ab5ffa8](https://github.com/folke/which-key.nvim/commit/ab5ffa83b4f10ea2360a32d855b016f72a2be6b6))
* which-key ignore and cleanup ([aeae826](https://github.com/folke/which-key.nvim/commit/aeae826f948cbaeb3a89d9025c423e8300cb5dd3))
## [2.1.0](https://github.com/folke/which-key.nvim/compare/v2.0.1...v2.1.0) (2024-06-06)
### Features
* **presets:** add descriptions for fold deletion ([#504](https://github.com/folke/which-key.nvim/issues/504)) ([53b6085](https://github.com/folke/which-key.nvim/commit/53b6085367a92740664783330583facd958dbceb))
### Bug Fixes
* black hole z= replacements ([#602](https://github.com/folke/which-key.nvim/issues/602)) ([f5b9124](https://github.com/folke/which-key.nvim/commit/f5b912451f33fd19e52230e73617ad099ffd3ab1))
* **keys:** fix nested operators. See [#600](https://github.com/folke/which-key.nvim/issues/600). Fixes [#609](https://github.com/folke/which-key.nvim/issues/609) ([25d5b9e](https://github.com/folke/which-key.nvim/commit/25d5b9e9b5775525248b8d5c95271ba28f75d326))
* restore win view after rendering buffer ([#516](https://github.com/folke/which-key.nvim/issues/516)) ([ea4a17d](https://github.com/folke/which-key.nvim/commit/ea4a17d63571c81f529669a373d20c855b9b351d)), closes [#515](https://github.com/folke/which-key.nvim/issues/515)
* support nested operators ([#600](https://github.com/folke/which-key.nvim/issues/600)) ([476f4ca](https://github.com/folke/which-key.nvim/commit/476f4cacb15da81dcebe68ea45333e660409612d))
## [2.0.1](https://github.com/folke/which-key.nvim/compare/v2.0.0...v2.0.1) (2024-06-06)
### Bug Fixes
* label -> desc ([b8eb534](https://github.com/folke/which-key.nvim/commit/b8eb5348a749e214dfd08d38654a736d91191918))
## [2.0.0](https://github.com/folke/which-key.nvim/compare/v1.6.1...v2.0.0) (2024-06-06)
### ⚠ BREAKING CHANGES
* which-key now requires Neovim >= 0.9
### Features
* **keys:** `desc` in `"<nop>"` or `""` keymaps can now become prefix label ([#522](https://github.com/folke/which-key.nvim/issues/522)) ([c1958e2](https://github.com/folke/which-key.nvim/commit/c1958e2529433ef096e924c72315733790ca7f88))
* **mappings:** check if desc exists when parsing mappings ([#589](https://github.com/folke/which-key.nvim/issues/589)) ([a7ced9f](https://github.com/folke/which-key.nvim/commit/a7ced9f00a309418865ec2e3c272113147d167fe))
* which-key now requires Neovim >= 0.9 ([53ba0ac](https://github.com/folke/which-key.nvim/commit/53ba0accc2d607ef3f2b4f6e40aa9ac75e611dee))
### Bug Fixes
* **ignore_missing:** not showing key maps with desc field ([#577](https://github.com/folke/which-key.nvim/issues/577)) ([928c6c8](https://github.com/folke/which-key.nvim/commit/928c6c8fb62df55fa640399b7d76410c037b5f55))
* **is_enabled:** disable whichkey in cmdline-window ([#581](https://github.com/folke/which-key.nvim/issues/581)) ([26ff0e6](https://github.com/folke/which-key.nvim/commit/26ff0e6084a4e957fc13ffe00bafd7c0c5ab81cc))
* **keys:** fix buffer-local mapping groups ([d87c01c](https://github.com/folke/which-key.nvim/commit/d87c01c9bbcc7c1c2d248dca1b11285259d66be8))
* **mappings:** dont remove desc ([4a7d732](https://github.com/folke/which-key.nvim/commit/4a7d7328b26d3f3355a43af4d8dc5ffd33cbd793))
## [1.6.1](https://github.com/folke/which-key.nvim/compare/v1.6.0...v1.6.1) (2024-05-31)
### Bug Fixes
* **reg:** Added check for OSC 52 to disable related register previews ([#604](https://github.com/folke/which-key.nvim/issues/604)) ([8063a7f](https://github.com/folke/which-key.nvim/commit/8063a7f33bfea6a6387907c93a30a5877aa02633))
* small typo in operator description ([#528](https://github.com/folke/which-key.nvim/issues/528)) ([d65087b](https://github.com/folke/which-key.nvim/commit/d65087b892c45d3722b6511c83a029671d6290e5))
## [1.6.0](https://github.com/folke/which-key.nvim/compare/v1.5.1...v1.6.0) (2023-10-17)
### Features
* **presets:** added gt and gT. Fixes [#457](https://github.com/folke/which-key.nvim/issues/457) ([3ba77f0](https://github.com/folke/which-key.nvim/commit/3ba77f0b0961b3fe685397b8d8f34f231b9350a6))
### Bug Fixes
* call config in issue template ([#489](https://github.com/folke/which-key.nvim/issues/489)) ([09a8188](https://github.com/folke/which-key.nvim/commit/09a8188224dc890618dfbc961436b106d912c2c1))
* **view:** set modifiable flag for view buffer ([#506](https://github.com/folke/which-key.nvim/issues/506)) ([1d17760](https://github.com/folke/which-key.nvim/commit/1d1776012eda4258985f6f1f0c02b78594a3f37b))
## [1.5.1](https://github.com/folke/which-key.nvim/compare/v1.5.0...v1.5.1) (2023-07-15)
### Bug Fixes
* revert: never overwrite actual keymaps with group names. Fixes [#478](https://github.com/folke/which-key.nvim/issues/478) Fixes [#479](https://github.com/folke/which-key.nvim/issues/479) Fixes [#480](https://github.com/folke/which-key.nvim/issues/480) ([fc25407](https://github.com/folke/which-key.nvim/commit/fc25407a360d27c36a30a90ff36861aa20ef2e54))
## [1.5.0](https://github.com/folke/which-key.nvim/compare/v1.4.3...v1.5.0) (2023-07-14)
### Features
* **marks:** show filename as label when no label ([25babc6](https://github.com/folke/which-key.nvim/commit/25babc6add21c17d6391a585302aee5632266622))
### Bug Fixes
* **keys:** don't show empty groups ([8503c0d](https://github.com/folke/which-key.nvim/commit/8503c0d725420b37ac31e44753657cde91435597))
* never overwrite actual keymaps with group names ([f61da3a](https://github.com/folke/which-key.nvim/commit/f61da3a3a6143b7a42b4b16e983004856ec26bd1))
* **registers:** dont trigger on @. Fixes [#466](https://github.com/folke/which-key.nvim/issues/466) ([65b36cc](https://github.com/folke/which-key.nvim/commit/65b36cc258e857dea92fc11cdc0d6e2bb01d3e87))
## [1.4.3](https://github.com/folke/which-key.nvim/compare/v1.4.2...v1.4.3) (2023-05-22)
### Bug Fixes
* **health:** dont show duplicates between global and buffer-local. It's too confusing ([015fdf3](https://github.com/folke/which-key.nvim/commit/015fdf3e3e052d4a9fee997ca0aa387c2dd3731c))
## [1.4.2](https://github.com/folke/which-key.nvim/compare/v1.4.1...v1.4.2) (2023-05-10)
### Bug Fixes
* **health:** update the deprecated function ([#453](https://github.com/folke/which-key.nvim/issues/453)) ([12d3b11](https://github.com/folke/which-key.nvim/commit/12d3b11a67b94d65483f10c6ba0a47474039543a))
## [1.4.1](https://github.com/folke/which-key.nvim/compare/v1.4.0...v1.4.1) (2023-05-04)
### Bug Fixes
* **keys:** dont overwrite existing keymaps with a callback. Fixes [#449](https://github.com/folke/which-key.nvim/issues/449) ([4db6bb0](https://github.com/folke/which-key.nvim/commit/4db6bb080b269ac155e5aa1696d26f2376c749ab))
## [1.4.0](https://github.com/folke/which-key.nvim/compare/v1.3.0...v1.4.0) (2023-04-18)
### Features
* **view:** ensure it's above other floating windows ([#442](https://github.com/folke/which-key.nvim/issues/442)) ([9443778](https://github.com/folke/which-key.nvim/commit/94437786a0d0fde61284f8476ac142896878c2d7))
## [1.3.0](https://github.com/folke/which-key.nvim/compare/v1.2.3...v1.3.0) (2023-04-17)
### Features
* **health:** move health check to separate health file ([b56c512](https://github.com/folke/which-key.nvim/commit/b56c5126752fcd498a81c6d8d1e7f51f251166eb))
* **preset:** add `z<CR>` preset ([#346](https://github.com/folke/which-key.nvim/issues/346)) ([ed37330](https://github.com/folke/which-key.nvim/commit/ed3733059ffa281c8144e44f1b4819a771ddf4de))
* **preset:** added `zi` and `CTRL-W_o` ([#378](https://github.com/folke/which-key.nvim/issues/378)) ([5e8e6b1](https://github.com/folke/which-key.nvim/commit/5e8e6b1c70d3fcbe2712453ef3ebbf07d0d2aff4))
* **view:** allow percentages for margins. Fixes [#436](https://github.com/folke/which-key.nvim/issues/436) ([0b5a653](https://github.com/folke/which-key.nvim/commit/0b5a6537b66ee37d03c6c3f0e21fd147f817422d))
### Bug Fixes
* **health:** add OK output to check_health fn ([#375](https://github.com/folke/which-key.nvim/issues/375)) ([c9c430a](https://github.com/folke/which-key.nvim/commit/c9c430ab19a3bf8dd394dd9925a3a219063276b9))
* **keys:** allow keymap desc to override preset labels. Fixes [#386](https://github.com/folke/which-key.nvim/issues/386) ([6aa1b2f](https://github.com/folke/which-key.nvim/commit/6aa1b2fa93a2a26a1bd752080ec6a51beb009e75))
* **tree:** don't cache plugin nodes. Fixes [#441](https://github.com/folke/which-key.nvim/issues/441) ([20fcd7b](https://github.com/folke/which-key.nvim/commit/20fcd7b602a2c58d634eaa1f1d28b16a6acbfad3))
* **util:** clear cache when leader changes ([df3597f](https://github.com/folke/which-key.nvim/commit/df3597f7dc0f379bda865e3c9dd6303fa6e4c959))
* **util:** missing return statement ([f6bb21c](https://github.com/folke/which-key.nvim/commit/f6bb21c8c1d72008783466e80e0c993ef056a3a9))
* **util:** nil check ([6ab25e2](https://github.com/folke/which-key.nvim/commit/6ab25e24ec2b2a8fb88f43eb13feb21e5042c280))
### Performance Improvements
* **keys:** optimized `update_keymaps` ([476d137](https://github.com/folke/which-key.nvim/commit/476d13754db0da7831fc3581fb243cd7f0d3e581))
* **tree:** added fast nodes lookup ([8e5e012](https://github.com/folke/which-key.nvim/commit/8e5e0126aaff9bd73eb25a6d5568f6b5bdff58f0))
* **util:** cache parse_keys ([8649bf5](https://github.com/folke/which-key.nvim/commit/8649bf5c66b8fa1fa6ee879b9af78e89f886d13c))
* **util:** cache replace termcodes ([eaa8027](https://github.com/folke/which-key.nvim/commit/eaa80272ef488c68cd51698c64e795767c6e0624))
## [1.2.3](https://github.com/folke/which-key.nvim/compare/v1.2.2...v1.2.3) (2023-04-17)
### Bug Fixes
* **util:** dont parse empty lhs ([8d5ab76](https://github.com/folke/which-key.nvim/commit/8d5ab76836d89be1c761a4ed61bf700d98c71e5d))
* **util:** only collect valid <> keys ([#438](https://github.com/folke/which-key.nvim/issues/438)) ([4bd6dca](https://github.com/folke/which-key.nvim/commit/4bd6dcaa6d7e1650590303f0066d32aa6762d8f3))
* **util:** replace `<lt>` by `<` before parsing ([789ac71](https://github.com/folke/which-key.nvim/commit/789ac718ee7a2b49dd82409e3d7cf45b52ea95ce))
* **view:** allow deviating paddings per side ([#400](https://github.com/folke/which-key.nvim/issues/400)) ([3090eaf](https://github.com/folke/which-key.nvim/commit/3090eafb780da76eb4876986081551db80bf35cd))
### Performance Improvements
* **util:** simplify and optimize parsers ([#435](https://github.com/folke/which-key.nvim/issues/435)) ([b0ebb67](https://github.com/folke/which-key.nvim/commit/b0ebb6722c77dda1ab1e3ce13521fe7db20cbc79))
## [1.2.2](https://github.com/folke/which-key.nvim/compare/v1.2.1...v1.2.2) (2023-04-16)
### Performance Improvements
* **mappings:** avoid computing error string on hot path ([#429](https://github.com/folke/which-key.nvim/issues/429)) ([6892f16](https://github.com/folke/which-key.nvim/commit/6892f165bb984561f8cac298a6747da338d04668))
## [1.2.1](https://github.com/folke/which-key.nvim/compare/v1.2.0...v1.2.1) (2023-03-26)
### Bug Fixes
* **icons:** fixed obsolete icons with nerdfix ([151f21d](https://github.com/folke/which-key.nvim/commit/151f21d34d50fc53506ddc9d8ec58234202df795))
* **view:** wrong window position when statusline is not set ([#363](https://github.com/folke/which-key.nvim/issues/363)) ([e14f8dc](https://github.com/folke/which-key.nvim/commit/e14f8dc6304e774ce005d09f7feebbd191fe20f9))
## [1.2.0](https://github.com/folke/which-key.nvim/compare/v1.1.1...v1.2.0) (2023-03-01)
### Features
* enable spelling plugin by default ([6d886f4](https://github.com/folke/which-key.nvim/commit/6d886f4dcaa25d1fe20e332f779fe1edb726d063))
* make delay configurable for marks/registers/spelling. Fixes [#379](https://github.com/folke/which-key.nvim/issues/379). Fixes [#152](https://github.com/folke/which-key.nvim/issues/152), Fixes [#220](https://github.com/folke/which-key.nvim/issues/220), Fixes [#334](https://github.com/folke/which-key.nvim/issues/334) ([5649320](https://github.com/folke/which-key.nvim/commit/56493205745597abdd8d3ceb22f502ffe74784f5))
## [1.1.1](https://github.com/folke/which-key.nvim/compare/v1.1.0...v1.1.1) (2023-02-10)
### Bug Fixes
* remove duplicate kaymap ([#361](https://github.com/folke/which-key.nvim/issues/361)) ([9a4680e](https://github.com/folke/which-key.nvim/commit/9a4680e95b7026c58f0a377de0f13ee2507ece7a))
## [1.1.0](https://github.com/folke/which-key.nvim/compare/v1.0.0...v1.1.0) (2023-01-10)
### Features
* Hide mapping when `desc = "which_key_ignore"` ([#391](https://github.com/folke/which-key.nvim/issues/391)) ([fd07b61](https://github.com/folke/which-key.nvim/commit/fd07b6137f1e362a66df04f7c7055b99319e3a4d))
### Bug Fixes
* visual-multi compatibility ([#389](https://github.com/folke/which-key.nvim/issues/389)) ([#385](https://github.com/folke/which-key.nvim/issues/385)) ([01334bb](https://github.com/folke/which-key.nvim/commit/01334bb48c53231fc8b2e2932215bfee05474904))
## 1.0.0 (2023-01-04)
### Features
* add <C-w>_ to misc ([#296](https://github.com/folke/which-key.nvim/issues/296)) ([03b8c1d](https://github.com/folke/which-key.nvim/commit/03b8c1dde8c02f187869c56a6019d5e2578f7af7))
* add preset key to mappings for API usage ([ed7d6c5](https://github.com/folke/which-key.nvim/commit/ed7d6c523ae8ef7b8059d2fee0836009e71bcd0c))
* added a winblend option for the floating window ([#161](https://github.com/folke/which-key.nvim/issues/161)) ([d3032b6](https://github.com/folke/which-key.nvim/commit/d3032b6d3e0adb667975170f626cb693bfc66baa))
* added duplicate mapping checks to checkhealth [#34](https://github.com/folke/which-key.nvim/issues/34) ([710c5f8](https://github.com/folke/which-key.nvim/commit/710c5f81da2c34e6e0f361d87cfca27207e1b994))
* added healthcheck to check for conflicting keymaps ([44d3c3f](https://github.com/folke/which-key.nvim/commit/44d3c3f9307930ce8c877383d51fca1a353982d8))
* added ignore_missing option to hide any keymap for which no label exists [#60](https://github.com/folke/which-key.nvim/issues/60) ([1ccba9d](https://github.com/folke/which-key.nvim/commit/1ccba9d0b553b08feaca9f432386f9c33bd1656f))
* added operators plugin ([c7f8496](https://github.com/folke/which-key.nvim/commit/c7f84968e44f1a9ab9687ddf0b3dc5465e48bc75))
* added option to configure scroll bindings inside the popup ([#175](https://github.com/folke/which-key.nvim/issues/175)) ([a54ef5f](https://github.com/folke/which-key.nvim/commit/a54ef5f5db5819ee65a5ec3dea9bae64476c5017))
* added options to align columns left, center or right [#82](https://github.com/folke/which-key.nvim/issues/82) ([2467fb1](https://github.com/folke/which-key.nvim/commit/2467fb15e8775928fba3d7d20a68b64852f44122))
* added settings to disable the WhichKey popup for certain buftypes and filetyes ([fb276a0](https://github.com/folke/which-key.nvim/commit/fb276a07c7dc305e48ecc2683e4bd28cda49499a))
* added support for expr mappings ([9d2785c](https://github.com/folke/which-key.nvim/commit/9d2785c4d44b4a8ca1095856cb4ee34a32497cf6))
* added triggers_blacklist to blacklist certain whichkey hooks [#73](https://github.com/folke/which-key.nvim/issues/73) ([ec1474b](https://github.com/folke/which-key.nvim/commit/ec1474bb0c373eb583962deff20860c2af54f932))
* added WhichKeyBorder highlight group ([9c190ea](https://github.com/folke/which-key.nvim/commit/9c190ea91939eba8c2d45660127e0403a5300b5a))
* allow functions to be passed to create keybindings. Implements [#31](https://github.com/folke/which-key.nvim/issues/31) ([cf644cd](https://github.com/folke/which-key.nvim/commit/cf644cd9a0e989ad3e0a6dffb98beced742f3297))
* allow manual setup of triggers [#30](https://github.com/folke/which-key.nvim/issues/30) ([423a50c](https://github.com/folke/which-key.nvim/commit/423a50cccfeb8b812e0e89f156316a4bd9d2673a))
* allow mapping to have multiple modes as a table ([0d559fa](https://github.com/folke/which-key.nvim/commit/0d559fa5573aa48c4822e8874315316bd075e17e))
* allow mode to be set on a single mapping ([2a08d58](https://github.com/folke/which-key.nvim/commit/2a08d58658e1de0fae3b44e21e8ed72399465701))
* allow overriding key labels [#77](https://github.com/folke/which-key.nvim/issues/77) ([2be929e](https://github.com/folke/which-key.nvim/commit/2be929e34b2f2b982e6b978c0bd94cd2e1d500e6))
* allow to close popup with <c-c> [#33](https://github.com/folke/which-key.nvim/issues/33) ([410523a](https://github.com/folke/which-key.nvim/commit/410523a6d7bcbcab73f8c7b0fc567893d7cd8c44))
* better logging ([c39df95](https://github.com/folke/which-key.nvim/commit/c39df95881a6cd8ac27fce5926dc2dc1b4597df9))
* better support for plugin actions with custom lua function ([222a8ee](https://github.com/folke/which-key.nvim/commit/222a8eeaf727f9b1b767424198f7c71274c04d43))
* builtin key mappings ([0063ceb](https://github.com/folke/which-key.nvim/commit/0063ceb161475097885d567500fe764358983c62))
* check for rogue existsing WhichKey mappings and show error. WK handles triggers automatically, no need to define them ([db97a30](https://github.com/folke/which-key.nvim/commit/db97a301fb7691b61cd6c975e3cc060fb53fd980))
* easily reset WK with plenary for development of WK ([55b4dab](https://github.com/folke/which-key.nvim/commit/55b4dabab649d59e657917eb17c9d57716817719))
* expose registers to customize order ([2b83fe7](https://github.com/folke/which-key.nvim/commit/2b83fe74dee00763e4c037d198c88ff11c843914))
* for nvim 0.7.0 or higher, use native keymap callbacks instead of which key functions ([5e96cf9](https://github.com/folke/which-key.nvim/commit/5e96cf950a864a4600512c90f2080b0b6f0eacb7))
* group symbol ([5e02b66](https://github.com/folke/which-key.nvim/commit/5e02b66b9e7add373967b798552a7cc9a427efb4))
* handle [count] with motion. Implements [#11](https://github.com/folke/which-key.nvim/issues/11) ([d93ef0f](https://github.com/folke/which-key.nvim/commit/d93ef0f2f1a9a6288016a3a82f70399e350a574f))
* hide mapping boilerplate ([#6](https://github.com/folke/which-key.nvim/issues/6)) ([b3357de](https://github.com/folke/which-key.nvim/commit/b3357de005f27a3cc6aabe922e8ee308470d9343))
* honor timeoutlen when typing an operator followed by i or a instead of showing immediately ([54d1b3a](https://github.com/folke/which-key.nvim/commit/54d1b3ab3ed9132142f2139964cfa68d018b38c5))
* initial commit ([970e79f](https://github.com/folke/which-key.nvim/commit/970e79f7016f6cc2a89dad8c50e2e89657684f55))
* keyamp functions ([801cc81](https://github.com/folke/which-key.nvim/commit/801cc810f4d57eca029261f383b2483ec21e5824))
* make custom operators configurable (fixes [#9](https://github.com/folke/which-key.nvim/issues/9)) ([81875d8](https://github.com/folke/which-key.nvim/commit/81875d875f7428c7a087e0d051744c7b3f9dc1b3))
* make help message configurable ([7b1c6aa](https://github.com/folke/which-key.nvim/commit/7b1c6aa23061a9ed1acdfec3d20dc5e361ec01a3))
* Make keypress message configuratble ([#351](https://github.com/folke/which-key.nvim/issues/351)) ([fd2422f](https://github.com/folke/which-key.nvim/commit/fd2422fb7030510cf9c3304047e653e8adcd8f20))
* motions plugin ([f989fcf](https://github.com/folke/which-key.nvim/commit/f989fcfeafd4fd333a8e87617fce39a449ae81ca))
* new keymap dsl ([#352](https://github.com/folke/which-key.nvim/issues/352)) Docs to come ([fbf0381](https://github.com/folke/which-key.nvim/commit/fbf038110edb5e2cbecaac57570aae2c9fa2939c))
* option to make some triggers show immediately, regardless of timeoutlen ([3a52dc0](https://github.com/folke/which-key.nvim/commit/3a52dc02b6e542d5cd216381ccfa108943bab17c))
* plugin for registers ([5415832](https://github.com/folke/which-key.nvim/commit/541583280fab4ea96900f35fb6b5ffb8de103a4c))
* plugin support + first builtin marks plugin ([9d5e631](https://github.com/folke/which-key.nvim/commit/9d5e6311c20970741eaaf7a3950c1a33de5eedaa))
* prefer `desc` to `cmd` as the fallback label ([#253](https://github.com/folke/which-key.nvim/issues/253)) ([bd4411a](https://github.com/folke/which-key.nvim/commit/bd4411a2ed4dd8bb69c125e339d837028a6eea71))
* preset with misc keybindings ([e610338](https://github.com/folke/which-key.nvim/commit/e61033858b8d5208a49c24d70eb9576cbd22e887))
* set keymap desc when creating new mappings based on the WhichKey labels ([f4518ca](https://github.com/folke/which-key.nvim/commit/f4518ca50193a545681ba65ba0c5bb8a8479c5b5))
* set popup filetype to WhichKey and buftype to nofile [#86](https://github.com/folke/which-key.nvim/issues/86) ([20682f1](https://github.com/folke/which-key.nvim/commit/20682f189a0c452203f6365f66eccb0407b20936))
* show a warning if <leader> is already mapped, even if it's <nop> ([ac56f45](https://github.com/folke/which-key.nvim/commit/ac56f45095e414c820f621423611aac4027f74bd))
* show breadcrumb and help on command line ([c27535c](https://github.com/folke/which-key.nvim/commit/c27535ca085c05ade1e23b3b347e39e53c24d33a))
* show keys and help in float when cmdheight == 0 ([f645017](https://github.com/folke/which-key.nvim/commit/f64501787bebe9ff28c10dbe470ffad5dd017769))
* show/hide a fake cursor when WK is open ([0f53f40](https://github.com/folke/which-key.nvim/commit/0f53f40c1b827d35771c82a5c47c5a54d9408f7c))
* spelling suggestion plugin ([4b74f21](https://github.com/folke/which-key.nvim/commit/4b74f218f4541991a40719286f96cce9447a89c4))
* support for custom text object completion. Fixes [#10](https://github.com/folke/which-key.nvim/issues/10) ([394ff5a](https://github.com/folke/which-key.nvim/commit/394ff5a37bab051857de4216ee25db2284de2196))
* support opts.remap for keymap ([#339](https://github.com/folke/which-key.nvim/issues/339)) ([6885b66](https://github.com/folke/which-key.nvim/commit/6885b669523ff4238de99a7c653d47b081b5506d))
* support using lua function for expr ([#110](https://github.com/folke/which-key.nvim/issues/110)) ([e0dce15](https://github.com/folke/which-key.nvim/commit/e0dce1552ea37964ae6ac7144709867544eae7f3))
* text objects ([d255b71](https://github.com/folke/which-key.nvim/commit/d255b71992494ce4998caae7fe281144fb669abb))
* WhichKey vim command to show arbitrary keymaps ([df615d4](https://github.com/folke/which-key.nvim/commit/df615d44987a8bfe8910c618164f696e227ecfd4))
### Bug Fixes
* :norm .. commands keep feeding <esc> at the end of the command [#58](https://github.com/folke/which-key.nvim/issues/58) ([d66ffdd](https://github.com/folke/which-key.nvim/commit/d66ffdd5a845c713f581ac6da36173e88096e0fa))
* add delay option to macro key ([#152](https://github.com/folke/which-key.nvim/issues/152)) ([#156](https://github.com/folke/which-key.nvim/issues/156)) ([bd226c4](https://github.com/folke/which-key.nvim/commit/bd226c4d02d7f360747364a59cc5f0da50524f2c))
* add remaining <esc> to pending in case there's no other characters ([29a82b5](https://github.com/folke/which-key.nvim/commit/29a82b575b9752a45b005327030948ce8cb513a0))
* add triggers for other modes in marks and register plugin ([#116](https://github.com/folke/which-key.nvim/issues/116)) ([bbfc640](https://github.com/folke/which-key.nvim/commit/bbfc640c44612d705f4b0670ec1387c8a6ff2c7c))
* added @ trigger for showing registers ([01b6676](https://github.com/folke/which-key.nvim/commit/01b66769480fac14f6efa7c31327234398d05837))
* added builtin plugins to config ([6e461ca](https://github.com/folke/which-key.nvim/commit/6e461caec3d3aa43f1fa2b7890b299705bccfe8d))
* added hidden option to disable the popup on motion counts (motions.count) ([ea975ef](https://github.com/folke/which-key.nvim/commit/ea975ef254f10c4938cd663a7c4fb14e2d7514c0))
* added support for operator pending keymaps ([1f6b510](https://github.com/folke/which-key.nvim/commit/1f6b510f6ef0c223b51f3599200bbf6abc30f909))
* added z= for spelling correction ([59603de](https://github.com/folke/which-key.nvim/commit/59603dee2f67f623a520148d60c634f6f56f6017))
* always escape <leader> when it's a backslash ([41636a3](https://github.com/folke/which-key.nvim/commit/41636a3be909af5d20d811f8ce6a304a5ee3cc21))
* always execute keys with remap, but unhook / hook WK triggers (Fixes [#8](https://github.com/folke/which-key.nvim/issues/8)) ([bf329df](https://github.com/folke/which-key.nvim/commit/bf329df0ee11d6c80c7208b40eab74368e963245))
* always map <leader>, even without register ([512631c](https://github.com/folke/which-key.nvim/commit/512631c1bdce96dd048115cb139ea3a8452a931a))
* always unhook and ignore errors ([01a60cd](https://github.com/folke/which-key.nvim/commit/01a60cd5929b395042c8ba3d872f6f25ccd55ecb))
* always use noremap=false for <plug> commands ([9b9cece](https://github.com/folke/which-key.nvim/commit/9b9cece006b78ff7527a35285a4b5c1359d70fd8))
* always use word under the cursor for spelling suggestions ([c5b19ec](https://github.com/folke/which-key.nvim/commit/c5b19ecf4d1d8f8c77ee982caf9792740f6d5e53))
* better handling of weird norm and getchar endless <esc> bug [#68](https://github.com/folke/which-key.nvim/issues/68) ([bfd37e9](https://github.com/folke/which-key.nvim/commit/bfd37e93761d622328c673828b537d5671389413))
* better sorting ([99e8940](https://github.com/folke/which-key.nvim/commit/99e894032afbe2543dbbf9bba05518d96b852aa0))
* center alignemnt should be an integer ([db85198](https://github.com/folke/which-key.nvim/commit/db851981595fc360e9b6196a7c3995611aceac3b))
* check for FloatBorder before setting winhighlight ([af6b91d](https://github.com/folke/which-key.nvim/commit/af6b91dc09e4ed830d8cd4a3652a5b3f80ccefac))
* check is hook exists before unhooking ([f6cf3a2](https://github.com/folke/which-key.nvim/commit/f6cf3a2e49c09aba739c0f6fc85d3aebf2b96cb6))
* cmd can be nil ([060a574](https://github.com/folke/which-key.nvim/commit/060a574c228433e9b17960fa0eafca0a975381e8))
* **colors:** Separator links to DiffAdd ([#302](https://github.com/folke/which-key.nvim/issues/302)) ([a2749c5](https://github.com/folke/which-key.nvim/commit/a2749c5b039ad34734c98f8752b9fb5da7ceac55))
* Compatibility with Visual Multi plug ([#278](https://github.com/folke/which-key.nvim/issues/278)) ([92916b6](https://github.com/folke/which-key.nvim/commit/92916b6cede0ffd7d5c1ce9abad93ec0c4d9635e))
* convert trings with strtrans to properly render non printable characters ([d85ce36](https://github.com/folke/which-key.nvim/commit/d85ce3627f4060f622e4c0a9657f26c0151829de))
* correct floating window position in Neovim 0.6 nightly ([#176](https://github.com/folke/which-key.nvim/issues/176)) ([a35a910](https://github.com/folke/which-key.nvim/commit/a35a910d28683294fd23d35dd03c06f6f7c37b17))
* correctly handle counts before commands [#17](https://github.com/folke/which-key.nvim/issues/17) ([4feb319](https://github.com/folke/which-key.nvim/commit/4feb319ff89fb8659efa2a788f808bc390afa490))
* correctly unhook buffer local mappings before executing keys ([4f98b47](https://github.com/folke/which-key.nvim/commit/4f98b4713ea9d4534662ceb7b542b0626eeb9ea8))
* disable folding on whichkey popup. Fixes [#99](https://github.com/folke/which-key.nvim/issues/99) ([78821de](https://github.com/folke/which-key.nvim/commit/78821de0b633275d6934660e67989639bc7a784c))
* disable operator pending maps for now ([#2](https://github.com/folke/which-key.nvim/issues/2)) ([0cd66a8](https://github.com/folke/which-key.nvim/commit/0cd66a84520fc0e7e3eec81f081157541cb48dbd))
* do feedkeys in correct mode when dealing with operator pending commands. Fixes [#8](https://github.com/folke/which-key.nvim/issues/8) ([cf30788](https://github.com/folke/which-key.nvim/commit/cf307886b68ed53334ffdcee809a751376269e33))
* don't show <esc> mappings since <esc> closes the popup ([09db756](https://github.com/folke/which-key.nvim/commit/09db756b5d357767a635a4d169e2e820b2962ea8))
* don't show spelling when the command was started with a count [#80](https://github.com/folke/which-key.nvim/issues/80) ([20a85bd](https://github.com/folke/which-key.nvim/commit/20a85bd8bc54a11cf040aafa5d60f8a735eecfbd))
* dont do feedkeys when user uses WhichKey command with non existing prefix ([f9537ce](https://github.com/folke/which-key.nvim/commit/f9537ce0f7457665e3b90d82c5f3f2c37fe0506f))
* dont pass zero counts ([0c3cfb0](https://github.com/folke/which-key.nvim/commit/0c3cfb0064ceec5b182bac580033e0654d9575e6))
* dont show errors about loading order of setup and register ([2adbc17](https://github.com/folke/which-key.nvim/commit/2adbc17e00061073f2c2a40b6420ee2a80ea458d))
* explicitely check if we try to execute an auto which-key mapping. shouldn't happen, but still safer to check ([30fdd46](https://github.com/folke/which-key.nvim/commit/30fdd465433d48cab3b1f894daf52fa0005cf7ac))
* expose presets so one can change them if needed [#70](https://github.com/folke/which-key.nvim/issues/70) ([46ea686](https://github.com/folke/which-key.nvim/commit/46ea686c6cc9bfc96bc492c76a76d43548a587c4))
* feed CTRL-O again if called from CTRL-O ([#145](https://github.com/folke/which-key.nvim/issues/145)) ([833b5ea](https://github.com/folke/which-key.nvim/commit/833b5ea1a0d4b3bddf4b5c68fc89f1234960edec))
* feed the keys as typed ([#333](https://github.com/folke/which-key.nvim/issues/333)) ([33b4e72](https://github.com/folke/which-key.nvim/commit/33b4e72a07546bc4798b4bafb99ae06df47bd790))
* fix flickering on tmux ([f112602](https://github.com/folke/which-key.nvim/commit/f11260251ad942ba1635db9bc25c2efaf75caf0a))
* fix issue when cmdheight=0 [#301](https://github.com/folke/which-key.nvim/issues/301) ([#305](https://github.com/folke/which-key.nvim/issues/305)) ([9cd09ca](https://github.com/folke/which-key.nvim/commit/9cd09ca6bbe5acfbce86ca023fdc720f6aa132d6))
* fixed 0 after an operator. Wrongly assumed any number to be a count for following op mode, but not the case for 0 [#59](https://github.com/folke/which-key.nvim/issues/59) [#61](https://github.com/folke/which-key.nvim/issues/61) ([36616ca](https://github.com/folke/which-key.nvim/commit/36616cacba5d9eb716017bf23b7bbbe4cb4a6822))
* fixed possible nil error when showing marks ([b44fc09](https://github.com/folke/which-key.nvim/commit/b44fc095f6d0144278f3413533ad2d40ae664229))
* for sporadic loss of lua function for mapping ([#216](https://github.com/folke/which-key.nvim/issues/216)) ([312c386](https://github.com/folke/which-key.nvim/commit/312c386ee0eafc925c27869d2be9c11ebdb807eb))
* formatting of text-objects plugin ([442d2d3](https://github.com/folke/which-key.nvim/commit/442d2d383284390c5ee1b922036fc10fff530b2d))
* get value of register '=' with getreg('=',1) ([#114](https://github.com/folke/which-key.nvim/issues/114)) ([6224ea8](https://github.com/folke/which-key.nvim/commit/6224ea81f505c66a9644f89129149b108f722e56))
* handle backslash as localleader [#47](https://github.com/folke/which-key.nvim/issues/47) ([cd23fdc](https://github.com/folke/which-key.nvim/commit/cd23fdc1b0cbdb22769bed5cb275a6d1c4bd9bfc))
* handle baskslashes when leader or localleader isn't set ([d155ab3](https://github.com/folke/which-key.nvim/commit/d155ab3bef11a8156995b47d5552586e5c9f66a3))
* handle keymaps with a <nop> rhs as non existing and possibly overwrite them with WK hooks [#35](https://github.com/folke/which-key.nvim/issues/35) ([402be18](https://github.com/folke/which-key.nvim/commit/402be18dc656897b1dc68c88fab4ffe8635b8209))
* handle nvim_{buf_}get_keymap return no rhs due to 'callback' mapping ([#223](https://github.com/folke/which-key.nvim/issues/223)) ([28d2bd1](https://github.com/folke/which-key.nvim/commit/28d2bd129575b5e9ebddd88506601290bb2bb221))
* handle possible errors when getting last expression register [#64](https://github.com/folke/which-key.nvim/issues/64) ([7a1be6f](https://github.com/folke/which-key.nvim/commit/7a1be6ff950c7fb94a4f9e9bdb428a514e569503))
* highlighting of line number in marks ([9997d93](https://github.com/folke/which-key.nvim/commit/9997d93e5adcf0352aa73c42d3c395ba775600e9))
* immediately show registers and marks. Fixes [#144](https://github.com/folke/which-key.nvim/issues/144) ([653ce71](https://github.com/folke/which-key.nvim/commit/653ce711e6c27416ac79c4811ff814e9a38fddcf))
* link default WhichKeyBorder to FloatBorder. Fixes [#331](https://github.com/folke/which-key.nvim/issues/331) ([1698d6d](https://github.com/folke/which-key.nvim/commit/1698d6d0ff0b00b8499d9aea8715d120dc526900))
* make register selection work in INSERT mode ([d4315f8](https://github.com/folke/which-key.nvim/commit/d4315f8991da816c30e9387a891c02774552dc36))
* make spelling suggestions also work for correctly spelled words ([d02dc34](https://github.com/folke/which-key.nvim/commit/d02dc344bdaf273dfde7672f3f8e70a307593f62))
* make sure we never accidentally show WK triggers ([197b4d3](https://github.com/folke/which-key.nvim/commit/197b4d3403c04c0045e8d541e8cd2504aba5f168))
* make which-key's lazy loading work when it is also lazy-loaded ([7d929b9](https://github.com/folke/which-key.nvim/commit/7d929b96e2588fe9710ad795402eaead1aa0f70f))
* manual command now uses proper escaping for prefix ([334fcca](https://github.com/folke/which-key.nvim/commit/334fcca64611dbca8c0c669260f4fb2a8ff81509))
* mapleader=\ ([b5c8985](https://github.com/folke/which-key.nvim/commit/b5c89851d580459c1dd33ecbda611ae06e22eec4))
* mapping when right-hand side is `nil` ([#323](https://github.com/folke/which-key.nvim/issues/323)) ([1d449d4](https://github.com/folke/which-key.nvim/commit/1d449d44e01787ef17dc7b0672eec01a8121b36e))
* never hook in SELECT mode and properly handle v, x, s [#45](https://github.com/folke/which-key.nvim/issues/45) [#46](https://github.com/folke/which-key.nvim/issues/46) ([2844e1c](https://github.com/folke/which-key.nvim/commit/2844e1cbf298129afa58c13a90f91be907232dbf))
* never hook j and k in INSERT mode automatcally to prevent jk kj <ESC> mappings to work as intended ([9a2faed](https://github.com/folke/which-key.nvim/commit/9a2faed055459d3226634344468f78bf85d77fa8))
* never hook numbers. locks up due to v:count. Fixes [#118](https://github.com/folke/which-key.nvim/issues/118) ([2d2954a](https://github.com/folke/which-key.nvim/commit/2d2954a1d05b4f074e022e64db9aa6093d439bb0))
* never hook on <esc> ([fd08322](https://github.com/folke/which-key.nvim/commit/fd0832233bd0c733618fab1c3df92f261c13d6b3))
* never hook q [#63](https://github.com/folke/which-key.nvim/issues/63) ([95ae9d2](https://github.com/folke/which-key.nvim/commit/95ae9d2d00e8714379e64994e69ae17fc540a7d6))
* never hook to operators in visual mode [#61](https://github.com/folke/which-key.nvim/issues/61) ([43d799a](https://github.com/folke/which-key.nvim/commit/43d799ad0e6218964e802ff342ca5f9352105175))
* nil in health check ([5c018ae](https://github.com/folke/which-key.nvim/commit/5c018ae412b235abe17e24b46057564db0944dc4))
* nvim_win_close force = true ([ca73a0e](https://github.com/folke/which-key.nvim/commit/ca73a0e03f142067a16891b712c7ea73ac646dff))
* nvim-0.7.0 check ([#338](https://github.com/folke/which-key.nvim/issues/338)) ([1491c35](https://github.com/folke/which-key.nvim/commit/1491c355ec9bb0ec4c8e71c8625bc5f55a54b925))
* only create mappings for builtin operators. plugings will always have their own mappings ([1b2ec76](https://github.com/folke/which-key.nvim/commit/1b2ec760d65ce9eda473879bec5c31c4771079e7))
* only enable plugins that are specified in the configuration ([b8ed0e8](https://github.com/folke/which-key.nvim/commit/b8ed0e8e675b747ce21aa830c38ddf4fb2458e05))
* only show message about existing <leader> mapping in NORMAL mode [#75](https://github.com/folke/which-key.nvim/issues/75) ([bcc8297](https://github.com/folke/which-key.nvim/commit/bcc829775b7d366f61bd2db1753e2c6b3d1ec4d3))
* only show up/down when scrolling is posible (fixes [#4](https://github.com/folke/which-key.nvim/issues/4)) ([9e7986d](https://github.com/folke/which-key.nvim/commit/9e7986d8726291ee93ef448ae8c452981f1fc75f))
* override <leader> if it's mapped to <nop> ([928288b](https://github.com/folke/which-key.nvim/commit/928288b543d77c38ade936ee8bdef32a769ebe3a))
* pass + and * regsiters to feedkeys [#36](https://github.com/folke/which-key.nvim/issues/36) ([ce37f41](https://github.com/folke/which-key.nvim/commit/ce37f41641edb90bf51b975999553d13961ed8fa))
* pass 0 instead of nil for current buffer ([#227](https://github.com/folke/which-key.nvim/issues/227)) ([387fd67](https://github.com/folke/which-key.nvim/commit/387fd676d3f9b419d38890820f6e262dc0fadb46))
* passing registers in INSERT mode, is not by pasting them 😅 [#62](https://github.com/folke/which-key.nvim/issues/62) ([342c8cd](https://github.com/folke/which-key.nvim/commit/342c8cdb3651967c96c356eb2d79561c0c9273ee))
* place popup correctly respecting cmdheight [#28](https://github.com/folke/which-key.nvim/issues/28) ([490e4d5](https://github.com/folke/which-key.nvim/commit/490e4d55315b74c63a63ada89ecf0e660a94db9a))
* possible nil value in health check ([b1627ca](https://github.com/folke/which-key.nvim/commit/b1627caa25e24c580bbc88377942353875f93a41))
* possible recursion ([f7fef32](https://github.com/folke/which-key.nvim/commit/f7fef32701aba0a822ac0a82679aea454bec702f))
* prevent double escaping of key codes ([1676611](https://github.com/folke/which-key.nvim/commit/167661151204ea7da2d365113a76ab223b3dc880))
* properly escape sequence ([2473329](https://github.com/folke/which-key.nvim/commit/24733293bb7b28f3d98d4a88323eb13cbe5b46f2))
* properly escape terminal chars to see if we already hooked a trigger ([1bee8a1](https://github.com/folke/which-key.nvim/commit/1bee8a151e72e5738d813964492248c9bbc4c5ba))
* properly format unicode text in columns (fixes [#66](https://github.com/folke/which-key.nvim/issues/66)) ([e3066fa](https://github.com/folke/which-key.nvim/commit/e3066facb6ed91ac013e4ff8faf24997ed44459c))
* properly handle < chatracters (should be <lt>) ([e618f84](https://github.com/folke/which-key.nvim/commit/e618f8403e615d4344f2964839ee0e2013b4253e))
* properly handle < when loading WK [#16](https://github.com/folke/which-key.nvim/issues/16) ([6cf68b4](https://github.com/folke/which-key.nvim/commit/6cf68b49d48f2e07b82aee18ad01c4115d9ce0e5))
* properly handle <lt> when executing keys (fixes [#16](https://github.com/folke/which-key.nvim/issues/16) again) ([8500ebf](https://github.com/folke/which-key.nvim/commit/8500ebf69e30629fc0e00f4b52afefc0cfe38379))
* properly handle buffer=0 as the current buffer. Fixes [#91](https://github.com/folke/which-key.nvim/issues/91) ([9ea98e5](https://github.com/folke/which-key.nvim/commit/9ea98e59ddeeafc9181815dd714bea513b298e33))
* properly handle selected regsiters when executing keys [#36](https://github.com/folke/which-key.nvim/issues/36) ([5248a2d](https://github.com/folke/which-key.nvim/commit/5248a2db7e46803e8d8786f84b05280116cec707))
* properly parse internal key codes and key notation ([535703c](https://github.com/folke/which-key.nvim/commit/535703cd4f08623e12458b5522be1f4ec2a878e7))
* redraw after nvim_echo to fix issue with cmdheight=0 ([abcc2c6](https://github.com/folke/which-key.nvim/commit/abcc2c63f723b69c0b31ccacdfddbaf3a03e2c12))
* registers plugin for visual mode ([86a58ea](https://github.com/folke/which-key.nvim/commit/86a58eac6a3bc69f5aa373b29df993d14fda3307))
* remove unnecessary replacement of backslash ([#284](https://github.com/folke/which-key.nvim/issues/284)) ([7afe584](https://github.com/folke/which-key.nvim/commit/7afe58460305bc68515858c22d39368bc75984b3))
* removed debug code ([2f823b8](https://github.com/folke/which-key.nvim/commit/2f823b87293657b5c34cf94a0ef72af02d0117e7))
* removed feedkeys as typed, since some normal mappings stop working ([e6a63ec](https://github.com/folke/which-key.nvim/commit/e6a63ec73efffdc63ee9da84d8a1dd1cbdff4650))
* removed triggers_nowait from README since this really only makes sense for plugins ([69fcfff](https://github.com/folke/which-key.nvim/commit/69fcfffe48f859b4192c111756221f967c8876b5))
* Reset `+` and `*` to default register when clipboard is set ([#233](https://github.com/folke/which-key.nvim/issues/233)) ([8154e65](https://github.com/folke/which-key.nvim/commit/8154e6552ef3188efb6c68d968791ac90e8f2b76))
* reset op_count when it's 0 ([e3ad7c9](https://github.com/folke/which-key.nvim/commit/e3ad7c92743b9168abbe974100909e7e761bdacd))
* set noautocmd on the WhichKey window, so it works properly for other floats like Telescope ([36fdfe8](https://github.com/folke/which-key.nvim/commit/36fdfe833207c120997c669a2c51060813f2f8a7))
* set scheduled instantly ([dc9c3be](https://github.com/folke/which-key.nvim/commit/dc9c3be7acae2a486c117f5a9f6ada62b2243336))
* show correct level and sort on keys / group ([a372c63](https://github.com/folke/which-key.nvim/commit/a372c63d5551a3656b7fa4388bdaf456d0d2cbb5))
* show error when setup was not run ([194f788](https://github.com/folke/which-key.nvim/commit/194f788cae6b41fe7edf362b6030237a1c221beb))
* sort keys case insensitive [#25](https://github.com/folke/which-key.nvim/issues/25) ([e26be8c](https://github.com/folke/which-key.nvim/commit/e26be8c3cb876d634545ed7013c69f45f4e9375c))
* special handling needed when <leader> = <bslash> [#40](https://github.com/folke/which-key.nvim/issues/40) ([c4a59d7](https://github.com/folke/which-key.nvim/commit/c4a59d76135563ea73beb87cf0d6d7a3302563be))
* start of visual selection mark should be <lt> instead of < [#69](https://github.com/folke/which-key.nvim/issues/69) ([840311c](https://github.com/folke/which-key.nvim/commit/840311c272eda2c4fc0d92070e9ef2dd13f884e7))
* typo ([4bacbfd](https://github.com/folke/which-key.nvim/commit/4bacbfdacb9eebee339d36243fe17b9185ccbb74))
* use buffer instead of bufnr + added warning ([df49a59](https://github.com/folke/which-key.nvim/commit/df49a59efdfd6a90f412aa251914183fec8593af))
* use Comment as fallback color for the Separator ([7ee35a7](https://github.com/folke/which-key.nvim/commit/7ee35a7614e34e562fd3f815ad35bd6d7e456093))
* use config.key_labels for cmdline trail as well (Fixes [#108](https://github.com/folke/which-key.nvim/issues/108)) ([1872dd8](https://github.com/folke/which-key.nvim/commit/1872dd8ca9daa0f6478a7771087aedae8518cb97))
* use mode instead of redraw when cmdheight=0. (Fixes [#327](https://github.com/folke/which-key.nvim/issues/327)) ([c966279](https://github.com/folke/which-key.nvim/commit/c96627900191355e6788629bbf5239d7295221f0))
* use secret nop bindings to make sure timeoutlen is always respected ([eccd5f8](https://github.com/folke/which-key.nvim/commit/eccd5f8bf22e60620eee833946638b90552c9b69))
* use strwidth instead of strdisplaywidth ([386591e](https://github.com/folke/which-key.nvim/commit/386591e24afe88c1c52c2291d450e7d7ad9cf02a))
### Performance Improvements
* as long as we didnt finish loading, queue registers ([1bac978](https://github.com/folke/which-key.nvim/commit/1bac978464fd00dddbeee9c5584120f553b1a660))
* defer loading to VimEnter and only process hooks once when ready ([84ddcdc](https://github.com/folke/which-key.nvim/commit/84ddcdcd862c4bb6dcac84a876f66f9777ecef7c))
* no need to create triggers for all levels. first level that is not a cmd is enough ([3cc0424](https://github.com/folke/which-key.nvim/commit/3cc042498db5792b8f3b081310926c779c7aac07))
* no need to hook buffer-local if we have a global hook for a certain prefix ([bb5e0d9](https://github.com/folke/which-key.nvim/commit/bb5e0d9be9c73b7d343ff4bf0ffbb9b6b4696811))
* only load modules when needed ([6f8ae23](https://github.com/folke/which-key.nvim/commit/6f8ae23540bc5f980862d2d5aa6d3c02bb1e2da0))
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: NEWS.md
================================================
# 💥 What's New in 3.0?
Major update for [which-key.nvim](https://github.com/folke/which-key.nvim)! This release includes a complete rewrite and several new features.
**which-key** was my very first plugin, so it was time for a fresh start. 🎉
- ✨ **Full Rewrite**: Improved performance and functionality.
- 👀 **Visual & Operator Pending Mode Integration**: Now uses `ModeChanged`, eliminating the need for operator remappings.
- 🔧 **Simplified Mappings**: Removed obscure secret mappings.
- 🔒 **Safer Auto Triggers**: Auto triggers are now never created for single keys apart from `g` and `z`. All other letters are unsafe.
- ⏱️ **Delay**: Set delay independently of `timeoutlen`.
- 🛠️ **Layout**:
- Presets: `classic`, `modern`, and `helix`.
- Enable/disable which-key for specific modes.
- Configurable sorting with options like `local`, `order`, `group`, `alphanum`, `mod`, `lower`, `icase`, `desc`, and `manual`.
- Expand groups with fewer keymaps.
- Customizable string replacements for `key` and `desc`.
- 🎨 **Icon Support**:
- Auto-detect icons for keymaps using `lazy.nvim`.
- Custom icon rules and specifications for mapping levels.
- 🚫 **Never Get in the Way**: Avoids overlapping with the cursor.
- 🗂️ **New Mapping Spec**: New and better mappings spec, more in line with `vim.keymap.set` and how you define keymaps with [lazy.nvim](https://github.com/folke/lazy.nvim)
- 🐛 New Bugs: Lots of new and exciting bugs to discover! 🐞
## Screenshots
**Classic Mode**

**Modern Mode**

**Helix Mode**

For detailed configuration and usage instructions, refer to the updated README.
================================================
FILE: README.md
================================================
# 💥 Which Key
**WhichKey** helps you remember your Neovim keymaps, by showing available keybindings
in a popup as you type.



## ✨ 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 `<esc>`
## ⚡️ 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 = {
{
"<leader>?",
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.
<details><summary>Default Options</summary>
<!-- config:start -->
```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 = {
{ "<auto>", 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 == "<C-V>"
end,
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
-- 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 <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
},
},
---@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 = "<c-d>", -- binding to scroll down inside the popup
scroll_up = "<c-u>", -- 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<string, ({[1]:string, [2]:string}|fun(str:string):string)[]>
replace = {
key = {
function(key)
return require("which-key.view").format(key)
end,
-- { "<Space>", "SPC" },
},
desc = {
{ "<Plug>%(?(.*)%)?", "%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
}
```
<!-- config:end -->
</details>
## ⌨️ 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({
{ "<leader>f", group = "file" }, -- group
{ "<leader>ff", "<cmd>Telescope find_files<cr>", desc = "Find File", mode = "n" },
{ "<leader>fb", function() print("hello") end, desc = "Foobar" },
{ "<leader>fn", desc = "New File" },
{ "<leader>f1", hidden = true }, -- hide this keymap
{ "<leader>w", proxy = "<c-w>", group = "windows" }, -- proxy to window mappings
{ "<leader>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
{ "<leader>q", "<cmd>q<cr>", desc = "Quit" }, -- no need to specify mode since it's inherited
{ "<leader>w", "<cmd>w<cr>", 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 `{ "<auto>", 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 = {
> { "<auto>", mode = "nixsotc" },
> { "a", mode = { "n", "v" } },
> }
> ```
> [!TIP]
> To manually setup triggers, you can set `opts.triggers` to:
>
> ```lua
> triggers = {
> { "<leader>", 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({ "<C-V>", "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 `<leader>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
- `<esc>` to cancel and close the popup
- `<bs>` go up one level
- `<c-d>` scroll down
- `<c-u>` scroll up
## 🐙 Hydra Mode
Hydra mode is a special mode that keeps the popup open until you hit `<esc>`.
```lua
-- Show hydra mode for changing windows
require("which-key").show({
keys = "<c-w>",
loop = true, -- this will keep the popup open until you hit <esc>
})
```
## 🔥 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 '

### Registers
Shows a list of your buffer local and global registers when you hit " in _NORMAL_ mode, or `<c-r>` in _INSERT_ mode.

### Spelling
When enabled, this plugin hooks into `z=` and replaces the full-screen spelling suggestions window by a list of suggestions within **WhichKey**.

## 🎨 Colors
The table below shows all the highlight groups defined for **WhichKey** with their default link.
<!-- colors:start -->
| 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) |
<!-- colors:end -->
================================================
FILE: TODO.md
================================================
# Todo
- [x] create keymaps in register()
- [x] distinction between actual keymap and just a desc
- [x] virtual mappings wihtout real children?
- [x] registers / counts?
- [x] presets / plugins
- [x] config?
- [x] auto blacklist single keys for default keymaps (for mappings like `aa`, a hook would be created for `a` and `a` would be ignored)
- [x] custom sorting
- [x] gr doesn't work because of grn and friends
- [x] same for gc opmode and gc normal mode
- [x] yank and shift-paste hangs
- [x] macro recording / macro execution
- [x] spell
- [x] spell with count, like `1z=`
- [x] which-key-ignore
- [x] empty groups?
- [x] timeoutlen and nowait
- [x] ui presets
- [x] ui opts & columns etc
- [x] scroll window
- [x] help text?
- [x] plugin layout?
- [x] ✅ 🔥🔥🚀
- [x] minimize attach
- [x] sometimes incorrectly attached `gcc` not working
- [x] error handling for view
- [x] spelling layout
- [x] better mappings parser? Especially needs typings
- [x] Mappings with mode `v`
- [x] allow register from opts
- [x] auto gen docs
- [x] health
- [x] `<leader>gh_`
- [x] devicons support
- [x] nowait, timeoutlen and delay
- [x] new mappings DSL
- [x] News
- [x] normal mode mappings in terminal mode?
- [x] dynamic size
- [x] situation with visual mode
- [x] fix timeoutlen
- [x] document hydra mode
- [x] floating help text?
- [x] move old option check to checkhealth
- [x] show scrolling hint when can't fit all mappings
- [ ] more tests
- [ ] hint characters in desc
- [ ] intgerate with lazy.nvim. Get description there if set
================================================
FILE: doc/which-key.nvim.txt
================================================
*which-key.nvim.txt* which-key.nvim docs
==============================================================================
Table of Contents *which-key.nvim-table-of-contents*
1. Which Key |which-key.nvim-which-key|
- Features |which-key.nvim-which-key-features|
- Requirements |which-key.nvim-which-key-requirements|
- Installation |which-key.nvim-which-key-installation|
- Configuration |which-key.nvim-which-key-configuration|
- Mappings |which-key.nvim-which-key-mappings|
- Triggers |which-key.nvim-which-key-triggers|
- Icons |which-key.nvim-which-key-icons|
- Usage |which-key.nvim-which-key-usage|
- Hydra Mode |which-key.nvim-which-key-hydra-mode|
- Plugins |which-key.nvim-which-key-plugins|
- Colors |which-key.nvim-which-key-colors|
2. Links |which-key.nvim-links|
==============================================================================
1. Which Key *which-key.nvim-which-key*
**WhichKey** helps you remember your Neovim keymaps, by showing available
keybindings in a popup as you type.
FEATURES *which-key.nvim-which-key-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 `<esc>`
REQUIREMENTS *which-key.nvim-which-key-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 *which-key.nvim-which-key-installation*
Install the plugin with your package manager:
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 = {
{
"<leader>?",
function()
require("which-key").show({ global = false })
end,
desc = "Buffer Local Keymaps (which-key)",
},
},
}
<
CONFIGURATION *which-key.nvim-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 = {
{ "<auto>", 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 == "<C-V>"
end,
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
-- 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 <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
},
},
---@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 = "<c-d>", -- binding to scroll down inside the popup
scroll_up = "<c-u>", -- 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<string, ({[1]:string, [2]:string}|fun(str:string):string)[]>
replace = {
key = {
function(key)
return require("which-key.view").format(key)
end,
-- { "<Space>", "SPC" },
},
desc = {
{ "<Plug>%(?(.*)%)?", "%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 *which-key.nvim-which-key-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({
{ "<leader>f", group = "file" }, -- group
{ "<leader>ff", "<cmd>Telescope find_files<cr>", desc = "Find File", mode = "n" },
{ "<leader>fb", function() print("hello") end, desc = "Foobar" },
{ "<leader>fn", desc = "New File" },
{ "<leader>f1", hidden = true }, -- hide this keymap
{ "<leader>w", proxy = "<c-w>", group = "windows" }, -- proxy to window mappings
{ "<leader>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
{ "<leader>q", "<cmd>q<cr>", desc = "Quit" }, -- no need to specify mode since it's inherited
{ "<leader>w", "<cmd>w<cr>", desc = "Write" },
}
})
<
TRIGGERS *which-key.nvim-which-key-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 `{ "<auto>", 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 = {
{ "<auto>", mode = "nixsotc" },
{ "a", mode = { "n", "v" } },
}
<
[!TIP] To manually setup triggers, you can set `opts.triggers` to:
>lua
triggers = {
{ "<leader>", 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({ "<C-V>", "V" }, ctx.mode)
end,
<
ICONS *which-key.nvim-which-key-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 `<leader>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 *which-key.nvim-which-key-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
- `<esc>` to cancel and close the popup
- `<bs>` go up one level
- `<c-d>` scroll down
- `<c-u>` scroll up
HYDRA MODE *which-key.nvim-which-key-hydra-mode*
Hydra mode is a special mode that keeps the popup open until you hit `<esc>`.
>lua
-- Show hydra mode for changing windows
require("which-key").show({
keys = "<c-w>",
loop = true, -- this will keep the popup open until you hit <esc>
})
<
PLUGINS *which-key.nvim-which-key-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 ’
REGISTERS ~
Shows a list of your buffer local and global registers when you hit ” in
_NORMAL_ mode, or `<c-r>` in _INSERT_ mode.
SPELLING ~
When enabled, this plugin hooks into `z=` and replaces the full-screen spelling
suggestions window by a list of suggestions within **WhichKey**.
COLORS *which-key.nvim-which-key-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)
-----------------------------------------------------------------------
==============================================================================
2. Links *which-key.nvim-links*
1. *image*: https://github.com/user-attachments/assets/89277334-dcdc-4b0f-9fd4-02f27012f589
2. *image*: https://github.com/user-attachments/assets/f8d71a75-312e-4a42-add8-d153493b2633
3. *image*: https://github.com/user-attachments/assets/e4400a1d-7e71-4439-b6ff-6cbc40647a6f
4. *image*: https://github.com/user-attachments/assets/43fb0874-7f79-4521-aee9-03e2b0841758
5. *image*: https://github.com/user-attachments/assets/d8077dcb-56fb-47b0-ad9e-1aba5db16950
6. *image*: https://github.com/user-attachments/assets/102c7963-329a-40b9-b0a8-72c8656318b7
Generated by panvimdoc <https://github.com/kdheepak/panvimdoc>
vim:tw=78:ts=8:noet:ft=help:norl:
================================================
FILE: lua/which-key/buf.lua
================================================
local Config = require("which-key.config")
local Tree = require("which-key.tree")
local Triggers = require("which-key.triggers")
local Util = require("which-key.util")
---@class wk.Mode
---@field buf wk.Buffer
---@field mode string
---@field tree wk.Tree
---@field triggers wk.Node[]
local Mode = {}
Mode.__index = Mode
---@param node wk.Node
local function is_special(node)
return (node:is_plugin() or node:is_proxy()) and not node.keymap
end
--- Checks if it's safe to add a trigger for the given node
---@param node wk.Node
---@param no_single? boolean
local function is_safe(node, no_single)
if node.keymap or is_special(node) or node:count() == 0 then
return false
end
if no_single and #node.path == 1 then
local key = node.path[1]
-- only z or g are safe
if key:match("^[a-z]$") and not key:match("^[gz]$") then
return false
end
-- only Z is safe
if key:match("^[A-Z]$") and not key:match("^[Z]$") then
return false
end
end
return true
end
function Mode:__tostring()
return string.format("Mode(%s:%d)", self.mode, self.buf.buf)
end
---@param buf wk.Buffer
---@param mode string
function Mode.new(buf, mode)
local self = setmetatable({}, Mode)
self.buf = buf
self.mode = mode
self.tree = Tree.new()
self.triggers = {}
self:update()
return self
end
function Mode:attach()
self.triggers = {}
-- NOTE: order is important for nowait to work!
-- * first add plugin mappings
-- * then add triggers
self.tree:walk(function(node)
if is_special(node) and not node:has_nowait_ancestor() then
table.insert(self.triggers, node)
return false
end
end)
if Config.triggers.modes[self.mode] then
-- Auto triggers
self.tree:walk(function(node)
if node.keymap and node.keymap.nowait then
return false
end
if is_safe(node, true) then
table.insert(self.triggers, node)
return false
end
end)
end
-- Manual triggers
for _, t in ipairs(Config.triggers.mappings) do
if self:has(t) then
local node = self.tree:find(t.lhs)
if node and is_safe(node) then
table.insert(self.triggers, node)
end
end
end
Triggers.schedule(self)
end
function Mode:xo()
return self.mode:find("[xo]") ~= nil
end
function Mode:clear()
Triggers.detach(self)
self.tree:clear()
end
---@param mode string
function Mode:is(mode)
if mode == "v" then
return self.mode == "x" or self.mode == "s"
end
return self.mode == mode
end
---@param mapping wk.Keymap
function Mode:has(mapping)
return self:is(mapping.mode) and (not mapping.buffer or mapping.buffer == self.buf.buf)
end
function Mode:update()
self.tree:clear()
local mappings = vim.api.nvim_get_keymap(self.mode)
vim.list_extend(mappings, vim.api.nvim_buf_get_keymap(self.buf.buf, self.mode))
---@cast mappings wk.Keymap[]
for _, mapping in ipairs(mappings) do
if mapping.desc and mapping.desc:find("which-key-trigger", 1, true) then
-- ignore which-key triggers
elseif Util.is_nop(mapping.rhs) then
self.tree:add(mapping, true)
elseif mapping.lhs:sub(1, 6) ~= "<Plug>" and mapping.lhs:sub(1, 5) ~= "<SNR>" then
self.tree:add(mapping)
end
end
for _, m in ipairs(Config.mappings) do
if self:has(m) then
self.tree:add(m, true)
end
end
self.tree:fix()
self:attach()
vim.schedule(function()
require("which-key.state").update()
end)
end
---@class wk.Buffer
---@field buf number
---@field modes table<string, wk.Mode>
local Buf = {}
Buf.__index = Buf
---@param buf? number
function Buf.new(buf)
local self = setmetatable({}, Buf)
buf = buf or 0
self.buf = buf == 0 and vim.api.nvim_get_current_buf() or buf
self.modes = {}
return self
end
---@param opts? wk.Filter
function Buf:clear(opts)
opts = opts or {}
assert(not opts.buf or opts.buf == self.buf, "buffer mismatch")
---@type string[]
local modes = opts.mode and { opts.mode } or vim.tbl_keys(self.modes)
for _, m in ipairs(modes) do
local mode = self.modes[m]
if mode then
mode:clear()
self.modes[m] = nil
end
end
end
function Buf:valid()
return vim.api.nvim_buf_is_valid(self.buf)
end
---@param opts? wk.Filter
---@return wk.Mode?
function Buf:get(opts)
if not self:valid() then
return
end
opts = opts or {}
local mode = opts.mode or Util.mapmode()
local ret = self.modes[mode]
if not ret then
self.modes[mode] = Mode.new(self, mode)
Util.debug("new " .. tostring(self.modes[mode]))
return self.modes[mode]
elseif opts.update then
Util.debug("update " .. tostring(ret))
ret:update()
end
return ret
end
local M = {}
M.Buf = Buf
M.bufs = {} ---@type table<number,wk.Buffer>
---@param opts? wk.Filter
function M.get(opts)
M.cleanup()
opts = opts or {}
local buf = opts.buf or vim.api.nvim_get_current_buf()
if not vim.api.nvim_buf_is_valid(buf) then
return
end
local ft = vim.bo[buf].filetype
local bt = vim.bo[buf].buftype
if vim.tbl_contains(Config.disable.ft, ft) then
return
end
if vim.tbl_contains(Config.disable.bt, bt) then
return
end
M.bufs[buf] = M.bufs[buf] or Buf.new(buf)
return M.bufs[buf]:get(opts)
end
function M.cleanup()
for buf, _ in pairs(M.bufs) do
if not vim.api.nvim_buf_is_valid(buf) then
M.bufs[buf] = nil
end
end
end
---@param opts? wk.Filter
function M.clear(opts)
M.cleanup()
opts = opts or {}
---@type number[]
local bufs = opts.buf and { opts.buf } or vim.tbl_keys(M.bufs)
for _, b in ipairs(bufs) do
if M.bufs[b] then
M.bufs[b]:clear(opts)
end
end
end
return M
================================================
FILE: lua/which-key/colors.lua
================================================
local M = {}
M.colors = {
[""] = "Function", -- the key
Separator = "Comment", -- the separator between the key and its description
Group = "Keyword", -- group name
Desc = "Identifier", -- description
Normal = "NormalFloat", -- Normal in th which-key window
Title = "FloatTitle", -- Title of the which-key window
Border = "FloatBorder", -- Border of the which-key window
Value = "Comment", -- values by plugins (like marks, registers, etc)
Icon = "@markup.link", -- icons
IconAzure = "Function",
IconBlue = "DiagnosticInfo",
IconCyan = "DiagnosticHint",
IconGreen = "DiagnosticOk",
IconGrey = "Normal",
IconOrange = "DiagnosticWarn",
IconPurple = "Constant",
IconRed = "DiagnosticError",
IconYellow = "DiagnosticWarn",
}
function M.setup()
for k, v in pairs(M.colors) do
vim.api.nvim_set_hl(0, "WhichKey" .. k, { link = v, default = true })
end
M.fix_colors()
vim.api.nvim_create_autocmd("ColorScheme", {
group = vim.api.nvim_create_augroup("wk-colors", { clear = true }),
callback = M.fix_colors,
})
end
function M.fix_colors()
for k in pairs(M.colors) do
if k:find("^Icon") then
local color = k:gsub("^Icon", "")
local wk_hl_group = "WhichKeyIcon" .. color
local mini_hl_group = "MiniIcons" .. color
local wk_hl = vim.api.nvim_get_hl(0, {
name = wk_hl_group,
link = true,
})
local mini_hl = vim.api.nvim_get_hl(0, {
name = mini_hl_group,
link = true,
})
if wk_hl.default and not vim.tbl_isempty(mini_hl) then
vim.api.nvim_set_hl(0, wk_hl_group, { link = mini_hl_group })
end
end
end
end
return M
================================================
FILE: lua/which-key/config.lua
================================================
---@class wk.Config: wk.Opts
---@field triggers {mappings: wk.Mapping[], modes: table<string,boolean>}
local M = {}
M.version = "3.17.0" -- x-release-please-version
---@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 = {
{ "<auto>", 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 == "<C-V>"
end,
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
-- 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 <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
},
},
---@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 = "<c-d>", -- binding to scroll down inside the popup
scroll_up = "<c-u>", -- 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<string, ({[1]:string, [2]:string}|fun(str:string):string)[]>
replace = {
key = {
function(key)
return require("which-key.view").format(key)
end,
-- { "<Space>", "SPC" },
},
desc = {
{ "<Plug>%(?(.*)%)?", "%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
}
M.loaded = false
---@type wk.Keymap[]
M.mappings = {}
---@type wk.Opts
M.options = nil
---@type {opt:string, msg:string}[]
M.issues = {}
function M.validate()
local deprecated = {
["operators"] = "see `opts.defer`",
["key_labels"] = "see `opts.replace`",
"motions",
["popup_mappings"] = "see `opts.keys`",
["window"] = "see `opts.win`",
["ignore_missing"] = "see `opts.filter`",
"hidden",
["triggers_nowait"] = "see `opts.delay`",
["triggers_blacklist"] = "see `opts.triggers`",
["disable.trigger"] = "see `opts.triggers`",
["modes"] = "see `opts.triggers`",
}
for k, v in pairs(deprecated) do
local opt = type(k) == "number" and v or k
local msg = "option is deprecated." .. (type(k) == "number" and "" or " " .. v)
local parts = vim.split(opt, ".", { plain = true })
if vim.tbl_get(M.options, unpack(parts)) ~= nil then
table.insert(M.issues, { opt = opt, msg = msg })
end
end
if type(M.options.triggers) ~= "table" then
table.insert(M.issues, { opt = "triggers", msg = "triggers must be a table" })
end
end
---@param opts? wk.Opts
function M.setup(opts)
if vim.fn.has("nvim-0.9.4") == 0 then
return vim.notify("which-key.nvim requires Neovim >= 0.9.4", vim.log.levels.ERROR)
end
M.options = vim.tbl_deep_extend("force", {}, defaults, opts or {})
local function load()
if M.loaded then
return
end
local Util = require("which-key.util")
if M.options.preset then
local Presets = require("which-key.presets")
M.options = vim.tbl_deep_extend("force", {}, defaults, Presets[M.options.preset] or {}, opts or {})
end
M.validate()
if #M.issues > 0 then
Util.warn({
"There are issues with your config.",
"Use `:checkhealth which-key` to find out more.",
}, { once = true })
end
for k, v in pairs(M.options.keys) do
M.options.keys[k] = Util.norm(v)
end
if M.options.debug then
Util.debug("\n\nDebug Started for v" .. M.version)
if package.loaded.lazy then
local Git = require("lazy.manage.git")
local plugin = require("lazy.core.config").plugins["which-key.nvim"]
Util.debug(vim.inspect(Git.info(plugin.dir)))
end
end
local wk = require("which-key")
-- replace by the real add function
wk.add = M.add
if type(M.options.triggers) ~= "table" then
---@diagnostic disable-next-line: inject-field
M.options.triggers = defaults.triggers
end
M.triggers = {
mappings = require("which-key.mappings").parse(M.options.triggers),
modes = {},
}
---@param m wk.Mapping
M.triggers.mappings = vim.tbl_filter(function(m)
if m.lhs == "<auto>" then
M.triggers.modes[m.mode] = true
return false
end
return true
end, M.triggers.mappings)
-- load presets first so that they can be overriden by the user
require("which-key.plugins").setup()
-- process mappings queue
for _, todo in ipairs(wk._queue) do
M.add(todo.spec, todo.opts)
end
wk._queue = {}
-- finally, add the mapppings from the config
M.add(M.options.spec)
-- setup colors and start which-key
require("which-key.colors").setup()
require("which-key.state").setup()
M.loaded = true
end
local _load = vim.schedule_wrap(load)
if vim.v.vim_did_enter == 1 then
_load()
else
vim.api.nvim_create_autocmd("VimEnter", { once = true, callback = _load })
end
vim.api.nvim_create_user_command("WhichKey", function(cmd)
load()
local mode, keys = cmd.args:match("^([nixsotc]?)%s*(.*)$")
if not mode then
return require("which-key.util").error("Usage: WhichKey [mode] [keys]")
end
if mode == "" then
mode = "n"
end
require("which-key").show({ mode = mode, keys = keys })
end, {
nargs = "*",
})
end
---@param opts? wk.Parse
---@param mappings wk.Spec
function M.add(mappings, opts)
opts = opts or {}
opts.create = opts.create ~= false
local Mappings = require("which-key.mappings")
for _, km in ipairs(Mappings.parse(mappings, opts)) do
table.insert(M.mappings, km)
km.idx = #M.mappings
end
if M.loaded then
require("which-key.buf").clear()
end
end
return setmetatable(M, {
__index = function(_, k)
if rawget(M, "options") == nil then
M.setup()
end
local opts = rawget(M, "options")
return k == "options" and opts or opts[k]
end,
})
================================================
FILE: lua/which-key/docs.lua
================================================
local Docs = require("lazy.docs")
local M = {}
function M.update()
local config = Docs.extract("lua/which-key/config.lua", "\n(--@class wk%.Opts.-\n})")
config = config:gsub("%s*debug = false.\n", "\n")
Docs.save({
config = config,
colors = Docs.colors({
modname = "which-key.colors",
path = "lua/which-key/colors.lua",
name = "WhichKey",
}),
})
end
M.update()
print("Updated docs")
return M
================================================
FILE: lua/which-key/extras.lua
================================================
local M = {}
---@type table<string, fun():wk.Spec>
M.expand = {}
---@return number[]
function M.bufs()
local current = vim.api.nvim_get_current_buf()
return vim.tbl_filter(function(buf)
return buf ~= current and vim.bo[buf].buflisted
end, vim.api.nvim_list_bufs())
end
function M.bufname(buf)
local name = vim.api.nvim_buf_get_name(buf)
return name == "" and "[No Name]" or vim.fn.fnamemodify(name, ":~:.")
end
---@param spec wk.Spec[]
function M.add_keys(spec)
table.sort(spec, function(a, b)
return a.desc < b.desc
end)
spec = vim.list_slice(spec, 1, 10)
for i, v in ipairs(spec) do
v[1] = tostring(i - 1)
end
return spec
end
function M.expand.buf()
local ret = {} ---@type wk.Spec[]
for _, buf in ipairs(M.bufs()) do
local name = M.bufname(buf)
ret[#ret + 1] = {
"",
function()
vim.api.nvim_set_current_buf(buf)
end,
desc = name,
icon = { cat = "file", name = name },
}
end
return M.add_keys(ret)
end
function M.expand.win()
---@type wk.Spec[]
local ret = {}
local current = vim.api.nvim_get_current_win()
for _, win in ipairs(vim.api.nvim_list_wins()) do
local is_float = vim.api.nvim_win_get_config(win).relative ~= ""
if win ~= current and not is_float then
local buf = vim.api.nvim_win_get_buf(win)
local name = M.bufname(buf)
ret[#ret + 1] = {
"",
function()
vim.api.nvim_set_current_win(win)
end,
desc = name,
icon = { cat = "file", name = name },
}
end
end
return M.add_keys(ret)
end
return M
================================================
FILE: lua/which-key/health.lua
================================================
local Buf = require("which-key.buf")
local Config = require("which-key.config")
local Icons = require("which-key.icons")
local Mappings = require("which-key.mappings")
local Migrate = require("which-key.migrate")
local Tree = require("which-key.tree")
local Util = require("which-key.util")
local M = {}
local start = vim.health.start or vim.health.report_start
local ok = vim.health.ok or vim.health.report_ok
local warn = vim.health.warn or vim.health.report_warn
local error = vim.health.error or vim.health.report_error
local info = vim.health.info or vim.health.report_info
-- TODO: Add more checks
-- * duplicate desc
-- * mapping.desc ~= keymap.desc
-- * check for old-style mappings
function M.check()
ok(
"Most of these checks are for informational purposes only.\n"
.. "WARNINGS should be treated as a warning, and don't necessarily indicate a problem with your config.\n"
.. "Please |DON'T| report these warnings as an issue."
)
start("Checking your config")
if #Config.issues > 0 then
local msg = {
"There are issues with your config:",
}
vim.list_extend(
msg,
vim.tbl_map(function(issue)
return "- `opts." .. issue.opt .. "`: " .. issue.msg
end, Config.issues)
)
msg[#msg + 1] = "Please refer to the docs for more info."
warn(table.concat(msg, "\n"))
end
local have_icons = false
for _, provider in ipairs(Icons.providers) do
if provider.available == nil then
provider.available = pcall(require, provider.name)
end
if provider.available then
ok("|" .. provider.name .. "| is installed")
have_icons = true
else
warn("|" .. provider.name .. "| is not installed")
end
end
if not have_icons then
warn("Keymap icon support will be limited.")
end
start("Checking for issues with your mappings")
if #Mappings.notifs == 0 then
ok("No issues reported")
end
for _, notif in ipairs(Mappings.notifs) do
local msg = notif.msg
if notif.spec then
msg = msg .. ": >\n" .. vim.inspect(notif.spec)
if msg:find("old version") then
local fixed = Migrate.migrate(notif.spec)
msg = msg .. "\n\n-- Suggested Spec:\n" .. fixed
end
end
(notif.level >= vim.log.levels.ERROR and error or warn)(msg)
end
start("checking for overlapping keymaps")
local found = false
Buf.cleanup()
---@type table<string, boolean>
local reported = {}
local mapmodes = vim.split("nixsotc", "")
for _, buf in pairs(Buf.bufs) do
for _, mapmode in ipairs(mapmodes) do
local mode = buf:get({ mode = mapmode })
if mode then
mode.tree:walk(function(node)
local km = node.keymap
if not km or Util.is_nop(km.rhs) or node.keys:sub(1, 6) == "<Plug>" then
return
end
if node.keymap and node:count() > 0 then
local id = mode.mode .. ":" .. node.keys
if reported[id] then
return
end
reported[id] = true
local overlaps = {}
local descs = {}
if node.desc and node.desc ~= "" then
descs[#descs + 1] = "- <" .. node.keys .. ">: " .. node.desc
end
local queue = node:children()
while #queue > 0 do
local child = table.remove(queue)
if child.keymap then
table.insert(overlaps, "<" .. child.keys .. ">")
if child.desc and child.desc ~= "" then
descs[#descs + 1] = "- <" .. child.keys .. ">: " .. child.desc
end
end
vim.list_extend(queue, child:children())
end
if #overlaps > 0 then
found = true
warn(
"In mode `"
.. mode.mode
.. "`, <"
.. node.keys
.. "> overlaps with "
.. table.concat(overlaps, ", ")
.. ":\n"
.. table.concat(descs, "\n")
)
end
return false
end
end)
end
end
end
if found then
ok(
"Overlapping keymaps are only reported for informational purposes.\n"
.. "This doesn't necessarily mean there is a problem with your config."
)
else
ok("No overlapping keymaps found")
end
start("Checking for duplicate mappings")
if vim.tbl_isempty(Tree.dups) then
ok("No duplicate mappings found")
else
for _, mappings in pairs(Tree.dups) do
---@type wk.Mapping[]
mappings = vim.tbl_keys(mappings)
local first = mappings[1]
warn(
"Duplicates for <"
.. first.lhs
.. "> in mode `"
.. first.mode
.. "`:\n"
.. table.concat(
vim.tbl_map(function(m)
m = vim.deepcopy(m)
local desc = (m.desc and (m.desc .. ": ") or "")
m.desc = nil
m.idx = nil
m.mode = nil
m.lhs = nil
return "* " .. desc .. "`" .. vim.inspect(m):gsub("%s+", " ") .. "`"
end, mappings),
"\n"
)
)
end
ok(
"Duplicate mappings are only reported for informational purposes.\n"
.. "This doesn't necessarily mean there is a problem with your config."
)
end
end
return M
================================================
FILE: lua/which-key/icons.lua
================================================
local Config = require("which-key.config")
local M = {}
--- * `WhichKeyColorAzure` - azure.
--- * `WhichKeyColorBlue` - blue.
--- * `WhichKeyColorCyan` - cyan.
--- * `WhichKeyColorGreen` - green.
--- * `WhichKeyColorGrey` - grey.
--- * `WhichKeyColorOrange` - orange.
--- * `WhichKeyColorPurple` - purple.
--- * `WhichKeyColorRed` - red.
--- * `WhichKeyColorYellow` - yellow.
---@type wk.IconRule[]
M.rules = {
{ plugin = "fzf-lua", cat = "filetype", name = "fzf" },
{ plugin = "neo-tree.nvim", cat = "filetype", name = "neo-tree" },
{ plugin = "octo.nvim", cat = "filetype", name = "git" },
{ plugin = "yanky.nvim", icon = "", color = "yellow" },
{ plugin = "zen-mode.nvim", icon = " ", color = "cyan" },
{ plugin = "telescope.nvim", pattern = "telescope", icon = "", color = "blue" },
{ plugin = "trouble.nvim", cat = "filetype", name = "trouble" },
{ plugin = "todo-comments.nvim", cat = "file", name = "TODO" },
{ plugin = "grapple.nvim", pattern = "grapple", icon = "", color = "azure" },
{ plugin = "nvim-spectre", icon = " ", color = "blue" },
{ plugin = "grug-far.nvim", pattern = "grug", icon = " ", color = "blue" },
{ plugin = "noice.nvim", pattern = "noice", icon = "", color = "orange" },
{ plugin = "persistence.nvim", icon = " ", color = "azure" },
{ plugin = "neotest", cat = "filetype", name = "neotest-summary" },
{ plugin = "lazy.nvim", cat = "filetype", name = "lazy" },
{ plugin = "snacks.nvim", icon = " ", color = "purple" },
{ plugin = "refactoring.nvim", pattern = "refactor", icon = " ", color = "cyan" },
{ pattern = "profiler", icon = "⚡", color = "orange" },
{ plugin = "CopilotChat.nvim", icon = " ", color = "orange" },
{ pattern = "%f[%a]git", cat = "filetype", name = "git" },
{ pattern = "terminal", icon = " ", color = "red" },
{ pattern = "find", icon = " ", color = "green" },
{ pattern = "search", icon = " ", color = "green" },
{ pattern = "test", cat = "filetype", name = "neotest-summary" },
{ pattern = "lazy", cat = "filetype", name = "lazy" },
{ pattern = "buffer", icon = "", color = "cyan" },
{ pattern = "file", icon = "", color = "cyan" },
{ pattern = "window", icon = " ", color = "blue" },
{ pattern = "diagnostic", icon = " ", color = "green" },
{ pattern = "format", icon = " ", color = "cyan" },
{ pattern = "debug", icon = " ", color = "red" },
{ pattern = "code", icon = " ", color = "orange" },
{ pattern = "notif", icon = " ", color = "blue" },
{ pattern = "toggle", icon = " ", color = "yellow" },
{ pattern = "session", icon = " ", color = "azure" },
{ pattern = "exit", icon = " ", color = "red" },
{ pattern = "quit", icon = " ", color = "red" },
{ pattern = "tab", icon = " ", color = "purple" },
{ pattern = "%f[%a]ai", icon = " ", color = "green" },
{ pattern = "ui", icon = " ", color = "cyan" },
}
---@type wk.IconProvider[]
M.providers = {
{
name = "mini.icons",
get = function(icon)
local Icons = require("mini.icons")
local ico, ico_hl, ico_def = Icons.get(icon.cat, icon.name) --[[@as string, string, boolean]]
if not ico_def then
return ico, ico_hl
end
end,
},
{
name = "nvim-web-devicons",
get = function(icon)
local Icons = require("nvim-web-devicons")
if icon.cat == "filetype" then
return Icons.get_icon_by_filetype(icon.name, { default = false })
elseif icon.cat == "file" then
return Icons.get_icon(icon.name, nil, { default = false }) --[[@as string, string]]
elseif icon.cat == "extension" then
return Icons.get_icon(nil, icon.name, { default = false }) --[[@as string, string]]
end
end,
},
}
---@return wk.IconProvider?
function M.get_provider()
for _, provider in ipairs(M.providers) do
if provider.available == nil then
provider.available = pcall(require, provider.name)
end
if provider.available then
return provider
end
end
end
function M.have()
return M.get_provider() ~= nil
end
---@param icon wk.Icon|string
---@return string?, string?
function M.get_icon(icon)
icon = type(icon) == "string" and { cat = "filetype", name = icon } or icon --[[@as wk.Icon]]
---@type string?, string?
local ret, hl
if icon.icon then
ret, hl = icon.icon, icon.hl
elseif icon.cat and icon.name then
local provider = M.get_provider()
if provider then
ret, hl = provider.get(icon)
end
end
if ret then
if icon.color then
hl = "WhichKeyIcon" .. icon.color:sub(1, 1):upper() .. icon.color:sub(2)
end
if not hl or Config.icons.colors == false or icon.color == false then
hl = "WhichKeyIcon"
end
return ret, hl
end
end
---@param rules wk.IconRule[]
---@param opts? {keymap?: wk.Keymap, desc?: string, ft?:string|string[]}|wk.Icon
---@param check_ft? boolean
---@return string?, string?
function M._get(rules, opts, check_ft)
opts = opts or {}
opts.ft = type(opts.ft) == "string" and { opts.ft } or opts.ft
---@type string?
local plugin
local fts = opts.ft or {} --[[@as string[] ]]
if opts.keymap and package.loaded.lazy then
local LazyUtil = require("lazy.core.util")
local Keys = require("lazy.core.handler").handlers.keys --[[@as LazyKeysHandler]]
local keys = Keys.parse(opts.keymap.lhs, opts.keymap.mode)
plugin = Keys.managed[keys.id]
if plugin then
fts[#fts + 1] = LazyUtil.normname(plugin)
end
end
-- plugin icons
if plugin then
for _, icon in ipairs(rules) do
if icon.plugin == plugin then
local ico, hl = M.get_icon(icon)
if ico then
return ico, hl
end
end
end
end
-- filetype icons
if check_ft then
if opts.keymap and opts.keymap.buffer and opts.keymap.buffer ~= 0 then
pcall(function()
fts[#fts + 1] = vim.bo[opts.keymap.buffer].filetype
end)
end
for _, ft in ipairs(fts) do
local icon, hl = M.get_icon({ cat = "filetype", name = ft })
if icon then
return icon, hl
end
end
end
-- pattern icons
if opts.desc then
for _, icon in ipairs(rules) do
if icon.pattern and opts.desc:lower():find(icon.pattern) then
local ico, hl = M.get_icon(icon)
if ico then
return ico, hl
end
end
end
end
end
---@param opts {keymap?: wk.Keymap, desc?: string, ft?:string|string[]}|wk.Icon|string
function M.get(opts)
if not Config.icons.mappings then
return
end
if type(opts) == "string" then
opts = { icon = opts }
end
if opts.icon or opts.cat then
return M.get_icon(opts)
end
if Config.icons.rules == false then
return
end
local icon, hl = M._get(Config.icons.rules, opts)
if icon then
return icon, hl
end
return M._get(M.rules, opts, true)
end
return M
================================================
FILE: lua/which-key/init.lua
================================================
---@class wk
---@field private _queue {spec: wk.Spec, opts?: wk.Parse}[]
local M = {}
M._queue = {}
M.did_setup = false
--- Open which-key
---@param opts? wk.Filter|string
function M.show(opts)
opts = opts or {}
opts = type(opts) == "string" and { keys = opts } or opts
if opts.delay == nil then
opts.delay = 0
end
opts.waited = vim.o.timeoutlen
---@diagnostic disable-next-line: param-type-mismatch
if not require("which-key.state").start(opts) then
require("which-key.util").warn(
"No mappings found for mode `" .. (opts.mode or "n") .. "` and keys `" .. (opts.keys or "") .. "`"
)
end
end
---@param opts? wk.Opts
function M.setup(opts)
M.did_setup = true
require("which-key.config").setup(opts)
end
-- Use `require("which-key").add()` instead.
-- The spec is different though, so check the docs!
---@deprecated
---@param mappings wk.Spec
---@param opts? wk.Mapping
function M.register(mappings, opts)
if opts then
for k, v in pairs(opts) do
mappings[k] = v
end
end
M.add(mappings, { version = 1 })
end
--- Add mappings to which-key
---@param mappings wk.Spec
---@param opts? wk.Parse
function M.add(mappings, opts)
table.insert(M._queue, { spec = mappings, opts = opts })
end
return M
================================================
FILE: lua/which-key/layout.lua
================================================
local Config = require("which-key.config")
local M = {}
local dw = vim.fn.strdisplaywidth
--- When `size` is a number, it is returned as is (fixed dize).
--- Otherwise, it is a percentage of `parent` (relative size).
--- If `size` is negative, it is subtracted from `parent`.
--- If `size` is a table, it is a range of values.
---@alias wk.Dim number|{min:number, max:number}
---@param size number
---@param parent number
---@param ... wk.Dim
---@return number
function M.dim(size, parent, ...)
size = math.abs(size) < 1 and parent * size or size
size = size < 0 and parent + size or size
for _, dim in ipairs({ ... } --[[ @as wk.Dim[] ]]) do
if type(dim) == "number" then
size = M.dim(dim, parent)
else
local min = dim.min and M.dim(dim.min, parent) or 0
local max = dim.max and M.dim(dim.max, parent) or parent
size = math.max(min, math.min(max, size))
end
end
return math.floor(math.max(0, math.min(parent, size)) + 0.5)
end
---@class wk.Table: wk.Table.opts
local Table = {}
Table.__index = Table
---@param opts wk.Table.opts
function Table.new(opts)
local self = setmetatable({}, Table)
self.cols = opts.cols
self.rows = opts.rows
return self
end
---@param opts? {spacing?: number}
---@return string[][], number[], number
function Table:cells(opts)
opts = opts or {}
opts.spacing = opts.spacing or 1
local widths = {} ---@type number[] actual column widths
local cells = {} ---@type string[][]
local total = 0
for c, col in ipairs(self.cols) do
widths[c] = 0
local all_ws = true
for r, row in ipairs(self.rows) do
cells[r] = cells[r] or {}
local value = row[col.key] or col.default or ""
value = tostring(value)
value = value:gsub("%s*$", "")
value = value:gsub("\n", Config.icons.keys.NL)
value = vim.fn.strtrans(value)
if value:find("%S") then
all_ws = false
end
if col.padding then
value = (" "):rep(col.padding[1] or 0) .. value .. (" "):rep(col.padding[2] or 0)
end
if c ~= #self.cols then
value = value .. (" "):rep(opts.spacing)
end
cells[r][c] = value
widths[c] = math.max(widths[c], dw(value))
end
if all_ws then
widths[c] = 0
for _, cell in pairs(cells) do
cell[c] = ""
end
end
total = total + widths[c]
end
return cells, widths, total
end
---@param opts {width: number, spacing?: number}
function Table:layout(opts)
local cells, widths = self:cells(opts)
local free = opts.width
for c, col in ipairs(self.cols) do
if not col.width then
free = free - widths[c]
end
end
free = math.max(free, 0)
for c, col in ipairs(self.cols) do
if col.width then
widths[c] = M.dim(widths[c], free, { max = col.width })
free = free - widths[c]
end
end
---@type {value: string, hl?:string}[][]
local ret = {}
for _, row in ipairs(cells) do
---@type {value: string, hl?:string}[]
local line = {}
for c, col in ipairs(self.cols) do
local value = row[c]
local width = dw(value)
if width > widths[c] then
local old = value
value = ""
for i = 0, vim.fn.strchars(old) do
value = value .. vim.fn.strcharpart(old, i, 1)
if dw(value) >= widths[c] - 1 - (opts.spacing or 1) then
break
end
end
value = value .. Config.icons.ellipsis .. string.rep(" ", opts.spacing or 1)
else
local align = col.align or "left"
if align == "left" then
value = value .. (" "):rep(widths[c] - width)
elseif align == "right" then
value = (" "):rep(widths[c] - width) .. value
elseif align == "center" then
local pad = (widths[c] - width) / 2
value = (" "):rep(math.floor(pad)) .. value .. (" "):rep(math.ceil(pad))
end
end
line[#line + 1] = { value = value, hl = col.hl }
end
ret[#ret + 1] = line
end
return ret
end
M.new = Table.new
return M
================================================
FILE: lua/which-key/mappings.lua
================================================
local Config = require("which-key.config")
local Util = require("which-key.util")
local M = {}
M.VERSION = 2
M.notifs = {} ---@type {msg:string, level:number, spec?:wk.Spec}[]
---@class wk.Field
---@field transform? string|(fun(value: any, parent:table): (value:any, key:string?))
---@field inherit? boolean
---@field deprecated? boolean
---@class wk.Parse
---@field version? number
---@field create? boolean
---@field notify? boolean
M.notify = true
---@type table<string, wk.Field>
M.fields = {
-- map args
rhs = {},
lhs = {},
buffer = { inherit = true },
callback = { transform = "rhs" },
desc = {},
expr = { inherit = true },
mode = { inherit = true },
noremap = {
transform = function(value)
return not value, "remap"
end,
},
nowait = { inherit = true },
remap = { inherit = true },
replace_keycodes = { inherit = true },
script = {},
silent = { inherit = true },
unique = { inherit = true },
-- wk args
plugin = { inherit = true },
group = {},
hidden = { inherit = true },
cond = { inherit = true },
preset = { inherit = true },
icon = { inherit = true },
real = { inherit = true },
proxy = {},
expand = {},
-- deprecated
name = { transform = "group", deprecated = true },
prefix = { inherit = true, deprecated = true },
cmd = { transform = "rhs", deprecated = true },
}
---@param msg string
---@param spec? wk.Spec
function M.error(msg, spec)
M.log(msg, vim.log.levels.ERROR, spec)
end
---@param msg string
---@param spec? wk.Spec
function M.warn(msg, spec)
M.log(msg, vim.log.levels.WARN, spec)
end
---@param msg string
---@param level number
---@param spec? wk.Spec
function M.log(msg, level, spec)
if not M.notify then
return
end
M.notifs[#M.notifs + 1] = { msg = msg, level = level, spec = spec }
if Config.notify then
Util.warn({
"There were issues reported with your **which-key** mappings.",
"Use `:checkhealth which-key` to find out more.",
}, { once = true })
end
end
---@param spec wk.Spec
---@param field string|number
---@param types string|string[]
function M.expect(spec, field, types)
types = type(types) == "string" and { types } or types
local ok = false
for _, t in ipairs(types) do
if type(spec[field]) == t then
ok = true
break
end
end
if not ok then
M.error("Expected `" .. field .. "` to be " .. table.concat(types, ", "), spec)
end
return ok
end
---@param spec wk.Spec
---@param ret? wk.Mapping[]
---@param opts? wk.Parse
function M._parse(spec, ret, opts)
opts = opts or {}
opts.version = opts.version or M.VERSION
if spec.version then
opts.version = spec.version
spec.version = nil
end
if ret == nil and opts.version ~= M.VERSION then
M.warn(
"You're using an old version of the which-key spec.\n"
.. "Your mappings will work, but it's recommended to update them to the new version.\n"
.. "Please check the docs and suggested spec below for more info.\nMappings",
vim.deepcopy(spec)
)
end
ret = ret or {}
spec = type(spec) == "string" and { desc = spec } or spec
---@type wk.Mapping
local mapping = {}
---@type wk.Spec[]
local children = {}
local keys = vim.tbl_keys(spec)
table.sort(keys, function(a, b)
local ta, tb = type(a), type(b)
if type(a) == type(b) then
return a < b
end
return ta < tb
end)
-- process fields
for _, k in ipairs(keys) do
local v = spec[k]
local field = M.fields[k] ---@type wk.Field?
if field then
if type(field.transform) == "string" then
k = field.transform --[[@as string]]
elseif type(field.transform) == "function" then
local vv, kk = field.transform(v, spec)
v, k = vv, (kk or k)
end
mapping[k] = v
elseif type(k) == "string" then
if opts.version == 1 then
if M.expect(spec, k, { "string", "table" }) then
if type(v) == "string" then
table.insert(children, { prefix = (spec.prefix or "") .. k, desc = v })
elseif type(v) == "table" then
v.prefix = (spec.prefix or "") .. k
table.insert(children, v)
end
end
else
M.error("Invalid field `" .. k .. "`", spec)
end
elseif type(k) == "number" and type(v) == "table" then
if opts.version == 1 then
v.prefix = spec.prefix or ""
end
table.insert(children, v)
spec[k] = nil
end
end
local count = #spec
-- process mapping
if opts.version == M.VERSION then
if count == 1 then
if M.expect(spec, 1, "string") then
mapping.lhs = spec[1] --[[@as string]]
end
elseif count == 2 then
if M.expect(spec, 1, "string") and M.expect(spec, 2, { "string", "function" }) then
mapping.lhs = spec[1] --[[@as string]]
mapping.rhs = spec[2] --[[@as string]]
end
elseif count > 2 then
M.error("expected 1 or 2 elements, got " .. count, spec)
end
elseif opts.version == 1 then
if mapping.expr and mapping.replace_keycodes == nil then
mapping.replace_keycodes = false
end
if count == 1 then
if M.expect(spec, 1, "string") then
if mapping.desc then
M.warn("overwriting desc", spec)
end
mapping.desc = spec[1] --[[@as string]]
end
elseif count == 2 then
if M.expect(spec, 1, { "string", "function" }) and M.expect(spec, 2, "string") then
if mapping.desc then
M.warn("overwriting desc", spec)
end
mapping.rhs = spec[1] --[[@as string]]
mapping.desc = spec[2] --[[@as string]]
end
elseif count > 2 then
M.error("expected 1 or 2 elements, got " .. count, spec)
end
end
-- add mapping
M.add(mapping, ret, opts)
-- process children
for _, child in ipairs(children) do
for k, v in pairs(mapping) do
if M.fields[k] and M.fields[k].inherit and child[k] == nil then
child[k] = v
end
end
M._parse(child, ret, opts)
end
return ret
end
---@param mapping wk.Spec
---@param opts? wk.Parse
---@param ret wk.Mapping[]
function M.add(mapping, ret, opts)
opts = opts or {}
if mapping.cond == false or ((type(mapping.cond) == "function") and not mapping.cond()) then
return
end
---@cast mapping wk.Mapping|wk.Spec
mapping.cond = nil
if mapping.desc == "which_key_ignore" then
mapping.hidden = true
mapping.desc = nil
end
if type(mapping.group) == "string" or type(mapping.group) == "function" then
mapping.desc = mapping.group --[[@as string]]
mapping.group = true
end
if mapping.plugin then
mapping.group = true
end
if mapping.group and mapping.desc then
mapping.desc = mapping.desc
if type(mapping.desc) == "string" then
mapping.desc = mapping.desc:gsub("^%+", "")
end
end
if mapping.buffer == 0 or mapping.buffer == true then
mapping.buffer = vim.api.nvim_get_current_buf()
end
if mapping.rhs then
mapping.silent = mapping.silent ~= false
end
mapping.lhs = mapping.lhs or mapping.prefix
if not mapping.lhs then
return
end
mapping.prefix = nil
local has_desc = mapping.desc ~= nil
Util.getters(mapping, { "desc", "icon" })
if has_desc or mapping.group or mapping.hidden or mapping.rhs or (opts.version == M.VERSION and mapping.lhs) then
local modes = mapping.mode or { "n" } --[[@as string|string[] ]]
modes = type(modes) == "string" and vim.split(modes, "") or modes --[[@as string[] ]]
for _, mode in ipairs(modes) do
if mode ~= "v" and mode ~= Util.mapmode(mode) then
M.warn("Invalid mode `" .. mode .. "`", mapping)
end
local m = vim.deepcopy(mapping)
m.mode = mode
table.insert(ret, m)
end
end
end
---@param mapping wk.Mapping
function M.create(mapping)
assert(mapping.lhs, "Missing lhs")
assert(mapping.mode, "Missing mode")
assert(mapping.rhs, "Missing rhs")
local valid =
{ "remap", "noremap", "buffer", "silent", "nowait", "expr", "unique", "script", "desc", "replace_keycodes" }
local opts = {} ---@type vim.keymap.set.Opts
for _, k in ipairs(valid) do
if mapping[k] ~= nil then
opts[k] = mapping[k]
end
end
vim.keymap.set(mapping.mode, mapping.lhs, mapping.rhs, opts)
end
---@param spec wk.Spec
---@param opts? wk.Parse
function M.parse(spec, opts)
opts = opts or {}
M.notify = opts.notify ~= false
local ret = M._parse(spec, nil, opts)
M.notify = true
for _, m in ipairs(ret) do
if m.rhs and opts.create then
M.create(m)
end
end
return ret
end
return M
================================================
FILE: lua/which-key/migrate.lua
================================================
local Mappings = require("which-key.mappings")
local M = {}
---@param spec wk.Spec
function M.migrate(spec)
spec = vim.deepcopy(spec)
local mappings = Mappings.parse(spec, { version = 1, notify = false })
---@type table<string, {m:wk.Mapping, mode:string[]}>
local mapping_modes = {}
for _, m in ipairs(mappings) do
m.preset = nil
m[1] = m.lhs:gsub("<lt>", "<")
m[2] = m.rhs
m.lhs = nil
m.rhs = nil
local mode = m.mode
m.mode = nil
if m.silent then
m.silent = nil
end
if m.group then
m.group = m.desc
m.desc = nil
end
local id = vim.inspect(m)
mapping_modes[id] = mapping_modes[id] or { m = m, mode = {} }
table.insert(mapping_modes[id].mode, mode)
end
mappings = vim.tbl_map(function(v)
local m = v.m
if not vim.deep_equal(v.mode, { "n" }) then
m.mode = v.mode
end
return m
end, vim.tbl_values(mapping_modes))
table.sort(mappings, function(a, b)
return a[1] < b[1]
end)
-- Group by modes
---@type table<string, wk.Mapping[]>
local modes = {}
for _, m in pairs(mappings) do
local mode = m.mode or {}
table.sort(mode)
local id = table.concat(mode)
modes[id] = modes[id] or {}
table.insert(modes[id], m)
end
local lines = {}
for mode, maps in pairs(modes) do
if #maps > 2 and mode ~= "" then
lines[#lines + 1] = " {"
lines[#lines + 1] = " mode = " .. vim.inspect(maps[1].mode) .. ","
for _, m in ipairs(maps) do
m.mode = nil
lines[#lines + 1] = " " .. vim.inspect(m):gsub("%s+", " ") .. ","
end
lines[#lines + 1] = " },"
else
for _, m in ipairs(maps) do
if m.mode and #m.mode == 1 then
m.mode = m.mode[1]
end
lines[#lines + 1] = " " .. vim.inspect(m):gsub("%s+", " ") .. ","
end
end
end
return "{\n" .. table.concat(lines, "\n") .. "\n}"
end
return M
================================================
FILE: lua/which-key/node.lua
================================================
local Util = require("which-key.util")
---@class wk.Node
---@field _children table<string, wk.Node>
local M = {}
---@param parent? wk.Node
---@param key? string
---@return wk.Node
function M.new(parent, key)
local self = setmetatable({}, M)
self.parent = parent
self.key = key or ""
self.path = {}
self.global = true
self._children = {}
self.keys = (parent and parent.keys or "") .. self.key
for _, p in ipairs(parent and parent.path or {}) do
table.insert(self.path, p)
end
if key then
table.insert(self.path, key)
end
return self
end
function M:has_nowait_ancestor()
local node = self
while node do
if node.keymap and node.keymap.nowait then
return true
end
node = node.parent
end
return false
end
function M:is_local()
if self.path[1] == Util.norm("<localleader>") then
return true
end
if self.buffer and self.buffer > 0 then
return true
end
for _, child in pairs(self._children) do
if child:is_local() then
return true
end
end
return false
end
function M:__index(k)
if k == "mapping" or k == "keymap" then
return
end
local v = rawget(M, k)
if v ~= nil then
return v
end
for _, m in ipairs({ "mapping", "keymap" }) do
local mm = rawget(self, m)
if k == m then
return mm
end
if mm and mm[k] ~= nil then
return mm[k]
end
end
end
function M:__tostring()
local info = { "Node(" .. self.keys .. ")" }
if self:is_plugin() then
info[#info + 1] = "Plugin(" .. self.plugin .. ")"
end
if self:is_proxy() then
info[#info + 1] = "Proxy(" .. self.mapping.proxy .. ")"
end
return table.concat(info, " ")
end
---@param depth? number
function M:inspect(depth)
local indent = (" "):rep(depth or 0)
local ret = { indent .. tostring(self) }
for _, child in ipairs(self:children()) do
table.insert(ret, child:inspect((depth or 0) + 1))
end
return table.concat(ret, "\n")
end
function M:count()
return not self:can_expand() and vim.tbl_count(self._children) or #self:children()
end
function M:is_group()
return self:can_expand() or self:count() > 0
end
function M:is_proxy()
return self.mapping and self.mapping.proxy
end
function M:is_plugin()
return self.plugin ~= nil
end
function M:can_expand()
return self.plugin or (self.mapping and (self.mapping.proxy or self.mapping.expand))
end
---@return wk.Node[]
function M:children()
return vim.tbl_values(self:expand())
end
---@return table<string, wk.Node>
function M:expand()
if not self:can_expand() then
return self._children
end
---@type table<string, wk.Node>
local ret = {}
-- plugin mappings
if self.plugin then
local plugin = require("which-key.plugins").plugins[self.plugin or ""]
assert(plugin, "plugin not found")
Util.debug(("Plugin(%q).expand"):format(self.plugin))
for i, item in ipairs(plugin.expand()) do
item.order = i
local child = M.new(self, item.key) --[[@as wk.Node.plugin.item]]
setmetatable(child, { __index = setmetatable(item, M) })
ret[item.key] = child
end
end
-- proxy mappings
local proxy = self.mapping.proxy
if proxy then
local keys = Util.keys(proxy)
local root = self:root()
local node = root:find(keys, { expand = true })
if node then
for k, v in pairs(node:expand()) do
ret[k] = v
end
end
end
-- expand mappings
local expand = self.mapping and self.mapping.expand
if expand then
local Tree = require("which-key.tree")
local tmp = Tree.new()
local mappings = require("which-key.mappings").parse(expand())
for _, mapping in ipairs(mappings) do
tmp:add(mapping, true)
end
for _, child in ipairs(tmp.root:children()) do
local action = child.mapping and child.mapping.rhs
if type(action) == "function" then
child.action = action
elseif type(action) == "string" then
Util.error("expand mappings only support functions as rhs:\n" .. vim.inspect(child.mapping))
end
ret[child.key] = child
end
end
-- custom mappings
for k, v in pairs(self._children) do
ret[k] = v
end
return ret
end
function M:root()
local node = self
while node.parent do
node = node.parent
end
return node
end
---@param path string[]|string
---@param opts? { create?: boolean, expand?: boolean }
---@return wk.Node?
function M:find(path, opts)
path = (type(path) == "string" and { path } or path) --[[@as string[] ]]
opts = opts or {}
local node = self
for _, key in ipairs(path) do
local child ---@type wk.Node?
if opts.expand then
child = node:expand()[key]
else
child = node._children[key]
end
if not child then
if not opts.create then
return
end
child = M.new(node, key)
node._children[key] = child
end
node = child
end
return node
end
return M
================================================
FILE: lua/which-key/plugins/init.lua
================================================
local Config = require("which-key.config")
local Util = require("which-key.util")
local M = {}
---@type table<string, wk.Plugin>
M.plugins = {}
function M.setup()
for name, opts in pairs(Config.plugins) do
-- only setup plugin if we didnt load it before
if not M.plugins[name] then
if type(opts) == "boolean" then
opts = { enabled = opts }
end
opts.enabled = opts.enabled ~= false
if opts.enabled then
M.plugins[name] = require("which-key.plugins." .. name)
M._setup(M.plugins[name], opts)
end
end
end
end
---@param plugin wk.Plugin
function M._setup(plugin, opts)
if plugin.mappings then
Config.add(plugin.mappings)
end
if plugin.setup then
plugin.setup(opts)
end
end
---@param name string
function M.cols(name)
local plugin = M.plugins[name]
assert(plugin, "plugin not found")
local ret = {} ---@type wk.Col[]
vim.list_extend(ret, plugin.cols or {})
ret[#ret + 1] = { key = "value", hl = "WhichKeyValue", width = 0.5 }
return ret
end
---@class wk.Node.plugin.item: wk.Node,wk.Plugin.item
return M
================================================
FILE: lua/which-key/plugins/marks.lua
================================================
---@diagnostic disable: missing-fields, inject-field
---@type wk.Plugin
local M = {}
M.name = "marks"
M.mappings = {
icon = { icon = " ", color = "orange" },
plugin = "marks",
{ "`", desc = "marks" },
{ "'", des
gitextract_lgngfsi1/ ├── .editorconfig ├── .github/ │ ├── .release-please-manifest.json │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ ├── config.yml │ │ └── feature_request.yml │ ├── PULL_REQUEST_TEMPLATE.md │ ├── dependabot.yml │ ├── release-please-config.json │ └── workflows/ │ ├── ci.yml │ ├── labeler.yml │ ├── pr.yml │ ├── stale.yml │ └── update.yml ├── .gitignore ├── .lua-format ├── .markdownlint-cli2.yaml ├── .neoconf.json ├── CHANGELOG.md ├── LICENSE ├── NEWS.md ├── README.md ├── TODO.md ├── doc/ │ └── which-key.nvim.txt ├── lua/ │ └── which-key/ │ ├── buf.lua │ ├── colors.lua │ ├── config.lua │ ├── docs.lua │ ├── extras.lua │ ├── health.lua │ ├── icons.lua │ ├── init.lua │ ├── layout.lua │ ├── mappings.lua │ ├── migrate.lua │ ├── node.lua │ ├── plugins/ │ │ ├── init.lua │ │ ├── marks.lua │ │ ├── presets.lua │ │ ├── registers.lua │ │ └── spelling.lua │ ├── presets.lua │ ├── state.lua │ ├── text.lua │ ├── tree.lua │ ├── triggers.lua │ ├── types.lua │ ├── util.lua │ ├── view.lua │ └── win.lua ├── plugin/ │ └── which-key.lua ├── scripts/ │ ├── docs │ └── test ├── selene.toml ├── stylua.toml ├── tests/ │ ├── buf_spec.lua │ ├── helpers.lua │ ├── layout_spec.lua │ ├── mappings_spec.lua │ ├── minit.lua │ └── util_spec.lua └── vim.yml
Condensed preview — 61 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (278K chars).
[
{
"path": ".editorconfig",
"chars": 98,
"preview": "root = true\n\n[*]\ninsert_final_newline = true\nindent_style = space\nindent_size = 2\ncharset = utf-8\n"
},
{
"path": ".github/.release-please-manifest.json",
"chars": 20,
"preview": "{\n \".\": \"3.17.0\"\n}\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.yml",
"chars": 2889,
"preview": "name: Bug Report\ndescription: File a bug/issue\ntitle: \"bug: \"\nlabels: [bug]\nbody:\n - type: markdown\n attributes:\n "
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 171,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: Ask a question\n url: https://github.com/folke/which-key.nvim/dis"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.yml",
"chars": 1252,
"preview": "name: Feature Request\ndescription: Suggest a new feature\ntitle: \"feature: \"\nlabels: [enhancement]\nbody:\n - type: checkb"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 334,
"preview": "## Description\n\n<!-- Describe the big picture of your changes to communicate to the maintainers\n why we should accept t"
},
{
"path": ".github/dependabot.yml",
"chars": 118,
"preview": "version: 2\nupdates:\n - package-ecosystem: \"github-actions\"\n directory: \"/\"\n schedule:\n interval: \"weekly\"\n"
},
{
"path": ".github/release-please-config.json",
"chars": 224,
"preview": "{\n \"$schema\": \"https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json\",\n \"packages\": {\n "
},
{
"path": ".github/workflows/ci.yml",
"chars": 226,
"preview": "name: CI\n\non:\n push:\n branches: [main, master]\n pull_request:\n\njobs:\n ci:\n uses: folke/github/.github/workflows"
},
{
"path": ".github/workflows/labeler.yml",
"chars": 144,
"preview": "name: \"PR Labeler\"\non:\n - pull_request_target\n\njobs:\n labeler:\n uses: folke/github/.github/workflows/labeler.yml@ma"
},
{
"path": ".github/workflows/pr.yml",
"chars": 275,
"preview": "name: PR Title\n\non:\n pull_request_target:\n types:\n - opened\n - edited\n - synchronize\n - reopened"
},
{
"path": ".github/workflows/stale.yml",
"chars": 236,
"preview": "name: Stale Issues & PRs\n\non:\n schedule:\n - cron: \"30 1 * * *\"\n\njobs:\n stale:\n if: contains(fromJSON('[\"folke\", "
},
{
"path": ".github/workflows/update.yml",
"chars": 272,
"preview": "name: Update Repo\n\non:\n workflow_dispatch:\n schedule:\n # Run every hour\n - cron: \"0 * * * *\"\n\njobs:\n update:\n "
},
{
"path": ".gitignore",
"chars": 70,
"preview": "*.log\n/.repro\n/.tests\n/build\n/debug\n/doc/tags\nfoo.*\nnode_modules\ntt.*\n"
},
{
"path": ".lua-format",
"chars": 385,
"preview": "# https://github.com/Koihik/LuaFormatter/blob/master/docs/Style-Config.md\ncolumn_limit: 100\nindent_width: 2\ncontinuation"
},
{
"path": ".markdownlint-cli2.yaml",
"chars": 38,
"preview": "config:\n MD013: false\n MD033: false\n"
},
{
"path": ".neoconf.json",
"chars": 286,
"preview": "{\n \"lspconfig\": {\n \"sumneko_lua\": {\n \"Lua.format.defaultConfig\": {\n \"indent_style\": \"space\",\n \"in"
},
{
"path": "CHANGELOG.md",
"chars": 82579,
"preview": "# Changelog\n\n## [3.17.0](https://github.com/folke/which-key.nvim/compare/v3.16.0...v3.17.0) (2025-02-14)\n\n\n### Features\n"
},
{
"path": "LICENSE",
"chars": 11357,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "NEWS.md",
"chars": 1905,
"preview": "# 💥 What's New in 3.0?\n\nMajor update for [which-key.nvim](https://github.com/folke/which-key.nvim)! This release include"
},
{
"path": "README.md",
"chars": 16520,
"preview": "# 💥 Which Key\n\n**WhichKey** helps you remember your Neovim keymaps, by showing available keybindings\nin a popup as you t"
},
{
"path": "TODO.md",
"chars": 1529,
"preview": "# Todo\n\n- [x] create keymaps in register()\n- [x] distinction between actual keymap and just a desc\n- [x] virtual mapping"
},
{
"path": "doc/which-key.nvim.txt",
"chars": 20187,
"preview": "*which-key.nvim.txt* which-key.nvim docs\n\n========================================"
},
{
"path": "lua/which-key/buf.lua",
"chars": 5649,
"preview": "local Config = require(\"which-key.config\")\nlocal Tree = require(\"which-key.tree\")\nlocal Triggers = require(\"which-key.tr"
},
{
"path": "lua/which-key/colors.lua",
"chars": 1666,
"preview": "local M = {}\n\nM.colors = {\n [\"\"] = \"Function\", -- the key\n Separator = \"Comment\", -- the separator between the key and"
},
{
"path": "lua/which-key/config.lua",
"chars": 10429,
"preview": "---@class wk.Config: wk.Opts\n---@field triggers {mappings: wk.Mapping[], modes: table<string,boolean>}\nlocal M = {}\n\nM.v"
},
{
"path": "lua/which-key/docs.lua",
"chars": 434,
"preview": "local Docs = require(\"lazy.docs\")\n\nlocal M = {}\n\nfunction M.update()\n local config = Docs.extract(\"lua/which-key/config"
},
{
"path": "lua/which-key/extras.lua",
"chars": 1601,
"preview": "local M = {}\n\n---@type table<string, fun():wk.Spec>\nM.expand = {}\n\n---@return number[]\nfunction M.bufs()\n local current"
},
{
"path": "lua/which-key/health.lua",
"chars": 5418,
"preview": "local Buf = require(\"which-key.buf\")\nlocal Config = require(\"which-key.config\")\nlocal Icons = require(\"which-key.icons\")"
},
{
"path": "lua/which-key/icons.lua",
"chars": 6840,
"preview": "local Config = require(\"which-key.config\")\n\nlocal M = {}\n\n--- * `WhichKeyColorAzure` - azure.\n--- * `WhichKeyColorBlue`"
},
{
"path": "lua/which-key/init.lua",
"chars": 1254,
"preview": "---@class wk\n---@field private _queue {spec: wk.Spec, opts?: wk.Parse}[]\nlocal M = {}\n\nM._queue = {}\nM.did_setup = false"
},
{
"path": "lua/which-key/layout.lua",
"chars": 4034,
"preview": "local Config = require(\"which-key.config\")\nlocal M = {}\n\nlocal dw = vim.fn.strdisplaywidth\n\n--- When `size` is a number,"
},
{
"path": "lua/which-key/mappings.lua",
"chars": 8602,
"preview": "local Config = require(\"which-key.config\")\nlocal Util = require(\"which-key.util\")\n\nlocal M = {}\n\nM.VERSION = 2\nM.notifs "
},
{
"path": "lua/which-key/migrate.lua",
"chars": 1927,
"preview": "local Mappings = require(\"which-key.mappings\")\n\nlocal M = {}\n\n---@param spec wk.Spec\nfunction M.migrate(spec)\n spec = v"
},
{
"path": "lua/which-key/node.lua",
"chars": 4902,
"preview": "local Util = require(\"which-key.util\")\n\n---@class wk.Node\n---@field _children table<string, wk.Node>\nlocal M = {}\n\n---@p"
},
{
"path": "lua/which-key/plugins/init.lua",
"chars": 1105,
"preview": "local Config = require(\"which-key.config\")\nlocal Util = require(\"which-key.util\")\n\nlocal M = {}\n\n---@type table<string, "
},
{
"path": "lua/which-key/plugins/marks.lua",
"chars": 1755,
"preview": "---@diagnostic disable: missing-fields, inject-field\n---@type wk.Plugin\nlocal M = {}\n\nM.name = \"marks\"\n\nM.mappings = {\n "
},
{
"path": "lua/which-key/plugins/presets.lua",
"chars": 6842,
"preview": "local M = {}\n\nM.name = \"presets\"\n\nM.operators = {\n preset = true,\n mode = { \"n\", \"x\" },\n { \"!\", desc = \"Run program\" "
},
{
"path": "lua/which-key/plugins/registers.lua",
"chars": 1892,
"preview": "local Util = require(\"which-key.util\")\n\n---@diagnostic disable: missing-fields, inject-field\n---@type wk.Plugin\nlocal M "
},
{
"path": "lua/which-key/plugins/spelling.lua",
"chars": 1243,
"preview": "---@diagnostic disable: missing-fields, inject-field\n---@type wk.Plugin\nlocal M = {}\n\nM.name = \"spelling\"\n\nM.mappings = "
},
{
"path": "lua/which-key/presets.lua",
"chars": 693,
"preview": "---@type table<string, wk.Opts>\nreturn {\n helix = {\n win = {\n width = { min = 30, max = 60 },\n height = { "
},
{
"path": "lua/which-key/state.lua",
"chars": 10186,
"preview": "local Buf = require(\"which-key.buf\")\nlocal Config = require(\"which-key.config\")\nlocal Triggers = require(\"which-key.trig"
},
{
"path": "lua/which-key/text.lua",
"chars": 3664,
"preview": "local Util = require(\"which-key.util\")\n\n---@class wk.Segment\n---@field str string Text\n---@field hl? string Extmark hl g"
},
{
"path": "lua/which-key/tree.lua",
"chars": 2609,
"preview": "local Config = require(\"which-key.config\")\nlocal Node = require(\"which-key.node\")\nlocal Util = require(\"which-key.util\")"
},
{
"path": "lua/which-key/triggers.lua",
"chars": 3976,
"preview": "local Util = require(\"which-key.util\")\n\n---@class wk.Trigger\n---@field buf number\n---@field mode string\n---@field keys s"
},
{
"path": "lua/which-key/types.lua",
"chars": 3267,
"preview": "---@meta\n\n--# selene: allow(unused_variable)\n\n---@class wk.Filter\n---@field mode? string\n---@field buf? number\n---@field"
},
{
"path": "lua/which-key/util.lua",
"chars": 7663,
"preview": "local M = {}\n\nM.cache = {\n keys = {}, ---@type table<string, string[]>\n norm = {}, ---@type table<string, string>\n te"
},
{
"path": "lua/which-key/view.lua",
"chars": 13735,
"preview": "local Buf = require(\"which-key.buf\")\nlocal Config = require(\"which-key.config\")\nlocal Icons = require(\"which-key.icons\")"
},
{
"path": "lua/which-key/win.lua",
"chars": 2884,
"preview": "local Util = require(\"which-key.util\")\n\n---@class wk.Win\n---@field win? number\n---@field buf? number\n---@field opts wk.W"
},
{
"path": "plugin/which-key.lua",
"chars": 202,
"preview": "local timer = (vim.uv or vim.loop).new_timer()\ntimer:start(\n 500,\n 0,\n vim.schedule_wrap(function()\n local wk = re"
},
{
"path": "scripts/docs",
"chars": 67,
"preview": "#!/bin/env bash\n\nnvim -u tests/minit.lua -l lua/which-key/docs.lua\n"
},
{
"path": "scripts/test",
"chars": 61,
"preview": "#!/usr/bin/env bash\n\nnvim -l tests/minit.lua --minitest \"$@\"\n"
},
{
"path": "selene.toml",
"chars": 39,
"preview": "std=\"vim\"\n\n[lints]\nmixed_table=\"allow\"\n"
},
{
"path": "stylua.toml",
"chars": 91,
"preview": "indent_type = \"Spaces\"\nindent_width = 2\ncolumn_width = 120\n[sort_requires]\nenabled = true\n\n"
},
{
"path": "tests/buf_spec.lua",
"chars": 357,
"preview": "local Buf = require(\"which-key.buf\")\n\nbefore_each(function()\n require(\"helpers\").reset()\nend)\n\ndescribe(\"triggers\", fun"
},
{
"path": "tests/helpers.lua",
"chars": 286,
"preview": "local M = {}\n\n---@param lines? string[]\nfunction M.reset(lines)\n vim.o.showmode = false\n vim.api.nvim_feedkeys(vim.key"
},
{
"path": "tests/layout_spec.lua",
"chars": 1265,
"preview": "local layout = require(\"which-key.layout\")\n\ndescribe(\"dim\", function()\n local tests = {\n { 100, { 200 }, 100 },\n "
},
{
"path": "tests/mappings_spec.lua",
"chars": 2670,
"preview": "local Mappings = require(\"which-key.mappings\")\n\nbefore_each(function()\n Mappings.notifs = {}\nend)\n\ndescribe(\"specs v1\","
},
{
"path": "tests/minit.lua",
"chars": 510,
"preview": "#!/usr/bin/env -S nvim -l\n\nvim.env.LAZY_STDPATH = \".tests\"\nvim.env.LAZY_PATH = vim.fs.normalize(\"~/projects/lazy.nvim\")\n"
},
{
"path": "tests/util_spec.lua",
"chars": 2769,
"preview": "---@module 'luassert'\n\nlocal Util = require(\"which-key.util\")\n\ndescribe(\"parse keys\", function()\n local tests = {\n ["
},
{
"path": "vim.yml",
"chars": 212,
"preview": "base: lua51\nlua_versions:\n - luajit\n\nglobals:\n Snacks:\n any: true\n vim:\n any: true\n jit:\n any: true\n asser"
}
]
About this extraction
This page contains the full source code of the folke/which-key.nvim GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 61 files (257.7 KB), approximately 81.7k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.