diff options
Diffstat (limited to 'src')
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); |