diff --git a/README.md b/README.md index 35f3d114..87f1249c 100644 --- a/README.md +++ b/README.md @@ -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 ~= "" @@ -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 @@ -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 @@ -319,7 +321,7 @@ Both can be configured using `opts.triggers` and `opts.defer`. By default `opts.triggers` includes `{ "", mode = "nixsotc" }`, which will setup keymap triggers for every mode automatically and will trigger during -`ModeChanged`. +`ModeChanged`. After setup you can add additional triggers with `wk.add_triggers()` > [!NOTE] > Auto triggers will never be created for existing keymaps. diff --git a/doc/which-key.nvim.txt b/doc/which-key.nvim.txt index 240dd3a1..102d1d09 100644 --- a/doc/which-key.nvim.txt +++ b/doc/which-key.nvim.txt @@ -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 ~= "" @@ -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 @@ -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 @@ -341,7 +343,7 @@ Both can be configured using `opts.triggers` and `opts.defer`. By default `opts.triggers` includes `{ "", mode = "nixsotc" }`, which will setup keymap triggers for every mode automatically and will trigger during -`ModeChanged`. +`ModeChanged`. After setup you can add additional triggers with `wk.add_triggers()` [!NOTE] Auto triggers will never be created for existing keymaps. That includes @@ -464,7 +466,7 @@ their default link. ----------------------------------------------------------------------- Highlight Group Default Group Description ----------------------- ----------------------- ----------------------- - WhichKey Function + WhichKey Function WhichKeyBorder FloatBorder Border of the which-key window @@ -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 diff --git a/lua/which-key/buf.lua b/lua/which-key/buf.lua index d1f2ba29..cb7829f0 100644 --- a/lua/which-key/buf.lua +++ b/lua/which-key/buf.lua @@ -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 @@ -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 diff --git a/lua/which-key/config.lua b/lua/which-key/config.lua index 474a2943..3a36c018 100644 --- a/lua/which-key/config.lua +++ b/lua/which-key/config.lua @@ -1,5 +1,4 @@ ---@class wk.Config: wk.Opts ----@field triggers {mappings: wk.Mapping[], modes: table} local M = {} M.version = "3.17.0" -- x-release-please-version @@ -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 ~= "" @@ -188,6 +187,12 @@ M.options = nil ---@type {opt:string, msg:string}[] M.issues = {} +---@type {mappings: wk.Mapping[], modes: table} +M.triggers = { + mappings = {}, + modes = {}, +} + function M.validate() local deprecated = { ["operators"] = "see `opts.defer`", @@ -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 @@ -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") @@ -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 == "" 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) @@ -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, { @@ -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 == "" 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 diff --git a/lua/which-key/health.lua b/lua/which-key/health.lua index 61b38aa6..3e221f4d 100644 --- a/lua/which-key/health.lua +++ b/lua/which-key/health.lua @@ -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() @@ -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) == "" then + if not km or Util.is_nop(km.rhs) or node.keys:sub(1, 6) == "" 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 diff --git a/lua/which-key/init.lua b/lua/which-key/init.lua index 5387f982..9a8eaff5 100644 --- a/lua/which-key/init.lua +++ b/lua/which-key/init.lua @@ -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 "") .. "`" @@ -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 diff --git a/lua/which-key/mappings.lua b/lua/which-key/mappings.lua index 7196aa3a..98222fda 100644 --- a/lua/which-key/mappings.lua +++ b/lua/which-key/mappings.lua @@ -49,6 +49,7 @@ M.fields = { real = { inherit = true }, proxy = {}, expand = {}, + op = {}, -- deprecated name = { transform = "group", deprecated = true }, prefix = { inherit = true, deprecated = true }, @@ -87,7 +88,7 @@ end ---@param field string|number ---@param types string|string[] function M.expect(spec, field, types) - types = type(types) == "string" and { types } or types + types = type(types) == "string" and { types } or types --[[@as string[] ]] local ok = false for _, t in ipairs(types) do if type(spec[field]) == t then @@ -109,6 +110,7 @@ function M._parse(spec, ret, opts) opts.version = opts.version or M.VERSION if spec.version then opts.version = spec.version + ---@diagnostic disable-next-line: inject-field spec.version = nil end @@ -126,6 +128,7 @@ function M._parse(spec, ret, opts) spec = type(spec) == "string" and { desc = spec } or spec ---@type wk.Mapping + ---@diagnostic disable-next-line: missing-fields local mapping = {} ---@type wk.Spec[] @@ -157,8 +160,10 @@ function M._parse(spec, ret, opts) if opts.version == 1 then if M.expect(spec, k, { "string", "table" }) then if type(v) == "string" then + ---@diagnostic disable-next-line: undefined-field table.insert(children, { prefix = (spec.prefix or "") .. k, desc = v }) elseif type(v) == "table" then + ---@diagnostic disable-next-line: inject-field, undefined-field v.prefix = (spec.prefix or "") .. k table.insert(children, v) end @@ -168,6 +173,7 @@ function M._parse(spec, ret, opts) end elseif type(k) == "number" and type(v) == "table" then if opts.version == 1 then + ---@diagnostic disable-next-line: inject-field, undefined-field v.prefix = spec.prefix or "" end table.insert(children, v) @@ -268,6 +274,7 @@ function M.add(mapping, ret, opts) if not mapping.lhs then return end + ---@diagnostic disable-next-line: inject-field mapping.prefix = nil local has_desc = mapping.desc ~= nil @@ -314,7 +321,12 @@ function M.parse(spec, opts) if m.rhs and opts.create then M.create(m) end + if m.proxy then + -- delete already existing keymaps for proxies + pcall(vim.keymap.del, m.mode, m.lhs) + end end + return ret end diff --git a/lua/which-key/migrate.lua b/lua/which-key/migrate.lua index 81eb51ed..80f76db6 100644 --- a/lua/which-key/migrate.lua +++ b/lua/which-key/migrate.lua @@ -21,7 +21,7 @@ function M.migrate(spec) m.silent = nil end if m.group then - m.group = m.desc + m.group = m.desc ---@type string m.desc = nil end local id = vim.inspect(m) diff --git a/lua/which-key/node.lua b/lua/which-key/node.lua index f7976714..46a6e4bf 100644 --- a/lua/which-key/node.lua +++ b/lua/which-key/node.lua @@ -24,10 +24,17 @@ function M.new(parent, key) return self end +function M:is_nowait() + local nowait = self.keymap and self.keymap.nowait + + -- lua equates 0 to true, so... + return nowait == true or nowait == 1 +end + function M:has_nowait_ancestor() local node = self while node do - if node.keymap and node.keymap.nowait then + if node:is_nowait() then return true end node = node.parent @@ -107,7 +114,7 @@ function M:is_plugin() end function M:can_expand() - return self.plugin or (self.mapping and (self.mapping.proxy or self.mapping.expand)) + return self.plugin or (self.mapping and (self.mapping.proxy or self.mapping.expand or self.mapping.op)) end ---@return wk.Node[] @@ -139,7 +146,7 @@ function M:expand() end -- proxy mappings - local proxy = self.mapping.proxy + local proxy = self.mapping and self.mapping.proxy if proxy then local keys = Util.keys(proxy) local root = self:root() @@ -151,6 +158,35 @@ function M:expand() end end + -- operator mappings + if self.mapping and self.mapping.op then + local Buf = require("which-key.buf") + local mode = Buf.get({ mode = "o" }) ---@type wk.Mode? + local root = mode and mode.tree.root + + if root then + for k, v in pairs(root:expand()) do + v = vim.deepcopy(v) + v.parent = self + + local queue = { v } + + while #queue > 0 do + local node = table.remove(queue, 1) ---@type wk.Node + + node.keys = node.parent.keys .. node.key + node.path = vim.list_extend(vim.deepcopy(node.parent.path), { node.path[#node.path] }) + + for _, child in pairs(node._children) do + queue[#queue + 1] = child + end + end + + ret[k] = v + end + end + end + -- expand mappings local expand = self.mapping and self.mapping.expand if expand then @@ -182,7 +218,7 @@ end function M:root() local node = self while node.parent do - node = node.parent + node = node.parent or {} end return node end diff --git a/lua/which-key/plugins/init.lua b/lua/which-key/plugins/init.lua index 1c2359e6..aa7fe226 100644 --- a/lua/which-key/plugins/init.lua +++ b/lua/which-key/plugins/init.lua @@ -1,5 +1,4 @@ local Config = require("which-key.config") -local Util = require("which-key.util") local M = {} diff --git a/lua/which-key/plugins/presets.lua b/lua/which-key/plugins/presets.lua index 109bb357..80fa2c74 100644 --- a/lua/which-key/plugins/presets.lua +++ b/lua/which-key/plugins/presets.lua @@ -113,6 +113,7 @@ M.windows = { { "k", desc = "Go to the up window" }, { "l", desc = "Go to the right window" }, { "o", desc = "Close all other windows" }, + { "c", desc = "Close current window" }, { "q", desc = "Quit a window" }, { "s", desc = "Split window" }, { "v", desc = "Split window vertically" }, @@ -161,20 +162,20 @@ M.nav = { { "H", desc = "Home line of window (top)" }, { "L", desc = "Last line of window" }, { "M", desc = "Middle line of window" }, - { "[%", desc = "Previous unmatched group" }, - { "[(", desc = "Previous (" }, - { "[<", desc = "Previous <" }, - { "[M", desc = "Previous method end" }, - { "[m", desc = "Previous method start" }, - { "[s", desc = "Previous misspelled word" }, - { "[{", desc = "Previous {" }, + { "[%", desc = "Prev unmatched group" }, { "]%", desc = "Next unmatched group" }, - { "](", desc = "Next (" }, - { "]<", desc = "Next <" }, + { "[(", desc = "Prev (" }, + { "])", desc = "Next )" }, + { "[<", desc = "Prev <" }, + { "]>", desc = "Next >" }, + { "[{", desc = "Prev {" }, + { "]}", desc = "Next }" }, + { "[M", desc = "Prev method end" }, { "]M", desc = "Next method end" }, + { "[m", desc = "Prev method start" }, { "]m", desc = "Next method start" }, + { "[s", desc = "Prev misspelled word" }, { "]s", desc = "Next misspelled word" }, - { "]{", desc = "Next {" }, } M.g = { @@ -182,6 +183,7 @@ M.g = { { "g%", desc = "Cycle backwards through results" }, { "g,", desc = "Go to [count] newer position in change list" }, { "g;", desc = "Go to [count] older position in change list" }, + { "g@", desc = "Operator-pending mode" }, { "gN", desc = "Search backwards and select" }, { "gT", desc = "Go to previous tab page" }, { "gf", desc = "Go to file under cursor" }, @@ -193,27 +195,32 @@ M.g = { } function M.setup(opts) - local wk = require("which-key") + local Config = require("which-key.config") + local add = function(tbl) + -- Use vim.deepcopy so that the presets aren't modified later + -- and can be used for example in an `expand` function + Config.add(vim.deepcopy(tbl)) + end -- Operators if opts.operators then - wk.add(M.operators) + add(M.operators) end -- Motions if opts.motions then - wk.add(M.motions) + add(M.motions) end -- Text objects if opts.text_objects then - wk.add(M.text_objects) + add(M.text_objects) end -- Misc for _, preset in pairs({ "windows", "nav", "z", "g" }) do if opts[preset] ~= false then - wk.add(M[preset]) + add(M[preset]) end end end diff --git a/lua/which-key/plugins/registers.lua b/lua/which-key/plugins/registers.lua index 4464a4ef..c7b5ec3f 100644 --- a/lua/which-key/plugins/registers.lua +++ b/lua/which-key/plugins/registers.lua @@ -1,5 +1,3 @@ -local Util = require("which-key.util") - ---@diagnostic disable: missing-fields, inject-field ---@type wk.Plugin local M = {} diff --git a/lua/which-key/plugins/spelling.lua b/lua/which-key/plugins/spelling.lua index 15707d10..6c17b935 100644 --- a/lua/which-key/plugins/spelling.lua +++ b/lua/which-key/plugins/spelling.lua @@ -33,7 +33,7 @@ function M.expand() local word = bad[1] == "" and cursor_word or bad[1] ---@type string[] - local suggestions = vim.fn.spellsuggest(word, M.opts.suggestions or 20, bad[2] == "caps" and 1 or 0) + local suggestions = vim.fn.spellsuggest(word, M.opts.suggestions or 20, bad[2] == "caps") local items = {} ---@type wk.Plugin.item[] local keys = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" diff --git a/lua/which-key/state.lua b/lua/which-key/state.lua index 85617b21..fd3ada92 100644 --- a/lua/which-key/state.lua +++ b/lua/which-key/state.lua @@ -22,6 +22,7 @@ M.redraw_timer = uv.new_timer() ---@return boolean safe, string? reason function M.safe(mode_change) + ---@diagnostic disable-next-line: unused-local local old, _new = unpack(vim.split(mode_change, ":", { plain = true })) if old == "c" then return false, "command-mode" @@ -41,6 +42,7 @@ function M.setup() local group = vim.api.nvim_create_augroup("wk", { clear = true }) if Config.debug then + ---@diagnostic disable-next-line: unused-local vim.on_key(function(_raw, key) if key and #key > 0 then key = vim.fn.keytrans(key) @@ -62,7 +64,7 @@ function M.setup() end, }) - local hide = uv.new_timer() + local hide = assert(uv.new_timer()) vim.api.nvim_create_autocmd({ "FocusLost", "FocusGained" }, { group = group, callback = function(ev) @@ -144,16 +146,20 @@ function M.setup() group = group, callback = function(ev) current_buf = ev.buf ---@type number - Util.trace(ev.event .. "(" .. ev.buf .. ")") - Buf.get() - Util.trace() + + -- move to end of event loop to give time to set localleader mappings + vim.defer_fn(function() + Util.trace(ev.event .. "(" .. ev.buf .. ")") + Buf.get() + Util.trace() + end, 0) end, }) -- HACK: ModeChanged does not always trigger, so we need to manually -- check for mode changes. This seems to be due to the usage of `:norm` in autocmds. -- See https://github.com/folke/which-key.nvim/issues/787 - local timer = uv.new_timer() + local timer = assert(uv.new_timer()) timer:start(0, 50, function() local mode = Util.mapmode() -- check if the mode exists for the current buffer @@ -189,11 +195,13 @@ function M.check(state, key) if node then -- NOTE: a node can be both a keymap and a group - -- when it's both, we honor timeoutlen and nowait to decide what to do + -- honor 'timeoutlen' only if the keymap is not an operator local has_children = node:count() > 0 - local is_nowait = node.keymap and (node.keymap.nowait == 1 or not timedout) + local is_nowait = node:is_nowait() + local is_timedout = node.keymap and not timedout and not node.op local is_action = node.action ~= nil - if has_children and not is_nowait and not is_action then + + if has_children and not is_nowait and not is_timedout and not is_action then Util.debug("continue", node.keys, tostring(state.mode), node.plugin) return node end @@ -233,10 +241,15 @@ function M.execute(state, key, node) if vim.v.register ~= Util.reg() and state.mode.mode ~= "i" and state.mode.mode ~= "c" then keystr = '"' .. vim.v.register .. keystr end + + local curr_mode = vim.api.nvim_get_mode().mode + + if curr_mode:find("ni[IRV]") ~= nil then + keystr = "" .. keystr + end end Util.debug("feedkeys", tostring(state.mode), keystr) - local feed = vim.api.nvim_replace_termcodes(keystr, true, true, true) - vim.api.nvim_feedkeys(feed, "mit", false) + vim.api.nvim_feedkeys(Util.t(keystr), "mit", false) end function M.getchar() @@ -386,6 +399,7 @@ end ---@param opts {delay?:number, mode:string, keys:string, plugin?:string, waited?: number} function M.delay(opts) + ---@diagnostic disable-next-line: param-type-mismatch local delay = opts.delay or type(Config.delay) == "function" and Config.delay(opts) or Config.delay --[[@as number]] if opts.waited then delay = delay - opts.waited diff --git a/lua/which-key/tree.lua b/lua/which-key/tree.lua index c80f7693..045b0a24 100644 --- a/lua/which-key/tree.lua +++ b/lua/which-key/tree.lua @@ -23,7 +23,7 @@ end ---@param keymap wk.Mapping|wk.Keymap ---@param virtual? boolean function M:add(keymap, virtual) - if not Config.filter(keymap) then + if not Config.filter(keymap) or Util.is_nop(keymap.rhs) then return end local keys = Util.keys(keymap.lhs, { norm = true }) diff --git a/lua/which-key/triggers.lua b/lua/which-key/triggers.lua index 789da74a..b7597c1b 100644 --- a/lua/which-key/triggers.lua +++ b/lua/which-key/triggers.lua @@ -5,6 +5,7 @@ local Util = require("which-key.util") ---@field mode string ---@field keys string ---@field plugin? string +---@field op? boolean local M = {} M._triggers = {} ---@type table @@ -28,6 +29,10 @@ function M.is_mapped(trigger) if Util.is_nop(km.rhs) then return false end + -- ignore operator mappings + if trigger.op then + return false + end -- ignore which-key triggers if km.desc and km.desc:find("which-key-trigger", 1, true) then return false @@ -100,6 +105,7 @@ function M.update(mode, triggers) mode = mode.mode, keys = node.keys, plugin = node.plugin, + op = node.op, } local id = M.id(trigger) keep[id] = true diff --git a/lua/which-key/types.lua b/lua/which-key/types.lua index 8d985d82..2393c158 100644 --- a/lua/which-key/types.lua +++ b/lua/which-key/types.lua @@ -60,6 +60,7 @@ ---@field icon? wk.Icon|string ---@field proxy? string ---@field expand? fun():wk.Spec +---@field op? boolean ---@class wk.Spec: {[number]: wk.Spec} , wk.Mapping ---@field [1]? string diff --git a/lua/which-key/util.lua b/lua/which-key/util.lua index 0aa2b445..34c541cb 100644 --- a/lua/which-key/util.lua +++ b/lua/which-key/util.lua @@ -7,7 +7,10 @@ M.cache = { } function M.t(str) - M.cache.termcodes[str] = M.cache.termcodes[str] or vim.api.nvim_replace_termcodes(str, true, true, true) + if not M.cache.termcodes[str] then + M.cache.termcodes[str] = vim.api.nvim_replace_termcodes(str, true, true, true) + end + return M.cache.termcodes[str] end @@ -35,10 +38,10 @@ end --- Normalizes (and fixes) the lhs of a keymap ---@param lhs string function M.norm(lhs) - if M.cache.norm[lhs] then - return M.cache.norm[lhs] + if not M.cache.norm[lhs] then + M.cache.norm[lhs] = vim.fn.keytrans(M.t(lhs)) end - M.cache.norm[lhs] = vim.fn.keytrans(M.t(lhs)) + return M.cache.norm[lhs] end @@ -141,7 +144,7 @@ end ---@param fn F ---@return F function M.debounce(ms, fn) - local timer = (vim.uv or vim.loop).new_timer() + local timer = assert((vim.uv or vim.loop).new_timer()) return function(...) local args = { ... } timer:start( @@ -227,6 +230,7 @@ function M.debug(msg, ...) end local data = { ... } if #data == 0 then + ---@diagnostic disable-next-line: cast-local-type data = nil elseif #data == 1 then data = data[1] @@ -235,6 +239,7 @@ function M.debug(msg, ...) data = data() end if type(data) == "table" then + ---@diagnostic disable-next-line: cast-local-type data = table.concat( vim.tbl_map(function(value) return type(value) == "string" and value or vim.inspect(value):gsub("%s+", " ") @@ -243,6 +248,7 @@ function M.debug(msg, ...) ) end if data and type(data) ~= "string" then + ---@diagnostic disable-next-line: cast-local-type data = vim.inspect(data):gsub("%s+", " ") end msg = data and ("%s: %s"):format(msg, data) or msg @@ -284,7 +290,6 @@ end ---@generic T: table ---@param t T ---@param fields string[] ----@return T function M.getters(t, fields) local getters = {} ---@type table for _, prop in ipairs(fields) do @@ -298,6 +303,7 @@ function M.getters(t, fields) setmetatable(t, { __index = function(_, key) if getters[key] then + ---@diagnostic disable-next-line: redundant-parameter return getters[key](t) end end, diff --git a/lua/which-key/view.lua b/lua/which-key/view.lua index 29015b90..c4f8b7c7 100644 --- a/lua/which-key/view.lua +++ b/lua/which-key/view.lua @@ -5,7 +5,6 @@ local Layout = require("which-key.layout") local Plugins = require("which-key.plugins") local State = require("which-key.state") local Text = require("which-key.text") -local Tree = require("which-key.tree") local Util = require("which-key.util") local Win = require("which-key.win") @@ -230,6 +229,7 @@ function M.trail(node, opts) did_op = true local mode = Buf.get({ buf = State.state.mode.buf.buf, mode = "n" }) if mode then + ---@diagnostic disable-next-line: cast-local-type node = mode.tree:find(m == "x" and "v" or vim.v.operator) end end