diff options
31 files changed, 956 insertions, 481 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index c6ed0f0cdf..527a085d3e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,7 +153,11 @@ if(${INIT_FLAGS_NAME}) set(CMAKE_REQUIRED_FLAGS "${${INIT_FLAGS_NAME}}") endif() +# Include <string.h> because some toolchains define _FORTIFY_SOURCE=2 in +# internal header files, which should in turn be #included by <string.h>. check_c_source_compiles(" +#include <string.h> + #if defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE > 1 #error \"_FORTIFY_SOURCE > 1\" #endif diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 4a90c11734..cad8da6ffb 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -35,7 +35,7 @@ add_custom_command(OUTPUT ${GENERATED_HELP_TAGS} COMMAND "${PROJECT_BINARY_DIR}/bin/nvim" -u NONE -i NONE - -es + -e --headless -c "helptags ++t ." -c quit diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index 30b7dcaa4a..861f736426 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -1,4 +1,4 @@ -*change.txt* For Vim version 7.4. Last change: 2015 Feb 10 +*change.txt* For Vim version 7.4. Last change: 2015 Jun 25 VIM REFERENCE MANUAL by Bram Moolenaar @@ -366,14 +366,45 @@ Adding and subtracting ~ CTRL-A Add [count] to the number or alphabetic character at or after the cursor. + *v_CTRL-A* +{Visual}CTRL-A Add [count] to the number or alphabetic character in + the highlighted text. {not in Vi} + + *v_g_CTRL-A* +{Visual}g CTRL-A Add [count] to the number or alphabetic character in + the highlighted text. If several lines are + highlighted, each one will be incremented by an + additional [count] (so effectively creating a + [count] incrementing sequence). {not in Vi} + For Example, if you have this list of numbers: + 1. ~ + 1. ~ + 1. ~ + 1. ~ + Move to the second "1." and Visually select three + lines, pressing g CTRL-A results in: + 1. ~ + 2. ~ + 3. ~ + 4. ~ + *CTRL-X* CTRL-X Subtract [count] from the number or alphabetic character at or after the cursor. -The CTRL-A and CTRL-X commands can work for: -- signed and unsigned decimal numbers -- unsigned binary, octal and hexadecimal numbers -- alphabetic characters + *v_CTRL-X* +{Visual}CTRL-X Subtract [count] from the number or alphabetic + character in the highlighted text. {not in Vi} + + *v_g_CTRL-X* +{Visual}g CTRL-X Subtract [count] from the number or alphabetic + character in the highlighted text. If several lines + are highlighted, each value will be decremented by an + additional [count] (so effectively creating a [count] + decrementing sequence). {not in Vi} + +The CTRL-A and CTRL-X commands work for (signed) decimal numbers, unsigned +binary/octal/hexadecimal numbers and alphabetic characters. This depends on the 'nrformats' option: - When 'nrformats' includes "bin", Vim assumes numbers starting with '0b' or diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index bb7ca77de7..a5f8660691 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 7.4. Last change: 2015 Nov 30 +*eval.txt* For Vim version 7.4. Last change: 2015 Jun 26 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1003,7 +1003,7 @@ function. Example: > -string *string* *expr-string* *E114* +string *string* *String* *expr-string* *E114* ------ "string" string constant *expr-quote* @@ -2028,7 +2028,7 @@ split( {expr} [, {pat} [, {keepempty}]]) sqrt( {expr}) Float square root of {expr} str2float( {expr}) Float convert String to Float str2nr( {expr} [, {base}]) Number convert String to Number -strchars( {expr}) Number character length of the String {expr} +strchars( {expr} [, {skipcc}]) Number character length of the String {expr} strdisplaywidth( {expr} [, {col}]) Number display length of the String {expr} strftime( {format}[, {time}]) String time in specified format stridx( {haystack}, {needle}[, {start}]) @@ -6157,15 +6157,17 @@ str2nr( {expr} [, {base}]) *str2nr()* Text after the number is silently ignored. -strchars({expr}) *strchars()* +strchars({expr} [, {skipcc}]) *strchars()* The result is a Number, which is the number of characters - String {expr} occupies. Composing characters are counted - separately. + in String {expr}. + When {skipcc} is omitted or zero, composing characters are + counted separately. + When {skipcc} set to 1, Composing characters are ignored. Also see |strlen()|, |strdisplaywidth()| and |strwidth()|. strdisplaywidth({expr}[, {col}]) *strdisplaywidth()* The result is a Number, which is the number of display cells - String {expr} occupies on the screen when it starts a {col}. + String {expr} occupies on the screen when it starts at {col}. When {col} is omitted zero is used. Otherwise it is the screen column where to start. This matters for Tab characters. @@ -6231,15 +6233,11 @@ string({expr}) Return {expr} converted to a String. If {expr} is a Number, *strlen()* strlen({expr}) The result is a Number, which is the length of the String {expr} in bytes. - If you want to count the number of multi-byte characters (not - counting composing characters) use something like this: > - - :let len = strlen(substitute(str, ".", "x", "g")) -< If the argument is a Number it is first converted to a String. For other types an error is given. - Also see |len()|, |strchars()|, |strdisplaywidth()| and - |strwidth()|. + If you want to count the number of multi-byte characters use + |strchars()|. + Also see |len()|, |strdisplaywidth()| and |strwidth()|. strpart({src}, {start}[, {len}]) *strpart()* The result is a String, which is part of {src}, starting from diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt index cb01d5fe92..f17410d1dc 100644 --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -1,4 +1,4 @@ -*insert.txt* For Vim version 7.4. Last change: 2015 May 22 +*insert.txt* For Vim version 7.4. Last change: 2015 Jun 20 VIM REFERENCE MANUAL by Bram Moolenaar @@ -78,8 +78,8 @@ CTRL-W Delete the word before the cursor (see |i_backspacing| about |word-motions|, for the definition of a word. *i_CTRL-U* CTRL-U Delete all entered characters before the cursor in the current - line. If there are no newly entereed characters and - 'backspace'is not empty, delete all characters before the + line. If there are no newly entered characters and + 'backspace' is not empty, delete all characters before the cursor in the current line. See |i_backspacing| about joining lines. *i_CTRL-I* *i_<Tab>* *i_Tab* diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 51bfc12f9d..e875be6218 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -50,8 +50,8 @@ achieve special effects. These options come in three forms: :se[t] {option}&vim Reset option to its Vim default value. :se[t] all& Set all options, except terminal options, to their - default value. The values of 'term', 'lines' and - 'columns' are not changed. + default value. The values of 'lines' and 'columns' + are not changed. *:set-args* *E487* *E521* :se[t] {option}={value} or @@ -772,14 +772,13 @@ A jump table for the options with a short description can be found at |Q_op|. putting a ":gui" command in the gvimrc file, before where the value of 'background' is used (e.g., before ":syntax on"). - For Windows the default is "dark". - For other systems "dark" is used when 'term' is "linux", - "screen.linux", "cygwin" or "putty", or $COLORFGBG suggests a dark - background. Otherwise the default is "light". + For Windows the default is "dark". "dark" should be used if $COLORFGBG + suggests a dark background (not yet implemented). Otherwise the default + is "light". Normally this option would be set in the vimrc file. Possibly depending on the terminal name. Example: > - :if &term == "xterm" + :if $TERM == "xterm" : set background=dark :endif < When this option is set, the default settings for the highlight groups @@ -6177,6 +6176,7 @@ A jump table for the options with a short description can be found at |Q_op|. split If included, split the current window before loading a buffer for a |quickfix| command that display errors. Otherwise: do not split, use current window. + vsplit Just like "split" but split vertically. newtab Like "split", but open a new tab page. Overrules "split" when both are present. @@ -6370,17 +6370,7 @@ A jump table for the options with a short description can be found at |Q_op|. mapping which should not change the tagstack. *'term'* *E529* *E530* *E531* -'term' string (default is $TERM, if that fails: - in the GUI: "builtin_gui" - on Mac: "mac-ansi" - on Unix: "ansi" - on Windows: "win32") - global - Name of the terminal. Used for choosing the terminal control - characters. Environment variables are expanded |:set_env|. - For example: > - :set term=$TERM -< See |termcap|. +'term' Removed. |vim-differences| {Nvim} *'termbidi'* *'tbidi'* *'notermbidi'* *'notbidi'* @@ -6560,9 +6550,7 @@ A jump table for the options with a short description can be found at |Q_op|. 'ttyscroll' 'tsl' Removed. |vim-differences| {Nvim} *'ttytype'* *'tty'* -'ttytype' 'tty' string (default from $TERM) - global - Alias for 'term', see above. +'ttytype' 'tty' Alias for 'term'. Removed. |vim-differences| {Nvim} *'undodir'* *'udir'* *E926* 'undodir' 'udir' string (default "$XDG_DATA_HOME/nvim/undo") diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index e2473976eb..ca02050cb8 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -217,7 +217,7 @@ argument. :set to display option values. When 'verbose' is non-zero messages are printed (for debugging, to stderr). - 'term' and $TERM are not used. + $TERM is not used. If Vim appears to be stuck try typing "qa!<Enter>". You don't get a prompt thus you can't see Vim is waiting for you to type something. @@ -354,13 +354,10 @@ argument. At startup, Vim checks environment variables and files and sets values accordingly. Vim proceeds in this order: -1. Set the 'shell' and 'term' option *SHELL* *COMSPEC* *TERM* +1. Set the 'shell' option *SHELL* *COMSPEC* *TERM* The environment variable SHELL, if it exists, is used to set the 'shell' option. On Windows, the COMSPEC variable is used if SHELL is not set. - The environment variable TERM, if it exists, is used to set the 'term' - option. However, 'term' will change later when starting the GUI (step - 8 below). 2. Process the arguments The options and file names from the command that start Vim are @@ -705,8 +702,8 @@ vimrc file. These commands will write ":map" and ":set" commands to a file, in such a way that when these commands are executed, the current key mappings and options will be set to the same values. The options 'columns', 'endofline', -'fileformat', 'lines', 'modified', 'scroll', and 'term' are not included, -because these are terminal or file dependent. +'fileformat', 'lines', 'modified', and 'scroll' are not included, because +these are terminal or file dependent. Note that the options 'binary', 'paste' and 'readonly' are included, this might not always be what you want. diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 6609a96e9e..f76e901b9a 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -207,6 +207,7 @@ Other options: 'shelltype' 'shortname' 'swapsync' + 'term' 'termencoding' (Vim 7.4.852 also removed this for Windows) 'textauto' 'textmode' @@ -214,6 +215,8 @@ Other options: 'toolbariconsize' 'ttybuiltin' 'ttymouse' + 'ttyscroll' + 'ttytype' 'weirdinvert' Other commands: diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 34e24712cd..9806623433 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1601,21 +1601,28 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit) col = 0; if (options & GETF_SWITCH) { - /* If 'switchbuf' contains "useopen": jump to first window containing - * "buf" if one exists */ - if (swb_flags & SWB_USEOPEN) + // If 'switchbuf' contains "useopen": jump to first window containing + // "buf" if one exists + if (swb_flags & SWB_USEOPEN) { wp = buf_jump_open_win(buf); - /* If 'switchbuf' contains "usetab": jump to first window in any tab - * page containing "buf" if one exists */ - if (wp == NULL && (swb_flags & SWB_USETAB)) + } + + // If 'switchbuf' contains "usetab": jump to first window in any tab + // page containing "buf" if one exists + if (wp == NULL && (swb_flags & SWB_USETAB)) { wp = buf_jump_open_tab(buf); - /* If 'switchbuf' contains "split" or "newtab" and the current buffer - * isn't empty: open new window */ - if (wp == NULL && (swb_flags & (SWB_SPLIT | SWB_NEWTAB)) && !bufempty()) { - if (swb_flags & SWB_NEWTAB) /* Open in a new tab */ + } + + // If 'switchbuf' contains "split", "vsplit" or "newtab" and the + // current buffer isn't empty: open new tab or window + if (wp == NULL && (swb_flags & (SWB_VSPLIT | SWB_SPLIT | SWB_NEWTAB)) + && !bufempty()) { + if (swb_flags & SWB_NEWTAB) { tabpage_new(); - else if (win_split(0, 0) == FAIL) /* Open in a new window */ + } else if (win_split(0, (swb_flags & SWB_VSPLIT) ? WSP_VERT : 0) + == FAIL) { return FAIL; + } RESET_BINDING(curwin); } } @@ -3895,6 +3902,11 @@ void get_rel_pos(win_T *wp, char_u *buf, int buflen) above = wp->w_topline - 1; above += diff_check_fill(wp, wp->w_topline) - wp->w_topfill; + if (wp->w_topline == 1 && wp->w_topfill >= 1) { + // All buffer lines are displayed and there is an indication + // of filler lines, that can be considered seeing all lines. + above = 0; + } below = wp->w_buffer->b_ml.ml_line_count - wp->w_botline + 1; if (below <= 0) STRLCPY(buf, (above == 0 ? _("All") : _("Bot")), buflen); diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 9a0e1440cc..4e329b5cd8 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -799,32 +799,35 @@ unsigned int win_linetabsize(win_T *wp, char_u *line, colnr_T len) return (unsigned int)col; } -/// Return TRUE if 'c' is a normal identifier character: -/// +/// Check that "c" is a normal identifier character: /// Letters and characters from the 'isident' option. /// -/// @param c -/// -/// @return TRUE if 'c' is a normal identifier character. -int vim_isIDc(int c) +/// @param c character to check +bool vim_isIDc(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return c > 0 && c < 0x100 && (chartab[c] & CT_ID_CHAR); } -/// return TRUE if 'c' is a keyword character: Letters and characters from -/// 'iskeyword' option for current buffer. -/// +/// Check that "c" is a keyword character: +/// Letters and characters from 'iskeyword' option for current buffer. /// For multi-byte characters mb_get_class() is used (builtin rules). /// -/// @param c -/// -/// @return TRUE if 'c' is a keyword character. -int vim_iswordc(int c) +/// @param c character to check +bool vim_iswordc(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return vim_iswordc_buf(c, curbuf); } -int vim_iswordc_buf(int c, buf_T *buf) +/// Check that "c" is a keyword character: +/// Letters and characters from 'iskeyword' option for given buffer. +/// For multi-byte characters mb_get_class() is used (builtin rules). +/// +/// @param c character to check +/// @param buf buffer whose keywords to use +bool vim_iswordc_buf(int c, buf_T *buf) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(2) { if (c >= 0x100) { if (enc_dbcs != 0) { @@ -840,10 +843,11 @@ int vim_iswordc_buf(int c, buf_T *buf) /// Just like vim_iswordc() but uses a pointer to the (multi-byte) character. /// -/// @param p +/// @param p pointer to the multi-byte character /// -/// @return TRUE if 'p' points to a keyword character. -int vim_iswordp(char_u *p) +/// @return true if "p" points to a keyword character. +bool vim_iswordp(char_u *p) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { if (has_mbyte && (MB_BYTE2LEN(*p) > 1)) { return mb_get_class(p) >= 2; @@ -851,7 +855,15 @@ int vim_iswordp(char_u *p) return GET_CHARTAB(curbuf, *p) != 0; } -int vim_iswordp_buf(char_u *p, buf_T *buf) +/// Just like vim_iswordc_buf() but uses a pointer to the (multi-byte) +/// character. +/// +/// @param p pointer to the multi-byte character +/// @param buf buffer whose keywords to use +/// +/// @return true if "p" points to a keyword character. +bool vim_iswordp_buf(char_u *p, buf_T *buf) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { if (has_mbyte && (MB_BYTE2LEN(*p) > 1)) { return mb_get_class(p) >= 2; @@ -859,26 +871,24 @@ int vim_iswordp_buf(char_u *p, buf_T *buf) return GET_CHARTAB(buf, *p) != 0; } -/// return TRUE if 'c' is a valid file-name character +/// Check that "c" is a valid file-name character. /// Assume characters above 0x100 are valid (multi-byte). /// -/// @param c -/// -/// @return TRUE if 'c' is a valid file name character. -int vim_isfilec(int c) +/// @param c character to check +bool vim_isfilec(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return c >= 0x100 || (c > 0 && (chartab[c] & CT_FNAME_CHAR)); } -/// return TRUE if 'c' is a valid file-name character or a wildcard character +/// Check that "c" is a valid file-name character or a wildcard character /// Assume characters above 0x100 are valid (multi-byte). /// Explicitly interpret ']' as a wildcard character as path_has_wildcard("]") /// returns false. /// -/// @param c -/// -/// @return TRUE if 'c' is a valid file-name character or wildcard character. -int vim_isfilec_or_wc(int c) +/// @param c character to check +bool vim_isfilec_or_wc(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { char_u buf[2]; buf[0] = (char_u)c; @@ -886,14 +896,12 @@ int vim_isfilec_or_wc(int c) return vim_isfilec(c) || c == ']' || path_has_wildcard(buf); } -/// return TRUE if 'c' is a printable character -/// Assume characters above 0x100 are printable (multi-byte), except for -/// Unicode. -/// -/// @param c +/// Check that "c" is a printable character. +/// Assume characters above 0x100 are printable for double-byte encodings. /// -/// @return TRUE if 'c' a printable character. -int vim_isprintc(int c) +/// @param c character to check +bool vim_isprintc(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { if (enc_utf8 && (c >= 0x100)) { return utf_printable(c); @@ -901,16 +909,17 @@ int vim_isprintc(int c) return c >= 0x100 || (c > 0 && (chartab[c] & CT_PRINT_CHAR)); } -/// Strict version of vim_isprintc(c), don't return TRUE if "c" is the head +/// Strict version of vim_isprintc(c), don't return true if "c" is the head /// byte of a double-byte character. /// -/// @param c +/// @param c character to check /// -/// @return TRUE if 'c' is a printable character. -int vim_isprintc_strict(int c) +/// @return true if "c" is a printable character. +bool vim_isprintc_strict(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { if ((enc_dbcs != 0) && (c < 0x100) && (MB_BYTE2LEN(c) > 1)) { - return FALSE; + return false; } if (enc_utf8 && (c >= 0x100)) { @@ -921,7 +930,7 @@ int vim_isprintc_strict(int c) /// like chartabsize(), but also check for line breaks on the screen /// -/// @param line +/// @param line /// @param s /// @param col /// @@ -1144,35 +1153,33 @@ static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp) return n; } -/// Return TRUE if virtual column "vcol" is in the rightmost column of window -/// "wp". +/// Check that virtual column "vcol" is in the rightmost column of window "wp". /// -/// @param wp -/// @param vcol -/// -/// @return TRUE if the virtual column is in the rightmost column. -int in_win_border(win_T *wp, colnr_T vcol) +/// @param wp window +/// @param vcol column number +bool in_win_border(win_T *wp, colnr_T vcol) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) { int width1; // width of first line (after line number) int width2; // width of further lines if (wp->w_width == 0) { // there is no border - return FALSE; + return false; } width1 = wp->w_width - win_col_off(wp); if ((int)vcol < width1 - 1) { - return FALSE; + return false; } if ((int)vcol == width1 - 1) { - return TRUE; + return true; } width2 = width1 + win_col_off2(wp); if (width2 <= 0) { - return FALSE; + return false; } return (vcol - width1) % width2 == width2 - 1; } @@ -1571,10 +1578,14 @@ static char_u latin1lower[257] = "\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee" "\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"; -int vim_islower(int c) +/// Check that the character is lower-case +/// +/// @param c character to check +bool vim_islower(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { if (c <= '@') { - return FALSE; + return false; } if (c >= 0x80) { @@ -1588,7 +1599,7 @@ int vim_islower(int c) } // islower() can't handle these chars and may crash - return FALSE; + return false; } if (enc_latin1like) { @@ -1598,10 +1609,14 @@ int vim_islower(int c) return islower(c); } -int vim_isupper(int c) +/// Check that the character is upper-case +/// +/// @param c character to check +bool vim_isupper(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { if (c <= '@') { - return FALSE; + return false; } if (c >= 0x80) { @@ -1614,8 +1629,8 @@ int vim_isupper(int c) return iswupper(c); } - // islower() can't handle these chars and may crash - return FALSE; + // isupper() can't handle these chars and may crash + return false; } if (enc_latin1like) { @@ -1744,12 +1759,10 @@ long getdigits_long(char_u **pp) return (long)number; } -/// Return TRUE if "lbuf" is empty or only contains blanks. -/// -/// @param lbuf +/// Check that "lbuf" is empty or only contains blanks. /// -/// @return TRUE if `lbuf` is empty or only contains blanks. -int vim_isblankline(char_u *lbuf) +/// @param lbuf line buffer to check +bool vim_isblankline(char_u *lbuf) { char_u *p = skipwhite(lbuf); return *p == NUL || *p == '\r' || *p == '\n'; @@ -1922,8 +1935,8 @@ int hex2nr(int c) return c - '0'; } -/// Return true if "str" starts with a backslash that should be removed. -/// For WIN32 this is only done when the character after the +/// Check that "str" starts with a backslash that should be removed. +/// For Windows this is only done when the character after the /// backslash is not a normal file name character. /// '$' is a valid file name character, we don't remove the backslash before /// it. This means it is not possible to use an environment variable after a @@ -1934,10 +1947,9 @@ int hex2nr(int c) /// character, assume that all multi-byte characters are valid file name /// characters. /// -/// @param str -/// -/// @return true if `str` starts with a backslash that should be removed. +/// @param str file path string to check bool rem_backslash(const char_u *str) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { #ifdef BACKSLASH_IN_FILENAME return str[0] == '\\' diff --git a/src/nvim/diff.c b/src/nvim/diff.c index ce79158050..e06ffd0bbc 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -763,8 +763,8 @@ void ex_diffupdate(exarg_T *eap) // Make a difference between the first buffer and every other. for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new) { buf_T *buf = curtab->tp_diffbuf[idx_new]; - if (buf == NULL) { - continue; + if (buf == NULL || buf->b_ml.ml_mfp == NULL) { + continue; // skip buffer that isn't loaded } if (diff_write(buf, tmp_new) == FAIL) { @@ -1057,27 +1057,28 @@ void diff_win_options(win_T *wp, int addbuf) newFoldLevel(); curwin = old_curwin; - wp->w_p_diff = TRUE; - // Use 'scrollbind' and 'cursorbind' when available - if (!wp->w_p_diff_saved) { + if (!wp->w_p_diff) { wp->w_p_scb_save = wp->w_p_scb; } wp->w_p_scb = TRUE; - if (!wp->w_p_diff_saved) { + if (!wp->w_p_diff) { wp->w_p_crb_save = wp->w_p_crb; } wp->w_p_crb = TRUE; - if (!wp->w_p_diff_saved) { + if (!wp->w_p_diff) { wp->w_p_wrap_save = wp->w_p_wrap; } wp->w_p_wrap = FALSE; curwin = wp; curbuf = curwin->w_buffer; - if (!wp->w_p_diff_saved) { + if (!wp->w_p_diff) { + if (wp->w_p_diff_saved) { + free_string_option(wp->w_p_fdm_save); + } wp->w_p_fdm_save = vim_strsave(wp->w_p_fdm); } set_string_option_direct((char_u *)"fdm", -1, (char_u *)"diff", @@ -1085,7 +1086,7 @@ void diff_win_options(win_T *wp, int addbuf) curwin = old_curwin; curbuf = curwin->w_buffer; - if (!wp->w_p_diff_saved) { + if (!wp->w_p_diff) { wp->w_p_fdc_save = wp->w_p_fdc; wp->w_p_fen_save = wp->w_p_fen; wp->w_p_fdl_save = wp->w_p_fdl; @@ -1104,6 +1105,8 @@ void diff_win_options(win_T *wp, int addbuf) // Saved the current values, to be restored in ex_diffoff(). wp->w_p_diff_saved = TRUE; + wp->w_p_diff = true; + if (addbuf) { diff_buf_add(wp->w_buffer); } @@ -1116,68 +1119,50 @@ void diff_win_options(win_T *wp, int addbuf) /// @param eap void ex_diffoff(exarg_T *eap) { - win_T *old_curwin = curwin; - int diffwin = FALSE; + int diffwin = false; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (eap->forceit ? wp->w_p_diff : (wp == curwin)) { - // Set 'diff', 'scrollbind' off and 'wrap' on. If option values - // were saved in diff_win_options() restore them. - wp->w_p_diff = FALSE; - - if (wp->w_p_scb) { - wp->w_p_scb = wp->w_p_diff_saved ? wp->w_p_scb_save : FALSE; - } - - if (wp->w_p_crb) { - wp->w_p_crb = wp->w_p_diff_saved ? wp->w_p_crb_save : FALSE; - } - - if (!wp->w_p_wrap) { - wp->w_p_wrap = wp->w_p_diff_saved ? wp->w_p_wrap_save : TRUE; - } - curwin = wp; - curbuf = curwin->w_buffer; + // Set 'diff' off. If option values were saved in + // diff_win_options(), restore the ones whose settings seem to have + // been left over from diff mode. + wp->w_p_diff = false; if (wp->w_p_diff_saved) { - free_string_option(wp->w_p_fdm); - wp->w_p_fdm = wp->w_p_fdm_save; - wp->w_p_fdm_save = empty_option; - } else { - set_string_option_direct((char_u *)"fdm", -1, - (char_u *)"manual", OPT_LOCAL | OPT_FREE, 0); - } - curwin = old_curwin; - curbuf = curwin->w_buffer; + if (wp->w_p_scb) { + wp->w_p_scb = wp->w_p_scb_save; + } - if (wp->w_p_fdc == diff_foldcolumn) { - wp->w_p_fdc = wp->w_p_diff_saved ? wp->w_p_fdc_save : 0; - } + if (wp->w_p_crb) { + wp->w_p_crb = wp->w_p_crb_save; + } - if ((wp->w_p_fdl == 0) - && wp->w_p_diff_saved) { - wp->w_p_fdl = wp->w_p_fdl_save; - } + if (!wp->w_p_wrap) { + wp->w_p_wrap = wp->w_p_wrap_save; + } - if (wp->w_p_fen) { + free_string_option(wp->w_p_fdm); + wp->w_p_fdm = vim_strsave(wp->w_p_fdm_save); + if (wp->w_p_fdc == diff_foldcolumn) { + wp->w_p_fdc = wp->w_p_fdc_save; + } + if (wp->w_p_fdl == 0) { + wp->w_p_fdl = wp->w_p_fdl_save; + } // Only restore 'foldenable' when 'foldmethod' is not // "manual", otherwise we continue to show the diff folds. - if (foldmethodIsManual(wp) || !wp->w_p_diff_saved) { - wp->w_p_fen = FALSE; - } else { - wp->w_p_fen = wp->w_p_fen_save; + if (wp->w_p_fen) { + wp->w_p_fen = foldmethodIsManual(wp) ? false : wp->w_p_fen_save; } - } - foldUpdateAll(wp); + foldUpdateAll(wp); - // make sure topline is not halfway through a fold - changed_window_setting_win(wp); + // make sure topline is not halfway through a fold + changed_window_setting_win(wp); + } // Note: 'sbo' is not restored, it's a global option. diff_buf_adjust(wp); - - wp->w_p_diff_saved = FALSE; } diffwin |= wp->w_p_diff; } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a9af7d94c1..b9b913a969 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1700,12 +1700,13 @@ static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first) } error = TRUE; } else { - if (tofree != NULL) + if (tofree != NULL) { name = tofree; - if (get_var_tv(name, len, &tv, TRUE, FALSE) == FAIL) - error = TRUE; - else { - /* handle d.key, l[idx], f(expr) */ + } + if (get_var_tv(name, len, &tv, NULL, true, false) == FAIL) { + error = true; + } else { + // handle d.key, l[idx], f(expr) arg_subsc = arg; if (handle_subscript(&arg, &tv, TRUE, TRUE) == FAIL) error = TRUE; @@ -2176,10 +2177,10 @@ get_lval ( if (len == -1) clear_tv(&var1); break; - } - /* existing variable, need to check if it can be changed */ - else if (var_check_ro(lp->ll_di->di_flags, name)) + } else if (var_check_ro(lp->ll_di->di_flags, name, false)) { + // existing variable, need to check if it can be changed return NULL; + } if (len == -1) clear_tv(&var1); @@ -2274,11 +2275,16 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch if (op != NULL && *op != '=') { typval_T tv; - /* handle +=, -= and .= */ + // handle +=, -= and .= + di = NULL; if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name), - &tv, TRUE, FALSE) == OK) { - if (tv_op(&tv, rettv, op) == OK) - set_var(lp->ll_name, &tv, FALSE); + &tv, &di, true, false) == OK) { + if ((di == NULL + || (!var_check_ro(di->di_flags, lp->ll_name, false) && + !tv_check_lock(di->di_tv.v_lock, lp->ll_name, false))) + && tv_op(&tv, rettv, op) == OK) { + set_var(lp->ll_name, &tv, false); + } clear_tv(&tv); } } else @@ -2286,16 +2292,17 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch *endp = cc; } } else if (tv_check_lock(lp->ll_newkey == NULL - ? lp->ll_tv->v_lock - : lp->ll_tv->vval.v_dict->dv_lock, lp->ll_name)) - ; - else if (lp->ll_range) { + ? lp->ll_tv->v_lock + : lp->ll_tv->vval.v_dict->dv_lock, + lp->ll_name, false)) { + } else if (lp->ll_range) { listitem_T *ll_li = lp->ll_li; int ll_n1 = lp->ll_n1; // Check whether any of the list items is locked - for (listitem_T *ri = rettv->vval.v_list->lv_first; ri != NULL && ll_li != NULL; ) { - if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name)) { + for (listitem_T *ri = rettv->vval.v_list->lv_first; + ri != NULL && ll_li != NULL; ) { + if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name, false)) { return; } ri = ri->li_next; @@ -2891,9 +2898,9 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) ret = FAIL; *name_end = cc; } else if ((lp->ll_list != NULL - && tv_check_lock(lp->ll_list->lv_lock, lp->ll_name)) + && tv_check_lock(lp->ll_list->lv_lock, lp->ll_name, false)) || (lp->ll_dict != NULL - && tv_check_lock(lp->ll_dict->dv_lock, lp->ll_name))) { + && tv_check_lock(lp->ll_dict->dv_lock, lp->ll_name, false))) { return FAIL; } else if (lp->ll_range) { listitem_T *li; @@ -2902,7 +2909,7 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) { li = ll_li->li_next; - if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name)) { + if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name, false)) { return false; } ll_li = li; @@ -2975,9 +2982,9 @@ int do_unlet(char_u *name, int forceit) hi = hash_find(ht, varname); if (!HASHITEM_EMPTY(hi)) { di = HI2DI(hi); - if (var_check_fixed(di->di_flags, name) - || var_check_ro(di->di_flags, name) - || tv_check_lock(d->dv_lock, name)) { + if (var_check_fixed(di->di_flags, name, false) + || var_check_ro(di->di_flags, name, false) + || tv_check_lock(d->dv_lock, name, false)) { return FAIL; } typval_T oldtv; @@ -3045,12 +3052,13 @@ static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock) li = li->li_next; ++lp->ll_n1; } - } else if (lp->ll_list != NULL) - /* (un)lock a List item. */ + } else if (lp->ll_list != NULL) { + // (un)lock a List item. item_lock(&lp->ll_li->li_tv, deep, lock); - else - /* un(lock) a Dictionary item. */ + } else { + // (un)lock a Dictionary item. item_lock(&lp->ll_di->di_tv, deep, lock); + } return ret; } @@ -4239,7 +4247,7 @@ static int eval7( ret = FAIL; } } else if (evaluate) { - ret = get_var_tv(s, len, rettv, true, false); + ret = get_var_tv(s, len, rettv, NULL, true, false); } else { ret = OK; } @@ -7337,7 +7345,7 @@ static struct fst { { "sqrt", 1, 1, f_sqrt }, { "str2float", 1, 1, f_str2float }, { "str2nr", 1, 2, f_str2nr }, - { "strchars", 1, 1, f_strchars }, + { "strchars", 1, 2, f_strchars }, { "strdisplaywidth", 1, 2, f_strdisplaywidth }, { "strftime", 1, 2, f_strftime }, { "stridx", 2, 3, f_stridx }, @@ -7855,7 +7863,8 @@ static void f_add(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = 1; /* Default: Failed */ if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, (char_u *)_("add() argument"))) { + && !tv_check_lock(l->lv_lock, + (char_u *)N_("add() argument"), true)) { list_append_tv(l, &argvars[1]); copy_tv(&argvars[0], rettv); } @@ -9130,9 +9139,10 @@ static void f_exists(typval_T *argvars, typval_T *rettv) name = p; len = get_name_len(&p, &tofree, TRUE, FALSE); if (len > 0) { - if (tofree != NULL) + if (tofree != NULL) { name = tofree; - n = (get_var_tv(name, len, &tv, FALSE, TRUE) == OK); + } + n = (get_var_tv(name, len, &tv, NULL, false, true) == OK); if (n) { /* handle d.key, l[idx], f(expr) */ n = (handle_subscript(&p, &tv, TRUE, FALSE) == OK); @@ -9230,7 +9240,7 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action) hashitem_T *hi2; int todo; bool watched = is_watched(d1); - char *arg_errmsg = N_("extend() argument"); + char_u *arg_errmsg = (char_u *)N_("extend() argument"); todo = (int)d2->dv_hashtab.ht_used; for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) { @@ -9264,8 +9274,8 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action) } else if (*action == 'f' && HI2DI(hi2) != di1) { typval_T oldtv; - if (tv_check_lock(di1->di_tv.v_lock, (char_u *)_(arg_errmsg)) - || var_check_ro(di1->di_flags, (char_u *)_(arg_errmsg))) { + if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, true) + || var_check_ro(di1->di_flags, arg_errmsg, true)) { break; } @@ -9291,7 +9301,7 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action) */ static void f_extend(typval_T *argvars, typval_T *rettv) { - char *arg_errmsg = N_("extend() argument"); + char_u *arg_errmsg = (char_u *)N_("extend() argument"); if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) { list_T *l1, *l2; @@ -9301,7 +9311,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv) l1 = argvars[0].vval.v_list; l2 = argvars[1].vval.v_list; - if (l1 != NULL && !tv_check_lock(l1->lv_lock, (char_u *)_(arg_errmsg)) + if (l1 != NULL && !tv_check_lock(l1->lv_lock, arg_errmsg, true) && l2 != NULL) { if (argvars[2].v_type != VAR_UNKNOWN) { before = get_tv_number_chk(&argvars[2], &error); @@ -9331,7 +9341,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv) d1 = argvars[0].vval.v_dict; d2 = argvars[1].vval.v_dict; - if (d1 != NULL && !tv_check_lock(d1->dv_lock, (char_u *)_(arg_errmsg)) + if (d1 != NULL && !tv_check_lock(d1->dv_lock, arg_errmsg, true) && d2 != NULL) { /* Check the third argument. */ if (argvars[2].v_type != VAR_UNKNOWN) { @@ -9477,19 +9487,19 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) int rem; int todo; char_u *ermsg = (char_u *)(map ? "map()" : "filter()"); - char *arg_errmsg = (map ? N_("map() argument") - : N_("filter() argument")); + char_u *arg_errmsg = (char_u *)(map ? N_("map() argument") + : N_("filter() argument")); int save_did_emsg; int idx = 0; if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) == NULL - || (!map && tv_check_lock(l->lv_lock, (char_u *)_(arg_errmsg)))) { + || (!map && tv_check_lock(l->lv_lock, arg_errmsg, true))) { return; } } else if (argvars[0].v_type == VAR_DICT) { if ((d = argvars[0].vval.v_dict) == NULL - || (!map && tv_check_lock(d->dv_lock, (char_u *)_(arg_errmsg)))) { + || (!map && tv_check_lock(d->dv_lock, arg_errmsg, true))) { return; } } else { @@ -9523,8 +9533,8 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) di = HI2DI(hi); if (map - && (tv_check_lock(di->di_tv.v_lock, (char_u *)_(arg_errmsg)) - || var_check_ro(di->di_flags, (char_u *)_(arg_errmsg)))) { + && (tv_check_lock(di->di_tv.v_lock, arg_errmsg, true) + || var_check_ro(di->di_flags, arg_errmsg, true))) { break; } @@ -9534,8 +9544,8 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) if (r == FAIL || did_emsg) break; if (!map && rem) { - if (var_check_fixed(di->di_flags, (char_u *)_(arg_errmsg)) - || var_check_ro(di->di_flags, (char_u *)_(arg_errmsg))) { + if (var_check_fixed(di->di_flags, arg_errmsg, true) + || var_check_ro(di->di_flags, arg_errmsg, true)) { break; } dictitem_remove(d, di); @@ -9547,7 +9557,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) vimvars[VV_KEY].vv_type = VAR_NUMBER; for (li = l->lv_first; li != NULL; li = nli) { - if (map && tv_check_lock(li->li_tv.v_lock, (char_u *)_(arg_errmsg))) { + if (map && tv_check_lock(li->li_tv.v_lock, arg_errmsg, true)) { break; } nli = li->li_next; @@ -10583,9 +10593,10 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv) varname = get_tv_string_chk(&argvars[1]); tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); if (tp != NULL && varname != NULL) { - /* Set tp to be our tabpage, temporarily. Also set the window to the - * first window in the tabpage, otherwise the window is not valid. */ - if (switch_win(&oldcurwin, &oldtabpage, tp->tp_firstwin, tp, TRUE) == OK) { + // Set tp to be our tabpage, temporarily. Also set the window to the + // first window in the tabpage, otherwise the window is not valid. + win_T *window = tp->tp_firstwin == NULL ? firstwin : tp->tp_firstwin; + if (switch_win(&oldcurwin, &oldtabpage, window, tp, true) == OK) { // look up the variable // Let gettabvar({nr}, "") return the "t:" dictionary. v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', varname, FALSE); @@ -11442,14 +11453,18 @@ static void f_insert(typval_T *argvars, typval_T *rettv) list_T *l; int error = FALSE; - if (argvars[0].v_type != VAR_LIST) + if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "insert()"); - else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, (char_u *)_("insert() argument"))) { - if (argvars[2].v_type != VAR_UNKNOWN) + } else if ((l = argvars[0].vval.v_list) != NULL + && !tv_check_lock(l->lv_lock, + (char_u *)N_("insert() argument"), true)) { + if (argvars[2].v_type != VAR_UNKNOWN) { before = get_tv_number_chk(&argvars[2], &error); - if (error) - return; /* type error; errmsg already given */ + } + if (error) { + // type error; errmsg already given + return; + } if (before == l->lv_len) item = NULL; @@ -13903,20 +13918,20 @@ static void f_remove(typval_T *argvars, typval_T *rettv) char_u *key; dict_T *d; dictitem_T *di; - char *arg_errmsg = N_("remove() argument"); + char_u *arg_errmsg = (char_u *)N_("remove() argument"); if (argvars[0].v_type == VAR_DICT) { - if (argvars[2].v_type != VAR_UNKNOWN) + if (argvars[2].v_type != VAR_UNKNOWN) { EMSG2(_(e_toomanyarg), "remove()"); - else if ((d = argvars[0].vval.v_dict) != NULL - && !tv_check_lock(d->dv_lock, (char_u *)_(arg_errmsg))) { + } else if ((d = argvars[0].vval.v_dict) != NULL + && !tv_check_lock(d->dv_lock, arg_errmsg, true)) { key = get_tv_string_chk(&argvars[1]); if (key != NULL) { di = dict_find(d, key, -1); if (di == NULL) { EMSG2(_(e_dictkey), key); - } else if (!var_check_fixed(di->di_flags, (char_u *)_(arg_errmsg)) - && !var_check_ro(di->di_flags, (char_u *)_(arg_errmsg))) { + } else if (!var_check_fixed(di->di_flags, arg_errmsg, true) + && !var_check_ro(di->di_flags, arg_errmsg, true)) { *rettv = di->di_tv; init_tv(&di->di_tv); dictitem_remove(d, di); @@ -13926,11 +13941,11 @@ static void f_remove(typval_T *argvars, typval_T *rettv) } } } - } else if (argvars[0].v_type != VAR_LIST) + } else if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listdictarg), "remove()"); - else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, (char_u *)_(arg_errmsg))) { - int error = FALSE; + } else if ((l = argvars[0].vval.v_list) != NULL + && !tv_check_lock(l->lv_lock, arg_errmsg, true)) { + int error = (int)false; idx = get_tv_number_chk(&argvars[1], &error); if (error) @@ -14204,10 +14219,11 @@ static void f_reverse(typval_T *argvars, typval_T *rettv) list_T *l; listitem_T *li, *ni; - if (argvars[0].v_type != VAR_LIST) + if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "reverse()"); - else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, (char_u *)_("reverse() argument"))) { + } else if ((l = argvars[0].vval.v_list) != NULL + && !tv_check_lock(l->lv_lock, + (char_u *)N_("reverse() argument"), true)) { li = l->lv_last; l->lv_first = l->lv_last = NULL; l->lv_len = 0; @@ -15741,8 +15757,12 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) EMSG2(_(e_listarg), sort ? "sort()" : "uniq()"); } else { l = argvars[0].vval.v_list; - if (l == NULL || tv_check_lock(l->lv_lock, - (char_u *)(sort ? _("sort() argument") : _("uniq() argument")))) { + if (l == NULL || + tv_check_lock(l->lv_lock, + (char_u *)(sort + ? N_("sort() argument") + : N_("uniq() argument")), + true)) { return; } rettv->vval.v_list = l; @@ -16213,13 +16233,23 @@ static void f_strlen(typval_T *argvars, typval_T *rettv) static void f_strchars(typval_T *argvars, typval_T *rettv) { char_u *s = get_tv_string(&argvars[0]); + int skipcc = 0; varnumber_T len = 0; + int (*func_mb_ptr2char_adv)(char_u **pp); - while (*s != NUL) { - mb_cptr2char_adv(&s); - ++len; + if (argvars[1].v_type != VAR_UNKNOWN) { + skipcc = get_tv_number_chk(&argvars[1], NULL); + } + if (skipcc < 0 || skipcc > 1) { + EMSG(_(e_invarg)); + } else { + func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv; + while (*s != NUL) { + func_mb_ptr2char_adv(&s); + ++len; + } + rettv->vval.v_number = len; } - rettv->vval.v_number = len; } /* @@ -18155,10 +18185,11 @@ char_u *set_cmdarg(exarg_T *eap, char_u *oldarg) static int get_var_tv ( char_u *name, - int len, /* length of "name" */ - typval_T *rettv, /* NULL when only checking existence */ - int verbose, /* may give error message */ - int no_autoload /* do not use script autoloading */ + int len, // length of "name" + typval_T *rettv, // NULL when only checking existence + dictitem_T **dip, // non-NULL when typval's dict item is needed + int verbose, // may give error message + int no_autoload // do not use script autoloading ) { int ret = OK; @@ -18184,8 +18215,12 @@ get_var_tv ( */ else { v = find_var(name, NULL, no_autoload); - if (v != NULL) + if (v != NULL) { tv = &v->di_tv; + if (dip != NULL) { + *dip = v; + } + } } if (tv == NULL) { @@ -18576,6 +18611,9 @@ static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d) hashitem_T *hi; *d = NULL; + if (name[0] == NUL) { + return NULL; + } if (name[1] != ':') { // name has implicit scope if (name[0] == ':' || name[0] == AUTOLOAD_CHAR) { @@ -18625,6 +18663,7 @@ end: } // Find the hashtab used for a variable name. +// Return NULL if the name is not valid. // Set "varname" to the start of name without ':'. static hashtab_T *find_var_ht(uint8_t *name, uint8_t **varname) { @@ -18848,10 +18887,11 @@ set_var ( return; if (v != NULL) { - /* existing variable, need to clear the value */ - if (var_check_ro(v->di_flags, name) - || tv_check_lock(v->di_tv.v_lock, name)) + // existing variable, need to clear the value + if (var_check_ro(v->di_flags, name, false) + || tv_check_lock(v->di_tv.v_lock, name, false)) { return; + } if (v->di_tv.v_type != tv->v_type && !((v->di_tv.v_type == VAR_STRING || v->di_tv.v_type == VAR_NUMBER) @@ -18866,10 +18906,8 @@ set_var ( return; } - /* - * Handle setting internal v: variables separately: we don't change - * the type. - */ + // Handle setting internal v: variables separately where needed to + // prevent changing the type. if (ht == &vimvarht) { if (v->di_tv.v_type == VAR_STRING) { xfree(v->di_tv.vval.v_string); @@ -18880,9 +18918,8 @@ set_var ( v->di_tv.vval.v_string = tv->vval.v_string; tv->vval.v_string = NULL; } - } else if (v->di_tv.v_type != VAR_NUMBER) - EMSG2(_(e_intern2), "set_var()"); - else { + return; + } else if (v->di_tv.v_type == VAR_NUMBER) { v->di_tv.vval.v_number = get_tv_number(tv); if (STRCMP(varname, "searchforward") == 0) set_search_direction(v->di_tv.vval.v_number ? '/' : '?'); @@ -18890,8 +18927,10 @@ set_var ( no_hlsearch = !v->di_tv.vval.v_number; redraw_all_later(SOME_VALID); } + return; + } else if (v->di_tv.v_type != tv->v_type) { + EMSG2(_(e_intern2), "set_var()"); } - return; } if (watched) { @@ -18936,34 +18975,31 @@ set_var ( } } -/* - * Return TRUE if di_flags "flags" indicates variable "name" is read-only. - * Also give an error message. - */ -static int var_check_ro(int flags, char_u *name) +// Return true if di_flags "flags" indicates variable "name" is read-only. +// Also give an error message. +static bool var_check_ro(int flags, char_u *name, bool use_gettext) { if (flags & DI_FLAGS_RO) { - EMSG2(_(e_readonlyvar), name); - return TRUE; + EMSG2(_(e_readonlyvar), use_gettext ? (char_u *)_(name) : name); + return true; } if ((flags & DI_FLAGS_RO_SBX) && sandbox) { - EMSG2(_(e_readonlysbx), name); - return TRUE; + EMSG2(_(e_readonlysbx), use_gettext ? (char_u *)_(name) : name); + return true; } - return FALSE; + return false; } -/* - * Return TRUE if di_flags "flags" indicates variable "name" is fixed. - * Also give an error message. - */ -static int var_check_fixed(int flags, char_u *name) +// Return true if di_flags "flags" indicates variable "name" is fixed. +// Also give an error message. +static bool var_check_fixed(int flags, char_u *name, bool use_gettext) { if (flags & DI_FLAGS_FIX) { - EMSG2(_("E795: Cannot delete variable %s"), name); - return TRUE; + EMSG2(_("E795: Cannot delete variable %s"), + use_gettext ? (char_u *)_(name) : name); + return true; } - return FALSE; + return false; } /* @@ -19011,23 +19047,28 @@ static int valid_varname(char_u *varname) return TRUE; } -/* - * Return TRUE if typeval "tv" is set to be locked (immutable). - * Also give an error message, using "name". - */ -static int tv_check_lock(int lock, char_u *name) +// Return true if typeval "tv" is set to be locked (immutable). +// Also give an error message, using "name" or _("name") when use_gettext is +// true. +static bool tv_check_lock(int lock, char_u *name, bool use_gettext) { if (lock & VAR_LOCKED) { EMSG2(_("E741: Value is locked: %s"), - name == NULL ? (char_u *)_("Unknown") : name); - return TRUE; + name == NULL + ? (char_u *)_("Unknown") + : use_gettext ? (char_u *)_(name) + : name); + return true; } if (lock & VAR_FIXED) { EMSG2(_("E742: Cannot change value of %s"), - name == NULL ? (char_u *)_("Unknown") : name); - return TRUE; + name == NULL + ? (char_u *)_("Unknown") + : use_gettext ? (char_u *)_(name) + : name); + return true; } - return FALSE; + return false; } /* @@ -19620,7 +19661,10 @@ void ex_function(exarg_T *eap) break; } } - ++p; /* skip the ')' */ + if (*p != ')') { + goto erret; + } + ++p; // skip the ')' /* find extra arguments "range", "dict" and "abort" */ for (;; ) { @@ -19844,13 +19888,14 @@ void ex_function(exarg_T *eap) goto erret; } if (fudi.fd_di == NULL) { - /* Can't add a function to a locked dictionary */ - if (tv_check_lock(fudi.fd_dict->dv_lock, eap->arg)) + if (tv_check_lock(fudi.fd_dict->dv_lock, eap->arg, false)) { + // Can't add a function to a locked dictionary goto erret; - } - /* Can't change an existing function if it is locked */ - else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg)) + } + } else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, false)) { + // Can't change an existing function if it is locked goto erret; + } /* Give the function a sequential number. Can only be used with a * Funcref! */ diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 28ff6fded4..d6976bcb8f 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1702,9 +1702,9 @@ static char_u * do_one_cmd(char_u **cmdlinep, p = vim_strnsave(ea.cmd, p - ea.cmd); int ret = apply_autocmds(EVENT_CMDUNDEFINED, p, p, TRUE, NULL); xfree(p); - if (ret && !aborting()) { - p = find_command(&ea, NULL); - } + // If the autocommands did something and didn't cause an error, try + // finding the command again. + p = (ret && !aborting()) ? find_command(&ea, NULL) : NULL; } if (p == NULL) { @@ -2348,8 +2348,11 @@ static char_u *find_command(exarg_T *eap, int *full) eap->cmdidx = CMD_k; ++p; } else if (p[0] == 's' - && ((p[1] == 'c' && p[2] != 's' && p[2] != 'r' - && p[3] != 'i' && p[4] != 'p') + && ((p[1] == 'c' + && (p[2] == NUL + || (p[2] != 's' && p[2] != 'r' + && (p[3] == NUL + || (p[3] != 'i' && p[4] != 'p'))))) || p[1] == 'g' || (p[1] == 'i' && p[2] != 'm' && p[2] != 'l' && p[2] != 'g') || p[1] == 'I' @@ -8280,16 +8283,22 @@ static char_u *arg_all(void) retval[len] = ' '; ++len; } - for (; *p != NUL; ++p) { - if (*p == ' ' || *p == '\\') { - /* insert a backslash */ - if (retval != NULL) + for (; *p != NUL; p++) { + if (*p == ' ' +#ifndef BACKSLASH_IN_FILENAME + || *p == '\\' +#endif + ) { + // insert a backslash + if (retval != NULL) { retval[len] = '\\'; - ++len; + } + len++; } - if (retval != NULL) + if (retval != NULL) { retval[len] = *p; - ++len; + } + len++; } } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 96bf2c78d2..d015f6b4a0 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -5136,6 +5136,8 @@ static int ex_window(void) /* Don't execute autocommands while deleting the window. */ block_autocmds(); + // Avoid command-line window first character being concealed + curwin->w_p_cole = 0; wp = curwin; bp = curbuf; win_goto(old_curwin); diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 90987d0b3d..383cd47dbe 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -7160,10 +7160,11 @@ char_u * file_pat_to_reg_pat( else reg_pat[i++] = '^'; endp = pat_end - 1; - if (*endp == '*') { - while (endp - pat > 0 && *endp == '*') + if (endp >= pat && *endp == '*') { + while (endp - pat > 0 && *endp == '*') { endp--; - add_dollar = FALSE; + } + add_dollar = false; } for (p = pat; *p && nested >= 0 && p <= endp; p++) { switch (*p) { @@ -7218,12 +7219,12 @@ char_u * file_pat_to_reg_pat( #ifdef BACKSLASH_IN_FILENAME && no_bslash #endif - ) + ) { reg_pat[i++] = '?'; - else if (*p == ',' || *p == '%' || *p == '#' - || *p == ' ' || *p == '{' || *p == '}') + } else if (*p == ',' || *p == '%' || *p == '#' + || ascii_isspace(*p) || *p == '{' || *p == '}') { reg_pat[i++] = *p; - else if (*p == '\\' && p[1] == '\\' && p[2] == '{') { + } else if (*p == '\\' && p[1] == '\\' && p[2] == '{') { reg_pat[i++] = '\\'; reg_pat[i++] = '{'; p += 2; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 9a9cf50e48..e6c5354941 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1538,9 +1538,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) curbuf->b_visual_mode_eval = VIsual_mode; } - /* In Select mode, a linewise selection is operated upon like a - * characterwise selection. */ - if (VIsual_select && VIsual_mode == 'V') { + // In Select mode, a linewise selection is operated upon like a + // characterwise selection. + // Special case: gH<Del> deletes the last line. + if (VIsual_select && VIsual_mode == 'V' + && cap->oap->op_type != OP_DELETE) { if (lt(VIsual, curwin->w_cursor)) { VIsual.col = 0; curwin->w_cursor.col = @@ -1676,20 +1678,15 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) && (include_line_break || !virtual_op) ) { oap->inclusive = false; - /* Try to include the newline, unless it's an operator - * that works on lines only. */ - if (*p_sel != 'o' && !op_on_lines(oap->op_type)) { - if (oap->end.lnum < curbuf->b_ml.ml_line_count) { - ++oap->end.lnum; - oap->end.col = 0; - oap->end.coladd = 0; - ++oap->line_count; - } else { - /* Cannot move below the last line, make the op - * inclusive to tell the operation to include the - * line break. */ - oap->inclusive = true; - } + // Try to include the newline, unless it's an operator + // that works on lines only. + if (*p_sel != 'o' + && !op_on_lines(oap->op_type) + && oap->end.lnum < curbuf->b_ml.ml_line_count) { + oap->end.lnum++; + oap->end.col = 0; + oap->end.coladd = 0; + oap->line_count++; } } } @@ -6955,10 +6952,16 @@ static void n_opencmd(cmdarg_T *cap) (cap->cmdchar == 'o' ? 1 : 0)) ) && open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD, - has_format_option(FO_OPEN_COMS) ? OPENLINE_DO_COM : - 0, 0)) { - if (curwin->w_p_cole > 0 && oldline != curwin->w_cursor.lnum) + has_format_option(FO_OPEN_COMS) + ? OPENLINE_DO_COM : 0, + 0)) { + if (curwin->w_p_cole > 0 && oldline != curwin->w_cursor.lnum) { update_single_line(curwin, oldline); + } + if (curwin->w_p_cul) { + // force redraw of cursorline + curwin->w_valid &= ~VALID_CROW; + } invoke_edit(cap, false, cap->cmdchar, true); } } @@ -7742,6 +7745,10 @@ static void nv_put(cmdarg_T *cap) if (was_visual) { curbuf->b_visual.vi_start = curbuf->b_op_start; curbuf->b_visual.vi_end = curbuf->b_op_end; + // need to adjust cursor position + if (*p_sel == 'e') { + inc(&curbuf->b_visual.vi_end); + } } /* When all lines were selected and deleted do_put() leaves an empty @@ -7798,20 +7805,23 @@ static void get_op_vcol( } getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol); - getvvcol(curwin, &(oap->end), &start, NULL, &end); + if (!redo_VIsual_busy) { + getvvcol(curwin, &(oap->end), &start, NULL, &end); - if (start < oap->start_vcol) { - oap->start_vcol = start; - } - if (end > oap->end_vcol) { - if (initial && *p_sel == 'e' - && start >= 1 - && start - 1 >= oap->end_vcol) { - oap->end_vcol = start - 1; - } else { - oap->end_vcol = end; + if (start < oap->start_vcol) { + oap->start_vcol = start; + } + if (end > oap->end_vcol) { + if (initial && *p_sel == 'e' + && start >= 1 + && start - 1 >= oap->end_vcol) { + oap->end_vcol = start - 1; + } else { + oap->end_vcol = end; + } } } + // if '$' was used, get oap->end_vcol from longest line if (curwin->w_curswant == MAXCOL) { curwin->w_cursor.col = MAXCOL; diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 7614e6365a..b1adc85e1d 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -1555,55 +1555,31 @@ int op_delete(oparg_T *oap) if (gchar_cursor() != NUL) curwin->w_cursor.coladd = 0; } - if (oap->op_type == OP_DELETE - && oap->inclusive - && oap->end.lnum == curbuf->b_ml.ml_line_count - && n > (int)STRLEN(ml_get(oap->end.lnum))) { - /* Special case: gH<Del> deletes the last line. */ - del_lines(1L, FALSE); - } else { - (void)del_bytes((long)n, !virtual_op, oap->op_type == OP_DELETE - && !oap->is_VIsual - ); - } - } else { /* delete characters between lines */ + + (void)del_bytes((long)n, !virtual_op, + oap->op_type == OP_DELETE && !oap->is_VIsual); + } else { + // delete characters between lines pos_T curpos; - int delete_last_line; /* save deleted and changed lines for undo */ if (u_save((linenr_T)(curwin->w_cursor.lnum - 1), (linenr_T)(curwin->w_cursor.lnum + oap->line_count)) == FAIL) return FAIL; - delete_last_line = (oap->end.lnum == curbuf->b_ml.ml_line_count); - truncate_line(TRUE); /* delete from cursor to end of line */ + truncate_line(true); // delete from cursor to end of line - curpos = curwin->w_cursor; /* remember curwin->w_cursor */ - ++curwin->w_cursor.lnum; - del_lines(oap->line_count - 2, FALSE); - - if (delete_last_line) - oap->end.lnum = curbuf->b_ml.ml_line_count; + curpos = curwin->w_cursor; // remember curwin->w_cursor + curwin->w_cursor.lnum++; + del_lines(oap->line_count - 2, false); + // delete from start of line until op_end n = (oap->end.col + 1 - !oap->inclusive); - if (oap->inclusive && delete_last_line - && n > (int)STRLEN(ml_get(oap->end.lnum))) { - /* Special case: gH<Del> deletes the last line. */ - del_lines(1L, FALSE); - curwin->w_cursor = curpos; /* restore curwin->w_cursor */ - if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) - curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - } else { - /* delete from start of line until op_end */ - curwin->w_cursor.col = 0; - (void)del_bytes((long)n, !virtual_op, oap->op_type == OP_DELETE - && !oap->is_VIsual - ); - curwin->w_cursor = curpos; /* restore curwin->w_cursor */ - } - if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { - do_join(2, FALSE, FALSE, FALSE, false); - } + curwin->w_cursor.col = 0; + (void)del_bytes((long)n, !virtual_op, + oap->op_type == OP_DELETE && !oap->is_VIsual); + curwin->w_cursor = curpos; // restore curwin->w_cursor + (void)do_join(2, false, false, false, false); } } @@ -2688,17 +2664,27 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) if (y_type == MLINE) { if (flags & PUT_LINE_SPLIT) { - /* "p" or "P" in Visual mode: split the lines to put the text in - * between. */ - if (u_save_cursor() == FAIL) + // "p" or "P" in Visual mode: split the lines to put the text in + // between. + if (u_save_cursor() == FAIL) { goto end; - ptr = vim_strsave(get_cursor_pos_ptr()); - ml_append(curwin->w_cursor.lnum, ptr, (colnr_T)0, FALSE); + } + char_u *p = get_cursor_pos_ptr(); + if (dir == FORWARD && *p != NUL) { + mb_ptr_adv(p); + } + ptr = vim_strsave(p); + ml_append(curwin->w_cursor.lnum, ptr, (colnr_T)0, false); xfree(ptr); - ptr = vim_strnsave(get_cursor_line_ptr(), curwin->w_cursor.col); - ml_replace(curwin->w_cursor.lnum, ptr, FALSE); - ++nr_lines; + oldp = get_cursor_line_ptr(); + p = oldp + curwin->w_cursor.col; + if (dir == FORWARD && *p != NUL) { + mb_ptr_adv(p); + } + ptr = vim_strnsave(oldp, p - oldp); + ml_replace(curwin->w_cursor.lnum, ptr, false); + nr_lines++; dir = FORWARD; } if (flags & PUT_LINE_FORWARD) { diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 11b5e31f77..10706a0753 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -571,41 +571,43 @@ EXTERN char_u *p_su; // 'suffixes' EXTERN char_u *p_swb; // 'switchbuf' EXTERN unsigned swb_flags; #ifdef IN_OPTION_C -static char *(p_swb_values[]) = {"useopen", "usetab", "split", "newtab", NULL}; +static char *(p_swb_values[]) = + { "useopen", "usetab", "split", "newtab", "vsplit", NULL }; #endif #define SWB_USEOPEN 0x001 #define SWB_USETAB 0x002 #define SWB_SPLIT 0x004 #define SWB_NEWTAB 0x008 -EXTERN int p_tbs; /* 'tagbsearch' */ -EXTERN long p_tl; /* 'taglength' */ -EXTERN int p_tr; /* 'tagrelative' */ -EXTERN char_u *p_tags; /* 'tags' */ -EXTERN int p_tgst; /* 'tagstack' */ -EXTERN int p_tbidi; /* 'termbidi' */ -EXTERN int p_terse; /* 'terse' */ -EXTERN int p_to; /* 'tildeop' */ -EXTERN int p_timeout; /* 'timeout' */ -EXTERN long p_tm; /* 'timeoutlen' */ -EXTERN int p_title; /* 'title' */ -EXTERN long p_titlelen; /* 'titlelen' */ -EXTERN char_u *p_titleold; /* 'titleold' */ -EXTERN char_u *p_titlestring; /* 'titlestring' */ -EXTERN char_u *p_tsr; /* 'thesaurus' */ -EXTERN int p_ttimeout; /* 'ttimeout' */ -EXTERN long p_ttm; /* 'ttimeoutlen' */ -EXTERN char_u *p_udir; /* 'undodir' */ -EXTERN long p_ul; /* 'undolevels' */ -EXTERN long p_ur; /* 'undoreload' */ -EXTERN long p_uc; /* 'updatecount' */ -EXTERN long p_ut; /* 'updatetime' */ -EXTERN char_u *p_fcs; /* 'fillchar' */ -EXTERN char_u *p_shada; /* 'shada' */ -EXTERN char_u *p_vdir; /* 'viewdir' */ -EXTERN char_u *p_vop; /* 'viewoptions' */ -EXTERN unsigned vop_flags; /* uses SSOP_ flags */ -EXTERN int p_vb; /* 'visualbell' */ -EXTERN char_u *p_ve; /* 'virtualedit' */ +#define SWB_VSPLIT 0x010 +EXTERN int p_tbs; ///< 'tagbsearch' +EXTERN long p_tl; ///< 'taglength' +EXTERN int p_tr; ///< 'tagrelative' +EXTERN char_u *p_tags; ///< 'tags' +EXTERN int p_tgst; ///< 'tagstack' +EXTERN int p_tbidi; ///< 'termbidi' +EXTERN int p_terse; ///< 'terse' +EXTERN int p_to; ///< 'tildeop' +EXTERN int p_timeout; ///< 'timeout' +EXTERN long p_tm; ///< 'timeoutlen' +EXTERN int p_title; ///< 'title' +EXTERN long p_titlelen; ///< 'titlelen' +EXTERN char_u *p_titleold; ///< 'titleold' +EXTERN char_u *p_titlestring; ///< 'titlestring' +EXTERN char_u *p_tsr; ///< 'thesaurus' +EXTERN int p_ttimeout; ///< 'ttimeout' +EXTERN long p_ttm; ///< 'ttimeoutlen' +EXTERN char_u *p_udir; ///< 'undodir' +EXTERN long p_ul; ///< 'undolevels' +EXTERN long p_ur; ///< 'undoreload' +EXTERN long p_uc; ///< 'updatecount' +EXTERN long p_ut; ///< 'updatetime' +EXTERN char_u *p_fcs; ///< 'fillchar' +EXTERN char_u *p_shada; ///< 'shada' +EXTERN char_u *p_vdir; ///< 'viewdir' +EXTERN char_u *p_vop; ///< 'viewoptions' +EXTERN unsigned vop_flags; ///< uses SSOP_ flags +EXTERN int p_vb; ///< 'visualbell' +EXTERN char_u *p_ve; ///< 'virtualedit' EXTERN unsigned ve_flags; # ifdef IN_OPTION_C static char *(p_ve_values[]) = {"block", "insert", "all", "onemore", NULL}; diff --git a/src/nvim/po/CMakeLists.txt b/src/nvim/po/CMakeLists.txt index 6687918df4..d1e08db65e 100644 --- a/src/nvim/po/CMakeLists.txt +++ b/src/nvim/po/CMakeLists.txt @@ -72,7 +72,7 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG) install_helper( FILES ${moFile} - DESTINATION ${CMAKE_INSTALL_LOCALEDIR}/${name}/LC_MESSAGES + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/nvim/runtime/lang/${name}/LC_MESSAGES RENAME nvim.mo) list(APPEND LANGUAGE_MO_FILES ${moFile}) diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 24422c71fb..821411ec7b 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -3004,14 +3004,19 @@ static void syn_cmd_spell(exarg_T *eap, int syncing) return; next = skiptowhite(arg); - if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8) + if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8) { curwin->w_s->b_syn_spell = SYNSPL_TOP; - else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10) + } else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10) { curwin->w_s->b_syn_spell = SYNSPL_NOTOP; - else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7) + } else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7) { curwin->w_s->b_syn_spell = SYNSPL_DEFAULT; - else + } else { EMSG2(_("E390: Illegal argument: %s"), arg); + return; + } + + // assume spell checking changed, force a redraw + redraw_win_later(curwin, NOT_VALID); } /* @@ -4183,12 +4188,16 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing) break; if (p[1] == NUL) { EMSG2(_("E789: Missing ']': %s"), kw); - kw = p + 2; /* skip over the NUL */ - break; + goto error; } if (p[1] == ']') { - kw = p + 1; /* skip over the "]" */ - break; + if (p[2] != NUL) { + EMSG3(_("E890: trailing char after ']': %s]%s"), + kw, &p[2]); + goto error; + } + kw = p + 1; + break; // skip over the "]" } if (has_mbyte) { int l = (*mb_ptr2len)(p + 1); @@ -4203,6 +4212,7 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing) } } +error: xfree(keyword_copy); xfree(syn_opt_arg.cont_in_list); xfree(syn_opt_arg.next_list); @@ -4843,9 +4853,10 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci) int idx; char_u *cpo_save; - /* need at least three chars */ - if (arg == NULL || arg[1] == NUL || arg[2] == NUL) + // need at least three chars + if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL) { return NULL; + } end = skip_regexp(arg + 1, *arg, TRUE, NULL); if (*end != *arg) { /* end delimiter not found */ diff --git a/src/nvim/testdir/test47.in b/src/nvim/testdir/test47.in index 13ad82462f..f15eaf0f8f 100644 --- a/src/nvim/testdir/test47.in +++ b/src/nvim/testdir/test47.in @@ -1,5 +1,7 @@ Tests for vertical splits and filler lines in diff mode +Also tests restoration of saved options by :diffoff. + STARTTEST :so small.vim :" Disable the title to avoid xterm keeping the wrong one. @@ -10,8 +12,19 @@ pkdd:w! Xtest ddGpkkrXoxxx:w! Xtest2 :file Nop ggoyyyjjjozzzz +:set foldmethod=marker foldcolumn=4 +:redir => nodiffsettings +:silent! :set diff? fdm? fdc? scb? crb? wrap? +:redir END :vert diffsplit Xtest :vert diffsplit Xtest2 +:redir => diffsettings +:silent! :set diff? fdm? fdc? scb? crb? wrap? +:redir END +:let diff_fdm = &fdm +:let diff_fdc = &fdc +:" repeat entering diff mode here to see if this saves the wrong settings +:diffthis :" jump to second window for a moment to have filler line appear at start of :" first window ggpgg:let one = winline() @@ -36,8 +49,36 @@ j:let three = three . "-" . winline() :call append("$", two) :call append("$", three) :$-2,$w! test.out -:" Test that diffing shows correct filler lines +:" +:" Test diffoff :diffoff! +1 +:let &diff = 1 +:let &fdm = diff_fdm +:let &fdc = diff_fdc +4 +:diffoff! +:$put =nodiffsettings +:$put =diffsettings +1 +:redir => nd1 +:silent! :set diff? fdm? fdc? scb? crb? wrap? +:redir END + +:redir => nd2 +:silent! :set diff? fdm? fdc? scb? crb? wrap? +:redir END + +:redir => nd3 +:silent! :set diff? fdm? fdc? scb? crb? wrap? +:redir END + +:$put =nd1 +:$put =nd2 +:$put =nd3 +:$-39,$w >> test.out +:" +:" Test that diffing shows correct filler lines :windo :bw! :enew :put =range(4,10) @@ -51,7 +92,7 @@ j:let three = three . "-" . winline() :enew :put =w0 :.w >> test.out -:unlet! one two three w0 +:unlet! one two three nodiffsettings diffsettings diff_fdm diff_fdc nd1 nd2 nd3 w0 :qa! ENDTEST diff --git a/src/nvim/testdir/test47.ok b/src/nvim/testdir/test47.ok index b1cba92b1c..83e96571ad 100644 --- a/src/nvim/testdir/test47.ok +++ b/src/nvim/testdir/test47.ok @@ -1,4 +1,44 @@ 2-4-5-6-8-9 1-2-4-5-8 2-3-4-5-6-7-8 + + +nodiff + foldmethod=marker + foldcolumn=4 +noscrollbind +nocursorbind + wrap + + + diff + foldmethod=diff + foldcolumn=2 + scrollbind + cursorbind +nowrap + + +nodiff + foldmethod=marker + foldcolumn=4 +noscrollbind +nocursorbind + wrap + + +nodiff + foldmethod=marker + foldcolumn=4 +noscrollbind +nocursorbind + wrap + + +nodiff + foldmethod=marker + foldcolumn=4 +noscrollbind +nocursorbind + wrap 1 diff --git a/src/nvim/testdir/test55.in b/src/nvim/testdir/test55.in index 7b6f684caa..9e3c1168cc 100644 --- a/src/nvim/testdir/test55.in +++ b/src/nvim/testdir/test55.in @@ -442,6 +442,17 @@ let l = [0, 1, 2, 3] :unlockvar 1 b: :unlet! b:testvar :" +:$put ='No :let += of locked list variable:' +:let l = ['a', 'b', 3] +:lockvar 1 l +:try +: let l += ['x'] +: $put ='did :let +=' +:catch +: $put =v:exception[:14] +:endtry +:$put =string(l) +:" :unlet l :let l = [1, 2, 3, 4] :lockvar! l diff --git a/src/nvim/testdir/test55.ok b/src/nvim/testdir/test55.ok index 4e0303c26e..607a95ead9 100644 --- a/src/nvim/testdir/test55.ok +++ b/src/nvim/testdir/test55.ok @@ -144,6 +144,9 @@ No extend() of write-protected scope-level variable: Vim(put):E742: No :unlet of variable in locked scope: Vim(unlet):E741: +No :let += of locked list variable: +Vim(let):E741: +['a', 'b', 3] [1, 2, 3, 4] [1, 2, 3, 4] [1, 2, 3, 4] diff --git a/src/nvim/testdir/test_listlbr.in b/src/nvim/testdir/test_listlbr.in index 57202b46eb..f13eee121e 100644 --- a/src/nvim/testdir/test_listlbr.in +++ b/src/nvim/testdir/test_listlbr.in @@ -75,6 +75,12 @@ Golong line: 40afoobar aTARGET at end :let g:test ="Test 8: set linebreak with visual char mode and changing block" :$put =g:test Go1111-1111-1111-11-1111-1111-11110f-lv3lc2222bgj. +:let g:test ="Test 9: using redo after block visual mode" +:$put =g:test +Go +aaa +aaa +a2k2j~e. :%w! test.out :qa! ENDTEST diff --git a/src/nvim/testdir/test_listlbr.ok b/src/nvim/testdir/test_listlbr.ok index 82881234c4..323bcdee08 100644 --- a/src/nvim/testdir/test_listlbr.ok +++ b/src/nvim/testdir/test_listlbr.ok @@ -41,3 +41,8 @@ Test 7: set linebreak with visual block mode and v_b_A long line: foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar TARGETx at end Test 8: set linebreak with visual char mode and changing block 1111-2222-1111-11-1111-2222-1111 +Test 9: using redo after block visual mode + +AaA +AaA +A diff --git a/src/nvim/version.c b/src/nvim/version.c index 0b5783b41c..a95e5f2adf 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -357,9 +357,9 @@ static int included_patches[] = { // 934 NA // 933, // 932, - // 931, + // 931 NA // 930 NA - // 929, + 929, // 928 NA // 927 NA // 926, @@ -454,7 +454,7 @@ static int included_patches[] = { // 837 NA 836, // 835, - // 834, + 834, // 833, // 832, // 831, @@ -463,34 +463,34 @@ static int included_patches[] = { // 828, // 827, 826, - // 825, + 825, // 824 NA 823, // 822, // 821, - // 820, + 820, // 819, // 818, - // 817, - // 816, - // 815, - // 814, + 817, + 816, + 815, + 814, 813, // 812, - // 811, - // 810, + 811, + 810, 809, // 808 NA 807, 806, - // 805, + 805, // 804, 803, 802, - // 801, - // 800, + 801, + 800, 799, - // 798, + 798, // 797, // 796 NA 795, @@ -519,8 +519,8 @@ static int included_patches[] = { // 772 NA // 771, // 770 NA - // 769, - // 768, + 769, + 768, // 767, // 766 NA 765, @@ -528,12 +528,12 @@ static int included_patches[] = { // 763 NA // 762 NA // 761 NA - // 760, + 760, // 759 NA - // 758, + 758, // 757 NA // 756 NA - // 755, + 755, 754, 753, // 752, @@ -545,18 +545,18 @@ static int included_patches[] = { 746, 745, // 744 NA - // 743, - // 742, + 743, + 742, 741, 740, 739, // 738 NA - // 737, + 737, 736, - // 735, - // 734, + // 735 NA + 734, // 733, - // 732, + 732, // 731 NA // 730 NA 729, @@ -571,7 +571,7 @@ static int included_patches[] = { // 720 NA 719, 718, - // 717, + 717, 716, 715, 714, @@ -580,7 +580,7 @@ static int included_patches[] = { 711, 710, 709, - // 708, + 708, 707, 706, // 705 NA diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 5f9785a9a9..762d349470 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -30,7 +30,7 @@ Error: configure did not run properly.Check auto/config.log. /* Can't use "PACKAGE" here, conflicts with a Perl include file. */ #ifndef VIMPACKAGE -# define VIMPACKAGE "vim" +# define VIMPACKAGE "nvim" #endif #include "nvim/os/os_defs.h" /* bring lots of system header files */ diff --git a/test/functional/legacy/091_context_variables_spec.lua b/test/functional/legacy/091_context_variables_spec.lua index ffeb0c657e..2c46ef643c 100644 --- a/test/functional/legacy/091_context_variables_spec.lua +++ b/test/functional/legacy/091_context_variables_spec.lua @@ -13,6 +13,9 @@ describe('context variables', function() -- Test for getbufvar(). -- Use strings to test for memory leaks. source([[ + let t:testvar='abcd' + $put =string(gettabvar(1, 'testvar')) + $put =string(gettabvar(1, 'testvar')) let b:var_num = '1234' let def_num = '5678' $put =string(getbufvar(1, 'var_num')) @@ -125,6 +128,8 @@ describe('context variables', function() -- Assert buffer contents. expect([[ start: + 'abcd' + 'abcd' '1234' '1234' {'var_num': '1234'} diff --git a/test/functional/legacy/094_visual_mode_operators_spec.lua b/test/functional/legacy/094_visual_mode_operators_spec.lua index c4aebe4ecc..4dce39b8d2 100644 --- a/test/functional/legacy/094_visual_mode_operators_spec.lua +++ b/test/functional/legacy/094_visual_mode_operators_spec.lua @@ -24,6 +24,27 @@ local function source_user_functions() ]]) end +local function put_abc() + source([[ + $put ='a' + $put ='b' + $put ='c']]) +end + +local function put_aaabbbccc() + source([[ + $put ='aaa' + $put ='bbb' + $put ='ccc']]) +end + +local function define_select_mode_maps() + source([[ + snoremap <lt>End> <End> + snoremap <lt>Down> <Down> + snoremap <lt>Del> <Del>]]) +end + describe('Visual mode and operator', function() before_each(function() clear() @@ -150,4 +171,228 @@ describe('Visual mode and operator', function() ok ok]]) end) + + describe('characterwise visual mode:', function() + it('replace last line', function() + source([[ + $put ='a' + let @" = 'x']]) + feed('v$p') + + expect([[ + + x]]) + end) + + it('delete middle line', function() + put_abc() + feed('kkv$d') + + expect([[ + + b + c]]) + end) + + it('delete middle two line', function() + put_abc() + feed('kkvj$d') + + expect([[ + + c]]) + end) + + it('delete last line', function() + put_abc() + feed('v$d') + + expect([[ + + a + b + ]]) + end) + + it('delete last two line', function() + put_abc() + feed('kvj$d') + + expect([[ + + a + ]]) + end) + end) + + describe('characterwise select mode:', function() + before_each(function() + define_select_mode_maps() + end) + + it('delete middle line', function() + put_abc() + feed('kkgh<End><Del>') + + expect([[ + + b + c]]) + end) + + it('delete middle two line', function() + put_abc() + feed('kkgh<Down><End><Del>') + + expect([[ + + c]]) + end) + + it('delete last line', function() + put_abc() + feed('gh<End><Del>') + + expect([[ + + a + b + ]]) + end) + + it('delete last two line', function() + put_abc() + feed('kgh<Down><End><Del>') + + expect([[ + + a + ]]) + end) + end) + + describe('linewise select mode:', function() + before_each(function() + define_select_mode_maps() + end) + + it('delete middle line', function() + put_abc() + feed(' kkgH<Del> ') + + expect([[ + + b + c]]) + end) + + it('delete middle two line', function() + put_abc() + feed('kkgH<Down><Del>') + + expect([[ + + c]]) + end) + + it('delete last line', function() + put_abc() + feed('gH<Del>') + + expect([[ + + a + b]]) + end) + + it('delete last two line', function() + put_abc() + feed('kgH<Down><Del>') + + expect([[ + + a]]) + end) + end) + + describe('v_p:', function() + it('replace last character with line register at middle line', function() + put_aaabbbccc() + execute('-2yank') + feed('k$vp') + + expect([[ + + aaa + bb + aaa + + ccc]]) + end) + + it('replace last character with line register at middle line selecting newline', function() + put_aaabbbccc() + execute('-2yank') + feed('k$v$p') + + expect([[ + + aaa + bb + aaa + ccc]]) + end) + + it('replace last character with line register at last line', function() + put_aaabbbccc() + execute('-2yank') + feed('$vp') + + expect([[ + + aaa + bbb + cc + aaa + ]]) + end) + + it('replace last character with line register at last line selecting newline', function() + put_aaabbbccc() + execute('-2yank') + feed('$v$p') + + expect([[ + + aaa + bbb + cc + aaa + ]]) + end) + end) + + it('gv in exclusive select mode after operation', function() + source([[ + $put ='zzz ' + $put ='äà ' + set selection=exclusive]]) + feed('kv3lyjv3lpgvcxxx<Esc>') + + expect([[ + + zzz + xxx ]]) + end) + + it('gv in exclusive select mode without operation', function() + source([[ + $put ='zzz ' + set selection=exclusive]]) + feed('0v3l<Esc>gvcxxx<Esc>') + + expect([[ + + xxx ]]) + end) end) diff --git a/test/functional/legacy/utf8_spec.lua b/test/functional/legacy/utf8_spec.lua index ef717042d0..c16b1c45f4 100644 --- a/test/functional/legacy/utf8_spec.lua +++ b/test/functional/legacy/utf8_spec.lua @@ -3,6 +3,7 @@ local helpers = require('test.functional.helpers') local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local execute, expect = helpers.execute, helpers.expect +local eq, eval = helpers.eq, helpers.eval describe('utf8', function() setup(clear) @@ -27,4 +28,26 @@ describe('utf8', function() xあああ bxbb]]) end) + + it('strchars()', function() + eq(1, eval('strchars("a")')) + eq(1, eval('strchars("a", 0)')) + eq(1, eval('strchars("a", 1)')) + + eq(3, eval('strchars("あいa")')) + eq(3, eval('strchars("あいa", 0)')) + eq(3, eval('strchars("あいa", 1)')) + + eq(2, eval('strchars("A\\u20dd")')) + eq(2, eval('strchars("A\\u20dd", 0)')) + eq(1, eval('strchars("A\\u20dd", 1)')) + + eq(3, eval('strchars("A\\u20dd\\u20dd")')) + eq(3, eval('strchars("A\\u20dd\\u20dd", 0)')) + eq(1, eval('strchars("A\\u20dd\\u20dd", 1)')) + + eq(1, eval('strchars("\\u20dd")')) + eq(1, eval('strchars("\\u20dd", 0)')) + eq(1, eval('strchars("\\u20dd", 1)')) + end) end) |