aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwatiko <service@mail.watiko.net>2016-01-14 17:06:55 +0900
committerwatiko <service@mail.watiko.net>2016-02-01 03:43:36 +0900
commit4d074e39ea1228e4f04c70ecfe4e1196a9638176 (patch)
treefc56179f5b08eac3233d961c79e5b2ee49f2f577
parent8f22031708b351ec5bb73952e6afc39b07a72ae2 (diff)
downloadrneovim-4d074e39ea1228e4f04c70ecfe4e1196a9638176.tar.gz
rneovim-4d074e39ea1228e4f04c70ecfe4e1196a9638176.tar.bz2
rneovim-4d074e39ea1228e4f04c70ecfe4e1196a9638176.zip
vim-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) https://github.com/vim/vim/commit/3a304b23823b089e499063e8211c5695d049f3ba
-rw-r--r--src/nvim/normal.c28
-rw-r--r--src/nvim/ops.c450
-rw-r--r--src/nvim/testdir/Makefile3
-rw-r--r--src/nvim/testdir/test_increment.in143
-rw-r--r--src/nvim/testdir/test_increment.ok66
-rw-r--r--src/nvim/version.c2
6 files changed, 485 insertions, 207 deletions
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index a116b5a0bd..684467d26a 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -3503,9 +3503,17 @@ static void nv_help(cmdarg_T *cap)
*/
static void nv_addsub(cmdarg_T *cap)
{
- if (!checkclearopq(cap->oap)
- && do_addsub(cap->cmdchar, cap->count1))
+ bool 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);
+ }
}
/*
@@ -6327,9 +6335,19 @@ 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;
+ 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..af738d3fa9 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -4201,272 +4201,322 @@ static void reverse_line(char_u *s)
/// 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 Prenum1 number to add or subtract
+/// @param g_cmd Prefixed with `g`.
///
/// @return FAIL for failure, OK otherwise
-int do_addsub(int command, linenr_T Prenum1)
+int do_addsub(int command, linenr_T Prenum1, bool g_cmd)
{
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;
+ long offset = 0;
+ unsigned long oldn;
char_u *ptr;
int c;
- int length = 0; // character length of the number
+ int length = 0; // character length of the number
int todel;
int dohex;
int dooct;
int dobin;
int doalp;
int firstdigit;
- int negative;
- int subtract;
+ bool subtract;
+ bool negative = false;
+ bool visual = VIsual_active;
+ 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"
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);
-
// First check if we are on a hexadecimal number, after the "0x".
col = curwin->w_cursor.col;
-
- if (dobin) {
- while (col > 0 && ascii_isbdigit(ptr[col])) {
- col--;
+ if (VIsual_active) {
+ if (lt(curwin->w_cursor, VIsual)) {
+ pos_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;
- if (dohex) {
- while (col > 0 && ascii_isxdigit(ptr[col])) {
- col--;
+ col = VIsual.col;
+ lnum = VIsual.lnum;
+ lnume = curwin->w_cursor.lnum;
+ if (ptr[col] == '-') {
+ negative = true;
+ 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
+ } else {
+ ptr = get_cursor_line_ptr();
+ RLADDSUBFIX(ptr);
- col = curwin->w_cursor.col;
+ if (dobin) {
+ while (col > 0 && ascii_isbdigit(ptr[col])) {
+ col--;
+ }
+ }
- while (col > 0 && ascii_isdigit(ptr[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
- && 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;
+ col = curwin->w_cursor.col;
- while (ptr[col] != NUL
- && !ascii_isdigit(ptr[col])
- && !(doalp && ASCII_ISALPHA(ptr[col]))) {
- col++;
+ while (col > 0 && ascii_isdigit(ptr[col])) {
+ 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]))) {
+ col++;
+ }
+
+ while (col > 0
+ && ascii_isdigit(ptr[col - 1])
+ && !(doalp && ASCII_ISALPHA(ptr[col]))) {
+ col--;
+ }
}
}
- // 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) {
- beep_flush();
- return FAIL;
- }
+ for (int i = lnum; i <= lnume; i++) {
+ curwin->w_cursor.lnum = i;
+ ptr = get_cursor_line_ptr();
+ 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 ((!ascii_isdigit(firstdigit) && !(doalp && ASCII_ISALPHA(firstdigit)))
+ || u_save_cursor() != OK) {
+ if (lnum < lnume) {
+ // Try again on next line
+ continue;
+ }
+ beep_flush();
+ return FAIL;
+ }
- // get ptr again, because u_save() may have changed it
- ptr = get_cursor_line_ptr();
- RLADDSUBFIX(ptr);
+ ptr = get_cursor_line_ptr();
+ RLADDSUBFIX(ptr);
- if (doalp && ASCII_ISALPHA(firstdigit)) {
- // decrement or increment alphabetic character
- if (command == Ctrl_X) {
- if (CharOrd(firstdigit) < Prenum1) {
- if (isupper(firstdigit)) {
- firstdigit = 'A';
+ if (doalp && ASCII_ISALPHA(firstdigit)) {
+ // decrement or increment alphabetic character
+ if (command == Ctrl_X) {
+ if (CharOrd(firstdigit) < Prenum1) {
+ if (isupper(firstdigit)) {
+ firstdigit = 'A';
+ } else {
+ firstdigit = 'a';
+ }
} else {
- firstdigit = 'a';
+ firstdigit -= Prenum1;
}
} else {
- firstdigit -= Prenum1;
- }
- } else {
- if (26 - CharOrd(firstdigit) - 1 < Prenum1) {
- if (isupper(firstdigit)) {
- firstdigit = 'Z';
+ if (26 - CharOrd(firstdigit) - 1 < Prenum1) {
+ if (isupper(firstdigit)) {
+ firstdigit = 'Z';
+ } else {
+ firstdigit = 'z';
+ }
} else {
- firstdigit = 'z';
+ firstdigit += Prenum1;
}
- } else {
- firstdigit += Prenum1;
}
- }
- 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;
- }
+ curwin->w_cursor.col = col;
+ (void)del_char(false);
+ ins_char(firstdigit);
+ } else {
+ 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);
+ // get the number value (unsigned)
+ vim_str2nr(ptr + col, &pre, &length, dobin, dooct, dohex, NULL, &n);
- // ignore leading '-' for hex, octal and bin numbers
- if (pre && negative) {
- ++col;
- --length;
- negative = false;
- }
+ // ignore leading '-' for hex, octal and bin numbers
+ if (pre && negative) {
+ col++;
+ length--;
+ negative = false;
+ }
- // add or subtract
- subtract = false;
- if (command == Ctrl_X) {
- subtract ^= true;
- }
- if (negative) {
- subtract ^= true;
- }
+ // add or subtract
+ subtract = false;
+ if (command == Ctrl_X) {
+ subtract ^= true;
+ }
+ if (negative) {
+ subtract ^= true;
+ }
- oldn = n;
+ oldn = n;
- n = subtract ? n - (unsigned long) Prenum1
- : n + (unsigned long) Prenum1;
+ n = subtract ? n - (unsigned long) Prenum1
+ : n + (unsigned long) Prenum1;
- // handle wraparound for decimal numbers
- if (!pre) {
- if (subtract) {
- if (n > oldn) {
- n = 1 + (n ^ (unsigned long)-1);
- negative ^= true;
+ // handle wraparound for decimal numbers
+ if (!pre) {
+ 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;
+ }
}
- } else { /* add */
- if (n < oldn) {
- n = (n ^ (unsigned long)-1);
- negative ^= true;
+ if (n == 0) {
+ negative = false;
}
}
- if (n == 0) {
- negative = false;
- }
- }
- // Delete the old number.
- curwin->w_cursor.col = col;
- todel = length;
- c = gchar_cursor();
+ // 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;
+ // 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();
}
- // 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 = xmalloc(length + NUMBUFLEN);
- ptr = buf1;
- if (negative) {
- *ptr++ = '-';
- }
- if (pre) {
- *ptr++ = '0';
- --length;
- }
- if (pre == 'b' || pre == 'B' ||
- pre == 'x' || pre == 'X') {
- *ptr++ = pre;
- --length;
- }
+ // Prepare the leading characters in buf1[].
+ // When there are many leading zeros it could be very long.
+ // Allocate a bit too much.
+ buf1 = xmalloc(length + NUMBUFLEN);
+ ptr = buf1;
+ // do not add leading '-' for visual mode'
+ if (negative && !visual) {
+ *ptr++ = '-';
+ }
+ if (pre) {
+ *ptr++ = '0';
+ length--;
+ }
+ if (pre == 'b' || pre == 'B' ||
+ pre == 'x' || pre == 'X') {
+ *ptr++ = pre;
+ length--;
+ }
- // Put the number characters in buf2[].
- if (pre == 'b' || pre == 'B') {
- size_t bits = 0;
- size_t pos = 0;
+ // Put the number characters in buf2[].
+ if (pre == 'b' || pre == 'B') {
+ size_t bits = 0;
+ size_t pos = 0;
- // leading zeros
- for (bits = 8 * sizeof(unsigned long); bits > 0; bits--) {
- if ((n >> (bits - 1)) & 0x1) { break; }
- }
+ // leading zeros
+ for (bits = 8 * sizeof(unsigned long); bits > 0; bits--) {
+ if ((n >> (bits - 1)) & 0x1) { break; }
+ }
+
+ while (bits > 0) {
+ buf2[pos++] = ((n >> --bits) & 0x1) ? '1' : '0';
+ }
- while (bits > 0) {
- buf2[pos++] = ((n >> --bits) & 0x1) ? '1' : '0';
+ buf2[pos] = '\0';
+
+ } else if (pre == 0) {
+ snprintf((char *)buf2, NUMBUFLEN, "%" PRIu64, (uint64_t)n + offset);
+ } else if (pre == '0') {
+ snprintf((char *)buf2, NUMBUFLEN, "%" PRIo64, (uint64_t)n + offset);
+ } else if (pre && hexupper) {
+ snprintf((char *)buf2, NUMBUFLEN, "%" PRIX64, (uint64_t)n + offset);
+ } else {
+ snprintf((char *)buf2, NUMBUFLEN, "%" PRIx64, (uint64_t)n + offset);
}
+ length -= (int)STRLEN(buf2);
- buf2[pos] = '\0';
+ if (g_cmd) {
+ offset = subtract ? offset - (unsigned long) Prenum1
+ : offset + (unsigned long) Prenum1;
+ }
- } else if (pre == 0) {
- snprintf((char *)buf2, NUMBUFLEN, "%" PRIu64, (uint64_t)n);
- } else if (pre == '0') {
- snprintf((char *)buf2, NUMBUFLEN, "%" PRIo64, (uint64_t)n);
- } else if (pre && hexupper) {
- snprintf((char *)buf2, NUMBUFLEN, "%" PRIX64, (uint64_t)n);
- } else {
- snprintf((char *)buf2, NUMBUFLEN, "%" PRIx64, (uint64_t)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 && pre == 0)) {
- while (length-- > 0) {
- *ptr++ = '0';
+ // 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 && pre == 0)) {
+ while (length-- > 0) {
+ *ptr++ = '0';
+ }
}
+ *ptr = NUL;
+ STRCAT(buf1, buf2);
+ ins_str(buf1); // insert the new number
+ xfree(buf1);
}
- *ptr = NUL;
- STRCAT(buf1, buf2);
- ins_str(buf1); /* insert the new number */
- xfree(buf1);
- }
- --curwin->w_cursor.col;
- curwin->w_set_curswant = true;
- ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, true);
- RLADDSUBFIX(ptr);
+ curwin->w_cursor.col--;
+ curwin->w_set_curswant = true;
+ ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, true);
+ RLADDSUBFIX(ptr);
+ }
return OK;
}
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index 41ce2daa91..c2fdca8514 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_increment.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_increment.in b/src/nvim/testdir/test_increment.in
new file mode 100644
index 0000000000..5b0ad06cf8
--- /dev/null
+++ b/src/nvim/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/nvim/testdir/test_increment.ok b/src/nvim/testdir/test_increment.ok
new file mode 100644
index 0000000000..4a61ad1d1d
--- /dev/null
+++ b/src/nvim/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/nvim/version.c b/src/nvim/version.c
index 30f104562f..24fa8defe8 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -380,7 +380,7 @@ static int included_patches[] = {
// 757 NA
// 756 NA
// 755,
- // 754,
+ 754,
753,
// 752,
// 751 NA