diff --git a/README.md b/README.md index c69007c..c091aa9 100644 --- a/README.md +++ b/README.md @@ -80,8 +80,9 @@ Code yanks are syntax-highlighted when ClipRing can detect a language (markdown | `dd` | Delete the selected entry from history | | `C` | Clear all entries (asks for confirmation: `y` yes, `n` cancel) | | `q` or `` | Close without pasting (`Esc` cancels a pending clear-all first) | +| `g?` / `` | Show picker key cheat-sheet (**only when [which-key.nvim](https://github.com/folke/which-key.nvim) is installed**) | -While the picker is focused, `` does not switch windows or open which-key (close the picker first, like Telescope). Keys apply to the history list; the preview pane is read-only. If you use [which-key.nvim](https://github.com/folke/which-key.nvim), `setup()` disables which-key on the history list buffer (`clipring` filetype). +When which-key is installed, `g?` and `` open a scoped which-key popup for this buffer. Without which-key, `` is disabled in the picker so you do not accidentally switch windows (close the picker first). Keys apply to the history list; the preview pane is read-only. ### Paste behavior by mode @@ -116,6 +117,7 @@ require("clipring").setup({ reorder_up_mapping = "", copy_mapping = "y", clear_all_mapping = "C", + help_mapping = "g?", -- which-key cheat-sheet (`g?`); requires which-key.nvim -- Layout (list position is fixed; preview auto-sizes within these limits) picker_width = 80, -- total inner width; 0 = nearly full editor width @@ -129,7 +131,7 @@ require("clipring").setup({ **`open_mapping`** — set a string (e.g. `"y"`) or multiple (`{ "y", "" }`) to open ClipRing from Normal, Visual, and Insert. Leave unset or `nil` to use only `:ClipRing`. Use `false` to clear a keymap after a previous `setup()`. -Omit `reorder_down_mapping` / `reorder_up_mapping` / `copy_mapping` / `clear_all_mapping` to keep the defaults above. Set any of them to `false` to turn off that binding. +Omit `reorder_down_mapping` / `reorder_up_mapping` / `copy_mapping` / `clear_all_mapping` / `help_mapping` to keep the defaults above. Set any of them to `false` to turn off that binding. `help_mapping` and `` cheat-sheet bindings apply only when which-key.nvim is installed. During clear-all confirmation, `y` confirms (same key as `copy_mapping` when not confirming) and `n` cancels. @@ -175,7 +177,7 @@ Coverage today: - **ring** — add, dedupe, max size, remove, reorder - **preview_syntax** — fence stripping, language detection, heuristics - **paste** — visual capture (`v` / `'<`), charwise replace vs append, insert-mode paste at saved cursor -- **ui** — picker from insert, navigation, wrap-around selection, fixed list layout, preview resize/restore, conditional preview, clear all with confirmation, syntax highlighting, clipboard copy, which-key / `` behavior +- **ui** — picker from insert, navigation, wrap-around selection, fixed list layout, preview resize/restore, conditional preview, clear all with confirmation, syntax highlighting, clipboard copy, which-key cheat-sheet (`g?` / ``) - **yank** — `TextYankPost` capture - **setup** — `open_mapping` registration diff --git a/lua/clipring/config.lua b/lua/clipring/config.lua index 4b75d50..e329076 100644 --- a/lua/clipring/config.lua +++ b/lua/clipring/config.lua @@ -12,6 +12,7 @@ local M = {} ---@field reorder_up_mapping string|false|nil move selected entry up in picker (default ``) ---@field copy_mapping string|false|nil copy selected entry to system clipboard in picker (default `y`) ---@field clear_all_mapping string|false|nil clear entire history in picker with confirmation (default `C`) +---@field help_mapping string|false|nil which-key cheat-sheet key in picker (default `g?`; only when which-key is installed) ---@field picker_width number total inner width of list + preview (`0` = nearly full editor width) ---@field list_width number width of the history list (`0` = auto; fixed width is advanced) ---@field preview_max_width number max preview width (`0` = content width up to screen edge) @@ -31,6 +32,7 @@ M.defaults = { reorder_up_mapping = "", copy_mapping = "y", clear_all_mapping = "C", + help_mapping = "g?", picker_width = 80, list_width = 0, preview_max_width = 0, diff --git a/lua/clipring/init.lua b/lua/clipring/init.lua index 5c1f4ab..f7c7d0d 100644 --- a/lua/clipring/init.lua +++ b/lua/clipring/init.lua @@ -2,7 +2,6 @@ local config = require("clipring.config") local yank = require("clipring.yank") local persist = require("clipring.persist") local ui = require("clipring.ui") -local which_key = require("clipring.which_key") local M = {} @@ -52,7 +51,6 @@ end function M.setup(opts) config.setup(opts) - which_key.setup() yank.setup() persist.setup() apply_open_mapping(config.get().open_mapping) diff --git a/lua/clipring/ui.lua b/lua/clipring/ui.lua index e4588f8..cf9b227 100644 --- a/lua/clipring/ui.lua +++ b/lua/clipring/ui.lua @@ -4,6 +4,7 @@ local paste = require("clipring.paste") local persist = require("clipring.persist") local preview_syntax = require("clipring.preview_syntax") local preview_highlight = require("clipring.preview_highlight") +local which_key = require("clipring.which_key") local M = {} @@ -734,8 +735,22 @@ local function attach_keymaps() local function block_window_prefix() return end - map("", block_window_prefix, "ClipRing: disable window switch") - map("", block_window_prefix, "ClipRing: disable window switch") + + if which_key.available() then + local function show_picker_help() + which_key.show_help() + end + + local help = picker_mapping("help_mapping", "g?") + if help then + map(help, show_picker_help, "ClipRing: show keymaps") + end + map("", show_picker_help, "ClipRing: show keymaps") + map("", show_picker_help, "ClipRing: show keymaps") + else + map("", block_window_prefix, "ClipRing: disable window switch") + map("", block_window_prefix, "ClipRing: disable window switch") + end end ---@param opts table|nil diff --git a/lua/clipring/which_key.lua b/lua/clipring/which_key.lua index 0076157..de93fe4 100644 --- a/lua/clipring/which_key.lua +++ b/lua/clipring/which_key.lua @@ -1,16 +1,18 @@ local M = {} ---- Disable which-key in ClipRing picker buffers (same idea as TelescopePrompt). -function M.setup() - local ok, wk_config = pcall(require, "which-key.config") - if not ok or not wk_config.disable or not wk_config.disable.ft then +---@return boolean +function M.available() + local ok, wk = pcall(require, "which-key") + return ok and type(wk.show) == "function" +end + +---Show a cheat-sheet of the current buffer's mappings via which-key. +---No-op when which-key is not installed. +function M.show_help() + if not M.available() then return end - for _, ft in ipairs({ "clipring", "clipring_preview" }) do - if not vim.tbl_contains(wk_config.disable.ft, ft) then - table.insert(wk_config.disable.ft, ft) - end - end + require("which-key").show({ global = false }) end return M diff --git a/tests/setup_spec.lua b/tests/setup_spec.lua index 3d68dc0..9870700 100644 --- a/tests/setup_spec.lua +++ b/tests/setup_spec.lua @@ -18,15 +18,6 @@ describe("clipring.setup", function() assert.are_not.equal("", vim.fn.maparg("", "n")) end) - it("registers clipring filetype with which-key disable when installed", function() - local ok, wk_config = pcall(require, "which-key.config") - if not ok then - return - end - require("clipring.which_key").setup() - assert.is_true(vim.tbl_contains(wk_config.disable.ft, "clipring")) - end) - it("clears open_mapping when setup sets open_mapping to false", function() clipring.setup({ open_mapping = "cr" }) clipring.setup({ open_mapping = false }) diff --git a/tests/ui_spec.lua b/tests/ui_spec.lua index bed52e3..7e333fd 100644 --- a/tests/ui_spec.lua +++ b/tests/ui_spec.lua @@ -36,16 +36,33 @@ describe("clipring.ui", function() return clip_buf end - it("maps Ctrl-w without a which-key trigger on the picker buffer", function() + it("maps Ctrl-w to block window switch when which-key is unavailable", function() + local which_key = require("clipring.which_key") + if which_key.available() then + return + end ui.open() local clip_buf = h.find_clipring_buf() vim.api.nvim_set_current_win(vim.fn.bufwinid(clip_buf)) local map = vim.fn.maparg("", "n", false, true) assert.is_true(type(map) == "table") assert.is_true(map.callback ~= nil) - if map.desc then - assert.is_nil(map.desc:find("which%-key%-trigger", 1, true)) + assert.are.equal("ClipRing: disable window switch", map.desc) + assert.are.equal("", vim.fn.maparg("g?", "n")) + ui.close() + end) + + it("maps Ctrl-w to show keymaps when which-key is installed", function() + local which_key = require("clipring.which_key") + if not which_key.available() then + return end + ui.open() + local clip_buf = h.find_clipring_buf() + vim.api.nvim_set_current_win(vim.fn.bufwinid(clip_buf)) + local map = vim.fn.maparg("", "n", false, true) + assert.are.equal("ClipRing: show keymaps", map.desc) + ui.close() end) it("blocks Ctrl-w window switch while picker is focused", function() diff --git a/tests/which_key_spec.lua b/tests/which_key_spec.lua new file mode 100644 index 0000000..c3bb15e --- /dev/null +++ b/tests/which_key_spec.lua @@ -0,0 +1,24 @@ +local h = require("tests.helpers") +local which_key = require("clipring.which_key") + +describe("clipring.which_key", function() + before_each(function() + h.reset() + end) + + it("reports whether which-key is installed", function() + assert.is_boolean(which_key.available()) + end) + + it("show_help is a no-op when which-key is unavailable", function() + if which_key.available() then + return + end + local buf = vim.api.nvim_create_buf(false, true) + vim.api.nvim_set_current_buf(buf) + assert.has_no.errors(function() + which_key.show_help() + end) + vim.api.nvim_buf_delete(buf, { force = true }) + end) +end)