aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval/vars.c
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2025-04-09 23:46:29 +0000
committerJosh Rahm <joshuarahm@gmail.com>2025-04-16 17:41:19 +0000
commit2034a8419e1c5675592cdd0d0ffeaadfda58001a (patch)
tree4ba185d58c2ea2b8893aad66aa96f6e5efaec1ef /src/nvim/eval/vars.c
parentf068386c9f709c586f44169f4566b4e31ce973de (diff)
downloadrneovim-2034a8419e1c5675592cdd0d0ffeaadfda58001a.tar.gz
rneovim-2034a8419e1c5675592cdd0d0ffeaadfda58001a.tar.bz2
rneovim-2034a8419e1c5675592cdd0d0ffeaadfda58001a.zip
feat(userregfunc): programmable user-defined registers with multibyte support
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.
Diffstat (limited to 'src/nvim/eval/vars.c')
-rw-r--r--src/nvim/eval/vars.c13
1 files changed, 8 insertions, 5 deletions
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index 012d23b567..8d637fcc43 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -34,6 +34,7 @@
#include "nvim/globals.h"
#include "nvim/hashtab.h"
#include "nvim/macros_defs.h"
+#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/ops.h"
@@ -599,7 +600,7 @@ const char *skip_var_list(const char *arg, int *var_count, int *semicolon, bool
static const char *skip_var_one(const char *arg)
{
if (*arg == '@' && arg[1] != NUL) {
- return arg + 2;
+ return arg + 1 + utfc_ptr2len(arg + 1);
}
return find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg,
NULL, NULL, FNE_INCL_BR | FNE_CHECK_START);
@@ -908,16 +909,18 @@ static char *ex_let_register(char *arg, typval_T *const tv, const bool is_const,
char *arg_end = NULL;
arg++;
+ int regname = utf_ptr2char(arg);
+ int mblen = utf_ptr2len(arg);
if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) {
semsg(_(e_letwrong), op);
} else if (endchars != NULL
- && vim_strchr(endchars, (uint8_t)(*skipwhite(arg + 1))) == NULL) {
+ && vim_strchr(endchars, (uint8_t)(*skipwhite(arg + mblen))) == NULL) {
emsg(_(e_letunexp));
} else {
char *ptofree = NULL;
const char *p = tv_get_string_chk(tv);
if (p != NULL && op != NULL && *op == '.') {
- char *s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc);
+ char *s = get_reg_contents(regname == '@' ? '"' : regname, kGRegExprSrc);
if (s != NULL) {
ptofree = concat_str(s, p);
p = ptofree;
@@ -925,8 +928,8 @@ static char *ex_let_register(char *arg, typval_T *const tv, const bool is_const,
}
}
if (p != NULL) {
- write_reg_contents(*arg == '@' ? '"' : *arg, p, (ssize_t)strlen(p), false);
- arg_end = arg + 1;
+ write_reg_contents(*arg == '@' ? '"' : regname, p, (ssize_t)strlen(p), false);
+ arg_end = arg + mblen;
}
xfree(ptofree);
}