Merge pull request #4013 from watiko/vim-increment

Vim patches related to increment and marks
This commit is contained in:
Justin M. Keyes
2016-02-01 01:47:37 -05:00
21 changed files with 1481 additions and 322 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 {
/*

View File

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

View File

@@ -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 (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
col = curwin->w_cursor.col;
while (col > 0 && ascii_isdigit(ptr[col])) {
if (!VIsual_active) {
if (dobin) {
while (col > 0 && ascii_isbdigit(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 (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;
while (col > 0 && ascii_isdigit(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 = pos->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 (visual) {
while (ptr[col] != NUL && length > 0 && !ascii_isdigit(ptr[col]) &&
!(doalp && ASCII_ISALPHA(ptr[col]))) {
col++;
length--;
}
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;
}
/*

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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];
curhead->uh_namedm[i] = 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;

View File

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

View File

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