aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/ui_events.in.h3
-rw-r--r--src/nvim/ascii.h2
-rw-r--r--src/nvim/buffer.c8
-rw-r--r--src/nvim/eval.c77
-rw-r--r--src/nvim/eval/funcs.c31
-rw-r--r--src/nvim/eval/typval.c76
-rw-r--r--src/nvim/eval/typval.h2
-rw-r--r--src/nvim/eval/userfunc.c8
-rw-r--r--src/nvim/event/multiqueue.c6
-rw-r--r--src/nvim/event/multiqueue.h2
-rw-r--r--src/nvim/ex_cmds.c32
-rw-r--r--src/nvim/ex_cmds2.c16
-rw-r--r--src/nvim/ex_docmd.c4
-rw-r--r--src/nvim/lua/executor.c10
-rw-r--r--src/nvim/lua/treesitter.c5
-rw-r--r--src/nvim/marktree.c1
-rw-r--r--src/nvim/ops.c6
-rw-r--r--src/nvim/option.c6
-rw-r--r--src/nvim/options.lua2
-rw-r--r--src/nvim/path.c48
-rw-r--r--src/nvim/quickfix.c153
-rw-r--r--src/nvim/screen.c19
-rw-r--r--src/nvim/testdir/test_autocmd.vim75
-rw-r--r--src/nvim/testdir/test_filetype.vim42
-rw-r--r--src/nvim/testdir/test_global.vim4
-rw-r--r--src/nvim/testdir/test_let.vim10
-rw-r--r--src/nvim/testdir/test_quickfix.vim129
-rw-r--r--src/nvim/testdir/test_visual.vim22
-rw-r--r--src/nvim/tui/tui.c31
-rw-r--r--src/nvim/window.c37
30 files changed, 676 insertions, 191 deletions
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index 11e21a88ea..35d39a34d7 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -130,7 +130,8 @@ void popupmenu_hide(void)
void popupmenu_select(Integer selected)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
-void tabline_update(Tabpage current, Array tabs)
+void tabline_update(Tabpage current, Array tabs,
+ Buffer current_buffer, Array buffers)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void cmdline_show(Array content, Integer pos, String firstc, String prompt,
diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h
index 7e4dee3d34..f41068ea70 100644
--- a/src/nvim/ascii.h
+++ b/src/nvim/ascii.h
@@ -31,9 +31,7 @@
#define CSI 0x9b // Control Sequence Introducer
#define CSI_STR "\233"
#define DCS 0x90 // Device Control String
-#define DCS_STR "\033P"
#define STERM 0x9c // String Terminator
-#define STERM_STR "\033\\"
#define POUND 0xA3
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 6a50264e0f..f1f32076bf 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -840,11 +840,7 @@ static void clear_wininfo(buf_T *buf)
while (buf->b_wininfo != NULL) {
wip = buf->b_wininfo;
buf->b_wininfo = wip->wi_next;
- if (wip->wi_optset) {
- clear_winopt(&wip->wi_opt);
- deleteFoldRecurse(buf, &wip->wi_folds);
- }
- xfree(wip);
+ free_wininfo(wip, buf);
}
}
@@ -5665,7 +5661,7 @@ bool buf_contents_changed(buf_T *buf)
void
wipe_buffer(
buf_T *buf,
- int aucmd // When true trigger autocommands.
+ bool aucmd // When true trigger autocommands.
)
{
if (!aucmd) {
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index a3fa9c986f..ff019d1e07 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -2340,10 +2340,8 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
if (get_var_tv((const char *)lp->ll_name, (int)STRLEN(lp->ll_name),
&tv, &di, true, false) == OK) {
if ((di == NULL
- || (!var_check_ro(di->di_flags, (const char *)lp->ll_name,
- TV_CSTRING)
- && !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name,
- TV_CSTRING)))
+ || (!var_check_ro(di->di_flags, lp->ll_name, TV_CSTRING)
+ && !tv_check_lock(&di->di_tv, lp->ll_name, TV_CSTRING)))
&& eexe_mod_op(&tv, rettv, op) == OK) {
set_var(lp->ll_name, lp->ll_name_len, &tv, false);
}
@@ -2353,10 +2351,10 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
set_var_const(lp->ll_name, lp->ll_name_len, rettv, copy, is_const);
}
*endp = cc;
- } else if (tv_check_lock(lp->ll_newkey == NULL
- ? lp->ll_tv->v_lock
- : lp->ll_tv->vval.v_dict->dv_lock,
- (const char *)lp->ll_name, TV_CSTRING)) {
+ } else if (var_check_lock(lp->ll_newkey == NULL
+ ? lp->ll_tv->v_lock
+ : lp->ll_tv->vval.v_dict->dv_lock,
+ lp->ll_name, TV_CSTRING)) {
} else if (lp->ll_range) {
listitem_T *ll_li = lp->ll_li;
int ll_n1 = lp->ll_n1;
@@ -2369,9 +2367,8 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
// Check whether any of the list items is locked
for (ri = tv_list_first(rettv->vval.v_list);
ri != NULL && ll_li != NULL; ) {
- if (tv_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock,
- (const char *)lp->ll_name,
- TV_CSTRING)) {
+ if (var_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, lp->ll_name,
+ TV_CSTRING)) {
return;
}
ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri);
@@ -2795,13 +2792,13 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap,
} else if ((lp->ll_list != NULL
// ll_list is not NULL when lvalue is not in a list, NULL lists
// yield E689.
- && tv_check_lock(tv_list_locked(lp->ll_list),
- (const char *)lp->ll_name,
- lp->ll_name_len))
+ && var_check_lock(tv_list_locked(lp->ll_list),
+ lp->ll_name,
+ lp->ll_name_len))
|| (lp->ll_dict != NULL
- && tv_check_lock(lp->ll_dict->dv_lock,
- (const char *)lp->ll_name,
- lp->ll_name_len))) {
+ && var_check_lock(lp->ll_dict->dv_lock,
+ lp->ll_name,
+ lp->ll_name_len))) {
return FAIL;
} else if (lp->ll_range) {
assert(lp->ll_list != NULL);
@@ -2810,9 +2807,9 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap,
listitem_T *last_li = first_li;
for (;;) {
listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li);
- if (tv_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock,
- (const char *)lp->ll_name,
- lp->ll_name_len)) {
+ if (var_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock,
+ lp->ll_name,
+ lp->ll_name_len)) {
return false;
}
lp->ll_li = li;
@@ -2897,11 +2894,11 @@ int do_unlet(const char *const name, const size_t name_len, const bool forceit)
dictitem_T *const di = TV_DICT_HI2DI(hi);
if (var_check_fixed(di->di_flags, (const char *)name, TV_CSTRING)
|| var_check_ro(di->di_flags, (const char *)name, TV_CSTRING)
- || tv_check_lock(d->dv_lock, (const char *)name, TV_CSTRING)) {
+ || var_check_lock(d->dv_lock, name, TV_CSTRING)) {
return FAIL;
}
- if (tv_check_lock(d->dv_lock, (const char *)name, TV_CSTRING)) {
+ if (var_check_lock(d->dv_lock, name, TV_CSTRING)) {
return FAIL;
}
@@ -5962,14 +5959,14 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
if (argvars[0].v_type == VAR_LIST) {
tv_copy(&argvars[0], rettv);
if ((l = argvars[0].vval.v_list) == NULL
- || (!map && tv_check_lock(tv_list_locked(l), arg_errmsg,
- TV_TRANSLATE))) {
+ || (!map
+ && var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE))) {
return;
}
} else if (argvars[0].v_type == VAR_DICT) {
tv_copy(&argvars[0], rettv);
if ((d = argvars[0].vval.v_dict) == NULL
- || (!map && tv_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) {
+ || (!map && var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) {
return;
}
} else {
@@ -6002,7 +5999,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
di = TV_DICT_HI2DI(hi);
if (map
- && (tv_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE)
+ && (var_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE)
|| var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) {
break;
}
@@ -6029,8 +6026,8 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
for (listitem_T *li = tv_list_first(l); li != NULL;) {
if (map
- && tv_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg,
- TV_TRANSLATE)) {
+ && var_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg,
+ TV_TRANSLATE)) {
break;
}
vimvars[VV_KEY].vv_nr = idx;
@@ -7200,12 +7197,15 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg)
r = FAIL;
} else if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING) {
char_u *name = arg->vval.v_string;
- if (name != NULL) {
+ if (name == NULL) {
+ r = FAIL;
+ } else if (*name == NUL) {
+ callback->type = kCallbackNone;
+ callback->data.funcref = NULL;
+ } else {
func_ref(name);
callback->data.funcref = vim_strsave(name);
callback->type = kCallbackFuncref;
- } else {
- r = FAIL;
}
} else if (nlua_is_table_from_lua(arg)) {
char_u *name = nlua_register_table_as_callable(arg);
@@ -7216,8 +7216,10 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg)
} else {
r = FAIL;
}
- } else if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0) {
+ } else if (arg->v_type == VAR_SPECIAL
+ || (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0)) {
callback->type = kCallbackNone;
+ callback->data.funcref = NULL;
} else {
r = FAIL;
}
@@ -7324,14 +7326,7 @@ void add_timer_info(typval_T *rettv, timer_T *timer)
return;
}
- if (timer->callback.type == kCallbackPartial) {
- di->di_tv.v_type = VAR_PARTIAL;
- di->di_tv.vval.v_partial = timer->callback.data.partial;
- timer->callback.data.partial->pt_refcount++;
- } else if (timer->callback.type == kCallbackFuncref) {
- di->di_tv.v_type = VAR_FUNC;
- di->di_tv.vval.v_string = vim_strsave(timer->callback.data.funcref);
- }
+ callback_put(&timer->callback, &di->di_tv);
}
void add_timer_info_all(typval_T *rettv)
@@ -8949,7 +8944,7 @@ static void set_var_const(const char *name, const size_t name_len,
// existing variable, need to clear the value
if (var_check_ro(v->di_flags, name, name_len)
- || tv_check_lock(v->di_tv.v_lock, name, name_len)) {
+ || var_check_lock(v->di_tv.v_lock, name, name_len)) {
return;
}
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 4f9a9fcd68..1ba31bfe68 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -268,7 +268,8 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = 1; // Default: failed.
if (argvars[0].v_type == VAR_LIST) {
list_T *const l = argvars[0].vval.v_list;
- if (!tv_check_lock(tv_list_locked(l), N_("add() argument"), TV_TRANSLATE)) {
+ if (!var_check_lock(tv_list_locked(l), N_("add() argument"),
+ TV_TRANSLATE)) {
tv_list_append_tv(l, &argvars[1]);
tv_copy(&argvars[0], rettv);
}
@@ -2277,9 +2278,9 @@ static void f_flatten(typval_T *argvars, typval_T *rettv, FunPtr fptr)
list = argvars[0].vval.v_list;
if (list != NULL
- && !tv_check_lock(tv_list_locked(list),
- N_("flatten() argument"),
- TV_TRANSLATE)
+ && !var_check_lock(tv_list_locked(list),
+ N_("flatten() argument"),
+ TV_TRANSLATE)
&& tv_list_flatten(list, maxdepth) == OK) {
tv_copy(&argvars[0], rettv);
}
@@ -2299,7 +2300,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
list_T *const l1 = argvars[0].vval.v_list;
list_T *const l2 = argvars[1].vval.v_list;
- if (!tv_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) {
+ if (!var_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) {
listitem_T *item;
if (argvars[2].v_type != VAR_UNKNOWN) {
before = (long)tv_get_number_chk(&argvars[2], &error);
@@ -2328,13 +2329,13 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
dict_T *const d1 = argvars[0].vval.v_dict;
dict_T *const d2 = argvars[1].vval.v_dict;
if (d1 == NULL) {
- const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE);
+ const bool locked = var_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE);
(void)locked;
assert(locked == true);
} else if (d2 == NULL) {
// Do nothing
tv_copy(&argvars[0], rettv);
- } else if (!tv_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) {
+ } else if (!var_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) {
const char *action = "force";
// Check the third argument.
if (argvars[2].v_type != VAR_UNKNOWN) {
@@ -4845,8 +4846,8 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[0].v_type != VAR_LIST) {
EMSG2(_(e_listarg), "insert()");
- } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
- N_("insert() argument"), TV_TRANSLATE)) {
+ } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
+ N_("insert() argument"), TV_TRANSLATE)) {
long before = 0;
if (argvars[2].v_type != VAR_UNKNOWN) {
before = tv_get_number_chk(&argvars[2], &error);
@@ -7079,7 +7080,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[2].v_type != VAR_UNKNOWN) {
EMSG2(_(e_toomanyarg), "remove()");
} else if ((d = argvars[0].vval.v_dict) != NULL
- && !tv_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) {
+ && !var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) {
const char *key = tv_get_string_chk(&argvars[1]);
if (key != NULL) {
di = tv_dict_find(d, key, -1);
@@ -7098,8 +7099,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
} else if (argvars[0].v_type != VAR_LIST) {
EMSG2(_(e_listdictarg), "remove()");
- } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
- arg_errmsg, TV_TRANSLATE)) {
+ } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
+ arg_errmsg, TV_TRANSLATE)) {
bool error = false;
idx = tv_get_number_chk(&argvars[1], &error);
@@ -7374,8 +7375,8 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
list_T *l;
if (argvars[0].v_type != VAR_LIST) {
EMSG2(_(e_listarg), "reverse()");
- } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
- N_("reverse() argument"), TV_TRANSLATE)) {
+ } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
+ N_("reverse() argument"), TV_TRANSLATE)) {
tv_list_reverse(l);
tv_list_set_ret(rettv, l);
}
@@ -9462,7 +9463,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
EMSG2(_(e_listarg), sort ? "sort()" : "uniq()");
} else {
list_T *const l = argvars[0].vval.v_list;
- if (tv_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) {
+ if (var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) {
goto theend;
}
tv_list_set_ret(rettv, l);
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 61de83fc21..7221dc8bc9 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -1162,6 +1162,49 @@ void callback_free(Callback *callback)
}
}
callback->type = kCallbackNone;
+ callback->data.funcref = NULL;
+}
+
+/// Copy a callback into a typval_T.
+void callback_put(Callback *cb, typval_T *tv)
+ FUNC_ATTR_NONNULL_ALL
+{
+ switch (cb->type) {
+ case kCallbackPartial:
+ tv->v_type = VAR_PARTIAL;
+ tv->vval.v_partial = cb->data.partial;
+ cb->data.partial->pt_refcount++;
+ break;
+ case kCallbackFuncref:
+ tv->v_type = VAR_FUNC;
+ tv->vval.v_string = vim_strsave(cb->data.funcref);
+ func_ref(cb->data.funcref);
+ break;
+ default:
+ tv->v_type = VAR_SPECIAL;
+ tv->vval.v_special = kSpecialVarNull;
+ break;
+ }
+}
+
+// Copy callback from "src" to "dest", incrementing the refcounts.
+void callback_copy(Callback *dest, Callback *src)
+ FUNC_ATTR_NONNULL_ALL
+{
+ dest->type = src->type;
+ switch (src->type) {
+ case kCallbackPartial:
+ dest->data.partial = src->data.partial;
+ dest->data.partial->pt_refcount++;
+ break;
+ case kCallbackFuncref:
+ dest->data.funcref = vim_strsave(src->data.funcref);
+ func_ref(src->data.funcref);
+ break;
+ default:
+ dest->data.funcref = NULL;
+ break;
+ }
}
/// Remove watcher from a dictionary
@@ -1951,7 +1994,7 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2,
} else if (*action == 'f' && di2 != di1) {
typval_T oldtv;
- if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len)
+ if (var_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len)
|| var_check_ro(di1->di_flags, arg_errmsg, arg_errmsg_len)) {
break;
}
@@ -2582,7 +2625,7 @@ bool tv_islocked(const typval_T *const tv)
///
/// Also gives an error message when typval is locked.
///
-/// @param[in] lock Lock status.
+/// @param[in] tv Typval.
/// @param[in] name Variable name, used in the error message.
/// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate
/// variable name and compute the length. Use #TV_CSTRING
@@ -2596,10 +2639,37 @@ bool tv_islocked(const typval_T *const tv)
/// gettext.
///
/// @return true if variable is locked, false otherwise.
-bool tv_check_lock(const VarLockStatus lock, const char *name,
+bool tv_check_lock(const typval_T *tv, const char *name,
size_t name_len)
FUNC_ATTR_WARN_UNUSED_RESULT
{
+ VarLockStatus lock = VAR_UNLOCKED;
+
+ switch (tv->v_type) {
+ // case VAR_BLOB:
+ // if (tv->vval.v_blob != NULL)
+ // lock = tv->vval.v_blob->bv_lock;
+ // break;
+ case VAR_LIST:
+ if (tv->vval.v_list != NULL) {
+ lock = tv->vval.v_list->lv_lock;
+ }
+ break;
+ case VAR_DICT:
+ if (tv->vval.v_dict != NULL) {
+ lock = tv->vval.v_dict->dv_lock;
+ }
+ break;
+ default:
+ break;
+ }
+ return var_check_lock(tv->v_lock, name, name_len)
+ || (lock != VAR_UNLOCKED && var_check_lock(lock, name, name_len));
+}
+
+/// @return true if variable "name" is locked (immutable)
+bool var_check_lock(VarLockStatus lock, const char *name, size_t name_len)
+{
const char *error_message = NULL;
switch (lock) {
case VAR_UNLOCKED: {
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index 2b4612016b..050b84efec 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -120,7 +120,7 @@ typedef enum {
VAR_DICT, ///< Dictionary, .v_dict is used.
VAR_FLOAT, ///< Floating-point value, .v_float is used.
VAR_BOOL, ///< true, false
- VAR_SPECIAL, ///< Special value (true, false, null), .v_special
+ VAR_SPECIAL, ///< Special value (null), .v_special
///< is used.
VAR_PARTIAL, ///< Partial, .v_partial is used.
} VarType;
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index f5d1b1e870..5ffc06ec44 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -2455,13 +2455,13 @@ void ex_function(exarg_T *eap)
goto erret;
}
if (fudi.fd_di == NULL) {
- if (tv_check_lock(fudi.fd_dict->dv_lock, (const char *)eap->arg,
- TV_CSTRING)) {
+ if (var_check_lock(fudi.fd_dict->dv_lock, (const char *)eap->arg,
+ TV_CSTRING)) {
// Can't add a function to a locked dictionary
goto erret;
}
- } else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, (const char *)eap->arg,
- TV_CSTRING)) {
+ } else if (var_check_lock(fudi.fd_di->di_tv.v_lock, (const char *)eap->arg,
+ TV_CSTRING)) {
// Can't change an existing function if it is locked
goto erret;
}
diff --git a/src/nvim/event/multiqueue.c b/src/nvim/event/multiqueue.c
index 1e6d62135c..f534fc483f 100644
--- a/src/nvim/event/multiqueue.c
+++ b/src/nvim/event/multiqueue.c
@@ -73,7 +73,7 @@ struct multiqueue_item {
struct multiqueue {
MultiQueue *parent;
QUEUE headtail; // circularly-linked
- put_callback put_cb;
+ PutCallback put_cb;
void *data;
size_t size;
};
@@ -91,7 +91,7 @@ typedef struct {
static Event NILEVENT = { .handler = NULL, .argv = {NULL} };
-MultiQueue *multiqueue_new_parent(put_callback put_cb, void *data)
+MultiQueue *multiqueue_new_parent(PutCallback put_cb, void *data)
{
return multiqueue_new(NULL, put_cb, data);
}
@@ -104,7 +104,7 @@ MultiQueue *multiqueue_new_child(MultiQueue *parent)
return multiqueue_new(parent, NULL, NULL);
}
-static MultiQueue *multiqueue_new(MultiQueue *parent, put_callback put_cb,
+static MultiQueue *multiqueue_new(MultiQueue *parent, PutCallback put_cb,
void *data)
{
MultiQueue *rv = xmalloc(sizeof(MultiQueue));
diff --git a/src/nvim/event/multiqueue.h b/src/nvim/event/multiqueue.h
index a688107665..dc60fbb4c7 100644
--- a/src/nvim/event/multiqueue.h
+++ b/src/nvim/event/multiqueue.h
@@ -7,7 +7,7 @@
#include "nvim/lib/queue.h"
typedef struct multiqueue MultiQueue;
-typedef void (*put_callback)(MultiQueue *multiq, void *data);
+typedef void (*PutCallback)(MultiQueue *multiq, void *data);
#define multiqueue_put(q, h, ...) \
multiqueue_put_event(q, event_create(h, __VA_ARGS__));
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 6a0a08eee8..4af7794317 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -3344,6 +3344,15 @@ static char_u *sub_parse_flags(char_u *cmd, subflags_T *subflags,
return cmd;
}
+static int check_regexp_delim(int c)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (isalpha(c)) {
+ EMSG(_("E146: Regular expressions can't be delimited by letters"));
+ return FAIL;
+ }
+ return OK;
+}
/// Perform a substitution from line eap->line1 to line eap->line2 using the
/// command pointed to by eap->arg which should be of the form:
@@ -3408,16 +3417,14 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
/* new pattern and substitution */
if (eap->cmd[0] == 's' && *cmd != NUL && !ascii_iswhite(*cmd)
&& vim_strchr((char_u *)"0123456789cegriIp|\"", *cmd) == NULL) {
- /* don't accept alphanumeric for separator */
- if (isalpha(*cmd)) {
- EMSG(_("E146: Regular expressions can't be delimited by letters"));
+ // don't accept alphanumeric for separator
+ if (check_regexp_delim(*cmd) == FAIL) {
return NULL;
}
- /*
- * undocumented vi feature:
- * "\/sub/" and "\?sub?" use last used search pattern (almost like
- * //sub/r). "\&sub&" use last substitute pattern (like //sub/).
- */
+
+ // undocumented vi feature:
+ // "\/sub/" and "\?sub?" use last used search pattern (almost like
+ // //sub/r). "\&sub&" use last substitute pattern (like //sub/).
if (*cmd == '\\') {
++cmd;
if (vim_strchr((char_u *)"/?&", *cmd) == NULL) {
@@ -4230,6 +4237,8 @@ skip:
}
}
+ curbuf->deleted_bytes2 = 0;
+
if (first_line != 0) {
/* Need to subtract the number of added lines from "last_line" to get
* the line number before the change (same as adding the number of
@@ -4455,6 +4464,8 @@ void ex_global(exarg_T *eap)
} else if (*cmd == NUL) {
EMSG(_("E148: Regular expression missing from global"));
return;
+ } else if (check_regexp_delim(*cmd) == FAIL) {
+ return;
} else {
delim = *cmd; /* get the delimiter */
if (delim)
@@ -4773,8 +4784,9 @@ void ex_help(exarg_T *eap)
* window. */
if (empty_fnum != 0 && curbuf->b_fnum != empty_fnum) {
buf = buflist_findnr(empty_fnum);
- if (buf != NULL && buf->b_nwindows == 0)
- wipe_buffer(buf, TRUE);
+ if (buf != NULL && buf->b_nwindows == 0) {
+ wipe_buffer(buf, true);
+ }
}
/* keep the previous alternate file */
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 9abeee47f4..9d500a8ddb 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -2665,7 +2665,8 @@ static void cmd_source_buffer(const exarg_T *eap)
};
if (curbuf != NULL && curbuf->b_fname
&& path_with_extension((const char *)curbuf->b_fname, "lua")) {
- nlua_source_using_linegetter(get_buffer_line, (void *)&cookie, ":source");
+ nlua_source_using_linegetter(get_buffer_line, (void *)&cookie,
+ ":source (no file)");
} else {
source_using_linegetter((void *)&cookie, get_buffer_line,
":source (no file)");
@@ -3002,14 +3003,23 @@ int do_source(char_u *fname, int check_other, int is_vimrc)
}
if (path_with_extension((const char *)fname, "lua")) {
+ // TODO(shadmansaleh): Properly handle :verbose for lua
+ // For now change currennt_sctx before sourcing lua files
+ // So verbose doesn't say everything was done in line 1 since we don't know
+ const sctx_T current_sctx_backup = current_sctx;
+ const linenr_T sourcing_lnum_backup = sourcing_lnum;
+ current_sctx.sc_lnum = 0;
+ sourcing_lnum = 0;
// Source the file as lua
- retval = (int)nlua_exec_file((const char *)fname);
+ nlua_exec_file((const char *)fname);
+ current_sctx = current_sctx_backup;
+ sourcing_lnum = sourcing_lnum_backup;
} else {
// Call do_cmdline, which will call getsourceline() to get the lines.
do_cmdline(firstline, getsourceline, (void *)&cookie,
DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT);
- retval = OK;
}
+ retval = OK;
if (l_do_profiling == PROF_YES) {
// Get "si" again, "script_items" may have been reallocated.
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 29347def4c..57d1339bb0 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -4184,10 +4184,6 @@ static char_u *invalid_range(exarg_T *eap)
}
break;
case ADDR_UNSIGNED:
- if (eap->line2 < 0) {
- return (char_u *)_(e_invrange);
- }
- break;
case ADDR_NONE:
// Will give an error elsewhere.
break;
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index afc387ef38..4d4286354b 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -1164,6 +1164,13 @@ static void nlua_typval_exec(const char *lcmd, size_t lcmd_len,
int nlua_source_using_linegetter(LineGetter fgetline,
void *cookie, char *name)
{
+ const linenr_T save_sourcing_lnum = sourcing_lnum;
+ const sctx_T save_current_sctx = current_sctx;
+ current_sctx.sc_sid = SID_STR;
+ current_sctx.sc_seq = 0;
+ current_sctx.sc_lnum = 0;
+ sourcing_lnum = 0;
+
garray_T ga;
char_u *line = NULL;
@@ -1174,6 +1181,9 @@ int nlua_source_using_linegetter(LineGetter fgetline,
char *code = (char *)ga_concat_strings_sep(&ga, "\n");
size_t len = strlen(code);
nlua_typval_exec(code, len, name, NULL, 0, false, NULL);
+
+ sourcing_lnum = save_sourcing_lnum;
+ current_sctx = save_current_sctx;
ga_clear_strings(&ga);
xfree(code);
return OK;
diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c
index c186928ae2..e3fa48f530 100644
--- a/src/nvim/lua/treesitter.c
+++ b/src/nvim/lua/treesitter.c
@@ -1073,6 +1073,11 @@ static int node_rawquery(lua_State *L)
// TODO(bfredl): these are expensive allegedly,
// use a reuse list later on?
TSQueryCursor *cursor = ts_query_cursor_new();
+ // TODO(clason): API introduced after tree-sitter release 0.19.5
+ // remove guard when minimum ts version is bumped to 0.19.6+
+#ifdef NVIM_TS_HAS_SET_MATCH_LIMIT
+ ts_query_cursor_set_match_limit(cursor, 32);
+#endif
ts_query_cursor_exec(cursor, query, node);
bool captures = lua_toboolean(L, 3);
diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c
index 34acf64d83..feb54eae4a 100644
--- a/src/nvim/marktree.c
+++ b/src/nvim/marktree.c
@@ -356,6 +356,7 @@ void marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev)
y = y->level ? y->ptr[0] : NULL;
}
}
+ itr->i--;
}
b->n_keys--;
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index f2f6803665..855f63ba7b 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -2719,10 +2719,13 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
// Copy a block range into a register.
// If "exclude_trailing_space" is set, do not copy trailing whitespaces.
-static void yank_copy_line(yankreg_T *reg, const struct block_def *bd,
+static void yank_copy_line(yankreg_T *reg, struct block_def *bd,
size_t y_idx, bool exclude_trailing_space)
FUNC_ATTR_NONNULL_ALL
{
+ if (exclude_trailing_space) {
+ bd->endspaces = 0;
+ }
int size = bd->startspaces + bd->endspaces + bd->textlen;
assert(size >= 0);
char_u *pnew = xmallocz((size_t)size);
@@ -3992,6 +3995,7 @@ int do_join(size_t count,
del_lines((long)count - 1, false);
curwin->w_cursor.lnum = t;
curbuf_splice_pending--;
+ curbuf->deleted_bytes2 = 0;
/*
* Set the cursor column:
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 57b8fe1a2e..388bedc043 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -85,6 +85,7 @@
#include "nvim/api/private/helpers.h"
#include "nvim/os/input.h"
#include "nvim/os/lang.h"
+#include "nvim/quickfix.h"
/*
* The options that are local to a window or buffer have "indir" set to one of
@@ -3182,6 +3183,10 @@ ambw_end:
}
}
}
+ } else if (varp == &p_qftf) {
+ if (!qf_process_qftf_option()) {
+ errmsg = e_invarg;
+ }
} else {
// Options that are a list of flags.
p = NULL;
@@ -7760,6 +7765,7 @@ static Dictionary vimoption2dict(vimoption_T *opt)
}
PUT(dict, "type", CSTR_TO_OBJ(type));
PUT(dict, "default", def);
+ PUT(dict, "allows_duplicates", BOOL(!(opt->flags & P_NODUP)));
return dict;
}
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 86dec74f56..0b09686675 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -3152,7 +3152,7 @@ return {
full_name='wildmode', abbreviation='wim',
short_desc=N_("mode for 'wildchar' command-line expansion"),
type='string', list='onecomma', scope={'global'},
- deny_duplicates=true,
+ deny_duplicates=false,
vim=true,
varname='p_wim',
defaults={if_true={vi="", vim="full"}}
diff --git a/src/nvim/path.c b/src/nvim/path.c
index fe50be5ea1..6ac24182cc 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -180,6 +180,34 @@ const char *path_next_component(const char *fname)
return fname;
}
+/// Returns the length of the path head on the current platform.
+/// @return
+/// - 3 on windows
+/// - 1 otherwise
+int path_head_length(void)
+{
+#ifdef WIN32
+ return 3;
+#else
+ return 1;
+#endif
+}
+
+/// Returns true if path begins with characters denoting the head of a path
+/// (e.g. '/' on linux and 'D:' on windows).
+/// @param path The path to be checked.
+/// @return
+/// - True if path begins with a path head
+/// - False otherwise
+bool is_path_head(const char_u *path)
+{
+#ifdef WIN32
+ return isalpha(path[0]) && path[1] == ':';
+#else
+ return vim_ispathsep(*path);
+#endif
+}
+
/// Get a pointer to one character past the head of a path name.
/// Unix: after "/"; Win: after "c:\"
/// If there is no head, path is returned.
@@ -189,7 +217,7 @@ char_u *get_past_head(const char_u *path)
#ifdef WIN32
// May skip "c:"
- if (isalpha(path[0]) && path[1] == ':') {
+ if (is_path_head(path)) {
retval = path + 2;
}
#endif
@@ -1991,10 +2019,24 @@ char_u *path_shorten_fname(char_u *full_path, char_u *dir_name)
assert(dir_name != NULL);
size_t len = strlen((char *)dir_name);
+
+ // If dir_name is a path head, full_path can always be made relative.
+ if (len == (size_t)path_head_length() && is_path_head(dir_name)) {
+ return full_path + len;
+ }
+
+ // If full_path and dir_name do not match, it's impossible to make one
+ // relative to the other.
+ if (fnamencmp(dir_name, full_path, len) != 0) {
+ return NULL;
+ }
+
char_u *p = full_path + len;
- if (fnamencmp(dir_name, full_path, len) != 0
- || !vim_ispathsep(*p)) {
+ // If *p is not pointing to a path separator, this means that full_path's
+ // last directory name is longer than *dir_name's last directory, so they
+ // don't actually match.
+ if (!vim_ispathsep(*p)) {
return NULL;
}
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 1a9bbe26f0..71624baaf4 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -100,7 +100,7 @@ typedef struct qf_list_S {
char_u *qf_title; ///< title derived from the command that created
///< the error list or set by setqflist
typval_T *qf_ctx; ///< context set by setqflist/setloclist
- char_u *qf_qftf; ///< 'quickfixtextfunc' setting for this list
+ Callback qftf_cb; ///< 'quickfixtextfunc' callback function
struct dir_stack_T *qf_dir_stack;
char_u *qf_directory;
@@ -541,6 +541,9 @@ static int efm_to_regpat(const char_u *efm, int len, efm_T *fmt_ptr,
static efm_T *fmt_start = NULL; // cached across qf_parse_line() calls
+// callback function for 'quickfixtextfunc'
+static Callback qftf_cb;
+
static void free_efm_list(efm_T **efm_first)
{
for (efm_T *efm_ptr = *efm_first; efm_ptr != NULL; efm_ptr = *efm_first) {
@@ -1978,7 +1981,7 @@ static int copy_loclist_entries(const qf_list_T *from_qfl, qf_list_T *to_qfl)
}
/// Copy the specified location list 'from_qfl' to 'to_qfl'.
-static int copy_loclist(const qf_list_T *from_qfl, qf_list_T *to_qfl)
+static int copy_loclist(qf_list_T *from_qfl, qf_list_T *to_qfl)
FUNC_ATTR_NONNULL_ALL
{
// Some of the fields are populated by qf_add_entry()
@@ -2000,11 +2003,7 @@ static int copy_loclist(const qf_list_T *from_qfl, qf_list_T *to_qfl)
} else {
to_qfl->qf_ctx = NULL;
}
- if (from_qfl->qf_qftf != NULL) {
- to_qfl->qf_qftf = vim_strsave(from_qfl->qf_qftf);
- } else {
- to_qfl->qf_qftf = NULL;
- }
+ callback_copy(&to_qfl->qftf_cb, &from_qfl->qftf_cb);
if (from_qfl->qf_count) {
if (copy_loclist_entries(from_qfl, to_qfl) == FAIL) {
@@ -3385,7 +3384,7 @@ static void qf_free(qf_list_T *qfl)
XFREE_CLEAR(qfl->qf_title);
tv_free(qfl->qf_ctx);
qfl->qf_ctx = NULL;
- XFREE_CLEAR(qfl->qf_qftf);
+ callback_free(&qfl->qftf_cb);
qfl->qf_id = 0;
qfl->qf_changedtick = 0L;
}
@@ -3860,6 +3859,41 @@ static buf_T *qf_find_buf(qf_info_T *qi)
return NULL;
}
+// Process the 'quickfixtextfunc' option value.
+bool qf_process_qftf_option(void)
+{
+ typval_T *tv;
+ Callback cb;
+
+ if (p_qftf == NULL || *p_qftf == NUL) {
+ callback_free(&qftf_cb);
+ return true;
+ }
+
+ if (*p_qftf == '{') {
+ // Lambda expression
+ tv = eval_expr(p_qftf);
+ if (tv == NULL) {
+ return false;
+ }
+ } else {
+ // treat everything else as a function name string
+ tv = xcalloc(1, sizeof(*tv));
+ tv->v_type = VAR_STRING;
+ tv->vval.v_string = vim_strsave(p_qftf);
+ }
+
+ if (!callback_from_typval(&cb, tv)) {
+ tv_free(tv);
+ return false;
+ }
+
+ callback_free(&qftf_cb);
+ qftf_cb = cb;
+ tv_free(tv);
+ return true;
+}
+
/// Update the w:quickfix_title variable in the quickfix/location list window in
/// all the tab pages.
static void qf_update_win_titlevar(qf_info_T *qi)
@@ -3891,7 +3925,15 @@ static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last)
int qf_winid = 0;
if (IS_LL_STACK(qi)) {
- qf_winid = curwin->handle;
+ if (curwin->w_llist == qi) {
+ win = curwin;
+ } else {
+ win = qf_find_win_with_loclist(qi);
+ if (win == NULL) {
+ return;
+ }
+ }
+ qf_winid = (int)win->handle;
}
if (old_last == NULL) {
@@ -3928,7 +3970,9 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum,
int len;
buf_T *errbuf;
- if (qftf_str != NULL) {
+ // If the 'quickfixtextfunc' function returned an non-empty custom string
+ // for this entry, then use it.
+ if (qftf_str != NULL && *qftf_str != NUL) {
STRLCPY(IObuff, qftf_str, IOSIZE);
} else {
if (qfp->qf_module != NULL) {
@@ -3997,22 +4041,25 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum,
return OK;
}
+// Call the 'quickfixtextfunc' function to get the list of lines to display in
+// the quickfix window for the entries 'start_idx' to 'end_idx'.
static list_T *call_qftf_func(qf_list_T *qfl,
int qf_winid,
long start_idx,
long end_idx)
{
- char_u *qftf = p_qftf;
+ Callback *cb = &qftf_cb;
list_T *qftf_list = NULL;
// If 'quickfixtextfunc' is set, then use the user-supplied function to get
// the text to display. Use the local value of 'quickfixtextfunc' if it is
// set.
- if (qfl->qf_qftf != NULL) {
- qftf = qfl->qf_qftf;
+ if (qfl->qftf_cb.type != kCallbackNone) {
+ cb = &qfl->qftf_cb;
}
- if (qftf != NULL && *qftf != NUL) {
+ if (cb != NULL && cb->type != kCallbackNone) {
typval_T args[1];
+ typval_T rettv;
// create the dict argument
dict_T *const dict = tv_dict_alloc_lock(VAR_FIXED);
@@ -4026,8 +4073,16 @@ static list_T *call_qftf_func(qf_list_T *qfl,
args[0].v_type = VAR_DICT;
args[0].vval.v_dict = dict;
- qftf_list = call_func_retlist(qftf, 1, args);
- dict->dv_refcount--;
+ qftf_list = NULL;
+
+ if (callback_call(cb, 1, args, &rettv)) {
+ if (rettv.v_type == VAR_LIST) {
+ qftf_list = rettv.vval.v_list;
+ tv_list_ref(qftf_list);
+ }
+ tv_clear(&rettv);
+ }
+ tv_dict_unref(dict);
}
return qftf_list;
@@ -4064,6 +4119,7 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last,
if (qfl != NULL) {
char_u dirname[MAXPATHL];
int prev_bufnr = -1;
+ bool invalid_val = false;
*dirname = NUL;
@@ -4086,10 +4142,15 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last,
while (lnum < qfl->qf_count) {
char_u *qftf_str = NULL;
- if (qftf_li != NULL) {
- // Use the text supplied by the user defined function
- qftf_str = (char_u *)tv_get_string_chk(TV_LIST_ITEM_TV(qftf_li));
+ // Use the text supplied by the user defined function (if any).
+ // If the returned value is not string, then ignore the rest
+ // of the returned values and use the default.
+ if (qftf_li != NULL && !invalid_val) {
+ qftf_str = (char_u *)tv_get_string_chk(TV_LIST_ITEM_TV(qftf_li));
+ if (qftf_str == NULL) {
+ invalid_val = true;
}
+ }
if (qf_buf_add_line(qfl, buf, lnum, qfp, dirname, qftf_str,
prev_bufnr != qfp->qf_fnum) == FAIL) {
@@ -5660,7 +5721,7 @@ static void wipe_dummy_buffer(buf_T *buf, char_u *dirname_start)
// work when got_int is set.
enter_cleanup(&cs);
- wipe_buffer(buf, FALSE);
+ wipe_buffer(buf, true);
// Restore the error/interrupt/exception state if not discarded by a
// new aborting error, interrupt, or uncaught exception.
@@ -5796,7 +5857,9 @@ enum {
QF_GETLIST_SIZE = 0x80,
QF_GETLIST_TICK = 0x100,
QF_GETLIST_FILEWINID = 0x200,
- QF_GETLIST_ALL = 0x3FF,
+ QF_GETLIST_QFBUFNR = 0x400,
+ QF_GETLIST_QFTF = 0x800,
+ QF_GETLIST_ALL = 0xFFF,
};
/// Parse text from 'di' and return the quickfix list items.
@@ -5894,6 +5957,9 @@ static int qf_getprop_keys2flags(const dict_T *what, bool loclist)
if (loclist && tv_dict_find(what, S_LEN("filewinid")) != NULL) {
flags |= QF_GETLIST_FILEWINID;
}
+ if (tv_dict_find(what, S_LEN("quickfixtextfunc")) != NULL) {
+ flags |= QF_GETLIST_QFTF;
+ }
return flags;
}
@@ -5985,6 +6051,9 @@ static int qf_getprop_defaults(qf_info_T *qi,
if ((status == OK) && locstack && (flags & QF_GETLIST_FILEWINID)) {
status = tv_dict_add_nr(retdict, S_LEN("filewinid"), 0);
}
+ if ((status == OK) && (flags & QF_GETLIST_QFTF)) {
+ status = tv_dict_add_str(retdict, S_LEN("quickfixtextfunc"), "");
+ }
return status;
}
@@ -6060,6 +6129,26 @@ static int qf_getprop_idx(qf_list_T *qfl, int eidx, dict_T *retdict)
return tv_dict_add_nr(retdict, S_LEN("idx"), eidx);
}
+/// Return the 'quickfixtextfunc' function of a quickfix/location list
+/// @return OK or FAIL
+static int qf_getprop_qftf(qf_list_T *qfl, dict_T *retdict)
+ FUNC_ATTR_NONNULL_ALL
+{
+ int status;
+
+ if (qfl->qftf_cb.type != kCallbackNone) {
+ typval_T tv;
+
+ callback_put(&qfl->qftf_cb, &tv);
+ status = tv_dict_add_tv(retdict, S_LEN("quickfixtextfunc"), &tv);
+ tv_clear(&tv);
+ } else {
+ status = tv_dict_add_str(retdict, S_LEN("quickfixtextfunc"), "");
+ }
+
+ return status;
+}
+
/// Return quickfix/location list details (title) as a dictionary.
/// 'what' contains the details to return. If 'list_idx' is -1,
/// then current list is used. Otherwise the specified list is used.
@@ -6133,19 +6222,25 @@ int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
if ((status == OK) && (wp != NULL) && (flags & QF_GETLIST_FILEWINID)) {
status = qf_getprop_filewinid(wp, qi, retdict);
}
+ if ((status == OK) && (flags & QF_GETLIST_QFTF)) {
+ status = qf_getprop_qftf(qfl, retdict);
+ }
return status;
}
/// Set the current index in the specified quickfix list
-static int qf_setprop_qftf(qf_info_T *qi, qf_list_T *qfl,
- dictitem_T *di)
+/// @return OK
+static int qf_setprop_qftf(qf_list_T *qfl, dictitem_T *di)
+ FUNC_ATTR_NONNULL_ALL
{
- XFREE_CLEAR(qfl->qf_qftf);
- if (di->di_tv.v_type == VAR_STRING && di->di_tv.vval.v_string != NULL) {
- qfl->qf_qftf = vim_strsave(di->di_tv.vval.v_string);
- }
- return OK;
+ Callback cb;
+
+ callback_free(&qfl->qftf_cb);
+ if (callback_from_typval(&cb, &di->di_tv)) {
+ qfl->qftf_cb = cb;
+ }
+ return OK;
}
/// Add a new quickfix entry to list at 'qf_idx' in the stack 'qi' from the
@@ -6514,7 +6609,7 @@ static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action,
retval = qf_setprop_curidx(qi, qfl, di);
}
if ((di = tv_dict_find(what, S_LEN("quickfixtextfunc"))) != NULL) {
- retval = qf_setprop_qftf(qi, qfl, di);
+ retval = qf_setprop_qftf(qfl, di);
}
if (newlist || retval == OK) {
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 04157a0154..3446a944cd 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -7211,7 +7211,24 @@ void ui_ext_tabline_update(void)
ADD(tabs, DICTIONARY_OBJ(tab_info));
}
- ui_call_tabline_update(curtab->handle, tabs);
+
+ Array buffers = ARRAY_DICT_INIT;
+ FOR_ALL_BUFFERS(buf) {
+ // Do not include unlisted buffers
+ if (!buf->b_p_bl) {
+ continue;
+ }
+
+ Dictionary buffer_info = ARRAY_DICT_INIT;
+ PUT(buffer_info, "buffer", BUFFER_OBJ(buf->handle));
+
+ get_trans_bufname(buf);
+ PUT(buffer_info, "name", STRING_OBJ(cstr_to_string((char *)NameBuff)));
+
+ ADD(buffers, DICTIONARY_OBJ(buffer_info));
+ }
+
+ ui_call_tabline_update(curtab->handle, tabs, curbuf->handle, buffers);
}
/*
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index bb84fa498e..ad28118f16 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -108,19 +108,19 @@ func Test_bufunload()
autocmd BufWipeout * call add(s:li, "bufwipeout")
augroup END
- let s:li=[]
+ let s:li = []
new
setlocal bufhidden=
bunload
call assert_equal(["bufunload", "bufdelete"], s:li)
- let s:li=[]
+ let s:li = []
new
setlocal bufhidden=delete
bunload
call assert_equal(["bufunload", "bufdelete"], s:li)
- let s:li=[]
+ let s:li = []
new
setlocal bufhidden=unload
bwipeout
@@ -196,6 +196,29 @@ func Test_autocmd_bufunload_avoiding_SEGV_02()
bwipe! a.txt
endfunc
+func Test_autocmd_dummy_wipeout()
+ " prepare files
+ call writefile([''], 'Xdummywipetest1.txt')
+ call writefile([''], 'Xdummywipetest2.txt')
+ augroup test_bufunload_group
+ autocmd!
+ autocmd BufUnload * call add(s:li, "bufunload")
+ autocmd BufDelete * call add(s:li, "bufdelete")
+ autocmd BufWipeout * call add(s:li, "bufwipeout")
+ augroup END
+
+ let s:li = []
+ split Xdummywipetest1.txt
+ silent! vimgrep /notmatched/ Xdummywipetest*
+ call assert_equal(["bufunload", "bufwipeout"], s:li)
+
+ bwipeout
+ call delete('Xdummywipetest1.txt')
+ call delete('Xdummywipetest2.txt')
+ au! test_bufunload_group
+ augroup! test_bufunload_group
+endfunc
+
func Test_win_tab_autocmd()
let g:record = []
@@ -428,7 +451,7 @@ func Test_autocmd_bufwipe_in_SessLoadPost()
let content =<< trim [CODE]
set nocp noswapfile
- let v:swapchoice="e"
+ let v:swapchoice = "e"
augroup test_autocmd_sessionload
autocmd!
autocmd SessionLoadPost * exe bufnr("Xsomething") . "bw!"
@@ -537,92 +560,92 @@ func Test_OptionSet()
au OptionSet * :call s:AutoCommandOptionSet(expand("<amatch>"))
" 1: Setting number option"
- let g:options=[['number', 0, 1, 'global']]
+ let g:options = [['number', 0, 1, 'global']]
set nu
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 2: Setting local number option"
- let g:options=[['number', 1, 0, 'local']]
+ let g:options = [['number', 1, 0, 'local']]
setlocal nonu
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 3: Setting global number option"
- let g:options=[['number', 1, 0, 'global']]
+ let g:options = [['number', 1, 0, 'global']]
setglobal nonu
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 4: Setting local autoindent option"
- let g:options=[['autoindent', 0, 1, 'local']]
+ let g:options = [['autoindent', 0, 1, 'local']]
setlocal ai
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 5: Setting global autoindent option"
- let g:options=[['autoindent', 0, 1, 'global']]
+ let g:options = [['autoindent', 0, 1, 'global']]
setglobal ai
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 6: Setting global autoindent option"
- let g:options=[['autoindent', 1, 0, 'global']]
+ let g:options = [['autoindent', 1, 0, 'global']]
set ai!
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" Should not print anything, use :noa
" 7: don't trigger OptionSet"
- let g:options=[['invalid', 1, 1, 'invalid']]
+ let g:options = [['invalid', 1, 1, 'invalid']]
noa set nonu
call assert_equal([['invalid', 1, 1, 'invalid']], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 8: Setting several global list and number option"
- let g:options=[['list', 0, 1, 'global'], ['number', 0, 1, 'global']]
+ let g:options = [['list', 0, 1, 'global'], ['number', 0, 1, 'global']]
set list nu
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 9: don't trigger OptionSet"
- let g:options=[['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']]
+ let g:options = [['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']]
noa set nolist nonu
call assert_equal([['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 10: Setting global acd"
- let g:options=[['autochdir', 0, 1, 'local']]
+ let g:options = [['autochdir', 0, 1, 'local']]
setlocal acd
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 11: Setting global autoread (also sets local value)"
- let g:options=[['autoread', 0, 1, 'global']]
+ let g:options = [['autoread', 0, 1, 'global']]
set ar
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 12: Setting local autoread"
- let g:options=[['autoread', 1, 1, 'local']]
+ let g:options = [['autoread', 1, 1, 'local']]
setlocal ar
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 13: Setting global autoread"
- let g:options=[['autoread', 1, 0, 'global']]
+ let g:options = [['autoread', 1, 0, 'global']]
setglobal invar
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 14: Setting option backspace through :let"
- let g:options=[['backspace', '', 'eol,indent,start', 'global']]
- let &bs="eol,indent,start"
+ let g:options = [['backspace', '', 'eol,indent,start', 'global']]
+ let &bs = "eol,indent,start"
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 15: Setting option backspace through setbufvar()"
- let g:options=[['backup', 0, 1, 'local']]
+ let g:options = [['backup', 0, 1, 'local']]
" try twice, first time, shouldn't trigger because option name is invalid,
" second time, it should trigger
call assert_fails("call setbufvar(1, '&l:bk', 1)", "E355")
@@ -632,13 +655,13 @@ func Test_OptionSet()
call assert_equal(g:opt[0], g:opt[1])
" 16: Setting number option using setwinvar"
- let g:options=[['number', 0, 1, 'local']]
+ let g:options = [['number', 0, 1, 'local']]
call setwinvar(0, '&number', 1)
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 17: Setting key option, shouldn't trigger"
- let g:options=[['key', 'invalid', 'invalid1', 'invalid']]
+ let g:options = [['key', 'invalid', 'invalid1', 'invalid']]
setlocal key=blah
setlocal key=
call assert_equal([['key', 'invalid', 'invalid1', 'invalid']], g:options)
@@ -646,13 +669,13 @@ func Test_OptionSet()
" 18: Setting string option"
let oldval = &tags
- let g:options=[['tags', oldval, 'tagpath', 'global']]
+ let g:options = [['tags', oldval, 'tagpath', 'global']]
set tags=tagpath
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 1l: Resetting string option"
- let g:options=[['tags', 'tagpath', oldval, 'global']]
+ let g:options = [['tags', 'tagpath', oldval, 'global']]
set tags&
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -672,7 +695,7 @@ func Test_OptionSet_diffmode()
call test_override('starting', 1)
" 18: Changing an option when entering diff mode
new
- au OptionSet diff :let &l:cul=v:option_new
+ au OptionSet diff :let &l:cul = v:option_new
call setline(1, ['buffer 1', 'line2', 'line3', 'line4'])
call assert_equal(0, &l:cul)
@@ -1754,7 +1777,7 @@ func Test_autocmd_CmdWinEnter()
autocmd CmdWinEnter * quit
let winnr = winnr('$')
END
- let filename='XCmdWinEnter'
+ let filename = 'XCmdWinEnter'
call writefile(lines, filename)
let buf = RunVimInTerminal('-S '.filename, #{rows: 6})
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 71a7a2cce5..eb6151fbe1 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -161,6 +161,8 @@ let s:filename_checks = {
\ 'ecd': ['file.ecd'],
\ 'edif': ['file.edf', 'file.edif', 'file.edo'],
\ 'elinks': ['elinks.conf'],
+ \ 'elixir': ['file.ex', 'file.exs', 'mix.lock'],
+ \ 'eelixir': ['file.eex', 'file.leex'],
\ 'elm': ['file.elm'],
\ 'elmfilt': ['filter-rules'],
\ 'epuppet': ['file.epp'],
@@ -257,7 +259,7 @@ let s:filename_checks = {
\ 'jgraph': ['file.jgr'],
\ 'jovial': ['file.jov', 'file.j73', 'file.jovial'],
\ 'jproperties': ['file.properties', 'file.properties_xx', 'file.properties_xx_xx', 'some.properties_xx_xx_file'],
- \ 'json': ['file.json', 'file.jsonp', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb'],
+ \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb'],
\ 'jsp': ['file.jsp'],
\ 'kconfig': ['Kconfig', 'Kconfig.debug', 'Kconfig.file'],
\ 'kivy': ['file.kv'],
@@ -422,7 +424,7 @@ let s:filename_checks = {
\ 'sass': ['file.sass'],
\ 'sather': ['file.sa'],
\ 'sbt': ['file.sbt'],
- \ 'scala': ['file.scala'],
+ \ 'scala': ['file.scala', 'file.sc'],
\ 'scheme': ['file.scm', 'file.ss', 'file.rkt'],
\ 'scilab': ['file.sci', 'file.sce'],
\ 'screen': ['.screenrc', 'screenrc'],
@@ -765,5 +767,41 @@ func Test_pp_file()
filetype off
endfunc
+func Test_ex_file()
+ filetype on
+
+ call writefile(['arbitrary content'], 'Xfile.ex')
+ split Xfile.ex
+ call assert_equal('elixir', &filetype)
+ bwipe!
+ let g:filetype_euphoria = 'euphoria4'
+ split Xfile.ex
+ call assert_equal('euphoria4', &filetype)
+ bwipe!
+ unlet g:filetype_euphoria
+
+ call writefile(['-- filetype euphoria comment'], 'Xfile.ex')
+ split Xfile.ex
+ call assert_equal('euphoria3', &filetype)
+ bwipe!
+
+ call writefile(['--filetype euphoria comment'], 'Xfile.ex')
+ split Xfile.ex
+ call assert_equal('euphoria3', &filetype)
+ bwipe!
+
+ call writefile(['ifdef '], 'Xfile.ex')
+ split Xfile.ex
+ call assert_equal('euphoria3', &filetype)
+ bwipe!
+
+ call writefile(['include '], 'Xfile.ex')
+ split Xfile.ex
+ call assert_equal('euphoria3', &filetype)
+ bwipe!
+
+ call delete('Xfile.ex')
+ filetype off
+endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_global.vim b/src/nvim/testdir/test_global.vim
index 2de2c412de..8edc9c2608 100644
--- a/src/nvim/testdir/test_global.vim
+++ b/src/nvim/testdir/test_global.vim
@@ -36,4 +36,8 @@ func Test_global_error()
call assert_fails('g/\(/y', 'E476:')
endfunc
+func Test_wrong_delimiter()
+ call assert_fails('g x^bxd', 'E146:')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim
index a5cbd8f6a6..6cb736a38a 100644
--- a/src/nvim/testdir/test_let.vim
+++ b/src/nvim/testdir/test_let.vim
@@ -126,11 +126,16 @@ endfunction
func s:set_varg7(...) abort
let b = a:000
- call add(b, 1)
+ let b += [1]
endfunction
func s:set_varg8(...) abort
let b = a:000
+ call add(b, 1)
+endfunction
+
+func s:set_varg9(...) abort
+ let b = a:000
let b[0][0] = 1
endfunction
@@ -142,7 +147,8 @@ func Test_let_varg_fail()
call s:set_varg5([0])
call assert_fails('call s:set_varg6(1)', 'E742:')
call assert_fails('call s:set_varg7(1)', 'E742:')
- call s:set_varg8([0])
+ call assert_fails('call s:set_varg8(1)', 'E742:')
+ call s:set_varg9([0])
endfunction
func Test_let_utf8_environment()
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index 14240f0d5f..6bd64caa6c 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -891,7 +891,7 @@ func Test_efm1()
Xtestfile:9: parse error before `asd'
make: *** [vim] Error 1
in file "Xtestfile" linenr 10: there is an error
-
+
2 returned
"Xtestfile", line 11 col 1; this is an error
"Xtestfile", line 12 col 2; this is another error
@@ -914,7 +914,7 @@ func Test_efm1()
x should be a dot
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 20
^
-
+
Does anyone know what is the problem and how to correction it?
"Xtestfile", line 21 col 9: What is the title of the quickfix window?
"Xtestfile", line 22 col 9: What is the title of the quickfix window?
@@ -3483,12 +3483,13 @@ func Xgetlist_empty_tests(cchar)
if a:cchar == 'c'
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0,
\ 'items' : [], 'nr' : 0, 'size' : 0,
- \ 'title' : '', 'winid' : 0, 'changedtick': 0},
- \ g:Xgetlist({'all' : 0}))
+ \ 'title' : '', 'winid' : 0, 'changedtick': 0,
+ \ 'quickfixtextfunc' : ''}, g:Xgetlist({'all' : 0}))
else
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0,
\ 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '',
- \ 'winid' : 0, 'changedtick': 0, 'filewinid' : 0},
+ \ 'winid' : 0, 'changedtick': 0, 'filewinid' : 0,
+ \ 'quickfixtextfunc' : ''},
\ g:Xgetlist({'all' : 0}))
endif
@@ -3526,11 +3527,13 @@ func Xgetlist_empty_tests(cchar)
if a:cchar == 'c'
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [],
\ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0,
+ \ 'quickfixtextfunc' : '',
\ 'changedtick' : 0}, g:Xgetlist({'id' : qfid, 'all' : 0}))
else
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [],
\ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0,
- \ 'changedtick' : 0, 'filewinid' : 0},
+ \ 'changedtick' : 0, 'filewinid' : 0,
+ \ 'quickfixtextfunc' : ''},
\ g:Xgetlist({'id' : qfid, 'all' : 0}))
endif
@@ -3547,12 +3550,13 @@ func Xgetlist_empty_tests(cchar)
if a:cchar == 'c'
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [],
\ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0,
- \ 'changedtick' : 0}, g:Xgetlist({'nr' : 5, 'all' : 0}))
+ \ 'changedtick' : 0,
+ \ 'quickfixtextfunc' : ''}, g:Xgetlist({'nr' : 5, 'all' : 0}))
else
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [],
\ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0,
- \ 'changedtick' : 0, 'filewinid' : 0},
- \ g:Xgetlist({'nr' : 5, 'all' : 0}))
+ \ 'changedtick' : 0, 'filewinid' : 0,
+ \ 'quickfixtextfunc' : ''}, g:Xgetlist({'nr' : 5, 'all' : 0}))
endif
endfunc
@@ -4994,6 +4998,9 @@ func Xtest_qftextfunc(cchar)
set efm=%f:%l:%c:%m
set quickfixtextfunc=Tqfexpr
+ call assert_equal('Tqfexpr', &quickfixtextfunc)
+ call assert_equal('',
+ \ g:Xgetlist({'quickfixtextfunc' : 1}).quickfixtextfunc)
Xexpr ['F1:10:2:green', 'F1:20:4:blue']
Xwindow
call assert_equal('F1-L10C2-green', getline(1))
@@ -5030,12 +5037,15 @@ func Xtest_qftextfunc(cchar)
call assert_equal('Line 10, Col 2', getline(1))
call assert_equal('Line 20, Col 4', getline(2))
Xclose
+ call assert_equal(function('PerQfText'),
+ \ g:Xgetlist({'quickfixtextfunc' : 1}).quickfixtextfunc)
" Add entries to the list when the quickfix buffer is hidden
Xaddexpr ['F1:30:6:red']
Xwindow
call assert_equal('Line 30, Col 6', getline(3))
Xclose
call g:Xsetlist([], 'r', {'quickfixtextfunc' : ''})
+ call assert_equal('', g:Xgetlist({'quickfixtextfunc' : 1}).quickfixtextfunc)
set quickfixtextfunc&
delfunc PerQfText
@@ -5074,12 +5084,53 @@ func Xtest_qftextfunc(cchar)
" \ 'E730:')
Xexpr ['F1:10:2:green', 'F1:20:4:blue', 'F1:30:6:red']
call assert_fails('Xwindow', 'E730:')
- call assert_equal(['one', 'F1|20 col 4| blue', 'two'], getline(1, '$'))
+ call assert_equal(['one', 'F1|20 col 4| blue', 'F1|30 col 6| red'],
+ \ getline(1, '$'))
Xclose
set quickfixtextfunc&
delfunc Xqftext
delfunc Xqftext2
+
+ " set the global option to a lambda function
+ set quickfixtextfunc={d\ ->\ map(g:Xgetlist({'id'\ :\ d.id,\ 'items'\ :\ 1}).items[d.start_idx-1:d.end_idx-1],\ 'v:val.text')}
+ Xexpr ['F1:10:2:green', 'F1:20:4:blue']
+ Xwindow
+ call assert_equal(['green', 'blue'], getline(1, '$'))
+ Xclose
+ call assert_equal("{d -> map(g:Xgetlist({'id' : d.id, 'items' : 1}).items[d.start_idx-1:d.end_idx-1], 'v:val.text')}", &quickfixtextfunc)
+ set quickfixtextfunc&
+
+ " use a lambda function that returns an empty list
+ set quickfixtextfunc={d\ ->\ []}
+ Xexpr ['F1:10:2:green', 'F1:20:4:blue']
+ Xwindow
+ call assert_equal(['F1|10 col 2| green', 'F1|20 col 4| blue'],
+ \ getline(1, '$'))
+ Xclose
+ set quickfixtextfunc&
+
+ " use a lambda function that returns a list with empty strings
+ set quickfixtextfunc={d\ ->\ ['',\ '']}
+ Xexpr ['F1:10:2:green', 'F1:20:4:blue']
+ Xwindow
+ call assert_equal(['F1|10 col 2| green', 'F1|20 col 4| blue'],
+ \ getline(1, '$'))
+ Xclose
+ set quickfixtextfunc&
+
+ " set the per-quickfix list text function to a lambda function
+ call g:Xsetlist([], ' ',
+ \ {'quickfixtextfunc' :
+ \ {d -> map(g:Xgetlist({'id' : d.id, 'items' : 1}).items[d.start_idx-1:d.end_idx-1],
+ \ "'Line ' .. v:val.lnum .. ', Col ' .. v:val.col")}})
+ Xaddexpr ['F1:10:2:green', 'F1:20:4:blue']
+ Xwindow
+ call assert_equal('Line 10, Col 2', getline(1))
+ call assert_equal('Line 20, Col 4', getline(2))
+ Xclose
+ call assert_match("function('<lambda>\\d\\+')", string(g:Xgetlist({'quickfixtextfunc' : 1}).quickfixtextfunc))
+ call g:Xsetlist([], 'f')
endfunc
func Test_qftextfunc()
@@ -5087,4 +5138,62 @@ func Test_qftextfunc()
call Xtest_qftextfunc('l')
endfunc
+" Test for updating a location list for some other window and check that
+" 'qftextfunc' uses the correct location list.
+func Test_qftextfunc_other_loclist()
+ %bw!
+ call setloclist(0, [], 'f')
+
+ " create a window and a location list for it and open the location list
+ " window
+ lexpr ['F1:10:12:one', 'F1:20:14:two']
+ let w1_id = win_getid()
+ call setloclist(0, [], ' ',
+ \ {'lines': ['F1:10:12:one', 'F1:20:14:two'],
+ \ 'quickfixtextfunc':
+ \ {d -> map(getloclist(d.winid, {'id' : d.id,
+ \ 'items' : 1}).items[d.start_idx-1:d.end_idx-1],
+ \ "'Line ' .. v:val.lnum .. ', Col ' .. v:val.col")}})
+ lwindow
+ let w2_id = win_getid()
+
+ " create another window and a location list for it and open the location
+ " list window
+ topleft new
+ let w3_id = win_getid()
+ call setloclist(0, [], ' ',
+ \ {'lines': ['F2:30:32:eleven', 'F2:40:34:twelve'],
+ \ 'quickfixtextfunc':
+ \ {d -> map(getloclist(d.winid, {'id' : d.id,
+ \ 'items' : 1}).items[d.start_idx-1:d.end_idx-1],
+ \ "'Ligne ' .. v:val.lnum .. ', Colonne ' .. v:val.col")}})
+ lwindow
+ let w4_id = win_getid()
+
+ topleft new
+ lexpr ['F3:50:52:green', 'F3:60:54:blue']
+ let w5_id = win_getid()
+
+ " change the location list for some other window
+ call setloclist(0, [], 'r', {'lines': ['F3:55:56:aaa', 'F3:57:58:bbb']})
+ call setloclist(w1_id, [], 'r', {'lines': ['F1:62:63:bbb', 'F1:64:65:ccc']})
+ call setloclist(w3_id, [], 'r', {'lines': ['F2:76:77:ddd', 'F2:78:79:eee']})
+ call assert_equal(['Line 62, Col 63', 'Line 64, Col 65'],
+ \ getbufline(winbufnr(w2_id), 1, '$'))
+ call assert_equal(['Ligne 76, Colonne 77', 'Ligne 78, Colonne 79'],
+ \ getbufline(winbufnr(w4_id), 1, '$'))
+ call setloclist(w2_id, [], 'r', {'lines': ['F1:32:33:fff', 'F1:34:35:ggg']})
+ call setloclist(w4_id, [], 'r', {'lines': ['F2:46:47:hhh', 'F2:48:49:jjj']})
+ call assert_equal(['Line 32, Col 33', 'Line 34, Col 35'],
+ \ getbufline(winbufnr(w2_id), 1, '$'))
+ call assert_equal(['Ligne 46, Colonne 47', 'Ligne 48, Colonne 49'],
+ \ getbufline(winbufnr(w4_id), 1, '$'))
+
+ call win_gotoid(w5_id)
+ lwindow
+ call assert_equal(['F3|55 col 56| aaa', 'F3|57 col 58| bbb'],
+ \ getline(1, '$'))
+ %bw!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
index a40b0236e0..b7c5717bd2 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -1,5 +1,8 @@
" Tests for various Visual modes.
+source shared.vim
+source check.vim
+
func Test_block_shift_multibyte()
" Uses double-wide character.
split
@@ -1060,5 +1063,24 @@ func Test_visual_put_in_block_using_zy_and_zp()
bwipe!
endfunc
+func Test_visual_put_blockedit_zy_and_zp()
+ new
+
+ call setline(1, ['aa', 'bbbbb', 'ccc', '', 'XX', 'GGHHJ', 'RTZU'])
+ exe "normal! gg0\<c-v>2j$zy"
+ norm! 5gg0zP
+ call assert_equal(['aa', 'bbbbb', 'ccc', '', 'aaXX', 'bbbbbGGHHJ', 'cccRTZU'], getline(1, 7))
+ "
+ " now with blockmode editing
+ sil %d
+ :set ve=block
+ call setline(1, ['aa', 'bbbbb', 'ccc', '', 'XX', 'GGHHJ', 'RTZU'])
+ exe "normal! gg0\<c-v>2j$zy"
+ norm! 5gg0zP
+ call assert_equal(['aa', 'bbbbb', 'ccc', '', 'aaXX', 'bbbbbGGHHJ', 'cccRTZU'], getline(1, 7))
+ set ve&vim
+ bw!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index fd83681aed..6e885279a9 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -52,21 +52,18 @@
#define OUTBUF_SIZE 0xffff
#define TOO_MANY_EVENTS 1000000
-#define STARTS_WITH(str, prefix) (strlen(str) >= (sizeof(prefix) - 1) \
- && 0 == memcmp((str), (prefix), sizeof(prefix) - 1))
-#define TMUX_WRAP(is_tmux, seq) ((is_tmux) \
- ? DCS_STR "tmux;\x1b" seq STERM_STR : seq)
-#define SCREEN_TMUX_WRAP(is_screen, is_tmux, seq) \
- ((is_screen) \
- ? DCS_STR seq STERM_STR : (is_tmux) \
- ? DCS_STR "tmux;\x1b" seq STERM_STR : seq)
+#define STARTS_WITH(str, prefix) \
+ (strlen(str) >= (sizeof(prefix) - 1) \
+ && 0 == memcmp((str), (prefix), sizeof(prefix) - 1))
+#define TMUX_WRAP(is_tmux, seq) \
+ ((is_tmux) ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq)
#define LINUXSET0C "\x1b[?0c"
#define LINUXSET1C "\x1b[?1c"
#ifdef NVIM_UNIBI_HAS_VAR_FROM
#define UNIBI_SET_NUM_VAR(var, num) \
do { \
- (var) = unibi_var_from_num((num)); \
+ (var) = unibi_var_from_num((num)); \
} while (0)
#else
#define UNIBI_SET_NUM_VAR(var, num) (var).i = (num);
@@ -301,12 +298,6 @@ static void terminfo_start(UI *ui)
data->invis, sizeof data->invis);
// Set 't_Co' from the result of unibilium & fix_terminfo.
t_colors = unibi_get_num(data->ut, unibi_max_colors);
- // Ask the terminal to send us the background color.
- // If get_bg is sent at the same time after enter_ca_mode, tmux will not send
- // get_bg to the host terminal. To avoid this, send get_bg before
- // enter_ca_mode.
- data->input.waiting_for_bg_response = 5;
- unibi_out_ext(ui, data->unibi_ext.get_bg);
// Enter alternate screen, save title, and clear.
// NOTE: Do this *before* changing terminal settings. #6433
unibi_out(ui, unibi_enter_ca_mode);
@@ -314,6 +305,9 @@ static void terminfo_start(UI *ui)
unibi_out_ext(ui, data->unibi_ext.save_title);
unibi_out(ui, unibi_keypad_xmit);
unibi_out(ui, unibi_clear_screen);
+ // Ask the terminal to send us the background color.
+ data->input.waiting_for_bg_response = 5;
+ unibi_out_ext(ui, data->unibi_ext.get_bg);
// Enable bracketed paste
unibi_out_ext(ui, data->unibi_ext.enable_bracketed_paste);
@@ -335,7 +329,6 @@ static void terminfo_start(UI *ui)
uv_pipe_init(&data->write_loop, &data->output_handle.pipe, 0);
uv_pipe_open(&data->output_handle.pipe, data->out_fd);
}
-
flush_buf(ui);
}
@@ -1780,10 +1773,8 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
#define XTERM_SETAB_16 \
"\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e39%;m"
- data->unibi_ext.get_bg =
- (int)unibi_add_ext_str(ut, "ext.get_bg",
- SCREEN_TMUX_WRAP((screen && !tmux), tmux,
- "\x1b]11;?\x07"));
+ data->unibi_ext.get_bg = (int)unibi_add_ext_str(ut, "ext.get_bg",
+ "\x1b]11;?\x07");
// Terminals with 256-colour SGR support despite what terminfo says.
if (unibi_get_num(ut, unibi_max_colors) < 256) {
diff --git a/src/nvim/window.c b/src/nvim/window.c
index aea60fe24c..d051e8e467 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -4597,6 +4597,18 @@ static win_T *win_alloc(win_T *after, int hidden)
}
+// Free one wininfo_T.
+void
+free_wininfo(wininfo_T *wip, buf_T *bp)
+{
+ if (wip->wi_optset) {
+ clear_winopt(&wip->wi_opt);
+ deleteFoldRecurse(bp, &wip->wi_folds);
+ }
+ xfree(wip);
+}
+
+
/*
* Remove window 'wp' from the window list and free the structure.
*/
@@ -4647,9 +4659,30 @@ win_free (
/* Remove the window from the b_wininfo lists, it may happen that the
* freed memory is re-used for another window. */
FOR_ALL_BUFFERS(buf) {
- for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next)
- if (wip->wi_win == wp)
+ for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) {
+ if (wip->wi_win == wp) {
+ wininfo_T *wip2;
+
+ // If there already is an entry with "wi_win" set to NULL it
+ // must be removed, it would never be used.
+ for (wip2 = buf->b_wininfo; wip2 != NULL; wip2 = wip2->wi_next) {
+ if (wip2->wi_win == NULL) {
+ if (wip2->wi_next != NULL) {
+ wip2->wi_next->wi_prev = wip2->wi_prev;
+ }
+ if (wip2->wi_prev == NULL) {
+ buf->b_wininfo = wip2->wi_next;
+ } else {
+ wip2->wi_prev->wi_next = wip2->wi_next;
+ }
+ free_wininfo(wip2, buf);
+ break;
+ }
+ }
+
wip->wi_win = NULL;
+ }
+ }
}
clear_matches(wp);