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.c485
1 files changed, 309 insertions, 176 deletions
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 6bf3f6036f..32b37b6458 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -60,6 +60,7 @@
#define DELETION_REGISTER 36
#define CLIP_REGISTER 37
+# define CB_UNNAMEDMASK (CB_UNNAMED | CB_UNNAMEDPLUS)
/*
* Each yank register is an array of pointers to lines.
*/
@@ -74,6 +75,7 @@ static struct yankreg *y_current; /* ptr to current yankreg */
static int y_append; /* TRUE when appending */
static struct yankreg *y_previous = NULL; /* ptr to last written yankreg */
+static bool clipboard_didwarn_unnamed = false;
/*
* structure used by block_prep, op_delete and op_yank for blockwise operators
* also op_change, op_shift, op_insert, op_replace - AKelly
@@ -751,7 +753,8 @@ void get_yank_register(int regname, int writing)
int i;
y_append = FALSE;
- if ((regname == 0 || regname == '"') && !writing && y_previous != NULL) {
+ int unnamedclip = cb_flags & CB_UNNAMEDMASK;
+ if ((regname == 0 || regname == '"') && !unnamedclip && !writing && y_previous != NULL) {
y_current = y_previous;
return;
}
@@ -1302,18 +1305,6 @@ cmdline_paste_reg (
return OK;
}
-bool adjust_clipboard_register(int *rp)
-{
- // If no reg. specified and 'unnamedclip' is set, use the
- // clipboard register.
- if (*rp == 0 && p_unc && eval_has_provider("clipboard")) {
- *rp = '+';
- return true;
- }
-
- return false;
-}
-
/*
* Handle a delete operation.
*
@@ -1328,7 +1319,6 @@ int op_delete(oparg_T *oap)
struct block_def bd;
linenr_T old_lcount = curbuf->b_ml.ml_line_count;
int did_yank = FALSE;
- int orig_regname = oap->regname;
if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to do */
return OK;
@@ -1342,8 +1332,6 @@ int op_delete(oparg_T *oap)
return FAIL;
}
- bool adjusted = adjust_clipboard_register(&oap->regname);
-
if (has_mbyte)
mb_adjust_opend(oap);
@@ -1393,9 +1381,10 @@ int op_delete(oparg_T *oap)
* register. For the black hole register '_' don't yank anything.
*/
if (oap->regname != '_') {
- if (oap->regname != 0) {
+ bool unnamedclip = oap->regname == 0 && (cb_flags & CB_UNNAMEDMASK);
+ if (oap->regname != 0 || unnamedclip) {
/* check for read-only register */
- if (!valid_yank_reg(oap->regname, TRUE)) {
+ if (!( valid_yank_reg(oap->regname, TRUE) || unnamedclip )) {
beep_flush();
return OK;
}
@@ -1407,10 +1396,8 @@ int op_delete(oparg_T *oap)
/*
* Put deleted text into register 1 and shift number registers if the
* delete contains a line break, or when a regname has been specified.
- * Use the register name from before adjust_clip_reg() may have
- * changed it.
*/
- if (orig_regname != 0 || oap->motion_type == MLINE
+ if (oap->regname != 0 || oap->motion_type == MLINE
|| oap->line_count > 1 || oap->use_reg_one) {
y_current = &y_regs[9];
free_yank_all(); /* free register nine */
@@ -1424,9 +1411,7 @@ int op_delete(oparg_T *oap)
/* Yank into small delete register when no named register specified
* and the delete is within one line. */
- if ((
- adjusted ||
- oap->regname == 0) && oap->motion_type != MLINE
+ if (oap->regname == 0 && oap->motion_type != MLINE
&& oap->line_count == 1) {
oap->regname = '-';
get_yank_register(oap->regname, TRUE);
@@ -2623,7 +2608,6 @@ do_put (
int allocated = FALSE;
long cnt;
- adjust_clipboard_register(&regname);
get_clipboard(regname);
if (flags & PUT_FIXINDENT)
@@ -3215,7 +3199,6 @@ void ex_display(exarg_T *eap)
)
continue; /* did not ask for this register */
- adjust_clipboard_register(&name);
get_clipboard(name);
if (i == -1) {
@@ -4671,30 +4654,43 @@ char_u get_reg_type(int regname, long *reglen)
return MAUTO;
}
-/*
- * Return the contents of a register as a single allocated string.
- * Used for "@r" in expressions and for getreg().
- * Returns NULL for error.
- */
-char_u *
-get_reg_contents (
- int regname,
- int allowexpr, /* allow "=" register */
- int expr_src /* get expression for "=" register */
-)
+/// When `flags` has `kGRegList` return a list with text `s`.
+/// Otherwise just return `s`.
+///
+/// Returns a void * for use in get_reg_contents().
+static void *get_reg_wrap_one_line(char_u *s, int flags)
+{
+ if (!(flags & kGRegList)) {
+ return s;
+ }
+ list_T *list = list_alloc();
+ list_append_string(list, NULL, -1);
+ list->lv_first->li_tv.vval.v_string = s;
+ return list;
+}
+
+/// Gets the contents of a register.
+/// @remark Used for `@r` in expressions and for `getreg()`.
+///
+/// @param regname The register.
+/// @param flags see @ref GRegFlags
+///
+/// @returns The contents of the register as an allocated string.
+/// @returns A linked list when `flags` contains @ref kGRegList.
+/// @returns NULL for error.
+void *get_reg_contents(int regname, int flags)
{
long i;
- char_u *retval;
- int allocated;
- /* Don't allow using an expression register inside an expression */
+ // Don't allow using an expression register inside an expression.
if (regname == '=') {
- if (allowexpr) {
- if (expr_src)
- return get_expr_line_src();
- return get_expr_line();
+ if (flags & kGRegNoExpr) {
+ return NULL;
}
- return NULL;
+ if (flags & kGRegExprSrc) {
+ return get_reg_wrap_one_line(get_expr_line_src(), flags);
+ }
+ return get_reg_wrap_one_line(get_expr_line(), flags);
}
if (regname == '@') /* "@@" is used for unnamed register */
@@ -4706,18 +4702,30 @@ get_reg_contents (
get_clipboard(regname);
+ char_u *retval;
+ int allocated;
if (get_spec_reg(regname, &retval, &allocated, FALSE)) {
if (retval == NULL)
return NULL;
- if (!allocated)
- retval = vim_strsave(retval);
- return retval;
+ if (allocated) {
+ return get_reg_wrap_one_line(retval, flags);
+ }
+ return get_reg_wrap_one_line(vim_strsave(retval), flags);
}
get_yank_register(regname, FALSE);
if (y_current->y_array == NULL)
return NULL;
+ if (flags & kGRegList) {
+ list_T *list = list_alloc();
+ for (int i = 0; i < y_current->y_size; ++i) {
+ list_append_string(list, y_current->y_array[i], -1);
+ }
+
+ return list;
+ }
+
/*
* Compute length of resulting string.
*/
@@ -4754,17 +4762,77 @@ get_reg_contents (
return retval;
}
+static bool init_write_reg(int name, struct yankreg **old_y_previous,
+ struct yankreg **old_y_current, int must_append)
+{
+ if (!valid_yank_reg(name, true)) { // check for valid reg name
+ emsg_invreg(name);
+ return false;
+ }
+
+ // Don't want to change the current (unnamed) register.
+ *old_y_previous = y_previous;
+ *old_y_current = y_current;
+
+ get_yank_register(name, true);
+ if (!y_append && !must_append) {
+ free_yank_all();
+ }
+ return true;
+}
+
+static void finish_write_reg(int name, struct yankreg *old_y_previous,
+ struct yankreg *old_y_current)
+{
+ // Send text of clipboard register to the clipboard.
+ set_clipboard(name);
+
+ // ':let @" = "val"' should change the meaning of the "" register
+ if (name != '"') {
+ y_previous = old_y_previous;
+ }
+ y_current = old_y_current;
+}
+
/// write_reg_contents - store `str` in register `name`
///
/// @see write_reg_contents_ex
-void write_reg_contents(int name,
- const char_u *str,
- ssize_t len,
+void write_reg_contents(int name, const char_u *str, ssize_t len,
int must_append)
{
write_reg_contents_ex(name, str, len, must_append, MAUTO, 0L);
}
+void write_reg_contents_lst(int name, char_u **strings, int maxlen,
+ int must_append, int yank_type, long block_len)
+{
+ if (name == '/' || name == '=') {
+ char_u *s = strings[0];
+ if (strings[0] == NULL) {
+ s = (char_u *)"";
+ } else if (strings[1] != NULL) {
+ EMSG(_("E883: search pattern and expression register may not "
+ "contain two or more lines"));
+ return;
+ }
+ write_reg_contents_ex(name, s, -1, must_append, yank_type, block_len);
+ return;
+ }
+
+ // black hole: nothing to do
+ if (name == '_') {
+ return;
+ }
+
+ struct yankreg *old_y_previous, *old_y_current;
+ if (!init_write_reg(name, &old_y_previous, &old_y_current, must_append)) {
+ return;
+ }
+
+ str_to_reg(y_current, yank_type, (char_u *) strings, -1, block_len, true);
+ finish_write_reg(name, old_y_previous, old_y_current);
+}
+
/// write_reg_contents_ex - store `str` in register `name`
///
/// If `str` ends in '\n' or '\r', use linewise, otherwise use
@@ -4791,8 +4859,6 @@ void write_reg_contents_ex(int name,
int yank_type,
long block_len)
{
- struct yankreg *old_y_previous, *old_y_current;
-
if (len < 0) {
len = (ssize_t) STRLEN(str);
}
@@ -4826,28 +4892,16 @@ void write_reg_contents_ex(int name,
return;
}
- if (!valid_yank_reg(name, TRUE)) { /* check for valid reg name */
- emsg_invreg(name);
+ if (name == '_') { // black hole: nothing to do
return;
}
- if (name == '_') /* black hole: nothing to do */
- return;
-
- /* Don't want to change the current (unnamed) register */
- old_y_previous = y_previous;
- old_y_current = y_current;
-
- get_yank_register(name, TRUE);
- if (!y_append && !must_append)
- free_yank_all();
- str_to_reg(y_current, yank_type, str, len, block_len);
-
-
- /* ':let @" = "val"' should change the meaning of the "" register */
- if (name != '"')
- y_previous = old_y_previous;
- y_current = old_y_current;
+ struct yankreg *old_y_previous, *old_y_current;
+ if (!init_write_reg(name, &old_y_previous, &old_y_current, must_append)) {
+ return;
+ }
+ str_to_reg(y_current, yank_type, str, len, block_len, false);
+ finish_write_reg(name, old_y_previous, old_y_current);
}
/// str_to_reg - Put a string into a register.
@@ -4856,100 +4910,100 @@ void write_reg_contents_ex(int name,
///
/// @param y_ptr pointer to yank register
/// @param yank_type MCHAR, MLINE, MBLOCK or MAUTO
-/// @param str string to put in register
-/// @param len length of the string
-/// @param blocklen width of visual block
-static void str_to_reg(struct yankreg *y_ptr,
- int yank_type,
- const char_u *str,
- long len,
- long blocklen)
+/// @param str string or list of strings to put in register
+/// @param len length of the string (Ignored when str_list=true.)
+/// @param blocklen width of visual block, or -1 for "I don't know."
+/// @param str_list True if str is `char_u **`.
+static void str_to_reg(struct yankreg *y_ptr, int yank_type, const char_u *str,
+ size_t len, colnr_T blocklen, bool str_list)
+ FUNC_ATTR_NONNULL_ALL
{
- int type; /* MCHAR, MLINE or MBLOCK */
- int lnum;
- long start;
- long i;
- int extra;
- size_t newlines; /* number of lines added */
- int extraline = 0; /* extra line at the end */
- int append = FALSE; /* append to last line in register */
- char_u *s;
- char_u **pp;
- long maxlen;
-
- if (y_ptr->y_array == NULL) /* NULL means empty register */
+ if (y_ptr->y_array == NULL) { // NULL means empty register
y_ptr->y_size = 0;
+ }
- if (yank_type == MAUTO)
- type = ((len > 0 && (str[len - 1] == NL || str[len - 1] == CAR))
+ int type = yank_type; // MCHAR, MLINE or MBLOCK
+ if (yank_type == MAUTO) {
+ type = ((str_list ||
+ (len > 0 && (str[len - 1] == NL || str[len - 1] == CAR)))
? MLINE : MCHAR);
- else
- type = yank_type;
-
- /*
- * Count the number of lines within the string
- */
- newlines = 0;
- for (i = 0; i < len; i++)
- if (str[i] == '\n')
- ++newlines;
- if (type == MCHAR || len == 0 || str[len - 1] != '\n') {
- extraline = 1;
- ++newlines; /* count extra newline at the end */
}
- if (y_ptr->y_size > 0 && y_ptr->y_type == MCHAR) {
- append = TRUE;
- --newlines; /* uncount newline when appending first line */
+
+ size_t newlines = 0;
+ bool extraline = false; // extra line at the end
+ bool append = false; // append to last line in register
+
+ // Count the number of lines within the string
+ if (str_list) {
+ for (char_u **ss = (char_u **) str; *ss != NULL; ++ss) {
+ newlines++;
+ }
+ } else {
+ newlines = memcnt(str, '\n', len);
+ if (type == MCHAR || len == 0 || str[len - 1] != '\n') {
+ extraline = 1;
+ ++newlines; // count extra newline at the end
+ }
+ if (y_ptr->y_size > 0 && y_ptr->y_type == MCHAR) {
+ append = true;
+ --newlines; // uncount newline when appending first line
+ }
}
- /*
- * Allocate an array to hold the pointers to the new register lines.
- * If the register was not empty, move the existing lines to the new array.
- */
- pp = xcalloc((y_ptr->y_size + newlines), sizeof(char_u *));
- for (lnum = 0; lnum < y_ptr->y_size; ++lnum)
- pp[lnum] = y_ptr->y_array[lnum];
- free(y_ptr->y_array);
+
+ // Grow the register array to hold the pointers to the new lines.
+ char_u **pp = xrealloc(y_ptr->y_array,
+ (y_ptr->y_size + newlines) * sizeof(char_u *));
y_ptr->y_array = pp;
- maxlen = 0;
- /*
- * Find the end of each line and save it into the array.
- */
- for (start = 0; start < len + extraline; start += i + 1) {
- // Let i represent the length of one line.
- const char_u *p = str + start;
- i = (char_u *)xmemscan(p, '\n', len - start) - p;
- if (i > maxlen)
- maxlen = i;
- if (append) {
- --lnum;
- extra = (int)STRLEN(y_ptr->y_array[lnum]);
- } else
- extra = 0;
- s = xmalloc(i + extra + 1);
- if (extra)
- memmove(s, y_ptr->y_array[lnum], (size_t)extra);
- if (append)
- free(y_ptr->y_array[lnum]);
- if (i)
- memmove(s + extra, str + start, (size_t)i);
- extra += i;
- s[extra] = NUL;
- y_ptr->y_array[lnum++] = s;
- while (--extra >= 0) {
- if (*s == NUL)
- *s = '\n'; /* replace NUL with newline */
- ++s;
+ linenr_T lnum = y_ptr->y_size; // The current line number.
+
+ // If called with `blocklen < 0`, we have to update the yank reg's width.
+ size_t maxlen = 0;
+
+ // Find the end of each line and save it into the array.
+ if (str_list) {
+ for (char_u **ss = (char_u **) str; *ss != NULL; ++ss, ++lnum) {
+ size_t ss_len = STRLEN(*ss);
+ pp[lnum] = xmemdupz(*ss, ss_len);
+ if (ss_len > maxlen) {
+ maxlen = ss_len;
+ }
+ }
+ } else {
+ size_t line_len;
+ for (const char_u *start = str, *end = str + len;
+ start < end + extraline;
+ start += line_len + 1, lnum++) {
+ line_len = (const char_u *) xmemscan(start, '\n', end - start) - start;
+ if (line_len > maxlen) {
+ maxlen = line_len;
+ }
+
+ // When appending, copy the previous line and free it after.
+ size_t extra = append ? STRLEN(pp[--lnum]) : 0;
+ char_u *s = xmallocz(line_len + extra);
+ memcpy(s, pp[lnum], extra);
+ memcpy(s + extra, start, line_len);
+ ssize_t s_len = extra + line_len;
+
+ if (append) {
+ free(pp[lnum]);
+ append = false; // only first line is appended
+ }
+ pp[lnum] = s;
+
+ // Convert NULs to '\n' to prevent truncation.
+ memchrsub(pp[lnum], NUL, '\n', s_len);
}
- append = FALSE; /* only first line is appended */
}
y_ptr->y_type = type;
y_ptr->y_size = lnum;
- if (type == MBLOCK)
- y_ptr->y_width = (blocklen < 0 ? maxlen - 1 : blocklen);
- else
+ if (type == MBLOCK) {
+ y_ptr->y_width = (blocklen == -1 ? (colnr_T) maxlen - 1 : blocklen);
+ } else {
y_ptr->y_width = 0;
+ }
}
void clear_oparg(oparg_T *oap)
@@ -5225,33 +5279,82 @@ static void free_register(struct yankreg *reg)
y_current = curr;
}
-static void copy_register(struct yankreg *dest, struct yankreg *src)
-{
- free_register(dest);
- *dest = *src;
- dest->y_array = xcalloc(src->y_size, sizeof(uint8_t *));
- for (int j = 0; j < src->y_size; ++j) {
- dest->y_array[j] = (uint8_t *)xstrdup((char *)src->y_array[j]);
+// return target register
+static int adjust_clipboard_name(int *name) {
+ if (*name == '*' || *name == '+') {
+ if(!eval_has_provider("clipboard")) {
+ EMSG("clipboard: provider is not available");
+ return -1;
+ }
+ return CLIP_REGISTER;
+ } else if (*name == NUL && (cb_flags & (CB_UNNAMED | CB_UNNAMEDPLUS))) {
+ if(!eval_has_provider("clipboard")) {
+ if (!clipboard_didwarn_unnamed) {
+ msg((char_u*)"clipboard: provider not available, ignoring clipboard=unnamed[plus]");
+ clipboard_didwarn_unnamed = true;
+ }
+ return -1;
+ }
+ if (cb_flags & CB_UNNAMEDPLUS) {
+ *name = '+';
+ } else {
+ *name = '*';
+ }
+ return 0; //unnamed
}
+ // don't do anything for other register names
+ return -1;
}
static void get_clipboard(int name)
{
- if (!(name == '*' || name == '+'
- || (p_unc && !name && eval_has_provider("clipboard")))) {
+ int ireg = adjust_clipboard_name(&name);
+ if (ireg < 0) {
return;
}
- struct yankreg *reg = &y_regs[CLIP_REGISTER];
+ struct yankreg *reg = &y_regs[ireg];
free_register(reg);
+
list_T *args = list_alloc();
+ char_u regname = name;
+ list_append_string(args, &regname, 1);
+
typval_T result = eval_call_provider("clipboard", "get", args);
if (result.v_type != VAR_LIST) {
goto err;
}
- list_T *lines = result.vval.v_list;
+ list_T *res = result.vval.v_list, *lines = NULL;
+ if (res->lv_len == 2 && res->lv_first->li_tv.v_type == VAR_LIST) {
+ lines = res->lv_first->li_tv.vval.v_list;
+ if (res->lv_last->li_tv.v_type != VAR_STRING) {
+ goto err;
+ }
+ char_u* regtype = res->lv_last->li_tv.vval.v_string;
+ if (regtype == NULL || strlen((char*)regtype) != 1) {
+ goto err;
+ }
+ switch (regtype[0]) {
+ case 'v': case 'c':
+ reg->y_type = MCHAR;
+ break;
+ case 'V': case 'l':
+ reg->y_type = MLINE;
+ break;
+ case 'b': case Ctrl_V:
+ reg->y_type = MBLOCK;
+ break;
+ default:
+ goto err;
+ }
+ } else {
+ lines = res;
+ // provider did not specify regtype, calculate it below
+ reg->y_type = MAUTO;
+ }
+
reg->y_array = xcalloc(lines->lv_len, sizeof(uint8_t *));
reg->y_size = lines->lv_len;
@@ -5263,9 +5366,23 @@ static void get_clipboard(int name)
reg->y_array[i++] = (uint8_t *)xstrdup((char *)li->li_tv.vval.v_string);
}
- if (!name && p_unc) {
- // copy to the unnamed register
- copy_register(&y_regs[0], reg);
+ if (reg->y_type == MAUTO) {
+ if (reg->y_size > 0 && strlen((char*)reg->y_array[reg->y_size-1]) == 0) {
+ reg->y_type = MLINE;
+ free(reg->y_array[reg->y_size-1]);
+ reg->y_size--;
+ } else {
+ reg->y_type = MCHAR;
+ }
+ } else if (reg->y_type == MBLOCK) {
+ int maxlen = 0;
+ for (int i = 0; i < reg->y_size; i++) {
+ int rowlen = STRLEN(reg->y_array[i]);
+ if (rowlen > maxlen) {
+ maxlen = rowlen;
+ }
+ }
+ reg->y_width = maxlen-1;
}
return;
@@ -5279,22 +5396,17 @@ err:
}
reg->y_array = NULL;
reg->y_size = 0;
- EMSG("Clipboard provider returned invalid data");
+ EMSG("clipboard: provider returned invalid data");
}
static void set_clipboard(int name)
{
- if (!(name == '*' || name == '+'
- || (p_unc && !name && eval_has_provider("clipboard")))) {
+ int ireg = adjust_clipboard_name(&name);
+ if (ireg < 0) {
return;
}
- struct yankreg *reg = &y_regs[CLIP_REGISTER];
-
- if (!name && p_unc) {
- // copy from the unnamed register
- copy_register(reg, &y_regs[0]);
- }
+ struct yankreg *reg = &y_regs[ireg];
list_T *lines = list_alloc();
@@ -5302,5 +5414,26 @@ static void set_clipboard(int name)
list_append_string(lines, reg->y_array[i], -1);
}
- (void)eval_call_provider("clipboard", "set", lines);
+ list_T *args = list_alloc();
+ list_append_list(args, lines);
+
+ char_u regtype;
+ switch (reg->y_type) {
+ case MLINE:
+ regtype = 'V';
+ list_append_string(lines, (char_u*)"", 0);
+ break;
+ case MCHAR:
+ regtype = 'v';
+ break;
+ case MBLOCK:
+ regtype = 'b';
+ break;
+ }
+ list_append_string(args, &regtype, 1);
+
+ char_u regname = name;
+ list_append_string(args, &regname, 1);
+
+ (void)eval_call_provider("clipboard", "set", args);
}