aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/autoload/health/provider.vim30
-rw-r--r--runtime/doc/eval.txt7
-rw-r--r--src/nvim/buffer.c2
-rw-r--r--src/nvim/eval.c63
-rw-r--r--src/nvim/ex_cmds.c8
-rw-r--r--src/nvim/ex_getln.c31
-rw-r--r--src/nvim/file_search.c66
-rw-r--r--src/nvim/log.c29
-rw-r--r--src/nvim/memline.c11
-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/po/check.vim88
-rw-r--r--src/nvim/po/cleanup.vim8
-rw-r--r--src/nvim/quickfix.c7
-rw-r--r--src/nvim/screen.c77
-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/Makefile1
-rw-r--r--src/nvim/testdir/runtest.vim3
-rw-r--r--src/nvim/testdir/setup.vim11
-rw-r--r--src/nvim/testdir/test_alot.vim1
-rw-r--r--src/nvim/testdir/test_cd.vim13
-rw-r--r--src/nvim/testdir/test_functions.vim8
-rw-r--r--src/nvim/testdir/test_windows_home.vim121
-rw-r--r--src/nvim/tui/tui.c11
-rw-r--r--test/functional/eval/system_spec.lua42
-rw-r--r--test/functional/helpers.lua2
-rw-r--r--test/functional/ui/sign_spec.lua33
-rw-r--r--test/helpers.lua4
31 files changed, 610 insertions, 149 deletions
diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim
index 7ab06c3820..9a04649bfa 100644
--- a/runtime/autoload/health/provider.vim
+++ b/runtime/autoload/health/provider.vim
@@ -37,7 +37,12 @@ endfunction
" Handler for s:system() function.
function! s:system_handler(jobid, data, event) dict abort
- if a:event ==# 'stdout' || a:event ==# 'stderr'
+ if a:event ==# 'stderr'
+ let self.stderr .= join(a:data, '')
+ if !self.ignore_stderr
+ let self.output .= join(a:data, '')
+ endif
+ elseif a:event ==# 'stdout'
let self.output .= join(a:data, '')
elseif a:event ==# 'exit'
let s:shell_error = a:data
@@ -57,16 +62,15 @@ endfunction
" Run a system command and timeout after 30 seconds.
function! s:system(cmd, ...) abort
let stdin = a:0 ? a:1 : ''
- let ignore_stderr = a:0 > 1 ? a:2 : 0
let ignore_error = a:0 > 2 ? a:3 : 0
let opts = {
+ \ 'ignore_stderr': a:0 > 1 ? a:2 : 0,
\ 'output': '',
+ \ 'stderr': '',
\ 'on_stdout': function('s:system_handler'),
+ \ 'on_stderr': function('s:system_handler'),
\ 'on_exit': function('s:system_handler'),
\ }
- if !ignore_stderr
- let opts.on_stderr = function('s:system_handler')
- endif
let jobid = jobstart(a:cmd, opts)
if jobid < 1
@@ -85,8 +89,8 @@ function! s:system(cmd, ...) abort
call health#report_error(printf('Command timed out: %s', s:shellify(a:cmd)))
call jobstop(jobid)
elseif s:shell_error != 0 && !ignore_error
- call health#report_error(printf("Command error (job=%d): `%s` (in %s)\nOutput: %s",
- \ jobid, s:shellify(a:cmd), string(getcwd()), opts.output))
+ call health#report_error(printf("Command error (job=%d, exit code %d): `%s` (in %s)\nOutput: %s\nStderr: %s",
+ \ jobid, s:shell_error, s:shellify(a:cmd), string(getcwd()), opts.output, opts.stderr))
endif
return opts.output
@@ -291,20 +295,14 @@ function! s:check_python(version) abort
if empty(pyname)
call health#report_warn('No Python interpreter was found with the neovim '
\ . 'module. Using the first available for diagnostics.')
- endif
-
- if !empty(pyname)
- if exists('g:'.host_prog_var)
- let python_bin = exepath(pyname)
- endif
- let pyname = fnamemodify(pyname, ':t')
+ elseif exists('g:'.host_prog_var)
+ let python_bin = pyname
endif
if !empty(pythonx_errs)
call health#report_error('Python provider error', pythonx_errs)
- endif
- if !empty(pyname) && empty(python_bin) && empty(pythonx_errs)
+ elseif !empty(pyname) && empty(python_bin)
if !exists('g:'.host_prog_var)
call health#report_info(printf('`g:%s` is not set. Searching for '
\ . '%s in the environment.', host_prog_var, pyname))
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index dce531b663..7b5fc191e9 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -2990,11 +2990,16 @@ cosh({expr}) *cosh()*
count({comp}, {expr} [, {ic} [, {start}]]) *count()*
Return the number of times an item with value {expr} appears
- in |List| or |Dictionary| {comp}.
+ in |String|, |List| or |Dictionary| {comp}.
+
If {start} is given then start with the item with this index.
{start} can only be used with a |List|.
+
When {ic} is given and it's |TRUE| then case is ignored.
+ When {comp} is a string then the number of not overlapping
+ occurences of {expr} is returned.
+
*cscope_connection()*
cscope_connection([{num} , {dbpath} [, {prepend}]])
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/eval.c b/src/nvim/eval.c
index 36662b2a39..3843245070 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -7592,9 +7592,38 @@ static void f_copy(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
long n = 0;
- int ic = FALSE;
+ int ic = 0;
+ bool error = false;
- if (argvars[0].v_type == VAR_LIST) {
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ ic = tv_get_number_chk(&argvars[2], &error);
+ }
+
+ if (argvars[0].v_type == VAR_STRING) {
+ const char_u *expr = (char_u *)tv_get_string_chk(&argvars[1]);
+ const char_u *p = argvars[0].vval.v_string;
+
+ if (!error && expr != NULL && p != NULL) {
+ if (ic) {
+ const size_t len = STRLEN(expr);
+
+ while (*p != NUL) {
+ if (mb_strnicmp(p, expr, len) == 0) {
+ n++;
+ p += len;
+ } else {
+ MB_PTR_ADV(p);
+ }
+ }
+ } else {
+ char_u *next;
+ while ((next = (char_u *)strstr((char *)p, (char *)expr)) != NULL) {
+ n++;
+ p = next + STRLEN(expr);
+ }
+ }
+ }
+ } else if (argvars[0].v_type == VAR_LIST) {
listitem_T *li;
list_T *l;
long idx;
@@ -7602,9 +7631,6 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if ((l = argvars[0].vval.v_list) != NULL) {
li = tv_list_first(l);
if (argvars[2].v_type != VAR_UNKNOWN) {
- bool error = false;
-
- ic = tv_get_number_chk(&argvars[2], &error);
if (argvars[3].v_type != VAR_UNKNOWN) {
idx = tv_get_number_chk(&argvars[3], &error);
if (!error) {
@@ -7630,10 +7656,7 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
hashitem_T *hi;
if ((d = argvars[0].vval.v_dict) != NULL) {
- bool error = false;
-
if (argvars[2].v_type != VAR_UNKNOWN) {
- ic = tv_get_number_chk(&argvars[2], &error);
if (argvars[3].v_type != VAR_UNKNOWN) {
EMSG(_(e_invarg));
}
@@ -7649,8 +7672,9 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
}
- } else
+ } else {
EMSG2(_(e_listdictarg), "count()");
+ }
rettv->vval.v_number = n;
}
@@ -16377,9 +16401,12 @@ static list_T *string_to_list(const char *str, size_t len, const bool keepempty)
return list;
}
+// os_system wrapper. Handles 'verbose', :profile, and v:shell_error.
static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv,
bool retlist)
{
+ proftime_T wait_time;
+
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
@@ -16406,11 +16433,29 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv,
return; // Already did emsg.
}
+ if (p_verbose > 3) {
+ char buf[NUMBUFLEN];
+ const char * cmd = tv_get_string_buf(argvars, buf);
+
+ verbose_enter_scroll();
+ smsg(_("Calling shell to execute: \"%s\""), cmd);
+ msg_puts("\n\n");
+ verbose_leave_scroll();
+ }
+
+ if (do_profiling == PROF_YES) {
+ prof_child_enter(&wait_time);
+ }
+
// execute the command
size_t nread = 0;
char *res = NULL;
int status = os_system(argv, input, input_len, &res, &nread);
+ if (do_profiling == PROF_YES) {
+ prof_child_exit(&wait_time);
+ }
+
xfree(input);
set_vim_var_nr(VV_SHELL_ERROR, (long) status);
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_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/file_search.c b/src/nvim/file_search.c
index 5b17b58781..ee775bab4a 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -577,7 +577,7 @@ char_u *vim_findfile(void *search_ctx_arg)
char_u *file_path;
char_u *rest_of_wildcards;
char_u *path_end = NULL;
- ff_stack_T *stackp;
+ ff_stack_T *stackp = NULL;
size_t len;
char_u *p;
char_u *suf;
@@ -683,28 +683,40 @@ char_u *vim_findfile(void *search_ctx_arg)
dirptrs[0] = file_path;
dirptrs[1] = NULL;
- /* if we have a start dir copy it in */
+ // if we have a start dir copy it in
if (!vim_isAbsName(stackp->ffs_fix_path)
&& search_ctx->ffsc_start_dir) {
+ if (STRLEN(search_ctx->ffsc_start_dir) + 1 >= MAXPATHL) {
+ goto fail;
+ }
STRCPY(file_path, search_ctx->ffsc_start_dir);
- add_pathsep((char *)file_path);
+ if (!add_pathsep((char *)file_path)) {
+ goto fail;
+ }
}
- /* append the fix part of the search path */
+ // append the fix part of the search path
+ if (STRLEN(file_path) + STRLEN(stackp->ffs_fix_path) + 1 >= MAXPATHL) {
+ goto fail;
+ }
STRCAT(file_path, stackp->ffs_fix_path);
- add_pathsep((char *)file_path);
+ if (!add_pathsep((char *)file_path)) {
+ goto fail;
+ }
rest_of_wildcards = stackp->ffs_wc_path;
if (*rest_of_wildcards != NUL) {
len = STRLEN(file_path);
if (STRNCMP(rest_of_wildcards, "**", 2) == 0) {
- /* pointer to the restrict byte
- * The restrict byte is not a character!
- */
+ // pointer to the restrict byte
+ // The restrict byte is not a character!
p = rest_of_wildcards + 2;
if (*p > 0) {
(*p)--;
+ if (len + 1 >= MAXPATHL) {
+ goto fail;
+ }
file_path[len++] = '*';
}
@@ -729,8 +741,12 @@ char_u *vim_findfile(void *search_ctx_arg)
* on the stack again for further search.
*/
while (*rest_of_wildcards
- && !vim_ispathsep(*rest_of_wildcards))
+ && !vim_ispathsep(*rest_of_wildcards)) {
+ if (len + 1 >= MAXPATHL) {
+ goto fail;
+ }
file_path[len++] = *rest_of_wildcards++;
+ }
file_path[len] = NUL;
if (vim_ispathsep(*rest_of_wildcards))
@@ -773,10 +789,15 @@ char_u *vim_findfile(void *search_ctx_arg)
&& !os_isdir(stackp->ffs_filearray[i]))
continue; /* not a directory */
- /* prepare the filename to be checked for existence
- * below */
+ // prepare the filename to be checked for existence below
+ if (STRLEN(stackp->ffs_filearray[i]) + 1
+ + STRLEN(search_ctx->ffsc_file_to_search) >= MAXPATHL) {
+ goto fail;
+ }
STRCPY(file_path, stackp->ffs_filearray[i]);
- add_pathsep((char *)file_path);
+ if (!add_pathsep((char *)file_path)) {
+ goto fail;
+ }
STRCAT(file_path, search_ctx->ffsc_file_to_search);
/*
@@ -924,8 +945,14 @@ char_u *vim_findfile(void *search_ctx_arg)
if (*search_ctx->ffsc_start_dir == 0)
break;
+ if (STRLEN(search_ctx->ffsc_start_dir) + 1
+ + STRLEN(search_ctx->ffsc_fix_path) >= MAXPATHL) {
+ goto fail;
+ }
STRCPY(file_path, search_ctx->ffsc_start_dir);
- add_pathsep((char *)file_path);
+ if (!add_pathsep((char *)file_path)) {
+ goto fail;
+ }
STRCAT(file_path, search_ctx->ffsc_fix_path);
/* create a new stack entry */
@@ -936,6 +963,8 @@ char_u *vim_findfile(void *search_ctx_arg)
break;
}
+fail:
+ ff_free_stack_element(stackp);
xfree(file_path);
return NULL;
}
@@ -1192,14 +1221,19 @@ static ff_stack_T *ff_pop(ff_search_ctx_T *search_ctx)
/*
* free the given stack element
*/
-static void ff_free_stack_element(ff_stack_T *stack_ptr)
+static void ff_free_stack_element(ff_stack_T *const stack_ptr)
{
- /* free handles possible NULL pointers */
+ if (stack_ptr == NULL) {
+ return;
+ }
+
+ // free handles possible NULL pointers
xfree(stack_ptr->ffs_fix_path);
xfree(stack_ptr->ffs_wc_path);
- if (stack_ptr->ffs_filearray != NULL)
+ if (stack_ptr->ffs_filearray != NULL) {
FreeWild(stack_ptr->ffs_filearray_size, stack_ptr->ffs_filearray);
+ }
xfree(stack_ptr);
}
diff --git a/src/nvim/log.c b/src/nvim/log.c
index 7bfe5c4089..6de231858e 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"
@@ -195,7 +198,7 @@ void log_callstack_to_file(FILE *log_file, const char *const func_name,
}
assert(24 + exepathlen < IOSIZE); // Must fit in `cmdbuf` below.
- char cmdbuf[IOSIZE + (20 * ARRAY_SIZE(trace))];
+ char cmdbuf[IOSIZE + (20 * ARRAY_SIZE(trace)) + MAXPATHL];
snprintf(cmdbuf, sizeof(cmdbuf), "addr2line -e %s -f -p", exepath);
for (int i = 1; i < trace_size; i++) {
char buf[20]; // 64-bit pointer 0xNNNNNNNNNNNNNNNN with leading space.
@@ -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/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/po/check.vim b/src/nvim/po/check.vim
index b323174550..eae27ef74d 100644
--- a/src/nvim/po/check.vim
+++ b/src/nvim/po/check.vim
@@ -38,6 +38,7 @@ let s:save_wrapscan = &wrapscan
set nowrapscan
" Start at the first "msgid" line.
+let wsv = winsaveview()
1
/^msgid\>
@@ -113,9 +114,96 @@ if search('msgid "\("\n"\)\?\([EW][0-9]\+:\).*\nmsgstr "\("\n"\)\?[^"]\@=\2\@!')
endif
endif
+func! CountNl(first, last)
+ let nl = 0
+ for lnum in range(a:first, a:last)
+ let nl += count(getline(lnum), "\n")
+ endfor
+ return nl
+endfunc
+
+" Check that the \n at the end of the msgid line is also present in the msgstr
+" line. Skip over the header.
+1
+/^"MIME-Version:
+while 1
+ let lnum = search('^msgid\>')
+ if lnum <= 0
+ break
+ endif
+ let strlnum = search('^msgstr\>')
+ let end = search('^$')
+ if end <= 0
+ let end = line('$') + 1
+ endif
+ let origcount = CountNl(lnum, strlnum - 1)
+ let transcount = CountNl(strlnum, end - 1)
+ " Allow for a few more or less line breaks when there are 2 or more
+ if origcount != transcount && (origcount <= 2 || transcount <= 2)
+ echomsg 'Mismatching "\n" in line ' . line('.')
+ if error == 0
+ let error = lnum
+ endif
+ endif
+endwhile
+
+" Check that the file is well formed according to msgfmts understanding
+if executable("msgfmt")
+ let filename = expand("%")
+ let a = system("msgfmt --statistics OLD_PO_FILE_INPUT=yes " . filename)
+ if v:shell_error != 0
+ let error = matchstr(a, filename.':\zs\d\+\ze:')+0
+ for line in split(a, '\n') | echomsg line | endfor
+ endif
+endif
+
+" Check that the plural form is properly initialized
+1
+let plural = search('^msgid_plural ', 'n')
+if (plural && search('^"Plural-Forms: ', 'n') == 0) || (plural && search('^msgstr\[0\] ".\+"', 'n') != plural + 1)
+ if search('^"Plural-Forms: ', 'n') == 0
+ echomsg "Missing Plural header"
+ if error == 0
+ let error = search('\(^"[A-Za-z-_]\+: .*\\n"\n\)\+\zs', 'n') - 1
+ endif
+ elseif error == 0
+ let error = plural
+ endif
+elseif !plural && search('^"Plural-Forms: ', 'n')
+ " We allow for a stray plural header, msginit adds one.
+endif
+
+" Check that 8bit encoding is used instead of 8-bit
+let cte = search('^"Content-Transfer-Encoding:\s\+8-bit', 'n')
+let ctc = search('^"Content-Type:.*;\s\+\<charset=[iI][sS][oO]_', 'n')
+let ctu = search('^"Content-Type:.*;\s\+\<charset=utf-8', 'n')
+if cte
+ echomsg "Content-Transfer-Encoding should be 8bit instead of 8-bit"
+ " TODO: make this an error
+ " if error == 0
+ " let error = cte
+ " endif
+elseif ctc
+ echomsg "Content-Type charset should be 'ISO-...' instead of 'ISO_...'"
+ " TODO: make this an error
+ " if error == 0
+ " let error = ct
+ " endif
+elseif ctu
+ echomsg "Content-Type charset should be 'UTF-8' instead of 'utf-8'"
+ " TODO: make this an error
+ " if error == 0
+ " let error = ct
+ " endif
+endif
+
+
if error == 0
+ " If all was OK restore the view.
+ call winrestview(wsv)
echomsg "OK"
else
+ " Put the cursor on the line with the error.
exe error
endif
diff --git a/src/nvim/po/cleanup.vim b/src/nvim/po/cleanup.vim
index 24ae74ed38..b27d88092f 100644
--- a/src/nvim/po/cleanup.vim
+++ b/src/nvim/po/cleanup.vim
@@ -8,12 +8,18 @@
let s:was_diff = &diff
setl nodiff
-silent g/^#: /d
+" untranslated message preceded by c-format or comment
+silent g/^#, c-format\n#/.d
+silent g/^#\..*\n#/.d
+
+silent g/^#[:~] /d
silent g/^#, fuzzy\(, .*\)\=\nmsgid ""\@!/.+1,/^$/-1s/^/#\~ /
silent g/^msgstr"/s//msgstr "/
silent g/^msgid"/s//msgid "/
silent g/^msgstr ""\(\n"\)\@!/?^msgid?,.s/^/#\~ /
+silent g/^\n\n\n/.d
+
if s:was_diff
setl diff
endif
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 e8dbc11710..9f0d8a5080 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -507,6 +507,7 @@ void update_single_line(win_T *wp, linenr_T lnum)
init_search_hl(wp);
start_search_hl();
prepare_search_hl(wp, lnum);
+ update_window_hl(wp, false);
win_line(wp, lnum, row, row + wp->w_lines[j].wl_size, false);
end_search_hl();
break;
@@ -542,51 +543,57 @@ static void update_finish(void)
updating_screen = FALSE;
}
-void update_debug_sign(buf_T *buf, linenr_T lnum)
+void update_debug_sign(const buf_T *const buf, const linenr_T lnum)
{
- int doit = FALSE;
- win_foldinfo.fi_level = 0;
-
- /* update/delete a specific mark */
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (buf != NULL && lnum > 0) {
- if (wp->w_buffer == buf && lnum >= wp->w_topline
- && lnum < wp->w_botline) {
- if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) {
- wp->w_redraw_top = lnum;
- }
- if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) {
- wp->w_redraw_bot = lnum;
- }
- redraw_win_later(wp, VALID);
+ bool doit = false;
+ win_foldinfo.fi_level = 0;
+
+ // update/delete a specific mark
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (buf != NULL && lnum > 0) {
+ if (wp->w_buffer == buf && lnum >= wp->w_topline
+ && lnum < wp->w_botline) {
+ if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) {
+ wp->w_redraw_top = lnum;
+ }
+ if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) {
+ wp->w_redraw_bot = lnum;
}
- } else {
redraw_win_later(wp, VALID);
}
- if (wp->w_redr_type != 0) {
- doit = TRUE;
- }
+ } else {
+ redraw_win_later(wp, VALID);
}
-
- /* Return when there is nothing to do, screen updating is already
- * happening (recursive call) or still starting up. */
- if (!doit || updating_screen || starting) {
- return;
+ if (wp->w_redr_type != 0) {
+ doit = true;
}
+ }
- /* update all windows that need updating */
- update_prepare();
+ // Return when there is nothing to do, screen updating is already
+ // happening (recursive call), messages on the screen or still starting up.
+ if (!doit
+ || updating_screen
+ || State == ASKMORE
+ || State == HITRETURN
+ || msg_scrolled
+ || starting) {
+ return;
+ }
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_redr_type != 0) {
- win_update(wp);
- }
- if (wp->w_redr_status) {
- win_redr_status(wp);
- }
+ // update all windows that need updating
+ update_prepare();
+
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_redr_type != 0) {
+ update_window_hl(wp, wp->w_redr_type >= NOT_VALID);
+ win_update(wp);
+ }
+ if (wp->w_redr_status) {
+ win_redr_status(wp);
}
+ }
- update_finish();
+ update_finish();
}
/*
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 87d7ff5bad..0379235ec0 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -116,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_alot.vim b/src/nvim/testdir/test_alot.vim
index 1a70ac152f..d026221dac 100644
--- a/src/nvim/testdir/test_alot.vim
+++ b/src/nvim/testdir/test_alot.vim
@@ -2,6 +2,7 @@
" This makes testing go faster, since Vim doesn't need to restart.
source test_assign.vim
+source test_cd.vim
source test_changedtick.vim
source test_cursor_func.vim
source test_ex_undo.vim
diff --git a/src/nvim/testdir/test_cd.vim b/src/nvim/testdir/test_cd.vim
new file mode 100644
index 0000000000..e573419bd0
--- /dev/null
+++ b/src/nvim/testdir/test_cd.vim
@@ -0,0 +1,13 @@
+" Test for :cd
+
+func Test_cd_large_path()
+ " This used to crash with a heap write overflow.
+ call assert_fails('cd ' . repeat('x', 5000), 'E472:')
+endfunc
+
+func Test_cd_up_and_down()
+ let path = getcwd()
+ cd ..
+ exe 'cd ' . path
+ call assert_equal(path, getcwd())
+endfunc
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index c59134908c..3b16f2ce9f 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -680,7 +680,13 @@ func Test_count()
call assert_equal(0, count(d, 'c', 1))
call assert_fails('call count(d, "a", 0, 1)', 'E474:')
- call assert_fails('call count("a", "a")', 'E712:')
+
+ call assert_equal(0, count("foo", "bar"))
+ call assert_equal(1, count("foo", "oo"))
+ call assert_equal(2, count("foo", "o"))
+ call assert_equal(0, count("foo", "O"))
+ call assert_equal(2, count("foo", "O", 1))
+ call assert_equal(2, count("fooooo", "oo"))
endfunc
func Test_changenr()
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 cfcfaa83a0..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);
diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua
index 5e12b6a6a4..03aa7fa45f 100644
--- a/test/functional/eval/system_spec.lua
+++ b/test/functional/eval/system_spec.lua
@@ -203,6 +203,48 @@ describe('system()', function()
]])
end)
+ it('prints verbose information', function()
+ feed(':4verbose echo system("echo hi")<cr>')
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ Calling shell to execute: "echo hi" |
+ |
+ hi |
+ |
+ Press ENTER or type command to continue^ |
+ ]])
+ feed('<cr>')
+ end)
+
+ it('self and total time recorded separately', function()
+ local tempfile = helpers.tmpname()
+
+ feed(':function! AlmostNoSelfTime()<cr>')
+ feed('echo system("echo hi")<cr>')
+ feed('endfunction<cr>')
+
+ feed(':profile start ' .. tempfile .. '<cr>')
+ feed(':profile func AlmostNoSelfTime<cr>')
+ feed(':call AlmostNoSelfTime()<cr>')
+ feed(':profile dump<cr>')
+
+ feed(':edit ' .. tempfile .. '<cr>')
+
+ local command_total_time = tonumber(helpers.funcs.split(helpers.funcs.getline(7))[2])
+ local command_self_time = tonumber(helpers.funcs.split(helpers.funcs.getline(7))[3])
+
+ helpers.neq(nil, command_total_time)
+ helpers.neq(nil, command_self_time)
+ end)
+
it('`yes` interrupted with CTRL-C', function()
feed(':call system("' .. (iswin()
and 'for /L %I in (1,0,2) do @echo y'
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index a6d2764187..72e71a2cf2 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -18,6 +18,7 @@ local expect_err = global_helpers.expect_err
local filter = global_helpers.filter
local map = global_helpers.map
local matches = global_helpers.matches
+local near = global_helpers.near
local neq = global_helpers.neq
local ok = global_helpers.ok
local read_file = global_helpers.read_file
@@ -699,6 +700,7 @@ local module = {
meths = meths,
missing_provider = missing_provider,
mkdir = lfs.mkdir,
+ near = near,
neq = neq,
new_pipename = new_pipename,
next_msg = next_msg,
diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua
index c00d99cf90..4fbb46ac34 100644
--- a/test/functional/ui/sign_spec.lua
+++ b/test/functional/ui/sign_spec.lua
@@ -1,6 +1,7 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear, feed, command = helpers.clear, helpers.feed, helpers.command
+local source = helpers.source
describe('Signs', function()
local screen
@@ -13,6 +14,9 @@ describe('Signs', function()
[0] = {bold=true, foreground=255},
[1] = {background = Screen.colors.Yellow},
[2] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.Grey},
+ [3] = {background = Screen.colors.Gray90},
+ [4] = {bold = true, reverse = true},
+ [5] = {reverse = true},
} )
end)
@@ -45,5 +49,34 @@ describe('Signs', function()
|
]])
end)
+
+ it('can be called right after :split', function()
+ feed('ia<cr>b<cr>c<cr><esc>gg')
+ -- This used to cause a crash due to :sign using a special redraw
+ -- (not updating nvim's specific highlight data structures)
+ -- without proper redraw first, as split just flags for redraw later.
+ source([[
+ set cursorline
+ sign define piet text=>> texthl=Search
+ split
+ sign place 3 line=2 name=piet buffer=1
+ ]])
+ screen:expect([[
+ {2: }{3:^a }|
+ {1:>>}b |
+ {2: }c |
+ {2: } |
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {4:[No Name] [+] }|
+ {2: }{3:a }|
+ {1:>>}b |
+ {2: }c |
+ {2: } |
+ {2: }{0:~ }|
+ {5:[No Name] [+] }|
+ |
+ ]])
+ end)
end)
end)
diff --git a/test/helpers.lua b/test/helpers.lua
index a774a67df3..66724f6a78 100644
--- a/test/helpers.lua
+++ b/test/helpers.lua
@@ -54,6 +54,9 @@ end
local function ok(res)
return assert.is_true(res)
end
+local function near(actual, expected, tolerance)
+ return assert.is.near(actual, expected, tolerance)
+end
local function matches(pat, actual)
if nil ~= string.match(actual, pat) then
return true
@@ -694,6 +697,7 @@ local module = {
map = map,
matches = matches,
mergedicts_copy = mergedicts_copy,
+ near = near,
neq = neq,
ok = ok,
popen_r = popen_r,