aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/buffer.c5
-rw-r--r--src/nvim/buffer_defs.h1
-rw-r--r--src/nvim/eval.c8
-rw-r--r--src/nvim/eval/funcs.c11
-rw-r--r--src/nvim/eval/userfunc.c198
-rw-r--r--src/nvim/ex_docmd.c9
-rw-r--r--src/nvim/fileio.c3
-rw-r--r--src/nvim/fold.c195
-rw-r--r--src/nvim/fold.h1
-rw-r--r--src/nvim/getchar.c7
-rw-r--r--src/nvim/lua/treesitter.c40
-rw-r--r--src/nvim/normal.c90
-rw-r--r--src/nvim/ops.c11
-rw-r--r--src/nvim/option.c8
-rw-r--r--src/nvim/option_defs.h1
-rw-r--r--src/nvim/options.lua10
-rw-r--r--src/nvim/os/lang.c16
-rw-r--r--src/nvim/quickfix.c16
-rw-r--r--src/nvim/regexp.c4
-rw-r--r--src/nvim/screen.c450
-rw-r--r--src/nvim/search.c32
-rw-r--r--src/nvim/spell.c24
-rw-r--r--src/nvim/syntax.c2
-rw-r--r--src/nvim/testdir/shared.vim14
-rw-r--r--src/nvim/testdir/test_edit.vim10
-rw-r--r--src/nvim/testdir/test_environ.vim25
-rw-r--r--src/nvim/testdir/test_filetype.vim4
-rw-r--r--src/nvim/testdir/test_functions.vim14
-rw-r--r--src/nvim/testdir/test_lambda.vim2
-rw-r--r--src/nvim/testdir/test_mapping.vim36
-rw-r--r--src/nvim/testdir/test_quickfix.vim12
-rw-r--r--src/nvim/testdir/test_search_stat.vim98
-rw-r--r--src/nvim/testdir/test_spell.vim42
-rw-r--r--src/nvim/testdir/test_syntax.vim4
-rw-r--r--src/nvim/testdir/test_timers.vim42
-rw-r--r--src/nvim/testdir/test_vimscript.vim11
36 files changed, 826 insertions, 630 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 4648631ebe..ec633dcc26 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -837,7 +837,7 @@ static void clear_wininfo(buf_T *buf)
buf->b_wininfo = wip->wi_next;
if (wip->wi_optset) {
clear_winopt(&wip->wi_opt);
- deleteFoldRecurse(&wip->wi_folds);
+ deleteFoldRecurse(buf, &wip->wi_folds);
}
xfree(wip);
}
@@ -1941,6 +1941,7 @@ void free_buf_options(buf_T *buf, int free_p_ff)
vim_regfree(buf->b_s.b_cap_prog);
buf->b_s.b_cap_prog = NULL;
clear_string_option(&buf->b_s.b_p_spl);
+ clear_string_option(&buf->b_s.b_p_spo);
clear_string_option(&buf->b_p_sua);
clear_string_option(&buf->b_p_ft);
clear_string_option(&buf->b_p_cink);
@@ -2502,7 +2503,7 @@ void buflist_setfpos(buf_T *const buf, win_T *const win,
}
if (copy_options && wip->wi_optset) {
clear_winopt(&wip->wi_opt);
- deleteFoldRecurse(&wip->wi_folds);
+ deleteFoldRecurse(buf, &wip->wi_folds);
}
}
if (lnum != 0) {
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index bd9cd2f5ec..ea968d9592 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -451,6 +451,7 @@ typedef struct {
regprog_T *b_cap_prog; // program for 'spellcapcheck'
char_u *b_p_spf; // 'spellfile'
char_u *b_p_spl; // 'spelllang'
+ char_u *b_p_spo; // 'spelloptions'
int b_cjk; // all CJK letters as OK
char_u b_syn_chartab[32]; // syntax iskeyword option
char_u *b_syn_isk; // iskeyword option
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 00542e3766..24192dfefa 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -736,7 +736,7 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv,
if (s == NULL || *s == NUL) {
return FAIL;
}
- if (call_func(s, (int)STRLEN(s), rettv, argc, argv, NULL,
+ if (call_func(s, -1, rettv, argc, argv, NULL,
0L, 0L, &dummy, true, NULL, NULL) == FAIL) {
return FAIL;
}
@@ -746,7 +746,7 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv,
if (s == NULL || *s == NUL) {
return FAIL;
}
- if (call_func(s, (int)STRLEN(s), rettv, argc, argv, NULL,
+ if (call_func(s, -1, rettv, argc, argv, NULL,
0L, 0L, &dummy, true, partial, NULL) == FAIL) {
return FAIL;
}
@@ -7270,7 +7270,7 @@ bool callback_call(Callback *const callback, const int argcount_in,
}
int dummy;
- return call_func(name, (int)STRLEN(name), rettv, argcount_in, argvars_in,
+ return call_func(name, -1, rettv, argcount_in, argvars_in,
NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy,
true, partial, NULL);
}
@@ -8492,7 +8492,7 @@ handle_subscript(
} else {
s = (char_u *)"";
}
- ret = get_func_tv(s, lua ? slen : (int)STRLEN(s), rettv, (char_u **)arg,
+ ret = get_func_tv(s, lua ? slen : -1, rettv, (char_u **)arg,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&len, evaluate, pt, selfdict);
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 3a4b4f2a50..83ad948a93 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -2584,8 +2584,6 @@ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char_u *text;
char_u buf[FOLD_TEXT_LEN];
- foldinfo_T foldinfo;
- int fold_count;
static bool entered = false;
rettv->v_type = VAR_STRING;
@@ -2599,9 +2597,10 @@ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (lnum < 0) {
lnum = 0;
}
- fold_count = foldedCount(curwin, lnum, &foldinfo);
- if (fold_count > 0) {
- text = get_foldtext(curwin, lnum, lnum + fold_count - 1, &foldinfo, buf);
+
+ foldinfo_T info = fold_info(curwin, lnum);
+ if (info.fi_lines > 0) {
+ text = get_foldtext(curwin, lnum, lnum + info.fi_lines - 1, info, buf);
if (text == buf) {
text = vim_strsave(text);
}
@@ -9175,7 +9174,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this
res = call_func((const char_u *)func_name,
- (int)STRLEN(func_name),
+ -1,
&rettv, 2, argv, NULL, 0L, 0L, &dummy, true,
partial, sortinfo->item_compare_selfdict);
tv_clear(&argv[0]);
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index 1b80b22213..e0361048bc 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -32,7 +32,11 @@
#define FC_DELETED 0x10 // :delfunction used while uf_refcount > 0
#define FC_REMOVED 0x20 // function redefined while uf_refcount > 0
#define FC_SANDBOX 0x40 // function defined in the sandbox
-#define FC_CFUNC 0x80 // C function extension
+#define FC_DEAD 0x80 // function kept only for reference to dfunc
+#define FC_EXPORT 0x100 // "export def Func()"
+#define FC_NOARGS 0x200 // no a: variables in lambda
+#define FC_VIM9 0x400 // defined in vim9 script file
+#define FC_CFUNC 0x800 // C function extension
#ifdef INCLUDE_GENERATED_DECLARATIONS
#include "eval/userfunc.c.generated.h"
@@ -246,6 +250,10 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
((char_u **)(newlines.ga_data))[newlines.ga_len++] = p;
STRCPY(p, "return ");
STRLCPY(p + 7, s, e - s + 1);
+ if (strstr((char *)p + 7, "a:") == NULL) {
+ // No a: variables are used for sure.
+ flags |= FC_NOARGS;
+ }
fp->uf_refcount = 1;
STRCPY(fp->uf_name, name);
@@ -367,7 +375,7 @@ void emsg_funcname(char *ermsg, const char_u *name)
int
get_func_tv(
const char_u *name, // name of the function
- int len, // length of "name"
+ int len, // length of "name" or -1 to use strlen()
typval_T *rettv,
char_u **arg, // argument, pointing to the '('
linenr_T firstline, // first line of range
@@ -813,17 +821,12 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
current_funccal = fc;
fc->func = fp;
fc->rettv = rettv;
- rettv->vval.v_number = 0;
- fc->linenr = 0;
- fc->returned = FALSE;
fc->level = ex_nesting_level;
// Check if this function has a breakpoint.
fc->breakpoint = dbg_find_breakpoint(false, fp->uf_name, (linenr_T)0);
fc->dbg_tick = debug_tick;
// Set up fields for closure.
- fc->fc_refcount = 0;
- fc->fc_copyID = 0;
ga_init(&fc->fc_funcs, sizeof(ufunc_T *), 1);
func_ptr_ref(fp);
@@ -853,37 +856,42 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
++selfdict->dv_refcount;
}
- /*
- * Init a: variables.
- * Set a:0 to "argcount".
- * Set a:000 to a list with room for the "..." arguments.
- */
+ // Init a: variables, unless none found (in lambda).
+ // Set a:0 to "argcount".
+ // Set a:000 to a list with room for the "..." arguments.
init_var_dict(&fc->l_avars, &fc->l_avars_var, VAR_SCOPE);
- add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], "0",
- (varnumber_T)(argcount - fp->uf_args.ga_len));
+ if ((fp->uf_flags & FC_NOARGS) == 0) {
+ add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], "0",
+ (varnumber_T)(argcount - fp->uf_args.ga_len));
+ }
fc->l_avars.dv_lock = VAR_FIXED;
- // Use "name" to avoid a warning from some compiler that checks the
- // destination size.
- v = (dictitem_T *)&fc->fixvar[fixvar_idx++];
+ if ((fp->uf_flags & FC_NOARGS) == 0) {
+ // Use "name" to avoid a warning from some compiler that checks the
+ // destination size.
+ v = (dictitem_T *)&fc->fixvar[fixvar_idx++];
#ifndef __clang_analyzer__
- name = v->di_key;
- STRCPY(name, "000");
+ name = v->di_key;
+ STRCPY(name, "000");
#endif
- v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
- tv_dict_add(&fc->l_avars, v);
- v->di_tv.v_type = VAR_LIST;
- v->di_tv.v_lock = VAR_FIXED;
- v->di_tv.vval.v_list = &fc->l_varlist;
+ v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
+ tv_dict_add(&fc->l_avars, v);
+ v->di_tv.v_type = VAR_LIST;
+ v->di_tv.v_lock = VAR_FIXED;
+ v->di_tv.vval.v_list = &fc->l_varlist;
+ }
tv_list_init_static(&fc->l_varlist);
tv_list_set_lock(&fc->l_varlist, VAR_FIXED);
// Set a:firstline to "firstline" and a:lastline to "lastline".
// Set a:name to named arguments.
// Set a:N to the "..." arguments.
- add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++],
- "firstline", (varnumber_T)firstline);
- add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++],
- "lastline", (varnumber_T)lastline);
+ // Skipped when no a: variables used (in lambda).
+ if ((fp->uf_flags & FC_NOARGS) == 0) {
+ add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++],
+ "firstline", (varnumber_T)firstline);
+ add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++],
+ "lastline", (varnumber_T)lastline);
+ }
for (int i = 0; i < argcount; i++) {
bool addlocal = false;
@@ -895,6 +903,10 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
addlocal = true;
}
} else {
+ if ((fp->uf_flags & FC_NOARGS) != 0) {
+ // Bail out if no a: arguments used (in lambda).
+ break;
+ }
// "..." argument a:1, a:2, etc.
snprintf((char *)numbuf, sizeof(numbuf), "%d", ai + 1);
name = numbuf;
@@ -1034,9 +1046,19 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
save_did_emsg = did_emsg;
did_emsg = FALSE;
- // call do_cmdline() to execute the lines
- do_cmdline(NULL, get_func_line, (void *)fc,
- DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
+ if (islambda) {
+ char_u *p = *(char_u **)fp->uf_lines.ga_data + 7;
+
+ // A Lambda always has the command "return {expr}". It is much faster
+ // to evaluate {expr} directly.
+ ex_nesting_level++;
+ eval1(&p, rettv, true);
+ ex_nesting_level--;
+ } else {
+ // call do_cmdline() to execute the lines
+ do_cmdline(NULL, get_func_line, (void *)fc,
+ DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
+ }
--RedrawingDisabled;
@@ -1291,7 +1313,7 @@ int func_call(char_u *name, typval_T *args, partial_T *partial,
tv_copy(TV_LIST_ITEM_TV(item), &argv[argc++]);
});
- r = call_func(name, (int)STRLEN(name), rettv, argc, argv, NULL,
+ r = call_func(name, -1, rettv, argc, argv, NULL,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&dummy, true, partial, selfdict);
@@ -1304,6 +1326,36 @@ func_call_skip_call:
return r;
}
+// Give an error message for the result of a function.
+// Nothing if "error" is FCERR_NONE.
+static void user_func_error(int error, const char_u *name)
+ FUNC_ATTR_NONNULL_ALL
+{
+ switch (error) {
+ case ERROR_UNKNOWN:
+ emsg_funcname(N_("E117: Unknown function: %s"), name);
+ break;
+ case ERROR_DELETED:
+ emsg_funcname(N_("E933: Function was deleted: %s"), name);
+ break;
+ case ERROR_TOOMANY:
+ emsg_funcname(_(e_toomanyarg), name);
+ break;
+ case ERROR_TOOFEW:
+ emsg_funcname(N_("E119: Not enough arguments for function: %s"),
+ name);
+ break;
+ case ERROR_SCRIPT:
+ emsg_funcname(N_("E120: Using <SID> not in a script context: %s"),
+ name);
+ break;
+ case ERROR_DICT:
+ emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"),
+ name);
+ break;
+ }
+}
+
/// Call a function with its resolved parameters
///
/// "argv_func", when not NULL, can be used to fill in arguments only when the
@@ -1316,7 +1368,7 @@ func_call_skip_call:
int
call_func(
const char_u *funcname, // name of the function
- int len, // length of "name"
+ int len, // length of "name" or -1 to use strlen()
typval_T *rettv, // [out] value goes here
int argcount_in, // number of "argvars"
typval_T *argvars_in, // vars for arguments, must have "argcount"
@@ -1333,11 +1385,11 @@ call_func(
{
int ret = FAIL;
int error = ERROR_NONE;
- ufunc_T *fp;
+ ufunc_T *fp = NULL;
char_u fname_buf[FLEN_FIXED + 1];
char_u *tofree = NULL;
- char_u *fname;
- char_u *name;
+ char_u *fname = NULL;
+ char_u *name = NULL;
int argcount = argcount_in;
typval_T *argvars = argvars_in;
dict_T *selfdict = selfdict_in;
@@ -1348,11 +1400,18 @@ call_func(
// even when call_func() returns FAIL.
rettv->v_type = VAR_UNKNOWN;
- // Make a copy of the name, if it comes from a funcref variable it could
- // be changed or deleted in the called function.
- name = vim_strnsave(funcname, len);
-
- fname = fname_trans_sid(name, fname_buf, &tofree, &error);
+ if (len <= 0) {
+ len = (int)STRLEN(funcname);
+ }
+ if (partial != NULL) {
+ fp = partial->pt_func;
+ }
+ if (fp == NULL) {
+ // Make a copy of the name, if it comes from a funcref variable it could
+ // be changed or deleted in the called function.
+ name = vim_strnsave(funcname, len);
+ fname = fname_trans_sid(name, fname_buf, &tofree, &error);
+ }
*doesrange = false;
@@ -1384,7 +1443,7 @@ call_func(
char_u *rfname = fname;
// Ignore "g:" before a function name.
- if (fname[0] == 'g' && fname[1] == ':') {
+ if (fp == NULL && fname[0] == 'g' && fname[1] == ':') {
rfname = fname + 2;
}
@@ -1397,11 +1456,9 @@ call_func(
error = ERROR_NONE;
nlua_typval_call((const char *)funcname, len, argvars, argcount, rettv);
}
- } else if (!builtin_function((const char *)rfname, -1)) {
+ } else if (fp != NULL || !builtin_function((const char *)rfname, -1)) {
// User defined function.
- if (partial != NULL && partial->pt_func != NULL) {
- fp = partial->pt_func;
- } else {
+ if (fp == NULL) {
fp = find_func(rfname);
}
@@ -1480,29 +1537,7 @@ theend:
// Report an error unless the argument evaluation or function call has been
// cancelled due to an aborting error, an interrupt, or an exception.
if (!aborting()) {
- switch (error) {
- case ERROR_UNKNOWN:
- emsg_funcname(N_("E117: Unknown function: %s"), name);
- break;
- case ERROR_DELETED:
- emsg_funcname(N_("E933: Function was deleted: %s"), name);
- break;
- case ERROR_TOOMANY:
- emsg_funcname(_(e_toomanyarg), name);
- break;
- case ERROR_TOOFEW:
- emsg_funcname(N_("E119: Not enough arguments for function: %s"),
- name);
- break;
- case ERROR_SCRIPT:
- emsg_funcname(N_("E120: Using <SID> not in a script context: %s"),
- name);
- break;
- case ERROR_DICT:
- emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"),
- name);
- break;
- }
+ user_func_error(error, (name != NULL) ? name : funcname);
}
while (argv_clear > 0) {
@@ -2853,7 +2888,7 @@ void ex_call(exarg_T *eap)
curwin->w_cursor.coladd = 0;
}
arg = startarg;
- if (get_func_tv(name, (int)STRLEN(name), &rettv, &arg,
+ if (get_func_tv(name, -1, &rettv, &arg,
eap->line1, eap->line2, &doesrange,
true, partial, fudi.fd_dict) == FAIL) {
failed = true;
@@ -3347,9 +3382,13 @@ bool set_ref_in_previous_funccal(int copyID)
{
bool abort = false;
- for (funccall_T *fc = previous_funccal; fc != NULL; fc = fc->caller) {
- abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1, NULL);
- abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1, NULL);
+ for (funccall_T *fc = previous_funccal; !abort && fc != NULL;
+ fc = fc->caller) {
+ fc->fc_copyID = copyID + 1;
+ abort = abort
+ || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1, NULL)
+ || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1, NULL)
+ || set_ref_in_list(&fc->l_varlist, copyID + 1, NULL);
}
return abort;
}
@@ -3360,9 +3399,11 @@ static bool set_ref_in_funccal(funccall_T *fc, int copyID)
if (fc->fc_copyID != copyID) {
fc->fc_copyID = copyID;
- abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL);
- abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL);
- abort = abort || set_ref_in_func(NULL, fc->func, copyID);
+ abort = abort
+ || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL)
+ || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL)
+ || set_ref_in_list(&fc->l_varlist, copyID, NULL)
+ || set_ref_in_func(NULL, fc->func, copyID);
}
return abort;
}
@@ -3372,12 +3413,13 @@ bool set_ref_in_call_stack(int copyID)
{
bool abort = false;
- for (funccall_T *fc = current_funccal; fc != NULL; fc = fc->caller) {
+ for (funccall_T *fc = current_funccal; !abort && fc != NULL;
+ fc = fc->caller) {
abort = abort || set_ref_in_funccal(fc, copyID);
}
// Also go through the funccal_stack.
- for (funccal_entry_T *entry = funccal_stack; entry != NULL;
+ for (funccal_entry_T *entry = funccal_stack; !abort && entry != NULL;
entry = entry->next) {
for (funccall_T *fc = entry->top_funccal; !abort && fc != NULL;
fc = fc->caller) {
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 60b017be28..7bb4bd32a3 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -9297,14 +9297,17 @@ static void ex_match(exarg_T *eap)
static void ex_fold(exarg_T *eap)
{
if (foldManualAllowed(true)) {
- foldCreate(curwin, eap->line1, eap->line2);
+ pos_T start = { eap->line1, 1, 0 };
+ pos_T end = { eap->line2, 1, 0 };
+ foldCreate(curwin, start, end);
}
}
static void ex_foldopen(exarg_T *eap)
{
- opFoldRange(eap->line1, eap->line2, eap->cmdidx == CMD_foldopen,
- eap->forceit, FALSE);
+ pos_T start = { eap->line1, 1, 0 };
+ pos_T end = { eap->line2, 1, 0 };
+ opFoldRange(start, end, eap->cmdidx == CMD_foldopen, eap->forceit, false);
}
static void ex_folddo(exarg_T *eap)
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 286f2b4fca..ed8b72e2be 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -210,7 +210,8 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr)
if (msg_silent != 0) {
return;
}
- add_quoted_fname((char *)IObuff, IOSIZE - 80, buf, (const char *)name);
+ add_quoted_fname((char *)IObuff, IOSIZE - 100, buf, (const char *)name);
+ // Avoid an over-long translation to cause trouble.
xstrlcat((char *)IObuff, (const char *)s, IOSIZE);
// For the first message may have to start a new line.
// For further ones overwrite the previous one, reset msg_scroll before
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 9994ad3ea8..197aedabec 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -153,14 +153,22 @@ bool hasFolding(linenr_T lnum, linenr_T *firstp, linenr_T *lastp)
return hasFoldingWin(curwin, lnum, firstp, lastp, true, NULL);
}
-/* hasFoldingWin() {{{2 */
+// hasFoldingWin() {{{2
+/// Search folds starting at lnum
+/// @param lnum first line to search
+/// @param[out] first first line of fold containing lnum
+/// @param[out] lastp last line with a fold
+/// @param cache when true: use cached values of window
+/// @param[out] infop where to store fold info
+///
+/// @return true if range contains folds
bool hasFoldingWin(
win_T *const win,
const linenr_T lnum,
linenr_T *const firstp,
linenr_T *const lastp,
- const bool cache, // when true: use cached values of window
- foldinfo_T *const infop // where to store fold info
+ const bool cache,
+ foldinfo_T *const infop
)
{
bool had_folded = false;
@@ -280,26 +288,31 @@ int foldLevel(linenr_T lnum)
// Return false if line is not folded.
bool lineFolded(win_T *const win, const linenr_T lnum)
{
- return foldedCount(win, lnum, NULL) != 0;
+ return fold_info(win, lnum).fi_lines != 0;
}
-/* foldedCount() {{{2 */
-/*
- * Count the number of lines that are folded at line number "lnum".
- * Normally "lnum" is the first line of a possible fold, and the returned
- * number is the number of lines in the fold.
- * Doesn't use caching from the displayed window.
- * Returns number of folded lines from "lnum", or 0 if line is not folded.
- * When "infop" is not NULL, fills *infop with the fold level info.
- */
-long foldedCount(win_T *win, linenr_T lnum, foldinfo_T *infop)
+/// fold_info() {{{2
+///
+/// Count the number of lines that are folded at line number "lnum".
+/// Normally "lnum" is the first line of a possible fold, and the returned
+/// number is the number of lines in the fold.
+/// Doesn't use caching from the displayed window.
+///
+/// @return with the fold level info.
+/// fi_lines = number of folded lines from "lnum",
+/// or 0 if line is not folded.
+foldinfo_T fold_info(win_T *win, linenr_T lnum)
{
+ foldinfo_T info;
linenr_T last;
- if (hasFoldingWin(win, lnum, NULL, &last, false, infop)) {
- return (long)(last - lnum + 1);
+ if (hasFoldingWin(win, lnum, NULL, &last, false, &info)) {
+ info.fi_lines = (long)(last - lnum + 1);
+ } else {
+ info.fi_lines = 0;
}
- return 0;
+
+ return info;
}
/* foldmethodIsManual() {{{2 */
@@ -356,23 +369,21 @@ int foldmethodIsDiff(win_T *wp)
return wp->w_p_fdm[0] == 'd';
}
-/* closeFold() {{{2 */
-/*
- * Close fold for current window at line "lnum".
- * Repeat "count" times.
- */
-void closeFold(linenr_T lnum, long count)
+// closeFold() {{{2
+/// Close fold for current window at line "lnum".
+/// Repeat "count" times.
+void closeFold(pos_T pos, long count)
{
- setFoldRepeat(lnum, count, FALSE);
+ setFoldRepeat(pos, count, false);
}
/* closeFoldRecurse() {{{2 */
/*
* Close fold for current window at line "lnum" recursively.
*/
-void closeFoldRecurse(linenr_T lnum)
+void closeFoldRecurse(pos_T pos)
{
- (void)setManualFold(lnum, FALSE, TRUE, NULL);
+ (void)setManualFold(pos, false, true, NULL);
}
/* opFoldRange() {{{2 */
@@ -382,28 +393,32 @@ void closeFoldRecurse(linenr_T lnum)
*/
void
opFoldRange(
- linenr_T first,
- linenr_T last,
+ pos_T firstpos,
+ pos_T lastpos,
int opening, // TRUE to open, FALSE to close
int recurse, // TRUE to do it recursively
int had_visual // TRUE when Visual selection used
)
{
- int done = DONE_NOTHING; /* avoid error messages */
+ int done = DONE_NOTHING; // avoid error messages
+ linenr_T first = firstpos.lnum;
+ linenr_T last = lastpos.lnum;
linenr_T lnum;
linenr_T lnum_next;
for (lnum = first; lnum <= last; lnum = lnum_next + 1) {
+ pos_T temp = { lnum, 0, 0 };
lnum_next = lnum;
/* Opening one level only: next fold to open is after the one going to
* be opened. */
if (opening && !recurse)
(void)hasFolding(lnum, NULL, &lnum_next);
- (void)setManualFold(lnum, opening, recurse, &done);
- /* Closing one level only: next line to close a fold is after just
- * closed fold. */
- if (!opening && !recurse)
+ (void)setManualFold(temp, opening, recurse, &done);
+ // Closing one level only: next line to close a fold is after just
+ // closed fold.
+ if (!opening && !recurse) {
(void)hasFolding(lnum, NULL, &lnum_next);
+ }
}
if (done == DONE_NOTHING)
EMSG(_(e_nofold));
@@ -417,18 +432,18 @@ opFoldRange(
* Open fold for current window at line "lnum".
* Repeat "count" times.
*/
-void openFold(linenr_T lnum, long count)
+void openFold(pos_T pos, long count)
{
- setFoldRepeat(lnum, count, TRUE);
+ setFoldRepeat(pos, count, true);
}
/* openFoldRecurse() {{{2 */
/*
* Open fold for current window at line "lnum" recursively.
*/
-void openFoldRecurse(linenr_T lnum)
+void openFoldRecurse(pos_T pos)
{
- (void)setManualFold(lnum, TRUE, TRUE, NULL);
+ (void)setManualFold(pos, true, true, NULL);
}
/* foldOpenCursor() {{{2 */
@@ -443,9 +458,10 @@ void foldOpenCursor(void)
if (hasAnyFolding(curwin))
for (;; ) {
done = DONE_NOTHING;
- (void)setManualFold(curwin->w_cursor.lnum, TRUE, FALSE, &done);
- if (!(done & DONE_ACTION))
+ (void)setManualFold(curwin->w_cursor, true, false, &done);
+ if (!(done & DONE_ACTION)) {
break;
+ }
}
}
@@ -542,21 +558,21 @@ int foldManualAllowed(int create)
// foldCreate() {{{2
/// Create a fold from line "start" to line "end" (inclusive) in the current
/// window.
-void foldCreate(win_T *wp, linenr_T start, linenr_T end)
+void foldCreate(win_T *wp, pos_T start, pos_T end)
{
fold_T *fp;
garray_T *gap;
garray_T fold_ga;
- int i, j;
+ int i;
int cont;
int use_level = FALSE;
int closed = FALSE;
int level = 0;
- linenr_T start_rel = start;
- linenr_T end_rel = end;
+ pos_T start_rel = start;
+ pos_T end_rel = end;
- if (start > end) {
- /* reverse the range */
+ if (start.lnum > end.lnum) {
+ // reverse the range
end = start_rel;
start = end_rel;
start_rel = start;
@@ -577,14 +593,14 @@ void foldCreate(win_T *wp, linenr_T start, linenr_T end)
i = 0;
} else {
for (;;) {
- if (!foldFind(gap, start_rel, &fp)) {
+ if (!foldFind(gap, start_rel.lnum, &fp)) {
break;
}
- if (fp->fd_top + fp->fd_len > end_rel) {
+ if (fp->fd_top + fp->fd_len > end_rel.lnum) {
// New fold is completely inside this fold: Go one level deeper.
gap = &fp->fd_nested;
- start_rel -= fp->fd_top;
- end_rel -= fp->fd_top;
+ start_rel.lnum -= fp->fd_top;
+ end_rel.lnum -= fp->fd_top;
if (use_level || fp->fd_flags == FD_LEVEL) {
use_level = true;
if (level >= wp->w_p_fdl) {
@@ -608,30 +624,35 @@ void foldCreate(win_T *wp, linenr_T start, linenr_T end)
fp = (fold_T *)gap->ga_data + i;
ga_init(&fold_ga, (int)sizeof(fold_T), 10);
- /* Count number of folds that will be contained in the new fold. */
- for (cont = 0; i + cont < gap->ga_len; ++cont)
- if (fp[cont].fd_top > end_rel)
+ // Count number of folds that will be contained in the new fold.
+ for (cont = 0; i + cont < gap->ga_len; cont++) {
+ if (fp[cont].fd_top > end_rel.lnum) {
break;
+ }
+ }
if (cont > 0) {
ga_grow(&fold_ga, cont);
/* If the first fold starts before the new fold, let the new fold
* start there. Otherwise the existing fold would change. */
- if (start_rel > fp->fd_top)
- start_rel = fp->fd_top;
-
- /* When last contained fold isn't completely contained, adjust end
- * of new fold. */
- if (end_rel < fp[cont - 1].fd_top + fp[cont - 1].fd_len - 1)
- end_rel = fp[cont - 1].fd_top + fp[cont - 1].fd_len - 1;
- /* Move contained folds to inside new fold. */
+ if (start_rel.lnum > fp->fd_top) {
+ start_rel.lnum = fp->fd_top;
+ }
+
+ // When last contained fold isn't completely contained, adjust end
+ // of new fold.
+ if (end_rel.lnum < fp[cont - 1].fd_top + fp[cont - 1].fd_len - 1) {
+ end_rel.lnum = fp[cont - 1].fd_top + fp[cont - 1].fd_len - 1;
+ }
+ // Move contained folds to inside new fold
memmove(fold_ga.ga_data, fp, sizeof(fold_T) * (size_t)cont);
fold_ga.ga_len += cont;
i += cont;
/* Adjust line numbers in contained folds to be relative to the
* new fold. */
- for (j = 0; j < cont; ++j)
- ((fold_T *)fold_ga.ga_data)[j].fd_top -= start_rel;
+ for (int j = 0; j < cont; j++) {
+ ((fold_T *)fold_ga.ga_data)[j].fd_top -= start_rel.lnum;
+ }
}
/* Move remaining entries to after the new fold. */
if (i < gap->ga_len)
@@ -641,8 +662,8 @@ void foldCreate(win_T *wp, linenr_T start, linenr_T end)
/* insert new fold */
fp->fd_nested = fold_ga;
- fp->fd_top = start_rel;
- fp->fd_len = end_rel - start_rel + 1;
+ fp->fd_top = start_rel.lnum;
+ fp->fd_len = end_rel.lnum - start_rel.lnum + 1;
/* We want the new fold to be closed. If it would remain open because
* of using 'foldlevel', need to adjust fd_flags of containing folds.
@@ -771,7 +792,7 @@ void deleteFold(
*/
void clearFolding(win_T *win)
{
- deleteFoldRecurse(&win->w_folds);
+ deleteFoldRecurse(win->w_buffer, &win->w_folds);
win->w_foldinvalid = false;
}
@@ -1143,14 +1164,14 @@ static void checkupdate(win_T *wp)
* Open or close fold for current window at line "lnum".
* Repeat "count" times.
*/
-static void setFoldRepeat(linenr_T lnum, long count, int do_open)
+static void setFoldRepeat(pos_T pos, long count, int do_open)
{
int done;
long n;
for (n = 0; n < count; ++n) {
done = DONE_NOTHING;
- (void)setManualFold(lnum, do_open, FALSE, &done);
+ (void)setManualFold(pos, do_open, false, &done);
if (!(done & DONE_ACTION)) {
/* Only give an error message when no fold could be opened. */
if (n == 0 && !(done & DONE_FOLD))
@@ -1167,12 +1188,13 @@ static void setFoldRepeat(linenr_T lnum, long count, int do_open)
*/
static linenr_T
setManualFold(
- linenr_T lnum,
+ pos_T pos,
int opening, // TRUE when opening, FALSE when closing
int recurse, // TRUE when closing/opening recursive
int *donep
)
{
+ linenr_T lnum = pos.lnum;
if (foldmethodIsDiff(curwin) && curwin->w_p_scb) {
linenr_T dlnum;
@@ -1326,7 +1348,7 @@ static void deleteFoldEntry(win_T *const wp, garray_T *const gap, const int idx,
fold_T *fp = (fold_T *)gap->ga_data + idx;
if (recursive || GA_EMPTY(&fp->fd_nested)) {
// recursively delete the contained folds
- deleteFoldRecurse(&fp->fd_nested);
+ deleteFoldRecurse(wp->w_buffer, &fp->fd_nested);
gap->ga_len--;
if (idx < gap->ga_len) {
memmove(fp, fp + 1, sizeof(*fp) * (size_t)(gap->ga_len - idx));
@@ -1368,9 +1390,9 @@ static void deleteFoldEntry(win_T *const wp, garray_T *const gap, const int idx,
/*
* Delete nested folds in a fold.
*/
-void deleteFoldRecurse(garray_T *gap)
+void deleteFoldRecurse(buf_T *bp, garray_T *gap)
{
-# define DELETE_FOLD_NESTED(fd) deleteFoldRecurse(&((fd)->fd_nested))
+# define DELETE_FOLD_NESTED(fd) deleteFoldRecurse(bp, &((fd)->fd_nested))
GA_DEEP_CLEAR(gap, fold_T, DELETE_FOLD_NESTED);
}
@@ -1610,7 +1632,7 @@ static void setSmallMaybe(garray_T *gap)
* Create a fold from line "start" to line "end" (inclusive) in the current
* window by adding markers.
*/
-static void foldCreateMarkers(win_T *wp, linenr_T start, linenr_T end)
+static void foldCreateMarkers(win_T *wp, pos_T start, pos_T end)
{
buf_T *buf = wp->w_buffer;
if (!MODIFIABLE(buf)) {
@@ -1625,13 +1647,13 @@ static void foldCreateMarkers(win_T *wp, linenr_T start, linenr_T end)
/* Update both changes here, to avoid all folds after the start are
* changed when the start marker is inserted and the end isn't. */
// TODO(teto): pass the buffer
- changed_lines(start, (colnr_T)0, end, 0L, false);
+ changed_lines(start.lnum, (colnr_T)0, end.lnum, 0L, false);
// Note: foldAddMarker() may not actually change start and/or end if
// u_save() is unable to save the buffer line, but we send the
// nvim_buf_lines_event anyway since it won't do any harm.
- int64_t num_changed = 1 + end - start;
- buf_updates_send_changes(buf, start, num_changed, num_changed, true);
+ int64_t num_changed = 1 + end.lnum - start.lnum;
+ buf_updates_send_changes(buf, start.lnum, num_changed, num_changed, true);
}
/* foldAddMarker() {{{2 */
@@ -1639,13 +1661,14 @@ static void foldCreateMarkers(win_T *wp, linenr_T start, linenr_T end)
* Add "marker[markerlen]" in 'commentstring' to line "lnum".
*/
static void foldAddMarker(
- buf_T *buf, linenr_T lnum, const char_u *marker, size_t markerlen)
+ buf_T *buf, pos_T pos, const char_u *marker, size_t markerlen)
{
char_u *cms = buf->b_p_cms;
char_u *line;
char_u *newline;
char_u *p = (char_u *)strstr((char *)buf->b_p_cms, "%s");
bool line_is_comment = false;
+ linenr_T lnum = pos.lnum;
// Allocate a new line: old-line + 'cms'-start + marker + 'cms'-end
line = ml_get_buf(buf, lnum, false);
@@ -1751,11 +1774,16 @@ static void foldDelMarker(
}
// get_foldtext() {{{2
-/// Return the text for a closed fold at line "lnum", with last line "lnume".
-/// When 'foldtext' isn't set puts the result in "buf[FOLD_TEXT_LEN]".
+/// Generates text to display
+///
+/// @param buf allocated memory of length FOLD_TEXT_LEN. Used when 'foldtext'
+/// isn't set puts the result in "buf[FOLD_TEXT_LEN]".
+/// @param at line "lnum", with last line "lnume".
+/// @return the text for a closed fold
+///
/// Otherwise the result is in allocated memory.
char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume,
- foldinfo_T *foldinfo, char_u *buf)
+ foldinfo_T foldinfo, char_u *buf)
FUNC_ATTR_NONNULL_ARG(1)
{
char_u *text = NULL;
@@ -1783,11 +1811,12 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume,
set_vim_var_nr(VV_FOLDSTART, (varnumber_T) lnum);
set_vim_var_nr(VV_FOLDEND, (varnumber_T) lnume);
- /* Set "v:folddashes" to a string of "level" dashes. */
- /* Set "v:foldlevel" to "level". */
- level = foldinfo->fi_level;
- if (level > (int)sizeof(dashes) - 1)
+ // Set "v:folddashes" to a string of "level" dashes.
+ // Set "v:foldlevel" to "level".
+ level = foldinfo.fi_level;
+ if (level > (int)sizeof(dashes) - 1) {
level = (int)sizeof(dashes) - 1;
+ }
memset(dashes, '-', (size_t)level);
dashes[level] = NUL;
set_vim_var_string(VV_FOLDDASHES, dashes, -1);
diff --git a/src/nvim/fold.h b/src/nvim/fold.h
index f35b328fb1..95c4b0c1dc 100644
--- a/src/nvim/fold.h
+++ b/src/nvim/fold.h
@@ -18,6 +18,7 @@ typedef struct foldinfo {
other fields are invalid */
int fi_low_level; /* lowest fold level that starts in the same
line */
+ long fi_lines;
} foldinfo_T;
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index c35398cd8d..ecb3931b82 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -2044,14 +2044,19 @@ static int vgetorpeek(bool advance)
*/
if (mp->m_expr) {
int save_vgetc_busy = vgetc_busy;
+ const bool save_may_garbage_collect = may_garbage_collect;
vgetc_busy = 0;
+ may_garbage_collect = false;
+
save_m_keys = vim_strsave(mp->m_keys);
save_m_str = vim_strsave(mp->m_str);
s = eval_map_expr(save_m_str, NUL);
vgetc_busy = save_vgetc_busy;
- } else
+ may_garbage_collect = save_may_garbage_collect;
+ } else {
s = mp->m_str;
+ }
/*
* Insert the 'to' part in the typebuf.tb_buf.
diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c
index 8be4b6f376..44e743e911 100644
--- a/src/nvim/lua/treesitter.c
+++ b/src/nvim/lua/treesitter.c
@@ -39,7 +39,7 @@ typedef struct {
static struct luaL_Reg parser_meta[] = {
{ "__gc", parser_gc },
{ "__tostring", parser_tostring },
- { "parse_buf", parser_parse_buf },
+ { "parse", parser_parse },
{ "edit", parser_edit },
{ "tree", parser_tree },
{ "set_included_ranges", parser_set_ranges },
@@ -306,22 +306,44 @@ static const char *input_cb(void *payload, uint32_t byte_index,
#undef BUFSIZE
}
-static int parser_parse_buf(lua_State *L)
+static int parser_parse(lua_State *L)
{
TSLua_parser *p = parser_check(L);
if (!p) {
return 0;
}
- long bufnr = lua_tointeger(L, 2);
- buf_T *buf = handle_get_buffer(bufnr);
+ TSTree *new_tree;
+ size_t len;
+ const char *str;
+ long bufnr;
+ buf_T *buf;
+ TSInput input;
+
+ // This switch is necessary because of the behavior of lua_isstring, that
+ // consider numbers as strings...
+ switch (lua_type(L, 2)) {
+ case LUA_TSTRING:
+ str = lua_tolstring(L, 2, &len);
+ new_tree = ts_parser_parse_string(p->parser, p->tree, str, len);
+ break;
+
+ case LUA_TNUMBER:
+ bufnr = lua_tointeger(L, 2);
+ buf = handle_get_buffer(bufnr);
+
+ if (!buf) {
+ return luaL_error(L, "invalid buffer handle: %d", bufnr);
+ }
- if (!buf) {
- return luaL_error(L, "invalid buffer handle: %d", bufnr);
- }
+ input = (TSInput){ (void *)buf, input_cb, TSInputEncodingUTF8 };
+ new_tree = ts_parser_parse(p->parser, p->tree, input);
- TSInput input = { (void *)buf, input_cb, TSInputEncodingUTF8 };
- TSTree *new_tree = ts_parser_parse(p->parser, p->tree, input);
+ break;
+
+ default:
+ return luaL_error(L, "invalid argument to parser:parse()");
+ }
uint32_t n_ranges = 0;
TSRange *changed = p->tree ? ts_tree_get_changed_ranges(p->tree, new_tree,
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 968cfde388..a51aa0dc07 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -1977,20 +1977,20 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
case OP_FOLD:
VIsual_reselect = false; // don't reselect now
- foldCreate(curwin, oap->start.lnum, oap->end.lnum);
+ foldCreate(curwin, oap->start, oap->end);
break;
case OP_FOLDOPEN:
case OP_FOLDOPENREC:
case OP_FOLDCLOSE:
case OP_FOLDCLOSEREC:
- VIsual_reselect = false; /* don't reselect now */
- opFoldRange(oap->start.lnum, oap->end.lnum,
- oap->op_type == OP_FOLDOPEN
- || oap->op_type == OP_FOLDOPENREC,
- oap->op_type == OP_FOLDOPENREC
- || oap->op_type == OP_FOLDCLOSEREC,
- oap->is_VIsual);
+ VIsual_reselect = false; // don't reselect now
+ opFoldRange(oap->start, oap->end,
+ oap->op_type == OP_FOLDOPEN
+ || oap->op_type == OP_FOLDOPENREC,
+ oap->op_type == OP_FOLDOPENREC
+ || oap->op_type == OP_FOLDCLOSEREC,
+ oap->is_VIsual);
break;
case OP_FOLDDEL:
@@ -2483,7 +2483,7 @@ do_mouse (
typval_T rettv;
int doesrange;
(void)call_func((char_u *)tab_page_click_defs[mouse_col].func,
- (int)strlen(tab_page_click_defs[mouse_col].func),
+ -1,
&rettv, ARRAY_SIZE(argv), argv, NULL,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&doesrange, true, NULL, NULL);
@@ -2590,14 +2590,16 @@ do_mouse (
&& !is_drag
&& (jump_flags & (MOUSE_FOLD_CLOSE | MOUSE_FOLD_OPEN))
&& which_button == MOUSE_LEFT) {
- /* open or close a fold at this line */
- if (jump_flags & MOUSE_FOLD_OPEN)
- openFold(curwin->w_cursor.lnum, 1L);
- else
- closeFold(curwin->w_cursor.lnum, 1L);
- /* don't move the cursor if still in the same window */
- if (curwin == old_curwin)
+ // open or close a fold at this line
+ if (jump_flags & MOUSE_FOLD_OPEN) {
+ openFold(curwin->w_cursor, 1L);
+ } else {
+ closeFold(curwin->w_cursor, 1L);
+ }
+ // don't move the cursor if still in the same window
+ if (curwin == old_curwin) {
curwin->w_cursor = save_cursor;
+ }
}
@@ -4393,51 +4395,55 @@ dozet:
case 'i': curwin->w_p_fen = !curwin->w_p_fen;
break;
- /* "za": open closed fold or close open fold at cursor */
- case 'a': if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
- openFold(curwin->w_cursor.lnum, cap->count1);
- else {
- closeFold(curwin->w_cursor.lnum, cap->count1);
+ // "za": open closed fold or close open fold at cursor
+ case 'a': if (hasFolding(curwin->w_cursor.lnum, NULL, NULL)) {
+ openFold(curwin->w_cursor, cap->count1);
+ } else {
+ closeFold(curwin->w_cursor, cap->count1);
curwin->w_p_fen = true;
}
break;
- /* "zA": open fold at cursor recursively */
- case 'A': if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
- openFoldRecurse(curwin->w_cursor.lnum);
- else {
- closeFoldRecurse(curwin->w_cursor.lnum);
+ // "zA": open fold at cursor recursively
+ case 'A': if (hasFolding(curwin->w_cursor.lnum, NULL, NULL)) {
+ openFoldRecurse(curwin->w_cursor);
+ } else {
+ closeFoldRecurse(curwin->w_cursor);
curwin->w_p_fen = true;
}
break;
- /* "zo": open fold at cursor or Visual area */
- case 'o': if (VIsual_active)
+ // "zo": open fold at cursor or Visual area
+ case 'o': if (VIsual_active) {
nv_operator(cap);
- else
- openFold(curwin->w_cursor.lnum, cap->count1);
+ } else {
+ openFold(curwin->w_cursor, cap->count1);
+ }
break;
- /* "zO": open fold recursively */
- case 'O': if (VIsual_active)
+ // "zO": open fold recursively
+ case 'O': if (VIsual_active) {
nv_operator(cap);
- else
- openFoldRecurse(curwin->w_cursor.lnum);
+ } else {
+ openFoldRecurse(curwin->w_cursor);
+ }
break;
- /* "zc": close fold at cursor or Visual area */
- case 'c': if (VIsual_active)
+ // "zc": close fold at cursor or Visual area
+ case 'c': if (VIsual_active) {
nv_operator(cap);
- else
- closeFold(curwin->w_cursor.lnum, cap->count1);
+ } else {
+ closeFold(curwin->w_cursor, cap->count1);
+ }
curwin->w_p_fen = true;
break;
- /* "zC": close fold recursively */
- case 'C': if (VIsual_active)
+ // "zC": close fold recursively
+ case 'C': if (VIsual_active) {
nv_operator(cap);
- else
- closeFoldRecurse(curwin->w_cursor.lnum);
+ } else {
+ closeFoldRecurse(curwin->w_cursor);
+ }
curwin->w_p_fen = true;
break;
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 6209dd6492..8329daf5f1 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -119,7 +119,7 @@ static char opchars[][3] =
{ 'r', NUL, OPF_CHANGE }, // OP_REPLACE
{ 'I', NUL, OPF_CHANGE }, // OP_INSERT
{ 'A', NUL, OPF_CHANGE }, // OP_APPEND
- { 'z', 'f', OPF_LINES }, // OP_FOLD
+ { 'z', 'f', 0 }, // OP_FOLD
{ 'z', 'o', OPF_LINES }, // OP_FOLDOPEN
{ 'z', 'O', OPF_LINES }, // OP_FOLDOPENREC
{ 'z', 'c', OPF_LINES }, // OP_FOLDCLOSE
@@ -3109,6 +3109,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
for (i = 0; i < y_size; i++) {
int spaces;
char shortline;
+ // can just be 0 or 1, needed for blockwise paste beyond the current
+ // buffer end
+ int lines_appended = 0;
bd.startspaces = 0;
bd.endspaces = 0;
@@ -3122,6 +3125,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
break;
}
nr_lines++;
+ lines_appended = 1;
}
/* get the old line and advance to the position to insert at */
oldp = get_cursor_line_ptr();
@@ -3194,14 +3198,15 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
memmove(ptr, oldp + bd.textcol + delcount, (size_t)columns);
ml_replace(curwin->w_cursor.lnum, newp, false);
extmark_splice_cols(curbuf, (int)curwin->w_cursor.lnum-1, bd.textcol,
- delcount, (int)totlen, kExtmarkUndo);
+ delcount, (int)totlen + lines_appended, kExtmarkUndo);
++curwin->w_cursor.lnum;
if (i == 0)
curwin->w_cursor.col += bd.startspaces;
}
- changed_lines(lnum, 0, curwin->w_cursor.lnum, nr_lines, true);
+ changed_lines(lnum, 0, curbuf->b_op_start.lnum + (linenr_T)y_size
+ - (linenr_T)nr_lines , nr_lines, true);
/* Set '[ mark. */
curbuf->b_op_start = curwin->w_cursor;
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 8d74cead8d..6ae9378236 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -174,6 +174,7 @@ static char_u *p_syn;
static char_u *p_spc;
static char_u *p_spf;
static char_u *p_spl;
+static char_u *p_spo;
static long p_ts;
static long p_tw;
static int p_udf;
@@ -2285,6 +2286,7 @@ void check_buf_options(buf_T *buf)
check_string_option(&buf->b_s.b_p_spc);
check_string_option(&buf->b_s.b_p_spf);
check_string_option(&buf->b_s.b_p_spl);
+ check_string_option(&buf->b_s.b_p_spo);
check_string_option(&buf->b_p_sua);
check_string_option(&buf->b_p_cink);
check_string_option(&buf->b_p_cino);
@@ -3090,6 +3092,10 @@ ambw_end:
} else if (varp == &(curwin->w_s->b_p_spc)) {
// When 'spellcapcheck' is set compile the regexp program.
errmsg = compile_cap_prog(curwin->w_s);
+ } else if (varp == &(curwin->w_s->b_p_spo)) { // 'spelloptions'
+ if (**varp != NUL && STRCMP("camel", *varp) != 0) {
+ errmsg = e_invarg;
+ }
} else if (varp == &p_sps) { // 'spellsuggest'
if (spell_check_sps() != OK) {
errmsg = e_invarg;
@@ -5896,6 +5902,7 @@ static char_u *get_varp(vimoption_T *p)
case PV_SPC: return (char_u *)&(curwin->w_s->b_p_spc);
case PV_SPF: return (char_u *)&(curwin->w_s->b_p_spf);
case PV_SPL: return (char_u *)&(curwin->w_s->b_p_spl);
+ case PV_SPO: return (char_u *)&(curwin->w_s->b_p_spo);
case PV_SW: return (char_u *)&(curbuf->b_p_sw);
case PV_TFU: return (char_u *)&(curbuf->b_p_tfu);
case PV_TS: return (char_u *)&(curbuf->b_p_ts);
@@ -6175,6 +6182,7 @@ void buf_copy_options(buf_T *buf, int flags)
(void)compile_cap_prog(&buf->b_s);
buf->b_s.b_p_spf = vim_strsave(p_spf);
buf->b_s.b_p_spl = vim_strsave(p_spl);
+ buf->b_s.b_p_spo = vim_strsave(p_spo);
buf->b_p_inde = vim_strsave(p_inde);
buf->b_p_indk = vim_strsave(p_indk);
buf->b_p_fp = empty_option;
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 02fa7ac216..a09811c8fb 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -787,6 +787,7 @@ enum {
, BV_SPC
, BV_SPF
, BV_SPL
+ , BV_SPO
, BV_STS
, BV_SUA
, BV_SW
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index f1221a52a2..02df0ab276 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -2320,6 +2320,16 @@ return {
defaults={if_true={vi="best"}}
},
{
+ full_name='spelloptions', abbreviation='spo',
+ type='string', list='onecomma', scope={'buffer'},
+ deny_duplicates=true,
+ secure=true,
+ vi_def=true,
+ expand=true,
+ varname='p_spo',
+ defaults={if_true={vi="", vim=""}}
+ },
+ {
full_name='splitbelow', abbreviation='sb',
type='bool', scope={'global'},
vi_def=true,
diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c
index fe2d7986bf..603191a0ff 100644
--- a/src/nvim/os/lang.c
+++ b/src/nvim/os/lang.c
@@ -43,14 +43,20 @@ void lang_init(void)
}
}
+ char buf[50] = { 0 };
+ bool set_lang;
if (lang_region) {
- os_setenv("LANG", lang_region, true);
+ set_lang = true;
+ xstrlcpy(buf, lang_region, sizeof(buf));
} else {
- char buf[20] = { 0 };
- if (CFStringGetCString(cf_lang_region, buf, 20,
- kCFStringEncodingUTF8)) {
- os_setenv("LANG", buf, true);
+ set_lang = CFStringGetCString(cf_lang_region, buf, 40,
+ kCFStringEncodingUTF8);
+ }
+ if (set_lang) {
+ if (strcasestr(buf, "utf-8") == NULL) {
+ xstrlcat(buf, ".UTF-8", sizeof(buf));
}
+ os_setenv("LANG", buf, true);
}
CFRelease(cf_lang_region);
# ifdef HAVE_LOCALE_H
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 5754950745..0c9902aaec 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -3752,7 +3752,7 @@ static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last)
// Add an error line to the quickfix buffer.
static int qf_buf_add_line(buf_T *buf, linenr_T lnum, const qfline_T *qfp,
- char_u *dirname)
+ char_u *dirname, bool first_bufline)
FUNC_ATTR_NONNULL_ALL
{
int len;
@@ -3767,9 +3767,12 @@ static int qf_buf_add_line(buf_T *buf, linenr_T lnum, const qfline_T *qfp,
if (qfp->qf_type == 1) { // :helpgrep
STRLCPY(IObuff, path_tail(errbuf->b_fname), IOSIZE - 1);
} else {
- // shorten the file name if not done already
- if (errbuf->b_sfname == NULL
- || path_is_absolute(errbuf->b_sfname)) {
+ // Shorten the file name if not done already.
+ // For optimization, do this only for the first entry in a
+ // buffer.
+ if (first_bufline
+ && (errbuf->b_sfname == NULL
+ || path_is_absolute(errbuf->b_sfname))) {
if (*dirname == NUL) {
os_dirname(dirname, MAXPATHL);
}
@@ -3847,6 +3850,7 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last)
// Check if there is anything to display
if (qfl != NULL) {
char_u dirname[MAXPATHL];
+ int prev_bufnr = -1;
*dirname = NUL;
@@ -3859,9 +3863,11 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last)
lnum = buf->b_ml.ml_line_count;
}
while (lnum < qfl->qf_count) {
- if (qf_buf_add_line(buf, lnum, qfp, dirname) == FAIL) {
+ if (qf_buf_add_line(buf, lnum, qfp, dirname,
+ prev_bufnr != qfp->qf_fnum) == FAIL) {
break;
}
+ prev_bufnr = qfp->qf_fnum;
lnum++;
qfp = qfp->qf_next;
if (qfp == NULL) {
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index a570328499..6316129c6a 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -6708,14 +6708,14 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
argv[0].vval.v_list = &matchList.sl_list;
if (expr->v_type == VAR_FUNC) {
s = expr->vval.v_string;
- call_func(s, (int)STRLEN(s), &rettv, 1, argv,
+ call_func(s, -1, &rettv, 1, argv,
fill_submatch_list, 0L, 0L, &dummy,
true, NULL, NULL);
} else if (expr->v_type == VAR_PARTIAL) {
partial_T *partial = expr->vval.v_partial;
s = partial_name(partial);
- call_func(s, (int)STRLEN(s), &rettv, 1, argv,
+ call_func(s, -1, &rettv, 1, argv,
fill_submatch_list, 0L, 0L, &dummy,
true, partial, NULL);
}
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 5ac5306dd9..3503348049 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -133,8 +133,6 @@ static sattr_T *linebuf_attr = NULL;
static match_T search_hl; /* used for 'hlsearch' highlight matching */
-static foldinfo_T win_foldinfo; /* info for 'foldcolumn' */
-
StlClickDefinition *tab_page_click_defs = NULL;
long tab_page_click_defs_size = 0;
@@ -702,7 +700,6 @@ static void win_update(win_T *wp)
long j;
static int recursive = FALSE; /* being called recursively */
int old_botline = wp->w_botline;
- long fold_count;
// Remember what happened to the previous line.
#define DID_NONE 1 // didn't update a line
#define DID_LINE 2 // updated a normal line
@@ -713,6 +710,7 @@ static void win_update(win_T *wp)
linenr_T mod_bot = 0;
int save_got_int;
+
// If we can compute a change in the automatic sizing of the sign column
// under 'signcolumn=auto:X' and signs currently placed in the buffer, better
// figuring it out here so we can redraw the entire screen for it.
@@ -1229,7 +1227,6 @@ static void win_update(win_T *wp)
// Set the time limit to 'redrawtime'.
proftime_T syntax_tm = profile_setlimit(p_rdt);
syn_set_timeout(&syntax_tm);
- win_foldinfo.fi_level = 0;
/*
* Update all the window rows.
@@ -1459,24 +1456,19 @@ static void win_update(win_T *wp)
* Otherwise, display normally (can be several display lines when
* 'wrap' is on).
*/
- fold_count = foldedCount(wp, lnum, &win_foldinfo);
- if (fold_count != 0) {
- fold_line(wp, fold_count, &win_foldinfo, lnum, row);
- ++row;
- --fold_count;
- wp->w_lines[idx].wl_folded = TRUE;
- wp->w_lines[idx].wl_lastlnum = lnum + fold_count;
- did_update = DID_FOLD;
- } else if (idx < wp->w_lines_valid
- && wp->w_lines[idx].wl_valid
- && wp->w_lines[idx].wl_lnum == lnum
- && lnum > wp->w_topline
- && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE))
- && srow + wp->w_lines[idx].wl_size > wp->w_grid.Rows
- && diff_check_fill(wp, lnum) == 0
- ) {
- /* This line is not going to fit. Don't draw anything here,
- * will draw "@ " lines below. */
+ foldinfo_T foldinfo = fold_info(wp, lnum);
+
+ if (foldinfo.fi_lines == 0
+ && idx < wp->w_lines_valid
+ && wp->w_lines[idx].wl_valid
+ && wp->w_lines[idx].wl_lnum == lnum
+ && lnum > wp->w_topline
+ && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE))
+ && srow + wp->w_lines[idx].wl_size > wp->w_grid.Rows
+ && diff_check_fill(wp, lnum) == 0
+ ) {
+ // This line is not going to fit. Don't draw anything here,
+ // will draw "@ " lines below.
row = wp->w_grid.Rows + 1;
} else {
prepare_search_hl(wp, lnum);
@@ -1485,14 +1477,21 @@ static void win_update(win_T *wp)
&& syntax_present(wp))
syntax_end_parsing(syntax_last_parsed + 1);
- /*
- * Display one line.
- */
- row = win_line(wp, lnum, srow, wp->w_grid.Rows, mod_top == 0, false);
+ // Display one line
+ row = win_line(wp, lnum, srow,
+ foldinfo.fi_lines ? srow : wp->w_grid.Rows,
+ mod_top == 0, false, foldinfo);
- wp->w_lines[idx].wl_folded = FALSE;
+ wp->w_lines[idx].wl_folded = foldinfo.fi_lines != 0;
wp->w_lines[idx].wl_lastlnum = lnum;
did_update = DID_LINE;
+
+ if (foldinfo.fi_lines > 0) {
+ did_update = DID_FOLD;
+ foldinfo.fi_lines--;
+ wp->w_lines[idx].wl_lastlnum = lnum + foldinfo.fi_lines;
+ }
+
syntax_last_parsed = lnum;
}
@@ -1507,20 +1506,17 @@ static void win_update(win_T *wp)
idx++;
break;
}
- if (dollar_vcol == -1)
+ if (dollar_vcol == -1) {
wp->w_lines[idx].wl_size = row - srow;
- ++idx;
- lnum += fold_count + 1;
+ }
+ idx++;
+ lnum += foldinfo.fi_lines + 1;
} else {
if (wp->w_p_rnu) {
// 'relativenumber' set: The text doesn't need to be drawn, but
// the number column nearly always does.
- fold_count = foldedCount(wp, lnum, &win_foldinfo);
- if (fold_count != 0) {
- fold_line(wp, fold_count, &win_foldinfo, lnum, row);
- } else {
- (void)win_line(wp, lnum, srow, wp->w_grid.Rows, true, true);
- }
+ foldinfo_T info = fold_info(wp, lnum);
+ (void)win_line(wp, lnum, srow, wp->w_grid.Rows, true, true, info);
}
// This line does not need to be drawn, advance to the next one.
@@ -1752,31 +1748,6 @@ static int advance_color_col(int vcol, int **color_cols)
return **color_cols >= 0;
}
-// Returns the next grid column.
-static int text_to_screenline(win_T *wp, char_u *text, int col, int off)
- FUNC_ATTR_NONNULL_ALL
-{
- int idx = wp->w_p_rl ? off : off + col;
- LineState s = LINE_STATE(text);
-
- while (*s.p != NUL) {
- // TODO(bfredl): cargo-culted from the old Vim code:
- // if(col + cells > wp->w_width - (wp->w_p_rl ? col : 0)) { break; }
- // This is obvious wrong. If Vim ever fixes this, solve for "cells" again
- // in the correct condition.
- const int maxcells = wp->w_grid.Columns - col - (wp->w_p_rl ? col : 0);
- const int cells = line_putchar(&s, &linebuf_char[idx], maxcells,
- wp->w_p_rl);
- if (cells == -1) {
- break;
- }
- col += cells;
- idx += cells;
- }
-
- return col;
-}
-
// Compute the width of the foldcolumn. Based on 'foldcolumn' and how much
// space is available for window "wp", minus "col".
static int compute_foldcolumn(win_T *wp, int col)
@@ -1841,271 +1812,6 @@ static int line_putchar(LineState *s, schar_T *dest, int maxcells, bool rl)
return cells;
}
-/*
- * Display one folded line.
- */
-static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T lnum, int row)
-{
- char_u buf[FOLD_TEXT_LEN];
- pos_T *top, *bot;
- linenr_T lnume = lnum + fold_count - 1;
- int len;
- char_u *text;
- int fdc;
- int col;
- int txtcol;
- int off;
-
- /* Build the fold line:
- * 1. Add the cmdwin_type for the command-line window
- * 2. Add the 'foldcolumn'
- * 3. Add the 'number' or 'relativenumber' column
- * 4. Compose the text
- * 5. Add the text
- * 6. set highlighting for the Visual area an other text
- */
- col = 0;
- off = 0;
-
- /*
- * 1. Add the cmdwin_type for the command-line window
- * Ignores 'rightleft', this window is never right-left.
- */
- if (cmdwin_type != 0 && wp == curwin) {
- schar_from_ascii(linebuf_char[off], cmdwin_type);
- linebuf_attr[off] = win_hl_attr(wp, HLF_AT);
- col++;
- }
-
-# define RL_MEMSET(p, v, l) \
- do { \
- if (wp->w_p_rl) { \
- for (int ri = 0; ri < l; ri++) { \
- linebuf_attr[off + (wp->w_grid.Columns - (p) - (l)) + ri] = v; \
- } \
- } else { \
- for (int ri = 0; ri < l; ri++) { \
- linebuf_attr[off + (p) + ri] = v; \
- } \
- } \
- } while (0)
-
- // 2. Add the 'foldcolumn'
- // Reduce the width when there is not enough space.
- fdc = compute_foldcolumn(wp, col);
- if (fdc > 0) {
- fill_foldcolumn(buf, wp, true, lnum);
- const char_u *it = &buf[0];
- for (int i = 0; i < fdc; i++) {
- int mb_c = mb_ptr2char_adv(&it);
- if (wp->w_p_rl) {
- schar_from_char(linebuf_char[off + wp->w_grid.Columns - i - 1 - col],
- mb_c);
- } else {
- schar_from_char(linebuf_char[off + col + i], mb_c);
- }
- }
- RL_MEMSET(col, win_hl_attr(wp, HLF_FC), fdc);
- col += fdc;
- }
-
- /* Set all attributes of the 'number' or 'relativenumber' column and the
- * text */
- RL_MEMSET(col, win_hl_attr(wp, HLF_FL), wp->w_grid.Columns - col);
-
- // If signs are being displayed, add spaces.
- if (win_signcol_count(wp) > 0) {
- len = wp->w_grid.Columns - col;
- if (len > 0) {
- int len_max = win_signcol_width(wp) * win_signcol_count(wp);
- if (len > len_max) {
- len = len_max;
- }
- char_u space_buf[18] = " ";
- assert((size_t)len_max <= sizeof(space_buf));
- copy_text_attr(off + col, space_buf, len,
- win_hl_attr(wp, HLF_FL));
- col += len;
- }
- }
-
- /*
- * 3. Add the 'number' or 'relativenumber' column
- */
- if (wp->w_p_nu || wp->w_p_rnu) {
- len = wp->w_grid.Columns - col;
- if (len > 0) {
- int w = number_width(wp);
- long num;
- char *fmt = "%*ld ";
-
- if (len > w + 1)
- len = w + 1;
-
- if (wp->w_p_nu && !wp->w_p_rnu)
- /* 'number' + 'norelativenumber' */
- num = (long)lnum;
- else {
- /* 'relativenumber', don't use negative numbers */
- num = labs((long)get_cursor_rel_lnum(wp, lnum));
- if (num == 0 && wp->w_p_nu && wp->w_p_rnu) {
- /* 'number' + 'relativenumber': cursor line shows absolute
- * line number */
- num = lnum;
- fmt = "%-*ld ";
- }
- }
-
- snprintf((char *)buf, FOLD_TEXT_LEN, fmt, w, num);
- if (wp->w_p_rl) {
- // the line number isn't reversed
- copy_text_attr(off + wp->w_grid.Columns - len - col, buf, len,
- win_hl_attr(wp, HLF_FL));
- } else {
- copy_text_attr(off + col, buf, len, win_hl_attr(wp, HLF_FL));
- }
- col += len;
- }
- }
-
- /*
- * 4. Compose the folded-line string with 'foldtext', if set.
- */
- text = get_foldtext(wp, lnum, lnume, foldinfo, buf);
-
- txtcol = col; /* remember where text starts */
-
- // 5. move the text to linebuf_char[off]. Fill up with "fold".
- // Right-left text is put in columns 0 - number-col, normal text is put
- // in columns number-col - window-width.
- col = text_to_screenline(wp, text, col, off);
-
- /* Fill the rest of the line with the fold filler */
- if (wp->w_p_rl)
- col -= txtcol;
-
- schar_T sc;
- schar_from_char(sc, wp->w_p_fcs_chars.fold);
- while (col < wp->w_grid.Columns
- - (wp->w_p_rl ? txtcol : 0)
- ) {
- schar_copy(linebuf_char[off+col++], sc);
- }
-
- if (text != buf)
- xfree(text);
-
- /*
- * 6. set highlighting for the Visual area an other text.
- * If all folded lines are in the Visual area, highlight the line.
- */
- if (VIsual_active && wp->w_buffer == curwin->w_buffer) {
- if (ltoreq(curwin->w_cursor, VIsual)) {
- /* Visual is after curwin->w_cursor */
- top = &curwin->w_cursor;
- bot = &VIsual;
- } else {
- /* Visual is before curwin->w_cursor */
- top = &VIsual;
- bot = &curwin->w_cursor;
- }
- if (lnum >= top->lnum
- && lnume <= bot->lnum
- && (VIsual_mode != 'v'
- || ((lnum > top->lnum
- || (lnum == top->lnum
- && top->col == 0))
- && (lnume < bot->lnum
- || (lnume == bot->lnum
- && (bot->col - (*p_sel == 'e'))
- >= (colnr_T)STRLEN(ml_get_buf(wp->w_buffer, lnume,
- FALSE))))))) {
- if (VIsual_mode == Ctrl_V) {
- // Visual block mode: highlight the chars part of the block
- if (wp->w_old_cursor_fcol + txtcol < (colnr_T)wp->w_grid.Columns) {
- if (wp->w_old_cursor_lcol != MAXCOL
- && wp->w_old_cursor_lcol + txtcol
- < (colnr_T)wp->w_grid.Columns) {
- len = wp->w_old_cursor_lcol;
- } else {
- len = wp->w_grid.Columns - txtcol;
- }
- RL_MEMSET(wp->w_old_cursor_fcol + txtcol, win_hl_attr(wp, HLF_V),
- len - (int)wp->w_old_cursor_fcol);
- }
- } else {
- // Set all attributes of the text
- RL_MEMSET(txtcol, win_hl_attr(wp, HLF_V), wp->w_grid.Columns - txtcol);
- }
- }
- }
-
- // Show colorcolumn in the fold line, but let cursorcolumn override it.
- if (wp->w_p_cc_cols) {
- int i = 0;
- int j = wp->w_p_cc_cols[i];
- int old_txtcol = txtcol;
-
- while (j > -1) {
- txtcol += j;
- if (wp->w_p_wrap) {
- txtcol -= wp->w_skipcol;
- } else {
- txtcol -= wp->w_leftcol;
- }
- if (txtcol >= 0 && txtcol < wp->w_grid.Columns) {
- linebuf_attr[off + txtcol] =
- hl_combine_attr(linebuf_attr[off + txtcol], win_hl_attr(wp, HLF_MC));
- }
- txtcol = old_txtcol;
- j = wp->w_p_cc_cols[++i];
- }
- }
-
- /* Show 'cursorcolumn' in the fold line. */
- if (wp->w_p_cuc) {
- txtcol += wp->w_virtcol;
- if (wp->w_p_wrap)
- txtcol -= wp->w_skipcol;
- else
- txtcol -= wp->w_leftcol;
- if (txtcol >= 0 && txtcol < wp->w_grid.Columns) {
- linebuf_attr[off + txtcol] = hl_combine_attr(
- linebuf_attr[off + txtcol], win_hl_attr(wp, HLF_CUC));
- }
- }
-
- grid_put_linebuf(&wp->w_grid, row, 0, wp->w_grid.Columns, wp->w_grid.Columns,
- false, wp, wp->w_hl_attr_normal, false);
-
- /*
- * Update w_cline_height and w_cline_folded if the cursor line was
- * updated (saves a call to plines() later).
- */
- if (wp == curwin
- && lnum <= curwin->w_cursor.lnum
- && lnume >= curwin->w_cursor.lnum) {
- curwin->w_cline_row = row;
- curwin->w_cline_height = 1;
- curwin->w_cline_folded = true;
- curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW);
- conceal_cursor_used = conceal_cursor_line(curwin);
- }
-}
-
-
-/// Copy "buf[len]" to linebuf_char["off"] and set attributes to "attr".
-///
-/// Only works for ASCII text!
-static void copy_text_attr(int off, char_u *buf, int len, int attr)
-{
- int i;
-
- for (i = 0; i < len; i++) {
- schar_from_ascii(linebuf_char[off + i], buf[i]);
- linebuf_attr[off + i] = attr;
- }
-}
/// Fills the foldcolumn at "p" for window "wp".
/// Only to be called when 'foldcolumn' > 0.
@@ -2120,7 +1826,7 @@ static size_t
fill_foldcolumn(
char_u *p,
win_T *wp,
- int closed,
+ foldinfo_T foldinfo,
linenr_T lnum
)
{
@@ -2131,10 +1837,11 @@ fill_foldcolumn(
size_t char_counter = 0;
int symbol = 0;
int len = 0;
+ bool closed = foldinfo.fi_lines > 0;
// Init to all spaces.
memset(p, ' ', MAX_MCO * fdc + 1);
- level = win_foldinfo.fi_level;
+ level = foldinfo.fi_level;
// If the column is too narrow, we start at the lowest level that
// fits and use numbers to indicated the depth.
@@ -2144,8 +1851,8 @@ fill_foldcolumn(
}
for (i = 0; i < MIN(fdc, level); i++) {
- if (win_foldinfo.fi_lnum == lnum
- && first_level + i >= win_foldinfo.fi_low_level) {
+ if (foldinfo.fi_lnum == lnum
+ && first_level + i >= foldinfo.fi_low_level) {
symbol = wp->w_p_fcs_chars.foldopen;
} else if (first_level == 1) {
symbol = wp->w_p_fcs_chars.foldsep;
@@ -2176,21 +1883,27 @@ fill_foldcolumn(
return MAX(char_counter + (fdc-i), (size_t)fdc);
}
-/*
- * Display line "lnum" of window 'wp' on the screen.
- * Start at row "startrow", stop when "endrow" is reached.
- * wp->w_virtcol needs to be valid.
- *
- * Return the number of last row the line occupies.
- */
+
+/// Display line "lnum" of window 'wp' on the screen.
+/// Start at row "startrow", stop when "endrow" is reached.
+/// wp->w_virtcol needs to be valid.
+///
+/// @param lnum line to display
+/// @param endrow stop drawing once reaching this row
+/// @param nochange not updating for changed text
+/// @param number_only only update the number column
+/// @param foldinfo fold info for this line
+///
+/// @return the number of last row the line occupies.
static int
win_line (
win_T *wp,
linenr_T lnum,
int startrow,
int endrow,
- bool nochange, // not updating for changed text
- bool number_only // only update the number column
+ bool nochange,
+ bool number_only,
+ foldinfo_T foldinfo
)
{
int c = 0; // init for GCC
@@ -2295,6 +2008,8 @@ win_line (
bool has_decorations = false; // this buffer has decorations
bool do_virttext = false; // draw virtual text for this line
+ char_u buf_fold[FOLD_TEXT_LEN + 1]; // Hold value returned by get_foldtext
+
/* draw_state: items that are drawn in sequence: */
#define WL_START 0 /* nothing done yet */
# define WL_CMDLINE WL_START + 1 /* cmdline window column */
@@ -2846,7 +2561,7 @@ win_line (
// already be in use.
xfree(p_extra_free);
p_extra_free = xmalloc(MAX_MCO * fdc + 1);
- n_extra = fill_foldcolumn(p_extra_free, wp, false, lnum);
+ n_extra = fill_foldcolumn(p_extra_free, wp, foldinfo, lnum);
p_extra_free[n_extra] = NUL;
p_extra = p_extra_free;
c_extra = NUL;
@@ -3072,6 +2787,51 @@ win_line (
break;
}
+ if (draw_state == WL_LINE
+ && foldinfo.fi_level != 0
+ && foldinfo.fi_lines > 0
+ && vcol == 0
+ && n_extra == 0
+ && row == startrow) {
+ char_attr = win_hl_attr(wp, HLF_FL);
+
+ linenr_T lnume = lnum + foldinfo.fi_lines - 1;
+ memset(buf_fold, ' ', FOLD_TEXT_LEN);
+ p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold);
+ n_extra = STRLEN(p_extra);
+
+ if (p_extra != buf_fold) {
+ xfree(p_extra_free);
+ p_extra_free = p_extra;
+ }
+ c_extra = NUL;
+ c_final = NUL;
+ p_extra[n_extra] = NUL;
+ }
+
+ if (draw_state == WL_LINE
+ && foldinfo.fi_level != 0
+ && foldinfo.fi_lines > 0
+ && col < grid->Columns
+ && n_extra == 0
+ && row == startrow) {
+ // fill rest of line with 'fold'
+ c_extra = wp->w_p_fcs_chars.fold;
+ c_final = NUL;
+
+ n_extra = wp->w_p_rl ? (col + 1) : (grid->Columns - col);
+ }
+
+ if (draw_state == WL_LINE
+ && foldinfo.fi_level != 0
+ && foldinfo.fi_lines > 0
+ && col >= grid->Columns
+ && n_extra != 0
+ && row == startrow) {
+ // Truncate the folding.
+ n_extra = 0;
+ }
+
if (draw_state == WL_LINE && (area_highlighting || has_spell)) {
// handle Visual or match highlighting in this line
if (vcol == fromcol
@@ -3300,6 +3060,10 @@ win_line (
p_extra++;
}
n_extra--;
+ } else if (foldinfo.fi_lines > 0) {
+ // skip writing the buffer line itself
+ c = NUL;
+ XFREE_CLEAR(p_extra_free);
} else {
int c0;
@@ -3868,7 +3632,7 @@ win_line (
// not showing the '>', put pointer back to avoid getting stuck
ptr++;
}
- }
+ } // end of printing from buffer content
/* In the cursor line and we may be concealing characters: correct
* the cursor column when we reach its position. */
@@ -4178,11 +3942,10 @@ win_line (
if (wp == curwin && lnum == curwin->w_cursor.lnum) {
curwin->w_cline_row = startrow;
curwin->w_cline_height = row - startrow;
- curwin->w_cline_folded = false;
+ curwin->w_cline_folded = foldinfo.fi_lines > 0;
curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW);
conceal_cursor_used = conceal_cursor_line(curwin);
}
-
break;
}
@@ -4371,6 +4134,7 @@ win_line (
* so far. If there is no more to display it is caught above.
*/
if ((wp->w_p_rl ? (col < 0) : (col >= grid->Columns))
+ && foldinfo.fi_lines == 0
&& (*ptr != NUL
|| filler_todo > 0
|| (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 517db05a40..b053459c7f 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -1155,8 +1155,8 @@ int do_search(
pat = p; /* put pat after search command */
}
- if ((options & SEARCH_ECHO) && messaging()
- && !cmd_silent && msg_silent == 0) {
+ if ((options & SEARCH_ECHO) && messaging() && !msg_silent
+ && (!cmd_silent || !shortmess(SHM_SEARCHCOUNT))) {
char_u *trunc;
char_u off_buf[40];
size_t off_len = 0;
@@ -1165,7 +1165,8 @@ int do_search(
msg_start();
// Get the offset, so we know how long it is.
- if (spats[0].off.line || spats[0].off.end || spats[0].off.off) {
+ if (!cmd_silent
+ && (spats[0].off.line || spats[0].off.end || spats[0].off.off)) {
p = off_buf; // -V507
*p++ = dirc;
if (spats[0].off.end) {
@@ -1190,14 +1191,14 @@ int do_search(
p = searchstr;
}
- if (!shortmess(SHM_SEARCHCOUNT)) {
+ if (!shortmess(SHM_SEARCHCOUNT) || cmd_silent) {
// Reserve enough space for the search pattern + offset +
// search stat. Use all the space available, so that the
// search state is right aligned. If there is not enough space
// msg_strtrunc() will shorten in the middle.
if (ui_has(kUIMessages)) {
len = 0; // adjusted below
- } else if (msg_scrolled != 0) {
+ } else if (msg_scrolled != 0 && !cmd_silent) {
// Use all the columns.
len = (Rows - msg_row) * Columns - 1;
} else {
@@ -1214,11 +1215,13 @@ int do_search(
xfree(msgbuf);
msgbuf = xmalloc(len);
- {
- memset(msgbuf, ' ', len);
- msgbuf[0] = dirc;
- msgbuf[len - 1] = NUL;
+ memset(msgbuf, ' ', len);
+ msgbuf[len - 1] = NUL;
+ // do not fill the msgbuf buffer, if cmd_silent is set, leave it
+ // empty for the search_stat feature.
+ if (!cmd_silent) {
+ msgbuf[0] = dirc;
if (utf_iscomposing(utf_ptr2char(p))) {
// Use a space to draw the composing char on.
msgbuf[1] = ' ';
@@ -1362,12 +1365,15 @@ int do_search(
// Show [1/15] if 'S' is not in 'shortmess'.
if ((options & SEARCH_ECHO)
&& messaging()
- && !(cmd_silent + msg_silent)
+ && !msg_silent
&& c != FAIL
&& !shortmess(SHM_SEARCHCOUNT)
&& msgbuf != NULL) {
search_stat(dirc, &pos, show_top_bot_msg, msgbuf,
- (count != 1 || has_offset));
+ (count != 1
+ || has_offset
+ || (!(fdo_flags & FDO_SEARCH)
+ && hasFolding(curwin->w_cursor.lnum, NULL, NULL))));
}
// The search command can be followed by a ';' to do another search.
@@ -4356,7 +4362,9 @@ static void search_stat(int dirc, pos_T *pos,
len = STRLEN(t);
if (show_top_bot_msg && len + 2 < SEARCH_STAT_BUF_LEN) {
- STRCPY(t + len, " W");
+ memmove(t + 2, t, len);
+ t[0] = 'W';
+ t[1] = ' ';
len += 2;
}
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index dc1bfe25b4..f036d7fe04 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -362,6 +362,8 @@ size_t spell_check(
size_t wrongcaplen = 0;
int lpi;
bool count_word = docount;
+ bool use_camel_case = *wp->w_s->b_p_spo != NUL;
+ bool camel_case = false;
// A word never starts at a space or a control character. Return quickly
// then, skipping over the character.
@@ -394,9 +396,24 @@ size_t spell_check(
mi.mi_word = ptr;
mi.mi_fend = ptr;
if (spell_iswordp(mi.mi_fend, wp)) {
+ int prev_upper;
+ int this_upper;
+
+ if (use_camel_case) {
+ c = PTR2CHAR(mi.mi_fend);
+ this_upper = SPELL_ISUPPER(c);
+ }
+
do {
MB_PTR_ADV(mi.mi_fend);
- } while (*mi.mi_fend != NUL && spell_iswordp(mi.mi_fend, wp));
+ if (use_camel_case) {
+ prev_upper = this_upper;
+ c = PTR2CHAR(mi.mi_fend);
+ this_upper = SPELL_ISUPPER(c);
+ camel_case = !prev_upper && this_upper;
+ }
+ } while (*mi.mi_fend != NUL && spell_iswordp(mi.mi_fend, wp)
+ && !camel_case);
if (capcol != NULL && *capcol == 0 && wp->w_s->b_cap_prog != NULL) {
// Check word starting with capital letter.
@@ -428,6 +445,11 @@ size_t spell_check(
(void)spell_casefold(ptr, (int)(mi.mi_fend - ptr), mi.mi_fword, MAXWLEN + 1);
mi.mi_fwordlen = (int)STRLEN(mi.mi_fword);
+ if (camel_case) {
+ // introduce a fake word end space into the folded word.
+ mi.mi_fword[mi.mi_fwordlen - 1] = ' ';
+ }
+
// The word is bad unless we recognize it.
mi.mi_result = SP_BAD;
mi.mi_result2 = SP_BAD;
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 4aa7c21ce4..9a9cc45c6b 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -5588,9 +5588,11 @@ void ex_ownsyntax(exarg_T *eap)
hash_init(&curwin->w_s->b_keywtab_ic);
// TODO: Keep the spell checking as it was. NOLINT(readability/todo)
curwin->w_p_spell = false; // No spell checking
+ // make sure option values are "empty_option" instead of NULL
clear_string_option(&curwin->w_s->b_p_spc);
clear_string_option(&curwin->w_s->b_p_spf);
clear_string_option(&curwin->w_s->b_p_spl);
+ clear_string_option(&curwin->w_s->b_p_spo);
clear_string_option(&curwin->w_s->b_syn_isk);
}
diff --git a/src/nvim/testdir/shared.vim b/src/nvim/testdir/shared.vim
index 6180d542ff..0d32f4d875 100644
--- a/src/nvim/testdir/shared.vim
+++ b/src/nvim/testdir/shared.vim
@@ -329,3 +329,17 @@ func RunVimPiped(before, after, arguments, pipecmd)
endif
return 1
endfunc
+
+" Get all messages but drop the maintainer entry.
+func GetMessages()
+ redir => result
+ redraw | messages
+ redir END
+ let msg_list = split(result, "\n")
+ " if msg_list->len() > 0 && msg_list[0] =~ 'Messages maintainer:'
+ " return msg_list[1:]
+ " endif
+ return msg_list
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index dbe12fc8fc..7f456ffbce 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -735,17 +735,16 @@ func! Test_edit_CTRL_O()
endfunc
func! Test_edit_CTRL_R()
- throw 'skipped: Nvim does not support test_override()'
" Insert Register
new
- call test_override("ALL", 1)
+ " call test_override("ALL", 1)
set showcmd
call feedkeys("AFOOBAR eins zwei\<esc>", 'tnix')
call feedkeys("O\<c-r>.", 'tnix')
call feedkeys("O\<c-r>=10*500\<cr>\<esc>", 'tnix')
call feedkeys("O\<c-r>=getreg('=', 1)\<cr>\<esc>", 'tnix')
call assert_equal(["getreg('=', 1)", '5000', "FOOBAR eins zwei", "FOOBAR eins zwei"], getline(1, '$'))
- call test_override("ALL", 0)
+ " call test_override("ALL", 0)
set noshowcmd
bw!
endfunc
@@ -957,7 +956,6 @@ func! Test_edit_DROP()
endfunc
func! Test_edit_CTRL_V()
- throw 'skipped: Nvim does not support test_override()'
if has("ebcdic")
return
endif
@@ -967,7 +965,7 @@ func! Test_edit_CTRL_V()
" force some redraws
set showmode showcmd
"call test_override_char_avail(1)
- call test_override('ALL', 1)
+ " call test_override('ALL', 1)
call feedkeys("A\<c-v>\<c-n>\<c-v>\<c-l>\<c-v>\<c-b>\<esc>", 'tnix')
call assert_equal(["abc\x0e\x0c\x02"], getline(1, '$'))
@@ -980,7 +978,7 @@ func! Test_edit_CTRL_V()
set norl
endif
- call test_override('ALL', 0)
+ " call test_override('ALL', 0)
set noshowmode showcmd
bw!
endfunc
diff --git a/src/nvim/testdir/test_environ.vim b/src/nvim/testdir/test_environ.vim
index 21bb09a690..a25d83753c 100644
--- a/src/nvim/testdir/test_environ.vim
+++ b/src/nvim/testdir/test_environ.vim
@@ -1,5 +1,9 @@
+" Test for environment variables.
+
scriptencoding utf-8
+source check.vim
+
func Test_environ()
unlet! $TESTENV
call assert_equal(0, has_key(environ(), 'TESTENV'))
@@ -42,3 +46,24 @@ func Test_external_env()
endif
call assert_equal('', result)
endfunc
+
+func Test_mac_locale()
+ CheckFeature osxdarwin
+
+ " If $LANG is not set then the system locale will be used.
+ " Run Vim after unsetting all the locale environmental vars, and capture the
+ " output of :lang.
+ let lang_results = system("unset LANG; unset LC_MESSAGES; " ..
+ \ shellescape(v:progpath) ..
+ \ " --clean -esX -c 'redir @a' -c 'lang' -c 'put a' -c 'print' -c 'qa!' ")
+
+ " Check that:
+ " 1. The locale is the form of <locale>.UTF-8.
+ " 2. Check that fourth item (LC_NUMERIC) is properly set to "C".
+ " Example match: "en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8"
+ call assert_match('"\([a-zA-Z_]\+\.UTF-8/\)\{3}C\(/[a-zA-Z_]\+\.UTF-8\)\{2}"',
+ \ lang_results,
+ \ "Default locale should have UTF-8 encoding set, and LC_NUMERIC set to 'C'")
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 617e3dfe41..9f8939f2f6 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -326,7 +326,7 @@ let s:filename_checks = {
\ 'pamconf': ['/etc/pam.conf'],
\ 'pamenv': ['/etc/security/pam_env.conf', '/home/user/.pam_environment'],
\ 'papp': ['file.papp', 'file.pxml', 'file.pxsl'],
- \ 'pascal': ['file.pas', 'file.dpr'],
+ \ 'pascal': ['file.pas', 'file.pp', 'file.dpr', 'file.lpr'],
\ 'passwd': ['any/etc/passwd', 'any/etc/passwd-', 'any/etc/passwd.edit', 'any/etc/shadow', 'any/etc/shadow-', 'any/etc/shadow.edit', 'any/var/backups/passwd.bak', 'any/var/backups/shadow.bak'],
\ 'pccts': ['file.g'],
\ 'pdf': ['file.pdf'],
@@ -455,7 +455,7 @@ let s:filename_checks = {
\ 'texmf': ['texmf.cnf'],
\ 'text': ['file.text', 'README'],
\ 'tf': ['file.tf', '.tfrc', 'tfrc'],
- \ 'tidy': ['.tidyrc', 'tidyrc'],
+ \ 'tidy': ['.tidyrc', 'tidyrc', 'tidy.conf'],
\ 'tilde': ['file.t.html'],
\ 'tli': ['file.tli'],
\ 'tmux': ['tmuxfile.conf', '.tmuxfile.conf'],
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index c29e0410a9..8fa70a5313 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -1222,16 +1222,16 @@ func Test_reg_executing_and_recording()
endfunc
func Test_getchar()
- throw 'skipped: Nvim does not support test_setmouse()'
call feedkeys('a', '')
call assert_equal(char2nr('a'), getchar())
- call test_setmouse(1, 3)
- let v:mouse_win = 9
- let v:mouse_winid = 9
- let v:mouse_lnum = 9
- let v:mouse_col = 9
- call feedkeys("\<S-LeftMouse>", '')
+ " call test_setmouse(1, 3)
+ " let v:mouse_win = 9
+ " let v:mouse_winid = 9
+ " let v:mouse_lnum = 9
+ " let v:mouse_col = 9
+ " call feedkeys("\<S-LeftMouse>", '')
+ call nvim_input_mouse('left', 'press', 'S', 0, 0, 2)
call assert_equal("\<S-LeftMouse>", getchar())
call assert_equal(1, v:mouse_win)
call assert_equal(win_getid(1), v:mouse_winid)
diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim
index bfbb3e5c5b..f026c8a55f 100644
--- a/src/nvim/testdir/test_lambda.vim
+++ b/src/nvim/testdir/test_lambda.vim
@@ -181,7 +181,7 @@ function! Test_lambda_scope()
let l:D = s:NewCounter2()
call assert_equal(1, l:C())
- call assert_fails(':call l:D()', 'E15:') " E121: then E15:
+ call assert_fails(':call l:D()', 'E121:')
call assert_equal(2, l:C())
endfunction
diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim
index 82562339f6..dd0da5db64 100644
--- a/src/nvim/testdir/test_mapping.vim
+++ b/src/nvim/testdir/test_mapping.vim
@@ -391,6 +391,42 @@ func Test_motionforce_omap()
delfunc GetCommand
endfunc
+func Test_error_in_map_expr()
+ if !has('terminal') || (has('win32') && has('gui_running'))
+ throw 'Skipped: cannot run Vim in a terminal window'
+ endif
+
+ let lines =<< trim [CODE]
+ func Func()
+ " fail to create list
+ let x = [
+ endfunc
+ nmap <expr> ! Func()
+ set updatetime=50
+ [CODE]
+ call writefile(lines, 'Xtest.vim')
+
+ let buf = term_start(GetVimCommandClean() .. ' -S Xtest.vim', {'term_rows': 8})
+ let job = term_getjob(buf)
+ call WaitForAssert({-> assert_notequal('', term_getline(buf, 8))})
+
+ " GC must not run during map-expr processing, which can make Vim crash.
+ call term_sendkeys(buf, '!')
+ call term_wait(buf, 100)
+ call term_sendkeys(buf, "\<CR>")
+ call term_wait(buf, 100)
+ call assert_equal('run', job_status(job))
+
+ call term_sendkeys(buf, ":qall!\<CR>")
+ call WaitFor({-> job_status(job) ==# 'dead'})
+ if has('unix')
+ call assert_equal('', job_info(job).termsig)
+ endif
+
+ call delete('Xtest.vim')
+ exe buf .. 'bwipe!'
+endfunc
+
" Test for mapping errors
func Test_map_error()
call assert_fails('unmap', 'E474:')
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index eeec5bd2c3..fab5cb320f 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -3473,6 +3473,18 @@ func Test_shorten_fname()
" Displaying the quickfix list should simplify the file path
silent! clist
call assert_equal('test_quickfix.vim', bufname('test_quickfix.vim'))
+ " Add a few entries for the same file with different paths and check whether
+ " the buffer name is shortened
+ %bwipe
+ call setqflist([], 'f')
+ call setqflist([{'filename' : 'test_quickfix.vim', 'lnum' : 10},
+ \ {'filename' : '../testdir/test_quickfix.vim', 'lnum' : 20},
+ \ {'filename' : fname, 'lnum' : 30}], ' ')
+ copen
+ call assert_equal(['test_quickfix.vim|10| ',
+ \ 'test_quickfix.vim|20| ',
+ \ 'test_quickfix.vim|30| '], getline(1, '$'))
+ cclose
endfunc
" Quickfix title tests
diff --git a/src/nvim/testdir/test_search_stat.vim b/src/nvim/testdir/test_search_stat.vim
index fa620b4e00..11c6489ca2 100644
--- a/src/nvim/testdir/test_search_stat.vim
+++ b/src/nvim/testdir/test_search_stat.vim
@@ -1,13 +1,9 @@
" Tests for search_stats, when "S" is not in 'shortmess'
-"
-" This test is fragile, it might not work interactively, but it works when run
-" as test!
-source shared.vim
source screendump.vim
source check.vim
-func! Test_search_stat()
+func Test_search_stat()
new
set shortmess-=S
" Append 50 lines with text to search for, "foobar" appears 20 times
@@ -47,7 +43,7 @@ func! Test_search_stat()
call assert_match(pat .. stat, g:a)
call cursor(line('$'), 1)
let g:a = execute(':unsilent :norm! n')
- let stat = '\[1/>99\] W'
+ let stat = 'W \[1/>99\]'
call assert_match(pat .. stat, g:a)
" Many matches
@@ -57,7 +53,7 @@ func! Test_search_stat()
call assert_match(pat .. stat, g:a)
call cursor(1, 1)
let g:a = execute(':unsilent :norm! N')
- let stat = '\[>99/>99\] W'
+ let stat = 'W \[>99/>99\]'
call assert_match(pat .. stat, g:a)
" right-left
@@ -89,7 +85,7 @@ func! Test_search_stat()
call cursor('$',1)
let pat = 'raboof/\s\+'
let g:a = execute(':unsilent :norm! n')
- let stat = '\[20/1\]'
+ let stat = 'W \[20/1\]'
call assert_match(pat .. stat, g:a)
call assert_match('search hit BOTTOM, continuing at TOP', g:a)
set norl
@@ -100,10 +96,10 @@ func! Test_search_stat()
let @/ = 'foobar'
let pat = '?foobar\s\+'
let g:a = execute(':unsilent :norm! N')
- let stat = '\[20/20\]'
+ let stat = 'W \[20/20\]'
call assert_match(pat .. stat, g:a)
call assert_match('search hit TOP, continuing at BOTTOM', g:a)
- call assert_match('\[20/20\] W', Screenline(&lines))
+ call assert_match('W \[20/20\]', Screenline(&lines))
" normal, no match
call cursor(1,1)
@@ -162,11 +158,91 @@ func! Test_search_stat()
let stat = '\[1/2\]'
call assert_notmatch(pat .. stat, g:a)
- " close the window
+ " normal, n comes from a silent mapping
+ " First test a normal mapping, then a silent mapping
+ call cursor(1,1)
+ nnoremap n n
+ let @/ = 'find this'
+ let pat = '/find this\s\+'
+ let g:a = execute(':unsilent :norm n')
+ let g:b = split(g:a, "\n")[-1]
+ let stat = '\[1/2\]'
+ call assert_match(pat .. stat, g:b)
+ nnoremap <silent> n n
+ call cursor(1,1)
+ let g:a = execute(':unsilent :norm n')
+ let g:b = split(g:a, "\n")[-1]
+ let stat = '\[1/2\]'
+ call assert_notmatch(pat .. stat, g:b)
+ call assert_match(stat, g:b)
+ " Test that the message is not truncated
+ " it would insert '...' into the output.
+ call assert_match('^\s\+' .. stat, g:b)
+ unmap n
+
+ " Clean up
set shortmess+=S
+ " close the window
bwipe!
endfunc
+func Test_search_stat_foldopen()
+ CheckScreendump
+
+ let lines =<< trim END
+ set shortmess-=S
+ setl foldenable foldmethod=indent foldopen-=search
+ call append(0, ['if', "\tfoo", "\tfoo", 'endif'])
+ let @/ = 'foo'
+ call cursor(1,1)
+ norm n
+ END
+ call writefile(lines, 'Xsearchstat1')
+
+ let buf = RunVimInTerminal('-S Xsearchstat1', #{rows: 10})
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_searchstat_3', {})
+
+ call term_sendkeys(buf, "n")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_searchstat_3', {})
+
+ call term_sendkeys(buf, "n")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_searchstat_3', {})
+
+ call StopVimInTerminal(buf)
+ call delete('Xsearchstat1')
+endfunc
+
+func! Test_search_stat_screendump()
+ CheckScreendump
+
+ let lines =<< trim END
+ set shortmess-=S
+ " Append 50 lines with text to search for, "foobar" appears 20 times
+ call append(0, repeat(['foobar', 'foo', 'fooooobar', 'foba', 'foobar'], 20))
+ call setline(2, 'find this')
+ call setline(70, 'find this')
+ nnoremap n n
+ let @/ = 'find this'
+ call cursor(1,1)
+ norm n
+ END
+ call writefile(lines, 'Xsearchstat')
+ let buf = RunVimInTerminal('-S Xsearchstat', #{rows: 10})
+ call term_wait(buf)
+ call VerifyScreenDump(buf, 'Test_searchstat_1', {})
+
+ call term_sendkeys(buf, ":nnoremap <silent> n n\<cr>")
+ call term_sendkeys(buf, "gg0n")
+ call term_wait(buf)
+ call VerifyScreenDump(buf, 'Test_searchstat_2', {})
+
+ call StopVimInTerminal(buf)
+ call delete('Xsearchstat')
+endfunc
+
func Test_searchcount_in_statusline()
CheckScreendump
diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim
index 414c7278eb..ab8a998bb8 100644
--- a/src/nvim/testdir/test_spell.vim
+++ b/src/nvim/testdir/test_spell.vim
@@ -79,6 +79,11 @@ func Test_spellbadword()
call assert_equal(['bycycle', 'bad'], spellbadword('My bycycle.'))
call assert_equal(['another', 'caps'], spellbadword('A sentence. another sentence'))
+ call assert_equal(['TheCamelWord', 'bad'], spellbadword('TheCamelWord asdf'))
+ set spelloptions=camel
+ call assert_equal(['asdf', 'bad'], spellbadword('TheCamelWord asdf'))
+ set spelloptions=
+
set spelllang=en
call assert_equal(['', ''], spellbadword('centre'))
call assert_equal(['', ''], spellbadword('center'))
@@ -113,6 +118,43 @@ foobar/?
set spell&
endfunc
+func Test_spelllang_inv_region()
+ set spell spelllang=en_xx
+ let messages = GetMessages()
+ call assert_equal('Warning: region xx not supported', messages[-1])
+ set spell& spelllang&
+endfunc
+
+func Test_compl_with_CTRL_X_CTRL_K_using_spell()
+ " When spell checking is enabled and 'dictionary' is empty,
+ " CTRL-X CTRL-K in insert mode completes using the spelling dictionary.
+ new
+ set spell spelllang=en dictionary=
+
+ set ignorecase
+ call feedkeys("Senglis\<c-x>\<c-k>\<esc>", 'tnx')
+ call assert_equal(['English'], getline(1, '$'))
+ call feedkeys("SEnglis\<c-x>\<c-k>\<esc>", 'tnx')
+ call assert_equal(['English'], getline(1, '$'))
+
+ set noignorecase
+ call feedkeys("Senglis\<c-x>\<c-k>\<esc>", 'tnx')
+ call assert_equal(['englis'], getline(1, '$'))
+ call feedkeys("SEnglis\<c-x>\<c-k>\<esc>", 'tnx')
+ call assert_equal(['English'], getline(1, '$'))
+
+ set spelllang=en_us
+ call feedkeys("Stheat\<c-x>\<c-k>\<esc>", 'tnx')
+ call assert_equal(['theater'], getline(1, '$'))
+ set spelllang=en_gb
+ call feedkeys("Stheat\<c-x>\<c-k>\<esc>", 'tnx')
+ " FIXME: commented out, expected theatre bug got theater. See issue #7025.
+ " call assert_equal(['theatre'], getline(1, '$'))
+
+ bwipe!
+ set spell& spelllang& dictionary& ignorecase&
+endfunc
+
func Test_spellreall()
new
set spell
diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim
index 85ee42420e..2404f113d9 100644
--- a/src/nvim/testdir/test_syntax.vim
+++ b/src/nvim/testdir/test_syntax.vim
@@ -369,7 +369,11 @@ func Test_ownsyntax()
call setline(1, '#define FOO')
syntax on
set filetype=c
+
ownsyntax perl
+ " this should not crash
+ set
+
call assert_equal('perlComment', synIDattr(synID(line('.'), col('.'), 1), 'name'))
call assert_equal('c', b:current_syntax)
call assert_equal('perl', w:current_syntax)
diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim
index cffd80ff4f..c3b03fe1a5 100644
--- a/src/nvim/testdir/test_timers.vim
+++ b/src/nvim/testdir/test_timers.vim
@@ -233,16 +233,17 @@ func Test_timer_catch_error()
endfunc
func FeedAndPeek(timer)
- call test_feedinput('a')
+ " call test_feedinput('a')
+ call nvim_input('a')
call getchar(1)
endfunc
func Interrupt(timer)
- call test_feedinput("\<C-C>")
+ " call test_feedinput("\<C-C>")
+ call nvim_input("\<C-C>")
endfunc
func Test_peek_and_get_char()
- throw 'skipped: Nvim does not support test_feedinput()'
if !has('unix') && !has('gui_running')
return
endif
@@ -339,6 +340,41 @@ func Test_nocatch_garbage_collect()
delfunc FeedChar
endfunc
+func Test_error_in_timer_callback()
+ if !has('terminal') || (has('win32') && has('gui_running'))
+ throw 'Skipped: cannot run Vim in a terminal window'
+ endif
+
+ let lines =<< trim [CODE]
+ func Func(timer)
+ " fail to create list
+ let x = [
+ endfunc
+ set updatetime=50
+ call timer_start(1, 'Func')
+ [CODE]
+ call writefile(lines, 'Xtest.vim')
+
+ let buf = term_start(GetVimCommandClean() .. ' -S Xtest.vim', {'term_rows': 8})
+ let job = term_getjob(buf)
+ call WaitForAssert({-> assert_notequal('', term_getline(buf, 8))})
+
+ " GC must not run during timer callback, which can make Vim crash.
+ call term_wait(buf, 100)
+ call term_sendkeys(buf, "\<CR>")
+ call term_wait(buf, 100)
+ call assert_equal('run', job_status(job))
+
+ call term_sendkeys(buf, ":qall!\<CR>")
+ call WaitFor({-> job_status(job) ==# 'dead'})
+ if has('unix')
+ call assert_equal('', job_info(job).termsig)
+ endif
+
+ call delete('Xtest.vim')
+ exe buf .. 'bwipe!'
+endfunc
+
func Test_timer_invalid_callback()
call assert_fails('call timer_start(0, "0")', 'E921')
endfunc
diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim
index d2f13ff072..cb81997d39 100644
--- a/src/nvim/testdir/test_vimscript.vim
+++ b/src/nvim/testdir/test_vimscript.vim
@@ -1409,6 +1409,17 @@ func Test_compound_assignment_operators()
let @/ = ''
endfunc
+func Test_funccall_garbage_collect()
+ func Func(x, ...)
+ call add(a:x, a:000)
+ endfunc
+ call Func([], [])
+ " Must not crash cause by invalid freeing
+ call test_garbagecollect_now()
+ call assert_true(v:true)
+ delfunc Func
+endfunc
+
func Test_function_defined_line()
if has('gui_running')
" Can't catch the output of gvim.