Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Working example for neovim (custom text object with repeat) #92

Open
JoseConseco opened this issue May 11, 2022 · 2 comments
Open

Working example for neovim (custom text object with repeat) #92

JoseConseco opened this issue May 11, 2022 · 2 comments

Comments

@JoseConseco
Copy link

JoseConseco commented May 11, 2022

I had trouble to make vim-repeat work in neovim with lua, but finally here it is:

--  tpope/vim-repeat - allows . repeat
vim.api.nvim_create_user_command( 'BigInnerWord',
    function() get_big_word('i') end,
    {}
)
vim.fn['repeat#set'](":BigInnerWord"..t'<CR>')  -- the vim-repeat magic
vim.keymap.set('o', 'W', ':BigInnerWord'..t'<CR>') 

t() function is just nvim helper:

local function t(str)
    return vim.api.nvim_replace_termcodes(str, true, true, true)
end 

All you have to do is to write your custom : get_big_word('i') - function - in my case I wanted to write custom big WORD text object.
I guess it should work for any custom lua functions, not just o .
Hopefully people will find this useful.

@JoseConseco JoseConseco changed the title Working example for custom text object with repeat Working example for neovim (custom text object with repeat) May 11, 2022
@dongdongbh
Copy link

Great, how to do this with custom key maping?

@wookayin
Copy link

wookayin commented Nov 26, 2023

A general recipe & usage for neovim (Lua)

You can use the following wrapper to conveniently wrap a rhs to be passed to vim.keymap.set:

---Register a global internal keymap that wraps `rhs` to be repeatable.
---@param mode string|table keymap mode, see vim.keymap.set()
---@param lhs string lhs of the internal keymap to be created, should be in the form `<Plug>(...)`
---@param rhs string|function rhs of the keymap, see vim.keymap.set()
---@return string The name of a registered internal `<Plug>(name)` keymap. Make sure you use { remap = true }.
local make_repeatable_keymap = function (mode, lhs, rhs)
  vim.validate {
    mode = { mode, { 'string', 'table' } },
    rhs = { rhs, { 'string', 'function' },
    lhs = { name = 'string' } }
  }
  if not vim.startswith(lhs, "<Plug>") then
    error("`lhs` should start with `<Plug>`, given: " .. lhs)
  end
  vim.keymap.set(mode, lhs, function()
    rhs()
    vim.fn['repeat#set'](vim.api.nvim_replace_termcodes(lhs, true, true, true))
  end)
  return lhs
end

(Note: this does not implement opts for such as buffer, nowait, silent, etc., but it should be straightforward to extend)

Example: If you want to make a keymap <leader>tc mapped to a lua function (or any keymap sequence string) repeatable:

vim.keymap.set('n', '<leader>tc', make_repeatable_keymap('n', '<Plug>(ToggleComment)', function()
   -- the actual body (rhs) goes here: some complex logic that you want to repeat
end, { remap = true })

This will:

  • Create an internal keymap <Plug>(ToggleComment) that does the job to make the internal keymap repeatable.
  • Map <leader>tc to <Plug>(ToggleComment), which will execute the desired body (rhs).
  • Note: Make sure you use { remap = true }.

To see a working example in the wild, please see my dotfiles: wookayin/dotfiles@3fe6e68. See how complicated it was to write things in vimscript with dirty string manipulation; in Lua, functional programming makes config more concise!

More detailed explanation and breakdown

So in order to make vim-repeat work, in general one should have a keymap (or a command) like:

  1. BEGIN: A keymap (or command) starts, say <Plug>(MyKeymap).
  2. (body -- what is going to be repeated; this is the body (rhs) of the keymap)
  3. END: call repeat#set("<Plug>(MyKeymap)") to mark the keymap sequence to repeat by ..
    • Note this is just implemented by feedkeys under the hood -- so need to use nvim_replace_termcodes; also an anonymous function cannot be used.

For keymap, it is very recommended (although not mandatory) to use <Plug> to create an internal mapping, because this will make key repeat much, much easier to write and understand.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants