diff options
140 files changed, 1664 insertions, 1209 deletions
diff --git a/.builds/openbsd.yml b/.builds/openbsd.yml new file mode 100644 index 0000000000..1ac830e6ad --- /dev/null +++ b/.builds/openbsd.yml @@ -0,0 +1,38 @@ +# sourcehut CI: https://builds.sr.ht/~jmk/neovim + +image: openbsd/6.5 + +packages: +- autoconf-2.69p2 +- automake-1.15.1 +- cmake +- gettext-0.19.8.1p3 +- gettext-tools-0.19.8.1 +- gmake +- libtool +- ninja-1.8.2p0 +- unzip-6.0p11 + +sources: +- https://github.com/neovim/neovim + +tasks: +- build: | + export AUTOCONF_VERSION=2.69 + export AUTOMAKE_VERSION=1.15 + cd neovim + mkdir .deps + cd .deps + cmake -G Ninja ../third-party/ + cmake --build . --config Debug + cd .. + mkdir build + cd build + cmake -G Ninja .. + cmake --build . --config Debug + ./bin/nvim --version +- test: | + export LC_CTYPE=en_US.UTF-8 + cd neovim + # oldtests + gmake -C src/nvim/testdir NVIM_PRG=`pwd`/build/bin/nvim diff --git a/.gitignore b/.gitignore index 68dbb7588a..7db3d96e2b 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,8 @@ tags /src/nvim/testdir/valgrind.* /src/nvim/testdir/.gdbinit /runtime/indent/testdir/*.out +# Generated by src/nvim/testdir/runnvim.sh. +/src/nvim/testdir/*.tlog # Generated by unit tests. /test/includes/post/ diff --git a/.travis.yml b/.travis.yml index f234c89c26..325b5e7b56 100644 --- a/.travis.yml +++ b/.travis.yml @@ -76,7 +76,7 @@ addons: - valgrind - xclip homebrew: - update: true + update: false packages: - ccache - ninja @@ -164,6 +164,9 @@ cache: directories: - "$CACHE_NVIM_DEPS_DIR" +git: + quiet: true + notifications: webhooks: urls: diff --git a/ci/build.ps1 b/ci/build.ps1 index 42066c462b..d533d7b4e0 100644 --- a/ci/build.ps1 +++ b/ci/build.ps1 @@ -139,7 +139,7 @@ if ($uploadToCodecov) { # But would break functionaltests, where its `more` would be used then. $OldPath = $env:PATH $env:PATH = "C:\msys64\usr\bin;$env:PATH" -& "C:\msys64\mingw$bits\bin\mingw32-make.exe" -C $(Convert-Path ..\src\nvim\testdir) VERBOSE=1 +& "C:\msys64\mingw$bits\bin\mingw32-make.exe" -C $(Convert-Path ..\src\nvim\testdir) VERBOSE=1 ; exitIfFailed $env:PATH = $OldPath if ($uploadToCodecov) { diff --git a/ci/common/build.sh b/ci/common/build.sh index bdbe012ca6..8e9b2f8ebb 100644 --- a/ci/common/build.sh +++ b/ci/common/build.sh @@ -84,12 +84,11 @@ build_nvim() { fi # Invoke nvim to trigger *San early. - if ! (bin/nvim --version && bin/nvim -u NONE -e -c ':qall') ; then - asan_check "${LOG_DIR}" + if ! (bin/nvim --version && bin/nvim -u NONE -e -cq | cat -vet) ; then + check_sanitizer "${LOG_DIR}" exit 1 fi - asan_check "${LOG_DIR}" - + check_sanitizer "${LOG_DIR}" cd "${TRAVIS_BUILD_DIR}" } diff --git a/ci/common/test.sh b/ci/common/test.sh index fb2dcc077e..0233d62c96 100644 --- a/ci/common/test.sh +++ b/ci/common/test.sh @@ -80,8 +80,8 @@ valgrind_check() { check_logs "${1}" "valgrind-*" } -asan_check() { - if test "${CLANG_SANITIZER}" = "ASAN_UBSAN" ; then +check_sanitizer() { + if test -n "${CLANG_SANITIZER}"; then check_logs "${1}" "*san.*" fi } @@ -104,7 +104,7 @@ run_functionaltests() {( fail 'functionaltests' F 'Functional tests failed' fi submit_coverage functionaltest - asan_check "${LOG_DIR}" + check_sanitizer "${LOG_DIR}" valgrind_check "${LOG_DIR}" check_core_dumps exit_suite @@ -118,7 +118,7 @@ run_oldtests() {( fail 'oldtests' F 'Legacy tests failed' fi submit_coverage oldtest - asan_check "${LOG_DIR}" + check_sanitizer "${LOG_DIR}" valgrind_check "${LOG_DIR}" check_core_dumps exit_suite diff --git a/cmake/FindLibLUV.cmake b/cmake/FindLibLUV.cmake index 784e3fd249..bc53d00f24 100644 --- a/cmake/FindLibLUV.cmake +++ b/cmake/FindLibLUV.cmake @@ -14,7 +14,8 @@ set(LIBLUV_DEFINITIONS ${PC_LIBLUV_CFLAGS_OTHER}) find_path(LIBLUV_INCLUDE_DIR luv/luv.h PATHS ${PC_LIBLUV_INCLUDEDIR} ${PC_LIBLUV_INCLUDE_DIRS}) -list(APPEND LIBLUV_NAMES luv) +# Explicitly look for luv.so. #10407 +list(APPEND LIBLUV_NAMES luv luv${CMAKE_SHARED_LIBRARY_SUFFIX}) find_library(LIBLUV_LIBRARY NAMES ${LIBLUV_NAMES} HINTS ${PC_LIBLUV_LIBDIR} ${PC_LIBLUV_LIBRARY_DIRS}) diff --git a/cmake/GetCompileFlags.cmake b/cmake/GetCompileFlags.cmake index 482eacca16..667b97350c 100644 --- a/cmake/GetCompileFlags.cmake +++ b/cmake/GetCompileFlags.cmake @@ -13,6 +13,11 @@ function(get_compile_flags _compile_flags) get_directory_property(compile_definitions DIRECTORY "src/nvim" COMPILE_DEFINITIONS) + get_target_property(compile_definitions_target nvim COMPILE_DEFINITIONS) + if(compile_definitions_target) + list(APPEND compile_definitions ${compile_definitions_target}) + list(REMOVE_DUPLICATES compile_definitions) + endif() # NOTE: list(JOIN) requires CMake 3.12, string(CONCAT) requires CMake 3. string(REPLACE ";" " -D" compile_definitions "${compile_definitions}") if(compile_definitions) @@ -28,6 +33,11 @@ function(get_compile_flags _compile_flags) get_directory_property(compile_options DIRECTORY "src/nvim" COMPILE_OPTIONS) + get_target_property(compile_options_target nvim COMPILE_OPTIONS) + if(compile_options_target) + list(APPEND compile_options ${compile_options_target}) + list(REMOVE_DUPLICATES compile_options) + endif() # NOTE: list(JOIN) requires CMake 3.12. string(REPLACE ";" " " compile_options "${compile_options}") string(REPLACE diff --git a/config/config.h.in b/config/config.h.in index 3216ab7556..0cb87c6b4d 100644 --- a/config/config.h.in +++ b/config/config.h.in @@ -13,7 +13,6 @@ #endif #define PROJECT_NAME "@PROJECT_NAME@" -#define LOCALE_INSTALL_DIR "@CMAKE_INSTALL_FULL_LOCALEDIR@" #cmakedefine HAVE__NSGETENVIRON #cmakedefine HAVE_FD_CLOEXEC diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index ce140b0948..e33dc31f6d 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -159,9 +159,7 @@ function! s:clipboard.set(lines, regtype, reg) abort end if s:selections[a:reg].owner > 0 - " The previous provider instance should exit when the new one takes - " ownership, but kill it to be sure we don't fill up the job table. - call jobstop(s:selections[a:reg].owner) + let prev_job = s:selections[a:reg].owner end let s:selections[a:reg] = copy(s:selection) let selection = s:selections[a:reg] @@ -175,13 +173,23 @@ function! s:clipboard.set(lines, regtype, reg) abort call jobsend(jobid, a:lines) call jobclose(jobid, 'stdin') let selection.owner = jobid + let ret = 1 else echohl WarningMsg echomsg 'clipboard: failed to execute: '.(s:copy[a:reg]) echohl None - return 0 + let ret = 1 + endif + + " The previous provider instance should exit when the new one takes + " ownership, but kill it to be sure we don't fill up the job table. + if exists('prev_job') + call timer_start(1000, {... -> + \ jobwait([prev_job], 0)[0] == -1 + \ && jobstop(prev_job)}) endif - return 1 + + return ret endfunction function! provider#clipboard#Call(method, args) abort diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt index 1fb06e169c..c240f08a75 100644 --- a/runtime/doc/intro.txt +++ b/runtime/doc/intro.txt @@ -404,6 +404,9 @@ Mapping <kHome> will not work then. Note: If numlock is on, the |TUI| receives plain ASCII values, so mappings to <k0> - <k9> and <kPoint> will not work. +Note: Nvim supports mapping multibyte chars with modifiers such as `<M-Γ€>`. +Which combinations actually are usable depends on the terminal emulator or GUI. + *<>* Examples are often given in the <> notation. Sometimes this is just to make clear what you need to type, but often it can be typed literally, e.g., with diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index 1440e2ac78..7d213f959b 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -292,6 +292,8 @@ numerical highlight ids to the actual attributes. `bold`: bold text. `underline`: underlined text. The line has `special` color. `undercurl`: undercurled text. The curl has `special` color. + `blend`: Blend level (0-100). Could be used by UIs to support + blending floating windows to the background. For absent color keys the default color should be used. Don't store the default value in the table, rather a sentinel value, so that diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 0fc7c780ca..06957dd77d 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -381,9 +381,14 @@ if(WIN32) list(APPEND NVIM_LINK_LIBRARIES ${WINPTY_LIBRARIES}) endif() +# Use "luv" as imported library, to work around CMake using "-lluv" for +# "luv.so". #10407 +add_library(luv UNKNOWN IMPORTED) +set_property(TARGET luv PROPERTY IMPORTED_LOCATION ${LIBLUV_LIBRARIES}) + # Put these last on the link line, since multiple things may depend on them. list(APPEND NVIM_LINK_LIBRARIES - ${LIBLUV_LIBRARIES} + luv ${LIBUV_LIBRARIES} ${MSGPACK_LIBRARIES} ${LIBVTERM_LIBRARIES} @@ -401,6 +406,7 @@ endif() set(NVIM_EXEC_LINK_LIBRARIES ${NVIM_LINK_LIBRARIES} ${LUA_PREFERRED_LIBRARIES}) +# Add IPO flags (for LTO), or error if CMake does not know the flags. #8654 if(POLICY CMP0069) cmake_policy(SET CMP0069 NEW) endif() @@ -546,19 +552,19 @@ if(CLANG_ASAN_UBSAN) else() set(SANITIZE_RECOVER -fno-sanitize-recover) # Clang 3.5- endif() - set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-DEXITFREE ") - set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "${SANITIZE_RECOVER} -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address -fsanitize=undefined -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/src/.asan-blacklist") + set_property(TARGET nvim APPEND PROPERTY COMPILE_DEFINITIONS EXITFREE) + set_property(TARGET nvim APPEND PROPERTY COMPILE_OPTIONS ${SANITIZE_RECOVER} -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address -fsanitize=undefined -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/src/.asan-blacklist) set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=address -fsanitize=undefined ") elseif(CLANG_MSAN) message(STATUS "Enabling Clang memory sanitizer for nvim.") - set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-DEXITFREE ") - set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer -fno-optimize-sibling-calls ") + set_property(TARGET nvim APPEND PROPERTY COMPILE_DEFINITIONS EXITFREE) + set_property(TARGET nvim APPEND PROPERTY COMPILE_OPTIONS -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer -fno-optimize-sibling-calls) set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=memory -fsanitize-memory-track-origins ") elseif(CLANG_TSAN) message(STATUS "Enabling Clang thread sanitizer for nvim.") - set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-DEXITFREE ") - set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-fsanitize=thread ") - set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-fPIE ") + set_property(TARGET nvim APPEND PROPERTY COMPILE_DEFINITIONS EXITFREE) + set_property(TARGET nvim APPEND PROPERTY COMPILE_OPTIONS -fsanitize=thread) + set_property(TARGET nvim APPEND PROPERTY COMPILE_OPTIONS -fPIE) set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=thread ") endif() diff --git a/src/nvim/README.md b/src/nvim/README.md index 3c956cb2e9..35ca2944e9 100644 --- a/src/nvim/README.md +++ b/src/nvim/README.md @@ -44,28 +44,30 @@ Behavior Sanitizer: UBSan, Memory Sanitizer: MSan, Thread Sanitizer: TSan) is a good way to catch undefined behavior, leaks and other errors as soon as they happen. It's significantly faster than Valgrind. -Requires clang 3.4 or later: +Requires clang 3.4 or later, and `llvm-symbolizer` must be in `$PATH`: clang --version -Build Nvim with sanitizer instrumentation: +Build Nvim with sanitizer instrumentation (choose one): CC=clang make CMAKE_EXTRA_FLAGS="-DCLANG_ASAN_UBSAN=ON" + CC=clang make CMAKE_EXTRA_FLAGS="-DCLANG_MSAN=ON" + CC=clang make CMAKE_EXTRA_FLAGS="-DCLANG_TSAN=ON" Create a directory to store logs: mkdir -p "$HOME/logs" -Enable the sanitizer(s) via these environment variables: +Configure the sanitizer(s) via these environment variables: # Change to detect_leaks=1 to detect memory leaks (slower). export ASAN_OPTIONS="detect_leaks=0:log_path=$HOME/logs/asan" - export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer + export MSAN_OPTIONS="log_path=${HOME}/logs/tsan" + export TSAN_OPTIONS="log_path=${HOME}/logs/tsan" - export MSAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer - export TSAN_OPTIONS="external_symbolizer_path=/usr/lib/llvm-5.0/bin/llvm-symbolizer log_path=${HOME}/logs/tsan" +Logs will be written to `${HOME}/logs/*san.PID` then. -Logs will be written to `${HOME}/logs/*san.PID`. +For more information: https://github.com/google/sanitizers/wiki/SanitizerCommonFlags TUI debugging ------------- diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 41bf0af65b..9f58257e53 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -100,7 +100,7 @@ void raw_line(Integer grid, Integer row, Integer startcol, void event(char *name, Array args, bool *args_consumed) FUNC_API_NOEXPORT; -void win_pos(Integer grid, Integer win, Integer startrow, +void win_pos(Integer grid, Window win, Integer startrow, Integer startcol, Integer width, Integer height) FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; void win_float_pos(Integer grid, Window win, String anchor, Integer anchor_grid, diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index ed6a28bcda..d027eca59a 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -364,7 +364,7 @@ String nvim_command_output(String command, Error *err) }; // redir usually (except :echon) prepends a newline. if (s.data[0] == '\n') { - memmove(s.data, s.data + 1, s.size); + memmove(s.data, s.data + 1, s.size - 1); s.data[s.size - 1] = '\0'; s.size = s.size - 1; } @@ -1071,10 +1071,10 @@ fail: /// disabled. This is useful when displaing a temporary /// float where the text should not be edited. Disables /// 'number', 'relativenumber', 'cursorline', 'cursorcolumn', -/// 'spell' and 'list' options. 'signcolumn' is changed to -/// `auto`. The end-of-buffer region is hidden by setting -/// `eob` flag of 'fillchars' to a space char, and clearing -/// the |EndOfBuffer| region in 'winhighlight'. +/// 'foldcolumn', 'spell' and 'list' options. 'signcolumn' +/// is changed to `auto`. The end-of-buffer region is hidden +/// by setting `eob` flag of 'fillchars' to a space char, +/// and clearing the |EndOfBuffer| region in 'winhighlight'. /// /// top-level window. Currently accepts no other positioning /// configuration together with this. diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 9060a0de82..1dec0beeee 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1900,7 +1900,8 @@ void backslash_halve(char_u *p) /// @param p /// /// @return String with the number of backslashes halved. -char_u* backslash_halve_save(char_u *p) +char_u *backslash_halve_save(const char_u *p) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET { // TODO(philix): simplify and improve backslash_halve_save algorithm char_u *res = vim_strsave(p); diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 2ac429cf9e..b53f9d0aa9 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -141,19 +141,13 @@ struct compl_S { compl_T *cp_next; compl_T *cp_prev; char_u *cp_str; // matched text - char cp_icase; // TRUE or FALSE: ignore case - char cp_equal; // TRUE or FALSE: ins_compl_equal always ok char_u *(cp_text[CPT_COUNT]); // text for the menu char_u *cp_fname; // file containing the match, allocated when - // cp_flags has FREE_FNAME - int cp_flags; // ORIGINAL_TEXT, CONT_S_IPOS or FREE_FNAME + // cp_flags has CP_FREE_FNAME + int cp_flags; // CP_ values int cp_number; // sequence number }; -// flags for ins_compl_add() -#define ORIGINAL_TEXT (1) // the original text when the expansion begun -#define FREE_FNAME (2) - /* * All the current matches are stored in a list. * "compl_first_match" points to the start of the list. @@ -182,18 +176,18 @@ static int compl_no_insert = FALSE; /* FALSE: select & insert static int compl_no_select = FALSE; /* FALSE: select & insert TRUE: noselect */ -static int compl_used_match; // Selected one of the matches. When - // FALSE the match was edited or using - // the longest common string. +static bool compl_used_match; // Selected one of the matches. + // When false the match was edited or using + // the longest common string. static int compl_was_interrupted = FALSE; /* didn't finish finding completions. */ static int compl_restarting = FALSE; /* don't insert match */ -/* When the first completion is done "compl_started" is set. When it's - * FALSE the word to be completed must be located. */ -static int compl_started = FALSE; +// When the first completion is done "compl_started" is set. When it's +// false the word to be completed must be located. +static bool compl_started = false; // Which Ctrl-X mode are we in? static int ctrl_x_mode = CTRL_X_NORMAL; @@ -2062,21 +2056,24 @@ static bool ins_compl_accept_char(int c) return vim_iswordc(c); } -// This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the -// case of the originally typed text is used, and the case of the completed -// text is inferred, ie this tries to work out what case you probably wanted -// the rest of the word to be in -- webb -int ins_compl_add_infercase(char_u *str_arg, int len, int icase, char_u *fname, - int dir, int flags) +/// This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the +/// case of the originally typed text is used, and the case of the completed +/// text is inferred, ie this tries to work out what case you probably wanted +/// the rest of the word to be in -- webb +/// +/// @param[in] cont_s_ipos next ^X<> will set initial_pos +int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname, + int dir, bool cont_s_ipos) + FUNC_ATTR_NONNULL_ARG(1) { char_u *str = str_arg; int i, c; int actual_len; /* Take multi-byte characters */ int actual_compl_length; /* into account. */ int min_len; - int *wca; /* Wide character array. */ - int has_lower = FALSE; - int was_letter = FALSE; + bool has_lower = false; + bool was_letter = false; + int flags = 0; if (p_ic && curbuf->b_p_inf && len > 0) { // Infer case of completed part. @@ -2108,8 +2105,8 @@ int ins_compl_add_infercase(char_u *str_arg, int len, int icase, char_u *fname, min_len = actual_len < actual_compl_length ? actual_len : actual_compl_length; - /* Allocate wide character array for the completion and fill it. */ - wca = xmalloc(actual_len * sizeof(*wca)); + // Allocate wide character array for the completion and fill it. + int *const wca = xmalloc(actual_len * sizeof(*wca)); { const char_u *p = str; for (i = 0; i < actual_len; i++) { @@ -2200,15 +2197,20 @@ int ins_compl_add_infercase(char_u *str_arg, int len, int icase, char_u *fname, str = IObuff; } - return ins_compl_add(str, len, icase, fname, NULL, false, dir, flags, - false, false); + if (cont_s_ipos) { + flags |= CP_CONT_S_IPOS; + } + if (icase) { + flags |= CP_ICASE; + } + + return ins_compl_add(str, len, fname, NULL, false, dir, flags, false); } /// Add a match to the list of matches /// /// @param[in] str Match to add. /// @param[in] len Match length, -1 to use #STRLEN. -/// @param[in] icase Whether case is to be ignored. /// @param[in] fname File name match comes from. May be NULL. /// @param[in] cptext Extra text for popup menu. May be NULL. If not NULL, /// must have exactly #CPT_COUNT items. @@ -2218,21 +2220,20 @@ int ins_compl_add_infercase(char_u *str_arg, int len, int icase, char_u *fname, /// cptext itself will not be freed. /// @param[in] cdir Completion direction. /// @param[in] adup True if duplicate matches are to be accepted. -/// @param[in] equal Match is always accepted by ins_compl_equal. /// /// @return NOTDONE if the given string is already in the list of completions, /// otherwise it is added to the list and OK is returned. FAIL will be /// returned in case of error. static int ins_compl_add(char_u *const str, int len, - const bool icase, char_u *const fname, + char_u *const fname, char_u *const *const cptext, const bool cptext_allocated, - const Direction cdir, int flags, const bool adup, - int equal) + const Direction cdir, int flags_arg, const bool adup) FUNC_ATTR_NONNULL_ARG(1) { compl_T *match; int dir = (cdir == kDirectionNotSet ? compl_direction : cdir); + int flags = flags_arg; os_breakcheck(); #define FREE_CPTEXT(cptext, cptext_allocated) \ @@ -2257,7 +2258,7 @@ static int ins_compl_add(char_u *const str, int len, if (compl_first_match != NULL && !adup) { match = compl_first_match; do { - if (!(match->cp_flags & ORIGINAL_TEXT) + if (!(match->cp_flags & CP_ORIGINAL_TEXT) && STRNCMP(match->cp_str, str, len) == 0 && match->cp_str[len] == NUL) { FREE_CPTEXT(cptext, cptext_allocated); @@ -2276,24 +2277,23 @@ static int ins_compl_add(char_u *const str, int len, */ match = xcalloc(1, sizeof(compl_T)); match->cp_number = -1; - if (flags & ORIGINAL_TEXT) + if (flags & CP_ORIGINAL_TEXT) { match->cp_number = 0; + } match->cp_str = vim_strnsave(str, len); - match->cp_icase = icase; - match->cp_equal = equal; - /* match-fname is: - * - compl_curr_match->cp_fname if it is a string equal to fname. - * - a copy of fname, FREE_FNAME is set to free later THE allocated mem. - * - NULL otherwise. --Acevedo */ + // match-fname is: + // - compl_curr_match->cp_fname if it is a string equal to fname. + // - a copy of fname, CP_FREE_FNAME is set to free later THE allocated mem. + // - NULL otherwise. --Acevedo if (fname != NULL && compl_curr_match != NULL && compl_curr_match->cp_fname != NULL - && STRCMP(fname, compl_curr_match->cp_fname) == 0) + && STRCMP(fname, compl_curr_match->cp_fname) == 0) { match->cp_fname = compl_curr_match->cp_fname; - else if (fname != NULL) { + } else if (fname != NULL) { match->cp_fname = vim_strsave(fname); - flags |= FREE_FNAME; + flags |= CP_FREE_FNAME; } else { match->cp_fname = NULL; } @@ -2339,14 +2339,15 @@ static int ins_compl_add(char_u *const str, int len, /* * Find the longest common string if still doing that. */ - if (compl_get_longest && (flags & ORIGINAL_TEXT) == 0) + if (compl_get_longest && (flags & CP_ORIGINAL_TEXT) == 0) { ins_compl_longest_match(match); + } return OK; } /// Check that "str[len]" matches with "match->cp_str", considering -/// "match->cp_icase". +/// "match->cp_flags". /// /// @param match completion match /// @param str character string to check @@ -2354,10 +2355,10 @@ static int ins_compl_add(char_u *const str, int len, static bool ins_compl_equal(compl_T *match, char_u *str, size_t len) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - if (match->cp_equal) { + if (match->cp_flags & CP_EQUAL) { return true; } - if (match->cp_icase) { + if (match->cp_flags & CP_ICASE) { return STRNICMP(match->cp_str, str, len) == 0; } return STRNCMP(match->cp_str, str, len) == 0; @@ -2384,7 +2385,7 @@ static void ins_compl_longest_match(compl_T *match) * again after redrawing. */ if (!had_match) ins_compl_delete(); - compl_used_match = FALSE; + compl_used_match = false; } else { /* Reduce the text if this match differs from compl_leader. */ p = compl_leader; @@ -2393,7 +2394,9 @@ static void ins_compl_longest_match(compl_T *match) c1 = utf_ptr2char(p); c2 = utf_ptr2char(s); - if (match->cp_icase ? (mb_tolower(c1) != mb_tolower(c2)) : (c1 != c2)) { + if ((match->cp_flags & CP_ICASE) + ? (mb_tolower(c1) != mb_tolower(c2)) + : (c1 != c2)) { break; } MB_PTR_ADV(p); @@ -2414,7 +2417,7 @@ static void ins_compl_longest_match(compl_T *match) ins_compl_delete(); } - compl_used_match = FALSE; + compl_used_match = false; } } @@ -2423,18 +2426,18 @@ static void ins_compl_longest_match(compl_T *match) * Frees matches[]. */ static void ins_compl_add_matches(int num_matches, char_u **matches, int icase) + FUNC_ATTR_NONNULL_ALL { - int i; int add_r = OK; int dir = compl_direction; - for (i = 0; i < num_matches && add_r != FAIL; i++) - if ((add_r = ins_compl_add(matches[i], -1, icase, - NULL, NULL, false, dir, 0, false, - false)) == OK) { + for (int i = 0; i < num_matches && add_r != FAIL; i++) { + if ((add_r = ins_compl_add(matches[i], -1, NULL, NULL, false, dir, + icase ? CP_ICASE : 0, false)) == OK) { // If dir was BACKWARD then honor it just once. dir = FORWARD; } + } FreeWild(num_matches, matches); } @@ -2484,6 +2487,8 @@ void completeopt_was_set(void) */ void set_completion(colnr_T startcol, list_T *list) { + int flags = CP_ORIGINAL_TEXT; + // If already doing completions stop it. if (ctrl_x_mode != CTRL_X_NORMAL) { ins_compl_prep(' '); @@ -2499,8 +2504,11 @@ void set_completion(colnr_T startcol, list_T *list) /* compl_pattern doesn't need to be set */ compl_orig_text = vim_strnsave(get_cursor_line_ptr() + compl_col, compl_length); - if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, false, 0, - ORIGINAL_TEXT, false, false) != OK) { + if (p_ic) { + flags |= CP_ICASE; + } + if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, 0, + flags, false) != OK) { return; } @@ -2508,8 +2516,8 @@ void set_completion(colnr_T startcol, list_T *list) ins_compl_add_list(list); compl_matches = ins_compl_make_cyclic(); - compl_started = TRUE; - compl_used_match = TRUE; + compl_started = true; + compl_used_match = true; compl_cont_status = 0; int save_w_wrow = curwin->w_wrow; int save_w_leftcol = curwin->w_leftcol; @@ -2569,7 +2577,8 @@ static bool pum_enough_matches(void) compl_T *comp = compl_first_match; int i = 0; do { - if (comp == NULL || ((comp->cp_flags & ORIGINAL_TEXT) == 0 && ++i == 2)) { + if (comp == NULL + || ((comp->cp_flags & CP_ORIGINAL_TEXT) == 0 && ++i == 2)) { break; } comp = comp->cp_next; @@ -2614,8 +2623,8 @@ void ins_compl_show_pum(void) { compl_T *compl; compl_T *shown_compl = NULL; - int did_find_shown_match = FALSE; - int shown_match_ok = FALSE; + bool did_find_shown_match = false; + bool shown_match_ok = false; int i; int cur = -1; colnr_T col; @@ -2647,7 +2656,7 @@ void ins_compl_show_pum(void) lead_len = (int)STRLEN(compl_leader); } do { - if ((compl->cp_flags & ORIGINAL_TEXT) == 0 + if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0 && (compl_leader == NULL || ins_compl_equal(compl, compl_leader, lead_len))) { compl_match_arraysize++; @@ -2661,13 +2670,14 @@ void ins_compl_show_pum(void) compl_match_array = xcalloc(compl_match_arraysize, sizeof(pumitem_T)); /* If the current match is the original text don't find the first * match after it, don't highlight anything. */ - if (compl_shown_match->cp_flags & ORIGINAL_TEXT) - shown_match_ok = TRUE; + if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) { + shown_match_ok = true; + } i = 0; compl = compl_first_match; do { - if ((compl->cp_flags & ORIGINAL_TEXT) == 0 + if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0 && (compl_leader == NULL || ins_compl_equal(compl, compl_leader, lead_len))) { if (!shown_match_ok) { @@ -2675,12 +2685,13 @@ void ins_compl_show_pum(void) /* This item is the shown match or this is the * first displayed item after the shown match. */ compl_shown_match = compl; - did_find_shown_match = TRUE; - shown_match_ok = TRUE; - } else - /* Remember this displayed match for when the - * shown match is just below it. */ + did_find_shown_match = true; + shown_match_ok = true; + } else { + // Remember this displayed match for when the + // shown match is just below it. shown_compl = compl; + } cur = i; } @@ -2699,18 +2710,19 @@ void ins_compl_show_pum(void) } if (compl == compl_shown_match) { - did_find_shown_match = TRUE; + did_find_shown_match = true; /* When the original text is the shown match don't set * compl_shown_match. */ - if (compl->cp_flags & ORIGINAL_TEXT) - shown_match_ok = TRUE; + if (compl->cp_flags & CP_ORIGINAL_TEXT) { + shown_match_ok = true; + } if (!shown_match_ok && shown_compl != NULL) { /* The shown match isn't displayed, set it to the * previously displayed match. */ compl_shown_match = shown_compl; - shown_match_ok = TRUE; + shown_match_ok = true; } } compl = compl->cp_next; @@ -2882,8 +2894,8 @@ static void ins_compl_files(int count, char_u **files, int thesaurus, int flags, ptr = find_word_end(ptr); } add_r = ins_compl_add_infercase(regmatch->startp[0], - (int)(ptr - regmatch->startp[0]), - p_ic, files[i], *dir, 0); + (int)(ptr - regmatch->startp[0]), + p_ic, files[i], *dir, false); if (thesaurus) { char_u *wstart; @@ -2914,11 +2926,11 @@ static void ins_compl_files(int count, char_u **files, int thesaurus, int flags, else ptr = find_word_end(ptr); - /* Add the word. Skip the regexp match. */ - if (wstart != regmatch->startp[0]) - add_r = ins_compl_add_infercase(wstart, - (int)(ptr - wstart), - p_ic, files[i], *dir, 0); + // Add the word. Skip the regexp match. + if (wstart != regmatch->startp[0]) { + add_r = ins_compl_add_infercase(wstart, (int)(ptr - wstart), + p_ic, files[i], *dir, false); + } } } if (add_r == OK) @@ -2995,7 +3007,6 @@ static char_u *find_line_end(char_u *ptr) static void ins_compl_free(void) { compl_T *match; - int i; XFREE_CLEAR(compl_pattern); XFREE_CLEAR(compl_leader); @@ -3011,11 +3022,13 @@ static void ins_compl_free(void) match = compl_curr_match; compl_curr_match = compl_curr_match->cp_next; xfree(match->cp_str); - /* several entries may use the same fname, free it just once. */ - if (match->cp_flags & FREE_FNAME) + // several entries may use the same fname, free it just once. + if (match->cp_flags & CP_FREE_FNAME) { xfree(match->cp_fname); - for (i = 0; i < CPT_COUNT; ++i) + } + for (int i = 0; i < CPT_COUNT; i++) { xfree(match->cp_text[i]); + } xfree(match); } while (compl_curr_match != NULL && compl_curr_match != compl_first_match); compl_first_match = compl_curr_match = NULL; @@ -3026,7 +3039,7 @@ static void ins_compl_free(void) static void ins_compl_clear(void) { compl_cont_status = 0; - compl_started = FALSE; + compl_started = false; compl_matches = 0; XFREE_CLEAR(compl_pattern); XFREE_CLEAR(compl_leader); @@ -3095,7 +3108,7 @@ void get_complete_info(list_T *what_list, dict_T *retdict) if (ret == OK && compl_first_match != NULL) { compl_T *match = compl_first_match; do { - if (!(match->cp_flags & ORIGINAL_TEXT)) { + if (!(match->cp_flags & CP_ORIGINAL_TEXT)) { dict_T *di = tv_dict_alloc(); tv_list_append_dict(li, di); @@ -3202,7 +3215,7 @@ static void ins_compl_new_leader(void) ins_compl_del_pum(); ins_compl_delete(); ins_bytes(compl_leader + ins_compl_len()); - compl_used_match = FALSE; + compl_used_match = false; if (compl_started) { ins_compl_set_original_text(compl_leader); @@ -3283,7 +3296,7 @@ static void ins_compl_restart(void) * will stay to the last popup menu and reduce flicker */ update_screen(0); ins_compl_free(); - compl_started = FALSE; + compl_started = false; compl_matches = 0; compl_cont_status = 0; compl_cont_mode = 0; @@ -3293,15 +3306,16 @@ static void ins_compl_restart(void) * Set the first match, the original text. */ static void ins_compl_set_original_text(char_u *str) + FUNC_ATTR_NONNULL_ALL { // Replace the original text entry. - // The ORIGINAL_TEXT flag is either at the first item or might possibly be + // The CP_ORIGINAL_TEXT flag is either at the first item or might possibly be // at the last item for backward completion - if (compl_first_match->cp_flags & ORIGINAL_TEXT) { // safety check + if (compl_first_match->cp_flags & CP_ORIGINAL_TEXT) { // safety check xfree(compl_first_match->cp_str); compl_first_match->cp_str = vim_strsave(str); } else if (compl_first_match->cp_prev != NULL - && (compl_first_match->cp_prev->cp_flags & ORIGINAL_TEXT)) { + && (compl_first_match->cp_prev->cp_flags & CP_ORIGINAL_TEXT)) { xfree(compl_first_match->cp_prev->cp_str); compl_first_match->cp_prev->cp_str = vim_strsave(str); } @@ -3322,7 +3336,7 @@ static void ins_compl_addfrommatch(void) if ((int)STRLEN(p) <= len) { /* the match is too short */ /* When still at the original match use the first entry that matches * the leader. */ - if (compl_shown_match->cp_flags & ORIGINAL_TEXT) { + if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) { p = NULL; for (cp = compl_shown_match->cp_next; cp != NULL && cp != compl_first_match; cp = cp->cp_next) { @@ -3371,8 +3385,7 @@ static bool ins_compl_prep(int c) if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET || (ctrl_x_mode == CTRL_X_NORMAL && !compl_started)) { compl_get_longest = (strstr((char *)p_cot, "longest") != NULL); - compl_used_match = TRUE; - + compl_used_match = true; } if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET) { @@ -3563,7 +3576,7 @@ static bool ins_compl_prep(int c) auto_format(FALSE, TRUE); ins_compl_free(); - compl_started = FALSE; + compl_started = false; compl_matches = 0; if (!shortmess(SHM_COMPLETIONMENU)) { msg_clr_cmdline(); // necessary for "noshowmode" @@ -3672,28 +3685,28 @@ static buf_T *ins_compl_next_buf(buf_T *buf, int flag) } -/* - * Execute user defined complete function 'completefunc' or 'omnifunc', and - * get matches in "matches". - */ +// Execute user defined complete function 'completefunc' or 'omnifunc', and +// get matches in "matches". static void -expand_by_function ( - int type, /* CTRL_X_OMNI or CTRL_X_FUNCTION */ +expand_by_function( + int type, // CTRL_X_OMNI or CTRL_X_FUNCTION char_u *base ) { - list_T *matchlist = NULL; - dict_T *matchdict = NULL; - char_u *funcname; + list_T *matchlist = NULL; + dict_T *matchdict = NULL; + char_u *funcname; pos_T pos; - win_T *curwin_save; - buf_T *curbuf_save; + win_T *curwin_save; + buf_T *curbuf_save; typval_T rettv; const int save_State = State; + assert(curbuf != NULL); funcname = (type == CTRL_X_FUNCTION) ? curbuf->b_p_cfu : curbuf->b_p_ofu; - if (*funcname == NUL) + if (*funcname == NUL) { return; + } // Call 'completefunc' to obtain the list of matches. typval_T args[3]; @@ -3807,10 +3820,9 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir) FUNC_ATTR_NONNULL_ALL { const char *word; - bool icase = false; - bool adup = false; - bool aempty = false; - bool aequal = false; + bool dup = false; + bool empty = false; + int flags = 0; char *(cptext[CPT_COUNT]); if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) { @@ -3822,46 +3834,47 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir) cptext[CPT_USER_DATA] = tv_dict_get_string(tv->vval.v_dict, "user_data", true); - icase = (bool)tv_dict_get_number(tv->vval.v_dict, "icase"); - adup = (bool)tv_dict_get_number(tv->vval.v_dict, "dup"); - aempty = (bool)tv_dict_get_number(tv->vval.v_dict, "empty"); - if (tv_dict_get_string(tv->vval.v_dict, "equal", false) != NULL) { - aequal = tv_dict_get_number(tv->vval.v_dict, "equal"); + if (tv_dict_get_number(tv->vval.v_dict, "icase")) { + flags |= CP_ICASE; + } + dup = (bool)tv_dict_get_number(tv->vval.v_dict, "dup"); + empty = (bool)tv_dict_get_number(tv->vval.v_dict, "empty"); + if (tv_dict_get_string(tv->vval.v_dict, "equal", false) != NULL + && tv_dict_get_number(tv->vval.v_dict, "equal")) { + flags |= CP_EQUAL; } } else { word = (const char *)tv_get_string_chk(tv); memset(cptext, 0, sizeof(cptext)); } - if (word == NULL || (!aempty && *word == NUL)) { + if (word == NULL || (!empty && *word == NUL)) { for (size_t i = 0; i < CPT_COUNT; i++) { xfree(cptext[i]); } return FAIL; } - return ins_compl_add((char_u *)word, -1, icase, NULL, - (char_u **)cptext, true, dir, 0, adup, aequal); + return ins_compl_add((char_u *)word, -1, NULL, + (char_u **)cptext, true, dir, flags, dup); } -/* - * Get the next expansion(s), using "compl_pattern". - * The search starts at position "ini" in curbuf and in the direction - * compl_direction. - * When "compl_started" is FALSE start at that position, otherwise continue - * where we stopped searching before. - * This may return before finding all the matches. - * Return the total number of matches or -1 if still unknown -- Acevedo - */ +// Get the next expansion(s), using "compl_pattern". +// The search starts at position "ini" in curbuf and in the direction +// compl_direction. +// When "compl_started" is false start at that position, otherwise continue +// where we stopped searching before. +// This may return before finding all the matches. +// Return the total number of matches or -1 if still unknown -- Acevedo static int ins_compl_get_exp(pos_T *ini) { static pos_T first_match_pos; static pos_T last_match_pos; - static char_u *e_cpt = (char_u *)""; /* curr. entry in 'complete' */ - static int found_all = FALSE; /* Found all matches of a - certain type. */ - static buf_T *ins_buf = NULL; /* buffer being scanned */ + static char_u *e_cpt = (char_u *)""; // curr. entry in 'complete' + static int found_all = false; // Found all matches of a + // certain type. + static buf_T *ins_buf = NULL; // buffer being scanned - pos_T *pos; - char_u **matches; + pos_T *pos; + char_u **matches; int save_p_scs; bool save_p_ws; int save_p_ic; @@ -3870,12 +3883,14 @@ static int ins_compl_get_exp(pos_T *ini) int len; int found_new_match; int type = ctrl_x_mode; - char_u *ptr; - char_u *dict = NULL; + char_u *ptr; + char_u *dict = NULL; int dict_f = 0; int set_match_pos; int l_ctrl_x_mode = ctrl_x_mode; + assert(curbuf != NULL); + if (!compl_started) { FOR_ALL_BUFFERS(buf) { buf->b_scanned = false; @@ -3899,9 +3914,9 @@ static int ins_compl_get_exp(pos_T *ini) assert(l_ctrl_x_mode == ctrl_x_mode); - /* For ^N/^P pick a new entry from e_cpt if compl_started is off, - * or if found_all says this entry is done. For ^X^L only use the - * entries from 'complete' that look in loaded buffers. */ + // For ^N/^P pick a new entry from e_cpt if compl_started is off, + // or if found_all says this entry is done. For ^X^L only use the + // entries from 'complete' that look in loaded buffers. if ((l_ctrl_x_mode == CTRL_X_NORMAL || CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode)) && (!compl_started || found_all)) { @@ -3923,23 +3938,24 @@ static int ins_compl_get_exp(pos_T *ini) last_match_pos = first_match_pos; type = 0; - /* Remember the first match so that the loop stops when we - * wrap and come back there a second time. */ - set_match_pos = TRUE; + // Remember the first match so that the loop stops when we + // wrap and come back there a second time. + set_match_pos = true; } else if (vim_strchr((char_u *)"buwU", *e_cpt) != NULL && (ins_buf = - ins_compl_next_buf(ins_buf, *e_cpt)) != curbuf) { - /* Scan a buffer, but not the current one. */ - if (ins_buf->b_ml.ml_mfp != NULL) { /* loaded buffer */ - compl_started = TRUE; + ins_compl_next_buf(ins_buf, *e_cpt)) != curbuf) { + // Scan a buffer, but not the current one. + if (ins_buf->b_ml.ml_mfp != NULL) { // loaded buffer + compl_started = true; first_match_pos.col = last_match_pos.col = 0; first_match_pos.lnum = ins_buf->b_ml.ml_line_count + 1; last_match_pos.lnum = 0; type = 0; - } else { /* unloaded buffer, scan like dictionary */ - found_all = TRUE; - if (ins_buf->b_fname == NULL) + } else { // unloaded buffer, scan like dictionary + found_all = true; + if (ins_buf->b_fname == NULL) { continue; + } type = CTRL_X_DICTIONARY; dict = ins_buf->b_fname; dict_f = DICT_EXACT; @@ -3977,7 +3993,7 @@ static int ins_compl_get_exp(pos_T *ini) type = -1; } - /* in any case e_cpt is advanced to the next entry */ + // in any case e_cpt is advanced to the next entry (void)copy_option_part(&e_cpt, IObuff, IOSIZE, ","); found_all = TRUE; @@ -4024,12 +4040,12 @@ static int ins_compl_get_exp(pos_T *ini) break; case CTRL_X_TAGS: - /* set p_ic according to p_ic, p_scs and pat for find_tags(). */ + // set p_ic according to p_ic, p_scs and pat for find_tags(). save_p_ic = p_ic; p_ic = ignorecase(compl_pattern); - /* Find up to TAG_MANY matches. Avoids that an enormous number - * of matches is found when compl_pattern is empty */ + // Find up to TAG_MANY matches. Avoids that an enormous number + // of matches is found when compl_pattern is empty if (find_tags(compl_pattern, &num_matches, &matches, TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP | (l_ctrl_x_mode != CTRL_X_NORMAL ? TAG_VERBOSE : 0), @@ -4041,9 +4057,8 @@ static int ins_compl_get_exp(pos_T *ini) case CTRL_X_FILES: if (expand_wildcards(1, &compl_pattern, &num_matches, &matches, - EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) == OK) { - - /* May change home directory back to "~". */ + EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) == OK) { + // May change home directory back to "~". tilde_replace(compl_pattern, num_matches, matches); ins_compl_add_matches(num_matches, matches, p_fic || p_wic); } @@ -4068,29 +4083,26 @@ static int ins_compl_get_exp(pos_T *ini) ins_compl_add_matches(num_matches, matches, p_ic); break; - default: /* normal ^P/^N and ^X^L */ - /* - * If 'infercase' is set, don't use 'smartcase' here - */ + default: // normal ^P/^N and ^X^L + // If 'infercase' is set, don't use 'smartcase' here save_p_scs = p_scs; assert(ins_buf); if (ins_buf->b_p_inf) p_scs = FALSE; - /* Buffers other than curbuf are scanned from the beginning or the - * end but never from the middle, thus setting nowrapscan in this - * buffers is a good idea, on the other hand, we always set - * wrapscan for curbuf to avoid missing matches -- Acevedo,Webb */ + // Buffers other than curbuf are scanned from the beginning or the + // end but never from the middle, thus setting nowrapscan in this + // buffers is a good idea, on the other hand, we always set + // wrapscan for curbuf to avoid missing matches -- Acevedo,Webb save_p_ws = p_ws; if (ins_buf != curbuf) p_ws = false; else if (*e_cpt == '.') p_ws = true; for (;; ) { - int flags = 0; - - ++msg_silent; /* Don't want messages for wrapscan. */ + bool cont_s_ipos = false; + msg_silent++; // Don't want messages for wrapscan. // CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode) || word-wise search that // has added a word that was at the beginning of the line. if (CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode) @@ -4107,47 +4119,52 @@ static int ins_compl_get_exp(pos_T *ini) } msg_silent--; if (!compl_started || set_match_pos) { - /* set "compl_started" even on fail */ - compl_started = TRUE; + // set "compl_started" even on fail + compl_started = true; first_match_pos = *pos; last_match_pos = *pos; - set_match_pos = FALSE; + set_match_pos = false; } else if (first_match_pos.lnum == last_match_pos.lnum - && first_match_pos.col == last_match_pos.col) + && first_match_pos.col == last_match_pos.col) { found_new_match = FAIL; + } if (found_new_match == FAIL) { if (ins_buf == curbuf) found_all = TRUE; break; } - /* when ADDING, the text before the cursor matches, skip it */ - if ( (compl_cont_status & CONT_ADDING) && ins_buf == curbuf - && ini->lnum == pos->lnum - && ini->col == pos->col) + // when ADDING, the text before the cursor matches, skip it + if ((compl_cont_status & CONT_ADDING) && ins_buf == curbuf + && ini->lnum == pos->lnum + && ini->col == pos->col) { continue; - ptr = ml_get_buf(ins_buf, pos->lnum, FALSE) + pos->col; + } + ptr = ml_get_buf(ins_buf, pos->lnum, false) + pos->col; if (CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode)) { if (compl_cont_status & CONT_ADDING) { - if (pos->lnum >= ins_buf->b_ml.ml_line_count) + if (pos->lnum >= ins_buf->b_ml.ml_line_count) { continue; - ptr = ml_get_buf(ins_buf, pos->lnum + 1, FALSE); - if (!p_paste) + } + ptr = ml_get_buf(ins_buf, pos->lnum + 1, false); + if (!p_paste) { ptr = skipwhite(ptr); + } } len = (int)STRLEN(ptr); } else { - char_u *tmp_ptr = ptr; + char_u *tmp_ptr = ptr; if (compl_cont_status & CONT_ADDING) { tmp_ptr += compl_length; - /* Skip if already inside a word. */ - if (vim_iswordp(tmp_ptr)) + // Skip if already inside a word. + if (vim_iswordp(tmp_ptr)) { continue; - /* Find start of next word. */ + } + // Find start of next word. tmp_ptr = find_word_start(tmp_ptr); } - /* Find end of this word. */ + // Find end of this word. tmp_ptr = find_word_end(tmp_ptr); len = (int)(tmp_ptr - ptr); @@ -4160,15 +4177,16 @@ static int ins_compl_get_exp(pos_T *ini) STRNCPY(IObuff, ptr, len); ptr = ml_get_buf(ins_buf, pos->lnum + 1, false); tmp_ptr = ptr = skipwhite(ptr); - /* Find start of next word. */ + // Find start of next word. tmp_ptr = find_word_start(tmp_ptr); - /* Find end of next word. */ + // Find end of next word. tmp_ptr = find_word_end(tmp_ptr); if (tmp_ptr > ptr) { if (*ptr != ')' && IObuff[len - 1] != TAB) { - if (IObuff[len - 1] != ' ') + if (IObuff[len - 1] != ' ') { IObuff[len++] = ' '; - /* IObuf =~ "\k.* ", thus len >= 2 */ + } + // IObuf =~ "\k.* ", thus len >= 2 if (p_js && (IObuff[len - 2] == '.' || IObuff[len - 2] == '?' @@ -4176,12 +4194,13 @@ static int ins_compl_get_exp(pos_T *ini) IObuff[len++] = ' '; } } - /* copy as much as possible of the new word */ - if (tmp_ptr - ptr >= IOSIZE - len) + // copy as much as possible of the new word + if (tmp_ptr - ptr >= IOSIZE - len) { tmp_ptr = ptr + IOSIZE - len - 1; - STRNCPY(IObuff + len, ptr, tmp_ptr - ptr); + } + STRLCPY(IObuff + len, ptr, IOSIZE - len); len += (int)(tmp_ptr - ptr); - flags |= CONT_S_IPOS; + cont_s_ipos = true; } IObuff[len] = NUL; ptr = IObuff; @@ -4190,9 +4209,9 @@ static int ins_compl_get_exp(pos_T *ini) continue; } } - if (ins_compl_add_infercase(ptr, len, p_ic, - ins_buf == curbuf ? NULL : ins_buf->b_sfname, - 0, flags) != NOTDONE) { + if (ins_compl_add_infercase( + ptr, len, p_ic, ins_buf == curbuf ? NULL : ins_buf->b_sfname, + 0, cont_s_ipos) != NOTDONE) { found_new_match = OK; break; } @@ -4214,27 +4233,28 @@ static int ins_compl_get_exp(pos_T *ini) || found_new_match != FAIL) { if (got_int) break; - /* Fill the popup menu as soon as possible. */ - if (type != -1) + // Fill the popup menu as soon as possible. + if (type != -1) { ins_compl_check_keys(0, false); + } if ((l_ctrl_x_mode != CTRL_X_NORMAL && !CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode)) || compl_interrupted) { break; } - compl_started = TRUE; + compl_started = true; } else { - /* Mark a buffer scanned when it has been scanned completely */ + // Mark a buffer scanned when it has been scanned completely if (type == 0 || type == CTRL_X_PATH_PATTERNS) { assert(ins_buf); ins_buf->b_scanned = true; } - compl_started = FALSE; + compl_started = false; } } - compl_started = TRUE; + compl_started = true; if ((l_ctrl_x_mode == CTRL_X_NORMAL || CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode)) @@ -4242,7 +4262,7 @@ static int ins_compl_get_exp(pos_T *ini) found_new_match = FAIL; } - i = -1; /* total of matches, unknown */ + i = -1; // total of matches, unknown if (found_new_match == FAIL || (l_ctrl_x_mode != CTRL_X_NORMAL && !CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode))) { @@ -4263,7 +4283,7 @@ static int ins_compl_get_exp(pos_T *ini) return i; } -/* Delete the old text being completed. */ +// Delete the old text being completed. static void ins_compl_delete(void) { int col; @@ -4290,10 +4310,7 @@ static void ins_compl_delete(void) static void ins_compl_insert(int in_compl_func) { ins_bytes(compl_shown_match->cp_str + ins_compl_len()); - if (compl_shown_match->cp_flags & ORIGINAL_TEXT) - compl_used_match = FALSE; - else - compl_used_match = TRUE; + compl_used_match = !(compl_shown_match->cp_flags & CP_ORIGINAL_TEXT); dict_T *dict = ins_compl_dict_alloc(compl_shown_match); set_vim_var_dict(VV_COMPLETED_ITEM, dict); @@ -4358,7 +4375,7 @@ ins_compl_next ( compl_T *found_compl = NULL; int found_end = FALSE; int advance; - int started = compl_started; + const bool started = compl_started; /* When user complete function return -1 for findstart which is next * time of 'always', compl_shown_match become NULL. */ @@ -4366,14 +4383,15 @@ ins_compl_next ( return -1; if (compl_leader != NULL - && (compl_shown_match->cp_flags & ORIGINAL_TEXT) == 0) { - /* Set "compl_shown_match" to the actually shown match, it may differ - * when "compl_leader" is used to omit some of the matches. */ + && (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0) { + // Set "compl_shown_match" to the actually shown match, it may differ + // when "compl_leader" is used to omit some of the matches. while (!ins_compl_equal(compl_shown_match, - compl_leader, (int)STRLEN(compl_leader)) + compl_leader, STRLEN(compl_leader)) && compl_shown_match->cp_next != NULL - && compl_shown_match->cp_next != compl_first_match) + && compl_shown_match->cp_next != compl_first_match) { compl_shown_match = compl_shown_match->cp_next; + } /* If we didn't find it searching forward, and compl_shows_dir is * backward, find the last match. */ @@ -4454,14 +4472,15 @@ ins_compl_next ( } found_end = FALSE; } - if ((compl_shown_match->cp_flags & ORIGINAL_TEXT) == 0 + if ((compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0 && compl_leader != NULL && !ins_compl_equal(compl_shown_match, - compl_leader, (int)STRLEN(compl_leader))) - ++todo; - else - /* Remember a matching item. */ + compl_leader, STRLEN(compl_leader))) { + todo++; + } else { + // Remember a matching item. found_compl = compl_shown_match; + } /* Stop at the end of the list when we found a usable match. */ if (found_end) { @@ -4476,7 +4495,7 @@ ins_compl_next ( /* Insert the text of the new completion, or the compl_leader. */ if (compl_no_insert && !started) { ins_bytes(compl_orig_text + ins_compl_len()); - compl_used_match = FALSE; + compl_used_match = false; } else if (insert_match) { if (!compl_get_longest || compl_used_match) { ins_compl_insert(in_compl_func); @@ -4484,7 +4503,7 @@ ins_compl_next ( ins_bytes(compl_leader + ins_compl_len()); } } else { - compl_used_match = FALSE; + compl_used_match = false; } if (!allow_get_expansion) { @@ -4692,6 +4711,7 @@ static int ins_complete(int c, bool enable_pum) int save_w_leftcol; int insert_match; const bool save_did_ai = did_ai; + int flags = CP_ORIGINAL_TEXT; compl_direction = ins_compl_key2dir(c); insert_match = ins_compl_use_match(c); @@ -5019,8 +5039,11 @@ static int ins_complete(int c, bool enable_pum) /* Always add completion for the original text. */ xfree(compl_orig_text); compl_orig_text = vim_strnsave(line + compl_col, compl_length); - if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, false, 0, - ORIGINAL_TEXT, false, false) != OK) { + if (p_ic) { + flags |= CP_ICASE; + } + if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, 0, + flags, false) != OK) { XFREE_CLEAR(compl_pattern); XFREE_CLEAR(compl_orig_text); return FAIL; @@ -5081,13 +5104,14 @@ static int ins_complete(int c, bool enable_pum) } } - if (compl_curr_match->cp_flags & CONT_S_IPOS) + if (compl_curr_match->cp_flags & CP_CONT_S_IPOS) { compl_cont_status |= CONT_S_IPOS; - else + } else { compl_cont_status &= ~CONT_S_IPOS; + } if (edit_submode_extra == NULL) { - if (compl_curr_match->cp_flags & ORIGINAL_TEXT) { + if (compl_curr_match->cp_flags & CP_ORIGINAL_TEXT) { edit_submode_extra = (char_u *)_("Back at original"); edit_submode_highl = HLF_W; } else if (compl_cont_status & CONT_S_IPOS) { diff --git a/src/nvim/edit.h b/src/nvim/edit.h index 433a941295..92dab37a70 100644 --- a/src/nvim/edit.h +++ b/src/nvim/edit.h @@ -13,6 +13,15 @@ #define CPT_USER_DATA 4 // "user data" #define CPT_COUNT 5 // Number of entries +// values for cp_flags +typedef enum { + CP_ORIGINAL_TEXT = 1, // the original text when the expansion begun + CP_FREE_FNAME = 2, // cp_fname is allocated + CP_CONT_S_IPOS = 4, // use CONT_S_IPOS for compl_cont_status + CP_EQUAL = 8, // ins_compl_equal() always returns true + CP_ICASE = 16, // ins_compl_equal ignores case +} cp_flags_T; + typedef int (*IndentGetter)(void); /* Values for in_cinkeys() */ diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1d221bb600..8f6d6cd55e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1989,7 +1989,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, } } if (p != NULL) { - vim_setenv(name, p); + os_setenv(name, p, 1); if (STRICMP(name, "HOME") == 0) { init_homedir(); } else if (didset_vim && STRICMP(name, "VIM") == 0) { @@ -2353,14 +2353,15 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } if (lp->ll_di == NULL) { - /* Can't add "v:" variable. */ - if (lp->ll_dict == &vimvardict) { + // Can't add "v:" or "a:" variable. + if (lp->ll_dict == &vimvardict + || &lp->ll_dict->dv_hashtab == get_funccal_args_ht()) { EMSG2(_(e_illvar), name); tv_clear(&var1); return NULL; } - /* Key does not exist in dict: may need to add it. */ + // Key does not exist in dict: may need to add it. if (*p == '[' || *p == '.' || unlet) { if (!quiet) { emsgf(_(e_dictkey), key); @@ -13640,10 +13641,6 @@ static void f_pumvisible(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_pyeval(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - if (p_pyx == 0) { - p_pyx = 2; - } - script_host_eval("python", argvars, rettv); } @@ -13652,10 +13649,6 @@ static void f_pyeval(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_py3eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - if (p_pyx == 0) { - p_pyx = 3; - } - script_host_eval("python3", argvars, rettv); } @@ -15359,7 +15352,7 @@ static void f_setenv(typval_T *argvars, typval_T *rettv, FunPtr fptr) && argvars[1].vval.v_number == kSpecialVarNull) { os_unsetenv(name); } else { - vim_setenv(name, tv_get_string_buf(&argvars[1], valbuf)); + os_setenv(name, tv_get_string_buf(&argvars[1], valbuf), 1); } } @@ -20497,8 +20490,8 @@ static void set_var_const(const char *name, const size_t name_len, } tv_clear(&v->di_tv); } else { // Add a new variable. - // Can't add "v:" variable. - if (ht == &vimvarht) { + // Can't add "v:" or "a:" variable. + if (ht == &vimvarht || ht == get_funccal_args_ht()) { emsgf(_(e_illvar), name); return; } @@ -22630,7 +22623,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, name = v->di_key; STRCPY(name, "self"); #endif - v->di_flags = DI_FLAGS_RO + DI_FLAGS_FIX; + v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; tv_dict_add(&fc->l_vars, v); v->di_tv.v_type = VAR_DICT; v->di_tv.v_lock = 0; @@ -22646,6 +22639,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, init_var_dict(&fc->l_avars, &fc->l_avars_var, VAR_SCOPE); add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], "0", (varnumber_T)(argcount - fp->uf_args.ga_len)); + fc->l_avars.dv_lock = VAR_FIXED; // Use "name" to avoid a warning from some compiler that checks the // destination size. v = (dictitem_T *)&fc->fixvar[fixvar_idx++]; diff --git a/src/nvim/event/stream.c b/src/nvim/event/stream.c index 7c8014dead..d1a53fa4b6 100644 --- a/src/nvim/event/stream.c +++ b/src/nvim/event/stream.c @@ -97,6 +97,13 @@ void stream_close(Stream *stream, stream_close_cb on_stream_close, void *data) stream->close_cb = on_stream_close; stream->close_cb_data = data; +#ifdef WIN32 + if (UV_TTY == uv_guess_handle(stream->fd)) { + // Undo UV_TTY_MODE_RAW from stream_init(). #10801 + uv_tty_set_mode(&stream->uv.tty, UV_TTY_MODE_NORMAL); + } +#endif + if (!stream->pending_reqs) { stream_close_handle(stream); } diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index e256351de2..a0fbde008b 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1392,9 +1392,6 @@ do_shell( msg_row = Rows - 1; msg_col = 0; - // display any error messages now - display_errors(); - apply_autocmds(EVENT_SHELLCMDPOST, NULL, NULL, FALSE, curbuf); } diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 7c28461f4c..df23d0630a 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2819,10 +2819,10 @@ void ex_packadd(exarg_T *eap) /// ":options" void ex_options(exarg_T *eap) { - vim_setenv("OPTWIN_CMD", cmdmod.tab ? "tab" : ""); - vim_setenv("OPTWIN_CMD", - cmdmod.tab ? "tab" : - (cmdmod.split & WSP_VERT) ? "vert" : ""); + os_setenv("OPTWIN_CMD", cmdmod.tab ? "tab" : "", 1); + os_setenv("OPTWIN_CMD", + cmdmod.tab ? "tab" : + (cmdmod.split & WSP_VERT) ? "vert" : "", 1); cmd_source((char_u *)SYS_OPTWIN_FILE, NULL); } @@ -2830,9 +2830,9 @@ void ex_options(exarg_T *eap) void init_pyxversion(void) { if (p_pyx == 0) { - if (!eval_has_provider("python3")) { + if (eval_has_provider("python3")) { p_pyx = 3; - } else if (!eval_has_provider("python")) { + } else if (eval_has_provider("python")) { p_pyx = 2; } } @@ -3927,19 +3927,19 @@ void ex_language(exarg_T *eap) _nl_msg_cat_cntr++; #endif // Reset $LC_ALL, otherwise it would overrule everything. - vim_setenv("LC_ALL", ""); + os_setenv("LC_ALL", "", 1); if (what != LC_TIME) { // Tell gettext() what to translate to. It apparently doesn't // use the currently effective locale. if (what == LC_ALL) { - vim_setenv("LANG", (char *)name); + os_setenv("LANG", (char *)name, 1); // Clear $LANGUAGE because GNU gettext uses it. - vim_setenv("LANGUAGE", ""); + os_setenv("LANGUAGE", "", 1); } if (what != LC_CTYPE) { - vim_setenv("LC_MESSAGES", (char *)name); + os_setenv("LC_MESSAGES", (char *)name, 1); set_helplang_default((char *)name); } } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 0f345df22b..b880ec4f6d 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5266,8 +5266,7 @@ static void ex_command(exarg_T *eap) } else if (!ASCII_ISUPPER(*name)) { EMSG(_("E183: User defined commands must start with an uppercase letter")); return; - } else if ((name_len == 1 && *name == 'X') - || (name_len <= 4 && STRNCMP(name, "Next", name_len) == 0)) { + } else if (name_len <= 4 && STRNCMP(name, "Next", name_len) == 0) { EMSG(_("E841: Reserved name, cannot be used for user defined command")); return; } else { @@ -8432,13 +8431,15 @@ static void ex_pedit(exarg_T *eap) { win_T *curwin_save = curwin; + // Open the preview window or popup and make it the current window. g_do_tagpreview = p_pvh; prepare_tagpreview(true); - keep_help_flag = bt_help(curwin_save->w_buffer); + + // Edit the file. do_exedit(eap, NULL); - keep_help_flag = FALSE; + if (curwin != curwin_save && win_valid(curwin_save)) { - /* Return cursor to where we were */ + // Return cursor to where we were validate_cursor(); redraw_later(VALID); win_enter(curwin_save, true); @@ -9181,7 +9182,7 @@ makeopens( // Take care of tab-local working directories if applicable if (tp->tp_localdir) { - if (fputs("if has('nvim') | tcd ", fd) < 0 + if (fputs("if exists(':tcd') == 2 | tcd ", fd) < 0 || ses_put_fname(fd, tp->tp_localdir, &ssop_flags) == FAIL || fputs(" | endif", fd) < 0 || put_eol(fd) == FAIL) { diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 38432a34db..e8d650accf 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -3483,6 +3483,7 @@ void compute_cmdrow(void) cmdline_row = wp->w_winrow + wp->w_height + wp->w_status_height; } + lines_left = cmdline_row; } static void cursorcmd(void) @@ -4211,24 +4212,24 @@ static int showmatches(expand_T *xp, int wildmenu) || xp->xp_context == EXPAND_BUFFERS) { /* highlight directories */ if (xp->xp_numfiles != -1) { - char_u *halved_slash; - char_u *exp_path; - - /* Expansion was done before and special characters - * were escaped, need to halve backslashes. Also - * $HOME has been replaced with ~/. */ - exp_path = expand_env_save_opt(files_found[k], TRUE); - halved_slash = backslash_halve_save( - exp_path != NULL ? exp_path : files_found[k]); + // Expansion was done before and special characters + // were escaped, need to halve backslashes. Also + // $HOME has been replaced with ~/. + char_u *exp_path = expand_env_save_opt(files_found[k], true); + char_u *path = exp_path != NULL ? exp_path : files_found[k]; + char_u *halved_slash = backslash_halve_save(path); j = os_isdir(halved_slash); xfree(exp_path); - xfree(halved_slash); - } else - /* Expansion was done here, file names are literal. */ + if (halved_slash != path) { + xfree(halved_slash); + } + } else { + // Expansion was done here, file names are literal. j = os_isdir(files_found[k]); - if (showtail) + } + if (showtail) { p = L_SHOWFILE(k); - else { + } else { home_replace(NULL, files_found[k], NameBuff, MAXPATHL, TRUE); p = NameBuff; diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index d2620376c6..8b19257d3d 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -777,9 +777,8 @@ readfile( fenc = curbuf->b_p_fenc; // use format from buffer fenc_alloced = false; } else { - fenc_next = p_fencs; /* try items in 'fileencodings' */ - fenc = next_fenc(&fenc_next); - fenc_alloced = true; + fenc_next = p_fencs; // try items in 'fileencodings' + fenc = next_fenc(&fenc_next, &fenc_alloced); } /* @@ -869,8 +868,7 @@ retry: if (fenc_alloced) xfree(fenc); if (fenc_next != NULL) { - fenc = next_fenc(&fenc_next); - fenc_alloced = (fenc_next != NULL); + fenc = next_fenc(&fenc_next, &fenc_alloced); } else { fenc = (char_u *)""; fenc_alloced = false; @@ -2082,19 +2080,19 @@ void set_forced_fenc(exarg_T *eap) } } -/* - * Find next fileencoding to use from 'fileencodings'. - * "pp" points to fenc_next. It's advanced to the next item. - * When there are no more items, an empty string is returned and *pp is set to - * NULL. - * When *pp is not set to NULL, the result is in allocated memory. - */ -static char_u *next_fenc(char_u **pp) +// Find next fileencoding to use from 'fileencodings'. +// "pp" points to fenc_next. It's advanced to the next item. +// When there are no more items, an empty string is returned and *pp is set to +// NULL. +// When *pp is not set to NULL, the result is in allocated memory and "alloced" +// is set to true. +static char_u *next_fenc(char_u **pp, bool *alloced) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET { char_u *p; char_u *r; + *alloced = false; if (**pp == NUL) { *pp = NULL; return (char_u *)""; @@ -2110,6 +2108,7 @@ static char_u *next_fenc(char_u **pp) xfree(r); r = p; } + *alloced = true; return r; } diff --git a/src/nvim/fold.c b/src/nvim/fold.c index ad0bfe29e2..5ce953e626 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -1645,19 +1645,22 @@ deleteFoldMarkers( foldendmarkerlen); } -/* foldDelMarker() {{{2 */ -/* - * Delete marker "marker[markerlen]" at the end of line "lnum". - * Delete 'commentstring' if it matches. - * If the marker is not found, there is no error message. Could a missing - * close-marker. - */ +// foldDelMarker() {{{2 +// +// Delete marker "marker[markerlen]" at the end of line "lnum". +// Delete 'commentstring' if it matches. +// If the marker is not found, there is no error message. Could be a missing +// close-marker. static void foldDelMarker(linenr_T lnum, char_u *marker, size_t markerlen) { char_u *newline; char_u *cms = curbuf->b_p_cms; char_u *cms2; + // end marker may be missing and fold extends below the last line + if (lnum > curbuf->b_ml.ml_line_count) { + return; + } char_u *line = ml_get(lnum); for (char_u *p = line; *p != NUL; ++p) { if (STRNCMP(p, marker, markerlen) != 0) { @@ -2426,15 +2429,18 @@ static linenr_T foldUpdateIEMSRecurse( * lvl >= level: fold continues below "bot" */ - /* Current fold at least extends until lnum. */ + // Current fold at least extends until lnum. if (fp->fd_len < flp->lnum - fp->fd_top) { fp->fd_len = flp->lnum - fp->fd_top; fp->fd_small = kNone; fold_changed = true; + } else if (fp->fd_top + fp->fd_len > linecount) { + // running into the end of the buffer (deleted last line) + fp->fd_len = linecount - fp->fd_top + 1; } - /* Delete contained folds from the end of the last one found until where - * we stopped looking. */ + // Delete contained folds from the end of the last one found until where + // we stopped looking. foldRemove(&fp->fd_nested, startlnum2 - fp->fd_top, flp->lnum - 1 - fp->fd_top); diff --git a/src/nvim/globals.h b/src/nvim/globals.h index b095e759d9..3bdbff79b4 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -895,11 +895,6 @@ EXTERN disptick_T display_tick INIT(= 0); * cursor position in Insert mode. */ EXTERN linenr_T spell_redraw_lnum INIT(= 0); -#ifdef USE_MCH_ERRMSG -// Grow array to collect error messages in until they can be displayed. -EXTERN garray_T error_ga INIT(= GA_EMPTY_INIT_VALUE); -#endif - /* * The error messages that can be shared are included here. diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index f11880cb2b..3a61409dfe 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -233,6 +233,7 @@ int hl_get_underline(void) .rgb_fg_color = -1, .rgb_bg_color = -1, .rgb_sp_color = -1, + .hl_blend = -1, }, .kind = kHlUI, .id1 = 0, @@ -427,6 +428,8 @@ int hl_blend_attrs(int back_attr, int front_attr, bool *through) cattrs.rgb_bg_color = rgb_blend(ratio, battrs.rgb_bg_color, fattrs.rgb_bg_color); + cattrs.hl_blend = -1; // blend property was consumed + HlKind kind = *through ? kHlBlendThrough : kHlBlend; id = get_attr_entry((HlEntry){ .attr = cattrs, .kind = kind, .id1 = back_attr, .id2 = front_attr }); @@ -614,6 +617,10 @@ Dictionary hlattrs2dict(HlAttrs ae, bool use_rgb) } } + if (ae.hl_blend > -1) { + PUT(hl, "blend", INTEGER_OBJ(ae.hl_blend)); + } + return hl; } diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index 9145813525..27052da9d8 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -604,7 +604,7 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, // Anything accepted, like <C-?>. // <C-"> or <M-"> are not special in strings as " is // the string delimiter. With a backslash it works: <M-\"> - if (end - bp > l && !(in_string && bp[1] == '"') && bp[2] == '>') { + if (end - bp > l && !(in_string && bp[1] == '"') && bp[l+1] == '>') { bp += l; } else if (end - bp > 2 && in_string && bp[1] == '\\' && bp[2] == '"' && bp[3] == '>') { diff --git a/src/nvim/main.c b/src/nvim/main.c index 6bb3c37b92..e7c45b1a7b 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -237,7 +237,7 @@ int main(int argc, char **argv) char **argv = xmalloc((size_t)argc * sizeof(char *)); for (int i = 0; i < argc; i++) { char *buf = NULL; - utf16_to_utf8(argv_w[i], &buf); + utf16_to_utf8(argv_w[i], -1, &buf); assert(buf); argv[i] = buf; } @@ -396,8 +396,7 @@ int main(int argc, char **argv) mch_exit(0); } - // Set a few option defaults after reading vimrc files: 'title', 'icon', - // 'shellpipe', 'shellredir'. + // Set some option defaults after reading vimrc files. set_init_3(); TIME_MSG("inits 3"); @@ -707,22 +706,14 @@ static void init_locale(void) setlocale(LC_NUMERIC, "C"); # endif -# ifdef LOCALE_INSTALL_DIR // gnu/linux standard: $prefix/share/locale - bindtextdomain(PROJECT_NAME, LOCALE_INSTALL_DIR); -# else // old vim style: $runtime/lang - { - char_u *p; - - // expand_env() doesn't work yet, because g_chartab[] is not - // initialized yet, call vim_getenv() directly - p = (char_u *)vim_getenv("VIMRUNTIME"); - if (p != NULL && *p != NUL) { - vim_snprintf((char *)NameBuff, MAXPATHL, "%s/lang", p); - bindtextdomain(PROJECT_NAME, (char *)NameBuff); - } - xfree(p); - } -# endif + char localepath[MAXPATHL] = { 0 }; + snprintf(localepath, sizeof(localepath), "%s", get_vim_var_str(VV_PROGPATH)); + char *tail = (char *)path_tail_with_sep((char_u *)localepath); + *tail = NUL; + tail = (char *)path_tail((char_u *)localepath); + xstrlcpy(tail, "share/locale", + sizeof(localepath) - (size_t)(tail - localepath)); + bindtextdomain(PROJECT_NAME, localepath); textdomain(PROJECT_NAME); TIME_MSG("locale set"); } diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index fae7635d34..29b8dc0ef2 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -1356,83 +1356,86 @@ static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1, # define CP_UTF8 65001 /* magic number from winnls.h */ #endif -/// Reassigns `strw` to a new, allocated pointer to a UTF16 string. -int utf8_to_utf16(const char *str, wchar_t **strw) +/// Converts string from UTF-8 to UTF-16. +/// +/// @param utf8 UTF-8 string. +/// @param utf8len Length of `utf8`. May be -1 if `utf8` is NUL-terminated. +/// @param utf16[out,allocated] NUL-terminated UTF-16 string, or NULL on error +/// @return 0 on success, or libuv error code +int utf8_to_utf16(const char *utf8, int utf8len, wchar_t **utf16) FUNC_ATTR_NONNULL_ALL { - ssize_t wchar_len = 0; - - // Compute the length needed to store the converted widechar string. - wchar_len = MultiByteToWideChar(CP_UTF8, - 0, // dwFlags: must be 0 for utf8 - str, // lpMultiByteStr: string to convert - -1, // -1 => process up to NUL - NULL, // lpWideCharStr: converted string - 0); // 0 => return length, don't convert - if (wchar_len == 0) { - return GetLastError(); - } - - ssize_t buf_sz = wchar_len * sizeof(wchar_t); - - if (buf_sz == 0) { - *strw = NULL; - return 0; + // Compute the length needed for the converted UTF-16 string. + int bufsize = MultiByteToWideChar(CP_UTF8, + 0, // dwFlags: must be 0 for UTF-8 + utf8, // -1: process up to NUL + utf8len, + NULL, + 0); // 0: get length, don't convert + if (bufsize == 0) { + *utf16 = NULL; + return uv_translate_sys_error(GetLastError()); } - char *buf = xmalloc(buf_sz); - char *pos = buf; + // Allocate the destination buffer adding an extra byte for the terminating + // NULL. If `utf8len` is not -1 MultiByteToWideChar will not add it, so + // we do it ourselves always, just in case. + *utf16 = xmalloc(sizeof(wchar_t) * (bufsize + 1)); - int r = MultiByteToWideChar(CP_UTF8, - 0, - str, - -1, - (wchar_t *)pos, - wchar_len); - assert(r == wchar_len); - if (r != wchar_len) { - EMSG2("MultiByteToWideChar failed: %d", r); + // Convert to UTF-16. + bufsize = MultiByteToWideChar(CP_UTF8, 0, utf8, utf8len, *utf16, bufsize); + if (bufsize == 0) { + XFREE_CLEAR(*utf16); + return uv_translate_sys_error(GetLastError()); } - *strw = (wchar_t *)pos; + (*utf16)[bufsize] = L'\0'; return 0; } -/// Reassigns `str` to a new, allocated pointer to a UTF8 string. -int utf16_to_utf8(const wchar_t *strw, char **str) +/// Converts string from UTF-16 to UTF-8. +/// +/// @param utf16 UTF-16 string. +/// @param utf16len Length of `utf16`. May be -1 if `utf16` is NUL-terminated. +/// @param utf8[out,allocated] NUL-terminated UTF-8 string, or NULL on error +/// @return 0 on success, or libuv error code +int utf16_to_utf8(const wchar_t *utf16, int utf16len, char **utf8) FUNC_ATTR_NONNULL_ALL { - *str = NULL; - // Compute the space required to store the string as UTF-8. - DWORD utf8_len = WideCharToMultiByte(CP_UTF8, - 0, - strw, - -1, - NULL, - 0, - NULL, - NULL); - if (utf8_len == 0) { - return GetLastError(); - } - - *str = xmallocz(utf8_len); + // Compute the space needed for the converted UTF-8 string. + DWORD bufsize = WideCharToMultiByte(CP_UTF8, + 0, + utf16, + utf16len, + NULL, + 0, + NULL, + NULL); + if (bufsize == 0) { + *utf8 = NULL; + return uv_translate_sys_error(GetLastError()); + } + + // Allocate the destination buffer adding an extra byte for the terminating + // NULL. If `utf16len` is not -1 WideCharToMultiByte will not add it, so + // we do it ourselves always, just in case. + *utf8 = xmalloc(bufsize + 1); // Convert to UTF-8. - utf8_len = WideCharToMultiByte(CP_UTF8, - 0, - strw, - -1, - *str, - utf8_len, - NULL, - NULL); - if (utf8_len == 0) { - XFREE_CLEAR(*str); - return GetLastError(); - } - (*str)[utf8_len] = '\0'; - + bufsize = WideCharToMultiByte(CP_UTF8, + 0, + utf16, + utf16len, + *utf8, + bufsize, + NULL, + NULL); + if (bufsize == 0) { + XFREE_CLEAR(*utf8); + return uv_translate_sys_error(GetLastError()); + } + + (*utf8)[bufsize] = '\0'; return 0; } diff --git a/src/nvim/message.c b/src/nvim/message.c index 12da48347e..9bea9f5c4a 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -582,9 +582,25 @@ static bool emsg_multiline(const char *s, bool multiline) } redir_write(s, strlen(s)); } + + // Log (silent) errors as debug messages. + if (sourcing_name != NULL && sourcing_lnum != 0) { + DLOG("(:silent) %s (%s (line %ld))", + s, sourcing_name, (long)sourcing_lnum); + } else { + DLOG("(:silent) %s", s); + } + return true; } + // Log editor errors as INFO. + if (sourcing_name != NULL && sourcing_lnum != 0) { + ILOG("%s (%s (line %ld))", s, sourcing_name, (long)sourcing_lnum); + } else { + ILOG("%s", s); + } + ex_exitval = 1; // Reset msg_silent, an error causes messages to be switched back on. @@ -2305,18 +2321,19 @@ int msg_use_printf(void) static void msg_puts_printf(const char *str, const ptrdiff_t maxlen) { const char *s = str; - char buf[4]; + char buf[7]; char *p; while ((maxlen < 0 || s - str < maxlen) && *s != NUL) { + int len = utf_ptr2len((const char_u *)s); if (!(silent_mode && p_verbose == 0)) { // NL --> CR NL translation (for Unix, not for "--version") p = &buf[0]; if (*s == '\n' && !info_message) { *p++ = '\r'; } - *p++ = *s; - *p = '\0'; + memcpy(p, s, len); + *(p + len) = '\0'; if (info_message) { mch_msg(buf); } else { @@ -2324,21 +2341,22 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen) } } + int cw = utf_char2cells(utf_ptr2char((const char_u *)s)); // primitive way to compute the current column if (cmdmsg_rl) { if (*s == '\r' || *s == '\n') { msg_col = Columns - 1; } else { - msg_col--; + msg_col -= cw; } } else { if (*s == '\r' || *s == '\n') { msg_col = 0; } else { - msg_col++; + msg_col += cw; } } - s++; + s += len; } msg_didout = true; // assume that line is not empty } @@ -2554,81 +2572,34 @@ static int do_more_prompt(int typed_char) return retval; } -#if defined(USE_MCH_ERRMSG) - -#ifdef mch_errmsg -# undef mch_errmsg -#endif -#ifdef mch_msg -# undef mch_msg -#endif - -/* - * Give an error message. To be used when the screen hasn't been initialized - * yet. When stderr can't be used, collect error messages until the GUI has - * started and they can be displayed in a message box. - */ -void mch_errmsg(const char *const str) - FUNC_ATTR_NONNULL_ALL +#if defined(WIN32) +void mch_errmsg(char *str) { -#ifdef UNIX - /* On Unix use stderr if it's a tty. - * When not going to start the GUI also use stderr. - * On Mac, when started from Finder, stderr is the console. */ - if (os_isatty(2)) { - fprintf(stderr, "%s", str); - return; - } -#endif - - /* avoid a delay for a message that isn't there */ - emsg_on_display = FALSE; - - const size_t len = strlen(str) + 1; - if (error_ga.ga_data == NULL) { - ga_set_growsize(&error_ga, 80); - error_ga.ga_itemsize = 1; - } - ga_grow(&error_ga, len); - memmove(error_ga.ga_data + error_ga.ga_len, str, len); -#ifdef UNIX - /* remove CR characters, they are displayed */ - { - char_u *p; - - p = (char_u *)error_ga.ga_data + error_ga.ga_len; - for (;; ) { - p = vim_strchr(p, '\r'); - if (p == NULL) - break; - *p = ' '; - } + assert(str != NULL); + wchar_t *utf16str; + int r = utf8_to_utf16(str, -1, &utf16str); + if (r != 0) { + fprintf(stderr, "utf8_to_utf16 failed: %d", r); + } else { + fwprintf(stderr, L"%ls", utf16str); + xfree(utf16str); } -#endif - --len; /* don't count the NUL at the end */ - error_ga.ga_len += len; } -/* - * Give a message. To be used when the screen hasn't been initialized yet. - * When there is no tty, collect messages until the GUI has started and they - * can be displayed in a message box. - */ +// Give a message. To be used when the UI is not initialized yet. void mch_msg(char *str) { -#ifdef UNIX - /* On Unix use stdout if we have a tty. This allows "vim -h | more" and - * uses mch_errmsg() when started from the desktop. - * When not going to start the GUI also use stdout. - * On Mac, when started from Finder, stderr is the console. */ - if (os_isatty(2)) { - printf("%s", str); - return; + assert(str != NULL); + wchar_t *utf16str; + int r = utf8_to_utf16(str, -1, &utf16str); + if (r != 0) { + fprintf(stderr, "utf8_to_utf16 failed: %d", r); + } else { + wprintf(L"%ls", utf16str); + xfree(utf16str); } -# endif - mch_errmsg(str); } -#endif /* USE_MCH_ERRMSG */ +#endif // WIN32 /* * Put a character on the screen at the current message position and advance diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 44e2c7df5f..bb95cd5737 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -61,7 +61,7 @@ #ifdef INCLUDE_GENERATED_DECLARATIONS # include "misc1.c.generated.h" #endif -/* All user names (for ~user completion as done by shell). */ +// All user names (for ~user completion as done by shell). static garray_T ga_users = GA_EMPTY_INIT_VALUE; /* diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 73841cf449..bfd91e688e 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -7827,13 +7827,15 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) // 'virtualedit' and past the end of the line, we use the 'c' operator in // do_put(), which requires the visual selection to still be active. if (!VIsual_active || VIsual_mode == 'V' || regname != '.') { - // Now delete the selected text. + // Now delete the selected text. Avoid messages here. cap->cmdchar = 'd'; cap->nchar = NUL; cap->oap->regname = NUL; + msg_silent++; nv_operator(cap); do_pending_operator(cap, 0, false); empty = (curbuf->b_ml.ml_flags & ML_EMPTY); + msg_silent--; // delete PUT_LINE_BACKWARD; cap->oap->regname = regname; @@ -7882,6 +7884,7 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) * line that needs to be deleted now. */ if (empty && *ml_get(curbuf->b_ml.ml_line_count) == NUL) { ml_delete(curbuf->b_ml.ml_line_count, true); + deleted_lines(curbuf->b_ml.ml_line_count + 1, 1); /* If the cursor was in that line, move it to the end of the last * line. */ diff --git a/src/nvim/option.c b/src/nvim/option.c index 40c1358fa5..699f17acc5 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1001,45 +1001,35 @@ void set_init_2(bool headless) p_window = Rows - 1; } set_number_default("window", Rows - 1); - parse_shape_opt(SHAPE_CURSOR); // set cursor shapes from 'guicursor' (void)parse_printoptions(); // parse 'printoptions' default value } -/* - * Initialize the options, part three: After reading the .vimrc - */ +/// Initialize the options, part three: After reading the .vimrc void set_init_3(void) { + parse_shape_opt(SHAPE_CURSOR); // set cursor shapes from 'guicursor' + // Set 'shellpipe' and 'shellredir', depending on the 'shell' option. // This is done after other initializations, where 'shell' might have been // set, but only if they have not been set before. - int idx_srr; - int do_srr; - int idx_sp; - int do_sp; - - idx_srr = findoption("srr"); - if (idx_srr < 0) { - do_srr = false; - } else { - do_srr = !(options[idx_srr].flags & P_WAS_SET); - } - idx_sp = findoption("sp"); - if (idx_sp < 0) { - do_sp = false; - } else { - do_sp = !(options[idx_sp].flags & P_WAS_SET); - } + int idx_srr = findoption("srr"); + int do_srr = (idx_srr < 0) + ? false + : !(options[idx_srr].flags & P_WAS_SET); + int idx_sp = findoption("sp"); + int do_sp = (idx_sp < 0) + ? false + : !(options[idx_sp].flags & P_WAS_SET); size_t len = 0; char_u *p = (char_u *)invocation_path_tail(p_sh, &len); p = vim_strnsave(p, len); { - /* - * Default for p_sp is "| tee", for p_srr is ">". - * For known shells it is changed here to include stderr. - */ + // + // Default for p_sp is "| tee", for p_srr is ">". + // For known shells it is changed here to include stderr. + // if ( fnamecmp(p, "csh") == 0 || fnamecmp(p, "tcsh") == 0 ) { @@ -1081,7 +1071,7 @@ void set_init_3(void) } } - set_title_defaults(); + set_title_defaults(); // 'title', 'icon' } /* @@ -2609,11 +2599,11 @@ did_set_string_option( } else if (varp == &p_hf) { // 'helpfile' // May compute new values for $VIM and $VIMRUNTIME if (didset_vim) { - vim_setenv("VIM", ""); + os_setenv("VIM", "", 1); didset_vim = false; } if (didset_vimruntime) { - vim_setenv("VIMRUNTIME", ""); + os_setenv("VIMRUNTIME", "", 1); didset_vimruntime = false; } } else if (varp == &curwin->w_p_cc) { // 'colorcolumn' @@ -6778,7 +6768,7 @@ void vimrc_found(char_u *fname, char_u *envname) // Set $MYVIMRC to the first vimrc file found. p = FullName_save((char *)fname, false); if (p != NULL) { - vim_setenv((char *)envname, p); + os_setenv((char *)envname, p, 1); xfree(p); } } else { diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 33b67a8116..f5dbf0694e 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -178,7 +178,7 @@ char *os_getenvname_at_index(size_t index) for (wchar_t *it = env; *it != L'\0' || *(it + 1) != L'\0'; it++) { if (index == current_index) { char *utf8_str; - int conversion_result = utf16_to_utf8(it, &utf8_str); + int conversion_result = utf16_to_utf8(it, -1, &utf8_str); if (conversion_result != 0) { EMSG2("utf16_to_utf8 failed: %d", conversion_result); break; @@ -258,7 +258,7 @@ void os_get_hostname(char *hostname, size_t size) host_utf16[host_wsize] = '\0'; char *host_utf8; - int conversion_result = utf16_to_utf8(host_utf16, &host_utf8); + int conversion_result = utf16_to_utf8(host_utf16, -1, &host_utf8); if (conversion_result != 0) { EMSG2("utf16_to_utf8 failed: %d", conversion_result); return; @@ -412,6 +412,7 @@ void expand_env_esc(char_u *restrict srcp, bool esc, bool one, char_u *prefix) + FUNC_ATTR_NONNULL_ARG(1, 2) { char_u *tail; char_u *var; @@ -845,10 +846,10 @@ char *vim_getenv(const char *name) // next time, and others can also use it (e.g. Perl). if (vim_path != NULL) { if (vimruntime) { - vim_setenv("VIMRUNTIME", vim_path); + os_setenv("VIMRUNTIME", vim_path, 1); didset_vimruntime = true; } else { - vim_setenv("VIM", vim_path); + os_setenv("VIM", vim_path, 1); didset_vim = true; } } @@ -995,22 +996,6 @@ char_u * home_replace_save(buf_T *buf, char_u *src) FUNC_ATTR_NONNULL_RET return dst; } -/// Vim setenv() wrapper with special handling for $VIMRUNTIME to keep the -/// localization machinery sane. -void vim_setenv(const char *name, const char *val) -{ - os_setenv(name, val, 1); -#ifndef LOCALE_INSTALL_DIR - // When setting $VIMRUNTIME adjust the directory to find message - // translations to $VIMRUNTIME/lang. - if (*val != NUL && STRICMP(name, "VIMRUNTIME") == 0) { - char *buf = (char *)concat_str((char_u *)val, (char_u *)"/lang"); - bindtextdomain(PROJECT_NAME, buf); - xfree(buf); - } -#endif -} - /// Function given to ExpandGeneric() to obtain an environment variable name. char_u *get_env_name(expand_T *xp, int idx) diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index dcb3ef7c4a..ae922e4040 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -293,7 +293,7 @@ static bool is_executable(const char *name, char **abspath) /// Checks if file `name` is executable under any of these conditions: /// - extension is in $PATHEXT and `name` is executable /// - result of any $PATHEXT extension appended to `name` is executable -static bool is_executable_ext(char *name, char **abspath) +static bool is_executable_ext(const char *name, char **abspath) FUNC_ATTR_NONNULL_ARG(1) { const bool is_unix_shell = strstr((char *)path_tail(p_sh), "sh") != NULL; @@ -1180,12 +1180,10 @@ char *os_resolve_shortcut(const char *fname) &IID_IShellLinkW, (void **)&pslw); if (hr == S_OK) { wchar_t *p; - const int conversion_result = utf8_to_utf16(fname, &p); - if (conversion_result != 0) { - EMSG2("utf8_to_utf16 failed: %d", conversion_result); - } - - if (p != NULL) { + const int r = utf8_to_utf16(fname, -1, &p); + if (r != 0) { + EMSG2("utf8_to_utf16 failed: %d", r); + } else if (p != NULL) { // Get a pointer to the IPersistFile interface. hr = pslw->lpVtbl->QueryInterface( pslw, &IID_IPersistFile, (void **)&ppf); @@ -1210,9 +1208,9 @@ char *os_resolve_shortcut(const char *fname) ZeroMemory(wsz, MAX_PATH * sizeof(wchar_t)); hr = pslw->lpVtbl->GetPath(pslw, wsz, MAX_PATH, &ffdw, 0); if (hr == S_OK && wsz[0] != NUL) { - const int conversion_result = utf16_to_utf8(wsz, &rfname); - if (conversion_result != 0) { - EMSG2("utf16_to_utf8 failed: %d", conversion_result); + const int r2 = utf16_to_utf8(wsz, -1, &rfname); + if (r2 != 0) { + EMSG2("utf16_to_utf8 failed: %d", r2); } } diff --git a/src/nvim/os/fs_defs.h b/src/nvim/os/fs_defs.h index 68e5c74ee1..f4929b12b1 100644 --- a/src/nvim/os/fs_defs.h +++ b/src/nvim/os/fs_defs.h @@ -21,12 +21,6 @@ typedef struct { uv_dirent_t ent; ///< @private The entry information. } Directory; -/// Converts libuv error (negative int) to error description string. -#define os_strerror uv_strerror - -/// Converts system error code to libuv error code. -#define os_translate_sys_error uv_translate_sys_error - // Values returned by os_nodetype() #define NODE_NORMAL 0 // file or directory, check with os_isdir() #define NODE_WRITABLE 1 // something we can write to (character diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h index c29af5c160..c8ac4218f6 100644 --- a/src/nvim/os/os_defs.h +++ b/src/nvim/os/os_defs.h @@ -32,11 +32,12 @@ # include <strings.h> #endif -/// Function to convert libuv error to char * error description -/// -/// negative libuv error codes are returned by a number of os functions. +/// Converts libuv error (negative int) to error description string. #define os_strerror uv_strerror +/// Converts system error code to libuv error code. +#define os_translate_sys_error uv_translate_sys_error + #ifdef WIN32 # define os_strtok strtok_s #else diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index 290668bca3..183219bd3e 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -51,26 +51,26 @@ int pty_process_spawn(PtyProcess *ptyproc) cfg = winpty_config_new(WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err); if (cfg == NULL) { - emsg = "Failed, winpty_config_new."; + emsg = "winpty_config_new failed"; goto cleanup; } winpty_config_set_initial_size(cfg, ptyproc->width, ptyproc->height); winpty_object = winpty_open(cfg, &err); if (winpty_object == NULL) { - emsg = "Failed, winpty_open."; + emsg = "winpty_open failed"; goto cleanup; } - status = utf16_to_utf8(winpty_conin_name(winpty_object), &in_name); + status = utf16_to_utf8(winpty_conin_name(winpty_object), -1, &in_name); if (status != 0) { - emsg = "Failed to convert in_name from utf16 to utf8."; + emsg = "utf16_to_utf8(winpty_conin_name) failed"; goto cleanup; } - status = utf16_to_utf8(winpty_conout_name(winpty_object), &out_name); + status = utf16_to_utf8(winpty_conout_name(winpty_object), -1, &out_name); if (status != 0) { - emsg = "Failed to convert out_name from utf16 to utf8."; + emsg = "utf16_to_utf8(winpty_conout_name) failed"; goto cleanup; } @@ -93,9 +93,9 @@ int pty_process_spawn(PtyProcess *ptyproc) } if (proc->cwd != NULL) { - status = utf8_to_utf16(proc->cwd, &cwd); + status = utf8_to_utf16(proc->cwd, -1, &cwd); if (status != 0) { - emsg = "Failed to convert pwd form utf8 to utf16."; + emsg = "utf8_to_utf16(proc->cwd) failed"; goto cleanup; } } @@ -103,7 +103,7 @@ int pty_process_spawn(PtyProcess *ptyproc) status = build_cmd_line(proc->argv, &cmd_line, os_shell_is_cmdexe(proc->argv[0])); if (status != 0) { - emsg = "Failed to convert cmd line form utf8 to utf16."; + emsg = "build_cmd_line failed"; goto cleanup; } @@ -115,7 +115,7 @@ int pty_process_spawn(PtyProcess *ptyproc) NULL, // Optional environment variables &err); if (spawncfg == NULL) { - emsg = "Failed winpty_spawn_config_new."; + emsg = "winpty_spawn_config_new failed"; goto cleanup; } @@ -128,9 +128,9 @@ int pty_process_spawn(PtyProcess *ptyproc) &err)) { if (win_err) { status = (int)win_err; - emsg = "Failed spawn process."; + emsg = "failed to spawn process"; } else { - emsg = "Failed winpty_spawn."; + emsg = "winpty_spawn failed"; } goto cleanup; } @@ -160,11 +160,11 @@ int pty_process_spawn(PtyProcess *ptyproc) cleanup: if (status) { // In the case of an error of MultiByteToWideChar or CreateProcessW. - ELOG("%s error code: %d", emsg, status); + ELOG("pty_process_spawn: %s: error code: %d", emsg, status); status = os_translate_sys_error(status); } else if (err != NULL) { status = (int)winpty_error_code(err); - ELOG("%s error code: %d", emsg, status); + ELOG("pty_process_spawn: %s: error code: %d", emsg, status); status = translate_winpty_error(status); } winpty_error_free(err); @@ -308,7 +308,7 @@ static int build_cmd_line(char **argv, wchar_t **cmd_line, bool is_cmdexe) } } - int result = utf8_to_utf16(utf8_cmd_line, cmd_line); + int result = utf8_to_utf16(utf8_cmd_line, -1, cmd_line); xfree(utf8_cmd_line); return result; } diff --git a/src/nvim/path.c b/src/nvim/path.c index 75a26d88c1..1c787e3a1d 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -852,8 +852,13 @@ static char_u *get_path_cutoff(char_u *fname, garray_T *gap) int j = 0; while ((fname[j] == path_part[i][j] - ) && fname[j] != NUL && path_part[i][j] != NUL) +#ifdef WIN32 + || (vim_ispathsep(fname[j]) && vim_ispathsep(path_part[i][j])) +#endif + ) // NOLINT(whitespace/parens) + && fname[j] != NUL && path_part[i][j] != NUL) { j++; + } if (j > maxlen) { maxlen = j; cutoff = &fname[j]; @@ -1257,7 +1262,10 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, } else { addfile(&ga, t, flags); } - xfree(t); + + if (t != p) { + xfree(t); + } } if (did_expand_in_path && !GA_EMPTY(&ga) && (flags & EW_PATH)) diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 99ccce1793..1b2143c419 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -4375,7 +4375,7 @@ static void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, screen_adjust_grid(&grid, &row, &coloff); // Safety check. Avoids clang warnings down the call stack. - if (grid->chars == NULL || row >= grid->Rows || col >= grid->Columns) { + if (grid->chars == NULL || row >= grid->Rows || coloff >= grid->Columns) { DLOG("invalid state, skipped"); return; } diff --git a/src/nvim/search.c b/src/nvim/search.c index fe4fdf57ba..26549208a8 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -4683,8 +4683,7 @@ search_line: } if (matched) { if (action == ACTION_EXPAND) { - int reuse = 0; - int add_r; + bool cont_s_ipos = false; char_u *aux; if (depth == -1 && lnum == curwin->w_cursor.lnum) @@ -4738,7 +4737,7 @@ search_line: p = aux + IOSIZE - i - 1; STRNCPY(IObuff + i, aux, p - aux); i += (int)(p - aux); - reuse |= CONT_S_IPOS; + cont_s_ipos = true; } IObuff[i] = NUL; aux = IObuff; @@ -4747,14 +4746,15 @@ search_line: goto exit_matched; } - add_r = ins_compl_add_infercase(aux, i, p_ic, - curr_fname == curbuf->b_fname ? NULL : curr_fname, - dir, reuse); - if (add_r == OK) - /* if dir was BACKWARD then honor it just once */ + const int add_r = ins_compl_add_infercase( + aux, i, p_ic, curr_fname == curbuf->b_fname ? NULL : curr_fname, + dir, cont_s_ipos); + if (add_r == OK) { + // if dir was BACKWARD then honor it just once dir = FORWARD; - else if (add_r == FAIL) + } else if (add_r == FAIL) { break; + } } else if (action == ACTION_SHOW_ALL) { found = TRUE; if (!did_show) diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 8d800843f8..40bb882948 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -7332,7 +7332,7 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, int *dir, int d ? mb_strnicmp(p, pat, STRLEN(pat)) == 0 : STRNCMP(p, pat, STRLEN(pat)) == 0) && ins_compl_add_infercase(p, (int)STRLEN(p), - p_ic, NULL, *dir, 0) == OK) { + p_ic, NULL, *dir, false) == OK) { // if dir was BACKWARD then honor it just once *dir = FORWARD; } diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 5aeea3223b..3faf6dd5bb 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -662,6 +662,7 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, .rgb_fg_color = vt_fg, .rgb_bg_color = vt_bg, .rgb_sp_color = -1, + .hl_blend = -1, }); } diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 8b43d91e25..03b1bae28f 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -16,7 +16,6 @@ export TMPDIR := $(abspath ../../../Xtest-tmpdir) SCRIPTS_DEFAULT = \ test42.out \ test48.out \ - test52.out \ test64.out \ ifneq ($(OS),Windows_NT) @@ -25,6 +24,12 @@ ifneq ($(OS),Windows_NT) endif +ifeq ($(OS),Windows_NT) + FIXFF = fixff +else + FIXFF = +endif + SCRIPTS ?= $(SCRIPTS_DEFAULT) # Tests using runtest.vim. @@ -74,27 +79,39 @@ ifdef TESTNUM SCRIPTS := test$(TESTNUM).out endif -nongui: nolog $(SCRIPTS) newtests report +nongui: nolog $(FIXFF) $(SCRIPTS) newtests report .gdbinit: @echo "[OLDTEST-PREP] Setting up .gdbinit" @echo 'set $$_exitcode = -1\nrun\nif $$_exitcode != -1\n quit\nend' > .gdbinit report: + $(RUN_VIMTEST) $(NO_INITS) -u NONE -S summarize.vim messages @echo @echo 'Test results:' - @/bin/sh -c "if test -f test.log; then \ - cat test.log; \ - echo TEST FAILURE; \ - exit 1; \ - else \ - echo ALL DONE; \ - fi" + @cat test_result.log + @/bin/sh -c "if test -f test.log; \ + then echo TEST FAILURE; exit 1; \ + else echo ALL DONE; \ + fi" test1.out: $(NVIM_PRG) $(SCRIPTS): $(NVIM_PRG) test1.out +NO_PLUGINS = --noplugin --headless +# In vim, if the -u command line option is specified, compatible is turned on +# and viminfo is not read. Unlike vim, neovim reads viminfo and requires the +# -i command line option. +NO_INITS = -U NONE -i NONE $(NO_PLUGINS) + +# TODO: find a way to avoid changing the distributed files. +fixff: + -$(NVIM_PRG) $(NO_INITS) -u unix.vim "+argdo set ff=dos|upd" +q \ + *.in *.ok + -$(NVIM_PRG) $(NO_INITS) -u unix.vim "+argdo set ff=dos|upd" +q \ + dotest.in + RM_ON_RUN := test.out X* viminfo RM_ON_START := test.ok RUN_VIM := $(TOOL) $(NVIM_PRG) -u unix.vim -U NONE -i viminfo --headless --noplugin -s dotest.in @@ -106,6 +123,7 @@ CLEAN_FILES := *.out \ *.orig \ *.tlog \ test.log \ + test_result.log \ messages \ $(RM_ON_RUN) \ $(RM_ON_START) \ @@ -145,7 +163,7 @@ nolog: # New style of tests uses Vim script with assert calls. These are easier # to write and a lot easier to read and debug. # Limitation: Only works with the +eval feature. -RUN_VIMTEST = $(TOOL) $(NVIM_PRG) -u unix.vim -U NONE -i viminfo --headless --noplugin +RUN_VIMTEST = $(TOOL) $(NVIM_PRG) -u unix.vim newtests: newtestssilent @/bin/sh -c "if test -f messages && grep -q 'FAILED' messages; then \ @@ -158,4 +176,4 @@ newtestssilent: $(NEW_TESTS) @echo "[OLDTEST] Running" $* @rm -rf $*.failed test.ok $(RM_ON_RUN) @mkdir -p $(TMPDIR) - @/bin/sh runnvim.sh $(ROOT) $(NVIM_PRG) $* $(RUN_VIMTEST) -u NONE -S runtest.vim $*.vim + @/bin/sh runnvim.sh $(ROOT) $(NVIM_PRG) $* $(RUN_VIMTEST) $(NO_INITS) -u NONE -S runtest.vim $*.vim diff --git a/src/nvim/testdir/load.vim b/src/nvim/testdir/load.vim index 6369b8f45e..5697ee7304 100644 --- a/src/nvim/testdir/load.vim +++ b/src/nvim/testdir/load.vim @@ -6,8 +6,8 @@ function! s:load_factor() abort for _ in range(5) let g:val = 0 - call timer_start(timeout, {-> nvim_set_var('val', 1)}) let start = reltime() + call timer_start(timeout, {-> nvim_set_var('val', 1)}) while 1 sleep 10m if g:val == 1 diff --git a/src/nvim/testdir/runnvim.sh b/src/nvim/testdir/runnvim.sh index 43556f3ad3..249b89d04e 100755 --- a/src/nvim/testdir/runnvim.sh +++ b/src/nvim/testdir/runnvim.sh @@ -64,7 +64,7 @@ main() {( fi valgrind_check . if test -n "$LOG_DIR" ; then - asan_check "$LOG_DIR" + check_sanitizer "$LOG_DIR" fi check_core_dumps if test "$FAILED" = 1 ; then diff --git a/src/nvim/testdir/runnvim.vim b/src/nvim/testdir/runnvim.vim index 52e05cfbeb..a46e2d3fc0 100644 --- a/src/nvim/testdir/runnvim.vim +++ b/src/nvim/testdir/runnvim.vim @@ -7,6 +7,13 @@ function s:logger.on_exit(id, data, event) call add(self.d_events, [a:event, ['']]) endfunction +" Replace non-printable chars by special sequence, or "<%x>". +let s:escaped_char = {"\n": '\n', "\r": '\r', "\t": '\t'} +function! s:escape_non_printable(char) abort + let r = get(s:escaped_char, a:char) + return r is 0 ? printf('<%x>', char2nr(a:char)) : r +endfunction + function Main() let argc = +$NVIM_TEST_ARGC let args = [] @@ -26,9 +33,8 @@ function Main() \'join(map(v:val[1], '. \ '''substitute(v:val, '. \ '"\\v\\C(\\p@!.|\\<)", '. - \ '"\\=printf(\"<%x>\", '. - \ 'char2nr(submatch(0)))", '. - \ '"")''), '. + \ '"\\=s:escape_non_printable(submatch(0))", '. + \ '"g")''), '. \ '''\n'')') call setline(1, [ \ 'Job exited with code ' . results[0], diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index c8161b1f9b..fa25740994 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -31,12 +31,14 @@ " Check that the screen size is at least 24 x 80 characters. if &lines < 24 || &columns < 80 - let error = 'Screen size too small! Tests require at least 24 lines with 80 characters' + let error = 'Screen size too small! Tests require at least 24 lines with 80 characters, got ' .. &lines .. ' lines with ' .. &columns .. ' characters' echoerr error split test.log $put =error write split messages + call append(line('$'), '') + call append(line('$'), 'From ' . expand('%') . ':') call append(line('$'), error) write qa! @@ -49,13 +51,22 @@ source setup.vim " This also enables use of line continuation. set nocp viminfo+=nviminfo -" Use utf-8 or latin1 by default, instead of whatever the system default -" happens to be. Individual tests can overrule this at the top of the file. -if has('multi_byte') - set encoding=utf-8 -else - set encoding=latin1 -endif +" Use utf-8 by default, instead of whatever the system default happens to be. +" Individual tests can overrule this at the top of the file. +set encoding=utf-8 + +" REDIR_TEST_TO_NULL has a very permissive SwapExists autocommand which is for +" the test_name.vim file itself. Replace it here with a more restrictive one, +" so we still catch mistakes. +let s:test_script_fname = expand('%') +au! SwapExists * call HandleSwapExists() +func HandleSwapExists() + " Only ignore finding a swap file for the test script (the user might be + " editing it and do ":make test_name") and the output file. + if expand('<afile>') == 'messages' || expand('<afile>') =~ s:test_script_fname + let v:swapchoice = 'e' + endif +endfunc " Avoid stopping at the "hit enter" prompt set nomore @@ -64,7 +75,7 @@ set nomore lang mess C " Always use forward slashes. -" set shellslash +set shellslash " Prepare for calling test_garbagecollect_now(). let v:testing = 1 @@ -148,8 +159,9 @@ func RunTheTest(test) endtry endif - " Clear any autocommands + " Clear any autocommands and put back the catch-all for SwapExists. au! + au SwapExists * call HandleSwapExists() " Close any extra tab pages and windows and make the current one not modified. while tabpagenr('$') > 1 @@ -256,6 +268,9 @@ if expand('%') =~ 'test_vimscript.vim' else try source % + catch /^\cskipped/ + call add(s:messages, ' Skipped') + call add(s:skipped, 'SKIPPED ' . expand('%') . ': ' . substitute(v:exception, '^\S*\s\+', '', '')) catch let s:fail += 1 call add(s:errors, 'Caught exception: ' . v:exception . ' @ ' . v:throwpoint) @@ -266,14 +281,16 @@ endif let s:flaky_tests = [ \ 'Test_cursorhold_insert()', \ 'Test_exit_callback_interval()', + \ 'Test_map_timeout_with_timer_interrupt()', \ 'Test_oneshot()', \ 'Test_out_cb()', \ 'Test_paused()', - \ 'Test_popup_and_window_resize()', \ 'Test_quoteplus()', \ 'Test_quotestar()', \ 'Test_reltime()', + \ 'Test_repeat_many()', \ 'Test_repeat_three()', + \ 'Test_stop_all_in_callback()', \ 'Test_terminal_composing_unicode()', \ 'Test_terminal_redir_file()', \ 'Test_terminal_tmap()', diff --git a/src/nvim/testdir/shared.vim b/src/nvim/testdir/shared.vim index 6cc2d06a36..bcd6d021a0 100644 --- a/src/nvim/testdir/shared.vim +++ b/src/nvim/testdir/shared.vim @@ -196,16 +196,30 @@ func s:feedkeys(timer) call feedkeys('x', 'nt') endfunc +" Get $VIMPROG to run Vim executable. +" The Makefile writes it as the first line in the "vimcmd" file. +" Nvim: uses $NVIM_TEST_ARG0. +func GetVimProg() + if empty($NVIM_TEST_ARG0) + " Assume the script was sourced instead of running "make". + return v:progpath + endif + if has('win32') + return substitute($NVIM_TEST_ARG0, '/', '\\', 'g') + else + return $NVIM_TEST_ARG0 + endif +endfunc + " Get the command to run Vim, with -u NONE and --headless arguments. " If there is an argument use it instead of "NONE". -" Returns an empty string on error. func GetVimCommand(...) if a:0 == 0 let name = 'NONE' else let name = a:1 endif - let cmd = v:progpath + let cmd = GetVimProg() let cmd = substitute(cmd, '-u \f\+', '-u ' . name, '') if cmd !~ '-u '. name let cmd = cmd . ' -u ' . name @@ -215,6 +229,14 @@ func GetVimCommand(...) return cmd endfunc +" Get the command to run Vim, with --clean. +func GetVimCommandClean() + let cmd = GetVimCommand() + let cmd = substitute(cmd, '-u NONE', '--clean', '') + let cmd = substitute(cmd, '--headless', '', '') + return cmd +endfunc + " Run Vim, using the "vimcmd" file and "-u NORC". " "before" is a list of Vim commands to be executed before loading plugins. " "after" is a list of Vim commands to be executed after loading plugins. diff --git a/src/nvim/testdir/summarize.vim b/src/nvim/testdir/summarize.vim new file mode 100644 index 0000000000..4a88935a40 --- /dev/null +++ b/src/nvim/testdir/summarize.vim @@ -0,0 +1,56 @@ +if 1 + " This is executed only with the eval feature + set nocompatible + func Count(match, type) + if a:type ==# 'executed' + let g:executed += (a:match+0) + elseif a:type ==# 'failed' + let g:failed += a:match+0 + elseif a:type ==# 'skipped' + let g:skipped += 1 + call extend(g:skipped_output, ["\t".a:match]) + endif + endfunc + + let g:executed = 0 + let g:skipped = 0 + let g:failed = 0 + let g:skipped_output = [] + let g:failed_output = [] + let output = [""] + + try + " This uses the :s command to just fetch and process the output of the + " tests, it doesn't acutally replace anything. + " And it uses "silent" to avoid reporting the number of matches. + silent %s/^Executed\s\+\zs\d\+\ze\s\+tests/\=Count(submatch(0),'executed')/egn + silent %s/^SKIPPED \zs.*/\=Count(submatch(0), 'skipped')/egn + silent %s/^\(\d\+\)\s\+FAILED:/\=Count(submatch(1), 'failed')/egn + + call extend(output, ["Skipped:"]) + call extend(output, skipped_output) + + call extend(output, [ + \ "", + \ "-------------------------------", + \ printf("Executed: %5d Tests", g:executed), + \ printf(" Skipped: %5d Tests", g:skipped), + \ printf(" %s: %5d Tests", g:failed == 0 ? 'Failed' : 'FAILED', g:failed), + \ "", + \ ]) + if filereadable('test.log') + " outputs and indents the failed test result + call extend(output, ["", "Failures: "]) + let failed_output = filter(readfile('test.log'), { v,k -> !empty(k)}) + call extend(output, map(failed_output, { v,k -> "\t".k})) + " Add a final newline + call extend(output, [""]) + endif + + catch " Catch-all + finally + call writefile(output, 'test_result.log') " overwrites an existing file + endtry +endif + +q! diff --git a/src/nvim/testdir/test42.in b/src/nvim/testdir/test42.in Binary files differindex 0ea0198d12..baa6e67d26 100644 --- a/src/nvim/testdir/test42.in +++ b/src/nvim/testdir/test42.in diff --git a/src/nvim/testdir/test52.in b/src/nvim/testdir/test52.in deleted file mode 100644 index fa75129193..0000000000 --- a/src/nvim/testdir/test52.in +++ /dev/null @@ -1,64 +0,0 @@ -Tests for reading and writing files with conversion for Win32. - -STARTTEST -:" make this a dummy test for non-Win32 systems -:if !has("win32") | e! test.ok | wq! test.out | endif -:" -:" write tests: -:" combine three values for 'encoding' with three values for 'fileencoding' -:" also write files for read tests -/^1 -:set encoding=utf-8 -:.w! ++enc=utf-8 test.out -:.w ++enc=cp1251 >>test.out -:.w ++enc=cp866 >>test.out -:.w! ++enc=utf-8 Xutf8 -/^2 -:set encoding=cp1251 -:.w ++enc=utf-8 >>test.out -:.w ++enc=cp1251 >>test.out -:.w ++enc=cp866 >>test.out -:.w! ++enc=cp1251 Xcp1251 -/^3 -:set encoding=cp866 -:.w ++enc=utf-8 >>test.out -:.w ++enc=cp1251 >>test.out -:.w ++enc=cp866 >>test.out -:.w! ++enc=cp866 Xcp866 -:" -:" read three 'fileencoding's with utf-8 'encoding' -:set encoding=utf-8 fencs=utf-8,cp1251 -:e Xutf8 -:.w ++enc=utf-8 >>test.out -:e Xcp1251 -:.w ++enc=utf-8 >>test.out -:set fencs=utf-8,cp866 -:e Xcp866 -:.w ++enc=utf-8 >>test.out -:" -:" read three 'fileencoding's with cp1251 'encoding' -:set encoding=utf-8 fencs=utf-8,cp1251 -:e Xutf8 -:.w ++enc=cp1251 >>test.out -:e Xcp1251 -:.w ++enc=cp1251 >>test.out -:set fencs=utf-8,cp866 -:e Xcp866 -:.w ++enc=cp1251 >>test.out -:" -:" read three 'fileencoding's with cp866 'encoding' -:set encoding=cp866 fencs=utf-8,cp1251 -:e Xutf8 -:.w ++enc=cp866 >>test.out -:e Xcp1251 -:.w ++enc=cp866 >>test.out -:set fencs=utf-8,cp866 -:e Xcp866 -:.w ++enc=cp866 >>test.out -:" -:qa! -ENDTEST - -1 utf-8 text: ΠΠ»Ρ Vim version 6.2. ΠΠΎΡΠ»Π΅Π΄Π½Π΅Π΅ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅: 1970 Jan 01 -2 cp1251 text: Δλ Vim version 6.2. Οξρλεδνεε θημενενθε: 1970 Jan 01 -3 cp866 text: «ο Vim version 6.2. α«₯€₯₯ ¨§¬₯₯¨₯: 1970 Jan 01 diff --git a/src/nvim/testdir/test52.ok b/src/nvim/testdir/test52.ok deleted file mode 100644 index 90b516508d..0000000000 --- a/src/nvim/testdir/test52.ok +++ /dev/null @@ -1,18 +0,0 @@ -1 utf-8 text: ΠΠ»Ρ Vim version 6.2. ΠΠΎΡΠ»Π΅Π΄Π½Π΅Π΅ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅: 1970 Jan 01 -1 utf-8 text: Δλ Vim version 6.2. Οξρλεδνεε θημενενθε: 1970 Jan 01 -1 utf-8 text: «ο Vim version 6.2. α«₯€₯₯ ¨§¬₯₯¨₯: 1970 Jan 01 -2 cp1251 text: ΠΠ»Ρ Vim version 6.2. ΠΠΎΡΠ»Π΅Π΄Π½Π΅Π΅ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅: 1970 Jan 01 -2 cp1251 text: Δλ Vim version 6.2. Οξρλεδνεε θημενενθε: 1970 Jan 01 -2 cp1251 text: «ο Vim version 6.2. α«₯€₯₯ ¨§¬₯₯¨₯: 1970 Jan 01 -3 cp866 text: ΠΠ»Ρ Vim version 6.2. ΠΠΎΡΠ»Π΅Π΄Π½Π΅Π΅ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅: 1970 Jan 01 -3 cp866 text: Δλ Vim version 6.2. Οξρλεδνεε θημενενθε: 1970 Jan 01 -3 cp866 text: «ο Vim version 6.2. α«₯€₯₯ ¨§¬₯₯¨₯: 1970 Jan 01 -1 utf-8 text: ΠΠ»Ρ Vim version 6.2. ΠΠΎΡΠ»Π΅Π΄Π½Π΅Π΅ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅: 1970 Jan 01 -2 cp1251 text: ΠΠ»Ρ Vim version 6.2. ΠΠΎΡΠ»Π΅Π΄Π½Π΅Π΅ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅: 1970 Jan 01 -3 cp866 text: ΠΠ»Ρ Vim version 6.2. ΠΠΎΡΠ»Π΅Π΄Π½Π΅Π΅ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅: 1970 Jan 01 -1 utf-8 text: Δλ Vim version 6.2. Οξρλεδνεε θημενενθε: 1970 Jan 01 -2 cp1251 text: Δλ Vim version 6.2. Οξρλεδνεε θημενενθε: 1970 Jan 01 -3 cp866 text: Δλ Vim version 6.2. Οξρλεδνεε θημενενθε: 1970 Jan 01 -1 utf-8 text: «ο Vim version 6.2. α«₯€₯₯ ¨§¬₯₯¨₯: 1970 Jan 01 -2 cp1251 text: «ο Vim version 6.2. α«₯€₯₯ ¨§¬₯₯¨₯: 1970 Jan 01 -3 cp866 text: «ο Vim version 6.2. α«₯€₯₯ ¨§¬₯₯¨₯: 1970 Jan 01 diff --git a/src/nvim/testdir/test_alot_utf8.vim b/src/nvim/testdir/test_alot_utf8.vim index 648d806a94..be0bd01413 100644 --- a/src/nvim/testdir/test_alot_utf8.vim +++ b/src/nvim/testdir/test_alot_utf8.vim @@ -2,8 +2,7 @@ " This makes testing go faster, since Vim doesn't need to restart. " These tests use utf8 'encoding'. Setting 'encoding' is already done in -" runtest.vim. Checking for the multi_byte feature is in the individual -" files, so that they can be run by themselves. +" runtest.vim. source test_charsearch_utf8.vim source test_expr_utf8.vim diff --git a/src/nvim/testdir/test_arabic.vim b/src/nvim/testdir/test_arabic.vim index d67f875f97..450c6f98f5 100644 --- a/src/nvim/testdir/test_arabic.vim +++ b/src/nvim/testdir/test_arabic.vim @@ -2,8 +2,8 @@ " NOTE: This just checks if the code works. If you know Arabic please add " functional tests that check the shaping works with real text. -if !has('arabic') || !has('multi_byte') - finish +if !has('arabic') + throw 'Skipped: arabic feature missing' endif source view_util.vim diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim index 05d69631c4..67c537b407 100644 --- a/src/nvim/testdir/test_autochdir.vim +++ b/src/nvim/testdir/test_autochdir.vim @@ -1,7 +1,7 @@ " Test 'autochdir' behavior if !exists("+autochdir") - finish + throw 'Skipped: autochdir feature missing' endif func Test_set_filename() diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim index 7deffbe452..4b34420cab 100644 --- a/src/nvim/testdir/test_breakindent.vim +++ b/src/nvim/testdir/test_breakindent.vim @@ -5,7 +5,7 @@ " It helps to change the tabstop setting and force a redraw (e.g. see " Test_breakindent08()) if !exists('+breakindent') - finish + throw 'Skipped: breakindent option not supported' endif source view_util.vim diff --git a/src/nvim/testdir/test_cd.vim b/src/nvim/testdir/test_cd.vim index 4436ebbf31..770ed55b8d 100644 --- a/src/nvim/testdir/test_cd.vim +++ b/src/nvim/testdir/test_cd.vim @@ -24,10 +24,7 @@ func Test_cd_no_arg() call assert_equal(path, getcwd()) else " Test that cd without argument echoes cwd on non-Unix systems. - let shellslash = &shellslash - set shellslash call assert_match(getcwd(), execute('cd')) - let &shellslash = shellslash endif endfunc diff --git a/src/nvim/testdir/test_charsearch_utf8.vim b/src/nvim/testdir/test_charsearch_utf8.vim index ade7dd408c..eac5d46ad8 100644 --- a/src/nvim/testdir/test_charsearch_utf8.vim +++ b/src/nvim/testdir/test_charsearch_utf8.vim @@ -1,7 +1,4 @@ " Tests for related f{char} and t{char} using utf-8. -if !has('multi_byte') - finish -endif " Test for t,f,F,T movement commands function! Test_search_cmds() diff --git a/src/nvim/testdir/test_checkpath.vim b/src/nvim/testdir/test_checkpath.vim index a7557a107d..eff30cf205 100644 --- a/src/nvim/testdir/test_checkpath.vim +++ b/src/nvim/testdir/test_checkpath.vim @@ -2,8 +2,6 @@ " Test for 'include' without \zs or \ze func Test_checkpath1() - let save_shellslash = &shellslash - set shellslash call mkdir("Xdir1/dir2", "p") call writefile(['#include "bar.a"'], 'Xdir1/dir2/foo.a') call writefile(['#include "baz.a"'], 'Xdir1/dir2/bar.a') @@ -27,7 +25,6 @@ func Test_checkpath1() call delete("./Xbase.a") call delete("Xdir1", "rf") set path& - let &shellslash = save_shellslash endfunc func DotsToSlashes() @@ -36,8 +33,6 @@ endfunc " Test for 'include' with \zs and \ze func Test_checkpath2() - let save_shellslash = &shellslash - set shellslash call mkdir("Xdir1/dir2", "p") call writefile(['%inc /bar/'], 'Xdir1/dir2/foo.b') call writefile(['%inc /baz/'], 'Xdir1/dir2/bar.b') @@ -66,7 +61,6 @@ func Test_checkpath2() set path& set include& set includeexpr& - let &shellslash = save_shellslash endfunc func StripNewlineChar() @@ -78,8 +72,6 @@ endfunc " Test for 'include' with \zs and no \ze func Test_checkpath3() - let save_shellslash = &shellslash - set shellslash call mkdir("Xdir1/dir2", "p") call writefile(['%inc bar.c'], 'Xdir1/dir2/foo.c') call writefile(['%inc baz.c'], 'Xdir1/dir2/bar.c') @@ -109,5 +101,4 @@ func Test_checkpath3() set path& set include& set includeexpr& - let &shellslash = save_shellslash endfunc diff --git a/src/nvim/testdir/test_clientserver.vim b/src/nvim/testdir/test_clientserver.vim index 02840de743..46ac59b3b1 100644 --- a/src/nvim/testdir/test_clientserver.vim +++ b/src/nvim/testdir/test_clientserver.vim @@ -1,7 +1,7 @@ " Tests for the +clientserver feature. if !has('job') || !has('clientserver') - finish + throw 'Skipped: job and/or clientserver feature missing' endif source shared.vim diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 4b333e444a..b8b018d5f7 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -568,7 +568,8 @@ func Test_setcmdpos() endfunc func Test_cmdline_overstrike() - let encodings = has('multi_byte') ? [ 'utf8' ] : [ 'latin1' ] + " Nvim: only utf8 is supported. + let encodings = ['utf8'] let encoding_save = &encoding for e in encodings @@ -587,11 +588,9 @@ func Test_cmdline_overstrike() call assert_equal('"ab0cd3ef4', @:) endfor - if has('multi_byte') - " Test overstrike with multi-byte characters. - call feedkeys(":\"γγγΉγγ¨γγ£γΏ\<home>\<right>\<right>ab\<right>\<insert>cd\<enter>", 'xt') - call assert_equal('"γabγcdγ¨γγ£γΏ', @:) - endif + " Test overstrike with multi-byte characters. + call feedkeys(":\"γγγΉγγ¨γγ£γΏ\<home>\<right>\<right>ab\<right>\<insert>cd\<enter>", 'xt') + call assert_equal('"γabγcdγ¨γγ£γΏ', @:) let &encoding = encoding_save endfunc diff --git a/src/nvim/testdir/test_debugger.vim b/src/nvim/testdir/test_debugger.vim index bb87ef9c58..3ef460b4fe 100644 --- a/src/nvim/testdir/test_debugger.vim +++ b/src/nvim/testdir/test_debugger.vim @@ -22,7 +22,7 @@ endfunc " Debugger tests func Test_Debugger() if !CanRunVimInTerminal() - return + throw 'Skipped: cannot run Vim in a terminal window' endif " Create a Vim script with some functions diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index 1ba36ca8e9..57b19aa817 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -723,7 +723,7 @@ endfunc func Test_diff_with_cursorline() if !CanRunVimInTerminal() - return + throw 'Skipped: cannot run Vim in a terminal window' endif call writefile([ @@ -750,7 +750,7 @@ endfunc func Test_diff_of_diff() if !CanRunVimInTerminal() - return + throw 'Skipped: cannot run Vim in a terminal window' endif if !has("rightleft") throw 'Skipped: rightleft not supported' diff --git a/src/nvim/testdir/test_digraph.vim b/src/nvim/testdir/test_digraph.vim index 271066df41..62a5da33df 100644 --- a/src/nvim/testdir/test_digraph.vim +++ b/src/nvim/testdir/test_digraph.vim @@ -1,6 +1,6 @@ " Tests for digraphs -if !has("digraphs") || !has("multi_byte") +if !has("digraphs") finish endif diff --git a/src/nvim/testdir/test_display.vim b/src/nvim/testdir/test_display.vim index 0ed672d577..5feb59eef1 100644 --- a/src/nvim/testdir/test_display.vim +++ b/src/nvim/testdir/test_display.vim @@ -41,7 +41,7 @@ func! Test_display_foldcolumn() endfunc func! Test_display_foldtext_mbyte() - if !has("folding") || !has("multi_byte") + if !has("folding") return endif call NewWindow(10, 40) diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim index 48376d7922..827c54e704 100644 --- a/src/nvim/testdir/test_edit.vim +++ b/src/nvim/testdir/test_edit.vim @@ -37,12 +37,10 @@ func! Test_edit_01() call assert_equal([''], getline(1,'$')) %d " 4) delete a multibyte character - if has("multi_byte") - call setline(1, "\u0401") - call feedkeys("i\<del>\<esc>", 'tnix') - call assert_equal([''], getline(1,'$')) - %d - endif + call setline(1, "\u0401") + call feedkeys("i\<del>\<esc>", 'tnix') + call assert_equal([''], getline(1,'$')) + %d " 5.1) delete linebreak with 'bs' option containing eol let _bs=&bs set bs=eol @@ -452,7 +450,7 @@ endfunc func! Test_edit_CTRL_() " disabled for Windows builds, why? - if !has("multi_byte") || !has("rightleft") || has("win32") + if !has("rightleft") || has("win32") return endif let _encoding=&encoding @@ -620,7 +618,7 @@ func! Test_edit_CTRL_K() endtry call delete('Xdictionary.txt') - if has("multi_byte") && !has("nvim") + if exists('*test_override') call test_override("char_avail", 1) set showcmd %d diff --git a/src/nvim/testdir/test_erasebackword.vim b/src/nvim/testdir/test_erasebackword.vim index 098d6edfcb..9522ec2cd6 100644 --- a/src/nvim/testdir/test_erasebackword.vim +++ b/src/nvim/testdir/test_erasebackword.vim @@ -1,10 +1,5 @@ func Test_erasebackword() - if !has('multi_byte') - return - endif - - set encoding=utf-8 enew exe "normal o wwwγγγ«γ‘γδΈηγ―γΌγ«γvim \<C-W>" @@ -21,5 +16,4 @@ func Test_erasebackword() call assert_equal('', getline('.')) enew! - set encoding& endfunc diff --git a/src/nvim/testdir/test_expr_utf8.vim b/src/nvim/testdir/test_expr_utf8.vim index 1737a9f745..fad725d2e5 100644 --- a/src/nvim/testdir/test_expr_utf8.vim +++ b/src/nvim/testdir/test_expr_utf8.vim @@ -1,7 +1,4 @@ " Tests for expressions using utf-8. -if !has('multi_byte') - finish -endif func Test_strgetchar() call assert_equal(char2nr('Γ‘'), strgetchar('Γ‘xb', 0)) diff --git a/src/nvim/testdir/test_find_complete.vim b/src/nvim/testdir/test_find_complete.vim index 7592b16192..a7bc135d47 100644 --- a/src/nvim/testdir/test_find_complete.vim +++ b/src/nvim/testdir/test_find_complete.vim @@ -3,8 +3,6 @@ " Do all the tests in a separate window to avoid E211 when we recursively " delete the Xfind directory during cleanup func Test_find_complete() - let shellslash = &shellslash - set shellslash set belloff=all " On windows a stale "Xfind" directory may exist, remove it so that @@ -162,5 +160,4 @@ func Test_find_complete() exe 'cd ' . cwd call delete('Xfind', 'rf') set path& - let &shellslash = shellslash endfunc diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index df3d691d85..3cb42579be 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -706,7 +706,7 @@ endfunc func Test_folds_with_rnu() if !CanRunVimInTerminal() - return + throw 'Skipped: cannot make screendumps' endif call writefile([ @@ -740,3 +740,19 @@ func Test_folds_marker_in_comment2() set foldmethod& bwipe! endfunc + +func Test_fold_delete_with_marker() + new + call setline(1, ['func Func() {{{1', 'endfunc']) + 1,2yank + new + set fdm=marker + call setline(1, 'x') + normal! Vp + normal! zd + call assert_equal(['func Func() ', 'endfunc'], getline(1, '$')) + + set fdm& + bwipe! + bwipe! +endfunc diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index fab1d7790d..fe3b8bef6e 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -106,11 +106,9 @@ func Test_strwidth() call assert_equal(4, strwidth(1234)) call assert_equal(5, strwidth(-1234)) - if has('multi_byte') - call assert_equal(2, strwidth('π')) - call assert_equal(17, strwidth('EΔ₯oΕanΔo ΔiuΔ΅aΕde')) - call assert_equal((aw == 'single') ? 6 : 7, strwidth('StraΓe')) - endif + call assert_equal(2, strwidth('π')) + call assert_equal(17, strwidth('EΔ₯oΕanΔo ΔiuΔ΅aΕde')) + call assert_equal((aw == 'single') ? 6 : 7, strwidth('StraΓe')) call assert_fails('call strwidth({->0})', 'E729:') call assert_fails('call strwidth([])', 'E730:') @@ -308,10 +306,8 @@ func Test_strpart() call assert_equal('fg', strpart('abcdefg', 5, 4)) call assert_equal('defg', strpart('abcdefg', 3)) - if has('multi_byte') - call assert_equal('lΓ©p', strpart('Γ©lΓ©phant', 2, 4)) - call assert_equal('lΓ©phant', strpart('Γ©lΓ©phant', 2)) - endif + call assert_equal('lΓ©p', strpart('Γ©lΓ©phant', 2, 4)) + call assert_equal('lΓ©phant', strpart('Γ©lΓ©phant', 2)) endfunc func Test_tolower() @@ -321,10 +317,6 @@ func Test_tolower() call assert_equal(' !"#$%&''()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\]^_`abcdefghijklmnopqrstuvwxyz{|}~', \ tolower(' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~')) - if !has('multi_byte') - return - endif - " Test with a few uppercase diacritics. call assert_equal("aà ÑÒãÀΓ₯ΔΔΔ
ΗΗΗ‘αΊ£", tolower("AΓΓΓΓΓΓ
ΔΔΔΗΗΗ αΊ’")) call assert_equal("bαΈαΈ", tolower("BαΈαΈ")) @@ -399,10 +391,6 @@ func Test_toupper() call assert_equal(' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~', \ toupper(' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~')) - if !has('multi_byte') - return - endif - " Test with a few lowercase diacritics. call assert_equal("AΓΓΓΓΓΓ
ΔΔΔΗΗΗ αΊ’", toupper("aà ÑÒãÀΓ₯ΔΔΔ
ΗΗΗ‘αΊ£")) call assert_equal("BαΈαΈ", toupper("bαΈαΈ")) @@ -976,8 +964,8 @@ func Test_balloon_show() endfunc func Test_shellescape() - let [save_shell, save_shellslash] = [&shell, &shellslash] - set shell=bash shellslash + let save_shell = &shell + set shell=bash call assert_equal("'text'", shellescape('text')) call assert_equal("'te\"xt'", shellescape('te"xt')) call assert_equal("'te'\\''xt'", shellescape("te'xt")) @@ -991,13 +979,13 @@ func Test_shellescape() call assert_equal("'te\nxt'", shellescape("te\nxt")) call assert_equal("'te\\\nxt'", shellescape("te\nxt", 1)) - set shell=tcsh shellslash + set shell=tcsh call assert_equal("'te\\!xt'", shellescape("te!xt")) call assert_equal("'te\\\\!xt'", shellescape("te!xt", 1)) call assert_equal("'te\\\nxt'", shellescape("te\nxt")) call assert_equal("'te\\\\\nxt'", shellescape("te\nxt", 1)) - let [&shell, &shellslash] = [save_shell, save_shellslash] + let &shell = save_shell endfunc func Test_redo_in_nested_functions() @@ -1170,6 +1158,8 @@ func Test_libcall_libcallnr() else let libc = '/usr/lib/libc.so' endif + elseif system('uname -s') =~ 'OpenBSD' + let libc = 'libc.so' else " On Unix, libc.so can be in various places. " Interestingly, using an empty string for the 1st argument of libcall diff --git a/src/nvim/testdir/test_ga.vim b/src/nvim/testdir/test_ga.vim index 6a7cba28f0..ea3d211aeb 100644 --- a/src/nvim/testdir/test_ga.vim +++ b/src/nvim/testdir/test_ga.vim @@ -21,10 +21,6 @@ func Test_ga_command() call assert_equal("\n<e> 101, Hex 65, Octal 145", Do_ga('e')) - if !has('multi_byte') - return - endif - " Test a few multi-bytes characters. call assert_equal("\n<Γ©> 233, Hex 00e9, Oct 351, Digr e'", Do_ga('Γ©')) call assert_equal("\n<αΊ»> 7867, Hex 1ebb, Oct 17273, Digr e2", Do_ga('αΊ»')) diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim index e751fb5d16..78ec26fd05 100644 --- a/src/nvim/testdir/test_highlight.vim +++ b/src/nvim/testdir/test_highlight.vim @@ -532,7 +532,7 @@ endfunc func Test_cursorline_after_yank() if !CanRunVimInTerminal() - return + throw 'Skipped: cannot make screendumps' endif call writefile([ @@ -554,7 +554,7 @@ endfunc func Test_cursorline_with_visualmode() if !CanRunVimInTerminal() - return + throw 'Skipped: cannot make screendumps' endif call writefile([ diff --git a/src/nvim/testdir/test_increment_dbcs.vim b/src/nvim/testdir/test_increment_dbcs.vim index 474a16feeb..e5d5ccffb3 100644 --- a/src/nvim/testdir/test_increment_dbcs.vim +++ b/src/nvim/testdir/test_increment_dbcs.vim @@ -1,5 +1,7 @@ " Tests for using Ctrl-A/Ctrl-X using DBCS. -if !has('multi_byte') +" neovim needs an iconv to handle cp932. Please do not remove the following +" conditions. +if !has('iconv') finish endif scriptencoding cp932 diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim index 24c6ef5e01..8a6f1bc320 100644 --- a/src/nvim/testdir/test_let.vim +++ b/src/nvim/testdir/test_let.vim @@ -25,3 +25,118 @@ func Test_let() let s = "\na #1\nb #2" call assert_equal(s, out) endfunc + +func s:set_arg1(a) abort + let a:a = 1 +endfunction + +func s:set_arg2(a) abort + let a:b = 1 +endfunction + +func s:set_arg3(a) abort + let b = a: + let b['a'] = 1 +endfunction + +func s:set_arg4(a) abort + let b = a: + let b['a'] = 1 +endfunction + +func s:set_arg5(a) abort + let b = a: + let b['a'][0] = 1 +endfunction + +func s:set_arg6(a) abort + let a:a[0] = 1 +endfunction + +func s:set_arg7(a) abort + call extend(a:, {'a': 1}) +endfunction + +func s:set_arg8(a) abort + call extend(a:, {'b': 1}) +endfunction + +func s:set_arg9(a) abort + let a:['b'] = 1 +endfunction + +func s:set_arg10(a) abort + let b = a: + call extend(b, {'a': 1}) +endfunction + +func s:set_arg11(a) abort + let b = a: + call extend(b, {'b': 1}) +endfunction + +func s:set_arg12(a) abort + let b = a: + let b['b'] = 1 +endfunction + +func Test_let_arg_fail() + call assert_fails('call s:set_arg1(1)', 'E46:') + call assert_fails('call s:set_arg2(1)', 'E461:') + call assert_fails('call s:set_arg3(1)', 'E46:') + call assert_fails('call s:set_arg4(1)', 'E46:') + call assert_fails('call s:set_arg5(1)', 'E46:') + call s:set_arg6([0]) + call assert_fails('call s:set_arg7(1)', 'E742:') + call assert_fails('call s:set_arg8(1)', 'E742:') + call assert_fails('call s:set_arg9(1)', 'E461:') + call assert_fails('call s:set_arg10(1)', 'E742:') + call assert_fails('call s:set_arg11(1)', 'E742:') + call assert_fails('call s:set_arg12(1)', 'E461:') +endfunction + +func s:set_varg1(...) abort + let a:000 = [] +endfunction + +func s:set_varg2(...) abort + let a:000[0] = 1 +endfunction + +func s:set_varg3(...) abort + let a:000 += [1] +endfunction + +func s:set_varg4(...) abort + call add(a:000, 1) +endfunction + +func s:set_varg5(...) abort + let a:000[0][0] = 1 +endfunction + +func s:set_varg6(...) abort + let b = a:000 + let b[0] = 1 +endfunction + +func s:set_varg7(...) abort + let b = a:000 + call add(b, 1) +endfunction + +func s:set_varg8(...) abort + let b = a:000 + let b[0][0] = 1 +endfunction + +func Test_let_varg_fail() + call assert_fails('call s:set_varg1(1)', 'E46:') + call assert_fails('call s:set_varg2(1)', 'E742:') + call assert_fails('call s:set_varg3(1)', 'E46:') + call assert_fails('call s:set_varg4(1)', 'E742:') + call s:set_varg5([0]) + call assert_fails('call s:set_varg6(1)', 'E742:') + call assert_fails('call s:set_varg7(1)', 'E742:') + call s:set_varg8([0]) +endfunction diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim index 9e060cdff6..bea62cb0ad 100644 --- a/src/nvim/testdir/test_listdict.vim +++ b/src/nvim/testdir/test_listdict.vim @@ -500,17 +500,20 @@ endfunc " No remove() of write-protected scope-level variable func! Tfunc(this_is_a_long_parameter_name) - call assert_fails("call remove(a:, 'this_is_a_long_parameter_name')", 'E795') + call assert_fails("call remove(a:, 'this_is_a_long_parameter_name')", 'E742') endfun func Test_dict_scope_var_remove() call Tfunc('testval') endfunc " No extend() of write-protected scope-level variable +func Test_dict_scope_var_extend() + call assert_fails("call extend(a:, {'this_is_a_long_parameter_name': 1234})", 'E742') +endfunc func! Tfunc(this_is_a_long_parameter_name) call assert_fails("call extend(a:, {'this_is_a_long_parameter_name': 1234})", 'E742') endfunc -func Test_dict_scope_var_extend() +func Test_dict_scope_var_extend_overwrite() call Tfunc('testval') endfunc @@ -699,3 +702,75 @@ func Test_listdict_extend() call assert_fails("call extend([1, 2], 1)", 'E712:') call assert_fails("call extend([1, 2], {})", 'E712:') endfunc + +func s:check_scope_dict(x, fixed) + func s:gen_cmd(cmd, x) + return substitute(a:cmd, '\<x\ze:', a:x, 'g') + endfunc + + let cmd = s:gen_cmd('let x:foo = 1', a:x) + if a:fixed + call assert_fails(cmd, 'E461') + else + exe cmd + exe s:gen_cmd('call assert_equal(1, x:foo)', a:x) + endif + + let cmd = s:gen_cmd('let x:["bar"] = 2', a:x) + if a:fixed + call assert_fails(cmd, 'E461') + else + exe cmd + exe s:gen_cmd('call assert_equal(2, x:bar)', a:x) + endif + + let cmd = s:gen_cmd('call extend(x:, {"baz": 3})', a:x) + if a:fixed + call assert_fails(cmd, 'E742') + else + exe cmd + exe s:gen_cmd('call assert_equal(3, x:baz)', a:x) + endif + + if a:fixed + if a:x ==# 'a' + call assert_fails('unlet a:x', 'E795') + call assert_fails('call remove(a:, "x")', 'E742') + elseif a:x ==# 'v' + call assert_fails('unlet v:count', 'E795') + call assert_fails('call remove(v:, "count")', 'E742') + endif + else + exe s:gen_cmd('unlet x:foo', a:x) + exe s:gen_cmd('unlet x:bar', a:x) + exe s:gen_cmd('call remove(x:, "baz")', a:x) + endif + + delfunc s:gen_cmd +endfunc + +func Test_scope_dict() + " Test for g: + call s:check_scope_dict('g', v:false) + + " Test for s: + call s:check_scope_dict('s', v:false) + + " Test for l: + call s:check_scope_dict('l', v:false) + + " Test for a: + call s:check_scope_dict('a', v:true) + + " Test for b: + call s:check_scope_dict('b', v:false) + + " Test for w: + call s:check_scope_dict('w', v:false) + + " Test for t: + call s:check_scope_dict('t', v:false) + + " Test for v: + call s:check_scope_dict('v', v:true) +endfunc diff --git a/src/nvim/testdir/test_makeencoding.vim b/src/nvim/testdir/test_makeencoding.vim index 6e4c7af821..2b346e0720 100644 --- a/src/nvim/testdir/test_makeencoding.vim +++ b/src/nvim/testdir/test_makeencoding.vim @@ -1,7 +1,4 @@ " Tests for 'makeencoding'. -if !has('multi_byte') - finish -endif source shared.vim diff --git a/src/nvim/testdir/test_maparg.vim b/src/nvim/testdir/test_maparg.vim index ee16a22398..6324e39eec 100644 --- a/src/nvim/testdir/test_maparg.vim +++ b/src/nvim/testdir/test_maparg.vim @@ -1,8 +1,5 @@ " Tests for maparg(). " Also test utf8 map with a 0x80 byte. -if !has("multi_byte") - finish -endif function s:SID() return str2nr(matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_SID$')) diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim index 9f253604ed..34e62e80e8 100644 --- a/src/nvim/testdir/test_mapping.vim +++ b/src/nvim/testdir/test_mapping.vim @@ -1,9 +1,5 @@ " Tests for mappings and abbreviations -if !has('multi_byte') - finish -endif - source shared.vim func Test_abbreviation() @@ -229,6 +225,12 @@ func Test_map_meta_quotes() iunmap <M-"> endfunc +func Test_map_meta_multibyte() + imap <M-Γ‘> foo + call assert_match('i <M-Γ‘>\s*foo', execute('imap')) + iunmap <M-Γ‘> +endfunc + func Test_abbr_after_line_join() new abbr foo bar @@ -282,7 +284,7 @@ func Test_map_timeout_with_timer_interrupt() let g:val = 0 nnoremap \12 :let g:val = 1<CR> nnoremap \123 :let g:val = 2<CR> - set timeout timeoutlen=1000 + set timeout timeoutlen=200 func ExitCb(job, status) let g:timer = timer_start(1, {_ -> feedkeys("3\<Esc>", 't')}) diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim index 7fd115fd68..272553c29f 100644 --- a/src/nvim/testdir/test_marks.vim +++ b/src/nvim/testdir/test_marks.vim @@ -122,9 +122,6 @@ func Test_marks_cmd() endfunc func Test_marks_cmd_multibyte() - if !has('multi_byte') - return - endif new Xone call setline(1, [repeat('Γ‘', &columns)]) norm! ma diff --git a/src/nvim/testdir/test_match.vim b/src/nvim/testdir/test_match.vim index e926b946a9..90dfcf952d 100644 --- a/src/nvim/testdir/test_match.vim +++ b/src/nvim/testdir/test_match.vim @@ -114,36 +114,33 @@ function Test_match() call assert_equal([{'group': 'MyGroup1', 'id': 3, 'priority': 10, 'pos1': [1, 5, 1], 'pos2': [1, 8, 3]}], getmatches()) call clearmatches() - " - if has('multi_byte') - call setline(1, 'abcdΞ£abcdef') - call matchaddpos("MyGroup1", [[1, 4, 2], [1, 9, 2]]) - 1 - redraw! - let v1 = screenattr(1, 1) - let v4 = screenattr(1, 4) - let v5 = screenattr(1, 5) - let v6 = screenattr(1, 6) - let v7 = screenattr(1, 7) - let v8 = screenattr(1, 8) - let v9 = screenattr(1, 9) - let v10 = screenattr(1, 10) - call assert_equal([{'group': 'MyGroup1', 'id': 11, 'priority': 10, 'pos1': [1, 4, 2], 'pos2': [1, 9, 2]}], getmatches()) - call assert_notequal(v1, v4) - call assert_equal(v5, v4) - call assert_equal(v6, v1) - call assert_equal(v7, v1) - call assert_equal(v8, v4) - call assert_equal(v9, v4) - call assert_equal(v10, v1) - - " Check, that setmatches() can correctly restore the matches from matchaddpos() - call matchadd('MyGroup1', '\%2lmatchadd') - let m=getmatches() - call clearmatches() - call setmatches(m) - call assert_equal([{'group': 'MyGroup1', 'id': 11, 'priority': 10, 'pos1': [1, 4, 2], 'pos2': [1,9, 2]}, {'group': 'MyGroup1', 'pattern': '\%2lmatchadd', 'priority': 10, 'id': 12}], getmatches()) - endif + call setline(1, 'abcdΞ£abcdef') + call matchaddpos("MyGroup1", [[1, 4, 2], [1, 9, 2]]) + 1 + redraw! + let v1 = screenattr(1, 1) + let v4 = screenattr(1, 4) + let v5 = screenattr(1, 5) + let v6 = screenattr(1, 6) + let v7 = screenattr(1, 7) + let v8 = screenattr(1, 8) + let v9 = screenattr(1, 9) + let v10 = screenattr(1, 10) + call assert_equal([{'group': 'MyGroup1', 'id': 11, 'priority': 10, 'pos1': [1, 4, 2], 'pos2': [1, 9, 2]}], getmatches()) + call assert_notequal(v1, v4) + call assert_equal(v5, v4) + call assert_equal(v6, v1) + call assert_equal(v7, v1) + call assert_equal(v8, v4) + call assert_equal(v9, v4) + call assert_equal(v10, v1) + + " Check, that setmatches() can correctly restore the matches from matchaddpos() + call matchadd('MyGroup1', '\%2lmatchadd') + let m=getmatches() + call clearmatches() + call setmatches(m) + call assert_equal([{'group': 'MyGroup1', 'id': 11, 'priority': 10, 'pos1': [1, 4, 2], 'pos2': [1,9, 2]}, {'group': 'MyGroup1', 'pattern': '\%2lmatchadd', 'priority': 10, 'id': 12}], getmatches()) highlight MyGroup1 NONE highlight MyGroup2 NONE diff --git a/src/nvim/testdir/test_matchadd_conceal_utf8.vim b/src/nvim/testdir/test_matchadd_conceal_utf8.vim index 24c848a99c..160d0598a1 100644 --- a/src/nvim/testdir/test_matchadd_conceal_utf8.vim +++ b/src/nvim/testdir/test_matchadd_conceal_utf8.vim @@ -1,5 +1,5 @@ " Test for matchadd() and conceal feature using utf-8. -if !has('conceal') || !has('multi_byte') +if !has('conceal') finish endif diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index d77dac69c7..4e6d96ee48 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -206,11 +206,10 @@ func Test_mkview_loadview_with_viewdir() " The directory Xviewdir/ should have been created and the view " should be stored in that directory. - let pathsep = has('win32') ? '\' : '/' - call assert_equal('Xviewdir' . pathsep . + call assert_equal('Xviewdir/' . \ substitute( \ substitute( - \ expand('%:p'), pathsep, '=+', 'g'), ':', '=-', 'g') . '=1.vim', + \ expand('%:p'), '/', '=+', 'g'), ':', '=-', 'g') . '=1.vim', \ glob('Xviewdir/*')) call assert_equal(1, &number) call assert_match('\*:mkview\*$', getline('.')) diff --git a/src/nvim/testdir/test_mksession_utf8.vim b/src/nvim/testdir/test_mksession_utf8.vim index 8ffbba2a1c..67af3a9ca2 100644 --- a/src/nvim/testdir/test_mksession_utf8.vim +++ b/src/nvim/testdir/test_mksession_utf8.vim @@ -3,7 +3,7 @@ set encoding=utf-8 scriptencoding utf-8 -if !has('multi_byte') || !has('mksession') +if !has('mksession') finish endif diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 945cd5a617..532beb9c39 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -1374,10 +1374,6 @@ func! Test_normal23_K() endfunc func! Test_normal24_rot13() - " This test uses multi byte characters - if !has("multi_byte") - return - endif " Testing for g?? g?g? new call append(0, 'abcdefghijklmnopqrstuvwxyzÀüâ') @@ -1628,10 +1624,6 @@ fun! Test_normal29_brace() endfunc fun! Test_normal30_changecase() - " This test uses multi byte characters - if !has("multi_byte") - return - endif new call append(0, 'This is a simple test: ÀüâΓ') norm! 1ggVu @@ -1901,42 +1893,36 @@ func! Test_g_ctrl_g() call assert_equal("\nCol 1 of 10; Line 1 of 2; Word 1 of 4; Char 1 of 23; Byte 1 of 22", a) set bin & eol& - if has('multi_byte') - call setline(1, ['FranΓ§ais', 'ζ₯ζ¬θͺ']) + call setline(1, ['FranΓ§ais', 'ζ₯ζ¬θͺ']) - let a = execute(":norm! \<Esc>gojlg\<c-g>") - call assert_equal("\nCol 4-3 of 9-6; Line 2 of 2; Word 2 of 2; Char 11 of 13; Byte 16 of 20", a) + let a = execute(":norm! \<Esc>gojlg\<c-g>") + call assert_equal("\nCol 4-3 of 9-6; Line 2 of 2; Word 2 of 2; Char 11 of 13; Byte 16 of 20", a) - let a = execute(":norm! \<Esc>gojvlg\<c-g>") - call assert_equal("\nSelected 1 of 2 Lines; 1 of 2 Words; 2 of 13 Chars; 6 of 20 Bytes", a) + let a = execute(":norm! \<Esc>gojvlg\<c-g>") + call assert_equal("\nSelected 1 of 2 Lines; 1 of 2 Words; 2 of 13 Chars; 6 of 20 Bytes", a) - let a = execute(":norm! \<Esc>goll\<c-v>jlg\<c-g>") - call assert_equal("\nSelected 4 Cols; 2 of 2 Lines; 2 of 2 Words; 6 of 13 Chars; 11 of 20 Bytes", a) + let a = execute(":norm! \<Esc>goll\<c-v>jlg\<c-g>") + call assert_equal("\nSelected 4 Cols; 2 of 2 Lines; 2 of 2 Words; 6 of 13 Chars; 11 of 20 Bytes", a) - set fenc=utf8 bomb - let a = execute(":norm! \<Esc>gojlg\<c-g>") - call assert_equal("\nCol 4-3 of 9-6; Line 2 of 2; Word 2 of 2; Char 11 of 13; Byte 16 of 20(+3 for BOM)", a) + set fenc=utf8 bomb + let a = execute(":norm! \<Esc>gojlg\<c-g>") + call assert_equal("\nCol 4-3 of 9-6; Line 2 of 2; Word 2 of 2; Char 11 of 13; Byte 16 of 20(+3 for BOM)", a) - set fenc=utf16 bomb - let a = execute(":norm! g\<c-g>") - call assert_equal("\nCol 4-3 of 9-6; Line 2 of 2; Word 2 of 2; Char 11 of 13; Byte 16 of 20(+2 for BOM)", a) + set fenc=utf16 bomb + let a = execute(":norm! g\<c-g>") + call assert_equal("\nCol 4-3 of 9-6; Line 2 of 2; Word 2 of 2; Char 11 of 13; Byte 16 of 20(+2 for BOM)", a) - set fenc=utf32 bomb - let a = execute(":norm! g\<c-g>") - call assert_equal("\nCol 4-3 of 9-6; Line 2 of 2; Word 2 of 2; Char 11 of 13; Byte 16 of 20(+4 for BOM)", a) + set fenc=utf32 bomb + let a = execute(":norm! g\<c-g>") + call assert_equal("\nCol 4-3 of 9-6; Line 2 of 2; Word 2 of 2; Char 11 of 13; Byte 16 of 20(+4 for BOM)", a) - set fenc& bomb& - endif + set fenc& bomb& set ff& bwipe! endfunc fun! Test_normal34_g_cmd3() - if !has("multi_byte") - return - endif - " Test for g8 new let a=execute(':norm! 1G0g8') @@ -1955,9 +1941,6 @@ fun! Test_normal34_g_cmd3() endfunc func Test_normal_8g8() - if !has("multi_byte") - return - endif new " Test 8g8 which finds invalid utf8 at or after the cursor. @@ -2298,11 +2281,6 @@ func! Test_normal45_drop() endfunc func! Test_normal46_ignore() - " This test uses multi byte characters - if !has("multi_byte") - return - endif - new " How to test this? " let's just for now test, that the buffer @@ -2475,9 +2453,7 @@ func Test_normal54_Ctrl_bsl() call assert_equal(['abcdefghijklmn'], getline(1,'$')) exe "norm! df\<c-\>m" call assert_equal(['abcdefghijklmn'], getline(1,'$')) - if !has("multi_byte") - return - endif + call setline(2, 'abcdefghijklmnΔf') norm! 2gg0 exe "norm! df\<Char-0x101>" @@ -2541,9 +2517,6 @@ func Test_changelist() endfunc func Test_delete_until_paragraph() - if !has('multi_byte') - return - endif new normal grΓ‘dv} call assert_equal('Γ‘', getline(1)) diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index a6ebd7b023..ffd344200b 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -210,8 +210,6 @@ func Test_set_completion() call assert_equal('"set fileencodings:ucs-bom,utf-8,default,latin1', @:) " Expand directories. - let shellslash = &shellslash - set shellslash call feedkeys(":set cdpath=./\<C-A>\<C-B>\"\<CR>", 'tx') call assert_match('./samples/ ', @:) call assert_notmatch('./small.vim ', @:) @@ -223,7 +221,6 @@ func Test_set_completion() call feedkeys(":set tags=./\\\\ dif\<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"set tags=./\\ diff diffexpr diffopt', @:) set tags& - let &shellslash = shellslash endfunc func Test_set_errors() diff --git a/src/nvim/testdir/test_plus_arg_edit.vim b/src/nvim/testdir/test_plus_arg_edit.vim index 71dbea1991..f6d31e7626 100644 --- a/src/nvim/testdir/test_plus_arg_edit.vim +++ b/src/nvim/testdir/test_plus_arg_edit.vim @@ -1,7 +1,7 @@ " Tests for complicated + argument to :edit command function Test_edit() - call writefile(["foo|bar"], "Xfile1") - call writefile(["foo/bar"], "Xfile2") + call writefile(["foo|bar"], "Xfile1") + call writefile(["foo/bar"], "Xfile2") edit +1|s/|/PIPE/|w Xfile1| e Xfile2|1 | s/\//SLASH/|w call assert_equal(["fooPIPEbar"], readfile("Xfile1")) call assert_equal(["fooSLASHbar"], readfile("Xfile2")) diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim index 98e9de9ffb..0027a0a52e 100644 --- a/src/nvim/testdir/test_popup.vim +++ b/src/nvim/testdir/test_popup.vim @@ -14,7 +14,7 @@ func ListMonths() if !empty(entered) let mth = filter(mth, 'v:val=~"^".entered') endif - call complete(1, mth) + call complete(1, mth) return '' endfunc @@ -72,7 +72,7 @@ func Test_popup_complete() call feedkeys("aJu\<f5>\<c-p>l\<c-y>", 'tx') call assert_equal(["Jul"], getline(1,2)) %d - + " any-non printable, non-white character: Add this character and " reduce number of matches call feedkeys("aJu\<f5>\<c-p>l\<c-n>\<c-y>", 'tx') @@ -94,7 +94,7 @@ func Test_popup_complete() call feedkeys("aJ\<f5>".repeat("\<c-n>",3)."\<c-l>\<esc>", 'tx') call assert_equal(["J"], getline(1,2)) %d - + " <c-l> - Insert one character from the current match call feedkeys("aJ\<f5>".repeat("\<c-n>",4)."\<c-l>\<esc>", 'tx') call assert_equal(["January"], getline(1,2)) @@ -516,6 +516,35 @@ func Test_completion_ctrl_e_without_autowrap() q! endfunc +func DummyCompleteSix() + call complete(1, ['Hello', 'World']) + return '' +endfunction + +" complete() correctly clears the list of autocomplete candidates +" See #1411 +func Test_completion_clear_candidate_list() + new + %d + " select first entry from the completion popup + call feedkeys("a xxx\<C-N>\<C-R>=DummyCompleteSix()\<CR>", "tx") + call assert_equal('Hello', getline(1)) + %d + " select second entry from the completion popup + call feedkeys("a xxx\<C-N>\<C-R>=DummyCompleteSix()\<CR>\<C-N>", "tx") + call assert_equal('World', getline(1)) + %d + " select original text + call feedkeys("a xxx\<C-N>\<C-R>=DummyCompleteSix()\<CR>\<C-N>\<C-N>", "tx") + call assert_equal(' xxx', getline(1)) + %d + " back at first entry from completion list + call feedkeys("a xxx\<C-N>\<C-R>=DummyCompleteSix()\<CR>\<C-N>\<C-N>\<C-N>", "tx") + call assert_equal('Hello', getline(1)) + + bw! +endfunc + func Test_completion_respect_bs_option() new let li = ["aaa", "aaa12345", "aaaabcdef", "aaaABC"] @@ -575,82 +604,6 @@ func Test_completion_comment_formatting() bwipe! endfunc -func DummyCompleteSix() - call complete(1, ['Hello', 'World']) - return '' -endfunction - -" complete() correctly clears the list of autocomplete candidates -func Test_completion_clear_candidate_list() - new - %d - " select first entry from the completion popup - call feedkeys("a xxx\<C-N>\<C-R>=DummyCompleteSix()\<CR>", "tx") - call assert_equal('Hello', getline(1)) - %d - " select second entry from the completion popup - call feedkeys("a xxx\<C-N>\<C-R>=DummyCompleteSix()\<CR>\<C-N>", "tx") - call assert_equal('World', getline(1)) - %d - " select original text - call feedkeys("a xxx\<C-N>\<C-R>=DummyCompleteSix()\<CR>\<C-N>\<C-N>", "tx") - call assert_equal(' xxx', getline(1)) - %d - " back at first entry from completion list - call feedkeys("a xxx\<C-N>\<C-R>=DummyCompleteSix()\<CR>\<C-N>\<C-N>\<C-N>", "tx") - call assert_equal('Hello', getline(1)) - - bw! -endfunc - -func Test_popup_complete_backwards() - new - call setline(1, ['Post', 'Port', 'Po']) - let expected=['Post', 'Port', 'Port'] - call cursor(3,2) - call feedkeys("A\<C-X>". repeat("\<C-P>", 3). "rt\<cr>", 'tx') - call assert_equal(expected, getline(1,'$')) - bwipe! -endfunc - -func Test_popup_and_preview_autocommand() - " This used to crash Vim - if !has('python') - return - endif - let h = winheight(0) - if h < 15 - return - endif - new - augroup MyBufAdd - au! - au BufAdd * nested tab sball - augroup END - set omnifunc=pythoncomplete#Complete - call setline(1, 'import os') - " make the line long - call setline(2, ' os.') - $ - call feedkeys("A\<C-X>\<C-O>\<C-N>\<C-N>\<C-N>\<enter>\<esc>", 'tx') - call assert_equal("import os", getline(1)) - call assert_match(' os.\(EX_IOERR\|O_CREAT\)$', getline(2)) - call assert_equal(1, winnr('$')) - " previewwindow option is not set - call assert_equal(0, &previewwindow) - norm! gt - call assert_equal(0, &previewwindow) - norm! gT - call assert_equal(10, tabpagenr('$')) - tabonly - pclose - augroup MyBufAdd - au! - augroup END - augroup! MyBufAdd - bw! -endfunc - func MessCompleteMonths() for m in split("Jan Feb Mar Apr May Jun Jul Aug Sep") call complete_add(m) @@ -716,10 +669,10 @@ func Test_popup_and_window_resize() if h < 15 return endif - let g:buf = term_start([$NVIM_PRG, '--clean', '-c', 'set noswapfile'], {'term_rows': h / 3}) - call term_sendkeys(g:buf, (h / 3 - 1)."o\<esc>G") - call term_sendkeys(g:buf, "i\<c-x>") + let g:buf = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile'], {'term_rows': h / 3}) + call term_sendkeys(g:buf, (h / 3 - 1)."o\<esc>") call term_wait(g:buf, 200) + call term_sendkeys(g:buf, "Gi\<c-x>") call term_sendkeys(g:buf, "\<c-v>") call term_wait(g:buf, 100) " popup first entry "!" must be at the top @@ -740,6 +693,55 @@ func Test_popup_and_window_resize() bwipe! endfunc +func Test_popup_and_preview_autocommand() + " This used to crash Vim + if !has('python') + return + endif + let h = winheight(0) + if h < 15 + return + endif + new + augroup MyBufAdd + au! + au BufAdd * nested tab sball + augroup END + set omnifunc=pythoncomplete#Complete + call setline(1, 'import os') + " make the line long + call setline(2, ' os.') + $ + call feedkeys("A\<C-X>\<C-O>\<C-N>\<C-N>\<C-N>\<enter>\<esc>", 'tx') + call assert_equal("import os", getline(1)) + call assert_match(' os.\(EX_IOERR\|O_CREAT\)$', getline(2)) + call assert_equal(1, winnr('$')) + " previewwindow option is not set + call assert_equal(0, &previewwindow) + norm! gt + call assert_equal(0, &previewwindow) + norm! gT + call assert_equal(10, tabpagenr('$')) + tabonly + pclose + augroup MyBufAdd + au! + augroup END + augroup! MyBufAdd + bw! +endfunc + + +func Test_popup_complete_backwards() + new + call setline(1, ['Post', 'Port', 'Po']) + let expected=['Post', 'Port', 'Port'] + call cursor(3,2) + call feedkeys("A\<C-X>". repeat("\<C-P>", 3). "rt\<cr>", 'tx') + call assert_equal(expected, getline(1,'$')) + bwipe! +endfunc + func Test_popup_complete_info_01() new inoremap <buffer><F5> <C-R>=complete_info().mode<CR> diff --git a/src/nvim/testdir/test_profile.vim b/src/nvim/testdir/test_profile.vim index 1aa4d5eaf8..9b78d90b0b 100644 --- a/src/nvim/testdir/test_profile.vim +++ b/src/nvim/testdir/test_profile.vim @@ -183,7 +183,7 @@ func Test_profile_errors() endfunc func Test_profile_truncate_mbyte() - if !has('multi_byte') || &enc !=# 'utf-8' + if &enc !=# 'utf-8' return endif diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index d8a231c52e..884ada7e88 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -1,9 +1,6 @@ " Tests for put commands, e.g. ":put", "p", "gp", "P", "gP", etc. func Test_put_block() - if !has('multi_byte') - return - endif new call feedkeys("i\<C-V>u2500\<CR>x\<ESC>", 'x') call feedkeys("\<C-V>y", 'x') diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index ce0b8f1be8..1072c51aa2 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -139,8 +139,6 @@ func XlistTests(cchar) \ ' 5:50 col 25 55: one'], l) " Test for module names, one needs to explicitly set `'valid':v:true` so - let save_shellslash = &shellslash - set shellslash call g:Xsetlist([ \ {'lnum':10,'col':5,'type':'W','module':'Data.Text','text':'ModuleWarning','nr':11,'valid':v:true}, \ {'lnum':20,'col':10,'type':'W','module':'Data.Text','filename':'Data/Text.hs','text':'ModuleWarning','nr':22,'valid':v:true}, @@ -149,7 +147,6 @@ func XlistTests(cchar) call assert_equal([' 1 Data.Text:10 col 5 warning 11: ModuleWarning', \ ' 2 Data.Text:20 col 10 warning 22: ModuleWarning', \ ' 3 Data/Text.hs:30 col 15 warning 33: FileWarning'], l) - let &shellslash = save_shellslash " Error cases call assert_fails('Xlist abc', 'E488:') diff --git a/src/nvim/testdir/test_regex_char_classes.vim b/src/nvim/testdir/test_regex_char_classes.vim index 7873502943..c1a4202c2b 100644 --- a/src/nvim/testdir/test_regex_char_classes.vim +++ b/src/nvim/testdir/test_regex_char_classes.vim @@ -1,9 +1,6 @@ " Tests for regexp with backslash and other special characters inside [] " Also test backslash for hex/octal numbered character. " -if !has('multi_byte') - finish -endif scriptencoding utf-8 diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index 8e284ba042..5d4c2a015f 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -480,9 +480,6 @@ endfunc " Test for search('multi-byte char', 'bce') func Test_search_multibyte() - if !has('multi_byte') - return - endif let save_enc = &encoding set encoding=utf8 enew! @@ -499,7 +496,7 @@ func Test_incsearch_substitute_dump() return endif if !CanRunVimInTerminal() - return + throw 'Skipped: cannot make screendumps' endif call writefile([ \ 'set incsearch hlsearch scrolloff=0', @@ -530,7 +527,7 @@ endfunc func Test_incsearch_with_change() if !has('timers') || !exists('+incsearch') || !CanRunVimInTerminal() - return + throw 'Skipped: cannot make screendumps and/or timers feature and/or incsearch option missing' endif call writefile([ diff --git a/src/nvim/testdir/test_source_utf8.vim b/src/nvim/testdir/test_source_utf8.vim index c29c2ec1f3..e93ea29dff 100644 --- a/src/nvim/testdir/test_source_utf8.vim +++ b/src/nvim/testdir/test_source_utf8.vim @@ -1,7 +1,4 @@ " Test the :source! command -if !has('multi_byte') - finish -endif func Test_source_utf8() " check that sourcing a script with 0x80 as second byte works diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim index 230cb72335..e49b5542fa 100644 --- a/src/nvim/testdir/test_spell.vim +++ b/src/nvim/testdir/test_spell.vim @@ -137,10 +137,8 @@ func Test_spellinfo() set enc=cp1250 spell spelllang=en call assert_match("^\nfile: .*/runtime/spell/en.ascii.spl\n$", execute('spellinfo')) - if has('multi_byte') - set enc=utf-8 spell spelllang=en - call assert_match("^\nfile: .*/runtime/spell/en.utf-8.spl\n$", execute('spellinfo')) - endif + set enc=utf-8 spell spelllang=en + call assert_match("^\nfile: .*/runtime/spell/en.utf-8.spl\n$", execute('spellinfo')) set enc=latin1 spell spelllang=en_us,en_nz call assert_match("^\n" . diff --git a/src/nvim/testdir/test_startup_utf8.vim b/src/nvim/testdir/test_startup_utf8.vim index f824925450..b24b0eb5cf 100644 --- a/src/nvim/testdir/test_startup_utf8.vim +++ b/src/nvim/testdir/test_startup_utf8.vim @@ -1,7 +1,4 @@ " Tests for startup using utf-8. -if !has('multi_byte') - finish -endif source shared.vim " source screendump.vim @@ -66,7 +63,7 @@ endfunc func Test_detect_ambiwidth() if !CanRunVimInTerminal() - return + throw 'Skipped: cannot run Vim in a terminal window' endif " Use the title termcap entries to output the escape sequence. diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim index ea29da0262..b86340a23a 100644 --- a/src/nvim/testdir/test_statusline.vim +++ b/src/nvim/testdir/test_statusline.vim @@ -86,11 +86,8 @@ func Test_statusline() call assert_match('^Xstatusline\s*$', s:get_statusline()) " %F: Full path to the file in the buffer. - let shellslash = &shellslash - set shellslash set statusline=%F call assert_match('/testdir/Xstatusline\s*$', s:get_statusline()) - let &shellslash = shellslash " %h: Help buffer flag, text is "[help]". " %H: Help buffer flag, text is ",HLP". diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index a3de879b2a..fc0dc6693c 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -470,7 +470,7 @@ func Test_bg_detection() hi Normal ctermbg=NONE endfunc -fun Test_synstack_synIDtrans() +func Test_synstack_synIDtrans() new setfiletype c syntax on @@ -494,6 +494,39 @@ fun Test_synstack_synIDtrans() bw! endfunc +" Check highlighting for a small piece of C code with a screen dump. +func Test_syntax_c() + if !CanRunVimInTerminal() + throw 'Skipped: cannot make screendumps' + endif + call writefile([ + \ '/* comment line at the top */', + \ ' int', + \ 'main(int argc, char **argv)// another comment', + \ '{', + \ '#if 0', + \ ' int not_used;', + \ '#else', + \ ' int used;', + \ '#endif', + \ ' printf("Just an example piece of C code\n");', + \ ' return 0x0ff;', + \ '}', + \ ' static void', + \ 'myFunction(const double count, struct nothing, long there) {', + \ ' // 123: nothing to read here', + \ ' for (int i = 0; i < count; ++i) {', + \ ' break;', + \ ' }', + \ '}', + \ ], 'Xtest.c') + let buf = RunVimInTerminal('Xtest.c', {}) + call VerifyScreenDump(buf, 'Test_syntax_c_01') + call StopVimInTerminal(buf) + + call delete('Xtest.c') +endfun + " Using \z() in a region with NFA failing should not crash. func Test_syn_wrong_z_one() new diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim index 8043d13433..c35ddc4473 100644 --- a/src/nvim/testdir/test_tabpage.vim +++ b/src/nvim/testdir/test_tabpage.vim @@ -558,7 +558,7 @@ endfunc func Test_tabpage_cmdheight() if !CanRunVimInTerminal() - throw 'Skipped: only works with terminal' + throw 'Skipped: cannot make screendumps' endif call writefile([ \ 'set laststatus=2', diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index 9384989a35..24c735865c 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -48,7 +48,7 @@ func Test_repeat_many() endif sleep 200m call timer_stop(timer) - call assert_inrange((has('mac') ? 1 : 2), LoadAdjust(4), g:val) + call assert_inrange((has('mac') ? 1 : 2), LoadAdjust(5), g:val) endfunc func Test_with_partial_callback() diff --git a/src/nvim/testdir/test_true_false.vim b/src/nvim/testdir/test_true_false.vim index ad865bb113..84aca737ac 100644 --- a/src/nvim/testdir/test_true_false.vim +++ b/src/nvim/testdir/test_true_false.vim @@ -57,9 +57,6 @@ endfunc " Test using TRUE or FALSE values for an argument. func Test_true_false_arg() - let shellslash = &shellslash - let wildignore = &wildignore - set shellslash call Try_arg_true_false('count(["a", "A"], "a", %v%)', 1, 2) set wildignore=*.swp @@ -113,8 +110,6 @@ func Test_true_false_arg() let here_id = synID(1, 3, 0) call Try_arg_true_false('synID(1, 3, %v%)', here_id, brackets_id) bwipe! - let &wildignore = wildignore - let &shellslash = shellslash endfunc function Try_arg_non_zero(expr, false_val, true_val) @@ -134,8 +129,6 @@ func Test_non_zero_arg() " call test_settime(93784) " call Try_arg_non_zero("mode(%v%)", 'x', 'x!') " call test_settime(0) - let shellslash = &shellslash - set shellslash call Try_arg_non_zero("shellescape('foo%', %v%)", "'foo%'", "'foo\\%'") @@ -154,6 +147,4 @@ func Test_non_zero_arg() let r = visualmode(v) call assert_equal('', r, 'result for ' . v . ' is not "" but ' . r) endfor - - let &shellslash = shellslash endfunc diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim index 0cb5dc4033..86674889ef 100644 --- a/src/nvim/testdir/test_undo.vim +++ b/src/nvim/testdir/test_undo.vim @@ -376,10 +376,9 @@ funct Test_undofile() " Replace windows drive such as C:... into C%... let cwd = substitute(cwd, '^\([a-zA-Z]\):', '\1%', 'g') endif - let pathsep = has('win32') ? '\' : '/' - let cwd = substitute(cwd . pathsep . 'Xundofoo', pathsep, '%', 'g') + let cwd = substitute(cwd . '/Xundofoo', '/', '%', 'g') if has('persistent_undo') - call assert_equal('Xundodir' . pathsep . cwd, undofile('Xundofoo')) + call assert_equal('Xundodir/' . cwd, undofile('Xundofoo')) else call assert_equal('', undofile('Xundofoo')) endif diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim index b23a4aa62f..2c7cb7bab7 100644 --- a/src/nvim/testdir/test_usercommands.vim +++ b/src/nvim/testdir/test_usercommands.vim @@ -201,7 +201,6 @@ func Test_CmdErrors() call assert_fails('com! docmd :', 'E183:') call assert_fails('com! \<Tab> :', 'E182:') call assert_fails('com! _ :', 'E182:') - call assert_fails('com! X :', 'E841:') call assert_fails('com! - DoCmd :', 'E175:') call assert_fails('com! -xxx DoCmd :', 'E181:') call assert_fails('com! -addr DoCmd :', 'E179:') diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim index 24e3db86fb..b1f33f56dd 100644 --- a/src/nvim/testdir/test_utf8.vim +++ b/src/nvim/testdir/test_utf8.vim @@ -1,7 +1,4 @@ " Tests for Unicode manipulations -if !has('multi_byte') - finish -endif " Visual block Insert adjusts for multi-byte char diff --git a/src/nvim/testdir/test_utf8_comparisons.vim b/src/nvim/testdir/test_utf8_comparisons.vim index 576e86142f..1fc670aafd 100644 --- a/src/nvim/testdir/test_utf8_comparisons.vim +++ b/src/nvim/testdir/test_utf8_comparisons.vim @@ -1,10 +1,6 @@ " Tests for case-insensitive UTF-8 comparisons (utf_strnicmp() in mbyte.c) " Also test "g~ap". -if !has("multi_byte") - finish -endif - function! Ch(a, op, b, expected) call assert_equal(eval(printf('"%s" %s "%s"', a:a, a:op, a:b)), a:expected, \ printf('"%s" %s "%s" should return %d', a:a, a:op, a:b, a:expected)) diff --git a/src/nvim/testdir/test_virtualedit.vim b/src/nvim/testdir/test_virtualedit.vim index abe79f6a4a..67adede8d7 100644 --- a/src/nvim/testdir/test_virtualedit.vim +++ b/src/nvim/testdir/test_virtualedit.vim @@ -48,11 +48,9 @@ func Test_replace_end_of_line() call setline(1, range(20)) exe "normal! gg2jv10lr-" call assert_equal(["1", "-----------", "3"], getline(2,4)) - if has('multi_byte') - call setline(1, range(20)) - exe "normal! gg2jv10lr\<c-k>hh" - call assert_equal(["1", "βββββββββββ", "3"], getline(2,4)) - endif + call setline(1, range(20)) + exe "normal! gg2jv10lr\<c-k>hh" + call assert_equal(["1", "βββββββββββ", "3"], getline(2,4)) bwipe! set virtualedit= diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index f69273635c..7fc8cdd7f4 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -2,9 +2,6 @@ func Test_block_shift_multibyte() " Uses double-wide character. - if !has('multi_byte') - return - endif split call setline(1, ['xγΉxxx', 'γΉxxx']) exe "normal 1G0l\<C-V>jl>" diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index 2a07a04401..c87c0a0af4 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -42,6 +42,8 @@ function Test_window_cmd_wincmd_gf() function s:swap_exists() let v:swapchoice = s:swap_choice endfunc + " Remove the catch-all that runtest.vim adds + au! SwapExists augroup test_window_cmd_wincmd_gf autocmd! exec "autocmd SwapExists " . fname . " call s:swap_exists()" @@ -144,6 +146,21 @@ func Test_window_preview() call assert_fails('wincmd P', 'E441:') endfunc +func Test_window_preview_from_help() + filetype on + call writefile(['/* some C code */'], 'Xpreview.c') + help + pedit Xpreview.c + wincmd P + call assert_equal(1, &previewwindow) + call assert_equal('c', &filetype) + wincmd z + + filetype off + close + call delete('Xpreview.c') +endfunc + func Test_window_exchange() e Xa @@ -519,6 +536,7 @@ func Test_winrestcmd() endfunc function! Fun_RenewFile() + " Need to wait a bit for the timestamp to be older. sleep 2 silent execute '!echo "1" > tmp.txt' sp @@ -536,7 +554,6 @@ func Test_window_prevwin() call writefile(['2'], 'tmp.txt') new tmp.txt q - " Need to wait a bit for the timestamp to be older. call Fun_RenewFile() call assert_equal(2, winnr()) wincmd p diff --git a/src/nvim/testdir/test_wordcount.vim b/src/nvim/testdir/test_wordcount.vim index 75c4e4bffa..6a3d4109a8 100644 --- a/src/nvim/testdir/test_wordcount.vim +++ b/src/nvim/testdir/test_wordcount.vim @@ -1,9 +1,5 @@ " Test for wordcount() function -if !has('multi_byte') - finish -endif - func Test_wordcount() let save_enc = &enc set encoding=utf-8 diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim index 6d88c0d8cd..6066d61af4 100644 --- a/src/nvim/testdir/test_writefile.vim +++ b/src/nvim/testdir/test_writefile.vim @@ -33,7 +33,7 @@ func Test_writefile_fails_gently() endfunc func Test_writefile_fails_conversion() - if !has('multi_byte') || !has('iconv') || system('uname -s') =~ 'SunOS' + if !has('iconv') || system('uname -s') =~ 'SunOS' return endif " Without a backup file the write won't happen if there is a conversion diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 519ef1cccd..9fdc6eceba 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -103,6 +103,7 @@ typedef struct { bool busy, is_invisible; bool cork, overflow; bool cursor_color_changed; + bool is_starting; cursorentry_T cursor_shapes[SHAPE_IDX_COUNT]; HlAttrs clear_attrs; kvec_t(HlAttrs) attrs; @@ -122,8 +123,9 @@ typedef struct { int reset_scroll_region; int set_cursor_style, reset_cursor_style; int save_title, restore_title; - int enter_undercurl_mode, exit_undercurl_mode, set_underline_color; int get_bg; + int set_underline_style; + int set_underline_color; } unibi_ext; char *space_buf; } TUIData; @@ -215,6 +217,7 @@ static void terminfo_start(UI *ui) data->unibi_ext.set_cursor_style = -1; data->unibi_ext.reset_cursor_style = -1; data->unibi_ext.get_bg = -1; + data->unibi_ext.set_underline_color = -1; data->out_fd = 1; data->out_isatty = os_isatty(data->out_fd); @@ -396,6 +399,7 @@ static void tui_main(UIBridgeData *bridge, UI *ui) ui->data = data; data->bridge = bridge; data->loop = &tui_loop; + data->is_starting = true; kv_init(data->invalid_regions); signal_watcher_init(data->loop, &data->winch_handle, ui); signal_watcher_init(data->loop, &data->cont_handle, data); @@ -532,7 +536,7 @@ static void update_attrs(UI *ui, int attr_id) bool underline; bool undercurl; - if (data->unibi_ext.enter_undercurl_mode) { + if (data->unibi_ext.set_underline_style != -1) { underline = attr & HL_UNDERLINE; undercurl = attr & HL_UNDERCURL; } else { @@ -575,10 +579,11 @@ static void update_attrs(UI *ui, int attr_id) if (italic) { unibi_out(ui, unibi_enter_italics_mode); } - if (undercurl && data->unibi_ext.enter_undercurl_mode) { - unibi_out_ext(ui, data->unibi_ext.enter_undercurl_mode); + if (undercurl && data->unibi_ext.set_underline_style != -1) { + UNIBI_SET_NUM_VAR(data->params[0], 3); + unibi_out_ext(ui, data->unibi_ext.set_underline_style); } - if ((undercurl || underline) && data->unibi_ext.set_underline_color) { + if ((undercurl || underline) && data->unibi_ext.set_underline_color != -1) { int color = attrs.rgb_sp_color; if (color != -1) { UNIBI_SET_NUM_VAR(data->params[0], (color >> 16) & 0xff); // red @@ -888,7 +893,7 @@ static void tui_grid_resize(UI *ui, Integer g, Integer width, Integer height) r->right = MIN(r->right, grid->width); } - if (!got_winch && (!starting || did_user_set_dimensions)) { + if (!got_winch && (!data->is_starting || did_user_set_dimensions)) { // Resize the _host_ terminal. UNIBI_SET_NUM_VAR(data->params[0], (int)height); UNIBI_SET_NUM_VAR(data->params[1], (int)width); @@ -1051,6 +1056,7 @@ static void tui_mode_change(UI *ui, String mode, Integer mode_idx) { TUIData *data = ui->data; tui_set_mode(ui, (ModeShape)mode_idx); + data->is_starting = false; // mode entered, no longer starting data->showing_mode = (ModeShape)mode_idx; } @@ -1355,7 +1361,7 @@ static void tui_guess_size(UI *ui) int width = 0, height = 0; // 1 - look for non-default 'columns' and 'lines' options during startup - if (starting && (Columns != DFLT_COLS || Rows != DFLT_ROWS)) { + if (data->is_starting && (Columns != DFLT_COLS || Rows != DFLT_ROWS)) { did_user_set_dimensions = true; assert(Columns >= INT_MIN && Columns <= INT_MAX); assert(Rows >= INT_MIN && Rows <= INT_MAX); @@ -1908,13 +1914,19 @@ static void augment_terminfo(TUIData *data, const char *term, data->unibi_ext.disable_mouse = (int)unibi_add_ext_str( ut, "ext.disable_mouse", "\x1b[?1002l\x1b[?1006l"); - int ext_bool_Su = unibi_find_ext_bool(ut, "Su"); // used by kitty - if (vte_version >= 5102 - || (ext_bool_Su != -1 && unibi_get_ext_bool(ut, (size_t)ext_bool_Su))) { - data->unibi_ext.enter_undercurl_mode = (int)unibi_add_ext_str( - ut, "ext.enter_undercurl_mode", "\x1b[4:3m"); - data->unibi_ext.exit_undercurl_mode = (int)unibi_add_ext_str( - ut, "ext.exit_undercurl_mode", "\x1b[4:0m"); + // Extended underline. + // terminfo will have Smulx for this (but no support for colors yet). + data->unibi_ext.set_underline_style = unibi_find_ext_str(ut, "Smulx"); + if (data->unibi_ext.set_underline_style == -1) { + int ext_bool_Su = unibi_find_ext_bool(ut, "Su"); // used by kitty + if (vte_version >= 5102 + || (ext_bool_Su != -1 + && unibi_get_ext_bool(ut, (size_t)ext_bool_Su))) { + data->unibi_ext.set_underline_style = (int)unibi_add_ext_str( + ut, "ext.set_underline_style", "\x1b[4:%p1%dm"); + } + } + if (data->unibi_ext.set_underline_style != -1) { // Only support colon syntax. #9270 data->unibi_ext.set_underline_color = (int)unibi_add_ext_str( ut, "ext.set_underline_color", "\x1b[58:2::%p1%d:%p2%d:%p3%dm"); diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index 2cb3cf7ee7..9517b362af 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -480,8 +480,26 @@ static void ui_comp_raw_line(UI *ui, Integer grid, Integer row, if (curgrid != &default_grid) { flags = flags & ~kLineFlagWrap; } - assert(row < default_grid.Rows); - assert(clearcol <= default_grid.Columns); + + assert(endcol <= clearcol); + + // TODO(bfredl): this should not really be necessary. But on some condition + // when resizing nvim, a window will be attempted to be drawn on the older + // and possibly larger global screen size. + if (row >= default_grid.Rows) { + DLOG("compositor: invalid row %"PRId64" on grid %"PRId64, row, grid); + return; + } + if (clearcol > default_grid.Columns) { + DLOG("compositor: invalid last column %"PRId64" on grid %"PRId64, + clearcol, grid); + if (startcol >= default_grid.Columns) { + return; + } + clearcol = default_grid.Columns; + endcol = MIN(endcol, clearcol); + } + if (flags & kLineFlagInvalid || kv_size(layers) > curgrid->comp_index+1 || curgrid->blending) { diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 3e0a5907be..60737014b3 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -287,9 +287,10 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext() // functions of these names. The declarations would break if the defines had // been seen at that stage. But it must be before globals.h, where error_ga // is declared. -#define mch_errmsg(str) fprintf(stderr, "%s", (str)) -#define display_errors() fflush(stderr) -#define mch_msg(str) printf("%s", (str)) +#ifndef WIN32 +# define mch_errmsg(str) fprintf(stderr, "%s", (str)) +# define mch_msg(str) printf("%s", (str)) +#endif #include "nvim/globals.h" // global variables and messages #include "nvim/buffer_defs.h" // buffer and windows diff --git a/src/nvim/window.c b/src/nvim/window.c index 22a8969b88..4173245439 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -598,6 +598,7 @@ void win_set_minimal_style(win_T *wp) wp->w_p_cuc = false; wp->w_p_spell = false; wp->w_p_list = false; + wp->w_p_fdc = 0; // Hide EOB region: use " " fillchar and cleared highlighting if (wp->w_p_fcs_chars.eob != ' ') { @@ -615,6 +616,7 @@ void win_set_minimal_style(win_T *wp) xfree(old); } + // signcolumn: use 'auto' if (wp->w_p_scl[0] != 'a') { xfree(wp->w_p_scl); wp->w_p_scl = (char_u *)xstrdup("auto"); diff --git a/test/functional/api/proc_spec.lua b/test/functional/api/proc_spec.lua index e11e03203f..063d382790 100644 --- a/test/functional/api/proc_spec.lua +++ b/test/functional/api/proc_spec.lua @@ -4,8 +4,8 @@ local clear = helpers.clear local eq = helpers.eq local funcs = helpers.funcs local iswin = helpers.iswin +local neq = helpers.neq local nvim_argv = helpers.nvim_argv -local ok = helpers.ok local request = helpers.request local retry = helpers.retry local NIL = helpers.NIL @@ -63,8 +63,9 @@ describe('api', function() local pid = funcs.getpid() local pinfo = request('nvim_get_proc', pid) eq((iswin() and 'nvim.exe' or 'nvim'), pinfo.name) - ok(pinfo.pid == pid) - ok(type(pinfo.ppid) == 'number' and pinfo.ppid ~= pid) + eq(pinfo.pid, pid) + eq(type(pinfo.ppid), 'number') + neq(pinfo.ppid, pid) end) it('validates input', function() diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua index dbe9f20412..ddd044a10f 100644 --- a/test/functional/api/server_requests_spec.lua +++ b/test/functional/api/server_requests_spec.lua @@ -169,8 +169,7 @@ describe('server -> client', function() if method == "notification" then eq('done!', eval('rpcrequest('..cid..', "nested")')) elseif method == "nested_done" then - -- this should never have been sent - ok(false) + ok(false, 'this should never have been sent') end end diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 110b3a4b16..cd45914552 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -178,6 +178,11 @@ describe('API', function() -- Verify NO hit-enter prompt. eq({mode='n', blocking=false}, nvim("get_mode")) end) + + it('Does not cause heap buffer overflow with large output', function() + eq(eval('string(range(1000000))'), + nvim('command_output', 'echo range(1000000)')) + end) end) describe('nvim_eval', function() @@ -1148,6 +1153,13 @@ describe('API', function() before_each(function() meths.set_option('isident', '') end) + + local it_maybe_pending = it + if (helpers.isCI('appveyor') and os.getenv('CONFIGURATION') == 'MSVC_32') then + -- For "works with &opt" (flaky on MSVC_32), but not easy to skip alone. #10241 + it_maybe_pending = pending + end + local function simplify_east_api_node(line, east_api_node) if east_api_node == NIL then return nil @@ -1345,7 +1357,7 @@ describe('API', function() end assert:set_parameter('TableFormatLevel', 1000000) require('test.unit.viml.expressions.parser_tests')( - it, _check_parsing, hl, fmtn) + it_maybe_pending, _check_parsing, hl, fmtn) end) describe('nvim_list_uis', function() diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 180ed9aa02..2531b45521 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -707,11 +707,10 @@ describe('jobs', function() it('will return -1 if the wait timed out', function() source([[ call rpcnotify(g:channel, 'wait', jobwait([ - \ jobstart('exit 4'), \ jobstart((has('win32') ? 'Start-Sleep 10' : 'sleep 10').'; exit 5'), - \ ], has('win32') ? 6000 : 100)) + \ ], 100)) ]]) - eq({'notification', 'wait', {{4, -1}}}, next_msg()) + eq({'notification', 'wait', {{-1}}}, next_msg()) end) it('can pass 0 to check if a job exists', function() @@ -769,16 +768,79 @@ describe('jobs', function() -- ..c.."', '-c', '"..c.."'])") -- Create child with several descendants. + if iswin() then + source([[ + function! s:formatprocs(pid, prefix) + let result = '' + let result .= a:prefix . printf("%-24.24s%6s %12.12s %s\n", + \ s:procs[a:pid]['name'], + \ a:pid, + \ s:procs[a:pid]['Session Name'], + \ s:procs[a:pid]['Session']) + if has_key(s:procs[a:pid], 'children') + for pid in s:procs[a:pid]['children'] + let result .= s:formatprocs(pid, a:prefix . ' ') + endfor + endif + return result + endfunction + + function! PsTree() abort + let s:procs = {} + for proc in map( + \ map( + \ systemlist('tasklist /NH'), + \ 'substitute(v:val, "\r", "", "")'), + \ 'split(v:val, "\\s\\+")') + if len(proc) == 6 + let s:procs[proc[1]] ..']]'..[[= {'name': proc[0], + \ 'Session Name': proc[2], + \ 'Session': proc[3]} + endif + endfor + for pid in keys(s:procs) + let children = nvim_get_proc_children(str2nr(pid)) + if !empty(children) + let s:procs[pid]['children'] = children + for cpid in children + let s:procs[printf('%d', cpid)]['parent'] = str2nr(pid) + endfor + endif + endfor + let result = '' + for pid in sort(keys(s:procs), {i1, i2 -> i1 - i2}) + if !has_key(s:procs[pid], 'parent') + let result .= s:formatprocs(pid, '') + endif + endfor + return result + endfunction + ]]) + end local sleep_cmd = (iswin() and 'ping -n 31 127.0.0.1' or 'sleep 30') local j = eval("jobstart('"..sleep_cmd..' | '..sleep_cmd..' | '..sleep_cmd.."')") local ppid = funcs.jobpid(j) local children - retry(nil, nil, function() - children = meths.get_proc_children(ppid) - eq((iswin() and 4 or 3), #children) - end) + if iswin() then + local status, result = pcall(retry, nil, nil, function() + children = meths.get_proc_children(ppid) + -- On Windows conhost.exe may exist, and + -- e.g. vctip.exe might appear. #10783 + ok(#children >= 3 and #children <= 5) + end) + if not status then + print('') + print(eval('PsTree()')) + error(result) + end + else + retry(nil, nil, function() + children = meths.get_proc_children(ppid) + eq(3, #children) + end) + end -- Assert that nvim_get_proc() sees the children. for _, child_pid in ipairs(children) do local info = meths.get_proc(child_pid) diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index 62a45fdf88..3b32c42ec0 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -226,6 +226,31 @@ describe('startup', function() clear{env={CDPATH='~doesnotexist'}} eq(',~doesnotexist', eval('&cdpath')) end) + + it('ENTER dismisses early message #7967', function() + local screen + screen = Screen.new(60, 6) + screen:attach() + command([[let g:id = termopen('"]]..nvim_prog.. + [[" -u NONE -i NONE --cmd "set noruler" --cmd "let g:foo = g:bar"')]]) + screen:expect([[ + ^ | + Error detected while processing pre-vimrc command line: | + E121: Undefined variable: g:bar | + E15: Invalid expression: g:bar | + Press ENTER or type command to continue | + | + ]]) + command([[call chansend(g:id, "\n")]]) + screen:expect([[ + ^ | + ~ | + ~ | + [No Name] | + | + | + ]]) + end) end) describe('sysinit', function() diff --git a/test/functional/eval/timer_spec.lua b/test/functional/eval/timer_spec.lua index cd099e30ed..5c5b1b42cb 100644 --- a/test/functional/eval/timer_spec.lua +++ b/test/functional/eval/timer_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') -local feed, eq, eval = helpers.feed, helpers.eq, helpers.eval +local feed, eq, eval, ok = helpers.feed, helpers.eq, helpers.eval, helpers.ok local source, nvim_async, run = helpers.source, helpers.nvim_async, helpers.run local clear, command, funcs = helpers.clear, helpers.command, helpers.funcs local curbufmeths = helpers.curbufmeths @@ -19,59 +19,71 @@ describe('timers', function() end) it('works one-shot', function() - command("call timer_start(50, 'MyHandler')") - eq(0,eval("g:val")) - run(nil, nil, nil, load_adjust(200)) + eq(0, eval("[timer_start(10, 'MyHandler'), g:val][1]")) + run(nil, nil, nil, load_adjust(100)) eq(1,eval("g:val")) end) it('works one-shot when repeat=0', function() - command("call timer_start(50, 'MyHandler', {'repeat': 0})") - eq(0,eval("g:val")) - run(nil, nil, nil, load_adjust(200)) - eq(1,eval("g:val")) + eq(0, eval("[timer_start(10, 'MyHandler', {'repeat': 0}), g:val][1]")) + run(nil, nil, nil, load_adjust(100)) + eq(1, eval("g:val")) end) - it('works with repeat two', function() - command("call timer_start(50, 'MyHandler', {'repeat': 2})") - eq(0,eval("g:val")) - run(nil, nil, nil, load_adjust(300)) - eq(2,eval("g:val")) + eq(0, eval("[timer_start(10, 'MyHandler', {'repeat': 2}), g:val][1]")) + run(nil, nil, nil, load_adjust(20)) + retry(nil, load_adjust(300), function() + eq(2, eval("g:val")) + end) end) it('are triggered during sleep', function() - command("call timer_start(50, 'MyHandler', {'repeat': 2})") + source([[ + let g:val = -1 + func! MyHandler(timer) + if g:val >= 0 + let g:val += 1 + if g:val == 2 + call timer_stop(a:timer) + endif + endif + endfunc + ]]) + eval("timer_start(10, 'MyHandler', {'repeat': -1})") nvim_async("command", "sleep 10") - eq(0,eval("g:val")) - run(nil, nil, nil, load_adjust(300)) - eq(2,eval("g:val")) + eq(-1, eval("g:val")) -- timer did nothing yet. + nvim_async("command", "let g:val = 0") + run(nil, nil, nil, load_adjust(20)) + retry(nil, nil, function() + eq(2, eval("g:val")) + end) end) it('works with zero timeout', function() -- timer_start does still not invoke the callback immediately - eq(0,eval("[timer_start(0, 'MyHandler', {'repeat': 1000}), g:val][1]")) - run(nil, nil, nil, load_adjust(400)) - eq(1000,eval("g:val")) + eq(0, eval("[timer_start(0, 'MyHandler', {'repeat': 1000}), g:val][1]")) + retry(nil, nil, function() + eq(1000, eval("g:val")) + end) end) it('can be started during sleep', function() nvim_async("command", "sleep 10") -- this also tests that remote requests works during sleep - eval("timer_start(50, 'MyHandler', {'repeat': 2})") - eq(0,eval("g:val")) - run(nil, nil, nil, load_adjust(300)) - eq(2,eval("g:val")) + eq(0, eval("[timer_start(10, 'MyHandler', {'repeat': 2}), g:val][1]")) + run(nil, nil, nil, load_adjust(20)) + retry(nil, load_adjust(300), function() eq(2,eval("g:val")) end) end) it('are paused when event processing is disabled', function() - command("call timer_start(50, 'MyHandler', {'repeat': -1})") - run(nil, nil, nil, load_adjust(100)) + command("call timer_start(5, 'MyHandler', {'repeat': -1})") + run(nil, nil, nil, load_adjust(10)) local count = eval("g:val") -- shows two line error message and thus invokes the return prompt. -- if we start to allow event processing here, we need to change this test. feed(':throw "fatal error"<CR>') - run(nil, nil, nil, load_adjust(300)) + run(nil, nil, nil, load_adjust(30)) feed("<cr>") local diff = eval("g:val") - count assert(0 <= diff and diff <= 4, @@ -79,12 +91,14 @@ describe('timers', function() end) it('are triggered in blocking getchar() call', function() - command("call timer_start(50, 'MyHandler', {'repeat': -1})") - nvim_async("command", "let g:c = getchar()") - run(nil, nil, nil, load_adjust(300)) + command("call timer_start(5, 'MyHandler', {'repeat': -1})") + nvim_async("command", "let g:val = 0 | let g:c = getchar()") + retry(nil, nil, function() + local val = eval("g:val") + ok(val >= 2, "expected >= 2, got: "..tostring(val)) + eq(0, eval("getchar(1)")) + end) feed("c") - local count = eval("g:val") - assert(count >= 3, 'expected count >= 3, got: '..tostring(count)) eq(99, eval("g:c")) end) @@ -99,12 +113,15 @@ describe('timers', function() source([[ func! AddItem(timer) call nvim_buf_set_lines(0, 2, 2, v:true, ['ITEM 3']) + + " Meant to test for what Vim tests in Test_peek_and_get_char. call getchar(1) + redraw endfunc - call timer_start(200, 'AddItem') ]]) nvim_async("command", "let g:c2 = getchar()") + nvim_async("command", "call timer_start("..load_adjust(100)..", 'AddItem')") screen:expect([[ ITEM 1 | @@ -137,18 +154,15 @@ describe('timers', function() end) it('can be stopped', function() - local t = eval("timer_start(50, 'MyHandler', {'repeat': -1})") - eq(0,eval("g:val")) - run(nil, nil, nil, load_adjust(300)) - funcs.timer_stop(t) + local t_init_val = eval("[timer_start(5, 'MyHandler', {'repeat': -1}), g:val]") + eq(0, t_init_val[2]) + run(nil, nil, nil, load_adjust(30)) + funcs.timer_stop(t_init_val[1]) local count = eval("g:val") - run(nil, nil, nil, load_adjust(300)) + run(nil, load_adjust(300), nil, load_adjust(30)) local count2 = eval("g:val") -- when count is eval:ed after timer_stop this should be non-racy eq(count, count2) - assert((3 <= count and count <= load_adjust(7)), - string.format('expected (3 <= count <= %s), got: %s', - load_adjust(7), tostring(count))) end) it('can be stopped from the handler', function() @@ -162,10 +176,9 @@ describe('timers', function() endif endfunc ]]) + eq(0, eval("g:val")) command("call timer_start(10, 'MyHandler', {'repeat': -1})") - eq(0,eval("g:val")) - run(nil, nil, nil, load_adjust(50)) - retry(nil, 5000, function() + retry(nil, nil, function() eq(3, eval("g:val")) end) end) @@ -177,9 +190,9 @@ describe('timers', function() let g:val2 += 1 endfunc ]]) - command("call timer_start(20, 'MyHandler', {'repeat': 3})") - command("call timer_start(40, 'MyHandler2', {'repeat': 2})") - run(nil, nil, nil, load_adjust(300)) + command("call timer_start(2, 'MyHandler', {'repeat': 3})") + command("call timer_start(4, 'MyHandler2', {'repeat': 2})") + run(nil, nil, nil, load_adjust(30)) eq(3,eval("g:val")) eq(2,eval("g:val2")) end) @@ -189,13 +202,15 @@ describe('timers', function() let g:val = 0 func! MyHandler(timer) call timer_stop(a:timer) - sleep 100m + sleep 10m let g:val += 1 endfunc ]]) command("call timer_start(5, 'MyHandler', {'repeat': 1})") - run(nil, nil, nil, load_adjust(300)) - eq(1,eval("g:val")) + run(nil, nil, nil, load_adjust(10)) + retry(nil, load_adjust(100), function() + eq(1, eval("g:val")) + end) end) @@ -232,5 +247,4 @@ describe('timers', function() eq(1, eval('g:val')) end) - end) diff --git a/test/functional/fixtures/tty-test.c b/test/functional/fixtures/tty-test.c index e2a78a594b..4438b73a22 100644 --- a/test/functional/fixtures/tty-test.c +++ b/test/functional/fixtures/tty-test.c @@ -38,6 +38,9 @@ bool owns_tty(void) static void walk_cb(uv_handle_t *handle, void *arg) { if (!uv_is_closing(handle)) { +#ifdef WIN32 + uv_tty_set_mode(&tty, UV_TTY_MODE_NORMAL); +#endif uv_close(handle, NULL); } } diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index d4ace3030c..d040ff5f8c 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -445,6 +445,8 @@ function module.new_argv(...) for _, k in ipairs({ 'HOME', 'ASAN_OPTIONS', + 'TSAN_OPTIONS', + 'MSAN_OPTIONS', 'LD_LIBRARY_PATH', 'PATH', 'NVIM_LOG_FILE', diff --git a/test/functional/legacy/055_list_and_dict_types_spec.lua b/test/functional/legacy/055_list_and_dict_types_spec.lua index dcbd8b7dff..91ba8bb106 100644 --- a/test/functional/legacy/055_list_and_dict_types_spec.lua +++ b/test/functional/legacy/055_list_and_dict_types_spec.lua @@ -666,7 +666,7 @@ describe('list and dictionary types', function() Vim(put):E741: {'a': 99, 'b': 100} No remove() of write-protected scope-level variable: - Vim(put):E795: + Vim(put):E742: No extend() of write-protected scope-level variable: Vim(put):E742: No :unlet of variable in locked scope: diff --git a/test/functional/provider/python3_spec.lua b/test/functional/provider/python3_spec.lua index 68d4d1e2a1..a3c6c57a73 100644 --- a/test/functional/provider/python3_spec.lua +++ b/test/functional/provider/python3_spec.lua @@ -90,6 +90,12 @@ describe('python3 provider', function() eq({1, 2, {['key'] = 'val'}}, eval([[py3eval('[1, 2, {"key": "val"}]')]])) end) + it('pyxeval #10758', function() + eq(0, eval([[&pyxversion]])) + eq(3, eval([[pyxeval('sys.version_info[:3][0]')]])) + eq(3, eval([[&pyxversion]])) + end) + it('RPC call to expand("<afile>") during BufDelete #5245 #5617', function() source([=[ python3 << EOF diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index e598c325a8..1763574bf9 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -158,14 +158,15 @@ describe(':terminal buffer', function() end) it('handles loss of focus gracefully', function() - if helpers.pending_win32(pending) then return end -- Change the statusline to avoid printing the file name, which varies. nvim('set_option', 'statusline', '==========') feed_command('set laststatus=0') -- Save the buffer number of the terminal for later testing. local tbuf = eval('bufnr("%")') - + local exitcmd = helpers.iswin() + and "['cmd', '/c', 'exit']" + or "['sh', '-c', 'exit']" source([[ function! SplitWindow(id, data, event) new @@ -173,7 +174,7 @@ describe(':terminal buffer', function() endfunction startinsert - call jobstart(['sh', '-c', 'exit'], {'on_exit': function("SplitWindow")}) + call jobstart(]]..exitcmd..[[, {'on_exit': function("SplitWindow")}) call feedkeys("\<C-\>", 't') " vim will expect <C-n>, but be exited out of " the terminal before it can be entered. ]]) diff --git a/test/functional/terminal/window_split_tab_spec.lua b/test/functional/terminal/window_split_tab_spec.lua index ad70b3d14f..7b49a38e77 100644 --- a/test/functional/terminal/window_split_tab_spec.lua +++ b/test/functional/terminal/window_split_tab_spec.lua @@ -37,7 +37,6 @@ describe(':terminal', function() end) it('does not change size on WinEnter', function() - if helpers.pending_win32(pending) then return end feed('<c-\\><c-n>') feed('k') feed_command('2split') diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index 8a1758c4a0..8dfe36c799 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -383,6 +383,7 @@ describe('floating windows', function() command('set number') command('set signcolumn=yes') command('set cursorline') + command('set foldcolumn=1') command('hi NormalFloat guibg=#333333') feed('ix<cr>y<cr><esc>gg') local win = meths.open_win(0, false, {relative='editor', width=20, height=4, row=4, col=10, style='minimal'}) @@ -397,9 +398,9 @@ describe('floating windows', function() [2:----------------------------------------]| | ## grid 2 - {19: }{20: 1 }{21:^x }| - {19: }{14: 2 }y | - {19: }{14: 3 } | + {19: }{20: 1 }{21:^x }| + {19: }{14: 2 }y | + {19: }{14: 3 } | {0:~ }| {0:~ }| {0:~ }| @@ -410,15 +411,15 @@ describe('floating windows', function() {15: }| ]], float_pos={[3] = {{id = 1001}, "NW", 1, 4, 10, true}}} else - screen:expect([[ - {19: }{20: 1 }{21:^x }| - {19: }{14: 2 }y | - {19: }{14: 3 } {15:x } | + screen:expect{grid=[[ + {19: }{20: 1 }{21:^x }| + {19: }{14: 2 }y | + {19: }{14: 3 } {15:x } | {0:~ }{15:y }{0: }| {0:~ }{15: }{0: }| {0:~ }{15: }{0: }| | - ]]) + ]]} end -- signcolumn=yes still works if there actually are signs @@ -435,9 +436,9 @@ describe('floating windows', function() [2:----------------------------------------]| | ## grid 2 - {17:π’ΜΜΜΜΜ
Μπ’ΜΜΜΜΜ
Μ}{20: 1 }{21:^x }| - {19: }{14: 2 }y | - {19: }{14: 3 } | + {19: }{17:π’ΜΜΜΜΜ
Μπ’ΜΜΜΜΜ
Μ}{20: 1 }{21:^x }| + {19: }{14: 2 }y | + {19: }{14: 3 } | {0:~ }| {0:~ }| {0:~ }| @@ -450,9 +451,9 @@ describe('floating windows', function() else screen:expect([[ - {17:π’ΜΜΜΜΜ
Μπ’ΜΜΜΜΜ
Μ}{20: 1 }{21:^x }| - {19: }{14: 2 }y | - {19: }{14: 3 } {17:π’ΜΜΜΜΜ
Μπ’ΜΜΜΜΜ
Μ}{15:x } | + {19: }{17:π’ΜΜΜΜΜ
Μπ’ΜΜΜΜΜ
Μ}{20: 1 }{21:^x }| + {19: }{14: 2 }y | + {19: }{14: 3 } {17:π’ΜΜΜΜΜ
Μπ’ΜΜΜΜΜ
Μ}{15:x } | {0:~ }{19: }{15:y }{0: }| {0:~ }{19: }{15: }{0: }| {0:~ }{15: }{0: }| @@ -474,9 +475,9 @@ describe('floating windows', function() [2:----------------------------------------]| | ## grid 2 - {19: }{20: 1 }{21:^x }| - {19: }{14: 2 }y | - {19: }{14: 3 } | + {19: }{20: 1 }{21:^x }| + {19: }{14: 2 }y | + {19: }{14: 3 } | {0:~ }| {0:~ }| {0:~ }| @@ -488,9 +489,9 @@ describe('floating windows', function() ]], float_pos={[3] = {{id = 1001}, "NW", 1, 4, 10, true}}} else screen:expect([[ - {19: }{20: 1 }{21:^x }| - {19: }{14: 2 }y | - {19: }{14: 3 } {15: } | + {19: }{20: 1 }{21:^x }| + {19: }{14: 2 }y | + {19: }{14: 3 } {15: } | {0:~ }{15: }{0: }| {0:~ }{15: }{0: }| {0:~ }{15: }{0: }| @@ -4143,14 +4144,14 @@ describe('floating windows', function() [4:----------------------------------------]| [4:----------------------------------------]| :tabnew | - ## grid 2 + ## grid 2 (hidden) x | {0:~ }| {0:~ }| {0:~ }| {0:~ }| {0:~ }| - ## grid 3 + ## grid 3 (hidden) {1:y }| {2:~ }| ## grid 4 @@ -4192,7 +4193,7 @@ describe('floating windows', function() ## grid 3 {1:y }| {2:~ }| - ## grid 4 + ## grid 4 (hidden) | {0:~ }| {0:~ }| @@ -4222,13 +4223,13 @@ describe('floating windows', function() [4:----------------------------------------]| [4:----------------------------------------]| :tabnext | - ## grid 2 + ## grid 2 (hidden) x | {0:~ }| {0:~ }| {0:~ }| {0:~ }| - ## grid 3 + ## grid 3 (hidden) {1:y }| {2:~ }| ## grid 4 @@ -4266,7 +4267,7 @@ describe('floating windows', function() [4:----------------------------------------]| [4:----------------------------------------]| :tabnew | - ## grid 2 + ## grid 2 (hidden) x | {0:~ }| {0:~ }| @@ -4312,7 +4313,7 @@ describe('floating windows', function() {0:~ }| {0:~ }| {0:~ }| - ## grid 4 + ## grid 4 (hidden) | {0:~ }| {0:~ }| @@ -4332,7 +4333,7 @@ describe('floating windows', function() [4:----------------------------------------]| [4:----------------------------------------]| :tabnext | - ## grid 2 + ## grid 2 (hidden) x | {0:~ }| {0:~ }| @@ -4364,7 +4365,11 @@ describe('floating windows', function() [5] = {foreground = tonumber('0x990000'), background = tonumber('0xfff1ff')}, [6] = {foreground = tonumber('0x332533'), background = tonumber('0xfff1ff')}, [7] = {background = tonumber('0xffcfff'), bold = true, foreground = tonumber('0x0000d8')}, - [8] = {background = Screen.colors.LightMagenta, bold = true, foreground = Screen.colors.Blue1} + [8] = {background = Screen.colors.LightMagenta, bold = true, foreground = Screen.colors.Blue1}, + [9] = {background = Screen.colors.LightMagenta, blend=30}, + [10] = {foreground = Screen.colors.Red, background = Screen.colors.LightMagenta, blend=0}, + [11] = {foreground = Screen.colors.Red, background = Screen.colors.LightMagenta, blend=80}, + [12] = {background = Screen.colors.LightMagenta, bold = true, foreground = Screen.colors.Blue1, blend=30}, }) insert([[ Lorem ipsum dolor sit amet, consectetur @@ -4444,9 +4449,9 @@ describe('floating windows', function() qui officia deserunt mollit anim id est | laborum^. | ## grid 4 - {1:test }| - {1: }| - {1:popup text }| + {9:test }| + {9: }| + {9:popup text }| ]], float_pos={[4] = {{id = 1002}, "NW", 1, 2, 5, true}}, unchanged=true} else screen:expect([[ @@ -4486,9 +4491,9 @@ describe('floating windows', function() qui officia deserunt mollit anim id est | laborum^. | ## grid 4 - {1:test }| - {1: }| - {4:popup text}{1: }| + {9:test }| + {9: }| + {10:popup text}{9: }| ]], float_pos={[4] = {{id = 1002}, "NW", 1, 2, 5, true}}} else screen:expect([[ @@ -4496,7 +4501,7 @@ describe('floating windows', function() exercitation ullamco laboris nisi ut aliquip ex | ea co{2:test}{3:o consequat}. Duis aute irure dolor in | repre{3:henderit in vol}uptate velit esse cillum | - dolor{4:popup text}{3:ul}la pariatur. Excepteur sint | + dolor{10:popup text}{3:ul}la pariatur. Excepteur sint | occaecat cupidatat non proident, sunt in culpa | qui officia deserunt mollit anim id est | laborum^. | @@ -4527,9 +4532,9 @@ describe('floating windows', function() qui officia deserunt mollit anim id est | laborum^. | ## grid 4 - {1:test }| - {1: }| - {4:popup text}{1: }| + {9:test }| + {9: }| + {11:popup text}{9: }| ]], float_pos={[4] = {{id = 1002}, "NW", 1, 2, 5, true}}, unchanged=true} else screen:expect([[ @@ -4569,9 +4574,9 @@ describe('floating windows', function() qui officia deserunt mollit anim id est | laborum^. | ## grid 4 - {4:popup text}{1: }| - {8:~ }| - {8:~ }| + {11:popup text}{9: }| + {12:~ }| + {12:~ }| ]], float_pos={[4] = {{id = 1002}, "NW", 1, 2, 5, true}}} else meths.input_mouse('wheel', 'down', '', 0, 4, 7) @@ -4671,7 +4676,7 @@ describe('floating windows', function() [2] = {foreground = Screen.colors.Grey0, background = tonumber('0xffcfff')}, [3] = {bold = true, foreground = Screen.colors.Blue1}, [4] = {background = tonumber('0xffcfff'), bold = true, foreground = tonumber('0xb282ff')}, - [5] = {background = Screen.colors.LightMagenta}, + [5] = {background = Screen.colors.LightMagenta, blend=30}, }) if multigrid then screen:expect{grid=[[ diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index 4f243e6413..238cc368da 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -2634,3 +2634,19 @@ it(':substitute with inccommand, timer-induced :redraw #9777', function() :%s/foo/ZZZ^ | ]]) end) + +it('long :%s/ with inccommand does not collapse cmdline', function() + local screen = Screen.new(10,5) + clear() + common_setup(screen) + command('set inccommand=nosplit') + feed(':%s/AAAAAAA', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A') + screen:expect([[ + {15:~ }| + {15:~ }| + :%s/AAAAAAAA| + AAAAAAAAAAAA| + AAAAAAA^ | + ]]) +end) diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua index 121cbe47d6..0009f2c31b 100644 --- a/test/functional/ui/input_spec.lua +++ b/test/functional/ui/input_spec.lua @@ -103,6 +103,11 @@ describe('mappings', function() check_mapping('<kequal>','<kequal>') check_mapping('<KPEquals>','<kequal>') end) + + it('support meta + multibyte char mapping', function() + add_mapping('<m-Γ€>', '<m-Γ€>') + check_mapping('<m-Γ€>', '<m-Γ€>') + end) end) describe('feeding large chunks of input with <Paste>', function() diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 9a1a0f66a2..ed65c4526f 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -6,7 +6,10 @@ local eq = helpers.eq local command = helpers.command local set_method_error = helpers.set_method_error local meths = helpers.meths - +local test_build_dir = helpers.test_build_dir +local nvim_prog = helpers.nvim_prog +local iswin = helpers.iswin +local exc_exec = helpers.exc_exec describe('ui/ext_messages', function() local screen @@ -1004,3 +1007,48 @@ describe('ui/ext_messages', function() }} end) end) + +describe('ui/msg_puts_printf', function() + it('output multibyte characters correctly', function() + local screen + local cmd = '' + local locale_dir = test_build_dir..'/share/locale/ja/LC_MESSAGES' + + clear({env={LANG='ja_JP.UTF-8'}}) + screen = Screen.new(25, 5) + screen:attach() + + if iswin() then + if os.execute('chcp 932 > NUL 2>&1') ~= 0 then + pending('missing japanese language features', function() end) + return + else + cmd = 'chcp 932 > NULL & ' + end + else + if (exc_exec('lang ja_JP.UTF-8') ~= 0) then + pending('Locale ja_JP.UTF-8 not supported', function() end) + return + elseif helpers.isCI() then + -- Fails non--Windows CI. Message catalog direcotry issue? + pending('fails on unix CI', function() end) + return + end + end + + os.execute('cmake -E make_directory '..locale_dir) + os.execute('cmake -E copy '..test_build_dir..'/src/nvim/po/ja.mo '..locale_dir..'/nvim.mo') + + cmd = cmd..'"'..nvim_prog..'" -u NONE -i NONE -Es -V1' + command([[call termopen(']]..cmd..[[')]]) + screen:expect([[ + ^Exγ’γΌγγ«ε
₯γγΎγ. γγΌ | + γγ«γ’γΌγγ«ζ»γγ«γ―"visu| + al"γ¨ε
₯εγγ¦γγ γγ. | + : | + | + ]]) + + os.execute('cmake -E remove_directory '..test_build_dir..'/share') + end) +end) diff --git a/test/functional/ui/mode_spec.lua b/test/functional/ui/mode_spec.lua index f6b3c1c3c9..a09df075aa 100644 --- a/test/functional/ui/mode_spec.lua +++ b/test/functional/ui/mode_spec.lua @@ -2,8 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local command, eval = helpers.command, helpers.eval -local eq = helpers.eq +local command = helpers.command describe('ui mode_change event', function() local screen @@ -63,7 +62,7 @@ describe('ui mode_change event', function() ]], mode="normal"} command("set showmatch") - eq(eval('&matchtime'), 5) -- tenths of seconds + command("set matchtime=1") -- tenths of seconds feed('a(stuff') screen:expect{grid=[[ word(stuff^ | @@ -80,7 +79,6 @@ describe('ui mode_change event', function() {2:-- INSERT --} | ]], mode="showmatch"} - screen:sleep(400) screen:expect{grid=[[ word(stuff)^ | {0:~ }| diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua index c5a23e4661..a910f5e2ea 100644 --- a/test/functional/ui/multigrid_spec.lua +++ b/test/functional/ui/multigrid_spec.lua @@ -76,7 +76,7 @@ describe('ext_multigrid', function() it('positions windows correctly', function() command('vsplit') - screen:expect([[ + screen:expect{grid=[[ ## grid 1 [3:--------------------------]{12:β}[2:--------------------------]| [3:--------------------------]{12:β}[2:--------------------------]| @@ -118,15 +118,15 @@ describe('ext_multigrid', function() {1:~ }| {1:~ }| {1:~ }| - ]], nil, nil, function() + ]], condition=function() eq({ - [2] = { win = 1000, startrow = 0, startcol = 27, width = 26, height = 12 }, - [3] = { win = 1001, startrow = 0, startcol = 0, width = 26, height = 12 } + [2] = { win = {id=1000}, startrow = 0, startcol = 27, width = 26, height = 12 }, + [3] = { win = {id=1001}, startrow = 0, startcol = 0, width = 26, height = 12 } }, screen.win_position) - end) + end} command('wincmd l') command('split') - screen:expect([[ + screen:expect{grid=[[ ## grid 1 [3:--------------------------]{12:β}[4:--------------------------]| [3:--------------------------]{12:β}[4:--------------------------]| @@ -168,16 +168,16 @@ describe('ext_multigrid', function() {1:~ }| {1:~ }| {1:~ }| - ]], nil, nil, function() + ]], condition=function() eq({ - [2] = { win = 1000, startrow = 7, startcol = 27, width = 26, height = 5 }, - [3] = { win = 1001, startrow = 0, startcol = 0, width = 26, height = 12 }, - [4] = { win = 1002, startrow = 0, startcol = 27, width = 26, height = 6 } + [2] = { win = {id=1000}, startrow = 7, startcol = 27, width = 26, height = 5 }, + [3] = { win = {id=1001}, startrow = 0, startcol = 0, width = 26, height = 12 }, + [4] = { win = {id=1002}, startrow = 0, startcol = 27, width = 26, height = 6 } }, screen.win_position) - end) + end} command('wincmd h') command('q') - screen:expect([[ + screen:expect{grid=[[ ## grid 1 [4:-----------------------------------------------------]| [4:-----------------------------------------------------]| @@ -206,12 +206,12 @@ describe('ext_multigrid', function() {1:~ }| {1:~ }| {1:~ }| - ]], nil, nil, function() + ]], condition=function() eq({ - [2] = { win = 1000, startrow = 7, startcol = 0, width = 53, height = 5 }, - [4] = { win = 1002, startrow = 0, startcol = 0, width = 53, height = 6 } + [2] = { win = {id=1000}, startrow = 7, startcol = 0, width = 53, height = 5 }, + [4] = { win = {id=1002}, startrow = 0, startcol = 0, width = 53, height = 6 } }, screen.win_position) - end) + end} end) describe('split', function () @@ -1206,7 +1206,7 @@ describe('ext_multigrid', function() ]]) end) - it('handles switich tabs', function() + it('handles switch tabs', function() command('vsp') screen:expect([[ ## grid 1 @@ -1271,7 +1271,7 @@ describe('ext_multigrid', function() [4:-----------------------------------------------------]| {11:[No Name] }| | - ## grid 2 + ## grid 2 (hidden) | {1:~ }| {1:~ }| @@ -1284,7 +1284,7 @@ describe('ext_multigrid', function() {1:~ }| {1:~ }| {1:~ }| - ## grid 3 + ## grid 3 (hidden) | {1:~ }| {1:~ }| @@ -1328,7 +1328,7 @@ describe('ext_multigrid', function() [4:-----------------------------------------------------]| {12:[No Name] }| | - ## grid 2 + ## grid 2 (hidden) | {1:~ }| {1:~ }| @@ -1341,7 +1341,7 @@ describe('ext_multigrid', function() {1:~ }| {1:~ }| {1:~ }| - ## grid 3 + ## grid 3 (hidden) | {1:~ }| {1:~ }| @@ -1409,13 +1409,13 @@ describe('ext_multigrid', function() {1:~ }| {1:~ }| {1:~ }| - ## grid 4 + ## grid 4 (hidden) | {1:~ }| {1:~ }| {1:~ }| {1:~ }| - ## grid 5 + ## grid 5 (hidden) | {1:~ }| {1:~ }| @@ -1440,7 +1440,7 @@ describe('ext_multigrid', function() [4:-----------------------------------------------------]| {12:[No Name] }| | - ## grid 2 + ## grid 2 (hidden) | {1:~ }| {1:~ }| @@ -1452,7 +1452,7 @@ describe('ext_multigrid', function() {1:~ }| {1:~ }| {1:~ }| - ## grid 3 + ## grid 3 (hidden) | {1:~ }| {1:~ }| diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua index 24bf66e2d8..4a0e7ccad0 100644 --- a/test/functional/ui/output_spec.lua +++ b/test/functional/ui/output_spec.lua @@ -12,8 +12,6 @@ local command = helpers.command local nvim_dir = helpers.nvim_dir describe("shell command :!", function() - if helpers.pending_win32(pending) then return end - local screen before_each(function() clear() @@ -36,6 +34,7 @@ describe("shell command :!", function() end) it("displays output without LF/EOF. #4646 #4569 #3772", function() + if helpers.pending_win32(pending) then return end -- NOTE: We use a child nvim (within a :term buffer) -- to avoid triggering a UI flush. child_session.feed_data(":!printf foo; sleep 200\n") diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index 9bfea28ed7..c4cfc9d8d3 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -1644,6 +1644,7 @@ describe('builtin popupmenu', function() [42] = {foreground = tonumber('0x0c0c0c'), background = tonumber('0xe5a8e5')}, [43] = {background = tonumber('0x7f5d7f'), bold = true, foreground = tonumber('0x3f3f3f')}, [44] = {foreground = tonumber('0x3f3f3f'), background = tonumber('0x7f5d7f')}, + [45] = {background = Screen.colors.WebGray, blend=0}, }) command('syntax on') command('set mouse=a') @@ -1761,7 +1762,7 @@ describe('builtin popupmenu', function() Lorem ipsum d{1:ol}or sit amet, consectetur | adipisicing elit, sed do eiusmod tempor | bla bla incididunt^ | - incidid{22: incididunt }{27: }d{1:ol}ore magna aliqua. | + incidid{45: incididunt }{27: }d{1:ol}ore magna aliqua. | Ut enim{28: }{29:ut}{28: minim veniam}{25:,} quis nostrud | exercit{28:a}{29:labore}{28:llamco la}{25:b}oris nisi ut aliquip ex | {2:[No Nam}{30:e}{43:et}{30:[+] }{32: }{2: }| diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 81a15cada2..3b39794465 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -316,9 +316,10 @@ local ext_keys = { -- cmdline_block: Expected ext_cmdline block (for function definitions) -- wildmenu_items: Expected items for ext_wildmenu -- wildmenu_pos: Expected position for ext_wildmenu -function Screen:expect(expected, attr_ids, attr_ignore) +function Screen:expect(expected, attr_ids, attr_ignore, ...) local grid, condition = nil, nil local expected_rows = {} + assert(next({...}) == nil, "invalid args to expect()") if type(expected) == "table" then assert(not (attr_ids ~= nil or attr_ignore ~= nil)) local is_key = {grid=true, attr_ids=true, attr_ignore=true, condition=true, @@ -499,7 +500,8 @@ function Screen:_wait(check, flags) local did_miminal_timeout = false local function notification_cb(method, args) - assert(method == 'redraw') + assert(method == 'redraw', string.format( + 'notification_cb: unexpected method (%s, args=%s)', method, inspect(args))) did_flush = self:_redraw(args) if not did_flush then return @@ -1211,7 +1213,11 @@ function Screen:render(headers, attr_state, preview) local rv = {} for igrid,grid in pairs(self._grids) do if headers then - table.insert(rv, "## grid "..igrid) + local suffix = "" + if igrid > 1 and self.win_position[igrid] == nil and self.float_pos[igrid] == nil then + suffix = " (hidden)" + end + table.insert(rv, "## grid "..igrid..suffix) end for i = 1, grid.height do local cursor = self._cursor.grid == igrid and self._cursor.row == i @@ -1491,7 +1497,7 @@ function Screen:_equal_attrs(a, b) a.underline == b.underline and a.undercurl == b.undercurl and a.italic == b.italic and a.reverse == b.reverse and a.foreground == b.foreground and a.background == b.background and - a.special == b.special + a.special == b.special and a.blend == b.blend end function Screen:_equal_info(a, b) diff --git a/test/helpers.lua b/test/helpers.lua index e14bcff2c8..ce5e8b9c04 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -59,8 +59,8 @@ end function module.neq(expected, actual, context) return assert.are_not.same(expected, actual, context) end -function module.ok(res) - return assert.is_true(res) +function module.ok(res, msg) + return assert.is_true(res, msg) end function module.near(actual, expected, tolerance) return assert.is.near(actual, expected, tolerance) |