diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2016-02-01 01:47:37 -0500 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2016-02-01 01:47:37 -0500 |
commit | 1d995bb35706c67b87280244fce6ebdcd2e7acb5 (patch) | |
tree | 48fb61734f6adf59ee0c38df1450358ad7ce47dd | |
parent | 5ee87c68b792b4a704200b4e51ba948833b9cbfb (diff) | |
parent | 228d236bdfd85721840e7ea7935fe0060a65fa93 (diff) | |
download | rneovim-1d995bb35706c67b87280244fce6ebdcd2e7acb5.tar.gz rneovim-1d995bb35706c67b87280244fce6ebdcd2e7acb5.tar.bz2 rneovim-1d995bb35706c67b87280244fce6ebdcd2e7acb5.zip |
Merge pull request #4013 from watiko/vim-increment
Vim patches related to increment and marks
-rw-r--r-- | runtime/doc/change.txt | 13 | ||||
-rw-r--r-- | runtime/doc/options.txt | 5 | ||||
-rw-r--r-- | src/nvim/charset.c | 73 | ||||
-rw-r--r-- | src/nvim/eval.c | 26 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 37 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 4 | ||||
-rw-r--r-- | src/nvim/keymap.c | 4 | ||||
-rw-r--r-- | src/nvim/normal.c | 79 | ||||
-rw-r--r-- | src/nvim/ops.c | 452 | ||||
-rw-r--r-- | src/nvim/ops.h | 64 | ||||
-rw-r--r-- | src/nvim/option.c | 2 | ||||
-rw-r--r-- | src/nvim/spell.c | 4 | ||||
-rw-r--r-- | src/nvim/testdir/Makefile | 3 | ||||
-rw-r--r-- | src/nvim/testdir/test_marks.in | 34 | ||||
-rw-r--r-- | src/nvim/testdir/test_marks.ok | 16 | ||||
-rw-r--r-- | src/nvim/undo.c | 7 | ||||
-rw-r--r-- | src/nvim/version.c | 66 | ||||
-rw-r--r-- | src/nvim/vim.h | 10 | ||||
-rw-r--r-- | test/functional/legacy/057_sort_spec.lua | 91 | ||||
-rw-r--r-- | test/functional/legacy/059_utf8_spell_checking_spec.lua | 70 | ||||
-rw-r--r-- | test/functional/legacy/increment_spec.lua | 723 |
21 files changed, 1471 insertions, 312 deletions
diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index 42dc84e0de..30b7dcaa4a 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -370,11 +370,14 @@ CTRL-A Add [count] to the number or alphabetic character at CTRL-X Subtract [count] from the number or alphabetic character at or after the cursor. -The CTRL-A and CTRL-X commands work for (signed) decimal numbers, unsigned -binary/octal/hexadecimal numbers and alphabetic characters. This -depends on the 'nrformats' option. -- When 'nrformats' includes "bin", Vim considers numbers starting with '0b' or - '0B' as binary. +The CTRL-A and CTRL-X commands can work for: +- signed and unsigned decimal numbers +- unsigned binary, octal and hexadecimal numbers +- alphabetic characters + +This depends on the 'nrformats' option: +- When 'nrformats' includes "bin", Vim assumes numbers starting with '0b' or + '0B' are binary. - When 'nrformats' includes "octal", Vim considers numbers starting with a '0' to be octal, unless the number includes a '8' or '9'. Other numbers are decimal and may have a preceding minus sign. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index bbd9cc1e2b..d9024b98c0 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -4396,7 +4396,7 @@ A jump table for the options with a short description can be found at |Q_op|. recognized as a multi click. *'nrformats'* *'nf'* -'nrformats' 'nf' string (default "hex") +'nrformats' 'nf' string (default "bin,hex") local to buffer This defines what bases Vim will consider for numbers when using the CTRL-A and CTRL-X commands for adding to and subtracting from a number @@ -4409,6 +4409,9 @@ A jump table for the options with a short description can be found at |Q_op|. hex If included, numbers starting with "0x" or "0X" will be considered to be hexadecimal. Example: Using CTRL-X on "0x100" results in "0x0ff". + bin If included, numbers starting with "0b" or "0B" will be + considered to be binary. Example: Using CTRL-X on + "0b1000" subtracts one, resulting in "0b0111". Numbers which simply begin with a digit in the range 1-9 are always considered decimal. This also happens for numbers that are not recognized as octal or hex. diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 9c63eca1f2..9a0e1440cc 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1767,29 +1767,26 @@ int vim_isblankline(char_u *lbuf) /// If "len" is not NULL, the length of the number in characters is returned. /// If "nptr" is not NULL, the signed result is returned in it. /// If "unptr" is not NULL, the unsigned result is returned in it. -/// If "dobin" is non-zero recognize binary numbers, when > 1 always assume -/// binary number. -/// If "dooct" is non-zero recognize octal numbers, when > 1 always assume -/// octal number. -/// If "dohex" is non-zero recognize hex numbers, when > 1 always assume -/// hex number. +/// If "what" contains STR2NR_BIN recognize binary numbers. +/// If "what" contains STR2NR_OCT recognize octal numbers. +/// If "what" contains STR2NR_HEX recognize hex numbers. +/// If "what" contains STR2NR_FORCE always assume bin/oct/hex. +/// If maxlen > 0, check at a maximum maxlen chars. /// /// @param start /// @param prep Returns type of number 0 = decimal, 'x' or 'X' is hex, -// '0' = octal, 'b' or 'B' is bin +/// '0' = octal, 'b' or 'B' is bin /// @param len Returns the detected length of number. -/// @param dobin recognize binary number -/// @param dooct recognize octal number -/// @param dohex recognize hex number +/// @param what Recognizes what number passed. /// @param nptr Returns the signed result. /// @param unptr Returns the unsigned result. -void vim_str2nr(char_u *start, int *prep, int *len, - int dobin, int dooct, int dohex, - long *nptr, unsigned long *unptr) +/// @param maxlen Max length of string to check. +void vim_str2nr(char_u *start, int *prep, int *len, int what, + long *nptr, unsigned long *unptr, int maxlen) { char_u *ptr = start; int pre = 0; // default is decimal - int negative = false; + bool negative = false; unsigned long un = 0; if (ptr[0] == '-') { @@ -1797,25 +1794,28 @@ void vim_str2nr(char_u *start, int *prep, int *len, ptr++; } - // Recognize hex, octal, and bin. - if ((ptr[0] == '0') && (ptr[1] != '8') && (ptr[1] != '9')) { + // Recognize hex, octal and bin. + if ((ptr[0] == '0') && (ptr[1] != '8') && (ptr[1] != '9') + && (maxlen == 0 || maxlen > 1)) { pre = ptr[1]; - if (dohex + if ((what & STR2NR_HEX) && ((pre == 'X') || (pre == 'x')) - && ascii_isxdigit(ptr[2])) { + && ascii_isxdigit(ptr[2]) + && (maxlen == 0 || maxlen > 2)) { // hexadecimal ptr += 2; - } else if (dobin + } else if ((what & STR2NR_BIN) && ((pre == 'B') || (pre == 'b')) - && ascii_isbdigit(ptr[2])) { + && ascii_isbdigit(ptr[2]) + && (maxlen == 0 || maxlen > 2)) { // binary ptr += 2; } else { - // default is decimal + // decimal or octal, default is decimal pre = 0; - if (dooct) { + if (what & STR2NR_OCT) { // Don't interpret "0", "08" or "0129" as octal. for (int n = 1; ascii_isdigit(ptr[n]); ++n) { if (ptr[n] > '7') { @@ -1827,35 +1827,58 @@ void vim_str2nr(char_u *start, int *prep, int *len, // assume octal pre = '0'; } + if (n == maxlen) { + break; + } } } } } // Do the string-to-numeric conversion "manually" to avoid sscanf quirks. - if ((pre == 'B') || (pre == 'b') || (dobin > 1)) { + int n = 1; + if ((pre == 'B') || (pre == 'b') || what == STR2NR_BIN + STR2NR_FORCE) { // bin + if (pre != 0) { + n += 2; // skip over "0b" + } while ('0' <= *ptr && *ptr <= '1') { un = 2 * un + (unsigned long)(*ptr - '0'); ptr++; + if (n++ == maxlen) { + break; + } } - } else if ((pre == '0') || (dooct > 1)) { + } else if ((pre == '0') || what == STR2NR_OCT + STR2NR_FORCE) { // octal while ('0' <= *ptr && *ptr <= '7') { un = 8 * un + (unsigned long)(*ptr - '0'); ptr++; + if (n++ == maxlen) { + break; + } } - } else if ((pre == 'X') || (pre == 'x') || dohex > 1) { + } else if ((pre == 'X') || (pre == 'x') + || what == STR2NR_HEX + STR2NR_FORCE) { // hex + if (pre != 0) { + n += 2; // skip over "0x" + } while (ascii_isxdigit(*ptr)) { un = 16 * un + (unsigned long)hex2nr(*ptr); ptr++; + if (n++ == maxlen) { + break; + } } } else { // decimal while (ascii_isdigit(*ptr)) { un = 10 * un + (unsigned long)(*ptr - '0'); ptr++; + if (n++ == maxlen) { + break; + } } } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a1c5f958d1..b591c91147 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1147,7 +1147,7 @@ int call_vim_function( len = 0; } else { // Recognize a number argument, the others must be strings. - vim_str2nr(argv[i], NULL, &len, true, true, true, &n, NULL); + vim_str2nr(argv[i], NULL, &len, STR2NR_ALL, &n, NULL, 0); } if (len != 0 && len == (int)STRLEN(argv[i])) { argvars[i].v_type = VAR_NUMBER; @@ -4138,7 +4138,7 @@ static int eval7( rettv->vval.v_float = f; } } else { - vim_str2nr(*arg, NULL, &len, true, true, true, &n, NULL); + vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0); *arg += len; if (evaluate) { rettv->v_type = VAR_NUMBER; @@ -16037,6 +16037,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv) int base = 10; char_u *p; long n; + int what; if (argvars[1].v_type != VAR_UNKNOWN) { base = get_tv_number(&argvars[1]); @@ -16050,11 +16051,20 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv) if (*p == '+') { p = skipwhite(p + 1); } - vim_str2nr(p, NULL, NULL, - base == 2 ? 2 : 0, - base == 8 ? 2 : 0, - base == 16 ? 2 : 0, - &n, NULL); + switch (base) { + case 2: + what = STR2NR_BIN + STR2NR_FORCE; + break; + case 8: + what = STR2NR_OCT + STR2NR_FORCE; + break; + case 16: + what = STR2NR_HEX + STR2NR_FORCE; + break; + default: + what = 0; + } + vim_str2nr(p, NULL, NULL, what, &n, NULL, 0); rettv->vval.v_number = n; } @@ -18336,7 +18346,7 @@ long get_tv_number_chk(typval_T *varp, int *denote) case VAR_STRING: if (varp->vval.v_string != NULL) { vim_str2nr(varp->vval.v_string, NULL, NULL, - true, true, true, &n, NULL); + STR2NR_ALL, &n, NULL, 0); } return n; case VAR_LIST: diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 407dded6af..a517037431 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -342,27 +342,27 @@ void ex_sort(exarg_T *eap) char_u *s; char_u *s2; char_u c; // temporary character storage - int unique = false; + bool unique = false; long deleted; colnr_T start_col; colnr_T end_col; - int sort_bin; // sort on bin number - int sort_oct; // sort on octal number - int sort_hex; // sort on hex number + int sort_what = 0; // Sorting one line is really quick! if (count <= 1) { return; } - if (u_save((linenr_T)(eap->line1 - 1), (linenr_T)(eap->line2 + 1)) == FAIL) + if (u_save((linenr_T)(eap->line1 - 1), (linenr_T)(eap->line2 + 1)) == FAIL) { return; + } sortbuf1 = NULL; sortbuf2 = NULL; regmatch.regprog = NULL; sorti_T *nrs = xmalloc(count * sizeof(sorti_T)); - sort_abort = sort_ic = sort_rx = sort_nr = sort_bin = sort_oct = sort_hex = 0; + sort_abort = sort_ic = sort_rx = sort_nr = 0; + size_t format_found = 0; for (p = eap->arg; *p != NUL; ++p) { if (ascii_iswhite(*p)) { @@ -372,12 +372,16 @@ void ex_sort(exarg_T *eap) sort_rx = true; } else if (*p == 'n') { sort_nr = 2; + format_found++; } else if (*p == 'b') { - sort_bin = 2; + sort_what = STR2NR_BIN + STR2NR_FORCE; + format_found++; } else if (*p == 'o') { - sort_oct = 2; + sort_what = STR2NR_OCT + STR2NR_FORCE; + format_found++; } else if (*p == 'x') { - sort_hex = 2; + sort_what = STR2NR_HEX + STR2NR_FORCE; + format_found++; } else if (*p == 'u') { unique = true; } else if (*p == '"') { @@ -415,13 +419,13 @@ void ex_sort(exarg_T *eap) } // Can only have one of 'n', 'b', 'o' and 'x'. - if (sort_nr + sort_bin + sort_oct + sort_hex > 2) { + if (format_found > 1) { EMSG(_(e_invarg)); goto sortend; } // From here on "sort_nr" is used as a flag for any number sorting. - sort_nr += sort_bin + sort_oct + sort_hex; + sort_nr += sort_what; // Make an array with all line numbers. This avoids having to copy all // the lines into allocated memory. @@ -457,22 +461,23 @@ void ex_sort(exarg_T *eap) *s2 = NUL; // Sorting on number: Store the number itself. p = s + start_col; - if (sort_hex) { + if (sort_what & STR2NR_HEX) { s = skiptohex(p); - } else if (sort_bin) { + } else if (sort_what & STR2NR_BIN) { s = (char_u*) skiptobin((char*) p); } else { s = skiptodigit(p); } if (s > p && s[-1] == '-') { - --s; // include preceding negative sign + // include preceding negative sign + s--; } if (*s == NUL) { // empty line should sort before any number nrs[lnum - eap->line1].start_col_nr = -MAXLNUM; } else { - vim_str2nr(s, NULL, NULL, sort_bin, sort_oct, sort_hex, - &nrs[lnum - eap->line1].start_col_nr, NULL); + vim_str2nr(s, NULL, NULL, sort_what, + &nrs[lnum - eap->line1].start_col_nr, NULL, 0); } *s2 = c; } else { diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index b19331ad06..96bf2c78d2 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -4786,7 +4786,7 @@ int get_list_range(char_u **str, int *num1, int *num2) *str = skipwhite(*str); if (**str == '-' || ascii_isdigit(**str)) { // parse "from" part of range - vim_str2nr(*str, NULL, &len, false, false, false, &num, NULL); + vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0); *str += len; *num1 = (int)num; first = true; @@ -4794,7 +4794,7 @@ int get_list_range(char_u **str, int *num1, int *num2) *str = skipwhite(*str); if (**str == ',') { // parse "to" part of range *str = skipwhite(*str + 1); - vim_str2nr(*str, NULL, &len, false, false, false, &num, NULL); + vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0); if (len > 0) { *num2 = (int)num; *str = skipwhite(*str + len); diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index 7054bb822a..65c808eb06 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -574,7 +574,7 @@ int find_special_key( if (bp[0] == 't' && bp[1] == '_' && bp[2] && bp[3]) { bp += 3; // skip t_xx, xx may be '-' or '>' } else if (STRNICMP(bp, "char-", 5) == 0) { - vim_str2nr(bp + 5, NULL, &l, true, true, true, NULL, NULL); + vim_str2nr(bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0); bp += l + 5; break; } @@ -600,7 +600,7 @@ int find_special_key( if (STRNICMP(last_dash + 1, "char-", 5) == 0 && ascii_isdigit(last_dash[6])) { // <Char-123> or <Char-033> or <Char-0x33> - vim_str2nr(last_dash + 6, NULL, NULL, true, true, true, NULL, &n); + vim_str2nr(last_dash + 6, NULL, NULL, STR2NR_ALL, NULL, &n, 0); key = (int)n; } else { /* diff --git a/src/nvim/normal.c b/src/nvim/normal.c index a116b5a0bd..cb3fc98dfa 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1414,11 +1414,12 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) int lbr_saved = curwin->w_p_lbr; - /* The visual area is remembered for redo */ - static int redo_VIsual_mode = NUL; /* 'v', 'V', or Ctrl-V */ - static linenr_T redo_VIsual_line_count; /* number of lines */ - static colnr_T redo_VIsual_vcol; /* number of cols or end column */ - static long redo_VIsual_count; /* count for Visual operator */ + // The visual area is remembered for redo + static int redo_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V + static linenr_T redo_VIsual_line_count; // number of lines + static colnr_T redo_VIsual_vcol; // number of cols or end column + static long redo_VIsual_count; // count for Visual operator + static int redo_VIsual_arg; // extra argument bool include_line_break = false; old_cursor = curwin->w_cursor; @@ -1656,6 +1657,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) redo_VIsual_vcol = resel_VIsual_vcol; redo_VIsual_line_count = resel_VIsual_line_count; redo_VIsual_count = cap->count0; + redo_VIsual_arg = cap->arg; } } @@ -1705,10 +1707,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) VIsual_active = false; setmouse(); mouse_dragging = 0; - if (mode_displayed) - clear_cmdline = true; /* unshow visual mode later */ - else - clear_showcmd(); + may_clear_cmdline(); if ((oap->op_type == OP_YANK || oap->op_type == OP_COLON || oap->op_type == OP_FUNCTION @@ -1993,6 +1992,20 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) deleteFold(oap->start.lnum, oap->end.lnum, oap->op_type == OP_FOLDDELREC, oap->is_VIsual); break; + + case OP_NR_ADD: + case OP_NR_SUB: + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + VIsual_active = true; + curwin->w_p_lbr = lbr_saved; + op_addsub(oap, cap->count1, redo_VIsual_arg); + VIsual_active = false; + } + check_cursor_col(); + break; default: clearopbeep(oap); } @@ -2852,10 +2865,7 @@ void end_visual_mode(void) if (!virtual_active()) curwin->w_cursor.coladd = 0; - if (mode_displayed) - clear_cmdline = true; /* unshow visual mode later */ - else - clear_showcmd(); + may_clear_cmdline(); adjust_cursor_eol(); } @@ -3121,10 +3131,19 @@ static void unshift_special(cmdarg_T *cap) cap->cmdchar = simplify_key(cap->cmdchar, &mod_mask); } -/* - * Routines for displaying a partly typed command - */ +/// If the mode is currently displayed clear the command line or update the +/// command displayed. +static void may_clear_cmdline(void) +{ + if (mode_displayed) { + // unshow visual mode later + clear_cmdline = true; + } else { + clear_showcmd(); + } +} +// Routines for displaying a partly typed command # define SHOWCMD_BUFLEN SHOWCMD_COLS + 1 + 30 static char_u showcmd_buf[SHOWCMD_BUFLEN]; static char_u old_showcmd_buf[SHOWCMD_BUFLEN]; /* For push_showcmd() */ @@ -3503,9 +3522,16 @@ static void nv_help(cmdarg_T *cap) */ static void nv_addsub(cmdarg_T *cap) { - if (!checkclearopq(cap->oap) - && do_addsub(cap->cmdchar, cap->count1)) + if (!VIsual_active && cap->oap->op_type == OP_NOP) { prep_redo_cmd(cap); + cap->oap->op_type = cap->cmdchar == Ctrl_A ? OP_NR_ADD : OP_NR_SUB; + op_addsub(cap->oap, cap->count1, cap->arg); + cap->oap->op_type = OP_NOP; + } else if (VIsual_active) { + nv_operator(cap); + } else { + clearop(cap->oap); + } } /* @@ -6327,9 +6353,20 @@ static void nv_g_cmd(cmdarg_T *cap) bool flag = false; switch (cap->nchar) { - /* - * "gR": Enter virtual replace mode. - */ + // "g^A/g^X": Sequentially increment visually selected region. + case Ctrl_A: + case Ctrl_X: + if (VIsual_active) { + cap->arg = true; + cap->cmdchar = cap->nchar; + cap->nchar = NUL; + nv_addsub(cap); + } else { + clearopbeep(oap); + } + break; + + // "gR": Enter virtual replace mode. case 'R': cap->arg = true; nv_Replace(cap); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 19dbd0f9f6..7614e6365a 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -87,34 +87,36 @@ struct block_def { */ static char opchars[][3] = { - {NUL, NUL, FALSE}, /* OP_NOP */ - {'d', NUL, FALSE}, /* OP_DELETE */ - {'y', NUL, FALSE}, /* OP_YANK */ - {'c', NUL, FALSE}, /* OP_CHANGE */ - {'<', NUL, TRUE}, /* OP_LSHIFT */ - {'>', NUL, TRUE}, /* OP_RSHIFT */ - {'!', NUL, TRUE}, /* OP_FILTER */ - {'g', '~', FALSE}, /* OP_TILDE */ - {'=', NUL, TRUE}, /* OP_INDENT */ - {'g', 'q', TRUE}, /* OP_FORMAT */ - {':', NUL, TRUE}, /* OP_COLON */ - {'g', 'U', FALSE}, /* OP_UPPER */ - {'g', 'u', FALSE}, /* OP_LOWER */ - {'J', NUL, TRUE}, /* DO_JOIN */ - {'g', 'J', TRUE}, /* DO_JOIN_NS */ - {'g', '?', FALSE}, /* OP_ROT13 */ - {'r', NUL, FALSE}, /* OP_REPLACE */ - {'I', NUL, FALSE}, /* OP_INSERT */ - {'A', NUL, FALSE}, /* OP_APPEND */ - {'z', 'f', TRUE}, /* OP_FOLD */ - {'z', 'o', TRUE}, /* OP_FOLDOPEN */ - {'z', 'O', TRUE}, /* OP_FOLDOPENREC */ - {'z', 'c', TRUE}, /* OP_FOLDCLOSE */ - {'z', 'C', TRUE}, /* OP_FOLDCLOSEREC */ - {'z', 'd', TRUE}, /* OP_FOLDDEL */ - {'z', 'D', TRUE}, /* OP_FOLDDELREC */ - {'g', 'w', TRUE}, /* OP_FORMAT2 */ - {'g', '@', FALSE}, /* OP_FUNCTION */ + { NUL, NUL, false }, // OP_NOP + { 'd', NUL, false }, // OP_DELETE + { 'y', NUL, false }, // OP_YANK + { 'c', NUL, false }, // OP_CHANGE + { '<', NUL, true }, // OP_LSHIFT + { '>', NUL, true }, // OP_RSHIFT + { '!', NUL, true }, // OP_FILTER + { 'g', '~', false }, // OP_TILDE + { '=', NUL, true }, // OP_INDENT + { 'g', 'q', true }, // OP_FORMAT + { ':', NUL, true }, // OP_COLON + { 'g', 'U', false }, // OP_UPPER + { 'g', 'u', false }, // OP_LOWER + { 'J', NUL, true }, // DO_JOIN + { 'g', 'J', true }, // DO_JOIN_NS + { 'g', '?', false }, // OP_ROT13 + { 'r', NUL, false }, // OP_REPLACE + { 'I', NUL, false }, // OP_INSERT + { 'A', NUL, false }, // OP_APPEND + { 'z', 'f', true }, // OP_FOLD + { 'z', 'o', true }, // OP_FOLDOPEN + { 'z', 'O', true }, // OP_FOLDOPENREC + { 'z', 'c', true }, // OP_FOLDCLOSE + { 'z', 'C', true }, // OP_FOLDCLOSEREC + { 'z', 'd', true }, // OP_FOLDDEL + { 'z', 'D', true }, // OP_FOLDDELREC + { 'g', 'w', true }, // OP_FORMAT2 + { 'g', '@', false }, // OP_FUNCTION + { Ctrl_A, NUL, false }, // OP_NR_ADD + { Ctrl_X, NUL, false }, // OP_NR_SUB }; /* @@ -125,13 +127,27 @@ int get_op_type(int char1, int char2) { int i; - if (char1 == 'r') /* ignore second character */ + if (char1 == 'r') { + // ignore second character return OP_REPLACE; - if (char1 == '~') /* when tilde is an operator */ + } + if (char1 == '~') { + // when tilde is an operator return OP_TILDE; - for (i = 0;; i++) - if (opchars[i][0] == char1 && opchars[i][1] == char2) + } + if (char1 == 'g' && char2 == Ctrl_A) { + // add + return OP_NR_ADD; + } + if (char1 == 'g' && char2 == Ctrl_X) { + // subtract + return OP_NR_SUB; + } + for (i = 0;; i++) { + if (opchars[i][0] == char1 && opchars[i][1] == char2) { break; + } + } return i; } @@ -4181,134 +4197,241 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, int i bdp->textstart = pstart; } - -static void reverse_line(char_u *s) +/// Handle the add/subtract operator. +/// +/// @param[in] oap Arguments of operator. +/// @param[in] Prenum1 Amount of addition or subtraction. +/// @param[in] g_cmd Prefixed with `g`. +void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd) { - int i, j; - char_u c; + pos_T pos; + struct block_def bd; + ssize_t change_cnt = 0; + linenr_T amount = Prenum1; - if ((i = (int)STRLEN(s) - 1) <= 0) - return; + if (!VIsual_active) { + pos = curwin->w_cursor; + if (u_save_cursor() == FAIL) { + return; + } + change_cnt = do_addsub(oap->op_type, &pos, 0, amount); + if (change_cnt) { + changed_lines(pos.lnum, 0, pos.lnum + 1, 0L); + } + } else { + int one_change; + int length; + pos_T startpos; - curwin->w_cursor.col = i - curwin->w_cursor.col; - for (j = 0; j < i; j++, i--) { - c = s[i]; s[i] = s[j]; s[j] = c; + if (u_save((linenr_T)(oap->start.lnum - 1), + (linenr_T)(oap->end.lnum + 1)) == FAIL) { + return; + } + + pos = oap->start; + for (; pos.lnum <= oap->end.lnum; ++pos.lnum) { + if (oap->motion_type == MBLOCK) { + // Visual block mode + block_prep(oap, &bd, pos.lnum, false); + pos.col = bd.textcol; + length = bd.textlen; + } else if (oap->motion_type == MLINE) { + curwin->w_cursor.col = 0; + pos.col = 0; + length = (colnr_T)STRLEN(ml_get(pos.lnum)); + } else { + // oap->motion_type == MCHAR + if (!oap->inclusive) { + dec(&(oap->end)); + } + length = (colnr_T)STRLEN(ml_get(pos.lnum)); + pos.col = 0; + if (pos.lnum == oap->start.lnum) { + pos.col += oap->start.col; + length -= oap->start.col; + } + if (pos.lnum == oap->end.lnum) { + length = (int)STRLEN(ml_get(oap->end.lnum)); + if (oap->end.col >= length) { + oap->end.col = length - 1; + } + length = oap->end.col - pos.col + 1; + } + } + 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) { + startpos = curbuf->b_op_start; + } + change_cnt++; + } + + if (g_cmd && one_change) { + amount += Prenum1; + } + } + if (change_cnt) { + changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L); + } + + if (!change_cnt && oap->is_VIsual) { + // No change: need to remove the Visual selection + redraw_curbuf_later(INVERTED); + } + + // Set '[ mark if something changed. Keep the last end + // position from do_addsub(). + if (change_cnt > 0) { + curbuf->b_op_start = startpos; + } + + if (change_cnt > p_report) { + if (change_cnt == 1) { + MSG(_("1 line changed")); + } else { + smsg((char *)_("%" PRId64 " lines changed"), (int64_t)change_cnt); + } + } } } -# define RLADDSUBFIX(ptr) if (curwin->w_p_rl) reverse_line(ptr); - /// Add or subtract from a number in a line. /// -/// @param command CTRL-A for add, CTRL-X for subtract -// @param Prenum1 number to add or subtract +/// @param op_type OP_NR_ADD or OP_NR_SUB. +/// @param pos Cursor position. +/// @param length Target number length. +/// @param Prenum1 Amount of addition or subtraction. /// -/// @return FAIL for failure, OK otherwise -int do_addsub(int command, linenr_T Prenum1) +/// @return true if some character was changed. +int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) { int col; char_u *buf1; char_u buf2[NUMBUFLEN]; - int pre; // 'X' or 'x': hex; '0': octal; 'B' or 'b': bin - static int hexupper = false; // 0xABC - unsigned long n, oldn; + int pre; // 'X' or 'x': hex; '0': octal; 'B' or 'b': bin + static bool hexupper = false; // 0xABC + unsigned long n; + unsigned long oldn; char_u *ptr; int c; - int length = 0; // character length of the number int todel; - int dohex; - int dooct; - int dobin; - int doalp; + bool dohex; + bool dooct; + bool dobin; + bool doalp; int firstdigit; - int negative; - int subtract; + bool subtract; + bool negative = false; + bool was_positive = true; + bool visual = VIsual_active; + bool did_change = false; + pos_T save_cursor = curwin->w_cursor; + int maxlen = 0; + pos_T startpos; + pos_T endpos; dohex = (vim_strchr(curbuf->b_p_nf, 'x') != NULL); // "heX" dooct = (vim_strchr(curbuf->b_p_nf, 'o') != NULL); // "Octal" dobin = (vim_strchr(curbuf->b_p_nf, 'b') != NULL); // "Bin" doalp = (vim_strchr(curbuf->b_p_nf, 'p') != NULL); // "alPha" - ptr = get_cursor_line_ptr(); - RLADDSUBFIX(ptr); + curwin->w_cursor = *pos; + ptr = ml_get(pos->lnum); + col = pos->col; + + if (*ptr == NUL) { + goto theend; + } // First check if we are on a hexadecimal number, after the "0x". - col = curwin->w_cursor.col; + if (!VIsual_active) { + if (dobin) { + while (col > 0 && ascii_isbdigit(ptr[col])) { + col--; + } + } - if (dobin) { - while (col > 0 && ascii_isbdigit(ptr[col])) { - col--; + if (dohex) { + while (col > 0 && ascii_isxdigit(ptr[col])) { + col--; + } } - } + if (dobin + && dohex + && !((col > 0 + && (ptr[col] == 'X' || + ptr[col] == 'x') + && ptr[col - 1] == '0' + && ascii_isxdigit(ptr[col + 1])))) { + // In case of binary/hexadecimal pattern overlap match, rescan - if (dohex) { - while (col > 0 && ascii_isxdigit(ptr[col])) { - col--; + col = curwin->w_cursor.col; + + while (col > 0 && ascii_isdigit(ptr[col])) { + col--; + } } - } - if (dobin - && dohex - && !((col > 0 - && (ptr[col] == 'X' || - ptr[col] == 'x') - && ptr[col - 1] == '0' - && ascii_isxdigit(ptr[col + 1])))) { - // In case of binary/hexadecimal pattern overlap match, rescan - col = curwin->w_cursor.col; + if ((dohex + && col > 0 + && (ptr[col] == 'X' + || ptr[col] == 'x') + && ptr[col - 1] == '0' + && ascii_isxdigit(ptr[col + 1])) || + (dobin + && col > 0 + && (ptr[col] == 'B' + || ptr[col] == 'b') + && ptr[col - 1] == '0' + && ascii_isbdigit(ptr[col + 1]))) { + // Found hexadecimal or binary number, move to its start. + col--; + } else { + // Search forward and then backward to find the start of number. + col = pos->col; + + while (ptr[col] != NUL + && !ascii_isdigit(ptr[col]) + && !(doalp && ASCII_ISALPHA(ptr[col]))) { + col++; + } - while (col > 0 && ascii_isdigit(ptr[col])) { + while (col > 0 + && ascii_isdigit(ptr[col - 1]) + && !(doalp && ASCII_ISALPHA(ptr[col]))) { col--; } + } } - if ((dohex - && col > 0 - && (ptr[col] == 'X' - || ptr[col] == 'x') - && ptr[col - 1] == '0' - && ascii_isxdigit(ptr[col + 1])) || - (dobin - && col > 0 - && (ptr[col] == 'B' - || ptr[col] == 'b') - && ptr[col - 1] == '0' - && ascii_isbdigit(ptr[col + 1]))) { - // Found hexadecimal or binary number, move to its start. - col--; - } else { - // Search forward and then backward to find the start of number. - col = curwin->w_cursor.col; - - while (ptr[col] != NUL - && !ascii_isdigit(ptr[col]) - && !(doalp && ASCII_ISALPHA(ptr[col]))) { + if (visual) { + while (ptr[col] != NUL && length > 0 && !ascii_isdigit(ptr[col]) && + !(doalp && ASCII_ISALPHA(ptr[col]))) { col++; + length--; } - while (col > 0 - && ascii_isdigit(ptr[col - 1]) - && !(doalp && ASCII_ISALPHA(ptr[col]))) { - col--; + if (length == 0) { + goto theend; + } + + if (col > pos->col && ptr[col - 1] == '-') { + negative = true; + was_positive = false; } } // If a number was found, and saving for undo works, replace the number. firstdigit = ptr[col]; - RLADDSUBFIX(ptr); - if ((!ascii_isdigit(firstdigit) && !(doalp && ASCII_ISALPHA(firstdigit))) - || u_save_cursor() != OK) { + if (!ascii_isdigit(firstdigit) && !(doalp && ASCII_ISALPHA(firstdigit))) { beep_flush(); - return FAIL; + goto theend; } - // get ptr again, because u_save() may have changed it - ptr = get_cursor_line_ptr(); - RLADDSUBFIX(ptr); - if (doalp && ASCII_ISALPHA(firstdigit)) { // decrement or increment alphabetic character - if (command == Ctrl_X) { + if (op_type == OP_NR_SUB) { if (CharOrd(firstdigit) < Prenum1) { if (isupper(firstdigit)) { firstdigit = 'A'; @@ -4330,28 +4453,44 @@ int do_addsub(int command, linenr_T Prenum1) } } curwin->w_cursor.col = col; + if (!did_change) { + startpos = curwin->w_cursor; + } + did_change = true; (void)del_char(false); ins_char(firstdigit); + endpos = curwin->w_cursor; + curwin->w_cursor.col = col; } else { - negative = false; - if (col > 0 && ptr[col - 1] == '-') { // negative number - --col; + if (col > 0 && ptr[col - 1] == '-' && !visual) { + // negative number + col--; negative = true; } // get the number value (unsigned) - vim_str2nr(ptr + col, &pre, &length, dobin, dooct, dohex, NULL, &n); + if (visual && VIsual_mode != 'V') { + maxlen = (curbuf->b_visual.vi_curswant == MAXCOL + ? (int)STRLEN(ptr) - col + : length); + } + + vim_str2nr(ptr + col, &pre, &length, + 0 + (dobin ? STR2NR_BIN : 0) + + (dooct ? STR2NR_OCT : 0) + + (dohex ? STR2NR_HEX : 0), + NULL, &n, maxlen); // ignore leading '-' for hex, octal and bin numbers if (pre && negative) { - ++col; - --length; + col++; + length--; negative = false; } // add or subtract subtract = false; - if (command == Ctrl_X) { + if (op_type == OP_NR_SUB) { subtract ^= true; } if (negative) { @@ -4370,7 +4509,8 @@ int do_addsub(int command, linenr_T Prenum1) n = 1 + (n ^ (unsigned long)-1); negative ^= true; } - } else { /* add */ + } else { + // add if (n < oldn) { n = (n ^ (unsigned long)-1); negative ^= true; @@ -4381,15 +4521,25 @@ int do_addsub(int command, linenr_T Prenum1) } } + if (visual && !was_positive && !negative && col > 0) { + // need to remove the '-' + col--; + length++; + } + // Delete the old number. curwin->w_cursor.col = col; + if (!did_change) { + startpos = curwin->w_cursor; + } + did_change = true; todel = length; c = gchar_cursor(); // Don't include the '-' in the length, only the length of the part // after it is kept the same. if (c == '-') { - --length; + length--; } while (todel-- > 0) { if (c < 0x100 && isalpha(c)) { @@ -4405,47 +4555,52 @@ int do_addsub(int command, linenr_T Prenum1) } // Prepare the leading characters in buf1[]. - // When there are many leading zeros it could be very long. Allocate - // a bit too much. + // When there are many leading zeros it could be very long. + // Allocate a bit too much. buf1 = xmalloc(length + NUMBUFLEN); + if (buf1 == NULL) { + goto theend; + } ptr = buf1; - if (negative) { + if (negative && (!visual || (visual && was_positive))) { *ptr++ = '-'; } if (pre) { *ptr++ = '0'; - --length; + length--; } if (pre == 'b' || pre == 'B' || pre == 'x' || pre == 'X') { *ptr++ = pre; - --length; + length--; } // Put the number characters in buf2[]. if (pre == 'b' || pre == 'B') { size_t bits = 0; - size_t pos = 0; + size_t i = 0; // leading zeros - for (bits = 8 * sizeof(unsigned long); bits > 0; bits--) { - if ((n >> (bits - 1)) & 0x1) { break; } + for (bits = 8 * sizeof(n); bits > 0; bits--) { + if ((n >> (bits - 1)) & 0x1) { + break; + } } while (bits > 0) { - buf2[pos++] = ((n >> --bits) & 0x1) ? '1' : '0'; + buf2[i++] = ((n >> --bits) & 0x1) ? '1' : '0'; } - buf2[pos] = '\0'; + buf2[i] = '\0'; } else if (pre == 0) { - snprintf((char *)buf2, NUMBUFLEN, "%" PRIu64, (uint64_t)n); + vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIu64, (uint64_t)n); } else if (pre == '0') { - snprintf((char *)buf2, NUMBUFLEN, "%" PRIo64, (uint64_t)n); + vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIo64, (uint64_t)n); } else if (pre && hexupper) { - snprintf((char *)buf2, NUMBUFLEN, "%" PRIX64, (uint64_t)n); + vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIX64, (uint64_t)n); } else { - snprintf((char *)buf2, NUMBUFLEN, "%" PRIx64, (uint64_t)n); + vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIx64, (uint64_t)n); } length -= (int)STRLEN(buf2); @@ -4460,14 +4615,29 @@ int do_addsub(int command, linenr_T Prenum1) } *ptr = NUL; STRCAT(buf1, buf2); - ins_str(buf1); /* insert the new number */ + ins_str(buf1); // insert the new number xfree(buf1); + endpos = curwin->w_cursor; + if (did_change && curwin->w_cursor.col) { + curwin->w_cursor.col--; + } } - --curwin->w_cursor.col; - curwin->w_set_curswant = true; - ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, true); - RLADDSUBFIX(ptr); - return OK; + + if (did_change) { + // set the '[ and '] marks + curbuf->b_op_start = startpos; + curbuf->b_op_end = endpos; + if (curbuf->b_op_end.col > 0) { + curbuf->b_op_end.col--; + } + } + +theend: + if (visual) { + curwin->w_cursor = save_cursor; + } + + return did_change; } /* diff --git a/src/nvim/ops.h b/src/nvim/ops.h index 507f933acf..f33e87572f 100644 --- a/src/nvim/ops.h +++ b/src/nvim/ops.h @@ -35,37 +35,39 @@ typedef int (*Indenter)(void); #define PLUS_REGISTER 38 #define NUM_REGISTERS 39 -/* - * Operator IDs; The order must correspond to opchars[] in ops.c! - */ -#define OP_NOP 0 /* no pending operation */ -#define OP_DELETE 1 /* "d" delete operator */ -#define OP_YANK 2 /* "y" yank operator */ -#define OP_CHANGE 3 /* "c" change operator */ -#define OP_LSHIFT 4 /* "<" left shift operator */ -#define OP_RSHIFT 5 /* ">" right shift operator */ -#define OP_FILTER 6 /* "!" filter operator */ -#define OP_TILDE 7 /* "g~" switch case operator */ -#define OP_INDENT 8 /* "=" indent operator */ -#define OP_FORMAT 9 /* "gq" format operator */ -#define OP_COLON 10 /* ":" colon operator */ -#define OP_UPPER 11 /* "gU" make upper case operator */ -#define OP_LOWER 12 /* "gu" make lower case operator */ -#define OP_JOIN 13 /* "J" join operator, only for Visual mode */ -#define OP_JOIN_NS 14 /* "gJ" join operator, only for Visual mode */ -#define OP_ROT13 15 /* "g?" rot-13 encoding */ -#define OP_REPLACE 16 /* "r" replace chars, only for Visual mode */ -#define OP_INSERT 17 /* "I" Insert column, only for Visual mode */ -#define OP_APPEND 18 /* "A" Append column, only for Visual mode */ -#define OP_FOLD 19 /* "zf" define a fold */ -#define OP_FOLDOPEN 20 /* "zo" open folds */ -#define OP_FOLDOPENREC 21 /* "zO" open folds recursively */ -#define OP_FOLDCLOSE 22 /* "zc" close folds */ -#define OP_FOLDCLOSEREC 23 /* "zC" close folds recursively */ -#define OP_FOLDDEL 24 /* "zd" delete folds */ -#define OP_FOLDDELREC 25 /* "zD" delete folds recursively */ -#define OP_FORMAT2 26 /* "gw" format operator, keeps cursor pos */ -#define OP_FUNCTION 27 /* "g@" call 'operatorfunc' */ +// Operator IDs; The order must correspond to opchars[] in ops.c! +#define OP_NOP 0 // no pending operation +#define OP_DELETE 1 // "d" delete operator +#define OP_YANK 2 // "y" yank operator +#define OP_CHANGE 3 // "c" change operator +#define OP_LSHIFT 4 // "<" left shift operator +#define OP_RSHIFT 5 // ">" right shift operator +#define OP_FILTER 6 // "!" filter operator +#define OP_TILDE 7 // "g~" switch case operator +#define OP_INDENT 8 // "=" indent operator +#define OP_FORMAT 9 // "gq" format operator +#define OP_COLON 10 // ":" colon operator +#define OP_UPPER 11 // "gU" make upper case operator +#define OP_LOWER 12 // "gu" make lower case operator +#define OP_JOIN 13 // "J" join operator, only for Visual mode +#define OP_JOIN_NS 14 // "gJ" join operator, only for Visual mode +#define OP_ROT13 15 // "g?" rot-13 encoding +#define OP_REPLACE 16 // "r" replace chars, only for Visual mode +#define OP_INSERT 17 // "I" Insert column, only for Visual mode +#define OP_APPEND 18 // "A" Append column, only for Visual mode +#define OP_FOLD 19 // "zf" define a fold +#define OP_FOLDOPEN 20 // "zo" open folds +#define OP_FOLDOPENREC 21 // "zO" open folds recursively +#define OP_FOLDCLOSE 22 // "zc" close folds +#define OP_FOLDCLOSEREC 23 // "zC" close folds recursively +#define OP_FOLDDEL 24 // "zd" delete folds +#define OP_FOLDDELREC 25 // "zD" delete folds recursively +#define OP_FORMAT2 26 // "gw" format operator, keeps cursor pos +#define OP_FUNCTION 27 // "g@" call 'operatorfunc' +#define OP_NR_ADD 28 // "<C-A>" Add to the number or alphabetic + // character (OP_ADD conflicts with Perl) +#define OP_NR_SUB 29 // "<C-X>" Subtract from the number or + // alphabetic character /// Flags for get_reg_contents(). enum GRegFlags { diff --git a/src/nvim/option.c b/src/nvim/option.c index b4054dc28c..18269f4c18 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1432,7 +1432,7 @@ do_set ( } else if (*arg == '-' || ascii_isdigit(*arg)) { // Allow negative (for 'undolevels'), octal and // hex numbers. - vim_str2nr(arg, NULL, &i, true, true, true, &value, NULL); + vim_str2nr(arg, NULL, &i, STR2NR_ALL, &value, NULL, 0); if (arg[i] != NUL && !ascii_iswhite(arg[i])) { errmsg = e_invarg; goto skip; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index b2028109be..cc7dc6210c 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -12910,8 +12910,8 @@ void ex_spelldump(exarg_T *eap) do_cmdline_cmd("new"); // enable spelling locally in the new window - set_option_value((char_u*)"spell", TRUE, (char_u*)"", OPT_LOCAL); - set_option_value((char_u*)"spl", dummy, spl, OPT_LOCAL); + set_option_value((char_u*)"spell", true, (char_u*)"", OPT_LOCAL); + set_option_value((char_u*)"spl", dummy, spl, OPT_LOCAL); xfree(spl); if (!bufempty() || !buf_valid(curbuf)) diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 41ce2daa91..fe511166f2 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -28,6 +28,7 @@ SCRIPTS := \ test_charsearch.out \ test_close_count.out \ test_command_count.out \ + test_marks.out \ NEW_TESTS = @@ -131,7 +132,7 @@ test1.out: .gdbinit test1.in # Check if the test.out file matches test.ok. @/bin/sh -c "if test -f test.out; then \ - if diff test.out $*.ok; then \ + if diff -u test.out $*.ok; then \ mv -f test.out $*.out; \ else \ echo $* FAILED >> test.log; \ diff --git a/src/nvim/testdir/test_marks.in b/src/nvim/testdir/test_marks.in new file mode 100644 index 0000000000..23c2fb65fe --- /dev/null +++ b/src/nvim/testdir/test_marks.in @@ -0,0 +1,34 @@ +Tests for marks. + +STARTTEST +:so small.vim +:" test that a deleted mark is restored after delete-undo-redo-undo +:/^\t/+1 +:set nocp viminfo+=nviminfo +madduu +:let a = string(getpos("'a")) +:$put ='Mark after delete-undo-redo-undo: '.a +:'' +ENDTEST + + textline A + textline B + textline C + +STARTTEST +:" test that CTRL-A and CTRL-X updates last changed mark '[, ']. +:/^123/ +:execute "normal! \<C-A>`[v`]rAjwvjw\<C-X>`[v`]rX" +ENDTEST + +CTRL-A CTRL-X: +123 123 123 +123 123 123 +123 123 123 + +STARTTEST +:g/^STARTTEST/.,/^ENDTEST/d +:wq! test.out +ENDTEST + +Results: diff --git a/src/nvim/testdir/test_marks.ok b/src/nvim/testdir/test_marks.ok new file mode 100644 index 0000000000..e6c02ee7b0 --- /dev/null +++ b/src/nvim/testdir/test_marks.ok @@ -0,0 +1,16 @@ +Tests for marks. + + + textline A + textline B + textline C + + +CTRL-A CTRL-X: +AAA 123 123 +123 XXXXXXX +XXX 123 123 + + +Results: +Mark after delete-undo-redo-undo: [0, 15, 2, 0] diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 6b60f95f22..b8cdffcda0 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -2222,12 +2222,17 @@ static void u_undoredo(int undo) /* * restore marks from before undo/redo */ - for (i = 0; i < NMARKS; ++i) + for (i = 0; i < NMARKS; ++i) { if (curhead->uh_namedm[i].mark.lnum != 0) { free_fmark(curbuf->b_namedm[i]); curbuf->b_namedm[i] = curhead->uh_namedm[i]; + } + if (namedm[i].mark.lnum != 0) { curhead->uh_namedm[i] = namedm[i]; + } else { + curhead->uh_namedm[i].mark.lnum = 0; } + } if (curhead->uh_visual.vi_start.lnum != 0) { curbuf->b_visual = curhead->uh_visual; curhead->uh_visual = visualinfo; diff --git a/src/nvim/version.c b/src/nvim/version.c index 30f104562f..c3555a9731 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -73,12 +73,40 @@ static int included_patches[] = { + 1089, + 1088, + 1087, + // 1086, + 1085, + 1084, + // 1083, + // 1082, 1081, - - - - - + // 1080, + // 1079, + // 1078, + // 1077, + 1076, + // 1075, + // 1074, + // 1073, + 1072, + // 1071, + // 1070, + // 1069, + // 1068, + // 1067, + // 1066, + 1065, + // 1064, + // 1063, + // 1062, + // 1061, + // 1060, + // 1059, + // 1058, + // 1057, + // 1056, 1055, // 1054, // 1053, @@ -105,9 +133,9 @@ static int included_patches[] = { 1032, // 1031, // 1030, - // 1029, + 1029, // 1028, - // 1027, + 1027, // 1026, // 1025, // 1024, @@ -286,7 +314,7 @@ static int included_patches[] = { // 851 NA // 850 NA 849, - // 848, + 848, // 847, // 846 NA // 845, @@ -311,7 +339,7 @@ static int included_patches[] = { 826, // 825, // 824 NA - // 823, + 823, // 822, // 821, // 820, @@ -327,8 +355,8 @@ static int included_patches[] = { // 810, 809, // 808 NA - // 807, - // 806, + 807, + 806, // 805, // 804, 803, @@ -352,11 +380,11 @@ static int included_patches[] = { 785, 784, // 783 NA - // 782, + 782, 781, - // 780 NA - // 779, - // 778, + 780, + 779, + 778, // 777 NA 776, 775, @@ -369,8 +397,8 @@ static int included_patches[] = { // 768, // 767, // 766 NA - // 765, - // 764, + 765, + 764, // 763 NA // 762 NA // 761 NA @@ -380,7 +408,7 @@ static int included_patches[] = { // 757 NA // 756 NA // 755, - // 754, + 754, 753, // 752, // 751 NA @@ -500,7 +528,7 @@ static int included_patches[] = { 637, 636, 635, - // 634, + 634, 633, // 632 NA 631, diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 2e20d48f90..5f9785a9a9 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -35,7 +35,15 @@ Error: configure did not run properly.Check auto/config.log. #include "nvim/os/os_defs.h" /* bring lots of system header files */ -#define NUMBUFLEN 65 // length of a buffer to store a number in ASCII +/// length of a buffer to store a number in ASCII (64 bits binary + NUL) +#define NUMBUFLEN 65 + +// flags for vim_str2nr() +#define STR2NR_BIN 1 +#define STR2NR_OCT 2 +#define STR2NR_HEX 4 +#define STR2NR_ALL (STR2NR_BIN + STR2NR_OCT + STR2NR_HEX) +#define STR2NR_FORCE 8 // only when ONE of the above is used #define MAX_TYPENR 65535 diff --git a/test/functional/legacy/057_sort_spec.lua b/test/functional/legacy/057_sort_spec.lua index 65defbae96..7eed31e292 100644 --- a/test/functional/legacy/057_sort_spec.lua +++ b/test/functional/legacy/057_sort_spec.lua @@ -600,39 +600,72 @@ describe(':sort', function() eq('Vim(sort):E474: Invalid argument', eval('tmpvar')) expect(text) end) - + it('binary', function() insert([[ - 0b111000 - 0b101100 - 0b101001 - 0b101001 - 0b101000 - 0b000000 - 0b001000 - 0b010000 - 0b101000 - 0b100000 - 0b101010 - 0b100010 - 0b100100 - 0b100010]]) + 0b111000 + 0b101100 + 0b101001 + 0b101001 + 0b101000 + 0b000000 + 0b001000 + 0b010000 + 0b101000 + 0b100000 + 0b101010 + 0b100010 + 0b100100 + 0b100010]]) execute([[sort b]]) expect([[ - 0b000000 - 0b001000 - 0b010000 - 0b100000 - 0b100010 - 0b100010 - 0b100100 - 0b101000 - 0b101000 - 0b101001 - 0b101001 - 0b101010 - 0b101100 - 0b111000]]) + 0b000000 + 0b001000 + 0b010000 + 0b100000 + 0b100010 + 0b100010 + 0b100100 + 0b101000 + 0b101000 + 0b101001 + 0b101001 + 0b101010 + 0b101100 + 0b111000]]) end) + it('binary with leading characters', function() + insert([[ + 0b100010 + 0b010000 + 0b101001 + b0b101100 + 0b100010 + 0b100100 + a0b001000 + 0b101000 + 0b101000 + a0b101001 + ab0b100000 + 0b101010 + 0b000000 + b0b111000]]) + execute([[sort b]]) + expect([[ + 0b000000 + a0b001000 + 0b010000 + ab0b100000 + 0b100010 + 0b100010 + 0b100100 + 0b101000 + 0b101000 + 0b101001 + a0b101001 + 0b101010 + b0b101100 + b0b111000]]) + end) end) diff --git a/test/functional/legacy/059_utf8_spell_checking_spec.lua b/test/functional/legacy/059_utf8_spell_checking_spec.lua index 5794e875a0..63df387be3 100644 --- a/test/functional/legacy/059_utf8_spell_checking_spec.lua +++ b/test/functional/legacy/059_utf8_spell_checking_spec.lua @@ -31,8 +31,6 @@ describe("spell checking with 'encoding' set to utf-8", function() RAR ? BAD ! - #NOSPLITSUGS - PFX I N 1 PFX I 0 in . @@ -92,8 +90,6 @@ describe("spell checking with 'encoding' set to utf-8", function() RAR ? BAD ! - #NOSPLITSUGS - PFX I N 1 PFX I 0 in . @@ -300,6 +296,24 @@ describe("spell checking with 'encoding' set to utf-8", function() tail/123 middle/77,1 ]]) + write_latin1('Xtest8.aff', [[ + SET ISO8859-1 + + NOSPLITSUGS + ]]) + write_latin1('Xtest8.dic', [[ + 1234 + foo + bar + faabar + ]]) + write_latin1('Xtest9.aff', [[ + ]]) + write_latin1('Xtest9.dic', [[ + 1234 + foo + bar + ]]) write_latin1('Xtest-sal.aff', [[ SET ISO8859-1 TRY esianrtolcdugmphbyfvkwjkqxz-ëéèêïîäàâöüû'ESIANRTOLCDUGMPHBYFVKWJKQXZ @@ -314,8 +328,6 @@ describe("spell checking with 'encoding' set to utf-8", function() RAR ? BAD ! - #NOSPLITSUGS - PFX I N 1 PFX I 0 in . @@ -483,6 +495,10 @@ describe("spell checking with 'encoding' set to utf-8", function() os.remove('Xtest6.dic') os.remove('Xtest7.aff') os.remove('Xtest7.dic') + os.remove('Xtest8.aff') + os.remove('Xtest8.dic') + os.remove('Xtest9.aff') + os.remove('Xtest9.dic') end) -- Function to test .aff/.dic with list of good and bad words. This was a @@ -940,4 +956,46 @@ describe("spell checking with 'encoding' set to utf-8", function() leadprobar ['leadprebar', 'lead prebar', 'leadbar']]=]) end) + + it('part 8-8', function() + insert([[ + 8good: foo bar faabar + bad: foobar barfoo + badend + ]]) + -- NOSPLITSUGS + test_one(8, 8) + -- Assert buffer contents. + execute('1,/^test 8-8/-1d') + expect([=[ + test 8-8 + # file: Xtest.utf-8.spl + bar + faabar + foo + ------- + bad + ['bar', 'foo'] + foobar + ['faabar', 'foo bar', 'bar'] + barfoo + ['bar foo', 'bar', 'foo']]=]) + end) + + it('part 9-9', function() + insert([[ + 9good: 0b1011 0777 1234 0x01ff + badend + ]]) + -- NOSPLITSUGS + test_one(9, 9) + -- Assert buffer contents. + execute('1,/^test 9-9/-1d') + expect([=[ + test 9-9 + # file: Xtest.utf-8.spl + bar + foo + -------]=]) + end) end) diff --git a/test/functional/legacy/increment_spec.lua b/test/functional/legacy/increment_spec.lua new file mode 100644 index 0000000000..6139ec0b67 --- /dev/null +++ b/test/functional/legacy/increment_spec.lua @@ -0,0 +1,723 @@ +-- Tests for using Ctrl-A/Ctrl-X on visual selections + +local helpers = require('test.functional.helpers') +local source, execute = helpers.source, helpers.execute +local call, clear = helpers.call, helpers.clear +local eq, nvim = helpers.eq, helpers.meths + +describe('Ctrl-A/Ctrl-X on visual selections', function() + + before_each(function() + clear() + source([=[ + " 1) Ctrl-A on visually selected number + " Text: + " foobar-10 + " Expected: + " 1) Ctrl-A on start of line: + " foobar-9 + " 2) Ctrl-A on visually selected "-10": + " foobar-9 + " 3) Ctrl-A on visually selected "10": + " foobar-11 + " 4) Ctrl-X on visually selected "-10" + " foobar-11 + " 5) Ctrl-X on visually selected "10" + " foobar-9 + func Test_visual_increment_01() + call setline(1, repeat(["foobaar-10"], 5)) + + call cursor(1, 1) + exec "norm! \<C-A>" + call assert_equal("foobaar-9", getline('.')) + call assert_equal([0, 1, 9, 0], getpos('.')) + + call cursor(2, 1) + exec "norm! f-v$\<C-A>" + call assert_equal("foobaar-9", getline('.')) + call assert_equal([0, 2, 8, 0], getpos('.')) + + call cursor(3, 1) + exec "norm! f1v$\<C-A>" + call assert_equal("foobaar-11", getline('.')) + call assert_equal([0, 3, 9, 0], getpos('.')) + + call cursor(4, 1) + exec "norm! f-v$\<C-X>" + call assert_equal("foobaar-11", getline('.')) + call assert_equal([0, 4, 8, 0], getpos('.')) + + call cursor(5, 1) + exec "norm! f1v$\<C-X>" + call assert_equal("foobaar-9", getline('.')) + call assert_equal([0, 5, 9, 0], getpos('.')) + endfunc + + " 2) Ctrl-A on visually selected lines + " Text: + " 10 + " 20 + " 30 + " 40 + " + " Expected: + " 1) Ctrl-A on visually selected lines: + " 11 + " 21 + " 31 + " 41 + " + " 2) Ctrl-X on visually selected lines: + " 9 + " 19 + " 29 + " 39 + func Test_visual_increment_02() + call setline(1, ["10", "20", "30", "40"]) + exec "norm! GV3k$\<C-A>" + call assert_equal(["11", "21", "31", "41"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, ["10", "20", "30", "40"]) + exec "norm! GV3k$\<C-X>" + call assert_equal(["9", "19", "29", "39"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 3) g Ctrl-A on visually selected lines, with non-numbers in between + " Text: + " 10 + " + " 20 + " + " 30 + " + " 40 + " + " Expected: + " 1) 2 g Ctrl-A on visually selected lines: + " 12 + " + " 24 + " + " 36 + " + " 48 + " 2) 2 g Ctrl-X on visually selected lines + " 8 + " + " 16 + " + " 24 + " + " 32 + func Test_visual_increment_03() + call setline(1, ["10", "", "20", "", "30", "", "40"]) + exec "norm! GV6k2g\<C-A>" + call assert_equal(["12", "", "24", "", "36", "", "48"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, ["10", "", "20", "", "30", "", "40"]) + exec "norm! GV6k2g\<C-X>" + call assert_equal(["8", "", "16", "", "24", "", "32"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 4) Ctrl-A on non-number + " Text: + " foobar-10 + " Expected: + " 1) visually select foobar: + " foobar-10 + func Test_visual_increment_04() + call setline(1, ["foobar-10"]) + exec "norm! vf-\<C-A>" + call assert_equal(["foobar-10"], getline(1, '$')) + " NOTE: I think this is correct behavior... + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 5) g<Ctrl-A> on letter + " Test: + " a + " a + " a + " a + " Expected: + " 1) g Ctrl-A on visually selected lines + " b + " c + " d + " e + func Test_visual_increment_05() + set nrformats+=alpha + call setline(1, repeat(["a"], 4)) + exec "norm! GV3kg\<C-A>" + call assert_equal(["b", "c", "d", "e"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 6) g<Ctrl-A> on letter + " Test: + " z + " z + " z + " z + " Expected: + " 1) g Ctrl-X on visually selected lines + " y + " x + " w + " v + func Test_visual_increment_06() + set nrformats+=alpha + call setline(1, repeat(["z"], 4)) + exec "norm! GV3kg\<C-X>" + call assert_equal(["y", "x", "w", "v"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 7) <Ctrl-A> on letter + " Test: + " 2 + " 1 + " 0 + " -1 + " -2 + " + " Expected: + " 1) Ctrl-A on visually selected lines + " 3 + " 2 + " 1 + " 0 + " -1 + " + " 2) Ctrl-X on visually selected lines + " 1 + " 0 + " -1 + " -2 + " -3 + func Test_visual_increment_07() + call setline(1, ["2", "1", "0", "-1", "-2"]) + exec "norm! GV4k\<C-A>" + call assert_equal(["3", "2", "1", "0", "-1"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, ["2", "1", "0", "-1", "-2"]) + exec "norm! GV4k\<C-X>" + call assert_equal(["1", "0", "-1", "-2", "-3"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 8) Block increment on 0x9 + " Text: + " 0x9 + " 0x9 + " Expected: + " 1) Ctrl-A on visually block selected region (cursor at beginning): + " 0xa + " 0xa + " 2) Ctrl-A on visually block selected region (cursor at end) + " 0xa + " 0xa + func Test_visual_increment_08() + call setline(1, repeat(["0x9"], 2)) + exec "norm! \<C-V>j$\<C-A>" + call assert_equal(["0xa", "0xa"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, repeat(["0x9"], 2)) + exec "norm! gg$\<C-V>+\<C-A>" + call assert_equal(["0xa", "0xa"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 9) Increment and redo + " Text: + " 2 + " 2 + " + " 3 + " 3 + " + " Expected: + " 1) 2 Ctrl-A on first 2 visually selected lines + " 4 + " 4 + " 2) redo (.) on 3 + " 5 + " 5 + func Test_visual_increment_09() + call setline(1, ["2", "2", "", "3", "3", ""]) + exec "norm! ggVj2\<C-A>" + call assert_equal(["4", "4", "", "3", "3", ""], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + exec "norm! 3j." + call assert_equal(["4", "4", "", "5", "5", ""], getline(1, '$')) + call assert_equal([0, 4, 1, 0], getpos('.')) + endfunc + + " 10) sequentially decrement 1 + " Text: + " 1 + " 1 + " 1 + " 1 + " Expected: + " 1) g Ctrl-X on visually selected lines + " 0 + " -1 + " -2 + " -3 + func Test_visual_increment_10() + call setline(1, repeat(["1"], 4)) + exec "norm! GV3kg\<C-X>" + call assert_equal(["0", "-1", "-2", "-3"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 11) visually block selected indented lines + " Text: + " 1 + " 1 + " 1 + " 1 + " Expexted: + " 1) g Ctrl-A on block selected indented lines + " 2 + " 1 + " 3 + " 4 + func Test_visual_increment_11() + call setline(1, [" 1", "1", " 1", " 1"]) + exec "norm! f1\<C-V>3jg\<C-A>" + call assert_equal([" 2", "1", " 3", " 4"], getline(1, '$')) + call assert_equal([0, 1, 5, 0], getpos('.')) + endfunc + + " 12) visually selected several columns + " Text: + " 0 0 + " 0 0 + " 0 0 + " Expected: + " 1) 'v' select last zero and first zeroes + " 0 1 + " 1 0 + " 1 0 + func Test_visual_increment_12() + call setline(1, repeat(["0 0"], 3)) + exec "norm! $v++\<C-A>" + call assert_equal(["0 1", "1 0", "1 0"], getline(1, '$')) + call assert_equal([0, 1, 3, 0], getpos('.')) + endfunc + + " 13) visually selected part of columns + " Text: + " max: 100px + " max: 200px + " max: 300px + " max: 400px + " Expected: + " 1) 'v' on first two numbers Ctrl-A + " max: 110px + " max: 220px + " max: 330px + " max: 400px + " 2) 'v' on first two numbers Ctrl-X + " max: 90px + " max: 190px + " max: 290px + " max: 400px + func Test_visual_increment_13() + call setline(1, ["max: 100px", "max: 200px", "max: 300px", "max: 400px"]) + exec "norm! f1\<C-V>l2j\<C-A>" + call assert_equal(["max: 110px", "max: 210px", "max: 310px", "max: 400px"], getline(1, '$')) + call assert_equal([0, 1, 6, 0], getpos('.')) + + call setline(1, ["max: 100px", "max: 200px", "max: 300px", "max: 400px"]) + exec "norm! ggf1\<C-V>l2j\<C-X>" + call assert_equal(["max: 90px", "max: 190px", "max: 290px", "max: 400px"], getline(1, '$')) + call assert_equal([0, 1, 6, 0], getpos('.')) + endfunc + + " 14) redo in block mode + " Text: + " 1 1 + " 1 1 + " Expected: + " 1) Ctrl-a on first column, redo on second column + " 2 2 + " 2 2 + func Test_visual_increment_14() + call setline(1, repeat(["1 1"], 2)) + exec "norm! G\<C-V>k\<C-A>w." + call assert_equal(["2 2", "2 2"], getline(1, '$')) + call assert_equal([0, 1, 3, 0], getpos('.')) + endfunc + + " 15) block select single numbers + " Text: + " 101 + " Expected: + " 1) Ctrl-a on visually selected zero + " 111 + func Test_visual_increment_15() + call setline(1, ["101"]) + exec "norm! lv\<C-A>" + call assert_equal(["111"], getline(1, '$')) + call assert_equal([0, 1, 2, 0], getpos('.')) + endfunc + + " 16) increment right aligned numbers + " Text: + " 1 + " 19 + " 119 + " Expected: + " 1) Ctrl-a on line selected region + " 2 + " 20 + " 120 + func Test_visual_increment_16() + call setline(1, [" 1", " 19", " 119"]) + exec "norm! VG\<C-A>" + call assert_equal([" 2", " 20", " 120"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 17) block-wise increment and redo + " Text: + " 100 + " 1 + " + " 100 + " 1 + " + " Expected: + " 1) Ctrl-V j $ on first block, afterwards '.' on second + " 101 + " 2 + " + " 101 + " 2 + func Test_visual_increment_17() + call setline(1, [" 100", " 1", "", " 100", " 1"]) + exec "norm! \<C-V>j$\<C-A>2j." + call assert_equal([" 101", " 2", "", " 101", " 1"], getline(1, '$')) + call assert_equal([0, 3, 1, 0], getpos('.')) + endfunc + + " 18) repeat of g<Ctrl-a> + " Text: + " 0 + " 0 + " 0 + " 0 + " + " Expected: + " 1) V 4j g<ctrl-a>, repeat twice afterwards with . + " 3 + " 6 + " 9 + " 12 + func Test_visual_increment_18() + call setline(1, repeat(["0"], 4)) + exec "norm! GV3kg\<C-A>" + exec "norm! .." + call assert_equal(["3", "6", "9", "12"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 19) increment on number with nrformat including alpha + " Text: + " 1 + " 1a + " + " Expected: + " 1) <Ctrl-V>j$ <ctrl-a> + " 2 + " 2a + func Test_visual_increment_19() + set nrformats+=alpha + call setline(1, ["1", "1a"]) + exec "norm! \<C-V>G$\<C-A>" + call assert_equal(["2", "2a"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 20) increment a single letter + " Text: + " a + " + " Expected: + " 1) <Ctrl-a> and cursor is on a + " b + func Test_visual_increment_20() + set nrformats+=alpha + call setline(1, ["a"]) + exec "norm! \<C-A>" + call assert_equal(["b"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 21) block-wise increment on part of hexadecimal + " Text: + " 0x123456 + " + " Expected: + " 1) Ctrl-V f3 <ctrl-a> + " 0x124456 + func Test_visual_increment_21() + call setline(1, ["0x123456"]) + exec "norm! \<C-V>f3\<C-A>" + call assert_equal(["0x124456"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 22) Block increment on 0b0 + " Text: + " 0b1 + " 0b1 + " Expected: + " 1) Ctrl-A on visually block selected region (cursor at beginning): + " 0b10 + " 0b10 + " 2) Ctrl-A on visually block selected region (cursor at end) + " 0b10 + " 0b10 + func Test_visual_increment_22() + call setline(1, repeat(["0b1"], 2)) + exec "norm! \<C-V>j$\<C-A>" + call assert_equal(repeat(["0b10"], 2), getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, repeat(["0b1"], 2)) + exec "norm! $\<C-V>+\<C-A>" + call assert_equal(repeat(["0b10"], 2), getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 23) block-wise increment on part of binary + " Text: + " 0b1001 + " + " Expected: + " 1) Ctrl-V 5l <ctrl-a> + " 0b1011 + func Test_visual_increment_23() + call setline(1, ["0b1001"]) + exec "norm! \<C-V>4l\<C-A>" + call assert_equal(["0b1011"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 24) increment hexadecimal + " Text: + " 0x0b1001 + " + " Expected: + " 1) <ctrl-a> + " 0x0b1002 + func Test_visual_increment_24() + call setline(1, ["0x0b1001"]) + exec "norm! \<C-V>$\<C-A>" + call assert_equal(["0x0b1002"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 25) increment binary with nrformats including alpha + " Text: + " 0b1001a + " + " Expected: + " 1) <ctrl-a> + " 0b1010a + func Test_visual_increment_25() + set nrformats+=alpha + call setline(1, ["0b1001a"]) + exec "norm! \<C-V>$\<C-A>" + call assert_equal(["0b1010a"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 26) increment binary with 32 bits + " Text: + " 0b11111111111111111111111111111110 + " + " Expected: + " 1) <ctrl-a> + " 0b11111111111111111111111111111111 + func Test_visual_increment_26() + set nrformats+=alpha + call setline(1, ["0b11111111111111111111111111111110"]) + exec "norm! \<C-V>$\<C-A>" + call assert_equal(["0b11111111111111111111111111111111"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + set nrformats-=alpha + endfunc + + " 27) increment with 'rightreft', if supported + func Test_visual_increment_27() + if exists('+rightleft') + set rightleft + call setline(1, ["1234 56"]) + + exec "norm! $\<C-A>" + call assert_equal(["1234 57"], getline(1, '$')) + call assert_equal([0, 1, 7, 0], getpos('.')) + + exec "norm! \<C-A>" + call assert_equal(["1234 58"], getline(1, '$')) + call assert_equal([0, 1, 7, 0], getpos('.')) + set norightleft + endif + endfunc + + " Tab code and linewise-visual inc/dec + func Test_visual_increment_28() + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! Vj\<C-A>" + call assert_equal(["x\<TAB>11", "\<TAB>0"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! ggVj\<C-X>" + call assert_equal(["x\<TAB>9", "\<TAB>-2"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " Tab code and linewise-visual inc/dec with 'nrformats'+=alpha + func Test_visual_increment_29() + set nrformats+=alpha + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! Vj\<C-A>" + call assert_equal(["y\<TAB>10", "\<TAB>0"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! ggVj\<C-X>" + call assert_equal(["w\<TAB>10", "\<TAB>-2"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " Tab code and character-visual inc/dec + func Test_visual_increment_30() + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! f1vjf1\<C-A>" + call assert_equal(["x\<TAB>11", "\<TAB>0"], getline(1, '$')) + call assert_equal([0, 1, 3, 0], getpos('.')) + + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! ggf1vjf1\<C-X>" + call assert_equal(["x\<TAB>9", "\<TAB>-2"], getline(1, '$')) + call assert_equal([0, 1, 3, 0], getpos('.')) + endfunc + + " Tab code and blockwise-visual inc/dec + func Test_visual_increment_31() + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! f1\<C-V>jl\<C-A>" + call assert_equal(["x\<TAB>11", "\<TAB>0"], getline(1, '$')) + call assert_equal([0, 1, 3, 0], getpos('.')) + + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! ggf1\<C-V>jl\<C-X>" + call assert_equal(["x\<TAB>9", "\<TAB>-2"], getline(1, '$')) + call assert_equal([0, 1, 3, 0], getpos('.')) + endfunc + + " Tab code and blockwise-visual decrement with 'linebreak' and 'showbreak' + func Test_visual_increment_32() + 28vnew dummy_31 + set linebreak showbreak=+ + call setline(1, ["x\<TAB>\<TAB>\<TAB>10", "\<TAB>\<TAB>\<TAB>\<TAB>-1"]) + exec "norm! ggf0\<C-V>jg_\<C-X>" + call assert_equal(["x\<TAB>\<TAB>\<TAB>1-1", "\<TAB>\<TAB>\<TAB>\<TAB>-2"], getline(1, '$')) + call assert_equal([0, 1, 6, 0], getpos('.')) + bwipe! + endfunc + + " Tab code and blockwise-visual increment with $ + func Test_visual_increment_33() + call setline(1, ["\<TAB>123", "456"]) + exec "norm! gg0\<C-V>j$\<C-A>" + call assert_equal(["\<TAB>124", "457"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " Tab code and blockwise-visual increment and redo + func Test_visual_increment_34() + call setline(1, ["\<TAB>123", " 456789"]) + exec "norm! gg0\<C-V>j\<C-A>" + call assert_equal(["\<TAB>123", " 457789"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + exec "norm! .." + call assert_equal(["\<TAB>123", " 459789"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " Tab code, spaces and character-visual increment and redo + func Test_visual_increment_35() + call setline(1, ["\<TAB>123", " 123", "\<TAB>123", "\<TAB>123"]) + exec "norm! ggvjf3\<C-A>..." + call assert_equal(["\<TAB>127", " 127", "\<TAB>123", "\<TAB>123"], getline(1, '$')) + call assert_equal([0, 1, 2, 0], getpos('.')) + endfunc + + " Tab code, spaces and blockwise-visual increment and redo + func Test_visual_increment_36() + call setline(1, [" 123", "\<TAB>456789"]) + exec "norm! G0\<C-V>kl\<C-A>" + call assert_equal([" 123", "\<TAB>556789"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + exec "norm! ..." + call assert_equal([" 123", "\<TAB>856789"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " block-wise increment and dot-repeat + " Text: + " 1 23 + " 4 56 + " + " Expected: + " 1) f2 Ctrl-V jl <ctrl-a>, repeat twice afterwards with . + " 1 26 + " 4 59 + " + " Try with and without indent. + func Test_visual_increment_37() + call setline(1, [" 1 23", " 4 56"]) + exec "norm! ggf2\<C-V>jl\<C-A>.." + call assert_equal([" 1 26", " 4 59"], getline(1, 2)) + + call setline(1, ["1 23", "4 56"]) + exec "norm! ggf2\<C-V>jl\<C-A>.." + call assert_equal(["1 26", "4 59"], getline(1, 2)) + endfunc + + " Check redo after the normal mode increment + func Test_visual_increment_38() + exec "norm! i10\<ESC>5\<C-A>." + call assert_equal(["20"], getline(1, '$')) + call assert_equal([0, 1, 2, 0], getpos('.')) + endfunc + ]=]) + end) + + for i = 1, 38 do + local id = string.format('%02d', i) + + it('works on Test ' .. id, function() + execute('set nrformats&vi') -- &vi makes Vim compatible + call('Test_visual_increment_' .. id) + eq({}, nvim.get_vvar('errors')) + end) + end +end) |