aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt8
-rw-r--r--README.md35
-rw-r--r--runtime/doc/eval.txt7
-rw-r--r--runtime/doc/starting.txt2
-rw-r--r--runtime/doc/vim_diff.txt3
-rw-r--r--src/nvim/CMakeLists.txt12
-rw-r--r--src/nvim/api/ui.c9
-rw-r--r--src/nvim/buffer.c2
-rw-r--r--src/nvim/charset.c6
-rw-r--r--src/nvim/eval.c6
-rw-r--r--src/nvim/ex_cmds.c8
-rw-r--r--src/nvim/ex_cmds2.c29
-rw-r--r--src/nvim/ex_docmd.c16
-rw-r--r--src/nvim/ex_getln.c31
-rw-r--r--src/nvim/log.c27
-rw-r--r--src/nvim/memline.c11
-rw-r--r--src/nvim/ops.c18
-rw-r--r--src/nvim/os/env.c22
-rw-r--r--src/nvim/os/time.c4
-rw-r--r--src/nvim/path.c3
-rw-r--r--src/nvim/quickfix.c7
-rw-r--r--src/nvim/screen.c8
-rw-r--r--src/nvim/spell.c22
-rw-r--r--src/nvim/spellfile.c8
-rw-r--r--src/nvim/tag.c23
-rw-r--r--src/nvim/testdir/Makefile2
-rw-r--r--src/nvim/testdir/runtest.vim3
-rw-r--r--src/nvim/testdir/setup.vim11
-rw-r--r--src/nvim/testdir/test_registers.vim65
-rw-r--r--src/nvim/testdir/test_virtualedit.vim18
-rw-r--r--src/nvim/testdir/test_windows_home.vim121
-rw-r--r--src/nvim/tui/tui.c13
-rw-r--r--src/nvim/ugrid.c2
-rw-r--r--test/functional/eval/special_vars_spec.lua12
-rw-r--r--test/functional/legacy/packadd_spec.lua23
-rw-r--r--test/functional/ui/screen.lua38
-rw-r--r--test/functional/ui/screen_basic_spec.lua95
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)
diff --git a/README.md b/README.md
index 803e16aaf0..d3b3752625 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,21 @@ for more information.
[![Throughput Graph](https://graphs.waffle.io/neovim/neovim/throughput.svg)](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)