diff options
Diffstat (limited to 'src')
43 files changed, 1388 insertions, 682 deletions
diff --git a/src/nvim/README.md b/src/nvim/README.md index e4939d94fd..f16c6de12f 100644 --- a/src/nvim/README.md +++ b/src/nvim/README.md @@ -11,7 +11,7 @@ that are constantly changing. As the code becomes more organized and stable, this document will be updated to reflect the changes. If you are looking for module-specific details, it is best to read the source -code. Some files are extensively commented at the top(eg: terminal.c, +code. Some files are extensively commented at the top (e.g. terminal.c, screen.c). ### Top-level program loops @@ -43,13 +43,13 @@ a typical editing session: Note that we have split user actions into sequences of inputs that change the state of the editor. While there's no documentation about a "g command -mode"(step 16), internally it is implemented similarly to "operator-pending +mode" (step 16), internally it is implemented similarly to "operator-pending mode". From this we can see that Vim has the behavior of a input-driven state -machine(more specifically, a pushdown automaton since it requires a stack for +machine (more specifically, a pushdown automaton since it requires a stack for transitioning back from states). Assuming each state has a callback responsible -for handling keys, this pseudocode(a python-like language) shows a good +for handling keys, this pseudocode (a python-like language) shows a good representation of the main program loop: ```py @@ -129,20 +129,20 @@ def insert_state(data, key): While the actual code is much more complicated, the above gives an idea of how Neovim is organized internally. Some states like the `g_command_state` or `get_operator_count_state` do not have a dedicated `state_enter` callback, but -are implicitly embedded into other states(this will change later as we continue +are implicitly embedded into other states (this will change later as we continue the refactoring effort). To start reading the actual code, here's the recommended order: -1. `state_enter()` function(state.c). This is the actual program loop, +1. `state_enter()` function (state.c). This is the actual program loop, note that a `VimState` structure is used, which contains function pointers for the callback and state data. -2. `main()` function(main.c). After all startup, `normal_enter` is called +2. `main()` function (main.c). After all startup, `normal_enter` is called at the end of function to enter normal mode. -3. `normal_enter()` function(normal.c) is a small wrapper for setting +3. `normal_enter()` function (normal.c) is a small wrapper for setting up the NormalState structure and calling `state_enter`. -4. `normal_check()` function(normal.c) is called before each iteration of +4. `normal_check()` function (normal.c) is called before each iteration of normal mode. -5. `normal_execute()` function(normal.c) is called when a key is read in normal +5. `normal_execute()` function (normal.c) is called when a key is read in normal mode. The basic structure described for normal mode in 3, 4 and 5 is used for other @@ -159,7 +159,7 @@ asynchronous events, which can include: - msgpack-rpc requests - job control callbacks -- timers(not implemented yet but the support code is already there) +- timers (not implemented yet but the support code is already there) Neovim implements this functionality by entering another event loop while waiting for characters, so instead of: @@ -180,11 +180,11 @@ def state_enter(state_callback, data): while state_callback(data, event) # invoke the callback for the current state ``` -where `event` is something the operating system delivers to us, including(but +where `event` is something the operating system delivers to us, including (but not limited to) user input. The `read_next_event()` part is internally implemented by libuv, the platform layer used by Neovim. Since Neovim inherited its code from Vim, the states are not prepared to receive -"arbitrary events", so we use a special key to represent those(When a state +"arbitrary events", so we use a special key to represent those (When a state receives an "arbitrary event", it normally doesn't do anything other update the screen). diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index a6e3fedd3f..34e24712cd 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -22,6 +22,7 @@ #include "nvim/api/private/handle.h" #include "nvim/ascii.h" +#include "nvim/assert.h" #include "nvim/vim.h" #include "nvim/buffer.h" #include "nvim/charset.h" @@ -2826,7 +2827,7 @@ typedef enum { /// @param fillchar Character to use when filling empty space in the statusline /// @param maxwidth The maximum width to make the statusline /// @param hltab HL attributes (can be NULL) -/// @param tabtab tab page nrs (can be NULL) +/// @param tabtab Tab clicks definition (can be NULL). /// /// @return The final width of the statusline int build_stl_str_hl( @@ -2838,13 +2839,15 @@ int build_stl_str_hl( int fillchar, int maxwidth, struct stl_hlrec *hltab, - struct stl_hlrec *tabtab + StlClickRecord *tabtab ) { int groupitem[STL_MAX_ITEM]; struct stl_item { // Where the item starts in the status line output buffer - char_u *start; + char_u *start; + // Function to run for ClickFunc items. + char *cmd; // The minimum width of the item int minwid; // The maximum width of the item @@ -2856,10 +2859,10 @@ int build_stl_str_hl( Middle, Highlight, TabPage, + ClickFunc, Trunc - } type; - } item[STL_MAX_ITEM]; - + } type; + } item[STL_MAX_ITEM]; #define TMPLEN 70 char_u tmp[TMPLEN]; char_u *usefmt = fmt; @@ -3164,6 +3167,24 @@ int build_stl_str_hl( continue; } + if (*fmt_p == STL_CLICK_FUNC) { + fmt_p++; + char *t = (char *) fmt_p; + while (*fmt_p != STL_CLICK_FUNC && *fmt_p) { + fmt_p++; + } + if (*fmt_p != STL_CLICK_FUNC) { + break; + } + item[curitem].type = ClickFunc; + item[curitem].start = out_p; + item[curitem].cmd = xmemdupz(t, (size_t) (((char *) fmt_p - t))); + item[curitem].minwid = minwid; + fmt_p++; + curitem++; + continue; + } + // Denotes the end of the minwid // the maxwid may follow immediately after if (*fmt_p == '.') { @@ -3281,6 +3302,7 @@ int build_stl_str_hl( } break; } + case STL_LINE: num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ? 0L : (long)(wp->w_cursor.lnum); @@ -3821,16 +3843,37 @@ int build_stl_str_hl( // Store the info about tab pages labels. if (tabtab != NULL) { - struct stl_hlrec *sp = tabtab; + StlClickRecord *cur_tab_rec = tabtab; for (long l = 0; l < itemcnt; l++) { if (item[l].type == TabPage) { - sp->start = item[l].start; - sp->userhl = item[l].minwid; - sp++; + cur_tab_rec->start = (char *) item[l].start; + if (item[l].minwid == 0) { + cur_tab_rec->def.type = kStlClickDisabled; + cur_tab_rec->def.tabnr = 0; + } else { + int tabnr = item[l].minwid; + if (item[l].minwid > 0) { + cur_tab_rec->def.type = kStlClickTabSwitch; + } else { + cur_tab_rec->def.type = kStlClickTabClose; + tabnr = -tabnr; + } + cur_tab_rec->def.tabnr = tabnr; + } + cur_tab_rec->def.func = NULL; + cur_tab_rec++; + } else if (item[l].type == ClickFunc) { + cur_tab_rec->start = (char *) item[l].start; + cur_tab_rec->def.type = kStlClickFuncRun; + cur_tab_rec->def.tabnr = item[l].minwid; + cur_tab_rec->def.func = item[l].cmd; + cur_tab_rec++; } } - sp->start = NULL; - sp->userhl = 0; + cur_tab_rec->start = NULL; + cur_tab_rec->def.type = kStlClickDisabled; + cur_tab_rec->def.tabnr = 0; + cur_tab_rec->def.func = NULL; } return width; diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h index 49025d3925..d51a2f7dae 100644 --- a/src/nvim/buffer.h +++ b/src/nvim/buffer.h @@ -4,6 +4,7 @@ #include "nvim/window.h" #include "nvim/pos.h" // for linenr_T #include "nvim/ex_cmds_defs.h" // for exarg_T +#include "nvim/screen.h" // for StlClickRecord // Values for buflist_getfile() enum getf_values { diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 9c63eca1f2..9a0e1440cc 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -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; + } } } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index dbbcf4f1b9..213df4f65a 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -2548,8 +2548,12 @@ void ins_compl_show_pum(void) } } - /* Compute the screen column of the start of the completed text. - * Use the cursor to get all wrapping and other settings right. */ + // In Replace mode when a $ is displayed at the end of the line only + // part of the screen would be updated. We do need to redraw here. + dollar_vcol = -1; + + // Compute the screen column of the start of the completed text. + // Use the cursor to get all wrapping and other settings right. col = curwin->w_cursor.col; curwin->w_cursor.col = compl_col; pum_display(compl_match_array, compl_match_arraysize, cur); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a1c5f958d1..a9af7d94c1 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -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; @@ -10930,6 +10930,7 @@ static void f_has(typval_T *argvars, typval_T *rettv) #if !defined(UNIX) "system", // TODO(SplinterOfChaos): This IS defined for UNIX! #endif + "tablineat", "tag_binary", "tag_old_static", "termresponse", @@ -10987,8 +10988,6 @@ static void f_has(typval_T *argvars, typval_T *rettv) #endif } else if (STRICMP(name, "syntax_items") == 0) { n = syntax_present(curwin); - } else if (STRICMP(name, "gui_running") == 0) { - n = ui_rgb_attached(); } } @@ -15209,6 +15208,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv) list_T *l; listitem_T *li; dict_T *d; + list_T *s = NULL; rettv->vval.v_number = -1; if (argvars[0].v_type != VAR_LIST) { @@ -15227,7 +15227,8 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv) return; } if (!(dict_find(d, (char_u *)"group", -1) != NULL - && dict_find(d, (char_u *)"pattern", -1) != NULL + && (dict_find(d, (char_u *)"pattern", -1) != NULL + || dict_find(d, (char_u *)"pos1", -1) != NULL) && dict_find(d, (char_u *)"priority", -1) != NULL && dict_find(d, (char_u *)"id", -1) != NULL)) { EMSG(_(e_invarg)); @@ -15239,11 +15240,47 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv) clear_matches(curwin); li = l->lv_first; while (li != NULL) { + int i = 0; + char_u buf[5]; + dictitem_T *di; d = li->li_tv.vval.v_dict; - match_add(curwin, get_dict_string(d, (char_u *)"group", FALSE), - get_dict_string(d, (char_u *)"pattern", FALSE), - (int)get_dict_number(d, (char_u *)"priority"), - (int)get_dict_number(d, (char_u *)"id"), NULL); + + if (dict_find(d, (char_u *)"pattern", -1) == NULL) { + if (s == NULL) { + s = list_alloc(); + if (s == NULL) { + return; + } + } + + // match from matchaddpos() + for (i = 1; i < 9; ++i) { + snprintf((char *)buf, sizeof(buf), (char *)"pos%d", i); + if ((di = dict_find(d, (char_u *)buf, -1)) != NULL) { + if (di->di_tv.v_type != VAR_LIST) { + return; + } + + list_append_tv(s, &di->di_tv); + s->lv_refcount++; + } else { + break; + } + } + } + + if (i == 0) { + match_add(curwin, get_dict_string(d, (char_u *)"group", false), + get_dict_string(d, (char_u *)"pattern", false), + (int)get_dict_number(d, (char_u *)"priority"), + (int)get_dict_number(d, (char_u *)"id"), NULL); + } else { + match_add(curwin, get_dict_string(d, (char_u *)"group", false), + NULL, (int)get_dict_number(d, (char_u *)"priority"), + (int)get_dict_number(d, (char_u *)"id"), s); + list_unref(s); + s = NULL; + } li = li->li_next; } rettv->vval.v_number = 0; @@ -16037,6 +16074,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 +16088,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 +18383,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: diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 407dded6af..4d62dd0ff9 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -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 { @@ -685,9 +690,17 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) { char_u *str; linenr_T l; - linenr_T extra; /* Num lines added before line1 */ - linenr_T num_lines; /* Num lines moved */ - linenr_T last_line; /* Last line in file after adding new text */ + linenr_T extra; // Num lines added before line1 + linenr_T num_lines; // Num lines moved + linenr_T last_line; // Last line in file after adding new text + + // Moving lines seems to corrupt the folds, delete folding info now + // and recreate it when finished. Don't do this for manual folding, it + // would delete all folds. + bool isFolded = hasAnyFolding(curwin) && !foldmethodIsManual(curwin); + if (isFolded) { + deleteFoldRecurse(&curwin->w_folds); + } if (dest >= line1 && dest < line2) { EMSG(_("E134: Move lines into themselves")); @@ -772,8 +785,14 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) if (dest > last_line + 1) dest = last_line + 1; changed_lines(line1, 0, dest, 0L); - } else + } else { changed_lines(dest + 1, 0, line1 + num_lines, 0L); + } + + // recreate folds + if (isFolded) { + foldUpdateAll(curwin); + } return OK; } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index cbe7c1a231..28ff6fded4 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5656,8 +5656,13 @@ static void ex_quit(exarg_T *eap) || (only_one_window() && check_changed_any(eap->forceit))) { not_exiting(); } else { - if (only_one_window()) { - // quit last window + // quit last window + // Note: only_one_window() returns true, even so a help window is + // still open. In that case only quit, if no address has been + // specified. Example: + // :h|wincmd w|1q - don't quit + // :h|wincmd w|q - quit + if (only_one_window() && (firstwin == lastwin || eap->addr_count == 0)) { getout(0); } /* close window; may free buffer */ @@ -6345,7 +6350,7 @@ static void ex_tabnext(exarg_T *eap) */ static void ex_tabmove(exarg_T *eap) { - int tab_number = 9999; + int tab_number; if (eap->arg && *eap->arg != NUL) { char_u *p = eap->arg; @@ -6361,17 +6366,35 @@ static void ex_tabmove(exarg_T *eap) } else p = eap->arg; - if (p == skipdigits(p)) { - /* No numbers as argument. */ - eap->errmsg = e_invarg; - return; + if (relative == 0) { + if (STRCMP(p, "$") == 0) { + tab_number = LAST_TAB_NR; + } else if (p == skipdigits(p)) { + // No numbers as argument. + eap->errmsg = e_invarg; + return; + } else { + tab_number = getdigits(&p); + } + } else { + if (*p != NUL) { + tab_number = getdigits(&p); + } else { + tab_number = 1; + } + tab_number = tab_number * relative + tabpage_index(curtab); + if (relative == -1) { + --tab_number; + } } - - tab_number = getdigits_int(&p); - if (relative != 0) - tab_number = tab_number * relative + tabpage_index(curtab) - 1; ; - } else if (eap->addr_count != 0) + } else if (eap->addr_count != 0) { tab_number = eap->line2; + if (**eap->cmdlinep == '-') { + --tab_number; + } + } else { + tab_number = LAST_TAB_NR; + } tabpage_move(tab_number); } @@ -8345,8 +8368,7 @@ makeopens ( { int only_save_windows = TRUE; int nr; - int cnr = 1; - int restore_size = TRUE; + int restore_size = true; win_T *wp; char_u *sname; win_T *edited_win = NULL; @@ -8463,7 +8485,8 @@ makeopens ( tab_firstwin = firstwin; /* first window in tab page "tabnr" */ tab_topframe = topframe; for (tabnr = 1;; ++tabnr) { - int need_tabnew = FALSE; + int need_tabnew = false; + int cnr = 1; if ((ssop_flags & SSOP_TABPAGES)) { tabpage_T *tp = find_tabpage(tabnr); diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index b19331ad06..96bf2c78d2 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -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); diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index badb5b85b0..90987d0b3d 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -1543,12 +1543,12 @@ rewind_retry: if (fileformat == EOL_UNKNOWN) { /* First try finding a NL, for Dos and Unix */ if (try_dos || try_unix) { - for (p = ptr; p < ptr + size; ++p) { - // Reset the carriage return counter. - if (try_mac) { - try_mac = 1; - } + // Reset the carriage return counter. + if (try_mac) { + try_mac = 1; + } + for (p = ptr; p < ptr + size; ++p) { if (*p == NL) { if (!try_unix || (try_dos && p > ptr && p[-1] == CAR)) diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 2e32e78062..6c135ef47b 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -767,9 +767,9 @@ void foldUpdate(win_T *wp, linenr_T top, linenr_T bot) return; } - /* Mark all folds from top to bot as maybe-small. */ - (void)foldFind(&curwin->w_folds, top, &fp); - while (fp < (fold_T *)curwin->w_folds.ga_data + curwin->w_folds.ga_len + // Mark all folds from top to bot as maybe-small. + (void)foldFind(&wp->w_folds, top, &fp); + while (fp < (fold_T *)wp->w_folds.ga_data + wp->w_folds.ga_len && fp->fd_top < bot) { fp->fd_small = MAYBE; ++fp; diff --git a/src/nvim/globals.h b/src/nvim/globals.h index b45f13de4c..697a4a765a 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -159,15 +159,6 @@ EXTERN int Screen_mco INIT(= 0); /* value of p_mco used when * These are single-width. */ EXTERN schar_T *ScreenLines2 INIT(= NULL); -/* - * Indexes for tab page line: - * N > 0 for label of tab page N - * N == 0 for no label - * N < 0 for closing tab page -N - * N == -999 for closing current tab page - */ -EXTERN short *TabPageIdxs INIT(= NULL); - EXTERN int screen_Rows INIT(= 0); /* actual size of ScreenLines[] */ EXTERN int screen_Columns INIT(= 0); /* actual size of ScreenLines[] */ diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index 7054bb822a..65c808eb06 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -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 { /* diff --git a/src/nvim/log.c b/src/nvim/log.c index 5767da03af..773d497881 100644 --- a/src/nvim/log.c +++ b/src/nvim/log.c @@ -10,10 +10,6 @@ #include "nvim/os/os.h" #include "nvim/os/time.h" -#ifdef HAVE_UNISTD_H -# include <unistd.h> -#endif - #define USR_LOG_FILE "$HOME" _PATHSEPSTR ".nvimlog" static uv_mutex_t mutex; diff --git a/src/nvim/main.c b/src/nvim/main.c index cef10d12d5..a8c2cebbbd 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -219,9 +219,10 @@ int main(int argc, char **argv) { argv0 = (char *)path_tail((char_u *)argv[0]); - char_u *fname = NULL; /* file name from command line */ - mparm_T params; /* various parameters passed between - * main() and other functions. */ + char_u *fname = NULL; // file name from command line + mparm_T params; // various parameters passed between + // main() and other functions. + char_u *cwd = NULL; // current workding dir on startup time_init(); /* Many variables are in "params" so that we can pass them to invoked @@ -461,11 +462,10 @@ int main(int argc, char **argv) TIME_MSG("jump to first error"); } - /* - * If opened more than one window, start editing files in the other - * windows. - */ - edit_buffers(¶ms); + // If opened more than one window, start editing files in the other + // windows. + edit_buffers(¶ms, cwd); + xfree(cwd); if (params.diff_mode) { /* set options in each window for "nvim -d". */ @@ -1182,12 +1182,19 @@ static char_u *get_fname(mparm_T *parmp) * Expand wildcards in file names. */ if (!parmp->literal) { - /* Temporarily add '(' and ')' to 'isfname'. These are valid - * filename characters but are excluded from 'isfname' to make - * "gf" work on a file name in parenthesis (e.g.: see vim.h). */ + cwd = xmalloc(MAXPATHL); + if (cwd != NULL) { + os_dirname(cwd, MAXPATHL); + } + // Temporarily add '(' and ')' to 'isfname'. These are valid + // filename characters but are excluded from 'isfname' to make + // "gf" work on a file name in parenthesis (e.g.: see vim.h). do_cmdline_cmd(":set isf+=(,)"); alist_expand(NULL, 0); do_cmdline_cmd(":set isf&"); + if (cwd != NULL) { + os_chdir((char *)cwd); + } } #endif return alist_name(&GARGLIST[0]); @@ -1417,11 +1424,9 @@ static void create_windows(mparm_T *parmp) } } -/* - * If opened more than one window, start editing files in the other - * windows. make_windows() has already opened the windows. - */ -static void edit_buffers(mparm_T *parmp) +/// If opened more than one window, start editing files in the other +/// windows. make_windows() has already opened the windows. +static void edit_buffers(mparm_T *parmp, char_u *cwd) { int arg_idx; /* index in argument list */ int i; @@ -1442,7 +1447,10 @@ static void edit_buffers(mparm_T *parmp) arg_idx = 1; for (i = 1; i < parmp->window_count; ++i) { - /* When w_arg_idx is -1 remove the window (see create_windows()). */ + if (cwd != NULL) { + os_chdir((char *)cwd); + } + // When w_arg_idx is -1 remove the window (see create_windows()). if (curwin->w_arg_idx == -1) { ++arg_idx; win_close(curwin, TRUE); diff --git a/src/nvim/normal.c b/src/nvim/normal.c index a116b5a0bd..9a9cf50e48 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -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); } @@ -2334,8 +2347,9 @@ do_mouse ( if (mouse_row == 0 && firstwin->w_winrow > 0) { if (is_drag) { if (in_tab_line) { - c1 = TabPageIdxs[mouse_col]; - tabpage_move(c1 <= 0 ? 9999 : c1 - 1); + tabpage_move(tab_page_click_defs[mouse_col].type == kStlClickTabClose + ? 9999 + : tab_page_click_defs[mouse_col].tabnr - 1); } return false; } @@ -2345,41 +2359,114 @@ do_mouse ( && cmdwin_type == 0 && mouse_col < Columns) { in_tab_line = true; - c1 = TabPageIdxs[mouse_col]; - if (c1 >= 0) { - if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) { - /* double click opens new page */ - end_visual_mode(); - tabpage_new(); - tabpage_move(c1 == 0 ? 9999 : c1 - 1); - } else { - /* Go to specified tab page, or next one if not clicking - * on a label. */ - goto_tabpage(c1); - - /* It's like clicking on the status line of a window. */ - if (curwin != old_curwin) + c1 = tab_page_click_defs[mouse_col].tabnr; + switch (tab_page_click_defs[mouse_col].type) { + case kStlClickDisabled: { + break; + } + case kStlClickTabClose: { + tabpage_T *tp; + + // Close the current or specified tab page. + if (c1 == 999) { + tp = curtab; + } else { + tp = find_tabpage(c1); + } + if (tp == curtab) { + if (first_tabpage->tp_next != NULL) { + tabpage_close(false); + } + } else if (tp != NULL) { + tabpage_close_other(tp, false); + } + break; + } + case kStlClickTabSwitch: { + if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) { + // double click opens new page end_visual_mode(); + tabpage_new(); + tabpage_move(c1 == 0 ? 9999 : c1 - 1); + } else { + // Go to specified tab page, or next one if not clicking + // on a label. + goto_tabpage(c1); + + // It's like clicking on the status line of a window. + if (curwin != old_curwin) { + end_visual_mode(); + } + } + break; + } + case kStlClickFuncRun: { + typval_T argv[] = { + { + .v_lock = VAR_FIXED, + .v_type = VAR_NUMBER, + .vval = { + .v_number = (varnumber_T) tab_page_click_defs[mouse_col].tabnr + }, + }, + { + .v_lock = VAR_FIXED, + .v_type = VAR_NUMBER, + .vval = { + .v_number = (((mod_mask & MOD_MASK_MULTI_CLICK) + == MOD_MASK_4CLICK) + ? 4 + : ((mod_mask & MOD_MASK_MULTI_CLICK) + == MOD_MASK_3CLICK) + ? 3 + : ((mod_mask & MOD_MASK_MULTI_CLICK) + == MOD_MASK_2CLICK) + ? 2 + : 1) + }, + }, + { + .v_lock = VAR_FIXED, + .v_type = VAR_STRING, + .vval = { .v_string = (char_u *) (which_button == MOUSE_LEFT + ? "l" + : which_button == MOUSE_RIGHT + ? "r" + : which_button == MOUSE_MIDDLE + ? "m" + : "?") }, + }, + { + .v_lock = VAR_FIXED, + .v_type = VAR_STRING, + .vval = { + .v_string = (char_u[]) { + (char_u) (mod_mask & MOD_MASK_SHIFT ? 's' : ' '), + (char_u) (mod_mask & MOD_MASK_CTRL ? 'c' : ' '), + (char_u) (mod_mask & MOD_MASK_ALT ? 'a' : ' '), + (char_u) (mod_mask & MOD_MASK_META ? 'm' : ' '), + NUL + } + }, + } + }; + typval_T rettv; + int doesrange; + (void) call_func((char_u *) tab_page_click_defs[mouse_col].func, + (int) strlen(tab_page_click_defs[mouse_col].func), + &rettv, ARRAY_SIZE(argv), argv, + curwin->w_cursor.lnum, curwin->w_cursor.lnum, + &doesrange, true, NULL); + clear_tv(&rettv); + break; } - } else if (c1 < 0) { - tabpage_T *tp; - - /* Close the current or specified tab page. */ - if (c1 == -999) - tp = curtab; - else - tp = find_tabpage(-c1); - if (tp == curtab) { - if (first_tabpage->tp_next != NULL) - tabpage_close(false); - } else if (tp != NULL) - tabpage_close_other(tp, false); } } return true; } else if (is_drag && in_tab_line) { - c1 = TabPageIdxs[mouse_col]; - tabpage_move(c1 <= 0 ? 9999 : c1 - 1); + tabpage_move(tab_page_click_defs[mouse_col].type == kStlClickTabClose + ? 9999 + : tab_page_click_defs[mouse_col].tabnr - 1); in_tab_line = false; return false; } @@ -2852,10 +2939,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 +3205,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 +3596,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 +6427,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); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 19dbd0f9f6..7614e6365a 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -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 (!VIsual_active) { + if (dobin) { + while (col > 0 && ascii_isbdigit(ptr[col])) { + 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 - if (dohex) { - while (col > 0 && ascii_isxdigit(ptr[col])) { - col--; + col = curwin->w_cursor.col; + + while (col > 0 && ascii_isdigit(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; + 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])) { + 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]))) { + if (visual) { + while (ptr[col] != NUL && length > 0 && !ascii_isdigit(ptr[col]) && + !(doalp && ASCII_ISALPHA(ptr[col]))) { col++; + length--; } - while (col > 0 - && ascii_isdigit(ptr[col - 1]) - && !(doalp && ASCII_ISALPHA(ptr[col]))) { - col--; + 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; } /* diff --git a/src/nvim/ops.h b/src/nvim/ops.h index 507f933acf..f33e87572f 100644 --- a/src/nvim/ops.h +++ b/src/nvim/ops.h @@ -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 { diff --git a/src/nvim/option.c b/src/nvim/option.c index b4054dc28c..d3a2ce971d 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -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; @@ -1673,6 +1673,11 @@ do_set ( && *newval != NUL); if (adding) { i = (int)STRLEN(origval); + // Strip a trailing comma, would get 2. + if (comma && i > 1 && origval[i - 1] == ',' + && origval[i - 2] != '\\') { + --i; + } memmove(newval + i + comma, newval, STRLEN(newval) + 1); memmove(newval, origval, (size_t)i); diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 7a837de45c..11b5e31f77 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -209,44 +209,58 @@ #define COM_ALL "nbsmexflrO" /* all flags for 'comments' option */ #define COM_MAX_LEN 50 /* maximum length of a part */ -/* flags for 'statusline' option */ -#define STL_FILEPATH 'f' /* path of file in buffer */ -#define STL_FULLPATH 'F' /* full path of file in buffer */ -#define STL_FILENAME 't' /* last part (tail) of file path */ -#define STL_COLUMN 'c' /* column og cursor*/ -#define STL_VIRTCOL 'v' /* virtual column */ -#define STL_VIRTCOL_ALT 'V' /* - with 'if different' display */ -#define STL_LINE 'l' /* line number of cursor */ -#define STL_NUMLINES 'L' /* number of lines in buffer */ -#define STL_BUFNO 'n' /* current buffer number */ -#define STL_KEYMAP 'k' /* 'keymap' when active */ -#define STL_OFFSET 'o' /* offset of character under cursor*/ -#define STL_OFFSET_X 'O' /* - in hexadecimal */ -#define STL_BYTEVAL 'b' /* byte value of character */ -#define STL_BYTEVAL_X 'B' /* - in hexadecimal */ -#define STL_ROFLAG 'r' /* readonly flag */ -#define STL_ROFLAG_ALT 'R' /* - other display */ -#define STL_HELPFLAG 'h' /* window is showing a help file */ -#define STL_HELPFLAG_ALT 'H' /* - other display */ -#define STL_FILETYPE 'y' /* 'filetype' */ -#define STL_FILETYPE_ALT 'Y' /* - other display */ -#define STL_PREVIEWFLAG 'w' /* window is showing the preview buf */ -#define STL_PREVIEWFLAG_ALT 'W' /* - other display */ -#define STL_MODIFIED 'm' /* modified flag */ -#define STL_MODIFIED_ALT 'M' /* - other display */ -#define STL_QUICKFIX 'q' /* quickfix window description */ -#define STL_PERCENTAGE 'p' /* percentage through file */ -#define STL_ALTPERCENT 'P' /* percentage as TOP BOT ALL or NN% */ -#define STL_ARGLISTSTAT 'a' /* argument list status as (x of y) */ -#define STL_PAGENUM 'N' /* page number (when printing)*/ -#define STL_VIM_EXPR '{' /* start of expression to substitute */ -#define STL_MIDDLEMARK '=' /* separation between left and right */ -#define STL_TRUNCMARK '<' /* truncation mark if line is too long*/ -#define STL_USER_HL '*' /* highlight from (User)1..9 or 0 */ -#define STL_HIGHLIGHT '#' /* highlight name */ -#define STL_TABPAGENR 'T' /* tab page label nr */ -#define STL_TABCLOSENR 'X' /* tab page close nr */ -#define STL_ALL ((char_u *) "fFtcvVlLknoObBrRhHmYyWwMqpPaN{#") +/// 'statusline' option flags +enum { + STL_FILEPATH = 'f', ///< Path of file in buffer. + STL_FULLPATH = 'F', ///< Full path of file in buffer. + STL_FILENAME = 't', ///< Last part (tail) of file path. + STL_COLUMN = 'c', ///< Column og cursor. + STL_VIRTCOL = 'v', ///< Virtual column. + STL_VIRTCOL_ALT = 'V', ///< - with 'if different' display. + STL_LINE = 'l', ///< Line number of cursor. + STL_NUMLINES = 'L', ///< Number of lines in buffer. + STL_BUFNO = 'n', ///< Current buffer number. + STL_KEYMAP = 'k', ///< 'keymap' when active. + STL_OFFSET = 'o', ///< Offset of character under cursor. + STL_OFFSET_X = 'O', ///< - in hexadecimal. + STL_BYTEVAL = 'b', ///< Byte value of character. + STL_BYTEVAL_X = 'B', ///< - in hexadecimal. + STL_ROFLAG = 'r', ///< Readonly flag. + STL_ROFLAG_ALT = 'R', ///< - other display. + STL_HELPFLAG = 'h', ///< Window is showing a help file. + STL_HELPFLAG_ALT = 'H', ///< - other display. + STL_FILETYPE = 'y', ///< 'filetype'. + STL_FILETYPE_ALT = 'Y', ///< - other display. + STL_PREVIEWFLAG = 'w', ///< Window is showing the preview buf. + STL_PREVIEWFLAG_ALT = 'W', ///< - other display. + STL_MODIFIED = 'm', ///< Modified flag. + STL_MODIFIED_ALT = 'M', ///< - other display. + STL_QUICKFIX = 'q', ///< Quickfix window description. + STL_PERCENTAGE = 'p', ///< Percentage through file. + STL_ALTPERCENT = 'P', ///< Percentage as TOP BOT ALL or NN%. + STL_ARGLISTSTAT = 'a', ///< Argument list status as (x of y). + STL_PAGENUM = 'N', ///< Page number (when printing). + STL_VIM_EXPR = '{', ///< Start of expression to substitute. + STL_MIDDLEMARK = '=', ///< Separation between left and right. + STL_TRUNCMARK = '<', ///< Truncation mark if line is too long. + STL_USER_HL = '*', ///< Highlight from (User)1..9 or 0. + STL_HIGHLIGHT = '#', ///< Highlight name. + STL_TABPAGENR = 'T', ///< Tab page label nr. + STL_TABCLOSENR = 'X', ///< Tab page close nr. + STL_CLICK_FUNC = '@', ///< Click region start. +}; +/// C string containing all 'statusline' option flags +#define STL_ALL ((char_u[]) { \ + STL_FILEPATH, STL_FULLPATH, STL_FILENAME, STL_COLUMN, STL_VIRTCOL, \ + STL_VIRTCOL_ALT, STL_LINE, STL_NUMLINES, STL_BUFNO, STL_KEYMAP, STL_OFFSET, \ + STL_OFFSET_X, STL_BYTEVAL, STL_BYTEVAL_X, STL_ROFLAG, STL_ROFLAG_ALT, \ + STL_HELPFLAG, STL_HELPFLAG_ALT, STL_FILETYPE, STL_FILETYPE_ALT, \ + STL_PREVIEWFLAG, STL_PREVIEWFLAG_ALT, STL_MODIFIED, STL_MODIFIED_ALT, \ + STL_QUICKFIX, STL_PERCENTAGE, STL_ALTPERCENT, STL_ARGLISTSTAT, STL_PAGENUM, \ + STL_VIM_EXPR, STL_MIDDLEMARK, STL_TRUNCMARK, STL_USER_HL, STL_HIGHLIGHT, \ + STL_TABPAGENR, STL_TABCLOSENR, STL_CLICK_FUNC, \ + 0, \ +}) /* flags used for parsed 'wildmode' */ #define WIM_FULL 1 diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 21f0fc6f41..2e671653ed 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -146,6 +146,16 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath) } size_t buf_len = STRLEN(name) + STRLEN(path) + 2; + +#ifdef WIN32 + const char *pathext = os_getenv("PATHEXT"); + if (!pathext) { + pathext = ".com;.exe;.bat;.cmd"; + } + + buf_len += STRLEN(pathext); +#endif + char_u *buf = xmalloc(buf_len); // Walk through all entries in $PATH to check if "name" exists there and @@ -169,6 +179,38 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath) return true; } +#ifdef WIN32 + // Try appending file extensions from $PATHEXT to the name. + char *buf_end = xstrchrnul((char *)buf, '\0'); + for (const char *ext = pathext; *ext; ext++) { + // Skip the extension if there is no suffix after a '.'. + if (ext[0] == '.' && (ext[1] == '\0' || ext[1] == ';')) { + *ext++; + + continue; + } + + const char *ext_end = xstrchrnul(ext, ENV_SEPCHAR); + STRLCPY(buf_end, ext, ext_end - ext + 1); + + if (is_executable(buf)) { + // Check if the caller asked for a copy of the path. + if (abspath != NULL) { + *abspath = save_absolute_path(buf); + } + + xfree(buf); + + return true; + } + + if (*ext_end != ENV_SEPCHAR) { + break; + } + ext = ext_end; + } +#endif + if (*e != ENV_SEPCHAR) { // End of $PATH without finding any executable called name. xfree(buf); diff --git a/src/nvim/os/unix_defs.h b/src/nvim/os/unix_defs.h index 78fc9331d1..690a39c3cd 100644 --- a/src/nvim/os/unix_defs.h +++ b/src/nvim/os/unix_defs.h @@ -2,7 +2,7 @@ #define NVIM_OS_UNIX_DEFS_H // Windows doesn't have unistd.h, so we include it here to avoid numerous -// instances of `#ifdef HAVE_UNISTD_H'. +// instances of `#ifdef WIN32'. #include <unistd.h> // POSIX.1-2008 says that NAME_MAX should be in here diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h index ba96347a12..242d355f77 100644 --- a/src/nvim/os/win_defs.h +++ b/src/nvim/os/win_defs.h @@ -3,6 +3,7 @@ #include <windows.h> #include <sys/stat.h> +#include <io.h> #include <stdio.h> // Windows does not have S_IFLNK but libuv defines it diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 85c69af192..f3abf864fb 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1606,13 +1606,12 @@ win_found: } if (qf_ptr->qf_col > 0) { curwin->w_cursor.col = qf_ptr->qf_col - 1; - if (qf_ptr->qf_viscol == TRUE) { - /* - * Check each character from the beginning of the error - * line up to the error column. For each tab character - * found, reduce the error column value by the length of - * a tab character. - */ + curwin->w_cursor.coladd = 0; + if (qf_ptr->qf_viscol == true) { + // Check each character from the beginning of the error + // line up to the error column. For each tab character + // found, reduce the error column value by the length of + // a tab character. line = get_cursor_line_ptr(); screen_col = 0; for (char_col = 0; char_col < curwin->w_cursor.col; ++char_col) { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 43bc2c1f68..e036c49be4 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -147,6 +147,9 @@ static foldinfo_T win_foldinfo; /* info for 'foldcolumn' */ */ static schar_T *current_ScreenLine; +StlClickDefinition *tab_page_click_defs = NULL; +long tab_page_click_defs_size = 0; + # define SCREEN_LINE(r, o, e, c, rl) screen_line((r), (o), (e), (c), (rl)) #ifdef INCLUDE_GENERATED_DECLARATIONS # include "screen.c.generated.h" @@ -5009,8 +5012,8 @@ win_redr_custom ( char_u *stl; char_u *p; struct stl_hlrec hltab[STL_MAX_ITEM]; - struct stl_hlrec tabtab[STL_MAX_ITEM]; - int use_sandbox = FALSE; + StlClickRecord tabtab[STL_MAX_ITEM]; + int use_sandbox = false; win_T *ewp; int p_crb_save; @@ -5126,20 +5129,24 @@ win_redr_custom ( screen_puts(p >= buf + len ? (char_u *)"" : p, row, col, curattr); if (wp == NULL) { - /* Fill the TabPageIdxs[] array for clicking in the tab pagesline. */ + // Fill the tab_page_click_defs array for clicking in the tab pages line. col = 0; len = 0; p = buf; - fillchar = 0; + StlClickDefinition cur_click_def = { + .type = kStlClickDisabled, + }; for (n = 0; tabtab[n].start != NULL; n++) { - len += vim_strnsize(p, (int)(tabtab[n].start - p)); - while (col < len) - TabPageIdxs[col++] = fillchar; - p = tabtab[n].start; - fillchar = tabtab[n].userhl; + len += vim_strnsize(p, (int)(tabtab[n].start - (char *) p)); + while (col < len) { + tab_page_click_defs[col++] = cur_click_def; + } + p = (char_u *) tabtab[n].start; + cur_click_def = tabtab[n].def; + } + while (col < Columns) { + tab_page_click_defs[col++] = cur_click_def; } - while (col < Columns) - TabPageIdxs[col++] = fillchar; } theend: @@ -5958,9 +5965,9 @@ void screenalloc(bool doclear) sattr_T *new_ScreenAttrs; unsigned *new_LineOffset; char_u *new_LineWraps; - short *new_TabPageIdxs; - static int entered = FALSE; /* avoid recursiveness */ - static int done_outofmem_msg = FALSE; /* did outofmem message */ + StlClickDefinition *new_tab_page_click_defs; + static bool entered = false; // avoid recursiveness + static bool done_outofmem_msg = false; int retry_count = 0; const bool l_enc_utf8 = enc_utf8; const int l_enc_dbcs = enc_dbcs; @@ -6033,7 +6040,8 @@ retry: new_ScreenAttrs = xmalloc((size_t)((Rows + 1) * Columns * sizeof(sattr_T))); new_LineOffset = xmalloc((size_t)(Rows * sizeof(unsigned))); new_LineWraps = xmalloc((size_t)(Rows * sizeof(char_u))); - new_TabPageIdxs = xmalloc((size_t)(Columns * sizeof(short))); + new_tab_page_click_defs = xcalloc( + (size_t) Columns, sizeof(*new_tab_page_click_defs)); FOR_ALL_TAB_WINDOWS(tp, wp) { win_alloc_lines(wp); @@ -6051,7 +6059,7 @@ retry: || new_ScreenAttrs == NULL || new_LineOffset == NULL || new_LineWraps == NULL - || new_TabPageIdxs == NULL + || new_tab_page_click_defs == NULL || outofmem) { if (ScreenLines != NULL || !done_outofmem_msg) { /* guess the size */ @@ -6077,8 +6085,8 @@ retry: new_LineOffset = NULL; xfree(new_LineWraps); new_LineWraps = NULL; - xfree(new_TabPageIdxs); - new_TabPageIdxs = NULL; + xfree(new_tab_page_click_defs); + new_tab_page_click_defs = NULL; } else { done_outofmem_msg = FALSE; @@ -6157,7 +6165,8 @@ retry: ScreenAttrs = new_ScreenAttrs; LineOffset = new_LineOffset; LineWraps = new_LineWraps; - TabPageIdxs = new_TabPageIdxs; + tab_page_click_defs = new_tab_page_click_defs; + tab_page_click_defs_size = Columns; /* It's important that screen_Rows and screen_Columns reflect the actual * size of ScreenLines[]. Set them before calling anything. */ @@ -6196,7 +6205,25 @@ void free_screenlines(void) xfree(ScreenAttrs); xfree(LineOffset); xfree(LineWraps); - xfree(TabPageIdxs); + clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size); + xfree(tab_page_click_defs); +} + +/// Clear tab_page_click_defs table +/// +/// @param[out] tpcd Table to clear. +/// @param[in] tpcd_size Size of the table. +void clear_tab_page_click_defs(StlClickDefinition *const tpcd, + const long tpcd_size) +{ + if (tpcd != NULL) { + for (long i = 0; i < tpcd_size; i++) { + if (i == 0 || tpcd[i].func != tpcd[i - 1].func) { + xfree(tpcd[i].func); + } + } + memset(tpcd, 0, (size_t) tpcd_size * sizeof(tpcd[0])); + } } void screenclear(void) @@ -6804,9 +6831,9 @@ static void draw_tabline(void) return; - /* Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect. */ - for (scol = 0; scol < Columns; ++scol) - TabPageIdxs[scol] = 0; + // Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect. + assert(Columns == tab_page_click_defs_size); + clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size); /* Use the 'tabline' option if it's set. */ if (*p_tal != NUL) { @@ -6904,11 +6931,16 @@ static void draw_tabline(void) } screen_putchar(' ', 0, col++, attr); - /* Store the tab page number in TabPageIdxs[], so that - * jump_to_mouse() knows where each one is. */ - ++tabcount; - while (scol < col) - TabPageIdxs[scol++] = tabcount; + // Store the tab page number in tab_page_click_defs[], so that + // jump_to_mouse() knows where each one is. + tabcount++; + while (scol < col) { + tab_page_click_defs[scol++] = (StlClickDefinition) { + .type = kStlClickTabSwitch, + .tabnr = tabcount, + .func = NULL, + }; + } } if (use_sep_chars) @@ -6920,7 +6952,11 @@ static void draw_tabline(void) /* Put an "X" for closing the current tab if there are several. */ if (first_tabpage->tp_next != NULL) { screen_putchar('X', 0, (int)Columns - 1, attr_nosel); - TabPageIdxs[Columns - 1] = -999; + tab_page_click_defs[Columns - 1] = (StlClickDefinition) { + .type = kStlClickTabClose, + .tabnr = 999, + .func = NULL, + }; } } diff --git a/src/nvim/screen.h b/src/nvim/screen.h index debf86ae67..81a8b9ed4c 100644 --- a/src/nvim/screen.h +++ b/src/nvim/screen.h @@ -16,6 +16,29 @@ #define NOT_VALID 40 /* buffer needs complete redraw */ #define CLEAR 50 /* screen messed up, clear it */ +/// Status line click definition +typedef struct { + enum { + kStlClickDisabled = 0, ///< Clicks to this area are ignored. + kStlClickTabSwitch, ///< Switch to the given tab. + kStlClickTabClose, ///< Close given tab. + kStlClickFuncRun, ///< Run user function. + } type; ///< Type of the click. + int tabnr; ///< Tab page number. + char *func; ///< Function to run. +} StlClickDefinition; + +/// Used for tabline clicks +typedef struct { + StlClickDefinition def; ///< Click definition. + const char *start; ///< Location where region starts. +} StlClickRecord; + +/// Array defining what should be done when tabline is clicked +extern StlClickDefinition *tab_page_click_defs; + +/// Size of the tab_page_click_defs array +extern long tab_page_click_defs_size; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "screen.h.generated.h" diff --git a/src/nvim/search.c b/src/nvim/search.c index d393ee7d02..2dd0201259 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -3076,18 +3076,18 @@ current_block ( } else old_end = VIsual; - /* - * Search backwards for unclosed '(', '{', etc.. - * Put this position in start_pos. - * Ignore quotes here. - */ + // Search backwards for unclosed '(', '{', etc.. + // Put this position in start_pos. + // Ignore quotes here. Keep the "M" flag in 'cpo', as that is what the + // user wants. save_cpo = p_cpo; - p_cpo = (char_u *)"%"; + p_cpo = (char_u *)(vim_strchr(p_cpo, CPO_MATCHBSL) != NULL ? "%M" : "%"); while (count-- > 0) { - if ((pos = findmatch(NULL, what)) == NULL) + if ((pos = findmatch(NULL, what)) == NULL) { break; + } curwin->w_cursor = *pos; - start_pos = *pos; /* the findmatch for end_pos will overwrite *pos */ + start_pos = *pos; // the findmatch for end_pos will overwrite *pos } p_cpo = save_cpo; diff --git a/src/nvim/shada.c b/src/nvim/shada.c index dcdf2195f8..def2de9b1a 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -6,9 +6,6 @@ #include <inttypes.h> #include <errno.h> #include <fcntl.h> -#ifdef HAVE_UNISTD_H -# include <unistd.h> -#endif #include <assert.h> #include <msgpack.h> diff --git a/src/nvim/spell.c b/src/nvim/spell.c index b2028109be..cc7dc6210c 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -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)) diff --git a/src/nvim/tag.c b/src/nvim/tag.c index d832924efd..8fcb02c3b6 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -445,17 +445,10 @@ do_tag ( tagmatchname = vim_strsave(name); } - /* - * If a count is supplied to the ":tag <name>" command, then - * jump to count'th matching tag. - */ - if (type == DT_TAG && *tag != NUL && count > 0) - cur_match = count - 1; - - if (type == DT_SELECT || type == DT_JUMP - || type == DT_LTAG - ) + if (type == DT_TAG || type == DT_SELECT || type == DT_JUMP + || type == DT_LTAG) { cur_match = MAXCOL - 1; + } max_num_matches = cur_match + 1; /* when the argument starts with '/', use it as a regexp */ @@ -506,18 +499,19 @@ do_tag ( EMSG2(_("E426: tag not found: %s"), name); g_do_tagpreview = 0; } else { - int ask_for_selection = FALSE; + bool ask_for_selection = false; if (type == DT_CSCOPE && num_matches > 1) { cs_print_tags(); - ask_for_selection = TRUE; - } else if (type == DT_SELECT || - (type == DT_JUMP && num_matches > 1)) { - /* - * List all the matching tags. - * Assume that the first match indicates how long the tags can - * be, and align the file names to that. - */ + ask_for_selection = true; + } else if (type == DT_TAG) { + // If a count is supplied to the ":tag <name>" command, then + // jump to count'th matching tag. + cur_match = count > 0 ? count - 1 : 0; + } else if (type == DT_SELECT || (type == DT_JUMP && num_matches > 1)) { + // List all the matching tags. + // Assume that the first match indicates how long the tags can + // be, and align the file names to that. parse_match(matches[0], &tagp); taglen = (int)(tagp.tagname_end - tagp.tagname + 2); if (taglen < 18) @@ -666,9 +660,10 @@ do_tag ( msg_putchar('\n'); os_breakcheck(); } - if (got_int) - got_int = FALSE; /* only stop the listing */ - ask_for_selection = TRUE; + if (got_int) { + got_int = false; // only stop the listing + } + ask_for_selection = true; } else if (type == DT_LTAG) { list_T *list; char_u tag_name[128 + 1]; @@ -801,10 +796,8 @@ do_tag ( cur_match = 0; /* Jump to the first tag */ } - if (ask_for_selection == TRUE) { - /* - * Ask to select a tag from the list. - */ + if (ask_for_selection) { + // Ask to select a tag from the list. i = prompt_for_number(NULL); if (i <= 0 || i > num_matches || got_int) { /* no valid choice: don't change anything */ @@ -851,7 +844,7 @@ do_tag ( ic = (matches[cur_match][0] & MT_IC_OFF); - if (type != DT_SELECT && type != DT_JUMP + if (type != DT_TAG && type != DT_SELECT && type != DT_JUMP && type != DT_CSCOPE && (num_matches > 1 || ic) && !skip_msg) { diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 41ce2daa91..d1a7abfbf7 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -15,7 +15,7 @@ SCRIPTS := \ test30.out \ test32.out test34.out \ test36.out test37.out test40.out \ - test42.out test45.out \ + test42.out \ test47.out test48.out test49.out \ test52.out test53.out test55.out \ test64.out \ @@ -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; \ diff --git a/src/nvim/testdir/test13.in b/src/nvim/testdir/test13.in index cb8a6fff89..6713f80e88 100644 --- a/src/nvim/testdir/test13.in +++ b/src/nvim/testdir/test13.in @@ -48,6 +48,12 @@ otestje3 :au BufWipeout Xtestje1 buf Xtestje1 :bwipe :w >>test.out +:only +:new|set buftype=help +:wincmd w +:1quit +:$put ='Final line' +:$w >>test.out :qa! ENDTEST diff --git a/src/nvim/testdir/test13.ok b/src/nvim/testdir/test13.ok index 0f1fc347a4..66ebce63f7 100644 --- a/src/nvim/testdir/test13.ok +++ b/src/nvim/testdir/test13.ok @@ -28,3 +28,4 @@ testje1 contents contents end of testfile +Final line diff --git a/src/nvim/testdir/test30.in b/src/nvim/testdir/test30.in index 3f7b9eb472..2a89eac73d 100644 --- a/src/nvim/testdir/test30.in +++ b/src/nvim/testdir/test30.in @@ -7,32 +7,27 @@ STARTTEST :" first write three test files, one in each format :set fileformat=unix :set fileformats= -:/^1/w! XX1 -:/^2/w! XX2 -:/^3/w! XX3 -:/^4/w! XX4 -:/^5/w! XX5 -:/^6/w! XX6 -:/^7/w! XX7 -:/^8/w! XX8 -:/^9/w! XX9 -:/^10/w! XX10 :/^unix/;/eof/-1w! XXUnix :/^dos/;/eof/-1w! XXDos :set bin noeol :$w! XXMac +Gonoeol +:$w! XXEol :set nobin eol +:enew! :bwipe XXUnix XXDos XXMac :" create mixed format files :if has("win32") : !copy /b XXUnix+XXDos XXUxDs : !copy /b XXUnix+XXMac XXUxMac : !copy /b XXDos+XXMac XXDosMac +: !copy /b XXMac+XXEol XXMacEol : !copy /b XXUnix+XXDos+XXMac XXUxDsMc :else : !cat XXUnix XXDos >XXUxDs : !cat XXUnix XXMac >XXUxMac : !cat XXDos XXMac >XXDosMac +: !cat XXMac XXEol >XXMacEol : !cat XXUnix XXDos XXMac >XXUxDsMc :endif :" @@ -97,26 +92,48 @@ STARTTEST :e! XXDosMac :w! XXtt53 :bwipe XXDosMac +:e! XXEol +ggO=&ffs +:=&ff +:w! XXtt54 +:bwipe XXEol :set fileformats=dos,mac :e! XXUxDs :w! XXtt61 :bwipe XXUxDs :e! XXUxMac -:w! XXtt62 +ggO=&ffs +:=&ff +:w! XXtt62 :bwipe XXUxMac :e! XXUxDsMc :w! XXtt63 :bwipe XXUxDsMc +:e! XXMacEol +ggO=&ffs +:=&ff +:w! XXtt64 +:bwipe XXMacEol :" :" try reading and writing with 'fileformats' set to three formats :set fileformats=unix,dos,mac :e! XXUxDsMc :w! XXtt71 :bwipe XXUxDsMc +:e! XXEol +ggO=&ffs +:=&ff +:w! XXtt72 +:bwipe XXEol :set fileformats=mac,dos,unix :e! XXUxDsMc :w! XXtt81 :bwipe XXUxDsMc +:e! XXEol +ggO=&ffs +:=&ff +:w! XXtt82 +:bwipe XXEol :" try with 'binary' set :set fileformats=mac,unix,dos :set binary @@ -150,11 +167,15 @@ ggdGaEND:w >>XXtt01 :w >>XXtt51 :w >>XXtt52 :w >>XXtt53 +:w >>XXtt54 :w >>XXtt61 :w >>XXtt62 :w >>XXtt63 +:w >>XXtt64 :w >>XXtt71 +:w >>XXtt72 :w >>XXtt81 +:w >>XXtt82 :w >>XXtt91 :w >>XXtt92 :w >>XXtt93 @@ -181,11 +202,15 @@ Go4:$r XXtt41 Go5:$r XXtt51 :$r XXtt52 :$r XXtt53 +:$r XXtt54 Go6:$r XXtt61 :$r XXtt62 :$r XXtt63 +:$r XXtt64 Go7:$r XXtt71 +:$r XXtt72 Go8:$r XXtt81 +:$r XXtt82 Go9:$r XXtt91 :$r XXtt92 :$r XXtt93 @@ -195,17 +220,6 @@ Go10:$r XXUnix :qa! ENDTEST -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 - unix unix eof diff --git a/src/nvim/testdir/test30.ok b/src/nvim/testdir/test30.ok index 380ce67061..b35f4f5904 100644 --- a/src/nvim/testdir/test30.ok +++ b/src/nvim/testdir/test30.ok @@ -70,12 +70,16 @@ END dos
dos
mac
mac
END +unix,mac:unix +noeol +END 6 unix
unix
dos
dos
END +dos,mac:dos
unix
unix
mac
mac
@@ -86,6 +90,7 @@ dos dos
mac
mac
END +dos,mac:mac
mac
mac
noeol
END 7 unix unix @@ -93,6 +98,9 @@ dos dos
mac
mac
END +unix,dos,mac:unix +noeol +END 8 unix unix @@ -100,6 +108,7 @@ dos dos
mac
mac
END +mac,dos,unix:mac
noeol
END 9 unix unix diff --git a/src/nvim/testdir/test45.in b/src/nvim/testdir/test45.in deleted file mode 100644 index e5af5073d9..0000000000 --- a/src/nvim/testdir/test45.in +++ /dev/null @@ -1,80 +0,0 @@ -Tests for folding. vim: set ft=vim : - -STARTTEST -:so small.vim -:" We also need the +syntax feature here. -:if !has("syntax") - e! test.ok - w! test.out - qa! -:endif -:" basic test if a fold can be created, opened, moving to the end and closed -/^1 -zf2j:call append("$", "manual " . getline(foldclosed("."))) -zo:call append("$", foldclosed(".")) -]z:call append("$", getline(".")) -zc:call append("$", getline(foldclosed("."))) -:" test folding with markers. -:set fdm=marker fdl=1 fdc=3 -/^5 -:call append("$", "marker " . foldlevel(".")) -[z:call append("$", foldlevel(".")) -jo{{ r{jj:call append("$", foldlevel(".")) -kYpj:call append("$", foldlevel(".")) -:" test folding with indent -:set fdm=indent sw=2 -/^2 b -i jI :call append("$", "indent " . foldlevel(".")) -k:call append("$", foldlevel(".")) -:" test syntax folding -:set fdm=syntax fdl=0 -:syn region Hup start="dd" end="ii" fold contains=Fd1,Fd2,Fd3 -:syn region Fd1 start="ee" end="ff" fold contained -:syn region Fd2 start="gg" end="hh" fold contained -:syn region Fd3 start="commentstart" end="commentend" fold contained -Gzk:call append("$", "folding " . getline(".")) -k:call append("$", getline(".")) -jAcommentstart Acommentend:set fdl=1 -3j:call append("$", getline(".")) -:set fdl=0 -zOj:call append("$", getline(".")) -:" test expression folding -:fun Flvl() - let l = getline(v:lnum) - if l =~ "bb$" - return 2 - elseif l =~ "gg$" - return "s1" - elseif l =~ "ii$" - return ">2" - elseif l =~ "kk$" - return "0" - endif - return "=" -endfun -:set fdm=expr fde=Flvl() -/bb$ -:call append("$", "expr " . foldlevel(".")) -/hh$ -:call append("$", foldlevel(".")) -/ii$ -:call append("$", foldlevel(".")) -/kk$ -:call append("$", foldlevel(".")) -:/^last/+1,$w! test.out -:delfun Flvl -:qa! -ENDTEST - -1 aa -2 bb -3 cc -4 dd {{{ -5 ee {{{ }}} -6 ff }}} -7 gg -8 hh -9 ii -a jj -b kk -last diff --git a/src/nvim/testdir/test45.ok b/src/nvim/testdir/test45.ok deleted file mode 100644 index f04996e337..0000000000 --- a/src/nvim/testdir/test45.ok +++ /dev/null @@ -1,18 +0,0 @@ -manual 1 aa --1 -3 cc -1 aa -marker 2 -1 -1 -0 -indent 2 -1 -folding 9 ii - 3 cc -7 gg -8 hh -expr 2 -1 -2 -0 diff --git a/src/nvim/testdir/test_marks.in b/src/nvim/testdir/test_marks.in new file mode 100644 index 0000000000..23c2fb65fe --- /dev/null +++ b/src/nvim/testdir/test_marks.in @@ -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: diff --git a/src/nvim/testdir/test_marks.ok b/src/nvim/testdir/test_marks.ok new file mode 100644 index 0000000000..e6c02ee7b0 --- /dev/null +++ b/src/nvim/testdir/test_marks.ok @@ -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] diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 6b60f95f22..b8cdffcda0 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -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]; + } + 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; diff --git a/src/nvim/version.c b/src/nvim/version.c index e7e6d8b55c..b28d5706c8 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -69,16 +69,170 @@ static char *features[] = { // clang-format off static int included_patches[] = { + // 1219 NA + // 1218 NA + // 1217 NA + // 1216 NA + // 1215 NA + // 1214 NA + // 1213 NA + // 1212 NA + // 1211 NA + // 1210 NA + // 1209 NA + // 1208 NA + // 1207 NA + // 1206 NA + // 1205 NA + // 1204 NA + // 1203 NA + // 1202 NA + // 1201 NA + // 1200 NA + // 1199 NA + // 1198 NA + // 1197 NA + // 1196 NA + // 1195 NA + // 1194 NA + // 1193 NA + // 1192 NA + // 1191 NA + // 1190 NA + // 1189 NA + // 1188, + // 1187 NA + // 1186, + // 1185 NA + // 1184 NA + // 1183 NA + // 1182 NA + // 1181, + 1180, + // 1179, + // 1178, + // 1177 NA + // 1176 NA + // 1175 NA + // 1174 NA + // 1173, + // 1172 NA + // 1171 NA + // 1170 NA + // 1169 NA + // 1168, + // 1167, + // 1166, + // 1165 NA + // 1164, + // 1163, + // 1162 NA + // 1161, + // 1160, + // 1159 NA + // 1158 NA + // 1157, + // 1156, + // 1155 NA + // 1154, + // 1153, + // 1152 NA + // 1151, + // 1150, + 1149, + // 1148 NA + // 1147, + // 1146 NA + // 1145 NA + // 1144 NA + // 1143, + // 1142, + // 1141, + // 1140, + // 1139 NA + // 1138 NA 1137, - - - + // 1136, + // 1135 NA, + // 1134 NA, + // 1133 NA + // 1132, + // 1131 NA + // 1130, + // 1129 NA + // 1128 NA + // 1127 NA + // 1126, + // 1125 NA + // 1124 NA + // 1123, + // 1122 NA + // 1121, + // 1120, + // 1119, + // 1118, + // 1117, + // 1116, + // 1115 NA + // 1114, + // 1113, + // 1112, + // 1111, + // 1110, + // 1109 NA + // 1108, + // 1107, + // 1106 NA + // 1105, + // 1104 NA + // 1103 NA + // 1102, + // 1101, + // 1100 NA + // 1099 NA + // 1098 NA + // 1097, + // 1096, + // 1095 NA + // 1094, + // 1093, + // 1092, + // 1091, + // 1090, + 1089, + 1088, + 1087, + // 1086, + 1085, + 1084, + // 1083 NA, + // 1082 NA, 1081, - - - - - + // 1080 NA, + // 1079, + // 1078 NA, + // 1077 NA, + 1076, + // 1075, + // 1074 NA, + // 1073, + 1072, + // 1071, + // 1070 NA, + // 1069 NA, + // 1068, + // 1067 NA, + // 1066 NA, + 1065, + // 1064, + // 1063 NA, + // 1062 NA, + // 1061, + // 1060 NA, + // 1059, + // 1058, + // 1057, + // 1056, 1055, // 1054, // 1053, @@ -89,50 +243,50 @@ static int included_patches[] = { // 1048, // 1047, // 1046, - // 1045, - // 1044, - // 1043, + // 1045 NA, + // 1044 NA, + // 1043 NA, // 1042, // 1041, - // 1040, + // 1040 NA, // 1039, - // 1038, + // 1038 NA, // 1037, // 1036, // 1035, // 1034, - // 1033, + // 1033 NA, 1032, - // 1031, + // 1031 NA, // 1030, - // 1029, - // 1028, - // 1027, - // 1026, - // 1025, - // 1024, - // 1023, - // 1022, - // 1021, - // 1020, - // 1019, + 1029, + // 1028 NA, + 1027, + // 1026 NA, + // 1025 NA, + // 1024 NA, + // 1023 NA, + // 1022 NA, + // 1021 NA, + // 1020 NA, + // 1019 NA, // 1018, // 1017, - // 1016, + // 1016 NA, // 1015, - // 1014, - // 1013, - // 1012, - // 1011, + // 1014 NA, + 1013, + // 1012 NA, + // 1011 NA, // 1010, - // 1009, - // 1008, + // 1009 NA, + // 1008 NA, // 1007, // 1006, // 1005, - // 1004, - // 1003, - // 1002, + // 1004 NA, + // 1003 NA, + // 1002 NA, // 1001, // 1000, // 999 NA @@ -286,7 +440,7 @@ static int included_patches[] = { // 851 NA // 850 NA 849, - // 848, + 848, // 847, // 846 NA // 845, @@ -311,7 +465,7 @@ static int included_patches[] = { 826, // 825, // 824 NA - // 823, + 823, // 822, // 821, // 820, @@ -327,8 +481,8 @@ static int included_patches[] = { // 810, 809, // 808 NA - // 807, - // 806, + 807, + 806, // 805, // 804, 803, @@ -352,11 +506,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 +523,8 @@ static int included_patches[] = { 768, // 767, // 766 NA - // 765, - // 764, + 765, + 764, // 763 NA // 762 NA // 761 NA @@ -380,21 +534,21 @@ static int included_patches[] = { // 757 NA // 756 NA // 755, - // 754, + 754, 753, // 752, // 751 NA // 750 NA // 749, - // 748, - // 747, - // 746, - // 745, + 748, + 747, + 746, + 745, // 744 NA // 743, // 742, - // 741, - // 740, + 741, + 740, 739, // 738 NA // 737, @@ -425,7 +579,7 @@ static int included_patches[] = { 712, 711, 710, - // 709, + 709, // 708, 707, 706, @@ -434,19 +588,19 @@ static int included_patches[] = { // 703 NA 702, // 701 NA - // 700, + 700, 699, 698, - // 697, + 697, 696, 695, - // 694, - // 693, + 694, + 693, // 692 NA // 691 NA 690, - // 689, - // 688, + 689, + 688, // 687 NA 686, 685, @@ -472,7 +626,7 @@ static int included_patches[] = { 665, // 664 NA // 663 NA - // 662, + 662, // 661 NA 660, 659, @@ -491,7 +645,7 @@ static int included_patches[] = { 646, 645, // 644 NA - // 643, + 643, 642, // 641 NA 640, @@ -500,7 +654,7 @@ static int included_patches[] = { 637, 636, 635, - // 634, + 634, 633, // 632 NA 631, diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 2e20d48f90..5f9785a9a9 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -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 diff --git a/src/nvim/window.c b/src/nvim/window.c index 191cb04d75..e84d8df36b 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -3281,17 +3281,27 @@ void goto_tabpage_win(tabpage_T *tp, win_T *wp) } } -/* - * Move the current tab page to before tab page "nr". - */ +// Move the current tab page to after tab page "nr". void tabpage_move(int nr) { - int n = nr; - tabpage_T *tp; + int n = 1; + tabpage_T *tp; + tabpage_T *tp_dst; if (first_tabpage->tp_next == NULL) return; + for (tp = first_tabpage; tp->tp_next != NULL && n < nr; tp = tp->tp_next) { + ++n; + } + + if (tp == curtab || (nr > 0 && tp->tp_next != NULL + && tp->tp_next == curtab)) { + return; + } + + tp_dst = tp; + /* Remove the current tab page from the list of tab pages. */ if (curtab == first_tabpage) first_tabpage = curtab->tp_next; @@ -3304,15 +3314,13 @@ void tabpage_move(int nr) tp->tp_next = curtab->tp_next; } - /* Re-insert it at the specified position. */ - if (n <= 0) { + // Re-insert it at the specified position. + if (nr <= 0) { curtab->tp_next = first_tabpage; first_tabpage = curtab; } else { - for (tp = first_tabpage; tp->tp_next != NULL && n > 1; tp = tp->tp_next) - --n; - curtab->tp_next = tp->tp_next; - tp->tp_next = curtab; + curtab->tp_next = tp_dst->tp_next; + tp_dst->tp_next = curtab; } /* Need to redraw the tabline. Tab page contents doesn't change. */ |