diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/auevents.lua | 2 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 1 | ||||
-rw-r--r-- | src/nvim/eval.c | 10 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 1 | ||||
-rw-r--r-- | src/nvim/eval/userfunc.c | 6 | ||||
-rw-r--r-- | src/nvim/eval/vars.c | 16 | ||||
-rw-r--r-- | src/nvim/map.c | 3 | ||||
-rw-r--r-- | src/nvim/map_defs.h | 1 | ||||
-rw-r--r-- | src/nvim/ops.c | 477 | ||||
-rw-r--r-- | src/nvim/ops.h | 10 | ||||
-rw-r--r-- | src/nvim/option.c | 4 | ||||
-rw-r--r-- | src/nvim/option_vars.h | 3 | ||||
-rw-r--r-- | src/nvim/options.lua | 59 | ||||
-rw-r--r-- | src/nvim/po/da.po | 2 | ||||
-rw-r--r-- | src/nvim/po/fr.po | 2 | ||||
-rw-r--r-- | src/nvim/po/tr.po | 2 | ||||
-rw-r--r-- | src/nvim/po/uk.po | 2 | ||||
-rw-r--r-- | src/nvim/shada.c | 4 | ||||
-rw-r--r-- | src/nvim/yankmap.c | 45 | ||||
-rw-r--r-- | src/nvim/yankmap.h | 25 |
20 files changed, 605 insertions, 70 deletions
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index 84735c293a..3243822c94 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -118,6 +118,8 @@ return { 'TextChangedP', -- text was modified in Insert mode(popup) 'TextChangedT', -- text was modified in Terminal mode 'TextYankPost', -- after a yank or delete was done (y, d, c) + 'TextPutPre', -- before a put was done (p, P) + 'TextPutPost', -- after a put was done (p, P) 'UIEnter', -- after UI attaches 'UILeave', -- after UI detaches 'User', -- user defined autocommand diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index bffb29578f..3b84a4420f 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -541,6 +541,7 @@ struct file_buffer { Callback b_ofu_cb; ///< 'omnifunc' callback char *b_p_tfu; ///< 'tagfunc' option value Callback b_tfu_cb; ///< 'tagfunc' callback + char *b_p_urf; ///< 'userregfunc' char *b_p_ffu; ///< 'findfunc' option value Callback b_ffu_cb; ///< 'findfunc' callback int b_p_eof; ///< 'endoffile' diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5b91f1248f..97d1a3c75d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3446,12 +3446,10 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan // Register contents: @r. case '@': (*arg)++; + int regname = mb_cptr2char_adv((const char**) arg); if (evaluate) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = get_reg_contents(**arg, kGRegExprSrc); - } - if (**arg != NUL) { - (*arg)++; + rettv->vval.v_string = get_reg_contents(regname, kGRegExprSrc); } break; @@ -4819,13 +4817,13 @@ bool garbage_collect(bool testing) // registers (ShaDa additional data) { - const void *reg_iter = NULL; + iter_register_T reg_iter = ITER_REGISTER_NULL; do { yankreg_T reg; char name = NUL; bool is_unnamed = false; reg_iter = op_global_reg_iter(reg_iter, &name, ®, &is_unnamed); - } while (reg_iter != NULL); + } while (reg_iter != ITER_REGISTER_NULL); } // global marks (ShaDa additional data) diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index c125bd8893..8db50a53b3 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3197,6 +3197,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) "xattr", #endif "nvim", + "rneovim", }; // XXX: eval_has_provider() may shell out :( diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index f386dd28b9..84ee27493c 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1020,6 +1020,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett bool started_profiling = false; bool did_save_redo = false; save_redo_T save_redo; + char* saved_repeat_cmdline = NULL; // If depth of calling is getting too high, don't execute the function if (depth >= p_mfd) { @@ -1032,6 +1033,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett // Save search patterns and redo buffer. save_search_patterns(); if (!ins_compl_active()) { + if (repeat_cmdline) { + saved_repeat_cmdline = xstrdup(repeat_cmdline); + } saveRedobuff(&save_redo); did_save_redo = true; } @@ -1376,6 +1380,8 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett // restore search patterns and redo buffer if (did_save_redo) { restoreRedobuff(&save_redo); + xfree(repeat_cmdline); + repeat_cmdline = saved_repeat_cmdline; } restore_search_patterns(); } diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 012d23b567..c64477741d 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,20 @@ 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(*arg == '@' ? '"' : regname, kGRegExprSrc); if (s != NULL) { ptofree = concat_str(s, p); p = ptofree; @@ -925,8 +930,9 @@ 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); } diff --git a/src/nvim/map.c b/src/nvim/map.c index be6bf58daa..d7d1a00158 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -118,6 +118,9 @@ void mh_clear(MapHash *h) #define VAL_NAME(x) quasiquote(x, ptr_t) #include "nvim/map_value_impl.c.h" #undef VAL_NAME +#define VAL_NAME(x) quasiquote(x, int) +#include "nvim/map_value_impl.c.h" +#undef VAL_NAME #undef KEY_NAME #define KEY_NAME(x) x##cstr_t diff --git a/src/nvim/map_defs.h b/src/nvim/map_defs.h index 836b1447c2..36c851497d 100644 --- a/src/nvim/map_defs.h +++ b/src/nvim/map_defs.h @@ -153,6 +153,7 @@ KEY_DECLS(HlEntry) KEY_DECLS(ColorKey) MAP_DECLS(int, int) +MAP_DECLS(ptr_t, int) MAP_DECLS(int, ptr_t) MAP_DECLS(cstr_t, ptr_t) MAP_DECLS(cstr_t, int) diff --git a/src/nvim/ops.c b/src/nvim/ops.c index d51b4cc88b..2f45d862c3 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -28,6 +28,7 @@ #include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/userfunc.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_cmds_defs.h" @@ -76,8 +77,26 @@ #include "nvim/ui_defs.h" #include "nvim/undo.h" #include "nvim/vim_defs.h" +#include "nvim/window.h" +#include "nvim/yankmap.h" +#include "nvim/window.h" -static yankreg_T y_regs[NUM_REGISTERS] = { 0 }; +struct yank_registers { + yankmap_T inner; +}; + +yank_registers_T y_regs; + +static yankreg_T *get_reg(yank_registers_T *regs, int idx) +{ + return yankmap_get(®s->inner, idx); + +} + +static yankreg_T *get_global_reg(int idx) +{ + return get_reg(&y_regs, idx); +} static yankreg_T *y_previous = NULL; // ptr to last written yankreg @@ -866,6 +885,24 @@ char *get_expr_line_src(void) return xstrdup(expr_line); } + +int get_userreg(int regname) +{ + if ((regname >= 'a' && regname <= 'z') + || (regname >= 'A' && regname <= 'Z') + || (regname >= '0' && regname <= '9') + || (regname <= 127 && strchr("\"-:.%#=*+_/", regname)) + || regname == Ctrl_F + || regname == Ctrl_P + || regname == Ctrl_W + || regname == Ctrl_A + || (regname + USER_REGISTERS_START) < regname) { + return -1; + } + + return regname + USER_REGISTERS_START; +} + /// @return whether `regname` is a valid name of a yank register. /// /// @note: There is no check for 0 (default register), caller should do this. @@ -882,12 +919,164 @@ bool valid_yank_reg(int regname, bool writing) || regname == '-' || regname == '_' || regname == '*' - || regname == '+') { + || regname == '+' + || get_userreg(regname) != -1) { return true; } return false; } +static int call_userreg_put(const char* urf, int regname, typval_T* out) +{ + char regname_str[5]; + int len; + + len = utf_char2len(regname); + regname_str[len] = 0; + utf_char2bytes(regname, regname_str); + + typval_T args[3]; + args[0].v_type = VAR_STRING; + args[1].v_type = VAR_STRING; + args[2].v_type = VAR_NUMBER; + + args[0].vval.v_string = "put"; + args[1].vval.v_string = regname_str; + args[2].vval.v_number = 0; + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.fe_evaluate = true; + + return call_func( + urf, + -1, + out, + /* argcount_in = */ 3, + args, + &funcexe); +} + +// Converts a typval returned from the userregfunction to a register. +static void typval_to_yankreg(yankreg_T* yankreg, typval_T* val) +{ + if (!yankreg || !val) { + return; + } + + char* type; + dict_T* dict; + typval_T tv; + size_t i; + size_t sz; + + free_register(yankreg); + memset(yankreg, 0, sizeof(*yankreg)); + + switch (val->v_type) { + + case VAR_DICT: + dict = val->vval.v_dict; + type = tv_dict_get_string(dict, "type", false); + + if (!strcmp(type, "block")) { + yankreg->y_width = (int) tv_dict_get_number(dict, "width"); + yankreg->y_type = kMTBlockWise; + } else if (!strcmp(type, "line")) { + yankreg->y_type = kMTLineWise; + } else { + yankreg->y_type = kMTCharWise; + } + + if (tv_dict_get_tv(dict, "lines", &tv) == OK) { + if (tv.v_type == VAR_STRING) { + yankreg->y_array = (String*) xcalloc(1, sizeof(String)); + yankreg->y_array[0].data = strdup(tv.vval.v_string); + yankreg->y_array[0].size = strlen(tv.vval.v_string); + } else if (tv.v_type == VAR_LIST) { + yankreg->y_array = + (String*) xcalloc((size_t) tv_list_len(tv.vval.v_list), sizeof(String)); + + i = 0; + TV_LIST_ITER_CONST(tv.vval.v_list, li, { + if (li->li_tv.v_type == VAR_STRING) { + char* tmp = strdup(tv_get_string(&li->li_tv)); + yankreg->y_array[i].data = tmp; + yankreg->y_array[i].size = strlen(tmp); + } else { + yankreg->y_array[i].data = NULL; + yankreg->y_array[i].size = 0; + } + ++ i; + }); + + yankreg->y_size = i; + } + } else { + yankreg->y_array = NULL; + } + + if (tv_dict_get_tv(dict, "additional_data", &tv) == OK) { + if (tv.v_type == VAR_DICT) { + yankreg->additional_data = NULL; // tv.vval.v_dict; + } + } + break; + + case VAR_LIST: + yankreg->y_type = kMTLineWise; + sz = (size_t) tv_list_len(val->vval.v_list); + yankreg->y_array = (String*) xcalloc(sz, sizeof(String)); + yankreg->y_size = sz; + i = 0; + TV_LIST_ITER_CONST(val->vval.v_list, li, { + char* tmp = strdup(tv_get_string(&li->li_tv)); + yankreg->y_array[i].data = tmp; + yankreg->y_array[i].size = strlen(tmp); + i ++; + }); + break; + + default: + yankreg->y_type = kMTCharWise; + yankreg->y_size = 1; + + if (val->vval.v_string) { + yankreg->y_array = (String*) xcalloc(1, sizeof(String)); + char* tmp = strdup(tv_get_string(val)); + yankreg->y_array[0].data = tmp; + yankreg->y_array[0].size = strlen(tmp); + } else { + yankreg->y_array = NULL; + } + + break; + + } + + yankreg->timestamp = os_time(); +} + +static void copy_userreg(yankreg_T* into, int regname) +{ + if (!into) { + return; + } + + if (!curbuf->b_p_urf || strlen(curbuf->b_p_urf) == 0) { + return; + } + + typval_T* ret = xmalloc(sizeof(typval_T)); + + if (call_userreg_put(curbuf->b_p_urf, regname, ret) == FAIL) { + return; + } + + typval_to_yankreg(into, ret); + + tv_free(ret); +} + /// @return yankreg_T to use, according to the value of `regname`. /// Cannot handle the '_' (black hole) register. /// Must only be called with a valid register name! @@ -931,7 +1120,11 @@ yankreg_T *get_yank_register(int regname, int mode) if (i == -1) { i = 0; } - reg = &y_regs[i]; + reg = get_global_reg(i); + if (get_userreg(regname) != -1 && mode != YREG_YANK) { + // If the mode is not yank, copy the userreg data to the reg. + copy_userreg(reg, regname); + } if (mode == YREG_YANK) { // remember the written register for unnamed paste @@ -988,8 +1181,7 @@ int do_record(int c) if (reg_recording == 0) { // start recording - // registers 0-9, a-z and " are allowed - if (c < 0 || (!ASCII_ISALNUM(c) && c != '"')) { + if (c < 0) { retval = FAIL; } else { reg_recording = c; @@ -1014,9 +1206,10 @@ int do_record(int c) } // Name of requested register, or empty string for unnamed operation. - char buf[NUMBUFLEN + 2]; - buf[0] = (char)regname; - buf[1] = NUL; + char buf[NUMBUFLEN + 5]; + int len = utf_char2len(regname); + utf_char2bytes(regname, buf); + buf[len] = NUL; tv_dict_add_str(dict, S_LEN("regname"), buf); tv_dict_set_keys_readonly(dict); @@ -1084,6 +1277,9 @@ static int stuff_yank(int regname, char *p) reg->y_type = kMTCharWise; } reg->timestamp = os_time(); + if (get_userreg(regname) != -1) { + return eval_yank_userreg(curbuf->b_p_urf, regname, reg); + } return OK; } @@ -1392,6 +1588,90 @@ int insert_reg(int regname, bool literally_arg) return retval; } +/// Converts a yankreg to a dict which can be used as an argument to the +// userregfunc. +static dict_T* yankreg_to_dict(yankreg_T* yankreg) { + dict_T *const dict = tv_dict_alloc(); + dict->dv_refcount = 1; + tv_dict_add_nr(dict, S_LEN("width"), yankreg->y_width); + + const char* type; + + switch(yankreg->y_type) { + case kMTLineWise: + type = "line"; + break; + case kMTCharWise: + type = "char"; + break; + case kMTBlockWise: + type = "block"; + break; + default: + type = "unknown"; + } + + tv_dict_add_str(dict, S_LEN("type"), type); + // if (yankreg->additional_data) { + // tv_dict_add_dict(dict, S_LEN("additional_data"), yankreg->additional_data); + // } + + list_T *const lines = tv_list_alloc((long)yankreg->y_size); + + size_t i; + for (i = 0; i < yankreg->y_size; ++ i) { + tv_list_append_string( + lines, yankreg->y_array[i].data, (long)yankreg->y_array[i].size); + } + + tv_dict_add_list(dict, S_LEN("lines"), lines); + + return dict; +} + +/* + * Executes the yank() function on a user-defined register to set the contents + * of that register. + */ +static int eval_yank_userreg(const char *ufn, int regname, yankreg_T *reg) +{ + if (!reg) + return -1; + + int ret, len; + char regname_str[5]; + + len = (*utf_char2len)(regname); + regname_str[len] = 0; + utf_char2bytes(regname, regname_str); + + typval_T args[4]; + args[0].v_type = VAR_STRING; + args[1].v_type = VAR_STRING; + args[2].v_type = VAR_DICT; + args[3].v_type = VAR_UNKNOWN; + + args[0].vval.v_string = "yank"; + args[1].vval.v_string = regname_str; + args[2].vval.v_dict = yankreg_to_dict(reg); + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.fe_evaluate = true; + + typval_T* out = xmalloc(sizeof(typval_T)); + return call_func( + ufn, + -1, + out, + /* argcount_in = */ 3, + args, + &funcexe + ); + + tv_free(out); + return ret; +} + /// If "regname" is a special register, return true and store a pointer to its /// value in "argp". /// @@ -1475,6 +1755,9 @@ bool get_spec_reg(int regname, char **argp, bool *allocated, bool errmsg) case '_': // black hole: always empty *argp = ""; return true; + + default: + break; } return false; @@ -1521,14 +1804,14 @@ bool cmdline_paste_reg(int regname, bool literally_arg, bool remcr) /// Shift the delete registers: "9 is cleared, "8 becomes "9, etc. static void shift_delete_registers(bool y_append) { - free_register(&y_regs[9]); // free register "9 + free_register(get_global_reg(9)); // free register "9 for (int n = 9; n > 1; n--) { - y_regs[n] = y_regs[n - 1]; + *get_global_reg(n) = *get_global_reg(n - 1); } if (!y_append) { - y_previous = &y_regs[1]; + y_previous = get_global_reg(1); } - y_regs[1].y_array = NULL; // set register "1 to empty + get_global_reg(1)->y_array = NULL; // set register "1 to empty } /// Handle a delete operation. @@ -1621,7 +1904,7 @@ int op_delete(oparg_T *oap) if (oap->motion_type == kMTLineWise || oap->line_count > 1 || oap->use_reg_one) { shift_delete_registers(is_append_register(oap->regname)); - reg = &y_regs[1]; + reg = get_global_reg(1); op_yank_reg(oap, false, reg, false); did_yank = true; } @@ -2598,11 +2881,20 @@ int op_change(oparg_T *oap) return retval; } + +/* + * set all the yank registers to empty (called from main()) + */ +void init_yank(void) +{ + init_yankmap(&y_regs.inner); +} + #if defined(EXITFREE) void clear_registers(void) { for (int i = 0; i < NUM_REGISTERS; i++) { - free_register(&y_regs[i]); + free_register(get_global_reg(i)); } } @@ -2648,6 +2940,14 @@ bool op_yank(oparg_T *oap, bool message) yankreg_T *reg = get_yank_register(oap->regname, YREG_YANK); op_yank_reg(oap, message, reg, is_append_register(oap->regname)); + + if (get_userreg(oap->regname) != -1) { + if (eval_yank_userreg(curbuf->b_p_urf, oap->regname, reg) == -1) { + beep_flush(); + return false; + } + } + set_clipboard(oap->regname, reg); do_autocmd_textyankpost(oap, reg); return true; @@ -2778,7 +3078,11 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) if (oap->regname == NUL) { *namebuf = NUL; } else { - vim_snprintf(namebuf, sizeof(namebuf), _(" into \"%c"), oap->regname); + char buf[5]; + int len = (*utf_char2len) (oap->regname); + utf_char2bytes(oap->regname, buf); + buf[len] = 0; + vim_snprintf(namebuf, sizeof(namebuf), _(" into \"%s"), buf); } // redisplay now, so message is not deleted @@ -2849,6 +3153,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) FUNC_ATTR_NONNULL_ALL { static bool recursive = false; + int len; if (recursive || !has_event(EVENT_TEXTYANKPOST)) { // No autocommand was defined, or we yanked from this autocommand. @@ -2870,13 +3175,14 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) tv_dict_add_list(dict, S_LEN("regcontents"), list); // Register type. - char buf[NUMBUFLEN + 2]; + char buf[NUMBUFLEN + 6]; format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf)); tv_dict_add_str(dict, S_LEN("regtype"), buf); // Name of requested register, or empty string for unnamed operation. - buf[0] = (char)oap->regname; - buf[1] = NUL; + len = utf_char2len(oap->regname); + buf[len] = 0; + utf_char2bytes(oap->regname, buf); tv_dict_add_str(dict, S_LEN("regname"), buf); // Motion type: inclusive or exclusive. @@ -2901,6 +3207,56 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) recursive = false; } +/// Execute autocommands for TextPutPost. +/// +/// @param oap Operator arguments. +/// @param reg The yank register used. +static void do_autocmd_textput(int regname, yankreg_T *reg, enum auto_event evt) + FUNC_ATTR_NONNULL_ALL +{ + static bool recursive = false; + int len; + + if (recursive || !has_event(evt)) { + // No autocommand was defined, or we yanked from this autocommand. + return; + } + + recursive = true; + + save_v_event_T save_v_event; + // Set the v:event dictionary with information about the yank. + dict_T *dict = get_v_event(&save_v_event); + + // The yanked text contents. + list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size); + for (size_t i = 0; i < reg->y_size; i++) { + tv_list_append_string(list, reg->y_array[i].data, reg->y_array[i].size); + } + tv_list_set_lock(list, VAR_FIXED); + (void)tv_dict_add_list(dict, S_LEN("regcontents"), list); + + // Register type. + char buf[NUMBUFLEN + 6]; + format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf)); + (void)tv_dict_add_str(dict, S_LEN("regtype"), buf); + + // Name of requested register, or empty string for unnamed operation. + len = utf_char2len(regname); + buf[len] = 0; + utf_char2bytes(regname, buf); + recursive = true; + (void)tv_dict_add_str(dict, S_LEN("regname"), buf); + + tv_dict_set_keys_readonly(dict); + textlock++; + apply_autocmds(evt, NULL, NULL, false, curbuf); + textlock--; + restore_v_event(dict, &save_v_event); + + recursive = false; +} + /// Put contents of register "regname" into the text. /// Caller must check "regname" to be valid! /// @@ -2924,6 +3280,10 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) const pos_T orig_end = curbuf->b_op_end; unsigned cur_ve_flags = get_ve_flags(curwin); + if (reg) { + do_autocmd_textput(regname, reg, EVENT_TEXTPUTPRE); + } + curbuf->b_op_start = curwin->w_cursor; // default for '[ mark curbuf->b_op_end = curwin->w_cursor; // default for '] mark @@ -3079,6 +3439,10 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) reg = get_yank_register(regname, YREG_PASTE); } + if (get_userreg(regname) != -1) { + copy_userreg(reg, regname); + } + y_type = reg->y_type; y_width = reg->y_width; y_size = reg->y_size; @@ -3682,6 +4046,10 @@ error: } end: + if (reg) { + do_autocmd_textput(regname, reg, EVENT_TEXTPUTPOST); + } + if (cmdmod.cmod_flags & CMOD_LOCKMARKS) { curbuf->b_op_start = orig_start; curbuf->b_op_end = orig_end; @@ -3754,7 +4122,7 @@ int get_register_name(int num) /// @return the index of the register "" points to. int get_unname_register(void) { - return y_previous == NULL ? -1 : (int)(y_previous - &y_regs[0]); + return yankmap_find(&y_regs.inner, y_previous); } /// ":dis" and ":registers": Display the contents of the yank registers. @@ -3791,10 +4159,10 @@ void ex_display(exarg_T *eap) if (y_previous != NULL) { yb = y_previous; } else { - yb = &(y_regs[0]); + yb = get_global_reg(0); } } else { - yb = &(y_regs[i]); + yb = get_global_reg(i); } get_clipboard(name, &yb, true); @@ -5040,6 +5408,10 @@ static yankreg_T *init_write_reg(int name, yankreg_T **old_y_previous, bool must static void finish_write_reg(int name, yankreg_T *reg, yankreg_T *old_y_previous) { + if (get_userreg(name) != -1) { + eval_yank_userreg(curbuf->b_p_urf, name, reg); + } + // Send text of clipboard register to the clipboard. set_clipboard(name, reg); @@ -6478,7 +6850,7 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing) } if (explicit_cb_reg) { - target = &y_regs[*name == '*' ? STAR_REGISTER : PLUS_REGISTER]; + target = get_global_reg(*name == '*' ? STAR_REGISTER : PLUS_REGISTER); if (writing && (cb_flags & (*name == '*' ? kOptCbFlagUnnamed : kOptCbFlagUnnamedplus))) { clipboard_needs_update = false; } @@ -6495,10 +6867,10 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing) if (cb_flags & kOptCbFlagUnnamedplus) { *name = (cb_flags & kOptCbFlagUnnamed && writing) ? '"' : '+'; - target = &y_regs[PLUS_REGISTER]; + target = get_global_reg(PLUS_REGISTER); } else { *name = '*'; - target = &y_regs[STAR_REGISTER]; + target = get_global_reg(STAR_REGISTER); } goto end; } @@ -6808,11 +7180,11 @@ static inline bool reg_empty(const yankreg_T *const reg) /// Iterate over global registers. /// /// @see op_register_iter -const void *op_global_reg_iter(const void *const iter, char *const name, yankreg_T *const reg, - bool *is_unnamed) +iter_register_T op_global_reg_iter(iter_register_T iter, char *const name, + yankreg_T *const reg, bool *is_unnamed) FUNC_ATTR_NONNULL_ARG(2, 3, 4) FUNC_ATTR_WARN_UNUSED_RESULT { - return op_reg_iter(iter, y_regs, name, reg, is_unnamed); + return op_reg_iter(iter, &y_regs, name, reg, is_unnamed); } /// Iterate over registers `regs`. @@ -6824,30 +7196,33 @@ const void *op_global_reg_iter(const void *const iter, char *const name, yankreg /// /// @return Pointer that must be passed to next `op_register_iter` call or /// NULL if iteration is over. -const void *op_reg_iter(const void *const iter, const yankreg_T *const regs, char *const name, - yankreg_T *const reg, bool *is_unnamed) +iter_register_T op_reg_iter(iter_register_T iter, yank_registers_T *regs, + char *const name, yankreg_T *const reg, + bool *is_unnamed) FUNC_ATTR_NONNULL_ARG(3, 4, 5) FUNC_ATTR_WARN_UNUSED_RESULT { *name = NUL; - const yankreg_T *iter_reg = (iter == NULL - ? &(regs[0]) - : (const yankreg_T *const)iter); - while (iter_reg - &(regs[0]) < NUM_SAVED_REGISTERS && reg_empty(iter_reg)) { - iter_reg++; + int iter_idx = (int)(iter == ITER_REGISTER_NULL ? 0 : iter - 1); + + while (iter_idx < NUM_SAVED_REGISTERS && reg_empty(get_reg(regs, iter_idx))) { + ++iter_idx; } - if (iter_reg - &(regs[0]) == NUM_SAVED_REGISTERS || reg_empty(iter_reg)) { - return NULL; + + if (iter_idx >= NUM_SAVED_REGISTERS || reg_empty(get_reg(regs, iter_idx))) { + return ITER_REGISTER_NULL; } - int iter_off = (int)(iter_reg - &(regs[0])); - *name = (char)get_register_name(iter_off); - *reg = *iter_reg; - *is_unnamed = (iter_reg == y_previous); - while (++iter_reg - &(regs[0]) < NUM_SAVED_REGISTERS) { - if (!reg_empty(iter_reg)) { - return (void *)iter_reg; + + *reg = *get_reg(regs, iter_idx); + *name = (char) get_register_name(iter_idx); + *is_unnamed = (get_reg(regs, iter_idx) == y_previous); + + while (++iter_idx < NUM_SAVED_REGISTERS) { + if (!reg_empty(get_reg(regs, iter_idx))) { + return iter_idx + 1; } } - return NULL; + + return ITER_REGISTER_NULL; } /// Get a number of non-empty registers @@ -6855,8 +7230,8 @@ size_t op_reg_amount(void) FUNC_ATTR_WARN_UNUSED_RESULT { size_t ret = 0; - for (size_t i = 0; i < NUM_SAVED_REGISTERS; i++) { - if (!reg_empty(y_regs + i)) { + for (int i = 0; i < NUM_SAVED_REGISTERS; i++) { + if (!reg_empty(get_global_reg(i))) { ret++; } } @@ -6876,11 +7251,11 @@ bool op_reg_set(const char name, const yankreg_T reg, bool is_unnamed) if (i == -1) { return false; } - free_register(&y_regs[i]); - y_regs[i] = reg; + free_register(get_global_reg(i)); + *get_global_reg(i) = reg; if (is_unnamed) { - y_previous = &y_regs[i]; + y_previous = get_global_reg(i); } return true; } @@ -6896,7 +7271,7 @@ const yankreg_T *op_reg_get(const char name) if (i == -1) { return NULL; } - return &y_regs[i]; + return get_global_reg(i); } /// Set the previous yank register @@ -6911,7 +7286,7 @@ bool op_reg_set_previous(const char name) return false; } - y_previous = &y_regs[i]; + y_previous = get_global_reg(i); return true; } diff --git a/src/nvim/ops.h b/src/nvim/ops.h index 99b9b6182d..05dd0454dc 100644 --- a/src/nvim/ops.h +++ b/src/nvim/ops.h @@ -37,6 +37,7 @@ typedef int (*Indenter)(void); /// flags for do_put() enum { + ITER_REGISTER_NULL = 0, PUT_FIXINDENT = 1, ///< make indent look nice PUT_CURSEND = 2, ///< leave cursor after end of new text PUT_CURSLINE = 4, ///< leave cursor on last line of new text @@ -60,6 +61,7 @@ enum { STAR_REGISTER = 37, PLUS_REGISTER = 38, NUM_REGISTERS = 39, + USER_REGISTERS_START = 39 }; /// Operator IDs; The order must correspond to opchars[] in ops.c! @@ -96,6 +98,10 @@ enum { OP_NR_SUB = 29, ///< "<C-X>" Subtract from the number or alphabetic character }; +struct yank_registers; +typedef struct yank_registers yank_registers_T; +typedef size_t iter_register_T; + /// Flags for get_reg_contents(). enum GRegFlags { kGRegNoExpr = 1, ///< Do not allow expression register. @@ -119,6 +125,8 @@ typedef enum { YREG_YANK, YREG_PUT, } yreg_mode_t; +/// Returns a reference to a user-defined register. +int get_userreg(int regname); #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ops.h.generated.h" @@ -146,7 +154,7 @@ static inline int op_reg_index(const int regname) } else if (regname == '+') { return PLUS_REGISTER; } else { - return -1; + return get_userreg(regname); } } diff --git a/src/nvim/option.c b/src/nvim/option.c index 796165453c..3d805dc319 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -4679,6 +4679,8 @@ void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win) return &(buf->b_p_cfu); case kOptOmnifunc: return &(buf->b_p_ofu); + case kOptUserregfunc: + return &(buf->b_p_urf); case kOptEndoffile: return &(buf->b_p_eof); case kOptEndofline: @@ -5102,7 +5104,9 @@ void buf_copy_options(buf_T *buf, int flags) set_buflocal_cfu_callback(buf); buf->b_p_ofu = xstrdup(p_ofu); COPY_OPT_SCTX(buf, kBufOptOmnifunc); + buf->b_p_urf = xstrdup(p_urf); set_buflocal_ofu_callback(buf); + COPY_OPT_SCTX(buf, kBufOptUserregfunc); buf->b_p_tfu = xstrdup(p_tfu); COPY_OPT_SCTX(buf, kBufOptTagfunc); set_buflocal_tfu_callback(buf); diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h index 340a12a32f..d31e76aff8 100644 --- a/src/nvim/option_vars.h +++ b/src/nvim/option_vars.h @@ -535,6 +535,7 @@ EXTERN char *p_udir; ///< 'undodir' EXTERN int p_udf; ///< 'undofile' EXTERN OptInt p_ul; ///< 'undolevels' EXTERN OptInt p_ur; ///< 'undoreload' +EXTERN char* p_urf; ///< 'userregfunction' EXTERN OptInt p_uc; ///< 'updatecount' EXTERN OptInt p_ut; ///< 'updatetime' EXTERN char *p_shada; ///< 'shada' @@ -577,7 +578,7 @@ EXTERN int p_wa; ///< 'writeany' EXTERN int p_wb; ///< 'writebackup' EXTERN OptInt p_wd; ///< 'writedelay' EXTERN int p_cdh; ///< 'cdhome' - + /// // Value for b_p_ul indicating the global value must be used. #define NO_LOCAL_UNDOLEVEL (-123456) diff --git a/src/nvim/options.lua b/src/nvim/options.lua index c61c6032c4..dcd6e0a58b 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -9588,6 +9588,65 @@ local options = { varname = 'p_ut', }, { + abbreviation='urf', + full_name='userregfunc', + desc= [=[ + This 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 'userregfunc' function is called each time a user register is read + from or written to. + + The 'userregfunc' 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 '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 'userregfunc' 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 userregfunc=MyUserregFunction +< + ]=], + short_desc=N_("Function used to define behavior of user-defined registers."), + type='string', + scope={'buf'}, + varname='p_urf', + defaults={if_true=""} + }, + { abbreviation = 'vsts', cb = 'did_set_varsofttabstop', defaults = '', diff --git a/src/nvim/po/da.po b/src/nvim/po/da.po index 8a7bffdd53..ba7ffa9f7b 100644 --- a/src/nvim/po/da.po +++ b/src/nvim/po/da.po @@ -4049,7 +4049,7 @@ msgid "freeing %ld lines" msgstr "frigør %ld linjer" #, c-format -msgid " into \"%c" +msgid " into \"%s" msgstr " i \"%c" #, c-format diff --git a/src/nvim/po/fr.po b/src/nvim/po/fr.po index 900f5bde03..0dae00f3a3 100644 --- a/src/nvim/po/fr.po +++ b/src/nvim/po/fr.po @@ -4313,7 +4313,7 @@ msgid "freeing %ld lines" msgstr "libration de %ld lignes" #, c-format -msgid " into \"%c" +msgid " into \"%s" msgstr " dans \"%c" #, c-format diff --git a/src/nvim/po/tr.po b/src/nvim/po/tr.po index e73c033a4e..ea41cf56b3 100644 --- a/src/nvim/po/tr.po +++ b/src/nvim/po/tr.po @@ -4208,7 +4208,7 @@ msgstr[0] "%<PRId64> satır değiştirildi" msgstr[1] "%<PRId64> satır değiştirildi" #, c-format -msgid " into \"%c" +msgid " into \"%s" msgstr " \"%c" #, c-format diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po index 25b31dac9b..cb422cc2b6 100644 --- a/src/nvim/po/uk.po +++ b/src/nvim/po/uk.po @@ -5375,7 +5375,7 @@ msgstr[1] "Змінено %<PRId64> рядки" msgstr[2] "Змінено %<PRId64> рядків" #, c-format -msgid " into \"%c" +msgid " into \"%s" msgstr " у \"%c" #, c-format diff --git a/src/nvim/shada.c b/src/nvim/shada.c index d60f2368ce..a5e98af7ac 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -2109,7 +2109,7 @@ static inline void add_search_pattern(PossiblyFreedShadaEntry *const ret_pse, static inline void shada_initialize_registers(WriteMergerState *const wms, int max_reg_lines) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE { - const void *reg_iter = NULL; + iter_register_T reg_iter = ITER_REGISTER_NULL; const bool limit_reg_lines = max_reg_lines >= 0; do { yankreg_T reg; @@ -2140,7 +2140,7 @@ static inline void shada_initialize_registers(WriteMergerState *const wms, int m .additional_data = reg.additional_data, } }; - } while (reg_iter != NULL); + } while (reg_iter != ITER_REGISTER_NULL); } /// Replace numbered mark in WriteMergerState diff --git a/src/nvim/yankmap.c b/src/nvim/yankmap.c new file mode 100644 index 0000000000..12b70fed65 --- /dev/null +++ b/src/nvim/yankmap.c @@ -0,0 +1,45 @@ +#include "nvim/yankmap.h" + +#include "nvim/memory.h" + +void init_yankmap(yankmap_T* map) +{ + memset(map, 0, sizeof(yankmap_T)); + + map->reg_to_yankreg = (Map(int, ptr_t))MAP_INIT; + map->yankreg_to_reg = (Map(ptr_t, int))MAP_INIT; + // map_init(int, ptr_t, &map->reg_to_yankreg); + // map_init(ptr_t, int, &map->yankreg_to_reg); +} + +yankreg_T* yankmap_get(yankmap_T* yankmap, int reg) +{ + bool is_new = false; + yankreg_T** ret + = (yankreg_T**)map_put_ref(int, ptr_t)(&yankmap->reg_to_yankreg, reg, NULL, &is_new); + + if (ret) { + if (is_new) { + *ret = xcalloc(1, sizeof(yankreg_T)); + } + + /* Add the back-reference */ + int* ref = map_put_ref(ptr_t, int)(&yankmap->yankreg_to_reg, *ret, NULL, NULL); + *ref = reg; + + return *ret; + } + + return NULL; +} + +int yankmap_find(yankmap_T* yankmap, yankreg_T* yankreg) +{ + int* ref = map_ref(ptr_t, int)(&yankmap->yankreg_to_reg, yankreg, NULL); + + if (ref) { + return *ref; + } + + return -1; +} diff --git a/src/nvim/yankmap.h b/src/nvim/yankmap.h new file mode 100644 index 0000000000..4468f3a016 --- /dev/null +++ b/src/nvim/yankmap.h @@ -0,0 +1,25 @@ +#ifndef YANK_TRIE_H_ +#define YANK_TRIE_H_ + +#include <stdbool.h> + +#include "nvim/map_defs.h" +#include "nvim/ops.h" + +typedef struct { + /* Register name to yank register. */ + Map(int, ptr_t) reg_to_yankreg; + + /* Yank register to register name. */ + Map(ptr_t, int) yankreg_to_reg; +} yankmap_T; + +void init_yankmap(yankmap_T* yankmap); + +yankreg_T* yankmap_get(yankmap_T* yankmap, int reg); + +yankreg_T* yankmap_put(yankmap_T* yankmap, int index); + +int yankmap_find(yankmap_T* yankmap, yankreg_T* yankreg); + +#endif |