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)

3a304b2382
This commit is contained in:
watiko 2016-01-14 17:06:55 +09:00
parent 8f22031708
commit 4d074e39ea
6 changed files with 497 additions and 219 deletions

View File

@ -3503,9 +3503,17 @@ static void nv_help(cmdarg_T *cap)
*/ */
static void nv_addsub(cmdarg_T *cap) static void nv_addsub(cmdarg_T *cap)
{ {
if (!checkclearopq(cap->oap) bool visual = VIsual_active;
&& do_addsub(cap->cmdchar, cap->count1)) if (cap->oap->op_type == OP_NOP
&& do_addsub((int)cap->cmdchar, cap->count1, cap->arg) == OK) {
prep_redo_cmd(cap); 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; bool flag = false;
switch (cap->nchar) { switch (cap->nchar) {
/* // "g^A/g^X": Sequentially increment visually selected region.
* "gR": Enter virtual replace mode. 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': case 'R':
cap->arg = true; cap->arg = true;
nv_Replace(cap); nv_Replace(cap);

View File

@ -4201,272 +4201,322 @@ static void reverse_line(char_u *s)
/// Add or subtract from a number in a line. /// Add or subtract from a number in a line.
/// ///
/// @param command CTRL-A for add, CTRL-X for subtract /// @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 /// @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; int col;
char_u *buf1; char_u *buf1;
char_u buf2[NUMBUFLEN]; char_u buf2[NUMBUFLEN];
int pre; // 'X' or 'x': hex; '0': octal; 'B' or 'b': bin int pre; // 'X' or 'x': hex; '0': octal; 'B' or 'b': bin
static int hexupper = false; // 0xABC static bool hexupper = false; // 0xABC
unsigned long n, oldn; unsigned long n;
long offset = 0;
unsigned long oldn;
char_u *ptr; char_u *ptr;
int c; int c;
int length = 0; // character length of the number int length = 0; // character length of the number
int todel; int todel;
int dohex; int dohex;
int dooct; int dooct;
int dobin; int dobin;
int doalp; int doalp;
int firstdigit; int firstdigit;
int negative; bool subtract;
int 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" dohex = (vim_strchr(curbuf->b_p_nf, 'x') != NULL); // "heX"
dooct = (vim_strchr(curbuf->b_p_nf, 'o') != NULL); // "Octal" dooct = (vim_strchr(curbuf->b_p_nf, 'o') != NULL); // "Octal"
dobin = (vim_strchr(curbuf->b_p_nf, 'b') != NULL); // "Bin" dobin = (vim_strchr(curbuf->b_p_nf, 'b') != NULL); // "Bin"
doalp = (vim_strchr(curbuf->b_p_nf, 'p') != NULL); // "alPha" 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". // First check if we are on a hexadecimal number, after the "0x".
col = curwin->w_cursor.col; col = curwin->w_cursor.col;
if (VIsual_active) {
if (dobin) { if (lt(curwin->w_cursor, VIsual)) {
while (col > 0 && ascii_isbdigit(ptr[col])) { pos_T t = curwin->w_cursor;
col--; curwin->w_cursor = VIsual;
VIsual = t;
} }
} if (VIsual_mode == 'V') {
VIsual.col = 0;
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
col = curwin->w_cursor.col; ptr = ml_get(VIsual.lnum);
RLADDSUBFIX(ptr);
while (col > 0 && ascii_isdigit(ptr[col])) { // store visual area for 'gv'
col--; curbuf->b_visual.vi_start = VIsual;
} curbuf->b_visual.vi_end = curwin->w_cursor;
} curbuf->b_visual.vi_mode = VIsual_mode;
if ((dohex col = VIsual.col;
&& col > 0 lnum = VIsual.lnum;
&& (ptr[col] == 'X' lnume = curwin->w_cursor.lnum;
|| ptr[col] == 'x') if (ptr[col] == '-') {
&& ptr[col - 1] == '0' negative = true;
&& 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++; 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;
}
// 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 (CharOrd(firstdigit) < Prenum1) {
if (isupper(firstdigit)) {
firstdigit = 'A';
} else {
firstdigit = 'a';
}
} else {
firstdigit -= Prenum1;
}
} else {
if (26 - CharOrd(firstdigit) - 1 < Prenum1) {
if (isupper(firstdigit)) {
firstdigit = 'Z';
} else {
firstdigit = 'z';
}
} else {
firstdigit += Prenum1;
}
}
curwin->w_cursor.col = col;
(void)del_char(false);
ins_char(firstdigit);
} else { } else {
negative = false; ptr = get_cursor_line_ptr();
if (col > 0 && ptr[col - 1] == '-') { // negative number RLADDSUBFIX(ptr);
--col;
negative = true; if (dobin) {
while (col > 0 && ascii_isbdigit(ptr[col])) {
col--;
}
} }
// get the number value (unsigned) if (dohex) {
vim_str2nr(ptr + col, &pre, &length, dobin, dooct, dohex, NULL, &n); while (col > 0 && ascii_isxdigit(ptr[col])) {
col--;
// ignore leading '-' for hex, octal and bin numbers }
if (pre && negative) {
++col;
--length;
negative = false;
} }
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
// add or subtract col = curwin->w_cursor.col;
subtract = false;
if (command == Ctrl_X) {
subtract ^= true;
}
if (negative) {
subtract ^= true;
}
oldn = n; while (col > 0 && ascii_isdigit(ptr[col])) {
col--;
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;
} }
} else { /* add */ }
if (n < oldn) {
n = (n ^ (unsigned long)-1); if ((dohex
negative ^= true; && 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--;
}
}
}
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;
}
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';
} else {
firstdigit = 'a';
}
} else {
firstdigit -= Prenum1;
}
} else {
if (26 - CharOrd(firstdigit) - 1 < Prenum1) {
if (isupper(firstdigit)) {
firstdigit = 'Z';
} else {
firstdigit = 'z';
}
} else {
firstdigit += Prenum1;
} }
} }
if (n == 0) { 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);
// ignore leading '-' for hex, octal and bin numbers
if (pre && negative) {
col++;
length--;
negative = false; negative = false;
} }
}
// Delete the old number. // add or subtract
curwin->w_cursor.col = col; subtract = false;
todel = length; if (command == Ctrl_X) {
c = gchar_cursor(); subtract ^= true;
}
if (negative) {
subtract ^= true;
}
// Don't include the '-' in the length, only the length of the part oldn = n;
// after it is kept the same.
if (c == '-') { n = subtract ? n - (unsigned long) Prenum1
--length; : n + (unsigned long) Prenum1;
}
while (todel-- > 0) { // handle wraparound for decimal numbers
if (c < 0x100 && isalpha(c)) { if (!pre) {
if (isupper(c)) { if (subtract) {
hexupper = true; if (n > oldn) {
n = 1 + (n ^ (unsigned long)-1);
negative ^= true;
}
} else { } else {
hexupper = false; // add
if (n < oldn) {
n = (n ^ (unsigned long)-1);
negative ^= true;
}
}
if (n == 0) {
negative = false;
} }
} }
// del_char() will mark line needing displaying
(void)del_char(false); // Delete the old number.
curwin->w_cursor.col = col;
todel = length;
c = gchar_cursor(); c = gchar_cursor();
}
// Prepare the leading characters in buf1[]. // Don't include the '-' in the length, only the length of the part
// When there are many leading zeros it could be very long. Allocate // after it is kept the same.
// a bit too much. if (c == '-') {
buf1 = xmalloc(length + NUMBUFLEN); length--;
ptr = buf1; }
if (negative) { while (todel-- > 0) {
*ptr++ = '-'; if (c < 0x100 && isalpha(c)) {
} if (isupper(c)) {
if (pre) { hexupper = true;
*ptr++ = '0'; } else {
--length; hexupper = false;
} }
if (pre == 'b' || pre == 'B' || }
pre == 'x' || pre == 'X') { // del_char() will mark line needing displaying
*ptr++ = pre; (void)del_char(false);
--length; c = gchar_cursor();
}
// 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; }
} }
while (bits > 0) { // Prepare the leading characters in buf1[].
buf2[pos++] = ((n >> --bits) & 0x1) ? '1' : '0'; // 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) {
buf2[pos] = '\0';
} 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'; *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;
// 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';
}
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);
if (g_cmd) {
offset = subtract ? offset - (unsigned long) Prenum1
: 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 && pre == 0)) {
while (length-- > 0) {
*ptr++ = '0';
}
}
*ptr = NUL;
STRCAT(buf1, buf2);
ins_str(buf1); // insert the new number
xfree(buf1);
} }
*ptr = NUL; curwin->w_cursor.col--;
STRCAT(buf1, buf2); curwin->w_set_curswant = true;
ins_str(buf1); /* insert the new number */ ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, true);
xfree(buf1); 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; return OK;
} }

View File

@ -28,6 +28,7 @@ SCRIPTS := \
test_charsearch.out \ test_charsearch.out \
test_close_count.out \ test_close_count.out \
test_command_count.out \ test_command_count.out \
test_increment.out \
NEW_TESTS = NEW_TESTS =
@ -131,7 +132,7 @@ test1.out: .gdbinit test1.in
# Check if the test.out file matches test.ok. # Check if the test.out file matches test.ok.
@/bin/sh -c "if test -f test.out; then \ @/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; \ mv -f test.out $*.out; \
else \ else \
echo $* FAILED >> test.log; \ echo $* FAILED >> test.log; \

View File

@ -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

View File

@ -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

View File

@ -380,7 +380,7 @@ static int included_patches[] = {
// 757 NA // 757 NA
// 756 NA // 756 NA
// 755, // 755,
// 754, 754,
753, 753,
// 752, // 752,
// 751 NA // 751 NA