which-key.nvim icon indicating copy to clipboard operation
which-key.nvim copied to clipboard

bug: which-key does not show up in visual mode and prevents key mappings from working

Open j-xella opened this issue 1 year ago • 7 comments

Did you check docs and existing issues?

  • [X] I have read all the which-key.nvim docs
  • [X] I have searched the existing issues of which-key.nvim
  • [X] I have searched the existing issues of plugins related to this issue

Neovim version (nvim -v)

v0.10.0-dev-273+gc8ebb04e9

Operating system/version

redhat

Describe the bug

I have a buffer mapping (for gitsigns), that is set like this:

vim.keymap.set( {'n', 'v'}, '<leader>hs', ':Gitsigns stage_hunk<CR>', { buffer = 123 } )

My leader key is the default \. I can see the mapping as registered if I type :vmap or map, both for normal and visual modes. The mapping works as expected in normal mode.

However, if I try executing this mapping in visual mode, it does not work as expected. Upon closer inspection, it seems that when I type the leader key, neovim waits for futher input. I guess that after 'timeout' which-key is supposed to take over and display the hint window. It tries to take over, but nothing is displayed. However, neovim also stops waiting for furhter key presses, meaning that I am back to visual mode, unable to execute my keymaps.

So, unless I am really really fast, pressing <leader>hs keymap results in:

<leader> - very quickly gets discarded h - move left s - whatever s does by itself ...

Steps To Reproduce

  1. Enter visual mode
  2. Press the leader key

Expected Behavior

A which-key pop-up appears at the bottom with descriptions of which keys can be pressed now, etc...

Repro

-- Minimal config:

vim.o.timeoutlen = 300

require("which-key").setup {
  -- your configuration comes here
  -- or leave it empty to use the default settings
}

j-xella avatar May 11 '23 12:05 j-xella

This is happening also to me with all visual commands

SergioQuijanoRey avatar Oct 08 '23 10:10 SergioQuijanoRey

Same here

dam9000 avatar Nov 28 '23 01:11 dam9000

A weird work-around: running the command: :WhichKey <leader> v makes the visual mode leader-key sequences working, including showing of the which-key menu, although only for the current buffer.

dam9000 avatar Nov 28 '23 02:11 dam9000

Ok, I got the visual<leader>hs to work, here's the bit of which-key config that enables the visual mode <leader> keys to work properly:

  require('which-key').register({
    ['<leader>'] = { name = 'VISUAL <leader>' },
    ['<leader>h'] = { 'Git [H]unk' },
  }, { mode = "v", })

dam9000 avatar Dec 03 '23 12:12 dam9000

@SergioQuijanoRey , @j-xella can you check if the above snippet fixes your issue? This should probably be added to the README or integrated in the default config. This is the minimal required config to get visual leader work with which-key:

  require('which-key').register({
    ['<leader>'] = { name = 'VISUAL <leader>' },
  }, { mode = "v" })

dam9000 avatar Dec 03 '23 12:12 dam9000

The last example you provided does not fix my issue. So I will provide my workaround in case someone finds it useful. I have a lua/myconf/aux.lua module where I define the following util functions:


-- Methods we are going to export
local M = {}

-- Libraries that we are going to use
local wk = require("which-key")

--- Function to set maps. This is handy because the api for this might change
--- (nvim is not quite stable yet), and thus we can have single change affect
--- all maps
---
--- NOTE: also useful because we are using third party libraries (whichkey) for
--- setting the keymaps, and this might change in the future
---
--- Example: `setmap("n", "<Tab>", ":echo hello", {noremap = True}, 'Just print hello world')`
function M.setmap(mode, keymap, command, opts, description)
    -- NOTE -- whichkey does not work well in visual mode
    --      -- see https://github.com/folke/which-key.nvim/issues/458
    --      -- So in this case register this command also with stdlib map
    if mode == "v" then
        M.stdlib_map(mode, keymap, command, opts, description)
        return
    end

    -- Sanitize input
    description = description or ""
    if opts == nil then
        opts = {}
    end

    -- WhichKey puts the mode inside the opts table
    local myopts = opts
    myopts.mode = mode

    -- Construct the whichkey mapping
    local mapping = {
        [keymap] = { command, description }
    }

    wk.register({ mapping, myopts })
end

--- Define a group name for a set of mappings using whichkey
function M.setmap_group_name(keymap, groupname)
    wk.register({
        [keymap] = { name = groupname }
    })
end

--- VIM way of setting maps. This is used for cases where whichkey cannot set
--- properly some maps in `M.setmap`
--- NOTE: `description` is not used, so don't hesitate to put proper values there
function M.stdlib_map(mode, keymap, command, opts, description)
    vim.api.nvim_set_keymap(mode, keymap, command, opts)
end

return M

With this, I can set keybindings and groups of keybindings like:

--- Avoid long lines for the set command
local setmap = require("myconf/aux").setmap
local setmap_group_name = require("myconf/aux").setmap_group_name

-- Whole block identation
setmap("v", "<", "<gv", { noremap = true }, "Ident block left")
setmap("v", ">", ">gv", { noremap = true }, "Ident block right")

-- File tree manipulation
setmap_group_name("<leader>o", "File tree management")
setmap("n", "<leader>oo", ":NvimTreeToggle<CR>", {}, "Nvim tree")
setmap("n", "<leader>oO", ":Oil<CR>", {}, "Nvim tree")
setmap("n", "<leader>-", ":Oil .<CR>", {}, "Nvim tree")

I did this in the first place because I didn't want to be dependent on whichkey. For example, I wanted to be able to roll back to normal nvim maps anytime, or be able to make a quick change to another / newer plugin. But having this setup, I can easily "correct" the buggy behaviour. Hope it helps.

SergioQuijanoRey avatar Dec 03 '23 15:12 SergioQuijanoRey

kinda fixed this by adding a visual mode mapping and a normal mode mappings: should probably add this info to the README

wk.register({
  h = {
    name = "Gitsigns",
    j = { "<cmd>lua require('gitsigns').next_hunk()<cr>", "next Line", mode = { 'n' } },
    k = { "<cmd>lua require('gitsigns').prev_hunk()<cr>", "prev Line", mode = { 'n' } },
    s = { "<cmd>lua require('gitsigns').stage_hunk()<cr>", "Stage Hunk", mode = { 'n' } },
    u = { "<cmd>lua require('gitsigns').undo_stage_hunk()<cr>", "Undo Stage Hunk", mode = { 'n' } },
  },
}, { prefix = "<leader>" })

wk.register({
  h = {
    s = {function() gitsigns.stage_hunk {vim.fn.line('.'), vim.fn.line('v')} end, "Stage Hunk visual mode", mode = { 'v' }},
    u = {function() gitsigns.reset_hunk {vim.fn.line('.'), vim.fn.line('v')} end, "reset hunk visual mode", mode = { 'v' }},
  },
}, { prefix = "<leader>", mode = "v" })

danielreis1 avatar Jun 11 '24 18:06 danielreis1