From 5b0e381a261b1450735f4eddac55aeb956c13713 Mon Sep 17 00:00:00 2001 From: Josh Rahm Date: Sun, 21 Aug 2022 22:01:00 -0600 Subject: feat(userreg): Add user-defined registers to Neovim. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change unlocks additional registers for Neovim by allowing a user to define their own behavior for non-builtin registers. This is accopmlished through a new option 'userregfunc' The 'userregfunc' defines the function to call when handling a register for which there is no builtin functionality. The 'userregfunc' function should take 3 arguments: action - Either "yank" or "put" register - The character corresponding to the register content - In the case of action == "yank", the dictionary describing the yanked content, with the following keys: {type} - Either "char", "line" or "block" {lines} - The lines being yanked as a list {width} - The width in case of "block" mode. {additional_data} - Additional data (can be returned in "put" mode) In case of "put" this function should return the content to put. This content can be either: * A dictionary in the same template as content above. * A list of strings. This will be assumed to be "line" mode. * A string. This will be assumed to be "char" mode. An example of a "null" 'userregfunc' that provides an implementation identical to traditional vim registers would be: let s:contents = {} function! MyUserregFunction(action, register, content) abort if a:action == "put" return get(s:contents, a:register, "") else let s:contents[a:register] = a:content endif endfunction set userregfun=MyUserregFunction It is important to note that any valid unicode character can now be a register, including something like @☺. This change also addresses the multibyte parsing issues surrounding let @a = 'xyz' let @🔨 = 'hammer' --- runtime/doc/options.txt | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 2e0c1f8cc4..22d8d7fe1a 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6722,6 +6722,57 @@ A jump table for the options with a short description can be found at |Q_op|. written to disk (see |crash-recovery|). Also used for the |CursorHold| autocommand event. + *'userregfun'* *'urf'* +'userregfun' 'urf' string (default "") + global + The option specifies a function to be used to handle any registers + that Neovim does not natively handle. This option unlocks all + characters to be used as registers by the user. + + The 'userregfun' function is called each time a user register is read + from or written to. + + The 'userregfun' function must take the following parameters: + + {action} The action being done on this register (either 'yank' + or 'put' + + {register} The string holding the name of the register. This + is always a single character, though multi-byte + characters are allowed. + + {content} If the action is 'yank' this is the content being + yanked into the register. The content is a dictionary + with the following items: + + {lines} The lines being yanked, as a list. + + {type} The type of yank, either "line", "char", or + "block" + + {width} The width in case of "block" mode. + + {additional_data} Additional data. (can be returned in + put mode). + + In case the action is 'put', the 'userregfun' function should return + the content to place in that location. The content can either be a + string, in which case "char" mode is inferred, or it can return a + dictionary of the same template that populates 'content'. + + A very simple example of a 'userregfun' function that behaves exactly + like traditional registers would look like: > + + let s:contents = {} + function! MyUserregFunction(action, register, content) abort + if a:action == "put" + return get(s:contents, a:register, "") + else + let s:contents[a:register] = a:content + endif + endfunction + set userregfun=MyUserregFunction +< *'varsofttabstop'* *'vsts'* 'varsofttabstop' 'vsts' string (default "") local to buffer -- cgit From 716b7da69fd127eaec301c5d0c6620281eba1d98 Mon Sep 17 00:00:00 2001 From: Josh Rahm Date: Sun, 21 Aug 2022 22:19:09 -0600 Subject: feat(userreg): fix typos userregfun -> userregfunc --- runtime/doc/options.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 22d8d7fe1a..6637464460 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6722,17 +6722,17 @@ A jump table for the options with a short description can be found at |Q_op|. written to disk (see |crash-recovery|). Also used for the |CursorHold| autocommand event. - *'userregfun'* *'urf'* -'userregfun' 'urf' string (default "") + *'userregfunc'* *'urf'* +'userregfunc' 'urf' string (default "") global The option specifies a function to be used to handle any registers that Neovim does not natively handle. This option unlocks all characters to be used as registers by the user. - The 'userregfun' function is called each time a user register is read + The 'userregfunc' function is called each time a user register is read from or written to. - The 'userregfun' function must take the following parameters: + The 'userregfunc' function must take the following parameters: {action} The action being done on this register (either 'yank' or 'put' @@ -6755,12 +6755,12 @@ A jump table for the options with a short description can be found at |Q_op|. {additional_data} Additional data. (can be returned in put mode). - In case the action is 'put', the 'userregfun' function should return + In case the action is 'put', the 'userregfunc' function should return the content to place in that location. The content can either be a string, in which case "char" mode is inferred, or it can return a dictionary of the same template that populates 'content'. - A very simple example of a 'userregfun' function that behaves exactly + A very simple example of a 'userregfunc' function that behaves exactly like traditional registers would look like: > let s:contents = {} @@ -6771,7 +6771,7 @@ A jump table for the options with a short description can be found at |Q_op|. let s:contents[a:register] = a:content endif endfunction - set userregfun=MyUserregFunction + set userregfunc=MyUserregFunction < *'varsofttabstop'* *'vsts'* 'varsofttabstop' 'vsts' string (default "") -- cgit From 4c09d516549ece383146d3f26359ce1a2305359c Mon Sep 17 00:00:00 2001 From: Josh Rahm Date: Sun, 21 Aug 2022 22:19:51 -0600 Subject: feat(userreg): add runtime files for userreg --- runtime/autoload/userreg.vim | 7 ++++++ runtime/lua/vim/userreg.lua | 51 ++++++++++++++++++++++++++++++++++++++++++++ runtime/plugin/userreg.vim | 1 + 3 files changed, 59 insertions(+) create mode 100644 runtime/autoload/userreg.vim create mode 100644 runtime/lua/vim/userreg.lua create mode 100644 runtime/plugin/userreg.vim (limited to 'runtime') diff --git a/runtime/autoload/userreg.vim b/runtime/autoload/userreg.vim new file mode 100644 index 0000000000..fd026a12e6 --- /dev/null +++ b/runtime/autoload/userreg.vim @@ -0,0 +1,7 @@ +" This is used for the default userreg function. + +lua vim.userreg = require('vim.userreg') + +function! userreg#func(action, register, content) abort + return v:lua.vim.userreg.fn(a:action, a:register, a:content) +endfunction diff --git a/runtime/lua/vim/userreg.lua b/runtime/lua/vim/userreg.lua new file mode 100644 index 0000000000..5abcff0407 --- /dev/null +++ b/runtime/lua/vim/userreg.lua @@ -0,0 +1,51 @@ +-- Defualt implementation of the userregfunc. This default implementation is +-- extensible and allows other plugins to register handlers for different +-- registers. +-- +-- The default handler behaves just as a normal register would. + +local userreg = {} + +-- Returns a "default handler" which behaves exactly like the builtin registers +-- in Vim. Simply stores whatever was yanked and returns the last thing that was +-- yanked. +function userreg._default_handler() + local d = {} + + function d.do_yank(self, content) + self.content = content + end + + function d.do_put(self) + return self.content or {} + end + + return d +end + +-- The store for registers default handler +userreg._regtable = {} + +-- Function for the userreg. This function will defer to the handler registered +-- to the given register. If no handler is registered to the given register, the +-- default handler is used. +function userreg.fn(action, register, content) + if not userreg._regtable[register] then + userreg._regtable[register] = userreg._default_handler() + end + + if action == "yank" then + userreg._regtable[register]:do_yank(content) + return nil + else + return userreg._regtable[register]:do_put() + end +end + +-- Registers a handler with a register. Future yanks and puts will defer to the +-- handler when determining the content to put/yank. +function userreg.register_handler(register, handler) + userreg._regtable[register] = handler +end + +return userreg diff --git a/runtime/plugin/userreg.vim b/runtime/plugin/userreg.vim new file mode 100644 index 0000000000..099e7c65cb --- /dev/null +++ b/runtime/plugin/userreg.vim @@ -0,0 +1 @@ +set userregfunc=userreg#func -- cgit