1

Regenerate nvim config

This commit is contained in:
2024-06-02 03:29:20 +02:00
parent 75eea0c030
commit ef2e28883d
5576 changed files with 604886 additions and 503 deletions

View File

@ -0,0 +1,89 @@
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 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: Repro
description: Minimal `init.lua` to reproduce this issue. Save as `repro.lua` and run with `nvim -u repro.lua`
value: |
-- DO NOT change the paths and don't remove the colorscheme
local root = vim.fn.fnamemodify("./.repro", ":p")
-- set stdpaths to use .repro
for _, name in ipairs({ "config", "data", "state", "cache" }) do
vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
end
-- bootstrap lazy
local lazypath = root .. "/plugins/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({ "git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", lazypath, })
end
vim.opt.runtimepath:prepend(lazypath)
-- install plugins
local plugins = {
"folke/tokyonight.nvim",
{ "folke/which-key.nvim", config = true },
-- add any other plugins here
}
require("lazy").setup(plugins, {
root = root .. "/plugins",
})
vim.cmd.colorscheme("tokyonight")
-- add anything else here
render: Lua
validations:
required: false

View File

@ -0,0 +1,36 @@
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.

View File

@ -0,0 +1,72 @@
name: CI
on:
push:
pull_request:
jobs:
tests:
strategy:
matrix:
# os: [ubuntu-latest, windows-latest]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Install Neovim
shell: bash
run: |
mkdir -p /tmp/nvim
wget -q https://github.com/neovim/neovim/releases/download/nightly/nvim.appimage -O /tmp/nvim/nvim.appimage
cd /tmp/nvim
chmod a+x ./nvim.appimage
./nvim.appimage --appimage-extract
echo "/tmp/nvim/squashfs-root/usr/bin/" >> $GITHUB_PATH
- name: Run Tests
run: |
nvim --version
[ ! -d tests ] && exit 0
nvim --headless -u tests/init.lua -c "PlenaryBustedDirectory tests/ {minimal_init = 'tests/init.lua', sequential = true}"
docs:
runs-on: ubuntu-latest
needs: tests
if: ${{ github.ref == 'refs/heads/main' }}
steps:
- uses: actions/checkout@v3
- name: panvimdoc
uses: kdheepak/panvimdoc@main
with:
vimdoc: which-key.nvim
version: "Neovim >= 0.8.0"
demojify: true
treesitter: true
- name: Push changes
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: "chore(build): auto-generate vimdoc"
commit_user_name: "github-actions[bot]"
commit_user_email: "github-actions[bot]@users.noreply.github.com"
commit_author: "github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
release:
name: release
if: ${{ github.ref == 'refs/heads/main' }}
needs:
- docs
- tests
runs-on: ubuntu-latest
steps:
- uses: google-github-actions/release-please-action@v3
id: release
with:
release-type: simple
package-name: which-key.nvim
- uses: actions/checkout@v3
- name: tag stable versions
if: ${{ steps.release.outputs.release_created }}
run: |
git config user.name github-actions[bot]
git config user.email github-actions[bot]@users.noreply.github.com
git remote add gh-token "https://${{ secrets.GITHUB_TOKEN }}@github.com/google-github-actions/release-please-action.git"
git tag -d stable || true
git push origin :stable || true
git tag -a stable -m "Last Stable Release"
git push origin stable

View File

@ -0,0 +1,8 @@
tt.*
.tests
doc/tags
debug
.repro
foo.*
*.log
data

View File

@ -0,0 +1,13 @@
# 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

View File

@ -0,0 +1,14 @@
{
"lspconfig": {
"sumneko_lua": {
"Lua.format.defaultConfig": {
"indent_style": "space",
"indent_size": "2",
"continuation_indent_size": "2"
},
"Lua.diagnostics.neededFileStatus": {
// "codestyle-check": "Any"
}
}
}
}

View File

@ -0,0 +1,334 @@
# Changelog
## [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&lt;CR&gt;` 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 &lt;&gt; keys ([#438](https://github.com/folke/which-key.nvim/issues/438)) ([4bd6dca](https://github.com/folke/which-key.nvim/commit/4bd6dcaa6d7e1650590303f0066d32aa6762d8f3))
* **util:** replace `&lt;lt&gt;` 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 &lt;C-w&gt;_ 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 &lt;c-c&gt; [#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 &lt;leader&gt; 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 &lt;esc&gt; 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 &lt;esc&gt; 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 &lt;leader&gt; 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 &lt;leader&gt;, 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 &lt;plug&gt; 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 &lt;esc&gt; 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 &lt;esc&gt; 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 &lt;nop&gt; 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 &lt;ESC&gt; 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 &lt;esc&gt; ([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 &lt;leader&gt; 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 &lt;leader&gt; 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 &lt; chatracters (should be <lt&gt;) ([e618f84](https://github.com/folke/which-key.nvim/commit/e618f8403e615d4344f2964839ee0e2013b4253e))
* properly handle &lt; 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;lt&gt; 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 &lt;leader&gt; = <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;lt&gt; 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))

View File

@ -0,0 +1,201 @@
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.

View File

