diff options
Diffstat (limited to 'src/nvim/ops.c')
-rw-r--r-- | src/nvim/ops.c | 959 |
1 files changed, 663 insertions, 296 deletions
diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 3a4e87edf7..9b969a2337 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -9,13 +9,16 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/types.h> +#include <uv.h> #include "nvim/api/private/defs.h" #include "nvim/ascii_defs.h" #include "nvim/assert_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" +#include "nvim/buffer_updates.h" #include "nvim/change.h" #include "nvim/charset.h" #include "nvim/cursor.h" @@ -23,23 +26,30 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/userfunc.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_getln.h" #include "nvim/extmark.h" #include "nvim/fold.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/getchar_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/indent.h" #include "nvim/indent_c.h" #include "nvim/keycodes.h" #include "nvim/macros_defs.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" +#include "nvim/memline_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/mouse.h" @@ -54,16 +64,34 @@ #include "nvim/plines.h" #include "nvim/search.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/strings.h" #include "nvim/terminal.h" #include "nvim/textformat.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/undo.h" #include "nvim/vim_defs.h" #include "nvim/window.h" +#include "nvim/yankmap.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 @@ -73,25 +101,6 @@ static bool clipboard_delay_update = false; // delay clipboard update static bool clipboard_needs_update = false; // clipboard was updated static bool clipboard_didwarn = false; -// structure used by block_prep, op_delete and op_yank for blockwise operators -// also op_change, op_shift, op_insert, op_replace - AKelly -struct block_def { - int startspaces; // 'extra' cols before first char - int endspaces; // 'extra' cols after last char - int textlen; // chars in block - char *textstart; // pointer to 1st char (partially) in block - colnr_T textcol; // index of chars (partially) in block - colnr_T start_vcol; // start col of 1st char wholly inside block - colnr_T end_vcol; // start col of 1st char wholly after block - int is_short; // true if line is too short to fit in block - int is_MAX; // true if curswant==MAXCOL when starting - int is_oneChar; // true if block within one character - int pre_whitesp; // screen cols of ws before block - int pre_whitesp_c; // chars of ws before block - colnr_T end_char_vcols; // number of vcols of post-block char - colnr_T start_char_vcols; // number of vcols of pre-block char -}; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ops.c.generated.h" #endif @@ -213,9 +222,8 @@ int get_extra_op_char(int optype) } /// handle a shift operation -void op_shift(oparg_T *oap, int curs_top, int amount) +void op_shift(oparg_T *oap, bool curs_top, int amount) { - int i; int block_col = 0; if (u_save((linenr_T)(oap->start.lnum - 1), @@ -227,7 +235,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount) block_col = curwin->w_cursor.col; } - for (i = oap->line_count - 1; i >= 0; i--) { + for (int i = oap->line_count - 1; i >= 0; i--) { int first_char = (uint8_t)(*get_cursor_line_ptr()); if (first_char == NUL) { // empty line curwin->w_cursor.col = 0; @@ -288,7 +296,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount) /// leaves cursor on first blank in the line. /// /// @param call_changed_bytes call changed_bytes() -void shift_line(int left, int round, int amount, int call_changed_bytes) +void shift_line(bool left, bool round, int amount, int call_changed_bytes) { const int sw_val = get_sw_value_indent(curbuf); @@ -324,7 +332,7 @@ void shift_line(int left, int round, int amount, int call_changed_bytes) if (State & VREPLACE_FLAG) { change_indent(INDENT_SET, count, false, NUL, call_changed_bytes); } else { - (void)set_indent(count, call_changed_bytes ? SIN_CHANGED : 0); + set_indent(count, call_changed_bytes ? SIN_CHANGED : 0); } } @@ -378,19 +386,21 @@ static void shift_block(oparg_T *oap, int amount) } // TODO(vim): is passing bd.textstart for start of the line OK? - chartabsize_T cts; - init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, - bd.start_vcol, bd.textstart, bd.textstart); - while (ascii_iswhite(*cts.cts_ptr)) { - incr = lbr_chartabsize_adv(&cts); + CharsizeArg csarg; + CSType cstype = init_charsize_arg(&csarg, curwin, curwin->w_cursor.lnum, bd.textstart); + StrCharInfo ci = utf_ptr2StrCharInfo(bd.textstart); + int vcol = bd.start_vcol; + while (ascii_iswhite(ci.chr.value)) { + incr = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg).width; + ci = utfc_next(ci); total += incr; - cts.cts_vcol += incr; + vcol += incr; } - bd.textstart = cts.cts_ptr; - bd.start_vcol = cts.cts_vcol; - clear_chartabsize_arg(&cts); + bd.textstart = ci.ptr; + bd.start_vcol = vcol; - int tabs = 0, spaces = 0; + int tabs = 0; + int spaces = 0; // OK, now total=all the VWS reqd, and textstart points at the 1st // non-ws char in the block. if (!curbuf->b_p_et) { @@ -438,16 +448,13 @@ static void shift_block(oparg_T *oap, int amount) // The character's column is in "bd.start_vcol". colnr_T non_white_col = bd.start_vcol; - chartabsize_T cts; - init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, - non_white_col, bd.textstart, non_white); - while (ascii_iswhite(*cts.cts_ptr)) { - incr = lbr_chartabsize_adv(&cts); - cts.cts_vcol += incr; + CharsizeArg csarg; + CSType cstype = init_charsize_arg(&csarg, curwin, curwin->w_cursor.lnum, bd.textstart); + while (ascii_iswhite(*non_white)) { + incr = win_charsize(cstype, non_white_col, non_white, (uint8_t)(*non_white), &csarg).width; + non_white_col += incr; + non_white++; } - non_white_col = cts.cts_vcol; - non_white = cts.cts_ptr; - clear_chartabsize_arg(&cts); const colnr_T block_space_width = non_white_col - oap->start_vcol; // We will shift by "total" or "block_space_width", whichever is less. @@ -468,19 +475,17 @@ static void shift_block(oparg_T *oap, int amount) if (bd.startspaces) { verbatim_copy_width -= bd.start_char_vcols; } - init_chartabsize_arg(&cts, curwin, 0, verbatim_copy_width, - bd.textstart, verbatim_copy_end); - while (cts.cts_vcol < destination_col) { - incr = lbr_chartabsize(&cts); - if (cts.cts_vcol + incr > destination_col) { + cstype = init_charsize_arg(&csarg, curwin, 0, bd.textstart); + StrCharInfo ci = utf_ptr2StrCharInfo(verbatim_copy_end); + while (verbatim_copy_width < destination_col) { + incr = win_charsize(cstype, verbatim_copy_width, ci.ptr, ci.chr.value, &csarg).width; + if (verbatim_copy_width + incr > destination_col) { break; } - cts.cts_vcol += incr; - MB_PTR_ADV(cts.cts_ptr); + verbatim_copy_width += incr; + ci = utfc_next(ci); } - verbatim_copy_width = cts.cts_vcol; - verbatim_copy_end = cts.cts_ptr; - clear_chartabsize_arg(&cts); + verbatim_copy_end = ci.ptr; // If "destination_col" is different from the width of the initial // part of the line that will be copied, it means we encountered a tab @@ -520,7 +525,7 @@ static void shift_block(oparg_T *oap, int amount) /// Insert string "s" (b_insert ? before : after) block :AKelly /// Caller must prepare for undo. -static void block_insert(oparg_T *oap, char *s, int b_insert, struct block_def *bdp) +static void block_insert(oparg_T *oap, char *s, bool b_insert, struct block_def *bdp) { int ts_val; int count = 0; // extra spaces to replace a cut TAB @@ -771,6 +776,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. @@ -787,12 +810,156 @@ 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 = (char**) xcalloc(sizeof(char*), 1); + yankreg->y_array[0] = strdup(tv.vval.v_string); + } else if (tv.v_type == VAR_LIST) { + yankreg->y_array = + (char**) xcalloc(sizeof(char*), (size_t) tv_list_len(tv.vval.v_list)); + + i = 0; + TV_LIST_ITER_CONST(tv.vval.v_list, li, { + if (li->li_tv.v_type == VAR_STRING) { + yankreg->y_array[i] = strdup(tv_get_string(&li->li_tv)); + } else { + yankreg->y_array[i] = NULL; + } + ++ 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 = 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 = (char**) xcalloc(sizeof(char*), sz); + yankreg->y_size = sz; + i = 0; + TV_LIST_ITER_CONST(val->vval.v_list, li, { + yankreg->y_array[i] = strdup(tv_get_string(&li->li_tv)); + i ++; + }); + break; + + default: + yankreg->y_type = kMTCharWise; + yankreg->y_size = 1; + + if (val->vval.v_string) { + yankreg->y_array = (char**) xcalloc(sizeof(char*), 1); + yankreg->y_array[0] = strdup(tv_get_string(val)); + } 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! @@ -815,9 +982,15 @@ yankreg_T *get_yank_register(int regname, int mode) { yankreg_T *reg; - if (mode == YREG_PASTE && get_clipboard(regname, ®, false)) { + if ((mode == YREG_PASTE || mode == YREG_PUT) + && get_clipboard(regname, ®, false)) { // reg is set to clipboard contents. return reg; + } else if (mode == YREG_PUT && (regname == '*' || regname == '+')) { + // in case clipboard not available and we aren't actually pasting, + // return an empty register + static yankreg_T empty_reg = { .y_array = NULL }; + return &empty_reg; } else if (mode != YREG_YANK && (regname == 0 || regname == '"' || regname == '*' || regname == '+') && y_previous != NULL) { @@ -830,7 +1003,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 @@ -856,7 +1033,7 @@ yankreg_T *copy_register(int name) if (copy->y_size == 0) { copy->y_array = NULL; } else { - copy->y_array = xcalloc(copy->y_size, sizeof(char *)); + copy->y_array = (char**) xcalloc(copy->y_size, sizeof(char *)); for (size_t i = 0; i < copy->y_size; i++) { copy->y_array[i] = xstrdup(reg->y_array[i]); } @@ -887,8 +1064,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; @@ -909,14 +1085,15 @@ int do_record(int c) if (p != NULL) { // Remove escaping for K_SPECIAL in multi-byte chars. vim_unescape_ks(p); - (void)tv_dict_add_str(dict, S_LEN("regcontents"), p); + tv_dict_add_str(dict, S_LEN("regcontents"), p); } // Name of requested register, or empty string for unnamed operation. - char buf[NUMBUFLEN + 2]; - buf[0] = (char)regname; - buf[1] = NUL; - (void)tv_dict_add_str(dict, S_LEN("regname"), buf); + 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); // Get the recorded key hits. K_SPECIAL will be escaped, this @@ -992,6 +1169,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; } @@ -1159,6 +1339,7 @@ int do_execreg(int regname, int colon, int addcr, int silent) } } reg_executing = regname == 0 ? '"' : regname; // disable the 'q' command + pending_end_reg_executing = false; } return retval; } @@ -1266,9 +1447,24 @@ int insert_reg(int regname, bool literally_arg) } else { for (size_t i = 0; i < reg->y_size; i++) { if (regname == '-') { + Direction dir = BACKWARD; + if ((State & REPLACE_FLAG) != 0) { + pos_T curpos; + if (u_save_cursor() == FAIL) { + return FAIL; + } + del_chars(mb_charlen(reg->y_array[0]), true); + curpos = curwin->w_cursor; + if (oneright() == FAIL) { + // hit end of line, need to put forward (after the current position) + dir = FORWARD; + } + curwin->w_cursor = curpos; + } + AppendCharToRedobuff(Ctrl_R); AppendCharToRedobuff(regname); - do_put(regname, NULL, BACKWARD, 1, PUT_CURSEND); + do_put(regname, NULL, dir, 1, PUT_CURSEND); } else { stuffescaped(reg->y_array[i], literally); } @@ -1284,6 +1480,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], (long)strlen(yankreg->y_array[i])); + } + + 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". /// @@ -1367,6 +1647,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; @@ -1413,14 +1696,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. @@ -1495,7 +1778,7 @@ int op_delete(oparg_T *oap) // register. For the black hole register '_' don't yank anything. if (oap->regname != '_') { yankreg_T *reg = NULL; - int did_yank = false; + bool did_yank = false; if (oap->regname != 0) { // check for read-only register if (!valid_yank_reg(oap->regname, true)) { @@ -1513,7 +1796,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; } @@ -1695,8 +1978,8 @@ int op_delete(oparg_T *oap) } } - (void)del_bytes((colnr_T)n, !virtual_op, - oap->op_type == OP_DELETE && !oap->is_VIsual); + del_bytes((colnr_T)n, !virtual_op, + oap->op_type == OP_DELETE && !oap->is_VIsual); } else { // delete characters between lines pos_T curpos; @@ -1722,10 +2005,10 @@ int op_delete(oparg_T *oap) // delete from start of line until op_end int n = (oap->end.col + 1 - !oap->inclusive); curwin->w_cursor.col = 0; - (void)del_bytes((colnr_T)n, !virtual_op, - oap->op_type == OP_DELETE && !oap->is_VIsual); + del_bytes((colnr_T)n, !virtual_op, + oap->op_type == OP_DELETE && !oap->is_VIsual); curwin->w_cursor = curpos; // restore curwin->w_cursor - (void)do_join(2, false, false, false, false); + do_join(2, false, false, false, false); curbuf_splice_pending--; extmark_splice(curbuf, (int)startpos.lnum - 1, startpos.col, (int)oap->line_count - 1, n, deleted_bytes, @@ -1760,8 +2043,13 @@ static void mb_adjust_opend(oparg_T *oap) return; } - char *p = ml_get(oap->end.lnum); - oap->end.col += utf_cp_tail_off(p, p + oap->end.col); + const char *line = ml_get(oap->end.lnum); + const char *ptr = line + oap->end.col; + if (*ptr != NUL) { + ptr -= utf_head_off(line, ptr); + ptr += utfc_ptr2len(ptr) - 1; + oap->end.col = (colnr_T)(ptr - line); + } } /// Put character 'c' at position 'lp' @@ -1793,7 +2081,7 @@ static int op_replace(oparg_T *oap, int c) int n; struct block_def bd; char *after_p = NULL; - int had_ctrl_v_cr = false; + bool had_ctrl_v_cr = false; if ((curbuf->b_ml.ml_flags & ML_EMPTY) || oap->empty) { return OK; // nothing to do @@ -1815,11 +2103,6 @@ static int op_replace(oparg_T *oap, int c) // block mode replace if (oap->motion_type == kMTBlockWise) { - int numc; - int num_chars; - char *newp; - char *oldp; - colnr_T oldlen; bd.is_MAX = (curwin->w_curswant == MAXCOL); for (; curwin->w_cursor.lnum <= oap->end.lnum; curwin->w_cursor.lnum++) { curwin->w_cursor.col = 0; // make sure cursor position is valid @@ -1850,7 +2133,7 @@ static int op_replace(oparg_T *oap, int c) && !bd.is_oneChar && bd.end_char_vcols > 0) ? bd.end_char_vcols - 1 : 0; // Figure out how many characters to replace. - numc = oap->end_vcol - oap->start_vcol + 1; + int numc = oap->end_vcol - oap->start_vcol + 1; if (bd.is_short && (!virtual_op || bd.is_MAX)) { numc -= (oap->end_vcol - bd.end_vcol) + 1; } @@ -1866,11 +2149,11 @@ static int op_replace(oparg_T *oap, int c) } // Compute bytes needed, move character count to num_chars. - num_chars = numc; + int num_chars = numc; numc *= utf_char2len(c); - oldp = get_cursor_line_ptr(); - oldlen = (int)strlen(oldp); + char *oldp = get_cursor_line_ptr(); + colnr_T oldlen = (int)strlen(oldp); size_t newp_size = (size_t)bd.textcol + (size_t)bd.startspaces; if (had_ctrl_v_cr || (c != '\r' && c != '\n')) { @@ -1880,7 +2163,7 @@ static int op_replace(oparg_T *oap, int c) - bd.textcol - bd.textlen); } } - newp = xmallocz(newp_size); + char *newp = xmallocz(newp_size); // copy up to deleted part memmove(newp, oldp, (size_t)bd.textcol); oldp += bd.textcol + bd.textlen; @@ -1891,7 +2174,8 @@ static int op_replace(oparg_T *oap, int c) size_t after_p_len = 0; int col = oldlen - bd.textcol - bd.textlen + 1; assert(col >= 0); - int newrows = 0, newcols = 0; + int newrows = 0; + int newcols = 0; if (had_ctrl_v_cr || (c != '\r' && c != '\n')) { // strlen(newp) at this point int newp_len = bd.textcol + bd.startspaces; @@ -2030,7 +2314,7 @@ static int op_replace(oparg_T *oap, int c) void op_tilde(oparg_T *oap) { struct block_def bd; - int did_change = false; + bool did_change = false; if (u_save((linenr_T)(oap->start.lnum - 1), (linenr_T)(oap->end.lnum + 1)) == FAIL) { @@ -2040,11 +2324,9 @@ void op_tilde(oparg_T *oap) pos_T pos = oap->start; if (oap->motion_type == kMTBlockWise) { // Visual block mode for (; pos.lnum <= oap->end.lnum; pos.lnum++) { - int one_change; - block_prep(oap, &bd, pos.lnum, false); pos.col = bd.textcol; - one_change = swapchars(oap->op_type, &pos, bd.textlen); + bool one_change = swapchars(oap->op_type, &pos, bd.textlen); did_change |= one_change; } if (did_change) { @@ -2142,16 +2424,16 @@ bool swapchar(int op_type, pos_T *pos) return false; } - if (op_type == OP_UPPER && c == 0xdf) { + // ~ is OP_NOP, g~ is OP_TILDE, gU is OP_UPPER + if ((op_type == OP_UPPER || op_type == OP_NOP || op_type == OP_TILDE) && c == 0xdf) { pos_T sp = curwin->w_cursor; - // Special handling of German sharp s: change to "SS". + // Special handling for lowercase German sharp s (ß): convert to uppercase (ẞ). curwin->w_cursor = *pos; del_char(false); - ins_char('S'); - ins_char('S'); + ins_char(0x1E9E); curwin->w_cursor = sp; - inc(pos); + return true; } int nc = c; @@ -2270,7 +2552,7 @@ void op_insert(oparg_T *oap, int count1) pos_T t1 = oap->start; const pos_T start_insert = curwin->w_cursor; - (void)edit(NUL, false, (linenr_T)count1); + edit(NUL, false, (linenr_T)count1); // When a tab was inserted, and the characters in front of the tab // have been converted to a tab as well, the column of the cursor @@ -2472,9 +2754,6 @@ int op_change(oparg_T *oap) ins_len = (int)strlen(firstline) - pre_textlen; if (ins_len > 0) { - int offset; - char *newp; - char *oldp; // Subsequent calls to ml_get() flush the firstline data - take a // copy of the inserted text. char *ins_text = xmalloc((size_t)ins_len + 1); @@ -2489,16 +2768,16 @@ int op_change(oparg_T *oap) // initial coladd offset as part of "startspaces" if (bd.is_short) { vpos.lnum = linenr; - (void)getvpos(&vpos, oap->start_vcol); + getvpos(&vpos, oap->start_vcol); } else { vpos.coladd = 0; } - oldp = ml_get(linenr); - newp = xmalloc(strlen(oldp) + (size_t)vpos.coladd - + (size_t)ins_len + 1); + char *oldp = ml_get(linenr); + char *newp = xmalloc(strlen(oldp) + (size_t)vpos.coladd + + (size_t)ins_len + 1); // copy up to block start memmove(newp, oldp, (size_t)bd.textcol); - offset = bd.textcol; + int offset = bd.textcol; memset(newp + offset, ' ', (size_t)vpos.coladd); offset += vpos.coladd; memmove(newp + offset, ins_text, (size_t)ins_len); @@ -2520,11 +2799,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)); } } @@ -2570,6 +2858,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); @@ -2635,66 +2931,11 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) reg->y_array[y_idx] = xstrdup(ml_get(lnum)); break; - case kMTCharWise: { - colnr_T startcol = 0, endcol = MAXCOL; - int is_oneChar = false; - colnr_T cs, ce; - char *p = ml_get(lnum); - bd.startspaces = 0; - bd.endspaces = 0; - - if (lnum == oap->start.lnum) { - startcol = oap->start.col; - if (virtual_op) { - getvcol(curwin, &oap->start, &cs, NULL, &ce); - if (ce != cs && oap->start.coladd > 0) { - // Part of a tab selected -- but don't double-count it. - bd.startspaces = (ce - cs + 1) - oap->start.coladd; - if (bd.startspaces < 0) { - bd.startspaces = 0; - } - startcol++; - } - } - } - - if (lnum == oap->end.lnum) { - endcol = oap->end.col; - if (virtual_op) { - getvcol(curwin, &oap->end, &cs, NULL, &ce); - if (p[endcol] == NUL || (cs + oap->end.coladd < ce - // Don't add space for double-wide - // char; endcol will be on last byte - // of multi-byte char. - && utf_head_off(p, p + endcol) == 0)) { - if (oap->start.lnum == oap->end.lnum - && oap->start.col == oap->end.col) { - // Special case: inside a single char - is_oneChar = true; - bd.startspaces = oap->end.coladd - - oap->start.coladd + oap->inclusive; - endcol = startcol; - } else { - bd.endspaces = oap->end.coladd - + oap->inclusive; - endcol -= oap->inclusive; - } - } - } - } - if (endcol == MAXCOL) { - endcol = (colnr_T)strlen(p); - } - if (startcol > endcol - || is_oneChar) { - bd.textlen = 0; - } else { - bd.textlen = endcol - startcol + oap->inclusive; - } - bd.textstart = p + startcol; + case kMTCharWise: + charwise_block_prep(oap->start, oap->end, &bd, lnum, oap->inclusive); yank_copy_line(reg, &bd, y_idx, false); break; - } + // NOTREACHED case kMTUnknown: abort(); @@ -2748,7 +2989,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 @@ -2818,6 +3063,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. @@ -2836,17 +3082,18 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) tv_list_append_string(list, reg->y_array[i], -1); } tv_list_set_lock(list, VAR_FIXED); - (void)tv_dict_add_list(dict, S_LEN("regcontents"), list); + 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)); - (void)tv_dict_add_str(dict, S_LEN("regtype"), 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; - (void)tv_dict_add_str(dict, S_LEN("regname"), buf); + 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. tv_dict_add_bool(dict, S_LEN("inclusive"), @@ -2855,11 +3102,11 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) // Kind of operation: yank, delete, change). buf[0] = (char)get_op_char(oap->op_type); buf[1] = NUL; - (void)tv_dict_add_str(dict, S_LEN("operator"), buf); + tv_dict_add_str(dict, S_LEN("operator"), buf); // Selection type: visual or not. - (void)tv_dict_add_bool(dict, S_LEN("visual"), - oap->is_VIsual ? kBoolVarTrue : kBoolVarFalse); + tv_dict_add_bool(dict, S_LEN("visual"), + oap->is_VIsual ? kBoolVarTrue : kBoolVarFalse); tv_dict_set_keys_readonly(dict); textlock++; @@ -2870,6 +3117,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_textputpost(int regname, yankreg_T *reg) + FUNC_ATTR_NONNULL_ALL +{ + static bool recursive = false; + int len; + + if (recursive || !has_event(EVENT_TEXTPUTPOST)) { + // 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, (const char *)reg->y_array[i], -1); + } + 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(EVENT_TEXTPUTPOST, 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! /// @@ -2928,7 +3225,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) if (flags & PUT_LINE) { stuffcharReadbuff(command_start_char); for (; count > 0; count--) { - (void)stuff_inserted(NUL, 1, count != 1); + stuff_inserted(NUL, 1, count != 1); if (count != 1) { // To avoid 'autoindent' affecting the text, use Ctrl_U to remove any // whitespace. Can't just insert Ctrl_U into readbuf1, this would go @@ -2940,7 +3237,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) } } } else { - (void)stuff_inserted(command_start_char, count, false); + stuff_inserted(command_start_char, count, false); } // Putting the text is done later, so can't move the cursor to the next @@ -3050,6 +3347,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; @@ -3061,6 +3362,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) return; } + colnr_T split_pos = 0; if (y_type == kMTLineWise) { if (flags & PUT_LINE_SPLIT) { // "p" or "P" in Visual mode: split the lines to put the text in @@ -3068,23 +3370,24 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) if (u_save_cursor() == FAIL) { goto end; } - char *p = get_cursor_pos_ptr(); + char *curline = get_cursor_line_ptr(); + char *p = curline + curwin->w_cursor.col; if (dir == FORWARD && *p != NUL) { MB_PTR_ADV(p); } + // we need this later for the correct extmark_splice() event + split_pos = (colnr_T)(p - curline); + char *ptr = xstrdup(p); ml_append(curwin->w_cursor.lnum, ptr, 0, false); xfree(ptr); - char *oldp = get_cursor_line_ptr(); - p = oldp + curwin->w_cursor.col; - if (dir == FORWARD && *p != NUL) { - MB_PTR_ADV(p); - } - ptr = xmemdupz(oldp, (size_t)(p - oldp)); + ptr = xmemdupz(get_cursor_line_ptr(), (size_t)split_pos); ml_replace(curwin->w_cursor.lnum, ptr, false); nr_lines++; dir = FORWARD; + + buf_updates_send_changes(curbuf, curwin->w_cursor.lnum, 1, 1); } if (flags & PUT_LINE_FORWARD) { // Must be "p" for a Visual block, put lines below the block. @@ -3118,9 +3421,9 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) // Correct line number for closed fold. Don't move the cursor yet, // u_save() uses it. if (dir == BACKWARD) { - (void)hasFolding(lnum, &lnum, NULL); + hasFolding(lnum, &lnum, NULL); } else { - (void)hasFolding(lnum, NULL, &lnum); + hasFolding(lnum, NULL, &lnum); } if (dir == FORWARD) { lnum++; @@ -3225,19 +3528,19 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) } // get the old line and advance to the position to insert at char *oldp = get_cursor_line_ptr(); - size_t oldlen = strlen(oldp); - chartabsize_T cts; - init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, 0, oldp, oldp); - while (cts.cts_vcol < col && *cts.cts_ptr != NUL) { - // Count a tab for what it's worth (if list mode not on) - incr = lbr_chartabsize_adv(&cts); - cts.cts_vcol += incr; + CharsizeArg csarg; + CSType cstype = init_charsize_arg(&csarg, curwin, curwin->w_cursor.lnum, oldp); + StrCharInfo ci = utf_ptr2StrCharInfo(oldp); + vcol = 0; + while (vcol < col && *ci.ptr != NUL) { + incr = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg).width; + vcol += incr; + ci = utfc_next(ci); } - vcol = cts.cts_vcol; - char *ptr = cts.cts_ptr; + size_t oldlen = (size_t)(ci.ptr - oldp) + strlen(ci.ptr); + char *ptr = ci.ptr; bd.textcol = (colnr_T)(ptr - oldp); - clear_chartabsize_arg(&cts); shortline = (vcol < col) || (vcol == col && !*ptr); @@ -3261,16 +3564,15 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) yanklen = (int)strlen(y_array[i]); if ((flags & PUT_BLOCK_INNER) == 0) { - // calculate number of spaces required to fill right side of - // block + // calculate number of spaces required to fill right side of block spaces = y_width + 1; - init_chartabsize_arg(&cts, curwin, 0, 0, y_array[i], y_array[i]); - for (int j = 0; j < yanklen; j++) { - spaces -= lbr_chartabsize(&cts); - cts.cts_ptr++; - cts.cts_vcol = 0; + + cstype = init_charsize_arg(&csarg, curwin, 0, y_array[i]); + ci = utf_ptr2StrCharInfo(y_array[i]); + while (*ci.ptr != NUL) { + spaces -= win_charsize(cstype, 0, ci.ptr, ci.chr.value, &csarg).width; + ci = utfc_next(ci); } - clear_chartabsize_arg(&cts); if (spaces < 0) { spaces = 0; } @@ -3344,13 +3646,11 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) } curbuf->b_op_end.coladd = 0; if (flags & PUT_CURSEND) { - colnr_T len; - curwin->w_cursor = curbuf->b_op_end; curwin->w_cursor.col++; // in Insert mode we might be after the NUL, correct for that - len = (colnr_T)strlen(get_cursor_line_ptr()); + colnr_T len = (colnr_T)strlen(get_cursor_line_ptr()); if (curwin->w_cursor.col > len) { curwin->w_cursor.col = len; } @@ -3533,7 +3833,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) } else if ((indent = get_indent() + indent_diff) < 0) { indent = 0; } - (void)set_indent(indent, SIN_NOMARK); + set_indent(indent, SIN_NOMARK); curwin->w_cursor = old_pos; // remember how many chars were removed if (cnt == count && i == y_size - 1) { @@ -3545,7 +3845,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) bcount_t totsize = 0; int lastsize = 0; if (y_type == kMTCharWise - || (y_type == kMTLineWise && flags & PUT_LINE_SPLIT)) { + || (y_type == kMTLineWise && (flags & PUT_LINE_SPLIT))) { for (i = 0; i < y_size - 1; i++) { totsize += (bcount_t)strlen(y_array[i]) + 1; } @@ -3556,9 +3856,9 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) extmark_splice(curbuf, (int)new_cursor.lnum - 1, col, 0, 0, 0, (int)y_size - 1, lastsize, totsize, kExtmarkUndo); - } else if (y_type == kMTLineWise && flags & PUT_LINE_SPLIT) { + } else if (y_type == kMTLineWise && (flags & PUT_LINE_SPLIT)) { // Account for last pasted NL + last NL - extmark_splice(curbuf, (int)new_cursor.lnum - 1, col + 1, 0, 0, 0, + extmark_splice(curbuf, (int)new_cursor.lnum - 1, split_pos, 0, 0, 0, (int)y_size + 1, 0, totsize + 2, kExtmarkUndo); } @@ -3652,6 +3952,10 @@ error: } end: + if (reg) { + do_autocmd_textputpost(regname, reg); + } + if (cmdmod.cmod_flags & CMOD_LOCKMARKS) { curbuf->b_op_start = orig_start; curbuf->b_op_end = orig_end; @@ -3696,7 +4000,7 @@ void adjust_cursor_eol(void) } /// @return true if lines starting with '#' should be left aligned. -int preprocs_left(void) +bool preprocs_left(void) { return ((curbuf->b_p_si && !curbuf->b_p_cin) || (curbuf->b_p_cin && in_cinkeys('#', ' ', true) @@ -3724,7 +4028,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. @@ -3761,10 +4065,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); @@ -3956,7 +4260,7 @@ char *skip_comment(char *line, bool process, bool include_space, bool *is_commen /// to set those marks. /// /// @return FAIL for failure, OK otherwise -int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions, bool setmark) +int do_join(size_t count, bool insert_space, bool save_undo, bool use_formatoptions, bool setmark) { char *curr = NULL; char *curr_start = NULL; @@ -3967,8 +4271,7 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions int sumsize = 0; // size of the long new line int ret = OK; int *comments = NULL; - int remove_comments = (use_formatoptions == true) - && has_format_option(FO_REMOVE_COMS); + bool remove_comments = use_formatoptions && has_format_option(FO_REMOVE_COMS); bool prev_was_comment = false; assert(count >= 1); @@ -4185,7 +4488,7 @@ static void restore_lbr(bool lbr_saved) /// - textlen includes the first/last char to be wholly yanked /// - start/endspaces is the number of columns of the first/last yanked char /// that are to be yanked. -static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool is_del) +void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool is_del) { int incr = 0; // Avoid a problem with unwanted linebreaks in block mode. @@ -4206,25 +4509,25 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool char *line = ml_get(lnum); char *prev_pstart = line; - chartabsize_T cts; - init_chartabsize_arg(&cts, curwin, lnum, bdp->start_vcol, line, line); - while (cts.cts_vcol < oap->start_vcol && *cts.cts_ptr != NUL) { - // Count a tab for what it's worth (if list mode not on) - incr = lbr_chartabsize(&cts); - cts.cts_vcol += incr; - if (ascii_iswhite(*cts.cts_ptr)) { + CharsizeArg csarg; + CSType cstype = init_charsize_arg(&csarg, curwin, lnum, line); + StrCharInfo ci = utf_ptr2StrCharInfo(line); + int vcol = bdp->start_vcol; + while (vcol < oap->start_vcol && *ci.ptr != NUL) { + incr = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg).width; + vcol += incr; + if (ascii_iswhite(ci.chr.value)) { bdp->pre_whitesp += incr; bdp->pre_whitesp_c++; } else { bdp->pre_whitesp = 0; bdp->pre_whitesp_c = 0; } - prev_pstart = cts.cts_ptr; - MB_PTR_ADV(cts.cts_ptr); + prev_pstart = ci.ptr; + ci = utfc_next(ci); } - bdp->start_vcol = cts.cts_vcol; - char *pstart = cts.cts_ptr; - clear_chartabsize_arg(&cts); + bdp->start_vcol = vcol; + char *pstart = ci.ptr; bdp->start_char_vcols = incr; if (bdp->start_vcol < oap->start_vcol) { // line too short @@ -4261,17 +4564,18 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool } } } else { - init_chartabsize_arg(&cts, curwin, lnum, bdp->end_vcol, line, pend); + cstype = init_charsize_arg(&csarg, curwin, lnum, line); + ci = utf_ptr2StrCharInfo(pend); + vcol = bdp->end_vcol; char *prev_pend = pend; - while (cts.cts_vcol <= oap->end_vcol && *cts.cts_ptr != NUL) { - // Count a tab for what it's worth (if list mode not on) - prev_pend = cts.cts_ptr; - incr = lbr_chartabsize_adv(&cts); - cts.cts_vcol += incr; + while (vcol <= oap->end_vcol && *ci.ptr != NUL) { + prev_pend = ci.ptr; + incr = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg).width; + vcol += incr; + ci = utfc_next(ci); } - bdp->end_vcol = cts.cts_vcol; - pend = cts.cts_ptr; - clear_chartabsize_arg(&cts); + bdp->end_vcol = vcol; + pend = ci.ptr; if (bdp->end_vcol <= oap->end_vcol && (!is_del @@ -4307,6 +4611,65 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool restore_lbr(lbr_saved); } +/// Get block text from "start" to "end" +void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T lnum, + bool inclusive) +{ + colnr_T startcol = 0; + colnr_T endcol = MAXCOL; + bool is_oneChar = false; + colnr_T cs, ce; + char *p = ml_get(lnum); + bdp->startspaces = 0; + bdp->endspaces = 0; + + if (lnum == start.lnum) { + startcol = start.col; + if (virtual_op) { + getvcol(curwin, &start, &cs, NULL, &ce); + if (ce != cs && start.coladd > 0) { + // Part of a tab selected -- but don't double-count it. + bdp->startspaces = (ce - cs + 1) - start.coladd; + if (bdp->startspaces < 0) { + bdp->startspaces = 0; + } + startcol++; + } + } + } + + if (lnum == end.lnum) { + endcol = end.col; + if (virtual_op) { + getvcol(curwin, &end, &cs, NULL, &ce); + if (p[endcol] == NUL || (cs + end.coladd < ce + // Don't add space for double-wide + // char; endcol will be on last byte + // of multi-byte char. + && utf_head_off(p, p + endcol) == 0)) { + if (start.lnum == end.lnum && start.col == end.col) { + // Special case: inside a single char + is_oneChar = true; + bdp->startspaces = end.coladd - start.coladd + inclusive; + endcol = startcol; + } else { + bdp->endspaces = end.coladd + inclusive; + endcol -= inclusive; + } + } + } + } + if (endcol == MAXCOL) { + endcol = (colnr_T)strlen(p); + } + if (startcol > endcol || is_oneChar) { + bdp->textlen = 0; + } else { + bdp->textlen = endcol - startcol + inclusive; + } + bdp->textstart = p + startcol; +} + /// Handle the add/subtract operator. /// /// @param[in] oap Arguments of operator. @@ -4374,7 +4737,7 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd) length = oap->end.col - pos.col + 1; } } - int one_change = do_addsub(oap->op_type, &pos, length, amount); + bool one_change = do_addsub(oap->op_type, &pos, length, amount); if (one_change) { // Remember the start position of the first change. if (change_cnt == 0) { @@ -4419,7 +4782,7 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd) /// @param Prenum1 Amount of addition or subtraction. /// /// @return true if some character was changed. -int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) +bool do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) { char *buf1 = NULL; char buf2[NUMBUFLEN]; @@ -4575,7 +4938,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) curwin->w_cursor.col = col; startpos = curwin->w_cursor; did_change = true; - (void)del_char(false); + del_char(false); ins_char(firstdigit); endpos = curwin->w_cursor; curwin->w_cursor.col = col; @@ -4630,13 +4993,13 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) if (!pre) { if (subtract) { if (n > oldn) { - n = 1 + (n ^ (uvarnumber_T) - 1); + n = 1 + (n ^ (uvarnumber_T)(-1)); negative ^= true; } } else { // add if (n < oldn) { - n = (n ^ (uvarnumber_T) - 1); + n = (n ^ (uvarnumber_T)(-1)); negative ^= true; } } @@ -4683,7 +5046,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) } } // del_char() will mark line needing displaying - (void)del_char(false); + del_char(false); c = gchar_cursor(); } @@ -4894,7 +5257,7 @@ void *get_reg_contents(int regname, int flags) return get_reg_wrap_one_line(xstrdup(retval), flags); } - yankreg_T *reg = get_yank_register(regname, YREG_PASTE); + yankreg_T *reg = get_yank_register(regname, YREG_PUT); if (reg->y_array == NULL) { return NULL; } @@ -4957,6 +5320,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); @@ -5224,16 +5591,16 @@ static varnumber_T line_count_info(char *line, varnumber_T *wc, varnumber_T *cc, varnumber_T i; varnumber_T words = 0; varnumber_T chars = 0; - int is_word = 0; + bool is_word = false; for (i = 0; i < limit && line[i] != NUL;) { if (is_word) { if (ascii_isspace(line[i])) { words++; - is_word = 0; + is_word = false; } } else if (!ascii_isspace(line[i])) { - is_word = 1; + is_word = true; } chars++; i += utfc_ptr2len(line + i); @@ -5521,7 +5888,7 @@ static void op_colon(oparg_T *oap) // When using !! on a closed fold the range ".!" works best to operate // on, it will be made the whole closed fold later. linenr_T endOfStartFold = oap->start.lnum; - (void)hasFolding(oap->start.lnum, NULL, &endOfStartFold); + hasFolding(oap->start.lnum, NULL, &endOfStartFold); if (oap->end.lnum != oap->start.lnum && oap->end.lnum != endOfStartFold) { // Make it a range with the end line. stuffcharReadbuff(','); @@ -5740,7 +6107,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) const bool redo_yank = vim_strchr(p_cpo, CPO_YANK) != NULL && !gui_yank; // Avoid a problem with unwanted linebreaks in block mode - (void)reset_lbr(); + reset_lbr(); oap->is_VIsual = VIsual_active; if (oap->motion_force == 'V') { oap->motion_type = kMTLineWise; @@ -6126,7 +6493,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) vim_beep(BO_OPER); CancelRedo(); } else { - (void)op_delete(oap); + op_delete(oap); // save cursor line for undo if it wasn't saved yet if (oap->motion_type == kMTLineWise && has_format_option(FO_AUTO) @@ -6145,7 +6512,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) } else { restore_lbr(lbr_saved); oap->excl_tr_ws = cap->cmdchar == 'z'; - (void)op_yank(oap, !gui_yank); + op_yank(oap, !gui_yank); } check_cursor_col(); break; @@ -6276,7 +6643,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) op_insert(oap, cap->count1); // Reset linebreak, so that formatting works correctly. - (void)reset_lbr(); + reset_lbr(); // TODO(brammool): when inserting in several lines, should format all // the lines. @@ -6350,7 +6717,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) if (!p_sol && oap->motion_type == kMTLineWise && !oap->end_adjusted && (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT || oap->op_type == OP_DELETE)) { - (void)reset_lbr(); + reset_lbr(); coladvance(curwin->w_curswant = old_col); } } else { @@ -6409,7 +6776,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; } @@ -6426,10 +6793,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; } @@ -6480,8 +6847,6 @@ bool prepare_yankreg_from_object(yankreg_T *reg, String regtype, size_t lines) } } - reg->y_array = xcalloc(lines, sizeof(uint8_t *)); - reg->y_size = lines; reg->additional_data = NULL; reg->timestamp = 0; return true; @@ -6494,7 +6859,6 @@ void finish_yankreg_from_object(yankreg_T *reg, bool clipboard_adjust) // but otherwise there is no line after the final newline if (reg->y_type != kMTCharWise) { if (reg->y_type == kMTUnknown || clipboard_adjust) { - xfree(reg->y_array[reg->y_size - 1]); reg->y_size--; } if (reg->y_type == kMTUnknown) { @@ -6680,7 +7044,7 @@ static void set_clipboard(int name, yankreg_T *reg) tv_list_append_string(args, ®type, 1); tv_list_append_string(args, ((char[]) { (char)name }), 1); - (void)eval_call_provider("clipboard", "set", args, true); + eval_call_provider("clipboard", "set", args, true); } /// Avoid slow things (clipboard) during batch operations (while/for-loops). @@ -6745,11 +7109,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`. @@ -6761,30 +7125,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 @@ -6792,8 +7159,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++; } } @@ -6813,11 +7180,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; } @@ -6833,7 +7200,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 @@ -6848,7 +7215,7 @@ bool op_reg_set_previous(const char name) return false; } - y_previous = &y_regs[i]; + y_previous = get_global_reg(i); return true; } |