Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ local defaults = {
delay = function(ctx)
return ctx.plugin and 0 or 200
end,
---@param mapping wk.Mapping
---@param mapping wk.Mapping|wk.Keymap
filter = function(mapping)
-- example to exclude mappings without a description
-- return mapping.desc and mapping.desc ~= ""
Expand Down Expand Up @@ -273,6 +273,7 @@ A mapping has the following attributes:
- **icon**: (`string|wk.Icon|fun():(wk.Icon|string)`) icon spec **_(optional)_**
- **proxy**: (`string`) proxy to another mapping **_(optional)_**
- **expand**: (`fun():wk.Spec`) nested mappings **_(optional)_**
- **op**: (`boolean`) is the mapping an operator like d, y, gc **(optional)**
- any other option valid for `vim.keymap.set`. These are only used for creating mappings.

When `desc`, `group`, or `icon` are functions, they are evaluated every time
Expand All @@ -297,6 +298,7 @@ wk.add({
return require("which-key.extras").expand.buf()
end
},
{ "gc", group = "toggle comments", op = true } -- mark as operator
{
-- Nested mappings are allowed and can be added in any order
-- Most attributes can be inherited or overridden on any level
Expand All @@ -319,7 +321,7 @@ Both can be configured using `opts.triggers` and `opts.defer`.

By default `opts.triggers` includes `{ "<auto>", mode = "nixsotc" }`, which
will setup keymap triggers for every mode automatically and will trigger during
`ModeChanged`.
`ModeChanged`. After setup you can add additional triggers with `wk.add_triggers()`

> [!NOTE]
> Auto triggers will never be created for existing keymaps.
Expand Down
26 changes: 14 additions & 12 deletions doc/which-key.nvim.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ Default Options ~
delay = function(ctx)
return ctx.plugin and 0 or 200
end,
---@param mapping wk.Mapping
---@param mapping wk.Mapping|wk.Keymap
filter = function(mapping)
-- example to exclude mappings without a description
-- return mapping.desc and mapping.desc ~= ""
Expand Down Expand Up @@ -293,6 +293,7 @@ A mapping has the following attributes:
- **icon**: (`string|wk.Icon|fun():(wk.Icon|string)`) icon spec **(optional)**
- **proxy**: (`string`) proxy to another mapping **(optional)**
- **expand**: (`fun():wk.Spec`) nested mappings **(optional)**
- **op**: (`boolean`) is the mapping an operator like d, y, gc **(optional)**
- any other option valid for `vim.keymap.set`. These are only used for creating mappings.

When `desc`, `group`, or `icon` are functions, they are evaluated every time
Expand All @@ -318,6 +319,7 @@ The `expand` property allows to create dynamic mappings. Only functions as
return require("which-key.extras").expand.buf()
end
},
{ "gc", group = "toggle comments", op = true } -- mark as operator
{
-- Nested mappings are allowed and can be added in any order
-- Most attributes can be inherited or overridden on any level
Expand All @@ -341,7 +343,7 @@ Both can be configured using `opts.triggers` and `opts.defer`.

By default `opts.triggers` includes `{ "<auto>", mode = "nixsotc" }`, which
will setup keymap triggers for every mode automatically and will trigger during
`ModeChanged`.
`ModeChanged`. After setup you can add additional triggers with `wk.add_triggers()`


[!NOTE] Auto triggers will never be created for existing keymaps. That includes
Expand Down Expand Up @@ -464,7 +466,7 @@ their default link.
-----------------------------------------------------------------------
Highlight Group Default Group Description
----------------------- ----------------------- -----------------------
WhichKey Function
WhichKey Function

WhichKeyBorder FloatBorder Border of the which-key
window
Expand All @@ -475,23 +477,23 @@ their default link.

WhichKeyIcon @markup.link icons

WhichKeyIconAzure Function
WhichKeyIconAzure Function

WhichKeyIconBlue DiagnosticInfo
WhichKeyIconBlue DiagnosticInfo

WhichKeyIconCyan DiagnosticHint
WhichKeyIconCyan DiagnosticHint

WhichKeyIconGreen DiagnosticOk
WhichKeyIconGreen DiagnosticOk

WhichKeyIconGrey Normal
WhichKeyIconGrey Normal

WhichKeyIconOrange DiagnosticWarn
WhichKeyIconOrange DiagnosticWarn

WhichKeyIconPurple Constant
WhichKeyIconPurple Constant

WhichKeyIconRed DiagnosticError
WhichKeyIconRed DiagnosticError

WhichKeyIconYellow DiagnosticWarn
WhichKeyIconYellow DiagnosticWarn

WhichKeyNormal NormalFloat Normal in th which-key
window
Expand Down
24 changes: 6 additions & 18 deletions lua/which-key/buf.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,11 @@ end

--- Checks if it's safe to add a trigger for the given node
---@param node wk.Node
---@param no_single? boolean
local function is_safe(node, no_single)
if node.keymap or is_special(node) or node:count() == 0 then
local function is_safe(node)
if (node.keymap and not node.op) or is_special(node) or node:count() == 0 then
return false
end
if no_single and #node.path == 1 then
local key = node.path[1]
-- only z or g are safe
if key:match("^[a-z]$") and not key:match("^[gz]$") then
return false
end
-- only Z is safe
if key:match("^[A-Z]$") and not key:match("^[Z]$") then
return false
end
end

return true
end

Expand Down Expand Up @@ -69,10 +58,9 @@ function Mode:attach()
if Config.triggers.modes[self.mode] then
-- Auto triggers
self.tree:walk(function(node)
if node.keymap and node.keymap.nowait then
return false
end
if is_safe(node, true) then
if node:is_nowait() then return false end

if is_safe(node) then
table.insert(self.triggers, node)
return false
end
Expand Down
64 changes: 37 additions & 27 deletions lua/which-key/config.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
---@class wk.Config: wk.Opts
---@field triggers {mappings: wk.Mapping[], modes: table<string,boolean>}
local M = {}

M.version = "3.17.0" -- x-release-please-version
Expand All @@ -13,7 +12,7 @@ local defaults = {
delay = function(ctx)
return ctx.plugin and 0 or 200
end,
---@param mapping wk.Mapping
---@param mapping wk.Mapping|wk.Keymap
filter = function(mapping)
-- example to exclude mappings without a description
-- return mapping.desc and mapping.desc ~= ""
Expand Down Expand Up @@ -188,6 +187,12 @@ M.options = nil
---@type {opt:string, msg:string}[]
M.issues = {}

---@type {mappings: wk.Mapping[], modes: table<string,boolean>}
M.triggers = {
mappings = {},
modes = {},
}

function M.validate()
local deprecated = {
["operators"] = "see `opts.defer`",
Expand All @@ -203,7 +208,7 @@ function M.validate()
["modes"] = "see `opts.triggers`",
}
for k, v in pairs(deprecated) do
local opt = type(k) == "number" and v or k
local opt = type(k) == "number" and v or k --[[@as string]]
local msg = "option is deprecated." .. (type(k) == "number" and "" or " " .. v)
local parts = vim.split(opt, ".", { plain = true })
if vim.tbl_get(M.options, unpack(parts)) ~= nil then
Expand All @@ -227,6 +232,7 @@ function M.setup(opts)
return
end
local Util = require("which-key.util")
local wk = require("which-key")

if M.options.preset then
local Presets = require("which-key.presets")
Expand Down Expand Up @@ -254,37 +260,25 @@ function M.setup(opts)
end
end

local wk = require("which-key")

-- replace by the real add function
wk.add = M.add

-- set triggers
if type(M.options.triggers) ~= "table" then
---@diagnostic disable-next-line: inject-field
M.options.triggers = defaults.triggers
end

M.triggers = {
mappings = require("which-key.mappings").parse(M.options.triggers),
modes = {},
}
---@param m wk.Mapping
M.triggers.mappings = vim.tbl_filter(function(m)
if m.lhs == "<auto>" then
M.triggers.modes[m.mode] = true
return false
end
return true
end, M.triggers.mappings)
M.add_triggers(M.options.triggers)

-- load presets first so that they can be overriden by the user
require("which-key.plugins").setup()

-- process mappings queue
for _, todo in ipairs(wk._queue) do
M.add(todo.spec, todo.opts)
end
wk._queue = {}
-- replace by the real functions
wk.add = M.add
wk.add_triggers = M.add_triggers

-- add things from before which-key was loaded properly
for _, todo in ipairs(wk.mappings) do M.add(todo.spec, todo.opts) end
for _, todo in ipairs(wk.triggers) do M.add_triggers(todo) end
wk.mappings = {}
wk.triggers = {}

-- finally, add the mapppings from the config
M.add(M.options.spec)
Expand All @@ -310,7 +304,7 @@ function M.setup(opts)
return require("which-key.util").error("Usage: WhichKey [mode] [keys]")
end
if mode == "" then
mode = "n"
mode = vim.api.nvim_get_mode().mode
end
require("which-key").show({ mode = mode, keys = keys })
end, {
Expand All @@ -333,6 +327,22 @@ function M.add(mappings, opts)
end
end

---@param triggers wk.Spec
function M.add_triggers(triggers)
if type(triggers) ~= "table" then return end

vim.list_extend(M.options.triggers, triggers)

---@param m wk.Mapping
M.triggers.mappings = vim.tbl_filter(function(m)
if m.lhs == "<auto>" then
M.triggers.modes[m.mode] = true
return false
end
return true
end, require("which-key.mappings").parse(M.options.triggers))
end

return setmetatable(M, {
__index = function(_, k)
if rawget(M, "options") == nil then
Expand Down
6 changes: 3 additions & 3 deletions lua/which-key/health.lua
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ function M.check()
(notif.level >= vim.log.levels.ERROR and error or warn)(msg)
end

start("checking for overlapping keymaps")
start("Checking for overlapping keymaps")
local found = false

Buf.cleanup()
Expand All @@ -90,10 +90,10 @@ function M.check()
if mode then
mode.tree:walk(function(node)
local km = node.keymap
if not km or Util.is_nop(km.rhs) or node.keys:sub(1, 6) == "<Plug>" then
if not km or Util.is_nop(km.rhs) or node.keys:sub(1, 6) == "<Plug>" or node.op then
return
end
if node.keymap and node:count() > 0 then
if km and node:count() > 0 then
local id = mode.mode .. ":" .. node.keys
if reported[id] then
return
Expand Down
18 changes: 13 additions & 5 deletions lua/which-key/init.lua
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
---@class wk
---@field private _queue {spec: wk.Spec, opts?: wk.Parse}[]
---@field mappings {spec: wk.Spec, opts?: wk.Parse}[]
---@field triggers wk.Spec[]
local M = {}

M._queue = {}
M.mappings = {}
M.triggers = {}
M.did_setup = false

--- Open which-key
---@param opts? wk.Filter|string
function M.show(opts)
opts = opts or {}
opts = type(opts) == "string" and { keys = opts } or opts
opts = type(opts) == "string" and { keys = opts } or opts --[[@as wk.Filter]]
if opts.delay == nil then
opts.delay = 0
end
opts.waited = vim.o.timeoutlen
---@diagnostic disable-next-line: param-type-mismatch
if not require("which-key.state").start(opts) then
require("which-key.util").warn(
"No mappings found for mode `" .. (opts.mode or "n") .. "` and keys `" .. (opts.keys or "") .. "`"
Expand Down Expand Up @@ -46,7 +47,14 @@ end
---@param mappings wk.Spec
---@param opts? wk.Parse
function M.add(mappings, opts)
table.insert(M._queue, { spec = mappings, opts = opts })
-- Is replaced in `require("which-key.config").setup`
table.insert(M.mappings, { spec = mappings, opts = opts })
end

---@param triggers wk.Spec
function M.add_triggers(triggers)
-- Is replaced in `require("which-key.config").setup`
table.insert(M.triggers, triggers)
end

return M
Loading