@ -0,0 +1,367 @@
# 💥 Which Key
**WhichKey** is a lua plugin for Neovim 0.5 that displays a popup with possible key bindings of
the command you started typing. Heavily inspired by the original [emacs-which-key](https://github.com/justbur/emacs-which-key) and [vim-which-key](https://github.com/liuchengxu/vim-which-key).
![image](https://user-images.githubusercontent.com/292349/116439438-669f8d00-a804-11eb-9b5b-c7122bd9acac.png)
## ✨ Features
- for Neovim 0.7 and higher, it uses the `desc` attributes of your mappings as the default label
- for Neovim 0.7 and higher, new mappings will be created with a `desc` attribute
- opens a popup with suggestions to complete a key binding
- works with any setting for [timeoutlen](https://neovim.io/doc/user/options.html#'timeoutlen'), including instantly (`timeoutlen=0`)
- works correctly with built-in key bindings
- works correctly with buffer-local mappings
- extensible plugin architecture
- built-in plugins:
- **marks:** shows your marks when you hit one of the jump keys.
- **registers:** shows the contents of your registers
- **presets:** built-in key binding help for `motions`, `text-objects`, `operators`, `windows`, `nav`, `z` and `g`
- **spelling:** spelling suggestions inside the which-key popup
## ⚡️ Requirements
- Neovim >= 0.5.0
## 📦 Installation
Install the plugin with your preferred package manager:
### [lazy.nvim](https://github.com/folke/lazy.nvim)
```lua
{
"folke/which-key.nvim",
event = "VeryLazy",
init = function()
vim.o.timeout = true
vim.o.timeoutlen = 300
end,
opts = {
-- your configuration comes here
-- or leave it empty to use the default settings
-- refer to the configuration section below
}
}
```
### [packer](https://github.com/wbthomason/packer.nvim)
```lua
-- Lua
use {
"folke/which-key.nvim",
config = function()
vim.o.timeout = true
vim.o.timeoutlen = 300
require("which-key").setup {
-- your configuration comes here
-- or leave it empty to use the default settings
-- refer to the configuration section below
}
end
}
```
## ⚙️ Configuration
> ❗️ IMPORTANT: the [timeout](https://neovim.io/doc/user/options.html#'timeout') when **WhichKey** opens is controlled by the vim setting [timeoutlen](https://neovim.io/doc/user/options.html#'timeoutlen').
> Please refer to the documentation to properly set it up. Setting it to `0`, will effectively
> always show **WhichKey** immediately, but a setting of `500` (500ms) is probably more appropriate.
> ❗️ don't create any keymappings yourself to trigger WhichKey. Unlike with _vim-which-key_, we do this fully automatically.
> Please remove any left-over triggers you might have from using _vim-which-key_.
> 🚑 You can run `:checkhealth which-key` to see if there's any conflicting keymaps that will prevent triggering **WhichKey**
WhichKey comes with the following defaults:
```lua
{
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
},
},
-- add operators that will trigger motion and text object completion
-- to enable all native operators, set the preset / operators plugin above
operators = { gc = "Comments" },
key_labels = {
-- override the label used to display some keys. It doesn't effect WK in any other way.
-- For example:
-- ["<space>"] = "SPC",
-- ["<cr>"] = "RET",
-- ["<tab>"] = "TAB",
},
motions = {
count = true,
},
icons = {
breadcrumb = "»", -- symbol used in the command line area that shows your active key combo
separator = "➜", -- symbol used between a key and it's label
group = "+", -- symbol prepended to a group
},
popup_mappings = {
scroll_down = "<c-d>", -- binding to scroll down inside the popup
scroll_up = "<c-u>", -- binding to scroll up inside the popup
},
window = {
border = "none", -- none, single, double, shadow
position = "bottom", -- bottom, top
margin = { 1, 0, 1, 0 }, -- extra window margin [top, right, bottom, left]. When between 0 and 1, will be treated as a percentage of the screen size.
padding = { 1, 2, 1, 2 }, -- extra window padding [top, right, bottom, left]
winblend = 0, -- value between 0-100 0 for fully opaque and 100 for fully transparent
zindex = 1000, -- positive value to position WhichKey above other floating windows.
},
layout = {
height = { min = 4, max = 25 }, -- min and max height of the columns
width = { min = 20, max = 50 }, -- min and max width of the columns
spacing = 3, -- spacing between columns
align = "left", -- align columns left, center or right
},
ignore_missing = false, -- enable this to hide mappings for which you didn't specify a label
hidden = { "<silent>", "<cmd>", "<Cmd>", "<CR>", "^:", "^ ", "^call ", "^lua " }, -- hide mapping boilerplate
show_help = true, -- show 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
triggers = "auto", -- automatically setup triggers
-- triggers = {"<leader>"} -- or specifiy a list manually
-- list of triggers, where WhichKey should not wait for timeoutlen and show immediately
triggers_nowait = {
-- marks
"`",
"'",
"g`",
"g'",
-- registers
'"',
"<c-r>",
-- spelling
"z=",
},
triggers_blacklist = {
-- list of mode / prefixes that should never be hooked by WhichKey
-- this is mostly relevant for keymaps that start with a native binding
i = { "j", "k" },
v = { "j", "k" },
},
-- disable the WhichKey popup for certain buf types and file types.
-- Disabled by default for Telescope
disable = {
buftypes = {},
filetypes = {},
},
}
```
## 🪄 Setup
With the default settings, **WhichKey** will work out of the box for most builtin keybindings,
but the real power comes from documenting and organizing your own keybindings.
To document and/or setup your own mappings, you need to call the `register` method
```lua
local wk = require("which-key")
wk.register(mappings, opts)
```
Default options for `opts`
```lua
{
mode = "n", -- NORMAL mode
-- prefix: use "<leader>f" for example for mapping everything related to finding files
-- the prefix is prepended to every mapping part of `mappings`
prefix = "",
buffer = nil, -- Global mappings. Specify a buffer number for buffer local mappings
silent = true, -- use `silent` when creating keymaps
noremap = true, -- use `noremap` when creating keymaps
nowait = false, -- use `nowait` when creating keymaps
expr = false, -- use `expr` when creating keymaps
}
```
> ❕ When you specify a command in your mapping that starts with `<Plug>`, then we automatically set `noremap=false`, since you always want recursive keybindings in this case
### ⌨️ Mappings
> ⌨ for **Neovim 0.7** and higher, which key will use the `desc` attribute of existing mappings as the default label
Group names use the special `name` key in the tables. There's multiple ways to define the mappings. `wk.register` can be called multiple times from anywhere in your config files.
```lua
local wk = require("which-key")
-- As an example, we will create the following mappings:
-- * <leader>ff find files
-- * <leader>fr show recent files
-- * <leader>fb Foobar
-- we'll document:
-- * <leader>fn new file
-- * <leader>fe edit file
-- and hide <leader>1
wk.register({
f = {
name = "file", -- optional group name
f = { "<cmd>Telescope find_files<cr>", "Find File" }, -- create a binding with label
r = { "<cmd>Telescope oldfiles<cr>", "Open Recent File", noremap=false, buffer = 123 }, -- additional options for creating the keymap
n = { "New File" }, -- just a label. don't create any mapping
e = "Edit File", -- same as above
["1"] = "which_key_ignore", -- special label to hide it in the popup
b = { function() print("bar") end, "Foobar" } -- you can also pass functions!
},
}, { prefix = "<leader>" })
```
<details>
<summary>Click to see more examples</summary>
```lua
-- all of the mappings below are equivalent
-- method 2
wk.register({
["<leader>"] = {
f = {
name = "+file",
f = { "<cmd>Telescope find_files<cr>", "Find File" },
r = { "<cmd>Telescope oldfiles<cr>", "Open Recent File" },
n = { "<cmd>enew<cr>", "New File" },
},
},
})
-- method 3
wk.register({
["<leader>f"] = {
name = "+file",
f = { "<cmd>Telescope find_files<cr>", "Find File" },
r = { "<cmd>Telescope oldfiles<cr>", "Open Recent File" },
n = { "<cmd>enew<cr>", "New File" },
},
})
-- method 4
wk.register({
["<leader>f"] = { name = "+file" },
["<leader>ff"] = { "<cmd>Telescope find_files<cr>", "Find File" },
["<leader>fr"] = { "<cmd>Telescope oldfiles<cr>", "Open Recent File" },
["<leader>fn"] = { "<cmd>enew<cr>", "New File" },
})
```
</details>
**Tips:** The default label is `keymap.desc` or `keymap.rhs` or `""`,
`:h nvim_set_keymap()` to get more details about `desc` and `rhs`.
### 🚙 Operators, Motions and Text Objects
**WhichKey** provides help to work with operators, motions and text objects.
> `[count]operator[count][text-object]`
- operators can be configured with the `operators` option
- set `plugins.presets.operators` to `true` to automatically configure vim built-in operators
- set this to `false`, to only include the list you configured in the `operators` option.
- see [here](https://github.com/folke/which-key.nvim/blob/main/lua/which-key/plugins/presets/init.lua#L5) for the full list part of the preset
- text objects are automatically retrieved from **operator pending** key maps (`omap`)
- set `plugins.presets.text_objects` to `true` to configure built-in text objects
- see [here](https://github.com/folke/which-key.nvim/blob/main/lua/which-key/plugins/presets/init.lua#L43)
- motions are part of the preset `plugins.presets.motions` setting
- see [here](https://github.com/folke/which-key.nvim/blob/main/lua/which-key/plugins/presets/init.lua#L20)
<details>
<summary>How to disable some operators? (like v)</summary>
```lua
-- make sure to run this code before calling setup()
-- refer to the full lists at https://github.com/folke/which-key.nvim/blob/main/lua/which-key/plugins/presets/init.lua
local presets = require("which-key.plugins.presets")
presets.operators["v"] = nil
```
</details>
## 🚀 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
Apart from the automatic opening, you can also manually open **WhichKey** for a certain `prefix`:
> ❗️ don't create any keymappings yourself to trigger WhichKey. Unlike with _vim-which-key_, we do this fully automatically.
> Please remove any left-over triggers you might have from using _vim-which-key_.
```vim
:WhichKey " show all mappings
:WhichKey <leader> " show all <leader> mappings
:WhichKey <leader> v " show all <leader> mappings for VISUAL mode
:WhichKey '' v " show ALL mappings for VISUAL mode
```
## 🔥 Plugins
Four built-in plugins are included with **WhichKey**.
### Marks
Shows a list of your buffer local and global marks when you hit \` or '
![image](https://user-images.githubusercontent.com/292349/116439573-8f278700-a804-11eb-80ca-bb9263e6d937.png)
### Registers
Shows a list of your buffer local and global registers when you hit " in _NORMAL_ mode, or `<c-r>` in _INSERT_ mode.
![image](https://user-images.githubusercontent.com/292349/116439609-98b0ef00-a804-11eb-9385-97c7d5ff4113.png)
### Presets
Built-in key binding help for `motions`, `text-objects`, `operators`, `windows`, `nav`, `z` and `g`
![image](https://user-images.githubusercontent.com/292349/116439871-df9ee480-a804-11eb-9529-800e167db65c.png)
### Spelling
When enabled, this plugin hooks into `z=` and replaces the full-screen spelling suggestions window by a list of suggestions within **WhichKey**.
![image](https://user-images.githubusercontent.com/292349/118102022-1c361880-b38d-11eb-8e82-79ad266d9bb8.png)
## 🎨 Colors
The table below shows all the highlight groups defined for **WhichKey** with their default link.
| Highlight Group | Defaults to | Description |
| ------------------- | ----------- | ------------------------------------------- |
| _WhichKey_ | Function | the key |
| _WhichKeyGroup_ | Keyword | a group |
| _WhichKeySeparator_ | DiffAdd | the separator between the key and its label |
| _WhichKeyDesc_ | Identifier | the label of the key |
| _WhichKeyFloat_ | NormalFloat | Normal in the popup window |
| _WhichKeyBorder_ | FloatBorder | Normal in the popup window |
| _WhichKeyValue_ | Comment | used by plugins that provide values |
<!-- markdownlint-disable-file MD033 -->
<!-- markdownlint-configure-file { "MD013": { "line_length": 120 } } -->
<!-- markdownlint-configure-file { "MD004": { "style": "sublist" } } -->

View File

@ -0,0 +1,21 @@
# Todo
* [x] hook into all groups
* [x] show mappings without keymap (zz etc)
* [x] plugin support for marks, registers, text objects
* [x] `<bs>` to go up a level
* [x] config modes
* [x] update buf only
* [x] + thingy for groups
* [x] text objects
* [x] get label from global when not found for buffer
* [x] operators & motions
* [x] show window after timeout?
* [x] make plugins a list of key value with config in value
* [x] cleanup text objects text
* [x] buf local mappings seems to interfere with global mappings (push K in help)
* [x] fix help in visual mode
* [x] Plug>whichkey nop
* [x] preset plugin
* [x] command should auto stuff
* [x] timeoutlen is always respected and should still work when zero

View File

@ -0,0 +1,419 @@
*which-key.nvim.txt* For Neovim >= 0.8.0 Last change: 2023 October 20
==============================================================================
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|
- Setup |which-key.nvim-which-key-setup|
- Usage |which-key.nvim-which-key-usage|
- Plugins |which-key.nvim-which-key-plugins|
- Colors |which-key.nvim-which-key-colors|
==============================================================================
1. Which Key *which-key.nvim-which-key*
**WhichKey** is a lua plugin for Neovim 0.5 that displays a popup with possible
key bindings of the command you started typing. Heavily inspired by the
original emacs-which-key <https://github.com/justbur/emacs-which-key> and
vim-which-key <https://github.com/liuchengxu/vim-which-key>.
FEATURES *which-key.nvim-which-key-features*
- for Neovim 0.7 and higher, it uses the `desc` attributes of your mappings as the default label
- for Neovim 0.7 and higher, new mappings will be created with a `desc` attribute
- opens a popup with suggestions to complete a key binding
- works with any setting for |timeoutlen|, including instantly (`timeoutlen=0`)
- works correctly with built-in key bindings
- works correctly with buffer-local mappings
- extensible plugin architecture
- built-in plugins:
- **marks:** shows your marks when you hit one of the jump keys.
- **registers:** shows the contents of your registers
- **presets:** built-in key binding help for `motions`, `text-objects`, `operators`, `windows`, `nav`, `z` and `g`
- **spelling:** spelling suggestions inside the which-key popup
REQUIREMENTS *which-key.nvim-which-key-requirements*
- Neovim >= 0.5.0
INSTALLATION *which-key.nvim-which-key-installation*
Install the plugin with your preferred package manager:
LAZY.NVIM ~
>lua
{
"folke/which-key.nvim",
event = "VeryLazy",
init = function()
vim.o.timeout = true
vim.o.timeoutlen = 300
end,
opts = {
-- your configuration comes here
-- or leave it empty to use the default settings
-- refer to the configuration section below
}
}
<
PACKER ~
>lua
-- Lua
use {
"folke/which-key.nvim",
config = function()
vim.o.timeout = true
vim.o.timeoutlen = 300
require("which-key").setup {
-- your configuration comes here
-- or leave it empty to use the default settings
-- refer to the configuration section below
}
end
}
<
CONFIGURATION *which-key.nvim-which-key-configuration*
IMPORTANT: the |timeout| when **WhichKey** opens is controlled by the vim
setting |timeoutlen|. Please refer to the documentation to properly set it up.
Setting it to `0`, will effectively always show **WhichKey** immediately, but a
setting of `500` (500ms) is probably more appropriate.
dont create any keymappings yourself to trigger WhichKey. Unlike with
_vim-which-key_, we do this fully automatically. Please remove any left-over
triggers you might have from using _vim-which-key_.
You can run `:checkhealth which-key` to see if theres any conflicting
keymaps that will prevent triggering **WhichKey**
WhichKey comes with the following defaults:
>lua
{
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
},
},
-- add operators that will trigger motion and text object completion
-- to enable all native operators, set the preset / operators plugin above
operators = { gc = "Comments" },
key_labels = {
-- override the label used to display some keys. It doesn't effect WK in any other way.
-- For example:
-- ["<space>"] = "SPC",
-- ["<cr>"] = "RET",
-- ["<tab>"] = "TAB",
},
motions = {
count = true,
},
icons = {
breadcrumb = "»", -- symbol used in the command line area that shows your active key combo
separator = "➜", -- symbol used between a key and it's label
group = "+", -- symbol prepended to a group
},
popup_mappings = {
scroll_down = "<c-d>", -- binding to scroll down inside the popup
scroll_up = "<c-u>", -- binding to scroll up inside the popup
},
window = {
border = "none", -- none, single, double, shadow
position = "bottom", -- bottom, top
margin = { 1, 0, 1, 0 }, -- extra window margin [top, right, bottom, left]. When between 0 and 1, will be treated as a percentage of the screen size.
padding = { 1, 2, 1, 2 }, -- extra window padding [top, right, bottom, left]
winblend = 0, -- value between 0-100 0 for fully opaque and 100 for fully transparent
zindex = 1000, -- positive value to position WhichKey above other floating windows.
},
layout = {
height = { min = 4, max = 25 }, -- min and max height of the columns
width = { min = 20, max = 50 }, -- min and max width of the columns
spacing = 3, -- spacing between columns
align = "left", -- align columns left, center or right
},
ignore_missing = false, -- enable this to hide mappings for which you didn't specify a label
hidden = { "<silent>", "<cmd>", "<Cmd>", "<CR>", "^:", "^ ", "^call ", "^lua " }, -- hide mapping boilerplate
show_help = true, -- show 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
triggers = "auto", -- automatically setup triggers
-- triggers = {"<leader>"} -- or specifiy a list manually
-- list of triggers, where WhichKey should not wait for timeoutlen and show immediately
triggers_nowait = {
-- marks
"`",
"'",
"g`",
"g'",
-- registers
'"',
"<c-r>",
-- spelling
"z=",
},
triggers_blacklist = {
-- list of mode / prefixes that should never be hooked by WhichKey
-- this is mostly relevant for keymaps that start with a native binding
i = { "j", "k" },
v = { "j", "k" },
},
-- disable the WhichKey popup for certain buf types and file types.
-- Disabled by default for Telescope
disable = {
buftypes = {},
filetypes = {},
},
}
<
SETUP *which-key.nvim-which-key-setup*
With the default settings, **WhichKey** will work out of the box for most
builtin keybindings, but the real power comes from documenting and organizing
your own keybindings.
To document and/or setup your own mappings, you need to call the `register`
method
>lua
local wk = require("which-key")
wk.register(mappings, opts)
<
Default options for `opts`
>lua
{
mode = "n", -- NORMAL mode
-- prefix: use "<leader>f" for example for mapping everything related to finding files
-- the prefix is prepended to every mapping part of `mappings`
prefix = "",
buffer = nil, -- Global mappings. Specify a buffer number for buffer local mappings
silent = true, -- use `silent` when creating keymaps
noremap = true, -- use `noremap` when creating keymaps
nowait = false, -- use `nowait` when creating keymaps
expr = false, -- use `expr` when creating keymaps
}
<
When you specify a command in your mapping that starts with `<Plug>`, then we
automatically set `noremap=false`, since you always want recursive keybindings
in this case
MAPPINGS ~
for **Neovim 0.7** and higher, which key will use the `desc` attribute of
existing mappings as the default label
Group names use the special `name` key in the tables. Theres multiple ways
to define the mappings. `wk.register` can be called multiple times from
anywhere in your config files.
>lua
local wk = require("which-key")
-- As an example, we will create the following mappings:
-- * <leader>ff find files
-- * <leader>fr show recent files
-- * <leader>fb Foobar
-- we'll document:
-- * <leader>fn new file
-- * <leader>fe edit file
-- and hide <leader>1
wk.register({
f = {
name = "file", -- optional group name
f = { "<cmd>Telescope find_files<cr>", "Find File" }, -- create a binding with label
r = { "<cmd>Telescope oldfiles<cr>", "Open Recent File", noremap=false, buffer = 123 }, -- additional options for creating the keymap
n = { "New File" }, -- just a label. don't create any mapping
e = "Edit File", -- same as above
["1"] = "which_key_ignore", -- special label to hide it in the popup
b = { function() print("bar") end, "Foobar" } -- you can also pass functions!
},
}, { prefix = "<leader>" })
<
Click to see more examples ~
>lua
-- all of the mappings below are equivalent
-- method 2
wk.register({
["<leader>"] = {
f = {
name = "+file",
f = { "<cmd>Telescope find_files<cr>", "Find File" },
r = { "<cmd>Telescope oldfiles<cr>", "Open Recent File" },
n = { "<cmd>enew<cr>", "New File" },
},
},
})
-- method 3
wk.register({
["<leader>f"] = {
name = "+file",
f = { "<cmd>Telescope find_files<cr>", "Find File" },
r = { "<cmd>Telescope oldfiles<cr>", "Open Recent File" },
n = { "<cmd>enew<cr>", "New File" },
},
})
-- method 4
wk.register({
["<leader>f"] = { name = "+file" },
["<leader>ff"] = { "<cmd>Telescope find_files<cr>", "Find File" },
["<leader>fr"] = { "<cmd>Telescope oldfiles<cr>", "Open Recent File" },
["<leader>fn"] = { "<cmd>enew<cr>", "New File" },
})
<
**Tips:** The default label is `keymap.desc` or `keymap.rhs` or `""`,
|nvim_set_keymap()| to get more details about `desc` and `rhs`.
OPERATORS, MOTIONS AND TEXT OBJECTS ~
**WhichKey** provides help to work with operators, motions and text objects.
`[count]operator[count][text-object]`
- operators can be configured with the `operators` option
- set `plugins.presets.operators` to `true` to automatically configure vim built-in operators
- set this to `false`, to only include the list you configured in the `operators` option.
- see here <https://github.com/folke/which-key.nvim/blob/main/lua/which-key/plugins/presets/init.lua#L5> for the full list part of the preset
- text objects are automatically retrieved from **operator pending** key maps (`omap`)
- set `plugins.presets.text_objects` to `true` to configure built-in text objects
- see here <https://github.com/folke/which-key.nvim/blob/main/lua/which-key/plugins/presets/init.lua#L43>
- motions are part of the preset `plugins.presets.motions` setting
- see here <https://github.com/folke/which-key.nvim/blob/main/lua/which-key/plugins/presets/init.lua#L20>
How to disable some operators? (like v) ~
>lua
-- make sure to run this code before calling setup()
-- refer to the full lists at https://github.com/folke/which-key.nvim/blob/main/lua/which-key/plugins/presets/init.lua
local presets = require("which-key.plugins.presets")
presets.operators["v"] = nil
<
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
Apart from the automatic opening, you can also manually open **WhichKey** for a
certain `prefix`
dont create any keymappings yourself to trigger WhichKey. Unlike with
_vim-which-key_, we do this fully automatically. Please remove any left-over
triggers you might have from using _vim-which-key_.
>vim
:WhichKey " show all mappings
:WhichKey <leader> " show all <leader> mappings
:WhichKey <leader> v " show all <leader> mappings for VISUAL mode
:WhichKey '' v " show ALL mappings for VISUAL mode
<
PLUGINS *which-key.nvim-which-key-plugins*
Four built-in plugins are included with **WhichKey**.
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.
PRESETS ~
Built-in key binding help for `motions`, `text-objects`, `operators`,
`windows`, `nav`, `z` and `g`
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 Defaults to Description
------------------- ------------- -----------------------------------------
WhichKey Function the key
WhichKeyGroup Keyword a group
WhichKeySeparator DiffAdd the separator between the key and its
label
WhichKeyDesc Identifier the label of the key
WhichKeyFloat NormalFloat Normal in the popup window
WhichKeyBorder FloatBorder Normal in the popup window
WhichKeyValue Comment used by plugins that provide values
---------------------------------------------------------------------------
==============================================================================
2. Links *which-key.nvim-links*
1. *image*: https://user-images.githubusercontent.com/292349/116439438-669f8d00-a804-11eb-9b5b-c7122bd9acac.png
2. *image*: https://user-images.githubusercontent.com/292349/116439573-8f278700-a804-11eb-80ca-bb9263e6d937.png
3. *image*: https://user-images.githubusercontent.com/292349/116439609-98b0ef00-a804-11eb-9385-97c7d5ff4113.png
4. *image*: https://user-images.githubusercontent.com/292349/116439871-df9ee480-a804-11eb-9529-800e167db65c.png
5. *image*: https://user-images.githubusercontent.com/292349/118102022-1c361880-b38d-11eb-8e82-79ad266d9bb8.png
Generated by panvimdoc <https://github.com/kdheepak/panvimdoc>
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,19 @@
local M = {}
local links = {
[""] = "Function",
Separator = "Comment",
Group = "Keyword",
Desc = "Identifier",
Float = "NormalFloat",
Border = "FloatBorder",
Value = "Comment",
}
function M.setup()
for k, v in pairs(links) do
vim.api.nvim_set_hl(0, "WhichKey" .. k, { link = v, default = true })
end
end
return M

View File

@ -0,0 +1,105 @@
local M = {}
M.namespace = vim.api.nvim_create_namespace("WhichKey")
---@class Options
local defaults = {
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
},
},
-- add operators that will trigger motion and text object completion
-- to enable all native operators, set the preset / operators plugin above
operators = { gc = "Comments" },
key_labels = {
-- override the label used to display some keys. It doesn't effect WK in any other way.
-- For example:
-- ["<space>"] = "SPC",
-- ["<cr>"] = "RET",
-- ["<tab>"] = "TAB",
},
motions = {
count = true,
},
icons = {
breadcrumb = "»", -- symbol used in the command line area that shows your active key combo
separator = "", -- symbol used between a key and it's label
group = "+", -- symbol prepended to a group
},
popup_mappings = {
scroll_down = "<c-d>", -- binding to scroll down inside the popup
scroll_up = "<c-u>", -- binding to scroll up inside the popup
},
window = {
border = "none", -- none, single, double, shadow
position = "bottom", -- bottom, top
margin = { 1, 0, 1, 0 }, -- extra window margin [top, right, bottom, left]. When between 0 and 1, will be treated as a percentage of the screen size.
padding = { 1, 2, 1, 2 }, -- extra window padding [top, right, bottom, left]
winblend = 0, -- value between 0-100 0 for fully opaque and 100 for fully transparent
zindex = 1000, -- positive value to position WhichKey above other floating windows.
},
layout = {
height = { min = 4, max = 25 }, -- min and max height of the columns
width = { min = 20, max = 50 }, -- min and max width of the columns
spacing = 3, -- spacing between columns
align = "left", -- align columns left, center or right
},
ignore_missing = false, -- enable this to hide mappings for which you didn't specify a label
hidden = { "<silent>", "<cmd>", "<Cmd>", "<CR>", "^:", "^ ", "^call ", "^lua " }, -- hide mapping boilerplate
show_help = true, -- show 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
triggers = "auto", -- automatically setup triggers
-- triggers = {"<leader>"} -- or specifiy a list manually
-- list of triggers, where WhichKey should not wait for timeoutlen and show immediately
triggers_nowait = {
-- marks
"`",
"'",
"g`",
"g'",
-- registers
'"',
"<c-r>",
-- spelling
"z=",
},
triggers_blacklist = {
-- list of mode / prefixes that should never be hooked by WhichKey
-- this is mostly relevant for keymaps that start with a native binding
i = { "j", "k" },
v = { "j", "k" },
},
-- disable the WhichKey popup for certain buf types and file types.
-- Disabled by deafult for Telescope
disable = {
buftypes = {},
filetypes = {},
},
}
---@type Options
M.options = {}
---@param options? Options
function M.setup(options)
M.options = vim.tbl_deep_extend("force", {}, defaults, options or {})
end
M.setup()
return M

View File

@ -0,0 +1,57 @@
local Keys = require("which-key.keys")
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
function M.check()
start("WhichKey: checking conflicting keymaps")
local conflicts = 0
for _, tree in pairs(Keys.mappings) do
Keys.update_keymaps(tree.mode, tree.buf)
tree.tree:walk(
---@param node Node
function(node)
local count = 0
for _ in pairs(node.children) do
count = count + 1
end
local auto_prefix = not node.mapping or (node.mapping.group == true and not node.mapping.cmd)
if node.prefix_i ~= "" and count > 0 and not auto_prefix then
conflicts = conflicts + 1
local msg = ("conflicting keymap exists for mode **%q**, lhs: **%q**"):format(tree.mode, node.mapping.prefix)
warn(msg)
local cmd = node.mapping.cmd or " "
info(("rhs: `%s`"):format(cmd))
end
end
)
end
if conflicts == 0 then
ok("No conflicting keymaps found")
return
end
for _, dup in ipairs(Keys.duplicates) do
local msg = ""
if dup.buf == dup.other.buffer then
msg = "duplicate keymap"
else
msg = "buffer-local keymap overriding global"
end
msg = (msg .. " for mode **%q**, buf: %d, lhs: **%q**"):format(dup.mode, dup.buf or 0, dup.prefix)
if dup.buf == dup.other.buffer then
error(msg)
else
warn(msg)
end
info(("old rhs: `%s`"):format(dup.other.rhs or ""))
info(("new rhs: `%s`"):format(dup.cmd or ""))
end
end
return M

View File

