# HG changeset patch # User Bram Moolenaar # Date 1435233456 -7200 # Node ID 9798a98a1583fec87694567028d969a5abdd7695 # Parent 92ce9afd7729dc177efa212cf3fa82bc9032dd72 patch 7.4.754 Problem: Using CTRL-A in Visual mode does not work well. (Gary Johnson) Solution: Make it increment all numbers in the Visual area. (Christian Brabandt) diff --git a/src/normal.c b/src/normal.c --- a/src/normal.c +++ b/src/normal.c @@ -4201,9 +4201,17 @@ nv_help(cap) nv_addsub(cap) cmdarg_T *cap; { - if (!checkclearopq(cap->oap) - && do_addsub((int)cap->cmdchar, cap->count1) == OK) + int visual = VIsual_active; + if (cap->oap->op_type == OP_NOP + && do_addsub((int)cap->cmdchar, cap->count1, cap->arg) == OK) prep_redo_cmd(cap); + else + clearopbeep(cap->oap); + if (visual) + { + VIsual_active = FALSE; + redraw_later(CLEAR); + } } /* @@ -7841,14 +7849,28 @@ nv_g_cmd(cap) switch (cap->nchar) { + case Ctrl_A: + case Ctrl_X: #ifdef MEM_PROFILE /* * "g^A": dump log of used memory. */ - case Ctrl_A: - vim_mem_profile_dump(); + if (!VIsual_active && cap->nchar == Ctrl_A) + vim_mem_profile_dump(); + else +#endif + /* + * "g^A/g^X": sequentially increment visually selected region + */ + if (VIsual_active) + { + cap->arg = TRUE; + cap->cmdchar = cap->nchar; + nv_addsub(cap); + } + else + clearopbeep(oap); break; -#endif #ifdef FEAT_VREPLACE /* diff --git a/src/ops.c b/src/ops.c --- a/src/ops.c +++ b/src/ops.c @@ -5375,9 +5375,10 @@ reverse_line(s) * return FAIL for failure, OK otherwise */ int -do_addsub(command, Prenum1) +do_addsub(command, Prenum1, g_cmd) int command; linenr_T Prenum1; + int g_cmd; /* was g/g */ { int col; char_u *buf1; @@ -5385,6 +5386,7 @@ do_addsub(command, Prenum1) int hex; /* 'X' or 'x': hex; '0': octal */ static int hexupper = FALSE; /* 0xABC */ unsigned long n; + long offset = 0; /* line offset for Ctrl_V mode */ long_u oldn; char_u *ptr; int c; @@ -5394,247 +5396,302 @@ do_addsub(command, Prenum1) int dooct; int doalp; int firstdigit; - int negative; int subtract; + int negative = FALSE; + int visual = VIsual_active; + int i; + int lnum = curwin->w_cursor.lnum; + int lnume = curwin->w_cursor.lnum; dohex = (vim_strchr(curbuf->b_p_nf, 'x') != NULL); /* "heX" */ dooct = (vim_strchr(curbuf->b_p_nf, 'o') != NULL); /* "Octal" */ doalp = (vim_strchr(curbuf->b_p_nf, 'p') != NULL); /* "alPha" */ - ptr = ml_get_curline(); - RLADDSUBFIX(ptr); - /* * First check if we are on a hexadecimal number, after the "0x". */ col = curwin->w_cursor.col; - if (dohex) - while (col > 0 && vim_isxdigit(ptr[col])) - --col; - if ( dohex - && col > 0 - && (ptr[col] == 'X' - || ptr[col] == 'x') - && ptr[col - 1] == '0' - && vim_isxdigit(ptr[col + 1])) - { - /* - * Found hexadecimal number, move to its start. - */ - --col; + if (VIsual_active) + { + if (lt(curwin->w_cursor, VIsual)) + { + pos_T t; + t = curwin->w_cursor; + curwin->w_cursor = VIsual; + VIsual = t; + } + if (VIsual_mode == 'V') + VIsual.col = 0; + + ptr = ml_get(VIsual.lnum); + RLADDSUBFIX(ptr); + + /* store visual area for 'gv' */ + curbuf->b_visual.vi_start = VIsual; + curbuf->b_visual.vi_end = curwin->w_cursor; + curbuf->b_visual.vi_mode = VIsual_mode; + + col = VIsual.col; + lnum = VIsual.lnum; + lnume = curwin->w_cursor.lnum; + if (ptr[col] == '-') + { + negative = TRUE; + col++; + } } else { - /* - * Search forward and then backward to find the start of number. - */ - col = curwin->w_cursor.col; - - while (ptr[col] != NUL - && !vim_isdigit(ptr[col]) - && !(doalp && ASCII_ISALPHA(ptr[col]))) - ++col; - - while (col > 0 - && vim_isdigit(ptr[col - 1]) - && !(doalp && ASCII_ISALPHA(ptr[col]))) + ptr = ml_get_curline(); + RLADDSUBFIX(ptr); + + if (dohex) + while (col > 0 && vim_isxdigit(ptr[col])) + --col; + if ( dohex + && col > 0 + && (ptr[col] == 'X' + || ptr[col] == 'x') + && ptr[col - 1] == '0' + && vim_isxdigit(ptr[col + 1])) + { + /* Found hexadecimal number, move to its start. */ --col; - } - - /* - * If a number was found, and saving for undo works, replace the number. - */ - firstdigit = ptr[col]; - RLADDSUBFIX(ptr); - if ((!VIM_ISDIGIT(firstdigit) && !(doalp && ASCII_ISALPHA(firstdigit))) - || u_save_cursor() != OK) - { - beep_flush(); - return FAIL; - } - - /* get ptr again, because u_save() may have changed it */ - ptr = ml_get_curline(); - RLADDSUBFIX(ptr); - - if (doalp && ASCII_ISALPHA(firstdigit)) - { - /* decrement or increment alphabetic character */ - if (command == Ctrl_X) + } + else { - if (CharOrd(firstdigit) < Prenum1) + /* + * Search forward and then backward to find the start of number. + */ + col = curwin->w_cursor.col; + + while (ptr[col] != NUL + && !vim_isdigit(ptr[col]) + && !(doalp && ASCII_ISALPHA(ptr[col]))) + ++col; + + while (col > 0 + && vim_isdigit(ptr[col - 1]) + && !(doalp && ASCII_ISALPHA(ptr[col]))) + --col; + } + } + + for (i = lnum; i <= lnume; i++) + { + curwin->w_cursor.lnum = i; + ptr = ml_get_curline(); + RLADDSUBFIX(ptr); + if ((int)STRLEN(ptr) <= col) + col = 0; + /* + * If a number was found, and saving for undo works, replace the number. + */ + firstdigit = ptr[col]; + RLADDSUBFIX(ptr); + if ((!VIM_ISDIGIT(firstdigit) && !(doalp && ASCII_ISALPHA(firstdigit))) + || u_save_cursor() != OK) + { + if (lnum < lnume) + /* Try again on next line */ + continue; + beep_flush(); + return FAIL; + } + + ptr = ml_get_curline(); + RLADDSUBFIX(ptr); + + if (doalp && ASCII_ISALPHA(firstdigit)) + { + /* decrement or increment alphabetic character */ + if (command == Ctrl_X) { - if (isupper(firstdigit)) - firstdigit = 'A'; + if (CharOrd(firstdigit) < Prenum1) + { + if (isupper(firstdigit)) + firstdigit = 'A'; + else + firstdigit = 'a'; + } else - firstdigit = 'a'; +#ifdef EBCDIC + firstdigit = EBCDIC_CHAR_ADD(firstdigit, -Prenum1); +#else + firstdigit -= Prenum1; +#endif } else + { + if (26 - CharOrd(firstdigit) - 1 < Prenum1) + { + if (isupper(firstdigit)) + firstdigit = 'Z'; + else + firstdigit = 'z'; + } + else #ifdef EBCDIC - firstdigit = EBCDIC_CHAR_ADD(firstdigit, -Prenum1); + firstdigit = EBCDIC_CHAR_ADD(firstdigit, Prenum1); #else - firstdigit -= Prenum1; -#endif + firstdigit += Prenum1; +#endif + } + curwin->w_cursor.col = col; + (void)del_char(FALSE); + ins_char(firstdigit); } else { - if (26 - CharOrd(firstdigit) - 1 < Prenum1) + if (col > 0 && ptr[col - 1] == '-' && !visual) + { + /* negative number */ + --col; + negative = TRUE; + } + + /* get the number value (unsigned) */ + vim_str2nr(ptr + col, &hex, &length, dooct, dohex, NULL, &n); + + /* ignore leading '-' for hex and octal numbers */ + if (hex && negative) { - if (isupper(firstdigit)) - firstdigit = 'Z'; - else - firstdigit = 'z'; + ++col; + --length; + negative = FALSE; } + + /* add or subtract */ + subtract = FALSE; + if (command == Ctrl_X) + subtract ^= TRUE; + if (negative) + subtract ^= TRUE; + + oldn = n; + if (subtract) + n -= (unsigned long)Prenum1; else -#ifdef EBCDIC - firstdigit = EBCDIC_CHAR_ADD(firstdigit, Prenum1); -#else - firstdigit += Prenum1; -#endif - } - curwin->w_cursor.col = col; - (void)del_char(FALSE); - ins_char(firstdigit); - } - else - { - negative = FALSE; - if (col > 0 && ptr[col - 1] == '-') /* negative number */ - { - --col; - negative = TRUE; - } - - /* get the number value (unsigned) */ - vim_str2nr(ptr + col, &hex, &length, dooct, dohex, NULL, &n); - - /* ignore leading '-' for hex and octal numbers */ - if (hex && negative) - { - ++col; - --length; - negative = FALSE; - } - - /* add or subtract */ - subtract = FALSE; - if (command == Ctrl_X) - subtract ^= TRUE; - if (negative) - subtract ^= TRUE; - - oldn = n; - if (subtract) - n -= (unsigned long)Prenum1; - else - n += (unsigned long)Prenum1; - - /* handle wraparound for decimal numbers */ - if (!hex) - { - if (subtract) + n += (unsigned long)Prenum1; + + /* handle wraparound for decimal numbers */ + if (!hex) + { + if (subtract) + { + if (n > oldn) + { + n = 1 + (n ^ (unsigned long)-1); + negative ^= TRUE; + } + } + else + { + /* add */ + if (n < oldn) + { + n = (n ^ (unsigned long)-1); + negative ^= TRUE; + } + } + if (n == 0) + negative = FALSE; + } + + /* + * Delete the old number. + */ + curwin->w_cursor.col = col; + 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; + while (todel-- > 0) { - if (n > oldn) + if (c < 0x100 && isalpha(c)) { - n = 1 + (n ^ (unsigned long)-1); - negative ^= TRUE; + if (isupper(c)) + hexupper = TRUE; + else + hexupper = FALSE; } + /* del_char() will mark line needing displaying */ + (void)del_char(FALSE); + c = gchar_cursor(); } - else /* add */ + + /* + * Prepare the leading characters in buf1[]. + * When there are many leading zeros it could be very long. + * Allocate a bit too much. + */ + buf1 = alloc((unsigned)length + NUMBUFLEN); + if (buf1 == NULL) + return FAIL; + ptr = buf1; + /* do not add leading '-' for visual mode */ + if (negative && !visual) + { + *ptr++ = '-'; + } + if (hex) + { + *ptr++ = '0'; + --length; + } + if (hex == 'x' || hex == 'X') { - if (n < oldn) - { - n = (n ^ (unsigned long)-1); - negative ^= TRUE; - } + *ptr++ = hex; + --length; } - if (n == 0) - negative = FALSE; + + /* + * Put the number characters in buf2[]. + */ + if (hex == 0) + sprintf((char *)buf2, "%lu", n + offset); + else if (hex == '0') + sprintf((char *)buf2, "%lo", n + offset); + else if (hex && hexupper) + sprintf((char *)buf2, "%lX", n + offset); + else + sprintf((char *)buf2, "%lx", n + offset); + length -= (int)STRLEN(buf2); + + if (g_cmd) + { + if (subtract) + offset -= (unsigned long)Prenum1; + else + offset += (unsigned long)Prenum1; + } + + /* + * Adjust number of zeros to the new number of digits, so the + * total length of the number remains the same. + * Don't do this when + * the result may look like an octal number. + */ + if (firstdigit == '0' && !(dooct && hex == 0)) + while (length-- > 0) + *ptr++ = '0'; + *ptr = NUL; + STRCAT(buf1, buf2); + ins_str(buf1); /* insert the new number */ + vim_free(buf1); } - - /* - * Delete the old number. - */ - curwin->w_cursor.col = col; - 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; - while (todel-- > 0) - { - if (c < 0x100 && isalpha(c)) - { - if (isupper(c)) - hexupper = TRUE; - else - hexupper = FALSE; - } - /* del_char() will mark line needing displaying */ - (void)del_char(FALSE); - c = gchar_cursor(); - } - - /* - * Prepare the leading characters in buf1[]. - * When there are many leading zeros it could be very long. Allocate - * a bit too much. - */ - buf1 = alloc((unsigned)length + NUMBUFLEN); - if (buf1 == NULL) - return FAIL; - ptr = buf1; - if (negative) - { - *ptr++ = '-'; - } - if (hex) - { - *ptr++ = '0'; - --length; - } - if (hex == 'x' || hex == 'X') - { - *ptr++ = hex; - --length; - } - - /* - * Put the number characters in buf2[]. - */ - if (hex == 0) - sprintf((char *)buf2, "%lu", n); - else if (hex == '0') - sprintf((char *)buf2, "%lo", n); - else if (hex && hexupper) - sprintf((char *)buf2, "%lX", n); - else - sprintf((char *)buf2, "%lx", n); - length -= (int)STRLEN(buf2); - - /* - * Adjust number of zeros to the new number of digits, so the - * total length of the number remains the same. - * Don't do this when - * the result may look like an octal number. - */ - if (firstdigit == '0' && !(dooct && hex == 0)) - while (length-- > 0) - *ptr++ = '0'; - *ptr = NUL; - STRCAT(buf1, buf2); - ins_str(buf1); /* insert the new number */ - vim_free(buf1); - } - --curwin->w_cursor.col; - curwin->w_set_curswant = TRUE; + --curwin->w_cursor.col; + curwin->w_set_curswant = TRUE; #ifdef FEAT_RIGHTLEFT - ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE); - RLADDSUBFIX(ptr); -#endif + ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE); + RLADDSUBFIX(ptr); +#endif + } return OK; } diff --git a/src/proto/ops.pro b/src/proto/ops.pro --- a/src/proto/ops.pro +++ b/src/proto/ops.pro @@ -43,7 +43,7 @@ void op_formatexpr __ARGS((oparg_T *oap) int fex_format __ARGS((linenr_T lnum, long count, int c)); void format_lines __ARGS((linenr_T line_count, int avoid_fex)); int paragraph_start __ARGS((linenr_T lnum)); -int do_addsub __ARGS((int command, linenr_T Prenum1)); +int do_addsub __ARGS((int command, linenr_T Prenum1, int g_cmd)); int read_viminfo_register __ARGS((vir_T *virp, int force)); void write_viminfo_registers __ARGS((FILE *fp)); void x11_export_final_selection __ARGS((void)); diff --git a/src/testdir/Make_amiga.mak b/src/testdir/Make_amiga.mak --- a/src/testdir/Make_amiga.mak +++ b/src/testdir/Make_amiga.mak @@ -45,6 +45,7 @@ SCRIPTS = test1.out test3.out test4.out test_command_count.out \ test_erasebackword.out \ test_eval.out \ + test_increment.out \ test_insertcount.out \ test_listchars.out \ test_listlbr.out \ @@ -192,6 +193,7 @@ test_close_count.out: test_close_count.i test_command_count.out: test_command_count.in test_erasebackword.out: test_erasebackword.in test_eval.out: test_eval.in +test_increment.out: test_increment.in test_insertcount.out: test_insertcount.in test_listchars.out: test_listchars.in test_listlbr.out: test_listlbr.in diff --git a/src/testdir/Make_dos.mak b/src/testdir/Make_dos.mak --- a/src/testdir/Make_dos.mak +++ b/src/testdir/Make_dos.mak @@ -44,6 +44,7 @@ SCRIPTS = test3.out test4.out test5.out test_command_count.out \ test_erasebackword.out \ test_eval.out \ + test_increment.out \ test_insertcount.out \ test_listchars.out \ test_listlbr.out \ diff --git a/src/testdir/Make_ming.mak b/src/testdir/Make_ming.mak --- a/src/testdir/Make_ming.mak +++ b/src/testdir/Make_ming.mak @@ -66,6 +66,7 @@ SCRIPTS = test3.out test4.out test5.out test_command_count.out \ test_erasebackword.out \ test_eval.out \ + test_increment.out \ test_insertcount.out \ test_listchars.out \ test_listlbr.out \ diff --git a/src/testdir/Make_os2.mak b/src/testdir/Make_os2.mak --- a/src/testdir/Make_os2.mak +++ b/src/testdir/Make_os2.mak @@ -46,6 +46,7 @@ SCRIPTS = test1.out test3.out test4.out test_command_count.out \ test_erasebackword.out \ test_eval.out \ + test_increment.out \ test_insertcount.out \ test_listchars.out \ test_listlbr.out \ diff --git a/src/testdir/Make_vms.mms b/src/testdir/Make_vms.mms --- a/src/testdir/Make_vms.mms +++ b/src/testdir/Make_vms.mms @@ -105,6 +105,7 @@ SCRIPT = test1.out test2.out test3.out test_command_count.out \ test_erasebackword.out \ test_eval.out \ + test_increment.out \ test_insertcount.out \ test_listchars.out \ test_listlbr.out \ diff --git a/src/testdir/Makefile b/src/testdir/Makefile --- a/src/testdir/Makefile +++ b/src/testdir/Makefile @@ -42,6 +42,7 @@ SCRIPTS = test1.out test2.out test3.out test_command_count.out \ test_erasebackword.out \ test_eval.out \ + test_increment.out \ test_insertcount.out \ test_listchars.out \ test_listlbr.out \ diff --git a/src/testdir/test_increment.in b/src/testdir/test_increment.in new file mode 100644 --- /dev/null +++ b/src/testdir/test_increment.in @@ -0,0 +1,143 @@ +Tests for using Ctrl-A/Ctrl-X on visual selections + +Test cases +========== + +1) Ctrl-A on visually selected number +Text: +foobar-10 + 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 + +2) Ctrl-A on visually selected lines +Text: +10 +20 +30 +40 + + 1) Ctrl-A on visually selected lines: +11 +21 +31 +41 + + 2) Ctrl-X on visually selected lines: +9 +19 +29 +39 + +3) g Ctrl-A on visually selected lines, with non-numbers in between +Text: +10 + +20 + +30 + +40 + + 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 + +4) Ctrl-A on non-number +Text: +foobar-10 + 1) visually select foobar: + foobar-10 + +STARTTEST +:so small.vim + +:" Test 1 +:/^S1=/+,/^E1=/-y a +:/^E1/+put a +:/^E1/+2put a +f-v$:/^E1/+3put a +f1v$:/^E1/+4put a +f-v$:/^E1/+5put a +f1v$ + +:" Test 22 +:/^S2=/+,/^E2=/-y a +:/^E2/+put a +V3k$:.+put a +V3k$ + +:" Test 3 +:/^S3=/+,/^E3=/-y a +:/^E3=/+put a +V6k2g:.+put a +V6k2g + +:" Test 4 +:/^S4=/+,/^E4=/-y a +:/^E4=/+put a +vf- + +:" Save the report +:/^# Test 1/,$w! test.out +:qa! + + +# Test 1 +S1====== +foobar-10 +E1====== + + + +# Test 2 +S2===== +10 +20 +30 +40 +E2===== + + + +# Test 3 +S3===== +10 + +20 + +30 + +40 +E3===== + + + +# Test 4 +S4===== +foobar-10 +E4===== + + +ENDTEST + diff --git a/src/testdir/test_increment.ok b/src/testdir/test_increment.ok new file mode 100644 --- /dev/null +++ b/src/testdir/test_increment.ok @@ -0,0 +1,66 @@ +# Test 1 +S1====== +foobar-10 +E1====== + +foobar-9 +foobar-9 +foobar-11 +foobar-11 +foobar-9 + + +# Test 2 +S2===== +10 +20 +30 +40 +E2===== + +11 +21 +31 +41 + +9 +19 +29 +39 + +# Test 3 +S3===== +10 + +20 + +30 + +40 +E3===== + +12 + +24 + +36 + +48 + +8 + +16 + +24 + +32 + +# Test 4 +S4===== +foobar-10 +E4===== + +foobar-10 + +ENDTEST + diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 754, +/**/ 753, /**/ 752,