From 2034a8419e1c5675592cdd0d0ffeaadfda58001a Mon Sep 17 00:00:00 2001 From: Josh Rahm Date: Wed, 9 Apr 2025 23:46:29 +0000 Subject: feat(userregfunc): programmable user-defined registers with multibyte support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch introduces a new global option `userregfunc`, allowing users to define custom behavior for registers not handled by Neovim internally. This enables programmable registers using any Unicode character — including multibyte characters. - A new register slot `USER_REGISTER` is introduced. Any register not matching the standard set (`0-9a-zA-Z"+-*%#/:.=`, etc.) is routed through this system. - When such a register is accessed, the function defined in `userregfunc` is called with three arguments: 1. `{action}` (string): either `"yank"` or `"put"` 2. `{register}` (string): UTF-8 character name of the register 3. `{content}`: - If `action == "yank"`: a dictionary with these keys: - `lines` (list of strings): the yanked text - `type` (string): one of `"v"` (charwise), `"V"` (linewise), or `"b"` (blockwise) - `width` (number, optional): present if `type == "b"` - `additional_data` (dict, optional): user-extensible metadata - If `action == "put"`: this is always `v:null` - The function may return either: - A **string** (used as a charwise register), or - A **dictionary** matching the structure above - Internally, `read_userregister()` and `write_userregister()` convert between `yankreg_T` and typval dictionaries. - Messages and internal logic fully support multibyte register names via UTF-8. - A new `USER_REGISTER` slot is used for logical separation in the register table. Included in this patch is an extensible Lua framework (`vim.userregs`) for defining user register handlers in Lua. It provides per-register handlers via `register_handler(registers, handler)` The global function `_G.def_userreg_func` is registered as the default implementation of `'userregfunc'`, enabling seamless integration with the Lua framework. - Register `λ` dynamically inserts the current date - Register `&` reads and writes from a "global register" file under `stdpath("run")` - Register `?` returns the result of a shell command - Registers that auto-adjust based on filetype, cursor context, or Treesitter nodes This change expands the register model into a programmable abstraction — fully scriptable and extensible — without breaking compatibility. --- src/nvim/api/command.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'src/nvim/api/command.c') diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index 4b93f09c61..b8892c9baa 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -496,18 +496,14 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Arena if (HAS_KEY(cmd, cmd, reg)) { VALIDATE_MOD((ea.argt & EX_REGSTR), "register", cmd->cmd.data); - VALIDATE_EXP((cmd->reg.size == 1), - "reg", "single character", cmd->reg.data, { - goto end; - }); - char regname = cmd->reg.data[0]; + int regname = utf_ptr2char(cmd->reg.data); VALIDATE((regname != '='), "%s", "Cannot use register \"=", { goto end; }); VALIDATE(valid_yank_reg(regname, (!IS_USER_CMDIDX(ea.cmdidx) && ea.cmdidx != CMD_put && ea.cmdidx != CMD_iput)), - "Invalid register: \"%c", regname, { + "Invalid register: \"%s", cmd->reg.data, { goto end; }); ea.regname = (uint8_t)regname; @@ -794,7 +790,7 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin // Command register. if (eap->argt & EX_REGSTR && eap->regname) { - kv_printf(cmdline, " %c", eap->regname); + kv_printf(cmdline, " %s", reg_to_mb(eap->regname)); } eap->argc = argc; -- cgit