Vim has the concept of actions, which can act on text objects via motions.
For example diw
will delete (the action) the inner word (the motion).
I want to create a function that can be applied/executed with those motions. For example, if my function appends to certain register, I can append whatever I want by doing myFunction + motion for what I want to add
.
2 Answers
Scripting language does not matter. The help for mappings is in
:h map.txt
. Specific topic is:h :map-operator
.There is
g@
normal operator andopfunc
global string option.In order to perform "custom action" we must
Make sure
opfunc
is set to the name of appropriate VimScript callable object;Execute
g@
in normal mode (that triggers the transition into operator-pending mode);Make sure Vim is left hanging in operator-pending mode;
After operator-pending mode is done
g@
saves the selected range to the "brackets" bookmark and invokesopfunc
value passing an extra string parameter.The quick and dumb test is
set opfunc=v:lua.print
and then typeg@aw
(prints "char"),g@as
(prints "line"),g@^VG
(prints "block") and so on.As we probably want more than one user-defined operator, we should pack all of that 1-2-3 into a new mapping.
Implement actual
opfunc
function to do something useful.
BTW. appending text to a register is a builtin. Like "Ayiw
etc.
UPD. Some example implementation for 1-2-3:
By :h :map-expression
function! Gat(method) abort
let &opfunc = a:method
return "g@"
endfunction
nnoremap <expr><f12> Gat("v:lua.print")
By :h :map-cmd
nnoremap <f12> <cmd>set opfunc=v:lua.print<CR>g@
-
Seems that some logic to setup a global lua function and also restore the previous opfunc is required. Can you also probide an example of 6? A mapping that performs the correct sequence to call opfunc? May 11, 2022 at 7:45
-
@Danielo515 You don't need to restore anything as long as you don't use
g@
directly. As for the mapping, either:map-expression
or:map-cmd
wil do. A basic example is under:h :map-operator
mentioned above. If you want to explore somewhat advanced use case check this plugin.– MattMay 11, 2022 at 8:39 -
Does you new answer require the lua function to be globally available? May 12, 2022 at 17:15
-
-
because you are just referencing a globally available lua function (print) I think your solution requires the function to use to be global May 13, 2022 at 13:14
As the other answer from Matt has pointed out, it is required to modify the opfunc to point to a global callable object and then call the g@
+ motion keymap. This is even suggested by the vim docs, where they create a mapping where both the opfunc is set and then the g@
motion is called on the same command.
This seems to be the closer you can get using lua until they provide a better lua integration
function format_range_operator()
local old_func = vim.go.operatorfunc -- backup previous reference
-- set a globally callable object/function
_G.op_func_formatting = function()
-- the content covered by the motion is between the [ and ] marks, so get those
local start = vim.api.nvim_buf_get_mark(0, '[')
local finish = vim.api.nvim_buf_get_mark(0, ']')
vim.lsp.buf.range_formatting({}, start, finish)
vim.go.operatorfunc = old_func -- restore previous opfunc
_G.op_func_formatting = nil -- deletes itself from global namespace
end
vim.go.operatorfunc = 'v:lua.op_func_formatting'
vim.api.nvim_feedkeys('g@', 'n', false)
end
vim.api.nvim_set_keymap("n", "gm", "<cmd>lua format_range_operator()<CR>", {noremap = true})
Picked from: https://github.com/neovim/nvim-lspconfig/wiki/User-contributed-tips#range-formatting-with-a-motion
-
Don't use
feedkeys
. This is too dirty and (almost) never needed. Also, creating two functions instead of one and then deleting one of them... This makes no sense.– MattMay 11, 2022 at 8:48 -
1Can you explain why it is dirty to use feedkeys? what is the alternative? The creation of two functions is required so you call the normal function from a command and that function does all the environment setup before calling the required mappings. I will be happy to see a cleaner implementation if you know one May 11, 2022 at 9:22
-
:h operator
,:h 'operatorfunc'
and:h :map-operator
. I think there is all the information you need there. If not you should try to implement what you want to do and edit your question with actual issues you're encountering.