@ -0,0 +1,108 @@
local Keys = require("which-key.keys")
local Util = require("which-key.util")
---@class WhichKey
local M = {}
local loaded = false -- once we loaded everything
local scheduled = false
local function schedule_load()
if scheduled then
return
end
scheduled = true
if vim.v.vim_did_enter == 0 then
vim.cmd([[au VimEnter * ++once lua require("which-key").load()]])
else
M.load()
end
end
---@param options? Options
function M.setup(options)
require("which-key.config").setup(options)
schedule_load()
end
function M.execute(id)
local func = Keys.functions[id]
return func()
end
function M.show(keys, opts)
opts = opts or {}
if type(opts) == "string" then
opts = { mode = opts }
end
keys = keys or ""
opts.mode = opts.mode or Util.get_mode()
local buf = vim.api.nvim_get_current_buf()
-- make sure the trees exist for update
Keys.get_tree(opts.mode)
Keys.get_tree(opts.mode, buf)
-- update only trees related to buf
Keys.update(buf)
-- trigger which key
require("which-key.view").open(keys, opts)
end
function M.show_command(keys, mode)
keys = keys or ""
keys = (keys == '""' or keys == "''") and "" or keys
mode = (mode == '""' or mode == "''") and "" or mode
mode = mode or "n"
keys = Util.t(keys)
if not Util.check_mode(mode) then
Util.error(
"Invalid mode passed to :WhichKey (Don't create any keymappings to trigger WhichKey. WhichKey does this automatically)"
)
else
M.show(keys, { mode = mode })
end
end
local queue = {}
-- Defer registering keymaps until VimEnter
function M.register(mappings, opts)
schedule_load()
if loaded then
Keys.register(mappings, opts)
Keys.update()
else
table.insert(queue, { mappings, opts })
end
end
-- Load mappings and update only once
function M.load()
if loaded then
return
end
require("which-key.plugins").setup()
require("which-key.colors").setup()
Keys.register({}, { prefix = "<leader>", mode = "n" })
Keys.register({}, { prefix = "<leader>", mode = "v" })
Keys.setup()
for _, reg in pairs(queue) do
local opts = reg[2] or {}
opts.update = false
Keys.register(reg[1], opts)
end
Keys.update()
queue = {}
loaded = true
end
function M.reset()
-- local mappings = Keys.mappings
require("plenary.reload").reload_module("which-key")
-- require("which-key.Keys").mappings = mappings
require("which-key").setup()
end
return M

View File

