aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/ops.c
diff options
context:
space:
mode:
authorwatiko <service@mail.watiko.net>2016-01-15 21:04:06 +0900
committerwatiko <service@mail.watiko.net>2016-02-01 03:47:09 +0900
commit8f212568aa5083a77b3617f2cef5b438f3d90e42 (patch)
tree3b0181cf8069fb4e94e1d5f8156cd8e1494d2a5d /src/nvim/ops.c
parent3a94e06abbcc0dfff658b626891ec308f7582180 (diff)
downloadrneovim-8f212568aa5083a77b3617f2cef5b438f3d90e42.tar.gz
rneovim-8f212568aa5083a77b3617f2cef5b438f3d90e42.tar.bz2
rneovim-8f212568aa5083a77b3617f2cef5b438f3d90e42.zip
vim-patch:7.4.1087
Problem: CTRL-A and CTRL-X do not work properly with blockwise visual selection if there is a mix of Tab and spaces. Solution: Add OP_NR_ADD and OP_NR_SUB. (Hirohito Higashi) https://github.com/vim/vim/commit/d79e55016cf8268cee935f1ac3b5b28712d1399e
Diffstat (limited to 'src/nvim/ops.c')
-rw-r--r--src/nvim/ops.c608
1 files changed, 320 insertions, 288 deletions
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 8887b3ae91..b08a5056e4 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -115,6 +115,8 @@ static char opchars[][3] =
{'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,14 +4197,115 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, int i
bdp->textstart = pstart;
}
+/// 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)
+{
+ pos_T pos;
+ struct block_def bd;
+ ssize_t change_cnt = 0;
+ linenr_T amount = Prenum1;
+
+ 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;
+
+ 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 if (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);
+ }
+ }
+ }
+}
+
/// 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 g_cmd Prefixed with `g`.
+/// @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, bool g_cmd)
+/// @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;
@@ -4196,11 +4313,9 @@ int do_addsub(int command, linenr_T Prenum1, bool g_cmd)
int pre; // 'X' or 'x': hex; '0': octal; 'B' or 'b': bin
static bool hexupper = false; // 0xABC
unsigned long n;
- unsigned long offset = 0;
unsigned long oldn;
char_u *ptr;
int c;
- int length = 0; // character length of the number
int todel;
bool dohex;
bool dooct;
@@ -4211,9 +4326,6 @@ int do_addsub(int command, linenr_T Prenum1, bool g_cmd)
bool negative = false;
bool was_positive = true;
bool visual = VIsual_active;
- int lnum = curwin->w_cursor.lnum;
- int lnume = curwin->w_cursor.lnum;
- int startcol = 0;
bool did_change = false;
pos_T t = curwin->w_cursor;
int maxlen = 0;
@@ -4225,44 +4337,16 @@ int do_addsub(int command, linenr_T Prenum1, bool g_cmd)
dobin = (vim_strchr(curbuf->b_p_nf, 'b') != NULL); // "Bin"
doalp = (vim_strchr(curbuf->b_p_nf, 'p') != NULL); // "alPha"
- // First check if we are on a hexadecimal number, after the "0x".
- col = curwin->w_cursor.col;
- if (VIsual_active) {
- if (lt(curwin->w_cursor, VIsual)) {
- curwin->w_cursor = VIsual;
- VIsual = t;
- }
-
- ptr = ml_get(VIsual.lnum);
- if (VIsual_mode == 'V') {
- VIsual.col = 0;
- curwin->w_cursor.col = STRLEN(ptr);
- } else if (VIsual_mode == Ctrl_V
- && VIsual.col > curwin->w_cursor.col) {
- t = VIsual;
- VIsual.col = curwin->w_cursor.col;
- curwin->w_cursor.col = t.col;
- }
-
- // 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;
- curbuf->b_visual.vi_curswant = curwin->w_curswant;
-
- if (VIsual_mode != 'v') {
- startcol = VIsual.col < curwin->w_cursor.col
- ? VIsual.col : curwin->w_cursor.col;
- } else {
- startcol = VIsual.col;
- }
- col = startcol;
+ curwin->w_cursor = *pos;
+ ptr = ml_get(pos->lnum);
+ col = pos->col;
- lnum = VIsual.lnum;
- lnume = curwin->w_cursor.lnum;
- } else {
- ptr = get_cursor_line_ptr();
+ if (*ptr == NUL) {
+ goto theend;
+ }
+ // First check if we are on a hexadecimal number, after the "0x".
+ if (!VIsual_active) {
if (dobin) {
while (col > 0 && ascii_isbdigit(ptr[col])) {
col--;
@@ -4306,7 +4390,7 @@ int do_addsub(int command, linenr_T Prenum1, bool g_cmd)
col--;
} else {
// Search forward and then backward to find the start of number.
- col = curwin->w_cursor.col;
+ col = pos->col;
while (ptr[col] != NUL
&& !ascii_isdigit(ptr[col])
@@ -4322,279 +4406,227 @@ int do_addsub(int command, linenr_T Prenum1, bool g_cmd)
}
}
- for (int i = lnum; i <= lnume; i++) {
- colnr_T stop = 0;
-
- t = curwin->w_cursor;
- curwin->w_cursor.lnum = i;
- ptr = get_cursor_line_ptr();
- if ((int)STRLEN(ptr) <= col) {
- // try again on next line
- continue;
+ if (visual) {
+ while (ptr[col] != NUL && length > 0 && !ascii_isdigit(ptr[col]) &&
+ !(doalp && ASCII_ISALPHA(ptr[col]))) {
+ col++;
+ length--;
}
- if (visual) {
- if (VIsual_mode == 'v' && i == lnume) {
- stop = curwin->w_cursor.col;
- } else if (VIsual_mode == Ctrl_V &&
- curbuf->b_visual.vi_curswant != MAXCOL) {
- stop = curwin->w_cursor.col;
- }
-
- while (ptr[col] != NUL
- && !ascii_isdigit(ptr[col])
- && !(doalp && ASCII_ISALPHA(ptr[col]))) {
- if (col > 0 && col == stop) {
- break;
- }
- col++;
- }
- if (col > startcol && ptr[col - 1] == '-') {
- negative = true;
- was_positive = false;
- }
+ if (length == 0) {
+ goto theend;
}
- // If a number was found, and saving for undo works, replace the number.
- firstdigit = ptr[col];
- if ((!ascii_isdigit(firstdigit) && !(doalp && ASCII_ISALPHA(firstdigit)))
- || u_save_cursor() != OK) {
- if (lnum < lnume) {
- if (visual && VIsual_mode != Ctrl_V) {
- col = 0;
- } else {
- col = startcol;
- }
- // Try again on next line
- continue;
- }
- beep_flush();
- return FAIL;
+
+ if (col > pos->col && ptr[col - 1] == '-') {
+ negative = true;
+ was_positive = false;
}
+ }
- 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';
- }
+ // If a number was found, and saving for undo works, replace the number.
+ firstdigit = ptr[col];
+ if (!ascii_isdigit(firstdigit) && !(doalp && ASCII_ISALPHA(firstdigit))) {
+ beep_flush();
+ goto theend;
+ }
+
+ if (doalp && ASCII_ISALPHA(firstdigit)) {
+ // decrement or increment alphabetic character
+ if (op_type == OP_NR_SUB) {
+ if (CharOrd(firstdigit) < Prenum1) {
+ if (isupper(firstdigit)) {
+ firstdigit = 'A';
} else {
- firstdigit -= Prenum1;
+ firstdigit = 'a';
}
} else {
- if (26 - CharOrd(firstdigit) - 1 < Prenum1) {
- if (isupper(firstdigit)) {
- firstdigit = 'Z';
- } else {
- firstdigit = 'z';
- }
- } else {
- firstdigit += Prenum1;
- }
+ firstdigit -= 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 {
- if (col > 0 && ptr[col - 1] == '-' && !visual) {
- // negative number
- col--;
- negative = true;
- }
-
- // get the number value (unsigned)
- if (visual && VIsual_mode != 'V') {
- if (VIsual_mode == 'v') {
- if (i == lnum) {
- maxlen = (lnum == lnume
- ? curwin->w_cursor.col - col + 1
- : (int)STRLEN(ptr) - col);
- } else {
- maxlen = (i == lnume
- ? curwin->w_cursor.col - col + 1
- : (int)STRLEN(ptr) - col);
- }
- } else if (VIsual_mode == Ctrl_V) {
- maxlen = (curbuf->b_visual.vi_curswant == MAXCOL
- ? (int)STRLEN(ptr) - col
- : curwin->w_cursor.col - col + 1);
+ if (26 - CharOrd(firstdigit) - 1 < Prenum1) {
+ if (isupper(firstdigit)) {
+ firstdigit = 'Z';
+ } else {
+ firstdigit = 'z';
}
+ } else {
+ firstdigit += 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 {
+ if (col > 0 && ptr[col - 1] == '-' && !visual) {
+ // negative number
+ col--;
+ negative = true;
+ }
- vim_str2nr(ptr + col, &pre, &length,
- 0 + (dobin ? STR2NR_BIN : 0)
- + (dooct ? STR2NR_OCT : 0)
- + (dohex ? STR2NR_HEX : 0),
- NULL, &n, maxlen);
+ // get the number value (unsigned)
+ if (visual && VIsual_mode != 'V') {
+ maxlen = (curbuf->b_visual.vi_curswant == MAXCOL
+ ? (int)STRLEN(ptr) - col
+ : length);
+ }
- // ignore leading '-' for hex, octal and bin numbers
- if (pre && negative) {
- col++;
- length--;
- negative = false;
- }
+ 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--;
+ negative = false;
+ }
- // add or subtract
- subtract = false;
- if (command == Ctrl_X) {
- subtract ^= true;
- }
- if (negative) {
- subtract ^= true;
- }
+ // add or subtract
+ subtract = false;
+ if (op_type == OP_NR_SUB) {
+ 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;
- }
- } else {
- // add
- if (n < oldn) {
- n = (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;
}
- if (n == 0) {
- negative = false;
+ } else {
+ // add
+ if (n < oldn) {
+ n = (n ^ (unsigned long)-1);
+ negative ^= true;
}
}
-
- if (visual && !was_positive && !negative && col > 0) {
- // need to remove the '-'
- col--;
- length++;
+ if (n == 0) {
+ negative = false;
}
+ }
- // Delete the old number.
- curwin->w_cursor.col = col;
- if (!did_change) {
- startpos = curwin->w_cursor;
- }
- did_change = true;
- todel = length;
- c = gchar_cursor();
+ if (visual && !was_positive && !negative && col > 0) {
+ // need to remove the '-'
+ col--;
+ length++;
+ }
- // 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;
- }
+ // 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--;
+ }
+ 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 && (!visual || (visual && was_positive))) {
- *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);
+ if (buf1 == NULL) {
+ goto theend;
+ }
+ ptr = buf1;
+ if (negative && (!visual || (visual && was_positive))) {
+ *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 i = 0;
- // leading zeros
- for (bits = 8 * sizeof(n); bits > 0; bits--) {
- if ((n >> (bits - 1)) & 0x1) {
- break;
- }
- }
+ // leading zeros
+ for (bits = 8 * sizeof(n); bits > 0; bits--) {
+ if ((n >> (bits - 1)) & 0x1) {
+ break;
+ }
+ }
- while (bits > 0) {
- buf2[pos++] = ((n >> --bits) & 0x1) ? '1' : '0';
- }
+ while (bits > 0) {
+ buf2[i++] = ((n >> --bits) & 0x1) ? '1' : '0';
+ }
- buf2[pos] = '\0';
+ buf2[i] = '\0';
- } else if (pre == 0) {
- vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIu64, (uint64_t)n);
- } else if (pre == '0') {
- vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIo64, (uint64_t)n);
- } else if (pre && hexupper) {
- vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIX64, (uint64_t)n);
- } else {
- vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" 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 = NUL;
- STRCAT(buf1, buf2);
- ins_str(buf1); // insert the new number
- xfree(buf1);
- endpos = curwin->w_cursor;
- if (lnum < lnume) {
- curwin->w_cursor.col = t.col;
- } else if (did_change && curwin->w_cursor.col) {
- curwin->w_cursor.col--;
- }
+ } else if (pre == 0) {
+ vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIu64, (uint64_t)n);
+ } else if (pre == '0') {
+ vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIo64, (uint64_t)n);
+ } else if (pre && hexupper) {
+ vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIX64, (uint64_t)n);
+ } else {
+ vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIx64, (uint64_t)n);
}
-
- if (g_cmd) {
- offset = (unsigned long)Prenum1;
- g_cmd = 0;
+ 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';
+ }
}
- // reset
- subtract = false;
- negative = false;
- was_positive = true;
- if (visual && VIsual_mode == Ctrl_V) {
- col = startcol;
- } else {
- col = 0;
+ *ptr = NUL;
+ STRCAT(buf1, buf2);
+ ins_str(buf1); // insert the new number
+ xfree(buf1);
+ endpos = curwin->w_cursor;
+ if (did_change && curwin->w_cursor.col) {
+ curwin->w_cursor.col--;
}
- Prenum1 += offset;
- curwin->w_set_curswant = true;
}
+
+theend:
if (visual) {
- // cursor at the top of the selection
- curwin->w_cursor = VIsual;
+ curwin->w_cursor = t;
}
if (did_change) {
// set the '[ and '] marks
@@ -4604,7 +4636,7 @@ int do_addsub(int command, linenr_T Prenum1, bool g_cmd)
curbuf->b_op_end.col--;
}
}
- return OK;
+ return did_change;
}
/*