diff options
37 files changed, 595 insertions, 135 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 27cc02bd65..3c2e40ab8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,6 +85,14 @@ endif() option(ENABLE_LIBINTL "enable libintl" ON) option(ENABLE_LIBICONV "enable libiconv" ON) +if (MINGW) + # Disable LTO by default as it may not compile + # See https://github.com/Alexpux/MINGW-packages/issues/3516 + # and https://github.com/neovim/neovim/pull/8654#issuecomment-402316672 + option(ENABLE_LTO "enable link time optimization" OFF) +else() + option(ENABLE_LTO "enable link time optimization" ON) +endif() # Set default build type. if(NOT CMAKE_BUILD_TYPE) @@ -29,6 +29,21 @@ for more information. [](https://waffle.io/neovim/neovim/metrics) +Features +-------- + +- Modern [GUIs](https://github.com/neovim/neovim/wiki/Related-projects#gui) +- [API](https://github.com/neovim/neovim/wiki/Related-projects#api-clients) + access from any language including clojure, lisp, go, haskell, lua, + javascript, perl, python, ruby, rust. +- Embedded, scriptable [terminal emulator](https://neovim.io/doc/user/nvim_terminal_emulator.html) +- Asynchronous [job control](https://github.com/neovim/neovim/pull/2247) +- [Shared data (shada)](https://github.com/neovim/neovim/pull/2506) among multiple editor instances +- [XDG base directories](https://github.com/neovim/neovim/pull/3470) support +- Compatible with most Vim plugins, including Ruby and Python plugins. + +See [`:help nvim-features`][nvim-features] for the full list! + Install from source ------------------- @@ -57,6 +72,11 @@ Pre-built packages for Windows, macOS, and Linux are found at the Managed packages are in [Homebrew], [Debian], [Ubuntu], [Fedora], [Arch Linux], [Gentoo], and [more](https://github.com/neovim/neovim/wiki/Installing-Neovim)! +Transitioning from Vim +-------------------- + +See [`:help nvim-from-vim`](https://neovim.io/doc/user/nvim.html#nvim-from-vim) for instructions. + Project layout -------------- @@ -76,21 +96,6 @@ Project layout ├─ third-party/ cmake subproject to build dependencies └─ test/ tests (see test/README.md) -Features --------- - -- Modern [GUIs](https://github.com/neovim/neovim/wiki/Related-projects#gui) -- [API](https://github.com/neovim/neovim/wiki/Related-projects#api-clients) - access from any language including clojure, lisp, go, haskell, lua, - javascript, perl, python, ruby, rust. -- Embedded, scriptable [terminal emulator](https://neovim.io/doc/user/nvim_terminal_emulator.html) -- Asynchronous [job control](https://github.com/neovim/neovim/pull/2247) -- [Shared data (shada)](https://github.com/neovim/neovim/pull/2506) among multiple editor instances -- [XDG base directories](https://github.com/neovim/neovim/pull/3470) support -- Compatible with most Vim plugins, including Ruby and Python plugins. - -See [`:help nvim-features`][nvim-features] for the full list! - License ------- diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index c460e65c64..dce531b663 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1508,8 +1508,7 @@ v:errmsg Last given error message. It's allowed to set this variable. :silent! next :if v:errmsg != "" : ... handle error -< "errmsg" also works, for backwards compatibility. - +< *v:errors* *errors-variable* v:errors Errors found by assert functions, such as |assert_true()|. This is a list of strings. @@ -1813,8 +1812,7 @@ v:shell_error Result of the last shell command. When non-zero, the last :if v:shell_error : echo 'could not rename "foo" to "bar"!' :endif -< "shell_error" also works, for backwards compatibility. - +< *v:statusmsg* *statusmsg-variable* v:statusmsg Last given status message. It's allowed to set this variable. @@ -1888,7 +1886,6 @@ v:testing Must be set before using `test_garbagecollect_now()`. v:this_session Full filename of the last loaded or saved session file. See |:mksession|. It is allowed to set this variable. When no session file has been saved, this variable is empty. - "this_session" also works, for backwards compatibility. *v:throwpoint* *throwpoint-variable* v:throwpoint The point where the exception most recently caught and not diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index ad1077bcab..24fb87a6b4 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -837,7 +837,7 @@ resulting file, when executed with a ":source" command: such as creating menu items in the GUI version. After restoring the Session, the full filename of your current Session is -available in the internal variable "v:this_session" |this_session-variable|. +available in the internal variable |v:this_session|. An example mapping: > :nmap <F2> :wa<Bar>exe "mksession! " . v:this_session<CR>:so ~/sessions/ This saves the current Session, and starts off the command to load another. diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 8bda114639..ef0b54fa33 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -352,6 +352,9 @@ TUI: VimL (Vim script) compatibility: `count` does not alias to |v:count| + `errmsg` does not alias to |v:errmsg| + `shell_error` does not alias to |v:shell_error| + `this_session` does not alias to |v:this_session| ============================================================================== 5. Missing legacy features *nvim-features-missing* diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 7106f76a6e..b32d54b28e 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -399,6 +399,10 @@ if(JEMALLOC_FOUND) list(APPEND NVIM_EXEC_LINK_LIBRARIES ${JEMALLOC_LIBRARIES}) endif() +if(POLICY CMP0069) + cmake_policy(SET CMP0069 NEW) +endif() + add_executable(nvim ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} ${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${NVIM_HEADERS}) target_link_libraries(nvim ${NVIM_EXEC_LINK_LIBRARIES}) @@ -407,6 +411,14 @@ install_helper(TARGETS nvim) set_property(TARGET nvim APPEND PROPERTY INCLUDE_DIRECTORIES ${LUA_PREFERRED_INCLUDE_DIRS}) +if(ENABLE_LTO AND (POLICY CMP0069)) + include(CheckIPOSupported) + check_ipo_supported(RESULT IPO_SUPPORTED) + if(IPO_SUPPORTED AND (NOT CMAKE_BUILD_TYPE MATCHES Debug)) + set_property(TARGET nvim PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) + endif() +endif() + if(WIN32) # Copy DLLs and third-party tools to bin/ and install them along with nvim add_custom_target(nvim_runtime_deps ALL diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 63c2c4a1b9..d0db43c588 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -302,6 +302,15 @@ static void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, args = (Array)ARRAY_DICT_INIT; ADD(args, INTEGER_OBJ(rows)); push_call(ui, "scroll", args); + + // some clients have "clear" being affected by scroll region, + // so reset it. + args = (Array)ARRAY_DICT_INIT; + ADD(args, INTEGER_OBJ(0)); + ADD(args, INTEGER_OBJ(ui->height-1)); + ADD(args, INTEGER_OBJ(0)); + ADD(args, INTEGER_OBJ(ui->width-1)); + push_call(ui, "set_scroll_region", args); } } diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 4152c16588..a8d29a3a53 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -4420,7 +4420,7 @@ do_arg_all ( if (i < alist->al_ga.ga_len && (AARGLIST(alist)[i].ae_fnum == buf->b_fnum || path_full_compare(alist_name(&AARGLIST(alist)[i]), - buf->b_ffname, TRUE) & kEqualFiles)) { + buf->b_ffname, true) & kEqualFiles)) { int weight = 1; if (old_curtab == curtab) { diff --git a/src/nvim/charset.c b/src/nvim/charset.c index a02d2a812d..231bff26e8 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1344,7 +1344,11 @@ colnr_T getvcol_nolist(pos_T *posp) colnr_T vcol; curwin->w_p_list = false; - getvcol(curwin, posp, NULL, &vcol, NULL); + if (posp->coladd) { + getvvcol(curwin, posp, NULL, &vcol, NULL); + } else { + getvcol(curwin, posp, NULL, &vcol, NULL); + } curwin->w_p_list = list_save; return vcol; } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e6880f58e7..36662b2a39 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -344,11 +344,11 @@ static struct vimvar { VV(VV_COUNT, "count", VAR_NUMBER, VV_RO), VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO), VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO), - VV(VV_ERRMSG, "errmsg", VAR_STRING, VV_COMPAT), + VV(VV_ERRMSG, "errmsg", VAR_STRING, 0), VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0), VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0), - VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_COMPAT+VV_RO), - VV(VV_THIS_SESSION, "this_session", VAR_STRING, VV_COMPAT), + VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_RO), + VV(VV_THIS_SESSION, "this_session", VAR_STRING, 0), VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT+VV_RO), VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX), VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO), diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 3c6a8b1074..176df58fb9 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -4977,7 +4977,8 @@ void fix_help_buffer(void) while (*p != NUL) { copy_option_part(&p, NameBuff, MAXPATHL, ","); rt = (char_u *)vim_getenv("VIMRUNTIME"); - if (path_full_compare(rt, NameBuff, FALSE) != kEqualFiles) { + if (rt != NULL + && path_full_compare(rt, NameBuff, false) != kEqualFiles) { int fcount; char_u **fnames; FILE *fd; @@ -5197,8 +5198,9 @@ static void helptags_one(char_u *dir, char_u *ext, char_u *tagfname, * add the "help-tags" tag. */ ga_init(&ga, (int)sizeof(char_u *), 100); - if (add_help_tags || path_full_compare((char_u *)"$VIMRUNTIME/doc", - dir, FALSE) == kEqualFiles) { + if (add_help_tags + || path_full_compare((char_u *)"$VIMRUNTIME/doc", + dir, false) == kEqualFiles) { s = xmalloc(18 + STRLEN(tagfname)); sprintf((char *)s, "help-tags\t%s\t1\n", tagfname); GA_APPEND(char_u *, &ga, s); diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index f07bc0e137..120278d3fb 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2693,14 +2693,27 @@ void ex_packloadall(exarg_T *eap) /// ":packadd[!] {name}" void ex_packadd(exarg_T *eap) { - static const char *plugpat = "pack/*/opt/%s"; // NOLINT - - size_t len = STRLEN(plugpat) + STRLEN(eap->arg); - char *pat = (char *)xmallocz(len); - vim_snprintf(pat, len, plugpat, eap->arg); - do_in_path(p_pp, (char_u *)pat, DIP_ALL + DIP_DIR + DIP_ERR, add_pack_plugin, - eap->forceit ? &APP_ADD_DIR : &APP_BOTH); - xfree(pat); + static const char *plugpat = "pack/*/%s/%s"; // NOLINT + int res = OK; + + // Round 1: use "start", round 2: use "opt". + for (int round = 1; round <= 2; round++) { + // Only look under "start" when loading packages wasn't done yet. + if (round == 1 && did_source_packages) { + continue; + } + + const size_t len = STRLEN(plugpat) + STRLEN(eap->arg) + 5; + char *pat = xmallocz(len); + vim_snprintf(pat, len, plugpat, round == 1 ? "start" : "opt", eap->arg); + // The first round don't give a "not found" error, in the second round + // only when nothing was found in the first round. + res = do_in_path(p_pp, (char_u *)pat, + DIP_ALL + DIP_DIR + + (round == 2 && res == FAIL ? DIP_ERR : 0), + add_pack_plugin, eap->forceit ? &APP_ADD_DIR : &APP_BOTH); + xfree(pat); + } } /// ":options" diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index b077aefa1e..97369c50d9 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -7920,7 +7920,7 @@ static void ex_mkrc(exarg_T *eap) if (failed) { EMSG(_(e_write)); } else if (eap->cmdidx == CMD_mksession) { - // successful session write - set this_session var + // successful session write - set v:this_session char *const tbuf = xmalloc(MAXPATHL); if (vim_FullName(fname, tbuf, MAXPATHL, false) == OK) { set_vim_var_string(VV_THIS_SESSION, tbuf, -1); @@ -8784,15 +8784,15 @@ makeopens( if (ssop_flags & SSOP_BUFFERS) only_save_windows = FALSE; /* Save ALL buffers */ - /* - * Begin by setting the this_session variable, and then other - * sessionable variables. - */ - if (put_line(fd, "let v:this_session=expand(\"<sfile>:p\")") == FAIL) + // Begin by setting v:this_session, and then other sessionable variables. + if (put_line(fd, "let v:this_session=expand(\"<sfile>:p\")") == FAIL) { return FAIL; - if (ssop_flags & SSOP_GLOBALS) - if (store_session_globals(fd) == FAIL) + } + if (ssop_flags & SSOP_GLOBALS) { + if (store_session_globals(fd) == FAIL) { return FAIL; + } + } /* * Close all windows but one. diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 775d002e58..fd11acff84 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -5062,38 +5062,33 @@ static void * call_user_expand_func(user_expand_func_T user_expand_func, */ static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file) { - char_u *retstr; - char_u *s; char_u *e; - char_u keep; garray_T ga; - retstr = call_user_expand_func((user_expand_func_T)call_func_retstr, xp, - num_file, file); + char_u *const retstr = call_user_expand_func( + (user_expand_func_T)call_func_retstr, xp, num_file, file); if (retstr == NULL) { return FAIL; } ga_init(&ga, (int)sizeof(char *), 3); - for (s = retstr; *s != NUL; s = e) { + for (char_u *s = retstr; *s != NUL; s = e) { e = vim_strchr(s, '\n'); if (e == NULL) e = s + STRLEN(s); - keep = *e; - *e = 0; + const int keep = *e; + *e = NUL; - if (xp->xp_pattern[0] && vim_regexec(regmatch, s, (colnr_T)0) == 0) { - *e = keep; - if (*e != NUL) - ++e; - continue; + const bool skip = xp->xp_pattern[0] + && vim_regexec(regmatch, s, (colnr_T)0) == 0; + *e = keep; + if (!skip) { + GA_APPEND(char_u *, &ga, vim_strnsave(s, (int)(e - s))); } - GA_APPEND(char_u *, &ga, vim_strnsave(s, (int)(e - s))); - - *e = keep; - if (*e != NUL) - ++e; + if (*e != NUL) { + e++; + } } xfree(retstr); *file = ga.ga_data; diff --git a/src/nvim/log.c b/src/nvim/log.c index 7bfe5c4089..e485d4c338 100644 --- a/src/nvim/log.c +++ b/src/nvim/log.c @@ -7,6 +7,9 @@ #include <stdbool.h> #include <stdint.h> #include <stdio.h> +#if !defined(WIN32) +# include <sys/time.h> // for gettimeofday() +#endif #include <uv.h> #include "nvim/log.h" @@ -260,25 +263,33 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, }; assert(log_level >= DEBUG_LOG_LEVEL && log_level <= ERROR_LOG_LEVEL); - // format current timestamp in local time + // Format the timestamp. struct tm local_time; - if (os_get_localtime(&local_time) == NULL) { + if (os_localtime(&local_time) == NULL) { return false; } char date_time[20]; - if (strftime(date_time, sizeof(date_time), "%Y/%m/%d %H:%M:%S", + if (strftime(date_time, sizeof(date_time), "%Y-%m-%dT%H:%M:%S", &local_time) == 0) { return false; } - // print the log message prefixed by the current timestamp and pid + int millis = 0; +#if !defined(WIN32) + struct timeval curtime; + if (gettimeofday(&curtime, NULL) == 0) { + millis = (int)curtime.tv_usec / 1000; + } +#endif + + // Print the log message. int64_t pid = os_get_pid(); int rv = (line_num == -1 || func_name == NULL) - ? fprintf(log_file, "%s %s %" PRId64 " %s", date_time, - log_levels[log_level], pid, + ? fprintf(log_file, "%s %s.%03d %-5" PRId64 " %s", + log_levels[log_level], date_time, millis, pid, (context == NULL ? "?:" : context)) - : fprintf(log_file, "%s %s %" PRId64 " %s%s:%d: ", date_time, - log_levels[log_level], pid, + : fprintf(log_file, "%s %s.%03d %-5" PRId64 " %s%s:%d: ", + log_levels[log_level], date_time, millis, pid, (context == NULL ? "" : context), func_name, line_num); if (rv < 0) { diff --git a/src/nvim/memline.c b/src/nvim/memline.c index fc9a1e8271..84ceaf0973 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -1365,11 +1365,11 @@ recover_names ( */ if (curbuf->b_ml.ml_mfp != NULL && (p = curbuf->b_ml.ml_mfp->mf_fname) != NULL) { - for (int i = 0; i < num_files; ++i) - if (path_full_compare(p, files[i], TRUE) & kEqualFiles) { - /* Remove the name from files[i]. Move further entries - * down. When the array becomes empty free it here, since - * FreeWild() won't be called below. */ + for (int i = 0; i < num_files; i++) { + if (path_full_compare(p, files[i], true) & kEqualFiles) { + // Remove the name from files[i]. Move further entries + // down. When the array becomes empty free it here, since + // FreeWild() won't be called below. xfree(files[i]); if (--num_files == 0) xfree(files); @@ -1377,6 +1377,7 @@ recover_names ( for (; i < num_files; ++i) files[i] = files[i + 1]; } + } } if (nr > 0) { file_count += num_files; diff --git a/src/nvim/ops.c b/src/nvim/ops.c index c95345f9b2..67171cb27e 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2509,19 +2509,27 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) } // Some versions of Vi use ">=" here, some don't... if (yanklines > (size_t)p_report) { + char namebuf[100]; + + if (oap->regname == NUL) { + *namebuf = NUL; + } else { + vim_snprintf(namebuf, sizeof(namebuf), _(" into \"%c"), oap->regname); + } + // redisplay now, so message is not deleted update_topline_redraw(); if (yanklines == 1) { if (yank_type == kMTBlockWise) { - MSG(_("block of 1 line yanked")); + smsg(_("block of 1 line yanked%s"), namebuf); } else { - MSG(_("1 line yanked")); + smsg(_("1 line yanked%s"), namebuf); } } else if (yank_type == kMTBlockWise) { - smsg(_("block of %" PRId64 " lines yanked"), - (int64_t)yanklines); + smsg(_("block of %" PRId64 " lines yanked%s"), + (int64_t)yanklines, namebuf); } else { - smsg(_("%" PRId64 " lines yanked"), (int64_t)yanklines); + smsg(_("%" PRId64 " lines yanked%s"), (int64_t)yanklines, namebuf); } } } diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 0df857352b..6997156d4c 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -196,16 +196,19 @@ void init_homedir(void) const char *homedrive = os_getenv("HOMEDRIVE"); const char *homepath = os_getenv("HOMEPATH"); if (homepath == NULL) { - homepath = "\\"; + homepath = "\\"; } - if (homedrive != NULL && strlen(homedrive) + strlen(homepath) < MAXPATHL) { + if (homedrive != NULL + && strlen(homedrive) + strlen(homepath) < MAXPATHL) { snprintf(os_buf, MAXPATHL, "%s%s", homedrive, homepath); if (os_buf[0] != NUL) { var = os_buf; - vim_setenv("HOME", os_buf); } } } + if (var == NULL) { + var = os_getenv("USERPROFILE"); + } #endif if (var != NULL) { @@ -608,6 +611,12 @@ char *vim_getenv(const char *name) return xstrdup(kos_env_path); } +#ifdef WIN32 + if (strcmp(name, "HOME") == 0) { + return xstrdup(homedir); + } +#endif + bool vimruntime = (strcmp(name, "VIMRUNTIME") == 0); if (!vimruntime && strcmp(name, "VIM") != 0) { return NULL; @@ -758,7 +767,12 @@ size_t home_replace(const buf_T *const buf, const char_u *src, dirlen = strlen(homedir); } - const char *const homedir_env = os_getenv("HOME"); + const char *homedir_env = os_getenv("HOME"); +#ifdef WIN32 + if (homedir_env == NULL) { + homedir_env = os_getenv("USERPROFILE"); + } +#endif char *homedir_env_mod = (char *)homedir_env; bool must_free = false; diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c index 290d421acc..31ef1a0cd6 100644 --- a/src/nvim/os/time.c +++ b/src/nvim/os/time.c @@ -114,12 +114,12 @@ struct tm *os_localtime_r(const time_t *restrict clock, #endif } -/// Obtains the current Unix timestamp and adjusts it to local time. +/// Gets the current Unix timestamp and adjusts it to local time. /// /// @param result Pointer to a 'struct tm' where the result should be placed /// @return A pointer to a 'struct tm' in the current time zone (the 'result' /// argument) or NULL in case of error -struct tm *os_get_localtime(struct tm *result) FUNC_ATTR_NONNULL_ALL +struct tm *os_localtime(struct tm *result) FUNC_ATTR_NONNULL_ALL { time_t rawtime = time(NULL); return os_localtime_r(&rawtime, result); diff --git a/src/nvim/path.c b/src/nvim/path.c index d5c636ff08..0b90329686 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -52,7 +52,8 @@ /// @param s2 Second file name. /// @param checkname When both files don't exist, only compare their names. /// @return Enum of type FileComparison. @see FileComparison. -FileComparison path_full_compare(char_u *s1, char_u *s2, int checkname) +FileComparison path_full_compare(char_u *const s1, char_u *const s2, + const bool checkname) { assert(s1 && s2); char_u exp1[MAXPATHL]; diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 47760e1e70..664dd3e968 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3603,15 +3603,16 @@ void ex_vimgrep(exarg_T *eap) goto theend; } - if (s != NULL && *s == NUL) { - /* Pattern is empty, use last search pattern. */ + if (s == NULL || *s == NUL) { + // Pattern is empty, use last search pattern. if (last_search_pat() == NULL) { EMSG(_(e_noprevre)); goto theend; } regmatch.regprog = vim_regcomp(last_search_pat(), RE_MAGIC); - } else + } else { regmatch.regprog = vim_regcomp(s, RE_MAGIC); + } if (regmatch.regprog == NULL) goto theend; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 65a3c17286..e8dbc11710 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -283,8 +283,11 @@ void update_screen(int type) if (msg_scrolled) { clear_cmdline = true; if (dy_flags & DY_MSGSEP) { + int valid = MAX(Rows - msg_scrollsize(), 0); + if (valid == 0) { + redraw_tabline = true; + } FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - int valid = Rows - msg_scrollsize(); if (wp->w_winrow + wp->w_height > valid) { wp->w_redr_type = NOT_VALID; wp->w_lines_valid = 0; @@ -292,9 +295,6 @@ void update_screen(int type) if (wp->w_winrow + wp->w_height + wp->w_status_height > valid) { wp->w_redr_status = true; } - if (valid == 0) { - redraw_tabline = true; - } } } else if (msg_scrolled > Rows - 5) { // clearing is faster type = CLEAR; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 0714eb3137..cb03257878 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -2043,9 +2043,11 @@ char_u *did_set_spelllang(win_T *wp) dont_use_region = true; // Check if we loaded this language before. - for (slang = first_lang; slang != NULL; slang = slang->sl_next) - if (path_full_compare(lang, slang->sl_fname, FALSE) == kEqualFiles) + for (slang = first_lang; slang != NULL; slang = slang->sl_next) { + if (path_full_compare(lang, slang->sl_fname, false) == kEqualFiles) { break; + } + } } else { filename = false; if (len > 3 && lang[len - 3] == '_') { @@ -2085,8 +2087,9 @@ char_u *did_set_spelllang(win_T *wp) } // Loop over the languages, there can be several files for "lang". - for (slang = first_lang; slang != NULL; slang = slang->sl_next) - if (filename ? path_full_compare(lang, slang->sl_fname, FALSE) == kEqualFiles + for (slang = first_lang; slang != NULL; slang = slang->sl_next) { + if (filename + ? path_full_compare(lang, slang->sl_fname, false) == kEqualFiles : STRICMP(lang, slang->sl_name) == 0) { region_mask = REGION_ALL; if (!filename && region != NULL) { @@ -2116,6 +2119,7 @@ char_u *did_set_spelllang(win_T *wp) nobreak = true; } } + } } // round 0: load int_wordlist, if possible. @@ -2137,17 +2141,21 @@ char_u *did_set_spelllang(win_T *wp) // If it was already found above then skip it. for (c = 0; c < ga.ga_len; ++c) { p = LANGP_ENTRY(ga, c)->lp_slang->sl_fname; - if (p != NULL && path_full_compare(spf_name, p, FALSE) == kEqualFiles) + if (p != NULL + && path_full_compare(spf_name, p, false) == kEqualFiles) { break; + } } if (c < ga.ga_len) continue; } // Check if it was loaded already. - for (slang = first_lang; slang != NULL; slang = slang->sl_next) - if (path_full_compare(spf_name, slang->sl_fname, FALSE) == kEqualFiles) + for (slang = first_lang; slang != NULL; slang = slang->sl_next) { + if (path_full_compare(spf_name, slang->sl_fname, false) == kEqualFiles) { break; + } + } if (slang == NULL) { // Not loaded, try loading it now. The language name includes the // region name, the region is ignored otherwise. for int_wordlist diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 69fa95107e..6578c7d66c 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -1786,7 +1786,7 @@ spell_reload_one ( bool didit = false; for (slang = first_lang; slang != NULL; slang = slang->sl_next) { - if (path_full_compare(fname, slang->sl_fname, FALSE) == kEqualFiles) { + if (path_full_compare(fname, slang->sl_fname, false) == kEqualFiles) { slang_clear(slang); if (spell_load_file(fname, NULL, slang, false) == NULL) // reloading failed, clear the language @@ -4714,9 +4714,11 @@ static void spell_make_sugfile(spellinfo_T *spin, char_u *wfname) // pointer-linked version of the trie. And it avoids having two versions // of the code for the soundfolding stuff. // It might have been done already by spell_reload_one(). - for (slang = first_lang; slang != NULL; slang = slang->sl_next) - if (path_full_compare(wfname, slang->sl_fname, FALSE) == kEqualFiles) + for (slang = first_lang; slang != NULL; slang = slang->sl_next) { + if (path_full_compare(wfname, slang->sl_fname, false) == kEqualFiles) { break; + } + } if (slang == NULL) { spell_message(spin, (char_u *)_("Reading back spell file...")); slang = spell_load_file(wfname, NULL, NULL, false); diff --git a/src/nvim/tag.c b/src/nvim/tag.c index beff89d191..c09a13edb1 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -2289,7 +2289,7 @@ static char_u *tag_full_fname(tagptrs_T *tagp) { int c = *tagp->fname_end; *tagp->fname_end = NUL; - char_u *fullname = expand_tag_fname(tagp->fname, tagp->tag_fname, FALSE); + char_u *fullname = expand_tag_fname(tagp->fname, tagp->tag_fname, false); *tagp->fname_end = c; return fullname; @@ -2363,8 +2363,8 @@ jumpto_tag ( * Expand file name, when needed (for environment variables). * If 'tagrelative' option set, may change file name. */ - fname = expand_tag_fname(fname, tagp.tag_fname, TRUE); - tofree_fname = fname; /* free() it later */ + fname = expand_tag_fname(fname, tagp.tag_fname, true); + tofree_fname = fname; // free() it later /* * Check if the file with the tag exists before abandoning the current @@ -2615,13 +2615,12 @@ erret: return retval; } -/* - * If "expand" is TRUE, expand wildcards in fname. - * If 'tagrelative' option set, change fname (name of file containing tag) - * according to tag_fname (name of tag file containing fname). - * Returns a pointer to allocated memory. - */ -static char_u *expand_tag_fname(char_u *fname, char_u *tag_fname, int expand) +// If "expand" is true, expand wildcards in fname. +// If 'tagrelative' option set, change fname (name of file containing tag) +// according to tag_fname (name of tag file containing fname). +// Returns a pointer to allocated memory. +static char_u *expand_tag_fname(char_u *fname, char_u *const tag_fname, + const bool expand) { char_u *p; char_u *expanded_fname = NULL; @@ -2676,8 +2675,8 @@ static int test_for_current(char_u *fname, char_u *fname_end, char_u *tag_fname, c = *fname_end; *fname_end = NUL; } - fullname = expand_tag_fname(fname, tag_fname, TRUE); - retval = (path_full_compare(fullname, buf_ffname, TRUE) & kEqualFiles); + fullname = expand_tag_fname(fname, tag_fname, true); + retval = (path_full_compare(fullname, buf_ffname, true) & kEqualFiles); xfree(fullname); *fname_end = c; } diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index e3b717989e..0379235ec0 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -93,6 +93,7 @@ NEW_TESTS ?= \ test_quickfix.res \ test_quotestar.res \ test_recover.res \ + test_registers.res \ test_retab.res \ test_scrollbind.res \ test_search.res \ @@ -115,6 +116,7 @@ NEW_TESTS ?= \ test_visual.res \ test_winbuf_close.res \ test_window_id.res \ + test_windows_home.res \ test_wordcount.res \ test_writefile.res \ test_alot_latin.res \ diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index f2cd84f556..8014b76af7 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -173,6 +173,9 @@ func FinishTesting() " Don't write viminfo on exit. set viminfo= + " Clean up files created by setup.vim + call delete('XfakeHOME', 'rf') + if s:fail == 0 " Success, create the .res file so that make knows it's done. exe 'split ' . fnamemodify(g:testname, ':r') . '.res' diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim index aac9fefef4..f3cc7da70f 100644 --- a/src/nvim/testdir/setup.vim +++ b/src/nvim/testdir/setup.vim @@ -1,5 +1,11 @@ " Common preparations for running tests. +" Only load this once. +if exists('s:did_load') + finish +endif +let s:did_load = 1 + " Align Nvim defaults to Vim. set sidescroll=0 set directory^=. @@ -16,7 +22,10 @@ set rtp=$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after let &packpath = &rtp " Make sure $HOME does not get read or written. -let $HOME = '/does/not/exist' +let $HOME = expand(getcwd() . '/XfakeHOME') +if !isdirectory($HOME) + call mkdir($HOME) +endif " Use default shell on Windows to avoid segfault, caused by TUI if has('win32') diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim new file mode 100644 index 0000000000..d7b6de5652 --- /dev/null +++ b/src/nvim/testdir/test_registers.vim @@ -0,0 +1,65 @@ + +func Test_yank_shows_register() + enew + set report=0 + call setline(1, ['foo', 'bar']) + " Line-wise + exe 'norm! yy' + call assert_equal('1 line yanked', v:statusmsg) + exe 'norm! "zyy' + call assert_equal('1 line yanked into "z', v:statusmsg) + exe 'norm! yj' + call assert_equal('2 lines yanked', v:statusmsg) + exe 'norm! "zyj' + call assert_equal('2 lines yanked into "z', v:statusmsg) + + " Block-wise + exe "norm! \<C-V>y" + call assert_equal('block of 1 line yanked', v:statusmsg) + exe "norm! \<C-V>\"zy" + call assert_equal('block of 1 line yanked into "z', v:statusmsg) + exe "norm! \<C-V>jy" + call assert_equal('block of 2 lines yanked', v:statusmsg) + exe "norm! \<C-V>j\"zy" + call assert_equal('block of 2 lines yanked into "z', v:statusmsg) + + bwipe! +endfunc + +func Test_display_registers() + e file1 + e file2 + call setline(1, ['foo', 'bar']) + /bar + exe 'norm! y2l"axx' + call feedkeys("i\<C-R>=2*4\n\<esc>") + call feedkeys(":ls\n", 'xt') + + let a = execute('display') + let b = execute('registers') + + call assert_equal(a, b) + call assert_match('^\n--- Registers ---\n' + \ . '"" a\n' + \ . '"0 ba\n' + \ . '"1 b\n' + \ . '"a b\n' + \ . '.*' + \ . '"- a\n' + \ . '.*' + \ . '": ls\n' + \ . '"% file2\n' + \ . '"# file1\n' + \ . '"/ bar\n' + \ . '"= 2\*4', a) + + let a = execute('registers a') + call assert_match('^\n--- Registers ---\n' + \ . '"a b', a) + + let a = execute('registers :') + call assert_match('^\n--- Registers ---\n' + \ . '": ls', a) + + bwipe! +endfunc diff --git a/src/nvim/testdir/test_virtualedit.vim b/src/nvim/testdir/test_virtualedit.vim index 2b8849f488..d49025237b 100644 --- a/src/nvim/testdir/test_virtualedit.vim +++ b/src/nvim/testdir/test_virtualedit.vim @@ -41,3 +41,21 @@ func Test_paste_end_of_line() bwipe! set virtualedit= endfunc + +func Test_edit_CTRL_G() + new + set virtualedit=insert + call setline(1, ['123', '1', '12']) + exe "normal! ggA\<c-g>jx\<c-g>jx" + call assert_equal(['123', '1 x', '12 x'], getline(1,'$')) + + set virtualedit=all + %d_ + call setline(1, ['1', '12']) + exe "normal! ggllix\<c-g>jx" + call assert_equal(['1 x', '12x'], getline(1,'$')) + + + bwipe! + set virtualedit= +endfunc diff --git a/src/nvim/testdir/test_windows_home.vim b/src/nvim/testdir/test_windows_home.vim new file mode 100644 index 0000000000..bbcbf96050 --- /dev/null +++ b/src/nvim/testdir/test_windows_home.vim @@ -0,0 +1,121 @@ +" Test for $HOME on Windows. + +if !has('win32') + finish +endif + +let s:env = {} + +func s:restore_env() + for i in keys(s:env) + exe 'let ' . i . '=s:env["' . i . '"]' + endfor +endfunc + +func s:save_env(...) + for i in a:000 + exe 'let s:env["' . i . '"]=' . i + endfor +endfunc + +func s:unlet_env(...) + for i in a:000 + exe 'let ' . i . '=""' + endfor +endfunc + +func CheckHomeIsMissingFromSubprocessEnvironment() + silent! let out = system('set') + let env = filter(split(out, "\n"), 'v:val=~"^HOME="') + call assert_equal(0, len(env)) +endfunc + +func CheckHomeIsInSubprocessEnvironment(exp) + silent! let out = system('set') + let env = filter(split(out, "\n"), 'v:val=~"^HOME="') + let home = len(env) == 0 ? "" : substitute(env[0], '[^=]\+=', '', '') + call assert_equal(a:exp, home) +endfunc + +func CheckHome(exp, ...) + call assert_equal(a:exp, $HOME) + call assert_equal(a:exp, expand('~', ':p')) + if !a:0 + call CheckHomeIsMissingFromSubprocessEnvironment() + else + call CheckHomeIsInSubprocessEnvironment(a:1) + endif +endfunc + +func Test_WindowsHome() + command! -nargs=* SaveEnv call <SID>save_env(<f-args>) + command! -nargs=* RestoreEnv call <SID>restore_env() + command! -nargs=* UnletEnv call <SID>unlet_env(<f-args>) + set noshellslash + + let save_home = $HOME + SaveEnv $USERPROFILE $HOMEDRIVE $HOMEPATH + try + " Normal behavior: use $HOMEDRIVE and $HOMEPATH, ignore $USERPROFILE + let $USERPROFILE = 'unused' + let $HOMEDRIVE = 'C:' + let $HOMEPATH = '\foobar' + let $HOME = '' " Force recomputing "homedir" + call CheckHome('C:\foobar') + + " Same, but with $HOMEPATH not set + UnletEnv $HOMEPATH + let $HOME = '' " Force recomputing "homedir" + call CheckHome('C:\') + + " Use $USERPROFILE if $HOMEPATH and $HOMEDRIVE are empty + UnletEnv $HOMEDRIVE $HOMEPATH + let $USERPROFILE = 'C:\foo' + let $HOME = '' " Force recomputing "homedir" + call CheckHome('C:\foo') + + " If $HOME is set the others don't matter + let $HOME = 'C:\bar' + let $USERPROFILE = 'unused' + let $HOMEDRIVE = 'unused' + let $HOMEPATH = 'unused' + call CheckHome('C:\bar', 'C:\bar') + + " If $HOME contains %USERPROFILE% it is expanded + let $USERPROFILE = 'C:\foo' + let $HOME = '%USERPROFILE%\bar' + let $HOMEDRIVE = 'unused' + let $HOMEPATH = 'unused' + " call CheckHome('C:\foo\bar', '%USERPROFILE%\bar') + + " Invalid $HOME is kept + let $USERPROFILE = 'C:\foo' + let $HOME = '%USERPROFILE' + let $HOMEDRIVE = 'unused' + let $HOMEPATH = 'unused' + call CheckHome('%USERPROFILE', '%USERPROFILE') + + " %USERPROFILE% not at start of $HOME is not expanded + let $USERPROFILE = 'unused' + let $HOME = 'C:\%USERPROFILE%' + let $HOMEDRIVE = 'unused' + let $HOMEPATH = 'unused' + call CheckHome('C:\%USERPROFILE%', 'C:\%USERPROFILE%') + + if has('channel') + RestoreEnv + let $HOME = save_home + let env = '' + let job = job_start('cmd /c set', {'out_cb': {ch,x->[env,execute('let env=x')]}}) + sleep 1 + let env = filter(split(env, "\n"), 'v:val=="HOME"') + let home = len(env) == 0 ? "" : env[0] + call assert_equal('', home) + endif + finally + RestoreEnv + delcommand SaveEnv + delcommand RestoreEnv + delcommand UnletEnv + endtry +endfunc diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 56c47ed6cc..508d25cd3b 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -802,7 +802,16 @@ static void reset_scroll_region(UI *ui) static void tui_grid_resize(UI *ui, Integer g, Integer width, Integer height) { TUIData *data = ui->data; - ugrid_resize(&data->grid, (int)width, (int)height); + UGrid *grid = &data->grid; + ugrid_resize(grid, (int)width, (int)height); + + // resize might not always be followed by a clear before flush + // so clip the invalid region + for (size_t i = 0; i < kv_size(data->invalid_regions); i++) { + Rect *r = &kv_A(data->invalid_regions, i); + r->bot = MIN(r->bot, grid->height-1); + r->right = MIN(r->right, grid->width-1); + } if (!got_winch) { // Try to resize the terminal window. UNIBI_SET_NUM_VAR(data->params[0], (int)height); @@ -823,7 +832,7 @@ static void tui_grid_clear(UI *ui, Integer g) UGrid *grid = &data->grid; ugrid_clear(grid); kv_size(data->invalid_regions) = 0; - clear_region(ui, grid->top, grid->bot, grid->left, grid->right, + clear_region(ui, 0, grid->height-1, 0, grid->width-1, data->clear_attrs); } diff --git a/src/nvim/ugrid.c b/src/nvim/ugrid.c index 48f3cff2d7..36936970f8 100644 --- a/src/nvim/ugrid.c +++ b/src/nvim/ugrid.c @@ -44,7 +44,7 @@ void ugrid_resize(UGrid *grid, int width, int height) void ugrid_clear(UGrid *grid) { - clear_region(grid, grid->top, grid->bot, grid->left, grid->right, + clear_region(grid, 0, grid->height-1, 0, grid->width-1, HLATTRS_INIT); } diff --git a/test/functional/eval/special_vars_spec.lua b/test/functional/eval/special_vars_spec.lua index b5773a5529..97a12d490d 100644 --- a/test/functional/eval/special_vars_spec.lua +++ b/test/functional/eval/special_vars_spec.lua @@ -174,5 +174,17 @@ describe('Special values', function() command('let count = []') -- v:count is readonly eq(1, eval('count is# g:["count"]')) end) + it('v:errmsg is distinct from errmsg', function() + command('let errmsg = 1') + eq(1, eval('errmsg is# g:["errmsg"]')) + end) + it('v:shell_error is distinct from shell_error', function() + command('let shell_error = []') -- v:shell_error is readonly + eq(1, eval('shell_error is# g:["shell_error"]')) + end) + it('v:this_session is distinct from this_session', function() + command('let this_session = []') + eq(1, eval('this_session is# g:["this_session"]')) + end) end) end) diff --git a/test/functional/legacy/packadd_spec.lua b/test/functional/legacy/packadd_spec.lua index fb308475c0..67f6006d1d 100644 --- a/test/functional/legacy/packadd_spec.lua +++ b/test/functional/legacy/packadd_spec.lua @@ -58,6 +58,24 @@ describe('packadd', function() call assert_fails("packadd", 'E471:') endfunc + func Test_packadd_start() + let plugdir = expand(s:topdir . '/pack/mine/start/other') + call mkdir(plugdir . '/plugin', 'p') + set rtp& + let rtp = &rtp + filetype on + + exe 'split ' . plugdir . '/plugin/test.vim' + call setline(1, 'let g:plugin_works = 24') + wq + + packadd other + + call assert_equal(24, g:plugin_works) + call assert_true(len(&rtp) > len(rtp)) + call assert_true(&rtp =~ (escape(plugdir, '\') . '\($\|,\)')) + endfunc + func Test_packadd_noload() call mkdir(s:plugdir . '/plugin', 'p') call mkdir(s:plugdir . '/syntax', 'p') @@ -286,6 +304,11 @@ describe('packadd', function() expected_empty() end) + it('loads packages from "start" directory', function() + call('Test_packadd_start') + expected_empty() + end) + describe('command line completion', function() local Screen = require('test.functional.ui.screen') local screen diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index d71d8cf3a8..c40b2210ff 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -73,6 +73,7 @@ local helpers = require('test.functional.helpers')(nil) local request, run, uimeths = helpers.request, helpers.run, helpers.uimeths +local eq = helpers.eq local dedent = helpers.dedent local Screen = {} @@ -389,12 +390,21 @@ function Screen:_handle_mode_info_set(cursor_style_enabled, mode_info) end function Screen:_handle_clear() + -- the first implemented UI protocol clients (python-gui and builitin TUI) + -- allowed the cleared region to be restricted by setting the scroll region. + -- this was never used by nvim tough, and not documented and implemented by + -- newer clients, to check we remain compatible with both kind of clients, + -- ensure the scroll region is in a reset state. + local expected_region = { + top = 1, bot = self._height, left = 1, right = self._width + } + eq(expected_region, self._scroll_region) self:_clear_block(1, self._height, 1, self._width) end function Screen:_handle_grid_clear(grid) assert(grid == 1) - self:_handle_clear() + self:_clear_block(1, self._height, 1, self._width) end function Screen:_handle_eol_clear() @@ -446,22 +456,30 @@ function Screen:_handle_scroll(count) local bot = self._scroll_region.bot local left = self._scroll_region.left local right = self._scroll_region.right + self:_handle_grid_scroll(1, top-1, bot, left-1, right, count, 0) +end + +function Screen:_handle_grid_scroll(grid, top, bot, left, right, rows, cols) + top = top+1 + left = left+1 + assert(grid == 1) + assert(cols == 0) local start, stop, step - if count > 0 then + if rows > 0 then start = top - stop = bot - count + stop = bot - rows step = 1 else start = bot - stop = top - count + stop = top - rows step = -1 end -- shift scroll region for i = start, stop, step do local target = self._rows[i] - local source = self._rows[i + count] + local source = self._rows[i + rows] for j = left, right do target[j].text = source[j].text target[j].attrs = source[j].attrs @@ -470,19 +488,11 @@ function Screen:_handle_scroll(count) end -- clear invalid rows - for i = stop + step, stop + count, step do + for i = stop + step, stop + rows, step do self:_clear_row_section(i, left, right) end end -function Screen:_handle_grid_scroll(grid, top, bot, left, right, rows, cols) - assert(grid == 1) - assert(cols == 0) - -- TODO: if we truly believe we should translate the other way - self:_handle_set_scroll_region(top,bot-1,left,right-1) - self:_handle_scroll(rows) -end - function Screen:_handle_hl_attr_define(id, rgb_attrs, cterm_attrs, info) self._attr_table[id] = {rgb_attrs, cterm_attrs} self._hl_info[id] = info diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua index 75a2d4978d..957d8c0915 100644 --- a/test/functional/ui/screen_basic_spec.lua +++ b/test/functional/ui/screen_basic_spec.lua @@ -354,6 +354,101 @@ local function screen_tests(newgrid) {0:~ }| | ]]) + + feed(':echo "'..string.rep('x\\n', 12)..'"<cr>') + screen:expect([[ + x | + x | + x | + x | + x | + x | + x | + x | + x | + x | + x | + x | + | + {7:Press ENTER or type command to continue}^ | + ]]) + + feed('<cr>') + screen:expect([[ + {4: [No Name] }{2: [No Name] }{3: }{4:X}| + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + + end) + + it('redraws properly with :tab split right after scroll', function() + feed('30Ofoo<esc>gg') + + command('vsplit') + screen:expect([[ + ^foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + {1:[No Name] [+] }{3:[No Name] [+] }| + | + ]]) + + feed('<PageDown>') + screen:expect([[ + ^foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + {1:[No Name] [+] }{3:[No Name] [+] }| + | + ]]) + + command('tab split') + screen:expect([[ + {4: }{5:2}{4:+ [No Name] }{2: + [No Name] }{3: }{4:X}| + ^foo | + foo | + foo | + foo | + foo | + foo | + foo | + foo | + foo | + foo | + foo | + foo | + | + ]]) end) end) |