@ -0,0 +1,438 @@
local Tree = require("which-key.tree")
local Util = require("which-key.util")
local Config = require("which-key.config")
-- secret character that will be used to create <nop> mappings
local secret = "Þ"
---@class Keys
local M = {}
M.functions = {}
M.operators = {}
M.nowait = {}
M.blacklist = {}
function M.setup()
local builtin_ops = require("which-key.plugins.presets").operators
for op, _ in pairs(builtin_ops) do
M.operators[op] = true
end
local mappings = {}
for op, label in pairs(Config.options.operators) do
M.operators[op] = true
if builtin_ops[op] then
mappings[op] = { name = label, i = { name = "inside" }, a = { name = "around" } }
end
end
for _, t in pairs(Config.options.triggers_nowait) do
M.nowait[t] = true
end
M.register(mappings, { mode = "n", preset = true })
M.register({ i = { name = "inside" }, a = { name = "around" } }, { mode = "v", preset = true })
for mode, blacklist in pairs(Config.options.triggers_blacklist) do
for _, prefix_n in ipairs(blacklist) do
M.blacklist[mode] = M.blacklist[mode] or {}
M.blacklist[mode][prefix_n] = true
end
end
end
function M.get_operator(prefix_i)
for op_n, _ in pairs(Config.options.operators) do
local op_i = Util.t(op_n)
if prefix_i:sub(1, #op_i) == op_i then
return op_i, op_n
end
end
end
function M.process_motions(ret, mode, prefix_i, buf)
local op_i, op_n = "", ""
if mode ~= "v" then
op_i, op_n = M.get_operator(prefix_i)
end
if (mode == "n" or mode == "v") and op_i then
local op_prefix_i = prefix_i:sub(#op_i + 1)
local op_count = op_prefix_i:match("^(%d+)")
if op_count == "0" then
op_count = nil
end
if Config.options.motions.count == false then
op_count = nil
end
if op_count then
op_prefix_i = op_prefix_i:sub(#op_count + 1)
end
local op_results = M.get_mappings("o", op_prefix_i, buf)
if not ret.mapping and op_results.mapping then
ret.mapping = op_results.mapping
ret.mapping.prefix = op_n .. (op_count or "") .. ret.mapping.prefix
ret.mapping.keys = Util.parse_keys(ret.mapping.prefix)
end
for _, mapping in pairs(op_results.mappings) do
mapping.prefix = op_n .. (op_count or "") .. mapping.prefix
mapping.keys = Util.parse_keys(mapping.prefix)
table.insert(ret.mappings, mapping)
end
end
end
---@return MappingGroup
function M.get_mappings(mode, prefix_i, buf)
---@class MappingGroup
---@field mode string
---@field prefix_i string
---@field buf number
---@field mapping? Mapping
---@field mappings VisualMapping[]
local ret
ret = { mapping = nil, mappings = {}, mode = mode, buf = buf, prefix_i = prefix_i }
local prefix_len = #Util.parse_internal(prefix_i)
---@param node? Node
local function add(node)
if node then
if node.mapping then
ret.mapping = vim.tbl_deep_extend("force", {}, ret.mapping or {}, node.mapping)
end
for k, child in pairs(node.children) do
if
child.mapping
and child.mapping.label ~= "which_key_ignore"
and child.mapping.desc ~= "which_key_ignore"
and not (child.mapping.group and vim.tbl_isempty(child.children))
then
ret.mappings[k] = vim.tbl_deep_extend("force", {}, ret.mappings[k] or {}, child.mapping)
end
end
end
end
local plugin_context = { buf = buf, mode = mode }
add(M.get_tree(mode).tree:get(prefix_i, nil, plugin_context))
add(M.get_tree(mode, buf).tree:get(prefix_i, nil, plugin_context))
-- Handle motions
M.process_motions(ret, mode, prefix_i, buf)
-- Fix labels
local tmp = {}
for _, value in pairs(ret.mappings) do
value.key = value.keys.notation[prefix_len + 1]
if Config.options.key_labels[value.key] then
value.key = Config.options.key_labels[value.key]
end
local skip = not value.label and Config.options.ignore_missing == true
if Util.t(value.key) == Util.t("<esc>") then
skip = true
end
if not skip then
if value.group then
value.label = value.label or "+prefix"
value.label = value.label:gsub("^%+", "")
value.label = Config.options.icons.group .. value.label
elseif not value.label then
value.label = value.desc or value.cmd or ""
for _, v in ipairs(Config.options.hidden) do
value.label = value.label:gsub(v, "")
end
end
if value.value then
value.value = vim.fn.strtrans(value.value)
end
-- remove duplicated keymap
local exists = false
for k, v in pairs(tmp) do
if type(v) == "table" and v.key == value.key then
tmp[k] = value
exists = true
break
end
end
if not exists then
table.insert(tmp, value)
end
end
end
-- Sort items, but not for plugins
table.sort(tmp, function(a, b)
if a.order and b.order then
return a.order < b.order
end
if a.group == b.group then
local ak = (a.key or ""):lower()
local bk = (b.key or ""):lower()
local aw = ak:match("[a-z]") and 1 or 0
local bw = bk:match("[a-z]") and 1 or 0
if aw == bw then
return ak < bk
end
return aw < bw
else
return (a.group and 1 or 0) < (b.group and 1 or 0)
end
end)
ret.mappings = tmp
return ret
end
---@type table<string, MappingTree>
M.mappings = {}
M.duplicates = {}
function M.map(mode, prefix_n, cmd, buf, opts)
local other = vim.api.nvim_buf_call(buf or 0, function()
local ret = vim.fn.maparg(prefix_n, mode, false, true)
---@diagnostic disable-next-line: undefined-field
return (ret and ret.lhs and ret.rhs and ret.rhs ~= cmd) and ret or nil
end)
if other and other.buffer == buf then
table.insert(M.duplicates, { mode = mode, prefix = prefix_n, cmd = cmd, buf = buf, other = other })
end
if buf ~= nil then
pcall(vim.api.nvim_buf_set_keymap, buf, mode, prefix_n, cmd, opts)
else
pcall(vim.api.nvim_set_keymap, mode, prefix_n, cmd, opts)
end
end
function M.register(mappings, opts)
opts = opts or {}
mappings = require("which-key.mappings").parse(mappings, opts)
-- always create the root node for the mode, even if there's no mappings,
-- to ensure we have at least a trigger hooked for non documented keymaps
local modes = {}
for _, mapping in pairs(mappings) do
if not modes[mapping.mode] then
modes[mapping.mode] = true
M.get_tree(mapping.mode)
end
if mapping.cmd ~= nil then
M.map(mapping.mode, mapping.prefix, mapping.cmd, mapping.buf, mapping.opts)
end
M.get_tree(mapping.mode, mapping.buf).tree:add(mapping)
end
end
M.hooked = {}
function M.hook_id(prefix_n, mode, buf)
return mode .. (buf or "") .. Util.t(prefix_n)
end
function M.is_hooked(prefix_n, mode, buf)
return M.hooked[M.hook_id(prefix_n, mode, buf)]
end
function M.hook_del(prefix_n, mode, buf)
local id = M.hook_id(prefix_n, mode, buf)
M.hooked[id] = nil
if buf then
pcall(vim.api.nvim_buf_del_keymap, buf, mode, prefix_n)
pcall(vim.api.nvim_buf_del_keymap, buf, mode, prefix_n .. secret)
else
pcall(vim.api.nvim_del_keymap, mode, prefix_n)
pcall(vim.api.nvim_del_keymap, mode, prefix_n .. secret)
end
end
function M.hook_add(prefix_n, mode, buf, secret_only)
-- check if this trigger is blacklisted
if M.blacklist[mode] and M.blacklist[mode][prefix_n] then
return
end
-- don't hook numbers. See #118
if tonumber(prefix_n) then
return
end
-- don't hook to j or k in INSERT mode
if mode == "i" and (prefix_n == "j" or prefix_n == "k") then
return
end
-- never hook q
if mode == "n" and prefix_n == "q" then
return
end
-- never hook into select mode
if mode == "s" then
return
end
-- never hook into operator pending mode
-- this is handled differently
if mode == "o" then
return
end
if Util.t(prefix_n) == Util.t("<esc>") then
return
end
-- never hook into operators in visual mode
if (mode == "v" or mode == "x") and (prefix_n == "a" or prefix_n == "i" or M.operators[prefix_n]) then
return
end
-- Check if we need to create the hook
if type(Config.options.triggers) == "string" and Config.options.triggers ~= "auto" then
if Util.t(prefix_n) ~= Util.t(Config.options.triggers) then
return
end
end
if type(Config.options.triggers) == "table" then
local ok = false
for _, trigger in pairs(Config.options.triggers) do
if Util.t(trigger) == Util.t(prefix_n) then
ok = true
break
end
end
if not ok then
return
end
end
local opts = { noremap = true, silent = true }
local id = M.hook_id(prefix_n, mode, buf)
local id_global = M.hook_id(prefix_n, mode)
-- hook up if needed
if not M.hooked[id] and not M.hooked[id_global] then
local cmd = [[<cmd>lua require("which-key").show(%q, {mode = %q, auto = true})<cr>]]
cmd = string.format(cmd, Util.t(prefix_n), mode)
-- map group triggers and nops
-- nops are needed, so that WhichKey always respects timeoutlen
local mapmode = mode == "v" and "x" or mode
if secret_only ~= true then
M.map(mapmode, prefix_n, cmd, buf, opts)
end
if not M.nowait[prefix_n] then
M.map(mapmode, prefix_n .. secret, "<nop>", buf, opts)
end
M.hooked[id] = true
end
end
function M.update(buf)
for k, tree in pairs(M.mappings) do
if tree.buf and not vim.api.nvim_buf_is_valid(tree.buf) then
-- remove group for invalid buffers
M.mappings[k] = nil
elseif not buf or not tree.buf or buf == tree.buf then
-- only update buffer maps, if:
-- 1. we dont pass a buffer
-- 2. this is a global node
-- 3. this is a local buffer node for the passed buffer
M.update_keymaps(tree.mode, tree.buf)
M.add_hooks(tree.mode, tree.buf, tree.tree.root)
end
end
end
---@param node Node
function M.add_hooks(mode, buf, node, secret_only)
if not node.mapping then
node.mapping = { prefix = node.prefix_n, group = true, keys = Util.parse_keys(node.prefix_n) }
end
if node.prefix_n ~= "" and node.mapping.group == true and not (node.mapping.cmd or node.mapping.callback) then
-- first non-cmd level, so create hook and make all decendents secret only
M.hook_add(node.prefix_n, mode, buf, secret_only)
secret_only = true
end
for _, child in pairs(node.children) do
M.add_hooks(mode, buf, child, secret_only)
end
end
function M.dump()
local ok = {}
local todo = {}
for _, tree in pairs(M.mappings) do
M.update_keymaps(tree.mode, tree.buf)
tree.tree:walk(
---@param node Node
function(node)
if node.mapping then
if node.mapping.label then
ok[node.mapping.prefix] = true
todo[node.mapping.prefix] = nil
elseif not ok[node.mapping.prefix] then
todo[node.mapping.prefix] = { node.mapping.cmd or "" }
end
end
end
)
end
return todo
end
---@param mode string
---@param buf? buffer
function M.get_tree(mode, buf)
if mode == "s" or mode == "x" then
mode = "v"
end
Util.check_mode(mode, buf)
local idx = mode .. (buf or "")
if not M.mappings[idx] then
M.mappings[idx] = { mode = mode, buf = buf, tree = Tree:new() }
end
return M.mappings[idx]
end
---@param prefix string
---@param cmd string?
function M.is_hook(prefix, cmd)
-- skip mappings with our secret nop command
if prefix:find(secret, 1, true) then
return true
end
-- skip auto which-key mappings
return cmd and cmd:find("which-key", 1, true) and cmd:find("auto", 1, true)
end
---@param mode string
---@param buf? number
function M.update_keymaps(mode, buf)
---@type Keymap[]
local keymaps = buf and vim.api.nvim_buf_get_keymap(buf, mode) or vim.api.nvim_get_keymap(mode)
local tree = M.get_tree(mode, buf).tree
local function is_nop(keymap)
return not keymap.callback and (keymap.rhs == "" or keymap.rhs:lower() == "<nop>")
end
for _, keymap in pairs(keymaps) do
local skip = M.is_hook(keymap.lhs, keymap.rhs)
if is_nop(keymap) then
skip = true
end
if not skip then
local mapping = {
prefix = keymap.lhs,
cmd = keymap.rhs,
desc = keymap.desc,
callback = keymap.callback,
keys = Util.parse_keys(keymap.lhs),
}
-- don't include Plug keymaps
if mapping.keys.notation[1]:lower() ~= "<plug>" then
local node = tree:add(mapping)
if node.mapping and node.mapping.preset and mapping.desc then
node.mapping.label = mapping.desc
end
end
end
end
end
return M

View File

@ -0,0 +1,216 @@
local Config = require("which-key.config")
local Text = require("which-key.text")
local Keys = require("which-key.keys")
local Util = require("which-key.util")
---@class Layout
---@field mapping Mapping
---@field items VisualMapping[]
---@field options Options
---@field text Text
---@field results MappingGroup
local Layout = {}
Layout.__index = Layout
---@param mappings MappingGroup
---@param options? Options
function Layout:new(mappings, options)
options = options or Config.options
local this = {
results = mappings,
mapping = mappings.mapping,
items = mappings.mappings,
options = options,
text = Text:new(),
}
setmetatable(this, self)
return this
end
function Layout:max_width(key)
local max = 0
for _, item in pairs(self.items) do
if item[key] and Text.len(item[key]) > max then
max = Text.len(item[key])
end
end
return max
end
function Layout:trail()
local prefix_i = self.results.prefix_i
local buf_path = Keys.get_tree(self.results.mode, self.results.buf).tree:path(prefix_i)
local path = Keys.get_tree(self.results.mode).tree:path(prefix_i)
local len = #self.results.mapping.keys.notation
local cmd_line = { { " " } }
for i = 1, len, 1 do
local node = buf_path[i]
if not (node and node.mapping and node.mapping.label) then
node = path[i]
end
local step = self.mapping.keys.notation[i]
if node and node.mapping and node.mapping.label then
step = self.options.icons.group .. node.mapping.label
end
if Config.options.key_labels[step] then
step = Config.options.key_labels[step]
end
if Config.options.show_keys then
table.insert(cmd_line, { step, "WhichKeyGroup" })
if i ~= #self.mapping.keys.notation then
table.insert(cmd_line, { " " .. self.options.icons.breadcrumb .. " ", "WhichKeySeparator" })
end
end
end
local width = 0
if Config.options.show_keys then
for _, line in pairs(cmd_line) do
width = width + Text.len(line[1])
end
end
local help = { --
["<bs>"] = "go up one level",
["<esc>"] = "close",
}
if #self.text.lines > self.options.layout.height.max then
help[Config.options.popup_mappings.scroll_down] = "scroll down"
help[Config.options.popup_mappings.scroll_up] = "scroll up"
end
local help_line = {}
local help_width = 0
for key, label in pairs(help) do
help_width = help_width + Text.len(key) + Text.len(label) + 2
table.insert(help_line, { key .. " ", "WhichKey" })
table.insert(help_line, { label .. " ", "WhichKeySeparator" })
end
if Config.options.show_keys then
table.insert(cmd_line, { string.rep(" ", math.floor(vim.o.columns / 2 - help_width / 2) - width) })
end
if self.options.show_help then
for _, l in pairs(help_line) do
table.insert(cmd_line, l)
end
end
if vim.o.cmdheight > 0 then
vim.api.nvim_echo(cmd_line, false, {})
vim.cmd([[redraw]])
else
local col = 1
self.text:nl()
local row = #self.text.lines
for _, text in ipairs(cmd_line) do
self.text:set(row, col, text[1], text[2] and text[2]:gsub("WhichKey", "") or nil)
col = col + vim.fn.strwidth(text[1])
end
end
end
function Layout:layout(win)
local pad_top, pad_right, pad_bot, pad_left = unpack(self.options.window.padding)
local window_width = vim.api.nvim_win_get_width(win)
local width = window_width
width = width - pad_right - pad_left
local max_key_width = self:max_width("key")
local max_label_width = self:max_width("label")
local max_value_width = self:max_width("value")
local intro_width = max_key_width + 2 + Text.len(self.options.icons.separator) + self.options.layout.spacing
local max_width = max_label_width + intro_width + max_value_width
if max_width > width then
max_width = width
end
local column_width = max_width
if max_value_width == 0 then
if column_width > self.options.layout.width.max then
column_width = self.options.layout.width.max
end
if column_width < self.options.layout.width.min then
column_width = self.options.layout.width.min
end
else
max_value_width = math.min(max_value_width, math.floor((column_width - intro_width) / 2))
end
max_label_width = column_width - (intro_width + max_value_width)
local columns = math.floor(width / column_width)
local height = math.ceil(#self.items / columns)
if height < self.options.layout.height.min then
height = self.options.layout.height.min
end
-- if height > self.options.layout.height.max then height = self.options.layout.height.max end
local col = 1
local row = 1
local columns_used = math.min(columns, math.ceil(#self.items / height))
local offset_x = 0
if columns_used < columns then
if self.options.layout.align == "right" then
offset_x = (columns - columns_used) * column_width
elseif self.options.layout.align == "center" then
offset_x = math.floor((columns - columns_used) * column_width / 2)
end
end
for _, item in pairs(self.items) do
local start = (col - 1) * column_width + self.options.layout.spacing + offset_x + pad_left
local key = item.key or ""
if key == "<lt>" then
key = "<"
end
if key == Util.t("<esc>") then
key = "<esc>"
end
if Text.len(key) < max_key_width then
key = string.rep(" ", max_key_width - Text.len(key)) .. key
end
self.text:set(row + pad_top, start, key, "")
start = start + Text.len(key) + 1
self.text:set(row + pad_top, start, self.options.icons.separator, "Separator")
start = start + Text.len(self.options.icons.separator) + 1
if item.value then
local value = item.value
start = start + 1
if Text.len(value) > max_value_width then
value = vim.fn.strcharpart(value, 0, max_value_width - 2) .. ""
end
self.text:set(row + pad_top, start, value, "Value")
if item.highlights then
for _, hl in pairs(item.highlights) do
self.text:highlight(row + pad_top, start + hl[1] - 1, start + hl[2] - 1, hl[3])
end
end
start = start + max_value_width + 2
end
local label = item.label
if Text.len(label) > max_label_width then
label = vim.fn.strcharpart(label, 0, max_label_width - 2) .. ""
end
self.text:set(row + pad_top, start, label, item.group and "Group" or "Desc")
if row % height == 0 then
col = col + 1
row = 1
else
row = row + 1
end
end
for _ = 1, pad_bot, 1 do
self.text:nl()
end
self:trail()
return self.text
end
return Layout

View File

@ -0,0 +1,231 @@
local Util = require("which-key.util")
local M = {}
local function lookup(...)
local ret = {}
for _, t in ipairs({ ... }) do
for _, v in ipairs(t) do
ret[v] = v
end
end
return ret
end
local mapargs = {
"noremap",
"desc",
"expr",
"silent",
"nowait",
"script",
"unique",
"callback",
"replace_keycodes", -- TODO: add config setting for default value
}
local wkargs = {
"prefix",
"mode",
"plugin",
"buffer",
"remap",
"cmd",
"name",
"group",
"preset",
"cond",
}
local transargs = lookup({
"noremap",
"expr",
"silent",
"nowait",
"script",
"unique",
"prefix",
"mode",
"buffer",
"preset",
"replace_keycodes",
})
local args = lookup(mapargs, wkargs)
function M.child_opts(opts)
local ret = {}
for k, v in pairs(opts) do
if transargs[k] then
ret[k] = v
end
end
return ret
end
function M._process(value, opts)
local list = {}
local children = {}
for k, v in pairs(value) do
if type(k) == "number" then
if type(v) == "table" then
-- nested child, without key
table.insert(children, v)
else
-- list value
table.insert(list, v)
end
elseif args[k] then
-- option
opts[k] = v
else
-- nested child, with key
children[k] = v
end
end
return list, children
end
function M._parse(value, mappings, opts)
if type(value) ~= "table" then
value = { value }
end
local list, children = M._process(value, opts)
if opts.plugin then
opts.group = true
end
if opts.name then
-- remove + from group names
opts.name = opts.name and opts.name:gsub("^%+", "")
opts.group = true
end
-- fix remap
if opts.remap then
opts.noremap = not opts.remap
opts.remap = nil
end
-- fix buffer
if opts.buffer == 0 then
opts.buffer = vim.api.nvim_get_current_buf()
end
if opts.cond ~= nil then
if type(opts.cond) == "function" then
if not opts.cond() then
return
end
elseif not opts.cond then
return
end
end
-- process any array child mappings
for k, v in pairs(children) do
local o = M.child_opts(opts)
if type(k) == "string" then
o.prefix = (o.prefix or "") .. k
end
M._try_parse(v, mappings, o)
end
-- { desc }
if #list == 1 then
if type(list[1]) ~= "string" then
error("Invalid mapping for " .. vim.inspect({ value = value, opts = opts }))
end
opts.desc = list[1]
-- { cmd, desc }
elseif #list == 2 then
-- desc
assert(type(list[2]) == "string")
opts.desc = list[2]
-- cmd
if type(list[1]) == "string" then
opts.cmd = list[1]
elseif type(list[1]) == "function" then
opts.cmd = ""
opts.callback = list[1]
else
error("Incorrect mapping " .. vim.inspect(list))
end
elseif #list > 2 then
error("Incorrect mapping " .. vim.inspect(list))
end
if opts.desc or opts.group then
if type(opts.mode) == "table" then
for _, mode in pairs(opts.mode) do
local mode_opts = vim.deepcopy(opts)
mode_opts.mode = mode
table.insert(mappings, mode_opts)
end
else
table.insert(mappings, opts)
end
end
end
---@return Mapping
function M.to_mapping(mapping)
mapping.silent = mapping.silent ~= false
mapping.noremap = mapping.noremap ~= false
if mapping.cmd and mapping.cmd:lower():find("^<plug>") then
mapping.noremap = false
end
mapping.buf = mapping.buffer
mapping.buffer = nil
mapping.mode = mapping.mode or "n"
mapping.label = mapping.desc or mapping.name
mapping.keys = Util.parse_keys(mapping.prefix or "")
local opts = {}
for _, o in ipairs(mapargs) do
opts[o] = mapping[o]
mapping[o] = nil
end
if vim.fn.has("nvim-0.7.0") == 0 then
opts.replace_keycodes = nil
-- Neovim < 0.7.0 doesn't support descriptions
opts.desc = nil
-- use lua functions proxy for Neovim < 0.7.0
if opts.callback then
local functions = require("which-key.keys").functions
table.insert(functions, opts.callback)
if opts.expr then
opts.cmd = string.format([[luaeval('require("which-key").execute(%d)')]], #functions)
else
opts.cmd = string.format([[<cmd>lua require("which-key").execute(%d)<cr>]], #functions)
end
opts.callback = nil
end
end
mapping.opts = opts
return mapping
end
function M._try_parse(value, mappings, opts)
local ok, err = pcall(M._parse, value, mappings, opts)
if not ok then
Util.error(err)
end
end
---@return Mapping[]
function M.parse(mappings, opts)
opts = opts or {}
local ret = {}
M._try_parse(mappings, ret, opts)
return vim.tbl_map(function(m)
return M.to_mapping(m)
end, ret)
end
return M

View File

@ -0,0 +1,59 @@
local Keys = require("which-key.keys")
local Util = require("which-key.util")
local Config = require("which-key.config")
local M = {}
M.plugins = {}
function M.setup()
for name, opts in pairs(Config.options.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 Plugin
function M._setup(plugin, opts)
if plugin.actions then
for _, trigger in pairs(plugin.actions) do
local prefix = trigger.trigger
local mode = trigger.mode or "n"
local label = trigger.label or plugin.name
Keys.register({ [prefix] = { label, plugin = plugin.name } }, { mode = mode })
end
end
if plugin.setup then
plugin.setup(require("which-key"), opts, Config.options)
end
end
---@param mapping Mapping
function M.invoke(mapping, context)
local plugin = M.plugins[mapping.plugin]
local prefix = mapping.prefix
local items = plugin.run(prefix, context.mode, context.buf)
local ret = {}
for i, item in
ipairs(items --[[@as VisualMapping[] ]])
do
item.order = i
item.keys = Util.parse_keys(prefix .. item.key)
item.prefix = prefix .. item.key
table.insert(ret, item)
end
return ret
end
return M

View File

@ -0,0 +1,66 @@
local M = {}
M.name = "marks"
M.actions = {
{ trigger = "`", mode = "n" },
{ trigger = "'", mode = "n" },
{ trigger = "g`", mode = "n" },
{ trigger = "g'", mode = "n" },
}
function M.setup(_wk, _config, options) end
local labels = {
["^"] = "Last position of cursor in insert mode",
["."] = "Last change in current buffer",
['"'] = "Last exited current buffer",
["0"] = "In last file edited",
["'"] = "Back to line in current buffer where jumped from",
["`"] = "Back to position in current buffer where jumped from",
["["] = "To beginning of previously changed or yanked text",
["]"] = "To end of previously changed or yanked text",
["<lt>"] = "To beginning of last visual selection",
[">"] = "To end of last visual selection",
}
---@type Plugin
---@return PluginItem[]
function M.run(_trigger, _mode, buf)
local items = {}
local marks = {}
vim.list_extend(marks, vim.fn.getmarklist(buf))
vim.list_extend(marks, vim.fn.getmarklist())
for _, mark in pairs(marks) do
local key = mark.mark:sub(2, 2)
if key == "<" then
key = "<lt>"
end
local lnum = mark.pos[2]
local line
if mark.pos[1] and mark.pos[1] ~= 0 then
local lines = vim.fn.getbufline(mark.pos[1], lnum)
if lines and lines[1] then
line = lines[1]
end
end
local file = mark.file and vim.fn.fnamemodify(mark.file, ":p:~:.")
local value = string.format("%4d ", lnum)
value = value .. (line or file or "")
table.insert(items, {
key = key,
label = labels[key] or file and ("file: " .. file) or "",
value = value,
highlights = { { 1, 5, "Number" } },
})
end
return items
end
return M

View File

@ -0,0 +1,107 @@
local M = {}
M.name = "presets"
M.operators = {
d = "Delete",
c = "Change",
y = "Yank (copy)",
["g~"] = "Toggle case",
["gu"] = "Lowercase",
["gU"] = "Uppercase",
[">"] = "Indent right",
["<lt>"] = "Indent left",
["zf"] = "Create fold",
["!"] = "Filter through external program",
["v"] = "Visual Character Mode",
-- ["V"] = "Visual Line Mode",
}
M.motions = {
["h"] = "Left",
["j"] = "Down",
["k"] = "Up",
["l"] = "Right",
["w"] = "Next word",
["%"] = "Matching character: '()', '{}', '[]'",
["b"] = "Previous word",
["e"] = "Next end of word",
["ge"] = "Previous end of word",
["0"] = "Start of line",
["^"] = "Start of line (non-blank)",
["$"] = "End of line",
["f"] = "Move to next char",
["F"] = "Move to previous char",
["t"] = "Move before next char",
["T"] = "Move before previous char",
["gg"] = "First line",
["G"] = "Last line",
["{"] = "Previous empty line",
["}"] = "Next empty line",
}
M.objects = {
a = { name = "around" },
i = { name = "inside" },
['a"'] = [[double quoted string]],
["a'"] = [[single quoted string]],
["a("] = [[same as ab]],
["a)"] = [[same as ab]],
["a<lt>"] = [[a <> from '<' to the matching '>']],
["a>"] = [[same as a<]],
["aB"] = [[a Block from [{ to ]} (with brackets)]],
["aW"] = [[a WORD (with white space)]],
["a["] = [[a [] from '[' to the matching ']']],
["a]"] = [[same as a[]],
["a`"] = [[string in backticks]],
["ab"] = [[a block from [( to ]) (with braces)]],
["ap"] = [[a paragraph (with white space)]],
["as"] = [[a sentence (with white space)]],
["at"] = [[a tag block (with white space)]],
["aw"] = [[a word (with white space)]],
["a{"] = [[same as aB]],
["a}"] = [[same as aB]],
['i"'] = [[double quoted string without the quotes]],
["i'"] = [[single quoted string without the quotes]],
["i("] = [[same as ib]],
["i)"] = [[same as ib]],
["i<lt>"] = [[inner <> from '<' to the matching '>']],
["i>"] = [[same as i<]],
["iB"] = [[inner Block from [{ and ]}]],
["iW"] = [[inner WORD]],
["i["] = [[inner [] from '[' to the matching ']']],
["i]"] = [[same as i[]],
["i`"] = [[string in backticks without the backticks]],
["ib"] = [[inner block from [( to ])]],
["ip"] = [[inner paragraph]],
["is"] = [[inner sentence]],
["it"] = [[inner tag block]],
["iw"] = [[inner word]],
["i{"] = [[same as iB]],
["i}"] = [[same as iB]],
}
---@param config Options
function M.setup(wk, opts, config)
require("which-key.plugins.presets.misc").setup(wk, opts)
-- Operators
if opts.operators then
for op, label in pairs(M.operators) do
config.operators[op] = label
end
end
-- Motions
if opts.motions then
wk.register(M.motions, { mode = "n", prefix = "", preset = true })
wk.register(M.motions, { mode = "o", prefix = "", preset = true })
end
-- Text objects
if opts.text_objects then
wk.register(M.objects, { mode = "o", prefix = "", preset = true })
end
end
return M

View File

@ -0,0 +1,97 @@
local M = {}
M.name = "misc"
local misc = {
windows = {
["<c-w>"] = {
name = "window",
s = "Split window",
v = "Split window vertically",
w = "Switch windows",
q = "Quit a window",
o = "Close all other windows",
T = "Break out into a new tab",
x = "Swap current with next",
["-"] = "Decrease height",
["+"] = "Increase height",
["<lt>"] = "Decrease width",
[">"] = "Increase width",
["|"] = "Max out the width",
["_"] = "Max out the height",
["="] = "Equally high and wide",
h = "Go to the left window",
l = "Go to the right window",
k = "Go to the up window",
j = "Go to the down window",
},
},
z = {
["z"] = {
o = "Open fold under cursor",
O = "Open all folds under cursor",
c = "Close fold under cursor",
C = "Close all folds under cursor",
a = "Toggle fold under cursor",
A = "Toggle all folds under cursor",
v = "Show cursor line",
M = "Close all folds",
R = "Open all folds",
m = "Fold more",
r = "Fold less",
x = "Update folds",
z = "Center this line",
t = "Top this line",
["<CR>"] = "Top this line, 1st non-blank col",
b = "Bottom this line",
g = "Add word to spell list",
w = "Mark word as bad/misspelling",
e = "Right this line",
s = "Left this line",
H = "Half screen to the left",
L = "Half screen to the right",
i = "Toggle folding",
["="] = "Spelling suggestions",
},
},
nav = {
["[{"] = "Previous {",
["[("] = "Previous (",
["[<lt>"] = "Previous <",
["[m"] = "Previous method start",
["[M"] = "Previous method end",
["[%"] = "Previous unmatched group",
["[s"] = "Previous misspelled word",
["]{"] = "Next {",
["]("] = "Next (",
["]<lt>"] = "Next <",
["]m"] = "Next method start",
["]M"] = "Next method end",
["]%"] = "Next unmatched group",
["]s"] = "Next misspelled word",
["H"] = "Home line of window (top)",
["M"] = "Middle line of window",
["L"] = "Last line of window",
},
g = {
["gf"] = "Go to file under cursor",
["gx"] = "Open the file under cursor with system app",
["gi"] = "Move to the last insertion and INSERT",
["gv"] = "Switch to VISUAL using last selection",
["gn"] = "Search forwards and select",
["gN"] = "Search backwards and select",
["g%"] = "Cycle backwards through results",
["gt"] = "Go to next tab page",
["gT"] = "Go to previous tab page",
},
}
function M.setup(wk, config)
for key, mappings in pairs(misc) do
if config[key] ~= false then
wk.register(mappings, { mode = "n", prefix = "", preset = true })
end
end
end
return M

View File

@ -0,0 +1,52 @@
---@type Plugin
local M = {}
M.name = "registers"
M.actions = {
{ trigger = '"', mode = "n" },
{ trigger = '"', mode = "v" },
-- { trigger = "@", mode = "n" },
{ trigger = "<c-r>", mode = "i" },
{ trigger = "<c-r>", mode = "c" },
}
function M.setup(_wk, _config, options) end
M.registers = '*+"-:.%/#=_abcdefghijklmnopqrstuvwxyz0123456789'
local labels = {
['"'] = "last deleted, changed, or yanked content",
["0"] = "last yank",
["-"] = "deleted or changed content smaller than one line",
["."] = "last inserted text",
["%"] = "name of the current file",
[":"] = "most recent executed command",
["#"] = "alternate buffer",
["="] = "result of an expression",
["+"] = "synchronized with the system clipboard",
["*"] = "synchronized with the selection clipboard",
["_"] = "black hole",
["/"] = "last search pattern",
}
---@type Plugin
---@return PluginItem[]
function M.run(_trigger, _mode, _buf)
local items = {}
for i = 1, #M.registers, 1 do
local key = M.registers:sub(i, i)
local ok, value = pcall(vim.fn.getreg, key, 1)
if not ok then
value = ""
end
if value ~= "" then
table.insert(items, { key = key, label = labels[key] or "", value = value })
end
end
return items
end
return M

View File

@ -0,0 +1,51 @@
local M = {}
M.name = "spelling"
M.actions = { { trigger = "z=", mode = "n" } }
M.opts = {}
function M.setup(_, config, options)
M.opts = config
end
---@type Plugin
---@return PluginItem[]
function M.run()
-- if started with a count, let the default keybinding work
local count = vim.api.nvim_get_vvar("count")
if count and count > 0 then
return {}
end
---@diagnostic disable-next-line: missing-parameter
local cursor_word = vim.fn.expand("<cword>")
-- get a misspellled word from under the cursor, if not found, then use the cursor_word instead
---@diagnostic disable-next-line: redundant-parameter
local bad = vim.fn.spellbadword(cursor_word)
local word = bad[1]
if word == "" then
word = cursor_word
end
local suggestions = vim.fn.spellsuggest(word, M.opts.suggestions or 20, bad[2] == "caps" and 1 or 0)
local items = {}
local keys = "1234567890abcdefghijklmnopqrstuvwxyz"
for i, label in ipairs(suggestions) do
local key = keys:sub(i, i)
table.insert(items, {
key = key,
label = label,
fn = function()
vim.cmd("norm! ciw" .. label)
end,
})
end
return items
end
return M

View File

@ -0,0 +1,72 @@
---@class Highlight
---@field group string
---@field line number
---@field from number
---@field to number
---@class Text
---@field lines string[]
---@field hl Highlight[]
---@field lineNr number
---@field current string
local Text = {}
Text.__index = Text
function Text.len(str)
return vim.fn.strwidth(str)
end
function Text:new()
local this = { lines = {}, hl = {}, lineNr = 0, current = "" }
setmetatable(this, self)
return this
end
function Text:fix_nl(line)
return line:gsub("[\n]", "")
end
function Text:nl()
local line = self:fix_nl(self.current)
table.insert(self.lines, line)
self.current = ""
self.lineNr = self.lineNr + 1
end
function Text:set(row, col, str, group)
str = self:fix_nl(str)
-- extend lines if needed
for i = 1, row, 1 do
if not self.lines[i] then
self.lines[i] = ""
end
end
-- extend columns when needed
local width = Text.len(self.lines[row])
if width < col then
self.lines[row] = self.lines[row] .. string.rep(" ", col - width)
end
local before = vim.fn.strcharpart(self.lines[row], 0, col)
local after = vim.fn.strcharpart(self.lines[row], col)
self.lines[row] = before .. str .. after
if not group then
return
end
-- set highlights
self:highlight(row, col, col + Text.len(str), "WhichKey" .. group)
end
function Text:highlight(row, from, to, group)
local line = self.lines[row]
local before = vim.fn.strcharpart(line, 0, from)
local str = vim.fn.strcharpart(line, 0, to)
from = vim.fn.strlen(before)
to = vim.fn.strlen(str)
table.insert(self.hl, { line = row - 1, from = from, to = to, group = group })
end
return Text

View File

@ -0,0 +1,108 @@
local Util = require("which-key.util")
---@class Tree
---@field root Node
---@field nodes table<string, Node>
local Tree = {}
Tree.__index = Tree
---@class Node
---@field mapping Mapping
---@field prefix_i string
---@field prefix_n string
---@field children table<string, Node>
-- selene: allow(unused_variable)
local Node
---@return Tree
function Tree:new()
local this = { root = { children = {}, prefix_i = "", prefix_n = "" }, nodes = {} }
setmetatable(this, self)
return this
end
---@param prefix_i string
---@param index? number defaults to last. If < 0, then offset from last
---@param plugin_context? any
---@return Node?
function Tree:get(prefix_i, index, plugin_context)
local prefix = Util.parse_internal(prefix_i)
local node = self.root
index = index or #prefix
if index < 0 then
index = #prefix + index
end
for i = 1, index, 1 do
node = node.children[prefix[i]]
if node and plugin_context and node.mapping and node.mapping.plugin then
local children = require("which-key.plugins").invoke(node.mapping, plugin_context)
node.children = {}
for _, child in pairs(children) do
self:add(child, { cache = false })
end
end
if not node then
return nil
end
end
return node
end
-- Returns the path (possibly incomplete) for the prefix
---@param prefix_i string
---@return Node[]
function Tree:path(prefix_i)
local prefix = Util.parse_internal(prefix_i)
local node = self.root
local path = {}
for i = 1, #prefix, 1 do
node = node.children[prefix[i]]
table.insert(path, node)
if not node then
break
end
end
return path
end
---@param mapping Mapping
---@param opts? {cache?: boolean}
---@return Node
function Tree:add(mapping, opts)
opts = opts or {}
opts.cache = opts.cache ~= false
local node_key = mapping.keys.keys
local node = opts.cache and self.nodes[node_key]
if not node then
local prefix_i = mapping.keys.internal
local prefix_n = mapping.keys.notation
node = self.root
local path_i = ""
local path_n = ""
for i = 1, #prefix_i, 1 do
path_i = path_i .. prefix_i[i]
path_n = path_n .. prefix_n[i]
if not node.children[prefix_i[i]] then
node.children[prefix_i[i]] = { children = {}, prefix_i = path_i, prefix_n = path_n }
end
node = node.children[prefix_i[i]]
end
if opts.cache then
self.nodes[node_key] = node
end
end
node.mapping = vim.tbl_deep_extend("force", node.mapping or {}, mapping)
return node
end
---@param cb fun(node:Node)
---@param node? Node
function Tree:walk(cb, node)
node = node or self.root
cb(node)
for _, child in pairs(node.children) do
self:walk(cb, child)
end
end
return Tree

View File

@ -0,0 +1,74 @@
---@meta
--# selene: allow(unused_variable)
---@class Keymap
---@field rhs string
---@field lhs string
---@field buffer number
---@field expr number
---@field lnum number
---@field mode string
---@field noremap number
---@field nowait number
---@field script number
---@field sid number
---@field silent number
---@field callback fun()|nil
---@field id string terminal keycodes for lhs
---@field desc string
---@class KeyCodes
---@field keys string
---@field internal string[]
---@field notation string[]
---@class MappingOptions
---@field noremap boolean
---@field silent boolean
---@field nowait boolean
---@field expr boolean
---@class Mapping
---@field buf number
---@field group boolean
---@field label string
---@field desc string
---@field prefix string
---@field cmd string
---@field opts MappingOptions
---@field keys KeyCodes
---@field mode? string
---@field callback fun()|nil
---@field preset boolean
---@field plugin string
---@field fn fun()
---@class MappingTree
---@field mode string
---@field buf? number
---@field tree Tree
---@class VisualMapping : Mapping
---@field key string
---@field highlights table
---@field value string
---@class PluginItem
---@field key string
---@field label string
---@field value string
---@field cmd string
---@field highlights table
---@class PluginAction
---@field trigger string
---@field mode string
---@field label? string
---@field delay? boolean
---@class Plugin
---@field name string
---@field actions PluginAction[]
---@field run fun(trigger:string, mode:string, buf:number):PluginItem[]
---@field setup fun(wk, opts, Options)

View File

@ -0,0 +1,196 @@
---@class Util
local M = {}
local strbyte = string.byte
local strsub = string.sub
---@type table<string, KeyCodes>
local cache = {}
---@type table<string,string>
local tcache = {}
local cache_leaders = ""
function M.check_cache()
---@type string
local leaders = (vim.g.mapleader or "") .. ":" .. (vim.g.maplocalleader or "")
if leaders ~= cache_leaders then
cache = {}
tcache = {}
cache_leaders = leaders
end
end
function M.count(tab)
local ret = 0
for _, _ in pairs(tab) do
ret = ret + 1
end
return ret
end
function M.get_mode()
local mode = vim.api.nvim_get_mode().mode
mode = mode:gsub(M.t("<C-V>"), "v")
mode = mode:gsub(M.t("<C-S>"), "s")
return mode:lower()
end
function M.is_empty(tab)
return M.count(tab) == 0
end
function M.t(str)
M.check_cache()
if not tcache[str] then
-- https://github.com/neovim/neovim/issues/17369
tcache[str] = vim.api.nvim_replace_termcodes(str, false, true, true):gsub("\128\254X", "\128")
end
return tcache[str]
end
-- stylua: ignore start
local utf8len_tab = {
-- ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?A ?B ?C ?D ?E ?F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 0?
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 1?
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 2?
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 3?
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 4?
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 5?
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 6?
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 7?
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 8?
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 9?
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- A?
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- B?
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -- C?
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -- D?
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, -- E?
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1, -- F?
}
-- stylua: ignore end
local Tokens = {
["<"] = strbyte("<"),
[">"] = strbyte(">"),
["-"] = strbyte("-"),
}
---@return KeyCodes
function M.parse_keys(keystr)
M.check_cache()
if cache[keystr] then
return cache[keystr]
end
local keys = M.t(keystr)
local internal = M.parse_internal(keys)
if #internal == 0 then
local ret = { keys = keys, internal = {}, notation = {} }
cache[keystr] = ret
return ret
end
local keystr_orig = keystr
keystr = keystr:gsub("<lt>", "<")
local notation = {}
---@alias ParseState
--- | "Character"
--- | "Special"
--- | "SpecialNoClose"
local start = 1
local i = start
---@type ParseState
local state = "Character"
while i <= #keystr do
local c = strbyte(keystr, i, i)
if state == "Character" then
start = i
-- Only interpret special tokens if neovim also replaces it
state = c == Tokens["<"] and internal[#notation + 1] ~= "<" and "Special" or state
elseif state == "Special" then
state = (c == Tokens["-"] and "SpecialNoClose") or (c == Tokens[">"] and "Character") or state
else
state = "Special"
end
i = i + utf8len_tab[c + 1]
if state == "Character" then
local k = strsub(keystr, start, i - 1)
notation[#notation + 1] = k == " " and "<space>" or k
end
end
local mapleader = vim.g.mapleader
mapleader = mapleader and M.t(mapleader)
notation[1] = internal[1] == mapleader and "<leader>" or notation[1]
if #notation ~= #internal then
error(vim.inspect({ keystr = keystr, internal = internal, notation = notation }))
end
local ret = {
keys = keys,
internal = internal,
notation = notation,
}
cache[keystr_orig] = ret
return ret
end
-- @return string[]
function M.parse_internal(keystr)
local keys = {}
---@alias ParseInternalState
--- | "Character"
--- | "Special"
---@type ParseInternalState
local state = "Character"
local start = 1
local i = 1
while i <= #keystr do
local c = strbyte(keystr, i, i)
if state == "Character" then
state = c == 128 and "Special" or state
i = i + utf8len_tab[c + 1]
if state == "Character" then
keys[#keys + 1] = strsub(keystr, start, i - 1)
start = i
end
else
-- This state is entered on the second byte of K_SPECIAL sequence.
if c == 252 then
-- K_SPECIAL KS_MODIFIER: skip this byte and the next
i = i + 2
else
-- K_SPECIAL _: skip this byte
i = i + 1
end
-- The last byte of this sequence should be between 0x02 and 0x7f,
-- switch to Character state to collect.
state = "Character"
end
end
return keys
end
function M.warn(msg)
vim.notify(msg, vim.log.levels.WARN, { title = "WhichKey" })
end
function M.error(msg)
vim.notify(msg, vim.log.levels.ERROR, { title = "WhichKey" })
end
function M.check_mode(mode, buf)
if not ("nvsxoiRct"):find(mode) then
M.error(string.format("Invalid mode %q for buf %d", mode, buf or 0))
return false
end
return true
end
return M

View File

@ -0,0 +1,350 @@
local Keys = require("which-key.keys")
local config = require("which-key.config")
local Layout = require("which-key.layout")
local Util = require("which-key.util")
local highlight = vim.api.nvim_buf_add_highlight
---@class View
local M = {}
M.keys = ""
M.mode = "n"
M.reg = nil
M.auto = false
M.count = 0
M.buf = nil
M.win = nil
function M.is_valid()
return M.buf
and M.win
and vim.api.nvim_buf_is_valid(M.buf)
and vim.api.nvim_buf_is_loaded(M.buf)
and vim.api.nvim_win_is_valid(M.win)
end
function M.show()
if vim.b.visual_multi then
vim.b.VM_skip_reset_once_on_bufleave = true
end
if M.is_valid() then
return
end
-- non-floating windows
local wins = vim.tbl_filter(function(w)
return vim.api.nvim_win_is_valid(w) and vim.api.nvim_win_get_config(w).relative == ""
end, vim.api.nvim_list_wins())
---@type number[]
local margins = {}
for i, m in ipairs(config.options.window.margin) do
if m > 0 and m < 1 then
if i % 2 == 0 then
m = math.floor(vim.o.columns * m)
else
m = math.floor(vim.o.lines * m)
end
end
margins[i] = m
end
local opts = {
relative = "editor",
width = vim.o.columns
- margins[2]
- margins[4]
- (vim.fn.has("nvim-0.6") == 0 and config.options.window.border ~= "none" and 2 or 0),
height = config.options.layout.height.min,
focusable = false,
anchor = "SW",
border = config.options.window.border,
row = vim.o.lines
- margins[3]
- (vim.fn.has("nvim-0.6") == 0 and config.options.window.border ~= "none" and 2 or 0)
+ ((vim.o.laststatus == 0 or vim.o.laststatus == 1 and #wins == 1) and 1 or 0)
- vim.o.cmdheight,
col = margins[4],
style = "minimal",
noautocmd = true,
zindex = config.options.window.zindex,
}
if config.options.window.position == "top" then
opts.anchor = "NW"
opts.row = margins[1]
end
M.buf = vim.api.nvim_create_buf(false, true)
M.win = vim.api.nvim_open_win(M.buf, false, opts)
vim.api.nvim_buf_set_option(M.buf, "filetype", "WhichKey")
vim.api.nvim_buf_set_option(M.buf, "buftype", "nofile")
vim.api.nvim_buf_set_option(M.buf, "bufhidden", "wipe")
vim.api.nvim_buf_set_option(M.buf, "modifiable", true)
local winhl = "NormalFloat:WhichKeyFloat"
if vim.fn.hlexists("FloatBorder") == 1 then
winhl = winhl .. ",FloatBorder:WhichKeyBorder"
end
vim.api.nvim_win_set_option(M.win, "winhighlight", winhl)
vim.api.nvim_win_set_option(M.win, "foldmethod", "manual")
vim.api.nvim_win_set_option(M.win, "winblend", config.options.window.winblend)
end
function M.read_pending()
local esc = ""
while true do
local n = vim.fn.getchar(0)
if n == 0 then
break
end
local c = (type(n) == "number" and vim.fn.nr2char(n) or n)
-- HACK: for some reason, when executing a :norm command,
-- vim keeps feeding <esc> at the end
if c == Util.t("<esc>") then
esc = esc .. c
-- more than 10 <esc> in a row? most likely the norm bug
if #esc > 10 then
return
end
else
-- we have <esc> characters, so add them to keys
if esc ~= "" then
M.keys = M.keys .. esc
esc = ""
end
M.keys = M.keys .. c
end
end
if esc ~= "" then
M.keys = M.keys .. esc
esc = ""
end
end
function M.getchar()
local ok, n = pcall(vim.fn.getchar)
-- bail out on keyboard interrupt
if not ok then
return Util.t("<esc>")
end
local c = (type(n) == "number" and vim.fn.nr2char(n) or n)
return c
end
function M.scroll(up)
local height = vim.api.nvim_win_get_height(M.win)
local cursor = vim.api.nvim_win_get_cursor(M.win)
if up then
cursor[1] = math.max(cursor[1] - height, 1)
else
cursor[1] = math.min(cursor[1] + height, vim.api.nvim_buf_line_count(M.buf))
end
vim.api.nvim_win_set_cursor(M.win, cursor)
end
function M.on_close()
M.hide()
end
function M.hide()
vim.api.nvim_echo({ { "" } }, false, {})
M.hide_cursor()
if M.buf and vim.api.nvim_buf_is_valid(M.buf) then
vim.api.nvim_buf_delete(M.buf, { force = true })
M.buf = nil
end
if M.win and vim.api.nvim_win_is_valid(M.win) then
vim.api.nvim_win_close(M.win, true)
M.win = nil
end
vim.cmd("redraw")
end
function M.show_cursor()
local buf = vim.api.nvim_get_current_buf()
local cursor = vim.api.nvim_win_get_cursor(0)
vim.api.nvim_buf_add_highlight(buf, config.namespace, "Cursor", cursor[1] - 1, cursor[2], cursor[2] + 1)
end
function M.hide_cursor()
local buf = vim.api.nvim_get_current_buf()
vim.api.nvim_buf_clear_namespace(buf, config.namespace, 0, -1)
end
function M.back()
local node = Keys.get_tree(M.mode, M.buf).tree:get(M.keys, -1) or Keys.get_tree(M.mode).tree:get(M.keys, -1)
if node then
M.keys = node.prefix_i
end
end
function M.execute(prefix_i, mode, buf)
local global_node = Keys.get_tree(mode).tree:get(prefix_i)
local buf_node = buf and Keys.get_tree(mode, buf).tree:get(prefix_i) or nil
if global_node and global_node.mapping and Keys.is_hook(prefix_i, global_node.mapping.cmd) then
return
end
if buf_node and buf_node.mapping and Keys.is_hook(prefix_i, buf_node.mapping.cmd) then
return
end
local hooks = {}
local function unhook(nodes, nodes_buf)
for _, node in pairs(nodes) do
if Keys.is_hooked(node.mapping.prefix, mode, nodes_buf) then
table.insert(hooks, { node.mapping.prefix, nodes_buf })
Keys.hook_del(node.mapping.prefix, mode, nodes_buf)
end
end
end
-- make sure we remove all WK hooks before executing the sequence
-- this is to make existing keybindongs work and prevent recursion
unhook(Keys.get_tree(mode).tree:path(prefix_i))
if buf then
unhook(Keys.get_tree(mode, buf).tree:path(prefix_i), buf)
end
-- feed CTRL-O again if called from CTRL-O
local full_mode = Util.get_mode()
if full_mode == "nii" or full_mode == "nir" or full_mode == "niv" or full_mode == "vs" then
vim.api.nvim_feedkeys(Util.t("<C-O>"), "n", false)
end
-- handle registers that were passed when opening the popup
if M.reg ~= '"' and M.mode ~= "i" and M.mode ~= "c" then
vim.api.nvim_feedkeys('"' .. M.reg, "n", false)
end
if M.count and M.count ~= 0 then
prefix_i = M.count .. prefix_i
end
-- feed the keys with remap
vim.api.nvim_feedkeys(prefix_i, "m", true)
-- defer hooking WK until after the keys were executed
vim.defer_fn(function()
for _, hook in pairs(hooks) do
Keys.hook_add(hook[1], mode, hook[2])
end
end, 0)
end
function M.open(keys, opts)
opts = opts or {}
M.keys = keys or ""
M.mode = opts.mode or Util.get_mode()
M.count = vim.api.nvim_get_vvar("count")
M.reg = vim.api.nvim_get_vvar("register")
if string.find(vim.o.clipboard, "unnamedplus") and M.reg == "+" then
M.reg = '"'
end
if string.find(vim.o.clipboard, "unnamed") and M.reg == "*" then
M.reg = '"'
end
M.show_cursor()
M.on_keys(opts)
end
function M.is_enabled(buf)
local buftype = vim.api.nvim_buf_get_option(buf, "buftype")
for _, bt in ipairs(config.options.disable.buftypes) do
if bt == buftype then
return false
end
end
local filetype = vim.api.nvim_buf_get_option(buf, "filetype")
for _, bt in ipairs(config.options.disable.filetypes) do
if bt == filetype then
return false
end
end
return true
end
function M.on_keys(opts)
local buf = vim.api.nvim_get_current_buf()
while true do
-- loop
M.read_pending()
local results = Keys.get_mappings(M.mode, M.keys, buf)
--- Check for an exact match. Feedkeys with remap
if results.mapping and not results.mapping.group and #results.mappings == 0 then
M.hide()
if results.mapping.fn then
results.mapping.fn()
else
M.execute(M.keys, M.mode, buf)
end
return
end
-- Check for no mappings found. Feedkeys without remap
if #results.mappings == 0 then
M.hide()
-- only execute if an actual key was typed while WK was open
if opts.auto then
M.execute(M.keys, M.mode, buf)
end
return
end
local layout = Layout:new(results)
if M.is_enabled(buf) then
if not M.is_valid() then
M.show()
end
M.render(layout:layout(M.win))
end
vim.cmd([[redraw]])
local c = M.getchar()
if c == Util.t("<esc>") then
M.hide()
break
elseif c == Util.t(config.options.popup_mappings.scroll_down) then
M.scroll(false)
elseif c == Util.t(config.options.popup_mappings.scroll_up) then
M.scroll(true)
elseif c == Util.t("<bs>") then
M.back()
else
M.keys = M.keys .. c
end
end
end
---@param text Text
function M.render(text)
vim.api.nvim_buf_set_lines(M.buf, 0, -1, false, text.lines)
local height = #text.lines
if height > config.options.layout.height.max then
height = config.options.layout.height.max
end
vim.api.nvim_win_set_height(M.win, height)
if vim.api.nvim_buf_is_valid(M.buf) then
vim.api.nvim_buf_clear_namespace(M.buf, config.namespace, 0, -1)
end
for _, data in ipairs(text.hl) do
highlight(M.buf, config.namespace, data.group, data.line, data.from, data.to)
end
end
return M

View File

@ -0,0 +1 @@
command! -nargs=* WhichKey lua require('which-key').show_command(<f-args>)

View File

@ -0,0 +1 @@
std="lua51+vim"

View File

@ -0,0 +1,3 @@
indent_type = "Spaces"
indent_width = 2
column_width = 120

View File

@ -0,0 +1,2 @@
[vim]
any = true