aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/ops.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/ops.c')
-rw-r--r--src/nvim/ops.c237
1 files changed, 190 insertions, 47 deletions
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 013a78bdac..ef9fe055ac 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -57,7 +57,24 @@
#include "nvim/vim.h"
#include "nvim/window.h"
-static yankreg_T y_regs[NUM_REGISTERS];
+#include "nvim/yankmap.h"
+
+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(&regs->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
@@ -776,6 +793,23 @@ char_u *get_expr_line_src(void)
return vim_strsave(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;
+}
+
/// Returns whether `regname` is a valid name of a yank register.
/// Note: There is no check for 0 (default register), caller should do this.
/// The black hole register '_' is regarded as valid.
@@ -791,7 +825,8 @@ bool valid_yank_reg(int regname, bool writing)
|| regname == '-'
|| regname == '_'
|| regname == '*'
- || regname == '+') {
+ || regname == '+'
+ || get_userreg(regname) != -1) {
return true;
}
return false;
@@ -834,7 +869,7 @@ yankreg_T *get_yank_register(int regname, int mode)
if (i == -1) {
i = 0;
}
- reg = &y_regs[i];
+ reg = get_global_reg(i);
if (mode == YREG_YANK) {
// remember the written register for unnamed paste
@@ -1273,7 +1308,96 @@ static void stuffescaped(const char *arg, bool literally)
/// @param errmsg give error message when failing
///
/// @return true if "regname" is a special register,
-bool get_spec_reg(int regname, char_u **argp, bool *allocated, bool errmsg)
+bool get_spec_reg(int regname, char_u **argp, bool *allocated, bool errmsg);
+
+/*
+ * Executes a call to the put() function on a user-defined register to get the
+ * contents of a user defined register.
+ */
+static int eval_urf_put(char_u *ufn, int regname, char_u **argp)
+{
+ char_u 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_UNKNOWN;
+
+ args[0].vval.v_string = (char_u *)"put";
+ args[1].vval.v_string = regname_str;
+
+ *argp = (char_u *)call_func_retstr((char *)ufn, 3, args);
+ return *argp == NULL;
+}
+
+/*
+ * Executes the yank() function on a user-defined register to set the contents
+ * of that register.
+ */
+static int eval_yank_userreg(const char_u *ufn, int regname, yankreg_T *reg)
+{
+ if (!reg)
+ return -1;
+
+ char_u *totalbuf;
+ size_t totallen = 0;
+ size_t i, j, k;
+ int ret, len;
+ char_u regname_str[5];
+
+ {
+ // Concat the contents of the register to pass into the yank()
+ // user-defined function.
+ for (i = 0; i < reg->y_size; ++i) {
+ totallen += strlen((char *)reg->y_array[i]) + 1;
+ }
+ totalbuf = xmalloc(sizeof(char_u) * totallen);
+ j = 0;
+ for (i = 0; i < reg->y_size; ++i) {
+ for (k = 0; reg->y_array[i][k] != 0; ++k, ++j) {
+ totalbuf[j] = reg->y_array[i][k];
+ }
+ if (i < reg->y_size - 1) {
+ totalbuf[j++] = '\n';
+ }
+ }
+ totalbuf[j++] = 0;
+ }
+
+ 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_STRING;
+ args[3].v_type = VAR_UNKNOWN;
+
+ args[0].vval.v_string = (char_u *)"yank";
+ args[1].vval.v_string = regname_str;
+ args[2].vval.v_string = totalbuf;
+
+ char_u *dup_ufn = (char_u *)xstrdup((char *)ufn);
+ ret = (int)call_func_retnr(dup_ufn, 3, args);
+ xfree(dup_ufn);
+ xfree(totalbuf);
+ return ret;
+}
+
+// If "regname" is a special register, return true and store a pointer to its
+// value in "argp".
+bool get_spec_reg(
+ int regname,
+ char_u **argp,
+ bool *allocated, // return: true when value was allocated
+ bool errmsg // give error message when failing
+)
{
size_t cnt;
@@ -1351,6 +1475,15 @@ bool get_spec_reg(int regname, char_u **argp, bool *allocated, bool errmsg)
case '_': // black hole: always empty
*argp = (char_u *)"";
return true;
+
+ default: /* User-defined registers. */
+ if (get_userreg(regname) != -1) {
+ if (!curbuf->b_p_urf || strlen((char *) curbuf->b_p_urf) == 0)
+ return false;
+ eval_urf_put(curbuf->b_p_urf, regname, argp);
+ *allocated = true;
+ return true;
+ }
}
return false;
@@ -1397,14 +1530,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
}
/*
@@ -1502,7 +1635,7 @@ int op_delete(oparg_T *oap)
if (oap->regname != 0 || 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;
}
@@ -2510,7 +2643,7 @@ int op_change(oparg_T *oap)
*/
void init_yank(void)
{
- memset(&(y_regs[0]), 0, sizeof(y_regs));
+ init_yankmap(&y_regs.inner);
}
#if defined(EXITFREE)
@@ -2519,7 +2652,7 @@ void clear_registers(void)
int i;
for (i = 0; i < NUM_REGISTERS; i++) {
- free_register(&y_regs[i]);
+ free_register(get_global_reg(i));
}
}
@@ -2563,6 +2696,11 @@ bool op_yank(oparg_T *oap, bool message, int deleting)
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) {
+ return eval_yank_userreg(curbuf->b_p_urf, oap->regname, reg) != -1;
+ }
+
// op_delete will set_clipboard and do_autocmd
if (!deleting) {
set_clipboard(oap->regname, reg);
@@ -3017,7 +3155,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
if (insert_string != NULL) {
y_type = kMTCharWise;
- if (regname == '=') {
+ if (regname == '=' || get_userreg(regname) != -1) {
/* For the = register we need to split the string at NL
* characters.
* Loop twice: count the number of lines and save them. */
@@ -3676,7 +3814,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);
}
/*
@@ -3718,10 +3856,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);
@@ -5406,6 +5544,10 @@ static void finish_write_reg(int name, yankreg_T *reg, yankreg_T *old_y_previous
// Send text of clipboard register to the clipboard.
set_clipboard(name, reg);
+ if (get_userreg(name) != -1) {
+ eval_yank_userreg(curbuf->b_p_urf, name, reg);
+ }
+
// ':let @" = "val"' should change the meaning of the "" register
if (name != '"') {
y_previous = old_y_previous;
@@ -6784,7 +6926,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 == '*' ? CB_UNNAMED : CB_UNNAMEDPLUS))) {
clipboard_needs_update = false;
}
@@ -6801,10 +6943,10 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
if (cb_flags & CB_UNNAMEDPLUS) {
*name = (cb_flags & CB_UNNAMED && 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;
}
@@ -7121,11 +7263,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`.
@@ -7137,30 +7279,31 @@ 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++;
- }
- if (iter_reg - &(regs[0]) == NUM_SAVED_REGISTERS || reg_empty(iter_reg)) {
- return 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;
+ 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_idx >= NUM_SAVED_REGISTERS || reg_empty(get_reg(regs, iter_idx)))
+ return ITER_REGISTER_NULL;
+
+ *reg = *get_reg(regs, iter_idx);
+ *name = (char)get_register_name((int)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_register_T)(iter_idx + 1);
}
}
- return NULL;
+
+ return ITER_REGISTER_NULL;
}
/// Get a number of non-empty registers
@@ -7168,8 +7311,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++;
}
}
@@ -7189,11 +7332,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;
}
@@ -7209,7 +7352,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
@@ -7225,7 +7368,7 @@ bool op_reg_set_previous(const char name)
return false;
}
- y_previous = &y_regs[i];
+ y_previous = get_global_reg(i);
return true;
}