From 3b562535c323292637a53d36d9bd341feea2f2c6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 12 Oct 2022 07:47:28 +0800 Subject: vim-patch:8.2.4523: when gvim is started maximized the 'window' option isn't set Problem: When gvim is started maximized the 'window' option isn't set properly. (Christian J. Robinson) Solution: Check if 'windows' was already set or not. (Ken Takata, closes vim/vim#9904) https://github.com/vim/vim/commit/6ca883dd8a585a85acdf9303b434211ea91872a7 --- src/nvim/window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/window.c b/src/nvim/window.c index d7ca718c68..d99d22af12 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5226,7 +5226,7 @@ void win_new_screensize(void) if (old_Rows != Rows) { // If 'window' uses the whole screen, keep it using that. // Don't change it when set with "-w size" on the command line. - if (p_window == old_Rows - 1 || (old_Rows == 0 && p_window == 0)) { + if (p_window == old_Rows - 1 || (old_Rows == 0 && !option_was_set("window"))) { p_window = Rows - 1; } old_Rows = Rows; -- cgit From eeeb6c80d8f32efee8d13ec4a56a7d487a28eba0 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 12 Oct 2022 10:59:06 +0100 Subject: refactor(search.c): clint (#20598) --- src/nvim/search.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/search.c b/src/nvim/search.c index ed0f25cba0..e404d00b60 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -777,9 +777,8 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir, } } if (ptr[matchcol] == NUL - || (nmatched = - vim_regexec_multi(®match, win, buf, lnum + matchpos.lnum, matchcol, - tm, timed_out)) == 0) { + || (nmatched = vim_regexec_multi(®match, win, buf, lnum + matchpos.lnum, + matchcol, tm, timed_out)) == 0) { // If the search timed out, we did find a match // but it might be the wrong one, so that's not // OK. @@ -1331,9 +1330,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, break; } } - } - // to the left, check for start of file - else { + } else { // to the left, check for start of file while (c++ < 0) { if (decl(&pos) == -1) { break; @@ -1765,9 +1762,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) || STRNCMP(ptr, "el", 2) == 0) { hash_dir = 1; } - } - // Are we on a comment? - else if (linep[pos.col] == '/') { + } else if (linep[pos.col] == '/') { // Are we on a comment? if (linep[pos.col + 1] == '*') { comment_dir = FORWARD; backwards = false; @@ -1989,7 +1984,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) if (comment_dir) { // Note: comments do not nest, and we ignore quotes in them - // TODO: ignore comment brackets inside strings + // TODO(vim): ignore comment brackets inside strings if (comment_dir == FORWARD) { if (linep[pos.col] == '*' && linep[pos.col + 1] == '/') { pos.col++; @@ -3459,9 +3454,10 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo // when CONT_SOL is set compare "ptr" with the beginning of the // line is faster than quote_meta/regcomp/regexec "ptr" -- Acevedo && !compl_status_sol()) { - pat = xmalloc(len + 5); + size_t patlen = len + 5; + pat = xmalloc(patlen); assert(len <= INT_MAX); - sprintf((char *)pat, whole ? "\\<%.*s\\>" : "%.*s", (int)len, ptr); + snprintf((char *)pat, patlen, whole ? "\\<%.*s\\>" : "%.*s", (int)len, ptr); // ignore case according to p_ic, p_scs and pat regmatch.rm_ic = ignorecase(pat); regmatch.regprog = vim_regcomp((char *)pat, p_magic ? RE_MAGIC : 0); @@ -3748,13 +3744,12 @@ search_line: if (matched) { if (action == ACTION_EXPAND) { bool cont_s_ipos = false; - char_u *aux; if (depth == -1 && lnum == curwin->w_cursor.lnum) { break; } found = true; - aux = p = startp; + char_u *aux = p = startp; if (compl_status_adding()) { p += ins_compl_len(); if (vim_iswordp(p)) { @@ -3767,7 +3762,7 @@ search_line: if (compl_status_adding() && i == ins_compl_len()) { // IOSIZE > compl_length, so the STRNCPY works - STRNCPY(IObuff, aux, i); + STRNCPY(IObuff, aux, i); // NOLINT(runtime/printf) // Get the next line: when "depth" < 0 from the current // buffer, otherwise from the included file. Jump to @@ -3805,7 +3800,7 @@ search_line: if (p - aux >= IOSIZE - i) { p = aux + IOSIZE - i - 1; } - STRNCPY(IObuff + i, aux, p - aux); + STRNCPY(IObuff + i, aux, p - aux); // NOLINT(runtime/printf) i += (int)(p - aux); cont_s_ipos = true; } @@ -4056,7 +4051,7 @@ static void show_pat_in_path(char_u *line, int type, bool did_show, int action, if (vim_fgets(line, LSIZE, fp)) { // end of file break; } - ++*lnum; + (*lnum)++; } else { if (++*lnum > curbuf->b_ml.ml_line_count) { break; -- cgit From 4f305fba14ec8c1919ed793b6e09e15ca54a8c1d Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 12 Oct 2022 15:23:42 +0200 Subject: vim-patch:9.0.0731: clang-tidy configuration files are not recognized (#20620) Problem: clang-tidy configuration files are not recognized. Solution: Recognize clang-tidy files as yaml. (closes vim/vim#11350) https://github.com/vim/vim/commit/af40f9af335e0c8b167eac31ceace45b6a2e0565 --- src/nvim/testdir/test_filetype.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index d123d469a6..b637f1a16f 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -641,7 +641,7 @@ let s:filename_checks = { \ 'xsd': ['file.xsd'], \ 'xslt': ['file.xsl', 'file.xslt'], \ 'yacc': ['file.yy', 'file.yxx', 'file.y++'], - \ 'yaml': ['file.yaml', 'file.yml'], + \ 'yaml': ['file.yaml', 'file.yml', '.clang-tidy'], \ 'yang': ['file.yang'], \ 'z8a': ['file.z8a'], \ 'zig': ['file.zig'], -- cgit From 0ef6aaa3a73d5089bf53e804364950c81784574c Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 12 Oct 2022 14:53:40 +0100 Subject: refactor: clint (#20600) --- src/nvim/eval/decode.c | 40 ++++++++++++++++------------------------ src/nvim/eval/encode.c | 18 +++++++++--------- src/nvim/ex_cmds.c | 5 ++--- src/nvim/ex_eval.c | 6 ++---- src/nvim/file_search.c | 7 +++---- src/nvim/fileio.c | 24 ++++++++++++------------ src/nvim/garray.c | 2 +- src/nvim/main.c | 5 ++--- src/nvim/move.c | 3 +-- src/nvim/ops.c | 2 +- src/nvim/option.c | 15 ++++++--------- src/nvim/path.c | 4 ++-- src/nvim/tag.c | 3 +-- src/nvim/window.c | 2 +- 14 files changed, 59 insertions(+), 77 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 94ef419bed..6b7eb2f8f8 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -271,11 +271,9 @@ typval_T decode_string(const char *const s, const size_t len, const TriState has list_T *const list = tv_list_alloc(kListLenMayKnow); tv_list_ref(list); create_special_dict(&tv, kMPString, - ((typval_T){ - .v_type = VAR_LIST, - .v_lock = VAR_UNLOCKED, - .vval = { .v_list = list }, - })); + (typval_T){ .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list } }); const int elw_ret = encode_list_write((void *)list, s, len); if (s_allocated) { xfree((void *)s); @@ -368,7 +366,7 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len, goto parse_json_string_fail; } } else { - uint8_t p_byte = (uint8_t)*p; + uint8_t p_byte = (uint8_t)(*p); // unescaped = %x20-21 / %x23-5B / %x5D-10FFFF if (p_byte < 0x20) { semsg(_("E474: ASCII control characters cannot be present " @@ -469,7 +467,7 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len, ['r'] = CAR, ['f'] = FF, }; - *str_end++ = escapes[(int)*t]; + *str_end++ = escapes[(int)(*t)]; break; } default: @@ -838,12 +836,10 @@ json_decode_string_cycle_start: .v_lock = VAR_UNLOCKED, .vval = { .v_list = list }, }; - kv_push(container_stack, ((ContainerStackItem) { - .stack_index = kv_size(stack), - .s = p, - .container = tv, - .special_val = NULL, - })); + kv_push(container_stack, ((ContainerStackItem) { .stack_index = kv_size(stack), + .s = p, + .container = tv, + .special_val = NULL })); kv_push(stack, OBJ(tv, false, didcomma, didcolon)); break; } @@ -862,12 +858,10 @@ json_decode_string_cycle_start: .vval = { .v_dict = dict }, }; } - kv_push(container_stack, ((ContainerStackItem) { - .stack_index = kv_size(stack), - .s = p, - .container = tv, - .special_val = val_list, - })); + kv_push(container_stack, ((ContainerStackItem) { .stack_index = kv_size(stack), + .s = p, + .container = tv, + .special_val = val_list })); kv_push(stack, OBJ(tv, false, didcomma, didcolon)); break; } @@ -1089,11 +1083,9 @@ msgpack_to_vim_generic_map: {} tv_list_append_number(list, mobj.via.ext.type); list_T *const ext_val_list = tv_list_alloc(kListLenMayKnow); tv_list_append_list(list, ext_val_list); - create_special_dict(rettv, kMPExt, ((typval_T) { - .v_type = VAR_LIST, - .v_lock = VAR_UNLOCKED, - .vval = { .v_list = list }, - })); + create_special_dict(rettv, kMPExt, ((typval_T) { .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list } })); if (encode_list_write((void *)ext_val_list, mobj.via.ext.ptr, mobj.via.ext.size) == -1) { return FAIL; diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 27d35ea24f..82c9f04f8f 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -144,16 +144,16 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack, case kMPConvPairs: case kMPConvList: { const int idx = (v.data.l.li == tv_list_first(v.data.l.list) - ? 0 - : (v.data.l.li == NULL - ? tv_list_len(v.data.l.list) - 1 - : (int)tv_list_idx_of_item(v.data.l.list, - TV_LIST_ITEM_PREV(v.data.l.list, - v.data.l.li)))); + ? 0 + : (v.data.l.li == NULL + ? tv_list_len(v.data.l.list) - 1 + : (int)tv_list_idx_of_item(v.data.l.list, + TV_LIST_ITEM_PREV(v.data.l.list, + v.data.l.li)))); const listitem_T *const li = (v.data.l.li == NULL - ? tv_list_last(v.data.l.list) - : TV_LIST_ITEM_PREV(v.data.l.list, - v.data.l.li)); + ? tv_list_last(v.data.l.list) + : TV_LIST_ITEM_PREV(v.data.l.list, + v.data.l.li)); if (v.type == kMPConvList || li == NULL || (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index ea51cac163..781463b881 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -266,13 +266,12 @@ void ex_align(exarg_T *eap) } for (curwin->w_cursor.lnum = eap->line1; - curwin->w_cursor.lnum <= eap->line2; ++curwin->w_cursor.lnum) { + curwin->w_cursor.lnum <= eap->line2; curwin->w_cursor.lnum++) { if (eap->cmdidx == CMD_left) { // left align new_indent = indent; } else { has_tab = false; // avoid uninit warnings - len = linelen(eap->cmdidx == CMD_right ? &has_tab - : NULL) - get_indent(); + len = linelen(eap->cmdidx == CMD_right ? &has_tab : NULL) - get_indent(); if (len <= 0) { // skip blank lines continue; diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index c2648b9bfc..09bd88c947 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -539,9 +539,7 @@ static void discard_exception(except_T *excp, bool was_finished) if (debug_break_level > 0 || *p_vfile == NUL) { msg_scroll = true; // always scroll up, don't overwrite } - smsg(was_finished ? _("Exception finished: %s") - : _("Exception discarded: %s"), - excp->value); + smsg(was_finished ? _("Exception finished: %s") : _("Exception discarded: %s"), excp->value); msg_puts("\n"); // don't overwrite this either if (debug_break_level > 0 || *p_vfile == NUL) { cmdline_row = msg_row; @@ -1950,7 +1948,7 @@ void rewind_conditionals(cstack_T *cstack, int idx, int cond_type, int *cond_lev { while (cstack->cs_idx > idx) { if (cstack->cs_flags[cstack->cs_idx] & cond_type) { - --*cond_level; + (*cond_level)--; } if (cstack->cs_flags[cstack->cs_idx] & CSF_FOR) { free_for_info(cstack->cs_forinfo[cstack->cs_idx]); diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index d9adf84acc..c41916d40a 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -308,7 +308,7 @@ void *vim_findfile_init(char *path, char *filename, char *stopdirs, int level, i goto error_return; } path += 2; - } else + } else // NOLINT(readability/braces) #endif if (os_dirname((char_u *)ff_expand_buffer, MAXPATHL) == FAIL) { goto error_return; @@ -619,16 +619,15 @@ char_u *vim_findfile(void *search_ctx_arg) #endif ff_free_stack_element(stackp); continue; - } #ifdef FF_VERBOSE - else if (p_verbose >= 5) { + } else if (p_verbose >= 5) { verbose_enter_scroll(); smsg("Searching: %s (%s)", stackp->ffs_fix_path, stackp->ffs_wc_path); msg_puts("\n"); // don't overwrite this either verbose_leave_scroll(); - } #endif + } // check depth if (stackp->ffs_level <= 0) { diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 30898981de..7f844bc8f7 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -1048,9 +1048,8 @@ retry: conv_error = curbuf->b_ml.ml_line_count - linecnt + 1; } - } - // Remember the first linenr with an illegal byte - else if (illegal_byte == 0) { + } else if (illegal_byte == 0) { + // Remember the first linenr with an illegal byte illegal_byte = curbuf->b_ml.ml_line_count - linecnt + 1; } @@ -1066,7 +1065,7 @@ retry: #ifdef HAVE_ICONV || iconv_fd != (iconv_t)-1 #endif - )) { + )) { // NOLINT(whitespace/parens) while (conv_restlen > 0) { *(--ptr) = (char)bad_char_behavior; conv_restlen--; @@ -3039,9 +3038,11 @@ nobackup: // false. while ((fd = os_open(wfname, O_WRONLY | - (append ? - (forceit ? (O_APPEND | O_CREAT) : O_APPEND) - : (O_CREAT | O_TRUNC)), + (append + ? (forceit + ? (O_APPEND | O_CREAT) + : O_APPEND) + : (O_CREAT | O_TRUNC)), perm < 0 ? 0666 : (perm & 0777))) < 0) { // A forced write will try to create a new file if the old one // is still readonly. This may also happen when the directory @@ -3336,7 +3337,7 @@ restore_backup: } else { errmsg_allocated = true; SET_ERRMSG(xmalloc(300)); - vim_snprintf(errmsg, 300, + vim_snprintf(errmsg, 300, // NOLINT(runtime/printf) _("E513: write error, conversion failed in line %" PRIdLINENR " (make 'fenc' empty to override)"), write_info.bw_conv_error_lnum); @@ -4258,7 +4259,7 @@ char *modname(const char *fname, const char *ext, bool prepend_dot) } else { fnamelen = strlen(fname); retval = xmalloc(fnamelen + extlen + 3); - strcpy(retval, fname); + strcpy(retval, fname); // NOLINT(runtime/printf) } // Search backwards until we hit a '/', '\' or ':'. @@ -4276,12 +4277,11 @@ char *modname(const char *fname, const char *ext, bool prepend_dot) ptr[BASENAMELEN] = '\0'; } - char *s; - s = ptr + strlen(ptr); + char *s = ptr + strlen(ptr); // Append the extension. // ext can start with '.' and cannot exceed 3 more characters. - strcpy(s, ext); + strcpy(s, ext); // NOLINT(runtime/printf) char *e; // Prepend the dot if needed. diff --git a/src/nvim/garray.c b/src/nvim/garray.c index 6c63ce5a7c..eefbfed3ef 100644 --- a/src/nvim/garray.c +++ b/src/nvim/garray.c @@ -167,7 +167,7 @@ char *ga_concat_strings_sep(const garray_T *gap, const char *sep) s = xstpcpy(s, strings[i]); s = xstpcpy(s, sep); } - strcpy(s, strings[nelem - 1]); + strcpy(s, strings[nelem - 1]); // NOLINT(runtime/printf) return ret; } diff --git a/src/nvim/main.c b/src/nvim/main.c index 5687e0a6a9..d5fa992218 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1274,9 +1274,8 @@ scripterror: kFileReadOnly|kFileNonBlocking); assert(stdin_dup != NULL); scriptin[0] = stdin_dup; - } else if ((scriptin[0] = - file_open_new(&error, argv[0], kFileReadOnly|kFileNonBlocking, - 0)) == NULL) { + } else if ((scriptin[0] = file_open_new(&error, argv[0], + kFileReadOnly|kFileNonBlocking, 0)) == NULL) { vim_snprintf((char *)IObuff, IOSIZE, _("Cannot open for reading: \"%s\": %s\n"), argv[0], os_strerror(error)); diff --git a/src/nvim/move.c b/src/nvim/move.c index 9b7755a828..b79edb50f9 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -813,8 +813,7 @@ void curs_columns(win_T *wp, int may_scroll) if ((wp->w_wrow >= wp->w_height_inner || ((prev_skipcol > 0 || wp->w_wrow + so >= wp->w_height_inner) - && (plines = - plines_win_nofill(wp, wp->w_cursor.lnum, false)) - 1 + && (plines = plines_win_nofill(wp, wp->w_cursor.lnum, false)) - 1 >= wp->w_height_inner)) && wp->w_height_inner != 0 && wp->w_cursor.lnum == wp->w_topline diff --git a/src/nvim/ops.c b/src/nvim/ops.c index d3a47d77a2..3bb397fdd8 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2239,7 +2239,7 @@ void op_insert(oparg_T *oap, long count1) } curwin->w_ve_flags = VE_ALL; coladvance_force(oap->op_type == OP_APPEND - ? oap->end_vcol + 1 : getviscol()); + ? oap->end_vcol + 1 : getviscol()); if (oap->op_type == OP_APPEND) { curwin->w_cursor.col--; } diff --git a/src/nvim/option.c b/src/nvim/option.c index 208112561a..3c3a69e063 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -207,7 +207,7 @@ void set_init_1(bool clean_arg) p = "/tmp"; # endif mustfree = false; - } else + } else // NOLINT(readability/braces) #endif { p = vim_getenv(names[n]); @@ -2042,10 +2042,9 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va } redraw_titles(); modified_was_set = value; - } #ifdef BACKSLASH_IN_FILENAME - else if ((int *)varp == &p_ssl) { + } else if ((int *)varp == &p_ssl) { if (p_ssl) { psepc = '/'; psepcN = '\\'; @@ -2060,9 +2059,8 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va buflist_slash_adjust(); alist_slash_adjust(); scriptnames_slash_adjust(); - } #endif - else if ((int *)varp == &curwin->w_p_wrap) { + } else if ((int *)varp == &curwin->w_p_wrap) { // If 'wrap' is set, set w_leftcol to zero. if (curwin->w_p_wrap) { curwin->w_leftcol = 0; @@ -2681,7 +2679,7 @@ int findoption_len(const char *const arg, const size_t len) // letter. There are 26 letters, plus the first "t_" option. if (quick_tab[1] == 0) { p = options[0].fullname; - for (short int i = 1; (s = options[i].fullname) != NULL; i++) { + for (uint16_t i = 1; (s = options[i].fullname) != NULL; i++) { if (s[0] != p[0]) { if (s[0] == 't' && s[1] == '_') { quick_tab[26] = i; @@ -3598,8 +3596,7 @@ void unset_global_local_option(char *name, void *from) } p = &(options[opt_idx]); - switch ((int)p->indir) - { + switch ((int)p->indir) { // global option with local value: use local value if it's been set case PV_EP: clear_string_option(&buf->b_p_ep); @@ -4862,7 +4859,7 @@ static void option_value2string(vimoption_T *opp, int opt_flags) snprintf((char *)NameBuff, sizeof(NameBuff), "%" PRId64, - (int64_t)*(long *)varp); + (int64_t)(*(long *)varp)); } } else { // P_STRING varp = *(char_u **)(varp); diff --git a/src/nvim/path.c b/src/nvim/path.c index 9295905415..981937b79e 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -645,7 +645,7 @@ static size_t do_path_expand(garray_T *gap, const char *path, size_t wildoff, in #ifndef MSWIN || (!p_fic && (flags & EW_ICASE) && mb_isalpha(utf_ptr2char((char *)path_end))) #endif - )) { + )) { // NOLINT(whitespace/parens) e = p; } len = (size_t)(utfc_ptr2len((char *)path_end)); @@ -787,7 +787,7 @@ static int find_previous_pathsep(char_u *path, char_u **psep) { // skip the current separator if (*psep > path && vim_ispathsep(**psep)) { - --*psep; + (*psep)--; } // find the previous separator diff --git a/src/nvim/tag.c b/src/nvim/tag.c index a8d8eebb0d..4658aed235 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -220,8 +220,7 @@ bool do_tag(char *tag, int type, int count, int forceit, int verbose) if (*tag != NUL && (type == DT_TAG || type == DT_SELECT || type == DT_JUMP || type == DT_LTAG - || type == DT_CSCOPE - )) { + || type == DT_CSCOPE)) { if (g_do_tagpreview != 0) { if (ptag_entry.tagname != NULL && strcmp(ptag_entry.tagname, tag) == 0) { diff --git a/src/nvim/window.c b/src/nvim/window.c index d99d22af12..c18f8ca6a3 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2583,7 +2583,7 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev // save index for tabclosed event char_u prev_idx[NUMBUFLEN]; - sprintf((char *)prev_idx, "%i", tabpage_index(prev_curtab)); + snprintf((char *)prev_idx, NUMBUFLEN, "%i", tabpage_index(prev_curtab)); // Safety check: Autocommands may have closed the window when jumping // to the other tab page. -- cgit From 73bdfdd382bf2addd7816571608db6911448b48a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 13 Oct 2022 09:43:06 +0800 Subject: vim-patch:8.2.4453: :helpgrep may free an option that was not allocated Problem: :helpgrep may free an option that was not allocated. (Yegappan Lakshmanan) Solution: Check if the value was allocated. https://github.com/vim/vim/commit/4791fcd82565adcc60b86830e0bb6cd5b6eea0a6 --- src/nvim/option.c | 6 ++++++ src/nvim/quickfix.c | 5 ++++- src/nvim/testdir/test_quickfix.vim | 27 +++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/option.c b/src/nvim/option.c index 3c3a69e063..11a92f7d56 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -3112,6 +3112,12 @@ void set_option_value_give_err(const char *name, long number, const char *string } } +bool is_option_allocated(const char *name) +{ + int idx = findoption(name); + return idx >= 0 && (options[idx].flags & P_ALLOCED); +} + /// Return true if "name" is a string option. /// Returns false if option "name" does not exist. bool is_string_option(const char *name) diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index f9d139c466..1b21d26dd3 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -7065,6 +7065,7 @@ void ex_helpgrep(exarg_T *eap) bool updated = false; // Make 'cpoptions' empty, the 'l' flag should not be used here. char *const save_cpo = p_cpo; + const bool save_cpo_allocated = is_option_allocated("cpo"); p_cpo = empty_option; bool new_qi = false; @@ -7104,7 +7105,9 @@ void ex_helpgrep(exarg_T *eap) if (*p_cpo == NUL) { set_option_value_give_err("cpo", 0L, save_cpo, 0); } - free_string_option(save_cpo); + if (save_cpo_allocated) { + free_string_option(save_cpo); + } } if (updated) { diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 8c9e39570f..51b11b5511 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -714,6 +714,33 @@ func Test_helpgrep() call s:test_xhelpgrep('l') endfunc +func Test_helpgrep_restore_cpo_aucmd() + let save_cpo = &cpo + augroup QF_Test + au! + autocmd BufNew * set cpo=acd + augroup END + + helpgrep quickfix + call assert_equal('acd', &cpo) + %bw! + + set cpo&vim + augroup QF_Test + au! + autocmd BufReadPost * set cpo= + augroup END + + helpgrep buffer + call assert_equal('', &cpo) + + augroup QF_Test + au! + augroup END + %bw! + let &cpo = save_cpo +endfunc + func Test_errortitle() augroup QfBufWinEnter au! -- cgit From cd1e0bb87dc71d51d9e8da097f5822c37e909335 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 13 Oct 2022 10:13:17 +0800 Subject: vim-patch:8.2.4462: not enough testing for quickfix code Problem: Not enough testing for quickfix code. Solution: Add more tests. Fix uncovered problem. (Yegappan Lakshmanan, closes vim/vim#9839) https://github.com/vim/vim/commit/9c9be05b17ececb1515a2f41a4dedbf848d3d8b6 Omit Test_helpgrep_vim9_restore_cpo(). Cherry-pick test_quickfix.vim change from patch 8.2.0644. --- src/nvim/quickfix.c | 15 ++- src/nvim/testdir/test_makeencoding.vim | 16 ++++ src/nvim/testdir/test_quickfix.vim | 165 +++++++++++++++++++++++++++++++-- src/nvim/window.c | 2 +- 4 files changed, 185 insertions(+), 13 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 1b21d26dd3..d364cebc9d 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1790,7 +1790,7 @@ void check_quickfix_busy(void) /// @param type type character /// @param valid valid entry /// -/// @returns QF_OK or QF_FAIL. +/// @return QF_OK on success or QF_FAIL on failure. static int qf_add_entry(qf_list_T *qfl, char *dir, char *fname, char *module, int bufnum, char *mesg, linenr_T lnum, linenr_T end_lnum, int col, int end_col, char vis_col, char *pattern, int nr, char type, char valid) @@ -3461,12 +3461,10 @@ void qf_view_result(bool split) { qf_info_T *qi = &ql_info; - if (!bt_quickfix(curbuf)) { - return; - } if (IS_LL_WINDOW(curwin)) { qi = GET_LOC_LIST(curwin); } + if (qf_list_empty(qf_get_curlist(qi))) { emsg(_(e_no_errors)); return; @@ -3869,7 +3867,12 @@ static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last) if (curwin->w_llist == qi) { win = curwin; } else { + // Find the file window (non-quickfix) with this location list win = qf_find_win_with_loclist(qi); + if (win == NULL) { + // File window is not found. Find the location list window. + win = qf_find_win(qi); + } if (win == NULL) { return; } @@ -7142,7 +7145,9 @@ void ex_helpgrep(exarg_T *eap) if (new_qi) { ll_free_all(&qi); } - } else if (curwin->w_llist == NULL) { + } else if (curwin->w_llist == NULL && new_qi) { + // current window didn't have a location list associated with it + // before. Associate the new location list now. curwin->w_llist = qi; } } diff --git a/src/nvim/testdir/test_makeencoding.vim b/src/nvim/testdir/test_makeencoding.vim index c53c07d991..e297bdc228 100644 --- a/src/nvim/testdir/test_makeencoding.vim +++ b/src/nvim/testdir/test_makeencoding.vim @@ -107,3 +107,19 @@ func Test_make() lclose endfor endfunc + +" Test for an error file with a long line that needs an encoding conversion +func Test_longline_conversion() + new + call setline(1, ['Xfile:10:' .. repeat("\xe0", 2000)]) + write ++enc=latin1 Xerr.out + bw! + set errorformat& + set makeencoding=latin1 + cfile Xerr.out + call assert_equal(repeat("\u00e0", 2000), getqflist()[0].text) + call delete('Xerr.out') + set makeencoding& +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 51b11b5511..e19766775d 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -261,6 +261,7 @@ func XwindowTests(cchar) " Opening the location list window without any errors should fail if a:cchar == 'l' call assert_fails('lopen', 'E776:') + call assert_fails('lwindow', 'E776:') endif " Create a list with no valid entries @@ -714,6 +715,8 @@ func Test_helpgrep() call s:test_xhelpgrep('l') endfunc +" When running the :helpgrep command, if an autocmd modifies the 'cpoptions' +" value, then Vim crashes. (issue fixed by 7.2b-004 and 8.2.4453) func Test_helpgrep_restore_cpo_aucmd() let save_cpo = &cpo augroup QF_Test @@ -1237,8 +1240,14 @@ func Xinvalid_efm_Tests(cchar) set efm= call assert_fails('Xexpr "abc.txt:1:Hello world"', 'E378:') + " Empty directory name. When there is an error in parsing new entries, make + " sure the previous quickfix list is made the current list. + set efm& + cexpr ["one", "two"] + let qf_id = getqflist(#{id: 0}).id set efm=%DEntering\ dir\ abc,%f:%l:%m call assert_fails('Xexpr ["Entering dir abc", "abc.txt:1:Hello world"]', 'E379:') + call assert_equal(qf_id, getqflist(#{id: 0}).id) let &efm = save_efm endfunc @@ -1492,7 +1501,7 @@ func XquickfixChangedByAutocmd(cchar) endfunc endif - augroup testgroup + augroup QF_Test au! autocmd BufReadCmd test_changed.txt call ReadFunc() augroup END @@ -1506,7 +1515,24 @@ func XquickfixChangedByAutocmd(cchar) endfor call assert_fails('Xrewind', ErrorNr . ':') - augroup! testgroup + augroup QF_Test + au! + augroup END + + if a:cchar == 'c' + cexpr ["Xtest1:1:Line"] + cwindow + only + augroup QF_Test + au! + autocmd WinEnter * call setqflist([], 'f') + augroup END + call assert_fails('exe "normal \"', 'E925:') + augroup QF_Test + au! + augroup END + endif + %bw! endfunc func Test_quickfix_was_changed_by_autocmd() @@ -1644,6 +1670,9 @@ func SetXlistTests(cchar, bnum) \ " {'bufnr':999, 'lnum':5}])", 'E92:') call g:Xsetlist([[1, 2,3]]) call assert_equal(0, len(g:Xgetlist())) + call assert_fails('call g:Xsetlist([], [])', 'E928:') + call g:Xsetlist([v:_null_dict]) + call assert_equal([], g:Xgetlist()) endfunc func Test_setqflist() @@ -2917,6 +2946,19 @@ func XvimgrepTests(cchar) call assert_equal(0, getbufinfo('Xtestfile1')[0].loaded) call assert_equal([], getbufinfo('Xtestfile2')) + " Test for opening the dummy buffer used by vimgrep in a window. The new + " window should be closed + %bw! + augroup QF_Test + au! + autocmd BufReadPre * exe "sb " .. expand("") + augroup END + call assert_fails("Xvimgrep /sublime/ Xtestfile1", 'E480:') + call assert_equal(1, winnr('$')) + augroup QF_Test + au! + augroup END + call delete('Xtestfile1') call delete('Xtestfile2') endfunc @@ -4088,14 +4130,19 @@ endfunc " The following test used to crash Vim func Test_lhelpgrep_autocmd() lhelpgrep quickfix - autocmd QuickFixCmdPost * call setloclist(0, [], 'f') + augroup QF_Test + au! + autocmd QuickFixCmdPost * call setloclist(0, [], 'f') + augroup END lhelpgrep buffer call assert_equal('help', &filetype) call assert_equal(0, getloclist(0, {'nr' : '$'}).nr) lhelpgrep tabpage call assert_equal('help', &filetype) call assert_equal(1, getloclist(0, {'nr' : '$'}).nr) - au! QuickFixCmdPost + augroup QF_Test + au! + augroup END new | only augroup QF_Test @@ -4108,7 +4155,7 @@ func Test_lhelpgrep_autocmd() wincmd w call assert_fails('helpgrep quickfix', 'E925:') augroup QF_Test - au! BufEnter + au! augroup END new | only @@ -4118,7 +4165,7 @@ func Test_lhelpgrep_autocmd() augroup END call assert_fails('helpgrep quickfix', 'E925:') augroup QF_Test - au! BufEnter + au! augroup END new | only @@ -4128,10 +4175,43 @@ func Test_lhelpgrep_autocmd() augroup END call assert_fails('lhelpgrep quickfix', 'E926:') augroup QF_Test - au! BufEnter + au! augroup END + " Replace the contents of a help window location list when it is still in + " use. new | only + lhelpgrep quickfix + wincmd w + augroup QF_Test + au! + autocmd WinEnter * call setloclist(0, [], 'r') + augroup END + call assert_fails('lhelpgrep win_getid', 'E926:') + augroup QF_Test + au! + augroup END + + %bw! +endfunc + +" The following test used to crash Vim +func Test_lhelpgrep_autocmd_free_loclist() + %bw! + lhelpgrep quickfix + wincmd w + augroup QF_Test + au! + autocmd WinEnter * call setloclist(0, [], 'f') + augroup END + lhelpgrep win_getid + wincmd w + wincmd w + wincmd w + augroup QF_Test + au! + augroup END + %bw! endfunc " Test for shortening/simplifying the file name when opening the @@ -5322,6 +5402,7 @@ func Xtest_getqflist_by_idx(cchar) call assert_equal('L20', l[0].text) call assert_equal([], g:Xgetlist({'idx' : -1, 'items' : 0}).items) call assert_equal([], g:Xgetlist({'idx' : 3, 'items' : 0}).items) + call assert_equal({}, g:Xgetlist(#{idx: "abc"})) %bwipe! endfunc @@ -5380,6 +5461,19 @@ func Xtest_qftextfunc(cchar) call assert_equal('F1|10 col 2-7| green', getline(1)) call assert_equal('F1|20-25 col 4-8| blue', getline(2)) Xclose + + set efm=%f:%l:%c:%m + set quickfixtextfunc=Tqfexpr + " Update the list with only the cwindow + Xwindow + only + call g:Xsetlist([ + \ { 'filename': 'F2', 'lnum': 20, 'col': 2, + \ 'end_col': 7, 'text': 'red'} + \ ]) + call assert_equal(['F2-L20C2-red'], getline(1, '$')) + new + Xclose set efm& set quickfixtextfunc& @@ -5687,5 +5781,62 @@ func Test_lopen_bwipe_all() call delete('Xresult') endfunc +" Test for calling setqflist() function recursively +func Test_recursive_setqflist() + augroup QF_Test + au! + autocmd BufWinEnter quickfix call setqflist([], 'r') + augroup END + + copen + call assert_fails("call setqflist([], 'a')", 'E952:') + + augroup QF_Test + au! + augroup END + %bw! +endfunc + +" Test for failure to create a new window when selecting a file from the +" quickfix window +func Test_cwindow_newwin_fails() + cgetexpr ["Xfile1:10:L10", "Xfile1:20:L20"] + cwindow + only + let qf_wid = win_getid() + " create the maximum number of scratch windows + let hor_win_count = (&lines - 1)/2 + let hor_split_count = hor_win_count - 1 + for s in range(1, hor_split_count) | new | set buftype=nofile | endfor + call win_gotoid(qf_wid) + call assert_fails('exe "normal \"', 'E36:') + %bw! +endfunc + +" Test for updating the location list when only the location list window is +" present and the corresponding file window is closed. +func Test_loclist_update_with_llwin_only() + %bw! + new + wincmd w + lexpr ["Xfile1:1:Line1"] + lopen + wincmd p + close + call setloclist(2, [], 'r', {'lines': ["Xtest2:2:Line2"]}) + call assert_equal(['Xtest2|2| Line2'], getbufline(winbufnr(2), 1, '$')) + %bw! +endfunc + +" Test for getting the quickfix list after a buffer with an error is wiped out +func Test_getqflist_wiped_out_buffer() + %bw! + cexpr ["Xtest1:34:Wiped out"] + let bnum = bufnr('Xtest1') + call assert_equal(bnum, getqflist()[0].bufnr) + bw Xtest1 + call assert_equal(0, getqflist()[0].bufnr) + %bw! +endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/window.c b/src/nvim/window.c index c18f8ca6a3..ed64062a55 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -4913,7 +4913,7 @@ win_T *buf_jump_open_win(buf_T *buf) } /// Jump to the first open window in any tab page that contains buffer "buf", -/// if one exists. +/// if one exists. First search in the windows present in the current tab page. /// @return the found window, or NULL. win_T *buf_jump_open_tab(buf_T *buf) { -- cgit From 5740b3e076770336a15e88263878546345a3d325 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 13 Oct 2022 11:06:49 +0800 Subject: vim-patch:9.0.0260: using freed memory when using 'quickfixtextfunc' recursively Problem: Using freed memory when using 'quickfixtextfunc' recursively. Solution: Do not allow for recursion. https://github.com/vim/vim/commit/d6c67629ed05aae436164eec474832daf8ba7420 Cherry-pick Test_qflist_statusmsg() from patch 8.2.4617. --- src/nvim/quickfix.c | 7 ++++++ src/nvim/testdir/test_quickfix.vim | 48 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) (limited to 'src/nvim') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index d364cebc9d..d680ab8cf3 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3984,6 +3984,12 @@ static list_T *call_qftf_func(qf_list_T *qfl, int qf_winid, long start_idx, long { Callback *cb = &qftf_cb; list_T *qftf_list = NULL; + static bool recursive = false; + + if (recursive) { + return NULL; // this doesn't work properly recursively + } + recursive = true; // 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 @@ -4017,6 +4023,7 @@ static list_T *call_qftf_func(qf_list_T *qfl, int qf_winid, long start_idx, long tv_dict_unref(dict); } + recursive = false; return qftf_list; } diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index e19766775d..449904fcb4 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -5839,4 +5839,52 @@ func Test_getqflist_wiped_out_buffer() %bw! endfunc +" Test for the status message that is displayed when opening a new quickfix +" list +func Test_qflist_statusmsg() + cexpr "1\n2" + cexpr "1\n2\n3\ntest_quickfix.vim:1:msg" + call assert_equal('(4 of 4): msg', v:statusmsg) + call setqflist([], 'f') + %bw! + + " When creating a new quickfix list, if an autocmd changes the quickfix list + " in the stack, then an error message should be displayed. + augroup QF_Test + au! + au BufEnter test_quickfix.vim colder + augroup END + cexpr "1\n2" + call assert_fails('cexpr "1\n2\n3\ntest_quickfix.vim:1:msg"', 'E925:') + call setqflist([], 'f') + augroup QF_Test + au! + augroup END + %bw! + + augroup QF_Test + au! + au BufEnter test_quickfix.vim caddexpr "4" + augroup END + call assert_fails('cexpr "1\n2\n3\ntest_quickfix.vim:1:msg"', 'E925:') + call setqflist([], 'f') + augroup QF_Test + au! + augroup END + %bw! +endfunc + +func Test_quickfixtextfunc_recursive() + func s:QFTfunc(o) + cgete '0' + endfunc + copen + let &quickfixtextfunc = 's:QFTfunc' + cex "" + + let &quickfixtextfunc = '' + cclose +endfunc + + " vim: shiftwidth=2 sts=2 expandtab -- cgit From f72ae4514c51bb2a7c0fdfc8e2a469037dd36666 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 13 Oct 2022 11:10:30 +0800 Subject: vim-patch:9.0.0286: using freed memory when location list changed in autocmd Problem: Using freed memory when location list changed in autocmd. Solution: Return QF_ABORT and handle it. (Yegappan Lakshmanan, closes vim/vim#10993) https://github.com/vim/vim/commit/6d24a51b94beb1991cddce221f90b455e2d50db7 --- src/nvim/quickfix.c | 37 ++++++++++++++++++++++++------------- src/nvim/testdir/test_quickfix.vim | 17 +++++++++++++++++ 2 files changed, 41 insertions(+), 13 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index d680ab8cf3..a65635cf5e 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -176,6 +176,7 @@ enum { QF_NOMEM = 3, QF_IGNORE_LINE = 4, QF_MULTISCAN = 5, + QF_ABORT = 6, }; /// State information used to parse lines and add entries to a quickfix/location @@ -2686,6 +2687,10 @@ static int qf_jump_to_usable_window(int qf_fnum, bool newwin, int *opened_window } /// Edit the selected file or help file. +/// @return OK if successfully edited the file. +/// FAIL on failing to open the buffer. +/// QF_ABORT if the quickfix/location list was freed by an autocmd +/// when opening the buffer. static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int prev_winid, int *opened_window) { @@ -2719,13 +2724,13 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int if (wp == NULL && curwin->w_llist != qi) { emsg(_("E924: Current window was closed")); *opened_window = false; - return NOTDONE; + return QF_ABORT; } } if (qfl_type == QFLT_QUICKFIX && !qflist_valid(NULL, save_qfid)) { emsg(_(e_current_quickfix_list_was_changed)); - return NOTDONE; + return QF_ABORT; } if (old_qf_curlist != qi->qf_curlist // -V560 @@ -2736,7 +2741,7 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int } else { emsg(_(e_current_location_list_was_changed)); } - return NOTDONE; + return QF_ABORT; } return retval; @@ -2814,9 +2819,10 @@ static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, buf /// Find a usable window for opening a file from the quickfix/location list. If /// a window is not found then open a new window. If 'newwin' is true, then open /// a new window. -/// Returns OK if successfully jumped or opened a window. Returns FAIL if not -/// able to jump/open a window. Returns NOTDONE if a file is not associated -/// with the entry. +/// @return OK if successfully jumped or opened a window. +/// FAIL if not able to jump/open a window. +/// NOTDONE if a file is not associated with the entry. +/// QF_ABORT if the quickfix/location list was modified by an autocmd. static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, bool newwin, int *opened_window) { qf_list_T *qfl = qf_get_curlist(qi); @@ -2838,7 +2844,7 @@ static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, bool newwin, int } else { emsg(_(e_current_location_list_was_changed)); } - return FAIL; + return QF_ABORT; } // If currently in the quickfix window, find another window to show the @@ -2863,7 +2869,7 @@ static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, bool newwin, int } else { emsg(_(e_current_location_list_was_changed)); } - return FAIL; + return QF_ABORT; } return OK; @@ -2872,9 +2878,9 @@ static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, bool newwin, int /// Edit a selected file from the quickfix/location list and jump to a /// particular line/column, adjust the folds and display a message about the /// jump. -/// Returns OK on success and FAIL on failing to open the file/buffer. Returns -/// NOTDONE if the quickfix/location list is freed by an autocmd when opening -/// the file. +/// @return OK on success and FAIL on failing to open the file/buffer. +/// QF_ABORT if the quickfix/location list is freed by an autocmd when opening +/// the file. static int qf_jump_to_buffer(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, int forceit, int prev_winid, int *opened_window, int openfold, int print_message) { @@ -2968,14 +2974,19 @@ static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, boo if (retval == FAIL) { goto failed; } + if (retval == QF_ABORT) { + qi = NULL; + qf_ptr = NULL; + goto theend; + } if (retval == NOTDONE) { goto theend; } retval = qf_jump_to_buffer(qi, qf_index, qf_ptr, forceit, prev_winid, &opened_window, old_KeyTyped, print_message); - if (retval == NOTDONE) { - // Quickfix/location list is freed by an autocmd + if (retval == QF_ABORT) { + // Quickfix/location list was modified by an autocmd qi = NULL; qf_ptr = NULL; } diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 449904fcb4..4802d1f188 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -5886,5 +5886,22 @@ func Test_quickfixtextfunc_recursive() cclose endfunc +" Test for replacing the location list from an autocmd. This used to cause a +" read from freed memory. +func Test_loclist_replace_autocmd() + %bw! + call setloclist(0, [], 'f') + let s:bufnr = bufnr() + cal setloclist(0, [{'0': 0, '': ''}]) + au BufEnter * cal setloclist(1, [{'t': ''}, {'bufnr': s:bufnr}], 'r') + lopen + try + exe "norm j\" + catch + endtry + lnext + %bw! + call setloclist(0, [], 'f') +endfunc " vim: shiftwidth=2 sts=2 expandtab -- cgit From 4fbf41dfb4e4c6fd91c5a3d581c771bd1d6839d5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 13 Oct 2022 14:31:10 +0800 Subject: vim-patch:8.2.4735: quickfix tests can be a bit hard to read (#20631) Problem: Quickfix tests can be a bit hard to read. Solution: Use heredoc instead of strings and line continuation. (Yegappan Lakshmanan, closes vim/vim#10145) https://github.com/vim/vim/commit/4a7724a4406f639edd3f93f3542626811cf56719 Cherry-pick a typo fix from patch 8.2.3637. --- src/nvim/testdir/test_quickfix.vim | 492 ++++++++++++++++++++++--------------- 1 file changed, 294 insertions(+), 198 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 4802d1f188..b0e83b7f5f 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -104,9 +104,15 @@ func XlistTests(cchar) call assert_true(v:errmsg ==# 'E42: No Errors') " Populate the list and then try - Xgetexpr ['non-error 1', 'Xtestfile1:1:3:Line1', - \ 'non-error 2', 'Xtestfile2:2:2:Line2', - \ 'non-error| 3', 'Xtestfile3:3:1:Line3'] + let lines =<< trim END + non-error 1 + Xtestfile1:1:3:Line1 + non-error 2 + Xtestfile2:2:2:Line2 + non-error| 3 + Xtestfile3:3:1:Line3 + END + Xgetexpr lines " List only valid entries let l = split(execute('Xlist', ''), "\n") @@ -272,8 +278,12 @@ func XwindowTests(cchar) call assert_true(winnr('$') == 1) " Create a list with valid entries - Xgetexpr ['Xtestfile1:1:3:Line1', 'Xtestfile2:2:2:Line2', - \ 'Xtestfile3:3:1:Line3'] + let lines =<< trim END + Xtestfile1:1:3:Line1 + Xtestfile2:2:2:Line2 + Xtestfile3:3:1:Line3 + END + Xgetexpr lines " Open the window Xwindow @@ -336,8 +346,12 @@ func XwindowTests(cchar) if a:cchar == 'c' " Opening the quickfix window in multiple tab pages should reuse the " quickfix buffer - Xgetexpr ['Xtestfile1:1:3:Line1', 'Xtestfile2:2:2:Line2', - \ 'Xtestfile3:3:1:Line3'] + let lines =<< trim END + Xtestfile1:1:3:Line1 + Xtestfile2:2:2:Line2 + Xtestfile3:3:1:Line3 + END + Xgetexpr lines Xopen let qfbufnum = bufnr('%') tabnew @@ -372,14 +386,16 @@ func Test_copenHeight_tabline() set tabline& showtabline& endfunc - " Tests for the :cfile, :lfile, :caddfile, :laddfile, :cgetfile and :lgetfile " commands. func XfileTests(cchar) call s:setup_commands(a:cchar) - call writefile(['Xtestfile1:700:10:Line 700', - \ 'Xtestfile2:800:15:Line 800'], 'Xqftestfile1') + let lines =<< trim END + Xtestfile1:700:10:Line 700 + Xtestfile2:800:15:Line 800 + END + call writefile(lines, 'Xqftestfile1') enew! Xfile Xqftestfile1 @@ -403,8 +419,11 @@ func XfileTests(cchar) call assert_true(len(l) == 3 && \ l[2].lnum == 900 && l[2].col == 30 && l[2].text ==# 'Line 900') - call writefile(['Xtestfile1:222:77:Line 222', - \ 'Xtestfile2:333:88:Line 333'], 'Xqftestfile1') + let lines =<< trim END + Xtestfile1:222:77:Line 222 + Xtestfile2:333:88:Line 333 + END + call writefile(lines, 'Xqftestfile1') enew! Xgetfile Xqftestfile1 @@ -434,8 +453,11 @@ func XbufferTests(cchar) call s:setup_commands(a:cchar) enew! - silent! call setline(1, ['Xtestfile7:700:10:Line 700', - \ 'Xtestfile8:800:15:Line 800']) + let lines =<< trim END + Xtestfile7:700:10:Line 700 + Xtestfile8:800:15:Line 800 + END + silent! call setline(1, lines) Xbuffer! let l = g:Xgetlist() call assert_true(len(l) == 2 && @@ -443,8 +465,11 @@ func XbufferTests(cchar) \ l[1].lnum == 800 && l[1].col == 15 && l[1].text ==# 'Line 800') enew! - silent! call setline(1, ['Xtestfile9:900:55:Line 900', - \ 'Xtestfile10:950:66:Line 950']) + let lines =<< trim END + Xtestfile9:900:55:Line 900 + Xtestfile10:950:66:Line 950 + END + silent! call setline(1, lines) Xgetbuffer let l = g:Xgetlist() call assert_true(len(l) == 2 && @@ -452,8 +477,11 @@ func XbufferTests(cchar) \ l[1].lnum == 950 && l[1].col == 66 && l[1].text ==# 'Line 950') enew! - silent! call setline(1, ['Xtestfile11:700:20:Line 700', - \ 'Xtestfile12:750:25:Line 750']) + let lines =<< trim END + Xtestfile11:700:20:Line 700 + Xtestfile12:750:25:Line 750 + END + silent! call setline(1, lines) Xaddbuffer let l = g:Xgetlist() call assert_true(len(l) == 4 && @@ -523,12 +551,15 @@ func Xtest_browse(cchar) call s:create_test_file('Xqftestfile1') call s:create_test_file('Xqftestfile2') - Xgetexpr ['Xqftestfile1:5:Line5', - \ 'Xqftestfile1:6:Line6', - \ 'Xqftestfile2:10:Line10', - \ 'Xqftestfile2:11:Line11', - \ 'RegularLine1', - \ 'RegularLine2'] + let lines =<< trim END + Xqftestfile1:5:Line5 + Xqftestfile1:6:Line6 + Xqftestfile2:10:Line10 + Xqftestfile2:11:Line11 + RegularLine1 + RegularLine2 + END + Xgetexpr lines Xfirst call assert_fails('-5Xcc', 'E16:') @@ -578,10 +609,13 @@ func Xtest_browse(cchar) call assert_equal(5, line('.')) " Jumping to an error from the error window using cc command - Xgetexpr ['Xqftestfile1:5:Line5', - \ 'Xqftestfile1:6:Line6', - \ 'Xqftestfile2:10:Line10', - \ 'Xqftestfile2:11:Line11'] + let lines =<< trim END + Xqftestfile1:5:Line5 + Xqftestfile1:6:Line6 + Xqftestfile2:10:Line10 + Xqftestfile2:11:Line11 + END + Xgetexpr lines Xopen 10Xcc call assert_equal(11, line('.')) @@ -1109,20 +1143,21 @@ func s:dir_stack_tests(cchar) let save_efm=&efm set efm=%DEntering\ dir\ '%f',%f:%l:%m,%XLeaving\ dir\ '%f' - let lines = ["Entering dir 'dir1/a'", - \ 'habits2.txt:1:Nine Healthy Habits', - \ "Entering dir 'b'", - \ 'habits3.txt:2:0 Hours of television', - \ 'habits2.txt:7:5 Small meals', - \ "Entering dir 'dir1/c'", - \ 'habits4.txt:3:1 Hour of exercise', - \ "Leaving dir 'dir1/c'", - \ "Leaving dir 'dir1/a'", - \ 'habits1.txt:4:2 Liters of water', - \ "Entering dir 'dir2'", - \ 'habits5.txt:5:3 Cups of hot green tea', - \ "Leaving dir 'dir2'" - \] + let lines =<< trim END + Entering dir 'dir1/a' + habits2.txt:1:Nine Healthy Habits + Entering dir 'b' + habits3.txt:2:0 Hours of television + habits2.txt:7:5 Small meals + Entering dir 'dir1/c' + habits4.txt:3:1 Hour of exercise + Leaving dir 'dir1/c' + Leaving dir 'dir1/a' + habits1.txt:4:2 Liters of water + Entering dir 'dir2' + habits5.txt:5:3 Cups of hot green tea + Leaving dir 'dir2' + END Xexpr "" for l in lines @@ -1156,19 +1191,19 @@ func Test_efm_dirstack() call mkdir('dir1/c') call mkdir('dir2') - let lines = ["Nine Healthy Habits", - \ "0 Hours of television", - \ "1 Hour of exercise", - \ "2 Liters of water", - \ "3 Cups of hot green tea", - \ "4 Short mental breaks", - \ "5 Small meals", - \ "6 AM wake up time", - \ "7 Minutes of laughter", - \ "8 Hours of sleep (at least)", - \ "9 PM end of the day and off to bed" - \ ] - + let lines =<< trim END + Nine Healthy Habits + 0 Hours of television + 1 Hour of exercise + 2 Liters of water + 3 Cups of hot green tea + 4 Short mental breaks + 5 Small meals + 6 AM wake up time + 7 Minutes of laughter + 8 Hours of sleep (at least) + 9 PM end of the day and off to bed + END call writefile(lines, 'habits1.txt') call writefile(lines, 'dir1/a/habits2.txt') call writefile(lines, 'dir1/a/b/habits3.txt') @@ -1194,7 +1229,13 @@ func Xefm_ignore_continuations(cchar) \ '%-Wignored %m %l,' . \ '%+Cmore ignored %m %l,' . \ '%Zignored end' - Xgetexpr ['ignored warning 1', 'more ignored continuation 2', 'ignored end', 'error resync 4'] + let lines =<< trim END + ignored warning 1 + more ignored continuation 2 + ignored end + error resync 4 + END + Xgetexpr lines let l = map(g:Xgetlist(), '[v:val.text, v:val.valid, v:val.lnum, v:val.type]') call assert_equal([['resync', 1, 4, 'E']], l) @@ -1438,8 +1479,14 @@ func Test_efm_error_type() " error type set efm=%f:%l:%t:%m - cexpr ["Xfile1:10:E:msg1", "Xfile1:20:W:msg2", "Xfile1:30:I:msg3", - \ "Xfile1:40:N:msg4", "Xfile1:50:R:msg5"] + let lines =<< trim END + Xfile1:10:E:msg1 + Xfile1:20:W:msg2 + Xfile1:30:I:msg3 + Xfile1:40:N:msg4 + Xfile1:50:R:msg5 + END + cexpr lines let output = split(execute('clist'), "\n") call assert_equal([ \ ' 1 Xfile1:10 error: msg1', @@ -1450,8 +1497,14 @@ func Test_efm_error_type() " error type and a error number set efm=%f:%l:%t:%n:%m - cexpr ["Xfile1:10:E:2:msg1", "Xfile1:20:W:4:msg2", "Xfile1:30:I:6:msg3", - \ "Xfile1:40:N:8:msg4", "Xfile1:50:R:3:msg5"] + let lines =<< trim END + Xfile1:10:E:2:msg1 + Xfile1:20:W:4:msg2 + Xfile1:30:I:6:msg3 + Xfile1:40:N:8:msg4 + Xfile1:50:R:3:msg5 + END + cexpr lines let output = split(execute('clist'), "\n") call assert_equal([ \ ' 1 Xfile1:10 error 2: msg1', @@ -1476,8 +1529,13 @@ func Test_efm_end_lnum_col() " multiple lines set efm=%A%n)%m,%Z%f:%l-%e:%c-%k - cexpr ["1)msg1", "Xfile1:14-24:1-2", - \ "2)msg2", "Xfile1:24-34:3-4"] + let lines =<< trim END + 1)msg1 + Xfile1:14-24:1-2 + 2)msg2 + Xfile1:24-34:3-4 + END + cexpr lines let output = split(execute('clist'), "\n") call assert_equal([ \ ' 1 Xfile1:14-24 col 1-2 error 1: msg1', @@ -1887,12 +1945,12 @@ func Test_cgetfile_on_long_lines() " Problematic values if the line is longer than 4096 bytes. Then 1024 bytes " are read at a time. for len in [4078, 4079, 4080, 5102, 5103, 5104, 6126, 6127, 6128, 7150, 7151, 7152] - let lines = [ - \ '/tmp/file1:1:1:aaa', - \ '/tmp/file2:1:1:%s', - \ '/tmp/file3:1:1:bbb', - \ '/tmp/file4:1:1:ccc', - \ ] + let lines =<< trim END + /tmp/file1:1:1:aaa + /tmp/file2:1:1:%s + /tmp/file3:1:1:bbb + /tmp/file4:1:1:ccc + END let lines[1] = substitute(lines[1], '%s', repeat('x', len), '') call writefile(lines, 'Xcqetfile.txt') cgetfile Xcqetfile.txt @@ -1919,12 +1977,15 @@ func Test_switchbuf() let file1_winid = win_getid() new Xqftestfile2 let file2_winid = win_getid() - cgetexpr ['Xqftestfile1:5:Line5', - \ 'Xqftestfile1:6:Line6', - \ 'Xqftestfile2:10:Line10', - \ 'Xqftestfile2:11:Line11', - \ 'Xqftestfile3:15:Line15', - \ 'Xqftestfile3:16:Line16'] + let lines =<< trim END + Xqftestfile1:5:Line5 + Xqftestfile1:6:Line6 + Xqftestfile2:10:Line10 + Xqftestfile2:11:Line11 + Xqftestfile3:15:Line15 + Xqftestfile3:16:Line16 + END + cgetexpr lines new let winid = win_getid() @@ -2586,21 +2647,23 @@ func Test_Autocmd() silent! cexpr non_existing_func() silent! caddexpr non_existing_func() silent! cgetexpr non_existing_func() - let l = ['precexpr', - \ 'postcexpr', - \ 'precaddexpr', - \ 'postcaddexpr', - \ 'precgetexpr', - \ 'postcgetexpr', - \ 'precexpr', - \ 'postcexpr', - \ 'precaddexpr', - \ 'postcaddexpr', - \ 'precgetexpr', - \ 'postcgetexpr', - \ 'precexpr', - \ 'precaddexpr', - \ 'precgetexpr'] + let l =<< trim END + precexpr + postcexpr + precaddexpr + postcaddexpr + precgetexpr + postcgetexpr + precexpr + postcexpr + precaddexpr + postcaddexpr + precgetexpr + postcgetexpr + precexpr + precaddexpr + precgetexpr + END call assert_equal(l, g:acmds) let g:acmds = [] @@ -2618,15 +2681,17 @@ func Test_Autocmd() exe 'silent! cgetbuffer ' . bnum exe 'silent! caddbuffer ' . bnum enew! - let l = ['precbuffer', - \ 'postcbuffer', - \ 'precgetbuffer', - \ 'postcgetbuffer', - \ 'precaddbuffer', - \ 'postcaddbuffer', - \ 'precbuffer', - \ 'precgetbuffer', - \ 'precaddbuffer'] + let l =<< trim END + precbuffer + postcbuffer + precgetbuffer + postcgetbuffer + precaddbuffer + postcaddbuffer + precbuffer + precgetbuffer + precaddbuffer + END call assert_equal(l, g:acmds) call writefile(['Xtest:1:Line1'], 'Xtest') @@ -2641,24 +2706,26 @@ func Test_Autocmd() silent! cfile do_not_exist silent! caddfile do_not_exist silent! cgetfile do_not_exist - let l = ['precfile', - \ 'postcfile', - \ 'precaddfile', - \ 'postcaddfile', - \ 'precgetfile', - \ 'postcgetfile', - \ 'precfile', - \ 'postcfile', - \ 'precaddfile', - \ 'postcaddfile', - \ 'precgetfile', - \ 'postcgetfile', - \ 'precfile', - \ 'postcfile', - \ 'precaddfile', - \ 'postcaddfile', - \ 'precgetfile', - \ 'postcgetfile'] + let l =<< trim END + precfile + postcfile + precaddfile + postcaddfile + precgetfile + postcgetfile + precfile + postcfile + precaddfile + postcaddfile + precgetfile + postcgetfile + precfile + postcfile + precaddfile + postcaddfile + precgetfile + postcgetfile + END call assert_equal(l, g:acmds) let g:acmds = [] @@ -2671,20 +2738,22 @@ func Test_Autocmd() set makeprg= silent! make set makeprg& - let l = ['prehelpgrep', - \ 'posthelpgrep', - \ 'prehelpgrep', - \ 'posthelpgrep', - \ 'previmgrep', - \ 'postvimgrep', - \ 'previmgrepadd', - \ 'postvimgrepadd', - \ 'previmgrep', - \ 'postvimgrep', - \ 'previmgrepadd', - \ 'postvimgrepadd', - \ 'premake', - \ 'postmake'] + let l =<< trim END + prehelpgrep + posthelpgrep + prehelpgrep + posthelpgrep + previmgrep + postvimgrep + previmgrepadd + postvimgrepadd + previmgrep + postvimgrep + previmgrepadd + postvimgrepadd + premake + postmake + END call assert_equal(l, g:acmds) if has('unix') @@ -2704,22 +2773,24 @@ func Test_Autocmd() silent lgrep Grep_Autocmd_Text test_quickfix.vim silent lgrepadd GrepAdd_Autocmd_Text test_quickfix.vim set grepprg&vim - let l = ['pregrep', - \ 'postgrep', - \ 'pregrepadd', - \ 'postgrepadd', - \ 'pregrep', - \ 'postgrep', - \ 'pregrepadd', - \ 'postgrepadd', - \ 'pregrep', - \ 'postgrep', - \ 'pregrepadd', - \ 'postgrepadd', - \ 'prelgrep', - \ 'postlgrep', - \ 'prelgrepadd', - \ 'postlgrepadd'] + let l =<< trim END + pregrep + postgrep + pregrepadd + postgrepadd + pregrep + postgrep + pregrepadd + postgrepadd + pregrep + postgrep + pregrepadd + postgrepadd + prelgrep + postlgrep + prelgrepadd + postlgrepadd + END call assert_equal(l, g:acmds) endif @@ -2878,11 +2949,11 @@ func Test_cwindow_highlight() CheckScreendump let lines =<< trim END - call setline(1, ['some', 'text', 'with', 'matches']) - write XCwindow - vimgrep e XCwindow - redraw - cwindow 4 + call setline(1, ['some', 'text', 'with', 'matches']) + write XCwindow + vimgrep e XCwindow + redraw + cwindow 4 END call writefile(lines, 'XtestCwindow') let buf = RunVimInTerminal('-S XtestCwindow', #{rows: 12}) @@ -2899,10 +2970,13 @@ endfunc func XvimgrepTests(cchar) call s:setup_commands(a:cchar) - call writefile(['Editor:VIM vim', - \ 'Editor:Emacs EmAcS', - \ 'Editor:Notepad NOTEPAD'], 'Xtestfile1') - call writefile(['Linux', 'MacOS', 'MS-Windows'], 'Xtestfile2') + let lines =<< trim END + Editor:VIM vim + Editor:Emacs EmAcS + Editor:Notepad NOTEPAD + END + call writefile(lines, 'Xtestfile1') + call writefile(['Linux', 'macOS', 'MS-Windows'], 'Xtestfile2') " Error cases call assert_fails('Xvimgrep /abc *', 'E682:') @@ -2916,7 +2990,7 @@ func XvimgrepTests(cchar) Xexpr "" Xvimgrepadd Notepad Xtestfile1 - Xvimgrepadd MacOS Xtestfile2 + Xvimgrepadd macOS Xtestfile2 let l = g:Xgetlist() call assert_equal(2, len(l)) call assert_equal('Editor:Notepad NOTEPAD', l[0].text) @@ -3412,14 +3486,15 @@ func Xmultifilestack_tests(cchar) " error line ends with a file stack. let efm_val = 'Error\ l%l\ in\ %f,' let efm_val .= '%-P%>(%f%r,Error\ l%l\ in\ %m,%-Q)%r' - let l = g:Xgetlist({'lines' : [ - \ '(one.txt', - \ 'Error l4 in one.txt', - \ ') (two.txt', - \ 'Error l6 in two.txt', - \ ')', - \ 'Error l8 in one.txt' - \ ], 'efm' : efm_val}) + let lines =<< trim END + (one.txt + Error l4 in one.txt + ) (two.txt + Error l6 in two.txt + ) + Error l8 in one.txt + END + let l = g:Xgetlist({'lines': lines, 'efm' : efm_val}) call assert_equal(3, len(l.items)) call assert_equal('one.txt', bufname(l.items[0].bufnr)) call assert_equal(4, l.items[0].lnum) @@ -3697,7 +3772,15 @@ func Xqfjump_tests(cchar) call g:Xsetlist([], 'f') setlocal buftype=nofile new - call g:Xsetlist([], ' ', {'lines' : ['F1:1:1:Line1', 'F1:2:2:Line2', 'F2:1:1:Line1', 'F2:2:2:Line2', 'F3:1:1:Line1', 'F3:2:2:Line2']}) + let lines =<< trim END + F1:1:1:Line1 + F1:2:2:Line2 + F2:1:1:Line1 + F2:2:2:Line2 + F3:1:1:Line1 + F3:2:2:Line2 + END + call g:Xsetlist([], ' ', {'lines': lines}) Xopen let winid = win_getid() wincmd p @@ -4929,9 +5012,20 @@ func Xtest_below(cchar) endif " Test for lines with multiple quickfix entries - Xexpr ["X1:5:L5", "X2:5:1:L5_1", "X2:5:2:L5_2", "X2:5:3:L5_3", - \ "X2:10:1:L10_1", "X2:10:2:L10_2", "X2:10:3:L10_3", - \ "X2:15:1:L15_1", "X2:15:2:L15_2", "X2:15:3:L15_3", "X3:3:L3"] + let lines =<< trim END + X1:5:L5 + X2:5:1:L5_1 + X2:5:2:L5_2 + X2:5:3:L5_3 + X2:10:1:L10_1 + X2:10:2:L10_2 + X2:10:3:L10_3 + X2:15:1:L15_1 + X2:15:2:L15_2 + X2:15:3:L15_3 + X3:3:L3 + END + Xexpr lines edit +1 X2 Xbelow 2 call assert_equal(['X2', 10, 1], [@%, line('.'), col('.')]) @@ -4995,33 +5089,32 @@ func Test_cbelow() endfunc func Test_quickfix_count() - let commands = [ - \ 'cNext', - \ 'cNfile', - \ 'cabove', - \ 'cbelow', - \ 'cfirst', - \ 'clast', - \ 'cnewer', - \ 'cnext', - \ 'cnfile', - \ 'colder', - \ 'cprevious', - \ 'crewind', - \ - \ 'lNext', - \ 'lNfile', - \ 'labove', - \ 'lbelow', - \ 'lfirst', - \ 'llast', - \ 'lnewer', - \ 'lnext', - \ 'lnfile', - \ 'lolder', - \ 'lprevious', - \ 'lrewind', - \ ] + let commands =<< trim END + cNext + cNfile + cabove + cbelow + cfirst + clast + cnewer + cnext + cnfile + colder + cprevious + crewind + lNext + lNfile + labove + lbelow + lfirst + llast + lnewer + lnext + lnfile + lolder + lprevious + lrewind + END for cmd in commands call assert_fails('-1' .. cmd, 'E16:') call assert_fails('.' .. cmd, 'E16:') @@ -5662,9 +5755,12 @@ func Test_locationlist_open_in_newtab() %bwipe! - lgetexpr ['Xqftestfile1:5:Line5', - \ 'Xqftestfile2:10:Line10', - \ 'Xqftestfile3:16:Line16'] + let lines =<< trim END + Xqftestfile1:5:Line5 + Xqftestfile2:10:Line10 + Xqftestfile3:16:Line16 + END + lgetexpr lines silent! llast call assert_equal(1, tabpagenr('$')) -- cgit From 06dfedc87ab4cc575a503195a1358b2984e248c4 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 13 Oct 2022 20:46:23 +0800 Subject: vim-patch:9.0.0738: cannot suppress completion "scanning" messages (#20633) Problem: Cannot suppress completion "scanning" messages. Solution: Add the "C" flag in 'shortmess'. (Bjorn Linse, closes vim/vim#11354) https://github.com/vim/vim/commit/91ccbad5ded8bcf2cc93a873ff2c3179b0c548c7 --- src/nvim/insexpand.c | 28 ++++++++++++++++------------ src/nvim/option_defs.h | 5 +++-- src/nvim/optionstr.c | 3 ++- 3 files changed, 21 insertions(+), 15 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index da1063f699..7a26f0a63a 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -1433,7 +1433,7 @@ static void ins_compl_files(int count, char **files, int thesaurus, int flags, r for (i = 0; i < count && !got_int && !compl_interrupted; i++) { fp = os_fopen(files[i], "r"); // open dictionary file - if (flags != DICT_EXACT) { + if (flags != DICT_EXACT && !shortmess(SHM_COMPLETIONSCAN)) { msg_hist_off = true; // reset in msg_trunc_attr() vim_snprintf((char *)IObuff, IOSIZE, _("Scanning dictionary: %s"), files[i]); @@ -2759,14 +2759,16 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar st->dict = (char_u *)st->ins_buf->b_fname; st->dict_f = DICT_EXACT; } - msg_hist_off = true; // reset in msg_trunc_attr() - vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"), - st->ins_buf->b_fname == NULL - ? buf_spname(st->ins_buf) - : st->ins_buf->b_sfname == NULL - ? st->ins_buf->b_fname - : st->ins_buf->b_sfname); - (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R)); + if (!shortmess(SHM_COMPLETIONSCAN)) { + msg_hist_off = true; // reset in msg_trunc_attr() + vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"), + st->ins_buf->b_fname == NULL + ? buf_spname(st->ins_buf) + : st->ins_buf->b_sfname == NULL + ? st->ins_buf->b_fname + : st->ins_buf->b_sfname); + (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R)); + } } else if (*st->e_cpt == NUL) { status = INS_COMPL_CPT_END; } else { @@ -2787,10 +2789,12 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar } else if (*st->e_cpt == 'd') { compl_type = CTRL_X_PATH_DEFINES; } else if (*st->e_cpt == ']' || *st->e_cpt == 't') { - msg_hist_off = true; // reset in msg_trunc_attr() compl_type = CTRL_X_TAGS; - vim_snprintf((char *)IObuff, IOSIZE, "%s", _("Scanning tags.")); - (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R)); + if (!shortmess(SHM_COMPLETIONSCAN)) { + msg_hist_off = true; // reset in msg_trunc_attr() + vim_snprintf((char *)IObuff, IOSIZE, "%s", _("Scanning tags.")); + (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R)); + } } else { compl_type = -1; } diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 25ef1fc091..5470b66d6d 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -239,7 +239,7 @@ enum { SHM_MOD = 'm', ///< Modified. SHM_FILE = 'f', ///< (file 1 of 2) SHM_LAST = 'i', ///< Last line incomplete. - SHM_TEXT = 'x', ///< Tx instead of textmode. + SHM_TEXT = 'x', ///< tx instead of textmode. SHM_LINES = 'l', ///< "L" instead of "lines". SHM_NEW = 'n', ///< "[New]" instead of "[New file]". SHM_WRI = 'w', ///< "[w]" instead of "written". @@ -253,9 +253,10 @@ enum { SHM_ATTENTION = 'A', ///< No ATTENTION messages. SHM_INTRO = 'I', ///< Intro messages. SHM_COMPLETIONMENU = 'c', ///< Completion menu messages. + SHM_COMPLETIONSCAN = 'C', ///< Completion scanning messages. SHM_RECORDING = 'q', ///< Short recording message. SHM_FILEINFO = 'F', ///< No file info messages. - SHM_SEARCHCOUNT = 'S', ///< Search sats: '[1/10]' + SHM_SEARCHCOUNT = 'S', ///< Search stats: '[1/10]' }; /// Represented by 'a' flag. #define SHM_ALL_ABBREVIATIONS ((char[]) { \ diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index 1da6fa15d7..24ee0a1f69 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -118,7 +118,8 @@ static char *(p_rdb_values[]) = { "compositor", "nothrottle", "invalid", "nodelt static char SHM_ALL[] = { SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, SHM_ABBREVIATIONS, SHM_WRITE, SHM_TRUNC, SHM_TRUNCALL, SHM_OVER, SHM_OVERALL, SHM_SEARCH, SHM_ATTENTION, SHM_INTRO, - SHM_COMPLETIONMENU, SHM_RECORDING, SHM_FILEINFO, SHM_SEARCHCOUNT, 0, }; + SHM_COMPLETIONMENU, SHM_COMPLETIONSCAN, SHM_RECORDING, SHM_FILEINFO, + SHM_SEARCHCOUNT, 0, }; /// After setting various option values: recompute variables that depend on /// option values. -- cgit From 730228f6db1f858795ea83ba2179c875dca37bc7 Mon Sep 17 00:00:00 2001 From: Enan Ajmain <3nan.ajmain@gmail.com> Date: Thu, 13 Oct 2022 18:48:12 +0600 Subject: feat(windows): show icon in terminal titlebar, taskbar #20607 closes #20071 --- src/nvim/main.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'src/nvim') diff --git a/src/nvim/main.c b/src/nvim/main.c index d5fa992218..f6ec08c8e0 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -207,6 +207,57 @@ void early_init(mparm_T *paramp) init_signs(); } +#ifdef MSWIN +HWND hWnd = NULL; +static HICON hOrigIconSmall = NULL; +static HICON hOrigIcon = NULL; + +/// Save Windows console icon to be reset later +static void SaveWin32ConsoleIcon(void) +{ + if ((hWnd = GetConsoleWindow()) == NULL) { + return; + } + hOrigIconSmall = (HICON)SendMessage(hWnd, WM_GETICON, (WPARAM)ICON_SMALL, (LPARAM)0); + hOrigIcon = (HICON)SendMessage(hWnd, WM_GETICON, (WPARAM)ICON_BIG, (LPARAM)0); +} + +static void SetConsoleIcon(HWND hWindow, HICON hIconSmall, HICON hIcon) +{ + if (hWindow == NULL) { + return; + } + if (hIconSmall != NULL) { + SendMessage(hWnd, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIconSmall); + } + if (hIcon != NULL) { + SendMessage(hWnd, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hIcon); + } +} + +/// Reset Windows console icon to original +static void ResetWin32ConsoleIcon(void) +{ + SetConsoleIcon(hWnd, hOrigIconSmall, hOrigIcon); +} + +/// Set Neovim logo as Windows console icon +static void SetWin32ConsoleIcon(void) +{ + const char *vimruntime = os_getenv("VIMRUNTIME"); + if (vimruntime != NULL) { + snprintf(NameBuff, MAXPATHL, "%s" _PATHSEPSTR "neovim.ico", vimruntime); + if (!os_path_exists(NameBuff)) { + WLOG("neovim.ico not found: %s", NameBuff); + } else { + HICON hVimIcon = LoadImage(NULL, NameBuff, IMAGE_ICON, 64, 64, + LR_LOADFROMFILE | LR_LOADMAP3DCOLORS); + SetConsoleIcon(hWnd, hVimIcon, hVimIcon); + } + } +} +#endif + #ifdef MAKE_LIB int nvim_main(int argc, char **argv); // silence -Wmissing-prototypes int nvim_main(int argc, char **argv) @@ -256,6 +307,11 @@ int main(int argc, char **argv) early_init(¶ms); +#ifdef MSWIN + SaveWin32ConsoleIcon(); + SetWin32ConsoleIcon(); +#endif + set_argv_var(argv, argc); // set v:argv // Check if we have an interactive window. @@ -721,6 +777,10 @@ void getout(int exitval) garbage_collect(false); } +#ifdef MSWIN + ResetWin32ConsoleIcon(); +#endif + os_exit(exitval); } -- cgit From eb123b565e201418dd135d2602dc20eea3cead39 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Thu, 13 Oct 2022 15:18:48 +0200 Subject: docs: fix typos (#20509) Co-authored-by: zeertzjq --- src/nvim/eval/userfunc.h | 2 +- src/nvim/globals.h | 10 +++++----- src/nvim/insexpand.c | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h index 4098622a14..ca09c2a6ec 100644 --- a/src/nvim/eval/userfunc.h +++ b/src/nvim/eval/userfunc.h @@ -23,7 +23,7 @@ #define FC_VIM9 0x400 // defined in vim9 script file #define FC_LUAREF 0x800 // luaref callback -///< Structure used by trans_function_name() +/// Structure used by trans_function_name() typedef struct { dict_T *fd_dict; ///< Dictionary used. char_u *fd_newkey; ///< New key in "dict" in allocated memory. diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 209caca880..5a38585bb1 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -199,8 +199,8 @@ EXTERN dict_T vimvardict; // Dictionary with v: variables EXTERN dict_T globvardict; // Dictionary with g: variables /// g: value #define globvarht globvardict.dv_hashtab -EXTERN int did_emsg; // set by emsg() when the message - // is displayed or thrown +EXTERN int did_emsg; // incremented by emsg() when a + // message is displayed or thrown EXTERN bool called_vim_beep; // set if vim_beep() is called EXTERN bool did_emsg_syntax; // did_emsg set because of a // syntax error @@ -1071,11 +1071,11 @@ EXTERN char windowsVersion[20] INIT(= { 0 }); EXTERN int exit_need_delay INIT(= 0); -///< Skip win_fix_cursor() call for 'splitkeep' when cmdwin is closed. +/// Skip win_fix_cursor() call for 'splitkeep' when cmdwin is closed. EXTERN bool skip_win_fix_cursor INIT(= false); -///< Skip win_fix_scroll() call for 'splitkeep' when closing tab page. +/// Skip win_fix_scroll() call for 'splitkeep' when closing tab page. EXTERN bool skip_win_fix_scroll INIT(= false); -///< Skip update_topline() call while executing win_fix_scroll(). +/// Skip update_topline() call while executing win_fix_scroll(). EXTERN bool skip_update_topline INIT(= false); #endif // NVIM_GLOBALS_H diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 7a26f0a63a..643b7f477c 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -215,11 +215,11 @@ static bool compl_interrupted = false; static bool compl_restarting = false; ///< don't insert match -///< When the first completion is done "compl_started" is set. When it's -///< false the word to be completed must be located. +/// When the first completion is done "compl_started" is set. When it's +/// false the word to be completed must be located. static bool compl_started = false; -///< Which Ctrl-X mode are we in? +/// Which Ctrl-X mode are we in? static int ctrl_x_mode = CTRL_X_NORMAL; static int compl_matches = 0; ///< number of completion matches -- cgit From 288208257c8d6b3c8dcce7ee6c7b6c7bb7bafb27 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sat, 8 Oct 2022 15:48:07 +0100 Subject: feat(cscope)!: remove --- src/nvim/cmdexpand.c | 12 - src/nvim/eval.lua | 1 - src/nvim/eval/funcs.c | 25 - src/nvim/ex_cmds.lua | 24 - src/nvim/ex_docmd.c | 5 - src/nvim/generators/gen_declarations.lua | 3 +- src/nvim/if_cscope.c | 2027 ------------------------------ src/nvim/if_cscope.h | 10 - src/nvim/if_cscope_defs.h | 64 - src/nvim/main.c | 2 - src/nvim/normal.c | 6 +- src/nvim/option_defs.h | 9 - src/nvim/options.lua | 52 - src/nvim/optionstr.c | 17 - src/nvim/tag.c | 226 ++-- src/nvim/tag.h | 2 - src/nvim/testdir/test_cscope.vim | 344 ----- src/nvim/usercmd.c | 1 - src/nvim/vim.h | 1 - 19 files changed, 89 insertions(+), 2742 deletions(-) delete mode 100644 src/nvim/if_cscope.c delete mode 100644 src/nvim/if_cscope.h delete mode 100644 src/nvim/if_cscope_defs.h delete mode 100644 src/nvim/testdir/test_cscope.vim (limited to 'src/nvim') diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index a29d022606..e728bd6967 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -22,7 +22,6 @@ #include "nvim/getchar.h" #include "nvim/help.h" #include "nvim/highlight_group.h" -#include "nvim/if_cscope.h" #include "nvim/locale.h" #include "nvim/lua/executor.h" #include "nvim/mapping.h" @@ -1419,11 +1418,6 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons case CMD_highlight: set_context_in_highlight_cmd(xp, arg); break; - case CMD_cscope: - case CMD_lcscope: - case CMD_scscope: - set_context_in_cscope_cmd(xp, arg, cmdidx); - break; case CMD_sign: set_context_in_sign_cmd(xp, (char *)arg); break; @@ -2063,7 +2057,6 @@ static int ExpandOther(expand_T *xp, regmatch_T *rmp, int *num_file, char ***fil { EXPAND_HIGHLIGHT, (ExpandFunc)get_highlight_name, true, false }, { EXPAND_EVENTS, expand_get_event_name, true, false }, { EXPAND_AUGROUP, expand_get_augroup_name, true, false }, - { EXPAND_CSCOPE, get_cscope_name, true, true }, { EXPAND_SIGN, get_sign_name, true, true }, { EXPAND_PROFILE, get_profile_name, true, true }, #ifdef HAVE_WORKING_LIBINTL @@ -2883,11 +2876,6 @@ void f_getcompletion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) xpc.xp_pattern_len = strlen(xpc.xp_pattern); } - if (xpc.xp_context == EXPAND_CSCOPE) { - set_context_in_cscope_cmd(&xpc, (const char *)xpc.xp_pattern, CMD_cscope); - xpc.xp_pattern_len = strlen(xpc.xp_pattern); - } - if (xpc.xp_context == EXPAND_SIGN) { set_context_in_sign_cmd(&xpc, xpc.xp_pattern); xpc.xp_pattern_len = strlen(xpc.xp_pattern); diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 837fef23f4..cc26bbf1a8 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -88,7 +88,6 @@ return { cos={args=1, base=1, float_func="cos"}, cosh={args=1, base=1, float_func="cosh"}, count={args={2, 4}, base=1}, - cscope_connection={args={0, 3}}, ctxget={args={0, 1}}, ctxpop={}, ctxpush={args={0, 1}}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index a326c44371..861f68993a 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -39,7 +39,6 @@ #include "nvim/getchar.h" #include "nvim/globals.h" #include "nvim/highlight_group.h" -#include "nvim/if_cscope.h" #include "nvim/indent.h" #include "nvim/indent_c.h" #include "nvim/input.h" @@ -1132,29 +1131,6 @@ static void f_count(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) rettv->vval.v_number = n; } -/// "cscope_connection([{num} , {dbpath} [, {prepend}]])" function -/// -/// Checks the existence of a cscope connection. -static void f_cscope_connection(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - int num = 0; - const char *dbpath = NULL; - const char *prepend = NULL; - char buf[NUMBUFLEN]; - - if (argvars[0].v_type != VAR_UNKNOWN - && argvars[1].v_type != VAR_UNKNOWN) { - num = (int)tv_get_number(&argvars[0]); - dbpath = tv_get_string(&argvars[1]); - if (argvars[2].v_type != VAR_UNKNOWN) { - prepend = tv_get_string_buf(&argvars[2], buf); - } - } - - rettv->vval.v_number = cs_connection(num, (char_u *)dbpath, - (char_u *)prepend); -} - /// "ctxget([{index}])" function static void f_ctxget(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -3565,7 +3541,6 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) "cmdwin", "comments", "conceal", - "cscope", "cursorbind", "cursorshape", #ifdef DEBUG diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 04b8832428..e3eea884c4 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -672,18 +672,6 @@ module.cmds = { addr_type='ADDR_UNSIGNED', func='ex_cc', }, - { - command='cscope', - flags=bit.bor(EXTRA, NOTRLCOM, XFILE), - addr_type='ADDR_NONE', - func='ex_cscope', - }, - { - command='cstag', - flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type='ADDR_NONE', - func='ex_cstag', - }, { command='cunmap', flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK), @@ -1404,12 +1392,6 @@ module.cmds = { addr_type='ADDR_OTHER', func='ex_cclose', }, - { - command='lcscope', - flags=bit.bor(EXTRA, NOTRLCOM, XFILE), - addr_type='ADDR_NONE', - func='ex_cscope', - }, { command='ldo', flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), @@ -2421,12 +2403,6 @@ module.cmds = { addr_type='ADDR_NONE', func='ex_scriptencoding', }, - { - command='scscope', - flags=bit.bor(EXTRA, NOTRLCOM), - addr_type='ADDR_NONE', - func='ex_scscope', - }, { command='set', flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, LOCK_OK, SBOXOK), diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 5591b5f55d..77d310e338 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -44,7 +44,6 @@ #include "nvim/hardcopy.h" #include "nvim/help.h" #include "nvim/highlight_group.h" -#include "nvim/if_cscope.h" #include "nvim/input.h" #include "nvim/keycodes.h" #include "nvim/locale.h" @@ -6589,10 +6588,6 @@ static void ex_tag_cmd(exarg_T *eap, char *name) cmd = DT_LAST; // ":tlast" break; default: // ":tag" - if (p_cst && *eap->arg != NUL) { - ex_cstag(eap); - return; - } cmd = DT_TAG; break; } diff --git a/src/nvim/generators/gen_declarations.lua b/src/nvim/generators/gen_declarations.lua index bf1adaeee3..4097ff7dc5 100755 --- a/src/nvim/generators/gen_declarations.lua +++ b/src/nvim/generators/gen_declarations.lua @@ -182,8 +182,7 @@ Additionally uses the following environment variables: If set to 1 then all generated declarations receive a comment with file name and line number after the declaration. This may be useful for debugging gen_declarations script, but not much beyond that with - configured development environment (i.e. with ctags/cscope/finding - definitions with clang/etc). + configured development environment (i.e. with with clang/etc). WARNING: setting this to 1 will cause extensive rebuilds: declarations generator script will not regenerate non-static.h file if its diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c deleted file mode 100644 index bc31d702f4..0000000000 --- a/src/nvim/if_cscope.c +++ /dev/null @@ -1,2027 +0,0 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - -// CSCOPE support for Vim added by Andy Kahn -// Ported to Win32 by Sergey Khorev -// -// The basic idea/structure of cscope for Vim was borrowed from Nvi. There -// might be a few lines of code that look similar to what Nvi has. - -#include -#include -#include -#include -#include -#include -#include - -#include "nvim/ascii.h" -#include "nvim/autocmd.h" -#include "nvim/buffer.h" -#include "nvim/charset.h" -#include "nvim/eval.h" -#include "nvim/event/stream.h" -#include "nvim/ex_eval.h" -#include "nvim/fileio.h" -#include "nvim/if_cscope.h" -#include "nvim/memory.h" -#include "nvim/message.h" -#include "nvim/os/input.h" -#include "nvim/os/os.h" -#include "nvim/os/time.h" -#include "nvim/path.h" -#include "nvim/quickfix.h" -#include "nvim/strings.h" -#include "nvim/tag.h" -#include "nvim/window.h" -#if defined(UNIX) -# include -#endif -#include "nvim/if_cscope_defs.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "if_cscope.c.generated.h" -#endif - -static csinfo_T *csinfo = NULL; -static size_t csinfo_size = 0; // number of items allocated in csinfo[] - -static int eap_arg_len; // length of eap->arg, set in cs_lookup_cmd() -static cscmd_T cs_cmds[] = -{ - { "add", cs_add, - N_("Add a new database"), "add file|dir [pre-path] [flags]", 0 }, - { "find", cs_find, - N_("Query for a pattern"), "find a|c|d|e|f|g|i|s|t name", 1 }, - { "help", cs_help, - N_("Show this message"), "help", 0 }, - { "kill", cs_kill, - N_("Kill a connection"), "kill #", 0 }, - { "reset", cs_reset, - N_("Reinit all connections"), "reset", 0 }, - { "show", cs_show, - N_("Show connections"), "show", 0 }, - { NULL, NULL, NULL, NULL, 0 } -}; - -static void cs_usage_msg(csid_e x) -{ - (void)semsg(_("E560: Usage: cs[cope] %s"), cs_cmds[(int)x].usage); -} - -static enum { - EXP_CSCOPE_SUBCMD, // expand ":cscope" sub-commands - EXP_SCSCOPE_SUBCMD, // expand ":scscope" sub-commands - EXP_CSCOPE_FIND, // expand ":cscope find" arguments - EXP_CSCOPE_KILL, // expand ":cscope kill" arguments -} expand_what; - -// Function given to ExpandGeneric() to obtain the cscope command -// expansion. -char *get_cscope_name(expand_T *xp, int idx) -{ - int current_idx; - - switch (expand_what) { - case EXP_CSCOPE_SUBCMD: - // Complete with sub-commands of ":cscope": - // add, find, help, kill, reset, show - return cs_cmds[idx].name; - case EXP_SCSCOPE_SUBCMD: { - // Complete with sub-commands of ":scscope": same sub-commands as - // ":cscope" but skip commands which don't support split windows - int i; - for (i = 0, current_idx = 0; cs_cmds[i].name != NULL; i++) { - if (cs_cmds[i].cansplit) { - if (current_idx++ == idx) { - break; - } - } - } - return cs_cmds[i].name; - } - case EXP_CSCOPE_FIND: { - const char *query_type[] = - { - "a", "c", "d", "e", "f", "g", "i", "s", "t", NULL - }; - - // Complete with query type of ":cscope find {query_type}". - // {query_type} can be letters (c, d, ... a) or numbers (0, 1, - // ..., 9) but only complete with letters, since numbers are - // redundant. - return (char *)query_type[idx]; - } - case EXP_CSCOPE_KILL: { - static char connection[5]; - - // ":cscope kill" accepts connection numbers or partial names of - // the pathname of the cscope database as argument. Only complete - // with connection numbers. -1 can also be used to kill all - // connections. - size_t i; - for (i = 0, current_idx = 0; i < csinfo_size; i++) { - if (csinfo[i].fname == NULL) { - continue; - } - if (current_idx++ == idx) { - vim_snprintf(connection, sizeof(connection), "%zu", i); - return connection; - } - } - return (current_idx == idx && idx > 0) ? "-1" : NULL; - } - default: - return NULL; - } -} - -// Handle command line completion for :cscope command. -void set_context_in_cscope_cmd(expand_T *xp, const char *arg, cmdidx_T cmdidx) -{ - // Default: expand subcommands. - xp->xp_context = EXPAND_CSCOPE; - xp->xp_pattern = (char *)arg; - expand_what = ((cmdidx == CMD_scscope) - ? EXP_SCSCOPE_SUBCMD : EXP_CSCOPE_SUBCMD); - - // (part of) subcommand already typed - if (*arg != NUL) { - const char *p = (const char *)skiptowhite(arg); - if (*p != NUL) { // Past first word. - xp->xp_pattern = skipwhite(p); - if (*skiptowhite(xp->xp_pattern) != NUL) { - xp->xp_context = EXPAND_NOTHING; - } else if (STRNICMP(arg, "add", p - arg) == 0) { - xp->xp_context = EXPAND_FILES; - } else if (STRNICMP(arg, "kill", p - arg) == 0) { - expand_what = EXP_CSCOPE_KILL; - } else if (STRNICMP(arg, "find", p - arg) == 0) { - expand_what = EXP_CSCOPE_FIND; - } else { - xp->xp_context = EXPAND_NOTHING; - } - } - } -} - -/// Find the command, print help if invalid, and then call the corresponding -/// command function. -/// -/// @param make_split whether to split window -static void do_cscope_general(exarg_T *eap, int make_split) -{ - cscmd_T *cmdp; - - if ((cmdp = cs_lookup_cmd(eap)) == NULL) { - cs_help(eap); - return; - } - - if (make_split) { - if (!cmdp->cansplit) { - (void)msg_puts(_("This cscope command does not support splitting the window.\n")); - return; - } - postponed_split = -1; - postponed_split_flags = cmdmod.cmod_split; - postponed_split_tab = cmdmod.cmod_tab; - } - - cmdp->func(eap); - - postponed_split_flags = 0; - postponed_split_tab = 0; -} - -/// Implementation of ":cscope" and ":lcscope" -void ex_cscope(exarg_T *eap) -{ - do_cscope_general(eap, false); -} - -/// Implementation of ":scscope". Same as ex_cscope(), but splits window, too. -void ex_scscope(exarg_T *eap) -{ - do_cscope_general(eap, true); -} - -/// Implementation of ":cstag" -void ex_cstag(exarg_T *eap) -{ - int ret = false; - - if (*eap->arg == NUL) { - (void)emsg(_("E562: Usage: cstag ")); - return; - } - - switch (p_csto) { - case 0: - if (cs_check_for_connections()) { - ret = cs_find_common("g", eap->arg, eap->forceit, false, - false, (char_u *)(*eap->cmdlinep)); - if (ret == false) { - cs_free_tags(); - if (msg_col) { - msg_putchar('\n'); - } - - if (cs_check_for_tags()) { - ret = do_tag(eap->arg, DT_JUMP, 0, eap->forceit, false); - } - } - } else if (cs_check_for_tags()) { - ret = do_tag(eap->arg, DT_JUMP, 0, eap->forceit, false); - } - break; - case 1: - if (cs_check_for_tags()) { - ret = do_tag(eap->arg, DT_JUMP, 0, eap->forceit, false); - if (ret == false) { - if (msg_col) { - msg_putchar('\n'); - } - - if (cs_check_for_connections()) { - ret = cs_find_common("g", eap->arg, eap->forceit, - false, false, (char_u *)(*eap->cmdlinep)); - if (ret == false) { - cs_free_tags(); - } - } - } - } else if (cs_check_for_connections()) { - ret = cs_find_common("g", eap->arg, eap->forceit, false, - false, (char_u *)(*eap->cmdlinep)); - if (ret == false) { - cs_free_tags(); - } - } - break; - default: - break; - } - - if (!ret) { - (void)emsg(_("E257: cstag: tag not found")); - g_do_tagpreview = 0; - } -} - -/// This simulates a vim_fgets(), but for cscope, returns the next line -/// from the cscope output. should only be called from find_tags() -/// -/// @return true if eof, false otherwise -bool cs_fgets(char_u *buf, int size) - FUNC_ATTR_NONNULL_ALL -{ - char *p; - - if ((p = cs_manage_matches(NULL, NULL, 0, Get)) == NULL) { - return true; - } - STRLCPY(buf, p, size); - - return false; -} - -/// Called only from do_tag(), when popping the tag stack. -void cs_free_tags(void) -{ - cs_manage_matches(NULL, NULL, 0, Free); -} - -/// Called from do_tag(). -void cs_print_tags(void) -{ - cs_manage_matches(NULL, NULL, 0, Print); -} - -// "cscope_connection([{num} , {dbpath} [, {prepend}]])" function -// -// Checks for the existence of a |cscope| connection. If no -// parameters are specified, then the function returns: -// -// 0, if cscope was not available (not compiled in), or if there -// are no cscope connections; or -// 1, if there is at least one cscope connection. -// -// If parameters are specified, then the value of {num} -// determines how existence of a cscope connection is checked: -// -// {num} Description of existence check -// ----- ------------------------------ -// 0 Same as no parameters (e.g., "cscope_connection()"). -// 1 Ignore {prepend}, and use partial string matches for -// {dbpath}. -// 2 Ignore {prepend}, and use exact string matches for -// {dbpath}. -// 3 Use {prepend}, use partial string matches for both -// {dbpath} and {prepend}. -// 4 Use {prepend}, use exact string matches for both -// {dbpath} and {prepend}. -// -// Note: All string comparisons are case sensitive! -bool cs_connection(int num, char_u *dbpath, char_u *ppath) -{ - if (num < 0 || num > 4 || (num > 0 && !dbpath)) { - return false; - } - - for (size_t i = 0; i < csinfo_size; i++) { - if (!csinfo[i].fname) { - continue; - } - if (num == 0) { - return true; - } - switch (num) { - case 1: - if (strstr(csinfo[i].fname, (char *)dbpath)) { - return true; - } - break; - case 2: - if (strcmp(csinfo[i].fname, (char *)dbpath) == 0) { - return true; - } - break; - case 3: - if (strstr(csinfo[i].fname, (char *)dbpath) - && ((!ppath && !csinfo[i].ppath) - || (ppath - && csinfo[i].ppath - && strstr(csinfo[i].ppath, (char *)ppath)))) { - return true; - } - break; - case 4: - if ((strcmp(csinfo[i].fname, (char *)dbpath) == 0) - && ((!ppath && !csinfo[i].ppath) - || (ppath - && csinfo[i].ppath - && (strcmp(csinfo[i].ppath, (char *)ppath) == 0)))) { - return true; - } - break; - } - } - - return false; -} // cs_connection - -// PRIVATE functions -// ************************************************************************** - -/// Add cscope database or a directory name (to look for cscope.out) -/// to the cscope connection list. -static int cs_add(exarg_T *eap) -{ - char *fname, *ppath, *flags = NULL; - - if ((fname = strtok((char *)NULL, (const char *)" ")) == NULL) { - cs_usage_msg(Add); - return CSCOPE_FAILURE; - } - if ((ppath = strtok((char *)NULL, (const char *)" ")) != NULL) { - flags = strtok((char *)NULL, (const char *)" "); - } - - return cs_add_common(fname, ppath, flags); -} - -static void cs_stat_emsg(char *fname) -{ - int err = errno; - (void)semsg(_("E563: stat(%s) error: %d"), fname, err); -} - -/// The common routine to add a new cscope connection. Called by -/// cs_add() and cs_reset(). I really don't like to do this, but this -/// routine uses a number of goto statements. -/// -/// @param arg1 filename - may contain environment variables -/// @param arg2 prepend path - may contain environment variables -static int cs_add_common(char *arg1, char *arg2, char *flags) -{ - char *fname = NULL; - char *fname2 = NULL; - char *ppath = NULL; - size_t usedlen = 0; - char *fbuf = NULL; - - // get the filename (arg1), expand it, and try to stat it - fname = xmalloc(MAXPATHL + 1); - - expand_env(arg1, fname, MAXPATHL); - size_t len = strlen(fname); - fbuf = fname; - (void)modify_fname(":p", false, &usedlen, &fname, &fbuf, &len); - if (fname == NULL) { - goto add_err; - } - fname = xstrnsave(fname, len); - xfree(fbuf); - FileInfo file_info; - bool file_info_ok = os_fileinfo(fname, &file_info); - if (!file_info_ok) { -staterr: - if (p_csverbose) { - cs_stat_emsg(fname); - } - goto add_err; - } - - // get the prepend path (arg2), expand it, and see if it exists - if (arg2 != NULL) { - ppath = xmalloc(MAXPATHL + 1); - expand_env(arg2, ppath, MAXPATHL); - if (!os_path_exists(ppath)) { - goto staterr; - } - } - - int i; - // if filename is a directory, append the cscope database name to it - if (S_ISDIR(file_info.stat.st_mode)) { - fname2 = (char *)xmalloc(strlen(CSCOPE_DBFILE) + strlen(fname) + 2); - - while (fname[strlen(fname) - 1] == '/' - ) { - fname[strlen(fname) - 1] = '\0'; - if (fname[0] == '\0') { - break; - } - } - if (fname[0] == '\0') { - (void)sprintf(fname2, "/%s", CSCOPE_DBFILE); - } else { - (void)sprintf(fname2, "%s/%s", fname, CSCOPE_DBFILE); - } - - file_info_ok = os_fileinfo(fname2, &file_info); - if (!file_info_ok) { - if (p_csverbose) { - cs_stat_emsg(fname2); - } - goto add_err; - } - - i = cs_insert_filelist(fname2, ppath, flags, &file_info); - } else if (S_ISREG(file_info.stat.st_mode) || S_ISLNK(file_info.stat.st_mode)) { - i = cs_insert_filelist(fname, ppath, flags, &file_info); - } else { - if (p_csverbose) { - (void)semsg(_("E564: %s is not a directory or a valid cscope database"), - fname); - } - goto add_err; - } - - if (i != -1) { - assert(i >= 0); - if (cs_create_connection((size_t)i) == CSCOPE_FAILURE - || cs_read_prompt((size_t)i) == CSCOPE_FAILURE) { - cs_release_csp((size_t)i, true); - goto add_err; - } - - if (p_csverbose) { - msg_clr_eos(); - (void)smsg_attr(HL_ATTR(HLF_R), - _("Added cscope database %s"), - csinfo[i].fname); - } - } - - xfree(fname); - xfree(fname2); - xfree(ppath); - return CSCOPE_SUCCESS; - -add_err: - xfree(fname2); - xfree(fname); - xfree(ppath); - return CSCOPE_FAILURE; -} - -static bool cs_check_for_connections(void) -{ - return cs_cnt_connections() > 0; -} - -static int cs_check_for_tags(void) -{ - return p_tags[0] != NUL && curbuf->b_p_tags != NULL; -} - -/// Count the number of cscope connections. -static size_t cs_cnt_connections(void) -{ - size_t cnt = 0; - - for (size_t i = 0; i < csinfo_size; i++) { - if (csinfo[i].fname != NULL) { - cnt++; - } - } - return cnt; -} - -/// @param idx connection index -static void cs_reading_emsg(size_t idx) -{ - semsg(_("E262: error reading cscope connection %" PRIu64), (uint64_t)idx); -} - -#define CSREAD_BUFSIZE 2048 -/// Count the number of matches for a given cscope connection. -static int cs_cnt_matches(size_t idx) -{ - char *stok; - int nlines = 0; - - char *buf = xmalloc(CSREAD_BUFSIZE); - for (;;) { - errno = 0; - if (!fgets(buf, CSREAD_BUFSIZE, csinfo[idx].fr_fp)) { - if (errno == EINTR) { - continue; - } - - if (feof(csinfo[idx].fr_fp)) { - errno = EIO; - } - - cs_reading_emsg(idx); - - xfree(buf); - return CSCOPE_FAILURE; - } - - // If the database is out of date, or there's some other problem, - // cscope will output error messages before the number-of-lines output. - // Display/discard any output that doesn't match what we want. - // Accept "\S*cscope: X lines", also matches "mlcscope". - // Bail out for the "Unable to search" error. - if (strstr((const char *)buf, "Unable to search database") != NULL) { - break; - } - if ((stok = strtok(buf, (const char *)" ")) == NULL) { - continue; - } - if (strstr((const char *)stok, "cscope:") == NULL) { - continue; - } - - if ((stok = strtok(NULL, (const char *)" ")) == NULL) { - continue; - } - nlines = atoi(stok); - if (nlines < 0) { - nlines = 0; - break; - } - - if ((stok = strtok(NULL, (const char *)" ")) == NULL) { - continue; - } - if (strncmp(stok, "lines", 5)) { - continue; - } - - break; - } - - xfree(buf); - return nlines; -} - -/// Creates the actual cscope command query from what the user entered. -static char *cs_create_cmd(char *csoption, char *pattern) -{ - char *cmd; - short search; - char *pat; - - switch (csoption[0]) { - case '0': - case 's': - search = 0; - break; - case '1': - case 'g': - search = 1; - break; - case '2': - case 'd': - search = 2; - break; - case '3': - case 'c': - search = 3; - break; - case '4': - case 't': - search = 4; - break; - case '6': - case 'e': - search = 6; - break; - case '7': - case 'f': - search = 7; - break; - case '8': - case 'i': - search = 8; - break; - case '9': - case 'a': - search = 9; - break; - default: - (void)emsg(_("E561: unknown cscope search type")); - cs_usage_msg(Find); - return NULL; - } - - // Skip white space before the pattern, except for text and pattern search, - // they may want to use the leading white space. - pat = pattern; - if (search != 4 && search != 6) { - while (ascii_iswhite(*pat)) { - pat++; - } - } - - cmd = xmalloc(strlen(pat) + 2); - - (void)sprintf(cmd, "%d%s", search, pat); - - return cmd; -} - -/// This piece of code was taken/adapted from nvi. do we need to add -/// the BSD license notice? -static int cs_create_connection(size_t i) -{ -#ifdef UNIX - int to_cs[2], from_cs[2]; -#endif - char *prog, *cmd, *ppath = NULL; - -#if defined(UNIX) - // Cscope reads from to_cs[0] and writes to from_cs[1]; vi reads from - // from_cs[0] and writes to to_cs[1]. - to_cs[0] = to_cs[1] = from_cs[0] = from_cs[1] = -1; - if (pipe(to_cs) < 0 || pipe(from_cs) < 0) { - (void)emsg(_("E566: Could not create cscope pipes")); -err_closing: - if (to_cs[0] != -1) { - (void)close(to_cs[0]); - } - if (to_cs[1] != -1) { - (void)close(to_cs[1]); - } - if (from_cs[0] != -1) { - (void)close(from_cs[0]); - } - if (from_cs[1] != -1) { - (void)close(from_cs[1]); - } - return CSCOPE_FAILURE; - } - - switch (csinfo[i].pid = fork()) { - case -1: - (void)emsg(_("E622: Could not fork for cscope")); - goto err_closing; - case 0: // child: run cscope. - if (dup2(to_cs[0], STDIN_FILENO) == -1) { - PERROR("cs_create_connection 1"); - } - if (dup2(from_cs[1], STDOUT_FILENO) == -1) { - PERROR("cs_create_connection 2"); - } - if (dup2(from_cs[1], STDERR_FILENO) == -1) { - PERROR("cs_create_connection 3"); - } - - // close unused - (void)close(to_cs[1]); - (void)close(from_cs[0]); -#else - // Create pipes to communicate with cscope - int fd; - SECURITY_ATTRIBUTES sa; - PROCESS_INFORMATION pi; - BOOL pipe_stdin = FALSE, pipe_stdout = FALSE; // NOLINT(readability/bool) - STARTUPINFO si; - HANDLE stdin_rd, stdout_rd; - HANDLE stdout_wr, stdin_wr; - BOOL created; - - sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.bInheritHandle = TRUE; - sa.lpSecurityDescriptor = NULL; - - if (!(pipe_stdin = CreatePipe(&stdin_rd, &stdin_wr, &sa, 0)) - || !(pipe_stdout = CreatePipe(&stdout_rd, &stdout_wr, &sa, 0))) { - (void)emsg(_("E566: Could not create cscope pipes")); -err_closing: - if (pipe_stdin) { - CloseHandle(stdin_rd); - CloseHandle(stdin_wr); - } - if (pipe_stdout) { - CloseHandle(stdout_rd); - CloseHandle(stdout_wr); - } - return CSCOPE_FAILURE; - } -#endif - // expand the cscope exec for env var's - prog = xmalloc(MAXPATHL + 1); - expand_env(p_csprg, prog, MAXPATHL); - - // alloc space to hold the cscope command - size_t len = strlen(prog) + strlen(csinfo[i].fname) + 32; - if (csinfo[i].ppath) { - // expand the prepend path for env var's - ppath = xmalloc(MAXPATHL + 1); - expand_env(csinfo[i].ppath, ppath, MAXPATHL); - - len += strlen(ppath); - } - - if (csinfo[i].flags) { - len += strlen(csinfo[i].flags); - } - - cmd = xmalloc(len); - - // run the cscope command; is there execl for non-unix systems? -#if defined(UNIX) - (void)snprintf(cmd, len, "exec %s -dl -f %s", prog, csinfo[i].fname); -#else - // MS-Windows - (void)snprintf(cmd, len, "%s -dl -f %s", prog, csinfo[i].fname); -#endif - if (csinfo[i].ppath != NULL) { - (void)strcat(cmd, " -P"); - (void)strcat(cmd, csinfo[i].ppath); - } - if (csinfo[i].flags != NULL) { - (void)strcat(cmd, " "); - (void)strcat(cmd, csinfo[i].flags); - } -#ifdef UNIX - // on Win32 we still need prog - xfree(prog); -#endif - xfree(ppath); - -#if defined(UNIX) -# if defined(HAVE_SETSID) || defined(HAVE_SETPGID) - // Change our process group to avoid cscope receiving SIGWINCH. -# if defined(HAVE_SETSID) - (void)setsid(); -# else - if (setpgid(0, 0) == -1) { - PERROR(_("cs_create_connection setpgid failed")); - } -# endif -# endif - if (execl("/bin/sh", "sh", "-c", cmd, (char *)NULL) == -1) { - PERROR(_("cs_create_connection exec failed")); - } - - exit(127); - // NOTREACHED - default: // parent. - // Save the file descriptors for later duplication, and - // reopen as streams. - if ((csinfo[i].to_fp = fdopen(to_cs[1], "w")) == NULL) { - PERROR(_("cs_create_connection: fdopen for to_fp failed")); - } - if ((csinfo[i].fr_fp = fdopen(from_cs[0], "r")) == NULL) { - PERROR(_("cs_create_connection: fdopen for fr_fp failed")); - } - - // close unused - (void)close(to_cs[0]); - (void)close(from_cs[1]); - - break; - } - -#else - // MS-Windows - // Create a new process to run cscope and use pipes to talk with it - GetStartupInfo(&si); - si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; - si.wShowWindow = SW_HIDE; // Hide child application window - si.hStdOutput = stdout_wr; - si.hStdError = stdout_wr; - si.hStdInput = stdin_rd; - created = CreateProcess(NULL, cmd, NULL, NULL, true, CREATE_NEW_CONSOLE, - NULL, NULL, &si, &pi); - xfree(prog); - xfree(cmd); - - if (!created) { - PERROR(_("cs_create_connection exec failed")); - (void)emsg(_("E623: Could not spawn cscope process")); - goto err_closing; - } - // else - csinfo[i].pid = pi.dwProcessId; - csinfo[i].hProc = pi.hProcess; - CloseHandle(pi.hThread); - - // TODO(neovim): tidy up after failure to create files on pipe handles. - if (((fd = _open_osfhandle((intptr_t)stdin_wr, _O_TEXT|_O_APPEND)) < 0) - || ((csinfo[i].to_fp = _fdopen(fd, "w")) == NULL)) { - PERROR(_("cs_create_connection: fdopen for to_fp failed")); - } - if (((fd = _open_osfhandle((intptr_t)stdout_rd, _O_TEXT|_O_RDONLY)) < 0) - || ((csinfo[i].fr_fp = _fdopen(fd, "r")) == NULL)) { - PERROR(_("cs_create_connection: fdopen for fr_fp failed")); - } - // Close handles for file descriptors inherited by the cscope process. - CloseHandle(stdin_rd); - CloseHandle(stdout_wr); - -#endif // !UNIX - - return CSCOPE_SUCCESS; -} - -/// Query cscope using command line interface. Parse the output and use tselect -/// to allow choices. Like Nvi, creates a pipe to send to/from query/cscope. -/// -/// @return true if we jump to a tag or abort, false if not. -static int cs_find(exarg_T *eap) -{ - char *opt, *pat; - - if (cs_check_for_connections() == false) { - (void)emsg(_("E567: no cscope connections")); - return false; - } - - if ((opt = strtok((char *)NULL, (const char *)" ")) == NULL) { - cs_usage_msg(Find); - return false; - } - - pat = opt + strlen(opt) + 1; - if (pat >= eap->arg + eap_arg_len) { - cs_usage_msg(Find); - return false; - } - - // Let's replace the NULs written by strtok() with spaces - we need the - // spaces to correctly display the quickfix/location list window's title. - for (int i = 0; i < eap_arg_len; i++) { - if (NUL == eap->arg[i]) { - eap->arg[i] = ' '; - } - } - - return cs_find_common(opt, pat, eap->forceit, true, - eap->cmdidx == CMD_lcscope, (char_u *)(*eap->cmdlinep)); -} - -/// Common code for cscope find, shared by cs_find() and ex_cstag(). -static bool cs_find_common(char *opt, char *pat, int forceit, int verbose, bool use_ll, - char_u *cmdline) -{ - char *cmd; - int *nummatches; - size_t totmatches; - char cmdletter; - char *qfpos; - - // get cmd letter - switch (opt[0]) { - case '0': - cmdletter = 's'; - break; - case '1': - cmdletter = 'g'; - break; - case '2': - cmdletter = 'd'; - break; - case '3': - cmdletter = 'c'; - break; - case '4': - cmdletter = 't'; - break; - case '6': - cmdletter = 'e'; - break; - case '7': - cmdletter = 'f'; - break; - case '8': - cmdletter = 'i'; - break; - case '9': - cmdletter = 'a'; - break; - default: - cmdletter = opt[0]; - } - - qfpos = vim_strchr(p_csqf, cmdletter); - if (qfpos != NULL) { - qfpos++; - // next symbol must be + or - - if (strchr(CSQF_FLAGS, *qfpos) == NULL) { - (void)semsg(_("E469: invalid cscopequickfix flag %c for %c"), *qfpos, *(qfpos - 1)); - return false; - } - - if (*qfpos != '0' - && apply_autocmds(EVENT_QUICKFIXCMDPRE, "cscope", curbuf->b_fname, true, curbuf)) { - if (aborting()) { - return false; - } - } - } - - // create the actual command to send to cscope - cmd = cs_create_cmd(opt, pat); - if (cmd == NULL) { - return false; - } - - nummatches = xmalloc(sizeof(int) * csinfo_size); - - // Send query to all open connections, then count the total number - // of matches so we can alloc all in one swell foop. - for (size_t i = 0; i < csinfo_size; i++) { - nummatches[i] = 0; - } - totmatches = 0; - for (size_t i = 0; i < csinfo_size; i++) { - if (csinfo[i].fname == NULL || csinfo[i].to_fp == NULL) { - continue; - } - - // send cmd to cscope - (void)fprintf(csinfo[i].to_fp, "%s\n", cmd); - (void)fflush(csinfo[i].to_fp); - - nummatches[i] = cs_cnt_matches(i); - - if (nummatches[i] > -1) { - totmatches += (size_t)nummatches[i]; - } - - if (nummatches[i] == 0) { - (void)cs_read_prompt(i); - } - } - xfree(cmd); - - if (totmatches == 0) { - if (verbose) { - (void)semsg(_("E259: no matches found for cscope query %s of %s"), opt, pat); - } - xfree(nummatches); - return false; - } - - if (qfpos != NULL && *qfpos != '0') { - // Fill error list. - FILE *f; - char_u *tmp = (char_u *)vim_tempname(); - qf_info_T *qi = NULL; - win_T *wp = NULL; - - f = os_fopen((char *)tmp, "w"); - if (f == NULL) { - semsg(_(e_notopen), tmp); - } else { - cs_file_results(f, nummatches); - fclose(f); - if (use_ll) { // Use location list - wp = curwin; - } - // '-' starts a new error list - if (qf_init(wp, (char *)tmp, "%f%*\\t%l%*\\t%m", - *qfpos == '-', (char *)cmdline, NULL) > 0) { - if (postponed_split != 0) { - (void)win_split(postponed_split > 0 ? postponed_split : 0, - postponed_split_flags); - RESET_BINDING(curwin); - postponed_split = 0; - } - - apply_autocmds(EVENT_QUICKFIXCMDPOST, "cscope", curbuf->b_fname, true, curbuf); - if (use_ll) { - // In the location list window, use the displayed location - // list. Otherwise, use the location list for the window. - qi = (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL) - ? wp->w_llist_ref : wp->w_llist; - } - qf_jump(qi, 0, 0, forceit); - } - } - os_remove((char *)tmp); - xfree(tmp); - xfree(nummatches); - return true; - } else { - char **matches = NULL, **contexts = NULL; - size_t matched = 0; - - // read output - cs_fill_results(pat, totmatches, nummatches, &matches, &contexts, &matched); - xfree(nummatches); - if (matches == NULL) { - return false; - } - - (void)cs_manage_matches(matches, contexts, matched, Store); - - return do_tag(pat, DT_CSCOPE, 0, forceit, verbose); - } -} - -/// Print help. -static int cs_help(exarg_T *eap) -{ - cscmd_T *cmdp = cs_cmds; - - (void)msg_puts(_("cscope commands:\n")); - while (cmdp->name != NULL) { - char *help = _(cmdp->help); - int space_cnt = 30 - vim_strsize(help); - - // Use %*s rather than %30s to ensure proper alignment in utf-8 - if (space_cnt < 0) { - space_cnt = 0; - } - (void)smsg(_("%-5s: %s%*s (Usage: %s)"), - cmdp->name, - help, space_cnt, " ", - cmdp->usage); - if (strcmp(cmdp->name, "find") == 0) { - msg_puts(_("\n" - " a: Find assignments to this symbol\n" - " c: Find functions calling this function\n" - " d: Find functions called by this function\n" - " e: Find this egrep pattern\n" - " f: Find this file\n" - " g: Find this definition\n" - " i: Find files #including this file\n" - " s: Find this C symbol\n" - " t: Find this text string\n")); - } - - cmdp++; - } - - wait_return(true); - return CSCOPE_SUCCESS; -} - -static void clear_csinfo(size_t i) -{ - csinfo[i].fname = NULL; - csinfo[i].ppath = NULL; - csinfo[i].flags = NULL; - csinfo[i].file_id = FILE_ID_EMPTY; - csinfo[i].pid = 0; - csinfo[i].fr_fp = NULL; - csinfo[i].to_fp = NULL; -} - -/// Insert a new cscope database filename into the filelist. -static int cs_insert_filelist(char *fname, char *ppath, char *flags, FileInfo *file_info) -{ - size_t i = 0; - bool empty_found = false; - - for (size_t j = 0; j < csinfo_size; j++) { - if (csinfo[j].fname != NULL - && os_fileid_equal_fileinfo(&(csinfo[j].file_id), file_info)) { - if (p_csverbose) { - (void)emsg(_("E568: duplicate cscope database not added")); - } - return CSCOPE_FAILURE; - } - - if (csinfo[j].fname == NULL && !empty_found) { - i = j; // remember first empty entry - empty_found = true; - } - } - - if (!empty_found) { - i = csinfo_size; - if (csinfo_size == 0) { - // First time allocation: allocate only 1 connection. It should - // be enough for most users. If more is needed, csinfo will be - // reallocated. - csinfo_size = 1; - csinfo = xcalloc(1, sizeof(csinfo_T)); - } else { - // Reallocate space for more connections. - csinfo_size *= 2; - csinfo = xrealloc(csinfo, sizeof(csinfo_T)*csinfo_size); - } - for (size_t j = csinfo_size/2; j < csinfo_size; j++) { - clear_csinfo(j); - } - } - - csinfo[i].fname = xmalloc(strlen(fname) + 1); - - (void)strcpy(csinfo[i].fname, (const char *)fname); - - if (ppath != NULL) { - csinfo[i].ppath = xmalloc(strlen(ppath) + 1); - (void)strcpy(csinfo[i].ppath, (const char *)ppath); - } else { - csinfo[i].ppath = NULL; - } - - if (flags != NULL) { - csinfo[i].flags = xmalloc(strlen(flags) + 1); - (void)strcpy(csinfo[i].flags, (const char *)flags); - } else { - csinfo[i].flags = NULL; - } - - os_fileinfo_id(file_info, &(csinfo[i].file_id)); - assert(i <= INT_MAX); - return (int)i; -} - -/// Find cscope command in command table. -static cscmd_T *cs_lookup_cmd(exarg_T *eap) -{ - cscmd_T *cmdp; - char *stok; - size_t len; - - if (eap->arg == NULL) { - return NULL; - } - - // Store length of eap->arg before it gets modified by strtok(). - eap_arg_len = (int)strlen(eap->arg); - - if ((stok = strtok(eap->arg, (const char *)" ")) == NULL) { // NOLINT(runtime/threadsafe_fn) - return NULL; - } - - len = strlen(stok); - for (cmdp = cs_cmds; cmdp->name != NULL; cmdp++) { - if (strncmp(stok, cmdp->name, len) == 0) { - return cmdp; - } - } - return NULL; -} - -/// Nuke em. -static int cs_kill(exarg_T *eap) -{ - char *stok; - int num; - size_t i = 0; - bool killall = false; - - if ((stok = strtok((char *)NULL, (const char *)" ")) == NULL) { - cs_usage_msg(Kill); - return CSCOPE_FAILURE; - } - - // Check if string is a number, only single digit - // positive and negative integers are allowed - if ((strlen(stok) < 2 && ascii_isdigit((int)(stok[0]))) - || (strlen(stok) < 3 && stok[0] == '-' - && ascii_isdigit((int)(stok[1])))) { - num = atoi(stok); - if (num == -1) { - killall = true; - } else if (num >= 0) { - i = (size_t)num; - } else { // All negative values besides -1 are invalid. - if (p_csverbose) { - (void)semsg(_("E261: cscope connection %s not found"), stok); - } - return CSCOPE_FAILURE; - } - } else { - // Else it must be part of a name. We will try to find a match - // within all the names in the csinfo data structure - for (i = 0; i < csinfo_size; i++) { - if (csinfo[i].fname != NULL && strstr(csinfo[i].fname, stok)) { - break; - } - } - } - - if (!killall && (i >= csinfo_size || csinfo[i].fname == NULL)) { - if (p_csverbose) { - (void)semsg(_("E261: cscope connection %s not found"), stok); - } - return CSCOPE_FAILURE; - } else { - if (killall) { - for (i = 0; i < csinfo_size; i++) { - if (csinfo[i].fname) { - cs_kill_execute(i, csinfo[i].fname); - } - } - } else { - cs_kill_execute(i, stok); - } - } - - return CSCOPE_SUCCESS; -} - -/// Actually kills a specific cscope connection. -/// -/// @param i cscope table index -/// @param cname cscope database name -static void cs_kill_execute(size_t i, char *cname) -{ - if (p_csverbose) { - msg_clr_eos(); - (void)smsg_attr(HL_ATTR(HLF_R) | MSG_HIST, - _("cscope connection %s closed"), cname); - } - cs_release_csp(i, true); -} - -/// Convert the cscope output into a ctags style entry (as might be found -/// in a ctags tags file). there's one catch though: cscope doesn't tell you -/// the type of the tag you are looking for. for example, in Darren Hiebert's -/// ctags (the one that comes with vim), #define's use a line number to find the -/// tag in a file while function definitions use a regexp search pattern. -/// -/// I'm going to always use the line number because cscope does something -/// quirky (and probably other things i don't know about): -/// -/// if you have "# define" in your source file, which is -/// perfectly legal, cscope thinks you have "#define". this -/// will result in a failed regexp search. :( -/// -/// Besides, even if this particular case didn't happen, the search pattern -/// would still have to be modified to escape all the special regular expression -/// characters to comply with ctags formatting. -static char *cs_make_vim_style_matches(char *fname, char *slno, char *search, char *tagstr) -{ - // vim style is ctags: - // - // \t\t"\t - // - // but as mentioned above, we'll always use the line number and - // put the search pattern (if one exists) as "extra" - // - // buf is used as part of vim's method of handling tags, and - // (i think) vim frees it when you pop your tags and get replaced - // by new ones on the tag stack. - char *buf; - size_t amt; - - if (search != NULL) { - amt = strlen(fname) + strlen(slno) + strlen(tagstr) + strlen(search) + 6; - buf = xmalloc(amt); - - (void)sprintf(buf, "%s\t%s\t%s;\"\t%s", tagstr, fname, slno, search); - } else { - amt = strlen(fname) + strlen(slno) + strlen(tagstr) + 5; - buf = xmalloc(amt); - - (void)sprintf(buf, "%s\t%s\t%s;\"", tagstr, fname, slno); - } - - return buf; -} - -/// This is kind of hokey, but i don't see an easy way round this. -/// -/// Store: keep a ptr to the (malloc'd) memory of matches originally -/// generated from cs_find(). the matches are originally lines directly -/// from cscope output, but transformed to look like something out of a -/// ctags. see cs_make_vim_style_matches for more details. -/// -/// Get: used only from cs_fgets(), this simulates a vim_fgets() to return -/// the next line from the cscope output. it basically keeps track of which -/// lines have been "used" and returns the next one. -/// -/// Free: frees up everything and resets -/// -/// Print: prints the tags -static char *cs_manage_matches(char **matches, char **contexts, size_t totmatches, mcmd_e cmd) -{ - static char **mp = NULL; - static char **cp = NULL; - static size_t cnt = 0; - static size_t next = 0; - char *p = NULL; - - switch (cmd) { - case Store: - assert(matches != NULL); - assert(totmatches > 0); - if (mp != NULL || cp != NULL) { - (void)cs_manage_matches(NULL, NULL, 0, Free); - } - mp = matches; - cp = contexts; - cnt = totmatches; - next = 0; - break; - case Get: - if (next >= cnt) { - return NULL; - } - - p = mp[next]; - next++; - break; - case Free: - if (mp != NULL) { - while (cnt--) { - xfree(mp[cnt]); - if (cp != NULL) { - xfree(cp[cnt]); - } - } - xfree(mp); - xfree(cp); - } - mp = NULL; - cp = NULL; - cnt = 0; - next = 0; - break; - case Print: - assert(mp != NULL); - assert(cp != NULL); - cs_print_tags_priv(mp, cp, cnt); - break; - default: // should not reach here - iemsg(_("E570: fatal error in cs_manage_matches")); - return NULL; - } - - return p; -} - -/// Parse cscope output. -static char *cs_parse_results(size_t cnumber, char *buf, int bufsize, char **context, - char **linenumber, char **search) -{ - int ch; - char *p; - char *name; - -retry: - errno = 0; - if (fgets(buf, bufsize, csinfo[cnumber].fr_fp) == NULL) { - if (errno == EINTR) { - goto retry; - } - - if (feof(csinfo[cnumber].fr_fp)) { - errno = EIO; - } - - cs_reading_emsg(cnumber); - - return NULL; - } - - // If the line's too long for the buffer, discard it. - if ((p = strchr(buf, '\n')) == NULL) { - while ((ch = getc(csinfo[cnumber].fr_fp)) != EOF && ch != '\n') {} - return NULL; - } - *p = '\0'; - - // cscope output is in the following format: - // - // - char *saveptr = NULL; - if ((name = os_strtok(buf, (const char *)" ", &saveptr)) == NULL) { - return NULL; - } - if ((*context = os_strtok(NULL, (const char *)" ", &saveptr)) == NULL) { - return NULL; - } - if ((*linenumber = os_strtok(NULL, (const char *)" ", &saveptr)) == NULL) { - return NULL; - } - *search = *linenumber + strlen(*linenumber) + 1; // +1 to skip \0 - - // --- nvi --- - // If the file is older than the cscope database, that is, - // the database was built since the file was last modified, - // or there wasn't a search string, use the line number. - if (strcmp(*search, "") == 0) { - *search = NULL; - } - - name = cs_resolve_file(cnumber, name); - return name; -} - -/// Write cscope find results to file. -static void cs_file_results(FILE *f, int *nummatches_a) -{ - char *search, *slno; - char *fullname; - char *cntx; - char *context; - - char *buf = xmalloc(CSREAD_BUFSIZE); - - for (size_t i = 0; i < csinfo_size; i++) { - if (nummatches_a[i] < 1) { - continue; - } - - for (int j = 0; j < nummatches_a[i]; j++) { - if ((fullname = cs_parse_results(i, buf, CSREAD_BUFSIZE, &cntx, - &slno, &search)) == NULL) { - continue; - } - - size_t context_len = strlen(cntx) + 5; - context = xmalloc(context_len); - - if (strcmp(cntx, "") == 0) { - xstrlcpy(context, "<>", context_len); - } else { - snprintf(context, context_len, "<<%s>>", cntx); - } - - if (search == NULL) { - fprintf(f, "%s\t%s\t%s\n", fullname, slno, context); - } else { - fprintf(f, "%s\t%s\t%s %s\n", fullname, slno, context, search); - } - - xfree(context); - xfree(fullname); - } // for all matches - - (void)cs_read_prompt(i); - } // for all cscope connections - xfree(buf); -} - -/// Get parsed cscope output and calls cs_make_vim_style_matches to convert -/// into ctags format. -/// When there are no matches sets "*matches_p" to NULL. -static void cs_fill_results(char *tagstr, size_t totmatches, int *nummatches_a, char ***matches_p, - char ***cntxts_p, size_t *matched) -{ - char *buf; - char *search, *slno; - size_t totsofar = 0; - char **matches = NULL; - char **cntxts = NULL; - char *fullname; - char *cntx; - - assert(totmatches > 0); - - buf = xmalloc(CSREAD_BUFSIZE); - matches = xmalloc(sizeof(char *) * totmatches); - cntxts = xmalloc(sizeof(char *) * totmatches); - - for (size_t i = 0; i < csinfo_size; i++) { - if (nummatches_a[i] < 1) { - continue; - } - - for (int j = 0; j < nummatches_a[i]; j++) { - if ((fullname = cs_parse_results(i, buf, CSREAD_BUFSIZE, &cntx, - &slno, &search)) == NULL) { - continue; - } - - matches[totsofar] = cs_make_vim_style_matches(fullname, slno, search, - tagstr); - - xfree(fullname); - - if (strcmp(cntx, "") == 0) { - cntxts[totsofar] = NULL; - } else { - cntxts[totsofar] = xstrdup(cntx); - } - - totsofar++; - } // for all matches - - (void)cs_read_prompt(i); - } // for all cscope connections - - if (totsofar == 0) { - // No matches, free the arrays and return NULL in "*matches_p". - XFREE_CLEAR(matches); - XFREE_CLEAR(cntxts); - } - *matched = totsofar; - *matches_p = matches; - *cntxts_p = cntxts; - - xfree(buf); -} - -// get the requested path components -static char *cs_pathcomponents(char *path) -{ - if (p_cspc == 0) { - return path; - } - - char *s = path + strlen(path) - 1; - for (int i = 0; i < p_cspc; i++) { - while (s > path && *--s != '/') {} - } - if ((s > path && *s == '/')) { - s++; - } - return s; -} - -/// Print cscope output that was converted into ctags style entries. -/// -/// Only called from cs_manage_matches(). -/// -/// @param matches Array of cscope lines in ctags style. Every entry was -// produced with a format string of the form -// "%s\t%s\t%s;\"\t%s" or -// "%s\t%s\t%s;\"" -// by cs_make_vim_style_matches(). -/// @param cntxts Context for matches. -/// @param num_matches Number of entries in matches/cntxts, always greater 0. -static void cs_print_tags_priv(char **matches, char **cntxts, - size_t num_matches) FUNC_ATTR_NONNULL_ALL -{ - char *globalcntx = "GLOBAL"; - char *cstag_msg = _("Cscope tag: %s"); - - assert(num_matches > 0); - assert(strcnt(matches[0], '\t') >= 2); - - char *ptag = matches[0]; - char *ptag_end = strchr(ptag, '\t'); - assert(ptag_end >= ptag); - // NUL terminate tag string in matches[0]. - *ptag_end = NUL; - - // The "%s" in cstag_msg won't appear in the result string, so we don't need - // extra memory for terminating NUL. - size_t newsize = strlen(cstag_msg) + (size_t)(ptag_end - ptag); - char *buf = xmalloc(newsize); - size_t bufsize = newsize; // Track available bufsize - (void)snprintf(buf, bufsize, cstag_msg, ptag); - msg_puts_attr(buf, HL_ATTR(HLF_T)); - msg_clr_eos(); - - // restore matches[0] - *ptag_end = '\t'; - - // Column headers for match number, line number and filename. - msg_puts_attr(_("\n # line"), HL_ATTR(HLF_T)); - msg_advance(msg_col + 2); - msg_puts_attr(_("filename / context / line\n"), HL_ATTR(HLF_T)); - - for (size_t i = 0; i < num_matches; i++) { - assert(strcnt(matches[i], '\t') >= 2); - - // Parse filename, line number and optional part. - char *fname = strchr(matches[i], '\t') + 1; - char *fname_end = strchr(fname, '\t'); - // Replace second '\t' in matches[i] with NUL to terminate fname. - *fname_end = NUL; - - char *lno = fname_end + 1; - char *extra = xstrchrnul(lno, '\t'); - // Ignore ;" at the end of lno. - char *lno_end = extra - 2; - *lno_end = NUL; - // Do we have an optional part? - extra = *extra ? extra + 1 : NULL; - - const char *csfmt_str = "%4zu %6s "; - // hopefully num_matches will be less than 10^16 - newsize = strlen(csfmt_str) + 16 + (size_t)(lno_end - lno); - if (bufsize < newsize) { - buf = xrealloc(buf, newsize); - bufsize = newsize; - } - (void)snprintf(buf, bufsize, csfmt_str, i + 1, lno); - msg_puts_attr(buf, HL_ATTR(HLF_CM)); - msg_outtrans_long_attr(cs_pathcomponents(fname), HL_ATTR(HLF_CM)); - - // compute the required space for the context - char *context = cntxts[i] ? cntxts[i] : globalcntx; - - const char *cntxformat = " <<%s>>"; - // '%s' won't appear in result string, so: - // newsize = len(cntxformat) - 2 + len(context) + 1 (for NUL). - newsize = strlen(context) + strlen(cntxformat) - 1; - - if (bufsize < newsize) { - buf = xrealloc(buf, newsize); - bufsize = newsize; - } - int buf_len = snprintf(buf, bufsize, cntxformat, context); - assert(buf_len >= 0); - - // Print the context only if it fits on the same line. - if (msg_col + buf_len >= Columns) { - msg_putchar('\n'); - } - msg_advance(12); - msg_outtrans_long_attr(buf, 0); - msg_putchar('\n'); - if (extra != NULL) { - msg_advance(13); - msg_outtrans_long_attr(extra, 0); - } - - // restore matches[i] - *fname_end = '\t'; - *lno_end = ';'; - - if (msg_col) { - msg_putchar('\n'); - } - - os_breakcheck(); - if (got_int) { - got_int = false; // don't print any more matches - break; - } - } - - xfree(buf); -} - -/// Read a cscope prompt (basically, skip over the ">> "). -static int cs_read_prompt(size_t i) -{ - int ch; - char *buf = NULL; // buffer for possible error message from cscope - size_t bufpos = 0; - char *cs_emsg = _("E609: Cscope error: %s"); - size_t cs_emsg_len = strlen(cs_emsg); - static char *eprompt = "Press the RETURN key to continue:"; - size_t epromptlen = strlen(eprompt); - - // compute maximum allowed len for Cscope error message - assert(IOSIZE >= cs_emsg_len); - size_t maxlen = IOSIZE - cs_emsg_len; - - while (1) { - while (1) { - do { - errno = 0; - ch = fgetc(csinfo[i].fr_fp); - } while (ch == EOF && errno == EINTR && ferror(csinfo[i].fr_fp)); - if (ch == EOF || ch == CSCOPE_PROMPT[0]) { - break; - } - // if there is room and char is printable - if (bufpos < maxlen - 1 && vim_isprintc(ch)) { - // lazy buffer allocation - if (buf == NULL) { - buf = xmalloc(maxlen); - } - // append character to the message - buf[bufpos++] = (char)ch; - buf[bufpos] = NUL; - if (bufpos >= epromptlen - && strcmp(&buf[bufpos - epromptlen], eprompt) == 0) { - // remove eprompt from buf - buf[bufpos - epromptlen] = NUL; - - // print message to user - (void)semsg(cs_emsg, buf); - - // send RETURN to cscope - (void)putc('\n', csinfo[i].to_fp); - (void)fflush(csinfo[i].to_fp); - - // clear buf - bufpos = 0; - buf[bufpos] = NUL; - } - } - } - - for (size_t n = 0; n < strlen(CSCOPE_PROMPT); n++) { - if (n > 0) { - do { - errno = 0; - ch = fgetc(csinfo[i].fr_fp); - } while (ch == EOF && errno == EINTR && ferror(csinfo[i].fr_fp)); - } - if (ch == EOF) { - PERROR("cs_read_prompt EOF"); - if (buf != NULL && buf[0] != NUL) { - (void)semsg(cs_emsg, buf); - } else if (p_csverbose) { - cs_reading_emsg(i); // don't have additional information - } - cs_release_csp(i, true); - xfree(buf); - return CSCOPE_FAILURE; - } - - if (ch != CSCOPE_PROMPT[n]) { - ch = EOF; - break; - } - } - - if (ch == EOF) { - continue; // didn't find the prompt - } - break; // did find the prompt - } - - xfree(buf); - return CSCOPE_SUCCESS; -} - -#if defined(UNIX) && defined(SIGALRM) -// Used to catch and ignore SIGALRM below. -static void sig_handler(int s) -{ - // do nothing -} - -#endif - -/// Does the actual free'ing for the cs ptr with an optional flag of whether -/// or not to free the filename. Called by cs_kill and cs_reset. -static void cs_release_csp(size_t i, bool freefnpp) -{ - // Trying to exit normally (not sure whether it is fit to Unix cscope) - if (csinfo[i].to_fp != NULL) { - (void)fputs("q\n", csinfo[i].to_fp); - (void)fflush(csinfo[i].to_fp); - } -#if defined(UNIX) - { - int waitpid_errno; - int pstat; - pid_t pid; - -# if defined(HAVE_SIGACTION) - struct sigaction sa, old; - - // Use sigaction() to limit the waiting time to two seconds. - sigemptyset(&sa.sa_mask); - sa.sa_handler = sig_handler; -# ifdef SA_NODEFER - sa.sa_flags = SA_NODEFER; -# else - sa.sa_flags = 0; -# endif - sigaction(SIGALRM, &sa, &old); - alarm(2); // 2 sec timeout - - // Block until cscope exits or until timer expires - pid = waitpid(csinfo[i].pid, &pstat, 0); - waitpid_errno = errno; - - // cancel pending alarm if still there and restore signal - alarm(0); - sigaction(SIGALRM, &old, NULL); -# else - int waited; - - // Can't use sigaction(), loop for two seconds. First yield the CPU - // to give cscope a chance to exit quickly. - sleep(0); - for (waited = 0; waited < 40; waited++) { - pid = waitpid(csinfo[i].pid, &pstat, WNOHANG); - waitpid_errno = errno; - if (pid != 0) { - break; // break unless the process is still running - } - os_delay(50L, false); // sleep 50 ms - } -# endif - // If the cscope process is still running: kill it. - // Safety check: If the PID would be zero here, the entire X session - // would be killed. -1 and 1 are dangerous as well. - if (pid < 0 && csinfo[i].pid > 1) { -# ifdef ECHILD - bool alive = true; - - if (waitpid_errno == ECHILD) { - // When using 'vim -g', vim is forked and cscope process is - // no longer a child process but a sibling. So waitpid() - // fails with errno being ECHILD (No child processes). - // Don't send SIGKILL to cscope immediately but wait - // (polling) for it to exit normally as result of sending - // the "q" command, hence giving it a chance to clean up - // its temporary files. - int waited; - - sleep(0); - for (waited = 0; waited < 40; waited++) { - // Check whether cscope process is still alive - if (kill(csinfo[i].pid, 0) != 0) { - alive = false; // cscope process no longer exists - break; - } - os_delay(50L, false); // sleep 50 ms - } - } - if (alive) -# endif - { - kill(csinfo[i].pid, SIGKILL); - (void)waitpid(csinfo[i].pid, &pstat, 0); - } - } - } -#else // !UNIX - if (csinfo[i].hProc != NULL) { - // Give cscope a chance to exit normally - if (WaitForSingleObject(csinfo[i].hProc, 1000) == WAIT_TIMEOUT) { - TerminateProcess(csinfo[i].hProc, 0); - } - CloseHandle(csinfo[i].hProc); - } -#endif - - if (csinfo[i].fr_fp != NULL) { - (void)fclose(csinfo[i].fr_fp); - } - if (csinfo[i].to_fp != NULL) { - (void)fclose(csinfo[i].to_fp); - } - - if (freefnpp) { - xfree(csinfo[i].fname); - xfree(csinfo[i].ppath); - xfree(csinfo[i].flags); - } - - clear_csinfo(i); -} - -/// Calls cs_kill on all cscope connections then reinits. -static int cs_reset(exarg_T *eap) -{ - char **dblist = NULL, **pplist = NULL, **fllist = NULL; - char buf[25]; // for snprintf " (#%zu)" - - if (csinfo_size == 0) { - return CSCOPE_SUCCESS; - } - - // malloc our db and ppath list - dblist = xmalloc(csinfo_size * sizeof(char *)); - pplist = xmalloc(csinfo_size * sizeof(char *)); - fllist = xmalloc(csinfo_size * sizeof(char *)); - - for (size_t i = 0; i < csinfo_size; i++) { - dblist[i] = csinfo[i].fname; - pplist[i] = csinfo[i].ppath; - fllist[i] = csinfo[i].flags; - if (csinfo[i].fname != NULL) { - cs_release_csp(i, false); - } - } - - // rebuild the cscope connection list - for (size_t i = 0; i < csinfo_size; i++) { - if (dblist[i] != NULL) { - cs_add_common(dblist[i], pplist[i], fllist[i]); - if (p_csverbose) { - // don't use smsg_attr() because we want to display the - // connection number in the same line as - // "Added cscope database..." - snprintf(buf, ARRAY_SIZE(buf), " (#%zu)", i); - msg_puts_attr(buf, HL_ATTR(HLF_R)); - } - } - xfree(dblist[i]); - xfree(pplist[i]); - xfree(fllist[i]); - } - xfree(dblist); - xfree(pplist); - xfree(fllist); - - if (p_csverbose) { - msg_attr(_("All cscope databases reset"), HL_ATTR(HLF_R) | MSG_HIST); - } - return CSCOPE_SUCCESS; -} - -/// Construct the full pathname to a file found in the cscope database. -/// (Prepends ppath, if there is one and if it's not already prepended, -/// otherwise just uses the name found.) -/// -/// We need to prepend the prefix because on some cscope's (e.g., the one that -/// ships with Solaris 2.6), the output never has the prefix prepended. -/// Contrast this with my development system (Digital Unix), which does. -static char *cs_resolve_file(size_t i, char *name) -{ - char *fullname; - char_u *csdir = NULL; - - // Ppath is freed when we destroy the cscope connection. - // Fullname is freed after cs_make_vim_style_matches, after it's been - // copied into the tag buffer used by Vim. - size_t len = strlen(name) + 2; - if (csinfo[i].ppath != NULL) { - len += strlen(csinfo[i].ppath); - } else if (p_csre && csinfo[i].fname != NULL) { - // If 'cscoperelative' is set and ppath is not set, use cscope.out - // path in path resolution. - csdir = xmalloc(MAXPATHL); - STRLCPY(csdir, csinfo[i].fname, - path_tail(csinfo[i].fname) - - csinfo[i].fname + 1); - len += STRLEN(csdir); - } - - // Note/example: this won't work if the cscope output already starts - // "../.." and the prefix path is also "../..". if something like this - // happens, you are screwed up and need to fix how you're using cscope. - if (csinfo[i].ppath != NULL - && (strncmp(name, csinfo[i].ppath, strlen(csinfo[i].ppath)) != 0) - && (name[0] != '/')) { - fullname = xmalloc(len); - (void)sprintf(fullname, "%s/%s", csinfo[i].ppath, name); - } else if (csdir != NULL && csinfo[i].fname != NULL && *csdir != NUL) { - // Check for csdir to be non empty to avoid empty path concatenated to - // cscope output. - fullname = concat_fnames((char *)csdir, name, true); - } else { - fullname = xstrdup(name); - } - - xfree(csdir); - return fullname; -} - -/// Show all cscope connections. -static int cs_show(exarg_T *eap) -{ - if (cs_cnt_connections() == 0) { - msg_puts(_("no cscope connections\n")); - } else { - msg_puts_attr(_(" # pid database name prepend path\n"), - HL_ATTR(HLF_T)); - for (size_t i = 0; i < csinfo_size; i++) { - if (csinfo[i].fname == NULL) { - continue; - } - - if (csinfo[i].ppath != NULL) { - (void)smsg("%2zu %-5" PRId64 " %-34s %-32s", i, - (int64_t)csinfo[i].pid, csinfo[i].fname, csinfo[i].ppath); - } else { - (void)smsg("%2zu %-5" PRId64 " %-34s ", i, - (int64_t)csinfo[i].pid, csinfo[i].fname); - } - } - } - - wait_return(false); - return CSCOPE_SUCCESS; -} - -/// Only called when VIM exits to quit any cscope sessions. -void cs_end(void) -{ - for (size_t i = 0; i < csinfo_size; i++) { - cs_release_csp(i, true); - } - xfree(csinfo); - csinfo_size = 0; -} - -// the end diff --git a/src/nvim/if_cscope.h b/src/nvim/if_cscope.h deleted file mode 100644 index 8dbc78943f..0000000000 --- a/src/nvim/if_cscope.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef NVIM_IF_CSCOPE_H -#define NVIM_IF_CSCOPE_H - -#include "nvim/ex_cmds_defs.h" // for exarg_T -#include "nvim/types.h" // for char_u and expand_T - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "if_cscope.h.generated.h" -#endif -#endif // NVIM_IF_CSCOPE_H diff --git a/src/nvim/if_cscope_defs.h b/src/nvim/if_cscope_defs.h deleted file mode 100644 index 6ded89fa0b..0000000000 --- a/src/nvim/if_cscope_defs.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef NVIM_IF_CSCOPE_DEFS_H -#define NVIM_IF_CSCOPE_DEFS_H - -// CSCOPE support for Vim added by Andy Kahn -// Ported to Win32 by Sergey Khorev -// -// The basic idea/structure of cscope for Vim was borrowed from Nvi. -// There might be a few lines of code that look similar to what Nvi -// has. If this is a problem and requires inclusion of the annoying -// BSD license, then sue me; I'm not worth much anyway. - -#if defined(UNIX) -# include // pid_t -#endif - -#include "nvim/ex_cmds_defs.h" -#include "nvim/os/fs_defs.h" -#include "nvim/os/os_defs.h" - -#define CSCOPE_SUCCESS 0 -#define CSCOPE_FAILURE -1 - -#define CSCOPE_DBFILE "cscope.out" -#define CSCOPE_PROMPT ">> " - -// See ":help cscope-find" for the possible queries. - -typedef struct { - char *name; - int (*func)(exarg_T *eap); - char *help; - char *usage; - int cansplit; // if supports splitting window -} cscmd_T; - -typedef struct csi { - char *fname; // cscope db name - char *ppath; // path to prepend (the -P option) - char *flags; // additional cscope flags/options (e.g, -p2) -#if defined(UNIX) - pid_t pid; // PID of the connected cscope process -#else - DWORD pid; // PID of the connected cscope process - HANDLE hProc; // cscope process handle - DWORD nVolume; // Volume serial number, instead of st_dev - DWORD nIndexHigh; // st_ino has no meaning on Windows - DWORD nIndexLow; -#endif - FileID file_id; - - FILE *fr_fp; // from cscope: FILE. - FILE *to_fp; // to cscope: FILE. -} csinfo_T; - -typedef enum { Add, Find, Help, Kill, Reset, Show, } csid_e; - -typedef enum { - Store, - Get, - Free, - Print, -} mcmd_e; - -#endif // NVIM_IF_CSCOPE_DEFS_H diff --git a/src/nvim/main.c b/src/nvim/main.c index f6ec08c8e0..82c32f8e25 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -31,7 +31,6 @@ #include "nvim/highlight.h" #include "nvim/highlight_group.h" #include "nvim/iconv.h" -#include "nvim/if_cscope.h" #include "nvim/insexpand.h" #include "nvim/locale.h" #include "nvim/log.h" @@ -772,7 +771,6 @@ void getout(int exitval) ui_call_set_title(cstr_as_string((char *)p_titleold)); } - cs_end(); if (garbage_collect_at_exit) { garbage_collect(false); } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index e058be9135..01a062d000 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -4310,11 +4310,7 @@ static void nv_ident(cmdarg_T *cap) case ']': tag_cmd = true; - if (p_cst) { - STRCPY(buf, "cstag "); - } else { - STRCPY(buf, "ts "); - } + STRCPY(buf, "ts "); break; default: diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 5470b66d6d..e5bd065aef 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -471,15 +471,6 @@ EXTERN long p_ph; // 'pumheight' EXTERN long p_pw; // 'pumwidth' EXTERN char *p_com; ///< 'comments' EXTERN char *p_cpo; // 'cpoptions' -EXTERN char *p_csprg; // 'cscopeprg' -EXTERN int p_csre; // 'cscoperelative' -EXTERN char *p_csqf; // 'cscopequickfix' -#define CSQF_CMDS "sgdctefia" -#define CSQF_FLAGS "+-0" -EXTERN int p_cst; // 'cscopetag' -EXTERN long p_csto; // 'cscopetagorder' -EXTERN long p_cspc; // 'cscopepathcomp' -EXTERN int p_csverbose; // 'cscopeverbose' EXTERN char *p_debug; // 'debug' EXTERN char *p_def; // 'define' EXTERN char *p_inc; diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 149d0bace4..f4d6c808fa 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -495,58 +495,6 @@ return { varname='p_cpo', defaults={if_true=macros('CPO_VIM')} }, - { - full_name='cscopepathcomp', abbreviation='cspc', - short_desc=N_("how many components of the path to show"), - type='number', scope={'global'}, - varname='p_cspc', - defaults={if_true=0} - }, - { - full_name='cscopeprg', abbreviation='csprg', - short_desc=N_("command to execute cscope"), - type='string', scope={'global'}, - secure=true, - expand=true, - varname='p_csprg', - defaults={if_true="cscope"} - }, - { - full_name='cscopequickfix', abbreviation='csqf', - short_desc=N_("use quickfix window for cscope results"), - type='string', list='onecomma', scope={'global'}, - deny_duplicates=true, - varname='p_csqf', - defaults={if_true=""} - }, - { - full_name='cscoperelative', abbreviation='csre', - short_desc=N_("Use cscope.out path basename as prefix"), - type='bool', scope={'global'}, - varname='p_csre', - defaults={if_true=0} - }, - { - full_name='cscopetag', abbreviation='cst', - short_desc=N_("use cscope for tag commands"), - type='bool', scope={'global'}, - varname='p_cst', - defaults={if_true=0} - }, - { - full_name='cscopetagorder', abbreviation='csto', - short_desc=N_("determines \":cstag\" search order"), - type='number', scope={'global'}, - varname='p_csto', - defaults={if_true=0} - }, - { - full_name='cscopeverbose', abbreviation='csverb', - short_desc=N_("give messages when adding a cscope database"), - type='bool', scope={'global'}, - varname='p_csverbose', - defaults={if_true=1} - }, { full_name='cursorbind', abbreviation='crb', short_desc=N_("move cursor in window as it moves in other windows"), diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index 24ee0a1f69..be3e137b69 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -1375,23 +1375,6 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf coladvance(curwin->w_virtcol); } } - } else if (varp == &p_csqf) { - if (p_csqf != NULL) { - p = p_csqf; - while (*p != NUL) { - if (vim_strchr(CSQF_CMDS, *p) == NULL - || p[1] == NUL - || vim_strchr(CSQF_FLAGS, p[1]) == NULL - || (p[2] != NUL && p[2] != ',')) { - errmsg = e_invarg; - break; - } else if (p[2] == NUL) { - break; - } else { - p += 3; - } - } - } } else if (gvarp == &p_cino) { // 'cinoptions' // TODO(vim): recognize errors parse_cino(curbuf); diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 4658aed235..19519c5402 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -24,7 +24,6 @@ #include "nvim/fold.h" #include "nvim/garray.h" #include "nvim/help.h" -#include "nvim/if_cscope.h" #include "nvim/input.h" #include "nvim/insexpand.h" #include "nvim/mark.h" @@ -132,12 +131,9 @@ static int tfu_in_use = false; // disallow recursive call of tagfunc /// type == DT_LAST: jump to last match of same tag /// type == DT_SELECT: ":tselect [tag]", select tag from a list of all matches /// type == DT_JUMP: ":tjump [tag]", jump to tag or select tag from a list -/// type == DT_CSCOPE: use cscope to find the tag /// type == DT_LTAG: use location list for displaying tag matches /// type == DT_FREE: free cached matches /// -/// for cscope, returns true if we jumped to tag or aborted, false otherwise -/// /// @param tag tag (pattern) to jump to /// @param forceit :ta with ! /// @param verbose print "tag not found" message @@ -181,7 +177,6 @@ bool do_tag(char *tag, int type, int count, int forceit, int verbose) if (type == DT_FREE) { // remove the list of matches FreeWild(num_matches, matches); - cs_free_tags(); num_matches = 0; return false; } @@ -219,8 +214,7 @@ bool do_tag(char *tag, int type, int count, int forceit, int verbose) // new pattern, add to the tag stack if (*tag != NUL && (type == DT_TAG || type == DT_SELECT || type == DT_JUMP - || type == DT_LTAG - || type == DT_CSCOPE)) { + || type == DT_LTAG)) { if (g_do_tagpreview != 0) { if (ptag_entry.tagname != NULL && strcmp(ptag_entry.tagname, tag) == 0) { @@ -311,7 +305,6 @@ bool do_tag(char *tag, int type, int count, int forceit, int verbose) // remove the old list of matches FreeWild(num_matches, matches); - cs_free_tags(); num_matches = 0; tag_freematch(); goto end_do_tag; @@ -360,7 +353,6 @@ bool do_tag(char *tag, int type, int count, int forceit, int verbose) cur_match = count - 1; break; case DT_SELECT: case DT_JUMP: - case DT_CSCOPE: case DT_LAST: cur_match = MAXCOL - 1; break; case DT_NEXT: @@ -454,9 +446,6 @@ bool do_tag(char *tag, int type, int count, int forceit, int verbose) flags = TAG_NOIC; } - if (type == DT_CSCOPE) { - flags = TAG_CSCOPE; - } if (verbose) { flags |= TAG_VERBOSE; } @@ -509,10 +498,7 @@ bool do_tag(char *tag, int type, int count, int forceit, int verbose) } else { bool ask_for_selection = false; - if (type == DT_CSCOPE && num_matches > 1) { - cs_print_tags(); - ask_for_selection = true; - } else if (type == DT_TAG && *tag != NUL) { + if (type == DT_TAG && *tag != NUL) { // If a count is supplied to the ":tag " command, then // jump to count'th matching tag. cur_match = count > 0 ? count - 1 : 0; @@ -536,7 +522,6 @@ bool do_tag(char *tag, int type, int count, int forceit, int verbose) tagstack[tagstackidx].fmark = saved_fmark; tagstackidx = prevtagstackidx; } - cs_free_tags(); jumped_to_tag = true; break; } @@ -586,7 +571,6 @@ bool do_tag(char *tag, int type, int count, int forceit, int verbose) ic = (matches[cur_match][0] & MT_IC_OFF); if (type != DT_TAG && type != DT_SELECT && type != DT_JUMP - && type != DT_CSCOPE && (num_matches > 1 || ic) && !skip_msg) { // Give an indication of the number of matching tags @@ -619,7 +603,7 @@ bool do_tag(char *tag, int type, int count, int forceit, int verbose) set_vim_var_string(VV_SWAPCOMMAND, (char *)IObuff, -1); // Jump to the desired match. - i = jumpto_tag((char_u *)matches[cur_match], forceit, type != DT_CSCOPE); + i = jumpto_tag((char_u *)matches[cur_match], forceit, true); set_vim_var_string(VV_SWAPCOMMAND, NULL, -1); @@ -1324,7 +1308,6 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl /// TAG_REGEXP use "pat" as a regexp /// TAG_NOIC don't always ignore case /// TAG_KEEP_LANG keep language -/// TAG_CSCOPE use cscope results for tags /// TAG_NO_TAGFUNC do not call the 'tagfunc' function /// /// @param pat pattern to search for @@ -1409,7 +1392,6 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc int name_only = (flags & TAG_NAMES); int noic = (flags & TAG_NOIC); int get_it_again = false; - int use_cscope = (flags & TAG_CSCOPE); int verbose = (flags & TAG_VERBOSE); int use_tfu = ((flags & TAG_NO_TAGFUNC) == 0); int save_p_ic = p_ic; @@ -1448,15 +1430,9 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc hash_init(&ht_match[mtt]); } - STRCPY(tag_fname, "from cscope"); // for error messages - // Initialize a few variables if (help_only) { // want tags from help file curbuf->b_help = true; // will be restored later - } else if (use_cscope) { - // Make sure we don't mix help and cscope, confuses Coverity. - help_only = false; - curbuf->b_help = false; } orgpat.len = (int)strlen(pat); @@ -1522,77 +1498,74 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc // Try tag file names from tags option one by one. for (first_file = true; - use_cscope || get_tagfname(&tn, first_file, (char *)tag_fname) == OK; + get_tagfname(&tn, first_file, (char *)tag_fname) == OK; first_file = false) { // A file that doesn't exist is silently ignored. Only when not a // single file is found, an error message is given (further on). - if (use_cscope) { - fp = NULL; // avoid GCC warning - } else { - if (curbuf->b_help) { - // Keep en if the file extension is .txt - if (is_txt) { - STRCPY(help_lang, "en"); + if (curbuf->b_help) { + // Keep en if the file extension is .txt + if (is_txt) { + STRCPY(help_lang, "en"); + } else { + // Prefer help tags according to 'helplang'. Put the + // two-letter language name in help_lang[]. + i = (int)STRLEN(tag_fname); + if (i > 3 && tag_fname[i - 3] == '-') { + STRCPY(help_lang, tag_fname + i - 2); } else { - // Prefer help tags according to 'helplang'. Put the - // two-letter language name in help_lang[]. - i = (int)STRLEN(tag_fname); - if (i > 3 && tag_fname[i - 3] == '-') { - STRCPY(help_lang, tag_fname + i - 2); - } else { - STRCPY(help_lang, "en"); - } + STRCPY(help_lang, "en"); } + } - // When searching for a specific language skip tags files - // for other languages. - if (help_lang_find != NULL - && STRICMP(help_lang, help_lang_find) != 0) { - continue; - } + // When searching for a specific language skip tags files + // for other languages. + if (help_lang_find != NULL + && STRICMP(help_lang, help_lang_find) != 0) { + continue; + } - // For CTRL-] in a help file prefer a match with the same - // language. - if ((flags & TAG_KEEP_LANG) - && help_lang_find == NULL - && curbuf->b_fname != NULL - && (i = (int)strlen(curbuf->b_fname)) > 4 - && curbuf->b_fname[i - 1] == 'x' - && curbuf->b_fname[i - 4] == '.' - && STRNICMP(curbuf->b_fname + i - 3, help_lang, 2) == 0) { - help_pri = 0; - } else { - help_pri = 1; - for (s = p_hlg; *s != NUL; s++) { - if (STRNICMP(s, help_lang, 2) == 0) { - break; - } - help_pri++; - if ((s = (char_u *)vim_strchr((char *)s, ',')) == NULL) { - break; - } + // For CTRL-] in a help file prefer a match with the same + // language. + if ((flags & TAG_KEEP_LANG) + && help_lang_find == NULL + && curbuf->b_fname != NULL + && (i = (int)strlen(curbuf->b_fname)) > 4 + && curbuf->b_fname[i - 1] == 'x' + && curbuf->b_fname[i - 4] == '.' + && STRNICMP(curbuf->b_fname + i - 3, help_lang, 2) == 0) { + help_pri = 0; + } else { + help_pri = 1; + for (s = p_hlg; *s != NUL; s++) { + if (STRNICMP(s, help_lang, 2) == 0) { + break; + } + help_pri++; + if ((s = (char_u *)vim_strchr((char *)s, ',')) == NULL) { + break; } - if (s == NULL || *s == NUL) { - // Language not in 'helplang': use last, prefer English, - // unless found already. + } + if (s == NULL || *s == NUL) { + // Language not in 'helplang': use last, prefer English, + // unless found already. + help_pri++; + if (STRICMP(help_lang, "en") != 0) { help_pri++; - if (STRICMP(help_lang, "en") != 0) { - help_pri++; - } } } } + } - if ((fp = os_fopen((char *)tag_fname, "r")) == NULL) { - continue; - } + if ((fp = os_fopen((char *)tag_fname, "r")) == NULL) { + continue; + } - if (p_verbose >= 5) { - verbose_enter(); - smsg(_("Searching tags file %s"), tag_fname); - verbose_leave(); - } + if (p_verbose >= 5) { + verbose_enter(); + smsg(_("Searching tags file %s"), tag_fname); + verbose_leave(); } + did_open = true; // remember that we found at least one file state = TS_START; // we're at the start of the file @@ -1676,9 +1649,7 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc // skip empty and blank lines do { - eof = use_cscope - ? cs_fgets(lbuf, lbuf_size) - : vim_fgets(lbuf, lbuf_size, fp); + eof = vim_fgets(lbuf, lbuf_size, fp); } while (!eof && vim_isblankline((char *)lbuf)); if (eof) { @@ -1745,8 +1716,7 @@ line_read_in: // the tag file isn't sorted, the second loop will find it. // When "!_TAG_FILE_SORTED" found: start binary search if // flag set. - // For cscope, it's always linear. - if (linear || use_cscope) { + if (linear) { state = TS_LINEAR; } else if (tag_file_sorted == NUL) { state = TS_BINARY; @@ -1797,7 +1767,7 @@ parse_line: // last-but-one byte (see vim_fgets()). // Has been reported for Mozilla JS with extremely long names. // In that case we need to increase lbuf_size. - if (lbuf[lbuf_size - 2] != NUL && !use_cscope) { + if (lbuf[lbuf_size - 2] != NUL) { lbuf_size *= 2; xfree(lbuf); lbuf = xmalloc((size_t)lbuf_size); @@ -1985,37 +1955,32 @@ parse_line: if (match) { size_t len = 0; - if (use_cscope) { - // Don't change the ordering, always use the same table. - mtt = MT_GL_OTH; - } else { - // Decide in which array to store this match. - is_current = test_for_current((char *)tagp.fname, (char *)tagp.fname_end, - (char *)tag_fname, - buf_ffname); - is_static = test_for_static(&tagp); - - // Decide in which of the sixteen tables to store this match. - if (is_static) { - if (is_current) { - mtt = MT_ST_CUR; - } else { - mtt = MT_ST_OTH; - } + // Decide in which array to store this match. + is_current = test_for_current((char *)tagp.fname, (char *)tagp.fname_end, + (char *)tag_fname, + buf_ffname); + is_static = test_for_static(&tagp); + + // Decide in which of the sixteen tables to store this match. + if (is_static) { + if (is_current) { + mtt = MT_ST_CUR; } else { - if (is_current) { - mtt = MT_GL_CUR; - } else { - mtt = MT_GL_OTH; - } - } - if (orgpat.regmatch.rm_ic && !match_no_ic) { - mtt += MT_IC_OFF; + mtt = MT_ST_OTH; } - if (match_re) { - mtt += MT_RE_OFF; + } else { + if (is_current) { + mtt = MT_GL_CUR; + } else { + mtt = MT_GL_OTH; } } + if (orgpat.regmatch.rm_ic && !match_no_ic) { + mtt += MT_IC_OFF; + } + if (match_re) { + mtt += MT_RE_OFF; + } // Add the found match in ht_match[mtt] and ga_match[mtt]. // Store the info we need later, which depends on the kind of @@ -2098,16 +2063,11 @@ parse_line: hashitem_T *hi; // Don't add identical matches. - // Add all cscope tags, because they are all listed. // "mfp" is used as a hash key, there is a NUL byte to end // the part that matters for comparing, more bytes may // follow after it. E.g. help tags store the priority // after the NUL. - if (use_cscope) { - hash++; - } else { - hash = hash_hash((char_u *)mfp); - } + hash = hash_hash((char_u *)mfp); hi = hash_lookup(&ht_match[mtt], (const char *)mfp, strlen(mfp), hash); if (HASHITEM_EMPTY(hi)) { @@ -2121,23 +2081,16 @@ parse_line: } } } - if (use_cscope && eof) { - break; - } } // forever if (line_error) { semsg(_("E431: Format error in tags file \"%s\""), tag_fname); - if (!use_cscope) { - semsg(_("Before byte %" PRId64), (int64_t)vim_ftell(fp)); - } + semsg(_("Before byte %" PRId64), (int64_t)vim_ftell(fp)); stop_searching = true; line_error = false; } - if (!use_cscope) { - fclose(fp); - } + fclose(fp); if (vimconv.vc_type != CONV_NONE) { convert_setup(&vimconv, NULL, NULL); } @@ -2154,23 +2107,18 @@ parse_line: stop_searching = true; } - if (stop_searching || use_cscope) { + if (stop_searching) { break; } } // end of for-each-file loop - if (!use_cscope) { - tagname_free(&tn); - } + tagname_free(&tn); // stop searching when already did a linear search, or when TAG_NOIC // used, and 'ignorecase' not set or already did case-ignore search if (stop_searching || linear || (!p_ic && noic) || orgpat.regmatch.rm_ic) { break; } - if (use_cscope) { - break; - } orgpat.regmatch.rm_ic = true; // try another time while ignoring case } @@ -2559,7 +2507,7 @@ static char_u *tag_full_fname(tagptrs_T *tagp) /// /// @param lbuf_arg line from the tags file for this tag /// @param forceit :ta with ! -/// @param keep_help keep help flag (false for cscope) +/// @param keep_help keep help flag /// /// @return OK for success, NOTAGFILE when file not found, FAIL otherwise. static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help) diff --git a/src/nvim/tag.h b/src/nvim/tag.h index 7f2ef8d6d7..e08145f727 100644 --- a/src/nvim/tag.h +++ b/src/nvim/tag.h @@ -14,7 +14,6 @@ #define DT_SELECT 7 // jump to selection from list #define DT_HELP 8 // like DT_TAG, but no wildcards #define DT_JUMP 9 // jump to new tag or selection from list -#define DT_CSCOPE 10 // cscope find command (like tjump) #define DT_LTAG 11 // tag using location list #define DT_FREE 99 // free cached matches @@ -23,7 +22,6 @@ #define TAG_NAMES 2 // only return name of tag #define TAG_REGEXP 4 // use tag pattern as regexp #define TAG_NOIC 8 // don't always ignore case -#define TAG_CSCOPE 16 // cscope tag #define TAG_VERBOSE 32 // message verbosity #define TAG_INS_COMP 64 // Currently doing insert completion #define TAG_KEEP_LANG 128 // keep current language diff --git a/src/nvim/testdir/test_cscope.vim b/src/nvim/testdir/test_cscope.vim deleted file mode 100644 index 76ea35fa10..0000000000 --- a/src/nvim/testdir/test_cscope.vim +++ /dev/null @@ -1,344 +0,0 @@ -" Test for cscope commands. - -source check.vim -CheckFeature cscope -CheckFeature quickfix - -if !executable('cscope') - throw 'Skipped: cscope program missing' -endif - -func CscopeSetupOrClean(setup) - if a:setup - noa sp samples/memfile_test.c - saveas! Xmemfile_test.c - call system('cscope -bk -fXcscope.out Xmemfile_test.c') - call system('cscope -bk -fXcscope2.out Xmemfile_test.c') - cscope add Xcscope.out - set cscopequickfix=s-,g-,d-,c-,t-,e-,f-,i-,a- - else - cscope kill -1 - for file in ['Xcscope.out', 'Xcscope2.out', 'Xmemfile_test.c'] - call delete(file) - endfo - endif -endfunc - -func Test_cscopeWithCscopeConnections() - call CscopeSetupOrClean(1) - " Test: E568: duplicate cscope database not added - try - set nocscopeverbose - cscope add Xcscope.out - set cscopeverbose - catch - call assert_report('exception thrown') - endtry - call assert_fails('cscope add', 'E560') - call assert_fails('cscope add Xcscope.out', 'E568') - call assert_fails('cscope add doesnotexist.out', 'E563') - if has('unix') - call assert_fails('cscope add /dev/null', 'E564:') - endif - - " Test: Find this C-Symbol - for cmd in ['cs find s main', 'cs find 0 main'] - let a = execute(cmd) - " Test where it moves the cursor - call assert_equal('main(void)', getline('.')) - " Test the output of the :cs command - call assert_match('\n(1 of 1): <
> main(void )', a) - endfor - - " Test: Find this definition - for cmd in ['cs find g test_mf_hash', - \ 'cs find 1 test_mf_hash', - \ 'cs find 1 test_mf_hash'] " leading space ignored. - exe cmd - call assert_equal(['', '/*', ' * Test mf_hash_*() functions.', ' */', ' static void', 'test_mf_hash(void)', '{'], getline(line('.')-5, line('.')+1)) - endfor - - " Test: Find functions called by this function - for cmd in ['cs find d test_mf_hash', 'cs find 2 test_mf_hash'] - let a = execute(cmd) - call assert_match('\n(1 of 42): <> mf_hash_init(&ht);', a) - call assert_equal(' mf_hash_init(&ht);', getline('.')) - endfor - - " Test: Find functions calling this function - for cmd in ['cs find c test_mf_hash', 'cs find 3 test_mf_hash'] - let a = execute(cmd) - call assert_match('\n(1 of 1): <
> test_mf_hash();', a) - call assert_equal(' test_mf_hash();', getline('.')) - endfor - - " Test: Find this text string - for cmd in ['cs find t Bram', 'cs find 4 Bram'] - let a = execute(cmd) - call assert_match('(1 of 1): <<>> \* VIM - Vi IMproved^Iby Bram Moolenaar', a) - call assert_equal(' * VIM - Vi IMproved by Bram Moolenaar', getline('.')) - endfor - - " Test: Find this egrep pattern - " test all matches returned by cscope - for cmd in ['cs find e ^\#includ.', 'cs find 6 ^\#includ.'] - let a = execute(cmd) - call assert_match('\n(1 of 3): <<>> #include ', a) - call assert_equal('#include ', getline('.')) - cnext - call assert_equal('#include "main.c"', getline('.')) - cnext - call assert_equal('#include "memfile.c"', getline('.')) - call assert_fails('cnext', 'E553:') - endfor - - " Test: Find the same egrep pattern using lcscope this time. - let a = execute('lcs find e ^\#includ.') - call assert_match('\n(1 of 3): <<>> #include ', a) - call assert_equal('#include ', getline('.')) - lnext - call assert_equal('#include "main.c"', getline('.')) - lnext - call assert_equal('#include "memfile.c"', getline('.')) - call assert_fails('lnext', 'E553:') - - " Test: Find this file - for cmd in ['cs find f Xmemfile_test.c', 'cs find 7 Xmemfile_test.c'] - enew - let a = execute(cmd) - call assert_true(a =~ '"Xmemfile_test.c" \d\+L, \d\+B') - call assert_equal('Xmemfile_test.c', @%) - endfor - - " Test: Find files #including this file - for cmd in ['cs find i assert.h', 'cs find 8 assert.h'] - enew - let a = execute(cmd) - let alines = split(a, '\n', 1) - call assert_equal('', alines[0]) - call assert_true(alines[1] =~ '"Xmemfile_test.c" \d\+L, \d\+B') - call assert_equal('(1 of 1): <> #include ', alines[2]) - call assert_equal('#include ', getline('.')) - endfor - - " Test: Invalid find command - call assert_fails('cs find', 'E560:') - call assert_fails('cs find x', 'E560:') - - if has('float') - " Test: Find places where this symbol is assigned a value - " this needs a cscope >= 15.8 - " unfortunately, Travis has cscope version 15.7 - let cscope_version = systemlist('cscope --version')[0] - let cs_version = str2float(matchstr(cscope_version, '\d\+\(\.\d\+\)\?')) - if cs_version >= 15.8 - for cmd in ['cs find a item', 'cs find 9 item'] - let a = execute(cmd) - call assert_equal(['', '(1 of 4): <> item = (mf_hashitem_T *)lalloc_clear(sizeof(*item), FALSE);'], split(a, '\n', 1)) - call assert_equal(' item = (mf_hashitem_T *)lalloc_clear(sizeof(*item), FALSE);', getline('.')) - cnext - call assert_equal(' item = mf_hash_find(&ht, key);', getline('.')) - cnext - call assert_equal(' item = mf_hash_find(&ht, key);', getline('.')) - cnext - call assert_equal(' item = mf_hash_find(&ht, key);', getline('.')) - endfor - endif - endif - - " Test: leading whitespace is not removed for cscope find text - let a = execute('cscope find t test_mf_hash') - call assert_equal(['', '(1 of 1): <<>> test_mf_hash();'], split(a, '\n', 1)) - call assert_equal(' test_mf_hash();', getline('.')) - - " Test: test with scscope - let a = execute('scs find t Bram') - call assert_match('(1 of 1): <<>> \* VIM - Vi IMproved^Iby Bram Moolenaar', a) - call assert_equal(' * VIM - Vi IMproved by Bram Moolenaar', getline('.')) - - " Test: cscope help - for cmd in ['cs', 'cs help', 'cs xxx'] - let a = execute(cmd) - call assert_match('^cscope commands:\n', a) - call assert_match('\nadd :', a) - call assert_match('\nfind :', a) - call assert_match('\nhelp : Show this message', a) - call assert_match('\nkill : Kill a connection', a) - call assert_match('\nreset: Reinit all connections', a) - call assert_match('\nshow : Show connections', a) - endfor - let a = execute('scscope help') - call assert_match('This cscope command does not support splitting the window\.', a) - - " Test: reset connections - let a = execute('cscope reset') - call assert_match('\nAdded cscope database.*Xcscope.out (#0)', a) - call assert_match('\nAll cscope databases reset', a) - - " Test: cscope show - let a = execute('cscope show') - call assert_match('\n 0 \d\+.*Xcscope.out\s*', a) - - " Test: cstag and 'csto' option - set csto=0 - let a = execute('cstag TEST_COUNT') - call assert_match('(1 of 1): <> #define TEST_COUNT 50000', a) - call assert_equal('#define TEST_COUNT 50000', getline('.')) - call assert_fails('cstag DOES_NOT_EXIST', 'E257:') - set csto=1 - let a = execute('cstag index_to_key') - call assert_match('(1 of 1): <> #define index_to_key(i) ((i) ^ 15167)', a) - call assert_equal('#define index_to_key(i) ((i) ^ 15167)', getline('.')) - call assert_fails('cstag DOES_NOT_EXIST', 'E257:') - call assert_fails('cstag', 'E562:') - let save_tags = &tags - set tags= - call assert_fails('cstag DOES_NOT_EXIST', 'E257:') - let a = execute('cstag index_to_key') - call assert_match('(1 of 1): <> #define index_to_key(i) ((i) ^ 15167)', a) - let &tags = save_tags - - " Test: 'cst' option - set nocst - call assert_fails('tag TEST_COUNT', 'E426:') - set cst - let a = execute('tag TEST_COUNT') - call assert_match('(1 of 1): <> #define TEST_COUNT 50000', a) - call assert_equal('#define TEST_COUNT 50000', getline('.')) - let a = execute('tags') - call assert_match('1 1 TEST_COUNT\s\+\d\+\s\+#define index_to_key', a) - - " Test: 'cscoperelative' - call mkdir('Xcscoperelative') - cd Xcscoperelative - let a = execute('cs find g test_mf_hash') - call assert_notequal('test_mf_hash(void)', getline('.')) - set cscoperelative - let a = execute('cs find g test_mf_hash') - call assert_equal('test_mf_hash(void)', getline('.')) - set nocscoperelative - cd .. - call delete('Xcscoperelative', 'd') - - " Test: E259: no match found - call assert_fails('cscope find g DOES_NOT_EXIST', 'E259:') - - " Test: this should trigger call to cs_print_tags() - " Unclear how to check result though, we just exercise the code. - set cst cscopequickfix=s0 - call feedkeys(":cs find s main\", 't') - - " Test: cscope kill - call assert_fails('cscope kill', 'E560:') - call assert_fails('cscope kill 2', 'E261:') - call assert_fails('cscope kill xxx', 'E261:') - - let a = execute('cscope kill 0') - call assert_match('cscope connection 0 closed', a) - - cscope add Xcscope.out - let a = execute('cscope kill Xcscope.out') - call assert_match('cscope connection Xcscope.out closed', a) - - cscope add Xcscope.out . - let a = execute('cscope kill -1') - call assert_match('cscope connection .*Xcscope.out closed', a) - let a = execute('cscope kill -1') - call assert_equal('', a) - - " Test: 'csprg' option - call assert_equal('cscope', &csprg) - set csprg=doesnotexist - call assert_fails('cscope add Xcscope2.out', 'E609:') - set csprg=cscope - - " Test: multiple cscope connections - cscope add Xcscope.out - cscope add Xcscope2.out . -C - let a = execute('cscope show') - call assert_match('\n 0 \d\+.*Xcscope.out\s*', a) - call assert_match('\n 1 \d\+.*Xcscope2.out\s*\.', a) - - " Test: test Ex command line completion - call feedkeys(":cs \\\"\", 'tx') - call assert_equal('"cs add find help kill reset show', @:) - - call feedkeys(":scs \\\"\", 'tx') - call assert_equal('"scs find', @:) - - call feedkeys(":cs find \\\"\", 'tx') - call assert_equal('"cs find a c d e f g i s t', @:) - - call feedkeys(":cs kill \\\"\", 'tx') - call assert_equal('"cs kill -1 0 1', @:) - - call feedkeys(":cs add Xcscope\\\"\", 'tx') - call assert_equal('"cs add Xcscope.out Xcscope2.out', @:) - - " Test: cscope_connection() - call assert_equal(cscope_connection(), 1) - call assert_equal(cscope_connection(0, 'out'), 1) - call assert_equal(cscope_connection(0, 'xxx'), 1) - - call assert_equal(cscope_connection(1, 'out'), 1) - call assert_equal(cscope_connection(1, 'xxx'), 0) - - call assert_equal(cscope_connection(2, 'out'), 0) - call assert_equal(cscope_connection(2, getcwd() .. '/Xcscope.out', 1), 1) - - call assert_equal(cscope_connection(3, 'xxx', '..'), 0) - call assert_equal(cscope_connection(3, 'out', 'xxx'), 0) - call assert_equal(cscope_connection(3, 'out', '.'), 1) - - call assert_equal(cscope_connection(4, 'out', '.'), 0) - - call assert_equal(cscope_connection(5, 'out'), 0) - call assert_equal(cscope_connection(-1, 'out'), 0) - - call CscopeSetupOrClean(0) -endfunc - -" Test ":cs add {dir}" (add the {dir}/cscope.out database) -func Test_cscope_add_dir() - call mkdir('Xcscopedir', 'p') - - " Cscope doesn't handle symlinks, so this needs to be resolved in case a - " shadow directory is being used. - let memfile = resolve('./samples/memfile_test.c') - call system('cscope -bk -fXcscopedir/cscope.out ' . memfile) - - cs add Xcscopedir - let a = execute('cscope show') - let lines = split(a, "\n", 1) - call assert_equal(3, len(lines)) - call assert_equal(' # pid database name prepend path', lines[0]) - call assert_equal('', lines[1]) - call assert_match('^ 0 \d\+.*Xcscopedir/cscope.out\s\+$', lines[2]) - - cs kill -1 - call delete('Xcscopedir/cscope.out') - call assert_fails('cs add Xcscopedir', 'E563:') - - call delete('Xcscopedir', 'd') -endfunc - -func Test_cscopequickfix() - set cscopequickfix=s-,g-,d+,c-,t+,e-,f0,i-,a- - call assert_equal('s-,g-,d+,c-,t+,e-,f0,i-,a-', &cscopequickfix) - - call assert_fails('set cscopequickfix=x-', 'E474:') - call assert_fails('set cscopequickfix=s', 'E474:') - call assert_fails('set cscopequickfix=s7', 'E474:') - call assert_fails('set cscopequickfix=s-a', 'E474:') -endfunc - -func Test_withoutCscopeConnection() - call assert_equal(cscope_connection(), 0) - - call assert_fails('cscope find s main', 'E567:') - let a = execute('cscope show') - call assert_match('no cscope connections', a) -endfunc - - -" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c index c3cf0b6df8..0e045c773f 100644 --- a/src/nvim/usercmd.c +++ b/src/nvim/usercmd.c @@ -46,7 +46,6 @@ static const char *command_complete[] = [EXPAND_COLORS] = "color", [EXPAND_COMMANDS] = "command", [EXPAND_COMPILER] = "compiler", - [EXPAND_CSCOPE] = "cscope", [EXPAND_USER_DEFINED] = "custom", [EXPAND_USER_LIST] = "customlist", [EXPAND_USER_LUA] = "", diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 5b7ff7ba52..4bcda9fc4f 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -138,7 +138,6 @@ enum { EXPAND_USER_LIST, EXPAND_USER_LUA, EXPAND_SHELLCMD, - EXPAND_CSCOPE, EXPAND_SIGN, EXPAND_PROFILE, EXPAND_BEHAVE, -- cgit From 7f11dfdef880ee98e4f18f046f21e285157d10ac Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 10 Oct 2022 12:02:11 +0100 Subject: refactor(tag): remove return type from do_tag() --- src/nvim/tag.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 19519c5402..2ac3fed8b7 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -137,7 +137,7 @@ static int tfu_in_use = false; // disallow recursive call of tagfunc /// @param tag tag (pattern) to jump to /// @param forceit :ta with ! /// @param verbose print "tag not found" message -bool do_tag(char *tag, int type, int count, int forceit, int verbose) +void do_tag(char *tag, int type, int count, int forceit, int verbose) { taggy_T *tagstack = curwin->w_tagstack; int tagstackidx = curwin->w_tagstackidx; @@ -154,7 +154,6 @@ bool do_tag(char *tag, int type, int count, int forceit, int verbose) int error_cur_match = 0; int save_pos = false; fmark_T saved_fmark; - bool jumped_to_tag = false; int new_num_matches; char **new_matches; int use_tagstack; @@ -170,7 +169,7 @@ bool do_tag(char *tag, int type, int count, int forceit, int verbose) if (tfu_in_use) { emsg(_(recurmsg)); - return false; + return; } #ifdef EXITFREE @@ -178,7 +177,7 @@ bool do_tag(char *tag, int type, int count, int forceit, int verbose) // remove the list of matches FreeWild(num_matches, matches); num_matches = 0; - return false; + return; } #endif @@ -522,7 +521,6 @@ bool do_tag(char *tag, int type, int count, int forceit, int verbose) tagstack[tagstackidx].fmark = saved_fmark; tagstackidx = prevtagstackidx; } - jumped_to_tag = true; break; } cur_match = i - 1; @@ -633,7 +631,6 @@ bool do_tag(char *tag, int type, int count, int forceit, int verbose) if (use_tagstack && tagstackidx > curwin->w_tagstacklen) { tagstackidx = curwin->w_tagstackidx; } - jumped_to_tag = true; } } break; @@ -647,7 +644,7 @@ end_do_tag: postponed_split = 0; // don't split next time g_do_tagpreview = 0; // don't do tag preview next time - return jumped_to_tag; + return; } // List all the matching tags. -- cgit From 0578c67767a9cbaeaab167092a7bdb389f556ee4 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Oct 2022 07:29:58 +0800 Subject: vim-patch:9.0.0747: too many #ifdefs (#20641) Problem: Too many #ifdefs. Solution: Gradudate the +cmdline_info feature. (Martin Tournoij, closes vim/vim#11330) https://github.com/vim/vim/commit/ba43e76fcd5b2da57dbaa4d9a555793fe8ac344e --- src/nvim/normal.c | 3 +++ src/nvim/screen.c | 1 + src/nvim/window.c | 1 + 3 files changed, 5 insertions(+) (limited to 'src/nvim') diff --git a/src/nvim/normal.c b/src/nvim/normal.c index e058be9135..8aafe6671d 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1132,6 +1132,7 @@ static int normal_execute(VimState *state, int key) if (s->need_flushbuf) { ui_flush(); } + if (s->ca.cmdchar != K_IGNORE && s->ca.cmdchar != K_EVENT) { did_cursorhold = false; } @@ -3513,6 +3514,7 @@ static bool nv_z_get_count(cmdarg_T *cap, int *nchar_arg) no_mapping--; allow_keys--; (void)add_to_showcmd(nchar); + if (nchar == K_DEL || nchar == K_KDEL) { n /= 10; } else if (ascii_isdigit(nchar)) { @@ -3553,6 +3555,7 @@ static int nv_zg_zw(cmdarg_T *cap, int nchar) no_mapping--; allow_keys--; (void)add_to_showcmd(nchar); + if (vim_strchr("gGwW", nchar) == NULL) { clearopbeep(cap->oap); return OK; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index bc440441e1..90011eb2c7 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -860,6 +860,7 @@ int showmode(void) if (redrawing() && last->w_status_height == 0 && global_stl_height() == 0) { win_redr_ruler(last, true); } + redraw_cmdline = false; redraw_mode = false; clear_cmdline = false; diff --git a/src/nvim/window.c b/src/nvim/window.c index ed64062a55..002d95ac2a 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -554,6 +554,7 @@ wingotofile: no_mapping--; allow_keys--; (void)add_to_showcmd(xchar); + switch (xchar) { case '}': xchar = Ctrl_RSB; -- cgit From 546b294e74ac10d0a5e1216dd530fc96bdc66f29 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 14 Oct 2022 11:49:57 +0100 Subject: fix(decoration): redraw correctly when re-using ids 00cfc1d (from #20249) reduced the amount of unnecessary redraws. This surfaced an issue where if and extmark with a specific ID is repositioned to a different row, the decorations from the old row were not redrawn and removed. This change fixes that by redrawing the old row. --- src/nvim/extmark.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim') diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index 176ad0d5c8..df87cc8ab6 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -112,6 +112,7 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col marktree_revise(buf->b_marktree, itr, decor_level, old_mark); goto revised; } + decor_remove(buf, old_mark.pos.row, old_mark.pos.row, old_mark.decor_full); marktree_del_itr(buf->b_marktree, itr, false); } } else { -- cgit From e6f7e038b8bbca487e78ebfc6fe21d6852330623 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Oct 2022 23:08:00 +0800 Subject: fix(completion): set pum_size even if ext_popupmenu is used (#20648) This allows CompleteChanged event to get the correct `v:event.size`. It should be harmless and more consistent to also set `pum_array`. --- src/nvim/popupmenu.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c index 74376c8b8a..3a3f966d10 100644 --- a/src/nvim/popupmenu.c +++ b/src/nvim/popupmenu.c @@ -264,12 +264,15 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i pum_row = above_row; pum_height = pum_win_row - above_row; } + + pum_array = array; + // Set "pum_size" before returning so that pum_set_event_info() gets the correct size. + pum_size = size; + if (pum_external) { return; } - pum_array = array; - pum_size = size; pum_compute_size(); int max_width = pum_base_width; -- cgit From ffc6d14af52ef5ea810ee90cdaff2605301c9f30 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 13 Oct 2022 15:08:38 +0200 Subject: fix(windows): set console icon later in startup Problem: Windows console icon is set early in startup, but there are some cases where `os_exit` is called and we don't restore the original icon. Solution: - Move `os_icon_init()` later in the startup sequence, and only if `use_builtin_ui==true`. - Rename functions: use `os_` prefix for platform-specific code. --- src/nvim/main.c | 45 ++++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 25 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/main.c b/src/nvim/main.c index 82c32f8e25..46db81412c 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -211,17 +211,7 @@ HWND hWnd = NULL; static HICON hOrigIconSmall = NULL; static HICON hOrigIcon = NULL; -/// Save Windows console icon to be reset later -static void SaveWin32ConsoleIcon(void) -{ - if ((hWnd = GetConsoleWindow()) == NULL) { - return; - } - hOrigIconSmall = (HICON)SendMessage(hWnd, WM_GETICON, (WPARAM)ICON_SMALL, (LPARAM)0); - hOrigIcon = (HICON)SendMessage(hWnd, WM_GETICON, (WPARAM)ICON_BIG, (LPARAM)0); -} - -static void SetConsoleIcon(HWND hWindow, HICON hIconSmall, HICON hIcon) +static void os_icon_set(HWND hWindow, HICON hIconSmall, HICON hIcon) { if (hWindow == NULL) { return; @@ -234,15 +224,18 @@ static void SetConsoleIcon(HWND hWindow, HICON hIconSmall, HICON hIcon) } } -/// Reset Windows console icon to original -static void ResetWin32ConsoleIcon(void) +/// Sets Nvim logo as Windows console icon. +/// +/// Saves the original icon so it can be restored at exit. +static void os_icon_init(void) { - SetConsoleIcon(hWnd, hOrigIconSmall, hOrigIcon); -} + if ((hWnd = GetConsoleWindow()) == NULL) { + return; + } + // Save Windows console icon to be restored later. + hOrigIconSmall = (HICON)SendMessage(hWnd, WM_GETICON, (WPARAM)ICON_SMALL, (LPARAM)0); + hOrigIcon = (HICON)SendMessage(hWnd, WM_GETICON, (WPARAM)ICON_BIG, (LPARAM)0); -/// Set Neovim logo as Windows console icon -static void SetWin32ConsoleIcon(void) -{ const char *vimruntime = os_getenv("VIMRUNTIME"); if (vimruntime != NULL) { snprintf(NameBuff, MAXPATHL, "%s" _PATHSEPSTR "neovim.ico", vimruntime); @@ -251,7 +244,7 @@ static void SetWin32ConsoleIcon(void) } else { HICON hVimIcon = LoadImage(NULL, NameBuff, IMAGE_ICON, 64, 64, LR_LOADFROMFILE | LR_LOADMAP3DCOLORS); - SetConsoleIcon(hWnd, hVimIcon, hVimIcon); + os_icon_set(hWnd, hVimIcon, hVimIcon); } } } @@ -306,11 +299,6 @@ int main(int argc, char **argv) early_init(¶ms); -#ifdef MSWIN - SaveWin32ConsoleIcon(); - SetWin32ConsoleIcon(); -#endif - set_argv_var(argv, argc); // set v:argv // Check if we have an interactive window. @@ -599,6 +587,12 @@ int main(int argc, char **argv) TIME_MSG("UIEnter autocommands"); } +#ifdef MSWIN + if (use_builtin_ui) { + os_icon_init(); + } +#endif + // Adjust default register name for "unnamed" in 'clipboard'. Can only be // done after the clipboard is available and all initial commands that may // modify the 'clipboard' setting have run; i.e. just before entering the @@ -776,7 +770,8 @@ void getout(int exitval) } #ifdef MSWIN - ResetWin32ConsoleIcon(); + // Restore Windows console icon before exiting. + os_icon_set(hWnd, hOrigIconSmall, hOrigIcon); #endif os_exit(exitval); -- cgit From 1ca3a3749f4addb175f96f41fa0fb210e1fa297d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 13 Oct 2022 15:25:23 +0200 Subject: refactor(windows): move os_icon_xx functions --- src/nvim/main.c | 46 +------------------------------------------ src/nvim/os/os_win_console.c | 47 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 45 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/main.c b/src/nvim/main.c index 46db81412c..7e488794f4 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -206,50 +206,6 @@ void early_init(mparm_T *paramp) init_signs(); } -#ifdef MSWIN -HWND hWnd = NULL; -static HICON hOrigIconSmall = NULL; -static HICON hOrigIcon = NULL; - -static void os_icon_set(HWND hWindow, HICON hIconSmall, HICON hIcon) -{ - if (hWindow == NULL) { - return; - } - if (hIconSmall != NULL) { - SendMessage(hWnd, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIconSmall); - } - if (hIcon != NULL) { - SendMessage(hWnd, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hIcon); - } -} - -/// Sets Nvim logo as Windows console icon. -/// -/// Saves the original icon so it can be restored at exit. -static void os_icon_init(void) -{ - if ((hWnd = GetConsoleWindow()) == NULL) { - return; - } - // Save Windows console icon to be restored later. - hOrigIconSmall = (HICON)SendMessage(hWnd, WM_GETICON, (WPARAM)ICON_SMALL, (LPARAM)0); - hOrigIcon = (HICON)SendMessage(hWnd, WM_GETICON, (WPARAM)ICON_BIG, (LPARAM)0); - - const char *vimruntime = os_getenv("VIMRUNTIME"); - if (vimruntime != NULL) { - snprintf(NameBuff, MAXPATHL, "%s" _PATHSEPSTR "neovim.ico", vimruntime); - if (!os_path_exists(NameBuff)) { - WLOG("neovim.ico not found: %s", NameBuff); - } else { - HICON hVimIcon = LoadImage(NULL, NameBuff, IMAGE_ICON, 64, 64, - LR_LOADFROMFILE | LR_LOADMAP3DCOLORS); - os_icon_set(hWnd, hVimIcon, hVimIcon); - } - } -} -#endif - #ifdef MAKE_LIB int nvim_main(int argc, char **argv); // silence -Wmissing-prototypes int nvim_main(int argc, char **argv) @@ -771,7 +727,7 @@ void getout(int exitval) #ifdef MSWIN // Restore Windows console icon before exiting. - os_icon_set(hWnd, hOrigIconSmall, hOrigIcon); + os_icon_set(NULL, NULL); #endif os_exit(exitval); diff --git a/src/nvim/os/os_win_console.c b/src/nvim/os/os_win_console.c index 20b7f869f1..ec0f03a1dc 100644 --- a/src/nvim/os/os_win_console.c +++ b/src/nvim/os/os_win_console.c @@ -2,6 +2,7 @@ // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com #include "nvim/os/input.h" +#include "nvim/os/os.h" #include "nvim/os/os_win_console.h" #include "nvim/vim.h" @@ -9,6 +10,10 @@ # include "os/os_win_console.c.generated.h" #endif +static HWND hWnd = NULL; +static HICON hOrigIconSmall = NULL; +static HICON hOrigIcon = NULL; + int os_get_conin_fd(void) { const HANDLE conin_handle = CreateFile("CONIN$", @@ -45,3 +50,45 @@ void os_replace_stdout_and_stderr_to_conout(void) const int conerr_fd = _open_osfhandle((intptr_t)conout_handle, 0); assert(conerr_fd == STDERR_FILENO); } + +/// Sets Windows console icon, or pass NULL to restore original icon. +void os_icon_set(HICON hIconSmall, HICON hIcon) +{ + if (hWnd == NULL) { + return; + } + hIconSmall = hIconSmall ? hIconSmall : hOrigIconSmall; + hIcon = hIcon ? hIcon : hOrigIcon; + + if (hIconSmall != NULL) { + SendMessage(hWnd, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIconSmall); + } + if (hIcon != NULL) { + SendMessage(hWnd, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hIcon); + } +} + +/// Sets Nvim logo as Windows console icon. +/// +/// Saves the original icon so it can be restored at exit. +void os_icon_init(void) +{ + if ((hWnd = GetConsoleWindow()) == NULL) { + return; + } + // Save Windows console icon to be restored later. + hOrigIconSmall = (HICON)SendMessage(hWnd, WM_GETICON, (WPARAM)ICON_SMALL, (LPARAM)0); + hOrigIcon = (HICON)SendMessage(hWnd, WM_GETICON, (WPARAM)ICON_BIG, (LPARAM)0); + + const char *vimruntime = os_getenv("VIMRUNTIME"); + if (vimruntime != NULL) { + snprintf(NameBuff, MAXPATHL, "%s" _PATHSEPSTR "neovim.ico", vimruntime); + if (!os_path_exists(NameBuff)) { + WLOG("neovim.ico not found: %s", NameBuff); + } else { + HICON hVimIcon = LoadImage(NULL, NameBuff, IMAGE_ICON, 64, 64, + LR_LOADFROMFILE | LR_LOADMAP3DCOLORS); + os_icon_set(hVimIcon, hVimIcon); + } + } +} -- cgit From 1c478391ca7754bf5ecb4b76c29acfa9b4978393 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Oct 2022 07:24:03 +0800 Subject: vim-patch:9.0.0750: crash when popup closed in callback (#20659) Problem: Crash when popup closed in callback. (Maxim Kim) Solution: In syntax_end_parsing() check that syn_block is valid. https://github.com/vim/vim/commit/0abd6cf62d65180dc2c40d67cd95f13b0691f7ea --- src/nvim/drawscreen.c | 4 ++-- src/nvim/syntax.c | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index 15a7294496..4ece72a6d5 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -1753,7 +1753,7 @@ win_update_start: // Let the syntax stuff know we skipped a few lines. if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum && syntax_present(wp)) { - syntax_end_parsing(syntax_last_parsed + 1); + syntax_end_parsing(wp, syntax_last_parsed + 1); } // Display one line @@ -1827,7 +1827,7 @@ win_update_start: // Let the syntax stuff know we stop parsing here. if (syntax_last_parsed != 0 && syntax_present(wp)) { - syntax_end_parsing(syntax_last_parsed + 1); + syntax_end_parsing(wp, syntax_last_parsed + 1); } // If we didn't hit the end of the file, and we didn't finish the last diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 575d475b87..ea78397d8c 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -1327,10 +1327,13 @@ static bool syn_stack_equal(synstate_T *sp) // displayed line // displayed line // lnum -> line below window -void syntax_end_parsing(linenr_T lnum) +void syntax_end_parsing(win_T *wp, linenr_T lnum) { synstate_T *sp; + if (syn_block != wp->w_s) { + return; // not the right window + } sp = syn_stack_find_entry(lnum); if (sp != NULL && sp->sst_lnum < lnum) { sp = sp->sst_next; -- cgit From e26b48bde6e116eb288893454d2876cdad2db1f9 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 15 Oct 2022 10:12:25 +0200 Subject: vim-patch:9.0.0752: Rprofile files are not recognized (#20658) Problem: Rprofile files are not recognized. Solution: Recognize Rprofile files as "r". (closes vim/vim#11369) https://github.com/vim/vim/commit/7e120ffccbf81ae8acac28f11fbd5eab79a1630d --- src/nvim/testdir/test_filetype.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index b637f1a16f..7669b3d82a 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -454,7 +454,7 @@ let s:filename_checks = { \ 'ql': ['file.ql', 'file.qll'], \ 'quake': ['anybaseq2/file.cfg', 'anyid1/file.cfg', 'quake3/file.cfg', 'baseq2/file.cfg', 'id1/file.cfg', 'quake1/file.cfg', 'some-baseq2/file.cfg', 'some-id1/file.cfg', 'some-quake1/file.cfg'], \ 'quarto': ['file.qmd'], - \ 'r': ['file.r'], + \ 'r': ['file.r', '.Rprofile', 'Rprofile', 'Rprofile.site'], \ 'radiance': ['file.rad', 'file.mat'], \ 'raku': ['file.pm6', 'file.p6', 'file.t6', 'file.pod6', 'file.raku', 'file.rakumod', 'file.rakudoc', 'file.rakutest'], \ 'raml': ['file.raml'], -- cgit From 65cbe0cc35c07a929152b86e78717efa4ba155da Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 5 Jul 2022 17:33:23 +0800 Subject: vim-patch:8.1.0342: crash when a callback deletes a window that is being used Problem: Crash when a callback deletes a window that is being used. Solution: Do not unload a buffer that is being displayed while redrawing the screen. Also avoid invoking callbacks while redrawing. (closes vim/vim#2107) https://github.com/vim/vim/commit/94f01956a583223dafe24135489d0ab1100ab0ad Omit parse_queued_messages(): N/A. Cherry-pick a break statement from patch 8.1.0425. --- src/nvim/buffer.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 85f79deb2f..e3f923a2c0 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -93,7 +93,6 @@ #endif static char *e_auabort = N_("E855: Autocommands caused command to abort"); -static char *e_buflocked = N_("E937: Attempt to delete a buffer that is in use"); // Number of times free_buffer() was called. static int buf_free_count = 0; @@ -401,6 +400,27 @@ bool buf_valid(buf_T *buf) return false; } +/// Return true when buffer "buf" can be unloaded. +/// Give an error message and return false when the buffer is locked or the +/// screen is being redrawn and the buffer is in a window. +static bool can_unload_buffer(buf_T *buf) +{ + bool can_unload = !buf->b_locked; + + if (can_unload && updating_screen) { + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->w_buffer == buf) { + can_unload = false; + break; + } + } + } + if (!can_unload) { + emsg(_("E937: Attempt to delete a buffer that is in use")); + } + return can_unload; +} + /// Close the link to a buffer. /// /// @param win If not NULL, set b_last_cursor. @@ -458,8 +478,7 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool i // Disallow deleting the buffer when it is locked (already being closed or // halfway a command that relies on it). Unloading is allowed. - if (buf->b_locked > 0 && (del_buf || wipe_buf)) { - emsg(_(e_buflocked)); + if ((del_buf || wipe_buf) && !can_unload_buffer(buf)) { return false; } @@ -1206,8 +1225,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit) if (unload) { int forward; bufref_T bufref; - if (buf->b_locked) { - emsg(_(e_buflocked)); + if (!can_unload_buffer(buf)) { return FAIL; } set_bufref(&bufref, buf); -- cgit From 6bc2d6b66b683faedded01128af8ad98b7130fef Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Oct 2022 16:10:56 +0800 Subject: vim-patch:9.0.0614: SpellFileMissing autocmd may delete buffer Problem: SpellFileMissing autocmd may delete buffer. Solution: Disallow deleting the current buffer to avoid using freed memory. https://github.com/vim/vim/commit/ef976323e770315b5fca544efb6b2faa25674d15 --- src/nvim/buffer.c | 6 +++++- src/nvim/spell.c | 6 ++++++ src/nvim/testdir/test_autocmd.vim | 10 ++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index e3f923a2c0..29b00bad2a 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -93,6 +93,8 @@ #endif static char *e_auabort = N_("E855: Autocommands caused command to abort"); +static char e_attempt_to_delete_buffer_that_is_in_use_str[] + = N_("E937: Attempt to delete a buffer that is in use: %s"); // Number of times free_buffer() was called. static int buf_free_count = 0; @@ -416,7 +418,9 @@ static bool can_unload_buffer(buf_T *buf) } } if (!can_unload) { - emsg(_("E937: Attempt to delete a buffer that is in use")); + char *fname = buf->b_fname != NULL ? buf->b_fname : buf->b_ffname; + semsg(_(e_attempt_to_delete_buffer_that_is_in_use_str), + fname != NULL ? fname : "[No Name]"); } return can_unload; } diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 7d2b58ff46..69c2b027bc 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -1511,6 +1511,10 @@ static void spell_load_lang(char_u *lang) sl.sl_slang = NULL; sl.sl_nobreak = false; + // Disallow deleting the current buffer. Autocommands can do weird things + // and cause "lang" to be freed. + curbuf->b_locked++; + // We may retry when no spell file is found for the language, an // autocommand may load it then. for (int round = 1; round <= 2; round++) { @@ -1553,6 +1557,8 @@ static void spell_load_lang(char_u *lang) STRCPY(fname_enc + strlen(fname_enc) - 3, "add.spl"); do_in_runtimepath((char *)fname_enc, DIP_ALL, spell_load_cb, &sl); } + + curbuf->b_locked--; } // Return the encoding used for spell checking: Use 'encoding', except that we diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 025bda4515..63ed3ff435 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -2752,6 +2752,16 @@ func Test_FileType_spell() setglobal spellfile= endfunc +" this was wiping out the current buffer and using freed memory +func Test_SpellFileMissing_bwipe() + next 0 + au SpellFileMissing 0 bwipe + call assert_fails('set spell spelllang=0', 'E937:') + + au! SpellFileMissing + bwipe +endfunc + " Test closing a window or editing another buffer from a FileChangedRO handler " in a readonly buffer func Test_FileChangedRO_winclose() -- cgit From a9452cf3d5e2904ff355daf660e8e56bcb2433eb Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Oct 2022 16:17:07 +0800 Subject: vim-patch:9.0.0616: spell test fails because error message changed Problem: Spell test fails because error message changed. Solution: Adjust expected error message. https://github.com/vim/vim/commit/371951d0c34d4f44b50ad8bc8d30a4ef7effade6 --- src/nvim/testdir/test_spell.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim index 8ab8204b10..e421d21de1 100644 --- a/src/nvim/testdir/test_spell.vim +++ b/src/nvim/testdir/test_spell.vim @@ -147,7 +147,7 @@ func Test_spell_file_missing() augroup TestSpellFileMissing autocmd! SpellFileMissing * bwipe augroup END - call assert_fails('set spell spelllang=ab_cd', 'E797:') + call assert_fails('set spell spelllang=ab_cd', 'E937:') " clean up augroup TestSpellFileMissing -- cgit From 04cdea5f4ac49fa62cc4091a5c26791b80b4cc4c Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Fri, 26 Aug 2022 23:11:25 +0200 Subject: refactor: replace char_u with char Work on https://github.com/neovim/neovim/issues/459 --- src/nvim/cmdexpand.c | 2 +- src/nvim/cmdhist.c | 2 +- src/nvim/drawline.c | 4 +- src/nvim/edit.c | 126 ++++++++++++++++---------------- src/nvim/eval.c | 2 +- src/nvim/eval/funcs.c | 41 +++++------ src/nvim/eval/userfunc.c | 35 +++++---- src/nvim/ex_docmd.c | 2 +- src/nvim/ex_getln.c | 44 ++++++------ src/nvim/ex_session.c | 4 +- src/nvim/fold.c | 14 ++-- src/nvim/getchar.c | 10 +-- src/nvim/hardcopy.c | 28 ++++---- src/nvim/help.c | 2 +- src/nvim/indent.c | 22 +++--- src/nvim/insexpand.c | 72 +++++++++---------- src/nvim/lua/executor.c | 4 +- src/nvim/lua/stdlib.c | 8 +-- src/nvim/lua/treesitter.c | 2 +- src/nvim/mark.c | 10 +-- src/nvim/normal.c | 28 ++++---- src/nvim/ops.c | 180 +++++++++++++++++++++++----------------------- src/nvim/option.c | 34 ++++----- src/nvim/option_defs.h | 4 +- src/nvim/quickfix.c | 6 +- src/nvim/screen.c | 8 +-- src/nvim/spell.c | 74 +++++++++---------- src/nvim/spellfile.c | 2 +- src/nvim/spellsuggest.c | 4 +- src/nvim/statusline.c | 30 ++++---- src/nvim/syntax.c | 12 ++-- src/nvim/tag.c | 89 ++++++++++++----------- src/nvim/textformat.c | 6 +- src/nvim/textobject.c | 6 +- src/nvim/undo.c | 10 +-- 35 files changed, 461 insertions(+), 466 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index e728bd6967..fb29a723bc 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -2192,7 +2192,7 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char ***f if (xp->xp_context == EXPAND_LUA) { ILOG("PAT %s", pat); - return nlua_expand_pat(xp, pat, num_file, file); + return nlua_expand_pat(xp, (char *)pat, num_file, file); } regmatch.regprog = vim_regcomp((char *)pat, p_magic ? RE_MAGIC : 0); diff --git a/src/nvim/cmdhist.c b/src/nvim/cmdhist.c index 5a59f09113..b080b817a7 100644 --- a/src/nvim/cmdhist.c +++ b/src/nvim/cmdhist.c @@ -203,7 +203,7 @@ static int in_history(int type, char *str, int move_to_front, int sep) // well. char *p = history[type][i].hisstr; if (strcmp(str, p) == 0 - && (type != HIST_SEARCH || sep == p[STRLEN(p) + 1])) { + && (type != HIST_SEARCH || sep == p[strlen(p) + 1])) { if (!move_to_front) { return true; } diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 10e1d95730..6735551dc7 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -1570,7 +1570,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, // Non-BMP character : display as ? or fullwidth ?. transchar_hex((char *)extra, mb_c); if (wp->w_p_rl) { // reverse - rl_mirror(extra); + rl_mirror((char *)extra); } p_extra = extra; @@ -2063,7 +2063,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, n_extra = byte2cells(c) - 1; } if ((dy_flags & DY_UHEX) && wp->w_p_rl) { - rl_mirror(p_extra); // reverse "<12>" + rl_mirror((char *)p_extra); // reverse "<12>" } c_extra = NUL; c_final = NUL; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 09f20baebf..0e14567286 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -81,7 +81,7 @@ typedef struct insert_state { int did_restart_edit; // remember if insert mode was restarted // after a ctrl+o bool nomove; - char_u *ptr; + char *ptr; } InsertState; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -143,14 +143,14 @@ static void insert_enter(InsertState *s) pos_T save_cursor = curwin->w_cursor; if (s->cmdchar == 'R') { - s->ptr = (char_u *)"r"; + s->ptr = "r"; } else if (s->cmdchar == 'V') { - s->ptr = (char_u *)"v"; + s->ptr = "v"; } else { - s->ptr = (char_u *)"i"; + s->ptr = "i"; } - set_vim_var_string(VV_INSERTMODE, (char *)s->ptr, 1); + set_vim_var_string(VV_INSERTMODE, s->ptr, 1); set_vim_var_string(VV_CHAR, NULL, -1); ins_apply_autocmds(EVENT_INSERTENTER); @@ -272,11 +272,11 @@ static void insert_enter(InsertState *s) update_curswant(); if (((ins_at_eol && curwin->w_cursor.lnum == o_lnum) || curwin->w_curswant > curwin->w_virtcol) - && *(s->ptr = (char_u *)get_cursor_line_ptr() + curwin->w_cursor.col) != NUL) { + && *(s->ptr = get_cursor_line_ptr() + curwin->w_cursor.col) != NUL) { if (s->ptr[1] == NUL) { curwin->w_cursor.col++; } else { - s->i = utfc_ptr2len((char *)s->ptr); + s->i = utfc_ptr2len(s->ptr); if (s->ptr[s->i] == NUL) { curwin->w_cursor.col += s->i; } @@ -317,11 +317,11 @@ static void insert_enter(InsertState *s) // Get the current length of the redo buffer, those characters have to be // skipped if we want to get to the inserted characters. - s->ptr = get_inserted(); + s->ptr = (char *)get_inserted(); if (s->ptr == NULL) { new_insert_skip = 0; } else { - new_insert_skip = (int)STRLEN(s->ptr); + new_insert_skip = (int)strlen(s->ptr); xfree(s->ptr); } @@ -1449,28 +1449,28 @@ char *buf_prompt_text(const buf_T *const buf) return buf->b_prompt_text; } -// Return the effective prompt for the current buffer. -char_u *prompt_text(void) +/// @return the effective prompt for the current buffer. +char *prompt_text(void) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE { - return (char_u *)buf_prompt_text(curbuf); + return buf_prompt_text(curbuf); } // Prepare for prompt mode: Make sure the last line has the prompt text. // Move the cursor to this line. static void init_prompt(int cmdchar_todo) { - char_u *prompt = prompt_text(); - char_u *text; + char *prompt = prompt_text(); + char *text; curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - text = (char_u *)get_cursor_line_ptr(); - if (STRNCMP(text, prompt, STRLEN(prompt)) != 0) { + text = get_cursor_line_ptr(); + if (STRNCMP(text, prompt, strlen(prompt)) != 0) { // prompt is missing, insert it or append a line with it if (*text == NUL) { - ml_replace(curbuf->b_ml.ml_line_count, (char *)prompt, true); + ml_replace(curbuf->b_ml.ml_line_count, prompt, true); } else { - ml_append(curbuf->b_ml.ml_line_count, (char *)prompt, 0, false); + ml_append(curbuf->b_ml.ml_line_count, prompt, 0, false); } curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; coladvance(MAXCOL); @@ -1478,9 +1478,9 @@ static void init_prompt(int cmdchar_todo) } // Insert always starts after the prompt, allow editing text after it. - if (Insstart_orig.lnum != curwin->w_cursor.lnum || Insstart_orig.col != (colnr_T)STRLEN(prompt)) { + if (Insstart_orig.lnum != curwin->w_cursor.lnum || Insstart_orig.col != (colnr_T)strlen(prompt)) { Insstart.lnum = curwin->w_cursor.lnum; - Insstart.col = (colnr_T)STRLEN(prompt); + Insstart.col = (colnr_T)strlen(prompt); Insstart_orig = Insstart; Insstart_textlen = Insstart.col; Insstart_blank_vcol = MAXCOL; @@ -1490,8 +1490,8 @@ static void init_prompt(int cmdchar_todo) if (cmdchar_todo == 'A') { coladvance(MAXCOL); } - if (curwin->w_cursor.col < (colnr_T)STRLEN(prompt)) { - curwin->w_cursor.col = (colnr_T)STRLEN(prompt); + if (curwin->w_cursor.col < (colnr_T)strlen(prompt)) { + curwin->w_cursor.col = (colnr_T)strlen(prompt); } // Make sure the cursor is in a valid position. check_cursor(); @@ -1502,7 +1502,7 @@ bool prompt_curpos_editable(void) FUNC_ATTR_PURE { return curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count - && curwin->w_cursor.col >= (int)STRLEN(prompt_text()); + && curwin->w_cursor.col >= (int)strlen(prompt_text()); } // Undo the previous edit_putchar(). @@ -1940,7 +1940,7 @@ int get_literal(bool no_simplify) /// @param ctrlv `c` was typed after CTRL-V static void insert_special(int c, int allow_modmask, int ctrlv) { - char_u *p; + char *p; int len; // Special function key, translate into "". Up to the last '>' is @@ -1952,16 +1952,16 @@ static void insert_special(int c, int allow_modmask, int ctrlv) allow_modmask = true; } if (IS_SPECIAL(c) || (mod_mask && allow_modmask)) { - p = get_special_key_name(c, mod_mask); - len = (int)STRLEN(p); - c = p[len - 1]; + p = (char *)get_special_key_name(c, mod_mask); + len = (int)strlen(p); + c = (uint8_t)p[len - 1]; if (len > 2) { if (stop_arrow() == FAIL) { return; } p[len - 1] = NUL; - ins_str((char *)p); - AppendToRedobuffLit((char *)p, -1); + ins_str(p); + AppendToRedobuffLit(p, -1); ctrlv = false; } } @@ -2286,7 +2286,7 @@ int stop_arrow(void) static void stop_insert(pos_T *end_insert_pos, int esc, int nomove) { int cc; - char_u *ptr; + char *ptr; stop_redo_ins(); replace_flush(); // abandon replace stack @@ -2294,11 +2294,11 @@ static void stop_insert(pos_T *end_insert_pos, int esc, int nomove) // Save the inserted text for later redo with ^@ and CTRL-A. // Don't do it when "restart_edit" was set and nothing was inserted, // otherwise CTRL-O w and then will clear "last_insert". - ptr = get_inserted(); + ptr = (char *)get_inserted(); if (did_restart_edit == 0 || (ptr != NULL - && (int)STRLEN(ptr) > new_insert_skip)) { + && (int)strlen(ptr) > new_insert_skip)) { xfree(last_insert); - last_insert = ptr; + last_insert = (char_u *)ptr; last_insert_skip = new_insert_skip; } else { xfree(ptr); @@ -2742,7 +2742,7 @@ char_u *get_last_insert_save(void) return NULL; } s = xstrdup((char *)last_insert + last_insert_skip); - len = (int)STRLEN(s); + len = (int)strlen(s); if (len > 0 && s[len - 1] == ESC) { // remove trailing ESC s[len - 1] = NUL; } @@ -2938,7 +2938,7 @@ static void replace_do_bs(int limit_col) int ins_len; int orig_vcols = 0; colnr_T start_vcol; - char_u *p; + char *p; int i; int vcol; const int l_State = State; @@ -2960,12 +2960,12 @@ static void replace_do_bs(int limit_col) if (l_State & VREPLACE_FLAG) { // Get the number of screen cells used by the inserted characters - p = (char_u *)get_cursor_pos_ptr(); - ins_len = (int)STRLEN(p) - orig_len; + p = get_cursor_pos_ptr(); + ins_len = (int)strlen(p) - orig_len; vcol = start_vcol; for (i = 0; i < ins_len; i++) { - vcol += win_chartabsize(curwin, (char *)p + i, vcol); - i += utfc_ptr2len((char *)p) - 1; + vcol += win_chartabsize(curwin, p + i, vcol); + i += utfc_ptr2len(p) - 1; } vcol -= start_vcol; @@ -3036,11 +3036,11 @@ void fix_indent(void) /// @param line_is_empty when true, accept keys with '0' before them. bool in_cinkeys(int keytyped, int when, bool line_is_empty) { - char_u *look; + uint8_t *look; int try_match; int try_match_word; - char_u *p; - char_u *line; + uint8_t *p; + uint8_t *line; bool icase; if (keytyped == NUL) { @@ -3049,9 +3049,9 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) } if (*curbuf->b_p_inde != NUL) { - look = (char_u *)curbuf->b_p_indk; // 'indentexpr' set: use 'indentkeys' + look = (uint8_t *)curbuf->b_p_indk; // 'indentexpr' set: use 'indentkeys' } else { - look = (char_u *)curbuf->b_p_cink; // 'indentexpr' empty: use 'cinkeys' + look = (uint8_t *)curbuf->b_p_cink; // 'indentexpr' empty: use 'cinkeys' } while (*look) { // Find out if we want to try a match with this key, depending on @@ -3104,8 +3104,8 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) // cursor. } else if (*look == 'e') { if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4) { - p = (char_u *)get_cursor_line_ptr(); - if ((char_u *)skipwhite((char *)p) == p + curwin->w_cursor.col - 4 + p = (uint8_t *)get_cursor_line_ptr(); + if ((uint8_t *)skipwhite((char *)p) == p + curwin->w_cursor.col - 4 && STRNCMP(p + curwin->w_cursor.col - 4, "else", 4) == 0) { return true; } @@ -3117,20 +3117,20 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) // class::method for C++). } else if (*look == ':') { if (try_match && keytyped == ':') { - p = (char_u *)get_cursor_line_ptr(); - if (cin_iscase(p, false) || cin_isscopedecl(p) || cin_islabel()) { + p = (uint8_t *)get_cursor_line_ptr(); + if (cin_iscase((char_u *)p, false) || cin_isscopedecl((char_u *)p) || cin_islabel()) { return true; } // Need to get the line again after cin_islabel(). - p = (char_u *)get_cursor_line_ptr(); + p = (uint8_t *)get_cursor_line_ptr(); if (curwin->w_cursor.col > 2 && p[curwin->w_cursor.col - 1] == ':' && p[curwin->w_cursor.col - 2] == ':') { p[curwin->w_cursor.col - 1] = ' '; - const bool i = cin_iscase(p, false) - || cin_isscopedecl(p) + const bool i = cin_iscase((char_u *)p, false) + || cin_isscopedecl((char_u *)p) || cin_islabel(); - p = (char_u *)get_cursor_line_ptr(); + p = (uint8_t *)get_cursor_line_ptr(); p[curwin->w_cursor.col - 1] = ':'; if (i) { return true; @@ -3150,7 +3150,7 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) return true; } - if (keytyped == get_special_key_code(look + 1)) { + if (keytyped == get_special_key_code((char_u *)look + 1)) { return true; } } @@ -3169,23 +3169,23 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) } else { icase = false; } - p = (char_u *)vim_strchr((char *)look, ','); + p = (uint8_t *)vim_strchr((char *)look, ','); if (p == NULL) { - p = look + STRLEN(look); + p = look + strlen((char *)look); } if ((try_match || try_match_word) && curwin->w_cursor.col >= (colnr_T)(p - look)) { bool match = false; if (keytyped == KEY_COMPLETE) { - char_u *n, *s; + uint8_t *n, *s; // Just completed a word, check if it starts with "look". // search back for the start of a word. - line = (char_u *)get_cursor_line_ptr(); + line = (uint8_t *)get_cursor_line_ptr(); for (s = line + curwin->w_cursor.col; s > line; s = n) { - n = mb_prevptr(line, s); - if (!vim_iswordp(n)) { + n = mb_prevptr((char_u *)line, (char_u *)s); + if (!vim_iswordp((char_u *)n)) { break; } } @@ -3201,7 +3201,7 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) if (keytyped == (int)p[-1] || (icase && keytyped < 256 && TOLOWER_LOC(keytyped) == TOLOWER_LOC((int)p[-1]))) { - line = (char_u *)get_cursor_pos_ptr(); + line = (uint8_t *)get_cursor_pos_ptr(); assert(p >= look && (uintmax_t)(p - look) <= SIZE_MAX); if ((curwin->w_cursor.col == (colnr_T)(p - look) || !vim_iswordc(line[-(p - look) - 1])) @@ -3237,7 +3237,7 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) } // Skip over ", ". - look = (char_u *)skip_to_option_part((char *)look); + look = (uint8_t *)skip_to_option_part((char *)look); } return false; } @@ -3895,10 +3895,10 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) // again when auto-formatting. if (has_format_option(FO_AUTO) && has_format_option(FO_WHITE_PAR)) { - char_u *ptr = (char_u *)ml_get_buf(curbuf, curwin->w_cursor.lnum, true); + char *ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, true); int len; - len = (int)STRLEN(ptr); + len = (int)strlen(ptr); if (len > 0 && ptr[len - 1] == ' ') { ptr[len - 1] = NUL; } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ebc60ea5c7..d9d276e8ea 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8626,7 +8626,7 @@ void invoke_prompt_callback(void) return; } char *text = ml_get(lnum); - char *prompt = (char *)prompt_text(); + char *prompt = prompt_text(); if (strlen(text) >= strlen(prompt)) { text += strlen(prompt); } diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 861f68993a..5073ab8f1b 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -577,7 +577,7 @@ buf_T *tv_get_buf(typval_T *tv, int curtab_only) return NULL; } - char_u *name = (char_u *)tv->vval.v_string; + char *name = tv->vval.v_string; if (name == NULL || *name == NUL) { return curbuf; @@ -592,7 +592,7 @@ buf_T *tv_get_buf(typval_T *tv, int curtab_only) char *save_cpo = p_cpo; p_cpo = empty_option; - buf_T *buf = buflist_findnr(buflist_findpat((char *)name, (char *)name + STRLEN(name), + buf_T *buf = buflist_findnr(buflist_findpat(name, name + strlen(name), true, false, curtab_only)); p_magic = save_magic; @@ -1056,12 +1056,12 @@ static void f_count(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } if (argvars[0].v_type == VAR_STRING) { - const char_u *expr = (char_u *)tv_get_string_chk(&argvars[1]); + const char *expr = tv_get_string_chk(&argvars[1]); const char_u *p = (char_u *)argvars[0].vval.v_string; if (!error && expr != NULL && *expr != NUL && p != NULL) { if (ic) { - const size_t len = STRLEN(expr); + const size_t len = strlen(expr); while (*p != NUL) { if (mb_strnicmp((char *)p, (char *)expr, len) == 0) { @@ -1075,7 +1075,7 @@ static void f_count(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) char_u *next; while ((next = (char_u *)strstr((char *)p, (char *)expr)) != NULL) { n++; - p = next + STRLEN(expr); + p = next + strlen(expr); } } } @@ -4855,9 +4855,9 @@ static void f_map(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) static void find_some_match(typval_T *const argvars, typval_T *const rettv, const SomeMatchType type) { - char_u *str = NULL; + char *str = NULL; long len = 0; - char_u *expr = NULL; + char *expr = NULL; regmatch_T regmatch; long start = 0; long nth = 1; @@ -4865,7 +4865,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, bool match = false; list_T *l = NULL; long idx = 0; - char_u *tofree = NULL; + char *tofree = NULL; // Make 'cpoptions' empty, the 'l' flag should not be used here. char *save_cpo = p_cpo; @@ -4902,8 +4902,8 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, } li = tv_list_first(l); } else { - expr = str = (char_u *)tv_get_string(&argvars[0]); - len = (long)STRLEN(str); + expr = str = (char *)tv_get_string(&argvars[0]); + len = (long)strlen(str); } char patbuf[NUMBUFLEN]; @@ -4962,14 +4962,13 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, break; } xfree(tofree); - tofree = expr = str = (char_u *)encode_tv2echo(TV_LIST_ITEM_TV(li), - NULL); + tofree = expr = str = encode_tv2echo(TV_LIST_ITEM_TV(li), NULL); if (str == NULL) { break; } } - match = vim_regexec_nl(®match, str, startcol); + match = vim_regexec_nl(®match, (char_u *)str, startcol); if (match && --nth <= 0) { break; @@ -4983,9 +4982,9 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, li = TV_LIST_ITEM_NEXT(l, li); idx++; } else { - startcol = (colnr_T)((char_u *)regmatch.startp[0] + startcol = (colnr_T)(regmatch.startp[0] + utfc_ptr2len(regmatch.startp[0]) - str); - if (startcol > (colnr_T)len || str + startcol <= (char_u *)regmatch.startp[0]) { + if (startcol > (colnr_T)len || str + startcol <= regmatch.startp[0]) { match = false; break; } @@ -5003,9 +5002,9 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, xfree(TV_LIST_ITEM_TV(li1)->vval.v_string); const size_t rd = (size_t)(regmatch.endp[0] - regmatch.startp[0]); - TV_LIST_ITEM_TV(li1)->vval.v_string = xmemdupz((const char *)regmatch.startp[0], rd); - TV_LIST_ITEM_TV(li3)->vval.v_number = (varnumber_T)((char_u *)regmatch.startp[0] - expr); - TV_LIST_ITEM_TV(li4)->vval.v_number = (varnumber_T)(regmatch.endp[0] - (char *)expr); + TV_LIST_ITEM_TV(li1)->vval.v_string = xmemdupz(regmatch.startp[0], rd); + TV_LIST_ITEM_TV(li3)->vval.v_number = (varnumber_T)(regmatch.startp[0] - expr); + TV_LIST_ITEM_TV(li4)->vval.v_number = (varnumber_T)(regmatch.endp[0] - expr); if (l != NULL) { TV_LIST_ITEM_TV(li2)->vval.v_number = (varnumber_T)idx; } @@ -5039,11 +5038,9 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, rettv->vval.v_number = idx; } else { if (type == kSomeMatch) { - rettv->vval.v_number = - (varnumber_T)((char_u *)regmatch.startp[0] - str); + rettv->vval.v_number = (varnumber_T)(regmatch.startp[0] - str); } else { - rettv->vval.v_number = - (varnumber_T)(regmatch.endp[0] - (char *)str); + rettv->vval.v_number = (varnumber_T)(regmatch.endp[0] - str); } rettv->vval.v_number += (varnumber_T)(str - expr); } diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index b0a56c4440..fdeb52053c 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -206,12 +206,12 @@ char_u *get_lambda_name(void) return name; } -static void set_ufunc_name(ufunc_T *fp, char_u *name) +static void set_ufunc_name(ufunc_T *fp, char *name) { STRCPY(fp->uf_name, name); - if (name[0] == K_SPECIAL) { - fp->uf_name_exp = xmalloc(STRLEN(name) + 3); + if ((uint8_t)name[0] == K_SPECIAL) { + fp->uf_name_exp = xmalloc(strlen(name) + 3); STRCPY(fp->uf_name_exp, ""); STRCAT(fp->uf_name_exp, fp->uf_name + 3); } @@ -275,9 +275,9 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate) char_u *p; garray_T newlines; - char_u *name = get_lambda_name(); + char *name = (char *)get_lambda_name(); - fp = xcalloc(1, offsetof(ufunc_T, uf_name) + STRLEN(name) + 1); + fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1); pt = xcalloc(1, sizeof(partial_T)); ga_init(&newlines, (int)sizeof(char_u *), 1); @@ -378,9 +378,9 @@ char_u *deref_func_name(const char *name, int *lenp, partial_T **const partialp, if (partialp != NULL) { *partialp = pt; } - char_u *s = (char_u *)partial_name(pt); - *lenp = (int)STRLEN(s); - return s; + char *s = partial_name(pt); + *lenp = (int)strlen(s); + return (char_u *)s; } return (char_u *)name; @@ -2476,8 +2476,8 @@ void ex_function(exarg_T *eap) if (SOURCING_NAME != NULL) { scriptname = (char_u *)autoload_name((const char *)name, strlen(name)); p = vim_strchr((char *)scriptname, '/'); - plen = (int)STRLEN(p); - slen = (int)STRLEN(SOURCING_NAME); + plen = (int)strlen(p); + slen = (int)strlen(SOURCING_NAME); if (slen > plen && path_fnamecmp(p, SOURCING_NAME + slen - plen) == 0) { j = OK; } @@ -2513,7 +2513,7 @@ void ex_function(exarg_T *eap) } // insert the new function in the function list - set_ufunc_name(fp, (char_u *)name); + set_ufunc_name(fp, name); if (overwrite) { hi = hash_find(&func_hashtab, name); hi->hi_key = UF2HIKEY(fp); @@ -2887,7 +2887,7 @@ void ex_call(exarg_T *eap) char_u *arg = (char_u *)eap->arg; char_u *startarg; char_u *name; - char_u *tofree; + char *tofree; int len; typval_T rettv; linenr_T lnum; @@ -2908,7 +2908,7 @@ void ex_call(exarg_T *eap) return; } - tofree = trans_function_name((char **)&arg, false, TFN_INT, &fudi, &partial); + tofree = (char *)trans_function_name((char **)&arg, false, TFN_INT, &fudi, &partial); if (fudi.fd_newkey != NULL) { // Still need to give an error message for missing key. semsg(_(e_dictkey), fudi.fd_newkey); @@ -2927,9 +2927,8 @@ void ex_call(exarg_T *eap) // If it is the name of a variable of type VAR_FUNC or VAR_PARTIAL use its // contents. For VAR_PARTIAL get its partial, unless we already have one // from trans_function_name(). - len = (int)STRLEN(tofree); - name = deref_func_name((const char *)tofree, &len, - partial != NULL ? NULL : &partial, false); + len = (int)strlen(tofree); + name = deref_func_name(tofree, &len, partial != NULL ? NULL : &partial, false); // Skip white space to allow ":call func ()". Not good, but required for // backward compatibility. @@ -3556,8 +3555,8 @@ bool set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID) /// Registers a luaref as a lambda. char_u *register_luafunc(LuaRef ref) { - char_u *name = get_lambda_name(); - ufunc_T *fp = xcalloc(1, offsetof(ufunc_T, uf_name) + STRLEN(name) + 1); + char *name = (char *)get_lambda_name(); + ufunc_T *fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1); fp->uf_refcount = 1; fp->uf_varargs = true; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 77d310e338..c5ba750eb3 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2869,7 +2869,7 @@ static void append_command(char *cmd) STRCPY(d, "..."); } STRCAT(IObuff, ": "); - d = (char *)IObuff + STRLEN(IObuff); + d = IObuff + strlen(IObuff); while (*s != NUL && d - IObuff + 5 < IOSIZE) { if ((char_u)s[0] == 0xc2 && (char_u)s[1] == 0xa0) { s += 2; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 031226c5a1..52abbd13f3 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -370,7 +370,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat pos_T end_pos; proftime_T tm; int skiplen, patlen; - char_u next_char; + char next_char; char_u use_last_pat; int search_delim; @@ -408,7 +408,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat int found; // do_search() result // Use the previous pattern for ":s//". - next_char = (char_u)ccline.cmdbuff[skiplen + patlen]; + next_char = ccline.cmdbuff[skiplen + patlen]; use_last_pat = patlen == 0 && skiplen > 0 && ccline.cmdbuff[skiplen - 1] == next_char; @@ -437,7 +437,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat found = do_search(NULL, firstc == ':' ? '/' : firstc, search_delim, (char_u *)ccline.cmdbuff + skiplen, count, search_flags, &sia); - ccline.cmdbuff[skiplen + patlen] = (char)next_char; + ccline.cmdbuff[skiplen + patlen] = next_char; emsg_off--; if (curwin->w_cursor.lnum < search_first_line || curwin->w_cursor.lnum > search_last_line) { @@ -486,13 +486,13 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat // Disable 'hlsearch' highlighting if the pattern matches // everything. Avoids a flash when typing "foo\|". if (!use_last_pat) { - next_char = (char_u)ccline.cmdbuff[skiplen + patlen]; + next_char = ccline.cmdbuff[skiplen + patlen]; ccline.cmdbuff[skiplen + patlen] = NUL; if (empty_pattern(ccline.cmdbuff) && !no_hlsearch) { redraw_all_later(UPD_SOME_VALID); set_no_hlsearch(true); } - ccline.cmdbuff[skiplen + patlen] = (char)next_char; + ccline.cmdbuff[skiplen + patlen] = next_char; } validate_cursor(); @@ -1051,7 +1051,7 @@ static int command_line_execute(VimState *state, int key) vungetc(s->c); s->c = Ctrl_BSL; } else if (s->c == 'e') { - char_u *p = NULL; + char *p = NULL; int len; // Replace the command line with the result of an expression. @@ -1066,11 +1066,11 @@ static int command_line_execute(VimState *state, int key) s->c = get_expr_register(); if (s->c == '=') { textlock++; - p = (char_u *)get_expr_line(); + p = get_expr_line(); textlock--; if (p != NULL) { - len = (int)STRLEN(p); + len = (int)strlen(p); realloc_cmdbuff(len + 1); ccline.cmdlen = len; STRCPY(ccline.cmdbuff, p); @@ -1303,20 +1303,20 @@ static int may_do_command_line_next_incsearch(int firstc, long count, incsearch_ ui_flush(); pos_T t; - char_u *pat; + char *pat; int search_flags = SEARCH_NOOF; - char_u save; + char save; if (search_delim == ccline.cmdbuff[skiplen]) { - pat = last_search_pattern(); + pat = (char *)last_search_pattern(); if (pat == NULL) { restore_last_search_pattern(); return FAIL; } skiplen = 0; - patlen = (int)STRLEN(pat); + patlen = (int)strlen(pat); } else { - pat = (char_u *)ccline.cmdbuff + skiplen; + pat = ccline.cmdbuff + skiplen; } if (next_match) { @@ -1338,7 +1338,7 @@ static int may_do_command_line_next_incsearch(int firstc, long count, incsearch_ pat[patlen] = NUL; int found = searchit(curwin, curbuf, &t, NULL, next_match ? FORWARD : BACKWARD, - pat, count, search_flags, + (char_u *)pat, count, search_flags, RE_SEARCH, NULL); emsg_off--; pat[patlen] = save; @@ -1843,21 +1843,21 @@ static int command_line_handle_key(CommandLineState *s) if (s->hiscnt != s->save_hiscnt) { // jumped to other entry - char_u *p; + char *p; int len = 0; int old_firstc; XFREE_CLEAR(ccline.cmdbuff); s->xpc.xp_context = EXPAND_NOTHING; if (s->hiscnt == get_hislen()) { - p = (char_u *)s->lookfor; // back to the old one + p = s->lookfor; // back to the old one } else { - p = (char_u *)get_histentry(s->histype)[s->hiscnt].hisstr; + p = get_histentry(s->histype)[s->hiscnt].hisstr; } if (s->histype == HIST_SEARCH - && p != (char_u *)s->lookfor - && (old_firstc = p[STRLEN(p) + 1]) != s->firstc) { + && p != s->lookfor + && (old_firstc = (uint8_t)p[strlen(p) + 1]) != s->firstc) { // Correct for the separator character used when // adding the history entry vs the one used now. // First loop: count length. @@ -1884,7 +1884,7 @@ static int command_line_handle_key(CommandLineState *s) } if (i > 0) { - ccline.cmdbuff[len] = (char)p[j]; + ccline.cmdbuff[len] = p[j]; } } len++; @@ -1896,7 +1896,7 @@ static int command_line_handle_key(CommandLineState *s) } ccline.cmdbuff[len] = NUL; } else { - alloc_cmdbuff((int)STRLEN(p)); + alloc_cmdbuff((int)strlen(p)); STRCPY(ccline.cmdbuff, p); } @@ -3381,7 +3381,7 @@ void put_on_cmdline(char_u *str, int len, int redraw) int c; if (len < 0) { - len = (int)STRLEN(str); + len = (int)strlen((char *)str); } realloc_cmdbuff(ccline.cmdlen + len + 1); diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index e907c45e79..eae3b4af1a 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -956,7 +956,7 @@ void ex_mkrc(exarg_T *eap) } // When using 'viewdir' may have to create the directory. - if (using_vdir && !os_isdir((char *)p_vdir)) { + if (using_vdir && !os_isdir(p_vdir)) { vim_mkdir_emsg((const char *)p_vdir, 0755); } @@ -1095,7 +1095,7 @@ static char *get_view_file(int c) len++; } } - char *retval = xmalloc(strlen(sname) + len + STRLEN(p_vdir) + 9); + char *retval = xmalloc(strlen(sname) + len + strlen(p_vdir) + 9); STRCPY(retval, p_vdir); add_pathsep(retval); char *s = retval + strlen(retval); diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 1872558669..04e6818d53 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -1583,7 +1583,7 @@ static void foldAddMarker(buf_T *buf, pos_T pos, const char *marker, size_t mark // Allocate a new line: old-line + 'cms'-start + marker + 'cms'-end char *line = ml_get_buf(buf, lnum, false); - size_t line_len = STRLEN(line); + size_t line_len = strlen(line); size_t added = 0; if (u_save(lnum - 1, lnum + 1) == OK) { @@ -3249,14 +3249,14 @@ void f_foldtext(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } // Find interesting text in this line. - char_u *s = (char_u *)skipwhite(ml_get(lnum)); + char *s = skipwhite(ml_get(lnum)); // skip C comment-start if (s[0] == '/' && (s[1] == '*' || s[1] == '/')) { - s = (char_u *)skipwhite((char *)s + 2); - if (*skipwhite((char *)s) == NUL && lnum + 1 < foldend) { - s = (char_u *)skipwhite(ml_get(lnum + 1)); + s = skipwhite(s + 2); + if (*skipwhite(s) == NUL && lnum + 1 < foldend) { + s = skipwhite(ml_get(lnum + 1)); if (*s == '*') { - s = (char_u *)skipwhite((char *)s + 1); + s = skipwhite(s + 1); } } } @@ -3265,7 +3265,7 @@ void f_foldtext(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) size_t len = strlen(txt) + strlen(dashes) // for %s + 20 // for %3ld - + STRLEN(s); // concatenated + + strlen(s); // concatenated char *r = xmalloc(len); snprintf(r, len, txt, dashes, count); len = strlen(r); diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 4135065787..16f3c477f6 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -170,15 +170,15 @@ static char_u *get_buffcont(buffheader_T *buffer, int dozero) /// K_SPECIAL in the returned string is escaped. char_u *get_recorded(void) { - char_u *p; + char *p; size_t len; - p = get_buffcont(&recordbuff, true); + p = (char *)get_buffcont(&recordbuff, true); free_buff(&recordbuff); // Remove the characters that were added the last time, these must be the // (possibly mapped) characters that stopped the recording. - len = STRLEN(p); + len = strlen(p); if (len >= last_recorded_len) { len -= last_recorded_len; p[len] = NUL; @@ -190,7 +190,7 @@ char_u *get_recorded(void) p[len - 1] = NUL; } - return p; + return (char_u *)p; } /// Return the contents of the redo buffer as a single string. @@ -2237,7 +2237,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) // If this is a LANGMAP mapping, then we didn't record the keys // at the start of the function and have to record them now. if (keylen > typebuf.tb_maplen && (mp->m_mode & MODE_LANGMAP) != 0) { - gotchars((char_u *)map_str, STRLEN(map_str)); + gotchars((char_u *)map_str, strlen(map_str)); } if (save_m_noremap != REMAP_YES) { diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index c46f95b64f..7cc434a197 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -253,14 +253,14 @@ struct prt_resfile_buffer_S { // Returns an error message or NULL; char *parse_printoptions(void) { - return parse_list_options((char_u *)p_popt, printer_opts, OPT_PRINT_NUM_OPTIONS); + return parse_list_options(p_popt, printer_opts, OPT_PRINT_NUM_OPTIONS); } // Parse 'printoptions' and set the flags in "printer_opts". // Returns an error message or NULL; char *parse_printmbfont(void) { - return parse_list_options((char_u *)p_pmfn, mbfont_opts, OPT_MBFONT_NUM_OPTIONS); + return parse_list_options(p_pmfn, mbfont_opts, OPT_MBFONT_NUM_OPTIONS); } // Parse a list of options in the form @@ -270,12 +270,12 @@ char *parse_printmbfont(void) // // Returns an error message for an illegal option, NULL otherwise. // Only used for the printer at the moment... -static char *parse_list_options(char_u *option_str, option_table_T *table, size_t table_size) +static char *parse_list_options(char *option_str, option_table_T *table, size_t table_size) { option_table_T *old_opts; char *ret = NULL; - char_u *stringp; - char_u *colonp; + char *stringp; + char *colonp; char *commap; char *p; size_t idx = 0; // init for GCC @@ -292,14 +292,14 @@ static char *parse_list_options(char_u *option_str, option_table_T *table, size_ // Repeat for all comma separated parts. stringp = option_str; while (*stringp) { - colonp = (char_u *)vim_strchr((char *)stringp, ':'); + colonp = vim_strchr(stringp, ':'); if (colonp == NULL) { ret = N_("E550: Missing colon"); break; } - commap = vim_strchr((char *)stringp, ','); + commap = vim_strchr(stringp, ','); if (commap == NULL) { - commap = (char *)option_str + STRLEN(option_str); + commap = option_str + strlen(option_str); } len = (int)(colonp - stringp); @@ -315,7 +315,7 @@ static char *parse_list_options(char_u *option_str, option_table_T *table, size_ break; } - p = (char *)colonp + 1; + p = colonp + 1; table[idx].present = true; if (table[idx].hasnum) { @@ -330,7 +330,7 @@ static char *parse_list_options(char_u *option_str, option_table_T *table, size_ table[idx].string = (char_u *)p; table[idx].strlen = (int)(commap - p); - stringp = (char_u *)commap; + stringp = commap; if (*stringp == ',') { stringp++; } @@ -1171,8 +1171,8 @@ static struct prt_ps_mbfont_S prt_ps_mbfonts[] = // VIM Prolog CIDProlog // 6.2 1.3 // 7.0 1.4 1.0 -#define PRT_PROLOG_VERSION ((char_u *)"1.4") -#define PRT_CID_PROLOG_VERSION ((char_u *)"1.0") +#define PRT_PROLOG_VERSION "1.4" +#define PRT_CID_PROLOG_VERSION "1.0" // Strings to look for in a PS resource file #define PRT_RESOURCE_HEADER "%!PS-Adobe-" @@ -1737,11 +1737,11 @@ static bool prt_open_resource(struct prt_ps_resource_S *resource) return true; } -static bool prt_check_resource(const struct prt_ps_resource_S *resource, const char_u *version) +static bool prt_check_resource(const struct prt_ps_resource_S *resource, const char *version) FUNC_ATTR_NONNULL_ALL { // Version number m.n should match, the revision number does not matter - if (STRNCMP(resource->version, version, STRLEN(version))) { + if (STRNCMP(resource->version, version, strlen(version))) { semsg(_("E621: \"%s\" resource file has wrong version"), resource->name); return false; diff --git a/src/nvim/help.c b/src/nvim/help.c index 7c61a56785..5cc2cda52b 100644 --- a/src/nvim/help.c +++ b/src/nvim/help.c @@ -659,7 +659,7 @@ void fix_help_buffer(void) if (!syntax_present(curwin)) { for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; lnum++) { line = ml_get_buf(curbuf, lnum, false); - const size_t len = STRLEN(line); + const size_t len = strlen(line); if (in_example && len > 0 && !ascii_iswhite(line[0])) { // End of example: non-white or '<' in first column. if (line[0] == '<') { diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 0f7a5a8e44..2509388009 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -434,10 +434,10 @@ int get_indent_str_vtab(const char *ptr, long ts, long *vts, bool list) // Returns true if the line was changed. int set_indent(int size, int flags) { - char_u *p; - char_u *newline; - char_u *oldline; - char_u *s; + char *p; + char *newline; + char *oldline; + char *s; int todo; int ind_len; // Measured in characters. int line_len; @@ -454,7 +454,7 @@ int set_indent(int size, int flags) // characters needed for the indent. todo = size; ind_len = 0; - p = oldline = (char_u *)get_cursor_line_ptr(); + p = oldline = get_cursor_line_ptr(); // Calculate the buffer size for the new indent, and check to see if it // isn't already set. @@ -549,7 +549,7 @@ int set_indent(int size, int flags) if (flags & SIN_INSERT) { p = oldline; } else { - p = (char_u *)skipwhite((char *)p); + p = skipwhite(p); } line_len = (int)STRLEN(p) + 1; @@ -631,7 +631,7 @@ int set_indent(int size, int flags) todo -= tab_pad; ind_done += tab_pad; } - p = (char_u *)skipwhite((char *)p); + p = skipwhite(p); } for (;;) { @@ -659,7 +659,7 @@ int set_indent(int size, int flags) const colnr_T new_offset = (colnr_T)(s - newline); // this may free "newline" - ml_replace(curwin->w_cursor.lnum, (char *)newline, false); + ml_replace(curwin->w_cursor.lnum, newline, false); if (!(flags & SIN_NOMARK)) { extmark_splice_cols(curbuf, (int)curwin->w_cursor.lnum - 1, @@ -1130,13 +1130,13 @@ int get_lisp_indent(void) static int lisp_match(char_u *p) { - char_u buf[LSIZE]; + char buf[LSIZE]; int len; char *word = (char *)(*curbuf->b_p_lw != NUL ? (char_u *)curbuf->b_p_lw : p_lispwords); while (*word != NUL) { - (void)copy_option_part(&word, (char *)buf, LSIZE, ","); - len = (int)STRLEN(buf); + (void)copy_option_part(&word, buf, LSIZE, ","); + len = (int)strlen(buf); if ((STRNCMP(buf, p, len) == 0) && (p[len] == ' ')) { return true; diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 643b7f477c..f136aa960b 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -1311,9 +1311,9 @@ static void ins_compl_dictionaries(char_u *dict_start, char_u *pat, int flags, i // to only match at the start of a line. Otherwise just match the // pattern. Also need to double backslashes. if (ctrl_x_mode_line_or_eval()) { - char_u *pat_esc = vim_strsave_escaped(pat, (char_u *)"\\"); + char *pat_esc = (char *)vim_strsave_escaped(pat, (char_u *)"\\"); - size_t len = STRLEN(pat_esc) + 10; + size_t len = strlen(pat_esc) + 10; ptr = xmalloc(len); vim_snprintf((char *)ptr, len, "^\\s*\\zs\\V%s", pat_esc); regmatch.regprog = vim_regcomp((char *)ptr, RE_MAGIC); @@ -1788,13 +1788,13 @@ static void ins_compl_set_original_text(char *str) /// matches. void ins_compl_addfrommatch(void) { - char_u *p; + char *p; int len = (int)curwin->w_cursor.col - (int)compl_col; int c; compl_T *cp; assert(compl_shown_match != NULL); - p = (char_u *)compl_shown_match->cp_str; - if ((int)STRLEN(p) <= len) { // the match is too short + p = compl_shown_match->cp_str; + if ((int)strlen(p) <= len) { // the match is too short // When still at the original match use the first entry that matches // the leader. if (!match_at_original_text(compl_shown_match)) { @@ -1805,17 +1805,17 @@ void ins_compl_addfrommatch(void) for (cp = compl_shown_match->cp_next; cp != NULL && !is_first_match(cp); cp = cp->cp_next) { if (compl_leader == NULL - || ins_compl_equal(cp, (char_u *)compl_leader, STRLEN(compl_leader))) { - p = (char_u *)cp->cp_str; + || ins_compl_equal(cp, (char_u *)compl_leader, strlen(compl_leader))) { + p = cp->cp_str; break; } } - if (p == NULL || (int)STRLEN(p) <= len) { + if (p == NULL || (int)strlen(p) <= len) { return; } } p += len; - c = utf_ptr2char((char *)p); + c = utf_ptr2char(p); ins_compl_addleader(c); } @@ -2008,17 +2008,17 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval) // but only do this, if the Popup is still visible if (c == Ctrl_E) { ins_compl_delete(); - char_u *p = NULL; + char *p = NULL; if (compl_leader != NULL) { - p = (char_u *)compl_leader; + p = compl_leader; } else if (compl_first_match != NULL) { - p = (char_u *)compl_orig_text; + p = compl_orig_text; } if (p != NULL) { const int compl_len = get_compl_len(); - const int len = (int)STRLEN(p); + const int len = (int)strlen(p); if (len > compl_len) { - ins_bytes_len((char *)p + compl_len, (size_t)(len - compl_len)); + ins_bytes_len(p + compl_len, (size_t)(len - compl_len)); } } retval = true; @@ -2929,33 +2929,33 @@ static char_u *ins_comp_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_p bool *cont_s_ipos) { *match_len = 0; - char_u *ptr = (char_u *)ml_get_buf(ins_buf, cur_match_pos->lnum, false) + cur_match_pos->col; + char *ptr = ml_get_buf(ins_buf, cur_match_pos->lnum, false) + cur_match_pos->col; int len; if (ctrl_x_mode_line_or_eval()) { if (compl_status_adding()) { if (cur_match_pos->lnum >= ins_buf->b_ml.ml_line_count) { return NULL; } - ptr = (char_u *)ml_get_buf(ins_buf, cur_match_pos->lnum + 1, false); + ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, false); if (!p_paste) { - ptr = (char_u *)skipwhite((char *)ptr); + ptr = skipwhite(ptr); } } - len = (int)STRLEN(ptr); + len = (int)strlen(ptr); } else { - char_u *tmp_ptr = ptr; + char *tmp_ptr = ptr; - if (compl_status_adding() && compl_length <= (int)STRLEN(tmp_ptr)) { + if (compl_status_adding() && compl_length <= (int)strlen(tmp_ptr)) { tmp_ptr += compl_length; // Skip if already inside a word. - if (vim_iswordp(tmp_ptr)) { + if (vim_iswordp((char_u *)tmp_ptr)) { return NULL; } // Find start of next word. - tmp_ptr = find_word_start(tmp_ptr); + tmp_ptr = (char *)find_word_start((char_u *)tmp_ptr); } // Find end of this word. - tmp_ptr = find_word_end(tmp_ptr); + tmp_ptr = (char *)find_word_end((char_u *)tmp_ptr); len = (int)(tmp_ptr - ptr); if (compl_status_adding() && len == compl_length) { @@ -2964,12 +2964,12 @@ static char_u *ins_comp_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_p // normal command "J" was used. IOSIZE is always greater than // compl_length, so the next STRNCPY always works -- Acevedo STRNCPY(IObuff, ptr, len); // NOLINT(runtime/printf) - ptr = (char_u *)ml_get_buf(ins_buf, cur_match_pos->lnum + 1, false); - tmp_ptr = ptr = (char_u *)skipwhite((char *)ptr); + ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, false); + tmp_ptr = ptr = skipwhite(ptr); // Find start of next word. - tmp_ptr = find_word_start(tmp_ptr); + tmp_ptr = (char *)find_word_start((char_u *)tmp_ptr); // Find end of next word. - tmp_ptr = find_word_end(tmp_ptr); + tmp_ptr = (char *)find_word_end((char_u *)tmp_ptr); if (tmp_ptr > ptr) { if (*ptr != ')' && IObuff[len - 1] != TAB) { if (IObuff[len - 1] != ' ') { @@ -2992,7 +2992,7 @@ static char_u *ins_comp_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_p *cont_s_ipos = true; } IObuff[len] = NUL; - ptr = (char_u *)IObuff; + ptr = IObuff; } if (len == compl_length) { return NULL; @@ -3001,7 +3001,7 @@ static char_u *ins_comp_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_p } *match_len = len; - return ptr; + return (char_u *)ptr; } /// Get the next set of words matching "compl_pattern" for default completion(s) @@ -3285,7 +3285,7 @@ static int ins_compl_get_exp(pos_T *ini) static void ins_compl_update_shown_match(void) { while (!ins_compl_equal(compl_shown_match, - (char_u *)compl_leader, STRLEN(compl_leader)) + (char_u *)compl_leader, strlen(compl_leader)) && compl_shown_match->cp_next != NULL && !is_first_match(compl_shown_match->cp_next)) { compl_shown_match = compl_shown_match->cp_next; @@ -3294,10 +3294,10 @@ static void ins_compl_update_shown_match(void) // If we didn't find it searching forward, and compl_shows_dir is // backward, find the last match. if (compl_shows_dir_backward() - && !ins_compl_equal(compl_shown_match, (char_u *)compl_leader, STRLEN(compl_leader)) + && !ins_compl_equal(compl_shown_match, (char_u *)compl_leader, strlen(compl_leader)) && (compl_shown_match->cp_next == NULL || is_first_match(compl_shown_match->cp_next))) { - while (!ins_compl_equal(compl_shown_match, (char_u *)compl_leader, STRLEN(compl_leader)) + while (!ins_compl_equal(compl_shown_match, (char_u *)compl_leader, strlen(compl_leader)) && compl_shown_match->cp_prev != NULL && !is_first_match(compl_shown_match->cp_prev)) { compl_shown_match = compl_shown_match->cp_prev; @@ -3442,7 +3442,7 @@ static int find_next_completion_match(bool allow_get_expansion, int todo, bool a if (!match_at_original_text(compl_shown_match) && compl_leader != NULL && !ins_compl_equal(compl_shown_match, - (char_u *)compl_leader, STRLEN(compl_leader))) { + (char_u *)compl_leader, strlen(compl_leader))) { todo++; } else { // Remember a matching item. @@ -3721,17 +3721,17 @@ static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col) compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length); } } else if (compl_status_adding()) { - char_u *prefix = (char_u *)"\\<"; + char *prefix = "\\<"; // we need up to 2 extra chars for the prefix compl_pattern = xmalloc(quote_meta(NULL, (char_u *)line + compl_col, compl_length) + 2); if (!vim_iswordp((char_u *)line + compl_col) || (compl_col > 0 && (vim_iswordp(mb_prevptr((char_u *)line, (char_u *)line + compl_col))))) { - prefix = (char_u *)""; + prefix = ""; } STRCPY(compl_pattern, prefix); - (void)quote_meta((char_u *)compl_pattern + STRLEN(prefix), + (void)quote_meta((char_u *)compl_pattern + strlen(prefix), (char_u *)line + compl_col, compl_length); } else if (--startcol < 0 || !vim_iswordp(mb_prevptr((char_u *)line, (char_u *)line + startcol + 1))) { diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index f3821f149a..fca07ee146 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1758,7 +1758,7 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_setfield(lstate, -2, "_ts_get_minimum_language_version"); } -int nlua_expand_pat(expand_T *xp, char_u *pat, int *num_results, char ***results) +int nlua_expand_pat(expand_T *xp, char *pat, int *num_results, char ***results) { lua_State *const lstate = global_lstate; int ret = OK; @@ -1771,7 +1771,7 @@ int nlua_expand_pat(expand_T *xp, char_u *pat, int *num_results, char ***results luaL_checktype(lstate, -1, LUA_TFUNCTION); // [ vim, vim._expand_pat, buf ] - lua_pushlstring(lstate, (const char *)pat, STRLEN(pat)); + lua_pushlstring(lstate, (const char *)pat, strlen(pat)); if (nlua_pcall(lstate, 1, 2) != 0) { nlua_error(lstate, diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index 2f46f6ff65..6974c6181c 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -111,14 +111,14 @@ static int regex_match_line(lua_State *lstate) return luaL_error(lstate, "invalid row"); } - char_u *line = (char_u *)ml_get_buf(buf, rownr + 1, false); - size_t len = STRLEN(line); + char *line = ml_get_buf(buf, rownr + 1, false); + size_t len = strlen(line); if (start < 0 || (size_t)start > len) { return luaL_error(lstate, "invalid start"); } - char_u save = NUL; + char save = NUL; if (end >= 0) { if ((size_t)end > len || end < start) { return luaL_error(lstate, "invalid end"); @@ -127,7 +127,7 @@ static int regex_match_line(lua_State *lstate) line[end] = NUL; } - int nret = regex_match(lstate, prog, line + start); + int nret = regex_match(lstate, prog, (char_u *)line + start); if (end >= 0) { line[end] = save; diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index 79b11eca4a..9d871939f8 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -333,7 +333,7 @@ static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position return ""; } char *line = ml_get_buf(bp, (linenr_T)position.row + 1, false); - size_t len = STRLEN(line); + size_t len = strlen(line); if (position.column > len) { *bytes_read = 0; return ""; diff --git a/src/nvim/mark.c b/src/nvim/mark.c index f41793c8a6..8172aec543 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -1698,18 +1698,18 @@ void mark_mb_adjustpos(buf_T *buf, pos_T *lp) FUNC_ATTR_NONNULL_ALL { if (lp->col > 0 || lp->coladd > 1) { - const char_u *const p = (char_u *)ml_get_buf(buf, lp->lnum, false); - if (*p == NUL || (int)STRLEN(p) < lp->col) { + const char *const p = ml_get_buf(buf, lp->lnum, false); + if (*p == NUL || (int)strlen(p) < lp->col) { lp->col = 0; } else { - lp->col -= utf_head_off((char *)p, (char *)p + lp->col); + lp->col -= utf_head_off(p, p + lp->col); } // Reset "coladd" when the cursor would be on the right half of a // double-wide character. if (lp->coladd == 1 && p[lp->col] != TAB - && vim_isprintc(utf_ptr2char((char *)p + lp->col)) - && ptr2cells((char *)p + lp->col) > 1) { + && vim_isprintc(utf_ptr2char(p + lp->col)) + && ptr2cells(p + lp->col) > 1) { lp->coladd = 0; } } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 0ef7b76de3..1ac8d7013e 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2622,7 +2622,7 @@ void may_clear_cmdline(void) // Routines for displaying a partly typed command #define SHOWCMD_BUFLEN (SHOWCMD_COLS + 1 + 30) -static char_u showcmd_buf[SHOWCMD_BUFLEN]; +static char showcmd_buf[SHOWCMD_BUFLEN]; static char_u old_showcmd_buf[SHOWCMD_BUFLEN]; // For push_showcmd() static bool showcmd_is_clear = true; static bool showcmd_visual = false; @@ -2662,10 +2662,10 @@ void clear_showcmd(void) getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol); p_sbr = saved_sbr; curwin->w_p_sbr = saved_w_sbr; - snprintf((char *)showcmd_buf, SHOWCMD_BUFLEN, "%" PRId64 "x%" PRId64, + snprintf(showcmd_buf, SHOWCMD_BUFLEN, "%" PRId64 "x%" PRId64, (int64_t)lines, (int64_t)rightcol - leftcol + 1); } else if (VIsual_mode == 'V' || VIsual.lnum != curwin->w_cursor.lnum) { - snprintf((char *)showcmd_buf, SHOWCMD_BUFLEN, "%" PRId64, (int64_t)lines); + snprintf(showcmd_buf, SHOWCMD_BUFLEN, "%" PRId64, (int64_t)lines); } else { char_u *s, *e; int l; @@ -2691,9 +2691,9 @@ void clear_showcmd(void) s += l; } if (bytes == chars) { - sprintf((char *)showcmd_buf, "%d", chars); + snprintf(showcmd_buf, SHOWCMD_BUFLEN, "%d", chars); } else { - sprintf((char *)showcmd_buf, "%d-%d", chars, bytes); + snprintf(showcmd_buf, SHOWCMD_BUFLEN, "%d-%d", chars, bytes); } } int limit = ui_has(kUIMessages) ? SHOWCMD_BUFLEN - 1 : SHOWCMD_COLS; @@ -2783,7 +2783,7 @@ static void del_from_showcmd(int len) return; } - old_len = (int)STRLEN(showcmd_buf); + old_len = (int)strlen(showcmd_buf); if (len > old_len) { len = old_len; } @@ -2822,7 +2822,7 @@ static void display_showcmd(void) } int len; - len = (int)STRLEN(showcmd_buf); + len = (int)strlen(showcmd_buf); showcmd_is_clear = (len == 0); if (ui_has(kUIMessages)) { @@ -2831,7 +2831,7 @@ static void display_showcmd(void) if (len > 0) { // placeholder for future highlight support ADD_C(chunk, INTEGER_OBJ(0)); - ADD_C(chunk, STRING_OBJ(cstr_as_string((char *)showcmd_buf))); + ADD_C(chunk, STRING_OBJ(cstr_as_string(showcmd_buf))); ADD_C(content, ARRAY_OBJ(chunk)); } ui_call_msg_showcmd(content); @@ -2843,7 +2843,7 @@ static void display_showcmd(void) grid_puts_line_start(&msg_grid_adj, showcmd_row); if (!showcmd_is_clear) { - grid_puts(&msg_grid_adj, (char *)showcmd_buf, showcmd_row, sc_col, + grid_puts(&msg_grid_adj, showcmd_buf, showcmd_row, sc_col, HL_ATTR(HLF_MSG)); } @@ -4360,7 +4360,7 @@ static void nv_ident(cmdarg_T *cap) aux_ptr = "\\|\"\n*?["; } - p = buf + STRLEN(buf); + p = buf + strlen(buf); while (n-- > 0) { // put a backslash before \ and some others if (vim_strchr(aux_ptr, *ptr) != NULL) { @@ -5295,7 +5295,7 @@ static void nv_kundo(cmdarg_T *cap) /// Handle the "r" command. static void nv_replace(cmdarg_T *cap) { - char_u *ptr; + char *ptr; int had_ctrl_v; if (checkclearop(cap->oap)) { @@ -5358,9 +5358,9 @@ static void nv_replace(cmdarg_T *cap) } // Abort if not enough characters to replace. - ptr = (char_u *)get_cursor_pos_ptr(); - if (STRLEN(ptr) < (unsigned)cap->count1 - || (mb_charlen(ptr) < cap->count1)) { + ptr = get_cursor_pos_ptr(); + if (strlen(ptr) < (unsigned)cap->count1 + || (mb_charlen((char_u *)ptr) < cap->count1)) { clearopbeep(cap->oap); return; } diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 3bb397fdd8..8b2fea9535 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -418,7 +418,7 @@ static void shift_block(oparg_T *oap, int amount) size_t fill; // nr of spaces that replace a TAB size_t new_line_len; // the length of the line after the // block shift - char_u *non_white = bd.textstart; + char *non_white = (char *)bd.textstart; // Firstly, let's find the first non-whitespace character that is // displayed after the block's start column and the character's column @@ -438,13 +438,13 @@ static void shift_block(oparg_T *oap, int amount) chartabsize_T cts; init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, - non_white_col, (char *)bd.textstart, (char *)non_white); + non_white_col, (char *)bd.textstart, non_white); while (ascii_iswhite(*cts.cts_ptr)) { incr = lbr_chartabsize_adv(&cts); cts.cts_vcol += incr; } non_white_col = cts.cts_vcol; - non_white = (char_u *)cts.cts_ptr; + non_white = cts.cts_ptr; clear_chartabsize_arg(&cts); const colnr_T block_space_width = non_white_col - oap->start_vcol; @@ -492,11 +492,11 @@ static void shift_block(oparg_T *oap, int amount) // - the beginning of the original line up to "verbatim_copy_end", // - "fill" number of spaces, // - the rest of the line, pointed to by non_white. - new_line_len = verbatim_diff + fill + STRLEN(non_white) + 1; + new_line_len = verbatim_diff + fill + strlen(non_white) + 1; newp = (char_u *)xmalloc(new_line_len); startcol = (int)verbatim_diff; - oldlen = bd.textcol + (int)(non_white - bd.textstart) - (int)verbatim_diff; + oldlen = bd.textcol + (int)(non_white - (char *)bd.textstart) - (int)verbatim_diff; newlen = (int)fill; memmove(newp, oldp, verbatim_diff); memset(newp + verbatim_diff, ' ', fill); @@ -515,14 +515,14 @@ static void shift_block(oparg_T *oap, int amount) /// Insert string "s" (b_insert ? before : after) block :AKelly /// Caller must prepare for undo. -static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def *bdp) +static void block_insert(oparg_T *oap, char *s, int b_insert, struct block_def *bdp) { int ts_val; int count = 0; // extra spaces to replace a cut TAB int spaces = 0; // non-zero if cutting a TAB colnr_T offset; // pointer along new line - size_t s_len = STRLEN(s); - char_u *newp, *oldp; // new, old lines + size_t s_len = strlen(s); + char *newp, *oldp; // new, old lines linenr_T lnum; // loop var int oldstate = State; State = MODE_INSERT; // don't want MODE_REPLACE for State @@ -533,7 +533,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def continue; // OP_INSERT, line ends before block start } - oldp = (char_u *)ml_get(lnum); + oldp = ml_get(lnum); if (b_insert) { ts_val = bdp->start_char_vcols; @@ -562,7 +562,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def if (spaces > 0) { // avoid copying part of a multi-byte character - offset -= utf_head_off((char *)oldp, (char *)oldp + offset); + offset -= utf_head_off(oldp, oldp + offset); } if (spaces < 0) { // can happen when the cursor was moved spaces = 0; @@ -570,7 +570,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def assert(count >= 0); // Make sure the allocated size matches what is actually copied below. - newp = xmalloc(STRLEN(oldp) + (size_t)spaces + s_len + newp = xmalloc(strlen(oldp) + (size_t)spaces + s_len + (spaces > 0 && !bdp->is_short ? (size_t)ts_val - (size_t)spaces : 0) + (size_t)count + 1); @@ -607,7 +607,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def } STRMOVE(newp + offset, oldp); - ml_replace(lnum, (char *)newp, false); + ml_replace(lnum, newp, false); extmark_splice_cols(curbuf, (int)lnum - 1, startcol, skipped, offset - startcol, kExtmarkUndo); @@ -706,7 +706,7 @@ void op_reindent(oparg_T *oap, Indenter how) } // Keep the last expression line here, for repeating. -static char_u *expr_line = NULL; +static char *expr_line = NULL; /// Get an expression for the "\"=expr1" or "CTRL-R =expr1" /// @@ -732,7 +732,7 @@ int get_expr_register(void) void set_expr_line(char *new_line) { xfree(expr_line); - expr_line = (char_u *)new_line; + expr_line = new_line; } /// Get the result of the '=' register expression. @@ -750,7 +750,7 @@ char *get_expr_line(void) // Make a copy of the expression, because evaluating it may cause it to be // changed. - expr_copy = xstrdup((char *)expr_line); + expr_copy = xstrdup(expr_line); // When we are invoked recursively limit the evaluation to 10 levels. // Then return the string as-is. @@ -771,7 +771,7 @@ char *get_expr_line_src(void) if (expr_line == NULL) { return NULL; } - return xstrdup((char *)expr_line); + return xstrdup(expr_line); } /// @return whether `regname` is a valid name of a yank register. @@ -952,7 +952,7 @@ int do_record(int c) // restore the current register name. old_y_previous = y_previous; - retval = stuff_yank(regname, p); + retval = stuff_yank(regname, (char *)p); y_previous = old_y_previous; } @@ -974,7 +974,7 @@ static void set_yreg_additional_data(yankreg_T *reg, dict_T *additional_data) /// uppercase). "p" must have been allocated. /// /// @return FAIL for failure, OK otherwise -static int stuff_yank(int regname, char_u *p) +static int stuff_yank(int regname, char *p) { // check for read-only register if (regname != 0 && !valid_yank_reg(regname, true)) { @@ -988,7 +988,7 @@ static int stuff_yank(int regname, char_u *p) yankreg_T *reg = get_yank_register(regname, YREG_YANK); if (is_append_register(regname) && reg->y_array != NULL) { char **pp = &(reg->y_array[reg->y_size - 1]); - char_u *lp = xmalloc(strlen(*pp) + STRLEN(p) + 1); + char_u *lp = xmalloc(strlen(*pp) + strlen(p) + 1); STRCPY(lp, *pp); // TODO(philix): use xstpcpy() in stuff_yank() STRCAT(lp, p); @@ -999,7 +999,7 @@ static int stuff_yank(int regname, char_u *p) free_register(reg); set_yreg_additional_data(reg, NULL); reg->y_array = xmalloc(sizeof(char_u *)); - reg->y_array[0] = (char *)p; + reg->y_array[0] = p; reg->y_size = 1; reg->y_type = kMTCharWise; } @@ -1448,7 +1448,7 @@ int op_delete(oparg_T *oap) int n; linenr_T lnum; char_u *ptr; - char_u *newp, *oldp; + char *newp, *oldp; struct block_def bd = { 0 }; linenr_T old_lcount = curbuf->b_ml.ml_line_count; @@ -1579,8 +1579,8 @@ int op_delete(oparg_T *oap) // If we delete a TAB, it may be replaced by several characters. // Thus the number of characters may increase! n = bd.textlen - bd.startspaces - bd.endspaces; - oldp = (char_u *)ml_get(lnum); - newp = (char_u *)xmalloc(STRLEN(oldp) - (size_t)n + 1); + oldp = ml_get(lnum); + newp = xmalloc(strlen(oldp) - (size_t)n + 1); // copy up to deleted part memmove(newp, oldp, (size_t)bd.textcol); // insert spaces @@ -1590,7 +1590,7 @@ int op_delete(oparg_T *oap) oldp += bd.textcol + bd.textlen; STRMOVE(newp + bd.textcol + bd.startspaces + bd.endspaces, oldp); // replace the line - ml_replace(lnum, (char *)newp, false); + ml_replace(lnum, newp, false); extmark_splice_cols(curbuf, (int)lnum - 1, bd.textcol, bd.textlen, bd.startspaces + bd.endspaces, @@ -1697,8 +1697,8 @@ int op_delete(oparg_T *oap) if (virtual_op) { // fix up things for virtualedit-delete: // break the tabs which are going to get in our way - char_u *curline = (char_u *)get_cursor_line_ptr(); - int len = (int)STRLEN(curline); + char *curline = get_cursor_line_ptr(); + int len = (int)strlen(curline); if (oap->end.coladd != 0 && (int)oap->end.col >= len - 1 @@ -1811,7 +1811,7 @@ static int op_replace(oparg_T *oap, int c) { int n, numc; int num_chars; - char_u *newp, *oldp; + char *newp, *oldp; colnr_T oldlen; struct block_def bd; char_u *after_p = NULL; @@ -1886,8 +1886,8 @@ static int op_replace(oparg_T *oap, int c) num_chars = numc; numc *= utf_char2len(c); - oldp = (char_u *)get_cursor_line_ptr(); - oldlen = (int)STRLEN(oldp); + oldp = get_cursor_line_ptr(); + oldlen = (int)strlen(oldp); size_t newp_size = (size_t)bd.textcol + (size_t)bd.startspaces; if (had_ctrl_v_cr || (c != '\r' && c != '\n')) { @@ -1913,7 +1913,7 @@ static int op_replace(oparg_T *oap, int c) // strlen(newp) at this point int newp_len = bd.textcol + bd.startspaces; while (--num_chars >= 0) { - newp_len += utf_char2bytes(c, (char *)newp + newp_len); + newp_len += utf_char2bytes(c, newp + newp_len); } if (!bd.is_short) { // insert post-spaces @@ -1931,7 +1931,7 @@ static int op_replace(oparg_T *oap, int c) newrows = 1; } // replace the line - ml_replace(curwin->w_cursor.lnum, (char *)newp, false); + ml_replace(curwin->w_cursor.lnum, newp, false); curbuf_splice_pending++; linenr_T baselnum = curwin->w_cursor.lnum; if (after_p != NULL) { @@ -2408,7 +2408,7 @@ void op_insert(oparg_T *oap, long count1) ins_text = xstrnsave(firstline, (size_t)ins_len); // block handled here if (u_save(oap->start.lnum, (linenr_T)(oap->end.lnum + 1)) == OK) { - block_insert(oap, (char_u *)ins_text, (oap->op_type == OP_INSERT), &bd); + block_insert(oap, ins_text, (oap->op_type == OP_INSERT), &bd); } curwin->w_cursor.col = oap->start.col; @@ -2431,9 +2431,9 @@ int op_change(oparg_T *oap) long pre_textlen = 0; long pre_indent = 0; char_u *newp; - char_u *firstline; + char *firstline; char_u *ins_text; - char_u *oldp; + char *oldp; struct block_def bd; l = oap->start.col; @@ -2465,9 +2465,9 @@ int op_change(oparg_T *oap) || gchar_cursor() == NUL)) { coladvance_force(getviscol()); } - firstline = (char_u *)ml_get(oap->start.lnum); - pre_textlen = (long)STRLEN(firstline); - pre_indent = (long)getwhitecols((char *)firstline); + firstline = ml_get(oap->start.lnum); + pre_textlen = (long)strlen(firstline); + pre_indent = (long)getwhitecols(firstline); bd.textcol = curwin->w_cursor.col; } @@ -2484,15 +2484,15 @@ int op_change(oparg_T *oap) && oap->start.lnum != oap->end.lnum && !got_int) { // Auto-indenting may have changed the indent. If the cursor was past // the indent, exclude that indent change from the inserted text. - firstline = (char_u *)ml_get(oap->start.lnum); + firstline = ml_get(oap->start.lnum); if (bd.textcol > (colnr_T)pre_indent) { - long new_indent = (long)getwhitecols((char *)firstline); + long new_indent = (long)getwhitecols(firstline); pre_textlen += new_indent - pre_indent; bd.textcol += (colnr_T)(new_indent - pre_indent); } - ins_len = (long)STRLEN(firstline) - pre_textlen; + ins_len = (long)strlen(firstline) - pre_textlen; if (ins_len > 0) { // Subsequent calls to ml_get() flush the firstline data - take a // copy of the inserted text. @@ -2512,8 +2512,8 @@ int op_change(oparg_T *oap) } else { vpos.coladd = 0; } - oldp = (char_u *)ml_get(linenr); - newp = xmalloc(STRLEN(oldp) + (size_t)vpos.coladd + oldp = ml_get(linenr); + newp = xmalloc(strlen(oldp) + (size_t)vpos.coladd + (size_t)ins_len + 1); // copy up to block start memmove(newp, oldp, (size_t)bd.textcol); @@ -2604,7 +2604,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) MotionType yank_type = oap->motion_type; size_t yanklines = (size_t)oap->line_count; linenr_T yankendlnum = oap->end.lnum; - char_u *p; + char *p; char_u *pnew; struct block_def bd; @@ -2663,7 +2663,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) colnr_T startcol = 0, endcol = MAXCOL; int is_oneChar = false; colnr_T cs, ce; - p = (char_u *)ml_get(lnum); + p = ml_get(lnum); bd.startspaces = 0; bd.endspaces = 0; @@ -2688,7 +2688,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) // Don't add space for double-wide // char; endcol will be on last byte // of multi-byte char. - && utf_head_off((char *)p, (char *)p + endcol) == 0)) { + && utf_head_off(p, p + endcol) == 0)) { if (oap->start.lnum == oap->end.lnum && oap->start.col == oap->end.col) { // Special case: inside a single char @@ -2705,7 +2705,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) } } if (endcol == MAXCOL) { - endcol = (colnr_T)STRLEN(p); + endcol = (colnr_T)strlen(p); } if (startcol > endcol || is_oneChar) { @@ -2713,7 +2713,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) } else { bd.textlen = endcol - startcol + oap->inclusive; } - bd.textstart = p + startcol; + bd.textstart = (char_u *)p + startcol; yank_copy_line(reg, &bd, y_idx, false); break; } @@ -3874,9 +3874,9 @@ void ex_display(exarg_T *eap) // display last used expression if (expr_line != NULL && (arg == NULL || vim_strchr((char *)arg, '=') != NULL) - && !got_int && !message_filtered((char *)expr_line)) { + && !got_int && !message_filtered(expr_line)) { msg_puts("\n c \"= "); - dis_msg((char *)expr_line, false); + dis_msg(expr_line, false); } } @@ -4452,12 +4452,12 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) { int col; char_u *buf1 = NULL; - char_u buf2[NUMBUFLEN]; + char buf2[NUMBUFLEN]; int pre; // 'X' or 'x': hex; '0': octal; 'B' or 'b': bin static bool hexupper = false; // 0xABC uvarnumber_T n; uvarnumber_T oldn; - char_u *ptr; + char *ptr; int c; int todel; int firstdigit; @@ -4484,10 +4484,10 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) } curwin->w_cursor = *pos; - ptr = (char_u *)ml_get(pos->lnum); + ptr = ml_get(pos->lnum); col = pos->col; - if (*ptr == NUL || col + !!save_coladd >= (int)STRLEN(ptr)) { + if (*ptr == NUL || col + !!save_coladd >= (int)strlen(ptr)) { goto theend; } @@ -4496,14 +4496,14 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) if (do_bin) { while (col > 0 && ascii_isbdigit(ptr[col])) { col--; - col -= utf_head_off((char *)ptr, (char *)ptr + col); + col -= utf_head_off(ptr, ptr + col); } } if (do_hex) { while (col > 0 && ascii_isxdigit(ptr[col])) { col--; - col -= utf_head_off((char *)ptr, (char *)ptr + col); + col -= utf_head_off(ptr, ptr + col); } } if (do_bin @@ -4511,7 +4511,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) && !((col > 0 && (ptr[col] == 'X' || ptr[col] == 'x') && ptr[col - 1] == '0' - && !utf_head_off((char *)ptr, (char *)ptr + col - 1) + && !utf_head_off(ptr, ptr + col - 1) && ascii_isxdigit(ptr[col + 1])))) { // In case of binary/hexadecimal pattern overlap match, rescan @@ -4519,7 +4519,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) while (col > 0 && ascii_isdigit(ptr[col])) { col--; - col -= utf_head_off((char *)ptr, (char *)ptr + col); + col -= utf_head_off(ptr, ptr + col); } } @@ -4527,17 +4527,17 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) && col > 0 && (ptr[col] == 'X' || ptr[col] == 'x') && ptr[col - 1] == '0' - && !utf_head_off((char *)ptr, (char *)ptr + col - 1) + && !utf_head_off(ptr, ptr + col - 1) && ascii_isxdigit(ptr[col + 1])) || (do_bin && col > 0 && (ptr[col] == 'B' || ptr[col] == 'b') && ptr[col - 1] == '0' - && !utf_head_off((char *)ptr, (char *)ptr + col - 1) + && !utf_head_off(ptr, ptr + col - 1) && ascii_isbdigit(ptr[col + 1]))) { // Found hexadecimal or binary number, move to its start. col--; - col -= utf_head_off((char *)ptr, (char *)ptr + col); + col -= utf_head_off(ptr, ptr + col); } else { // Search forward and then backward to find the start of number. col = pos->col; @@ -4559,7 +4559,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) if (visual) { while (ptr[col] != NUL && length > 0 && !ascii_isdigit(ptr[col]) && !(do_alpha && ASCII_ISALPHA(ptr[col]))) { - int mb_len = utfc_ptr2len((char *)ptr + col); + int mb_len = utfc_ptr2len(ptr + col); col += mb_len; length -= mb_len; @@ -4570,7 +4570,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) } if (col > pos->col && ptr[col - 1] == '-' - && !utf_head_off((char *)ptr, (char *)ptr + col - 1) + && !utf_head_off(ptr, ptr + col - 1) && !do_unsigned) { negative = true; was_positive = false; @@ -4578,7 +4578,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) } // If a number was found, and saving for undo works, replace the number. - firstdigit = ptr[col]; + firstdigit = (uint8_t)ptr[col]; if (!ascii_isdigit(firstdigit) && !(do_alpha && ASCII_ISALPHA(firstdigit))) { beep_flush(); goto theend; @@ -4616,7 +4616,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) curwin->w_cursor.col = col; } else { if (col > 0 && ptr[col - 1] == '-' - && !utf_head_off((char *)ptr, (char *)ptr + col - 1) + && !utf_head_off(ptr, ptr + col - 1) && !visual && !do_unsigned) { // negative number @@ -4627,11 +4627,11 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) // get the number value (unsigned) if (visual && VIsual_mode != 'V') { maxlen = (curbuf->b_visual.vi_curswant == MAXCOL - ? (int)STRLEN(ptr) - col + ? (int)strlen(ptr) - col : length); } - vim_str2nr((char *)ptr + col, &pre, &length, + vim_str2nr(ptr + col, &pre, &length, 0 + (do_bin ? STR2NR_BIN : 0) + (do_oct ? STR2NR_OCT : 0) + (do_hex ? STR2NR_HEX : 0), @@ -4723,7 +4723,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) // When there are many leading zeros it could be very long. // Allocate a bit too much. buf1 = xmalloc((size_t)length + NUMBUFLEN); - ptr = buf1; + ptr = (char *)buf1; if (negative && (!visual || was_positive)) { *ptr++ = '-'; } @@ -4732,7 +4732,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) length--; } if (pre == 'b' || pre == 'B' || pre == 'x' || pre == 'X') { - *ptr++ = (char_u)pre; + *ptr++ = (char)pre; length--; } @@ -4754,15 +4754,15 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) buf2[i] = '\0'; } else if (pre == 0) { - vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIu64, (uint64_t)n); + vim_snprintf(buf2, ARRAY_SIZE(buf2), "%" PRIu64, (uint64_t)n); } else if (pre == '0') { - vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIo64, (uint64_t)n); + vim_snprintf(buf2, ARRAY_SIZE(buf2), "%" PRIo64, (uint64_t)n); } else if (hexupper) { - vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIX64, (uint64_t)n); + vim_snprintf(buf2, ARRAY_SIZE(buf2), "%" PRIX64, (uint64_t)n); } else { - vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIx64, (uint64_t)n); + vim_snprintf(buf2, ARRAY_SIZE(buf2), "%" PRIx64, (uint64_t)n); } - length -= (int)STRLEN(buf2); + length -= (int)strlen(buf2); // Adjust number of zeros to the new number of digits, so the // total length of the number remains the same. @@ -5032,7 +5032,7 @@ void write_reg_contents_lst(int name, char **strings, bool must_append, MotionTy return; } - str_to_reg(reg, yank_type, (char *)strings, STRLEN(strings), + str_to_reg(reg, yank_type, (char *)strings, strlen((char *)strings), block_len, true); finish_write_reg(name, reg, old_y_previous); } @@ -5096,7 +5096,7 @@ void write_reg_contents_ex(int name, const char *str, ssize_t len, bool must_app if (must_append && expr_line) { // append has been specified and expr_line already exists, so we'll // append the new string to expr_line. - size_t exprlen = STRLEN(expr_line); + size_t exprlen = strlen(expr_line); totlen += exprlen; offset = exprlen; @@ -5186,8 +5186,8 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char *str, // Find the end of each line and save it into the array. if (str_list) { - for (char_u **ss = (char_u **)str; *ss != NULL; ss++, lnum++) { - size_t ss_len = STRLEN(*ss); + for (char **ss = (char **)str; *ss != NULL; ss++, lnum++) { + size_t ss_len = strlen(*ss); pp[lnum] = xmemdupz(*ss, ss_len); if (ss_len > maxlen) { maxlen = ss_len; @@ -5294,7 +5294,7 @@ static varnumber_T line_count_info(char_u *line, varnumber_T *wc, varnumber_T *c /// @param dict when not NULL, store the info there instead of showing it. void cursor_pos_info(dict_T *dict) { - char_u *p; + char *p; char_u buf1[50]; char_u buf2[40]; linenr_T lnum; @@ -5378,7 +5378,7 @@ void cursor_pos_info(dict_T *dict) // Do extra processing for VIsual mode. if (l_VIsual_active && lnum >= min_pos.lnum && lnum <= max_pos.lnum) { - char_u *s = NULL; + char *s = NULL; long len = 0L; switch (l_VIsual_mode) { @@ -5386,11 +5386,11 @@ void cursor_pos_info(dict_T *dict) virtual_op = virtual_active(); block_prep(&oparg, &bd, lnum, false); virtual_op = kNone; - s = bd.textstart; + s = (char *)bd.textstart; len = (long)bd.textlen; break; case 'V': - s = (char_u *)ml_get(lnum); + s = ml_get(lnum); len = MAXCOL; break; case 'v': { @@ -5399,18 +5399,18 @@ void cursor_pos_info(dict_T *dict) colnr_T end_col = (lnum == max_pos.lnum) ? max_pos.col - start_col + 1 : MAXCOL; - s = (char_u *)ml_get(lnum) + start_col; + s = ml_get(lnum) + start_col; len = end_col; } break; } if (s != NULL) { - byte_count_cursor += line_count_info(s, &word_count_cursor, + byte_count_cursor += line_count_info((char_u *)s, &word_count_cursor, &char_count_cursor, len, eol_size); if (lnum == curbuf->b_ml.ml_line_count && !curbuf->b_p_eol && (curbuf->b_p_bin || !curbuf->b_p_fixeol) - && (long)STRLEN(s) < len) { + && (long)strlen(s) < len) { byte_count_cursor -= eol_size; } } @@ -5471,11 +5471,11 @@ void cursor_pos_info(dict_T *dict) (int64_t)byte_count_cursor, (int64_t)byte_count); } } else { - p = (char_u *)get_cursor_line_ptr(); + p = get_cursor_line_ptr(); validate_virtcol(); col_print((char *)buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1); - col_print((char *)buf2, sizeof(buf2), (int)STRLEN(p), linetabsize(p)); + col_print((char *)buf2, sizeof(buf2), (int)strlen(p), linetabsize((char_u *)p)); if (char_count_cursor == byte_count_cursor && char_count == byte_count) { @@ -5512,14 +5512,14 @@ void cursor_pos_info(dict_T *dict) } if (dict == NULL) { // Don't shorten this message, the user asked for it. - p = (char_u *)p_shm; + p = p_shm; p_shm = ""; if (p_ch < 1) { msg_start(); msg_scroll = true; } msg((char *)IObuff); - p_shm = (char *)p; + p_shm = p; } } @@ -6554,8 +6554,8 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) if (TV_LIST_ITEM_TV(tv_list_last(res))->v_type != VAR_STRING) { goto err; } - char_u *regtype = (char_u *)TV_LIST_ITEM_TV(tv_list_last(res))->vval.v_string; - if (regtype == NULL || STRLEN(regtype) > 1) { + char *regtype = TV_LIST_ITEM_TV(tv_list_last(res))->vval.v_string; + if (regtype == NULL || strlen(regtype) > 1) { goto err; } switch (regtype[0]) { diff --git a/src/nvim/option.c b/src/nvim/option.c index 11a92f7d56..7202156c46 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -219,7 +219,7 @@ void set_init_1(bool clean_arg) xstrlcpy(item, p, len); add_pathsep(item); xstrlcat(item, "*", len); - if (find_dup_item(ga.ga_data, (char_u *)item, options[opt_idx].flags) + if (find_dup_item(ga.ga_data, item, options[opt_idx].flags) == NULL) { ga_grow(&ga, (int)len); if (!GA_EMPTY(&ga)) { @@ -242,15 +242,15 @@ void set_init_1(bool clean_arg) } { - char_u *cdpath; - char_u *buf; + char *cdpath; + char *buf; int i; int j; // Initialize the 'cdpath' option's default value. - cdpath = (char_u *)vim_getenv("CDPATH"); + cdpath = vim_getenv("CDPATH"); if (cdpath != NULL) { - buf = xmalloc(2 * STRLEN(cdpath) + 2); + buf = xmalloc(2 * strlen(cdpath) + 2); { buf[0] = ','; // start with ",", current dir first j = 1; @@ -267,7 +267,7 @@ void set_init_1(bool clean_arg) buf[j] = NUL; opt_idx = findoption("cdpath"); if (opt_idx >= 0) { - options[opt_idx].def_val = (char *)buf; + options[opt_idx].def_val = buf; options[opt_idx].flags |= P_DEF_ALLOCED; } else { xfree(buf); // cannot happen @@ -515,9 +515,9 @@ static void set_string_default(const char *name, char *val, bool allocated) } } -// For an option value that contains comma separated items, find "newval" in -// "origval". Return NULL if not found. -static char_u *find_dup_item(char_u *origval, const char_u *newval, uint32_t flags) +/// For an option value that contains comma separated items, find "newval" in +/// "origval". Return NULL if not found. +static char *find_dup_item(char *origval, const char *newval, uint32_t flags) FUNC_ATTR_NONNULL_ARG(2) { int bs = 0; @@ -526,8 +526,8 @@ static char_u *find_dup_item(char_u *origval, const char_u *newval, uint32_t fla return NULL; } - const size_t newlen = STRLEN(newval); - for (char_u *s = origval; *s != NUL; s++) { + const size_t newlen = strlen(newval); + for (char *s = origval; *s != NUL; s++) { if ((!(flags & P_COMMA) || s == origval || (s[-1] == ',' && !(bs & 1))) && STRNCMP(s, newval, newlen) == 0 && (!(flags & P_COMMA) || s[newlen] == ',' || s[newlen] == NUL)) { @@ -948,7 +948,7 @@ static int do_set_string(int opt_idx, int opt_flags, char **argp, int nextchar, int len = 0; if (op == OP_REMOVING || (flags & P_NODUP)) { len = (int)STRLEN(newval); - s = (char *)find_dup_item(origval, (char_u *)newval, flags); + s = find_dup_item((char *)origval, newval, flags); // do not add if already there if ((op == OP_ADDING || op == OP_PREPENDING) && s != NULL) { @@ -1182,7 +1182,7 @@ int do_set(char *arg, int opt_flags) } len++; if (opt_idx == -1) { - key = find_key_option((char_u *)arg + 1, true); + key = find_key_option(arg + 1, true); } } else { len = 0; @@ -1196,7 +1196,7 @@ int do_set(char *arg, int opt_flags) } opt_idx = findoption_len((const char *)arg, (size_t)len); if (opt_idx == -1) { - key = find_key_option((char_u *)arg, false); + key = find_key_option(arg, false); } } @@ -1543,7 +1543,7 @@ void did_set_option(int opt_idx, int opt_flags, int new_value, int value_checked int string_to_key(char_u *arg) { if (*arg == '<') { - return find_key_option(arg + 1, true); + return find_key_option((char *)arg + 1, true); } if (*arg == '^') { return CTRL_CHR(arg[1]); @@ -3151,9 +3151,9 @@ int find_key_option_len(const char_u *arg_arg, size_t len, bool has_lt) return key; } -static int find_key_option(const char_u *arg, bool has_lt) +static int find_key_option(const char *arg, bool has_lt) { - return find_key_option_len(arg, STRLEN(arg), has_lt); + return find_key_option_len((char_u *)arg, strlen(arg), has_lt); } /// if 'all' == 0: show changed options diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index e5bd065aef..196ab865a2 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -679,7 +679,7 @@ EXTERN unsigned ssop_flags; EXTERN char *p_sh; // 'shell' EXTERN char_u *p_shcf; // 'shellcmdflag' EXTERN char *p_sp; // 'shellpipe' -EXTERN char_u *p_shq; // 'shellquote' +EXTERN char *p_shq; // 'shellquote' EXTERN char *p_sxq; // 'shellxquote' EXTERN char_u *p_sxe; // 'shellxescape' EXTERN char *p_srr; // 'shellredir' @@ -775,7 +775,7 @@ EXTERN char *p_shada; ///< 'shada' EXTERN char *p_shadafile; ///< 'shadafile' EXTERN char *p_vsts; ///< 'varsofttabstop' EXTERN char *p_vts; ///< 'vartabstop' -EXTERN char_u *p_vdir; ///< 'viewdir' +EXTERN char *p_vdir; ///< 'viewdir' EXTERN char *p_vop; ///< 'viewoptions' EXTERN unsigned vop_flags; ///< uses SSOP_ flags EXTERN int p_vb; ///< 'visualbell' diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index a65635cf5e..2d8100595f 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3983,7 +3983,7 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfli } if (ml_append_buf(buf, lnum, (char_u *)IObuff, - (colnr_T)STRLEN(IObuff) + 1, false) == FAIL) { + (colnr_T)strlen(IObuff) + 1, false) == FAIL) { return FAIL; } return OK; @@ -4239,7 +4239,7 @@ static char *make_get_fullcmd(const char *makecmd, const char *fname) len += strlen(p_sp) + strlen(fname) + 3; } char *const cmd = xmalloc(len); - snprintf(cmd, len, "%s%s%s", (char *)p_shq, (char *)makecmd, (char *)p_shq); + snprintf(cmd, len, "%s%s%s", p_shq, (char *)makecmd, p_shq); // If 'shellpipe' empty: don't redirect to 'errorfile'. if (*p_sp != NUL) { @@ -5284,7 +5284,7 @@ static bool existing_swapfile(const buf_T *buf) { if (buf->b_ml.ml_mfp != NULL && buf->b_ml.ml_mfp->mf_fname != NULL) { const char *const fname = buf->b_ml.ml_mfp->mf_fname; - const size_t len = STRLEN(fname); + const size_t len = strlen(fname); return fname[len - 1] != 'p' || fname[len - 2] != 'w'; } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 90011eb2c7..cd1d6553cb 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -237,12 +237,12 @@ size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_T lnum) /// Mirror text "str" for right-left displaying. /// Only works for single-byte characters (e.g., numbers). -void rl_mirror(char_u *str) +void rl_mirror(char *str) { - char_u *p1, *p2; - char_u t; + char *p1, *p2; + char t; - for (p1 = str, p2 = str + STRLEN(str) - 1; p1 < p2; p1++, p2--) { + for (p1 = str, p2 = str + strlen(str) - 1; p1 < p2; p1++, p2--) { t = *p1; *p1 = *p2; *p2 = t; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 69c2b027bc..adfbbb9573 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -344,7 +344,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou // Count the word in the first language where it's found to be OK. if (count_word && mi.mi_result == SP_OK) { - count_common_word(mi.mi_lp->lp_slang, ptr, + count_common_word(mi.mi_lp->lp_slang, (char *)ptr, (int)(mi.mi_end - ptr), 1); count_word = false; } @@ -911,12 +911,12 @@ static void find_word(matchinf_T *mip, int mode) bool match_checkcompoundpattern(char_u *ptr, int wlen, garray_T *gap) { for (int i = 0; i + 1 < gap->ga_len; i += 2) { - char_u *p = ((char_u **)gap->ga_data)[i + 1]; - if (STRNCMP(ptr + wlen, p, STRLEN(p)) == 0) { + char *p = ((char **)gap->ga_data)[i + 1]; + if (STRNCMP(ptr + wlen, p, strlen(p)) == 0) { // Second part matches at start of following compound word, now // check if first part matches at end of previous word. - p = ((char_u **)gap->ga_data)[i]; - int len = (int)STRLEN(p); + p = ((char **)gap->ga_data)[i]; + int len = (int)strlen(p); if (len <= wlen && STRNCMP(ptr + wlen - len, p, len) == 0) { return true; } @@ -1236,7 +1236,7 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att size_t len; int has_syntax = syntax_present(wp); colnr_T col; - char_u *buf = NULL; + char *buf = NULL; size_t buflen = 0; int skip = 0; colnr_T capcol = -1; @@ -1275,9 +1275,9 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att decor_spell_nav_start(wp); while (!got_int) { - char_u *line = (char_u *)ml_get_buf(wp->w_buffer, lnum, false); + char *line = ml_get_buf(wp->w_buffer, lnum, false); - len = STRLEN(line); + len = strlen(line); if (buflen < len + MAXWLEN + 2) { xfree(buf); buflen = len + MAXWLEN + 2; @@ -1292,17 +1292,17 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att // For checking first word with a capital skip white space. if (capcol == 0) { - capcol = (colnr_T)getwhitecols((char *)line); + capcol = (colnr_T)getwhitecols(line); } else if (curline && wp == curwin) { // For spellbadword(): check if first word needs a capital. - col = (colnr_T)getwhitecols((char *)line); + col = (colnr_T)getwhitecols(line); if (check_need_cap(lnum, col)) { capcol = col; } // Need to get the line again, may have looked at the previous // one. - line = (char_u *)ml_get_buf(wp->w_buffer, lnum, false); + line = ml_get_buf(wp->w_buffer, lnum, false); } // Copy the line into "buf" and append the start of the next line if @@ -1311,12 +1311,12 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att bool empty_line = *skipwhite((const char *)line) == NUL; STRCPY(buf, line); if (lnum < wp->w_buffer->b_ml.ml_line_count) { - spell_cat_line(buf + STRLEN(buf), + spell_cat_line((char_u *)buf + strlen(buf), (char_u *)ml_get_buf(wp->w_buffer, lnum + 1, false), MAXWLEN); } - char_u *p = buf + skip; - char_u *endp = buf + len; + char *p = buf + skip; + char *endp = buf + len; while (p < endp) { // When searching backward don't search after the cursor. Unless // we wrapped around the end of the buffer. @@ -1329,7 +1329,7 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att // start of word attr = HLF_COUNT; - len = spell_check(wp, p, &attr, &capcol, false); + len = spell_check(wp, (char_u *)p, &attr, &capcol, false); if (attr != HLF_COUNT) { // We found a bad word. Check the attribute. @@ -1565,7 +1565,7 @@ static void spell_load_lang(char_u *lang) // use "latin1" for "latin9". And limit to 60 characters (just in case). char_u *spell_enc(void) { - if (STRLEN(p_enc) < 60 && strcmp(p_enc, "iso-8859-15") != 0) { + if (strlen(p_enc) < 60 && strcmp(p_enc, "iso-8859-15") != 0) { return (char_u *)p_enc; } return (char_u *)"latin1"; @@ -1720,10 +1720,10 @@ static void spell_load_cb(char *fname, void *cookie) /// @param[in] word added to common words hashtable /// @param[in] len length of word or -1 for NUL terminated /// @param[in] count 1 to count once, 10 to init -void count_common_word(slang_T *lp, char_u *word, int len, uint8_t count) +void count_common_word(slang_T *lp, char *word, int len, uint8_t count) { - char_u buf[MAXWLEN]; - char_u *p; + char buf[MAXWLEN]; + char *p; if (len == -1) { p = word; @@ -1735,8 +1735,8 @@ void count_common_word(slang_T *lp, char_u *word, int len, uint8_t count) } wordcount_T *wc; - hash_T hash = hash_hash(p); - const size_t p_len = STRLEN(p); + hash_T hash = hash_hash((char_u *)p); + const size_t p_len = strlen(p); hashitem_T *hi = hash_lookup(&lp->sl_wordcount, (const char *)p, p_len, hash); if (HASHITEM_EMPTY(hi)) { wc = xmalloc(sizeof(wordcount_T) + p_len); @@ -1769,17 +1769,17 @@ bool byte_in_str(char_u *str, int n) int init_syl_tab(slang_T *slang) { ga_init(&slang->sl_syl_items, sizeof(syl_item_T), 4); - char_u *p = (char_u *)vim_strchr((char *)slang->sl_syllable, '/'); + char *p = vim_strchr((char *)slang->sl_syllable, '/'); while (p != NULL) { *p++ = NUL; if (*p == NUL) { // trailing slash break; } - char_u *s = p; - p = (char_u *)vim_strchr((char *)p, '/'); + char *s = p; + p = vim_strchr(p, '/'); int l; if (p == NULL) { - l = (int)STRLEN(s); + l = (int)strlen(s); } else { l = (int)(p - s); } @@ -2518,23 +2518,23 @@ bool check_need_cap(linenr_T lnum, colnr_T col) return false; } - char_u *line = (char_u *)get_cursor_line_ptr(); - char_u *line_copy = NULL; + char *line = get_cursor_line_ptr(); + char *line_copy = NULL; colnr_T endcol = 0; - if (getwhitecols((char *)line) >= (int)col) { + if (getwhitecols(line) >= (int)col) { // At start of line, check if previous line is empty or sentence // ends there. if (lnum == 1) { need_cap = true; } else { - line = (char_u *)ml_get(lnum - 1); - if (*skipwhite((char *)line) == NUL) { + line = ml_get(lnum - 1); + if (*skipwhite(line) == NUL) { need_cap = true; } else { // Append a space in place of the line break. - line_copy = (char_u *)concat_str((char *)line, " "); + line_copy = concat_str(line, " "); line = line_copy; - endcol = (colnr_T)STRLEN(line); + endcol = (colnr_T)strlen(line); } } } else { @@ -2547,14 +2547,14 @@ bool check_need_cap(linenr_T lnum, colnr_T col) .regprog = curwin->w_s->b_cap_prog, .rm_ic = false }; - char_u *p = line + endcol; + char *p = line + endcol; for (;;) { MB_PTR_BACK(line, p); - if (p == line || spell_iswordp_nmw(p, curwin)) { + if (p == line || spell_iswordp_nmw((char_u *)p, curwin)) { break; } - if (vim_regexec(®match, (char *)p, 0) - && (char_u *)regmatch.endp[0] == line + endcol) { + if (vim_regexec(®match, p, 0) + && regmatch.endp[0] == line + endcol) { need_cap = true; break; } @@ -2596,7 +2596,7 @@ void ex_spellrepall(exarg_T *eap) // Only replace when the right word isn't there yet. This happens // when changing "etc" to "etc.". - char_u *line = (char_u *)get_cursor_line_ptr(); + char *line = get_cursor_line_ptr(); if (addlen <= 0 || STRNCMP(line + curwin->w_cursor.col, repl_to, strlen(repl_to)) != 0) { char_u *p = xmalloc(STRLEN(line) + (size_t)addlen + 1); diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 611c43e85e..d7a3efda83 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -1314,7 +1314,7 @@ static int read_words_section(FILE *fd, slang_T *lp, int len) } // Init the count to 10. - count_common_word(lp, word, -1, 10); + count_common_word(lp, (char *)word, -1, 10); done += i + 1; } return 0; diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c index f2a0da188e..2ff41d5157 100644 --- a/src/nvim/spellsuggest.c +++ b/src/nvim/spellsuggest.c @@ -533,7 +533,7 @@ void spell_suggest(int count) } vim_snprintf((char *)IObuff, IOSIZE, "%2d", i + 1); if (cmdmsg_rl) { - rl_mirror((char_u *)IObuff); + rl_mirror(IObuff); } msg_puts((const char *)IObuff); @@ -559,7 +559,7 @@ void spell_suggest(int count) } if (cmdmsg_rl) { // Mirror the numbers, but keep the leading space. - rl_mirror((char_u *)IObuff + 1); + rl_mirror(IObuff + 1); } msg_advance(30); msg_puts((const char *)IObuff); diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index 33c5c3a347..c4329fd84d 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -43,7 +43,7 @@ void win_redr_status(win_T *wp) { int row; int col; - char_u *p; + char *p; int len; int fillchar; int attr; @@ -77,8 +77,8 @@ void win_redr_status(win_T *wp) width = is_stl_global ? Columns : wp->w_width; get_trans_bufname(wp->w_buffer); - p = (char_u *)NameBuff; - len = (int)STRLEN(p); + p = NameBuff; + len = (int)strlen(p); if ((bt_help(wp->w_buffer) || wp->w_p_pvw @@ -88,19 +88,19 @@ void win_redr_status(win_T *wp) *(p + len++) = ' '; } if (bt_help(wp->w_buffer)) { - snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[Help]")); - len += (int)STRLEN(p + len); + snprintf(p + len, MAXPATHL - (size_t)len, "%s", _("[Help]")); + len += (int)strlen(p + len); } if (wp->w_p_pvw) { - snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[Preview]")); - len += (int)STRLEN(p + len); + snprintf(p + len, MAXPATHL - (size_t)len, "%s", _("[Preview]")); + len += (int)strlen(p + len); } if (bufIsChanged(wp->w_buffer)) { - snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", "[+]"); - len += (int)STRLEN(p + len); + snprintf(p + len, MAXPATHL - (size_t)len, "%s", "[+]"); + len += (int)strlen(p + len); } if (wp->w_buffer->b_p_ro) { - snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[RO]")); + snprintf(p + len, MAXPATHL - (size_t)len, "%s", _("[RO]")); // len += (int)strlen(p + len); // dead assignment } @@ -109,19 +109,19 @@ void win_redr_status(win_T *wp) this_ru_col = (width + 1) / 2; } if (this_ru_col <= 1) { - p = (char_u *)"<"; // No room for file name! + p = "<"; // No room for file name! len = 1; } else { int clen = 0, i; // Count total number of display cells. - clen = (int)mb_string2cells((char *)p); + clen = (int)mb_string2cells(p); // Find first character that will fit. // Going from start to end is much faster for DBCS. for (i = 0; p[i] != NUL && clen >= this_ru_col - 1; - i += utfc_ptr2len((char *)p + i)) { - clen -= utf_ptr2cells((char *)p + i); + i += utfc_ptr2len(p + i)) { + clen -= utf_ptr2cells(p + i); } len = clen; if (i > 0) { @@ -133,7 +133,7 @@ void win_redr_status(win_T *wp) row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp); col = is_stl_global ? 0 : wp->w_wincol; - grid_puts(&default_grid, (char *)p, row, col, attr); + grid_puts(&default_grid, p, row, col, attr); grid_fill(&default_grid, row, row + 1, len + col, this_ru_col + col, fillchar, fillchar, attr); diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index ea78397d8c..527cca05f5 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -504,7 +504,7 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) bool had_sync_point; stateitem_T *cur_si; synpat_T *spp; - char_u *line; + char *line; int found_flags = 0; int found_match_idx = 0; linenr_T found_current_lnum = 0; @@ -554,8 +554,8 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) // Skip lines that end in a backslash. for (; start_lnum > 1; start_lnum--) { - line = (char_u *)ml_get(start_lnum - 1); - if (*line == NUL || *(line + STRLEN(line) - 1) != '\\') { + line = ml_get(start_lnum - 1); + if (*line == NUL || *(line + strlen(line) - 1) != '\\') { break; } } @@ -2366,7 +2366,7 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_ regmmatch_T regmatch; regmmatch_T best_regmatch; // startpos/endpos of best match lpos_T pos; - char_u *line; + char *line; bool had_match = false; char_u buf_chartab[32]; // chartab array for syn option iskeyword @@ -2471,8 +2471,8 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_ break; } - line = (char_u *)ml_get_buf(syn_buf, startpos->lnum, false); - int line_len = (int)STRLEN(line); + line = ml_get_buf(syn_buf, startpos->lnum, false); + int line_len = (int)strlen(line); // take care of an empty match or negative offset if (pos.col <= matchcol) { diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 2ac3fed8b7..7c465ac909 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -58,7 +58,7 @@ typedef struct tag_pointers { char_u *command; // first char of command // filled in by parse_match(): char_u *command_end; // first char after command - char_u *tag_fname; // file name of the tags file. This is used + char *tag_fname; // file name of the tags file. This is used // when 'tr' is set. char_u *tagkind; // "kind:" value char_u *tagkind_end; // end of tagkind @@ -1212,30 +1212,30 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl char *const mfp = name_only ? xstrdup(res_name) : xmalloc(len + 2); if (!name_only) { - char_u *p = (char_u *)mfp; + char *p = mfp; *p++ = MT_GL_OTH + 1; // mtt *p++ = TAG_SEP; // no tag file name STRCPY(p, res_name); - p += STRLEN(p); + p += strlen(p); *p++ = TAB; STRCPY(p, res_fname); - p += STRLEN(p); + p += strlen(p); *p++ = TAB; STRCPY(p, res_cmd); - p += STRLEN(p); + p += strlen(p); if (has_extra) { STRCPY(p, ";\""); - p += STRLEN(p); + p += strlen(p); if (res_kind) { *p++ = TAB; STRCPY(p, res_kind); - p += STRLEN(p); + p += strlen(p); } TV_DICT_ITER(TV_LIST_ITEM_TV(li)->vval.v_dict, di, { @@ -1260,11 +1260,11 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl *p++ = TAB; STRCPY(p, dict_key); - p += STRLEN(p); + p += strlen(p); STRCPY(p, ":"); - p += STRLEN(p); + p += strlen(p); STRCPY(p, tv->vval.v_string); - p += STRLEN(p); + p += strlen(p); }); } } @@ -1316,9 +1316,9 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc char *buf_ffname) { FILE *fp; - char_u *lbuf; // line buffer + char *lbuf; // line buffer int lbuf_size = LSIZE; // length of lbuf - char_u *tag_fname; // name of tag file + char *tag_fname; // name of tag file tagname_T tn; // info for get_tagfname() int first_file; // trying first tag file tagptrs_T tagp; @@ -1328,7 +1328,7 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc int is_static; // current tag line is static int is_current; // file name matches bool eof = false; // found end-of-file - char_u *p; + char *p; char_u *s; int i; int tag_file_sorted = NUL; // !_TAG_FILE_SORTED value @@ -1617,7 +1617,7 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc // Adjust the search file offset to the correct position search_info.curr_offset_used = search_info.curr_offset; vim_fseek(fp, search_info.curr_offset, SEEK_SET); - eof = vim_fgets(lbuf, lbuf_size, fp); + eof = vim_fgets((char_u *)lbuf, lbuf_size, fp); if (!eof && search_info.curr_offset != 0) { // The explicit cast is to work around a bug in gcc 3.4.2 // (repeated below). @@ -1627,12 +1627,12 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc vim_fseek(fp, search_info.low_offset, SEEK_SET); search_info.curr_offset = search_info.low_offset; } - eof = vim_fgets(lbuf, lbuf_size, fp); + eof = vim_fgets((char_u *)lbuf, lbuf_size, fp); } // skip empty and blank lines - while (!eof && vim_isblankline((char *)lbuf)) { + while (!eof && vim_isblankline(lbuf)) { search_info.curr_offset = vim_ftell(fp); - eof = vim_fgets(lbuf, lbuf_size, fp); + eof = vim_fgets((char_u *)lbuf, lbuf_size, fp); } if (eof) { // Hit end of file. Skip backwards. @@ -1646,7 +1646,7 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc // skip empty and blank lines do { - eof = vim_fgets(lbuf, lbuf_size, fp); + eof = vim_fgets((char_u *)lbuf, lbuf_size, fp); } while (!eof && vim_isblankline((char *)lbuf)); if (eof) { @@ -1656,16 +1656,16 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc line_read_in: if (vimconv.vc_type != CONV_NONE) { - char_u *conv_line; + char *conv_line; int len; // Convert every line. Converting the pattern from 'enc' to // the tags file encoding doesn't work, because characters are // not recognized. - conv_line = (char_u *)string_convert(&vimconv, (char *)lbuf, NULL); + conv_line = string_convert(&vimconv, lbuf, NULL); if (conv_line != NULL) { // Copy or swap lbuf and conv_line. - len = (int)STRLEN(conv_line) + 1; + len = (int)strlen(conv_line) + 1; if (len > lbuf_size) { xfree(lbuf); lbuf = conv_line; @@ -1691,14 +1691,14 @@ line_read_in: // Read header line. if (STRNCMP(lbuf, "!_TAG_FILE_SORTED\t", 18) == 0) { - tag_file_sorted = lbuf[18]; + tag_file_sorted = (uint8_t)lbuf[18]; } if (STRNCMP(lbuf, "!_TAG_FILE_ENCODING\t", 20) == 0) { // Prepare to convert every line from the specified // encoding to 'encoding'. for (p = lbuf + 20; *p > ' ' && *p < 127; p++) {} *p = NUL; - convert_setup(&vimconv, (char *)lbuf + 20, p_enc); + convert_setup(&vimconv, lbuf + 20, p_enc); } // Read the next line. Unrecognized flags are ignored. @@ -1779,8 +1779,8 @@ parse_line: // This speeds up tag searching a lot! if (orgpat.headlen) { CLEAR_FIELD(tagp); - tagp.tagname = (char *)lbuf; - tagp.tagname_end = (char_u *)vim_strchr((char *)lbuf, TAB); + tagp.tagname = lbuf; + tagp.tagname_end = (char_u *)vim_strchr(lbuf, TAB); if (tagp.tagname_end == NULL) { // Corrupted tag line. line_error = true; @@ -1898,8 +1898,7 @@ parse_line: i = OK; } } else { - i = parse_tag_line(lbuf, - &tagp); + i = parse_tag_line((char_u *)lbuf, &tagp); } if (i == FAIL) { line_error = true; @@ -1992,11 +1991,11 @@ parse_line: len = (size_t)(tagp.tagname_end - (char_u *)tagp.tagname); mfp = xmalloc(sizeof(char) + len + 10 + ML_EXTRA + 1); - p = (char_u *)mfp; + p = mfp; STRCPY(p, tagp.tagname); p[len] = '@'; STRCPY(p + len + 1, help_lang); - snprintf((char *)p + len + 1 + ML_EXTRA, STRLEN(p) + len + 1 + ML_EXTRA, "%06d", + snprintf(p + len + 1 + ML_EXTRA, strlen(p) + len + 1 + ML_EXTRA, "%06d", help_heuristic(tagp.tagname, match_re ? matchoff : 0, !match_no_ic) + help_pri); @@ -2033,7 +2032,7 @@ parse_line: } } } else { - size_t tag_fname_len = STRLEN(tag_fname); + size_t tag_fname_len = strlen(tag_fname); // Save the tag in a buffer. // Use 0x02 to separate fields (Can't use NUL, because the // hash key is terminated by NUL). @@ -2041,10 +2040,10 @@ parse_line: // other tag: <0x02><0x02> // without Emacs tags: <0x02> // Here is the "mtt" value plus 1 to avoid NUL. - len = tag_fname_len + STRLEN(lbuf) + 3; + len = tag_fname_len + strlen(lbuf) + 3; mfp = xmalloc(sizeof(char) + len + 1); - p = (char_u *)mfp; - p[0] = (char_u)(mtt + 1); + p = mfp; + p[0] = (char)(mtt + 1); STRCPY(p + 1, tag_fname); #ifdef BACKSLASH_IN_FILENAME // Ignore differences in slashes, avoid adding @@ -2052,7 +2051,7 @@ parse_line: slash_adjust(p + 1); #endif p[tag_fname_len + 1] = TAG_SEP; - s = p + 1 + tag_fname_len + 1; + s = (char_u *)p + 1 + tag_fname_len + 1; STRCPY(s, lbuf); } @@ -2154,7 +2153,7 @@ findtag_end: *mfp = (char)(*mfp - 1); // change the TAG_SEP back to NUL - for (p = (char_u *)mfp + 1; *p != NUL; p++) { + for (p = mfp + 1; *p != NUL; p++) { if (*p == TAG_SEP) { *p = NUL; } @@ -2395,14 +2394,14 @@ static bool test_for_static(tagptrs_T *tagp) return false; } -// Returns the length of a matching tag line. -static size_t matching_line_len(const char_u *const lbuf) +/// @return the length of a matching tag line. +static size_t matching_line_len(const char *const lbuf) { - const char_u *p = lbuf + 1; + const char *p = lbuf + 1; // does the same thing as parse_match() - p += STRLEN(p) + 1; - return (size_t)(p - lbuf) + STRLEN(p); + p += strlen(p) + 1; + return (size_t)(p - lbuf) + strlen(p); } /// Parse a line from a matching tag. Does not change the line itself. @@ -2422,8 +2421,8 @@ static int parse_match(char *lbuf, tagptrs_T *tagp) char *p; char *pc, *pt; - tagp->tag_fname = (char_u *)lbuf + 1; - lbuf += STRLEN(tagp->tag_fname) + 2; + tagp->tag_fname = lbuf + 1; + lbuf += strlen(tagp->tag_fname) + 2; // Find search pattern and the file name for non-etags. retval = parse_tag_line((char_u *)lbuf, tagp); @@ -2494,7 +2493,7 @@ static char_u *tag_full_fname(tagptrs_T *tagp) int c = *tagp->fname_end; *tagp->fname_end = NUL; char_u *fullname = - (char_u *)expand_tag_fname((char *)tagp->fname, (char *)tagp->tag_fname, false); + (char_u *)expand_tag_fname((char *)tagp->fname, tagp->tag_fname, false); *tagp->fname_end = (char_u)c; return fullname; @@ -2526,7 +2525,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help) char_u *full_fname = NULL; const bool old_KeyTyped = KeyTyped; // getting the file may reset it const int l_g_do_tagpreview = g_do_tagpreview; - const size_t len = matching_line_len(lbuf_arg) + 1; + const size_t len = matching_line_len((char *)lbuf_arg) + 1; char_u *lbuf = xmalloc(len); memmove(lbuf, lbuf_arg, len); @@ -2563,7 +2562,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help) // Expand file name, when needed (for environment variables). // If 'tagrelative' option set, may change file name. - fname = expand_tag_fname(fname, (char *)tagp.tag_fname, true); + fname = expand_tag_fname(fname, tagp.tag_fname, true); tofree_fname = (char_u *)fname; // free() it later // Check if the file with the tag exists before abandoning the current diff --git a/src/nvim/textformat.c b/src/nvim/textformat.c index 25728ef0f1..61949fec6a 100644 --- a/src/nvim/textformat.c +++ b/src/nvim/textformat.c @@ -493,14 +493,14 @@ static int fmt_check_par(linenr_T lnum, int *leader_len, char_u **leader_flags, /// @return true if line "lnum" ends in a white character. static bool ends_in_white(linenr_T lnum) { - char_u *s = (char_u *)ml_get(lnum); + char *s = ml_get(lnum); size_t l; if (*s == NUL) { return false; } - l = STRLEN(s) - 1; - return ascii_iswhite(s[l]); + l = strlen(s) - 1; + return ascii_iswhite((uint8_t)s[l]); } /// @return true if the two comment leaders given are the same. diff --git a/src/nvim/textobject.c b/src/nvim/textobject.c index e6b330cbf1..dbe7110d60 100644 --- a/src/nvim/textobject.c +++ b/src/nvim/textobject.c @@ -213,13 +213,13 @@ bool findpar(bool *pincl, int dir, long count, int what, bool both) } curwin->w_cursor.lnum = curr; if (curr == curbuf->b_ml.ml_line_count && what != '}') { - char_u *line = (char_u *)ml_get(curr); + char *line = ml_get(curr); // Put the cursor on the last character in the last line and make the // motion inclusive. - if ((curwin->w_cursor.col = (colnr_T)STRLEN(line)) != 0) { + if ((curwin->w_cursor.col = (colnr_T)strlen(line)) != 0) { curwin->w_cursor.col--; - curwin->w_cursor.col -= utf_head_off((char *)line, (char *)line + curwin->w_cursor.col); + curwin->w_cursor.col -= utf_head_off(line, line + curwin->w_cursor.col); *pincl = true; } } else { diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 1a9066d7f1..ef8dc0775b 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -621,8 +621,8 @@ void u_compute_hash(buf_T *buf, char_u *hash) context_sha256_T ctx; sha256_start(&ctx); for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { - char_u *p = (char_u *)ml_get_buf(buf, lnum, false); - sha256_update(&ctx, p, (uint32_t)(STRLEN(p) + 1)); + char *p = ml_get_buf(buf, lnum, false); + sha256_update(&ctx, (char_u *)p, (uint32_t)(strlen(p) + 1)); } sha256_finish(&ctx, hash); } @@ -2636,7 +2636,7 @@ void ex_undolist(exarg_T *eap) if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark && uhp->uh_walk != mark) { vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7d ", uhp->uh_seq, changes); - undo_fmt_time((char_u *)IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff), uhp->uh_time); + undo_fmt_time((char_u *)IObuff + strlen(IObuff), IOSIZE - strlen(IObuff), uhp->uh_time); if (uhp->uh_save_nr > 0) { while (strlen(IObuff) < 33) { STRCAT(IObuff, " "); @@ -2991,13 +2991,13 @@ void u_undoline(void) return; } - char_u *oldp = u_save_line(curbuf->b_u_line_lnum); + char *oldp = (char *)u_save_line(curbuf->b_u_line_lnum); ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, true); changed_bytes(curbuf->b_u_line_lnum, 0); extmark_splice_cols(curbuf, (int)curbuf->b_u_line_lnum - 1, 0, (colnr_T)STRLEN(oldp), (colnr_T)strlen(curbuf->b_u_line_ptr), kExtmarkUndo); xfree(curbuf->b_u_line_ptr); - curbuf->b_u_line_ptr = (char *)oldp; + curbuf->b_u_line_ptr = oldp; colnr_T t = curbuf->b_u_line_colnr; if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum) { -- cgit From eaac0958256f2fb3b0fa9d20790bc38ed9eb3005 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Oct 2022 19:25:51 +0800 Subject: vim-patch:partial:9.0.0737: Lisp word only recognized when a space follows Problem: Lisp word only recognized when a space follows. Solution: Also match a word at the end of a line. Rename the test. Use a compiled function to avoid backslashes. https://github.com/vim/vim/commit/d26c5805bcbd630dab0478c2d22503a6e32a83c1 Keep the old Test_lisp_indent(). --- src/nvim/indent.c | 2 +- src/nvim/testdir/test_alot.vim | 1 - src/nvim/testdir/test_lispindent.vim | 103 +++++++++++++++++++++++++++++++++++ src/nvim/testdir/test_lispwords.vim | 98 --------------------------------- 4 files changed, 104 insertions(+), 100 deletions(-) create mode 100644 src/nvim/testdir/test_lispindent.vim delete mode 100644 src/nvim/testdir/test_lispwords.vim (limited to 'src/nvim') diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 0f7a5a8e44..74249777d6 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -1138,7 +1138,7 @@ static int lisp_match(char_u *p) (void)copy_option_part(&word, (char *)buf, LSIZE, ","); len = (int)STRLEN(buf); - if ((STRNCMP(buf, p, len) == 0) && (p[len] == ' ')) { + if ((STRNCMP(buf, p, len) == 0) && ascii_iswhite_or_nul(p[len])) { return true; } } diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index a83ef50abc..a3d240f27e 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -15,7 +15,6 @@ source test_fnamemodify.vim source test_ga.vim source test_glob2regpat.vim source test_global.vim -source test_lispwords.vim source test_move.vim source test_put.vim source test_reltime.vim diff --git a/src/nvim/testdir/test_lispindent.vim b/src/nvim/testdir/test_lispindent.vim new file mode 100644 index 0000000000..d4cab6d17e --- /dev/null +++ b/src/nvim/testdir/test_lispindent.vim @@ -0,0 +1,103 @@ +" Tests for 'lispwords' settings being global-local. +" And other lisp indent stuff. + +set nocompatible viminfo+=nviminfo + +func Test_global_local_lispwords() + setglobal lispwords=foo,bar,baz + setlocal lispwords-=foo | setlocal lispwords+=quux + call assert_equal('foo,bar,baz', &g:lispwords) + call assert_equal('bar,baz,quux', &l:lispwords) + call assert_equal('bar,baz,quux', &lispwords) + + setlocal lispwords< + call assert_equal('foo,bar,baz', &g:lispwords) + call assert_equal('foo,bar,baz', &l:lispwords) + call assert_equal('foo,bar,baz', &lispwords) +endfunc + +func Test_lisp_indent() + enew! + + call append(0, [ + \ '(defun html-file (base)', + \ '(format nil "~(~A~).html" base))', + \ '', + \ '(defmacro page (name title &rest body)', + \ '(let ((ti (gensym)))', + \ '`(with-open-file (*standard-output*', + \ '(html-file ,name)', + \ ':direction :output', + \ ':if-exists :supersede)', + \ '(let ((,ti ,title))', + \ '(as title ,ti)', + \ '(with center ', + \ '(as h2 (string-upcase ,ti)))', + \ '(brs 3)', + \ ',@body))))', + \ '', + \ ';;; Utilities for generating links', + \ '', + \ '(defmacro with-link (dest &rest body)', + \ '`(progn', + \ '(format t "" (html-file ,dest))', + \ ',@body', + \ '(princ "")))' + \ ]) + call assert_equal(7, lispindent(2)) + call assert_equal(5, 6->lispindent()) + call assert_equal(-1, lispindent(-1)) + + set lisp + set lispwords& + let save_copt = &cpoptions + set cpoptions+=p + normal 1G=G + + call assert_equal([ + \ '(defun html-file (base)', + \ ' (format nil "~(~A~).html" base))', + \ '', + \ '(defmacro page (name title &rest body)', + \ ' (let ((ti (gensym)))', + \ ' `(with-open-file (*standard-output*', + \ ' (html-file ,name)', + \ ' :direction :output', + \ ' :if-exists :supersede)', + \ ' (let ((,ti ,title))', + \ ' (as title ,ti)', + \ ' (with center ', + \ ' (as h2 (string-upcase ,ti)))', + \ ' (brs 3)', + \ ' ,@body))))', + \ '', + \ ';;; Utilities for generating links', + \ '', + \ '(defmacro with-link (dest &rest body)', + \ ' `(progn', + \ ' (format t "" (html-file ,dest))', + \ ' ,@body', + \ ' (princ "")))', + \ '' + \ ], getline(1, "$")) + + enew! + let &cpoptions=save_copt + set nolisp +endfunc + +func Test_lispindent_negative() + " in legacy script there is no error + call assert_equal(-1, lispindent(-1)) +endfunc + +func Test_lisp_indent_works() + " This was reading beyond the end of the line + new + exe "norm a\tü(\=" + set lisp + norm == + bwipe! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_lispwords.vim b/src/nvim/testdir/test_lispwords.vim deleted file mode 100644 index 4144fb0521..0000000000 --- a/src/nvim/testdir/test_lispwords.vim +++ /dev/null @@ -1,98 +0,0 @@ -" Tests for 'lispwords' settings being global-local. -" And other lisp indent stuff. - -set nocompatible viminfo+=nviminfo - -func Test_global_local_lispwords() - setglobal lispwords=foo,bar,baz - setlocal lispwords-=foo | setlocal lispwords+=quux - call assert_equal('foo,bar,baz', &g:lispwords) - call assert_equal('bar,baz,quux', &l:lispwords) - call assert_equal('bar,baz,quux', &lispwords) - - setlocal lispwords< - call assert_equal('foo,bar,baz', &g:lispwords) - call assert_equal('foo,bar,baz', &l:lispwords) - call assert_equal('foo,bar,baz', &lispwords) -endfunc - -func Test_lisp_indent() - enew! - - call append(0, [ - \ '(defun html-file (base)', - \ '(format nil "~(~A~).html" base))', - \ '', - \ '(defmacro page (name title &rest body)', - \ '(let ((ti (gensym)))', - \ '`(with-open-file (*standard-output*', - \ '(html-file ,name)', - \ ':direction :output', - \ ':if-exists :supersede)', - \ '(let ((,ti ,title))', - \ '(as title ,ti)', - \ '(with center ', - \ '(as h2 (string-upcase ,ti)))', - \ '(brs 3)', - \ ',@body))))', - \ '', - \ ';;; Utilities for generating links', - \ '', - \ '(defmacro with-link (dest &rest body)', - \ '`(progn', - \ '(format t "" (html-file ,dest))', - \ ',@body', - \ '(princ "")))' - \ ]) - call assert_equal(7, lispindent(2)) - call assert_equal(5, 6->lispindent()) - call assert_equal(-1, lispindent(-1)) - - set lisp - set lispwords& - let save_copt = &cpoptions - set cpoptions+=p - normal 1G=G - - call assert_equal([ - \ '(defun html-file (base)', - \ ' (format nil "~(~A~).html" base))', - \ '', - \ '(defmacro page (name title &rest body)', - \ ' (let ((ti (gensym)))', - \ ' `(with-open-file (*standard-output*', - \ ' (html-file ,name)', - \ ' :direction :output', - \ ' :if-exists :supersede)', - \ ' (let ((,ti ,title))', - \ ' (as title ,ti)', - \ ' (with center ', - \ ' (as h2 (string-upcase ,ti)))', - \ ' (brs 3)', - \ ' ,@body))))', - \ '', - \ ';;; Utilities for generating links', - \ '', - \ '(defmacro with-link (dest &rest body)', - \ ' `(progn', - \ ' (format t "" (html-file ,dest))', - \ ' ,@body', - \ ' (princ "")))', - \ '' - \ ], getline(1, "$")) - - enew! - let &cpoptions=save_copt - set nolisp -endfunc - -func Test_lisp_indent_works() - " This was reading beyond the end of the line - new - exe "norm a\tü(\=" - set lisp - norm == - bwipe! -endfunc - -" vim: shiftwidth=2 sts=2 expandtab -- cgit From 32ced1f08fd551770b4f4a0fd69dfe2d36c417b6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Oct 2022 19:42:38 +0800 Subject: vim-patch:9.0.0754: 'indentexpr' overrules lisp indenting in one situation Problem: 'indentexpr' overrules lisp indenting in one situation. Solution: Add "else" to keep the lisp indent. (issue vim/vim#11327) https://github.com/vim/vim/commit/a79b35b5781ae770334cec781d17fec3875f8108 --- src/nvim/change.c | 8 +++----- src/nvim/testdir/test_lispindent.vim | 11 +++++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/change.c b/src/nvim/change.c index c9e57ab88f..c6f9e9f5c2 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -1814,17 +1814,15 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) vreplace_mode = 0; } - // May do lisp indenting. if (!p_paste && leader == NULL && curbuf->b_p_lisp && curbuf->b_p_ai) { + // do lisp indenting fixthisline(get_lisp_indent); ai_col = (colnr_T)getwhitecols_curline(); - } - - // May do indenting after opening a new line. - if (do_cindent) { + } else if (do_cindent) { + // do 'cindent' or 'indentexpr' indenting do_c_expr_indent(); ai_col = (colnr_T)getwhitecols_curline(); } diff --git a/src/nvim/testdir/test_lispindent.vim b/src/nvim/testdir/test_lispindent.vim index d4cab6d17e..8987f67a80 100644 --- a/src/nvim/testdir/test_lispindent.vim +++ b/src/nvim/testdir/test_lispindent.vim @@ -91,6 +91,17 @@ func Test_lispindent_negative() call assert_equal(-1, lispindent(-1)) endfunc +func Test_lispindent_with_indentexpr() + enew + setl ai lisp nocin indentexpr=11 + exe "normal a(x\1\2)\" + let expected = ['(x', ' 1', ' 2)'] + call assert_equal(expected, getline(1, 3)) + normal 1G=G + call assert_equal(expected, getline(1, 3)) + bwipe! +endfunc + func Test_lisp_indent_works() " This was reading beyond the end of the line new -- cgit From 1ba9d63d77fd8a3e7560188f608f89444c0ac3b8 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sat, 15 Oct 2022 12:45:57 +0100 Subject: refactor(shada.c): clint (#20599) --- src/nvim/shada.c | 83 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 39 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 7580cc8897..e56febec9b 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -454,10 +454,7 @@ static const ShadaEntry sd_default_values[] = { .additional_data = NULL), DEF_SDE(Variable, global_var, .name = NULL, - .value = { - .v_type = VAR_UNKNOWN, - .vval = { .v_string = NULL } - }, + .value = { .v_type = VAR_UNKNOWN, .vval = { .v_string = NULL } }, .additional_elements = NULL), DEF_SDE(GlobalMark, filemark, .name = '"', @@ -1127,26 +1124,27 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) const bool get_old_files = (flags & (kShaDaGetOldfiles | kShaDaForceit) && (force || tv_list_len(oldfiles_list) == 0)); const bool want_marks = flags & kShaDaWantMarks; - const unsigned srni_flags = (unsigned)( - (flags & kShaDaWantInfo - ? (kSDReadUndisableableData - | kSDReadRegisters - | kSDReadGlobalMarks - | (p_hi ? kSDReadHistory : 0) - | (find_shada_parameter('!') != NULL - ? kSDReadVariables - : 0) - | (find_shada_parameter('%') != NULL - && ARGCOUNT == 0 - ? kSDReadBufferList - : 0)) - : 0) - | (want_marks && get_shada_parameter('\'') > 0 - ? kSDReadLocalMarks | kSDReadChanges - : 0) - | (get_old_files - ? kSDReadLocalMarks - : 0)); + const unsigned srni_flags = + (unsigned)( + (flags & kShaDaWantInfo + ? (kSDReadUndisableableData + | kSDReadRegisters + | kSDReadGlobalMarks + | (p_hi ? kSDReadHistory : 0) + | (find_shada_parameter('!') != NULL + ? kSDReadVariables + : 0) + | (find_shada_parameter('%') != NULL + && ARGCOUNT == 0 + ? kSDReadBufferList + : 0)) + : 0) + | (want_marks && get_shada_parameter('\'') > 0 + ? kSDReadLocalMarks | kSDReadChanges + : 0) + | (get_old_files + ? kSDReadLocalMarks + : 0)); if (srni_flags == 0) { // Nothing to do. return; @@ -1191,17 +1189,18 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) case kSDItemSearchPattern: if (!force) { SearchPattern pat; - (cur_entry.data.search_pattern.is_substitute_pattern - ? &get_substitute_pattern - : &get_search_pattern)(&pat); + if (cur_entry.data.search_pattern.is_substitute_pattern) { + get_substitute_pattern(&pat); + } else { + get_search_pattern(&pat); + } if (pat.pat != NULL && pat.timestamp >= cur_entry.timestamp) { shada_free_shada_entry(&cur_entry); break; } } - (cur_entry.data.search_pattern.is_substitute_pattern - ? &set_substitute_pattern - : &set_search_pattern)((SearchPattern) { + + SearchPattern spat = (SearchPattern) { .magic = cur_entry.data.search_pattern.magic, .no_scs = !cur_entry.data.search_pattern.smartcase, .off = { @@ -1213,7 +1212,14 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) .pat = (char_u *)cur_entry.data.search_pattern.pat, .additional_data = cur_entry.data.search_pattern.additional_data, .timestamp = cur_entry.timestamp, - }); + }; + + if (cur_entry.data.search_pattern.is_substitute_pattern) { + set_substitute_pattern(spat); + } else { + set_search_pattern(spat); + } + if (cur_entry.data.search_pattern.is_last_used) { set_last_used_pattern(cur_entry.data.search_pattern.is_substitute_pattern); set_no_hlsearch(!cur_entry.data.search_pattern.highlighted); @@ -1688,8 +1694,8 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntr msgpack_pack_long(spacker, entry.data.filemark.mark.col); } assert(entry.type == kSDItemJump || entry.type == kSDItemChange - ? CHECK_DEFAULT(entry, filemark.name) - : true); + ? CHECK_DEFAULT(entry, filemark.name) + : true); if (!CHECK_DEFAULT(entry, filemark.name)) { PACK_STATIC_STR(KEY_NAME_CHAR); msgpack_pack_uint8(spacker, (uint8_t)entry.data.filemark.name); @@ -1699,15 +1705,14 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntr break; } case kSDItemRegister: { - const size_t map_size = (size_t)( - 2 // Register contents and name + const size_t map_size = (size_t)(2 // Register contents and name + ONE_IF_NOT_DEFAULT(entry, reg.type) + ONE_IF_NOT_DEFAULT(entry, reg.width) + ONE_IF_NOT_DEFAULT(entry, reg.is_unnamed) // Additional entries, if any: + (size_t)(entry.data.reg.additional_data == NULL - ? 0 - : entry.data.reg.additional_data->dv_hashtab.ht_used)); + ? 0 + : entry.data.reg.additional_data->dv_hashtab.ht_used)); msgpack_pack_map(spacker, map_size); PACK_STATIC_STR(REG_KEY_CONTENTS); msgpack_pack_array(spacker, entry.data.reg.contents_size); @@ -2097,8 +2102,8 @@ static inline ShaDaWriteResult shada_read_when_writing(ShaDaReadDef *const sd_re break; case kSDItemSearchPattern: COMPARE_WITH_ENTRY((entry.data.search_pattern.is_substitute_pattern - ? &wms->sub_search_pattern - : &wms->search_pattern), entry); + ? &wms->sub_search_pattern + : &wms->search_pattern), entry); break; case kSDItemSubString: COMPARE_WITH_ENTRY(&wms->replacement, entry); -- cgit From 80161ec7d6edc8f5ae1fa21de81c43f2f01751c2 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sat, 15 Oct 2022 18:01:52 +0100 Subject: refactor(drawscreen.c): reduce scopes of locals (#20668) --- src/nvim/drawscreen.c | 101 +++++++++++++++++++++++--------------------------- 1 file changed, 47 insertions(+), 54 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index 4ece72a6d5..99f8c70799 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -185,14 +185,12 @@ void screenclear(void) { check_for_delay(false); - int i; - if (starting == NO_SCREEN || default_grid.chars == NULL) { return; } // blank out the default grid - for (i = 0; i < default_grid.rows; i++) { + for (int i = 0; i < default_grid.rows; i++) { grid_clear_line(&default_grid, default_grid.line_offset[i], default_grid.cols, true); default_grid.line_wraps[i] = false; @@ -804,12 +802,10 @@ static bool vsep_connected(win_T *wp, WindowCorner corner) /// Draw the vertical separator right of window "wp" static void draw_vsep_win(win_T *wp) { - int hl; - int c; - if (wp->w_vsep_width) { // draw the vertical separator right of this window - c = fillchar_vsep(wp, &hl); + int hl; + int c = fillchar_vsep(wp, &hl); grid_fill(&default_grid, wp->w_winrow, W_ENDROW(wp), W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl); } @@ -818,12 +814,10 @@ static void draw_vsep_win(win_T *wp) /// Draw the horizontal separator below window "wp" static void draw_hsep_win(win_T *wp) { - int hl; - int c; - if (wp->w_hsep_height) { // draw the horizontal separator below this window - c = fillchar_hsep(wp, &hl); + int hl; + int c = fillchar_hsep(wp, &hl); grid_fill(&default_grid, W_ENDROW(wp), W_ENDROW(wp) + 1, wp->w_wincol, W_ENDCOL(wp), c, c, hl); } @@ -930,8 +924,6 @@ static void win_update(win_T *wp, DecorProviders *providers) bool called_decor_providers = false; win_update_start: ; - buf_T *buf = wp->w_buffer; - int type; int top_end = 0; // Below last row of the top area that needs // updating. 0 when no top area updating. int mid_start = 999; // first row of the mid area that needs @@ -946,28 +938,20 @@ win_update_start: int bot_scroll_start = 999; // first line that needs to be redrawn due to // scrolling. only used for EOB - int row; // current window row to display - linenr_T lnum; // current buffer lnum to display - int idx; // current index in w_lines[] - int srow; // starting row of the current line - - bool eof = false; // if true, we hit the end of the file - bool didline = false; // if true, we finished the last line - int i; - long j; static bool recursive = false; // being called recursively - const linenr_T old_botline = wp->w_botline; + // Remember what happened to the previous line. -#define DID_NONE 1 // didn't update a line -#define DID_LINE 2 // updated a normal line -#define DID_FOLD 3 // updated a folded line - int did_update = DID_NONE; + enum { + DID_NONE = 1, // didn't update a line + DID_LINE = 2, // updated a normal line + DID_FOLD = 3, // updated a folded line + } did_update = DID_NONE; + linenr_T syntax_last_parsed = 0; // last parsed text line linenr_T mod_top = 0; linenr_T mod_bot = 0; - int save_got_int; - type = wp->w_redr_type; + int type = wp->w_redr_type; if (type >= UPD_NOT_VALID) { // TODO(bfredl): should only be implied for CLEAR, not NOT_VALID! @@ -998,12 +982,14 @@ win_update_start: init_search_hl(wp, &screen_search_hl); + buf_T *buf = wp->w_buffer; + // Force redraw when width of 'number' or 'relativenumber' column // changes. - i = (wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) : 0; - if (wp->w_nrwidth != i) { + int nrwidth = (wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) : 0; + if (wp->w_nrwidth != nrwidth) { type = UPD_NOT_VALID; - wp->w_nrwidth = i; + wp->w_nrwidth = nrwidth; if (buf->terminal) { terminal_check_size(buf->terminal); @@ -1075,7 +1061,7 @@ win_update_start: // to this line. If there is no valid entry, use MAXLNUM. lnumt = wp->w_topline; lnumb = MAXLNUM; - for (i = 0; i < wp->w_lines_valid; i++) { + for (int i = 0; i < wp->w_lines_valid; i++) { if (wp->w_lines[i].wl_valid) { if (wp->w_lines[i].wl_lastlnum < mod_top) { lnumt = wp->w_lines[i].wl_lastlnum + 1; @@ -1130,8 +1116,8 @@ win_update_start: // When only displaying the lines at the top, set top_end. Used when // window has scrolled down for msg_scrolled. if (type == UPD_REDRAW_TOP) { - j = 0; - for (i = 0; i < wp->w_lines_valid; i++) { + long j = 0; + for (int i = 0; i < wp->w_lines_valid; i++) { j += wp->w_lines[i].wl_size; if (j >= wp->w_upd_rows) { top_end = (int)j; @@ -1167,6 +1153,7 @@ win_update_start: || (wp->w_topline == wp->w_lines[0].wl_lnum && wp->w_topfill > wp->w_old_topfill))) { // New topline is above old topline: May scroll down. + long j; if (hasAnyFolding(wp)) { linenr_T ln; @@ -1184,7 +1171,7 @@ win_update_start: j = wp->w_lines[0].wl_lnum - wp->w_topline; } if (j < wp->w_grid.rows - 2) { // not too far off - i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1); + int i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1); // insert extra lines for previously invisible filler lines if (wp->w_lines[0].wl_lnum != wp->w_topline) { i += win_get_fill(wp, wp->w_lines[0].wl_lnum) - wp->w_old_topfill; @@ -1206,6 +1193,7 @@ win_update_start: if ((wp->w_lines_valid += (linenr_T)j) > wp->w_grid.rows) { wp->w_lines_valid = wp->w_grid.rows; } + int idx; for (idx = wp->w_lines_valid; idx - j >= 0; idx--) { wp->w_lines[idx] = wp->w_lines[idx - j]; } @@ -1225,9 +1213,9 @@ win_update_start: // needs updating. // try to find wp->w_topline in wp->w_lines[].wl_lnum - j = -1; - row = 0; - for (i = 0; i < wp->w_lines_valid; i++) { + long j = -1; + int row = 0; + for (int i = 0; i < wp->w_lines_valid; i++) { if (wp->w_lines[i].wl_valid && wp->w_lines[i].wl_lnum == wp->w_topline) { j = i; @@ -1263,7 +1251,7 @@ win_update_start: // upwards, to compensate for the deleted lines. Set // bot_start to the first row that needs redrawing. bot_start = 0; - idx = 0; + int idx = 0; for (;;) { wp->w_lines[idx] = wp->w_lines[j]; // stop at line that didn't fit, unless it is still @@ -1460,9 +1448,9 @@ win_update_start: // above the Visual area and reset wl_valid, do count these for // mid_end (in srow). if (mid_start > 0) { - lnum = wp->w_topline; - idx = 0; - srow = 0; + linenr_T lnum = wp->w_topline; + int idx = 0; + int srow = 0; if (scrolled_down) { mid_start = top_end; } else { @@ -1509,18 +1497,12 @@ win_update_start: } // reset got_int, otherwise regexp won't work - save_got_int = got_int; + int save_got_int = got_int; got_int = 0; // Set the time limit to 'redrawtime'. proftime_T syntax_tm = profile_setlimit(p_rdt); syn_set_timeout(&syntax_tm); - // Update all the window rows. - idx = 0; // first entry in w_lines[].wl_size - row = 0; - srow = 0; - lnum = wp->w_topline; // first line shown in window - win_extmark_arr.size = 0; decor_redraw_reset(buf, &decor_state); @@ -1540,6 +1522,14 @@ win_update_start: win_check_ns_hl(wp); + // Update all the window rows. + int idx = 0; // first entry in w_lines[].wl_size + int row = 0; // current window row to display + int srow = 0; // starting row of the current line + linenr_T lnum = wp->w_topline; // first line shown in window + + bool eof = false; // if true, we hit the end of the file + bool didline = false; // if true, we finished the last line for (;;) { // stop updating when reached the end of the window (check for _past_ // the end of the window is at the end of the loop) @@ -1603,6 +1593,7 @@ win_update_start: int new_rows = 0; int xtra_rows; linenr_T l; + int i; // Count the old number of window rows, using w_lines[], which // should still contain the sizes for the lines as they are @@ -1637,7 +1628,7 @@ win_update_start: } else { // Able to count old number of rows: Count new window // rows, and may insert/delete lines - j = idx; + long j = idx; for (l = lnum; l < mod_bot; l++) { if (hasFoldingWin(wp, l, NULL, &l, true, NULL)) { new_rows++; @@ -1830,6 +1821,8 @@ win_update_start: syntax_end_parsing(wp, syntax_last_parsed + 1); } + const linenr_T old_botline = wp->w_botline; + // If we didn't hit the end of the file, and we didn't finish the last // line we were working on, then the line didn't fit. wp->w_empty_rows = 0; @@ -1873,7 +1866,7 @@ win_update_start: } else { if (eof) { // we hit the end of the file wp->w_botline = buf->b_ml.ml_line_count + 1; - j = win_get_fill(wp, wp->w_botline); + long j = win_get_fill(wp, wp->w_botline); if (j > 0 && !wp->w_botfill && row < wp->w_grid.rows) { // Display filler text below last line. win_line() will check // for ml_line_count+1 and only draw filler lines @@ -1943,11 +1936,11 @@ win_update_start: update_topline(curwin); // may invalidate w_botline again if (must_redraw != 0) { // Don't update for changes in buffer again. - i = curbuf->b_mod_set; + int mod_set = curbuf->b_mod_set; curbuf->b_mod_set = false; win_update(curwin, providers); must_redraw = 0; - curbuf->b_mod_set = i; + curbuf->b_mod_set = mod_set; } recursive = false; } -- cgit From 2921de6a964e68bb0c9591c14b8550aee2e337da Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sat, 15 Oct 2022 10:53:30 +0100 Subject: fix(decoration): call providers in win_update() earlier Fixes #20651 --- src/nvim/drawscreen.c | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index 99f8c70799..a1582eac53 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -978,12 +978,33 @@ win_update_start: return; } + buf_T *buf = wp->w_buffer; + + // reset got_int, otherwise regexp won't work + int save_got_int = got_int; + got_int = 0; + // Set the time limit to 'redrawtime'. + proftime_T syntax_tm = profile_setlimit(p_rdt); + syn_set_timeout(&syntax_tm); + + win_extmark_arr.size = 0; + + decor_redraw_reset(buf, &decor_state); + + DecorProviders line_providers; + decor_providers_invoke_win(wp, providers, &line_providers, &provider_err); + if (must_redraw != 0) { + must_redraw = 0; + if (!called_decor_providers) { + called_decor_providers = true; + goto win_update_start; + } + } + redraw_win_signcol(wp); init_search_hl(wp, &screen_search_hl); - buf_T *buf = wp->w_buffer; - // Force redraw when width of 'number' or 'relativenumber' column // changes. int nrwidth = (wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) : 0; @@ -1496,28 +1517,6 @@ win_update_start: wp->w_old_visual_col = 0; } - // reset got_int, otherwise regexp won't work - int save_got_int = got_int; - got_int = 0; - // Set the time limit to 'redrawtime'. - proftime_T syntax_tm = profile_setlimit(p_rdt); - syn_set_timeout(&syntax_tm); - - win_extmark_arr.size = 0; - - decor_redraw_reset(buf, &decor_state); - - DecorProviders line_providers; - decor_providers_invoke_win(wp, providers, &line_providers, &provider_err); - (void)win_signcol_count(wp); // check if provider changed signcol width - if (must_redraw != 0) { - must_redraw = 0; - if (!called_decor_providers) { - called_decor_providers = true; - goto win_update_start; - } - } - bool cursorline_standout = win_cursorline_standout(wp); win_check_ns_hl(wp); -- cgit From bc798dfd8cea9a5f93461e05dcb8409b6d96afc0 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 16 Oct 2022 08:01:44 +0800 Subject: vim-patch:9.0.0765: with a Visual block a put command column may go negative (#20676) Problem: With a Visual block a put command column may go negative. Solution: Check that the column does not become negative. https://github.com/vim/vim/commit/36343ae0fb7247e060abfd35fb8e4337b33abb4b --- src/nvim/ops.c | 3 +++ src/nvim/testdir/test_visual.vim | 12 ++++++++++++ 2 files changed, 15 insertions(+) (limited to 'src/nvim') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 8b2fea9535..71e754537d 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3370,6 +3370,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) // adjust '] mark curbuf->b_op_end.lnum = curwin->w_cursor.lnum - 1; curbuf->b_op_end.col = bd.textcol + (colnr_T)totlen - 1; + if (curbuf->b_op_end.col < 0) { + curbuf->b_op_end.col = 0; + } curbuf->b_op_end.coladd = 0; if (flags & PUT_CURSEND) { colnr_T len; diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 65665d36c0..7fb34ec81f 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -474,6 +474,18 @@ func Test_visual_block_put() bw! endfunc +func Test_visual_block_put_invalid() + enew! + behave mswin + norm yy + norm v)Ps/^/ + " this was causing the column to become negative + silent norm ggv)P + + bwipe! + behave xterm +endfunc + " Visual modes (v V CTRL-V) followed by an operator; count; repeating func Test_visual_mode_op() new -- cgit From 19eb7054ff7b1fbc78e56e7f9ed6537b085147bc Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 16 Oct 2022 08:06:07 +0800 Subject: vim-patch:9.0.0761: cannot use 'indentexpr' for Lisp indenting Problem: Cannot use 'indentexpr' for Lisp indenting. Solution: Add the 'lispoptions' option. https://github.com/vim/vim/commit/49846fb1a31de99f49d6a7e70efe685197423c84 vim-patch:9.0.0762: build failure Problem: Build failure. Solution: Add missing change. https://github.com/vim/vim/commit/4b082c4bd05f504fda1acaa9d28fca55a2d04857 --- src/nvim/buffer.c | 3 ++- src/nvim/buffer_defs.h | 1 + src/nvim/change.c | 24 +++++++++++--------- src/nvim/edit.c | 28 ----------------------- src/nvim/edit.h | 2 -- src/nvim/indent.c | 43 ++++++++++++++++++++++++++++++++++++ src/nvim/indent.h | 2 ++ src/nvim/option.c | 4 ++++ src/nvim/option_defs.h | 2 ++ src/nvim/options.lua | 8 +++++++ src/nvim/optionstr.c | 5 +++++ src/nvim/testdir/test_lispindent.vim | 15 +++++++++++++ 12 files changed, 95 insertions(+), 42 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 29b00bad2a..84ff2fa59b 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1959,8 +1959,9 @@ void free_buf_options(buf_T *buf, int free_p_ff) clear_string_option(&buf->b_p_ft); clear_string_option(&buf->b_p_cink); clear_string_option(&buf->b_p_cino); - clear_string_option(&buf->b_p_cinw); + clear_string_option(&buf->b_p_lop); clear_string_option(&buf->b_p_cinsd); + clear_string_option(&buf->b_p_cinw); clear_string_option(&buf->b_p_cpt); clear_string_option(&buf->b_p_cfu); clear_string_option(&buf->b_p_ofu); diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 630e1d14a8..043a31bf16 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -699,6 +699,7 @@ struct file_buffer { uint32_t b_p_fex_flags; ///< flags for 'formatexpr' char *b_p_kp; ///< 'keywordprg' int b_p_lisp; ///< 'lisp' + char *b_p_lop; ///< 'lispoptions' char *b_p_menc; ///< 'makeencoding' char *b_p_mps; ///< 'matchpairs' int b_p_ml; ///< 'modeline' diff --git a/src/nvim/change.c b/src/nvim/change.c index c6f9e9f5c2..2424a8a2eb 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -1814,17 +1814,19 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) vreplace_mode = 0; } - if (!p_paste - && leader == NULL - && curbuf->b_p_lisp - && curbuf->b_p_ai) { - // do lisp indenting - fixthisline(get_lisp_indent); - ai_col = (colnr_T)getwhitecols_curline(); - } else if (do_cindent) { - // do 'cindent' or 'indentexpr' indenting - do_c_expr_indent(); - ai_col = (colnr_T)getwhitecols_curline(); + if (!p_paste) { + if (leader == NULL + && !use_indentexpr_for_lisp() + && curbuf->b_p_lisp + && curbuf->b_p_ai) { + // do lisp indenting + fixthisline(get_lisp_indent); + ai_col = (colnr_T)getwhitecols_curline(); + } else if (do_cindent || (curbuf->b_p_ai && use_indentexpr_for_lisp())) { + // do 'cindent' or 'indentexpr' indenting + do_c_expr_indent(); + ai_col = (colnr_T)getwhitecols_curline(); + } } if (vreplace_mode != 0) { diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 0e14567286..e37f967a2c 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -2993,34 +2993,6 @@ bool cindent_on(void) return !p_paste && (curbuf->b_p_cin || *curbuf->b_p_inde != NUL); } -// Re-indent the current line, based on the current contents of it and the -// surrounding lines. Fixing the cursor position seems really easy -- I'm very -// confused what all the part that handles Control-T is doing that I'm not. -// "get_the_indent" should be get_c_indent, get_expr_indent or get_lisp_indent. -void fixthisline(IndentGetter get_the_indent) -{ - int amount = get_the_indent(); - - if (amount >= 0) { - change_indent(INDENT_SET, amount, false, 0, true); - if (linewhite(curwin->w_cursor.lnum)) { - did_ai = true; // delete the indent if the line stays empty - } - } -} - -void fix_indent(void) -{ - if (p_paste) { - return; - } - if (curbuf->b_p_lisp && curbuf->b_p_ai) { - fixthisline(get_lisp_indent); - } else if (cindent_on()) { - do_c_expr_indent(); - } -} - /// Check that "cinkeys" contains the key "keytyped", /// when == '*': Only if key is preceded with '*' (indent before insert) /// when == '!': Only if key is preceded with '!' (don't insert) diff --git a/src/nvim/edit.h b/src/nvim/edit.h index eda6d8c9db..91c519f015 100644 --- a/src/nvim/edit.h +++ b/src/nvim/edit.h @@ -4,8 +4,6 @@ #include "nvim/autocmd.h" #include "nvim/vim.h" -typedef int (*IndentGetter)(void); - // Values for in_cinkeys() #define KEY_OPEN_FORW 0x101 #define KEY_OPEN_BACK 0x102 diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 3f5a8afbc1..3f08aa7043 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -15,6 +15,7 @@ #include "nvim/eval.h" #include "nvim/extmark.h" #include "nvim/indent.h" +#include "nvim/indent_c.h" #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/memory.h" @@ -1144,3 +1145,45 @@ static int lisp_match(char_u *p) } return false; } + +/// Re-indent the current line, based on the current contents of it and the +/// surrounding lines. Fixing the cursor position seems really easy -- I'm very +/// confused what all the part that handles Control-T is doing that I'm not. +/// "get_the_indent" should be get_c_indent, get_expr_indent or get_lisp_indent. +void fixthisline(IndentGetter get_the_indent) +{ + int amount = get_the_indent(); + + if (amount >= 0) { + change_indent(INDENT_SET, amount, false, 0, true); + if (linewhite(curwin->w_cursor.lnum)) { + did_ai = true; // delete the indent if the line stays empty + } + } +} + +/// Return true if 'indentexpr' should be used for Lisp indenting. +/// Caller may want to check 'autoindent'. +bool use_indentexpr_for_lisp(void) +{ + return curbuf->b_p_lisp + && *curbuf->b_p_inde != NUL + && strcmp(curbuf->b_p_lop, "expr:1") == 0; +} + +/// Fix indent for 'lisp' and 'cindent'. +void fix_indent(void) +{ + if (p_paste) { + return; // no auto-indenting when 'paste' is set + } + if (curbuf->b_p_lisp && curbuf->b_p_ai) { + if (use_indentexpr_for_lisp()) { + do_c_expr_indent(); + } else { + fixthisline(get_lisp_indent); + } + } else if (cindent_on()) { + do_c_expr_indent(); + } +} diff --git a/src/nvim/indent.h b/src/nvim/indent.h index f96732bf1c..f807bbb42b 100644 --- a/src/nvim/indent.h +++ b/src/nvim/indent.h @@ -3,6 +3,8 @@ #include "nvim/vim.h" +typedef int (*IndentGetter)(void); + // flags for set_indent() #define SIN_CHANGED 1 // call changed_bytes() when line changed #define SIN_INSERT 2 // insert indent before existing text diff --git a/src/nvim/option.c b/src/nvim/option.c index 7202156c46..0b819aad43 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -4008,6 +4008,8 @@ static char_u *get_varp(vimoption_T *p) return (char_u *)&(curbuf->b_p_fex); case PV_LISP: return (char_u *)&(curbuf->b_p_lisp); + case PV_LOP: + return (char_u *)&(curbuf->b_p_lop); case PV_ML: return (char_u *)&(curbuf->b_p_ml); case PV_MPS: @@ -4414,6 +4416,8 @@ void buf_copy_options(buf_T *buf, int flags) COPY_OPT_SCTX(buf, BV_CINO); buf->b_p_cinsd = xstrdup(p_cinsd); COPY_OPT_SCTX(buf, BV_CINSD); + buf->b_p_lop = xstrdup(p_lop); + COPY_OPT_SCTX(buf, BV_LOP); // Don't copy 'filetype', it must be detected buf->b_p_ft = empty_option; diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 196ab865a2..c4333a6f61 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -580,6 +580,7 @@ EXTERN char_u *p_lm; // 'langmenu' EXTERN long p_lines; // 'lines' EXTERN long p_linespace; // 'linespace' EXTERN int p_lisp; ///< 'lisp' +EXTERN char *p_lop; ///< 'lispoptions' EXTERN char_u *p_lispwords; // 'lispwords' EXTERN long p_ls; // 'laststatus' EXTERN long p_stal; // 'showtabline' @@ -878,6 +879,7 @@ enum { BV_KMAP, BV_KP, BV_LISP, + BV_LOP, BV_LW, BV_MENC, BV_MA, diff --git a/src/nvim/options.lua b/src/nvim/options.lua index f4d6c808fa..8a883a09c3 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1363,6 +1363,14 @@ return { varname='p_lisp', defaults={if_true=false} }, + { + full_name='lispoptions', abbreviation='lop', + short_desc=N_("options for lisp indenting"), + type='string', list='onecomma', scope={'buffer'}, + deny_duplicates=true, + varname='p_lop', pv_name='p_lop', + defaults={if_true=''} + }, { full_name='lispwords', abbreviation='lw', short_desc=N_("words that change how lisp indenting works"), diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index be3e137b69..43628d2842 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -228,6 +228,7 @@ void check_buf_options(buf_T *buf) check_string_option(&buf->b_p_cink); check_string_option(&buf->b_p_cino); parse_cino(buf); + check_string_option(&buf->b_p_lop); check_string_option(&buf->b_p_ft); check_string_option(&buf->b_p_cinw); check_string_option(&buf->b_p_cinsd); @@ -1378,6 +1379,10 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf } else if (gvarp == &p_cino) { // 'cinoptions' // TODO(vim): recognize errors parse_cino(curbuf); + } else if (gvarp == &p_lop) { // 'lispoptions' + if (**varp != NUL && strcmp(*varp, "expr:0") != 0 && strcmp(*varp, "expr:1") != 0) { + errmsg = e_invarg; + } } else if (varp == &p_icm) { // 'inccommand' if (check_opt_strings(p_icm, p_icm_values, false) != OK) { errmsg = e_invarg; diff --git a/src/nvim/testdir/test_lispindent.vim b/src/nvim/testdir/test_lispindent.vim index 8987f67a80..2d6060bba3 100644 --- a/src/nvim/testdir/test_lispindent.vim +++ b/src/nvim/testdir/test_lispindent.vim @@ -97,8 +97,23 @@ func Test_lispindent_with_indentexpr() exe "normal a(x\1\2)\" let expected = ['(x', ' 1', ' 2)'] call assert_equal(expected, getline(1, 3)) + " with Lisp indenting the first line is not indented normal 1G=G call assert_equal(expected, getline(1, 3)) + + %del + setl lispoptions=expr:1 indentexpr=5 + exe "normal a(x\1\2)\" + let expected_expr = ['(x', ' 1', ' 2)'] + call assert_equal(expected_expr, getline(1, 3)) + normal 2G2<<=G + call assert_equal(expected_expr, getline(1, 3)) + + setl lispoptions=expr:0 + " with Lisp indenting the first line is not indented + normal 1G3<<=G + call assert_equal(expected, getline(1, 3)) + bwipe! endfunc -- cgit From c8fbf39d474b1140bee10edbcf36305ea49bf863 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 16 Oct 2022 08:34:09 +0800 Subject: vim-patch:9.0.0764: indent and option tests fail Problem: Indent and option tests fail. Solution: Change OP_INDENT. Add entry to options test table. https://github.com/vim/vim/commit/c8b673557390e5cd20bc0a4c2786d0db1d77a24c --- src/nvim/ops.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 71e754537d..29008200a7 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -6206,7 +6206,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) // If 'equalprg' is empty, do the indenting internally. if (oap->op_type == OP_INDENT && *get_equalprg() == NUL) { if (curbuf->b_p_lisp) { - op_reindent(oap, get_lisp_indent); + if (use_indentexpr_for_lisp()) { + op_reindent(oap, get_expr_indent); + } else { + op_reindent(oap, get_lisp_indent); + } break; } op_reindent(oap, -- cgit From 935e1ca743abb4fce1effef063e5645f3df88119 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Sun, 16 Oct 2022 15:55:18 -0600 Subject: feat: mention ":help news" in intro #20674 --- src/nvim/version.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/nvim') diff --git a/src/nvim/version.c b/src/nvim/version.c index d4d406482d..32e15f200a 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -2228,6 +2228,9 @@ void intro_message(int colon) N_("type :q to exit "), N_("type :help for help "), "", + N_("type :help news to see changes in") + " v" STR(NVIM_VERSION_MAJOR) "." STR(NVIM_VERSION_MINOR) "." STR(NVIM_VERSION_PATCH), + "", N_("Help poor children in Uganda!"), N_("type :help iccf for information "), }; -- cgit From d44f088834081ee404db4459fdcfba82d14ef157 Mon Sep 17 00:00:00 2001 From: Jonas Strittmatter <40792180+smjonas@users.noreply.github.com> Date: Mon, 17 Oct 2022 08:18:57 +0200 Subject: vim-patch:9.0.0771: cannot always tell the difference beween tex and … (#20687) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vim-patch:9.0.0771: cannot always tell the difference beween tex and rexx files Problem: Cannot always tell the difference beween tex and rexx files. Solution: Recognize tex by a leading backslash. (Martin Tournoij, closes vim/vim#11380) https://github.com/vim/vim/commit/bd053f894b0d7652928201faa68c53d1ce2acdc5 --- src/nvim/testdir/test_filetype.vim | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 7669b3d82a..1ceab2a67a 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -1768,6 +1768,11 @@ func Test_cls_file() call assert_equal('tex', &filetype) bwipe! + call writefile(['\NeedsTeXFormat{LaTeX2e}'], 'Xfile.cls') + split Xfile.cls + call assert_equal('tex', &filetype) + bwipe! + " Rexx call writefile(['# rexx'], 'Xfile.cls') -- cgit From 042eb74ff1ed63d79f8a642649cd6be6ec4b0eb9 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 17 Oct 2022 08:52:40 +0200 Subject: feat(runtime)!: remove filetype.vim (#20428) Made obsolete by now graduated `filetype.lua` (enabled by default). Note that changes or additions to the filetype detection still need to be made through a PR to vim/vim as we port the _logic_ as well as tests. --- src/nvim/testdir/test_legacy_filetype.vim | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 src/nvim/testdir/test_legacy_filetype.vim (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_legacy_filetype.vim b/src/nvim/testdir/test_legacy_filetype.vim deleted file mode 100644 index 772faaadb0..0000000000 --- a/src/nvim/testdir/test_legacy_filetype.vim +++ /dev/null @@ -1,4 +0,0 @@ -let g:do_legacy_filetype = 1 -filetype on - -source test_filetype.vim -- cgit From 637ab296cba9e37e7374a8c076342487398605ee Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 17 Oct 2022 21:00:50 +0800 Subject: feat(api): nvim_select_popupmenu_item support cmdline pum (#20652) --- src/nvim/api/vim.c | 27 ++++++++++++++------------- src/nvim/cmdexpand.c | 12 ++++++++---- src/nvim/cmdexpand.h | 3 +++ src/nvim/ex_getln.c | 42 ++++++++++++++++++++++++++++++------------ src/nvim/insexpand.c | 11 ----------- src/nvim/insexpand.h | 8 -------- src/nvim/main.c | 1 - src/nvim/popupmenu.c | 11 +++++++++++ src/nvim/popupmenu.h | 8 ++++++++ 9 files changed, 74 insertions(+), 49 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index fa8d26914a..0c0c71b694 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1908,19 +1908,20 @@ Object nvim_get_proc(Integer pid, Error *err) return rvobj; } -/// Selects an item in the completion popupmenu. -/// -/// If |ins-completion| is not active this API call is silently ignored. -/// Useful for an external UI using |ui-popupmenu| to control the popupmenu -/// with the mouse. Can also be used in a mapping; use |:map-cmd| to -/// ensure the mapping doesn't end completion mode. -/// -/// @param item Index (zero-based) of the item to select. Value of -1 selects -/// nothing and restores the original text. -/// @param insert Whether the selection should be inserted in the buffer. -/// @param finish Finish the completion and dismiss the popupmenu. Implies -/// `insert`. -/// @param opts Optional parameters. Reserved for future use. +/// Selects an item in the completion popup menu. +/// +/// If neither |ins-completion| nor |cmdline-completion| popup menu is active +/// this API call is silently ignored. +/// Useful for an external UI using |ui-popupmenu| to control the popup menu with the mouse. +/// Can also be used in a mapping; use |:map-cmd| or a Lua mapping to ensure the mapping +/// doesn't end completion mode. +/// +/// @param item Index (zero-based) of the item to select. Value of -1 selects nothing +/// and restores the original text. +/// @param insert For |ins-completion|, whether the selection should be inserted in the buffer. +/// Ignored for |cmdline-completion|. +/// @param finish Finish the completion and dismiss the popup menu. Implies {insert}. +/// @param opts Optional parameters. Reserved for future use. /// @param[out] err Error details, if any void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish, Dictionary opts, Error *err) diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index fb29a723bc..a8a5848c71 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -180,7 +180,7 @@ int nextwild(expand_T *xp, int type, int options, bool escape) assert(ccline->cmdpos >= i); xp->xp_pattern_len = (size_t)ccline->cmdpos - (size_t)i; - if (type == WILD_NEXT || type == WILD_PREV) { + if (type == WILD_NEXT || type == WILD_PREV || type == WILD_PUM_WANT) { // Get next/previous match for a previous expanded pattern. p2 = (char_u *)ExpandOne(xp, NULL, NULL, 0, type); } else { @@ -290,8 +290,11 @@ static char *get_next_or_prev_match(int mode, expand_T *xp, int *p_findex, char findex = xp->xp_numfiles; } findex--; - } else { // mode == WILD_NEXT + } else if (mode == WILD_NEXT) { findex++; + } else { // mode == WILD_PUM_WANT + assert(pum_want.active); + findex = pum_want.item; } // When wrapping around, return the original string, set findex to -1. @@ -419,7 +422,7 @@ static char *find_longest_match(expand_T *xp, int options) return xstrndup(xp->xp_files[0], len); } -/// Do wildcard expansion on the string 'str'. +/// Do wildcard expansion on the string "str". /// Chars that should not be expanded must be preceded with a backslash. /// Return a pointer to allocated memory containing the new string. /// Return NULL for failure. @@ -443,6 +446,7 @@ static char *find_longest_match(expand_T *xp, int options) /// popup menu and close the menu. /// mode = WILD_CANCEL: cancel and close the cmdline completion popup and /// use the original text. +/// mode = WILD_PUM_WANT: use the match at index pum_want.item /// /// options = WILD_LIST_NOTFOUND: list entries without a match /// options = WILD_HOME_REPLACE: do home_replace() for buffer names @@ -466,7 +470,7 @@ char *ExpandOne(expand_T *xp, char *str, char *orig, int options, int mode) int i; // first handle the case of using an old match - if (mode == WILD_NEXT || mode == WILD_PREV) { + if (mode == WILD_NEXT || mode == WILD_PREV || mode == WILD_PUM_WANT) { return get_next_or_prev_match(mode, xp, &findex, orig_save); } diff --git a/src/nvim/cmdexpand.h b/src/nvim/cmdexpand.h index 93e91af169..cdd6192086 100644 --- a/src/nvim/cmdexpand.h +++ b/src/nvim/cmdexpand.h @@ -19,6 +19,9 @@ enum { WILD_ALL_KEEP = 8, WILD_CANCEL = 9, WILD_APPLY = 10, + // WILD_PAGEUP = 11, not ported yet + // WILD_PAGEDOWN = 12, not ported yet + WILD_PUM_WANT = 13, }; enum { diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 52abbd13f3..a1e4bc96b5 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -919,6 +919,22 @@ static int command_line_check(VimState *state) return 1; } +static void command_line_end_wildmenu(CommandLineState *s) +{ + if (cmdline_pum_active()) { + cmdline_pum_remove(); + } + if (s->xpc.xp_numfiles != -1) { + (void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE); + } + s->did_wild_list = false; + if (!p_wmnu || (s->c != K_UP && s->c != K_DOWN)) { + s->xpc.xp_context = EXPAND_NOTHING; + } + s->wim_index = 0; + wildmenu_cleanup(&ccline); +} + static int command_line_execute(VimState *state, int key) { if (key == K_IGNORE || key == K_NOP) { @@ -937,6 +953,19 @@ static int command_line_execute(VimState *state, int key) map_execute_lua(); } + // nvim_select_popupmenu_item() can be called from the handling of + // K_EVENT, K_COMMAND, or K_LUA. + if (pum_want.active) { + if (cmdline_pum_active()) { + nextwild(&s->xpc, WILD_PUM_WANT, 0, s->firstc != '@'); + if (pum_want.finish) { + nextwild(&s->xpc, WILD_APPLY, WILD_NO_BEEP, s->firstc != '@'); + command_line_end_wildmenu(s); + } + } + pum_want.active = false; + } + if (!cmdline_was_last_drawn) { redrawcmdline(); } @@ -1016,18 +1045,7 @@ static int command_line_execute(VimState *state, int key) if (!(s->c == p_wc && KeyTyped) && s->c != p_wcm && s->c != Ctrl_Z && s->c != Ctrl_N && s->c != Ctrl_P && s->c != Ctrl_A && s->c != Ctrl_L) { - if (cmdline_pum_active()) { - cmdline_pum_remove(); - } - if (s->xpc.xp_numfiles != -1) { - (void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE); - } - s->did_wild_list = false; - if (!p_wmnu || (s->c != K_UP && s->c != K_DOWN)) { - s->xpc.xp_context = EXPAND_NOTHING; - } - s->wim_index = 0; - wildmenu_cleanup(&ccline); + command_line_end_wildmenu(s); } if (p_wmnu) { diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index f136aa960b..0f29ae9806 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -3563,17 +3563,6 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match return num_matches; } -void pum_ext_select_item(int item, bool insert, bool finish) -{ - if (!pum_visible() || item < -1 || item >= compl_match_arraysize) { - return; - } - pum_want.active = true; - pum_want.item = item; - pum_want.insert = insert; - pum_want.finish = finish; -} - /// Call this while finding completions, to check whether the user has hit a key /// that should change the currently displayed completion, or exit completion /// mode. Also, when compl_pending is not zero, show a completion as soon as diff --git a/src/nvim/insexpand.h b/src/nvim/insexpand.h index 8e183455ca..c394468a45 100644 --- a/src/nvim/insexpand.h +++ b/src/nvim/insexpand.h @@ -3,14 +3,6 @@ #include "nvim/vim.h" -/// state for pum_ext_select_item. -EXTERN struct { - bool active; - int item; - bool insert; - bool finish; -} pum_want; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "insexpand.h.generated.h" #endif diff --git a/src/nvim/main.c b/src/nvim/main.c index 7e488794f4..07be01da3a 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -31,7 +31,6 @@ #include "nvim/highlight.h" #include "nvim/highlight_group.h" #include "nvim/iconv.h" -#include "nvim/insexpand.h" #include "nvim/locale.h" #include "nvim/log.h" #include "nvim/lua/executor.h" diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c index 3a3f966d10..893ed38e25 100644 --- a/src/nvim/popupmenu.c +++ b/src/nvim/popupmenu.c @@ -904,6 +904,17 @@ void pum_recompose(void) ui_comp_compose_grid(&pum_grid); } +void pum_ext_select_item(int item, bool insert, bool finish) +{ + if (!pum_visible() || item < -1 || item >= pum_size) { + return; + } + pum_want.active = true; + pum_want.item = item; + pum_want.insert = insert; + pum_want.finish = finish; +} + /// Gets the height of the menu. /// /// @return the height of the popup menu, the number of entries visible. diff --git a/src/nvim/popupmenu.h b/src/nvim/popupmenu.h index 851ad31486..20b24fc219 100644 --- a/src/nvim/popupmenu.h +++ b/src/nvim/popupmenu.h @@ -16,6 +16,14 @@ typedef struct { EXTERN ScreenGrid pum_grid INIT(= SCREEN_GRID_INIT); +/// state for pum_ext_select_item. +EXTERN struct { + bool active; + int item; + bool insert; + bool finish; +} pum_want; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "popupmenu.h.generated.h" #endif -- cgit From 46eabe1ac1f70d0d7b199cb7e505b275b2f01bff Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Sun, 16 Oct 2022 22:34:14 +0200 Subject: fix: 'scroll' is not set correctly for floats with 'splitkeep' vim-patch:9.0.0780: 'scroll' value computed in unexpected location Problem: 'scroll' value computed in unexpected location. Solution: Compute 'scroll' when the window height is changed. (Luuk van Baal, closes vim/vim#11387) https://github.com/vim/vim/commit/a1a46da87d91cfbf46866627b3e66a757145e8c3 --- src/nvim/window.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/window.c b/src/nvim/window.c index 002d95ac2a..7c246191a5 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -6119,7 +6119,6 @@ void win_fix_scroll(int resize) invalidate_botline_win(wp); validate_botline(wp); } - win_comp_scroll(wp); wp->w_prev_height = wp->w_height; wp->w_prev_winrow = wp->w_winrow; } @@ -6292,7 +6291,6 @@ void scroll_to_fraction(win_T *wp, int prev_height) wp->w_prev_fraction_row = wp->w_wrow; } - win_comp_scroll(wp); redraw_later(wp, UPD_SOME_VALID); wp->w_redr_status = true; invalidate_botline_win(wp); @@ -6327,6 +6325,7 @@ void win_set_inner_size(win_T *wp, bool valid_cursor) } wp->w_skipcol = 0; wp->w_height_inner = height; + win_comp_scroll(wp); // There is no point in adjusting the scroll position when exiting. Some // values might be invalid. -- cgit From e4273135455084bca54a484f88fd364af62bf69c Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 17 Oct 2022 18:36:10 +0200 Subject: vim-patch:9.0.0782: OpenVPN files are not recognized (#20702) Problem: OpenVPN files are not recognized. Solution: Add patterns for OpenVPN files. (closes vim/vim#11391) https://github.com/vim/vim/commit/4bf67ec52e938a3edaa4f452adab42a57505f940 --- src/nvim/testdir/test_filetype.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 1ceab2a67a..cecc112635 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -401,6 +401,7 @@ let s:filename_checks = { \ 'opam': ['opam', 'file.opam', 'file.opam.template'], \ 'openroad': ['file.or'], \ 'openscad': ['file.scad'], + \ 'openvpn': ['file.ovpn', '/etc/openvpn/client/client.conf', '/usr/share/openvpn/examples/server.conf'], \ 'opl': ['file.OPL', 'file.OPl', 'file.OpL', 'file.Opl', 'file.oPL', 'file.oPl', 'file.opL', 'file.opl'], \ 'ora': ['file.ora'], \ 'org': ['file.org', 'file.org_archive'], -- cgit From bd7ca10fdd762f8846dc0ef9db0a723bb8f3610a Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Mon, 17 Oct 2022 21:15:24 -0500 Subject: fix(qflist): avoid read of uninitialized memory (#20709) If the call to `qf_setup_state()` in `qf_init_ext()` fails, control flow jumps to label `qf_init_end` where a call to `qf_update_buffer()` is made with `old_last` as a function call argument. Prior to this patch, `old_last` would not yet have been initialized to its default value of `NULL`, resulting in `qf_update_buffer()` attempting to compare against its uninitialized value (quickfix.c:3882) then later forwarding it to `qf_fill_buffer()` where the address is dereferenced and repeatedly read from/ written to for performing core qflist operations. Depending on what the default value of `old_last` was, the results may range from a best case scenario of neovim terminating with SIGSEGV from an attempt to dereference an invalid pointer (quickfix.c:4056) to memory corruption if it contained some other value that results in `qfp` being initialized from `old_last->qf_next` (after which it is subsequently written to and read from in a fairly arbitrary fashion). Though extremely unlikely, it's possible for there to be security considerations as a user can ensure that the next call to `qf_setup_state()` fails. This patch ensures that `old_last` is NULL-assigned before control flow jumps to `qf_init_end`. Closes #20639. --- src/nvim/quickfix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 2d8100595f..af1db1956b 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1059,6 +1059,7 @@ static int qf_init_ext(qf_info_T *qi, int qf_idx, const char *restrict efile, bu { qfstate_T state = { 0 }; qffields_T fields = { 0 }; + qfline_T *old_last = NULL; static efm_T *fmt_first = NULL; static char *last_efm = NULL; int retval = -1; // default: return error flag @@ -1072,7 +1073,6 @@ static int qf_init_ext(qf_info_T *qi, int qf_idx, const char *restrict efile, bu } qf_list_T *qfl; - qfline_T *old_last = NULL; bool adding = false; if (newlist || qf_idx == qi->qf_listcount) { // make place for a new list -- cgit From 97164748b933a351423824b1988c863cc5994cdb Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 18 Oct 2022 18:42:32 +0800 Subject: fix(intro): omit patch version in ":help news" item #20713 Because maintenance releases share the same news.txt as the last non-maintenance release. --- src/nvim/version.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/version.c b/src/nvim/version.c index 32e15f200a..f0e2f92ba4 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -2228,8 +2228,8 @@ void intro_message(int colon) N_("type :q to exit "), N_("type :help for help "), "", - N_("type :help news to see changes in") - " v" STR(NVIM_VERSION_MAJOR) "." STR(NVIM_VERSION_MINOR) "." STR(NVIM_VERSION_PATCH), + N_("type :help news to see changes in") + " v" STR(NVIM_VERSION_MAJOR) "." STR(NVIM_VERSION_MINOR), "", N_("Help poor children in Uganda!"), N_("type :help iccf for information "), -- cgit From 4d896be681d9b93ebe34cce38a5e787cd0332261 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 18 Oct 2022 20:46:42 +0800 Subject: vim-patch:9.0.0786: user command does not get number from :tab modifier (#20716) Problem: User command does not get number from :tab modifier. Solution: Include the number. (closes vim/vim#11393, closes vim/vim#6901) https://github.com/vim/vim/commit/208567e9d744ef7b89bed1f62e951ae4ee2f6f5f --- src/nvim/testdir/test_usercommands.vim | 13 +++++++++++++ src/nvim/usercmd.c | 14 ++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim index 1065dd16e2..12fe39851d 100644 --- a/src/nvim/testdir/test_usercommands.vim +++ b/src/nvim/testdir/test_usercommands.vim @@ -79,6 +79,19 @@ function Test_cmdmods() call assert_equal('silent!', g:mods) tab MyCmd call assert_equal('tab', g:mods) + 0tab MyCmd + call assert_equal('0tab', g:mods) + tab split + tab MyCmd + call assert_equal('tab', g:mods) + 1tab MyCmd + call assert_equal('1tab', g:mods) + tabprev + tab MyCmd + call assert_equal('tab', g:mods) + 2tab MyCmd + call assert_equal('2tab', g:mods) + 2tabclose topleft MyCmd call assert_equal('topleft', g:mods) to MyCmd diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c index 0e045c773f..4c2ea75047 100644 --- a/src/nvim/usercmd.c +++ b/src/nvim/usercmd.c @@ -1236,8 +1236,18 @@ size_t add_win_cmd_modifers(char *buf, const cmdmod_T *cmod, bool *multi_mods) // :tab if (cmod->cmod_tab > 0) { - result += add_cmd_modifier(buf, "tab", multi_mods); + int tabnr = cmod->cmod_tab - 1; + if (tabnr == tabpage_index(curtab)) { + // For compatibility, don't add a tabpage number if it is the same + // as the default number for :tab. + result += add_cmd_modifier(buf, "tab", multi_mods); + } else { + char tab_buf[NUMBUFLEN + 3]; + snprintf(tab_buf, sizeof(tab_buf), "%dtab", tabnr); + result += add_cmd_modifier(buf, tab_buf, multi_mods); + } } + // :topleft if (cmod->cmod_split & WSP_TOP) { result += add_cmd_modifier(buf, "topleft", multi_mods); @@ -1307,7 +1317,7 @@ size_t uc_mods(char *buf, const cmdmod_T *cmod, bool quote) result += add_cmd_modifier(buf, "verbose", &multi_mods); } else { char verbose_buf[NUMBUFLEN]; - snprintf(verbose_buf, NUMBUFLEN, "%dverbose", verbose_value); + snprintf(verbose_buf, sizeof(verbose_buf), "%dverbose", verbose_value); result += add_cmd_modifier(buf, verbose_buf, &multi_mods); } } -- cgit From aa2f08a050682ed6b2ffae521feec88451f2d3c0 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 18 Oct 2022 18:46:09 +0200 Subject: fix(highlight): link more treesitter groups by default (#20711) Problem: Captures used by bundled parsers are not highlighted by default Solution: Add links to default groups A link is added for a capture if * there is a default group of the same name (e.g., `@tag` -> `Tag`) * it's used in a bundled query and doesn't have a reasonable fallback (e.g., `@text.literal`) Also add all linked groups to the treesitter docs. --- src/nvim/highlight_group.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index 3508560e20..4c253480be 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -98,8 +98,6 @@ enum { // The default highlight groups. These are compiled-in for fast startup and // they still work when the runtime files can't be found. -// -// When making changes here, also change runtime/colors/default.vim! static const char *highlight_init_both[] = { "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey", @@ -186,9 +184,13 @@ static const char *highlight_init_both[] = { "default link DiagnosticSignInfo DiagnosticInfo", "default link DiagnosticSignHint DiagnosticHint", + // Text + "default link @text.literal Comment", + "default link @text.reference Identifier", + "default link @text.title Title", + "default link @text.uri Underlined", "default link @text.underline Underlined", - "default link @todo Todo", - "default link @debug Debug", + "default link @text.todo Todo", // Miscs "default link @comment Comment", @@ -202,6 +204,7 @@ static const char *highlight_init_both[] = { "default link @macro Macro", "default link @string String", "default link @string.escape SpecialChar", + "default link @string.special SpecialChar", "default link @character Character", "default link @character.special SpecialChar", "default link @number Number", @@ -226,12 +229,16 @@ static const char *highlight_init_both[] = { "default link @keyword Keyword", "default link @exception Exception", + "default link @variable Identifier", "default link @type Type", "default link @type.definition Typedef", "default link @storageclass StorageClass", "default link @structure Structure", + "default link @namespace Identifier", "default link @include Include", "default link @preproc PreProc", + "default link @debug Debug", + "default link @tag Tag", NULL }; -- cgit From 228a04070e94ca31884f304f3a8d34e67654025d Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 18 Oct 2022 20:05:50 +0200 Subject: vim-patch:9.0.0779: lsl and lm3 file extensions are not recognized (#20704) Problem: lsl and lm3 file extensions are not recognized. Solution: Add *.lsl and *.lm3 patterns. (Doug Kearns, closes vim/vim#11384) https://github.com/vim/vim/commit/4ac8e7948cb3e07bc4598ede8b274891d14dfa7c --- src/nvim/testdir/test_filetype.vim | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index cecc112635..c9ec7771f4 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -366,7 +366,7 @@ let s:filename_checks = { \ 'mmp': ['file.mmp'], \ 'modconf': ['/etc/modules.conf', '/etc/modules', '/etc/conf.modules', '/etc/modprobe.file', 'any/etc/conf.modules', 'any/etc/modprobe.file', 'any/etc/modules', 'any/etc/modules.conf'], \ 'modula2': ['file.m2', 'file.mi'], - \ 'modula3': ['file.m3', 'file.mg', 'file.i3', 'file.ig'], + \ 'modula3': ['file.m3', 'file.mg', 'file.i3', 'file.ig', 'file.lm3'], \ 'monk': ['file.isc', 'file.monk', 'file.ssc', 'file.tsc'], \ 'moo': ['file.moo'], \ 'moonscript': ['file.moon'], @@ -1963,4 +1963,36 @@ func Test_inc_file() filetype off endfunc +func Test_lsl_file() + filetype on + + call writefile(['looks like Linden Scripting Language'], 'Xfile.lsl') + split Xfile.lsl + call assert_equal('lsl', &filetype) + bwipe! + + " Test dist#ft#FTlsl() + + let g:filetype_lsl = 'larch' + split Xfile.lsl + call assert_equal('larch', &filetype) + bwipe! + unlet g:filetype_lsl + + " Larch Shared Language + + call writefile(['% larch comment'], 'Xfile.lsl') + split Xfile.lsl + call assert_equal('larch', &filetype) + bwipe! + + call writefile(['foo: trait'], 'Xfile.lsl') + split Xfile.lsl + call assert_equal('larch', &filetype) + bwipe! + + call delete('Xfile.lsl') + filetype off +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From a5a5e273233ec6738fa6cda2a6618009646c4c47 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 18 Oct 2022 23:06:10 +0100 Subject: refactor(window.c): reduce scope of locals (#20301) --- src/nvim/window.c | 551 +++++++++++++++++++++++------------------------------- 1 file changed, 234 insertions(+), 317 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/window.c b/src/nvim/window.c index 7c246191a5..0e6bb7822a 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -125,15 +125,10 @@ win_T *prevwin_curwin(void) /// @param xchar extra char from ":wincmd gx" or NUL void do_window(int nchar, long Prenum, int xchar) { - long Prenum1; - win_T *wp; - char *ptr; - linenr_T lnum = -1; int type = FIND_DEFINE; - size_t len; char cbuf[40]; - Prenum1 = Prenum == 0 ? 1 : Prenum; + long Prenum1 = Prenum == 0 ? 1 : Prenum; #define CHECK_CMDWIN \ do { \ @@ -236,8 +231,8 @@ newwindow: break; // cursor to preview window - case 'P': - wp = NULL; + case 'P': { + win_T *wp = NULL; FOR_ALL_WINDOWS_IN_TAB(wp2, curtab) { if (wp2->w_p_pvw) { wp = wp2; @@ -250,6 +245,7 @@ newwindow: win_goto(wp); } break; + } // close all but current window case Ctrl_O: @@ -269,6 +265,7 @@ newwindow: if (ONE_WINDOW && Prenum != 1) { // just one window beep_flush(); } else { + win_T *wp; if (Prenum) { // go to specified window for (wp = firstwin; --Prenum > 0;) { if (wp->w_next == NULL) { @@ -346,7 +343,7 @@ newwindow: // First create a new tab with the window, then go back to // the old tab and close the window there. - wp = curwin; + win_T *wp = curwin; if (win_new_tabpage((int)Prenum, NULL) == OK && valid_tabpage(oldtab)) { newtab = curtab; @@ -486,11 +483,12 @@ newwindow: // edit file name under cursor in a new window case 'f': case 'F': - case Ctrl_F: + case Ctrl_F: { wingotofile: CHECK_CMDWIN; - ptr = (char *)grab_file_name(Prenum1, &lnum); + linenr_T lnum = -1; + char *ptr = (char *)grab_file_name(Prenum1, &lnum); if (ptr != NULL) { tabpage_T *oldtab = curtab; win_T *oldwin = curwin; @@ -510,6 +508,7 @@ wingotofile: xfree(ptr); } break; + } // Go to the first occurrence of the identifier under cursor along path in a // new window -- webb @@ -518,8 +517,10 @@ wingotofile: type = FIND_ANY; FALLTHROUGH; case 'd': // Go to definition, using 'define' - case Ctrl_D: + case Ctrl_D: { CHECK_CMDWIN; + size_t len; + char *ptr; if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) { break; } @@ -532,6 +533,7 @@ wingotofile: xfree(ptr); curwin->w_set_curswant = true; break; + } // Quickfix window only: view the result under the cursor in a new split. case K_KENTER: @@ -638,11 +640,13 @@ void win_set_buf(Window window, Buffer buffer, bool noautocmd, Error *err) { win_T *win = find_window_by_handle(window, err); buf_T *buf = find_buffer_by_handle(buffer, err); - tabpage_T *tab = win_find_tabpage(win); if (!win || !buf) { return; } + + tabpage_T *tab = win_find_tabpage(win); + if (noautocmd) { block_autocmds(); } @@ -1025,26 +1029,13 @@ int win_split(int size, int flags) int win_split_ins(int size, int flags, win_T *new_wp, int dir) { win_T *wp = new_wp; - win_T *oldwin; - int new_size = size; - int i; - int need_status = 0; - bool do_equal = false; - int needed; - int available; - int oldwin_height = 0; - int layout; - frame_T *frp, *curfrp, *frp2, *prevfrp; - int before; - int minheight; - int wmh1; - bool did_set_fraction = false; // aucmd_win should always remain floating if (new_wp != NULL && new_wp == aucmd_win) { return FAIL; } + win_T *oldwin; if (flags & WSP_TOP) { oldwin = firstwin; } else if (flags & WSP_BOT || curwin->w_floating) { @@ -1054,6 +1045,8 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) oldwin = curwin; } + int need_status = 0; + int new_size = size; bool new_in_layout = (new_wp == NULL || new_wp->w_floating); // add a status line when p_ls == 1 and splitting the first window @@ -1065,30 +1058,33 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) need_status = STATUS_HEIGHT; } - if (flags & WSP_VERT) { - int wmw1; - int minwidth; - - layout = FR_ROW; + bool do_equal = false; + int oldwin_height = 0; + const int layout = flags & WSP_VERT ? FR_ROW : FR_COL; + bool did_set_fraction = false; + if (flags & WSP_VERT) { // Check if we are able to split the current window and compute its // width. // Current window requires at least 1 space. - wmw1 = (p_wmw == 0 ? 1 : (int)p_wmw); - needed = wmw1 + 1; + int wmw1 = (p_wmw == 0 ? 1 : (int)p_wmw); + int needed = wmw1 + 1; if (flags & WSP_ROOM) { needed += (int)p_wiw - wmw1; } + int minwidth; + int available; if (flags & (WSP_BOT | WSP_TOP)) { minwidth = frame_minwidth(topframe, NOWIN); available = topframe->fr_width; needed += minwidth; } else if (p_ea) { minwidth = frame_minwidth(oldwin->w_frame, NOWIN); - prevfrp = oldwin->w_frame; - for (frp = oldwin->w_frame->fr_parent; frp != NULL; + frame_T *prevfrp = oldwin->w_frame; + for (frame_T *frp = oldwin->w_frame->fr_parent; frp != NULL; frp = frp->fr_parent) { if (frp->fr_layout == FR_ROW) { + frame_T *frp2; FOR_ALL_FRAMES(frp2, frp->fr_child) { if (frp2 != prevfrp) { minwidth += frame_minwidth(frp2, NOWIN); @@ -1134,7 +1130,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) // is wider than one of the split windows. if (!do_equal && p_ea && size == 0 && *p_ead != 'v' && oldwin->w_frame->fr_parent != NULL) { - frp = oldwin->w_frame->fr_parent->fr_child; + frame_T *frp = oldwin->w_frame->fr_parent->fr_child; while (frp != NULL) { if (frp->fr_win != oldwin && frp->fr_win != NULL && (frp->fr_win->w_width > new_size @@ -1147,27 +1143,28 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } } } else { - layout = FR_COL; - // Check if we are able to split the current window and compute its height. // Current window requires at least 1 space plus space for the window bar. - wmh1 = MAX((int)p_wmh, 1) + oldwin->w_winbar_height; - needed = wmh1 + STATUS_HEIGHT; + int wmh1 = MAX((int)p_wmh, 1) + oldwin->w_winbar_height; + int needed = wmh1 + STATUS_HEIGHT; if (flags & WSP_ROOM) { needed += (int)p_wh - wmh1 + oldwin->w_winbar_height; } if (p_ch < 1) { needed += 1; // Adjust for cmdheight=0. } + int minheight; + int available; if (flags & (WSP_BOT | WSP_TOP)) { minheight = frame_minheight(topframe, NOWIN) + need_status; available = topframe->fr_height; needed += minheight; } else if (p_ea) { minheight = frame_minheight(oldwin->w_frame, NOWIN) + need_status; - prevfrp = oldwin->w_frame; - for (frp = oldwin->w_frame->fr_parent; frp != NULL; frp = frp->fr_parent) { + frame_T *prevfrp = oldwin->w_frame; + for (frame_T *frp = oldwin->w_frame->fr_parent; frp != NULL; frp = frp->fr_parent) { if (frp->fr_layout == FR_COL) { + frame_T *frp2; FOR_ALL_FRAMES(frp2, frp->fr_child) { if (frp2 != prevfrp) { minheight += frame_minheight(frp2, NOWIN); @@ -1230,7 +1227,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) if (!do_equal && p_ea && size == 0 && *p_ead != 'h' && oldwin->w_frame->fr_parent != NULL) { - frp = oldwin->w_frame->fr_parent->fr_child; + frame_T *frp = oldwin->w_frame->fr_parent->fr_child; while (frp != NULL) { if (frp->fr_win != oldwin && frp->fr_win != NULL && (frp->fr_win->w_height > new_size @@ -1280,6 +1277,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) CLEAR_FIELD(wp->w_border_adj); } + int before; + frame_T *curfrp; + // Reorganise the tree of frames to insert the new window. if (flags & (WSP_TOP | WSP_BOT)) { if ((topframe->fr_layout == FR_COL && (flags & WSP_VERT) == 0) @@ -1308,7 +1308,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } if (curfrp->fr_parent == NULL || curfrp->fr_parent->fr_layout != layout) { // Need to create a new frame in the tree to make a branch. - frp = xcalloc(1, sizeof(frame_T)); + frame_T *frp = xcalloc(1, sizeof(frame_T)); *frp = *curfrp; curfrp->fr_layout = (char)layout; frp->fr_parent = curfrp; @@ -1326,6 +1326,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } } + frame_T *frp; if (new_wp == NULL) { frp = wp->w_frame; } else { @@ -1492,6 +1493,8 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) win_fix_scroll(false); } + int i; + // Don't change the window height/width to 'winheight' / 'winwidth' if a // size was given. if (flags & WSP_VERT) { @@ -1530,8 +1533,6 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) // being copied. static void win_init(win_T *newp, win_T *oldp, int flags) { - int i; - newp->w_buffer = oldp->w_buffer; newp->w_s = &(oldp->w_buffer->b_s); oldp->w_buffer->b_nwindows++; @@ -1568,7 +1569,7 @@ static void win_init(win_T *newp, win_T *oldp, int flags) } // copy tagstack and folds - for (i = 0; i < oldp->w_tagstacklen; i++) { + for (int i = 0; i < oldp->w_tagstacklen; i++) { taggy_T *tag = &newp->w_tagstack[i]; *tag = oldp->w_tagstack[i]; if (tag->tagname != NULL) { @@ -1690,7 +1691,6 @@ int win_count(void) int make_windows(int count, bool vertical) { int maxcount; - int todo; if (vertical) { // Each window needs at least 'winminwidth' lines and a separator column. @@ -1720,6 +1720,8 @@ int make_windows(int count, bool vertical) // when putting the buffers in the windows. block_autocmds(); + int todo; + // todo is number of windows left to create for (todo = count - 1; todo > 0; todo--) { if (vertical) { @@ -1745,12 +1747,6 @@ int make_windows(int count, bool vertical) // Exchange current and next window static void win_exchange(long Prenum) { - frame_T *frp; - frame_T *frp2; - win_T *wp; - win_T *wp2; - int temp; - if (curwin->w_floating) { emsg(e_floatexchange); return; @@ -1762,6 +1758,8 @@ static void win_exchange(long Prenum) return; } + frame_T *frp; + // find window to exchange with if (Prenum) { frp = curwin->w_frame->fr_parent->fr_child; @@ -1779,7 +1777,7 @@ static void win_exchange(long Prenum) if (frp == NULL || frp->fr_win == NULL || frp->fr_win == curwin) { return; } - wp = frp->fr_win; + win_T *wp = frp->fr_win; // 1. remove curwin from the list. Remember after which window it was in wp2 // 2. insert curwin before wp in the list @@ -1787,8 +1785,8 @@ static void win_exchange(long Prenum) // 3. remove wp from the list // 4. insert wp after wp2 // 5. exchange the status line height, winbar height, hsep height and vsep width. - wp2 = curwin->w_prev; - frp2 = curwin->w_frame->fr_prev; + win_T *wp2 = curwin->w_prev; + frame_T *frp2 = curwin->w_frame->fr_prev; if (wp->w_prev != curwin) { win_remove(curwin, NULL); frame_remove(curwin->w_frame); @@ -1805,7 +1803,7 @@ static void win_exchange(long Prenum) frame_append(frp2, wp->w_frame); } } - temp = curwin->w_status_height; + int temp = curwin->w_status_height; curwin->w_status_height = wp->w_status_height; wp->w_status_height = temp; temp = curwin->w_vsep_width; @@ -1837,11 +1835,6 @@ static void win_exchange(long Prenum) // if upwards false the first window becomes the second one static void win_rotate(bool upwards, int count) { - win_T *wp1; - win_T *wp2; - frame_T *frp; - int n; - if (curwin->w_floating) { emsg(e_floatexchange); return; @@ -1854,6 +1847,7 @@ static void win_rotate(bool upwards, int count) } // Check if all frames in this row/col have one window. + frame_T *frp; FOR_ALL_FRAMES(frp, curwin->w_frame->fr_parent->fr_child) { if (frp->fr_win == NULL) { emsg(_("E443: Cannot rotate when another window is split")); @@ -1861,6 +1855,9 @@ static void win_rotate(bool upwards, int count) } } + win_T *wp1; + win_T *wp2; + while (count--) { if (upwards) { // first window becomes last window // remove first window/frame from the list @@ -1893,7 +1890,7 @@ static void win_rotate(bool upwards, int count) } // exchange status height, winbar height, hsep height and vsep width of old and new last window - n = wp2->w_status_height; + int n = wp2->w_status_height; wp2->w_status_height = wp1->w_status_height; wp1->w_status_height = n; n = wp2->w_hsep_height; @@ -1965,8 +1962,6 @@ static void win_totop(int size, int flags) // window. Only works within the same frame! void win_move_after(win_T *win1, win_T *win2) { - int height; - // check if the arguments are reasonable if (win1 == win2) { return; @@ -1982,7 +1977,7 @@ void win_move_after(win_T *win1, win_T *win2) // may need to move the status line, window bar, horizontal or vertical separator of the last // window if (win1 == lastwin) { - height = win1->w_prev->w_status_height; + int height = win1->w_prev->w_status_height; win1->w_prev->w_status_height = win1->w_status_height; win1->w_status_height = height; @@ -1999,7 +1994,7 @@ void win_move_after(win_T *win1, win_T *win2) win1->w_frame->fr_width += 1; } } else if (win2 == lastwin) { - height = win1->w_status_height; + int height = win1->w_status_height; win1->w_status_height = win2->w_status_height; win2->w_status_height = height; @@ -2096,15 +2091,11 @@ void win_equal(win_T *next_curwin, bool current, int dir) static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int dir, int col, int row, int width, int height) { - int n, m; int extra_sep = 0; - int wincount, totwincount = 0; - frame_T *fr; + int totwincount = 0; int next_curwin_size = 0; int room = 0; - int new_size; int has_next_curwin = 0; - bool hnc; if (topfr->fr_layout == FR_LEAF) { // Set the width/height of this frame. @@ -2125,7 +2116,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int if (dir != 'v') { // equalize frame widths // Compute the maximum number of windows horizontally in this // frame. - n = frame_minwidth(topfr, NOWIN); + int n = frame_minwidth(topfr, NOWIN); // add one for the rightmost window, it doesn't have a separator if (col + width == Columns) { extra_sep = 1; @@ -2138,13 +2129,14 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int // Compute width for "next_curwin" window and room available for // other windows. // "m" is the minimal width when counting p_wiw for "next_curwin". - m = frame_minwidth(topfr, next_curwin); + int m = frame_minwidth(topfr, next_curwin); room = width - m; if (room < 0) { next_curwin_size = (int)p_wiw + room; room = 0; } else { next_curwin_size = -1; + frame_T *fr; FOR_ALL_FRAMES(fr, topfr->fr_child) { if (!frame_fixed_width(fr)) { continue; @@ -2152,7 +2144,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int // If 'winfixwidth' set keep the window width if possible. // Watch out for this window being the next_curwin. n = frame_minwidth(fr, NOWIN); - new_size = fr->fr_width; + int new_size = fr->fr_width; if (frame_has_win(fr, next_curwin)) { room += (int)p_wiw - (int)p_wmw; next_curwin_size = 0; @@ -2193,8 +2185,10 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int } } + frame_T *fr; FOR_ALL_FRAMES(fr, topfr->fr_child) { - wincount = 1; + int wincount = 1; + int new_size; if (fr->fr_next == NULL) { // last frame gets all that remains (avoid roundoff error) new_size = width; @@ -2205,14 +2199,10 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int wincount = 0; // doesn't count as a sizeable window } else { // Compute the maximum number of windows horiz. in "fr". - n = frame_minwidth(fr, NOWIN); + int n = frame_minwidth(fr, NOWIN); wincount = (n + (fr->fr_next == NULL ? extra_sep : 0)) / ((int)p_wmw + 1); - m = frame_minwidth(fr, next_curwin); - if (has_next_curwin) { - hnc = frame_has_win(fr, next_curwin); - } else { - hnc = false; - } + int m = frame_minwidth(fr, next_curwin); + bool hnc = has_next_curwin && frame_has_win(fr, next_curwin); if (hnc) { // don't count next_curwin wincount--; } @@ -2249,7 +2239,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int if (dir != 'h') { // equalize frame heights // Compute maximum number of windows vertically in this frame. - n = frame_minheight(topfr, NOWIN); + int n = frame_minheight(topfr, NOWIN); // add one for the bottom window if it doesn't have a statusline or separator if (row + height >= cmdline_row && p_ls == 0) { extra_sep = STATUS_HEIGHT; @@ -2264,7 +2254,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int // Compute height for "next_curwin" window and room available for // other windows. // "m" is the minimal height when counting p_wh for "next_curwin". - m = frame_minheight(topfr, next_curwin); + int m = frame_minheight(topfr, next_curwin); room = height - m; if (room < 0) { // The room is less than 'winheight', use all space for the @@ -2273,6 +2263,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int room = 0; } else { next_curwin_size = -1; + frame_T *fr; FOR_ALL_FRAMES(fr, topfr->fr_child) { if (!frame_fixed_height(fr)) { continue; @@ -2280,7 +2271,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int // If 'winfixheight' set keep the window height if possible. // Watch out for this window being the next_curwin. n = frame_minheight(fr, NOWIN); - new_size = fr->fr_height; + int new_size = fr->fr_height; if (frame_has_win(fr, next_curwin)) { room += (int)p_wh - (int)p_wmh; next_curwin_size = 0; @@ -2321,8 +2312,10 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int } } + frame_T *fr; FOR_ALL_FRAMES(fr, topfr->fr_child) { - wincount = 1; + int new_size; + int wincount = 1; if (fr->fr_next == NULL) { // last frame gets all that remains (avoid roundoff error) new_size = height; @@ -2333,14 +2326,10 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int wincount = 0; // doesn't count as a sizeable window } else { // Compute the maximum number of windows vert. in "fr". - n = frame_minheight(fr, NOWIN); + int n = frame_minheight(fr, NOWIN); wincount = get_maximum_wincount(fr, (n + (fr->fr_next == NULL ? extra_sep : 0))); - m = frame_minheight(fr, next_curwin); - if (has_next_curwin) { - hnc = frame_has_win(fr, next_curwin); - } else { - hnc = false; - } + int m = frame_minheight(fr, next_curwin); + bool hnc = has_next_curwin && frame_has_win(fr, next_curwin); if (hnc) { // don't count next_curwin wincount--; } @@ -2450,8 +2439,6 @@ void curwin_init(void) /// @param keep_curwin don't close `curwin` void close_windows(buf_T *buf, bool keep_curwin) { - tabpage_T *tp, *nexttp; - RedrawingDisabled++; // Start from lastwin to close floating windows with the same buffer first. @@ -2471,8 +2458,10 @@ void close_windows(buf_T *buf, bool keep_curwin) } } + tabpage_T *nexttp; + // Also check windows in other tab pages. - for (tp = first_tabpage; tp != NULL; tp = nexttp) { + for (tabpage_T *tp = first_tabpage; tp != NULL; tp = nexttp) { nexttp = tp->tp_next; if (tp != curtab) { // Start from tp_lastwin to close floating windows with the same buffer first. @@ -2645,11 +2634,6 @@ static void win_close_buffer(win_T *win, bool free_buf, bool abort_if_last) // Returns FAIL when the window was not closed. int win_close(win_T *win, bool free_buf, bool force) { - win_T *wp; - bool other_buffer = false; - bool close_curwin = false; - int dir; - bool help_window = false; tabpage_T *prev_curtab = curtab; frame_T *win_frame = win->w_floating ? NULL : win->w_frame->fr_parent; const bool had_diffmode = win->w_p_diff; @@ -2694,6 +2678,8 @@ int win_close(win_T *win, bool free_buf, bool force) return FAIL; } + bool help_window = false; + // When closing the help window, try restoring a snapshot after closing // the window. Otherwise clear the snapshot, it's now invalid. if (bt_help(win->w_buffer)) { @@ -2702,6 +2688,9 @@ int win_close(win_T *win, bool free_buf, bool force) clear_snapshot(curtab, SNAP_HELP_IDX); } + win_T *wp; + bool other_buffer = false; + if (win == curwin) { leaving_window(curwin); @@ -2816,6 +2805,7 @@ int win_close(win_T *win, bool free_buf, bool force) // Free the memory used for the window and get the window that received // the screen space. + int dir; wp = win_free_mem(win, &dir, NULL); if (help_window) { @@ -2827,6 +2817,8 @@ int win_close(win_T *win, bool free_buf, bool force) } } + bool close_curwin = false; + // Make sure curwin isn't invalid. It can cause severe trouble when // printing an error message. For win_equal() curbuf needs to be valid // too. @@ -2938,10 +2930,6 @@ static void do_autocmd_winclosed(win_T *win) // updated. void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) { - int dir; - tabpage_T *ptp = NULL; - bool free_tp = false; - // Get here with win->w_buffer == NULL when win_close() detects the tab page // changed. if (win->w_closing @@ -2961,6 +2949,8 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false, true); } + tabpage_T *ptp = NULL; + // Careful: Autocommands may have closed the tab page or made it the // current tab page. for (ptp = first_tabpage; ptp != NULL && ptp != tp; ptp = ptp->tp_next) {} @@ -2989,6 +2979,8 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) } } + bool free_tp = false; + // When closing the last window in a tab page remove the tab page. if (tp->tp_firstwin == tp->tp_lastwin) { char prev_idx[NUMBUFLEN]; @@ -3023,6 +3015,7 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) } // Free the memory used for the window. + int dir; win_free_mem(win, &dir, tp); if (free_tp) { @@ -3038,12 +3031,11 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) /// @return a pointer to the window that got the freed up space. static win_T *win_free_mem(win_T *win, int *dirp, tabpage_T *tp) { - frame_T *frp; win_T *wp; if (!win->w_floating) { // Remove the window and its frame from the tree of frames. - frp = win->w_frame; + frame_T *frp = win->w_frame; wp = winframe_remove(win, dirp, tp); xfree(frp); } else { @@ -3072,8 +3064,6 @@ static win_T *win_free_mem(win_T *win, int *dirp, tabpage_T *tp) #if defined(EXITFREE) void win_free_all(void) { - int dummy; - // avoid an error for switching tabpage with the cmdline window open cmdwin_type = 0; @@ -3084,6 +3074,7 @@ void win_free_all(void) while (lastwin != NULL && lastwin->w_floating) { win_T *wp = lastwin; win_remove(lastwin, NULL); + int dummy; (void)win_free_mem(wp, &dummy, NULL); if (wp == aucmd_win) { aucmd_win = NULL; @@ -3091,11 +3082,13 @@ void win_free_all(void) } if (aucmd_win != NULL) { + int dummy; (void)win_free_mem(aucmd_win, &dummy, NULL); aucmd_win = NULL; } while (firstwin != NULL) { + int dummy; (void)win_free_mem(firstwin, &dummy, NULL); } @@ -3114,18 +3107,16 @@ void win_free_all(void) /// @return a pointer to the window that got the freed up space. win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp) { - frame_T *frp, *frp2, *frp3; - frame_T *frp_close = win->w_frame; - win_T *wp; - // If there is only one window there is nothing to remove. if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin) { return NULL; } + frame_T *frp_close = win->w_frame; + // Remove the window from its frame. - frp2 = win_altframe(win, tp); - wp = frame2win(frp2); + frame_T *frp2 = win_altframe(win, tp); + win_T *wp = frame2win(frp2); // Remove this frame from the list of frames. frame_remove(frp_close); @@ -3135,8 +3126,8 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp) // (as close to the closed frame as possible) to distribute the height // to. if (frp2->fr_win != NULL && frp2->fr_win->w_p_wfh) { - frp = frp_close->fr_prev; - frp3 = frp_close->fr_next; + frame_T *frp = frp_close->fr_prev; + frame_T *frp3 = frp_close->fr_next; while (frp != NULL || frp3 != NULL) { if (frp != NULL) { if (!frame_fixed_height(frp)) { @@ -3164,8 +3155,8 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp) // (as close to the closed frame as possible) to distribute the width // to. if (frp2->fr_win != NULL && frp2->fr_win->w_p_wfw) { - frp = frp_close->fr_prev; - frp3 = frp_close->fr_next; + frame_T *frp = frp_close->fr_prev; + frame_T *frp3 = frp_close->fr_next; while (frp != NULL || frp3 != NULL) { if (frp != NULL) { if (!frame_fixed_width(frp)) { @@ -3204,6 +3195,7 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp) // and remove it. frp2->fr_parent->fr_layout = frp2->fr_layout; frp2->fr_parent->fr_child = frp2->fr_child; + frame_T *frp; FOR_ALL_FRAMES(frp, frp2->fr_child) { frp->fr_parent = frp2->fr_parent; } @@ -3229,6 +3221,7 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp) if (frp->fr_prev != NULL) { frp->fr_prev->fr_next = frp->fr_child; } + frame_T *frp3; for (frp3 = frp->fr_child;; frp3 = frp3->fr_next) { frp3->fr_parent = frp2; if (frp3->fr_next == NULL) { @@ -3261,13 +3254,11 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp) /// is left over after "win" is closed. static frame_T *win_altframe(win_T *win, tabpage_T *tp) { - frame_T *frp; - if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin) { return alt_tabpage()->tp_curwin->w_frame; } - frp = win->w_frame; + frame_T *frp = win->w_frame; if (frp->fr_prev == NULL) { return frp->fr_next; @@ -3313,14 +3304,13 @@ static frame_T *win_altframe(win_T *win, tabpage_T *tp) // Return the tabpage that will be used if the current one is closed. static tabpage_T *alt_tabpage(void) { - tabpage_T *tp; - // Use the next tab page if possible. if (curtab->tp_next != NULL) { return curtab->tp_next; } // Find the last but one tab page. + tabpage_T *tp; for (tp = first_tabpage; tp->tp_next != curtab; tp = tp->tp_next) {} return tp; } @@ -3358,8 +3348,7 @@ static bool frame_has_win(const frame_T *frp, const win_T *wp) static bool is_bottom_win(win_T *wp) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - frame_T *frp; - for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) { + for (frame_T *frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) { if (frp->fr_parent->fr_layout == FR_COL && frp->fr_next != NULL) { return false; } @@ -3375,19 +3364,15 @@ static bool is_bottom_win(win_T *wp) void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh) FUNC_ATTR_NONNULL_ALL { - frame_T *frp; - int extra_lines; - int h; - win_T *wp; - if (topfrp->fr_win != NULL) { // Simple case: just one window. - wp = topfrp->fr_win; + win_T *wp = topfrp->fr_win; if (is_bottom_win(wp)) { wp->w_hsep_height = 0; } win_new_height(wp, height - wp->w_hsep_height - wp->w_status_height); } else if (topfrp->fr_layout == FR_ROW) { + frame_T *frp; do { // All frames in this row get the same new height. FOR_ALL_FRAMES(frp, topfrp->fr_child) { @@ -3403,7 +3388,7 @@ void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh) // Complicated case: Resize a column of frames. Resize the bottom // frame first, frames above that when needed. - frp = topfrp->fr_child; + frame_T *frp = topfrp->fr_child; if (wfh) { // Advance past frames with one window with 'wfh' set. while (frame_fixed_height(frp)) { @@ -3426,11 +3411,11 @@ void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh) } } - extra_lines = height - topfrp->fr_height; + int extra_lines = height - topfrp->fr_height; if (extra_lines < 0) { // reduce height of contained frames, bottom or top frame first while (frp != NULL) { - h = frame_minheight(frp, NULL); + int h = frame_minheight(frp, NULL); if (frp->fr_height + extra_lines < h) { extra_lines += frp->fr_height - h; frame_new_height(frp, h, topfirst, wfh); @@ -3533,10 +3518,8 @@ static bool frame_fixed_width(frame_T *frp) // Note: Does not check if there is room! static void frame_add_statusline(frame_T *frp) { - win_T *wp; - if (frp->fr_layout == FR_LEAF) { - wp = frp->fr_win; + win_T *wp = frp->fr_win; if (wp->w_status_height == 0) { if (wp->w_height - STATUS_HEIGHT >= 0) { // don't make it negative wp->w_height -= STATUS_HEIGHT; @@ -3564,15 +3547,11 @@ static void frame_add_statusline(frame_T *frp) /// may cause the width not to be set. static void frame_new_width(frame_T *topfrp, int width, bool leftfirst, bool wfw) { - frame_T *frp; - int extra_cols; - int w; - win_T *wp; - if (topfrp->fr_layout == FR_LEAF) { // Simple case: just one window. - wp = topfrp->fr_win; + win_T *wp = topfrp->fr_win; // Find out if there are any windows right of this one. + frame_T *frp; for (frp = topfrp; frp->fr_parent != NULL; frp = frp->fr_parent) { if (frp->fr_parent->fr_layout == FR_ROW && frp->fr_next != NULL) { break; @@ -3583,6 +3562,7 @@ static void frame_new_width(frame_T *topfrp, int width, bool leftfirst, bool wfw } win_new_width(wp, width - wp->w_vsep_width); } else if (topfrp->fr_layout == FR_COL) { + frame_T *frp; do { // All frames in this column get the same new width. FOR_ALL_FRAMES(frp, topfrp->fr_child) { @@ -3598,7 +3578,7 @@ static void frame_new_width(frame_T *topfrp, int width, bool leftfirst, bool wfw // Complicated case: Resize a row of frames. Resize the rightmost // frame first, frames left of it when needed. - frp = topfrp->fr_child; + frame_T *frp = topfrp->fr_child; if (wfw) { // Advance past frames with one window with 'wfw' set. while (frame_fixed_width(frp)) { @@ -3621,11 +3601,11 @@ static void frame_new_width(frame_T *topfrp, int width, bool leftfirst, bool wfw } } - extra_cols = width - topfrp->fr_width; + int extra_cols = width - topfrp->fr_width; if (extra_cols < 0) { // reduce frame width, rightmost frame first while (frp != NULL) { - w = frame_minwidth(frp, NULL); + int w = frame_minwidth(frp, NULL); if (frp->fr_width + extra_cols < w) { extra_cols += frp->fr_width - w; frame_new_width(frp, w, leftfirst, wfw); @@ -3661,10 +3641,8 @@ static void frame_new_width(frame_T *topfrp, int width, bool leftfirst, bool wfw static void frame_add_vsep(const frame_T *frp) FUNC_ATTR_NONNULL_ARG(1) { - win_T *wp; - if (frp->fr_layout == FR_LEAF) { - wp = frp->fr_win; + win_T *wp = frp->fr_win; if (wp->w_vsep_width == 0) { if (wp->w_width > 0) { // don't make it negative wp->w_width--; @@ -3692,10 +3670,8 @@ static void frame_add_vsep(const frame_T *frp) static void frame_add_hsep(const frame_T *frp) FUNC_ATTR_NONNULL_ARG(1) { - win_T *wp; - if (frp->fr_layout == FR_LEAF) { - wp = frp->fr_win; + win_T *wp = frp->fr_win; if (wp->w_hsep_height == 0) { if (wp->w_height > 0) { // don't make it negative wp->w_height++; @@ -3736,9 +3712,7 @@ static void frame_fix_height(win_T *wp) /// When "next_curwin" is NOWIN, don't use at least one line for the current window. static int frame_minheight(frame_T *topfrp, win_T *next_curwin) { - frame_T *frp; int m; - int n; if (topfrp->fr_win != NULL) { // Combined height of window bar and separator column or status line. @@ -3759,8 +3733,9 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin) } else if (topfrp->fr_layout == FR_ROW) { // get the minimal height from each frame in this row m = 0; + frame_T *frp; FOR_ALL_FRAMES(frp, topfrp->fr_child) { - n = frame_minheight(frp, next_curwin); + int n = frame_minheight(frp, next_curwin); if (n > m) { m = n; } @@ -3768,6 +3743,7 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin) } else { // Add up the minimal heights for all frames in this column. m = 0; + frame_T *frp; FOR_ALL_FRAMES(frp, topfrp->fr_child) { m += frame_minheight(frp, next_curwin); } @@ -3784,8 +3760,7 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin) /// @param next_curwin use p_wh and p_wiw for next_curwin static int frame_minwidth(frame_T *topfrp, win_T *next_curwin) { - frame_T *frp; - int m, n; + int m; if (topfrp->fr_win != NULL) { if (topfrp->fr_win == next_curwin) { @@ -3801,8 +3776,9 @@ static int frame_minwidth(frame_T *topfrp, win_T *next_curwin) } else if (topfrp->fr_layout == FR_COL) { // get the minimal width from each frame in this column m = 0; + frame_T *frp; FOR_ALL_FRAMES(frp, topfrp->fr_child) { - n = frame_minwidth(frp, next_curwin); + int n = frame_minwidth(frp, next_curwin); if (n > m) { m = n; } @@ -3810,6 +3786,7 @@ static int frame_minwidth(frame_T *topfrp, win_T *next_curwin) } else { // Add up the minimal widths for all frames in this row. m = 0; + frame_T *frp; FOR_ALL_FRAMES(frp, topfrp->fr_child) { m += frame_minwidth(frp, next_curwin); } @@ -3827,10 +3804,6 @@ static int frame_minwidth(frame_T *topfrp, win_T *next_curwin) /// @param forceit always hide all other windows void close_others(int message, int forceit) { - win_T *wp; - win_T *nextwp; - int r; - if (curwin->w_floating) { if (message && !autocmd_busy) { emsg(e_floatonly); @@ -3847,14 +3820,15 @@ void close_others(int message, int forceit) } // Be very careful here: autocommands may change the window layout. - for (wp = firstwin; win_valid(wp); wp = nextwp) { + win_T *nextwp; + for (win_T *wp = firstwin; win_valid(wp); wp = nextwp) { nextwp = wp->w_next; if (wp == curwin) { // don't close current window continue; } // Check if it's allowed to abandon this window - r = can_abandon(wp->w_buffer, forceit); + int r = can_abandon(wp->w_buffer, forceit); if (!win_valid(wp)) { // autocommands messed wp up nextwp = firstwin; continue; @@ -3989,11 +3963,9 @@ static tabpage_T *alloc_tabpage(void) void free_tabpage(tabpage_T *tp) { - int idx; - pmap_del(handle_T)(&tabpage_handles, tp->handle); diff_clear(tp); - for (idx = 0; idx < SNAP_COUNT; idx++) { + for (int idx = 0; idx < SNAP_COUNT; idx++) { clear_snapshot(tp, idx); } vars_clear(&tp->tp_vars->dv_hashtab); // free all t: variables @@ -4020,15 +3992,13 @@ void free_tabpage(tabpage_T *tp) int win_new_tabpage(int after, char_u *filename) { tabpage_T *old_curtab = curtab; - tabpage_T *newtp; - int n; if (cmdwin_type != 0) { emsg(_(e_cmdwin)); return FAIL; } - newtp = alloc_tabpage(); + tabpage_T *newtp = alloc_tabpage(); // Remember the current windows in this Tab page. if (leave_tabpage(curbuf, true) == FAIL) { @@ -4053,7 +4023,7 @@ int win_new_tabpage(int after, char_u *filename) if (after > 0) { // Put new tab page before tab page "after". - n = 2; + int n = 2; for (tp = first_tabpage; tp->tp_next != NULL && n < after; tp = tp->tp_next) { n++; @@ -4113,7 +4083,6 @@ int may_open_tabpage(void) int make_tabpages(int maxcount) { int count = maxcount; - int todo; // Limit to 'tabpagemax' tabs. if (count > p_tpm) { @@ -4124,6 +4093,7 @@ int make_tabpages(int maxcount) // when putting the buffers in the windows. block_autocmds(); + int todo; for (todo = count - 1; todo > 0; todo--) { if (win_new_tabpage(0, NULL) == FAIL) { break; @@ -4361,10 +4331,6 @@ static void tabpage_check_windows(tabpage_T *old_curtab) // When "n" is 9999 go to the last tab page. void goto_tabpage(int n) { - tabpage_T *tp = NULL; // shut up compiler - tabpage_T *ttp; - int i; - if (text_locked()) { // Not allowed when editing the command line. text_locked_msg(); @@ -4379,6 +4345,8 @@ void goto_tabpage(int n) return; } + tabpage_T *tp = NULL; // shut up compiler + if (n == 0) { // No count, go to next tab page, wrap around end. if (curtab->tp_next == NULL) { @@ -4389,8 +4357,8 @@ void goto_tabpage(int n) } else if (n < 0) { // "gT": go to previous tab page, wrap around end. "N gT" repeats // this N times. - ttp = curtab; - for (i = n; i < 0; i++) { + tabpage_T *ttp = curtab; + for (int i = n; i < 0; i++) { for (tp = first_tabpage; tp->tp_next != ttp && tp->tp_next != NULL; tp = tp->tp_next) {} ttp = tp; @@ -4462,16 +4430,15 @@ void goto_tabpage_win(tabpage_T *tp, win_T *wp) // Move the current tab page to after tab page "nr". void tabpage_move(int nr) { - int n = 1; - tabpage_T *tp; - tabpage_T *tp_dst; - assert(curtab != NULL); if (first_tabpage->tp_next == NULL) { return; } + int n = 1; + tabpage_T *tp; + for (tp = first_tabpage; tp->tp_next != NULL && n < nr; tp = tp->tp_next) { n++; } @@ -4481,7 +4448,7 @@ void tabpage_move(int nr) return; } - tp_dst = tp; + tabpage_T *tp_dst = tp; // Remove the current tab page from the list of tab pages. if (curtab == first_tabpage) { @@ -4566,20 +4533,17 @@ tabpage_T *win_find_tabpage(win_T *win) /// @return found window win_T *win_vert_neighbor(tabpage_T *tp, win_T *wp, bool up, long count) { - frame_T *fr; - frame_T *nfr; - frame_T *foundfr; - - foundfr = wp->w_frame; + frame_T *foundfr = wp->w_frame; if (wp->w_floating) { return win_valid(prevwin) && !prevwin->w_floating ? prevwin : firstwin; } while (count--) { + frame_T *nfr; // First go upwards in the tree of frames until we find an upwards or // downwards neighbor. - fr = foundfr; + frame_T *fr = foundfr; for (;;) { if (fr == tp->tp_topframe) { goto end; @@ -4645,20 +4609,17 @@ static void win_goto_ver(bool up, long count) /// @return found window win_T *win_horz_neighbor(tabpage_T *tp, win_T *wp, bool left, long count) { - frame_T *fr; - frame_T *nfr; - frame_T *foundfr; - - foundfr = wp->w_frame; + frame_T *foundfr = wp->w_frame; if (wp->w_floating) { return win_valid(prevwin) && !prevwin->w_floating ? prevwin : firstwin; } while (count--) { + frame_T *nfr; // First go upwards in the tree of frames until we find a left or // right neighbor. - fr = foundfr; + frame_T *fr = foundfr; for (;;) { if (fr == tp->tp_topframe) { goto end; @@ -5022,9 +4983,6 @@ void free_wininfo(wininfo_T *wip, buf_T *bp) /// @param tp tab page "win" is in, NULL for current static void win_free(win_T *wp, tabpage_T *tp) { - int i; - wininfo_T *wip; - pmap_del(handle_T)(&window_handles, wp->handle); clearFolding(wp); @@ -5056,7 +5014,7 @@ static void win_free(win_T *wp, tabpage_T *tp) xfree(wp->w_lines); - for (i = 0; i < wp->w_tagstacklen; i++) { + for (int i = 0; i < wp->w_tagstacklen; i++) { xfree(wp->w_tagstack[i].tagname); xfree(wp->w_tagstack[i].user_data); } @@ -5073,7 +5031,7 @@ static void win_free(win_T *wp, tabpage_T *tp) // 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) { + for (wininfo_T *wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) { if (wip->wi_win == wp) { wininfo_T *wip2; @@ -5139,13 +5097,8 @@ void win_free_grid(win_T *wp, bool reinit) // Append window "wp" in the window list after window "after". void win_append(win_T *after, win_T *wp) { - win_T *before; - - if (after == NULL) { // after NULL is in front of the first - before = firstwin; - } else { - before = after->w_next; - } + // after NULL is in front of the first + win_T *before = after == NULL ? firstwin : after->w_next; wp->w_next = before; wp->w_prev = after; @@ -5393,12 +5346,7 @@ void win_reconfig_floats(void) // to the bottom-right position plus one. static void frame_comp_pos(frame_T *topfrp, int *row, int *col) { - win_T *wp; - frame_T *frp; - int startcol; - int startrow; - - wp = topfrp->fr_win; + win_T *wp = topfrp->fr_win; if (wp != NULL) { if (wp->w_winrow != *row || wp->w_wincol != *col) { @@ -5413,8 +5361,9 @@ static void frame_comp_pos(frame_T *topfrp, int *row, int *col) *row += h > topfrp->fr_height ? topfrp->fr_height : h; *col += wp->w_width + wp->w_vsep_width; } else { - startrow = *row; - startcol = *col; + int startrow = *row; + int startcol = *col; + frame_T *frp; FOR_ALL_FRAMES(frp, topfrp->fr_child) { if (topfrp->fr_layout == FR_ROW) { *row = startrow; // all frames are at the same row @@ -5487,14 +5436,6 @@ void win_setheight_win(int height, win_T *win) // At the top level we can also use change the command line height. static void frame_setheight(frame_T *curfrp, int height) { - int room; // total number of lines available - int take; // number of lines taken from other windows - int room_cmdline; // lines available from cmdline - int run; - frame_T *frp; - int h; - int room_reserved; - // If the height already is the desired value, nothing to do. if (curfrp->fr_height == height) { return; @@ -5514,7 +5455,7 @@ static void frame_setheight(frame_T *curfrp, int height) } else if (curfrp->fr_parent->fr_layout == FR_ROW) { // Row of frames: Also need to resize frames left and right of this // one. First check for the minimal height of these. - h = frame_minheight(curfrp->fr_parent, NULL); + int h = frame_minheight(curfrp->fr_parent, NULL); if (height < h) { height = h; } @@ -5522,14 +5463,19 @@ static void frame_setheight(frame_T *curfrp, int height) } else { // Column of frames: try to change only frames in this column. + int room; // total number of lines available + int room_cmdline; // lines available from cmdline + int room_reserved; + // Do this twice: // 1: compute room available, if it's not enough try resizing the // containing frame. // 2: compute the room available and adjust the height to it. // Try not to reduce the height of a window with 'winfixheight' set. - for (run = 1; run <= 2; run++) { + for (int run = 1; run <= 2; run++) { room = 0; room_reserved = 0; + frame_T *frp; FOR_ALL_FRAMES(frp, curfrp->fr_parent->fr_child) { if (frp != curfrp && frp->fr_win != NULL @@ -5566,7 +5512,7 @@ static void frame_setheight(frame_T *curfrp, int height) // Compute the number of lines we will take from others frames (can be // negative!). - take = height - curfrp->fr_height; + int take = height - curfrp->fr_height; // If there is not enough room, also reduce the height of a window // with 'winfixheight' set. @@ -5594,14 +5540,13 @@ static void frame_setheight(frame_T *curfrp, int height) // First take lines from the frames after the current frame. If // that is not enough, takes lines from frames above the current // frame. - for (run = 0; run < 2; run++) { - if (run == 0) { - frp = curfrp->fr_next; // 1st run: start with next window - } else { - frp = curfrp->fr_prev; // 2nd run: start with prev window - } + for (int run = 0; run < 2; run++) { + // 1st run: start with next window + // 2nd run: start with prev window + frame_T *frp = run == 0 ? curfrp->fr_next : curfrp->fr_prev; + while (frp != NULL && take != 0) { - h = frame_minheight(frp, NULL); + int h = frame_minheight(frp, NULL); if (room_reserved > 0 && frp->fr_win != NULL && frp->fr_win->w_p_wfh) { @@ -5675,13 +5620,6 @@ void win_setwidth_win(int width, win_T *wp) // Strategy is similar to frame_setheight(). static void frame_setwidth(frame_T *curfrp, int width) { - int room; // total number of lines available - int take; // number of lines taken from other windows - int run; - frame_T *frp; - int w; - int room_reserved; - // If the width already is the desired value, nothing to do. if (curfrp->fr_width == width) { return; @@ -5695,7 +5633,7 @@ static void frame_setwidth(frame_T *curfrp, int width) if (curfrp->fr_parent->fr_layout == FR_COL) { // Column of frames: Also need to resize frames above and below of // this one. First check for the minimal width of these. - w = frame_minwidth(curfrp->fr_parent, NULL); + int w = frame_minwidth(curfrp->fr_parent, NULL); if (width < w) { width = w; } @@ -5707,9 +5645,13 @@ static void frame_setwidth(frame_T *curfrp, int width) // 1: compute room available, if it's not enough try resizing the // containing frame. // 2: compute the room available and adjust the width to it. - for (run = 1; run <= 2; run++) { + + int room; // total number of lines available + int room_reserved; + for (int run = 1; run <= 2; run++) { room = 0; room_reserved = 0; + frame_T *frp; FOR_ALL_FRAMES(frp, curfrp->fr_parent->fr_child) { if (frp != curfrp && frp->fr_win != NULL @@ -5735,7 +5677,7 @@ static void frame_setwidth(frame_T *curfrp, int width) // Compute the number of lines we will take from others frames (can be // negative!). - take = width - curfrp->fr_width; + int take = width - curfrp->fr_width; // If there is not enough room, also reduce the width of a window // with 'winfixwidth' set. @@ -5754,14 +5696,13 @@ static void frame_setwidth(frame_T *curfrp, int width) // First take lines from the frames right of the current frame. If // that is not enough, takes lines from frames left of the current // frame. - for (run = 0; run < 2; run++) { - if (run == 0) { - frp = curfrp->fr_next; // 1st run: start with next window - } else { - frp = curfrp->fr_prev; // 2nd run: start with prev window - } + for (int run = 0; run < 2; run++) { + // 1st run: start with next window + // 2nd run: start with prev window + frame_T *frp = run == 0 ? curfrp->fr_next : curfrp->fr_prev; + while (frp != NULL && take != 0) { - w = frame_minwidth(frp, NULL); + int w = frame_minwidth(frp, NULL); if (room_reserved > 0 && frp->fr_win != NULL && frp->fr_win->w_p_wfw) { @@ -5837,12 +5778,6 @@ void win_setminwidth(void) /// Status line of dragwin is dragged "offset" lines down (negative is up). void win_drag_status_line(win_T *dragwin, int offset) { - frame_T *curfr; - frame_T *fr; - int room; - int row; - bool up; // if true, drag status line up, otherwise down - int n; static bool p_ch_was_zero = false; // If the user explicitly set 'cmdheight' to zero, then allow for dragging @@ -5851,8 +5786,8 @@ void win_drag_status_line(win_T *dragwin, int offset) p_ch_was_zero = true; } - fr = dragwin->w_frame; - curfr = fr; + frame_T *fr = dragwin->w_frame; + frame_T *curfr = fr; if (fr != topframe) { // more than one window fr = fr->fr_parent; // When the parent frame is not a column of frames, its parent should @@ -5877,8 +5812,10 @@ void win_drag_status_line(win_T *dragwin, int offset) } } - if (offset < 0) { // drag up - up = true; + int room; + const bool up = offset < 0; // if true, drag status line up, otherwise down + + if (up) { // drag up offset = -offset; // sum up the room of the current frame and above it if (fr == curfr) { @@ -5895,7 +5832,6 @@ void win_drag_status_line(win_T *dragwin, int offset) } fr = curfr->fr_next; // put fr at frame that grows } else { // drag down - up = false; // Only dragging the last status line can reduce p_ch. room = Rows - cmdline_row; if (curfr->fr_next != NULL) { @@ -5933,7 +5869,7 @@ void win_drag_status_line(win_T *dragwin, int offset) } // Now make the other frames smaller. while (fr != NULL && offset > 0) { - n = frame_minheight(fr, NULL); + int n = frame_minheight(fr, NULL); if (fr->fr_height - offset <= n) { offset -= fr->fr_height - n; frame_new_height(fr, n, !up, false); @@ -5947,7 +5883,7 @@ void win_drag_status_line(win_T *dragwin, int offset) fr = fr->fr_next; } } - row = win_comp_pos(); + int row = win_comp_pos(); grid_fill(&default_grid, row, cmdline_row, 0, Columns, ' ', ' ', 0); if (msg_grid.chars) { clear_cmdline = true; @@ -5967,17 +5903,11 @@ void win_drag_status_line(win_T *dragwin, int offset) // Separator line of dragwin is dragged "offset" lines right (negative is left). void win_drag_vsep_line(win_T *dragwin, int offset) { - frame_T *curfr; - frame_T *fr; - int room; - bool left; // if true, drag separator line left, otherwise right - int n; - - fr = dragwin->w_frame; + frame_T *fr = dragwin->w_frame; if (fr == topframe) { // only one window (cannot happen?) return; } - curfr = fr; + frame_T *curfr = fr; fr = fr->fr_parent; // When the parent frame is not a row of frames, its parent should be. if (fr->fr_layout != FR_ROW) { @@ -6002,8 +5932,10 @@ void win_drag_vsep_line(win_T *dragwin, int offset) } } - if (offset < 0) { // drag left - left = true; + int room; + const bool left = offset < 0; // if true, drag separator line left, otherwise right + + if (left) { // drag left offset = -offset; // sum up the room of the current frame and left of it room = 0; @@ -6015,7 +5947,6 @@ void win_drag_vsep_line(win_T *dragwin, int offset) } fr = curfr->fr_next; // put fr at frame that grows } else { // drag right - left = false; // sum up the room of frames right of the current one room = 0; FOR_ALL_FRAMES(fr, curfr->fr_next) { @@ -6050,7 +5981,7 @@ void win_drag_vsep_line(win_T *dragwin, int offset) fr = curfr->fr_next; // next frame gets smaller } while (fr != NULL && offset > 0) { - n = frame_minwidth(fr, NULL); + int n = frame_minwidth(fr, NULL); if (fr->fr_width - offset <= n) { offset -= fr->fr_width - n; frame_new_width(fr, n, !left, false); @@ -6199,8 +6130,6 @@ void win_new_height(win_T *wp, int height) void scroll_to_fraction(win_T *wp, int prev_height) { - linenr_T lnum; - int sline, line_size; int height = wp->w_height_inner; // Don't change w_topline in any of these cases: @@ -6214,13 +6143,13 @@ void scroll_to_fraction(win_T *wp, int prev_height) || wp->w_topline > 1)) { // Find a value for w_topline that shows the cursor at the same // relative position in the window as before (more or less). - lnum = wp->w_cursor.lnum; + linenr_T lnum = wp->w_cursor.lnum; if (lnum < 1) { // can happen when starting up lnum = 1; } wp->w_wrow = (int)((long)wp->w_fraction * (long)height - 1L) / FRACTION_MULT; - line_size = plines_win_col(wp, lnum, (long)(wp->w_cursor.col)) - 1; - sline = wp->w_wrow - line_size; + int line_size = plines_win_col(wp, lnum, (long)(wp->w_cursor.col)) - 1; + int sline = wp->w_wrow - line_size; if (sline >= 0) { // Make sure the whole cursor line is visible, if possible. @@ -6399,8 +6328,6 @@ void win_comp_scroll(win_T *wp) /// command_height: called whenever p_ch has been changed. void command_height(void) { - int h; - frame_T *frp; int old_p_ch = (int)curtab->tp_ch_used; // Use the value of p_ch that we remembered. This is needed for when the @@ -6419,7 +6346,7 @@ void command_height(void) } // Find bottom frame with width of screen. - frp = lastwin_nofloating()->w_frame; + frame_T *frp = lastwin_nofloating()->w_frame; while (frp->fr_width != Columns && frp->fr_parent != NULL) { frp = frp->fr_parent; } @@ -6442,7 +6369,7 @@ void command_height(void) cmdline_row = Rows - (int)p_ch; break; } - h = frp->fr_height - frame_minheight(frp, NULL); + int h = frp->fr_height - frame_minheight(frp, NULL); if (h > p_ch - old_p_ch) { h = (int)p_ch - old_p_ch; } @@ -6538,13 +6465,8 @@ char_u *file_name_at_cursor(int options, long count, linenr_T *file_lnum) char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u *rel_fname, linenr_T *file_lnum) { - char *ptr; - size_t len; - bool in_type = true; - bool is_url = false; - // search forward for what could be the start of a file name - ptr = (char *)line + col; + char *ptr = (char *)line + col; while (*ptr != NUL && !vim_isfilec((uint8_t)(*ptr))) { MB_PTR_ADV(ptr); } @@ -6555,6 +6477,10 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u return NULL; } + size_t len; + bool in_type = true; + bool is_url = false; + // Search backward for first char of the file name. // Go one char back to ":" before "//" even when ':' is not in 'isfname'. while ((char_u *)ptr > line) { @@ -6600,14 +6526,13 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u } if (file_lnum != NULL) { - char *p; const char *line_english = " line "; const char *line_transl = _(line_msg); // Get the number after the file name and a separator character. // Also accept " line 999" with and without the same translation as // used in last_set_msg(). - p = ptr + len; + char *p = ptr + len; if (STRNCMP(p, line_english, strlen(line_english)) == 0) { p += strlen(line_english); } else if (STRNCMP(p, line_transl, strlen(line_transl)) == 0) { @@ -6722,11 +6647,8 @@ static bool resize_frame_for_winbar(frame_T *fr) static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global) { - frame_T *fp; - win_T *wp; - if (fr->fr_layout == FR_LEAF) { - wp = fr->fr_win; + win_T *wp = fr->fr_win; bool is_last = is_bottom_win(wp); if (is_last) { @@ -6756,6 +6678,7 @@ static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global) } } else { // For a column or row frame, recursively call this function for all child frames + frame_T *fp; FOR_ALL_FRAMES(fp, fr->fr_child) { last_status_rec(fp, statusline, is_stl_global); } @@ -7036,13 +6959,11 @@ static win_T *get_snapshot_curwin(int idx) /// @param close_curwin closing current window void restore_snapshot(int idx, int close_curwin) { - win_T *wp; - if (curtab->tp_snapshot[idx] != NULL && curtab->tp_snapshot[idx]->fr_width == topframe->fr_width && curtab->tp_snapshot[idx]->fr_height == topframe->fr_height && check_snapshot_rec(curtab->tp_snapshot[idx], topframe) == OK) { - wp = restore_snapshot_rec(curtab->tp_snapshot[idx], topframe); + win_T *wp = restore_snapshot_rec(curtab->tp_snapshot[idx], topframe); (void)win_comp_pos(); if (wp != NULL && close_curwin) { win_goto(wp); @@ -7075,7 +6996,6 @@ static int check_snapshot_rec(frame_T *sn, frame_T *fr) static win_T *restore_snapshot_rec(frame_T *sn, frame_T *fr) { win_T *wp = NULL; - win_T *wp2; fr->fr_height = sn->fr_height; fr->fr_width = sn->fr_width; @@ -7085,13 +7005,13 @@ static win_T *restore_snapshot_rec(frame_T *sn, frame_T *fr) wp = sn->fr_win; } if (sn->fr_next != NULL) { - wp2 = restore_snapshot_rec(sn->fr_next, fr->fr_next); + win_T *wp2 = restore_snapshot_rec(sn->fr_next, fr->fr_next); if (wp2 != NULL) { wp = wp2; } } if (sn->fr_child != NULL) { - wp2 = restore_snapshot_rec(sn->fr_child, fr->fr_child); + win_T *wp2 = restore_snapshot_rec(sn->fr_child, fr->fr_child); if (wp2 != NULL) { wp = wp2; } @@ -7260,17 +7180,14 @@ static int int_cmp(const void *a, const void *b) /// @return error message, NULL if it's OK. char *check_colorcolumn(win_T *wp) { - char *s; - int col; - unsigned int count = 0; - int color_cols[256]; - int j = 0; - if (wp->w_buffer == NULL) { return NULL; // buffer was closed } - for (s = wp->w_p_cc; *s != NUL && count < 255;) { + unsigned int count = 0; + int color_cols[256]; + for (char *s = wp->w_p_cc; *s != NUL && count < 255;) { + int col; if (*s == '-' || *s == '+') { // -N and +N: add to 'textwidth' col = (*s == '-') ? -1 : 1; @@ -7319,6 +7236,7 @@ skip: // win_line() qsort(color_cols, count, sizeof(int), int_cmp); + int j = 0; for (unsigned int i = 0; i < count; i++) { // skip duplicates if (j == 0 || wp->w_p_cc_cols[j - 1] != color_cols[i]) { @@ -7390,10 +7308,10 @@ void win_get_tabwin(handle_T id, int *tabnr, int *winnr) void win_id2tabwin(typval_T *const argvars, typval_T *const rettv) { - int winnr = 1; - int tabnr = 1; handle_T id = (handle_T)tv_get_number(&argvars[0]); + int winnr = 1; + int tabnr = 1; win_get_tabwin(id, &tabnr, &winnr); list_T *const list = tv_list_alloc_ret(rettv, 2); @@ -7449,12 +7367,11 @@ void win_findbuf(typval_T *argvars, list_T *list) // Get the layout of the given tab page for winlayout(). void get_framelayout(const frame_T *fr, list_T *l, bool outer) { - list_T *fr_list; - if (fr == NULL) { return; } + list_T *fr_list; if (outer) { // outermost call from f_winlayout() fr_list = l; -- cgit From 22473672aa1ce005d3841d0838a21cd6c6b721f7 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 19 Oct 2022 07:05:54 +0800 Subject: vim-patch:9.0.0788: ModeChanged autocmd not executed when Visual ends with CTRL-C (#20722) Problem: ModeChanged autocmd not executed when Visual mode is ended with CTRL-C. Solution: Do not trigger the autocmd when got_int is set. (closes vim/vim#11394) https://github.com/vim/vim/commit/61c4b04799bf114cadc3bbf212ae8b2ad22a6980 Cherry-pick removal of cmdwin feature check from patch 9.0.0663. --- src/nvim/plines.c | 6 ++++++ src/nvim/state.c | 4 +++- src/nvim/testdir/test_autocmd.vim | 36 +++++++++++++++++++++++------------- 3 files changed, 32 insertions(+), 14 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index cbde0cfff9..42218ac847 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -37,6 +37,9 @@ /// Functions calculating vertical size of text when displayed inside a window. /// Calls horizontal size functions defined below. +/// Return the number of window lines occupied by buffer line "lnum". +/// Includes any filler lines. +/// /// @param winheight when true limit to window height int plines_win(win_T *wp, linenr_T lnum, bool winheight) { @@ -71,6 +74,9 @@ bool win_may_fill(win_T *wp) return (wp->w_p_diff && diffopt_filler()) || wp->w_buffer->b_virt_line_blocks; } +/// Return the number of window lines occupied by buffer line "lnum". +/// Does not include filler lines. +/// /// @param winheight when true limit to window height int plines_win_nofill(win_T *wp, linenr_T lnum, bool winheight) { diff --git a/src/nvim/state.c b/src/nvim/state.c index 7712fcd39a..460a9dd637 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -229,7 +229,9 @@ void get_mode(char *buf) /// Fires a ModeChanged autocmd if appropriate. void may_trigger_modechanged(void) { - if (!has_event(EVENT_MODECHANGED)) { + // Skip this when got_int is set, the autocommand will not be executed. + // Better trigger it next time. + if (!has_event(EVENT_MODECHANGED) || got_int) { return; } diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 63ed3ff435..f98d7d10ab 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -3198,19 +3198,29 @@ func Test_mode_changes() call assert_equal(5, g:nori_to_any) endif - if has('cmdwin') - let g:n_to_c = 0 - au ModeChanged n:c let g:n_to_c += 1 - let g:c_to_n = 0 - au ModeChanged c:n let g:c_to_n += 1 - let g:mode_seq += ['c', 'n', 'c', 'n'] - call feedkeys("q:\\", 'tnix') - call assert_equal(len(g:mode_seq) - 1, g:index) - call assert_equal(2, g:n_to_c) - call assert_equal(2, g:c_to_n) - unlet g:n_to_c - unlet g:c_to_n - endif + let g:n_to_c = 0 + au ModeChanged n:c let g:n_to_c += 1 + let g:c_to_n = 0 + au ModeChanged c:n let g:c_to_n += 1 + let g:mode_seq += ['c', 'n', 'c', 'n'] + call feedkeys("q:\\", 'tnix') + call assert_equal(len(g:mode_seq) - 1, g:index) + call assert_equal(2, g:n_to_c) + call assert_equal(2, g:c_to_n) + unlet g:n_to_c + unlet g:c_to_n + + let g:n_to_v = 0 + au ModeChanged n:v let g:n_to_v += 1 + let g:v_to_n = 0 + au ModeChanged v:n let g:v_to_n += 1 + let g:mode_seq += ['v', 'n'] + call feedkeys("v\", 'tnix') + call assert_equal(len(g:mode_seq) - 1, g:index) + call assert_equal(1, g:n_to_v) + call assert_equal(1, g:v_to_n) + unlet g:n_to_v + unlet g:v_to_n au! ModeChanged delfunc TestMode -- cgit From 66933b45dcff8cc9f323a71583bca3698566abb9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 19 Oct 2022 07:10:22 +0800 Subject: vim-patch:9.0.0789: dummy buffer ends up in a window Problem: Dummy buffer ends up in a window. Solution: Disallow navigating to a dummy buffer. https://github.com/vim/vim/commit/8f3c3c6cd044e3b5bf08dbfa3b3f04bb3f711bad --- src/nvim/buffer.c | 5 +++++ src/nvim/testdir/test_autocmd.vim | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+) (limited to 'src/nvim') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 84ff2fa59b..8016904702 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1224,6 +1224,11 @@ int do_buffer(int action, int start, int dir, int count, int forceit) } return FAIL; } + if ((action == DOBUF_GOTO || action == DOBUF_SPLIT) && (buf->b_flags & BF_DUMMY)) { + // disallow navigating to the dummy buffer + semsg(_(e_nobufnr), count); + return FAIL; + } // delete buffer "buf" from memory and/or the list if (unload) { diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index f98d7d10ab..e869353893 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -3269,4 +3269,24 @@ func Test_noname_autocmd() augroup! test_noname_autocmd_group endfunc +func Test_autocmd_split_dummy() + " Autocommand trying to split a window containing a dummy buffer. + auto BufReadPre * exe "sbuf " .. expand("") + " Avoid the "W11" prompt + au FileChangedShell * let v:fcs_choice = 'reload' + func Xautocmd_changelist() + cal writefile(['Xtestfile2:4:4'], 'Xerr') + edit Xerr + lex 'Xtestfile2:4:4' + endfunc + call Xautocmd_changelist() + call assert_fails('call Xautocmd_changelist()', 'E86:') + + au! BufReadPre + au! FileChangedShell + delfunc Xautocmd_changelist + bwipe! Xerr + call delete('Xerr') +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 88eeb4d941a4ae3f75bbf4faae2882786e44f687 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 19 Oct 2022 07:15:08 +0800 Subject: vim-patch:9.0.0790: test for dummy buffer does not always produce the E86 error Problem: Test for dummy buffer does not always produce the E86 error. Solution: Do not check if the error is produced. https://github.com/vim/vim/commit/53c5c9f50ca68d3ed559eebb2c5f7d23f39a768c --- src/nvim/testdir/test_autocmd.vim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index e869353893..8c15249f97 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -3280,7 +3280,8 @@ func Test_autocmd_split_dummy() lex 'Xtestfile2:4:4' endfunc call Xautocmd_changelist() - call assert_fails('call Xautocmd_changelist()', 'E86:') + " Should get E86, but it doesn't always happen (timing?) + silent! call Xautocmd_changelist() au! BufReadPre au! FileChangedShell -- cgit From a5d893bebdf13c224b363edace4778a75305c909 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 19 Oct 2022 09:24:18 +0800 Subject: revert: "oldtests: wait 200ms on mac for timer test" (#20728) This reverts commit 3bad76008e1c98724eca7d986a6340eff1de8193. --- src/nvim/testdir/test_timers.vim | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index b3a22614b0..4d450e180b 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -46,9 +46,6 @@ endfunc func Test_timer_repeat_many() let g:val = 0 let timer = timer_start(50, 'MyHandler', {'repeat': -1}) - if has('mac') - sleep 200m - endif sleep 200m call timer_stop(timer) call assert_inrange((has('mac') ? 1 : 2), LoadAdjust(5), g:val) -- cgit From 96cf385a7f4ab29f6987c10b5c3625d99b22f6fc Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 19 Oct 2022 11:32:26 +0800 Subject: vim-patch:9.0.0739: mouse column not correctly used for popup_setpos (#20729) Problem: Mouse column not correctly used for popup_setpos. Solution: Adjust off-by-one error and handle Visual line selection properly. (Yee Cheng Chin, closes vim/vim#11356) https://github.com/vim/vim/commit/17822c507c03d509037c9ee5eee5cfbb201b3f01 The test_termcodes.vim test cannot be used. Use a Lua test instead. --- src/nvim/mouse.c | 7 +++---- src/nvim/normal.c | 15 +++++++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 7b267d6ce4..734ece73b4 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -31,7 +31,9 @@ static linenr_T orig_topline = 0; static int orig_topfill = 0; -/// Translate window coordinates to buffer position without any side effects +/// Translate window coordinates to buffer position without any side effects. +/// Returns IN_BUFFER and sets "mpos->col" to the column when in buffer text. +/// The column is one for the first column. int get_fpos_of_mouse(pos_T *mpos) { int grid = mouse_grid; @@ -67,9 +69,6 @@ int get_fpos_of_mouse(pos_T *mpos) mpos->col = vcol2col(wp, mpos->lnum, col); - if (mpos->col > 0) { - mpos->col--; - } mpos->coladd = 0; return IN_BUFFER; } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 1ac8d7013e..b7febe2d51 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1820,10 +1820,17 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent) } else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER) { jump_flags = MOUSE_MAY_STOP_VIS; } else { - if ((lt(curwin->w_cursor, VIsual) - && (lt(m_pos, curwin->w_cursor) || lt(VIsual, m_pos))) - || (lt(VIsual, curwin->w_cursor) - && (lt(m_pos, VIsual) || lt(curwin->w_cursor, m_pos)))) { + if (VIsual_mode == 'V') { + if ((curwin->w_cursor.lnum <= VIsual.lnum + && (m_pos.lnum < curwin->w_cursor.lnum || VIsual.lnum < m_pos.lnum)) + || (VIsual.lnum < curwin->w_cursor.lnum + && (m_pos.lnum < VIsual.lnum || curwin->w_cursor.lnum < m_pos.lnum))) { + jump_flags = MOUSE_MAY_STOP_VIS; + } + } else if ((ltoreq(curwin->w_cursor, VIsual) + && (lt(m_pos, curwin->w_cursor) || lt(VIsual, m_pos))) + || (lt(VIsual, curwin->w_cursor) + && (lt(m_pos, VIsual) || lt(curwin->w_cursor, m_pos)))) { jump_flags = MOUSE_MAY_STOP_VIS; } else if (VIsual_mode == Ctrl_V) { getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol); -- cgit From fad558b6affd54075654dd55922348f76a95e338 Mon Sep 17 00:00:00 2001 From: ObserverOfTime Date: Wed, 19 Oct 2022 20:08:01 +0300 Subject: vim-patch:9.0.0798: clang format configuration files are not recognized (#20741) Problem: Clang format configuration files are not recognized. Solution: Use yaml for Clang format configuration files. (Marwin Glaser, closes vim/vim#11398) https://github.com/vim/vim/commit/3c708c43908ba44f075bbaa7daf584c6b46d9723 --- src/nvim/testdir/test_filetype.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index c9ec7771f4..44711e3fcc 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -642,7 +642,7 @@ let s:filename_checks = { \ 'xsd': ['file.xsd'], \ 'xslt': ['file.xsl', 'file.xslt'], \ 'yacc': ['file.yy', 'file.yxx', 'file.y++'], - \ 'yaml': ['file.yaml', 'file.yml', '.clang-tidy'], + \ 'yaml': ['file.yaml', 'file.yml', '.clang-format', '.clang-tidy'], \ 'yang': ['file.yang'], \ 'z8a': ['file.z8a'], \ 'zig': ['file.zig'], -- cgit From e33995e936c57064bf5629f6b527bfc1b77f77f8 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Thu, 20 Oct 2022 13:03:41 +0200 Subject: fix(build): duplicate version string "v0.8.0-v0.8.0" #20578 - Prevent duplicate version strings such as v0.8.0-v0.8.0. - Change the format for git releases from v0.9.0-dev-67-g625ba79be to v0.9.0-dev-67+g625ba79be. Nvim versions are now: release : v0.9.0 prerelease without git info: v0.9.0-dev prerelease with git info : v0.9.0-dev-67+g625ba79be --- src/nvim/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 2960e6d9d2..ac27f46bfb 100755 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -206,9 +206,7 @@ add_custom_target(update_version_stamp -DNVIM_VERSION_PATCH=${NVIM_VERSION_PATCH} -DNVIM_VERSION_PRERELEASE=${NVIM_VERSION_PRERELEASE} -DOUTPUT=${NVIM_VERSION_GIT_H} - -DCMAKE_MESSAGE_LOG_LEVEL=${CMAKE_MESSAGE_LOG_LEVEL} -P ${PROJECT_SOURCE_DIR}/cmake/GenerateVersion.cmake - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} BYPRODUCTS ${NVIM_VERSION_GIT_H}) # NVIM_GENERATED_FOR_HEADERS: generated headers to be included in headers -- cgit From be0f284ae1c9e986ac47487ce32e39e318a0d194 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 20 Oct 2022 21:50:27 +0800 Subject: vim-patch:partial:8.2.0418: code in eval.c not sufficiently covered by tests Problem: Code in eval.c not sufficiently covered by tests. Solution: Add more tests. (Yegappan Lakshmanan, closes vim/vim#5815) https://github.com/vim/vim/commit/8b633135106dda8605463b780573c45b00c22afe Only port test_expr.vim and the first hunk of test_cmdline.vim. Add missing test from patch 7.4.1755. Cherry-pick test_expr.vim change from patch 8.2.2060. --- src/nvim/testdir/test_cmdline.vim | 29 ----------- src/nvim/testdir/test_expr.vim | 104 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 29 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 3f8e141afa..f37e8be59a 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -349,35 +349,6 @@ func Test_highlight_completion() call assert_equal([], getcompletion('A', 'highlight')) endfunc -func Test_expr_completion() - if !has('cmdline_compl') - return - endif - for cmd in [ - \ 'let a = ', - \ 'const a = ', - \ 'if', - \ 'elseif', - \ 'while', - \ 'for', - \ 'echo', - \ 'echon', - \ 'execute', - \ 'echomsg', - \ 'echoerr', - \ 'call', - \ 'return', - \ 'cexpr', - \ 'caddexpr', - \ 'cgetexpr', - \ 'lexpr', - \ 'laddexpr', - \ 'lgetexpr'] - call feedkeys(":" . cmd . " getl\\\"\", 'xt') - call assert_equal('"' . cmd . ' getline(', getreg(':')) - endfor -endfunc - func Test_getcompletion() if !has('cmdline_compl') return diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 15622cd6fe..8d3fb88541 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -1,5 +1,7 @@ " Tests for expressions. +source check.vim + func Test_equal() let base = {} func base.method() @@ -76,6 +78,17 @@ func Test_strcharpart() call assert_equal('', strcharpart('axb', -2, 2)) call assert_equal('a', strcharpart('axb', -1, 2)) + + call assert_equal('edit', "editor"[-10:3]) +endfunc + +func Test_getreg_empty_list() + call assert_equal('', getreg('x')) + call assert_equal([], getreg('x', 1, 1)) + let x = getreg('x', 1, 1) + let y = x + call add(x, 'foo') + call assert_equal(['foo'], y) endfunc func Test_loop_over_null_list() @@ -529,6 +542,7 @@ func Test_function_with_funcref() call assert_fails("call function('foo()')", 'E475:') call assert_fails("call function('foo()')", 'foo()') + call assert_fails("function('')", 'E129:') endfunc func Test_funcref() @@ -587,3 +601,93 @@ func Test_eval_after_if() if 0 | eval SetVal('a') | endif | call SetVal('b') call assert_equal('b', s:val) endfunc + +" Test for command-line completion of expressions +func Test_expr_completion() + CheckFeature cmdline_compl + for cmd in [ + \ 'let a = ', + \ 'const a = ', + \ 'if', + \ 'elseif', + \ 'while', + \ 'for', + \ 'echo', + \ 'echon', + \ 'execute', + \ 'echomsg', + \ 'echoerr', + \ 'call', + \ 'return', + \ 'cexpr', + \ 'caddexpr', + \ 'cgetexpr', + \ 'lexpr', + \ 'laddexpr', + \ 'lgetexpr'] + call feedkeys(":" . cmd . " getl\\\"\", 'xt') + call assert_equal('"' . cmd . ' getline(', getreg(':')) + endfor + + " completion for the expression register + call feedkeys(":\"\=float2\t\"\\"\", 'xt') + call assert_equal('"float2nr("', @=) + + " completion for window local variables + let w:wvar1 = 10 + let w:wvar2 = 10 + call feedkeys(":echo w:wvar\\\"\", 'xt') + call assert_equal('"echo w:wvar1 w:wvar2', @:) + unlet w:wvar1 w:wvar2 + + " completion for tab local variables + let t:tvar1 = 10 + let t:tvar2 = 10 + call feedkeys(":echo t:tvar\\\"\", 'xt') + call assert_equal('"echo t:tvar1 t:tvar2', @:) + unlet t:tvar1 t:tvar2 + + " completion for variables + let g:tvar1 = 1 + let g:tvar2 = 2 + call feedkeys(":let g:tv\\\"\", 'xt') + call assert_equal('"let g:tvar1 g:tvar2', @:) + " completion for variables after a || + call feedkeys(":echo 1 || g:tv\\\"\", 'xt') + call assert_equal('"echo 1 || g:tvar1 g:tvar2', @:) + + " completion for options + call feedkeys(":echo &compat\\\"\", 'xt') + call assert_equal('"echo &compatible', @:) + call feedkeys(":echo 1 && &compat\\\"\", 'xt') + call assert_equal('"echo 1 && &compatible', @:) + call feedkeys(":echo &g:equala\\\"\", 'xt') + call assert_equal('"echo &g:equalalways', @:) + + " completion for string + call feedkeys(":echo \"Hello\\ World\"\\\"\", 'xt') + call assert_equal("\"echo \"Hello\\ World\"\", @:) + call feedkeys(":echo 'Hello World'\\\"\", 'xt') + call assert_equal("\"echo 'Hello World'\", @:) + + " completion for command after a | + call feedkeys(":echo 'Hello' | cwin\\\"\", 'xt') + call assert_equal("\"echo 'Hello' | cwindow", @:) + + " completion for environment variable + let $X_VIM_TEST_COMPLETE_ENV = 'foo' + call feedkeys(":let $X_VIM_TEST_COMPLETE_E\\\"\", 'tx') + call assert_match('"let $X_VIM_TEST_COMPLETE_ENV', @:) + unlet $X_VIM_TEST_COMPLETE_ENV +endfunc + +" Test for errors in expression evaluation +func Test_expr_eval_error() + call assert_fails("let i = 'abc' . []", 'E730:') + call assert_fails("let l = [] + 10", 'E745:') + call assert_fails("let v = 10 + []", 'E745:') + call assert_fails("let v = 10 / []", 'E745:') + call assert_fails("let v = -{}", 'E728:') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab -- cgit From d1484b58ae877f1423f37092c7bfdacedccfb455 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 20 Oct 2022 22:05:51 +0800 Subject: vim-patch:9.0.0804: crash when trying to divide a number by -1 Problem: Crash when trying to divice the largest negative number by -1. Solution: Handle this case specifically. https://github.com/vim/vim/commit/cdef1cefa2a440911c727558562f83ed9b00e16b --- src/nvim/eval.c | 4 ++++ src/nvim/testdir/test_expr.vim | 6 ++++++ 2 files changed, 10 insertions(+) (limited to 'src/nvim') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d9d276e8ea..245ad8d9d8 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -330,6 +330,10 @@ varnumber_T num_divide(varnumber_T n1, varnumber_T n2) } else { result = VARNUMBER_MAX; } + } else if (n1 == VARNUMBER_MIN && n2 == -1) { + // specific case: trying to do VARNUMBAR_MIN / -1 results in a positive + // number that doesn't fit in varnumber_T and causes an FPE + result = VARNUMBER_MAX; } else { result = n1 / n2; } diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 8d3fb88541..c63a969e50 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -602,6 +602,12 @@ func Test_eval_after_if() call assert_equal('b', s:val) endfunc +func Test_divide_by_zero() + " only tests that this doesn't crash, the result is not important + echo 0 / 0 + echo 0 / 0 / -1 +endfunc + " Test for command-line completion of expressions func Test_expr_completion() CheckFeature cmdline_compl -- cgit From 45ae5c6dc5af63957eb2009e0425d91a3cc79d66 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Thu, 20 Oct 2022 22:49:22 +0200 Subject: vim-patch:9.0.0808: jsonnet filetype detection has a typo (#20753) Problem: jsonnet filetype detection has a typo. Solution: Change "libjsonnet" to "libsonnet". (Maxime Brunet, closes vim/vim#11412) https://github.com/vim/vim/commit/6c8bc37a1083d17447156592f6f52da2d40b4855 --- src/nvim/testdir/test_filetype.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 44711e3fcc..3888a384c7 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -291,7 +291,7 @@ let s:filename_checks = { \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.babelrc', '.eslintrc', '.prettierrc', '.firebaserc', 'file.slnf'], \ 'json5': ['file.json5'], \ 'jsonc': ['file.jsonc'], - \ 'jsonnet': ['file.jsonnet', 'file.libjsonnet'], + \ 'jsonnet': ['file.jsonnet', 'file.libsonnet'], \ 'jsp': ['file.jsp'], \ 'julia': ['file.jl'], \ 'kconfig': ['Kconfig', 'Kconfig.debug', 'Kconfig.file'], -- cgit From a288b4f21423efb056061e4da3871a4247a7de79 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 21 Oct 2022 06:32:15 +0800 Subject: vim-patch:9.0.0806: 'langmap' works differently when there are modifiers (#20754) Problem: 'langmap' works differently when there are modifiers. Solution: Only apply 'langmap' to a character where modifiers have no effect. (closes vim/vim#11395, closes vim/vim#11404) https://github.com/vim/vim/commit/49660f5139d3fd55326a54eadf6bb31a3ffec2bf --- src/nvim/getchar.c | 22 +++++++++++++++++----- src/nvim/testdir/test_langmap.vim | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 5 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 16f3c477f6..ca8e07bba3 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1959,16 +1959,28 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) if (mp->m_keys[0] == tb_c1 && (mp->m_mode & local_State) && ((mp->m_mode & MODE_LANGMAP) == 0 || typebuf.tb_maplen == 0)) { int nomap = nolmaplen; - int c2; + int modifiers = 0; // find the match length of this mapping for (mlen = 1; mlen < typebuf.tb_len; mlen++) { - c2 = typebuf.tb_buf[typebuf.tb_off + mlen]; + int c2 = typebuf.tb_buf[typebuf.tb_off + mlen]; if (nomap > 0) { + if (nomap == 2 && c2 == KS_MODIFIER) { + modifiers = 1; + } else if (nomap == 1 && modifiers == 1) { + modifiers = c2; + } nomap--; - } else if (c2 == K_SPECIAL) { - nomap = 2; } else { - LANGMAP_ADJUST(c2, true); + if (c2 == K_SPECIAL) { + nomap = 2; + } else if (merge_modifiers(c2, &modifiers) == c2) { + // Only apply 'langmap' if merging modifiers into + // the key will not result in another character, + // so that 'langmap' behaves consistently in + // different terminals and GUIs. + LANGMAP_ADJUST(c2, true); + } + modifiers = 0; } if (mp->m_keys[mlen] != c2) { break; diff --git a/src/nvim/testdir/test_langmap.vim b/src/nvim/testdir/test_langmap.vim index 4f831aa40b..2284704603 100644 --- a/src/nvim/testdir/test_langmap.vim +++ b/src/nvim/testdir/test_langmap.vim @@ -49,6 +49,41 @@ func Test_langmap() call feedkeys(';', 'tx') call assert_equal(5, col('.')) + set langmap=RL + let g:counter = 0 + nnoremap L;L let g:counter += 1 + nnoremap throw 'This mapping shoud not be triggered' + + " 'langmap' is applied to keys without modifiers when matching a mapping + call feedkeys('R;R', 'tx') + call assert_equal(1, g:counter) + nunmap L;L + unlet g:counter + + delete + call assert_equal('', getline(1)) + undo + call assert_equal('Hello World', getline(1)) + " 'langmap' does not change Ctrl-R to Ctrl-L for consistency + call feedkeys("\<*C-R>", 'tx') + call assert_equal('', getline(1)) + + set langmap=6L + undo + setlocal bufhidden=hide + let oldbuf = bufnr() + enew + call assert_notequal(oldbuf, bufnr()) + " 'langmap' does not change Ctrl-6 to Ctrl-L for consistency + " Ctrl-6 becomes Ctrl-^ after merging the Ctrl modifier + call feedkeys("\<*C-6>", 'tx') + call assert_equal(oldbuf, bufnr()) + setlocal bufhidden& + + nunmap + set langmap& quit! endfunc + +" vim: shiftwidth=2 sts=2 expandtab -- cgit From 24c9561a68aa9b9cf8d8a503c5e85f63d7ebb543 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 21 Oct 2022 06:56:09 -0400 Subject: vim-patch: bump VIM_VERSION from 8.0 => 8.1 #20762 There are 6 remaining 8.0.x patches, tracked in: https://github.com/neovim/neovim/issues/5431 --- src/nvim/version.c | 1132 +++++++++++++++++++++++++++++++++++++++------------- src/nvim/version.h | 2 +- 2 files changed, 854 insertions(+), 280 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/version.c b/src/nvim/version.c index f0e2f92ba4..963ca66344 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -71,13 +71,587 @@ static char *features[] = { // clang-format off static const int included_patches[] = { - 1850, + 2424, + 2423, + 2422, + 2421, + // 2420, + 2419, + // 2418, + 2417, + 2416, + // 2415, + 2414, + 2413, + 2412, + 2411, + 2410, + 2409, + 2408, + 2407, + 2406, + 2405, + 2404, + // 2403, + 2402, + 2401, + 2400, + // 2399, + 2398, + 2397, + 2396, + 2395, + 2394, + 2393, + 2392, + 2391, + 2390, + 2389, + 2388, + 2387, + // 2386, + 2385, + 2384, + 2383, + 2382, + // 2381, + 2380, + 2379, + 2378, + 2377, + 2376, + 2375, + 2374, + // 2373, + 2372, + // 2371, + 2370, + // 2369, + 2368, + 2367, + 2366, + 2365, + 2364, + 2363, + // 2362, + 2361, + 2360, + 2359, + 2358, + 2357, + 2356, + 2355, + 2354, + 2353, + 2352, + // 2351, + 2350, + 2349, + 2348, + 2347, + 2346, + 2345, + 2344, + 2343, + 2342, + 2341, + 2340, + 2339, + 2338, + // 2337, + 2336, + 2335, + // 2334, + 2333, + 2332, + 2331, + 2330, + 2329, + 2328, + 2327, + 2326, + 2325, + // 2324, + 2323, + 2322, + 2321, + 2320, + 2319, + 2318, + 2317, + 2316, + 2315, + 2314, + 2313, + 2312, + 2311, + 2310, + 2309, + // 2308, + // 2307, + 2306, + 2305, + 2304, + 2303, + 2302, + 2301, + // 2300, + // 2299, + // 2298, + 2297, + // 2296, + // 2295, + 2294, + 2293, + // 2292, + 2291, + 2290, + 2289, + // 2288, + // 2287, + // 2286, + 2285, + 2284, + 2283, + 2282, + 2281, + 2280, + // 2279, + 2278, + // 2277, + // 2276, + 2275, + 2274, + // 2273, + 2272, + 2271, + 2270, + 2269, + 2268, + 2267, + // 2266, + // 2265, + 2264, + 2263, + 2262, + 2261, + 2260, + 2259, + 2258, + 2257, + 2256, + 2255, + 2254, + 2253, + 2252, + // 2251, + // 2250, + 2249, + 2248, + 2247, + 2246, + 2245, + 2244, + 2243, + 2242, + // 2241, + // 2240, + 2239, + 2238, + 2237, + 2236, + 2235, + 2234, + 2233, + // 2232, + 2231, + // 2230, + 2229, + 2228, + 2227, + 2226, + 2225, + 2224, + 2223, + 2222, + 2221, + 2220, + // 2219, + 2218, + 2217, + // 2216, + // 2215, + 2214, + // 2213, + 2212, + // 2211, + // 2210, + 2209, + // 2208, + 2207, + 2206, + 2205, + 2204, + 2203, + 2202, + 2201, + // 2200, + 2199, + 2198, + 2197, + 2196, + // 2195, + // 2194, + // 2193, + // 2192, + 2191, + 2190, + // 2189, + 2188, + 2187, + 2186, + 2185, + 2184, + 2183, + 2182, + // 2181, + 2180, + 2179, + 2178, + 2177, + // 2176, + 2175, + 2174, + 2173, + 2172, + 2171, + 2170, + 2169, + 2168, + 2167, + 2166, + 2165, + // 2164, + 2163, + 2162, + 2161, + 2160, + 2159, + // 2158, + 2157, + // 2156, + // 2155, + 2154, + // 2153, + 2152, + 2151, + 2150, + 2149, + // 2148, + 2147, + // 2146, + 2145, + 2144, + 2143, + // 2142, + 2141, + 2140, + // 2139, + 2138, + 2137, + 2136, + 2135, + 2134, + 2133, + 2132, + 2131, + 2130, + 2129, + 2128, + // 2127, + 2126, + 2125, + 2124, + 2123, + 2122, + // 2121, + 2120, + 2119, + 2118, + 2117, + 2116, + 2115, + // 2114, + 2113, + 2112, + 2111, + // 2110, + // 2109, + 2108, + // 2107, + 2106, + 2105, + 2104, + 2103, + 2102, + 2101, + 2100, + // 2099, + 2098, + 2097, + 2096, + 2095, + // 2094, + // 2093, + // 2092, + 2091, + 2090, + 2089, + 2088, + 2087, + 2086, + // 2085, + 2084, + 2083, + 2082, + 2081, + // 2080, + 2079, + 2078, + // 2077, + // 2076, + 2075, + 2074, + 2073, + 2072, + // 2071, + // 2070, + // 2069, + // 2068, + // 2067, + // 2066, + 2065, + 2064, + 2063, + // 2062, + 2061, + 2060, + 2059, + 2058, + 2057, + 2056, + 2055, + 2054, + // 2053, + 2052, + 2051, + 2050, + 2049, + // 2048, + // 2047, + // 2046, + 2045, + // 2044, + 2043, + 2042, + 2041, + // 2040, + // 2039, + 2038, + 2037, + 2036, + 2035, + 2034, + 2033, + // 2032, + 2031, + 2030, + 2029, + 2028, + 2027, + 2026, + 2025, + 2024, + 2023, + // 2022, + // 2021, + 2020, + 2019, + 2018, + 2017, + 2016, + 2015, + 2014, + 2013, + 2012, + 2011, + 2010, + // 2009, + // 2008, + 2007, + 2006, + 2005, + // 2004, + 2003, + 2002, + // 2001, + 2000, + // 1999, + // 1998, + // 1997, + // 1996, + 1995, + 1994, + // 1993, + 1992, + 1991, + 1990, + // 1989, + 1988, + // 1987, + // 1986, + // 1985, + 1984, + 1983, + // 1982, + // 1981, + 1980, + // 1979, + // 1978, + 1977, + 1976, + 1975, + 1974, + 1973, + 1972, + 1971, + 1970, + // 1969, + // 1968, + 1967, + 1966, + 1965, + // 1964, + // 1963, + 1962, + 1961, + 1960, + // 1959, + 1958, + // 1957, + 1956, + 1955, + // 1954, + 1953, + 1952, + 1951, + 1950, + // 1949, + 1948, + 1947, + 1946, + // 1945, + // 1944, + // 1943, + 1942, + 1941, + // 1940, + // 1939, + 1938, + 1937, + // 1936, + 1935, + // 1934, + 1933, + 1932, + 1931, + 1930, + // 1929, + // 1928, + 1927, + 1926, + 1925, + 1924, + 1923, + 1922, + 1921, + // 1920, + // 1919, + // 1918, + // 1917, + 1916, + 1915, + 1914, + 1913, + 1912, + 1911, + 1910, + 1909, + // 1908, + // 1907, + // 1906, + // 1905, + // 1904, + 1903, + // 1902, + 1901, + 1900, + 1899, + 1898, + 1897, + 1896, + 1895, + 1894, + 1893, + // 1892, + // 1891, + 1890, + 1889, + 1888, + 1887, + 1886, + 1885, + // 1884, + 1883, + // 1882, + 1881, + // 1880, + 1879, + 1878, + 1877, + 1876, + 1875, + // 1874, + 1873, + 1872, + // 1871, + 1870, + 1869, + 1868, + 1867, + // 1866, + 1865, + 1864, + 1863, + 1862, + 1861, + 1860, + 1859, + 1858, + 1857, + 1856, + 1855, + 1854, + // 1853, + 1852, + // 1851, + // 1850, 1849, 1848, 1847, 1846, - 1845, - 1844, + // 1845, + // 1844, 1843, 1842, 1841, @@ -94,66 +668,66 @@ static const int included_patches[] = { 1830, 1829, 1828, - 1827, - 1826, + // 1827, + // 1826, 1825, 1824, 1823, 1822, 1821, 1820, - 1819, + // 1819, 1818, 1817, 1816, 1815, - 1814, - 1813, + // 1814, + // 1813, 1812, - 1811, - 1810, + // 1811, + // 1810, 1809, 1808, 1807, 1806, 1805, - // 1804, + 1804, 1803, - 1802, + // 1802, 1801, 1800, - 1799, + // 1799, 1798, 1797, 1796, 1795, - // 1794, + 1794, 1793, 1792, 1791, 1790, - 1789, + // 1789, 1788, - 1787, - 1786, + // 1787, + // 1786, 1785, - 1784, + // 1784, 1783, 1782, 1781, 1780, - 1779, - 1778, + // 1779, + // 1778, 1777, 1776, 1775, 1774, - 1773, + // 1773, 1772, 1771, - 1770, + // 1770, 1769, - 1768, + // 1768, 1767, 1766, 1765, @@ -166,34 +740,34 @@ static const int included_patches[] = { 1758, 1757, 1756, - 1755, - 1754, - 1753, - 1752, - 1751, + // 1755, + // 1754, + // 1753, + // 1752, + // 1751, 1750, 1749, 1748, 1747, 1746, 1745, - // 1744, - // 1743, + 1744, + 1743, 1742, 1741, 1740, 1739, 1738, 1737, - 1736, + // 1736, 1735, 1734, - 1733, - // 1732, + // 1733, + 1732, 1731, - 1730, + // 1730, 1729, - 1728, + // 1728, 1727, 1726, 1725, @@ -202,27 +776,27 @@ static const int included_patches[] = { 1722, 1721, 1720, - 1719, - 1718, + // 1719, + // 1718, 1717, 1716, 1715, - 1714, - 1713, + // 1714, + // 1713, 1712, - 1711, - 1710, - 1709, + // 1711, + // 1710, + // 1709, 1708, - 1707, + // 1707, 1706, 1705, 1704, - 1703, + // 1703, 1702, 1701, - 1700, - 1699, + // 1700, + // 1699, 1698, 1697, 1696, @@ -231,7 +805,7 @@ static const int included_patches[] = { 1693, 1692, 1691, - 1690, + // 1690, 1689, 1688, 1687, @@ -240,140 +814,140 @@ static const int included_patches[] = { 1684, 1683, 1682, - 1681, + // 1681, 1680, 1679, - 1678, + // 1678, 1677, - 1676, - 1675, + // 1676, + // 1675, 1674, - 1673, + // 1673, 1672, 1671, 1670, 1669, 1668, 1667, - 1666, - 1665, + // 1666, + // 1665, 1664, - 1663, + // 1663, 1662, 1661, 1660, - 1659, + // 1659, 1658, 1657, - 1656, - 1655, + // 1656, + // 1655, 1654, 1653, 1652, 1651, 1650, - 1649, + // 1649, 1648, 1647, - 1646, + // 1646, 1645, 1644, 1643, 1642, - 1641, + // 1641, 1640, 1639, 1638, 1637, - 1636, + // 1636, 1635, 1634, 1633, 1632, 1631, 1630, - 1629, - 1628, + // 1629, + // 1628, 1627, - 1626, + // 1626, 1625, 1624, 1623, - 1622, + // 1622, 1621, - 1620, + // 1620, 1619, 1618, - 1617, + // 1617, 1616, - 1615, + // 1615, 1614, 1613, - 1612, + // 1612, 1611, 1610, - 1609, + // 1609, 1608, - 1607, + // 1607, 1606, 1605, 1604, - 1603, - 1602, + // 1603, + // 1602, 1601, - 1600, - 1599, + // 1600, + // 1599, 1598, - 1597, - 1596, + // 1597, + // 1596, 1595, 1594, 1593, - // 1592, + 1592, 1591, 1590, - 1589, + // 1589, 1588, 1587, - 1586, + // 1586, 1585, - 1584, - 1583, + // 1584, + // 1583, 1582, 1581, - 1580, + // 1580, 1579, 1578, - 1577, + // 1577, 1576, 1575, - 1574, - 1573, + // 1574, + // 1573, 1572, - 1571, + // 1571, 1570, 1569, 1568, 1567, 1566, - 1565, + // 1565, 1564, 1563, - 1562, - 1561, - 1560, - 1559, - 1558, + // 1562, + // 1561, + // 1560, + // 1559, + // 1558, 1557, 1556, - 1555, + // 1555, 1554, - 1553, + // 1553, 1552, - 1551, - 1550, + // 1551, + // 1550, 1549, - 1548, + // 1548, 1547, 1546, 1545, @@ -383,59 +957,59 @@ static const int included_patches[] = { 1541, 1540, 1539, - 1538, - 1537, + // 1538, + // 1537, 1536, 1535, - 1534, + // 1534, 1533, 1532, 1531, 1530, 1529, 1528, - 1527, - 1526, - 1525, + // 1527, + // 1526, + // 1525, 1524, - 1523, - 1522, - 1521, - 1520, + // 1523, + // 1522, + // 1521, + // 1520, 1519, - 1518, - 1517, + // 1518, + // 1517, 1516, - 1515, + // 1515, 1514, - 1513, + // 1513, 1512, - 1511, + // 1511, 1510, 1509, - 1508, + // 1508, 1507, 1506, 1505, 1504, 1503, - 1502, + // 1502, 1501, 1500, - 1499, + // 1499, 1498, - 1497, - 1496, - 1495, - 1494, - 1493, + // 1497, + // 1496, + // 1495, + // 1494, + // 1493, 1492, 1491, 1490, 1489, 1488, 1487, - 1486, + // 1486, 1485, 1484, 1483, @@ -448,7 +1022,7 @@ static const int included_patches[] = { 1476, 1475, 1474, - 1473, + // 1473, 1472, 1471, 1470, @@ -458,83 +1032,83 @@ static const int included_patches[] = { 1466, 1465, 1464, - 1463, + // 1463, 1462, 1461, - 1460, - 1459, + // 1460, + // 1459, 1458, 1457, 1456, - 1455, + // 1455, 1454, - 1453, - 1452, - 1451, - 1450, - 1449, - 1448, - 1447, - 1446, - 1445, - 1444, - 1443, - 1442, - 1441, + // 1453, + // 1452, + // 1451, + // 1450, + // 1449, + // 1448, + // 1447, + // 1446, + // 1445, + // 1444, + // 1443, + // 1442, + // 1441, 1440, 1439, - 1438, + // 1438, 1437, 1436, 1435, 1434, 1433, - 1432, - 1431, - 1430, - 1429, - 1428, - 1427, - 1426, + // 1432, + // 1431, + // 1430, + // 1429, + // 1428, + // 1427, + // 1426, 1425, 1424, - 1423, - 1422, - 1421, - 1420, - 1419, + // 1423, + // 1422, + // 1421, + // 1420, + // 1419, 1418, - 1417, - 1416, + // 1417, + // 1416, 1415, - 1414, - 1413, + // 1414, + // 1413, 1412, 1411, - 1410, + // 1410, 1409, - 1408, - 1407, - 1406, - 1405, + // 1408, + // 1407, + // 1406, + // 1405, 1404, 1403, - 1402, + // 1402, 1401, - 1400, - 1399, + // 1400, + // 1399, 1398, 1397, - 1396, - 1395, + // 1396, + // 1395, 1394, 1393, 1392, - 1391, + // 1391, 1390, - 1389, - 1388, - 1387, + // 1389, + // 1388, + // 1387, 1386, 1385, 1384, @@ -545,7 +1119,7 @@ static const int included_patches[] = { 1379, 1378, 1377, - 1376, + // 1376, 1375, 1374, 1373, @@ -557,12 +1131,12 @@ static const int included_patches[] = { 1367, 1366, 1365, - 1364, + // 1364, 1363, 1362, 1361, 1360, - 1359, + // 1359, 1358, 1357, 1356, @@ -570,38 +1144,38 @@ static const int included_patches[] = { 1354, 1353, 1352, - 1351, + // 1351, 1350, 1349, 1348, 1347, 1346, 1345, - 1344, - 1343, + // 1344, + // 1343, 1342, - 1341, - 1340, + // 1341, + // 1340, 1339, 1338, - 1337, + // 1337, 1336, - 1335, + // 1335, 1334, - 1333, - 1332, + // 1333, + // 1332, 1331, 1330, - 1329, - 1328, + // 1329, + // 1328, 1327, - 1326, + // 1326, 1325, 1324, 1323, 1322, - 1321, - 1320, + // 1321, + // 1320, 1319, 1318, 1317, @@ -621,7 +1195,7 @@ static const int included_patches[] = { 1303, 1302, 1301, - // 1300, + 1300, 1299, 1298, 1297, @@ -641,11 +1215,11 @@ static const int included_patches[] = { 1283, 1282, 1281, - 1280, + // 1280, 1279, - 1278, + // 1278, 1277, - 1276, + // 1276, 1275, 1274, 1273, @@ -654,15 +1228,15 @@ static const int included_patches[] = { 1270, 1269, 1268, - 1267, + // 1267, 1266, - 1265, + // 1265, 1264, 1263, 1262, 1261, 1260, - 1259, + // 1259, 1258, 1257, 1256, @@ -695,15 +1269,15 @@ static const int included_patches[] = { 1229, 1228, 1227, - 1226, + // 1226, 1225, - 1224, + // 1224, 1223, 1222, 1221, 1220, 1219, - 1218, + // 1218, 1217, 1216, 1215, @@ -721,7 +1295,7 @@ static const int included_patches[] = { 1203, 1202, 1201, - 1200, + // 1200, 1199, 1198, 1197, @@ -785,10 +1359,10 @@ static const int included_patches[] = { 1139, 1138, 1137, - 1136, + // 1136, 1135, 1134, - 1133, + // 1133, 1132, 1131, 1130, @@ -815,10 +1389,10 @@ static const int included_patches[] = { 1109, 1108, 1107, - 1106, + // 1106, 1105, 1104, - 1103, + // 1103, 1102, 1101, 1100, @@ -831,14 +1405,14 @@ static const int included_patches[] = { 1093, 1092, 1091, - 1090, + // 1090, 1089, 1088, 1087, 1086, 1085, 1084, - 1083, + // 1083, 1082, 1081, 1080, @@ -877,8 +1451,8 @@ static const int included_patches[] = { 1047, 1046, 1045, - 1044, - 1043, + // 1044, + // 1043, 1042, 1041, 1040, @@ -886,14 +1460,14 @@ static const int included_patches[] = { 1038, 1037, 1036, - 1035, + // 1035, 1034, 1033, 1032, 1031, 1030, 1029, - 1028, + // 1028, 1027, 1026, 1025, @@ -902,7 +1476,7 @@ static const int included_patches[] = { 1022, 1021, 1020, - 1019, + // 1019, 1018, 1017, 1016, @@ -917,7 +1491,7 @@ static const int included_patches[] = { 1007, 1006, 1005, - 1004, + // 1004, 1003, 1002, 1001, @@ -944,15 +1518,15 @@ static const int included_patches[] = { 980, 979, 978, - 977, + // 977, 976, 975, 974, 973, 972, 971, - 970, - 969, + // 970, + // 969, 968, 967, 966, @@ -968,7 +1542,7 @@ static const int included_patches[] = { 956, 955, 954, - 953, + // 953, 952, 951, 950, @@ -981,7 +1555,7 @@ static const int included_patches[] = { 943, 942, 941, - 940, + // 940, 939, 938, 937, @@ -993,7 +1567,7 @@ static const int included_patches[] = { 931, 930, 929, - 928, + // 928, 927, 926, 925, @@ -1003,19 +1577,19 @@ static const int included_patches[] = { 921, 920, 919, - 918, + // 918, 917, 916, 915, - 914, + // 914, 913, 912, 911, 910, - 909, + // 909, 908, 907, - 906, + // 906, 905, 904, 903, @@ -1026,12 +1600,12 @@ static const int included_patches[] = { 898, 897, 896, - 895, - 894, + // 895, + // 894, 893, 892, 891, - 890, + // 890, 889, 888, 887, @@ -1041,30 +1615,30 @@ static const int included_patches[] = { 883, 882, 881, - 880, - 879, + // 880, + // 879, 878, 877, - 876, + // 876, 875, 874, 873, 872, 871, - 870, + // 870, 869, 868, 867, 866, 865, 864, - 863, + // 863, 862, 861, 860, 859, 858, - 857, + // 857, 856, 855, 854, @@ -1076,7 +1650,7 @@ static const int included_patches[] = { 848, 847, 846, - 845, + // 845, 844, 843, 842, @@ -1097,16 +1671,16 @@ static const int included_patches[] = { 827, 826, 825, - 824, + // 824, 823, 822, 821, - 820, + // 820, 819, 818, 817, 816, - 815, + // 815, 814, 813, 812, @@ -1151,16 +1725,16 @@ static const int included_patches[] = { 773, 772, 771, - 770, + // 770, 769, - 768, + // 768, 767, 766, 765, 764, 763, 762, - 761, + // 761, 760, 759, 758, @@ -1211,14 +1785,14 @@ static const int included_patches[] = { 713, 712, 711, - 710, + // 710, 709, 708, - 707, + // 707, 706, 705, 704, - 703, + // 703, 702, 701, 700, @@ -1227,26 +1801,26 @@ static const int included_patches[] = { 697, 696, 695, - 694, + // 694, 693, 692, - 691, - 690, - 689, - 688, + // 691, + // 690, + // 689, + // 688, 687, 686, 685, - 684, + // 684, 683, - 682, - 681, + // 682, + // 681, 680, 679, 678, 677, - 676, - 675, + // 676, + // 675, 674, 673, 672, @@ -1254,11 +1828,11 @@ static const int included_patches[] = { 670, 669, 668, - 667, + // 667, 666, - 665, + // 665, 664, - 663, + // 663, 662, 661, 660, @@ -1266,8 +1840,8 @@ static const int included_patches[] = { 658, 657, 656, - 655, - 654, + // 655, + // 654, 653, 652, 651, @@ -1278,16 +1852,16 @@ static const int included_patches[] = { 646, 645, 644, - 643, + // 643, 642, 641, 640, - 639, - 638, + // 639, + // 638, 637, - 636, + // 636, 635, - 634, + // 634, 633, 632, 631, @@ -1309,7 +1883,7 @@ static const int included_patches[] = { 615, 614, 613, - 612, + // 612, 611, 610, 609, @@ -1320,7 +1894,7 @@ static const int included_patches[] = { 604, 603, 602, - 601, + // 601, 600, 599, 598, @@ -1339,10 +1913,10 @@ static const int included_patches[] = { 585, 584, 583, - 582, + // 582, 581, 580, - 579, + // 579, 578, 577, 576, @@ -1496,7 +2070,7 @@ static const int included_patches[] = { 428, 427, 426, - 425, + // 425, 424, 423, 422, @@ -1554,7 +2128,7 @@ static const int included_patches[] = { 370, 369, 368, - 367, + // 367, 366, 365, 364, @@ -1593,7 +2167,7 @@ static const int included_patches[] = { 331, 330, 329, - 328, + // 328, 327, 326, 325, @@ -1609,7 +2183,7 @@ static const int included_patches[] = { 315, 314, 313, - 312, + // 312, 311, 310, 309, @@ -1617,7 +2191,7 @@ static const int included_patches[] = { 307, 306, 305, - 304, + // 304, 303, 302, 301, @@ -1763,7 +2337,7 @@ static const int included_patches[] = { 161, 160, 159, - 158, + // 158, 157, 156, 155, @@ -1834,7 +2408,7 @@ static const int included_patches[] = { 90, 89, 88, - 87, + // 87, 86, 85, 84, @@ -1849,21 +2423,21 @@ static const int included_patches[] = { 75, 74, 73, - 72, + // 72, 71, 70, 69, 68, 67, 66, - 65, + // 65, 64, 63, - 62, + // 62, 61, 60, 59, - 58, + // 58, 57, 56, 55, @@ -1871,8 +2445,8 @@ static const int included_patches[] = { 53, 52, 51, - 50, - 49, + // 50, + // 49, 48, 47, 46, @@ -1921,7 +2495,7 @@ static const int included_patches[] = { 3, 2, 1, - 0, + // 0, }; // clang-format on diff --git a/src/nvim/version.h b/src/nvim/version.h index a19e863152..484350edee 100644 --- a/src/nvim/version.h +++ b/src/nvim/version.h @@ -14,7 +14,7 @@ extern char *longVersion; // Values that change for a new release #define VIM_VERSION_MAJOR 8 -#define VIM_VERSION_MINOR 0 +#define VIM_VERSION_MINOR 1 // Values based on the above #define VIM_VERSION_MAJOR_STR STR(VIM_VERSION_MAJOR) -- cgit From 784e498c4a9c1f03266ced5ec3f55c3a6c94b80d Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Fri, 21 Oct 2022 14:47:44 +0200 Subject: refactor: clang-tidy fixes to silence clangd warning (#20683) * refactor: readability-uppercase-literal-suffix * refactor: readability-named-parameter * refactor: bugprone-suspicious-string-compare * refactor: google-readability-casting * refactor: readability-redundant-control-flow * refactor: bugprone-too-small-loop-variable * refactor: readability-non-const-parameter * refactor: readability-avoid-const-params-in-decls * refactor: google-readability-todo * refactor: readability-inconsistent-declaration-parameter-name * refactor: bugprone-suspicious-missing-comma * refactor: remove noisy or slow warnings --- src/nvim/api/private/defs.h | 2 +- src/nvim/api/ui.c | 2 +- src/nvim/api/vim.c | 4 ++-- src/nvim/ascii.h | 14 +++++++------- src/nvim/buffer.h | 8 ++++---- src/nvim/cmdexpand.c | 2 +- src/nvim/drawline.c | 7 +++++-- src/nvim/eval.c | 8 ++++---- src/nvim/eval/encode.h | 12 ++++++------ src/nvim/eval/funcs.c | 12 ++++++------ src/nvim/eval/typval.c | 2 +- src/nvim/eval/typval.h | 40 ++++++++++++++++++++-------------------- src/nvim/eval/typval_encode.h | 2 +- src/nvim/eval/userfunc.c | 2 +- src/nvim/ex_cmds.c | 2 +- src/nvim/ex_docmd.c | 14 +++++++------- src/nvim/ex_session.c | 2 +- src/nvim/fileio.c | 4 ++-- src/nvim/getchar.c | 4 ++-- src/nvim/hardcopy.c | 2 +- src/nvim/indent.c | 10 +++++----- src/nvim/insexpand.c | 5 +++-- src/nvim/locale.c | 2 +- src/nvim/mapping.c | 2 +- src/nvim/mark.h | 9 +++++---- src/nvim/mbyte.c | 2 +- src/nvim/memline.c | 22 +++++++++++----------- src/nvim/message.c | 2 +- src/nvim/normal.c | 2 +- src/nvim/option.c | 4 ++-- src/nvim/os/fileio.h | 4 ++-- src/nvim/os/time.c | 8 ++++---- src/nvim/profile.c | 4 ++-- src/nvim/quickfix.c | 4 ++-- src/nvim/runtime.c | 2 +- src/nvim/sha256.c | 2 +- src/nvim/shada.c | 12 ++++++------ src/nvim/sign.c | 4 ++-- src/nvim/spell.c | 8 ++++---- src/nvim/spellfile.c | 24 ++++++++++-------------- src/nvim/spellsuggest.c | 5 +++-- src/nvim/statusline.c | 6 +++--- src/nvim/syntax.c | 2 +- src/nvim/tag.c | 10 ++++------ src/nvim/terminal.c | 2 +- src/nvim/textobject.c | 2 +- src/nvim/ui.c | 4 ++-- src/nvim/ui_compositor.c | 2 +- src/nvim/usercmd.c | 2 +- src/nvim/version.c | 4 ++-- src/nvim/viml/parser/parser.h | 26 +++++++++++++------------- src/nvim/window.c | 2 +- 52 files changed, 172 insertions(+), 172 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 2ae3ee6c7c..8693751c97 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -48,7 +48,7 @@ typedef enum { /// Internal call from lua code #define LUA_INTERNAL_CALL (VIML_INTERNAL_CALL + 1) -static inline bool is_internal_call(const uint64_t channel_id) +static inline bool is_internal_call(uint64_t channel_id) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_CONST; /// Check whether call is internal diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index e6d8cb2fdb..f251e0043f 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -845,7 +845,7 @@ static void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startc for (size_t i = 0; i < ncells; i++) { repeat++; if (i == ncells - 1 || attrs[i] != attrs[i + 1] - || strcmp(chunk[i], chunk[i + 1])) { + || strcmp(chunk[i], chunk[i + 1]) != 0) { if (UI_BUF_SIZE - BUF_POS(data) < 2 * (1 + 2 + sizeof(schar_T) + 5 + 5)) { // close to overflowing the redraw buffer. finish this event, // flush, and start a new "grid_line" event at the current position. diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 0c0c71b694..32d52bef46 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1012,7 +1012,7 @@ Integer nvim_open_term(Buffer buffer, DictionaryOf(LuaRef) opts, Error *err) return (Integer)chan->id; } -static void term_write(char *buf, size_t size, void *data) +static void term_write(char *buf, size_t size, void *data) // NOLINT(readability-non-const-parameter) { Channel *chan = data; LuaRef cb = chan->stream.internal.cb; @@ -2125,7 +2125,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * bool use_tabline = false; bool highlights = false; - if (str.size < 2 || memcmp(str.data, "%!", 2)) { + if (str.size < 2 || memcmp(str.data, "%!", 2) != 0) { const char *const errmsg = check_stl_option(str.data); if (errmsg) { api_set_error(err, kErrorTypeValidation, "%s", errmsg); diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h index b1241166bf..96d6fe214a 100644 --- a/src/nvim/ascii.h +++ b/src/nvim/ascii.h @@ -84,31 +84,31 @@ # define PATHSEPSTR "/" #endif -static inline bool ascii_iswhite(int) +static inline bool ascii_iswhite(int c) REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; -static inline bool ascii_iswhite_or_nul(int) +static inline bool ascii_iswhite_or_nul(int c) REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; -static inline bool ascii_isdigit(int) +static inline bool ascii_isdigit(int c) REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; -static inline bool ascii_isxdigit(int) +static inline bool ascii_isxdigit(int c) REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; -static inline bool ascii_isident(int) +static inline bool ascii_isident(int c) REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; -static inline bool ascii_isbdigit(int) +static inline bool ascii_isbdigit(int c) REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; -static inline bool ascii_isspace(int) +static inline bool ascii_isspace(int c) REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h index d56a70dc7e..ae3d6cc117 100644 --- a/src/nvim/buffer.h +++ b/src/nvim/buffer.h @@ -69,8 +69,8 @@ EXTERN char *msg_qflist INIT(= N_("[Quickfix List]")); # include "buffer.h.generated.h" #endif -static inline void buf_set_changedtick(buf_T *const buf, - const varnumber_T changedtick) +static inline void buf_set_changedtick(buf_T *buf, + varnumber_T changedtick) REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE; /// Set b:changedtick, also checking b: for consistency in debug build @@ -102,7 +102,7 @@ static inline void buf_set_changedtick(buf_T *const buf, const varnumber_T chang } } -static inline varnumber_T buf_get_changedtick(const buf_T *const buf) +static inline varnumber_T buf_get_changedtick(const buf_T *buf) REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; @@ -116,7 +116,7 @@ static inline varnumber_T buf_get_changedtick(const buf_T *const buf) return buf->changedtick_di.di_tv.vval.v_number; } -static inline void buf_inc_changedtick(buf_T *const buf) +static inline void buf_inc_changedtick(buf_T *buf) REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE; /// Increment b:changedtick value diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index a8a5848c71..47027ae1e7 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -1880,7 +1880,7 @@ void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline /// @param col position of cursor /// @param matchcount return: nr of matches /// @param matches return: array of pointers to matches -int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char ***matches) +int expand_cmdline(expand_T *xp, const char_u *str, int col, int *matchcount, char ***matches) { char_u *file_str = NULL; int options = WILD_ADD_SLASH|WILD_SILENT; diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 6735551dc7..08b6fd89af 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -2212,7 +2212,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, // flag to indicate whether prevcol equals startcol of search_hl or // one of the matches bool prevcol_hl_flag = get_prevcol_hl_flag(wp, &screen_search_hl, - (long)(ptr - line) - 1); + (long)(ptr - line) - 1); // NOLINT(google-readability-casting) // Invert at least one char, used for Visual and empty line or // highlight match at end of line. If it's beyond the last @@ -2248,7 +2248,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, if (area_attr == 0 && !has_fold) { // Use attributes from match with highest priority among // 'search_hl' and the match list. - get_search_match_hl(wp, &screen_search_hl, (long)(ptr - line), &char_attr); + get_search_match_hl(wp, + &screen_search_hl, + (long)(ptr - line), // NOLINT(google-readability-casting) + &char_attr); } int eol_attr = char_attr; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 245ad8d9d8..69bc26b82e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -489,7 +489,7 @@ void eval_clear(void) /// Set an internal variable to a string value. Creates the variable if it does /// not already exist. -void set_internal_string_var(const char *name, char *value) +void set_internal_string_var(const char *name, char *value) // NOLINT(readability-non-const-parameter) FUNC_ATTR_NONNULL_ARG(1) { typval_T tv = { @@ -4225,11 +4225,11 @@ bool garbage_collect(bool testing) // history items (ShaDa additional elements) if (p_hi) { - for (uint8_t i = 0; i < HIST_COUNT; i++) { + for (HistoryType i = 0; i < HIST_COUNT; i++) { const void *iter = NULL; do { histentry_T hist; - iter = hist_iter(iter, i, false, &hist); + iter = hist_iter(iter, (uint8_t)i, false, &hist); if (hist.hisstr != NULL) { ABORTING(set_ref_list)(hist.additional_elements, copyID); } @@ -8311,7 +8311,7 @@ repeat: /// "flags" can be "g" to do a global substitute. /// /// @return an allocated string, NULL for error. -char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags) +char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char *flags) { int sublen; regmatch_T regmatch; diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h index 8755ff48ac..9d311fe356 100644 --- a/src/nvim/eval/encode.h +++ b/src/nvim/eval/encode.h @@ -15,9 +15,9 @@ /// @param[in] objname Object name, used for error message. /// /// @return OK in case of success, FAIL otherwise. -int encode_vim_to_msgpack(msgpack_packer *const packer, - typval_T *const tv, - const char *const objname); +int encode_vim_to_msgpack(msgpack_packer *packer, + typval_T *tv, + const char *objname); /// Convert VimL value to :echo output /// @@ -26,9 +26,9 @@ int encode_vim_to_msgpack(msgpack_packer *const packer, /// @param[in] objname Object name, used for error message. /// /// @return OK in case of success, FAIL otherwise. -int encode_vim_to_echo(garray_T *const packer, - typval_T *const tv, - const char *const objname); +int encode_vim_to_echo(garray_T *packer, + typval_T *tv, + const char *objname); /// Structure defining state for read_from_list() typedef struct { diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 5073ab8f1b..f66ff7b5bb 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -5866,8 +5866,8 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) char buf[(IOSIZE/256) * 256]; // rounded to avoid odd + 1 int io_size = sizeof(buf); char *prev = NULL; // previously read bytes, if any - long prevlen = 0; // length of data in prev - long prevsize = 0; // size of prev buffer + ptrdiff_t prevlen = 0; // length of data in prev + ptrdiff_t prevsize = 0; // size of prev buffer long maxline = MAXLNUM; if (argvars[1].v_type != VAR_UNKNOWN) { @@ -6022,17 +6022,17 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) // small, to avoid repeatedly 'allocing' large and // 'reallocing' small. if (prevsize == 0) { - prevsize = (long)(p - start); + prevsize = p - start; } else { - long grow50pc = (prevsize * 3) / 2; - long growmin = (long)((p - start) * 2 + prevlen); + ptrdiff_t grow50pc = (prevsize * 3) / 2; + ptrdiff_t growmin = (p - start) * 2 + prevlen; prevsize = grow50pc > growmin ? grow50pc : growmin; } prev = xrealloc(prev, (size_t)prevsize); } // Add the line part to end of "prev". memmove(prev + prevlen, start, (size_t)(p - start)); - prevlen += (long)(p - start); + prevlen += p - start; } } // while diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index a0b06aaaf4..6bb390d793 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1361,7 +1361,7 @@ void tv_list_reverse(list_T *const l) /// true list will not be modified. Must be initialized to false /// by the caller. void tv_list_item_sort(list_T *const l, ListSortItem *const ptrs, - const ListSorter item_compare_func, bool *errp) + const ListSorter item_compare_func, const bool *errp) FUNC_ATTR_NONNULL_ARG(3, 4) { const int len = tv_list_len(l); diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 0a4adc1f53..e4cd76e8a6 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -90,7 +90,7 @@ static inline void list_log(const list_T *const l, const listitem_T *const li1, #define TV_DICT_HI2DI(hi) \ ((dictitem_T *)((hi)->hi_key - offsetof(dictitem_T, di_key))) -static inline void tv_list_ref(list_T *const l) +static inline void tv_list_ref(list_T *l) REAL_FATTR_ALWAYS_INLINE; /// Increase reference count for a given list @@ -106,7 +106,7 @@ static inline void tv_list_ref(list_T *const l) l->lv_refcount++; } -static inline void tv_list_set_ret(typval_T *const tv, list_T *const l) +static inline void tv_list_set_ret(typval_T *tv, list_T *l) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1); /// Set a list as the return value. Increments the reference count. @@ -120,7 +120,7 @@ static inline void tv_list_set_ret(typval_T *const tv, list_T *const l) tv_list_ref(l); } -static inline VarLockStatus tv_list_locked(const list_T *const l) +static inline VarLockStatus tv_list_locked(const list_T *l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get list lock status @@ -163,7 +163,7 @@ static inline void tv_list_set_copyid(list_T *const l, const int copyid) l->lv_copyID = copyid; } -static inline int tv_list_len(const list_T *const l) +static inline int tv_list_len(const list_T *l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get the number of items in a list @@ -178,7 +178,7 @@ static inline int tv_list_len(const list_T *const l) return l->lv_len; } -static inline int tv_list_copyid(const list_T *const l) +static inline int tv_list_copyid(const list_T *l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; /// Get list copyID @@ -191,7 +191,7 @@ static inline int tv_list_copyid(const list_T *const l) return l->lv_copyID; } -static inline list_T *tv_list_latest_copy(const list_T *const l) +static inline list_T *tv_list_latest_copy(const list_T *l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; /// Get latest list copy @@ -206,7 +206,7 @@ static inline list_T *tv_list_latest_copy(const list_T *const l) return l->lv_copylist; } -static inline int tv_list_uidx(const list_T *const l, int n) +static inline int tv_list_uidx(const list_T *l, int n) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Normalize index: that is, return either -1 or non-negative index @@ -229,7 +229,7 @@ static inline int tv_list_uidx(const list_T *const l, int n) return n; } -static inline bool tv_list_has_watchers(const list_T *const l) +static inline bool tv_list_has_watchers(const list_T *l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Check whether list has watchers @@ -244,7 +244,7 @@ static inline bool tv_list_has_watchers(const list_T *const l) return l && l->lv_watch; } -static inline listitem_T *tv_list_first(const list_T *const l) +static inline listitem_T *tv_list_first(const list_T *l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get first list item @@ -262,7 +262,7 @@ static inline listitem_T *tv_list_first(const list_T *const l) return l->lv_first; } -static inline listitem_T *tv_list_last(const list_T *const l) +static inline listitem_T *tv_list_last(const list_T *l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get last list item @@ -280,7 +280,7 @@ static inline listitem_T *tv_list_last(const list_T *const l) return l->lv_last; } -static inline void tv_dict_set_ret(typval_T *const tv, dict_T *const d) +static inline void tv_dict_set_ret(typval_T *tv, dict_T *d) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1); /// Set a dictionary as the return value @@ -296,7 +296,7 @@ static inline void tv_dict_set_ret(typval_T *const tv, dict_T *const d) } } -static inline long tv_dict_len(const dict_T *const d) +static inline long tv_dict_len(const dict_T *d) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get the number of items in a Dictionary @@ -310,7 +310,7 @@ static inline long tv_dict_len(const dict_T *const d) return (long)d->dv_hashtab.ht_used; } -static inline bool tv_dict_is_watched(const dict_T *const d) +static inline bool tv_dict_is_watched(const dict_T *d) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Check if dictionary is watched @@ -323,7 +323,7 @@ static inline bool tv_dict_is_watched(const dict_T *const d) return d && !QUEUE_EMPTY(&d->watchers); } -static inline void tv_blob_set_ret(typval_T *const tv, blob_T *const b) +static inline void tv_blob_set_ret(typval_T *tv, blob_T *b) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1); /// Set a blob as the return value. @@ -341,7 +341,7 @@ static inline void tv_blob_set_ret(typval_T *const tv, blob_T *const b) } } -static inline int tv_blob_len(const blob_T *const b) +static inline int tv_blob_len(const blob_T *b) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get the length of the data in the blob, in bytes. @@ -355,7 +355,7 @@ static inline int tv_blob_len(const blob_T *const b) return b->bv_ga.ga_len; } -static inline char_u tv_blob_get(const blob_T *const b, int idx) +static inline char_u tv_blob_get(const blob_T *b, int idx) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; /// Get the byte at index `idx` in the blob. @@ -369,7 +369,7 @@ static inline char_u tv_blob_get(const blob_T *const b, int idx) return ((char_u *)b->bv_ga.ga_data)[idx]; } -static inline void tv_blob_set(blob_T *const b, int idx, char_u c) +static inline void tv_blob_set(blob_T *b, int idx, char_u c) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL; /// Store the byte `c` at index `idx` in the blob. @@ -488,8 +488,8 @@ extern bool tv_in_free_unref_items; } \ }) -static inline bool tv_get_float_chk(const typval_T *const tv, - float_T *const ret_f) +static inline bool tv_get_float_chk(const typval_T *tv, + float_T *ret_f) REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; /// Get the float value @@ -527,7 +527,7 @@ static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) return QUEUE_DATA(q, DictWatcher, node); } -static inline bool tv_is_func(const typval_T tv) +static inline bool tv_is_func(typval_T tv) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST; /// Check whether given typval_T contains a function diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h index 33e19c531c..2f19144da3 100644 --- a/src/nvim/eval/typval_encode.h +++ b/src/nvim/eval/typval_encode.h @@ -71,7 +71,7 @@ typedef kvec_withinit_t(MPConvStackVal, 8) MPConvStack; #define _mp_pop kv_pop #define _mp_last kv_last -static inline size_t tv_strlen(const typval_T *const tv) +static inline size_t tv_strlen(const typval_T *tv) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index fdeb52053c..f6530d0e92 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1193,7 +1193,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett /// For the first we only count the name stored in func_hashtab as a reference, /// using function() does not count as a reference, because the function is /// looked up by name. -static bool func_name_refcount(char_u *name) +static bool func_name_refcount(const char_u *name) { return isdigit(*name) || *name == '<'; } diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 781463b881..8f550d537b 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3239,7 +3239,7 @@ void sub_set_replacement(SubReplacementString sub) /// @param[in] save Save pattern to options, history /// /// @returns true if :substitute can be replaced with a join command -static bool sub_joining_lines(exarg_T *eap, char *pat, char *sub, char *cmd, bool save) +static bool sub_joining_lines(exarg_T *eap, char *pat, const char *sub, const char *cmd, bool save) FUNC_ATTR_NONNULL_ARG(1, 3, 4) { // TODO(vim): find a generic solution to make line-joining operations more diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index c5ba750eb3..e867bd57d4 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1059,7 +1059,7 @@ static int current_tab_nr(tabpage_T *tab) #define LAST_TAB_NR current_tab_nr(NULL) /// Figure out the address type for ":wincmd". -static void get_wincmd_addr_type(char *arg, exarg_T *eap) +static void get_wincmd_addr_type(const char *arg, exarg_T *eap) { switch (*arg) { case 'S': @@ -2837,7 +2837,7 @@ theend: /// @param pp start of command /// @param cmd name of command /// @param len required length -bool checkforcmd(char **pp, char *cmd, int len) +bool checkforcmd(char **pp, const char *cmd, int len) { int i; @@ -5654,7 +5654,7 @@ void do_sleep(long msec) { ui_flush(); // flush before waiting for (long left = msec; !got_int && left > 0; left -= 1000L) { - int next = left > 1000l ? 1000 : (int)left; + int next = left > 1000L ? 1000 : (int)left; LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, (int)next, got_int); os_breakcheck(); } @@ -6559,7 +6559,7 @@ static void ex_tag(exarg_T *eap) ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name); } -static void ex_tag_cmd(exarg_T *eap, char *name) +static void ex_tag_cmd(exarg_T *eap, const char *name) { int cmd; @@ -6685,8 +6685,8 @@ ssize_t find_cmdline_var(const char_u *src, size_t *usedlen) /// @return an allocated string if a valid match was found. /// Returns NULL if no match was found. "usedlen" then still contains the /// number of characters to skip. -char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnump, char **errormsg, - int *escaped, bool empty_is_error) +char_u *eval_vars(char_u *src, const char_u *srcstart, size_t *usedlen, linenr_T *lnump, + char **errormsg, int *escaped, bool empty_is_error) { char *result; char *resultbuf = NULL; @@ -7266,7 +7266,7 @@ static void ex_terminal(exarg_T *eap) void verify_command(char *cmd) { - if (strcmp("smile", cmd)) { + if (strcmp("smile", cmd) != 0) { return; // acceptable non-existing command } msg(" #xxn` #xnxx` ,+x@##@Mz;` .xxx" diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index eae3b4af1a..887ba5400a 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -240,7 +240,7 @@ static int ses_arglist(FILE *fd, char *cmd, garray_T *gap, int fullname, unsigne } /// @return the buffer name for `buf`. -static char *ses_get_fname(buf_T *buf, unsigned *flagp) +static char *ses_get_fname(buf_T *buf, const unsigned *flagp) { // Use the short file name if the current directory is known at the time // the session file will be sourced. diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 7f844bc8f7..cfdd6fe697 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -1933,7 +1933,7 @@ bool is_dev_fd_file(char *fname) /// @param linecnt line count before reading more bytes /// @param p start of more bytes read /// @param endp end of more bytes read -static linenr_T readfile_linenr(linenr_T linecnt, char_u *p, char_u *endp) +static linenr_T readfile_linenr(linenr_T linecnt, char_u *p, const char_u *endp) { char_u *s; linenr_T lnum; @@ -4097,7 +4097,7 @@ static int get_fio_flags(const char_u *name) /// /// @return the name of the encoding and set "*lenp" to the length or, /// NULL when no BOM found. -static char_u *check_for_bom(char_u *p, long size, int *lenp, int flags) +static char_u *check_for_bom(const char_u *p, long size, int *lenp, int flags) { char *name = NULL; int len = 2; diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index ca8e07bba3..f76107c401 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -509,7 +509,7 @@ void AppendToRedobuffLit(const char *str, int len) s--; } if (s > start) { - add_buff(&redobuff, start, (long)(s - start)); + add_buff(&redobuff, start, s - start); } if (*s == NUL || (len >= 0 && s - str >= len)) { @@ -1886,7 +1886,7 @@ static int check_simplify_modifier(int max_offset) /// - When there is no match yet, return map_result_nomatch, need to get more /// typeahead. /// - On failure (out of memory) return map_result_fail. -static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) +static int handle_mapping(int *keylenp, const bool *timedout, int *mapdepth) { mapblock_T *mp = NULL; mapblock_T *mp2; diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index 7cc434a197..507942985c 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -1741,7 +1741,7 @@ static bool prt_check_resource(const struct prt_ps_resource_S *resource, const c FUNC_ATTR_NONNULL_ALL { // Version number m.n should match, the revision number does not matter - if (STRNCMP(resource->version, version, strlen(version))) { + if (STRNCMP(resource->version, version, strlen(version)) != 0) { semsg(_("E621: \"%s\" resource file has wrong version"), resource->name); return false; diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 3f08aa7043..cb5819e946 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -101,7 +101,7 @@ bool tabstop_set(char *var, long **array) /// Calculate the number of screen spaces a tab will occupy. /// If "vts" is set then the tab widths are taken from that array, /// otherwise the value of ts is used. -int tabstop_padding(colnr_T col, long ts_arg, long *vts) +int tabstop_padding(colnr_T col, long ts_arg, const long *vts) { long ts = ts_arg == 0 ? 8 : ts_arg; colnr_T tabcol = 0; @@ -129,7 +129,7 @@ int tabstop_padding(colnr_T col, long ts_arg, long *vts) } /// Find the size of the tab that covers a particular column. -int tabstop_at(colnr_T col, long ts, long *vts) +int tabstop_at(colnr_T col, long ts, const long *vts) { colnr_T tabcol = 0; int t; @@ -178,7 +178,7 @@ colnr_T tabstop_start(colnr_T col, long ts, long *vts) /// Find the number of tabs and spaces necessary to get from one column /// to another. -void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, long *vts, int *ntabs, +void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, const long *vts, int *ntabs, int *nspcs) { int spaces = end_col - start_col; @@ -242,7 +242,7 @@ void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, long *vts, } /// See if two tabstop arrays contain the same values. -bool tabstop_eq(long *ts1, long *ts2) +bool tabstop_eq(const long *ts1, const long *ts2) { int t; @@ -266,7 +266,7 @@ bool tabstop_eq(long *ts1, long *ts2) } /// Copy a tabstop array, allocating space for the new array. -int *tabstop_copy(long *oldts) +int *tabstop_copy(const long *oldts) { long *newts; int t; diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 0f29ae9806..ff4145c92d 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -572,7 +572,7 @@ bool ins_compl_accept_char(int c) /// Get the completed text by inferring the case of the originally typed text. /// If the result is in allocated memory "tofree" is set to it. -static char_u *ins_compl_infercase_gettext(char_u *str, int char_len, int compl_char_len, +static char_u *ins_compl_infercase_gettext(const char_u *str, int char_len, int compl_char_len, int min_len, char **tofree) { bool has_lower = false; @@ -1378,7 +1378,8 @@ theend: /// skipping the word at 'skip_word'. /// /// @return OK on success. -static int thesaurus_add_words_in_line(char *fname, char_u **buf_arg, int dir, char_u *skip_word) +static int thesaurus_add_words_in_line(char *fname, char_u **buf_arg, int dir, + const char_u *skip_word) { int status = OK; diff --git a/src/nvim/locale.c b/src/nvim/locale.c index c472d9ba66..f2db9fcc59 100644 --- a/src/nvim/locale.c +++ b/src/nvim/locale.c @@ -42,7 +42,7 @@ static char *get_locale_val(int what) /// @return true when "lang" starts with a valid language name. /// Rejects NULL, empty string, "C", "C.UTF-8" and others. -static bool is_valid_mess_lang(char *lang) +static bool is_valid_mess_lang(const char *lang) { return lang != NULL && ASCII_ISALPHA(lang[0]) && ASCII_ISALPHA(lang[1]); } diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 001bbf7e48..2b42d7725b 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -1350,7 +1350,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file) char **ptr3 = ptr1 + count; while (ptr2 < ptr3) { - if (strcmp(*ptr1, *ptr2)) { + if (strcmp(*ptr1, *ptr2) != 0) { *++ptr1 = *ptr2++; } else { xfree(*ptr2++); diff --git a/src/nvim/mark.h b/src/nvim/mark.h index 6da976e8d3..a3fcf6d7e7 100644 --- a/src/nvim/mark.h +++ b/src/nvim/mark.h @@ -78,12 +78,13 @@ static inline int mark_local_index(const char name) : -1)))); } -static inline bool lt(pos_T, pos_T) REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; -static inline bool equalpos(pos_T, pos_T) +static inline bool lt(pos_T a, pos_T b) REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; -static inline bool ltoreq(pos_T, pos_T) +static inline bool equalpos(pos_T a, pos_T b) REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; -static inline void clearpos(pos_T *) +static inline bool ltoreq(pos_T a, pos_T b) + REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; +static inline void clearpos(pos_T *a) REAL_FATTR_ALWAYS_INLINE; /// Return true if position a is before (less than) position b. diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 33d652a51f..ddcab37e34 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -2301,7 +2301,7 @@ void *my_iconv_open(char_u *to, char_u *from) // sequence and set "*unconvlenp" to the length of it. // Returns the converted string in allocated memory. NULL for an error. // If resultlenp is not NULL, sets it to the result length in bytes. -static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, size_t slen, +static char_u *iconv_string(const vimconv_T *const vcp, const char_u *str, size_t slen, size_t *unconvlenp, size_t *resultlenp) { const char *from; diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 56342942db..da31235e74 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -437,7 +437,7 @@ void ml_open_file(buf_T *buf) if (buf->b_spell) { char *fname = vim_tempname(); if (fname != NULL) { - (void)mf_open_file(mfp, (char *)fname); // consumes fname! + (void)mf_open_file(mfp, fname); // consumes fname! } buf->b_may_swap = false; return; @@ -1193,7 +1193,7 @@ int recover_names(char_u *fname, int list, int nr, char **fname_out) // Isolate a directory name from *dirp and put it in dir_name (we know // it is large enough, so use 31000 for length). // Advance dirp to next directory name. - (void)copy_option_part(&dirp, (char *)dir_name, 31000, ","); + (void)copy_option_part(&dirp, dir_name, 31000, ","); if (dir_name[0] == '.' && dir_name[1] == NUL) { // check current dir if (fname == NULL) { @@ -1208,24 +1208,24 @@ int recover_names(char_u *fname, int list, int nr, char **fname_out) } } else { // check directory dir_name if (fname == NULL) { - names[0] = concat_fnames((char *)dir_name, "*.sw?", true); + names[0] = concat_fnames(dir_name, "*.sw?", true); // For Unix names starting with a dot are special. MS-Windows // supports this too, on some file systems. - names[1] = concat_fnames((char *)dir_name, ".*.sw?", true); - names[2] = concat_fnames((char *)dir_name, ".sw?", true); + names[1] = concat_fnames(dir_name, ".*.sw?", true); + names[2] = concat_fnames(dir_name, ".sw?", true); num_names = 3; } else { int len = (int)STRLEN(dir_name); p = dir_name + len; - if (after_pathsep((char *)dir_name, (char *)p) + if (after_pathsep(dir_name, p) && len > 1 && p[-1] == p[-2]) { // Ends with '//', Use Full path for swap name - tail = (char_u *)make_percent_swname((char *)dir_name, + tail = (char_u *)make_percent_swname(dir_name, (char *)fname_res); } else { tail = (char_u *)path_tail((char *)fname_res); - tail = (char_u *)concat_fnames((char *)dir_name, (char *)tail, true); + tail = (char_u *)concat_fnames(dir_name, (char *)tail, true); } num_names = recov_file_names(names, (char *)tail, false); xfree(tail); @@ -1262,7 +1262,7 @@ int recover_names(char_u *fname, int list, int nr, char **fname_out) for (int i = 0; i < num_files; i++) { // Do not expand wildcards, on Windows would try to expand // "%tmp%" in "%tmp%file" - if (path_full_compare((char *)p, files[i], true, false) & kEqualFiles) { + if (path_full_compare(p, files[i], true, false) & kEqualFiles) { // Remove the name from files[i]. Move further entries // down. When the array becomes empty free it here, since // FreeWild() won't be called below. @@ -1292,7 +1292,7 @@ int recover_names(char_u *fname, int list, int nr, char **fname_out) } } else { msg_puts(_(" In directory ")); - msg_home_replace((char *)dir_name); + msg_home_replace(dir_name); msg_puts(":\n"); } @@ -3485,7 +3485,7 @@ static void long_to_char(long n, char_u *s) s[3] = (char_u)(n & 0xff); } -static long char_to_long(char_u *s) +static long char_to_long(const char_u *s) { long retval; diff --git a/src/nvim/message.c b/src/nvim/message.c index a90e675423..b608b59c9b 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -318,7 +318,7 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline) || (*s != '<' && last_msg_hist != NULL && last_msg_hist->msg != NULL - && strcmp(s, last_msg_hist->msg))) { + && strcmp(s, last_msg_hist->msg) != 0)) { add_msg_hist(s, -1, attr, multiline); } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index b7febe2d51..b058a1e30a 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -3066,7 +3066,7 @@ static void nv_gd(oparg_T *oap, int nchar, int thisblock) /// @return true if line[offset] is not inside a C-style comment or string, /// false otherwise. -static bool is_ident(char_u *line, int offset) +static bool is_ident(const char_u *line, int offset) { bool incomment = false; int instring = 0; diff --git a/src/nvim/option.c b/src/nvim/option.c index 0b819aad43..1a6cd0c1af 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -3256,7 +3256,7 @@ static void showoptions(int all, int opt_flags) } /// Return true if option "p" has its default value. -static int optval_default(vimoption_T *p, char_u *varp) +static int optval_default(vimoption_T *p, const char_u *varp) { if (varp == NULL) { return true; // hidden option is always at default @@ -4889,7 +4889,7 @@ static void option_value2string(vimoption_T *opp, int opt_flags) /// Return true if "varp" points to 'wildchar' or 'wildcharm' and it can be /// printed as a keyname. /// "*wcp" is set to the value of the option if it's 'wildchar' or 'wildcharm'. -static int wc_use_keyname(char_u *varp, long *wcp) +static int wc_use_keyname(const char_u *varp, long *wcp) { if (((long *)varp == &p_wc) || ((long *)varp == &p_wcm)) { *wcp = *(long *)varp; diff --git a/src/nvim/os/fileio.h b/src/nvim/os/fileio.h index 426dc422c2..da23a54c4e 100644 --- a/src/nvim/os/fileio.h +++ b/src/nvim/os/fileio.h @@ -37,7 +37,7 @@ typedef enum { ///< EAGAIN was encountered. } FileOpenFlags; -static inline bool file_eof(const FileDescriptor *const fp) +static inline bool file_eof(const FileDescriptor *fp) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; /// Check whether end of file was encountered @@ -51,7 +51,7 @@ static inline bool file_eof(const FileDescriptor *const fp) return fp->eof && rbuffer_size(fp->rv) == 0; } -static inline int file_fd(const FileDescriptor *const fp) +static inline int file_fd(const FileDescriptor *fp) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; /// Return the file descriptor associated with the FileDescriptor structure diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c index 396bf6986a..161c8d28b8 100644 --- a/src/nvim/os/time.c +++ b/src/nvim/os/time.c @@ -67,7 +67,7 @@ void os_delay(uint64_t ms, bool ignoreinput) } LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, (int)ms, got_int); } else { - os_microdelay(ms * 1000u, ignoreinput); + os_microdelay(ms * 1000U, ignoreinput); } } @@ -80,10 +80,10 @@ void os_delay(uint64_t ms, bool ignoreinput) /// If false, waiting is aborted on any input. void os_microdelay(uint64_t us, bool ignoreinput) { - uint64_t elapsed = 0u; + uint64_t elapsed = 0U; uint64_t base = uv_hrtime(); // Convert microseconds to nanoseconds, or UINT64_MAX on overflow. - const uint64_t ns = (us < UINT64_MAX / 1000u) ? us * 1000u : UINT64_MAX; + const uint64_t ns = (us < UINT64_MAX / 1000U) ? us * 1000U : UINT64_MAX; uv_mutex_lock(&delay_mutex); @@ -92,7 +92,7 @@ void os_microdelay(uint64_t us, bool ignoreinput) // Else we check for input in ~100ms intervals. const uint64_t ns_delta = ignoreinput ? ns - elapsed - : MIN(ns - elapsed, 100000000u); // 100ms + : MIN(ns - elapsed, 100000000U); // 100ms const int rv = uv_cond_timedwait(&delay_cond, &delay_mutex, ns_delta); if (0 != rv && UV_ETIMEDOUT != rv) { diff --git a/src/nvim/profile.c b/src/nvim/profile.c index 50a8a371f5..b588431bda 100644 --- a/src/nvim/profile.c +++ b/src/nvim/profile.c @@ -398,7 +398,7 @@ bool prof_def_func(void) /// Print the count and times for one function or function line. /// /// @param prefer_self when equal print only self time -static void prof_func_line(FILE *fd, int count, proftime_T *total, proftime_T *self, +static void prof_func_line(FILE *fd, int count, const proftime_T *total, const proftime_T *self, bool prefer_self) { if (count > 0) { @@ -684,7 +684,7 @@ void script_prof_save(proftime_T *tm) } /// Count time spent in children after invoking another script or function. -void script_prof_restore(proftime_T *tm) +void script_prof_restore(const proftime_T *tm) { scriptitem_T *si; diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index af1db1956b..2e14f26861 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -563,7 +563,7 @@ static size_t efm_regpat_bufsz(char *efm) } /// Return the length of a 'errorformat' option part (separated by ","). -static int efm_option_part_len(char *efm) +static int efm_option_part_len(const char *efm) { int len; @@ -5257,7 +5257,7 @@ static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *sp /// Jump to the first match and update the directory. static void vgr_jump_to_match(qf_info_T *qi, int forceit, bool *redraw_for_dummy, - buf_T *first_match_buf, char *target_dir) + buf_T *first_match_buf, char *target_dir) // NOLINT(readability-non-const-parameter) { buf_T *buf = curbuf; qf_jump(qi, 0, 0, forceit); diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 935bf4c507..fa7aa35a4d 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -343,7 +343,7 @@ RuntimeSearchPath copy_runtime_search_path(const RuntimeSearchPath src) return dst; } -void runtime_search_path_unref(RuntimeSearchPath path, int *ref) +void runtime_search_path_unref(RuntimeSearchPath path, const int *ref) FUNC_ATTR_NONNULL_ALL { if (*ref) { diff --git a/src/nvim/sha256.c b/src/nvim/sha256.c index 012f145875..6522158f12 100644 --- a/src/nvim/sha256.c +++ b/src/nvim/sha256.c @@ -339,7 +339,7 @@ bool sha256_self_test(void) } } - if (memcmp(output, sha_self_test_vector[i], SHA256_BUFFER_SIZE)) { + if (memcmp(output, sha_self_test_vector[i], SHA256_BUFFER_SIZE) != 0) { failures = true; output[sizeof(output) - 1] = '\0'; diff --git a/src/nvim/shada.c b/src/nvim/shada.c index e56febec9b..89126b349a 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -1151,8 +1151,8 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) } HistoryMergerState hms[HIST_COUNT]; if (srni_flags & kSDReadHistory) { - for (uint8_t i = 0; i < HIST_COUNT; i++) { - hms_init(&hms[i], i, (size_t)p_hi, true, true); + for (HistoryType i = 0; i < HIST_COUNT; i++) { + hms_init(&hms[i], (uint8_t)i, (size_t)p_hi, true, true); } } ShadaEntry cur_entry; @@ -1416,12 +1416,12 @@ shada_read_main_cycle_end: // memory for the history string itself and separator character which // may be assigned right away. if (srni_flags & kSDReadHistory) { - for (uint8_t i = 0; i < HIST_COUNT; i++) { + for (HistoryType i = 0; i < HIST_COUNT; i++) { hms_insert_whole_neovim_history(&hms[i]); clr_history(i); int *new_hisidx; int *new_hisnum; - histentry_T *hist = hist_get_array(i, &new_hisidx, &new_hisnum); + histentry_T *hist = hist_get_array((uint8_t)i, &new_hisidx, &new_hisnum); if (hist != NULL) { hms_to_he_array(&hms[i], hist, new_hisidx, new_hisnum); } @@ -2511,7 +2511,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef bool dump_history = false; // Initialize history merger - for (uint8_t i = 0; i < HIST_COUNT; i++) { + for (HistoryType i = 0; i < HIST_COUNT; i++) { long num_saved = get_shada_parameter(hist_type2char(i)); if (num_saved == -1) { num_saved = p_hi; @@ -2519,7 +2519,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef if (num_saved > 0) { dump_history = true; dump_one_history[i] = true; - hms_init(&wms->hms[i], i, (size_t)num_saved, sd_reader != NULL, false); + hms_init(&wms->hms[i], (uint8_t)i, (size_t)num_saved, sd_reader != NULL, false); } else { dump_one_history[i] = false; } diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 9c517098b9..8c0ae6dca8 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -1271,7 +1271,7 @@ static void sign_place_cmd(buf_T *buf, linenr_T lnum, char *sign_name, int id, c } /// ":sign unplace" command -static void sign_unplace_cmd(buf_T *buf, linenr_T lnum, char *sign_name, int id, char *group) +static void sign_unplace_cmd(buf_T *buf, linenr_T lnum, const char *sign_name, int id, char *group) { if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0')) { emsg(_(e_invarg)); @@ -1328,7 +1328,7 @@ static void sign_unplace_cmd(buf_T *buf, linenr_T lnum, char *sign_name, int id, /// :sign jump {id} buffer={nr} /// :sign jump {id} group={group} file={fname} /// :sign jump {id} group={group} buffer={nr} -static void sign_jump_cmd(buf_T *buf, linenr_T lnum, char *sign_name, int id, char *group) +static void sign_jump_cmd(buf_T *buf, linenr_T lnum, const char *sign_name, int id, char *group) { if (sign_name == NULL && group == NULL && id == -1) { emsg(_(e_argreq)); diff --git a/src/nvim/spell.c b/src/nvim/spell.c index adfbbb9573..1bd0b9c85f 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -960,7 +960,7 @@ bool can_compound(slang_T *slang, const char_u *word, const char_u *flags) // compound rule. This is used to stop trying a compound if the flags // collected so far can't possibly match any compound rule. // Caller must check that slang->sl_comprules is not NULL. -bool match_compoundrule(slang_T *slang, char_u *compflags) +bool match_compoundrule(slang_T *slang, const char_u *compflags) { // loop over all the COMPOUNDRULE entries for (char_u *p = slang->sl_comprules; *p != NUL; p++) { @@ -2184,7 +2184,7 @@ static void use_midword(slang_T *lp, win_T *wp) // Find the region "region[2]" in "rp" (points to "sl_regions"). // Each region is simply stored as the two characters of its name. // Returns the index if found (first is 0), REGION_ALL if not found. -static int find_region(char_u *rp, char_u *region) +static int find_region(const char_u *rp, const char_u *region) { int i; @@ -2209,7 +2209,7 @@ static int find_region(char_u *rp, char_u *region) /// @param[in] end End of word or NULL for NUL delimited string /// /// @returns Case type of word -int captype(char_u *word, char_u *end) +int captype(char_u *word, const char_u *end) FUNC_ATTR_NONNULL_ARG(1) { char_u *p; @@ -2814,7 +2814,7 @@ static void spell_soundfold_sofo(slang_T *slang, char_u *inword, char_u *res) // Turn "inword" into its sound-a-like equivalent in "res[MAXWLEN]". // Multi-byte version of spell_soundfold(). -static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) +static void spell_soundfold_wsal(slang_T *slang, const char_u *inword, char_u *res) { salitem_T *smp = (salitem_T *)slang->sl_sal.ga_data; int word[MAXWLEN] = { 0 }; diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index d7a3efda83..793985f45d 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -819,7 +819,7 @@ endOK: // Fill in the wordcount fields for a trie. // Returns the total number of words. -static void tree_count_words(char_u *byts, idx_T *idxs) +static void tree_count_words(const char_u *byts, idx_T *idxs) { int depth; idx_T arridx[MAXWLEN]; @@ -2171,18 +2171,14 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) midword = (char_u *)getroom_save(spin, (char_u *)items[1]); } else if (is_aff_rule(items, itemcnt, "TRY", 2)) { // ignored, we look in the tree for what chars may appear - } - // TODO: remove "RAR" later - else if ((is_aff_rule(items, itemcnt, "RAR", 2) - || is_aff_rule(items, itemcnt, "RARE", 2)) - && aff->af_rare == 0) { + } else if ((is_aff_rule(items, itemcnt, "RAR", 2) // TODO(vim): remove "RAR" later + || is_aff_rule(items, itemcnt, "RARE", 2)) + && aff->af_rare == 0) { aff->af_rare = affitem2flag(aff->af_flagtype, (char_u *)items[1], fname, lnum); - } - // TODO: remove "KEP" later - else if ((is_aff_rule(items, itemcnt, "KEP", 2) - || is_aff_rule(items, itemcnt, "KEEPCASE", 2)) - && aff->af_keepcase == 0) { + } else if ((is_aff_rule(items, itemcnt, "KEP", 2) // TODO(vim): remove "KEP" later + || is_aff_rule(items, itemcnt, "KEEPCASE", 2)) + && aff->af_keepcase == 0) { aff->af_keepcase = affitem2flag(aff->af_flagtype, (char_u *)items[1], fname, lnum); } else if ((is_aff_rule(items, itemcnt, "BAD", 2) @@ -3962,8 +3958,8 @@ static int store_word(spellinfo_T *spin, char_u *word, int flags, int region, co // When "flags" < 0 we are adding to the prefix tree where "flags" is used for // "rare" and "region" is the condition nr. // Returns FAIL when out of memory. -static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int flags, int region, - int affixID) +static int tree_add_word(spellinfo_T *spin, const char_u *word, wordnode_T *root, int flags, + int region, int affixID) { wordnode_T *node = root; wordnode_T *np; @@ -5742,7 +5738,7 @@ static void init_spellfile(void) /// Set the spell character tables from strings in the .spl file. /// /// @param cnt length of "flags" -static void set_spell_charflags(char_u *flags, int cnt, char_u *fol) +static void set_spell_charflags(const char_u *flags, int cnt, char_u *fol) { // We build the new tables here first, so that we can compare with the // previous one. diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c index 2ff41d5157..fbe2ec837b 100644 --- a/src/nvim/spellsuggest.c +++ b/src/nvim/spellsuggest.c @@ -3530,7 +3530,7 @@ static int soundalike_score(char *goodstart, char *badstart) /// The implementation of the algorithm comes from Aspell editdist.cpp, /// edit_distance(). It has been converted from C++ to C and modified to /// support multi-byte characters. -static int spell_edit_score(slang_T *slang, char_u *badword, char_u *goodword) +static int spell_edit_score(slang_T *slang, const char_u *badword, const char_u *goodword) { int *cnt; int j, i; @@ -3635,7 +3635,8 @@ static int spell_edit_score_limit(slang_T *slang, char_u *badword, char_u *goodw /// Multi-byte version of spell_edit_score_limit(). /// Keep it in sync with the above! -static int spell_edit_score_limit_w(slang_T *slang, char_u *badword, char_u *goodword, int limit) +static int spell_edit_score_limit_w(slang_T *slang, const char_u *badword, const char_u *goodword, + int limit) { limitscore_T stack[10]; // allow for over 3 * 2 edits int stackidx; diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index c4329fd84d..fb49a1b6a7 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -829,7 +829,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san // so `vim_strsize` will work. char *t = stl_items[stl_groupitems[groupdepth]].start; *out_p = NUL; - long group_len = vim_strsize(t); + ptrdiff_t group_len = vim_strsize(t); // If the group contained internal items // and the group did not have a minimum width, @@ -915,7 +915,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san } // If the group is shorter than the minimum width, add padding characters. } else if (abs(stl_items[stl_groupitems[groupdepth]].minwid) > group_len) { - long min_group_width = stl_items[stl_groupitems[groupdepth]].minwid; + ptrdiff_t min_group_width = stl_items[stl_groupitems[groupdepth]].minwid; // If the group is left-aligned, add characters to the right. if (min_group_width < 0) { min_group_width = 0 - min_group_width; @@ -929,7 +929,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san group_len = (min_group_width - group_len) * utf_char2len(fillchar); memmove(t + group_len, t, (size_t)(out_p - t)); if (out_p + group_len >= (out_end_p + 1)) { - group_len = (long)(out_end_p - out_p); + group_len = out_end_p - out_p; } out_p += group_len; // } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 527cca05f5..4934168acf 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -5304,7 +5304,7 @@ void ex_ownsyntax(exarg_T *eap) curwin->w_s = xcalloc(1, sizeof(synblock_T)); hash_init(&curwin->w_s->b_keywtab); hash_init(&curwin->w_s->b_keywtab_ic); - // TODO: Keep the spell checking as it was. NOLINT(readability/todo) + // TODO(vim): Keep the spell checking as it was. curwin->w_p_spell = false; // No spell checking // make sure option values are "empty_option" instead of NULL clear_string_option(&curwin->w_s->b_p_spc); diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 7c465ac909..07aee92d92 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -643,8 +643,6 @@ end_do_tag: } postponed_split = 0; // don't split next time g_do_tagpreview = 0; // don't do tag preview next time - - return; } // List all the matching tags. @@ -1495,7 +1493,7 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc // Try tag file names from tags option one by one. for (first_file = true; - get_tagfname(&tn, first_file, (char *)tag_fname) == OK; + get_tagfname(&tn, first_file, tag_fname) == OK; first_file = false) { // A file that doesn't exist is silently ignored. Only when not a // single file is found, an error message is given (further on). @@ -1553,7 +1551,7 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc } } - if ((fp = os_fopen((char *)tag_fname, "r")) == NULL) { + if ((fp = os_fopen(tag_fname, "r")) == NULL) { continue; } @@ -1647,7 +1645,7 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc // skip empty and blank lines do { eof = vim_fgets((char_u *)lbuf, lbuf_size, fp); - } while (!eof && vim_isblankline((char *)lbuf)); + } while (!eof && vim_isblankline(lbuf)); if (eof) { break; // end of file @@ -1953,7 +1951,7 @@ parse_line: // Decide in which array to store this match. is_current = test_for_current((char *)tagp.fname, (char *)tagp.fname_end, - (char *)tag_fname, + tag_fname, buf_ffname); is_static = test_for_static(&tagp); diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 50574b292d..5e221e13df 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -428,7 +428,7 @@ bool terminal_enter(void) long save_w_p_so = curwin->w_p_so; long save_w_p_siso = curwin->w_p_siso; if (curwin->w_p_cul && curwin->w_p_culopt_flags & CULOPT_NBR) { - if (strcmp(curwin->w_p_culopt, "number")) { + if (strcmp(curwin->w_p_culopt, "number") != 0) { save_w_p_culopt = curwin->w_p_culopt; curwin->w_p_culopt = xstrdup("number"); } diff --git a/src/nvim/textobject.c b/src/nvim/textobject.c index dbe7110d60..8290fe14e5 100644 --- a/src/nvim/textobject.c +++ b/src/nvim/textobject.c @@ -229,7 +229,7 @@ bool findpar(bool *pincl, int dir, long count, int what, bool both) } /// check if the string 's' is a nroff macro that is in option 'opt' -static bool inmacro(char_u *opt, char_u *s) +static bool inmacro(char_u *opt, const char_u *s) { char_u *macro; diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 2c9510bf34..eb59d72e43 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -322,7 +322,7 @@ void vim_beep(unsigned val) // Only beep up to three times per half a second, // otherwise a sequence of beeps would freeze Vim. - if (start_time == 0 || os_hrtime() - start_time > 500000000u) { + if (start_time == 0 || os_hrtime() - start_time > 500000000U) { beeps = 0; start_time = os_hrtime(); } @@ -452,7 +452,7 @@ void ui_line(ScreenGrid *grid, int row, int startcol, int endcol, int clearcol, MIN(clearcol, (int)grid->cols - 1)); ui_call_flush(); uint64_t wd = (uint64_t)labs(p_wd); - os_microdelay(wd * 1000u, true); + os_microdelay(wd * 1000U, true); pending_cursor_update = true; // restore the cursor later } } diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index 47c83b8ed1..84e1a5e513 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -505,7 +505,7 @@ static void debug_delay(Integer lines) ui_call_flush(); uint64_t wd = (uint64_t)labs(p_wd); uint64_t factor = (uint64_t)MAX(MIN(lines, 5), 1); - os_microdelay(factor * wd * 1000u, true); + os_microdelay(factor * wd * 1000U, true); } static void compose_area(Integer startrow, Integer endrow, Integer startcol, Integer endcol) diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c index 4c2ea75047..1cb3b18345 100644 --- a/src/nvim/usercmd.c +++ b/src/nvim/usercmd.c @@ -1077,7 +1077,7 @@ bool uc_split_args_iter(const char *arg, size_t arglen, size_t *end, char *buf, } /// split and quote args for -static char *uc_split_args(char *arg, char **args, size_t *arglens, size_t argc, size_t *lenp) +static char *uc_split_args(char *arg, char **args, const size_t *arglens, size_t argc, size_t *lenp) { char *buf; char *p; diff --git a/src/nvim/version.c b/src/nvim/version.c index 963ca66344..e76d3ef9bf 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -34,7 +34,7 @@ "." STR(NVIM_VERSION_MINOR) "." STR(NVIM_VERSION_PATCH) \ NVIM_VERSION_PRERELEASE #endif -#define NVIM_VERSION_LONG "NVIM " NVIM_VERSION_MEDIUM +#define NVIM_VERSION_LONG "NVIM " NVIM_VERSION_MEDIUM // NOLINT(bugprone-suspicious-missing-comma) char *Version = VIM_VERSION_SHORT; char *longVersion = NVIM_VERSION_LONG; @@ -2813,7 +2813,7 @@ void intro_message(int colon) size_t lines_size = ARRAY_SIZE(lines); assert(lines_size <= LONG_MAX); - blanklines = Rows - ((long)lines_size - 1l); + blanklines = Rows - ((long)lines_size - 1L); // Don't overwrite a statusline. Depends on 'cmdheight'. if (p_ls > 1) { diff --git a/src/nvim/viml/parser/parser.h b/src/nvim/viml/parser/parser.h index 404dc5a0d1..5e1cf9c6d8 100644 --- a/src/nvim/viml/parser/parser.h +++ b/src/nvim/viml/parser/parser.h @@ -81,8 +81,8 @@ typedef struct { bool can_continuate; } ParserState; -static inline void viml_parser_init(ParserState *const ret_pstate, const ParserLineGetter get_line, - void *const cookie, ParserHighlight *const colors) +static inline void viml_parser_init(ParserState *ret_pstate, ParserLineGetter get_line, + void *cookie, ParserHighlight *colors) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1, 2); /// Initialize a new parser state instance @@ -109,7 +109,7 @@ static inline void viml_parser_init(ParserState *const ret_pstate, const ParserL kvi_init(ret_pstate->stack); } -static inline void viml_parser_destroy(ParserState *const pstate) +static inline void viml_parser_destroy(ParserState *pstate) REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE; /// Free all memory allocated by the parser on heap @@ -127,8 +127,8 @@ static inline void viml_parser_destroy(ParserState *const pstate) kvi_destroy(pstate->stack); } -static inline void viml_preader_get_line(ParserInputReader *const preader, - ParserLine *const ret_pline) +static inline void viml_preader_get_line(ParserInputReader *preader, + ParserLine *ret_pline) REAL_FATTR_NONNULL_ALL; /// Get one line from ParserInputReader @@ -152,8 +152,8 @@ static inline void viml_preader_get_line(ParserInputReader *const preader, *ret_pline = pline; } -static inline bool viml_parser_get_remaining_line(ParserState *const pstate, - ParserLine *const ret_pline) +static inline bool viml_parser_get_remaining_line(ParserState *pstate, + ParserLine *ret_pline) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; /// Get currently parsed line, shifted to pstate->pos.col @@ -178,8 +178,8 @@ static inline bool viml_parser_get_remaining_line(ParserState *const pstate, return ret_pline->data != NULL; } -static inline void viml_parser_advance(ParserState *const pstate, - const size_t len) +static inline void viml_parser_advance(ParserState *pstate, + size_t len) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL; /// Advance position by a given number of bytes @@ -200,10 +200,10 @@ static inline void viml_parser_advance(ParserState *const pstate, const size_t l } } -static inline void viml_parser_highlight(ParserState *const pstate, - const ParserPosition start, - const size_t end_col, - const char *const group) +static inline void viml_parser_highlight(ParserState *pstate, + ParserPosition start, + size_t len, + const char *group) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL; /// Record highlighting of some region of text diff --git a/src/nvim/window.c b/src/nvim/window.c index 0e6bb7822a..4812b9ef9d 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -785,7 +785,7 @@ void win_config_float(win_T *wp, FloatConfig fconfig) bool change_border = (fconfig.border != wp->w_float_config.border || memcmp(fconfig.border_hl_ids, wp->w_float_config.border_hl_ids, - sizeof fconfig.border_hl_ids)); + sizeof fconfig.border_hl_ids) != 0); wp->w_float_config = fconfig; -- cgit From b967cb2e03d32e7e525592049ca7f4f92413188c Mon Sep 17 00:00:00 2001 From: dundargoc Date: Fri, 14 Oct 2022 16:34:39 +0200 Subject: refactor(uncrustify): move macros definitions to enable formatting Uncrustify struggles to format function-like macros which are defined in deeply nested areas of the code. Un-nesting them unblocks useful formatting rules from uncrustify. --- src/nvim/ex_cmds.c | 48 ++++++++-------- src/nvim/shada.c | 115 +++++++++++++++++++------------------ src/nvim/viml/parser/expressions.c | 32 +++++------ 3 files changed, 100 insertions(+), 95 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 8f550d537b..87c4f4e654 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3409,6 +3409,30 @@ static int check_regexp_delim(int c) /// @return 0, 1 or 2. See show_cmdpreview() for more information on what the return value means. static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T cmdpreview_bufnr) { +#define ADJUST_SUB_FIRSTLNUM() \ + do { \ + /* For a multi-line match, make a copy of the last matched */ \ + /* line and continue in that one. */ \ + if (nmatch > 1) { \ + sub_firstlnum += (linenr_T)nmatch - 1; \ + xfree(sub_firstline); \ + sub_firstline = xstrdup(ml_get(sub_firstlnum)); \ + /* When going beyond the last line, stop substituting. */ \ + if (sub_firstlnum <= line2) { \ + do_again = true; \ + } else { \ + subflags.do_all = false; \ + } \ + } \ + if (skip_match) { \ + /* Already hit end of the buffer, sub_firstlnum is one */ \ + /* less than what it ought to be. */ \ + xfree(sub_firstline); \ + sub_firstline = xstrdup(""); \ + copycol = 0; \ + } \ + } while (0) + long i = 0; regmmatch_T regmatch; static subflags_T subflags = { @@ -3980,30 +4004,6 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T skip_match = true; } -#define ADJUST_SUB_FIRSTLNUM() \ - do { \ - /* For a multi-line match, make a copy of the last matched */ \ - /* line and continue in that one. */ \ - if (nmatch > 1) { \ - sub_firstlnum += (linenr_T)nmatch - 1; \ - xfree(sub_firstline); \ - sub_firstline = xstrdup(ml_get(sub_firstlnum)); \ - /* When going beyond the last line, stop substituting. */ \ - if (sub_firstlnum <= line2) { \ - do_again = true; \ - } else { \ - subflags.do_all = false; \ - } \ - } \ - if (skip_match) { \ - /* Already hit end of the buffer, sub_firstlnum is one */ \ - /* less than what it ought to be. */ \ - xfree(sub_firstline); \ - sub_firstline = xstrdup(""); \ - copycol = 0; \ - } \ - } while (0) - // Save the line numbers for the preview buffer // NOTE: If the pattern matches a final newline, the next line will // be shown also, but should not be highlighted. Intentional for now. diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 89126b349a..244e644495 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -1557,6 +1557,18 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntr (sd_default_values[(entry).type].data.attr == (entry).data.attr) #define ONE_IF_NOT_DEFAULT(entry, attr) \ ((size_t)(!CHECK_DEFAULT(entry, attr))) + +#define PACK_BOOL(entry, name, attr) \ + do { \ + if (!CHECK_DEFAULT(entry, search_pattern.attr)) { \ + PACK_STATIC_STR(name); \ + if (sd_default_values[(entry).type].data.search_pattern.attr) { \ + msgpack_pack_false(spacker); \ + } else { \ + msgpack_pack_true(spacker); \ + } \ + } \ + } while (0) switch (entry.type) { case kSDItemMissing: abort(); @@ -1640,17 +1652,6 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntr msgpack_pack_map(spacker, map_size); PACK_STATIC_STR(SEARCH_KEY_PAT); PACK_BIN(cstr_as_string(entry.data.search_pattern.pat)); -#define PACK_BOOL(entry, name, attr) \ - do { \ - if (!CHECK_DEFAULT(entry, search_pattern.attr)) { \ - PACK_STATIC_STR(name); \ - if (sd_default_values[(entry).type].data.search_pattern.attr) { \ - msgpack_pack_false(spacker); \ - } else { \ - msgpack_pack_true(spacker); \ - } \ - } \ - } while (0) PACK_BOOL(entry, SEARCH_KEY_MAGIC, magic); PACK_BOOL(entry, SEARCH_KEY_IS_LAST_USED, is_last_used); PACK_BOOL(entry, SEARCH_KEY_SMARTCASE, smartcase); @@ -1957,6 +1958,28 @@ static const char *shada_format_entry(const ShadaEntry entry) ret[0] = 0; vim_snprintf(S_LEN(ret), "%s", "[ ] ts=%" PRIu64 " "); // ^ Space for `can_free_entry` +#define FORMAT_MARK_ENTRY(entry_name, name_fmt, name_fmt_arg) \ + do { \ + typval_T ad_tv = { \ + .v_type = VAR_DICT, \ + .vval.v_dict = entry.data.filemark.additional_data \ + }; \ + size_t ad_len; \ + char *const ad = encode_tv2string(&ad_tv, &ad_len); \ + vim_snprintf_add(S_LEN(ret), \ + entry_name " {" name_fmt " file=[%zu]\"%.512s\", " \ + "pos={l=%" PRIdLINENR ",c=%" PRIdCOLNR ",a=%" PRIdCOLNR "}, " \ + "ad={%p:[%zu]%.64s} }", \ + name_fmt_arg, \ + strlen(entry.data.filemark.fname), \ + entry.data.filemark.fname, \ + entry.data.filemark.mark.lnum, \ + entry.data.filemark.mark.col, \ + entry.data.filemark.mark.coladd, \ + (void *)entry.data.filemark.additional_data, \ + ad_len, \ + ad); \ + } while (0) switch (entry.type) { case kSDItemMissing: vim_snprintf_add(S_LEN(ret), "Missing"); @@ -1985,28 +2008,6 @@ static const char *shada_format_entry(const ShadaEntry entry) case kSDItemVariable: vim_snprintf_add(S_LEN(ret), "Variable { TODO }"); break; -#define FORMAT_MARK_ENTRY(entry_name, name_fmt, name_fmt_arg) \ - do { \ - typval_T ad_tv = { \ - .v_type = VAR_DICT, \ - .vval.v_dict = entry.data.filemark.additional_data \ - }; \ - size_t ad_len; \ - char *const ad = encode_tv2string(&ad_tv, &ad_len); \ - vim_snprintf_add(S_LEN(ret), \ - entry_name " {" name_fmt " file=[%zu]\"%.512s\", " \ - "pos={l=%" PRIdLINENR ",c=%" PRIdCOLNR ",a=%" PRIdCOLNR "}, " \ - "ad={%p:[%zu]%.64s} }", \ - name_fmt_arg, \ - strlen(entry.data.filemark.fname), \ - entry.data.filemark.fname, \ - entry.data.filemark.mark.lnum, \ - entry.data.filemark.mark.col, \ - entry.data.filemark.mark.coladd, \ - (void *)entry.data.filemark.additional_data, \ - ad_len, \ - ad); \ - } while (0) case kSDItemGlobalMark: FORMAT_MARK_ENTRY("GlobalMark", " name='%c',", entry.data.filemark.name); break; @@ -2055,6 +2056,32 @@ static inline ShaDaWriteResult shada_read_when_writing(ShaDaReadDef *const sd_re ShaDaWriteResult ret = kSDWriteSuccessfull; ShadaEntry entry; ShaDaReadResult srni_ret; + +#define COMPARE_WITH_ENTRY(wms_entry_, entry) \ + do { \ + PossiblyFreedShadaEntry *const wms_entry = (wms_entry_); \ + if (wms_entry->data.type != kSDItemMissing) { \ + if (wms_entry->data.timestamp >= (entry).timestamp) { \ + shada_free_shada_entry(&(entry)); \ + break; \ + } \ + if (wms_entry->can_free_entry) { \ + shada_free_shada_entry(&wms_entry->data); \ + } \ + } \ + *wms_entry = pfs_entry; \ + } while (0) + +#define FREE_POSSIBLY_FREED_SHADA_ENTRY(entry) \ + do { \ + if ((entry).can_free_entry) { \ + shada_free_shada_entry(&(entry).data); \ + } \ + } while (0) + +#define SDE_TO_PFSDE(entry) \ + ((PossiblyFreedShadaEntry) { .can_free_entry = true, .data = (entry) }) + while ((srni_ret = shada_read_next_item(sd_reader, &entry, srni_flags, max_kbyte)) != kSDReadStatusFinished) { @@ -2072,20 +2099,6 @@ static inline ShaDaWriteResult shada_read_when_writing(ShaDaReadDef *const sd_re case kSDReadStatusMalformed: continue; } -#define COMPARE_WITH_ENTRY(wms_entry_, entry) \ - do { \ - PossiblyFreedShadaEntry *const wms_entry = (wms_entry_); \ - if (wms_entry->data.type != kSDItemMissing) { \ - if (wms_entry->data.timestamp >= (entry).timestamp) { \ - shada_free_shada_entry(&(entry)); \ - break; \ - } \ - if (wms_entry->can_free_entry) { \ - shada_free_shada_entry(&wms_entry->data); \ - } \ - } \ - *wms_entry = pfs_entry; \ - } while (0) const PossiblyFreedShadaEntry pfs_entry = { .can_free_entry = true, .data = entry, @@ -2225,14 +2238,6 @@ static inline ShaDaWriteResult shada_read_when_writing(ShaDaReadDef *const sd_re *wms_entry = pfs_entry; } } else { -#define FREE_POSSIBLY_FREED_SHADA_ENTRY(entry) \ - do { \ - if ((entry).can_free_entry) { \ - shada_free_shada_entry(&(entry).data); \ - } \ - } while (0) -#define SDE_TO_PFSDE(entry) \ - ((PossiblyFreedShadaEntry) { .can_free_entry = true, .data = (entry) }) #define AFTERFREE_DUMMY(entry) #define DUMMY_IDX_ADJ(i) MERGE_JUMPS(filemarks->changes_size, filemarks->changes, diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c index 4564831824..8028950c07 100644 --- a/src/nvim/viml/parser/expressions.c +++ b/src/nvim/viml/parser/expressions.c @@ -2120,6 +2120,22 @@ viml_pexpr_parse_process_token: assert(kv_size(pt_stack)); const ExprASTParseType cur_pt = kv_last(pt_stack); assert(lambda_node == NULL || cur_pt == kEPTLambdaArguments); +#define SIMPLE_UB_OP(op) \ + case kExprLex##op: { \ + if (want_node == kENodeValue) { \ + /* Value level: assume unary operator. */ \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnary##op); \ + *top_node_p = cur_node; \ + kvi_push(ast_stack, &cur_node->children); \ + HL_CUR_TOKEN(Unary##op); \ + } else { \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeBinary##op); \ + ADD_OP_NODE(cur_node); \ + HL_CUR_TOKEN(Binary##op); \ + } \ + want_node = kENodeValue; \ + break; \ + } switch (tok_type) { case kExprLexMissing: case kExprLexSpacing: @@ -2141,22 +2157,6 @@ viml_pexpr_parse_process_token: HL_CUR_TOKEN(Register); break; } -#define SIMPLE_UB_OP(op) \ - case kExprLex##op: { \ - if (want_node == kENodeValue) { \ - /* Value level: assume unary operator. */ \ - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnary##op); \ - *top_node_p = cur_node; \ - kvi_push(ast_stack, &cur_node->children); \ - HL_CUR_TOKEN(Unary##op); \ - } else { \ - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeBinary##op); \ - ADD_OP_NODE(cur_node); \ - HL_CUR_TOKEN(Binary##op); \ - } \ - want_node = kENodeValue; \ - break; \ - } SIMPLE_UB_OP(Plus) SIMPLE_UB_OP(Minus) #undef SIMPLE_UB_OP -- cgit From 6ff245732a5a8ab821598a38fb0c5805e6bd3779 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Fri, 14 Oct 2022 17:04:28 +0200 Subject: refactor(uncrustify): improved formatting rules --- src/nvim/api/private/dispatch.h | 5 +---- src/nvim/api/ui_events.in.h | 43 +++++++++++++++++------------------------ src/nvim/edit.c | 3 +-- src/nvim/eval/typval.h | 9 +++------ src/nvim/event/stream.h | 3 +-- src/nvim/file_search.c | 3 +-- src/nvim/normal.c | 3 +-- src/nvim/tag.c | 3 +-- 8 files changed, 27 insertions(+), 45 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/api/private/dispatch.h b/src/nvim/api/private/dispatch.h index f92b205531..f0161a53a8 100644 --- a/src/nvim/api/private/dispatch.h +++ b/src/nvim/api/private/dispatch.h @@ -3,10 +3,7 @@ #include "nvim/api/private/defs.h" -typedef Object (*ApiDispatchWrapper)(uint64_t channel_id, - Array args, - Arena *arena, - Error *error); +typedef Object (*ApiDispatchWrapper)(uint64_t channel_id, Array args, Arena *arena, Error *error); /// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores /// functions of this type. diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 17930dca85..21400862b9 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -69,11 +69,10 @@ void scroll(Integer count) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; // Second revision of the grid protocol, used with ext_linegrid ui option -void default_colors_set(Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, - Integer cterm_fg, Integer cterm_bg) +void default_colors_set(Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, Integer cterm_fg, + Integer cterm_bg) FUNC_API_SINCE(4) FUNC_API_REMOTE_IMPL; -void hl_attr_define(Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs, - Array info) +void hl_attr_define(Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs, Array info) FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL; void hl_group_set(String name, Integer id) FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL; @@ -85,8 +84,8 @@ void grid_cursor_goto(Integer grid, Integer row, Integer col) FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL; void grid_line(Integer grid, Integer row, Integer col_start, Array data) FUNC_API_SINCE(5) FUNC_API_REMOTE_ONLY FUNC_API_CLIENT_IMPL; -void grid_scroll(Integer grid, Integer top, Integer bot, - Integer left, Integer right, Integer rows, Integer cols) +void grid_scroll(Integer grid, Integer top, Integer bot, Integer left, Integer right, Integer rows, + Integer cols) FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL; void grid_destroy(Integer grid) FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; @@ -94,20 +93,18 @@ void grid_destroy(Integer grid) // For performance and simplicity, we use the dense screen representation // in internal code, such as compositor and TUI. The remote_ui module will // translate this in to the public grid_line format. -void raw_line(Integer grid, Integer row, Integer startcol, - Integer endcol, Integer clearcol, Integer clearattr, - LineFlags flags, const schar_T *chunk, const sattr_T *attrs) +void raw_line(Integer grid, Integer row, Integer startcol, Integer endcol, Integer clearcol, + Integer clearattr, LineFlags flags, const schar_T *chunk, const sattr_T *attrs) FUNC_API_NOEXPORT FUNC_API_COMPOSITOR_IMPL; void event(char *name, Array args) FUNC_API_NOEXPORT FUNC_API_COMPOSITOR_IMPL; -void win_pos(Integer grid, Window win, Integer startrow, - Integer startcol, Integer width, Integer height) +void win_pos(Integer grid, Window win, Integer startrow, Integer startcol, Integer width, + Integer height) FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; -void win_float_pos(Integer grid, Window win, String anchor, Integer anchor_grid, - Float anchor_row, Float anchor_col, Boolean focusable, - Integer zindex) +void win_float_pos(Integer grid, Window win, String anchor, Integer anchor_grid, Float anchor_row, + Float anchor_col, Boolean focusable, Integer zindex) FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; void win_external_pos(Integer grid, Window win) FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; @@ -118,29 +115,25 @@ void win_close(Integer grid) void msg_set_pos(Integer grid, Integer row, Boolean scrolled, String sep_char) FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL FUNC_API_COMPOSITOR_IMPL; -void win_viewport(Integer grid, Window win, Integer topline, - Integer botline, Integer curline, Integer curcol, - Integer line_count) +void win_viewport(Integer grid, Window win, Integer topline, Integer botline, Integer curline, + Integer curcol, Integer line_count) FUNC_API_SINCE(7) FUNC_API_BRIDGE_IMPL; -void win_extmark(Integer grid, Window win, Integer ns_id, Integer mark_id, - Integer row, Integer col) +void win_extmark(Integer grid, Window win, Integer ns_id, Integer mark_id, Integer row, Integer col) FUNC_API_SINCE(10) FUNC_API_REMOTE_ONLY; -void popupmenu_show(Array items, Integer selected, - Integer row, Integer col, Integer grid) +void popupmenu_show(Array items, Integer selected, Integer row, Integer col, Integer grid) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; void popupmenu_hide(void) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; void popupmenu_select(Integer selected) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; -void tabline_update(Tabpage current, Array tabs, - Buffer current_buffer, Array buffers) +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, - Integer indent, Integer level) +void cmdline_show(Array content, Integer pos, String firstc, String prompt, Integer indent, + Integer level) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; void cmdline_pos(Integer pos, Integer level) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index e37f967a2c..90a18947dd 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -439,8 +439,7 @@ static int insert_check(VimState *state) s->mincol = curwin->w_wcol; validate_cursor_col(); - if ( - curwin->w_wcol < s->mincol - tabstop_at(get_nolist_virtcol(), + if (curwin->w_wcol < s->mincol - tabstop_at(get_nolist_virtcol(), curbuf->b_p_ts, curbuf->b_p_vts_array) && curwin->w_wrow == curwin->w_winrow diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index e4cd76e8a6..fcc933c967 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -43,10 +43,8 @@ static inline ListLog *list_log_new(const size_t size) return ret; } -static inline void list_log(const list_T *const l, - const listitem_T *const li1, - const listitem_T *const li2, - const char *const action) +static inline void list_log(const list_T *const l, const listitem_T *const li1, + const listitem_T *const li2, const char *const action) REAL_FATTR_ALWAYS_INLINE; /// Add new entry to log @@ -488,8 +486,7 @@ extern bool tv_in_free_unref_items; } \ }) -static inline bool tv_get_float_chk(const typval_T *tv, - float_T *ret_f) +static inline bool tv_get_float_chk(const typval_T *tv, float_T *ret_f) REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; /// Get the float value diff --git a/src/nvim/event/stream.h b/src/nvim/event/stream.h index b580d3c33d..f2858f0b6d 100644 --- a/src/nvim/event/stream.h +++ b/src/nvim/event/stream.h @@ -16,8 +16,7 @@ typedef struct stream Stream; /// @param count Number of bytes that was read. /// @param data User-defined data /// @param eof If the stream reached EOF. -typedef void (*stream_read_cb)(Stream *stream, RBuffer *buf, size_t count, - void *data, bool eof); +typedef void (*stream_read_cb)(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof); /// Type of function called when the Stream has information about a write /// request. diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index c41916d40a..1434ba415b 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1413,8 +1413,7 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first // When the file doesn't exist, try adding parts of 'suffixesadd'. buf = (char *)suffixes; for (;;) { - if ( - (os_path_exists(NameBuff) + if ((os_path_exists(NameBuff) && (find_what == FINDFILE_BOTH || ((find_what == FINDFILE_DIR) == os_isdir(NameBuff))))) { diff --git a/src/nvim/normal.c b/src/nvim/normal.c index b058a1e30a..e09fb149b4 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -6518,8 +6518,7 @@ static void n_opencmd(cmdarg_T *cap) if (u_save((linenr_T)(curwin->w_cursor.lnum - (cap->cmdchar == 'O' ? 1 : 0)), (linenr_T)(curwin->w_cursor.lnum + - (cap->cmdchar == 'o' ? 1 : 0)) - ) + (cap->cmdchar == 'o' ? 1 : 0))) && open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD, has_format_option(FO_OPEN_COMS) ? OPENLINE_DO_COM : 0, 0, NULL)) { diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 07aee92d92..8c0c2e8f57 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -252,8 +252,7 @@ void do_tag(char *tag, int type, int count, int forceit, int verbose) new_tag = true; } else { - if ( - g_do_tagpreview != 0 ? ptag_entry.tagname == NULL : + if (g_do_tagpreview != 0 ? ptag_entry.tagname == NULL : tagstacklen == 0) { // empty stack emsg(_(e_tagstack)); -- cgit From 837190720310deca0231fc42aa3023957ff79a3a Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 21 Oct 2022 16:36:43 +0200 Subject: vim-patch:9.0.0814: aws config files are not recognized (#20769) vim-patch:436e5d395fd6 (since upstream tagged the wrong commit) Problem: Aws config files are not recognized. Solution: Use "confini" for aws config files. (Justin M. Keyes, closes vim/vim#11416) https://github.com/vim/vim/commit/436e5d395fd629c8d33b5cf7b373aad007f16851 --- src/nvim/testdir/test_filetype.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 3888a384c7..c6f90604e5 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -122,7 +122,7 @@ let s:filename_checks = { \ 'conaryrecipe': ['file.recipe'], \ 'conf': ['auto.master'], \ 'config': ['configure.in', 'configure.ac', '/etc/hostname.file', 'any/etc/hostname.file'], - \ 'confini': ['/etc/pacman.conf', 'any/etc/pacman.conf', 'mpv.conf'], + \ 'confini': ['/etc/pacman.conf', 'any/etc/pacman.conf', 'mpv.conf', 'any/.aws/config', 'any/.aws/credentials'], \ 'context': ['tex/context/any/file.tex', 'file.mkii', 'file.mkiv', 'file.mkvi', 'file.mkxl', 'file.mklx'], \ 'cook': ['file.cook'], \ 'cpp': ['file.cxx', 'file.c++', 'file.hh', 'file.hxx', 'file.hpp', 'file.ipp', 'file.moc', 'file.tcc', 'file.inl', 'file.tlh'], -- cgit From abf758a2977c4e6cab4dfa217f56da853d85851c Mon Sep 17 00:00:00 2001 From: dundargoc Date: Fri, 21 Oct 2022 16:43:03 +0200 Subject: refactor: fix uncrustify lint errors --- src/nvim/buffer.h | 3 +-- src/nvim/eval/encode.h | 8 ++------ src/nvim/viml/parser/parser.h | 13 ++++--------- 3 files changed, 7 insertions(+), 17 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h index ae3d6cc117..7380bb045a 100644 --- a/src/nvim/buffer.h +++ b/src/nvim/buffer.h @@ -69,8 +69,7 @@ EXTERN char *msg_qflist INIT(= N_("[Quickfix List]")); # include "buffer.h.generated.h" #endif -static inline void buf_set_changedtick(buf_T *buf, - varnumber_T changedtick) +static inline void buf_set_changedtick(buf_T *buf, varnumber_T changedtick) REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE; /// Set b:changedtick, also checking b: for consistency in debug build diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h index 9d311fe356..c0c98e0990 100644 --- a/src/nvim/eval/encode.h +++ b/src/nvim/eval/encode.h @@ -15,9 +15,7 @@ /// @param[in] objname Object name, used for error message. /// /// @return OK in case of success, FAIL otherwise. -int encode_vim_to_msgpack(msgpack_packer *packer, - typval_T *tv, - const char *objname); +int encode_vim_to_msgpack(msgpack_packer *packer, typval_T *tv, const char *objname); /// Convert VimL value to :echo output /// @@ -26,9 +24,7 @@ int encode_vim_to_msgpack(msgpack_packer *packer, /// @param[in] objname Object name, used for error message. /// /// @return OK in case of success, FAIL otherwise. -int encode_vim_to_echo(garray_T *packer, - typval_T *tv, - const char *objname); +int encode_vim_to_echo(garray_T *packer, typval_T *tv, const char *objname); /// Structure defining state for read_from_list() typedef struct { diff --git a/src/nvim/viml/parser/parser.h b/src/nvim/viml/parser/parser.h index 5e1cf9c6d8..56e8b2d32b 100644 --- a/src/nvim/viml/parser/parser.h +++ b/src/nvim/viml/parser/parser.h @@ -127,8 +127,7 @@ static inline void viml_parser_destroy(ParserState *const pstate) kvi_destroy(pstate->stack); } -static inline void viml_preader_get_line(ParserInputReader *preader, - ParserLine *ret_pline) +static inline void viml_preader_get_line(ParserInputReader *preader, ParserLine *ret_pline) REAL_FATTR_NONNULL_ALL; /// Get one line from ParserInputReader @@ -152,8 +151,7 @@ static inline void viml_preader_get_line(ParserInputReader *const preader, *ret_pline = pline; } -static inline bool viml_parser_get_remaining_line(ParserState *pstate, - ParserLine *ret_pline) +static inline bool viml_parser_get_remaining_line(ParserState *pstate, ParserLine *ret_pline) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; /// Get currently parsed line, shifted to pstate->pos.col @@ -178,8 +176,7 @@ static inline bool viml_parser_get_remaining_line(ParserState *const pstate, return ret_pline->data != NULL; } -static inline void viml_parser_advance(ParserState *pstate, - size_t len) +static inline void viml_parser_advance(ParserState *pstate, size_t len) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL; /// Advance position by a given number of bytes @@ -200,9 +197,7 @@ static inline void viml_parser_advance(ParserState *const pstate, const size_t l } } -static inline void viml_parser_highlight(ParserState *pstate, - ParserPosition start, - size_t len, +static inline void viml_parser_highlight(ParserState *pstate, ParserPosition start, size_t len, const char *group) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL; -- cgit From 2f9b94a26836ecb081c717e23913f5b6576cce99 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 22 Oct 2022 07:53:39 +0800 Subject: fix(ui): send grid_resize events before triggering VimResized (#20760) --- src/nvim/drawscreen.c | 52 +++++++++++++++++++++------------------------------ 1 file changed, 21 insertions(+), 31 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index a1582eac53..adf52ef6e4 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -125,6 +125,8 @@ void conceal_check_cursor_line(void) /// default_grid.Columns to access items in default_grid.chars[]. Use Rows /// and Columns for positioning text etc. where the final size of the screen is /// needed. +/// +/// @return whether resizing has been done bool default_grid_alloc(void) { static bool resizing = false; @@ -264,17 +266,28 @@ void screen_resize(int width, int height) p_lines = Rows; p_columns = Columns; - // was invoked recursively from a VimResized autocmd, handled as a loop below - if (resizing_autocmd) { - return; - } + ui_call_grid_resize(1, width, height); int retry_count = 0; resizing_autocmd = true; - bool retry_resize = true; - while (retry_resize) { - retry_resize = default_grid_alloc(); + // In rare cases, autocommands may have altered Rows or Columns, + // so retry to check if we need to allocate the screen again. + while (default_grid_alloc()) { + // win_new_screensize will recompute floats position, but tell the + // compositor to not redraw them yet + ui_comp_set_screen_valid(false); + if (msg_grid.chars) { + msg_grid_invalid = true; + } + + RedrawingDisabled++; + + win_new_screensize(); // fit the windows in the new sized screen + + comp_col(); // recompute columns for shown command and ruler + + RedrawingDisabled--; // Do not apply autocommands more than 3 times to avoid an endless loop // in case applying autocommands always changes Rows or Columns. @@ -282,33 +295,10 @@ void screen_resize(int width, int height) break; } - if (retry_resize) { - // In rare cases, autocommands may have altered Rows or Columns, - // retry to check if we need to allocate the screen again. - apply_autocmds(EVENT_VIMRESIZED, NULL, NULL, false, curbuf); - } + apply_autocmds(EVENT_VIMRESIZED, NULL, NULL, false, curbuf); } resizing_autocmd = false; - - ui_call_grid_resize(1, width, height); - - // win_new_screensize will recompute floats position, but tell the - // compositor to not redraw them yet - ui_comp_set_screen_valid(false); - if (msg_grid.chars) { - msg_grid_invalid = true; - } - - // Note that the window sizes are updated before reallocating the arrays, - // thus we must not redraw here! - RedrawingDisabled++; - - win_new_screensize(); // fit the windows in the new sized screen - - comp_col(); // recompute columns for shown command and ruler - - RedrawingDisabled--; redraw_all_later(UPD_CLEAR); if (starting != NO_SCREEN) { -- cgit From d70193c3260d2baa264b7a287bff352e32b33cc2 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sun, 23 Oct 2022 00:57:51 +0100 Subject: refactor(diff.c): reduce scope of variables (#20781) Co-authored-by: zeertzjq --- src/nvim/diff.c | 114 +++++++++++++++++++++++++------------------------------- 1 file changed, 50 insertions(+), 64 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 9446d35630..4fa5d0df35 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -309,8 +309,6 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T diff_T *dp = tp->tp_first_diff; linenr_T lnum_deleted = line1; // lnum of remaining deletion - linenr_T n; - linenr_T off; for (;;) { // If the change is after the previous diff block and before the next // diff block, thus not touching an existing change, create a new diff @@ -374,7 +372,8 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T // 2. 3. 4. 5.: inserted/deleted lines touching this diff. if (deleted > 0) { - off = 0; + linenr_T n; + linenr_T off = 0; if (dp->df_lnum[idx] >= line1) { if (last <= line2) { // 4. delete all lines of diff @@ -1537,14 +1536,8 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) int line_idx = 0; diff_T *dprev = NULL; diff_T *dp = curtab->tp_first_diff; - diff_T *dn, *dpl; diffout_T *dout = &dio->dio_diff; - char linebuf[LBUFLEN]; // only need to hold the diff line - char *line; - linenr_T off; - int i; int notset = true; // block "*dp" not set yet - diffhunk_T *hunk = NULL; // init to avoid gcc warning enum { DIFF_ED, DIFF_UNIFIED, @@ -1561,6 +1554,8 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) } } + diffhunk_T *hunk = NULL; + if (!dio->dio_internal) { hunk = xmalloc(sizeof(*hunk)); } @@ -1572,6 +1567,8 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) } hunk = ((diffhunk_T **)dout->dout_ga.ga_data)[line_idx++]; } else { + char *line; + char linebuf[LBUFLEN]; // only need to hold the diff line if (fd == NULL) { if (line_idx >= dout->dout_ga.ga_len) { break; // did last line @@ -1647,6 +1644,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) && (hunk->lnum_orig + hunk->count_orig >= dp->df_lnum[idx_orig])) { // New block overlaps with existing block(s). // First find last block that overlaps. + diff_T *dpl; for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next) { if (hunk->lnum_orig + hunk->count_orig < dpl->df_next->df_lnum[idx_orig]) { break; @@ -1655,10 +1653,10 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) // If the newly found block starts before the old one, set the // start back a number of lines. - off = dp->df_lnum[idx_orig] - hunk->lnum_orig; + linenr_T off = dp->df_lnum[idx_orig] - hunk->lnum_orig; if (off > 0) { - for (i = idx_orig; i < idx_new; i++) { + for (int i = idx_orig; i < idx_new; i++) { if (curtab->tp_diffbuf[i] != NULL) { dp->df_lnum[i] -= off; } @@ -1692,7 +1690,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) off = 0; } - for (i = idx_orig; i < idx_new; i++) { + for (int i = idx_orig; i < idx_new; i++) { if (curtab->tp_diffbuf[i] != NULL) { dp->df_count[i] = dpl->df_lnum[i] + dpl->df_count[i] - dp->df_lnum[i] + off; @@ -1700,7 +1698,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) } // Delete the diff blocks that have been merged into one. - dn = dp->df_next; + diff_T *dn = dp->df_next; dp->df_next = dpl->df_next; while (dn != dp->df_next) { @@ -1720,7 +1718,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) // Set values for other buffers, these must be equal to the // original buffer, otherwise there would have been a change // already. - for (i = idx_orig + 1; i < idx_new; i++) { + for (int i = idx_orig + 1; i < idx_new; i++) { if (curtab->tp_diffbuf[i] != NULL) { diff_copy_entry(dprev, dp, idx_orig, i); } @@ -1729,7 +1727,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) notset = false; // "*dp" has been set } -// for remaining diff blocks orig and new are equal + // for remaining diff blocks orig and new are equal while (dp != NULL) { if (notset) { diff_copy_entry(dprev, dp, idx_orig, idx_new); @@ -1797,7 +1795,6 @@ void diff_clear(tabpage_T *tp) /// @return diff status. int diff_check(win_T *wp, linenr_T lnum) { - diff_T *dp; buf_T *buf = wp->w_buffer; if (curtab->tp_diff_invalid) { @@ -1828,6 +1825,7 @@ int diff_check(win_T *wp, linenr_T lnum) } // search for a change that includes "lnum" in the list of diffblocks. + diff_T *dp; for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) { break; @@ -2023,7 +2021,6 @@ void diff_set_topline(win_T *fromwin, win_T *towin) { buf_T *frombuf = fromwin->w_buffer; linenr_T lnum = fromwin->w_topline; - diff_T *dp; int fromidx = diff_buf_idx(frombuf); if (fromidx == DB_COUNT) { @@ -2038,6 +2035,7 @@ void diff_set_topline(win_T *fromwin, win_T *towin) towin->w_topfill = 0; // search for a change that includes "lnum" in the list of diffblocks. + diff_T *dp; for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { if (lnum <= dp->df_lnum[fromidx] + dp->df_count[fromidx]) { break; @@ -2281,14 +2279,6 @@ bool diffopt_filler(void) bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - char *line_new; - int si_org; - int si_new; - int ei_org; - int ei_new; - bool added = true; - int l; - // Make a copy of the line, the next ml_get() will invalidate it. char *line_org = xstrdup(ml_get_buf(wp->w_buffer, lnum, false)); @@ -2313,6 +2303,12 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) return false; } + int si_org; + int si_new; + int ei_org; + int ei_new; + bool added = true; + linenr_T off = lnum - dp->df_lnum[idx]; int i; for (i = 0; i < DB_COUNT; i++) { @@ -2322,7 +2318,7 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) continue; } added = false; - line_new = ml_get_buf(curtab->tp_diffbuf[i], dp->df_lnum[i] + off, false); + char *line_new = ml_get_buf(curtab->tp_diffbuf[i], dp->df_lnum[i] + off, false); // Search for start of difference si_org = si_new = 0; @@ -2337,6 +2333,7 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) si_org = (int)(skipwhite(line_org + si_org) - line_org); si_new = (int)(skipwhite(line_new + si_new) - line_new); } else { + int l; if (!diff_equal_char((char_u *)line_org + si_org, (char_u *)line_new + si_new, &l)) { break; } @@ -2383,6 +2380,7 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) p1 -= utf_head_off(line_org, (char *)p1); p2 -= utf_head_off(line_new, (char *)p2); + int l; if (!diff_equal_char(p1, p2, &l)) { break; } @@ -2412,16 +2410,14 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) bool diff_infold(win_T *wp, linenr_T lnum) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) { - bool other = false; - // Return if 'diff' isn't set. if (!wp->w_p_diff) { return false; } int idx = -1; - int i; - for (i = 0; i < DB_COUNT; i++) { + bool other = false; + for (int i = 0; i < DB_COUNT; i++) { if (curtab->tp_diffbuf[i] == wp->w_buffer) { idx = i; } else if (curtab->tp_diffbuf[i] != NULL) { @@ -2461,13 +2457,13 @@ bool diff_infold(win_T *wp, linenr_T lnum) /// "dp" and "do" commands. void nv_diffgetput(bool put, size_t count) { - exarg_T ea; - char buf[30]; - if (bt_prompt(curbuf)) { vim_beep(BO_OPER); return; } + + exarg_T ea; + char buf[30]; if (count == 0) { ea.arg = ""; } else { @@ -2503,24 +2499,9 @@ static bool valid_diff(diff_T *diff) /// @param eap void ex_diffgetput(exarg_T *eap) { - linenr_T lnum; - linenr_T count; linenr_T off = 0; - diff_T *dp; - diff_T *dfree; - int i; - int added; - char *p; aco_save_T aco; - buf_T *buf; - linenr_T start_skip; - linenr_T end_skip; - linenr_T new_count; - int buf_empty; - int found_not_ma = false; int idx_other; - int idx_from; - int idx_to; // Find the current buffer in the list of diff buffers. int idx_cur = diff_buf_idx(curbuf); @@ -2530,6 +2511,7 @@ void ex_diffgetput(exarg_T *eap) } if (*eap->arg == NUL) { + int found_not_ma = false; // No argument: Find the other buffer in the list of diff buffers. for (idx_other = 0; idx_other < DB_COUNT; idx_other++) { if ((curtab->tp_diffbuf[idx_other] != curbuf) @@ -2552,7 +2534,7 @@ void ex_diffgetput(exarg_T *eap) } // Check that there isn't a third buffer in the list - for (i = idx_other + 1; i < DB_COUNT; i++) { + for (int i = idx_other + 1; i < DB_COUNT; i++) { if ((curtab->tp_diffbuf[i] != curbuf) && (curtab->tp_diffbuf[i] != NULL) && ((eap->cmdidx != CMD_diffput) @@ -2564,11 +2546,12 @@ void ex_diffgetput(exarg_T *eap) } } else { // Buffer number or pattern given. Ignore trailing white space. - p = eap->arg + strlen(eap->arg); + char *p = eap->arg + strlen(eap->arg); while (p > eap->arg && ascii_iswhite(p[-1])) { p--; } + int i; for (i = 0; ascii_isdigit(eap->arg[i]) && eap->arg + i < p; i++) {} if (eap->arg + i == p) { @@ -2582,7 +2565,7 @@ void ex_diffgetput(exarg_T *eap) return; } } - buf = buflist_findnr(i); + buf_T *buf = buflist_findnr(i); if (buf == NULL) { semsg(_("E102: Can't find buffer \"%s\""), eap->arg); @@ -2617,6 +2600,8 @@ void ex_diffgetput(exarg_T *eap) } } + int idx_from; + int idx_to; if (eap->cmdidx == CMD_diffget) { idx_from = idx_other; idx_to = idx_cur; @@ -2644,20 +2629,20 @@ void ex_diffgetput(exarg_T *eap) diff_T *dprev = NULL; - for (dp = curtab->tp_first_diff; dp != NULL;) { + for (diff_T *dp = curtab->tp_first_diff; dp != NULL;) { if (dp->df_lnum[idx_cur] > eap->line2 + off) { // past the range that was specified break; } - dfree = NULL; - lnum = dp->df_lnum[idx_to]; - count = dp->df_count[idx_to]; + diff_T *dfree = NULL; + linenr_T lnum = dp->df_lnum[idx_to]; + linenr_T count = dp->df_count[idx_to]; if ((dp->df_lnum[idx_cur] + dp->df_count[idx_cur] > eap->line1 + off) && (u_save(lnum - 1, lnum + count) != FAIL)) { // Inside the specified range and saving for undo worked. - start_skip = 0; - end_skip = 0; + linenr_T start_skip = 0; + linenr_T end_skip = 0; if (eap->addr_count > 0) { // A range was specified: check if lines need to be skipped. @@ -2682,7 +2667,7 @@ void ex_diffgetput(exarg_T *eap) // range ends above end of current/from diff block if (idx_cur == idx_from) { // :diffput - i = dp->df_count[idx_cur] - start_skip - end_skip; + int i = dp->df_count[idx_cur] - start_skip - end_skip; if (count > i) { count = i; @@ -2701,10 +2686,10 @@ void ex_diffgetput(exarg_T *eap) } } - buf_empty = buf_is_empty(curbuf); - added = 0; + bool buf_empty = buf_is_empty(curbuf); + int added = 0; - for (i = 0; i < count; i++) { + for (int i = 0; i < count; i++) { // remember deleting the last line of the buffer buf_empty = curbuf->b_ml.ml_line_count == 1; if (ml_delete(lnum, false) == OK) { @@ -2712,12 +2697,12 @@ void ex_diffgetput(exarg_T *eap) } } - for (i = 0; i < dp->df_count[idx_from] - start_skip - end_skip; i++) { + for (int i = 0; i < dp->df_count[idx_from] - start_skip - end_skip; i++) { linenr_T nr = dp->df_lnum[idx_from] + start_skip + i; if (nr > curtab->tp_diffbuf[idx_from]->b_ml.ml_line_count) { break; } - p = xstrdup(ml_get_buf(curtab->tp_diffbuf[idx_from], nr, false)); + char *p = xstrdup(ml_get_buf(curtab->tp_diffbuf[idx_from], nr, false)); ml_append(lnum + i - 1, p, 0, false); xfree(p); added++; @@ -2728,12 +2713,13 @@ void ex_diffgetput(exarg_T *eap) ml_delete((linenr_T)2, false); } } - new_count = dp->df_count[idx_to] + added; + linenr_T new_count = dp->df_count[idx_to] + added; dp->df_count[idx_to] = new_count; if ((start_skip == 0) && (end_skip == 0)) { // Check if there are any other buffers and if the diff is // equal in them. + int i; for (i = 0; i < DB_COUNT; i++) { if ((curtab->tp_diffbuf[i] != NULL) && (i != idx_from) -- cgit From 1887d8d7d0dd619fa90fe11182c436bc3c71c9d5 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sun, 23 Oct 2022 03:45:39 +0200 Subject: docs: fix typos (#20724) Co-authored-by: Marco Lehmann --- src/nvim/api/vim.c | 2 +- src/nvim/cmdexpand.c | 2 +- src/nvim/ex_cmds_defs.h | 2 +- src/nvim/testdir/test_arglist.vim | 2 +- src/nvim/testdir/test_langmap.vim | 2 +- src/nvim/testdir/test_menu.vim | 2 +- src/nvim/testdir/test_quickfix.vim | 2 +- src/nvim/testdir/test_vartabs.vim | 2 +- src/nvim/testdir/test_visual.vim | 2 +- src/nvim/viml/parser/expressions.h | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 32d52bef46..beb48b8d7d 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1685,7 +1685,7 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Arena *arena, Error *er // error handled after loop break; } - // TODO(bfredl): wastefull copy. It could be avoided to encoding to msgpack + // TODO(bfredl): wasteful copy. It could be avoided to encoding to msgpack // directly here. But `result` might become invalid when next api function // is called in the loop. ADD_C(results, copy_object(result, arena)); diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index 47027ae1e7..ca95a9a24e 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -2351,7 +2351,7 @@ static void expand_shellcmd(char *filepat, int *num_file, char ***file, int flag } // Go over all directories in $PATH. Expand matches in that directory and - // collect them in "ga". When "." is not in $PATH also expaned for the + // collect them in "ga". When "." is not in $PATH also expand for the // current directory, to find "subdir/cmd". ga_init(&ga, (int)sizeof(char *), 10); hashtab_T found_ht; diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index 71956b2246..0015a82880 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -62,7 +62,7 @@ #define EX_FLAGS 0x200000u // allow flags after count in argument #define EX_LOCK_OK 0x1000000u // command can be executed when textlock is // set; when missing disallows editing another - // buffer when current buffer is locked + // buffer when curbuf->b_ro_locked is set #define EX_KEEPSCRIPT 0x4000000u // keep sctx of where command was invoked #define EX_PREVIEW 0x8000000u // allow incremental command preview #define EX_FILES (EX_XFILE | EX_EXTRA) // multiple extra files allowed diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim index 0fd65e8f5a..443a217143 100644 --- a/src/nvim/testdir/test_arglist.vim +++ b/src/nvim/testdir/test_arglist.vim @@ -550,7 +550,7 @@ func Test_argdo() bwipe Xa.c Xb.c Xc.c endfunc -" Test for quiting Vim with unedited files in the argument list +" Test for quitting Vim with unedited files in the argument list func Test_quit_with_arglist() if !CanRunVimInTerminal() throw 'Skipped: cannot run vim in terminal' diff --git a/src/nvim/testdir/test_langmap.vim b/src/nvim/testdir/test_langmap.vim index 2284704603..aaed77e109 100644 --- a/src/nvim/testdir/test_langmap.vim +++ b/src/nvim/testdir/test_langmap.vim @@ -52,7 +52,7 @@ func Test_langmap() set langmap=RL let g:counter = 0 nnoremap L;L let g:counter += 1 - nnoremap throw 'This mapping shoud not be triggered' + nnoremap throw 'This mapping should not be triggered' " 'langmap' is applied to keys without modifiers when matching a mapping call feedkeys('R;R', 'tx') diff --git a/src/nvim/testdir/test_menu.vim b/src/nvim/testdir/test_menu.vim index db7ec92bf8..2e149ad5a5 100644 --- a/src/nvim/testdir/test_menu.vim +++ b/src/nvim/testdir/test_menu.vim @@ -429,7 +429,7 @@ func Test_menu_special() nunmenu Test.Sign endfunc -" Test for "icon=filname" in a toolbar +" Test for "icon=filename" in a toolbar func Test_menu_icon() CheckFeature toolbar nmenu icon=myicon.xpm Toolbar.Foo :echo "Foo" diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index b0e83b7f5f..dcedfe26a2 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -3251,7 +3251,7 @@ func Test_cclose_in_autocmd() " call test_override('starting', 0) endfunc -" Check that ":file" without an argument is possible even when curbuf is locked +" Check that ":file" without an argument is possible even when "curbuf->b_ro_locked" " is set. func Test_file_from_copen() " Works without argument. diff --git a/src/nvim/testdir/test_vartabs.vim b/src/nvim/testdir/test_vartabs.vim index 0acd7fc1e5..32ad64cda4 100644 --- a/src/nvim/testdir/test_vartabs.vim +++ b/src/nvim/testdir/test_vartabs.vim @@ -97,7 +97,7 @@ func Test_vartabs() .retab! call assert_equal("\t\t\t\tl", getline(1)) - " Test for 'retab' with same vlaues as vts + " Test for 'retab' with same values as vts set ts=8 sts=0 vts=5,3,6,2 vsts= exe "norm! S l" .retab! 5,3,6,2 diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 7fb34ec81f..a9c057088d 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -1281,7 +1281,7 @@ func Test_visual_block_with_virtualedit() endfunc func Test_visual_block_ctrl_w_f() - " Emtpy block selected in new buffer should not result in an error. + " Empty block selected in new buffer should not result in an error. au! BufNew foo sil norm f edit foo diff --git a/src/nvim/viml/parser/expressions.h b/src/nvim/viml/parser/expressions.h index 9d0bc9d468..77fbfa615f 100644 --- a/src/nvim/viml/parser/expressions.h +++ b/src/nvim/viml/parser/expressions.h @@ -357,7 +357,7 @@ typedef struct { int arg_len; } ExprASTError; -/// Structure representing complety AST for one expression +/// Structure representing complete AST for one expression typedef struct { /// When AST is not correct this message will be printed. /// -- cgit From 57fdfc1c906c15aeff240be616f2adf0c0f776ba Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sun, 23 Oct 2022 12:01:36 +0100 Subject: refactor(diff.c): break up ex_diffgetput() Problem: ex_diffgetput is too big Solution: factor out main loop into function Co-authored-by: zeertzjq --- src/nvim/diff.c | 102 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 55 insertions(+), 47 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 4fa5d0df35..e3f5cf3e30 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -2499,8 +2499,6 @@ static bool valid_diff(diff_T *diff) /// @param eap void ex_diffgetput(exarg_T *eap) { - linenr_T off = 0; - aco_save_T aco; int idx_other; // Find the current buffer in the list of diff buffers. @@ -2600,15 +2598,9 @@ void ex_diffgetput(exarg_T *eap) } } - int idx_from; - int idx_to; - if (eap->cmdidx == CMD_diffget) { - idx_from = idx_other; - idx_to = idx_cur; - } else { - idx_from = idx_cur; - idx_to = idx_other; + aco_save_T aco; + if (eap->cmdidx != CMD_diffget) { // Need to make the other buffer the current buffer to be able to make // changes in it. @@ -2616,6 +2608,9 @@ void ex_diffgetput(exarg_T *eap) aucmd_prepbuf(&aco, curtab->tp_diffbuf[idx_other]); } + const int idx_from = eap->cmdidx == CMD_diffget ? idx_other : idx_cur; + const int idx_to = eap->cmdidx == CMD_diffget ? idx_cur : idx_other; + // May give the warning for a changed buffer here, which can trigger the // FileChangedRO autocommand, which may do nasty things and mess // everything up. @@ -2627,10 +2622,55 @@ void ex_diffgetput(exarg_T *eap) } } + diffgetput(eap->addr_count, idx_cur, idx_from, idx_to, eap->line1, eap->line2); + + // restore curwin/curbuf and a few other things + if (eap->cmdidx != CMD_diffget) { + // Syncing undo only works for the current buffer, but we change + // another buffer. Sync undo if the command was typed. This isn't + // 100% right when ":diffput" is used in a function or mapping. + if (KeyTyped) { + u_sync(false); + } + aucmd_restbuf(&aco); + } + +theend: + diff_busy = false; + + if (diff_need_update) { + ex_diffupdate(NULL); + } + + // Check that the cursor is on a valid character and update its + // position. When there were filler lines the topline has become + // invalid. + check_cursor(); + changed_line_abv_curs(); + + if (diff_need_update) { + // redraw already done by ex_diffupdate() + diff_need_update = false; + } else { + // Also need to redraw the other buffers. + diff_redraw(false); + apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, false, curbuf); + } +} + +/// Apply diffget/diffput to buffers and diffblocks +/// +/// @param idx_cur index of "curbuf" before aucmd_prepbuf() in the list of diff buffers +/// @param idx_from index of the buffer to read from in the list of diff buffers +/// @param idx_to index of the buffer to modify in the list of diff buffers +static void diffgetput(const int addr_count, const int idx_cur, const int idx_from, + const int idx_to, const linenr_T line1, const linenr_T line2) +{ + linenr_T off = 0; diff_T *dprev = NULL; for (diff_T *dp = curtab->tp_first_diff; dp != NULL;) { - if (dp->df_lnum[idx_cur] > eap->line2 + off) { + if (dp->df_lnum[idx_cur] > line2 + off) { // past the range that was specified break; } @@ -2638,15 +2678,15 @@ void ex_diffgetput(exarg_T *eap) linenr_T lnum = dp->df_lnum[idx_to]; linenr_T count = dp->df_count[idx_to]; - if ((dp->df_lnum[idx_cur] + dp->df_count[idx_cur] > eap->line1 + off) + if ((dp->df_lnum[idx_cur] + dp->df_count[idx_cur] > line1 + off) && (u_save(lnum - 1, lnum + count) != FAIL)) { // Inside the specified range and saving for undo worked. linenr_T start_skip = 0; linenr_T end_skip = 0; - if (eap->addr_count > 0) { + if (addr_count > 0) { // A range was specified: check if lines need to be skipped. - start_skip = eap->line1 + off - dp->df_lnum[idx_cur]; + start_skip = line1 + off - dp->df_lnum[idx_cur]; if (start_skip > 0) { // range starts below start of current diff block if (start_skip > count) { @@ -2661,7 +2701,7 @@ void ex_diffgetput(exarg_T *eap) } end_skip = dp->df_lnum[idx_cur] + dp->df_count[idx_cur] - 1 - - (eap->line2 + off); + - (line2 + off); if (end_skip > 0) { // range ends above end of current/from diff block @@ -2786,38 +2826,6 @@ void ex_diffgetput(exarg_T *eap) dp = dp->df_next; } } - - // restore curwin/curbuf and a few other things - if (eap->cmdidx != CMD_diffget) { - // Syncing undo only works for the current buffer, but we change - // another buffer. Sync undo if the command was typed. This isn't - // 100% right when ":diffput" is used in a function or mapping. - if (KeyTyped) { - u_sync(false); - } - aucmd_restbuf(&aco); - } - -theend: - diff_busy = false; - if (diff_need_update) { - ex_diffupdate(NULL); - } - - // Check that the cursor is on a valid character and update its - // position. When there were filler lines the topline has become - // invalid. - check_cursor(); - changed_line_abv_curs(); - - if (diff_need_update) { - // redraw already done by ex_diffupdate() - diff_need_update = false; - } else { - // Also need to redraw the other buffers. - diff_redraw(false); - apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, false, curbuf); - } } /// Update folds for all diff buffers for entry "dp". -- cgit From c5f280c522d77b14e3f138797b6628f50fa80c14 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 24 Oct 2022 13:48:51 +0800 Subject: vim-patch:8.2.0908: crash when changing the function table while listing it Problem: Crash when changing the function table while listing it. Solution: Bail out when the function table changes. (closes vim/vim#6209) https://github.com/vim/vim/commit/3fffa97159a427067b60c80ed4645e168cc5c4bd Co-authored-by: Bram Moolenaar --- src/nvim/eval/userfunc.c | 58 ++++++++++++++++++++++------------------ src/nvim/testdir/test_timers.vim | 25 +++++++++++++++++ 2 files changed, 57 insertions(+), 26 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index f6530d0e92..4a62b4bf8d 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1877,6 +1877,36 @@ theend: #define MAX_FUNC_NESTING 50 +/// List functions. +/// +/// @param regmatch When NULL, all of them. +/// Otherwise functions matching "regmatch". +static void list_functions(regmatch_T *regmatch) +{ + const size_t used = func_hashtab.ht_used; + size_t todo = used; + const hashitem_T *const ht_array = func_hashtab.ht_array; + + for (const hashitem_T *hi = ht_array; todo > 0 && !got_int; hi++) { + if (!HASHITEM_EMPTY(hi)) { + ufunc_T *fp = HI2UF(hi); + todo--; + if ((fp->uf_flags & FC_DEAD) == 0 + && (regmatch == NULL + ? (!message_filtered((char *)fp->uf_name) + && !func_name_refcount(fp->uf_name)) + : (!isdigit(*fp->uf_name) + && vim_regexec(regmatch, (char *)fp->uf_name, 0)))) { + list_func_head(fp, false, false); + if (used != func_hashtab.ht_used || ht_array != func_hashtab.ht_array) { + emsg(_("E454: function list was modified")); + return; + } + } + } + } +} + /// ":function" void ex_function(exarg_T *eap) { @@ -1903,7 +1933,6 @@ void ex_function(exarg_T *eap) static int func_nr = 0; // number for nameless function int paren; hashtab_T *ht; - int todo; hashitem_T *hi; linenr_T sourcing_lnum_off; linenr_T sourcing_lnum_top; @@ -1916,19 +1945,7 @@ void ex_function(exarg_T *eap) // ":function" without argument: list functions. if (ends_excmd(*eap->arg)) { if (!eap->skip) { - todo = (int)func_hashtab.ht_used; - for (hi = func_hashtab.ht_array; todo > 0 && !got_int; hi++) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - fp = HI2UF(hi); - if (message_filtered((char *)fp->uf_name)) { - continue; - } - if (!func_name_refcount(fp->uf_name)) { - list_func_head(fp, false, false); - } - } - } + list_functions(NULL); } eap->nextcmd = check_nextcmd(eap->arg); return; @@ -1946,18 +1963,7 @@ void ex_function(exarg_T *eap) *p = c; if (regmatch.regprog != NULL) { regmatch.rm_ic = p_ic; - - todo = (int)func_hashtab.ht_used; - for (hi = func_hashtab.ht_array; todo > 0 && !got_int; hi++) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - fp = HI2UF(hi); - if (!isdigit(*fp->uf_name) - && vim_regexec(®match, (char *)fp->uf_name, 0)) { - list_func_head(fp, false, false); - } - } - } + list_functions(®match); vim_regfree(regmatch.regprog); } } diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index 4d450e180b..771f61442d 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -3,6 +3,7 @@ source check.vim CheckFeature timers +source screendump.vim source shared.vim source term_util.vim source load.vim @@ -408,6 +409,30 @@ func Test_timer_invalid_callback() call assert_fails('call timer_start(0, "0")', 'E921') endfunc +func Test_timer_changing_function_list() + CheckRunVimInTerminal + + " Create a large number of functions. Should get the "more" prompt. + " The typing "G" triggers the timer, which changes the function table. + let lines =<< trim END + for func in map(range(1,99), "'Func' .. v:val") + exe "func " .. func .. "()" + endfunc + endfor + au CmdlineLeave : call timer_start(0, {-> 0}) + END + call writefile(lines, 'XTest_timerchange') + let buf = RunVimInTerminal('-S XTest_timerchange', #{rows: 10}) + call term_sendkeys(buf, ":fu\") + call WaitForAssert({-> assert_match('-- More --', term_getline(buf, 10))}) + call term_sendkeys(buf, "G") + call WaitForAssert({-> assert_match('E454', term_getline(buf, 9))}) + call term_sendkeys(buf, "\") + + call StopVimInTerminal(buf) + call delete('XTest_timerchange') +endfunc + func Test_timer_using_win_execute_undo_sync() let bufnr1 = bufnr() new -- cgit From a6e404b7d58654c4e160bb534ca418369736b20b Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sun, 23 Oct 2022 12:24:33 +0100 Subject: refactor(diff.c): remove char_u declarations --- src/nvim/diff.c | 130 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 64 insertions(+), 66 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index e3f5cf3e30..4b1604ea07 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -74,13 +74,13 @@ static TriState diff_a_works = kNone; // used for diff input typedef struct { - char_u *din_fname; // used for external diff + char *din_fname; // used for external diff mmfile_t din_mmfile; // used for internal diff } diffin_T; // used for diff result typedef struct { - char_u *dout_fname; // used for external diff + char *dout_fname; // used for external diff garray_T dout_ga; // used for internal diff } diffout_T; @@ -703,7 +703,7 @@ static void clear_diffin(diffin_T *din) if (din->din_fname == NULL) { XFREE_CLEAR(din->din_mmfile.ptr); } else { - os_remove((char *)din->din_fname); + os_remove(din->din_fname); } } @@ -712,7 +712,7 @@ static void clear_diffout(diffout_T *dout) if (dout->dout_fname == NULL) { ga_clear_strings(&dout->dout_ga); } else { - os_remove((char *)dout->dout_fname); + os_remove(dout->dout_fname); } } @@ -730,7 +730,7 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din) for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { len += (long)strlen(ml_get_buf(buf, lnum, false)) + 1; } - char_u *ptr = try_malloc((size_t)len); + char *ptr = try_malloc((size_t)len); if (ptr == NULL) { // Allocating memory failed. This can happen, because we try to read // the whole buffer text into memory. Set the failed flag, the diff @@ -744,12 +744,12 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din) } return FAIL; } - din->din_mmfile.ptr = (char *)ptr; + din->din_mmfile.ptr = ptr; din->din_mmfile.size = len; len = 0; for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { - for (char_u *s = (char_u *)ml_get_buf(buf, lnum, false); *s != NUL;) { + for (char *s = ml_get_buf(buf, lnum, false); *s != NUL;) { if (diff_flags & DIFF_ICASE) { int c; char cbuf[MB_MAXBYTES + 1]; @@ -758,11 +758,11 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din) c = NUL; } else { // xdiff doesn't support ignoring case, fold-case the text. - c = utf_ptr2char((char *)s); + c = utf_ptr2char(s); c = utf_fold(c); } - const int orig_len = utfc_ptr2len((char *)s); - if (utf_char2bytes(c, (char *)cbuf) != orig_len) { + const int orig_len = utfc_ptr2len(s); + if (utf_char2bytes(c, cbuf) != orig_len) { // TODO(Bram): handle byte length difference memmove(ptr + len, s, (size_t)orig_len); } else { @@ -802,7 +802,7 @@ static int diff_write(buf_T *buf, diffin_T *din) // Writing the buffer is an implementation detail of performing the diff, // so it shouldn't update the '[ and '] marks. cmdmod.cmod_flags |= CMOD_LOCKMARKS; - int r = buf_write(buf, (char *)din->din_fname, NULL, + int r = buf_write(buf, din->din_fname, NULL, (linenr_T)1, buf->b_ml.ml_line_count, NULL, false, false, false, true); cmdmod.cmod_flags = save_cmod_flags; @@ -823,9 +823,9 @@ static void diff_try_update(diffio_T *dio, int idx_orig, exarg_T *eap) ga_init(&dio->dio_diff.dout_ga, sizeof(char *), 1000); } else { // We need three temp file names. - dio->dio_orig.din_fname = (char_u *)vim_tempname(); - dio->dio_new.din_fname = (char_u *)vim_tempname(); - dio->dio_diff.dout_fname = (char_u *)vim_tempname(); + dio->dio_orig.din_fname = vim_tempname(); + dio->dio_new.din_fname = vim_tempname(); + dio->dio_diff.dout_fname = vim_tempname(); if (dio->dio_orig.din_fname == NULL || dio->dio_new.din_fname == NULL || dio->dio_diff.dout_fname == NULL) { @@ -991,7 +991,7 @@ static int check_external_diff(diffio_T *diffio) TriState ok = kFalse; for (;;) { ok = kFalse; - FILE *fd = os_fopen((char *)diffio->dio_orig.din_fname, "w"); + FILE *fd = os_fopen(diffio->dio_orig.din_fname, "w"); if (fd == NULL) { io_error = true; @@ -1000,7 +1000,7 @@ static int check_external_diff(diffio_T *diffio) io_error = true; } fclose(fd); - fd = os_fopen((char *)diffio->dio_new.din_fname, "w"); + fd = os_fopen(diffio->dio_new.din_fname, "w"); if (fd == NULL) { io_error = true; @@ -1011,18 +1011,18 @@ static int check_external_diff(diffio_T *diffio) fclose(fd); fd = NULL; if (diff_file(diffio) == OK) { - fd = os_fopen((char *)diffio->dio_diff.dout_fname, "r"); + fd = os_fopen(diffio->dio_diff.dout_fname, "r"); } if (fd == NULL) { io_error = true; } else { - char_u linebuf[LBUFLEN]; + char linebuf[LBUFLEN]; for (;;) { // For normal diff there must be a line that contains // "1c1". For unified diff "@@ -1 +1 @@". - if (vim_fgets(linebuf, LBUFLEN, fd)) { + if (vim_fgets((char_u *)linebuf, LBUFLEN, fd)) { break; } @@ -1033,10 +1033,10 @@ static int check_external_diff(diffio_T *diffio) } fclose(fd); } - os_remove((char *)diffio->dio_diff.dout_fname); - os_remove((char *)diffio->dio_new.din_fname); + os_remove(diffio->dio_diff.dout_fname); + os_remove(diffio->dio_new.din_fname); } - os_remove((char *)diffio->dio_orig.din_fname); + os_remove(diffio->dio_orig.din_fname); } // When using 'diffexpr' break here. @@ -1114,9 +1114,9 @@ static int diff_file_internal(diffio_T *diffio) /// @return OK or FAIL static int diff_file(diffio_T *dio) { - char *tmp_orig = (char *)dio->dio_orig.din_fname; - char *tmp_new = (char *)dio->dio_new.din_fname; - char *tmp_diff = (char *)dio->dio_diff.dout_fname; + char *tmp_orig = dio->dio_orig.din_fname; + char *tmp_new = dio->dio_new.din_fname; + char *tmp_diff = dio->dio_diff.dout_fname; if (*p_dex != NUL) { // Use 'diffexpr' to generate the diff file. eval_diff(tmp_orig, tmp_new, tmp_diff); @@ -1166,10 +1166,10 @@ static int diff_file(diffio_T *dio) /// @param eap void ex_diffpatch(exarg_T *eap) { - char_u *buf = NULL; + char *buf = NULL; win_T *old_curwin = curwin; char *newname = NULL; // name of patched file buffer - char_u *esc_name = NULL; + char *esc_name = NULL; #ifdef UNIX char *fullname = NULL; @@ -1177,16 +1177,16 @@ void ex_diffpatch(exarg_T *eap) // We need two temp file names. // Name of original temp file. - char_u *tmp_orig = (char_u *)vim_tempname(); + char *tmp_orig = vim_tempname(); // Name of patched temp file. - char_u *tmp_new = (char_u *)vim_tempname(); + char *tmp_new = vim_tempname(); if ((tmp_orig == NULL) || (tmp_new == NULL)) { goto theend; } // Write the current buffer to "tmp_orig". - if (buf_write(curbuf, (char *)tmp_orig, NULL, + if (buf_write(curbuf, tmp_orig, NULL, (linenr_T)1, curbuf->b_ml.ml_line_count, NULL, false, false, false, true) == FAIL) { goto theend; @@ -1196,7 +1196,7 @@ void ex_diffpatch(exarg_T *eap) // Get the absolute path of the patchfile, changing directory below. fullname = FullName_save(eap->arg, false); esc_name = - vim_strsave_shellescape((char_u *)(fullname != NULL ? fullname : eap->arg), true, true); + (char *)vim_strsave_shellescape((char_u *)(fullname != NULL ? fullname : eap->arg), true, true); #else esc_name = vim_strsave_shellescape(eap->arg, true, true); #endif @@ -1204,14 +1204,14 @@ void ex_diffpatch(exarg_T *eap) buf = xmalloc(buflen); #ifdef UNIX - char_u dirbuf[MAXPATHL]; + char dirbuf[MAXPATHL]; // Temporarily chdir to /tmp, to avoid patching files in the current // directory when the patch file contains more than one patch. When we // have our own temp dir use that instead, it will be cleaned up when we // exit (any .rej files created). Don't change directory if we can't // return to the current. - if ((os_dirname(dirbuf, MAXPATHL) != OK) - || (os_chdir((char *)dirbuf) != 0)) { + if ((os_dirname((char_u *)dirbuf, MAXPATHL) != OK) + || (os_chdir(dirbuf) != 0)) { dirbuf[0] = NUL; } else { char *tempdir = vim_gettempdir(); @@ -1226,24 +1226,22 @@ void ex_diffpatch(exarg_T *eap) if (*p_pex != NUL) { // Use 'patchexpr' to generate the new file. #ifdef UNIX - eval_patch((char *)tmp_orig, - (fullname != NULL ? fullname : eap->arg), - (char *)tmp_new); + eval_patch(tmp_orig, (fullname != NULL ? fullname : eap->arg), tmp_new); #else - eval_patch((char *)tmp_orig, (char *)eap->arg, (char *)tmp_new); + eval_patch(tmp_orig, eap->arg, tmp_new); #endif } else { // Build the patch command and execute it. Ignore errors. - vim_snprintf((char *)buf, buflen, "patch -o %s %s < %s", + vim_snprintf(buf, buflen, "patch -o %s %s < %s", tmp_new, tmp_orig, esc_name); block_autocmds(); // Avoid ShellCmdPost stuff - (void)call_shell(buf, kShellOptFilter, NULL); + (void)call_shell((char_u *)buf, kShellOptFilter, NULL); unblock_autocmds(); } #ifdef UNIX if (dirbuf[0] != NUL) { - if (os_chdir((char *)dirbuf) != 0) { + if (os_chdir(dirbuf) != 0) { emsg(_(e_prev_dir)); } shorten_fnames(true); @@ -1253,14 +1251,14 @@ void ex_diffpatch(exarg_T *eap) // Delete any .orig or .rej file created. STRCPY(buf, tmp_new); STRCAT(buf, ".orig"); - os_remove((char *)buf); + os_remove(buf); STRCPY(buf, tmp_new); STRCAT(buf, ".rej"); - os_remove((char *)buf); + os_remove(buf); // Only continue if the output file was created. FileInfo file_info; - bool info_ok = os_fileinfo((char *)tmp_new, &file_info); + bool info_ok = os_fileinfo(tmp_new, &file_info); uint64_t filesize = os_fileinfo_size(&file_info); if (!info_ok || filesize == 0) { emsg(_("E816: Cannot read patch output")); @@ -1276,7 +1274,7 @@ void ex_diffpatch(exarg_T *eap) if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL) { // Pretend it was a ":split fname" command eap->cmdidx = CMD_split; - eap->arg = (char *)tmp_new; + eap->arg = tmp_new; do_exedit(eap, old_curwin); // check that split worked and editing tmp_new @@ -1301,12 +1299,12 @@ void ex_diffpatch(exarg_T *eap) theend: if (tmp_orig != NULL) { - os_remove((char *)tmp_orig); + os_remove(tmp_orig); } xfree(tmp_orig); if (tmp_new != NULL) { - os_remove((char *)tmp_new); + os_remove(tmp_new); } xfree(tmp_new); xfree(newname); @@ -1547,7 +1545,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) if (dout->dout_fname == NULL) { diffstyle = DIFF_UNIFIED; } else { - fd = os_fopen((char *)dout->dout_fname, "r"); + fd = os_fopen(dout->dout_fname, "r"); if (fd == NULL) { emsg(_("E98: Cannot read diff output")); return; @@ -1613,7 +1611,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) if (!isdigit(*line)) { continue; // not the start of a diff block } - if (parse_diff_ed((char_u *)line, hunk) == FAIL) { + if (parse_diff_ed(line, hunk) == FAIL) { continue; } } else { @@ -1621,7 +1619,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) if (STRNCMP(line, "@@ ", 3) != 0) { continue; // not the start of a diff block } - if (parse_diff_unified((char_u *)line, hunk) == FAIL) { + if (parse_diff_unified(line, hunk) == FAIL) { continue; } } @@ -1934,24 +1932,24 @@ static bool diff_equal_entry(diff_T *dp, int idx1, int idx2) // Compare the characters at "p1" and "p2". If they are equal (possibly // ignoring case) return true and set "len" to the number of bytes. -static bool diff_equal_char(const char_u *const p1, const char_u *const p2, int *const len) +static bool diff_equal_char(const char *const p1, const char *const p2, int *const len) { - const int l = utfc_ptr2len((char *)p1); + const int l = utfc_ptr2len(p1); - if (l != utfc_ptr2len((char *)p2)) { + if (l != utfc_ptr2len(p2)) { return false; } if (l > 1) { if (STRNCMP(p1, p2, l) != 0 && (!(diff_flags & DIFF_ICASE) - || utf_fold(utf_ptr2char((char *)p1)) != utf_fold(utf_ptr2char((char *)p2)))) { + || utf_fold(utf_ptr2char(p1)) != utf_fold(utf_ptr2char(p2)))) { return false; } *len = l; } else { if ((*p1 != *p2) && (!(diff_flags & DIFF_ICASE) - || TOLOWER_LOC(*p1) != TOLOWER_LOC(*p2))) { + || TOLOWER_LOC((uint8_t)(*p1)) != TOLOWER_LOC((uint8_t)(*p2)))) { return false; } *len = 1; @@ -1978,7 +1976,7 @@ static int diff_cmp(char *s1, char *s2) } if ((diff_flags & DIFF_ICASE) && !(diff_flags & ALL_WHITE_DIFF)) { - return mb_stricmp((const char *)s1, (const char *)s2); + return mb_stricmp(s1, s2); } char *p1 = s1; @@ -1994,7 +1992,7 @@ static int diff_cmp(char *s1, char *s2) p2 = skipwhite(p2); } else { int l; - if (!diff_equal_char((char_u *)p1, (char_u *)p2, &l)) { + if (!diff_equal_char(p1, p2, &l)) { break; } p1 += l; @@ -2334,7 +2332,7 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) si_new = (int)(skipwhite(line_new + si_new) - line_new); } else { int l; - if (!diff_equal_char((char_u *)line_org + si_org, (char_u *)line_new + si_new, &l)) { + if (!diff_equal_char(line_org + si_org, line_new + si_new, &l)) { break; } si_org += l; @@ -2374,11 +2372,11 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) ei_new--; } } else { - const char_u *p1 = (char_u *)line_org + ei_org; - const char_u *p2 = (char_u *)line_new + ei_new; + const char *p1 = line_org + ei_org; + const char *p2 = line_new + ei_new; - p1 -= utf_head_off(line_org, (char *)p1); - p2 -= utf_head_off(line_new, (char *)p2); + p1 -= utf_head_off(line_org, p1); + p2 -= utf_head_off(line_new, p2); int l; if (!diff_equal_char(p1, p2, &l)) { @@ -3049,7 +3047,7 @@ linenr_T diff_lnum_win(linenr_T lnum, win_T *wp) /// Handle an ED style diff line. /// Return FAIL if the line does not contain diff info. /// -static int parse_diff_ed(char_u *line, diffhunk_T *hunk) +static int parse_diff_ed(char *line, diffhunk_T *hunk) { long l1, l2; @@ -3057,7 +3055,7 @@ static int parse_diff_ed(char_u *line, diffhunk_T *hunk) // change: {first}[,{last}]c{first}[,{last}] // append: {first}a{first}[,{last}] // delete: {first}[,{last}]d{first} - char *p = (char *)line; + char *p = line; linenr_T f1 = getdigits_int32(&p, true, 0); if (*p == ',') { p++; @@ -3101,11 +3099,11 @@ static int parse_diff_ed(char_u *line, diffhunk_T *hunk) /// Parses unified diff with zero(!) context lines. /// Return FAIL if there is no diff information in "line". /// -static int parse_diff_unified(char_u *line, diffhunk_T *hunk) +static int parse_diff_unified(char *line, diffhunk_T *hunk) { // Parse unified diff hunk header: // @@ -oldline,oldcount +newline,newcount @@ - char *p = (char *)line; + char *p = line; if (*p++ == '@' && *p++ == '@' && *p++ == ' ' && *p++ == '-') { long oldcount; long newline; -- cgit From 267494151bcc6e4fff7b9b8c80b7a5f42e2aa002 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sun, 23 Oct 2022 12:38:19 +0100 Subject: refactor(diff.c): allocate hunks directly in ga_array --- src/nvim/diff.c | 65 +++++++++++++++++++++++++-------------------------------- 1 file changed, 28 insertions(+), 37 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 4b1604ea07..0efd0e8bf1 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -26,6 +26,7 @@ #include "nvim/ex_docmd.h" #include "nvim/fileio.h" #include "nvim/fold.h" +#include "nvim/garray.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" @@ -710,7 +711,7 @@ static void clear_diffin(diffin_T *din) static void clear_diffout(diffout_T *dout) { if (dout->dout_fname == NULL) { - ga_clear_strings(&dout->dout_ga); + ga_clear(&dout->dout_ga); } else { os_remove(dout->dout_fname); } @@ -820,7 +821,7 @@ static int diff_write(buf_T *buf, diffin_T *din) static void diff_try_update(diffio_T *dio, int idx_orig, exarg_T *eap) { if (dio->dio_internal) { - ga_init(&dio->dio_diff.dout_ga, sizeof(char *), 1000); + ga_init(&dio->dio_diff.dout_ga, sizeof(diffhunk_T), 100); } else { // We need three temp file names. dio->dio_orig.din_fname = vim_tempname(); @@ -1552,18 +1553,14 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) } } - diffhunk_T *hunk = NULL; - - if (!dio->dio_internal) { - hunk = xmalloc(sizeof(*hunk)); - } + diffhunk_T hunk; for (;;) { if (dio->dio_internal) { if (line_idx >= dout->dout_ga.ga_len) { break; // did last line } - hunk = ((diffhunk_T **)dout->dout_ga.ga_data)[line_idx++]; + hunk = ((diffhunk_T *)dout->dout_ga.ga_data)[line_idx++]; } else { char *line; char linebuf[LBUFLEN]; // only need to hold the diff line @@ -1611,7 +1608,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) if (!isdigit(*line)) { continue; // not the start of a diff block } - if (parse_diff_ed(line, hunk) == FAIL) { + if (parse_diff_ed(line, &hunk) == FAIL) { continue; } } else { @@ -1619,7 +1616,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) if (STRNCMP(line, "@@ ", 3) != 0) { continue; // not the start of a diff block } - if (parse_diff_unified(line, hunk) == FAIL) { + if (parse_diff_unified(line, &hunk) == FAIL) { continue; } } @@ -1628,7 +1625,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) // Go over blocks before the change, for which orig and new are equal. // Copy blocks from orig to new. while (dp != NULL - && hunk->lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) { + && hunk.lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) { if (notset) { diff_copy_entry(dprev, dp, idx_orig, idx_new); } @@ -1638,20 +1635,20 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) } if ((dp != NULL) - && (hunk->lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) - && (hunk->lnum_orig + hunk->count_orig >= dp->df_lnum[idx_orig])) { + && (hunk.lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) + && (hunk.lnum_orig + hunk.count_orig >= dp->df_lnum[idx_orig])) { // New block overlaps with existing block(s). // First find last block that overlaps. diff_T *dpl; for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next) { - if (hunk->lnum_orig + hunk->count_orig < dpl->df_next->df_lnum[idx_orig]) { + if (hunk.lnum_orig + hunk.count_orig < dpl->df_next->df_lnum[idx_orig]) { break; } } // If the newly found block starts before the old one, set the // start back a number of lines. - linenr_T off = dp->df_lnum[idx_orig] - hunk->lnum_orig; + linenr_T off = dp->df_lnum[idx_orig] - hunk.lnum_orig; if (off > 0) { for (int i = idx_orig; i < idx_new; i++) { @@ -1659,15 +1656,15 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) dp->df_lnum[i] -= off; } } - dp->df_lnum[idx_new] = hunk->lnum_new; - dp->df_count[idx_new] = (linenr_T)hunk->count_new; + dp->df_lnum[idx_new] = hunk.lnum_new; + dp->df_count[idx_new] = (linenr_T)hunk.count_new; } else if (notset) { // new block inside existing one, adjust new block - dp->df_lnum[idx_new] = hunk->lnum_new + off; - dp->df_count[idx_new] = (linenr_T)hunk->count_new - off; + dp->df_lnum[idx_new] = hunk.lnum_new + off; + dp->df_count[idx_new] = (linenr_T)hunk.count_new - off; } else { // second overlap of new block with existing block - dp->df_count[idx_new] += (linenr_T)hunk->count_new - (linenr_T)hunk->count_orig + dp->df_count[idx_new] += (linenr_T)hunk.count_new - (linenr_T)hunk.count_orig + dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig] - (dp->df_lnum[idx_orig] + @@ -1676,7 +1673,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) // Adjust the size of the block to include all the lines to the // end of the existing block or the new diff, whatever ends last. - off = (hunk->lnum_orig + (linenr_T)hunk->count_orig) + off = (hunk.lnum_orig + (linenr_T)hunk.count_orig) - (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]); if (off < 0) { @@ -1708,10 +1705,10 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) // Allocate a new diffblock. dp = diff_alloc_new(curtab, dprev, dp); - dp->df_lnum[idx_orig] = hunk->lnum_orig; - dp->df_count[idx_orig] = (linenr_T)hunk->count_orig; - dp->df_lnum[idx_new] = hunk->lnum_new; - dp->df_count[idx_new] = (linenr_T)hunk->count_new; + dp->df_lnum[idx_orig] = hunk.lnum_orig; + dp->df_count[idx_orig] = (linenr_T)hunk.count_orig; + dp->df_lnum[idx_new] = hunk.lnum_new; + dp->df_count[idx_new] = (linenr_T)hunk.count_new; // Set values for other buffers, these must be equal to the // original buffer, otherwise there would have been a change @@ -1735,10 +1732,6 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) notset = true; } - if (!dio->dio_internal) { - xfree(hunk); - } - if (fd != NULL) { fclose(fd); } @@ -3155,13 +3148,11 @@ static int parse_diff_unified(char *line, diffhunk_T *hunk) static int xdiff_out(long start_a, long count_a, long start_b, long count_b, void *priv) { diffout_T *dout = (diffout_T *)priv; - diffhunk_T *p = xmalloc(sizeof(*p)); - - ga_grow(&dout->dout_ga, 1); - p->lnum_orig = (linenr_T)start_a + 1; - p->count_orig = count_a; - p->lnum_new = (linenr_T)start_b + 1; - p->count_new = count_b; - ((diffhunk_T **)dout->dout_ga.ga_data)[dout->dout_ga.ga_len++] = p; + GA_APPEND(diffhunk_T, &(dout->dout_ga), ((diffhunk_T){ + .lnum_orig = (linenr_T)start_a + 1, + .count_orig = count_a, + .lnum_new = (linenr_T)start_b + 1, + .count_new = count_b, + })); return 0; } -- cgit From 29f138d5ad9a6609efaee52e053b288c7ac86c8b Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sun, 23 Oct 2022 15:08:50 +0100 Subject: refactor(diff.c): factor out hunk extraction --- src/nvim/diff.c | 153 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 82 insertions(+), 71 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 0efd0e8bf1..9fd9976cbe 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -101,6 +101,12 @@ typedef struct { int dio_internal; // using internal diff } diffio_T; +typedef enum { + DIFF_ED, + DIFF_UNIFIED, + DIFF_NONE, +} diffstyle_T; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "diff.c.generated.h" #endif @@ -1524,6 +1530,74 @@ void ex_diffoff(exarg_T *eap) } } +static bool extract_hunk_internal(diffout_T *dout, diffhunk_T *hunk, int *line_idx) +{ + bool eof = *line_idx >= dout->dout_ga.ga_len; + if (!eof) { + *hunk = ((diffhunk_T *)dout->dout_ga.ga_data)[(*line_idx)++]; + } + return eof; +} + +// Extract hunk by parsing the diff output from file and calculate the diffstyle. +static bool extract_hunk(FILE *fd, diffhunk_T *hunk, diffstyle_T *diffstyle) +{ + for (;;) { + char line[LBUFLEN]; // only need to hold the diff line + if (vim_fgets((char_u *)line, LBUFLEN, fd)) { + return true; // end of file + } + + if (*diffstyle == DIFF_NONE) { + // Determine diff style. + // ed like diff looks like this: + // {first}[,{last}]c{first}[,{last}] + // {first}a{first}[,{last}] + // {first}[,{last}]d{first} + // + // unified diff looks like this: + // --- file1 2018-03-20 13:23:35.783153140 +0100 + // +++ file2 2018-03-20 13:23:41.183156066 +0100 + // @@ -1,3 +1,5 @@ + if (isdigit(*line)) { + *diffstyle = DIFF_ED; + } else if ((STRNCMP(line, "@@ ", 3) == 0)) { + *diffstyle = DIFF_UNIFIED; + } else if ((STRNCMP(line, "--- ", 4) == 0) // -V501 + && (vim_fgets((char_u *)line, LBUFLEN, fd) == 0) // -V501 + && (STRNCMP(line, "+++ ", 4) == 0) + && (vim_fgets((char_u *)line, LBUFLEN, fd) == 0) // -V501 + && (STRNCMP(line, "@@ ", 3) == 0)) { + *diffstyle = DIFF_UNIFIED; + } else { + // Format not recognized yet, skip over this line. Cygwin diff + // may put a warning at the start of the file. + continue; + } + } + + if (*diffstyle == DIFF_ED) { + if (!isdigit(*line)) { + continue; // not the start of a diff block + } + if (parse_diff_ed(line, hunk) == FAIL) { + continue; + } + } else { + assert(*diffstyle == DIFF_UNIFIED); + if (STRNCMP(line, "@@ ", 3) != 0) { + continue; // not the start of a diff block + } + if (parse_diff_unified(line, hunk) == FAIL) { + continue; + } + } + + // Successfully parsed diff output, can return + return false; + } +} + /// Read the diff output and add each entry to the diff list. /// /// @param idx_orig idx of original file @@ -1537,13 +1611,9 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) diff_T *dp = curtab->tp_first_diff; diffout_T *dout = &dio->dio_diff; int notset = true; // block "*dp" not set yet - enum { - DIFF_ED, - DIFF_UNIFIED, - DIFF_NONE, - } diffstyle = DIFF_NONE; + diffstyle_T diffstyle = DIFF_NONE; - if (dout->dout_fname == NULL) { + if (dio->dio_internal) { diffstyle = DIFF_UNIFIED; } else { fd = os_fopen(dout->dout_fname, "r"); @@ -1553,73 +1623,14 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) } } - diffhunk_T hunk; - for (;;) { - if (dio->dio_internal) { - if (line_idx >= dout->dout_ga.ga_len) { - break; // did last line - } - hunk = ((diffhunk_T *)dout->dout_ga.ga_data)[line_idx++]; - } else { - char *line; - char linebuf[LBUFLEN]; // only need to hold the diff line - if (fd == NULL) { - if (line_idx >= dout->dout_ga.ga_len) { - break; // did last line - } - line = ((char **)dout->dout_ga.ga_data)[line_idx++]; - } else { - if (vim_fgets((char_u *)linebuf, LBUFLEN, fd)) { - break; // end of file - } - line = linebuf; - } - - if (diffstyle == DIFF_NONE) { - // Determine diff style. - // ed like diff looks like this: - // {first}[,{last}]c{first}[,{last}] - // {first}a{first}[,{last}] - // {first}[,{last}]d{first} - // - // unified diff looks like this: - // --- file1 2018-03-20 13:23:35.783153140 +0100 - // +++ file2 2018-03-20 13:23:41.183156066 +0100 - // @@ -1,3 +1,5 @@ - if (isdigit(*line)) { - diffstyle = DIFF_ED; - } else if ((STRNCMP(line, "@@ ", 3) == 0)) { - diffstyle = DIFF_UNIFIED; - } else if ((STRNCMP(line, "--- ", 4) == 0) // -V501 - && (vim_fgets((char_u *)linebuf, LBUFLEN, fd) == 0) // -V501 - && (STRNCMP(line, "+++ ", 4) == 0) - && (vim_fgets((char_u *)linebuf, LBUFLEN, fd) == 0) // -V501 - && (STRNCMP(line, "@@ ", 3) == 0)) { - diffstyle = DIFF_UNIFIED; - } else { - // Format not recognized yet, skip over this line. Cygwin diff - // may put a warning at the start of the file. - continue; - } - } + diffhunk_T hunk = { 0 }; + bool eof = dio->dio_internal + ? extract_hunk_internal(dout, &hunk, &line_idx) + : extract_hunk(fd, &hunk, &diffstyle); - if (diffstyle == DIFF_ED) { - if (!isdigit(*line)) { - continue; // not the start of a diff block - } - if (parse_diff_ed(line, &hunk) == FAIL) { - continue; - } - } else { - assert(diffstyle == DIFF_UNIFIED); - if (STRNCMP(line, "@@ ", 3) != 0) { - continue; // not the start of a diff block - } - if (parse_diff_unified(line, &hunk) == FAIL) { - continue; - } - } + if (eof) { + break; } // Go over blocks before the change, for which orig and new are equal. -- cgit From b15f7007ebd2681f08d742dcff8e371bcb47ec27 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 24 Oct 2022 09:54:02 +0100 Subject: refactor(diff.c): factor out hunk processing --- src/nvim/diff.c | 209 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 110 insertions(+), 99 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 9fd9976cbe..5069d30e46 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -1598,6 +1598,114 @@ static bool extract_hunk(FILE *fd, diffhunk_T *hunk, diffstyle_T *diffstyle) } } +static void process_hunk(diff_T **dpp, diff_T **dprevp, int idx_orig, int idx_new, diffhunk_T *hunk, + bool *notsetp) +{ + diff_T *dp = *dpp; + diff_T *dprev = *dprevp; + + // Go over blocks before the change, for which orig and new are equal. + // Copy blocks from orig to new. + while (dp != NULL + && hunk->lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) { + if (*notsetp) { + diff_copy_entry(dprev, dp, idx_orig, idx_new); + } + dprev = dp; + dp = dp->df_next; + *notsetp = true; + } + + if ((dp != NULL) + && (hunk->lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) + && (hunk->lnum_orig + hunk->count_orig >= dp->df_lnum[idx_orig])) { + // New block overlaps with existing block(s). + // First find last block that overlaps. + diff_T *dpl; + for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next) { + if (hunk->lnum_orig + hunk->count_orig < dpl->df_next->df_lnum[idx_orig]) { + break; + } + } + + // If the newly found block starts before the old one, set the + // start back a number of lines. + linenr_T off = dp->df_lnum[idx_orig] - hunk->lnum_orig; + + if (off > 0) { + for (int i = idx_orig; i < idx_new; i++) { + if (curtab->tp_diffbuf[i] != NULL) { + dp->df_lnum[i] -= off; + } + } + dp->df_lnum[idx_new] = hunk->lnum_new; + dp->df_count[idx_new] = (linenr_T)hunk->count_new; + } else if (*notsetp) { + // new block inside existing one, adjust new block + dp->df_lnum[idx_new] = hunk->lnum_new + off; + dp->df_count[idx_new] = (linenr_T)hunk->count_new - off; + } else { + // second overlap of new block with existing block + dp->df_count[idx_new] += (linenr_T)hunk->count_new - (linenr_T)hunk->count_orig + + dpl->df_lnum[idx_orig] + + dpl->df_count[idx_orig] + - (dp->df_lnum[idx_orig] + + dp->df_count[idx_orig]); + } + + // Adjust the size of the block to include all the lines to the + // end of the existing block or the new diff, whatever ends last. + off = (hunk->lnum_orig + (linenr_T)hunk->count_orig) + - (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]); + + if (off < 0) { + // new change ends in existing block, adjust the end if not + // done already + if (*notsetp) { + dp->df_count[idx_new] += -off; + } + off = 0; + } + + for (int i = idx_orig; i < idx_new; i++) { + if (curtab->tp_diffbuf[i] != NULL) { + dp->df_count[i] = dpl->df_lnum[i] + dpl->df_count[i] + - dp->df_lnum[i] + off; + } + } + + // Delete the diff blocks that have been merged into one. + diff_T *dn = dp->df_next; + dp->df_next = dpl->df_next; + + while (dn != dp->df_next) { + dpl = dn->df_next; + xfree(dn); + dn = dpl; + } + } else { + // Allocate a new diffblock. + dp = diff_alloc_new(curtab, dprev, dp); + + dp->df_lnum[idx_orig] = hunk->lnum_orig; + dp->df_count[idx_orig] = (linenr_T)hunk->count_orig; + dp->df_lnum[idx_new] = hunk->lnum_new; + dp->df_count[idx_new] = (linenr_T)hunk->count_new; + + // Set values for other buffers, these must be equal to the + // original buffer, otherwise there would have been a change + // already. + for (int i = idx_orig + 1; i < idx_new; i++) { + if (curtab->tp_diffbuf[i] != NULL) { + diff_copy_entry(dprev, dp, idx_orig, i); + } + } + } + *notsetp = false; // "*dp" has been set + *dpp = dp; + *dprevp = dprev; +} + /// Read the diff output and add each entry to the diff list. /// /// @param idx_orig idx of original file @@ -1610,7 +1718,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) diff_T *dprev = NULL; diff_T *dp = curtab->tp_first_diff; diffout_T *dout = &dio->dio_diff; - int notset = true; // block "*dp" not set yet + bool notset = true; // block "*dp" not set yet diffstyle_T diffstyle = DIFF_NONE; if (dio->dio_internal) { @@ -1633,104 +1741,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) break; } - // Go over blocks before the change, for which orig and new are equal. - // Copy blocks from orig to new. - while (dp != NULL - && hunk.lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) { - if (notset) { - diff_copy_entry(dprev, dp, idx_orig, idx_new); - } - dprev = dp; - dp = dp->df_next; - notset = true; - } - - if ((dp != NULL) - && (hunk.lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) - && (hunk.lnum_orig + hunk.count_orig >= dp->df_lnum[idx_orig])) { - // New block overlaps with existing block(s). - // First find last block that overlaps. - diff_T *dpl; - for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next) { - if (hunk.lnum_orig + hunk.count_orig < dpl->df_next->df_lnum[idx_orig]) { - break; - } - } - - // If the newly found block starts before the old one, set the - // start back a number of lines. - linenr_T off = dp->df_lnum[idx_orig] - hunk.lnum_orig; - - if (off > 0) { - for (int i = idx_orig; i < idx_new; i++) { - if (curtab->tp_diffbuf[i] != NULL) { - dp->df_lnum[i] -= off; - } - } - dp->df_lnum[idx_new] = hunk.lnum_new; - dp->df_count[idx_new] = (linenr_T)hunk.count_new; - } else if (notset) { - // new block inside existing one, adjust new block - dp->df_lnum[idx_new] = hunk.lnum_new + off; - dp->df_count[idx_new] = (linenr_T)hunk.count_new - off; - } else { - // second overlap of new block with existing block - dp->df_count[idx_new] += (linenr_T)hunk.count_new - (linenr_T)hunk.count_orig - + dpl->df_lnum[idx_orig] + - dpl->df_count[idx_orig] - - (dp->df_lnum[idx_orig] + - dp->df_count[idx_orig]); - } - - // Adjust the size of the block to include all the lines to the - // end of the existing block or the new diff, whatever ends last. - off = (hunk.lnum_orig + (linenr_T)hunk.count_orig) - - (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]); - - if (off < 0) { - // new change ends in existing block, adjust the end if not - // done already - if (notset) { - dp->df_count[idx_new] += -off; - } - off = 0; - } - - for (int i = idx_orig; i < idx_new; i++) { - if (curtab->tp_diffbuf[i] != NULL) { - dp->df_count[i] = dpl->df_lnum[i] + dpl->df_count[i] - - dp->df_lnum[i] + off; - } - } - - // Delete the diff blocks that have been merged into one. - diff_T *dn = dp->df_next; - dp->df_next = dpl->df_next; - - while (dn != dp->df_next) { - dpl = dn->df_next; - xfree(dn); - dn = dpl; - } - } else { - // Allocate a new diffblock. - dp = diff_alloc_new(curtab, dprev, dp); - - dp->df_lnum[idx_orig] = hunk.lnum_orig; - dp->df_count[idx_orig] = (linenr_T)hunk.count_orig; - dp->df_lnum[idx_new] = hunk.lnum_new; - dp->df_count[idx_new] = (linenr_T)hunk.count_new; - - // Set values for other buffers, these must be equal to the - // original buffer, otherwise there would have been a change - // already. - for (int i = idx_orig + 1; i < idx_new; i++) { - if (curtab->tp_diffbuf[i] != NULL) { - diff_copy_entry(dprev, dp, idx_orig, i); - } - } - } - notset = false; // "*dp" has been set + process_hunk(&dp, &dprev, idx_orig, idx_new, &hunk, ¬set); } // for remaining diff blocks orig and new are equal -- cgit From b2011bae3eecd5da1dbc0a228cccaa37ead029a9 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 24 Oct 2022 14:21:37 +0100 Subject: refactor(diff.c): simplify diff_buf_idx() --- src/nvim/diff.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 5069d30e46..a12dfdd9b2 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -211,13 +211,7 @@ static void diff_buf_clear(void) /// @return Its index or DB_COUNT if not found. static int diff_buf_idx(buf_T *buf) { - int idx; - for (idx = 0; idx < DB_COUNT; idx++) { - if (curtab->tp_diffbuf[idx] == buf) { - break; - } - } - return idx; + return diff_buf_idx_tp(buf, curtab); } /// Find buffer "buf" in the list of diff buffers for tab page "tp". -- cgit From bf60b9134b7a2348dc080319e597ad4a1aaa859a Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 24 Oct 2022 14:34:19 +0100 Subject: refactor(diff.c): internal does not need diffstyle --- src/nvim/diff.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index a12dfdd9b2..87ead61c36 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -1715,9 +1715,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) bool notset = true; // block "*dp" not set yet diffstyle_T diffstyle = DIFF_NONE; - if (dio->dio_internal) { - diffstyle = DIFF_UNIFIED; - } else { + if (!dio->dio_internal) { fd = os_fopen(dout->dout_fname, "r"); if (fd == NULL) { emsg(_("E98: Cannot read diff output")); -- cgit From a53998ae78902309c225b323e0b8d9f1f75fe147 Mon Sep 17 00:00:00 2001 From: shadmansaleh <13149513+shadmansaleh@users.noreply.github.com> Date: Sat, 22 Oct 2022 13:54:29 +0600 Subject: fix: setting tabline option not redrawing tabline With #20374 tabline option is marked with 'statuslines' redraw flag. But 'statuslines' doesn't redraw tabline. As a result, tabline doesn't get redrawn when tabline option is set and statuslines get unnecessarily redrawn. This patch fixes the issue by adding a new redraw flag P_RTABL to redraw tabline. --- src/nvim/generators/gen_options.lua | 1 + src/nvim/option.c | 4 ++++ src/nvim/option_defs.h | 1 + src/nvim/options.lua | 4 ++-- 4 files changed, 8 insertions(+), 2 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua index 6dba6552b3..4e4dd83367 100644 --- a/src/nvim/generators/gen_options.lua +++ b/src/nvim/generators/gen_options.lua @@ -30,6 +30,7 @@ local type_flags={ local redraw_flags={ statuslines='P_RSTAT', + tabline = 'P_RTABL', current_window='P_RWIN', current_window_only='P_RWINONLY', current_buffer='P_RBUF', diff --git a/src/nvim/option.c b/src/nvim/option.c index 1a6cd0c1af..06662afd08 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2648,6 +2648,10 @@ void check_redraw(uint32_t flags) status_redraw_all(); } + if ((flags & P_RTABL) || all) { // mark tablines dirty + redraw_tabline = true; + } + if ((flags & P_RBUF) || (flags & P_RWIN) || all) { changed_window_setting(); } diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index c4333a6f61..19e4780e0a 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -24,6 +24,7 @@ #define P_NO_MKRC 0x200U ///< don't include in :mkvimrc output // when option changed, what to display: +#define P_RTABL 0x800U ///< redraw tabline #define P_RSTAT 0x1000U ///< redraw status lines #define P_RWIN 0x2000U ///< redraw current window and recompute text #define P_RBUF 0x4000U ///< redraw current buffer and recompute text diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 8a883a09c3..ba483d3714 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -19,7 +19,7 @@ -- types: bool, number, string -- lists: (nil), comma, onecomma, flags, flagscomma -- scopes: global, buffer, window --- redraw options: statuslines, current_window, curent_window_only, +-- redraw options: statuslines, tabline, current_window, curent_window_only, -- current_buffer, all_windows, curswant -- defaults: {condition=#if condition, if_true=default, if_false=default} -- #if condition: @@ -2407,7 +2407,7 @@ return { short_desc=N_("custom format for the console tab pages line"), type='string', scope={'global'}, modelineexpr=true, - redraw={'statuslines'}, + redraw={'tabline'}, varname='p_tal', defaults={if_true=""} }, -- cgit From 49c2da432bc4bef37903b0eda2ec8d26bdcb9c8b Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 24 Oct 2022 16:45:26 +0100 Subject: refactor(diff.c): factor out diffblock deletion --- src/nvim/diff.c | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 87ead61c36..1c68bad9a8 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -462,9 +462,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T dprev->df_count[i] += dp->df_count[i]; } } - dprev->df_next = dp->df_next; - xfree(dp); - dp = dprev->df_next; + dp = diff_free(tp, dprev, dp); } else { // Advance to next entry. dprev = dp; @@ -485,15 +483,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T } if (i == DB_COUNT) { - diff_T *dnext = dp->df_next; - xfree(dp); - dp = dnext; - - if (dprev == NULL) { - tp->tp_first_diff = dnext; - } else { - dprev->df_next = dnext; - } + dp = diff_free(tp, dprev, dp); } else { // Advance to next entry. dprev = dp; @@ -533,6 +523,20 @@ static diff_T *diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp) return dnew; } +static diff_T *diff_free(tabpage_T *tp, diff_T *dprev, diff_T *dp) +{ + diff_T *ret = dp->df_next; + xfree(dp); + + if (dprev == NULL) { + tp->tp_first_diff = ret; + } else { + dprev->df_next = ret; + } + + return ret; +} + /// Check if the diff block "dp" can be made smaller for lines at the start and /// end that are equal. Called after inserting lines. /// @@ -2777,13 +2781,6 @@ static void diffgetput(const int addr_count, const int idx_cur, const int idx_fr if (i == DB_COUNT) { // delete the diff entry, the buffers are now equal here dfree = dp; - dp = dp->df_next; - - if (dprev == NULL) { - curtab->tp_first_diff = dp; - } else { - dprev->df_next = dp; - } } } @@ -2802,10 +2799,10 @@ static void diffgetput(const int addr_count, const int idx_cur, const int idx_fr } changed_lines(lnum, 0, lnum + count, added, true); - if (dfree != NULL) { + if (dfree == dp) { // Diff is deleted, update folds in other windows. diff_fold_update(dfree, idx_to); - xfree(dfree); + dp = diff_free(curtab, dprev, dp); } // mark_adjust() may have made "dp" invalid. We don't know where -- cgit From 395a2712539906705a2f93924ad96185973d0e9e Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 24 Oct 2022 16:19:19 +0100 Subject: refactor(diff.c): copy lines via memmove --- src/nvim/diff.c | 49 ++++++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 27 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 1c68bad9a8..f458f0940d 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -727,15 +727,15 @@ static void clear_diffout(diffout_T *dout) /// @param din /// /// @return FAIL for failure. -static int diff_write_buffer(buf_T *buf, diffin_T *din) +static int diff_write_buffer(buf_T *buf, mmfile_t *m) { - long len = 0; + size_t len = 0; // xdiff requires one big block of memory with all the text. for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { - len += (long)strlen(ml_get_buf(buf, lnum, false)) + 1; + len += strlen(ml_get_buf(buf, lnum, false)) + 1; } - char *ptr = try_malloc((size_t)len); + char *ptr = try_malloc(len); if (ptr == NULL) { // Allocating memory failed. This can happen, because we try to read // the whole buffer text into memory. Set the failed flag, the diff @@ -749,37 +749,32 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din) } return FAIL; } - din->din_mmfile.ptr = ptr; - din->din_mmfile.size = len; + m->ptr = ptr; + m->size = (long)len; len = 0; for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { - for (char *s = ml_get_buf(buf, lnum, false); *s != NUL;) { - if (diff_flags & DIFF_ICASE) { - int c; + char *s = ml_get_buf(buf, lnum, false); + if (diff_flags & DIFF_ICASE) { + while (*s != NUL) { char cbuf[MB_MAXBYTES + 1]; - if (*s == NL) { - c = NUL; - } else { - // xdiff doesn't support ignoring case, fold-case the text. - c = utf_ptr2char(s); - c = utf_fold(c); - } + // xdiff doesn't support ignoring case, fold-case the text. + int c = *s == NL ? NUL : utf_fold(utf_ptr2char(s)); const int orig_len = utfc_ptr2len(s); - if (utf_char2bytes(c, cbuf) != orig_len) { - // TODO(Bram): handle byte length difference - memmove(ptr + len, s, (size_t)orig_len); - } else { - memmove(ptr + len, cbuf, (size_t)orig_len); - } + // TODO(Bram): handle byte length difference + char *s1 = (utf_char2bytes(c, cbuf) != orig_len) ? s : cbuf; + memmove(ptr + len, s1, (size_t)orig_len); s += orig_len; - len += orig_len; - } else { - ptr[len++] = *s == NL ? NUL : *s; - s++; + len += (size_t)orig_len; } + } else { + size_t slen = strlen(s); + memmove(ptr + len, s, slen); + // NUL is represented as NL; convert + memchrsub(ptr + len, NL, NUL, slen); + len += slen; } ptr[len++] = NL; } @@ -797,7 +792,7 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din) static int diff_write(buf_T *buf, diffin_T *din) { if (din->din_fname == NULL) { - return diff_write_buffer(buf, din); + return diff_write_buffer(buf, &din->din_mmfile); } // Always use 'fileformat' set to "unix". -- cgit From 3c5b43580163340209aa13c731e5a34ff3c5deb4 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 26 Oct 2022 08:42:28 +0800 Subject: vim-patch:8.2.0179: still a few places where range() does not work Problem: Still a few places where range() does not work. Solution: Fix using range() causing problems. https://github.com/vim/vim/commit/b09920203a0f2b202497ef9632f8447f73d0f1fb Code is mostly N/A. Cherry-pick all of Test_range() from patch 8.2.0159. - Use nvim_input() instead of test_feedinput() - Assert a different result from json_encode() - Assert a different error for sign_undefine() Co-authored-by: Bram Moolenaar --- src/nvim/tag.c | 2 + src/nvim/testdir/test_functions.vim | 262 ++++++++++++++++++++++++++++++++++++ 2 files changed, 264 insertions(+) (limited to 'src/nvim') diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 8c0c2e8f57..90b21320d2 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -3289,10 +3289,12 @@ int set_tagstack(win_T *wp, const dict_T *d, int action) if ((di = tv_dict_find(d, "curidx", -1)) != NULL) { tagstack_set_curidx(wp, (int)tv_get_number(&di->di_tv) - 1); } + if (action == 't') { // truncate the stack taggy_T *const tagstack = wp->w_tagstack; const int tagstackidx = wp->w_tagstackidx; int tagstacklen = wp->w_tagstacklen; + // delete all the tag stack entries above the current entry while (tagstackidx < tagstacklen) { tagstack_clear_entry(&tagstack[--tagstacklen]); diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 7ad0cb5884..b9446a7a87 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1915,6 +1915,268 @@ func Test_bufadd_bufload() call delete('XotherName') endfunc +func Test_range() + " destructuring + let [x, y] = range(2) + call assert_equal([0, 1], [x, y]) + + " index + call assert_equal(4, range(1, 10)[3]) + + " add() + call assert_equal([0, 1, 2, 3], add(range(3), 3)) + call assert_equal([0, 1, 2, [0, 1, 2]], add([0, 1, 2], range(3))) + call assert_equal([0, 1, 2, [0, 1, 2]], add(range(3), range(3))) + + " append() + new + call append('.', range(5)) + call assert_equal(['', '0', '1', '2', '3', '4'], getline(1, '$')) + bwipe! + + " appendbufline() + new + call appendbufline(bufnr(''), '.', range(5)) + call assert_equal(['0', '1', '2', '3', '4', ''], getline(1, '$')) + bwipe! + + " call() + func TwoArgs(a, b) + return [a:a, a:b] + endfunc + call assert_equal([0, 1], call('TwoArgs', range(2))) + + " col() + new + call setline(1, ['foo', 'bar']) + call assert_equal(2, col(range(1, 2))) + bwipe! + + " complete() + execute "normal! a\=[complete(col('.'), range(10)), ''][1]\" + " complete_info() + execute "normal! a\=[complete(col('.'), range(10)), ''][1]\\=[complete_info(range(5)), ''][1]\" + + " copy() + call assert_equal([1, 2, 3], copy(range(1, 3))) + + " count() + call assert_equal(0, count(range(0), 3)) + call assert_equal(0, count(range(2), 3)) + call assert_equal(1, count(range(5), 3)) + + " cursor() + new + call setline(1, ['aaa', 'bbb', 'ccc']) + call cursor(range(1, 2)) + call assert_equal([2, 1], [col('.'), line('.')]) + bwipe! + + " deepcopy() + call assert_equal([1, 2, 3], deepcopy(range(1, 3))) + + " empty() + call assert_true(empty(range(0))) + call assert_false(empty(range(2))) + + " execute() + new + call setline(1, ['aaa', 'bbb', 'ccc']) + call execute(range(3)) + call assert_equal(2, line('.')) + bwipe! + + " extend() + call assert_equal([1, 2, 3, 4], extend([1], range(2, 4))) + call assert_equal([1, 2, 3, 4], extend(range(1, 1), range(2, 4))) + call assert_equal([1, 2, 3, 4], extend(range(1, 1), [2, 3, 4])) + + " filter() + call assert_equal([1, 3], filter(range(5), 'v:val % 2')) + + " funcref() + call assert_equal([0, 1], funcref('TwoArgs', range(2))()) + + " function() + call assert_equal([0, 1], function('TwoArgs', range(2))()) + + " garbagecollect() + let thelist = [1, range(2), 3] + let otherlist = range(3) + call test_garbagecollect_now() + + " get() + call assert_equal(4, get(range(1, 10), 3)) + call assert_equal(-1, get(range(1, 10), 42, -1)) + + " index() + call assert_equal(1, index(range(1, 5), 2)) + + " inputlist() + " call test_feedinput("1\") + call nvim_input('1') + call assert_equal(1, inputlist(range(10))) + " call test_feedinput("1\") + call nvim_input('1') + call assert_equal(1, inputlist(range(3, 10))) + + " call assert_equal('[0,1,2,3]', json_encode(range(4))) + call assert_equal('[0, 1, 2, 3]', json_encode(range(4))) + + " insert() + call assert_equal([42, 1, 2, 3, 4, 5], insert(range(1, 5), 42)) + call assert_equal([42, 1, 2, 3, 4, 5], insert(range(1, 5), 42, 0)) + call assert_equal([1, 42, 2, 3, 4, 5], insert(range(1, 5), 42, 1)) + call assert_equal([1, 2, 3, 4, 42, 5], insert(range(1, 5), 42, 4)) + call assert_equal([1, 2, 3, 4, 42, 5], insert(range(1, 5), 42, -1)) + call assert_equal([1, 2, 3, 4, 5, 42], insert(range(1, 5), 42, 5)) + + " join() + call assert_equal('0 1 2 3 4', join(range(5))) + + " len() + call assert_equal(0, len(range(0))) + call assert_equal(2, len(range(2))) + call assert_equal(5, len(range(0, 12, 3))) + call assert_equal(4, len(range(3, 0, -1))) + + " list2str() + call assert_equal('ABC', list2str(range(65, 67))) + + " lock() + let thelist = range(5) + lockvar thelist + + " map() + call assert_equal([0, 2, 4, 6, 8], map(range(5), 'v:val * 2')) + + " match() + call assert_equal(3, match(range(5), 3)) + + " matchaddpos() + highlight MyGreenGroup ctermbg=green guibg=green + call matchaddpos('MyGreenGroup', range(line('.'), line('.'))) + + " matchend() + call assert_equal(4, matchend(range(5), '4')) + call assert_equal(3, matchend(range(1, 5), '4')) + call assert_equal(-1, matchend(range(1, 5), '42')) + + " matchstrpos() + call assert_equal(['4', 4, 0, 1], matchstrpos(range(5), '4')) + call assert_equal(['4', 3, 0, 1], matchstrpos(range(1, 5), '4')) + call assert_equal(['', -1, -1, -1], matchstrpos(range(1, 5), '42')) + + " max() reverse() + call assert_equal(0, max(range(0))) + call assert_equal(0, max(range(10, 9))) + call assert_equal(9, max(range(10))) + call assert_equal(18, max(range(0, 20, 3))) + call assert_equal(20, max(range(20, 0, -3))) + call assert_equal(99999, max(range(100000))) + call assert_equal(99999, max(range(99999, 0, -1))) + call assert_equal(99999, max(reverse(range(100000)))) + call assert_equal(99999, max(reverse(range(99999, 0, -1)))) + + " min() reverse() + call assert_equal(0, min(range(0))) + call assert_equal(0, min(range(10, 9))) + call assert_equal(5, min(range(5, 10))) + call assert_equal(5, min(range(5, 10, 3))) + call assert_equal(2, min(range(20, 0, -3))) + call assert_equal(0, min(range(100000))) + call assert_equal(0, min(range(99999, 0, -1))) + call assert_equal(0, min(reverse(range(100000)))) + call assert_equal(0, min(reverse(range(99999, 0, -1)))) + + " remove() + call assert_equal(1, remove(range(1, 10), 0)) + call assert_equal(2, remove(range(1, 10), 1)) + call assert_equal(9, remove(range(1, 10), 8)) + call assert_equal(10, remove(range(1, 10), 9)) + call assert_equal(10, remove(range(1, 10), -1)) + call assert_equal([3, 4, 5], remove(range(1, 10), 2, 4)) + + " repeat() + call assert_equal([0, 1, 2, 0, 1, 2], repeat(range(3), 2)) + call assert_equal([0, 1, 2], repeat(range(3), 1)) + call assert_equal([], repeat(range(3), 0)) + call assert_equal([], repeat(range(5, 4), 2)) + call assert_equal([], repeat(range(5, 4), 0)) + + " reverse() + call assert_equal([2, 1, 0], reverse(range(3))) + call assert_equal([0, 1, 2, 3], reverse(range(3, 0, -1))) + call assert_equal([9, 8, 7, 6, 5, 4, 3, 2, 1, 0], reverse(range(10))) + call assert_equal([20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10], reverse(range(10, 20))) + call assert_equal([16, 13, 10], reverse(range(10, 18, 3))) + call assert_equal([19, 16, 13, 10], reverse(range(10, 19, 3))) + call assert_equal([19, 16, 13, 10], reverse(range(10, 20, 3))) + call assert_equal([11, 14, 17, 20], reverse(range(20, 10, -3))) + call assert_equal([], reverse(range(0))) + + " TODO: setpos() + " new + " call setline(1, repeat([''], bufnr(''))) + " call setline(bufnr('') + 1, repeat('x', bufnr('') * 2 + 6)) + " call setpos('x', range(bufnr(''), bufnr('') + 3)) + " bwipe! + + " setreg() + call setreg('a', range(3)) + call assert_equal("0\n1\n2\n", getreg('a')) + + " settagstack() + call settagstack(1, #{items : range(4)}) + + " sign_define() + call assert_fails("call sign_define(range(5))", "E715:") + call assert_fails("call sign_placelist(range(5))", "E715:") + + " sign_undefine() + " call assert_fails("call sign_undefine(range(5))", "E908:") + call assert_fails("call sign_undefine(range(5))", "E155:") + + " sign_unplacelist() + call assert_fails("call sign_unplacelist(range(5))", "E715:") + + " sort() + call assert_equal([0, 1, 2, 3, 4, 5], sort(range(5, 0, -1))) + + " 'spellsuggest' + func MySuggest() + return range(3) + endfunc + set spell spellsuggest=expr:MySuggest() + call assert_equal([], spellsuggest('baord', 3)) + set nospell spellsuggest& + + " string() + call assert_equal('[0, 1, 2, 3, 4]', string(range(5))) + + " taglist() with 'tagfunc' + func TagFunc(pattern, flags, info) + return range(10) + endfunc + set tagfunc=TagFunc + call assert_fails("call taglist('asdf')", 'E987:') + set tagfunc= + + " term_start() + if has('terminal') + call assert_fails('call term_start(range(3, 4))', 'E474:') + let g:terminal_ansi_colors = range(16) + call assert_fails('call term_start("ls", #{term_finish: "close"})', 'E475:') + unlet g:terminal_ansi_colors + endif + + " type() + call assert_equal(v:t_list, type(range(5))) + + " uniq() + call assert_equal([0, 1, 2, 3, 4], uniq(range(5))) +endfunc + " Test for the eval() function func Test_eval() call assert_fails("call eval('5 a')", 'E488:') -- cgit From 0aaef07224eb4243c944765cfc6692b08cbe8750 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 26 Oct 2022 08:57:59 +0800 Subject: vim-patch:8.2.0211: test for ANSI colors fails without an "ls" command Problem: Test for ANSI colors fails without an "ls" command. Solution: Use "dir". (Ken Takata, closes vim/vim#5582) https://github.com/vim/vim/commit/94255df057afa0b7dde77612f3274d4440871bd1 Cherry-pick test_functions.vim change from patch 8.2.0186. --- src/nvim/testdir/test_functions.vim | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index b9446a7a87..871aab7ff1 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -2128,7 +2128,7 @@ func Test_range() " settagstack() call settagstack(1, #{items : range(4)}) - + " sign_define() call assert_fails("call sign_define(range(5))", "E715:") call assert_fails("call sign_placelist(range(5))", "E715:") @@ -2161,12 +2161,17 @@ func Test_range() set tagfunc=TagFunc call assert_fails("call taglist('asdf')", 'E987:') set tagfunc= - + " term_start() - if has('terminal') + if has('terminal') && has('termguicolors') call assert_fails('call term_start(range(3, 4))', 'E474:') let g:terminal_ansi_colors = range(16) - call assert_fails('call term_start("ls", #{term_finish: "close"})', 'E475:') + if has('win32') + let cmd = "cmd /c dir" + else + let cmd = "ls" + endif + call assert_fails('call term_start("' .. cmd .. '", #{term_finish: "close"})', 'E475:') unlet g:terminal_ansi_colors endif -- cgit From 0fb08f353948016c8ad6e4a13a9a128208f4dcc5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 26 Oct 2022 13:38:51 +0800 Subject: vim-patch:7.4.1081 Problem: No test for what previously caused a crash. Solution: Add test for unletting errmsg. https://github.com/vim/vim/commit/254b105b755d9736ece5f7f28db92acaf3e7bf76 Use v:errmsg instead of errmsg. Co-authored-by: Bram Moolenaar --- src/nvim/testdir/test_unlet.vim | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_unlet.vim b/src/nvim/testdir/test_unlet.vim index b02bdaab3b..db064b5e9b 100644 --- a/src/nvim/testdir/test_unlet.vim +++ b/src/nvim/testdir/test_unlet.vim @@ -7,6 +7,12 @@ func Test_read_only() catch call assert_true(v:exception =~ ':E795:') endtry + try + " this caused a crash + unlet v:errmsg + catch + call assert_true(v:exception =~ ':E795:') + endtry endfunc func Test_existing() -- cgit From b4c250d37b7b53312b734f1dae6d9aed3942c563 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 26 Oct 2022 13:40:04 +0800 Subject: vim-patch:7.4.1097 Problem: Looking up the alloc ID for tests fails. Solution: Fix the line computation. Use assert_fails() for unlet test. https://github.com/vim/vim/commit/065ee9aebf9abe08ae8c0dba7d05cbdcc423c8e0 Use v:count and v:errmsg instead of count and errmsg. Co-authored-by: Bram Moolenaar --- src/nvim/testdir/test_unlet.vim | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_unlet.vim b/src/nvim/testdir/test_unlet.vim index db064b5e9b..873cd066a9 100644 --- a/src/nvim/testdir/test_unlet.vim +++ b/src/nvim/testdir/test_unlet.vim @@ -1,18 +1,9 @@ " Tests for :unlet func Test_read_only() - try - " this caused a crash - unlet v:count - catch - call assert_true(v:exception =~ ':E795:') - endtry - try - " this caused a crash - unlet v:errmsg - catch - call assert_true(v:exception =~ ':E795:') - endtry + " these caused a crash + call assert_fails('unlet v:count', 'E795:') + call assert_fails('unlet v:errmsg', 'E795:') endfunc func Test_existing() @@ -24,11 +15,7 @@ endfunc func Test_not_existing() unlet! does_not_exist - try - unlet does_not_exist - catch - call assert_true(v:exception =~ ':E108:') - endtry + call assert_fails('unlet does_not_exist', 'E108:') endfunc func Test_unlet_fails() -- cgit From d40739843cf9a160587032bf381c440079e5a8ce Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 26 Oct 2022 13:05:33 +0800 Subject: vim-patch:8.2.0401: not enough test coverage for evalvars.c Problem: Not enough test coverage for evalvars.c. Solution: Add more tests. (Yegappan Lakshmanan, closes vim/vim#5804) https://github.com/vim/vim/commit/8dfcce3a78ccb520cc9d09081f998091494c50bf Assert E475 instead of E474 in :redir test because a later patch changed the error number. Comment out the test for :echo with a deeply nested container as Nvim implements :echo very differently. --- src/nvim/testdir/test_cmdline.vim | 42 ++++++++++++++++-- src/nvim/testdir/test_const.vim | 10 +++++ src/nvim/testdir/test_diffmode.vim | 27 ++++++++++++ src/nvim/testdir/test_excmd.vim | 9 ++++ src/nvim/testdir/test_functions.vim | 16 +++---- src/nvim/testdir/test_let.vim | 88 +++++++++++++++++++++++++++++++++++++ src/nvim/testdir/test_listdict.vim | 54 +++++++++++++++++++++++ src/nvim/testdir/test_spell.vim | 29 ++++++++++++ src/nvim/testdir/test_unlet.vim | 3 ++ src/nvim/testdir/test_user_func.vim | 3 ++ src/nvim/testdir/test_vimscript.vim | 3 ++ 11 files changed, 272 insertions(+), 12 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index f37e8be59a..c8b8c88d75 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -1077,6 +1077,40 @@ func Test_cmdline_complete_various() " completion for the expression register call feedkeys(":\"\=float2\t\"\\"\", 'xt') call assert_equal('"float2nr("', @=) + + " completion for :language command with an invalid argument + call feedkeys(":language dummy \t\\"\", 'xt') + call assert_equal("\"language dummy \t", @:) + + " completion for commands after a :global command + call feedkeys(":g/a\\xb/call float2\t\\"\", 'xt') + call assert_equal('"g/a\xb/call float2nr(', @:) + + " completion with ambiguous user defined commands + com TCmd1 echo 'TCmd1' + com TCmd2 echo 'TCmd2' + call feedkeys(":TCmd \t\\"\", 'xt') + call assert_equal('"TCmd ', @:) + delcom TCmd1 + delcom TCmd2 + + " completion after a range followed by a pipe (|) character + call feedkeys(":1,10 | chist\t\\"\", 'xt') + call assert_equal('"1,10 | chistory', @:) + + " completion for window local variables + let w:wvar1 = 10 + let w:wvar2 = 10 + call feedkeys(":echo w:wvar\\\"\", 'xt') + call assert_equal('"echo w:wvar1 w:wvar2', @:) + unlet w:wvar1 w:wvar2 + + " completion for tab local variables + let t:tvar1 = 10 + let t:tvar2 = 10 + call feedkeys(":echo t:tvar\\\"\", 'xt') + call assert_equal('"echo t:tvar1 t:tvar2', @:) + unlet t:tvar1 t:tvar2 endfunc func Test_cmdline_write_alternatefile() @@ -1682,16 +1716,16 @@ func Test_wildmode() " Test for wildmode=longest with 'fileignorecase' set set wildmode=longest set fileignorecase - argadd AA AAA AAAA - call feedkeys(":buffer \t\\"\", 'xt') - call assert_equal('"buffer AA', @:) + argadd AAA AAAA AAAAA + call feedkeys(":buffer a\t\\"\", 'xt') + call assert_equal('"buffer AAA', @:) set fileignorecase& " Test for listing files with wildmode=list set wildmode=list let g:Sline = '' call feedkeys(":b A\t\t\\\"\", 'xt') - call assert_equal('AA AAA AAAA', g:Sline) + call assert_equal('AAA AAAA AAAAA', g:Sline) call assert_equal('"b A', @:) %argdelete diff --git a/src/nvim/testdir/test_const.vim b/src/nvim/testdir/test_const.vim index 0d064617a5..7f19085b16 100644 --- a/src/nvim/testdir/test_const.vim +++ b/src/nvim/testdir/test_const.vim @@ -231,6 +231,14 @@ func Test_const_with_special_variables() call assert_fails('const &filetype = "vim"', 'E996:') call assert_fails('const &l:filetype = "vim"', 'E996:') call assert_fails('const &g:encoding = "utf-8"', 'E996:') + + call assert_fails('const [a, $CONST_FOO] = [369, "abc"]', 'E996:') + call assert_equal(369, a) + call assert_equal(v:null, getenv("CONST_FOO")) + + call assert_fails('const [b; $CONST_FOO] = [246, 2, "abc"]', 'E996:') + call assert_equal(246, b) + call assert_equal(v:null, getenv("CONST_FOO")) endfunc func Test_const_with_eval_name() @@ -274,3 +282,5 @@ func Test_lock_depth_is_2() const d2 = #{a: 0, b: lvar, c: 4} let d2.b[1] = 'd' endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index 1cb71664bd..831efdbfc2 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -1198,6 +1198,33 @@ func Test_diff_maintains_change_mark() delfunc DiffMaintainsChangeMark endfunc +" Test for 'patchexpr' +func Test_patchexpr() + let g:patch_args = [] + func TPatch() + call add(g:patch_args, readfile(v:fname_in)) + call add(g:patch_args, readfile(v:fname_diff)) + call writefile(['output file'], v:fname_out) + endfunc + set patchexpr=TPatch() + + call writefile(['input file'], 'Xinput') + call writefile(['diff file'], 'Xdiff') + %bwipe! + edit Xinput + diffpatch Xdiff + call assert_equal('output file', getline(1)) + call assert_equal('Xinput.new', bufname()) + call assert_equal(2, winnr('$')) + call assert_true(&diff) + + call delete('Xinput') + call delete('Xdiff') + set patchexpr& + delfunc TPatch + %bwipe! +endfunc + func Test_diff_rnu() CheckScreendump diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim index 7692d4fc55..acf23fbc3c 100644 --- a/src/nvim/testdir/test_excmd.vim +++ b/src/nvim/testdir/test_excmd.vim @@ -479,12 +479,21 @@ endfunc func Test_redir_cmd() call assert_fails('redir @@', 'E475:') call assert_fails('redir abc', 'E475:') + call assert_fails('redir => 1abc', 'E474:') + call assert_fails('redir => a b', 'E488:') + call assert_fails('redir => abc[1]', 'E475:') + let b=0zFF + call assert_fails('redir =>> b', 'E734:') + unlet b + if has('unix') + " Redirecting to a directory name call mkdir('Xdir') call assert_fails('redir > Xdir', 'E17:') call delete('Xdir', 'd') endif if !has('bsd') + " Redirecting to a read-only file call writefile([], 'Xfile') call setfperm('Xfile', 'r--r--r--') call assert_fails('redir! > Xfile', 'E190:') diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 871aab7ff1..27aab6330f 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -804,6 +804,10 @@ func Test_getbufvar() call assert_equal(0, getbufvar(bnr, '&autoindent')) call assert_equal(0, getbufvar(bnr, '&autoindent', 1)) + " Set and get a buffer-local variable + call setbufvar(bnr, 'bufvar_test', ['one', 'two']) + call assert_equal(['one', 'two'], getbufvar(bnr, 'bufvar_test')) + " Open new window with forced option values set fileformats=unix,dos new ++ff=dos ++bin ++enc=iso-8859-2 @@ -1633,6 +1637,10 @@ func Test_func_sandbox() call assert_fails('call Fsandbox()', 'E48:') delfunc Fsandbox + + " From a sandbox try to set a predefined variable (which cannot be modified + " from a sandbox) + call assert_fails('sandbox let v:lnum = 10', 'E794:') endfunc func EditAnotherFile() @@ -2143,14 +2151,6 @@ func Test_range() " sort() call assert_equal([0, 1, 2, 3, 4, 5], sort(range(5, 0, -1))) - " 'spellsuggest' - func MySuggest() - return range(3) - endfunc - set spell spellsuggest=expr:MySuggest() - call assert_equal([], spellsuggest('baord', 3)) - set nospell spellsuggest& - " string() call assert_equal('[0, 1, 2, 3, 4]', string(range(5))) diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim index 6cb736a38a..009735e004 100644 --- a/src/nvim/testdir/test_let.vim +++ b/src/nvim/testdir/test_let.vim @@ -25,9 +25,62 @@ func Test_let() let s = "\na #1\nb #2" call assert_equal(s, out) + " Test for displaying a string variable + let s = 'vim' + let out = execute('let s') + let s = "\ns vim" + call assert_equal(s, out) + + " Test for displaying a list variable + let l = [1, 2] + let out = execute('let l') + let s = "\nl [1, 2]" + call assert_equal(s, out) + + " Test for displaying a dict variable + let d = {'k' : 'v'} + let out = execute('let d') + let s = "\nd {'k': 'v'}" + call assert_equal(s, out) + + " Test for displaying a function reference variable + let F = function('min') + let out = execute('let F') + let s = "\nF *min()" + call assert_equal(s, out) + let x = 0 if 0 | let x = 1 | endif call assert_equal(0, x) + + " Display a list item using an out of range index + let l = [10] + call assert_fails('let l[1]', 'E684:') + + " List special variable dictionaries + let g:Test_Global_Var = 5 + call assert_match("\nTest_Global_Var #5", execute('let g:')) + unlet g:Test_Global_Var + + let b:Test_Buf_Var = 8 + call assert_match("\nb:Test_Buf_Var #8", execute('let b:')) + unlet b:Test_Buf_Var + + let w:Test_Win_Var = 'foo' + call assert_equal("\nw:Test_Win_Var foo", execute('let w:')) + unlet w:Test_Win_Var + + let t:Test_Tab_Var = 'bar' + call assert_equal("\nt:Test_Tab_Var bar", execute('let t:')) + unlet t:Test_Tab_Var + + let s:Test_Script_Var = [7] + call assert_match("\ns:Test_Script_Var \\[7]", execute('let s:')) + unlet s:Test_Script_Var + + let l:Test_Local_Var = {'k' : 5} + call assert_match("\nl:Test_Local_Var {'k': 5}", execute('let l:')) + call assert_match("v:errors []", execute('let v:')) endfunc func s:set_arg1(a) abort @@ -201,16 +254,45 @@ func Test_let_option_error() let &fillchars = _w endfunc +" Errors with the :let statement func Test_let_errors() let s = 'abcd' call assert_fails('let s[1] = 5', 'E689:') let l = [1, 2, 3] call assert_fails('let l[:] = 5', 'E709:') + + call assert_fails('let x:lnum=5', 'E488:') + call assert_fails('let v:=5', 'E461:') + call assert_fails('let [a]', 'E474:') + call assert_fails('let [a, b] = [', 'E697:') + call assert_fails('let [a, b] = [10, 20', 'E696:') + call assert_fails('let [a, b] = 10', 'E714:') + call assert_fails('let [a, , b] = [10, 20]', 'E475:') + call assert_fails('let [a, b&] = [10, 20]', 'E475:') + call assert_fails('let $ = 10', 'E475:') + call assert_fails('let $FOO[1] = "abc"', 'E18:') + call assert_fails('let &buftype[1] = "nofile"', 'E18:') + let s = "var" + let var = 1 + call assert_fails('let {s}.1 = 2', 'E18:') + + " This test works only when the language is English + if v:lang == "C" || v:lang =~ '^[Ee]n' + call assert_fails('let [a ; b;] = [10, 20]', + \ 'Double ; in list of variables') + endif endfunc func Test_let_heredoc_fails() call assert_fails('let v =<< marker', 'E991:') + try + exe "let v =<< TEXT | abc | TEXT" + call assert_report('No exception thrown') + catch /E488:/ + catch + call assert_report("Caught exception: " .. v:exception) + endtry let text =<< trim END func WrongSyntax() @@ -243,6 +325,10 @@ func Test_let_heredoc_fails() call writefile(text, 'XheredocBadMarker') call assert_fails('source XheredocBadMarker', 'E221:') call delete('XheredocBadMarker') + + call writefile(['let v =<< TEXT', 'abc'], 'XheredocMissingMarker') + call assert_fails('source XheredocMissingMarker', 'E990:') + call delete('XheredocMissingMarker') endfunc func Test_let_heredoc_trim_no_indent_marker() @@ -361,3 +447,5 @@ E END call assert_equal([' x', ' \y', ' z'], [a, b, c]) endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim index 9cef6905a5..8c182ee445 100644 --- a/src/nvim/testdir/test_listdict.vim +++ b/src/nvim/testdir/test_listdict.vim @@ -574,6 +574,18 @@ func Test_let_lock_list() unlet l endfunc +" Locking part of the list +func Test_let_lock_list_items() + let l = [1, 2, 3, 4] + lockvar l[2:] + call assert_equal(0, islocked('l[0]')) + call assert_equal(1, islocked('l[2]')) + call assert_equal(1, islocked('l[3]')) + call assert_fails('let l[2] = 10', 'E741:') + call assert_fails('let l[3] = 20', 'E741:') + unlet l +endfunc + " lockvar/islocked() triggering script autoloading func Test_lockvar_script_autoload() let old_rtp = &rtp @@ -870,6 +882,46 @@ func Test_scope_dict() call s:check_scope_dict('v', v:true) endfunc +" Test for deep nesting of lists (> 100) +func Test_deep_nested_list() + let deep_list = [] + let l = deep_list + for i in range(102) + let newlist = [] + call add(l, newlist) + let l = newlist + endfor + call add(l, 102) + + call assert_fails('let m = deepcopy(deep_list)', 'E698:') + call assert_fails('lockvar 110 deep_list', 'E743:') + call assert_fails('unlockvar 110 deep_list', 'E743:') + " Nvim implements :echo very differently + " call assert_fails('let x = execute("echo deep_list")', 'E724:') + call test_garbagecollect_now() + unlet deep_list +endfunc + +" Test for deep nesting of dicts (> 100) +func Test_deep_nested_dict() + let deep_dict = {} + let d = deep_dict + for i in range(102) + let newdict = {} + let d.k = newdict + let d = newdict + endfor + let d.k = 'v' + + call assert_fails('let m = deepcopy(deep_dict)', 'E698:') + call assert_fails('lockvar 110 deep_dict', 'E743:') + call assert_fails('unlockvar 110 deep_dict', 'E743:') + " Nvim implements :echo very differently + " call assert_fails('let x = execute("echo deep_dict")', 'E724:') + call test_garbagecollect_now() + unlet deep_dict +endfunc + " Test for a null list func Test_null_list() let l = v:_null_list @@ -906,3 +958,5 @@ func Test_null_list() call assert_equal(1, islocked('l')) unlockvar l endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim index e421d21de1..4af02d3d31 100644 --- a/src/nvim/testdir/test_spell.vim +++ b/src/nvim/testdir/test_spell.vim @@ -474,6 +474,35 @@ func Test_spellsuggest_option_expr() bwipe! endfunc +" Test for 'spellsuggest' expr errrors +func Test_spellsuggest_expr_errors() + " 'spellsuggest' + func MySuggest() + return range(3) + endfunc + set spell spellsuggest=expr:MySuggest() + call assert_equal([], spellsuggest('baord', 3)) + + " Test for 'spellsuggest' expression returning a non-list value + func! MySuggest2() + return 'good' + endfunc + set spellsuggest=expr:MySuggest2() + call assert_equal([], spellsuggest('baord')) + + " Test for 'spellsuggest' expression returning a list with dict values + func! MySuggest3() + return [[{}, {}]] + endfunc + set spellsuggest=expr:MySuggest3() + call assert_fails("call spellsuggest('baord')", 'E728:') + + set nospell spellsuggest& + delfunc MySuggest + delfunc MySuggest2 + delfunc MySuggest3 +endfunc + func Test_spellsuggest_timeout() set spellsuggest=timeout:30 set spellsuggest=timeout:-123 diff --git a/src/nvim/testdir/test_unlet.vim b/src/nvim/testdir/test_unlet.vim index 873cd066a9..ed80c91277 100644 --- a/src/nvim/testdir/test_unlet.vim +++ b/src/nvim/testdir/test_unlet.vim @@ -20,6 +20,7 @@ endfunc func Test_unlet_fails() call assert_fails('unlet v:["count"]', 'E46:') + call assert_fails('unlet $', 'E475:') endfunc func Test_unlet_env() @@ -55,3 +56,5 @@ func Test_unlet_complete() call feedkeys(":unlet $FOO\t\n", 'tx') call assert_true(!exists('$FOOBAR') || empty($FOOBAR)) endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_user_func.vim b/src/nvim/testdir/test_user_func.vim index c14624f5b4..13a0334cd3 100644 --- a/src/nvim/testdir/test_user_func.vim +++ b/src/nvim/testdir/test_user_func.vim @@ -87,6 +87,9 @@ func Test_user_func() call assert_fails("call extend(g:, {'max': function('min')})", 'E704') call assert_equal(3, max([1, 2, 3])) + " Try to overwrite an user defined function with a function reference + call assert_fails("let Expr1 = function('min')", 'E705:') + " Regression: the first line below used to throw ?E110: Missing ')'? " Second is here just to prove that this line is correct when not skipping " rhs of &&. diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 3487a028ca..ea3d15915a 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1542,6 +1542,9 @@ func Test_delfunction_force() endfunc delfunc! Xtest delfunc! Xtest + + " Try deleting the current function + call assert_fails('delfunc Test_delfunction_force', 'E131:') endfunc " Test using bang after user command {{{1 -- cgit From 7b39ce36a4599539cd5cb07dad6bd980d30a3180 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 26 Oct 2022 13:41:43 +0800 Subject: vim-patch:8.2.0418: code in eval.c not sufficiently covered by tests Problem: Code in eval.c not sufficiently covered by tests. Solution: Add more tests. (Yegappan Lakshmanan, closes vim/vim#5815) https://github.com/vim/vim/commit/8b633135106dda8605463b780573c45b00c22afe Nvim does not have v:none, so comment out test for it. --- src/nvim/testdir/test_blob.vim | 1 + src/nvim/testdir/test_cmdline.vim | 26 +++----------------------- src/nvim/testdir/test_functions.vim | 14 ++++++++++++++ src/nvim/testdir/test_lambda.vim | 1 + src/nvim/testdir/test_let.vim | 11 +++++++++++ src/nvim/testdir/test_listdict.vim | 22 ++++++++++++++++++++++ src/nvim/testdir/test_marks.vim | 9 +++++++++ src/nvim/testdir/test_method.vim | 1 + src/nvim/testdir/test_normal.vim | 1 + src/nvim/testdir/test_unlet.vim | 6 ++++++ src/nvim/testdir/test_usercommands.vim | 21 +++++++++++++++++++++ src/nvim/testdir/test_vimscript.vim | 16 ++++++++++++++++ src/nvim/testdir/test_window_cmd.vim | 5 +++++ 13 files changed, 111 insertions(+), 23 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_blob.vim b/src/nvim/testdir/test_blob.vim index 70529c14d5..513877580f 100644 --- a/src/nvim/testdir/test_blob.vim +++ b/src/nvim/testdir/test_blob.vim @@ -88,6 +88,7 @@ func Test_blob_get_range() call assert_equal(0z0011223344, b[:]) call assert_equal(0z0011223344, b[:-1]) call assert_equal(0z, b[5:6]) + call assert_equal(0z0011, b[-10:1]) endfunc func Test_blob_get() diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index c8b8c88d75..00bfadec93 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -854,7 +854,7 @@ func Test_cmdline_complete_bang() endif endfunc -funct Test_cmdline_complete_languages() +func Test_cmdline_complete_languages() let lang = substitute(execute('language time'), '.*"\(.*\)"$', '\1', '') call assert_equal(lang, v:lc_time) @@ -891,10 +891,8 @@ endfunc func Test_cmdline_complete_env_variable() let $X_VIM_TEST_COMPLETE_ENV = 'foo' - call feedkeys(":edit $X_VIM_TEST_COMPLETE_E\\\"\", 'tx') call assert_match('"edit $X_VIM_TEST_COMPLETE_ENV', @:) - unlet $X_VIM_TEST_COMPLETE_ENV endfunc @@ -1074,17 +1072,13 @@ func Test_cmdline_complete_various() call feedkeys(":e `a1b2c\t\\"\", 'xt') call assert_equal('"e `a1b2c', @:) - " completion for the expression register - call feedkeys(":\"\=float2\t\"\\"\", 'xt') - call assert_equal('"float2nr("', @=) - " completion for :language command with an invalid argument call feedkeys(":language dummy \t\\"\", 'xt') call assert_equal("\"language dummy \t", @:) " completion for commands after a :global command - call feedkeys(":g/a\\xb/call float2\t\\"\", 'xt') - call assert_equal('"g/a\xb/call float2nr(', @:) + call feedkeys(":g/a\\xb/clearj\t\\"\", 'xt') + call assert_equal('"g/a\xb/clearjumps', @:) " completion with ambiguous user defined commands com TCmd1 echo 'TCmd1' @@ -1097,20 +1091,6 @@ func Test_cmdline_complete_various() " completion after a range followed by a pipe (|) character call feedkeys(":1,10 | chist\t\\"\", 'xt') call assert_equal('"1,10 | chistory', @:) - - " completion for window local variables - let w:wvar1 = 10 - let w:wvar2 = 10 - call feedkeys(":echo w:wvar\\\"\", 'xt') - call assert_equal('"echo w:wvar1 w:wvar2', @:) - unlet w:wvar1 w:wvar2 - - " completion for tab local variables - let t:tvar1 = 10 - let t:tvar2 = 10 - call feedkeys(":echo t:tvar\\\"\", 'xt') - call assert_equal('"echo t:tvar1 t:tvar2', @:) - unlet t:tvar1 t:tvar2 endfunc func Test_cmdline_write_alternatefile() diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 27aab6330f..7049602d98 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1199,6 +1199,7 @@ func Test_col() norm gg4|mx6|mY2| call assert_equal(2, col('.')) call assert_equal(7, col('$')) + call assert_equal(2, col('v')) call assert_equal(4, col("'x")) call assert_equal(6, col("'Y")) call assert_equal(2, [1, 2]->col()) @@ -1209,6 +1210,19 @@ func Test_col() call assert_equal(0, col([2, '$'])) call assert_equal(0, col([1, 100])) call assert_equal(0, col([1])) + + " test for getting the visual start column + func T() + let g:Vcol = col('v') + return '' + endfunc + let g:Vcol = 0 + xmap T() + exe "normal gg3|ve\" + call assert_equal(3, g:Vcol) + xunmap + delfunc T + bw! endfunc diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim index c178c87d3e..997c3dcd3a 100644 --- a/src/nvim/testdir/test_lambda.vim +++ b/src/nvim/testdir/test_lambda.vim @@ -63,6 +63,7 @@ function Test_lambda_fails() call assert_equal(3, {a, b -> a + b}(1, 2)) call assert_fails('echo {a, a -> a + a}(1, 2)', 'E853:') call assert_fails('echo {a, b -> a + b)}(1, 2)', 'E15:') + echo assert_fails('echo 10->{a -> a + 2}', 'E107:') endfunc func Test_not_lambda() diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim index 009735e004..89cf4b5498 100644 --- a/src/nvim/testdir/test_let.vim +++ b/src/nvim/testdir/test_let.vim @@ -276,6 +276,17 @@ func Test_let_errors() let s = "var" let var = 1 call assert_fails('let {s}.1 = 2', 'E18:') + call assert_fails('let a[1] = 5', 'E121:') + let l = [[1,2]] + call assert_fails('let l[:][0] = [5]', 'E708:') + let d = {'k' : 4} + call assert_fails('let d.# = 5', 'E713:') + call assert_fails('let d.m += 5', 'E734:') + let l = [1, 2] + call assert_fails('let l[2] = 0', 'E684:') + call assert_fails('let l[0:1] = [1, 2, 3]', 'E710:') + call assert_fails('let l[-2:-3] = [3, 4]', 'E684:') + call assert_fails('let l[0:4] = [5, 6]', 'E711:') " This test works only when the language is English if v:lang == "C" || v:lang =~ '^[Ee]n' diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim index 8c182ee445..3b5ed27f5e 100644 --- a/src/nvim/testdir/test_listdict.vim +++ b/src/nvim/testdir/test_listdict.vim @@ -31,6 +31,7 @@ func Test_list_slice() call assert_equal([1, 'as''d', [1, 2, function('strlen')]], l[:-2]) call assert_equal([1, 'as''d', [1, 2, function('strlen')], {'a': 1}], l[0:8]) call assert_equal([], l[8:-1]) + call assert_equal([], l[0:-10]) endfunc " List identity @@ -104,6 +105,8 @@ func Test_list_range_assign() let l = [0] let l[:] = [1, 2] call assert_equal([1, 2], l) + let l[-4:-1] = [5, 6] + call assert_equal([5, 6], l) endfunc " Test removing items in list @@ -709,6 +712,12 @@ func Test_listdict_compare() call assert_true(d == d) call assert_false(l != deepcopy(l)) call assert_false(d != deepcopy(d)) + + " comparison errors + call assert_fails('echo [1, 2] =~ {}', 'E691:') + call assert_fails('echo [1, 2] =~ [1, 2]', 'E692:') + call assert_fails('echo {} =~ 5', 'E735:') + call assert_fails('echo {} =~ {}', 'E736:') endfunc " compare complex recursively linked list and dict @@ -922,6 +931,19 @@ func Test_deep_nested_dict() unlet deep_dict endfunc +" List and dict indexing tests +func Test_listdict_index() + call assert_fails('echo function("min")[0]', 'E695:') + call assert_fails('echo v:true[0]', 'E909:') + let d = {'k' : 10} + call assert_fails('echo d.', 'E15:') + call assert_fails('echo d[1:2]', 'E719:') + call assert_fails("let v = [4, 6][{-> 1}]", 'E729:') + call assert_fails("let v = range(5)[2:[]]", 'E730:') + call assert_fails("let v = range(5)[2:{-> 2}(]", 'E116:') + call assert_fails("let v = range(5)[2:3", 'E111:') +endfunc + " Test for a null list func Test_null_list() let l = v:_null_list diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim index 74e63d9d69..054ebf1218 100644 --- a/src/nvim/testdir/test_marks.vim +++ b/src/nvim/testdir/test_marks.vim @@ -91,6 +91,15 @@ func Test_setpos() call assert_equal([0, 1, 21341234, 0], getpos("'a")) call assert_equal(4, virtcol("'a")) + " Test with invalid buffer number, line number and column number + call cursor(2, 2) + call setpos('.', [-1, 1, 1, 0]) + call assert_equal([2, 2], [line('.'), col('.')]) + call setpos('.', [0, -1, 1, 0]) + call assert_equal([2, 2], [line('.'), col('.')]) + call setpos('.', [0, 1, -1, 0]) + call assert_equal([2, 2], [line('.'), col('.')]) + bwipe! call win_gotoid(twowin) bwipe! diff --git a/src/nvim/testdir/test_method.vim b/src/nvim/testdir/test_method.vim index cdf688b857..e035b3ef50 100644 --- a/src/nvim/testdir/test_method.vim +++ b/src/nvim/testdir/test_method.vim @@ -35,6 +35,7 @@ func Test_list_method() call assert_equal(v:t_list, l->type()) call assert_equal([1, 2, 3], [1, 1, 2, 3, 3]->uniq()) call assert_fails('eval l->values()', 'E715:') + call assert_fails('echo []->len', 'E107:') endfunc func Test_dict_method() diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 4f842189b6..5730085c78 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -514,6 +514,7 @@ func Test_normal10_expand() " Test expand(`=...`) i.e. backticks expression expansion call assert_equal('5', expand('`=2+3`')) + call assert_equal('3.14', expand('`=3.14`')) " clean up bw! diff --git a/src/nvim/testdir/test_unlet.vim b/src/nvim/testdir/test_unlet.vim index ed80c91277..acf98bb1fc 100644 --- a/src/nvim/testdir/test_unlet.vim +++ b/src/nvim/testdir/test_unlet.vim @@ -21,6 +21,12 @@ endfunc func Test_unlet_fails() call assert_fails('unlet v:["count"]', 'E46:') call assert_fails('unlet $', 'E475:') + let v = {} + call assert_fails('unlet v[:]', 'E719:') + let l = [] + call assert_fails("unlet l['k'", 'E111:') + let d = {'k' : 1} + call assert_fails("unlet d.k2", 'E716:') endfunc func Test_unlet_env() diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim index 12fe39851d..a3070d6517 100644 --- a/src/nvim/testdir/test_usercommands.vim +++ b/src/nvim/testdir/test_usercommands.vim @@ -617,6 +617,27 @@ func Test_command_list() call assert_equal("\nNo user-defined commands found", execute('command')) endfunc +" Test for a custom user completion returning the wrong value type +func Test_usercmd_custom() + func T1(a, c, p) + return "a\nb\n" + endfunc + command -nargs=* -complete=customlist,T1 TCmd1 + call feedkeys(":T1 \\\"\", 'xt') + call assert_equal('"T1 ', @:) + delcommand TCmd1 + delfunc T1 + + func T2(a, c, p) + return ['a', 'b', 'c'] + endfunc + command -nargs=* -complete=customlist,T2 TCmd2 + call feedkeys(":T2 \\\"\", 'xt') + call assert_equal('"T2 ', @:) + delcommand TCmd2 + delfunc T2 +endfunc + func Test_delcommand_buffer() command Global echo 'global' command -buffer OneBuffer echo 'one' diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index ea3d15915a..7a75d0e88a 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1656,6 +1656,8 @@ func Test_compound_assignment_operators() call assert_equal(4.2, x) call assert_fails('let x %= 0.5', 'E734') call assert_fails('let x .= "f"', 'E734') + let x = !3.14 + call assert_equal(0.0, x) endif " Test for environment variable @@ -1942,6 +1944,20 @@ func Test_sfile_in_function() delfunc Xfunc endfunc +" Test for errors in converting to float from various types {{{1 +func Test_float_conversion_errors() + if has('float') + call assert_fails('let x = 4.0 % 2.0', 'E804') + call assert_fails('echo 1.1[0]', 'E806') + call assert_fails('echo sort([function("min"), 1], "f")', 'E891:') + call assert_fails('echo 3.2 == "vim"', 'E892:') + call assert_fails('echo sort([[], 1], "f")', 'E893:') + call assert_fails('echo sort([{}, 1], "f")', 'E894:') + call assert_fails('echo 3.2 == v:true', 'E362:') + " call assert_fails('echo 3.2 == v:none', 'E907:') + endif +endfunc + func Test_for_over_string() let res = '' for c in 'aécÌ€d' diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index c4ce4d638c..e01993b99c 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -592,6 +592,11 @@ func Test_window_contents() call assert_equal(59, line("w0")) call assert_equal('59 ', s3) + %d + call setline(1, ['one', 'two', 'three']) + call assert_equal(1, line('w0')) + call assert_equal(3, line('w$')) + bwipeout! call test_garbagecollect_now() endfunc -- cgit From fed86bc78ae18eafd9fe247f7cc56ef3dcec6d09 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 26 Oct 2022 17:18:05 +0800 Subject: fix(spell): fix wrong cast (#20810) Fix #20787 --- src/nvim/spellsuggest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c index fbe2ec837b..400579a233 100644 --- a/src/nvim/spellsuggest.c +++ b/src/nvim/spellsuggest.c @@ -2250,7 +2250,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so ftp = (fromto_T *)gap->ga_data + sp->ts_curi++; if (*ftp->ft_from != *p) { // past possible matching entries - sp->ts_curi = (char_u)gap->ga_len; + sp->ts_curi = (int16_t)gap->ga_len; break; } if (STRNCMP(ftp->ft_from, p, STRLEN(ftp->ft_from)) == 0 -- cgit From a0ade77353f23a41e04ef20064577e14814fb386 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 26 Oct 2022 18:21:52 +0800 Subject: vim-patch:8.2.0162: balloon test fails in the GUI Problem: Balloon test fails in the GUI. Solution: Skip test in the GUI. https://github.com/vim/vim/commit/7d8ea0b24191d64155fcf9e8d2d2eefff91ae549 Co-authored-by: Bram Moolenaar --- src/nvim/testdir/test_functions.vim | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 7049602d98..50005c6c37 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1320,6 +1320,9 @@ func Test_balloon_show() " This won't do anything but must not crash either. call balloon_show('hi!') + if !has('gui_running') + call balloon_show(range(3)) + endif endfunc func Test_shellescape() -- cgit From 19eca77af434a609d60ea1e3d9594100df74f301 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 26 Oct 2022 18:12:16 +0800 Subject: vim-patch:8.2.0163: test hangs on MS-Windows console Problem: Test hangs on MS-Windows console. Solution: use feedkeys() instead of test_feedinput(). (Ken Takata) https://github.com/vim/vim/commit/272ca95fc3d21ae1e2626a7aec38a6990e88ad6b Co-authored-by: Bram Moolenaar --- src/nvim/testdir/test_functions.vim | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 50005c6c37..8b30c53178 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -2038,15 +2038,10 @@ func Test_range() call assert_equal(1, index(range(1, 5), 2)) " inputlist() - " call test_feedinput("1\") - call nvim_input('1') - call assert_equal(1, inputlist(range(10))) - " call test_feedinput("1\") - call nvim_input('1') - call assert_equal(1, inputlist(range(3, 10))) - - " call assert_equal('[0,1,2,3]', json_encode(range(4))) - call assert_equal('[0, 1, 2, 3]', json_encode(range(4))) + call feedkeys(":let result = inputlist(range(10))\1\", 'x') + call assert_equal(1, result) + call feedkeys(":let result = inputlist(range(3, 10))\1\", 'x') + call assert_equal(1, result) " insert() call assert_equal([42, 1, 2, 3, 4, 5], insert(range(1, 5), 42)) @@ -2059,6 +2054,10 @@ func Test_range() " join() call assert_equal('0 1 2 3 4', join(range(5))) + " json_encode() + " call assert_equal('[0,1,2,3]', json_encode(range(4))) + call assert_equal('[0, 1, 2, 3]', json_encode(range(4))) + " len() call assert_equal(0, len(range(0))) call assert_equal(2, len(range(2))) -- cgit From cfccae95844db21aad773c9a8f8b636f53d6c8c4 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 26 Oct 2022 19:04:38 +0800 Subject: vim-patch:8.2.0610: some tests are still old style Problem: Some tests are still old style. Solution: Convert to new style tests. (Yegappan Lakshmanan, closes vim/vim#5957) https://github.com/vim/vim/commit/08f4157c5cabc55bcb22f04dd7c717aba40caa34 Fix missing error message when sort() compare function fails. Cherry-pick a line in test_utf8.vim from patch 8.2.0448. Cherry-pick builtin_function() change from patch 8.2.0595. --- src/nvim/eval/typval.c | 4 +- src/nvim/eval/userfunc.c | 4 +- src/nvim/testdir/test_blob.vim | 3 ++ src/nvim/testdir/test_expr.vim | 1 + src/nvim/testdir/test_filter_map.vim | 4 ++ src/nvim/testdir/test_functions.vim | 6 +++ src/nvim/testdir/test_listdict.vim | 34 ++++++++++++++- src/nvim/testdir/test_sort.vim | 2 +- src/nvim/testdir/test_syntax.vim | 4 ++ src/nvim/testdir/test_utf8.vim | 6 ++- src/nvim/testdir/test_vimscript.vim | 80 ++++++++++++++++++++++++++++++++++++ 11 files changed, 142 insertions(+), 6 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 6bb390d793..a6a8c16a3b 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1094,7 +1094,9 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) tv_clear(&argv[1]); if (res == FAIL) { + // XXX: ITEM_COMPARE_FAIL is unused res = ITEM_COMPARE_FAIL; + sortinfo->item_compare_func_err = true; } else { res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err); if (res > 0) { @@ -1257,7 +1259,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) } else { li = TV_LIST_ITEM_NEXT(l, li); } - if (info.item_compare_func_err) { // -V547 + if (info.item_compare_func_err) { emsg(_("E882: Uniq compare function failed")); break; } diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 4a62b4bf8d..7440044e52 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1335,10 +1335,10 @@ void free_all_functions(void) /// @param[in] len length of "name", or -1 for NUL terminated. /// /// @return true if "name" looks like a builtin function name: starts with a -/// lower case letter and doesn't contain AUTOLOAD_CHAR. +/// lower case letter and doesn't contain AUTOLOAD_CHAR or ':'. static bool builtin_function(const char *name, int len) { - if (!ASCII_ISLOWER(name[0])) { + if (!ASCII_ISLOWER(name[0]) || name[1] == ':') { return false; } diff --git a/src/nvim/testdir/test_blob.vim b/src/nvim/testdir/test_blob.vim index 513877580f..f2b32c06d4 100644 --- a/src/nvim/testdir/test_blob.vim +++ b/src/nvim/testdir/test_blob.vim @@ -314,6 +314,9 @@ func Test_blob_insert() call assert_fails('call insert(b, -1)', 'E475:') call assert_fails('call insert(b, 257)', 'E475:') call assert_fails('call insert(b, 0, [9])', 'E745:') + call assert_fails('call insert(b, 0, -20)', 'E475:') + call assert_fails('call insert(b, 0, 20)', 'E475:') + call assert_fails('call insert(b, [])', 'E745:') call assert_equal(0, insert(v:_null_blob, 0x33)) " Translated from v8.2.3284 diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index c63a969e50..f6e6004a31 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -46,6 +46,7 @@ func Test_dict() call assert_equal('zero', d[0]) call assert_true(has_key(d, '')) call assert_true(has_key(d, 'a')) + call assert_fails("let i = has_key([], 'a')", 'E715:') let d[''] = 'none' let d['a'] = 'aaa' diff --git a/src/nvim/testdir/test_filter_map.vim b/src/nvim/testdir/test_filter_map.vim index 1cd3a2287b..54f6e269e0 100644 --- a/src/nvim/testdir/test_filter_map.vim +++ b/src/nvim/testdir/test_filter_map.vim @@ -86,6 +86,10 @@ func Test_map_filter_fails() call assert_fails('call filter([1], "42 +")', 'E15:') call assert_fails("let l = map('abc', '\"> \" . v:val')", 'E896:') call assert_fails("let l = filter('abc', '\"> \" . v:val')", 'E896:') + call assert_fails("let l = filter([1, 2, 3], '{}')", 'E728:') + call assert_fails("let l = filter({'k' : 10}, '{}')", 'E728:') + call assert_equal(0, map(v:_null_list, '"> " .. v:val')) + call assert_equal(0, map(v:_null_dict, '"> " .. v:val')) endfunc func Test_map_and_modify() diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 8b30c53178..8f2a61e399 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -67,9 +67,11 @@ func Test_len() call assert_equal(2, len('ab')) call assert_equal(0, len([])) + call assert_equal(0, len(v:_null_list)) call assert_equal(2, len([2, 1])) call assert_equal(0, len({})) + call assert_equal(0, len(v:_null_dict)) call assert_equal(2, len({'a': 1, 'b': 2})) " call assert_fails('call len(v:none)', 'E701:') @@ -771,6 +773,9 @@ func Test_append() split only undo + + " Using $ instead of '$' must give an error + call assert_fails("call append($, 'foobar')", 'E116:') endfunc func Test_getbufvar() @@ -2066,6 +2071,7 @@ func Test_range() " list2str() call assert_equal('ABC', list2str(range(65, 67))) + call assert_fails('let s = list2str(5)', 'E474:') " lock() let thelist = range(5) diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim index 3b5ed27f5e..d92563a451 100644 --- a/src/nvim/testdir/test_listdict.vim +++ b/src/nvim/testdir/test_listdict.vim @@ -32,6 +32,7 @@ func Test_list_slice() call assert_equal([1, 'as''d', [1, 2, function('strlen')], {'a': 1}], l[0:8]) call assert_equal([], l[8:-1]) call assert_equal([], l[0:-10]) + call assert_equal([], v:_null_list[:2]) endfunc " List identity @@ -169,6 +170,19 @@ func Test_dict() call filter(d, 'v:key =~ ''[ac391]''') call assert_equal({'c': 'ccc', '1': 99, '3': 33, '-1': {'a': 1}}, d) + " duplicate key + call assert_fails("let d = {'k' : 10, 'k' : 20}", 'E721:') + " missing comma + call assert_fails("let d = {'k' : 10 'k' : 20}", 'E722:') + " missing curly brace + call assert_fails("let d = {'k' : 10,", 'E723:') + " invalid key + call assert_fails('let d = #{++ : 10}', 'E15:') + " wrong type for key + call assert_fails('let d={[] : 10}', 'E730:') + " undefined variable as value + call assert_fails("let d={'k' : i}", 'E121:') + " allow key starting with number at the start, not a curly expression call assert_equal({'1foo': 77}, #{1foo: 77}) @@ -269,7 +283,7 @@ func Test_script_local_dict_func() unlet g:dict endfunc -" Test removing items in la dictionary +" Test removing items in a dictionary func Test_dict_func_remove() let d = {1:'a', 2:'b', 3:'c'} call assert_equal('b', remove(d, 2)) @@ -643,6 +657,11 @@ func Test_reverse_sort_uniq() call assert_fails('call reverse("")', 'E899:') call assert_fails('call uniq([1, 2], {x, y -> []})', 'E882:') + call assert_fails("call sort([1, 2], function('min'), 1)", "E715:") + call assert_fails("call sort([1, 2], function('invalid_func'))", "E700:") + call assert_fails("call sort([1, 2], function('min'))", "E702:") + call assert_equal(0, sort(v:_null_list)) + call assert_equal(0, uniq(v:_null_list)) endfunc " reduce a list or a blob @@ -942,6 +961,9 @@ func Test_listdict_index() call assert_fails("let v = range(5)[2:[]]", 'E730:') call assert_fails("let v = range(5)[2:{-> 2}(]", 'E116:') call assert_fails("let v = range(5)[2:3", 'E111:') + call assert_fails("let l = insert([1,2,3], 4, 10)", 'E684:') + call assert_fails("let l = insert([1,2,3], 4, -10)", 'E684:') + call assert_fails("let l = insert([1,2,3], 4, [])", 'E745:') endfunc " Test for a null list @@ -981,4 +1003,14 @@ func Test_null_list() unlockvar l endfunc +" Test for a null dict +func Test_null_dict() + call assert_equal(0, items(v:_null_dict)) + call assert_equal(0, keys(v:_null_dict)) + call assert_equal(0, values(v:_null_dict)) + call assert_false(has_key(v:_null_dict, 'k')) + call assert_fails("let l = [] + v:_null_list", 'E15:') + call assert_fails("let l = v:_null_list + []", 'E15:') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_sort.vim b/src/nvim/testdir/test_sort.vim index 9895ad754c..c3e7788164 100644 --- a/src/nvim/testdir/test_sort.vim +++ b/src/nvim/testdir/test_sort.vim @@ -80,7 +80,7 @@ func Test_sort_default() call assert_equal(['2', 'A', 'AA', 'a', 1, 3.3], sort([3.3, 1, "2", "A", "a", "AA"], '')) call assert_equal(['2', 'A', 'AA', 'a', 1, 3.3], sort([3.3, 1, "2", "A", "a", "AA"], 0)) call assert_equal(['2', 'A', 'a', 'AA', 1, 3.3], sort([3.3, 1, "2", "A", "a", "AA"], 1)) - call assert_fails('call sort([3.3, 1, "2"], 3)', "E474") + call assert_fails('call sort([3.3, 1, "2"], 3)', "E474:") endfunc " Tests for the ":sort" command. diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index ccff01486e..9b4293764d 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -171,6 +171,10 @@ func Test_syntax_list() let a = execute('syntax list') call assert_equal("\nNo Syntax items defined for this buffer", a) + syntax keyword Type int containedin=g1 skipwhite skipempty skipnl nextgroup=Abc + let exp = "Type xxx containedin=g1 nextgroup=Abc skipnl skipwhite skipempty int" + call assert_equal(exp, split(execute("syntax list"), "\n")[1]) + bd endfunc diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim index ab3503c282..7145d303cc 100644 --- a/src/nvim/testdir/test_utf8.vim +++ b/src/nvim/testdir/test_utf8.vim @@ -131,9 +131,13 @@ func Test_list2str_str2list_latin1() let save_encoding = &encoding " set encoding=latin1 - + let lres = str2list(s, 1) let sres = list2str(l, 1) + call assert_equal([65, 66, 67], str2list("ABC")) + + " Try converting a list to a string in latin-1 encoding + call assert_equal([1, 2, 3], str2list(list2str([1, 2, 3]))) let &encoding = save_encoding call assert_equal(l, lres) diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 7a75d0e88a..3d3f0a8839 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1958,6 +1958,86 @@ func Test_float_conversion_errors() endif endfunc +func Test_invalid_function_names() + " function name not starting with capital + let caught_e128 = 0 + try + func! g:test() + echo "test" + endfunc + catch /E128:/ + let caught_e128 = 1 + endtry + call assert_equal(1, caught_e128) + + " function name includes a colon + let caught_e884 = 0 + try + func! b:test() + echo "test" + endfunc + catch /E884:/ + let caught_e884 = 1 + endtry + call assert_equal(1, caught_e884) + + " function name folowed by # + let caught_e128 = 0 + try + func! test2() "# + echo "test2" + endfunc + catch /E128:/ + let caught_e128 = 1 + endtry + call assert_equal(1, caught_e128) + + " function name starting with/without "g:", buffer-local funcref. + function! g:Foo(n) + return 'called Foo(' . a:n . ')' + endfunction + let b:my_func = function('Foo') + call assert_equal('called Foo(1)', b:my_func(1)) + call assert_equal('called Foo(2)', g:Foo(2)) + call assert_equal('called Foo(3)', Foo(3)) + delfunc g:Foo + + " script-local function used in Funcref must exist. + let lines =<< trim END + func s:Testje() + return "foo" + endfunc + let Bar = function('s:Testje') + call assert_equal(0, exists('s:Testje')) + call assert_equal(1, exists('*s:Testje')) + call assert_equal(1, exists('Bar')) + call assert_equal(1, exists('*Bar')) + END + call writefile(lines, 'Xscript') + source Xscript + call delete('Xscript') +endfunc + +" substring and variable name +func Test_substring_var() + let str = 'abcdef' + let n = 3 + call assert_equal('def', str[n:]) + call assert_equal('abcd', str[:n]) + call assert_equal('d', str[n:n]) + unlet n + let nn = 3 + call assert_equal('def', str[nn:]) + call assert_equal('abcd', str[:nn]) + call assert_equal('d', str[nn:nn]) + unlet nn + let b:nn = 4 + call assert_equal('ef', str[b:nn:]) + call assert_equal('abcde', str[:b:nn]) + call assert_equal('e', str[b:nn:b:nn]) + unlet b:nn +endfunc + func Test_for_over_string() let res = '' for c in 'aécÌ€d' -- cgit From ef363ed37cdf5c460cca840aebc825011e0294ee Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 26 Oct 2022 19:53:54 +0800 Subject: vim-patch:8.2.0619: null dict is not handled like an empty dict Problem: Null dict is not handled like an empty dict. Solution: Fix the code and add tests. (Yegappan Lakshmanan, closes vim/vim#5968) https://github.com/vim/vim/commit/ea04a6e8baff2f27da7cdd54bf70a5525994f76d Nvim doesn't support modifying NULL list, so comment out a line. --- src/nvim/eval.c | 1 + src/nvim/eval/typval.c | 8 ++++-- src/nvim/testdir/test_blob.vim | 1 + src/nvim/testdir/test_expr.vim | 7 ----- src/nvim/testdir/test_filter_map.vim | 1 + src/nvim/testdir/test_let.vim | 3 +++ src/nvim/testdir/test_listdict.vim | 47 ++++++++++++++++++++++++++-------- src/nvim/testdir/test_unlet.vim | 1 + src/nvim/testdir/test_usercommands.vim | 10 ++++---- src/nvim/testdir/test_vimscript.vim | 17 ++++++++++++ 10 files changed, 72 insertions(+), 24 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 69bc26b82e..ffbe66aa25 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1449,6 +1449,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const key[len] = prevval; } if (wrong) { + tv_clear(&var1); return NULL; } } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index a6a8c16a3b..3b513bfafb 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2490,10 +2490,14 @@ bool tv_dict_equal(dict_T *const d1, dict_T *const d2, const bool ic, const bool if (d1 == d2) { return true; } - if (d1 == NULL || d2 == NULL) { + if (tv_dict_len(d1) != tv_dict_len(d2)) { return false; } - if (tv_dict_len(d1) != tv_dict_len(d2)) { + if (tv_dict_len(d1) == 0) { + // empty and NULL dicts are considered equal + return true; + } + if (d1 == NULL || d2 == NULL) { return false; } diff --git a/src/nvim/testdir/test_blob.vim b/src/nvim/testdir/test_blob.vim index f2b32c06d4..c5a217f36f 100644 --- a/src/nvim/testdir/test_blob.vim +++ b/src/nvim/testdir/test_blob.vim @@ -209,6 +209,7 @@ func Test_blob_add() call assert_equal(0z001122, b) call add(b, '51') call assert_equal(0z00112233, b) + call assert_equal(1, add(v:_null_blob, 0x22)) call assert_fails('call add(b, [9])', 'E745:') call assert_fails('call add("", 0x01)', 'E897:') diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index f6e6004a31..66660ab75e 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -99,13 +99,6 @@ func Test_loop_over_null_list() endfor endfunc -func Test_compare_null_dict() - call assert_fails('let x = v:_null_dict[10]') - call assert_equal({}, {}) - call assert_equal(v:_null_dict, v:_null_dict) - call assert_notequal({}, v:_null_dict) -endfunc - func Test_set_reg_null_list() call setreg('x', v:_null_list) endfunc diff --git a/src/nvim/testdir/test_filter_map.vim b/src/nvim/testdir/test_filter_map.vim index 54f6e269e0..ebcb6699c6 100644 --- a/src/nvim/testdir/test_filter_map.vim +++ b/src/nvim/testdir/test_filter_map.vim @@ -88,6 +88,7 @@ func Test_map_filter_fails() call assert_fails("let l = filter('abc', '\"> \" . v:val')", 'E896:') call assert_fails("let l = filter([1, 2, 3], '{}')", 'E728:') call assert_fails("let l = filter({'k' : 10}, '{}')", 'E728:') + call assert_fails("let l = filter([1, 2], {})", 'E731:') call assert_equal(0, map(v:_null_list, '"> " .. v:val')) call assert_equal(0, map(v:_null_dict, '"> " .. v:val')) endfunc diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim index 89cf4b5498..937076aa2a 100644 --- a/src/nvim/testdir/test_let.vim +++ b/src/nvim/testdir/test_let.vim @@ -275,6 +275,7 @@ func Test_let_errors() call assert_fails('let &buftype[1] = "nofile"', 'E18:') let s = "var" let var = 1 + call assert_fails('let var += [1,2]', 'E734:') call assert_fails('let {s}.1 = 2', 'E18:') call assert_fails('let a[1] = 5', 'E121:') let l = [[1,2]] @@ -287,6 +288,8 @@ func Test_let_errors() call assert_fails('let l[0:1] = [1, 2, 3]', 'E710:') call assert_fails('let l[-2:-3] = [3, 4]', 'E684:') call assert_fails('let l[0:4] = [5, 6]', 'E711:') + call assert_fails('let g:["a;b"] = 10', 'E461:') + call assert_fails('let g:.min = function("max")', 'E704:') " This test works only when the language is English if v:lang == "C" || v:lang =~ '^[Ee]n' diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim index d92563a451..f7261b2055 100644 --- a/src/nvim/testdir/test_listdict.vim +++ b/src/nvim/testdir/test_listdict.vim @@ -32,7 +32,11 @@ func Test_list_slice() call assert_equal([1, 'as''d', [1, 2, function('strlen')], {'a': 1}], l[0:8]) call assert_equal([], l[8:-1]) call assert_equal([], l[0:-10]) - call assert_equal([], v:_null_list[:2]) + " perform an operation on a list slice + let l = [1, 2, 3] + let l[:1] += [1, 2] + let l[2:] -= [1] + call assert_equal([2, 4, 2], l) endfunc " List identity @@ -147,6 +151,20 @@ func Test_list_func_remove() call assert_fails("call remove(l, l)", 'E745:') endfunc +" List add() function +func Test_list_add() + let l = [] + call add(l, 1) + call add(l, [2, 3]) + call add(l, []) + call add(l, v:_null_list) + call add(l, {'k' : 3}) + call add(l, {}) + call add(l, v:_null_dict) + call assert_equal([1, [2, 3], [], [], {'k' : 3}, {}, {}], l) + " call assert_equal(1, add(v:_null_list, 4)) +endfunc + " Tests for Dictionary type func Test_dict() @@ -660,8 +678,6 @@ func Test_reverse_sort_uniq() call assert_fails("call sort([1, 2], function('min'), 1)", "E715:") call assert_fails("call sort([1, 2], function('invalid_func'))", "E700:") call assert_fails("call sort([1, 2], function('min'))", "E702:") - call assert_equal(0, sort(v:_null_list)) - call assert_equal(0, uniq(v:_null_list)) endfunc " reduce a list or a blob @@ -709,7 +725,7 @@ func Test_reduce() call assert_equal(42, reduce(v:_null_blob, function('add'), 42)) endfunc -" splitting a string to a List +" splitting a string to a List using split() func Test_str_split() call assert_equal(['aa', 'bb'], split(' aa bb ')) call assert_equal(['aa', 'bb'], split(' aa bb ', '\W\+', 0)) @@ -964,6 +980,11 @@ func Test_listdict_index() call assert_fails("let l = insert([1,2,3], 4, 10)", 'E684:') call assert_fails("let l = insert([1,2,3], 4, -10)", 'E684:') call assert_fails("let l = insert([1,2,3], 4, [])", 'E745:') + let l = [1, 2, 3] + call assert_fails("let l[i] = 3", 'E121:') + call assert_fails("let l[1.1] = 4", 'E806:') + call assert_fails("let l[:i] = [4, 5]", 'E121:') + call assert_fails("let l[:3.2] = [4, 5]", 'E806:') endfunc " Test for a null list @@ -1005,12 +1026,18 @@ endfunc " Test for a null dict func Test_null_dict() - call assert_equal(0, items(v:_null_dict)) - call assert_equal(0, keys(v:_null_dict)) - call assert_equal(0, values(v:_null_dict)) - call assert_false(has_key(v:_null_dict, 'k')) - call assert_fails("let l = [] + v:_null_list", 'E15:') - call assert_fails("let l = v:_null_list + []", 'E15:') + call assert_equal(v:_null_dict, v:_null_dict) + let d = v:_null_dict + call assert_equal({}, d) + call assert_equal(0, len(d)) + call assert_equal(1, empty(d)) + call assert_equal(0, items(d)) + call assert_equal(0, keys(d)) + call assert_equal(0, values(d)) + call assert_false(has_key(d, 'k')) + call assert_equal('{}', string(d)) + call assert_fails('let x = v:_null_dict[10]') + call assert_equal({}, {}) endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_unlet.vim b/src/nvim/testdir/test_unlet.vim index acf98bb1fc..4779d17906 100644 --- a/src/nvim/testdir/test_unlet.vim +++ b/src/nvim/testdir/test_unlet.vim @@ -27,6 +27,7 @@ func Test_unlet_fails() call assert_fails("unlet l['k'", 'E111:') let d = {'k' : 1} call assert_fails("unlet d.k2", 'E716:') + call assert_fails("unlet {a};", 'E488:') endfunc func Test_unlet_env() diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim index a3070d6517..5b8b384bae 100644 --- a/src/nvim/testdir/test_usercommands.vim +++ b/src/nvim/testdir/test_usercommands.vim @@ -623,17 +623,17 @@ func Test_usercmd_custom() return "a\nb\n" endfunc command -nargs=* -complete=customlist,T1 TCmd1 - call feedkeys(":T1 \\\"\", 'xt') - call assert_equal('"T1 ', @:) + call feedkeys(":TCmd1 \\\"\", 'xt') + call assert_equal('"TCmd1 ', @:) delcommand TCmd1 delfunc T1 func T2(a, c, p) - return ['a', 'b', 'c'] + return {} endfunc command -nargs=* -complete=customlist,T2 TCmd2 - call feedkeys(":T2 \\\"\", 'xt') - call assert_equal('"T2 ', @:) + call feedkeys(":TCmd2 \\\"\", 'xt') + call assert_equal('"TCmd2 ', @:) delcommand TCmd2 delfunc T2 endfunc diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 3d3f0a8839..a9fd7a4bef 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1658,6 +1658,20 @@ func Test_compound_assignment_operators() call assert_fails('let x .= "f"', 'E734') let x = !3.14 call assert_equal(0.0, x) + + " integer and float operations + let x = 1 + let x *= 2.1 + call assert_equal(2.1, x) + let x = 1 + let x /= 0.25 + call assert_equal(4.0, x) + let x = 1 + call assert_fails('let x %= 0.25', 'E734:') + let x = 1 + call assert_fails('let x .= 0.25', 'E734:') + let x = 1.0 + call assert_fails('let x += [1.1]', 'E734:') endif " Test for environment variable @@ -1839,6 +1853,9 @@ func Test_missing_end() " Missing 'in' in a :for statement call assert_fails('for i range(1) | endfor', 'E690:') + + " Incorrect number of variables in for + call assert_fails('for [i,] in range(3) | endfor', 'E475:') endfunc " Test for deep nesting of if/for/while/try statements {{{1 -- cgit From 46a54dd6a03f51ba08142abe0aa5710705917987 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 26 Oct 2022 20:10:41 +0800 Subject: vim-patch:8.2.1852: map() returing zero for NULL list is unexpected Problem: map() returing zero for NULL list is unexpected. Solution: Return the empty list. (closes vim/vim#7133) https://github.com/vim/vim/commit/ffdf8adfa8108d4765fdc68abbd2fe49a4292b25 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 6 +++--- src/nvim/testdir/test_blob.vim | 1 + src/nvim/testdir/test_filter_map.vim | 6 ++++-- 3 files changed, 8 insertions(+), 5 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ffbe66aa25..8bd91ec9a2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4786,20 +4786,20 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) int save_did_emsg; int idx = 0; + // Always return the first argument, also on failure. + tv_copy(&argvars[0], rettv); + if (argvars[0].v_type == VAR_BLOB) { - tv_copy(&argvars[0], rettv); if ((b = argvars[0].vval.v_blob) == NULL) { return; } } else if (argvars[0].v_type == VAR_LIST) { - tv_copy(&argvars[0], rettv); if ((l = argvars[0].vval.v_list) == NULL || (!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 && var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { return; diff --git a/src/nvim/testdir/test_blob.vim b/src/nvim/testdir/test_blob.vim index c5a217f36f..770b2d16ef 100644 --- a/src/nvim/testdir/test_blob.vim +++ b/src/nvim/testdir/test_blob.vim @@ -274,6 +274,7 @@ endfunc " filter() item in blob func Test_blob_filter() + call assert_equal(v:_null_blob, filter(v:_null_blob, '0')) call assert_equal(0z, filter(0zDEADBEEF, '0')) call assert_equal(0zADBEEF, filter(0zDEADBEEF, 'v:val != 0xDE')) call assert_equal(0zDEADEF, filter(0zDEADBEEF, 'v:val != 0xBE')) diff --git a/src/nvim/testdir/test_filter_map.vim b/src/nvim/testdir/test_filter_map.vim index ebcb6699c6..c75177ea39 100644 --- a/src/nvim/testdir/test_filter_map.vim +++ b/src/nvim/testdir/test_filter_map.vim @@ -89,8 +89,10 @@ func Test_map_filter_fails() call assert_fails("let l = filter([1, 2, 3], '{}')", 'E728:') call assert_fails("let l = filter({'k' : 10}, '{}')", 'E728:') call assert_fails("let l = filter([1, 2], {})", 'E731:') - call assert_equal(0, map(v:_null_list, '"> " .. v:val')) - call assert_equal(0, map(v:_null_dict, '"> " .. v:val')) + call assert_equal(v:_null_list, filter(v:_null_list, 0)) + call assert_equal(v:_null_dict, filter(v:_null_dict, 0)) + call assert_equal(v:_null_list, map(v:_null_list, '"> " .. v:val')) + call assert_equal(v:_null_dict, map(v:_null_dict, '"> " .. v:val')) endfunc func Test_map_and_modify() -- cgit From 157baef02636fed4a99ef401e470fee8c51ce852 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 26 Oct 2022 21:53:11 +0800 Subject: vim-patch:8.1.1826: tests use hand coded feature and option checks Problem: Tests use hand coded feature and option checks. Solution: Use the commands from check.vim in more tests. https://github.com/vim/vim/commit/8c5a278fc508da6dfe50e69b6ee734451aa4eafb Omit Test_wincolor(): there are later patches that touch that function. Omit test_memory_usage.vim: a Lua test is used for that file. Cherry-pick Test_issue_3969() from patch 8.1.0969. Co-authored-by: Bram Moolenaar --- src/nvim/testdir/check.vim | 16 +++++++++++++++ src/nvim/testdir/test_breakindent.vim | 5 ++--- src/nvim/testdir/test_bufline.vim | 6 +++--- src/nvim/testdir/test_fold.vim | 5 ++--- src/nvim/testdir/test_functions.vim | 5 ++--- src/nvim/testdir/test_highlight.vim | 8 ++------ src/nvim/testdir/test_mapping.vim | 4 +++- src/nvim/testdir/test_match.vim | 5 ++--- src/nvim/testdir/test_options.vim | 5 ++--- src/nvim/testdir/test_popup.vim | 12 ++++++------ src/nvim/testdir/test_signals.vim | 5 +++-- src/nvim/testdir/test_startup.vim | 37 ++++++++++++++++++++--------------- src/nvim/testdir/test_syntax.vim | 5 ++--- src/nvim/testdir/test_timers.vim | 9 ++++----- src/nvim/testdir/test_vimscript.vim | 5 +---- 15 files changed, 71 insertions(+), 61 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim index 4107df99d6..92a51d4371 100644 --- a/src/nvim/testdir/check.vim +++ b/src/nvim/testdir/check.vim @@ -39,6 +39,22 @@ func CheckFunction(name) endif endfunc +" Command to check for the presence of an Ex command +command -nargs=1 CheckCommand call CheckCommand() +func CheckCommand(name) + if !exists(':' .. a:name) + throw 'Skipped: ' .. a:name .. ' command not supported' + endif +endfunc + +" Command to check for the presence of a shell command +command -nargs=1 CheckExecutable call CheckExecutable() +func CheckExecutable(name) + if !executable(a:name) + throw 'Skipped: ' .. a:name .. ' program not executable' + endif +endfunc + " Command to check for the presence of python. Argument should have been " obtained with PythonProg() func CheckPython(name) diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim index a37751e748..ed4d886fd1 100644 --- a/src/nvim/testdir/test_breakindent.vim +++ b/src/nvim/testdir/test_breakindent.vim @@ -4,9 +4,8 @@ " while the test is run, the breakindent caching gets in its way. " It helps to change the tabstop setting and force a redraw (e.g. see " Test_breakindent08()) -if !exists('+breakindent') - throw 'Skipped: breakindent option not supported' -endif +source check.vim +CheckOption breakindent source view_util.vim diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim index 3b5bcbce89..8f853fe44e 100644 --- a/src/nvim/testdir/test_bufline.vim +++ b/src/nvim/testdir/test_bufline.vim @@ -2,6 +2,7 @@ source shared.vim source screendump.vim +source check.vim func Test_setbufline_getbufline() new @@ -130,9 +131,8 @@ func Test_deletebufline() endfunc func Test_appendbufline_redraw() - if !CanRunVimInTerminal() - throw 'Skipped: cannot make screendumps' - endif + CheckScreendump + let lines =<< trim END new foo let winnr = 'foo'->bufwinnr() diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index 327f0f73f2..0a9be310ff 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -1,5 +1,6 @@ " Test for folding +source check.vim source view_util.vim source screendump.vim @@ -727,9 +728,7 @@ func Test_fold_last_line_with_pagedown() endfunc func Test_folds_with_rnu() - if !CanRunVimInTerminal() - throw 'Skipped: cannot make screendumps' - endif + CheckScreendump call writefile([ \ 'set fdm=marker rnu foldcolumn=2', diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 8f2a61e399..6ef20107d8 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1745,9 +1745,8 @@ endfunc func Test_confirm() " requires a UI to be active throw 'Skipped: use test/functional/vimscript/input_spec.lua' - if !has('unix') || has('gui_running') - return - endif + CheckUnix + CheckNotGui call feedkeys('o', 'L') let a = confirm('Press O to proceed') diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim index e84c45c635..2be82f4e3c 100644 --- a/src/nvim/testdir/test_highlight.vim +++ b/src/nvim/testdir/test_highlight.vim @@ -536,9 +536,7 @@ func Test_termguicolors() endfunc func Test_cursorline_after_yank() - if !CanRunVimInTerminal() - throw 'Skipped: cannot make screendumps' - endif + CheckScreendump call writefile([ \ 'set cul rnu', @@ -578,9 +576,7 @@ func Test_put_before_cursorline() endfunc func Test_cursorline_with_visualmode() - if !CanRunVimInTerminal() - throw 'Skipped: cannot make screendumps' - endif + CheckScreendump call writefile([ \ 'set cul', diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim index bde3624adf..2d8c45210b 100644 --- a/src/nvim/testdir/test_mapping.vim +++ b/src/nvim/testdir/test_mapping.vim @@ -395,7 +395,9 @@ func Test_motionforce_omap() endfunc func Test_error_in_map_expr() - if !has('terminal') || (has('win32') && has('gui_running')) + " Unlike CheckRunVimInTerminal this does work in a win32 console + CheckFeature terminal + if has('win32') && has('gui_running') throw 'Skipped: cannot run Vim in a terminal window' endif diff --git a/src/nvim/testdir/test_match.vim b/src/nvim/testdir/test_match.vim index 4f22e54563..fe931fefb2 100644 --- a/src/nvim/testdir/test_match.vim +++ b/src/nvim/testdir/test_match.vim @@ -322,9 +322,8 @@ func OtherWindowCommon() endfunc func Test_matchdelete_other_window() - if !CanRunVimInTerminal() - throw 'Skipped: cannot make screendumps' - endif + CheckScreendump + let buf = OtherWindowCommon() call term_sendkeys(buf, ":call matchdelete(mid, winid)\") call VerifyScreenDump(buf, 'Test_matchdelete_1', {}) diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index ada6d2406b..8fc4968ad9 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -432,9 +432,8 @@ endfunc " Must be executed before other tests that set 'term'. func Test_000_term_option_verbose() - if has('nvim') || has('gui_running') - return - endif + throw "Skipped: Nvim does not support setting 'term'" + CheckNotGui call CheckWasNotSet('t_cm') diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim index 067e5d14e5..7f183f0849 100644 --- a/src/nvim/testdir/test_popup.vim +++ b/src/nvim/testdir/test_popup.vim @@ -677,9 +677,9 @@ func Test_complete_CTRLN_startofbuffer() endfunc func Test_popup_and_window_resize() - if !has('terminal') || has('gui_running') - return - endif + CheckFeature terminal + CheckNotGui + let h = winheight(0) if h < 15 return @@ -948,9 +948,9 @@ func Test_complete_o_tab() endfunc func Test_menu_only_exists_in_terminal() - if !exists(':tlmenu') || has('gui_running') - return - endif + CheckCommand tlmenu + CheckNotGui + tlnoremenu &Edit.&Paste"+gP "+ aunmenu * try diff --git a/src/nvim/testdir/test_signals.vim b/src/nvim/testdir/test_signals.vim index 338c0d79ff..719f90c808 100644 --- a/src/nvim/testdir/test_signals.vim +++ b/src/nvim/testdir/test_signals.vim @@ -16,8 +16,9 @@ endfunc " Test signal WINCH (window resize signal) func Test_signal_WINCH() throw 'skipped: Nvim cannot avoid terminal resize' - if has('gui_running') || !HasSignal('WINCH') - return + CheckNotGui + if !HasSignal('WINCH') + throw 'Skipped: WINCH signal not supported' endif " We do not actually want to change the size of the terminal. diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim index b30a5e7edb..e267d3f972 100644 --- a/src/nvim/testdir/test_startup.vim +++ b/src/nvim/testdir/test_startup.vim @@ -267,10 +267,9 @@ endfunc " Test the -V[N] argument to set the 'verbose' option to [N] func Test_V_arg() - if has('gui_running') - " Can't catch the output of gvim. - return - endif + " Can't catch the output of gvim. + CheckNotGui + let out = system(GetVimCommand() . ' --clean -es -X -V0 -c "set verbose?" -cq') call assert_equal(" verbose=0\n", out) @@ -543,10 +542,9 @@ endfunc func Test_invalid_args() - if !has('unix') || has('gui_running') - " can't get output of Vim. - return - endif + " must be able to get the output of Vim. + CheckUnix + CheckNotGui for opt in ['-Y', '--does-not-exist'] let out = split(system(GetVimCommand() .. ' ' .. opt), "\n") @@ -747,10 +745,9 @@ func Test_progpath() endfunc func Test_silent_ex_mode() - if !has('unix') || has('gui_running') - " can't get output of Vim. - return - endif + " must be able to get the output of Vim. + CheckUnix + CheckNotGui " This caused an ml_get error. let out = system(GetVimCommand() . ' -u NONE -es -c''set verbose=1|h|exe "%norm\\"'' -c cq') @@ -758,10 +755,9 @@ func Test_silent_ex_mode() endfunc func Test_default_term() - if !has('unix') || has('gui_running') - " can't get output of Vim. - return - endif + " must be able to get the output of Vim. + CheckUnix + CheckNotGui let save_term = $TERM let $TERM = 'unknownxxx' @@ -796,6 +792,15 @@ func Test_zzz_startinsert() call delete('Xtestout') endfunc +func Test_issue_3969() + " Can't catch the output of gvim. + CheckNotGui + + " Check that message is not truncated. + let out = system(GetVimCommand() . ' -es -X -V1 -c "echon ''hello''" -cq') + call assert_equal('hello', out) +endfunc + func Test_start_with_tabs() if !CanRunVimInTerminal() return diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 9b4293764d..1402f0b9ad 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -489,9 +489,8 @@ func Test_conceal() endfunc func Test_bg_detection() - if has('gui_running') - return - endif + CheckNotGui + " auto-detection of &bg, make sure sure it isn't set anywhere before " this test hi Normal ctermbg=0 diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index 771f61442d..135bd2bcdd 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -263,9 +263,9 @@ func Interrupt(timer) endfunc func Test_timer_peek_and_get_char() - if !has('unix') && !has('gui_running') - return - endif + CheckUnix + CheckGui + call timer_start(0, 'FeedAndPeek') let intr = timer_start(100, 'Interrupt') let c = getchar() @@ -275,8 +275,7 @@ endfunc func Test_timer_getchar_zero() if has('win32') && !has('gui_running') - " Console: no low-level input - return + throw 'Skipped: cannot get low-level input' endif " Measure the elapsed time to avoid a hang when it fails. diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index a9fd7a4bef..c47c3672c4 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1747,10 +1747,7 @@ func Test_funccall_garbage_collect() endfunc func Test_function_defined_line() - if has('gui_running') - " Can't catch the output of gvim. - return - endif + CheckNotGui let lines =<< trim [CODE] " F1 -- cgit From 8f8205ffe43541ccd475835a06fc89c8d19299f5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 26 Oct 2022 22:13:12 +0800 Subject: vim-patch:9.0.0019: timers test not run where possible Problem: Timers test not run where possible. Solution: Adjust platform checks. (closes vim/vim#10645) https://github.com/vim/vim/commit/eb273cd7b036c35ae9070bd6352101914f273e71 Cherry-pick a line from patch 8.2.0183. --- src/nvim/testdir/test_timers.vim | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index 135bd2bcdd..3a6abb3968 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -263,8 +263,9 @@ func Interrupt(timer) endfunc func Test_timer_peek_and_get_char() - CheckUnix - CheckGui + if !has('unix') && !has('gui_running') + throw 'Skipped: cannot feed low-level input' + endif call timer_start(0, 'FeedAndPeek') let intr = timer_start(100, 'Interrupt') @@ -275,8 +276,9 @@ endfunc func Test_timer_getchar_zero() if has('win32') && !has('gui_running') - throw 'Skipped: cannot get low-level input' + throw 'Skipped: cannot feed low-level input' endif + CheckFunction reltimefloat " Measure the elapsed time to avoid a hang when it fails. let start = reltime() -- cgit From 5559cabf4d851edb94cc2f09d50bcf0a04bd0819 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 26 Oct 2022 22:38:47 +0800 Subject: vim-patch:8.2.0531: various errors not tested Problem: Various errors not tested. Solution: Add tests. (Yegappan Lakshmanan, closes vim/vim#5895) https://github.com/vim/vim/commit/476a613135bdc94e61c1dce8a9cbb4ab0b6dc2d1 Need to remove "F" flag from 'shortmess' as early as possible. --- src/nvim/testdir/Makefile | 2 +- src/nvim/testdir/test_source.vim | 6 + src/nvim/testdir/test_syntax.vim | 58 ++++++++ src/nvim/testdir/test_user_func.vim | 256 ++++++++++++++++++++++++++++++++++++ src/nvim/testdir/test_vimscript.vim | 160 ---------------------- 5 files changed, 321 insertions(+), 161 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 4641408069..a6d1cf1003 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -179,4 +179,4 @@ newtestssilent: $(NEW_TESTS_RES) @echo "[OLDTEST] Running" $* @rm -rf $*.failed test.ok $(RM_ON_RUN) @mkdir -p $(TMPDIR) - @/bin/sh runnvim.sh $(ROOT) $(NVIM_PRG) $* $(RUN_VIMTEST) $(NO_INITS) -u NONE -S runtest.vim $*.vim + @/bin/sh runnvim.sh $(ROOT) $(NVIM_PRG) $* $(RUN_VIMTEST) $(NO_INITS) -u NONE --cmd "set shortmess-=F" -S runtest.vim $*.vim diff --git a/src/nvim/testdir/test_source.vim b/src/nvim/testdir/test_source.vim index ba6fd5ad95..0fd923abf2 100644 --- a/src/nvim/testdir/test_source.vim +++ b/src/nvim/testdir/test_source.vim @@ -87,4 +87,10 @@ func Test_source_autocmd_sfile() call delete('Xscript.vim') endfunc +func Test_source_error() + call assert_fails('scriptencoding utf-8', 'E167:') + call assert_fails('finish', 'E168:') + " call assert_fails('scriptversion 2', 'E984:') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 1402f0b9ad..52510843a3 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -363,6 +363,64 @@ func Test_syntax_invalid_arg() call assert_fails('syntax sync x', 'E404:') call assert_fails('syntax keyword Abc a[', 'E789:') call assert_fails('syntax keyword Abc a[bc]d', 'E890:') + + let caught_393 = 0 + try + syntax keyword cMyItem grouphere G1 + catch /E393:/ + let caught_393 = 1 + endtry + call assert_equal(1, caught_393) + + let caught_394 = 0 + try + syntax sync match Abc grouphere MyItem "abc"' + catch /E394:/ + let caught_394 = 1 + endtry + call assert_equal(1, caught_394) + + " Test for too many \z\( and unmatched \z\( + " Not able to use assert_fails() here because both E50:/E879: and E475: + " messages are emitted. + set regexpengine=1 + let caught_52 = 0 + try + syntax region MyRegion start='\z\(' end='\*/' + catch /E52:/ + let caught_52 = 1 + endtry + call assert_equal(1, caught_52) + + let caught_50 = 0 + try + let cmd = "syntax region MyRegion start='" + let cmd ..= repeat("\\z\\(.\\)", 10) .. "' end='\*/'" + exe cmd + catch /E50:/ + let caught_50 = 1 + endtry + call assert_equal(1, caught_50) + + set regexpengine=2 + let caught_54 = 0 + try + syntax region MyRegion start='\z\(' end='\*/' + catch /E54:/ + let caught_54 = 1 + endtry + call assert_equal(1, caught_54) + + let caught_879 = 0 + try + let cmd = "syntax region MyRegion start='" + let cmd ..= repeat("\\z\\(.\\)", 10) .. "' end='\*/'" + exe cmd + catch /E879:/ + let caught_879 = 1 + endtry + call assert_equal(1, caught_879) + set regexpengine& endfunc func Test_syn_sync() diff --git a/src/nvim/testdir/test_user_func.vim b/src/nvim/testdir/test_user_func.vim index 13a0334cd3..c517d1133a 100644 --- a/src/nvim/testdir/test_user_func.vim +++ b/src/nvim/testdir/test_user_func.vim @@ -3,6 +3,9 @@ " Also test that a builtin function cannot be replaced. " Also test for regression when calling arbitrary expression. +source check.vim +source shared.vim + func Table(title, ...) let ret = a:title let idx = 1 @@ -83,6 +86,7 @@ func Test_user_func() normal o[(one again call assert_equal('1. one again', getline('.')) + " Try to overwrite a function in the global (g:) scope call assert_equal(3, max([1, 2, 3])) call assert_fails("call extend(g:, {'max': function('min')})", 'E704') call assert_equal(3, max([1, 2, 3])) @@ -178,4 +182,256 @@ func Test_function_list() call assert_fails("function Xabc", 'E123:') endfunc +" Test for , in a function +func Test_sfile_in_function() + func Xfunc() + call assert_match('..Test_sfile_in_function\[5]..Xfunc', expand('')) + call assert_equal('2', expand('')) + endfunc + call Xfunc() + delfunc Xfunc +endfunc + +" Test trailing text after :endfunction {{{1 +func Test_endfunction_trailing() + call assert_false(exists('*Xtest')) + + exe "func Xtest()\necho 'hello'\nendfunc\nlet done = 'yes'" + call assert_true(exists('*Xtest')) + call assert_equal('yes', done) + delfunc Xtest + unlet done + + exe "func Xtest()\necho 'hello'\nendfunc|let done = 'yes'" + call assert_true(exists('*Xtest')) + call assert_equal('yes', done) + delfunc Xtest + unlet done + + " trailing line break + exe "func Xtest()\necho 'hello'\nendfunc\n" + call assert_true(exists('*Xtest')) + delfunc Xtest + + set verbose=1 + exe "func Xtest()\necho 'hello'\nendfunc \" garbage" + call assert_notmatch('W22:', split(execute('1messages'), "\n")[0]) + call assert_true(exists('*Xtest')) + delfunc Xtest + + exe "func Xtest()\necho 'hello'\nendfunc garbage" + call assert_match('W22:', split(execute('1messages'), "\n")[0]) + call assert_true(exists('*Xtest')) + delfunc Xtest + set verbose=0 + + function Foo() + echo 'hello' + endfunction | echo 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' + delfunc Foo +endfunc + +func Test_delfunction_force() + delfunc! Xtest + delfunc! Xtest + func Xtest() + echo 'nothing' + endfunc + delfunc! Xtest + delfunc! Xtest + + " Try deleting the current function + call assert_fails('delfunc Test_delfunction_force', 'E131:') +endfunc + +func Test_function_defined_line() + CheckNotGui + + let lines =<< trim [CODE] + " F1 + func F1() + " F2 + func F2() + " + " + " + return + endfunc + " F3 + execute "func F3()\n\n\n\nreturn\nendfunc" + " F4 + execute "func F4()\n + \\n + \\n + \\n + \return\n + \endfunc" + endfunc + " F5 + execute "func F5()\n\n\n\nreturn\nendfunc" + " F6 + execute "func F6()\n + \\n + \\n + \\n + \return\n + \endfunc" + call F1() + verbose func F1 + verbose func F2 + verbose func F3 + verbose func F4 + verbose func F5 + verbose func F6 + qall! + [CODE] + + call writefile(lines, 'Xtest.vim') + let res = system(GetVimCommandClean() .. ' -es -X -S Xtest.vim') + call assert_equal(0, v:shell_error) + + let m = matchstr(res, 'function F1()[^[:print:]]*[[:print:]]*') + call assert_match(' line 2$', m) + + let m = matchstr(res, 'function F2()[^[:print:]]*[[:print:]]*') + call assert_match(' line 4$', m) + + let m = matchstr(res, 'function F3()[^[:print:]]*[[:print:]]*') + call assert_match(' line 11$', m) + + let m = matchstr(res, 'function F4()[^[:print:]]*[[:print:]]*') + call assert_match(' line 13$', m) + + let m = matchstr(res, 'function F5()[^[:print:]]*[[:print:]]*') + call assert_match(' line 21$', m) + + let m = matchstr(res, 'function F6()[^[:print:]]*[[:print:]]*') + call assert_match(' line 23$', m) + + call delete('Xtest.vim') +endfunc + +" Test for defining a function reference in the global scope +func Test_add_funcref_to_global_scope() + let x = g: + let caught_E862 = 0 + try + func x.Xfunc() + return 1 + endfunc + catch /E862:/ + let caught_E862 = 1 + endtry + call assert_equal(1, caught_E862) +endfunc + +func Test_funccall_garbage_collect() + func Func(x, ...) + call add(a:x, a:000) + endfunc + call Func([], []) + " Must not crash cause by invalid freeing + call test_garbagecollect_now() + call assert_true(v:true) + delfunc Func +endfunc + +" Test for script-local function +func DoLast() + call append(line('$'), "last line") +endfunc + +func s:DoNothing() + call append(line('$'), "nothing line") +endfunc + +func Test_script_local_func() + set nocp nomore viminfo+=nviminfo + new + nnoremap _x :call DoNothing()call DoLast()delfunc DoNothingdelfunc DoLast + + normal _x + call assert_equal('nothing line', getline(2)) + call assert_equal('last line', getline(3)) + close! + + " Try to call a script local function in global scope + let lines =<< trim [CODE] + :call assert_fails('call s:Xfunc()', 'E81:') + :call assert_fails('let x = call("Xfunc", [])', 'E120:') + :call writefile(v:errors, 'Xresult') + :qall + + [CODE] + call writefile(lines, 'Xscript') + if RunVim([], [], '-s Xscript') + call assert_equal([], readfile('Xresult')) + endif + call delete('Xresult') + call delete('Xscript') +endfunc + +" Test for errors in defining new functions +func Test_func_def_error() + call assert_fails('func Xfunc abc ()', 'E124:') + call assert_fails('func Xfunc(', 'E125:') + call assert_fails('func xfunc()', 'E128:') + + " Try to redefine a function that is in use + let caught_E127 = 0 + try + func! Test_func_def_error() + endfunc + catch /E127:/ + let caught_E127 = 1 + endtry + call assert_equal(1, caught_E127) + + " Try to define a function in a dict twice + let d = {} + let lines =<< trim END + func d.F1() + return 1 + endfunc + END + let l = join(lines, "\n") . "\n" + exe l + call assert_fails('exe l', 'E717:') + + " Define an autoload function with an incorrect file name + call writefile(['func foo#Bar()', 'return 1', 'endfunc'], 'Xscript') + call assert_fails('source Xscript', 'E746:') + call delete('Xscript') +endfunc + +" Test for deleting a function +func Test_del_func() + call assert_fails('delfunction Xabc', 'E130:') + let d = {'a' : 10} + call assert_fails('delfunc d.a', 'E718:') +endfunc + +" Test for calling return outside of a function +func Test_return_outside_func() + call writefile(['return 10'], 'Xscript') + call assert_fails('source Xscript', 'E133:') + call delete('Xscript') +endfunc + +" Test for errors in calling a function +func Test_func_arg_error() + " Too many arguments + call assert_fails("call call('min', range(1,20))", 'E118:') + call assert_fails("call call('min', range(1,21))", 'E699:') + call assert_fails('echo min(0,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,0,1)', + \ 'E740:') + + " Missing dict argument + func Xfunc() dict + return 1 + endfunc + call assert_fails('call Xfunc()', 'E725:') + delfunc Xfunc +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index c47c3672c4..f20fd12ed3 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1495,58 +1495,6 @@ func Test_bitwise_functions() call assert_fails("call invert({})", 'E728:') endfunc -" Test trailing text after :endfunction {{{1 -func Test_endfunction_trailing() - call assert_false(exists('*Xtest')) - - exe "func Xtest()\necho 'hello'\nendfunc\nlet done = 'yes'" - call assert_true(exists('*Xtest')) - call assert_equal('yes', done) - delfunc Xtest - unlet done - - exe "func Xtest()\necho 'hello'\nendfunc|let done = 'yes'" - call assert_true(exists('*Xtest')) - call assert_equal('yes', done) - delfunc Xtest - unlet done - - " trailing line break - exe "func Xtest()\necho 'hello'\nendfunc\n" - call assert_true(exists('*Xtest')) - delfunc Xtest - - set verbose=1 - exe "func Xtest()\necho 'hello'\nendfunc \" garbage" - call assert_notmatch('W22:', split(execute('1messages'), "\n")[0]) - call assert_true(exists('*Xtest')) - delfunc Xtest - - exe "func Xtest()\necho 'hello'\nendfunc garbage" - call assert_match('W22:', split(execute('1messages'), "\n")[0]) - call assert_true(exists('*Xtest')) - delfunc Xtest - set verbose=0 - - function Foo() - echo 'hello' - endfunction | echo 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' - delfunc Foo -endfunc - -func Test_delfunction_force() - delfunc! Xtest - delfunc! Xtest - func Xtest() - echo 'nothing' - endfunc - delfunc! Xtest - delfunc! Xtest - - " Try deleting the current function - call assert_fails('delfunc Test_delfunction_force', 'E131:') -endfunc - " Test using bang after user command {{{1 func Test_user_command_with_bang() command -bang Nieuw let nieuw = 1 @@ -1556,26 +1504,6 @@ func Test_user_command_with_bang() delcommand Nieuw endfunc -" Test for script-local function -func DoLast() - call append(line('$'), "last line") -endfunc - -func s:DoNothing() - call append(line('$'), "nothing line") -endfunc - -func Test_script_local_func() - set nocp nomore viminfo+=nviminfo - new - nnoremap _x :call DoNothing()call DoLast()delfunc DoNothingdelfunc DoLast - - normal _x - call assert_equal('nothing line', getline(2)) - call assert_equal('last line', getline(3)) - enew! | close -endfunc - func Test_script_expand_sfile() let lines =<< trim END func s:snr() @@ -1735,84 +1663,6 @@ func Test_unlet_env() call assert_equal('', $TESTVAR) endfunc -func Test_funccall_garbage_collect() - func Func(x, ...) - call add(a:x, a:000) - endfunc - call Func([], []) - " Must not crash cause by invalid freeing - call test_garbagecollect_now() - call assert_true(v:true) - delfunc Func -endfunc - -func Test_function_defined_line() - CheckNotGui - - let lines =<< trim [CODE] - " F1 - func F1() - " F2 - func F2() - " - " - " - return - endfunc - " F3 - execute "func F3()\n\n\n\nreturn\nendfunc" - " F4 - execute "func F4()\n - \\n - \\n - \\n - \return\n - \endfunc" - endfunc - " F5 - execute "func F5()\n\n\n\nreturn\nendfunc" - " F6 - execute "func F6()\n - \\n - \\n - \\n - \return\n - \endfunc" - call F1() - verbose func F1 - verbose func F2 - verbose func F3 - verbose func F4 - verbose func F5 - verbose func F6 - qall! - [CODE] - - call writefile(lines, 'Xtest.vim') - let res = system(GetVimCommandClean() .. ' -es -X -S Xtest.vim') - call assert_equal(0, v:shell_error) - - let m = matchstr(res, 'function F1()[^[:print:]]*[[:print:]]*') - call assert_match(' line 2$', m) - - let m = matchstr(res, 'function F2()[^[:print:]]*[[:print:]]*') - call assert_match(' line 4$', m) - - let m = matchstr(res, 'function F3()[^[:print:]]*[[:print:]]*') - call assert_match(' line 11$', m) - - let m = matchstr(res, 'function F4()[^[:print:]]*[[:print:]]*') - call assert_match(' line 13$', m) - - let m = matchstr(res, 'function F5()[^[:print:]]*[[:print:]]*') - call assert_match(' line 21$', m) - - let m = matchstr(res, 'function F6()[^[:print:]]*[[:print:]]*') - call assert_match(' line 23$', m) - - call delete('Xtest.vim') -endfunc - " Test for missing :endif, :endfor, :endwhile and :endtry {{{1 func Test_missing_end() call writefile(['if 2 > 1', 'echo ">"'], 'Xscript') @@ -1948,16 +1798,6 @@ func Test_deep_nest() call delete('Xscript') endfunc -" Test for , in a function {{{1 -func Test_sfile_in_function() - func Xfunc() - call assert_match('..Test_sfile_in_function\[5]..Xfunc', expand('')) - call assert_equal('2', expand('')) - endfunc - call Xfunc() - delfunc Xfunc -endfunc - " Test for errors in converting to float from various types {{{1 func Test_float_conversion_errors() if has('float') -- cgit From 31758d032ffeaf746eaae99f79c6b4e4e35dfebe Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 26 Oct 2022 23:22:37 +0800 Subject: vim-patch:8.2.0534: client-server test fails under valgrind Problem: Client-server test fails under valgrind. Solution: Use WaitForAssert(). https://github.com/vim/vim/commit/25d57009520f0e590920b9f953b1cbbb358e72a2 Co-authored-by: Bram Moolenaar --- src/nvim/testdir/test_clientserver.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_clientserver.vim b/src/nvim/testdir/test_clientserver.vim index a4ebce5af9..943f79d98f 100644 --- a/src/nvim/testdir/test_clientserver.vim +++ b/src/nvim/testdir/test_clientserver.vim @@ -147,7 +147,7 @@ func Test_client_server() " Edit files in separate tab pages call system(cmd .. ' --remote-tab Xfile1 Xfile2 Xfile3') - call assert_equal('3', remote_expr(name, 'tabpagenr("$")')) + call WaitForAssert({-> assert_equal('3', remote_expr(name, 'tabpagenr("$")'))}) call assert_equal('Xfile2', remote_expr(name, 'bufname(tabpagebuflist(2)[0])')) eval name->remote_send(":%bw!\") -- cgit From 27436b733fb1bc60e78f7fb5a0274b54b87fb04e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 26 Oct 2022 23:24:43 +0800 Subject: vim-patch:8.2.0606: several syntax HL errors not checked Problem: Several syntax HL errors not checked. Solution: Add tests. (Yegappan Lakshmanan, closes vim/vim#5954) https://github.com/vim/vim/commit/fbf2122cf920a89274ffbefaaeb6c5eeacf5187b --- src/nvim/testdir/test_syntax.vim | 95 +++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 50 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 52510843a3..2a173916d0 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -351,6 +351,18 @@ func Test_syntax_arg_skipped() syn clear endfunc +" Check for an error. Used when multiple errors are thrown and we are checking +" for an earliest error. +func AssertFails(cmd, errcode) + let save_exception = '' + try + exe a:cmd + catch + let save_exception = v:exception + endtry + call assert_match(a:errcode, save_exception) +endfunc + func Test_syntax_invalid_arg() call assert_fails('syntax case asdf', 'E390:') if has('conceal') @@ -358,69 +370,49 @@ func Test_syntax_invalid_arg() endif call assert_fails('syntax spell asdf', 'E390:') call assert_fails('syntax clear @ABCD', 'E391:') - call assert_fails('syntax include @Xxx', 'E397:') - call assert_fails('syntax region X start="{"', 'E399:') + call assert_fails('syntax include random_file', 'E484:') + call assert_fails('syntax include ', 'E495:') call assert_fails('syntax sync x', 'E404:') call assert_fails('syntax keyword Abc a[', 'E789:') call assert_fails('syntax keyword Abc a[bc]d', 'E890:') - - let caught_393 = 0 - try - syntax keyword cMyItem grouphere G1 - catch /E393:/ - let caught_393 = 1 - endtry - call assert_equal(1, caught_393) - - let caught_394 = 0 - try - syntax sync match Abc grouphere MyItem "abc"' - catch /E394:/ - let caught_394 = 1 - endtry - call assert_equal(1, caught_394) + call assert_fails('syntax cluster Abc add=A add=', 'E475:') " Test for too many \z\( and unmatched \z\( " Not able to use assert_fails() here because both E50:/E879: and E475: " messages are emitted. set regexpengine=1 - let caught_52 = 0 - try - syntax region MyRegion start='\z\(' end='\*/' - catch /E52:/ - let caught_52 = 1 - endtry - call assert_equal(1, caught_52) + call AssertFails("syntax region MyRegion start='\\z\\(' end='\\*/'", 'E52:') - let caught_50 = 0 - try - let cmd = "syntax region MyRegion start='" - let cmd ..= repeat("\\z\\(.\\)", 10) .. "' end='\*/'" - exe cmd - catch /E50:/ - let caught_50 = 1 - endtry - call assert_equal(1, caught_50) + let cmd = "syntax region MyRegion start='" + let cmd ..= repeat("\\z\\(.\\)", 10) .. "' end='\*/'" + call AssertFails(cmd, 'E50:') set regexpengine=2 - let caught_54 = 0 - try - syntax region MyRegion start='\z\(' end='\*/' - catch /E54:/ - let caught_54 = 1 - endtry - call assert_equal(1, caught_54) + call AssertFails("syntax region MyRegion start='\\z\\(' end='\\*/'", 'E54:') - let caught_879 = 0 - try - let cmd = "syntax region MyRegion start='" - let cmd ..= repeat("\\z\\(.\\)", 10) .. "' end='\*/'" - exe cmd - catch /E879:/ - let caught_879 = 1 - endtry - call assert_equal(1, caught_879) + let cmd = "syntax region MyRegion start='" + let cmd ..= repeat("\\z\\(.\\)", 10) .. "' end='\*/'" + call AssertFails(cmd, 'E879:') set regexpengine& + + call AssertFails('syntax keyword cMyItem grouphere G1', 'E393:') + call AssertFails('syntax sync match Abc grouphere MyItem "abc"', 'E394:') + call AssertFails('syn keyword Type contains int', 'E395:') + call assert_fails('syntax include @Xxx', 'E397:') + call AssertFails('syntax region X start', 'E398:') + call assert_fails('syntax region X start="{"', 'E399:') + call AssertFails('syntax cluster contains=Abc', 'E400:') + call AssertFails("syntax match Character /'.'", 'E401:') + call AssertFails("syntax match Character /'.'/a", 'E402:') + call assert_fails('syntax sync linecont /pat', 'E404:') + call assert_fails('syntax sync linecont', 'E404:') + call assert_fails('syntax sync linecont /pat1/ linecont /pat2/', 'E403:') + call assert_fails('syntax sync minlines=a', 'E404:') + call AssertFails('syntax match ABC /x/ contains=', 'E406:') + call AssertFails("syntax match Character contains /'.'/", 'E405:') + call AssertFails('syntax match ccFoo "Foo" nextgroup=ALLBUT,F', 'E407:') + call AssertFails('syntax region Block start="{" contains=F,ALLBUT', 'E408:') + call AssertFails("syntax match Characters contains=a.*x /'.'/", 'E409:') endfunc func Test_syn_sync() @@ -448,6 +440,7 @@ func Test_syn_clear() hi clear Foo call assert_equal('Foo', synIDattr(hlID("Foo"), "name")) hi clear Bar + call assert_fails('syntax clear invalid_syngroup', 'E28:') endfunc func Test_invalid_name() @@ -541,6 +534,8 @@ func Test_conceal() call assert_match('16 ', ScreenLines(2, 7)[0]) call assert_equal([[0, '', 0], [1, '', 1], [1, '', 1], [1, '', 2], [1, '', 2], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)')) + call AssertFails("syntax match Entity '&' conceal cchar=\", 'E844:') + syn clear set conceallevel& bw! -- cgit From 514e6bf07b6b0fe08019906b56d1226a70d14119 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 26 Oct 2022 23:26:12 +0800 Subject: vim-patch:8.2.1113: no test for verbose output of :call Problem: No test for verbose output of :call. Solution: Add a test. https://github.com/vim/vim/commit/a0d072ef8203b225bd46bcd826cb3d2e3c3b941a Co-authored-by: Bram Moolenaar --- src/nvim/testdir/test_user_func.vim | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_user_func.vim b/src/nvim/testdir/test_user_func.vim index c517d1133a..8b5ee72bf1 100644 --- a/src/nvim/testdir/test_user_func.vim +++ b/src/nvim/testdir/test_user_func.vim @@ -225,6 +225,17 @@ func Test_endfunction_trailing() delfunc Xtest set verbose=0 + func Xtest(a1, a2) + echo a:a1 .. a:a2 + endfunc + set verbose=15 + redir @a + call Xtest(123, repeat('x', 100)) + redir END + call assert_match('calling Xtest(123, ''xxxxxxx.*x\.\.\.x.*xxxx'')', getreg('a')) + delfunc Xtest + set verbose=0 + function Foo() echo 'hello' endfunction | echo 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' -- cgit From 624f6a8ca012dd6d27f1e67ace6ef10d04c5625f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 27 Oct 2022 07:53:54 +0800 Subject: vim-patch:8.2.2837: various code lines not covered by tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Various code lines not covered by tests. Solution: Add test cases. (Dominique Pellé, closes vim/vim#8178) https://github.com/vim/vim/commit/6d37e8e3baafba460bd2d051170d213c1ba9a523 Co-authored-by: Dominique Pelle --- src/nvim/testdir/test_excmd.vim | 6 ++++++ src/nvim/testdir/test_functions.vim | 2 ++ src/nvim/testdir/test_options.vim | 29 +++++++++++++++++++++++++++++ src/nvim/testdir/test_startup.vim | 7 +++++++ src/nvim/testdir/test_syntax.vim | 3 +++ 5 files changed, 47 insertions(+) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim index acf23fbc3c..04ab8e288f 100644 --- a/src/nvim/testdir/test_excmd.vim +++ b/src/nvim/testdir/test_excmd.vim @@ -683,6 +683,12 @@ func Test_sandbox() sandbox call Sandbox_tests() endfunc +func Test_command_not_implemented_E319() + if !has('mzscheme') + call assert_fails('mzscheme', 'E319:') + endif +endfunc + func Test_not_break_expression_register() call setreg('=', '1+1') if 0 diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 6ef20107d8..1b9b9abd91 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1018,7 +1018,9 @@ func Test_charidx() call assert_equal(2, charidx(a, 4)) call assert_equal(3, charidx(a, 7)) call assert_equal(-1, charidx(a, 8)) + call assert_equal(-1, charidx(a, -1)) call assert_equal(-1, charidx('', 0)) + call assert_equal(-1, charidx(v:_null_string, 0)) " count composing characters call assert_equal(0, charidx(a, 0, 1)) diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 8fc4968ad9..2836e81c4a 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -1116,6 +1116,35 @@ func Test_opt_reset_scroll() call delete('Xscroll') endfunc +" Check that VIM_POSIX env variable influences default value of 'cpo' and 'shm' +func Test_VIM_POSIX() + throw 'Skipped: Nvim does not support $VIM_POSIX' + let saved_VIM_POSIX = getenv("VIM_POSIX") + + call setenv('VIM_POSIX', "1") + let after =<< trim [CODE] + call writefile([&cpo, &shm], 'X_VIM_POSIX') + qall + [CODE] + if RunVim([], after, '') + call assert_equal(['aAbBcCdDeEfFgHiIjJkKlLmMnoOpPqrRsStuvwWxXyZ$!%*-+<>#{|&/\.;', + \ 'AS'], readfile('X_VIM_POSIX')) + endif + + call setenv('VIM_POSIX', v:null) + let after =<< trim [CODE] + call writefile([&cpo, &shm], 'X_VIM_POSIX') + qall + [CODE] + if RunVim([], after, '') + call assert_equal(['aAbBcCdDeEfFgHiIjJkKlLmMnoOpPqrRsStuvwWxXyZ$!%*-+<>;', + \ 'S'], readfile('X_VIM_POSIX')) + endif + + call delete('X_VIM_POSIX') + call setenv('VIM_POSIX', saved_VIM_POSIX) +endfunc + " Test for setting an option to a Vi or Vim default func Test_opt_default() throw 'Skipped: Nvim has different defaults' diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim index e267d3f972..b8aad1bc46 100644 --- a/src/nvim/testdir/test_startup.vim +++ b/src/nvim/testdir/test_startup.vim @@ -620,6 +620,12 @@ func Test_invalid_args() endfor if has('gui_gtk') + let out = split(system(GetVimCommand() .. ' --socketid'), "\n") + call assert_equal(1, v:shell_error) + call assert_match('^VIM - Vi IMproved .* (.*)$', out[0]) + call assert_equal('Argument missing after: "--socketid"', out[1]) + call assert_equal('More info with: "vim -h"', out[2]) + for opt in ['--socketid x', '--socketid 0xg'] let out = split(system(GetVimCommand() .. ' ' .. opt), "\n") call assert_equal(1, v:shell_error) @@ -627,6 +633,7 @@ func Test_invalid_args() call assert_equal('Invalid argument for: "--socketid"', out[1]) call assert_equal('More info with: "vim -h"', out[2]) endfor + endif endfunc diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 2a173916d0..9d617e145c 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -113,6 +113,9 @@ func Test_syntime() let a = execute('syntime report') call assert_equal("\nNo Syntax items defined for this buffer", a) + let a = execute('syntime clear') + call assert_equal("\nNo Syntax items defined for this buffer", a) + view samples/memfile_test.c setfiletype cpp redraw -- cgit From c031547c8d39c5fc99c6f1fab31b50ba6fb32cbc Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 27 Oct 2022 07:58:51 +0800 Subject: vim-patch:8.2.3839: using \z() with \z1 not tested for syntax highlighting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Using \z() with \z1 not tested for syntax highlighting. Solution: Add a test. (Dominique Pellé, closes vim/vim#9365) https://github.com/vim/vim/commit/354b23a9f87fd8c5aec457d88320a0a5bce4b985 Co-authored-by: Dominique Pelle --- src/nvim/testdir/test_syntax.vim | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 9d617e145c..d686ad7e96 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -666,6 +666,24 @@ func Test_syntax_c() call delete('Xtest.c') endfun +" Test \z(...) along with \z1 +func Test_syn_zsub() + new + syntax on + call setline(1, 'xxx start foo xxx not end foo xxx end foo xxx') + let l:expected = ' ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ' + + for l:re in [0, 1, 2] + " Example taken from :help :syn-ext-match + syntax region Z start="start \z(\I\i*\)" skip="not end \z1" end="end \z1" + eval AssertHighlightGroups(1, 1, l:expected, 1, 'regexp=' .. l:re) + syntax clear Z + endfor + + set re& + bw! +endfunc + " Using \z() in a region with NFA failing should not crash. func Test_syn_wrong_z_one() new -- cgit From 245e6c5b305a0f32c39c88df8c0bf3c38505cc60 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 27 Oct 2022 08:25:28 +0800 Subject: test(old): test_lambda.vim garbagecollect() -> test_garbagecollect_now() --- src/nvim/testdir/test_lambda.vim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim index 997c3dcd3a..3c12c7b02e 100644 --- a/src/nvim/testdir/test_lambda.vim +++ b/src/nvim/testdir/test_lambda.vim @@ -126,7 +126,7 @@ func Test_lambda_closure_counter() endfunc let l:F = s:foo() - call garbagecollect() + call test_garbagecollect_now() call assert_equal(1, l:F()) call assert_equal(2, l:F()) call assert_equal(3, l:F()) @@ -209,9 +209,9 @@ func Test_lambda_circular_reference() endfunc call s:Foo() - call garbagecollect() + call test_garbagecollect_now() let i = 0 | while i < 10000 | call s:Foo() | let i+= 1 | endwhile - call garbagecollect() + call test_garbagecollect_now() endfunc func Test_lambda_combination() @@ -240,7 +240,7 @@ func Test_closure_counter() endfunc let l:F = s:foo() - call garbagecollect() + call test_garbagecollect_now() call assert_equal(1, l:F()) call assert_equal(2, l:F()) call assert_equal(3, l:F()) @@ -258,7 +258,7 @@ func Test_closure_unlet() endfunc call assert_false(has_key(s:foo(), 'x')) - call garbagecollect() + call test_garbagecollect_now() endfunc func LambdaFoo() @@ -295,7 +295,7 @@ func Test_named_function_closure() endfunc call Afoo() call assert_equal(14, s:Abar()) - call garbagecollect() + call test_garbagecollect_now() call assert_equal(14, s:Abar()) endfunc -- cgit From bd122494cc3012a4885a55663e7c158c7a402878 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 27 Oct 2022 08:26:09 +0800 Subject: vim-patch:8.2.2100: insufficient testing for function range and dict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Insufficient testing for function range and dict. Solution: Add a few tests. (Dominique Pellé, closes vim/vim#7428) https://github.com/vim/vim/commit/67322bf74a106b6476b093e75da87d61e2181b76 --- src/nvim/testdir/test_functions.vim | 1 + src/nvim/testdir/test_lambda.vim | 5 +++++ src/nvim/testdir/test_signals.vim | 3 +-- src/nvim/testdir/test_user_func.vim | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 1b9b9abd91..b751215b79 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1865,6 +1865,7 @@ func Test_call() let mydict = {'data': [0, 1, 2, 3], 'len': function("Mylen")} eval mydict.len->call([], mydict)->assert_equal(4) call assert_fails("call call('Mylen', [], 0)", 'E715:') + call assert_fails('call foo', 'E107:') endfunc func Test_char2nr() diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim index 3c12c7b02e..ce15243993 100644 --- a/src/nvim/testdir/test_lambda.vim +++ b/src/nvim/testdir/test_lambda.vim @@ -245,6 +245,11 @@ func Test_closure_counter() call assert_equal(2, l:F()) call assert_equal(3, l:F()) call assert_equal(4, l:F()) + + call assert_match("^\n function \\d\\+_bar() closure" + \ .. "\n1 let x += 1" + \ .. "\n2 return x" + \ .. "\n endfunction$", execute('func s:bar')) endfunc func Test_closure_unlet() diff --git a/src/nvim/testdir/test_signals.vim b/src/nvim/testdir/test_signals.vim index 719f90c808..e1c6e5d11f 100644 --- a/src/nvim/testdir/test_signals.vim +++ b/src/nvim/testdir/test_signals.vim @@ -129,8 +129,7 @@ func Test_deadly_signal_TERM() call assert_equal(['foo'], getline(1, '$')) let result = readfile('XautoOut') - call assert_match('VimLeavePre triggered', result[0]) - call assert_match('VimLeave triggered', result[1]) + call assert_equal(["VimLeavePre triggered", "VimLeave triggered"], result) %bwipe! call delete('.Xsig_TERM.swp') diff --git a/src/nvim/testdir/test_user_func.vim b/src/nvim/testdir/test_user_func.vim index 8b5ee72bf1..5041fa9ad4 100644 --- a/src/nvim/testdir/test_user_func.vim +++ b/src/nvim/testdir/test_user_func.vim @@ -445,4 +445,36 @@ func Test_func_arg_error() delfunc Xfunc endfunc +func Test_func_dict() + let mydict = {'a': 'b'} + function mydict.somefunc() dict + return len(self) + endfunc + + call assert_equal("{'a': 'b', 'somefunc': function('2')}", string(mydict)) + call assert_equal(2, mydict.somefunc()) + call assert_match("^\n function \\d\\\+() dict" + \ .. "\n1 return len(self)" + \ .. "\n endfunction$", execute('func mydict.somefunc')) +endfunc + +func Test_func_range() + new + call setline(1, range(1, 8)) + func FuncRange() range + echo a:firstline + echo a:lastline + endfunc + 3 + call assert_equal("\n3\n3", execute('call FuncRange()')) + call assert_equal("\n4\n6", execute('4,6 call FuncRange()')) + call assert_equal("\n function FuncRange() range" + \ .. "\n1 echo a:firstline" + \ .. "\n2 echo a:lastline" + \ .. "\n endfunction", + \ execute('function FuncRange')) + + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 1f438b23381cf79e9c6fbdfa69dfeff9ecbce3e6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 27 Oct 2022 08:30:27 +0800 Subject: vim-patch:8.2.2726: confusing error message with white space before comma Problem: Confusing error message with white space before comma in the arguments of a function declaration. Solution: Give a specific error message. (closes vim/vim#2235) https://github.com/vim/vim/commit/86cdb8a4bd1abff40b5f80c3c4149b33cbaab990 Co-authored-by: Bram Moolenaar --- src/nvim/eval/userfunc.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src/nvim') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 7440044e52..2a7ad792df 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -48,6 +48,8 @@ static char *e_funcexts = N_("E122: Function %s already exists, add ! to replace static char *e_funcdict = N_("E717: Dictionary entry already exists"); static char *e_funcref = N_("E718: Funcref required"); static char *e_nofunc = N_("E130: Unknown function: %s"); +static char e_no_white_space_allowed_before_str_str[] + = N_("E1068: No white space allowed before '%s': %s"); void func_init(void) { @@ -149,6 +151,15 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int emsg(_("E989: Non-default argument follows default argument")); mustend = true; } + + if (ascii_iswhite(*p) && *skipwhite(p) == ',') { + // Be tolerant when skipping + if (!skip) { + semsg(_(e_no_white_space_allowed_before_str_str), ",", p); + goto err_ret; + } + p = skipwhite(p); + } if (*p == ',') { p++; } else { -- cgit From 905bef7bd9cd5d1751fc09aad3c6fb78e2c60ff8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 27 Oct 2022 08:34:01 +0800 Subject: vim-patch:8.2.2727: function test fails Problem: Function test fails. Solution: Adjust expected error number. https://github.com/vim/vim/commit/e9b8b78e046b40b877c999432c4698edb3413d5d Cherry-pick colons from patch 8.2.1593. Co-authored-by: Bram Moolenaar --- src/nvim/testdir/test_user_func.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_user_func.vim b/src/nvim/testdir/test_user_func.vim index 5041fa9ad4..01bf2e1d0f 100644 --- a/src/nvim/testdir/test_user_func.vim +++ b/src/nvim/testdir/test_user_func.vim @@ -149,8 +149,8 @@ func Test_default_arg() call assert_equal(res.optional, 2) call assert_equal(res['0'], 1) - call assert_fails("call MakeBadFunc()", 'E989') - call assert_fails("fu F(a=1 ,) | endf", 'E475') + call assert_fails("call MakeBadFunc()", 'E989:') + call assert_fails("fu F(a=1 ,) | endf", 'E1068:') " Since neovim does not have v:none, the ability to use the default " argument with the intermediate argument set to v:none has been omitted. -- cgit From 9eaae3d56b8d44907da3286084d3ee7b50fe7a07 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 27 Oct 2022 09:36:54 +0800 Subject: vim-patch:partial:8.2.2881: various pieces of code not covered by tests Problem: Various pieces of code not covered by tests. Solution: Add a few more tests. (Yegappan Lakshmanan, closes vim/vim#8245) https://github.com/vim/vim/commit/611728f80604dd56960e8c197e5749d203c8feb1 Only port the last two hunks of test_user_func.vim. Co-authored-by: Yegappan Lakshmanan --- src/nvim/testdir/test_user_func.vim | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_user_func.vim b/src/nvim/testdir/test_user_func.vim index 01bf2e1d0f..98c2fdd531 100644 --- a/src/nvim/testdir/test_user_func.vim +++ b/src/nvim/testdir/test_user_func.vim @@ -420,6 +420,11 @@ func Test_del_func() call assert_fails('delfunction Xabc', 'E130:') let d = {'a' : 10} call assert_fails('delfunc d.a', 'E718:') + func d.fn() + return 1 + endfunc + delfunc d.fn + call assert_equal({'a' : 10}, d) endfunc " Test for calling return outside of a function @@ -451,11 +456,12 @@ func Test_func_dict() return len(self) endfunc - call assert_equal("{'a': 'b', 'somefunc': function('2')}", string(mydict)) + call assert_equal("{'a': 'b', 'somefunc': function('3')}", string(mydict)) call assert_equal(2, mydict.somefunc()) call assert_match("^\n function \\d\\\+() dict" \ .. "\n1 return len(self)" \ .. "\n endfunction$", execute('func mydict.somefunc')) + call assert_fails('call mydict.nonexist()', 'E716:') endfunc func Test_func_range() -- cgit From acbfbbb649c39694c3a6a92160984db2fcb6f3ec Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 27 Oct 2022 09:44:13 +0800 Subject: vim-patch:8.2.3408: can delete a numbered function Problem: Can delete a numbered function. (Naohiro Ono) Solution: Disallow deleting a numbered function. (closes vim/vim#8760) https://github.com/vim/vim/commit/ddfc05100a29263a682dd96bb924dfde4354a654 Co-authored-by: Bram Moolenaar --- src/nvim/eval/userfunc.c | 7 +++++++ src/nvim/testdir/test_user_func.vim | 5 +++++ 2 files changed, 12 insertions(+) (limited to 'src/nvim') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 2a7ad792df..147beb78ad 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -2704,6 +2704,13 @@ void ex_delfunction(exarg_T *eap) *p = NUL; } + if (isdigit(*name) && fudi.fd_dict == NULL) { + if (!eap->skip) { + semsg(_(e_invarg2), eap->arg); + } + xfree(name); + return; + } if (!eap->skip) { fp = find_func(name); } diff --git a/src/nvim/testdir/test_user_func.vim b/src/nvim/testdir/test_user_func.vim index 98c2fdd531..7aa21d7816 100644 --- a/src/nvim/testdir/test_user_func.vim +++ b/src/nvim/testdir/test_user_func.vim @@ -423,6 +423,11 @@ func Test_del_func() func d.fn() return 1 endfunc + + " cannot delete the dict function by number + let nr = substitute(execute('echo d'), '.*function(''\(\d\+\)'').*', '\1', '') + call assert_fails('delfunction g:' .. nr, 'E475: Invalid argument: g:') + delfunc d.fn call assert_equal({'a' : 10}, d) endfunc -- cgit From 0117c97e624ec14da05844f4e6f162b4f4e2a827 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Thu, 27 Oct 2022 06:28:16 +0200 Subject: refactor(clint): convert short to int16_t (#20815) --- src/nvim/memline.c | 6 +++--- src/nvim/regexp.c | 2 +- src/nvim/spellfile.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/memline.c b/src/nvim/memline.c index da31235e74..225e2aeab1 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -169,7 +169,7 @@ struct block0 { char_u b0_fname[B0_FNAME_SIZE_ORG]; // name of file being edited long b0_magic_long; // check for byte order of long int b0_magic_int; // check for byte order of int - short b0_magic_short; // check for byte order of short + int16_t b0_magic_short; // check for byte order of short char_u b0_magic_char; // check for last char }; @@ -272,7 +272,7 @@ int ml_open(buf_T *buf) b0p->b0_id[1] = BLOCK0_ID1; b0p->b0_magic_long = B0_MAGIC_LONG; b0p->b0_magic_int = (int)B0_MAGIC_INT; - b0p->b0_magic_short = (short)B0_MAGIC_SHORT; + b0p->b0_magic_short = (int16_t)B0_MAGIC_SHORT; b0p->b0_magic_char = B0_MAGIC_CHAR; xstrlcpy(xstpcpy((char *)b0p->b0_version, "VIM "), Version, 6); long_to_char((long)mfp->mf_page_size, b0p->b0_page_size); @@ -3376,7 +3376,7 @@ static int b0_magic_wrong(ZERO_BL *b0p) { return b0p->b0_magic_long != B0_MAGIC_LONG || b0p->b0_magic_int != (int)B0_MAGIC_INT - || b0p->b0_magic_short != (short)B0_MAGIC_SHORT + || b0p->b0_magic_short != (int16_t)B0_MAGIC_SHORT || b0p->b0_magic_char != B0_MAGIC_CHAR; } diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index e87382ff7c..6bd15fdbbe 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -240,7 +240,7 @@ static int get_char_class(char **pp) * Specific version of character class functions. * Using a table to keep this fast. */ -static short class_tab[256]; +static int16_t class_tab[256]; #define RI_DIGIT 0x01 #define RI_HEX 0x02 diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 793985f45d..d5fbbaff1f 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -415,7 +415,7 @@ struct wordnode_S { // "wn_region" the LSW of the wordnr. char_u wn_affixID; // supported/required prefix ID or 0 uint16_t wn_flags; // WF_ flags - short wn_region; // region mask + int16_t wn_region; // region mask #ifdef SPELL_PRINTTREE int wn_nr; // sequence nr for printing -- cgit From 7765f2bb8304631c00f1e00ffc73c18cd4d22601 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Thu, 27 Oct 2022 06:46:18 +0200 Subject: build: copy each treesitter parser library individually #20797 Copying each .so file one by one, rather than the entire parser directory, allows newer .so files to overwrite older .so files with the same name. This is useful when bumping a parser version and rebuilding neovim without needing to run `make distclean` beforehand. --- src/nvim/CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index ac27f46bfb..aee6319770 100755 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -571,7 +571,10 @@ file(MAKE_DIRECTORY ${BINARY_LIB_DIR}) # install treesitter parser if bundled if(EXISTS ${DEPS_PREFIX}/lib/nvim/parser) - file(COPY ${DEPS_PREFIX}/lib/nvim/parser DESTINATION ${BINARY_LIB_DIR}) + glob_wrapper(TREESITTER_PARSERS ${DEPS_PREFIX}/lib/nvim/parser/*) + foreach(parser_lib ${TREESITTER_PARSERS}) + file(COPY ${parser_lib} DESTINATION ${BINARY_LIB_DIR}/parser) + endforeach() endif() install(DIRECTORY ${BINARY_LIB_DIR} -- cgit From b793395019333127e085997b7ced4ea02053697e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 27 Oct 2022 11:43:10 +0800 Subject: vim-patch:8.2.4070: using uninitialized memory when reading empty file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Using uninitialized memory when reading empty file. Solution: Check for empty file before checking for NL. (Dominique Pellé, closes vim/vim#9511) https://github.com/vim/vim/commit/f5d639a8af719eb8ecb141b5c0890627e4d83134 Co-authored-by: Dominique Pelle --- src/nvim/eval/funcs.c | 2 +- src/nvim/testdir/test_eval_stuff.vim | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index f66ff7b5bb..b475ff1096 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -5921,7 +5921,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) for (p = buf, start = buf; p < buf + readlen || (readlen <= 0 && (prevlen > 0 || binary)); p++) { - if (*p == '\n' || readlen <= 0) { + if (readlen <= 0 || *p == '\n') { char *s = NULL; size_t len = (size_t)(p - start); diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim index dc110af356..5c60b64c22 100644 --- a/src/nvim/testdir/test_eval_stuff.vim +++ b/src/nvim/testdir/test_eval_stuff.vim @@ -120,6 +120,13 @@ func Test_readfile_binary() call delete('XReadfile_bin') endfunc +func Test_readfile_binary_empty() + call writefile([], 'Xempty-file') + " This used to compare uninitialized memory in Vim <= 8.2.4065 + call assert_equal([''], readfile('Xempty-file', 'b')) + call delete('Xempty-file') +endfunc + func Test_readfile_bom() call writefile(["\ufeffFOO", "FOO\ufeffBAR"], 'XReadfile_bom') call assert_equal(['FOO', 'FOOBAR'], readfile('XReadfile_bom')) -- cgit From 807c6bb909806b5abc3e46a9677bedfdddf2a7f0 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 27 Oct 2022 11:40:38 +0800 Subject: vim-patch:8.2.4206: condition with many "(" causes a crash Problem: Condition with many "(" causes a crash. Solution: Limit recursion to 1000. https://github.com/vim/vim/commit/fe6fb267e6ee5c5da2f41889e4e0e0ac5bf4b89d Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 12 ++++++++++++ src/nvim/testdir/test_eval_stuff.vim | 5 +++++ 2 files changed, 17 insertions(+) (limited to 'src/nvim') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8bd91ec9a2..42ea8bd79b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -65,6 +65,7 @@ static char *e_nowhitespace = N_("E274: No white space allowed before parenthesis"); static char *e_write2 = N_("E80: Error while writing: %s"); static char *e_string_list_or_blob_required = N_("E1098: String, List or Blob required"); +static char e_expression_too_recursive_str[] = N_("E1169: Expression too recursive: %s"); static char * const namespace_char = "abglstvw"; @@ -2911,6 +2912,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) const char *start_leader, *end_leader; int ret = OK; char *alias; + static int recurse = 0; // Initialise variable so that tv_clear() can't mistake this for a // string and free a string that isn't there. @@ -2923,6 +2925,14 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) } end_leader = *arg; + // Limit recursion to 1000 levels. At least at 10000 we run out of stack + // and crash. + if (recurse == 1000) { + semsg(_(e_expression_too_recursive_str), *arg); + return FAIL; + } + recurse++; + switch (**arg) { // Number constant. case '0': @@ -3127,6 +3137,8 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) if (ret == OK && evaluate && end_leader > start_leader) { ret = eval7_leader(rettv, (char *)start_leader, &end_leader); } + + recurse--; return ret; } diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim index 5c60b64c22..851048ec5b 100644 --- a/src/nvim/testdir/test_eval_stuff.vim +++ b/src/nvim/testdir/test_eval_stuff.vim @@ -367,6 +367,11 @@ func Test_curly_assignment() unlet g:gvar endfunc +func Test_deep_recursion() + " this was running out of stack + call assert_fails("exe 'if ' .. repeat('(', 1002)", 'E1169: Expression too recursive: ((') +endfunc + " K_SPECIAL in the modified character used be escaped, which causes " double-escaping with feedkeys() or as the return value of an mapping, " and doesn't match what getchar() returns, -- cgit From e3acf913db7eb27d53ea8f91b70fb2c723796be9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 27 Oct 2022 11:50:08 +0800 Subject: vim-patch:8.2.4207: recursion test fails with MSVC Problem: Recursion test fails with MSVC. Solution: Use a smaller limit for MSVC. https://github.com/vim/vim/commit/50e05254450954f04183efc7bc871527a67868b8 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 42ea8bd79b..0e4dbeaea6 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2926,8 +2926,14 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) end_leader = *arg; // Limit recursion to 1000 levels. At least at 10000 we run out of stack - // and crash. - if (recurse == 1000) { + // and crash. With MSVC the stack is smaller. + if (recurse == +#ifdef _MSC_VER + 300 +#else + 1000 +#endif + ) { semsg(_(e_expression_too_recursive_str), *arg); return FAIL; } -- cgit From 25cf4f2fc712530a65dc3b533508c36d0439b728 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 27 Oct 2022 13:04:08 +0800 Subject: vim-patch:8.2.2653: build failure Problem: Build failure. Solution: Add missing changes. https://github.com/vim/vim/commit/3a0f092ac0dbdd4ce71f9c4abe020e89f13df36c Omit E1176: only applicable to Vim9 script. Co-authored-by: Bram Moolenaar --- src/nvim/eval/typval.c | 2 +- src/nvim/globals.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 3b513bfafb..fb601c4307 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -43,7 +43,7 @@ static char e_string_required_for_argument_nr[] = N_("E1174: String required for argument %d"); static char e_non_empty_string_required_for_argument_nr[] - = N_("E1142: Non-empty string required for argument %d"); + = N_("E1175: Non-empty string required for argument %d"); static char e_number_required_for_argument_nr[] = N_("E1210: Number required for argument %d"); diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 5a38585bb1..f6fbd6ffe9 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -997,8 +997,6 @@ EXTERN char e_luv_api_disabled[] INIT(= N_("E5560: %s must not be called in a lu EXTERN char e_floatonly[] INIT(= N_("E5601: Cannot close window, only floating window would remain")); EXTERN char e_floatexchange[] INIT(= N_("E5602: Cannot exchange or rotate float")); -EXTERN char e_non_empty_string_required[] INIT(= N_("E1142: Non-empty string required")); - EXTERN char e_cannot_define_autocommands_for_all_events[] INIT(= N_("E1155: Cannot define autocommands for ALL events")); EXTERN char e_resulting_text_too_long[] INIT(= N_("E1240: Resulting text too long")); -- cgit From 762ca67091d13336f90350a15e0a1b965d6d5c01 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 27 Oct 2022 13:08:01 +0800 Subject: vim-patch:8.2.4234: test_garbagecollect_now() does not check v:testing Problem: test_garbagecollect_now() does not check v:testing as documented. Solution: Give an error if v:testing is not set. https://github.com/vim/vim/commit/b3d83980d2ac0f7a25314270416f17af874ca269 Co-authored-by: Bram Moolenaar --- src/nvim/testdir/test_functions.vim | 6 ++++++ src/nvim/testing.c | 9 ++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index b751215b79..fa79aaf6d7 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -2206,6 +2206,12 @@ func Test_range() call assert_equal([0, 1, 2, 3, 4], uniq(range(5))) endfunc +func Test_garbagecollect_now_fails() + let v:testing = 0 + call assert_fails('call test_garbagecollect_now()', 'E1142:') + let v:testing = 1 +endfunc + " Test for the eval() function func Test_eval() call assert_fails("call eval('5 a')", 'E488:') diff --git a/src/nvim/testing.c b/src/nvim/testing.c index 348d5c6e29..45134db14f 100644 --- a/src/nvim/testing.c +++ b/src/nvim/testing.c @@ -14,6 +14,9 @@ # include "testing.c.generated.h" #endif +static char e_calling_test_garbagecollect_now_while_v_testing_is_not_set[] + = N_("E1142: Calling test_garbagecollect_now() while v:testing is not set"); + /// Prepare "gap" for an assert error and add the sourcing position. static void prepare_assert_error(garray_T *gap) { @@ -614,7 +617,11 @@ void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv, EvalFuncData { // This is dangerous, any Lists and Dicts used internally may be freed // while still in use. - garbage_collect(true); + if (!get_vim_var_nr(VV_TESTING)) { + emsg(_(e_calling_test_garbagecollect_now_while_v_testing_is_not_set)); + } else { + garbage_collect(true); + } } /// "test_write_list_log()" function -- cgit From 57af2d6953243cc2ae2ec721606e2447768a7021 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 27 Oct 2022 21:59:46 +0800 Subject: build: fix plural messages missing from .po files (#20830) --- src/nvim/po/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/po/CMakeLists.txt b/src/nvim/po/CMakeLists.txt index 57896b74ce..74d9901bad 100644 --- a/src/nvim/po/CMakeLists.txt +++ b/src/nvim/po/CMakeLists.txt @@ -49,7 +49,8 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG) add_custom_command( OUTPUT ${NVIM_POT} COMMAND ${XGETTEXT_PRG} -o ${NVIM_POT} --default-domain=nvim - --add-comments --keyword=_ --keyword=N_ -D ${CMAKE_CURRENT_SOURCE_DIR} + --add-comments --keyword=_ --keyword=N_ --keyword=NGETTEXT:1,2 + -D ${CMAKE_CURRENT_SOURCE_DIR} ${NVIM_RELATIVE_SOURCES} DEPENDS ${NVIM_SOURCES}) -- cgit From f44ad753801d881f5352c9182167ced18e79e456 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Thu, 27 Oct 2022 22:31:58 +0200 Subject: docs(api): pattern is not expanded for autocommands (#20812) Problem: Unlike `:autocmd`, `nvim_create_autocommand()` does not expand environment variables in the `pattern`, which is unexpected. Solution: Add a note to the documentation explaining this and suggesting using `expand()` explicitly. --- src/nvim/api/autocmd.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/nvim') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index b5c695b9ce..3dfe77ba38 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -401,6 +401,13 @@ cleanup: /// pattern = { "*.py", "*.pyi" } /// /// +/// Note: The `pattern` is passed to callbacks and commands as a literal string; environment +/// variables like `$HOME` and `~` are not automatically expanded as they are by |:autocmd|. +/// Instead, |expand()| such variables explicitly: +///
+///   pattern = vim.fn.expand("~") .. "/some/path/*.py"
+/// 
+/// /// Example values for event: ///
 ///   "BufWritePre"
@@ -411,7 +418,7 @@ cleanup:
 /// @param opts Dictionary of autocommand options:
 ///             - group (string|integer) optional: the autocommand group name or
 ///             id to match against.
-///             - pattern (string|array) optional: pattern or patterns to match
+///             - pattern (string|array) optional: pattern or patterns to match literally
 ///             against |autocmd-pattern|.
 ///             - buffer (integer) optional: buffer number for buffer local autocommands
 ///             |autocmd-buflocal|. Cannot be used with {pattern}.
-- 
cgit 


From 5568267ccb94924b9dcf7bfa5d52da0f16d161e4 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 27 Oct 2022 18:08:06 +0800
Subject: vim-patch:8.2.1544: cannot translate messages in a Vim script

Problem:    Cannot translate messages in a Vim script.
Solution:   Add gettext().  Try it out for a few messages in the options
            window.

https://github.com/vim/vim/commit/0b39c3fd4c5d1c8ebd2efa85fced7df5e17efd3b

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval.lua            |  1 +
 src/nvim/eval/funcs.c        | 13 +++++++++++++
 src/nvim/po/fixfilenames.vim | 13 +++++++++++++
 src/nvim/po/tojavascript.vim | 18 ++++++++++++++++++
 4 files changed, 45 insertions(+)
 create mode 100644 src/nvim/po/fixfilenames.vim
 create mode 100644 src/nvim/po/tojavascript.vim

(limited to 'src/nvim')

diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index cc26bbf1a8..264659af1f 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -185,6 +185,7 @@ return {
     gettabvar={args={2, 3}, base=1},
     gettabwinvar={args={3, 4}, base=1},
     gettagstack={args={0, 1}, base=1},
+    gettext={args=1, base=1},
     getwininfo={args={0, 1}, base=1},
     getwinpos={args={0, 1}, base=1},
     getwinposx={},
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index b475ff1096..e08dc2e4a5 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -3502,6 +3502,19 @@ static void f_glob2regpat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   rettv->vval.v_string = (pat == NULL) ? NULL : file_pat_to_reg_pat(pat, NULL, NULL, false);
 }
 
+/// "gettext()" function
+static void f_gettext(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+  if (argvars[0].v_type != VAR_STRING
+      || argvars[0].vval.v_string == NULL
+      || *argvars[0].vval.v_string == NUL) {
+    semsg(_(e_invarg2), tv_get_string(&argvars[0]));
+  } else {
+    rettv->v_type = VAR_STRING;
+    rettv->vval.v_string = xstrdup(_(argvars[0].vval.v_string));
+  }
+}
+
 /// "has()" function
 static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 {
diff --git a/src/nvim/po/fixfilenames.vim b/src/nvim/po/fixfilenames.vim
new file mode 100644
index 0000000000..65d448ce41
--- /dev/null
+++ b/src/nvim/po/fixfilenames.vim
@@ -0,0 +1,13 @@
+" Invoked with the name "vim.pot" and a list of Vim script names.
+" Converts them to a .js file, stripping comments, so that xgettext works.
+
+set shortmess+=A
+
+for name in argv()[1:]
+  let jsname = fnamemodify(name, ":t:r") .. ".js"
+  exe "%s+" .. jsname .. "+" .. name .. "+"
+endfor
+
+write
+last
+quit
diff --git a/src/nvim/po/tojavascript.vim b/src/nvim/po/tojavascript.vim
new file mode 100644
index 0000000000..7868570be7
--- /dev/null
+++ b/src/nvim/po/tojavascript.vim
@@ -0,0 +1,18 @@
+" Invoked with the name "vim.pot" and a list of Vim script names.
+" Converts them to a .js file, stripping comments, so that xgettext works.
+" Javascript is used because, like Vim, it accepts both single and double
+" quoted strings.
+
+set shortmess+=A
+
+for name in argv()[1:]
+  exe 'edit ' .. fnameescape(name)
+
+  " Strip comments
+  g/^\s*"/s/.*//
+
+  " Write as .js file, xgettext recognizes them
+  exe 'w! ' .. fnamemodify(name, ":t:r") .. ".js"
+endfor
+
+quit
-- 
cgit 


From bbbcd5393dc1ad02effc87d55665412ffaa19cc8 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 27 Oct 2022 22:07:30 +0800
Subject: vim-patch:8.2.1585: messages in globals.h not translated

Problem:    Messages in globals.h not translated, xgettext on MS-Windows not
            fully supported.
Solution:   Add globals.h to list of input files.  Update MS-Windows makefiles
            to improve message translations. (Ken Takata, closes vim/vim#6858)

https://github.com/vim/vim/commit/fa57335e532e505ce9229ddb2354a593fb057561

Also update gettext() docs to match latest Vim.
---
 src/nvim/po/fixfilenames.vim | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/po/fixfilenames.vim b/src/nvim/po/fixfilenames.vim
index 65d448ce41..04bc0791c0 100644
--- a/src/nvim/po/fixfilenames.vim
+++ b/src/nvim/po/fixfilenames.vim
@@ -5,7 +5,7 @@ set shortmess+=A
 
 for name in argv()[1:]
   let jsname = fnamemodify(name, ":t:r") .. ".js"
-  exe "%s+" .. jsname .. "+" .. name .. "+"
+  exe "%s+" .. jsname .. "+" .. substitute(name, '\\', '/', 'g') .. "+"
 endfor
 
 write
-- 
cgit 


From bfdf10d87088ee8094cdbab7cef1cb49dc637663 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 28 Oct 2022 11:39:51 +0800
Subject: vim-patch:8.2.1395: Vim9: no error if declaring a funcref with lower
 case letter

Problem:    Vim9: no error if declaring a funcref with a lower case letter.
Solution:   Check the name after the type is inferred. Fix confusing name.

https://github.com/vim/vim/commit/98b4f145eb89405021e23a4a37db51d60a75a1d0

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval.c        |  2 +-
 src/nvim/eval/typval.c |  2 +-
 src/nvim/eval/vars.c   | 12 ++++++------
 3 files changed, 8 insertions(+), 8 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 0e4dbeaea6..d6411fb15b 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -1444,7 +1444,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
         }
         wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE
                   && tv_is_func(*rettv)
-                  && !var_check_func_name((const char *)key, lp->ll_di == NULL))
+                  && var_wrong_func_name((const char *)key, lp->ll_di == NULL))
                  || !valid_varname((const char *)key));
         if (len != -1) {
           key[len] = prevval;
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index fb601c4307..f45e68acb3 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -2436,7 +2436,7 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action
       // Check the key to be valid when adding to any scope.
       if (d1->dv_scope == VAR_DEF_SCOPE
           && tv_is_func(di2->di_tv)
-          && !var_check_func_name((const char *)di2->di_key, di1 == NULL)) {
+          && var_wrong_func_name((const char *)di2->di_key, di1 == NULL)) {
         break;
       }
       if (!valid_varname((const char *)di2->di_key)) {
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index 4d7214205d..ef6e3f02e2 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -1257,7 +1257,7 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv,
     v = find_var_in_scoped_ht(name, name_len, true);
   }
 
-  if (tv_is_func(*tv) && !var_check_func_name(name, v == NULL)) {
+  if (tv_is_func(*tv) && var_wrong_func_name(name, v == NULL)) {
     return;
   }
 
@@ -1445,9 +1445,9 @@ bool var_check_fixed(const int flags, const char *name, size_t name_len)
 /// @param[in]  name  Possible function/funcref name.
 /// @param[in]  new_var  True if it is a name for a variable.
 ///
-/// @return false in case of error, true in case of success. Also gives an
+/// @return false in case of success, true in case of failure. Also gives an
 ///         error message if appropriate.
-bool var_check_func_name(const char *const name, const bool new_var)
+bool var_wrong_func_name(const char *const name, const bool new_var)
   FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
 {
   // Allow for w: b: s: and t:.
@@ -1455,16 +1455,16 @@ bool var_check_func_name(const char *const name, const bool new_var)
       && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':')
                         ? name[2] : name[0])) {
     semsg(_("E704: Funcref variable name must start with a capital: %s"), name);
-    return false;
+    return true;
   }
   // Don't allow hiding a function.  When "v" is not NULL we might be
   // assigning another function to the same var, the type is checked
   // below.
   if (new_var && function_exists(name, false)) {
     semsg(_("E705: Variable name conflicts with existing function: %s"), name);
-    return false;
+    return true;
   }
-  return true;
+  return false;
 }
 
 // TODO(ZyX-I): move to eval/expressions
-- 
cgit 


From 3afcc48a05693738f613464607286b5684b148cd Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 28 Oct 2022 13:27:38 +0800
Subject: vim-patch:8.2.3407: using uninitialized memory with "let g:['bar'] =
 2"

Problem:    Using uninitialized memory with "let g:['bar'] = 2".
Solution:   Initialize v_type of a new dict item.

https://github.com/vim/vim/commit/3b318513561b5862944769188ae4af6b70311838

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval/typval.c | 1 +
 1 file changed, 1 insertion(+)

(limited to 'src/nvim')

diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index f45e68acb3..cc6c2b5d90 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -1841,6 +1841,7 @@ dictitem_T *tv_dict_item_alloc_len(const char *const key, const size_t key_len)
   di->di_key[key_len] = NUL;
   di->di_flags = DI_FLAGS_ALLOC;
   di->di_tv.v_lock = VAR_UNLOCKED;
+  di->di_tv.v_type = VAR_UNKNOWN;
   return di;
 }
 
-- 
cgit 


From 60427eb05f8b86bc669b5a5c8a9d4630d0cfa55c Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Fri, 28 Oct 2022 13:22:10 +0100
Subject: fix(diff.c): regression in diffgetput (#20843)

---
 src/nvim/diff.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index f458f0940d..964db599a3 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -2678,7 +2678,8 @@ static void diffgetput(const int addr_count, const int idx_cur, const int idx_fr
       // past the range that was specified
       break;
     }
-    diff_T *dfree = NULL;
+    diff_T dfree = { 0 };
+    bool did_free = false;
     linenr_T lnum = dp->df_lnum[idx_to];
     linenr_T count = dp->df_count[idx_to];
 
@@ -2775,7 +2776,9 @@ static void diffgetput(const int addr_count, const int idx_cur, const int idx_fr
 
         if (i == DB_COUNT) {
           // delete the diff entry, the buffers are now equal here
-          dfree = dp;
+          dfree = *dp;
+          did_free = true;
+          dp = diff_free(curtab, dprev, dp);
         }
       }
 
@@ -2794,10 +2797,9 @@ static void diffgetput(const int addr_count, const int idx_cur, const int idx_fr
       }
       changed_lines(lnum, 0, lnum + count, added, true);
 
-      if (dfree == dp) {
+      if (did_free) {
         // Diff is deleted, update folds in other windows.
-        diff_fold_update(dfree, idx_to);
-        dp = diff_free(curtab, dprev, dp);
+        diff_fold_update(&dfree, idx_to);
       }
 
       // mark_adjust() may have made "dp" invalid.  We don't know where
@@ -2806,7 +2808,7 @@ static void diffgetput(const int addr_count, const int idx_cur, const int idx_fr
         break;
       }
 
-      if (dfree == NULL) {
+      if (!did_free) {
         // mark_adjust() may have changed the count in a wrong way
         dp->df_count[idx_to] = new_count;
       }
@@ -2818,7 +2820,7 @@ static void diffgetput(const int addr_count, const int idx_cur, const int idx_fr
     }
 
     // If before the range or not deleted, go to next diff.
-    if (dfree == NULL) {
+    if (!did_free) {
       dprev = dp;
       dp = dp->df_next;
     }
-- 
cgit 


From d9dce2d955b0359d1555bfa86aa641f26ad6e8bc Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 28 Oct 2022 22:35:48 +0800
Subject: build: make update-po support optwin.vim (#20840)

---
 src/nvim/po/CMakeLists.txt | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/po/CMakeLists.txt b/src/nvim/po/CMakeLists.txt
index 74d9901bad..1db21880bb 100644
--- a/src/nvim/po/CMakeLists.txt
+++ b/src/nvim/po/CMakeLists.txt
@@ -48,11 +48,16 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG)
   list(SORT NVIM_RELATIVE_SOURCES)
   add_custom_command(
     OUTPUT ${NVIM_POT}
+    COMMAND $ -u NONE -i NONE -n --headless --cmd "set cpo+=+"
+      -S ${CMAKE_CURRENT_SOURCE_DIR}/tojavascript.vim ${NVIM_POT} ${PROJECT_SOURCE_DIR}/runtime/optwin.vim
     COMMAND ${XGETTEXT_PRG} -o ${NVIM_POT} --default-domain=nvim
       --add-comments --keyword=_ --keyword=N_ --keyword=NGETTEXT:1,2
-      -D ${CMAKE_CURRENT_SOURCE_DIR}
-      ${NVIM_RELATIVE_SOURCES}
-    DEPENDS ${NVIM_SOURCES})
+      -D ${CMAKE_CURRENT_SOURCE_DIR} -D ${CMAKE_CURRENT_BINARY_DIR}
+      ${NVIM_RELATIVE_SOURCES} optwin.js
+    COMMAND $ -u NONE -i NONE -n --headless --cmd "set cpo+=+"
+      -S ${CMAKE_CURRENT_SOURCE_DIR}/fixfilenames.vim ${NVIM_POT} ../../../runtime/optwin.vim
+    VERBATIM
+    DEPENDS ${NVIM_SOURCES} nvim nvim_runtime_deps)
 
   set(LANGUAGE_MO_FILES)
   set(UPDATE_PO_TARGETS)
-- 
cgit 


From 6884f017b53369d6c9b06ddd3aedeb642dbd24a8 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Sat, 29 Oct 2022 17:41:22 +0200
Subject: vim-patch:partial:6ebe4f970b8b (#20860)

Update runtime files

https://github.com/vim/vim/commit/6ebe4f970b8b398087076a72a7aae6e680fb92da

Co-authored-by: Bram Moolenaar 
---
 src/nvim/tui/tui.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 1cb1c34ad3..083677a58e 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -2099,7 +2099,7 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version,
   // Dickey ncurses terminfo does not include the setrgbf and setrgbb
   // capabilities, proposed by Rüdiger Sonderfeld on 2013-10-15.  Adding
   // them here when terminfo lacks them is an augmentation, not a fixup.
-  // https://gist.github.com/XVilka/8346728
+  // https://github.com/termstandard/colors
 
   // At this time (2017-07-12) it seems like all terminals that support rgb
   // color codes can use semicolons in the terminal code and be fine.
-- 
cgit 


From a7d100f052b45a106d1385ed419509c047c12431 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 30 Oct 2022 06:49:39 +0800
Subject: fix: avoid unsigned overflow in home_replace() (#20854)

---
 src/nvim/os/env.c | 6 ++++++
 1 file changed, 6 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index bd79b43574..faafc546a4 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -1119,10 +1119,16 @@ size_t home_replace(const buf_T *const buf, const char *src, char *const dst, si
       len = envlen;
     }
 
+    if (dstlen == 0) {
+      break;  // Avoid overflowing below.
+    }
     // if (!one) skip to separator: space or comma.
     while (*src && (one || (*src != ',' && *src != ' ')) && --dstlen > 0) {
       *dst_p++ = *src++;
     }
+    if (dstlen == 0) {
+      break;  // Avoid overflowing below.
+    }
     // Skip separator.
     while ((*src == ' ' || *src == ',') && --dstlen > 0) {
       *dst_p++ = *src++;
-- 
cgit 


From f2d9c330fc56d1e589d33c82d372532e1695ce40 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 30 Oct 2022 07:10:04 +0800
Subject: fix(path): don't remove trailing slash when getting absolute path
 (#20853)

Before Vim patch 8.2.3468 relative_directory is never used in the
resulting path name, so whether it has a trailing slash didn't matter.
Now path_full_dir_name() appends a non-existing relative directory to
the current directory name, so the trailing slash needs to be kept.
---
 src/nvim/path.c | 13 +++----------
 1 file changed, 3 insertions(+), 10 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/path.c b/src/nvim/path.c
index 981937b79e..625a3c7922 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -2362,16 +2362,9 @@ static int path_to_absolute(const char *fname, char *buf, size_t len, int force)
     }
 #endif
     if (p != NULL) {
-      // relative to root
-      if (p == fname) {
-        // only one path component
-        relative_directory[0] = PATHSEP;
-        relative_directory[1] = NUL;
-      } else {
-        assert(p >= fname);
-        memcpy(relative_directory, fname, (size_t)(p - fname));
-        relative_directory[p - fname] = NUL;
-      }
+      assert(p >= fname);
+      memcpy(relative_directory, fname, (size_t)(p - fname + 1));
+      relative_directory[p - fname + 1] = NUL;
       end_of_path = p + 1;
     } else {
       relative_directory[0] = NUL;
-- 
cgit 


From 23080a4d7a9043913cb4875e18118731a4562032 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 30 Oct 2022 07:10:39 +0800
Subject: fix(mouse): ensure no scrolling with "ver:0" in 'mousescroll'
 (#20861)

---
 src/nvim/edit.c   | 2 +-
 src/nvim/normal.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 90a18947dd..c99449e0f3 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -4147,7 +4147,7 @@ static void ins_mousescroll(int dir)
     if (dir == MSCR_DOWN || dir == MSCR_UP) {
       if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
         scroll_redraw(dir, (long)(curwin->w_botline - curwin->w_topline));
-      } else {
+      } else if (p_mousescroll_vert > 0) {
         scroll_redraw(dir, p_mousescroll_vert);
       }
     } else {
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index e09fb149b4..3c49948b55 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -3417,7 +3417,7 @@ static void nv_mousescroll(cmdarg_T *cap)
   if (cap->arg == MSCR_UP || cap->arg == MSCR_DOWN) {
     if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
       (void)onepage(cap->arg ? FORWARD : BACKWARD, 1L);
-    } else {
+    } else if (p_mousescroll_vert > 0) {
       cap->count1 = p_mousescroll_vert;
       cap->count0 = p_mousescroll_vert;
       nv_scroll_line(cap);
-- 
cgit 


From feabc1c98cb077f60fde1a14e8540f2cf99eb8d2 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 29 Oct 2022 09:03:15 +0800
Subject: vim-patch:9.0.0816: CTRL-Z at end of file is always dropped

Problem:    CTRL-Z at end of file is always dropped.
Solution:   Add the 'endoffile' option, like the 'endofline' option.
            (closes vim/vim#11408, closes vim/vim#11397)

Cherry-pick test_fixeol.vim changes from patch 8.2.1432.
Cherry-pick 'endoffile' changes from latest Vim runtime update.

https://github.com/vim/vim/commit/fb0cf2357e0c85bbfd9f9178705ad8d77b6b3b4e

vim-patch:f0b567e32a46

Revert unintended Makefile change

https://github.com/vim/vim/commit/f0b567e32a462fe838170a202919d18b53eff987

vim-patch:72c8e3c070b3

Fix wrong struct access for member.

https://github.com/vim/vim/commit/72c8e3c070b30f82bc0d203a62c168e43a13e99b

vim-patch:3f68a4136eb9

Add missing entry for the 'endoffile' option.

https://github.com/vim/vim/commit/3f68a4136eb99840d739af5133ab31948f273f63

Co-authored-by: Bram Moolenaar 
---
 src/nvim/buffer_defs.h           |  1 +
 src/nvim/fileio.c                | 13 ++++++++++---
 src/nvim/option.c                |  2 ++
 src/nvim/option_defs.h           |  2 ++
 src/nvim/options.lua             |  9 +++++++++
 src/nvim/testdir/test_fixeol.vim | 15 +++++++++------
 6 files changed, 33 insertions(+), 9 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 043a31bf16..8e29edc0f5 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -675,6 +675,7 @@ struct file_buffer {
   char *b_p_cfu;                ///< 'completefunc'
   char *b_p_ofu;                ///< 'omnifunc'
   char *b_p_tfu;                ///< 'tagfunc'
+  int b_p_eof;                  ///< 'endoffile'
   int b_p_eol;                  ///< 'endofline'
   int b_p_fixeol;               ///< 'fixendofline'
   int b_p_et;                   ///< 'expandtab'
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index cfdd6fe697..ae0340cdc6 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -525,6 +525,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
     // correctly set when reading stdin.
     if (!read_buffer) {
       curbuf->b_p_eol = true;
+      curbuf->b_p_eof = false;
       curbuf->b_start_eol = true;
     }
     curbuf->b_p_bomb = false;
@@ -1629,12 +1630,13 @@ failed:
       && !got_int
       && linerest != 0
       && !(!curbuf->b_p_bin
-           && fileformat == EOL_DOS
-           && *line_start == Ctrl_Z
-           && ptr == line_start + 1)) {
+           && fileformat == EOL_DOS)) {
     // remember for when writing
     if (set_options) {
       curbuf->b_p_eol = false;
+      if (*line_start == Ctrl_Z && ptr == line_start + 1) {
+        curbuf->b_p_eof = false;
+      }
     }
     *ptr = NUL;
     len = (colnr_T)(ptr - line_start + 1);
@@ -3191,6 +3193,11 @@ restore_backup:
         len = 0;
         write_info.bw_start_lnum = lnum;
       }
+      if (!buf->b_p_fixeol && buf->b_p_eof) {
+        // write trailing CTRL-Z
+        (void)write_eintr(write_info.bw_fd, "\x1a", 1);
+      }
+
       // write failed or last line has no EOL: stop here
       if (end == 0
           || (lnum == end
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 06662afd08..8de86ce76e 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -3978,6 +3978,8 @@ static char_u *get_varp(vimoption_T *p)
     return (char_u *)&(curbuf->b_p_cfu);
   case PV_OFU:
     return (char_u *)&(curbuf->b_p_ofu);
+  case PV_EOF:
+    return (char_u *)&(curbuf->b_p_eof);
   case PV_EOL:
     return (char_u *)&(curbuf->b_p_eol);
   case PV_FIXEOL:
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 19e4780e0a..7c33773e41 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -497,6 +497,7 @@ EXTERN char_u *p_ef;            // 'errorfile'
 EXTERN char *p_efm;             // 'errorformat'
 EXTERN char *p_gefm;            // 'grepformat'
 EXTERN char *p_gp;              // 'grepprg'
+EXTERN int p_eof;               ///< 'endoffile'
 EXTERN int p_eol;               ///< 'endofline'
 EXTERN char *p_ei;              // 'eventignore'
 EXTERN int p_et;                ///< 'expandtab'
@@ -858,6 +859,7 @@ enum {
   BV_CFU,
   BV_DEF,
   BV_INC,
+  BV_EOF,
   BV_EOL,
   BV_FIXEOL,
   BV_EP,
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index ba483d3714..088aa40fda 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -640,6 +640,15 @@ return {
       varname='p_enc',
       defaults={if_true=macros('ENC_DFLT')}
     },
+    {
+      full_name='endoffile', abbreviation='eof',
+      short_desc=N_("write CTRL-Z for last line in file"),
+      type='bool', scope={'buffer'},
+      no_mkrc=true,
+      redraw={'statuslines'},
+      varname='p_eof',
+      defaults={if_true=true}
+    },
     {
       full_name='endofline', abbreviation='eol',
       short_desc=N_("write  for last line in file"),
diff --git a/src/nvim/testdir/test_fixeol.vim b/src/nvim/testdir/test_fixeol.vim
index 32cb059e26..3ede84f49e 100644
--- a/src/nvim/testdir/test_fixeol.vim
+++ b/src/nvim/testdir/test_fixeol.vim
@@ -1,16 +1,17 @@
-" Tests for 'fixeol' and 'eol'
+" Tests for 'fixeol', 'eof' and 'eol'
+
 func Test_fixeol()
   " first write two test files – with and without trailing EOL
   " use Unix fileformat for consistency
   set ff=unix
   enew!
-  call setline('.', 'with eol')
+  call setline('.', 'with eol or eof')
   w! XXEol
   enew!
-  set noeol nofixeol
-  call setline('.', 'without eol')
+  set noeof noeol nofixeol
+  call setline('.', 'without eol or eof')
   w! XXNoEol
-  set eol fixeol
+  set eol eof fixeol
   bwipe XXEol XXNoEol
 
   " try editing files with 'fixeol' disabled
@@ -43,6 +44,8 @@ func Test_fixeol()
   call delete('XXNoEol')
   call delete('XXTestEol')
   call delete('XXTestNoEol')
-  set ff& fixeol& eol&
+  set ff& fixeol& eof& eol&
   enew!
 endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From 4158ad38ecb107cfe3d1dd7472cc9e17039f6ee2 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 30 Oct 2022 07:56:10 +0800
Subject: vim-patch:9.0.0819: still a build error, tests are failing

Problem:    Still a build error, tests are failing.
Solution:   Correct recent changes. Add missing init for 'eof'.

https://github.com/vim/vim/commit/1577537f109d97a975fda9a899cacfb598617767

vim-patch:1577537f109d

Co-authored-by: Bram Moolenaar 
---
 src/nvim/buffer.c                |  2 ++
 src/nvim/buffer_defs.h           |  1 +
 src/nvim/change.c                |  4 +++-
 src/nvim/fileio.c                | 10 +++++++---
 src/nvim/testdir/test_fixeol.vim |  8 ++++----
 5 files changed, 17 insertions(+), 8 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 8016904702..ade5c35450 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -689,6 +689,8 @@ void buf_clear_file(buf_T *buf)
 {
   buf->b_ml.ml_line_count = 1;
   unchanged(buf, true, true);
+  buf->b_p_eof = false;
+  buf->b_start_eof = false;
   buf->b_p_eol = true;
   buf->b_start_eol = true;
   buf->b_p_bomb = false;
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 8e29edc0f5..52449faa5b 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -794,6 +794,7 @@ struct file_buffer {
   linenr_T b_no_eol_lnum;       // non-zero lnum when last line of next binary
                                 // write should not have an end-of-line
 
+  int b_start_eof;              // last line had eof (CTRL-Z) when it was read
   int b_start_eol;              // last line had eol when it was read
   int b_start_ffc;              // first char of 'ff' when edit started
   char *b_start_fenc;           // 'fileencoding' when edit started or NULL
diff --git a/src/nvim/change.c b/src/nvim/change.c
index 2424a8a2eb..c6df98651d 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -539,6 +539,7 @@ void unchanged(buf_T *buf, int ff, bool always_inc_changedtick)
 void save_file_ff(buf_T *buf)
 {
   buf->b_start_ffc = (unsigned char)(*buf->b_p_ff);
+  buf->b_start_eof = buf->b_p_eof;
   buf->b_start_eol = buf->b_p_eol;
   buf->b_start_bomb = buf->b_p_bomb;
 
@@ -573,7 +574,8 @@ bool file_ff_differs(buf_T *buf, bool ignore_empty)
   if (buf->b_start_ffc != *buf->b_p_ff) {
     return true;
   }
-  if ((buf->b_p_bin || !buf->b_p_fixeol) && buf->b_start_eol != buf->b_p_eol) {
+  if ((buf->b_p_bin || !buf->b_p_fixeol)
+      && (buf->b_start_eof != buf->b_p_eof || buf->b_start_eol != buf->b_p_eol)) {
     return true;
   }
   if (!buf->b_p_bin && buf->b_start_bomb != buf->b_p_bomb) {
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index ae0340cdc6..2ba2f4c9c9 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -524,8 +524,9 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
     // Don't change 'eol' if reading from buffer as it will already be
     // correctly set when reading stdin.
     if (!read_buffer) {
-      curbuf->b_p_eol = true;
       curbuf->b_p_eof = false;
+      curbuf->b_start_eof = false;
+      curbuf->b_p_eol = true;
       curbuf->b_start_eol = true;
     }
     curbuf->b_p_bomb = false;
@@ -1629,13 +1630,16 @@ failed:
   if (!error
       && !got_int
       && linerest != 0
+      // TODO(vim): should we handle CTRL-Z differently here for 'endoffile'?
       && !(!curbuf->b_p_bin
-           && fileformat == EOL_DOS)) {
+           && fileformat == EOL_DOS
+           && *line_start == Ctrl_Z
+           && ptr == line_start + 1)) {
     // remember for when writing
     if (set_options) {
       curbuf->b_p_eol = false;
       if (*line_start == Ctrl_Z && ptr == line_start + 1) {
-        curbuf->b_p_eof = false;
+        curbuf->b_p_eof = true;
       }
     }
     *ptr = NUL;
diff --git a/src/nvim/testdir/test_fixeol.vim b/src/nvim/testdir/test_fixeol.vim
index 3ede84f49e..9d6c900bdb 100644
--- a/src/nvim/testdir/test_fixeol.vim
+++ b/src/nvim/testdir/test_fixeol.vim
@@ -34,10 +34,10 @@ func Test_fixeol()
   w >>XXTestEol
   w >>XXTestNoEol
 
-  call assert_equal(['with eol', 'END'], readfile('XXEol'))
-  call assert_equal(['without eolEND'], readfile('XXNoEol'))
-  call assert_equal(['with eol', 'stays eol', 'END'], readfile('XXTestEol'))
-  call assert_equal(['without eol', 'stays withoutEND'],
+  call assert_equal(['with eol or eof', 'END'], readfile('XXEol'))
+  call assert_equal(['without eol or eofEND'], readfile('XXNoEol'))
+  call assert_equal(['with eol or eof', 'stays eol', 'END'], readfile('XXTestEol'))
+  call assert_equal(['without eol or eof', 'stays withoutEND'],
 	      \ readfile('XXTestNoEol'))
 
   call delete('XXEol')
-- 
cgit 


From 0240fd6d0f7e27c459b243578ad51100ff6e2b66 Mon Sep 17 00:00:00 2001
From: Jan Palus 
Date: Wed, 5 Oct 2022 00:18:09 +0200
Subject: fix(memory): fix memory alignment for dynamic allocation

all pointers returned by arena_alloc residing in arena block should be
properly aligned

to meet neovim's alignment requirements but keeping it simple settle on
ARENA_ALIGN = MAX(sizeof(void *), sizeof(double)).
---
 src/nvim/memory.c | 32 +++++++++++++++++++-------------
 src/nvim/memory.h |  2 +-
 2 files changed, 20 insertions(+), 14 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 61c43d8f99..93aa9bd6e5 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -6,6 +6,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 #include "nvim/api/extmark.h"
@@ -576,6 +577,12 @@ void alloc_block(Arena *arena)
   blk->prev = prev_blk;
 }
 
+static size_t arena_align_offset(void *ptr, size_t alignment)
+{
+  uintptr_t uptr = (uintptr_t)ptr;
+  return ((uptr + (alignment - 1)) & ~(alignment - 1)) - uptr;
+}
+
 /// @param arena if NULL, do a global allocation. caller must then free the value!
 /// @param size if zero, will still return a non-null pointer, but not a unique one
 void *arena_alloc(Arena *arena, size_t size, bool align)
@@ -583,34 +590,33 @@ void *arena_alloc(Arena *arena, size_t size, bool align)
   if (!arena) {
     return xmalloc(size);
   }
-  if (align) {
-    arena->pos = (arena->pos + (ARENA_ALIGN - 1)) & ~(ARENA_ALIGN - 1);
+  if (!arena->cur_blk) {
+    alloc_block(arena);
   }
-  if (arena->pos + size > arena->size || !arena->cur_blk) {
-    if (size > (ARENA_BLOCK_SIZE - sizeof(struct consumed_blk)) >> 1) {
+  size_t align_pos = align ? arena_align_offset(arena->cur_blk + arena->pos, ARENA_ALIGN) : 0;
+  if (arena->pos + align_pos + size > arena->size) {
+    if (size + (align ? (ARENA_ALIGN - 1) : 0) > (ARENA_BLOCK_SIZE - sizeof(struct consumed_blk))
+        >> 1) {
       // if allocation is too big, allocate a large block with the requested
       // size, but still with block pointer head. We do this even for
       // arena->size / 2, as there likely is space left for the next
       // small allocation in the current block.
-      if (!arena->cur_blk) {
-        // to simplify free-list management, arena->cur_blk must
-        // always be a normal, ARENA_BLOCK_SIZE sized, block
-        alloc_block(arena);
-      }
       arena_alloc_count++;
-      char *alloc = xmalloc(size + sizeof(struct consumed_blk));
+      char *alloc = xmalloc(size + sizeof(struct consumed_blk) + (align ? (ARENA_ALIGN - 1) : 0));
       struct consumed_blk *cur_blk = (struct consumed_blk *)arena->cur_blk;
       struct consumed_blk *fix_blk = (struct consumed_blk *)alloc;
       fix_blk->prev = cur_blk->prev;
       cur_blk->prev = fix_blk;
-      return (alloc + sizeof(struct consumed_blk));
+      char *mem = (alloc + sizeof(struct consumed_blk));
+      return mem + (align ? arena_align_offset(mem, ARENA_ALIGN) : 0);
     } else {
       alloc_block(arena);
+      align_pos = align ? arena_align_offset(arena->cur_blk + arena->pos, ARENA_ALIGN) : 0;
     }
   }
 
-  char *mem = arena->cur_blk + arena->pos;
-  arena->pos += size;
+  char *mem = arena->cur_blk + arena->pos + align_pos;
+  arena->pos += (size + align_pos);
   return mem;
 }
 
diff --git a/src/nvim/memory.h b/src/nvim/memory.h
index f407192331..1c2ed2ba3b 100644
--- a/src/nvim/memory.h
+++ b/src/nvim/memory.h
@@ -45,7 +45,7 @@ typedef struct consumed_blk {
   struct consumed_blk *prev;
 } *ArenaMem;
 
-#define ARENA_ALIGN sizeof(void *)
+#define ARENA_ALIGN MAX(sizeof(void *), sizeof(double))
 
 typedef struct {
   char *cur_blk;
-- 
cgit 


From 8b7247af7dc613a7e4248ba14760f586a8a66a32 Mon Sep 17 00:00:00 2001
From: bfredl 
Date: Mon, 31 Oct 2022 10:07:21 +0100
Subject: refactor(memory): simplify new alignment logic

In particular, we can assume the xmalloc-ed pointer is at least
double-aligned, otherwise nothing work work.
---
 src/nvim/memory.c | 32 +++++++++++++++++---------------
 1 file changed, 17 insertions(+), 15 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 93aa9bd6e5..16033e9c63 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -577,14 +577,13 @@ void alloc_block(Arena *arena)
   blk->prev = prev_blk;
 }
 
-static size_t arena_align_offset(void *ptr, size_t alignment)
+static size_t arena_align_offset(uint64_t off)
 {
-  uintptr_t uptr = (uintptr_t)ptr;
-  return ((uptr + (alignment - 1)) & ~(alignment - 1)) - uptr;
+  return ((off + (ARENA_ALIGN - 1)) & ~(ARENA_ALIGN - 1));
 }
 
 /// @param arena if NULL, do a global allocation. caller must then free the value!
-/// @param size if zero, will still return a non-null pointer, but not a unique one
+/// @param size if zero, will still return a non-null pointer, but not a usable or unique one
 void *arena_alloc(Arena *arena, size_t size, bool align)
 {
   if (!arena) {
@@ -593,30 +592,33 @@ void *arena_alloc(Arena *arena, size_t size, bool align)
   if (!arena->cur_blk) {
     alloc_block(arena);
   }
-  size_t align_pos = align ? arena_align_offset(arena->cur_blk + arena->pos, ARENA_ALIGN) : 0;
-  if (arena->pos + align_pos + size > arena->size) {
-    if (size + (align ? (ARENA_ALIGN - 1) : 0) > (ARENA_BLOCK_SIZE - sizeof(struct consumed_blk))
-        >> 1) {
+  size_t alloc_pos = align ? arena_align_offset(arena->pos) : arena->pos;
+  if (alloc_pos + size > arena->size) {
+    if (size > (ARENA_BLOCK_SIZE - sizeof(struct consumed_blk)) >> 1) {
       // if allocation is too big, allocate a large block with the requested
       // size, but still with block pointer head. We do this even for
       // arena->size / 2, as there likely is space left for the next
       // small allocation in the current block.
       arena_alloc_count++;
-      char *alloc = xmalloc(size + sizeof(struct consumed_blk) + (align ? (ARENA_ALIGN - 1) : 0));
+      size_t hdr_size = sizeof(struct consumed_blk);
+      size_t aligned_hdr_size = (align ? arena_align_offset(hdr_size) : hdr_size);
+      char *alloc = xmalloc(size + aligned_hdr_size);
+
+      // to simplify free-list management, arena->cur_blk must
+      // always be a normal, ARENA_BLOCK_SIZE sized, block
       struct consumed_blk *cur_blk = (struct consumed_blk *)arena->cur_blk;
       struct consumed_blk *fix_blk = (struct consumed_blk *)alloc;
       fix_blk->prev = cur_blk->prev;
       cur_blk->prev = fix_blk;
-      char *mem = (alloc + sizeof(struct consumed_blk));
-      return mem + (align ? arena_align_offset(mem, ARENA_ALIGN) : 0);
+      return alloc + aligned_hdr_size;
     } else {
-      alloc_block(arena);
-      align_pos = align ? arena_align_offset(arena->cur_blk + arena->pos, ARENA_ALIGN) : 0;
+      alloc_block(arena);  // resets arena->pos
+      alloc_pos = align ? arena_align_offset(arena->pos) : arena->pos;
     }
   }
 
-  char *mem = arena->cur_blk + arena->pos + align_pos;
-  arena->pos += (size + align_pos);
+  char *mem = arena->cur_blk + alloc_pos;
+  arena->pos = alloc_pos + size;
   return mem;
 }
 
-- 
cgit 


From 428ab6f24e6b5bae60a71138d571d57ac18528d5 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Tue, 1 Nov 2022 06:14:20 +0800
Subject: fix(mark): do not restore view in op-pending mode (#20889)

---
 src/nvim/normal.c | 4 ++++
 1 file changed, 4 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 3c49948b55..a83111136d 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -5697,6 +5697,10 @@ static void nv_gomark(cmdarg_T *cap)
 {
   int name;
   MarkMove flags = jop_flags & JOP_VIEW ? kMarkSetView : 0;  // flags for moving to the mark
+  if (cap->oap->op_type != OP_NOP) {
+    // When there is a pending operator, do not restore the view as this is usually unexpected.
+    flags = 0;
+  }
   MarkMoveRes move_res = 0;  // Result from moving to the mark
   const bool old_KeyTyped = KeyTyped;  // getting file may reset it
 
-- 
cgit 


From d8dbf58b4372d1415e3ca69541fc6fe71890ba53 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Tue, 1 Nov 2022 07:29:55 +0800
Subject: vim-patch:9.0.0821: crash with win_move_statusline() in another
 tabpage (#20894)

vim-patch:86e6717ace4f

Problem:    Crash when using win_move_statusline() in another tab page.
Solution:   Check for valid window pointer. (issue vim/vim#11427)

https://github.com/vim/vim/commit/86e6717ace4f5e00eaeb84b59e3fc92bca548155

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval/funcs.c                |  6 ++++++
 src/nvim/testdir/test_window_cmd.vim | 10 ++++++++++
 2 files changed, 16 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index e08dc2e4a5..17781cf4ef 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -112,6 +112,8 @@ PRAGMA_DIAG_POP
 static char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob");
 static char *e_invalwindow = N_("E957: Invalid window number");
 static char *e_reduceempty = N_("E998: Reduce of an empty %s with no initial value");
+static char e_cannot_resize_window_in_another_tab_page[]
+  = N_("E1308: Cannot resize a window in another tab page");
 
 /// Dummy va_list for passing to vim_snprintf
 ///
@@ -9694,6 +9696,10 @@ static void f_win_move_statusline(typval_T *argvars, typval_T *rettv, EvalFuncDa
   if (wp == NULL || wp->w_floating) {
     return;
   }
+  if (!win_valid(wp)) {
+    emsg(_(e_cannot_resize_window_in_another_tab_page));
+    return;
+  }
 
   offset = (int)tv_get_number(&argvars[1]);
   win_drag_status_line(wp, offset);
diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim
index e01993b99c..902a3791d4 100644
--- a/src/nvim/testdir/test_window_cmd.vim
+++ b/src/nvim/testdir/test_window_cmd.vim
@@ -1459,17 +1459,20 @@ func Test_win_move_statusline()
     call assert_true(id->win_move_statusline(-offset))
     call assert_equal(h, winheight(id))
   endfor
+
   " check that win_move_statusline doesn't error with offsets beyond moving
   " possibility
   call assert_true(win_move_statusline(id, 5000))
   call assert_true(winheight(id) > h)
   call assert_true(win_move_statusline(id, -5000))
   call assert_true(winheight(id) < h)
+
   " check that win_move_statusline returns false for an invalid window
   wincmd =
   let h = winheight(0)
   call assert_false(win_move_statusline(-1, 1))
   call assert_equal(h, winheight(0))
+
   " check that win_move_statusline returns false for a floating window
   let id = nvim_open_win(
         \ 0, 0, #{relative: 'editor', row: 2, col: 2, width: 5, height: 3})
@@ -1477,6 +1480,13 @@ func Test_win_move_statusline()
   call assert_false(win_move_statusline(id, 1))
   call assert_equal(h, winheight(id))
   call nvim_win_close(id, 1)
+
+  " check that using another tabpage fails without crash
+  let id = win_getid()
+  tabnew
+  call assert_fails('call win_move_statusline(id, -1)', 'E1308:')
+  tabclose
+
   %bwipe!
 endfunc
 
-- 
cgit 


From c3aba403c6091a56c517e60beb73d9873d4bb8af Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Tue, 1 Nov 2022 09:14:25 +0800
Subject: refactor: move do_mouse() and its helpers to mouse.c (#20895)

Vim moved it in patch 8.1.2062, and this is required for patch 9.0.0822.
Also change macros in mouse.h to enums.
---
 src/nvim/mouse.c  | 874 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/nvim/mouse.h  |  81 ++---
 src/nvim/normal.c | 862 -----------------------------------------------------
 3 files changed, 918 insertions(+), 899 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 734ece73b4..88dd81da8b 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -4,18 +4,26 @@
 #include 
 
 #include "nvim/ascii.h"
+#include "nvim/buffer.h"
 #include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/cursor.h"
 #include "nvim/diff.h"
 #include "nvim/drawscreen.h"
+#include "nvim/eval.h"
+#include "nvim/ex_docmd.h"
 #include "nvim/fold.h"
+#include "nvim/getchar.h"
 #include "nvim/grid.h"
 #include "nvim/memline.h"
+#include "nvim/menu.h"
 #include "nvim/mouse.h"
 #include "nvim/move.h"
+#include "nvim/ops.h"
+#include "nvim/option.h"
 #include "nvim/os_unix.h"
 #include "nvim/plines.h"
+#include "nvim/search.h"
 #include "nvim/state.h"
 #include "nvim/strings.h"
 #include "nvim/syntax.h"
@@ -31,10 +39,156 @@
 static linenr_T orig_topline = 0;
 static int orig_topfill = 0;
 
+/// Get class of a character for selection: same class means same word.
+/// 0: blank
+/// 1: punctuation groups
+/// 2: normal word character
+/// >2: multi-byte word character.
+static int get_mouse_class(char_u *p)
+{
+  if (MB_BYTE2LEN(p[0]) > 1) {
+    return mb_get_class(p);
+  }
+
+  const int c = *p;
+  if (c == ' ' || c == '\t') {
+    return 0;
+  }
+  if (vim_iswordc(c)) {
+    return 2;
+  }
+
+  // There are a few special cases where we want certain combinations of
+  // characters to be considered as a single word.  These are things like
+  // "->", "/ *", "*=", "+=", "&=", "<=", ">=", "!=" etc.  Otherwise, each
+  // character is in its own class.
+  if (c != NUL && vim_strchr("-+*/%<>&|^!=", c) != NULL) {
+    return 1;
+  }
+  return c;
+}
+
+/// Move "pos" back to the start of the word it's in.
+static void find_start_of_word(pos_T *pos)
+{
+  char_u *line;
+  int cclass;
+  int col;
+
+  line = (char_u *)ml_get(pos->lnum);
+  cclass = get_mouse_class(line + pos->col);
+
+  while (pos->col > 0) {
+    col = pos->col - 1;
+    col -= utf_head_off((char *)line, (char *)line + col);
+    if (get_mouse_class(line + col) != cclass) {
+      break;
+    }
+    pos->col = col;
+  }
+}
+
+/// Move "pos" forward to the end of the word it's in.
+/// When 'selection' is "exclusive", the position is just after the word.
+static void find_end_of_word(pos_T *pos)
+{
+  char_u *line;
+  int cclass;
+  int col;
+
+  line = (char_u *)ml_get(pos->lnum);
+  if (*p_sel == 'e' && pos->col > 0) {
+    pos->col--;
+    pos->col -= utf_head_off((char *)line, (char *)line + pos->col);
+  }
+  cclass = get_mouse_class(line + pos->col);
+  while (line[pos->col] != NUL) {
+    col = pos->col + utfc_ptr2len((char *)line + pos->col);
+    if (get_mouse_class(line + col) != cclass) {
+      if (*p_sel == 'e') {
+        pos->col = col;
+      }
+      break;
+    }
+    pos->col = col;
+  }
+}
+
+/// Move the current tab to tab in same column as mouse or to end of the
+/// tabline if there is no tab there.
+static void move_tab_to_mouse(void)
+{
+  int tabnr = tab_page_click_defs[mouse_col].tabnr;
+  if (tabnr <= 0) {
+    tabpage_move(9999);
+  } else if (tabnr < tabpage_index(curtab)) {
+    tabpage_move(tabnr - 1);
+  } else {
+    tabpage_move(tabnr);
+  }
+}
+
+/// Call click definition function for column "col" in the "click_defs" array for button
+/// "which_button".
+static void call_click_def_func(StlClickDefinition *click_defs, int col, int which_button)
+{
+  typval_T argv[] = {
+    {
+      .v_lock = VAR_FIXED,
+      .v_type = VAR_NUMBER,
+      .vval = {
+        .v_number = (varnumber_T)click_defs[col].tabnr
+      },
+    },
+    {
+      .v_lock = VAR_FIXED,
+      .v_type = VAR_NUMBER,
+      .vval = {
+        .v_number = ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK
+                     ? 4
+                     : ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK
+                        ? 3
+                        : ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK
+                           ? 2
+                           : 1)))
+      },
+    },
+    {
+      .v_lock = VAR_FIXED,
+      .v_type = VAR_STRING,
+      .vval = {
+        .v_string = (which_button == MOUSE_LEFT
+                     ? "l"
+                     : (which_button == MOUSE_RIGHT
+                        ? "r"
+                        : (which_button == MOUSE_MIDDLE
+                           ? "m"
+                           : "?")))
+      },
+    },
+    {
+      .v_lock = VAR_FIXED,
+      .v_type = VAR_STRING,
+      .vval = {
+        .v_string = (char[]) {
+          (char)(mod_mask & MOD_MASK_SHIFT ? 's' : ' '),
+          (char)(mod_mask & MOD_MASK_CTRL ? 'c' : ' '),
+          (char)(mod_mask & MOD_MASK_ALT ? 'a' : ' '),
+          (char)(mod_mask & MOD_MASK_META ? 'm' : ' '),
+          NUL
+        }
+      },
+    }
+  };
+  typval_T rettv;
+  (void)call_vim_function(click_defs[col].func, ARRAY_SIZE(argv), argv, &rettv);
+  tv_clear(&rettv);
+}
+
 /// Translate window coordinates to buffer position without any side effects.
 /// Returns IN_BUFFER and sets "mpos->col" to the column when in buffer text.
 /// The column is one for the first column.
-int get_fpos_of_mouse(pos_T *mpos)
+static int get_fpos_of_mouse(pos_T *mpos)
 {
   int grid = mouse_grid;
   int row = mouse_row;
@@ -73,6 +227,715 @@ int get_fpos_of_mouse(pos_T *mpos)
   return IN_BUFFER;
 }
 
+/// Do the appropriate action for the current mouse click in the current mode.
+/// Not used for Command-line mode.
+///
+/// Normal and Visual Mode:
+/// event         modi-  position      visual       change   action
+///               fier   cursor                     window
+/// left press     -     yes         end             yes
+/// left press     C     yes         end             yes     "^]" (2)
+/// left press     S     yes     end (popup: extend) yes     "*" (2)
+/// left drag      -     yes     start if moved      no
+/// left relse     -     yes     start if moved      no
+/// middle press   -     yes      if not active      no      put register
+/// middle press   -     yes      if active          no      yank and put
+/// right press    -     yes     start or extend     yes
+/// right press    S     yes     no change           yes     "#" (2)
+/// right drag     -     yes     extend              no
+/// right relse    -     yes     extend              no
+///
+/// Insert or Replace Mode:
+/// event         modi-  position      visual       change   action
+///               fier   cursor                     window
+/// left press     -     yes     (cannot be active)  yes
+/// left press     C     yes     (cannot be active)  yes     "CTRL-O^]" (2)
+/// left press     S     yes     (cannot be active)  yes     "CTRL-O*" (2)
+/// left drag      -     yes     start or extend (1) no      CTRL-O (1)
+/// left relse     -     yes     start or extend (1) no      CTRL-O (1)
+/// middle press   -     no      (cannot be active)  no      put register
+/// right press    -     yes     start or extend     yes     CTRL-O
+/// right press    S     yes     (cannot be active)  yes     "CTRL-O#" (2)
+///
+/// (1) only if mouse pointer moved since press
+/// (2) only if click is in same buffer
+///
+/// @param oap        operator argument, can be NULL
+/// @param c          K_LEFTMOUSE, etc
+/// @param dir        Direction to 'put' if necessary
+/// @param fixindent  PUT_FIXINDENT if fixing indent necessary
+///
+/// @return           true if start_arrow() should be called for edit mode.
+bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
+{
+  static bool got_click = false;        // got a click some time back
+
+  int which_button;             // MOUSE_LEFT, _MIDDLE or _RIGHT
+  bool is_click;                // If false it's a drag or release event
+  bool is_drag;                 // If true it's a drag event
+  int jump_flags = 0;           // flags for jump_to_mouse()
+  pos_T start_visual;
+  bool moved;                   // Has cursor moved?
+  bool in_winbar;               // mouse in window bar
+  bool in_status_line;          // mouse in status line
+  static bool in_tab_line = false;   // mouse clicked in tab line
+  bool in_sep_line;             // mouse in vertical separator line
+  int c1, c2;
+  pos_T save_cursor;
+  win_T *old_curwin = curwin;
+  static pos_T orig_cursor;
+  colnr_T leftcol, rightcol;
+  pos_T end_visual;
+  long diff;
+  int old_active = VIsual_active;
+  int old_mode = VIsual_mode;
+  int regname;
+
+  save_cursor = curwin->w_cursor;
+
+  for (;;) {
+    which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
+    if (is_drag) {
+      // If the next character is the same mouse event then use that
+      // one. Speeds up dragging the status line.
+      // Note: Since characters added to the stuff buffer in the code
+      // below need to come before the next character, do not do this
+      // when the current character was stuffed.
+      if (!KeyStuffed && vpeekc() != NUL) {
+        int nc;
+        int save_mouse_grid = mouse_grid;
+        int save_mouse_row = mouse_row;
+        int save_mouse_col = mouse_col;
+
+        // Need to get the character, peeking doesn't get the actual one.
+        nc = safe_vgetc();
+        if (c == nc) {
+          continue;
+        }
+        vungetc(nc);
+        mouse_grid = save_mouse_grid;
+        mouse_row = save_mouse_row;
+        mouse_col = save_mouse_col;
+      }
+    }
+    break;
+  }
+
+  if (c == K_MOUSEMOVE) {
+    // Mouse moved without a button pressed.
+    return false;
+  }
+
+  // Ignore drag and release events if we didn't get a click.
+  if (is_click) {
+    got_click = true;
+  } else {
+    if (!got_click) {                   // didn't get click, ignore
+      return false;
+    }
+    if (!is_drag) {                     // release, reset got_click
+      got_click = false;
+      if (in_tab_line) {
+        in_tab_line = false;
+        return false;
+      }
+    }
+  }
+
+  // CTRL right mouse button does CTRL-T
+  if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT) {
+    if (State & MODE_INSERT) {
+      stuffcharReadbuff(Ctrl_O);
+    }
+    if (count > 1) {
+      stuffnumReadbuff(count);
+    }
+    stuffcharReadbuff(Ctrl_T);
+    got_click = false;                  // ignore drag&release now
+    return false;
+  }
+
+  // CTRL only works with left mouse button
+  if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT) {
+    return false;
+  }
+
+  // When a modifier is down, ignore drag and release events, as well as
+  // multiple clicks and the middle mouse button.
+  // Accept shift-leftmouse drags when 'mousemodel' is "popup.*".
+  if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT
+                   | MOD_MASK_META))
+      && (!is_click
+          || (mod_mask & MOD_MASK_MULTI_CLICK)
+          || which_button == MOUSE_MIDDLE)
+      && !((mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))
+           && mouse_model_popup()
+           && which_button == MOUSE_LEFT)
+      && !((mod_mask & MOD_MASK_ALT)
+           && !mouse_model_popup()
+           && which_button == MOUSE_RIGHT)) {
+    return false;
+  }
+
+  // If the button press was used as the movement command for an operator (eg
+  // "d"), or it is the middle button that is held down, ignore
+  // drag/release events.
+  if (!is_click && which_button == MOUSE_MIDDLE) {
+    return false;
+  }
+
+  if (oap != NULL) {
+    regname = oap->regname;
+  } else {
+    regname = 0;
+  }
+
+  // Middle mouse button does a 'put' of the selected text
+  if (which_button == MOUSE_MIDDLE) {
+    if (State == MODE_NORMAL) {
+      // If an operator was pending, we don't know what the user wanted to do.
+      // Go back to normal mode: Clear the operator and beep().
+      if (oap != NULL && oap->op_type != OP_NOP) {
+        clearopbeep(oap);
+        return false;
+      }
+
+      // If visual was active, yank the highlighted text and put it
+      // before the mouse pointer position.
+      // In Select mode replace the highlighted text with the clipboard.
+      if (VIsual_active) {
+        if (VIsual_select) {
+          stuffcharReadbuff(Ctrl_G);
+          stuffReadbuff("\"+p");
+        } else {
+          stuffcharReadbuff('y');
+          stuffcharReadbuff(K_MIDDLEMOUSE);
+        }
+        return false;
+      }
+      // The rest is below jump_to_mouse()
+    } else if ((State & MODE_INSERT) == 0) {
+      return false;
+    }
+
+    // Middle click in insert mode doesn't move the mouse, just insert the
+    // contents of a register.  '.' register is special, can't insert that
+    // with do_put().
+    // Also paste at the cursor if the current mode isn't in 'mouse' (only
+    // happens for the GUI).
+    if ((State & MODE_INSERT)) {
+      if (regname == '.') {
+        insert_reg(regname, true);
+      } else {
+        if (regname == 0 && eval_has_provider("clipboard")) {
+          regname = '*';
+        }
+        if ((State & REPLACE_FLAG) && !yank_register_mline(regname)) {
+          insert_reg(regname, true);
+        } else {
+          do_put(regname, NULL, BACKWARD, 1L,
+                 (fixindent ? PUT_FIXINDENT : 0) | PUT_CURSEND);
+
+          // Repeat it with CTRL-R CTRL-O r or CTRL-R CTRL-P r
+          AppendCharToRedobuff(Ctrl_R);
+          AppendCharToRedobuff(fixindent ? Ctrl_P : Ctrl_O);
+          AppendCharToRedobuff(regname == 0 ? '"' : regname);
+        }
+      }
+      return false;
+    }
+  }
+
+  // When dragging or button-up stay in the same window.
+  if (!is_click) {
+    jump_flags |= MOUSE_FOCUS | MOUSE_DID_MOVE;
+  }
+
+  start_visual.lnum = 0;
+
+  // Check for clicking in the tab page line.
+  if (mouse_grid <= 1 && mouse_row == 0 && firstwin->w_winrow > 0) {
+    if (is_drag) {
+      if (in_tab_line) {
+        move_tab_to_mouse();
+      }
+      return false;
+    }
+
+    // click in a tab selects that tab page
+    if (is_click && cmdwin_type == 0 && mouse_col < Columns) {
+      in_tab_line = true;
+      c1 = tab_page_click_defs[mouse_col].tabnr;
+      switch (tab_page_click_defs[mouse_col].type) {
+      case kStlClickDisabled:
+        break;
+      case kStlClickTabClose: {
+        tabpage_T *tp;
+
+        // Close the current or specified tab page.
+        if (c1 == 999) {
+          tp = curtab;
+        } else {
+          tp = find_tabpage(c1);
+        }
+        if (tp == curtab) {
+          if (first_tabpage->tp_next != NULL) {
+            tabpage_close(false);
+          }
+        } else if (tp != NULL) {
+          tabpage_close_other(tp, false);
+        }
+        break;
+      }
+      case kStlClickTabSwitch:
+        if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
+          // double click opens new page
+          end_visual_mode();
+          tabpage_new();
+          tabpage_move(c1 == 0 ? 9999 : c1 - 1);
+        } else {
+          // Go to specified tab page, or next one if not clicking
+          // on a label.
+          goto_tabpage(c1);
+
+          // It's like clicking on the status line of a window.
+          if (curwin != old_curwin) {
+            end_visual_mode();
+          }
+        }
+        break;
+      case kStlClickFuncRun:
+        call_click_def_func(tab_page_click_defs, mouse_col, which_button);
+        break;
+      }
+    }
+    return true;
+  } else if (is_drag && in_tab_line) {
+    move_tab_to_mouse();
+    return false;
+  }
+
+  // When 'mousemodel' is "popup" or "popup_setpos", translate mouse events:
+  // right button up   -> pop-up menu
+  // shift-left button -> right button
+  // alt-left button   -> alt-right button
+  if (mouse_model_popup()) {
+    if (which_button == MOUSE_RIGHT
+        && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
+      if (!is_click) {
+        // Ignore right button release events, only shows the popup
+        // menu on the button down event.
+        return false;
+      }
+      jump_flags = 0;
+      if (strcmp(p_mousem, "popup_setpos") == 0) {
+        // First set the cursor position before showing the popup
+        // menu.
+        if (VIsual_active) {
+          pos_T m_pos;
+          // set MOUSE_MAY_STOP_VIS if we are outside the
+          // selection or the current window (might have false
+          // negative here)
+          if (mouse_row < curwin->w_winrow
+              || mouse_row > (curwin->w_winrow + curwin->w_height)) {
+            jump_flags = MOUSE_MAY_STOP_VIS;
+          } else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER) {
+            jump_flags = MOUSE_MAY_STOP_VIS;
+          } else {
+            if (VIsual_mode == 'V') {
+              if ((curwin->w_cursor.lnum <= VIsual.lnum
+                   && (m_pos.lnum < curwin->w_cursor.lnum || VIsual.lnum < m_pos.lnum))
+                  || (VIsual.lnum < curwin->w_cursor.lnum
+                      && (m_pos.lnum < VIsual.lnum || curwin->w_cursor.lnum < m_pos.lnum))) {
+                jump_flags = MOUSE_MAY_STOP_VIS;
+              }
+            } else if ((ltoreq(curwin->w_cursor, VIsual)
+                        && (lt(m_pos, curwin->w_cursor) || lt(VIsual, m_pos)))
+                       || (lt(VIsual, curwin->w_cursor)
+                           && (lt(m_pos, VIsual) || lt(curwin->w_cursor, m_pos)))) {
+              jump_flags = MOUSE_MAY_STOP_VIS;
+            } else if (VIsual_mode == Ctrl_V) {
+              getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
+              getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL);
+              if (m_pos.col < leftcol || m_pos.col > rightcol) {
+                jump_flags = MOUSE_MAY_STOP_VIS;
+              }
+            }
+          }
+        } else {
+          jump_flags = MOUSE_MAY_STOP_VIS;
+        }
+      }
+      if (jump_flags) {
+        jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
+        redraw_curbuf_later(VIsual_active ? UPD_INVERTED : UPD_VALID);
+        update_screen();
+        setcursor();
+        ui_flush();  // Update before showing popup menu
+      }
+      show_popupmenu();
+      got_click = false;  // ignore release events
+      return (jump_flags & CURSOR_MOVED) != 0;
+    }
+    if (which_button == MOUSE_LEFT
+        && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))) {
+      which_button = MOUSE_RIGHT;
+      mod_mask &= ~MOD_MASK_SHIFT;
+    }
+  }
+
+  if ((State & (MODE_NORMAL | MODE_INSERT))
+      && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
+    if (which_button == MOUSE_LEFT) {
+      if (is_click) {
+        // stop Visual mode for a left click in a window, but not when on a status line
+        if (VIsual_active) {
+          jump_flags |= MOUSE_MAY_STOP_VIS;
+        }
+      } else {
+        jump_flags |= MOUSE_MAY_VIS;
+      }
+    } else if (which_button == MOUSE_RIGHT) {
+      if (is_click && VIsual_active) {
+        // Remember the start and end of visual before moving the cursor.
+        if (lt(curwin->w_cursor, VIsual)) {
+          start_visual = curwin->w_cursor;
+          end_visual = VIsual;
+        } else {
+          start_visual = VIsual;
+          end_visual = curwin->w_cursor;
+        }
+      }
+      jump_flags |= MOUSE_FOCUS;
+      jump_flags |= MOUSE_MAY_VIS;
+    }
+  }
+
+  // If an operator is pending, ignore all drags and releases until the next mouse click.
+  if (!is_drag && oap != NULL && oap->op_type != OP_NOP) {
+    got_click = false;
+    oap->motion_type = kMTCharWise;
+  }
+
+  // When releasing the button let jump_to_mouse() know.
+  if (!is_click && !is_drag) {
+    jump_flags |= MOUSE_RELEASED;
+  }
+
+  // JUMP!
+  jump_flags = jump_to_mouse(jump_flags,
+                             oap == NULL ? NULL : &(oap->inclusive),
+                             which_button);
+
+  moved = (jump_flags & CURSOR_MOVED);
+  in_winbar = (jump_flags & MOUSE_WINBAR);
+  in_status_line = (jump_flags & IN_STATUS_LINE);
+  in_sep_line = (jump_flags & IN_SEP_LINE);
+
+  if ((in_winbar || in_status_line) && is_click) {
+    // Handle click event on window bar or status lin
+    int click_grid = mouse_grid;
+    int click_row = mouse_row;
+    int click_col = mouse_col;
+    win_T *wp = mouse_find_win(&click_grid, &click_row, &click_col);
+    if (wp == NULL) {
+      return false;
+    }
+
+    StlClickDefinition *click_defs = in_status_line ? wp->w_status_click_defs
+                                                    : wp->w_winbar_click_defs;
+
+    if (in_status_line && global_stl_height() > 0) {
+      // global statusline is displayed for the current window,
+      // and spans the whole screen.
+      click_defs = curwin->w_status_click_defs;
+      click_col = mouse_col;
+    }
+
+    if (click_defs != NULL) {
+      switch (click_defs[click_col].type) {
+      case kStlClickDisabled:
+        break;
+      case kStlClickFuncRun:
+        call_click_def_func(click_defs, click_col, which_button);
+        break;
+      default:
+        assert(false && "winbar and statusline only support %@ for clicks");
+        break;
+      }
+    }
+
+    return false;
+  } else if (in_winbar) {
+    // A drag or release event in the window bar has no side effects.
+    return false;
+  }
+
+  // When jumping to another window, clear a pending operator.  That's a bit
+  // friendlier than beeping and not jumping to that window.
+  if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP) {
+    clearop(oap);
+  }
+
+  if (mod_mask == 0
+      && !is_drag
+      && (jump_flags & (MOUSE_FOLD_CLOSE | MOUSE_FOLD_OPEN))
+      && which_button == MOUSE_LEFT) {
+    // open or close a fold at this line
+    if (jump_flags & MOUSE_FOLD_OPEN) {
+      openFold(curwin->w_cursor, 1L);
+    } else {
+      closeFold(curwin->w_cursor, 1L);
+    }
+    // don't move the cursor if still in the same window
+    if (curwin == old_curwin) {
+      curwin->w_cursor = save_cursor;
+    }
+  }
+
+  // Set global flag that we are extending the Visual area with mouse dragging;
+  // temporarily minimize 'scrolloff'.
+  if (VIsual_active && is_drag && get_scrolloff_value(curwin)) {
+    // In the very first line, allow scrolling one line
+    if (mouse_row == 0) {
+      mouse_dragging = 2;
+    } else {
+      mouse_dragging = 1;
+    }
+  }
+
+  // When dragging the mouse above the window, scroll down.
+  if (is_drag && mouse_row < 0 && !in_status_line) {
+    scroll_redraw(false, 1L);
+    mouse_row = 0;
+  }
+
+  if (start_visual.lnum) {              // right click in visual mode
+    // When ALT is pressed make Visual mode blockwise.
+    if (mod_mask & MOD_MASK_ALT) {
+      VIsual_mode = Ctrl_V;
+    }
+
+    // In Visual-block mode, divide the area in four, pick up the corner
+    // that is in the quarter that the cursor is in.
+    if (VIsual_mode == Ctrl_V) {
+      getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
+      if (curwin->w_curswant > (leftcol + rightcol) / 2) {
+        end_visual.col = leftcol;
+      } else {
+        end_visual.col = rightcol;
+      }
+      if (curwin->w_cursor.lnum >=
+          (start_visual.lnum + end_visual.lnum) / 2) {
+        end_visual.lnum = start_visual.lnum;
+      }
+
+      // move VIsual to the right column
+      start_visual = curwin->w_cursor;              // save the cursor pos
+      curwin->w_cursor = end_visual;
+      coladvance(end_visual.col);
+      VIsual = curwin->w_cursor;
+      curwin->w_cursor = start_visual;              // restore the cursor
+    } else {
+      // If the click is before the start of visual, change the start.
+      // If the click is after the end of visual, change the end.  If
+      // the click is inside the visual, change the closest side.
+      if (lt(curwin->w_cursor, start_visual)) {
+        VIsual = end_visual;
+      } else if (lt(end_visual, curwin->w_cursor)) {
+        VIsual = start_visual;
+      } else {
+        // In the same line, compare column number
+        if (end_visual.lnum == start_visual.lnum) {
+          if (curwin->w_cursor.col - start_visual.col >
+              end_visual.col - curwin->w_cursor.col) {
+            VIsual = start_visual;
+          } else {
+            VIsual = end_visual;
+          }
+        } else {
+          // In different lines, compare line number
+          diff = (curwin->w_cursor.lnum - start_visual.lnum) -
+                 (end_visual.lnum - curwin->w_cursor.lnum);
+
+          if (diff > 0) {                       // closest to end
+            VIsual = start_visual;
+          } else if (diff < 0) {                   // closest to start
+            VIsual = end_visual;
+          } else {                                // in the middle line
+            if (curwin->w_cursor.col <
+                (start_visual.col + end_visual.col) / 2) {
+              VIsual = end_visual;
+            } else {
+              VIsual = start_visual;
+            }
+          }
+        }
+      }
+    }
+  } else if ((State & MODE_INSERT) && VIsual_active) {
+    // If Visual mode started in insert mode, execute "CTRL-O"
+    stuffcharReadbuff(Ctrl_O);
+  }
+
+  // Middle mouse click: Put text before cursor.
+  if (which_button == MOUSE_MIDDLE) {
+    if (regname == 0 && eval_has_provider("clipboard")) {
+      regname = '*';
+    }
+    if (yank_register_mline(regname)) {
+      if (mouse_past_bottom) {
+        dir = FORWARD;
+      }
+    } else if (mouse_past_eol) {
+      dir = FORWARD;
+    }
+
+    if (fixindent) {
+      c1 = (dir == BACKWARD) ? '[' : ']';
+      c2 = 'p';
+    } else {
+      c1 = (dir == FORWARD) ? 'p' : 'P';
+      c2 = NUL;
+    }
+    prep_redo(regname, count, NUL, c1, NUL, c2, NUL);
+
+    // Remember where the paste started, so in edit() Insstart can be set to this position
+    if (restart_edit != 0) {
+      where_paste_started = curwin->w_cursor;
+    }
+    do_put(regname, NULL, dir, count,
+           (fixindent ? PUT_FIXINDENT : 0)| PUT_CURSEND);
+  } else if (((mod_mask & MOD_MASK_CTRL) || (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
+             && bt_quickfix(curbuf)) {
+    // Ctrl-Mouse click or double click in a quickfix window jumps to the
+    // error under the mouse pointer.
+    if (curwin->w_llist_ref == NULL) {          // quickfix window
+      do_cmdline_cmd(".cc");
+    } else {                                    // location list window
+      do_cmdline_cmd(".ll");
+    }
+    got_click = false;                  // ignore drag&release now
+  } else if ((mod_mask & MOD_MASK_CTRL)
+             || (curbuf->b_help && (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)) {
+    // Ctrl-Mouse click (or double click in a help window) jumps to the tag
+    // under the mouse pointer.
+    if (State & MODE_INSERT) {
+      stuffcharReadbuff(Ctrl_O);
+    }
+    stuffcharReadbuff(Ctrl_RSB);
+    got_click = false;                  // ignore drag&release now
+  } else if ((mod_mask & MOD_MASK_SHIFT)) {
+    // Shift-Mouse click searches for the next occurrence of the word under
+    // the mouse pointer
+    if (State & MODE_INSERT || (VIsual_active && VIsual_select)) {
+      stuffcharReadbuff(Ctrl_O);
+    }
+    if (which_button == MOUSE_LEFT) {
+      stuffcharReadbuff('*');
+    } else {  // MOUSE_RIGHT
+      stuffcharReadbuff('#');
+    }
+  } else if (in_status_line || in_sep_line) {
+    // Do nothing if on status line or vertical separator
+    // Handle double clicks otherwise
+  } else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (MODE_NORMAL | MODE_INSERT))) {
+    if (is_click || !VIsual_active) {
+      if (VIsual_active) {
+        orig_cursor = VIsual;
+      } else {
+        VIsual = curwin->w_cursor;
+        orig_cursor = VIsual;
+        VIsual_active = true;
+        VIsual_reselect = true;
+        // start Select mode if 'selectmode' contains "mouse"
+        may_start_select('o');
+        setmouse();
+      }
+      if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
+        // Double click with ALT pressed makes it blockwise.
+        if (mod_mask & MOD_MASK_ALT) {
+          VIsual_mode = Ctrl_V;
+        } else {
+          VIsual_mode = 'v';
+        }
+      } else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK) {
+        VIsual_mode = 'V';
+      } else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK) {
+        VIsual_mode = Ctrl_V;
+      }
+    }
+    // A double click selects a word or a block.
+    if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
+      pos_T *pos = NULL;
+      int gc;
+
+      if (is_click) {
+        // If the character under the cursor (skipping white space) is
+        // not a word character, try finding a match and select a (),
+        // {}, [], #if/#endif, etc. block.
+        end_visual = curwin->w_cursor;
+        while (gc = gchar_pos(&end_visual), ascii_iswhite(gc)) {
+          inc(&end_visual);
+        }
+        if (oap != NULL) {
+          oap->motion_type = kMTCharWise;
+        }
+        if (oap != NULL
+            && VIsual_mode == 'v'
+            && !vim_iswordc(gchar_pos(&end_visual))
+            && equalpos(curwin->w_cursor, VIsual)
+            && (pos = findmatch(oap, NUL)) != NULL) {
+          curwin->w_cursor = *pos;
+          if (oap->motion_type == kMTLineWise) {
+            VIsual_mode = 'V';
+          } else if (*p_sel == 'e') {
+            if (lt(curwin->w_cursor, VIsual)) {
+              VIsual.col++;
+            } else {
+              curwin->w_cursor.col++;
+            }
+          }
+        }
+      }
+
+      if (pos == NULL && (is_click || is_drag)) {
+        // When not found a match or when dragging: extend to include a word.
+        if (lt(curwin->w_cursor, orig_cursor)) {
+          find_start_of_word(&curwin->w_cursor);
+          find_end_of_word(&VIsual);
+        } else {
+          find_start_of_word(&VIsual);
+          if (*p_sel == 'e' && *get_cursor_pos_ptr() != NUL) {
+            curwin->w_cursor.col +=
+              utfc_ptr2len(get_cursor_pos_ptr());
+          }
+          find_end_of_word(&curwin->w_cursor);
+        }
+      }
+      curwin->w_set_curswant = true;
+    }
+    if (is_click) {
+      redraw_curbuf_later(UPD_INVERTED);  // update the inversion
+    }
+  } else if (VIsual_active && !old_active) {
+    if (mod_mask & MOD_MASK_ALT) {
+      VIsual_mode = Ctrl_V;
+    } else {
+      VIsual_mode = 'v';
+    }
+  }
+
+  // If Visual mode changed show it later.
+  if ((!VIsual_active && old_active && mode_displayed)
+      || (VIsual_active && p_smd && msg_silent == 0
+          && (!old_active || VIsual_mode != old_mode))) {
+    redraw_cmdline = true;
+  }
+
+  return moved;
+}
+
 /// Return true if "c" is a mouse key.
 bool is_mouse_key(int c)
 {
@@ -99,6 +962,13 @@ bool is_mouse_key(int c)
          || c == K_X2DRAG
          || c == K_X2RELEASE;
 }
+
+/// @return  true when 'mousemodel' is set to "popup" or "popup_setpos".
+static bool mouse_model_popup(void)
+{
+  return p_mousem[0] == 'p';
+}
+
 /// Move the cursor to the specified row and column on the screen.
 /// Change current window if necessary. Returns an integer with the
 /// CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
@@ -653,7 +1523,7 @@ void setmouse(void)
 
 // Set orig_topline.  Used when jumping to another window, so that a double
 // click still works.
-void set_mouse_topline(win_T *wp)
+static void set_mouse_topline(win_T *wp)
 {
   orig_topline = wp->w_topline;
   orig_topfill = wp->w_topfill;
diff --git a/src/nvim/mouse.h b/src/nvim/mouse.h
index 21ff56bbbc..d493a479b1 100644
--- a/src/nvim/mouse.h
+++ b/src/nvim/mouse.h
@@ -4,42 +4,53 @@
 #include 
 
 #include "nvim/buffer_defs.h"
+#include "nvim/normal.h"
 #include "nvim/vim.h"
-
-// jump_to_mouse() returns one of first four these values, possibly with
-// some of the other three added.
-#define IN_UNKNOWN             0
-#define IN_BUFFER              1
-#define IN_STATUS_LINE         2       // on status or command line
-#define IN_SEP_LINE            4       // on vertical separator line
-#define IN_OTHER_WIN           8       // in other window but can't go there
-#define CURSOR_MOVED           0x100
-#define MOUSE_FOLD_CLOSE       0x200   // clicked on '-' in fold column
-#define MOUSE_FOLD_OPEN        0x400   // clicked on '+' in fold column
-#define MOUSE_WINBAR           0x800   // in window toolbar
-
-// flags for jump_to_mouse()
-#define MOUSE_FOCUS            0x01    // need to stay in this window
-#define MOUSE_MAY_VIS          0x02    // may start Visual mode
-#define MOUSE_DID_MOVE         0x04    // only act when mouse has moved
-#define MOUSE_SETPOS           0x08    // only set current mouse position
-#define MOUSE_MAY_STOP_VIS     0x10    // may stop Visual mode
-#define MOUSE_RELEASED         0x20    // button was released
-
-// Codes for mouse button events in lower three bits:
-#define MOUSE_LEFT     0x00
-#define MOUSE_MIDDLE   0x01
-#define MOUSE_RIGHT    0x02
-#define MOUSE_RELEASE  0x03
-
-#define MOUSE_X1       0x300  // Mouse-button X1 (6th)
-#define MOUSE_X2       0x400  // Mouse-button X2
-
-// Direction for nv_mousescroll() and ins_mousescroll()
-#define MSCR_DOWN       0     // DOWN must be false
-#define MSCR_UP         1
-#define MSCR_LEFT       (-1)
-#define MSCR_RIGHT      (-2)
+#include "nvim/window.h"
+
+/// jump_to_mouse() returns one of first five these values, possibly with
+/// some of the other four added.
+enum {
+  IN_UNKNOWN       = 0,
+  IN_BUFFER        = 1,
+  IN_STATUS_LINE   = 2,      ///< on status or command line
+  IN_SEP_LINE      = 4,      ///< on vertical separator line
+  IN_OTHER_WIN     = 8,      ///< in other window but can't go there
+  CURSOR_MOVED     = 0x100,
+  MOUSE_FOLD_CLOSE = 0x200,  ///< clicked on '-' in fold column
+  MOUSE_FOLD_OPEN  = 0x400,  ///< clicked on '+' in fold column
+  MOUSE_WINBAR     = 0x800,  ///< in window toolbar
+};
+
+/// flags for jump_to_mouse()
+enum {
+  MOUSE_FOCUS        = 0x01,  ///< need to stay in this window
+  MOUSE_MAY_VIS      = 0x02,  ///< may start Visual mode
+  MOUSE_DID_MOVE     = 0x04,  ///< only act when mouse has moved
+  MOUSE_SETPOS       = 0x08,  ///< only set current mouse position
+  MOUSE_MAY_STOP_VIS = 0x10,  ///< may stop Visual mode
+  MOUSE_RELEASED     = 0x20,  ///< button was released
+};
+
+enum {
+  // Codes for mouse button events in lower three bits:
+  MOUSE_LEFT    = 0x00,
+  MOUSE_MIDDLE  = 0x01,
+  MOUSE_RIGHT   = 0x02,
+  MOUSE_RELEASE = 0x03,
+
+  // mouse buttons that are handled like a key press
+  MOUSE_X1 = 0x300,  ///< Mouse-button X1 (6th)
+  MOUSE_X2 = 0x400,  ///< Mouse-button X2
+};
+
+/// Direction for nv_mousescroll() and ins_mousescroll()
+enum {
+  MSCR_DOWN  = 0,  ///< DOWN must be false
+  MSCR_UP    = 1,
+  MSCR_LEFT  = -1,
+  MSCR_RIGHT = -2,
+};
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "mouse.h.generated.h"
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index a83111136d..6bddd34367 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -45,7 +45,6 @@
 #include "nvim/mark.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
-#include "nvim/menu.h"
 #include "nvim/message.h"
 #include "nvim/mouse.h"
 #include "nvim/move.h"
@@ -1434,861 +1433,6 @@ static void set_vcount_ca(cmdarg_T *cap, bool *set_prevcount)
   *set_prevcount = false;    // only set v:prevcount once
 }
 
-/// Move the current tab to tab in same column as mouse or to end of the
-/// tabline if there is no tab there.
-static void move_tab_to_mouse(void)
-{
-  int tabnr = tab_page_click_defs[mouse_col].tabnr;
-  if (tabnr <= 0) {
-    tabpage_move(9999);
-  } else if (tabnr < tabpage_index(curtab)) {
-    tabpage_move(tabnr - 1);
-  } else {
-    tabpage_move(tabnr);
-  }
-}
-
-/// Call click definition function for column "col" in the "click_defs" array for button
-/// "which_button".
-static void call_click_def_func(StlClickDefinition *click_defs, int col, int which_button)
-{
-  typval_T argv[] = {
-    {
-      .v_lock = VAR_FIXED,
-      .v_type = VAR_NUMBER,
-      .vval = {
-        .v_number = (varnumber_T)click_defs[col].tabnr
-      },
-    },
-    {
-      .v_lock = VAR_FIXED,
-      .v_type = VAR_NUMBER,
-      .vval = {
-        .v_number = ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK
-                     ? 4
-                     : ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK
-                        ? 3
-                        : ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK
-                           ? 2
-                           : 1)))
-      },
-    },
-    {
-      .v_lock = VAR_FIXED,
-      .v_type = VAR_STRING,
-      .vval = {
-        .v_string = (which_button == MOUSE_LEFT
-                     ? "l"
-                     : (which_button == MOUSE_RIGHT
-                        ? "r"
-                        : (which_button == MOUSE_MIDDLE
-                           ? "m"
-                           : "?")))
-      },
-    },
-    {
-      .v_lock = VAR_FIXED,
-      .v_type = VAR_STRING,
-      .vval = {
-        .v_string = (char[]) {
-          (char)(mod_mask & MOD_MASK_SHIFT ? 's' : ' '),
-          (char)(mod_mask & MOD_MASK_CTRL ? 'c' : ' '),
-          (char)(mod_mask & MOD_MASK_ALT ? 'a' : ' '),
-          (char)(mod_mask & MOD_MASK_META ? 'm' : ' '),
-          NUL
-        }
-      },
-    }
-  };
-  typval_T rettv;
-  (void)call_vim_function(click_defs[col].func, ARRAY_SIZE(argv), argv, &rettv);
-  tv_clear(&rettv);
-}
-
-/// Do the appropriate action for the current mouse click in the current mode.
-/// Not used for Command-line mode.
-///
-/// Normal and Visual Mode:
-/// event         modi-  position      visual       change   action
-///               fier   cursor                     window
-/// left press     -     yes         end             yes
-/// left press     C     yes         end             yes     "^]" (2)
-/// left press     S     yes     end (popup: extend) yes     "*" (2)
-/// left drag      -     yes     start if moved      no
-/// left relse     -     yes     start if moved      no
-/// middle press   -     yes      if not active      no      put register
-/// middle press   -     yes      if active          no      yank and put
-/// right press    -     yes     start or extend     yes
-/// right press    S     yes     no change           yes     "#" (2)
-/// right drag     -     yes     extend              no
-/// right relse    -     yes     extend              no
-///
-/// Insert or Replace Mode:
-/// event         modi-  position      visual       change   action
-///               fier   cursor                     window
-/// left press     -     yes     (cannot be active)  yes
-/// left press     C     yes     (cannot be active)  yes     "CTRL-O^]" (2)
-/// left press     S     yes     (cannot be active)  yes     "CTRL-O*" (2)
-/// left drag      -     yes     start or extend (1) no      CTRL-O (1)
-/// left relse     -     yes     start or extend (1) no      CTRL-O (1)
-/// middle press   -     no      (cannot be active)  no      put register
-/// right press    -     yes     start or extend     yes     CTRL-O
-/// right press    S     yes     (cannot be active)  yes     "CTRL-O#" (2)
-///
-/// (1) only if mouse pointer moved since press
-/// (2) only if click is in same buffer
-///
-/// @param oap        operator argument, can be NULL
-/// @param c          K_LEFTMOUSE, etc
-/// @param dir        Direction to 'put' if necessary
-/// @param fixindent  PUT_FIXINDENT if fixing indent necessary
-///
-/// @return           true if start_arrow() should be called for edit mode.
-bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
-{
-  static bool got_click = false;        // got a click some time back
-
-  int which_button;             // MOUSE_LEFT, _MIDDLE or _RIGHT
-  bool is_click;                // If false it's a drag or release event
-  bool is_drag;                 // If true it's a drag event
-  int jump_flags = 0;           // flags for jump_to_mouse()
-  pos_T start_visual;
-  bool moved;                   // Has cursor moved?
-  bool in_winbar;               // mouse in window bar
-  bool in_status_line;          // mouse in status line
-  static bool in_tab_line = false;   // mouse clicked in tab line
-  bool in_sep_line;             // mouse in vertical separator line
-  int c1, c2;
-  pos_T save_cursor;
-  win_T *old_curwin = curwin;
-  static pos_T orig_cursor;
-  colnr_T leftcol, rightcol;
-  pos_T end_visual;
-  long diff;
-  int old_active = VIsual_active;
-  int old_mode = VIsual_mode;
-  int regname;
-
-  save_cursor = curwin->w_cursor;
-
-  for (;;) {
-    which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
-    if (is_drag) {
-      // If the next character is the same mouse event then use that
-      // one. Speeds up dragging the status line.
-      // Note: Since characters added to the stuff buffer in the code
-      // below need to come before the next character, do not do this
-      // when the current character was stuffed.
-      if (!KeyStuffed && vpeekc() != NUL) {
-        int nc;
-        int save_mouse_grid = mouse_grid;
-        int save_mouse_row = mouse_row;
-        int save_mouse_col = mouse_col;
-
-        // Need to get the character, peeking doesn't get the actual one.
-        nc = safe_vgetc();
-        if (c == nc) {
-          continue;
-        }
-        vungetc(nc);
-        mouse_grid = save_mouse_grid;
-        mouse_row = save_mouse_row;
-        mouse_col = save_mouse_col;
-      }
-    }
-    break;
-  }
-
-  if (c == K_MOUSEMOVE) {
-    // Mouse moved without a button pressed.
-    return false;
-  }
-
-  // Ignore drag and release events if we didn't get a click.
-  if (is_click) {
-    got_click = true;
-  } else {
-    if (!got_click) {                   // didn't get click, ignore
-      return false;
-    }
-    if (!is_drag) {                     // release, reset got_click
-      got_click = false;
-      if (in_tab_line) {
-        in_tab_line = false;
-        return false;
-      }
-    }
-  }
-
-  // CTRL right mouse button does CTRL-T
-  if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT) {
-    if (State & MODE_INSERT) {
-      stuffcharReadbuff(Ctrl_O);
-    }
-    if (count > 1) {
-      stuffnumReadbuff(count);
-    }
-    stuffcharReadbuff(Ctrl_T);
-    got_click = false;                  // ignore drag&release now
-    return false;
-  }
-
-  // CTRL only works with left mouse button
-  if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT) {
-    return false;
-  }
-
-  // When a modifier is down, ignore drag and release events, as well as
-  // multiple clicks and the middle mouse button.
-  // Accept shift-leftmouse drags when 'mousemodel' is "popup.*".
-  if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT
-                   | MOD_MASK_META))
-      && (!is_click
-          || (mod_mask & MOD_MASK_MULTI_CLICK)
-          || which_button == MOUSE_MIDDLE)
-      && !((mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))
-           && mouse_model_popup()
-           && which_button == MOUSE_LEFT)
-      && !((mod_mask & MOD_MASK_ALT)
-           && !mouse_model_popup()
-           && which_button == MOUSE_RIGHT)) {
-    return false;
-  }
-
-  // If the button press was used as the movement command for an operator (eg
-  // "d"), or it is the middle button that is held down, ignore
-  // drag/release events.
-  if (!is_click && which_button == MOUSE_MIDDLE) {
-    return false;
-  }
-
-  if (oap != NULL) {
-    regname = oap->regname;
-  } else {
-    regname = 0;
-  }
-
-  // Middle mouse button does a 'put' of the selected text
-  if (which_button == MOUSE_MIDDLE) {
-    if (State == MODE_NORMAL) {
-      // If an operator was pending, we don't know what the user wanted to do.
-      // Go back to normal mode: Clear the operator and beep().
-      if (oap != NULL && oap->op_type != OP_NOP) {
-        clearopbeep(oap);
-        return false;
-      }
-
-      // If visual was active, yank the highlighted text and put it
-      // before the mouse pointer position.
-      // In Select mode replace the highlighted text with the clipboard.
-      if (VIsual_active) {
-        if (VIsual_select) {
-          stuffcharReadbuff(Ctrl_G);
-          stuffReadbuff("\"+p");
-        } else {
-          stuffcharReadbuff('y');
-          stuffcharReadbuff(K_MIDDLEMOUSE);
-        }
-        return false;
-      }
-      // The rest is below jump_to_mouse()
-    } else if ((State & MODE_INSERT) == 0) {
-      return false;
-    }
-
-    // Middle click in insert mode doesn't move the mouse, just insert the
-    // contents of a register.  '.' register is special, can't insert that
-    // with do_put().
-    // Also paste at the cursor if the current mode isn't in 'mouse' (only
-    // happens for the GUI).
-    if ((State & MODE_INSERT)) {
-      if (regname == '.') {
-        insert_reg(regname, true);
-      } else {
-        if (regname == 0 && eval_has_provider("clipboard")) {
-          regname = '*';
-        }
-        if ((State & REPLACE_FLAG) && !yank_register_mline(regname)) {
-          insert_reg(regname, true);
-        } else {
-          do_put(regname, NULL, BACKWARD, 1L,
-                 (fixindent ? PUT_FIXINDENT : 0) | PUT_CURSEND);
-
-          // Repeat it with CTRL-R CTRL-O r or CTRL-R CTRL-P r
-          AppendCharToRedobuff(Ctrl_R);
-          AppendCharToRedobuff(fixindent ? Ctrl_P : Ctrl_O);
-          AppendCharToRedobuff(regname == 0 ? '"' : regname);
-        }
-      }
-      return false;
-    }
-  }
-
-  // When dragging or button-up stay in the same window.
-  if (!is_click) {
-    jump_flags |= MOUSE_FOCUS | MOUSE_DID_MOVE;
-  }
-
-  start_visual.lnum = 0;
-
-  // Check for clicking in the tab page line.
-  if (mouse_grid <= 1 && mouse_row == 0 && firstwin->w_winrow > 0) {
-    if (is_drag) {
-      if (in_tab_line) {
-        move_tab_to_mouse();
-      }
-      return false;
-    }
-
-    // click in a tab selects that tab page
-    if (is_click && cmdwin_type == 0 && mouse_col < Columns) {
-      in_tab_line = true;
-      c1 = tab_page_click_defs[mouse_col].tabnr;
-      switch (tab_page_click_defs[mouse_col].type) {
-      case kStlClickDisabled:
-        break;
-      case kStlClickTabClose: {
-        tabpage_T *tp;
-
-        // Close the current or specified tab page.
-        if (c1 == 999) {
-          tp = curtab;
-        } else {
-          tp = find_tabpage(c1);
-        }
-        if (tp == curtab) {
-          if (first_tabpage->tp_next != NULL) {
-            tabpage_close(false);
-          }
-        } else if (tp != NULL) {
-          tabpage_close_other(tp, false);
-        }
-        break;
-      }
-      case kStlClickTabSwitch:
-        if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
-          // double click opens new page
-          end_visual_mode();
-          tabpage_new();
-          tabpage_move(c1 == 0 ? 9999 : c1 - 1);
-        } else {
-          // Go to specified tab page, or next one if not clicking
-          // on a label.
-          goto_tabpage(c1);
-
-          // It's like clicking on the status line of a window.
-          if (curwin != old_curwin) {
-            end_visual_mode();
-          }
-        }
-        break;
-      case kStlClickFuncRun:
-        call_click_def_func(tab_page_click_defs, mouse_col, which_button);
-        break;
-      }
-    }
-    return true;
-  } else if (is_drag && in_tab_line) {
-    move_tab_to_mouse();
-    return false;
-  }
-
-  // When 'mousemodel' is "popup" or "popup_setpos", translate mouse events:
-  // right button up   -> pop-up menu
-  // shift-left button -> right button
-  // alt-left button   -> alt-right button
-  if (mouse_model_popup()) {
-    if (which_button == MOUSE_RIGHT
-        && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
-      if (!is_click) {
-        // Ignore right button release events, only shows the popup
-        // menu on the button down event.
-        return false;
-      }
-      jump_flags = 0;
-      if (strcmp(p_mousem, "popup_setpos") == 0) {
-        // First set the cursor position before showing the popup
-        // menu.
-        if (VIsual_active) {
-          pos_T m_pos;
-          // set MOUSE_MAY_STOP_VIS if we are outside the
-          // selection or the current window (might have false
-          // negative here)
-          if (mouse_row < curwin->w_winrow
-              || mouse_row > (curwin->w_winrow + curwin->w_height)) {
-            jump_flags = MOUSE_MAY_STOP_VIS;
-          } else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER) {
-            jump_flags = MOUSE_MAY_STOP_VIS;
-          } else {
-            if (VIsual_mode == 'V') {
-              if ((curwin->w_cursor.lnum <= VIsual.lnum
-                   && (m_pos.lnum < curwin->w_cursor.lnum || VIsual.lnum < m_pos.lnum))
-                  || (VIsual.lnum < curwin->w_cursor.lnum
-                      && (m_pos.lnum < VIsual.lnum || curwin->w_cursor.lnum < m_pos.lnum))) {
-                jump_flags = MOUSE_MAY_STOP_VIS;
-              }
-            } else if ((ltoreq(curwin->w_cursor, VIsual)
-                        && (lt(m_pos, curwin->w_cursor) || lt(VIsual, m_pos)))
-                       || (lt(VIsual, curwin->w_cursor)
-                           && (lt(m_pos, VIsual) || lt(curwin->w_cursor, m_pos)))) {
-              jump_flags = MOUSE_MAY_STOP_VIS;
-            } else if (VIsual_mode == Ctrl_V) {
-              getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
-              getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL);
-              if (m_pos.col < leftcol || m_pos.col > rightcol) {
-                jump_flags = MOUSE_MAY_STOP_VIS;
-              }
-            }
-          }
-        } else {
-          jump_flags = MOUSE_MAY_STOP_VIS;
-        }
-      }
-      if (jump_flags) {
-        jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
-        redraw_curbuf_later(VIsual_active ? UPD_INVERTED : UPD_VALID);
-        update_screen();
-        setcursor();
-        ui_flush();  // Update before showing popup menu
-      }
-      show_popupmenu();
-      got_click = false;  // ignore release events
-      return (jump_flags & CURSOR_MOVED) != 0;
-    }
-    if (which_button == MOUSE_LEFT
-        && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))) {
-      which_button = MOUSE_RIGHT;
-      mod_mask &= ~MOD_MASK_SHIFT;
-    }
-  }
-
-  if ((State & (MODE_NORMAL | MODE_INSERT))
-      && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
-    if (which_button == MOUSE_LEFT) {
-      if (is_click) {
-        // stop Visual mode for a left click in a window, but not when on a status line
-        if (VIsual_active) {
-          jump_flags |= MOUSE_MAY_STOP_VIS;
-        }
-      } else {
-        jump_flags |= MOUSE_MAY_VIS;
-      }
-    } else if (which_button == MOUSE_RIGHT) {
-      if (is_click && VIsual_active) {
-        // Remember the start and end of visual before moving the cursor.
-        if (lt(curwin->w_cursor, VIsual)) {
-          start_visual = curwin->w_cursor;
-          end_visual = VIsual;
-        } else {
-          start_visual = VIsual;
-          end_visual = curwin->w_cursor;
-        }
-      }
-      jump_flags |= MOUSE_FOCUS;
-      jump_flags |= MOUSE_MAY_VIS;
-    }
-  }
-
-  // If an operator is pending, ignore all drags and releases until the next mouse click.
-  if (!is_drag && oap != NULL && oap->op_type != OP_NOP) {
-    got_click = false;
-    oap->motion_type = kMTCharWise;
-  }
-
-  // When releasing the button let jump_to_mouse() know.
-  if (!is_click && !is_drag) {
-    jump_flags |= MOUSE_RELEASED;
-  }
-
-  // JUMP!
-  jump_flags = jump_to_mouse(jump_flags,
-                             oap == NULL ? NULL : &(oap->inclusive),
-                             which_button);
-
-  moved = (jump_flags & CURSOR_MOVED);
-  in_winbar = (jump_flags & MOUSE_WINBAR);
-  in_status_line = (jump_flags & IN_STATUS_LINE);
-  in_sep_line = (jump_flags & IN_SEP_LINE);
-
-  if ((in_winbar || in_status_line) && is_click) {
-    // Handle click event on window bar or status lin
-    int click_grid = mouse_grid;
-    int click_row = mouse_row;
-    int click_col = mouse_col;
-    win_T *wp = mouse_find_win(&click_grid, &click_row, &click_col);
-    if (wp == NULL) {
-      return false;
-    }
-
-    StlClickDefinition *click_defs = in_status_line ? wp->w_status_click_defs
-                                                    : wp->w_winbar_click_defs;
-
-    if (in_status_line && global_stl_height() > 0) {
-      // global statusline is displayed for the current window,
-      // and spans the whole screen.
-      click_defs = curwin->w_status_click_defs;
-      click_col = mouse_col;
-    }
-
-    if (click_defs != NULL) {
-      switch (click_defs[click_col].type) {
-      case kStlClickDisabled:
-        break;
-      case kStlClickFuncRun:
-        call_click_def_func(click_defs, click_col, which_button);
-        break;
-      default:
-        assert(false && "winbar and statusline only support %@ for clicks");
-        break;
-      }
-    }
-
-    return false;
-  } else if (in_winbar) {
-    // A drag or release event in the window bar has no side effects.
-    return false;
-  }
-
-  // When jumping to another window, clear a pending operator.  That's a bit
-  // friendlier than beeping and not jumping to that window.
-  if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP) {
-    clearop(oap);
-  }
-
-  if (mod_mask == 0
-      && !is_drag
-      && (jump_flags & (MOUSE_FOLD_CLOSE | MOUSE_FOLD_OPEN))
-      && which_button == MOUSE_LEFT) {
-    // open or close a fold at this line
-    if (jump_flags & MOUSE_FOLD_OPEN) {
-      openFold(curwin->w_cursor, 1L);
-    } else {
-      closeFold(curwin->w_cursor, 1L);
-    }
-    // don't move the cursor if still in the same window
-    if (curwin == old_curwin) {
-      curwin->w_cursor = save_cursor;
-    }
-  }
-
-  // Set global flag that we are extending the Visual area with mouse dragging;
-  // temporarily minimize 'scrolloff'.
-  if (VIsual_active && is_drag && get_scrolloff_value(curwin)) {
-    // In the very first line, allow scrolling one line
-    if (mouse_row == 0) {
-      mouse_dragging = 2;
-    } else {
-      mouse_dragging = 1;
-    }
-  }
-
-  // When dragging the mouse above the window, scroll down.
-  if (is_drag && mouse_row < 0 && !in_status_line) {
-    scroll_redraw(false, 1L);
-    mouse_row = 0;
-  }
-
-  if (start_visual.lnum) {              // right click in visual mode
-    // When ALT is pressed make Visual mode blockwise.
-    if (mod_mask & MOD_MASK_ALT) {
-      VIsual_mode = Ctrl_V;
-    }
-
-    // In Visual-block mode, divide the area in four, pick up the corner
-    // that is in the quarter that the cursor is in.
-    if (VIsual_mode == Ctrl_V) {
-      getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
-      if (curwin->w_curswant > (leftcol + rightcol) / 2) {
-        end_visual.col = leftcol;
-      } else {
-        end_visual.col = rightcol;
-      }
-      if (curwin->w_cursor.lnum >=
-          (start_visual.lnum + end_visual.lnum) / 2) {
-        end_visual.lnum = start_visual.lnum;
-      }
-
-      // move VIsual to the right column
-      start_visual = curwin->w_cursor;              // save the cursor pos
-      curwin->w_cursor = end_visual;
-      coladvance(end_visual.col);
-      VIsual = curwin->w_cursor;
-      curwin->w_cursor = start_visual;              // restore the cursor
-    } else {
-      // If the click is before the start of visual, change the start.
-      // If the click is after the end of visual, change the end.  If
-      // the click is inside the visual, change the closest side.
-      if (lt(curwin->w_cursor, start_visual)) {
-        VIsual = end_visual;
-      } else if (lt(end_visual, curwin->w_cursor)) {
-        VIsual = start_visual;
-      } else {
-        // In the same line, compare column number
-        if (end_visual.lnum == start_visual.lnum) {
-          if (curwin->w_cursor.col - start_visual.col >
-              end_visual.col - curwin->w_cursor.col) {
-            VIsual = start_visual;
-          } else {
-            VIsual = end_visual;
-          }
-        } else {
-          // In different lines, compare line number
-          diff = (curwin->w_cursor.lnum - start_visual.lnum) -
-                 (end_visual.lnum - curwin->w_cursor.lnum);
-
-          if (diff > 0) {                       // closest to end
-            VIsual = start_visual;
-          } else if (diff < 0) {                   // closest to start
-            VIsual = end_visual;
-          } else {                                // in the middle line
-            if (curwin->w_cursor.col <
-                (start_visual.col + end_visual.col) / 2) {
-              VIsual = end_visual;
-            } else {
-              VIsual = start_visual;
-            }
-          }
-        }
-      }
-    }
-  } else if ((State & MODE_INSERT) && VIsual_active) {
-    // If Visual mode started in insert mode, execute "CTRL-O"
-    stuffcharReadbuff(Ctrl_O);
-  }
-
-  // Middle mouse click: Put text before cursor.
-  if (which_button == MOUSE_MIDDLE) {
-    if (regname == 0 && eval_has_provider("clipboard")) {
-      regname = '*';
-    }
-    if (yank_register_mline(regname)) {
-      if (mouse_past_bottom) {
-        dir = FORWARD;
-      }
-    } else if (mouse_past_eol) {
-      dir = FORWARD;
-    }
-
-    if (fixindent) {
-      c1 = (dir == BACKWARD) ? '[' : ']';
-      c2 = 'p';
-    } else {
-      c1 = (dir == FORWARD) ? 'p' : 'P';
-      c2 = NUL;
-    }
-    prep_redo(regname, count, NUL, c1, NUL, c2, NUL);
-
-    // Remember where the paste started, so in edit() Insstart can be set to this position
-    if (restart_edit != 0) {
-      where_paste_started = curwin->w_cursor;
-    }
-    do_put(regname, NULL, dir, count,
-           (fixindent ? PUT_FIXINDENT : 0)| PUT_CURSEND);
-  } else if (((mod_mask & MOD_MASK_CTRL) || (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
-             && bt_quickfix(curbuf)) {
-    // Ctrl-Mouse click or double click in a quickfix window jumps to the
-    // error under the mouse pointer.
-    if (curwin->w_llist_ref == NULL) {          // quickfix window
-      do_cmdline_cmd(".cc");
-    } else {                                    // location list window
-      do_cmdline_cmd(".ll");
-    }
-    got_click = false;                  // ignore drag&release now
-  } else if ((mod_mask & MOD_MASK_CTRL)
-             || (curbuf->b_help && (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)) {
-    // Ctrl-Mouse click (or double click in a help window) jumps to the tag
-    // under the mouse pointer.
-    if (State & MODE_INSERT) {
-      stuffcharReadbuff(Ctrl_O);
-    }
-    stuffcharReadbuff(Ctrl_RSB);
-    got_click = false;                  // ignore drag&release now
-  } else if ((mod_mask & MOD_MASK_SHIFT)) {
-    // Shift-Mouse click searches for the next occurrence of the word under
-    // the mouse pointer
-    if (State & MODE_INSERT || (VIsual_active && VIsual_select)) {
-      stuffcharReadbuff(Ctrl_O);
-    }
-    if (which_button == MOUSE_LEFT) {
-      stuffcharReadbuff('*');
-    } else {  // MOUSE_RIGHT
-      stuffcharReadbuff('#');
-    }
-  } else if (in_status_line || in_sep_line) {
-    // Do nothing if on status line or vertical separator
-    // Handle double clicks otherwise
-  } else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (MODE_NORMAL | MODE_INSERT))) {
-    if (is_click || !VIsual_active) {
-      if (VIsual_active) {
-        orig_cursor = VIsual;
-      } else {
-        VIsual = curwin->w_cursor;
-        orig_cursor = VIsual;
-        VIsual_active = true;
-        VIsual_reselect = true;
-        // start Select mode if 'selectmode' contains "mouse"
-        may_start_select('o');
-        setmouse();
-      }
-      if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
-        // Double click with ALT pressed makes it blockwise.
-        if (mod_mask & MOD_MASK_ALT) {
-          VIsual_mode = Ctrl_V;
-        } else {
-          VIsual_mode = 'v';
-        }
-      } else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK) {
-        VIsual_mode = 'V';
-      } else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK) {
-        VIsual_mode = Ctrl_V;
-      }
-    }
-    // A double click selects a word or a block.
-    if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
-      pos_T *pos = NULL;
-      int gc;
-
-      if (is_click) {
-        // If the character under the cursor (skipping white space) is
-        // not a word character, try finding a match and select a (),
-        // {}, [], #if/#endif, etc. block.
-        end_visual = curwin->w_cursor;
-        while (gc = gchar_pos(&end_visual), ascii_iswhite(gc)) {
-          inc(&end_visual);
-        }
-        if (oap != NULL) {
-          oap->motion_type = kMTCharWise;
-        }
-        if (oap != NULL
-            && VIsual_mode == 'v'
-            && !vim_iswordc(gchar_pos(&end_visual))
-            && equalpos(curwin->w_cursor, VIsual)
-            && (pos = findmatch(oap, NUL)) != NULL) {
-          curwin->w_cursor = *pos;
-          if (oap->motion_type == kMTLineWise) {
-            VIsual_mode = 'V';
-          } else if (*p_sel == 'e') {
-            if (lt(curwin->w_cursor, VIsual)) {
-              VIsual.col++;
-            } else {
-              curwin->w_cursor.col++;
-            }
-          }
-        }
-      }
-
-      if (pos == NULL && (is_click || is_drag)) {
-        // When not found a match or when dragging: extend to include a word.
-        if (lt(curwin->w_cursor, orig_cursor)) {
-          find_start_of_word(&curwin->w_cursor);
-          find_end_of_word(&VIsual);
-        } else {
-          find_start_of_word(&VIsual);
-          if (*p_sel == 'e' && *get_cursor_pos_ptr() != NUL) {
-            curwin->w_cursor.col +=
-              utfc_ptr2len(get_cursor_pos_ptr());
-          }
-          find_end_of_word(&curwin->w_cursor);
-        }
-      }
-      curwin->w_set_curswant = true;
-    }
-    if (is_click) {
-      redraw_curbuf_later(UPD_INVERTED);  // update the inversion
-    }
-  } else if (VIsual_active && !old_active) {
-    if (mod_mask & MOD_MASK_ALT) {
-      VIsual_mode = Ctrl_V;
-    } else {
-      VIsual_mode = 'v';
-    }
-  }
-
-  // If Visual mode changed show it later.
-  if ((!VIsual_active && old_active && mode_displayed)
-      || (VIsual_active && p_smd && msg_silent == 0
-          && (!old_active || VIsual_mode != old_mode))) {
-    redraw_cmdline = true;
-  }
-
-  return moved;
-}
-
-/// Move "pos" back to the start of the word it's in.
-static void find_start_of_word(pos_T *pos)
-{
-  char_u *line;
-  int cclass;
-  int col;
-
-  line = (char_u *)ml_get(pos->lnum);
-  cclass = get_mouse_class(line + pos->col);
-
-  while (pos->col > 0) {
-    col = pos->col - 1;
-    col -= utf_head_off((char *)line, (char *)line + col);
-    if (get_mouse_class(line + col) != cclass) {
-      break;
-    }
-    pos->col = col;
-  }
-}
-
-/// Move "pos" forward to the end of the word it's in.
-/// When 'selection' is "exclusive", the position is just after the word.
-static void find_end_of_word(pos_T *pos)
-{
-  char_u *line;
-  int cclass;
-  int col;
-
-  line = (char_u *)ml_get(pos->lnum);
-  if (*p_sel == 'e' && pos->col > 0) {
-    pos->col--;
-    pos->col -= utf_head_off((char *)line, (char *)line + pos->col);
-  }
-  cclass = get_mouse_class(line + pos->col);
-  while (line[pos->col] != NUL) {
-    col = pos->col + utfc_ptr2len((char *)line + pos->col);
-    if (get_mouse_class(line + col) != cclass) {
-      if (*p_sel == 'e') {
-        pos->col = col;
-      }
-      break;
-    }
-    pos->col = col;
-  }
-}
-
-/// Get class of a character for selection: same class means same word.
-/// 0: blank
-/// 1: punctuation groups
-/// 2: normal word character
-/// >2: multi-byte word character.
-static int get_mouse_class(char_u *p)
-{
-  if (MB_BYTE2LEN(p[0]) > 1) {
-    return mb_get_class(p);
-  }
-
-  const int c = *p;
-  if (c == ' ' || c == '\t') {
-    return 0;
-  }
-  if (vim_iswordc(c)) {
-    return 2;
-  }
-
-  // There are a few special cases where we want certain combinations of
-  // characters to be considered as a single word.  These are things like
-  // "->", "/ *", "*=", "+=", "&=", "<=", ">=", "!=" etc.  Otherwise, each
-  // character is in its own class.
-  if (c != NUL && vim_strchr("-+*/%<>&|^!=", c) != NULL) {
-    return 1;
-  }
-  return c;
-}
-
 /// End Visual mode.
 /// This function should ALWAYS be called to end Visual mode, except from
 /// do_pending_operator().
@@ -7401,12 +6545,6 @@ static void nv_event(cmdarg_T *cap)
   }
 }
 
-/// @return  true when 'mousemodel' is set to "popup" or "popup_setpos".
-static bool mouse_model_popup(void)
-{
-  return p_mousem[0] == 'p';
-}
-
 void normal_cmd(oparg_T *oap, bool toplevel)
 {
   NormalState s;
-- 
cgit 


From c46d46e9f11a9960fcf9c498ecc72be4c416cfa5 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Tue, 1 Nov 2022 20:21:48 +0800
Subject: vim-patch:8.2.2343: Vim9: return type of readfile() is any (#20896)

Problem:    Vim9: return type of readfile() is any.
Solution:   Add readblob() so that readfile() can be expected to always
            return a list of strings. (closes vim/vim#7671)

https://github.com/vim/vim/commit/c423ad77ed763c11ba67729bbf63c1cf0915231f

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval.lua                   |  1 +
 src/nvim/eval/funcs.c               | 20 ++++++++++++++++----
 src/nvim/testdir/test_mksession.vim |  4 +---
 3 files changed, 18 insertions(+), 7 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 264659af1f..ecb411a652 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -291,6 +291,7 @@ return {
     perleval={args=1, base=1},
     rand={args={0, 1}, base=1},
     range={args={1, 3}, base=1},
+    readblob={args=1, base=1},
     readdir={args={1, 2}, base=1},
     readfile={args={1, 3}, base=1},
     reduce={args={2, 3}, base=1},
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 17781cf4ef..79c93f1917 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -5872,11 +5872,11 @@ static void f_readdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   ga_clear_strings(&ga);
 }
 
-/// "readfile()" function
-static void f_readfile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+/// "readfile()" or "readblob()" function
+static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_blob)
 {
   bool binary = false;
-  bool blob = false;
+  bool blob = always_blob;
   FILE *fd;
   char buf[(IOSIZE/256) * 256];    // rounded to avoid odd + 1
   int io_size = sizeof(buf);
@@ -6011,8 +6011,8 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
             int adjust_prevlen = 0;
 
             if (dest < buf) {  // -V782
-              adjust_prevlen = (int)(buf - dest);  // -V782
               // adjust_prevlen must be 1 or 2.
+              adjust_prevlen = (int)(buf - dest);  // -V782
               dest = buf;
             }
             if (readlen > p - buf + 1) {
@@ -6055,6 +6055,18 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   fclose(fd);
 }
 
+/// "readblob()" function
+static void f_readblob(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+  read_file_or_blob(argvars, rettv, true);
+}
+
+/// "readfile()" function
+static void f_readfile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+  read_file_or_blob(argvars, rettv, false);
+}
+
 /// "getreginfo()" function
 static void f_getreginfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 {
diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim
index ccc775560f..9cf80f631c 100644
--- a/src/nvim/testdir/test_mksession.vim
+++ b/src/nvim/testdir/test_mksession.vim
@@ -851,9 +851,7 @@ func Test_mksession_shortmess_with_A()
   edit Xtestfile
   write
   let fname = swapname('%')
-  " readblob() needs patch 8.2.2343
-  " let cont = readblob(fname)
-  let cont = readfile(fname, 'B')
+  let cont = readblob(fname)
   set sessionoptions-=options
   mksession Xtestsession
   bwipe!
-- 
cgit 


From b05d1943f063c382ea96b76d250877bc58297314 Mon Sep 17 00:00:00 2001
From: dundargoc <33953936+dundargoc@users.noreply.github.com>
Date: Tue, 1 Nov 2022 15:39:49 +0100
Subject: build(lint): remove clint.py rules for braces #20880

Uncrustify is the source of truth where possible.
Remove any redundant checks from clint.py.
See also https://github.com/neovim/neovim/pull/18563
---
 src/nvim/buffer_defs.h   |  2 ++
 src/nvim/cursor_shape.c  |  3 +--
 src/nvim/digraph.c       |  1 -
 src/nvim/eval.c          |  3 +--
 src/nvim/eval/typval.c   | 28 ++++++++++++++--------------
 src/nvim/eval/userfunc.c |  2 +-
 src/nvim/ex_eval.c       |  2 +-
 src/nvim/fold.c          |  2 +-
 src/nvim/globals.h       |  4 ++--
 src/nvim/hardcopy.c      | 45 +++++++++++++++------------------------------
 src/nvim/insexpand.c     |  3 +--
 src/nvim/keycodes.c      |  6 ++----
 src/nvim/lua/executor.c  |  2 +-
 src/nvim/mbyte.c         |  9 +++------
 src/nvim/memory.h        |  2 +-
 src/nvim/normal.c        |  5 ++---
 src/nvim/ops.c           |  3 +--
 src/nvim/quickfix.c      |  5 ++---
 src/nvim/regexp.c        | 12 ++++--------
 src/nvim/search.c        |  3 +--
 src/nvim/shada.c         |  2 +-
 src/nvim/strings.c       |  2 +-
 src/nvim/syntax.c        |  9 +++------
 src/nvim/usercmd.c       |  6 ++----
 24 files changed, 63 insertions(+), 98 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 52449faa5b..d8edd4b2d0 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -1404,7 +1404,9 @@ struct window_S {
 };
 
 /// Macros defined in Vim, but not in Neovim
+// uncrustify:off
 #define CHANGEDTICK(buf) \
   (=== Include buffer.h & use buf_(get|set|inc) _changedtick ===)
+// uncrustify:on
 
 #endif  // NVIM_BUFFER_DEFS_H
diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c
index 6f4f17659a..68522bdaa0 100644
--- a/src/nvim/cursor_shape.c
+++ b/src/nvim/cursor_shape.c
@@ -19,8 +19,7 @@
 #endif
 
 /// Handling of cursor and mouse pointer shapes in various modes.
-cursorentry_T shape_table[SHAPE_IDX_COUNT] =
-{
+cursorentry_T shape_table[SHAPE_IDX_COUNT] = {
   // Values are set by 'guicursor' and 'mouseshape'.
   // Adjust the SHAPE_IDX_ defines when changing this!
   { "normal", 0, 0, 0, 700L, 400L, 250L, 0, 0, "n", SHAPE_CURSOR + SHAPE_MOUSE },
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
index 1267d49ad1..c4a36e783f 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -52,7 +52,6 @@ static garray_T user_digraphs = { 0, 0, (int)sizeof(digr_T), 10, NULL };
 /// Note: Characters marked with XX are not included literally, because some
 /// compilers cannot handle them (Amiga SAS/C is the most picky one).
 static digr_T digraphdefault[] =
-
 // digraphs for Unicode from RFC1345
 // (also work for ISO-8859-1 aka latin1)
 {
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index d6411fb15b..89ae4a2cd0 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -133,8 +133,7 @@ static struct vimvar {
   char *vv_name;  ///< Name of the variable, without v:.
   TV_DICTITEM_STRUCT(VIMVAR_KEY_LEN + 1) vv_di;  ///< Value and name for key (max 16 chars).
   char vv_flags;  ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX.
-} vimvars[] =
-{
+} vimvars[] = {
   // VV_ tails differing from upcased string literals:
   // VV_CC_FROM "charconvert_from"
   // VV_CC_TO "charconvert_to"
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index cc6c2b5d90..11b2b6e5fd 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -3595,13 +3595,13 @@ bool tv_check_str_or_nr(const typval_T *const tv)
 #define FUNC_ERROR "E703: Using a Funcref as a Number"
 
 static const char *const num_errors[] = {
-  [VAR_PARTIAL]=N_(FUNC_ERROR),
-  [VAR_FUNC]=N_(FUNC_ERROR),
-  [VAR_LIST]=N_("E745: Using a List as a Number"),
-  [VAR_DICT]=N_("E728: Using a Dictionary as a Number"),
-  [VAR_FLOAT]=N_("E805: Using a Float as a Number"),
-  [VAR_BLOB]=N_("E974: Using a Blob as a Number"),
-  [VAR_UNKNOWN]=N_("E685: using an invalid value as a Number"),
+  [VAR_PARTIAL]= N_(FUNC_ERROR),
+  [VAR_FUNC]= N_(FUNC_ERROR),
+  [VAR_LIST]= N_("E745: Using a List as a Number"),
+  [VAR_DICT]= N_("E728: Using a Dictionary as a Number"),
+  [VAR_FLOAT]= N_("E805: Using a Float as a Number"),
+  [VAR_BLOB]= N_("E974: Using a Blob as a Number"),
+  [VAR_UNKNOWN]= N_("E685: using an invalid value as a Number"),
 };
 
 #undef FUNC_ERROR
@@ -3640,13 +3640,13 @@ bool tv_check_num(const typval_T *const tv)
 #define FUNC_ERROR "E729: using Funcref as a String"
 
 static const char *const str_errors[] = {
-  [VAR_PARTIAL]=N_(FUNC_ERROR),
-  [VAR_FUNC]=N_(FUNC_ERROR),
-  [VAR_LIST]=N_("E730: using List as a String"),
-  [VAR_DICT]=N_("E731: using Dictionary as a String"),
-  [VAR_FLOAT]=((const char *)e_float_as_string),
-  [VAR_BLOB]=N_("E976: using Blob as a String"),
-  [VAR_UNKNOWN]=N_("E908: using an invalid value as a String"),
+  [VAR_PARTIAL]= N_(FUNC_ERROR),
+  [VAR_FUNC]= N_(FUNC_ERROR),
+  [VAR_LIST]= N_("E730: using List as a String"),
+  [VAR_DICT]= N_("E731: using Dictionary as a String"),
+  [VAR_FLOAT]= ((const char *)e_float_as_string),
+  [VAR_BLOB]= N_("E976: using Blob as a String"),
+  [VAR_UNKNOWN]= N_("E908: using an invalid value as a String"),
 };
 
 #undef FUNC_ERROR
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index 147beb78ad..c22718c65d 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -998,7 +998,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
       listitem_T *li = &fc->l_listitems[ai];
 
       *TV_LIST_ITEM_TV(li) = argvars[i];
-      TV_LIST_ITEM_TV(li)->v_lock =  VAR_FIXED;
+      TV_LIST_ITEM_TV(li)->v_lock = VAR_FIXED;
       tv_list_append(&fc->l_varlist, li);
     }
   }
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 09bd88c947..2d6b236007 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -1083,7 +1083,7 @@ void ex_endwhile(exarg_T *eap)
       }
       // Try to find the matching ":while" and report what's missing.
       for (idx = cstack->cs_idx; idx > 0; idx--) {
-        fl =  cstack->cs_flags[idx];
+        fl = cstack->cs_flags[idx];
         if ((fl & CSF_TRY) && !(fl & CSF_FINALLY)) {
           // Give up at a try conditional not in its finally clause.
           // Ignore the ":endwhile"/":endfor".
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 04e6818d53..02fbbc20db 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -1083,7 +1083,7 @@ static int foldLevelWin(win_T *wp, linenr_T lnum)
 {
   fold_T *fp;
   linenr_T lnum_rel = lnum;
-  int level =  0;
+  int level = 0;
 
   // Recursively search for a fold that contains "lnum".
   garray_T *gap = &wp->w_folds;
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index f6fbd6ffe9..c556aac1fd 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -613,7 +613,7 @@ EXTERN int State INIT(= MODE_NORMAL);
 EXTERN bool debug_mode INIT(= false);
 EXTERN bool finish_op INIT(= false);    // true while an operator is pending
 EXTERN long opcount INIT(= 0);          // count for pending operator
-EXTERN int motion_force INIT(=0);       // motion force for pending operator
+EXTERN int motion_force INIT(= 0);       // motion force for pending operator
 
 // Ex Mode (Q) state
 EXTERN bool exmode_active INIT(= false);  // true if Ex mode is active
@@ -621,7 +621,7 @@ EXTERN bool exmode_active INIT(= false);  // true if Ex mode is active
 /// Flag set when normal_check() should return 0 when entering Ex mode.
 EXTERN bool pending_exmode_active INIT(= false);
 
-EXTERN bool ex_no_reprint INIT(=false);   // No need to print after z or p.
+EXTERN bool ex_no_reprint INIT(= false);   // No need to print after z or p.
 
 // 'inccommand' command preview state
 EXTERN bool cmdpreview INIT(= false);
diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c
index 507942985c..7345e9cc35 100644
--- a/src/nvim/hardcopy.c
+++ b/src/nvim/hardcopy.c
@@ -140,8 +140,7 @@ static int page_count;
 #define OPT_MBFONT_BOLDOBLIQUE 5
 #define OPT_MBFONT_NUM_OPTIONS 6
 
-static option_table_T mbfont_opts[OPT_MBFONT_NUM_OPTIONS] =
-{
+static option_table_T mbfont_opts[OPT_MBFONT_NUM_OPTIONS] = {
   { "c",       false, 0, NULL, 0, false },
   { "a",       false, 0, NULL, 0, false },
   { "r",       false, 0, NULL, 0, false },
@@ -208,8 +207,7 @@ typedef enum {
 } PrtResourceType;
 
 // String versions of PS resource types
-static const char *const prt_resource_types[] =
-{
+static const char *const prt_resource_types[] = {
   [PRT_RESOURCE_TYPE_PROCSET] = "procset",
   [PRT_RESOURCE_TYPE_ENCODING] = "encoding",
   [PRT_RESOURCE_TYPE_CMAP] = "cmap",
@@ -945,8 +943,7 @@ static colnr_T hardcopy_line(prt_settings_T *psettings, int page_line, prt_pos_T
 
 #define PRT_MEDIASIZE_LEN  ARRAY_SIZE(prt_mediasize)
 
-static struct prt_mediasize_S prt_mediasize[] =
-{
+static struct prt_mediasize_S prt_mediasize[] = {
   { "A4",              595.0,  842.0 },
   { "letter",          612.0,  792.0 },
   { "10x14",           720.0, 1008.0 },
@@ -969,8 +966,7 @@ static struct prt_mediasize_S prt_mediasize[] =
 #define PRT_PS_FONT_BOLDOBLIQUE (3)
 
 // Standard font metrics for Courier family
-static struct prt_ps_font_S prt_ps_courier_font =
-{
+static struct prt_ps_font_S prt_ps_courier_font = {
   600,
   -100, 50,
   -250, 805,
@@ -978,8 +974,7 @@ static struct prt_ps_font_S prt_ps_courier_font =
 };
 
 // Generic font metrics for multi-byte fonts
-static struct prt_ps_font_S prt_ps_mb_font =
-{
+static struct prt_ps_font_S prt_ps_mb_font = {
   1000,
   -100, 50,
   -250, 805,
@@ -999,8 +994,7 @@ static struct prt_ps_font_S *prt_ps_font;
 #define CS_KANJITALK7   (0x80)
 
 // Japanese encodings and charsets
-static struct prt_ps_encoding_S j_encodings[] =
-{
+static struct prt_ps_encoding_S j_encodings[] = {
   { "iso-2022-jp", NULL,       (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990|
                                 CS_NEC) },
   { "euc-jp",      "EUC",      (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990) },
@@ -1010,8 +1004,7 @@ static struct prt_ps_encoding_S j_encodings[] =
   { "ucs-2",       "UCS2",     CS_JIS_X_1990 },
   { "utf-8",       "UTF8",    CS_JIS_X_1990 }
 };
-static struct prt_ps_charset_S j_charsets[] =
-{
+static struct prt_ps_charset_S j_charsets[] = {
   { "JIS_C_1978",  "78",       CS_JIS_C_1978 },
   { "JIS_X_1983",  NULL,       CS_JIS_X_1983 },
   { "JIS_X_1990",  "Hojo",     CS_JIS_X_1990 },
@@ -1031,8 +1024,7 @@ static struct prt_ps_charset_S j_charsets[] =
 #define CS_SC_ISO10646      (0x40)
 
 // Simplified Chinese encodings and charsets
-static struct prt_ps_encoding_S sc_encodings[] =
-{
+static struct prt_ps_encoding_S sc_encodings[] = {
   { "iso-2022",    NULL,       (CS_GB_2312_80|CS_GBT_12345_90) },
   { "gb18030",     NULL,       CS_GBK2K },
   { "euc-cn",      "EUC",      (CS_GB_2312_80|CS_GBT_12345_90|CS_SC_MAC|
@@ -1041,8 +1033,7 @@ static struct prt_ps_encoding_S sc_encodings[] =
   { "ucs-2",       "UCS2",     CS_SC_ISO10646 },
   { "utf-8",       "UTF8",     CS_SC_ISO10646 }
 };
-static struct prt_ps_charset_S sc_charsets[] =
-{
+static struct prt_ps_charset_S sc_charsets[] = {
   { "GB_2312-80",  "GB",       CS_GB_2312_80 },
   { "GBT_12345-90", "GBT",      CS_GBT_12345_90 },
   { "MAC",         "GBpc",     CS_SC_MAC },
@@ -1067,8 +1058,7 @@ static struct prt_ps_charset_S sc_charsets[] =
 #define CS_TC_ISO10646      (0x1000)
 
 // Traditional Chinese encodings and charsets
-static struct prt_ps_encoding_S tc_encodings[] =
-{
+static struct prt_ps_encoding_S tc_encodings[] = {
   { "iso-2022",    NULL,       (CS_CNS_PLANE_1|CS_CNS_PLANE_2) },
   { "euc-tw",      "EUC",      CS_CNS_PLANE_1_2 },
   { "big5",        "B5",       (CS_B5|CS_ETEN|CS_HK_GCCS|CS_HK_SCS|
@@ -1080,8 +1070,7 @@ static struct prt_ps_encoding_S tc_encodings[] =
   { "utf-16",      "UTF16",    CS_TC_ISO10646 },
   { "utf-32",      "UTF32",    CS_TC_ISO10646 }
 };
-static struct prt_ps_charset_S tc_charsets[] =
-{
+static struct prt_ps_charset_S tc_charsets[] = {
   { "CNS_1992_1",  "CNS1",     CS_CNS_PLANE_1 },
   { "CNS_1992_2",  "CNS2",     CS_CNS_PLANE_2 },
   { "CNS_1993",    "CNS",      CS_CNS_PLANE_1_2 },
@@ -1104,8 +1093,7 @@ static struct prt_ps_charset_S tc_charsets[] =
 #define CS_KR_ISO10646      (0x08)
 
 // Korean encodings and charsets
-static struct prt_ps_encoding_S k_encodings[] =
-{
+static struct prt_ps_encoding_S k_encodings[] = {
   { "iso-2022-kr", NULL,       CS_KR_X_1992 },
   { "euc-kr",      "EUC",      (CS_KR_X_1992|CS_KR_MAC) },
   { "johab",       "Johab",    CS_KR_X_1992 },
@@ -1115,8 +1103,7 @@ static struct prt_ps_encoding_S k_encodings[] =
   { "ucs-2",       "UCS2",     CS_KR_ISO10646 },
   { "utf-8",       "UTF8",     CS_KR_ISO10646 }
 };
-static struct prt_ps_charset_S k_charsets[] =
-{
+static struct prt_ps_charset_S k_charsets[] = {
   { "KS_X_1992",   "KSC",      CS_KR_X_1992 },
   { "CP1361",      "KSC",      CS_KR_X_1992 },
   { "MAC",         "KSCpc",    CS_KR_MAC },
@@ -1126,8 +1113,7 @@ static struct prt_ps_charset_S k_charsets[] =
   { "ISO10646",    "UniKS",    CS_KR_ISO10646 }
 };
 
-static struct prt_ps_mbfont_S prt_ps_mbfonts[] =
-{
+static struct prt_ps_mbfont_S prt_ps_mbfonts[] = {
   {
     ARRAY_SIZE(j_encodings),
     j_encodings,
@@ -1193,8 +1179,7 @@ static struct prt_ps_mbfont_S prt_ps_mbfonts[] =
 #define PRT_DSC_ENDCOMMENTS         "%%EndComments:"
 
 #define SIZEOF_CSTR(s)      (sizeof(s) - 1)
-static struct prt_dsc_comment_S prt_dsc_table[] =
-{
+static struct prt_dsc_comment_S prt_dsc_table[] = {
   { PRT_DSC_TITLE,       SIZEOF_CSTR(PRT_DSC_TITLE),     PRT_DSC_TITLE_TYPE },
   { PRT_DSC_VERSION,     SIZEOF_CSTR(PRT_DSC_VERSION),
     PRT_DSC_VERSION_TYPE },
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index ff4145c92d..9f4c02da19 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -77,8 +77,7 @@
 #define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT]
 
 /// Message for CTRL-X mode, index is ctrl_x_mode.
-static char *ctrl_x_msgs[] =
-{
+static char *ctrl_x_msgs[] = {
   N_(" Keyword completion (^N^P)"),  // CTRL_X_NORMAL, ^P/^N compl.
   N_(" ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"),
   NULL,  // CTRL_X_SCROLL: depends on state
diff --git a/src/nvim/keycodes.c b/src/nvim/keycodes.c
index 61dc2ac035..de3c1dbf84 100644
--- a/src/nvim/keycodes.c
+++ b/src/nvim/keycodes.c
@@ -46,8 +46,7 @@ static const struct modmasktable {
 
 #define MOD_KEYS_ENTRY_SIZE 5
 
-static char_u modifier_keys_table[] =
-{
+static char_u modifier_keys_table[] = {
   //  mod mask      with modifier               without modifier
   MOD_MASK_SHIFT, '&', '9',                   '@', '1',         // begin
   MOD_MASK_SHIFT, '&', '0',                   '@', '2',         // cancel
@@ -347,8 +346,7 @@ static struct mousetable {
   int button;                 // Which mouse button is it?
   bool is_click;              // Is it a mouse button click event?
   bool is_drag;               // Is it a mouse drag event?
-} mouse_table[] =
-{
+} mouse_table[] = {
   { KE_LEFTMOUSE,        MOUSE_LEFT,     true,   false },
   { KE_LEFTDRAG,         MOUSE_LEFT,     false,  true },
   { KE_LEFTRELEASE,      MOUSE_LEFT,     false,  false },
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index fca07ee146..9d63fe55f9 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -400,7 +400,7 @@ static int nlua_wait(lua_State *lstate)
 
   bool fast_only = false;
   if (lua_top >= 4) {
-    fast_only =  lua_toboolean(lstate, 4);
+    fast_only = lua_toboolean(lstate, 4);
   }
 
   MultiQueue *loop_events = fast_only || in_fast_callback > 0
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index ddcab37e34..9e34c7e413 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -136,8 +136,7 @@ const uint8_t utf8len_tab_zero[] = {
 // "iso-8859-n" is handled by enc_canonize() directly.
 static struct
 {   const char *name;   int prop;              int codepage; }
-enc_canon_table[] =
-{
+enc_canon_table[] = {
 #define IDX_LATIN_1     0
   { "latin1",          ENC_8BIT + ENC_LATIN1,  1252 },
 #define IDX_ISO_2       1
@@ -270,8 +269,7 @@ enc_canon_table[] =
 // Aliases for encoding names.
 static struct
 {   const char *name; int canon; }
-enc_alias_table[] =
-{
+enc_alias_table[] = {
   { "ansi",            IDX_LATIN_1 },
   { "iso-8859-1",      IDX_LATIN_1 },
   { "latin2",          IDX_ISO_2 },
@@ -1024,8 +1022,7 @@ bool utf_printable(int c)
 {
   // Sorted list of non-overlapping intervals.
   // 0xd800-0xdfff is reserved for UTF-16, actually illegal.
-  static struct interval nonprint[] =
-  {
+  static struct interval nonprint[] = {
     { 0x070f, 0x070f }, { 0x180b, 0x180e }, { 0x200b, 0x200f }, { 0x202a, 0x202e },
     { 0x2060, 0x206f }, { 0xd800, 0xdfff }, { 0xfeff, 0xfeff }, { 0xfff9, 0xfffb },
     { 0xfffe, 0xffff }
diff --git a/src/nvim/memory.h b/src/nvim/memory.h
index 1c2ed2ba3b..5c3d18ac93 100644
--- a/src/nvim/memory.h
+++ b/src/nvim/memory.h
@@ -39,7 +39,7 @@ extern MemRealloc mem_realloc;
 extern bool entered_free_all_mem;
 #endif
 
-EXTERN size_t arena_alloc_count INIT(=0);
+EXTERN size_t arena_alloc_count INIT(= 0);
 
 typedef struct consumed_blk {
   struct consumed_blk *prev;
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 6bddd34367..d142af555a 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -144,8 +144,7 @@ static const struct nv_cmd {
   nv_func_T cmd_func;           ///< function for this command
   uint16_t cmd_flags;           ///< NV_ flags
   int16_t cmd_arg;              ///< value for ca.arg
-} nv_cmds[] =
-{
+} nv_cmds[] = {
   { NUL,       nv_error,       0,                      0 },
   { Ctrl_A,    nv_addsub,      0,                      0 },
   { Ctrl_B,    nv_page,        NV_STS,                 BACKWARD },
@@ -4896,7 +4895,7 @@ static void nv_pcmark(cmdarg_T *cap)
       fm = get_changelist(curbuf, curwin, (int)cap->count1);
     } else {
       fm = get_jumplist(curwin, (int)cap->count1);
-      flags |=  KMarkNoContext | kMarkJumpList;
+      flags |= KMarkNoContext | kMarkJumpList;
     }
     // Changelist and jumplist have their own error messages. Therefore avoid
     // calling nv_mark_move_to() when not found to avoid incorrect error
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 29008200a7..7f8a5b6f2e 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -95,8 +95,7 @@ struct block_def {
 // The names of operators.
 // IMPORTANT: Index must correspond with defines in vim.h!!!
 // The third field indicates whether the operator always works on lines.
-static char opchars[][3] =
-{
+static char opchars[][3] = {
   { NUL, NUL, 0 },                       // OP_NOP
   { 'd', NUL, OPF_CHANGE },              // OP_DELETE
   { 'y', NUL, 0 },                       // OP_YANK
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 2e14f26861..acf9b881f8 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -337,8 +337,7 @@ static const size_t LINE_MAXLEN = 4096;
 static struct fmtpattern {
   char convchar;
   char *pattern;
-} fmt_pat[FMT_PATTERNS] =
-{
+} fmt_pat[FMT_PATTERNS] = {
   { 'f', ".\\+" },      // only used when at end
   { 'n', "\\d\\+" },    // 1
   { 'l', "\\d\\+" },    // 2
@@ -7066,7 +7065,7 @@ static void hgr_search_in_rtp(qf_list_T *qfl, regmatch_T *p_regmatch, const char
 void ex_helpgrep(exarg_T *eap)
 {
   qf_info_T *qi = &ql_info;
-  char *au_name =  NULL;
+  char *au_name = NULL;
 
   switch (eap->cmdidx) {
   case CMD_helpgrep:
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 6bd15fdbbe..d6f207a248 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -181,8 +181,7 @@ static int backslash_trans(int c)
 /// recognized.  Otherwise "pp" is advanced to after the item.
 static int get_char_class(char **pp)
 {
-  static const char *(class_names[]) =
-  {
+  static const char *(class_names[]) = {
     "alnum:]",
 #define CLASS_ALNUM 0
     "alpha:]",
@@ -1328,8 +1327,7 @@ typedef struct {
 } decomp_T;
 
 // 0xfb20 - 0xfb4f
-static decomp_T decomp_table[0xfb4f - 0xfb20 + 1] =
-{
+static decomp_T decomp_table[0xfb4f - 0xfb20 + 1] = {
   { 0x5e2, 0, 0 },          // 0xfb20       alt ayin
   { 0x5d0, 0, 0 },          // 0xfb21       alt alef
   { 0x5d3, 0, 0 },          // 0xfb22       alt dalet
@@ -2276,16 +2274,14 @@ list_T *reg_submatch_list(int no)
 # include "nvim/regexp_nfa.c"
 #endif
 
-static regengine_T bt_regengine =
-{
+static regengine_T bt_regengine = {
   bt_regcomp,
   bt_regfree,
   bt_regexec_nl,
   bt_regexec_multi,
 };
 
-static regengine_T nfa_regengine =
-{
+static regengine_T nfa_regengine = {
   nfa_regcomp,
   nfa_regfree,
   nfa_regexec_nl,
diff --git a/src/nvim/search.c b/src/nvim/search.c
index e404d00b60..49892a4cc5 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -80,8 +80,7 @@
 // one for other searches.  last_idx points to the one that was used the last
 // time.
 
-static struct spat spats[2] =
-{
+static struct spat spats[2] = {
   // Last used search pattern
   [0] = { NULL, true, false, 0, { '/', false, false, 0L }, NULL },
   // Last used substitute pattern
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 244e644495..7830dd5206 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -1476,7 +1476,7 @@ static char *shada_filename(const char *file)
       file = p_shadafile;
     } else {
       if ((file = (char *)find_shada_parameter('n')) == NULL || *file == NUL) {
-        file =  shada_get_default_file();
+        file = shada_get_default_file();
       }
       // XXX It used to be one level lower, so that whatever is in
       //     `p_shadafile` was expanded. I intentionally moved it here
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index 10173fac1d..4d1401293b 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -1041,7 +1041,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
             break;
           }
           if (arg > 0) {
-            arg_sign =  1;
+            arg_sign = 1;
           } else if (arg < 0) {
             arg_sign = -1;
           }
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 4934168acf..e5962cd273 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -3344,8 +3344,7 @@ static int last_matchgroup;
 static void syn_list_one(const int id, const bool syncing, const bool link_only)
 {
   bool did_header = false;
-  static struct name_list namelist1[] =
-  {
+  static struct name_list namelist1[] = {
     { HL_DISPLAY, "display" },
     { HL_CONTAINED, "contained" },
     { HL_ONELINE, "oneline" },
@@ -3358,8 +3357,7 @@ static void syn_list_one(const int id, const bool syncing, const bool link_only)
     { HL_CONCEALENDS, "concealends" },
     { 0, NULL }
   };
-  static struct name_list namelist2[] =
-  {
+  static struct name_list namelist2[] = {
     { HL_SKIPWHITE, "skipwhite" },
     { HL_SKIPNL, "skipnl" },
     { HL_SKIPEMPTY, "skipempty" },
@@ -5238,8 +5236,7 @@ struct subcommand {
   void (*func)(exarg_T *, int);              // function to call
 };
 
-static struct subcommand subcommands[] =
-{
+static struct subcommand subcommands[] = {
   { "case",      syn_cmd_case },
   { "clear",     syn_cmd_clear },
   { "cluster",   syn_cmd_cluster },
diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c
index 1cb3b18345..408cdc93a7 100644
--- a/src/nvim/usercmd.c
+++ b/src/nvim/usercmd.c
@@ -36,8 +36,7 @@ static char e_no_such_user_defined_command_in_current_buffer_str[]
 
 /// List of names for completion for ":command" with the EXPAND_ flag.
 /// Must be alphabetical for completion.
-static const char *command_complete[] =
-{
+static const char *command_complete[] = {
   [EXPAND_ARGLIST] = "arglist",
   [EXPAND_AUGROUP] = "augroup",
   [EXPAND_BEHAVE] = "behave",
@@ -86,8 +85,7 @@ static struct {
   cmd_addr_T expand;
   char *name;
   char *shortname;
-} addr_type_complete[] =
-{
+} addr_type_complete[] = {
   { ADDR_ARGUMENTS, "arguments", "arg" },
   { ADDR_LINES, "lines", "line" },
   { ADDR_LOADED_BUFFERS, "loaded_buffers", "load" },
-- 
cgit 


From aeb87f8b4a87e99c392e7ec11b29b715e1f31ac3 Mon Sep 17 00:00:00 2001
From: dundargoc 
Date: Tue, 1 Nov 2022 14:29:17 +0100
Subject: build: add EXCLUDE option to add_glob_target

EXCLUDE filters out all elements containing regex, meaning it works on
both files and directories.

Also rename add_glob_targets to add_glob_target since only one target is
being created.
---
 src/nvim/CMakeLists.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index aee6319770..6511fe6fbb 100755
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -772,7 +772,7 @@ add_custom_target(uncrustify-version
     -D CONFIG_FILE=${PROJECT_SOURCE_DIR}/src/uncrustify.cfg
     -P ${PROJECT_SOURCE_DIR}/cmake/CheckUncrustifyVersion.cmake)
 
-add_glob_targets(
+add_glob_target(
   TARGET lintuncrustify
   COMMAND ${UNCRUSTIFY_PRG}
   FLAGS -c "${PROJECT_SOURCE_DIR}/src/uncrustify.cfg" -q --check
-- 
cgit 


From 41f308feab35007534f0c213947701174d60c548 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Wed, 2 Nov 2022 08:02:52 +0800
Subject: vim-patch:9.0.0826: if 'endofline' is set CTRL-Z may be written in a
 wrong place (#20903)

Problem:    If 'endofline' is set the CTRL-Z may be written in the wrong
            place.
Solution:   Write CTRL-Z at the end of the file.  Update the help to explain
            the possibilities better. (Ken Takata, closes vim/vim#11486)

https://github.com/vim/vim/commit/3af982196b1b973e953c35351961f2a96fe34172

Co-authored-by: K.Takata 
---
 src/nvim/fileio.c                | 38 ++++++++++++++---------
 src/nvim/testdir/test_fixeol.vim | 67 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 90 insertions(+), 15 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 2ba2f4c9c9..c3feadeb36 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -1624,23 +1624,31 @@ failed:
     error = false;
   }
 
+  // In Dos format ignore a trailing CTRL-Z, unless 'binary' is set.
+  // In old days the file length was in sector count and the CTRL-Z the
+  // marker where the file really ended.  Assuming we write it to a file
+  // system that keeps file length properly the CTRL-Z should be dropped.
+  // Set the 'endoffile' option so the user can decide what to write later.
+  // In Unix format the CTRL-Z is just another character.
+  if (linerest != 0
+      && !curbuf->b_p_bin
+      && fileformat == EOL_DOS
+      && ptr[-1] == Ctrl_Z) {
+    ptr--;
+    linerest--;
+    if (set_options) {
+      curbuf->b_p_eof = true;
+    }
+  }
+
   // If we get EOF in the middle of a line, note the fact and
   // complete the line ourselves.
-  // In Dos format ignore a trailing CTRL-Z, unless 'binary' set.
   if (!error
       && !got_int
-      && linerest != 0
-      // TODO(vim): should we handle CTRL-Z differently here for 'endoffile'?
-      && !(!curbuf->b_p_bin
-           && fileformat == EOL_DOS
-           && *line_start == Ctrl_Z
-           && ptr == line_start + 1)) {
+      && linerest != 0) {
     // remember for when writing
     if (set_options) {
       curbuf->b_p_eol = false;
-      if (*line_start == Ctrl_Z && ptr == line_start + 1) {
-        curbuf->b_p_eof = true;
-      }
     }
     *ptr = NUL;
     len = (colnr_T)(ptr - line_start + 1);
@@ -3197,11 +3205,6 @@ restore_backup:
         len = 0;
         write_info.bw_start_lnum = lnum;
       }
-      if (!buf->b_p_fixeol && buf->b_p_eof) {
-        // write trailing CTRL-Z
-        (void)write_eintr(write_info.bw_fd, "\x1a", 1);
-      }
-
       // write failed or last line has no EOL: stop here
       if (end == 0
           || (lnum == end
@@ -3253,6 +3256,11 @@ restore_backup:
       nchars += len;
     }
 
+    if (!buf->b_p_fixeol && buf->b_p_eof) {
+      // write trailing CTRL-Z
+      (void)write_eintr(write_info.bw_fd, "\x1a", 1);
+    }
+
     // Stop when writing done or an error was encountered.
     if (!checking_conversion || end == 0) {
       break;
diff --git a/src/nvim/testdir/test_fixeol.vim b/src/nvim/testdir/test_fixeol.vim
index 9d6c900bdb..41d47d6a06 100644
--- a/src/nvim/testdir/test_fixeol.vim
+++ b/src/nvim/testdir/test_fixeol.vim
@@ -48,4 +48,71 @@ func Test_fixeol()
   enew!
 endfunc
 
+func Test_eof()
+  let data = 0z68656c6c6f.0d0a.776f726c64   " "hello\r\nworld"
+
+  " 1. Eol, Eof
+  " read
+  call writefile(data + 0z0d0a.1a, 'XXEolEof')
+  e! XXEolEof
+  call assert_equal(['hello', 'world'], getline(1, 2))
+  call assert_equal([1, 1], [&eol, &eof])
+  " write
+  set fixeol
+  w!
+  call assert_equal(data + 0z0d0a, readblob('XXEolEof'))
+  set nofixeol
+  w!
+  call assert_equal(data + 0z0d0a.1a, readblob('XXEolEof'))
+
+  " 2. NoEol, Eof
+  " read
+  call writefile(data + 0z1a, 'XXNoEolEof')
+  e! XXNoEolEof
+  call assert_equal(['hello', 'world'], getline(1, 2))
+  call assert_equal([0, 1], [&eol, &eof])
+  " write
+  set fixeol
+  w!
+  call assert_equal(data + 0z0d0a, readblob('XXNoEolEof'))
+  set nofixeol
+  w!
+  call assert_equal(data + 0z1a, readblob('XXNoEolEof'))
+
+  " 3. Eol, NoEof
+  " read
+  call writefile(data + 0z0d0a, 'XXEolNoEof')
+  e! XXEolNoEof
+  call assert_equal(['hello', 'world'], getline(1, 2))
+  call assert_equal([1, 0], [&eol, &eof])
+  " write
+  set fixeol
+  w!
+  call assert_equal(data + 0z0d0a, readblob('XXEolNoEof'))
+  set nofixeol
+  w!
+  call assert_equal(data + 0z0d0a, readblob('XXEolNoEof'))
+
+  " 4. NoEol, NoEof
+  " read
+  call writefile(data, 'XXNoEolNoEof')
+  e! XXNoEolNoEof
+  call assert_equal(['hello', 'world'], getline(1, 2))
+  call assert_equal([0, 0], [&eol, &eof])
+  " write
+  set fixeol
+  w!
+  call assert_equal(data + 0z0d0a, readblob('XXNoEolNoEof'))
+  set nofixeol
+  w!
+  call assert_equal(data, readblob('XXNoEolNoEof'))
+
+  call delete('XXEolEof')
+  call delete('XXNoEolEof')
+  call delete('XXEolNoEof')
+  call delete('XXNoEolNoEof')
+  set ff& fixeol& eof& eol&
+  enew!
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From a600e73007a2cc9ced7eeaeb5f8c05ac454d080e Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Tue, 1 Nov 2022 07:13:02 +0800
Subject: vim-patch:9.0.0822: crash when dragging the statusline with a mapping

Problem:    Crash when dragging the statusline with a mapping.
Solution:   Check for valid window pointer. (issue vim/vim#11427)

https://github.com/vim/vim/commit/8ab9ca93eea32b318235384720200771863ecaee

Co-authored-by: Bram Moolenaar 
---
 src/nvim/mouse.c                  | 29 ++++++++++++++++++-----------
 src/nvim/testdir/test_mapping.vim | 18 ++++++++++++++++++
 src/nvim/window.c                 |  2 ++
 3 files changed, 38 insertions(+), 11 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 88dd81da8b..94bc13a3dc 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -227,6 +227,15 @@ static int get_fpos_of_mouse(pos_T *mpos)
   return IN_BUFFER;
 }
 
+static bool mouse_got_click = false;  ///< got a click some time back
+
+/// Reset the flag that a mouse click was seen.  To be called when switching tab
+/// page.
+void reset_mouse_got_click(void)
+{
+  mouse_got_click = false;
+}
+
 /// Do the appropriate action for the current mouse click in the current mode.
 /// Not used for Command-line mode.
 ///
@@ -268,8 +277,6 @@ static int get_fpos_of_mouse(pos_T *mpos)
 /// @return           true if start_arrow() should be called for edit mode.
 bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
 {
-  static bool got_click = false;        // got a click some time back
-
   int which_button;             // MOUSE_LEFT, _MIDDLE or _RIGHT
   bool is_click;                // If false it's a drag or release event
   bool is_drag;                 // If true it's a drag event
@@ -328,13 +335,13 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
 
   // Ignore drag and release events if we didn't get a click.
   if (is_click) {
-    got_click = true;
+    mouse_got_click = true;
   } else {
-    if (!got_click) {                   // didn't get click, ignore
+    if (!mouse_got_click) {             // didn't get click, ignore
       return false;
     }
-    if (!is_drag) {                     // release, reset got_click
-      got_click = false;
+    if (!is_drag) {                     // release, reset mouse_got_click
+      mouse_got_click = false;
       if (in_tab_line) {
         in_tab_line = false;
         return false;
@@ -351,7 +358,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
       stuffnumReadbuff(count);
     }
     stuffcharReadbuff(Ctrl_T);
-    got_click = false;                  // ignore drag&release now
+    mouse_got_click = false;  // ignore drag&release now
     return false;
   }
 
@@ -574,7 +581,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
         ui_flush();  // Update before showing popup menu
       }
       show_popupmenu();
-      got_click = false;  // ignore release events
+      mouse_got_click = false;  // ignore release events
       return (jump_flags & CURSOR_MOVED) != 0;
     }
     if (which_button == MOUSE_LEFT
@@ -613,7 +620,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
 
   // If an operator is pending, ignore all drags and releases until the next mouse click.
   if (!is_drag && oap != NULL && oap->op_type != OP_NOP) {
-    got_click = false;
+    mouse_got_click = false;
     oap->motion_type = kMTCharWise;
   }
 
@@ -815,7 +822,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
     } else {                                    // location list window
       do_cmdline_cmd(".ll");
     }
-    got_click = false;                  // ignore drag&release now
+    mouse_got_click = false;  // ignore drag&release now
   } else if ((mod_mask & MOD_MASK_CTRL)
              || (curbuf->b_help && (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)) {
     // Ctrl-Mouse click (or double click in a help window) jumps to the tag
@@ -824,7 +831,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
       stuffcharReadbuff(Ctrl_O);
     }
     stuffcharReadbuff(Ctrl_RSB);
-    got_click = false;                  // ignore drag&release now
+    mouse_got_click = false;  // ignore drag&release now
   } else if ((mod_mask & MOD_MASK_SHIFT)) {
     // Shift-Mouse click searches for the next occurrence of the word under
     // the mouse pointer
diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim
index 2d8c45210b..a286774d56 100644
--- a/src/nvim/testdir/test_mapping.vim
+++ b/src/nvim/testdir/test_mapping.vim
@@ -1050,6 +1050,24 @@ func Test_mouse_drag_mapped_start_select()
   set mouse&
 endfunc
 
+func Test_mouse_drag_statusline()
+  set laststatus=2
+  set mouse=a
+  func ClickExpr()
+      call Ntest_setmouse(&lines - 1, 1)
+        return "\"
+  endfunc
+  func DragExpr()
+      call Ntest_setmouse(&lines - 2, 1)
+        return "\"
+  endfunc
+  nnoremap   ClickExpr()
+  nnoremap   DragExpr()
+
+  " this was causing a crash in win_drag_status_line()
+  call feedkeys("\:tabnew\\", 'tx')
+endfunc
+
 " Test for mapping  in Insert mode
 func Test_mouse_drag_insert_map()
   set mouse=a
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 4812b9ef9d..8c34e4fb6c 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -4195,6 +4195,7 @@ static int leave_tabpage(buf_T *new_curbuf, bool trigger_leave_autocmds)
 {
   tabpage_T *tp = curtab;
 
+  reset_mouse_got_click();
   leaving_window(curwin);
   reset_VIsual_and_resel();     // stop Visual mode
   if (trigger_leave_autocmds) {
@@ -4392,6 +4393,7 @@ void goto_tabpage_tp(tabpage_T *tp, bool trigger_enter_autocmds, bool trigger_le
   // Don't repeat a message in another tab page.
   set_keep_msg(NULL, 0);
 
+  reset_mouse_got_click();
   skip_win_fix_scroll = true;
   if (tp != curtab && leave_tabpage(tp->tp_curwin->w_buffer,
                                     trigger_leave_autocmds) == OK) {
-- 
cgit 


From 20bd4d89977005845c070cde9df75496f948fa1e Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Tue, 1 Nov 2022 10:21:15 +0800
Subject: vim-patch:9.0.0823: mouse drag test fails

Problem:    Mouse drag test fails.
Solution:   Only reset the mouse click flag when actually switching to another
            tab page.  Disable test that keeps failing.

https://github.com/vim/vim/commit/7a7db047dcb2336de5103e793345eb5a9d125900

Omit test_termcodes.vim change: reverted in patch 9.0.0825.

Co-authored-by: Bram Moolenaar 
---
 src/nvim/window.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/window.c b/src/nvim/window.c
index 8c34e4fb6c..c1ed2b7920 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -4195,7 +4195,6 @@ static int leave_tabpage(buf_T *new_curbuf, bool trigger_leave_autocmds)
 {
   tabpage_T *tp = curtab;
 
-  reset_mouse_got_click();
   leaving_window(curwin);
   reset_VIsual_and_resel();     // stop Visual mode
   if (trigger_leave_autocmds) {
@@ -4214,6 +4213,8 @@ static int leave_tabpage(buf_T *new_curbuf, bool trigger_leave_autocmds)
       return FAIL;
     }
   }
+
+  reset_mouse_got_click();
   tp->tp_curwin = curwin;
   tp->tp_prevwin = prevwin;
   tp->tp_firstwin = firstwin;
@@ -4276,6 +4277,10 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a
     clear_cmdline = true;
   }
 
+  // If there was a click in a window, it won't be usable for a following
+  // drag.
+  reset_mouse_got_click();
+
   // The tabpage line may have appeared or disappeared, may need to resize the frames for that.
   // When the Vim window was resized or ROWS_AVAIL changed need to update frame sizes too.
   if (curtab->tp_old_Rows_avail != ROWS_AVAIL || (old_off != firstwin->w_winrow)) {
@@ -4393,7 +4398,6 @@ void goto_tabpage_tp(tabpage_T *tp, bool trigger_enter_autocmds, bool trigger_le
   // Don't repeat a message in another tab page.
   set_keep_msg(NULL, 0);
 
-  reset_mouse_got_click();
   skip_win_fix_scroll = true;
   if (tp != curtab && leave_tabpage(tp->tp_curwin->w_buffer,
                                     trigger_leave_autocmds) == OK) {
-- 
cgit 


From 39f85cdf6b40cbdd26256260d0d6d4e071b631a2 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Tue, 1 Nov 2022 20:22:48 +0800
Subject: vim-patch:9.0.0824: crash when using win_move_separator() in other
 tab page

Problem:    Crash when using win_move_separator() in other tab page.
Solution:   Check for valid window in current tab page.
            (closes vim/vim#11479, closes vim/vim#11427)

https://github.com/vim/vim/commit/873f41a0187e81a22aa4622fbf938de72a54abba
---
 src/nvim/eval/funcs.c                |  4 ++++
 src/nvim/testdir/test_mapping.vim    | 14 ++++++++++----
 src/nvim/testdir/test_window_cmd.vim | 10 ++++++++++
 3 files changed, 24 insertions(+), 4 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 79c93f1917..e676e2e656 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -9690,6 +9690,10 @@ static void f_win_move_separator(typval_T *argvars, typval_T *rettv, EvalFuncDat
   if (wp == NULL || wp->w_floating) {
     return;
   }
+  if (!win_valid(wp)) {
+    emsg(_(e_cannot_resize_window_in_another_tab_page));
+    return;
+  }
 
   int offset = (int)tv_get_number(&argvars[1]);
   win_drag_vsep_line(wp, offset);
diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim
index a286774d56..560883ba5d 100644
--- a/src/nvim/testdir/test_mapping.vim
+++ b/src/nvim/testdir/test_mapping.vim
@@ -1054,18 +1054,24 @@ func Test_mouse_drag_statusline()
   set laststatus=2
   set mouse=a
   func ClickExpr()
-      call Ntest_setmouse(&lines - 1, 1)
-        return "\"
+    call Ntest_setmouse(&lines - 1, 1)
+    return "\"
   endfunc
   func DragExpr()
-      call Ntest_setmouse(&lines - 2, 1)
-        return "\"
+    call Ntest_setmouse(&lines - 2, 1)
+    return "\"
   endfunc
   nnoremap   ClickExpr()
   nnoremap   DragExpr()
 
   " this was causing a crash in win_drag_status_line()
   call feedkeys("\:tabnew\\", 'tx')
+
+  nunmap 
+  nunmap 
+  delfunc ClickExpr
+  delfunc DragExpr
+  set laststatus& mouse&
 endfunc
 
 " Test for mapping  in Insert mode
diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim
index 902a3791d4..f38eaaf318 100644
--- a/src/nvim/testdir/test_window_cmd.vim
+++ b/src/nvim/testdir/test_window_cmd.vim
@@ -1393,17 +1393,20 @@ func Test_win_move_separator()
   call assert_equal(w0, winwidth(0))
   call assert_true(win_move_separator(0, -1))
   call assert_equal(w0, winwidth(0))
+
   " check that win_move_separator doesn't error with offsets beyond moving
   " possibility
   call assert_true(win_move_separator(id, 5000))
   call assert_true(winwidth(id) > w)
   call assert_true(win_move_separator(id, -5000))
   call assert_true(winwidth(id) < w)
+
   " check that win_move_separator returns false for an invalid window
   wincmd =
   let w = winwidth(0)
   call assert_false(win_move_separator(-1, 1))
   call assert_equal(w, winwidth(0))
+
   " check that win_move_separator returns false for a floating window
   let id = nvim_open_win(
         \ 0, 0, #{relative: 'editor', row: 2, col: 2, width: 5, height: 3})
@@ -1411,6 +1414,13 @@ func Test_win_move_separator()
   call assert_false(win_move_separator(id, 1))
   call assert_equal(w, winwidth(id))
   call nvim_win_close(id, 1)
+
+  " check that using another tabpage fails without crash
+  let id = win_getid()
+  tabnew
+  call assert_fails('call win_move_separator(id, -1)', 'E1308:')
+  tabclose
+
   %bwipe!
 endfunc
 
-- 
cgit 


From 419ee612e692fb8985342a2091a0d9bf6dfffe46 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Wed, 2 Nov 2022 08:04:00 +0800
Subject: vim-patch:9.0.0825: cannot drag an entry in the tabpage line

Problem:    Cannot drag an entry in the tabpage line.
Solution:   Clear dragwin instead of got_click. (closes vim/vim#11483,
            closes vim/vim#11482)

https://github.com/vim/vim/commit/8e0ccb6bc21a446e5c6375b7fdf200fb53a129da

Omit Test_term_mouse_drag_to_move_tab(): covered by ui/mouse_spec.lua.
---
 src/nvim/mouse.c  | 38 +++++++++++++++++++-------------------
 src/nvim/window.c |  4 ++--
 2 files changed, 21 insertions(+), 21 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 94bc13a3dc..53431187af 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -227,15 +227,6 @@ static int get_fpos_of_mouse(pos_T *mpos)
   return IN_BUFFER;
 }
 
-static bool mouse_got_click = false;  ///< got a click some time back
-
-/// Reset the flag that a mouse click was seen.  To be called when switching tab
-/// page.
-void reset_mouse_got_click(void)
-{
-  mouse_got_click = false;
-}
-
 /// Do the appropriate action for the current mouse click in the current mode.
 /// Not used for Command-line mode.
 ///
@@ -277,6 +268,8 @@ void reset_mouse_got_click(void)
 /// @return           true if start_arrow() should be called for edit mode.
 bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
 {
+  static bool got_click = false;        // got a click some time back
+
   int which_button;             // MOUSE_LEFT, _MIDDLE or _RIGHT
   bool is_click;                // If false it's a drag or release event
   bool is_drag;                 // If true it's a drag event
@@ -335,13 +328,13 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
 
   // Ignore drag and release events if we didn't get a click.
   if (is_click) {
-    mouse_got_click = true;
+    got_click = true;
   } else {
-    if (!mouse_got_click) {             // didn't get click, ignore
+    if (!got_click) {                   // didn't get click, ignore
       return false;
     }
-    if (!is_drag) {                     // release, reset mouse_got_click
-      mouse_got_click = false;
+    if (!is_drag) {                     // release, reset got_click
+      got_click = false;
       if (in_tab_line) {
         in_tab_line = false;
         return false;
@@ -358,7 +351,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
       stuffnumReadbuff(count);
     }
     stuffcharReadbuff(Ctrl_T);
-    mouse_got_click = false;  // ignore drag&release now
+    got_click = false;                  // ignore drag&release now
     return false;
   }
 
@@ -581,7 +574,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
         ui_flush();  // Update before showing popup menu
       }
       show_popupmenu();
-      mouse_got_click = false;  // ignore release events
+      got_click = false;  // ignore release events
       return (jump_flags & CURSOR_MOVED) != 0;
     }
     if (which_button == MOUSE_LEFT
@@ -620,7 +613,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
 
   // If an operator is pending, ignore all drags and releases until the next mouse click.
   if (!is_drag && oap != NULL && oap->op_type != OP_NOP) {
-    mouse_got_click = false;
+    got_click = false;
     oap->motion_type = kMTCharWise;
   }
 
@@ -822,7 +815,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
     } else {                                    // location list window
       do_cmdline_cmd(".ll");
     }
-    mouse_got_click = false;  // ignore drag&release now
+    got_click = false;                  // ignore drag&release now
   } else if ((mod_mask & MOD_MASK_CTRL)
              || (curbuf->b_help && (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)) {
     // Ctrl-Mouse click (or double click in a help window) jumps to the tag
@@ -831,7 +824,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
       stuffcharReadbuff(Ctrl_O);
     }
     stuffcharReadbuff(Ctrl_RSB);
-    mouse_got_click = false;  // ignore drag&release now
+    got_click = false;                  // ignore drag&release now
   } else if ((mod_mask & MOD_MASK_SHIFT)) {
     // Shift-Mouse click searches for the next occurrence of the word under
     // the mouse pointer
@@ -976,6 +969,14 @@ static bool mouse_model_popup(void)
   return p_mousem[0] == 'p';
 }
 
+static win_T *dragwin = NULL;  ///< window being dragged
+
+/// Reset the window being dragged.  To be called when switching tab page.
+void reset_dragwin(void)
+{
+  dragwin = NULL;
+}
+
 /// Move the cursor to the specified row and column on the screen.
 /// Change current window if necessary. Returns an integer with the
 /// CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
@@ -1012,7 +1013,6 @@ int jump_to_mouse(int flags, bool *inclusive, int which_button)
   static bool on_winbar = false;
   static int prev_row = -1;
   static int prev_col = -1;
-  static win_T *dragwin = NULL;         // window being dragged
   static int did_drag = false;          // drag was noticed
 
   win_T *wp, *old_curwin;
diff --git a/src/nvim/window.c b/src/nvim/window.c
index c1ed2b7920..c755f58c4f 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -4214,7 +4214,7 @@ static int leave_tabpage(buf_T *new_curbuf, bool trigger_leave_autocmds)
     }
   }
 
-  reset_mouse_got_click();
+  reset_dragwin();
   tp->tp_curwin = curwin;
   tp->tp_prevwin = prevwin;
   tp->tp_firstwin = firstwin;
@@ -4279,7 +4279,7 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a
 
   // If there was a click in a window, it won't be usable for a following
   // drag.
-  reset_mouse_got_click();
+  reset_dragwin();
 
   // The tabpage line may have appeared or disappeared, may need to resize the frames for that.
   // When the Vim window was resized or ROWS_AVAIL changed need to update frame sizes too.
-- 
cgit 


From 4716a578ae0c3516d685495bb55e40c939a4ac87 Mon Sep 17 00:00:00 2001
From: dundargoc 
Date: Sun, 23 Oct 2022 10:17:45 +0200
Subject: docs: fix typos

---
 src/nvim/README.md                   |  4 ++--
 src/nvim/eval.c                      |  6 +++---
 src/nvim/eval/userfunc.c             |  2 +-
 src/nvim/ex_docmd.c                  |  2 +-
 src/nvim/mapping.c                   |  2 +-
 src/nvim/ops.c                       |  2 +-
 src/nvim/options.lua                 |  2 +-
 src/nvim/runtime.c                   |  2 +-
 src/nvim/shada.c                     | 18 +++++++++---------
 src/nvim/spell.c                     |  4 ++--
 src/nvim/spellfile.c                 |  2 +-
 src/nvim/testdir/test_options.vim    |  2 +-
 src/nvim/testdir/test_vimscript.vim  |  2 +-
 src/nvim/testdir/test_window_cmd.vim |  2 +-
 src/nvim/usercmd.c                   |  4 ++--
 15 files changed, 28 insertions(+), 28 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/README.md b/src/nvim/README.md
index 91fb3ca2f6..e710c3ef58 100644
--- a/src/nvim/README.md
+++ b/src/nvim/README.md
@@ -391,8 +391,8 @@ implemented by libuv, the platform layer used by Nvim.
 
 Since Nvim inherited its code from Vim, the states are not prepared to receive
 "arbitrary events", so we use a special key to represent those (When a state
-receives an "arbitrary event", it normally doesn't do anything other update the
-screen).
+receives an "arbitrary event", it normally doesn't do anything other than
+update the screen).
 
 Main loop
 ---------
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 89ae4a2cd0..bc669a3e11 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -3051,7 +3051,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
   case '#':
     if ((*arg)[1] == '{') {
       (*arg)++;
-      ret = dict_get_tv(arg, rettv, evaluate, true);
+      ret = eval_dict(arg, rettv, evaluate, true);
     } else {
       ret = NOTDONE;
     }
@@ -3062,7 +3062,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
   case '{':
     ret = get_lambda_tv(arg, rettv, evaluate);
     if (ret == NOTDONE) {
-      ret = dict_get_tv(arg, rettv, evaluate, false);
+      ret = eval_dict(arg, rettv, evaluate, false);
     }
     break;
 
@@ -4595,7 +4595,7 @@ static int get_literal_key(char **arg, typval_T *tv)
 /// "literal" is true for #{key: val}
 ///
 /// @return  OK or FAIL.  Returns NOTDONE for {expr}.
-static int dict_get_tv(char **arg, typval_T *rettv, int evaluate, bool literal)
+static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal)
 {
   typval_T tv;
   char *key = NULL;
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index c22718c65d..71f33fe465 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -460,7 +460,7 @@ int get_func_tv(const char_u *name, int len, typval_T *rettv, char **arg, funcex
     int i = 0;
 
     if (get_vim_var_nr(VV_TESTING)) {
-      // Prepare for calling garbagecollect_for_testing(), need to know
+      // Prepare for calling test_garbagecollect_now(), need to know
       // what variables are used on the call stack.
       if (funcargs.ga_itemsize == 0) {
         ga_init(&funcargs, (int)sizeof(typval_T *), 50);
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index e867bd57d4..d1a1b496f4 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -5212,7 +5212,7 @@ void do_exedit(exarg_T *eap, win_T *old_curwin)
                   old_curwin == NULL ? curwin : NULL);
   } else if ((eap->cmdidx != CMD_split && eap->cmdidx != CMD_vsplit)
              || *eap->arg != NUL) {
-    // Can't edit another file when "curbuf->b_ro_lockec" is set.  Only ":edit"
+    // Can't edit another file when "curbuf->b_ro_locked" is set.  Only ":edit"
     // can bring us here, others are stopped earlier.
     if (*eap->arg != NUL && curbuf_locked()) {
       return;
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index 2b42d7725b..0fff48019b 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -176,7 +176,7 @@ static void showmap(mapblock_T *mp, bool local)
   // Display the LHS.  Get length of what we write.
   len = (size_t)msg_outtrans_special((char *)mp->m_keys, true, 0);
   do {
-    msg_putchar(' ');                   // padd with blanks
+    msg_putchar(' ');                   // pad with blanks
     len++;
   } while (len < 12);
 
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 7f8a5b6f2e..2d53918ded 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -3272,7 +3272,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
 
       shortline = (vcol < col) || (vcol == col && !*ptr);
 
-      if (vcol < col) {     // line too short, padd with spaces
+      if (vcol < col) {     // line too short, pad with spaces
         bd.startspaces = col - vcol;
       } else if (vcol > col) {
         bd.endspaces = vcol - col;
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 088aa40fda..3a59becb33 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -19,7 +19,7 @@
 -- types: bool, number, string
 -- lists: (nil), comma, onecomma, flags, flagscomma
 -- scopes: global, buffer, window
--- redraw options: statuslines, tabline, current_window, curent_window_only,
+-- redraw options: statuslines, tabline, current_window, current_window_only,
 --                 current_buffer, all_windows, curswant
 -- defaults: {condition=#if condition, if_true=default, if_false=default}
 -- #if condition:
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index fa7aa35a4d..6becd50910 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -1633,7 +1633,7 @@ void ex_options(exarg_T *eap)
   bool multi_mods = 0;
 
   buf[0] = NUL;
-  (void)add_win_cmd_modifers(buf, &cmdmod, &multi_mods);
+  (void)add_win_cmd_modifiers(buf, &cmdmod, &multi_mods);
 
   os_setenv("OPTWIN_CMD", buf, 1);
   cmd_source(SYS_OPTWIN_FILE, NULL);
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 7830dd5206..40b3429de9 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -166,7 +166,7 @@ typedef enum {
 
 /// Possible results of shada_write function.
 typedef enum {
-  kSDWriteSuccessfull,   ///< Writing was successful.
+  kSDWriteSuccessful,    ///< Writing was successful.
   kSDWriteReadNotShada,  ///< Writing was successful, but when reading it
                          ///< attempted to read file that did not look like
                          ///< a ShaDa file.
@@ -1511,7 +1511,7 @@ static char *shada_filename(const char *file)
 /// @param[in]  max_kbyte  Maximum size of an item in KiB. Zero means no
 ///                        restrictions.
 ///
-/// @return kSDWriteSuccessfull, kSDWriteFailed or kSDWriteIgnError.
+/// @return kSDWriteSuccessful, kSDWriteFailed or kSDWriteIgnError.
 static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntry entry,
                                          const size_t max_kbyte)
   FUNC_ATTR_NONNULL_ALL
@@ -1819,7 +1819,7 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntr
   }
   msgpack_packer_free(spacker);
   msgpack_sbuffer_destroy(&sbuf);
-  return kSDWriteSuccessfull;
+  return kSDWriteSuccessful;
 shada_pack_entry_error:
   msgpack_packer_free(spacker);
   msgpack_sbuffer_destroy(&sbuf);
@@ -1839,7 +1839,7 @@ static inline ShaDaWriteResult shada_pack_pfreed_entry(msgpack_packer *const pac
                                                        const size_t max_kbyte)
   FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE
 {
-  ShaDaWriteResult ret = kSDWriteSuccessfull;
+  ShaDaWriteResult ret = kSDWriteSuccessful;
   ret = shada_pack_entry(packer, entry.data, max_kbyte);
   if (entry.can_free_entry) {
     shada_free_shada_entry(&entry.data);
@@ -2053,7 +2053,7 @@ static inline ShaDaWriteResult shada_read_when_writing(ShaDaReadDef *const sd_re
                                                        msgpack_packer *const packer)
   FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
 {
-  ShaDaWriteResult ret = kSDWriteSuccessfull;
+  ShaDaWriteResult ret = kSDWriteSuccessful;
   ShadaEntry entry;
   ShaDaReadResult srni_ret;
 
@@ -2492,7 +2492,7 @@ static int hist_type2char(const int type)
 static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef *const sd_reader)
   FUNC_ATTR_NONNULL_ARG(1)
 {
-  ShaDaWriteResult ret = kSDWriteSuccessfull;
+  ShaDaWriteResult ret = kSDWriteSuccessful;
   int max_kbyte_i = get_shada_parameter('s');
   if (max_kbyte_i < 0) {
     max_kbyte_i = 10;
@@ -2652,7 +2652,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef
       }
       tv_clear(&vartv);
       tv_clear(&tgttv);
-      if (spe_ret == kSDWriteSuccessfull) {
+      if (spe_ret == kSDWriteSuccessful) {
         int kh_ret;
         (void)kh_put(strset, &wms->dumped_variables, name, &kh_ret);
       }
@@ -2817,7 +2817,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef
   if (sd_reader != NULL) {
     const ShaDaWriteResult srww_ret = shada_read_when_writing(sd_reader, srni_flags, max_kbyte, wms,
                                                               packer);
-    if (srww_ret != kSDWriteSuccessfull) {
+    if (srww_ret != kSDWriteSuccessful) {
       ret = srww_ret;
     }
   }
@@ -3078,7 +3078,7 @@ shada_write_file_nomerge: {}
   if (!nomerge) {
     sd_reader.close(&sd_reader);
     bool did_remove = false;
-    if (sw_ret == kSDWriteSuccessfull) {
+    if (sw_ret == kSDWriteSuccessful) {
 #ifdef UNIX
       // For Unix we check the owner of the file.  It's not very nice to
       // overwrite a user’s viminfo file after a "su root", with a
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index 1bd0b9c85f..e76ac49b5d 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -50,7 +50,7 @@
 // Use SPELL_PRINTTREE for debugging: dump the word tree after adding a word.
 // Only use it for small word lists!
 
-// Use SPELL_COMPRESS_ALLWAYS for debugging: compress the word tree after
+// Use SPELL_COMPRESS_ALWAYS for debugging: compress the word tree after
 // adding a word.  Only use it for small word lists!
 
 // Use DEBUG_TRIEWALK to print the changes made in suggest_trie_walk() for a
@@ -160,7 +160,7 @@ typedef struct matchinf_S {
   win_T *mi_win;                  // buffer being checked
 
   // for NOBREAK
-  int mi_result2;                       // "mi_resul" without following word
+  int mi_result2;                       // "mi_result" without following word
   char_u *mi_end2;                 // "mi_end" without following word
 } matchinf_T;
 
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index d5fbbaff1f..7837f242b5 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -4089,7 +4089,7 @@ static int tree_add_word(spellinfo_T *spin, const char_u *word, wordnode_T *root
   // 3. When compressed before, added "compress_added" words
   //    (si_compress_cnt == 1) and the number of free nodes drops below the
   //    maximum word length.
-#ifndef SPELL_COMPRESS_ALLWAYS
+#ifndef SPELL_COMPRESS_ALWAYS
   if (spin->si_compress_cnt == 1       // NOLINT(readability/braces)
       ? spin->si_free_count < MAXWLEN
       : spin->si_blocks_cnt >= compress_start)
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index 2836e81c4a..3916d7c554 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -263,7 +263,7 @@ func Test_set_completion()
   call feedkeys(":setglobal di\\\"\", 'tx')
   call assert_equal('"setglobal dictionary diff diffexpr diffopt digraph directory display', @:)
 
-  " Expand boolan options. When doing :set no
+  " Expand boolean options. When doing :set no
   " vim displays the options names without "no" but completion uses "no...".
   call feedkeys(":set nodi\\\"\", 'tx')
   call assert_equal('"set nodiff digraph', @:)
diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim
index f20fd12ed3..bd60dcb707 100644
--- a/src/nvim/testdir/test_vimscript.vim
+++ b/src/nvim/testdir/test_vimscript.vim
@@ -1443,7 +1443,7 @@ endfunc
 "	    Undefined behavior was detected by ubsan with line continuation
 "	    after an empty line.
 "-------------------------------------------------------------------------------
-func Test_script_emty_line_continuation()
+func Test_script_empty_line_continuation()
 
     \
 endfunc
diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim
index f38eaaf318..909db3a1bb 100644
--- a/src/nvim/testdir/test_window_cmd.vim
+++ b/src/nvim/testdir/test_window_cmd.vim
@@ -1214,7 +1214,7 @@ endfunc
 
 " Test for jumping to a vertical/horizontal neighbor window based on the
 " current cursor position
-func Test_window_goto_neightbor()
+func Test_window_goto_neighbor()
   %bw!
 
   " Vertical window movement
diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c
index 408cdc93a7..bed4d55d4e 100644
--- a/src/nvim/usercmd.c
+++ b/src/nvim/usercmd.c
@@ -1215,7 +1215,7 @@ static size_t add_cmd_modifier(char *buf, char *mod_str, bool *multi_mods)
 /// was added.
 ///
 /// @return the number of bytes added
-size_t add_win_cmd_modifers(char *buf, const cmdmod_T *cmod, bool *multi_mods)
+size_t add_win_cmd_modifiers(char *buf, const cmdmod_T *cmod, bool *multi_mods)
 {
   size_t result = 0;
 
@@ -1320,7 +1320,7 @@ size_t uc_mods(char *buf, const cmdmod_T *cmod, bool quote)
     }
   }
   // flags from cmod->cmod_split
-  result += add_win_cmd_modifers(buf, cmod, &multi_mods);
+  result += add_win_cmd_modifiers(buf, cmod, &multi_mods);
 
   if (quote && buf != NULL) {
     buf += result - 2;
-- 
cgit 


From 04fbb1de4488852c3ba332898b17180500f8984e Mon Sep 17 00:00:00 2001
From: Jonathon <32371757+jwhite510@users.noreply.github.com>
Date: Fri, 4 Nov 2022 05:07:22 -0400
Subject: Enable new diff option linematch (#14537)

Co-authored-by: Lewis Russell 
---
 src/nvim/buffer_defs.h |   2 +
 src/nvim/diff.c        | 431 +++++++++++++++++++++++++++++++++++++++++++------
 src/nvim/drawline.c    |  11 +-
 src/nvim/eval/funcs.c  |   7 +-
 src/nvim/linematch.c   | 377 ++++++++++++++++++++++++++++++++++++++++++
 src/nvim/linematch.h   |  10 ++
 src/nvim/lua/xdiff.c   | 122 +++++++++++---
 7 files changed, 879 insertions(+), 81 deletions(-)
 create mode 100644 src/nvim/linematch.c
 create mode 100644 src/nvim/linematch.h

(limited to 'src/nvim')

diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index d8edd4b2d0..3629199f9a 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -894,6 +894,8 @@ struct diffblock_S {
   diff_T *df_next;
   linenr_T df_lnum[DB_COUNT];           // line number in buffer
   linenr_T df_count[DB_COUNT];          // nr of inserted/changed lines
+  bool is_linematched;  // has the linematch algorithm ran on this diff hunk to divide it into
+                        // smaller diff hunks?
 };
 
 #define SNAP_HELP_IDX   0
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 964db599a3..503e546562 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -27,6 +27,7 @@
 #include "nvim/fileio.h"
 #include "nvim/fold.h"
 #include "nvim/garray.h"
+#include "nvim/linematch.h"
 #include "nvim/mark.h"
 #include "nvim/mbyte.h"
 #include "nvim/memline.h"
@@ -62,10 +63,12 @@ static bool diff_need_update = false;  // ex_diffupdate needs to be called
 #define DIFF_INTERNAL   0x200   // use internal xdiff algorithm
 #define DIFF_CLOSE_OFF  0x400   // diffoff when closing window
 #define DIFF_FOLLOWWRAP 0x800   // follow the wrap option
+#define DIFF_LINEMATCH  0x1000  // match most similar lines within diff
 #define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL)
 static int diff_flags = DIFF_INTERNAL | DIFF_FILLER | DIFF_CLOSE_OFF;
 
 static long diff_algorithm = 0;
+static int linematch_lines = 0;
 
 #define LBUFLEN 50               // length of line in diff file
 
@@ -362,7 +365,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
     if (last >= line1 - 1) {
       // 6. change below line2: only adjust for amount_after; also when
       // "deleted" became zero when deleted all lines between two diffs.
-      if (dp->df_lnum[idx] - (deleted + inserted != 0) > line2) {
+      if (dp->df_lnum[idx] - (deleted + inserted != 0) > line2 - dp->is_linematched) {
         if (amount_after == 0) {
           // nothing left to change
           break;
@@ -454,7 +457,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
     }
 
     // check if this block touches the previous one, may merge them.
-    if ((dprev != NULL)
+    if ((dprev != NULL) && !dp->is_linematched
         && (dprev->df_lnum[idx] + dprev->df_count[idx] == dp->df_lnum[idx])) {
       int i;
       for (i = 0; i < DB_COUNT; i++) {
@@ -513,6 +516,7 @@ static diff_T *diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp)
 {
   diff_T *dnew = xmalloc(sizeof(*dnew));
 
+  dnew->is_linematched = false;
   dnew->df_next = dp;
   if (dprev == NULL) {
     tp->tp_first_diff = dnew;
@@ -727,12 +731,16 @@ static void clear_diffout(diffout_T *dout)
 /// @param din
 ///
 /// @return FAIL for failure.
-static int diff_write_buffer(buf_T *buf, mmfile_t *m)
+static int diff_write_buffer(buf_T *buf, mmfile_t *m, linenr_T start, linenr_T end)
 {
   size_t len = 0;
 
+  if (end < 0) {
+    end = buf->b_ml.ml_line_count;
+  }
+
   // xdiff requires one big block of memory with all the text.
-  for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
+  for (linenr_T lnum = start; lnum <= end; lnum++) {
     len += strlen(ml_get_buf(buf, lnum, false)) + 1;
   }
   char *ptr = try_malloc(len);
@@ -753,7 +761,7 @@ static int diff_write_buffer(buf_T *buf, mmfile_t *m)
   m->size = (long)len;
 
   len = 0;
-  for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
+  for (linenr_T lnum = start; lnum <= end; lnum++) {
     char *s = ml_get_buf(buf, lnum, false);
     if (diff_flags & DIFF_ICASE) {
       while (*s != NUL) {
@@ -792,7 +800,7 @@ static int diff_write_buffer(buf_T *buf, mmfile_t *m)
 static int diff_write(buf_T *buf, diffin_T *din)
 {
   if (din->din_fname == NULL) {
-    return diff_write_buffer(buf, &din->din_mmfile);
+    return diff_write_buffer(buf, &din->din_mmfile, 1, -1);
   }
 
   // Always use 'fileformat' set to "unix".
@@ -1784,6 +1792,294 @@ void diff_clear(tabpage_T *tp)
   tp->tp_first_diff = NULL;
 }
 
+///
+/// return true if the options are set to use diff linematch
+///
+bool diff_linematch(diff_T *dp)
+{
+  if (!(diff_flags & DIFF_LINEMATCH)) {
+    return false;
+  }
+  // are there more than three diff buffers?
+  int tsize = 0;
+  for (int i = 0; i < DB_COUNT; i++) {
+    if (curtab->tp_diffbuf[i] != NULL) {
+      // for the rare case (bug?) that the count of a diff block is negative, do
+      // not run the algorithm because this will try to allocate a negative
+      // amount of space and crash
+      if (dp->df_count[i] < 0) {
+        return false;
+      }
+      tsize += dp->df_count[i];
+    }
+  }
+  // avoid allocating a huge array because it will lag
+  return tsize <= linematch_lines;
+}
+
+static int get_max_diff_length(const diff_T *dp)
+{
+  int maxlength = 0;
+  for (int k = 0; k < DB_COUNT; k++) {
+    if (curtab->tp_diffbuf[k] != NULL) {
+      if (dp->df_count[k] > maxlength) {
+        maxlength = dp->df_count[k];
+      }
+    }
+  }
+  return maxlength;
+}
+
+static void find_top_diff_block(diff_T **thistopdiff, diff_T **nextblockblock, int fromidx,
+                                int topline)
+{
+  diff_T *topdiff = NULL;
+  diff_T *localtopdiff = NULL;
+  int topdiffchange = 0;
+
+  for (topdiff = curtab->tp_first_diff; topdiff != NULL; topdiff = topdiff->df_next) {
+    // set the top of the current overlapping diff block set as we
+    // iterate through all of the sets of overlapping diff blocks
+    if (!localtopdiff || topdiffchange) {
+      localtopdiff = topdiff;
+      topdiffchange = 0;
+    }
+
+    // check if the fromwin topline is matched by the current diff. if so, set it to the top of the diff block
+    if (topline >= topdiff->df_lnum[fromidx] && topline <=
+        (topdiff->df_lnum[fromidx] + topdiff->df_count[fromidx])) {
+      // this line is inside the current diff block, so we will save the
+      // top block of the set of blocks to refer to later
+      if ((*thistopdiff) == NULL) {
+        (*thistopdiff) = localtopdiff;
+      }
+    }
+
+    // check if the next set of overlapping diff blocks is next
+    if (!(topdiff->df_next && (topdiff->df_next->df_lnum[fromidx] ==
+                               (topdiff->df_lnum[fromidx] + topdiff->df_count[fromidx])))) {
+      // mark that the next diff block is belongs to a different set of
+      // overlapping diff blocks
+      topdiffchange = 1;
+
+      // if we already have found that the line number is inside a diff block,
+      // set the marker of the next block and finish the iteration
+      if (*thistopdiff) {
+        (*nextblockblock) = topdiff->df_next;
+        break;
+      }
+    }
+  }
+}
+
+static void count_filler_lines_and_topline(int *curlinenum_to, int *linesfiller,
+                                           const diff_T *thistopdiff, const int toidx,
+                                           int virtual_lines_passed)
+{
+  const diff_T *curdif = thistopdiff;
+  int ch_virtual_lines = 0;
+  int isfiller = 0;
+  while (virtual_lines_passed) {
+    if (ch_virtual_lines) {
+      virtual_lines_passed--;
+      ch_virtual_lines--;
+      if (!isfiller) {
+        (*curlinenum_to)++;
+      } else {
+        (*linesfiller)++;
+      }
+    } else {
+      (*linesfiller) = 0;
+      ch_virtual_lines = get_max_diff_length(curdif);
+      isfiller = (curdif->df_count[toidx] ? 0 : 1);
+      if (isfiller) {
+        while (curdif && curdif->df_next && curdif->df_lnum[toidx] ==
+               curdif->df_next->df_lnum[toidx]
+               && curdif->df_next->df_count[toidx] == 0) {
+          curdif = curdif->df_next;
+          ch_virtual_lines += get_max_diff_length(curdif);
+        }
+      }
+      if (curdif) {
+        curdif = curdif->df_next;
+      }
+    }
+  }
+}
+
+static void calculate_topfill_and_topline(const int fromidx, const int toidx, const
+                                          int from_topline, const int from_topfill, int *topfill,
+                                          linenr_T *topline)
+{
+  // 1. find the position from the top of the diff block, and the start
+  // of the next diff block
+  diff_T *thistopdiff = NULL;
+  diff_T *nextblockblock = NULL;
+  int virtual_lines_passed = 0;
+
+  find_top_diff_block(&thistopdiff, &nextblockblock, fromidx, from_topline);
+
+  // count the virtual lines that have been passed
+
+  diff_T *curdif = thistopdiff;
+  while (curdif && (curdif->df_lnum[fromidx] + curdif->df_count[fromidx])
+         <= from_topline) {
+    virtual_lines_passed += get_max_diff_length(curdif);
+
+    curdif = curdif->df_next;
+  }
+
+  if (curdif != nextblockblock) {
+    virtual_lines_passed += from_topline - curdif->df_lnum[fromidx];
+  }
+  virtual_lines_passed -= from_topfill;
+
+  // count the same amount of virtual lines in the toidx buffer
+  curdif = thistopdiff;
+  int curlinenum_to = thistopdiff->df_lnum[toidx];
+  int linesfiller = 0;
+  count_filler_lines_and_topline(&curlinenum_to, &linesfiller,
+                                 thistopdiff, toidx, virtual_lines_passed);
+
+  // count the number of filler lines that would normally be above this line
+  int maxfiller = 0;
+  for (diff_T *dpfillertest = thistopdiff; dpfillertest != NULL;
+       dpfillertest = dpfillertest->df_next) {
+    if (dpfillertest->df_lnum[toidx] == curlinenum_to) {
+      while (dpfillertest && dpfillertest->df_lnum[toidx] == curlinenum_to) {
+        maxfiller += dpfillertest->df_count[toidx] ? 0 : get_max_diff_length(dpfillertest);
+        dpfillertest = dpfillertest->df_next;
+      }
+      break;
+    }
+  }
+  (*topfill) = maxfiller - linesfiller;
+  (*topline) = curlinenum_to;
+}
+
+static int linematched_filler_lines(diff_T *dp, int idx, linenr_T lnum, int *linestatus)
+{
+  int filler_lines_d1 = 0;
+  while (dp && dp->df_next
+         && lnum == (dp->df_lnum[idx] + dp->df_count[idx])
+         && dp->df_next->df_lnum[idx] == lnum) {
+    if (dp->df_count[idx] == 0) {
+      filler_lines_d1 += get_max_diff_length(dp);
+    }
+    dp = dp->df_next;
+  }
+
+  if (dp->df_count[idx] == 0) {
+    filler_lines_d1 += get_max_diff_length(dp);
+  }
+
+  if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) {
+    int j = 0;
+    for (int i = 0; i < DB_COUNT; i++) {
+      if (curtab->tp_diffbuf[i] != NULL) {
+        if (dp->df_count[i]) {
+          j++;
+        }
+      }
+      // is this an added line or a changed line?
+      if (linestatus) {
+        (*linestatus) = (j == 1) ? -2 : -1;
+      }
+    }
+  }
+  return filler_lines_d1;
+}
+
+// Apply results from the linematch algorithm and apply to 'dp' by splitting it into multiple
+// adjacent diff blocks.
+static void apply_linematch_results(diff_T *dp, size_t decisions_length, const int *decisions)
+{
+  // get the start line number here in each diff buffer, and then increment
+  int line_numbers[DB_COUNT];
+  int outputmap[DB_COUNT];
+  size_t ndiffs = 0;
+  for (int i = 0; i < DB_COUNT; i++) {
+    if (curtab->tp_diffbuf[i] != NULL) {
+      line_numbers[i] = dp->df_lnum[i];
+      dp->df_count[i] = 0;
+
+      // Keep track of the index of the diff buffer we are using here.
+      // We will use this to write the output of the algorithm to
+      // diff_T structs at the correct indexes
+      outputmap[ndiffs] = i;
+      ndiffs++;
+    }
+  }
+
+  // write the diffs starting with the current diff block
+  diff_T *dp_s = dp;
+  for (size_t i = 0; i < decisions_length; i++) {
+    // Don't allocate on first iter since we can reuse the initial diffblock
+    if (i != 0 && (decisions[i - 1] != decisions[i])) {
+      // create new sub diff blocks to segment the original diff block which we
+      // further divided by running the linematch algorithm
+      dp_s = diff_alloc_new(curtab, dp_s, dp_s->df_next);
+      dp_s->is_linematched = true;
+      for (int j = 0; j < DB_COUNT; j++) {
+        if (curtab->tp_diffbuf[j] != NULL) {
+          dp_s->df_lnum[j] = line_numbers[j];
+          dp_s->df_count[j] = 0;
+        }
+      }
+    }
+    for (size_t j = 0; j < ndiffs; j++) {
+      if (decisions[i] & (1 << j)) {
+        // will need to use the map here
+        dp_s->df_count[outputmap[j]]++;
+        line_numbers[outputmap[j]]++;
+      }
+    }
+  }
+  dp->is_linematched = true;
+}
+
+static void run_linematch_algorithm(diff_T *dp)
+{
+  // define buffers for diff algorithm
+  mmfile_t diffbufs_mm[DB_COUNT];
+  const char *diffbufs[DB_COUNT];
+  int diff_length[DB_COUNT];
+  size_t ndiffs = 0;
+  for (int i = 0; i < DB_COUNT; i++) {
+    if (curtab->tp_diffbuf[i] != NULL) {
+      // write the contents of the entire buffer to
+      // diffbufs_mm[diffbuffers_count]
+      diff_write_buffer(curtab->tp_diffbuf[i], &diffbufs_mm[ndiffs],
+                        dp->df_lnum[i], dp->df_lnum[i] + dp->df_count[i] - 1);
+
+      // we want to get the char* to the diff buffer that was just written
+      // we add it to the array of char*, diffbufs
+      diffbufs[ndiffs] = diffbufs_mm[ndiffs].ptr;
+
+      // keep track of the length of this diff block to pass it to the linematch
+      // algorithm
+      diff_length[ndiffs] = dp->df_count[i];
+
+      // increment the amount of diff buffers we are passing to the algorithm
+      ndiffs++;
+    }
+  }
+
+  // we will get the output of the linematch algorithm in the format of an array
+  // of integers (*decisions) and the length of that array (decisions_length)
+  int *decisions = NULL;
+  const bool iwhite = (diff_flags & (DIFF_IWHITEALL | DIFF_IWHITE)) > 0;
+  size_t decisions_length = linematch_nbuffers(diffbufs, diff_length, ndiffs, &decisions, iwhite);
+
+  for (size_t i = 0; i < ndiffs; i++) {
+    XFREE_CLEAR(diffbufs_mm[i].ptr);
+  }
+
+  apply_linematch_results(dp, decisions_length, decisions);
+
+  xfree(decisions);
+}
+
 /// Check diff status for line "lnum" in buffer "buf":
 ///
 /// Returns 0 for nothing special
@@ -1795,9 +2091,10 @@ void diff_clear(tabpage_T *tp)
 ///
 /// @param wp
 /// @param lnum
+/// @param[out] linestatus
 ///
 /// @return diff status.
-int diff_check(win_T *wp, linenr_T lnum)
+int diff_check_with_linestatus(win_T *wp, linenr_T lnum, int *linestatus)
 {
   buf_T *buf = wp->w_buffer;
 
@@ -1840,6 +2137,14 @@ int diff_check(win_T *wp, linenr_T lnum)
     return 0;
   }
 
+  if (!dp->is_linematched && diff_linematch(dp)) {
+    run_linematch_algorithm(dp);
+  }
+
+  if (dp->is_linematched) {
+    return linematched_filler_lines(dp, idx, lnum, linestatus);
+  }
+
   if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) {
     int zero = false;
 
@@ -1894,15 +2199,16 @@ int diff_check(win_T *wp, linenr_T lnum)
 
   // Insert filler lines above the line just below the change.  Will return
   // 0 when this buf had the max count.
-  linenr_T maxcount = 0;
-  for (int i = 0; i < DB_COUNT; i++) {
-    if ((curtab->tp_diffbuf[i] != NULL) && (dp->df_count[i] > maxcount)) {
-      maxcount = dp->df_count[i];
-    }
-  }
+  int maxcount = get_max_diff_length(dp);
   return maxcount - dp->df_count[idx];
 }
 
+/// See diff_check_with_linestatus
+int diff_check(win_T *wp, linenr_T lnum)
+{
+  return diff_check_with_linestatus(wp, lnum, NULL);
+}
+
 /// Compare two entries in diff "dp" and return true if they are equal.
 ///
 /// @param  dp    diff
@@ -2062,46 +2368,51 @@ void diff_set_topline(win_T *fromwin, win_T *towin)
     towin->w_topline = lnum + (dp->df_lnum[toidx] - dp->df_lnum[fromidx]);
 
     if (lnum >= dp->df_lnum[fromidx]) {
-      // Inside a change: compute filler lines. With three or more
-      // buffers we need to know the largest count.
-      linenr_T max_count = 0;
-
-      for (int i = 0; i < DB_COUNT; i++) {
-        if ((curtab->tp_diffbuf[i] != NULL) && (max_count < dp->df_count[i])) {
-          max_count = dp->df_count[i];
-        }
-      }
+      if (diff_flags & DIFF_LINEMATCH) {
+        calculate_topfill_and_topline(fromidx, toidx, fromwin->w_topline,
+                                      fromwin->w_topfill, &towin->w_topfill, &towin->w_topline);
+      } else {
+        // Inside a change: compute filler lines. With three or more
+        // buffers we need to know the largest count.
+        linenr_T max_count = 0;
 
-      if (dp->df_count[toidx] == dp->df_count[fromidx]) {
-        // same number of lines: use same filler count
-        towin->w_topfill = fromwin->w_topfill;
-      } else if (dp->df_count[toidx] > dp->df_count[fromidx]) {
-        if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) {
-          // more lines in towin and fromwin doesn't show diff
-          // lines, only filler lines
-          if (max_count - fromwin->w_topfill >= dp->df_count[toidx]) {
-            // towin also only shows filler lines
-            towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx];
-            towin->w_topfill = fromwin->w_topfill;
-          } else {
-            // towin still has some diff lines to show
-            towin->w_topline = dp->df_lnum[toidx]
-                               + max_count - fromwin->w_topfill;
+        for (int i = 0; i < DB_COUNT; i++) {
+          if ((curtab->tp_diffbuf[i] != NULL) && (max_count < dp->df_count[i])) {
+            max_count = dp->df_count[i];
           }
         }
-      } else if (towin->w_topline >= dp->df_lnum[toidx]
-                 + dp->df_count[toidx]) {
-        // less lines in towin and no diff lines to show: compute
-        // filler lines
-        towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx];
 
-        if (diff_flags & DIFF_FILLER) {
+        if (dp->df_count[toidx] == dp->df_count[fromidx]) {
+          // same number of lines: use same filler count
+          towin->w_topfill = fromwin->w_topfill;
+        } else if (dp->df_count[toidx] > dp->df_count[fromidx]) {
           if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) {
-            // fromwin is also out of diff lines
-            towin->w_topfill = fromwin->w_topfill;
-          } else {
-            // fromwin has some diff lines
-            towin->w_topfill = dp->df_lnum[fromidx] + max_count - lnum;
+            // more lines in towin and fromwin doesn't show diff
+            // lines, only filler lines
+            if (max_count - fromwin->w_topfill >= dp->df_count[toidx]) {
+              // towin also only shows filler lines
+              towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx];
+              towin->w_topfill = fromwin->w_topfill;
+            } else {
+              // towin still has some diff lines to show
+              towin->w_topline = dp->df_lnum[toidx]
+                                 + max_count - fromwin->w_topfill;
+            }
+          }
+        } else if (towin->w_topline >= dp->df_lnum[toidx]
+                   + dp->df_count[toidx]) {
+          // less lines in towin and no diff lines to show: compute
+          // filler lines
+          towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx];
+
+          if (diff_flags & DIFF_FILLER) {
+            if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) {
+              // fromwin is also out of diff lines
+              towin->w_topfill = fromwin->w_topfill;
+            } else {
+              // fromwin has some diff lines
+              towin->w_topfill = dp->df_lnum[fromidx] + max_count - lnum;
+            }
           }
         }
       }
@@ -2136,6 +2447,7 @@ void diff_set_topline(win_T *fromwin, win_T *towin)
 int diffopt_changed(void)
 {
   int diff_context_new = 6;
+  int linematch_lines_new = 0;
   int diff_flags_new = 0;
   int diff_foldcolumn_new = 2;
   long diff_algorithm_new = 0;
@@ -2205,6 +2517,10 @@ int diffopt_changed(void)
       } else {
         return FAIL;
       }
+    } else if ((STRNCMP(p, "linematch:", 10) == 0) && ascii_isdigit(p[10])) {
+      p += 10;
+      linematch_lines_new = getdigits_int(&p, false, linematch_lines_new);
+      diff_flags_new |= DIFF_LINEMATCH;
     }
 
     if ((*p != ',') && (*p != NUL)) {
@@ -2233,6 +2549,7 @@ int diffopt_changed(void)
 
   diff_flags = diff_flags_new;
   diff_context = diff_context_new == 0 ? 1 : diff_context_new;
+  linematch_lines = linematch_lines_new;
   diff_foldcolumn = diff_foldcolumn_new;
   diff_algorithm = diff_algorithm_new;
 
@@ -2300,6 +2617,13 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
       break;
     }
   }
+  if (dp->is_linematched) {
+    while (dp && dp->df_next
+           && lnum == dp->df_count[idx] + dp->df_lnum[idx]
+           && dp->df_next->df_lnum[idx] == lnum) {
+      dp = dp->df_next;
+    }
+  }
 
   if ((dp == NULL) || (diff_check_sanity(curtab, dp) == FAIL)) {
     xfree(line_org);
@@ -2674,6 +2998,17 @@ static void diffgetput(const int addr_count, const int idx_cur, const int idx_fr
   diff_T *dprev = NULL;
 
   for (diff_T *dp = curtab->tp_first_diff; dp != NULL;) {
+    if (!addr_count) {
+      // handle the case with adjacent diff blocks
+      while (dp->is_linematched
+             && dp->df_next
+             && dp->df_next->df_lnum[idx_cur] == dp->df_lnum[idx_cur] + dp->df_count[idx_cur]
+             && dp->df_next->df_lnum[idx_cur] == line1 + off + 1) {
+        dprev = dp;
+        dp = dp->df_next;
+      }
+    }
+
     if (dp->df_lnum[idx_cur] > line2 + off) {
       // past the range that was specified
       break;
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 08b6fd89af..cb7d85a467 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -804,9 +804,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
 
   int bg_attr = win_bg_attr(wp);
 
-  filler_lines = diff_check(wp, lnum);
-  if (filler_lines < 0) {
-    if (filler_lines == -1) {
+  int linestatus = 0;
+  filler_lines = diff_check_with_linestatus(wp, lnum, &linestatus);
+  if (filler_lines < 0 || linestatus < 0) {
+    if (filler_lines == -1 || linestatus == -1) {
       if (diff_find_change(wp, lnum, &change_start, &change_end)) {
         diff_hlf = HLF_ADD;             // added line
       } else if (change_start == 0) {
@@ -817,7 +818,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
     } else {
       diff_hlf = HLF_ADD;               // added line
     }
-    filler_lines = 0;
+    if (linestatus == 0) {
+      filler_lines = 0;
+    }
     area_highlighting = true;
   }
   VirtLines virt_lines = KV_INITIAL_VALUE;
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index e676e2e656..0e3de29cce 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -1573,9 +1573,10 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
       || changedtick != buf_get_changedtick(curbuf)
       || fnum != curbuf->b_fnum) {
     // New line, buffer, change: need to get the values.
-    int filler_lines = diff_check(curwin, lnum);
-    if (filler_lines < 0) {
-      if (filler_lines == -1) {
+    int linestatus = 0;
+    int filler_lines = diff_check_with_linestatus(curwin, lnum, &linestatus);
+    if (filler_lines < 0 || linestatus < 0) {
+      if (filler_lines == -1 || linestatus == -1) {
         change_start = MAXCOL;
         change_end = -1;
         if (diff_find_change(curwin, lnum, &change_start, &change_end)) {
diff --git a/src/nvim/linematch.c b/src/nvim/linematch.c
new file mode 100644
index 0000000000..ef63bd2321
--- /dev/null
+++ b/src/nvim/linematch.c
@@ -0,0 +1,377 @@
+#include "nvim/linematch.h"
+#include "nvim/memory.h"
+#include "nvim/vim.h"
+
+// struct for running the diff linematch algorithm
+typedef struct {
+  int *df_decision;  // to keep track of this path traveled
+  int df_lev_score;  // to keep track of the total score of this path
+  size_t df_path_idx;   // current index of this path
+} diffcmppath_T;
+
+#define LN_MAX_BUFS 8
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "linematch.c.generated.h"
+#endif
+
+static size_t line_len(const char *s)
+{
+  char *end = strchr(s, '\n');
+  if (end) {
+    return (size_t)(end - s);
+  }
+  return STRLEN(s);
+}
+
+/// Same as matching_chars but ignore whitespace
+///
+/// @param s1
+/// @param s2
+static int matching_chars_iwhite(const char *s1, const char *s2)
+{
+  // the newly processed strings that will be compared
+  // delete the white space characters, and/or replace all upper case with lower
+  char *strsproc[2];
+  const char *strsorig[2] = { s1, s2 };
+  for (int k = 0; k < 2; k++) {
+    size_t d = 0;
+    size_t i = 0;
+    size_t slen = line_len(strsorig[k]);
+    strsproc[k] = xmalloc((slen + 1) * sizeof(char));
+    while (d + i < slen) {
+      char e = strsorig[k][i + d];
+      if (e != ' ' && e != '\t') {
+        strsproc[k][i] = e;
+        i++;
+      } else {
+        d++;
+      }
+    }
+    strsproc[k][i] = '\0';
+  }
+  int matching = matching_chars(strsproc[0], strsproc[1]);
+  xfree(strsproc[0]);
+  xfree(strsproc[1]);
+  return matching;
+}
+
+/// update the path of a point in the diff linematch algorithm
+/// @param diffcmppath
+/// @param score
+/// @param to
+/// @param from
+/// @param choice
+static void update_path_flat(diffcmppath_T *diffcmppath, int score, size_t to, size_t from,
+                             const int choice)
+{
+  size_t path_idx = diffcmppath[from].df_path_idx;
+
+  for (size_t k = 0; k < path_idx; k++) {
+    diffcmppath[to].df_decision[k] = diffcmppath[from].df_decision[k];
+  }
+
+  diffcmppath[to].df_decision[path_idx] = choice;
+  diffcmppath[to].df_lev_score = score;
+  diffcmppath[to].df_path_idx = path_idx + 1;
+}
+
+#define MATCH_CHAR_MAX_LEN 500
+
+/// Return matching characters between "s1" and "s2" whilst respecting sequence order.
+/// Consider the case of two strings 'AAACCC' and 'CCCAAA', the
+/// return value from this function will be 3, either to match
+/// the 3 C's, or the 3 A's.
+///
+/// Examples:
+///   matching_chars("aabc", "acba")               -> 2  // 'a' and 'b' in common
+///   matching_chars("123hello567", "he123ll567o") -> 8  // '123', 'll' and '567' in common
+///   matching_chars("abcdefg", "gfedcba")         -> 1  // all characters in common,
+///                                                      // but only at most 1 in sequence
+///
+/// @param s1
+/// @param s2
+static int matching_chars(const char *s1, const char *s2)
+{
+  size_t s1len = MIN(MATCH_CHAR_MAX_LEN, line_len(s1));
+  size_t s2len = MIN(MATCH_CHAR_MAX_LEN, line_len(s2));
+  int matrix[2][MATCH_CHAR_MAX_LEN] = { 0 };
+  bool icur = 1;  // save space by storing only two rows for i axis
+  for (size_t i = 0; i < s1len; i++) {
+    icur = !icur;
+    int *e1 = matrix[icur];
+    int *e2 = matrix[!icur];
+    for (size_t j = 0; j < s2len; j++) {
+      // skip char in s1
+      if (e2[j + 1] > e1[j + 1]) {
+        e1[j + 1] = e2[j + 1];
+      }
+      // skip char in s2
+      if (e1[j] > e1[j + 1]) {
+        e1[j + 1] = e1[j];
+      }
+      // compare char in s1 and s2
+      if ((s1[i] == s2[j]) && (e2[j] + 1) > e1[j + 1]) {
+        e1[j + 1] = e2[j] + 1;
+      }
+    }
+  }
+  return matrix[icur][s2len];
+}
+
+/// count the matching characters between a variable number of strings "sp"
+/// mark the strings that have already been compared to extract them later
+/// without re-running the character match counting.
+/// @param sp
+/// @param fomvals
+/// @param n
+static int count_n_matched_chars(const char **sp, const size_t n, bool iwhite)
+{
+  int matched_chars = 0;
+  int matched = 0;
+  for (size_t i = 0; i < n; i++) {
+    for (size_t j = i + 1; j < n; j++) {
+      if (sp[i] != NULL && sp[j] != NULL) {
+        matched++;
+        // TODO(lewis6991): handle whitespace ignoring higher up in the stack
+        matched_chars += iwhite ? matching_chars_iwhite(sp[i], sp[j])
+                                : matching_chars(sp[i], sp[j]);
+      }
+    }
+  }
+
+  // prioritize a match of 3 (or more lines) equally to a match of 2 lines
+  if (matched >= 2) {
+    matched_chars *= 2;
+    matched_chars /= matched;
+  }
+
+  return matched_chars;
+}
+
+void fastforward_buf_to_lnum(const char **s, size_t lnum)
+{
+  assert(lnum > 0);
+  for (size_t j = 0; j < lnum - 1; j++) {
+    *s = strchr(*s, '\n');
+    (*s)++;
+  }
+}
+
+/// try all the different ways to compare these lines and use the one that
+/// results in the most matching characters
+/// @param df_iters
+/// @param paths
+/// @param npaths
+/// @param path_idx
+/// @param choice
+/// @param diffcmppath
+/// @param diff_len
+/// @param ndiffs
+/// @param diff_blk
+static void try_possible_paths(const int *df_iters, const size_t *paths, const int npaths,
+                               const int path_idx, int *choice, diffcmppath_T *diffcmppath,
+                               const int *diff_len, const size_t ndiffs, const char **diff_blk,
+                               bool iwhite)
+{
+  if (path_idx == npaths) {
+    if ((*choice) > 0) {
+      int from_vals[LN_MAX_BUFS];
+      const int *to_vals = df_iters;
+      const char *current_lines[LN_MAX_BUFS];
+      for (size_t k = 0; k < ndiffs; k++) {
+        from_vals[k] = df_iters[k];
+        // get the index at all of the places
+        if ((*choice) & (1 << k)) {
+          from_vals[k]--;
+          const char *p = diff_blk[k];
+          fastforward_buf_to_lnum(&p, (size_t)df_iters[k]);
+          current_lines[k] = p;
+        } else {
+          current_lines[k] = NULL;
+        }
+      }
+      size_t unwrapped_idx_from = unwrap_indexes(from_vals, diff_len, ndiffs);
+      size_t unwrapped_idx_to = unwrap_indexes(to_vals, diff_len, ndiffs);
+      int matched_chars = count_n_matched_chars(current_lines, ndiffs, iwhite);
+      int score = diffcmppath[unwrapped_idx_from].df_lev_score + matched_chars;
+      if (score > diffcmppath[unwrapped_idx_to].df_lev_score) {
+        update_path_flat(diffcmppath, score, unwrapped_idx_to, unwrapped_idx_from, *choice);
+      }
+    } else {
+      // initialize the 0, 0, 0 ... choice
+      size_t i = 0;
+      while (i < ndiffs && df_iters[i] == 0) {
+        i++;
+        if (i == ndiffs) {
+          diffcmppath[0].df_lev_score = 0;
+          diffcmppath[0].df_path_idx = 0;
+        }
+      }
+    }
+    return;
+  }
+  size_t bit_place = paths[path_idx];
+  *(choice) |= (1 << bit_place);  // set it to 1
+  try_possible_paths(df_iters, paths, npaths, path_idx + 1, choice,
+                     diffcmppath, diff_len, ndiffs, diff_blk, iwhite);
+  *(choice) &= ~(1 << bit_place);  // set it to 0
+  try_possible_paths(df_iters, paths, npaths, path_idx + 1, choice,
+                     diffcmppath, diff_len, ndiffs, diff_blk, iwhite);
+}
+
+/// unwrap indexes to access n dimensional tensor
+/// @param values
+/// @param diff_len
+/// @param ndiffs
+static size_t unwrap_indexes(const int *values, const int *diff_len, const size_t ndiffs)
+{
+  size_t num_unwrap_scalar = 1;
+  for (size_t k = 0; k < ndiffs; k++) {
+    num_unwrap_scalar *= (size_t)diff_len[k] + 1;
+  }
+
+  size_t path_idx = 0;
+  for (size_t k = 0; k < ndiffs; k++) {
+    num_unwrap_scalar /= (size_t)diff_len[k] + 1;
+
+    // (k == 0) space optimization
+    int n = k == 0 ? values[k] % 2 : values[k];
+    path_idx += num_unwrap_scalar * (size_t)n;
+  }
+  return path_idx;
+}
+
+/// populate the values of the linematch algorithm tensor, and find the best
+/// decision for how to compare the relevant lines from each of the buffers at
+/// each point in the tensor
+/// @param df_iters
+/// @param ch_dim
+/// @param diffcmppath
+/// @param diff_len
+/// @param ndiffs
+/// @param diff_blk
+static void populate_tensor(int *df_iters, const size_t ch_dim, diffcmppath_T *diffcmppath,
+                            const int *diff_len, const size_t ndiffs, const char **diff_blk,
+                            bool iwhite)
+{
+  if (ch_dim == ndiffs) {
+    int npaths = 0;
+    size_t paths[LN_MAX_BUFS];
+
+    for (size_t j = 0; j < ndiffs; j++) {
+      if (df_iters[j] > 0) {
+        paths[npaths] = j;
+        npaths++;
+      }
+    }
+    int choice = 0;
+    size_t unwrapper_idx_to = unwrap_indexes(df_iters, diff_len, ndiffs);
+    diffcmppath[unwrapper_idx_to].df_lev_score = -1;
+    try_possible_paths(df_iters, paths, npaths, 0, &choice, diffcmppath,
+                       diff_len, ndiffs, diff_blk, iwhite);
+    return;
+  }
+
+  for (int i = 0; i <= diff_len[ch_dim]; i++) {
+    df_iters[ch_dim] = i;
+    populate_tensor(df_iters, ch_dim + 1, diffcmppath, diff_len,
+                    ndiffs, diff_blk, iwhite);
+  }
+}
+
+/// algorithm to find an optimal alignment of lines of a diff block with 2 or
+/// more files. The algorithm is generalized to work for any number of files
+/// which corresponds to another dimmension added to the tensor used in the
+/// algorithm
+///
+/// for questions and information about the linematch algorithm please contact
+/// Jonathon White (jonathonwhite@protonmail.com)
+///
+/// for explanation, a summary of the algorithm in 3 dimmensions (3 files
+///     compared) follows
+///
+/// The 3d case (for 3 buffers) of the algorithm implemented when diffopt
+/// 'linematch' is enabled. The algorithm constructs a 3d tensor to
+/// compare a diff between 3 buffers. The dimmensions of the tensor are
+/// the length of the diff in each buffer plus 1 A path is constructed by
+/// moving from one edge of the cube/3d tensor to the opposite edge.
+/// Motions from one cell of the cube to the next represent decisions. In
+/// a 3d cube, there are a total of 7 decisions that can be made,
+/// represented by the enum df_path3_choice which is defined in
+/// buffer_defs.h a comparison of buffer 0 and 1 represents a motion
+/// toward the opposite edge of the cube with components along the 0 and
+/// 1 axes.  a comparison of buffer 0, 1, and 2 represents a motion
+/// toward the opposite edge of the cube with components along the 0, 1,
+/// and 2 axes. A skip of buffer 0 represents a motion along only the 0
+/// axis. For each action, a point value is awarded, and the path is
+/// saved for reference later, if it is found to have been the optimal
+/// path. The optimal path has the highest score.  The score is
+/// calculated as the summation of the total characters matching between
+/// all of the lines which were compared. The structure of the algorithm
+/// is that of a dynamic programming problem.  We can calculate a point
+/// i,j,k in the cube as a function of i-1, j-1, and k-1. To find the
+/// score and path at point i,j,k, we must determine which path we want
+/// to use, this is done by looking at the possibilities and choosing
+/// the one which results in the local highest score.  The total highest
+/// scored path is, then in the end represented by the cell in the
+/// opposite corner from the start location.  The entire algorithm
+/// consits of populating the 3d cube with the optimal paths from which
+/// it may have came.
+///
+/// Optimizations:
+/// As the function to calculate the cell of a tensor at point i,j,k is a
+/// function of the cells at i-1, j-1, k-1, the whole tensor doesn't need
+/// to be stored in memory at once. In the case of the 3d cube, only two
+/// slices (along k and j axis) are stored in memory. For the 2d matrix
+/// (for 2 files), only two rows are stored at a time. The next/previous
+/// slice (or row) is always calculated from the other, and they alternate
+/// at each iteration.
+/// In the 3d case, 3 arrays are populated to memorize the score (matched
+/// characters) of the 3 buffers, so a redundant calculation of the
+/// scores does not occur
+/// @param diff_blk
+/// @param diff_len
+/// @param ndiffs
+/// @param [out] [allocated] decisions
+/// @return the length of decisions
+size_t linematch_nbuffers(const char **diff_blk, const int *diff_len, const size_t ndiffs,
+                          int **decisions, bool iwhite)
+{
+  assert(ndiffs <= LN_MAX_BUFS);
+
+  size_t memsize = 1;
+  size_t memsize_decisions = 0;
+  for (size_t i = 0; i < ndiffs; i++) {
+    assert(diff_len[i] >= 0);
+    memsize *= i == 0 ? 2 : (size_t)(diff_len[i] + 1);
+    memsize_decisions += (size_t)diff_len[i];
+  }
+
+  // create the flattened path matrix
+  diffcmppath_T *diffcmppath = xmalloc(sizeof(diffcmppath_T) * memsize);
+  // allocate memory here
+  for (size_t i = 0; i < memsize; i++) {
+    diffcmppath[i].df_decision = xmalloc(memsize_decisions * sizeof(int));
+  }
+
+  // memory for avoiding repetitive calculations of score
+  int df_iters[LN_MAX_BUFS];
+  populate_tensor(df_iters, 0, diffcmppath, diff_len, ndiffs, diff_blk, iwhite);
+
+  const size_t u = unwrap_indexes(diff_len, diff_len, ndiffs);
+  const size_t best_path_idx = diffcmppath[u].df_path_idx;
+  const int *best_path_decisions = diffcmppath[u].df_decision;
+
+  *decisions = xmalloc(sizeof(int) * best_path_idx);
+  for (size_t i = 0; i < best_path_idx; i++) {
+    (*decisions)[i] = best_path_decisions[i];
+  }
+
+  for (size_t i = 0; i < memsize; i++) {
+    xfree(diffcmppath[i].df_decision);
+  }
+  xfree(diffcmppath);
+
+  return best_path_idx;
+}
diff --git a/src/nvim/linematch.h b/src/nvim/linematch.h
new file mode 100644
index 0000000000..052d438617
--- /dev/null
+++ b/src/nvim/linematch.h
@@ -0,0 +1,10 @@
+#ifndef NVIM_LINEMATCH_H
+#define NVIM_LINEMATCH_H
+
+#include 
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "linematch.h.generated.h"
+#endif
+
+#endif  // NVIM_LINEMATCH_H
diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c
index b2b5dfedee..428a33cdad 100644
--- a/src/nvim/lua/xdiff.c
+++ b/src/nvim/lua/xdiff.c
@@ -10,12 +10,16 @@
 #include 
 
 #include "nvim/api/private/helpers.h"
+#include "nvim/linematch.h"
 #include "nvim/lua/converter.h"
 #include "nvim/lua/executor.h"
 #include "nvim/lua/xdiff.h"
 #include "nvim/vim.h"
 #include "xdiff/xdiff.h"
 
+#define COMPARED_BUFFER0 (1 << 0)
+#define COMPARED_BUFFER1 (1 << 1)
+
 typedef enum {
   kNluaXdiffModeUnified = 0,
   kNluaXdiffModeOnHunkCB,
@@ -25,12 +29,72 @@ typedef enum {
 typedef struct {
   lua_State *lstate;
   Error *err;
+  mmfile_t *ma;
+  mmfile_t *mb;
+  bool linematch;
+  bool iwhite;
 } hunkpriv_t;
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "lua/xdiff.c.generated.h"
 #endif
 
+static void lua_pushhunk(lua_State *lstate, long start_a, long count_a, long start_b, long count_b)
+{
+  lua_createtable(lstate, 0, 0);
+  lua_pushinteger(lstate, start_a);
+  lua_rawseti(lstate, -2, 1);
+  lua_pushinteger(lstate, count_a);
+  lua_rawseti(lstate, -2, 2);
+  lua_pushinteger(lstate, start_b);
+  lua_rawseti(lstate, -2, 3);
+  lua_pushinteger(lstate, count_b);
+  lua_rawseti(lstate, -2, 4);
+  lua_rawseti(lstate, -2, (signed)lua_objlen(lstate, -2) + 1);
+}
+
+static void get_linematch_results(lua_State *lstate, mmfile_t *ma, mmfile_t *mb, long start_a,
+                                  long count_a, long start_b, long count_b, bool iwhite)
+{
+  // get the pointer to char of the start of the diff to pass it to linematch algorithm
+  const char *diff_begin[2] = { ma->ptr, mb->ptr };
+  int diff_length[2] = { (int)count_a, (int)count_b };
+
+  fastforward_buf_to_lnum(&diff_begin[0], (size_t)start_a);
+  fastforward_buf_to_lnum(&diff_begin[1], (size_t)start_b);
+
+  int *decisions = NULL;
+  size_t decisions_length = linematch_nbuffers(diff_begin, diff_length, 2, &decisions, iwhite);
+
+  long lnuma = start_a, lnumb = start_b;
+
+  long hunkstarta = lnuma;
+  long hunkstartb = lnumb;
+  long hunkcounta = 0;
+  long hunkcountb = 0;
+  for (size_t i = 0; i < decisions_length; i++) {
+    if (i && (decisions[i - 1] != decisions[i])) {
+      lua_pushhunk(lstate, hunkstarta, hunkcounta, hunkstartb, hunkcountb);
+
+      hunkstarta = lnuma;
+      hunkstartb = lnumb;
+      hunkcounta = 0;
+      hunkcountb = 0;
+      // create a new hunk
+    }
+    if (decisions[i] & COMPARED_BUFFER0) {
+      lnuma++;
+      hunkcounta++;
+    }
+    if (decisions[i] & COMPARED_BUFFER1) {
+      lnumb++;
+      hunkcountb++;
+    }
+  }
+  lua_pushhunk(lstate, hunkstarta, hunkcounta, hunkstartb, hunkcountb);
+  xfree(decisions);
+}
+
 static int write_string(void *priv, mmbuffer_t *mb, int nbuf)
 {
   luaL_Buffer *buf = (luaL_Buffer *)priv;
@@ -61,20 +125,14 @@ static int hunk_locations_cb(long start_a, long count_a, long start_b, long coun
   if (count_b > 0) {
     start_b += 1;
   }
-
-  lua_State *lstate = (lua_State *)cb_data;
-  lua_createtable(lstate, 0, 0);
-
-  lua_pushinteger(lstate, start_a);
-  lua_rawseti(lstate, -2, 1);
-  lua_pushinteger(lstate, count_a);
-  lua_rawseti(lstate, -2, 2);
-  lua_pushinteger(lstate, start_b);
-  lua_rawseti(lstate, -2, 3);
-  lua_pushinteger(lstate, count_b);
-  lua_rawseti(lstate, -2, 4);
-
-  lua_rawseti(lstate, -2, (signed)lua_objlen(lstate, -2) + 1);
+  hunkpriv_t *priv = (hunkpriv_t *)cb_data;
+  lua_State *lstate = priv->lstate;
+  if (priv->linematch) {
+    get_linematch_results(lstate, priv->ma, priv->mb, start_a, count_a, start_b, count_b,
+                          priv->iwhite);
+  } else {
+    lua_pushhunk(lstate, start_a, count_a, start_b, count_b);
+  }
 
   return 0;
 }
@@ -149,7 +207,7 @@ static bool check_xdiff_opt(ObjectType actType, ObjectType expType, const char *
 }
 
 static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg, xpparam_t *params,
-                                           Error *err)
+                                           bool *linematch, Error *err)
 {
   const DictionaryOf(LuaRef) opts = nlua_pop_Dictionary(lstate, true, err);
 
@@ -205,6 +263,11 @@ static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg,
         goto exit_1;
       }
       cfg->interhunkctxlen = v->data.integer;
+    } else if (strequal("linematch", k.data)) {
+      *linematch = api_object_to_bool(*v, "linematch", false, err);
+      if (ERROR_SET(err)) {
+        goto exit_1;
+      }
     } else {
       struct {
         const char *name;
@@ -244,10 +307,8 @@ static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg,
 
   if (had_on_hunk) {
     mode = kNluaXdiffModeOnHunkCB;
-    cfg->hunk_func = call_on_hunk_cb;
   } else if (had_result_type_indices) {
     mode = kNluaXdiffModeLocations;
-    cfg->hunk_func = hunk_locations_cb;
   }
 
 exit_1:
@@ -268,6 +329,7 @@ int nlua_xdl_diff(lua_State *lstate)
   xdemitconf_t cfg;
   xpparam_t params;
   xdemitcb_t ecb;
+  bool linematch = false;
 
   CLEAR_FIELD(cfg);
   CLEAR_FIELD(params);
@@ -280,7 +342,7 @@ int nlua_xdl_diff(lua_State *lstate)
       return luaL_argerror(lstate, 3, "expected table");
     }
 
-    mode = process_xdl_diff_opts(lstate, &cfg, ¶ms, &err);
+    mode = process_xdl_diff_opts(lstate, &cfg, ¶ms, &linematch, &err);
 
     if (ERROR_SET(&err)) {
       goto exit_0;
@@ -288,7 +350,7 @@ int nlua_xdl_diff(lua_State *lstate)
   }
 
   luaL_Buffer buf;
-  hunkpriv_t *priv = NULL;
+  hunkpriv_t priv;
   switch (mode) {
   case kNluaXdiffModeUnified:
     luaL_buffinit(lstate, &buf);
@@ -296,14 +358,24 @@ int nlua_xdl_diff(lua_State *lstate)
     ecb.out_line = write_string;
     break;
   case kNluaXdiffModeOnHunkCB:
-    priv = xmalloc(sizeof(*priv));
-    priv->lstate = lstate;
-    priv->err = &err;
-    ecb.priv = priv;
+    cfg.hunk_func = call_on_hunk_cb;
+    priv = (hunkpriv_t) {
+      .lstate = lstate,
+      .err    = &err,
+    };
+    ecb.priv = &priv;
     break;
   case kNluaXdiffModeLocations:
+    cfg.hunk_func = hunk_locations_cb;
+    priv = (hunkpriv_t) {
+      .lstate    = lstate,
+      .ma        = &ma,
+      .mb        = &mb,
+      .linematch = linematch,
+      .iwhite    = (params.flags & XDF_IGNORE_WHITESPACE) > 0
+    };
+    ecb.priv = &priv;
     lua_createtable(lstate, 0, 0);
-    ecb.priv = lstate;
     break;
   }
 
@@ -314,8 +386,6 @@ int nlua_xdl_diff(lua_State *lstate)
     }
   }
 
-  XFREE_CLEAR(priv);
-
 exit_0:
   if (ERROR_SET(&err)) {
     luaL_where(lstate, 1);
-- 
cgit 


From 24fa5f70edd4cc3b613237283ee7d63af1948c16 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 4 Nov 2022 18:17:26 +0800
Subject: vim-patch:8.2.0448: various functions not properly tested (#20926)

Problem:    Various functions not properly tested.
Solution:   Add more tests, especially for failures. (Yegappan Lakshmanan,
            closes vim/vim#5843)

https://github.com/vim/vim/commit/0e05de46226eb4e5ea580beefa71831f92d613d3

Cherry-pick test changes from patch 8.2.0427 and skip Test_has().
Cherry-pick Test_complete_wildmenu() change from patch 8.2.4339.
---
 src/nvim/testdir/check.vim            |  3 ++
 src/nvim/testdir/test_blob.vim        |  2 +
 src/nvim/testdir/test_breakindent.vim |  1 +
 src/nvim/testdir/test_cmdline.vim     |  7 ++++
 src/nvim/testdir/test_exists.vim      |  6 +++
 src/nvim/testdir/test_expand_func.vim |  1 +
 src/nvim/testdir/test_expr.vim        |  5 +++
 src/nvim/testdir/test_functions.vim   | 76 ++++++++++++++++++++++++++++++++++-
 src/nvim/testdir/test_listdict.vim    |  8 ++++
 src/nvim/testdir/test_marks.vim       |  2 +
 src/nvim/testdir/test_partial.vim     |  5 +++
 src/nvim/testdir/test_registers.vim   | 14 +++++++
 src/nvim/testdir/test_spell.vim       |  2 +
 src/nvim/testdir/test_substitute.vim  |  5 +++
 src/nvim/testdir/test_syntax.vim      |  2 +
 src/nvim/testdir/test_taglist.vim     |  2 +
 src/nvim/testdir/test_utf8.vim        |  2 +
 src/nvim/testdir/test_vartabs.vim     |  2 +
 src/nvim/testdir/test_window_cmd.vim  |  1 +
 19 files changed, 145 insertions(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim
index 92a51d4371..1459b70243 100644
--- a/src/nvim/testdir/check.vim
+++ b/src/nvim/testdir/check.vim
@@ -4,6 +4,9 @@ source term_util.vim
 " Command to check for the presence of a feature.
 command -nargs=1 CheckFeature call CheckFeature()
 func CheckFeature(name)
+  " if !has(a:name, 1)
+  "   throw 'Checking for non-existent feature ' .. a:name
+  " endif
   if !has(a:name)
     throw 'Skipped: ' .. a:name .. ' feature missing'
   endif
diff --git a/src/nvim/testdir/test_blob.vim b/src/nvim/testdir/test_blob.vim
index 770b2d16ef..151de71312 100644
--- a/src/nvim/testdir/test_blob.vim
+++ b/src/nvim/testdir/test_blob.vim
@@ -300,6 +300,8 @@ func Test_blob_index()
   call assert_equal(3, 0z11110111->index(0x11, 2))
   call assert_equal(2, index(0z11111111, 0x11, -2))
   call assert_equal(3, index(0z11110111, 0x11, -2))
+  call assert_equal(0, index(0z11110111, 0x11, -10))
+  call assert_fails("echo index(0z11110111, 0x11, [])", 'E745:')
 
   call assert_fails('call index("asdf", 0)', 'E897:')
 endfunc
diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim
index ed4d886fd1..69c98f1f05 100644
--- a/src/nvim/testdir/test_breakindent.vim
+++ b/src/nvim/testdir/test_breakindent.vim
@@ -421,6 +421,7 @@ func Test_breakindent11()
   let width = strlen(text[1:]) + indent(2) + strlen(&sbr) * 3 " text wraps 3 times
   call assert_equal(width, strdisplaywidth(text))
   call s:close_windows('set sbr=')
+  call assert_equal(4, strdisplaywidth("\t", 4))
 endfunc
 
 func Test_breakindent11_vartabs()
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 00bfadec93..27ac91e49f 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -89,6 +89,13 @@ func Test_complete_wildmenu()
   call assert_equal('"e Xtestfile3 Xtestfile4', @:)
   cd -
 
+  cnoremap   wildmenumode()
+  call feedkeys(":cd Xdir\\\\"\", 'tx')
+  call assert_equal('"cd Xdir1/0', @:)
+  call feedkeys(":e Xdir1/\\\\"\", 'tx')
+  call assert_equal('"e Xdir1/Xdir2/1', @:)
+  cunmap 
+
   " cleanup
   %bwipe
   call delete('Xdir1/Xdir2/Xtestfile4')
diff --git a/src/nvim/testdir/test_exists.vim b/src/nvim/testdir/test_exists.vim
index 471c77853d..62c66192ef 100644
--- a/src/nvim/testdir/test_exists.vim
+++ b/src/nvim/testdir/test_exists.vim
@@ -68,6 +68,10 @@ func Test_exists()
   " Existing environment variable
   let $EDITOR_NAME = 'Vim Editor'
   call assert_equal(1, exists('$EDITOR_NAME'))
+  if has('unix')
+    " ${name} environment variables are supported only on Unix-like systems
+    call assert_equal(1, exists('${VIM}'))
+  endif
   " Non-existing environment variable
   call assert_equal(0, exists('$NON_ENV_VAR'))
 
@@ -323,3 +327,5 @@ endfunc
 func Test_exists_funcarg()
   call FuncArg_Tests("arg1", "arg2")
 endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_expand_func.vim b/src/nvim/testdir/test_expand_func.vim
index 80bfdb8553..454d76f0aa 100644
--- a/src/nvim/testdir/test_expand_func.vim
+++ b/src/nvim/testdir/test_expand_func.vim
@@ -139,6 +139,7 @@ func Test_expand_wildignore()
   call assert_equal('test_expand_func.vim', expand('test_expand_func.vim', 1))
   call assert_equal(['test_expand_func.vim'],
         \ expand('test_expand_func.vim', 1, 1))
+  call assert_fails("call expand('*', [])", 'E745:')
   set wildignore&
 endfunc
 
diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim
index 66660ab75e..00ea4275ab 100644
--- a/src/nvim/testdir/test_expr.vim
+++ b/src/nvim/testdir/test_expr.vim
@@ -65,6 +65,8 @@ func Test_strgetchar()
   call assert_equal(-1, strgetchar('axb', -1))
   call assert_equal(-1, strgetchar('axb', 3))
   call assert_equal(-1, strgetchar('', 0))
+  call assert_fails("let c=strgetchar([], 1)", 'E730:')
+  call assert_fails("let c=strgetchar('axb', [])", 'E745:')
 endfunc
 
 func Test_strcharpart()
@@ -502,6 +504,9 @@ func Test_substitute_expr()
   endfunc
   " recursive call works
   call assert_equal('-y-x-', substitute('xxx', 'x\(.\)x', {-> '-' . Recurse() . '-' . submatch(1) . '-'}, ''))
+
+  call assert_fails("let s=submatch([])", 'E745:')
+  call assert_fails("let s=submatch(2, [])", 'E745:')
 endfunc
 
 func Test_invalid_submatch()
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index fa79aaf6d7..f6c16a366b 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -19,6 +19,25 @@ func Test_00_bufexists()
   call assert_equal(0, bufexists('Xfoo'))
 endfunc
 
+func Test_has()
+  throw 'Skipped: Nvim has removed some features'
+  call assert_equal(1, has('eval'))
+  call assert_equal(1, has('eval', 1))
+
+  if has('unix')
+    call assert_equal(1, or(has('ttyin'), 1))
+    call assert_equal(0, and(has('ttyout'), 0))
+    call assert_equal(1, has('multi_byte_encoding'))
+  endif
+
+  call assert_equal(0, has('nonexistent'))
+  call assert_equal(0, has('nonexistent', 1))
+
+  " Will we ever have patch 9999?
+  let ver = 'patch-' .. v:version / 100 .. '.' .. v:version % 100 .. '.9999'
+  call assert_equal(0, has(ver))
+endfunc
+
 func Test_empty()
   call assert_equal(1, empty(''))
   call assert_equal(0, empty('a'))
@@ -383,6 +402,8 @@ func Test_strpart()
   call assert_equal('abcdefg', 'abcdefg'->strpart(-2))
   call assert_equal('fg', strpart('abcdefg', 5, 4))
   call assert_equal('defg', strpart('abcdefg', 3))
+  call assert_equal('', strpart('abcdefg', 10))
+  call assert_fails("let s=strpart('abcdef', [])", 'E745:')
 
   call assert_equal('lép', strpart('éléphant', 2, 4))
   call assert_equal('léphant', strpart('éléphant', 2))
@@ -552,6 +573,15 @@ endfunc
 func Test_tr()
   call assert_equal('foo', tr('bar', 'bar', 'foo'))
   call assert_equal('zxy', 'cab'->tr('abc', 'xyz'))
+  call assert_fails("let s=tr([], 'abc', 'def')", 'E730:')
+  call assert_fails("let s=tr('abc', [], 'def')", 'E730:')
+  call assert_fails("let s=tr('abc', 'abc', [])", 'E730:')
+  call assert_fails("let s=tr('abcd', 'abcd', 'def')", 'E475:')
+  " set encoding=latin1
+  call assert_fails("let s=tr('abcd', 'abcd', 'def')", 'E475:')
+  call assert_equal('hEllO', tr('hello', 'eo', 'EO'))
+  call assert_equal('hello', tr('hello', 'xy', 'ab'))
+  set encoding=utf8
 endfunc
 
 " Tests for the mode() function
@@ -851,6 +881,8 @@ func Test_stridx()
   call assert_equal(-1, stridx('hello', 'l', 10))
   call assert_equal(2,  stridx('hello', 'll'))
   call assert_equal(-1, stridx('hello', 'hello world'))
+  call assert_fails("let n=stridx('hello', [])", 'E730:')
+  call assert_fails("let n=stridx([], 'l')", 'E730:')
 endfunc
 
 func Test_strridx()
@@ -867,6 +899,8 @@ func Test_strridx()
   call assert_equal(-1, strridx('hello', 'l', -1))
   call assert_equal(2,  strridx('hello', 'll'))
   call assert_equal(-1, strridx('hello', 'hello world'))
+  call assert_fails("let n=strridx('hello', [])", 'E730:')
+  call assert_fails("let n=strridx([], 'l')", 'E730:')
 endfunc
 
 func Test_match_func()
@@ -876,6 +910,11 @@ func Test_match_func()
   call assert_equal(-1, match('testing', 'ing', 8))
   call assert_equal(1, match(['vim', 'testing', 'execute'], 'ing'))
   call assert_equal(-1, match(['vim', 'testing', 'execute'], 'img'))
+  call assert_fails("let x=match('vim', [])", 'E730:')
+  call assert_equal(3, match(['a', 'b', 'c', 'a'], 'a', 1))
+  call assert_equal(-1, match(['a', 'b', 'c', 'a'], 'a', 5))
+  call assert_equal(4,  match('testing', 'ing', -1))
+  call assert_fails("let x=match('testing', 'ing', 0, [])", 'E745:')
 endfunc
 
 func Test_matchend()
@@ -982,6 +1021,7 @@ func Test_byte2line_line2byte()
   bw!
 endfunc
 
+" Test for byteidx() and byteidxcomp() functions
 func Test_byteidx()
   let a = '.é.' " one char of two bytes
   call assert_equal(0, byteidx(a, 0))
@@ -1001,6 +1041,7 @@ func Test_byteidx()
   call assert_equal(4, b->byteidx(2))
   call assert_equal(5, b->byteidx(3))
   call assert_equal(-1, b->byteidx(4))
+  call assert_fails("call byteidx([], 0)", 'E730:')
 
   call assert_equal(0, b->byteidxcomp(0))
   call assert_equal(1, b->byteidxcomp(1))
@@ -1008,6 +1049,7 @@ func Test_byteidx()
   call assert_equal(4, b->byteidxcomp(3))
   call assert_equal(5, b->byteidxcomp(4))
   call assert_equal(-1, b->byteidxcomp(5))
+  call assert_fails("call byteidxcomp([], 0)", 'E730:')
 endfunc
 
 " Test for charidx()
@@ -1230,6 +1272,22 @@ func Test_col()
   xunmap 
   delfunc T
 
+  " Test for the visual line start and end marks '< and '>
+  call setline(1, ['one', 'one two', 'one two three'])
+  "normal! ggVG
+  call feedkeys("ggVG\", 'xt')
+  call assert_equal(1, col("'<"))
+  call assert_equal(14, col("'>"))
+  " Delete the last line of the visually selected region
+  $d
+  call assert_notequal(14, col("'>"))
+
+  " Test with 'virtualedit'
+  set virtualedit=all
+  call cursor(1, 10)
+  call assert_equal(4, col('.'))
+  set virtualedit&
+
   bw!
 endfunc
 
@@ -1763,7 +1821,7 @@ func Test_confirm()
   call assert_equal(2, a)
 
   " confirm() should return 0 when pressing CTRL-C.
-  call feedkeys("\", 'L')
+  call feedkeys("\", 'L')
   let a = confirm('Are you sure?', "&Yes\n&No")
   call assert_equal(0, a)
 
@@ -1871,6 +1929,9 @@ endfunc
 func Test_char2nr()
   call assert_equal(12354, char2nr('ã‚', 1))
   call assert_equal(120, 'x'->char2nr())
+  " set encoding=latin1
+  call assert_equal(120, 'x'->char2nr())
+  set encoding=utf-8
 endfunc
 
 func Test_charclass()
@@ -2043,6 +2104,7 @@ func Test_range()
 
   " index()
   call assert_equal(1, index(range(1, 5), 2))
+  call assert_fails("echo index([1, 2], 1, [])", 'E745:')
 
   " inputlist()
   call feedkeys(":let result = inputlist(range(10))\1\", 'x')
@@ -2204,6 +2266,11 @@ func Test_range()
 
   " uniq()
   call assert_equal([0, 1, 2, 3, 4], uniq(range(5)))
+
+  " errors
+  call assert_fails('let x=range(2, 8, 0)', 'E726:')
+  call assert_fails('let x=range(3, 1)', 'E727:')
+  call assert_fails('let x=range(1, 3, -2)', 'E727:')
 endfunc
 
 func Test_garbagecollect_now_fails()
@@ -2255,6 +2322,13 @@ func Test_nr2char()
   call assert_equal("\x80\xfc\b" .. nr2char(0x40000000), eval('"\"'))
 endfunc
 
+" Test for screenattr(), screenchar() and screenchars() functions
+func Test_screen_functions()
+  call assert_equal(-1, screenattr(-1, -1))
+  call assert_equal(-1, screenchar(-1, -1))
+  call assert_equal([], screenchars(-1, -1))
+endfunc
+
 " Test for getcurpos() and setpos()
 func Test_getcurpos_setpos()
   new
diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim
index f7261b2055..1ecdcd2157 100644
--- a/src/nvim/testdir/test_listdict.vim
+++ b/src/nvim/testdir/test_listdict.vim
@@ -341,6 +341,7 @@ func Test_dict_deepcopy()
   let l[1] = l2
   let l3 = deepcopy(l2)
   call assert_true(l3[1] is l3[2])
+  call assert_fails("call deepcopy([1, 2], 2)", 'E474:')
 endfunc
 
 " Locked variables
@@ -420,6 +421,11 @@ func Test_list_locked_var()
       call assert_equal(expected[depth][u][1], ps)
     endfor
   endfor
+  call assert_fails("let x=islocked('a b')", 'E488:')
+  let mylist = [1, 2, 3]
+  call assert_fails("let x = islocked('mylist[1:2]')", 'E786:')
+  let mydict = {'k' : 'v'}
+  call assert_fails("let x = islocked('mydict.a')", 'E716:')
 endfunc
 
 " Unletting locked variables
@@ -736,6 +742,8 @@ func Test_str_split()
   call assert_equal(['aa', '', 'bb', 'cc', ''], split('aa,,bb, cc,', ',\s*', 1))
   call assert_equal(['a', 'b', 'c'], split('abc', '\zs'))
   call assert_equal(['', 'a', '', 'b', '', 'c', ''], split('abc', '\zs', 1))
+  call assert_fails("call split('abc', [])", 'E730:')
+  call assert_fails("call split('abc', 'b', [])", 'E745:')
 endfunc
 
 " compare recursively linked list and dict
diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim
index 054ebf1218..b432b7bbbc 100644
--- a/src/nvim/testdir/test_marks.vim
+++ b/src/nvim/testdir/test_marks.vim
@@ -100,6 +100,8 @@ func Test_setpos()
   call setpos('.', [0, 1, -1, 0])
   call assert_equal([2, 2], [line('.'), col('.')])
 
+  call assert_fails("call setpos('ab', [0, 1, 1, 0])", 'E474:')
+
   bwipe!
   call win_gotoid(twowin)
   bwipe!
diff --git a/src/nvim/testdir/test_partial.vim b/src/nvim/testdir/test_partial.vim
index 8c90f21600..3020668f1b 100644
--- a/src/nvim/testdir/test_partial.vim
+++ b/src/nvim/testdir/test_partial.vim
@@ -82,6 +82,9 @@ func Test_partial_dict()
 
   let dict = {"tr": function('tr', ['hello', 'h', 'H'])}
   call assert_equal("Hello", dict.tr())
+
+  call assert_fails("let F=function('setloclist', 10)", "E923:")
+  call assert_fails("let F=function('setloclist', [], [])", "E922:")
 endfunc
 
 func Test_partial_implicit()
@@ -354,3 +357,5 @@ func Test_compare_partials()
   call assert_true(F1 isnot# F1d1)  " Partial /= non-partial
   call assert_true(d1.f1 isnot# d1.f1)  " handle_subscript creates new partial each time
 endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim
index 11dd3badb6..40320c6405 100644
--- a/src/nvim/testdir/test_registers.vim
+++ b/src/nvim/testdir/test_registers.vim
@@ -263,8 +263,16 @@ func Test_get_register()
   call assert_equal('', getreg("\"))
   call assert_equal('', getreg("\"))
   call assert_equal('', getreg("\"))
+  " Change the last used register to '"' for the next test
+  normal! ""yy
+  let @" = 'happy'
+  call assert_equal('happy', getreg())
+  call assert_equal('happy', getreg(''))
 
   call assert_equal('', getregtype('!'))
+  call assert_fails('echo getregtype([])', 'E730:')
+  call assert_equal('v', getregtype())
+  call assert_equal('v', getregtype(''))
 
   " Test for inserting an invalid register content
   call assert_beeps('exe "normal i\!"')
@@ -349,6 +357,12 @@ func Test_set_register()
   normal 0".gP
   call assert_equal('abcabcabc', getline(1))
 
+  let @"=''
+  call setreg('', '1')
+  call assert_equal('1', @")
+  call setreg('@', '2')
+  call assert_equal('2', @")
+
   enew!
 endfunc
 
diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim
index 4af02d3d31..f7144bbc7e 100644
--- a/src/nvim/testdir/test_spell.vim
+++ b/src/nvim/testdir/test_spell.vim
@@ -1430,3 +1430,5 @@ let g:test_data_aff_sal = [
       \"SAL ZZ-                  _",
       \"SAL Z                    S",
       \ ]
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim
index 88a3c13d65..2a8a925fdb 100644
--- a/src/nvim/testdir/test_substitute.vim
+++ b/src/nvim/testdir/test_substitute.vim
@@ -453,6 +453,11 @@ func Test_substitute_errors()
   setl nomodifiable
   call assert_fails('s/foo/bar/', 'E21:')
 
+  call assert_fails("let s=substitute([], 'a', 'A', 'g')", 'E730:')
+  call assert_fails("let s=substitute('abcda', [], 'A', 'g')", 'E730:')
+  call assert_fails("let s=substitute('abcda', 'a', [], 'g')", 'E730:')
+  call assert_fails("let s=substitute('abcda', 'a', 'A', [])", 'E730:')
+
   bwipe!
 endfunc
 
diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim
index d686ad7e96..9903b48dbe 100644
--- a/src/nvim/testdir/test_syntax.vim
+++ b/src/nvim/testdir/test_syntax.vim
@@ -623,6 +623,8 @@ func Test_synstack_synIDtrans()
   call assert_equal(['cComment', 'cTodo'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")'))
   call assert_equal(['Comment', 'Todo'],   map(synstack(line("."), col(".")), 'synIDattr(synIDtrans(v:val), "name")'))
 
+  call assert_fails("let n=synIDtrans([])", 'E745:')
+
   syn clear
   bw!
 endfunc
diff --git a/src/nvim/testdir/test_taglist.vim b/src/nvim/testdir/test_taglist.vim
index be46773256..0c47fc9445 100644
--- a/src/nvim/testdir/test_taglist.vim
+++ b/src/nvim/testdir/test_taglist.vim
@@ -36,6 +36,8 @@ func Test_taglist()
   call assert_equal('d', cmd[0]['kind'])
   call assert_equal('call cursor(3, 4)', cmd[0]['cmd'])
 
+  call assert_fails("let l=taglist([])", 'E730:')
+
   call delete('Xtags')
   set tags&
   bwipe
diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim
index 7145d303cc..2fced0dd2f 100644
--- a/src/nvim/testdir/test_utf8.vim
+++ b/src/nvim/testdir/test_utf8.vim
@@ -21,6 +21,8 @@ func Test_strchars()
     call assert_equal(exp[i][1], inp[i]->strchars(0))
     call assert_equal(exp[i][2], strchars(inp[i], 1))
   endfor
+  call assert_fails("let v=strchars('abc', [])", 'E474:')
+  call assert_fails("let v=strchars('abc', 2)", 'E474:')
 endfunc
 
 " Test for customlist completion
diff --git a/src/nvim/testdir/test_vartabs.vim b/src/nvim/testdir/test_vartabs.vim
index 32ad64cda4..e12c71d521 100644
--- a/src/nvim/testdir/test_vartabs.vim
+++ b/src/nvim/testdir/test_vartabs.vim
@@ -379,6 +379,8 @@ func Test_vartabs_shiftwidth()
   let lines = ScreenLines([1, 3], winwidth(0))
   call s:compare_lines(expect4, lines)
 
+  call assert_fails('call shiftwidth([])', 'E745:')
+
   " cleanup
   bw!
   bw!
diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim
index 909db3a1bb..2ed537c601 100644
--- a/src/nvim/testdir/test_window_cmd.vim
+++ b/src/nvim/testdir/test_window_cmd.vim
@@ -542,6 +542,7 @@ func Test_window_newtab()
   call assert_equal(2, tabpagenr('$'))
   call assert_equal(['Xb', 'Xa'], map(tabpagebuflist(1), 'bufname(v:val)'))
   call assert_equal(['Xc'      ], map(2->tabpagebuflist(), 'bufname(v:val)'))
+  call assert_equal(['Xc'      ], map(tabpagebuflist(), 'bufname(v:val)'))
 
   %bw!
 endfunc
-- 
cgit 


From f1c864cfe340dbb225caaf3dcbe28ee705be9f75 Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Fri, 4 Nov 2022 10:31:09 +0000
Subject: fix(diff): remove size_t underflow (#20929)

---
 src/nvim/linematch.c | 7 +++----
 src/nvim/lua/xdiff.c | 4 ++--
 2 files changed, 5 insertions(+), 6 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/linematch.c b/src/nvim/linematch.c
index ef63bd2321..04209bb836 100644
--- a/src/nvim/linematch.c
+++ b/src/nvim/linematch.c
@@ -149,10 +149,9 @@ static int count_n_matched_chars(const char **sp, const size_t n, bool iwhite)
   return matched_chars;
 }
 
-void fastforward_buf_to_lnum(const char **s, size_t lnum)
+void fastforward_buf_to_lnum(const char **s, long lnum)
 {
-  assert(lnum > 0);
-  for (size_t j = 0; j < lnum - 1; j++) {
+  for (long i = 0; i < lnum - 1; i++) {
     *s = strchr(*s, '\n');
     (*s)++;
   }
@@ -185,7 +184,7 @@ static void try_possible_paths(const int *df_iters, const size_t *paths, const i
         if ((*choice) & (1 << k)) {
           from_vals[k]--;
           const char *p = diff_blk[k];
-          fastforward_buf_to_lnum(&p, (size_t)df_iters[k]);
+          fastforward_buf_to_lnum(&p, df_iters[k]);
           current_lines[k] = p;
         } else {
           current_lines[k] = NULL;
diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c
index 428a33cdad..ec7dc3c666 100644
--- a/src/nvim/lua/xdiff.c
+++ b/src/nvim/lua/xdiff.c
@@ -60,8 +60,8 @@ static void get_linematch_results(lua_State *lstate, mmfile_t *ma, mmfile_t *mb,
   const char *diff_begin[2] = { ma->ptr, mb->ptr };
   int diff_length[2] = { (int)count_a, (int)count_b };
 
-  fastforward_buf_to_lnum(&diff_begin[0], (size_t)start_a);
-  fastforward_buf_to_lnum(&diff_begin[1], (size_t)start_b);
+  fastforward_buf_to_lnum(&diff_begin[0], start_a);
+  fastforward_buf_to_lnum(&diff_begin[1], start_b);
 
   int *decisions = NULL;
   size_t decisions_length = linematch_nbuffers(diff_begin, diff_length, 2, &decisions, iwhite);
-- 
cgit 


From b854f2ce093de9593278b371de796f2ace044376 Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Fri, 4 Nov 2022 11:12:17 +0000
Subject: fix(vim.diff): correctly apply hunk offsets with linematch (#20931)

---
 src/nvim/lua/xdiff.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c
index ec7dc3c666..3adb6088fc 100644
--- a/src/nvim/lua/xdiff.c
+++ b/src/nvim/lua/xdiff.c
@@ -41,6 +41,15 @@ typedef struct {
 
 static void lua_pushhunk(lua_State *lstate, long start_a, long count_a, long start_b, long count_b)
 {
+  // Mimic extra offsets done by xdiff, see:
+  // src/xdiff/xemit.c:284
+  // src/xdiff/xutils.c:(356,368)
+  if (count_a > 0) {
+    start_a += 1;
+  }
+  if (count_b > 0) {
+    start_b += 1;
+  }
   lua_createtable(lstate, 0, 0);
   lua_pushinteger(lstate, start_a);
   lua_rawseti(lstate, -2, 1);
@@ -116,15 +125,6 @@ static int write_string(void *priv, mmbuffer_t *mb, int nbuf)
 // hunk_func callback used when opts.hunk_lines = true
 static int hunk_locations_cb(long start_a, long count_a, long start_b, long count_b, void *cb_data)
 {
-  // Mimic extra offsets done by xdiff, see:
-  // src/xdiff/xemit.c:284
-  // src/xdiff/xutils.c:(356,368)
-  if (count_a > 0) {
-    start_a += 1;
-  }
-  if (count_b > 0) {
-    start_b += 1;
-  }
   hunkpriv_t *priv = (hunkpriv_t *)cb_data;
   lua_State *lstate = priv->lstate;
   if (priv->linematch) {
-- 
cgit 


From dce3fc3e9a455426a45db072f28604b1bc63680a Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 4 Nov 2022 19:31:58 +0800
Subject: vim-patch:8.2.0540: regexp and other code not tested (#20930)

Problem:    Regexp and other code not tested.
Solution:   Add more tests. (Yegappan Lakshmanan, closes vim/vim#5904)

https://github.com/vim/vim/commit/004a6781b3cf15ca5dd632c38cc09bb3b253d1f8
---
 src/nvim/testdir/samples/re.freeze.txt |   6 ++
 src/nvim/testdir/test_expr.vim         |  46 --------------
 src/nvim/testdir/test_increment.vim    |  18 ++++--
 src/nvim/testdir/test_normal.vim       | 111 ++++++++++++++++++++++-----------
 src/nvim/testdir/test_regexp_latin.vim |   6 ++
 src/nvim/testdir/test_search.vim       |  19 ++++++
 src/nvim/testdir/test_substitute.vim   |  75 ++++++++++++++++++++++
 src/nvim/testdir/test_virtualedit.vim  |  11 ++++
 8 files changed, 207 insertions(+), 85 deletions(-)
 create mode 100644 src/nvim/testdir/samples/re.freeze.txt

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/samples/re.freeze.txt b/src/nvim/testdir/samples/re.freeze.txt
new file mode 100644
index 0000000000..d768c23c5e
--- /dev/null
+++ b/src/nvim/testdir/samples/re.freeze.txt
@@ -0,0 +1,6 @@
+:set re=0 or 2
+Search for the pattern: /\s\+\%#\@55555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555 
+
diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim
index 00ea4275ab..dc8401003d 100644
--- a/src/nvim/testdir/test_expr.vim
+++ b/src/nvim/testdir/test_expr.vim
@@ -487,52 +487,6 @@ function Test_max_min_errors()
   call assert_fails('call min(v:true)', 'min()')
 endfunc
 
-func Test_substitute_expr()
-  let g:val = 'XXX'
-  call assert_equal('XXX', substitute('yyy', 'y*', '\=g:val', ''))
-  call assert_equal('XXX', substitute('yyy', 'y*', {-> g:val}, ''))
-  call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
-			   \ '\=nr2char("0x" . submatch(1))', 'g'))
-  call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
-			   \ {-> nr2char("0x" . submatch(1))}, 'g'))
-
-  call assert_equal('231', substitute('123', '\(.\)\(.\)\(.\)',
-	\ {-> submatch(2) . submatch(3) . submatch(1)}, ''))
-
-  func Recurse()
-    return substitute('yyy', 'y\(.\)y', {-> submatch(1)}, '')
-  endfunc
-  " recursive call works
-  call assert_equal('-y-x-', substitute('xxx', 'x\(.\)x', {-> '-' . Recurse() . '-' . submatch(1) . '-'}, ''))
-
-  call assert_fails("let s=submatch([])", 'E745:')
-  call assert_fails("let s=submatch(2, [])", 'E745:')
-endfunc
-
-func Test_invalid_submatch()
-  " This was causing invalid memory access in Vim-7.4.2232 and older
-  call assert_fails("call substitute('x', '.', {-> submatch(10)}, '')", 'E935:')
-endfunc
-
-func Test_substitute_expr_arg()
-  call assert_equal('123456789-123456789=', substitute('123456789',
-	\ '\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)',
-	\ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
-
-  call assert_equal('123456-123456=789', substitute('123456789',
-	\ '\(.\)\(.\)\(.\)\(a*\)\(n*\)\(.\)\(.\)\(.\)\(x*\)',
-	\ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
-
-  call assert_equal('123456789-123456789x=', substitute('123456789',
-	\ '\(.\)\(.\)\(.*\)',
-	\ {m -> m[0] . '-' . m[1] . m[2] . m[3] . 'x' . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
-
-  call assert_fails("call substitute('xxx', '.', {m -> string(add(m, 'x'))}, '')", 'E742:')
-  call assert_fails("call substitute('xxx', '.', {m -> string(insert(m, 'x'))}, '')", 'E742:')
-  call assert_fails("call substitute('xxx', '.', {m -> string(extend(m, ['x']))}, '')", 'E742:')
-  call assert_fails("call substitute('xxx', '.', {m -> string(remove(m, 1))}, '')", 'E742:')
-endfunc
-
 func Test_function_with_funcref()
   let s:f = function('type')
   let s:fref = function(s:f)
diff --git a/src/nvim/testdir/test_increment.vim b/src/nvim/testdir/test_increment.vim
index 2559654f25..52355d86fb 100644
--- a/src/nvim/testdir/test_increment.vim
+++ b/src/nvim/testdir/test_increment.vim
@@ -476,6 +476,10 @@ func Test_visual_increment_20()
   exec "norm! \"
   call assert_equal(["b"], getline(1, '$'))
   call assert_equal([0, 1, 1, 0], getpos('.'))
+  " decrement a and A and increment z and Z
+  call setline(1, ['a', 'A', 'z', 'Z'])
+  exe "normal 1G\2G\3G\4G\"
+  call assert_equal(['a', 'A', 'z', 'Z'], getline(1, '$'))
 endfunc
 
 " 21) block-wise increment on part of hexadecimal
@@ -566,12 +570,14 @@ endfunc
 "   1) 
 " 0b11111111111111111111111111111111
 func Test_visual_increment_26()
-  set nrformats+=alpha
+  set nrformats+=bin
   call setline(1, ["0b11111111111111111111111111111110"])
   exec "norm! \$\"
   call assert_equal(["0b11111111111111111111111111111111"], getline(1, '$'))
   call assert_equal([0, 1, 1, 0], getpos('.'))
-  set nrformats-=alpha
+  exec "norm! \$\"
+  call assert_equal(["0b11111111111111111111111111111110"], getline(1, '$'))
+  set nrformats-=bin
 endfunc
 
 " 27) increment with 'rightreft', if supported
@@ -772,7 +778,6 @@ func Test_normal_increment_03()
 endfunc
 
 func Test_increment_empty_line()
-  new
   call setline(1, ['0', '0', '0', '0', '0', '0', ''])
   exe "normal Gvgg\"
   call assert_equal(['1', '1', '1', '1', '1', '1', ''], getline(1, 7))
@@ -783,8 +788,13 @@ func Test_increment_empty_line()
   exe "normal! c\l"
   exe "normal! c\l"
   call assert_equal('one two', getline(1))
+endfunc
 
-  bwipe!
+" Try incrementing/decrementing a non-digit/alpha character
+func Test_increment_special_char()
+  call setline(1, '!')
+  call assert_beeps("normal \")
+  call assert_beeps("normal \")
 endfunc
 
 " Try incrementing/decrementing a number when nrformats contains unsigned
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index 5730085c78..32bb755584 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -140,16 +140,15 @@ func Test_normal03_join()
   $
   :j 10
   call assert_equal('100', getline('.'))
+  call assert_beeps('normal GVJ')
   " clean up
   bw!
 endfunc
 
+" basic filter test
 func Test_normal04_filter()
-  " basic filter test
   " only test on non windows platform
-  if has('win32')
-    return
-  endif
+  CheckNotMSWindows
   call Setup_NewWindow()
   1
   call feedkeys("!!sed -e 's/^/|    /'\n", 'tx')
@@ -222,12 +221,10 @@ func Test_normal_formatexpr_returns_nonzero()
   close!
 endfunc
 
+" basic test for formatprg
 func Test_normal06_formatprg()
-  " basic test for formatprg
   " only test on non windows platform
-  if has('win32')
-    return
-  endif
+  CheckNotMSWindows
 
   " uses sed to number non-empty lines
   call writefile(['#!/bin/sh', 'sed ''/./=''|sed ''/./{', 'N', 's/\n/    /', '}'''], 'Xsed_format.sh')
@@ -240,16 +237,24 @@ func Test_normal06_formatprg()
   set formatprg=./Xsed_format.sh
   norm! gggqG
   call assert_equal(expected, getline(1, '$'))
-  bw!
+  %d
 
-  10new
   call setline(1, text)
   set formatprg=donothing
   setlocal formatprg=./Xsed_format.sh
   norm! gggqG
   call assert_equal(expected, getline(1, '$'))
-  bw!
+  %d
 
+  " Check for the command-line ranges added to 'formatprg'
+  set formatprg=cat
+  call setline(1, ['one', 'two', 'three', 'four', 'five'])
+  call feedkeys('gggqG', 'xt')
+  call assert_equal('.,$!cat', @:)
+  call feedkeys('2Ggq2j', 'xt')
+  call assert_equal('.,.+2!cat', @:)
+
+  bw!
   " clean up
   set formatprg=
   setlocal formatprg=
@@ -263,18 +268,16 @@ func Test_normal07_internalfmt()
   10new
   call setline(1, list)
   set tw=12
-  norm! gggqG
+  norm! ggVGgq
   call assert_equal(['1    2    3', '4    5    6', '7    8    9', '10    11    '], getline(1, '$'))
   " clean up
   set tw=0
   bw!
 endfunc
 
+" basic tests for foldopen/folddelete
 func Test_normal08_fold()
-  " basic tests for foldopen/folddelete
-  if !has("folding")
-    return
-  endif
+  CheckFeature folding
   call Setup_NewWindow()
   50
   setl foldenable fdm=marker
@@ -1432,10 +1435,8 @@ func Test_normal18_z_fold()
 endfunc
 
 func Test_normal20_exmode()
-  if !has("unix")
-    " Reading from redirected file doesn't work on MS-Windows
-    return
-  endif
+  " Reading from redirected file doesn't work on MS-Windows
+  CheckNotMSWindows
   call writefile(['1a', 'foo', 'bar', '.', 'w! Xfile2', 'q!'], 'Xscript')
   call writefile(['1', '2'], 'Xfile')
   call system(GetVimCommand() .. ' -e -s < Xscript Xfile')
@@ -2154,6 +2155,12 @@ func Test_normal31_r_cmd()
   " r command should fail in operator pending mode
   call assert_beeps('normal! cr')
 
+  " replace a tab character in visual mode
+  %d
+  call setline(1, ["a\tb", "c\td", "e\tf"])
+  normal gglvjjrx
+  call assert_equal(['axx', 'xxx', 'xxf'], getline(1, '$'))
+
   " clean up
   set noautoindent
   bw!
@@ -2178,9 +2185,7 @@ endfunc
 " Test for g`, g;, g,, g&, gv, gk, gj, gJ, g0, g^, g_, gm, g$, gM, g CTRL-G,
 " gi and gI commands
 func Test_normal33_g_cmd2()
-  if !has("jumplist")
-    return
-  endif
+  CheckFeature jumplist
   call Setup_NewWindow()
   " Test for g`
   clearjumps
@@ -2849,9 +2854,8 @@ func Test_normal49_counts()
 endfunc
 
 func Test_normal50_commandline()
-  if !has("timers") || !has("cmdline_hist")
-    return
-  endif
+  CheckFeature timers
+  CheckFeature cmdline_hist
   func! DoTimerWork(id)
     call assert_equal('[Command Line]', bufname(''))
     " should fail, with E11, but does fail with E23?
@@ -2880,9 +2884,7 @@ func Test_normal50_commandline()
 endfunc
 
 func Test_normal51_FileChangedRO()
-  if !has("autocmd")
-    return
-  endif
+  CheckFeature autocmd
   call writefile(['foo'], 'Xreadonly.log')
   new Xreadonly.log
   setl ro
@@ -2897,9 +2899,7 @@ func Test_normal51_FileChangedRO()
 endfunc
 
 func Test_normal52_rl()
-  if !has("rightleft")
-    return
-  endif
+  CheckFeature rightleft
   new
   call setline(1, 'abcde fghij klmnopq')
   norm! 1gg$
@@ -2932,9 +2932,7 @@ func Test_normal52_rl()
 endfunc
 
 func Test_normal53_digraph()
-  if !has('digraphs')
-    return
-  endif
+  CheckFeature digraphs
   new
   call setline(1, 'abcdefgh|')
   exe "norm! 1gg0f\!!"
@@ -3371,6 +3369,49 @@ func Test_normal_colon_op()
   close!
 endfunc
 
+" Test for d and D commands
+func Test_normal_delete_cmd()
+  new
+  " D in an empty line
+  call setline(1, '')
+  normal D
+  call assert_equal('', getline(1))
+  " D in an empty line in virtualedit mode
+  set virtualedit=all
+  normal D
+  call assert_equal('', getline(1))
+  set virtualedit&
+  " delete to a readonly register
+  call setline(1, ['abcd'])
+  call assert_beeps('normal ":d2l')
+  close!
+endfunc
+
+" Test for the 'E' flag in 'cpo' with yank, change, delete, etc. operators
+func Test_empty_region_error()
+  new
+  call setline(1, '')
+  set cpo+=E
+  " yank an empty line
+  call assert_beeps('normal "ayl')
+  " change an empty line
+  call assert_beeps('normal lcTa')
+  " delete an empty line
+  call assert_beeps('normal D')
+  call assert_beeps('normal dl')
+  call assert_equal('', getline(1))
+  " change case of an empty line
+  call assert_beeps('normal gul')
+  call assert_beeps('normal gUl')
+  " replace a character
+  call assert_beeps('normal vrx')
+  " increment and decrement
+  call assert_beeps('exe "normal v\"')
+  call assert_beeps('exe "normal v\"')
+  set cpo-=E
+  close!
+endfunc
+
 " Test for deleting or changing characters across lines with 'whichwrap'
 " containing 's'. Should count  as one character.
 func Test_normal_op_across_lines()
diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim
index d08a980787..2520273e55 100644
--- a/src/nvim/testdir/test_regexp_latin.vim
+++ b/src/nvim/testdir/test_regexp_latin.vim
@@ -146,6 +146,10 @@ func Test_regexp_single_line_pat()
   call add(tl, [2, 'c*', 'abdef', ''])
   call add(tl, [2, 'bc\+', 'abccccdef', 'bcccc'])
   call add(tl, [2, 'bc\+', 'abdef']) " no match
+  " match escape character in a string
+  call add(tl, [2, '.\e.', "one\two", "e\t"])
+  " match backspace character in a string
+  call add(tl, [2, '.\b.', "one\two", "e\t"])
   " match newline character in a string
   call add(tl, [2, 'o\nb', "foo\nbar", "o\nb"])
 
@@ -895,6 +899,8 @@ func Test_regexp_error()
   call assert_fails("call matchlist('x x', '\\%#=2 \\zs*')", 'E888:')
   call assert_fails("call matchlist('x x', '\\%#=2 \\ze*')", 'E888:')
   call assert_fails('exe "normal /\\%#=1\\%[x\\%[x]]\"', 'E369:')
+  call assert_fails("call matchstr('abcd', '\\%o841\\%o142')", 'E678:')
+  call assert_equal('', matchstr('abcd', '\%o181\%o142'))
 endfunc
 
 " Test for using the last substitute string pattern (~)
diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim
index 0cf55c7d0b..d79910fbc1 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -1752,6 +1752,25 @@ func Test_invalid_regexp()
   call assert_fails("call search('\\%#=3ab')", 'E864:')
 endfunc
 
+" Test for searching a very complex pattern in a string. Should switch the
+" regexp engine from NFA to the old engine.
+func Test_regexp_switch_engine()
+  let l = readfile('samples/re.freeze.txt')
+  let v = substitute(l[4], '..\@\"
+  call assert_equal([2, 5], [line('.'), col('.')])
+  exe "normal 2GVj$?\\%Vbar\\"
+  call assert_equal([3, 5], [line('.'), col('.')])
+  close!
+endfunc
+
 " Test for searching with 'smartcase' and 'ignorecase'
 func Test_search_smartcase()
   new
diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim
index 2a8a925fdb..9b90205c3d 100644
--- a/src/nvim/testdir/test_substitute.vim
+++ b/src/nvim/testdir/test_substitute.vim
@@ -493,6 +493,9 @@ func Test_sub_replace_1()
   call assert_equal("x\x", substitute('xXx', 'X', "\r", ''))
   call assert_equal("YyyY", substitute('Y', 'Y', '\L\uyYy\l\EY', ''))
   call assert_equal("zZZz", substitute('Z', 'Z', '\U\lZzZ\u\Ez', ''))
+  " \v or \V after $
+  call assert_equal('abxx', substitute('abcd', 'xy$\v|cd$', 'xx', ''))
+  call assert_equal('abxx', substitute('abcd', 'xy$\V\|cd\$', 'xx', ''))
 endfunc
 
 func Test_sub_replace_2()
@@ -867,6 +870,40 @@ endfunc
 
 func Test_substitute()
   call assert_equal('a1a2a3a', substitute('123', '\zs', 'a', 'g'))
+  " Substitute with special keys
+  call assert_equal("a\c", substitute('abc', "a.c", "a\c", ''))
+endfunc
+
+func Test_substitute_expr()
+  let g:val = 'XXX'
+  call assert_equal('XXX', substitute('yyy', 'y*', '\=g:val', ''))
+  call assert_equal('XXX', substitute('yyy', 'y*', {-> g:val}, ''))
+  call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
+			   \ '\=nr2char("0x" . submatch(1))', 'g'))
+  call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
+			   \ {-> nr2char("0x" . submatch(1))}, 'g'))
+
+  call assert_equal('231', substitute('123', '\(.\)\(.\)\(.\)',
+	\ {-> submatch(2) . submatch(3) . submatch(1)}, ''))
+
+  func Recurse()
+    return substitute('yyy', 'y\(.\)y', {-> submatch(1)}, '')
+  endfunc
+  " recursive call works
+  call assert_equal('-y-x-', substitute('xxx', 'x\(.\)x', {-> '-' . Recurse() . '-' . submatch(1) . '-'}, ''))
+
+  call assert_fails("let s=submatch([])", 'E745:')
+  call assert_fails("let s=submatch(2, [])", 'E745:')
+endfunc
+
+func Test_invalid_submatch()
+  " This was causing invalid memory access in Vim-7.4.2232 and older
+  call assert_fails("call substitute('x', '.', {-> submatch(10)}, '')", 'E935:')
+  call assert_fails('eval submatch(-1)', 'E935:')
+  call assert_equal('', submatch(0))
+  call assert_equal('', submatch(1))
+  call assert_equal([], submatch(0, 1))
+  call assert_equal([], submatch(1, 1))
 endfunc
 
 func Test_submatch_list_concatenate()
@@ -875,6 +912,44 @@ func Test_submatch_list_concatenate()
   call substitute('A1', pat, Rep, '')->assert_equal("[['A1'], ['1']]")
 endfunc
 
+func Test_substitute_expr_arg()
+  call assert_equal('123456789-123456789=', substitute('123456789',
+	\ '\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)',
+	\ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+  call assert_equal('123456-123456=789', substitute('123456789',
+	\ '\(.\)\(.\)\(.\)\(a*\)\(n*\)\(.\)\(.\)\(.\)\(x*\)',
+	\ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+  call assert_equal('123456789-123456789x=', substitute('123456789',
+	\ '\(.\)\(.\)\(.*\)',
+	\ {m -> m[0] . '-' . m[1] . m[2] . m[3] . 'x' . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+  call assert_fails("call substitute('xxx', '.', {m -> string(add(m, 'x'))}, '')", 'E742:')
+  call assert_fails("call substitute('xxx', '.', {m -> string(insert(m, 'x'))}, '')", 'E742:')
+  call assert_fails("call substitute('xxx', '.', {m -> string(extend(m, ['x']))}, '')", 'E742:')
+  call assert_fails("call substitute('xxx', '.', {m -> string(remove(m, 1))}, '')", 'E742:')
+endfunc
+
+" Test for using a function to supply the substitute string
+func Test_substitute_using_func()
+  func Xfunc()
+    return '1234'
+  endfunc
+  call assert_equal('a1234f', substitute('abcdef', 'b..e',
+        \ function("Xfunc"), ''))
+  delfunc Xfunc
+endfunc
+
+" Test for using submatch() with a multiline match
+func Test_substitute_multiline_submatch()
+  new
+  call setline(1, ['line1', 'line2', 'line3', 'line4'])
+  %s/^line1\(\_.\+\)line4$/\=submatch(1)/
+  call assert_equal(['', 'line2', 'line3', ''], getline(1, '$'))
+  close!
+endfunc
+
 func Test_substitute_skipped_range()
   new
   if 0
diff --git a/src/nvim/testdir/test_virtualedit.vim b/src/nvim/testdir/test_virtualedit.vim
index e712896562..ddc7c20637 100644
--- a/src/nvim/testdir/test_virtualedit.vim
+++ b/src/nvim/testdir/test_virtualedit.vim
@@ -346,6 +346,17 @@ func Test_yank_paste_small_del_reg()
   set virtualedit=
 endfunc
 
+" Test for delete that breaks a tab into spaces
+func Test_delete_break_tab()
+  new
+  call setline(1, "one\ttwo")
+  set virtualedit=all
+  normal v3ld
+  call assert_equal('    two', getline(1))
+  set virtualedit&
+  close!
+endfunc
+
 " After calling s:TryVirtualeditReplace(), line 1 will contain one of these
 " two strings, depending on whether virtual editing is on or off.
 let s:result_ve_on  = 'a      x'
-- 
cgit 


From 32a2c556ab3697111f01c9ef50447ec5d78070f7 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 4 Nov 2022 19:42:10 +0800
Subject: vim-patch:8.2.0968: no proper testing of the 'cpoptions' flags

Problem:    No proper testing of the 'cpoptions' flags.
Solution:   Add tests. (Yegappan Lakshmanan, closes vim/vim#6251)

https://github.com/vim/vim/commit/c9630d2658af9dcaa01913e899b201bfdef7b536
---
 src/nvim/testdir/test_cpoptions.vim | 631 ++++++++++++++++++++++++++++++++++++
 src/nvim/testdir/test_edit.vim      |  34 --
 src/nvim/testdir/test_normal.vim    |  79 -----
 3 files changed, 631 insertions(+), 113 deletions(-)
 create mode 100644 src/nvim/testdir/test_cpoptions.vim

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_cpoptions.vim b/src/nvim/testdir/test_cpoptions.vim
new file mode 100644
index 0000000000..ceaa728018
--- /dev/null
+++ b/src/nvim/testdir/test_cpoptions.vim
@@ -0,0 +1,631 @@
+" Test for various 'cpoptions' (cpo) flags
+
+source check.vim
+source view_util.vim
+
+" Test for the 'a' flag in 'cpo'. Reading a file should set the alternate
+" file name.
+func Test_cpo_a()
+  let save_cpo = &cpo
+  call writefile(['one'], 'Xfile')
+  " Wipe out all the buffers, so that the alternate file is empty
+  edit Xfoo | %bw
+  set cpo-=a
+  new
+  read Xfile
+  call assert_equal('', @#)
+  %d
+  set cpo+=a
+  read Xfile
+  call assert_equal('Xfile', @#)
+  close!
+  call delete('Xfile')
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'A' flag in 'cpo'. Writing a file should set the alternate
+" file name.
+func Test_cpo_A()
+  let save_cpo = &cpo
+  " Wipe out all the buffers, so that the alternate file is empty
+  edit Xfoo | %bw
+  set cpo-=A
+  new Xfile1
+  write Xfile2
+  call assert_equal('', @#)
+  %bw
+  call delete('Xfile2')
+  new Xfile1
+  set cpo+=A
+  write Xfile2
+  call assert_equal('Xfile2', @#)
+  close!
+  call delete('Xfile2')
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'b' flag in 'cpo'. "\|" at the end of a map command is
+" recognized as the end of the map.
+func Test_cpo_b()
+  let save_cpo = &cpo
+  set cpo+=b
+  nnoremap  :pwd\\|let i = 1
+  call assert_equal(':pwd\\', maparg(''))
+  nunmap 
+  exe "nnoremap  :pwd\|let i = 1"
+  call assert_equal(':pwd|let i = 1', maparg(''))
+  nunmap 
+  set cpo-=b
+  nnoremap  :pwd\\|let i = 1
+  call assert_equal(':pwd\|let i = 1', maparg(''))
+  let &cpo = save_cpo
+  nunmap 
+endfunc
+
+" Test for the 'c' flag in 'cpo'.
+func Test_cpo_c()
+  let save_cpo = &cpo
+  set cpo+=c
+  new
+  call setline(1, ' abababababab')
+  exe "normal gg/abab\"
+  call assert_equal(3, searchcount().total)
+  set cpo-=c
+  exe "normal gg/abab\"
+  call assert_equal(5, searchcount().total)
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'C' flag in 'cpo' (line continuation)
+func Test_cpo_C()
+  let save_cpo = &cpo
+  call writefile(['let l = [', '\ 1,', '\ 2]'], 'Xfile')
+  set cpo-=C
+  source Xfile
+  call assert_equal([1, 2], g:l)
+  set cpo+=C
+  call assert_fails('source Xfile', 'E10:')
+  call delete('Xfile')
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'd' flag in 'cpo' (tags relative to the current file)
+func Test_cpo_d()
+  let save_cpo = &cpo
+  call mkdir('Xdir')
+  call writefile(["one\tXfile1\t/^one$/"], 'tags')
+  call writefile(["two\tXfile2\t/^two$/"], 'Xdir/tags')
+  set tags=./tags
+  set cpo-=d
+  edit Xdir/Xfile
+  call assert_equal('two', taglist('.*')[0].name)
+  set cpo+=d
+  call assert_equal('one', taglist('.*')[0].name)
+  %bw!
+  call delete('tags')
+  call delete('Xdir', 'rf')
+  set tags&
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'D' flag in 'cpo' (digraph after a r, f or t)
+func Test_cpo_D()
+  CheckFeature digraphs
+  let save_cpo = &cpo
+  new
+  set cpo-=D
+  call setline(1, 'abcdefgh|')
+  exe "norm! 1gg0f\!!"
+  call assert_equal(9, col('.'))
+  set cpo+=D
+  exe "norm! 1gg0f\!!"
+  call assert_equal(1, col('.'))
+  set cpo-=D
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'e' flag in 'cpo'
+func Test_cpo_e()
+  let save_cpo = &cpo
+  let @a='let i = 45'
+  set cpo+=e
+  call feedkeys(":@a\", 'xt')
+  call assert_equal(45, i)
+  set cpo-=e
+  call feedkeys(":@a\6\", 'xt')
+  call assert_equal(456, i)
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'E' flag in 'cpo' with yank, change, delete, etc. operators
+func Test_cpo_E()
+  new
+  call setline(1, '')
+  set cpo+=E
+  " yank an empty line
+  call assert_beeps('normal "ayl')
+  " change an empty line
+  call assert_beeps('normal lcTa')
+  " delete an empty line
+  call assert_beeps('normal D')
+  call assert_beeps('normal dl')
+  call assert_equal('', getline(1))
+  " change case of an empty line
+  call assert_beeps('normal gul')
+  call assert_beeps('normal gUl')
+  " replace a character
+  call assert_beeps('normal vrx')
+  " increment and decrement
+  call assert_beeps('exe "normal v\"')
+  call assert_beeps('exe "normal v\"')
+  set cpo-=E
+  close!
+endfunc
+
+" Test for the 'f' flag in 'cpo' (read in an empty buffer sets the file name)
+func Test_cpo_f()
+  let save_cpo = &cpo
+  new
+  set cpo-=f
+  read test_cpoptions.vim
+  call assert_equal('', @%)
+  %d
+  set cpo+=f
+  read test_cpoptions.vim
+  call assert_equal('test_cpoptions.vim', @%)
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'F' flag in 'cpo' (write in an empty buffer sets the file name)
+func Test_cpo_F()
+  let save_cpo = &cpo
+  new
+  set cpo-=F
+  write Xfile
+  call assert_equal('', @%)
+  call delete('Xfile')
+  set cpo+=F
+  write Xfile
+  call assert_equal('Xfile', @%)
+  close!
+  call delete('Xfile')
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'g' flag in 'cpo' (jump to line 1 when re-editing a file)
+func Test_cpo_g()
+  throw 'Skipped: Nvim does not support cpoptions flag "g"'
+  let save_cpo = &cpo
+  new test_cpoptions.vim
+  set cpo-=g
+  normal 20G
+  edit
+  call assert_equal(20, line('.'))
+  set cpo+=g
+  edit
+  call assert_equal(1, line('.'))
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for inserting text in a line with only spaces ('H' flag in 'cpoptions')
+func Test_cpo_H()
+  throw 'Skipped: Nvim does not support cpoptions flag "H"'
+  let save_cpo = &cpo
+  new
+  set cpo-=H
+  call setline(1, '    ')
+  normal! Ia
+  call assert_equal('    a', getline(1))
+  set cpo+=H
+  call setline(1, '    ')
+  normal! Ia
+  call assert_equal('   a ', getline(1))
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'I' flag in 'cpo' (deleting autoindent when using arrow keys)
+func Test_cpo_I()
+  let save_cpo = &cpo
+  new
+  setlocal autoindent
+  set cpo+=I
+  exe "normal i    one\\"
+  call assert_equal('    ', getline(2))
+  set cpo-=I
+  %d
+  exe "normal i    one\\"
+  call assert_equal('', getline(2))
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'J' flag in 'cpo' (two spaces after a sentence)
+func Test_cpo_J()
+  let save_cpo = &cpo
+  new
+  set cpo-=J
+  call setline(1, 'one. two!  three? four."''  five.)]')
+  normal 0
+  for colnr in [6, 12, 19, 28, 34]
+    normal )
+    call assert_equal(colnr, col('.'))
+  endfor
+  for colnr in [28, 19, 12, 6, 1]
+    normal (
+    call assert_equal(colnr, col('.'))
+  endfor
+  set cpo+=J
+  normal 0
+  for colnr in [12, 28, 34]
+    normal )
+    call assert_equal(colnr, col('.'))
+  endfor
+  for colnr in [28, 12, 1]
+    normal (
+    call assert_equal(colnr, col('.'))
+  endfor
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" TODO: Add a test for 'k' in 'cpo'
+
+" TODO: Add a test for 'K' in 'cpo'
+
+" Test for the 'l' flag in 'cpo' (backslash in a [] range)
+func Test_cpo_l()
+  let save_cpo = &cpo
+  new
+  call setline(1, ['', "a\tc" .. '\t'])
+  set cpo-=l
+  exe 'normal gg/[\t]' .. "\"
+  call assert_equal([2, 8], [col('.'), virtcol('.')])
+  set cpo+=l
+  exe 'normal gg/[\t]' .. "\"
+  call assert_equal([4, 10], [col('.'), virtcol('.')])
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for inserting tab in virtual replace mode ('L' flag in 'cpoptions')
+func Test_cpo_L()
+  let save_cpo = &cpo
+  new
+  set cpo-=L
+  call setline(1, 'abcdefghijklmnopqr')
+  exe "normal 0gR\"
+  call assert_equal("\ijklmnopqr", getline(1))
+  set cpo+=L
+  set list
+  call setline(1, 'abcdefghijklmnopqr')
+  exe "normal 0gR\"
+  call assert_equal("\cdefghijklmnopqr", getline(1))
+  set nolist
+  call setline(1, 'abcdefghijklmnopqr')
+  exe "normal 0gR\"
+  call assert_equal("\ijklmnopqr", getline(1))
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" TODO: This test doesn't work.
+
+" Test for the 'M' flag in 'cpo' (% with escape parenthesis)
+func Test_cpo_M()
+  let save_cpo = &cpo
+  new
+  call setline(1, ['( \( )', '\( ( \)'])
+
+  set cpo-=M
+  call cursor(1, 1)
+  normal %
+  call assert_equal(6, col('.'))
+  call cursor(1, 4)
+  call assert_beeps('normal %')
+  call cursor(2, 2)
+  normal %
+  call assert_equal(7, col('.'))
+  call cursor(2, 4)
+  call assert_beeps('normal %')
+
+  set cpo+=M
+  call cursor(1, 4)
+  normal %
+  call assert_equal(6, col('.'))
+  call cursor(1, 1)
+  call assert_beeps('normal %')
+  call cursor(2, 4)
+  normal %
+  call assert_equal(7, col('.'))
+  call cursor(2, 1)
+  call assert_beeps('normal %')
+
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'n' flag in 'cpo' (using number column for wrapped lines)
+func Test_cpo_n()
+  let save_cpo = &cpo
+  new
+  call setline(1, repeat('a', &columns))
+  setlocal number
+  set cpo-=n
+  redraw!
+  call assert_equal('    aaaa', Screenline(2))
+  set cpo+=n
+  redraw!
+  call assert_equal('aaaa', Screenline(2))
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'o' flag in 'cpo' (line offset to search command)
+func Test_cpo_o()
+  let save_cpo = &cpo
+  new
+  call setline(1, ['', 'one', 'two', 'three', 'one', 'two', 'three'])
+  set cpo-=o
+  exe "normal /one/+2\"
+  normal n
+  call assert_equal(7, line('.'))
+  set cpo+=o
+  exe "normal /one/+2\"
+  normal n
+  call assert_equal(5, line('.'))
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'O' flag in 'cpo' (overwriting an existing file)
+func Test_cpo_O()
+  let save_cpo = &cpo
+  new Xfile
+  call setline(1, 'one')
+  call writefile(['two'], 'Xfile')
+  set cpo-=O
+  call assert_fails('write', 'E13:')
+  set cpo+=O
+  write
+  call assert_equal(['one'], readfile('Xfile'))
+  close!
+  call delete('Xfile')
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'P' flag in 'cpo' (appending to a file sets the current file
+" name)
+func Test_cpo_P()
+  let save_cpo = &cpo
+  call writefile([], 'Xfile')
+  new
+  call setline(1, 'one')
+  set cpo+=F
+  set cpo-=P
+  write >> Xfile
+  call assert_equal('', @%)
+  set cpo+=P
+  write >> Xfile
+  call assert_equal('Xfile', @%)
+  close!
+  call delete('Xfile')
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'q' flag in 'cpo' (joining multiple lines)
+func Test_cpo_q()
+  let save_cpo = &cpo
+  new
+  call setline(1, ['one', 'two', 'three', 'four', 'five'])
+  set cpo-=q
+  normal gg4J
+  call assert_equal(14, col('.'))
+  %d
+  call setline(1, ['one', 'two', 'three', 'four', 'five'])
+  set cpo+=q
+  normal gg4J
+  call assert_equal(4, col('.'))
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'r' flag in 'cpo' (redo command with a search motion)
+func Test_cpo_r()
+  let save_cpo = &cpo
+  new
+  call setline(1, repeat(['one two three four'], 2))
+  set cpo-=r
+  exe "normal ggc/two\abc "
+  let @/ = 'three'
+  normal 2G.
+  call assert_equal('abc two three four', getline(2))
+  %d
+  call setline(1, repeat(['one two three four'], 2))
+  set cpo+=r
+  exe "normal ggc/two\abc "
+  let @/ = 'three'
+  normal 2G.
+  call assert_equal('abc three four', getline(2))
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'R' flag in 'cpo' (clear marks after a filter command)
+func Test_cpo_R()
+  CheckUnix
+  let save_cpo = &cpo
+  new
+  call setline(1, ['three', 'one', 'two'])
+  set cpo-=R
+  3mark r
+  %!sort
+  call assert_equal(3, line("'r"))
+  %d
+  call setline(1, ['three', 'one', 'two'])
+  set cpo+=R
+  3mark r
+  %!sort
+  call assert_equal(0, line("'r"))
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'S' flag in 'cpo' (copying buffer options)
+func Test_cpo_S()
+  let save_cpo = &cpo
+  new Xfile1
+  set noautoindent
+  new Xfile2
+  set cpo-=S
+  set autoindent
+  wincmd p
+  call assert_equal(0, &autoindent)
+  wincmd p
+  call assert_equal(1, &autoindent)
+  set cpo+=S
+  wincmd p
+  call assert_equal(1, &autoindent)
+  set noautoindent
+  wincmd p
+  call assert_equal(0, &autoindent)
+  wincmd t
+  close!
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'u' flag in 'cpo' (Vi-compatible undo)
+func Test_cpo_u()
+  let save_cpo = &cpo
+  new
+  set cpo-=u
+  exe "normal iabc\udef\ughi"
+  normal uu
+  call assert_equal('abc', getline(1))
+  %d
+  set cpo+=u
+  exe "normal iabc\udef\ughi"
+  normal uu
+  call assert_equal('abcdefghi', getline(1))
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'x' flag in 'cpo' (Esc on command-line executes command)
+func Test_cpo_x()
+  let save_cpo = &cpo
+  set cpo-=x
+  let i = 1
+  call feedkeys(":let i=10\", 'xt')
+  call assert_equal(1, i)
+  set cpo+=x
+  call feedkeys(":let i=10\", 'xt')
+  call assert_equal(10, i)
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'X' flag in 'cpo' ('R' with a count)
+func Test_cpo_X()
+  let save_cpo = &cpo
+  new
+  call setline(1, 'aaaaaa')
+  set cpo-=X
+  normal gg4Rx
+  call assert_equal('xxxxaa', getline(1))
+  normal ggRy
+  normal 4.
+  call assert_equal('yyyyaa', getline(1))
+  call setline(1, 'aaaaaa')
+  set cpo+=X
+  normal gg4Rx
+  call assert_equal('xxxxaaaaa', getline(1))
+  normal ggRy
+  normal 4.
+  call assert_equal('yyyyxxxaaaaa', getline(1))
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'y' flag in 'cpo' (repeating a yank command)
+func Test_cpo_y()
+  let save_cpo = &cpo
+  new
+  call setline(1, ['one', 'two'])
+  set cpo-=y
+  normal ggyy
+  normal 2G.
+  call assert_equal("one\n", @")
+  %d
+  call setline(1, ['one', 'two'])
+  set cpo+=y
+  normal ggyy
+  normal 2G.
+  call assert_equal("two\n", @")
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'Z' flag in 'cpo' (write! resets 'readonly')
+func Test_cpo_Z()
+  let save_cpo = &cpo
+  call writefile([], 'Xfile')
+  new Xfile
+  setlocal readonly
+  set cpo-=Z
+  write!
+  call assert_equal(0, &readonly)
+  set cpo+=Z
+  setlocal readonly
+  write!
+  call assert_equal(1, &readonly)
+  close!
+  call delete('Xfile')
+  let &cpo = save_cpo
+endfunc
+
+" Test for cursor movement with '-' in 'cpoptions'
+func Test_cpo_minus()
+  throw 'Skipped: Nvim does not support cpoptions flag "-"'
+  new
+  call setline(1, ['foo', 'bar', 'baz'])
+  let save_cpo = &cpo
+  set cpo+=-
+  call assert_beeps('normal 10j')
+  call assert_equal(1, line('.'))
+  normal G
+  call assert_beeps('normal 10k')
+  call assert_equal(3, line('.'))
+  call assert_fails(10, 'E16:')
+  let &cpo = save_cpo
+  close!
+endfunc
+
+" Test for displaying dollar when changing text ('$' flag in 'cpoptions')
+func Test_cpo_dollar()
+  throw 'Skipped: use test/functional/legacy/cpoptions_spec.lua'
+  new
+  let g:Line = ''
+  func SaveFirstLine()
+    let g:Line = Screenline(1)
+    return ''
+  endfunc
+  inoremap    SaveFirstLine()
+  call test_override('redraw_flag', 1)
+  set cpo+=$
+  call setline(1, 'one two three')
+  redraw!
+  exe "normal c2w\vim"
+  call assert_equal('one tw$ three', g:Line)
+  call assert_equal('vim three', getline(1))
+  set cpo-=$
+  call test_override('ALL', 0)
+  delfunc SaveFirstLine
+  %bw!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index 9783ed19a7..d19826f5b9 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -1724,40 +1724,6 @@ func Test_edit_illegal_filename()
   close!
 endfunc
 
-" Test for inserting text in a line with only spaces ('H' flag in 'cpoptions')
-func Test_edit_cpo_H()
-  throw 'Skipped: Nvim does not support cpoptions flag "H"'
-  new
-  call setline(1, '    ')
-  normal! Ia
-  call assert_equal('    a', getline(1))
-  set cpo+=H
-  call setline(1, '    ')
-  normal! Ia
-  call assert_equal('   a ', getline(1))
-  set cpo-=H
-  close!
-endfunc
-
-" Test for inserting tab in virtual replace mode ('L' flag in 'cpoptions')
-func Test_edit_cpo_L()
-  new
-  call setline(1, 'abcdefghijklmnopqr')
-  exe "normal 0gR\"
-  call assert_equal("\ijklmnopqr", getline(1))
-  set cpo+=L
-  set list
-  call setline(1, 'abcdefghijklmnopqr')
-  exe "normal 0gR\"
-  call assert_equal("\cdefghijklmnopqr", getline(1))
-  set nolist
-  call setline(1, 'abcdefghijklmnopqr')
-  exe "normal 0gR\"
-  call assert_equal("\ijklmnopqr", getline(1))
-  set cpo-=L
-  %bw!
-endfunc
-
 " Test for editing a directory
 func Test_edit_is_a_directory()
   CheckEnglish
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index 32bb755584..3c9a7a3986 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -2931,20 +2931,6 @@ func Test_normal52_rl()
   bw!
 endfunc
 
-func Test_normal53_digraph()
-  CheckFeature digraphs
-  new
-  call setline(1, 'abcdefgh|')
-  exe "norm! 1gg0f\!!"
-  call assert_equal(9, col('.'))
-  set cpo+=D
-  exe "norm! 1gg0f\!!"
-  call assert_equal(1, col('.'))
-
-  set cpo-=D
-  bw!
-endfunc
-
 func Test_normal54_Ctrl_bsl()
   new
   call setline(1, 'abcdefghijklmn')
@@ -3265,46 +3251,6 @@ func Test_normal_gk_gj()
   set cpoptions& number& numberwidth& wrap&
 endfunc
 
-" Test for cursor movement with '-' in 'cpoptions'
-func Test_normal_cpo_minus()
-  throw 'Skipped: Nvim does not support cpoptions flag "-"'
-  new
-  call setline(1, ['foo', 'bar', 'baz'])
-  let save_cpo = &cpo
-  set cpo+=-
-  call assert_beeps('normal 10j')
-  call assert_equal(1, line('.'))
-  normal G
-  call assert_beeps('normal 10k')
-  call assert_equal(3, line('.'))
-  call assert_fails(10, 'E16:')
-  let &cpo = save_cpo
-  close!
-endfunc
-
-" Test for displaying dollar when changing text ('$' flag in 'cpoptions')
-func Test_normal_cpo_dollar()
-  throw 'Skipped: use test/functional/legacy/cpoptions_spec.lua'
-  new
-  let g:Line = ''
-  func SaveFirstLine()
-    let g:Line = Screenline(1)
-    return ''
-  endfunc
-  inoremap    SaveFirstLine()
-  call test_override('redraw_flag', 1)
-  set cpo+=$
-  call setline(1, 'one two three')
-  redraw!
-  exe "normal c2w\vim"
-  call assert_equal('one tw$ three', g:Line)
-  call assert_equal('vim three', getline(1))
-  set cpo-=$
-  call test_override('ALL', 0)
-  delfunc SaveFirstLine
-  %bw!
-endfunc
-
 " Test for using : to run a multi-line Ex command in operator pending mode
 func Test_normal_yank_with_excmd()
   new
@@ -3387,31 +3333,6 @@ func Test_normal_delete_cmd()
   close!
 endfunc
 
-" Test for the 'E' flag in 'cpo' with yank, change, delete, etc. operators
-func Test_empty_region_error()
-  new
-  call setline(1, '')
-  set cpo+=E
-  " yank an empty line
-  call assert_beeps('normal "ayl')
-  " change an empty line
-  call assert_beeps('normal lcTa')
-  " delete an empty line
-  call assert_beeps('normal D')
-  call assert_beeps('normal dl')
-  call assert_equal('', getline(1))
-  " change case of an empty line
-  call assert_beeps('normal gul')
-  call assert_beeps('normal gUl')
-  " replace a character
-  call assert_beeps('normal vrx')
-  " increment and decrement
-  call assert_beeps('exe "normal v\"')
-  call assert_beeps('exe "normal v\"')
-  set cpo-=E
-  close!
-endfunc
-
 " Test for deleting or changing characters across lines with 'whichwrap'
 " containing 's'. Should count  as one character.
 func Test_normal_op_across_lines()
-- 
cgit 


From 2b86ca81b400b1311379f3258cc6c38be94fcf36 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 4 Nov 2022 19:51:54 +0800
Subject: vim-patch:8.2.0976: some 'cpoptions' not tested

Problem:    Some 'cpoptions' not tested.
Solution:   Add more tests. (Yegappan Lakshmanan, closes vim/vim#6253)

https://github.com/vim/vim/commit/df7df59d85e7e56a796912dc865488a75d3f0e53
---
 src/nvim/testdir/test_cd.vim         |  24 ---
 src/nvim/testdir/test_charsearch.vim |  30 ----
 src/nvim/testdir/test_cpoptions.vim  | 327 ++++++++++++++++++++++++++++++++---
 src/nvim/testdir/test_normal.vim     |   9 -
 4 files changed, 307 insertions(+), 83 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_cd.vim b/src/nvim/testdir/test_cd.vim
index d6d44d1901..43c4e09d40 100644
--- a/src/nvim/testdir/test_cd.vim
+++ b/src/nvim/testdir/test_cd.vim
@@ -68,30 +68,6 @@ func Test_cd_minus()
   call delete('Xresult')
 endfunc
 
-func Test_cd_with_cpo_chdir()
-  e Xfoo
-  call setline(1, 'foo')
-  let path = getcwd()
-  " set cpo+=.
-
-  " :cd should fail when buffer is modified and 'cpo' contains dot.
-  " call assert_fails('cd ..', 'E747:')
-  call assert_equal(path, getcwd())
-
-  " :cd with exclamation mark should succeed.
-  cd! ..
-  call assert_notequal(path, getcwd())
-
-  " :cd should succeed when buffer has been written.
-  w!
-  exe 'cd ' .. fnameescape(path)
-  call assert_equal(path, getcwd())
-
-  call delete('Xfoo')
-  set cpo&
-  bw!
-endfunc
-
 " Test for chdir()
 func Test_chdir_func()
   let topdir = getcwd()
diff --git a/src/nvim/testdir/test_charsearch.vim b/src/nvim/testdir/test_charsearch.vim
index d386d74f8d..4e6a75c3a0 100644
--- a/src/nvim/testdir/test_charsearch.vim
+++ b/src/nvim/testdir/test_charsearch.vim
@@ -43,36 +43,6 @@ func Test_charsearch()
   enew!
 endfunc
 
-" Test for t,f,F,T movement commands and 'cpo-;' setting
-func Test_search_cmds()
-  enew!
-  call append(0, ["aaa two three four", "    zzz", "yyy   ",
-	      \ "bbb yee yoo four", "ccc two three four",
-	      \ "ddd yee yoo four"])
-  set cpo-=;
-  1
-  normal! 0tt;D
-  2
-  normal! 0fz;D
-  3
-  normal! $Fy;D
-  4
-  normal! $Ty;D
-  set cpo+=;
-  5
-  normal! 0tt;;D
-  6
-  normal! $Ty;;D
-
-  call assert_equal('aaa two', getline(1))
-  call assert_equal('    z', getline(2))
-  call assert_equal('y', getline(3))
-  call assert_equal('bbb y', getline(4))
-  call assert_equal('ccc', getline(5))
-  call assert_equal('ddd yee y', getline(6))
-  enew!
-endfunc
-
 " Test for character search in virtual edit mode with 
 func Test_csearch_virtualedit()
   new
diff --git a/src/nvim/testdir/test_cpoptions.vim b/src/nvim/testdir/test_cpoptions.vim
index ceaa728018..f0732934d4 100644
--- a/src/nvim/testdir/test_cpoptions.vim
+++ b/src/nvim/testdir/test_cpoptions.vim
@@ -1,6 +1,7 @@
-" Test for various 'cpoptions' (cpo) flags
+" Test for the various 'cpoptions' (cpo) flags
 
 source check.vim
+source shared.vim
 source view_util.vim
 
 " Test for the 'a' flag in 'cpo'. Reading a file should set the alternate
@@ -62,6 +63,24 @@ func Test_cpo_b()
   nunmap 
 endfunc
 
+" Test for the 'B' flag in 'cpo'. A backslash in mappings, abbreviations, user
+" commands and menu commands has no special meaning.
+func Test_cpo_B()
+  let save_cpo = &cpo
+  new
+  set cpo-=B
+  iabbr  abc ab\d
+  exe "normal iabc "
+  call assert_equal('abd ', getline(1))
+  %d
+  set cpo+=B
+  iabbr  abc ab\d
+  exe "normal iabc "
+  call assert_equal('abd ', getline(1))
+  close!
+  let &cpo = save_cpo
+endfunc
+
 " Test for the 'c' flag in 'cpo'.
 func Test_cpo_c()
   let save_cpo = &cpo
@@ -228,6 +247,8 @@ func Test_cpo_H()
   let &cpo = save_cpo
 endfunc
 
+" TODO: Add a test for the 'i' flag in 'cpo'
+
 " Test for the 'I' flag in 'cpo' (deleting autoindent when using arrow keys)
 func Test_cpo_I()
   let save_cpo = &cpo
@@ -244,6 +265,8 @@ func Test_cpo_I()
   let &cpo = save_cpo
 endfunc
 
+" Test for the 'j' flag in 'cpo' is in the test_join.vim file.
+
 " Test for the 'J' flag in 'cpo' (two spaces after a sentence)
 func Test_cpo_J()
   let save_cpo = &cpo
@@ -273,9 +296,9 @@ func Test_cpo_J()
   let &cpo = save_cpo
 endfunc
 
-" TODO: Add a test for 'k' in 'cpo'
+" TODO: Add a test for the 'k' flag in 'cpo'
 
-" TODO: Add a test for 'K' in 'cpo'
+" TODO: Add a test for the 'K' flag in 'cpo'
 
 " Test for the 'l' flag in 'cpo' (backslash in a [] range)
 func Test_cpo_l()
@@ -313,7 +336,7 @@ func Test_cpo_L()
   let &cpo = save_cpo
 endfunc
 
-" TODO: This test doesn't work.
+" TODO: Add a test for the 'm' flag in 'cpo'
 
 " Test for the 'M' flag in 'cpo' (% with escape parenthesis)
 func Test_cpo_M()
@@ -398,6 +421,8 @@ func Test_cpo_O()
   let &cpo = save_cpo
 endfunc
 
+" Test for the 'p' flag in 'cpo' is in the test_lispwords.vim file.
+
 " Test for the 'P' flag in 'cpo' (appending to a file sets the current file
 " name)
 func Test_cpo_P()
@@ -475,6 +500,8 @@ func Test_cpo_R()
   let &cpo = save_cpo
 endfunc
 
+" TODO: Add a test for the 's' flag in 'cpo'
+
 " Test for the 'S' flag in 'cpo' (copying buffer options)
 func Test_cpo_S()
   let save_cpo = &cpo
@@ -499,6 +526,8 @@ func Test_cpo_S()
   let &cpo = save_cpo
 endfunc
 
+" Test for the 't' flag in 'cpo' is in the test_tagjump.vim file.
+
 " Test for the 'u' flag in 'cpo' (Vi-compatible undo)
 func Test_cpo_u()
   let save_cpo = &cpo
@@ -516,6 +545,33 @@ func Test_cpo_u()
   let &cpo = save_cpo
 endfunc
 
+" TODO: Add a test for the 'v' flag in 'cpo' (backspace doesn't remove
+" characters from the screen)
+
+" Test for the 'w' flag in 'cpo' ('cw' on a blank character changes only one
+" character)
+func Test_cpo_w()
+  throw 'Skipped: Nvim does not support cpoptions flag "w"'
+  let save_cpo = &cpo
+  new
+  set cpo+=w
+  call setline(1, 'here      are   some words')
+  norm! 1gg0elcwZZZ
+  call assert_equal('hereZZZ     are   some words', getline('.'))
+  norm! 1gg2elcWYYY
+  call assert_equal('hereZZZ     areYYY  some words', getline('.'))
+  set cpo-=w
+  call setline(1, 'here      are   some words')
+  norm! 1gg0elcwZZZ
+  call assert_equal('hereZZZare   some words', getline('.'))
+  norm! 1gg2elcWYYY
+  call assert_equal('hereZZZare   someYYYwords', getline('.'))
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the 'W' flag in 'cpo' is in the test_writefile.vim file
+
 " Test for the 'x' flag in 'cpo' (Esc on command-line executes command)
 func Test_cpo_x()
   let save_cpo = &cpo
@@ -588,22 +644,7 @@ func Test_cpo_Z()
   let &cpo = save_cpo
 endfunc
 
-" Test for cursor movement with '-' in 'cpoptions'
-func Test_cpo_minus()
-  throw 'Skipped: Nvim does not support cpoptions flag "-"'
-  new
-  call setline(1, ['foo', 'bar', 'baz'])
-  let save_cpo = &cpo
-  set cpo+=-
-  call assert_beeps('normal 10j')
-  call assert_equal(1, line('.'))
-  normal G
-  call assert_beeps('normal 10k')
-  call assert_equal(3, line('.'))
-  call assert_fails(10, 'E16:')
-  let &cpo = save_cpo
-  close!
-endfunc
+" Test for the '!' flag in 'cpo' is in the test_normal.vim file
 
 " Test for displaying dollar when changing text ('$' flag in 'cpoptions')
 func Test_cpo_dollar()
@@ -628,4 +669,250 @@ func Test_cpo_dollar()
   %bw!
 endfunc
 
+" Test for the '%' flag in 'cpo' (parenthesis matching inside strings)
+func Test_cpo_percent()
+  let save_cpo = &cpo
+  new
+  call setline(1, '    if (strcmp("ab)cd(", s))')
+  set cpo-=%
+  normal 8|%
+  call assert_equal(28, col('.'))
+  normal 15|%
+  call assert_equal(27, col('.'))
+  normal 27|%
+  call assert_equal(15, col('.'))
+  call assert_beeps("normal 19|%")
+  call assert_beeps("normal 22|%")
+  set cpo+=%
+  normal 8|%
+  call assert_equal(28, col('.'))
+  normal 15|%
+  call assert_equal(19, col('.'))
+  normal 27|%
+  call assert_equal(22, col('.'))
+  normal 19|%
+  call assert_equal(15, col('.'))
+  normal 22|%
+  call assert_equal(27, col('.'))
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for cursor movement with '-' in 'cpoptions'
+func Test_cpo_minus()
+  throw 'Skipped: Nvim does not support cpoptions flag "-"'
+  new
+  call setline(1, ['foo', 'bar', 'baz'])
+  let save_cpo = &cpo
+  set cpo+=-
+  call assert_beeps('normal 10j')
+  call assert_equal(1, line('.'))
+  normal G
+  call assert_beeps('normal 10k')
+  call assert_equal(3, line('.'))
+  call assert_fails(10, 'E16:')
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the '+' flag in 'cpo' ('write file' command resets the 'modified'
+" flag)
+func Test_cpo_plus()
+  let save_cpo = &cpo
+  call writefile([], 'Xfile')
+  new Xfile
+  call setline(1, 'foo')
+  write X1
+  call assert_equal(1, &modified)
+  set cpo+=+
+  write X2
+  call assert_equal(0, &modified)
+  close!
+  call delete('Xfile')
+  call delete('X1')
+  call delete('X2')
+  let &cpo = save_cpo
+endfunc
+
+" Test for the '*' flag in 'cpo' (':*' is same as ':@')
+func Test_cpo_star()
+  throw 'Skipped: Nvim does not support cpoptions flag "*"'
+  let save_cpo = &cpo
+  let x = 0
+  new
+  set cpo-=*
+  let @a = 'let x += 1'
+  call assert_fails('*a', 'E20:')
+  set cpo+=*
+  *a
+  call assert_equal(1, x)
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the '<' flag in 'cpo' is in the test_mapping.vim file
+
+" Test for the '>' flag in 'cpo' (use a new line when appending to a register)
+func Test_cpo_gt()
+  let save_cpo = &cpo
+  new
+  call setline(1, 'one two')
+  set cpo-=>
+  let @r = ''
+  normal gg"Rye
+  normal "Rye
+  call assert_equal("oneone", @r)
+  set cpo+=>
+  let @r = ''
+  normal gg"Rye
+  normal "Rye
+  call assert_equal("\none\none", @r)
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the ';' flag in 'cpo'
+" Test for t,f,F,T movement commands and 'cpo-;' setting
+func Test_cpo_semicolon()
+  let save_cpo = &cpo
+  new
+  call append(0, ["aaa two three four", "    zzz", "yyy   ",
+	      \ "bbb yee yoo four", "ccc two three four",
+	      \ "ddd yee yoo four"])
+  set cpo-=;
+  1
+  normal! 0tt;D
+  2
+  normal! 0fz;D
+  3
+  normal! $Fy;D
+  4
+  normal! $Ty;D
+  set cpo+=;
+  5
+  normal! 0tt;;D
+  6
+  normal! $Ty;;D
+
+  call assert_equal('aaa two', getline(1))
+  call assert_equal('    z', getline(2))
+  call assert_equal('y', getline(3))
+  call assert_equal('bbb y', getline(4))
+  call assert_equal('ccc', getline(5))
+  call assert_equal('ddd yee y', getline(6))
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the '#' flag in 'cpo' (count before 'D', 'o' and 'O' operators)
+func Test_cpo_hash()
+  throw 'Skipped: Nvim does not support cpoptions flag "#"'
+  let save_cpo = &cpo
+  new
+  set cpo-=#
+  call setline(1, ['one', 'two', 'three'])
+  normal gg2D
+  call assert_equal(['three'], getline(1, '$'))
+  normal gg2ofour
+  call assert_equal(['three', 'four', 'four'], getline(1, '$'))
+  normal gg2Otwo
+  call assert_equal(['two', 'two', 'three', 'four', 'four'], getline(1, '$'))
+  %d
+  set cpo+=#
+  call setline(1, ['one', 'two', 'three'])
+  normal gg2D
+  call assert_equal(['', 'two', 'three'], getline(1, '$'))
+  normal gg2oone
+  call assert_equal(['', 'one', 'two', 'three'], getline(1, '$'))
+  normal gg2Ozero
+  call assert_equal(['zero', '', 'one', 'two', 'three'], getline(1, '$'))
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the '&' flag in 'cpo'. The swap file is kept when a buffer is still
+" loaded and ':preserve' is used.
+func Test_cpo_ampersand()
+  throw 'Skipped: Nvim does not support cpoptions flag "&"'
+  call writefile(['one'], 'Xfile')
+  let after =<< trim [CODE]
+    set cpo+=&
+    preserve
+    qall
+  [CODE]
+  if RunVim([], after, 'Xfile')
+    call assert_equal(1, filereadable('.Xfile.swp'))
+    call delete('.Xfile.swp')
+  endif
+  call delete('Xfile')
+endfunc
+
+" Test for the '\' flag in 'cpo' (backslash in a [] range in a search pattern)
+func Test_cpo_backslash()
+  throw 'Skipped: Nvim does not support cpoptions flag "\"'
+  let save_cpo = &cpo
+  new
+  call setline(1, ['', " \\-string"])
+  set cpo-=\
+  exe 'normal gg/[ \-]' .. "\n"
+  call assert_equal(3, col('.'))
+  set cpo+=\
+  exe 'normal gg/[ \-]' .. "\n"
+  call assert_equal(2, col('.'))
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the '/' flag in 'cpo' is in the test_substitute.vim file
+
+" Test for the '{' flag in 'cpo' (the "{" and "}" commands stop at a {
+" character at the start of a line)
+func Test_cpo_brace()
+  throw 'Skipped: Nvim does not support cpoptions flag "{"'
+  let save_cpo = &cpo
+  new
+  call setline(1, ['', '{', '    int i;', '}', ''])
+  set cpo-={
+  normal gg}
+  call assert_equal(5, line('.'))
+  normal G{
+  call assert_equal(1, line('.'))
+  set cpo+={
+  normal gg}
+  call assert_equal(2, line('.'))
+  normal G{
+  call assert_equal(2, line('.'))
+  close!
+  let &cpo = save_cpo
+endfunc
+
+" Test for the '.' flag in 'cpo' (:cd command fails if the current buffer is
+" modified)
+func Test_cpo_dot()
+  throw 'Skipped: Nvim does not support cpoptions flag "."'
+  let save_cpo = &cpo
+  new Xfoo
+  call setline(1, 'foo')
+  let save_dir = getcwd()
+  set cpo+=.
+
+  " :cd should fail when buffer is modified and 'cpo' contains dot.
+  call assert_fails('cd ..', 'E747:')
+  call assert_equal(save_dir, getcwd())
+
+  " :cd with exclamation mark should succeed.
+  cd! ..
+  call assert_notequal(save_dir, getcwd())
+
+  " :cd should succeed when buffer has been written.
+  w!
+  exe 'cd ' .. fnameescape(save_dir)
+  call assert_equal(save_dir, getcwd())
+
+  call delete('Xfoo')
+  set cpo&
+  close!
+  let &cpo = save_cpo
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index 3c9a7a3986..04faeca455 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -2644,7 +2644,6 @@ endfunc
 " Test for cw cW ce
 func Test_normal39_cw()
   " Test for cw and cW on whitespace
-  " and cpo+=w setting
   new
   set tw=0
   call append(0, 'here      are   some words')
@@ -2652,14 +2651,6 @@ func Test_normal39_cw()
   call assert_equal('hereZZZare   some words', getline('.'))
   norm! 1gg0elcWYYY
   call assert_equal('hereZZZareYYYsome words', getline('.'))
-  " Nvim: no "w" flag in 'cpoptions'.
-  " set cpo+=w
-  " call setline(1, 'here      are   some words')
-  " norm! 1gg0elcwZZZ
-  " call assert_equal('hereZZZ     are   some words', getline('.'))
-  " norm! 1gg2elcWYYY
-  " call assert_equal('hereZZZ     areYYY  some words', getline('.'))
-  set cpo-=w
   norm! 2gg0cwfoo
   call assert_equal('foo', getline('.'))
 
-- 
cgit 


From 2476f41a4a4dcf940bce9ea9ae48a6017a35fbc2 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 4 Nov 2022 19:49:23 +0800
Subject: vim-patch:8.2.1022: various parts of code not covered by tests

Problem:    Various parts of code not covered by tests.
Solution:   Add more tests. (Yegappan Lakshmanan, closes vim/vim#6300)

https://github.com/vim/vim/commit/845e0ee59430eac07e74b6cb92020e420d17953d

Omit test_iminsert.vim: the commit that created this file was N/A.
Omit test_viminfo.vim: the added tests are N/A.
---
 src/nvim/testdir/test_blob.vim        |   1 +
 src/nvim/testdir/test_cpoptions.vim   |  20 ++++--
 src/nvim/testdir/test_edit.vim        | 112 +++++++++++++++++++++++++++++++++-
 src/nvim/testdir/test_selectmode.vim  |   3 +
 src/nvim/testdir/test_tabpage.vim     |  11 ++++
 src/nvim/testdir/test_tagjump.vim     |  55 ++++++++++++++++-
 src/nvim/testdir/test_textformat.vim  |  14 +++++
 src/nvim/testdir/test_virtualedit.vim |  18 ++++++
 src/nvim/testdir/test_visual.vim      |   9 +++
 9 files changed, 233 insertions(+), 10 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_blob.vim b/src/nvim/testdir/test_blob.vim
index 151de71312..1c0261933f 100644
--- a/src/nvim/testdir/test_blob.vim
+++ b/src/nvim/testdir/test_blob.vim
@@ -252,6 +252,7 @@ func Test_blob_func_remove()
   call assert_fails("call remove(b, 3, 2)", 'E979:')
   call assert_fails("call remove(1, 0)", 'E896:')
   call assert_fails("call remove(b, b)", 'E974:')
+  call assert_fails("call remove(b, 1, [])", 'E745:')
   call assert_fails("call remove(v:_null_blob, 1, 2)", 'E979:')
 
   " Translated from v8.2.3284
diff --git a/src/nvim/testdir/test_cpoptions.vim b/src/nvim/testdir/test_cpoptions.vim
index f0732934d4..cc281ef521 100644
--- a/src/nvim/testdir/test_cpoptions.vim
+++ b/src/nvim/testdir/test_cpoptions.vim
@@ -248,6 +248,7 @@ func Test_cpo_H()
 endfunc
 
 " TODO: Add a test for the 'i' flag in 'cpo'
+" Interrupting the reading of a file will leave it modified.
 
 " Test for the 'I' flag in 'cpo' (deleting autoindent when using arrow keys)
 func Test_cpo_I()
@@ -296,9 +297,12 @@ func Test_cpo_J()
   let &cpo = save_cpo
 endfunc
 
-" TODO: Add a test for the 'k' flag in 'cpo'
+" TODO: Add a test for the 'k' flag in 'cpo'.
+" Disable the recognition of raw key codes in mappings, abbreviations, and the
+" "to" part of menu commands.
 
-" TODO: Add a test for the 'K' flag in 'cpo'
+" TODO: Add a test for the 'K' flag in 'cpo'.
+" Don't wait for a key code to complete when it is halfway a mapping.
 
 " Test for the 'l' flag in 'cpo' (backslash in a [] range)
 func Test_cpo_l()
@@ -336,7 +340,9 @@ func Test_cpo_L()
   let &cpo = save_cpo
 endfunc
 
-" TODO: Add a test for the 'm' flag in 'cpo'
+" TODO: Add a test for the 'm' flag in 'cpo'.
+" When included, a showmatch will always wait half a second.  When not
+" included, a showmatch will wait half a second or until a character is typed.
 
 " Test for the 'M' flag in 'cpo' (% with escape parenthesis)
 func Test_cpo_M()
@@ -500,7 +506,9 @@ func Test_cpo_R()
   let &cpo = save_cpo
 endfunc
 
-" TODO: Add a test for the 's' flag in 'cpo'
+" TODO: Add a test for the 's' flag in 'cpo'.
+" Set buffer options when entering the buffer for the first time.  If not
+" present the options are set when the buffer is created.
 
 " Test for the 'S' flag in 'cpo' (copying buffer options)
 func Test_cpo_S()
@@ -545,8 +553,8 @@ func Test_cpo_u()
   let &cpo = save_cpo
 endfunc
 
-" TODO: Add a test for the 'v' flag in 'cpo' (backspace doesn't remove
-" characters from the screen)
+" TODO: Add a test for the 'v' flag in 'cpo'.
+" Backspaced characters remain visible on the screen in Insert mode.
 
 " Test for the 'w' flag in 'cpo' ('cw' on a blank character changes only one
 " character)
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index d19826f5b9..89fd73351d 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -1433,9 +1433,7 @@ endfunc
 
 func Test_edit_rightleft()
   " Cursor in rightleft mode moves differently
-  if !exists("+rightleft")
-    return
-  endif
+  CheckFeature rightleft
   call NewWindow(10, 20)
   call setline(1, ['abc', 'def', 'ghi'])
   call cursor(1, 2)
@@ -1480,6 +1478,13 @@ func Test_edit_rightleft()
         \"                 ihg",
         \"                   ~"]
   call assert_equal(join(expect, "\n"), join(lines, "\n"))
+  %d _
+  " call test_override('redraw_flag', 1)
+  " call test_override('char_avail', 1)
+  call feedkeys("a\x41", "xt")
+  redraw!
+  call assert_equal(repeat(' ', 19) .. 'A', Screenline(1))
+  " call test_override('ALL', 0)
   set norightleft
   bw!
 endfunc
@@ -1868,6 +1873,107 @@ func Test_edit_insertmode_ex_edit()
   call delete('Xtest_edit_insertmode_ex_edit')
 endfunc
 
+" Pressing escape in 'insertmode' should beep
+func Test_edit_insertmode_esc_beeps()
+  throw "Skipped: Nvim does not support 'insertmode'"
+  new
+  set insertmode
+  call assert_beeps("call feedkeys(\"one\\", 'xt')")
+  set insertmode&
+  " unsupported CTRL-G command should beep in insert mode.
+  call assert_beeps("normal i\l")
+  close!
+endfunc
+
+" Test for 'hkmap' and 'hkmapp'
+func Test_edit_hkmap()
+  CheckFeature rightleft
+  if has('win32') && !has('gui')
+    " Test fails on the MS-Windows terminal version
+    return
+  endif
+  new
+
+  set revins hkmap
+  let str = 'abcdefghijklmnopqrstuvwxyz'
+  let str ..= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+  let str ..= '`/'',.;'
+  call feedkeys('i' .. str, 'xt')
+  let expected = "óõú,.;"
+  let expected ..= "ZYXWVUTSRQPONMLKJIHGFEDCBA"
+  let expected ..= "æèñ'äåàãø/ôíîöêìçïéòë÷âáðù"
+  call assert_equal(expected, getline(1))
+
+  %d
+  set revins hkmap hkmapp
+  let str = 'abcdefghijklmnopqrstuvwxyz'
+  let str ..= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+  call feedkeys('i' .. str, 'xt')
+  let expected = "õYXWVUTSRQóOïíLKJIHGFEDêBA"
+  let expected ..= "öòXùåèúæø'ôñðîì÷çéäâóǟãëáà"
+  call assert_equal(expected, getline(1))
+
+  set revins& hkmap& hkmapp&
+  close!
+endfunc
+
+" Test for 'allowrevins' and using CTRL-_ in insert mode
+func Test_edit_allowrevins()
+  CheckFeature rightleft
+  new
+  set allowrevins
+  call feedkeys("iABC\DEF\GHI", 'xt')
+  call assert_equal('ABCFEDGHI', getline(1))
+  set allowrevins&
+  close!
+endfunc
+
+" Test for inserting a register in insert mode using CTRL-R
+func Test_edit_insert_reg()
+  throw 'Skipped: use test/functional/legacy/edit_spec.lua'
+  new
+  let g:Line = ''
+  func SaveFirstLine()
+    let g:Line = Screenline(1)
+    return 'r'
+  endfunc
+  inoremap    SaveFirstLine()
+  call test_override('redraw_flag', 1)
+  call test_override('char_avail', 1)
+  let @r = 'sample'
+  call feedkeys("a\=SaveFirstLine()\", "xt")
+  call assert_equal('"', g:Line)
+  call test_override('ALL', 0)
+  close!
+endfunc
+
+" When a character is inserted at the last position of the last line in a
+" window, the window contents should be scrolled one line up. If the top line
+" is part of a fold, then the entire fold should be scrolled up.
+func Test_edit_lastline_scroll()
+  new
+  let h = winheight(0)
+  let lines = ['one', 'two', 'three']
+  let lines += repeat(['vim'], h - 4)
+  call setline(1, lines)
+  call setline(h, repeat('x', winwidth(0) - 1))
+  call feedkeys("GAx", 'xt')
+  redraw!
+  call assert_equal(h - 1, winline())
+  call assert_equal(2, line('w0'))
+
+  " scroll with a fold
+  1,2fold
+  normal gg
+  call setline(h + 1, repeat('x', winwidth(0) - 1))
+  call feedkeys("GAx", 'xt')
+  redraw!
+  call assert_equal(h - 1, winline())
+  call assert_equal(3, line('w0'))
+
+  close!
+endfunc
+
 func Test_edit_browse()
   " in the GUI this opens a file picker, we only test the terminal behavior
   CheckNotGui
diff --git a/src/nvim/testdir/test_selectmode.vim b/src/nvim/testdir/test_selectmode.vim
index f2cab45450..041f0592f1 100644
--- a/src/nvim/testdir/test_selectmode.vim
+++ b/src/nvim/testdir/test_selectmode.vim
@@ -34,6 +34,9 @@ func Test_selectmode_start()
   set selectmode=cmd
   call feedkeys('gvabc', 'xt')
   call assert_equal('abctdef', getline(1))
+  " arrow keys without shift should not start selection
+  call feedkeys("A\\\ro", 'xt')
+  call assert_equal('roabctdef', getline(1))
   set selectmode= keymodel=
   bw!
 endfunc
diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim
index 6d468ec9de..4ada48d56c 100644
--- a/src/nvim/testdir/test_tabpage.vim
+++ b/src/nvim/testdir/test_tabpage.vim
@@ -623,6 +623,17 @@ func Test_tabpage_close_cmdwin()
   tabonly
 endfunc
 
+" Pressing  in insert mode should go to the previous tab page
+" and  should go to the next tab page
+func Test_tabpage_Ctrl_Pageup()
+  tabnew
+  call feedkeys("i\", 'xt')
+  call assert_equal(1, tabpagenr())
+  call feedkeys("i\", 'xt')
+  call assert_equal(2, tabpagenr())
+  %bw!
+endfunc
+
 " Return the terminal key code for selecting a tab page from the tabline. This
 " sequence contains the following codes: a CSI (0x9b), KS_TABLINE (0xf0),
 " KS_FILLER (0x58) and then the tab page number.
diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim
index 04c0218f74..592e13e340 100644
--- a/src/nvim/testdir/test_tagjump.vim
+++ b/src/nvim/testdir/test_tagjump.vim
@@ -282,6 +282,7 @@ func Test_tag_file_encoding()
   call delete('Xtags1')
 endfunc
 
+" Test for emacs-style tags file (TAGS)
 func Test_tagjump_etags()
   if !has('emacs_tags')
     return
@@ -1099,7 +1100,7 @@ func Test_tselect_listing()
   call writefile([
         \ "!_TAG_FILE_ENCODING\tutf-8\t//",
         \ "first\tXfoo\t1" .. ';"' .. "\tv\ttyperef:typename:int\tfile:",
-        \ "first\tXfoo\t2" .. ';"' .. "\tv\ttyperef:typename:char\tfile:"],
+        \ "first\tXfoo\t2" .. ';"' .. "\tkind:v\ttyperef:typename:char\tfile:"],
         \ 'Xtags')
   set tags=Xtags
 
@@ -1422,4 +1423,56 @@ func Test_tag_length()
   set tags& taglength&
 endfunc
 
+" Tests for errors in a tags file
+func Test_tagfile_errors()
+  set tags=Xtags
+
+  " missing search pattern or line number for a tag
+  call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+        \ "foo\tXfile\t"], 'Xtags', 'b')
+  call writefile(['foo'], 'Xfile')
+
+  enew
+  tag foo
+  call assert_equal('', @%)
+  let caught_431 = v:false
+  try
+    eval taglist('.*')
+  catch /:E431:/
+    let caught_431 = v:true
+  endtry
+  call assert_equal(v:true, caught_431)
+
+  call delete('Xtags')
+  call delete('Xfile')
+  set tags&
+endfunc
+
+" When :stag fails to open the file, should close the new window
+func Test_stag_close_window_on_error()
+  new | only
+  set tags=Xtags
+  call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+        \ "foo\tXfile\t1"], 'Xtags')
+  call writefile(['foo'], 'Xfile')
+  call writefile([], '.Xfile.swp')
+  " Remove the catch-all that runtest.vim adds
+  au! SwapExists
+  augroup StagTest
+    au!
+    autocmd SwapExists Xfile let v:swapchoice='q'
+  augroup END
+
+  stag foo
+  call assert_equal(1, winnr('$'))
+  call assert_equal('', @%)
+
+  augroup StagTest
+    au!
+  augroup END
+  call delete('Xfile')
+  call delete('.Xfile.swp')
+  set tags&
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim
index 4eb6e69adf..3bce2eb24d 100644
--- a/src/nvim/testdir/test_textformat.vim
+++ b/src/nvim/testdir/test_textformat.vim
@@ -1096,6 +1096,20 @@ func Test_fo_a_w()
   call feedkeys("iabc abc a abc\k0weade", 'xt')
   call assert_equal(['abc abcde ', 'a abc'], getline(1, '$'))
 
+  " when a line ends with space, it is not broken up.
+  %d
+  call feedkeys("ione two to    ", 'xt')
+  call assert_equal('one two to    ', getline(1))
+
+  " when a line ends with spaces and backspace is used in the next line, the
+  " last space in the previous line should be removed.
+  %d
+  set backspace=indent,eol,start
+  call setline(1, ['one    ', 'two'])
+  exe "normal 2Gi\"
+  call assert_equal(['one   two'], getline(1, '$'))
+  set backspace&
+
   " Test for 'a', 'w' and '1' options.
   setlocal textwidth=0
   setlocal fo=1aw
diff --git a/src/nvim/testdir/test_virtualedit.vim b/src/nvim/testdir/test_virtualedit.vim
index ddc7c20637..edf68e6482 100644
--- a/src/nvim/testdir/test_virtualedit.vim
+++ b/src/nvim/testdir/test_virtualedit.vim
@@ -357,6 +357,24 @@ func Test_delete_break_tab()
   close!
 endfunc
 
+" Test for using ,  and  in virtual edit mode
+" to erase character, word and line.
+func Test_ve_backspace()
+  new
+  call setline(1, 'sample')
+  set virtualedit=all
+  set backspace=indent,eol,start
+  exe "normal 15|i\\"
+  call assert_equal([0, 1, 7, 5], getpos('.'))
+  exe "normal 15|i\"
+  call assert_equal([0, 1, 6, 0], getpos('.'))
+  exe "normal 15|i\"
+  call assert_equal([0, 1, 1, 0], getpos('.'))
+  set backspace&
+  set virtualedit&
+  close!
+endfunc
+
 " After calling s:TryVirtualeditReplace(), line 1 will contain one of these
 " two strings, depending on whether virtual editing is on or off.
 let s:result_ve_on  = 'a      x'
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
index a9c057088d..712ea343ed 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -225,6 +225,15 @@ func Test_virtual_replace()
   exe "normal iabcdefghijklmnopqrst\0gRAB\tIJKLMNO\tQR"
   call assert_equal(['AB......CDEFGHI.Jkl',
 	      \ 'AB	IJKLMNO	QRst'], getline(12, 13))
+
+  " Test inserting Tab with 'noexpandtab' and 'softabstop' set to 4
+  %d
+  call setline(1, 'aaaaaaaaaaaaa')
+  set softtabstop=4
+  exe "normal gggR\\x"
+  call assert_equal("\txaaaa", getline(1))
+  set softtabstop&
+
   enew!
   set noai bs&vim
   if exists('save_t_kD')
-- 
cgit 


From 26a9f786c41bc8fa383e3ffe55a1fe77b50fb320 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 4 Nov 2022 20:31:25 +0800
Subject: vim-patch:8.2.1810: some code in normal.c not covered by tests

Problem:    Some code in normal.c not covered by tests.
Solution:   Add normal mode tests. (Yegappan Lakshmanan, closes vim/vim#7086)

https://github.com/vim/vim/commit/d7e5e9430ae192c76f1f03c3ac53fae823d94c33
---
 src/nvim/testdir/test_charsearch.vim | 31 ++++++++++++++--
 src/nvim/testdir/test_normal.vim     | 68 ++++++++++++++++++++++++++++++++++++
 2 files changed, 97 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_charsearch.vim b/src/nvim/testdir/test_charsearch.vim
index 4e6a75c3a0..54e0a62ce5 100644
--- a/src/nvim/testdir/test_charsearch.vim
+++ b/src/nvim/testdir/test_charsearch.vim
@@ -51,7 +51,7 @@ func Test_csearch_virtualedit()
   normal! tb
   call assert_equal([0, 1, 2, 6], getpos('.'))
   set virtualedit&
-  close!
+  bw!
 endfunc
 
 " Test for character search failure in latin1 encoding
@@ -65,7 +65,34 @@ func Test_charsearch_latin1()
   call assert_beeps('normal $Fz')
   call assert_beeps('normal $Tx')
   let &encoding = save_enc
-  close!
+  bw!
+endfunc
+
+" Test for using character search to find a multibyte character with composing
+" characters.
+func Test_charsearch_composing_char()
+  new
+  call setline(1, "one two thq\u0328\u0301r\u0328\u0301ree")
+  call feedkeys("fr\u0328\u0301", 'xt')
+  call assert_equal([0, 1, 16, 0, 12], getcurpos())
+
+  " use character search with a multi-byte character followed by a
+  " non-composing character
+  call setline(1, "abc deȉf ghi")
+  call feedkeys("ggcf\u0209\u0210", 'xt')
+  call assert_equal("\u0210f ghi", getline(1))
+  bw!
+endfunc
+
+" Test for character search with 'hkmap'
+func Test_charsearch_hkmap()
+  new
+  set hkmap
+  call setline(1, "ùðáâ÷ëòéïçìêöî")
+  call feedkeys("fë", 'xt')
+  call assert_equal([0, 1, 11, 0, 6], getcurpos())
+  set hkmap&
+  bw!
 endfunc
 
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index 04faeca455..6804d549c6 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -120,6 +120,39 @@ func Test_normal01_keymodel()
   call feedkeys("Vkk\yy", 'tx')
   call assert_equal(['47', '48', '49', '50'], getreg(0, 0, 1))
 
+  " Test for using special keys to start visual selection
+  %d
+  call setline(1, ['red fox tail', 'red fox tail', 'red fox tail'])
+  set keymodel=startsel
+  " Test for  and 
+  call cursor(1, 1)
+  call feedkeys("\y", 'xt')
+  call assert_equal([0, 1, 1, 0], getpos("'<"))
+  call assert_equal([0, 3, 1, 0], getpos("'>"))
+  call feedkeys("Gz\8|\y", 'xt')
+  call assert_equal([0, 2, 1, 0], getpos("'<"))
+  call assert_equal([0, 3, 8, 0], getpos("'>"))
+  " Test for  and 
+  call cursor(2, 12)
+  call feedkeys("\y", 'xt')
+  call assert_equal([0, 1, 1, 0], getpos("'<"))
+  call assert_equal([0, 2, 12, 0], getpos("'>"))
+  call cursor(1, 4)
+  call feedkeys("\y", 'xt')
+  call assert_equal([0, 1, 4, 0], getpos("'<"))
+  call assert_equal([0, 3, 13, 0], getpos("'>"))
+  " Test for  and 
+  call cursor(2, 5)
+  call feedkeys("\y", 'xt')
+  call assert_equal([0, 2, 5, 0], getpos("'<"))
+  call assert_equal([0, 2, 9, 0], getpos("'>"))
+  call cursor(2, 9)
+  call feedkeys("\y", 'xt')
+  call assert_equal([0, 2, 5, 0], getpos("'<"))
+  call assert_equal([0, 2, 9, 0], getpos("'>"))
+
+  set keymodel&
+
   " clean up
   bw!
 endfunc
@@ -509,6 +542,14 @@ func Test_normal10_expand()
     call assert_equal(expected[i], expand(''), 'i == ' . i)
   endfor
 
+  " Test for  in state.val and ptr->val
+  call setline(1, 'x = state.val;')
+  call cursor(1, 10)
+  call assert_equal('state.val', expand(''))
+  call setline(1, 'x = ptr->val;')
+  call cursor(1, 9)
+  call assert_equal('ptr->val', expand(''))
+
   if executable('echo')
     " Test expand(`...`) i.e. backticks command expansion.
     " MS-Windows has a trailing space.
@@ -523,6 +564,19 @@ func Test_normal10_expand()
   bw!
 endfunc
 
+" Test for expand() in latin1 encoding
+func Test_normal_expand_latin1()
+  new
+  let save_enc = &encoding
+  " set encoding=latin1
+  call setline(1, 'val = item->color;')
+  call cursor(1, 11)
+  call assert_equal('color', expand(""))
+  call assert_equal('item->color', expand(""))
+  let &encoding = save_enc
+  bw!
+endfunc
+
 func Test_normal11_showcmd()
   " test for 'showcmd'
   10new
@@ -547,6 +601,13 @@ func Test_normal11_showcmd()
   redraw!
   call assert_match('1-3$', Screenline(&lines))
   call feedkeys("v", 'xt')
+  " test for visually selecting the end of line
+  call setline(1, ["foobar"])
+  call feedkeys("$vl", 'xt')
+  redraw!
+  call assert_match('2$', Screenline(&lines))
+  call feedkeys("y", 'xt')
+  call assert_equal("r\n", @")
   bw!
 endfunc
 
@@ -2161,6 +2222,13 @@ func Test_normal31_r_cmd()
   normal gglvjjrx
   call assert_equal(['axx', 'xxx', 'xxf'], getline(1, '$'))
 
+  " replace with a multibyte character (with multiple composing characters)
+  %d
+  new
+  call setline(1, 'aaa')
+  exe "normal $ra\u0328\u0301"
+  call assert_equal("aaa\u0328\u0301", getline(1))
+
   " clean up
   set noautoindent
   bw!
-- 
cgit 


From 2aafaa59928e17fd7858a89d203e2b2a07707601 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 4 Nov 2022 20:41:53 +0800
Subject: vim-patch:8.2.2901: some operators not fully tested

Problem:    Some operators not fully tested.
Solution:   Add a few test cases. (Yegappan Lakshmanan, closes vim/vim#8282)

https://github.com/vim/vim/commit/3e72dcad8b752a42b6eaf71213e3f5d534175256
---
 src/nvim/testdir/test_cpoptions.vim   |  1 +
 src/nvim/testdir/test_increment.vim   | 17 +++++++++++++++++
 src/nvim/testdir/test_normal.vim      | 31 +++++++++++++++++++++++++++++++
 src/nvim/testdir/test_virtualedit.vim | 27 +++++++++++++++++++++++++++
 4 files changed, 76 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_cpoptions.vim b/src/nvim/testdir/test_cpoptions.vim
index cc281ef521..76d2c9542d 100644
--- a/src/nvim/testdir/test_cpoptions.vim
+++ b/src/nvim/testdir/test_cpoptions.vim
@@ -167,6 +167,7 @@ func Test_cpo_E()
   call assert_beeps('normal "ayl')
   " change an empty line
   call assert_beeps('normal lcTa')
+  call assert_beeps('normal 0c0')
   " delete an empty line
   call assert_beeps('normal D')
   call assert_beeps('normal dl')
diff --git a/src/nvim/testdir/test_increment.vim b/src/nvim/testdir/test_increment.vim
index 52355d86fb..3c2b88ef9f 100644
--- a/src/nvim/testdir/test_increment.vim
+++ b/src/nvim/testdir/test_increment.vim
@@ -877,4 +877,21 @@ func Test_normal_increment_with_virtualedit()
   set virtualedit&
 endfunc
 
+" Test for incrementing a signed hexadecimal and octal number
+func Test_normal_increment_signed_hexoct_nr()
+  new
+  " negative sign before a hex number should be ignored
+  call setline(1, ["-0x9"])
+  exe "norm \"
+  call assert_equal(["-0xa"], getline(1, '$'))
+  exe "norm \"
+  call assert_equal(["-0x9"], getline(1, '$'))
+  call setline(1, ["-007"])
+  exe "norm \"
+  call assert_equal(["-010"], getline(1, '$'))
+  exe "norm \"
+  call assert_equal(["-007"], getline(1, '$'))
+  bw!
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index 6804d549c6..5fc670e422 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -2125,6 +2125,16 @@ func Test_normal30_changecase()
   call assert_equal(['aaaaaa', 'AAAAaa'], getline(1, 2))
   set whichwrap&
 
+  " try changing the case with a double byte encoding (DBCS)
+  %bw!
+  let enc = &enc
+  " set encoding=cp932
+  call setline(1, "\u8470")
+  normal ~
+  normal gU$gu$gUgUg~g~gugu
+  call assert_equal("\u8470", getline(1))
+  let &encoding = enc
+
   " clean up
   bw!
 endfunc
@@ -3499,6 +3509,27 @@ func Test_normal_percent_jump()
   close!
 endfunc
 
+" Test for << and >> commands to shift text by 'shiftwidth'
+func Test_normal_shift_rightleft()
+  new
+  call setline(1, ['one', '', "\t", '  two', "\tthree", '      four'])
+  set shiftwidth=2 tabstop=8
+  normal gg6>>
+  call assert_equal(['  one', '', "\t  ", '    two', "\t  three", "\tfour"],
+        \ getline(1, '$'))
+  normal ggVG2>>
+  call assert_equal(['      one', '', "\t      ", "\ttwo",
+        \ "\t      three", "\t    four"], getline(1, '$'))
+  normal gg6<<
+  call assert_equal(['    one', '', "\t    ", '      two', "\t    three",
+        \ "\t  four"], getline(1, '$'))
+  normal ggVG2<<
+  call assert_equal(['one', '', "\t", '  two', "\tthree", '      four'],
+        \ getline(1, '$'))
+  set shiftwidth& tabstop&
+  bw!
+endfunc
+
 " Some commands like yy, cc, dd, >>, << and !! accept a count after
 " typing the first letter of the command.
 func Test_normal_count_after_operator()
diff --git a/src/nvim/testdir/test_virtualedit.vim b/src/nvim/testdir/test_virtualedit.vim
index edf68e6482..2bf8c3fc77 100644
--- a/src/nvim/testdir/test_virtualedit.vim
+++ b/src/nvim/testdir/test_virtualedit.vim
@@ -80,6 +80,10 @@ func Test_edit_change()
   call setline(1, "\t⒌")
   normal Cx
   call assert_equal('x', getline(1))
+  " Do a visual block change
+  call setline(1, ['a', 'b', 'c'])
+  exe "normal gg3l\2jcx"
+  call assert_equal(['a  x', 'b  x', 'c  x'], getline(1, '$'))
   bwipe!
   set virtualedit=
 endfunc
@@ -289,6 +293,16 @@ func Test_replace_after_eol()
   call append(0, '"r"')
   normal gg$5lrxa
   call assert_equal('"r"    x', getline(1))
+  " visual block replace
+  %d _
+  call setline(1, ['a', '', 'b'])
+  exe "normal 2l\2jrx"
+  call assert_equal(['a x', '  x', 'b x'], getline(1, '$'))
+  " visual characterwise selection replace after eol
+  %d _
+  call setline(1, 'a')
+  normal 4lv2lrx
+  call assert_equal('a   xxx', getline(1))
   bwipe!
   set virtualedit=
 endfunc
@@ -375,6 +389,19 @@ func Test_ve_backspace()
   close!
 endfunc
 
+" Test for delete (x) on EOL character and after EOL
+func Test_delete_past_eol()
+  new
+  call setline(1, "ab")
+  set virtualedit=all
+  exe "normal 2lx"
+  call assert_equal('ab', getline(1))
+  exe "normal 10lx"
+  call assert_equal('ab', getline(1))
+  set virtualedit&
+  bw!
+endfunc
+
 " After calling s:TryVirtualeditReplace(), line 1 will contain one of these
 " two strings, depending on whether virtual editing is on or off.
 let s:result_ve_on  = 'a      x'
-- 
cgit 


From 95f2f3cb5efcc18f17f2812ba6d1d9e1c06b2b5a Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Sat, 5 Nov 2022 00:58:01 +0000
Subject: fix(vim.diff): fix fastforward off-by-1 (#20937)

---
 src/nvim/lua/xdiff.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c
index 3adb6088fc..cd542b0e00 100644
--- a/src/nvim/lua/xdiff.c
+++ b/src/nvim/lua/xdiff.c
@@ -69,8 +69,8 @@ static void get_linematch_results(lua_State *lstate, mmfile_t *ma, mmfile_t *mb,
   const char *diff_begin[2] = { ma->ptr, mb->ptr };
   int diff_length[2] = { (int)count_a, (int)count_b };
 
-  fastforward_buf_to_lnum(&diff_begin[0], start_a);
-  fastforward_buf_to_lnum(&diff_begin[1], start_b);
+  fastforward_buf_to_lnum(&diff_begin[0], start_a + 1);
+  fastforward_buf_to_lnum(&diff_begin[1], start_b + 1);
 
   int *decisions = NULL;
   size_t decisions_length = linematch_nbuffers(diff_begin, diff_length, 2, &decisions, iwhite);
-- 
cgit 


From 19a487bc86482392eb50c61375d20e440e6e16b7 Mon Sep 17 00:00:00 2001
From: Will Spurgin 
Date: Fri, 4 Nov 2022 20:24:34 -0500
Subject: fix(fileio): use first available directory in backupdir for
 backupcopy (#20655)

Fix backups failing for symlink files

Set backup to NULL prior to continuing & Clear backup prior to NULL set
to avoid leaking

Fixes #11349

Remove testing hacks in scripts for windows

Skip FreeBSD

Something appears up with these types of tests for FreeBSD on
Circus, see 2d6735d8ce
---
 src/nvim/fileio.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index c3feadeb36..c780f64a7a 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -2781,10 +2781,11 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
 #endif
 
           // copy the file
-          if (os_copy(fname, backup, UV_FS_COPYFILE_FICLONE)
-              != 0) {
-            SET_ERRMSG(_("E506: Can't write to backup file "
-                         "(add ! to override)"));
+          if (os_copy(fname, backup, UV_FS_COPYFILE_FICLONE) != 0) {
+            SET_ERRMSG(_("E509: Cannot create backup file (add ! to override)"));
+            XFREE_CLEAR(backup);
+            backup = NULL;
+            continue;
           }
 
 #ifdef UNIX
@@ -2795,6 +2796,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
 #ifdef HAVE_ACL
           mch_set_acl((char_u *)backup, acl);
 #endif
+          SET_ERRMSG(NULL);
           break;
         }
       }
-- 
cgit 


From b7b4914fe44b4a192379659dbcb958c63b82327c Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 08:03:34 +0800
Subject: vim-patch:8.2.0433: INT signal not properly tested

Problem:    INT signal not properly tested.
Solution:   Add a test.  Also clean up some unnecessary lines. (Dominique
            Pelle, closes vim/vim#5828)

https://github.com/vim/vim/commit/bad8804cdd739a5a7321b8411ad7fd4f45741b54
---
 src/nvim/testdir/test_display.vim |  2 --
 src/nvim/testdir/test_ex_mode.vim |  1 -
 src/nvim/testdir/test_signals.vim | 31 ++++++++++++++++++++++++++++++-
 3 files changed, 30 insertions(+), 4 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_display.vim b/src/nvim/testdir/test_display.vim
index 13796449ab..679fe89628 100644
--- a/src/nvim/testdir/test_display.vim
+++ b/src/nvim/testdir/test_display.vim
@@ -195,8 +195,6 @@ func Test_edit_long_file_name()
 
   call VerifyScreenDump(buf, 'Test_long_file_name_1', {})
 
-  call term_sendkeys(buf, ":q\")
-
   " clean up
   call StopVimInTerminal(buf)
   call delete(longName)
diff --git a/src/nvim/testdir/test_ex_mode.vim b/src/nvim/testdir/test_ex_mode.vim
index 2f734cba26..93100732ed 100644
--- a/src/nvim/testdir/test_ex_mode.vim
+++ b/src/nvim/testdir/test_ex_mode.vim
@@ -97,7 +97,6 @@ func Test_Ex_substitute()
   call term_sendkeys(buf, ":vi\")
   call WaitForAssert({-> assert_match('foo bar', term_getline(buf, 1))}, 1000)
 
-  call term_sendkeys(buf, ":q!\n")
   call StopVimInTerminal(buf)
 endfunc
 
diff --git a/src/nvim/testdir/test_signals.vim b/src/nvim/testdir/test_signals.vim
index e1c6e5d11f..31b76919e1 100644
--- a/src/nvim/testdir/test_signals.vim
+++ b/src/nvim/testdir/test_signals.vim
@@ -53,7 +53,7 @@ endfunc
 " Test signal PWR, which should update the swap file.
 func Test_signal_PWR()
   if !HasSignal('PWR')
-    return
+    throw 'Skipped: PWR signal not supported'
   endif
 
   " Set a very large 'updatetime' and 'updatecount', so that we can be sure
@@ -79,6 +79,35 @@ func Test_signal_PWR()
   set updatetime& updatecount&
 endfunc
 
+" Test signal INT. Handler sets got_int. It should be like typing CTRL-C.
+func Test_signal_INT()
+  if !HasSignal('INT')
+    throw 'Skipped: INT signal not supported'
+  endif
+
+  " Skip the rest of the test when running with valgrind as signal INT is not
+  " received somehow by Vim when running with valgrind.
+  let cmd = GetVimCommand()
+  if cmd =~ 'valgrind'
+    throw 'Skipped: cannot test signal INT with valgrind'
+  endif
+
+  if !CanRunVimInTerminal()
+    throw 'Skipped: cannot run vim in terminal'
+  endif
+  let buf = RunVimInTerminal('', {'rows': 6})
+  let pid_vim = term_getjob(buf)->job_info().process
+
+  " Check that an endless loop in Vim is interrupted by signal INT.
+  call term_sendkeys(buf, ":while 1 | endwhile\n")
+  call WaitForAssert({-> assert_equal(':while 1 | endwhile', term_getline(buf, 6))})
+  exe 'silent !kill -s INT ' .. pid_vim
+  call term_sendkeys(buf, ":call setline(1, 'INTERUPTED')\n")
+  call WaitForAssert({-> assert_equal('INTERUPTED', term_getline(buf, 1))})
+
+  call StopVimInTerminal(buf)
+endfunc
+
 " Test a deadly signal.
 "
 " There are several deadly signals: SISEGV, SIBUS, SIGTERM...
-- 
cgit 


From e30929cda5cad8afb384cdb5b1ce62758dca6bde Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 07:34:06 +0800
Subject: vim-patch:8.2.0644: insufficient testing for invalid function
 arguments

Problem:    Insufficient testing for invalid function arguments.
Solution:   Add more tests. (Yegappan Lakshmanan, closes vim/vim#5988)

https://github.com/vim/vim/commit/99fa721944dda9d07c53c907c33466728df5c271

Omit test_listener.vim: changed again in patch 8.2.1183.
Omit test_textprop.vim: changed again in patch 8.2.1183.
Cherry-pick quickfix feature checks from patch 8.1.2373.
Omit Test_saveas() change: duplicate and removed in patch 8.2.0866.
---
 src/nvim/testdir/test_bufline.vim      |  1 +
 src/nvim/testdir/test_clientserver.vim |  1 +
 src/nvim/testdir/test_expr.vim         |  1 +
 src/nvim/testdir/test_functions.vim    | 21 +++++++++++++++------
 src/nvim/testdir/test_match.vim        |  6 +++++-
 src/nvim/testdir/test_menu.vim         |  1 +
 src/nvim/testdir/test_registers.vim    |  4 +++-
 src/nvim/testdir/test_reltime.vim      |  4 ++++
 src/nvim/testdir/test_window_cmd.vim   | 12 ++++++++++++
 src/nvim/testdir/test_window_id.vim    | 12 ++++++++++++
 src/nvim/testdir/test_writefile.vim    |  7 +++++++
 11 files changed, 62 insertions(+), 8 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim
index 8f853fe44e..5a47f8ef25 100644
--- a/src/nvim/testdir/test_bufline.vim
+++ b/src/nvim/testdir/test_bufline.vim
@@ -26,6 +26,7 @@ func Test_setbufline_getbufline()
   call assert_equal(['d'], getbufline(b, 4))
   call assert_equal(['e'], getbufline(b, 5))
   call assert_equal([], getbufline(b, 6))
+  call assert_equal([], getbufline(b, 2, 1))
   exe "bwipe! " . b
 endfunc
 
diff --git a/src/nvim/testdir/test_clientserver.vim b/src/nvim/testdir/test_clientserver.vim
index 943f79d98f..301dad8341 100644
--- a/src/nvim/testdir/test_clientserver.vim
+++ b/src/nvim/testdir/test_clientserver.vim
@@ -182,6 +182,7 @@ func Test_client_server()
     endif
   endtry
 
+  call assert_fails('call remote_startserver([])', 'E730:')
   call assert_fails("let x = remote_peek([])", 'E730:')
   call assert_fails("let x = remote_read('vim10')", 'E277:')
   call assert_fails("call server2client('abc', 'xyz')", 'E258:')
diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim
index dc8401003d..0579ce7dcb 100644
--- a/src/nvim/testdir/test_expr.vim
+++ b/src/nvim/testdir/test_expr.vim
@@ -528,6 +528,7 @@ func Test_setmatches()
   endif
   eval set->setmatches()
   call assert_equal(exp, getmatches())
+  call assert_fails('let m = setmatches([], [])', 'E957:')
 endfunc
 
 func Test_empty_concatenate()
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index f6c16a366b..5718266dae 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -29,6 +29,8 @@ func Test_has()
     call assert_equal(0, and(has('ttyout'), 0))
     call assert_equal(1, has('multi_byte_encoding'))
   endif
+  call assert_equal(1, has('vcon', 1))
+  call assert_equal(1, has('mouse_gpm_enabled', 1))
 
   call assert_equal(0, has('nonexistent'))
   call assert_equal(0, has('nonexistent', 1))
@@ -1331,12 +1333,15 @@ endfunc
 
 " Test for the inputdialog() function
 func Test_inputdialog()
-  CheckNotGui
-
-  call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\\", 'xt')
-  call assert_equal('xx', v)
-  call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\\", 'xt')
-  call assert_equal('yy', v)
+  if has('gui_running')
+    call assert_fails('let v=inputdialog([], "xx")', 'E730:')
+    call assert_fails('let v=inputdialog("Q", [])', 'E730:')
+  else
+    call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\\", 'xt')
+    call assert_equal('xx', v)
+    call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\\", 'xt')
+    call assert_equal('yy', v)
+  endif
 endfunc
 
 " Test for inputlist()
@@ -1387,6 +1392,7 @@ func Test_balloon_show()
   call balloon_show('hi!')
   if !has('gui_running')
     call balloon_show(range(3))
+    call balloon_show([])
   endif
 endfunc
 
@@ -2271,6 +2277,9 @@ func Test_range()
   call assert_fails('let x=range(2, 8, 0)', 'E726:')
   call assert_fails('let x=range(3, 1)', 'E727:')
   call assert_fails('let x=range(1, 3, -2)', 'E727:')
+  call assert_fails('let x=range([])', 'E745:')
+  call assert_fails('let x=range(1, [])', 'E745:')
+  call assert_fails('let x=range(1, 4, [])', 'E745:')
 endfunc
 
 func Test_garbagecollect_now_fails()
diff --git a/src/nvim/testdir/test_match.vim b/src/nvim/testdir/test_match.vim
index fe931fefb2..352493de9c 100644
--- a/src/nvim/testdir/test_match.vim
+++ b/src/nvim/testdir/test_match.vim
@@ -160,12 +160,14 @@ endfunc
 func Test_matchadd_error()
   call clearmatches()
   " Nvim: not an error anymore:
+  " call assert_fails("call matchadd('GroupDoesNotExist', 'X')", 'E28:')
   call matchadd('GroupDoesNotExist', 'X')
   call assert_equal([{'group': 'GroupDoesNotExist', 'pattern': 'X', 'priority': 10, 'id': 1206}], getmatches())
   call assert_fails("call matchadd('Search', '\\(')", 'E475:')
   call assert_fails("call matchadd('Search', 'XXX', 1, 123, 1)", 'E715:')
   call assert_fails("call matchadd('Error', 'XXX', 1, 3)", 'E798:')
   call assert_fails("call matchadd('Error', 'XXX', 1, 0)", 'E799:')
+  call assert_fails("call matchadd('Error', 'XXX', [], 0)", 'E745:')
 endfunc
 
 func Test_matchaddpos()
@@ -305,7 +307,10 @@ func Test_matchaddpos_error()
   call assert_fails("call matchaddpos('Error', [1], 1, 123, 1)", 'E715:')
   call assert_fails("call matchaddpos('Error', [1], 1, 5, {'window':12345})", 'E957:')
   " Why doesn't the following error have an error code E...?
+  " call assert_fails("call matchaddpos('Error', [{}])", 'E290:')
   call assert_fails("call matchaddpos('Error', [{}])", 'E5031:')
+  call assert_equal(-1, matchaddpos('Error', v:_null_list))
+  call assert_fails("call matchaddpos('Error', [1], [], 1)", 'E745:')
 endfunc
 
 func OtherWindowCommon()
@@ -362,5 +367,4 @@ func Test_matchadd_other_window()
   call delete('XscriptMatchCommon')
 endfunc
 
-
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_menu.vim b/src/nvim/testdir/test_menu.vim
index 2e149ad5a5..58da0ed382 100644
--- a/src/nvim/testdir/test_menu.vim
+++ b/src/nvim/testdir/test_menu.vim
@@ -252,6 +252,7 @@ func Test_menu_info()
   nmenu Test.abc  
   call assert_equal('', menu_info('Test.abc').rhs)
   call assert_fails('call menu_info([])', 'E730:')
+  call assert_fails('call menu_info("", [])', 'E730:')
   nunmenu Test
 
   " Test for defining menus in different modes
diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim
index 40320c6405..5bdbbe7a22 100644
--- a/src/nvim/testdir/test_registers.vim
+++ b/src/nvim/testdir/test_registers.vim
@@ -285,7 +285,9 @@ func Test_get_register()
 
   " Test for inserting a multi-line register in the command line
   call feedkeys(":\r\", 'xt')
-  call assert_equal("a\rb", histget(':', -1))  " Modified because of #6137
+  " Nvim: no trailing CR because of #6137
+  " call assert_equal("a\rb\r", histget(':', -1))
+  call assert_equal("a\rb", histget(':', -1))
 
   call assert_fails('let r = getreg("=", [])', 'E745:')
   call assert_fails('let r = getreg("=", 1, [])', 'E745:')
diff --git a/src/nvim/testdir/test_reltime.vim b/src/nvim/testdir/test_reltime.vim
index b381f1ddbb..f4ce5de118 100644
--- a/src/nvim/testdir/test_reltime.vim
+++ b/src/nvim/testdir/test_reltime.vim
@@ -24,4 +24,8 @@ func Test_reltime()
   call assert_true(reltimefloat(differs) < 0.1)
   call assert_true(reltimefloat(differs) > 0.0)
 
+  call assert_equal(0, reltime({}))
+  call assert_equal(0, reltime({}, {}))
 endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim
index 2ed537c601..c7b5896082 100644
--- a/src/nvim/testdir/test_window_cmd.vim
+++ b/src/nvim/testdir/test_window_cmd.vim
@@ -501,10 +501,13 @@ func Test_win_screenpos()
   call assert_equal([1, 32], win_screenpos(2))
   call assert_equal([12, 1], win_screenpos(3))
   call assert_equal([0, 0], win_screenpos(4))
+  call assert_fails('let l = win_screenpos([])', 'E745:')
   only
 endfunc
 
 func Test_window_jump_tag()
+  CheckFeature quickfix
+
   help
   /iccf
   call assert_match('^|iccf|',  getline('.'))
@@ -734,6 +737,7 @@ func Test_relative_cursor_position_in_one_line_window()
 
   only!
   bwipe!
+  call assert_fails('call winrestview(v:_null_dict)', 'E474:')
 endfunc
 
 func Test_relative_cursor_position_after_move_and_resize()
@@ -910,6 +914,10 @@ func Test_winnr()
   call assert_fails("echo winnr('ll')", 'E15:')
   call assert_fails("echo winnr('5')", 'E15:')
   call assert_equal(4, winnr('0h'))
+  call assert_fails("let w = winnr([])", 'E730:')
+  call assert_equal('unknown', win_gettype(-1))
+  call assert_equal(-1, winheight(-1))
+  call assert_equal(-1, winwidth(-1))
 
   tabnew
   call assert_equal(8, tabpagewinnr(1, 'j'))
@@ -930,9 +938,12 @@ func Test_winrestview()
   call assert_equal(view, winsaveview())
 
   bwipe!
+  call assert_fails('call winrestview(v:_null_dict)', 'E474:')
 endfunc
 
 func Test_win_splitmove()
+  CheckFeature quickfix
+
   edit a
   leftabove split b
   leftabove vsplit c
@@ -958,6 +969,7 @@ func Test_win_splitmove()
   call assert_equal(bufname(winbufnr(2)), 'b')
   call assert_equal(bufname(winbufnr(3)), 'a')
   call assert_equal(bufname(winbufnr(4)), 'd')
+  call assert_fails('call win_splitmove(winnr(), winnr("k"), v:_null_dict)', 'E474:')
   only | bd
 
   call assert_fails('call win_splitmove(winnr(), 123)', 'E957:')
diff --git a/src/nvim/testdir/test_window_id.vim b/src/nvim/testdir/test_window_id.vim
index 8bf4ede350..396a49b55f 100644
--- a/src/nvim/testdir/test_window_id.vim
+++ b/src/nvim/testdir/test_window_id.vim
@@ -1,5 +1,7 @@
 " Test using the window ID.
 
+source check.vim
+
 func Test_win_getid()
   edit one
   let id1 = win_getid()
@@ -90,10 +92,16 @@ func Test_win_getid()
   split
   call assert_equal(sort([id5, win_getid()]), sort(win_findbuf(bufnr5)))
 
+  call assert_fails('let w = win_getid([])', 'E745:')
+  call assert_equal(0, win_getid(-1))
+  call assert_equal(-1, win_getid(1, -1))
+
   only!
 endfunc
 
 func Test_win_getid_curtab()
+  CheckFeature quickfix
+
   tabedit X
   tabfirst
   copen
@@ -127,4 +135,8 @@ func Test_winlayout()
   let w2 = win_getid()
   call assert_equal(['leaf', w2], 2->winlayout())
   tabclose
+
+  call assert_equal([], winlayout(-1))
 endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim
index adc05ab979..7dfcaaedeb 100644
--- a/src/nvim/testdir/test_writefile.vim
+++ b/src/nvim/testdir/test_writefile.vim
@@ -260,6 +260,13 @@ func Test_write_errors()
   close
 
   call delete('Xfile')
+
+  " Nvim treats NULL list/blob more like empty list/blob
+  " call writefile(v:_null_list, 'Xfile')
+  " call assert_false(filereadable('Xfile'))
+  " call writefile(v:_null_blob, 'Xfile')
+  " call assert_false(filereadable('Xfile'))
+  call assert_fails('call writefile([], "")', 'E482:')
 endfunc
 
 " Test for writing a file using invalid file encoding
-- 
cgit 


From 40ca9b9528e5a4f043f0e6858786bf0c3d992a90 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 08:07:06 +0800
Subject: vim-patch:8.2.0866: not enough tests for buffer writing

Problem:    Not enough tests for buffer writing.
Solution:   Add more tests. Use CheckRunVimInTerminal in more places.
            (Yegappan Lakshmanan, closes vim/vim#6167)

https://github.com/vim/vim/commit/494e9069cb32620f7688a7cb128a3feff827639e
---
 src/nvim/testdir/test_arglist.vim      |   5 +-
 src/nvim/testdir/test_match.vim        |   8 +-
 src/nvim/testdir/test_search.vim       |   4 +-
 src/nvim/testdir/test_signals.vim      |   8 +-
 src/nvim/testdir/test_signs.vim        |   4 +-
 src/nvim/testdir/test_startup.vim      |   8 +-
 src/nvim/testdir/test_startup_utf8.vim |   4 +-
 src/nvim/testdir/test_syntax.vim       |   4 +-
 src/nvim/testdir/test_tabpage.vim      |   4 +-
 src/nvim/testdir/test_timers.vim       |   4 +-
 src/nvim/testdir/test_vimscript.vim    |   4 +-
 src/nvim/testdir/test_writefile.vim    | 132 +++++++++++++++++++++++++++++++++
 12 files changed, 147 insertions(+), 42 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim
index 443a217143..cae71e10f3 100644
--- a/src/nvim/testdir/test_arglist.vim
+++ b/src/nvim/testdir/test_arglist.vim
@@ -1,5 +1,6 @@
 " Test argument list commands
 
+source check.vim
 source shared.vim
 source term_util.vim
 
@@ -552,9 +553,7 @@ endfunc
 
 " Test for quitting Vim with unedited files in the argument list
 func Test_quit_with_arglist()
-  if !CanRunVimInTerminal()
-    throw 'Skipped: cannot run vim in terminal'
-  endif
+  CheckRunVimInTerminal
   let buf = RunVimInTerminal('', {'rows': 6})
   call term_sendkeys(buf, ":set nomore\n")
   call term_sendkeys(buf, ":args a b c\n")
diff --git a/src/nvim/testdir/test_match.vim b/src/nvim/testdir/test_match.vim
index 352493de9c..29d087bc23 100644
--- a/src/nvim/testdir/test_match.vim
+++ b/src/nvim/testdir/test_match.vim
@@ -343,9 +343,7 @@ func Test_matchdelete_error()
 endfunc
 
 func Test_matchclear_other_window()
-  if !CanRunVimInTerminal()
-    throw 'Skipped: cannot make screendumps'
-  endif
+  CheckRunVimInTerminal
   let buf = OtherWindowCommon()
   call term_sendkeys(buf, ":call clearmatches(winid)\")
   call VerifyScreenDump(buf, 'Test_matchclear_1', {})
@@ -355,9 +353,7 @@ func Test_matchclear_other_window()
 endfunc
 
 func Test_matchadd_other_window()
-  if !CanRunVimInTerminal()
-    throw 'Skipped: cannot make screendumps'
-  endif
+  CheckRunVimInTerminal
   let buf = OtherWindowCommon()
   call term_sendkeys(buf, ":call matchadd('Search', 'Hello', 1, -1, #{window: winid})\")
   call term_sendkeys(buf, ":\")
diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim
index d79910fbc1..cb35868b47 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -897,9 +897,7 @@ func Test_incsearch_cmdline_modifier()
 endfunc
 
 func Test_incsearch_scrolling()
-  if !CanRunVimInTerminal()
-    throw 'Skipped: cannot make screendumps'
-  endif
+  CheckRunVimInTerminal
   call assert_equal(0, &scrolloff)
   call writefile([
 	\ 'let dots = repeat(".", 120)',
diff --git a/src/nvim/testdir/test_signals.vim b/src/nvim/testdir/test_signals.vim
index 31b76919e1..c291c68e0d 100644
--- a/src/nvim/testdir/test_signals.vim
+++ b/src/nvim/testdir/test_signals.vim
@@ -81,6 +81,7 @@ endfunc
 
 " Test signal INT. Handler sets got_int. It should be like typing CTRL-C.
 func Test_signal_INT()
+  CheckRunVimInTerminal
   if !HasSignal('INT')
     throw 'Skipped: INT signal not supported'
   endif
@@ -92,9 +93,6 @@ func Test_signal_INT()
     throw 'Skipped: cannot test signal INT with valgrind'
   endif
 
-  if !CanRunVimInTerminal()
-    throw 'Skipped: cannot run vim in terminal'
-  endif
   let buf = RunVimInTerminal('', {'rows': 6})
   let pid_vim = term_getjob(buf)->job_info().process
 
@@ -121,9 +119,7 @@ func Test_deadly_signal_TERM()
   if !HasSignal('TERM')
     throw 'Skipped: TERM signal not supported'
   endif
-  if !CanRunVimInTerminal()
-    throw 'Skipped: cannot run vim in terminal'
-  endif
+  CheckRunVimInTerminal
   let cmd = GetVimCommand()
   if cmd =~ 'valgrind'
     throw 'Skipped: cannot test signal TERM with valgrind'
diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim
index ff9ba3d8ed..aa43477c5f 100644
--- a/src/nvim/testdir/test_signs.vim
+++ b/src/nvim/testdir/test_signs.vim
@@ -1741,9 +1741,7 @@ endfunc
 
 " Test for correct cursor position after the sign column appears or disappears.
 func Test_sign_cursor_position()
-  if !CanRunVimInTerminal()
-    throw 'Skipped: cannot make screendumps'
-  endif
+  CheckRunVimInTerminal
 
   let lines =<< trim END
 	call setline(1, [repeat('x', 75), 'mmmm', 'yyyy'])
diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim
index b8aad1bc46..30bbd355df 100644
--- a/src/nvim/testdir/test_startup.vim
+++ b/src/nvim/testdir/test_startup.vim
@@ -809,9 +809,7 @@ func Test_issue_3969()
 endfunc
 
 func Test_start_with_tabs()
-  if !CanRunVimInTerminal()
-    return
-  endif
+  CheckRunVimInTerminal
 
   let buf = RunVimInTerminal('-p a b c', {})
   call VerifyScreenDump(buf, 'Test_start_with_tabs', {})
@@ -968,9 +966,7 @@ endfunc
 
 " Test for specifying a non-existing vimrc file using "-u"
 func Test_missing_vimrc()
-  if !CanRunVimInTerminal()
-    throw 'Skipped: cannot run vim in terminal'
-  endif
+  CheckRunVimInTerminal
   let after =<< trim [CODE]
     call assert_match('^E282:', v:errmsg)
     call writefile(v:errors, 'Xtestout')
diff --git a/src/nvim/testdir/test_startup_utf8.vim b/src/nvim/testdir/test_startup_utf8.vim
index bb4304396e..2ee6ecc41d 100644
--- a/src/nvim/testdir/test_startup_utf8.vim
+++ b/src/nvim/testdir/test_startup_utf8.vim
@@ -63,9 +63,7 @@ func Test_read_fifo_utf8()
 endfunc
 
 func Test_detect_ambiwidth()
-  if !CanRunVimInTerminal()
-    throw 'Skipped: cannot run Vim in a terminal window'
-  endif
+  CheckRunVimInTerminal
 
   " Use the title termcap entries to output the escape sequence.
   call writefile([
diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim
index 9903b48dbe..29ebe141f7 100644
--- a/src/nvim/testdir/test_syntax.vim
+++ b/src/nvim/testdir/test_syntax.vim
@@ -631,9 +631,7 @@ endfunc
 
 " Check highlighting for a small piece of C code with a screen dump.
 func Test_syntax_c()
-  if !CanRunVimInTerminal()
-    throw 'Skipped: cannot make screendumps'
-  endif
+  CheckRunVimInTerminal
   call writefile([
 	\ '/* comment line at the top */',
 	\ 'int main(int argc, char **argv) { // another comment',
diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim
index 4ada48d56c..b97aa409d8 100644
--- a/src/nvim/testdir/test_tabpage.vim
+++ b/src/nvim/testdir/test_tabpage.vim
@@ -591,9 +591,7 @@ func Test_tabs()
 endfunc
 
 func Test_tabpage_cmdheight()
-  if !CanRunVimInTerminal()
-    throw 'Skipped: cannot make screendumps'
-  endif
+  CheckRunVimInTerminal
   call writefile([
         \ 'set laststatus=2',
         \ 'set cmdheight=2',
diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim
index 3a6abb3968..f94ee6c9f3 100644
--- a/src/nvim/testdir/test_timers.vim
+++ b/src/nvim/testdir/test_timers.vim
@@ -304,9 +304,7 @@ func Test_timer_ex_mode()
 endfunc
 
 func Test_timer_restore_count()
-  if !CanRunVimInTerminal()
-    throw 'Skipped: cannot run Vim in a terminal window'
-  endif
+  CheckRunVimInTerminal
   " Check that v:count is saved and restored, not changed by a timer.
   call writefile([
         \ 'nnoremap  L v:count ? v:count . "l" : "l"',
diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim
index bd60dcb707..3a6baaf1f0 100644
--- a/src/nvim/testdir/test_vimscript.vim
+++ b/src/nvim/testdir/test_vimscript.vim
@@ -1707,9 +1707,7 @@ endfunc
 
 " Test for deep nesting of if/for/while/try statements              {{{1
 func Test_deep_nest()
-  if !CanRunVimInTerminal()
-    throw 'Skipped: cannot run vim in terminal'
-  endif
+  CheckRunVimInTerminal
 
   let lines =<< trim [SCRIPT]
     " Deep nesting of if ... endif
diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim
index 7dfcaaedeb..59214d1aab 100644
--- a/src/nvim/testdir/test_writefile.vim
+++ b/src/nvim/testdir/test_writefile.vim
@@ -267,6 +267,138 @@ func Test_write_errors()
   " call writefile(v:_null_blob, 'Xfile')
   " call assert_false(filereadable('Xfile'))
   call assert_fails('call writefile([], "")', 'E482:')
+
+  " very long file name
+  let long_fname = repeat('n', 5000)
+  call assert_fails('exe "w " .. long_fname', 'E75:')
+  call assert_fails('call writefile([], long_fname)', 'E482:')
+endfunc
+
+" Test for writing to a file which is modified after Vim read it
+func Test_write_file_mtime()
+  CheckEnglish
+  CheckRunVimInTerminal
+
+  " First read the file into a buffer
+  call writefile(["Line1", "Line2"], 'Xfile')
+  let old_ftime = getftime('Xfile')
+  let buf = RunVimInTerminal('Xfile', #{rows : 10})
+  call term_wait(buf)
+  call term_sendkeys(buf, ":set noswapfile\")
+  call term_wait(buf)
+
+  " Modify the file directly.  Make sure the file modification time is
+  " different. Note that on Linux/Unix, the file is considered modified
+  " outside, only if the difference is 2 seconds or more
+  sleep 1
+  call writefile(["Line3", "Line4"], 'Xfile')
+  let new_ftime = getftime('Xfile')
+  while new_ftime - old_ftime < 2
+    sleep 100m
+    call writefile(["Line3", "Line4"], 'Xfile')
+    let new_ftime = getftime('Xfile')
+  endwhile
+
+  " Try to overwrite the file and check for the prompt
+  call term_sendkeys(buf, ":w\")
+  call term_wait(buf)
+  call WaitForAssert({-> assert_equal("WARNING: The file has been changed since reading it!!!", term_getline(buf, 9))})
+  call assert_equal("Do you really want to write to it (y/n)?",
+        \ term_getline(buf, 10))
+  call term_sendkeys(buf, "n\")
+  call term_wait(buf)
+  call assert_equal(new_ftime, getftime('Xfile'))
+  call term_sendkeys(buf, ":w\")
+  call term_wait(buf)
+  call term_sendkeys(buf, "y\")
+  call term_wait(buf)
+  call WaitForAssert({-> assert_equal('Line2', readfile('Xfile')[1])})
+
+  " clean up
+  call StopVimInTerminal(buf)
+  call delete('Xfile')
+endfunc
+
+" Test for an autocmd unloading a buffer during a write command
+func Test_write_autocmd_unloadbuf_lockmark()
+  augroup WriteTest
+    autocmd BufWritePre Xfile enew | write
+  augroup END
+  e Xfile
+  call assert_fails('lockmarks write', 'E203:')
+  augroup WriteTest
+    au!
+  augroup END
+  augroup! WriteTest
+endfunc
+
+" Test for writing a buffer with 'acwrite' but without autocmds
+func Test_write_acwrite_error()
+  new Xfile
+  call setline(1, ['line1', 'line2', 'line3'])
+  set buftype=acwrite
+  call assert_fails('write', 'E676:')
+  call assert_fails('1,2write!', 'E676:')
+  call assert_fails('w >>', 'E676:')
+  close!
+endfunc
+
+" Test for adding and removing lines from an autocmd when writing a buffer
+func Test_write_autocmd_add_remove_lines()
+  new Xfile
+  call setline(1, ['aaa', 'bbb', 'ccc', 'ddd'])
+
+  " Autocmd deleting lines from the file when writing a partial file
+  augroup WriteTest2
+    au!
+    autocmd FileWritePre Xfile 1,2d
+  augroup END
+  call assert_fails('2,3w!', 'E204:')
+
+  " Autocmd adding lines to a file when writing a partial file
+  augroup WriteTest2
+    au!
+    autocmd FileWritePre Xfile call append(0, ['xxx', 'yyy'])
+  augroup END
+  %d
+  call setline(1, ['aaa', 'bbb', 'ccc', 'ddd'])
+  1,2w!
+  call assert_equal(['xxx', 'yyy', 'aaa', 'bbb'], readfile('Xfile'))
+
+  " Autocmd deleting lines from the file when writing the whole file
+  augroup WriteTest2
+    au!
+    autocmd BufWritePre Xfile 1,2d
+  augroup END
+  %d
+  call setline(1, ['aaa', 'bbb', 'ccc', 'ddd'])
+  w
+  call assert_equal(['ccc', 'ddd'], readfile('Xfile'))
+
+  augroup WriteTest2
+    au!
+  augroup END
+  augroup! WriteTest2
+
+  close!
+  call delete('Xfile')
+endfunc
+
+" Test for writing to a readonly file
+func Test_write_readonly()
+  " In Cirrus-CI, the freebsd tests are run under a root account. So this test
+  " doesn't fail.
+  CheckNotBSD
+  call writefile([], 'Xfile')
+  call setfperm('Xfile', "r--------")
+  edit Xfile
+  set noreadonly
+  call assert_fails('write', 'E505:')
+  let save_cpo = &cpo
+  set cpo+=W
+  call assert_fails('write!', 'E504:')
+  let &cpo = save_cpo
+  call delete('Xfile')
 endfunc
 
 " Test for writing a file using invalid file encoding
-- 
cgit 


From b002499c1978c98cc4dfc3a3f2326997720d571a Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 08:22:46 +0800
Subject: vim-patch:8.2.0958: not sufficient testing for buffer writing

Problem:    Not sufficient testing for buffer writing.
Solution:   Add a few tests. (Yegappan Lakshmanan, closes vim/vim#6238)

https://github.com/vim/vim/commit/1de5f7c81d5e78fb4d612134bd2dfa6ee9183fae
---
 src/nvim/testdir/test_backup.vim    | 18 ++++++++++++++
 src/nvim/testdir/test_writefile.vim | 48 +++++++++++++++++++++++++++++++++++++
 2 files changed, 66 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_backup.vim b/src/nvim/testdir/test_backup.vim
index ce2bfe72bc..7eff818732 100644
--- a/src/nvim/testdir/test_backup.vim
+++ b/src/nvim/testdir/test_backup.vim
@@ -1,5 +1,7 @@
 " Tests for the backup function
 
+source check.vim
+
 func Test_backup()
   set backup backupdir=. backupskip=
   new
@@ -56,3 +58,19 @@ func Test_backup2_backupcopy()
   call delete(f)
   set backup&vim backupdir&vim backupcopy&vim backupskip&vim
 endfunc
+
+" Test for using a non-existing directory as a backup directory
+func Test_non_existing_backupdir()
+  throw 'Skipped: Nvim auto-creates backup directory'
+  CheckNotBSD
+  let save_backup = &backupdir
+  set backupdir=./non_existing_dir
+  call writefile(['line1'], 'Xfile')
+  new Xfile
+  " TODO: write doesn't fail in Cirrus FreeBSD CI test
+  call assert_fails('write', 'E510:')
+  let &backupdir = save_backup
+  call delete('Xfile')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim
index 59214d1aab..5ee13ef144 100644
--- a/src/nvim/testdir/test_writefile.vim
+++ b/src/nvim/testdir/test_writefile.vim
@@ -398,9 +398,57 @@ func Test_write_readonly()
   set cpo+=W
   call assert_fails('write!', 'E504:')
   let &cpo = save_cpo
+  call setline(1, ['line1'])
+  write!
+  call assert_equal(['line1'], readfile('Xfile'))
   call delete('Xfile')
 endfunc
 
+" Test for 'patchmode'
+func Test_patchmode()
+  CheckNotBSD
+  call writefile(['one'], 'Xfile')
+  set patchmode=.orig nobackup writebackup
+  new Xfile
+  call setline(1, 'two')
+  " first write should create the .orig file
+  write
+  " TODO: Xfile.orig is not created in Cirrus FreeBSD CI test
+  call assert_equal(['one'], readfile('Xfile.orig'))
+  call setline(1, 'three')
+  " subsequent writes should not create/modify the .orig file
+  write
+  call assert_equal(['one'], readfile('Xfile.orig'))
+  set patchmode& backup& writebackup&
+  call delete('Xfile')
+  call delete('Xfile.orig')
+endfunc
+
+" Test for writing to a file in a readonly directory
+func Test_write_readonly_dir()
+  if !has('unix') || has('bsd')
+    " On MS-Windows, modifying files in a read-only directory is allowed.
+    " In Cirrus-CI for Freebsd, tests are run under a root account where
+    " modifying files in a read-only directory are allowed.
+    return
+  endif
+  call mkdir('Xdir')
+  call writefile(['one'], 'Xdir/Xfile1')
+  call setfperm('Xdir', 'r-xr--r--')
+  " try to create a new file in the directory
+  new Xdir/Xfile2
+  call setline(1, 'two')
+  call assert_fails('write', 'E212:')
+  " try to create a backup file in the directory
+  edit! Xdir/Xfile1
+  set backupdir=./Xdir
+  set patchmode=.orig
+  call assert_fails('write', 'E509:')
+  call setfperm('Xdir', 'rwxr--r--')
+  call delete('Xdir', 'rf')
+  set backupdir& patchmode&
+endfunc
+
 " Test for writing a file using invalid file encoding
 func Test_write_invalid_encoding()
   new
-- 
cgit 


From 45ca7d4a62d9d6275339e9d9c0a4930448592712 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 11:17:10 +0800
Subject: vim-patch:8.2.0502: Vim9: some code is not tested

Problem:    Vim9: some code is not tested.
Solution:   Add more tests.  Fix uncovered problems.

https://github.com/vim/vim/commit/e8c4abbbd711af8fd3ed85ea69e9ac3d63a0d879

Co-authored-by: Bram Moolenaar 
---
 src/nvim/cmdexpand.c     |  4 ++--
 src/nvim/eval/userfunc.c |  2 +-
 src/nvim/ex_cmds.c       |  8 ++++----
 src/nvim/ex_docmd.c      |  4 ++--
 src/nvim/ex_eval.c       |  2 +-
 src/nvim/ex_getln.c      |  2 +-
 src/nvim/match.c         |  2 +-
 src/nvim/regexp.c        | 12 +++++++++++-
 src/nvim/search.c        |  2 +-
 src/nvim/syntax.c        |  4 ++--
 src/nvim/tag.c           |  4 ++--
 11 files changed, 28 insertions(+), 18 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index ca95a9a24e..480f3aa18c 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -1246,7 +1246,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
       arg = (const char *)skipwhite(skiptowhite(arg));
       if (*arg != NUL) {
         xp->xp_context = EXPAND_NOTHING;
-        arg = (const char *)skip_regexp((char *)arg + 1, (uint8_t)(*arg), p_magic, NULL);
+        arg = (const char *)skip_regexp((char *)arg + 1, (uint8_t)(*arg), p_magic);
       }
     }
     return (const char *)find_nextcmd(arg);
@@ -1285,7 +1285,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
     if (delim) {
       // Skip "from" part.
       arg++;
-      arg = (const char *)skip_regexp((char *)arg, delim, p_magic, NULL);
+      arg = (const char *)skip_regexp((char *)arg, delim, p_magic);
     }
     // Skip "to" part.
     while (arg[0] != NUL && (uint8_t)arg[0] != delim) {
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index 71f33fe465..4024410a04 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -1964,7 +1964,7 @@ void ex_function(exarg_T *eap)
 
   // ":function /pat": list functions matching pattern.
   if (*eap->arg == '/') {
-    p = skip_regexp(eap->arg + 1, '/', true, NULL);
+    p = skip_regexp(eap->arg + 1, '/', true);
     if (!eap->skip) {
       regmatch_T regmatch;
 
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 87c4f4e654..e79a587000 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -504,7 +504,7 @@ void ex_sort(exarg_T *eap)
       eap->nextcmd = check_nextcmd(p);
       break;
     } else if (!ASCII_ISALPHA(*p) && regmatch.regprog == NULL) {
-      s = skip_regexp(p + 1, *p, true, NULL);
+      s = skip_regexp(p + 1, *p, true);
       if (*s != *p) {
         emsg(_(e_invalpat));
         goto sortend;
@@ -3503,7 +3503,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
       which_pat = RE_LAST;                  // use last used regexp
       delimiter = (char_u)(*cmd++);                   // remember delimiter character
       pat = cmd;                            // remember start of search pat
-      cmd = skip_regexp(cmd, delimiter, p_magic, &eap->arg);
+      cmd = skip_regexp_ex(cmd, delimiter, p_magic, &eap->arg, NULL);
       if (cmd[0] == delimiter) {            // end delimiter found
         *cmd++ = NUL;                       // replace it with a NUL
         has_second_delim = true;
@@ -4536,7 +4536,7 @@ void ex_global(exarg_T *eap)
     delim = *cmd;               // get the delimiter
     cmd++;                      // skip delimiter if there is one
     pat = cmd;                  // remember start of pattern
-    cmd = skip_regexp(cmd, delim, p_magic, &eap->arg);
+    cmd = skip_regexp_ex(cmd, delim, p_magic, &eap->arg, NULL);
     if (cmd[0] == delim) {                  // end delimiter found
       *cmd++ = NUL;                         // replace it with a NUL
     }
@@ -4849,7 +4849,7 @@ char *skip_vimgrep_pat(char *p, char **s, int *flags)
       *s = p + 1;
     }
     c = (char_u)(*p);
-    p = skip_regexp(p + 1, c, true, NULL);
+    p = skip_regexp(p + 1, c, true);
     if (*p != c) {
       return NULL;
     }
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index d1a1b496f4..dad656d8b6 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -3351,7 +3351,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
         goto error;
       }
       if (skip) {                       // skip "/pat/"
-        cmd = skip_regexp(cmd, c, p_magic, NULL);
+        cmd = skip_regexp(cmd, c, p_magic);
         if (*cmd == c) {
           cmd++;
         }
@@ -6494,7 +6494,7 @@ static void ex_findpat(exarg_T *eap)
   if (*eap->arg == '/') {   // Match regexp, not just whole words
     whole = false;
     eap->arg++;
-    char *p = skip_regexp(eap->arg, '/', p_magic, NULL);
+    char *p = skip_regexp(eap->arg, '/', p_magic);
     if (*p) {
       *p++ = NUL;
       p = skipwhite(p);
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 2d6b236007..761c0770b6 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -1296,7 +1296,7 @@ void ex_catch(exarg_T *eap)
     eap->nextcmd = find_nextcmd(eap->arg);
   } else {
     pat = eap->arg + 1;
-    end = skip_regexp(pat, *eap->arg, true, NULL);
+    end = skip_regexp(pat, *eap->arg, true);
   }
 
   if (!give_up) {
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index a1e4bc96b5..1d242e4ed2 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -314,7 +314,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
   p = skipwhite(p);
   delim = (delim_optional && vim_isIDc(*p)) ? ' ' : *p++;
   *search_delim = delim;
-  end = skip_regexp(p, delim, p_magic, NULL);
+  end = skip_regexp(p, delim, p_magic);
 
   use_last_pat = end == p && *end == delim;
   if (end == p && !use_last_pat) {
diff --git a/src/nvim/match.c b/src/nvim/match.c
index b422dc0ba8..916bb44d8c 100644
--- a/src/nvim/match.c
+++ b/src/nvim/match.c
@@ -1206,7 +1206,7 @@ void ex_match(exarg_T *eap)
       semsg(_(e_invarg2), eap->arg);
       return;
     }
-    end = skip_regexp(p + 1, *p, true, NULL);
+    end = skip_regexp(p + 1, *p, true);
     if (!eap->skip) {
       if (*end != NUL && !ends_excmd(*skipwhite(end + 1))) {
         xfree(g);
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index d6f207a248..4ae7fc7578 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -484,10 +484,17 @@ static char_u *skip_anyof(char *p)
 /// Stop at end of "startp" or where "dirc" is found ('/', '?', etc).
 /// Take care of characters with a backslash in front of it.
 /// Skip strings inside [ and ].
+char *skip_regexp(char *startp, int dirc, int magic)
+{
+  return skip_regexp_ex(startp, dirc, magic, NULL, NULL);
+}
+
+/// skip_regexp() with extra arguments:
 /// When "newp" is not NULL and "dirc" is '?', make an allocated copy of the
 /// expression and change "\?" to "?".  If "*newp" is not NULL the expression
 /// is changed in-place.
-char *skip_regexp(char *startp, int dirc, int magic, char **newp)
+/// If a "\?" is changed to "?" then "dropped" is incremented, unless NULL.
+char *skip_regexp_ex(char *startp, int dirc, int magic, char **newp, int *dropped)
 {
   int mymagic;
   char *p = startp;
@@ -516,6 +523,9 @@ char *skip_regexp(char *startp, int dirc, int magic, char **newp)
           *newp = xstrdup(startp);
           p = *newp + (p - startp);
         }
+        if (dropped != NULL) {
+          (*dropped)++;
+        }
         STRMOVE(p, p + 1);
       } else {
         p++;            // skip next character
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 49892a4cc5..2f3e5a2cb6 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -1080,7 +1080,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
       // Find end of regular expression.
       // If there is a matching '/' or '?', toss it.
       ps = (char_u *)strcopy;
-      p = (char_u *)skip_regexp((char *)pat, search_delim, p_magic, &strcopy);
+      p = (char_u *)skip_regexp_ex((char *)pat, search_delim, p_magic, &strcopy, NULL);
       if (strcopy != (char *)ps) {
         // made a copy of "pat" to change "\?" to "?"
         searchcmdlen += (int)(STRLEN(pat) - strlen(strcopy));
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index e5962cd273..fb82df4fe9 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -4749,7 +4749,7 @@ static char *get_syn_pattern(char *arg, synpat_T *ci)
     return NULL;
   }
 
-  end = skip_regexp(arg + 1, *arg, true, NULL);
+  end = skip_regexp(arg + 1, *arg, true);
   if (*end != *arg) {                       // end delimiter not found
     semsg(_("E401: Pattern delimiter not found: %s"), arg);
     return NULL;
@@ -4902,7 +4902,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
         finished = true;
         break;
       }
-      arg_end = skip_regexp(next_arg + 1, *next_arg, true, NULL);
+      arg_end = skip_regexp(next_arg + 1, *next_arg, true);
       if (*arg_end != *next_arg) {          // end delimiter not found
         illegal = true;
         break;
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 90b21320d2..264f961b43 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -2670,7 +2670,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
     // anything following.
     str = pbuf;
     if (pbuf[0] == '/' || pbuf[0] == '?') {
-      str = (char_u *)skip_regexp((char *)pbuf + 1, pbuf[0], false, NULL) + 1;
+      str = (char_u *)skip_regexp((char *)pbuf + 1, pbuf[0], false) + 1;
     }
     if (str > pbuf_end - 1) {   // search command with nothing following
       save_p_ws = p_ws;
@@ -2883,7 +2883,7 @@ static int find_extra(char_u **pp)
     if (ascii_isdigit(*str)) {
       str = (char_u *)skipdigits((char *)str + 1);
     } else if (*str == '/' || *str == '?') {
-      str = (char_u *)skip_regexp((char *)str + 1, *str, false, NULL);
+      str = (char_u *)skip_regexp((char *)str + 1, *str, false);
       if (*str != first_char) {
         str = NULL;
       } else {
-- 
cgit 


From 199c7c28989a3c36447ef56b71c7b84756950a11 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 11:27:28 +0800
Subject: vim-patch:8.2.0612: Vim9: no check for space before #comment

Problem:    Vim9: no check for space before #comment.
Solution:   Add space checks.

https://github.com/vim/vim/commit/2c5ed4e3300378ce76c8d9c3818d6f73e5119f68

Omit ends_excmd2(): the same as ends_excmd() in legacy Vim script.

Co-authored-by: Bram Moolenaar 
---
 src/nvim/ex_cmds.c             |  5 ++---
 src/nvim/ex_eval.c             |  7 +++++--
 src/nvim/highlight_group.c     |  2 +-
 src/nvim/regexp.c              | 19 ++++++++++++++++---
 src/nvim/testdir/test_sort.vim |  2 +-
 5 files changed, 25 insertions(+), 10 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index e79a587000..89e6d47950 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -504,9 +504,8 @@ void ex_sort(exarg_T *eap)
       eap->nextcmd = check_nextcmd(p);
       break;
     } else if (!ASCII_ISALPHA(*p) && regmatch.regprog == NULL) {
-      s = skip_regexp(p + 1, *p, true);
-      if (*s != *p) {
-        emsg(_(e_invalpat));
+      s = skip_regexp_err(p + 1, *p, true);
+      if (s == NULL) {
         goto sortend;
       }
       *s = NUL;
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 761c0770b6..8c2ac895cb 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -900,12 +900,12 @@ void ex_else(exarg_T *eap)
   if (eap->cmdidx == CMD_elseif) {
     bool error;
     result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
+
     // When throwing error exceptions, we want to throw always the first
     // of several errors in a row.  This is what actually happens when
     // a conditional error was detected above and there is another failure
     // when parsing the expression.  Since the skip flag is set in this
     // case, the parsing error will be ignored by emsg().
-
     if (!skip && !error) {
       if (result) {
         cstack->cs_flags[cstack->cs_idx] = CSF_ACTIVE | CSF_TRUE;
@@ -1296,7 +1296,10 @@ void ex_catch(exarg_T *eap)
     eap->nextcmd = find_nextcmd(eap->arg);
   } else {
     pat = eap->arg + 1;
-    end = skip_regexp(pat, *eap->arg, true);
+    end = skip_regexp_err(pat, *eap->arg, true);
+    if (end == NULL) {
+      give_up = true;
+    }
   }
 
   if (!give_up) {
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index 4c253480be..9a09118939 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -851,7 +851,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
   bool did_highlight_changed = false;
 
   // If no argument, list current highlighting.
-  if (ends_excmd((uint8_t)(*line))) {
+  if (!init && ends_excmd((uint8_t)(*line))) {
     for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) {
       // TODO(brammool): only call when the group has attributes set
       highlight_list_one(i);
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 4ae7fc7578..68ffc70457 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -481,12 +481,25 @@ static char_u *skip_anyof(char *p)
 }
 
 /// Skip past regular expression.
-/// Stop at end of "startp" or where "dirc" is found ('/', '?', etc).
+/// Stop at end of "startp" or where "delim" is found ('/', '?', etc).
 /// Take care of characters with a backslash in front of it.
 /// Skip strings inside [ and ].
-char *skip_regexp(char *startp, int dirc, int magic)
+char *skip_regexp(char *startp, int delim, int magic)
 {
-  return skip_regexp_ex(startp, dirc, magic, NULL, NULL);
+  return skip_regexp_ex(startp, delim, magic, NULL, NULL);
+}
+
+/// Call skip_regexp() and when the delimiter does not match give an error and
+/// return NULL.
+char *skip_regexp_err(char *startp, int delim, int magic)
+{
+  char *p = skip_regexp(startp, delim, magic);
+
+  if (*p != delim) {
+    semsg(_("E654: missing delimiter after search pattern: %s"), startp);
+    return NULL;
+  }
+  return p;
 }
 
 /// skip_regexp() with extra arguments:
diff --git a/src/nvim/testdir/test_sort.vim b/src/nvim/testdir/test_sort.vim
index c3e7788164..f9cbcbb55f 100644
--- a/src/nvim/testdir/test_sort.vim
+++ b/src/nvim/testdir/test_sort.vim
@@ -1360,7 +1360,7 @@ func Test_sort_cmd()
   call setline(1, ['line1', 'line2'])
   call assert_fails('sort no', 'E474:')
   call assert_fails('sort c', 'E475:')
-  call assert_fails('sort #pat%', 'E682:')
+  call assert_fails('sort #pat%', 'E654:')
 
   enew!
 endfunc
-- 
cgit 


From daf9a63d67254342382cf79f1cd216f8e5722579 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Sat, 5 Nov 2022 12:20:14 +0800
Subject: version.c: update [skip ci] (#20825)

N/A patches:
vim-patch 9.0.0829: wrong counts in macro comment
---
 src/nvim/version.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/version.c b/src/nvim/version.c
index e76d3ef9bf..98f34ca11f 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -669,7 +669,7 @@ static const int included_patches[] = {
   1829,
   1828,
   // 1827,
-  // 1826,
+  1826,
   1825,
   1824,
   1823,
-- 
cgit 


From a86295cd5c2bf15a11eb05e226fd8e226154f6a6 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 12:26:17 +0800
Subject: vim-patch:8.2.0615: regexp benchmark stest is old style (#20940)

Problem:    Regexp benchmark stest is old style.
Solution:   Make it a new style test.  Fix using a NULL list.  Add more tests.
            (Yegappan Lakshmanan, closes vim/vim#5963)

https://github.com/vim/vim/commit/ad48e6c1590842ab6d48e6caba3e9250734dae27

N/A patches:
vim-patch:9.0.0829: wrong counts in macro comment
---
 src/nvim/eval.c                      |  8 ++++++++
 src/nvim/testdir/test_autocmd.vim    |  2 ++
 src/nvim/testdir/test_blob.vim       |  1 +
 src/nvim/testdir/test_bufline.vim    | 31 +++++++++++++++++++++++++++++++
 src/nvim/testdir/test_cmdline.vim    |  5 +++++
 src/nvim/testdir/test_functions.vim  | 25 +++++++++++++++++++++++++
 src/nvim/testdir/test_tagjump.vim    |  1 +
 src/nvim/testdir/test_window_cmd.vim | 12 ++++++++++++
 8 files changed, 85 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index bc669a3e11..37da475e59 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -5581,6 +5581,13 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T
   const char *line = NULL;
   if (lines->v_type == VAR_LIST) {
     l = lines->vval.v_list;
+    if (l == NULL || tv_list_len(l) == 0) {
+      // set proper return code
+      if (lnum > curbuf->b_ml.ml_line_count) {
+        rettv->vval.v_number = 1;       // FAIL
+      }
+      goto done;
+    }
     li = tv_list_first(l);
   } else {
     line = tv_get_string_chk(lines);
@@ -5651,6 +5658,7 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T
     update_topline(curwin);
   }
 
+done:
   if (!is_curbuf) {
     curbuf = curbuf_save;
     curwin = curwin_save;
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 8c15249f97..a3534cea87 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -533,6 +533,8 @@ func Test_augroup_warning()
   redir END
   call assert_notmatch("W19:", res)
   au! VimEnter
+
+  call assert_fails('augroup!', 'E471:')
 endfunc
 
 func Test_BufReadCmdHelp()
diff --git a/src/nvim/testdir/test_blob.vim b/src/nvim/testdir/test_blob.vim
index 1c0261933f..046acb81e1 100644
--- a/src/nvim/testdir/test_blob.vim
+++ b/src/nvim/testdir/test_blob.vim
@@ -303,6 +303,7 @@ func Test_blob_index()
   call assert_equal(3, index(0z11110111, 0x11, -2))
   call assert_equal(0, index(0z11110111, 0x11, -10))
   call assert_fails("echo index(0z11110111, 0x11, [])", 'E745:')
+  call assert_equal(-1, index(v:_null_blob, 1))
 
   call assert_fails('call index("asdf", 0)', 'E897:')
 endfunc
diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim
index 5a47f8ef25..2867f13cbc 100644
--- a/src/nvim/testdir/test_bufline.vim
+++ b/src/nvim/testdir/test_bufline.vim
@@ -19,8 +19,19 @@ func Test_setbufline_getbufline()
   call setline(1, ['a', 'b', 'c'])
   let b = bufnr('%')
   wincmd w
+
+  call assert_equal(1, setbufline(b, 5, 'x'))
   call assert_equal(1, setbufline(b, 5, ['x']))
+  call assert_equal(1, setbufline(b, 5, []))
+  call assert_equal(1, setbufline(b, 5, v:_null_list))
+
+  call assert_equal(1, 'x'->setbufline(bufnr('$') + 1, 1))
   call assert_equal(1, ['x']->setbufline(bufnr('$') + 1, 1))
+  call assert_equal(1, []->setbufline(bufnr('$') + 1, 1))
+  call assert_equal(1, v:_null_list->setbufline(bufnr('$') + 1, 1))
+
+  call assert_equal(['a', 'b', 'c'], getbufline(b, 1, '$'))
+
   call assert_equal(0, setbufline(b, 4, ['d', 'e']))
   call assert_equal(['c'], b->getbufline(3))
   call assert_equal(['d'], getbufline(b, 4))
@@ -84,9 +95,29 @@ func Test_appendbufline()
   call setline(1, ['a', 'b', 'c'])
   let b = bufnr('%')
   wincmd w
+
+  call assert_equal(1, appendbufline(b, -1, 'x'))
   call assert_equal(1, appendbufline(b, -1, ['x']))
+  call assert_equal(1, appendbufline(b, -1, []))
+  call assert_equal(1, appendbufline(b, -1, v:_null_list))
+
+  call assert_equal(1, appendbufline(b, 4, 'x'))
   call assert_equal(1, appendbufline(b, 4, ['x']))
+  call assert_equal(1, appendbufline(b, 4, []))
+  call assert_equal(1, appendbufline(b, 4, v:_null_list))
+
+  call assert_equal(1, appendbufline(1234, 1, 'x'))
   call assert_equal(1, appendbufline(1234, 1, ['x']))
+  call assert_equal(1, appendbufline(1234, 1, []))
+  call assert_equal(1, appendbufline(1234, 1, v:_null_list))
+
+  call assert_equal(0, appendbufline(b, 1, []))
+  call assert_equal(0, appendbufline(b, 1, v:_null_list))
+  call assert_equal(1, appendbufline(b, 3, []))
+  call assert_equal(1, appendbufline(b, 3, v:_null_list))
+
+  call assert_equal(['a', 'b', 'c'], getbufline(b, 1, '$'))
+
   call assert_equal(0, appendbufline(b, 3, ['d', 'e']))
   call assert_equal(['c'], getbufline(b, 3))
   call assert_equal(['d'], getbufline(b, 4))
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 27ac91e49f..3e5fe06c90 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -1270,6 +1270,11 @@ func Test_verbosefile()
   let log = readfile('Xlog')
   call assert_match("foo\nbar", join(log, "\n"))
   call delete('Xlog')
+  call mkdir('Xdir')
+  if !has('win32')  " FIXME: no error on Windows, libuv bug?
+  call assert_fails('set verbosefile=Xdir', 'E474:')
+  endif
+  call delete('Xdir', 'd')
 endfunc
 
 func Test_verbose_option()
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 5718266dae..1ba0cf9080 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -798,18 +798,41 @@ func Test_mode()
   delfunction OperatorFunc
 endfunc
 
+" Test for append()
 func Test_append()
   enew!
   split
   call append(0, ["foo"])
+  call append(1, [])
+  call append(1, v:_null_list)
+  call assert_equal(['foo', ''], getline(1, '$'))
   split
   only
   undo
+  undo
 
   " Using $ instead of '$' must give an error
   call assert_fails("call append($, 'foobar')", 'E116:')
 endfunc
 
+" Test for setline()
+func Test_setline()
+  new
+  call setline(0, ["foo"])
+  call setline(0, [])
+  call setline(0, v:_null_list)
+  call setline(1, ["bar"])
+  call setline(1, [])
+  call setline(1, v:_null_list)
+  call setline(2, [])
+  call setline(2, v:_null_list)
+  call setline(3, [])
+  call setline(3, v:_null_list)
+  call setline(2, ["baz"])
+  call assert_equal(['bar', 'baz'], getline(1, '$'))
+  close!
+endfunc
+
 func Test_getbufvar()
   let bnr = bufnr('%')
   let b:var_num = '1234'
@@ -917,6 +940,7 @@ func Test_match_func()
   call assert_equal(-1, match(['a', 'b', 'c', 'a'], 'a', 5))
   call assert_equal(4,  match('testing', 'ing', -1))
   call assert_fails("let x=match('testing', 'ing', 0, [])", 'E745:')
+  call assert_equal(-1, match(v:_null_list, 2))
 endfunc
 
 func Test_matchend()
@@ -1922,6 +1946,7 @@ func Test_call()
   call assert_equal(3, 'len'->call([123]))
   call assert_fails("call call('len', 123)", 'E714:')
   call assert_equal(0, call('', []))
+  call assert_equal(0, call('len', v:_null_list))
 
   function Mylen() dict
      return len(self.data)
diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim
index 592e13e340..f3ca5e306b 100644
--- a/src/nvim/testdir/test_tagjump.vim
+++ b/src/nvim/testdir/test_tagjump.vim
@@ -372,6 +372,7 @@ func Test_getsettagstack()
   call assert_fails("call settagstack(1, {'items' : 10})", 'E714')
   call assert_fails("call settagstack(1, {'items' : []}, 10)", 'E928')
   call assert_fails("call settagstack(1, {'items' : []}, 'b')", 'E962')
+  call assert_equal(-1, settagstack(0, v:_null_dict))
 
   set tags=Xtags
   call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim
index c7b5896082..20564fed66 100644
--- a/src/nvim/testdir/test_window_cmd.vim
+++ b/src/nvim/testdir/test_window_cmd.vim
@@ -1142,6 +1142,18 @@ func Test_split_cmds_with_no_room()
   call Run_noroom_for_newwindow_test('v')
 endfunc
 
+" Test for various wincmd failures
+func Test_wincmd_fails()
+  only!
+  call assert_beeps("normal \w")
+  call assert_beeps("normal \p")
+  call assert_beeps("normal \gk")
+  call assert_beeps("normal \r")
+  call assert_beeps("normal \K")
+  call assert_beeps("normal \H")
+  call assert_beeps("normal \2gt")
+endfunc
+
 func Test_window_resize()
   " Vertical :resize (absolute, relative, min and max size).
   vsplit
-- 
cgit 


From 8b0c5de4e0964109326a0befb1b3bff6aac4d0db Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 07:24:06 +0800
Subject: vim-patch:partial:8.2.1183: assert_fails() checks the last error
 message

Problem:    assert_fails() checks the last error message.
Solution:   Check the first error, it is more relevant.  Fix all the tests
            that rely on the old behavior.

https://github.com/vim/vim/commit/9b7bf9e98f06ece595fed7a3ff53ecce89797a53

Skip test_listener.vim, test_textprop.vim, test_viminfo.vim.
Skip test_python2.vim: affected line fails and hasn't been ported.
Skip test_python3.vim: affected lines fail and haven't been ported.
Skip CHECK_LIST_MATERIALIZE.

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval.c                        | 12 ++++----
 src/nvim/globals.h                     |  4 +++
 src/nvim/message.c                     |  4 +++
 src/nvim/testdir/test_autochdir.vim    |  4 +--
 src/nvim/testdir/test_autocmd.vim      |  6 ++--
 src/nvim/testdir/test_buffer.vim       |  4 +--
 src/nvim/testdir/test_cd.vim           |  8 ++----
 src/nvim/testdir/test_clientserver.vim |  6 ++--
 src/nvim/testdir/test_cmdline.vim      | 14 +++-------
 src/nvim/testdir/test_cpoptions.vim    |  2 +-
 src/nvim/testdir/test_excmd.vim        |  4 +--
 src/nvim/testdir/test_expr.vim         |  2 +-
 src/nvim/testdir/test_functions.vim    | 10 +++----
 src/nvim/testdir/test_global.vim       |  2 +-
 src/nvim/testdir/test_let.vim          |  2 +-
 src/nvim/testdir/test_listdict.vim     |  6 ++--
 src/nvim/testdir/test_match.vim        |  2 +-
 src/nvim/testdir/test_matchfuzzy.vim   | 18 ++++--------
 src/nvim/testdir/test_menu.vim         |  2 +-
 src/nvim/testdir/test_method.vim       |  4 +--
 src/nvim/testdir/test_normal.vim       |  2 +-
 src/nvim/testdir/test_quickfix.vim     |  6 ++--
 src/nvim/testdir/test_random.vim       | 10 +++----
 src/nvim/testdir/test_regexp_latin.vim |  2 +-
 src/nvim/testdir/test_search.vim       |  8 +++---
 src/nvim/testdir/test_signs.vim        |  6 ++--
 src/nvim/testdir/test_spell.vim        |  2 +-
 src/nvim/testdir/test_substitute.vim   |  8 +++---
 src/nvim/testdir/test_syntax.vim       |  2 +-
 src/nvim/testdir/test_tagfunc.vim      |  2 +-
 src/nvim/testdir/test_tagjump.vim      |  4 +--
 src/nvim/testdir/test_taglist.vim      |  4 +--
 src/nvim/testdir/test_trycatch.vim     |  6 ++--
 src/nvim/testdir/test_utf8.vim         |  2 +-
 src/nvim/testdir/test_winbuf_close.vim |  2 +-
 src/nvim/testdir/test_writefile.vim    |  2 +-
 src/nvim/testing.c                     | 50 ++++++++++++++++++++++++++++++----
 37 files changed, 127 insertions(+), 107 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 37da475e59..5b8cbcfbb3 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -545,7 +545,7 @@ int var_redir_start(char *name, int append)
 
   // check if we can write to the variable: set it to or append an empty
   // string
-  int save_emsg = did_emsg;
+  const int called_emsg_before = called_emsg;
   did_emsg = false;
   typval_T tv;
   tv.v_type = VAR_STRING;
@@ -556,9 +556,7 @@ int var_redir_start(char *name, int append)
     set_var_lval(redir_lval, redir_endp, &tv, true, false, "=");
   }
   clear_lval(redir_lval);
-  int err = did_emsg;
-  did_emsg |= save_emsg;
-  if (err) {
+  if (called_emsg > called_emsg_before) {
     redir_endp = NULL;      // don't store a value, only cleanup
     var_redir_stop();
     return FAIL;
@@ -2185,7 +2183,7 @@ char *get_user_var_name(expand_T *xp, int idx)
 /// Does not use 'cpo' and always uses 'magic'.
 ///
 /// @return  true if "pat" matches "text".
-int pattern_match(char *pat, char *text, bool ic)
+int pattern_match(const char *pat, const char *text, bool ic)
 {
   int matches = 0;
   regmatch_T regmatch;
@@ -2193,7 +2191,7 @@ int pattern_match(char *pat, char *text, bool ic)
   // avoid 'l' flag in 'cpoptions'
   char *save_cpo = p_cpo;
   p_cpo = empty_option;
-  regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
+  regmatch.regprog = vim_regcomp((char *)pat, RE_MAGIC + RE_STRING);
   if (regmatch.regprog != NULL) {
     regmatch.rm_ic = ic;
     matches = vim_regexec_nl(®match, (char_u *)text, (colnr_T)0);
@@ -8881,7 +8879,7 @@ int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic)
 
     case EXPR_MATCH:
     case EXPR_NOMATCH:
-      n1 = pattern_match((char *)s2, (char *)s1, ic);
+      n1 = pattern_match(s2, s1, ic);
       if (type == EXPR_NOMATCH) {
         n1 = !n1;
       }
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index c556aac1fd..d7b2164d12 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -194,6 +194,10 @@ EXTERN int emsg_skip INIT(= 0);             // don't display errors for
                                             // expression that is skipped
 EXTERN bool emsg_severe INIT(= false);      // use message of next of several
                                             //  emsg() calls for throw
+// used by assert_fails()
+EXTERN bool emsg_assert_fails_used INIT(= false);
+EXTERN char *emsg_assert_fails_msg INIT(= NULL);
+
 EXTERN bool did_endif INIT(= false);        // just had ":endif"
 EXTERN dict_T vimvardict;                   // Dictionary with v: variables
 EXTERN dict_T globvardict;                  // Dictionary with g: variables
diff --git a/src/nvim/message.c b/src/nvim/message.c
index b608b59c9b..4837f4131a 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -663,6 +663,10 @@ static bool emsg_multiline(const char *s, bool multiline)
       return true;
     }
 
+    if (emsg_assert_fails_used && emsg_assert_fails_msg == NULL) {
+      emsg_assert_fails_msg = xstrdup(s);
+    }
+
     // set "v:errmsg", also when using ":silent! cmd"
     set_vim_var_string(VV_ERRMSG, s, -1);
 
diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim
index 4229095f9f..a8810047a0 100644
--- a/src/nvim/testdir/test_autochdir.vim
+++ b/src/nvim/testdir/test_autochdir.vim
@@ -122,9 +122,7 @@ endfunc
 func Test_multibyte()
   " using an invalid character should not cause a crash
   set wic
-  " Except on Windows, E472 is also thrown last, but v8.1.1183 isn't ported yet
-  " call assert_fails('tc û¦*', has('win32') ? 'E480:' : 'E344:')
-  call assert_fails('tc û¦*', has('win32') ? 'E480:' : 'E472:')
+  call assert_fails('tc û¦*', has('win32') ? 'E480:' : 'E344:')
   set nowic
 endfunc
 
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index a3534cea87..50904bab34 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -175,9 +175,7 @@ func Test_autocmd_bufunload_avoiding_SEGV_01()
     exe 'autocmd BufUnload  ' . (lastbuf + 1) . 'bwipeout!'
   augroup END
 
-  " Todo: check for E937 generated first
-  " call assert_fails('edit bb.txt', 'E937:')
-  call assert_fails('edit bb.txt', 'E517:')
+  call assert_fails('edit bb.txt', ['E937:', 'E517:'])
 
   autocmd! test_autocmd_bufunload
   augroup! test_autocmd_bufunload
@@ -2933,7 +2931,7 @@ func Test_BufDelete_changebuf()
   augroup END
   let save_cpo = &cpo
   set cpo+=f
-  call assert_fails('r Xfile', 'E484:')
+  call assert_fails('r Xfile', ['E812:', 'E484:'])
   call assert_equal('somefile', @%)
   let &cpo = save_cpo
   augroup TestAuCmd
diff --git a/src/nvim/testdir/test_buffer.vim b/src/nvim/testdir/test_buffer.vim
index 4def3b5df9..27c2d5d442 100644
--- a/src/nvim/testdir/test_buffer.vim
+++ b/src/nvim/testdir/test_buffer.vim
@@ -76,7 +76,7 @@ func Test_bunload_with_offset()
     let caught_E90 = 1
   endtry
   call assert_equal(1, caught_E90)
-  call assert_fails('$bunload', 'E515:')
+  call assert_fails('$bunload', 'E90:')
 endfunc
 
 " Test for :buffer, :bnext, :bprevious, :brewind, :blast and :bmodified
@@ -282,7 +282,7 @@ func Test_goto_buf_with_confirm()
   call assert_equal(1, &modified)
   call assert_equal('', @%)
   call feedkeys('y', 'L')
-  call assert_fails('confirm b Xfile', 'E37:')
+  call assert_fails('confirm b Xfile', ['', 'E37:'])
   call assert_equal(1, &modified)
   call assert_equal('', @%)
   call feedkeys('n', 'L')
diff --git a/src/nvim/testdir/test_cd.vim b/src/nvim/testdir/test_cd.vim
index 43c4e09d40..2a2437f542 100644
--- a/src/nvim/testdir/test_cd.vim
+++ b/src/nvim/testdir/test_cd.vim
@@ -5,7 +5,7 @@ source check.vim
 
 func Test_cd_large_path()
   " This used to crash with a heap write overflow.
-  call assert_fails('cd ' . repeat('x', 5000), 'E472:')
+  call assert_fails('cd ' . repeat('x', 5000), 'E344:')
 endfunc
 
 func Test_cd_up_and_down()
@@ -45,9 +45,7 @@ func Test_cd_minus()
   call assert_equal(path, getcwd())
 
   " Test for :cd - after a failed :cd
-  " v8.2.1183 is not ported yet
-  " call assert_fails('cd /nonexistent', 'E344:')
-  call assert_fails('cd /nonexistent', 'E472:')
+  call assert_fails('cd /nonexistent', 'E344:')
   call assert_equal(path, getcwd())
   cd -
   call assert_equal(path_dotdot, getcwd())
@@ -103,7 +101,7 @@ func Test_chdir_func()
   call assert_equal('testdir', fnamemodify(getcwd(1, 1), ':t'))
 
   " Error case
-  call assert_fails("call chdir('dir-abcd')", 'E472:')
+  call assert_fails("call chdir('dir-abcd')", 'E344:')
   silent! let d = chdir("dir_abcd")
   call assert_equal("", d)
   " Should not crash
diff --git a/src/nvim/testdir/test_clientserver.vim b/src/nvim/testdir/test_clientserver.vim
index 301dad8341..19a92dce3c 100644
--- a/src/nvim/testdir/test_clientserver.vim
+++ b/src/nvim/testdir/test_clientserver.vim
@@ -102,7 +102,7 @@ func Test_client_server()
   call remote_send(v:servername, ":let g:testvar2 = 75\")
   call feedkeys('', 'x')
   call assert_equal(75, g:testvar2)
-  call assert_fails('let v = remote_expr(v:servername, "/2")', 'E449:')
+  call assert_fails('let v = remote_expr(v:servername, "/2")', ['E15:.*/2'])
 
   call remote_send(name, ":call server2client(expand(''), 'got it')\", 'g:myserverid')
   call assert_equal('got it', g:myserverid->remote_read(2))
@@ -184,8 +184,8 @@ func Test_client_server()
 
   call assert_fails('call remote_startserver([])', 'E730:')
   call assert_fails("let x = remote_peek([])", 'E730:')
-  call assert_fails("let x = remote_read('vim10')", 'E277:')
-  call assert_fails("call server2client('abc', 'xyz')", 'E258:')
+  call assert_fails("let x = remote_read('vim10')", ['E573:.*vim10'])
+  call assert_fails("call server2client('abc', 'xyz')", ['E573:.*abc'])
 endfunc
 
 " Uncomment this line to get a debugging log
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 3e5fe06c90..c00172ed68 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -1154,7 +1154,7 @@ func Test_cmdline_search_range()
   call assert_equal('B', getline(2))
 
   let @/ = 'apple'
-  call assert_fails('\/print', 'E486:')
+  call assert_fails('\/print', ['E486:.*apple'])
 
   bwipe!
 endfunc
@@ -1272,7 +1272,7 @@ func Test_verbosefile()
   call delete('Xlog')
   call mkdir('Xdir')
   if !has('win32')  " FIXME: no error on Windows, libuv bug?
-  call assert_fails('set verbosefile=Xdir', 'E474:')
+  call assert_fails('set verbosefile=Xdir', ['E484:.*Xdir', 'E474:'])
   endif
   call delete('Xdir', 'd')
 endfunc
@@ -1525,7 +1525,7 @@ func Test_cmdwin_jump_to_win()
   call assert_fails('call feedkeys("q:\\\", "xt")', 'E11:')
   new
   set modified
-  call assert_fails('call feedkeys("q/:qall\", "xt")', 'E162:')
+  call assert_fails('call feedkeys("q/:qall\", "xt")', ['E37:', 'E162:'])
   close!
   call feedkeys("q/:close\", "xt")
   call assert_equal(1, winnr('$'))
@@ -1539,13 +1539,7 @@ endfunc
 
 func Test_cmdwin_tabpage()
   tabedit
-  " v8.2.1919 isn't ported yet, so E492 is thrown after E11 here.
-  " v8.2.1183 also isn't ported yet, so we also can't assert E11 directly.
-  " For now, assert E11 and E492 separately. When v8.2.1183 is ported, the
-  " assert for E492 will fail and this workaround should be removed.
-  " call assert_fails("silent norm q/g	:I\", 'E11:')
-  call assert_fails("silent norm q/g	", 'E11:')
-  call assert_fails("silent norm q/g	:I\", 'E492:')
+  call assert_fails("silent norm q/g	:I\", 'E11:')
   tabclose!
 endfunc
 
diff --git a/src/nvim/testdir/test_cpoptions.vim b/src/nvim/testdir/test_cpoptions.vim
index 76d2c9542d..ef51d955f1 100644
--- a/src/nvim/testdir/test_cpoptions.vim
+++ b/src/nvim/testdir/test_cpoptions.vim
@@ -104,7 +104,7 @@ func Test_cpo_C()
   source Xfile
   call assert_equal([1, 2], g:l)
   set cpo+=C
-  call assert_fails('source Xfile', 'E10:')
+  call assert_fails('source Xfile', ['E697:', 'E10:'])
   call delete('Xfile')
   let &cpo = save_cpo
 endfunc
diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim
index 04ab8e288f..582dcaac2c 100644
--- a/src/nvim/testdir/test_excmd.vim
+++ b/src/nvim/testdir/test_excmd.vim
@@ -481,8 +481,8 @@ func Test_redir_cmd()
   call assert_fails('redir abc', 'E475:')
   call assert_fails('redir => 1abc', 'E474:')
   call assert_fails('redir => a b', 'E488:')
-  call assert_fails('redir => abc[1]', 'E475:')
-  let b=0zFF
+  call assert_fails('redir => abc[1]', 'E121:')
+  let b = 0zFF
   call assert_fails('redir =>> b', 'E734:')
   unlet b
 
diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim
index 0579ce7dcb..ea874cc398 100644
--- a/src/nvim/testdir/test_expr.vim
+++ b/src/nvim/testdir/test_expr.vim
@@ -528,7 +528,7 @@ func Test_setmatches()
   endif
   eval set->setmatches()
   call assert_equal(exp, getmatches())
-  call assert_fails('let m = setmatches([], [])', 'E957:')
+  call assert_fails('let m = setmatches([], [])', 'E745:')
 endfunc
 
 func Test_empty_concatenate()
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 1ba0cf9080..06c995db86 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -211,7 +211,7 @@ func Test_str2nr()
   if has('float')
     call assert_fails('call str2nr(1.2)', 'E806:')
   endif
-  call assert_fails('call str2nr(10, [])', 'E474:')
+  call assert_fails('call str2nr(10, [])', 'E745:')
 endfunc
 
 func Test_strftime()
@@ -1728,11 +1728,11 @@ func Test_libcall_libcallnr()
   call assert_equal(4, 'abcd'->libcallnr(libc, 'strlen'))
   call assert_equal(char2nr('A'), char2nr('a')->libcallnr(libc, 'toupper'))
 
-  call assert_fails("call libcall(libc, 'Xdoesnotexist_', '')", 'E364:')
-  call assert_fails("call libcallnr(libc, 'Xdoesnotexist_', '')", 'E364:')
+  call assert_fails("call libcall(libc, 'Xdoesnotexist_', '')", ['', 'E364:'])
+  call assert_fails("call libcallnr(libc, 'Xdoesnotexist_', '')", ['', 'E364:'])
 
-  call assert_fails("call libcall('Xdoesnotexist_', 'getenv', 'HOME')", 'E364:')
-  call assert_fails("call libcallnr('Xdoesnotexist_', 'strlen', 'abcd')", 'E364:')
+  call assert_fails("call libcall('Xdoesnotexist_', 'getenv', 'HOME')", ['', 'E364:'])
+  call assert_fails("call libcallnr('Xdoesnotexist_', 'strlen', 'abcd')", ['', 'E364:'])
 endfunc
 
 sandbox function Fsandbox()
diff --git a/src/nvim/testdir/test_global.vim b/src/nvim/testdir/test_global.vim
index cb6851250c..bbfe374f51 100644
--- a/src/nvim/testdir/test_global.vim
+++ b/src/nvim/testdir/test_global.vim
@@ -39,7 +39,7 @@ endfunc
 func Test_global_error()
   call assert_fails('g\\a', 'E10:')
   call assert_fails('g', 'E148:')
-  call assert_fails('g/\(/y', 'E476:')
+  call assert_fails('g/\(/y', 'E54:')
 endfunc
 
 " Test for printing lines using :g with different search patterns
diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim
index 937076aa2a..f05e06f774 100644
--- a/src/nvim/testdir/test_let.vim
+++ b/src/nvim/testdir/test_let.vim
@@ -262,7 +262,7 @@ func Test_let_errors()
   let l = [1, 2, 3]
   call assert_fails('let l[:] = 5', 'E709:')
 
-  call assert_fails('let x:lnum=5', 'E488:')
+  call assert_fails('let x:lnum=5', ['E121:', 'E488:'])
   call assert_fails('let v:=5', 'E461:')
   call assert_fails('let [a]', 'E474:')
   call assert_fails('let [a, b] = [', 'E697:')
diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim
index 1ecdcd2157..7cb48876e8 100644
--- a/src/nvim/testdir/test_listdict.vim
+++ b/src/nvim/testdir/test_listdict.vim
@@ -680,10 +680,10 @@ func Test_reverse_sort_uniq()
   endif
 
   call assert_fails('call reverse("")', 'E899:')
-  call assert_fails('call uniq([1, 2], {x, y -> []})', 'E882:')
+  call assert_fails('call uniq([1, 2], {x, y -> []})', 'E745:')
   call assert_fails("call sort([1, 2], function('min'), 1)", "E715:")
   call assert_fails("call sort([1, 2], function('invalid_func'))", "E700:")
-  call assert_fails("call sort([1, 2], function('min'))", "E702:")
+  call assert_fails("call sort([1, 2], function('min'))", "E118:")
 endfunc
 
 " reduce a list or a blob
@@ -983,7 +983,7 @@ func Test_listdict_index()
   call assert_fails('echo d[1:2]', 'E719:')
   call assert_fails("let v = [4, 6][{-> 1}]", 'E729:')
   call assert_fails("let v = range(5)[2:[]]", 'E730:')
-  call assert_fails("let v = range(5)[2:{-> 2}(]", 'E116:')
+  call assert_fails("let v = range(5)[2:{-> 2}(]", ['E15:', 'E116:'])
   call assert_fails("let v = range(5)[2:3", 'E111:')
   call assert_fails("let l = insert([1,2,3], 4, 10)", 'E684:')
   call assert_fails("let l = insert([1,2,3], 4, -10)", 'E684:')
diff --git a/src/nvim/testdir/test_match.vim b/src/nvim/testdir/test_match.vim
index 29d087bc23..5d9be99444 100644
--- a/src/nvim/testdir/test_match.vim
+++ b/src/nvim/testdir/test_match.vim
@@ -163,7 +163,7 @@ func Test_matchadd_error()
   " call assert_fails("call matchadd('GroupDoesNotExist', 'X')", 'E28:')
   call matchadd('GroupDoesNotExist', 'X')
   call assert_equal([{'group': 'GroupDoesNotExist', 'pattern': 'X', 'priority': 10, 'id': 1206}], getmatches())
-  call assert_fails("call matchadd('Search', '\\(')", 'E475:')
+  call assert_fails("call matchadd('Search', '\\(')", 'E54:')
   call assert_fails("call matchadd('Search', 'XXX', 1, 123, 1)", 'E715:')
   call assert_fails("call matchadd('Error', 'XXX', 1, 3)", 'E798:')
   call assert_fails("call matchadd('Error', 'XXX', 1, 0)", 'E799:')
diff --git a/src/nvim/testdir/test_matchfuzzy.vim b/src/nvim/testdir/test_matchfuzzy.vim
index c836bc87aa..b46550fbc3 100644
--- a/src/nvim/testdir/test_matchfuzzy.vim
+++ b/src/nvim/testdir/test_matchfuzzy.vim
@@ -6,9 +6,7 @@ source check.vim
 " Test for matchfuzzy()
 func Test_matchfuzzy()
   call assert_fails('call matchfuzzy(10, "abc")', 'E686:')
-  " Needs v8.2.1183; match the final error that's thrown for now
-  " call assert_fails('call matchfuzzy(["abc"], [])', 'E730:')
-  call assert_fails('call matchfuzzy(["abc"], [])', 'E475:')
+  call assert_fails('call matchfuzzy(["abc"], [])', 'E730:')
   call assert_fails("let x = matchfuzzy(v:_null_list, 'foo')", 'E686:')
   call assert_fails('call matchfuzzy(["abc"], v:_null_string)', 'E475:')
   call assert_equal([], matchfuzzy([], 'abc'))
@@ -75,12 +73,9 @@ func Test_matchfuzzy()
   call assert_fails("let x = matchfuzzy(l, 'day', {'text_cb' : {a, b -> 1}})", 'E119:')
   call assert_equal([], matchfuzzy(l, 'cam'))
   " Nvim's callback implementation is different, so E6000 is expected instead,
-  " but we need v8.2.1183 to assert it
   " call assert_fails("let x = matchfuzzy(l, 'cam', {'text_cb' : []})", 'E921:')
-  " call assert_fails("let x = matchfuzzy(l, 'cam', {'text_cb' : []})", 'E6000:')
-  call assert_fails("let x = matchfuzzy(l, 'cam', {'text_cb' : []})", 'E475:')
-  " call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : []})", 'E730:')
-  call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : []})", 'E475:')
+  call assert_fails("let x = matchfuzzy(l, 'cam', {'text_cb' : []})", 'E6000:')
+  call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : []})", 'E730:')
   call assert_fails("let x = matchfuzzy(l, 'cam', v:_null_dict)", 'E715:')
   call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : v:_null_string})", 'E475:')
   " Nvim doesn't have null functions
@@ -155,12 +150,9 @@ func Test_matchfuzzypos()
   call assert_fails("let x = matchfuzzypos(l, 'day', {'text_cb' : {a, b -> 1}})", 'E119:')
   call assert_equal([[], [], []], matchfuzzypos(l, 'cam'))
   " Nvim's callback implementation is different, so E6000 is expected instead,
-  " but we need v8.2.1183 to assert it
   " call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 'E921:')
-  " call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 'E6000:')
-  call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 'E475:')
-  " call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : []})", 'E730:')
-  call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : []})", 'E475:')
+  call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 'E6000:')
+  call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : []})", 'E730:')
   call assert_fails("let x = matchfuzzypos(l, 'cam', v:_null_dict)", 'E715:')
   call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : v:_null_string})", 'E475:')
   " Nvim doesn't have null functions
diff --git a/src/nvim/testdir/test_menu.vim b/src/nvim/testdir/test_menu.vim
index 58da0ed382..a1121632e6 100644
--- a/src/nvim/testdir/test_menu.vim
+++ b/src/nvim/testdir/test_menu.vim
@@ -153,7 +153,7 @@ func Test_menu_errors()
   call assert_fails('menu Test.Foo.Bar', 'E327:')
   call assert_fails('cmenu Test.Foo', 'E328:')
   call assert_fails('emenu x Test.Foo', 'E475:')
-  call assert_fails('emenu Test.Foo.Bar', 'E334:')
+  call assert_fails('emenu Test.Foo.Bar', 'E327:')
   call assert_fails('menutranslate Test', 'E474:')
 
   silent! unmenu Foo
diff --git a/src/nvim/testdir/test_method.vim b/src/nvim/testdir/test_method.vim
index e035b3ef50..057f4a1bea 100644
--- a/src/nvim/testdir/test_method.vim
+++ b/src/nvim/testdir/test_method.vim
@@ -131,9 +131,9 @@ func Test_method_syntax()
   eval [1, 2, 3]  
 	\ ->sort(
 	\ )
-  call assert_fails('eval [1, 2, 3]-> sort()', 'E260:')
+  call assert_fails('eval [1, 2, 3]-> sort()', 'E15:')
   call assert_fails('eval [1, 2, 3]->sort ()', 'E274:')
-  call assert_fails('eval [1, 2, 3]-> sort ()', 'E260:')
+  call assert_fails('eval [1, 2, 3]-> sort ()', 'E15:')
 endfunc
 
 func Test_method_lambda()
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index 5fc670e422..84929e2be3 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -1650,7 +1650,7 @@ func Test_normal23_K()
   call setline(1, '---')
   call assert_fails('normal! ggv2lK', 'E349:')
   call setline(1, ['abc', 'xyz'])
-  call assert_fails("normal! gg2lv2h\", 'E426:')
+  call assert_fails("normal! gg2lv2h\", 'E433:')
   call assert_beeps("normal! ggVjK")
 
   " clean up
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index dcedfe26a2..99d9c9c1fa 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -740,7 +740,7 @@ func s:test_xhelpgrep(cchar)
   " Search for non existing help string
   call assert_fails('Xhelpgrep a1b2c3', 'E480:')
   " Invalid regular expression
-  call assert_fails('Xhelpgrep \@"', 'E33:')
     call assert_fails('exe "normal ?~\"', 'E33:')
     set regexpengine=2
-    call assert_fails('exe "normal /~\"', 'E383:')
-    call assert_fails('exe "normal ?~\"', 'E383:')
+    call assert_fails('exe "normal /~\"', ['E33:', 'E383:'])
+    call assert_fails('exe "normal ?~\"', ['E33:', 'E383:'])
     set regexpengine&
     call writefile(v:errors, 'Xresult')
     qall!
diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim
index aa43477c5f..1f1b3097b1 100644
--- a/src/nvim/testdir/test_signs.vim
+++ b/src/nvim/testdir/test_signs.vim
@@ -483,13 +483,13 @@ func Test_sign_funcs()
   call assert_fails('call sign_place(5, "", "sign1", "@", {"lnum" : 10})',
 	      \ 'E158:')
   call assert_fails('call sign_place(5, "", "sign1", [], {"lnum" : 10})',
-	      \ 'E158:')
+	      \ 'E730:')
   call assert_fails('call sign_place(21, "", "sign1", "Xsign",
 	      \ {"lnum" : -1})', 'E474:')
   call assert_fails('call sign_place(22, "", "sign1", "Xsign",
 	      \ {"lnum" : 0})', 'E474:')
   call assert_fails('call sign_place(22, "", "sign1", "Xsign",
-	      \ {"lnum" : []})', 'E474:')
+	      \ {"lnum" : []})', 'E745:')
   call assert_equal(-1, sign_place(1, "*", "sign1", "Xsign", {"lnum" : 10}))
 
   " Tests for sign_getplaced()
@@ -1731,7 +1731,7 @@ func Test_sign_jump_func()
   call assert_fails("call sign_jump(5, 'g5', 'foo')", 'E157:')
   call assert_fails('call sign_jump([], "", "foo")', 'E745:')
   call assert_fails('call sign_jump(2, [], "foo")', 'E730:')
-  call assert_fails('call sign_jump(2, "", {})', 'E158:')
+  call assert_fails('call sign_jump(2, "", {})', 'E731:')
   call assert_fails('call sign_jump(2, "", "baz")', 'E158:')
 
   sign unplace * group=*
diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim
index f7144bbc7e..d8495fdb9b 100644
--- a/src/nvim/testdir/test_spell.vim
+++ b/src/nvim/testdir/test_spell.vim
@@ -495,7 +495,7 @@ func Test_spellsuggest_expr_errors()
     return [[{}, {}]]
   endfunc
   set spellsuggest=expr:MySuggest3()
-  call assert_fails("call spellsuggest('baord')", 'E728:')
+  call assert_fails("call spellsuggest('baord')", 'E731:')
 
   set nospell spellsuggest&
   delfunc MySuggest
diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim
index 9b90205c3d..5b476ddd7f 100644
--- a/src/nvim/testdir/test_substitute.vim
+++ b/src/nvim/testdir/test_substitute.vim
@@ -446,7 +446,7 @@ func Test_substitute_errors()
 
   call assert_fails('s/FOO/bar/', 'E486:')
   call assert_fails('s/foo/bar/@', 'E488:')
-  call assert_fails('s/\(/bar/', 'E476:')
+  call assert_fails('s/\(/bar/', 'E54:')
   call assert_fails('s afooabara', 'E146:')
   call assert_fails('s\\a', 'E10:')
 
@@ -841,9 +841,9 @@ endfunc
 func Test_sub_with_no_last_pat()
   let lines =<< trim [SCRIPT]
     call assert_fails('~', 'E33:')
-    call assert_fails('s//abc/g', 'E476:')
-    call assert_fails('s\/bar', 'E476:')
-    call assert_fails('s\&bar&', 'E476:')
+    call assert_fails('s//abc/g', 'E35:')
+    call assert_fails('s\/bar', 'E35:')
+    call assert_fails('s\&bar&', 'E33:')
     call writefile(v:errors, 'Xresult')
     qall!
   [SCRIPT]
diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim
index 29ebe141f7..9d108c6ca2 100644
--- a/src/nvim/testdir/test_syntax.vim
+++ b/src/nvim/testdir/test_syntax.vim
@@ -378,7 +378,7 @@ func Test_syntax_invalid_arg()
   call assert_fails('syntax sync x', 'E404:')
   call assert_fails('syntax keyword Abc a[', 'E789:')
   call assert_fails('syntax keyword Abc a[bc]d', 'E890:')
-  call assert_fails('syntax cluster Abc add=A add=', 'E475:')
+  call assert_fails('syntax cluster Abc add=A add=', 'E406:')
 
   " Test for too many \z\( and unmatched \z\(
   " Not able to use assert_fails() here because both E50:/E879: and E475:
diff --git a/src/nvim/testdir/test_tagfunc.vim b/src/nvim/testdir/test_tagfunc.vim
index ffc1d63b90..bdf5afa5b2 100644
--- a/src/nvim/testdir/test_tagfunc.vim
+++ b/src/nvim/testdir/test_tagfunc.vim
@@ -85,7 +85,7 @@ func Test_tagfunc()
     return v:null
   endfunc
   set tags= tfu=NullTagFunc
-  call assert_fails('tag nothing', 'E426')
+  call assert_fails('tag nothing', 'E433')
   delf NullTagFunc
 
   bwipe!
diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim
index f3ca5e306b..61bf9e6d0d 100644
--- a/src/nvim/testdir/test_tagjump.vim
+++ b/src/nvim/testdir/test_tagjump.vim
@@ -8,7 +8,7 @@ func Test_ptag_with_notagstack()
   CheckFeature quickfix
 
   set notagstack
-  call assert_fails('ptag does_not_exist_tag_name', 'E426')
+  call assert_fails('ptag does_not_exist_tag_name', 'E433')
   set tagstack&vim
 endfunc
 
@@ -346,7 +346,7 @@ func Test_tagjump_etags()
         \ "Xmain.c,64",
         \ ";;;;\x7f1,0",
 	\ ], 'Xtags')
-  call assert_fails('tag foo', 'E426:')
+  call assert_fails('tag foo', 'E431:')
 
   call delete('Xtags')
   call delete('Xtags2')
diff --git a/src/nvim/testdir/test_taglist.vim b/src/nvim/testdir/test_taglist.vim
index 0c47fc9445..658485582c 100644
--- a/src/nvim/testdir/test_taglist.vim
+++ b/src/nvim/testdir/test_taglist.vim
@@ -84,13 +84,11 @@ func Test_taglist_ctags_etags()
 endfunc
 
 func Test_tags_too_long()
-  call assert_fails('tag ' . repeat('x', 1020), 'E426')
+  call assert_fails('tag ' . repeat('x', 1020), ['E433', 'E426'])
   tags
 endfunc
 
 func Test_tagfiles()
-  " Nvim: different default for 'tags'.
-  set tags=./tags,tags
   call assert_equal([], tagfiles())
 
   call writefile(["FFoo\tXfoo\t1"], 'Xtags1')
diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim
index d71bb5bbb8..3dff8fa2d8 100644
--- a/src/nvim/testdir/test_trycatch.vim
+++ b/src/nvim/testdir/test_trycatch.vim
@@ -2000,13 +2000,11 @@ endfunc
 func Test_try_catch_errors()
   call assert_fails('throw |', 'E471:')
   call assert_fails("throw \n ", 'E471:')
-  call assert_fails('catch abc', 'E603:')
+  call assert_fails('catch abc', 'E654:')
   call assert_fails('try | let i = 1| finally | catch | endtry', 'E604:')
   call assert_fails('finally', 'E606:')
   call assert_fails('try | finally | finally | endtry', 'E607:')
-  " v8.2.3486 has been ported, but v8.2.1183 hasn't, so E170 appears here.
-  " call assert_fails('try | for i in range(5) | endif | endtry', 'E580:')
-  call assert_fails('try | for i in range(5) | endif | endtry', 'E170:')
+  call assert_fails('try | for i in range(5) | endif | endtry', 'E580:')
   call assert_fails('try | while v:true | endtry', 'E170:')
   call assert_fails('try | if v:true | endtry', 'E171:')
 endfunc
diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim
index 2fced0dd2f..8e13ed778f 100644
--- a/src/nvim/testdir/test_utf8.vim
+++ b/src/nvim/testdir/test_utf8.vim
@@ -21,7 +21,7 @@ func Test_strchars()
     call assert_equal(exp[i][1], inp[i]->strchars(0))
     call assert_equal(exp[i][2], strchars(inp[i], 1))
   endfor
-  call assert_fails("let v=strchars('abc', [])", 'E474:')
+  call assert_fails("let v=strchars('abc', [])", 'E745:')
   call assert_fails("let v=strchars('abc', 2)", 'E474:')
 endfunc
 
diff --git a/src/nvim/testdir/test_winbuf_close.vim b/src/nvim/testdir/test_winbuf_close.vim
index f4878c2397..643c1068bd 100644
--- a/src/nvim/testdir/test_winbuf_close.vim
+++ b/src/nvim/testdir/test_winbuf_close.vim
@@ -115,7 +115,7 @@ func Test_winbuf_close()
   call assert_equal('Xtest2', bufname('%'))
   quit!
   call assert_equal('Xtest3', bufname('%'))
-  call assert_fails('silent! quit!', 'E162')
+  call assert_fails('silent! quit!', 'E37')
   call assert_equal('Xtest1', bufname('%'))
 
   call delete('Xtest1')
diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim
index 5ee13ef144..8fb4c8fa4c 100644
--- a/src/nvim/testdir/test_writefile.vim
+++ b/src/nvim/testdir/test_writefile.vim
@@ -325,7 +325,7 @@ func Test_write_autocmd_unloadbuf_lockmark()
     autocmd BufWritePre Xfile enew | write
   augroup END
   e Xfile
-  call assert_fails('lockmarks write', 'E203:')
+  call assert_fails('lockmarks write', ['E32', 'E203:'])
   augroup WriteTest
     au!
   augroup END
diff --git a/src/nvim/testing.c b/src/nvim/testing.c
index 45134db14f..5c0e757ccf 100644
--- a/src/nvim/testing.c
+++ b/src/nvim/testing.c
@@ -249,8 +249,7 @@ static int assert_match_common(typval_T *argvars, assert_type_T atype)
 
   if (pat == NULL || text == NULL) {
     emsg(_(e_invarg));
-  } else if (pattern_match((char *)pat, (char *)text, false)
-             != (atype == ASSERT_MATCH)) {
+  } else if (pattern_match(pat, text, false) != (atype == ASSERT_MATCH)) {
     garray_T ga;
     prepare_assert_error(&ga);
     fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], atype);
@@ -477,11 +476,13 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   garray_T ga;
   int save_trylevel = trylevel;
   const int called_emsg_before = called_emsg;
+  bool wrong_arg = false;
 
   // trylevel must be zero for a ":throw" command to be considered failed
   trylevel = 0;
   suppress_errthrow = true;
   emsg_silent = true;
+  emsg_assert_fails_used = true;
 
   do_cmdline_cmd(cmd);
   if (called_emsg == called_emsg_before) {
@@ -493,13 +494,43 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
     rettv->vval.v_number = 1;
   } else if (argvars[1].v_type != VAR_UNKNOWN) {
     char buf[NUMBUFLEN];
-    const char *const error = tv_get_string_buf_chk(&argvars[1], buf);
+    const char *expected;
+    bool error_found = false;
+    char *actual = emsg_assert_fails_msg == NULL ? "[unknown]" : emsg_assert_fails_msg;
+
+    if (argvars[1].v_type == VAR_STRING) {
+      expected = tv_get_string_buf_chk(&argvars[1], buf);
+      error_found = expected == NULL || strstr(actual, expected) == NULL;
+    } else if (argvars[1].v_type == VAR_LIST) {
+      const list_T *const list = argvars[1].vval.v_list;
+      if (list == NULL || tv_list_len(list) < 1 || tv_list_len(list) > 2) {
+        wrong_arg = true;
+        goto theend;
+      }
+      const typval_T *tv = TV_LIST_ITEM_TV(tv_list_first(list));
+      expected = tv_get_string_buf_chk(tv, buf);
+      if (!pattern_match(expected, actual, false)) {
+        error_found = true;
+      } else if (tv_list_len(list) == 2) {
+        tv = TV_LIST_ITEM_TV(tv_list_last(list));
+        actual = get_vim_var_str(VV_ERRMSG);
+        expected = tv_get_string_buf_chk(tv, buf);
+        if (!pattern_match(expected, actual, false)) {
+          error_found = true;
+        }
+      }
+    } else {
+      wrong_arg = true;
+      goto theend;
+    }
 
-    if (error == NULL
-        || strstr(get_vim_var_str(VV_ERRMSG), error) == NULL) {
+    if (error_found) {
+      typval_T actual_tv;
+      actual_tv.v_type = VAR_STRING;
+      actual_tv.vval.v_string = actual;
       prepare_assert_error(&ga);
       fill_assert_error(&ga, &argvars[2], NULL, &argvars[1],
-                        get_vim_var_tv(VV_ERRMSG), ASSERT_OTHER);
+                        &actual_tv, ASSERT_OTHER);
       ga_concat(&ga, ": ");
       assert_append_cmd_or_arg(&ga, argvars, cmd);
       assert_error(&ga);
@@ -508,11 +539,18 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
     }
   }
 
+theend:
   trylevel = save_trylevel;
   suppress_errthrow = false;
   emsg_silent = false;
   emsg_on_display = false;
+  emsg_assert_fails_used = false;
+  XFREE_CLEAR(emsg_assert_fails_msg);
   set_vim_var_string(VV_ERRMSG, NULL, 0);
+  if (wrong_arg) {
+    emsg(_(
+          "E856: assert_fails() second argument must be a string or a list with one or two strings"));
+  }
 }
 
 // "assert_false(actual[, msg])" function
-- 
cgit 


From 159d52b433055e957fec6dee5da20f23fabf10d4 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 11:08:40 +0800
Subject: vim-patch:8.2.1184: some tests fail

Problem:    Some tests fail.
Solution:   Adjust tests for different assert_fails() behavior.  Remove unused
            variable.

https://github.com/vim/vim/commit/2b6ef856fb89f703714f3f1f567d9bd7c81079f3

Co-authored-by: Bram Moolenaar 
---
 src/nvim/testdir/test_eval_stuff.vim | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim
index 851048ec5b..46482c34a1 100644
--- a/src/nvim/testdir/test_eval_stuff.vim
+++ b/src/nvim/testdir/test_eval_stuff.vim
@@ -20,13 +20,8 @@ func Test_nocatch_restore_silent_emsg()
     throw 1
   catch
   endtry
-  echoerr 'wrong'
-  let c1 = nr2char(screenchar(&lines, 1))
-  let c2 = nr2char(screenchar(&lines, 2))
-  let c3 = nr2char(screenchar(&lines, 3))
-  let c4 = nr2char(screenchar(&lines, 4))
-  let c5 = nr2char(screenchar(&lines, 5))
-  call assert_equal('wrong', c1 . c2 . c3 . c4 . c5)
+  echoerr 'wrong again'
+  call assert_equal('wrong again', ScreenLine(&lines))
 endfunc
 
 func Test_mkdir_p()
-- 
cgit 


From b33de61cc3e14cc6160a972205f6543e82b843aa Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 12:28:19 +0800
Subject: vim-patch:8.2.1199: not all assert functions are fully tested

Problem:    Not all assert functions are fully tested.
Solution:   Test more assert functions.

https://github.com/vim/vim/commit/7177da9dd4d9a521c6141c6fbf7e9a4d6296ab05

Co-authored-by: Bram Moolenaar 
---
 src/nvim/testdir/test_assert.vim | 46 ++++++++++++++++++++++++++++++++++++++++
 src/nvim/testing.c               | 11 +++++-----
 2 files changed, 52 insertions(+), 5 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_assert.vim b/src/nvim/testdir/test_assert.vim
index 8723a0a38d..0ba45d0b13 100644
--- a/src/nvim/testdir/test_assert.vim
+++ b/src/nvim/testdir/test_assert.vim
@@ -48,6 +48,11 @@ func Test_assert_equal()
   call assert_equal('XxxxxxxxxxxxxxxxxxxxxxX', 'XyyyyyyyyyyyyyyyyyyyyyyyyyX')
   call assert_match("Expected 'X\\\\\\[x occurs 21 times]X' but got 'X\\\\\\[y occurs 25 times]X'", v:errors[0])
   call remove(v:errors, 0)
+
+  " special characters are escaped
+  call assert_equal("\b\e\f\n\t\r\\\x01\x7f", 'x')
+  call assert_match('Expected ''\\b\\e\\f\\n\\t\\r\\\\\\x01\\x7f'' but got ''x''', v:errors[0])
+  call remove(v:errors, 0)
 endfunc
 
 func Test_assert_equal_dict()
@@ -143,6 +148,14 @@ func Test_assert_exception()
     call assert_equal(0, assert_exception('E492:'))
   endtry
 
+  try
+    nocommand
+  catch
+    call assert_equal(1, assert_exception('E12345:'))
+  endtry
+  call assert_match("Expected 'E12345:' but got 'Vim:E492: ", v:errors[0])
+  call remove(v:errors, 0)
+
   try
     nocommand
   catch
@@ -153,6 +166,10 @@ func Test_assert_exception()
       call assert_equal(0, assert_exception('E730:'))
     endtry
   endtry
+
+  call assert_equal(1, assert_exception('E492:'))
+  call assert_match('v:exception is not set', v:errors[0])
+  call remove(v:errors, 0)
 endfunc
 
 func Test_wrong_error_type()
@@ -202,6 +219,14 @@ func Test_assert_fail_fails()
   call assert_match("stupid: Expected 'E9876' but got 'E492:", v:errors[0])
   call remove(v:errors, 0)
 
+  call assert_equal(1, assert_fails('xxx', ['E9876']))
+  call assert_match("Expected \\['E9876'\\] but got 'E492:", v:errors[0])
+  call remove(v:errors, 0)
+
+  call assert_equal(1, assert_fails('xxx', ['E492:', 'E9876']))
+  call assert_match("Expected \\['E492:', 'E9876'\\] but got 'E492:", v:errors[0])
+  call remove(v:errors, 0)
+
   call assert_equal(1, assert_fails('echo', '', 'echo command'))
   call assert_match("command did not fail: echo command", v:errors[0])
   call remove(v:errors, 0)
@@ -209,6 +234,27 @@ func Test_assert_fail_fails()
   call assert_equal(1, 'echo'->assert_fails('', 'echo command'))
   call assert_match("command did not fail: echo command", v:errors[0])
   call remove(v:errors, 0)
+
+  try
+    call assert_equal(1, assert_fails('xxx', []))
+  catch
+    let exp = v:exception
+  endtry
+  call assert_match("E856: assert_fails() second argument", exp)
+
+  try
+    call assert_equal(1, assert_fails('xxx', ['1', '2', '3']))
+  catch
+    let exp = v:exception
+  endtry
+  call assert_match("E856: assert_fails() second argument", exp)
+
+  try
+    call assert_equal(1, assert_fails('xxx', #{one: 1}))
+  catch
+    let exp = v:exception
+  endtry
+  call assert_match("E856: assert_fails() second argument", exp)
 endfunc
 
 func Test_assert_fails_in_try_block()
diff --git a/src/nvim/testing.c b/src/nvim/testing.c
index 5c0e757ccf..e1a28fbde3 100644
--- a/src/nvim/testing.c
+++ b/src/nvim/testing.c
@@ -68,7 +68,7 @@ static void ga_concat_esc(garray_T *gap, const char_u *p, int clen)
     case '\\':
       ga_concat(gap, "\\\\"); break;
     default:
-      if (*p < ' ') {
+      if (*p < ' ' || *p == 0x7f) {
         vim_snprintf((char *)buf, NUMBUFLEN, "\\x%02x", *p);
         ga_concat(gap, (char *)buf);
       } else {
@@ -244,12 +244,12 @@ static int assert_match_common(typval_T *argvars, assert_type_T atype)
 {
   char buf1[NUMBUFLEN];
   char buf2[NUMBUFLEN];
+  const int called_emsg_before = called_emsg;
   const char *const pat = tv_get_string_buf_chk(&argvars[0], buf1);
   const char *const text = tv_get_string_buf_chk(&argvars[1], buf2);
 
-  if (pat == NULL || text == NULL) {
-    emsg(_(e_invarg));
-  } else if (pattern_match(pat, text, false) != (atype == ASSERT_MATCH)) {
+  if (called_emsg == called_emsg_before
+      && pattern_match(pat, text, false) != (atype == ASSERT_MATCH)) {
     garray_T ga;
     prepare_assert_error(&ga);
     fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], atype);
@@ -350,11 +350,12 @@ static int assert_equalfile(typval_T *argvars)
 {
   char buf1[NUMBUFLEN];
   char buf2[NUMBUFLEN];
+  const int called_emsg_before = called_emsg;
   const char *const fname1 = tv_get_string_buf_chk(&argvars[0], buf1);
   const char *const fname2 = tv_get_string_buf_chk(&argvars[1], buf2);
   garray_T ga;
 
-  if (fname1 == NULL || fname2 == NULL) {
+  if (called_emsg > called_emsg_before) {
     return 0;
   }
 
-- 
cgit 


From 0d8293364f78237afb83d4822611d6fd8add66f8 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 12:37:28 +0800
Subject: vim-patch:8.2.1479: Vim9: error for list index uses wrong line number

Problem:    Vim9: error for list index uses wrong line number.
Solution:   Set source line number. (closes vim/vim#6724)  Add a way to assert the
            line number of the error with assert_fails().

https://github.com/vim/vim/commit/1d634542cf5ebcd1d5d83bd124b3e1d5e7c96c58

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval.lua  |  2 +-
 src/nvim/globals.h |  1 +
 src/nvim/message.c |  1 +
 src/nvim/testing.c | 25 +++++++++++++++++++++----
 4 files changed, 24 insertions(+), 5 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index ecb411a652..5952267c02 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -38,7 +38,7 @@ return {
     assert_equal={args={2, 3}, base=2},
     assert_equalfile={args={2, 3}, base=1},
     assert_exception={args={1, 2}},
-    assert_fails={args={1, 3}, base=1},
+    assert_fails={args={1, 4}, base=1},
     assert_false={args={1, 2}, base=1},
     assert_inrange={args={3, 4}, base=3},
     assert_match={args={2, 3}, base=2},
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index d7b2164d12..f3e9336baa 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -197,6 +197,7 @@ EXTERN bool emsg_severe INIT(= false);      // use message of next of several
 // used by assert_fails()
 EXTERN bool emsg_assert_fails_used INIT(= false);
 EXTERN char *emsg_assert_fails_msg INIT(= NULL);
+EXTERN long emsg_assert_fails_lnum INIT(= 0);
 
 EXTERN bool did_endif INIT(= false);        // just had ":endif"
 EXTERN dict_T vimvardict;                   // Dictionary with v: variables
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 4837f4131a..c95968afb4 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -665,6 +665,7 @@ static bool emsg_multiline(const char *s, bool multiline)
 
     if (emsg_assert_fails_used && emsg_assert_fails_msg == NULL) {
       emsg_assert_fails_msg = xstrdup(s);
+      emsg_assert_fails_lnum = SOURCING_LNUM;
     }
 
     // set "v:errmsg", also when using ":silent! cmd"
diff --git a/src/nvim/testing.c b/src/nvim/testing.c
index e1a28fbde3..7651122cce 100644
--- a/src/nvim/testing.c
+++ b/src/nvim/testing.c
@@ -124,7 +124,10 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_s
   bool did_copy = false;
   int omitted = 0;
 
-  if (opt_msg_tv->v_type != VAR_UNKNOWN) {
+  if (opt_msg_tv->v_type != VAR_UNKNOWN
+      && !(opt_msg_tv->v_type == VAR_STRING
+           && (opt_msg_tv->vval.v_string == NULL
+               || *opt_msg_tv->vval.v_string == NUL))) {
     tofree = (char_u *)encode_tv2echo(opt_msg_tv, NULL);
     ga_concat(gap, (char *)tofree);
     xfree(tofree);
@@ -497,6 +500,7 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
     char buf[NUMBUFLEN];
     const char *expected;
     bool error_found = false;
+    bool lnum_error_found = false;
     char *actual = emsg_assert_fails_msg == NULL ? "[unknown]" : emsg_assert_fails_msg;
 
     if (argvars[1].v_type == VAR_STRING) {
@@ -525,12 +529,25 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
       goto theend;
     }
 
+    if (!error_found && argvars[3].v_type == VAR_NUMBER
+        && argvars[3].vval.v_number >= 0
+        && argvars[3].vval.v_number != emsg_assert_fails_lnum) {
+      error_found = true;
+      lnum_error_found = true;
+    }
+
     if (error_found) {
       typval_T actual_tv;
-      actual_tv.v_type = VAR_STRING;
-      actual_tv.vval.v_string = actual;
       prepare_assert_error(&ga);
-      fill_assert_error(&ga, &argvars[2], NULL, &argvars[1],
+      if (lnum_error_found) {
+        actual_tv.v_type = VAR_NUMBER;
+        actual_tv.vval.v_number = emsg_assert_fails_lnum;
+      } else {
+        actual_tv.v_type = VAR_STRING;
+        actual_tv.vval.v_string = actual;
+      }
+      fill_assert_error(&ga, &argvars[2], NULL,
+                        &argvars[lnum_error_found ? 3 : 1],
                         &actual_tv, ASSERT_OTHER);
       ga_concat(&ga, ": ");
       assert_append_cmd_or_arg(&ga, argvars, cmd);
-- 
cgit 


From 8ba7a966a1339767b19a5ca4449b38ef0cae49c7 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 12:40:46 +0800
Subject: vim-patch:8.2.1484: flaky failure in assert_fails()

Problem:    Flaky failure in assert_fails().
Solution:   Only used fourth argument if there is a third argument.

https://github.com/vim/vim/commit/9b02d64cff7664b9643205d6e23b08da688fe87a

Co-authored-by: Bram Moolenaar 
---
 src/nvim/testing.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testing.c b/src/nvim/testing.c
index 7651122cce..4ab1367f26 100644
--- a/src/nvim/testing.c
+++ b/src/nvim/testing.c
@@ -529,7 +529,8 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
       goto theend;
     }
 
-    if (!error_found && argvars[3].v_type == VAR_NUMBER
+    if (!error_found && argvars[2].v_type != VAR_UNKNOWN
+        && argvars[3].v_type == VAR_NUMBER
         && argvars[3].vval.v_number >= 0
         && argvars[3].vval.v_number != emsg_assert_fails_lnum) {
       error_found = true;
-- 
cgit 


From 02f80d9a8a560a93142bcebf324ba14cde4dd1b5 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 12:41:36 +0800
Subject: vim-patch:8.2.1631: test_fails() does not check the context of the
 line number

Problem:    test_fails() does not check the context of the line number.
Solution:   Use another argument to specify the context of the line number.

https://github.com/vim/vim/commit/9bd5d879c2ecfbdbb168b090e12f4b89724a302e

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval.lua  |  2 +-
 src/nvim/globals.h |  1 +
 src/nvim/message.c |  2 ++
 src/nvim/testing.c | 28 +++++++++++++++++++---------
 4 files changed, 23 insertions(+), 10 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 5952267c02..61e7f99afd 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -38,7 +38,7 @@ return {
     assert_equal={args={2, 3}, base=2},
     assert_equalfile={args={2, 3}, base=1},
     assert_exception={args={1, 2}},
-    assert_fails={args={1, 4}, base=1},
+    assert_fails={args={1, 5}, base=1},
     assert_false={args={1, 2}, base=1},
     assert_inrange={args={3, 4}, base=3},
     assert_match={args={2, 3}, base=2},
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index f3e9336baa..14266fc859 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -198,6 +198,7 @@ EXTERN bool emsg_severe INIT(= false);      // use message of next of several
 EXTERN bool emsg_assert_fails_used INIT(= false);
 EXTERN char *emsg_assert_fails_msg INIT(= NULL);
 EXTERN long emsg_assert_fails_lnum INIT(= 0);
+EXTERN char *emsg_assert_fails_context INIT(= NULL);
 
 EXTERN bool did_endif INIT(= false);        // just had ":endif"
 EXTERN dict_T vimvardict;                   // Dictionary with v: variables
diff --git a/src/nvim/message.c b/src/nvim/message.c
index c95968afb4..fa1c8036e6 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -666,6 +666,8 @@ static bool emsg_multiline(const char *s, bool multiline)
     if (emsg_assert_fails_used && emsg_assert_fails_msg == NULL) {
       emsg_assert_fails_msg = xstrdup(s);
       emsg_assert_fails_lnum = SOURCING_LNUM;
+      xfree(emsg_assert_fails_context);
+      emsg_assert_fails_context = xstrdup(SOURCING_NAME == NULL ? "" : SOURCING_NAME);
     }
 
     // set "v:errmsg", also when using ":silent! cmd"
diff --git a/src/nvim/testing.c b/src/nvim/testing.c
index 4ab1367f26..9dfd828a67 100644
--- a/src/nvim/testing.c
+++ b/src/nvim/testing.c
@@ -500,7 +500,7 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
     char buf[NUMBUFLEN];
     const char *expected;
     bool error_found = false;
-    bool lnum_error_found = false;
+    int error_found_index = 1;
     char *actual = emsg_assert_fails_msg == NULL ? "[unknown]" : emsg_assert_fails_msg;
 
     if (argvars[1].v_type == VAR_STRING) {
@@ -530,26 +530,36 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
     }
 
     if (!error_found && argvars[2].v_type != VAR_UNKNOWN
-        && argvars[3].v_type == VAR_NUMBER
-        && argvars[3].vval.v_number >= 0
-        && argvars[3].vval.v_number != emsg_assert_fails_lnum) {
-      error_found = true;
-      lnum_error_found = true;
+        && argvars[3].v_type == VAR_NUMBER) {
+      if (argvars[3].vval.v_number >= 0
+          && argvars[3].vval.v_number != emsg_assert_fails_lnum) {
+        error_found = true;
+        error_found_index = 3;
+      }
+      if (!error_found && argvars[4].v_type == VAR_STRING
+          && argvars[4].vval.v_string != NULL
+          && !pattern_match(argvars[4].vval.v_string,
+                            emsg_assert_fails_context, false)) {
+        error_found = true;
+        error_found_index = 4;
+      }
     }
 
     if (error_found) {
       typval_T actual_tv;
       prepare_assert_error(&ga);
-      if (lnum_error_found) {
+      if (error_found_index == 3) {
         actual_tv.v_type = VAR_NUMBER;
         actual_tv.vval.v_number = emsg_assert_fails_lnum;
+      } else if (error_found_index == 4) {
+        actual_tv.v_type = VAR_STRING;
+        actual_tv.vval.v_string = emsg_assert_fails_context;
       } else {
         actual_tv.v_type = VAR_STRING;
         actual_tv.vval.v_string = actual;
       }
       fill_assert_error(&ga, &argvars[2], NULL,
-                        &argvars[lnum_error_found ? 3 : 1],
-                        &actual_tv, ASSERT_OTHER);
+                        &argvars[error_found_index], &actual_tv, ASSERT_OTHER);
       ga_concat(&ga, ": ");
       assert_append_cmd_or_arg(&ga, argvars, cmd);
       assert_error(&ga);
-- 
cgit 


From 6956971ec790e636b16eeaec798c826515da9834 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 12:33:10 +0800
Subject: vim-patch:8.2.1632: not checking the context of test_fails()

Problem:    Not checking the context of test_fails().
Solution:   Add the line number and context arguments.  Give error if
            assert_fails() argument types are wrong.

https://github.com/vim/vim/commit/44d6652d561d628d12e3ff7f6636ea7d1f805ced

Co-authored-by: Bram Moolenaar 
---
 src/nvim/testdir/test_assert.vim | 14 ++++++++++++++
 src/nvim/testing.c               | 42 ++++++++++++++++++++++++++--------------
 2 files changed, 41 insertions(+), 15 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_assert.vim b/src/nvim/testdir/test_assert.vim
index 0ba45d0b13..431908e95c 100644
--- a/src/nvim/testdir/test_assert.vim
+++ b/src/nvim/testdir/test_assert.vim
@@ -255,6 +255,20 @@ func Test_assert_fail_fails()
     let exp = v:exception
   endtry
   call assert_match("E856: assert_fails() second argument", exp)
+
+  try
+    call assert_equal(1, assert_fails('xxx', 'E492', '', 'burp'))
+  catch
+    let exp = v:exception
+  endtry
+  call assert_match("E1115: assert_fails() fourth argument must be a number", exp)
+
+  try
+    call assert_equal(1, assert_fails('xxx', 'E492', '', 54, 123))
+  catch
+    let exp = v:exception
+  endtry
+  call assert_match("E1116: assert_fails() fifth argument must be a string", exp)
 endfunc
 
 func Test_assert_fails_in_try_block()
diff --git a/src/nvim/testing.c b/src/nvim/testing.c
index 9dfd828a67..9dd41224da 100644
--- a/src/nvim/testing.c
+++ b/src/nvim/testing.c
@@ -14,6 +14,12 @@
 # include "testing.c.generated.h"
 #endif
 
+static char e_assert_fails_second_arg[]
+  = N_("E856: assert_fails() second argument must be a string or a list with one or two strings");
+static char e_assert_fails_fourth_argument[]
+  = N_("E1115: assert_fails() fourth argument must be a number");
+static char e_assert_fails_fifth_argument[]
+  = N_("E1116: assert_fails() fifth argument must be a string");
 static char e_calling_test_garbagecollect_now_while_v_testing_is_not_set[]
   = N_("E1142: Calling test_garbagecollect_now() while v:testing is not set");
 
@@ -480,7 +486,7 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   garray_T ga;
   int save_trylevel = trylevel;
   const int called_emsg_before = called_emsg;
-  bool wrong_arg = false;
+  char *wrong_arg_msg = NULL;
 
   // trylevel must be zero for a ":throw" command to be considered failed
   trylevel = 0;
@@ -509,7 +515,7 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
     } else if (argvars[1].v_type == VAR_LIST) {
       const list_T *const list = argvars[1].vval.v_list;
       if (list == NULL || tv_list_len(list) < 1 || tv_list_len(list) > 2) {
-        wrong_arg = true;
+        wrong_arg_msg = e_assert_fails_second_arg;
         goto theend;
       }
       const typval_T *tv = TV_LIST_ITEM_TV(tv_list_first(list));
@@ -525,23 +531,30 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
         }
       }
     } else {
-      wrong_arg = true;
+      wrong_arg_msg = e_assert_fails_second_arg;
       goto theend;
     }
 
     if (!error_found && argvars[2].v_type != VAR_UNKNOWN
-        && argvars[3].v_type == VAR_NUMBER) {
-      if (argvars[3].vval.v_number >= 0
-          && argvars[3].vval.v_number != emsg_assert_fails_lnum) {
+        && argvars[3].v_type != VAR_UNKNOWN) {
+      if (argvars[3].v_type != VAR_NUMBER) {
+        wrong_arg_msg = e_assert_fails_fourth_argument;
+        goto theend;
+      } else if (argvars[3].vval.v_number >= 0
+                 && argvars[3].vval.v_number != emsg_assert_fails_lnum) {
         error_found = true;
         error_found_index = 3;
       }
-      if (!error_found && argvars[4].v_type == VAR_STRING
-          && argvars[4].vval.v_string != NULL
-          && !pattern_match(argvars[4].vval.v_string,
-                            emsg_assert_fails_context, false)) {
-        error_found = true;
-        error_found_index = 4;
+      if (!error_found && argvars[4].v_type != VAR_UNKNOWN) {
+        if (argvars[4].v_type != VAR_STRING) {
+          wrong_arg_msg = e_assert_fails_fifth_argument;
+          goto theend;
+        } else if (argvars[4].vval.v_string != NULL
+                   && !pattern_match(argvars[4].vval.v_string,
+                                     emsg_assert_fails_context, false)) {
+          error_found = true;
+          error_found_index = 4;
+        }
       }
     }
 
@@ -576,9 +589,8 @@ theend:
   emsg_assert_fails_used = false;
   XFREE_CLEAR(emsg_assert_fails_msg);
   set_vim_var_string(VV_ERRMSG, NULL, 0);
-  if (wrong_arg) {
-    emsg(_(
-          "E856: assert_fails() second argument must be a string or a list with one or two strings"));
+  if (wrong_arg_msg != NULL) {
+    emsg(_(wrong_arg_msg));
   }
 }
 
-- 
cgit 


From 5731f406fa6ddf9c8340329ec1e534cd968df94f Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 14:35:46 +0800
Subject: vim-patch:8.2.3252: duplicated code for adding buffer lines

Problem:    Duplicated code for adding buffer lines.
Solution:   Move code to a common function.  Also move map functions to map.c.
            (Yegappan Lakshmanan, closes vim/vim#8665)

https://github.com/vim/vim/commit/4a15504e911bc90a29d862862f0b7a46d8acd12a

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/eval/funcs.c | 23 ++++++++++-------------
 1 file changed, 10 insertions(+), 13 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 0e3de29cce..c4d0604b67 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -370,18 +370,24 @@ static void f_append(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   set_buffer_lines(curbuf, lnum, true, &argvars[1], rettv);
 }
 
-/// "appendbufline(buf, lnum, string/list)" function
-static void f_appendbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+/// Set or append lines to a buffer.
+static void buf_set_append_line(typval_T *argvars, typval_T *rettv, bool append)
 {
   buf_T *const buf = tv_get_buf(&argvars[0], false);
   if (buf == NULL) {
     rettv->vval.v_number = 1;  // FAIL
   } else {
     const linenr_T lnum = tv_get_lnum_buf(&argvars[1], buf);
-    set_buffer_lines(buf, lnum, true, &argvars[2], rettv);
+    set_buffer_lines(buf, lnum, append, &argvars[2], rettv);
   }
 }
 
+/// "appendbufline(buf, lnum, string/list)" function
+static void f_appendbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+  buf_set_append_line(argvars, rettv, true);
+}
+
 /// "atan2()" function
 static void f_atan2(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 {
@@ -7501,16 +7507,7 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 /// "setbufline()" function
 static void f_setbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 {
-  linenr_T lnum;
-  buf_T *buf;
-
-  buf = tv_get_buf(&argvars[0], false);
-  if (buf == NULL) {
-    rettv->vval.v_number = 1;  // FAIL
-  } else {
-    lnum = tv_get_lnum_buf(&argvars[1], buf);
-    set_buffer_lines(buf, lnum, false, &argvars[2], rettv);
-  }
+  buf_set_append_line(argvars, rettv, false);
 }
 
 /// Set the cursor or mark position.
-- 
cgit 


From 48405df046e6d15c26aeea429fa44950ccc1a8ac Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 14:31:40 +0800
Subject: vim-patch:8.2.3919: Vim9: wrong argument for append() results in two
 errors

Problem:    Vim9: wrong argument for append() results in two errors.
Solution:   Check did_emsg.  Also for setline().  Adjust the help for
            appendbufline().

https://github.com/vim/vim/commit/8b6256f6ec075cca40341e61ebc9f538b4902dd1

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval.c       |  1 +
 src/nvim/eval/funcs.c | 29 ++++++++++++++++++++++-------
 2 files changed, 23 insertions(+), 7 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 5b8cbcfbb3..1200ba20ba 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -5200,6 +5200,7 @@ linenr_T tv_get_lnum_buf(const typval_T *const tv, const buf_T *const buf)
   if (tv->v_type == VAR_STRING
       && tv->vval.v_string != NULL
       && tv->vval.v_string[0] == '$'
+      && tv->vval.v_string[1] == NUL
       && buf != NULL) {
     return buf->b_ml.ml_line_count;
   }
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index c4d0604b67..cf924b1c49 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -365,20 +365,25 @@ static void f_api_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 /// "append(lnum, string/list)" function
 static void f_append(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 {
+  const int did_emsg_before = did_emsg;
   const linenr_T lnum = tv_get_lnum(&argvars[0]);
-
-  set_buffer_lines(curbuf, lnum, true, &argvars[1], rettv);
+  if (did_emsg == did_emsg_before) {
+    set_buffer_lines(curbuf, lnum, true, &argvars[1], rettv);
+  }
 }
 
 /// Set or append lines to a buffer.
 static void buf_set_append_line(typval_T *argvars, typval_T *rettv, bool append)
 {
+  const int did_emsg_before = did_emsg;
   buf_T *const buf = tv_get_buf(&argvars[0], false);
   if (buf == NULL) {
     rettv->vval.v_number = 1;  // FAIL
   } else {
     const linenr_T lnum = tv_get_lnum_buf(&argvars[1], buf);
-    set_buffer_lines(buf, lnum, append, &argvars[2], rettv);
+    if (did_emsg == did_emsg_before) {
+      set_buffer_lines(buf, lnum, append, &argvars[2], rettv);
+    }
   }
 }
 
@@ -1476,9 +1481,10 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, EvalFuncData fp
 /// "deletebufline()" function
 static void f_deletebufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 {
+  const int did_emsg_before = did_emsg;
+  rettv->vval.v_number = 1;   // FAIL by default
   buf_T *const buf = tv_get_buf(&argvars[0], false);
   if (buf == NULL) {
-    rettv->vval.v_number = 1;  // FAIL
     return;
   }
   const bool is_curbuf = buf == curbuf;
@@ -1486,6 +1492,9 @@ static void f_deletebufline(typval_T *argvars, typval_T *rettv, EvalFuncData fpt
 
   linenr_T last;
   const linenr_T first = tv_get_lnum_buf(&argvars[1], buf);
+  if (did_emsg > did_emsg_before) {
+    return;
+  }
   if (argvars[2].v_type != VAR_UNKNOWN) {
     last = tv_get_lnum_buf(&argvars[2], buf);
   } else {
@@ -1494,7 +1503,6 @@ static void f_deletebufline(typval_T *argvars, typval_T *rettv, EvalFuncData fpt
 
   if (buf->b_ml.ml_mfp == NULL || first < 1
       || first > buf->b_ml.ml_line_count || last < first) {
-    rettv->vval.v_number = 1;  // FAIL
     return;
   }
 
@@ -1547,6 +1555,7 @@ static void f_deletebufline(typval_T *argvars, typval_T *rettv, EvalFuncData fpt
     curwin = curwin_save;
     VIsual_active = save_VIsual_active;
   }
+  rettv->vval.v_number = 0;  // OK
 }
 
 /// "did_filetype()" function
@@ -2581,9 +2590,12 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retli
 /// "getbufline()" function
 static void f_getbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 {
+  const int did_emsg_before = did_emsg;
   buf_T *const buf = tv_get_buf_from_arg(&argvars[0]);
-
   const linenr_T lnum = tv_get_lnum_buf(&argvars[1], buf);
+  if (did_emsg > did_emsg_before) {
+    return;
+  }
   const linenr_T end = (argvars[2].v_type == VAR_UNKNOWN
                         ? lnum
                         : tv_get_lnum_buf(&argvars[2], buf));
@@ -7636,8 +7648,11 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 /// "setline()" function
 static void f_setline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 {
+  const int did_emsg_before = did_emsg;
   linenr_T lnum = tv_get_lnum(&argvars[0]);
-  set_buffer_lines(curbuf, lnum, false, &argvars[1], rettv);
+  if (did_emsg == did_emsg_before) {
+    set_buffer_lines(curbuf, lnum, false, &argvars[1], rettv);
+  }
 }
 
 /// "setpos()" function
-- 
cgit 


From 30cfdd0ea1f67afed6732ecbcdda9dda72a2b0a0 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 14:26:28 +0800
Subject: vim-patch:8.2.5027: error for missing :endif when an exception was
 thrown

Problem:    Error for missing :endif when an exception was thrown. (Dani
            Dickstein)
Solution:   Do not give an error when aborting. (closes vim/vim#10490)

https://github.com/vim/vim/commit/bf79a4e48d09a5ae08645592885d54230fed30b8

Co-authored-by: Bram Moolenaar 
---
 src/nvim/ex_docmd.c                |  2 +-
 src/nvim/testdir/test_trycatch.vim | 17 +++++++++++++++++
 2 files changed, 18 insertions(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index dad656d8b6..ed74993b84 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -726,7 +726,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
   if (cstack.cs_idx >= 0) {
     // If a sourced file or executed function ran to its end, report the
     // unclosed conditional.
-    if (!got_int && !did_throw
+    if (!got_int && !did_throw && !aborting()
         && ((getline_equal(fgetline, cookie, getsourceline)
              && !source_finished(fgetline, cookie))
             || (getline_equal(fgetline, cookie, get_func_line)
diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim
index 3dff8fa2d8..8a1d2d3fa7 100644
--- a/src/nvim/testdir/test_trycatch.vim
+++ b/src/nvim/testdir/test_trycatch.vim
@@ -2221,6 +2221,23 @@ func Test_user_command_throw_in_function_call()
   unlet g:caught
 endfunc
 
+" Test that after reporting an uncaught exception there is no error for a
+" missing :endif
+func Test_after_exception_no_endif_error()
+  function Throw()
+    throw "Failure"
+  endfunction
+
+  function Foo()
+    if 1
+      call Throw()
+    endif
+  endfunction
+  call assert_fails('call Foo()', ['E605:', 'E605:'])
+  delfunc Throw
+  delfunc Foo
+endfunc
+
 " Test for using throw in a called function with following endtry    {{{1
 func Test_user_command_function_call_with_endtry()
   let lines =<< trim END
-- 
cgit 


From 78e69412acb481c7ad56e68c541f5c5383992d5b Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 15:51:26 +0800
Subject: vim-patch:8.2.4688: new regexp engine does not give an error for
 "\%v"

Problem:    New regexp engine does not give an error for "\%v".
Solution:   Check for a value argument. (issue vim/vim#10079)

https://github.com/vim/vim/commit/91ff3d4f52a55a7c37a52aaad524cd9dd12efae4

Co-authored-by: Bram Moolenaar 
---
 src/nvim/regexp.c                      | 28 +++++++++++++++-------------
 src/nvim/regexp_bt.c                   |  2 +-
 src/nvim/regexp_nfa.c                  |  6 +++++-
 src/nvim/testdir/test_regexp_latin.vim | 12 ++++++++++++
 4 files changed, 33 insertions(+), 15 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 68ffc70457..3e8724c1ef 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -97,20 +97,22 @@ static int toggle_Magic(int x)
 
 #define MAX_LIMIT       (32767L << 16L)
 
-static char_u e_missingbracket[] = N_("E769: Missing ] after %s[");
-static char_u e_reverse_range[] = N_("E944: Reverse range in character class");
-static char_u e_large_class[] = N_("E945: Range too large in character class");
-static char_u e_unmatchedpp[] = N_("E53: Unmatched %s%%(");
-static char_u e_unmatchedp[] = N_("E54: Unmatched %s(");
-static char_u e_unmatchedpar[] = N_("E55: Unmatched %s)");
-static char_u e_z_not_allowed[] = N_("E66: \\z( not allowed here");
-static char_u e_z1_not_allowed[] = N_("E67: \\z1 - \\z9 not allowed here");
-static char_u e_missing_sb[] = N_("E69: Missing ] after %s%%[");
-static char_u e_empty_sb[] = N_("E70: Empty %s%%[]");
-static char_u e_recursive[] = N_("E956: Cannot use pattern recursively");
-static char_u e_regexp_number_after_dot_pos_search[]
+static char e_missingbracket[] = N_("E769: Missing ] after %s[");
+static char e_reverse_range[] = N_("E944: Reverse range in character class");
+static char e_large_class[] = N_("E945: Range too large in character class");
+static char e_unmatchedpp[] = N_("E53: Unmatched %s%%(");
+static char e_unmatchedp[] = N_("E54: Unmatched %s(");
+static char e_unmatchedpar[] = N_("E55: Unmatched %s)");
+static char e_z_not_allowed[] = N_("E66: \\z( not allowed here");
+static char e_z1_not_allowed[] = N_("E67: \\z1 - \\z9 not allowed here");
+static char e_missing_sb[] = N_("E69: Missing ] after %s%%[");
+static char e_empty_sb[] = N_("E70: Empty %s%%[]");
+static char e_recursive[] = N_("E956: Cannot use pattern recursively");
+static char e_regexp_number_after_dot_pos_search_chr[]
   = N_("E1204: No Number allowed after .: '\\%%%c'");
-static char_u e_substitute_nesting_too_deep[] = N_("E1290: substitute nesting too deep");
+static char e_nfa_regexp_missing_value_in_chr[]
+  = N_("E1273: (NFA regexp) missing value in '\\%%%c'");
+static char e_substitute_nesting_too_deep[] = N_("E1290: substitute nesting too deep");
 
 #define NOT_MULTI       0
 #define MULTI_ONE       1
diff --git a/src/nvim/regexp_bt.c b/src/nvim/regexp_bt.c
index ac33fc0f13..bde2962d3b 100644
--- a/src/nvim/regexp_bt.c
+++ b/src/nvim/regexp_bt.c
@@ -2117,7 +2117,7 @@ static char_u *regatom(int *flagp)
           break;
         } else if (c == 'l' || c == 'c' || c == 'v') {
           if (cur && n) {
-            semsg(_(e_regexp_number_after_dot_pos_search), no_Magic(c));
+            semsg(_(e_regexp_number_after_dot_pos_search_chr), no_Magic(c));
             rc_did_emsg = true;
             return NULL;
           }
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index fbd4e26c75..a2f4e209f1 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -2151,7 +2151,7 @@ static int nfa_regatom(void)
       }
       while (ascii_isdigit(c)) {
         if (cur) {
-          semsg(_(e_regexp_number_after_dot_pos_search), no_Magic(c));
+          semsg(_(e_regexp_number_after_dot_pos_search_chr), no_Magic(c));
           return FAIL;
         }
         if (n > (INT32_MAX - (c - '0')) / 10) {
@@ -2165,6 +2165,10 @@ static int nfa_regatom(void)
       if (c == 'l' || c == 'c' || c == 'v') {
         int32_t limit = INT32_MAX;
 
+        if (!cur && n == 0) {
+          semsg(_(e_nfa_regexp_missing_value_in_chr), no_Magic(c));
+          return FAIL;
+        }
         if (c == 'l') {
           if (cur) {
             n = curwin->w_cursor.lnum;
diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim
index 2a9a0e9d50..2671313997 100644
--- a/src/nvim/testdir/test_regexp_latin.vim
+++ b/src/nvim/testdir/test_regexp_latin.vim
@@ -105,6 +105,18 @@ func Test_multi_failure()
   set re=0
 endfunc
 
+func Test_column_failure()
+  set re=1
+  call assert_fails('/\%v', 'E71:')
+  call assert_fails('/\%c', 'E71:')
+  call assert_fails('/\%l', 'E71:')
+  set re=2
+  call assert_fails('/\%v', 'E1273:')
+  call assert_fails('/\%c', 'E1273:')
+  call assert_fails('/\%l', 'E1273:')
+  set re=0
+endfunc
+
 func Test_recursive_addstate()
   throw 'skipped: TODO: '
   " This will call addstate() recursively until it runs into the limit.
-- 
cgit 


From 77e25e56d8ccc0c174305f9fe64ad06f0223ab2d Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 15:56:15 +0800
Subject: vim-patch:8.2.4693: new regexp does not accept pattern "\%>0v"

Problem:    new regexp does not accept pattern "\%>0v".
Solution:   Do accept digit zero.

https://github.com/vim/vim/commit/72bb10df1fb3eb69bc91f5babfb8881ce098cba1

Co-authored-by: Bram Moolenaar 
---
 src/nvim/regexp_bt.c                   |  4 +++-
 src/nvim/regexp_nfa.c                  |  4 +++-
 src/nvim/testdir/test_regexp_latin.vim | 15 ++++++++++++++-
 3 files changed, 20 insertions(+), 3 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/regexp_bt.c b/src/nvim/regexp_bt.c
index bde2962d3b..f0efd06cc0 100644
--- a/src/nvim/regexp_bt.c
+++ b/src/nvim/regexp_bt.c
@@ -2091,6 +2091,7 @@ static char_u *regatom(int *flagp)
         uint32_t n = 0;
         int cmp;
         bool cur = false;
+        bool got_digit = false;
 
         cmp = c;
         if (cmp == '<' || cmp == '>') {
@@ -2101,6 +2102,7 @@ static char_u *regatom(int *flagp)
           c = getchr();
         }
         while (ascii_isdigit(c)) {
+          got_digit = true;
           n = n * 10 + (uint32_t)(c - '0');
           c = getchr();
         }
@@ -2115,7 +2117,7 @@ static char_u *regatom(int *flagp)
             *regcode++ = (char_u)cmp;
           }
           break;
-        } else if (c == 'l' || c == 'c' || c == 'v') {
+        } else if ((c == 'l' || c == 'c' || c == 'v') && (cur || got_digit)) {
           if (cur && n) {
             semsg(_(e_regexp_number_after_dot_pos_search_chr), no_Magic(c));
             rc_did_emsg = true;
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index a2f4e209f1..c93164ed7e 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -2141,6 +2141,7 @@ static int nfa_regatom(void)
       int64_t n = 0;
       const int cmp = c;
       bool cur = false;
+      bool got_digit = false;
 
       if (c == '<' || c == '>') {
         c = getchr();
@@ -2161,11 +2162,12 @@ static int nfa_regatom(void)
         }
         n = n * 10 + (c - '0');
         c = getchr();
+        got_digit = true;
       }
       if (c == 'l' || c == 'c' || c == 'v') {
         int32_t limit = INT32_MAX;
 
-        if (!cur && n == 0) {
+        if (!cur && !got_digit) {
           semsg(_(e_nfa_regexp_missing_value_in_chr), no_Magic(c));
           return FAIL;
         }
diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim
index 2671313997..5312c6f26a 100644
--- a/src/nvim/testdir/test_regexp_latin.vim
+++ b/src/nvim/testdir/test_regexp_latin.vim
@@ -105,16 +105,29 @@ func Test_multi_failure()
   set re=0
 endfunc
 
-func Test_column_failure()
+func Test_column_success_failure()
+  new
+  call setline(1, 'xbar')
+
   set re=1
+  %s/\%>0v./A/
+  call assert_equal('Abar', getline(1))
   call assert_fails('/\%v', 'E71:')
+  call assert_fails('/\%>v', 'E71:')
   call assert_fails('/\%c', 'E71:')
+  call assert_fails('/\%0v./B/
+  call assert_equal('Bbar', getline(1))
   call assert_fails('/\%v', 'E1273:')
+  call assert_fails('/\%>v', 'E1273:')
   call assert_fails('/\%c', 'E1273:')
+  call assert_fails('/\%
Date: Sat, 5 Nov 2022 15:59:17 +0800
Subject: vim-patch:8.2.4978: no error if engine selection atom is not at the
 start

Problem:    No error if engine selection atom is not at the start.
Solution:   Give an error. (Christian Brabandt, closes vim/vim#10439)

https://github.com/vim/vim/commit/360da40b47a84ee8586c3b5d062f8c64a2ac9cc6

Co-authored-by: Christian Brabandt 
---
 src/nvim/regexp.c                      |  2 ++
 src/nvim/regexp_bt.c                   |  5 +++++
 src/nvim/regexp_nfa.c                  |  6 ++++++
 src/nvim/testdir/test_regexp_latin.vim | 18 ++++++++++++++++++
 4 files changed, 31 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 3e8724c1ef..7a96889f22 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -112,6 +112,8 @@ static char e_regexp_number_after_dot_pos_search_chr[]
   = N_("E1204: No Number allowed after .: '\\%%%c'");
 static char e_nfa_regexp_missing_value_in_chr[]
   = N_("E1273: (NFA regexp) missing value in '\\%%%c'");
+static char e_atom_engine_must_be_at_start_of_pattern[]
+  = N_("E1281: Atom '\\%%#=%c' must be at the start of the pattern");
 static char e_substitute_nesting_too_deep[] = N_("E1290: substitute nesting too deep");
 
 #define NOT_MULTI       0
diff --git a/src/nvim/regexp_bt.c b/src/nvim/regexp_bt.c
index f0efd06cc0..6f63b38a90 100644
--- a/src/nvim/regexp_bt.c
+++ b/src/nvim/regexp_bt.c
@@ -1971,6 +1971,11 @@ static char_u *regatom(int *flagp)
       break;
 
     case '#':
+      if (regparse[0] == '=' && regparse[1] >= 48 && regparse[1] <= 50) {
+        // misplaced \%#=1
+        semsg(_(e_atom_engine_must_be_at_start_of_pattern), regparse[1]);
+        return FAIL;
+      }
       ret = regnode(CURSOR);
       break;
 
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index c93164ed7e..2f4b1b98c1 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -2094,6 +2094,12 @@ static int nfa_regatom(void)
       break;
 
     case '#':
+      if (regparse[0] == '=' && regparse[1] >= 48
+          && regparse[1] <= 50) {
+        // misplaced \%#=1
+        semsg(_(e_atom_engine_must_be_at_start_of_pattern), regparse[1]);
+        return FAIL;
+      }
       EMIT(NFA_CURSOR);
       break;
 
diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim
index 5312c6f26a..3a4a7ad910 100644
--- a/src/nvim/testdir/test_regexp_latin.vim
+++ b/src/nvim/testdir/test_regexp_latin.vim
@@ -1058,6 +1058,24 @@ func Test_using_invalid_visual_position()
   bwipe!
 endfunc
 
+func Test_using_two_engines_pattern()
+  new
+  call setline(1, ['foobar=0', 'foobar=1', 'foobar=2'])
+  " \%#= at the end of the pattern
+  for i in range(0, 2)
+    call cursor( (i+1), 7) 
+    call assert_fails("%s/foobar\\%#=" .. i, 'E1281:')
+  endfor
+
+  " \%#= at the start of the pattern
+  for i in range(0, 2)
+    call cursor( (i+1), 7) 
+    exe ":%s/\\%#=" .. i .. "foobar=" .. i .. "/xx"
+  endfor
+  call assert_equal(['xx', 'xx', 'xx'], getline(1, '$'))
+  bwipe!
+endfunc
+
 func Test_recursive_substitute_expr()
   new
   func Repl()
-- 
cgit 


From e33ffab1a776518dbf59ba5fe82453fa019569eb Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 16:03:00 +0800
Subject: vim-patch:9.0.0053: E1281 not tested with the old regexp engine
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Problem:    E1281 not tested with the old regexp engine.
Solution:   Loop over the values of 'regexp'. (Dominique Pellé, closes vim/vim#10695)

https://github.com/vim/vim/commit/3a393790a4fd7a5edcafbb55cd79438b6e641714

Co-authored-by: Dominique Pelle 
---
 src/nvim/testdir/test_regexp_latin.vim | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim
index 3a4a7ad910..ece6ae518e 100644
--- a/src/nvim/testdir/test_regexp_latin.vim
+++ b/src/nvim/testdir/test_regexp_latin.vim
@@ -1063,13 +1063,17 @@ func Test_using_two_engines_pattern()
   call setline(1, ['foobar=0', 'foobar=1', 'foobar=2'])
   " \%#= at the end of the pattern
   for i in range(0, 2)
-    call cursor( (i+1), 7) 
-    call assert_fails("%s/foobar\\%#=" .. i, 'E1281:')
+    for j in range(0, 2)
+      exe "set re=" .. i
+      call cursor(j + 1, 7)
+      call assert_fails("%s/foobar\\%#=" .. j, 'E1281:')
+    endfor
   endfor
+  set re=0
 
   " \%#= at the start of the pattern
   for i in range(0, 2)
-    call cursor( (i+1), 7) 
+    call cursor(i + 1, 7)
     exe ":%s/\\%#=" .. i .. "foobar=" .. i .. "/xx"
   endfor
   call assert_equal(['xx', 'xx', 'xx'], getline(1, '$'))
-- 
cgit 


From 562d572926e0c11329c13c9df45be7170888e7f6 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 22 Oct 2022 10:05:34 +0800
Subject: vim-patch:8.1.1949: cannot scroll a popup window to the very bottom

Problem:    Cannot scroll a popup window to the very bottom.
Solution:   Scroll to the bottom when the "firstline" property was set to -1.
            (closes vim/vim#4577)  Allow resetting min/max width/height.
https://github.com/vim/vim/commit/8c6173c7d3431dd8bc2b6ffc076ef49512a7e175

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval/typval.c | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 11b2b6e5fd..31b73f8d48 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -2060,10 +2060,25 @@ int tv_dict_get_tv(dict_T *d, const char *const key, typval_T *rettv)
 /// @return Dictionary item.
 varnumber_T tv_dict_get_number(const dict_T *const d, const char *const key)
   FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+  return tv_dict_get_number_def(d, key, 0);
+}
+
+/// Get a number item from a dictionary.
+///
+/// Returns "def" if the entry doesn't exist.
+///
+/// @param[in]  d  Dictionary to get item from.
+/// @param[in]  key  Key to find in dictionary.
+/// @param[in]  def  Default value.
+///
+/// @return Dictionary item.
+varnumber_T tv_dict_get_number_def(const dict_T *const d, const char *const key, const int def)
+  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
 {
   dictitem_T *const di = tv_dict_find(d, key, -1);
   if (di == NULL) {
-    return 0;
+    return def;
   }
   return tv_get_number(&di->di_tv);
 }
-- 
cgit 


From e25193143bca3a48920322634120a442854bb891 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 14:59:45 +0800
Subject: vim-patch:8.2.1751: using 2 where bool is expected may throw an error

Problem:    Using 2 where bool is expected may throw an error.
Solution:   Make this backwards compatible.

https://github.com/vim/vim/commit/bade44e5cad1b08c85d4a8ba08d94a30458dddfb

In legacy Vim script get_bool functions do the same thing as get_number
functions, so just add aliases using #define.

N/A patches for version.c:

vim-patch:8.2.1506: Vim9: no error when using a number other than 0 or 1 as bool

Problem:    Vim9: no error when using a number other than 0 or 1 as bool.
Solution:   Check the number is 0 or 1.

https://github.com/vim/vim/commit/d70840ed68296c1144d743e6335003c81c558c24

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval/funcs.c            | 6 ++++--
 src/nvim/eval/typval.h           | 5 +++++
 src/nvim/testdir/test_search.vim | 3 +++
 3 files changed, 12 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index cf924b1c49..1661a7079e 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -112,6 +112,8 @@ PRAGMA_DIAG_POP
 static char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob");
 static char *e_invalwindow = N_("E957: Invalid window number");
 static char *e_reduceempty = N_("E998: Reduce of an empty %s with no initial value");
+static char e_using_number_as_bool_nr[]
+  = N_("E1023: Using a Number as a Bool: %d");
 static char e_cannot_resize_window_in_another_tab_page[]
   = N_("E1308: Cannot resize a window in another tab page");
 
@@ -1361,7 +1363,7 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
     noref = (int)tv_get_number_chk(&argvars[1], NULL);
   }
   if (noref < 0 || noref > 1) {
-    emsg(_(e_invarg));
+    semsg(_(e_using_number_as_bool_nr), noref);
   } else {
     var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0
                                                    ? get_copyID()
@@ -8457,7 +8459,7 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
     skipcc = (int)tv_get_number_chk(&argvars[1], NULL);
   }
   if (skipcc < 0 || skipcc > 1) {
-    emsg(_(e_invarg));
+    semsg(_(e_using_number_as_bool_nr), skipcc);
   } else {
     func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv;
     while (*s != NUL) {
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index fcc933c967..6757d058ef 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -559,4 +559,9 @@ EXTERN const size_t kTVTranslate INIT(= TV_TRANSLATE);
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "eval/typval.h.generated.h"
 #endif
+
+#define tv_get_bool tv_get_number
+#define tv_get_bool_chk tv_get_number_chk
+#define tv_dict_get_bool tv_dict_get_number_def
+
 #endif  // NVIM_EVAL_TYPVAL_H
diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim
index e77a022d11..96ed35718f 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -294,6 +294,9 @@ func Test_searchpair()
   new
   call setline(1, ['other code', 'here [', ' [', ' " cursor here', ' ]]'])
 
+  " should not give an error for using "42"
+  call assert_equal(0, searchpair('a', 'b', 'c', '', 42))
+
   4
   call assert_equal(3, searchpair('\[', '', ']', 'bW'))
   call assert_equal([0, 3, 2, 0], getpos('.'))
-- 
cgit 


From 38c113ae842aa8c42ad66bba15f16cf401637ab8 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 17:43:38 +0800
Subject: vim-patch:8.2.1600: Vim9: cannot use "true" with deepcopy()

Problem:    Vim9: cannot use "true" with deepcopy().
Solution:   Use tv_get_bool_chk(). (closes vim/vim#6867)

https://github.com/vim/vim/commit/44b4a246b62e0622550b963bcf3034dce3bcfc0c

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval/funcs.c              | 2 +-
 src/nvim/testdir/test_listdict.vim | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 1661a7079e..553a377d21 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -1360,7 +1360,7 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   int noref = 0;
 
   if (argvars[1].v_type != VAR_UNKNOWN) {
-    noref = (int)tv_get_number_chk(&argvars[1], NULL);
+    noref = (int)tv_get_bool_chk(&argvars[1], NULL);
   }
   if (noref < 0 || noref > 1) {
     semsg(_(e_using_number_as_bool_nr), noref);
diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim
index 7cb48876e8..ecf95ba8c0 100644
--- a/src/nvim/testdir/test_listdict.vim
+++ b/src/nvim/testdir/test_listdict.vim
@@ -336,12 +336,12 @@ func Test_dict_deepcopy()
   let l = [4, d, 6]
   let d[3] = l
   let dc = deepcopy(d)
-  call assert_fails('call deepcopy(d, 1)', 'E698')
+  call assert_fails('call deepcopy(d, 1)', 'E698:')
   let l2 = [0, l, l, 3]
   let l[1] = l2
   let l3 = deepcopy(l2)
   call assert_true(l3[1] is l3[2])
-  call assert_fails("call deepcopy([1, 2], 2)", 'E474:')
+  call assert_fails("call deepcopy([1, 2], 2)", 'E1023:')
 endfunc
 
 " Locked variables
-- 
cgit 


From d857569dbdc0a9374b2cf661b9a7d49b38aad966 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 15:02:54 +0800
Subject: vim-patch:8.2.1624: Vim9: cannot pass "true" to split(), str2nr() and
 strchars()

Problem:    Vim9: cannot pass "true" to split(), str2nr() and strchars().
Solution:   Use tv_get_bool_chk(). (closes vim/vim#6884, closes vim/vim#6885, closes vim/vim#6886)

https://github.com/vim/vim/commit/3986b94b090ea258109630008611230a599999ab

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval/funcs.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 553a377d21..4939c5ef5d 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -8162,7 +8162,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
       typeerr = true;
     }
     if (argvars[2].v_type != VAR_UNKNOWN) {
-      keepempty = (bool)tv_get_number_chk(&argvars[2], &typeerr);
+      keepempty = (bool)tv_get_bool_chk(&argvars[2], &typeerr);
     }
   }
   if (pat == NULL || *pat == NUL) {
@@ -8292,7 +8292,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
       emsg(_(e_invarg));
       return;
     }
-    if (argvars[2].v_type != VAR_UNKNOWN && tv_get_number(&argvars[2])) {
+    if (argvars[2].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[2])) {
       what |= STR2NR_QUOTE;
     }
   }
@@ -8451,12 +8451,12 @@ static void f_strlen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 static void f_strchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 {
   const char *s = tv_get_string(&argvars[0]);
-  int skipcc = 0;
+  int skipcc = false;
   varnumber_T len = 0;
   int (*func_mb_ptr2char_adv)(const char_u **pp);
 
   if (argvars[1].v_type != VAR_UNKNOWN) {
-    skipcc = (int)tv_get_number_chk(&argvars[1], NULL);
+    skipcc = (int)tv_get_bool(&argvars[1]);
   }
   if (skipcc < 0 || skipcc > 1) {
     semsg(_(e_using_number_as_bool_nr), skipcc);
-- 
cgit 


From 451850920b361059ba99bbde4b90b7730327eebb Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 15:06:39 +0800
Subject: vim-patch:8.2.1626: test for strchars() fails with different error
 number

Problem:    Test for strchars() fails with different error number.
Solution:   Adjust the error number.

https://github.com/vim/vim/commit/707be5f3524accb8b36e80bd2532e00b8246df55

Co-authored-by: Bram Moolenaar 
---
 src/nvim/testdir/test_utf8.vim | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim
index 8e13ed778f..3d29d38231 100644
--- a/src/nvim/testdir/test_utf8.vim
+++ b/src/nvim/testdir/test_utf8.vim
@@ -22,7 +22,7 @@ func Test_strchars()
     call assert_equal(exp[i][2], strchars(inp[i], 1))
   endfor
   call assert_fails("let v=strchars('abc', [])", 'E745:')
-  call assert_fails("let v=strchars('abc', 2)", 'E474:')
+  call assert_fails("let v=strchars('abc', 2)", 'E1023:')
 endfunc
 
 " Test for customlist completion
-- 
cgit 


From 781616bee5e319fdfa034484c026222b50926ebf Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 17:16:49 +0800
Subject: vim-patch:8.2.2606: strchars() defaults to counting composing
 characters

Problem:    strchars() defaults to counting composing characters.
Solution:   Add strcharlen() which ignores composing characters.

https://github.com/vim/vim/commit/70ce8a1561c5396e4c4381f76a005cbb97646f80

Use docs from latest Vim instead.

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval.lua              |  1 +
 src/nvim/eval/funcs.c          | 30 +++++++++++++++++++++---------
 src/nvim/testdir/test_utf8.vim |  9 ++++++++-
 3 files changed, 30 insertions(+), 10 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 61e7f99afd..f47d258125 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -376,6 +376,7 @@ return {
     str2float={args=1, base=1},
     str2list={args={1, 2}, base=1},
     str2nr={args={1, 3}, base=1},
+    strcharlen={args=1, base=1},
     strcharpart={args={2, 3}, base=1},
     strchars={args={1, 2}, base=1},
     strdisplaywidth={args={1, 2}, base=1},
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 4939c5ef5d..7ad4850d40 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -8447,26 +8447,38 @@ static void f_strlen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   rettv->vval.v_number = (varnumber_T)strlen(tv_get_string(&argvars[0]));
 }
 
-/// "strchars()" function
-static void f_strchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+static void strchar_common(typval_T *argvars, typval_T *rettv, bool skipcc)
 {
   const char *s = tv_get_string(&argvars[0]);
-  int skipcc = false;
   varnumber_T len = 0;
   int (*func_mb_ptr2char_adv)(const char_u **pp);
 
+  func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv;
+  while (*s != NUL) {
+    func_mb_ptr2char_adv((const char_u **)&s);
+    len++;
+  }
+  rettv->vval.v_number = len;
+}
+
+/// "strcharlen()" function
+static void f_strcharlen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+  strchar_common(argvars, rettv, true);
+}
+
+/// "strchars()" function
+static void f_strchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+  int skipcc = false;
+
   if (argvars[1].v_type != VAR_UNKNOWN) {
     skipcc = (int)tv_get_bool(&argvars[1]);
   }
   if (skipcc < 0 || skipcc > 1) {
     semsg(_(e_using_number_as_bool_nr), skipcc);
   } else {
-    func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv;
-    while (*s != NUL) {
-      func_mb_ptr2char_adv((const char_u **)&s);
-      len++;
-    }
-    rettv->vval.v_number = len;
+    strchar_common(argvars, rettv, skipcc);
   }
 }
 
diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim
index 3d29d38231..aa3b02b575 100644
--- a/src/nvim/testdir/test_utf8.vim
+++ b/src/nvim/testdir/test_utf8.vim
@@ -12,7 +12,7 @@ func Test_visual_block_insert()
   bwipeout!
 endfunc
 
-" Test for built-in function strchars()
+" Test for built-in functions strchars() and strcharlen()
 func Test_strchars()
   let inp = ["a", "ã‚ã„a", "A\u20dd", "A\u20dd\u20dd", "\u20dd"]
   let exp = [[1, 1, 1], [3, 3, 3], [2, 2, 1], [3, 3, 1], [1, 1, 1]]
@@ -21,6 +21,13 @@ func Test_strchars()
     call assert_equal(exp[i][1], inp[i]->strchars(0))
     call assert_equal(exp[i][2], strchars(inp[i], 1))
   endfor
+
+  let exp = [1, 3, 1, 1, 1]
+  for i in range(len(inp))
+    call assert_equal(exp[i], inp[i]->strcharlen())
+    call assert_equal(exp[i], strcharlen(inp[i]))
+  endfor
+
   call assert_fails("let v=strchars('abc', [])", 'E745:')
   call assert_fails("let v=strchars('abc', 2)", 'E1023:')
 endfunc
-- 
cgit 


From 8e868d699a9d0b68342d8d460a8f6dd9c075d7a8 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 22 Oct 2022 09:32:01 +0800
Subject: vim-patch:8.2.4679: cannot have expandcmd() give an error message for
 mistakes

Problem:    Cannot have expandcmd() give an error message for mistakes.
Solution:   Add an optional argument to give errors. Fix memory leak when
            expanding files fails. (Yegappan Lakshmanan, closes vim/vim#10071)
https://github.com/vim/vim/commit/2b74b6805b5c8c4836b66df5d949f5ff6a77f8c7

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/eval.lua                |  2 +-
 src/nvim/eval/funcs.c            | 20 +++++++++++++++++---
 src/nvim/path.c                  |  2 +-
 src/nvim/testdir/test_expand.vim | 18 +++++++++++++++---
 4 files changed, 34 insertions(+), 8 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index f47d258125..8bfa6797d0 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -118,7 +118,7 @@ return {
     exists={args=1, base=1},
     exp={args=1, base=1, float_func="exp"},
     expand={args={1, 3}, base=1},
-    expandcmd={args=1, base=1},
+    expandcmd={args={1, 2}, base=1},
     extend={args={2, 3}, base=1},
     feedkeys={args={1, 2}, base=1},
     file_readable={args=1, base=1, func='f_filereadable'},  -- obsolete
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 7ad4850d40..1e971791bd 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -2058,6 +2058,12 @@ static void f_menu_get(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 static void f_expandcmd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 {
   char *errormsg = NULL;
+  bool emsgoff = true;
+
+  if (argvars[1].v_type == VAR_DICT
+      && tv_dict_get_bool(argvars[1].vval.v_dict, "errmsg", kBoolVarFalse)) {
+    emsgoff = false;
+  }
 
   rettv->v_type = VAR_STRING;
   char *cmdstr = xstrdup(tv_get_string(&argvars[0]));
@@ -2071,9 +2077,17 @@ static void f_expandcmd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   };
   eap.argt |= EX_NOSPC;
 
-  emsg_off++;
-  expand_filename(&eap, &cmdstr, &errormsg);
-  emsg_off--;
+  if (emsgoff) {
+    emsg_off++;
+  }
+  if (expand_filename(&eap, &cmdstr, &errormsg) == FAIL) {
+    if (!emsgoff && errormsg != NULL && *errormsg != NUL) {
+      emsg(errormsg);
+    }
+  }
+  if (emsgoff) {
+    emsg_off--;
+  }
 
   rettv->vval.v_string = cmdstr;
 }
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 625a3c7922..f568ba8854 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -1244,7 +1244,7 @@ int gen_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, i
       add_pat = expand_backtick(&ga, (char *)p, flags);
       if (add_pat == -1) {
         recursive = false;
-        FreeWild(ga.ga_len, ga.ga_data);
+        ga_clear_strings(&ga);
         *num_file = 0;
         *file = NULL;
         return FAIL;
diff --git a/src/nvim/testdir/test_expand.vim b/src/nvim/testdir/test_expand.vim
index aa131a49ff..4f5bb67d21 100644
--- a/src/nvim/testdir/test_expand.vim
+++ b/src/nvim/testdir/test_expand.vim
@@ -90,14 +90,26 @@ func Test_expandcmd()
   " Test for expression expansion `=
   let $FOO= "blue"
   call assert_equal("blue sky", expandcmd("`=$FOO .. ' sky'`"))
+  let x = expandcmd("`=axbycz`")
+  call assert_equal('`=axbycz`', x)
+  call assert_fails('let x = expandcmd("`=axbycz`", #{errmsg: 1})', 'E121:')
+  let x = expandcmd("`=axbycz`", #{abc: []})
+  call assert_equal('`=axbycz`', x)
 
   " Test for env variable with spaces
   let $FOO= "foo bar baz"
   call assert_equal("e foo bar baz", expandcmd("e $FOO"))
 
-  if has('unix')
-    " test for using the shell to expand a command argument
-    call assert_equal('{1..4}', expandcmd('{1..4}'))
+  if has('unix') && executable('bash')
+    " test for using the shell to expand a command argument.
+    " only bash supports the {..} syntax
+    set shell=bash
+    let x = expandcmd('{1..4}')
+    call assert_equal('{1..4}', x)
+    call assert_fails("let x = expandcmd('{1..4}', #{errmsg: v:true})", 'E77:')
+    let x = expandcmd('{1..4}', #{error: v:true})
+    call assert_equal('{1..4}', x)
+    set shell&
   endif
 
   unlet $FOO
-- 
cgit 


From 3c0651fb459189f5be6165454810e4476296cb7c Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 18:02:05 +0800
Subject: fix(eval): make error number of charidx() same as Vim

---
 src/nvim/eval/funcs.c               | 2 +-
 src/nvim/testdir/test_functions.vim | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 1e971791bd..26a5c133da 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -908,7 +908,7 @@ static void f_charidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
     countcc = (int)tv_get_number(&argvars[2]);
   }
   if (countcc < 0 || countcc > 1) {
-    emsg(_(e_invarg));
+    semsg(_(e_using_number_as_bool_nr), countcc);
     return;
   }
 
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 06c995db86..d2603809b9 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -1101,8 +1101,8 @@ func Test_charidx()
   call assert_fails('let x = charidx([], 1)', 'E474:')
   call assert_fails('let x = charidx("abc", [])', 'E474:')
   call assert_fails('let x = charidx("abc", 1, [])', 'E474:')
-  call assert_fails('let x = charidx("abc", 1, -1)', 'E474:')
-  call assert_fails('let x = charidx("abc", 1, 2)', 'E474:')
+  call assert_fails('let x = charidx("abc", 1, -1)', 'E1023:')
+  call assert_fails('let x = charidx("abc", 1, 2)', 'E1023:')
 endfunc
 
 func Test_count()
-- 
cgit 


From 3e60b9f1cc7a24e801d1ee38b4d03032cc6962f2 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 1 Apr 2022 17:58:19 +0800
Subject: vim-patch:8.2.4029: debugging NFA regexp my crash, cached indent may
 be wrong

Problem:    Debugging NFA regexp my crash, cached indent may be wrong.
Solution:   Fix some debug warnings in the NFA regexp code.  Make sure log_fd
            is set when used.  Fix breakindent and indent caching. (Christian
            Brabandt, closes vim/vim#9482)
https://github.com/vim/vim/commit/b2d85e3784ac89f5209489844c1ee0f54d117abb
---
 src/nvim/indent.c     | 47 +++++++++++++++++++++++++++++------------------
 src/nvim/optionstr.c  | 10 ++++++++++
 src/nvim/regexp_nfa.c | 45 ++++++++++++++++++++++++++-------------------
 3 files changed, 65 insertions(+), 37 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index cb5819e946..c929a0d14a 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -783,19 +783,22 @@ int get_breakindent_win(win_T *wp, char_u *line)
   FUNC_ATTR_NONNULL_ALL
 {
   static int prev_indent = 0;  // Cached indent value.
-  static long prev_ts = 0;  // Cached tabstop value.
+  static long prev_ts = 0L;  // Cached tabstop value.
   static const char_u *prev_line = NULL;  // cached pointer to line.
   static varnumber_T prev_tick = 0;  // Changedtick of cached value.
-  static long *prev_vts = NULL;    // Cached vartabs values.
+  static long *prev_vts = NULL;  // Cached vartabs values.
+  static int prev_list = 0;  // cached list value
+  static int prev_listopt = 0;  // cached w_p_briopt_list value
   int bri = 0;
   // window width minus window margin space, i.e. what rests for text
   const int eff_wwidth = wp->w_width_inner -
                          ((wp->w_p_nu || wp->w_p_rnu)
                           && (vim_strchr(p_cpo, CPO_NUMCOL) == NULL) ? number_width(wp) + 1 : 0);
 
-  // used cached indent, unless pointer or 'tabstop' changed
+  // used cached indent, unless line, 'tabstop' or briopt_list changed
   if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts
       || prev_tick != buf_get_changedtick(wp->w_buffer)
+      || prev_listopt != wp->w_briopt_list
       || prev_vts != wp->w_buffer->b_p_vts_array) {
     prev_line = line;
     prev_ts = wp->w_buffer->b_p_ts;
@@ -805,6 +808,25 @@ int get_breakindent_win(win_T *wp, char_u *line)
                                       wp->w_buffer->b_p_ts,
                                       wp->w_buffer->b_p_vts_array,
                                       wp->w_p_list);
+    prev_listopt = wp->w_briopt_list;
+    // add additional indent for numbered lists
+    if (wp->w_briopt_list != 0) {
+      regmatch_T regmatch = {
+        .regprog = vim_regcomp(curbuf->b_p_flp,
+                               RE_MAGIC + RE_STRING + RE_AUTO + RE_STRICT),
+      };
+      if (regmatch.regprog != NULL) {
+        regmatch.rm_ic = false;
+        if (vim_regexec(®match, (char *)line, 0)) {
+          if (wp->w_briopt_list > 0) {
+            prev_list += wp->w_briopt_list;
+          } else {
+            prev_list = (int)(*regmatch.endp - *regmatch.startp);
+          }
+        }
+        vim_regfree(regmatch.regprog);
+      }
+    }
   }
   bri = prev_indent + wp->w_briopt_shift;
 
@@ -813,21 +835,10 @@ int get_breakindent_win(win_T *wp, char_u *line)
 
   // add additional indent for numbered lists
   if (wp->w_briopt_list != 0) {
-    regmatch_T regmatch = {
-      .regprog = vim_regcomp(curbuf->b_p_flp,
-                             RE_MAGIC + RE_STRING + RE_AUTO + RE_STRICT),
-    };
-
-    if (regmatch.regprog != NULL) {
-      regmatch.rm_ic = false;
-      if (vim_regexec(®match, (char *)line, 0)) {
-        if (wp->w_briopt_list > 0) {
-          bri += wp->w_briopt_list;
-        } else {
-          bri = (int)(*regmatch.endp - *regmatch.startp);
-        }
-      }
-      vim_regfree(regmatch.regprog);
+    if (wp->w_briopt_list > 0) {
+      bri += prev_list;
+    } else {
+      bri = prev_list;
     }
   }
 
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index 43628d2842..65bc9f60df 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -697,6 +697,10 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
     if (briopt_check(curwin) == FAIL) {
       errmsg = e_invarg;
     }
+    // list setting requires a redraw
+    if (curwin->w_briopt_list) {
+      redraw_all_later(UPD_NOT_VALID);
+    }
   } else if (varp == &p_isi
              || varp == &(curbuf->b_p_isk)
              || varp == &p_isp
@@ -1601,6 +1605,12 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
     setmouse();  // in case 'mouse' changed
   }
 
+  // Changing Formatlistpattern when briopt includes the list setting:
+  // redraw
+  if ((varp == &p_flp || varp == &(curbuf->b_p_flp)) && curwin->w_briopt_list) {
+    redraw_all_later(UPD_NOT_VALID);
+  }
+
   if (curwin->w_curswant != MAXCOL
       && (opt->flags & (P_CURSWANT | P_RALL)) != 0) {
     curwin->w_set_curswant = true;
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index 2f4b1b98c1..d4d2ed28cc 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -3370,8 +3370,8 @@ static void nfa_print_state2(FILE *debugf, nfa_state_T *state, garray_T *indent)
     int last = indent->ga_len - 3;
     char_u save[2];
 
-    STRNCPY(save, &p[last], 2);
-    STRNCPY(&p[last], "+-", 2);
+    STRNCPY(save, &p[last], 2);  // NOLINT(runtime/printf)
+    memcpy(&p[last], "+-", 2);
     fprintf(debugf, " %s", p);
     STRNCPY(&p[last], save, 2);  // NOLINT(runtime/printf)
   } else {
@@ -4635,6 +4635,20 @@ static bool sub_equal(regsub_T *sub1, regsub_T *sub2)
 }
 
 #ifdef REGEXP_DEBUG
+static void open_debug_log(TriState result)
+{
+  log_fd = fopen(NFA_REGEXP_RUN_LOG, "a");
+  if (log_fd == NULL) {
+    emsg(_(e_log_open_failed));
+    log_fd = stderr;
+  }
+
+  fprintf(log_fd, "****************************\n");
+  fprintf(log_fd, "FINISHED RUNNING nfa_regmatch() recursively\n");
+  fprintf(log_fd, "MATCH = %s\n", result == kTrue ? "OK" : result == kNone ? "MAYBE" : "FALSE");
+  fprintf(log_fd, "****************************\n");
+}
+
 static void report_state(char *action, regsub_T *sub, nfa_state_T *state, int lid, nfa_pim_T *pim)
 {
   int col;
@@ -4647,6 +4661,9 @@ static void report_state(char *action, regsub_T *sub, nfa_state_T *state, int li
     col = (int)(sub->list.line[0].start - rex.line);
   }
   nfa_set_code(state->c);
+  if (log_fd == NULL) {
+    open_debug_log(kNone);
+  }
   fprintf(log_fd, "> %s state %d to list %d. char %d: %s (start col %d)%s\n",
           action, abs(state->id), lid, state->c, code, col,
           pim_info(pim));
@@ -5668,16 +5685,7 @@ static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T
   nfa_endp = save_nfa_endp;
 
 #ifdef REGEXP_DEBUG
-  log_fd = fopen(NFA_REGEXP_RUN_LOG, "a");
-  if (log_fd != NULL) {
-    fprintf(log_fd, "****************************\n");
-    fprintf(log_fd, "FINISHED RUNNING nfa_regmatch() recursively\n");
-    fprintf(log_fd, "MATCH = %s\n", !result ? "false" : "OK");
-    fprintf(log_fd, "****************************\n");
-  } else {
-    emsg(_(e_log_open_failed));
-    log_fd = stderr;
-  }
+  open_debug_log(result);
 #endif
 
   return result;
@@ -5983,16 +5991,15 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
 
 #ifdef REGEXP_DEBUG
   log_fd = fopen(NFA_REGEXP_RUN_LOG, "a");
-  if (log_fd != NULL) {
-    fprintf(log_fd, "**********************************\n");
-    nfa_set_code(start->c);
-    fprintf(log_fd, " RUNNING nfa_regmatch() starting with state %d, code %s\n",
-            abs(start->id), code);
-    fprintf(log_fd, "**********************************\n");
-  } else {
+  if (log_fd == NULL) {
     emsg(_(e_log_open_failed));
     log_fd = stderr;
   }
+  fprintf(log_fd, "**********************************\n");
+  nfa_set_code(start->c);
+  fprintf(log_fd, " RUNNING nfa_regmatch() starting with state %d, code %s\n",
+          abs(start->id), code);
+  fprintf(log_fd, "**********************************\n");
 #endif
 
   thislist = &list[0];
-- 
cgit 


From b92ed35a0bb873589bba9b51fdb92ffd00dc3e57 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 1 Apr 2022 18:12:55 +0800
Subject: vim-patch:8.2.4093: cached breakindent values not initialized
 properly

Problem:    Cached breakindent values not initialized properly.
Solution:   Initialize and cache formatlistpat. (Christian Brabandt,
            closes vim/vim#9526, closes vim/vim#9512)
https://github.com/vim/vim/commit/c53b467473160b5cfce77277fbae414bf43e66ce

Co-authored-by: Christian Brabandt 
---
 src/nvim/indent.c                     | 14 +++++++--
 src/nvim/option.c                     | 12 ++++++++
 src/nvim/testdir/test_breakindent.vim | 57 +++++++++++++++++++++++++++++++++++
 3 files changed, 80 insertions(+), 3 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index c929a0d14a..359dd32fdf 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -789,16 +789,22 @@ int get_breakindent_win(win_T *wp, char_u *line)
   static long *prev_vts = NULL;  // Cached vartabs values.
   static int prev_list = 0;  // cached list value
   static int prev_listopt = 0;  // cached w_p_briopt_list value
+  static char *prev_flp = NULL;  // cached formatlistpat value
   int bri = 0;
   // window width minus window margin space, i.e. what rests for text
   const int eff_wwidth = wp->w_width_inner -
                          ((wp->w_p_nu || wp->w_p_rnu)
                           && (vim_strchr(p_cpo, CPO_NUMCOL) == NULL) ? number_width(wp) + 1 : 0);
 
-  // used cached indent, unless line, 'tabstop' or briopt_list changed
+  // used cached indent, unless
+  // - line pointer changed
+  // - 'tabstop' changed
+  // - 'briopt_list changed' changed or
+  // - 'formatlistpattern' changed
   if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts
       || prev_tick != buf_get_changedtick(wp->w_buffer)
       || prev_listopt != wp->w_briopt_list
+      || (prev_flp == NULL || (strcmp(prev_flp, get_flp_value(wp->w_buffer)) != 0))
       || prev_vts != wp->w_buffer->b_p_vts_array) {
     prev_line = line;
     prev_ts = wp->w_buffer->b_p_ts;
@@ -809,11 +815,13 @@ int get_breakindent_win(win_T *wp, char_u *line)
                                       wp->w_buffer->b_p_vts_array,
                                       wp->w_p_list);
     prev_listopt = wp->w_briopt_list;
+    prev_list = 0;
+    xfree(prev_flp);
+    prev_flp = xstrdup(get_flp_value(wp->w_buffer));
     // add additional indent for numbered lists
     if (wp->w_briopt_list != 0) {
       regmatch_T regmatch = {
-        .regprog = vim_regcomp(curbuf->b_p_flp,
-                               RE_MAGIC + RE_STRING + RE_AUTO + RE_STRICT),
+        .regprog = vim_regcomp(prev_flp, RE_MAGIC + RE_STRING + RE_AUTO + RE_STRICT),
       };
       if (regmatch.regprog != NULL) {
         regmatch.rm_ic = false;
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 8de86ce76e..dc9f01ff60 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -5220,6 +5220,18 @@ unsigned int get_bkc_value(buf_T *buf)
   return buf->b_bkc_flags ? buf->b_bkc_flags : bkc_flags;
 }
 
+/// Get the local or global value of 'formatlistpat'.
+///
+/// @param buf The buffer.
+char *get_flp_value(buf_T *buf)
+{
+  return buf->b_p_flp ? buf->b_p_flp : p_flp;
+  if (buf->b_p_flp == NULL || *buf->b_p_flp == NUL) {
+    return p_flp;
+  }
+  return buf->b_p_flp;
+}
+
 /// Get the local or global value of the 'virtualedit' flags.
 unsigned int get_ve_flags(void)
 {
diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim
index 69c98f1f05..a665ee5b28 100644
--- a/src/nvim/testdir/test_breakindent.vim
+++ b/src/nvim/testdir/test_breakindent.vim
@@ -889,4 +889,61 @@ func Test_window_resize_with_linebreak()
   %bw!
 endfunc
 
+func Test_no_spurious_match()
+  let s:input = printf('- y %s y %s', repeat('x', 50), repeat('x', 50))
+  call s:test_windows('setl breakindent breakindentopt=list:-1 formatlistpat=^- hls')
+  let @/ = '\%>3v[y]'
+  redraw!
+  call searchcount().total->assert_equal(1)
+  " cleanup
+  set hls&vim
+  let s:input = "\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP"
+  bwipeout!
+endfunc
+
+func Test_no_extra_indent()
+  call s:test_windows('setl breakindent breakindentopt=list:-1,min:10')
+  %d
+  let &l:formatlistpat='^\s*\d\+\.\s\+'
+  let text = 'word '
+  let len = text->strcharlen()
+  let line1 = text->repeat((winwidth(0) / len) * 2)
+  let line2 = repeat(' ', 2) .. '1. ' .. line1
+  call setline(1, [line2])
+  redraw!
+  " 1) matches formatlist pattern, so indent
+  let expect = [
+  \ "  1. word word word ",
+  \ "     word word word ",
+  \ "     word word      ",
+  \ "~                   ",
+  \ ]
+  let lines = s:screen_lines2(1, 4, 20)
+  call s:compare_lines(expect, lines)
+  " 2) change formatlist pattern
+  " -> indent adjusted
+  let &l:formatlistpat='^\s*\d\+\.'
+  let expect = [
+  \ "  1. word word word ",
+  \ "    word word word  ",
+  \ "    word word       ",
+  \ "~                   ",
+  \ ]
+  let lines = s:screen_lines2(1, 4, 20)
+  " 3) add something in front, no additional indent
+  norm! gg0
+  exe ":norm! 5iword \"
+  redraw!
+  let expect = [
+  \ "word word word word ",
+  \ "word   1. word word ",
+  \ "word word word word ",
+  \ "word word           ",
+  \ "~                   ",
+  \ ]
+  let lines = s:screen_lines2(1, 5, 20)
+  call s:compare_lines(expect, lines)
+  bwipeout!
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From 8b43091392ec895c77b83ff5964cd37b54976089 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 1 Apr 2022 18:31:19 +0800
Subject: vim-patch:8.2.4100: early return when getting the 'formatlistpat'
 value

Problem:    Early return when getting the 'formatlistpat' value.
Solution:   Remove the first line. (Christian Brabandt)
https://github.com/vim/vim/commit/04b871da800768287a8a432de568b11297db8686
---
 src/nvim/option.c                     |  1 -
 src/nvim/testdir/test_breakindent.vim | 17 ++++++++++++++++-
 2 files changed, 16 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/option.c b/src/nvim/option.c
index dc9f01ff60..8142be4eb1 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -5225,7 +5225,6 @@ unsigned int get_bkc_value(buf_T *buf)
 /// @param buf The buffer.
 char *get_flp_value(buf_T *buf)
 {
-  return buf->b_p_flp ? buf->b_p_flp : p_flp;
   if (buf->b_p_flp == NULL || *buf->b_p_flp == NUL) {
     return p_flp;
   }
diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim
index a665ee5b28..8bbac2d237 100644
--- a/src/nvim/testdir/test_breakindent.vim
+++ b/src/nvim/testdir/test_breakindent.vim
@@ -930,7 +930,22 @@ func Test_no_extra_indent()
   \ "~                   ",
   \ ]
   let lines = s:screen_lines2(1, 4, 20)
-  " 3) add something in front, no additional indent
+  " 3) no local formatlist pattern,
+  " so use global one -> indent
+  let g_flp = &g:flp
+  let &g:formatlistpat='^\s*\d\+\.\s\+'
+  let &l:formatlistpat=''
+  let expect = [
+  \ "  1. word word word ",
+  \ "     word word word ",
+  \ "     word word      ",
+  \ "~                   ",
+  \ ]
+  let lines = s:screen_lines2(1, 4, 20)
+  call s:compare_lines(expect, lines)
+  let &g:flp = g_flp
+  let &l:formatlistpat='^\s*\d\+\.'
+  " 4) add something in front, no additional indent
   norm! gg0
   exe ":norm! 5iword \"
   redraw!
-- 
cgit 


From 6374120558476b9ab0ec4dc1df0523c73756e4ae Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 19:08:25 +0800
Subject: vim-patch:8.2.4501: with 'showbreak' set cursor displayed in wrong
 position

Problem:    With 'showbreak' set and after the end of the line the cursor
            may be displayed in the wrong position.
Solution:   Do not apply 'showbreak' after the end of the line. (closes vim/vim#9884)

https://github.com/vim/vim/commit/21efafe4c25373929979c72dc8aafa119f12dd8b

Co-authored-by: Bram Moolenaar 
---
 src/nvim/plines.c                     |  4 ++--
 src/nvim/testdir/test_breakindent.vim | 22 ++++++++++++++++++++++
 2 files changed, 24 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/plines.c b/src/nvim/plines.c
index 42218ac847..bed15f9e36 100644
--- a/src/nvim/plines.c
+++ b/src/nvim/plines.c
@@ -444,9 +444,9 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
   // May have to add something for 'breakindent' and/or 'showbreak'
   // string at start of line.
   // Set *headp to the size of what we add.
+  // Do not use 'showbreak' at the NUL after the text.
   added = 0;
-
-  char *const sbr = (char *)get_showbreak_value(wp);
+  char *const sbr = c == NUL ? empty_option : (char *)get_showbreak_value(wp);
   if ((*sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && vcol != 0) {
     colnr_T sbrlen = 0;
     int numberwidth = win_col_off(wp);
diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim
index 8bbac2d237..934dca4793 100644
--- a/src/nvim/testdir/test_breakindent.vim
+++ b/src/nvim/testdir/test_breakindent.vim
@@ -8,6 +8,7 @@ source check.vim
 CheckOption breakindent
 
 source view_util.vim
+source screendump.vim
 
 let s:input ="\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP"
 
@@ -889,6 +890,27 @@ func Test_window_resize_with_linebreak()
   %bw!
 endfunc
 
+func Test_cursor_position_with_showbreak()
+  CheckScreendump
+
+  let lines =<< trim END
+      vim9script
+      &signcolumn = 'yes'
+      &showbreak = '+ '
+      var leftcol: number = win_getid()->getwininfo()->get(0, {})->get('textoff')
+      repeat('x', &columns - leftcol - 1)->setline(1)
+      'second line'->setline(2)
+  END
+  call writefile(lines, 'XscriptShowbreak')
+  let buf = RunVimInTerminal('-S XscriptShowbreak', #{rows: 6})
+
+  call term_sendkeys(buf, "AX")
+  call VerifyScreenDump(buf, 'Test_cursor_position_with_showbreak', {})
+
+  call StopVimInTerminal(buf)
+  call delete('XscriptShowbreak')
+endfunc
+
 func Test_no_spurious_match()
   let s:input = printf('- y %s y %s', repeat('x', 50), repeat('x', 50))
   call s:test_windows('setl breakindent breakindentopt=list:-1 formatlistpat=^- hls')
-- 
cgit 


From e0ec83a9701ffd9b30a41763ad2e2326d85d8480 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 19:19:55 +0800
Subject: vim-patch:8.2.4882: cannot make 'breakindent' use a specific column

Problem:    Cannot make 'breakindent' use a specific column.
Solution:   Add the "column" entry in 'breakindentopt'. (Christian Brabandt,
            closes vim/vim#10362, closes vim/vim#10325)

https://github.com/vim/vim/commit/e7d6dbc5721342e3d6b04cf285e4510b5569e707

Co-authored-by: Christian Brabandt 
---
 src/nvim/buffer_defs.h                |  4 ++-
 src/nvim/indent.c                     | 27 +++++++++++----
 src/nvim/testdir/test_breakindent.vim | 62 ++++++++++++++++++++++++++++++++---
 3 files changed, 81 insertions(+), 12 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 3629199f9a..4e46a1aef2 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -963,7 +963,8 @@ struct frame_S {
                             // for first
   // fr_child and fr_win are mutually exclusive
   frame_T *fr_child;        // first contained frame
-  win_T *fr_win;          // window that fills this frame
+  win_T *fr_win;        // window that fills this frame; for a snapshot
+                        // set to the current window
 };
 
 #define FR_LEAF 0       // frame is a leaf
@@ -1340,6 +1341,7 @@ struct window_S {
   int w_briopt_shift;               // additional shift for breakindent
   bool w_briopt_sbr;                // sbr in 'briopt'
   int w_briopt_list;                // additional indent for lists
+  int w_briopt_vcol;                // indent for specific column
 
   // transform a pointer to a "onebuf" option into a "allbuf" option
 #define GLOBAL_WO(p)    ((char *)(p) + sizeof(winopt_T))
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index 359dd32fdf..d372e41459 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -743,6 +743,7 @@ bool briopt_check(win_T *wp)
   int bri_min = 20;
   bool bri_sbr = false;
   int bri_list = 0;
+  int bri_vcol = 0;
 
   char *p = wp->w_p_briopt;
   while (*p != NUL) {
@@ -759,6 +760,9 @@ bool briopt_check(win_T *wp)
     } else if (STRNCMP(p, "list:", 5) == 0) {
       p += 5;
       bri_list = (int)getdigits(&p, false, 0);
+    } else if (STRNCMP(p, "column:", 7) == 0) {
+      p += 7;
+      bri_vcol = (int)getdigits(&p, false, 0);
     }
     if (*p != ',' && *p != NUL) {
       return false;
@@ -771,7 +775,8 @@ bool briopt_check(win_T *wp)
   wp->w_briopt_shift = bri_shift;
   wp->w_briopt_min = bri_min;
   wp->w_briopt_sbr = bri_sbr;
-  wp->w_briopt_list  = bri_list;
+  wp->w_briopt_list = bri_list;
+  wp->w_briopt_vcol = bri_vcol;
 
   return true;
 }
@@ -810,16 +815,18 @@ int get_breakindent_win(win_T *wp, char_u *line)
     prev_ts = wp->w_buffer->b_p_ts;
     prev_tick = buf_get_changedtick(wp->w_buffer);
     prev_vts = wp->w_buffer->b_p_vts_array;
-    prev_indent = get_indent_str_vtab((char *)line,
-                                      wp->w_buffer->b_p_ts,
-                                      wp->w_buffer->b_p_vts_array,
-                                      wp->w_p_list);
+    if (wp->w_briopt_vcol == 0) {
+      prev_indent = get_indent_str_vtab((char *)line,
+                                        wp->w_buffer->b_p_ts,
+                                        wp->w_buffer->b_p_vts_array,
+                                        wp->w_p_list);
+    }
     prev_listopt = wp->w_briopt_list;
     prev_list = 0;
     xfree(prev_flp);
     prev_flp = xstrdup(get_flp_value(wp->w_buffer));
     // add additional indent for numbered lists
-    if (wp->w_briopt_list != 0) {
+    if (wp->w_briopt_list != 0 && wp->w_briopt_vcol == 0) {
       regmatch_T regmatch = {
         .regprog = vim_regcomp(prev_flp, RE_MAGIC + RE_STRING + RE_AUTO + RE_STRICT),
       };
@@ -836,7 +843,13 @@ int get_breakindent_win(win_T *wp, char_u *line)
       }
     }
   }
-  bri = prev_indent + wp->w_briopt_shift;
+  if (wp->w_briopt_vcol != 0) {
+    // column value has priority
+    bri = wp->w_briopt_vcol;
+    prev_list = 0;
+  } else {
+    bri = prev_indent + wp->w_briopt_shift;
+  }
 
   // Add offset for number column, if 'n' is in 'cpoptions'
   bri += win_col_off2(wp);
diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim
index 934dca4793..995683c68c 100644
--- a/src/nvim/testdir/test_breakindent.vim
+++ b/src/nvim/testdir/test_breakindent.vim
@@ -877,16 +877,17 @@ endfunc
 func Test_window_resize_with_linebreak()
   new
   53vnew
-  set linebreak
-  set showbreak=>>
-  set breakindent
-  set breakindentopt=shift:4
+  setl linebreak
+  setl showbreak=>>
+  setl breakindent
+  setl breakindentopt=shift:4
   call setline(1, "\naaaaaaaaa\n\na\naaaaa\n¯aaaaaaaaaa\naaaaaaaaaaaa\naaa\n\"a:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaa\"\naaaaaaaa\n\"a")
   redraw!
   call assert_equal(["    >>aa^@\"a: "], ScreenLines(2, 14))
   vertical resize 52
   redraw!
   call assert_equal(["    >>aaa^@\"a:"], ScreenLines(2, 14))
+  set linebreak& showbreak& breakindent& breakindentopt&
   %bw!
 endfunc
 
@@ -983,4 +984,57 @@ func Test_no_extra_indent()
   bwipeout!
 endfunc
 
+func Test_breakindent_column()
+  " restore original
+  let s:input ="\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP"
+  call s:test_windows('setl breakindent breakindentopt=column:10')
+  redraw!
+  " 1) default: does not indent, too wide :(
+  let expect = [
+  \ "                    ",
+  \ "    abcdefghijklmnop",
+  \ "qrstuvwxyzABCDEFGHIJ",
+  \ "KLMNOP              "
+  \ ]
+  let lines = s:screen_lines2(1, 4, 20)
+  call s:compare_lines(expect, lines)
+  " 2) lower min value, so that breakindent works
+  setl breakindentopt+=min:5
+  redraw!
+  let expect = [
+  \ "                    ",
+  \ "    abcdefghijklmnop",
+  \ "          qrstuvwxyz",
+  \ "          ABCDEFGHIJ",
+  \ "          KLMNOP    "
+  \ ]
+  let lines = s:screen_lines2(1, 5, 20)
+  " 3) set shift option -> no influence
+  setl breakindentopt+=shift:5
+  redraw!
+  let expect = [
+  \ "                    ",
+  \ "    abcdefghijklmnop",
+  \ "          qrstuvwxyz",
+  \ "          ABCDEFGHIJ",
+  \ "          KLMNOP    "
+  \ ]
+  let lines = s:screen_lines2(1, 5, 20)
+  call s:compare_lines(expect, lines)
+  " 4) add showbreak value
+  setl showbreak=++
+  redraw!
+  let expect = [
+  \ "                    ",
+  \ "    abcdefghijklmnop",
+  \ "          ++qrstuvwx",
+  \ "          ++yzABCDEF",
+  \ "          ++GHIJKLMN",
+  \ "          ++OP      "
+  \ ]
+  let lines = s:screen_lines2(1, 6, 20)
+  call s:compare_lines(expect, lines)
+  bwipeout!
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From cb8bc9b33c84ad0447d5aed262970d718d406501 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 21:37:57 +0800
Subject: vim-patch:8.2.0883: memory leak in test 49

Problem:    Memory leak in test 49.
Solution:   Free "sfile" from the exception.

https://github.com/vim/vim/commit/5fbf3bc3f9d007ab91eb005f9e3da6570992cb43

Co-authored-by: Bram Moolenaar 
---
 src/nvim/ex_docmd.c | 1 +
 1 file changed, 1 insertion(+)

(limited to 'src/nvim')

diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index ed74993b84..dc2b7247f1 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -807,6 +807,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
           next = messages->next;
           emsg(messages->msg);
           xfree(messages->msg);
+          xfree(messages->sfile);
           xfree(messages);
           messages = next;
         } while (messages != NULL);
-- 
cgit 


From 01ccfb40e3ac678829f301aec1d10f5fc06548c6 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 20:16:29 +0800
Subject: vim-patch:8.2.1106: crash when trying to use s: variable in typed
 command

Problem:    Crash when trying to use s: variable in typed command.
Solution:   Don't use the script index when not set. (Ken Takata,
            closes vim/vim#6366)

https://github.com/vim/vim/commit/8e6cbb72324b6fb25d1a9abd6cc4d102d0e5f14e
---
 src/nvim/testdir/test_vimscript.vim | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim
index 3a6baaf1f0..a7ab78d87a 100644
--- a/src/nvim/testdir/test_vimscript.vim
+++ b/src/nvim/testdir/test_vimscript.vim
@@ -1810,6 +1810,7 @@ func Test_float_conversion_errors()
   endif
 endfunc
 
+" invalid function names               {{{1
 func Test_invalid_function_names()
   " function name not starting with capital
   let caught_e128 = 0
@@ -1870,7 +1871,7 @@ func Test_invalid_function_names()
   call delete('Xscript')
 endfunc
 
-" substring and variable name
+" substring and variable name              {{{1
 func Test_substring_var()
   let str = 'abcdef'
   let n = 3
@@ -1890,6 +1891,20 @@ func Test_substring_var()
   unlet b:nn
 endfunc
 
+" Test using s: with a typed command              {{{1
+func Test_typed_script_var()
+  CheckRunVimInTerminal
+
+  let buf = RunVimInTerminal('', {'rows': 6})
+
+  " Deep nesting of if ... endif
+  call term_sendkeys(buf, ":echo get(s:, 'foo', 'x')\n")
+  call TermWait(buf)
+  call WaitForAssert({-> assert_match('^E116:', term_getline(buf, 5))})
+
+  call StopVimInTerminal(buf)
+endfunc
+
 func Test_for_over_string()
   let res = ''
   for c in 'aéc̀d'
-- 
cgit 


From 42e7c7fc9c50394bd9fc4e56b0487b47f2931531 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 20:23:29 +0800
Subject: vim-patch:8.2.1366: test 49 is old style

Problem:    Test 49 is old style.
Solution:   Convert several tests to new style. (Yegappan Lakshmanan,
            closes vim/vim#6629)

https://github.com/vim/vim/commit/a6296200bd5191bab7efcdcc16c9e79eb498e8e0
---
 src/nvim/testdir/test49.ok          |   21 -
 src/nvim/testdir/test49.vim         | 1643 +------------------------------
 src/nvim/testdir/test_vimscript.vim | 1852 +++++++++++++++++++++++++++++++++--
 3 files changed, 1764 insertions(+), 1752 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test49.ok b/src/nvim/testdir/test49.ok
index 9f283e808b..50696fd643 100644
--- a/src/nvim/testdir/test49.ok
+++ b/src/nvim/testdir/test49.ok
@@ -1,25 +1,4 @@
 Results of test49.vim:
-*** Test  18: OK (67224583)
-*** Test  19: OK (69275973)
-*** Test  20: OK (1874575085)
-*** Test  21: OK (147932225)
-*** Test  22: OK (4161)
-*** Test  23: OK (49)
-*** Test  24: OK (41)
-*** Test  27: OK (1996459)
-*** Test  28: OK (1996459)
-*** Test  29: OK (170428555)
-*** Test  30: OK (190905173)
-*** Test  31: OK (190905173)
-*** Test  34: OK (2146584868)
-*** Test  35: OK (2146584868)
-*** Test  36: OK (1071644672)
-*** Test  37: OK (1071644672)
-*** Test  38: OK (357908480)
-*** Test  39: OK (357908480)
-*** Test  40: OK (357908480)
-*** Test  49: OK (179000669)
-*** Test  50: OK (363550045)
 *** Test  52: OK (1247112011)
 *** Test  53: OK (131071)
 *** Test  54: OK (2047)
diff --git a/src/nvim/testdir/test49.vim b/src/nvim/testdir/test49.vim
index 3ee5e9ff7c..d51ba24153 100644
--- a/src/nvim/testdir/test49.vim
+++ b/src/nvim/testdir/test49.vim
@@ -1,6 +1,6 @@
 " Vim script language tests
 " Author:	Servatius Brandt 
-" Last Change:	2019 Nov 03
+" Last Change:	2020 Jun 07
 
 "-------------------------------------------------------------------------------
 " Test environment							    {{{1
@@ -607,1647 +607,10 @@ com! -nargs=1 -bar ExecAsScript call ExecAsScript()
 
 " END_OF_TEST_ENVIRONMENT - do not change or remove this line.
 
-
-" Tests 1 to 17 were moved to test_vimscript.vim
-let Xtest = 18
-
-"-------------------------------------------------------------------------------
-" Test 18:  Interrupt (Ctrl-C pressed)					    {{{1
-"
-"	    On an interrupt, the script processing is terminated immediately.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-    if 1
-	Xpath 1					" X: 1
-	while 1
-	    Xpath 2				" X: 2
-	    if 1
-		Xpath 4				" X: 4
-		"INTERRUPT
-		Xpath 8				" X: 0
-		break
-		finish
-	    endif | Xpath 16			" X: 0
-	    Xpath 32				" X: 0
-	endwhile | Xpath 64			" X: 0
-	Xpath 128				" X: 0
-    endif | Xpath 256				" X: 0
-    Xpath 512					" X: 0
-endif
-
-if ExtraVim()
-    try
-	Xpath 1024				" X: 1024
-	"INTERRUPT
-	Xpath 2048				" X: 0
-    endtry | Xpath 4096				" X: 0
-    Xpath 8192					" X: 0
-endif
-
-if ExtraVim()
-    function! F()
-	if 1
-	    Xpath 16384				" X: 16384
-	    while 1
-		Xpath 32768			" X: 32768
-		if 1
-		    Xpath 65536			" X: 65536
-		    "INTERRUPT
-		    Xpath 131072		" X: 0
-		    break
-		    return
-		endif | Xpath 262144		" X: 0
-		Xpath Xpath 524288		" X: 0
-	    endwhile | Xpath 1048576		" X: 0
-	    Xpath Xpath 2097152			" X: 0
-	endif | Xpath Xpath 4194304		" X: 0
-	Xpath Xpath 8388608			" X: 0
-    endfunction
-
-    call F() | Xpath 16777216			" X: 0
-    Xpath 33554432				" X: 0
-endif
-
-if ExtraVim()
-    function! G()
-	try
-	    Xpath 67108864			" X: 67108864
-	    "INTERRUPT
-	    Xpath 134217728			" X: 0
-	endtry | Xpath 268435456		" X: 0
-	Xpath 536870912				" X: 0
-    endfunction
-
-    call G() | Xpath 1073741824			" X: 0
-    " The Xpath command does not accept 2^31 (negative); display explicitly:
-    exec "!echo 2147483648 >>" . g:ExtraVimResult
-						" X: 0
-endif
-
-Xcheck 67224583
-
-
-"-------------------------------------------------------------------------------
-" Test 19:  Aborting on errors inside :try/:endtry			    {{{1
-"
-"	    An error in a command dynamically enclosed in a :try/:endtry region
-"	    aborts script processing immediately.  It does not matter whether
-"	    the failing command is outside or inside a function and whether a
-"	    function has an "abort" attribute.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-    function! F() abort
-	Xpath 1					" X: 1
-	asdf
-	Xpath 2					" X: 0
-    endfunction
-
-    try
-	Xpath 4					" X: 4
-	call F()
-	Xpath 8					" X: 0
-    endtry | Xpath 16				" X: 0
-    Xpath 32					" X: 0
-endif
-
-if ExtraVim()
-    function! G()
-	Xpath 64				" X: 64
-	asdf
-	Xpath 128				" X: 0
-    endfunction
-
-    try
-	Xpath 256				" X: 256
-	call G()
-	Xpath 512				" X: 0
-    endtry | Xpath 1024				" X: 0
-    Xpath 2048					" X: 0
-endif
-
-if ExtraVim()
-    try
-	Xpath 4096				" X: 4096
-	asdf
-	Xpath 8192				" X: 0
-    endtry | Xpath 16384			" X: 0
-    Xpath 32768					" X: 0
-endif
-
-if ExtraVim()
-    if 1
-	try
-	    Xpath 65536				" X: 65536
-	    asdf
-	    Xpath 131072			" X: 0
-	endtry | Xpath 262144			" X: 0
-    endif | Xpath 524288			" X: 0
-    Xpath 1048576				" X: 0
-endif
-
-if ExtraVim()
-    let p = 1
-    while p
-	let p = 0
-	try
-	    Xpath 2097152			" X: 2097152
-	    asdf
-	    Xpath 4194304			" X: 0
-	endtry | Xpath 8388608			" X: 0
-    endwhile | Xpath 16777216			" X: 0
-    Xpath 33554432				" X: 0
-endif
-
-if ExtraVim()
-    let p = 1
-    while p
-	let p = 0
-"	try
-	    Xpath 67108864			" X: 67108864
-    endwhile | Xpath 134217728			" X: 0
-    Xpath 268435456				" X: 0
-endif
-
-Xcheck 69275973
-"-------------------------------------------------------------------------------
-" Test 20:  Aborting on errors after :try/:endtry			    {{{1
-"
-"	    When an error occurs after the last active :try/:endtry region has
-"	    been left, termination behavior is as if no :try/:endtry has been
-"	    seen.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-    let p = 1
-    while p
-	let p = 0
-	try
-	    Xpath 1				" X: 1
-	endtry
-	asdf
-    endwhile | Xpath 2				" X: 0
-    Xpath 4					" X: 4
-endif
-
-if ExtraVim()
-    while 1
-	try
-	    Xpath 8				" X: 8
-	    break
-	    Xpath 16				" X: 0
-	endtry
-    endwhile
-    Xpath 32					" X: 32
-    asdf
-    Xpath 64					" X: 64
-endif
-
-if ExtraVim()
-    while 1
-	try
-	    Xpath 128				" X: 128
-	    break
-	    Xpath 256				" X: 0
-	finally
-	    Xpath 512				" X: 512
-	endtry
-    endwhile
-    Xpath 1024					" X: 1024
-    asdf
-    Xpath 2048					" X: 2048
-endif
-
-if ExtraVim()
-    while 1
-	try
-	    Xpath 4096				" X: 4096
-	finally
-	    Xpath 8192				" X: 8192
-	    break
-	    Xpath 16384				" X: 0
-	endtry
-    endwhile
-    Xpath 32768					" X: 32768
-    asdf
-    Xpath 65536					" X: 65536
-endif
-
-if ExtraVim()
-    let p = 1
-    while p
-	let p = 0
-	try
-	    Xpath 131072			" X: 131072
-	    continue
-	    Xpath 262144			" X: 0
-	endtry
-    endwhile
-    Xpath 524288				" X: 524288
-    asdf
-    Xpath 1048576				" X: 1048576
-endif
-
-if ExtraVim()
-    let p = 1
-    while p
-	let p = 0
-	try
-	    Xpath 2097152			" X: 2097152
-	    continue
-	    Xpath 4194304			" X: 0
-	finally
-	    Xpath 8388608			" X: 8388608
-	endtry
-    endwhile
-    Xpath 16777216				" X: 16777216
-    asdf
-    Xpath 33554432				" X: 33554432
-endif
-
-if ExtraVim()
-    let p = 1
-    while p
-	let p = 0
-	try
-	    Xpath 67108864			" X: 67108864
-	finally
-	    Xpath 134217728			" X: 134217728
-	    continue
-	    Xpath 268435456			" X: 0
-	endtry
-    endwhile
-    Xpath 536870912				" X: 536870912
-    asdf
-    Xpath 1073741824				" X: 1073741824
-endif
-
-Xcheck 1874575085
-
-
-"-------------------------------------------------------------------------------
-" Test 21:  :finally for :try after :continue/:break/:return/:finish	    {{{1
-"
-"	    If a :try conditional stays inactive due to a preceding :continue,
-"	    :break, :return, or :finish, its :finally clause should not be
-"	    executed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-    function F()
-	let loops = 2
-	XloopINIT! 1 256
-	while loops > 0
-	    XloopNEXT
-	    let loops = loops - 1
-	    try
-		if loops == 1
-		    Xloop 1			" X: 1
-		    continue
-		    Xloop 2			" X: 0
-		elseif loops == 0
-		    Xloop 4			" X: 4*256
-		    break
-		    Xloop 8			" X: 0
-		endif
-
-		try		" inactive
-		    Xloop 16			" X: 0
-		finally
-		    Xloop 32			" X: 0
-		endtry
-	    finally
-		Xloop 64			" X: 64 + 64*256
-	    endtry
-	    Xloop 128				" X: 0
-	endwhile
-
-	try
-	    Xpath 65536				" X: 65536
-	    return
-	    Xpath 131072			" X: 0
-	    try		    " inactive
-		Xpath 262144			" X: 0
-	    finally
-		Xpath 524288			" X: 0
-	    endtry
-	finally
-	    Xpath 1048576			" X: 1048576
-	endtry
-	Xpath 2097152				" X: 0
-    endfunction
-
-    try
-	Xpath 4194304				" X: 4194304
-	call F()
-	Xpath 8388608				" X: 8388608
-	finish
-	Xpath 16777216				" X: 0
-	try		" inactive
-	    Xpath 33554432			" X: 0
-	finally
-	    Xpath 67108864			" X: 0
-	endtry
-    finally
-	Xpath 134217728				" X: 134217728
-    endtry
-    Xpath 268435456				" X: 0
-endif
-
-Xcheck 147932225
-
-
-"-------------------------------------------------------------------------------
-" Test 22:  :finally for a :try after an error/interrupt/:throw		    {{{1
-"
-"	    If a :try conditional stays inactive due to a preceding error or
-"	    interrupt or :throw, its :finally clause should not be executed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-    function! Error()
-	try
-	    asdf    " aborting error, triggering error exception
-	endtry
-    endfunction
-
-    Xpath 1					" X: 1
-    call Error()
-    Xpath 2					" X: 0
-
-    if 1	" not active due to error
-	try	" not active since :if inactive
-	    Xpath 4				" X: 0
-	finally
-	    Xpath 8				" X: 0
-	endtry
-    endif
-
-    try		" not active due to error
-	Xpath 16				" X: 0
-    finally
-	Xpath 32				" X: 0
-    endtry
-endif
-
-if ExtraVim()
-    function! Interrupt()
-	try
-	    "INTERRUPT	" triggering interrupt exception
-	endtry
-    endfunction
-
-    Xpath 64					" X: 64
-    call Interrupt()
-    Xpath 128					" X: 0
-
-    if 1	" not active due to interrupt
-	try	" not active since :if inactive
-	    Xpath 256				" X: 0
-	finally
-	    Xpath 512				" X: 0
-	endtry
-    endif
-
-    try		" not active due to interrupt
-	Xpath 1024				" X: 0
-    finally
-	Xpath 2048				" X: 0
-    endtry
-endif
-
-if ExtraVim()
-    function! Throw()
-	throw "xyz"
-    endfunction
-
-    Xpath 4096					" X: 4096
-    call Throw()
-    Xpath 8192					" X: 0
-
-    if 1	" not active due to :throw
-	try	" not active since :if inactive
-	    Xpath 16384				" X: 0
-	finally
-	    Xpath 32768				" X: 0
-	endtry
-    endif
-
-    try		" not active due to :throw
-	Xpath 65536				" X: 0
-    finally
-	Xpath 131072				" X: 0
-    endtry
-endif
-
-Xcheck 4161
-
-
-"-------------------------------------------------------------------------------
-" Test 23:  :catch clauses for a :try after a :throw			    {{{1
-"
-"	    If a :try conditional stays inactive due to a preceding :throw,
-"	    none of its :catch clauses should be executed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-    try
-	Xpath 1					" X: 1
-	throw "xyz"
-	Xpath 2					" X: 0
-
-	if 1	" not active due to :throw
-	    try	" not active since :if inactive
-		Xpath 4				" X: 0
-	    catch /xyz/
-		Xpath 8				" X: 0
-	    endtry
-	endif
-    catch /xyz/
-	Xpath 16				" X: 16
-    endtry
-
-    Xpath 32					" X: 32
-    throw "abc"
-    Xpath 64					" X: 0
-
-    try		" not active due to :throw
-	Xpath 128				" X: 0
-    catch /abc/
-	Xpath 256				" X: 0
-    endtry
-endif
-
-Xcheck 49
-
-
-"-------------------------------------------------------------------------------
-" Test 24:  :endtry for a :try after a :throw				    {{{1
-"
-"	    If a :try conditional stays inactive due to a preceding :throw,
-"	    its :endtry should not rethrow the exception to the next surrounding
-"	    active :try conditional.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-    try			" try 1
-	try		" try 2
-	    Xpath 1				" X: 1
-	    throw "xyz"	" makes try 2 inactive
-	    Xpath 2				" X: 0
-
-	    try		" try 3
-		Xpath 4				" X: 0
-	    endtry	" no rethrow to try 1
-	catch /xyz/	" should catch although try 2 inactive
-	    Xpath 8				" X: 8
-	endtry
-    catch /xyz/		" try 1 active, but exception already caught
-	Xpath 16				" X: 0
-    endtry
-    Xpath 32					" X: 32
-endif
-
-Xcheck 41
-
-" Tests 25 and 26 were moved to test_trycatch.vim
-let Xtest = 27
-
-
-"-------------------------------------------------------------------------------
-" Test 27:  Executing :finally clauses after :return			    {{{1
-"
-"	    For a :return command dynamically enclosed in a :try/:endtry region,
-"	    :finally clauses are executed and the called function is ended.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! F()
-    try
-	Xpath 1					" X: 1
-	try
-	    Xpath 2				" X: 2
-	    return
-	    Xpath 4				" X: 0
-	finally
-	    Xpath 8				" X: 8
-	endtry
-	Xpath 16				" X: 0
-    finally
-	Xpath 32				" X: 32
-    endtry
-    Xpath 64					" X: 0
-endfunction
-
-function! G()
-    try
-	Xpath 128				" X: 128
-	return
-	Xpath 256				" X: 0
-    finally
-	Xpath 512				" X: 512
-	call F()
-	Xpath 1024				" X: 1024
-    endtry
-    Xpath 2048					" X: 0
-endfunction
-
-function! H()
-    try
-	Xpath 4096				" X: 4096
-	call G()
-	Xpath 8192				" X: 8192
-    finally
-	Xpath 16384				" X: 16384
-	return
-	Xpath 32768				" X: 0
-    endtry
-    Xpath 65536					" X: 0
-endfunction
-
-try
-    Xpath 131072				" X: 131072
-    call H()
-    Xpath 262144				" X: 262144
-finally
-    Xpath 524288				" X: 524288
-endtry
-Xpath 1048576					" X: 1048576
-
-Xcheck 1996459
-
-" Leave F, G, and H for execution as scripts in the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 28:  Executing :finally clauses after :finish			    {{{1
-"
-"	    For a :finish command dynamically enclosed in a :try/:endtry region,
-"	    :finally clauses are executed and the sourced file is finished.
-"
-"	    This test executes the bodies of the functions F, G, and H from the
-"	    previous test as script files (:return replaced by :finish).
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let scriptF = MakeScript("F")			" X: 1 + 2 + 8 + 32
-let scriptG = MakeScript("G", scriptF)		" X: 128 + 512 + 1024
-let scriptH = MakeScript("H", scriptG)		" X: 4096 + 8192 + 16384
-
-try
-    Xpath 131072				" X: 131072
-    exec "source" scriptH
-    Xpath 262144				" X: 262144
-finally
-    Xpath 524288				" X: 524288
-endtry
-Xpath 1048576					" X: 1048576
-
-call delete(scriptF)
-call delete(scriptG)
-call delete(scriptH)
-unlet scriptF scriptG scriptH
-delfunction F
-delfunction G
-delfunction H
-
-Xcheck 1996459
-
-
-"-------------------------------------------------------------------------------
-" Test 29:  Executing :finally clauses on errors			    {{{1
-"
-"	    After an error in a command dynamically enclosed in a :try/:endtry
-"	    region, :finally clauses are executed and the script processing is
-"	    terminated.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-    function! F()
-	while 1
-	    try
-		Xpath 1				" X: 1
-		while 1
-		    try
-			Xpath 2			" X: 2
-			asdf	    " error
-			Xpath 4			" X: 0
-		    finally
-			Xpath 8			" X: 8
-		    endtry | Xpath 16		" X: 0
-		    Xpath 32			" X: 0
-		    break
-		endwhile
-		Xpath 64			" X: 0
-	    finally
-		Xpath 128			" X: 128
-	    endtry | Xpath 256			" X: 0
-	    Xpath 512				" X: 0
-	    break
-	endwhile
-	Xpath 1024				" X: 0
-    endfunction
-
-    while 1
-	try
-	    Xpath 2048				" X: 2048
-	    while 1
-		call F()
-		Xpath 4096			" X: 0
-		break
-	    endwhile  | Xpath 8192		" X: 0
-	    Xpath 16384				" X: 0
-	finally
-	    Xpath 32768				" X: 32768
-	endtry | Xpath 65536			" X: 0
-    endwhile | Xpath 131072			" X: 0
-    Xpath 262144				" X: 0
-endif
-
-if ExtraVim()
-    function! G() abort
-	if 1
-	    try
-		Xpath 524288			" X: 524288
-		asdf	    " error
-		Xpath 1048576			" X: 0
-	    finally
-		Xpath 2097152			" X: 2097152
-	    endtry | Xpath 4194304		" X: 0
-	endif | Xpath 8388608			" X: 0
-	Xpath 16777216				" X: 0
-    endfunction
-
-    if 1
-	try
-	    Xpath 33554432			" X: 33554432
-	    call G()
-	    Xpath 67108864			" X: 0
-	finally
-	    Xpath 134217728			" X: 134217728
-	endtry | Xpath 268435456		" X: 0
-    endif | Xpath 536870912			" X: 0
-    Xpath 1073741824				" X: 0
-endif
-
-Xcheck 170428555
-
-
-"-------------------------------------------------------------------------------
-" Test 30:  Executing :finally clauses on interrupt			    {{{1
-"
-"	    After an interrupt in a command dynamically enclosed in
-"	    a :try/:endtry region, :finally clauses are executed and the
-"	    script processing is terminated.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-    XloopINIT 1 16
-
-    function! F()
-	try
-	    Xloop 1				" X: 1 + 1*16
-	    "INTERRUPT
-	    Xloop 2				" X: 0
-	finally
-	    Xloop 4				" X: 4 + 4*16
-	endtry
-	Xloop 8					" X: 0
-    endfunction
-
-    try
-	Xpath 256				" X: 256
-	try
-	    Xpath 512				" X: 512
-	    "INTERRUPT
-	    Xpath 1024				" X: 0
-	finally
-	    Xpath 2048				" X: 2048
-	    try
-		Xpath 4096			" X: 4096
-		try
-		    Xpath 8192			" X: 8192
-		finally
-		    Xpath 16384			" X: 16384
-		    try
-			Xpath 32768		" X: 32768
-			"INTERRUPT
-			Xpath 65536		" X: 0
-		    endtry
-		    Xpath 131072		" X: 0
-		endtry
-		Xpath 262144			" X: 0
-	    endtry
-	    Xpath 524288			" X: 0
-	endtry
-	Xpath 1048576				" X: 0
-    finally
-	Xpath 2097152				" X: 2097152
-	try
-	    Xpath 4194304			" X: 4194304
-	    call F()
-	    Xpath 8388608			" X: 0
-	finally
-	    Xpath 16777216			" X: 16777216
-	    try
-		Xpath 33554432			" X: 33554432
-		XloopNEXT
-		ExecAsScript F
-		Xpath 67108864			" X: 0
-	    finally
-		Xpath 134217728			" X: 134217728
-	    endtry
-	    Xpath 268435456			" X: 0
-	endtry
-	Xpath 536870912				" X: 0
-    endtry
-    Xpath 1073741824				" X: 0
-endif
-
-Xcheck 190905173
-
-
-"-------------------------------------------------------------------------------
-" Test 31:  Executing :finally clauses after :throw			    {{{1
-"
-"	    After a :throw dynamically enclosed in a :try/:endtry region,
-"	    :finally clauses are executed and the script processing is
-"	    terminated.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-    XloopINIT 1 16
-
-    function! F()
-	try
-	    Xloop 1				" X: 1 + 1*16
-	    throw "exception"
-	    Xloop 2				" X: 0
-	finally
-	    Xloop 4				" X: 4 + 4*16
-	endtry
-	Xloop 8					" X: 0
-    endfunction
-
-    try
-	Xpath 256				" X: 256
-	try
-	    Xpath 512				" X: 512
-	    throw "exception"
-	    Xpath 1024				" X: 0
-	finally
-	    Xpath 2048				" X: 2048
-	    try
-		Xpath 4096			" X: 4096
-		try
-		    Xpath 8192			" X: 8192
-		finally
-		    Xpath 16384			" X: 16384
-		    try
-			Xpath 32768		" X: 32768
-			throw "exception"
-			Xpath 65536		" X: 0
-		    endtry
-		    Xpath 131072		" X: 0
-		endtry
-		Xpath 262144			" X: 0
-	    endtry
-	    Xpath 524288			" X: 0
-	endtry
-	Xpath 1048576				" X: 0
-    finally
-	Xpath 2097152				" X: 2097152
-	try
-	    Xpath 4194304			" X: 4194304
-	    call F()
-	    Xpath 8388608			" X: 0
-	finally
-	    Xpath 16777216			" X: 16777216
-	    try
-		Xpath 33554432			" X: 33554432
-		XloopNEXT
-		ExecAsScript F
-		Xpath 67108864			" X: 0
-	    finally
-		Xpath 134217728			" X: 134217728
-	    endtry
-	    Xpath 268435456			" X: 0
-	endtry
-	Xpath 536870912				" X: 0
-    endtry
-    Xpath 1073741824				" X: 0
-endif
-
-Xcheck 190905173
-
-" Tests 32 and 33 were moved to test_trycatch.vim
-let Xtest = 34
-
-
-"-------------------------------------------------------------------------------
-" Test 34:  :finally reason discarded by :continue			    {{{1
-"
-"	    When a :finally clause is executed due to a :continue, :break,
-"	    :return, :finish, error, interrupt or :throw, the jump reason is
-"	    discarded by a :continue in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
-    XloopINIT! 1 8
-
-    function! C(jump)
-	XloopNEXT
-	let loop = 0
-	while loop < 2
-	    let loop = loop + 1
-	    if loop == 1
-		try
-		    if a:jump == "continue"
-			continue
-		    elseif a:jump == "break"
-			break
-		    elseif a:jump == "return" || a:jump == "finish"
-			return
-		    elseif a:jump == "error"
-			asdf
-		    elseif a:jump == "interrupt"
-			"INTERRUPT
-			let dummy = 0
-		    elseif a:jump == "throw"
-			throw "abc"
-		    endif
-		finally
-		    continue	" discards jump that caused the :finally
-		    Xloop 1		" X: 0
-		endtry
-		Xloop 2			" X: 0
-	    elseif loop == 2
-		Xloop 4			" X: 4*(1+8+64+512+4096+32768+262144)
-	    endif
-	endwhile
-    endfunction
-
-    call C("continue")
-    Xpath 2097152				" X: 2097152
-    call C("break")
-    Xpath 4194304				" X: 4194304
-    call C("return")
-    Xpath 8388608				" X: 8388608
-    let g:jump = "finish"
-    ExecAsScript C
-    unlet g:jump
-    Xpath 16777216				" X: 16777216
-    try
-	call C("error")
-	Xpath 33554432				" X: 33554432
-    finally
-	Xpath 67108864				" X: 67108864
-	try
-	    call C("interrupt")
-	    Xpath 134217728			" X: 134217728
-	finally
-	    Xpath 268435456			" X: 268435456
-	    call C("throw")
-	    Xpath 536870912			" X: 536870912
-	endtry
-    endtry
-    Xpath 1073741824				" X: 1073741824
-
-    delfunction C
-
-endif
-
-Xcheck 2146584868
-
-
-"-------------------------------------------------------------------------------
-" Test 35:  :finally reason discarded by :break				    {{{1
-"
-"	    When a :finally clause is executed due to a :continue, :break,
-"	    :return, :finish, error, interrupt or :throw, the jump reason is
-"	    discarded by a :break in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
-    XloopINIT! 1 8
-
-    function! B(jump)
-	XloopNEXT
-	let loop = 0
-	while loop < 2
-	    let loop = loop + 1
-	    if loop == 1
-		try
-		    if a:jump == "continue"
-			continue
-		    elseif a:jump == "break"
-			break
-		    elseif a:jump == "return" || a:jump == "finish"
-			return
-		    elseif a:jump == "error"
-			asdf
-		    elseif a:jump == "interrupt"
-			"INTERRUPT
-			let dummy = 0
-		    elseif a:jump == "throw"
-			throw "abc"
-		    endif
-		finally
-		    break	" discards jump that caused the :finally
-		    Xloop 1		" X: 0
-		endtry
-	    elseif loop == 2
-		Xloop 2			" X: 0
-	    endif
-	endwhile
-	Xloop 4				" X: 4*(1+8+64+512+4096+32768+262144)
-    endfunction
-
-    call B("continue")
-    Xpath 2097152				" X: 2097152
-    call B("break")
-    Xpath 4194304				" X: 4194304
-    call B("return")
-    Xpath 8388608				" X: 8388608
-    let g:jump = "finish"
-    ExecAsScript B
-    unlet g:jump
-    Xpath 16777216				" X: 16777216
-    try
-	call B("error")
-	Xpath 33554432				" X: 33554432
-    finally
-	Xpath 67108864				" X: 67108864
-	try
-	    call B("interrupt")
-	    Xpath 134217728			" X: 134217728
-	finally
-	    Xpath 268435456			" X: 268435456
-	    call B("throw")
-	    Xpath 536870912			" X: 536870912
-	endtry
-    endtry
-    Xpath 1073741824				" X: 1073741824
-
-    delfunction B
-
-endif
-
-Xcheck 2146584868
-
-
-"-------------------------------------------------------------------------------
-" Test 36:  :finally reason discarded by :return			    {{{1
-"
-"	    When a :finally clause is executed due to a :continue, :break,
-"	    :return, :finish, error, interrupt or :throw, the jump reason is
-"	    discarded by a :return in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
-    XloopINIT! 1 8
-
-    function! R(jump, retval) abort
-	XloopNEXT
-	let loop = 0
-	while loop < 2
-	    let loop = loop + 1
-	    if loop == 1
-		try
-		    if a:jump == "continue"
-			continue
-		    elseif a:jump == "break"
-			break
-		    elseif a:jump == "return"
-			return
-		    elseif a:jump == "error"
-			asdf
-		    elseif a:jump == "interrupt"
-			"INTERRUPT
-			let dummy = 0
-		    elseif a:jump == "throw"
-			throw "abc"
-		    endif
-		finally
-		    return a:retval	" discards jump that caused the :finally
-		    Xloop 1			" X: 0
-		endtry
-	    elseif loop == 2
-		Xloop 2				" X: 0
-	    endif
-	endwhile
-	Xloop 4					" X: 0
-    endfunction
-
-    let sum =  -R("continue", -8)
-    Xpath 2097152				" X: 2097152
-    let sum = sum - R("break", -16)
-    Xpath 4194304				" X: 4194304
-    let sum = sum - R("return", -32)
-    Xpath 8388608				" X: 8388608
-    try
-	let sum = sum - R("error", -64)
-	Xpath 16777216				" X: 16777216
-    finally
-	Xpath 33554432				" X: 33554432
-	try
-	    let sum = sum - R("interrupt", -128)
-	    Xpath 67108864			" X: 67108864
-	finally
-	    Xpath 134217728			" X: 134217728
-	    let sum = sum - R("throw", -256)
-	    Xpath 268435456			" X: 268435456
-	endtry
-    endtry
-    Xpath 536870912				" X: 536870912
-
-    let expected = 8 + 16 + 32 + 64 + 128 + 256
-    if sum != expected
-	Xpath 1073741824			" X: 0
-	Xout "sum =" . sum . ", expected: " . expected
-    endif
-
-    unlet sum expected
-    delfunction R
-
-endif
-
-Xcheck 1071644672
-
-
-"-------------------------------------------------------------------------------
-" Test 37:  :finally reason discarded by :finish			    {{{1
-"
-"	    When a :finally clause is executed due to a :continue, :break,
-"	    :return, :finish, error, interrupt or :throw, the jump reason is
-"	    discarded by a :finish in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
-    XloopINIT! 1 8
-
-    function! F(jump)	" not executed as function, transformed to a script
-	XloopNEXT
-	let loop = 0
-	while loop < 2
-	    let loop = loop + 1
-	    if loop == 1
-		try
-		    if a:jump == "continue"
-			continue
-		    elseif a:jump == "break"
-			break
-		    elseif a:jump == "finish"
-			finish
-		    elseif a:jump == "error"
-			asdf
-		    elseif a:jump == "interrupt"
-			"INTERRUPT
-			let dummy = 0
-		    elseif a:jump == "throw"
-			throw "abc"
-		    endif
-		finally
-		    finish	" discards jump that caused the :finally
-		    Xloop 1			" X: 0
-		endtry
-	    elseif loop == 2
-		Xloop 2				" X: 0
-	    endif
-	endwhile
-	Xloop 4					" X: 0
-    endfunction
-
-    let scriptF = MakeScript("F")
-    delfunction F
-
-    let g:jump = "continue"
-    exec "source" scriptF
-    Xpath 2097152				" X: 2097152
-    let g:jump = "break"
-    exec "source" scriptF
-    Xpath 4194304				" X: 4194304
-    let g:jump = "finish"
-    exec "source" scriptF
-    Xpath 8388608				" X: 8388608
-    try
-	let g:jump = "error"
-	exec "source" scriptF
-	Xpath 16777216				" X: 16777216
-    finally
-	Xpath 33554432				" X: 33554432
-	try
-	    let g:jump = "interrupt"
-	    exec "source" scriptF
-	    Xpath 67108864			" X: 67108864
-	finally
-	    Xpath 134217728			" X: 134217728
-	    try
-		let g:jump = "throw"
-		exec "source" scriptF
-		Xpath 268435456			" X: 268435456
-	    finally
-		Xpath 536870912			" X: 536870912
-	    endtry
-	endtry
-    endtry
-    unlet g:jump
-
-    call delete(scriptF)
-    unlet scriptF
-
-endif
-
-Xcheck 1071644672
-
-
-"-------------------------------------------------------------------------------
-" Test 38:  :finally reason discarded by an error			    {{{1
-"
-"	    When a :finally clause is executed due to a :continue, :break,
-"	    :return, :finish, error, interrupt or :throw, the jump reason is
-"	    discarded by an error in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
-    XloopINIT! 1 4
-
-    function! E(jump)
-	XloopNEXT
-	let loop = 0
-	while loop < 2
-	    let loop = loop + 1
-	    if loop == 1
-		try
-		    if a:jump == "continue"
-			continue
-		    elseif a:jump == "break"
-			break
-		    elseif a:jump == "return" || a:jump == "finish"
-			return
-		    elseif a:jump == "error"
-			asdf
-		    elseif a:jump == "interrupt"
-			"INTERRUPT
-			let dummy = 0
-		    elseif a:jump == "throw"
-			throw "abc"
-		    endif
-		finally
-		    asdf	" error; discards jump that caused the :finally
-		endtry
-	    elseif loop == 2
-		Xloop 1				" X: 0
-	    endif
-	endwhile
-	Xloop 2					" X: 0
-    endfunction
-
-    try
-	Xpath 16384				" X: 16384
-	call E("continue")
-	Xpath 32768				" X: 0
-    finally
-	try
-	    Xpath 65536				" X: 65536
-	    call E("break")
-	    Xpath 131072			" X: 0
-	finally
-	    try
-		Xpath 262144			" X: 262144
-		call E("return")
-		Xpath 524288			" X: 0
-	    finally
-		try
-		    Xpath 1048576		" X: 1048576
-		    let g:jump = "finish"
-		    ExecAsScript E
-		    Xpath 2097152		" X: 0
-		finally
-		    unlet g:jump
-		    try
-			Xpath 4194304		" X: 4194304
-			call E("error")
-			Xpath 8388608		" X: 0
-		    finally
-			try
-			    Xpath 16777216	" X: 16777216
-			    call E("interrupt")
-			    Xpath 33554432	" X: 0
-			finally
-			    try
-				Xpath 67108864	" X: 67108864
-				call E("throw")
-				Xpath 134217728	" X: 0
-			    finally
-				Xpath 268435456	" X: 268435456
-				delfunction E
-			    endtry
-			endtry
-		    endtry
-		endtry
-	    endtry
-	endtry
-    endtry
-    Xpath 536870912				" X: 0
-
-endif
-
-Xcheck 357908480
-
-
-"-------------------------------------------------------------------------------
-" Test 39:  :finally reason discarded by an interrupt			    {{{1
-"
-"	    When a :finally clause is executed due to a :continue, :break,
-"	    :return, :finish, error, interrupt or :throw, the jump reason is
-"	    discarded by an interrupt in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
-    XloopINIT! 1 4
-
-    function! I(jump)
-	XloopNEXT
-	let loop = 0
-	while loop < 2
-	    let loop = loop + 1
-	    if loop == 1
-		try
-		    if a:jump == "continue"
-			continue
-		    elseif a:jump == "break"
-			break
-		    elseif a:jump == "return" || a:jump == "finish"
-			return
-		    elseif a:jump == "error"
-			asdf
-		    elseif a:jump == "interrupt"
-			"INTERRUPT
-			let dummy = 0
-		    elseif a:jump == "throw"
-			throw "abc"
-		    endif
-		finally
-		    "INTERRUPT - discards jump that caused the :finally
-		    let dummy = 0
-		endtry
-	    elseif loop == 2
-		Xloop 1				" X: 0
-	    endif
-	endwhile
-	Xloop 2					" X: 0
-    endfunction
-
-    try
-	Xpath 16384				" X: 16384
-	call I("continue")
-	Xpath 32768				" X: 0
-    finally
-	try
-	    Xpath 65536				" X: 65536
-	    call I("break")
-	    Xpath 131072			" X: 0
-	finally
-	    try
-		Xpath 262144			" X: 262144
-		call I("return")
-		Xpath 524288			" X: 0
-	    finally
-		try
-		    Xpath 1048576		" X: 1048576
-		    let g:jump = "finish"
-		    ExecAsScript I
-		    Xpath 2097152		" X: 0
-		finally
-		    unlet g:jump
-		    try
-			Xpath 4194304		" X: 4194304
-			call I("error")
-			Xpath 8388608		" X: 0
-		    finally
-			try
-			    Xpath 16777216	" X: 16777216
-			    call I("interrupt")
-			    Xpath 33554432	" X: 0
-			finally
-			    try
-				Xpath 67108864	" X: 67108864
-				call I("throw")
-				Xpath 134217728	" X: 0
-			    finally
-				Xpath 268435456	" X: 268435456
-				delfunction I
-			    endtry
-			endtry
-		    endtry
-		endtry
-	    endtry
-	endtry
-    endtry
-    Xpath 536870912				" X: 0
-
-endif
-
-Xcheck 357908480
-
-
-"-------------------------------------------------------------------------------
-" Test 40:  :finally reason discarded by :throw				    {{{1
-"
-"	    When a :finally clause is executed due to a :continue, :break,
-"	    :return, :finish, error, interrupt or :throw, the jump reason is
-"	    discarded by a :throw in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
-    XloopINIT! 1 4
-
-    function! T(jump)
-	XloopNEXT
-	let loop = 0
-	while loop < 2
-	    let loop = loop + 1
-	    if loop == 1
-		try
-		    if a:jump == "continue"
-			continue
-		    elseif a:jump == "break"
-			break
-		    elseif a:jump == "return" || a:jump == "finish"
-			return
-		    elseif a:jump == "error"
-			asdf
-		    elseif a:jump == "interrupt"
-			"INTERRUPT
-			let dummy = 0
-		    elseif a:jump == "throw"
-			throw "abc"
-		    endif
-		finally
-		    throw "xyz"	" discards jump that caused the :finally
-		endtry
-	    elseif loop == 2
-		Xloop 1				" X: 0
-	    endif
-	endwhile
-	Xloop 2					" X: 0
-    endfunction
-
-    try
-	Xpath 16384				" X: 16384
-	call T("continue")
-	Xpath 32768				" X: 0
-    finally
-	try
-	    Xpath 65536				" X: 65536
-	    call T("break")
-	    Xpath 131072			" X: 0
-	finally
-	    try
-		Xpath 262144			" X: 262144
-		call T("return")
-		Xpath 524288			" X: 0
-	    finally
-		try
-		    Xpath 1048576		" X: 1048576
-		    let g:jump = "finish"
-		    ExecAsScript T
-		    Xpath 2097152		" X: 0
-		finally
-		    unlet g:jump
-		    try
-			Xpath 4194304		" X: 4194304
-			call T("error")
-			Xpath 8388608		" X: 0
-		    finally
-			try
-			    Xpath 16777216	" X: 16777216
-			    call T("interrupt")
-			    Xpath 33554432	" X: 0
-			finally
-			    try
-				Xpath 67108864	" X: 67108864
-				call T("throw")
-				Xpath 134217728	" X: 0
-			    finally
-				Xpath 268435456	" X: 268435456
-				delfunction T
-			    endtry
-			endtry
-		    endtry
-		endtry
-	    endtry
-	endtry
-    endtry
-    Xpath 536870912				" X: 0
-
-endif
-
-Xcheck 357908480
-
-" Tests 41 to 48 were moved to test_trycatch.vim
-let Xtest = 49
-
-
-"-------------------------------------------------------------------------------
-" Test 49:  Throwing exceptions across functions			    {{{1
-"
-"	    When an exception is thrown but not caught inside a function, the
-"	    caller is checked for a matching :catch clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! C()
-    try
-	Xpath 1					" X: 1
-	throw "arrgh"
-	Xpath 2					" X: 0
-    catch /arrgh/
-	Xpath 4					" X: 4
-    endtry
-    Xpath 8					" X: 8
-endfunction
-
-XloopINIT! 16 16
-
-function! T1()
-    XloopNEXT
-    try
-	Xloop 1					" X: 16 + 16*16
-	throw "arrgh"
-	Xloop 2					" X: 0
-    finally
-	Xloop 4					" X: 64 + 64*16
-    endtry
-    Xloop 8					" X: 0
-endfunction
-
-function! T2()
-    try
-	Xpath 4096				" X: 4096
-	call T1()
-	Xpath 8192				" X: 0
-    finally
-	Xpath 16384				" X: 16384
-    endtry
-    Xpath 32768					" X: 0
-endfunction
-
-try
-    Xpath 65536					" X: 65536
-    call C()	" throw and catch
-    Xpath 131072				" X: 131072
-catch /.*/
-    Xpath 262144				" X: 0
-    Xout v:exception "in" v:throwpoint
-endtry
-
-try
-    Xpath 524288				" X: 524288
-    call T1()  " throw, one level
-    Xpath 1048576				" X: 0
-catch /arrgh/
-    Xpath 2097152				" X: 2097152
-catch /.*/
-    Xpath 4194304				" X: 0
-    Xout v:exception "in" v:throwpoint
-endtry
-
-try
-    Xpath 8388608				" X: 8388608
-    call T2()	" throw, two levels
-    Xpath 16777216				" X: 0
-catch /arrgh/
-    Xpath 33554432				" X: 33554432
-catch /.*/
-    Xpath 67108864				" X: 0
-    Xout v:exception "in" v:throwpoint
-endtry
-Xpath 134217728					" X: 134217728
-
-Xcheck 179000669
-
-" Leave C, T1, and T2 for execution as scripts in the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 50:  Throwing exceptions across script files			    {{{1
-"
-"	    When an exception is thrown but not caught inside a script file,
-"	    the sourcing script or function is checked for a matching :catch
-"	    clause.
-"
-"	    This test executes the bodies of the functions C, T1, and T2 from
-"	    the previous test as script files (:return replaced by :finish).
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let scriptC = MakeScript("C")			" X: 1 + 4 + 8
-delfunction C
-
-XloopINIT! 16 16
-
-let scriptT1 = MakeScript("T1")			" X: 16 + 64 + 16*16 + 64*16
-delfunction T1
-
-let scriptT2 = MakeScript("T2", scriptT1)	" X: 4096 + 16384
-delfunction T2
-
-function! F()
-    try
-	Xpath 65536				" X: 65536
-	exec "source" g:scriptC
-	Xpath 131072				" X: 131072
-    catch /.*/
-	Xpath 262144				" X: 0
-	Xout v:exception "in" v:throwpoint
-    endtry
-
-    try
-	Xpath 524288				" X: 524288
-	exec "source" g:scriptT1
-	Xpath 1048576				" X: 0
-    catch /arrgh/
-	Xpath 2097152				" X: 2097152
-    catch /.*/
-	Xpath 4194304				" X: 0
-	Xout v:exception "in" v:throwpoint
-    endtry
-endfunction
-
-try
-    Xpath 8388608				" X: 8388608
-    call F()
-    Xpath 16777216				" X: 16777216
-    exec "source" scriptT2
-    Xpath 33554432				" X: 0
-catch /arrgh/
-    Xpath 67108864				" X: 67108864
-catch /.*/
-    Xpath 134217728				" X: 0
-    Xout v:exception "in" v:throwpoint
-endtry
-Xpath 268435456					" X: 268435456
-
-call delete(scriptC)
-call delete(scriptT1)
-call delete(scriptT2)
-unlet scriptC scriptT1 scriptT2
-delfunction F
-
-Xcheck 363550045
-
-" Test 51 was moved to test_trycatch.vim
+" Tests 1 to 50, 87 were moved to test_vimscript.vim
+" Tests 25, 26, 32, 33, 41-48, 51, 69-75 were moved to test_trycatch.vim
 let Xtest = 52
 
-
 "-------------------------------------------------------------------------------
 " Test 52:  Uncaught exceptions						    {{{1
 "
diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim
index a7ab78d87a..d49681974e 100644
--- a/src/nvim/testdir/test_vimscript.vim
+++ b/src/nvim/testdir/test_vimscript.vim
@@ -3,14 +3,12 @@
 
 source check.vim
 source shared.vim
+source script_util.vim
 
 "-------------------------------------------------------------------------------
 " Test environment							    {{{1
 "-------------------------------------------------------------------------------
 
-com!		   XpathINIT  let g:Xpath = ''
-com! -nargs=1 -bar Xpath      let g:Xpath = g:Xpath . 
-
 " Append a message to the "messages" file
 func Xout(text)
     split messages
@@ -20,67 +18,30 @@ endfunc
 
 com! -nargs=1	     Xout     call Xout()
 
-" MakeScript() - Make a script file from a function.			    {{{2
-"
-" Create a script that consists of the body of the function a:funcname.
-" Replace any ":return" by a ":finish", any argument variable by a global
-" variable, and every ":call" by a ":source" for the next following argument
-" in the variable argument list.  This function is useful if similar tests are
-" to be made for a ":return" from a function call or a ":finish" in a script
-" file.
-func MakeScript(funcname, ...)
-    let script = tempname()
-    execute "redir! >" . script
-    execute "function" a:funcname
-    redir END
-    execute "edit" script
-    " Delete the "function" and the "endfunction" lines.  Do not include the
-    " word "function" in the pattern since it might be translated if LANG is
-    " set.  When MakeScript() is being debugged, this deletes also the debugging
-    " output of its line 3 and 4.
-    exec '1,/.*' . a:funcname . '(.*)/d'
-    /^\d*\s*endfunction\>/,$d
-    %s/^\d*//e
-    %s/return/finish/e
-    %s/\ 0
-	let cnt = cnt + 1
-	s/\)
-
-
 "-------------------------------------------------------------------------------
 " Test 1:   :endwhile in function					    {{{1
 "
@@ -90,7 +51,7 @@ com! -nargs=1 -bar ExecAsScript call ExecAsScript()
 "	    tests will hang.
 "-------------------------------------------------------------------------------
 
-function! T1_F()
+func T1_F()
     Xpath 'a'
     let first = 1
     while 1
@@ -104,9 +65,9 @@ function! T1_F()
 	    return
 	endif
     endwhile
-endfunction
+endfunc
 
-function! T1_G()
+func T1_G()
     Xpath 'h'
     let first = 1
     while 1
@@ -121,7 +82,7 @@ function! T1_G()
 	endif
 	if 1	" unmatched :if
     endwhile
-endfunction
+endfunc
 
 func Test_endwhile_function()
   XpathINIT
@@ -175,7 +136,7 @@ endfunc
 " Test 3:   :if, :elseif, :while, :continue, :break			    {{{1
 "-------------------------------------------------------------------------------
 
-function Test_if_while()
+func Test_if_while()
     XpathINIT
     if 1
 	Xpath 'a'
@@ -235,7 +196,7 @@ endfunc
 " Test 4:   :return							    {{{1
 "-------------------------------------------------------------------------------
 
-function! T4_F()
+func T4_F()
     if 1
 	Xpath 'a'
 	let loops = 3
@@ -253,15 +214,15 @@ function! T4_F()
     else
 	Xpath 'g'
     endif
-endfunction
+endfunc
 
-function Test_return()
+func Test_return()
     XpathINIT
     call T4_F()
     Xpath '4'
 
     call assert_equal('ab3e3b2c24', g:Xpath)
-endfunction
+endfunc
 
 
 "-------------------------------------------------------------------------------
@@ -271,14 +232,14 @@ endfunction
 "	    test as a script file (:return replaced by :finish).
 "-------------------------------------------------------------------------------
 
-function Test_finish()
+func Test_finish()
     XpathINIT
     ExecAsScript T4_F
     Xpath '5'
     call DeleteTheScript()
 
     call assert_equal('ab3e3b2c25', g:Xpath)
-endfunction
+endfunc
 
 
 
@@ -412,7 +373,7 @@ delfunction G31
 delfunction G32
 delfunction G33
 
-function Test_defining_functions()
+func Test_defining_functions()
     call assert_equal('ade2ie3ibcg0h1g1h2g2h3fg0h1g1h2g2h3m', g:test6_result)
     call assert_equal('F1G1F2G21G22G23F3G31G32G33', g:test6_calls)
 endfunc
@@ -476,7 +437,7 @@ endfunc
 
 XpathINIT
 
-function! T8_F()
+func T8_F()
     if 1
 	Xpath 'a'
 	while 1
@@ -508,9 +469,9 @@ function! T8_F()
     return novar		" returns (default return value 0)
     Xpath 'q'
     return 1			" not reached
-endfunction
+endfunc
 
-function! T8_G() abort
+func T8_G() abort
     if 1
 	Xpath 'r'
 	while 1
@@ -524,9 +485,9 @@ function! T8_G() abort
     Xpath 'x'
 
     return -4			" not reached
-endfunction
+endfunc
 
-function! T8_H() abort
+func T8_H() abort
     while 1
 	Xpath 'A'
 	if 1
@@ -540,7 +501,7 @@ function! T8_H() abort
     Xpath 'F'
 
     return -4			" not reached
-endfunction
+endfunc
 
 " Aborted functions (T8_G and T8_H) return -1.
 let g:test8_sum = (T8_F() + 1) - 4 * T8_G() - 8 * T8_H()
@@ -567,7 +528,7 @@ endfunc
 
 XpathINIT
 
-function! F() abort
+func F() abort
     Xpath 'a'
     let result = G()	" not aborted
     Xpath 'b'
@@ -575,30 +536,30 @@ function! F() abort
 	Xpath 'c'
     endif
     return 1
-endfunction
+endfunc
 
-function! G()		" no abort attribute
+func G()		" no abort attribute
     Xpath 'd'
     if H() != -1	" aborted
 	Xpath 'e'
     endif
     Xpath 'f'
     return 2
-endfunction
+endfunc
 
-function! H() abort
+func H() abort
     Xpath 'g'
     call I()		" aborted
     Xpath 'h'
     return 4
-endfunction
+endfunc
 
-function! I() abort
+func I() abort
     Xpath 'i'
     asdf		" error
     Xpath 'j'
     return 8
-endfunction
+endfunc
 
 if F() != 1
     Xpath 'k'
@@ -626,7 +587,7 @@ endfunc
 
 XpathINIT
 
-function! MSG(enr, emsg)
+func MSG(enr, emsg)
     let english = v:lang == "C" || v:lang =~ '^[Ee]n'
     if a:enr == ""
 	Xout "TODO: Add message number for:" a:emsg
@@ -710,10 +671,10 @@ XpathINIT
 
 let calls = 0
 
-function! P(num)
+func P(num)
     let g:calls = g:calls + a:num   " side effect on call
     return 0
-endfunction
+endfunc
 
 if 1
     Xpath 'a'
@@ -1092,7 +1053,1716 @@ func Test_unmatched_if_in_while()
 endfunc
 
 "-------------------------------------------------------------------------------
+" Test 18:  Interrupt (Ctrl-C pressed)					    {{{1
+"
+"	    On an interrupt, the script processing is terminated immediately.
+"-------------------------------------------------------------------------------
+
+func Test_interrupt_while_if()
+  let test =<< trim [CODE]
+    try
+      if 1
+        Xpath 'a'
+        while 1
+          Xpath 'b'
+          if 1
+            Xpath 'c'
+            call interrupt()
+            call assert_report('should not get here')
+            break
+            finish
+          endif | call assert_report('should not get here')
+          call assert_report('should not get here')
+        endwhile | call assert_report('should not get here')
+        call assert_report('should not get here')
+      endif | call assert_report('should not get here')
+      call assert_report('should not get here')
+    catch /^Vim:Interrupt$/
+      Xpath 'd'
+    endtry | Xpath 'e'
+    Xpath 'f'
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abcdef', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_interrupt_try()
+  let test =<< trim [CODE]
+    try
+      try
+        Xpath 'a'
+        call interrupt()
+        call assert_report('should not get here')
+      endtry | call assert_report('should not get here')
+      call assert_report('should not get here')
+    catch /^Vim:Interrupt$/
+      Xpath 'b'
+    endtry | Xpath 'c'
+    Xpath 'd'
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abcd', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_interrupt_func_while_if()
+  let test =<< trim [CODE]
+    func F()
+      if 1
+        Xpath 'a'
+        while 1
+          Xpath 'b'
+          if 1
+            Xpath 'c'
+            call interrupt()
+            call assert_report('should not get here')
+            break
+            return
+          endif | call assert_report('should not get here')
+          call assert_report('should not get here')
+        endwhile | call assert_report('should not get here')
+        call assert_report('should not get here')
+      endif | call assert_report('should not get here')
+      call assert_report('should not get here')
+    endfunc
+
+    Xpath 'd'
+    try
+      call F() | call assert_report('should not get here')
+    catch /^Vim:Interrupt$/
+      Xpath 'e'
+    endtry | Xpath 'f'
+    Xpath 'g'
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('dabcefg', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_interrupt_func_try()
+  let test =<< trim [CODE]
+    func G()
+      try
+        Xpath 'a'
+        call interrupt()
+        call assert_report('should not get here')
+      endtry | call assert_report('should not get here')
+      call assert_report('should not get here')
+    endfunc
+
+    Xpath 'b'
+    try
+      call G() | call assert_report('should not get here')
+    catch /^Vim:Interrupt$/
+      Xpath 'c'
+    endtry | Xpath 'd'
+    Xpath 'e'
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('bacde', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 19:  Aborting on errors inside :try/:endtry			    {{{1
+"
+"	    An error in a command dynamically enclosed in a :try/:endtry region
+"	    aborts script processing immediately.  It does not matter whether
+"	    the failing command is outside or inside a function and whether a
+"	    function has an "abort" attribute.
+"-------------------------------------------------------------------------------
+
+func Test_try_error_abort_1()
+  let test =<< trim [CODE]
+    func F() abort
+      Xpath 'a'
+      asdf
+      call assert_report('should not get here')
+    endfunc
+
+    try
+      Xpath 'b'
+      call F()
+      call assert_report('should not get here')
+    endtry | call assert_report('should not get here')
+    call assert_report('should not get here')
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('ba', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_try_error_abort_2()
+  let test =<< trim [CODE]
+    func G()
+      Xpath 'a'
+      asdf
+      call assert_report('should not get here')
+    endfunc
+
+    try
+      Xpath 'b'
+      call G()
+      call assert_report('should not get here')
+    endtry | call assert_report('should not get here')
+    call assert_report('should not get here')
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('ba', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_try_error_abort_3()
+  let test =<< trim [CODE]
+    try
+      Xpath 'a'
+      asdf
+      call assert_report('should not get here')
+    endtry | call assert_report('should not get here')
+    call assert_report('should not get here')
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('a', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_try_error_abort_4()
+  let test =<< trim [CODE]
+    if 1
+      try
+        Xpath 'a'
+        asdf
+        call assert_report('should not get here')
+      endtry | call assert_report('should not get here')
+    endif | call assert_report('should not get here')
+    call assert_report('should not get here')
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('a', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_try_error_abort_5()
+  let test =<< trim [CODE]
+    let p = 1
+    while p
+      let p = 0
+      try
+        Xpath 'a'
+        asdf
+        call assert_report('should not get here')
+      endtry | call assert_report('should not get here')
+    endwhile | call assert_report('should not get here')
+    call assert_report('should not get here')
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('a', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_try_error_abort_6()
+  let test =<< trim [CODE]
+    let p = 1
+    Xpath 'a'
+    while p
+      Xpath 'b'
+      let p = 0
+      try
+        Xpath 'c'
+    endwhile | call assert_report('should not get here')
+    call assert_report('should not get here')
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abc', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 20:  Aborting on errors after :try/:endtry			    {{{1
+"
+"	    When an error occurs after the last active :try/:endtry region has
+"	    been left, termination behavior is as if no :try/:endtry has been
+"	    seen.
+"-------------------------------------------------------------------------------
+
+func Test_error_after_try_1()
+  let test =<< trim [CODE]
+    let p = 1
+    while p
+      let p = 0
+      Xpath 'a'
+      try
+        Xpath 'b'
+      endtry
+      asdf
+      call assert_report('should not get here')
+    endwhile | call assert_report('should not get here')
+    Xpath 'c'
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abc', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_error_after_try_2()
+  let test =<< trim [CODE]
+    while 1
+      try
+        Xpath 'a'
+        break
+        call assert_report('should not get here')
+      endtry
+    endwhile
+    Xpath 'b'
+    asdf
+    Xpath 'c'
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abc', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_error_after_try_3()
+  let test =<< trim [CODE]
+    while 1
+      try
+        Xpath 'a'
+        break
+        call assert_report('should not get here')
+      finally
+        Xpath 'b'
+      endtry
+    endwhile
+    Xpath 'c'
+    asdf
+    Xpath 'd'
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abcd', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_error_after_try_4()
+  let test =<< trim [CODE]
+    while 1
+      try
+        Xpath 'a'
+      finally
+        Xpath 'b'
+        break
+        call assert_report('should not get here')
+      endtry
+    endwhile
+    Xpath 'c'
+    asdf
+    Xpath 'd'
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abcd', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_error_after_try_5()
+  let test =<< trim [CODE]
+    let p = 1
+    while p
+      let p = 0
+      try
+        Xpath 'a'
+        continue
+        call assert_report('should not get here')
+      endtry
+    endwhile
+    Xpath 'b'
+    asdf
+    Xpath 'c'
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abc', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_error_after_try_6()
+  let test =<< trim [CODE]
+    let p = 1
+    while p
+      let p = 0
+      try
+        Xpath 'a'
+        continue
+        call assert_report('should not get here')
+      finally
+        Xpath 'b'
+      endtry
+    endwhile
+    Xpath 'c'
+    asdf
+    Xpath 'd'
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abcd', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_error_after_try_7()
+  let test =<< trim [CODE]
+    let p = 1
+    while p
+      let p = 0
+      try
+        Xpath 'a'
+      finally
+        Xpath 'b'
+        continue
+        call assert_report('should not get here')
+      endtry
+    endwhile
+    Xpath 'c'
+    asdf
+    Xpath 'd'
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abcd', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 21:  :finally for :try after :continue/:break/:return/:finish	    {{{1
+"
+"	    If a :try conditional stays inactive due to a preceding :continue,
+"	    :break, :return, or :finish, its :finally clause should not be
+"	    executed.
+"-------------------------------------------------------------------------------
+
+func Test_finally_after_loop_ctrl_statement()
+  let test =<< trim [CODE]
+    func F()
+      let loops = 2
+      while loops > 0
+        XloopNEXT
+        let loops = loops - 1
+        try
+          if loops == 1
+            Xloop 'a'
+            continue
+            call assert_report('should not get here')
+          elseif loops == 0
+            Xloop 'b'
+            break
+            call assert_report('should not get here')
+          endif
+
+          try		" inactive
+            call assert_report('should not get here')
+          finally
+            call assert_report('should not get here')
+          endtry
+        finally
+          Xloop 'c'
+        endtry
+        call assert_report('should not get here')
+      endwhile
+
+      try
+        Xpath 'd'
+        return
+        call assert_report('should not get here')
+        try		    " inactive
+          call assert_report('should not get here')
+        finally
+          call assert_report('should not get here')
+        endtry
+      finally
+        Xpath 'e'
+      endtry
+      call assert_report('should not get here')
+    endfunc
+
+    try
+      Xpath 'f'
+      call F()
+      Xpath 'g'
+      finish
+      call assert_report('should not get here')
+      try		" inactive
+        call assert_report('should not get here')
+      finally
+        call assert_report('should not get here')
+      endtry
+    finally
+      Xpath 'h'
+    endtry
+    call assert_report('should not get here')
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('fa2c2b3c3degh', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 22:  :finally for a :try after an error/interrupt/:throw		    {{{1
+"
+"	    If a :try conditional stays inactive due to a preceding error or
+"	    interrupt or :throw, its :finally clause should not be executed.
+"-------------------------------------------------------------------------------
+
+func Test_finally_after_error_in_func()
+  let test =<< trim [CODE]
+    func Error()
+      try
+        Xpath 'b'
+        asdf    " aborting error, triggering error exception
+        call assert_report('should not get here')
+      endtry
+      call assert_report('should not get here')
+    endfunc
+
+    Xpath 'a'
+    call Error()
+    call assert_report('should not get here')
+
+    if 1	" not active due to error
+      try	" not active since :if inactive
+        call assert_report('should not get here')
+      finally
+        call assert_report('should not get here')
+      endtry
+    endif
+
+    try		" not active due to error
+      call assert_report('should not get here')
+    finally
+      call assert_report('should not get here')
+    endtry
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('ab', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_finally_after_interrupt()
+  let test =<< trim [CODE]
+    func Interrupt()
+      try
+        Xpath 'a'
+        call interrupt()            " triggering interrupt exception
+        call assert_report('should not get here')
+      endtry
+    endfunc
+
+    Xpath 'b'
+    try
+      call Interrupt()
+    catch /^Vim:Interrupt$/
+      Xpath 'c'
+      finish
+    endtry
+    call assert_report('should not get here')
+
+    if 1	" not active due to interrupt
+      try	" not active since :if inactive
+        call assert_report('should not get here')
+      finally
+        call assert_report('should not get here')
+      endtry
+    endif
+
+    try		" not active due to interrupt
+      call assert_report('should not get here')
+    finally
+      call assert_report('should not get here')
+    endtry
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('bac', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_finally_after_throw()
+  let test =<< trim [CODE]
+    func Throw()
+      Xpath 'a'
+      throw 'xyz'
+    endfunc
+
+    Xpath 'b'
+    call Throw()
+    call assert_report('should not get here')
+
+    if 1	" not active due to :throw
+      try	" not active since :if inactive
+        call assert_report('should not get here')
+      finally
+        call assert_report('should not get here')
+      endtry
+    endif
+
+    try		" not active due to :throw
+      call assert_report('should not get here')
+    finally
+      call assert_report('should not get here')
+    endtry
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('ba', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 23:  :catch clauses for a :try after a :throw			    {{{1
+"
+"	    If a :try conditional stays inactive due to a preceding :throw,
+"	    none of its :catch clauses should be executed.
+"-------------------------------------------------------------------------------
+
+func Test_catch_after_throw()
+  let test =<< trim [CODE]
+    try
+      Xpath 'a'
+      throw "xyz"
+      call assert_report('should not get here')
+
+      if 1	" not active due to :throw
+        try	" not active since :if inactive
+          call assert_report('should not get here')
+        catch /xyz/
+          call assert_report('should not get here')
+        endtry
+      endif
+    catch /xyz/
+      Xpath 'b'
+    endtry
+
+    Xpath 'c'
+    throw "abc"
+    call assert_report('should not get here')
+
+    try		" not active due to :throw
+      call assert_report('should not get here')
+    catch /abc/
+      call assert_report('should not get here')
+    endtry
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abc', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 24:  :endtry for a :try after a :throw				    {{{1
+"
+"	    If a :try conditional stays inactive due to a preceding :throw,
+"	    its :endtry should not rethrow the exception to the next surrounding
+"	    active :try conditional.
 "-------------------------------------------------------------------------------
+
+func Test_endtry_after_throw()
+  let test =<< trim [CODE]
+    try			" try 1
+      try		" try 2
+        Xpath 'a'
+        throw "xyz"	" makes try 2 inactive
+        call assert_report('should not get here')
+
+        try		" try 3
+          call assert_report('should not get here')
+        endtry	" no rethrow to try 1
+      catch /xyz/	" should catch although try 2 inactive
+        Xpath 'b'
+      endtry
+    catch /xyz/		" try 1 active, but exception already caught
+      call assert_report('should not get here')
+    endtry
+    Xpath 'c'
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abc', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 27:  Executing :finally clauses after :return			    {{{1
+"
+"	    For a :return command dynamically enclosed in a :try/:endtry region,
+"	    :finally clauses are executed and the called function is ended.
+"-------------------------------------------------------------------------------
+
+func T27_F()
+  try
+    Xpath 'a'
+    try
+      Xpath 'b'
+      return
+      call assert_report('should not get here')
+    finally
+      Xpath 'c'
+    endtry
+    Xpath 'd'
+  finally
+    Xpath 'e'
+  endtry
+  call assert_report('should not get here')
+endfunc
+
+func T27_G()
+  try
+    Xpath 'f'
+    return
+    call assert_report('should not get here')
+  finally
+    Xpath 'g'
+    call T27_F()
+    Xpath 'h'
+  endtry
+  call assert_report('should not get here')
+endfunc
+
+func T27_H()
+  try
+    Xpath 'i'
+    call T27_G()
+    Xpath 'j'
+  finally
+    Xpath 'k'
+    return
+    call assert_report('should not get here')
+  endtry
+  call assert_report('should not get here')
+endfunction
+
+func Test_finally_after_return()
+  XpathINIT
+  try
+      Xpath 'l'
+      call T27_H()
+      Xpath 'm'
+  finally
+      Xpath 'n'
+  endtry
+  call assert_equal('lifgabcehjkmn', g:Xpath)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 28:  Executing :finally clauses after :finish			    {{{1
+"
+"	    For a :finish command dynamically enclosed in a :try/:endtry region,
+"	    :finally clauses are executed and the sourced file is finished.
+"
+"	    This test executes the bodies of the functions F, G, and H from the
+"	    previous test as script files (:return replaced by :finish).
+"-------------------------------------------------------------------------------
+
+func Test_finally_after_finish()
+  XpathINIT
+
+  let scriptF = MakeScript("T27_F")
+  let scriptG = MakeScript("T27_G", scriptF)
+  let scriptH = MakeScript("T27_H", scriptG)
+
+  try
+    Xpath 'A'
+    exec "source" scriptH
+    Xpath 'B'
+  finally
+    Xpath 'C'
+  endtry
+  Xpath 'D'
+  call assert_equal('AifgabcehjkBCD', g:Xpath)
+  call delete(scriptF)
+  call delete(scriptG)
+  call delete(scriptH)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 29:  Executing :finally clauses on errors			    {{{1
+"
+"	    After an error in a command dynamically enclosed in a :try/:endtry
+"	    region, :finally clauses are executed and the script processing is
+"	    terminated.
+"-------------------------------------------------------------------------------
+
+func Test_finally_after_error_1()
+  let test =<< trim [CODE]
+    func F()
+      while 1
+        try
+          Xpath 'a'
+          while 1
+            try
+              Xpath 'b'
+              asdf	    " error
+              call assert_report('should not get here')
+            finally
+              Xpath 'c'
+            endtry | call assert_report('should not get here')
+            call assert_report('should not get here')
+            break
+          endwhile
+          call assert_report('should not get here')
+        finally
+          Xpath 'd'
+        endtry | call assert_report('should not get here')
+        call assert_report('should not get here')
+        break
+      endwhile
+      call assert_report('should not get here')
+    endfunc
+
+    while 1
+      try
+        Xpath 'e'
+        while 1
+          call F()
+          call assert_report('should not get here')
+          break
+        endwhile  | call assert_report('should not get here')
+        call assert_report('should not get here')
+      finally
+        Xpath 'f'
+      endtry | call assert_report('should not get here')
+    endwhile | call assert_report('should not get here')
+    call assert_report('should not get here')
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('eabcdf', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_finally_after_error_2()
+  let test =<< trim [CODE]
+    func G() abort
+      if 1
+        try
+          Xpath 'a'
+          asdf	    " error
+          call assert_report('should not get here')
+        finally
+          Xpath 'b'
+        endtry | Xpath 'c'
+      endif | Xpath 'd'
+      call assert_report('should not get here')
+    endfunc
+
+    if 1
+      try
+        Xpath 'e'
+        call G()
+        call assert_report('should not get here')
+      finally
+        Xpath 'f'
+      endtry | call assert_report('should not get here')
+    endif | call assert_report('should not get here')
+    call assert_report('should not get here')
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('eabf', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 30:  Executing :finally clauses on interrupt			    {{{1
+"
+"	    After an interrupt in a command dynamically enclosed in
+"	    a :try/:endtry region, :finally clauses are executed and the
+"	    script processing is terminated.
+"-------------------------------------------------------------------------------
+
+func Test_finally_on_interrupt()
+  let test =<< trim [CODE]
+    func F()
+      try
+        Xloop 'a'
+        call interrupt()
+        call assert_report('should not get here')
+      finally
+        Xloop 'b'
+      endtry
+      call assert_report('should not get here')
+    endfunc
+
+    try
+      try
+        Xpath 'c'
+        try
+          Xpath 'd'
+          call interrupt()
+          call assert_report('should not get here')
+        finally
+          Xpath 'e'
+          try
+            Xpath 'f'
+            try
+              Xpath 'g'
+            finally
+              Xpath 'h'
+              try
+                Xpath 'i'
+                call interrupt()
+                call assert_report('should not get here')
+              endtry
+              call assert_report('should not get here')
+            endtry
+            call assert_report('should not get here')
+          endtry
+          call assert_report('should not get here')
+        endtry
+        call assert_report('should not get here')
+      finally
+        Xpath 'j'
+        try
+          Xpath 'k'
+          call F()
+          call assert_report('should not get here')
+        finally
+          Xpath 'l'
+          try
+            Xpath 'm'
+            XloopNEXT
+            ExecAsScript F
+            call assert_report('should not get here')
+          finally
+            Xpath 'n'
+          endtry
+          call assert_report('should not get here')
+        endtry
+        call assert_report('should not get here')
+      endtry
+      call assert_report('should not get here')
+    catch /^Vim:Interrupt$/
+      Xpath 'o'
+    endtry
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('cdefghijka1b1lma2b2no', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 31:  Executing :finally clauses after :throw			    {{{1
+"
+"	    After a :throw dynamically enclosed in a :try/:endtry region,
+"	    :finally clauses are executed and the script processing is
+"	    terminated.
+"-------------------------------------------------------------------------------
+
+func Test_finally_after_throw_2()
+  let test =<< trim [CODE]
+    func F()
+      try
+        Xloop 'a'
+        throw "exception"
+        call assert_report('should not get here')
+      finally
+        Xloop 'b'
+      endtry
+      call assert_report('should not get here')
+    endfunc
+
+    try
+      Xpath 'c'
+      try
+        Xpath 'd'
+        throw "exception"
+        call assert_report('should not get here')
+      finally
+        Xpath 'e'
+        try
+          Xpath 'f'
+          try
+            Xpath 'g'
+          finally
+            Xpath 'h'
+            try
+              Xpath 'i'
+              throw "exception"
+              call assert_report('should not get here')
+            endtry
+            call assert_report('should not get here')
+          endtry
+          call assert_report('should not get here')
+        endtry
+        call assert_report('should not get here')
+      endtry
+      call assert_report('should not get here')
+    finally
+      Xpath 'j'
+      try
+        Xpath 'k'
+        call F()
+        call assert_report('should not get here')
+      finally
+        Xpath 'l'
+        try
+          Xpath 'm'
+          XloopNEXT
+          ExecAsScript F
+          call assert_report('should not get here')
+        finally
+          Xpath 'n'
+        endtry
+        call assert_report('should not get here')
+      endtry
+      call assert_report('should not get here')
+    endtry
+    call assert_report('should not get here')
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('cdefghijka1b1lma2b2n', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 34:  :finally reason discarded by :continue			    {{{1
+"
+"	    When a :finally clause is executed due to a :continue, :break,
+"	    :return, :finish, error, interrupt or :throw, the jump reason is
+"	    discarded by a :continue in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_after_continue()
+  let test =<< trim [CODE]
+    func C(jump)
+      XloopNEXT
+      let loop = 0
+      while loop < 2
+        let loop = loop + 1
+        if loop == 1
+          try
+            if a:jump == "continue"
+              continue
+            elseif a:jump == "break"
+              break
+            elseif a:jump == "return" || a:jump == "finish"
+              return
+            elseif a:jump == "error"
+              asdf
+            elseif a:jump == "interrupt"
+              call interrupt()
+              let dummy = 0
+            elseif a:jump == "throw"
+              throw "abc"
+            endif
+          finally
+            continue	" discards jump that caused the :finally
+            call assert_report('should not get here')
+          endtry
+          call assert_report('should not get here')
+        elseif loop == 2
+          Xloop 'a'
+        endif
+      endwhile
+    endfunc
+
+    call C("continue")
+    Xpath 'b'
+    call C("break")
+    Xpath 'c'
+    call C("return")
+    Xpath 'd'
+    let g:jump = "finish"
+    ExecAsScript C
+    unlet g:jump
+    Xpath 'e'
+    try
+      call C("error")
+      Xpath 'f'
+    finally
+      Xpath 'g'
+      try
+        call C("interrupt")
+        Xpath 'h'
+      finally
+        Xpath 'i'
+        call C("throw")
+        Xpath 'j'
+      endtry
+    endtry
+    Xpath 'k'
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('a2ba3ca4da5ea6fga7hia8jk', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 35:  :finally reason discarded by :break				    {{{1
+"
+"	    When a :finally clause is executed due to a :continue, :break,
+"	    :return, :finish, error, interrupt or :throw, the jump reason is
+"	    discarded by a :break in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_discard_by_break()
+  let test =<< trim [CODE]
+    func B(jump)
+      XloopNEXT
+      let loop = 0
+      while loop < 2
+        let loop = loop + 1
+        if loop == 1
+          try
+            if a:jump == "continue"
+              continue
+            elseif a:jump == "break"
+              break
+            elseif a:jump == "return" || a:jump == "finish"
+              return
+            elseif a:jump == "error"
+              asdf
+            elseif a:jump == "interrupt"
+              call interrupt()
+              let dummy = 0
+            elseif a:jump == "throw"
+              throw "abc"
+            endif
+          finally
+            break	" discards jump that caused the :finally
+            call assert_report('should not get here')
+          endtry
+        elseif loop == 2
+          call assert_report('should not get here')
+        endif
+      endwhile
+      Xloop 'a'
+    endfunc
+
+    call B("continue")
+    Xpath 'b'
+    call B("break")
+    Xpath 'c'
+    call B("return")
+    Xpath 'd'
+    let g:jump = "finish"
+    ExecAsScript B
+    unlet g:jump
+    Xpath 'e'
+    try
+      call B("error")
+      Xpath 'f'
+    finally
+      Xpath 'g'
+      try
+        call B("interrupt")
+        Xpath 'h'
+      finally
+        Xpath 'i'
+        call B("throw")
+        Xpath 'j'
+      endtry
+    endtry
+    Xpath 'k'
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('a2ba3ca4da5ea6fga7hia8jk', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 36:  :finally reason discarded by :return			    {{{1
+"
+"	    When a :finally clause is executed due to a :continue, :break,
+"	    :return, :finish, error, interrupt or :throw, the jump reason is
+"	    discarded by a :return in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_discard_by_return()
+  let test =<< trim [CODE]
+    func R(jump, retval) abort
+      let loop = 0
+      while loop < 2
+        let loop = loop + 1
+        if loop == 1
+          try
+            if a:jump == "continue"
+              continue
+            elseif a:jump == "break"
+              break
+            elseif a:jump == "return"
+              return
+            elseif a:jump == "error"
+              asdf
+            elseif a:jump == "interrupt"
+              call interrupt()
+              let dummy = 0
+            elseif a:jump == "throw"
+              throw "abc"
+            endif
+          finally
+            return a:retval	" discards jump that caused the :finally
+            call assert_report('should not get here')
+          endtry
+        elseif loop == 2
+          call assert_report('should not get here')
+        endif
+      endwhile
+      call assert_report('should not get here')
+    endfunc
+
+    let sum =  -R("continue", -8)
+    Xpath 'a'
+    let sum = sum - R("break", -16)
+    Xpath 'b'
+    let sum = sum - R("return", -32)
+    Xpath 'c'
+    try
+      let sum = sum - R("error", -64)
+      Xpath 'd'
+    finally
+      Xpath 'e'
+      try
+        let sum = sum - R("interrupt", -128)
+        Xpath 'f'
+      finally
+        Xpath 'g'
+        let sum = sum - R("throw", -256)
+        Xpath 'h'
+      endtry
+    endtry
+    Xpath 'i'
+
+    let expected = 8 + 16 + 32 + 64 + 128 + 256
+    call assert_equal(sum, expected)
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abcdefghi', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 37:  :finally reason discarded by :finish			    {{{1
+"
+"	    When a :finally clause is executed due to a :continue, :break,
+"	    :return, :finish, error, interrupt or :throw, the jump reason is
+"	    discarded by a :finish in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_discard_by_finish()
+  let test =<< trim [CODE]
+    func F(jump)	" not executed as function, transformed to a script
+      let loop = 0
+      while loop < 2
+        let loop = loop + 1
+        if loop == 1
+          try
+            if a:jump == "continue"
+              continue
+            elseif a:jump == "break"
+              break
+            elseif a:jump == "finish"
+              finish
+            elseif a:jump == "error"
+              asdf
+            elseif a:jump == "interrupt"
+              call interrupt()
+              let dummy = 0
+            elseif a:jump == "throw"
+              throw "abc"
+            endif
+          finally
+            finish	" discards jump that caused the :finally
+            call assert_report('should not get here')
+          endtry
+        elseif loop == 2
+          call assert_report('should not get here')
+        endif
+      endwhile
+      call assert_report('should not get here')
+    endfunc
+
+    let scriptF = MakeScript("F")
+    delfunction F
+
+    let g:jump = "continue"
+    exec "source" scriptF
+    Xpath 'a'
+    let g:jump = "break"
+    exec "source" scriptF
+    Xpath 'b'
+    let g:jump = "finish"
+    exec "source" scriptF
+    Xpath 'c'
+    try
+      let g:jump = "error"
+      exec "source" scriptF
+      Xpath 'd'
+    finally
+      Xpath 'e'
+      try
+        let g:jump = "interrupt"
+        exec "source" scriptF
+        Xpath 'f'
+      finally
+        Xpath 'g'
+        try
+          let g:jump = "throw"
+          exec "source" scriptF
+          Xpath 'h'
+        finally
+          Xpath 'i'
+        endtry
+      endtry
+    endtry
+    unlet g:jump
+    call delete(scriptF)
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abcdefghi', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 38:  :finally reason discarded by an error			    {{{1
+"
+"	    When a :finally clause is executed due to a :continue, :break,
+"	    :return, :finish, error, interrupt or :throw, the jump reason is
+"	    discarded by an error in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_discard_by_error()
+  let test =<< trim [CODE]
+    func E(jump)
+      let loop = 0
+      while loop < 2
+        let loop = loop + 1
+        if loop == 1
+          try
+            if a:jump == "continue"
+              continue
+            elseif a:jump == "break"
+              break
+            elseif a:jump == "return" || a:jump == "finish"
+              return
+            elseif a:jump == "error"
+              asdf
+            elseif a:jump == "interrupt"
+              call interrupt()
+              let dummy = 0
+            elseif a:jump == "throw"
+              throw "abc"
+            endif
+          finally
+            asdf	" error; discards jump that caused the :finally
+          endtry
+        elseif loop == 2
+          call assert_report('should not get here')
+        endif
+      endwhile
+      call assert_report('should not get here')
+    endfunc
+
+    try
+      Xpath 'a'
+      call E("continue")
+      call assert_report('should not get here')
+    finally
+      try
+        Xpath 'b'
+        call E("break")
+        call assert_report('should not get here')
+      finally
+        try
+          Xpath 'c'
+          call E("return")
+          call assert_report('should not get here')
+        finally
+          try
+            Xpath 'd'
+            let g:jump = "finish"
+            ExecAsScript E
+            call assert_report('should not get here')
+          finally
+            unlet g:jump
+            try
+              Xpath 'e'
+              call E("error")
+              call assert_report('should not get here')
+            finally
+              try
+                Xpath 'f'
+                call E("interrupt")
+                call assert_report('should not get here')
+              finally
+                try
+                  Xpath 'g'
+                  call E("throw")
+                  call assert_report('should not get here')
+                finally
+                  Xpath 'h'
+                  delfunction E
+                endtry
+              endtry
+            endtry
+          endtry
+        endtry
+      endtry
+    endtry
+    call assert_report('should not get here')
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abcdefgh', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 39:  :finally reason discarded by an interrupt			    {{{1
+"
+"	    When a :finally clause is executed due to a :continue, :break,
+"	    :return, :finish, error, interrupt or :throw, the jump reason is
+"	    discarded by an interrupt in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_discarded_by_interrupt()
+  let test =<< trim [CODE]
+    func I(jump)
+      let loop = 0
+      while loop < 2
+        let loop = loop + 1
+        if loop == 1
+          try
+            if a:jump == "continue"
+              continue
+            elseif a:jump == "break"
+              break
+            elseif a:jump == "return" || a:jump == "finish"
+              return
+            elseif a:jump == "error"
+              asdf
+            elseif a:jump == "interrupt"
+              call interrupt()
+              let dummy = 0
+            elseif a:jump == "throw"
+              throw "abc"
+            endif
+          finally
+            call interrupt()
+            let dummy = 0
+          endtry
+        elseif loop == 2
+          call assert_report('should not get here')
+        endif
+      endwhile
+      call assert_report('should not get here')
+    endfunc
+
+    try
+      try
+        Xpath 'a'
+        call I("continue")
+        call assert_report('should not get here')
+      finally
+        try
+          Xpath 'b'
+          call I("break")
+          call assert_report('should not get here')
+        finally
+          try
+            Xpath 'c'
+            call I("return")
+            call assert_report('should not get here')
+          finally
+            try
+              Xpath 'd'
+              let g:jump = "finish"
+              ExecAsScript I
+              call assert_report('should not get here')
+            finally
+              unlet g:jump
+              try
+                Xpath 'e'
+                call I("error")
+                call assert_report('should not get here')
+              finally
+                try
+                  Xpath 'f'
+                  call I("interrupt")
+                  call assert_report('should not get here')
+                finally
+                  try
+                    Xpath 'g'
+                    call I("throw")
+                    call assert_report('should not get here')
+                  finally
+                    Xpath 'h'
+                    delfunction I
+                  endtry
+                endtry
+              endtry
+            endtry
+          endtry
+        endtry
+      endtry
+      call assert_report('should not get here')
+    catch /^Vim:Interrupt$/
+      Xpath 'A'
+    endtry
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abcdefghA', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 40:  :finally reason discarded by :throw				    {{{1
+"
+"	    When a :finally clause is executed due to a :continue, :break,
+"	    :return, :finish, error, interrupt or :throw, the jump reason is
+"	    discarded by a :throw in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_discard_by_throw()
+  let test =<< trim [CODE]
+    func T(jump)
+      let loop = 0
+      while loop < 2
+        let loop = loop + 1
+        if loop == 1
+          try
+            if a:jump == "continue"
+              continue
+            elseif a:jump == "break"
+              break
+            elseif a:jump == "return" || a:jump == "finish"
+              return
+            elseif a:jump == "error"
+              asdf
+            elseif a:jump == "interrupt"
+              call interrupt()
+              let dummy = 0
+            elseif a:jump == "throw"
+              throw "abc"
+            endif
+          finally
+            throw "xyz"	" discards jump that caused the :finally
+          endtry
+        elseif loop == 2
+          call assert_report('should not get here')
+        endif
+      endwhile
+      call assert_report('should not get here')
+    endfunc
+
+    try
+      Xpath 'a'
+      call T("continue")
+      call assert_report('should not get here')
+    finally
+      try
+        Xpath 'b'
+        call T("break")
+        call assert_report('should not get here')
+      finally
+        try
+          Xpath 'c'
+          call T("return")
+          call assert_report('should not get here')
+        finally
+          try
+            Xpath 'd'
+            let g:jump = "finish"
+            ExecAsScript T
+            call assert_report('should not get here')
+          finally
+            unlet g:jump
+            try
+              Xpath 'e'
+              call T("error")
+              call assert_report('should not get here')
+            finally
+              try
+                Xpath 'f'
+                call T("interrupt")
+                call assert_report('should not get here')
+              finally
+                try
+                  Xpath 'g'
+                  call T("throw")
+                  call assert_report('should not get here')
+                finally
+                  Xpath 'h'
+                  delfunction T
+                endtry
+              endtry
+            endtry
+          endtry
+        endtry
+      endtry
+    endtry
+    call assert_report('should not get here')
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abcdefgh', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 49:  Throwing exceptions across functions			    {{{1
+"
+"	    When an exception is thrown but not caught inside a function, the
+"	    caller is checked for a matching :catch clause.
+"-------------------------------------------------------------------------------
+
+func T49_C()
+  try
+    Xpath 'a'
+    throw "arrgh"
+    call assert_report('should not get here')
+  catch /arrgh/
+    Xpath 'b'
+  endtry
+  Xpath 'c'
+endfunc
+
+func T49_T1()
+  XloopNEXT
+  try
+    Xloop 'd'
+    throw "arrgh"
+    call assert_report('should not get here')
+  finally
+    Xloop 'e'
+  endtry
+  Xloop 'f'
+endfunc
+
+func T49_T2()
+  try
+    Xpath 'g'
+    call T49_T1()
+    call assert_report('should not get here')
+  finally
+    Xpath 'h'
+  endtry
+  call assert_report('should not get here')
+endfunc
+
+func Test_throw_exception_across_funcs()
+  XpathINIT
+  XloopINIT
+  try
+    Xpath 'i'
+    call T49_C()            " throw and catch
+    Xpath 'j'
+  catch /.*/
+    call assert_report('should not get here')
+  endtry
+
+  try
+    Xpath 'k'
+    call T49_T1()  " throw, one level
+    call assert_report('should not get here')
+  catch /arrgh/
+    Xpath 'l'
+  catch /.*/
+    call assert_report('should not get here')
+  endtry
+
+  try
+    Xpath 'm'
+    call T49_T2()	" throw, two levels
+    call assert_report('should not get here')
+  catch /arrgh/
+    Xpath 'n'
+  catch /.*/
+    call assert_report('should not get here')
+  endtry
+  Xpath 'o'
+
+  call assert_equal('iabcjkd2e2lmgd3e3hno', g:Xpath)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 50:  Throwing exceptions across script files			    {{{1
+"
+"	    When an exception is thrown but not caught inside a script file,
+"	    the sourcing script or function is checked for a matching :catch
+"	    clause.
+"
+"	    This test executes the bodies of the functions C, T1, and T2 from
+"	    the previous test as script files (:return replaced by :finish).
+"-------------------------------------------------------------------------------
+
+func T50_F()
+  try
+    Xpath 'A'
+    exec "source" g:scriptC
+    Xpath 'B'
+  catch /.*/
+    call assert_report('should not get here')
+  endtry
+
+  try
+    Xpath 'C'
+    exec "source" g:scriptT1
+    call assert_report('should not get here')
+  catch /arrgh/
+    Xpath 'D'
+  catch /.*/
+    call assert_report('should not get here')
+  endtry
+endfunc
+
+func Test_throw_across_script()
+  XpathINIT
+  XloopINIT
+  let g:scriptC = MakeScript("T49_C")
+  let g:scriptT1 = MakeScript("T49_T1")
+  let scriptT2 = MakeScript("T49_T2", g:scriptT1)
+
+  try
+    Xpath 'E'
+    call T50_F()
+    Xpath 'F'
+    exec "source" scriptT2
+    call assert_report('should not get here')
+  catch /arrgh/
+    Xpath 'G'
+  catch /.*/
+    call assert_report('should not get here')
+  endtry
+  Xpath 'H'
+  call assert_equal('EAabcBCd2e2DFgd3e3hGH', g:Xpath)
+
+  call delete(g:scriptC)
+  call delete(g:scriptT1)
+  call delete(scriptT2)
+  unlet g:scriptC g:scriptT1 scriptT2
+endfunc
+
 "-------------------------------------------------------------------------------
 " Test 87   using (expr) ? funcref : funcref				    {{{1
 "
-- 
cgit 


From 8d6a217b9a1c3db6ed4ee8fabee20ca75a515857 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 20:25:07 +0800
Subject: vim-patch:8.2.1383: test 49 is old style

Problem:    Test 49 is old style.
Solution:   Convert test cases to new style. (Yegappan Lakshmanan,
            closes vim/vim#6638)

https://github.com/vim/vim/commit/9470a4d88acf948af1596101527b3a505f8c14e9

Cherry-pick AssertException() from patch 8.2.1146.
---
 src/nvim/testdir/shared.vim         |   15 +
 src/nvim/testdir/test49.ok          |    9 -
 src/nvim/testdir/test49.vim         | 1254 ++---------------------------------
 src/nvim/testdir/test_vimscript.vim | 1147 ++++++++++++++++++++++++++++++++
 4 files changed, 1201 insertions(+), 1224 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/shared.vim b/src/nvim/testdir/shared.vim
index c2809844ac..ef7cc4ac5f 100644
--- a/src/nvim/testdir/shared.vim
+++ b/src/nvim/testdir/shared.vim
@@ -364,4 +364,19 @@ func GetMessages()
   return msg_list
 endfunc
 
+" Run the list of commands in 'cmds' and look for 'errstr' in exception.
+" Note that assert_fails() cannot be used in some places and this function
+" can be used.
+func AssertException(cmds, errstr)
+  let save_exception = ''
+  try
+    for cmd in a:cmds
+      exe cmd
+    endfor
+  catch
+    let save_exception = v:exception
+  endtry
+  call assert_match(a:errstr, save_exception)
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test49.ok b/src/nvim/testdir/test49.ok
index 50696fd643..8ca8a564c3 100644
--- a/src/nvim/testdir/test49.ok
+++ b/src/nvim/testdir/test49.ok
@@ -1,18 +1,9 @@
 Results of test49.vim:
-*** Test  52: OK (1247112011)
-*** Test  53: OK (131071)
-*** Test  54: OK (2047)
-*** Test  55: OK (1023)
-*** Test  56: OK (511)
-*** Test  57: OK (2147450880)
-*** Test  58: OK (624945)
 *** Test  59: OK (2038431743)
 *** Test  60: OK (311511339)
-*** Test  61: OK (374889517)
 *** Test  62: OK (286331153)
 *** Test  63: OK (236978127)
 *** Test  64: OK (1499645335)
-*** Test  65: OK (70187)
 *** Test  66: OK (5464)
 *** Test  67: OK (212514423)
 *** Test  68: OK (212514423)
diff --git a/src/nvim/testdir/test49.vim b/src/nvim/testdir/test49.vim
index d51ba24153..6133f410ac 100644
--- a/src/nvim/testdir/test49.vim
+++ b/src/nvim/testdir/test49.vim
@@ -456,7 +456,7 @@ function ExtraVim(...)
     " messing up the user's viminfo file.
     let redirect = a:0 ?
 	\ " -c 'au VimLeave * redir END' -c 'redir\\! >" . a:1 . "'" : ""
-    exec "!echo '" . debug_quits . "q' | " .. v:progpath .. " -u NONE -N -es" . redirect .
+    exec "!echo '" . debug_quits . "q' | " .. v:progpath .. " -u NONE -N -Xes" . redirect .
 	\ " -c 'debuggreedy|set viminfo+=nviminfo'" .
 	\ " -c 'let ExtraVimBegin = " . extra_begin . "'" .
 	\ " -c 'let ExtraVimResult = \"" . resultfile . "\"'" . breakpoints .
@@ -607,1002 +607,52 @@ com! -nargs=1 -bar ExecAsScript call ExecAsScript()
 
 " END_OF_TEST_ENVIRONMENT - do not change or remove this line.
 
-" Tests 1 to 50, 87 were moved to test_vimscript.vim
-" Tests 25, 26, 32, 33, 41-48, 51, 69-75 were moved to test_trycatch.vim
-let Xtest = 52
-
-"-------------------------------------------------------------------------------
-" Test 52:  Uncaught exceptions						    {{{1
-"
-"	    When an exception is thrown but not caught, an error message is
-"	    displayed when the script is terminated.  In case of an interrupt
-"	    or error exception, the normal interrupt or error message(s) are
-"	    displayed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let msgfile = tempname()
-
 function! MESSAGES(...)
     try
 	exec "edit" g:msgfile
     catch /^Vim(edit):/
 	return 0
-    endtry
-
-    let english = v:lang == "C" || v:lang =~ '^[Ee]n'
-    let match = 1
-    norm gg
-
-    let num = a:0 / 2
-    let cnt = 1
-    while cnt <= num
-	let enr = a:{2*cnt - 1}
-	let emsg= a:{2*cnt}
-	let cnt = cnt + 1
-
-	if enr == ""
-	    Xout "TODO: Add message number for:" emsg
-	elseif enr == "INT"
-	    let enr = ""
-	endif
-	if enr == "" && !english
-	    continue
-	endif
-	let pattern = (enr != "") ? enr . ':.*' : ''
-	if english
-	    let pattern = pattern . emsg
-	endif
-	if !search(pattern, "W")
-	    let match = 0
-	    Xout "No match for:" pattern
-	endif
-	norm $
-    endwhile
-
-    bwipeout!
-    return match
-endfunction
-
-if ExtraVim(msgfile)
-    Xpath 1					" X: 1
-    throw "arrgh"
-endif
-
-Xpath 2						" X: 2
-if !MESSAGES('E605', "Exception not caught")
-    Xpath 4					" X: 0
-endif
-
-if ExtraVim(msgfile)
-    try
-	Xpath 8					" X: 8
-	throw "oops"
-    catch /arrgh/
-	Xpath 16				" X: 0
-    endtry
-    Xpath 32					" X: 0
-endif
-
-Xpath 64					" X: 64
-if !MESSAGES('E605', "Exception not caught")
-    Xpath 128					" X: 0
-endif
-
-if ExtraVim(msgfile)
-    function! T()
-	throw "brrr"
-    endfunction
-
-    try
-	Xpath 256				" X: 256
-	throw "arrgh"
-    catch /.*/
-	Xpath 512				" X: 512
-	call T()
-    endtry
-    Xpath 1024					" X: 0
-endif
-
-Xpath 2048					" X: 2048
-if !MESSAGES('E605', "Exception not caught")
-    Xpath 4096					" X: 0
-endif
-
-if ExtraVim(msgfile)
-    try
-	Xpath 8192				" X: 8192
-	throw "arrgh"
-    finally
-	Xpath 16384				" X: 16384
-	throw "brrr"
-    endtry
-    Xpath 32768					" X: 0
-endif
-
-Xpath 65536					" X: 65536
-if !MESSAGES('E605', "Exception not caught")
-    Xpath 131072				" X: 0
-endif
-
-if ExtraVim(msgfile)
-    try
-	Xpath 262144				" X: 262144
-	"INTERRUPT
-    endtry
-    Xpath 524288				" X: 0
-endif
-
-Xpath 1048576					" X: 1048576
-if !MESSAGES('INT', "Interrupted")
-    Xpath 2097152				" X: 0
-endif
-
-if ExtraVim(msgfile)
-    try
-	Xpath 4194304				" X: 4194304
-	let x = novar	" error E121/E15; exception: E121
-    catch /E15:/	" should not catch
-	Xpath 8388608				" X: 0
-    endtry
-    Xpath 16777216				" X: 0
-endif
-
-Xpath 33554432					" X: 33554432
-if !MESSAGES('E121', "Undefined variable", 'E15', "Invalid expression")
-    Xpath 67108864				" X: 0
-endif
-
-if ExtraVim(msgfile)
-    try
-	Xpath 134217728				" X: 134217728
-"	unlet novar #	" error E108/E488; exception: E488
-    catch /E108:/	" should not catch
-	Xpath 268435456				" X: 0
-    endtry
-    Xpath 536870912				" X: 0
-endif
-
-Xpath 1073741824				" X: 1073741824
-if !MESSAGES('E108', "No such variable", 'E488', "Trailing characters")
-    " The Xpath command does not accept 2^31 (negative); add explicitly:
-    let Xpath = Xpath + 2147483648		" X: 0
-endif
-
-call delete(msgfile)
-unlet msgfile
-
-Xcheck 1247112011
-
-" Leave MESSAGES() for the next tests.
-
-
-"-------------------------------------------------------------------------------
-" Test 53:  Nesting errors: :endif/:else/:elseif			    {{{1
-"
-"	    For nesting errors of :if conditionals the correct error messages
-"	    should be given.
-"
-"	    This test reuses the function MESSAGES() from the previous test.
-"	    This functions checks the messages in g:msgfile.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let msgfile = tempname()
-
-if ExtraVim(msgfile)
-"   endif
-endif
-if MESSAGES('E580', ":endif without :if")
-    Xpath 1					" X: 1
-endif
-
-if ExtraVim(msgfile)
-"   while 1
-"       endif
-"   endwhile
-endif
-if MESSAGES('E580', ":endif without :if")
-    Xpath 2					" X: 2
-endif
-
-if ExtraVim(msgfile)
-"   try
-"   finally
-"       endif
-"   endtry
-endif
-if MESSAGES('E580', ":endif without :if")
-    Xpath 4					" X: 4
-endif
-
-if ExtraVim(msgfile)
-"   try
-"       endif
-"   endtry
-endif
-if MESSAGES('E580', ":endif without :if")
-    Xpath 8					" X: 8
-endif
-
-if ExtraVim(msgfile)
-"   try
-"       throw "a"
-"   catch /a/
-"       endif
-"   endtry
-endif
-if MESSAGES('E580', ":endif without :if")
-    Xpath 16					" X: 16
-endif
-
-if ExtraVim(msgfile)
-"   else
-endif
-if MESSAGES('E581', ":else without :if")
-    Xpath 32					" X: 32
-endif
-
-if ExtraVim(msgfile)
-"   while 1
-"       else
-"   endwhile
-endif
-if MESSAGES('E581', ":else without :if")
-    Xpath 64					" X: 64
-endif
-
-if ExtraVim(msgfile)
-"   try
-"   finally
-"       else
-"   endtry
-endif
-if MESSAGES('E581', ":else without :if")
-    Xpath 128					" X: 128
-endif
-
-if ExtraVim(msgfile)
-"   try
-"       else
-"   endtry
-endif
-if MESSAGES('E581', ":else without :if")
-    Xpath 256					" X: 256
-endif
-
-if ExtraVim(msgfile)
-"   try
-"       throw "a"
-"   catch /a/
-"       else
-"   endtry
-endif
-if MESSAGES('E581', ":else without :if")
-    Xpath 512					" X: 512
-endif
-
-if ExtraVim(msgfile)
-"   elseif
-endif
-if MESSAGES('E582', ":elseif without :if")
-    Xpath 1024					" X: 1024
-endif
-
-if ExtraVim(msgfile)
-"   while 1
-"       elseif
-"   endwhile
-endif
-if MESSAGES('E582', ":elseif without :if")
-    Xpath 2048					" X: 2048
-endif
-
-if ExtraVim(msgfile)
-"   try
-"   finally
-"       elseif
-"   endtry
-endif
-if MESSAGES('E582', ":elseif without :if")
-    Xpath 4096					" X: 4096
-endif
-
-if ExtraVim(msgfile)
-"   try
-"       elseif
-"   endtry
-endif
-if MESSAGES('E582', ":elseif without :if")
-    Xpath 8192					" X: 8192
-endif
-
-if ExtraVim(msgfile)
-"   try
-"       throw "a"
-"   catch /a/
-"       elseif
-"   endtry
-endif
-if MESSAGES('E582', ":elseif without :if")
-    Xpath 16384					" X: 16384
-endif
-
-if ExtraVim(msgfile)
-"   if 1
-"   else
-"   else
-"   endif
-endif
-if MESSAGES('E583', "multiple :else")
-    Xpath 32768					" X: 32768
-endif
-
-if ExtraVim(msgfile)
-"   if 1
-"   else
-"   elseif 1
-"   endif
-endif
-if MESSAGES('E584', ":elseif after :else")
-    Xpath 65536					" X: 65536
-endif
-
-call delete(msgfile)
-unlet msgfile
-
-Xcheck 131071
-
-" Leave MESSAGES() for the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 54:  Nesting errors: :while/:endwhile				    {{{1
-"
-"	    For nesting errors of :while conditionals the correct error messages
-"	    should be given.
-"
-"	    This test reuses the function MESSAGES() from the previous test.
-"	    This functions checks the messages in g:msgfile.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let msgfile = tempname()
-
-if ExtraVim(msgfile)
-"   endwhile
-endif
-if MESSAGES('E588', ":endwhile without :while")
-    Xpath 1					" X: 1
-endif
-
-if ExtraVim(msgfile)
-"   if 1
-"       endwhile
-"   endif
-endif
-if MESSAGES('E588', ":endwhile without :while")
-    Xpath 2					" X: 2
-endif
-
-if ExtraVim(msgfile)
-"   while 1
-"       if 1
-"   endwhile
-endif
-if MESSAGES('E171', "Missing :endif")
-    Xpath 4					" X: 4
-endif
-
-if ExtraVim(msgfile)
-"   try
-"   finally
-"       endwhile
-"   endtry
-endif
-if MESSAGES('E588', ":endwhile without :while")
-    Xpath 8					" X: 8
-endif
-
-if ExtraVim(msgfile)
-"   while 1
-"       try
-"       finally
-"   endwhile
-endif
-if MESSAGES('E600', "Missing :endtry")
-    Xpath 16					" X: 16
-endif
-
-if ExtraVim(msgfile)
-"   while 1
-"       if 1
-"	    try
-"	    finally
-"   endwhile
-endif
-if MESSAGES('E600', "Missing :endtry")
-    Xpath 32					" X: 32
-endif
-
-if ExtraVim(msgfile)
-"   while 1
-"       try
-"       finally
-"	    if 1
-"   endwhile
-endif
-if MESSAGES('E171', "Missing :endif")
-    Xpath 64					" X: 64
-endif
-
-if ExtraVim(msgfile)
-"   try
-"       endwhile
-"   endtry
-endif
-if MESSAGES('E588', ":endwhile without :while")
-    Xpath 128					" X: 128
-endif
-
-if ExtraVim(msgfile)
-"   while 1
-"       try
-"	    endwhile
-"       endtry
-"   endwhile
-endif
-if MESSAGES('E588', ":endwhile without :while")
-    Xpath 256					" X: 256
-endif
-
-if ExtraVim(msgfile)
-"   try
-"       throw "a"
-"   catch /a/
-"       endwhile
-"   endtry
-endif
-if MESSAGES('E588', ":endwhile without :while")
-    Xpath 512					" X: 512
-endif
-
-if ExtraVim(msgfile)
-"   while 1
-"       try
-"	    throw "a"
-"	catch /a/
-"	    endwhile
-"       endtry
-"   endwhile
-endif
-if MESSAGES('E588', ":endwhile without :while")
-    Xpath 1024					" X: 1024
-endif
-
-
-call delete(msgfile)
-unlet msgfile
-
-Xcheck 2047
-
-" Leave MESSAGES() for the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 55:  Nesting errors: :continue/:break				    {{{1
-"
-"	    For nesting errors of :continue and :break commands the correct
-"	    error messages should be given.
-"
-"	    This test reuses the function MESSAGES() from the previous test.
-"	    This functions checks the messages in g:msgfile.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let msgfile = tempname()
-
-if ExtraVim(msgfile)
-"   continue
-endif
-if MESSAGES('E586', ":continue without :while")
-    Xpath 1					" X: 1
-endif
-
-if ExtraVim(msgfile)
-"   if 1
-"       continue
-"   endif
-endif
-if MESSAGES('E586', ":continue without :while")
-    Xpath 2					" X: 2
-endif
-
-if ExtraVim(msgfile)
-"   try
-"   finally
-"       continue
-"   endtry
-endif
-if MESSAGES('E586', ":continue without :while")
-    Xpath 4					" X: 4
-endif
-
-if ExtraVim(msgfile)
-"   try
-"       continue
-"   endtry
-endif
-if MESSAGES('E586', ":continue without :while")
-    Xpath 8					" X: 8
-endif
-
-if ExtraVim(msgfile)
-"   try
-"       throw "a"
-"   catch /a/
-"       continue
-"   endtry
-endif
-if MESSAGES('E586', ":continue without :while")
-    Xpath 16					" X: 16
-endif
-
-if ExtraVim(msgfile)
-"   break
-endif
-if MESSAGES('E587', ":break without :while")
-    Xpath 32					" X: 32
-endif
-
-if ExtraVim(msgfile)
-"   if 1
-"       break
-"   endif
-endif
-if MESSAGES('E587', ":break without :while")
-    Xpath 64					" X: 64
-endif
-
-if ExtraVim(msgfile)
-"   try
-"   finally
-"       break
-"   endtry
-endif
-if MESSAGES('E587', ":break without :while")
-    Xpath 128					" X: 128
-endif
-
-if ExtraVim(msgfile)
-"   try
-"       break
-"   endtry
-endif
-if MESSAGES('E587', ":break without :while")
-    Xpath 256					" X: 256
-endif
-
-if ExtraVim(msgfile)
-"   try
-"       throw "a"
-"   catch /a/
-"       break
-"   endtry
-endif
-if MESSAGES('E587', ":break without :while")
-    Xpath 512					" X: 512
-endif
-
-call delete(msgfile)
-unlet msgfile
-
-Xcheck 1023
-
-" Leave MESSAGES() for the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 56:  Nesting errors: :endtry					    {{{1
-"
-"	    For nesting errors of :try conditionals the correct error messages
-"	    should be given.
-"
-"	    This test reuses the function MESSAGES() from the previous test.
-"	    This functions checks the messages in g:msgfile.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let msgfile = tempname()
-
-if ExtraVim(msgfile)
-"   endtry
-endif
-if MESSAGES('E602', ":endtry without :try")
-    Xpath 1					" X: 1
-endif
-
-if ExtraVim(msgfile)
-"   if 1
-"       endtry
-"   endif
-endif
-if MESSAGES('E602', ":endtry without :try")
-    Xpath 2					" X: 2
-endif
-
-if ExtraVim(msgfile)
-"   while 1
-"       endtry
-"   endwhile
-endif
-if MESSAGES('E602', ":endtry without :try")
-    Xpath 4					" X: 4
-endif
-
-if ExtraVim(msgfile)
-"   try
-"       if 1
-"   endtry
-endif
-if MESSAGES('E171', "Missing :endif")
-    Xpath 8					" X: 8
-endif
-
-if ExtraVim(msgfile)
-"   try
-"       while 1
-"   endtry
-endif
-if MESSAGES('E170', "Missing :endwhile")
-    Xpath 16					" X: 16
-endif
-
-if ExtraVim(msgfile)
-"   try
-"   finally
-"       if 1
-"   endtry
-endif
-if MESSAGES('E171', "Missing :endif")
-    Xpath 32					" X: 32
-endif
-
-if ExtraVim(msgfile)
-"   try
-"   finally
-"       while 1
-"   endtry
-endif
-if MESSAGES('E170', "Missing :endwhile")
-    Xpath 64					" X: 64
-endif
-
-if ExtraVim(msgfile)
-    try
-	Xpath 4194304				" X: 4194304
-	let x = novar	" error E121; exception: E121
-    catch /E15:/	" should not catch
-	Xpath 8388608				" X: 0
-    endtry
-    Xpath 16777216				" X: 0
-endif
-
-Xpath 33554432					" X: 33554432
-if !MESSAGES('E121', "Undefined variable")
-    Xpath 67108864				" X: 0
-endif
-
-if ExtraVim(msgfile)
-"   try
-"       throw "a"
-"   catch /a/
-"       while 1
-"   endtry
-endif
-if MESSAGES('E170', "Missing :endwhile")
-    Xpath 256					" X: 256
-endif
-
-call delete(msgfile)
-unlet msgfile
-
-delfunction MESSAGES
-
-Xcheck 511
-
-
-"-------------------------------------------------------------------------------
-" Test 57:  v:exception and v:throwpoint for user exceptions		    {{{1
-"
-"	    v:exception evaluates to the value of the exception that was caught
-"	    most recently and is not finished.  (A caught exception is finished
-"	    when the next ":catch", ":finally", or ":endtry" is reached.)
-"	    v:throwpoint evaluates to the script/function name and line number
-"	    where that exception has been thrown.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! FuncException()
-    let g:exception = v:exception
-endfunction
-
-function! FuncThrowpoint()
-    let g:throwpoint = v:throwpoint
-endfunction
-
-let scriptException  = MakeScript("FuncException")
-let scriptThrowPoint = MakeScript("FuncThrowpoint")
-
-command! CmdException  let g:exception  = v:exception
-command! CmdThrowpoint let g:throwpoint = v:throwpoint
-
-XloopINIT! 1 2
-
-function! CHECK(n, exception, throwname, throwline)
-    XloopNEXT
-    let error = 0
-    if v:exception != a:exception
-	Xout a:n.": v:exception is" v:exception "instead of" a:exception
-	let error = 1
-    endif
-    if v:throwpoint !~ a:throwname
-	let name = escape(a:throwname, '\')
-	Xout a:n.": v:throwpoint (".v:throwpoint.") does not match" name
-	let error = 1
-    endif
-    if v:throwpoint !~ a:throwline
-	let line = escape(a:throwline, '\')
-	Xout a:n.": v:throwpoint (".v:throwpoint.") does not match" line
-	let error = 1
-    endif
-    if error
-	Xloop 1					" X: 0
-    endif
-endfunction
-
-function! T(arg, line)
-    if a:line == 2
-	throw a:arg		" in line 2
-    elseif a:line == 4
-	throw a:arg		" in line 4
-    elseif a:line == 6
-	throw a:arg		" in line 6
-    elseif a:line == 8
-	throw a:arg		" in line 8
-    endif
-endfunction
-
-function! G(arg, line)
-    call T(a:arg, a:line)
-endfunction
-
-function! F(arg, line)
-    call G(a:arg, a:line)
-endfunction
-
-let scriptT = MakeScript("T")
-let scriptG = MakeScript("G", scriptT)
-let scriptF = MakeScript("F", scriptG)
-
-try
-    Xpath 32768					" X: 32768
-    call F("oops", 2)
-catch /.*/
-    Xpath 65536					" X: 65536
-    let exception  = v:exception
-    let throwpoint = v:throwpoint
-    call CHECK(1, "oops", '\', '\<2\>')
-    exec "let exception  = v:exception"
-    exec "let throwpoint = v:throwpoint"
-    call CHECK(2, "oops", '\', '\<2\>')
-    CmdException
-    CmdThrowpoint
-    call CHECK(3, "oops", '\', '\<2\>')
-    call FuncException()
-    call FuncThrowpoint()
-    call CHECK(4, "oops", '\', '\<2\>')
-    exec "source" scriptException
-    exec "source" scriptThrowPoint
-    call CHECK(5, "oops", '\', '\<2\>')
-    try
-	Xpath 131072				" X: 131072
-	call G("arrgh", 4)
-    catch /.*/
-	Xpath 262144				" X: 262144
-	let exception  = v:exception
-	let throwpoint = v:throwpoint
-	call CHECK(6, "arrgh", '\', '\<4\>')
-	try
-	    Xpath 524288			" X: 524288
-	    let g:arg = "autsch"
-	    let g:line = 6
-	    exec "source" scriptF
-	catch /.*/
-	    Xpath 1048576			" X: 1048576
-	    let exception  = v:exception
-	    let throwpoint = v:throwpoint
-	    " Symbolic links in tempname()s are not resolved, whereas resolving
-	    " is done for v:throwpoint.  Resolve the temporary file name for
-	    " scriptT, so that it can be matched against v:throwpoint.
-	    call CHECK(7, "autsch", resolve(scriptT), '\<6\>')
-	finally
-	    Xpath 2097152			" X: 2097152
-	    let exception  = v:exception
-	    let throwpoint = v:throwpoint
-	    call CHECK(8, "arrgh", '\', '\<4\>')
-	    try
-		Xpath 4194304			" X: 4194304
-		let g:arg = "brrrr"
-		let g:line = 8
-		exec "source" scriptG
-	    catch /.*/
-		Xpath 8388608			" X: 8388608
-		let exception  = v:exception
-		let throwpoint = v:throwpoint
-		" Resolve scriptT for matching it against v:throwpoint.
-		call CHECK(9, "brrrr", resolve(scriptT), '\<8\>')
-	    finally
-		Xpath 16777216			" X: 16777216
-		let exception  = v:exception
-		let throwpoint = v:throwpoint
-		call CHECK(10, "arrgh", '\', '\<4\>')
-	    endtry
-	    Xpath 33554432			" X: 33554432
-	    let exception  = v:exception
-	    let throwpoint = v:throwpoint
-	    call CHECK(11, "arrgh", '\', '\<4\>')
-	endtry
-	Xpath 67108864				" X: 67108864
-	let exception  = v:exception
-	let throwpoint = v:throwpoint
-	call CHECK(12, "arrgh", '\', '\<4\>')
-    finally
-	Xpath 134217728				" X: 134217728
-	let exception  = v:exception
-	let throwpoint = v:throwpoint
-	call CHECK(13, "oops", '\', '\<2\>')
-    endtry
-    Xpath 268435456				" X: 268435456
-    let exception  = v:exception
-    let throwpoint = v:throwpoint
-    call CHECK(14, "oops", '\', '\<2\>')
-finally
-    Xpath 536870912				" X: 536870912
-    let exception  = v:exception
-    let throwpoint = v:throwpoint
-    call CHECK(15, "", '^$', '^$')
-endtry
-
-Xpath 1073741824				" X: 1073741824
-
-unlet exception throwpoint
-delfunction FuncException
-delfunction FuncThrowpoint
-call delete(scriptException)
-call delete(scriptThrowPoint)
-unlet scriptException scriptThrowPoint
-delcommand CmdException
-delcommand CmdThrowpoint
-delfunction T
-delfunction G
-delfunction F
-call delete(scriptT)
-call delete(scriptG)
-call delete(scriptF)
-unlet scriptT scriptG scriptF
-
-Xcheck 2147450880
-
-
-"-------------------------------------------------------------------------------
-"
-" Test 58:  v:exception and v:throwpoint for error/interrupt exceptions	    {{{1
-"
-"	    v:exception and v:throwpoint work also for error and interrupt
-"	    exceptions.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
-    function! T(line)
-	if a:line == 2
-	    delfunction T		" error (function in use) in line 2
-	elseif a:line == 4
-	    let dummy = 0		" INTERRUPT1 - interrupt in line 4
-	endif
-    endfunction
+    endtry
 
-    while 1
-	try
-	    Xpath 1				" X: 1
-	    let caught = 0
-	    call T(2)
-	catch /.*/
-	    let caught = 1
-	    if v:exception !~ 'Vim(delfunction):'
-		Xpath 2				" X: 0
-	    endif
-	    if v:throwpoint !~ '\'
-		Xpath 4				" X: 0
-	    endif
-	    if v:throwpoint !~ '\<2\>'
-		Xpath 8				" X: 0
-	    endif
-	finally
-	    Xpath 16				" X: 16
-	    if caught || $VIMNOERRTHROW
-		Xpath 32			" X: 32
-	    endif
-	    if v:exception != ""
-		Xpath 64			" X: 0
-	    endif
-	    if v:throwpoint != ""
-		Xpath 128			" X: 0
-	    endif
-	    break		" discard error for $VIMNOERRTHROW
-	endtry
-    endwhile
+    let english = v:lang == "C" || v:lang =~ '^[Ee]n'
+    let match = 1
+    norm gg
 
-    Xpath 256					" X: 256
-    if v:exception != ""
-	Xpath 512				" X: 0
-    endif
-    if v:throwpoint != ""
-	Xpath 1024				" X: 0
-    endif
+    let num = a:0 / 2
+    let cnt = 1
+    while cnt <= num
+	let enr = a:{2*cnt - 1}
+	let emsg= a:{2*cnt}
+	let cnt = cnt + 1
 
-    while 1
-	try
-	    Xpath 2048				" X: 2048
-	    let caught = 0
-	    call T(4)
-	catch /.*/
-	    let caught = 1
-	    if v:exception != 'Vim:Interrupt'
-		Xpath 4096			" X: 0
-	    endif
-	    if v:throwpoint !~ '\'
-		Xpath 8192			" X: 0
-	    endif
-	    if v:throwpoint !~ '\<4\>'
-		Xpath 16384			" X: 0
-	    endif
-	finally
-	    Xpath 32768				" X: 32768
-	    if caught || $VIMNOINTTHROW
-		Xpath 65536			" X: 65536
-	    endif
-	    if v:exception != ""
-		Xpath 131072			" X: 0
-	    endif
-	    if v:throwpoint != ""
-		Xpath 262144			" X: 0
-	    endif
-	    break		" discard error for $VIMNOERRTHROW
-	endtry
+	if enr == ""
+	    Xout "TODO: Add message number for:" emsg
+	elseif enr == "INT"
+	    let enr = ""
+	endif
+	if enr == "" && !english
+	    continue
+	endif
+	let pattern = (enr != "") ? enr . ':.*' : ''
+	if english
+	    let pattern = pattern . emsg
+	endif
+	if !search(pattern, "W")
+	    let match = 0
+	    Xout "No match for:" pattern
+	endif
+	norm $
     endwhile
 
-    Xpath 524288				" X: 524288
-    if v:exception != ""
-	Xpath 1048576				" X: 0
-    endif
-    if v:throwpoint != ""
-	Xpath 2097152				" X: 0
-    endif
-
-endif
+    bwipeout!
+    return match
+endfunction
 
-Xcheck 624945
+" Leave MESSAGES() for the next tests.
 
+" Tests 1 to 50, 52 to 57, 87 were moved to test_vimscript.vim
+" Tests 25, 26, 32, 33, 41-48, 51, 69-75 were moved to test_trycatch.vim
+let Xtest = 59
 
 "-------------------------------------------------------------------------------
 "
@@ -2071,142 +1121,8 @@ endif
 
 Xcheck 311511339
 
-
-"-------------------------------------------------------------------------------
-" Test 61:  Catching interrupt exceptions				    {{{1
-"
-"	    When an interrupt occurs inside a :try/:endtry region, an
-"	    interrupt exception is thrown and can be caught.  Its value is
-"	    "Vim:Interrupt".  If the interrupt occurs after an error or a :throw
-"	    but before a matching :catch is reached, all following :catches of
-"	    that try block are ignored, but the interrupt exception can be
-"	    caught by the next surrounding try conditional.  An interrupt is
-"	    ignored when there is a previous interrupt that has not been caught
-"	    or causes a :finally clause to be executed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
-    while 1
-	try
-	    try
-		Xpath 1				" X: 1
-		let caught = 0
-		"INTERRUPT
-		Xpath 2				" X: 0
-	    catch /^Vim:Interrupt$/
-		let caught = 1
-	    finally
-		Xpath 4				" X: 4
-		if caught || $VIMNOINTTHROW
-		    Xpath 8			" X: 8
-		endif
-	    endtry
-	catch /.*/
-	    Xpath 16				" X: 0
-	    Xout v:exception "in" v:throwpoint
-	finally
-	    break		" discard interrupt for $VIMNOINTTHROW
-	endtry
-    endwhile
-
-    while 1
-	try
-	    try
-		let caught = 0
-		try
-		    Xpath 32			" X: 32
-		    asdf
-		    Xpath 64			" X: 0
-		catch /do_not_catch/
-		    Xpath 128			" X: 0
-		catch /.*/	"INTERRUPT - throw interrupt if !$VIMNOERRTHROW
-		    Xpath 256			" X: 0
-		catch /.*/
-		    Xpath 512			" X: 0
-		finally		"INTERRUPT - throw interrupt if $VIMNOERRTHROW
-		    Xpath 1024			" X: 1024
-		endtry
-	    catch /^Vim:Interrupt$/
-		let caught = 1
-	    finally
-		Xpath 2048			" X: 2048
-		if caught || $VIMNOINTTHROW
-		    Xpath 4096			" X: 4096
-		endif
-	    endtry
-	catch /.*/
-	    Xpath 8192				" X: 0
-	    Xout v:exception "in" v:throwpoint
-	finally
-	    break		" discard interrupt for $VIMNOINTTHROW
-	endtry
-    endwhile
-
-    while 1
-	try
-	    try
-		let caught = 0
-		try
-		    Xpath 16384			" X: 16384
-		    throw "x"
-		    Xpath 32768			" X: 0
-		catch /do_not_catch/
-		    Xpath 65536			" X: 0
-		catch /x/	"INTERRUPT
-		    Xpath 131072		" X: 0
-		catch /.*/
-		    Xpath 262144		" X: 0
-		endtry
-	    catch /^Vim:Interrupt$/
-		let caught = 1
-	    finally
-		Xpath 524288			" X: 524288
-		if caught || $VIMNOINTTHROW
-		    Xpath 1048576		" X: 1048576
-		endif
-	    endtry
-	catch /.*/
-	    Xpath 2097152			" X: 0
-	    Xout v:exception "in" v:throwpoint
-	finally
-	    break		" discard interrupt for $VIMNOINTTHROW
-	endtry
-    endwhile
-
-    while 1
-	try
-	    let caught = 0
-	    try
-		Xpath 4194304			" X: 4194304
-		"INTERRUPT
-		Xpath 8388608			" X: 0
-	    catch /do_not_catch/ "INTERRUPT
-		Xpath 16777216			" X: 0
-	    catch /^Vim:Interrupt$/
-		let caught = 1
-	    finally
-		Xpath 33554432			" X: 33554432
-		if caught || $VIMNOINTTHROW
-		    Xpath 67108864		" X: 67108864
-		endif
-	    endtry
-	catch /.*/
-	    Xpath 134217728			" X: 0
-	    Xout v:exception "in" v:throwpoint
-	finally
-	    break		" discard interrupt for $VIMNOINTTHROW
-	endtry
-    endwhile
-
-    Xpath 268435456				" X: 268435456
-
-endif
-
-Xcheck 374889517
-
+" Test 61 was moved to test_vimscript.vim
+let Xtest = 62
 
 "-------------------------------------------------------------------------------
 " Test 62:  Catching error exceptions					    {{{1
@@ -2729,100 +1645,8 @@ endif
 
 Xcheck 1499645335
 
-
-"-------------------------------------------------------------------------------
-" Test 65:  Errors in the /pattern/ argument of a :catch		    {{{1
-"
-"	    On an error in the /pattern/ argument of a :catch, the :catch does
-"	    not match.  Any following :catches of the same :try/:endtry don't
-"	    match either.  Finally clauses are executed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! MSG(enr, emsg)
-    let english = v:lang == "C" || v:lang =~ '^[Ee]n'
-    if a:enr == ""
-	Xout "TODO: Add message number for:" a:emsg
-	let v:errmsg = ":" . v:errmsg
-    endif
-    let match = 1
-    if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg)
-	let match = 0
-	if v:errmsg == ""
-	    Xout "Message missing."
-	else
-	    let v:errmsg = escape(v:errmsg, '"')
-	    Xout "Unexpected message:" v:errmsg
-	endif
-    endif
-    return match
-endfunction
-
-try
-    try
-	Xpath 1					" X: 1
-	throw "oops"
-    catch /^oops$/
-	Xpath 2					" X: 2
-    catch /\)/		" not checked; exception has already been caught
-	Xpath 4					" X: 0
-    endtry
-    Xpath 8					" X: 8
-catch /.*/
-    Xpath 16					" X: 0
-    Xout v:exception "in" v:throwpoint
-endtry
-
-function! F()
-    try
-	let caught = 0
-	try
-	    try
-		Xpath 32			" X: 32
-		throw "ab"
-	    catch /abc/	" does not catch
-		Xpath 64			" X: 0
-	    catch /\)/	" error; discards exception
-		Xpath 128			" X: 0
-	    catch /.*/	" not checked
-		Xpath 256			" X: 0
-	    finally
-		Xpath 512			" X: 512
-	    endtry
-	    Xpath 1024				" X: 0
-	catch /^ab$/	" checked, but original exception is discarded
-	    Xpath 2048				" X: 0
-	catch /^Vim(catch):/
-	    let caught = 1
-	    let v:errmsg = substitute(v:exception, '^Vim(catch):', '', "")
-	finally
-	    Xpath 4096				" X: 4096
-	    if !caught && !$VIMNOERRTHROW
-		Xpath 8192			" X: 0
-	    endif
-	    if !MSG('E475', "Invalid argument")
-		Xpath 16384			" X: 0
-	    endif
-	    if !caught
-		return	| " discard error
-	    endif
-	endtry
-    catch /.*/
-	Xpath 32768				" X: 0
-	Xout v:exception "in" v:throwpoint
-    endtry
-endfunction
-
-call F()
-Xpath 65536					" X: 65536
-
-delfunction MSG
-delfunction F
-unlet! caught
-
-Xcheck 70187
-
+" Test 65 was moved to test_vimscript.vim
+let Xtest = 66
 
 "-------------------------------------------------------------------------------
 " Test 66:  Stop range :call on error, interrupt, or :throw		    {{{1
diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim
index d49681974e..5e112e05f9 100644
--- a/src/nvim/testdir/test_vimscript.vim
+++ b/src/nvim/testdir/test_vimscript.vim
@@ -2763,6 +2763,1153 @@ func Test_throw_across_script()
   unlet g:scriptC g:scriptT1 scriptT2
 endfunc
 
+"-------------------------------------------------------------------------------
+" Test 52:  Uncaught exceptions						    {{{1
+"
+"	    When an exception is thrown but not caught, an error message is
+"	    displayed when the script is terminated.  In case of an interrupt
+"	    or error exception, the normal interrupt or error message(s) are
+"	    displayed.
+"-------------------------------------------------------------------------------
+
+func Test_uncaught_exception_1()
+  CheckEnglish
+
+  let test =<< trim [CODE]
+    Xpath 'a'
+    throw "arrgh"
+    call assert_report('should not get here')`
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('E605: Exception not caught: arrgh', v:errmsg)
+    call assert_equal('a', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_uncaught_exception_2()
+  CheckEnglish
+
+  let test =<< trim [CODE]
+    try
+      Xpath 'a'
+      throw "oops"
+      call assert_report('should not get here')`
+    catch /arrgh/
+      call assert_report('should not get here')`
+    endtry
+    call assert_report('should not get here')`
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('E605: Exception not caught: oops', v:errmsg)
+    call assert_equal('a', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_uncaught_exception_3()
+  CheckEnglish
+
+  let test =<< trim [CODE]
+    func T()
+      Xpath 'c'
+      throw "brrr"
+      call assert_report('should not get here')`
+    endfunc
+
+    try
+      Xpath 'a'
+      throw "arrgh"
+      call assert_report('should not get here')`
+    catch /.*/
+      Xpath 'b'
+      call T()
+      call assert_report('should not get here')`
+    endtry
+    call assert_report('should not get here')`
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('E605: Exception not caught: brrr', v:errmsg)
+    call assert_equal('abc', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_uncaught_exception_4()
+  CheckEnglish
+
+  let test =<< trim [CODE]
+    try
+      Xpath 'a'
+      throw "arrgh"
+      call assert_report('should not get here')`
+    finally
+      Xpath 'b'
+      throw "brrr"
+      call assert_report('should not get here')`
+    endtry
+    call assert_report('should not get here')`
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('E605: Exception not caught: brrr', v:errmsg)
+    call assert_equal('ab', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_uncaught_exception_5()
+  CheckEnglish
+
+  " Need to catch and handle interrupt, otherwise the test will wait for the
+  " user to press  to continue
+  let test =<< trim [CODE]
+    try
+      try
+        Xpath 'a'
+        call interrupt()
+        call assert_report('should not get here')
+      endtry
+      call assert_report('should not get here')
+    catch /^Vim:Interrupt$/
+      Xpath 'b'
+    endtry
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('ab', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_uncaught_exception_6()
+  CheckEnglish
+
+  let test =<< trim [CODE]
+    try
+      Xpath 'a'
+      let x = novar	" error E121; exception: E121
+    catch /E15:/	" should not catch
+      call assert_report('should not get here')
+    endtry
+    call assert_report('should not get here')
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('a', g:Xpath)
+    call assert_equal('E121: Undefined variable: novar', v:errmsg)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+func Test_uncaught_exception_7()
+  CheckEnglish
+
+  let test =<< trim [CODE]
+    try
+      Xpath 'a'
+      " error E108/E488; exception: E488
+      unlet novar #
+    catch /E108:/       " should not catch
+      call assert_report('should not get here')
+    endtry
+    call assert_report('should not get here')
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('a', g:Xpath)
+    call assert_equal('E488: Trailing characters: #', v:errmsg)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 53:  Nesting errors: :endif/:else/:elseif			    {{{1
+"
+"	    For nesting errors of :if conditionals the correct error messages
+"	    should be given.
+"-------------------------------------------------------------------------------
+
+func Test_nested_if_else_errors()
+  CheckEnglish
+
+  " :endif without :if
+  let code =<< trim END
+    endif
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')
+
+  " :endif without :if
+  let code =<< trim END
+    while 1
+      endif
+    endwhile
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')
+
+  " :endif without :if
+  let code =<< trim END
+    try
+    finally
+      endif
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')
+
+  " :endif without :if
+  let code =<< trim END
+    try
+      endif
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')
+
+  " :endif without :if
+  let code =<< trim END
+    try
+      throw "a"
+    catch /a/
+      endif
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')
+
+  " :else without :if
+  let code =<< trim END
+    else
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')
+
+  " :else without :if
+  let code =<< trim END
+    while 1
+      else
+    endwhile
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')
+
+  " :else without :if
+  let code =<< trim END
+    try
+    finally
+      else
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')
+
+  " :else without :if
+  let code =<< trim END
+    try
+      else
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')
+
+  " :else without :if
+  let code =<< trim END
+    try
+      throw "a"
+    catch /a/
+      else
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')
+
+  " :elseif without :if
+  let code =<< trim END
+    elseif
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')
+
+  " :elseif without :if
+  let code =<< trim END
+    while 1
+      elseif
+    endwhile
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')
+
+  " :elseif without :if
+  let code =<< trim END
+    try
+    finally
+      elseif
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')
+
+  " :elseif without :if
+  let code =<< trim END
+    try
+      elseif
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')
+
+  " :elseif without :if
+  let code =<< trim END
+    try
+      throw "a"
+    catch /a/
+      elseif
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')
+
+  " multiple :else
+  let code =<< trim END
+    if 1
+    else
+    else
+    endif
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(else):E583: multiple :else')
+
+  " :elseif after :else
+  let code =<< trim END
+    if 1
+    else
+    elseif 1
+    endif
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(elseif):E584: :elseif after :else')
+
+  call delete('Xtest')
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 54:  Nesting errors: :while/:endwhile				    {{{1
+"
+"	    For nesting errors of :while conditionals the correct error messages
+"	    should be given.
+"
+"	    This test reuses the function MESSAGES() from the previous test.
+"	    This functions checks the messages in g:msgfile.
+"-------------------------------------------------------------------------------
+
+func Test_nested_while_error()
+  CheckEnglish
+
+  " :endwhile without :while
+  let code =<< trim END
+    endwhile
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+  " :endwhile without :while
+  let code =<< trim END
+    if 1
+      endwhile
+    endif
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+  " Missing :endif
+  let code =<< trim END
+    while 1
+      if 1
+    endwhile
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endwhile):E171: Missing :endif')
+
+  " :endwhile without :while
+  let code =<< trim END
+    try
+    finally
+      endwhile
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+  " Missing :endtry
+  let code =<< trim END
+    while 1
+      try
+      finally
+    endwhile
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endwhile):E600: Missing :endtry')
+
+  " Missing :endtry
+  let code =<< trim END
+    while 1
+      if 1
+        try
+        finally
+    endwhile
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endwhile):E600: Missing :endtry')
+
+  " Missing :endif
+  let code =<< trim END
+    while 1
+      try
+      finally
+        if 1
+    endwhile
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endwhile):E171: Missing :endif')
+
+  " :endwhile without :while
+  let code =<< trim END
+    try
+      endwhile
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+  " :endwhile without :while
+  let code =<< trim END
+    while 1
+      try
+        endwhile
+      endtry
+    endwhile
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+  " :endwhile without :while
+  let code =<< trim END
+    try
+      throw "a"
+    catch /a/
+      endwhile
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+  " :endwhile without :while
+  let code =<< trim END
+    while 1
+      try
+        throw "a"
+      catch /a/
+        endwhile
+      endtry
+    endwhile
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+  call delete('Xtest')
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 55:  Nesting errors: :continue/:break				    {{{1
+"
+"	    For nesting errors of :continue and :break commands the correct
+"	    error messages should be given.
+"
+"	    This test reuses the function MESSAGES() from the previous test.
+"	    This functions checks the messages in g:msgfile.
+"-------------------------------------------------------------------------------
+
+func Test_nested_cont_break_error()
+  CheckEnglish
+
+  " :continue without :while
+  let code =<< trim END
+    continue
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')
+
+  " :continue without :while
+  let code =<< trim END
+    if 1
+      continue
+    endif
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')
+
+  " :continue without :while
+  let code =<< trim END
+    try
+    finally
+      continue
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')
+
+  " :continue without :while
+  let code =<< trim END
+    try
+      continue
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')
+
+  " :continue without :while
+  let code =<< trim END
+    try
+      throw "a"
+    catch /a/
+      continue
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')
+
+  " :break without :while
+  let code =<< trim END
+    break
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')
+
+  " :break without :while
+  let code =<< trim END
+    if 1
+      break
+    endif
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')
+
+  " :break without :while
+  let code =<< trim END
+    try
+    finally
+      break
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')
+
+  " :break without :while
+  let code =<< trim END
+    try
+      break
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')
+
+  " :break without :while
+  let code =<< trim END
+    try
+      throw "a"
+    catch /a/
+      break
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')
+
+  call delete('Xtest')
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 56:  Nesting errors: :endtry					    {{{1
+"
+"	    For nesting errors of :try conditionals the correct error messages
+"	    should be given.
+"
+"	    This test reuses the function MESSAGES() from the previous test.
+"	    This functions checks the messages in g:msgfile.
+"-------------------------------------------------------------------------------
+
+func Test_nested_endtry_error()
+  CheckEnglish
+
+  " :endtry without :try
+  let code =<< trim END
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try')
+
+  " :endtry without :try
+  let code =<< trim END
+    if 1
+      endtry
+    endif
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try')
+
+  " :endtry without :try
+  let code =<< trim END
+    while 1
+      endtry
+    endwhile
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try')
+
+  " Missing :endif
+  let code =<< trim END
+    try
+        if 1
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif')
+
+  " Missing :endwhile
+  let code =<< trim END
+    try
+      while 1
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile')
+
+  " Missing :endif
+  let code =<< trim END
+    try
+    finally
+      if 1
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif')
+
+  " Missing :endwhile
+  let code =<< trim END
+    try
+    finally
+      while 1
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile')
+
+  " Missing :endif
+  let code =<< trim END
+    try
+      throw "a"
+    catch /a/
+      if 1
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif')
+
+  " Missing :endwhile
+  let code =<< trim END
+    try
+      throw "a"
+    catch /a/
+      while 1
+    endtry
+  END
+  call writefile(code, 'Xtest')
+  call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile')
+
+  call delete('Xtest')
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 57:  v:exception and v:throwpoint for user exceptions		    {{{1
+"
+"	    v:exception evaluates to the value of the exception that was caught
+"	    most recently and is not finished.  (A caught exception is finished
+"	    when the next ":catch", ":finally", or ":endtry" is reached.)
+"	    v:throwpoint evaluates to the script/function name and line number
+"	    where that exception has been thrown.
+"-------------------------------------------------------------------------------
+
+func Test_user_exception_info()
+  CheckEnglish
+
+  XpathINIT
+  XloopINIT
+
+  func FuncException()
+    let g:exception = v:exception
+  endfunc
+
+  func FuncThrowpoint()
+    let g:throwpoint = v:throwpoint
+  endfunc
+
+  let scriptException  = MakeScript("FuncException")
+  let scriptThrowPoint = MakeScript("FuncThrowpoint")
+
+  command! CmdException  let g:exception  = v:exception
+  command! CmdThrowpoint let g:throwpoint = v:throwpoint
+
+  func T(arg, line)
+    if a:line == 2
+      throw a:arg		" in line 2
+    elseif a:line == 4
+      throw a:arg		" in line 4
+    elseif a:line == 6
+      throw a:arg		" in line 6
+    elseif a:line == 8
+      throw a:arg		" in line 8
+    endif
+  endfunc
+
+  func G(arg, line)
+    call T(a:arg, a:line)
+  endfunc
+
+  func F(arg, line)
+    call G(a:arg, a:line)
+  endfunc
+
+  let scriptT = MakeScript("T")
+  let scriptG = MakeScript("G", scriptT)
+  let scriptF = MakeScript("F", scriptG)
+
+  try
+    Xpath 'a'
+    call F("oops", 2)
+  catch /.*/
+    Xpath 'b'
+    let exception  = v:exception
+    let throwpoint = v:throwpoint
+    call assert_equal("oops", v:exception)
+    call assert_match('\', v:throwpoint)
+    call assert_match('\<2\>', v:throwpoint)
+
+    exec "let exception  = v:exception"
+    exec "let throwpoint = v:throwpoint"
+    call assert_equal("oops", v:exception)
+    call assert_match('\', v:throwpoint)
+    call assert_match('\<2\>', v:throwpoint)
+
+    CmdException
+    CmdThrowpoint
+    call assert_equal("oops", v:exception)
+    call assert_match('\', v:throwpoint)
+    call assert_match('\<2\>', v:throwpoint)
+
+    call FuncException()
+    call FuncThrowpoint()
+    call assert_equal("oops", v:exception)
+    call assert_match('\', v:throwpoint)
+    call assert_match('\<2\>', v:throwpoint)
+
+    exec "source" scriptException
+    exec "source" scriptThrowPoint
+    call assert_equal("oops", v:exception)
+    call assert_match('\', v:throwpoint)
+    call assert_match('\<2\>', v:throwpoint)
+
+    try
+      Xpath 'c'
+      call G("arrgh", 4)
+    catch /.*/
+      Xpath 'd'
+      let exception  = v:exception
+      let throwpoint = v:throwpoint
+      call assert_equal("arrgh", v:exception)
+      call assert_match('\', v:throwpoint)
+      call assert_match('\<4\>', v:throwpoint)
+
+      try
+        Xpath 'e'
+        let g:arg = "autsch"
+        let g:line = 6
+        exec "source" scriptF
+      catch /.*/
+        Xpath 'f'
+        let exception  = v:exception
+        let throwpoint = v:throwpoint
+        call assert_equal("autsch", v:exception)
+        call assert_match(fnamemodify(scriptT, ':t'), v:throwpoint)
+        call assert_match('\<6\>', v:throwpoint)
+      finally
+        Xpath 'g'
+        let exception  = v:exception
+        let throwpoint = v:throwpoint
+        call assert_equal("arrgh", v:exception)
+        call assert_match('\', v:throwpoint)
+        call assert_match('\<4\>', v:throwpoint)
+        try
+          Xpath 'h'
+          let g:arg = "brrrr"
+          let g:line = 8
+          exec "source" scriptG
+        catch /.*/
+          Xpath 'i'
+          let exception  = v:exception
+          let throwpoint = v:throwpoint
+          " Resolve scriptT for matching it against v:throwpoint.
+          call assert_equal("brrrr", v:exception)
+          call assert_match(fnamemodify(scriptT, ':t'), v:throwpoint)
+          call assert_match('\<8\>', v:throwpoint)
+        finally
+          Xpath 'j'
+          let exception  = v:exception
+          let throwpoint = v:throwpoint
+          call assert_equal("arrgh", v:exception)
+          call assert_match('\', v:throwpoint)
+          call assert_match('\<4\>', v:throwpoint)
+        endtry
+        Xpath 'k'
+        let exception  = v:exception
+        let throwpoint = v:throwpoint
+        call assert_equal("arrgh", v:exception)
+        call assert_match('\', v:throwpoint)
+        call assert_match('\<4\>', v:throwpoint)
+      endtry
+      Xpath 'l'
+      let exception  = v:exception
+      let throwpoint = v:throwpoint
+      call assert_equal("arrgh", v:exception)
+      call assert_match('\', v:throwpoint)
+      call assert_match('\<4\>', v:throwpoint)
+    finally
+      Xpath 'm'
+      let exception  = v:exception
+      let throwpoint = v:throwpoint
+      call assert_equal("oops", v:exception)
+      call assert_match('\', v:throwpoint)
+      call assert_match('\<2\>', v:throwpoint)
+    endtry
+    Xpath 'n'
+    let exception  = v:exception
+    let throwpoint = v:throwpoint
+    call assert_equal("oops", v:exception)
+    call assert_match('\', v:throwpoint)
+    call assert_match('\<2\>', v:throwpoint)
+  finally
+    Xpath 'o'
+    let exception  = v:exception
+    let throwpoint = v:throwpoint
+    call assert_equal("", v:exception)
+    call assert_match('^$', v:throwpoint)
+    call assert_match('^$', v:throwpoint)
+  endtry
+
+  call assert_equal('abcdefghijklmno', g:Xpath)
+
+  unlet exception throwpoint
+  delfunction FuncException
+  delfunction FuncThrowpoint
+  call delete(scriptException)
+  call delete(scriptThrowPoint)
+  unlet scriptException scriptThrowPoint
+  delcommand CmdException
+  delcommand CmdThrowpoint
+  delfunction T
+  delfunction G
+  delfunction F
+  call delete(scriptT)
+  call delete(scriptG)
+  call delete(scriptF)
+  unlet scriptT scriptG scriptF
+endfunc
+
+"-------------------------------------------------------------------------------
+"
+" Test 58:  v:exception and v:throwpoint for error/interrupt exceptions	    {{{1
+"
+"	    v:exception and v:throwpoint work also for error and interrupt
+"	    exceptions.
+"-------------------------------------------------------------------------------
+
+func Test_execption_info_for_error()
+  CheckEnglish
+
+  let test =<< trim [CODE]
+    func T(line)
+      if a:line == 2
+        delfunction T		" error (function in use) in line 2
+      elseif a:line == 4
+        call interrupt()
+      endif
+    endfunc
+
+    while 1
+      try
+        Xpath 'a'
+        call T(2)
+        call assert_report('should not get here')
+      catch /.*/
+        Xpath 'b'
+        if v:exception !~ 'Vim(delfunction):'
+          call assert_report('should not get here')
+        endif
+        if v:throwpoint !~ '\'
+          call assert_report('should not get here')
+        endif
+        if v:throwpoint !~ '\<2\>'
+          call assert_report('should not get here')
+        endif
+      finally
+        Xpath 'c'
+        if v:exception != ""
+          call assert_report('should not get here')
+        endif
+        if v:throwpoint != ""
+          call assert_report('should not get here')
+        endif
+        break
+      endtry
+    endwhile
+
+    Xpath 'd'
+    if v:exception != ""
+      call assert_report('should not get here')
+    endif
+    if v:throwpoint != ""
+      call assert_report('should not get here')
+    endif
+
+    while 1
+      try
+        Xpath 'e'
+        call T(4)
+        call assert_report('should not get here')
+      catch /.*/
+        Xpath 'f'
+        if v:exception != 'Vim:Interrupt'
+          call assert_report('should not get here')
+        endif
+        if v:throwpoint !~ 'function T'
+          call assert_report('should not get here')
+        endif
+        if v:throwpoint !~ '\<4\>'
+          call assert_report('should not get here')
+        endif
+      finally
+        Xpath 'g'
+        if v:exception != ""
+          call assert_report('should not get here')
+        endif
+        if v:throwpoint != ""
+          call assert_report('should not get here')
+        endif
+        break
+      endtry
+    endwhile
+
+    Xpath 'h'
+    if v:exception != ""
+      call assert_report('should not get here')
+    endif
+    if v:throwpoint != ""
+      call assert_report('should not get here')
+    endif
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abcdefgh', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 61:  Catching interrupt exceptions				    {{{1
+"
+"	    When an interrupt occurs inside a :try/:endtry region, an
+"	    interrupt exception is thrown and can be caught.  Its value is
+"	    "Vim:Interrupt".  If the interrupt occurs after an error or a :throw
+"	    but before a matching :catch is reached, all following :catches of
+"	    that try block are ignored, but the interrupt exception can be
+"	    caught by the next surrounding try conditional.  An interrupt is
+"	    ignored when there is a previous interrupt that has not been caught
+"	    or causes a :finally clause to be executed.
+"-------------------------------------------------------------------------------
+
+func Test_catch_intr_exception()
+  let test =<< trim [CODE]
+    while 1
+      try
+        try
+          Xpath 'a'
+          call interrupt()
+          call assert_report('should not get here')
+        catch /^Vim:Interrupt$/
+          Xpath 'b'
+        finally
+          Xpath 'c'
+        endtry
+      catch /.*/
+        call assert_report('should not get here')
+      finally
+        Xpath 'd'
+        break
+      endtry
+    endwhile
+
+    while 1
+      try
+        try
+          try
+            Xpath 'e'
+            asdf
+            call assert_report('should not get here')
+          catch /do_not_catch/
+            call assert_report('should not get here')
+          catch /.*/
+            Xpath 'f'
+            call interrupt()
+            call assert_report('should not get here')
+          catch /.*/
+            call assert_report('should not get here')
+          finally
+            Xpath 'g'
+            call interrupt()
+            call assert_report('should not get here')
+          endtry
+        catch /^Vim:Interrupt$/
+          Xpath 'h'
+        finally
+          Xpath 'i'
+        endtry
+      catch /.*/
+        call assert_report('should not get here')
+      finally
+        Xpath 'j'
+        break
+      endtry
+    endwhile
+
+    while 1
+      try
+        try
+          try
+            Xpath 'k'
+            throw "x"
+            call assert_report('should not get here')
+          catch /do_not_catch/
+            call assert_report('should not get here')
+          catch /x/
+            Xpath 'l'
+            call interrupt()
+            call assert_report('should not get here')
+          catch /.*/
+            call assert_report('should not get here')
+          endtry
+        catch /^Vim:Interrupt$/
+          Xpath 'm'
+        finally
+          Xpath 'n'
+        endtry
+      catch /.*/
+        call assert_report('should not get here')
+      finally
+        Xpath 'o'
+        break
+      endtry
+    endwhile
+
+    while 1
+      try
+        try
+          Xpath 'p'
+          call interrupt()
+          call assert_report('should not get here')
+        catch /do_not_catch/
+          call interrupt()
+          call assert_report('should not get here')
+        catch /^Vim:Interrupt$/
+          Xpath 'q'
+        finally
+          Xpath 'r'
+        endtry
+      catch /.*/
+        call assert_report('should not get here')
+      finally
+        Xpath 's'
+        break
+      endtry
+    endwhile
+
+    Xpath 't'
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abcdefghijklmnopqrst', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 65:  Errors in the /pattern/ argument of a :catch		    {{{1
+"
+"	    On an error in the /pattern/ argument of a :catch, the :catch does
+"	    not match.  Any following :catches of the same :try/:endtry don't
+"	    match either.  Finally clauses are executed.
+"-------------------------------------------------------------------------------
+
+func Test_catch_pattern_error()
+  CheckEnglish
+  XpathINIT
+
+  try
+    try
+      Xpath 'a'
+      throw "oops"
+    catch /^oops$/
+      Xpath 'b'
+    catch /\)/		" not checked; exception has already been caught
+      call assert_report('should not get here')
+    endtry
+    Xpath 'c'
+  catch /.*/
+    call assert_report('should not get here')
+  endtry
+  call assert_equal('abc', g:Xpath)
+
+  XpathINIT
+  func F()
+    try
+      try
+        try
+          Xpath 'a'
+          throw "ab"
+        catch /abc/	" does not catch
+          call assert_report('should not get here')
+        catch /\)/	" error; discards exception
+          call assert_report('should not get here')
+        catch /.*/	" not checked
+          call assert_report('should not get here')
+        finally
+          Xpath 'b'
+        endtry
+        call assert_report('should not get here')
+      catch /^ab$/	" checked, but original exception is discarded
+        call assert_report('should not get here')
+      catch /^Vim(catch):/
+        Xpath 'c'
+        call assert_match('Vim(catch):E475: Invalid argument:', v:exception)
+      finally
+        Xpath 'd'
+      endtry
+      Xpath 'e'
+    catch /.*/
+      call assert_report('should not get here')
+    endtry
+    Xpath 'f'
+  endfunc
+
+  call F()
+  call assert_equal('abcdef', g:Xpath)
+
+  delfunc F
+endfunc
+
 "-------------------------------------------------------------------------------
 " Test 87   using (expr) ? funcref : funcref				    {{{1
 "
-- 
cgit 


From 13381692cd191fe003e77215f01483bb34b3917e Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 20:35:54 +0800
Subject: vim-patch:8.2.1417: test 49 is old style

Problem:    Test 49 is old style.
Solution:   Convert more parts to new style test. (Yegappan Lakshmanan,
            closes vim/vim#6682)

https://github.com/vim/vim/commit/efb6482949580ab89e6d7c5e1cb8d744ddd6ef80
---
 src/nvim/testdir/test49.ok          |   14 -
 src/nvim/testdir/test49.vim         | 2467 +----------------------------------
 src/nvim/testdir/test_vimscript.vim | 2055 ++++++++++++++++++++++++++++-
 3 files changed, 2023 insertions(+), 2513 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test49.ok b/src/nvim/testdir/test49.ok
index 8ca8a564c3..d687f5bc77 100644
--- a/src/nvim/testdir/test49.ok
+++ b/src/nvim/testdir/test49.ok
@@ -1,18 +1,4 @@
 Results of test49.vim:
-*** Test  59: OK (2038431743)
-*** Test  60: OK (311511339)
-*** Test  62: OK (286331153)
-*** Test  63: OK (236978127)
-*** Test  64: OK (1499645335)
-*** Test  66: OK (5464)
-*** Test  67: OK (212514423)
-*** Test  68: OK (212514423)
-*** Test  76: OK (1610087935)
-*** Test  77: OK (1388671)
-*** Test  78: OK (134217728)
-*** Test  79: OK (70288929)
-*** Test  80: OK (17895765)
-*** Test  81: OK (387)
 *** Test  82: OK (8454401)
 *** Test  83: OK (2835)
 *** Test  84: OK (934782101)
diff --git a/src/nvim/testdir/test49.vim b/src/nvim/testdir/test49.vim
index 6133f410ac..d5f8cd3b60 100644
--- a/src/nvim/testdir/test49.vim
+++ b/src/nvim/testdir/test49.vim
@@ -648,2468 +648,11 @@ function! MESSAGES(...)
     return match
 endfunction
 
-" Leave MESSAGES() for the next tests.
-
-" Tests 1 to 50, 52 to 57, 87 were moved to test_vimscript.vim
-" Tests 25, 26, 32, 33, 41-48, 51, 69-75 were moved to test_trycatch.vim
-let Xtest = 59
-
-"-------------------------------------------------------------------------------
-"
-" Test 59:  v:exception and v:throwpoint when discarding exceptions	    {{{1
-"
-"	    When a :catch clause is left by a ":break" etc or an error or
-"	    interrupt exception, v:exception and v:throwpoint are reset.  They
-"	    are not affected by an exception that is discarded before being
-"	    caught.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
-    XloopINIT! 1 2
-
-    let sfile = expand("")
-
-    function! LineNumber()
-	return substitute(substitute(v:throwpoint, g:sfile, '', ""),
-	    \ '\D*\(\d*\).*', '\1', "")
-    endfunction
-
-    command! -nargs=1 SetLineNumber
-	\ try | throw "line" | catch /.*/ | let  =  LineNumber() | endtry
-
-    " Check v:exception/v:throwpoint against second/fourth parameter if
-    " specified, check for being empty else.
-    function! CHECK(n, ...)
-	XloopNEXT
-	let exception = a:0 != 0 ? a:1 : ""	" second parameter (optional)
-	let emsg      = a:0 != 0 ? a:2 : ""	" third parameter (optional)
-	let line      = a:0 != 0 ? a:3 : 0	" fourth parameter (optional)
-	let error = 0
-	if emsg != ""
-	    " exception is the error number, emsg the English error message text
-	    if exception !~ '^E\d\+$'
-		Xout "TODO: Add message number for:" emsg
-	    elseif v:lang == "C" || v:lang =~ '^[Ee]n'
-		if exception == "E492" && emsg == "Not an editor command"
-		    let exception = '^Vim:' . exception . ': ' . emsg
-		else
-		    let exception = '^Vim(\a\+):' . exception . ': ' . emsg
-		endif
-	    else
-		if exception == "E492"
-		    let exception = '^Vim:' . exception
-		else
-		    let exception = '^Vim(\a\+):' . exception
-		endif
-	    endif
-	endif
-	if exception == "" && v:exception != ""
-	    Xout a:n.": v:exception is set:" v:exception
-	    let error = 1
-	elseif exception != "" && v:exception !~ exception
-	    Xout a:n.": v:exception (".v:exception.") does not match" exception
-	    let error = 1
-	endif
-	if line == 0 && v:throwpoint != ""
-	    Xout a:n.": v:throwpoint is set:" v:throwpoint
-	    let error = 1
-	elseif line != 0 && v:throwpoint !~ '\<' . line . '\>'
-	    Xout a:n.": v:throwpoint (".v:throwpoint.") does not match" line
-	    let error = 1
-	endif
-	if !error
-	    Xloop 1				" X: 2097151
-	endif
-    endfunction
-
-    while 1
-	try
-	    throw "x1"
-	catch /.*/
-	    break
-	endtry
-    endwhile
-    call CHECK(1)
-
-    while 1
-	try
-	    throw "x2"
-	catch /.*/
-	    break
-	finally
-	    call CHECK(2)
-	endtry
-	break
-    endwhile
-    call CHECK(3)
-
-    while 1
-	try
-	    let errcaught = 0
-	    try
-		try
-		    throw "x3"
-		catch /.*/
-		    SetLineNumber line_before_error
-		    asdf
-		endtry
-	    catch /.*/
-		let errcaught = 1
-		call CHECK(4, 'E492', "Not an editor command",
-		    \ line_before_error + 1)
-	    endtry
-	finally
-	    if !errcaught && $VIMNOERRTHROW
-		call CHECK(4)
-	    endif
-	    break		" discard error for $VIMNOERRTHROW
-	endtry
-    endwhile
-    call CHECK(5)
-
-    Xpath 2097152				" X: 2097152
-
-    while 1
-	try
-	    let intcaught = 0
-	    try
-		try
-		    throw "x4"
-		catch /.*/
-		    SetLineNumber two_lines_before_interrupt
-		    "INTERRUPT
-		    let dummy = 0
-		endtry
-	    catch /.*/
-		let intcaught = 1
-		call CHECK(6, "Vim:Interrupt", '',
-		    \ two_lines_before_interrupt + 2)
-	    endtry
-	finally
-	    if !intcaught && $VIMNOINTTHROW
-		call CHECK(6)
-	    endif
-	    break		" discard interrupt for $VIMNOINTTHROW
-	endtry
-    endwhile
-    call CHECK(7)
-
-    Xpath 4194304				" X: 4194304
-
-    while 1
-	try
-	    let errcaught = 0
-	    try
-		try
-"		    if 1
-			SetLineNumber line_before_throw
-			throw "x5"
-		    " missing endif
-		catch /.*/
-		    Xpath 8388608			" X: 0
-		endtry
-	    catch /.*/
-		let errcaught = 1
-		call CHECK(8, 'E171', "Missing :endif", line_before_throw + 3)
-	    endtry
-	finally
-	    if !errcaught && $VIMNOERRTHROW
-		call CHECK(8)
-	    endif
-	    break		" discard error for $VIMNOERRTHROW
-	endtry
-    endwhile
-    call CHECK(9)
-
-    Xpath 16777216				" X: 16777216
-
-    try
-	while 1
-	    try
-		throw "x6"
-	    finally
-		break
-	    endtry
-	    break
-	endwhile
-    catch /.*/
-	Xpath 33554432				" X: 0
-    endtry
-    call CHECK(10)
-
-    try
-	while 1
-	    try
-		throw "x7"
-	    finally
-		break
-	    endtry
-	    break
-	endwhile
-    catch /.*/
-	Xpath 67108864				" X: 0
-    finally
-	call CHECK(11)
-    endtry
-    call CHECK(12)
-
-    while 1
-	try
-	    let errcaught = 0
-	    try
-		try
-		    throw "x8"
-		finally
-		    SetLineNumber line_before_error
-		    asdf
-		endtry
-	    catch /.*/
-		let errcaught = 1
-		call CHECK(13, 'E492', "Not an editor command",
-		    \ line_before_error + 1)
-	    endtry
-	finally
-	    if !errcaught && $VIMNOERRTHROW
-		call CHECK(13)
-	    endif
-	    break		" discard error for $VIMNOERRTHROW
-	endtry
-    endwhile
-    call CHECK(14)
-
-    Xpath 134217728				" X: 134217728
-
-    while 1
-	try
-	    let intcaught = 0
-	    try
-		try
-		    throw "x9"
-		finally
-		    SetLineNumber two_lines_before_interrupt
-		    "INTERRUPT
-		endtry
-	    catch /.*/
-		let intcaught = 1
-		call CHECK(15, "Vim:Interrupt", '',
-		    \ two_lines_before_interrupt + 2)
-	    endtry
-	finally
-	    if !intcaught && $VIMNOINTTHROW
-		call CHECK(15)
-	    endif
-	    break		" discard interrupt for $VIMNOINTTHROW
-	endtry
-    endwhile
-    call CHECK(16)
-
-    Xpath 268435456				" X: 268435456
-
-    while 1
-	try
-	    let errcaught = 0
-	    try
-		try
-"		    if 1
-			SetLineNumber line_before_throw
-			throw "x10"
-		    " missing endif
-		finally
-		    call CHECK(17)
-		endtry
-	    catch /.*/
-		let errcaught = 1
-		call CHECK(18, 'E171', "Missing :endif", line_before_throw + 3)
-	    endtry
-	finally
-	    if !errcaught && $VIMNOERRTHROW
-		call CHECK(18)
-	    endif
-	    break		" discard error for $VIMNOERRTHROW
-	endtry
-    endwhile
-    call CHECK(19)
-
-    Xpath 536870912				" X: 536870912
-
-    while 1
-	try
-	    let errcaught = 0
-	    try
-		try
-"		    if 1
-			SetLineNumber line_before_throw
-			throw "x11"
-		    " missing endif
-		endtry
-	    catch /.*/
-		let errcaught = 1
-		call CHECK(20, 'E171', "Missing :endif", line_before_throw + 3)
-	    endtry
-	finally
-	    if !errcaught && $VIMNOERRTHROW
-		call CHECK(20)
-	    endif
-	    break		" discard error for $VIMNOERRTHROW
-	endtry
-    endwhile
-    call CHECK(21)
-
-    Xpath 1073741824				" X: 1073741824
-
-endif
-
-Xcheck 2038431743
-
-
-"-------------------------------------------------------------------------------
-"
-" Test 60:  (Re)throwing v:exception; :echoerr.				    {{{1
-"
-"	    A user exception can be rethrown after catching by throwing
-"	    v:exception.  An error or interrupt exception cannot be rethrown
-"	    because Vim exceptions cannot be faked.  A Vim exception using the
-"	    value of v:exception can, however, be triggered by the :echoerr
-"	    command.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-try
-    try
-	Xpath 1					" X: 1
-	throw "oops"
-    catch /oops/
-	Xpath 2					" X: 2
-	throw v:exception	" rethrow user exception
-    catch /.*/
-	Xpath 4					" X: 0
-    endtry
-catch /^oops$/			" catches rethrown user exception
-    Xpath 8					" X: 8
-catch /.*/
-    Xpath 16					" X: 0
-endtry
-
-function! F()
-    try
-	let caught = 0
-	try
-	    Xpath 32				" X: 32
-	    write /n/o/n/w/r/i/t/a/b/l/e/_/f/i/l/e
-	    Xpath 64				" X: 0
-	    Xout "did_emsg was reset before executing " .
-		\ "BufWritePost autocommands."
-	catch /^Vim(write):/
-	    let caught = 1
-	    throw v:exception	" throw error: cannot fake Vim exception
-	catch /.*/
-	    Xpath 128				" X: 0
-	finally
-	    Xpath 256				" X: 256
-	    if !caught && !$VIMNOERRTHROW
-		Xpath 512			" X: 0
-	    endif
-	endtry
-    catch /^Vim(throw):/	" catches throw error
-	let caught = caught + 1
-    catch /.*/
-	Xpath 1024				" X: 0
-    finally
-	Xpath 2048				" X: 2048
-	if caught != 2
-	    if !caught && !$VIMNOERRTHROW
-		Xpath 4096			" X: 0
-	    elseif caught
-		Xpath 8192			" X: 0
-	    endif
-	    return		| " discard error for $VIMNOERRTHROW
-	endif
-    endtry
-endfunction
-
-call F()
-delfunction F
-
-function! G()
-    try
-	let caught = 0
-	try
-	    Xpath 16384				" X: 16384
-	    asdf
-	catch /^Vim/		" catch error exception
-	    let caught = 1
-	    " Trigger Vim error exception with value specified after :echoerr
-	    let value = substitute(v:exception, '^Vim\((.*)\)\=:', '', "")
-	    echoerr value
-	catch /.*/
-	    Xpath 32768				" X: 0
-	finally
-	    Xpath 65536				" X: 65536
-	    if !caught
-		if !$VIMNOERRTHROW
-		    Xpath 131072		" X: 0
-		else
-		    let value = "Error"
-		    echoerr value
-		endif
-	    endif
-	endtry
-    catch /^Vim(echoerr):/
-	let caught = caught + 1
-	if v:exception !~ value
-	    Xpath 262144			" X: 0
-	endif
-    catch /.*/
-	Xpath 524288				" X: 0
-    finally
-	Xpath 1048576				" X: 1048576
-	if caught != 2
-	    if !caught && !$VIMNOERRTHROW
-		Xpath 2097152			" X: 0
-	    elseif caught
-		Xpath 4194304			" X: 0
-	    endif
-	    return		| " discard error for $VIMNOERRTHROW
-	endif
-    endtry
-endfunction
-
-call G()
-delfunction G
-
-unlet! value caught
-
-if ExtraVim()
-    try
-	let errcaught = 0
-	try
-	    Xpath 8388608			" X: 8388608
-	    let intcaught = 0
-	    "INTERRUPT
-	catch /^Vim:/		" catch interrupt exception
-	    let intcaught = 1
-	    " Trigger Vim error exception with value specified after :echoerr
-	    echoerr substitute(v:exception, '^Vim\((.*)\)\=:', '', "")
-	catch /.*/
-	    Xpath 16777216			" X: 0
-	finally
-	    Xpath 33554432			" X: 33554432
-	    if !intcaught
-		if !$VIMNOINTTHROW
-		    Xpath 67108864		" X: 0
-		else
-		    echoerr "Interrupt"
-		endif
-	    endif
-	endtry
-    catch /^Vim(echoerr):/
-	let errcaught = 1
-	if v:exception !~ "Interrupt"
-	    Xpath 134217728			" X: 0
-	endif
-    finally
-	Xpath 268435456				" X: 268435456
-	if !errcaught && !$VIMNOERRTHROW
-	    Xpath 536870912			" X: 0
-	endif
-    endtry
-endif
-
-Xcheck 311511339
-
-" Test 61 was moved to test_vimscript.vim
-let Xtest = 62
-
-"-------------------------------------------------------------------------------
-" Test 62:  Catching error exceptions					    {{{1
-"
-"	    An error inside a :try/:endtry region is converted to an exception
-"	    and can be caught.  The error exception has a "Vim(cmdname):" prefix
-"	    where cmdname is the name of the failing command, or a "Vim:" prefix
-"	    if no command name is known.  The "Vim" prefixes cannot be faked.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! MSG(enr, emsg)
-    let english = v:lang == "C" || v:lang =~ '^[Ee]n'
-    if a:enr == ""
-	Xout "TODO: Add message number for:" a:emsg
-	let v:errmsg = ":" . v:errmsg
-    endif
-    let match = 1
-    if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg)
-	let match = 0
-	if v:errmsg == ""
-	    Xout "Message missing."
-	else
-	    let v:errmsg = escape(v:errmsg, '"')
-	    Xout "Unexpected message:" v:errmsg
-	endif
-    endif
-    return match
-endfunction
-
-while 1
-    try
-	try
-	    let caught = 0
-	    unlet novar
-	catch /^Vim(unlet):/
-	    let caught = 1
-	    let v:errmsg = substitute(v:exception, '^Vim(unlet):', '', "")
-	finally
-	    Xpath 1				" X: 1
-	    if !caught && !$VIMNOERRTHROW
-		Xpath 2				" X: 0
-	    endif
-	    if !MSG('E108', "No such variable")
-		Xpath 4				" X: 0
-	    endif
-	endtry
-    catch /.*/
-	Xpath 8					" X: 0
-	Xout v:exception "in" v:throwpoint
-    finally
-	break		" discard error for $VIMNOERRTHROW
-    endtry
-endwhile
-
-while 1
-    try
-	try
-	    let caught = 0
-	    throw novar			" error in :throw
-	catch /^Vim(throw):/
-	    let caught = 1
-	    let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "")
-	finally
-	    Xpath 16				" X: 16
-	    if !caught && !$VIMNOERRTHROW
-		Xpath 32			" X: 0
-	    endif
-	    if caught ? !MSG('E121', "Undefined variable")
-			\ : !MSG('E15', "Invalid expression")
-		Xpath 64			" X: 0
-	    endif
-	endtry
-    catch /.*/
-	Xpath 128				" X: 0
-	Xout v:exception "in" v:throwpoint
-    finally
-	break		" discard error for $VIMNOERRTHROW
-    endtry
-endwhile
-
-while 1
-    try
-	try
-	    let caught = 0
-	    throw "Vim:faked"		" error: cannot fake Vim exception
-	catch /^Vim(throw):/
-	    let caught = 1
-	    let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "")
-	finally
-	    Xpath 256				" X: 256
-	    if !caught && !$VIMNOERRTHROW
-		Xpath 512			" X: 0
-	    endif
-	    if !MSG('E608', "Cannot :throw exceptions with 'Vim' prefix")
-		Xpath 1024			" X: 0
-	    endif
-	endtry
-    catch /.*/
-	Xpath 2048				" X: 0
-	Xout v:exception "in" v:throwpoint
-    finally
-	break		" discard error for $VIMNOERRTHROW
-    endtry
-endwhile
-
-function! F()
-    while 1
-    " Missing :endwhile
-endfunction
-
-while 1
-    try
-	try
-	    let caught = 0
-	    call F()
-	catch /^Vim(endfunction):/
-	    let caught = 1
-	    let v:errmsg = substitute(v:exception, '^Vim(endfunction):', '', "")
-	finally
-	    Xpath 4096				" X: 4096
-	    if !caught && !$VIMNOERRTHROW
-		Xpath 8192			" X: 0
-	    endif
-	    if !MSG('E170', "Missing :endwhile")
-		Xpath 16384			" X: 0
-	    endif
-	endtry
-    catch /.*/
-	Xpath 32768				" X: 0
-	Xout v:exception "in" v:throwpoint
-    finally
-	break		" discard error for $VIMNOERRTHROW
-    endtry
-endwhile
-
-while 1
-    try
-	try
-	    let caught = 0
-	    ExecAsScript F
-	catch /^Vim:/
-	    let caught = 1
-	    let v:errmsg = substitute(v:exception, '^Vim:', '', "")
-	finally
-	    Xpath 65536				" X: 65536
-	    if !caught && !$VIMNOERRTHROW
-		Xpath 131072			" X: 0
-	    endif
-	    if !MSG('E170', "Missing :endwhile")
-		Xpath 262144			" X: 0
-	    endif
-	endtry
-    catch /.*/
-	Xpath 524288				" X: 0
-	Xout v:exception "in" v:throwpoint
-    finally
-	break		" discard error for $VIMNOERRTHROW
-    endtry
-endwhile
-
-function! G()
-    call G()
-endfunction
-
-while 1
-    try
-	let mfd_save = &mfd
-	set mfd=3
-	try
-	    let caught = 0
-	    call G()
-	catch /^Vim(call):/
-	    let caught = 1
-	    let v:errmsg = substitute(v:exception, '^Vim(call):', '', "")
-	finally
-	    Xpath 1048576			" X: 1048576
-	    if !caught && !$VIMNOERRTHROW
-		Xpath 2097152			" X: 0
-	    endif
-	    if !MSG('E132', "Function call depth is higher than 'maxfuncdepth'")
-		Xpath 4194304			" X: 0
-	    endif
-	endtry
-    catch /.*/
-	Xpath 8388608				" X: 0
-	Xout v:exception "in" v:throwpoint
-    finally
-	let &mfd = mfd_save
-	break		" discard error for $VIMNOERRTHROW
-    endtry
-endwhile
-
-function! H()
-    return H()
-endfunction
-
-while 1
-    try
-	let mfd_save = &mfd
-	set mfd=3
-	try
-	    let caught = 0
-	    call H()
-	catch /^Vim(return):/
-	    let caught = 1
-	    let v:errmsg = substitute(v:exception, '^Vim(return):', '', "")
-	finally
-	    Xpath 16777216			" X: 16777216
-	    if !caught && !$VIMNOERRTHROW
-		Xpath 33554432			" X: 0
-	    endif
-	    if !MSG('E132', "Function call depth is higher than 'maxfuncdepth'")
-		Xpath 67108864			" X: 0
-	    endif
-	endtry
-    catch /.*/
-	Xpath 134217728				" X: 0
-	Xout v:exception "in" v:throwpoint
-    finally
-	let &mfd = mfd_save
-	break		" discard error for $VIMNOERRTHROW
-    endtry
-endwhile
-
-unlet! caught mfd_save
-delfunction F
-delfunction G
-delfunction H
-Xpath 268435456					" X: 268435456
-
-Xcheck 286331153
-
-" Leave MSG() for the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 63:  Suppressing error exceptions by :silent!.			    {{{1
-"
-"	    A :silent! command inside a :try/:endtry region suppresses the
-"	    conversion of errors to an exception and the immediate abortion on
-"	    error.  When the commands executed by the :silent! themselves open
-"	    a new :try/:endtry region, conversion of errors to exception and
-"	    immediate abortion is switched on again - until the next :silent!
-"	    etc.  The :silent! has the effect of setting v:errmsg to the error
-"	    message text (without displaying it) and continuing with the next
-"	    script line.
-"
-"	    When a command triggering autocommands is executed by :silent!
-"	    inside a :try/:endtry, the autocommand execution is not suppressed
-"	    on error.
-"
-"	    This test reuses the function MSG() from the previous test.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-XloopINIT! 1 4
-
-let taken = ""
-
-function! S(n) abort
-    XloopNEXT
-    let g:taken = g:taken . "E" . a:n
-    let v:errmsg = ""
-    exec "asdf" . a:n
-
-    " Check that ":silent!" continues:
-    Xloop 1
-
-    " Check that ":silent!" sets "v:errmsg":
-    if MSG('E492', "Not an editor command")
-	Xloop 2
-    endif
-endfunction
-
-function! Foo()
-    while 1
-	try
-	    try
-		let caught = 0
-		" This is not silent:
-		call S(3)				" X: 0 * 16
-	    catch /^Vim:/
-		let caught = 1
-		let errmsg3 = substitute(v:exception, '^Vim:', '', "")
-		silent! call S(4)			" X: 3 * 64
-	    finally
-		if !caught
-		    let errmsg3 = v:errmsg
-		    " Do call S(4) here if not executed in :catch.
-		    silent! call S(4)
-		endif
-		Xpath 1048576			" X: 1048576
-		if !caught && !$VIMNOERRTHROW
-		    Xpath 2097152		" X: 0
-		endif
-		let v:errmsg = errmsg3
-		if !MSG('E492', "Not an editor command")
-		    Xpath 4194304		" X: 0
-		endif
-		silent! call S(5)			" X: 3 * 256
-		" Break out of try conditionals that cover ":silent!".  This also
-		" discards the aborting error when $VIMNOERRTHROW is non-zero.
-		break
-	    endtry
-	catch /.*/
-	    Xpath 8388608			" X: 0
-	    Xout v:exception "in" v:throwpoint
-	endtry
-    endwhile
-    " This is a double ":silent!" (see caller).
-    silent! call S(6)					" X: 3 * 1024
-endfunction
-
-function! Bar()
-    try
-	silent! call S(2)				" X: 3 * 4
-							" X: 3 * 4096
-	silent! execute "call Foo() | call S(7)"
-	silent! call S(8)				" X: 3 * 16384
-    endtry	" normal end of try cond that covers ":silent!"
-    " This has a ":silent!" from the caller:
-    call S(9)						" X: 3 * 65536
-endfunction
-
-silent! call S(1)					" X: 3 * 1
-silent! call Bar()
-silent! call S(10)					" X: 3 * 262144
-
-let expected = "E1E2E3E4E5E6E7E8E9E10"
-if taken != expected
-    Xpath 16777216				" X: 0
-    Xout "'taken' is" taken "instead of" expected
-endif
-
-augroup TMP
-    autocmd BufWritePost * Xpath 33554432	" X: 33554432
-augroup END
-
-Xpath 67108864					" X: 67108864
-write /i/m/p/o/s/s/i/b/l/e
-Xpath 134217728					" X: 134217728
-
-autocmd! TMP
-unlet! caught errmsg3 taken expected
-delfunction S
-delfunction Foo
-delfunction Bar
-delfunction MSG
-
-Xcheck 236978127
-
-
-"-------------------------------------------------------------------------------
-" Test 64:  Error exceptions after error, interrupt or :throw		    {{{1
-"
-"	    When an error occurs after an interrupt or a :throw but before
-"	    a matching :catch is reached, all following :catches of that try
-"	    block are ignored, but the error exception can be caught by the next
-"	    surrounding try conditional.  Any previous error exception is
-"	    discarded.  An error is ignored when there is a previous error that
-"	    has not been caught.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
-    while 1
-	try
-	    try
-		Xpath 1				" X: 1
-		let caught = 0
-		while 1
-"		    if 1
-		    " Missing :endif
-		endwhile	" throw error exception
-	    catch /^Vim(/
-		let caught = 1
-	    finally
-		Xpath 2				" X: 2
-		if caught || $VIMNOERRTHROW
-		    Xpath 4			" X: 4
-		endif
-	    endtry
-	catch /.*/
-	    Xpath 8				" X: 0
-	    Xout v:exception "in" v:throwpoint
-	finally
-	    break		" discard error for $VIMNOERRTHROW
-	endtry
-    endwhile
-
-    while 1
-	try
-	    try
-		Xpath 16			" X: 16
-		let caught = 0
-		try
-"		    if 1
-		    " Missing :endif
-		catch /.*/	" throw error exception
-		    Xpath 32			" X: 0
-		catch /.*/
-		    Xpath 64			" X: 0
-		endtry
-	    catch /^Vim(/
-		let caught = 1
-	    finally
-		Xpath 128			" X: 128
-		if caught || $VIMNOERRTHROW
-		    Xpath 256			" X: 256
-		endif
-	    endtry
-	catch /.*/
-	    Xpath 512				" X: 0
-	    Xout v:exception "in" v:throwpoint
-	finally
-	    break		" discard error for $VIMNOERRTHROW
-	endtry
-    endwhile
-
-    while 1
-	try
-	    try
-		let caught = 0
-		try
-		    Xpath 1024			" X: 1024
-		    "INTERRUPT
-		catch /do_not_catch/
-		    Xpath 2048			" X: 0
-"		    if 1
-		    " Missing :endif
-		catch /.*/	" throw error exception
-		    Xpath 4096			" X: 0
-		catch /.*/
-		    Xpath 8192			" X: 0
-		endtry
-	    catch /^Vim(/
-		let caught = 1
-	    finally
-		Xpath 16384			" X: 16384
-		if caught || $VIMNOERRTHROW
-		    Xpath 32768			" X: 32768
-		endif
-	    endtry
-	catch /.*/
-	    Xpath 65536				" X: 0
-	    Xout v:exception "in" v:throwpoint
-	finally
-	    break		" discard error for $VIMNOERRTHROW
-	endtry
-    endwhile
-
-    while 1
-	try
-	    try
-		let caught = 0
-		try
-		    Xpath 131072		" X: 131072
-		    throw "x"
-		catch /do_not_catch/
-		    Xpath 262144		" X: 0
-"		    if 1
-		    " Missing :endif
-		catch /x/	" throw error exception
-		    Xpath 524288		" X: 0
-		catch /.*/
-		   Xpath 1048576		" X: 0
-		endtry
-	    catch /^Vim(/
-		let caught = 1
-	    finally
-		Xpath 2097152			" X: 2097152
-		if caught || $VIMNOERRTHROW
-		    Xpath 4194304		" X: 4194304
-		endif
-	    endtry
-	catch /.*/
-	    Xpath 8388608			" X: 0
-	    Xout v:exception "in" v:throwpoint
-	finally
-	    break		" discard error for $VIMNOERRTHROW
-	endtry
-    endwhile
-
-    while 1
-	try
-	    try
-		let caught = 0
-		Xpath 16777216			" X: 16777216
-"		endif		" :endif without :if; throw error exception
-"		if 1
-		" Missing :endif
-	    catch /do_not_catch/ " ignore new error
-		Xpath 33554432			" X: 0
-	    catch /^Vim(endif):/
-		let caught = 1
-	    catch /^Vim(/
-		Xpath 67108864			" X: 0
-	    finally
-		Xpath 134217728			" X: 134217728
-		if caught || $VIMNOERRTHROW
-		    Xpath 268435456		" X: 268435456
-		endif
-	    endtry
-	catch /.*/
-	    Xpath 536870912			" X: 0
-	    Xout v:exception "in" v:throwpoint
-	finally
-	    break		" discard error for $VIMNOERRTHROW
-	endtry
-    endwhile
-
-    Xpath 1073741824				" X: 1073741824
-
-endif
-
-Xcheck 1499645335
-
-" Test 65 was moved to test_vimscript.vim
-let Xtest = 66
-
-"-------------------------------------------------------------------------------
-" Test 66:  Stop range :call on error, interrupt, or :throw		    {{{1
-"
-"	    When a function which is multiply called for a range since it
-"	    doesn't handle the range itself has an error in a command
-"	    dynamically enclosed by :try/:endtry or gets an interrupt or
-"	    executes a :throw, no more calls for the remaining lines in the
-"	    range are made.  On an error in a command not dynamically enclosed
-"	    by :try/:endtry, the function is executed again for the remaining
-"	    lines in the range.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
-    let file = tempname()
-    exec "edit" file
-
-    insert
-line 1
-line 2
-line 3
-.
-
-    XloopINIT! 1 2
-
-    let taken = ""
-    let expected = "G1EF1E(1)F1E(2)F1E(3)G2EF2E(1)G3IF3I(1)G4TF4T(1)G5AF5A(1)"
-
-    function! F(reason, n) abort
-	let g:taken = g:taken . "F" . a:n .
-	    \ substitute(a:reason, '\(\l\).*', '\u\1', "") .
-	    \ "(" . line(".") . ")"
-
-	if a:reason == "error"
-	    asdf
-	elseif a:reason == "interrupt"
-	    "INTERRUPT
-	    let dummy = 0
-	elseif a:reason == "throw"
-	    throw "xyz"
-	elseif a:reason == "aborting error"
-	    XloopNEXT
-	    if g:taken != g:expected
-		Xloop 1				" X: 0
-		Xout "'taken' is" g:taken "instead of" g:expected
-	    endif
-	    try
-		bwipeout!
-		call delete(file)
-		asdf
-	    endtry
-	endif
-    endfunction
-
-    function! G(reason, n)
-	let g:taken = g:taken . "G" . a:n .
-	    \ substitute(a:reason, '\(\l\).*', '\u\1', "")
-	1,3call F(a:reason, a:n)
-    endfunction
-
-    Xpath 8					" X: 8
-    call G("error", 1)
-    try
-	Xpath 16				" X: 16
-	try
-	    call G("error", 2)
-	    Xpath 32				" X: 0
-	finally
-	    Xpath 64				" X: 64
-	    try
-		call G("interrupt", 3)
-		Xpath 128			" X: 0
-	    finally
-		Xpath 256			" X: 256
-		try
-		    call G("throw", 4)
-		    Xpath 512			" X: 0
-		endtry
-	    endtry
-	endtry
-    catch /xyz/
-	Xpath 1024				" X: 1024
-    catch /.*/
-	Xpath 2048				" X: 0
-	Xout v:exception "in" ExtraVimThrowpoint()
-    endtry
-    Xpath 4096					" X: 4096
-    call G("aborting error", 5)
-    Xpath 8192					" X: 0
-    Xout "'taken' is" taken "instead of" expected
-
-endif
-
-Xcheck 5464
-
-
-"-------------------------------------------------------------------------------
-" Test 67:  :throw across :call command					    {{{1
-"
-"	    On a call command, an exception might be thrown when evaluating the
-"	    function name, during evaluation of the arguments, or when the
-"	    function is being executed.  The exception can be caught by the
-"	    caller.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! THROW(x, n)
-    if a:n == 1
-	Xpath 1						" X: 1
-    elseif a:n == 2
-	Xpath 2						" X: 2
-    elseif a:n == 3
-	Xpath 4						" X: 4
-    endif
-    throw a:x
-endfunction
-
-function! NAME(x, n)
-    if a:n == 1
-	Xpath 8						" X: 0
-    elseif a:n == 2
-	Xpath 16					" X: 16
-    elseif a:n == 3
-	Xpath 32					" X: 32
-    elseif a:n == 4
-	Xpath 64					" X: 64
-    endif
-    return a:x
-endfunction
-
-function! ARG(x, n)
-    if a:n == 1
-	Xpath 128					" X: 0
-    elseif a:n == 2
-	Xpath 256					" X: 0
-    elseif a:n == 3
-	Xpath 512					" X: 512
-    elseif a:n == 4
-	Xpath 1024					" X: 1024
-    endif
-    return a:x
-endfunction
-
-function! F(x, n)
-    if a:n == 2
-	Xpath 2048					" X: 0
-    elseif a:n == 4
-	Xpath 4096					" X: 4096
-    endif
-endfunction
-
-while 1
-    try
-	let error = 0
-	let v:errmsg = ""
-
-	while 1
-	    try
-		Xpath 8192				" X: 8192
-		call {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1)
-		Xpath 16384				" X: 0
-	    catch /^name$/
-		Xpath 32768				" X: 32768
-	    catch /.*/
-		let error = 1
-		Xout "1:" v:exception "in" v:throwpoint
-	    finally
-		if !error && $VIMNOERRTHROW && v:errmsg != ""
-		    let error = 1
-		    Xout "1:" v:errmsg
-		endif
-		if error
-		    Xpath 65536				" X: 0
-		endif
-		let error = 0
-		let v:errmsg = ""
-		break		" discard error for $VIMNOERRTHROW
-	    endtry
-	endwhile
-
-	while 1
-	    try
-		Xpath 131072				" X: 131072
-		call {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2)
-		Xpath 262144				" X: 0
-	    catch /^arg$/
-		Xpath 524288				" X: 524288
-	    catch /.*/
-		let error = 1
-		Xout "2:" v:exception "in" v:throwpoint
-	    finally
-		if !error && $VIMNOERRTHROW && v:errmsg != ""
-		    let error = 1
-		    Xout "2:" v:errmsg
-		endif
-		if error
-		    Xpath 1048576			" X: 0
-		endif
-		let error = 0
-		let v:errmsg = ""
-		break		" discard error for $VIMNOERRTHROW
-	    endtry
-	endwhile
-
-	while 1
-	    try
-		Xpath 2097152				" X: 2097152
-		call {NAME("THROW", 3)}(ARG("call", 3), 3)
-		Xpath 4194304				" X: 0
-	    catch /^call$/
-		Xpath 8388608				" X: 8388608
-	    catch /^0$/	    " default return value
-		Xpath 16777216				" X: 0
-		Xout "3:" v:throwpoint
-	    catch /.*/
-		let error = 1
-		Xout "3:" v:exception "in" v:throwpoint
-	    finally
-		if !error && $VIMNOERRTHROW && v:errmsg != ""
-		    let error = 1
-		    Xout "3:" v:errmsg
-		endif
-		if error
-		    Xpath 33554432			" X: 0
-		endif
-		let error = 0
-		let v:errmsg = ""
-		break		" discard error for $VIMNOERRTHROW
-	    endtry
-	endwhile
-
-	while 1
-	    try
-		Xpath 67108864				" X: 67108864
-		call {NAME("F", 4)}(ARG(4711, 4), 4)
-		Xpath 134217728				" X: 134217728
-	    catch /.*/
-		let error = 1
-		Xout "4:" v:exception "in" v:throwpoint
-	    finally
-		if !error && $VIMNOERRTHROW && v:errmsg != ""
-		    let error = 1
-		    Xout "4:" v:errmsg
-		endif
-		if error
-		    Xpath 268435456			" X: 0
-		endif
-		let error = 0
-		let v:errmsg = ""
-		break		" discard error for $VIMNOERRTHROW
-	    endtry
-	endwhile
-
-    catch /^0$/	    " default return value
-	Xpath 536870912					" X: 0
-	Xout v:throwpoint
-    catch /.*/
-	let error = 1
-	Xout v:exception "in" v:throwpoint
-    finally
-	if !error && $VIMNOERRTHROW && v:errmsg != ""
-	    let error = 1
-	    Xout v:errmsg
-	endif
-	if error
-	    Xpath 1073741824				" X: 0
-	endif
-	break		" discard error for $VIMNOERRTHROW
-    endtry
-endwhile
-
-unlet error
-delfunction F
-
-Xcheck 212514423
-
-" Leave THROW(), NAME(), and ARG() for the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 68:  :throw across function calls in expressions			    {{{1
-"
-"	    On a function call within an expression, an exception might be
-"	    thrown when evaluating the function name, during evaluation of the
-"	    arguments, or when the function is being executed.  The exception
-"	    can be caught by the caller.
-"
-"	    This test reuses the functions THROW(), NAME(), and ARG() from the
-"	    previous test.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! F(x, n)
-    if a:n == 2
-	Xpath 2048					" X: 0
-    elseif a:n == 4
-	Xpath 4096					" X: 4096
-    endif
-    return a:x
-endfunction
-
-unlet! var1 var2 var3 var4
-
-while 1
-    try
-	let error = 0
-	let v:errmsg = ""
-
-	while 1
-	    try
-		Xpath 8192				" X: 8192
-		let var1 = {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1)
-		Xpath 16384				" X: 0
-	    catch /^name$/
-		Xpath 32768				" X: 32768
-	    catch /.*/
-		let error = 1
-		Xout "1:" v:exception "in" v:throwpoint
-	    finally
-		if !error && $VIMNOERRTHROW && v:errmsg != ""
-		    let error = 1
-		    Xout "1:" v:errmsg
-		endif
-		if error
-		    Xpath 65536				" X: 0
-		endif
-		let error = 0
-		let v:errmsg = ""
-		break		" discard error for $VIMNOERRTHROW
-	    endtry
-	endwhile
-
-	while 1
-	    try
-		Xpath 131072				" X: 131072
-		let var2 = {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2)
-		Xpath 262144				" X: 0
-	    catch /^arg$/
-		Xpath 524288				" X: 524288
-	    catch /.*/
-		let error = 1
-		Xout "2:" v:exception "in" v:throwpoint
-	    finally
-		if !error && $VIMNOERRTHROW && v:errmsg != ""
-		    let error = 1
-		    Xout "2:" v:errmsg
-		endif
-		if error
-		    Xpath 1048576			" X: 0
-		endif
-		let error = 0
-		let v:errmsg = ""
-		break		" discard error for $VIMNOERRTHROW
-	    endtry
-	endwhile
-
-	while 1
-	    try
-		Xpath 2097152				" X: 2097152
-		let var3 = {NAME("THROW", 3)}(ARG("call", 3), 3)
-		Xpath 4194304				" X: 0
-	    catch /^call$/
-		Xpath 8388608				" X: 8388608
-	    catch /^0$/	    " default return value
-		Xpath 16777216				" X: 0
-		Xout "3:" v:throwpoint
-	    catch /.*/
-		let error = 1
-		Xout "3:" v:exception "in" v:throwpoint
-	    finally
-		if !error && $VIMNOERRTHROW && v:errmsg != ""
-		    let error = 1
-		    Xout "3:" v:errmsg
-		endif
-		if error
-		    Xpath 33554432			" X: 0
-		endif
-		let error = 0
-		let v:errmsg = ""
-		break		" discard error for $VIMNOERRTHROW
-	    endtry
-	endwhile
-
-	while 1
-	    try
-		Xpath 67108864				" X: 67108864
-		let var4 = {NAME("F", 4)}(ARG(4711, 4), 4)
-		Xpath 134217728				" X: 134217728
-	    catch /.*/
-		let error = 1
-		Xout "4:" v:exception "in" v:throwpoint
-	    finally
-		if !error && $VIMNOERRTHROW && v:errmsg != ""
-		    let error = 1
-		    Xout "4:" v:errmsg
-		endif
-		if error
-		    Xpath 268435456			" X: 0
-		endif
-		let error = 0
-		let v:errmsg = ""
-		break		" discard error for $VIMNOERRTHROW
-	    endtry
-	endwhile
-
-    catch /^0$/	    " default return value
-	Xpath 536870912					" X: 0
-	Xout v:throwpoint
-    catch /.*/
-	let error = 1
-	Xout v:exception "in" v:throwpoint
-    finally
-	if !error && $VIMNOERRTHROW && v:errmsg != ""
-	    let error = 1
-	    Xout v:errmsg
-	endif
-	if error
-	    Xpath 1073741824				" X: 0
-	endif
-	break		" discard error for $VIMNOERRTHROW
-    endtry
-endwhile
-
-if exists("var1") || exists("var2") || exists("var3") ||
-	    \ !exists("var4") || var4 != 4711
-    " The Xpath command does not accept 2^31 (negative); add explicitly:
-    let Xpath = Xpath + 2147483648			" X: 0
-    if exists("var1")
-	Xout "var1 =" var1
-    endif
-    if exists("var2")
-	Xout "var2 =" var2
-    endif
-    if exists("var3")
-	Xout "var3 =" var3
-    endif
-    if !exists("var4")
-	Xout "var4 unset"
-    elseif var4 != 4711
-	Xout "var4 =" var4
-    endif
-endif
-
-unlet! error var1 var2 var3 var4
-delfunction THROW
-delfunction NAME
-delfunction ARG
-delfunction F
-
-Xcheck 212514423
-
-" Tests 69 to 75 were moved to test_trycatch.vim
-let Xtest = 76
-
-
-"-------------------------------------------------------------------------------
-" Test 76:  Errors, interrupts, :throw during expression evaluation	    {{{1
-"
-"	    When a function call made during expression evaluation is aborted
-"	    due to an error inside a :try/:endtry region or due to an interrupt
-"	    or a :throw, the expression evaluation is aborted as well.	No
-"	    message is displayed for the cancelled expression evaluation.  On an
-"	    error not inside :try/:endtry, the expression evaluation continues.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
-    let taken = ""
-
-    function! ERR(n)
-	let g:taken = g:taken . "E" . a:n
-	asdf
-    endfunction
-
-    function! ERRabort(n) abort
-	let g:taken = g:taken . "A" . a:n
-	asdf
-    endfunction	" returns -1; may cause follow-up msg for illegal var/func name
-
-    function! WRAP(n, arg)
-	let g:taken = g:taken . "W" . a:n
-	let g:saved_errmsg = v:errmsg
-	return arg
-    endfunction
-
-    function! INT(n)
-	let g:taken = g:taken . "I" . a:n
-	"INTERRUPT9
-	let dummy = 0
-    endfunction
-
-    function! THR(n)
-	let g:taken = g:taken . "T" . a:n
-	throw "should not be caught"
-    endfunction
-
-    function! CONT(n)
-	let g:taken = g:taken . "C" . a:n
-    endfunction
-
-    function! MSG(n)
-	let g:taken = g:taken . "M" . a:n
-	let errmsg = (a:n >= 37 && a:n <= 44) ? g:saved_errmsg : v:errmsg
-	let msgptn = (a:n >= 10 && a:n <= 27) ? "^$" : "asdf"
-	if errmsg !~ msgptn
-	    let g:taken = g:taken . "x"
-	    Xout "Expr" a:n.": Unexpected message:" v:errmsg
-	endif
-	let v:errmsg = ""
-	let g:saved_errmsg = ""
-    endfunction
-
-    let v:errmsg = ""
-
-    try
-	let t = 1
-	XloopINIT 1 2
-	while t <= 9
-	    Xloop 1				" X: 511
-	    try
-		if t == 1
-		    let v{ERR(t) + CONT(t)} = 0
-		elseif t == 2
-		    let v{ERR(t) + CONT(t)}
-		elseif t == 3
-		    let var = exists('v{ERR(t) + CONT(t)}')
-		elseif t == 4
-		    unlet v{ERR(t) + CONT(t)}
-		elseif t == 5
-		    function F{ERR(t) + CONT(t)}()
-		    endfunction
-		elseif t == 6
-		    function F{ERR(t) + CONT(t)}
-		elseif t == 7
-		    let var = exists('*F{ERR(t) + CONT(t)}')
-		elseif t == 8
-		    delfunction F{ERR(t) + CONT(t)}
-		elseif t == 9
-		    let var = ERR(t) + CONT(t)
-		endif
-	    catch /asdf/
-		" v:errmsg is not set when the error message is converted to an
-		" exception.  Set it to the original error message.
-		let v:errmsg = substitute(v:exception, '^Vim:', '', "")
-	    catch /^Vim\((\a\+)\)\=:/
-		" An error exception has been thrown after the original error.
-		let v:errmsg = ""
-	    finally
-		call MSG(t)
-		let t = t + 1
-		XloopNEXT
-		continue	" discard an aborting error
-	    endtry
-	endwhile
-    catch /.*/
-	Xpath 512				" X: 0
-	Xout v:exception "in" ExtraVimThrowpoint()
-    endtry
-
-    try
-	let t = 10
-	XloopINIT 1024 2
-	while t <= 18
-	    Xloop 1				" X: 1024 * 511
-	    try
-		if t == 10
-		    let v{INT(t) + CONT(t)} = 0
-		elseif t == 11
-		    let v{INT(t) + CONT(t)}
-		elseif t == 12
-		    let var = exists('v{INT(t) + CONT(t)}')
-		elseif t == 13
-		    unlet v{INT(t) + CONT(t)}
-		elseif t == 14
-		    function F{INT(t) + CONT(t)}()
-		    endfunction
-		elseif t == 15
-		    function F{INT(t) + CONT(t)}
-		elseif t == 16
-		    let var = exists('*F{INT(t) + CONT(t)}')
-		elseif t == 17
-		    delfunction F{INT(t) + CONT(t)}
-		elseif t == 18
-		    let var = INT(t) + CONT(t)
-		endif
-	    catch /^Vim\((\a\+)\)\=:\(Interrupt\)\@!/
-		" An error exception has been triggered after the interrupt.
-		let v:errmsg = substitute(v:exception,
-		    \ '^Vim\((\a\+)\)\=:', '', "")
-	    finally
-		call MSG(t)
-		let t = t + 1
-		XloopNEXT
-		continue	" discard interrupt
-	    endtry
-	endwhile
-    catch /.*/
-	Xpath 524288				" X: 0
-	Xout v:exception "in" ExtraVimThrowpoint()
-    endtry
-
-    try
-	let t = 19
-	XloopINIT 1048576 2
-	while t <= 27
-	    Xloop 1				" X: 1048576 * 511
-	    try
-		if t == 19
-		    let v{THR(t) + CONT(t)} = 0
-		elseif t == 20
-		    let v{THR(t) + CONT(t)}
-		elseif t == 21
-		    let var = exists('v{THR(t) + CONT(t)}')
-		elseif t == 22
-		    unlet v{THR(t) + CONT(t)}
-		elseif t == 23
-		    function F{THR(t) + CONT(t)}()
-		    endfunction
-		elseif t == 24
-		    function F{THR(t) + CONT(t)}
-		elseif t == 25
-		    let var = exists('*F{THR(t) + CONT(t)}')
-		elseif t == 26
-		    delfunction F{THR(t) + CONT(t)}
-		elseif t == 27
-		    let var = THR(t) + CONT(t)
-		endif
-	    catch /^Vim\((\a\+)\)\=:/
-		" An error exception has been triggered after the :throw.
-		let v:errmsg = substitute(v:exception,
-		    \ '^Vim\((\a\+)\)\=:', '', "")
-	    finally
-		call MSG(t)
-		let t = t + 1
-		XloopNEXT
-		continue	" discard exception
-	    endtry
-	endwhile
-    catch /.*/
-	Xpath 536870912				" X: 0
-	Xout v:exception "in" ExtraVimThrowpoint()
-    endtry
-
-    let v{ERR(28) + CONT(28)} = 0
-    call MSG(28)
-    let v{ERR(29) + CONT(29)}
-    call MSG(29)
-    let var = exists('v{ERR(30) + CONT(30)}')
-    call MSG(30)
-    unlet v{ERR(31) + CONT(31)}
-    call MSG(31)
-    function F{ERR(32) + CONT(32)}()
-    endfunction
-    call MSG(32)
-    function F{ERR(33) + CONT(33)}
-    call MSG(33)
-    let var = exists('*F{ERR(34) + CONT(34)}')
-    call MSG(34)
-    delfunction F{ERR(35) + CONT(35)}
-    call MSG(35)
-    let var = ERR(36) + CONT(36)
-    call MSG(36)
-
-    let saved_errmsg = ""
-
-    let v{WRAP(37, ERRabort(37)) + CONT(37)} = 0
-    call MSG(37)
-    let v{WRAP(38, ERRabort(38)) + CONT(38)}
-    call MSG(38)
-    let var = exists('v{WRAP(39, ERRabort(39)) + CONT(39)}')
-    call MSG(39)
-    unlet v{WRAP(40, ERRabort(40)) + CONT(40)}
-    call MSG(40)
-    function F{WRAP(41, ERRabort(41)) + CONT(41)}()
-    endfunction
-    call MSG(41)
-    function F{WRAP(42, ERRabort(42)) + CONT(42)}
-    call MSG(42)
-    let var = exists('*F{WRAP(43, ERRabort(43)) + CONT(43)}')
-    call MSG(43)
-    delfunction F{WRAP(44, ERRabort(44)) + CONT(44)}
-    call MSG(44)
-    let var = ERRabort(45) + CONT(45)
-    call MSG(45)
-
-    Xpath 1073741824				" X: 1073741824
-
-    let expected = ""
-	\ . "E1M1E2M2E3M3E4M4E5M5E6M6E7M7E8M8E9M9"
-	\ . "I10M10I11M11I12M12I13M13I14M14I15M15I16M16I17M17I18M18"
-	\ . "T19M19T20M20T21M21T22M22T23M23T24M24T25M25T26M26T27M27"
-	\ . "E28C28M28E29C29M29E30C30M30E31C31M31E32C32M32E33C33M33"
-	\ . "E34C34M34E35C35M35E36C36M36"
-	\ . "A37W37C37M37A38W38C38M38A39W39C39M39A40W40C40M40A41W41C41M41"
-	\ . "A42W42C42M42A43W43C43M43A44W44C44M44A45C45M45"
-
-    if taken != expected
-	" The Xpath command does not accept 2^31 (negative); display explicitly:
-	exec "!echo 2147483648 >>" . g:ExtraVimResult
-						" X: 0
-	Xout "'taken' is" taken "instead of" expected
-	if substitute(taken,
-	\ '\(.*\)E3C3M3x\(.*\)E30C30M30x\(.*\)A39C39M39x\(.*\)',
-	\ '\1E3M3\2E30C30M30\3A39C39M39\4',
-	\ "") == expected
-	    Xout "Is ++emsg_skip for var with expr_start non-NULL"
-		\ "in f_exists ok?"
-	endif
-    endif
-
-    unlet! v var saved_errmsg taken expected
-    call delete(WA_t5)
-    call delete(WA_t14)
-    call delete(WA_t23)
-    unlet! WA_t5 WA_t14 WA_t23
-    delfunction WA_t5
-    delfunction WA_t14
-    delfunction WA_t23
-
-endif
-
-Xcheck 1610087935
-
-
-"-------------------------------------------------------------------------------
-" Test 77:  Errors, interrupts, :throw in name{brace-expression}	    {{{1
-"
-"	    When a function call made during evaluation of an expression in
-"	    braces as part of a function name after ":function" is aborted due
-"	    to an error inside a :try/:endtry region or due to an interrupt or
-"	    a :throw, the expression evaluation is aborted as well, and the
-"	    function definition is ignored, skipping all commands to the
-"	    ":endfunction".  On an error not inside :try/:endtry, the expression
-"	    evaluation continues and the function gets defined, and can be
-"	    called and deleted.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-XloopINIT 1 4
-
-function! ERR() abort
-    Xloop 1					" X: 1 + 4 + 16 + 64
-    asdf
-endfunction		" returns -1
-
-function! OK()
-    Xloop 2					" X: 2 * (1 + 4 + 16)
-    let v:errmsg = ""
-    return 0
-endfunction
-
-let v:errmsg = ""
-
-Xpath 4096					" X: 4096
-function! F{1 + ERR() + OK()}(arg)
-    " F0 should be defined.
-    if exists("a:arg") && a:arg == "calling"
-	Xpath 8192				" X: 8192
-    else
-	Xpath 16384				" X: 0
-    endif
-endfunction
-if v:errmsg != ""
-    Xpath 32768					" X: 0
-endif
-XloopNEXT
-
-Xpath 65536					" X: 65536
-call F{1 + ERR() + OK()}("calling")
-if v:errmsg != ""
-    Xpath 131072				" X: 0
-endif
-XloopNEXT
-
-Xpath 262144					" X: 262144
-delfunction F{1 + ERR() + OK()}
-if v:errmsg != ""
-    Xpath 524288				" X: 0
-endif
-XloopNEXT
-
-try
-    while 1
-	let caught = 0
-	try
-	    Xpath 1048576			" X: 1048576
-	    function! G{1 + ERR() + OK()}(arg)
-		" G0 should not be defined, and the function body should be
-		" skipped.
-		if exists("a:arg") && a:arg == "calling"
-		    Xpath 2097152		" X: 0
-		else
-		    Xpath 4194304		" X: 0
-		endif
-		" Use an unmatched ":finally" to check whether the body is
-		" skipped when an error occurs in ERR().  This works whether or
-		" not the exception is converted to an exception.
-		finally
-		    Xpath 8388608		" X: 0
-		    Xout "Body of G{1 + ERR() + OK()}() not skipped"
-		    " Discard the aborting error or exception, and break the
-		    " while loop.
-		    break
-		" End the try conditional and start a new one to avoid
-		" ":catch after :finally" errors.
-		endtry
-		try
-		Xpath 16777216			" X: 0
-	    endfunction
-
-	    " When the function was not defined, this won't be reached - whether
-	    " the body was skipped or not.  When the function was defined, it
-	    " can be called and deleted here.
-	    Xpath 33554432			" X: 0
-	    Xout "G0() has been defined"
-	    XloopNEXT
-	    try
-		call G{1 + ERR() + OK()}("calling")
-	    catch /.*/
-		Xpath 67108864			" X: 0
-	    endtry
-	    Xpath 134217728			" X: 0
-	    XloopNEXT
-	    try
-		delfunction G{1 + ERR() + OK()}
-	    catch /.*/
-		Xpath 268435456			" X: 0
-	    endtry
-	catch /asdf/
-	    " Jumped to when the function is not defined and the body is
-	    " skipped.
-	    let caught = 1
-	catch /.*/
-	    Xpath 536870912			" X: 0
-	finally
-	    if !caught && !$VIMNOERRTHROW
-		Xpath 1073741824		" X: 0
-	    endif
-	    break		" discard error for $VIMNOERRTHROW
-	endtry			" jumped to when the body is not skipped
-    endwhile
-catch /.*/
-    " The Xpath command does not accept 2^31 (negative); add explicitly:
-    let Xpath = Xpath + 2147483648		" X: 0
-    Xout "Body of G{1 + ERR() + OK()}() not skipped, exception caught"
-    Xout v:exception "in" v:throwpoint
-endtry
-
-Xcheck 1388671
-
-
-"-------------------------------------------------------------------------------
-" Test 78:  Messages on parsing errors in expression evaluation		    {{{1
-"
-"	    When an expression evaluation detects a parsing error, an error
-"	    message is given and converted to an exception, and the expression
-"	    evaluation is aborted.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
-    let taken = ""
-
-    function! F(n)
-	let g:taken = g:taken . "F" . a:n
-    endfunction
-
-    function! MSG(n, enr, emsg)
-	let g:taken = g:taken . "M" . a:n
-	let english = v:lang == "C" || v:lang =~ '^[Ee]n'
-	if a:enr == ""
-	    Xout "TODO: Add message number for:" a:emsg
-	    let v:errmsg = ":" . v:errmsg
-	endif
-	if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg)
-	    if v:errmsg == ""
-		Xout "Expr" a:n.": Message missing."
-		let g:taken = g:taken . "x"
-	    else
-		let v:errmsg = escape(v:errmsg, '"')
-		Xout "Expr" a:n.": Unexpected message:" v:errmsg
-		Xout "Expected: " . a:enr . ': ' . a:emsg
-		let g:taken = g:taken . "X"
-	    endif
-	endif
-    endfunction
-
-    function! CONT(n)
-	let g:taken = g:taken . "C" . a:n
-    endfunction
-
-    let v:errmsg = ""
-    XloopINIT 1 2
-
-    try
-	let t = 1
-	while t <= 14
-	    let g:taken = g:taken . "T" . t
-	    let v:errmsg = ""
-	    try
-		let caught = 0
-		if t == 1
-		    let v{novar + CONT(t)} = 0
-		elseif t == 2
-		    let v{novar + CONT(t)}
-		elseif t == 3
-		    let var = exists('v{novar + CONT(t)}')
-		elseif t == 4
-		    unlet v{novar + CONT(t)}
-		elseif t == 5
-		    function F{novar + CONT(t)}()
-		    endfunction
-		elseif t == 6
-		    function F{novar + CONT(t)}
-		elseif t == 7
-		    let var = exists('*F{novar + CONT(t)}')
-		elseif t == 8
-		    delfunction F{novar + CONT(t)}
-		elseif t == 9
-		    echo novar + CONT(t)
-		elseif t == 10
-		    echo v{novar + CONT(t)}
-		elseif t == 11
-		    echo F{novar + CONT(t)}
-		elseif t == 12
-		    let var = novar + CONT(t)
-		elseif t == 13
-		    let var = v{novar + CONT(t)}
-		elseif t == 14
-		    let var = F{novar + CONT(t)}()
-		endif
-	    catch /^Vim\((\a\+)\)\=:/
-		" v:errmsg is not set when the error message is converted to an
-		" exception.  Set it to the original error message.
-		let v:errmsg = substitute(v:exception,
-		    \ '^Vim\((\a\+)\)\=:', '', "")
-		let caught = 1
-	    finally
-		if t <= 8 && t != 3 && t != 7
-		    call MSG(t, 'E475', 'Invalid argument\>')
-		else
-		    if !caught	" no error exceptions ($VIMNOERRTHROW set)
-			call MSG(t, 'E15', "Invalid expression")
-		    else
-			call MSG(t, 'E121', "Undefined variable")
-		    endif
-		endif
-		let t = t + 1
-		XloopNEXT
-		continue	" discard an aborting error
-	    endtry
-	endwhile
-    catch /.*/
-	Xloop 1					" X: 0
-	Xout t.":" v:exception "in" ExtraVimThrowpoint()
-    endtry
-
-    function! T(n, expr, enr, emsg)
-	try
-	    let g:taken = g:taken . "T" . a:n
-	    let v:errmsg = ""
-	    try
-		let caught = 0
-		execute "let var = " . a:expr
-	    catch /^Vim\((\a\+)\)\=:/
-		" v:errmsg is not set when the error message is converted to an
-		" exception.  Set it to the original error message.
-		let v:errmsg = substitute(v:exception,
-		    \ '^Vim\((\a\+)\)\=:', '', "")
-		let caught = 1
-	    finally
-		if !caught	" no error exceptions ($VIMNOERRTHROW set)
-		    call MSG(a:n, 'E15', "Invalid expression")
-		else
-		    call MSG(a:n, a:enr, a:emsg)
-		endif
-		XloopNEXT
-		" Discard an aborting error:
-		return
-	    endtry
-	catch /.*/
-	    Xloop 1				" X: 0
-	    Xout a:n.":" v:exception "in" ExtraVimThrowpoint()
-	endtry
-    endfunction
-
-    call T(15, 'Nofunc() + CONT(15)',	'E117',	"Unknown function")
-    call T(16, 'F(1 2 + CONT(16))',	'E116',	"Invalid arguments")
-    call T(17, 'F(1, 2) + CONT(17)',	'E118',	"Too many arguments")
-    call T(18, 'F() + CONT(18)',	'E119',	"Not enough arguments")
-    call T(19, '{(1} + CONT(19)',	'E110',	"Missing ')'")
-    call T(20, '("abc"[1) + CONT(20)',	'E111',	"Missing ']'")
-    call T(21, '(1 +) + CONT(21)',	'E15',	"Invalid expression")
-    call T(22, '1 2 + CONT(22)',	'E15',	"Invalid expression")
-    call T(23, '(1 ? 2) + CONT(23)',	'E109',	"Missing ':' after '?'")
-    call T(24, '("abc) + CONT(24)',	'E114',	"Missing quote")
-    call T(25, "('abc) + CONT(25)",	'E115',	"Missing quote")
-    call T(26, '& + CONT(26)',		'E112', "Option name missing")
-    call T(27, '&asdf + CONT(27)',	'E113', "Unknown option")
-
-    Xpath 134217728				" X: 134217728
-
-    let expected = ""
-	\ . "T1M1T2M2T3M3T4M4T5M5T6M6T7M7T8M8T9M9T10M10T11M11T12M12T13M13T14M14"
-	\ . "T15M15T16M16T17M17T18M18T19M19T20M20T21M21T22M22T23M23T24M24T25M25"
-	\ . "T26M26T27M27"
-
-    if taken != expected
-	Xpath 268435456				" X: 0
-	Xout "'taken' is" taken "instead of" expected
-	if substitute(taken, '\(.*\)T3M3x\(.*\)', '\1T3M3\2', "") == expected
-	    Xout "Is ++emsg_skip for var with expr_start non-NULL"
-		\ "in f_exists ok?"
-	endif
-    endif
-
-    unlet! var caught taken expected
-    call delete(WA_t5)
-    unlet! WA_t5
-    delfunction WA_t5
-
-endif
-
-Xcheck 134217728
-
-
-"-------------------------------------------------------------------------------
-" Test 79:  Throwing one of several errors for the same command		    {{{1
-"
-"	    When several errors appear in a row (for instance during expression
-"	    evaluation), the first as the most specific one is used when
-"	    throwing an error exception.  If, however, a syntax error is
-"	    detected afterwards, this one is used for the error exception.
-"	    On a syntax error, the next command is not executed, on a normal
-"	    error, however, it is (relevant only in a function without the
-"	    "abort" flag).  v:errmsg is not set.
-"
-"	    If throwing error exceptions is configured off, v:errmsg is always
-"	    set to the latest error message, that is, to the more general
-"	    message or the syntax error, respectively.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-XloopINIT 1 2
-
-function! NEXT(cmd)
-    exec a:cmd . " | Xloop 1"
-endfunction
-
-call NEXT('echo novar')				" X: 1 *  1  (checks nextcmd)
-XloopNEXT
-call NEXT('let novar #')			" X: 0 *  2  (skips nextcmd)
-XloopNEXT
-call NEXT('unlet novar #')			" X: 0 *  4  (skips nextcmd)
-XloopNEXT
-call NEXT('let {novar}')			" X: 0 *  8  (skips nextcmd)
-XloopNEXT
-call NEXT('unlet{ novar}')			" X: 0 * 16  (skips nextcmd)
-
-function! EXEC(cmd)
-    exec a:cmd
-endfunction
-
-function! MATCH(expected, msg, enr, emsg)
-    let msg = a:msg
-    if a:enr == ""
-	Xout "TODO: Add message number for:" a:emsg
-	let msg = ":" . msg
-    endif
-    let english = v:lang == "C" || v:lang =~ '^[Ee]n'
-    if msg !~ '^'.a:enr.':' || (english && msg !~ a:emsg)
-	let match =  0
-	if a:expected		" no match although expected
-	    if a:msg == ""
-		Xout "Message missing."
-	    else
-		let msg = escape(msg, '"')
-		Xout "Unexpected message:" msg
-		Xout "Expected:" a:enr . ": " . a:emsg
-	    endif
-	endif
-    else
-	let match =  1
-	if !a:expected		" match although not expected
-	    let msg = escape(msg, '"')
-	    Xout "Unexpected message:" msg
-	    Xout "Expected none."
-	endif
-    endif
-    return match
-endfunction
-
-try
-
-    while 1				" dummy loop
-	try
-	    let v:errmsg = ""
-	    let caught = 0
-	    let thrmsg = ""
-	    call EXEC('echo novar')	" normal error
-	catch /^Vim\((\a\+)\)\=:/
-	    let caught = 1
-	    let thrmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
-	finally
-	    Xpath 32				" X: 32
-	    if !caught
-		if !$VIMNOERRTHROW
-		    Xpath 64			" X: 0
-		endif
-	    elseif !MATCH(1, thrmsg, 'E121', "Undefined variable")
-	    \ || v:errmsg != ""
-		Xpath 128			" X: 0
-	    endif
-	    if !caught && !MATCH(1, v:errmsg, 'E15', "Invalid expression")
-		Xpath 256			" X: 0
-	    endif
-	    break			" discard error if $VIMNOERRTHROW == 1
-	endtry
-    endwhile
-
-    Xpath 512					" X: 512
-    let cmd = "let"
-    XloopINIT 1024 32
-    while cmd != ""
-	try
-	    let v:errmsg = ""
-	    let caught = 0
-	    let thrmsg = ""
-	    call EXEC(cmd . ' novar #')		" normal plus syntax error
-	catch /^Vim\((\a\+)\)\=:/
-	    let caught = 1
-	    let thrmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
-	finally
-	    Xloop 1				" X: 1024 * (1 + 32)
-	    if !caught
-		if !$VIMNOERRTHROW
-		    Xloop 2			" X: 0
-		endif
-	    else
-		if cmd == "let"
-		    let match = MATCH(0, thrmsg, 'E121', "Undefined variable")
-		elseif cmd == "unlet"
-		    let match = MATCH(0, thrmsg, 'E108', "No such variable")
-		endif
-		if match					" normal error
-		    Xloop 4			" X: 0
-		endif
-		if !MATCH(1, thrmsg, 'E488', "Trailing characters")
-		\|| v:errmsg != ""
-								" syntax error
-		    Xloop 8			" X: 0
-		endif
-	    endif
-	    if !caught && !MATCH(1, v:errmsg, 'E488', "Trailing characters")
-								" last error
-		Xloop 16			" X: 0
-	    endif
-	    if cmd == "let"
-		let cmd = "unlet"
-	    else
-		let cmd = ""
-	    endif
-	    XloopNEXT
-	    continue			" discard error if $VIMNOERRTHROW == 1
-	endtry
-    endwhile
-
-    Xpath 1048576				" X: 1048576
-    let cmd = "let"
-    XloopINIT 2097152 32
-    while cmd != ""
-	try
-	    let v:errmsg = ""
-	    let caught = 0
-	    let thrmsg = ""
-	    call EXEC(cmd . ' {novar}')		" normal plus syntax error
-	catch /^Vim\((\a\+)\)\=:/
-	    let caught = 1
-	    let thrmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
-	finally
-	    Xloop 1				" X: 2097152 * (1 + 32)
-	    if !caught
-		if !$VIMNOERRTHROW
-		    Xloop 2			" X: 0
-		endif
-	    else
-		if MATCH(0, thrmsg, 'E121', "Undefined variable") " normal error
-		    Xloop 4			" X: 0
-		endif
-		if !MATCH(1, thrmsg, 'E475', 'Invalid argument\>')
-		\ || v:errmsg != ""				  " syntax error
-		    Xloop 8			" X: 0
-		endif
-	    endif
-	    if !caught && !MATCH(1, v:errmsg, 'E475', 'Invalid argument\>')
-								" last error
-		Xloop 16			" X: 0
-	    endif
-	    if cmd == "let"
-		let cmd = "unlet"
-	    else
-		let cmd = ""
-	    endif
-	    XloopNEXT
-	    continue			" discard error if $VIMNOERRTHROW == 1
-	endtry
-    endwhile
-
-catch /.*/
-    " The Xpath command does not accept 2^31 (negative); add explicitly:
-    let Xpath = Xpath + 2147483648		" X: 0
-    Xout v:exception "in" v:throwpoint
-endtry
-
-unlet! next_command thrmsg match
-delfunction NEXT
-delfunction EXEC
-delfunction MATCH
-
-Xcheck 70288929
-
-
-"-------------------------------------------------------------------------------
-" Test 80:  Syntax error in expression for illegal :elseif		    {{{1
-"
-"	    If there is a syntax error in the expression after an illegal
-"	    :elseif, an error message is given (or an error exception thrown)
-"	    for the illegal :elseif rather than the expression error.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! MSG(enr, emsg)
-    let english = v:lang == "C" || v:lang =~ '^[Ee]n'
-    if a:enr == ""
-	Xout "TODO: Add message number for:" a:emsg
-	let v:errmsg = ":" . v:errmsg
-    endif
-    let match = 1
-    if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg)
-	let match = 0
-	if v:errmsg == ""
-	    Xout "Message missing."
-	else
-	    let v:errmsg = escape(v:errmsg, '"')
-	    Xout "Unexpected message:" v:errmsg
-	endif
-    endif
-    return match
-endfunction
-
-let v:errmsg = ""
-if 0
-else
-elseif 1 ||| 2
-endif
-Xpath 1						" X: 1
-if !MSG('E584', ":elseif after :else")
-    Xpath 2					" X: 0
-endif
-
-let v:errmsg = ""
-if 1
-else
-elseif 1 ||| 2
-endif
-Xpath 4						" X: 4
-if !MSG('E584', ":elseif after :else")
-    Xpath 8					" X: 0
-endif
-
-let v:errmsg = ""
-elseif 1 ||| 2
-Xpath 16					" X: 16
-if !MSG('E582', ":elseif without :if")
-    Xpath 32					" X: 0
-endif
-
-let v:errmsg = ""
-while 1
-    elseif 1 ||| 2
-endwhile
-Xpath 64					" X: 64
-if !MSG('E582', ":elseif without :if")
-    Xpath 128					" X: 0
-endif
-
-while 1
-    try
-	try
-	    let v:errmsg = ""
-	    let caught = 0
-	    if 0
-	    else
-	    elseif 1 ||| 2
-	    endif
-	catch /^Vim\((\a\+)\)\=:/
-	    let caught = 1
-	    let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
-	finally
-	    Xpath 256				" X: 256
-	    if !caught && !$VIMNOERRTHROW
-		Xpath 512			" X: 0
-	    endif
-	    if !MSG('E584', ":elseif after :else")
-		Xpath 1024			" X: 0
-	    endif
-	endtry
-    catch /.*/
-	Xpath 2048				" X: 0
-	Xout v:exception "in" v:throwpoint
-    finally
-	break		" discard error for $VIMNOERRTHROW
-    endtry
-endwhile
-
-while 1
-    try
-	try
-	    let v:errmsg = ""
-	    let caught = 0
-	    if 1
-	    else
-	    elseif 1 ||| 2
-	    endif
-	catch /^Vim\((\a\+)\)\=:/
-	    let caught = 1
-	    let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
-	finally
-	    Xpath 4096				" X: 4096
-	    if !caught && !$VIMNOERRTHROW
-		Xpath 8192			" X: 0
-	    endif
-	    if !MSG('E584', ":elseif after :else")
-		Xpath 16384			" X: 0
-	    endif
-	endtry
-    catch /.*/
-	Xpath 32768				" X: 0
-	Xout v:exception "in" v:throwpoint
-    finally
-	break		" discard error for $VIMNOERRTHROW
-    endtry
-endwhile
-
-while 1
-    try
-	try
-	    let v:errmsg = ""
-	    let caught = 0
-	    elseif 1 ||| 2
-	catch /^Vim\((\a\+)\)\=:/
-	    let caught = 1
-	    let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
-	finally
-	    Xpath 65536				" X: 65536
-	    if !caught && !$VIMNOERRTHROW
-		Xpath 131072			" X: 0
-	    endif
-	    if !MSG('E582', ":elseif without :if")
-		Xpath 262144			" X: 0
-	    endif
-	endtry
-    catch /.*/
-	Xpath 524288				" X: 0
-	Xout v:exception "in" v:throwpoint
-    finally
-	break		" discard error for $VIMNOERRTHROW
-    endtry
-endwhile
-
-while 1
-    try
-	try
-	    let v:errmsg = ""
-	    let caught = 0
-	    while 1
-		elseif 1 ||| 2
-	    endwhile
-	catch /^Vim\((\a\+)\)\=:/
-	    let caught = 1
-	    let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
-	finally
-	    Xpath 1048576			" X: 1048576
-	    if !caught && !$VIMNOERRTHROW
-		Xpath 2097152			" X: 0
-	    endif
-	    if !MSG('E582', ":elseif without :if")
-		Xpath 4194304			" X: 0
-	    endif
-	endtry
-    catch /.*/
-	Xpath 8388608				" X: 0
-	Xout v:exception "in" v:throwpoint
-    finally
-	break		" discard error for $VIMNOERRTHROW
-    endtry
-endwhile
-
-Xpath 16777216					" X: 16777216
-
-unlet! caught
-delfunction MSG
-
-Xcheck 17895765
-
-
-"-------------------------------------------------------------------------------
-" Test 81:  Discarding exceptions after an error or interrupt		    {{{1
-"
-"	    When an exception is thrown from inside a :try conditional without
-"	    :catch and :finally clauses and an error or interrupt occurs before
-"	    the :endtry is reached, the exception is discarded.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-    try
-	Xpath 1					" X: 1
-	try
-	    Xpath 2				" X: 2
-	    throw "arrgh"
-	    Xpath 4				" X: 0
-"	    if 1
-		Xpath 8				" X: 0
-	    " error after :throw: missing :endif
-	endtry
-	Xpath 16				" X: 0
-    catch /arrgh/
-	Xpath 32				" X: 0
-    endtry
-    Xpath 64					" X: 0
-endif
-
-if ExtraVim()
-    try
-	Xpath 128				" X: 128
-	try
-	    Xpath 256				" X: 256
-	    throw "arrgh"
-	    Xpath 512				" X: 0
-	endtry		" INTERRUPT
-	Xpath 1024				" X: 0
-    catch /arrgh/
-	Xpath 2048				" X: 0
-    endtry
-    Xpath 4096					" X: 0
-endif
-
-Xcheck 387
-
+" Following tests were moved to test_vimscript.vim:
+"     1-24, 27-31, 34-40, 49-50, 52-68, 76-81, 87
+" Following tests were moved to test_trycatch.vim:
+"     25-26, 32-33, 41-48, 51, 69-75
+let Xtest = 82
 
 "-------------------------------------------------------------------------------
 " Test 82:  Ignoring :catch clauses after an error or interrupt		    {{{1
diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim
index 5e112e05f9..8faa9135e5 100644
--- a/src/nvim/testdir/test_vimscript.vim
+++ b/src/nvim/testdir/test_vimscript.vim
@@ -23,6 +23,7 @@ com! -nargs=1	     Xout     call Xout()
 " file. If the test passes successfully, then Xtest.out should be empty.
 func RunInNewVim(test, verify)
   let init =<< trim END
+    set cpo-=C            " support line-continuation in sourced script
     source script_util.vim
     XpathINIT
     XloopINIT
@@ -3719,6 +3720,381 @@ func Test_execption_info_for_error()
   call RunInNewVim(test, verify)
 endfunc
 
+"-------------------------------------------------------------------------------
+"
+" Test 59:  v:exception and v:throwpoint when discarding exceptions	    {{{1
+"
+"	    When a :catch clause is left by a ":break" etc or an error or
+"	    interrupt exception, v:exception and v:throwpoint are reset.  They
+"	    are not affected by an exception that is discarded before being
+"	    caught.
+"-------------------------------------------------------------------------------
+func Test_exception_info_on_discard()
+  CheckEnglish
+
+  let test =<< trim [CODE]
+    let sfile = expand("")
+
+    while 1
+      try
+        throw "x1"
+      catch /.*/
+        break
+      endtry
+    endwhile
+    call assert_equal('', v:exception)
+    call assert_equal('', v:throwpoint)
+
+    while 1
+      try
+        throw "x2"
+      catch /.*/
+        break
+      finally
+        call assert_equal('', v:exception)
+        call assert_equal('', v:throwpoint)
+      endtry
+      break
+    endwhile
+    call assert_equal('', v:exception)
+    call assert_equal('', v:throwpoint)
+
+    while 1
+      try
+        let errcaught = 0
+        try
+          try
+            throw "x3"
+          catch /.*/
+            let lnum = expand("")
+            asdf
+          endtry
+        catch /.*/
+          let errcaught = 1
+          call assert_match('Vim:E492: Not an editor command:', v:exception)
+          call assert_match('line ' .. (lnum + 1), v:throwpoint)
+        endtry
+      finally
+        call assert_equal(1, errcaught)
+        break
+      endtry
+    endwhile
+    call assert_equal('', v:exception)
+    call assert_equal('', v:throwpoint)
+
+    Xpath 'a'
+
+    while 1
+      try
+        let intcaught = 0
+        try
+          try
+            throw "x4"
+          catch /.*/
+            let lnum = expand("")
+            call interrupt()
+          endtry
+        catch /.*/
+          let intcaught = 1
+          call assert_match('Vim:Interrupt', v:exception)
+          call assert_match('line ' .. (lnum + 1), v:throwpoint)
+        endtry
+      finally
+        call assert_equal(1, intcaught)
+        break
+      endtry
+    endwhile
+    call assert_equal('', v:exception)
+    call assert_equal('', v:throwpoint)
+
+    Xpath 'b'
+
+    while 1
+      try
+        let errcaught = 0
+        try
+          try
+            if 1
+              let lnum = expand("")
+              throw "x5"
+            " missing endif
+          catch /.*/
+            call assert_report('should not get here')
+          endtry
+        catch /.*/
+          let errcaught = 1
+          call assert_match('Vim(catch):E171: Missing :endif:', v:exception)
+          call assert_match('line ' .. (lnum + 3), v:throwpoint)
+        endtry
+      finally
+        call assert_equal(1, errcaught)
+        break
+      endtry
+    endwhile
+    call assert_equal('', v:exception)
+    call assert_equal('', v:throwpoint)
+
+    Xpath 'c'
+
+    try
+      while 1
+        try
+          throw "x6"
+        finally
+          break
+        endtry
+        break
+      endwhile
+    catch /.*/
+      call assert_report('should not get here')
+    endtry
+    call assert_equal('', v:exception)
+    call assert_equal('', v:throwpoint)
+
+    try
+      while 1
+        try
+          throw "x7"
+        finally
+          break
+        endtry
+        break
+      endwhile
+    catch /.*/
+      call assert_report('should not get here')
+    finally
+      call assert_equal('', v:exception)
+      call assert_equal('', v:throwpoint)
+    endtry
+    call assert_equal('', v:exception)
+    call assert_equal('', v:throwpoint)
+
+    while 1
+      try
+        let errcaught = 0
+        try
+          try
+            throw "x8"
+          finally
+            let lnum = expand("")
+            asdf
+          endtry
+        catch /.*/
+          let errcaught = 1
+          call assert_match('Vim:E492: Not an editor command:', v:exception)
+          call assert_match('line ' .. (lnum + 1), v:throwpoint)
+        endtry
+      finally
+        call assert_equal(1, errcaught)
+        break
+      endtry
+    endwhile
+    call assert_equal('', v:exception)
+    call assert_equal('', v:throwpoint)
+
+    Xpath 'd'
+
+    while 1
+      try
+        let intcaught = 0
+        try
+          try
+            throw "x9"
+          finally
+            let lnum = expand("")
+            call interrupt()
+          endtry
+        catch /.*/
+          let intcaught = 1
+          call assert_match('Vim:Interrupt', v:exception)
+          call assert_match('line ' .. (lnum + 1), v:throwpoint)
+        endtry
+      finally
+        call assert_equal(1, intcaught)
+        break
+      endtry
+    endwhile
+    call assert_equal('', v:exception)
+    call assert_equal('', v:throwpoint)
+
+    Xpath 'e'
+
+    while 1
+      try
+        let errcaught = 0
+        try
+          try
+            if 1
+              let lnum = expand("")
+              throw "x10"
+            " missing endif
+          finally
+            call assert_equal('', v:exception)
+            call assert_equal('', v:throwpoint)
+          endtry
+        catch /.*/
+          let errcaught = 1
+          call assert_match('Vim(finally):E171: Missing :endif:', v:exception)
+          call assert_match('line ' .. (lnum + 3), v:throwpoint)
+        endtry
+      finally
+        call assert_equal(1, errcaught)
+        break
+      endtry
+    endwhile
+    call assert_equal('', v:exception)
+    call assert_equal('', v:throwpoint)
+
+    Xpath 'f'
+
+    while 1
+      try
+        let errcaught = 0
+        try
+          try
+            if 1
+              let lnum = expand("")
+              throw "x11"
+            " missing endif
+          endtry
+        catch /.*/
+          let errcaught = 1
+          call assert_match('Vim(endtry):E171: Missing :endif:', v:exception)
+          call assert_match('line ' .. (lnum + 3), v:throwpoint)
+        endtry
+      finally
+        call assert_equal(1, errcaught)
+        break
+      endtry
+    endwhile
+    call assert_equal('', v:exception)
+    call assert_equal('', v:throwpoint)
+
+    Xpath 'g'
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abcdefg', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+"
+" Test 60:  (Re)throwing v:exception; :echoerr.				    {{{1
+"
+"	    A user exception can be rethrown after catching by throwing
+"	    v:exception.  An error or interrupt exception cannot be rethrown
+"	    because Vim exceptions cannot be faked.  A Vim exception using the
+"	    value of v:exception can, however, be triggered by the :echoerr
+"	    command.
+"-------------------------------------------------------------------------------
+
+func Test_rethrow_exception_1()
+  XpathINIT
+  try
+    try
+      Xpath 'a'
+      throw "oops"
+    catch /oops/
+      Xpath 'b'
+      throw v:exception	" rethrow user exception
+    catch /.*/
+      call assert_report('should not get here')
+    endtry
+  catch /^oops$/			" catches rethrown user exception
+    Xpath 'c'
+  catch /.*/
+    call assert_report('should not get here')
+  endtry
+  call assert_equal('abc', g:Xpath)
+endfunc
+
+func Test_rethrow_exception_2()
+  XpathINIT
+  try
+    let caught = 0
+    try
+      Xpath 'a'
+      write /n/o/n/w/r/i/t/a/b/l/e/_/f/i/l/e
+      call assert_report('should not get here')
+    catch /^Vim(write):/
+      let caught = 1
+      throw v:exception	" throw error: cannot fake Vim exception
+    catch /.*/
+      call assert_report('should not get here')
+    finally
+      Xpath 'b'
+      call assert_equal(1, caught)
+    endtry
+  catch /^Vim(throw):/	" catches throw error
+    let caught = caught + 1
+  catch /.*/
+    call assert_report('should not get here')
+  finally
+    Xpath 'c'
+    call assert_equal(2, caught)
+  endtry
+  call assert_equal('abc', g:Xpath)
+endfunc
+
+func Test_rethrow_exception_3()
+  XpathINIT
+  try
+    let caught = 0
+    try
+      Xpath 'a'
+      asdf
+    catch /^Vim/		" catch error exception
+      let caught = 1
+      " Trigger Vim error exception with value specified after :echoerr
+      let value = substitute(v:exception, '^Vim\((.*)\)\=:', '', "")
+      echoerr value
+    catch /.*/
+      call assert_report('should not get here')
+    finally
+      Xpath 'b'
+      call assert_equal(1, caught)
+    endtry
+  catch /^Vim(echoerr):/
+    let caught = caught + 1
+    call assert_match(value, v:exception)
+  catch /.*/
+    call assert_report('should not get here')
+  finally
+    Xpath 'c'
+    call assert_equal(2, caught)
+  endtry
+  call assert_equal('abc', g:Xpath)
+endfunc
+
+func Test_rethrow_exception_3()
+  XpathINIT
+  try
+    let errcaught = 0
+    try
+      Xpath 'a'
+      let intcaught = 0
+      call interrupt()
+    catch /^Vim:/		" catch interrupt exception
+      let intcaught = 1
+      " Trigger Vim error exception with value specified after :echoerr
+      echoerr substitute(v:exception, '^Vim\((.*)\)\=:', '', "")
+    catch /.*/
+      call assert_report('should not get here')
+    finally
+      Xpath 'b'
+      call assert_equal(1, intcaught)
+    endtry
+  catch /^Vim(echoerr):/
+    let errcaught = 1
+    call assert_match('Interrupt', v:exception)
+  finally
+    Xpath 'c'
+    call assert_equal(1, errcaught)
+  endtry
+  call assert_equal('abc', g:Xpath)
+endfunc
+
 "-------------------------------------------------------------------------------
 " Test 61:  Catching interrupt exceptions				    {{{1
 "
@@ -3846,68 +4222,1673 @@ func Test_catch_intr_exception()
 endfunc
 
 "-------------------------------------------------------------------------------
-" Test 65:  Errors in the /pattern/ argument of a :catch		    {{{1
+" Test 62:  Catching error exceptions					    {{{1
 "
-"	    On an error in the /pattern/ argument of a :catch, the :catch does
-"	    not match.  Any following :catches of the same :try/:endtry don't
-"	    match either.  Finally clauses are executed.
+"	    An error inside a :try/:endtry region is converted to an exception
+"	    and can be caught.  The error exception has a "Vim(cmdname):" prefix
+"	    where cmdname is the name of the failing command, or a "Vim:" prefix
+"	    if no command name is known.  The "Vim" prefixes cannot be faked.
 "-------------------------------------------------------------------------------
 
-func Test_catch_pattern_error()
-  CheckEnglish
+func Test_catch_err_exception_1()
   XpathINIT
-
-  try
+  while 1
     try
-      Xpath 'a'
-      throw "oops"
-    catch /^oops$/
-      Xpath 'b'
-    catch /\)/		" not checked; exception has already been caught
+      try
+        let caught = 0
+        unlet novar
+      catch /^Vim(unlet):/
+        Xpath 'a'
+        let caught = 1
+        let v:errmsg = substitute(v:exception, '^Vim(unlet):', '', "")
+      finally
+        Xpath 'b'
+        call assert_equal(1, caught)
+        call assert_match('E108: No such variable: "novar"', v:errmsg)
+      endtry
+    catch /.*/
       call assert_report('should not get here')
-    endtry
-    Xpath 'c'
-  catch /.*/
+    finally
+      Xpath 'c'
+      break
+    endtry
     call assert_report('should not get here')
-  endtry
+  endwhile
+  call assert_equal('abc', g:Xpath)
+endfunc
+
+func Test_catch_err_exception_2()
+  XpathINIT
+  while 1
+    try
+      try
+        let caught = 0
+        throw novar			" error in :throw
+      catch /^Vim(throw):/
+        Xpath 'a'
+        let caught = 1
+        let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "")
+      finally
+        Xpath 'b'
+        call assert_equal(1, caught)
+        call assert_match('E121: Undefined variable: novar', v:errmsg)
+      endtry
+    catch /.*/
+      call assert_report('should not get here')
+    finally
+      Xpath 'c'
+      break
+    endtry
+    call assert_report('should not get here')
+  endwhile
+  call assert_equal('abc', g:Xpath)
+endfunc
+
+func Test_catch_err_exception_3()
+  XpathINIT
+  while 1
+    try
+      try
+        let caught = 0
+        throw "Vim:faked"		" error: cannot fake Vim exception
+      catch /^Vim(throw):/
+        Xpath 'a'
+        let caught = 1
+        let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "")
+      finally
+        Xpath 'b'
+        call assert_equal(1, caught)
+        call assert_match("E608: Cannot :throw exceptions with 'Vim' prefix",
+              \ v:errmsg)
+      endtry
+    catch /.*/
+      call assert_report('should not get here')
+    finally
+      Xpath 'c'
+      break
+    endtry
+    call assert_report('should not get here')
+  endwhile
+  call assert_equal('abc', g:Xpath)
+endfunc
+
+func Test_catch_err_exception_4()
+  XpathINIT
+  func F()
+    while 1
+    " Missing :endwhile
+  endfunc
+
+  while 1
+    try
+      try
+        let caught = 0
+        call F()
+      catch /^Vim(endfunction):/
+        Xpath 'a'
+        let caught = 1
+        let v:errmsg = substitute(v:exception, '^Vim(endfunction):', '', "")
+      finally
+        Xpath 'b'
+        call assert_equal(1, caught)
+        call assert_match("E170: Missing :endwhile", v:errmsg)
+      endtry
+    catch /.*/
+      call assert_report('should not get here')
+    finally
+      Xpath 'c'
+      break
+    endtry
+    call assert_report('should not get here')
+  endwhile
   call assert_equal('abc', g:Xpath)
+  delfunc F
+endfunc
 
+func Test_catch_err_exception_5()
   XpathINIT
   func F()
+    while 1
+    " Missing :endwhile
+  endfunc
+
+  while 1
+    try
+      try
+        let caught = 0
+        ExecAsScript F
+      catch /^Vim:/
+        Xpath 'a'
+        let caught = 1
+        let v:errmsg = substitute(v:exception, '^Vim:', '', "")
+      finally
+        Xpath 'b'
+        call assert_equal(1, caught)
+        call assert_match("E170: Missing :endwhile", v:errmsg)
+      endtry
+    catch /.*/
+      call assert_report('should not get here')
+    finally
+      Xpath 'c'
+      break
+    endtry
+    call assert_report('should not get here')
+  endwhile
+  call assert_equal('abc', g:Xpath)
+  delfunc F
+endfunc
+
+func Test_catch_err_exception_6()
+  XpathINIT
+  func G()
+    call G()
+  endfunc
+
+  while 1
+    try
+      let mfd_save = &mfd
+      set mfd=3
+      try
+        let caught = 0
+        call G()
+      catch /^Vim(call):/
+        Xpath 'a'
+        let caught = 1
+        let v:errmsg = substitute(v:exception, '^Vim(call):', '', "")
+      finally
+        Xpath 'b'
+        call assert_equal(1, caught)
+        call assert_match("E132: Function call depth is higher than 'maxfuncdepth'", v:errmsg)
+      endtry
+    catch /.*/
+      call assert_report('should not get here')
+    finally
+      Xpath 'c'
+      let &mfd = mfd_save
+      break
+    endtry
+    call assert_report('should not get here')
+  endwhile
+  call assert_equal('abc', g:Xpath)
+  delfunc G
+endfunc
+
+func Test_catch_err_exception_7()
+  XpathINIT
+  func H()
+    return H()
+  endfunc
+
+  while 1
+    try
+      let mfd_save = &mfd
+      set mfd=3
+      try
+        let caught = 0
+        call H()
+      catch /^Vim(return):/
+        Xpath 'a'
+        let caught = 1
+        let v:errmsg = substitute(v:exception, '^Vim(return):', '', "")
+      finally
+        Xpath 'b'
+        call assert_equal(1, caught)
+        call assert_match("E132: Function call depth is higher than 'maxfuncdepth'", v:errmsg)
+      endtry
+    catch /.*/
+      call assert_report('should not get here')
+    finally
+      Xpath 'c'
+      let &mfd = mfd_save
+      break		" discard error for $VIMNOERRTHROW
+    endtry
+    call assert_report('should not get here')
+  endwhile
+
+  call assert_equal('abc', g:Xpath)
+  delfunc H
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 63:  Suppressing error exceptions by :silent!.			    {{{1
+"
+"	    A :silent! command inside a :try/:endtry region suppresses the
+"	    conversion of errors to an exception and the immediate abortion on
+"	    error.  When the commands executed by the :silent! themselves open
+"	    a new :try/:endtry region, conversion of errors to exception and
+"	    immediate abortion is switched on again - until the next :silent!
+"	    etc.  The :silent! has the effect of setting v:errmsg to the error
+"	    message text (without displaying it) and continuing with the next
+"	    script line.
+"
+"	    When a command triggering autocommands is executed by :silent!
+"	    inside a :try/:endtry, the autocommand execution is not suppressed
+"	    on error.
+"
+"	    This test reuses the function MSG() from the previous test.
+"-------------------------------------------------------------------------------
+
+func Test_silent_exception()
+  XpathINIT
+  XloopINIT
+  let g:taken = ""
+
+  func S(n) abort
+    XloopNEXT
+    let g:taken = g:taken . "E" . a:n
+    let v:errmsg = ""
+    exec "asdf" . a:n
+
+    " Check that ":silent!" continues:
+    Xloop 'a'
+
+    " Check that ":silent!" sets "v:errmsg":
+    call assert_match("E492: Not an editor command", v:errmsg)
+  endfunc
+
+  func Foo()
+    while 1
+      try
+        try
+          let caught = 0
+          " This is not silent:
+          call S(3)
+        catch /^Vim:/
+          Xpath 'b'
+          let caught = 1
+          let errmsg3 = substitute(v:exception, '^Vim:', '', "")
+          silent! call S(4)
+        finally
+          call assert_equal(1, caught)
+          Xpath 'c'
+          call assert_match("E492: Not an editor command", errmsg3)
+          silent! call S(5)
+          " Break out of try conditionals that cover ":silent!".  This also
+          " discards the aborting error when $VIMNOERRTHROW is non-zero.
+          break
+        endtry
+      catch /.*/
+        call assert_report('should not get here')
+      endtry
+    endwhile
+    " This is a double ":silent!" (see caller).
+    silent! call S(6)
+  endfunc
+
+  func Bar()
+    try
+      silent! call S(2)
+      silent! execute "call Foo() | call S(7)"
+      silent! call S(8)
+    endtry	" normal end of try cond that covers ":silent!"
+    " This has a ":silent!" from the caller:
+    call S(9)
+  endfunc
+
+  silent! call S(1)
+  silent! call Bar()
+  silent! call S(10)
+
+  call assert_equal("E1E2E3E4E5E6E7E8E9E10", g:taken)
+
+  augroup TMP
+    au!
+    autocmd BufWritePost * Xpath 'd'
+  augroup END
+
+  Xpath 'e'
+  silent! write /i/m/p/o/s/s/i/b/l/e
+  Xpath 'f'
+
+  call assert_equal('a2a3ba5ca6a7a8a9a10a11edf', g:Xpath)
+
+  augroup TMP
+    au!
+  augroup END
+  augroup! TMP
+  delfunction S
+  delfunction Foo
+  delfunction Bar
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 64:  Error exceptions after error, interrupt or :throw		    {{{1
+"
+"	    When an error occurs after an interrupt or a :throw but before
+"	    a matching :catch is reached, all following :catches of that try
+"	    block are ignored, but the error exception can be caught by the next
+"	    surrounding try conditional.  Any previous error exception is
+"	    discarded.  An error is ignored when there is a previous error that
+"	    has not been caught.
+"-------------------------------------------------------------------------------
+
+func Test_exception_after_error_1()
+  XpathINIT
+  while 1
+    try
+      try
+        Xpath 'a'
+        let caught = 0
+        while 1
+          if 1
+          " Missing :endif
+        endwhile	" throw error exception
+      catch /^Vim(/
+        Xpath 'b'
+        let caught = 1
+      finally
+        Xpath 'c'
+        call assert_equal(1, caught)
+      endtry
+    catch /.*/
+      call assert_report('should not get here')
+    finally
+      Xpath 'd'
+      break
+    endtry
+    call assert_report('should not get here')
+  endwhile
+  call assert_equal('abcd', g:Xpath)
+endfunc
+
+func Test_exception_after_error_2()
+  XpathINIT
+  while 1
+    try
+      try
+        Xpath 'a'
+        let caught = 0
+        try
+          if 1
+          " Missing :endif
+        catch /.*/	" throw error exception
+          call assert_report('should not get here')
+        catch /.*/
+          call assert_report('should not get here')
+        endtry
+      catch /^Vim(/
+        Xpath 'b'
+        let caught = 1
+      finally
+        Xpath 'c'
+        call assert_equal(1, caught)
+      endtry
+    catch /.*/
+      call assert_report('should not get here')
+    finally
+      Xpath 'd'
+      break
+    endtry
+    call assert_report('should not get here')
+  endwhile
+  call assert_equal('abcd', g:Xpath)
+endfunc
+
+func Test_exception_after_error_3()
+  XpathINIT
+  while 1
     try
       try
+        let caught = 0
         try
           Xpath 'a'
-          throw "ab"
-        catch /abc/	" does not catch
+          call interrupt()
+        catch /do_not_catch/
           call assert_report('should not get here')
-        catch /\)/	" error; discards exception
+          if 1
+          " Missing :endif
+        catch /.*/	" throw error exception
           call assert_report('should not get here')
-        catch /.*/	" not checked
+        catch /.*/
           call assert_report('should not get here')
-        finally
-          Xpath 'b'
         endtry
-        call assert_report('should not get here')
-      catch /^ab$/	" checked, but original exception is discarded
-        call assert_report('should not get here')
-      catch /^Vim(catch):/
-        Xpath 'c'
-        call assert_match('Vim(catch):E475: Invalid argument:', v:exception)
+      catch /^Vim(/
+        Xpath 'b'
+        let caught = 1
       finally
-        Xpath 'd'
+        Xpath 'c'
+        call assert_equal(1, caught)
       endtry
-      Xpath 'e'
     catch /.*/
       call assert_report('should not get here')
+    finally
+      Xpath 'd'
+      break
     endtry
-    Xpath 'f'
-  endfunc
-
-  call F()
-  call assert_equal('abcdef', g:Xpath)
+    call assert_report('should not get here')
+  endwhile
+  call assert_equal('abcd', g:Xpath)
+endfunc
 
-  delfunc F
+func Test_exception_after_error_4()
+  XpathINIT
+  while 1
+    try
+      try
+        let caught = 0
+        try
+          Xpath 'a'
+          throw "x"
+        catch /do_not_catch/
+          call assert_report('should not get here')
+          if 1
+          " Missing :endif
+        catch /x/	" throw error exception
+          call assert_report('should not get here')
+        catch /.*/
+          call assert_report('should not get here')
+        endtry
+      catch /^Vim(/
+        Xpath 'b'
+        let caught = 1
+      finally
+        Xpath 'c'
+        call assert_equal(1, caught)
+      endtry
+    catch /.*/
+      call assert_report('should not get here')
+    finally
+      Xpath 'd'
+      break
+    endtry
+    call assert_report('should not get here')
+  endwhile
+  call assert_equal('abcd', g:Xpath)
+endfunc
+
+func Test_exception_after_error_5()
+  XpathINIT
+  while 1
+    try
+      try
+        let caught = 0
+        Xpath 'a'
+        endif		" :endif without :if; throw error exception
+        if 1
+        " Missing :endif
+      catch /do_not_catch/ " ignore new error
+        call assert_report('should not get here')
+      catch /^Vim(endif):/
+        Xpath 'b'
+        let caught = 1
+      catch /^Vim(/
+        call assert_report('should not get here')
+      finally
+        Xpath 'c'
+        call assert_equal(1, caught)
+      endtry
+    catch /.*/
+      call assert_report('should not get here')
+    finally
+      Xpath 'd'
+      break
+    endtry
+    call assert_report('should not get here')
+  endwhile
+  call assert_equal('abcd', g:Xpath)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 65:  Errors in the /pattern/ argument of a :catch		    {{{1
+"
+"	    On an error in the /pattern/ argument of a :catch, the :catch does
+"	    not match.  Any following :catches of the same :try/:endtry don't
+"	    match either.  Finally clauses are executed.
+"-------------------------------------------------------------------------------
+
+func Test_catch_pattern_error()
+  CheckEnglish
+  XpathINIT
+
+  try
+    try
+      Xpath 'a'
+      throw "oops"
+    catch /^oops$/
+      Xpath 'b'
+    catch /\)/		" not checked; exception has already been caught
+      call assert_report('should not get here')
+    endtry
+    Xpath 'c'
+  catch /.*/
+    call assert_report('should not get here')
+  endtry
+  call assert_equal('abc', g:Xpath)
+
+  XpathINIT
+  func F()
+    try
+      try
+        try
+          Xpath 'a'
+          throw "ab"
+        catch /abc/	" does not catch
+          call assert_report('should not get here')
+        catch /\)/	" error; discards exception
+          call assert_report('should not get here')
+        catch /.*/	" not checked
+          call assert_report('should not get here')
+        finally
+          Xpath 'b'
+        endtry
+        call assert_report('should not get here')
+      catch /^ab$/	" checked, but original exception is discarded
+        call assert_report('should not get here')
+      catch /^Vim(catch):/
+        Xpath 'c'
+        call assert_match('Vim(catch):E475: Invalid argument:', v:exception)
+      finally
+        Xpath 'd'
+      endtry
+      Xpath 'e'
+    catch /.*/
+      call assert_report('should not get here')
+    endtry
+    Xpath 'f'
+  endfunc
+
+  call F()
+  call assert_equal('abcdef', g:Xpath)
+
+  delfunc F
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 66:  Stop range :call on error, interrupt, or :throw		    {{{1
+"
+"	    When a function which is multiply called for a range since it
+"	    doesn't handle the range itself has an error in a command
+"	    dynamically enclosed by :try/:endtry or gets an interrupt or
+"	    executes a :throw, no more calls for the remaining lines in the
+"	    range are made.  On an error in a command not dynamically enclosed
+"	    by :try/:endtry, the function is executed again for the remaining
+"	    lines in the range.
+"-------------------------------------------------------------------------------
+
+func Test_stop_range_on_error()
+  let test =<< trim [CODE]
+    let file = tempname()
+    exec "edit" file
+    call setline(1, ['line 1', 'line 2', 'line 3'])
+    let taken = ""
+    let expected = "G1EF1E(1)F1E(2)F1E(3)G2EF2E(1)G3IF3I(1)G4TF4T(1)G5AF5A(1)"
+
+    func F(reason, n) abort
+      let g:taken = g:taken .. "F" .. a:n ..
+                          \ substitute(a:reason, '\(\l\).*', '\u\1', "") ..
+                          \ "(" .. line(".") .. ")"
+
+      if a:reason == "error"
+        asdf
+      elseif a:reason == "interrupt"
+        call interrupt()
+      elseif a:reason == "throw"
+        throw "xyz"
+      elseif a:reason == "aborting error"
+        XloopNEXT
+        call assert_equal(g:taken, g:expected)
+        try
+          bwipeout!
+          call delete(g:file)
+          asdf
+        endtry
+      endif
+    endfunc
+
+    func G(reason, n)
+      let g:taken = g:taken .. "G" .. a:n ..
+                              \ substitute(a:reason, '\(\l\).*', '\u\1', "")
+      1,3call F(a:reason, a:n)
+    endfunc
+
+    Xpath 'a'
+    call G("error", 1)
+    try
+      Xpath 'b'
+      try
+        call G("error", 2)
+        call assert_report('should not get here')
+      finally
+        Xpath 'c'
+        try
+          call G("interrupt", 3)
+          call assert_report('should not get here')
+        finally
+          Xpath 'd'
+          try
+            call G("throw", 4)
+            call assert_report('should not get here')
+          endtry
+        endtry
+      endtry
+    catch /xyz/
+      Xpath 'e'
+    catch /.*/
+      call assert_report('should not get here')
+    endtry
+    Xpath 'f'
+    call G("aborting error", 5)
+    call assert_report('should not get here')
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abcdef', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 67:  :throw across :call command					    {{{1
+"
+"	    On a call command, an exception might be thrown when evaluating the
+"	    function name, during evaluation of the arguments, or when the
+"	    function is being executed.  The exception can be caught by the
+"	    caller.
+"-------------------------------------------------------------------------------
+
+func THROW(x, n)
+  if a:n == 1
+    Xpath 'A'
+  elseif a:n == 2
+    Xpath 'B'
+  elseif a:n == 3
+    Xpath 'C'
+  endif
+  throw a:x
+endfunc
+
+func NAME(x, n)
+  if a:n == 1
+    call assert_report('should not get here')
+  elseif a:n == 2
+    Xpath 'D'
+  elseif a:n == 3
+    Xpath 'E'
+  elseif a:n == 4
+    Xpath 'F'
+  endif
+  return a:x
+endfunc
+
+func ARG(x, n)
+  if a:n == 1
+    call assert_report('should not get here')
+  elseif a:n == 2
+    call assert_report('should not get here')
+  elseif a:n == 3
+    Xpath 'G'
+  elseif a:n == 4
+    Xpath 'I'
+  endif
+  return a:x
+endfunc
+
+func Test_throw_across_call_cmd()
+  XpathINIT
+
+  func F(x, n)
+    if a:n == 2
+      call assert_report('should not get here')
+    elseif a:n == 4
+      Xpath 'a'
+    endif
+  endfunc
+
+  while 1
+    try
+      let v:errmsg = ""
+
+      while 1
+        try
+          Xpath 'b'
+          call {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1)
+          call assert_report('should not get here')
+        catch /^name$/
+          Xpath 'c'
+        catch /.*/
+          call assert_report('should not get here')
+        finally
+          call assert_equal("", v:errmsg)
+          let v:errmsg = ""
+          break
+        endtry
+      endwhile
+
+      while 1
+        try
+          Xpath 'd'
+          call {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2)
+          call assert_report('should not get here')
+        catch /^arg$/
+          Xpath 'e'
+        catch /.*/
+          call assert_report('should not get here')
+        finally
+          call assert_equal("", v:errmsg)
+          let v:errmsg = ""
+          break
+        endtry
+      endwhile
+
+      while 1
+        try
+          Xpath 'f'
+          call {NAME("THROW", 3)}(ARG("call", 3), 3)
+          call assert_report('should not get here')
+        catch /^call$/
+          Xpath 'g'
+        catch /^0$/	    " default return value
+          call assert_report('should not get here')
+        catch /.*/
+          call assert_report('should not get here')
+        finally
+          call assert_equal("", v:errmsg)
+          let v:errmsg = ""
+          break
+        endtry
+      endwhile
+
+      while 1
+        try
+          Xpath 'h'
+          call {NAME("F", 4)}(ARG(4711, 4), 4)
+          Xpath 'i'
+        catch /.*/
+          call assert_report('should not get here')
+        finally
+          call assert_equal("", v:errmsg)
+          let v:errmsg = ""
+          break
+        endtry
+      endwhile
+
+    catch /^0$/	    " default return value
+      call assert_report('should not get here')
+    catch /.*/
+      call assert_report('should not get here')
+    finally
+      call assert_equal("", v:errmsg)
+      let v:errmsg = ""
+      break
+    endtry
+  endwhile
+
+  call assert_equal('bAcdDBefEGCghFIai', g:Xpath)
+  delfunction F
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 68:  :throw across function calls in expressions			    {{{1
+"
+"	    On a function call within an expression, an exception might be
+"	    thrown when evaluating the function name, during evaluation of the
+"	    arguments, or when the function is being executed.  The exception
+"	    can be caught by the caller.
+"
+"	    This test reuses the functions THROW(), NAME(), and ARG() from the
+"	    previous test.
+"-------------------------------------------------------------------------------
+
+func Test_throw_across_call_expr()
+  XpathINIT
+
+  func F(x, n)
+    if a:n == 2
+      call assert_report('should not get here')
+    elseif a:n == 4
+      Xpath 'a'
+    endif
+    return a:x
+  endfunction
+
+  while 1
+    try
+      let error = 0
+      let v:errmsg = ""
+
+      while 1
+        try
+          Xpath 'b'
+          let var1 = {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1)
+          call assert_report('should not get here')
+        catch /^name$/
+          Xpath 'c'
+        catch /.*/
+          call assert_report('should not get here')
+        finally
+          call assert_equal("", v:errmsg)
+          let v:errmsg = ""
+          break
+        endtry
+      endwhile
+      call assert_true(!exists('var1'))
+
+      while 1
+        try
+          Xpath 'd'
+          let var2 = {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2)
+          call assert_report('should not get here')
+        catch /^arg$/
+          Xpath 'e'
+        catch /.*/
+          call assert_report('should not get here')
+        finally
+          call assert_equal("", v:errmsg)
+          let v:errmsg = ""
+          break
+        endtry
+      endwhile
+      call assert_true(!exists('var2'))
+
+      while 1
+        try
+          Xpath 'f'
+          let var3 = {NAME("THROW", 3)}(ARG("call", 3), 3)
+          call assert_report('should not get here')
+        catch /^call$/
+          Xpath 'g'
+        catch /^0$/	    " default return value
+          call assert_report('should not get here')
+        catch /.*/
+          call assert_report('should not get here')
+        finally
+          call assert_equal("", v:errmsg)
+          let v:errmsg = ""
+          break
+        endtry
+      endwhile
+      call assert_true(!exists('var3'))
+
+      while 1
+        try
+          Xpath 'h'
+          let var4 = {NAME("F", 4)}(ARG(4711, 4), 4)
+          Xpath 'i'
+        catch /.*/
+          call assert_report('should not get here')
+        finally
+          call assert_equal("", v:errmsg)
+          let v:errmsg = ""
+          break
+        endtry
+      endwhile
+      call assert_true(exists('var4') && var4 == 4711)
+
+    catch /^0$/	    " default return value
+      call assert_report('should not get here')
+    catch /.*/
+      call assert_report('should not get here')
+    finally
+      call assert_equal("", v:errmsg)
+      break
+    endtry
+  endwhile
+
+  call assert_equal('bAcdDBefEGCghFIai', g:Xpath)
+  delfunc F
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 76:  Errors, interrupts, :throw during expression evaluation	    {{{1
+"
+"	    When a function call made during expression evaluation is aborted
+"	    due to an error inside a :try/:endtry region or due to an interrupt
+"	    or a :throw, the expression evaluation is aborted as well.	No
+"	    message is displayed for the cancelled expression evaluation.  On an
+"	    error not inside :try/:endtry, the expression evaluation continues.
+"-------------------------------------------------------------------------------
+
+func Test_expr_eval_error()
+  let test =<< trim [CODE]
+    let taken = ""
+
+    func ERR(n)
+      let g:taken = g:taken .. "E" .. a:n
+      asdf
+    endfunc
+
+    func ERRabort(n) abort
+      let g:taken = g:taken .. "A" .. a:n
+      asdf
+    endfunc	" returns -1; may cause follow-up msg for illegal var/func name
+
+    func WRAP(n, arg)
+      let g:taken = g:taken .. "W" .. a:n
+      let g:saved_errmsg = v:errmsg
+      return arg
+    endfunc
+
+    func INT(n)
+      let g:taken = g:taken .. "I" .. a:n
+      call interrupt()
+    endfunc
+
+    func THR(n)
+      let g:taken = g:taken .. "T" .. a:n
+      throw "should not be caught"
+    endfunc
+
+    func CONT(n)
+      let g:taken = g:taken .. "C" .. a:n
+    endfunc
+
+    func MSG(n)
+      let g:taken = g:taken .. "M" .. a:n
+      let errmsg = (a:n >= 37 && a:n <= 44) ? g:saved_errmsg : v:errmsg
+      let msgptn = (a:n >= 10 && a:n <= 27) ? "^$" : "asdf"
+      call assert_match(msgptn, errmsg)
+      let v:errmsg = ""
+      let g:saved_errmsg = ""
+    endfunc
+
+    let v:errmsg = ""
+
+    try
+      let t = 1
+      while t <= 9
+        Xloop 'a'
+        try
+          if t == 1
+            let v{ERR(t) + CONT(t)} = 0
+          elseif t == 2
+            let v{ERR(t) + CONT(t)}
+          elseif t == 3
+            let var = exists('v{ERR(t) + CONT(t)}')
+          elseif t == 4
+            unlet v{ERR(t) + CONT(t)}
+          elseif t == 5
+            function F{ERR(t) + CONT(t)}()
+            endfunction
+          elseif t == 6
+            function F{ERR(t) + CONT(t)}
+          elseif t == 7
+            let var = exists('*F{ERR(t) + CONT(t)}')
+          elseif t == 8
+            delfunction F{ERR(t) + CONT(t)}
+          elseif t == 9
+            let var = ERR(t) + CONT(t)
+          endif
+        catch /asdf/
+          " v:errmsg is not set when the error message is converted to an
+          " exception.  Set it to the original error message.
+          let v:errmsg = substitute(v:exception, '^Vim:', '', "")
+        catch /^Vim\((\a\+)\)\=:/
+          " An error exception has been thrown after the original error.
+          let v:errmsg = ""
+        finally
+          call MSG(t)
+          let t = t + 1
+          XloopNEXT
+          continue	" discard an aborting error
+        endtry
+      endwhile
+    catch /.*/
+      call assert_report('should not get here')
+    endtry
+
+    try
+      let t = 10
+      while t <= 18
+        Xloop 'b'
+        try
+          if t == 10
+            let v{INT(t) + CONT(t)} = 0
+          elseif t == 11
+            let v{INT(t) + CONT(t)}
+          elseif t == 12
+            let var = exists('v{INT(t) + CONT(t)}')
+          elseif t == 13
+            unlet v{INT(t) + CONT(t)}
+          elseif t == 14
+            function F{INT(t) + CONT(t)}()
+            endfunction
+          elseif t == 15
+            function F{INT(t) + CONT(t)}
+          elseif t == 16
+            let var = exists('*F{INT(t) + CONT(t)}')
+          elseif t == 17
+            delfunction F{INT(t) + CONT(t)}
+          elseif t == 18
+            let var = INT(t) + CONT(t)
+          endif
+        catch /^Vim\((\a\+)\)\=:\(Interrupt\)\@!/
+          " An error exception has been triggered after the interrupt.
+          let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
+        finally
+          call MSG(t)
+          let t = t + 1
+          XloopNEXT
+          continue	" discard interrupt
+        endtry
+      endwhile
+    catch /.*/
+      call assert_report('should not get here')
+    endtry
+
+    try
+      let t = 19
+      while t <= 27
+        Xloop 'c'
+        try
+          if t == 19
+            let v{THR(t) + CONT(t)} = 0
+          elseif t == 20
+            let v{THR(t) + CONT(t)}
+          elseif t == 21
+            let var = exists('v{THR(t) + CONT(t)}')
+          elseif t == 22
+            unlet v{THR(t) + CONT(t)}
+          elseif t == 23
+            function F{THR(t) + CONT(t)}()
+            endfunction
+          elseif t == 24
+            function F{THR(t) + CONT(t)}
+          elseif t == 25
+            let var = exists('*F{THR(t) + CONT(t)}')
+          elseif t == 26
+            delfunction F{THR(t) + CONT(t)}
+          elseif t == 27
+            let var = THR(t) + CONT(t)
+          endif
+        catch /^Vim\((\a\+)\)\=:/
+          " An error exception has been triggered after the :throw.
+          let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
+        finally
+          call MSG(t)
+          let t = t + 1
+          XloopNEXT
+          continue	" discard exception
+        endtry
+      endwhile
+    catch /.*/
+      call assert_report('should not get here')
+    endtry
+
+    let v{ERR(28) + CONT(28)} = 0
+    call MSG(28)
+    let v{ERR(29) + CONT(29)}
+    call MSG(29)
+    let var = exists('v{ERR(30) + CONT(30)}')
+    call MSG(30)
+    unlet v{ERR(31) + CONT(31)}
+    call MSG(31)
+    function F{ERR(32) + CONT(32)}()
+    endfunction
+    call MSG(32)
+    function F{ERR(33) + CONT(33)}
+    call MSG(33)
+    let var = exists('*F{ERR(34) + CONT(34)}')
+    call MSG(34)
+    delfunction F{ERR(35) + CONT(35)}
+    call MSG(35)
+    let var = ERR(36) + CONT(36)
+    call MSG(36)
+
+    let saved_errmsg = ""
+
+    let v{WRAP(37, ERRabort(37)) + CONT(37)} = 0
+    call MSG(37)
+    let v{WRAP(38, ERRabort(38)) + CONT(38)}
+    call MSG(38)
+    let var = exists('v{WRAP(39, ERRabort(39)) + CONT(39)}')
+    call MSG(39)
+    unlet v{WRAP(40, ERRabort(40)) + CONT(40)}
+    call MSG(40)
+    function F{WRAP(41, ERRabort(41)) + CONT(41)}()
+    endfunction
+    call MSG(41)
+    function F{WRAP(42, ERRabort(42)) + CONT(42)}
+    call MSG(42)
+    let var = exists('*F{WRAP(43, ERRabort(43)) + CONT(43)}')
+    call MSG(43)
+    delfunction F{WRAP(44, ERRabort(44)) + CONT(44)}
+    call MSG(44)
+    let var = ERRabort(45) + CONT(45)
+    call MSG(45)
+    Xpath 'd'
+
+    let expected = ""
+          \ .. "E1M1E2M2E3M3E4M4E5M5E6M6E7M7E8M8E9M9"
+          \ .. "I10M10I11M11I12M12I13M13I14M14I15M15I16M16I17M17I18M18"
+          \ .. "T19M19T20M20T21M21T22M22T23M23T24M24T25M25T26M26T27M27"
+          \ .. "E28C28M28E29C29M29E30C30M30E31C31M31E32C32M32E33C33M33"
+          \ .. "E34C34M34E35C35M35E36C36M36"
+          \ .. "A37W37C37M37A38W38C38M38A39W39C39M39A40W40C40M40A41W41C41M41"
+          \ .. "A42W42C42M42A43W43C43M43A44W44C44M44A45C45M45"
+    call assert_equal(expected, taken)
+  [CODE]
+  let verify =<< trim [CODE]
+    let expected = "a1a2a3a4a5a6a7a8a9"
+                      \ .. "b10b11b12b13b14b15b16b17b18"
+                      \ .. "c19c20c21c22c23c24c25c26c27d"
+    call assert_equal(expected, g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 77:  Errors, interrupts, :throw in name{brace-expression}	    {{{1
+"
+"	    When a function call made during evaluation of an expression in
+"	    braces as part of a function name after ":function" is aborted due
+"	    to an error inside a :try/:endtry region or due to an interrupt or
+"	    a :throw, the expression evaluation is aborted as well, and the
+"	    function definition is ignored, skipping all commands to the
+"	    ":endfunction".  On an error not inside :try/:endtry, the expression
+"	    evaluation continues and the function gets defined, and can be
+"	    called and deleted.
+"-------------------------------------------------------------------------------
+func Test_brace_expr_error()
+  let test =<< trim [CODE]
+    func ERR() abort
+      Xloop 'a'
+      asdf
+    endfunc					" returns -1
+
+    func OK()
+      Xloop 'b'
+      let v:errmsg = ""
+      return 0
+    endfunc
+
+    let v:errmsg = ""
+
+    Xpath 'c'
+    func F{1 + ERR() + OK()}(arg)
+      " F0 should be defined.
+      if exists("a:arg") && a:arg == "calling"
+        Xpath 'd'
+      else
+        call assert_report('should not get here')
+      endif
+    endfunction
+    call assert_equal("", v:errmsg)
+    XloopNEXT
+
+    Xpath 'e'
+    call F{1 + ERR() + OK()}("calling")
+    call assert_equal("", v:errmsg)
+    XloopNEXT
+
+    Xpath 'f'
+    delfunction F{1 + ERR() + OK()}
+    call assert_equal("", v:errmsg)
+    XloopNEXT
+
+    try
+      while 1
+        try
+          Xpath 'g'
+          func G{1 + ERR() + OK()}(arg)
+            " G0 should not be defined, and the function body should be
+            " skipped.
+            call assert_report('should not get here')
+            " Use an unmatched ":finally" to check whether the body is
+            " skipped when an error occurs in ERR().  This works whether or
+            " not the exception is converted to an exception.
+            finally
+              call assert_report('should not get here')
+            endtry
+          try
+            call assert_report('should not get here')
+          endfunction
+
+          call assert_report('should not get here')
+        catch /asdf/
+          " Jumped to when the function is not defined and the body is
+          " skipped.
+          Xpath 'h'
+        catch /.*/
+          call assert_report('should not get here')
+        finally
+          Xpath 'i'
+          break
+        endtry			" jumped to when the body is not skipped
+      endwhile
+    catch /.*/
+      call assert_report('should not get here')
+    endtry
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('ca1b1ea2b2dfa3b3ga4hi', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 78:  Messages on parsing errors in expression evaluation		    {{{1
+"
+"	    When an expression evaluation detects a parsing error, an error
+"	    message is given and converted to an exception, and the expression
+"	    evaluation is aborted.
+"-------------------------------------------------------------------------------
+func Test_expr_eval_error_msg()
+  CheckEnglish
+
+  let test =<< trim [CODE]
+    let taken = ""
+
+    func F(n)
+      let g:taken = g:taken . "F" . a:n
+    endfunc
+
+    func MSG(n, enr, emsg)
+      let g:taken = g:taken . "M" . a:n
+      call assert_match('^' .. a:enr .. ':', v:errmsg)
+      call assert_match(a:emsg, v:errmsg)
+    endfunc
+
+    func CONT(n)
+      let g:taken = g:taken . "C" . a:n
+    endfunc
+
+    let v:errmsg = ""
+    try
+      let t = 1
+      while t <= 14
+        let g:taken = g:taken . "T" . t
+        let v:errmsg = ""
+        try
+          if t == 1
+            let v{novar + CONT(t)} = 0
+          elseif t == 2
+            let v{novar + CONT(t)}
+          elseif t == 3
+            let var = exists('v{novar + CONT(t)}')
+          elseif t == 4
+            unlet v{novar + CONT(t)}
+          elseif t == 5
+            function F{novar + CONT(t)}()
+            endfunction
+          elseif t == 6
+            function F{novar + CONT(t)}
+          elseif t == 7
+            let var = exists('*F{novar + CONT(t)}')
+          elseif t == 8
+            delfunction F{novar + CONT(t)}
+          elseif t == 9
+            echo novar + CONT(t)
+          elseif t == 10
+            echo v{novar + CONT(t)}
+          elseif t == 11
+            echo F{novar + CONT(t)}
+          elseif t == 12
+            let var = novar + CONT(t)
+          elseif t == 13
+            let var = v{novar + CONT(t)}
+          elseif t == 14
+            let var = F{novar + CONT(t)}()
+          endif
+        catch /^Vim\((\a\+)\)\=:/
+          Xloop 'a'
+          " v:errmsg is not set when the error message is converted to an
+          " exception.  Set it to the original error message.
+          let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
+        finally
+          Xloop 'b'
+          if t <= 8 && t != 3 && t != 7
+            call MSG(t, 'E475', 'Invalid argument\>')
+          else
+            call MSG(t, 'E121', "Undefined variable")
+          endif
+          let t = t + 1
+          XloopNEXT
+          continue	" discard an aborting error
+        endtry
+      endwhile
+    catch /.*/
+      call assert_report('should not get here')
+    endtry
+
+    func T(n, expr, enr, emsg)
+      try
+        let g:taken = g:taken . "T" . a:n
+        let v:errmsg = ""
+        try
+          execute "let var = " . a:expr
+        catch /^Vim\((\a\+)\)\=:/
+          Xloop 'c'
+          " v:errmsg is not set when the error message is converted to an
+          " exception.  Set it to the original error message.
+          let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
+        finally
+          Xloop 'd'
+          call MSG(a:n, a:enr, a:emsg)
+          XloopNEXT
+          " Discard an aborting error:
+          return
+        endtry
+      catch /.*/
+        call assert_report('should not get here')
+      endtry
+    endfunc
+
+    call T(15, 'Nofunc() + CONT(15)',	'E117',	"Unknown function")
+    call T(16, 'F(1 2 + CONT(16))',	'E116',	"Invalid arguments")
+    call T(17, 'F(1, 2) + CONT(17)',	'E118',	"Too many arguments")
+    call T(18, 'F() + CONT(18)',	'E119',	"Not enough arguments")
+    call T(19, '{(1} + CONT(19)',	'E110',	"Missing ')'")
+    call T(20, '("abc"[1) + CONT(20)',	'E111',	"Missing ']'")
+    call T(21, '(1 +) + CONT(21)',	'E15',	"Invalid expression")
+    call T(22, '1 2 + CONT(22)',	'E15',	"Invalid expression")
+    call T(23, '(1 ? 2) + CONT(23)',	'E109',	"Missing ':' after '?'")
+    call T(24, '("abc) + CONT(24)',	'E114',	"Missing quote")
+    call T(25, "('abc) + CONT(25)",	'E115',	"Missing quote")
+    call T(26, '& + CONT(26)',		'E112', "Option name missing")
+    call T(27, '&asdf + CONT(27)',	'E113', "Unknown option")
+
+    let expected = ""
+      \ .. "T1M1T2M2T3M3T4M4T5M5T6M6T7M7T8M8T9M9T10M10T11M11T12M12T13M13T14M14"
+      \ .. "T15M15T16M16T17M17T18M18T19M19T20M20T21M21T22M22T23M23T24M24T25M25"
+      \ .. "T26M26T27M27"
+
+    call assert_equal(expected, taken)
+  [CODE]
+  let verify =<< trim [CODE]
+    let expected = "a1b1a2b2a3b3a4b4a5b5a6b6a7b7a8b8a9b9a10b10a11b11a12b12"
+                  \ .. "a13b13a14b14c15d15c16d16c17d17c18d18c19d19c20d20"
+                  \ .. "c21d21c22d22c23d23c24d24c25d25c26d26c27d27"
+    call assert_equal(expected, g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 79:  Throwing one of several errors for the same command		    {{{1
+"
+"	    When several errors appear in a row (for instance during expression
+"	    evaluation), the first as the most specific one is used when
+"	    throwing an error exception.  If, however, a syntax error is
+"	    detected afterwards, this one is used for the error exception.
+"	    On a syntax error, the next command is not executed, on a normal
+"	    error, however, it is (relevant only in a function without the
+"	    "abort" flag).  v:errmsg is not set.
+"
+"	    If throwing error exceptions is configured off, v:errmsg is always
+"	    set to the latest error message, that is, to the more general
+"	    message or the syntax error, respectively.
+"-------------------------------------------------------------------------------
+func Test_throw_multi_error()
+  CheckEnglish
+
+  let test =<< trim [CODE]
+    func NEXT(cmd)
+      exec a:cmd . " | Xloop 'a'"
+    endfun
+
+    call NEXT('echo novar')			" (checks nextcmd)
+    XloopNEXT
+    call NEXT('let novar #')			" (skips nextcmd)
+    XloopNEXT
+    call NEXT('unlet novar #')			" (skips nextcmd)
+    XloopNEXT
+    call NEXT('let {novar}')			" (skips nextcmd)
+    XloopNEXT
+    call NEXT('unlet{ novar}')			" (skips nextcmd)
+
+    call assert_equal('a1', g:Xpath)
+    XpathINIT
+    XloopINIT
+
+    func EXEC(cmd)
+      exec a:cmd
+    endfunc
+
+    try
+      while 1				" dummy loop
+        try
+          let v:errmsg = ""
+          call EXEC('echo novar')	" normal error
+        catch /^Vim\((\a\+)\)\=:/
+          Xpath 'b'
+          call assert_match('E121: Undefined variable: novar', v:exception)
+        finally
+          Xpath 'c'
+          call assert_equal("", v:errmsg)
+          break
+        endtry
+      endwhile
+
+      Xpath 'd'
+      let cmd = "let"
+      while cmd != ""
+        try
+          let v:errmsg = ""
+          call EXEC(cmd . ' novar #')		" normal plus syntax error
+        catch /^Vim\((\a\+)\)\=:/
+          Xloop 'e'
+          call assert_match('E488: Trailing characters', v:exception)
+        finally
+          Xloop 'f'
+          call assert_equal("", v:errmsg)
+          if cmd == "let"
+            let cmd = "unlet"
+          else
+            let cmd = ""
+          endif
+          XloopNEXT
+          continue
+        endtry
+      endwhile
+
+      Xpath 'g'
+      let cmd = "let"
+      while cmd != ""
+        try
+          let v:errmsg = ""
+          call EXEC(cmd . ' {novar}')		" normal plus syntax error
+        catch /^Vim\((\a\+)\)\=:/
+          Xloop 'h'
+          call assert_match('E475: Invalid argument: {novar}', v:exception)
+        finally
+          Xloop 'i'
+          call assert_equal("", v:errmsg)
+          if cmd == "let"
+            let cmd = "unlet"
+          else
+            let cmd = ""
+          endif
+          XloopNEXT
+          continue
+        endtry
+      endwhile
+    catch /.*/
+      call assert_report('should not get here')
+    endtry
+    Xpath 'j'
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('bcde1f1e2f2gh3i3h4i4j', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 80:  Syntax error in expression for illegal :elseif		    {{{1
+"
+"	    If there is a syntax error in the expression after an illegal
+"	    :elseif, an error message is given (or an error exception thrown)
+"	    for the illegal :elseif rather than the expression error.
+"-------------------------------------------------------------------------------
+func Test_if_syntax_error()
+  CheckEnglish
+
+  let test =<< trim [CODE]
+    let v:errmsg = ""
+    if 0
+    else
+    elseif 1 ||| 2
+    endif
+    Xpath 'a'
+    call assert_match('E584: :elseif after :else', v:errmsg)
+
+    let v:errmsg = ""
+    if 1
+    else
+    elseif 1 ||| 2
+    endif
+    Xpath 'b'
+    call assert_match('E584: :elseif after :else', v:errmsg)
+
+    let v:errmsg = ""
+    elseif 1 ||| 2
+    Xpath 'c'
+    call assert_match('E582: :elseif without :if', v:errmsg)
+
+    let v:errmsg = ""
+    while 1
+      elseif 1 ||| 2
+    endwhile
+    Xpath 'd'
+    call assert_match('E582: :elseif without :if', v:errmsg)
+
+    while 1
+      try
+        try
+          let v:errmsg = ""
+          if 0
+          else
+          elseif 1 ||| 2
+          endif
+        catch /^Vim\((\a\+)\)\=:/
+          Xpath 'e'
+          call assert_match('E584: :elseif after :else', v:exception)
+        finally
+          Xpath 'f'
+          call assert_equal("", v:errmsg)
+        endtry
+      catch /.*/
+      call assert_report('should not get here')
+      finally
+        Xpath 'g'
+        break
+      endtry
+    endwhile
+
+    while 1
+      try
+        try
+          let v:errmsg = ""
+          if 1
+          else
+          elseif 1 ||| 2
+          endif
+        catch /^Vim\((\a\+)\)\=:/
+          Xpath 'h'
+          call assert_match('E584: :elseif after :else', v:exception)
+        finally
+          Xpath 'i'
+          call assert_equal("", v:errmsg)
+        endtry
+      catch /.*/
+        call assert_report('should not get here')
+      finally
+        Xpath 'j'
+        break
+      endtry
+    endwhile
+
+    while 1
+      try
+        try
+          let v:errmsg = ""
+          elseif 1 ||| 2
+        catch /^Vim\((\a\+)\)\=:/
+          Xpath 'k'
+          call assert_match('E582: :elseif without :if', v:exception)
+        finally
+          Xpath 'l'
+          call assert_equal("", v:errmsg)
+        endtry
+      catch /.*/
+        call assert_report('should not get here')
+      finally
+        Xpath 'm'
+        break
+      endtry
+    endwhile
+
+    while 1
+      try
+        try
+          let v:errmsg = ""
+          while 1
+              elseif 1 ||| 2
+          endwhile
+        catch /^Vim\((\a\+)\)\=:/
+          Xpath 'n'
+          call assert_match('E582: :elseif without :if', v:exception)
+        finally
+          Xpath 'o'
+          call assert_equal("", v:errmsg)
+        endtry
+      catch /.*/
+        call assert_report('should not get here')
+      finally
+        Xpath 'p'
+        break
+      endtry
+    endwhile
+    Xpath 'q'
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('abcdefghijklmnopq', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 81:  Discarding exceptions after an error or interrupt		    {{{1
+"
+"	    When an exception is thrown from inside a :try conditional without
+"	    :catch and :finally clauses and an error or interrupt occurs before
+"	    the :endtry is reached, the exception is discarded.
+"-------------------------------------------------------------------------------
+
+func Test_discard_exception_after_error_1()
+  let test =<< trim [CODE]
+    try
+      Xpath 'a'
+      try
+        Xpath 'b'
+        throw "arrgh"
+        call assert_report('should not get here')
+        if 1
+        call assert_report('should not get here')
+        " error after :throw: missing :endif
+      endtry
+      call assert_report('should not get here')
+    catch /arrgh/
+      call assert_report('should not get here')
+    endtry
+    call assert_report('should not get here')
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('ab', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
+endfunc
+
+" TODO: Not able inject an interrupt after throwing an exception
+func Disable_Test_discard_exception_after_error_2()
+  let test =<< trim [CODE]
+    try
+      Xpath 'a'
+      try
+        Xpath 'b'
+        throw "arrgh"
+        call interrupt()    " FIXME: throw is not interrupted here
+        call assert_report('should not get here')
+      endtry
+      call assert_report('should not get here')
+    catch /arrgh/
+      call assert_report('should not get here')
+    endtry
+    call assert_report('should not get here')
+  [CODE]
+  let verify =<< trim [CODE]
+    call assert_equal('ab', g:Xpath)
+  [CODE]
+  call RunInNewVim(test, verify)
 endfunc
 
 "-------------------------------------------------------------------------------
-- 
cgit 


From d5dd7573f32411746867b935b8db2165d14018ec Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 05:02:57 +0800
Subject: vim-patch:8.2.3713: MS-Windows: no error if vimgrep pattern is not
 matching (#20947)

Problem:    MS-Windows: No error message if vimgrep pattern is not matching.
Solution:   Give an error message. (Christian Brabandt, closes vim/vim#9245,
            closes vim/vim#8762)

https://github.com/vim/vim/commit/0b226f60be5c30c32fb01fc0b6dc11286e7e2313

Co-authored-by: Christian Brabandt 
---
 src/nvim/quickfix.c                |  6 ++----
 src/nvim/testdir/test_quickfix.vim | 15 +++++++++++++++
 2 files changed, 17 insertions(+), 4 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index acf9b881f8..5d101ee415 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -5325,10 +5325,8 @@ static int vgr_process_args(exarg_T *eap, vgr_args_T *args)
   }
 
   // Parse the list of arguments, wildcards have already been expanded.
-  if (get_arglist_exp(p, &args->fcount, &args->fnames, true) == FAIL) {
-    return FAIL;
-  }
-  if (args->fcount == 0) {
+  if (get_arglist_exp(p, &args->fcount, &args->fnames, true) == FAIL
+      || args->fcount == 0) {
     emsg(_(e_nomatch));
     return FAIL;
   }
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index 99d9c9c1fa..9d9fc5e77b 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -5801,6 +5801,21 @@ func Test_win_gettype()
   lclose
 endfunc
 
+fun Test_vimgrep_nomatch()
+  call XexprTests('c')
+  call g:Xsetlist([{'lnum':10,'text':'Line1'}])
+  copen
+  if has("win32")
+    call assert_fails('vimgrep foo *.zzz', 'E479:')
+    let expected = [{'lnum': 10, 'bufnr': 0, 'end_lnum': 0, 'pattern': '', 'valid': 0, 'vcol': 0, 'nr': 0, 'module': '', 'type': '', 'end_col': 0, 'col': 0, 'text': 'Line1'}]
+  else
+    call assert_fails('vimgrep foo *.zzz', 'E480:')
+    let expected = []
+  endif
+  call assert_equal(expected, getqflist())
+  cclose
+endfunc
+
 " Test for opening the quickfix window in two tab pages and then closing one
 " of the quickfix windows. This should not make the quickfix buffer unlisted.
 " (github issue #9300).
-- 
cgit 


From 83ea9e23a23408ee2bfbfbae01f081de011dc49b Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 05:47:57 +0800
Subject: vim-patch:9.0.0835: the window title is not redrawn when 'endoffile'
 changes (#20951)

Problem:    The window title is not redrawn when 'endoffile' changes.
Solution:   redraw the window title when 'endoffile' is changed. (Ken Takata,
            closes vim/vim#11488)

https://github.com/vim/vim/commit/845bbb72ed2da4b5fb2a503d91cfd6435df2f584

Co-authored-by: K.Takata 
---
 src/nvim/option.c | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/option.c b/src/nvim/option.c
index 8142be4eb1..306a63b74d 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -1975,14 +1975,12 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
   } else if ((int *)varp == &curbuf->b_p_ma) {
     // when 'modifiable' is changed, redraw the window title
     redraw_titles();
-  } else if ((int *)varp == &curbuf->b_p_eol) {
-    // when 'endofline' is changed, redraw the window title
-    redraw_titles();
-  } else if ((int *)varp == &curbuf->b_p_fixeol) {
-    // when 'fixeol' is changed, redraw the window title
-    redraw_titles();
-  } else if ((int *)varp == &curbuf->b_p_bomb) {
-    // when 'bomb' is changed, redraw the window title and tab page text
+  } else if ((int *)varp == &curbuf->b_p_eof
+             || (int *)varp == &curbuf->b_p_eol
+             || (int *)varp == &curbuf->b_p_fixeol
+             || (int *)varp == &curbuf->b_p_bomb) {
+    // redraw the window title and tab page text when 'endoffile', 'endofline',
+    // 'fixeol' or 'bomb' is changed
     redraw_titles();
   } else if ((int *)varp == &curbuf->b_p_bin) {
     // when 'bin' is set also set some other options
-- 
cgit 


From e03f23189d765ade07b21d2f50c047f84741a133 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 06:31:00 +0800
Subject: vim-patch:8.2.1274: Vim9: no error for missing white space at script
 level

Problem:    Vim9: no error for missing white space in assignment at script
            level.
Solution:   Check for white space. (closes vim/vim#6495)

https://github.com/vim/vim/commit/63be3d4ba01d565e645d8bf7f4dc900fc9011534

Cherry-pick Test_let_errors() change from patch 8.2.0633.

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval.c               |  3 ++-
 src/nvim/eval/vars.c          |  6 ++++--
 src/nvim/testdir/test_let.vim | 11 +++++++++--
 3 files changed, 15 insertions(+), 5 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 1200ba20ba..b93367381d 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -6694,7 +6694,8 @@ const char *find_name_end(const char *arg, const char **expr_start, const char *
   for (p = arg; *p != NUL
        && (eval_isnamec(*p)
            || *p == '{'
-           || ((flags & FNE_INCL_BR) && (*p == '[' || *p == '.'))
+           || ((flags & FNE_INCL_BR) && (*p == '['
+                                         || (*p == '.' && eval_isnamec1(p[1]))))
            || mb_nest != 0
            || br_nest != 0); MB_PTR_ADV(p)) {
     if (*p == '\'') {
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index ef6e3f02e2..3f73dbfdc5 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -231,11 +231,13 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
           expr++;
         }
       }
-      expr = skipwhite(expr + 2);
+      expr += 2;
     } else {
-      expr = skipwhite(expr + 1);
+      expr += 1;
     }
 
+    expr = skipwhite(expr);
+
     if (eap->skip) {
       emsg_skip++;
     }
diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim
index f05e06f774..79619b0f1e 100644
--- a/src/nvim/testdir/test_let.vim
+++ b/src/nvim/testdir/test_let.vim
@@ -276,20 +276,27 @@ func Test_let_errors()
   let s = "var"
   let var = 1
   call assert_fails('let var += [1,2]', 'E734:')
-  call assert_fails('let {s}.1 = 2', 'E18:')
+  call assert_fails('let {s}.1 = 2', 'E15:')
   call assert_fails('let a[1] = 5', 'E121:')
   let l = [[1,2]]
   call assert_fails('let l[:][0] = [5]', 'E708:')
   let d = {'k' : 4}
-  call assert_fails('let d.# = 5', 'E713:')
+  call assert_fails('let d.# = 5', 'E488:')
   call assert_fails('let d.m += 5', 'E734:')
+  call assert_fails('let m = d[{]', 'E15:')
   let l = [1, 2]
   call assert_fails('let l[2] = 0', 'E684:')
   call assert_fails('let l[0:1] = [1, 2, 3]', 'E710:')
   call assert_fails('let l[-2:-3] = [3, 4]', 'E684:')
   call assert_fails('let l[0:4] = [5, 6]', 'E711:')
+  call assert_fails('let l -= 2', 'E734:')
+  call assert_fails('let l += 2', 'E734:')
   call assert_fails('let g:["a;b"] = 10', 'E461:')
   call assert_fails('let g:.min = function("max")', 'E704:')
+  if has('channel')
+    let ch = test_null_channel()
+    call assert_fails('let ch += 1', 'E734:')
+  endif
 
   " This test works only when the language is English
   if v:lang == "C" || v:lang =~ '^[Ee]n'
-- 
cgit 


From 1f0bf65ad6ce6df3eedfb013cf9b95aed2a90781 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 06:35:34 +0800
Subject: vim-patch:8.2.1306: checking for first character of dict key is
 inconsistent

Problem:    Checking for first character of dict key is inconsistent.
Solution:   Add eval_isdictc(). (closes vim/vim#6546)

https://github.com/vim/vim/commit/b13ab99908097d54e21ab5adad22f4ad2a8ec688

Omit handle_subscript() change: only affects Vim9 script.

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval.c                    | 15 +++++++++++----
 src/nvim/testdir/test_let.vim      |  2 +-
 src/nvim/testdir/test_listdict.vim |  7 +++++++
 3 files changed, 19 insertions(+), 5 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index b93367381d..4b545c0dec 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -3418,7 +3418,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
   if (**arg == '.') {
     // dict.name
     key = *arg + 1;
-    for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) {}
+    for (len = 0; eval_isdictc(key[len]); len++) {}
     if (len == 0) {
       return FAIL;
     }
@@ -6695,7 +6695,7 @@ const char *find_name_end(const char *arg, const char **expr_start, const char *
        && (eval_isnamec(*p)
            || *p == '{'
            || ((flags & FNE_INCL_BR) && (*p == '['
-                                         || (*p == '.' && eval_isnamec1(p[1]))))
+                                         || (*p == '.' && eval_isdictc(p[1]))))
            || mb_nest != 0
            || br_nest != 0); MB_PTR_ADV(p)) {
     if (*p == '\'') {
@@ -6808,18 +6808,25 @@ static char *make_expanded_name(const char *in_start, char *expr_start, char *ex
 
 /// @return  true if character "c" can be used in a variable or function name.
 ///          Does not include '{' or '}' for magic braces.
-int eval_isnamec(int c)
+bool eval_isnamec(int c)
 {
   return ASCII_ISALNUM(c) || c == '_' || c == ':' || c == AUTOLOAD_CHAR;
 }
 
 /// @return  true if character "c" can be used as the first character in a
 ///          variable or function name (excluding '{' and '}').
-int eval_isnamec1(int c)
+bool eval_isnamec1(int c)
 {
   return ASCII_ISALPHA(c) || c == '_';
 }
 
+/// @return  true if character "c" can be used as the first character of a
+///          dictionary key.
+bool eval_isdictc(int c)
+{
+  return ASCII_ISALNUM(c) || c == '_';
+}
+
 /// Get typval_T v: variable value.
 typval_T *get_vim_var_tv(int idx)
 {
diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim
index 79619b0f1e..e8bc2ac155 100644
--- a/src/nvim/testdir/test_let.vim
+++ b/src/nvim/testdir/test_let.vim
@@ -276,7 +276,7 @@ func Test_let_errors()
   let s = "var"
   let var = 1
   call assert_fails('let var += [1,2]', 'E734:')
-  call assert_fails('let {s}.1 = 2', 'E15:')
+  call assert_fails('let {s}.1 = 2', 'E18:')
   call assert_fails('let a[1] = 5', 'E121:')
   let l = [[1,2]]
   call assert_fails('let l[:][0] = [5]', 'E708:')
diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim
index ecf95ba8c0..21d21ce428 100644
--- a/src/nvim/testdir/test_listdict.vim
+++ b/src/nvim/testdir/test_listdict.vim
@@ -289,6 +289,13 @@ func Test_dict_func()
   call assert_equal('xxx3', Fn('xxx'))
 endfunc
 
+func Test_dict_assign()
+  let d = {}
+  let d.1 = 1
+  let d._ = 2
+  call assert_equal({'1': 1, '_': 2}, d)
+endfunc
+
 " Function in script-local List or Dict
 func Test_script_local_dict_func()
   let g:dict = {}
-- 
cgit 


From a208a8b10eced35916cc1093453f4485bdbccb7a Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 06:46:30 +0800
Subject: vim-patch:8.2.2722: Vim9: crash when using LHS with double index

Problem:    Vim9: crash when using LHS with double index.
Solution:   Handle lhs_dest which is "dest_expr". (closes vim/vim#8068)
            Fix confusing error message for missing dict item.

https://github.com/vim/vim/commit/b9c0cd897ab4ad54f514187e89719c0241393f8b

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 4b545c0dec..62e006fd57 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -1757,7 +1757,7 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool
     // Assign to a List or Dictionary item.
     if (lp->ll_newkey != NULL) {
       if (op != NULL && *op != '=') {
-        semsg(_(e_letwrong), op);
+        semsg(_(e_dictkey), lp->ll_newkey);
         return;
       }
 
-- 
cgit 


From fa2ed8ca1fbf98053e8273d814322018895d6b9b Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 06:47:08 +0800
Subject: vim-patch:8.2.2723: assignment test fails

Problem:    Assignment test fails.
Solution:   Adjust error number.

https://github.com/vim/vim/commit/58fb7c39a04aabfa399ae4f2142ee0728bc22483

Co-authored-by: Bram Moolenaar 
---
 src/nvim/testdir/test_let.vim | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim
index e8bc2ac155..3ef3d961a7 100644
--- a/src/nvim/testdir/test_let.vim
+++ b/src/nvim/testdir/test_let.vim
@@ -282,7 +282,7 @@ func Test_let_errors()
   call assert_fails('let l[:][0] = [5]', 'E708:')
   let d = {'k' : 4}
   call assert_fails('let d.# = 5', 'E488:')
-  call assert_fails('let d.m += 5', 'E734:')
+  call assert_fails('let d.m += 5', 'E716:')
   call assert_fails('let m = d[{]', 'E15:')
   let l = [1, 2]
   call assert_fails('let l[2] = 0', 'E684:')
-- 
cgit 


From 7d7208a88b2bb078c9c2727e93ef390f2a564027 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 06:47:54 +0800
Subject: vim-patch:8.2.3016: confusing error when expression is followed by
 comma

Problem:    Confusing error when expression is followed by comma.
Solution:   Give a different error for trailing text. (closes vim/vim#8395)

https://github.com/vim/vim/commit/fae55a9cb0838e4c2e634e55a3468af4a75fbdf2

Omit test_eval_stuff.vim and test_viminfo.vim: changes tests are N/A.

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval.c                     | 13 +++++++++++--
 src/nvim/testdir/test_let.vim       |  1 +
 src/nvim/testdir/test_vimscript.vim |  2 +-
 3 files changed, 13 insertions(+), 3 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 62e006fd57..465d76de69 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -2277,10 +2277,15 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
   char *p;
   const int did_emsg_before = did_emsg;
   const int called_emsg_before = called_emsg;
+  bool end_error = false;
 
   p = skipwhite(arg);
   ret = eval1(&p, rettv, evaluate);
-  if (ret == FAIL || !ends_excmd(*p)) {
+
+  if (ret != FAIL) {
+    end_error = !ends_excmd(*p);
+  }
+  if (ret == FAIL || end_error) {
     if (ret != FAIL) {
       tv_clear(rettv);
     }
@@ -2290,7 +2295,11 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
     // Also check called_emsg for when using assert_fails().
     if (!aborting() && did_emsg == did_emsg_before
         && called_emsg == called_emsg_before) {
-      semsg(_(e_invexpr2), arg);
+      if (end_error) {
+        semsg(_(e_trailing_arg), p);
+      } else {
+        semsg(_(e_invexpr2), arg);
+      }
     }
     ret = FAIL;
   }
diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim
index 3ef3d961a7..48c5699093 100644
--- a/src/nvim/testdir/test_let.vim
+++ b/src/nvim/testdir/test_let.vim
@@ -297,6 +297,7 @@ func Test_let_errors()
     let ch = test_null_channel()
     call assert_fails('let ch += 1', 'E734:')
   endif
+  call assert_fails('let name = "a" .. "b",', 'E488: Trailing characters: ,')
 
   " This test works only when the language is English
   if v:lang == "C" || v:lang =~ '^[Ee]n'
diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim
index 8faa9135e5..44904af160 100644
--- a/src/nvim/testdir/test_vimscript.vim
+++ b/src/nvim/testdir/test_vimscript.vim
@@ -5569,7 +5569,7 @@ func Test_expr_eval_error_msg()
     call T(19, '{(1} + CONT(19)',	'E110',	"Missing ')'")
     call T(20, '("abc"[1) + CONT(20)',	'E111',	"Missing ']'")
     call T(21, '(1 +) + CONT(21)',	'E15',	"Invalid expression")
-    call T(22, '1 2 + CONT(22)',	'E15',	"Invalid expression")
+    call T(22, '1 2 + CONT(22)',	'E488',	"Trailing characters: 2 +")
     call T(23, '(1 ? 2) + CONT(23)',	'E109',	"Missing ':' after '?'")
     call T(24, '("abc) + CONT(24)',	'E114',	"Missing quote")
     call T(25, "('abc) + CONT(25)",	'E115',	"Missing quote")
-- 
cgit 


From dc17df31908ac0e0c092760d84f035d3bfc55aae Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 09:35:02 +0800
Subject: fix(eval): change some tv_dict_add() usages back to hash_add()

Needed for Vim patch 8.2.2920.
---
 src/nvim/eval/userfunc.c | 10 +++++-----
 src/nvim/eval/vars.c     |  2 +-
 2 files changed, 6 insertions(+), 6 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index 4024410a04..ef74ca58e3 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -589,7 +589,7 @@ static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr)
   STRCPY(v->di_key, name);
 #endif
   v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
-  tv_dict_add(dp, v);
+  hash_add(&dp->dv_hashtab, v->di_key);
   v->di_tv.v_type = VAR_NUMBER;
   v->di_tv.v_lock = VAR_FIXED;
   v->di_tv.vval.v_number = nr;
@@ -885,7 +885,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
     STRCPY(name, "self");
 #endif
     v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
-    tv_dict_add(&fc->l_vars, v);
+    hash_add(&fc->l_vars.dv_hashtab, v->di_key);
     v->di_tv.v_type = VAR_DICT;
     v->di_tv.v_lock = VAR_UNLOCKED;
     v->di_tv.vval.v_dict = selfdict;
@@ -911,7 +911,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
     STRCPY(name, "000");
 #endif
     v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
-    tv_dict_add(&fc->l_avars, v);
+    hash_add(&fc->l_avars.dv_hashtab, v->di_key);
     v->di_tv.v_type = VAR_LIST;
     v->di_tv.v_lock = VAR_FIXED;
     v->di_tv.vval.v_list = &fc->l_varlist;
@@ -989,9 +989,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
       // Named arguments can be accessed without the "a:" prefix in lambda
       // expressions. Add to the l: dict.
       tv_copy(&v->di_tv, &v->di_tv);
-      tv_dict_add(&fc->l_vars, v);
+      hash_add(&fc->l_vars.dv_hashtab, v->di_key);
     } else {
-      tv_dict_add(&fc->l_avars, v);
+      hash_add(&fc->l_avars.dv_hashtab, v->di_key);
     }
 
     if (ai >= 0 && ai < MAX_FUNC_ARGS) {
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index 3f73dbfdc5..a0877deaf7 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -1331,7 +1331,7 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv,
 
     v = xmalloc(sizeof(dictitem_T) + strlen(varname));
     STRCPY(v->di_key, varname);
-    if (tv_dict_add(dict, v) == FAIL) {
+    if (hash_add(ht, v->di_key) == FAIL) {
       xfree(v);
       return;
     }
-- 
cgit 


From 7683199a9bf5d84745a10222feddc43343410068 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 28 Oct 2022 11:53:22 +0800
Subject: vim-patch:8.2.2918: builtin function can be shadowed by global
 variable

Problem:    Builtin function can be shadowed by global variable.
Solution:   Check for builtin function before variable. (Yasuhiro Matsumoto,
            closes vim/vim#8302)

https://github.com/vim/vim/commit/3d9c4eefe656ee8bf58c0496a48bd56bac180056

Cherry-pick Test_gettext() from patch 8.2.2886.
---
 src/nvim/eval.c                     |  7 +++++++
 src/nvim/testdir/test_functions.vim | 13 +++++++++++++
 2 files changed, 20 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 465d76de69..5b5b4d259c 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -1760,6 +1760,13 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool
         semsg(_(e_dictkey), lp->ll_newkey);
         return;
       }
+      if ((lp->ll_tv->vval.v_dict == &globvardict
+           // || lp->ll_tv->vval.v_dict == &SCRIPT_ITEM(current_sctx.sc_sid)->sn_vars->sv_dict
+           )
+          && (rettv->v_type == VAR_FUNC || rettv->v_type == VAR_PARTIAL)
+          && var_wrong_func_name(lp->ll_newkey, true)) {
+        return;
+      }
 
       // Need to add an item to the Dictionary.
       di = tv_dict_item_alloc((const char *)lp->ll_newkey);
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index d2603809b9..afeea20f2d 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -2478,4 +2478,17 @@ func Test_default_arg_value()
   call assert_equal('msg', HasDefault())
 endfunc
 
+" Test for gettext()
+func Test_gettext()
+  call assert_fails('call gettext(1)', 'E475:')
+endfunc
+
+func Test_builtin_check()
+  call assert_fails('let g:["trim"] = {x -> " " .. x}', 'E704:')
+  call assert_fails('let g:.trim = {x -> " " .. x}', 'E704:')
+  call assert_fails('let s:["trim"] = {x -> " " .. x}', 'E704:')
+  call assert_fails('let s:.trim = {x -> " " .. x}', 'E704:')
+endfunc
+
+
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From 6b3db3f92906b1c155b11f177c9378d9ff4d7d6b Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 28 Oct 2022 11:56:29 +0800
Subject: vim-patch:8.2.2920: still a way to shadow a builtin function

Problem:    Still a way to shadow a builtin function. (Yasuhiro Matsumoto)
Solution:   Check the key when using extend(). (issue vim/vim#8302)

https://github.com/vim/vim/commit/6f1d2aa437744a7cb0206fdaa543a788c5a56c79

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval.c                     |  6 +-----
 src/nvim/eval/typval.c              | 18 ++++++++++++++++++
 src/nvim/testdir/test_functions.vim |  5 +++++
 3 files changed, 24 insertions(+), 5 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 5b5b4d259c..72df343932 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -1760,11 +1760,7 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool
         semsg(_(e_dictkey), lp->ll_newkey);
         return;
       }
-      if ((lp->ll_tv->vval.v_dict == &globvardict
-           // || lp->ll_tv->vval.v_dict == &SCRIPT_ITEM(current_sctx.sc_sid)->sn_vars->sv_dict
-           )
-          && (rettv->v_type == VAR_FUNC || rettv->v_type == VAR_PARTIAL)
-          && var_wrong_func_name(lp->ll_newkey, true)) {
+      if (tv_dict_wrong_func_name(lp->ll_tv->vval.v_dict, rettv, lp->ll_newkey)) {
         return;
       }
 
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 31b73f8d48..2b44dacca4 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -2206,6 +2206,18 @@ bool tv_dict_get_callback(dict_T *const d, const char *const key, const ptrdiff_
   return res;
 }
 
+/// Check for adding a function to g: or s:.
+/// If the name is wrong give an error message and return true.
+int tv_dict_wrong_func_name(dict_T *d, typval_T *tv, const char *name)
+{
+  return (d == &globvardict
+          // || (SCRIPT_ID_VALID(current_sctx.sc_sid)
+          //     && d == &SCRIPT_ITEM(current_sctx.sc_sid)->sn_vars->sv_dict)
+          )
+         && (tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL)
+         && var_wrong_func_name(name, true);
+}
+
 //{{{2 dict_add*
 
 /// Add item to dictionary
@@ -2217,6 +2229,9 @@ bool tv_dict_get_callback(dict_T *const d, const char *const key, const ptrdiff_
 int tv_dict_add(dict_T *const d, dictitem_T *const item)
   FUNC_ATTR_NONNULL_ALL
 {
+  if (tv_dict_wrong_func_name(d, &item->di_tv, (const char *)item->di_key)) {
+    return FAIL;
+  }
   return hash_add(&d->dv_hashtab, item->di_key);
 }
 
@@ -2477,6 +2492,9 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action
           || var_check_ro(di1->di_flags, arg_errmsg, arg_errmsg_len)) {
         break;
       }
+      if (tv_dict_wrong_func_name(d1, &di2->di_tv, (const char *)di2->di_key)) {
+        break;
+      }
 
       if (watched) {
         tv_copy(&di1->di_tv, &oldtv);
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index afeea20f2d..011e9e000e 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -2488,6 +2488,11 @@ func Test_builtin_check()
   call assert_fails('let g:.trim = {x -> " " .. x}', 'E704:')
   call assert_fails('let s:["trim"] = {x -> " " .. x}', 'E704:')
   call assert_fails('let s:.trim = {x -> " " .. x}', 'E704:')
+
+  call assert_fails('call extend(g:, #{foo: { -> "foo" }})', 'E704:')
+  let g:bar = 123
+  call extend(g:, #{bar: { -> "foo" }}, "keep")
+  call assert_fails('call extend(g:, #{bar: { -> "foo" }}, "force")', 'E704:')
 endfunc
 
 
-- 
cgit 


From 4740672b37612004d559e9577ca87223ed49ec64 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 28 Oct 2022 12:08:18 +0800
Subject: vim-patch:8.2.2921: E704 for script local variable is not backwards
 compatible

Problem:    E704 for script local variable is not backwards compatible.
            (Yasuhiro Matsumoto)
Solution:   Only give the error in Vim9 script.  Also check for function-local
            variable.

https://github.com/vim/vim/commit/b54abeeafb074248597878a874fed9a66b114c06

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval/typval.c              | 5 +----
 src/nvim/testdir/test_functions.vim | 9 +++++++--
 2 files changed, 8 insertions(+), 6 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 2b44dacca4..c8f89407b9 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -2210,10 +2210,7 @@ bool tv_dict_get_callback(dict_T *const d, const char *const key, const ptrdiff_
 /// If the name is wrong give an error message and return true.
 int tv_dict_wrong_func_name(dict_T *d, typval_T *tv, const char *name)
 {
-  return (d == &globvardict
-          // || (SCRIPT_ID_VALID(current_sctx.sc_sid)
-          //     && d == &SCRIPT_ITEM(current_sctx.sc_sid)->sn_vars->sv_dict)
-          )
+  return (d == &globvardict || &d->dv_hashtab == get_funccal_local_ht())
          && (tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL)
          && var_wrong_func_name(name, true);
 }
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 011e9e000e..dbb39d0f23 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -2486,8 +2486,13 @@ endfunc
 func Test_builtin_check()
   call assert_fails('let g:["trim"] = {x -> " " .. x}', 'E704:')
   call assert_fails('let g:.trim = {x -> " " .. x}', 'E704:')
-  call assert_fails('let s:["trim"] = {x -> " " .. x}', 'E704:')
-  call assert_fails('let s:.trim = {x -> " " .. x}', 'E704:')
+  call assert_fails('let l:["trim"] = {x -> " " .. x}', 'E704:')
+  call assert_fails('let l:.trim = {x -> " " .. x}', 'E704:')
+  let lines =<< trim END
+    vim9script
+    var s:trim = (x) => " " .. x
+  END
+  " call CheckScriptFailure(lines, 'E704:')
 
   call assert_fails('call extend(g:, #{foo: { -> "foo" }})', 'E704:')
   let g:bar = 123
-- 
cgit 


From d4353c3645f294b67f5b30dae9f39de8f99c7413 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 07:01:45 +0800
Subject: vim-patch:9.0.0836: wrong error when using extend() with funcref

Problem:    Wrong error when using extend() with funcref.
Solution:   Better check the variable type. (closes vim/vim#11468, closes vim/vim#11455)

https://github.com/vim/vim/commit/91c75d18d9cdc32df57e648640de7476fbcb4d76
---
 src/nvim/eval/typval.c              | 19 ++++++-------------
 src/nvim/testdir/test_functions.vim | 19 +++++++++++++++++++
 src/nvim/testdir/test_let.vim       |  1 +
 3 files changed, 26 insertions(+), 13 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index c8f89407b9..13d836071d 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -2206,12 +2206,12 @@ bool tv_dict_get_callback(dict_T *const d, const char *const key, const ptrdiff_
   return res;
 }
 
-/// Check for adding a function to g: or s:.
+/// Check for adding a function to g: or l:.
 /// If the name is wrong give an error message and return true.
 int tv_dict_wrong_func_name(dict_T *d, typval_T *tv, const char *name)
 {
   return (d == &globvardict || &d->dv_hashtab == get_funccal_local_ht())
-         && (tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL)
+         && tv_is_func(*tv)
          && var_wrong_func_name(name, true);
 }
 
@@ -2459,17 +2459,9 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action
 
   TV_DICT_ITER(d2, di2, {
     dictitem_T *const di1 = tv_dict_find(d1, (const char *)di2->di_key, -1);
-    if (d1->dv_scope != VAR_NO_SCOPE) {
-      // Disallow replacing a builtin function in l: and g:.
-      // Check the key to be valid when adding to any scope.
-      if (d1->dv_scope == VAR_DEF_SCOPE
-          && tv_is_func(di2->di_tv)
-          && var_wrong_func_name((const char *)di2->di_key, di1 == NULL)) {
-        break;
-      }
-      if (!valid_varname((const char *)di2->di_key)) {
-        break;
-      }
+    // Check the key to be valid when adding to any scope.
+    if (d1->dv_scope != VAR_NO_SCOPE && !valid_varname((const char *)di2->di_key)) {
+      break;
     }
     if (di1 == NULL) {
       dictitem_T *const new_di = tv_dict_item_copy(di2);
@@ -2489,6 +2481,7 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action
           || var_check_ro(di1->di_flags, arg_errmsg, arg_errmsg_len)) {
         break;
       }
+      // Disallow replacing a builtin function.
       if (tv_dict_wrong_func_name(d1, &di2->di_tv, (const char *)di2->di_key)) {
         break;
       }
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index dbb39d0f23..743022ece4 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -2498,6 +2498,25 @@ func Test_builtin_check()
   let g:bar = 123
   call extend(g:, #{bar: { -> "foo" }}, "keep")
   call assert_fails('call extend(g:, #{bar: { -> "foo" }}, "force")', 'E704:')
+  unlet g:bar
+
+  call assert_fails('call extend(l:, #{foo: { -> "foo" }})', 'E704:')
+  let bar = 123
+  call extend(l:, #{bar: { -> "foo" }}, "keep")
+  call assert_fails('call extend(l:, #{bar: { -> "foo" }}, "force")', 'E704:')
+  unlet bar
+
+  call assert_fails('call extend(g:, #{foo: function("extend")})', 'E704:')
+  let g:bar = 123
+  call extend(g:, #{bar: function("extend")}, "keep")
+  call assert_fails('call extend(g:, #{bar: function("extend")}, "force")', 'E704:')
+  unlet g:bar
+
+  call assert_fails('call extend(l:, #{foo: function("extend")})', 'E704:')
+  let bar = 123
+  call extend(l:, #{bar: function("extend")}, "keep")
+  call assert_fails('call extend(l:, #{bar: function("extend")}, "force")', 'E704:')
+  unlet bar
 endfunc
 
 
diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim
index 48c5699093..cb883fc238 100644
--- a/src/nvim/testdir/test_let.vim
+++ b/src/nvim/testdir/test_let.vim
@@ -293,6 +293,7 @@ func Test_let_errors()
   call assert_fails('let l += 2', 'E734:')
   call assert_fails('let g:["a;b"] = 10', 'E461:')
   call assert_fails('let g:.min = function("max")', 'E704:')
+  call assert_fails('let g:cos = "" | let g:.cos = {-> 42}', 'E704:')
   if has('channel')
     let ch = test_null_channel()
     call assert_fails('let ch += 1', 'E734:')
-- 
cgit 


From 95c862095f54d21737ef12f7da689a037e9a1c3e Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 15:08:24 +0800
Subject: refactor(eval): make get_lval() explicitly check for v:lua

Needed for Vim patch 8.2.3055.
---
 src/nvim/eval.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 72df343932..2ce5cd2104 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -1313,8 +1313,15 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
     return NULL;
   }
 
-  // Loop until no more [idx] or .key is following.
   lp->ll_tv = &v->di_tv;
+
+  if (tv_is_luafunc(lp->ll_tv)) {
+    // For v:lua just return a pointer to the "." after the "v:lua".
+    // If the caller is trans_function_name() it will check for a Lua function name.
+    return p;
+  }
+
+  // Loop until no more [idx] or .key is following.
   typval_T var1;
   var1.v_type = VAR_UNKNOWN;
   typval_T var2;
-- 
cgit 


From 7404c6010dd7abd4339a4ffd6961f2a420fe7ddb Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 14:50:09 +0800
Subject: vim-patch:8.2.3055: strange error for assigning to "x.key" on
 non-dictionary

Problem:    Strange error for assigning to "x.key" on non-dictionary.
Solution:   Add a specific error message. (closes vim/vim#8451)

https://github.com/vim/vim/commit/3a3b10e87a10dd0bc355618dbfc4a0a6c828aad7

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval.c                    | 10 +++++++++-
 src/nvim/testdir/test_let.vim      |  2 +-
 src/nvim/testdir/test_listdict.vim |  3 +++
 3 files changed, 13 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 2ce5cd2104..fe4ae92834 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -66,6 +66,8 @@ static char *e_nowhitespace
 static char *e_write2 = N_("E80: Error while writing: %s");
 static char *e_string_list_or_blob_required = N_("E1098: String, List or Blob required");
 static char e_expression_too_recursive_str[] = N_("E1169: Expression too recursive: %s");
+static char e_dot_can_only_be_used_on_dictionary_str[]
+  = N_("E1203: Dot can only be used on a dictionary: %s");
 
 static char * const namespace_char = "abglstvw";
 
@@ -1326,7 +1328,13 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
   var1.v_type = VAR_UNKNOWN;
   typval_T var2;
   var2.v_type = VAR_UNKNOWN;
-  while (*p == '[' || (*p == '.' && lp->ll_tv->v_type == VAR_DICT)) {
+  while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.')) {
+    if (*p == '.' && lp->ll_tv->v_type != VAR_DICT) {
+      if (!quiet) {
+        semsg(_(e_dot_can_only_be_used_on_dictionary_str), name);
+      }
+      return NULL;
+    }
     if (!(lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list != NULL)
         && !(lp->ll_tv->v_type == VAR_DICT && lp->ll_tv->vval.v_dict != NULL)
         && !(lp->ll_tv->v_type == VAR_BLOB && lp->ll_tv->vval.v_blob != NULL)) {
diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim
index cb883fc238..49f656c787 100644
--- a/src/nvim/testdir/test_let.vim
+++ b/src/nvim/testdir/test_let.vim
@@ -276,7 +276,7 @@ func Test_let_errors()
   let s = "var"
   let var = 1
   call assert_fails('let var += [1,2]', 'E734:')
-  call assert_fails('let {s}.1 = 2', 'E18:')
+  call assert_fails('let {s}.1 = 2', 'E1203:')
   call assert_fails('let a[1] = 5', 'E121:')
   let l = [[1,2]]
   call assert_fails('let l[:][0] = [5]', 'E708:')
diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim
index 21d21ce428..3d90222b90 100644
--- a/src/nvim/testdir/test_listdict.vim
+++ b/src/nvim/testdir/test_listdict.vim
@@ -294,6 +294,9 @@ func Test_dict_assign()
   let d.1 = 1
   let d._ = 2
   call assert_equal({'1': 1, '_': 2}, d)
+
+  let n = 0
+  call assert_fails('let n.key = 3', 'E1203: Dot can only be used on a dictionary: n.key = 3')
 endfunc
 
 " Function in script-local List or Dict
-- 
cgit 


From ed01ef7fa5c2611748f90acfdc2145b22629dc36 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 15:11:48 +0800
Subject: vim-patch:9.0.0355: check for uppercase char in autoload name is
 wrong

Problem:    Check for uppercase char in autoload name is wrong, it checks the
            name of the script.
Solution:   Remove the check. (closes vim/vim#11031)

https://github.com/vim/vim/commit/6c667bdc9489963102bd6c46b1b73e4d43c034ce

Co-authored-by: thinca 
---
 src/nvim/eval/vars.c          | 5 +++--
 src/nvim/testdir/test_let.vim | 4 ++++
 2 files changed, 7 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index a0877deaf7..139e7ed66b 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -1453,9 +1453,10 @@ bool var_wrong_func_name(const char *const name, const bool new_var)
   FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
 {
   // Allow for w: b: s: and t:.
+  // Allow autoload variable.
   if (!(vim_strchr("wbst", name[0]) != NULL && name[1] == ':')
-      && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':')
-                        ? name[2] : name[0])) {
+      && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') ? name[2] : name[0])
+      && vim_strchr(name, '#') == NULL) {
     semsg(_("E704: Funcref variable name must start with a capital: %s"), name);
     return true;
   }
diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim
index 49f656c787..35745e9c6a 100644
--- a/src/nvim/testdir/test_let.vim
+++ b/src/nvim/testdir/test_let.vim
@@ -6,6 +6,10 @@ func Test_let()
   let Test104#numvar = function('tr')
   call assert_equal("function('tr')", string(Test104#numvar))
 
+  let foo#tr = function('tr')
+  call assert_equal("function('tr')", string(foo#tr))
+  unlet foo#tr
+
   let a = 1
   let b = 2
 
-- 
cgit 


From cac576322d4b22c0fa7ba7564ba7ca9656fc96b9 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 15:52:42 +0800
Subject: vim-patch:8.2.1736: failure to compile a pattern not tested much

Problem:    Failure to compile a pattern not tested much.
Solution:   Add tests where a pattern fails to compile. (Yegappan Lakshmanan,
            closes vim/vim#7004)

https://github.com/vim/vim/commit/531be47ac5522807b265c6287021a01c9b895ac9
---
 src/nvim/testdir/test_autocmd.vim     |  1 +
 src/nvim/testdir/test_buffer.vim      |  1 +
 src/nvim/testdir/test_checkpath.vim   | 17 +++++++++++++++++
 src/nvim/testdir/test_cmdline.vim     |  2 ++
 src/nvim/testdir/test_functions.vim   |  1 +
 src/nvim/testdir/test_listdict.vim    |  1 +
 src/nvim/testdir/test_options.vim     |  1 +
 src/nvim/testdir/test_search_stat.vim |  8 ++++++++
 src/nvim/testdir/test_sort.vim        |  1 +
 src/nvim/testdir/test_substitute.vim  |  1 +
 src/nvim/testdir/test_syntax.vim      |  2 ++
 src/nvim/testdir/test_tagjump.vim     |  4 ++++
 src/nvim/testdir/test_user_func.vim   |  3 +++
 13 files changed, 43 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 50904bab34..1488fe8431 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -2865,6 +2865,7 @@ func Test_autocmd_invalid_args()
   call assert_fails('doautocmd * BufEnter', 'E217:')
   call assert_fails('augroup! x1a2b3', 'E367:')
   call assert_fails('autocmd BufNew  pwd', 'E680:')
+  call assert_fails('autocmd BufNew \) set ff=unix', 'E55:')
 endfunc
 
 " Test for deep nesting of autocmds
diff --git a/src/nvim/testdir/test_buffer.vim b/src/nvim/testdir/test_buffer.vim
index 27c2d5d442..f4aa3b5238 100644
--- a/src/nvim/testdir/test_buffer.vim
+++ b/src/nvim/testdir/test_buffer.vim
@@ -147,6 +147,7 @@ func Test_bdelete_cmd()
   %bwipe!
   call assert_fails('bdelete 5', 'E516:')
   call assert_fails('1,1bdelete 1 2', 'E488:')
+  call assert_fails('bdelete \)', 'E55:')
 
   " Deleting a unlisted and unloaded buffer
   edit Xfile1
diff --git a/src/nvim/testdir/test_checkpath.vim b/src/nvim/testdir/test_checkpath.vim
index eff30cf205..f6a3bbce08 100644
--- a/src/nvim/testdir/test_checkpath.vim
+++ b/src/nvim/testdir/test_checkpath.vim
@@ -102,3 +102,20 @@ func Test_checkpath3()
   set include&
   set includeexpr&
 endfunc
+
+" Test for invalid regex in 'include' and 'define' options
+func Test_checkpath_errors()
+  let save_include = &include
+  set include=\\%(
+  call assert_fails('checkpath', 'E53:')
+  let &include = save_include
+
+  let save_define = &define
+  set define=\\%(
+  call assert_fails('dsearch abc', 'E53:')
+  let &define = save_define
+
+  call assert_fails('psearch \%(', 'E53:')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index c00172ed68..7cef3117c2 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -316,6 +316,8 @@ func Test_map_completion()
   unmap 
   " set cpo-=k
 
+  call assert_fails('call feedkeys(":map \\\\%(\\\"\", "xt")', 'E53:')
+
   unmap x
   set cpo&vim
 endfunc
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 743022ece4..b464f6591d 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -941,6 +941,7 @@ func Test_match_func()
   call assert_equal(4,  match('testing', 'ing', -1))
   call assert_fails("let x=match('testing', 'ing', 0, [])", 'E745:')
   call assert_equal(-1, match(v:_null_list, 2))
+  call assert_equal(-1, match('abc', '\\%('))
 endfunc
 
 func Test_matchend()
diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim
index 3d90222b90..7ea488f41d 100644
--- a/src/nvim/testdir/test_listdict.vim
+++ b/src/nvim/testdir/test_listdict.vim
@@ -754,6 +754,7 @@ func Test_str_split()
   call assert_equal(['', 'a', '', 'b', '', 'c', ''], split('abc', '\zs', 1))
   call assert_fails("call split('abc', [])", 'E730:')
   call assert_fails("call split('abc', 'b', [])", 'E745:')
+  call assert_equal(['abc'], split('abc', '\\%('))
 endfunc
 
 " compare recursively linked list and dict
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index 3916d7c554..80569bde03 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -399,6 +399,7 @@ func Test_set_errors()
     call assert_fails('set pyxversion=6', 'E474:')
   endif
   call assert_fails("let &tabstop='ab'", 'E521:')
+  call assert_fails('set spellcapcheck=%\\(', 'E54:')
   call assert_fails('set sessionoptions=curdir,sesdir', 'E474:')
   call assert_fails('set foldmarker={{{,', 'E474:')
   call assert_fails('set sessionoptions=sesdir,curdir', 'E474:')
diff --git a/src/nvim/testdir/test_search_stat.vim b/src/nvim/testdir/test_search_stat.vim
index 89e09cf85b..77bd50ada2 100644
--- a/src/nvim/testdir/test_search_stat.vim
+++ b/src/nvim/testdir/test_search_stat.vim
@@ -260,6 +260,14 @@ endfunc
 
 func Test_searchcount_fails()
   call assert_fails('echo searchcount("boo!")', 'E715:')
+  call assert_fails('echo searchcount({"timeout" : []})', 'E745:')
+  call assert_fails('echo searchcount({"maxcount" : []})', 'E745:')
+  call assert_fails('echo searchcount({"pattern" : []})', 'E730:')
+  call assert_fails('echo searchcount({"pos" : 1})', 'E475:')
+  call assert_fails('echo searchcount({"pos" : [1]})', 'E475:')
+  call assert_fails('echo searchcount({"pos" : [[], 2, 3]})', 'E745:')
+  call assert_fails('echo searchcount({"pos" : [1, [], 3]})', 'E745:')
+  call assert_fails('echo searchcount({"pos" : [1, 2, []]})', 'E745:')
 endfunc
 
 func Test_searchcount_in_statusline()
diff --git a/src/nvim/testdir/test_sort.vim b/src/nvim/testdir/test_sort.vim
index f9cbcbb55f..534393b724 100644
--- a/src/nvim/testdir/test_sort.vim
+++ b/src/nvim/testdir/test_sort.vim
@@ -1361,6 +1361,7 @@ func Test_sort_cmd()
   call assert_fails('sort no', 'E474:')
   call assert_fails('sort c', 'E475:')
   call assert_fails('sort #pat%', 'E654:')
+  call assert_fails('sort /\%(/', 'E53:')
 
   enew!
 endfunc
diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim
index 5b476ddd7f..5fd30c7da7 100644
--- a/src/nvim/testdir/test_substitute.vim
+++ b/src/nvim/testdir/test_substitute.vim
@@ -457,6 +457,7 @@ func Test_substitute_errors()
   call assert_fails("let s=substitute('abcda', [], 'A', 'g')", 'E730:')
   call assert_fails("let s=substitute('abcda', 'a', [], 'g')", 'E730:')
   call assert_fails("let s=substitute('abcda', 'a', 'A', [])", 'E730:')
+  call assert_fails("let s=substitute('abc', '\\%(', 'A', 'g')", 'E53:')
 
   bwipe!
 endfunc
diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim
index 9d108c6ca2..45230c4208 100644
--- a/src/nvim/testdir/test_syntax.vim
+++ b/src/nvim/testdir/test_syntax.vim
@@ -407,6 +407,7 @@ func Test_syntax_invalid_arg()
   call AssertFails('syntax cluster contains=Abc', 'E400:')
   call AssertFails("syntax match Character /'.'", 'E401:')
   call AssertFails("syntax match Character /'.'/a", 'E402:')
+  call assert_fails('syntax sync linecont /\%(/', 'E53:')
   call assert_fails('syntax sync linecont /pat', 'E404:')
   call assert_fails('syntax sync linecont', 'E404:')
   call assert_fails('syntax sync linecont /pat1/ linecont /pat2/', 'E403:')
@@ -416,6 +417,7 @@ func Test_syntax_invalid_arg()
   call AssertFails('syntax match ccFoo "Foo" nextgroup=ALLBUT,F', 'E407:')
   call AssertFails('syntax region Block start="{" contains=F,ALLBUT', 'E408:')
   call AssertFails("syntax match Characters contains=a.*x /'.'/", 'E409:')
+  call assert_fails('syntax match Search /abc/ contains=ALLBUT,/\%(/', 'E53:')
 endfunc
 
 func Test_syn_sync()
diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim
index 61bf9e6d0d..bfc61e7b48 100644
--- a/src/nvim/testdir/test_tagjump.vim
+++ b/src/nvim/testdir/test_tagjump.vim
@@ -184,6 +184,10 @@ function Test_keyword_jump()
   call search("start")
   exe "normal! 5\\"
   call assert_equal("		start OK if found this line", getline('.'))
+
+  " invalid tag search pattern
+  call assert_fails('tag /\%(/', 'E426:')
+
   enew! | only
   call delete('Xtestfile')
   call delete('Xinclude')
diff --git a/src/nvim/testdir/test_user_func.vim b/src/nvim/testdir/test_user_func.vim
index 7aa21d7816..f13c082814 100644
--- a/src/nvim/testdir/test_user_func.vim
+++ b/src/nvim/testdir/test_user_func.vim
@@ -413,6 +413,9 @@ func Test_func_def_error()
   call writefile(['func foo#Bar()', 'return 1', 'endfunc'], 'Xscript')
   call assert_fails('source Xscript', 'E746:')
   call delete('Xscript')
+
+  " Try to list functions using an invalid search pattern
+  call assert_fails('function /\%(/', 'E53:')
 endfunc
 
 " Test for deleting a function
-- 
cgit 


From 7add38233e53f9813b50558b00644db669b6b309 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 16:31:33 +0800
Subject: vim-patch:8.2.3719: MS-Windows: test sometimes runs into existing
 swap file

Problem:    MS-Windows: test sometimes runs into existing swap file.
Solution:   Use a different file name.

https://github.com/vim/vim/commit/f8bc0ce2671d7f7f73760f665b52e4f00a1bbcac

Co-authored-by: Bram Moolenaar 
---
 src/nvim/testdir/test_buffer.vim | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_buffer.vim b/src/nvim/testdir/test_buffer.vim
index f4aa3b5238..d8d22ff872 100644
--- a/src/nvim/testdir/test_buffer.vim
+++ b/src/nvim/testdir/test_buffer.vim
@@ -402,12 +402,12 @@ func Test_buffer_scheme()
   set noshellslash
   %bwipe!
   let bufnames = [
-    \ #{id: 'b0', name: 'test://xyz/foo/b0'    , match: 1},
-    \ #{id: 'b1', name: 'test+abc://xyz/foo/b1', match: 0},
-    \ #{id: 'b2', name: 'test_abc://xyz/foo/b2', match: 0},
-    \ #{id: 'b3', name: 'test-abc://xyz/foo/b3', match: 1},
-    \ #{id: 'b4', name: '-test://xyz/foo/b4'   , match: 0},
-    \ #{id: 'b5', name: 'test-://xyz/foo/b5'   , match: 0},
+    \ #{id: 'ssb0', name: 'test://xyz/foo/ssb0'    , match: 1},
+    \ #{id: 'ssb1', name: 'test+abc://xyz/foo/ssb1', match: 0},
+    \ #{id: 'ssb2', name: 'test_abc://xyz/foo/ssb2', match: 0},
+    \ #{id: 'ssb3', name: 'test-abc://xyz/foo/ssb3', match: 1},
+    \ #{id: 'ssb4', name: '-test://xyz/foo/ssb4'   , match: 0},
+    \ #{id: 'ssb5', name: 'test-://xyz/foo/ssb5'   , match: 0},
     \]
   for buf in bufnames
     new `=buf.name`
-- 
cgit 


From 70843631fe8d2e8e93e6ecf7d08c7824352938b7 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 16:46:23 +0800
Subject: vim-patch:8.2.3744: E854 is not tested; some spelling suggestions are
 not tested
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Problem:    E854 is not tested; some spelling suggestions are not tested.
Solution:   Add a couple of tests. (Dominique Pellé, closes vim/vim#9279)

https://github.com/vim/vim/commit/f645ee47c85940d05f492a1b3932fbcdfd4204b3

Add missing Test_signcolumn() from patch 7.4.2201.

Co-authored-by: Dominique Pelle 
---
 src/nvim/testdir/test_options.vim | 14 ++++++++++++++
 src/nvim/testdir/test_spell.vim   |  5 +++++
 2 files changed, 19 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index 80569bde03..d689ece70d 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -157,6 +157,20 @@ func Test_path_keep_commas()
   set path&
 endfunc
 
+func Test_path_too_long()
+  exe 'set path=' .. repeat('x', 10000)
+  call assert_fails('find x', 'E854:')
+  set path&
+endfunc
+
+func Test_signcolumn()
+  CheckFeature signs
+  call assert_equal("auto", &signcolumn)
+  set signcolumn=yes
+  set signcolumn=no
+  call assert_fails('set signcolumn=nope')
+endfunc
+
 func Test_filetype_valid()
   set ft=valid_name
   call assert_equal("valid_name", &filetype)
diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim
index d8495fdb9b..ea18fc5194 100644
--- a/src/nvim/testdir/test_spell.vim
+++ b/src/nvim/testdir/test_spell.vim
@@ -329,6 +329,11 @@ func Test_spellsuggest()
   call assert_equal(['Third'], spellsuggest('THird', 1))
   call assert_equal(['All'],      spellsuggest('ALl', 1))
 
+  " Special suggestion for repeated 'the the'.
+  call assert_inrange(0, 2, index(spellsuggest('the the',   3), 'the'))
+  call assert_inrange(0, 2, index(spellsuggest('the   the', 3), 'the'))
+  call assert_inrange(0, 2, index(spellsuggest('The the',   3), 'The'))
+
   call assert_fails("call spellsuggest('maxch', [])", 'E745:')
   call assert_fails("call spellsuggest('maxch', 2, [])", 'E745:')
 
-- 
cgit 


From a79d28e4d7939c13f38cf4ce63ff240011bca96d Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 17:58:13 +0800
Subject: vim-patch:9.0.0265: no good reason why the "gf" command isn't in the
 tiny version (#20964)

Problem:    No good reason why the "gf" command is not in the tiny version.
Solution:   Graduate the file_in_path feature.

https://github.com/vim/vim/commit/f80f40a55ccff0a4331c5fbd1ac446511f622ed0

Co-authored-by: Bram Moolenaar 
---
 src/nvim/option.c                 | 3 +--
 src/nvim/testdir/test_options.vim | 1 -
 src/nvim/window.c                 | 1 +
 3 files changed, 2 insertions(+), 3 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/option.c b/src/nvim/option.c
index 306a63b74d..523ae13e52 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -4702,8 +4702,7 @@ void set_context_in_set_cmd(expand_T *xp, char_u *arg, int opt_flags)
         || p == (char_u *)&p_cdpath
         || p == (char_u *)&p_vdir) {
       xp->xp_context = EXPAND_DIRECTORIES;
-      if (p == (char_u *)&p_path
-          || p == (char_u *)&p_cdpath) {
+      if (p == (char_u *)&p_path || p == (char_u *)&p_cdpath) {
         xp->xp_backslash = XP_BS_THREE;
       } else {
         xp->xp_backslash = XP_BS_ONE;
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index d689ece70d..db544e47b8 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -895,7 +895,6 @@ endfunc
 
 " Test for the default CDPATH option
 func Test_opt_default_cdpath()
-  CheckFeature file_in_path
   let after =<< trim [CODE]
     call assert_equal(',/path/to/dir1,/path/to/dir2', &cdpath)
     call writefile(v:errors, 'Xtestout')
diff --git a/src/nvim/window.c b/src/nvim/window.c
index c755f58c4f..10b366ce23 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -585,6 +585,7 @@ wingotofile:
       cmdmod.cmod_tab = tabpage_index(curtab) + 1;
       nchar = xchar;
       goto wingotofile;
+
     case 't':                       // CTRL-W gt: go to next tab page
       goto_tabpage((int)Prenum);
       break;
-- 
cgit 


From 731cdde28ea8d48cc23ba2752a08c261c87eee92 Mon Sep 17 00:00:00 2001
From: dundargoc 
Date: Sat, 22 Oct 2022 12:36:38 +0200
Subject: refactor: fix clang-tidy warnings

Enable and fix bugprone-misplaced-widening-cast warning.

Fix some modernize-macro-to-enum and readability-else-after-return
warnings, but don't enable them. While the warnings can be useful, they
are in general too noisy to enable.
---
 src/nvim/api/extmark.c     |   3 +-
 src/nvim/api/tabpage.c     |  13 +++--
 src/nvim/api/vimscript.c   |   2 +-
 src/nvim/arabic.c          | 130 +++++++++++++++++++++++----------------------
 src/nvim/decoration.c      |  11 ++--
 src/nvim/diff.c            |  59 ++++++++++----------
 src/nvim/edit.c            |  13 ++---
 src/nvim/eval.c            |  13 ++---
 src/nvim/eval/decode.c     |  11 ++--
 src/nvim/eval/funcs.c      |   8 ++-
 src/nvim/eval/typval.c     |   3 +-
 src/nvim/eval/userfunc.c   |   3 +-
 src/nvim/eval/vars.c       |   6 +--
 src/nvim/ex_cmds.c         |   3 +-
 src/nvim/ex_docmd.c        |   6 +--
 src/nvim/fileio.c          |  44 +++++++--------
 src/nvim/fold.c            |  14 ++---
 src/nvim/hardcopy.c        | 120 +++++++++++++++++++++++------------------
 src/nvim/highlight.c       |  14 +++--
 src/nvim/highlight_group.c |  11 ++--
 src/nvim/indent.c          |   3 +-
 src/nvim/indent_c.c        |  18 +++----
 src/nvim/insexpand.c       |  38 ++++++-------
 src/nvim/lua/converter.c   |   3 +-
 src/nvim/lua/stdlib.c      |  15 +++---
 src/nvim/main.c            |  20 ++++---
 src/nvim/mark.c            |   3 +-
 src/nvim/match.c           |  18 +++----
 src/nvim/mbyte.c           |   3 +-
 src/nvim/memfile.c         |   6 ++-
 src/nvim/memline.c         |  52 ++++++++++--------
 src/nvim/memory.c          |   3 +-
 src/nvim/message.c         |   6 ++-
 src/nvim/mouse.c           |   6 +--
 src/nvim/normal.c          |   3 +-
 src/nvim/option.c          |  12 ++---
 src/nvim/os/env.c          |  12 ++---
 src/nvim/os/fs.c           |   9 ++--
 src/nvim/os/input.c        |   3 +-
 src/nvim/path.c            |   6 ++-
 src/nvim/plines.c          |   3 +-
 src/nvim/popupmenu.c       |   9 ++--
 src/nvim/quickfix.c        |  23 ++++----
 src/nvim/shada.c           |  33 ++++++------
 src/nvim/spell.c           |  12 +++--
 src/nvim/spellfile.c       |  67 ++++++++++++-----------
 src/nvim/spellsuggest.c    | 108 +++++++++++++++++++++----------------
 src/nvim/strings.c         |   5 +-
 src/nvim/syntax.c          |  16 +++---
 src/nvim/tag.c             |  21 ++++----
 src/nvim/tui/input.c       |  44 ++++++++-------
 src/nvim/tui/tui.c         |   3 +-
 src/nvim/undo.c            |   3 +-
 src/nvim/usercmd.c         |   6 +--
 src/nvim/window.c          |  26 ++++-----
 55 files changed, 558 insertions(+), 547 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index 3b1b470629..fee6876469 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -833,9 +833,8 @@ uint32_t src2ns(Integer *src_id)
   }
   if (*src_id < 0) {
     return (((uint32_t)1) << 31) - 1;
-  } else {
-    return (uint32_t)(*src_id);
   }
+  return (uint32_t)(*src_id);
 }
 
 /// Adds a highlight to buffer.
diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c
index b81fc3b7d7..31f9fab82a 100644
--- a/src/nvim/api/tabpage.c
+++ b/src/nvim/api/tabpage.c
@@ -110,15 +110,14 @@ Window nvim_tabpage_get_win(Tabpage tabpage, Error *err)
 
   if (tab == curtab) {
     return nvim_get_current_win();
-  } else {
-    FOR_ALL_WINDOWS_IN_TAB(wp, tab) {
-      if (wp == tab->tp_curwin) {
-        return wp->handle;
-      }
+  }
+  FOR_ALL_WINDOWS_IN_TAB(wp, tab) {
+    if (wp == tab->tp_curwin) {
+      return wp->handle;
     }
-    // There should always be a current window for a tabpage
-    abort();
   }
+  // There should always be a current window for a tabpage
+  abort();
 }
 
 /// Gets the tabpage number
diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c
index 71209c9ab6..dccc2d42d8 100644
--- a/src/nvim/api/vimscript.c
+++ b/src/nvim/api/vimscript.c
@@ -532,7 +532,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E
       kv_drop(ast_conv_stack, 1);
     } else {
       if (cur_item.ret_node_p->type == kObjectTypeNil) {
-        size_t items_size = (size_t)(3  // "type", "start" and "len"
+        size_t items_size = (size_t)(3  // "type", "start" and "len"  // NOLINT(bugprone-misplaced-widening-cast)
                                      + (node->children != NULL)  // "children"
                                      + (node->type == kExprNodeOption
                                         || node->type == kExprNodePlainIdentifier)  // "scope"
diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c
index b57019286f..ac6269493d 100644
--- a/src/nvim/arabic.c
+++ b/src/nvim/arabic.c
@@ -27,70 +27,72 @@
 #include "nvim/vim.h"
 
 // Unicode values for Arabic characters.
-#define a_HAMZA                         0x0621
-#define a_ALEF_MADDA                    0x0622
-#define a_ALEF_HAMZA_ABOVE              0x0623
-#define a_WAW_HAMZA                     0x0624
-#define a_ALEF_HAMZA_BELOW              0x0625
-#define a_YEH_HAMZA                     0x0626
-#define a_ALEF                          0x0627
-#define a_BEH                           0x0628
-#define a_TEH_MARBUTA                   0x0629
-#define a_TEH                           0x062a
-#define a_THEH                          0x062b
-#define a_JEEM                          0x062c
-#define a_HAH                           0x062d
-#define a_KHAH                          0x062e
-#define a_DAL                           0x062f
-#define a_THAL                          0x0630
-#define a_REH                           0x0631
-#define a_ZAIN                          0x0632
-#define a_SEEN                          0x0633
-#define a_SHEEN                         0x0634
-#define a_SAD                           0x0635
-#define a_DAD                           0x0636
-#define a_TAH                           0x0637
-#define a_ZAH                           0x0638
-#define a_AIN                           0x0639
-#define a_GHAIN                         0x063a
-#define a_TATWEEL                       0x0640
-#define a_FEH                           0x0641
-#define a_QAF                           0x0642
-#define a_KAF                           0x0643
-#define a_LAM                           0x0644
-#define a_MEEM                          0x0645
-#define a_NOON                          0x0646
-#define a_HEH                           0x0647
-#define a_WAW                           0x0648
-#define a_ALEF_MAKSURA                  0x0649
-#define a_YEH                           0x064a
-#define a_FATHATAN                      0x064b
-#define a_DAMMATAN                      0x064c
-#define a_KASRATAN                      0x064d
-#define a_FATHA                         0x064e
-#define a_DAMMA                         0x064f
-#define a_KASRA                         0x0650
-#define a_SHADDA                        0x0651
-#define a_SUKUN                         0x0652
-#define a_MADDA_ABOVE                   0x0653
-#define a_HAMZA_ABOVE                   0x0654
-#define a_HAMZA_BELOW                   0x0655
-
-#define a_PEH                           0x067e
-#define a_TCHEH                         0x0686
-#define a_JEH                           0x0698
-#define a_FKAF                          0x06a9
-#define a_GAF                           0x06af
-#define a_FYEH                          0x06cc
-
-#define a_s_LAM_ALEF_MADDA_ABOVE        0xfef5
-#define a_f_LAM_ALEF_MADDA_ABOVE        0xfef6
-#define a_s_LAM_ALEF_HAMZA_ABOVE        0xfef7
-#define a_f_LAM_ALEF_HAMZA_ABOVE        0xfef8
-#define a_s_LAM_ALEF_HAMZA_BELOW        0xfef9
-#define a_f_LAM_ALEF_HAMZA_BELOW        0xfefa
-#define a_s_LAM_ALEF                    0xfefb
-#define a_f_LAM_ALEF                    0xfefc
+enum {
+  a_HAMZA = 0x0621,
+  a_ALEF_MADDA = 0x0622,
+  a_ALEF_HAMZA_ABOVE = 0x0623,
+  a_WAW_HAMZA = 0x0624,
+  a_ALEF_HAMZA_BELOW = 0x0625,
+  a_YEH_HAMZA = 0x0626,
+  a_ALEF = 0x0627,
+  a_BEH = 0x0628,
+  a_TEH_MARBUTA = 0x0629,
+  a_TEH = 0x062a,
+  a_THEH = 0x062b,
+  a_JEEM = 0x062c,
+  a_HAH = 0x062d,
+  a_KHAH = 0x062e,
+  a_DAL = 0x062f,
+  a_THAL = 0x0630,
+  a_REH = 0x0631,
+  a_ZAIN = 0x0632,
+  a_SEEN = 0x0633,
+  a_SHEEN = 0x0634,
+  a_SAD = 0x0635,
+  a_DAD = 0x0636,
+  a_TAH = 0x0637,
+  a_ZAH = 0x0638,
+  a_AIN = 0x0639,
+  a_GHAIN = 0x063a,
+  a_TATWEEL = 0x0640,
+  a_FEH = 0x0641,
+  a_QAF = 0x0642,
+  a_KAF = 0x0643,
+  a_LAM = 0x0644,
+  a_MEEM = 0x0645,
+  a_NOON = 0x0646,
+  a_HEH = 0x0647,
+  a_WAW = 0x0648,
+  a_ALEF_MAKSURA = 0x0649,
+  a_YEH = 0x064a,
+  a_FATHATAN = 0x064b,
+  a_DAMMATAN = 0x064c,
+  a_KASRATAN = 0x064d,
+  a_FATHA = 0x064e,
+  a_DAMMA = 0x064f,
+  a_KASRA = 0x0650,
+  a_SHADDA = 0x0651,
+  a_SUKUN = 0x0652,
+  a_MADDA_ABOVE = 0x0653,
+  a_HAMZA_ABOVE = 0x0654,
+  a_HAMZA_BELOW = 0x0655,
+
+  a_PEH = 0x067e,
+  a_TCHEH = 0x0686,
+  a_JEH = 0x0698,
+  a_FKAF = 0x06a9,
+  a_GAF = 0x06af,
+  a_FYEH = 0x06cc,
+
+  a_s_LAM_ALEF_MADDA_ABOVE = 0xfef5,
+  a_f_LAM_ALEF_MADDA_ABOVE = 0xfef6,
+  a_s_LAM_ALEF_HAMZA_ABOVE = 0xfef7,
+  a_f_LAM_ALEF_HAMZA_ABOVE = 0xfef8,
+  a_s_LAM_ALEF_HAMZA_BELOW = 0xfef9,
+  a_f_LAM_ALEF_HAMZA_BELOW = 0xfefa,
+  a_s_LAM_ALEF = 0xfefb,
+  a_f_LAM_ALEF = 0xfefc,
+};
 
 static struct achar {
   unsigned c;
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index 5a1708a57c..19e99fa7a6 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -169,13 +169,12 @@ Decoration get_decor(mtkey_t mark)
 {
   if (mark.decor_full) {
     return *mark.decor_full;
-  } else {
-    Decoration fake = DECORATION_INIT;
-    fake.hl_id = mark.hl_id;
-    fake.priority = mark.priority;
-    fake.hl_eol = (mark.flags & MT_FLAG_HL_EOL);
-    return fake;
   }
+  Decoration fake = DECORATION_INIT;
+  fake.hl_id = mark.hl_id;
+  fake.priority = mark.priority;
+  fake.hl_eol = (mark.flags & MT_FLAG_HL_EOL);
+  return fake;
 }
 
 /// @return true if decor has a virtual position (virtual text or ui_watched)
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 503e546562..b20d0976e4 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -1133,37 +1133,36 @@ static int diff_file(diffio_T *dio)
   // Use xdiff for generating the diff.
   if (dio->dio_internal) {
     return diff_file_internal(dio);
-  } else {
-    const size_t len = (strlen(tmp_orig) + strlen(tmp_new) + strlen(tmp_diff)
-                        + strlen(p_srr) + 27);
-    char *const cmd = xmalloc(len);
-
-    // We don't want $DIFF_OPTIONS to get in the way.
-    if (os_getenv("DIFF_OPTIONS")) {
-      os_unsetenv("DIFF_OPTIONS");
-    }
-
-    // Build the diff command and execute it.  Always use -a, binary
-    // differences are of no use.  Ignore errors, diff returns
-    // non-zero when differences have been found.
-    vim_snprintf(cmd, len, "diff %s%s%s%s%s%s%s%s %s",
-                 diff_a_works == kFalse ? "" : "-a ",
-                 "",
-                 (diff_flags & DIFF_IWHITE) ? "-b " : "",
-                 (diff_flags & DIFF_IWHITEALL) ? "-w " : "",
-                 (diff_flags & DIFF_IWHITEEOL) ? "-Z " : "",
-                 (diff_flags & DIFF_IBLANK) ? "-B " : "",
-                 (diff_flags & DIFF_ICASE) ? "-i " : "",
-                 tmp_orig, tmp_new);
-    append_redir(cmd, len, p_srr, tmp_diff);
-    block_autocmds();  // Avoid ShellCmdPost stuff
-    (void)call_shell((char_u *)cmd,
-                     kShellOptFilter | kShellOptSilent | kShellOptDoOut,
-                     NULL);
-    unblock_autocmds();
-    xfree(cmd);
-    return OK;
   }
+  const size_t len = (strlen(tmp_orig) + strlen(tmp_new) + strlen(tmp_diff)
+                      + strlen(p_srr) + 27);
+  char *const cmd = xmalloc(len);
+
+  // We don't want $DIFF_OPTIONS to get in the way.
+  if (os_getenv("DIFF_OPTIONS")) {
+    os_unsetenv("DIFF_OPTIONS");
+  }
+
+  // Build the diff command and execute it.  Always use -a, binary
+  // differences are of no use.  Ignore errors, diff returns
+  // non-zero when differences have been found.
+  vim_snprintf(cmd, len, "diff %s%s%s%s%s%s%s%s %s",
+               diff_a_works == kFalse ? "" : "-a ",
+               "",
+               (diff_flags & DIFF_IWHITE) ? "-b " : "",
+               (diff_flags & DIFF_IWHITEALL) ? "-w " : "",
+               (diff_flags & DIFF_IWHITEEOL) ? "-Z " : "",
+               (diff_flags & DIFF_IBLANK) ? "-B " : "",
+               (diff_flags & DIFF_ICASE) ? "-i " : "",
+               tmp_orig, tmp_new);
+  append_redir(cmd, len, p_srr, tmp_diff);
+  block_autocmds();  // Avoid ShellCmdPost stuff
+  (void)call_shell((char_u *)cmd,
+                   kShellOptFilter | kShellOptSilent | kShellOptDoOut,
+                   NULL);
+  unblock_autocmds();
+  xfree(cmd);
+  return OK;
 }
 
 /// Create a new version of a file from the current buffer and a diff file.
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index c99449e0f3..4540ddf4fe 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -87,10 +87,12 @@ typedef struct insert_state {
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "edit.c.generated.h"
 #endif
-#define BACKSPACE_CHAR              1
-#define BACKSPACE_WORD              2
-#define BACKSPACE_WORD_NOT_SPACE    3
-#define BACKSPACE_LINE              4
+enum {
+  BACKSPACE_CHAR = 1,
+  BACKSPACE_WORD = 2,
+  BACKSPACE_WORD_NOT_SPACE = 3,
+  BACKSPACE_LINE = 4,
+};
 
 /// Set when doing something for completion that may call edit() recursively,
 /// which is not allowed.
@@ -1227,9 +1229,8 @@ bool edit(int cmdchar, bool startln, long count)
       restart_edit = 'i';
       force_restart_edit = true;
       return false;
-    } else {
-      return terminal_enter();
     }
+    return terminal_enter();
   }
 
   // Don't allow inserting in the sandbox.
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index fe4ae92834..f39cebefb2 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -1942,11 +1942,10 @@ bool next_for_item(void *fi_void, char *arg)
   listitem_T *item = fi->fi_lw.lw_item;
   if (item == NULL) {
     return false;
-  } else {
-    fi->fi_lw.lw_item = TV_LIST_ITEM_NEXT(fi->fi_list, item);
-    return (ex_let_vars(arg, TV_LIST_ITEM_TV(item), true,
-                        fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK);
   }
+  fi->fi_lw.lw_item = TV_LIST_ITEM_NEXT(fi->fi_list, item);
+  return (ex_let_vars(arg, TV_LIST_ITEM_TV(item), true,
+                      fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK);
 }
 
 // TODO(ZyX-I): move to eval/ex_cmds
@@ -7172,9 +7171,8 @@ int check_luafunc_name(const char *const str, const bool paren)
   const char *const p = skip_luafunc_name(str);
   if (*p != (paren ? '(' : NUL)) {
     return 0;
-  } else {
-    return (int)(p - str);
   }
+  return (int)(p - str);
 }
 
 /// Handle:
@@ -7920,9 +7918,8 @@ static var_flavour_T var_flavour(char *varname)
       }
     }
     return VAR_FLAVOUR_SHADA;
-  } else {
-    return VAR_FLAVOUR_DEFAULT;
   }
+  return VAR_FLAVOUR_DEFAULT;
 }
 
 /// Iterate over global variables
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c
index 6b7eb2f8f8..f677148e08 100644
--- a/src/nvim/eval/decode.c
+++ b/src/nvim/eval/decode.c
@@ -284,13 +284,12 @@ typval_T decode_string(const char *const s, const size_t len, const TriState has
       }
     }
     return tv;
-  } else {
-    return (typval_T) {
-      .v_type = VAR_STRING,
-      .v_lock = VAR_UNLOCKED,
-      .vval = { .v_string = ((s == NULL || s_allocated) ? (char *)s : xmemdupz(s, len)) },
-    };
   }
+  return (typval_T) {
+    .v_type = VAR_STRING,
+    .v_lock = VAR_UNLOCKED,
+    .vval = { .v_string = ((s == NULL || s_allocated) ? (char *)s : xmemdupz(s, len)) },
+  };
 }
 
 /// Parse JSON double-quoted string
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 26a5c133da..5acc37bfb1 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -5238,10 +5238,9 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
         xfree(failed_dir);
         rettv->vval.v_number = FAIL;
         return;
-      } else {
-        rettv->vval.v_number = OK;
-        return;
       }
+      rettv->vval.v_number = OK;
+      return;
     }
   }
   rettv->vval.v_number = vim_mkdir_emsg(dir, prot);
@@ -7483,9 +7482,8 @@ static void f_serverstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
     if (argvars[0].v_type != VAR_STRING) {
       emsg(_(e_invarg));
       return;
-    } else {
-      address = xstrdup(tv_get_string(argvars));
     }
+    address = xstrdup(tv_get_string(argvars));
   } else {
     address = server_address_new(NULL);
   }
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 13d836071d..e09fda173b 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -1747,9 +1747,8 @@ static bool tv_dict_watcher_matches(DictWatcher *watcher, const char *const key)
   const size_t len = watcher->key_pattern_len;
   if (len && watcher->key_pattern[len - 1] == '*') {
     return strncmp(key, watcher->key_pattern, len - 1) == 0;
-  } else {
-    return strcmp(key, watcher->key_pattern) == 0;
   }
+  return strcmp(key, watcher->key_pattern) == 0;
 }
 
 /// Send a change notification to all dictionary watchers that match given key
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index ef74ca58e3..638573a235 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -2019,9 +2019,8 @@ void ex_function(exarg_T *eap)
       }
       xfree(fudi.fd_newkey);
       return;
-    } else {
-      eap->skip = true;
     }
+    eap->skip = true;
   }
 
   // An error in a function call during evaluation of an expression in magic
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index 139e7ed66b..97630d4953 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -380,9 +380,8 @@ const char *skip_var_list(const char *arg, int *var_count, int *semicolon)
       }
     }
     return p + 1;
-  } else {
-    return skip_var_one((char *)arg);
   }
+  return skip_var_one((char *)arg);
 }
 
 /// Skip one (assignable) variable name, including @r, $VAR, &option, d.key,
@@ -884,9 +883,8 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_
       lp->ll_n1++;
       if (lp->ll_li == NULL || (!lp->ll_empty2 && lp->ll_n2 < lp->ll_n1)) {
         break;
-      } else {
-        last_li = lp->ll_li;
       }
+      last_li = lp->ll_li;
     }
     tv_list_remove_items(lp->ll_list, first_li, last_li);
   } else {
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 89e6d47950..1b850ab425 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -2137,9 +2137,8 @@ static int check_readonly(int *forceit, buf_T *buf)
         // Set forceit, to force the writing of a readonly file
         *forceit = true;
         return false;
-      } else {
-        return true;
       }
+      return true;
     } else if (buf->b_p_ro) {
       emsg(_(e_readonly));
     } else {
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index dc2b7247f1..0c2a92ef43 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -4357,9 +4357,8 @@ char *check_nextcmd(char *p)
 
   if (*s == '|' || *s == '\n') {
     return s + 1;
-  } else {
-    return NULL;
   }
+  return NULL;
 }
 
 /// - if there are more files to edit
@@ -4758,9 +4757,8 @@ static void ex_only(exarg_T *eap)
     for (wp = firstwin; --wnr > 0;) {
       if (wp->w_next == NULL) {
         break;
-      } else {
-        wp = wp->w_next;
       }
+      wp = wp->w_next;
     }
   } else {
     wp = curwin;
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index c780f64a7a..414a32fab3 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -72,15 +72,17 @@
 #endif
 
 #define HAS_BW_FLAGS
-#define FIO_LATIN1     0x01    // convert Latin1
-#define FIO_UTF8       0x02    // convert UTF-8
-#define FIO_UCS2       0x04    // convert UCS-2
-#define FIO_UCS4       0x08    // convert UCS-4
-#define FIO_UTF16      0x10    // convert UTF-16
-#define FIO_ENDIAN_L   0x80    // little endian
-#define FIO_NOCONVERT  0x2000  // skip encoding conversion
-#define FIO_UCSBOM     0x4000  // check for BOM at start of file
-#define FIO_ALL        (-1)    // allow all formats
+enum {
+  FIO_LATIN1 = 0x01,       // convert Latin1
+  FIO_UTF8 = 0x02,         // convert UTF-8
+  FIO_UCS2 = 0x04,         // convert UCS-2
+  FIO_UCS4 = 0x08,         // convert UCS-4
+  FIO_UTF16 = 0x10,        // convert UTF-16
+  FIO_ENDIAN_L = 0x80,     // little endian
+  FIO_NOCONVERT = 0x2000,  // skip encoding conversion
+  FIO_UCSBOM = 0x4000,     // check for BOM at start of file
+  FIO_ALL = -1,            // allow all formats
+};
 
 // When converting, a read() or write() may leave some bytes to be converted
 // for the next call.  The value is guessed...
@@ -498,18 +500,17 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
         return FAIL;
       }
       return OK;                  // a new file is not an error
-    } else {
-      filemess(curbuf, sfname, ((fd == UV_EFBIG) ? _("[File too big]") :
+    }
+    filemess(curbuf, sfname, ((fd == UV_EFBIG) ? _("[File too big]") :
 #if defined(UNIX) && defined(EOVERFLOW)
-                                // libuv only returns -errno
-                                // in Unix and in Windows
-                                // open() does not set
-                                // EOVERFLOW
-                                (fd == -EOVERFLOW) ? _("[File too big]") :
+                              // libuv only returns -errno
+                              // in Unix and in Windows
+                              // open() does not set
+                              // EOVERFLOW
+                              (fd == -EOVERFLOW) ? _("[File too big]") :
 #endif
-                                _("[Permission Denied]")), 0);
-      curbuf->b_p_ro = true;                  // must use "w!" now
-    }
+                              _("[Permission Denied]")), 0);
+    curbuf->b_p_ro = true;                  // must use "w!" now
 
     return FAIL;
   }
@@ -5232,10 +5233,9 @@ static void vim_mktempdir(void)
     if (vim_settempdir(path)) {
       // Successfully created and set temporary directory so stop trying.
       break;
-    } else {
-      // Couldn't set `vim_tempdir` to `path` so remove created directory.
-      os_rmdir(path);
     }
+    // Couldn't set `vim_tempdir` to `path` so remove created directory.
+    os_rmdir(path);
   }
   (void)umask(umask_save);
 }
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 02fbbc20db..dd74971ab5 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -57,9 +57,11 @@ typedef struct {
                                 // folds too
 } fold_T;
 
-#define FD_OPEN         0       // fold is open (nested ones can be closed)
-#define FD_CLOSED       1       // fold is closed
-#define FD_LEVEL        2       // depends on 'foldlevel' (nested folds too)
+enum {
+  FD_OPEN = 0,    // fold is open (nested ones can be closed)
+  FD_CLOSED = 1,  // fold is closed
+  FD_LEVEL = 2,   // depends on 'foldlevel' (nested folds too)
+};
 
 #define MAX_LEVEL       20      // maximum fold depth
 
@@ -1772,7 +1774,7 @@ char *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldinfo
     }
   }
   if (text == NULL) {
-    unsigned long count = (unsigned long)(lnume - lnum + 1);
+    unsigned long count = (unsigned long)(lnume - lnum + 1);  // NOLINT(bugprone-misplaced-widening-cast)
 
     vim_snprintf(buf, FOLD_TEXT_LEN,
                  NGETTEXT("+--%3ld line folded",
@@ -3115,7 +3117,7 @@ static int put_folds_recurse(FILE *fd, garray_T *gap, linenr_T off)
       return FAIL;
     }
     if (fprintf(fd, "%" PRId64 ",%" PRId64 "fold",
-                (int64_t)(fp->fd_top + off),
+                (int64_t)fp->fd_top + off,
                 (int64_t)(fp->fd_top + off + fp->fd_len - 1)) < 0
         || put_eol(fd) == FAIL) {
       return FAIL;
@@ -3136,7 +3138,7 @@ static int put_foldopen_recurse(FILE *fd, win_T *wp, garray_T *gap, linenr_T off
     if (fp->fd_flags != FD_LEVEL) {
       if (!GA_EMPTY(&fp->fd_nested)) {
         // open nested folds while this fold is open
-        if (fprintf(fd, "%" PRId64, (int64_t)(fp->fd_top + off)) < 0
+        if (fprintf(fd, "%" PRId64, (int64_t)fp->fd_top + off) < 0
             || put_eol(fd) == FAIL
             || put_line(fd, "normal! zo") == FAIL) {
           return FAIL;
diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c
index 7345e9cc35..ff59536e8d 100644
--- a/src/nvim/hardcopy.c
+++ b/src/nvim/hardcopy.c
@@ -122,8 +122,10 @@ static const uint32_t cterm_color_16[16] = {
 
 static int current_syn_id;
 
-#define PRCOLOR_BLACK 0
-#define PRCOLOR_WHITE 0xffffff
+enum {
+  PRCOLOR_BLACK = 0,
+  PRCOLOR_WHITE = 0xffffff,
+};
 
 static TriState curr_italic;
 static TriState curr_bold;
@@ -132,13 +134,15 @@ static uint32_t curr_bg;
 static uint32_t curr_fg;
 static int page_count;
 
-#define OPT_MBFONT_USECOURIER  0
-#define OPT_MBFONT_ASCII       1
-#define OPT_MBFONT_REGULAR     2
-#define OPT_MBFONT_BOLD        3
-#define OPT_MBFONT_OBLIQUE     4
-#define OPT_MBFONT_BOLDOBLIQUE 5
-#define OPT_MBFONT_NUM_OPTIONS 6
+enum {
+  OPT_MBFONT_USECOURIER = 0,
+  OPT_MBFONT_ASCII = 1,
+  OPT_MBFONT_REGULAR = 2,
+  OPT_MBFONT_BOLD = 3,
+  OPT_MBFONT_OBLIQUE = 4,
+  OPT_MBFONT_BOLDOBLIQUE = 5,
+  OPT_MBFONT_NUM_OPTIONS = 6,
+};
 
 static option_table_T mbfont_opts[OPT_MBFONT_NUM_OPTIONS] = {
   { "c",       false, 0, NULL, 0, false },
@@ -938,8 +942,10 @@ static colnr_T hardcopy_line(prt_settings_T *psettings, int page_line, prt_pos_T
 // Some of these documents can be found in PDF form on Adobe's web site -
 // http://www.adobe.com
 
-#define PRT_PS_DEFAULT_DPI          (72)    // Default user space resolution
-#define PRT_PS_DEFAULT_FONTSIZE     (10)
+enum {
+  PRT_PS_DEFAULT_DPI = 72,  // Default user space resolution
+  PRT_PS_DEFAULT_FONTSIZE = 10,
+};
 
 #define PRT_MEDIASIZE_LEN  ARRAY_SIZE(prt_mediasize)
 
@@ -960,10 +966,12 @@ static struct prt_mediasize_S prt_mediasize[] = {
   { "tabloid",         792.0, 1224.0 }
 };
 
-#define PRT_PS_FONT_ROMAN       (0)
-#define PRT_PS_FONT_BOLD        (1)
-#define PRT_PS_FONT_OBLIQUE     (2)
-#define PRT_PS_FONT_BOLDOBLIQUE (3)
+enum {
+  PRT_PS_FONT_ROMAN = 0,
+  PRT_PS_FONT_BOLD = 1,
+  PRT_PS_FONT_OBLIQUE = 2,
+  PRT_PS_FONT_BOLDOBLIQUE = 3,
+};
 
 // Standard font metrics for Courier family
 static struct prt_ps_font_S prt_ps_courier_font = {
@@ -984,14 +992,16 @@ static struct prt_ps_font_S prt_ps_mb_font = {
 // Pointer to current font set being used
 static struct prt_ps_font_S *prt_ps_font;
 
-#define CS_JIS_C_1978   (0x01)
-#define CS_JIS_X_1983   (0x02)
-#define CS_JIS_X_1990   (0x04)
-#define CS_NEC          (0x08)
-#define CS_MSWINDOWS    (0x10)
-#define CS_CP932        (0x20)
-#define CS_KANJITALK6   (0x40)
-#define CS_KANJITALK7   (0x80)
+enum {
+  CS_JIS_C_1978 = 0x01,
+  CS_JIS_X_1983 = 0x02,
+  CS_JIS_X_1990 = 0x04,
+  CS_NEC = 0x08,
+  CS_MSWINDOWS = 0x10,
+  CS_CP932 = 0x20,
+  CS_KANJITALK6 = 0x40,
+  CS_KANJITALK7 = 0x80,
+};
 
 // Japanese encodings and charsets
 static struct prt_ps_encoding_S j_encodings[] = {
@@ -1015,13 +1025,15 @@ static struct prt_ps_charset_S j_charsets[] = {
   { "KANJITALK7",  "90pv",     CS_KANJITALK7 }
 };
 
-#define CS_GB_2312_80       (0x01)
-#define CS_GBT_12345_90     (0x02)
-#define CS_GBK2K            (0x04)
-#define CS_SC_MAC           (0x08)
-#define CS_GBT_90_MAC       (0x10)
-#define CS_GBK              (0x20)
-#define CS_SC_ISO10646      (0x40)
+enum {
+  CS_GB_2312_80 = 0x01,
+  CS_GBT_12345_90 = 0x02,
+  CS_GBK2K = 0x04,
+  CS_SC_MAC = 0x08,
+  CS_GBT_90_MAC = 0x10,
+  CS_GBK = 0x20,
+  CS_SC_ISO10646 = 0x40,
+};
 
 // Simplified Chinese encodings and charsets
 static struct prt_ps_encoding_S sc_encodings[] = {
@@ -1043,19 +1055,21 @@ static struct prt_ps_charset_S sc_charsets[] = {
   { "ISO10646",    "UniGB",    CS_SC_ISO10646 }
 };
 
-#define CS_CNS_PLANE_1      (0x01)
-#define CS_CNS_PLANE_2      (0x02)
-#define CS_CNS_PLANE_1_2    (0x04)
-#define CS_B5               (0x08)
-#define CS_ETEN             (0x10)
-#define CS_HK_GCCS          (0x20)
-#define CS_HK_SCS           (0x40)
-#define CS_HK_SCS_ETEN      (0x80)
-#define CS_MTHKL            (0x100)
-#define CS_MTHKS            (0x200)
-#define CS_DLHKL            (0x400)
-#define CS_DLHKS            (0x800)
-#define CS_TC_ISO10646      (0x1000)
+enum {
+  CS_CNS_PLANE_1 = 0x01,
+  CS_CNS_PLANE_2 = 0x02,
+  CS_CNS_PLANE_1_2 = 0x04,
+  CS_B5 = 0x08,
+  CS_ETEN = 0x10,
+  CS_HK_GCCS = 0x20,
+  CS_HK_SCS = 0x40,
+  CS_HK_SCS_ETEN = 0x80,
+  CS_MTHKL = 0x100,
+  CS_MTHKS = 0x200,
+  CS_DLHKL = 0x400,
+  CS_DLHKS = 0x800,
+  CS_TC_ISO10646 = 0x1000,
+};
 
 // Traditional Chinese encodings and charsets
 static struct prt_ps_encoding_S tc_encodings[] = {
@@ -1087,10 +1101,12 @@ static struct prt_ps_charset_S tc_charsets[] = {
   { "ISO10646",    "UniCNS",   CS_TC_ISO10646 }
 };
 
-#define CS_KR_X_1992        (0x01)
-#define CS_KR_MAC           (0x02)
-#define CS_KR_X_1992_MS     (0x04)
-#define CS_KR_ISO10646      (0x08)
+enum {
+  CS_KR_X_1992 = 0x01,
+  CS_KR_MAC = 0x02,
+  CS_KR_X_1992_MS = 0x04,
+  CS_KR_ISO10646 = 0x08,
+};
 
 // Korean encodings and charsets
 static struct prt_ps_encoding_S k_encodings[] = {
@@ -1169,10 +1185,12 @@ static struct prt_ps_mbfont_S prt_ps_mbfonts[] = {
 
 // Data for table based DSC comment recognition, easy to extend if VIM needs to
 // read more comments.
-#define PRT_DSC_MISC_TYPE           (-1)
-#define PRT_DSC_TITLE_TYPE          (1)
-#define PRT_DSC_VERSION_TYPE        (2)
-#define PRT_DSC_ENDCOMMENTS_TYPE    (3)
+enum {
+  PRT_DSC_MISC_TYPE = -1,
+  PRT_DSC_TITLE_TYPE = 1,
+  PRT_DSC_VERSION_TYPE = 2,
+  PRT_DSC_ENDCOMMENTS_TYPE = 3,
+};
 
 #define PRT_DSC_TITLE               "%%Title:"
 #define PRT_DSC_VERSION             "%%Version:"
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index d507f07bca..c992731ee3 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -141,10 +141,9 @@ int hl_get_syn_attr(int ns_id, int idx, HlAttrs at_en)
       || at_en.rgb_ae_attr != 0 || ns_id != 0) {
     return get_attr_entry((HlEntry){ .attr = at_en, .kind = kHlSyntax,
                                      .id1 = idx, .id2 = ns_id });
-  } else {
-    // If all the fields are cleared, clear the attr field back to default value
-    return 0;
   }
+  // If all the fields are cleared, clear the attr field back to default value
+  return 0;
 }
 
 void ns_hl_def(NS ns_id, int hl_id, HlAttrs attrs, int link_id, Dict(highlight) *dict)
@@ -238,12 +237,11 @@ int ns_get_hl(NS *ns_hl, int hl_id, bool link, bool nodefault)
   if (link) {
     if (it.attr_id >= 0) {
       return 0;
-    } else {
-      if (it.link_global) {
-        *ns_hl = 0;
-      }
-      return it.link_id;
     }
+    if (it.link_global) {
+      *ns_hl = 0;
+    }
+    return it.link_id;
   } else {
     return it.attr_id;
   }
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index 9a09118939..a2f36424a3 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -23,9 +23,11 @@
 
 /// \addtogroup SG_SET
 /// @{
-#define SG_CTERM        2       // cterm has been set
-#define SG_GUI          4       // gui has been set
-#define SG_LINK         8       // link has been set
+enum {
+  SG_CTERM = 2,  // cterm has been set
+  SG_GUI = 4,    // gui has been set
+  SG_LINK = 8,   // link has been set
+};
 /// @}
 
 #define MAX_SYN_NAME 200
@@ -1729,9 +1731,8 @@ int syn_name2id(const char *name)
   if (name[0] == '@') {
     // if we look up @aaa.bbb, we have to consider @aaa as well
     return syn_check_group(name, strlen(name));
-  } else {
-    return syn_name2id_len(name, strlen(name));
   }
+  return syn_name2id_len(name, strlen(name));
 }
 
 /// Lookup a highlight group name and return its ID.
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index d372e41459..0bf36c42f4 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -896,9 +896,8 @@ int inindent(int extra)
 
   if (col >= curwin->w_cursor.col + extra) {
     return true;
-  } else {
-    return false;
   }
+  return false;
 }
 
 /// @return  true if the conditions are OK for smart indenting.
diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c
index 5eed2601d5..5a38fb5aae 100644
--- a/src/nvim/indent_c.c
+++ b/src/nvim/indent_c.c
@@ -458,9 +458,8 @@ bool cin_iscase(const char_u *s, bool strict)
         // JS etc.
         if (strict) {
           return false;                 // stop at string
-        } else {
-          return true;
         }
+        return true;
       }
     }
     return false;
@@ -2877,14 +2876,13 @@ int get_c_indent(void)
               // Line below current line is the one that starts a
               // (possibly broken) line ending in a comma.
               break;
-            } else {
-              amount = get_indent();
-              if (curwin->w_cursor.lnum - 1 == ourscope) {
-                // line above is start of the scope, thus current
-                // line is the one that stars a (possibly broken)
-                // line ending in a comma.
-                break;
-              }
+            }
+            amount = get_indent();
+            if (curwin->w_cursor.lnum - 1 == ourscope) {
+              // line above is start of the scope, thus current
+              // line is the one that stars a (possibly broken)
+              // line ending in a comma.
+              break;
             }
           }
 
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index 9f4c02da19..0c57e4f885 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -55,24 +55,26 @@
 
 #define CTRL_X_WANT_IDENT       0x100
 
-#define CTRL_X_NORMAL           0  ///< CTRL-N CTRL-P completion, default
-#define CTRL_X_NOT_DEFINED_YET  1
-#define CTRL_X_SCROLL           2
-#define CTRL_X_WHOLE_LINE       3
-#define CTRL_X_FILES            4
-#define CTRL_X_TAGS             (5 + CTRL_X_WANT_IDENT)
-#define CTRL_X_PATH_PATTERNS    (6 + CTRL_X_WANT_IDENT)
-#define CTRL_X_PATH_DEFINES     (7 + CTRL_X_WANT_IDENT)
-#define CTRL_X_FINISHED         8
-#define CTRL_X_DICTIONARY       (9 + CTRL_X_WANT_IDENT)
-#define CTRL_X_THESAURUS        (10 + CTRL_X_WANT_IDENT)
-#define CTRL_X_CMDLINE          11
-#define CTRL_X_FUNCTION         12
-#define CTRL_X_OMNI             13
-#define CTRL_X_SPELL            14
-#define CTRL_X_LOCAL_MSG        15  ///< only used in "ctrl_x_msgs"
-#define CTRL_X_EVAL             16  ///< for builtin function complete()
-#define CTRL_X_CMDLINE_CTRL_X   17  ///< CTRL-X typed in CTRL_X_CMDLINE
+enum {
+  CTRL_X_NORMAL = 0,  ///< CTRL-N CTRL-P completion, default
+  CTRL_X_NOT_DEFINED_YET = 1,
+  CTRL_X_SCROLL = 2,
+  CTRL_X_WHOLE_LINE = 3,
+  CTRL_X_FILES = 4,
+  CTRL_X_TAGS = (5 + CTRL_X_WANT_IDENT),
+  CTRL_X_PATH_PATTERNS = (6 + CTRL_X_WANT_IDENT),
+  CTRL_X_PATH_DEFINES = (7 + CTRL_X_WANT_IDENT),
+  CTRL_X_FINISHED = 8,
+  CTRL_X_DICTIONARY = (9 + CTRL_X_WANT_IDENT),
+  CTRL_X_THESAURUS = (10 + CTRL_X_WANT_IDENT),
+  CTRL_X_CMDLINE = 11,
+  CTRL_X_FUNCTION = 12,
+  CTRL_X_OMNI = 13,
+  CTRL_X_SPELL = 14,
+  CTRL_X_LOCAL_MSG = 15,       ///< only used in "ctrl_x_msgs"
+  CTRL_X_EVAL = 16,            ///< for builtin function complete()
+  CTRL_X_CMDLINE_CTRL_X = 17,  ///< CTRL-X typed in CTRL_X_CMDLINE
+};
 
 #define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT]
 
diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c
index bdb0719809..d7881350aa 100644
--- a/src/nvim/lua/converter.c
+++ b/src/nvim/lua/converter.c
@@ -906,9 +906,8 @@ Float nlua_pop_Float(lua_State *lstate, Error *err)
   lua_pop(lstate, 1);
   if (table_props.type != kObjectTypeFloat) {
     return 0;
-  } else {
-    return (Float)table_props.val;
   }
+  return (Float)table_props.val;
 }
 
 /// Convert lua table to array without determining whether it is array
diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c
index 6974c6181c..54e2c24523 100644
--- a/src/nvim/lua/stdlib.c
+++ b/src/nvim/lua/stdlib.c
@@ -378,15 +378,14 @@ int nlua_setvar(lua_State *lstate)
     if (di == NULL) {
       // Doesn't exist, nothing to do
       return 0;
-    } else {
-      // Notify watchers
-      if (watched) {
-        tv_dict_watcher_notify(dict, key.data, NULL, &di->di_tv);
-      }
-
-      // Delete the entry
-      tv_dict_item_remove(dict, di);
     }
+    // Notify watchers
+    if (watched) {
+      tv_dict_watcher_notify(dict, key.data, NULL, &di->di_tv);
+    }
+
+    // Delete the entry
+    tv_dict_item_remove(dict, di);
   } else {
     // Update the key
     typval_T tv;
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 07be01da3a..fc88739738 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -88,16 +88,20 @@
 #include "nvim/api/extmark.h"
 
 // values for "window_layout"
-#define WIN_HOR     1       // "-o" horizontally split windows
-#define WIN_VER     2       // "-O" vertically split windows
-#define WIN_TABS    3       // "-p" windows on tab pages
+enum {
+  WIN_HOR = 1,   // "-o" horizontally split windows
+  WIN_VER = 2,   // "-O" vertically split windows
+  WIN_TABS = 3,  // "-p" windows on tab pages
+};
 
 // Values for edit_type.
-#define EDIT_NONE   0       // no edit type yet
-#define EDIT_FILE   1       // file name argument[s] given, use argument list
-#define EDIT_STDIN  2       // read file from stdin
-#define EDIT_TAG    3       // tag name argument given, use tagname
-#define EDIT_QF     4       // start in quickfix mode
+enum {
+  EDIT_NONE = 0,   // no edit type yet
+  EDIT_FILE = 1,   // file name argument[s] given, use argument list
+  EDIT_STDIN = 2,  // read file from stdin
+  EDIT_TAG = 3,    // tag name argument given, use tagname
+  EDIT_QF = 4,     // start in quickfix mode
+};
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "main.c.generated.h"
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index 8172aec543..f9ed80e3ce 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -1479,9 +1479,8 @@ const void *mark_jumplist_iter(const void *const iter, const win_T *const win, x
   *fm = *iter_mark;
   if (iter_mark == &(win->w_jumplist[win->w_jumplistlen - 1])) {
     return NULL;
-  } else {
-    return iter_mark + 1;
   }
+  return iter_mark + 1;
 }
 
 /// Iterate over global marks
diff --git a/src/nvim/match.c b/src/nvim/match.c
index 916bb44d8c..1e77dc3e91 100644
--- a/src/nvim/match.c
+++ b/src/nvim/match.c
@@ -805,17 +805,17 @@ bool get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
                                 || (prevcol > (long)search_hl->startcol
                                     && search_hl->endcol == MAXCOL))) {
     return true;
-  } else {
-    cur = wp->w_match_head;
-    while (cur != NULL) {
-      if (!cur->mit_hl.is_addpos && (prevcol == (long)cur->mit_hl.startcol
-                                     || (prevcol > (long)cur->mit_hl.startcol
-                                         && cur->mit_hl.endcol == MAXCOL))) {
-        return true;
-      }
-      cur = cur->mit_next;
+  }
+  cur = wp->w_match_head;
+  while (cur != NULL) {
+    if (!cur->mit_hl.is_addpos && (prevcol == (long)cur->mit_hl.startcol
+                                   || (prevcol > (long)cur->mit_hl.startcol
+                                       && cur->mit_hl.endcol == MAXCOL))) {
+      return true;
     }
+    cur = cur->mit_next;
   }
+
   return false;
 }
 
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 9e34c7e413..b4b2c4c7eb 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -1186,9 +1186,8 @@ static int utf_convert(int a, const convertStruct *const table, size_t n_items)
       && a <= table[start].rangeEnd
       && (a - table[start].rangeStart) % table[start].step == 0) {
     return a + table[start].offset;
-  } else {
-    return a;
   }
+  return a;
 }
 
 // Return the folded-case equivalent of "a", which is a UCS-4 character.  Uses
diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c
index bb9be0766e..608d194cad 100644
--- a/src/nvim/memfile.c
+++ b/src/nvim/memfile.c
@@ -815,8 +815,10 @@ static bool mf_do_open(memfile_T *mfp, char *fname, int flags)
 /// The number of buckets in the hashtable is increased by a factor of
 /// MHT_GROWTH_FACTOR when the average number of items per bucket
 /// exceeds 2 ^ MHT_LOG_LOAD_FACTOR.
-#define MHT_LOG_LOAD_FACTOR 6
-#define MHT_GROWTH_FACTOR   2   // must be a power of two
+enum {
+  MHT_LOG_LOAD_FACTOR = 6,
+  MHT_GROWTH_FACTOR = 2,  // must be a power of two
+};
 
 /// Initialize an empty hash table.
 static void mf_hash_init(mf_hashtab_T *mht)
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 225e2aeab1..615363e846 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -83,10 +83,12 @@ typedef struct pointer_block PTR_BL;        // contents of a pointer block
 typedef struct data_block DATA_BL;          // contents of a data block
 typedef struct pointer_entry PTR_EN;        // block/line-count pair
 
-#define DATA_ID        (('d' << 8) + 'a')   // data block id
-#define PTR_ID         (('p' << 8) + 't')   // pointer block id
-#define BLOCK0_ID0     'b'                  // block 0 id 0
-#define BLOCK0_ID1     '0'                  // block 0 id 1
+enum {
+  DATA_ID = (('d' << 8) + 'a'),  // data block id
+  PTR_ID = (('p' << 8) + 't'),   // pointer block id
+  BLOCK0_ID0 = 'b',              // block 0 id 0
+  BLOCK0_ID1 = '0',              // block 0 id 1
+};
 
 // pointer to a block, used in a pointer block
 struct pointer_entry {
@@ -134,18 +136,22 @@ struct data_block {
 #define INDEX_SIZE  (sizeof(unsigned))      // size of one db_index entry
 #define HEADER_SIZE (sizeof(DATA_BL) - INDEX_SIZE)  // size of data block header
 
-#define B0_FNAME_SIZE_ORG       900     // what it was in older versions
-#define B0_FNAME_SIZE_NOCRYPT   898     // 2 bytes used for other things
-#define B0_FNAME_SIZE_CRYPT     890     // 10 bytes used for other things
-#define B0_UNAME_SIZE           40
-#define B0_HNAME_SIZE           40
+enum {
+  B0_FNAME_SIZE_ORG = 900,      // what it was in older versions
+  B0_FNAME_SIZE_NOCRYPT = 898,  // 2 bytes used for other things
+  B0_FNAME_SIZE_CRYPT = 890,    // 10 bytes used for other things
+  B0_UNAME_SIZE = 40,
+  B0_HNAME_SIZE = 40,
+};
 // Restrict the numbers to 32 bits, otherwise most compilers will complain.
 // This won't detect a 64 bit machine that only swaps a byte in the top 32
 // bits, but that is crazy anyway.
-#define B0_MAGIC_LONG   0x30313233L
-#define B0_MAGIC_INT    0x20212223L
-#define B0_MAGIC_SHORT  0x10111213L
-#define B0_MAGIC_CHAR   0x55
+enum {
+  B0_MAGIC_LONG = 0x30313233L,
+  B0_MAGIC_INT = 0x20212223L,
+  B0_MAGIC_SHORT = 0x10111213L,
+  B0_MAGIC_CHAR = 0x55,
+};
 
 // Block zero holds all info about the swap file.
 //
@@ -205,10 +211,12 @@ struct block0 {
 static linenr_T lowest_marked = 0;
 
 // arguments for ml_find_line()
-#define ML_DELETE       0x11        // delete line
-#define ML_INSERT       0x12        // insert line
-#define ML_FIND         0x13        // just find the line
-#define ML_FLUSH        0x02        // flush locked block
+enum {
+  ML_DELETE = 0x11,  // delete line
+  ML_INSERT = 0x12,  // insert line
+  ML_FIND = 0x13,    // just find the line
+  ML_FLUSH = 0x02,   // flush locked block
+};
 #define ML_SIMPLE(x)    ((x) & 0x10)  // DEL, INS or FIND
 
 // argument for ml_upd_block0()
@@ -271,7 +279,7 @@ int ml_open(buf_T *buf)
   b0p->b0_id[0] = BLOCK0_ID0;
   b0p->b0_id[1] = BLOCK0_ID1;
   b0p->b0_magic_long = B0_MAGIC_LONG;
-  b0p->b0_magic_int = (int)B0_MAGIC_INT;
+  b0p->b0_magic_int = B0_MAGIC_INT;
   b0p->b0_magic_short = (int16_t)B0_MAGIC_SHORT;
   b0p->b0_magic_char = B0_MAGIC_CHAR;
   xstrlcpy(xstpcpy((char *)b0p->b0_version, "VIM "), Version, 6);
@@ -3375,7 +3383,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
 static int b0_magic_wrong(ZERO_BL *b0p)
 {
   return b0p->b0_magic_long != B0_MAGIC_LONG
-         || b0p->b0_magic_int != (int)B0_MAGIC_INT
+         || b0p->b0_magic_int != B0_MAGIC_INT
          || b0p->b0_magic_short != (int16_t)B0_MAGIC_SHORT
          || b0p->b0_magic_char != B0_MAGIC_CHAR;
 }
@@ -3525,8 +3533,10 @@ void ml_setflags(buf_T *buf)
   }
 }
 
-#define MLCS_MAXL 800   // max no of lines in chunk
-#define MLCS_MINL 400   // should be half of MLCS_MAXL
+enum {
+  MLCS_MAXL = 800,  // max no of lines in chunk
+  MLCS_MINL = 400,  // should be half of MLCS_MAXL
+};
 
 /// Keep information for finding byte offset of a line
 ///
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 16033e9c63..81299d3e25 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -439,9 +439,8 @@ char *xstrdupnul(const char *const str)
 {
   if (str == NULL) {
     return xmallocz(0);
-  } else {
-    return xstrdup(str);
   }
+  return xstrdup(str);
 }
 
 /// A version of memchr that starts the search at `src + len`.
diff --git a/src/nvim/message.c b/src/nvim/message.c
index fa1c8036e6..f274bd7289 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -59,8 +59,10 @@ struct msgchunk_S {
 };
 
 // Magic chars used in confirm dialog strings
-#define DLG_BUTTON_SEP  '\n'
-#define DLG_HOTKEY_CHAR '&'
+enum {
+  DLG_BUTTON_SEP = '\n',
+  DLG_HOTKEY_CHAR = '&',
+};
 
 static int confirm_msg_used = false;            // displaying confirm_msg
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 53431187af..ed69197cde 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -1179,17 +1179,15 @@ retnomove:
       // Don't use start_arrow() if we're in the same window
       if (curwin == old_curwin) {
         return IN_STATUS_LINE;
-      } else {
-        return IN_STATUS_LINE | CURSOR_MOVED;
       }
+      return IN_STATUS_LINE | CURSOR_MOVED;
     }
     if (sep_line_offset) {                          // In (or below) status line
       // Don't use start_arrow() if we're in the same window
       if (curwin == old_curwin) {
         return IN_SEP_LINE;
-      } else {
-        return IN_SEP_LINE | CURSOR_MOVED;
       }
+      return IN_SEP_LINE | CURSOR_MOVED;
     }
 
     curwin->w_cursor.lnum = curwin->w_topline;
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index d142af555a..d558ec6c28 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -4299,9 +4299,8 @@ static void nv_brackets(cmdarg_T *cap)
                         cap->nchar == 's', false, NULL) == 0) {
         clearopbeep(cap->oap);
         break;
-      } else {
-        curwin->w_set_curswant = true;
       }
+      curwin->w_set_curswant = true;
     }
     if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped) {
       foldOpenCursor();
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 523ae13e52..628e1a6581 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -2950,15 +2950,13 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
     if (p->indir & PV_WIN) {
       if (opt_type == SREQ_BUF) {
         return 0;  // Requested buffer-local, not window-local option
-      } else {
-        rv |= SOPT_WIN;
       }
+      rv |= SOPT_WIN;
     } else if (p->indir & PV_BUF) {
       if (opt_type == SREQ_WIN) {
         return 0;  // Requested window-local, not buffer-local option
-      } else {
-        rv |= SOPT_BUF;
       }
+      rv |= SOPT_BUF;
     }
   }
 
@@ -2969,9 +2967,8 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
   if (opt_type == SREQ_GLOBAL) {
     if (p->var == VAR_WIN) {
       return 0;
-    } else {
-      varp = p->var;
     }
+    varp = p->var;
   } else {
     if (opt_type == SREQ_BUF) {
       // Special case: 'modified' is b_changed, but we also want to
@@ -3094,9 +3091,8 @@ char *set_option_value(const char *const name, const long number, const char *co
       }
       if (flags & P_NUM) {
         return set_num_option(opt_idx, varp, numval, NULL, 0, opt_flags);
-      } else {
-        return set_bool_option(opt_idx, varp, (int)numval, opt_flags);
       }
+      return set_bool_option(opt_idx, varp, (int)numval, opt_flags);
     }
   }
   return NULL;
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index faafc546a4..ca6bff662d 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -837,10 +837,9 @@ const void *vim_env_iter(const char delim, const char *const val, const void *co
   if (dirend == NULL) {
     *len = strlen(varval);
     return NULL;
-  } else {
-    *len = (size_t)(dirend - varval);
-    return dirend + 1;
   }
+  *len = (size_t)(dirend - varval);
+  return dirend + 1;
 }
 
 /// Iterates $PATH-like delimited list `val` in reverse order.
@@ -870,11 +869,10 @@ const void *vim_env_iter_rev(const char delim, const char *const val, const void
     *len = varlen;
     *dir = val;
     return NULL;
-  } else {
-    *dir = colon + 1;
-    *len = (size_t)(varend - colon);
-    return colon - 1;
   }
+  *dir = colon + 1;
+  *len = (size_t)(varend - colon);
+  return colon - 1;
 }
 
 /// @param[out] exe_name should be at least MAXPATHL in size
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 68e96eea6e..2a0018da9c 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -121,9 +121,8 @@ bool os_isrealdir(const char *name)
   fs_loop_unlock();
   if (S_ISLNK(request.statbuf.st_mode)) {
     return false;
-  } else {
-    return S_ISDIR(request.statbuf.st_mode);
   }
+  return S_ISDIR(request.statbuf.st_mode);
 }
 
 /// Check if the given path exists and is a directory.
@@ -249,9 +248,8 @@ bool os_can_exe(const char *name, char **abspath, bool use_path)
         && is_executable(name, abspath)) {
 #endif
       return true;
-    } else {
-      return false;
     }
+    return false;
   }
 
   return is_executable_in_path(name, abspath);
@@ -756,9 +754,8 @@ int32_t os_getperm(const char *name)
   int stat_result = os_stat(name, &statbuf);
   if (stat_result == kLibuvSuccess) {
     return (int32_t)statbuf.st_mode;
-  } else {
-    return stat_result;
   }
+  return stat_result;
 }
 
 /// Set the permission of a file.
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index cb0dba8cac..f8c1ee57ea 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -469,9 +469,8 @@ static InbufPollResult inbuf_poll(int ms, MultiQueue *events)
 
   if (input_ready(events)) {
     return kInputAvail;
-  } else {
-    return input_eof ? kInputEof : kInputNone;
   }
+  return input_eof ? kInputEof : kInputNone;
 }
 
 void input_done(void)
diff --git a/src/nvim/path.c b/src/nvim/path.c
index f568ba8854..1413000680 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -33,8 +33,10 @@
 #include "nvim/vim.h"
 #include "nvim/window.h"
 
-#define URL_SLASH       1               // path_is_url() has found ":/"
-#define URL_BACKSLASH   2               // path_is_url() has found ":\\"
+enum {
+  URL_SLASH = 1,      // path_is_url() has found ":/"
+  URL_BACKSLASH = 2,  // path_is_url() has found ":\\"
+};
 
 #ifdef gen_expand_wildcards
 # undef gen_expand_wildcards
diff --git a/src/nvim/plines.c b/src/nvim/plines.c
index bed15f9e36..268e57927b 100644
--- a/src/nvim/plines.c
+++ b/src/nvim/plines.c
@@ -238,9 +238,8 @@ int win_chartabsize(win_T *wp, char *p, colnr_T col)
   buf_T *buf = wp->w_buffer;
   if (*p == TAB && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
     return tabstop_padding(col, buf->b_p_ts, buf->b_p_vts_array);
-  } else {
-    return ptr2cells(p);
   }
+  return ptr2cells(p);
 }
 
 /// Return the number of characters the string 's' will take on the screen,
diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c
index 893ed38e25..db22d50d66 100644
--- a/src/nvim/popupmenu.c
+++ b/src/nvim/popupmenu.c
@@ -762,12 +762,11 @@ static bool pum_set_selected(int n, int repeat)
             if (e == NULL) {
               ml_append(lnum++, (char *)p, 0, false);
               break;
-            } else {
-              *e = NUL;
-              ml_append(lnum++, (char *)p, (int)(e - p + 1), false);
-              *e = '\n';
-              p = e + 1;
             }
+            *e = NUL;
+            ml_append(lnum++, (char *)p, (int)(e - p + 1), false);
+            *e = '\n';
+            p = e + 1;
           }
 
           // Increase the height of the preview window to show the
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 5d101ee415..82c1a50421 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -2153,12 +2153,11 @@ static char *qf_push_dir(char *dirbuf, struct dir_stack_T **stackptr, bool is_fi
 
   if ((*stackptr)->dirname != NULL) {
     return (*stackptr)->dirname;
-  } else {
-    ds_ptr = *stackptr;
-    *stackptr = (*stackptr)->next;
-    xfree(ds_ptr);
-    return NULL;
   }
+  ds_ptr = *stackptr;
+  *stackptr = (*stackptr)->next;
+  xfree(ds_ptr);
+  return NULL;
 }
 
 // pop dirbuf from the directory stack and return previous directory or NULL if
@@ -2706,11 +2705,10 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int
     if (!can_abandon(curbuf, forceit)) {
       no_write_message();
       return FAIL;
-    } else {
-      retval = do_ecmd(qf_ptr->qf_fnum, NULL, NULL, NULL, (linenr_T)1,
-                       ECMD_HIDE + ECMD_SET_HELP,
-                       prev_winid == curwin->handle ? curwin : NULL);
     }
+    retval = do_ecmd(qf_ptr->qf_fnum, NULL, NULL, NULL, (linenr_T)1,
+                     ECMD_HIDE + ECMD_SET_HELP,
+                     prev_winid == curwin->handle ? curwin : NULL);
   } else {
     retval = buflist_getfile(qf_ptr->qf_fnum, (linenr_T)1,
                              GETF_SETMARK | GETF_SWITCH, forceit);
@@ -5140,11 +5138,10 @@ static bool vgr_qflist_valid(win_T *wp, qf_info_T *qi, unsigned qfid, char *titl
       // An autocmd has freed the location list
       emsg(_(e_current_location_list_was_changed));
       return false;
-    } else {
-      // Quickfix list is not found, create a new one.
-      qf_new_list(qi, title);
-      return true;
     }
+    // Quickfix list is not found, create a new one.
+    qf_new_list(qi, title);
+    return true;
   }
   if (qf_restore_list(qi, qfid) == FAIL) {
     return false;
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 40b3429de9..11e468038e 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -3017,10 +3017,9 @@ shada_write_file_open: {}
           assert(sd_reader.close != NULL);
           sd_reader.close(&sd_reader);
           return FAIL;
-        } else {
-          (*wp)++;
-          goto shada_write_file_open;
         }
+        (*wp)++;
+        goto shada_write_file_open;
       } else {
         semsg(_(SERR "System error while opening temporary ShaDa file %s "
                 "for writing: %s"), tempname, os_strerror(error));
@@ -3257,13 +3256,12 @@ static ShaDaReadResult fread_len(ShaDaReadDef *const sd_reader, char *const buff
       semsg(_(SERR "System error while reading ShaDa file: %s"),
             sd_reader->error);
       return kSDReadStatusReadError;
-    } else {
-      semsg(_(RCERR "Error while reading ShaDa file: "
-              "last entry specified that it occupies %" PRIu64 " bytes, "
-              "but file ended earlier"),
-            (uint64_t)length);
-      return kSDReadStatusNotShaDa;
     }
+    semsg(_(RCERR "Error while reading ShaDa file: "
+            "last entry specified that it occupies %" PRIu64 " bytes, "
+            "but file ended earlier"),
+          (uint64_t)length);
+    return kSDReadStatusNotShaDa;
   }
   return kSDReadStatusSuccess;
 }
@@ -3576,16 +3574,15 @@ shada_read_next_item_start:
         entry->type = kSDItemMissing;
       }
       return spm_ret;
-    } else {
-      entry->data.unknown_item.contents = xmalloc(length);
-      const ShaDaReadResult fl_ret =
-        fread_len(sd_reader, entry->data.unknown_item.contents, length);
-      if (fl_ret != kSDReadStatusSuccess) {
-        shada_free_shada_entry(entry);
-        entry->type = kSDItemMissing;
-      }
-      return fl_ret;
     }
+    entry->data.unknown_item.contents = xmalloc(length);
+    const ShaDaReadResult fl_ret =
+      fread_len(sd_reader, entry->data.unknown_item.contents, length);
+    if (fl_ret != kSDReadStatusSuccess) {
+      shada_free_shada_entry(entry);
+      entry->type = kSDItemMissing;
+    }
+    return fl_ret;
   }
 
   msgpack_unpacked unpacked;
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index e76ac49b5d..3ca9693609 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -112,11 +112,13 @@
 #include "nvim/vim.h"             // for curwin, strlen, STRLCPY, STRNCMP
 
 // Result values.  Lower number is accepted over higher one.
-#define SP_BANNED       (-1)
-#define SP_RARE         0
-#define SP_OK           1
-#define SP_LOCAL        2
-#define SP_BAD          3
+enum {
+  SP_BANNED = -1,
+  SP_RARE = 0,
+  SP_OK = 1,
+  SP_LOCAL = 2,
+  SP_BAD = 3,
+};
 
 // First language that is loaded, start of the linked list of loaded
 // languages.
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index 7837f242b5..fe154a15f7 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -258,50 +258,55 @@
 
 // Special byte values for .  Some are only used in the tree for
 // postponed prefixes, some only in the other trees.  This is a bit messy...
-#define BY_NOFLAGS      0       // end of word without flags or region; for
-                                // postponed prefix: no 
-#define BY_INDEX        1       // child is shared, index follows
-#define BY_FLAGS        2       // end of word,  byte follows; for
-                                // postponed prefix:  follows
-#define BY_FLAGS2       3       // end of word,  and  bytes
-                                // follow; never used in prefix tree
-#define BY_SPECIAL  BY_FLAGS2   // highest special byte value
+enum {
+  BY_NOFLAGS = 0,  // end of word without flags or region; for postponed prefix: no 
+  BY_INDEX = 1,    // child is shared, index follows
+  BY_FLAGS = 2,    // end of word,  byte follows; for postponed prefix:  follows
+  BY_FLAGS2 = 3,   // end of word,  and  bytes follow; never used in prefix tree
+  BY_SPECIAL = BY_FLAGS2,  // highest special byte value
+};
 
 #define ZERO_FLAG   65009       // used when flag is zero: "0"
 
 // Flags used in .spl file for soundsalike flags.
-#define SAL_F0LLOWUP            1
-#define SAL_COLLAPSE            2
-#define SAL_REM_ACCENTS         4
+enum {
+  SAL_F0LLOWUP = 1,
+  SAL_COLLAPSE = 2,
+  SAL_REM_ACCENTS = 4,
+};
 
 #define VIMSPELLMAGIC "VIMspell"  // string at start of Vim spell file
 #define VIMSPELLMAGICL (sizeof(VIMSPELLMAGIC) - 1)
 #define VIMSPELLVERSION 50
 
 // Section IDs.  Only renumber them when VIMSPELLVERSION changes!
-#define SN_REGION       0       //  section
-#define SN_CHARFLAGS    1       // charflags section
-#define SN_MIDWORD      2       //  section
-#define SN_PREFCOND     3       //  section
-#define SN_REP          4       // REP items section
-#define SN_SAL          5       // SAL items section
-#define SN_SOFO         6       // soundfolding section
-#define SN_MAP          7       // MAP items section
-#define SN_COMPOUND     8       // compound words section
-#define SN_SYLLABLE     9       // syllable section
-#define SN_NOBREAK      10      // NOBREAK section
-#define SN_SUGFILE      11      // timestamp for .sug file
-#define SN_REPSAL       12      // REPSAL items section
-#define SN_WORDS        13      // common words
-#define SN_NOSPLITSUGS  14      // don't split word for suggestions
-#define SN_INFO         15      // info section
-#define SN_NOCOMPOUNDSUGS 16    // don't compound for suggestions
-#define SN_END          255     // end of sections
+enum {
+  SN_REGION = 0,           //  section
+  SN_CHARFLAGS = 1,        // charflags section
+  SN_MIDWORD = 2,          //  section
+  SN_PREFCOND = 3,         //  section
+  SN_REP = 4,              // REP items section
+  SN_SAL = 5,              // SAL items section
+  SN_SOFO = 6,             // soundfolding section
+  SN_MAP = 7,              // MAP items section
+  SN_COMPOUND = 8,         // compound words section
+  SN_SYLLABLE = 9,         // syllable section
+  SN_NOBREAK = 10,         // NOBREAK section
+  SN_SUGFILE = 11,         // timestamp for .sug file
+  SN_REPSAL = 12,          // REPSAL items section
+  SN_WORDS = 13,           // common words
+  SN_NOSPLITSUGS = 14,     // don't split word for suggestions
+  SN_INFO = 15,            // info section
+  SN_NOCOMPOUNDSUGS = 16,  // don't compound for suggestions
+  SN_END = 255,            // end of sections
+};
 
 #define SNF_REQUIRED    1       // : required section
 
-#define CF_WORD         0x01
-#define CF_UPPER        0x02
+enum {
+  CF_WORD = 0x01,
+  CF_UPPER = 0x02,
+};
 
 static char *e_spell_trunc = N_("E758: Truncated spell file");
 static char *e_illegal_character_in_word = N_("E1280: Illegal character in word");
diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c
index 400579a233..28bc77c1e5 100644
--- a/src/nvim/spellsuggest.c
+++ b/src/nvim/spellsuggest.c
@@ -91,45 +91,55 @@ typedef struct {
 #define SUG_MAX_COUNT(su)       (SUG_CLEAN_COUNT(su) + 50)
 
 // score for various changes
-#define SCORE_SPLIT     149     // split bad word
-#define SCORE_SPLIT_NO  249     // split bad word with NOSPLITSUGS
-#define SCORE_ICASE     52      // slightly different case
-#define SCORE_REGION    200     // word is for different region
-#define SCORE_RARE      180     // rare word
-#define SCORE_SWAP      75      // swap two characters
-#define SCORE_SWAP3     110     // swap two characters in three
-#define SCORE_REP       65      // REP replacement
-#define SCORE_SUBST     93      // substitute a character
-#define SCORE_SIMILAR   33      // substitute a similar character
-#define SCORE_SUBCOMP   33      // substitute a composing character
-#define SCORE_DEL       94      // delete a character
-#define SCORE_DELDUP    66      // delete a duplicated character
-#define SCORE_DELCOMP   28      // delete a composing character
-#define SCORE_INS       96      // insert a character
-#define SCORE_INSDUP    67      // insert a duplicate character
-#define SCORE_INSCOMP   30      // insert a composing character
-#define SCORE_NONWORD   103     // change non-word to word char
-
-#define SCORE_FILE      30      // suggestion from a file
-#define SCORE_MAXINIT   350     // Initial maximum score: higher == slower.
-                                // 350 allows for about three changes.
-
-#define SCORE_COMMON1   30      // subtracted for words seen before
-#define SCORE_COMMON2   40      // subtracted for words often seen
-#define SCORE_COMMON3   50      // subtracted for words very often seen
-#define SCORE_THRES2    10      // word count threshold for COMMON2
-#define SCORE_THRES3    100     // word count threshold for COMMON3
+enum {
+  SCORE_SPLIT = 149,     // split bad word
+  SCORE_SPLIT_NO = 249,  // split bad word with NOSPLITSUGS
+  SCORE_ICASE = 52,      // slightly different case
+  SCORE_REGION = 200,    // word is for different region
+  SCORE_RARE = 180,      // rare word
+  SCORE_SWAP = 75,       // swap two characters
+  SCORE_SWAP3 = 110,     // swap two characters in three
+  SCORE_REP = 65,        // REP replacement
+  SCORE_SUBST = 93,      // substitute a character
+  SCORE_SIMILAR = 33,    // substitute a similar character
+  SCORE_SUBCOMP = 33,    // substitute a composing character
+  SCORE_DEL = 94,        // delete a character
+  SCORE_DELDUP = 66,     // delete a duplicated character
+  SCORE_DELCOMP = 28,    // delete a composing character
+  SCORE_INS = 96,        // insert a character
+  SCORE_INSDUP = 67,     // insert a duplicate character
+  SCORE_INSCOMP = 30,    // insert a composing character
+  SCORE_NONWORD = 103,   // change non-word to word char
+};
+
+enum {
+  SCORE_FILE = 30,      // suggestion from a file
+  SCORE_MAXINIT = 350,  // Initial maximum score: higher == slower.
+                        // 350 allows for about three changes.
+};
+
+enum {
+  SCORE_COMMON1 = 30,  // subtracted for words seen before
+  SCORE_COMMON2 = 40,  // subtracted for words often seen
+  SCORE_COMMON3 = 50,  // subtracted for words very often seen
+  SCORE_THRES2 = 10,   // word count threshold for COMMON2
+  SCORE_THRES3 = 100,  // word count threshold for COMMON3
+};
 
 // When trying changed soundfold words it becomes slow when trying more than
 // two changes.  With less than two changes it's slightly faster but we miss a
 // few good suggestions.  In rare cases we need to try three of four changes.
-#define SCORE_SFMAX1    200     // maximum score for first try
-#define SCORE_SFMAX2    300     // maximum score for second try
-#define SCORE_SFMAX3    400     // maximum score for third try
+enum {
+  SCORE_SFMAX1 = 200,  // maximum score for first try
+  SCORE_SFMAX2 = 300,  // maximum score for second try
+  SCORE_SFMAX3 = 400,  // maximum score for third try
+};
 
 #define SCORE_BIG       (SCORE_INS * 3)  // big difference
-#define SCORE_MAXMAX    999999           // accept any score
-#define SCORE_LIMITMAX  350              // for spell_edit_score_limit()
+enum {
+  SCORE_MAXMAX = 999999,  // accept any score
+  SCORE_LIMITMAX = 350,   // for spell_edit_score_limit()
+};
 
 // for spell_edit_score_limit() we need to know the minimum value of
 // SCORE_ICASE, SCORE_SWAP, SCORE_DEL, SCORE_SIMILAR and SCORE_INS
@@ -186,19 +196,25 @@ typedef struct trystate_S {
 } trystate_T;
 
 // values for ts_isdiff
-#define DIFF_NONE       0       // no different byte (yet)
-#define DIFF_YES        1       // different byte found
-#define DIFF_INSERT     2       // inserting character
+enum {
+  DIFF_NONE = 0,    // no different byte (yet)
+  DIFF_YES = 1,     // different byte found
+  DIFF_INSERT = 2,  // inserting character
+};
 
 // values for ts_flags
-#define TSF_PREFIXOK    1       // already checked that prefix is OK
-#define TSF_DIDSPLIT    2       // tried split at this point
-#define TSF_DIDDEL      4       // did a delete, "ts_delidx" has index
+enum {
+  TSF_PREFIXOK = 1,  // already checked that prefix is OK
+  TSF_DIDSPLIT = 2,  // tried split at this point
+  TSF_DIDDEL = 4,    // did a delete, "ts_delidx" has index
+};
 
 // special values ts_prefixdepth
-#define PFD_NOPREFIX    0xff    // not using prefixes
-#define PFD_PREFIXTREE  0xfe    // walking through the prefix tree
-#define PFD_NOTSPECIAL  0xfd    // highest value that's not special
+enum {
+  PFD_NOPREFIX = 0xff,    // not using prefixes
+  PFD_PREFIXTREE = 0xfe,  // walking through the prefix tree
+  PFD_NOTSPECIAL = 0xfd,  // highest value that's not special
+};
 
 static long spell_suggest_timeout = 5000;
 
@@ -341,9 +357,11 @@ static int bytes2offset(char_u **pp)
 }
 
 // values for sps_flags
-#define SPS_BEST    1
-#define SPS_FAST    2
-#define SPS_DOUBLE  4
+enum {
+  SPS_BEST = 1,
+  SPS_FAST = 2,
+  SPS_DOUBLE = 4,
+};
 
 static int sps_flags = SPS_BEST;  ///< flags from 'spellsuggest'
 static int sps_limit = 9999;      ///< max nr of suggestions given
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index 4d1401293b..806e1eb5ec 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -604,10 +604,9 @@ static const void *tv_ptr(const typval_T *const tvs, int *const idxp)
   if (tvs[idx].v_type == VAR_UNKNOWN) {
     emsg(_(e_printf));
     return NULL;
-  } else {
-    (*idxp)++;
-    return tvs[idx].vval.v_string;
   }
+  (*idxp)++;
+  return tvs[idx].vval.v_string;
 }
 
 /// Get float argument from idxp entry in tvs
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index fb82df4fe9..f49cbaa39c 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -3123,22 +3123,20 @@ static void syn_cmd_clear(exarg_T *eap, int syncing)
         if (id == 0) {
           semsg(_("E391: No such syntax cluster: %s"), arg);
           break;
-        } else {
-          // We can't physically delete a cluster without changing
-          // the IDs of other clusters, so we do the next best thing
-          // and make it empty.
-          int scl_id = id - SYNID_CLUSTER;
-
-          XFREE_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
         }
+        // We can't physically delete a cluster without changing
+        // the IDs of other clusters, so we do the next best thing
+        // and make it empty.
+        int scl_id = id - SYNID_CLUSTER;
+
+        XFREE_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
       } else {
         id = syn_name2id_len(arg, (size_t)(arg_end - arg));
         if (id == 0) {
           semsg(_(e_nogroup), arg);
           break;
-        } else {
-          syn_clear_one(id, syncing);
         }
+        syn_clear_one(id, syncing);
       }
       arg = skipwhite(arg_end);
     }
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 264f961b43..607446a8a1 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -81,14 +81,16 @@ typedef struct {
 // ht_match[] is used to find duplicates, ga_match[] to keep them in sequence.
 // At the end, the matches from ga_match[] are concatenated, to make a list
 // sorted on priority.
-#define MT_ST_CUR       0               // static match in current file
-#define MT_GL_CUR       1               // global match in current file
-#define MT_GL_OTH       2               // global match in other file
-#define MT_ST_OTH       3               // static match in other file
-#define MT_IC_OFF       4               // add for icase match
-#define MT_RE_OFF       8               // add for regexp match
-#define MT_MASK         7               // mask for printing priority
-#define MT_COUNT        16
+enum {
+  MT_ST_CUR = 0,  // static match in current file
+  MT_GL_CUR = 1,  // global match in current file
+  MT_GL_OTH = 2,  // global match in other file
+  MT_ST_OTH = 3,  // static match in other file
+  MT_IC_OFF = 4,  // add for icase match
+  MT_RE_OFF = 8,  // add for regexp match
+  MT_MASK = 7,    // mask for printing priority
+  MT_COUNT = 16,
+};
 
 static char *mt_names[MT_COUNT/2] =
 { "FSC", "F C", "F  ", "FS ", " SC", "  C", "   ", " S " };
@@ -1595,9 +1597,8 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc
                                               - search_info.low_offset) / 2);
           if (offset == search_info.curr_offset) {
             break;              // End the binary search without a match.
-          } else {
-            search_info.curr_offset = offset;
           }
+          search_info.curr_offset = offset;
         } else if (state == TS_SKIP_BACK) {
           // Skipping back (after a match during binary search).
           search_info.curr_offset -= lbuf_size * 2;
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index fbeca26274..2089686e5e 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -324,15 +324,14 @@ static void forward_simple_utf8(TermInput *input, TermKeyKey *key)
       && map_has(KittyKey, cstr_t)(&kitty_key_map, (KittyKey)key->code.codepoint)) {
     handle_kitty_key_protocol(input, key);
     return;
-  } else {
-    while (*ptr) {
-      if (*ptr == '<') {
-        len += (size_t)snprintf(buf + len, sizeof(buf) - len, "");
-      } else {
-        buf[len++] = *ptr;
-      }
-      ptr++;
+  }
+  while (*ptr) {
+    if (*ptr == '<') {
+      len += (size_t)snprintf(buf + len, sizeof(buf) - len, "");
+    } else {
+      buf[len++] = *ptr;
     }
+    ptr++;
   }
 
   tinput_enqueue(input, buf, len);
@@ -355,21 +354,20 @@ static void forward_modified_utf8(TermInput *input, TermKeyKey *key)
                                      (KittyKey)key->code.codepoint)) {
       handle_kitty_key_protocol(input, key);
       return;
-    } else {
-      // Termkey doesn't include the S- modifier for ASCII characters (e.g.,
-      // ctrl-shift-l is  instead of .  Vim, on the other hand,
-      // treats  and  the same, requiring the S- modifier.
-      len = termkey_strfkey(input->tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM);
-      if ((key->modifiers & TERMKEY_KEYMOD_CTRL)
-          && !(key->modifiers & TERMKEY_KEYMOD_SHIFT)
-          && ASCII_ISUPPER(key->code.codepoint)) {
-        assert(len <= 62);
-        // Make room for the S-
-        memmove(buf + 3, buf + 1, len - 1);
-        buf[1] = 'S';
-        buf[2] = '-';
-        len += 2;
-      }
+    }
+    // Termkey doesn't include the S- modifier for ASCII characters (e.g.,
+    // ctrl-shift-l is  instead of .  Vim, on the other hand,
+    // treats  and  the same, requiring the S- modifier.
+    len = termkey_strfkey(input->tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM);
+    if ((key->modifiers & TERMKEY_KEYMOD_CTRL)
+        && !(key->modifiers & TERMKEY_KEYMOD_SHIFT)
+        && ASCII_ISUPPER(key->code.codepoint)) {
+      assert(len <= 62);
+      // Make room for the S-
+      memmove(buf + 3, buf + 1, len - 1);
+      buf[1] = 'S';
+      buf[2] = '-';
+      len += 2;
     }
   }
 
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 083677a58e..e2d37860c4 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -1651,9 +1651,8 @@ static void out(void *ctx, const char *str, size_t len)
       // Called by unibi_format(): avoid flush_buf() halfway an escape sequence.
       data->overflow = true;
       return;
-    } else {
-      flush_buf(ui);
     }
+    flush_buf(ui);
   }
 
   memcpy(data->buf + data->bufpos, str, len);
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index ef8dc0775b..d450043d2a 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -2713,9 +2713,8 @@ void ex_undojoin(exarg_T *eap)
   }
   if (get_undolevel(curbuf) < 0) {
     return;                 // no entries, nothing to do
-  } else {
-    curbuf->b_u_synced = false;  // Append next change to last entry
   }
+  curbuf->b_u_synced = false;  // Append next change to last entry
 }
 
 /// Called after writing or reloading the file and setting b_changed to false.
diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c
index bed4d55d4e..21a433d855 100644
--- a/src/nvim/usercmd.c
+++ b/src/nvim/usercmd.c
@@ -343,9 +343,8 @@ static char *get_command_complete(int arg)
 {
   if (arg >= (int)(ARRAY_SIZE(command_complete))) {
     return NULL;
-  } else {
-    return (char *)command_complete[arg];
   }
+  return (char *)command_complete[arg];
 }
 
 /// Function given to ExpandGeneric() to obtain the list of values for -complete.
@@ -357,9 +356,8 @@ char *get_user_cmd_complete(expand_T *xp, int idx)
   char *cmd_compl = get_command_complete(idx);
   if (cmd_compl == NULL) {
     return "";
-  } else {
-    return cmd_compl;
   }
+  return cmd_compl;
 }
 
 int cmdcomplete_str_to_type(const char *complete_str)
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 10b366ce23..e1ed61ae93 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -270,9 +270,8 @@ newwindow:
         for (wp = firstwin; --Prenum > 0;) {
           if (wp->w_next == NULL) {
             break;
-          } else {
-            wp = wp->w_next;
           }
+          wp = wp->w_next;
         }
       } else {
         if (nchar == 'W') {  // go to previous window
@@ -872,9 +871,8 @@ int win_fdccol_count(win_T *wp)
     const int fdccol = fdc[4] == ':' ? fdc[5] - '0' : 1;
     int needed_fdccols = getDeepestNesting(wp);
     return MIN(fdccol, needed_fdccols);
-  } else {
-    return fdc[0] - '0';
   }
+  return fdc[0] - '0';
 }
 
 void ui_ext_win_position(win_T *wp, bool validate)
@@ -4869,12 +4867,11 @@ win_T *buf_jump_open_win(buf_T *buf)
   if (curwin->w_buffer == buf) {
     win_enter(curwin, false);
     return curwin;
-  } else {
-    FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
-      if (wp->w_buffer == buf) {
-        win_enter(wp, false);
-        return wp;
-      }
+  }
+  FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+    if (wp->w_buffer == buf) {
+      win_enter(wp, false);
+      return wp;
     }
   }
 
@@ -6642,12 +6639,11 @@ static bool resize_frame_for_winbar(frame_T *fr)
   if (fp == NULL || fp == fr) {
     emsg(_(e_noroom));
     return false;
-  } else {
-    frame_new_height(fp, fp->fr_height - 1, false, false);
-    win_new_height(wp, wp->w_height + 1);
-    frame_fix_height(wp);
-    (void)win_comp_pos();
   }
+  frame_new_height(fp, fp->fr_height - 1, false, false);
+  win_new_height(wp, wp->w_height + 1);
+  frame_fix_height(wp);
+  (void)win_comp_pos();
 
   return true;
 }
-- 
cgit 


From 1af4bd04f9ad157edbfea30642250e854c5cb5d2 Mon Sep 17 00:00:00 2001
From: Raphael 
Date: Sun, 6 Nov 2022 18:59:43 +0800
Subject: feat(ui): add support to display a title in the border of a float
 (#20184)

add "title" and "title_pos" keys to win config dict.
---
 src/nvim/api/keysets.lua   |   2 +
 src/nvim/api/win_config.c  | 118 ++++++++++++++++++++++++++++++++++++++++++++-
 src/nvim/buffer_defs.h     |  12 +++++
 src/nvim/decoration.h      |   1 -
 src/nvim/drawscreen.c      |  31 ++++++++++++
 src/nvim/extmark_defs.h    |   2 +
 src/nvim/highlight_defs.h  |   2 +
 src/nvim/highlight_group.c |   1 +
 src/nvim/window.c          |   4 ++
 9 files changed, 171 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua
index ea8949bd2c..7e0d399573 100644
--- a/src/nvim/api/keysets.lua
+++ b/src/nvim/api/keysets.lua
@@ -81,6 +81,8 @@ return {
     "focusable";
     "zindex";
     "border";
+    "title";
+    "title_pos";
     "style";
     "noautocmd";
   };
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index 636b9566ce..9f15e5a85b 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -2,14 +2,17 @@
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
 #include 
+#include 
 #include 
 #include 
 #include 
 
+#include "nvim/api/extmark.h"
 #include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/api/win_config.h"
 #include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/drawscreen.h"
 #include "nvim/highlight_group.h"
 #include "nvim/option.h"
@@ -134,6 +137,11 @@
 ///     By default, `FloatBorder` highlight is used, which links to `WinSeparator`
 ///     when not defined.  It could also be specified by character:
 ///       [ {"+", "MyCorner"}, {"x", "MyBorder"} ].
+///   - title: Title (optional) in window border, String or list.
+///     List is [text, highlight] tuples. if is string the default
+///     highlight group is `FloatBorderTitle`.
+///   - title_pos: Title position must set with title option.
+///     value can be of `left` `center` `right` default is left.
 ///   - noautocmd: If true then no buffer-related autocommand events such as
 ///                  |BufEnter|, |BufLeave| or |BufWinEnter| may fire from
 ///                  calling this function.
@@ -273,6 +281,21 @@ Dictionary nvim_win_get_config(Window window, Error *err)
         }
       }
       PUT(rv, "border", ARRAY_OBJ(border));
+      if (config->title) {
+        Array titles = ARRAY_DICT_INIT;
+        VirtText title_datas = config->title_chunks;
+        for (size_t i = 0; i < title_datas.size; i++) {
+          Array tuple = ARRAY_DICT_INIT;
+          ADD(tuple, CSTR_TO_OBJ((const char *)title_datas.items[i].text));
+          if (title_datas.items[i].hl_id > 0) {
+            ADD(tuple,
+                STRING_OBJ(cstr_to_string((const char *)syn_id2name(title_datas.items[i].hl_id))));
+          }
+          ADD(titles, ARRAY_OBJ(tuple));
+        }
+        PUT(rv, "title", ARRAY_OBJ(titles));
+        PUT(rv, "title_pos", INTEGER_OBJ(config->title_pos));
+      }
     }
   }
 
@@ -330,7 +353,75 @@ static bool parse_float_bufpos(Array bufpos, lpos_T *out)
   return true;
 }
 
-static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
+static void parse_border_title(Object title, Object title_pos, FloatConfig *fconfig, Error *err)
+{
+  if (!parse_title_pos(title_pos, fconfig, err)) {
+    return;
+  }
+
+  if (title.type == kObjectTypeString) {
+    if (title.data.string.size == 0) {
+      fconfig->title = false;
+      return;
+    }
+    int hl_id = syn_check_group(S_LEN("FloatBorderTitle"));
+    kv_push(fconfig->title_chunks, ((VirtTextChunk){ .text = xstrdup(title.data.string.data),
+                                                     .hl_id = hl_id }));
+    fconfig->title_width = (int)mb_string2cells(title.data.string.data);
+    fconfig->title = true;
+    return;
+  }
+
+  if (title.type != kObjectTypeArray) {
+    api_set_error(err, kErrorTypeValidation, "title must be string or array");
+    return;
+  }
+
+  if (title.type == kObjectTypeArray && title.data.array.size == 0) {
+    api_set_error(err, kErrorTypeValidation, "title cannot be an empty array");
+    return;
+  }
+
+  fconfig->title_width = 0;
+  fconfig->title_chunks = parse_virt_text(title.data.array, err, &fconfig->title_width);
+
+  fconfig->title = true;
+  return;
+}
+
+static bool parse_title_pos(Object title_pos, FloatConfig *fconfig, Error *err)
+{
+  if (!HAS_KEY(title_pos)) {
+    fconfig->title_pos = kAlignLeft;
+    return true;
+  }
+
+  if (title_pos.type != kObjectTypeString) {
+    api_set_error(err, kErrorTypeValidation, "title_pos must be string");
+    return false;
+  }
+
+  if (title_pos.data.string.size == 0) {
+    fconfig->title_pos = kAlignLeft;
+    return true;
+  }
+
+  char *pos = title_pos.data.string.data;
+
+  if (strequal(pos, "left")) {
+    fconfig->title_pos = kAlignLeft;
+  } else if (strequal(pos, "center")) {
+    fconfig->title_pos = kAlignCenter;
+  } else if (strequal(pos, "right")) {
+    fconfig->title_pos = kAlignRight;
+  } else {
+    api_set_error(err, kErrorTypeValidation, "invalid title_pos value");
+    return false;
+  }
+  return true;
+}
+
+static void parse_border_style(Object style,  FloatConfig *fconfig, Error *err)
 {
   struct {
     const char *name;
@@ -414,6 +505,8 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
     String str = style.data.string;
     if (str.size == 0 || strequal(str.data, "none")) {
       fconfig->border = false;
+      // title does not work with border equal none
+      fconfig->title = false;
       return;
     }
     for (size_t i = 0; defaults[i].name; i++) {
@@ -603,6 +696,29 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
     return false;
   }
 
+  if (HAS_KEY(config->title_pos)) {
+    if (!HAS_KEY(config->title)) {
+      api_set_error(err, kErrorTypeException, "title_pos requires title to be set");
+      return false;
+    }
+  }
+
+  if (HAS_KEY(config->title)) {
+    // title only work with border
+    if (!HAS_KEY(config->border) && !fconfig->border) {
+      api_set_error(err, kErrorTypeException, "title requires border to be set");
+      return false;
+    }
+
+    if (fconfig->title) {
+      clear_virttext(&fconfig->title_chunks);
+    }
+    parse_border_title(config->title, config->title_pos, fconfig, err);
+    if (ERROR_SET(err)) {
+      return false;
+    }
+  }
+
   if (HAS_KEY(config->border)) {
     parse_border_style(config->border, fconfig, err);
     if (ERROR_SET(err)) {
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 4e46a1aef2..2b42289858 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -44,6 +44,8 @@ typedef struct {
 #include "klib/kvec.h"
 // for marktree
 #include "nvim/marktree.h"
+// for float window title
+#include "nvim/extmark_defs.h"
 
 #define GETFILE_SUCCESS(x)    ((x) <= 0)
 #define MODIFIABLE(buf) (buf->b_p_ma)
@@ -1048,6 +1050,12 @@ typedef enum {
   kWinStyleMinimal,  /// Minimal UI: no number column, eob markers, etc
 } WinStyle;
 
+typedef enum {
+  kAlignLeft   = 0,
+  kAlignCenter = 1,
+  kAlignRight  = 2,
+} AlignTextPos;
+
 typedef struct {
   Window window;
   lpos_T bufpos;
@@ -1060,10 +1068,14 @@ typedef struct {
   int zindex;
   WinStyle style;
   bool border;
+  bool title;
   bool shadow;
   schar_T border_chars[8];
   int border_hl_ids[8];
   int border_attr[8];
+  AlignTextPos title_pos;
+  VirtText title_chunks;
+  int title_width;
   bool noautocmd;
 } FloatConfig;
 
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index bdbfd72a81..9ba621d7a4 100644
--- a/src/nvim/decoration.h
+++ b/src/nvim/decoration.h
@@ -28,7 +28,6 @@ typedef enum {
 
 EXTERN const char *const hl_mode_str[] INIT(= { "", "replace", "combine", "blend" });
 
-typedef kvec_t(VirtTextChunk) VirtText;
 #define VIRTTEXT_EMPTY ((VirtText)KV_INITIAL_VALUE)
 
 typedef kvec_t(struct virt_line { VirtText line; bool left_col; }) VirtLines;
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index adf52ef6e4..30abcd1a31 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -60,11 +60,13 @@
 #include 
 
 #include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/cmdexpand.h"
 #include "nvim/diff.h"
 #include "nvim/drawscreen.h"
 #include "nvim/ex_getln.h"
+#include "nvim/extmark_defs.h"
 #include "nvim/grid.h"
 #include "nvim/highlight.h"
 #include "nvim/highlight_group.h"
@@ -614,6 +616,20 @@ int update_screen(void)
   return OK;
 }
 
+static void win_border_redr_title(win_T *wp, ScreenGrid *grid, int col)
+{
+  VirtText title_chunks = wp->w_float_config.title_chunks;
+
+  for (size_t i = 0; i < title_chunks.size; i++) {
+    char *text = title_chunks.items[i].text;
+    int cell = (int)mb_string2cells(text);
+    int hl_id = title_chunks.items[i].hl_id;
+    int attr = hl_id ? syn_id2attr(hl_id) : 0;
+    grid_puts(grid, text, 0, col, attr);
+    col += cell;
+  }
+}
+
 static void win_redr_border(win_T *wp)
 {
   wp->w_redr_border = false;
@@ -634,9 +650,24 @@ static void win_redr_border(win_T *wp)
     if (adj[3]) {
       grid_put_schar(grid, 0, 0, chars[0], attrs[0]);
     }
+
     for (int i = 0; i < icol; i++) {
       grid_put_schar(grid, 0, i + adj[3], chars[1], attrs[1]);
     }
+
+    if (wp->w_float_config.title) {
+      int title_col = 0;
+      int title_width = wp->w_float_config.title_width;
+      AlignTextPos title_pos = wp->w_float_config.title_pos;
+
+      if (title_pos == kAlignCenter) {
+        title_col = (icol - title_width) / 2 + 1;
+      } else {
+        title_col = title_pos == kAlignLeft ? 1 : icol - title_width + 1;
+      }
+
+      win_border_redr_title(wp, grid, title_col);
+    }
     if (adj[1]) {
       grid_put_schar(grid, 0, icol + adj[3], chars[2], attrs[2]);
     }
diff --git a/src/nvim/extmark_defs.h b/src/nvim/extmark_defs.h
index 9ef4ec23e5..51b60dcf6d 100644
--- a/src/nvim/extmark_defs.h
+++ b/src/nvim/extmark_defs.h
@@ -9,6 +9,8 @@ typedef struct {
   int hl_id;
 } VirtTextChunk;
 
+typedef kvec_t(VirtTextChunk) VirtText;
+
 typedef struct undo_object ExtmarkUndoObject;
 typedef kvec_t(ExtmarkUndoObject) extmark_undo_vec_t;
 
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index ffcb0f3f22..d66bcca57f 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -114,6 +114,7 @@ typedef enum {
   HLF_WBR,        // Window bars
   HLF_WBRNC,      // Window bars of not-current windows
   HLF_CU,         // Cursor
+  HLF_BTITLE,     // Float Border Title
   HLF_COUNT,      // MUST be the last one
 } hlf_T;
 
@@ -178,6 +179,7 @@ EXTERN const char *hlf_names[] INIT(= {
   [HLF_WBR] = "WinBar",
   [HLF_WBRNC] = "WinBarNC",
   [HLF_CU] = "Cursor",
+  [HLF_BTITLE] = "FloatBorderTitle",
 });
 
 EXTERN int highlight_attr[HLF_COUNT + 1];     // Highl. attr for each context.
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index 9a09118939..a83f4ea969 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -131,6 +131,7 @@ static const char *highlight_init_both[] = {
   "default link MsgSeparator StatusLine",
   "default link NormalFloat Pmenu",
   "default link FloatBorder WinSeparator",
+  "default link FloatBorderTitle Title",
   "default FloatShadow blend=80 guibg=Black",
   "default FloatShadowThrough blend=100 guibg=Black",
   "RedrawDebugNormal cterm=reverse gui=reverse",
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 10b366ce23..1cde433b0a 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -4,6 +4,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include "nvim/api/private/helpers.h"
 #include "nvim/api/vim.h"
@@ -5066,6 +5067,9 @@ static void win_free(win_T *wp, tabpage_T *tp)
     }
   }
 
+  // free the border title text
+  clear_virttext(&wp->w_float_config.title_chunks);
+
   clear_matches(wp);
 
   free_jumplist(wp);
-- 
cgit 


From f3cea06d0114f2bcd2176b4a026d2bd4b68871d8 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 20:22:37 +0800
Subject: test(old): remove stray test42 files (#20966)

Forgot to remove in #17350.
---
 src/nvim/testdir/Makefile  |   3 +--
 src/nvim/testdir/test42.in | Bin 2387 -> 0 bytes
 src/nvim/testdir/test42.ok | Bin 409 -> 0 bytes
 3 files changed, 1 insertion(+), 2 deletions(-)
 delete mode 100644 src/nvim/testdir/test42.in
 delete mode 100644 src/nvim/testdir/test42.ok

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index a6d1cf1003..2d2853ead7 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -13,8 +13,7 @@ export SHELL := sh
 export NVIM_PRG := $(NVIM_PRG)
 export TMPDIR := $(abspath Xtest-tmpdir)
 
-SCRIPTS_DEFAULT = \
-	test42.out             \
+SCRIPTS_DEFAULT =
 
 ifneq ($(OS),Windows_NT)
   SCRIPTS_DEFAULTS := $(SCRIPTS_DEFAULT) \
diff --git a/src/nvim/testdir/test42.in b/src/nvim/testdir/test42.in
deleted file mode 100644
index 456f9ddb07..0000000000
Binary files a/src/nvim/testdir/test42.in and /dev/null differ
diff --git a/src/nvim/testdir/test42.ok b/src/nvim/testdir/test42.ok
deleted file mode 100644
index 183430d71c..0000000000
Binary files a/src/nvim/testdir/test42.ok and /dev/null differ
-- 
cgit 


From da0fb438496a7d721830bea51f0eb38d8c995883 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 21:56:37 +0800
Subject: vim-patch:8.1.1200: old style comments in debugger source

Problem:    Old style comments in debugger source.
Solution:   Use new style comments. (Yegappan Lakshmanan, closes vim/vim#4286)

https://github.com/vim/vim/commit/31fc39e47b3cc5e9ef4fa05943d42a4fb22de55e
---
 src/nvim/debugger.c | 1 +
 1 file changed, 1 insertion(+)

(limited to 'src/nvim')

diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c
index 3e0cf82740..e7c376621b 100644
--- a/src/nvim/debugger.c
+++ b/src/nvim/debugger.c
@@ -114,6 +114,7 @@ void do_debug(char *cmd)
   for (;;) {
     msg_scroll = true;
     need_wait_return = false;
+
     // Save the current typeahead buffer and replace it with an empty one.
     // This makes sure we get input from the user here and don't interfere
     // with the commands being executed.  Reset "ex_normal_busy" to avoid
-- 
cgit 


From 728c69bc8d52a3f2281a76e4142b2d766dc45da0 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 22:13:36 +0800
Subject: vim-patch:8.2.1340: some tests fail on Cirrus CI and/or with FreeBSD
 (#20967)

Problem:    Some tests fail on Cirrus CI and/or with FreeBSD.
Solution:   Make 'backupskip' empty. Do not run tests as root. Check for
            directory when using viminfo. (Ozaki Kiichi, closes vim/vim#6596)

https://github.com/vim/vim/commit/b86abadf87bd0f85f800077171ec4b98aefff776
---
 src/nvim/shada.c                    | 55 +++++++++++++++++++------------------
 src/nvim/testdir/test_backup.vim    | 27 +++++++++++++-----
 src/nvim/testdir/test_edit.vim      |  1 -
 src/nvim/testdir/test_writefile.vim | 28 ++++++-------------
 4 files changed, 58 insertions(+), 53 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 40b3429de9..d5676e9d70 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -3079,32 +3079,37 @@ shada_write_file_nomerge: {}
     sd_reader.close(&sd_reader);
     bool did_remove = false;
     if (sw_ret == kSDWriteSuccessful) {
-#ifdef UNIX
-      // For Unix we check the owner of the file.  It's not very nice to
-      // overwrite a user’s viminfo file after a "su root", with a
-      // viminfo file that the user can't read.
       FileInfo old_info;
-      if (os_fileinfo(fname, &old_info)) {
-        if (getuid() == ROOT_UID) {
-          if (old_info.stat.st_uid != ROOT_UID
-              || old_info.stat.st_gid != getgid()) {
-            const uv_uid_t old_uid = (uv_uid_t)old_info.stat.st_uid;
-            const uv_gid_t old_gid = (uv_gid_t)old_info.stat.st_gid;
-            const int fchown_ret = os_fchown(file_fd(sd_writer.cookie),
-                                             old_uid, old_gid);
-            if (fchown_ret != 0) {
-              semsg(_(RNERR "Failed setting uid and gid for file %s: %s"),
-                    tempname, os_strerror(fchown_ret));
-              goto shada_write_file_did_not_remove;
-            }
+      if (!os_fileinfo(fname, &old_info)
+          || S_ISDIR(old_info.stat.st_mode)
+#ifdef UNIX
+          // For Unix we check the owner of the file.  It's not very nice
+          // to overwrite a user's viminfo file after a "su root", with a
+          // viminfo file that the user can't read.
+          || (getuid() != ROOT_UID
+              && !(old_info.stat.st_uid == getuid()
+                   ? (old_info.stat.st_mode & 0200)
+                   : (old_info.stat.st_gid == getgid()
+                      ? (old_info.stat.st_mode & 0020)
+                      : (old_info.stat.st_mode & 0002))))
+#endif
+          ) {
+        semsg(_("E137: ShaDa file is not writable: %s"), fname);
+        goto shada_write_file_did_not_remove;
+      }
+#ifdef UNIX
+      if (getuid() == ROOT_UID) {
+        if (old_info.stat.st_uid != ROOT_UID
+            || old_info.stat.st_gid != getgid()) {
+          const uv_uid_t old_uid = (uv_uid_t)old_info.stat.st_uid;
+          const uv_gid_t old_gid = (uv_gid_t)old_info.stat.st_gid;
+          const int fchown_ret = os_fchown(file_fd(sd_writer.cookie),
+                                           old_uid, old_gid);
+          if (fchown_ret != 0) {
+            semsg(_(RNERR "Failed setting uid and gid for file %s: %s"),
+                  tempname, os_strerror(fchown_ret));
+            goto shada_write_file_did_not_remove;
           }
-        } else if (!(old_info.stat.st_uid == getuid()
-                     ? (old_info.stat.st_mode & 0200)
-                     : (old_info.stat.st_gid == getgid()
-                        ? (old_info.stat.st_mode & 0020)
-                        : (old_info.stat.st_mode & 0002)))) {
-          semsg(_("E137: ShaDa file is not writable: %s"), fname);
-          goto shada_write_file_did_not_remove;
         }
       }
 #endif
@@ -3125,9 +3130,7 @@ shada_write_file_nomerge: {}
       }
     }
     if (!did_remove) {
-#ifdef UNIX
 shada_write_file_did_not_remove:
-#endif
       semsg(_(RNERR "Do not forget to remove %s or rename it manually to %s."),
             tempname, fname);
     }
diff --git a/src/nvim/testdir/test_backup.vim b/src/nvim/testdir/test_backup.vim
index 7eff818732..d304773da4 100644
--- a/src/nvim/testdir/test_backup.vim
+++ b/src/nvim/testdir/test_backup.vim
@@ -19,6 +19,22 @@ func Test_backup()
   call delete('Xbackup.txt~')
 endfunc
 
+func Test_backup_backupskip()
+  set backup backupdir=. backupskip=*.txt
+  new
+  call setline(1, ['line1', 'line2'])
+  :f Xbackup.txt
+  :w! Xbackup.txt
+  " backup file is only created after
+  " writing a second time (before overwriting)
+  :w! Xbackup.txt
+  call assert_false(filereadable('Xbackup.txt~'))
+  bw!
+  set backup&vim backupdir&vim backupskip&vim
+  call delete('Xbackup.txt')
+  call delete('Xbackup.txt~')
+endfunc
+
 func Test_backup2()
   set backup backupdir=.// backupskip=
   new
@@ -30,7 +46,7 @@ func Test_backup2()
   :w! Xbackup.txt
   sp *Xbackup.txt~
   call assert_equal(['line1', 'line2', 'line3'], getline(1,'$'))
-  let f=expand('%')
+  let f = expand('%')
   call assert_match('%testdir%Xbackup.txt\~', f)
   bw!
   bw!
@@ -50,7 +66,7 @@ func Test_backup2_backupcopy()
   :w! Xbackup.txt
   sp *Xbackup.txt~
   call assert_equal(['line1', 'line2', 'line3'], getline(1,'$'))
-  let f=expand('%')
+  let f = expand('%')
   call assert_match('%testdir%Xbackup.txt\~', f)
   bw!
   bw!
@@ -62,14 +78,11 @@ endfunc
 " Test for using a non-existing directory as a backup directory
 func Test_non_existing_backupdir()
   throw 'Skipped: Nvim auto-creates backup directory'
-  CheckNotBSD
-  let save_backup = &backupdir
-  set backupdir=./non_existing_dir
+  set backupdir=./non_existing_dir backupskip=
   call writefile(['line1'], 'Xfile')
   new Xfile
-  " TODO: write doesn't fail in Cirrus FreeBSD CI test
   call assert_fails('write', 'E510:')
-  let &backupdir = save_backup
+  set backupdir&vim backupskip&vim
   call delete('Xfile')
 endfunc
 
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index 89fd73351d..4994e7af8f 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -1836,7 +1836,6 @@ endfunc
 " Test for editing a file without read permission
 func Test_edit_file_no_read_perm()
   CheckUnix
-  CheckNotBSD
   call writefile(['one', 'two'], 'Xfile')
   call setfperm('Xfile', '-w-------')
   new
diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim
index 8fb4c8fa4c..e4950de658 100644
--- a/src/nvim/testdir/test_writefile.vim
+++ b/src/nvim/testdir/test_writefile.vim
@@ -136,9 +136,7 @@ func Test_writefile_sync_arg()
 endfunc
 
 func Test_writefile_sync_dev_stdout()
-  if !has('unix')
-    return
-  endif
+  CheckUnix
   if filewritable('/dev/stdout')
     " Just check that this doesn't cause an error.
     call writefile(['one'], '/dev/stdout', 's')
@@ -386,13 +384,10 @@ endfunc
 
 " Test for writing to a readonly file
 func Test_write_readonly()
-  " In Cirrus-CI, the freebsd tests are run under a root account. So this test
-  " doesn't fail.
-  CheckNotBSD
   call writefile([], 'Xfile')
   call setfperm('Xfile', "r--------")
   edit Xfile
-  set noreadonly
+  set noreadonly backupskip=
   call assert_fails('write', 'E505:')
   let save_cpo = &cpo
   set cpo+=W
@@ -401,37 +396,32 @@ func Test_write_readonly()
   call setline(1, ['line1'])
   write!
   call assert_equal(['line1'], readfile('Xfile'))
+  set backupskip&
   call delete('Xfile')
 endfunc
 
 " Test for 'patchmode'
 func Test_patchmode()
-  CheckNotBSD
   call writefile(['one'], 'Xfile')
-  set patchmode=.orig nobackup writebackup
+  set patchmode=.orig nobackup backupskip= writebackup
   new Xfile
   call setline(1, 'two')
   " first write should create the .orig file
   write
-  " TODO: Xfile.orig is not created in Cirrus FreeBSD CI test
   call assert_equal(['one'], readfile('Xfile.orig'))
   call setline(1, 'three')
   " subsequent writes should not create/modify the .orig file
   write
   call assert_equal(['one'], readfile('Xfile.orig'))
-  set patchmode& backup& writebackup&
+  set patchmode& backup& backupskip& writebackup&
   call delete('Xfile')
   call delete('Xfile.orig')
 endfunc
 
 " Test for writing to a file in a readonly directory
 func Test_write_readonly_dir()
-  if !has('unix') || has('bsd')
-    " On MS-Windows, modifying files in a read-only directory is allowed.
-    " In Cirrus-CI for Freebsd, tests are run under a root account where
-    " modifying files in a read-only directory are allowed.
-    return
-  endif
+  " On MS-Windows, modifying files in a read-only directory is allowed.
+  CheckUnix
   call mkdir('Xdir')
   call writefile(['one'], 'Xdir/Xfile1')
   call setfperm('Xdir', 'r-xr--r--')
@@ -441,12 +431,12 @@ func Test_write_readonly_dir()
   call assert_fails('write', 'E212:')
   " try to create a backup file in the directory
   edit! Xdir/Xfile1
-  set backupdir=./Xdir
+  set backupdir=./Xdir backupskip=
   set patchmode=.orig
   call assert_fails('write', 'E509:')
   call setfperm('Xdir', 'rwxr--r--')
   call delete('Xdir', 'rf')
-  set backupdir& patchmode&
+  set backupdir& backupskip& patchmode&
 endfunc
 
 " Test for writing a file using invalid file encoding
-- 
cgit 


From de500095b1adc675999315e70307b3425dca089c Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 23:07:57 +0800
Subject: vim-patch:8.2.2592: code coverage could be improved (#20969)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Problem:    Code coverage could be improved.
Solution:   Add a few more tests. (Dominique Pellé, closes vim/vim#7957)

https://github.com/vim/vim/commit/6fd367a97c8653a2d734a38252c7d68d4b2ebaa7

Test case in test_viminfo.vim is applicable.
---
 src/nvim/testdir/test_fileformat.vim |  9 +++++++++
 src/nvim/testdir/test_normal.vim     |  7 +++++++
 src/nvim/testdir/test_sleep.vim      |  1 +
 src/nvim/testdir/test_textformat.vim | 16 ++++++++++++++++
 src/nvim/testdir/test_viminfo.vim    |  5 +++++
 5 files changed, 38 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_fileformat.vim b/src/nvim/testdir/test_fileformat.vim
index 81127ea59a..8d727a68c4 100644
--- a/src/nvim/testdir/test_fileformat.vim
+++ b/src/nvim/testdir/test_fileformat.vim
@@ -31,6 +31,15 @@ func Test_fileformat_autocommand()
   bw!
 endfunc
 
+func Test_fileformat_nomodifiable()
+  new
+  setlocal nomodifiable
+
+  call assert_fails('set fileformat=latin1', 'E21:')
+
+  bw
+endfunc
+
 " Convert the contents of a file into a literal string
 func s:file2str(fname)
   let b = readfile(a:fname, 'B')
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index 84929e2be3..6160352052 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -3399,6 +3399,13 @@ func Test_normal_delete_cmd()
   " delete to a readonly register
   call setline(1, ['abcd'])
   call assert_beeps('normal ":d2l')
+
+  " D and d with 'nomodifiable'
+  call setline(1, ['abcd'])
+  setlocal nomodifiable
+  call assert_fails('normal D', 'E21:')
+  call assert_fails('normal d$', 'E21:')
+
   close!
 endfunc
 
diff --git a/src/nvim/testdir/test_sleep.vim b/src/nvim/testdir/test_sleep.vim
index f71855fd4b..a428f380b0 100644
--- a/src/nvim/testdir/test_sleep.vim
+++ b/src/nvim/testdir/test_sleep.vim
@@ -21,6 +21,7 @@ func! Test_sleep_bang()
   call s:assert_takes_longer('sl 50m', 50)
   call s:assert_takes_longer('sl! 50m', 50)
   call s:assert_takes_longer('1sleep', 1000)
+  call s:assert_takes_longer('normal 1gs', 1000)
 endfunc
 
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim
index 3bce2eb24d..7a13de12f4 100644
--- a/src/nvim/testdir/test_textformat.vim
+++ b/src/nvim/testdir/test_textformat.vim
@@ -1044,6 +1044,22 @@ func Test_empty_matchpairs()
   bwipe!
 endfunc
 
+func Test_mps_error()
+  let encoding_save = &encoding
+
+  " for e in ['utf-8', 'latin1']
+  for e in ['utf-8']
+    exe 'set encoding=' .. e
+
+    call assert_fails('set mps=<:', 'E474:', e)
+    call assert_fails('set mps=:>', 'E474:', e)
+    call assert_fails('set mps=<>', 'E474:', e)
+    call assert_fails('set mps=<:>_', 'E474:', e)
+  endfor
+
+  let &encoding = encoding_save
+endfunc
+
 " Test for ra on multi-byte characters
 func Test_ra_multibyte()
   new
diff --git a/src/nvim/testdir/test_viminfo.vim b/src/nvim/testdir/test_viminfo.vim
index 2d6d598011..e792db90ab 100644
--- a/src/nvim/testdir/test_viminfo.vim
+++ b/src/nvim/testdir/test_viminfo.vim
@@ -18,4 +18,9 @@ func Test_viminfo_option_error()
   call assert_fails('set viminfo=%10', 'E528:')
 endfunc
 
+func Test_viminfo_oldfiles_newfile()
+  let v:oldfiles = v:_null_list
+  call assert_equal("\nNo old files", execute('oldfiles'))
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From df71537a12e454075e02824bb8f570e1e838ee64 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 22:15:33 +0800
Subject: vim-patch:8.2.2570: tests fail when run as root

Problem:    Tests fail when run as root.
Solution:   Add a comment mentioning the expected failure. (issue vim/vim#7919)

https://github.com/vim/vim/commit/f9a65505d1d93f3e67e5b8646bde3bbc44c70f7d

Co-authored-by: Bram Moolenaar 
---
 src/nvim/testdir/test_edit.vim      | 1 +
 src/nvim/testdir/test_excmd.vim     | 1 +
 src/nvim/testdir/test_help.vim      | 1 +
 src/nvim/testdir/test_writefile.vim | 1 +
 4 files changed, 4 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index 4994e7af8f..bd7b5609a5 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -1834,6 +1834,7 @@ func Test_edit_charconvert()
 endfunc
 
 " Test for editing a file without read permission
+" NOTE: if you run tests as root this will fail.  Don't run tests as root!
 func Test_edit_file_no_read_perm()
   CheckUnix
   call writefile(['one', 'two'], 'Xfile')
diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim
index 582dcaac2c..91fdb23a63 100644
--- a/src/nvim/testdir/test_excmd.vim
+++ b/src/nvim/testdir/test_excmd.vim
@@ -476,6 +476,7 @@ func Test_winsize_cmd()
 endfunc
 
 " Test for the :redir command
+" NOTE: if you run tests as root this will fail.  Don't run tests as root!
 func Test_redir_cmd()
   call assert_fails('redir @@', 'E475:')
   call assert_fails('redir abc', 'E475:')
diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim
index 19c0fcd820..5779cc6d11 100644
--- a/src/nvim/testdir/test_help.vim
+++ b/src/nvim/testdir/test_help.vim
@@ -98,6 +98,7 @@ func Test_help_completion()
 endfunc
 
 " Test for the :helptags command
+" NOTE: if you run tests as root this will fail.  Don't run tests as root!
 func Test_helptag_cmd()
   call mkdir('Xdir/a/doc', 'p')
 
diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim
index e4950de658..738e66ea27 100644
--- a/src/nvim/testdir/test_writefile.vim
+++ b/src/nvim/testdir/test_writefile.vim
@@ -419,6 +419,7 @@ func Test_patchmode()
 endfunc
 
 " Test for writing to a file in a readonly directory
+" NOTE: if you run tests as root this will fail.  Don't run tests as root!
 func Test_write_readonly_dir()
   " On MS-Windows, modifying files in a read-only directory is allowed.
   CheckUnix
-- 
cgit 


From fbe2761b20afaf25a02c06decaf543d211e997f0 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 22:15:59 +0800
Subject: vim-patch:8.2.2623: some tests fail when run as root

Problem:    Some tests fail when run as root.
Solution:   Use CheckNotRoot.

https://github.com/vim/vim/commit/17709e280ac5ba234b04641cde88d38e3522cedf

Co-authored-by: Bram Moolenaar 
---
 src/nvim/testdir/test_edit.vim      |  3 ++-
 src/nvim/testdir/test_excmd.vim     | 18 ++++++++-----
 src/nvim/testdir/test_help.vim      | 53 +++++++++++++++++++++----------------
 src/nvim/testdir/test_writefile.vim |  3 +++
 4 files changed, 46 insertions(+), 31 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index bd7b5609a5..dc850515b0 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -1834,9 +1834,10 @@ func Test_edit_charconvert()
 endfunc
 
 " Test for editing a file without read permission
-" NOTE: if you run tests as root this will fail.  Don't run tests as root!
 func Test_edit_file_no_read_perm()
   CheckUnix
+  CheckNotRoot
+
   call writefile(['one', 'two'], 'Xfile')
   call setfperm('Xfile', '-w-------')
   new
diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim
index 91fdb23a63..0e8af08b4b 100644
--- a/src/nvim/testdir/test_excmd.vim
+++ b/src/nvim/testdir/test_excmd.vim
@@ -493,13 +493,6 @@ func Test_redir_cmd()
     call assert_fails('redir > Xdir', 'E17:')
     call delete('Xdir', 'd')
   endif
-  if !has('bsd')
-    " Redirecting to a read-only file
-    call writefile([], 'Xfile')
-    call setfperm('Xfile', 'r--r--r--')
-    call assert_fails('redir! > Xfile', 'E190:')
-    call delete('Xfile')
-  endif
 
   " Test for redirecting to a register
   redir @q> | echon 'clean ' | redir END
@@ -512,6 +505,17 @@ func Test_redir_cmd()
   call assert_equal('blue sky', color)
 endfunc
 
+func Test_redir_cmd_readonly()
+  CheckNotRoot
+  CheckNotBSD
+
+  " Redirecting to a read-only file
+  call writefile([], 'Xfile')
+  call setfperm('Xfile', 'r--r--r--')
+  call assert_fails('redir! > Xfile', 'E190:')
+  call delete('Xfile')
+endfunc
+
 " Test for the :filetype command
 func Test_filetype_cmd()
   call assert_fails('filetype abc', 'E475:')
diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim
index 5779cc6d11..787f673991 100644
--- a/src/nvim/testdir/test_help.vim
+++ b/src/nvim/testdir/test_help.vim
@@ -1,5 +1,7 @@
 " Tests for :help
 
+source check.vim
+
 func Test_help_restore_snapshot()
   help
   set buftype=
@@ -112,29 +114,6 @@ func Test_helptag_cmd()
   call assert_equal(["help-tags\ttags\t1"], readfile('Xdir/tags'))
   call delete('Xdir/tags')
 
-  " The following tests fail on FreeBSD for some reason
-  if has('unix') && !has('bsd')
-    " Read-only tags file
-    call mkdir('Xdir/doc', 'p')
-    call writefile([''], 'Xdir/doc/tags')
-    call writefile([], 'Xdir/doc/sample.txt')
-    call setfperm('Xdir/doc/tags', 'r-xr--r--')
-    call assert_fails('helptags Xdir/doc', 'E152:', getfperm('Xdir/doc/tags'))
-
-    let rtp = &rtp
-    let &rtp = 'Xdir'
-    helptags ALL
-    let &rtp = rtp
-
-    call delete('Xdir/doc/tags')
-
-    " No permission to read the help file
-    call setfperm('Xdir/a/doc/sample.txt', '-w-------')
-    call assert_fails('helptags Xdir', 'E153:', getfperm('Xdir/a/doc/sample.txt'))
-    call delete('Xdir/a/doc/sample.txt')
-    call delete('Xdir/tags')
-  endif
-
   " Duplicate tags in the help file
   call writefile(['*tag1*', '*tag1*', '*tag2*'], 'Xdir/a/doc/sample.txt')
   call assert_fails('helptags Xdir', 'E154:')
@@ -142,6 +121,34 @@ func Test_helptag_cmd()
   call delete('Xdir', 'rf')
 endfunc
 
+func Test_helptag_cmd_readonly()
+  CheckUnix
+  CheckNotRoot
+  " The following tests fail on FreeBSD for some reason
+  CheckNotBSD
+
+  " Read-only tags file
+  call mkdir('Xdir/doc', 'p')
+  call writefile([''], 'Xdir/doc/tags')
+  call writefile([], 'Xdir/doc/sample.txt')
+  call setfperm('Xdir/doc/tags', 'r-xr--r--')
+  call assert_fails('helptags Xdir/doc', 'E152:', getfperm('Xdir/doc/tags'))
+
+  let rtp = &rtp
+  let &rtp = 'Xdir'
+  helptags ALL
+  let &rtp = rtp
+
+  call delete('Xdir/doc/tags')
+
+  " No permission to read the help file
+  call mkdir('Xdir/b/doc', 'p')
+  call writefile([], 'Xdir/b/doc/sample.txt')
+  call setfperm('Xdir/b/doc/sample.txt', '-w-------')
+  call assert_fails('helptags Xdir', 'E153:', getfperm('Xdir/b/doc/sample.txt'))
+  call delete('Xdir', 'rf')
+endfunc
+
 " Test for setting the 'helpheight' option in the help window
 func Test_help_window_height()
   let &cmdheight = &lines - 24
diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim
index 738e66ea27..a7ebe8cde6 100644
--- a/src/nvim/testdir/test_writefile.vim
+++ b/src/nvim/testdir/test_writefile.vim
@@ -423,6 +423,9 @@ endfunc
 func Test_write_readonly_dir()
   " On MS-Windows, modifying files in a read-only directory is allowed.
   CheckUnix
+  " Root can do it too.
+  CheckNotRoot
+
   call mkdir('Xdir')
   call writefile(['one'], 'Xdir/Xfile1')
   call setfperm('Xdir', 'r-xr--r--')
-- 
cgit 


From bfa0be49ed07d1e576c6452bb7e82956df7b19d5 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 22:28:39 +0800
Subject: vim-patch:8.2.2627: no need to check for BSD after checking for not
 root

Problem:    No need to check for BSD after checking for not root.
Solution:   Remove CheckNotBSD. (Ozaki Kiichi, closes vim/vim#7989)

https://github.com/vim/vim/commit/4355894869355c185e7810e67d52802453576e81
---
 src/nvim/testdir/check.vim      | 9 ---------
 src/nvim/testdir/test_excmd.vim | 1 -
 src/nvim/testdir/test_help.vim  | 2 --
 3 files changed, 12 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim
index 1459b70243..8a1080a2f3 100644
--- a/src/nvim/testdir/check.vim
+++ b/src/nvim/testdir/check.vim
@@ -90,15 +90,6 @@ func CheckUnix()
   endif
 endfunc
 
-" Command to check for not running on a BSD system.
-" TODO: using this checks should not be needed
-command CheckNotBSD call CheckNotBSD()
-func CheckNotBSD()
-  if has('bsd')
-    throw 'Skipped: does not work on BSD'
-  endif
-endfunc
-
 " Command to check that making screendumps is supported.
 " Caller must source screendump.vim
 command CheckScreendump call CheckScreendump()
diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim
index 0e8af08b4b..42b1f8ca48 100644
--- a/src/nvim/testdir/test_excmd.vim
+++ b/src/nvim/testdir/test_excmd.vim
@@ -507,7 +507,6 @@ endfunc
 
 func Test_redir_cmd_readonly()
   CheckNotRoot
-  CheckNotBSD
 
   " Redirecting to a read-only file
   call writefile([], 'Xfile')
diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim
index 787f673991..6478ae5dbf 100644
--- a/src/nvim/testdir/test_help.vim
+++ b/src/nvim/testdir/test_help.vim
@@ -124,8 +124,6 @@ endfunc
 func Test_helptag_cmd_readonly()
   CheckUnix
   CheckNotRoot
-  " The following tests fail on FreeBSD for some reason
-  CheckNotBSD
 
   " Read-only tags file
   call mkdir('Xdir/doc', 'p')
-- 
cgit 


From 8c454776f8a1d030b326347e1dd2d4e1fd6d7f7f Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 22:32:47 +0800
Subject: vim-patch:8.2.4495: help test fails in 24 line terminal

Problem:    Help test fails in 24 line terminal.
Solution:   Use up to 23 lines for text.

https://github.com/vim/vim/commit/e4e1a1e1c8a2bc6efd806e379b5fc80998318830

Co-authored-by: Bram Moolenaar 
---
 src/nvim/testdir/test_help.vim | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim
index 6478ae5dbf..e30d305703 100644
--- a/src/nvim/testdir/test_help.vim
+++ b/src/nvim/testdir/test_help.vim
@@ -149,7 +149,7 @@ endfunc
 
 " Test for setting the 'helpheight' option in the help window
 func Test_help_window_height()
-  let &cmdheight = &lines - 24
+  let &cmdheight = &lines - 23
   set helpheight=10
   help
   set helpheight=14
-- 
cgit 


From 3b3611a3d0f1e13e277360d6bacf97a0fb7eb6de Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 7 Nov 2022 07:03:31 +0800
Subject: vim-patch:9.0.0841: deletebufline() does not always return 1 on
 failure (#20980)

Problem:    deletebufline() does not always return 1 on failure.
Solution:   Refactor the code to make it work more predictable. (closes vim/vim#11511)

https://github.com/vim/vim/commit/7af3ee2b83545169d78a28ab1cd89aff1127f8b3
---
 src/nvim/eval/funcs.c             | 38 ++++++++++++++++++++------------------
 src/nvim/testdir/test_bufline.vim | 16 ++++++++++++++++
 2 files changed, 36 insertions(+), 18 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 26a5c133da..1492a2d30d 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -1510,6 +1510,7 @@ static void f_deletebufline(typval_T *argvars, typval_T *rettv, EvalFuncData fpt
 
   buf_T *curbuf_save = NULL;
   win_T *curwin_save = NULL;
+  // After this don't use "return", goto "cleanup"!
   if (!is_curbuf) {
     VIsual_active = false;
     curbuf_save = curbuf;
@@ -1530,34 +1531,35 @@ static void f_deletebufline(typval_T *argvars, typval_T *rettv, EvalFuncData fpt
   }
 
   if (u_save(first - 1, last + 1) == FAIL) {
-    rettv->vval.v_number = 1;  // FAIL
-  } else {
-    for (linenr_T lnum = first; lnum <= last; lnum++) {
-      ml_delete(first, true);
-    }
+    goto cleanup;
+  }
 
-    FOR_ALL_TAB_WINDOWS(tp, wp) {
-      if (wp->w_buffer == buf) {
-        if (wp->w_cursor.lnum > last) {
-          wp->w_cursor.lnum -= (linenr_T)count;
-        } else if (wp->w_cursor.lnum > first) {
-          wp->w_cursor.lnum = first;
-        }
-        if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) {
-          wp->w_cursor.lnum = wp->w_buffer->b_ml.ml_line_count;
-        }
+  for (linenr_T lnum = first; lnum <= last; lnum++) {
+    ml_delete(first, true);
+  }
+
+  FOR_ALL_TAB_WINDOWS(tp, wp) {
+    if (wp->w_buffer == buf) {
+      if (wp->w_cursor.lnum > last) {
+        wp->w_cursor.lnum -= (linenr_T)count;
+      } else if (wp->w_cursor.lnum > first) {
+        wp->w_cursor.lnum = first;
+      }
+      if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) {
+        wp->w_cursor.lnum = wp->w_buffer->b_ml.ml_line_count;
       }
     }
-    check_cursor_col();
-    deleted_lines_mark(first, count);
   }
+  check_cursor_col();
+  deleted_lines_mark(first, count);
+  rettv->vval.v_number = 0;  // OK
 
+cleanup:
   if (!is_curbuf) {
     curbuf = curbuf_save;
     curwin = curwin_save;
     VIsual_active = save_VIsual_active;
   }
-  rettv->vval.v_number = 0;  // OK
 }
 
 /// "did_filetype()" function
diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim
index 2867f13cbc..fae6b1dab7 100644
--- a/src/nvim/testdir/test_bufline.vim
+++ b/src/nvim/testdir/test_bufline.vim
@@ -239,4 +239,20 @@ func Test_setbufline_startup_nofile()
   call delete('Xresult')
 endfunc
 
+" Test that setbufline(), appendbufline() and deletebufline() should fail and
+" return 1 when "textlock" is active.
+func Test_change_bufline_with_textlock()
+  new
+  inoremap    setbufline('', 1, '')
+  call assert_fails("normal a\", 'E565:')
+  call assert_equal('1', getline(1))
+  inoremap    appendbufline('', 1, '')
+  call assert_fails("normal a\", 'E565:')
+  call assert_equal('11', getline(1))
+  inoremap    deletebufline('', 1)
+  call assert_fails("normal a\", 'E565:')
+  call assert_equal('111', getline(1))
+  bwipe!
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From b0190f4543397d6f3485e1eb03f30f80e358c201 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 7 Nov 2022 07:04:49 +0800
Subject: vim-patch:8.2.2849: bufwrite not sufficiently tested

Problem:    Bufwrite not sufficiently tested.
Solution:   Add a few more tests. (Yegappan Lakshmanan, closes vim/vim#8192)

https://github.com/vim/vim/commit/36f96a515109dc1fad279571a645c0f0d65f2de4

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/testdir/test_startup.vim   |  15 +++
 src/nvim/testdir/test_writefile.vim | 197 ++++++++++++++++++++++++++++++++++++
 2 files changed, 212 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim
index 30bbd355df..f9f7c5b492 100644
--- a/src/nvim/testdir/test_startup.vim
+++ b/src/nvim/testdir/test_startup.vim
@@ -1279,4 +1279,19 @@ func Test_progname()
   call delete('Xprogname', 'd')
 endfunc
 
+" Test for doing a write from .vimrc
+func Test_write_in_vimrc()
+  call writefile(['silent! write'], 'Xvimrc')
+  let after =<< trim [CODE]
+    call assert_match('E32: ', v:errmsg)
+    call writefile(v:errors, 'Xtestout')
+    qall
+  [CODE]
+  if RunVim([], after, '-u Xvimrc')
+    call assert_equal([], readfile('Xtestout'))
+    call delete('Xtestout')
+  endif
+  call delete('Xvimrc')
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim
index a7ebe8cde6..0ecb25d3e4 100644
--- a/src/nvim/testdir/test_writefile.vim
+++ b/src/nvim/testdir/test_writefile.vim
@@ -57,7 +57,29 @@ func Test_writefile_fails_conversion()
   call assert_fails('write ++enc=cp932', 'E513:')
   call assert_equal(contents, readfile('Xfile'))
 
+  " With 'backupcopy' set, if there is a conversion error, the backup file is
+  " still created.
+  set backupcopy=yes writebackup& backup&
+  call delete('Xfile' .. &backupext)
+  call assert_fails('write ++enc=cp932', 'E513:')
+  call assert_equal(contents, readfile('Xfile'))
+  call assert_equal(contents, readfile('Xfile' .. &backupext))
+  set backupcopy&
+  %bw!
+
+  " Conversion error during write
+  new
+  call setline(1, ["\U10000000"])
+  let output = execute('write! ++enc=utf-16 Xfile')
+  call assert_match('CONVERSION ERROR', output)
+  let output = execute('write! ++enc=ucs-2 Xfile')
+  call assert_match('CONVERSION ERROR', output)
+  call delete('Xfilz~')
+  call delete('Xfily~')
+  %bw!
+
   call delete('Xfile')
+  call delete('Xfile' .. &backupext)
   bwipe!
   set backup& writebackup& backupdir&vim backupskip&vim
 endfunc
@@ -270,6 +292,16 @@ func Test_write_errors()
   let long_fname = repeat('n', 5000)
   call assert_fails('exe "w " .. long_fname', 'E75:')
   call assert_fails('call writefile([], long_fname)', 'E482:')
+
+  " Test for writing to a block device on Unix-like systems
+  if has('unix') && getfperm('/dev/loop0') != ''
+        \ && getftype('/dev/loop0') == 'bdev' && !IsRoot()
+    new
+    edit /dev/loop0
+    call assert_fails('write', 'E505: ')
+    call assert_fails('write!', 'E503: ')
+    close!
+  endif
 endfunc
 
 " Test for writing to a file which is modified after Vim read it
@@ -396,8 +428,21 @@ func Test_write_readonly()
   call setline(1, ['line1'])
   write!
   call assert_equal(['line1'], readfile('Xfile'))
+
+  " Auto-saving a readonly file should fail with 'autowriteall'
+  %bw!
+  e Xfile
+  set noreadonly autowriteall
+  call setline(1, ['aaaa'])
+  call assert_fails('n', 'E505:')
+  set cpo+=W
+  call assert_fails('n', 'E504:')
+  set cpo-=W
+  set autowriteall&
+
   set backupskip&
   call delete('Xfile')
+  %bw!
 endfunc
 
 " Test for 'patchmode'
@@ -413,6 +458,19 @@ func Test_patchmode()
   " subsequent writes should not create/modify the .orig file
   write
   call assert_equal(['one'], readfile('Xfile.orig'))
+
+  " use 'patchmode' with 'nobackup' and 'nowritebackup' to create an empty
+  " original file
+  call delete('Xfile')
+  call delete('Xfile.orig')
+  %bw!
+  set patchmode=.orig nobackup nowritebackup
+  edit Xfile
+  call setline(1, ['xxx'])
+  write
+  call assert_equal(['xxx'], readfile('Xfile'))
+  call assert_equal([], readfile('Xfile.orig'))
+
   set patchmode& backup& backupskip& writebackup&
   call delete('Xfile')
   call delete('Xfile.orig')
@@ -714,6 +772,145 @@ func Test_read_write_bin()
   bwipe! XNoEolSetEol
 endfunc
 
+" Test for the 'backupcopy' option when writing files
+func Test_backupcopy()
+  CheckUnix
+  set backupskip=
+  " With the default 'backupcopy' setting, saving a symbolic link file
+  " should not break the link.
+  set backupcopy&
+  call writefile(['1111'], 'Xfile1')
+  silent !ln -s Xfile1 Xfile2
+  new Xfile2
+  call setline(1, ['2222'])
+  write
+  close
+  call assert_equal(['2222'], readfile('Xfile1'))
+  call assert_equal('Xfile1', resolve('Xfile2'))
+  call assert_equal('link', getftype('Xfile2'))
+  call delete('Xfile1')
+  call delete('Xfile2')
+
+  " With the 'backupcopy' set to 'breaksymlink', saving a symbolic link file
+  " should break the link.
+  set backupcopy=yes,breaksymlink
+  call writefile(['1111'], 'Xfile1')
+  silent !ln -s Xfile1 Xfile2
+  new Xfile2
+  call setline(1, ['2222'])
+  write
+  close
+  call assert_equal(['1111'], readfile('Xfile1'))
+  call assert_equal(['2222'], readfile('Xfile2'))
+  call assert_equal('Xfile2', resolve('Xfile2'))
+  call assert_equal('file', getftype('Xfile2'))
+  call delete('Xfile1')
+  call delete('Xfile2')
+  set backupcopy&
+
+  " With the default 'backupcopy' setting, saving a hard link file
+  " should not break the link.
+  set backupcopy&
+  call writefile(['1111'], 'Xfile1')
+  silent !ln Xfile1 Xfile2
+  new Xfile2
+  call setline(1, ['2222'])
+  write
+  close
+  call assert_equal(['2222'], readfile('Xfile1'))
+  call delete('Xfile1')
+  call delete('Xfile2')
+
+  " With the 'backupcopy' set to 'breaksymlink', saving a hard link file
+  " should break the link.
+  set backupcopy=yes,breakhardlink
+  call writefile(['1111'], 'Xfile1')
+  silent !ln Xfile1 Xfile2
+  new Xfile2
+  call setline(1, ['2222'])
+  write
+  call assert_equal(['1111'], readfile('Xfile1'))
+  call assert_equal(['2222'], readfile('Xfile2'))
+  call delete('Xfile1')
+  call delete('Xfile2')
+
+  " If a backup file is already present, then a slightly modified filename
+  " should be used as the backup file. Try with 'backupcopy' set to 'yes' and
+  " 'no'.
+  %bw
+  call writefile(['aaaa'], 'Xfile')
+  call writefile(['bbbb'], 'Xfile.bak')
+  set backupcopy=yes backupext=.bak
+  new Xfile
+  call setline(1, ['cccc'])
+  write
+  close
+  call assert_equal(['cccc'], readfile('Xfile'))
+  call assert_equal(['bbbb'], readfile('Xfile.bak'))
+  set backupcopy=no backupext=.bak
+  new Xfile
+  call setline(1, ['dddd'])
+  write
+  close
+  call assert_equal(['dddd'], readfile('Xfile'))
+  call assert_equal(['bbbb'], readfile('Xfile.bak'))
+  call delete('Xfile')
+  call delete('Xfile.bak')
+
+  " Write to a device file (in Unix-like systems) which cannot be backed up.
+  if has('unix')
+    set writebackup backupcopy=yes nobackup
+    new
+    call setline(1, ['aaaa'])
+    let output = execute('write! /dev/null')
+    call assert_match('"/dev/null" \[Device]', output)
+    close
+    set writebackup backupcopy=no nobackup
+    new
+    call setline(1, ['aaaa'])
+    let output = execute('write! /dev/null')
+    call assert_match('"/dev/null" \[Device]', output)
+    close
+    set backup writebackup& backupcopy&
+    new
+    call setline(1, ['aaaa'])
+    let output = execute('write! /dev/null')
+    call assert_match('"/dev/null" \[Device]', output)
+    close
+  endif
+
+  set backupcopy& backupskip& backupext& backup&
+endfunc
+
+" Test for writing a file with 'encoding' set to 'utf-16'
+func Test_write_utf16()
+  new
+  call setline(1, ["\U00010001"])
+  write ++enc=utf-16 Xfile
+  bw!
+  call assert_equal(0zD800DC01, readfile('Xfile', 'B')[0:3])
+  call delete('Xfile')
+endfunc
+
+" Test for trying to save a backup file when the backup file is a symbolic
+" link to the original file. The backup file should not be modified.
+func Test_write_backup_symlink()
+  CheckUnix
+  call writefile(['1111'], 'Xfile')
+  silent !ln -s Xfile Xfile.bak
+
+  new Xfile
+  set backup backupcopy=yes backupext=.bak
+  write
+  call assert_equal('link', getftype('Xfile.bak'))
+  call assert_equal('Xfile', resolve('Xfile.bak'))
+  set backup& backupcopy& backupext&
+  close
+
+  call delete('Xfile')
+  call delete('Xfile.bak')
+endfunc
+
 " Check that buffer is written before triggering QuitPre
 func Test_wq_quitpre_autocommand()
   edit Xsomefile
-- 
cgit 


From bdf87efeb523aecffde7dbf3c17806f7ca98471f Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 7 Nov 2022 07:06:49 +0800
Subject: vim-patch:8.2.2856: get readonly error for device that can't be
 written to

Problem:    Get readonly error for device that can't be written to.
Solution:   Check for being able to write first. (closes vim/vim#8205)

https://github.com/vim/vim/commit/50157ef1c2e36d8696e79fd688bdd08312196bc6

Co-authored-by: Bram Moolenaar 
---
 src/nvim/ex_cmds.c                  | 17 ++++++++++++++++-
 src/nvim/testdir/test_writefile.vim |  2 +-
 2 files changed, 17 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 89e6d47950..d3a4b6c282 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -1772,6 +1772,17 @@ void ex_write(exarg_T *eap)
   }
 }
 
+#ifdef UNIX
+static int check_writable(const char *fname)
+{
+  if (os_nodetype(fname) == NODE_OTHER) {
+    semsg(_("E503: \"%s\" is not a file or writable device"), fname);
+    return FAIL;
+  }
+  return OK;
+}
+#endif
+
 /// write current buffer to file 'eap->arg'
 /// if 'eap->append' is true, append to the file
 ///
@@ -1829,7 +1840,11 @@ int do_write(exarg_T *eap)
   // Writing to the current file is not allowed in readonly mode
   // and a file name is required.
   // "nofile" and "nowrite" buffers cannot be written implicitly either.
-  if (!other && (bt_dontwrite_msg(curbuf) || check_fname() == FAIL
+  if (!other && (bt_dontwrite_msg(curbuf)
+                 || check_fname() == FAIL
+#ifdef UNIX
+                 || check_writable(curbuf->b_ffname) == FAIL
+#endif
                  || check_readonly(&eap->forceit, curbuf))) {
     goto theend;
   }
diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim
index 0ecb25d3e4..b80519f316 100644
--- a/src/nvim/testdir/test_writefile.vim
+++ b/src/nvim/testdir/test_writefile.vim
@@ -298,7 +298,7 @@ func Test_write_errors()
         \ && getftype('/dev/loop0') == 'bdev' && !IsRoot()
     new
     edit /dev/loop0
-    call assert_fails('write', 'E505: ')
+    call assert_fails('write', 'E503: ')
     call assert_fails('write!', 'E503: ')
     close!
   endif
-- 
cgit 


From 7e1d9c560b09cacb78b2fc8f9428a77d6fca66e1 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 7 Nov 2022 07:21:46 +0800
Subject: vim-patch:8.2.2873: not enough tests for writing buffers

Problem:    Not enough tests for writing buffers.
Solution:   Add a few more tests. (Yegappan Lakshmanan, closes vim/vim#8229)

https://github.com/vim/vim/commit/46aa6f93acb5d932d2893606d980a6b4b8a9594c

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/testdir/test_buffer.vim    | 20 ++++++++++++++++++++
 src/nvim/testdir/test_cmdline.vim   | 21 +++++++++++++++++++++
 src/nvim/testdir/test_functions.vim |  1 +
 src/nvim/testdir/test_writefile.vim | 22 ++++++++++++++++++++--
 4 files changed, 62 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_buffer.vim b/src/nvim/testdir/test_buffer.vim
index d8d22ff872..3300278802 100644
--- a/src/nvim/testdir/test_buffer.vim
+++ b/src/nvim/testdir/test_buffer.vim
@@ -434,4 +434,24 @@ func Test_buf_pattern_invalid()
   bwipe!
 endfunc
 
+" Test for the 'maxmem' and 'maxmemtot' options
+func Test_buffer_maxmem()
+  " use 1KB per buffer and 2KB for all the buffers
+  " set maxmem=1 maxmemtot=2
+  new
+  let v:errmsg = ''
+  " try opening some files
+  edit test_arglist.vim
+  call assert_equal('test_arglist.vim', bufname())
+  edit test_eval_stuff.vim
+  call assert_equal('test_eval_stuff.vim', bufname())
+  b test_arglist.vim
+  call assert_equal('test_arglist.vim', bufname())
+  b test_eval_stuff.vim
+  call assert_equal('test_eval_stuff.vim', bufname())
+  close
+  call assert_equal('', v:errmsg)
+  " set maxmem& maxmemtot&
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 7cef3117c2..b0be7ab396 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -1513,6 +1513,7 @@ func Test_cmdline_expand_special()
   call assert_fails('e ', 'E495:')
   call assert_fails('e ', 'E496:')
   call assert_fails('e ', 'E497:')
+
   call writefile([], 'Xfile.cpp')
   call writefile([], 'Xfile.java')
   new Xfile.cpp
@@ -2006,6 +2007,26 @@ func Test_recalling_cmdline()
   cunmap (save-cmdline)
 endfunc
 
+" Test for the 'suffixes' option
+func Test_suffixes_opt()
+  call writefile([], 'Xfile')
+  call writefile([], 'Xfile.c')
+  call writefile([], 'Xfile.o')
+  set suffixes=
+  call feedkeys(":e Xfi*\\\"\", 'xt')
+  call assert_equal('"e Xfile Xfile.c Xfile.o', @:)
+  set suffixes=.c
+  call feedkeys(":e Xfi*\\\"\", 'xt')
+  call assert_equal('"e Xfile Xfile.o Xfile.c', @:)
+  set suffixes=,,
+  call feedkeys(":e Xfi*\\\"\", 'xt')
+  call assert_equal('"e Xfile.c Xfile.o Xfile', @:)
+  set suffixes&
+  call delete('Xfile')
+  call delete('Xfile.c')
+  call delete('Xfile.o')
+endfunc
+
 " Test for using a popup menu for the command line completion matches
 " (wildoptions=pum)
 func Test_wildmenu_pum()
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index b464f6591d..0f2066b7c1 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -2454,6 +2454,7 @@ endfunc
 func Test_glob()
   call assert_equal('', glob(v:_null_string))
   call assert_equal('', globpath(v:_null_string, v:_null_string))
+  call assert_fails("let x = globpath(&rtp, 'syntax/c.vim', [])", 'E745:')
 
   call writefile([], 'Xglob1')
   call writefile([], 'XGLOB2')
diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim
index b80519f316..81615b5963 100644
--- a/src/nvim/testdir/test_writefile.vim
+++ b/src/nvim/testdir/test_writefile.vim
@@ -484,7 +484,7 @@ func Test_write_readonly_dir()
   " Root can do it too.
   CheckNotRoot
 
-  call mkdir('Xdir')
+  call mkdir('Xdir/')
   call writefile(['one'], 'Xdir/Xfile1')
   call setfperm('Xdir', 'r-xr--r--')
   " try to create a new file in the directory
@@ -768,7 +768,7 @@ func Test_read_write_bin()
   call assert_equal(0z6E6F656F6C0A, readfile('XNoEolSetEol', 'B'))
 
   call delete('XNoEolSetEol')
-  set ff&
+  set ff& fixeol&
   bwipe! XNoEolSetEol
 endfunc
 
@@ -911,6 +911,24 @@ func Test_write_backup_symlink()
   call delete('Xfile.bak')
 endfunc
 
+" Test for ':write ++bin' and ':write ++nobin'
+func Test_write_binary_file()
+  " create a file without an eol/eof character
+  call writefile(0z616161, 'Xfile1', 'b')
+  new Xfile1
+  write ++bin Xfile2
+  write ++nobin Xfile3
+  call assert_equal(0z616161, readblob('Xfile2'))
+  if has('win32')
+    call assert_equal(0z6161610D.0A, readblob('Xfile3'))
+  else
+    call assert_equal(0z6161610A, readblob('Xfile3'))
+  endif
+  call delete('Xfile1')
+  call delete('Xfile2')
+  call delete('Xfile3')
+endfunc
+
 " Check that buffer is written before triggering QuitPre
 func Test_wq_quitpre_autocommand()
   edit Xsomefile
-- 
cgit 


From 900dd2bdab85b25237cec638265d44c2174154ed Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 20 Aug 2022 08:27:10 +0800
Subject: vim-patch:8.2.3665: cannot use a lambda for 'tagfunc'

Problem:    Cannot use a lambda for 'tagfunc'.
Solution:   Use 'tagfunc' like 'opfunc'. (Yegappan Lakshmanan, closes vim/vim#9204)
https://github.com/vim/vim/commit/19916a8c8920b6a1fd737ffa6d4e363fc7a96319

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/buffer.c                 |  1 +
 src/nvim/buffer_defs.h            |  1 +
 src/nvim/option.c                 |  3 +++
 src/nvim/optionstr.c              |  5 ++++
 src/nvim/tag.c                    | 41 +++++++++++++++++++++++++++++++-
 src/nvim/testdir/test_tagfunc.vim | 50 +++++++++++++++++++++++++++++++++++++++
 6 files changed, 100 insertions(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index ade5c35450..4c8faccaa7 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1981,6 +1981,7 @@ void free_buf_options(buf_T *buf, int free_p_ff)
   clear_string_option(&buf->b_p_tags);
   clear_string_option(&buf->b_p_tc);
   clear_string_option(&buf->b_p_tfu);
+  callback_free(&buf->b_tfu_cb);
   clear_string_option(&buf->b_p_dict);
   clear_string_option(&buf->b_p_tsr);
   clear_string_option(&buf->b_p_qe);
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 2b42289858..d8e86a75cf 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -677,6 +677,7 @@ struct file_buffer {
   char *b_p_cfu;                ///< 'completefunc'
   char *b_p_ofu;                ///< 'omnifunc'
   char *b_p_tfu;                ///< 'tagfunc'
+  Callback b_tfu_cb;            ///< 'tagfunc' callback
   int b_p_eof;                  ///< 'endoffile'
   int b_p_eol;                  ///< 'endofline'
   int b_p_fixeol;               ///< 'fixendofline'
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 523ae13e52..1eabe8c540 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -78,6 +78,7 @@
 #include "nvim/spellsuggest.h"
 #include "nvim/strings.h"
 #include "nvim/syntax.h"
+#include "nvim/tag.h"
 #include "nvim/ui.h"
 #include "nvim/ui_compositor.h"
 #include "nvim/undo.h"
@@ -577,6 +578,7 @@ void free_all_options(void)
     }
   }
   free_operatorfunc_option();
+  free_tagfunc_option();
 }
 #endif
 
@@ -4383,6 +4385,7 @@ void buf_copy_options(buf_T *buf, int flags)
       COPY_OPT_SCTX(buf, BV_OFU);
       buf->b_p_tfu = xstrdup(p_tfu);
       COPY_OPT_SCTX(buf, BV_TFU);
+      buf_set_tfu_callback(buf);
       buf->b_p_sts = p_sts;
       COPY_OPT_SCTX(buf, BV_STS);
       buf->b_p_sts_nopaste = p_sts_nopaste;
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index 65bc9f60df..abae338daa 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -38,6 +38,7 @@
 #include "nvim/spellfile.h"
 #include "nvim/spellsuggest.h"
 #include "nvim/strings.h"
+#include "nvim/tag.h"
 #include "nvim/ui.h"
 #include "nvim/vim.h"
 #include "nvim/window.h"
@@ -1480,6 +1481,10 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
     if (qf_process_qftf_option() == FAIL) {
       errmsg = e_invarg;
     }
+  } else if (gvarp == &p_tfu) {  // 'tagfunc'
+    if (set_tagfunc_option() == FAIL) {
+      errmsg = e_invarg;
+    }
   } else {
     // Options that are a list of flags.
     p = NULL;
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 264f961b43..e38fbb7da9 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -114,10 +114,49 @@ static char *tagmatchname = NULL;   // name of last used tag
 static taggy_T ptag_entry = { NULL, INIT_FMARK, 0, 0, NULL };
 
 static int tfu_in_use = false;  // disallow recursive call of tagfunc
+static Callback tfu_cb;         // 'tagfunc' callback function
 
 // Used instead of NUL to separate tag fields in the growarrays.
 #define TAG_SEP 0x02
 
+/// Reads the 'tagfunc' option value and convert that to a callback value.
+/// Invoked when the 'tagfunc' option is set. The option value can be a name of
+/// a function (string), or function() or funcref() or a lambda.
+int set_tagfunc_option(void)
+{
+  callback_free(&tfu_cb);
+  callback_free(&curbuf->b_tfu_cb);
+
+  if (*curbuf->b_p_tfu == NUL) {
+    return OK;
+  }
+
+  if (option_set_callback_func(curbuf->b_p_tfu, &tfu_cb) == FAIL) {
+    return FAIL;
+  }
+
+  callback_copy(&curbuf->b_tfu_cb, &tfu_cb);
+
+  return OK;
+}
+
+#if defined(EXITFREE)
+void free_tagfunc_option(void)
+{
+  callback_free(&tfu_cb);
+}
+#endif
+
+/// Copy the global 'tagfunc' callback function to the buffer-local 'tagfunc'
+/// callback for 'buf'.
+void buf_set_tfu_callback(buf_T *buf)
+{
+  callback_free(&buf->b_tfu_cb);
+  if (tfu_cb.data.funcref != NULL && *tfu_cb.data.funcref != NUL) {
+    callback_copy(&buf->b_tfu_cb, &tfu_cb);
+  }
+}
+
 /// Jump to tag; handling of tag commands and tag stack
 ///
 /// *tag != NUL: ":tag {tag}", jump to new tag, add to tag stack
@@ -1129,7 +1168,7 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
                flags & TAG_REGEXP   ? "r": "");
 
   save_pos = curwin->w_cursor;
-  result = call_vim_function(curbuf->b_p_tfu, 3, args, &rettv);
+  result = callback_call(&curbuf->b_tfu_cb, 3, args, &rettv);
   curwin->w_cursor = save_pos;  // restore the cursor position
   d->dv_refcount--;
 
diff --git a/src/nvim/testdir/test_tagfunc.vim b/src/nvim/testdir/test_tagfunc.vim
index bdf5afa5b2..92c2f2d6a1 100644
--- a/src/nvim/testdir/test_tagfunc.vim
+++ b/src/nvim/testdir/test_tagfunc.vim
@@ -117,4 +117,54 @@ func Test_tagfunc_settagstack()
   delfunc Mytagfunc2
 endfunc
 
+" Test for different ways of setting the 'tagfunc' option
+func Test_tagfunc_callback()
+  " Test for using a function()
+  func MytagFunc1(pat, flags, info)
+    let g:MytagFunc1_args = [a:pat, a:flags, a:info]
+    return v:null
+  endfunc
+  let g:MytagFunc1_args = []
+  set tagfunc=function('MytagFunc1')
+  call assert_fails('tag abc', 'E433:')
+  call assert_equal(['abc', '', {}], g:MytagFunc1_args)
+
+  " Test for using a funcref()
+  new
+  func MytagFunc2(pat, flags, info)
+    let g:MytagFunc2_args = [a:pat, a:flags, a:info]
+    return v:null
+  endfunc
+  let g:MytagFunc2_args = []
+  set tagfunc=funcref('MytagFunc2')
+  call assert_fails('tag def', 'E433:')
+  call assert_equal(['def', '', {}], g:MytagFunc2_args)
+
+  " Test for using a lambda function
+  new
+  func MytagFunc3(pat, flags, info)
+    let g:MytagFunc3_args = [a:pat, a:flags, a:info]
+    return v:null
+  endfunc
+  let g:MytagFunc3_args = []
+  let &tagfunc= '{a, b, c -> MytagFunc3(a, b, c)}'
+  call assert_fails('tag ghi', 'E433:')
+  call assert_equal(['ghi', '', {}], g:MytagFunc3_args)
+
+  " Test for clearing the 'tagfunc' option
+  set tagfunc=''
+  set tagfunc&
+
+  call assert_fails("set tagfunc=function('abc')", "E700:")
+  call assert_fails("set tagfunc=funcref('abc')", "E700:")
+  let &tagfunc = "{a -> 'abc'}"
+  call assert_fails("echo taglist('a')", "E987:")
+
+  " cleanup
+  delfunc MytagFunc1
+  delfunc MytagFunc2
+  delfunc MytagFunc3
+  %bw!
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From b3e9010f4783a51407ec5a5ad9fda1216d4db3fe Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 15:30:54 +0800
Subject: vim-patch:8.2.3705: cannot pass a lambda name to function() or
 funcref()

Problem:    Cannot pass a lambda name to function() or funcref(). (Yegappan
            Lakshmanan)
Solution:   Handle a lambda name differently.

https://github.com/vim/vim/commit/eba3b7f6645c8f856132b4c06a009a3b0a44e21c

Co-authored-by: Bram Moolenaar 
---
 src/nvim/eval.c                |  5 ++---
 src/nvim/eval/userfunc.c       | 30 ++++++++++++++++++++++--------
 src/nvim/testdir/test_expr.vim |  7 +++++++
 3 files changed, 31 insertions(+), 11 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index fe4ae92834..c983388450 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -5029,9 +5029,8 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref)
 
   if ((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL) || is_funcref) {
     name = s;
-    trans_name = (char *)trans_function_name(&name, false,
-                                             TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD
-                                             | TFN_NO_DEREF, NULL, NULL);
+    trans_name = save_function_name(&name, false,
+                                    TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL);
     if (*name != NUL) {
       s = NULL;
     }
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index ef74ca58e3..e6b038a335 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -1886,6 +1886,27 @@ theend:
   return (char_u *)name;
 }
 
+/// Call trans_function_name(), except that a lambda is returned as-is.
+/// Returns the name in allocated memory.
+char *save_function_name(char **name, bool skip, int flags, funcdict_T *fudi)
+{
+  char *p = *name;
+  char *saved;
+
+  if (strncmp(p, "", 8) == 0) {
+    p += 8;
+    (void)getdigits(&p, false, 0);
+    saved = xstrndup(*name, (size_t)(p - *name));
+    if (fudi != NULL) {
+      CLEAR_POINTER(fudi);
+    }
+  } else {
+    saved = (char *)trans_function_name(&p, skip, flags, fudi, NULL);
+  }
+  *name = p;
+  return saved;
+}
+
 #define MAX_FUNC_NESTING 50
 
 /// List functions.
@@ -2000,14 +2021,7 @@ void ex_function(exarg_T *eap)
   // s:func      script-local function name
   // g:func      global function name, same as "func"
   p = eap->arg;
-  if (strncmp(p, "", 8) == 0) {
-    p += 8;
-    (void)getdigits(&p, false, 0);
-    name = xstrndup(eap->arg, (size_t)(p - eap->arg));
-    CLEAR_FIELD(fudi);
-  } else {
-    name = (char *)trans_function_name(&p, eap->skip, TFN_NO_AUTOLOAD, &fudi, NULL);
-  }
+  name = save_function_name(&p, eap->skip, TFN_NO_AUTOLOAD, &fudi);
   paren = (vim_strchr(p, '(') != NULL);
   if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip) {
     // Return on an invalid expression in braces, unless the expression
diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim
index ea874cc398..9dbc923b96 100644
--- a/src/nvim/testdir/test_expr.vim
+++ b/src/nvim/testdir/test_expr.vim
@@ -496,6 +496,13 @@ func Test_function_with_funcref()
   call assert_fails("call function('foo()')", 'E475:')
   call assert_fails("call function('foo()')", 'foo()')
   call assert_fails("function('')", 'E129:')
+
+  let Len = {s -> strlen(s)}
+  call assert_equal(6, Len('foobar'))
+  let name = string(Len)
+  " can evaluate "function('99')"
+  call execute('let Ref = ' .. name)
+  call assert_equal(4, Ref('text'))
 endfunc
 
 func Test_funcref()
-- 
cgit 


From 1508618d4c35dafee2b82726d2d27fae4e314386 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 20 Aug 2022 09:35:09 +0800
Subject: vim-patch:8.2.3712: cannot use Vim9 lambda for 'tagfunc'

Problem:    Cannot use Vim9 lambda for 'tagfunc'.
Solution:   Make it work, add more tests. (Yegappan Lakshmanan, closes vim/vim#9250)
https://github.com/vim/vim/commit/05e59e3a9ffddbf93c7af02cd2ba1d0f822d4625

Omit Vim9 script in code and comment out in tests.

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/insexpand.c              |   6 +--
 src/nvim/testdir/test_tagfunc.vim | 107 ++++++++++++++++++++++++++++++++++----
 2 files changed, 99 insertions(+), 14 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index 9f4c02da19..7ff4735480 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -2239,10 +2239,10 @@ static char_u *get_complete_funcname(int type)
   }
 }
 
-/// Execute user defined complete function 'completefunc' or 'omnifunc', and
-/// get matches in "matches".
+/// Execute user defined complete function 'completefunc', 'omnifunc' or
+/// 'thesaurusfunc', and get matches in "matches".
 ///
-/// @param type  CTRL_X_OMNI or CTRL_X_FUNCTION
+/// @param type  either CTRL_X_OMNI or CTRL_X_FUNCTION or CTRL_X_THESAURUS
 static void expand_by_function(int type, char_u *base)
 {
   list_T *matchlist = NULL;
diff --git a/src/nvim/testdir/test_tagfunc.vim b/src/nvim/testdir/test_tagfunc.vim
index 92c2f2d6a1..29ca69278d 100644
--- a/src/nvim/testdir/test_tagfunc.vim
+++ b/src/nvim/testdir/test_tagfunc.vim
@@ -124,32 +124,73 @@ func Test_tagfunc_callback()
     let g:MytagFunc1_args = [a:pat, a:flags, a:info]
     return v:null
   endfunc
-  let g:MytagFunc1_args = []
   set tagfunc=function('MytagFunc1')
-  call assert_fails('tag abc', 'E433:')
-  call assert_equal(['abc', '', {}], g:MytagFunc1_args)
+  new | only
+  let g:MytagFunc1_args = []
+  call assert_fails('tag a11', 'E433:')
+  call assert_equal(['a11', '', {}], g:MytagFunc1_args)
+
+  " Using a funcref variable to set 'tagfunc'
+  let Fn = function('MytagFunc1')
+  let &tagfunc = string(Fn)
+  new | only
+  let g:MytagFunc1_args = []
+  call assert_fails('tag a12', 'E433:')
+  call assert_equal(['a12', '', {}], g:MytagFunc1_args)
+  call assert_fails('let &tagfunc = Fn', 'E729:')
 
   " Test for using a funcref()
-  new
   func MytagFunc2(pat, flags, info)
     let g:MytagFunc2_args = [a:pat, a:flags, a:info]
     return v:null
   endfunc
-  let g:MytagFunc2_args = []
   set tagfunc=funcref('MytagFunc2')
-  call assert_fails('tag def', 'E433:')
-  call assert_equal(['def', '', {}], g:MytagFunc2_args)
+  new | only
+  let g:MytagFunc2_args = []
+  call assert_fails('tag a13', 'E433:')
+  call assert_equal(['a13', '', {}], g:MytagFunc2_args)
+
+  " Using a funcref variable to set 'tagfunc'
+  let Fn = funcref('MytagFunc2')
+  let &tagfunc = string(Fn)
+  new | only
+  let g:MytagFunc2_args = []
+  call assert_fails('tag a14', 'E433:')
+  call assert_equal(['a14', '', {}], g:MytagFunc2_args)
+  call assert_fails('let &tagfunc = Fn', 'E729:')
 
   " Test for using a lambda function
-  new
   func MytagFunc3(pat, flags, info)
     let g:MytagFunc3_args = [a:pat, a:flags, a:info]
     return v:null
   endfunc
+  set tagfunc={a,\ b,\ c\ ->\ MytagFunc3(a,\ b,\ c)}
+  new | only
   let g:MytagFunc3_args = []
-  let &tagfunc= '{a, b, c -> MytagFunc3(a, b, c)}'
-  call assert_fails('tag ghi', 'E433:')
-  call assert_equal(['ghi', '', {}], g:MytagFunc3_args)
+  call assert_fails('tag a15', 'E433:')
+  call assert_equal(['a15', '', {}], g:MytagFunc3_args)
+
+  " Set 'tagfunc' to a lambda expression
+  let &tagfunc = '{a, b, c -> MytagFunc3(a, b, c)}'
+  new | only
+  let g:MytagFunc3_args = []
+  call assert_fails('tag a16', 'E433:')
+  call assert_equal(['a16', '', {}], g:MytagFunc3_args)
+
+  " Set 'tagfunc' to a variable with a lambda expression
+  let Lambda = {a, b, c -> MytagFunc3(a, b, c)}
+  let &tagfunc = string(Lambda)
+  new | only
+  let g:MytagFunc3_args = []
+  call assert_fails("tag a17", "E433:")
+  call assert_equal(['a17', '', {}], g:MytagFunc3_args)
+  call assert_fails('let &tagfunc = Lambda', 'E729:')
+
+  " Test for using a lambda function with incorrect return value
+  let Lambda = {s -> strlen(s)}
+  let &tagfunc = string(Lambda)
+  new | only
+  call assert_fails("tag a17", "E987:")
 
   " Test for clearing the 'tagfunc' option
   set tagfunc=''
@@ -160,10 +201,54 @@ func Test_tagfunc_callback()
   let &tagfunc = "{a -> 'abc'}"
   call assert_fails("echo taglist('a')", "E987:")
 
+  " Vim9 tests
+  let lines =<< trim END
+    vim9script
+
+    # Test for using function()
+    def MytagFunc1(pat: string, flags: string, info: dict): any
+      g:MytagFunc1_args = [pat, flags, info]
+      return null
+    enddef
+    set tagfunc=function('MytagFunc1')
+    new | only
+    g:MytagFunc1_args = []
+    assert_fails('tag a10', 'E433:')
+    assert_equal(['a10', '', {}], g:MytagFunc1_args)
+
+    # Test for using a lambda
+    def MytagFunc2(pat: string, flags: string, info: dict): any
+      g:MytagFunc2_args = [pat, flags, info]
+      return null
+    enddef
+    &tagfunc = '(a, b, c) => MytagFunc2(a, b, c)'
+    new | only
+    g:MytagFunc2_args = []
+    assert_fails('tag a20', 'E433:')
+    assert_equal(['a20', '', {}], g:MytagFunc2_args)
+
+    # Test for using a variable with a lambda expression
+    var Fn: func = (a, b, c) => MytagFunc2(a, b, c)
+    &tagfunc = string(Fn)
+    new | only
+    g:MytagFunc2_args = []
+    assert_fails('tag a30', 'E433:')
+    assert_equal(['a30', '', {}], g:MytagFunc2_args)
+  END
+  " call CheckScriptSuccess(lines)
+
+  " Using Vim9 lambda expression in legacy context should fail
+  " set tagfunc=(a,\ b,\ c)\ =>\ g:MytagFunc2(a,\ b,\ c)
+  " new | only
+  " let g:MytagFunc3_args = []
+  " call assert_fails("tag a17", "E117:")
+  " call assert_equal([], g:MytagFunc3_args)
+
   " cleanup
   delfunc MytagFunc1
   delfunc MytagFunc2
   delfunc MytagFunc3
+  set tagfunc&
   %bw!
 endfunc
 
-- 
cgit 


From 1e4adf4b56cb7a360d16c4d04290c50ae7e80fbc Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 20 Aug 2022 08:45:15 +0800
Subject: vim-patch:8.2.3725: cannot use a lambda for 'completefunc' and
 'omnifunc'

Problem:    Cannot use a lambda for 'completefunc' and 'omnifunc'.
Solution:   Implement lambda support. (Yegappan Lakshmanan, closes vim/vim#9257)
https://github.com/vim/vim/commit/8658c759f05b317707d56e3b65a5ef63930c7498

Comment out Vim9 script in tests.

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/buffer.c                      |   3 +
 src/nvim/buffer_defs.h                 |   3 +
 src/nvim/eval.c                        |  19 +-
 src/nvim/eval/userfunc.c               |  18 ++
 src/nvim/insexpand.c                   | 100 ++++++-
 src/nvim/option.c                      |   5 +-
 src/nvim/optionstr.c                   |  12 +
 src/nvim/tag.c                         |   2 +-
 src/nvim/testdir/test_ins_complete.vim | 499 +++++++++++++++++++++++++++++++++
 src/nvim/testdir/test_tagfunc.vim      |  35 ++-
 10 files changed, 667 insertions(+), 29 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 4c8faccaa7..2c87677925 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1971,8 +1971,11 @@ void free_buf_options(buf_T *buf, int free_p_ff)
   clear_string_option(&buf->b_p_cinw);
   clear_string_option(&buf->b_p_cpt);
   clear_string_option(&buf->b_p_cfu);
+  callback_free(&buf->b_cfu_cb);
   clear_string_option(&buf->b_p_ofu);
+  callback_free(&buf->b_ofu_cb);
   clear_string_option(&buf->b_p_tsrfu);
+  callback_free(&buf->b_tsrfu_cb);
   clear_string_option(&buf->b_p_gp);
   clear_string_option(&buf->b_p_mp);
   clear_string_option(&buf->b_p_efm);
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index d8e86a75cf..6448c6b6f6 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -675,7 +675,9 @@ struct file_buffer {
   char *b_p_csl;                ///< 'completeslash'
 #endif
   char *b_p_cfu;                ///< 'completefunc'
+  Callback b_cfu_cb;            ///< 'completefunc' callback
   char *b_p_ofu;                ///< 'omnifunc'
+  Callback b_ofu_cb;            ///< 'omnifunc' callback
   char *b_p_tfu;                ///< 'tagfunc'
   Callback b_tfu_cb;            ///< 'tagfunc' callback
   int b_p_eof;                  ///< 'endoffile'
@@ -749,6 +751,7 @@ struct file_buffer {
   char *b_p_dict;               ///< 'dictionary' local value
   char *b_p_tsr;                ///< 'thesaurus' local value
   char *b_p_tsrfu;              ///< 'thesaurusfunc' local value
+  Callback b_tsrfu_cb;          ///< 'thesaurusfunc' callback
   long b_p_ul;                  ///< 'undolevels' local value
   int b_p_udf;                  ///< 'undofile'
   char *b_p_lw;                 ///< 'lispwords' local value
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index c983388450..c578d9fd39 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -1110,25 +1110,7 @@ fail:
 
   return ret;
 }
-/// Call Vim script function and return the result as a number
-///
-/// @param[in]  func  Function name.
-/// @param[in]  argc  Number of arguments.
-/// @param[in]  argv  Array with typval_T arguments.
-///
-/// @return -1 when calling function fails, result of function otherwise.
-varnumber_T call_func_retnr(const char *func, int argc, typval_T *argv)
-  FUNC_ATTR_NONNULL_ALL
-{
-  typval_T rettv;
 
-  if (call_vim_function((char *)func, argc, argv, &rettv) == FAIL) {
-    return -1;
-  }
-  varnumber_T retval = tv_get_number_chk(&rettv, NULL);
-  tv_clear(&rettv);
-  return retval;
-}
 /// Call Vim script function and return the result as a string
 ///
 /// @param[in]  func  Function name.
@@ -1151,6 +1133,7 @@ char *call_func_retstr(const char *const func, int argc, typval_T *argv)
   tv_clear(&rettv);
   return retval;
 }
+
 /// Call Vim script function and return the result as a List
 ///
 /// @param[in]  func  Function name.
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index e6b038a335..d07cfe0bd9 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -1393,6 +1393,24 @@ func_call_skip_call:
   return r;
 }
 
+/// call the 'callback' function and return the result as a number.
+/// Returns -1 when calling the function fails.  Uses argv[0] to argv[argc - 1]
+/// for the function arguments. argv[argc] should have type VAR_UNKNOWN.
+///
+/// @param argcount  number of "argvars"
+/// @param argvars   vars for arguments, must have "argcount" PLUS ONE elements!
+varnumber_T callback_call_retnr(Callback *callback, int argcount, typval_T *argvars)
+{
+  typval_T rettv;
+  if (!callback_call(callback, argcount, argvars, &rettv)) {
+    return -1;
+  }
+
+  varnumber_T retval = tv_get_number_chk(&rettv, NULL);
+  tv_clear(&rettv);
+  return retval;
+}
+
 /// Give an error message for the result of a function.
 /// Nothing if "error" is FCERR_NONE.
 static void user_func_error(int error, const char_u *name)
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index 7ff4735480..1acdddedef 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -18,6 +18,7 @@
 #include "nvim/edit.h"
 #include "nvim/eval.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/userfunc.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_eval.h"
 #include "nvim/ex_getln.h"
@@ -2224,6 +2225,82 @@ static buf_T *ins_compl_next_buf(buf_T *buf, int flag)
   return buf;
 }
 
+static Callback cfu_cb;    ///< 'completefunc' callback function
+static Callback ofu_cb;    ///< 'omnifunc' callback function
+static Callback tsrfu_cb;  ///< 'thesaurusfunc' callback function
+
+/// Copy a global callback function to a buffer local callback.
+static void copy_global_to_buflocal_cb(Callback *globcb, Callback *bufcb)
+{
+  callback_free(bufcb);
+  if (globcb->data.funcref != NULL && *globcb->data.funcref != NUL) {
+    callback_copy(bufcb, globcb);
+  }
+}
+
+/// Parse the 'completefunc' option value and set the callback function.
+/// Invoked when the 'completefunc' option is set. The option value can be a
+/// name of a function (string), or function() or funcref() or a
+/// lambda expression.
+int set_completefunc_option(void)
+{
+  int retval = option_set_callback_func(curbuf->b_p_cfu, &cfu_cb);
+  if (retval == OK) {
+    set_buflocal_cfu_callback(curbuf);
+  }
+
+  return retval;
+}
+
+/// Copy the global 'completefunc' callback function to the buffer-local
+/// 'completefunc' callback for "buf".
+void set_buflocal_cfu_callback(buf_T *buf)
+{
+  copy_global_to_buflocal_cb(&cfu_cb, &buf->b_cfu_cb);
+}
+
+/// Parse the 'omnifunc' option value and set the callback function.
+/// Invoked when the 'omnifunc' option is set. The option value can be a
+/// name of a function (string), or function() or funcref() or a
+/// lambda expression.
+int set_omnifunc_option(void)
+{
+  int retval = option_set_callback_func(curbuf->b_p_ofu, &ofu_cb);
+  if (retval == OK) {
+    set_buflocal_ofu_callback(curbuf);
+  }
+
+  return retval;
+}
+
+/// Copy the global 'omnifunc' callback function to the buffer-local 'omnifunc'
+/// callback for "buf".
+void set_buflocal_ofu_callback(buf_T *buf)
+{
+  copy_global_to_buflocal_cb(&ofu_cb, &buf->b_ofu_cb);
+}
+
+/// Parse the 'thesaurusfunc' option value and set the callback function.
+/// Invoked when the 'thesaurusfunc' option is set. The option value can be a
+/// name of a function (string), or function() or funcref() or a
+/// lambda expression.
+int set_thesaurusfunc_option(void)
+{
+  int retval;
+
+  if (*curbuf->b_p_tsrfu != NUL) {
+    // buffer-local option set
+    callback_free(&curbuf->b_tsrfu_cb);
+    retval = option_set_callback_func(curbuf->b_p_tsrfu, &curbuf->b_tsrfu_cb);
+  } else {
+    // global option set
+    callback_free(&tsrfu_cb);
+    retval = option_set_callback_func(p_tsrfu, &tsrfu_cb);
+  }
+
+  return retval;
+}
+
 /// Get the user-defined completion function name for completion "type"
 static char_u *get_complete_funcname(int type)
 {
@@ -2239,6 +2316,19 @@ static char_u *get_complete_funcname(int type)
   }
 }
 
+/// Get the callback to use for insert mode completion.
+static Callback *get_insert_callback(int type)
+{
+  if (type == CTRL_X_FUNCTION) {
+    return &curbuf->b_cfu_cb;
+  }
+  if (type == CTRL_X_OMNI) {
+    return &curbuf->b_ofu_cb;
+  }
+  // CTRL_X_THESAURUS
+  return (*curbuf->b_p_tsrfu != NUL) ? &curbuf->b_tsrfu_cb : &tsrfu_cb;
+}
+
 /// Execute user defined complete function 'completefunc', 'omnifunc' or
 /// 'thesaurusfunc', and get matches in "matches".
 ///
@@ -2272,8 +2362,10 @@ static void expand_by_function(int type, char_u *base)
   // Insert mode in another buffer.
   textlock++;
 
+  Callback *cb = get_insert_callback(type);
+
   // Call a function, which returns a list or dict.
-  if (call_vim_function((char *)funcname, 2, args, &rettv) == OK) {
+  if (callback_call(cb, 2, args, &rettv)) {
     switch (rettv.v_type) {
     case VAR_LIST:
       matchlist = rettv.vval.v_list;
@@ -3851,7 +3943,8 @@ static int get_userdefined_compl_info(colnr_T curs_col)
 
   pos_T pos = curwin->w_cursor;
   textlock++;
-  colnr_T col = (colnr_T)call_func_retnr((char *)funcname, 2, args);
+  Callback *cb = get_insert_callback(ctrl_x_mode);
+  colnr_T col = (colnr_T)callback_call_retnr(cb, 2, args);
   textlock--;
 
   State = save_State;
@@ -4354,6 +4447,9 @@ static unsigned quote_meta(char_u *dest, char_u *src, int len)
 void free_insexpand_stuff(void)
 {
   XFREE_CLEAR(compl_orig_text);
+  callback_free(&cfu_cb);
+  callback_free(&ofu_cb);
+  callback_free(&tsrfu_cb);
 }
 #endif
 
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 1eabe8c540..da3fad0d61 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -51,6 +51,7 @@
 #include "nvim/highlight_group.h"
 #include "nvim/indent.h"
 #include "nvim/indent_c.h"
+#include "nvim/insexpand.h"
 #include "nvim/keycodes.h"
 #include "nvim/locale.h"
 #include "nvim/macros.h"
@@ -4381,11 +4382,13 @@ void buf_copy_options(buf_T *buf, int flags)
 #endif
       buf->b_p_cfu = xstrdup(p_cfu);
       COPY_OPT_SCTX(buf, BV_CFU);
+      set_buflocal_cfu_callback(buf);
       buf->b_p_ofu = xstrdup(p_ofu);
       COPY_OPT_SCTX(buf, BV_OFU);
+      set_buflocal_ofu_callback(buf);
       buf->b_p_tfu = xstrdup(p_tfu);
       COPY_OPT_SCTX(buf, BV_TFU);
-      buf_set_tfu_callback(buf);
+      set_buflocal_tfu_callback(buf);
       buf->b_p_sts = p_sts;
       COPY_OPT_SCTX(buf, BV_STS);
       buf->b_p_sts_nopaste = p_sts_nopaste;
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index abae338daa..b088a4c8c7 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -1473,6 +1473,18 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
         }
       }
     }
+  } else if (gvarp == &p_cfu) {  // 'completefunc'
+    if (set_completefunc_option() == FAIL) {
+      errmsg = e_invarg;
+    }
+  } else if (gvarp == &p_ofu) {  // 'omnifunc'
+    if (set_omnifunc_option() == FAIL) {
+      errmsg = e_invarg;
+    }
+  } else if (gvarp == &p_tsrfu) {  // 'thesaurusfunc'
+    if (set_thesaurusfunc_option() == FAIL) {
+      errmsg = e_invarg;
+    }
   } else if (varp == &p_opfunc) {  // 'operatorfunc'
     if (set_operatorfunc_option() == FAIL) {
       errmsg = e_invarg;
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index e38fbb7da9..d6e62cff6d 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -149,7 +149,7 @@ void free_tagfunc_option(void)
 
 /// Copy the global 'tagfunc' callback function to the buffer-local 'tagfunc'
 /// callback for 'buf'.
-void buf_set_tfu_callback(buf_T *buf)
+void set_buflocal_tfu_callback(buf_T *buf)
 {
   callback_free(&buf->b_tfu_cb);
   if (tfu_cb.data.funcref != NULL && *tfu_cb.data.funcref != NUL) {
diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim
index f706322a85..fc612dcbe2 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -1285,6 +1285,505 @@ func Test_no_mapping_for_ctrl_x_key()
   bwipe!
 endfunc
 
+" Test for different ways of setting the 'completefunc' option
+func Test_completefunc_callback()
+  " Test for using a function()
+  func MycompleteFunc1(findstart, base)
+    call add(g:MycompleteFunc1_args, [a:findstart, a:base])
+    return a:findstart ? 0 : []
+  endfunc
+  set completefunc=function('MycompleteFunc1')
+  new | only
+  call setline(1, 'one')
+  let g:MycompleteFunc1_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'one']], g:MycompleteFunc1_args)
+  bw!
+
+  " Using a funcref variable to set 'completefunc'
+  let Fn = function('MycompleteFunc1')
+  let &completefunc = string(Fn)
+  new | only
+  call setline(1, 'two')
+  let g:MycompleteFunc1_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'two']], g:MycompleteFunc1_args)
+  call assert_fails('let &completefunc = Fn', 'E729:')
+  bw!
+
+  " Test for using a funcref()
+  func MycompleteFunc2(findstart, base)
+    call add(g:MycompleteFunc2_args, [a:findstart, a:base])
+    return a:findstart ? 0 : []
+  endfunc
+  set completefunc=funcref('MycompleteFunc2')
+  new | only
+  call setline(1, 'three')
+  let g:MycompleteFunc2_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'three']], g:MycompleteFunc2_args)
+  bw!
+
+  " Using a funcref variable to set 'completefunc'
+  let Fn = funcref('MycompleteFunc2')
+  let &completefunc = string(Fn)
+  new | only
+  call setline(1, 'four')
+  let g:MycompleteFunc2_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'four']], g:MycompleteFunc2_args)
+  call assert_fails('let &completefunc = Fn', 'E729:')
+  bw!
+
+  " Test for using a lambda function
+  func MycompleteFunc3(findstart, base)
+    call add(g:MycompleteFunc3_args, [a:findstart, a:base])
+    return a:findstart ? 0 : []
+  endfunc
+  set completefunc={a,\ b,\ ->\ MycompleteFunc3(a,\ b,)}
+  new | only
+  call setline(1, 'five')
+  let g:MycompleteFunc3_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'five']], g:MycompleteFunc3_args)
+  bw!
+
+  " Set 'completefunc' to a lambda expression
+  let &completefunc = '{a, b -> MycompleteFunc3(a, b)}'
+  new | only
+  call setline(1, 'six')
+  let g:MycompleteFunc3_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'six']], g:MycompleteFunc3_args)
+  bw!
+
+  " Set 'completefunc' to a variable with a lambda expression
+  let Lambda = {a, b -> MycompleteFunc3(a, b)}
+  let &completefunc = string(Lambda)
+  new | only
+  call setline(1, 'seven')
+  let g:MycompleteFunc3_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'seven']], g:MycompleteFunc3_args)
+  call assert_fails('let &completefunc = Lambda', 'E729:')
+  bw!
+
+  " Test for using a lambda function with incorrect return value
+  let Lambda = {s -> strlen(s)}
+  let &completefunc = string(Lambda)
+  new | only
+  call setline(1, 'eight')
+  call feedkeys("A\\\", 'x')
+  bw!
+
+  " Test for clearing the 'completefunc' option
+  set completefunc=''
+  set completefunc&
+
+  call assert_fails("set completefunc=function('abc')", "E700:")
+  call assert_fails("set completefunc=funcref('abc')", "E700:")
+  let &completefunc = "{a -> 'abc'}"
+  call feedkeys("A\\\", 'x')
+
+  " Vim9 tests
+  let lines =<< trim END
+    vim9script
+
+    # Test for using function()
+    def MycompleteFunc1(findstart: number, base: string): any
+      add(g:MycompleteFunc1_args, [findstart, base])
+      return findstart ? 0 : []
+    enddef
+    set completefunc=function('MycompleteFunc1')
+    new | only
+    setline(1, 'one')
+    g:MycompleteFunc1_args = []
+    feedkeys("A\\\", 'x')
+    assert_equal([[1, ''], [0, 'one']], g:MycompleteFunc1_args)
+    bw!
+
+    # Test for using a lambda
+    def MycompleteFunc2(findstart: number, base: string): any
+      add(g:MycompleteFunc2_args, [findstart, base])
+      return findstart ? 0 : []
+    enddef
+    &completefunc = '(a, b) => MycompleteFunc2(a, b)'
+    new | only
+    setline(1, 'two')
+    g:MycompleteFunc2_args = []
+    feedkeys("A\\\", 'x')
+    assert_equal([[1, ''], [0, 'two']], g:MycompleteFunc2_args)
+    bw!
+
+    # Test for using a variable with a lambda expression
+    var Fn: func = (a, b) => MycompleteFunc2(a, b)
+    &completefunc = string(Fn)
+    new | only
+    setline(1, 'three')
+    g:MycompleteFunc2_args = []
+    feedkeys("A\\\", 'x')
+    assert_equal([[1, ''], [0, 'three']], g:MycompleteFunc2_args)
+    bw!
+  END
+  " call CheckScriptSuccess(lines)
+
+  " Using Vim9 lambda expression in legacy context should fail
+  " set completefunc=(a,\ b)\ =>\ g:MycompleteFunc2(a,\ b)
+  " new | only
+  " let g:MycompleteFunc2_args = []
+  " call assert_fails('call feedkeys("A\\\", "x")', 'E117:')
+  " call assert_equal([], g:MycompleteFunc2_args)
+
+  " cleanup
+  delfunc MycompleteFunc1
+  delfunc MycompleteFunc2
+  delfunc MycompleteFunc3
+  set completefunc&
+  %bw!
+endfunc
+
+" Test for different ways of setting the 'omnifunc' option
+func Test_omnifunc_callback()
+  " Test for using a function()
+  func MyomniFunc1(findstart, base)
+    call add(g:MyomniFunc1_args, [a:findstart, a:base])
+    return a:findstart ? 0 : []
+  endfunc
+  set omnifunc=function('MyomniFunc1')
+  new | only
+  call setline(1, 'one')
+  let g:MyomniFunc1_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'one']], g:MyomniFunc1_args)
+  bw!
+
+  " Using a funcref variable to set 'omnifunc'
+  let Fn = function('MyomniFunc1')
+  let &omnifunc = string(Fn)
+  new | only
+  call setline(1, 'two')
+  let g:MyomniFunc1_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'two']], g:MyomniFunc1_args)
+  call assert_fails('let &omnifunc = Fn', 'E729:')
+  bw!
+
+  " Test for using a funcref()
+  func MyomniFunc2(findstart, base)
+    call add(g:MyomniFunc2_args, [a:findstart, a:base])
+    return a:findstart ? 0 : []
+  endfunc
+  set omnifunc=funcref('MyomniFunc2')
+  new | only
+  call setline(1, 'three')
+  let g:MyomniFunc2_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'three']], g:MyomniFunc2_args)
+  bw!
+
+  " Using a funcref variable to set 'omnifunc'
+  let Fn = funcref('MyomniFunc2')
+  let &omnifunc = string(Fn)
+  new | only
+  call setline(1, 'four')
+  let g:MyomniFunc2_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'four']], g:MyomniFunc2_args)
+  call assert_fails('let &omnifunc = Fn', 'E729:')
+  bw!
+
+  " Test for using a lambda function
+  func MyomniFunc3(findstart, base)
+    call add(g:MyomniFunc3_args, [a:findstart, a:base])
+    return a:findstart ? 0 : []
+  endfunc
+  set omnifunc={a,\ b,\ ->\ MyomniFunc3(a,\ b,)}
+  new | only
+  call setline(1, 'five')
+  let g:MyomniFunc3_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'five']], g:MyomniFunc3_args)
+  bw!
+
+  " Set 'omnifunc' to a lambda expression
+  let &omnifunc = '{a, b -> MyomniFunc3(a, b)}'
+  new | only
+  call setline(1, 'six')
+  let g:MyomniFunc3_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'six']], g:MyomniFunc3_args)
+  bw!
+
+  " Set 'omnifunc' to a variable with a lambda expression
+  let Lambda = {a, b -> MyomniFunc3(a, b)}
+  let &omnifunc = string(Lambda)
+  new | only
+  call setline(1, 'seven')
+  let g:MyomniFunc3_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'seven']], g:MyomniFunc3_args)
+  call assert_fails('let &omnifunc = Lambda', 'E729:')
+  bw!
+
+  " Test for using a lambda function with incorrect return value
+  let Lambda = {s -> strlen(s)}
+  let &omnifunc = string(Lambda)
+  new | only
+  call setline(1, 'eight')
+  call feedkeys("A\\\", 'x')
+  bw!
+
+  " Test for clearing the 'omnifunc' option
+  set omnifunc=''
+  set omnifunc&
+
+  call assert_fails("set omnifunc=function('abc')", "E700:")
+  call assert_fails("set omnifunc=funcref('abc')", "E700:")
+  let &omnifunc = "{a -> 'abc'}"
+  call feedkeys("A\\\", 'x')
+
+  " Vim9 tests
+  let lines =<< trim END
+    vim9script
+
+    # Test for using function()
+    def MyomniFunc1(findstart: number, base: string): any
+      add(g:MyomniFunc1_args, [findstart, base])
+      return findstart ? 0 : []
+    enddef
+    set omnifunc=function('MyomniFunc1')
+    new | only
+    setline(1, 'one')
+    g:MyomniFunc1_args = []
+    feedkeys("A\\\", 'x')
+    assert_equal([[1, ''], [0, 'one']], g:MyomniFunc1_args)
+    bw!
+
+    # Test for using a lambda
+    def MyomniFunc2(findstart: number, base: string): any
+      add(g:MyomniFunc2_args, [findstart, base])
+      return findstart ? 0 : []
+    enddef
+    &omnifunc = '(a, b) => MyomniFunc2(a, b)'
+    new | only
+    setline(1, 'two')
+    g:MyomniFunc2_args = []
+    feedkeys("A\\\", 'x')
+    assert_equal([[1, ''], [0, 'two']], g:MyomniFunc2_args)
+    bw!
+
+    # Test for using a variable with a lambda expression
+    var Fn: func = (a, b) => MyomniFunc2(a, b)
+    &omnifunc = string(Fn)
+    new | only
+    setline(1, 'three')
+    g:MyomniFunc2_args = []
+    feedkeys("A\\\", 'x')
+    assert_equal([[1, ''], [0, 'three']], g:MyomniFunc2_args)
+    bw!
+  END
+  " call CheckScriptSuccess(lines)
+
+  " Using Vim9 lambda expression in legacy context should fail
+  " set omnifunc=(a,\ b)\ =>\ g:MyomniFunc2(a,\ b)
+  " new | only
+  " let g:MyomniFunc2_args = []
+  " call assert_fails('call feedkeys("A\\\", "x")', 'E117:')
+  " call assert_equal([], g:MyomniFunc2_args)
+
+  " cleanup
+  delfunc MyomniFunc1
+  delfunc MyomniFunc2
+  delfunc MyomniFunc3
+  set omnifunc&
+  %bw!
+endfunc
+
+" Test for different ways of setting the 'thesaurusfunc' option
+func Test_thesaurusfunc_callback()
+  " Test for using a function()
+  func MytsrFunc1(findstart, base)
+    call add(g:MytsrFunc1_args, [a:findstart, a:base])
+    return a:findstart ? 0 : []
+  endfunc
+  set thesaurusfunc=function('MytsrFunc1')
+  new | only
+  call setline(1, 'one')
+  let g:MytsrFunc1_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'one']], g:MytsrFunc1_args)
+  bw!
+
+  " Using a funcref variable to set 'thesaurusfunc'
+  let Fn = function('MytsrFunc1')
+  let &thesaurusfunc = string(Fn)
+  new | only
+  call setline(1, 'two')
+  let g:MytsrFunc1_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'two']], g:MytsrFunc1_args)
+  call assert_fails('let &thesaurusfunc = Fn', 'E729:')
+  bw!
+
+  " Test for using a funcref()
+  func MytsrFunc2(findstart, base)
+    call add(g:MytsrFunc2_args, [a:findstart, a:base])
+    return a:findstart ? 0 : []
+  endfunc
+  set thesaurusfunc=funcref('MytsrFunc2')
+  new | only
+  call setline(1, 'three')
+  let g:MytsrFunc2_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'three']], g:MytsrFunc2_args)
+  bw!
+
+  " Using a funcref variable to set 'thesaurusfunc'
+  let Fn = funcref('MytsrFunc2')
+  let &thesaurusfunc = string(Fn)
+  new | only
+  call setline(1, 'four')
+  let g:MytsrFunc2_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'four']], g:MytsrFunc2_args)
+  call assert_fails('let &thesaurusfunc = Fn', 'E729:')
+  bw!
+
+  " Test for using a lambda function
+  func MytsrFunc3(findstart, base)
+    call add(g:MytsrFunc3_args, [a:findstart, a:base])
+    return a:findstart ? 0 : []
+  endfunc
+  set thesaurusfunc={a,\ b,\ ->\ MytsrFunc3(a,\ b,)}
+  new | only
+  call setline(1, 'five')
+  let g:MytsrFunc3_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'five']], g:MytsrFunc3_args)
+  bw!
+
+  " Set 'thesaurusfunc' to a lambda expression
+  let &thesaurusfunc = '{a, b -> MytsrFunc3(a, b)}'
+  new | only
+  call setline(1, 'six')
+  let g:MytsrFunc3_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'six']], g:MytsrFunc3_args)
+  bw!
+
+  " Set 'thesaurusfunc' to a variable with a lambda expression
+  let Lambda = {a, b -> MytsrFunc3(a, b)}
+  let &thesaurusfunc = string(Lambda)
+  new | only
+  call setline(1, 'seven')
+  let g:MytsrFunc3_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'seven']], g:MytsrFunc3_args)
+  call assert_fails('let &thesaurusfunc = Lambda', 'E729:')
+  bw!
+
+  " Test for using a lambda function with incorrect return value
+  let Lambda = {s -> strlen(s)}
+  let &thesaurusfunc = string(Lambda)
+  new | only
+  call setline(1, 'eight')
+  call feedkeys("A\\\", 'x')
+  bw!
+
+  " Test for clearing the 'thesaurusfunc' option
+  set thesaurusfunc=''
+  set thesaurusfunc&
+
+  call assert_fails("set thesaurusfunc=function('abc')", "E700:")
+  call assert_fails("set thesaurusfunc=funcref('abc')", "E700:")
+  let &thesaurusfunc = "{a -> 'abc'}"
+  call feedkeys("A\\\", 'x')
+
+  " Vim9 tests
+  let lines =<< trim END
+    vim9script
+
+    # Test for using function()
+    def MytsrFunc1(findstart: number, base: string): any
+      add(g:MytsrFunc1_args, [findstart, base])
+      return findstart ? 0 : []
+    enddef
+    set thesaurusfunc=function('MytsrFunc1')
+    new | only
+    setline(1, 'one')
+    g:MytsrFunc1_args = []
+    feedkeys("A\\\", 'x')
+    assert_equal([[1, ''], [0, 'one']], g:MytsrFunc1_args)
+    bw!
+
+    # Test for using a lambda
+    def MytsrFunc2(findstart: number, base: string): any
+      add(g:MytsrFunc2_args, [findstart, base])
+      return findstart ? 0 : []
+    enddef
+    &thesaurusfunc = '(a, b) => MytsrFunc2(a, b)'
+    new | only
+    setline(1, 'two')
+    g:MytsrFunc2_args = []
+    feedkeys("A\\\", 'x')
+    assert_equal([[1, ''], [0, 'two']], g:MytsrFunc2_args)
+    bw!
+
+    # Test for using a variable with a lambda expression
+    var Fn: func = (a, b) => MytsrFunc2(a, b)
+    &thesaurusfunc = string(Fn)
+    new | only
+    setline(1, 'three')
+    g:MytsrFunc2_args = []
+    feedkeys("A\\\", 'x')
+    assert_equal([[1, ''], [0, 'three']], g:MytsrFunc2_args)
+    bw!
+  END
+  " call CheckScriptSuccess(lines)
+
+  " Using Vim9 lambda expression in legacy context should fail
+  " set thesaurusfunc=(a,\ b)\ =>\ g:MytsrFunc2(a,\ b)
+  " new | only
+  " let g:MytsrFunc2_args = []
+  " call assert_fails('call feedkeys("A\\\", "x")', 'E117:')
+  " call assert_equal([], g:MytsrFunc2_args)
+  " bw!
+
+  " Use a buffer-local value and a global value
+  func MytsrFunc4(findstart, base)
+    call add(g:MytsrFunc4_args, [a:findstart, a:base])
+    return a:findstart ? 0 : ['sunday']
+  endfunc
+  set thesaurusfunc&
+  setlocal thesaurusfunc=function('MytsrFunc4')
+  call setline(1, 'sun')
+  let g:MytsrFunc4_args = []
+  call feedkeys("A\\\", "x")
+  call assert_equal('sunday', getline(1))
+  call assert_equal([[1, ''], [0, 'sun']], g:MytsrFunc4_args)
+  new
+  call setline(1, 'sun')
+  let g:MytsrFunc4_args = []
+  call feedkeys("A\\\", "x")
+  call assert_equal('sun', getline(1))
+  call assert_equal([], g:MytsrFunc4_args)
+  set thesaurusfunc=function('MytsrFunc1')
+  wincmd w
+  call setline(1, 'sun')
+  let g:MytsrFunc4_args = []
+  call feedkeys("A\\\", "x")
+  call assert_equal('sunday', getline(1))
+  call assert_equal([[1, ''], [0, 'sun']], g:MytsrFunc4_args)
+
+  " cleanup
+  set thesaurusfunc&
+  delfunc MytsrFunc1
+  delfunc MytsrFunc2
+  delfunc MytsrFunc3
+  delfunc MytsrFunc4
+  %bw!
+endfunc
+
 func FooBarComplete(findstart, base)
   if a:findstart
     return col('.') - 1
diff --git a/src/nvim/testdir/test_tagfunc.vim b/src/nvim/testdir/test_tagfunc.vim
index 29ca69278d..e3085b9395 100644
--- a/src/nvim/testdir/test_tagfunc.vim
+++ b/src/nvim/testdir/test_tagfunc.vim
@@ -117,6 +117,12 @@ func Test_tagfunc_settagstack()
   delfunc Mytagfunc2
 endfunc
 
+" Script local tagfunc callback function
+func s:ScriptLocalTagFunc(pat, flags, info)
+  let g:ScriptLocalFuncArgs = [a:pat, a:flags, a:info]
+  return v:null
+endfunc
+
 " Test for different ways of setting the 'tagfunc' option
 func Test_tagfunc_callback()
   " Test for using a function()
@@ -159,6 +165,21 @@ func Test_tagfunc_callback()
   call assert_equal(['a14', '', {}], g:MytagFunc2_args)
   call assert_fails('let &tagfunc = Fn', 'E729:')
 
+  " Test for using a script local function
+  set tagfunc=ScriptLocalTagFunc
+  new | only
+  let g:ScriptLocalFuncArgs = []
+  call assert_fails('tag a15', 'E433:')
+  call assert_equal(['a15', '', {}], g:ScriptLocalFuncArgs)
+
+  " Test for using a script local funcref variable
+  let Fn = function("s:ScriptLocalTagFunc")
+  let &tagfunc= string(Fn)
+  new | only
+  let g:ScriptLocalFuncArgs = []
+  call assert_fails('tag a16', 'E433:')
+  call assert_equal(['a16', '', {}], g:ScriptLocalFuncArgs)
+
   " Test for using a lambda function
   func MytagFunc3(pat, flags, info)
     let g:MytagFunc3_args = [a:pat, a:flags, a:info]
@@ -167,30 +188,30 @@ func Test_tagfunc_callback()
   set tagfunc={a,\ b,\ c\ ->\ MytagFunc3(a,\ b,\ c)}
   new | only
   let g:MytagFunc3_args = []
-  call assert_fails('tag a15', 'E433:')
-  call assert_equal(['a15', '', {}], g:MytagFunc3_args)
+  call assert_fails('tag a17', 'E433:')
+  call assert_equal(['a17', '', {}], g:MytagFunc3_args)
 
   " Set 'tagfunc' to a lambda expression
   let &tagfunc = '{a, b, c -> MytagFunc3(a, b, c)}'
   new | only
   let g:MytagFunc3_args = []
-  call assert_fails('tag a16', 'E433:')
-  call assert_equal(['a16', '', {}], g:MytagFunc3_args)
+  call assert_fails('tag a18', 'E433:')
+  call assert_equal(['a18', '', {}], g:MytagFunc3_args)
 
   " Set 'tagfunc' to a variable with a lambda expression
   let Lambda = {a, b, c -> MytagFunc3(a, b, c)}
   let &tagfunc = string(Lambda)
   new | only
   let g:MytagFunc3_args = []
-  call assert_fails("tag a17", "E433:")
-  call assert_equal(['a17', '', {}], g:MytagFunc3_args)
+  call assert_fails("tag a19", "E433:")
+  call assert_equal(['a19', '', {}], g:MytagFunc3_args)
   call assert_fails('let &tagfunc = Lambda', 'E729:')
 
   " Test for using a lambda function with incorrect return value
   let Lambda = {s -> strlen(s)}
   let &tagfunc = string(Lambda)
   new | only
-  call assert_fails("tag a17", "E987:")
+  call assert_fails("tag a20", "E987:")
 
   " Test for clearing the 'tagfunc' option
   set tagfunc=''
-- 
cgit 


From 595f7f37a98f3af12fe94ba4332b8f33004b7e4b Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 16:59:07 +0800
Subject: vim-patch:9.0.0246: using freed memory when 'tagfunc' deletes the
 buffer

Problem:    Using freed memory when 'tagfunc' deletes the buffer.
Solution:   Make a copy of the tag name.

https://github.com/vim/vim/commit/adce965162dd89bf29ee0e5baf53652e7515762c

Co-authored-by: Bram Moolenaar 
---
 src/nvim/tag.c                    |  7 ++++++-
 src/nvim/testdir/test_tagfunc.vim | 12 ++++++++++++
 2 files changed, 18 insertions(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index d6e62cff6d..5216919d98 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -199,6 +199,7 @@ void do_tag(char *tag, int type, int count, int forceit, int verbose)
   int skip_msg = false;
   char_u *buf_ffname = (char_u *)curbuf->b_ffname;  // name for priority computation
   int use_tfu = 1;
+  char *tofree = NULL;
 
   // remember the matches for the last used tag
   static int num_matches = 0;
@@ -450,7 +451,10 @@ void do_tag(char *tag, int type, int count, int forceit, int verbose)
 
     // When desired match not found yet, try to find it (and others).
     if (use_tagstack) {
-      name = tagstack[tagstackidx].tagname;
+      // make a copy, the tagstack may change in 'tagfunc'
+      name = xstrdup(tagstack[tagstackidx].tagname);
+      xfree(tofree);
+      tofree = name;
     } else if (g_do_tagpreview != 0) {
       name = ptag_entry.tagname;
     } else {
@@ -681,6 +685,7 @@ end_do_tag:
   }
   postponed_split = 0;          // don't split next time
   g_do_tagpreview = 0;          // don't do tag preview next time
+  xfree(tofree);
 }
 
 // List all the matching tags.
diff --git a/src/nvim/testdir/test_tagfunc.vim b/src/nvim/testdir/test_tagfunc.vim
index e3085b9395..ad761f1b23 100644
--- a/src/nvim/testdir/test_tagfunc.vim
+++ b/src/nvim/testdir/test_tagfunc.vim
@@ -273,4 +273,16 @@ func Test_tagfunc_callback()
   %bw!
 endfunc
 
+func Test_tagfunc_wipes_buffer()
+  func g:Tag0unc0(t,f,o)
+   bwipe
+  endfunc
+  set tagfunc=g:Tag0unc0
+  new
+  cal assert_fails('tag 0', 'E987:')
+
+  delfunc g:Tag0unc0
+  set tagfunc=
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From 84881674fd702cad5b7572ac868f6d40965a2806 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 5 Nov 2022 17:01:39 +0800
Subject: vim-patch:9.0.0389: crash when 'tagfunc' closes the window

Problem:    Crash when 'tagfunc' closes the window.
Solution:   Bail out when the window was closed.

https://github.com/vim/vim/commit/ccfde4d028e891a41e3548323c3d47b06fb0b83e

Add docs for E1299 from Vim runtime.

Co-authored-by: Bram Moolenaar 
---
 src/nvim/tag.c                    | 11 +++++++++++
 src/nvim/testdir/test_tagfunc.vim | 13 +++++++++++++
 2 files changed, 24 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 5216919d98..16d6629c2e 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -106,6 +106,8 @@ static char_u *recurmsg
   = (char_u *)N_("E986: cannot modify the tag stack within tagfunc");
 static char_u *tfu_inv_ret_msg
   = (char_u *)N_("E987: invalid return value from tagfunc");
+static char e_window_unexpectedly_close_while_searching_for_tags[]
+  = N_("E1299: Window unexpectedly closed while searching for tags");
 
 static char *tagmatchname = NULL;   // name of last used tag
 
@@ -501,6 +503,15 @@ void do_tag(char *tag, int type, int count, int forceit, int verbose)
                                    // found: all matches found.
       }
 
+      // A tag function may do anything, which may cause various
+      // information to become invalid.  At least check for the tagstack
+      // to still be the same.
+      if (tagstack != curwin->w_tagstack) {
+        emsg(_(e_window_unexpectedly_close_while_searching_for_tags));
+        FreeWild(new_num_matches, new_matches);
+        break;
+      }
+
       // If there already were some matches for the same name, move them
       // to the start.  Avoids that the order changes when using
       // ":tnext" and jumping to another file.
diff --git a/src/nvim/testdir/test_tagfunc.vim b/src/nvim/testdir/test_tagfunc.vim
index ad761f1b23..8690e30e77 100644
--- a/src/nvim/testdir/test_tagfunc.vim
+++ b/src/nvim/testdir/test_tagfunc.vim
@@ -285,4 +285,17 @@ func Test_tagfunc_wipes_buffer()
   set tagfunc=
 endfunc
 
+func Test_tagfunc_closes_window()
+  split any
+  func MytagfuncClose(pat, flags, info)
+    close
+    return [{'name' : 'mytag', 'filename' : 'Xtest', 'cmd' : '1'}]
+  endfunc
+  set tagfunc=MytagfuncClose
+  call assert_fails('tag xyz', 'E1299:')
+
+  set tagfunc=
+endfunc
+
+
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From 8045296e8b6d10611a5fce2db0b9b90a1989100c Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 7 Nov 2022 09:08:25 +0800
Subject: fix(stdpath): default to /tmp if stdpath('run') cannot be created
 #20952

Fix #20949
---
 src/nvim/os/stdpaths.c | 3 +++
 1 file changed, 3 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c
index 31d85ac2eb..2aaf776fc6 100644
--- a/src/nvim/os/stdpaths.c
+++ b/src/nvim/os/stdpaths.c
@@ -87,6 +87,9 @@ char *stdpaths_get_xdg_var(const XDGVarType idx)
   } else if (idx == kXDGRuntimeDir) {
     // Special-case: stdpath('run') is defined at startup.
     ret = vim_gettempdir();
+    if (ret == NULL) {
+      ret = "/tmp/";
+    }
     size_t len = strlen(ret);
     ret = xstrndup(ret, len >= 2 ? len - 1 : 0);  // Trim trailing slash.
   }
-- 
cgit 


From 72f8613e97e3ab4e375a1b9dd20c847f7148acf2 Mon Sep 17 00:00:00 2001
From: notomo 
Date: Mon, 7 Nov 2022 10:20:27 +0900
Subject: fix(ui-ext): correct message kind in history before vim.ui_attach()

---
 src/nvim/message.c | 1 +
 1 file changed, 1 insertion(+)

(limited to 'src/nvim')

diff --git a/src/nvim/message.c b/src/nvim/message.c
index fa1c8036e6..e42fd42d46 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -3151,6 +3151,7 @@ int msg_end(void)
 void msg_ext_ui_flush(void)
 {
   if (!ui_has(kUIMessages)) {
+    msg_ext_kind = NULL;
     return;
   }
 
-- 
cgit 


From 4fd876271a8bc3349fccb14d2e0203af1f9e6698 Mon Sep 17 00:00:00 2001
From: Shougo 
Date: Mon, 7 Nov 2022 12:02:29 +0900
Subject: vim-patch:9.0.0756 #20680

Problem:    No autocmd event for changing text in a terminal window.
Solution:   Add TextChangedT. (Shougo Matsushita, closes vim/vim#11366)

https://github.com/vim/vim/commit/4ccaedfcd7526983f4b6b3b06b0bfb54f333f1f3
---
 src/nvim/auevents.lua |  1 +
 src/nvim/terminal.c   | 12 ++++++++++++
 2 files changed, 13 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index 93a870fe04..65c22c922a 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -108,6 +108,7 @@ return {
     'TextChanged',            -- text was modified
     'TextChangedI',           -- text was modified in Insert mode(no popup)
     'TextChangedP',           -- text was modified in Insert mode(popup)
+    'TextChangedT',           -- text was modified in Terminal mode
     'TextYankPost',           -- after a yank or delete was done (y, d, c)
     'UIEnter',                -- after UI attaches
     'UILeave',                -- after UI detaches
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 5e221e13df..890b04a614 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -523,6 +523,18 @@ static int terminal_check(VimState *state)
 
   if (must_redraw) {
     update_screen();
+
+    // Make sure an invoked autocmd doesn't delete the buffer (and the
+    // terminal) under our fingers.
+    curbuf->b_locked++;
+
+    // save and restore curwin and curbuf, in case the autocmd changes them
+    aco_save_T aco;
+    aucmd_prepbuf(&aco, curbuf);
+    apply_autocmds(EVENT_TEXTCHANGEDT, NULL, NULL, false, curbuf);
+    aucmd_restbuf(&aco);
+
+    curbuf->b_locked--;
   }
 
   if (need_maketitle) {  // Update title in terminal-mode. #7248
-- 
cgit 


From 9f125371e08f4bbb38b84f323608d01d35895a3a Mon Sep 17 00:00:00 2001
From: luukvbaal <31730729+luukvbaal@users.noreply.github.com>
Date: Mon, 7 Nov 2022 04:10:09 +0100
Subject: refactor: click definition functions #20923

Need this part of `win_redr_custom()` in `drawline.c` for #20621.
Another refactor is pending in https://github.com/vim/vim/pull/11467
---
 src/nvim/screen.c     | 43 +++++++++++++++++++++++++++++++++++++++++++
 src/nvim/statusline.c | 45 +++++----------------------------------------
 2 files changed, 48 insertions(+), 40 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index cd1d6553cb..377927ba4d 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -598,6 +598,49 @@ void stl_clear_click_defs(StlClickDefinition *const click_defs, const long click
   }
 }
 
+/// Allocate or resize the click definitions array if needed.
+StlClickDefinition *stl_alloc_click_defs(StlClickDefinition *cdp, long width, size_t *size)
+{
+  if (*size < (size_t)width) {
+    xfree(cdp);
+    *size = (size_t)width;
+    cdp = xcalloc(*size, sizeof(StlClickDefinition));
+  }
+  return cdp;
+}
+
+/// Fill the click definitions array if needed.
+void stl_fill_click_defs(StlClickDefinition *click_defs, StlClickRecord *click_recs, char *buf,
+                         int width, bool tabline)
+{
+  if (click_defs == NULL) {
+    return;
+  }
+
+  int col = 0;
+  int len = 0;
+
+  StlClickDefinition cur_click_def = {
+    .type = kStlClickDisabled,
+  };
+  for (int i = 0; click_recs[i].start != NULL; i++) {
+    len += vim_strnsize(buf, (int)(click_recs[i].start - buf));
+    while (col < len) {
+      click_defs[col++] = cur_click_def;
+    }
+    buf = (char *)click_recs[i].start;
+    cur_click_def = click_recs[i].def;
+    if (!tabline && !(cur_click_def.type == kStlClickDisabled
+                      || cur_click_def.type == kStlClickFuncRun)) {
+      // window bar and status line only support click functions
+      cur_click_def.type = kStlClickDisabled;
+    }
+  }
+  while (col < width) {
+    click_defs[col++] = cur_click_def;
+  }
+}
+
 /// Set cursor to its position in the current window.
 void setcursor(void)
 {
diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c
index fb49a1b6a7..71f9bde2e9 100644
--- a/src/nvim/statusline.c
+++ b/src/nvim/statusline.c
@@ -470,26 +470,16 @@ void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
     attr = (wp == curwin) ? win_hl_attr(wp, HLF_WBR) : win_hl_attr(wp, HLF_WBRNC);
     maxwidth = wp->w_width_inner;
     use_sandbox = was_set_insecurely(wp, "winbar", 0);
-
     stl_clear_click_defs(wp->w_winbar_click_defs, (long)wp->w_winbar_click_defs_size);
-    // Allocate / resize the click definitions array for winbar if needed.
-    if (wp->w_winbar_height && wp->w_winbar_click_defs_size < (size_t)maxwidth) {
-      xfree(wp->w_winbar_click_defs);
-      wp->w_winbar_click_defs_size = (size_t)maxwidth;
-      wp->w_winbar_click_defs = xcalloc(wp->w_winbar_click_defs_size, sizeof(StlClickRecord));
-    }
+    wp->w_winbar_click_defs = stl_alloc_click_defs(wp->w_winbar_click_defs, maxwidth,
+                                                   &wp->w_winbar_click_defs_size);
   } else {
     row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp);
     fillchar = fillchar_status(&attr, wp);
     maxwidth = is_stl_global ? Columns : wp->w_width;
-
     stl_clear_click_defs(wp->w_status_click_defs, (long)wp->w_status_click_defs_size);
-    // Allocate / resize the click definitions array for statusline if needed.
-    if (wp->w_status_click_defs_size < (size_t)maxwidth) {
-      xfree(wp->w_status_click_defs);
-      wp->w_status_click_defs_size = (size_t)maxwidth;
-      wp->w_status_click_defs = xcalloc(wp->w_status_click_defs_size, sizeof(StlClickRecord));
-    }
+    wp->w_status_click_defs = stl_alloc_click_defs(wp->w_status_click_defs, maxwidth,
+                                                   &wp->w_status_click_defs_size);
 
     if (draw_ruler) {
       stl = p_ruf;
@@ -597,32 +587,7 @@ void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
                                                 : draw_winbar ? wp->w_winbar_click_defs
                                                               : wp->w_status_click_defs;
 
-  if (click_defs == NULL) {
-    goto theend;
-  }
-
-  col = 0;
-  len = 0;
-  p = buf;
-  StlClickDefinition cur_click_def = {
-    .type = kStlClickDisabled,
-  };
-  for (n = 0; tabtab[n].start != NULL; n++) {
-    len += vim_strnsize(p, (int)(tabtab[n].start - p));
-    while (col < len) {
-      click_defs[col++] = cur_click_def;
-    }
-    p = (char *)tabtab[n].start;
-    cur_click_def = tabtab[n].def;
-    if ((wp != NULL) && !(cur_click_def.type == kStlClickDisabled
-                          || cur_click_def.type == kStlClickFuncRun)) {
-      // window bar and status line only support click functions
-      cur_click_def.type = kStlClickDisabled;
-    }
-  }
-  while (col < maxwidth) {
-    click_defs[col++] = cur_click_def;
-  }
+  stl_fill_click_defs(click_defs, tabtab, buf, maxwidth, wp == NULL);
 
 theend:
   entered = false;
-- 
cgit 


From d337814906b1377e34aa2c2dfd8aa16285328692 Mon Sep 17 00:00:00 2001
From: Victor Blanchard <48864055+Viblanc@users.noreply.github.com>
Date: Mon, 7 Nov 2022 04:31:50 +0100
Subject: feat: ":write ++p" creates parent dirs #20835
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- `:write ++p foo/bar/baz.txt` should create parent directories `foo/bar/` if
   they do not exist
    - Note: `:foo ++…` is usually for options. No existing options have
      a single-char abbreviation (presumably by design), so it's safe to
      special-case `++p` here.
- Same for `writefile(…, 'foo/bar/baz.txt', 'p')`
- `BufWriteCmd` can see the ++p flag via `v:cmdarg`.

closes #19884
---
 src/nvim/eval.c         |  8 ++++++++
 src/nvim/eval/funcs.c   |  4 ++++
 src/nvim/ex_cmds.c      |  7 +++++++
 src/nvim/ex_cmds_defs.h |  1 +
 src/nvim/ex_docmd.c     |  7 +++++++
 src/nvim/os/fileio.c    |  8 ++++++++
 src/nvim/os/fileio.h    |  1 +
 src/nvim/os/fs.c        | 31 +++++++++++++++++++++++++++++++
 8 files changed, 67 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index c578d9fd39..0848326d90 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -7067,6 +7067,9 @@ char *set_cmdarg(exarg_T *eap, char *oldarg)
   if (eap->bad_char != 0) {
     len += 7 + 4;  // " ++bad=" + "keep" or "drop"
   }
+  if (eap->mkdir_p != 0) {
+    len += 4;
+  }
 
   const size_t newval_len = len + 1;
   char *newval = xmalloc(newval_len);
@@ -7100,6 +7103,11 @@ char *set_cmdarg(exarg_T *eap, char *oldarg)
     snprintf(newval + strlen(newval), newval_len, " ++bad=%c",
              eap->bad_char);
   }
+
+  if (eap->mkdir_p) {
+    snprintf(newval, newval_len, " ++p");
+  }
+
   vimvars[VV_CMDARG].vv_str = newval;
   return oldval;
 }
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 1492a2d30d..5fcbc40623 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -9970,6 +9970,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   bool binary = false;
   bool append = false;
   bool do_fsync = !!p_fs;
+  bool mkdir_p = false;
   if (argvars[2].v_type != VAR_UNKNOWN) {
     const char *const flags = tv_get_string_chk(&argvars[2]);
     if (flags == NULL) {
@@ -9985,6 +9986,8 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
         do_fsync = true; break;
       case 'S':
         do_fsync = false; break;
+      case 'p':
+        mkdir_p = true; break;
       default:
         // Using %s, p and not %c, *p to preserve multibyte characters
         semsg(_("E5060: Unknown flag: %s"), p);
@@ -10004,6 +10007,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
     emsg(_("E482: Can't open file with an empty name"));
   } else if ((error = file_open(&fp, fname,
                                 ((append ? kFileAppend : kFileTruncate)
+                                 | (mkdir_p ? kFileMkDir : kFileCreate)
                                  | kFileCreate), 0666)) != 0) {
     semsg(_("E482: Can't open file %s for writing: %s"),
           fname, os_strerror(error));
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index d3a4b6c282..925ff3b8ea 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -1922,6 +1922,13 @@ int do_write(exarg_T *eap)
       fname = curbuf->b_sfname;
     }
 
+    if (eap->mkdir_p) {
+      if (os_file_mkdir(fname, 0755) < 0) {
+        retval = FAIL;
+        goto theend;
+      }
+    }
+
     name_was_missing = curbuf->b_ffname == NULL;
     retval = buf_write(curbuf, ffname, fname, eap->line1, eap->line2,
                        eap, eap->append, eap->forceit, true, false);
diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index 0015a82880..378271c107 100644
--- a/src/nvim/ex_cmds_defs.h
+++ b/src/nvim/ex_cmds_defs.h
@@ -202,6 +202,7 @@ struct exarg {
   int regname;                  ///< register name (NUL if none)
   int force_bin;                ///< 0, FORCE_BIN or FORCE_NOBIN
   int read_edit;                ///< ++edit argument
+  int mkdir_p;                  ///< ++p argument
   int force_ff;                 ///< ++ff= argument (first char of argument)
   int force_enc;                ///< ++enc= argument (index in cmd[])
   int bad_char;                 ///< BAD_KEEP, BAD_DROP or replacement byte
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index dc2b7247f1..5bb7cb2da2 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -4074,6 +4074,13 @@ static int getargopt(exarg_T *eap)
     return OK;
   }
 
+  // ":write ++p foo/bar/file
+  if (strncmp(arg, "p", 1) == 0) {
+    eap->mkdir_p = true;
+    eap->arg = skipwhite(arg + 1);
+    return OK;
+  }
+
   if (STRNCMP(arg, "ff", 2) == 0) {
     arg += 2;
     pp = &eap->force_ff;
diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c
index b1710737d0..280a9c2bee 100644
--- a/src/nvim/os/fileio.c
+++ b/src/nvim/os/fileio.c
@@ -71,6 +71,7 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname, const int f
   FLAG(flags, kFileReadOnly, O_RDONLY, kFalse, wr != kTrue);
 #ifdef O_NOFOLLOW
   FLAG(flags, kFileNoSymlink, O_NOFOLLOW, kNone, true);
+  FLAG(flags, kFileMkDir, O_CREAT|O_WRONLY, kTrue, !(flags & kFileCreateOnly));
 #endif
 #undef FLAG
   // wr is used for kFileReadOnly flag, but on
@@ -78,6 +79,13 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname, const int f
   // `error: variable ‘wr’ set but not used [-Werror=unused-but-set-variable]`
   (void)wr;
 
+  if (flags & kFileMkDir) {
+    int mkdir_ret = os_file_mkdir((char *)fname, 0755);
+    if (mkdir_ret < 0) {
+      return mkdir_ret;
+    }
+  }
+
   const int fd = os_open(fname, os_open_flags, mode);
 
   if (fd < 0) {
diff --git a/src/nvim/os/fileio.h b/src/nvim/os/fileio.h
index da23a54c4e..5e47bbf921 100644
--- a/src/nvim/os/fileio.h
+++ b/src/nvim/os/fileio.h
@@ -35,6 +35,7 @@ typedef enum {
                      ///< be used with kFileCreateOnly.
   kFileNonBlocking = 128,  ///< Do not restart read() or write() syscall if
                            ///< EAGAIN was encountered.
+  kFileMkDir = 256,
 } FileOpenFlags;
 
 static inline bool file_eof(const FileDescriptor *fp)
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 68e96eea6e..3c9578979e 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -946,6 +946,37 @@ int os_mkdir_recurse(const char *const dir, int32_t mode, char **const failed_di
   return 0;
 }
 
+/// Create the parent directory of a file if it does not exist
+///
+/// @param[in] fname Full path of the file name whose parent directories
+///                  we want to create
+/// @param[in] mode  Permissions for the newly-created directory.
+///
+/// @return `0` for success, libuv error code for failure.
+int os_file_mkdir(char *fname, int32_t mode)
+  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+  if (!dir_of_file_exists((char_u *)fname)) {
+    char *tail = path_tail_with_sep(fname);
+    char *last_char = tail + strlen(tail) - 1;
+    if (vim_ispathsep(*last_char)) {
+      emsg(_(e_noname));
+      return -1;
+    }
+    char c = *tail;
+    *tail = NUL;
+    int r;
+    char *failed_dir;
+    if ((r = os_mkdir_recurse(fname, mode, &failed_dir) < 0)) {
+      semsg(_(e_mkdir), failed_dir, os_strerror(r));
+      xfree(failed_dir);
+    }
+    *tail = c;
+    return r;
+  }
+  return 0;
+}
+
 /// Create a unique temporary directory.
 ///
 /// @param[in] template Template of the path to the directory with XXXXXX
-- 
cgit 


From f91d200c056940e92277e59ffd6507c4db1973d8 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 7 Nov 2022 10:24:55 +0800
Subject: vim-patch:8.2.3735: cannot use a lambda for 'imactivatefunc'

Problem:    Cannot use a lambda for 'imactivatefunc'.
Solution:   Add lambda support for 'imactivatefunc' and 'imstatusfunc'.
            (Yegappan Lakshmanan, closes vim/vim#9275)

https://github.com/vim/vim/commit/7645da568c5e3b4ee339a2e99c3b3af790619787

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/testdir/test_ins_complete.vim | 25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim
index fc612dcbe2..0478ad1516 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -1340,7 +1340,7 @@ func Test_completefunc_callback()
     call add(g:MycompleteFunc3_args, [a:findstart, a:base])
     return a:findstart ? 0 : []
   endfunc
-  set completefunc={a,\ b,\ ->\ MycompleteFunc3(a,\ b,)}
+  set completefunc={a,\ b\ ->\ MycompleteFunc3(a,\ b)}
   new | only
   call setline(1, 'five')
   let g:MycompleteFunc3_args = []
@@ -1403,26 +1403,29 @@ func Test_completefunc_callback()
     bw!
 
     # Test for using a lambda
-    def MycompleteFunc2(findstart: number, base: string): any
-      add(g:MycompleteFunc2_args, [findstart, base])
+    def LambdaComplete1(findstart: number, base: string): any
+      add(g:LambdaComplete1_args, [findstart, base])
       return findstart ? 0 : []
     enddef
-    &completefunc = '(a, b) => MycompleteFunc2(a, b)'
+    &completefunc = '(a, b) => LambdaComplete1(a, b)'
     new | only
     setline(1, 'two')
-    g:MycompleteFunc2_args = []
+    g:LambdaComplete1_args = []
     feedkeys("A\\\", 'x')
-    assert_equal([[1, ''], [0, 'two']], g:MycompleteFunc2_args)
+    assert_equal([[1, ''], [0, 'two']], g:LambdaComplete1_args)
     bw!
 
     # Test for using a variable with a lambda expression
-    var Fn: func = (a, b) => MycompleteFunc2(a, b)
+    var Fn: func = (findstart, base) => {
+            add(g:LambdaComplete2_args, [findstart, base])
+            return findstart ? 0 : []
+        }
     &completefunc = string(Fn)
     new | only
     setline(1, 'three')
-    g:MycompleteFunc2_args = []
+    g:LambdaComplete2_args = []
     feedkeys("A\\\", 'x')
-    assert_equal([[1, ''], [0, 'three']], g:MycompleteFunc2_args)
+    assert_equal([[1, ''], [0, 'three']], g:LambdaComplete2_args)
     bw!
   END
   " call CheckScriptSuccess(lines)
@@ -1497,7 +1500,7 @@ func Test_omnifunc_callback()
     call add(g:MyomniFunc3_args, [a:findstart, a:base])
     return a:findstart ? 0 : []
   endfunc
-  set omnifunc={a,\ b,\ ->\ MyomniFunc3(a,\ b,)}
+  set omnifunc={a,\ b\ ->\ MyomniFunc3(a,\ b)}
   new | only
   call setline(1, 'five')
   let g:MyomniFunc3_args = []
@@ -1654,7 +1657,7 @@ func Test_thesaurusfunc_callback()
     call add(g:MytsrFunc3_args, [a:findstart, a:base])
     return a:findstart ? 0 : []
   endfunc
-  set thesaurusfunc={a,\ b,\ ->\ MytsrFunc3(a,\ b,)}
+  set thesaurusfunc={a,\ b\ ->\ MytsrFunc3(a,\ b)}
   new | only
   call setline(1, 'five')
   let g:MytsrFunc3_args = []
-- 
cgit 


From 42e44d6d334bda8b97afe9e34a819ab293e5e10a Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 7 Nov 2022 10:26:54 +0800
Subject: vim-patch:8.2.3751: cannot assign a lambda to an option that takes a
 function

Problem:    Cannot assign a lambda to an option that takes a function.
Solution:   Automatically convert the lambda to a string. (Yegappan
            Lakshmanan, closes vim/vim#9286)

https://github.com/vim/vim/commit/6409553b6e3b4de4e1d72b8ee5445595214581ff

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/api/options.c                 |   2 +-
 src/nvim/context.c                     |   2 +-
 src/nvim/eval.c                        |  14 +--
 src/nvim/eval/vars.c                   |  23 ++--
 src/nvim/generators/gen_options.lua    |   5 +-
 src/nvim/option.c                      |  25 +++--
 src/nvim/option_defs.h                 |   5 +-
 src/nvim/options.lua                   |   9 ++
 src/nvim/spell.c                       |   2 +-
 src/nvim/testdir/test_ins_complete.vim | 192 ++++++++++++++++++++++++++++++---
 src/nvim/testdir/test_tagfunc.vim      |  56 +++++++++-
 11 files changed, 287 insertions(+), 48 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c
index ec1f19cf6a..92f20fff48 100644
--- a/src/nvim/api/options.c
+++ b/src/nvim/api/options.c
@@ -487,7 +487,7 @@ static getoption_T access_option_value(char *key, long *numval, char **stringval
                                        bool get, Error *err)
 {
   if (get) {
-    return get_option_value(key, numval, stringval, opt_flags);
+    return get_option_value(key, numval, stringval, NULL, opt_flags);
   } else {
     char *errmsg;
     if ((errmsg = set_option_value(key, *numval, *stringval, opt_flags))) {
diff --git a/src/nvim/context.c b/src/nvim/context.c
index 34692cdf64..c765b95733 100644
--- a/src/nvim/context.c
+++ b/src/nvim/context.c
@@ -131,7 +131,7 @@ bool ctx_restore(Context *ctx, const int flags)
   }
 
   char *op_shada;
-  get_option_value("shada", NULL, &op_shada, OPT_GLOBAL);
+  get_option_value("shada", NULL, &op_shada, NULL, OPT_GLOBAL);
   set_option_value("shada", 0L, "!,'100,%", OPT_GLOBAL);
 
   if (flags & kCtxRegs) {
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 0848326d90..a4fadcd7bc 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -3689,10 +3689,10 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
 int get_option_tv(const char **const arg, typval_T *const rettv, const bool evaluate)
   FUNC_ATTR_NONNULL_ARG(1)
 {
-  int opt_flags;
+  int scope;
 
   // Isolate the option name and find its value.
-  char *option_end = (char *)find_option_end(arg, &opt_flags);
+  char *option_end = (char *)find_option_end(arg, &scope);
   if (option_end == NULL) {
     if (rettv != NULL) {
       semsg(_("E112: Option name missing: %s"), *arg);
@@ -3712,7 +3712,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval
   char c = *option_end;
   *option_end = NUL;
   getoption_T opt_type = get_option_value(*arg, &numval,
-                                          rettv == NULL ? NULL : &stringval, opt_flags);
+                                          rettv == NULL ? NULL : &stringval, NULL, scope);
 
   if (opt_type == gov_unknown) {
     if (rettv != NULL) {
@@ -7794,19 +7794,19 @@ void ex_execute(exarg_T *eap)
 ///
 /// @return  NULL when no option name found.  Otherwise pointer to the char
 ///          after the option name.
-const char *find_option_end(const char **const arg, int *const opt_flags)
+const char *find_option_end(const char **const arg, int *const scope)
 {
   const char *p = *arg;
 
   p++;
   if (*p == 'g' && p[1] == ':') {
-    *opt_flags = OPT_GLOBAL;
+    *scope = OPT_GLOBAL;
     p += 2;
   } else if (*p == 'l' && p[1] == ':') {
-    *opt_flags = OPT_LOCAL;
+    *scope = OPT_LOCAL;
     p += 2;
   } else {
-    *opt_flags = 0;
+    *scope = 0;
   }
 
   if (!ASCII_ISALPHA(*p)) {
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index 139e7ed66b..6c4d33df89 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -560,8 +560,6 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
 {
   char *arg_end = NULL;
   int len;
-  int opt_flags;
-  char *tofree = NULL;
 
   // ":let $VAR = expr": Set environment variable.
   if (*arg == '$') {
@@ -582,12 +580,12 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
                  && vim_strchr(endchars, *skipwhite(arg)) == NULL) {
         emsg(_(e_letunexp));
       } else if (!check_secure()) {
+        char *tofree = NULL;
         const char c1 = name[len];
         name[len] = NUL;
         const char *p = tv_get_string_chk(tv);
         if (p != NULL && op != NULL && *op == '.') {
           char *s = vim_getenv(name);
-
           if (s != NULL) {
             tofree = concat_str(s, p);
             p = (const char *)tofree;
@@ -611,7 +609,8 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
       return NULL;
     }
     // Find the end of the name.
-    char *const p = (char *)find_option_end((const char **)&arg, &opt_flags);
+    int scope;
+    char *const p = (char *)find_option_end((const char **)&arg, &scope);
     if (p == NULL
         || (endchars != NULL
             && vim_strchr(endchars, *skipwhite(p)) == NULL)) {
@@ -623,11 +622,13 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
       char *stringval = NULL;
       const char *s = NULL;
       bool failed = false;
+      uint32_t opt_p_flags;
+      char *tofree = NULL;
 
       const char c1 = *p;
       *p = NUL;
 
-      opt_type = get_option_value(arg, &numval, &stringval, opt_flags);
+      opt_type = get_option_value(arg, &numval, &stringval, &opt_p_flags, scope);
       if (opt_type == gov_bool
           || opt_type == gov_number
           || opt_type == gov_hidden_bool
@@ -636,8 +637,13 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
         n = (long)tv_get_number(tv);
       }
 
-      // Avoid setting a string option to the text "v:false" or similar.
-      if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) {
+      if ((opt_p_flags & P_FUNC) && tv_is_func(*tv)) {
+        // If the option can be set to a function reference or a lambda
+        // and the passed value is a function reference, then convert it to
+        // the name (string) of the function reference.
+        s = tofree = encode_tv2string(tv, NULL);
+      } else if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) {
+        // Avoid setting a string option to the text "v:false" or similar.
         s = tv_get_string_chk(tv);
       }
 
@@ -674,7 +680,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
 
       if (!failed) {
         if (opt_type != gov_string || s != NULL) {
-          char *err = set_option_value(arg, n, s, opt_flags);
+          char *err = set_option_value(arg, n, s, scope);
           arg_end = p;
           if (err != NULL) {
             emsg(_(err));
@@ -685,6 +691,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
       }
       *p = c1;
       xfree(stringval);
+      xfree(tofree);
     }
     // ":let @r = expr": Set register contents.
   } else if (*arg == '@') {
diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua
index 4e4dd83367..af21d44eaf 100644
--- a/src/nvim/generators/gen_options.lua
+++ b/src/nvim/generators/gen_options.lua
@@ -29,14 +29,14 @@ local type_flags={
 }
 
 local redraw_flags={
+  ui_option='P_UI_OPTION',
+  tabline='P_RTABL',
   statuslines='P_RSTAT',
-  tabline = 'P_RTABL',
   current_window='P_RWIN',
   current_window_only='P_RWINONLY',
   current_buffer='P_RBUF',
   all_windows='P_RALL',
   curswant='P_CURSWANT',
-  ui_option='P_UI_OPTION',
 }
 
 local list_flags={
@@ -78,6 +78,7 @@ local get_flags = function(o)
     {'deny_in_modelines', 'P_NO_ML'},
     {'deny_duplicates', 'P_NODUP'},
     {'modelineexpr', 'P_MLE'},
+    {'func'}
   }) do
     local key_name = flag_desc[1]
     local def_name = flag_desc[2] or ('P_' .. key_name:upper())
diff --git a/src/nvim/option.c b/src/nvim/option.c
index da3fad0d61..c0a1f14463 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -2842,6 +2842,7 @@ int findoption(const char *const arg)
 /// Gets the value for an option.
 ///
 /// @param stringval  NULL when only checking existence
+/// @param flagsp     set to the option flags (P_xxxx) (if not NULL)
 ///
 /// @returns:
 ///           Number option: gov_number, *numval gets value.
@@ -2851,7 +2852,8 @@ int findoption(const char *const arg)
 ///           Hidden Toggle option: gov_hidden_bool.
 ///           Hidden String option: gov_hidden_string.
 ///           Unknown option: gov_unknown.
-getoption_T get_option_value(const char *name, long *numval, char **stringval, int opt_flags)
+getoption_T get_option_value(const char *name, long *numval, char **stringval, uint32_t *flagsp,
+                             int scope)
 {
   if (get_tty_option(name, stringval)) {
     return gov_string;
@@ -2862,7 +2864,12 @@ getoption_T get_option_value(const char *name, long *numval, char **stringval, i
     return gov_unknown;
   }
 
-  char_u *varp = (char_u *)get_varp_scope(&(options[opt_idx]), opt_flags);
+  char_u *varp = (char_u *)get_varp_scope(&(options[opt_idx]), scope);
+
+  if (flagsp != NULL) {
+    // Return the P_xxxx option flags.
+    *flagsp = options[opt_idx].flags;
+  }
 
   if (options[opt_idx].flags & P_STRING) {
     if (varp == NULL) {  // hidden option
@@ -3092,7 +3099,7 @@ char *set_option_value(const char *const name, const long number, const char *co
           numval = -1;
         } else {
           char *s = NULL;
-          (void)get_option_value(name, &numval, &s, OPT_GLOBAL);
+          (void)get_option_value(name, &numval, &s, NULL, OPT_GLOBAL);
         }
       }
       if (flags & P_NUM) {
@@ -3701,15 +3708,17 @@ void unset_global_local_option(char *name, void *from)
 }
 
 /// Get pointer to option variable, depending on local or global scope.
-char *get_varp_scope(vimoption_T *p, int opt_flags)
+///
+/// @param scope  can be OPT_LOCAL, OPT_GLOBAL or a combination.
+char *get_varp_scope(vimoption_T *p, int scope)
 {
-  if ((opt_flags & OPT_GLOBAL) && p->indir != PV_NONE) {
+  if ((scope & OPT_GLOBAL) && p->indir != PV_NONE) {
     if (p->var == VAR_WIN) {
       return GLOBAL_WO(get_varp(p));
     }
     return (char *)p->var;
   }
-  if ((opt_flags & OPT_LOCAL) && ((int)p->indir & PV_BOTH)) {
+  if ((scope & OPT_LOCAL) && ((int)p->indir & PV_BOTH)) {
     switch ((int)p->indir) {
     case PV_FP:
       return (char *)&(curbuf->b_p_fp);
@@ -4863,9 +4872,9 @@ void ExpandOldSetting(int *num_file, char ***file)
 /// NameBuff[].  Must not be called with a hidden option!
 ///
 /// @param opt_flags  OPT_GLOBAL and/or OPT_LOCAL
-static void option_value2string(vimoption_T *opp, int opt_flags)
+static void option_value2string(vimoption_T *opp, int scope)
 {
-  char_u *varp = (char_u *)get_varp_scope(opp, opt_flags);
+  char_u *varp = (char_u *)get_varp_scope(opp, scope);
 
   if (opp->flags & P_NUM) {
     long wc = 0;
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 7c33773e41..d505e75be4 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -24,6 +24,7 @@
 #define P_NO_MKRC      0x200U       ///< don't include in :mkvimrc output
 
 // when option changed, what to display:
+#define P_UI_OPTION    0x400U       ///< send option to remote UI
 #define P_RTABL        0x800U       ///< redraw tabline
 #define P_RSTAT        0x1000U      ///< redraw status lines
 #define P_RWIN         0x2000U      ///< redraw current window and recompute text
@@ -50,9 +51,9 @@
 #define P_NDNAME       0x8000000U   ///< only normal dir name chars allowed
 #define P_RWINONLY     0x10000000U  ///< only redraw current window
 #define P_MLE          0x20000000U  ///< under control of 'modelineexpr'
+#define P_FUNC         0x40000000U  ///< accept a function reference or a lambda
 
-#define P_NO_DEF_EXP   0x40000000U  ///< Do not expand default value.
-#define P_UI_OPTION    0x80000000U  ///< send option to remote ui
+#define P_NO_DEF_EXP   0x80000000U  ///< Do not expand default value.
 
 /// Flags for option-setting functions
 ///
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 3a59becb33..e938760e67 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -10,6 +10,7 @@
 --    secure=nil, gettext=nil, noglob=nil, normal_fname_chars=nil,
 --    pri_mkrc=nil, deny_in_modelines=nil, normal_dname_chars=nil,
 --    modelineexpr=nil,
+--    func=nil,
 --    expand=nil, nodefault=nil, no_mkrc=nil,
 --    alloced=nil,
 --    save_pv_indir=nil,
@@ -455,6 +456,7 @@ return {
       type='string', scope={'buffer'},
       secure=true,
       alloced=true,
+      func=true,
       varname='p_cfu',
       defaults={if_true=""}
     },
@@ -1638,6 +1640,7 @@ return {
       type='string', scope={'buffer'},
       secure=true,
       alloced=true,
+      func=true,
       varname='p_ofu',
       defaults={if_true=""}
     },
@@ -1653,6 +1656,7 @@ return {
       short_desc=N_("function to be called for |g@| operator"),
       type='string', scope={'global'},
       secure=true,
+      func=true,
       varname='p_opfunc',
       defaults={if_true=""}
     },
@@ -1835,6 +1839,8 @@ return {
       full_name='quickfixtextfunc', abbreviation='qftf',
       short_desc=N_("customize the quickfix window"),
       type='string', scope={'global'},
+      secure=true,
+      func=true,
       varname='p_qftf',
       defaults={if_true=""}
     },
@@ -2408,6 +2414,8 @@ return {
       full_name='tagfunc', abbreviation='tfu',
       short_desc=N_("function used to perform tag searches"),
       type='string', scope={'buffer'},
+      secure=true,
+      func=true,
       varname='p_tfu',
       defaults={if_true=""}
     },
@@ -2538,6 +2546,7 @@ return {
       type='string', scope={'global', 'buffer'},
       secure=true,
       alloced=true,
+      func=true,
       varname='p_tsrfu',
       defaults={if_true=""}
     },
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index e76ac49b5d..1b78aa4d72 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -3145,7 +3145,7 @@ void ex_spelldump(exarg_T *eap)
   }
   char *spl;
   long dummy;
-  (void)get_option_value("spl", &dummy, &spl, OPT_LOCAL);
+  (void)get_option_value("spl", &dummy, &spl, NULL, OPT_LOCAL);
 
   // Create a new empty buffer in a new window.
   do_cmdline_cmd("new");
diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim
index 0478ad1516..8b5f9a189f 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -1302,13 +1302,22 @@ func Test_completefunc_callback()
 
   " Using a funcref variable to set 'completefunc'
   let Fn = function('MycompleteFunc1')
+  let &completefunc = Fn
+  new | only
+  call setline(1, 'two')
+  let g:MycompleteFunc1_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'two']], g:MycompleteFunc1_args)
+  bw!
+
+  " Using string(funcref_variable) to set 'completefunc'
+  let Fn = function('MycompleteFunc1')
   let &completefunc = string(Fn)
   new | only
   call setline(1, 'two')
   let g:MycompleteFunc1_args = []
   call feedkeys("A\\\", 'x')
   call assert_equal([[1, ''], [0, 'two']], g:MycompleteFunc1_args)
-  call assert_fails('let &completefunc = Fn', 'E729:')
   bw!
 
   " Test for using a funcref()
@@ -1326,13 +1335,22 @@ func Test_completefunc_callback()
 
   " Using a funcref variable to set 'completefunc'
   let Fn = funcref('MycompleteFunc2')
+  let &completefunc = Fn
+  new | only
+  call setline(1, 'four')
+  let g:MycompleteFunc2_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'four']], g:MycompleteFunc2_args)
+  bw!
+
+  " Using a string(funcref_variable) to set 'completefunc'
+  let Fn = funcref('MycompleteFunc2')
   let &completefunc = string(Fn)
   new | only
   call setline(1, 'four')
   let g:MycompleteFunc2_args = []
   call feedkeys("A\\\", 'x')
   call assert_equal([[1, ''], [0, 'four']], g:MycompleteFunc2_args)
-  call assert_fails('let &completefunc = Fn', 'E729:')
   bw!
 
   " Test for using a lambda function
@@ -1349,6 +1367,15 @@ func Test_completefunc_callback()
   bw!
 
   " Set 'completefunc' to a lambda expression
+  let &completefunc = {a, b -> MycompleteFunc3(a, b)}
+  new | only
+  call setline(1, 'six')
+  let g:MycompleteFunc3_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'six']], g:MycompleteFunc3_args)
+  bw!
+
+  " Set 'completefunc' to string(lambda_expression)
   let &completefunc = '{a, b -> MycompleteFunc3(a, b)}'
   new | only
   call setline(1, 'six')
@@ -1359,18 +1386,27 @@ func Test_completefunc_callback()
 
   " Set 'completefunc' to a variable with a lambda expression
   let Lambda = {a, b -> MycompleteFunc3(a, b)}
+  let &completefunc = Lambda
+  new | only
+  call setline(1, 'seven')
+  let g:MycompleteFunc3_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'seven']], g:MycompleteFunc3_args)
+  bw!
+
+  " Set 'completefunc' to a string(variable with a lambda expression)
+  let Lambda = {a, b -> MycompleteFunc3(a, b)}
   let &completefunc = string(Lambda)
   new | only
   call setline(1, 'seven')
   let g:MycompleteFunc3_args = []
   call feedkeys("A\\\", 'x')
   call assert_equal([[1, ''], [0, 'seven']], g:MycompleteFunc3_args)
-  call assert_fails('let &completefunc = Lambda', 'E729:')
   bw!
 
   " Test for using a lambda function with incorrect return value
   let Lambda = {s -> strlen(s)}
-  let &completefunc = string(Lambda)
+  let &completefunc = Lambda
   new | only
   call setline(1, 'eight')
   call feedkeys("A\\\", 'x')
@@ -1382,7 +1418,7 @@ func Test_completefunc_callback()
 
   call assert_fails("set completefunc=function('abc')", "E700:")
   call assert_fails("set completefunc=funcref('abc')", "E700:")
-  let &completefunc = "{a -> 'abc'}"
+  let &completefunc = {a -> 'abc'}
   call feedkeys("A\\\", 'x')
 
   " Vim9 tests
@@ -1407,6 +1443,15 @@ func Test_completefunc_callback()
       add(g:LambdaComplete1_args, [findstart, base])
       return findstart ? 0 : []
     enddef
+    &completefunc = (a, b) => LambdaComplete1(a, b)
+    new | only
+    setline(1, 'two')
+    g:LambdaComplete1_args = []
+    feedkeys("A\\\", 'x')
+    assert_equal([[1, ''], [0, 'two']], g:LambdaComplete1_args)
+    bw!
+
+    # Test for using a string(lambda)
     &completefunc = '(a, b) => LambdaComplete1(a, b)'
     new | only
     setline(1, 'two')
@@ -1420,6 +1465,15 @@ func Test_completefunc_callback()
             add(g:LambdaComplete2_args, [findstart, base])
             return findstart ? 0 : []
         }
+    &completefunc = Fn
+    new | only
+    setline(1, 'three')
+    g:LambdaComplete2_args = []
+    feedkeys("A\\\", 'x')
+    assert_equal([[1, ''], [0, 'three']], g:LambdaComplete2_args)
+    bw!
+
+    # Test for using a string(variable with a lambda expression)
     &completefunc = string(Fn)
     new | only
     setline(1, 'three')
@@ -1462,13 +1516,22 @@ func Test_omnifunc_callback()
 
   " Using a funcref variable to set 'omnifunc'
   let Fn = function('MyomniFunc1')
+  let &omnifunc = Fn
+  new | only
+  call setline(1, 'two')
+  let g:MyomniFunc1_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'two']], g:MyomniFunc1_args)
+  bw!
+
+  " Using a string(funcref_variable) to set 'omnifunc'
+  let Fn = function('MyomniFunc1')
   let &omnifunc = string(Fn)
   new | only
   call setline(1, 'two')
   let g:MyomniFunc1_args = []
   call feedkeys("A\\\", 'x')
   call assert_equal([[1, ''], [0, 'two']], g:MyomniFunc1_args)
-  call assert_fails('let &omnifunc = Fn', 'E729:')
   bw!
 
   " Test for using a funcref()
@@ -1486,13 +1549,22 @@ func Test_omnifunc_callback()
 
   " Using a funcref variable to set 'omnifunc'
   let Fn = funcref('MyomniFunc2')
+  let &omnifunc = Fn
+  new | only
+  call setline(1, 'four')
+  let g:MyomniFunc2_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'four']], g:MyomniFunc2_args)
+  bw!
+
+  " Using a string(funcref_variable) to set 'omnifunc'
+  let Fn = funcref('MyomniFunc2')
   let &omnifunc = string(Fn)
   new | only
   call setline(1, 'four')
   let g:MyomniFunc2_args = []
   call feedkeys("A\\\", 'x')
   call assert_equal([[1, ''], [0, 'four']], g:MyomniFunc2_args)
-  call assert_fails('let &omnifunc = Fn', 'E729:')
   bw!
 
   " Test for using a lambda function
@@ -1509,6 +1581,15 @@ func Test_omnifunc_callback()
   bw!
 
   " Set 'omnifunc' to a lambda expression
+  let &omnifunc = {a, b -> MyomniFunc3(a, b)}
+  new | only
+  call setline(1, 'six')
+  let g:MyomniFunc3_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'six']], g:MyomniFunc3_args)
+  bw!
+
+  " Set 'omnifunc' to a string(lambda_expression)
   let &omnifunc = '{a, b -> MyomniFunc3(a, b)}'
   new | only
   call setline(1, 'six')
@@ -1519,18 +1600,27 @@ func Test_omnifunc_callback()
 
   " Set 'omnifunc' to a variable with a lambda expression
   let Lambda = {a, b -> MyomniFunc3(a, b)}
+  let &omnifunc = Lambda
+  new | only
+  call setline(1, 'seven')
+  let g:MyomniFunc3_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'seven']], g:MyomniFunc3_args)
+  bw!
+
+  " Set 'omnifunc' to a string(variable with a lambda expression)
+  let Lambda = {a, b -> MyomniFunc3(a, b)}
   let &omnifunc = string(Lambda)
   new | only
   call setline(1, 'seven')
   let g:MyomniFunc3_args = []
   call feedkeys("A\\\", 'x')
   call assert_equal([[1, ''], [0, 'seven']], g:MyomniFunc3_args)
-  call assert_fails('let &omnifunc = Lambda', 'E729:')
   bw!
 
   " Test for using a lambda function with incorrect return value
   let Lambda = {s -> strlen(s)}
-  let &omnifunc = string(Lambda)
+  let &omnifunc = Lambda
   new | only
   call setline(1, 'eight')
   call feedkeys("A\\\", 'x')
@@ -1542,7 +1632,7 @@ func Test_omnifunc_callback()
 
   call assert_fails("set omnifunc=function('abc')", "E700:")
   call assert_fails("set omnifunc=funcref('abc')", "E700:")
-  let &omnifunc = "{a -> 'abc'}"
+  let &omnifunc = {a -> 'abc'}
   call feedkeys("A\\\", 'x')
 
   " Vim9 tests
@@ -1567,6 +1657,15 @@ func Test_omnifunc_callback()
       add(g:MyomniFunc2_args, [findstart, base])
       return findstart ? 0 : []
     enddef
+    &omnifunc = (a, b) => MyomniFunc2(a, b)
+    new | only
+    setline(1, 'two')
+    g:MyomniFunc2_args = []
+    feedkeys("A\\\", 'x')
+    assert_equal([[1, ''], [0, 'two']], g:MyomniFunc2_args)
+    bw!
+
+    # Test for using a string(lambda)
     &omnifunc = '(a, b) => MyomniFunc2(a, b)'
     new | only
     setline(1, 'two')
@@ -1577,6 +1676,15 @@ func Test_omnifunc_callback()
 
     # Test for using a variable with a lambda expression
     var Fn: func = (a, b) => MyomniFunc2(a, b)
+    &omnifunc = Fn
+    new | only
+    setline(1, 'three')
+    g:MyomniFunc2_args = []
+    feedkeys("A\\\", 'x')
+    assert_equal([[1, ''], [0, 'three']], g:MyomniFunc2_args)
+    bw!
+
+    # Test for using a string(variable with a lambda expression)
     &omnifunc = string(Fn)
     new | only
     setline(1, 'three')
@@ -1619,13 +1727,22 @@ func Test_thesaurusfunc_callback()
 
   " Using a funcref variable to set 'thesaurusfunc'
   let Fn = function('MytsrFunc1')
+  let &thesaurusfunc = Fn
+  new | only
+  call setline(1, 'two')
+  let g:MytsrFunc1_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'two']], g:MytsrFunc1_args)
+  bw!
+
+  " Using a string(funcref_variable) to set 'thesaurusfunc'
+  let Fn = function('MytsrFunc1')
   let &thesaurusfunc = string(Fn)
   new | only
   call setline(1, 'two')
   let g:MytsrFunc1_args = []
   call feedkeys("A\\\", 'x')
   call assert_equal([[1, ''], [0, 'two']], g:MytsrFunc1_args)
-  call assert_fails('let &thesaurusfunc = Fn', 'E729:')
   bw!
 
   " Test for using a funcref()
@@ -1643,13 +1760,22 @@ func Test_thesaurusfunc_callback()
 
   " Using a funcref variable to set 'thesaurusfunc'
   let Fn = funcref('MytsrFunc2')
+  let &thesaurusfunc = Fn
+  new | only
+  call setline(1, 'four')
+  let g:MytsrFunc2_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'four']], g:MytsrFunc2_args)
+  bw!
+
+  " Using a string(funcref_variable) to set 'thesaurusfunc'
+  let Fn = funcref('MytsrFunc2')
   let &thesaurusfunc = string(Fn)
   new | only
   call setline(1, 'four')
   let g:MytsrFunc2_args = []
   call feedkeys("A\\\", 'x')
   call assert_equal([[1, ''], [0, 'four']], g:MytsrFunc2_args)
-  call assert_fails('let &thesaurusfunc = Fn', 'E729:')
   bw!
 
   " Test for using a lambda function
@@ -1666,6 +1792,15 @@ func Test_thesaurusfunc_callback()
   bw!
 
   " Set 'thesaurusfunc' to a lambda expression
+  let &thesaurusfunc = {a, b -> MytsrFunc3(a, b)}
+  new | only
+  call setline(1, 'six')
+  let g:MytsrFunc3_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'six']], g:MytsrFunc3_args)
+  bw!
+
+  " Set 'thesaurusfunc' to a string(lambda expression)
   let &thesaurusfunc = '{a, b -> MytsrFunc3(a, b)}'
   new | only
   call setline(1, 'six')
@@ -1676,18 +1811,27 @@ func Test_thesaurusfunc_callback()
 
   " Set 'thesaurusfunc' to a variable with a lambda expression
   let Lambda = {a, b -> MytsrFunc3(a, b)}
+  let &thesaurusfunc = Lambda
+  new | only
+  call setline(1, 'seven')
+  let g:MytsrFunc3_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'seven']], g:MytsrFunc3_args)
+  bw!
+
+  " Set 'thesaurusfunc' to a string(variable with a lambda expression)
+  let Lambda = {a, b -> MytsrFunc3(a, b)}
   let &thesaurusfunc = string(Lambda)
   new | only
   call setline(1, 'seven')
   let g:MytsrFunc3_args = []
   call feedkeys("A\\\", 'x')
   call assert_equal([[1, ''], [0, 'seven']], g:MytsrFunc3_args)
-  call assert_fails('let &thesaurusfunc = Lambda', 'E729:')
   bw!
 
   " Test for using a lambda function with incorrect return value
   let Lambda = {s -> strlen(s)}
-  let &thesaurusfunc = string(Lambda)
+  let &thesaurusfunc = Lambda
   new | only
   call setline(1, 'eight')
   call feedkeys("A\\\", 'x')
@@ -1699,7 +1843,7 @@ func Test_thesaurusfunc_callback()
 
   call assert_fails("set thesaurusfunc=function('abc')", "E700:")
   call assert_fails("set thesaurusfunc=funcref('abc')", "E700:")
-  let &thesaurusfunc = "{a -> 'abc'}"
+  let &thesaurusfunc = {a -> 'abc'}
   call feedkeys("A\\\", 'x')
 
   " Vim9 tests
@@ -1724,6 +1868,15 @@ func Test_thesaurusfunc_callback()
       add(g:MytsrFunc2_args, [findstart, base])
       return findstart ? 0 : []
     enddef
+    &thesaurusfunc = (a, b) => MytsrFunc2(a, b)
+    new | only
+    setline(1, 'two')
+    g:MytsrFunc2_args = []
+    feedkeys("A\\\", 'x')
+    assert_equal([[1, ''], [0, 'two']], g:MytsrFunc2_args)
+    bw!
+
+    # Test for using a string(lambda)
     &thesaurusfunc = '(a, b) => MytsrFunc2(a, b)'
     new | only
     setline(1, 'two')
@@ -1734,6 +1887,15 @@ func Test_thesaurusfunc_callback()
 
     # Test for using a variable with a lambda expression
     var Fn: func = (a, b) => MytsrFunc2(a, b)
+    &thesaurusfunc = Fn
+    new | only
+    setline(1, 'three')
+    g:MytsrFunc2_args = []
+    feedkeys("A\\\", 'x')
+    assert_equal([[1, ''], [0, 'three']], g:MytsrFunc2_args)
+    bw!
+
+    # Test for using a string(variable with a lambda expression)
     &thesaurusfunc = string(Fn)
     new | only
     setline(1, 'three')
diff --git a/src/nvim/testdir/test_tagfunc.vim b/src/nvim/testdir/test_tagfunc.vim
index 8690e30e77..06d074c261 100644
--- a/src/nvim/testdir/test_tagfunc.vim
+++ b/src/nvim/testdir/test_tagfunc.vim
@@ -138,12 +138,19 @@ func Test_tagfunc_callback()
 
   " Using a funcref variable to set 'tagfunc'
   let Fn = function('MytagFunc1')
+  let &tagfunc = Fn
+  new | only
+  let g:MytagFunc1_args = []
+  call assert_fails('tag a12', 'E433:')
+  call assert_equal(['a12', '', {}], g:MytagFunc1_args)
+
+  " Using a string(funcref_variable) to set 'tagfunc'
+  let Fn = function('MytagFunc1')
   let &tagfunc = string(Fn)
   new | only
   let g:MytagFunc1_args = []
   call assert_fails('tag a12', 'E433:')
   call assert_equal(['a12', '', {}], g:MytagFunc1_args)
-  call assert_fails('let &tagfunc = Fn', 'E729:')
 
   " Test for using a funcref()
   func MytagFunc2(pat, flags, info)
@@ -158,12 +165,19 @@ func Test_tagfunc_callback()
 
   " Using a funcref variable to set 'tagfunc'
   let Fn = funcref('MytagFunc2')
+  let &tagfunc = Fn
+  new | only
+  let g:MytagFunc2_args = []
+  call assert_fails('tag a14', 'E433:')
+  call assert_equal(['a14', '', {}], g:MytagFunc2_args)
+
+  " Using a string(funcref_variable) to set 'tagfunc'
+  let Fn = funcref('MytagFunc2')
   let &tagfunc = string(Fn)
   new | only
   let g:MytagFunc2_args = []
   call assert_fails('tag a14', 'E433:')
   call assert_equal(['a14', '', {}], g:MytagFunc2_args)
-  call assert_fails('let &tagfunc = Fn', 'E729:')
 
   " Test for using a script local function
   set tagfunc=ScriptLocalTagFunc
@@ -174,6 +188,14 @@ func Test_tagfunc_callback()
 
   " Test for using a script local funcref variable
   let Fn = function("s:ScriptLocalTagFunc")
+  let &tagfunc= Fn
+  new | only
+  let g:ScriptLocalFuncArgs = []
+  call assert_fails('tag a16', 'E433:')
+  call assert_equal(['a16', '', {}], g:ScriptLocalFuncArgs)
+
+  " Test for using a string(script local funcref variable)
+  let Fn = function("s:ScriptLocalTagFunc")
   let &tagfunc= string(Fn)
   new | only
   let g:ScriptLocalFuncArgs = []
@@ -192,6 +214,13 @@ func Test_tagfunc_callback()
   call assert_equal(['a17', '', {}], g:MytagFunc3_args)
 
   " Set 'tagfunc' to a lambda expression
+  let &tagfunc = {a, b, c -> MytagFunc3(a, b, c)}
+  new | only
+  let g:MytagFunc3_args = []
+  call assert_fails('tag a18', 'E433:')
+  call assert_equal(['a18', '', {}], g:MytagFunc3_args)
+
+  " Set 'tagfunc' to a string(lambda expression)
   let &tagfunc = '{a, b, c -> MytagFunc3(a, b, c)}'
   new | only
   let g:MytagFunc3_args = []
@@ -200,12 +229,19 @@ func Test_tagfunc_callback()
 
   " Set 'tagfunc' to a variable with a lambda expression
   let Lambda = {a, b, c -> MytagFunc3(a, b, c)}
+  let &tagfunc = Lambda
+  new | only
+  let g:MytagFunc3_args = []
+  call assert_fails("tag a19", "E433:")
+  call assert_equal(['a19', '', {}], g:MytagFunc3_args)
+
+  " Set 'tagfunc' to a string(variable with a lambda expression)
+  let Lambda = {a, b, c -> MytagFunc3(a, b, c)}
   let &tagfunc = string(Lambda)
   new | only
   let g:MytagFunc3_args = []
   call assert_fails("tag a19", "E433:")
   call assert_equal(['a19', '', {}], g:MytagFunc3_args)
-  call assert_fails('let &tagfunc = Lambda', 'E729:')
 
   " Test for using a lambda function with incorrect return value
   let Lambda = {s -> strlen(s)}
@@ -242,6 +278,13 @@ func Test_tagfunc_callback()
       g:MytagFunc2_args = [pat, flags, info]
       return null
     enddef
+    &tagfunc = (a, b, c) => MytagFunc2(a, b, c)
+    new | only
+    g:MytagFunc2_args = []
+    assert_fails('tag a20', 'E433:')
+    assert_equal(['a20', '', {}], g:MytagFunc2_args)
+
+    # Test for using a string(lambda)
     &tagfunc = '(a, b, c) => MytagFunc2(a, b, c)'
     new | only
     g:MytagFunc2_args = []
@@ -250,6 +293,13 @@ func Test_tagfunc_callback()
 
     # Test for using a variable with a lambda expression
     var Fn: func = (a, b, c) => MytagFunc2(a, b, c)
+    &tagfunc = Fn
+    new | only
+    g:MytagFunc2_args = []
+    assert_fails('tag a30', 'E433:')
+    assert_equal(['a30', '', {}], g:MytagFunc2_args)
+
+    # Test for using a variable with a lambda expression
     &tagfunc = string(Fn)
     new | only
     g:MytagFunc2_args = []
-- 
cgit 


From d7bd7f13a8f026b8b95fdc49b4754f6199105891 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 7 Nov 2022 11:04:33 +0800
Subject: vim-patch:8.2.3756: might crash when callback is not valid

Problem:    might crash when callback is not valid.
Solution:   Check for valid callback. (Yegappan Lakshmanan, closes vim/vim#9293)

https://github.com/vim/vim/commit/4dc24eb5adbcc76838fae1e900936dd230209d96

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/eval.c                        |  5 +++--
 src/nvim/eval/userfunc.c               |  4 ++--
 src/nvim/insexpand.c                   |  4 +---
 src/nvim/option.c                      |  2 +-
 src/nvim/tag.c                         |  4 ++--
 src/nvim/testdir/test_ins_complete.vim | 28 ++++++++++++++++++++++++++++
 src/nvim/testdir/test_tagfunc.vim      |  5 +++++
 7 files changed, 42 insertions(+), 10 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index a4fadcd7bc..daae6416dc 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -5860,6 +5860,7 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg)
   return true;
 }
 
+/// @return  whether the callback could be called.
 bool callback_call(Callback *const callback, const int argcount_in, typval_T *const argvars_in,
                    typval_T *const rettv)
   FUNC_ATTR_NONNULL_ALL
@@ -8705,9 +8706,9 @@ bool invoke_prompt_interrupt(void)
   argv[0].v_type = VAR_UNKNOWN;
 
   got_int = false;  // don't skip executing commands
-  callback_call(&curbuf->b_prompt_interrupt, 0, argv, &rettv);
+  int ret = callback_call(&curbuf->b_prompt_interrupt, 0, argv, &rettv);
   tv_clear(&rettv);
-  return true;
+  return ret != FAIL;
 }
 
 /// Compare "typ1" and "typ2".  Put the result in "typ1".
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index d07cfe0bd9..cc21bf56ca 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -1394,7 +1394,7 @@ func_call_skip_call:
 }
 
 /// call the 'callback' function and return the result as a number.
-/// Returns -1 when calling the function fails.  Uses argv[0] to argv[argc - 1]
+/// Returns -2 when calling the function fails.  Uses argv[0] to argv[argc - 1]
 /// for the function arguments. argv[argc] should have type VAR_UNKNOWN.
 ///
 /// @param argcount  number of "argvars"
@@ -1403,7 +1403,7 @@ varnumber_T callback_call_retnr(Callback *callback, int argcount, typval_T *argv
 {
   typval_T rettv;
   if (!callback_call(callback, argcount, argvars, &rettv)) {
-    return -1;
+    return -2;
   }
 
   varnumber_T retval = tv_get_number_chk(&rettv, NULL);
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index 1acdddedef..7d4e77b263 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -2233,7 +2233,7 @@ static Callback tsrfu_cb;  ///< 'thesaurusfunc' callback function
 static void copy_global_to_buflocal_cb(Callback *globcb, Callback *bufcb)
 {
   callback_free(bufcb);
-  if (globcb->data.funcref != NULL && *globcb->data.funcref != NUL) {
+  if (globcb->type != kCallbackNone) {
     callback_copy(bufcb, globcb);
   }
 }
@@ -2290,11 +2290,9 @@ int set_thesaurusfunc_option(void)
 
   if (*curbuf->b_p_tsrfu != NUL) {
     // buffer-local option set
-    callback_free(&curbuf->b_tsrfu_cb);
     retval = option_set_callback_func(curbuf->b_p_tsrfu, &curbuf->b_tsrfu_cb);
   } else {
     // global option set
-    callback_free(&tsrfu_cb);
     retval = option_set_callback_func(p_tsrfu, &tsrfu_cb);
   }
 
diff --git a/src/nvim/option.c b/src/nvim/option.c
index c0a1f14463..419cfb6873 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -5193,7 +5193,7 @@ int option_set_callback_func(char *optval, Callback *optcb)
   }
 
   Callback cb;
-  if (!callback_from_typval(&cb, tv)) {
+  if (!callback_from_typval(&cb, tv) || cb.type == kCallbackNone) {
     tv_free(tv);
     return FAIL;
   }
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 16d6629c2e..d57bbead63 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -154,7 +154,7 @@ void free_tagfunc_option(void)
 void set_buflocal_tfu_callback(buf_T *buf)
 {
   callback_free(&buf->b_tfu_cb);
-  if (tfu_cb.data.funcref != NULL && *tfu_cb.data.funcref != NUL) {
+  if (tfu_cb.type != kCallbackNone) {
     callback_copy(&buf->b_tfu_cb, &tfu_cb);
   }
 }
@@ -1153,7 +1153,7 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
     }
   }
 
-  if (*curbuf->b_p_tfu == NUL) {
+  if (*curbuf->b_p_tfu == NUL || curbuf->b_tfu_cb.type == kCallbackNone) {
     return FAIL;
   }
 
diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim
index 8b5f9a189f..05a67e7956 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -1491,6 +1491,15 @@ func Test_completefunc_callback()
   " call assert_fails('call feedkeys("A\\\", "x")', 'E117:')
   " call assert_equal([], g:MycompleteFunc2_args)
 
+  " set 'completefunc' to a non-existing function
+  set completefunc=MycompleteFunc2
+  call setline(1, 'five')
+  call assert_fails("set completefunc=function('NonExistingFunc')", 'E700:')
+  call assert_fails("let &completefunc = function('NonExistingFunc')", 'E700:')
+  let g:MycompleteFunc2_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'five']], g:MycompleteFunc2_args)
+
   " cleanup
   delfunc MycompleteFunc1
   delfunc MycompleteFunc2
@@ -1702,6 +1711,15 @@ func Test_omnifunc_callback()
   " call assert_fails('call feedkeys("A\\\", "x")', 'E117:')
   " call assert_equal([], g:MyomniFunc2_args)
 
+  " set 'omnifunc' to a non-existing function
+  set omnifunc=MyomniFunc2
+  call setline(1, 'nine')
+  call assert_fails("set omnifunc=function('NonExistingFunc')", 'E700:')
+  call assert_fails("let &omnifunc = function('NonExistingFunc')", 'E700:')
+  let g:MyomniFunc2_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'nine']], g:MyomniFunc2_args)
+
   " cleanup
   delfunc MyomniFunc1
   delfunc MyomniFunc2
@@ -1939,6 +1957,16 @@ func Test_thesaurusfunc_callback()
   call feedkeys("A\\\", "x")
   call assert_equal('sunday', getline(1))
   call assert_equal([[1, ''], [0, 'sun']], g:MytsrFunc4_args)
+  %bw!
+
+  " set 'thesaurusfunc' to a non-existing function
+  set thesaurusfunc=MytsrFunc2
+  call setline(1, 'ten')
+  call assert_fails("set thesaurusfunc=function('NonExistingFunc')", 'E700:')
+  call assert_fails("let &thesaurusfunc = function('NonExistingFunc')", 'E700:')
+  let g:MytsrFunc2_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'ten']], g:MytsrFunc2_args)
 
   " cleanup
   set thesaurusfunc&
diff --git a/src/nvim/testdir/test_tagfunc.vim b/src/nvim/testdir/test_tagfunc.vim
index 06d074c261..88500db269 100644
--- a/src/nvim/testdir/test_tagfunc.vim
+++ b/src/nvim/testdir/test_tagfunc.vim
@@ -315,6 +315,11 @@ func Test_tagfunc_callback()
   " call assert_fails("tag a17", "E117:")
   " call assert_equal([], g:MytagFunc3_args)
 
+  " set 'tagfunc' to a non-existing function
+  call assert_fails("set tagfunc=function('NonExistingFunc')", 'E700:')
+  call assert_fails("let &tagfunc = function('NonExistingFunc')", 'E700:')
+  call assert_fails("tag axb123", 'E426:')
+
   " cleanup
   delfunc MytagFunc1
   delfunc MytagFunc2
-- 
cgit 


From 8f9ae5278464205004c421e49dad640808b2256f Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 7 Nov 2022 11:20:42 +0800
Subject: vim-patch:8.2.3758: options that take a function insufficiently
 tested

Problem:    Options that take a function insufficiently tested.
Solution:   Add additional tests and enhance existing tests. (Yegappan
            Lakshmanan, closes vim/vim#9298)

https://github.com/vim/vim/commit/2172bff36417ddd90653531edc65897411c83b3f

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/testdir/test_ins_complete.vim | 506 ++++++++++++++++-----------------
 src/nvim/testdir/test_normal.vim       | 240 +++++++++++-----
 src/nvim/testdir/test_tagfunc.vim      | 134 +++++----
 3 files changed, 480 insertions(+), 400 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim
index 05a67e7956..eaac66dae0 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -1287,121 +1287,114 @@ endfunc
 
 " Test for different ways of setting the 'completefunc' option
 func Test_completefunc_callback()
-  " Test for using a function()
-  func MycompleteFunc1(findstart, base)
-    call add(g:MycompleteFunc1_args, [a:findstart, a:base])
+  func MycompleteFunc1(val, findstart, base)
+    call add(g:MycompleteFunc1_args, [a:val, a:findstart, a:base])
     return a:findstart ? 0 : []
   endfunc
-  set completefunc=function('MycompleteFunc1')
+
+  " Test for using a function()
+  set completefunc=function('MycompleteFunc1',[10])
   new | only
   call setline(1, 'one')
   let g:MycompleteFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'one']], g:MycompleteFunc1_args)
+  call assert_equal([[10, 1, ''], [10, 0, 'one']], g:MycompleteFunc1_args)
   bw!
 
   " Using a funcref variable to set 'completefunc'
-  let Fn = function('MycompleteFunc1')
+  let Fn = function('MycompleteFunc1', [11])
   let &completefunc = Fn
   new | only
   call setline(1, 'two')
   let g:MycompleteFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'two']], g:MycompleteFunc1_args)
+  call assert_equal([[11, 1, ''], [11, 0, 'two']], g:MycompleteFunc1_args)
   bw!
 
   " Using string(funcref_variable) to set 'completefunc'
-  let Fn = function('MycompleteFunc1')
+  let Fn = function('MycompleteFunc1', [12])
   let &completefunc = string(Fn)
   new | only
   call setline(1, 'two')
   let g:MycompleteFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'two']], g:MycompleteFunc1_args)
+  call assert_equal([[12, 1, ''], [12, 0, 'two']], g:MycompleteFunc1_args)
   bw!
 
   " Test for using a funcref()
-  func MycompleteFunc2(findstart, base)
-    call add(g:MycompleteFunc2_args, [a:findstart, a:base])
-    return a:findstart ? 0 : []
-  endfunc
-  set completefunc=funcref('MycompleteFunc2')
+  set completefunc=funcref('MycompleteFunc1',\ [13])
   new | only
   call setline(1, 'three')
-  let g:MycompleteFunc2_args = []
+  let g:MycompleteFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'three']], g:MycompleteFunc2_args)
+  call assert_equal([[13, 1, ''], [13, 0, 'three']], g:MycompleteFunc1_args)
   bw!
 
   " Using a funcref variable to set 'completefunc'
-  let Fn = funcref('MycompleteFunc2')
+  let Fn = funcref('MycompleteFunc1', [14])
   let &completefunc = Fn
   new | only
   call setline(1, 'four')
-  let g:MycompleteFunc2_args = []
+  let g:MycompleteFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'four']], g:MycompleteFunc2_args)
+  call assert_equal([[14, 1, ''], [14, 0, 'four']], g:MycompleteFunc1_args)
   bw!
 
   " Using a string(funcref_variable) to set 'completefunc'
-  let Fn = funcref('MycompleteFunc2')
+  let Fn = funcref('MycompleteFunc1', [15])
   let &completefunc = string(Fn)
   new | only
   call setline(1, 'four')
-  let g:MycompleteFunc2_args = []
+  let g:MycompleteFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'four']], g:MycompleteFunc2_args)
+  call assert_equal([[15, 1, ''], [15, 0, 'four']], g:MycompleteFunc1_args)
   bw!
 
   " Test for using a lambda function
-  func MycompleteFunc3(findstart, base)
-    call add(g:MycompleteFunc3_args, [a:findstart, a:base])
-    return a:findstart ? 0 : []
-  endfunc
-  set completefunc={a,\ b\ ->\ MycompleteFunc3(a,\ b)}
+  set completefunc={a,\ b\ ->\ MycompleteFunc1(16,\ a,\ b)}
   new | only
   call setline(1, 'five')
-  let g:MycompleteFunc3_args = []
+  let g:MycompleteFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'five']], g:MycompleteFunc3_args)
+  call assert_equal([[16, 1, ''], [16, 0, 'five']], g:MycompleteFunc1_args)
   bw!
 
   " Set 'completefunc' to a lambda expression
-  let &completefunc = {a, b -> MycompleteFunc3(a, b)}
+  let &completefunc = {a, b -> MycompleteFunc1(17, a, b)}
   new | only
   call setline(1, 'six')
-  let g:MycompleteFunc3_args = []
+  let g:MycompleteFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'six']], g:MycompleteFunc3_args)
+  call assert_equal([[17, 1, ''], [17, 0, 'six']], g:MycompleteFunc1_args)
   bw!
 
   " Set 'completefunc' to string(lambda_expression)
-  let &completefunc = '{a, b -> MycompleteFunc3(a, b)}'
+  let &completefunc = '{a, b -> MycompleteFunc1(18, a, b)}'
   new | only
   call setline(1, 'six')
-  let g:MycompleteFunc3_args = []
+  let g:MycompleteFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'six']], g:MycompleteFunc3_args)
+  call assert_equal([[18, 1, ''], [18, 0, 'six']], g:MycompleteFunc1_args)
   bw!
 
   " Set 'completefunc' to a variable with a lambda expression
-  let Lambda = {a, b -> MycompleteFunc3(a, b)}
+  let Lambda = {a, b -> MycompleteFunc1(19, a, b)}
   let &completefunc = Lambda
   new | only
   call setline(1, 'seven')
-  let g:MycompleteFunc3_args = []
+  let g:MycompleteFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'seven']], g:MycompleteFunc3_args)
+  call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:MycompleteFunc1_args)
   bw!
 
   " Set 'completefunc' to a string(variable with a lambda expression)
-  let Lambda = {a, b -> MycompleteFunc3(a, b)}
+  let Lambda = {a, b -> MycompleteFunc1(20, a, b)}
   let &completefunc = string(Lambda)
   new | only
   call setline(1, 'seven')
-  let g:MycompleteFunc3_args = []
+  let g:MycompleteFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'seven']], g:MycompleteFunc3_args)
+  call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:MycompleteFunc1_args)
   bw!
 
   " Test for using a lambda function with incorrect return value
@@ -1421,210 +1414,201 @@ func Test_completefunc_callback()
   let &completefunc = {a -> 'abc'}
   call feedkeys("A\\\", 'x')
 
+  " Using Vim9 lambda expression in legacy context should fail
+  " set completefunc=(a,\ b)\ =>\ g:MycompleteFunc1(21,\ a,\ b)
+  new | only
+  let g:MycompleteFunc1_args = []
+  " call assert_fails('call feedkeys("A\\\", "x")', 'E117:')
+  call assert_equal([], g:MycompleteFunc1_args)
+
+  " set 'completefunc' to a non-existing function
+  func MycompleteFunc2(findstart, base)
+    call add(g:MycompleteFunc2_args, [a:findstart, a:base])
+    return a:findstart ? 0 : []
+  endfunc
+  set completefunc=MycompleteFunc2
+  call setline(1, 'five')
+  call assert_fails("set completefunc=function('NonExistingFunc')", 'E700:')
+  call assert_fails("let &completefunc = function('NonExistingFunc')", 'E700:')
+  let g:MycompleteFunc2_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'five']], g:MycompleteFunc2_args)
+  bw!
+
   " Vim9 tests
   let lines =<< trim END
     vim9script
 
     # Test for using function()
-    def MycompleteFunc1(findstart: number, base: string): any
-      add(g:MycompleteFunc1_args, [findstart, base])
+    def Vim9CompleteFunc(val: number, findstart: number, base: string): any
+      add(g:Vim9completeFuncArgs, [val, findstart, base])
       return findstart ? 0 : []
     enddef
-    set completefunc=function('MycompleteFunc1')
+    set completefunc=function('Vim9CompleteFunc',\ [60])
     new | only
     setline(1, 'one')
-    g:MycompleteFunc1_args = []
+    g:Vim9completeFuncArgs = []
     feedkeys("A\\\", 'x')
-    assert_equal([[1, ''], [0, 'one']], g:MycompleteFunc1_args)
+    assert_equal([[60, 1, ''], [60, 0, 'one']], g:Vim9completeFuncArgs)
     bw!
 
     # Test for using a lambda
-    def LambdaComplete1(findstart: number, base: string): any
-      add(g:LambdaComplete1_args, [findstart, base])
-      return findstart ? 0 : []
-    enddef
-    &completefunc = (a, b) => LambdaComplete1(a, b)
+    &completefunc = (a, b) => Vim9CompleteFunc(61, a, b)
     new | only
     setline(1, 'two')
-    g:LambdaComplete1_args = []
+    g:Vim9completeFuncArgs = []
     feedkeys("A\\\", 'x')
-    assert_equal([[1, ''], [0, 'two']], g:LambdaComplete1_args)
+    assert_equal([[61, 1, ''], [61, 0, 'two']], g:Vim9completeFuncArgs)
     bw!
 
     # Test for using a string(lambda)
-    &completefunc = '(a, b) => LambdaComplete1(a, b)'
+    &completefunc = '(a, b) => Vim9CompleteFunc(62, a, b)'
     new | only
     setline(1, 'two')
-    g:LambdaComplete1_args = []
+    g:Vim9completeFuncArgs = []
     feedkeys("A\\\", 'x')
-    assert_equal([[1, ''], [0, 'two']], g:LambdaComplete1_args)
+    assert_equal([[62, 1, ''], [62, 0, 'two']], g:Vim9completeFuncArgs)
     bw!
 
     # Test for using a variable with a lambda expression
-    var Fn: func = (findstart, base) => {
-            add(g:LambdaComplete2_args, [findstart, base])
-            return findstart ? 0 : []
-        }
+    var Fn: func = (a, b) => Vim9CompleteFunc(63, a, b)
     &completefunc = Fn
     new | only
     setline(1, 'three')
-    g:LambdaComplete2_args = []
+    g:Vim9completeFuncArgs = []
     feedkeys("A\\\", 'x')
-    assert_equal([[1, ''], [0, 'three']], g:LambdaComplete2_args)
+    assert_equal([[63, 1, ''], [63, 0, 'three']], g:Vim9completeFuncArgs)
     bw!
 
     # Test for using a string(variable with a lambda expression)
+    Fn = (a, b) => Vim9CompleteFunc(64, a, b)
     &completefunc = string(Fn)
     new | only
     setline(1, 'three')
-    g:LambdaComplete2_args = []
+    g:Vim9completeFuncArgs = []
     feedkeys("A\\\", 'x')
-    assert_equal([[1, ''], [0, 'three']], g:LambdaComplete2_args)
+    assert_equal([[64, 1, ''], [64, 0, 'three']], g:Vim9completeFuncArgs)
     bw!
   END
   " call CheckScriptSuccess(lines)
 
-  " Using Vim9 lambda expression in legacy context should fail
-  " set completefunc=(a,\ b)\ =>\ g:MycompleteFunc2(a,\ b)
-  " new | only
-  " let g:MycompleteFunc2_args = []
-  " call assert_fails('call feedkeys("A\\\", "x")', 'E117:')
-  " call assert_equal([], g:MycompleteFunc2_args)
-
-  " set 'completefunc' to a non-existing function
-  set completefunc=MycompleteFunc2
-  call setline(1, 'five')
-  call assert_fails("set completefunc=function('NonExistingFunc')", 'E700:')
-  call assert_fails("let &completefunc = function('NonExistingFunc')", 'E700:')
-  let g:MycompleteFunc2_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'five']], g:MycompleteFunc2_args)
-
   " cleanup
   delfunc MycompleteFunc1
   delfunc MycompleteFunc2
-  delfunc MycompleteFunc3
   set completefunc&
   %bw!
 endfunc
 
 " Test for different ways of setting the 'omnifunc' option
 func Test_omnifunc_callback()
-  " Test for using a function()
-  func MyomniFunc1(findstart, base)
-    call add(g:MyomniFunc1_args, [a:findstart, a:base])
+  func MyomniFunc1(val, findstart, base)
+    call add(g:MyomniFunc1_args, [a:val, a:findstart, a:base])
     return a:findstart ? 0 : []
   endfunc
-  set omnifunc=function('MyomniFunc1')
+
+  " Test for using a function()
+  set omnifunc=function('MyomniFunc1',[10])
   new | only
   call setline(1, 'one')
   let g:MyomniFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'one']], g:MyomniFunc1_args)
+  call assert_equal([[10, 1, ''], [10, 0, 'one']], g:MyomniFunc1_args)
   bw!
 
   " Using a funcref variable to set 'omnifunc'
-  let Fn = function('MyomniFunc1')
+  let Fn = function('MyomniFunc1', [11])
   let &omnifunc = Fn
   new | only
   call setline(1, 'two')
   let g:MyomniFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'two']], g:MyomniFunc1_args)
+  call assert_equal([[11, 1, ''], [11, 0, 'two']], g:MyomniFunc1_args)
   bw!
 
   " Using a string(funcref_variable) to set 'omnifunc'
-  let Fn = function('MyomniFunc1')
+  let Fn = function('MyomniFunc1', [12])
   let &omnifunc = string(Fn)
   new | only
   call setline(1, 'two')
   let g:MyomniFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'two']], g:MyomniFunc1_args)
+  call assert_equal([[12, 1, ''], [12, 0, 'two']], g:MyomniFunc1_args)
   bw!
 
   " Test for using a funcref()
-  func MyomniFunc2(findstart, base)
-    call add(g:MyomniFunc2_args, [a:findstart, a:base])
-    return a:findstart ? 0 : []
-  endfunc
-  set omnifunc=funcref('MyomniFunc2')
+  set omnifunc=funcref('MyomniFunc1',\ [13])
   new | only
   call setline(1, 'three')
-  let g:MyomniFunc2_args = []
+  let g:MyomniFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'three']], g:MyomniFunc2_args)
+  call assert_equal([[13, 1, ''], [13, 0, 'three']], g:MyomniFunc1_args)
   bw!
 
   " Using a funcref variable to set 'omnifunc'
-  let Fn = funcref('MyomniFunc2')
+  let Fn = funcref('MyomniFunc1', [14])
   let &omnifunc = Fn
   new | only
   call setline(1, 'four')
-  let g:MyomniFunc2_args = []
+  let g:MyomniFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'four']], g:MyomniFunc2_args)
+  call assert_equal([[14, 1, ''], [14, 0, 'four']], g:MyomniFunc1_args)
   bw!
 
   " Using a string(funcref_variable) to set 'omnifunc'
-  let Fn = funcref('MyomniFunc2')
+  let Fn = funcref('MyomniFunc1', [15])
   let &omnifunc = string(Fn)
   new | only
   call setline(1, 'four')
-  let g:MyomniFunc2_args = []
+  let g:MyomniFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'four']], g:MyomniFunc2_args)
+  call assert_equal([[15, 1, ''], [15, 0, 'four']], g:MyomniFunc1_args)
   bw!
 
   " Test for using a lambda function
-  func MyomniFunc3(findstart, base)
-    call add(g:MyomniFunc3_args, [a:findstart, a:base])
-    return a:findstart ? 0 : []
-  endfunc
-  set omnifunc={a,\ b\ ->\ MyomniFunc3(a,\ b)}
+  set omnifunc={a,\ b\ ->\ MyomniFunc1(16,\ a,\ b)}
   new | only
   call setline(1, 'five')
-  let g:MyomniFunc3_args = []
+  let g:MyomniFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'five']], g:MyomniFunc3_args)
+  call assert_equal([[16, 1, ''], [16, 0, 'five']], g:MyomniFunc1_args)
   bw!
 
   " Set 'omnifunc' to a lambda expression
-  let &omnifunc = {a, b -> MyomniFunc3(a, b)}
+  let &omnifunc = {a, b -> MyomniFunc1(17, a, b)}
   new | only
   call setline(1, 'six')
-  let g:MyomniFunc3_args = []
+  let g:MyomniFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'six']], g:MyomniFunc3_args)
+  call assert_equal([[17, 1, ''], [17, 0, 'six']], g:MyomniFunc1_args)
   bw!
 
   " Set 'omnifunc' to a string(lambda_expression)
-  let &omnifunc = '{a, b -> MyomniFunc3(a, b)}'
+  let &omnifunc = '{a, b -> MyomniFunc1(18, a, b)}'
   new | only
   call setline(1, 'six')
-  let g:MyomniFunc3_args = []
+  let g:MyomniFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'six']], g:MyomniFunc3_args)
+  call assert_equal([[18, 1, ''], [18, 0, 'six']], g:MyomniFunc1_args)
   bw!
 
   " Set 'omnifunc' to a variable with a lambda expression
-  let Lambda = {a, b -> MyomniFunc3(a, b)}
+  let Lambda = {a, b -> MyomniFunc1(19, a, b)}
   let &omnifunc = Lambda
   new | only
   call setline(1, 'seven')
-  let g:MyomniFunc3_args = []
+  let g:MyomniFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'seven']], g:MyomniFunc3_args)
+  call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:MyomniFunc1_args)
   bw!
 
   " Set 'omnifunc' to a string(variable with a lambda expression)
-  let Lambda = {a, b -> MyomniFunc3(a, b)}
+  let Lambda = {a, b -> MyomniFunc1(20, a, b)}
   let &omnifunc = string(Lambda)
   new | only
   call setline(1, 'seven')
-  let g:MyomniFunc3_args = []
+  let g:MyomniFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'seven']], g:MyomniFunc3_args)
+  call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:MyomniFunc1_args)
   bw!
 
   " Test for using a lambda function with incorrect return value
@@ -1644,207 +1628,201 @@ func Test_omnifunc_callback()
   let &omnifunc = {a -> 'abc'}
   call feedkeys("A\\\", 'x')
 
+  " Using Vim9 lambda expression in legacy context should fail
+  " set omnifunc=(a,\ b)\ =>\ g:MyomniFunc1(21,\ a,\ b)
+  new | only
+  let g:MyomniFunc1_args = []
+  " call assert_fails('call feedkeys("A\\\", "x")', 'E117:')
+  call assert_equal([], g:MyomniFunc1_args)
+
+  " set 'omnifunc' to a non-existing function
+  func MyomniFunc2(findstart, base)
+    call add(g:MyomniFunc2_args, [a:findstart, a:base])
+    return a:findstart ? 0 : []
+  endfunc
+  set omnifunc=MyomniFunc2
+  call setline(1, 'nine')
+  call assert_fails("set omnifunc=function('NonExistingFunc')", 'E700:')
+  call assert_fails("let &omnifunc = function('NonExistingFunc')", 'E700:')
+  let g:MyomniFunc2_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'nine']], g:MyomniFunc2_args)
+  bw!
+
   " Vim9 tests
   let lines =<< trim END
     vim9script
 
     # Test for using function()
-    def MyomniFunc1(findstart: number, base: string): any
-      add(g:MyomniFunc1_args, [findstart, base])
+    def Vim9omniFunc(val: number, findstart: number, base: string): any
+      add(g:Vim9omniFunc_Args, [val, findstart, base])
       return findstart ? 0 : []
     enddef
-    set omnifunc=function('MyomniFunc1')
+    set omnifunc=function('Vim9omniFunc',\ [60])
     new | only
     setline(1, 'one')
-    g:MyomniFunc1_args = []
+    g:Vim9omniFunc_Args = []
     feedkeys("A\\\", 'x')
-    assert_equal([[1, ''], [0, 'one']], g:MyomniFunc1_args)
+    assert_equal([[60, 1, ''], [60, 0, 'one']], g:Vim9omniFunc_Args)
     bw!
 
     # Test for using a lambda
-    def MyomniFunc2(findstart: number, base: string): any
-      add(g:MyomniFunc2_args, [findstart, base])
-      return findstart ? 0 : []
-    enddef
-    &omnifunc = (a, b) => MyomniFunc2(a, b)
+    &omnifunc = (a, b) => Vim9omniFunc(61, a, b)
     new | only
     setline(1, 'two')
-    g:MyomniFunc2_args = []
+    g:Vim9omniFunc_Args = []
     feedkeys("A\\\", 'x')
-    assert_equal([[1, ''], [0, 'two']], g:MyomniFunc2_args)
+    assert_equal([[61, 1, ''], [61, 0, 'two']], g:Vim9omniFunc_Args)
     bw!
 
     # Test for using a string(lambda)
-    &omnifunc = '(a, b) => MyomniFunc2(a, b)'
+    &omnifunc = '(a, b) => Vim9omniFunc(62, a, b)'
     new | only
     setline(1, 'two')
-    g:MyomniFunc2_args = []
+    g:Vim9omniFunc_Args = []
     feedkeys("A\\\", 'x')
-    assert_equal([[1, ''], [0, 'two']], g:MyomniFunc2_args)
+    assert_equal([[62, 1, ''], [62, 0, 'two']], g:Vim9omniFunc_Args)
     bw!
 
     # Test for using a variable with a lambda expression
-    var Fn: func = (a, b) => MyomniFunc2(a, b)
+    var Fn: func = (a, b) => Vim9omniFunc(63, a, b)
     &omnifunc = Fn
     new | only
     setline(1, 'three')
-    g:MyomniFunc2_args = []
+    g:Vim9omniFunc_Args = []
     feedkeys("A\\\", 'x')
-    assert_equal([[1, ''], [0, 'three']], g:MyomniFunc2_args)
+    assert_equal([[63, 1, ''], [63, 0, 'three']], g:Vim9omniFunc_Args)
     bw!
 
     # Test for using a string(variable with a lambda expression)
+    Fn = (a, b) => Vim9omniFunc(64, a, b)
     &omnifunc = string(Fn)
     new | only
     setline(1, 'three')
-    g:MyomniFunc2_args = []
+    g:Vim9omniFunc_Args = []
     feedkeys("A\\\", 'x')
-    assert_equal([[1, ''], [0, 'three']], g:MyomniFunc2_args)
+    assert_equal([[64, 1, ''], [64, 0, 'three']], g:Vim9omniFunc_Args)
     bw!
   END
   " call CheckScriptSuccess(lines)
 
-  " Using Vim9 lambda expression in legacy context should fail
-  " set omnifunc=(a,\ b)\ =>\ g:MyomniFunc2(a,\ b)
-  " new | only
-  " let g:MyomniFunc2_args = []
-  " call assert_fails('call feedkeys("A\\\", "x")', 'E117:')
-  " call assert_equal([], g:MyomniFunc2_args)
-
-  " set 'omnifunc' to a non-existing function
-  set omnifunc=MyomniFunc2
-  call setline(1, 'nine')
-  call assert_fails("set omnifunc=function('NonExistingFunc')", 'E700:')
-  call assert_fails("let &omnifunc = function('NonExistingFunc')", 'E700:')
-  let g:MyomniFunc2_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'nine']], g:MyomniFunc2_args)
-
   " cleanup
   delfunc MyomniFunc1
   delfunc MyomniFunc2
-  delfunc MyomniFunc3
   set omnifunc&
   %bw!
 endfunc
 
 " Test for different ways of setting the 'thesaurusfunc' option
 func Test_thesaurusfunc_callback()
-  " Test for using a function()
-  func MytsrFunc1(findstart, base)
-    call add(g:MytsrFunc1_args, [a:findstart, a:base])
+  func MytsrFunc1(val, findstart, base)
+    call add(g:MytsrFunc1_args, [a:val, a:findstart, a:base])
     return a:findstart ? 0 : []
   endfunc
-  set thesaurusfunc=function('MytsrFunc1')
+
+  " Test for using a function()
+  set thesaurusfunc=function('MytsrFunc1',[10])
   new | only
   call setline(1, 'one')
   let g:MytsrFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'one']], g:MytsrFunc1_args)
+  call assert_equal([[10, 1, ''], [10, 0, 'one']], g:MytsrFunc1_args)
   bw!
 
   " Using a funcref variable to set 'thesaurusfunc'
-  let Fn = function('MytsrFunc1')
+  let Fn = function('MytsrFunc1', [11])
   let &thesaurusfunc = Fn
   new | only
   call setline(1, 'two')
   let g:MytsrFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'two']], g:MytsrFunc1_args)
+  call assert_equal([[11, 1, ''], [11, 0, 'two']], g:MytsrFunc1_args)
   bw!
 
   " Using a string(funcref_variable) to set 'thesaurusfunc'
-  let Fn = function('MytsrFunc1')
+  let Fn = function('MytsrFunc1', [12])
   let &thesaurusfunc = string(Fn)
   new | only
   call setline(1, 'two')
   let g:MytsrFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'two']], g:MytsrFunc1_args)
+  call assert_equal([[12, 1, ''], [12, 0, 'two']], g:MytsrFunc1_args)
   bw!
 
   " Test for using a funcref()
-  func MytsrFunc2(findstart, base)
-    call add(g:MytsrFunc2_args, [a:findstart, a:base])
-    return a:findstart ? 0 : []
-  endfunc
-  set thesaurusfunc=funcref('MytsrFunc2')
+  set thesaurusfunc=funcref('MytsrFunc1',[13])
   new | only
   call setline(1, 'three')
-  let g:MytsrFunc2_args = []
+  let g:MytsrFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'three']], g:MytsrFunc2_args)
+  call assert_equal([[13, 1, ''], [13, 0, 'three']], g:MytsrFunc1_args)
   bw!
 
   " Using a funcref variable to set 'thesaurusfunc'
-  let Fn = funcref('MytsrFunc2')
+  let Fn = funcref('MytsrFunc1', [14])
   let &thesaurusfunc = Fn
   new | only
   call setline(1, 'four')
-  let g:MytsrFunc2_args = []
+  let g:MytsrFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'four']], g:MytsrFunc2_args)
+  call assert_equal([[14, 1, ''], [14, 0, 'four']], g:MytsrFunc1_args)
   bw!
 
   " Using a string(funcref_variable) to set 'thesaurusfunc'
-  let Fn = funcref('MytsrFunc2')
+  let Fn = funcref('MytsrFunc1', [15])
   let &thesaurusfunc = string(Fn)
   new | only
   call setline(1, 'four')
-  let g:MytsrFunc2_args = []
+  let g:MytsrFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'four']], g:MytsrFunc2_args)
+  call assert_equal([[15, 1, ''], [15, 0, 'four']], g:MytsrFunc1_args)
   bw!
 
   " Test for using a lambda function
-  func MytsrFunc3(findstart, base)
-    call add(g:MytsrFunc3_args, [a:findstart, a:base])
-    return a:findstart ? 0 : []
-  endfunc
-  set thesaurusfunc={a,\ b\ ->\ MytsrFunc3(a,\ b)}
+  set thesaurusfunc={a,\ b\ ->\ MytsrFunc1(16,\ a,\ b)}
   new | only
   call setline(1, 'five')
-  let g:MytsrFunc3_args = []
+  let g:MytsrFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'five']], g:MytsrFunc3_args)
+  call assert_equal([[16, 1, ''], [16, 0, 'five']], g:MytsrFunc1_args)
   bw!
 
   " Set 'thesaurusfunc' to a lambda expression
-  let &thesaurusfunc = {a, b -> MytsrFunc3(a, b)}
+  let &thesaurusfunc = {a, b -> MytsrFunc1(17, a, b)}
   new | only
   call setline(1, 'six')
-  let g:MytsrFunc3_args = []
+  let g:MytsrFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'six']], g:MytsrFunc3_args)
+  call assert_equal([[17, 1, ''], [17, 0, 'six']], g:MytsrFunc1_args)
   bw!
 
   " Set 'thesaurusfunc' to a string(lambda expression)
-  let &thesaurusfunc = '{a, b -> MytsrFunc3(a, b)}'
+  let &thesaurusfunc = '{a, b -> MytsrFunc1(18, a, b)}'
   new | only
   call setline(1, 'six')
-  let g:MytsrFunc3_args = []
+  let g:MytsrFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'six']], g:MytsrFunc3_args)
+  call assert_equal([[18, 1, ''], [18, 0, 'six']], g:MytsrFunc1_args)
   bw!
 
   " Set 'thesaurusfunc' to a variable with a lambda expression
-  let Lambda = {a, b -> MytsrFunc3(a, b)}
+  let Lambda = {a, b -> MytsrFunc1(19, a, b)}
   let &thesaurusfunc = Lambda
   new | only
   call setline(1, 'seven')
-  let g:MytsrFunc3_args = []
+  let g:MytsrFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'seven']], g:MytsrFunc3_args)
+  call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:MytsrFunc1_args)
   bw!
 
   " Set 'thesaurusfunc' to a string(variable with a lambda expression)
-  let Lambda = {a, b -> MytsrFunc3(a, b)}
+  let Lambda = {a, b -> MytsrFunc1(20, a, b)}
   let &thesaurusfunc = string(Lambda)
   new | only
   call setline(1, 'seven')
-  let g:MytsrFunc3_args = []
+  let g:MytsrFunc1_args = []
   call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'seven']], g:MytsrFunc3_args)
+  call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:MytsrFunc1_args)
   bw!
 
   " Test for using a lambda function with incorrect return value
@@ -1864,116 +1842,112 @@ func Test_thesaurusfunc_callback()
   let &thesaurusfunc = {a -> 'abc'}
   call feedkeys("A\\\", 'x')
 
+  " Using Vim9 lambda expression in legacy context should fail
+  " set thesaurusfunc=(a,\ b)\ =>\ g:MytsrFunc1(21,\ a,\ b)
+  new | only
+  let g:MytsrFunc1_args = []
+  " call assert_fails('call feedkeys("A\\\", "x")', 'E117:')
+  call assert_equal([], g:MytsrFunc1_args)
+  bw!
+
+  " Use a buffer-local value and a global value
+  set thesaurusfunc&
+  setlocal thesaurusfunc=function('MytsrFunc1',[22])
+  call setline(1, 'sun')
+  let g:MytsrFunc1_args = []
+  call feedkeys("A\\\", "x")
+  call assert_equal('sun', getline(1))
+  call assert_equal([[22, 1, ''], [22, 0, 'sun']], g:MytsrFunc1_args)
+  new
+  call setline(1, 'sun')
+  let g:MytsrFunc1_args = []
+  call feedkeys("A\\\", "x")
+  call assert_equal('sun', getline(1))
+  call assert_equal([], g:MytsrFunc1_args)
+  set thesaurusfunc=function('MytsrFunc1',[23])
+  wincmd w
+  call setline(1, 'sun')
+  let g:MytsrFunc1_args = []
+  call feedkeys("A\\\", "x")
+  call assert_equal('sun', getline(1))
+  call assert_equal([[22, 1, ''], [22, 0, 'sun']], g:MytsrFunc1_args)
+  %bw!
+
+  " set 'thesaurusfunc' to a non-existing function
+  func MytsrFunc2(findstart, base)
+    call add(g:MytsrFunc2_args, [a:findstart, a:base])
+    return a:findstart ? 0 : ['sunday']
+  endfunc
+  set thesaurusfunc=MytsrFunc2
+  call setline(1, 'ten')
+  call assert_fails("set thesaurusfunc=function('NonExistingFunc')", 'E700:')
+  call assert_fails("let &thesaurusfunc = function('NonExistingFunc')", 'E700:')
+  let g:MytsrFunc2_args = []
+  call feedkeys("A\\\", 'x')
+  call assert_equal([[1, ''], [0, 'ten']], g:MytsrFunc2_args)
+  bw!
+
   " Vim9 tests
   let lines =<< trim END
     vim9script
 
     # Test for using function()
-    def MytsrFunc1(findstart: number, base: string): any
-      add(g:MytsrFunc1_args, [findstart, base])
+    def Vim9tsrFunc(val: number, findstart: number, base: string): any
+      add(g:Vim9tsrFunc_Args, [val, findstart, base])
       return findstart ? 0 : []
     enddef
-    set thesaurusfunc=function('MytsrFunc1')
+    set thesaurusfunc=function('Vim9tsrFunc',\ [60])
     new | only
     setline(1, 'one')
-    g:MytsrFunc1_args = []
+    g:Vim9tsrFunc_Args = []
     feedkeys("A\\\", 'x')
-    assert_equal([[1, ''], [0, 'one']], g:MytsrFunc1_args)
+    assert_equal([[60, 1, ''], [60, 0, 'one']], g:Vim9tsrFunc_Args)
     bw!
 
     # Test for using a lambda
-    def MytsrFunc2(findstart: number, base: string): any
-      add(g:MytsrFunc2_args, [findstart, base])
-      return findstart ? 0 : []
-    enddef
-    &thesaurusfunc = (a, b) => MytsrFunc2(a, b)
+    &thesaurusfunc = (a, b) => Vim9tsrFunc(61, a, b)
     new | only
     setline(1, 'two')
-    g:MytsrFunc2_args = []
+    g:Vim9tsrFunc_Args = []
     feedkeys("A\\\", 'x')
-    assert_equal([[1, ''], [0, 'two']], g:MytsrFunc2_args)
+    assert_equal([[61, 1, ''], [61, 0, 'two']], g:Vim9tsrFunc_Args)
     bw!
 
     # Test for using a string(lambda)
-    &thesaurusfunc = '(a, b) => MytsrFunc2(a, b)'
+    &thesaurusfunc = '(a, b) => Vim9tsrFunc(62, a, b)'
     new | only
     setline(1, 'two')
-    g:MytsrFunc2_args = []
+    g:Vim9tsrFunc_Args = []
     feedkeys("A\\\", 'x')
-    assert_equal([[1, ''], [0, 'two']], g:MytsrFunc2_args)
+    assert_equal([[62, 1, ''], [62, 0, 'two']], g:Vim9tsrFunc_Args)
     bw!
 
     # Test for using a variable with a lambda expression
-    var Fn: func = (a, b) => MytsrFunc2(a, b)
+    var Fn: func = (a, b) => Vim9tsrFunc(63, a, b)
     &thesaurusfunc = Fn
     new | only
     setline(1, 'three')
-    g:MytsrFunc2_args = []
+    g:Vim9tsrFunc_Args = []
     feedkeys("A\\\", 'x')
-    assert_equal([[1, ''], [0, 'three']], g:MytsrFunc2_args)
+    assert_equal([[63, 1, ''], [63, 0, 'three']], g:Vim9tsrFunc_Args)
     bw!
 
     # Test for using a string(variable with a lambda expression)
+    Fn = (a, b) => Vim9tsrFunc(64, a, b)
     &thesaurusfunc = string(Fn)
     new | only
     setline(1, 'three')
-    g:MytsrFunc2_args = []
+    g:Vim9tsrFunc_Args = []
     feedkeys("A\\\", 'x')
-    assert_equal([[1, ''], [0, 'three']], g:MytsrFunc2_args)
+    assert_equal([[64, 1, ''], [64, 0, 'three']], g:Vim9tsrFunc_Args)
     bw!
   END
   " call CheckScriptSuccess(lines)
 
-  " Using Vim9 lambda expression in legacy context should fail
-  " set thesaurusfunc=(a,\ b)\ =>\ g:MytsrFunc2(a,\ b)
-  " new | only
-  " let g:MytsrFunc2_args = []
-  " call assert_fails('call feedkeys("A\\\", "x")', 'E117:')
-  " call assert_equal([], g:MytsrFunc2_args)
-  " bw!
-
-  " Use a buffer-local value and a global value
-  func MytsrFunc4(findstart, base)
-    call add(g:MytsrFunc4_args, [a:findstart, a:base])
-    return a:findstart ? 0 : ['sunday']
-  endfunc
-  set thesaurusfunc&
-  setlocal thesaurusfunc=function('MytsrFunc4')
-  call setline(1, 'sun')
-  let g:MytsrFunc4_args = []
-  call feedkeys("A\\\", "x")
-  call assert_equal('sunday', getline(1))
-  call assert_equal([[1, ''], [0, 'sun']], g:MytsrFunc4_args)
-  new
-  call setline(1, 'sun')
-  let g:MytsrFunc4_args = []
-  call feedkeys("A\\\", "x")
-  call assert_equal('sun', getline(1))
-  call assert_equal([], g:MytsrFunc4_args)
-  set thesaurusfunc=function('MytsrFunc1')
-  wincmd w
-  call setline(1, 'sun')
-  let g:MytsrFunc4_args = []
-  call feedkeys("A\\\", "x")
-  call assert_equal('sunday', getline(1))
-  call assert_equal([[1, ''], [0, 'sun']], g:MytsrFunc4_args)
-  %bw!
-
-  " set 'thesaurusfunc' to a non-existing function
-  set thesaurusfunc=MytsrFunc2
-  call setline(1, 'ten')
-  call assert_fails("set thesaurusfunc=function('NonExistingFunc')", 'E700:')
-  call assert_fails("let &thesaurusfunc = function('NonExistingFunc')", 'E700:')
-  let g:MytsrFunc2_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'ten']], g:MytsrFunc2_args)
-
   " cleanup
   set thesaurusfunc&
   delfunc MytsrFunc1
   delfunc MytsrFunc2
-  delfunc MytsrFunc3
-  delfunc MytsrFunc4
   %bw!
 endfunc
 
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index 6160352052..b9cc858cdb 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -388,70 +388,6 @@ func Test_normal09a_operatorfunc()
   norm V10j,,
   call assert_equal(22, g:a)
 
-  " Use a lambda function for 'opfunc'
-  unmap  ,,
-  call cursor(1, 1)
-  let g:a=0
-  nmap  ,, :set opfunc={type\ ->\ CountSpaces(type)}g@
-  vmap  ,, :call CountSpaces(visualmode(), 1)
-  50
-  norm V2j,,
-  call assert_equal(6, g:a)
-  norm V,,
-  call assert_equal(2, g:a)
-  norm ,,l
-  call assert_equal(0, g:a)
-  50
-  exe "norm 0\10j2l,,"
-  call assert_equal(11, g:a)
-  50
-  norm V10j,,
-  call assert_equal(22, g:a)
-
-  " use a partial function for 'opfunc'
-  let g:OpVal = 0
-  func! Test_opfunc1(x, y, type)
-    let g:OpVal =  a:x + a:y
-  endfunc
-  set opfunc=function('Test_opfunc1',\ [5,\ 7])
-  normal! g@l
-  call assert_equal(12, g:OpVal)
-  " delete the function and try to use g@
-  delfunc Test_opfunc1
-  call test_garbagecollect_now()
-  call assert_fails('normal! g@l', 'E117:')
-  set opfunc=
-
-  " use a funcref for 'opfunc'
-  let g:OpVal = 0
-  func! Test_opfunc2(x, y, type)
-    let g:OpVal =  a:x + a:y
-  endfunc
-  set opfunc=funcref('Test_opfunc2',\ [4,\ 3])
-  normal! g@l
-  call assert_equal(7, g:OpVal)
-  " delete the function and try to use g@
-  delfunc Test_opfunc2
-  call test_garbagecollect_now()
-  call assert_fails('normal! g@l', 'E933:')
-  set opfunc=
-
-  " Try to use a function with two arguments for 'operatorfunc'
-  let g:OpVal = 0
-  func! Test_opfunc3(x, y)
-    let g:OpVal = 4
-  endfunc
-  set opfunc=Test_opfunc3
-  call assert_fails('normal! g@l', 'E119:')
-  call assert_equal(0, g:OpVal)
-  set opfunc=
-  delfunc Test_opfunc3
-  unlet g:OpVal
-
-  " Try to use a lambda function with two arguments for 'operatorfunc'
-  set opfunc={x,\ y\ ->\ 'done'}
-  call assert_fails('normal! g@l', 'E119:')
-
   " clean up
   unmap  ,,
   set opfunc=
@@ -520,6 +456,182 @@ func Test_normal09c_operatorfunc()
   set operatorfunc=
 endfunc
 
+" Test for different ways of setting the 'operatorfunc' option
+func Test_opfunc_callback()
+  new
+  func MyopFunc(val, type)
+    let g:OpFuncArgs = [a:val, a:type]
+  endfunc
+
+  " Test for using a function()
+  set opfunc=function('MyopFunc',\ [11])
+  let g:OpFuncArgs = []
+  normal! g@l
+  call assert_equal([11, 'char'], g:OpFuncArgs)
+
+  " Using a funcref variable to set 'operatorfunc'
+  let Fn = function('MyopFunc', [12])
+  let &opfunc = Fn
+  let g:OpFuncArgs = []
+  normal! g@l
+  call assert_equal([12, 'char'], g:OpFuncArgs)
+
+  " Using a string(funcref_variable) to set 'operatorfunc'
+  let Fn = function('MyopFunc', [13])
+  let &operatorfunc = string(Fn)
+  let g:OpFuncArgs = []
+  normal! g@l
+  call assert_equal([13, 'char'], g:OpFuncArgs)
+
+  " Test for using a funcref()
+  set operatorfunc=funcref('MyopFunc',\ [14])
+  let g:OpFuncArgs = []
+  normal! g@l
+  call assert_equal([14, 'char'], g:OpFuncArgs)
+
+  " Using a funcref variable to set 'operatorfunc'
+  let Fn = funcref('MyopFunc', [15])
+  let &opfunc = Fn
+  let g:OpFuncArgs = []
+  normal! g@l
+  call assert_equal([15, 'char'], g:OpFuncArgs)
+
+  " Using a string(funcref_variable) to set 'operatorfunc'
+  let Fn = funcref('MyopFunc', [16])
+  let &opfunc = string(Fn)
+  let g:OpFuncArgs = []
+  normal! g@l
+  call assert_equal([16, 'char'], g:OpFuncArgs)
+
+  " Test for using a lambda function using set
+  set opfunc={a\ ->\ MyopFunc(17,\ a)}
+  let g:OpFuncArgs = []
+  normal! g@l
+  call assert_equal([17, 'char'], g:OpFuncArgs)
+
+  " Test for using a lambda function using let
+  let &opfunc = {a -> MyopFunc(18, a)}
+  let g:OpFuncArgs = []
+  normal! g@l
+  call assert_equal([18, 'char'], g:OpFuncArgs)
+
+  " Set 'operatorfunc' to a string(lambda expression)
+  let &opfunc = '{a -> MyopFunc(19, a)}'
+  let g:OpFuncArgs = []
+  normal! g@l
+  call assert_equal([19, 'char'], g:OpFuncArgs)
+
+  " Set 'operatorfunc' to a variable with a lambda expression
+  let Lambda = {a -> MyopFunc(20, a)}
+  let &opfunc = Lambda
+  let g:OpFuncArgs = []
+  normal! g@l
+  call assert_equal([20, 'char'], g:OpFuncArgs)
+
+  " Set 'operatorfunc' to a string(variable with a lambda expression)
+  let Lambda = {a -> MyopFunc(21, a)}
+  let &opfunc = string(Lambda)
+  let g:OpFuncArgs = []
+  normal! g@l
+  call assert_equal([21, 'char'], g:OpFuncArgs)
+
+  " Try to use 'operatorfunc' after the function is deleted
+  func TmpOpFunc(type)
+    let g:OpFuncArgs = [22, a:type]
+  endfunc
+  let &opfunc = function('TmpOpFunc')
+  delfunc TmpOpFunc
+  call test_garbagecollect_now()
+  let g:OpFuncArgs = []
+  call assert_fails('normal! g@l', 'E117:')
+  call assert_equal([], g:OpFuncArgs)
+
+  " Try to use a function with two arguments for 'operatorfunc'
+  func! MyopFunc2(x, y)
+    let g:OpFuncArgs = [a:x, a:y]
+  endfunc
+  set opfunc=MyopFunc2
+  let g:OpFuncArgs = []
+  call assert_fails('normal! g@l', 'E119:')
+  call assert_equal([], g:OpFuncArgs)
+
+  " Try to use a lambda function with two arguments for 'operatorfunc'
+  let &opfunc = {a, b -> MyopFunc(23, b)}
+  let g:OpFuncArgs = []
+  call assert_fails('normal! g@l', 'E119:')
+  call assert_equal([], g:OpFuncArgs)
+
+  " Test for clearing the 'operatorfunc' option
+  set opfunc=''
+  set opfunc&
+
+  call assert_fails("set opfunc=function('abc')", "E700:")
+  call assert_fails("set opfunc=funcref('abc')", "E700:")
+
+  " Using Vim9 lambda expression in legacy context should fail
+  " set opfunc=(a)\ =>\ MyopFunc(24,\ a)
+  let g:OpFuncArgs = []
+  " call assert_fails('normal! g@l', 'E117:')
+  call assert_equal([], g:OpFuncArgs)
+
+  " set 'operatorfunc' to a non-existing function
+  let &opfunc = function('MyopFunc', [25])
+  call assert_fails("set opfunc=function('NonExistingFunc')", 'E700:')
+  call assert_fails("let &opfunc = function('NonExistingFunc')", 'E700:')
+  let g:OpFuncArgs = []
+  normal! g@l
+  call assert_equal([25, 'char'], g:OpFuncArgs)
+
+  " Vim9 tests
+  let lines =<< trim END
+    vim9script
+
+    # Test for using function()
+    def g:Vim9opFunc(val: number, type: string): void
+      g:OpFuncArgs = [val, type]
+    enddef
+    set opfunc=function('g:Vim9opFunc',\ [60])
+    g:OpFuncArgs = []
+    normal! g@l
+    assert_equal([60, 'char'], g:OpFuncArgs)
+
+    # Test for using a lambda
+    &opfunc = (a) => Vim9opFunc(61, a)
+    g:OpFuncArgs = []
+    normal! g@l
+    assert_equal([61, 'char'], g:OpFuncArgs)
+
+    # Test for using a string(lambda)
+    &opfunc = '(a) => Vim9opFunc(62, a)'
+    g:OpFuncArgs = []
+    normal! g@l
+    assert_equal([62, 'char'], g:OpFuncArgs)
+
+    # Test for using a variable with a lambda expression
+    var Fn: func = (a) => Vim9opFunc(63, a)
+    &opfunc = Fn
+    g:OpFuncArgs = []
+    normal! g@l
+    assert_equal([63, 'char'], g:OpFuncArgs)
+
+    # Test for using a string(variable with a lambda expression)
+    Fn = (a) => Vim9opFunc(64, a)
+    &opfunc = string(Fn)
+    g:OpFuncArgs = []
+    normal! g@l
+    assert_equal([64, 'char'], g:OpFuncArgs)
+    bw!
+  END
+  " call CheckScriptSuccess(lines)
+
+  " cleanup
+  set opfunc&
+  delfunc MyopFunc
+  delfunc MyopFunc2
+  unlet g:OpFuncArgs
+  %bw!
+endfunc
+
 func Test_normal10_expand()
   " Test for expand()
   10new
diff --git a/src/nvim/testdir/test_tagfunc.vim b/src/nvim/testdir/test_tagfunc.vim
index 88500db269..79b1110b43 100644
--- a/src/nvim/testdir/test_tagfunc.vim
+++ b/src/nvim/testdir/test_tagfunc.vim
@@ -125,59 +125,60 @@ endfunc
 
 " Test for different ways of setting the 'tagfunc' option
 func Test_tagfunc_callback()
-  " Test for using a function()
-  func MytagFunc1(pat, flags, info)
-    let g:MytagFunc1_args = [a:pat, a:flags, a:info]
+  func MytagFunc1(val, pat, flags, info)
+    let g:MytagFunc1_args = [a:val, a:pat, a:flags, a:info]
     return v:null
   endfunc
-  set tagfunc=function('MytagFunc1')
+
+  " Test for using a function()
+  set tagfunc=function('MytagFunc1',[10])
   new | only
   let g:MytagFunc1_args = []
   call assert_fails('tag a11', 'E433:')
-  call assert_equal(['a11', '', {}], g:MytagFunc1_args)
+  call assert_equal([10, 'a11', '', {}], g:MytagFunc1_args)
 
   " Using a funcref variable to set 'tagfunc'
-  let Fn = function('MytagFunc1')
+  let Fn = function('MytagFunc1', [11])
   let &tagfunc = Fn
   new | only
   let g:MytagFunc1_args = []
   call assert_fails('tag a12', 'E433:')
-  call assert_equal(['a12', '', {}], g:MytagFunc1_args)
+  call assert_equal([11, 'a12', '', {}], g:MytagFunc1_args)
 
   " Using a string(funcref_variable) to set 'tagfunc'
-  let Fn = function('MytagFunc1')
+  let Fn = function('MytagFunc1', [12])
   let &tagfunc = string(Fn)
   new | only
   let g:MytagFunc1_args = []
   call assert_fails('tag a12', 'E433:')
-  call assert_equal(['a12', '', {}], g:MytagFunc1_args)
+  call assert_equal([12, 'a12', '', {}], g:MytagFunc1_args)
 
   " Test for using a funcref()
   func MytagFunc2(pat, flags, info)
     let g:MytagFunc2_args = [a:pat, a:flags, a:info]
     return v:null
   endfunc
-  set tagfunc=funcref('MytagFunc2')
+  set tagfunc=funcref('MytagFunc1',[13])
   new | only
-  let g:MytagFunc2_args = []
+  let g:MytagFunc1_args = []
   call assert_fails('tag a13', 'E433:')
-  call assert_equal(['a13', '', {}], g:MytagFunc2_args)
+  call assert_equal([13, 'a13', '', {}], g:MytagFunc1_args)
 
   " Using a funcref variable to set 'tagfunc'
-  let Fn = funcref('MytagFunc2')
+  let Fn = funcref('MytagFunc1', [14])
   let &tagfunc = Fn
   new | only
-  let g:MytagFunc2_args = []
+  let g:MytagFunc1_args = []
   call assert_fails('tag a14', 'E433:')
-  call assert_equal(['a14', '', {}], g:MytagFunc2_args)
+  call assert_equal([14, 'a14', '', {}], g:MytagFunc1_args)
 
   " Using a string(funcref_variable) to set 'tagfunc'
-  let Fn = funcref('MytagFunc2')
+  let Fn = funcref('MytagFunc1', [15])
   let &tagfunc = string(Fn)
   new | only
-  let g:MytagFunc2_args = []
+  let g:MytagFunc1_args = []
   call assert_fails('tag a14', 'E433:')
-  call assert_equal(['a14', '', {}], g:MytagFunc2_args)
+  call assert_equal([15, 'a14', '', {}], g:MytagFunc1_args)
 
   " Test for using a script local function
   set tagfunc=ScriptLocalTagFunc
@@ -203,45 +204,41 @@ func Test_tagfunc_callback()
   call assert_equal(['a16', '', {}], g:ScriptLocalFuncArgs)
 
   " Test for using a lambda function
-  func MytagFunc3(pat, flags, info)
-    let g:MytagFunc3_args = [a:pat, a:flags, a:info]
-    return v:null
-  endfunc
-  set tagfunc={a,\ b,\ c\ ->\ MytagFunc3(a,\ b,\ c)}
+  set tagfunc={a,\ b,\ c\ ->\ MytagFunc1(16,\ a,\ b,\ c)}
   new | only
-  let g:MytagFunc3_args = []
+  let g:MytagFunc1_args = []
   call assert_fails('tag a17', 'E433:')
-  call assert_equal(['a17', '', {}], g:MytagFunc3_args)
+  call assert_equal([16, 'a17', '', {}], g:MytagFunc1_args)
 
   " Set 'tagfunc' to a lambda expression
-  let &tagfunc = {a, b, c -> MytagFunc3(a, b, c)}
+  let &tagfunc = {a, b, c -> MytagFunc1(17, a, b, c)}
   new | only
-  let g:MytagFunc3_args = []
+  let g:MytagFunc1_args = []
   call assert_fails('tag a18', 'E433:')
-  call assert_equal(['a18', '', {}], g:MytagFunc3_args)
+  call assert_equal([17, 'a18', '', {}], g:MytagFunc1_args)
 
   " Set 'tagfunc' to a string(lambda expression)
-  let &tagfunc = '{a, b, c -> MytagFunc3(a, b, c)}'
+  let &tagfunc = '{a, b, c -> MytagFunc1(18, a, b, c)}'
   new | only
-  let g:MytagFunc3_args = []
+  let g:MytagFunc1_args = []
   call assert_fails('tag a18', 'E433:')
-  call assert_equal(['a18', '', {}], g:MytagFunc3_args)
+  call assert_equal([18, 'a18', '', {}], g:MytagFunc1_args)
 
   " Set 'tagfunc' to a variable with a lambda expression
-  let Lambda = {a, b, c -> MytagFunc3(a, b, c)}
+  let Lambda = {a, b, c -> MytagFunc1(19, a, b, c)}
   let &tagfunc = Lambda
   new | only
-  let g:MytagFunc3_args = []
+  let g:MytagFunc1_args = []
   call assert_fails("tag a19", "E433:")
-  call assert_equal(['a19', '', {}], g:MytagFunc3_args)
+  call assert_equal([19, 'a19', '', {}], g:MytagFunc1_args)
 
   " Set 'tagfunc' to a string(variable with a lambda expression)
-  let Lambda = {a, b, c -> MytagFunc3(a, b, c)}
+  let Lambda = {a, b, c -> MytagFunc1(20, a, b, c)}
   let &tagfunc = string(Lambda)
   new | only
-  let g:MytagFunc3_args = []
+  let g:MytagFunc1_args = []
   call assert_fails("tag a19", "E433:")
-  call assert_equal(['a19', '', {}], g:MytagFunc3_args)
+  call assert_equal([20, 'a19', '', {}], g:MytagFunc1_args)
 
   " Test for using a lambda function with incorrect return value
   let Lambda = {s -> strlen(s)}
@@ -258,72 +255,69 @@ func Test_tagfunc_callback()
   let &tagfunc = "{a -> 'abc'}"
   call assert_fails("echo taglist('a')", "E987:")
 
+  " Using Vim9 lambda expression in legacy context should fail
+  " set tagfunc=(a,\ b,\ c)\ =>\ g:MytagFunc1(21,\ a,\ b,\ c)
+  new | only
+  let g:MytagFunc1_args = []
+  " call assert_fails("tag a17", "E117:")
+  call assert_equal([], g:MytagFunc1_args)
+
+  " set 'tagfunc' to a non-existing function
+  call assert_fails("set tagfunc=function('NonExistingFunc')", 'E700:')
+  call assert_fails("let &tagfunc = function('NonExistingFunc')", 'E700:')
+  call assert_fails("tag axb123", 'E426:')
+  bw!
+
   " Vim9 tests
   let lines =<< trim END
     vim9script
 
     # Test for using function()
-    def MytagFunc1(pat: string, flags: string, info: dict): any
-      g:MytagFunc1_args = [pat, flags, info]
+    def Vim9tagFunc(val: number, pat: string, flags: string, info: dict): any
+      g:Vim9tagFuncArgs = [val, pat, flags, info]
       return null
     enddef
-    set tagfunc=function('MytagFunc1')
+    set tagfunc=function('Vim9tagFunc',\ [60])
     new | only
-    g:MytagFunc1_args = []
+    g:Vim9tagFuncArgs = []
     assert_fails('tag a10', 'E433:')
-    assert_equal(['a10', '', {}], g:MytagFunc1_args)
+    assert_equal([60, 'a10', '', {}], g:Vim9tagFuncArgs)
 
     # Test for using a lambda
-    def MytagFunc2(pat: string, flags: string, info: dict): any
-      g:MytagFunc2_args = [pat, flags, info]
-      return null
-    enddef
-    &tagfunc = (a, b, c) => MytagFunc2(a, b, c)
+    &tagfunc = (a, b, c) => MytagFunc1(61, a, b, c)
     new | only
-    g:MytagFunc2_args = []
+    g:MytagFunc1_args = []
     assert_fails('tag a20', 'E433:')
-    assert_equal(['a20', '', {}], g:MytagFunc2_args)
+    assert_equal([61, 'a20', '', {}], g:MytagFunc1_args)
 
     # Test for using a string(lambda)
-    &tagfunc = '(a, b, c) => MytagFunc2(a, b, c)'
+    &tagfunc = '(a, b, c) => MytagFunc1(62, a, b, c)'
     new | only
-    g:MytagFunc2_args = []
+    g:MytagFunc1_args = []
     assert_fails('tag a20', 'E433:')
-    assert_equal(['a20', '', {}], g:MytagFunc2_args)
+    assert_equal([62, 'a20', '', {}], g:MytagFunc1_args)
 
     # Test for using a variable with a lambda expression
-    var Fn: func = (a, b, c) => MytagFunc2(a, b, c)
+    var Fn: func = (a, b, c) => MytagFunc1(63, a, b, c)
     &tagfunc = Fn
     new | only
-    g:MytagFunc2_args = []
+    g:MytagFunc1_args = []
     assert_fails('tag a30', 'E433:')
-    assert_equal(['a30', '', {}], g:MytagFunc2_args)
+    assert_equal([63, 'a30', '', {}], g:MytagFunc1_args)
 
     # Test for using a variable with a lambda expression
+    Fn = (a, b, c) => MytagFunc1(64, a, b, c)
     &tagfunc = string(Fn)
     new | only
-    g:MytagFunc2_args = []
+    g:MytagFunc1_args = []
     assert_fails('tag a30', 'E433:')
-    assert_equal(['a30', '', {}], g:MytagFunc2_args)
+    assert_equal([64, 'a30', '', {}], g:MytagFunc1_args)
   END
   " call CheckScriptSuccess(lines)
 
-  " Using Vim9 lambda expression in legacy context should fail
-  " set tagfunc=(a,\ b,\ c)\ =>\ g:MytagFunc2(a,\ b,\ c)
-  " new | only
-  " let g:MytagFunc3_args = []
-  " call assert_fails("tag a17", "E117:")
-  " call assert_equal([], g:MytagFunc3_args)
-
-  " set 'tagfunc' to a non-existing function
-  call assert_fails("set tagfunc=function('NonExistingFunc')", 'E700:')
-  call assert_fails("let &tagfunc = function('NonExistingFunc')", 'E700:')
-  call assert_fails("tag axb123", 'E426:')
-
   " cleanup
   delfunc MytagFunc1
   delfunc MytagFunc2
-  delfunc MytagFunc3
   set tagfunc&
   %bw!
 endfunc
-- 
cgit 


From c00d241981f292a6529242bb98ed16cfc8c53ae4 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 7 Nov 2022 13:37:22 +0800
Subject: vim-patch:8.2.3788: lambda for option that is a function may be freed

Problem:    Lambda for option that is a function may be garbage collected.
Solution:   Set a reference in the funcref. (Yegappan Lakshmanan,
            closes vim/vim#9330)

https://github.com/vim/vim/commit/6ae8fae8696623b527c7fb22567f6a3705b2f0dd

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/eval.c                        |  23 +-
 src/nvim/insexpand.c                   |  11 +
 src/nvim/ops.c                         |   7 +
 src/nvim/quickfix.c                    |  13 +-
 src/nvim/tag.c                         |   7 +
 src/nvim/testdir/test_ins_complete.vim | 960 ++++++++++++++++-----------------
 src/nvim/testdir/test_normal.vim       | 249 +++++----
 src/nvim/testdir/test_quickfix.vim     | 126 +++++
 src/nvim/testdir/test_tagfunc.vim      | 266 +++++----
 src/nvim/testdir/vim9.vim              |  80 +++
 10 files changed, 978 insertions(+), 764 deletions(-)
 create mode 100644 src/nvim/testdir/vim9.vim

(limited to 'src/nvim')

diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index daae6416dc..7c6f81df40 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -30,6 +30,7 @@
 #include "nvim/ex_session.h"
 #include "nvim/getchar.h"
 #include "nvim/highlight_group.h"
+#include "nvim/insexpand.h"
 #include "nvim/locale.h"
 #include "nvim/lua/executor.h"
 #include "nvim/mark.h"
@@ -49,6 +50,7 @@
 #include "nvim/search.h"
 #include "nvim/sign.h"
 #include "nvim/syntax.h"
+#include "nvim/tag.h"
 #include "nvim/ui.h"
 #include "nvim/ui_compositor.h"
 #include "nvim/undo.h"
@@ -4168,10 +4170,23 @@ bool garbage_collect(bool testing)
     ABORTING(set_ref_dict)(buf->additional_data, copyID);
 
     // buffer callback functions
-    set_ref_in_callback(&buf->b_prompt_callback, copyID, NULL, NULL);
-    set_ref_in_callback(&buf->b_prompt_interrupt, copyID, NULL, NULL);
+    ABORTING(set_ref_in_callback)(&buf->b_prompt_callback, copyID, NULL, NULL);
+    ABORTING(set_ref_in_callback)(&buf->b_prompt_interrupt, copyID, NULL, NULL);
+    ABORTING(set_ref_in_callback)(&buf->b_cfu_cb, copyID, NULL, NULL);
+    ABORTING(set_ref_in_callback)(&buf->b_ofu_cb, copyID, NULL, NULL);
+    ABORTING(set_ref_in_callback)(&buf->b_tsrfu_cb, copyID, NULL, NULL);
+    ABORTING(set_ref_in_callback)(&buf->b_tfu_cb, copyID, NULL, NULL);
   }
 
+  // 'completefunc', 'omnifunc' and 'thesaurusfunc' callbacks
+  ABORTING(set_ref_in_insexpand_funcs)(copyID);
+
+  // 'operatorfunc' callback
+  ABORTING(set_ref_in_opfunc)(copyID);
+
+  // 'tagfunc' callback
+  ABORTING(set_ref_in_tagfunc)(copyID);
+
   FOR_ALL_TAB_WINDOWS(tp, wp) {
     // window-local variables
     ABORTING(set_ref_in_item)(&wp->w_winvar.di_tv, copyID, NULL, NULL);
@@ -5910,8 +5925,8 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co
   return call_func(name, -1, rettv, argcount_in, argvars_in, &funcexe);
 }
 
-static bool set_ref_in_callback(Callback *callback, int copyID, ht_stack_T **ht_stack,
-                                list_stack_T **list_stack)
+bool set_ref_in_callback(Callback *callback, int copyID, ht_stack_T **ht_stack,
+                         list_stack_T **list_stack)
 {
   typval_T tv;
   switch (callback->type) {
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index 7d4e77b263..78d820c2a7 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -2299,6 +2299,17 @@ int set_thesaurusfunc_option(void)
   return retval;
 }
 
+/// Mark the global 'completefunc' 'omnifunc' and 'thesaurusfunc' callbacks with
+/// "copyID" so that they are not garbage collected.
+bool set_ref_in_insexpand_funcs(int copyID)
+{
+  bool abort = set_ref_in_callback(&cfu_cb, copyID, NULL, NULL);
+  abort = abort || set_ref_in_callback(&ofu_cb, copyID, NULL, NULL);
+  abort = abort || set_ref_in_callback(&tsrfu_cb, copyID, NULL, NULL);
+
+  return abort;
+}
+
 /// Get the user-defined completion function name for completion "type"
 static char_u *get_complete_funcname(int type)
 {
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 2d53918ded..65fc42bc08 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -5606,6 +5606,13 @@ void free_operatorfunc_option(void)
 }
 #endif
 
+/// Mark the global 'operatorfunc' callback with "copyID" so that it is not
+/// garbage collected.
+bool set_ref_in_opfunc(int copyID)
+{
+  return set_ref_in_callback(&opfunc_cb, copyID, NULL, NULL);
+}
+
 /// Handle the "g@" operator: call 'operatorfunc'.
 static void op_function(const oparg_T *oap)
   FUNC_ATTR_NONNULL_ALL
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 5d101ee415..7ecb4e4956 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -6686,7 +6686,8 @@ int set_errorlist(win_T *wp, list_T *list, int action, char *title, dict_T *what
   return retval;
 }
 
-/// Mark the context as in use for all the lists in a quickfix stack.
+/// Mark the quickfix context and callback function as in use for all the lists
+/// in a quickfix stack.
 static bool mark_quickfix_ctx(qf_info_T *qi, int copyID)
 {
   bool abort = false;
@@ -6695,8 +6696,11 @@ static bool mark_quickfix_ctx(qf_info_T *qi, int copyID)
     typval_T *ctx = qi->qf_lists[i].qf_ctx;
     if (ctx != NULL && ctx->v_type != VAR_NUMBER
         && ctx->v_type != VAR_STRING && ctx->v_type != VAR_FLOAT) {
-      abort = set_ref_in_item(ctx, copyID, NULL, NULL);
+      abort = abort || set_ref_in_item(ctx, copyID, NULL, NULL);
     }
+
+    Callback *cb = &qi->qf_lists[i].qf_qftf_cb;
+    abort = abort || set_ref_in_callback(cb, copyID, NULL, NULL);
   }
 
   return abort;
@@ -6711,6 +6715,11 @@ bool set_ref_in_quickfix(int copyID)
     return abort;
   }
 
+  abort = set_ref_in_callback(&qftf_cb, copyID, NULL, NULL);
+  if (abort) {
+    return abort;
+  }
+
   FOR_ALL_TAB_WINDOWS(tp, win) {
     if (win->w_llist != NULL) {
       abort = mark_quickfix_ctx(win->w_llist, copyID);
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index d57bbead63..9ea0b71dc1 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -149,6 +149,13 @@ void free_tagfunc_option(void)
 }
 #endif
 
+/// Mark the global 'tagfunc' callback with "copyID" so that it is not garbage
+/// collected.
+bool set_ref_in_tagfunc(int copyID)
+{
+  return set_ref_in_callback(&tfu_cb, copyID, NULL, NULL);
+}
+
 /// Copy the global 'tagfunc' callback function to the buffer-local 'tagfunc'
 /// callback for 'buf'.
 void set_buflocal_tfu_callback(buf_T *buf)
diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim
index eaac66dae0..db2bb2e2a0 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -1,5 +1,8 @@
+" Test for insert completion
+
 source screendump.vim
 source check.vim
+source vim9.vim
 
 " Test for insert expansion
 func Test_ins_complete()
@@ -1292,154 +1295,179 @@ func Test_completefunc_callback()
     return a:findstart ? 0 : []
   endfunc
 
-  " Test for using a function()
-  set completefunc=function('MycompleteFunc1',[10])
-  new | only
-  call setline(1, 'one')
-  let g:MycompleteFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[10, 1, ''], [10, 0, 'one']], g:MycompleteFunc1_args)
-  bw!
+  let lines =<< trim END
+    #" Test for using a function()
+    set completefunc=function('g:MycompleteFunc1',\ [10])
+    new | only
+    call setline(1, 'one')
+    LET g:MycompleteFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[10, 1, ''], [10, 0, 'one']], g:MycompleteFunc1_args)
+    bw!
 
-  " Using a funcref variable to set 'completefunc'
-  let Fn = function('MycompleteFunc1', [11])
-  let &completefunc = Fn
-  new | only
-  call setline(1, 'two')
-  let g:MycompleteFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[11, 1, ''], [11, 0, 'two']], g:MycompleteFunc1_args)
-  bw!
+    #" Using a funcref variable to set 'completefunc'
+    VAR Fn = function('g:MycompleteFunc1', [11])
+    LET &completefunc = Fn
+    new | only
+    call setline(1, 'two')
+    LET g:MycompleteFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[11, 1, ''], [11, 0, 'two']], g:MycompleteFunc1_args)
+    bw!
 
-  " Using string(funcref_variable) to set 'completefunc'
-  let Fn = function('MycompleteFunc1', [12])
-  let &completefunc = string(Fn)
-  new | only
-  call setline(1, 'two')
-  let g:MycompleteFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[12, 1, ''], [12, 0, 'two']], g:MycompleteFunc1_args)
-  bw!
+    #" Using string(funcref_variable) to set 'completefunc'
+    LET Fn = function('g:MycompleteFunc1', [12])
+    LET &completefunc = string(Fn)
+    new | only
+    call setline(1, 'two')
+    LET g:MycompleteFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[12, 1, ''], [12, 0, 'two']], g:MycompleteFunc1_args)
+    bw!
 
-  " Test for using a funcref()
-  set completefunc=funcref('MycompleteFunc1',\ [13])
-  new | only
-  call setline(1, 'three')
-  let g:MycompleteFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[13, 1, ''], [13, 0, 'three']], g:MycompleteFunc1_args)
-  bw!
+    #" Test for using a funcref()
+    set completefunc=funcref('g:MycompleteFunc1',\ [13])
+    new | only
+    call setline(1, 'three')
+    LET g:MycompleteFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[13, 1, ''], [13, 0, 'three']], g:MycompleteFunc1_args)
+    bw!
 
-  " Using a funcref variable to set 'completefunc'
-  let Fn = funcref('MycompleteFunc1', [14])
-  let &completefunc = Fn
-  new | only
-  call setline(1, 'four')
-  let g:MycompleteFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[14, 1, ''], [14, 0, 'four']], g:MycompleteFunc1_args)
-  bw!
+    #" Using a funcref variable to set 'completefunc'
+    LET Fn = funcref('g:MycompleteFunc1', [14])
+    LET &completefunc = Fn
+    new | only
+    call setline(1, 'four')
+    LET g:MycompleteFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[14, 1, ''], [14, 0, 'four']], g:MycompleteFunc1_args)
+    bw!
 
-  " Using a string(funcref_variable) to set 'completefunc'
-  let Fn = funcref('MycompleteFunc1', [15])
-  let &completefunc = string(Fn)
-  new | only
-  call setline(1, 'four')
-  let g:MycompleteFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[15, 1, ''], [15, 0, 'four']], g:MycompleteFunc1_args)
-  bw!
+    #" Using a string(funcref_variable) to set 'completefunc'
+    LET Fn = funcref('g:MycompleteFunc1', [15])
+    LET &completefunc = string(Fn)
+    new | only
+    call setline(1, 'four')
+    LET g:MycompleteFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[15, 1, ''], [15, 0, 'four']], g:MycompleteFunc1_args)
+    bw!
 
-  " Test for using a lambda function
-  set completefunc={a,\ b\ ->\ MycompleteFunc1(16,\ a,\ b)}
-  new | only
-  call setline(1, 'five')
-  let g:MycompleteFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[16, 1, ''], [16, 0, 'five']], g:MycompleteFunc1_args)
-  bw!
+    #" Test for using a lambda function with set
+    VAR optval = "LSTART a, b LMIDDLE MycompleteFunc1(16, a, b) LEND"
+    LET optval = substitute(optval, ' ', '\\ ', 'g')
+    exe "set completefunc=" .. optval
+    new | only
+    call setline(1, 'five')
+    LET g:MycompleteFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[16, 1, ''], [16, 0, 'five']], g:MycompleteFunc1_args)
+    bw!
 
-  " Set 'completefunc' to a lambda expression
-  let &completefunc = {a, b -> MycompleteFunc1(17, a, b)}
-  new | only
-  call setline(1, 'six')
-  let g:MycompleteFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[17, 1, ''], [17, 0, 'six']], g:MycompleteFunc1_args)
-  bw!
+    #" Set 'completefunc' to a lambda expression
+    LET &completefunc = LSTART a, b LMIDDLE MycompleteFunc1(17, a, b) LEND
+    new | only
+    call setline(1, 'six')
+    LET g:MycompleteFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[17, 1, ''], [17, 0, 'six']], g:MycompleteFunc1_args)
+    bw!
 
-  " Set 'completefunc' to string(lambda_expression)
-  let &completefunc = '{a, b -> MycompleteFunc1(18, a, b)}'
-  new | only
-  call setline(1, 'six')
-  let g:MycompleteFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[18, 1, ''], [18, 0, 'six']], g:MycompleteFunc1_args)
-  bw!
+    #" Set 'completefunc' to string(lambda_expression)
+    LET &completefunc = 'LSTART a, b LMIDDLE MycompleteFunc1(18, a, b) LEND'
+    new | only
+    call setline(1, 'six')
+    LET g:MycompleteFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[18, 1, ''], [18, 0, 'six']], g:MycompleteFunc1_args)
+    bw!
 
-  " Set 'completefunc' to a variable with a lambda expression
-  let Lambda = {a, b -> MycompleteFunc1(19, a, b)}
-  let &completefunc = Lambda
-  new | only
-  call setline(1, 'seven')
-  let g:MycompleteFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:MycompleteFunc1_args)
-  bw!
+    #" Set 'completefunc' to a variable with a lambda expression
+    VAR Lambda = LSTART a, b LMIDDLE MycompleteFunc1(19, a, b) LEND
+    LET &completefunc = Lambda
+    new | only
+    call setline(1, 'seven')
+    LET g:MycompleteFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:MycompleteFunc1_args)
+    bw!
 
-  " Set 'completefunc' to a string(variable with a lambda expression)
-  let Lambda = {a, b -> MycompleteFunc1(20, a, b)}
-  let &completefunc = string(Lambda)
-  new | only
-  call setline(1, 'seven')
-  let g:MycompleteFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:MycompleteFunc1_args)
-  bw!
+    #" Set 'completefunc' to a string(variable with a lambda expression)
+    LET Lambda = LSTART a, b LMIDDLE MycompleteFunc1(20, a, b) LEND
+    LET &completefunc = string(Lambda)
+    new | only
+    call setline(1, 'seven')
+    LET g:MycompleteFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:MycompleteFunc1_args)
+    bw!
 
-  " Test for using a lambda function with incorrect return value
-  let Lambda = {s -> strlen(s)}
-  let &completefunc = Lambda
-  new | only
-  call setline(1, 'eight')
-  call feedkeys("A\\\", 'x')
-  bw!
+    #" Test for using a lambda function with incorrect return value
+    LET Lambda = LSTART a, b LMIDDLE strlen(a) LEND
+    LET &completefunc = Lambda
+    new | only
+    call setline(1, 'eight')
+    call feedkeys("A\\\", 'x')
+    bw!
 
-  " Test for clearing the 'completefunc' option
-  set completefunc=''
-  set completefunc&
+    #" Test for clearing the 'completefunc' option
+    set completefunc=''
+    set completefunc&
+    call assert_fails("set completefunc=function('abc')", "E700:")
+    call assert_fails("set completefunc=funcref('abc')", "E700:")
+
+    #" set 'completefunc' to a non-existing function
+    func MycompleteFunc2(findstart, base)
+      call add(g:MycompleteFunc2_args, [a:findstart, a:base])
+      return a:findstart ? 0 : []
+    endfunc
+    set completefunc=MycompleteFunc2
+    call setline(1, 'five')
+    call assert_fails("set completefunc=function('NonExistingFunc')", 'E700:')
+    call assert_fails("LET &completefunc = function('NonExistingFunc')", 'E700:')
+    LET g:MycompleteFunc2_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[1, ''], [0, 'five']], g:MycompleteFunc2_args)
+    bw!
+  END
+  call CheckLegacyAndVim9Success(lines)
 
-  call assert_fails("set completefunc=function('abc')", "E700:")
-  call assert_fails("set completefunc=funcref('abc')", "E700:")
   let &completefunc = {a -> 'abc'}
   call feedkeys("A\\\", 'x')
 
   " Using Vim9 lambda expression in legacy context should fail
-  " set completefunc=(a,\ b)\ =>\ g:MycompleteFunc1(21,\ a,\ b)
+  " set completefunc=(a,\ b)\ =>\ MycompleteFunc1(21,\ a,\ b)
   new | only
   let g:MycompleteFunc1_args = []
   " call assert_fails('call feedkeys("A\\\", "x")', 'E117:')
   call assert_equal([], g:MycompleteFunc1_args)
 
-  " set 'completefunc' to a non-existing function
-  func MycompleteFunc2(findstart, base)
-    call add(g:MycompleteFunc2_args, [a:findstart, a:base])
-    return a:findstart ? 0 : []
+  " set 'completefunc' to a partial with dict. This used to cause a crash.
+  func SetCompleteFunc()
+    let params = {'complete': function('g:DictCompleteFunc')}
+    let &completefunc = params.complete
   endfunc
-  set completefunc=MycompleteFunc2
-  call setline(1, 'five')
-  call assert_fails("set completefunc=function('NonExistingFunc')", 'E700:')
-  call assert_fails("let &completefunc = function('NonExistingFunc')", 'E700:')
-  let g:MycompleteFunc2_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'five']], g:MycompleteFunc2_args)
-  bw!
+  func g:DictCompleteFunc(_) dict
+  endfunc
+  call SetCompleteFunc()
+  new
+  call SetCompleteFunc()
+  bw
+  call test_garbagecollect_now()
+  new
+  set completefunc=
+  wincmd w
+  set completefunc=
+  %bw!
+  delfunc g:DictCompleteFunc
+  delfunc SetCompleteFunc
 
   " Vim9 tests
   let lines =<< trim END
     vim9script
 
-    # Test for using function()
+    # Test for using a def function with completefunc
     def Vim9CompleteFunc(val: number, findstart: number, base: string): any
       add(g:Vim9completeFuncArgs, [val, findstart, base])
       return findstart ? 0 : []
@@ -1451,44 +1479,6 @@ func Test_completefunc_callback()
     feedkeys("A\\\", 'x')
     assert_equal([[60, 1, ''], [60, 0, 'one']], g:Vim9completeFuncArgs)
     bw!
-
-    # Test for using a lambda
-    &completefunc = (a, b) => Vim9CompleteFunc(61, a, b)
-    new | only
-    setline(1, 'two')
-    g:Vim9completeFuncArgs = []
-    feedkeys("A\\\", 'x')
-    assert_equal([[61, 1, ''], [61, 0, 'two']], g:Vim9completeFuncArgs)
-    bw!
-
-    # Test for using a string(lambda)
-    &completefunc = '(a, b) => Vim9CompleteFunc(62, a, b)'
-    new | only
-    setline(1, 'two')
-    g:Vim9completeFuncArgs = []
-    feedkeys("A\\\", 'x')
-    assert_equal([[62, 1, ''], [62, 0, 'two']], g:Vim9completeFuncArgs)
-    bw!
-
-    # Test for using a variable with a lambda expression
-    var Fn: func = (a, b) => Vim9CompleteFunc(63, a, b)
-    &completefunc = Fn
-    new | only
-    setline(1, 'three')
-    g:Vim9completeFuncArgs = []
-    feedkeys("A\\\", 'x')
-    assert_equal([[63, 1, ''], [63, 0, 'three']], g:Vim9completeFuncArgs)
-    bw!
-
-    # Test for using a string(variable with a lambda expression)
-    Fn = (a, b) => Vim9CompleteFunc(64, a, b)
-    &completefunc = string(Fn)
-    new | only
-    setline(1, 'three')
-    g:Vim9completeFuncArgs = []
-    feedkeys("A\\\", 'x')
-    assert_equal([[64, 1, ''], [64, 0, 'three']], g:Vim9completeFuncArgs)
-    bw!
   END
   " call CheckScriptSuccess(lines)
 
@@ -1506,154 +1496,179 @@ func Test_omnifunc_callback()
     return a:findstart ? 0 : []
   endfunc
 
-  " Test for using a function()
-  set omnifunc=function('MyomniFunc1',[10])
-  new | only
-  call setline(1, 'one')
-  let g:MyomniFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[10, 1, ''], [10, 0, 'one']], g:MyomniFunc1_args)
-  bw!
+  let lines =<< trim END
+    #" Test for using a function()
+    set omnifunc=function('g:MyomniFunc1',\ [10])
+    new | only
+    call setline(1, 'one')
+    LET g:MyomniFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[10, 1, ''], [10, 0, 'one']], g:MyomniFunc1_args)
+    bw!
 
-  " Using a funcref variable to set 'omnifunc'
-  let Fn = function('MyomniFunc1', [11])
-  let &omnifunc = Fn
-  new | only
-  call setline(1, 'two')
-  let g:MyomniFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[11, 1, ''], [11, 0, 'two']], g:MyomniFunc1_args)
-  bw!
+    #" Using a funcref variable to set 'omnifunc'
+    VAR Fn = function('g:MyomniFunc1', [11])
+    LET &omnifunc = Fn
+    new | only
+    call setline(1, 'two')
+    LET g:MyomniFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[11, 1, ''], [11, 0, 'two']], g:MyomniFunc1_args)
+    bw!
 
-  " Using a string(funcref_variable) to set 'omnifunc'
-  let Fn = function('MyomniFunc1', [12])
-  let &omnifunc = string(Fn)
-  new | only
-  call setline(1, 'two')
-  let g:MyomniFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[12, 1, ''], [12, 0, 'two']], g:MyomniFunc1_args)
-  bw!
+    #" Using a string(funcref_variable) to set 'omnifunc'
+    LET Fn = function('g:MyomniFunc1', [12])
+    LET &omnifunc = string(Fn)
+    new | only
+    call setline(1, 'two')
+    LET g:MyomniFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[12, 1, ''], [12, 0, 'two']], g:MyomniFunc1_args)
+    bw!
 
-  " Test for using a funcref()
-  set omnifunc=funcref('MyomniFunc1',\ [13])
-  new | only
-  call setline(1, 'three')
-  let g:MyomniFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[13, 1, ''], [13, 0, 'three']], g:MyomniFunc1_args)
-  bw!
+    #" Test for using a funcref()
+    set omnifunc=funcref('g:MyomniFunc1',\ [13])
+    new | only
+    call setline(1, 'three')
+    LET g:MyomniFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[13, 1, ''], [13, 0, 'three']], g:MyomniFunc1_args)
+    bw!
 
-  " Using a funcref variable to set 'omnifunc'
-  let Fn = funcref('MyomniFunc1', [14])
-  let &omnifunc = Fn
-  new | only
-  call setline(1, 'four')
-  let g:MyomniFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[14, 1, ''], [14, 0, 'four']], g:MyomniFunc1_args)
-  bw!
+    #" Use let to set 'omnifunc' to a funcref
+    LET Fn = funcref('g:MyomniFunc1', [14])
+    LET &omnifunc = Fn
+    new | only
+    call setline(1, 'four')
+    LET g:MyomniFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[14, 1, ''], [14, 0, 'four']], g:MyomniFunc1_args)
+    bw!
 
-  " Using a string(funcref_variable) to set 'omnifunc'
-  let Fn = funcref('MyomniFunc1', [15])
-  let &omnifunc = string(Fn)
-  new | only
-  call setline(1, 'four')
-  let g:MyomniFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[15, 1, ''], [15, 0, 'four']], g:MyomniFunc1_args)
-  bw!
+    #" Using a string(funcref) to set 'omnifunc'
+    LET Fn = funcref("g:MyomniFunc1", [15])
+    LET &omnifunc = string(Fn)
+    new | only
+    call setline(1, 'four')
+    LET g:MyomniFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[15, 1, ''], [15, 0, 'four']], g:MyomniFunc1_args)
+    bw!
 
-  " Test for using a lambda function
-  set omnifunc={a,\ b\ ->\ MyomniFunc1(16,\ a,\ b)}
-  new | only
-  call setline(1, 'five')
-  let g:MyomniFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[16, 1, ''], [16, 0, 'five']], g:MyomniFunc1_args)
-  bw!
+    #" Test for using a lambda function with set
+    VAR optval = "LSTART a, b LMIDDLE MyomniFunc1(16, a, b) LEND"
+    LET optval = substitute(optval, ' ', '\\ ', 'g')
+    exe "set omnifunc=" .. optval
+    new | only
+    call setline(1, 'five')
+    LET g:MyomniFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[16, 1, ''], [16, 0, 'five']], g:MyomniFunc1_args)
+    bw!
 
-  " Set 'omnifunc' to a lambda expression
-  let &omnifunc = {a, b -> MyomniFunc1(17, a, b)}
-  new | only
-  call setline(1, 'six')
-  let g:MyomniFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[17, 1, ''], [17, 0, 'six']], g:MyomniFunc1_args)
-  bw!
+    #" Set 'omnifunc' to a lambda expression
+    LET &omnifunc = LSTART a, b LMIDDLE MyomniFunc1(17, a, b) LEND
+    new | only
+    call setline(1, 'six')
+    LET g:MyomniFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[17, 1, ''], [17, 0, 'six']], g:MyomniFunc1_args)
+    bw!
 
-  " Set 'omnifunc' to a string(lambda_expression)
-  let &omnifunc = '{a, b -> MyomniFunc1(18, a, b)}'
-  new | only
-  call setline(1, 'six')
-  let g:MyomniFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[18, 1, ''], [18, 0, 'six']], g:MyomniFunc1_args)
-  bw!
+    #" Set 'omnifunc' to a string(lambda_expression)
+    LET &omnifunc = 'LSTART a, b LMIDDLE MyomniFunc1(18, a, b) LEND'
+    new | only
+    call setline(1, 'six')
+    LET g:MyomniFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[18, 1, ''], [18, 0, 'six']], g:MyomniFunc1_args)
+    bw!
 
-  " Set 'omnifunc' to a variable with a lambda expression
-  let Lambda = {a, b -> MyomniFunc1(19, a, b)}
-  let &omnifunc = Lambda
-  new | only
-  call setline(1, 'seven')
-  let g:MyomniFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:MyomniFunc1_args)
-  bw!
+    #" Set 'omnifunc' to a variable with a lambda expression
+    VAR Lambda = LSTART a, b LMIDDLE MyomniFunc1(19, a, b) LEND
+    LET &omnifunc = Lambda
+    new | only
+    call setline(1, 'seven')
+    LET g:MyomniFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:MyomniFunc1_args)
+    bw!
 
-  " Set 'omnifunc' to a string(variable with a lambda expression)
-  let Lambda = {a, b -> MyomniFunc1(20, a, b)}
-  let &omnifunc = string(Lambda)
-  new | only
-  call setline(1, 'seven')
-  let g:MyomniFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:MyomniFunc1_args)
-  bw!
+    #" Set 'omnifunc' to a string(variable with a lambda expression)
+    LET Lambda = LSTART a, b LMIDDLE MyomniFunc1(20, a, b) LEND
+    LET &omnifunc = string(Lambda)
+    new | only
+    call setline(1, 'seven')
+    LET g:MyomniFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:MyomniFunc1_args)
+    bw!
 
-  " Test for using a lambda function with incorrect return value
-  let Lambda = {s -> strlen(s)}
-  let &omnifunc = Lambda
-  new | only
-  call setline(1, 'eight')
-  call feedkeys("A\\\", 'x')
-  bw!
+    #" Test for using a lambda function with incorrect return value
+    LET Lambda = LSTART a, b LMIDDLE strlen(a) LEND
+    LET &omnifunc = Lambda
+    new | only
+    call setline(1, 'eight')
+    call feedkeys("A\\\", 'x')
+    bw!
 
-  " Test for clearing the 'omnifunc' option
-  set omnifunc=''
-  set omnifunc&
+    #" Test for clearing the 'omnifunc' option
+    set omnifunc=''
+    set omnifunc&
+    call assert_fails("set omnifunc=function('abc')", "E700:")
+    call assert_fails("set omnifunc=funcref('abc')", "E700:")
+
+    #" set 'omnifunc' to a non-existing function
+    func MyomniFunc2(findstart, base)
+      call add(g:MyomniFunc2_args, [a:findstart, a:base])
+      return a:findstart ? 0 : []
+    endfunc
+    set omnifunc=MyomniFunc2
+    call setline(1, 'nine')
+    call assert_fails("set omnifunc=function('NonExistingFunc')", 'E700:')
+    call assert_fails("LET &omnifunc = function('NonExistingFunc')", 'E700:')
+    LET g:MyomniFunc2_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[1, ''], [0, 'nine']], g:MyomniFunc2_args)
+    bw!
+  END
+  call CheckLegacyAndVim9Success(lines)
 
-  call assert_fails("set omnifunc=function('abc')", "E700:")
-  call assert_fails("set omnifunc=funcref('abc')", "E700:")
   let &omnifunc = {a -> 'abc'}
   call feedkeys("A\\\", 'x')
 
   " Using Vim9 lambda expression in legacy context should fail
-  " set omnifunc=(a,\ b)\ =>\ g:MyomniFunc1(21,\ a,\ b)
+  " set omnifunc=(a,\ b)\ =>\ MyomniFunc1(21,\ a,\ b)
   new | only
   let g:MyomniFunc1_args = []
   " call assert_fails('call feedkeys("A\\\", "x")', 'E117:')
   call assert_equal([], g:MyomniFunc1_args)
 
-  " set 'omnifunc' to a non-existing function
-  func MyomniFunc2(findstart, base)
-    call add(g:MyomniFunc2_args, [a:findstart, a:base])
-    return a:findstart ? 0 : []
+  " set 'omnifunc' to a partial with dict. This used to cause a crash.
+  func SetOmniFunc()
+    let params = {'omni': function('g:DictOmniFunc')}
+    let &omnifunc = params.omni
   endfunc
-  set omnifunc=MyomniFunc2
-  call setline(1, 'nine')
-  call assert_fails("set omnifunc=function('NonExistingFunc')", 'E700:')
-  call assert_fails("let &omnifunc = function('NonExistingFunc')", 'E700:')
-  let g:MyomniFunc2_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'nine']], g:MyomniFunc2_args)
-  bw!
+  func g:DictOmniFunc(_) dict
+  endfunc
+  call SetOmniFunc()
+  new
+  call SetOmniFunc()
+  bw
+  call test_garbagecollect_now()
+  new
+  set omnifunc=
+  wincmd w
+  set omnifunc=
+  %bw!
+  delfunc g:DictOmniFunc
+  delfunc SetOmniFunc
 
   " Vim9 tests
   let lines =<< trim END
     vim9script
 
-    # Test for using function()
+    # Test for using a def function with omnifunc
     def Vim9omniFunc(val: number, findstart: number, base: string): any
       add(g:Vim9omniFunc_Args, [val, findstart, base])
       return findstart ? 0 : []
@@ -1665,44 +1680,6 @@ func Test_omnifunc_callback()
     feedkeys("A\\\", 'x')
     assert_equal([[60, 1, ''], [60, 0, 'one']], g:Vim9omniFunc_Args)
     bw!
-
-    # Test for using a lambda
-    &omnifunc = (a, b) => Vim9omniFunc(61, a, b)
-    new | only
-    setline(1, 'two')
-    g:Vim9omniFunc_Args = []
-    feedkeys("A\\\", 'x')
-    assert_equal([[61, 1, ''], [61, 0, 'two']], g:Vim9omniFunc_Args)
-    bw!
-
-    # Test for using a string(lambda)
-    &omnifunc = '(a, b) => Vim9omniFunc(62, a, b)'
-    new | only
-    setline(1, 'two')
-    g:Vim9omniFunc_Args = []
-    feedkeys("A\\\", 'x')
-    assert_equal([[62, 1, ''], [62, 0, 'two']], g:Vim9omniFunc_Args)
-    bw!
-
-    # Test for using a variable with a lambda expression
-    var Fn: func = (a, b) => Vim9omniFunc(63, a, b)
-    &omnifunc = Fn
-    new | only
-    setline(1, 'three')
-    g:Vim9omniFunc_Args = []
-    feedkeys("A\\\", 'x')
-    assert_equal([[63, 1, ''], [63, 0, 'three']], g:Vim9omniFunc_Args)
-    bw!
-
-    # Test for using a string(variable with a lambda expression)
-    Fn = (a, b) => Vim9omniFunc(64, a, b)
-    &omnifunc = string(Fn)
-    new | only
-    setline(1, 'three')
-    g:Vim9omniFunc_Args = []
-    feedkeys("A\\\", 'x')
-    assert_equal([[64, 1, ''], [64, 0, 'three']], g:Vim9omniFunc_Args)
-    bw!
   END
   " call CheckScriptSuccess(lines)
 
@@ -1720,178 +1697,215 @@ func Test_thesaurusfunc_callback()
     return a:findstart ? 0 : []
   endfunc
 
-  " Test for using a function()
-  set thesaurusfunc=function('MytsrFunc1',[10])
-  new | only
-  call setline(1, 'one')
-  let g:MytsrFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[10, 1, ''], [10, 0, 'one']], g:MytsrFunc1_args)
-  bw!
+  let lines =<< trim END
+    #" Test for using a function()
+    set thesaurusfunc=function('g:MytsrFunc1',\ [10])
+    new | only
+    call setline(1, 'one')
+    LET g:MytsrFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[10, 1, ''], [10, 0, 'one']], g:MytsrFunc1_args)
+    bw!
 
-  " Using a funcref variable to set 'thesaurusfunc'
-  let Fn = function('MytsrFunc1', [11])
-  let &thesaurusfunc = Fn
-  new | only
-  call setline(1, 'two')
-  let g:MytsrFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[11, 1, ''], [11, 0, 'two']], g:MytsrFunc1_args)
-  bw!
+    #" Using a funcref variable to set 'thesaurusfunc'
+    VAR Fn = function('g:MytsrFunc1', [11])
+    LET &thesaurusfunc = Fn
+    new | only
+    call setline(1, 'two')
+    LET g:MytsrFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[11, 1, ''], [11, 0, 'two']], g:MytsrFunc1_args)
+    bw!
 
-  " Using a string(funcref_variable) to set 'thesaurusfunc'
-  let Fn = function('MytsrFunc1', [12])
-  let &thesaurusfunc = string(Fn)
-  new | only
-  call setline(1, 'two')
-  let g:MytsrFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[12, 1, ''], [12, 0, 'two']], g:MytsrFunc1_args)
-  bw!
+    #" Using a string(funcref_variable) to set 'thesaurusfunc'
+    LET Fn = function('g:MytsrFunc1', [12])
+    LET &thesaurusfunc = string(Fn)
+    new | only
+    call setline(1, 'two')
+    LET g:MytsrFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[12, 1, ''], [12, 0, 'two']], g:MytsrFunc1_args)
+    bw!
 
-  " Test for using a funcref()
-  set thesaurusfunc=funcref('MytsrFunc1',[13])
-  new | only
-  call setline(1, 'three')
-  let g:MytsrFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[13, 1, ''], [13, 0, 'three']], g:MytsrFunc1_args)
-  bw!
+    #" Test for using a funcref()
+    set thesaurusfunc=funcref('g:MytsrFunc1',\ [13])
+    new | only
+    call setline(1, 'three')
+    LET g:MytsrFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[13, 1, ''], [13, 0, 'three']], g:MytsrFunc1_args)
+    bw!
 
-  " Using a funcref variable to set 'thesaurusfunc'
-  let Fn = funcref('MytsrFunc1', [14])
-  let &thesaurusfunc = Fn
-  new | only
-  call setline(1, 'four')
-  let g:MytsrFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[14, 1, ''], [14, 0, 'four']], g:MytsrFunc1_args)
-  bw!
+    #" Using a funcref variable to set 'thesaurusfunc'
+    LET Fn = funcref('g:MytsrFunc1', [14])
+    LET &thesaurusfunc = Fn
+    new | only
+    call setline(1, 'four')
+    LET g:MytsrFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[14, 1, ''], [14, 0, 'four']], g:MytsrFunc1_args)
+    bw!
 
-  " Using a string(funcref_variable) to set 'thesaurusfunc'
-  let Fn = funcref('MytsrFunc1', [15])
-  let &thesaurusfunc = string(Fn)
-  new | only
-  call setline(1, 'four')
-  let g:MytsrFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[15, 1, ''], [15, 0, 'four']], g:MytsrFunc1_args)
-  bw!
+    #" Using a string(funcref_variable) to set 'thesaurusfunc'
+    LET Fn = funcref('g:MytsrFunc1', [15])
+    LET &thesaurusfunc = string(Fn)
+    new | only
+    call setline(1, 'four')
+    LET g:MytsrFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[15, 1, ''], [15, 0, 'four']], g:MytsrFunc1_args)
+    bw!
 
-  " Test for using a lambda function
-  set thesaurusfunc={a,\ b\ ->\ MytsrFunc1(16,\ a,\ b)}
-  new | only
-  call setline(1, 'five')
-  let g:MytsrFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[16, 1, ''], [16, 0, 'five']], g:MytsrFunc1_args)
-  bw!
+    #" Test for using a lambda function
+    VAR optval = "LSTART a, b LMIDDLE MytsrFunc1(16, a, b) LEND"
+    LET optval = substitute(optval, ' ', '\\ ', 'g')
+    exe "set thesaurusfunc=" .. optval
+    new | only
+    call setline(1, 'five')
+    LET g:MytsrFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[16, 1, ''], [16, 0, 'five']], g:MytsrFunc1_args)
+    bw!
 
-  " Set 'thesaurusfunc' to a lambda expression
-  let &thesaurusfunc = {a, b -> MytsrFunc1(17, a, b)}
-  new | only
-  call setline(1, 'six')
-  let g:MytsrFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[17, 1, ''], [17, 0, 'six']], g:MytsrFunc1_args)
-  bw!
+    #" Test for using a lambda function with set
+    LET &thesaurusfunc = LSTART a, b LMIDDLE MytsrFunc1(17, a, b) LEND
+    new | only
+    call setline(1, 'six')
+    LET g:MytsrFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[17, 1, ''], [17, 0, 'six']], g:MytsrFunc1_args)
+    bw!
 
-  " Set 'thesaurusfunc' to a string(lambda expression)
-  let &thesaurusfunc = '{a, b -> MytsrFunc1(18, a, b)}'
-  new | only
-  call setline(1, 'six')
-  let g:MytsrFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[18, 1, ''], [18, 0, 'six']], g:MytsrFunc1_args)
-  bw!
+    #" Set 'thesaurusfunc' to a string(lambda expression)
+    LET &thesaurusfunc = 'LSTART a, b LMIDDLE MytsrFunc1(18, a, b) LEND'
+    new | only
+    call setline(1, 'six')
+    LET g:MytsrFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[18, 1, ''], [18, 0, 'six']], g:MytsrFunc1_args)
+    bw!
 
-  " Set 'thesaurusfunc' to a variable with a lambda expression
-  let Lambda = {a, b -> MytsrFunc1(19, a, b)}
-  let &thesaurusfunc = Lambda
-  new | only
-  call setline(1, 'seven')
-  let g:MytsrFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:MytsrFunc1_args)
-  bw!
+    #" Set 'thesaurusfunc' to a variable with a lambda expression
+    VAR Lambda = LSTART a, b LMIDDLE MytsrFunc1(19, a, b) LEND
+    LET &thesaurusfunc = Lambda
+    new | only
+    call setline(1, 'seven')
+    LET g:MytsrFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:MytsrFunc1_args)
+    bw!
 
-  " Set 'thesaurusfunc' to a string(variable with a lambda expression)
-  let Lambda = {a, b -> MytsrFunc1(20, a, b)}
-  let &thesaurusfunc = string(Lambda)
-  new | only
-  call setline(1, 'seven')
-  let g:MytsrFunc1_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:MytsrFunc1_args)
-  bw!
+    #" Set 'thesaurusfunc' to a string(variable with a lambda expression)
+    LET Lambda = LSTART a, b LMIDDLE MytsrFunc1(20, a, b) LEND
+    LET &thesaurusfunc = string(Lambda)
+    new | only
+    call setline(1, 'seven')
+    LET g:MytsrFunc1_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:MytsrFunc1_args)
+    bw!
 
-  " Test for using a lambda function with incorrect return value
-  let Lambda = {s -> strlen(s)}
-  let &thesaurusfunc = Lambda
-  new | only
-  call setline(1, 'eight')
-  call feedkeys("A\\\", 'x')
-  bw!
+    #" Test for using a lambda function with incorrect return value
+    LET Lambda = LSTART a, b LMIDDLE strlen(a) LEND
+    LET &thesaurusfunc = Lambda
+    new | only
+    call setline(1, 'eight')
+    call feedkeys("A\\\", 'x')
+    bw!
 
-  " Test for clearing the 'thesaurusfunc' option
-  set thesaurusfunc=''
-  set thesaurusfunc&
+    #" Test for clearing the 'thesaurusfunc' option
+    set thesaurusfunc=''
+    set thesaurusfunc&
+    call assert_fails("set thesaurusfunc=function('abc')", "E700:")
+    call assert_fails("set thesaurusfunc=funcref('abc')", "E700:")
+
+    #" set 'thesaurusfunc' to a non-existing function
+    func MytsrFunc2(findstart, base)
+      call add(g:MytsrFunc2_args, [a:findstart, a:base])
+      return a:findstart ? 0 : ['sunday']
+    endfunc
+    set thesaurusfunc=MytsrFunc2
+    call setline(1, 'ten')
+    call assert_fails("set thesaurusfunc=function('NonExistingFunc')", 'E700:')
+    call assert_fails("LET &thesaurusfunc = function('NonExistingFunc')", 'E700:')
+    LET g:MytsrFunc2_args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[1, ''], [0, 'ten']], g:MytsrFunc2_args)
+    bw!
+
+    #" Use a buffer-local value and a global value
+    set thesaurusfunc&
+    setlocal thesaurusfunc=function('g:MytsrFunc1',\ [22])
+    call setline(1, 'sun')
+    LET g:MytsrFunc1_args = []
+    call feedkeys("A\\\", "x")
+    call assert_equal('sun', getline(1))
+    call assert_equal([[22, 1, ''], [22, 0, 'sun']], g:MytsrFunc1_args)
+    new
+    call setline(1, 'sun')
+    LET g:MytsrFunc1_args = []
+    call feedkeys("A\\\", "x")
+    call assert_equal('sun', getline(1))
+    call assert_equal([], g:MytsrFunc1_args)
+    set thesaurusfunc=function('g:MytsrFunc1',\ [23])
+    wincmd w
+    call setline(1, 'sun')
+    LET g:MytsrFunc1_args = []
+    call feedkeys("A\\\", "x")
+    call assert_equal('sun', getline(1))
+    call assert_equal([[22, 1, ''], [22, 0, 'sun']], g:MytsrFunc1_args)
+    :%bw!
+  END
+  call CheckLegacyAndVim9Success(lines)
 
-  call assert_fails("set thesaurusfunc=function('abc')", "E700:")
-  call assert_fails("set thesaurusfunc=funcref('abc')", "E700:")
   let &thesaurusfunc = {a -> 'abc'}
   call feedkeys("A\\\", 'x')
 
   " Using Vim9 lambda expression in legacy context should fail
-  " set thesaurusfunc=(a,\ b)\ =>\ g:MytsrFunc1(21,\ a,\ b)
+  " set thesaurusfunc=(a,\ b)\ =>\ MytsrFunc1(21,\ a,\ b)
   new | only
   let g:MytsrFunc1_args = []
   " call assert_fails('call feedkeys("A\\\", "x")', 'E117:')
   call assert_equal([], g:MytsrFunc1_args)
   bw!
 
-  " Use a buffer-local value and a global value
-  set thesaurusfunc&
-  setlocal thesaurusfunc=function('MytsrFunc1',[22])
-  call setline(1, 'sun')
-  let g:MytsrFunc1_args = []
-  call feedkeys("A\\\", "x")
-  call assert_equal('sun', getline(1))
-  call assert_equal([[22, 1, ''], [22, 0, 'sun']], g:MytsrFunc1_args)
+  " set 'thesaurusfunc' to a partial with dict. This used to cause a crash.
+  func SetTsrFunc()
+    let params = {'thesaurus': function('g:DictTsrFunc')}
+    let &thesaurusfunc = params.thesaurus
+  endfunc
+  func g:DictTsrFunc(_) dict
+  endfunc
+  call SetTsrFunc()
   new
-  call setline(1, 'sun')
-  let g:MytsrFunc1_args = []
-  call feedkeys("A\\\", "x")
-  call assert_equal('sun', getline(1))
-  call assert_equal([], g:MytsrFunc1_args)
-  set thesaurusfunc=function('MytsrFunc1',[23])
+  call SetTsrFunc()
+  bw
+  call test_garbagecollect_now()
+  new
+  set thesaurusfunc=
   wincmd w
-  call setline(1, 'sun')
-  let g:MytsrFunc1_args = []
-  call feedkeys("A\\\", "x")
-  call assert_equal('sun', getline(1))
-  call assert_equal([[22, 1, ''], [22, 0, 'sun']], g:MytsrFunc1_args)
   %bw!
+  delfunc SetTsrFunc
 
-  " set 'thesaurusfunc' to a non-existing function
-  func MytsrFunc2(findstart, base)
-    call add(g:MytsrFunc2_args, [a:findstart, a:base])
-    return a:findstart ? 0 : ['sunday']
+  " set buffer-local 'thesaurusfunc' to a partial with dict. This used to
+  " cause a crash.
+  func SetLocalTsrFunc()
+    let params = {'thesaurus': function('g:DictTsrFunc')}
+    let &l:thesaurusfunc = params.thesaurus
   endfunc
-  set thesaurusfunc=MytsrFunc2
-  call setline(1, 'ten')
-  call assert_fails("set thesaurusfunc=function('NonExistingFunc')", 'E700:')
-  call assert_fails("let &thesaurusfunc = function('NonExistingFunc')", 'E700:')
-  let g:MytsrFunc2_args = []
-  call feedkeys("A\\\", 'x')
-  call assert_equal([[1, ''], [0, 'ten']], g:MytsrFunc2_args)
+  call SetLocalTsrFunc()
+  call test_garbagecollect_now()
+  call SetLocalTsrFunc()
+  set thesaurusfunc=
   bw!
+  delfunc g:DictTsrFunc
+  delfunc SetLocalTsrFunc
 
   " Vim9 tests
   let lines =<< trim END
     vim9script
 
-    # Test for using function()
+    # Test for using a def function with thesaurusfunc
     def Vim9tsrFunc(val: number, findstart: number, base: string): any
       add(g:Vim9tsrFunc_Args, [val, findstart, base])
       return findstart ? 0 : []
@@ -1903,44 +1917,6 @@ func Test_thesaurusfunc_callback()
     feedkeys("A\\\", 'x')
     assert_equal([[60, 1, ''], [60, 0, 'one']], g:Vim9tsrFunc_Args)
     bw!
-
-    # Test for using a lambda
-    &thesaurusfunc = (a, b) => Vim9tsrFunc(61, a, b)
-    new | only
-    setline(1, 'two')
-    g:Vim9tsrFunc_Args = []
-    feedkeys("A\\\", 'x')
-    assert_equal([[61, 1, ''], [61, 0, 'two']], g:Vim9tsrFunc_Args)
-    bw!
-
-    # Test for using a string(lambda)
-    &thesaurusfunc = '(a, b) => Vim9tsrFunc(62, a, b)'
-    new | only
-    setline(1, 'two')
-    g:Vim9tsrFunc_Args = []
-    feedkeys("A\\\", 'x')
-    assert_equal([[62, 1, ''], [62, 0, 'two']], g:Vim9tsrFunc_Args)
-    bw!
-
-    # Test for using a variable with a lambda expression
-    var Fn: func = (a, b) => Vim9tsrFunc(63, a, b)
-    &thesaurusfunc = Fn
-    new | only
-    setline(1, 'three')
-    g:Vim9tsrFunc_Args = []
-    feedkeys("A\\\", 'x')
-    assert_equal([[63, 1, ''], [63, 0, 'three']], g:Vim9tsrFunc_Args)
-    bw!
-
-    # Test for using a string(variable with a lambda expression)
-    Fn = (a, b) => Vim9tsrFunc(64, a, b)
-    &thesaurusfunc = string(Fn)
-    new | only
-    setline(1, 'three')
-    g:Vim9tsrFunc_Args = []
-    feedkeys("A\\\", 'x')
-    assert_equal([[64, 1, ''], [64, 0, 'three']], g:Vim9tsrFunc_Args)
-    bw!
   END
   " call CheckScriptSuccess(lines)
 
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index b9cc858cdb..b3e0be8f77 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -3,6 +3,7 @@
 source shared.vim
 source check.vim
 source view_util.vim
+source vim9.vim
 source screendump.vim
 
 func Setup_NewWindow()
@@ -463,110 +464,122 @@ func Test_opfunc_callback()
     let g:OpFuncArgs = [a:val, a:type]
   endfunc
 
-  " Test for using a function()
-  set opfunc=function('MyopFunc',\ [11])
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([11, 'char'], g:OpFuncArgs)
-
-  " Using a funcref variable to set 'operatorfunc'
-  let Fn = function('MyopFunc', [12])
-  let &opfunc = Fn
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([12, 'char'], g:OpFuncArgs)
-
-  " Using a string(funcref_variable) to set 'operatorfunc'
-  let Fn = function('MyopFunc', [13])
-  let &operatorfunc = string(Fn)
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([13, 'char'], g:OpFuncArgs)
-
-  " Test for using a funcref()
-  set operatorfunc=funcref('MyopFunc',\ [14])
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([14, 'char'], g:OpFuncArgs)
-
-  " Using a funcref variable to set 'operatorfunc'
-  let Fn = funcref('MyopFunc', [15])
-  let &opfunc = Fn
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([15, 'char'], g:OpFuncArgs)
-
-  " Using a string(funcref_variable) to set 'operatorfunc'
-  let Fn = funcref('MyopFunc', [16])
-  let &opfunc = string(Fn)
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([16, 'char'], g:OpFuncArgs)
+  let lines =<< trim END
+    #" Test for using a function()
+    set opfunc=function('g:MyopFunc',\ [10])
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([10, 'char'], g:OpFuncArgs)
 
-  " Test for using a lambda function using set
-  set opfunc={a\ ->\ MyopFunc(17,\ a)}
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([17, 'char'], g:OpFuncArgs)
+    #" Using a funcref variable to set 'operatorfunc'
+    VAR Fn = function('g:MyopFunc', [11])
+    LET &opfunc = Fn
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([11, 'char'], g:OpFuncArgs)
 
-  " Test for using a lambda function using let
-  let &opfunc = {a -> MyopFunc(18, a)}
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([18, 'char'], g:OpFuncArgs)
+    #" Using a string(funcref_variable) to set 'operatorfunc'
+    LET Fn = function('g:MyopFunc', [12])
+    LET &operatorfunc = string(Fn)
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([12, 'char'], g:OpFuncArgs)
 
-  " Set 'operatorfunc' to a string(lambda expression)
-  let &opfunc = '{a -> MyopFunc(19, a)}'
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([19, 'char'], g:OpFuncArgs)
+    #" Test for using a funcref()
+    set operatorfunc=funcref('g:MyopFunc',\ [13])
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([13, 'char'], g:OpFuncArgs)
 
-  " Set 'operatorfunc' to a variable with a lambda expression
-  let Lambda = {a -> MyopFunc(20, a)}
-  let &opfunc = Lambda
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([20, 'char'], g:OpFuncArgs)
+    #" Using a funcref variable to set 'operatorfunc'
+    LET Fn = funcref('g:MyopFunc', [14])
+    LET &opfunc = Fn
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([14, 'char'], g:OpFuncArgs)
 
-  " Set 'operatorfunc' to a string(variable with a lambda expression)
-  let Lambda = {a -> MyopFunc(21, a)}
-  let &opfunc = string(Lambda)
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([21, 'char'], g:OpFuncArgs)
+    #" Using a string(funcref_variable) to set 'operatorfunc'
+    LET Fn = funcref('g:MyopFunc', [15])
+    LET &opfunc = string(Fn)
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([15, 'char'], g:OpFuncArgs)
 
-  " Try to use 'operatorfunc' after the function is deleted
-  func TmpOpFunc(type)
-    let g:OpFuncArgs = [22, a:type]
-  endfunc
-  let &opfunc = function('TmpOpFunc')
-  delfunc TmpOpFunc
-  call test_garbagecollect_now()
-  let g:OpFuncArgs = []
-  call assert_fails('normal! g@l', 'E117:')
-  call assert_equal([], g:OpFuncArgs)
+    #" Test for using a lambda function using set
+    VAR optval = "LSTART a LMIDDLE MyopFunc(16, a) LEND"
+    LET optval = substitute(optval, ' ', '\\ ', 'g')
+    exe "set opfunc=" .. optval
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([16, 'char'], g:OpFuncArgs)
 
-  " Try to use a function with two arguments for 'operatorfunc'
-  func! MyopFunc2(x, y)
-    let g:OpFuncArgs = [a:x, a:y]
-  endfunc
-  set opfunc=MyopFunc2
-  let g:OpFuncArgs = []
-  call assert_fails('normal! g@l', 'E119:')
-  call assert_equal([], g:OpFuncArgs)
+    #" Test for using a lambda function using LET
+    LET &opfunc = LSTART a LMIDDLE MyopFunc(17, a) LEND
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([17, 'char'], g:OpFuncArgs)
 
-  " Try to use a lambda function with two arguments for 'operatorfunc'
-  let &opfunc = {a, b -> MyopFunc(23, b)}
-  let g:OpFuncArgs = []
-  call assert_fails('normal! g@l', 'E119:')
-  call assert_equal([], g:OpFuncArgs)
+    #" Set 'operatorfunc' to a string(lambda expression)
+    LET &opfunc = 'LSTART a LMIDDLE MyopFunc(18, a) LEND'
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([18, 'char'], g:OpFuncArgs)
 
-  " Test for clearing the 'operatorfunc' option
-  set opfunc=''
-  set opfunc&
+    #" Set 'operatorfunc' to a variable with a lambda expression
+    VAR Lambda = LSTART a LMIDDLE MyopFunc(19, a) LEND
+    LET &opfunc = Lambda
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([19, 'char'], g:OpFuncArgs)
 
-  call assert_fails("set opfunc=function('abc')", "E700:")
-  call assert_fails("set opfunc=funcref('abc')", "E700:")
+    #" Set 'operatorfunc' to a string(variable with a lambda expression)
+    LET Lambda = LSTART a LMIDDLE MyopFunc(20, a) LEND
+    LET &opfunc = string(Lambda)
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([20, 'char'], g:OpFuncArgs)
+
+    #" Try to use 'operatorfunc' after the function is deleted
+    func g:TmpOpFunc(type)
+      LET g:OpFuncArgs = [21, a:type]
+    endfunc
+    LET &opfunc = function('g:TmpOpFunc')
+    delfunc g:TmpOpFunc
+    call test_garbagecollect_now()
+    LET g:OpFuncArgs = []
+    call assert_fails('normal! g@l', 'E117:')
+    call assert_equal([], g:OpFuncArgs)
+
+    #" Try to use a function with two arguments for 'operatorfunc'
+    func MyopFunc2(x, y)
+      LET g:OpFuncArgs = [a:x, a:y]
+    endfunc
+    set opfunc=MyopFunc2
+    LET g:OpFuncArgs = []
+    call assert_fails('normal! g@l', 'E119:')
+    call assert_equal([], g:OpFuncArgs)
+
+    #" Try to use a lambda function with two arguments for 'operatorfunc'
+    LET &opfunc = LSTART a, b LMIDDLE MyopFunc(22, b) LEND
+    LET g:OpFuncArgs = []
+    call assert_fails('normal! g@l', 'E119:')
+    call assert_equal([], g:OpFuncArgs)
+
+    #" Test for clearing the 'operatorfunc' option
+    set opfunc=''
+    set opfunc&
+    call assert_fails("set opfunc=function('abc')", "E700:")
+    call assert_fails("set opfunc=funcref('abc')", "E700:")
+
+    #" set 'operatorfunc' to a non-existing function
+    LET &opfunc = function('g:MyopFunc', [23])
+    call assert_fails("set opfunc=function('NonExistingFunc')", 'E700:')
+    call assert_fails("LET &opfunc = function('NonExistingFunc')", 'E700:')
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([23, 'char'], g:OpFuncArgs)
+  END
+  call CheckTransLegacySuccess(lines)
 
   " Using Vim9 lambda expression in legacy context should fail
   " set opfunc=(a)\ =>\ MyopFunc(24,\ a)
@@ -574,19 +587,24 @@ func Test_opfunc_callback()
   " call assert_fails('normal! g@l', 'E117:')
   call assert_equal([], g:OpFuncArgs)
 
-  " set 'operatorfunc' to a non-existing function
-  let &opfunc = function('MyopFunc', [25])
-  call assert_fails("set opfunc=function('NonExistingFunc')", 'E700:')
-  call assert_fails("let &opfunc = function('NonExistingFunc')", 'E700:')
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([25, 'char'], g:OpFuncArgs)
+  " set 'operatorfunc' to a partial with dict. This used to cause a crash.
+  func SetOpFunc()
+    let operator = {'execute': function('OperatorExecute')}
+    let &opfunc = operator.execute
+  endfunc
+  func OperatorExecute(_) dict
+  endfunc
+  call SetOpFunc()
+  call test_garbagecollect_now()
+  set operatorfunc=
+  delfunc SetOpFunc
+  delfunc OperatorExecute
 
   " Vim9 tests
   let lines =<< trim END
     vim9script
 
-    # Test for using function()
+    # Test for using a def function with opfunc
     def g:Vim9opFunc(val: number, type: string): void
       g:OpFuncArgs = [val, type]
     enddef
@@ -594,33 +612,6 @@ func Test_opfunc_callback()
     g:OpFuncArgs = []
     normal! g@l
     assert_equal([60, 'char'], g:OpFuncArgs)
-
-    # Test for using a lambda
-    &opfunc = (a) => Vim9opFunc(61, a)
-    g:OpFuncArgs = []
-    normal! g@l
-    assert_equal([61, 'char'], g:OpFuncArgs)
-
-    # Test for using a string(lambda)
-    &opfunc = '(a) => Vim9opFunc(62, a)'
-    g:OpFuncArgs = []
-    normal! g@l
-    assert_equal([62, 'char'], g:OpFuncArgs)
-
-    # Test for using a variable with a lambda expression
-    var Fn: func = (a) => Vim9opFunc(63, a)
-    &opfunc = Fn
-    g:OpFuncArgs = []
-    normal! g@l
-    assert_equal([63, 'char'], g:OpFuncArgs)
-
-    # Test for using a string(variable with a lambda expression)
-    Fn = (a) => Vim9opFunc(64, a)
-    &opfunc = string(Fn)
-    g:OpFuncArgs = []
-    normal! g@l
-    assert_equal([64, 'char'], g:OpFuncArgs)
-    bw!
   END
   " call CheckScriptSuccess(lines)
 
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index 9d9fc5e77b..a18d6fd6ab 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -1,6 +1,7 @@
 " Test for the quickfix feature.
 
 source check.vim
+source vim9.vim
 CheckFeature quickfix
 
 source screendump.vim
@@ -5690,6 +5691,131 @@ func Test_qftextfunc()
   call Xtest_qftextfunc('l')
 endfunc
 
+func Test_qftextfunc_callback()
+  let lines =<< trim END
+    set efm=%f:%l:%c:%m
+
+    #" Test for using a function()
+    set qftf=function('g:Tqfexpr')
+    cexpr "F1:1:1:L1"
+    copen
+    call assert_equal('F1-L1C1-L1', getline(1))
+    cclose
+
+    #" Using a funcref variable to set 'quickfixtextfunc'
+    VAR Fn = function('g:Tqfexpr')
+    LET &qftf = Fn
+    cexpr "F2:2:2:L2"
+    copen
+    call assert_equal('F2-L2C2-L2', getline(1))
+    cclose
+
+    #" Using string(funcref_variable) to set 'quickfixtextfunc'
+    LET Fn = function('g:Tqfexpr')
+    LET &qftf = string(Fn)
+    cexpr "F3:3:3:L3"
+    copen
+    call assert_equal('F3-L3C3-L3', getline(1))
+    cclose
+
+    #" Test for using a funcref()
+    set qftf=funcref('g:Tqfexpr')
+    cexpr "F4:4:4:L4"
+    copen
+    call assert_equal('F4-L4C4-L4', getline(1))
+    cclose
+
+    #" Using a funcref variable to set 'quickfixtextfunc'
+    LET Fn = funcref('g:Tqfexpr')
+    LET &qftf = Fn
+    cexpr "F5:5:5:L5"
+    copen
+    call assert_equal('F5-L5C5-L5', getline(1))
+    cclose
+
+    #" Using a string(funcref_variable) to set 'quickfixtextfunc'
+    LET Fn = funcref('g:Tqfexpr')
+    LET &qftf = string(Fn)
+    cexpr "F5:5:5:L5"
+    copen
+    call assert_equal('F5-L5C5-L5', getline(1))
+    cclose
+
+    #" Test for using a lambda function with set
+    VAR optval = "LSTART a LMIDDLE Tqfexpr(a) LEND"
+    LET optval = substitute(optval, ' ', '\\ ', 'g')
+    exe "set qftf=" .. optval
+    cexpr "F6:6:6:L6"
+    copen
+    call assert_equal('F6-L6C6-L6', getline(1))
+    cclose
+
+    #" Set 'quickfixtextfunc' to a lambda expression
+    LET &qftf = LSTART a LMIDDLE Tqfexpr(a) LEND
+    cexpr "F7:7:7:L7"
+    copen
+    call assert_equal('F7-L7C7-L7', getline(1))
+    cclose
+
+    #" Set 'quickfixtextfunc' to string(lambda_expression)
+    LET &qftf = "LSTART a LMIDDLE Tqfexpr(a) LEND"
+    cexpr "F8:8:8:L8"
+    copen
+    call assert_equal('F8-L8C8-L8', getline(1))
+    cclose
+
+    #" Set 'quickfixtextfunc' to a variable with a lambda expression
+    VAR Lambda = LSTART a LMIDDLE Tqfexpr(a) LEND
+    LET &qftf = Lambda
+    cexpr "F9:9:9:L9"
+    copen
+    call assert_equal('F9-L9C9-L9', getline(1))
+    cclose
+
+    #" Set 'quickfixtextfunc' to a string(variable with a lambda expression)
+    LET Lambda = LSTART a LMIDDLE Tqfexpr(a) LEND
+    LET &qftf = string(Lambda)
+    cexpr "F9:9:9:L9"
+    copen
+    call assert_equal('F9-L9C9-L9', getline(1))
+    cclose
+  END
+  call CheckLegacyAndVim9Success(lines)
+
+  " set 'quickfixtextfunc' to a partial with dict. This used to cause a crash.
+  func SetQftfFunc()
+    let params = {'qftf': function('g:DictQftfFunc')}
+    let &quickfixtextfunc = params.qftf
+  endfunc
+  func g:DictQftfFunc(_) dict
+  endfunc
+  call SetQftfFunc()
+  new
+  call SetQftfFunc()
+  bw
+  call test_garbagecollect_now()
+  new
+  set qftf=
+  wincmd w
+  set qftf=
+  :%bw!
+
+  " set per-quickfix list 'quickfixtextfunc' to a partial with dict. This used
+  " to cause a crash.
+  let &qftf = ''
+  func SetLocalQftfFunc()
+    let params = {'qftf': function('g:DictQftfFunc')}
+    call setqflist([], 'a', {'quickfixtextfunc' : params.qftf})
+  endfunc
+  call SetLocalQftfFunc()
+  call test_garbagecollect_now()
+  call setqflist([], 'a', {'quickfixtextfunc' : ''})
+  delfunc g:DictQftfFunc
+  delfunc SetQftfFunc
+  delfunc SetLocalQftfFunc
+  set efm&
+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()
diff --git a/src/nvim/testdir/test_tagfunc.vim b/src/nvim/testdir/test_tagfunc.vim
index 79b1110b43..827159e3c8 100644
--- a/src/nvim/testdir/test_tagfunc.vim
+++ b/src/nvim/testdir/test_tagfunc.vim
@@ -1,5 +1,7 @@
 " Test 'tagfunc'
 
+source vim9.vim
+
 func TagFunc(pat, flag, info)
   let g:tagfunc_args = [a:pat, a:flag, a:info]
   let tags = []
@@ -130,55 +132,121 @@ func Test_tagfunc_callback()
     return v:null
   endfunc
 
-  " Test for using a function()
-  set tagfunc=function('MytagFunc1',[10])
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails('tag a11', 'E433:')
-  call assert_equal([10, 'a11', '', {}], g:MytagFunc1_args)
+  let lines =<< trim END
+    #" Test for using a function()
+    set tagfunc=function('g:MytagFunc1',\ [10])
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails('tag a11', 'E433:')
+    call assert_equal([10, 'a11', '', {}], g:MytagFunc1_args)
 
-  " Using a funcref variable to set 'tagfunc'
-  let Fn = function('MytagFunc1', [11])
-  let &tagfunc = Fn
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails('tag a12', 'E433:')
-  call assert_equal([11, 'a12', '', {}], g:MytagFunc1_args)
+    #" Using a funcref variable to set 'tagfunc'
+    VAR Fn = function('g:MytagFunc1', [11])
+    LET &tagfunc = Fn
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails('tag a12', 'E433:')
+    call assert_equal([11, 'a12', '', {}], g:MytagFunc1_args)
 
-  " Using a string(funcref_variable) to set 'tagfunc'
-  let Fn = function('MytagFunc1', [12])
-  let &tagfunc = string(Fn)
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails('tag a12', 'E433:')
-  call assert_equal([12, 'a12', '', {}], g:MytagFunc1_args)
+    #" Using a string(funcref_variable) to set 'tagfunc'
+    LET Fn = function('g:MytagFunc1', [12])
+    LET &tagfunc = string(Fn)
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails('tag a12', 'E433:')
+    call assert_equal([12, 'a12', '', {}], g:MytagFunc1_args)
 
-  " Test for using a funcref()
-  func MytagFunc2(pat, flags, info)
-    let g:MytagFunc2_args = [a:pat, a:flags, a:info]
-    return v:null
-  endfunc
-  set tagfunc=funcref('MytagFunc1',[13])
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails('tag a13', 'E433:')
-  call assert_equal([13, 'a13', '', {}], g:MytagFunc1_args)
+    #" Test for using a funcref()
+    set tagfunc=funcref('g:MytagFunc1',\ [13])
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails('tag a13', 'E433:')
+    call assert_equal([13, 'a13', '', {}], g:MytagFunc1_args)
 
-  " Using a funcref variable to set 'tagfunc'
-  let Fn = funcref('MytagFunc1', [14])
-  let &tagfunc = Fn
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails('tag a14', 'E433:')
-  call assert_equal([14, 'a14', '', {}], g:MytagFunc1_args)
+    #" Using a funcref variable to set 'tagfunc'
+    LET Fn = funcref('g:MytagFunc1', [14])
+    LET &tagfunc = Fn
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails('tag a14', 'E433:')
+    call assert_equal([14, 'a14', '', {}], g:MytagFunc1_args)
+
+    #" Using a string(funcref_variable) to set 'tagfunc'
+    LET Fn = funcref('g:MytagFunc1', [15])
+    LET &tagfunc = string(Fn)
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails('tag a14', 'E433:')
+    call assert_equal([15, 'a14', '', {}], g:MytagFunc1_args)
+
+    #" Test for using a lambda function
+    VAR optval = "LSTART a, b, c LMIDDLE MytagFunc1(16, a, b, c) LEND"
+    LET optval = substitute(optval, ' ', '\\ ', 'g')
+    exe "set tagfunc=" .. optval
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails('tag a17', 'E433:')
+    call assert_equal([16, 'a17', '', {}], g:MytagFunc1_args)
+
+    #" Set 'tagfunc' to a lambda expression
+    LET &tagfunc = LSTART a, b, c LMIDDLE MytagFunc1(17, a, b, c) LEND
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails('tag a18', 'E433:')
+    call assert_equal([17, 'a18', '', {}], g:MytagFunc1_args)
 
-  " Using a string(funcref_variable) to set 'tagfunc'
-  let Fn = funcref('MytagFunc1', [15])
-  let &tagfunc = string(Fn)
+    #" Set 'tagfunc' to a string(lambda expression)
+    LET &tagfunc = 'LSTART a, b, c LMIDDLE MytagFunc1(18, a, b, c) LEND'
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails('tag a18', 'E433:')
+    call assert_equal([18, 'a18', '', {}], g:MytagFunc1_args)
+
+    #" Set 'tagfunc' to a variable with a lambda expression
+    VAR Lambda = LSTART a, b, c LMIDDLE MytagFunc1(19, a, b, c) LEND
+    LET &tagfunc = Lambda
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails("tag a19", "E433:")
+    call assert_equal([19, 'a19', '', {}], g:MytagFunc1_args)
+
+    #" Set 'tagfunc' to a string(variable with a lambda expression)
+    LET Lambda = LSTART a, b, c LMIDDLE MytagFunc1(20, a, b, c) LEND
+    LET &tagfunc = string(Lambda)
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails("tag a19", "E433:")
+    call assert_equal([20, 'a19', '', {}], g:MytagFunc1_args)
+
+    #" Test for using a lambda function with incorrect return value
+    LET Lambda = LSTART a, b, c LMIDDLE strlen(a) LEND
+    LET &tagfunc = string(Lambda)
+    new | only
+    call assert_fails("tag a20", "E987:")
+
+    #" Test for clearing the 'tagfunc' option
+    set tagfunc=''
+    set tagfunc&
+    call assert_fails("set tagfunc=function('abc')", "E700:")
+    call assert_fails("set tagfunc=funcref('abc')", "E700:")
+
+    #" set 'tagfunc' to a non-existing function
+    LET &tagfunc = function('g:MytagFunc1', [21])
+    call assert_fails("set tagfunc=function('NonExistingFunc')", 'E700:')
+    call assert_fails("LET &tagfunc = function('NonExistingFunc')", 'E700:')
+    call assert_fails("tag axb123", 'E426:')
+  END
+  call CheckLegacyAndVim9Success(lines)
+
+  let &tagfunc = "{a -> 'abc'}"
+  call assert_fails("echo taglist('a')", "E987:")
+
+  " Using Vim9 lambda expression in legacy context should fail
+  " set tagfunc=(a,\ b,\ c)\ =>\ g:MytagFunc1(21,\ a,\ b,\ c)
   new | only
   let g:MytagFunc1_args = []
-  call assert_fails('tag a14', 'E433:')
-  call assert_equal([15, 'a14', '', {}], g:MytagFunc1_args)
+  " call assert_fails("tag a17", "E117:")
+  call assert_equal([], g:MytagFunc1_args)
 
   " Test for using a script local function
   set tagfunc=ScriptLocalTagFunc
@@ -203,70 +271,25 @@ func Test_tagfunc_callback()
   call assert_fails('tag a16', 'E433:')
   call assert_equal(['a16', '', {}], g:ScriptLocalFuncArgs)
 
-  " Test for using a lambda function
-  set tagfunc={a,\ b,\ c\ ->\ MytagFunc1(16,\ a,\ b,\ c)}
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails('tag a17', 'E433:')
-  call assert_equal([16, 'a17', '', {}], g:MytagFunc1_args)
-
-  " Set 'tagfunc' to a lambda expression
-  let &tagfunc = {a, b, c -> MytagFunc1(17, a, b, c)}
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails('tag a18', 'E433:')
-  call assert_equal([17, 'a18', '', {}], g:MytagFunc1_args)
-
-  " Set 'tagfunc' to a string(lambda expression)
-  let &tagfunc = '{a, b, c -> MytagFunc1(18, a, b, c)}'
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails('tag a18', 'E433:')
-  call assert_equal([18, 'a18', '', {}], g:MytagFunc1_args)
-
-  " Set 'tagfunc' to a variable with a lambda expression
-  let Lambda = {a, b, c -> MytagFunc1(19, a, b, c)}
-  let &tagfunc = Lambda
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails("tag a19", "E433:")
-  call assert_equal([19, 'a19', '', {}], g:MytagFunc1_args)
-
-  " Set 'tagfunc' to a string(variable with a lambda expression)
-  let Lambda = {a, b, c -> MytagFunc1(20, a, b, c)}
-  let &tagfunc = string(Lambda)
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails("tag a19", "E433:")
-  call assert_equal([20, 'a19', '', {}], g:MytagFunc1_args)
-
-  " Test for using a lambda function with incorrect return value
-  let Lambda = {s -> strlen(s)}
-  let &tagfunc = string(Lambda)
-  new | only
-  call assert_fails("tag a20", "E987:")
-
-  " Test for clearing the 'tagfunc' option
-  set tagfunc=''
-  set tagfunc&
-
-  call assert_fails("set tagfunc=function('abc')", "E700:")
-  call assert_fails("set tagfunc=funcref('abc')", "E700:")
-  let &tagfunc = "{a -> 'abc'}"
-  call assert_fails("echo taglist('a')", "E987:")
-
-  " Using Vim9 lambda expression in legacy context should fail
-  " set tagfunc=(a,\ b,\ c)\ =>\ g:MytagFunc1(21,\ a,\ b,\ c)
-  new | only
-  let g:MytagFunc1_args = []
-  " call assert_fails("tag a17", "E117:")
-  call assert_equal([], g:MytagFunc1_args)
-
-  " set 'tagfunc' to a non-existing function
-  call assert_fails("set tagfunc=function('NonExistingFunc')", 'E700:')
-  call assert_fails("let &tagfunc = function('NonExistingFunc')", 'E700:')
-  call assert_fails("tag axb123", 'E426:')
-  bw!
+  " set 'tagfunc' to a partial with dict. This used to cause a crash.
+  func SetTagFunc()
+    let params = {'tagfn': function('g:DictTagFunc')}
+    let &tagfunc = params.tagfn
+  endfunc
+  func g:DictTagFunc(_) dict
+  endfunc
+  call SetTagFunc()
+  new
+  call SetTagFunc()
+  bw
+  call test_garbagecollect_now()
+  new
+  set tagfunc=
+  wincmd w
+  set tagfunc=
+  :%bw!
+  delfunc g:DictTagFunc
+  delfunc SetTagFunc
 
   " Vim9 tests
   let lines =<< trim END
@@ -282,42 +305,11 @@ func Test_tagfunc_callback()
     g:Vim9tagFuncArgs = []
     assert_fails('tag a10', 'E433:')
     assert_equal([60, 'a10', '', {}], g:Vim9tagFuncArgs)
-
-    # Test for using a lambda
-    &tagfunc = (a, b, c) => MytagFunc1(61, a, b, c)
-    new | only
-    g:MytagFunc1_args = []
-    assert_fails('tag a20', 'E433:')
-    assert_equal([61, 'a20', '', {}], g:MytagFunc1_args)
-
-    # Test for using a string(lambda)
-    &tagfunc = '(a, b, c) => MytagFunc1(62, a, b, c)'
-    new | only
-    g:MytagFunc1_args = []
-    assert_fails('tag a20', 'E433:')
-    assert_equal([62, 'a20', '', {}], g:MytagFunc1_args)
-
-    # Test for using a variable with a lambda expression
-    var Fn: func = (a, b, c) => MytagFunc1(63, a, b, c)
-    &tagfunc = Fn
-    new | only
-    g:MytagFunc1_args = []
-    assert_fails('tag a30', 'E433:')
-    assert_equal([63, 'a30', '', {}], g:MytagFunc1_args)
-
-    # Test for using a variable with a lambda expression
-    Fn = (a, b, c) => MytagFunc1(64, a, b, c)
-    &tagfunc = string(Fn)
-    new | only
-    g:MytagFunc1_args = []
-    assert_fails('tag a30', 'E433:')
-    assert_equal([64, 'a30', '', {}], g:MytagFunc1_args)
   END
   " call CheckScriptSuccess(lines)
 
   " cleanup
   delfunc MytagFunc1
-  delfunc MytagFunc2
   set tagfunc&
   %bw!
 endfunc
diff --git a/src/nvim/testdir/vim9.vim b/src/nvim/testdir/vim9.vim
new file mode 100644
index 0000000000..db74ce3b11
--- /dev/null
+++ b/src/nvim/testdir/vim9.vim
@@ -0,0 +1,80 @@
+
+" Use a different file name for each run.
+let s:sequence = 1
+
+" Check that "lines" inside a legacy function has no error.
+func CheckLegacySuccess(lines)
+  let cwd = getcwd()
+  let fname = 'XlegacySuccess' .. s:sequence
+  let s:sequence += 1
+  call writefile(['func Func()'] + a:lines + ['endfunc'], fname)
+  try
+    exe 'so ' .. fname
+    call Func()
+  finally
+    delfunc! Func
+    call chdir(cwd)
+    call delete(fname)
+  endtry
+endfunc
+
+" Check that "lines" inside a legacy function results in the expected error
+func CheckLegacyFailure(lines, error)
+  let cwd = getcwd()
+  let fname = 'XlegacyFails' .. s:sequence
+  let s:sequence += 1
+  call writefile(['func Func()'] + a:lines + ['endfunc', 'call Func()'], fname)
+  try
+    call assert_fails('so ' .. fname, a:error)
+  finally
+    delfunc! Func
+    call chdir(cwd)
+    call delete(fname)
+  endtry
+endfunc
+
+" Execute "lines" in a legacy function, translated as in
+" CheckLegacyAndVim9Success()
+func CheckTransLegacySuccess(lines)
+  let legacylines = a:lines->deepcopy()->map({_, v ->
+                              \ v->substitute('\', 'let', 'g')
+                              \  ->substitute('\', 'let', 'g')
+                              \  ->substitute('\', '{', 'g')
+                              \  ->substitute('\', '->', 'g')
+                              \  ->substitute('\', '}', 'g')
+                              \  ->substitute('\', '1', 'g')
+                              \  ->substitute('\', '0', 'g')
+                              \  ->substitute('#"', ' "', 'g')
+                              \ })
+  call CheckLegacySuccess(legacylines)
+endfunc
+
+" Execute "lines" in a legacy function
+" Use 'VAR' for a declaration.
+" Use 'LET' for an assignment
+" Use ' #"' for a comment
+" Use LSTART arg LMIDDLE expr LEND for lambda
+" Use 'TRUE' for 1
+" Use 'FALSE' for 0
+func CheckLegacyAndVim9Success(lines)
+  call CheckTransLegacySuccess(a:lines)
+endfunc
+
+" Execute "lines" in a legacy function
+" Use 'VAR' for a declaration.
+" Use 'LET' for an assignment
+" Use ' #"' for a comment
+func CheckLegacyAndVim9Failure(lines, error)
+  if type(a:error) == type('string')
+    let legacyError = error
+  else
+    let legacyError = error[0]
+  endif
+
+  let legacylines = a:lines->deepcopy()->map({_, v ->
+                              \ v->substitute('\', 'let', 'g')
+                              \  ->substitute('\', 'let', 'g')
+                              \  ->substitute('#"', ' "', 'g')
+                              \ })
+  call CheckLegacyFailure(legacylines, legacyError)
+endfunc
-- 
cgit 


From be19990f30ed004f7f363f79ed05f23039bdfd07 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 7 Nov 2022 14:08:29 +0800
Subject: vim-patch:8.2.3792: setting *func options insufficiently tested

Problem:    Setting *func options insufficiently tested.
Solution:   Impove tests. (Yegappan Lakshmanan, closes vim/vim#9337)

https://github.com/vim/vim/commit/04ef1fb13d200f770952e670357dddadb6210dd4

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/testdir/test_ins_complete.vim | 400 ++++++++++++++++++---------------
 src/nvim/testdir/test_normal.vim       | 132 ++++++-----
 src/nvim/testdir/test_quickfix.vim     |   7 +
 src/nvim/testdir/test_tagfunc.vim      | 132 ++++++-----
 4 files changed, 373 insertions(+), 298 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim
index db2bb2e2a0..d788841833 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -1290,123 +1290,136 @@ endfunc
 
 " Test for different ways of setting the 'completefunc' option
 func Test_completefunc_callback()
-  func MycompleteFunc1(val, findstart, base)
-    call add(g:MycompleteFunc1_args, [a:val, a:findstart, a:base])
+  func CompleteFunc1(callnr, findstart, base)
+    call add(g:CompleteFunc1Args, [a:callnr, a:findstart, a:base])
+    return a:findstart ? 0 : []
+  endfunc
+  func CompleteFunc2(findstart, base)
+    call add(g:CompleteFunc2Args, [a:findstart, a:base])
     return a:findstart ? 0 : []
   endfunc
 
   let lines =<< trim END
+    #" Test for using a function name
+    LET &completefunc = 'g:CompleteFunc2'
+    new
+    call setline(1, 'zero')
+    LET g:CompleteFunc2Args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[1, ''], [0, 'zero']], g:CompleteFunc2Args)
+    bw!
+
     #" Test for using a function()
-    set completefunc=function('g:MycompleteFunc1',\ [10])
-    new | only
+    set completefunc=function('g:CompleteFunc1',\ [10])
+    new
     call setline(1, 'one')
-    LET g:MycompleteFunc1_args = []
+    LET g:CompleteFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[10, 1, ''], [10, 0, 'one']], g:MycompleteFunc1_args)
+    call assert_equal([[10, 1, ''], [10, 0, 'one']], g:CompleteFunc1Args)
     bw!
 
     #" Using a funcref variable to set 'completefunc'
-    VAR Fn = function('g:MycompleteFunc1', [11])
+    VAR Fn = function('g:CompleteFunc1', [11])
     LET &completefunc = Fn
-    new | only
+    new
     call setline(1, 'two')
-    LET g:MycompleteFunc1_args = []
+    LET g:CompleteFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[11, 1, ''], [11, 0, 'two']], g:MycompleteFunc1_args)
+    call assert_equal([[11, 1, ''], [11, 0, 'two']], g:CompleteFunc1Args)
     bw!
 
     #" Using string(funcref_variable) to set 'completefunc'
-    LET Fn = function('g:MycompleteFunc1', [12])
+    LET Fn = function('g:CompleteFunc1', [12])
     LET &completefunc = string(Fn)
-    new | only
+    new
     call setline(1, 'two')
-    LET g:MycompleteFunc1_args = []
+    LET g:CompleteFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[12, 1, ''], [12, 0, 'two']], g:MycompleteFunc1_args)
+    call assert_equal([[12, 1, ''], [12, 0, 'two']], g:CompleteFunc1Args)
     bw!
 
     #" Test for using a funcref()
-    set completefunc=funcref('g:MycompleteFunc1',\ [13])
-    new | only
+    set completefunc=funcref('g:CompleteFunc1',\ [13])
+    new
     call setline(1, 'three')
-    LET g:MycompleteFunc1_args = []
+    LET g:CompleteFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[13, 1, ''], [13, 0, 'three']], g:MycompleteFunc1_args)
+    call assert_equal([[13, 1, ''], [13, 0, 'three']], g:CompleteFunc1Args)
     bw!
 
     #" Using a funcref variable to set 'completefunc'
-    LET Fn = funcref('g:MycompleteFunc1', [14])
+    LET Fn = funcref('g:CompleteFunc1', [14])
     LET &completefunc = Fn
-    new | only
+    new
     call setline(1, 'four')
-    LET g:MycompleteFunc1_args = []
+    LET g:CompleteFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[14, 1, ''], [14, 0, 'four']], g:MycompleteFunc1_args)
+    call assert_equal([[14, 1, ''], [14, 0, 'four']], g:CompleteFunc1Args)
     bw!
 
     #" Using a string(funcref_variable) to set 'completefunc'
-    LET Fn = funcref('g:MycompleteFunc1', [15])
+    LET Fn = funcref('g:CompleteFunc1', [15])
     LET &completefunc = string(Fn)
-    new | only
+    new
     call setline(1, 'four')
-    LET g:MycompleteFunc1_args = []
+    LET g:CompleteFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[15, 1, ''], [15, 0, 'four']], g:MycompleteFunc1_args)
+    call assert_equal([[15, 1, ''], [15, 0, 'four']], g:CompleteFunc1Args)
     bw!
 
     #" Test for using a lambda function with set
-    VAR optval = "LSTART a, b LMIDDLE MycompleteFunc1(16, a, b) LEND"
+    VAR optval = "LSTART a, b LMIDDLE CompleteFunc1(16, a, b) LEND"
     LET optval = substitute(optval, ' ', '\\ ', 'g')
     exe "set completefunc=" .. optval
-    new | only
+    new
     call setline(1, 'five')
-    LET g:MycompleteFunc1_args = []
+    LET g:CompleteFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[16, 1, ''], [16, 0, 'five']], g:MycompleteFunc1_args)
+    call assert_equal([[16, 1, ''], [16, 0, 'five']], g:CompleteFunc1Args)
     bw!
 
     #" Set 'completefunc' to a lambda expression
-    LET &completefunc = LSTART a, b LMIDDLE MycompleteFunc1(17, a, b) LEND
-    new | only
+    LET &completefunc = LSTART a, b LMIDDLE CompleteFunc1(17, a, b) LEND
+    new
     call setline(1, 'six')
-    LET g:MycompleteFunc1_args = []
+    LET g:CompleteFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[17, 1, ''], [17, 0, 'six']], g:MycompleteFunc1_args)
+    call assert_equal([[17, 1, ''], [17, 0, 'six']], g:CompleteFunc1Args)
     bw!
 
     #" Set 'completefunc' to string(lambda_expression)
-    LET &completefunc = 'LSTART a, b LMIDDLE MycompleteFunc1(18, a, b) LEND'
-    new | only
+    LET &completefunc = 'LSTART a, b LMIDDLE CompleteFunc1(18, a, b) LEND'
+    new
     call setline(1, 'six')
-    LET g:MycompleteFunc1_args = []
+    LET g:CompleteFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[18, 1, ''], [18, 0, 'six']], g:MycompleteFunc1_args)
+    call assert_equal([[18, 1, ''], [18, 0, 'six']], g:CompleteFunc1Args)
     bw!
 
     #" Set 'completefunc' to a variable with a lambda expression
-    VAR Lambda = LSTART a, b LMIDDLE MycompleteFunc1(19, a, b) LEND
+    VAR Lambda = LSTART a, b LMIDDLE CompleteFunc1(19, a, b) LEND
     LET &completefunc = Lambda
-    new | only
+    new
     call setline(1, 'seven')
-    LET g:MycompleteFunc1_args = []
+    LET g:CompleteFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:MycompleteFunc1_args)
+    call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:CompleteFunc1Args)
     bw!
 
     #" Set 'completefunc' to a string(variable with a lambda expression)
-    LET Lambda = LSTART a, b LMIDDLE MycompleteFunc1(20, a, b) LEND
+    LET Lambda = LSTART a, b LMIDDLE CompleteFunc1(20, a, b) LEND
     LET &completefunc = string(Lambda)
-    new | only
+    new
     call setline(1, 'seven')
-    LET g:MycompleteFunc1_args = []
+    LET g:CompleteFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:MycompleteFunc1_args)
+    call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:CompleteFunc1Args)
     bw!
 
     #" Test for using a lambda function with incorrect return value
     LET Lambda = LSTART a, b LMIDDLE strlen(a) LEND
     LET &completefunc = Lambda
-    new | only
+    new
     call setline(1, 'eight')
     call feedkeys("A\\\", 'x')
     bw!
@@ -1418,17 +1431,13 @@ func Test_completefunc_callback()
     call assert_fails("set completefunc=funcref('abc')", "E700:")
 
     #" set 'completefunc' to a non-existing function
-    func MycompleteFunc2(findstart, base)
-      call add(g:MycompleteFunc2_args, [a:findstart, a:base])
-      return a:findstart ? 0 : []
-    endfunc
-    set completefunc=MycompleteFunc2
+    set completefunc=CompleteFunc2
     call setline(1, 'five')
     call assert_fails("set completefunc=function('NonExistingFunc')", 'E700:')
     call assert_fails("LET &completefunc = function('NonExistingFunc')", 'E700:')
-    LET g:MycompleteFunc2_args = []
+    LET g:CompleteFunc2Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[1, ''], [0, 'five']], g:MycompleteFunc2_args)
+    call assert_equal([[1, ''], [0, 'five']], g:CompleteFunc2Args)
     bw!
   END
   call CheckLegacyAndVim9Success(lines)
@@ -1437,11 +1446,11 @@ func Test_completefunc_callback()
   call feedkeys("A\\\", 'x')
 
   " Using Vim9 lambda expression in legacy context should fail
-  " set completefunc=(a,\ b)\ =>\ MycompleteFunc1(21,\ a,\ b)
+  " set completefunc=(a,\ b)\ =>\ CompleteFunc1(21,\ a,\ b)
   new | only
-  let g:MycompleteFunc1_args = []
+  let g:CompleteFunc1Args = []
   " call assert_fails('call feedkeys("A\\\", "x")', 'E117:')
-  call assert_equal([], g:MycompleteFunc1_args)
+  call assert_equal([], g:CompleteFunc1Args)
 
   " set 'completefunc' to a partial with dict. This used to cause a crash.
   func SetCompleteFunc()
@@ -1483,131 +1492,145 @@ func Test_completefunc_callback()
   " call CheckScriptSuccess(lines)
 
   " cleanup
-  delfunc MycompleteFunc1
-  delfunc MycompleteFunc2
   set completefunc&
+  delfunc CompleteFunc1
+  delfunc CompleteFunc2
+  unlet g:CompleteFunc1Args g:CompleteFunc2Args
   %bw!
 endfunc
 
 " Test for different ways of setting the 'omnifunc' option
 func Test_omnifunc_callback()
-  func MyomniFunc1(val, findstart, base)
-    call add(g:MyomniFunc1_args, [a:val, a:findstart, a:base])
+  func OmniFunc1(callnr, findstart, base)
+    call add(g:OmniFunc1Args, [a:callnr, a:findstart, a:base])
+    return a:findstart ? 0 : []
+  endfunc
+  func OmniFunc2(findstart, base)
+    call add(g:OmniFunc2Args, [a:findstart, a:base])
     return a:findstart ? 0 : []
   endfunc
 
   let lines =<< trim END
+    #" Test for using a function name
+    LET &omnifunc = 'g:OmniFunc2'
+    new
+    call setline(1, 'zero')
+    LET g:OmniFunc2Args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[1, ''], [0, 'zero']], g:OmniFunc2Args)
+    bw!
+
     #" Test for using a function()
-    set omnifunc=function('g:MyomniFunc1',\ [10])
-    new | only
+    set omnifunc=function('g:OmniFunc1',\ [10])
+    new
     call setline(1, 'one')
-    LET g:MyomniFunc1_args = []
+    LET g:OmniFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[10, 1, ''], [10, 0, 'one']], g:MyomniFunc1_args)
+    call assert_equal([[10, 1, ''], [10, 0, 'one']], g:OmniFunc1Args)
     bw!
 
     #" Using a funcref variable to set 'omnifunc'
-    VAR Fn = function('g:MyomniFunc1', [11])
+    VAR Fn = function('g:OmniFunc1', [11])
     LET &omnifunc = Fn
-    new | only
+    new
     call setline(1, 'two')
-    LET g:MyomniFunc1_args = []
+    LET g:OmniFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[11, 1, ''], [11, 0, 'two']], g:MyomniFunc1_args)
+    call assert_equal([[11, 1, ''], [11, 0, 'two']], g:OmniFunc1Args)
     bw!
 
     #" Using a string(funcref_variable) to set 'omnifunc'
-    LET Fn = function('g:MyomniFunc1', [12])
+    LET Fn = function('g:OmniFunc1', [12])
     LET &omnifunc = string(Fn)
-    new | only
+    new
     call setline(1, 'two')
-    LET g:MyomniFunc1_args = []
+    LET g:OmniFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[12, 1, ''], [12, 0, 'two']], g:MyomniFunc1_args)
+    call assert_equal([[12, 1, ''], [12, 0, 'two']], g:OmniFunc1Args)
     bw!
 
     #" Test for using a funcref()
-    set omnifunc=funcref('g:MyomniFunc1',\ [13])
-    new | only
+    set omnifunc=funcref('g:OmniFunc1',\ [13])
+    new
     call setline(1, 'three')
-    LET g:MyomniFunc1_args = []
+    LET g:OmniFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[13, 1, ''], [13, 0, 'three']], g:MyomniFunc1_args)
+    call assert_equal([[13, 1, ''], [13, 0, 'three']], g:OmniFunc1Args)
     bw!
 
     #" Use let to set 'omnifunc' to a funcref
-    LET Fn = funcref('g:MyomniFunc1', [14])
+    LET Fn = funcref('g:OmniFunc1', [14])
     LET &omnifunc = Fn
-    new | only
+    new
     call setline(1, 'four')
-    LET g:MyomniFunc1_args = []
+    LET g:OmniFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[14, 1, ''], [14, 0, 'four']], g:MyomniFunc1_args)
+    call assert_equal([[14, 1, ''], [14, 0, 'four']], g:OmniFunc1Args)
     bw!
 
     #" Using a string(funcref) to set 'omnifunc'
-    LET Fn = funcref("g:MyomniFunc1", [15])
+    LET Fn = funcref("g:OmniFunc1", [15])
     LET &omnifunc = string(Fn)
-    new | only
+    new
     call setline(1, 'four')
-    LET g:MyomniFunc1_args = []
+    LET g:OmniFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[15, 1, ''], [15, 0, 'four']], g:MyomniFunc1_args)
+    call assert_equal([[15, 1, ''], [15, 0, 'four']], g:OmniFunc1Args)
     bw!
 
     #" Test for using a lambda function with set
-    VAR optval = "LSTART a, b LMIDDLE MyomniFunc1(16, a, b) LEND"
+    VAR optval = "LSTART a, b LMIDDLE OmniFunc1(16, a, b) LEND"
     LET optval = substitute(optval, ' ', '\\ ', 'g')
     exe "set omnifunc=" .. optval
-    new | only
+    new
     call setline(1, 'five')
-    LET g:MyomniFunc1_args = []
+    LET g:OmniFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[16, 1, ''], [16, 0, 'five']], g:MyomniFunc1_args)
+    call assert_equal([[16, 1, ''], [16, 0, 'five']], g:OmniFunc1Args)
     bw!
 
     #" Set 'omnifunc' to a lambda expression
-    LET &omnifunc = LSTART a, b LMIDDLE MyomniFunc1(17, a, b) LEND
-    new | only
+    LET &omnifunc = LSTART a, b LMIDDLE OmniFunc1(17, a, b) LEND
+    new
     call setline(1, 'six')
-    LET g:MyomniFunc1_args = []
+    LET g:OmniFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[17, 1, ''], [17, 0, 'six']], g:MyomniFunc1_args)
+    call assert_equal([[17, 1, ''], [17, 0, 'six']], g:OmniFunc1Args)
     bw!
 
     #" Set 'omnifunc' to a string(lambda_expression)
-    LET &omnifunc = 'LSTART a, b LMIDDLE MyomniFunc1(18, a, b) LEND'
-    new | only
+    LET &omnifunc = 'LSTART a, b LMIDDLE OmniFunc1(18, a, b) LEND'
+    new
     call setline(1, 'six')
-    LET g:MyomniFunc1_args = []
+    LET g:OmniFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[18, 1, ''], [18, 0, 'six']], g:MyomniFunc1_args)
+    call assert_equal([[18, 1, ''], [18, 0, 'six']], g:OmniFunc1Args)
     bw!
 
     #" Set 'omnifunc' to a variable with a lambda expression
-    VAR Lambda = LSTART a, b LMIDDLE MyomniFunc1(19, a, b) LEND
+    VAR Lambda = LSTART a, b LMIDDLE OmniFunc1(19, a, b) LEND
     LET &omnifunc = Lambda
-    new | only
+    new
     call setline(1, 'seven')
-    LET g:MyomniFunc1_args = []
+    LET g:OmniFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:MyomniFunc1_args)
+    call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:OmniFunc1Args)
     bw!
 
     #" Set 'omnifunc' to a string(variable with a lambda expression)
-    LET Lambda = LSTART a, b LMIDDLE MyomniFunc1(20, a, b) LEND
+    LET Lambda = LSTART a, b LMIDDLE OmniFunc1(20, a, b) LEND
     LET &omnifunc = string(Lambda)
-    new | only
+    new
     call setline(1, 'seven')
-    LET g:MyomniFunc1_args = []
+    LET g:OmniFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:MyomniFunc1_args)
+    call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:OmniFunc1Args)
     bw!
 
     #" Test for using a lambda function with incorrect return value
     LET Lambda = LSTART a, b LMIDDLE strlen(a) LEND
     LET &omnifunc = Lambda
-    new | only
+    new
     call setline(1, 'eight')
     call feedkeys("A\\\", 'x')
     bw!
@@ -1619,17 +1642,13 @@ func Test_omnifunc_callback()
     call assert_fails("set omnifunc=funcref('abc')", "E700:")
 
     #" set 'omnifunc' to a non-existing function
-    func MyomniFunc2(findstart, base)
-      call add(g:MyomniFunc2_args, [a:findstart, a:base])
-      return a:findstart ? 0 : []
-    endfunc
-    set omnifunc=MyomniFunc2
+    set omnifunc=OmniFunc2
     call setline(1, 'nine')
     call assert_fails("set omnifunc=function('NonExistingFunc')", 'E700:')
     call assert_fails("LET &omnifunc = function('NonExistingFunc')", 'E700:')
-    LET g:MyomniFunc2_args = []
+    LET g:OmniFunc2Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[1, ''], [0, 'nine']], g:MyomniFunc2_args)
+    call assert_equal([[1, ''], [0, 'nine']], g:OmniFunc2Args)
     bw!
   END
   call CheckLegacyAndVim9Success(lines)
@@ -1638,11 +1657,11 @@ func Test_omnifunc_callback()
   call feedkeys("A\\\", 'x')
 
   " Using Vim9 lambda expression in legacy context should fail
-  " set omnifunc=(a,\ b)\ =>\ MyomniFunc1(21,\ a,\ b)
+  " set omnifunc=(a,\ b)\ =>\ OmniFunc1(21,\ a,\ b)
   new | only
-  let g:MyomniFunc1_args = []
+  let g:OmniFunc1Args = []
   " call assert_fails('call feedkeys("A\\\", "x")', 'E117:')
-  call assert_equal([], g:MyomniFunc1_args)
+  call assert_equal([], g:OmniFunc1Args)
 
   " set 'omnifunc' to a partial with dict. This used to cause a crash.
   func SetOmniFunc()
@@ -1684,131 +1703,145 @@ func Test_omnifunc_callback()
   " call CheckScriptSuccess(lines)
 
   " cleanup
-  delfunc MyomniFunc1
-  delfunc MyomniFunc2
   set omnifunc&
+  delfunc OmniFunc1
+  delfunc OmniFunc2
+  unlet g:OmniFunc1Args g:OmniFunc2Args
   %bw!
 endfunc
 
 " Test for different ways of setting the 'thesaurusfunc' option
 func Test_thesaurusfunc_callback()
-  func MytsrFunc1(val, findstart, base)
-    call add(g:MytsrFunc1_args, [a:val, a:findstart, a:base])
+  func TsrFunc1(callnr, findstart, base)
+    call add(g:TsrFunc1Args, [a:callnr, a:findstart, a:base])
     return a:findstart ? 0 : []
   endfunc
+  func TsrFunc2(findstart, base)
+    call add(g:TsrFunc2Args, [a:findstart, a:base])
+    return a:findstart ? 0 : ['sunday']
+  endfunc
 
   let lines =<< trim END
+    #" Test for using a function name
+    LET &thesaurusfunc = 'g:TsrFunc2'
+    new
+    call setline(1, 'zero')
+    LET g:TsrFunc2Args = []
+    call feedkeys("A\\\", 'x')
+    call assert_equal([[1, ''], [0, 'zero']], g:TsrFunc2Args)
+    bw!
+
     #" Test for using a function()
-    set thesaurusfunc=function('g:MytsrFunc1',\ [10])
-    new | only
+    set thesaurusfunc=function('g:TsrFunc1',\ [10])
+    new
     call setline(1, 'one')
-    LET g:MytsrFunc1_args = []
+    LET g:TsrFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[10, 1, ''], [10, 0, 'one']], g:MytsrFunc1_args)
+    call assert_equal([[10, 1, ''], [10, 0, 'one']], g:TsrFunc1Args)
     bw!
 
     #" Using a funcref variable to set 'thesaurusfunc'
-    VAR Fn = function('g:MytsrFunc1', [11])
+    VAR Fn = function('g:TsrFunc1', [11])
     LET &thesaurusfunc = Fn
-    new | only
+    new
     call setline(1, 'two')
-    LET g:MytsrFunc1_args = []
+    LET g:TsrFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[11, 1, ''], [11, 0, 'two']], g:MytsrFunc1_args)
+    call assert_equal([[11, 1, ''], [11, 0, 'two']], g:TsrFunc1Args)
     bw!
 
     #" Using a string(funcref_variable) to set 'thesaurusfunc'
-    LET Fn = function('g:MytsrFunc1', [12])
+    LET Fn = function('g:TsrFunc1', [12])
     LET &thesaurusfunc = string(Fn)
-    new | only
+    new
     call setline(1, 'two')
-    LET g:MytsrFunc1_args = []
+    LET g:TsrFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[12, 1, ''], [12, 0, 'two']], g:MytsrFunc1_args)
+    call assert_equal([[12, 1, ''], [12, 0, 'two']], g:TsrFunc1Args)
     bw!
 
     #" Test for using a funcref()
-    set thesaurusfunc=funcref('g:MytsrFunc1',\ [13])
-    new | only
+    set thesaurusfunc=funcref('g:TsrFunc1',\ [13])
+    new
     call setline(1, 'three')
-    LET g:MytsrFunc1_args = []
+    LET g:TsrFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[13, 1, ''], [13, 0, 'three']], g:MytsrFunc1_args)
+    call assert_equal([[13, 1, ''], [13, 0, 'three']], g:TsrFunc1Args)
     bw!
 
     #" Using a funcref variable to set 'thesaurusfunc'
-    LET Fn = funcref('g:MytsrFunc1', [14])
+    LET Fn = funcref('g:TsrFunc1', [14])
     LET &thesaurusfunc = Fn
-    new | only
+    new
     call setline(1, 'four')
-    LET g:MytsrFunc1_args = []
+    LET g:TsrFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[14, 1, ''], [14, 0, 'four']], g:MytsrFunc1_args)
+    call assert_equal([[14, 1, ''], [14, 0, 'four']], g:TsrFunc1Args)
     bw!
 
     #" Using a string(funcref_variable) to set 'thesaurusfunc'
-    LET Fn = funcref('g:MytsrFunc1', [15])
+    LET Fn = funcref('g:TsrFunc1', [15])
     LET &thesaurusfunc = string(Fn)
-    new | only
+    new
     call setline(1, 'four')
-    LET g:MytsrFunc1_args = []
+    LET g:TsrFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[15, 1, ''], [15, 0, 'four']], g:MytsrFunc1_args)
+    call assert_equal([[15, 1, ''], [15, 0, 'four']], g:TsrFunc1Args)
     bw!
 
     #" Test for using a lambda function
-    VAR optval = "LSTART a, b LMIDDLE MytsrFunc1(16, a, b) LEND"
+    VAR optval = "LSTART a, b LMIDDLE TsrFunc1(16, a, b) LEND"
     LET optval = substitute(optval, ' ', '\\ ', 'g')
     exe "set thesaurusfunc=" .. optval
-    new | only
+    new
     call setline(1, 'five')
-    LET g:MytsrFunc1_args = []
+    LET g:TsrFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[16, 1, ''], [16, 0, 'five']], g:MytsrFunc1_args)
+    call assert_equal([[16, 1, ''], [16, 0, 'five']], g:TsrFunc1Args)
     bw!
 
     #" Test for using a lambda function with set
-    LET &thesaurusfunc = LSTART a, b LMIDDLE MytsrFunc1(17, a, b) LEND
-    new | only
+    LET &thesaurusfunc = LSTART a, b LMIDDLE TsrFunc1(17, a, b) LEND
+    new
     call setline(1, 'six')
-    LET g:MytsrFunc1_args = []
+    LET g:TsrFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[17, 1, ''], [17, 0, 'six']], g:MytsrFunc1_args)
+    call assert_equal([[17, 1, ''], [17, 0, 'six']], g:TsrFunc1Args)
     bw!
 
     #" Set 'thesaurusfunc' to a string(lambda expression)
-    LET &thesaurusfunc = 'LSTART a, b LMIDDLE MytsrFunc1(18, a, b) LEND'
-    new | only
+    LET &thesaurusfunc = 'LSTART a, b LMIDDLE TsrFunc1(18, a, b) LEND'
+    new
     call setline(1, 'six')
-    LET g:MytsrFunc1_args = []
+    LET g:TsrFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[18, 1, ''], [18, 0, 'six']], g:MytsrFunc1_args)
+    call assert_equal([[18, 1, ''], [18, 0, 'six']], g:TsrFunc1Args)
     bw!
 
     #" Set 'thesaurusfunc' to a variable with a lambda expression
-    VAR Lambda = LSTART a, b LMIDDLE MytsrFunc1(19, a, b) LEND
+    VAR Lambda = LSTART a, b LMIDDLE TsrFunc1(19, a, b) LEND
     LET &thesaurusfunc = Lambda
-    new | only
+    new
     call setline(1, 'seven')
-    LET g:MytsrFunc1_args = []
+    LET g:TsrFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:MytsrFunc1_args)
+    call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:TsrFunc1Args)
     bw!
 
     #" Set 'thesaurusfunc' to a string(variable with a lambda expression)
-    LET Lambda = LSTART a, b LMIDDLE MytsrFunc1(20, a, b) LEND
+    LET Lambda = LSTART a, b LMIDDLE TsrFunc1(20, a, b) LEND
     LET &thesaurusfunc = string(Lambda)
-    new | only
+    new
     call setline(1, 'seven')
-    LET g:MytsrFunc1_args = []
+    LET g:TsrFunc1Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:MytsrFunc1_args)
+    call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:TsrFunc1Args)
     bw!
 
     #" Test for using a lambda function with incorrect return value
     LET Lambda = LSTART a, b LMIDDLE strlen(a) LEND
     LET &thesaurusfunc = Lambda
-    new | only
+    new
     call setline(1, 'eight')
     call feedkeys("A\\\", 'x')
     bw!
@@ -1820,40 +1853,36 @@ func Test_thesaurusfunc_callback()
     call assert_fails("set thesaurusfunc=funcref('abc')", "E700:")
 
     #" set 'thesaurusfunc' to a non-existing function
-    func MytsrFunc2(findstart, base)
-      call add(g:MytsrFunc2_args, [a:findstart, a:base])
-      return a:findstart ? 0 : ['sunday']
-    endfunc
-    set thesaurusfunc=MytsrFunc2
+    set thesaurusfunc=TsrFunc2
     call setline(1, 'ten')
     call assert_fails("set thesaurusfunc=function('NonExistingFunc')", 'E700:')
     call assert_fails("LET &thesaurusfunc = function('NonExistingFunc')", 'E700:')
-    LET g:MytsrFunc2_args = []
+    LET g:TsrFunc2Args = []
     call feedkeys("A\\\", 'x')
-    call assert_equal([[1, ''], [0, 'ten']], g:MytsrFunc2_args)
+    call assert_equal([[1, ''], [0, 'ten']], g:TsrFunc2Args)
     bw!
 
     #" Use a buffer-local value and a global value
     set thesaurusfunc&
-    setlocal thesaurusfunc=function('g:MytsrFunc1',\ [22])
+    setlocal thesaurusfunc=function('g:TsrFunc1',\ [22])
     call setline(1, 'sun')
-    LET g:MytsrFunc1_args = []
+    LET g:TsrFunc1Args = []
     call feedkeys("A\\\", "x")
     call assert_equal('sun', getline(1))
-    call assert_equal([[22, 1, ''], [22, 0, 'sun']], g:MytsrFunc1_args)
+    call assert_equal([[22, 1, ''], [22, 0, 'sun']], g:TsrFunc1Args)
     new
     call setline(1, 'sun')
-    LET g:MytsrFunc1_args = []
+    LET g:TsrFunc1Args = []
     call feedkeys("A\\\", "x")
     call assert_equal('sun', getline(1))
-    call assert_equal([], g:MytsrFunc1_args)
-    set thesaurusfunc=function('g:MytsrFunc1',\ [23])
+    call assert_equal([], g:TsrFunc1Args)
+    set thesaurusfunc=function('g:TsrFunc1',\ [23])
     wincmd w
     call setline(1, 'sun')
-    LET g:MytsrFunc1_args = []
+    LET g:TsrFunc1Args = []
     call feedkeys("A\\\", "x")
     call assert_equal('sun', getline(1))
-    call assert_equal([[22, 1, ''], [22, 0, 'sun']], g:MytsrFunc1_args)
+    call assert_equal([[22, 1, ''], [22, 0, 'sun']], g:TsrFunc1Args)
     :%bw!
   END
   call CheckLegacyAndVim9Success(lines)
@@ -1862,11 +1891,11 @@ func Test_thesaurusfunc_callback()
   call feedkeys("A\\\", 'x')
 
   " Using Vim9 lambda expression in legacy context should fail
-  " set thesaurusfunc=(a,\ b)\ =>\ MytsrFunc1(21,\ a,\ b)
+  " set thesaurusfunc=(a,\ b)\ =>\ TsrFunc1(21,\ a,\ b)
   new | only
-  let g:MytsrFunc1_args = []
+  let g:TsrFunc1Args = []
   " call assert_fails('call feedkeys("A\\\", "x")', 'E117:')
-  call assert_equal([], g:MytsrFunc1_args)
+  call assert_equal([], g:TsrFunc1Args)
   bw!
 
   " set 'thesaurusfunc' to a partial with dict. This used to cause a crash.
@@ -1922,8 +1951,9 @@ func Test_thesaurusfunc_callback()
 
   " cleanup
   set thesaurusfunc&
-  delfunc MytsrFunc1
-  delfunc MytsrFunc2
+  delfunc TsrFunc1
+  delfunc TsrFunc2
+  unlet g:TsrFunc1Args g:TsrFunc2Args
   %bw!
 endfunc
 
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index b3e0be8f77..0d75644920 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -460,110 +460,120 @@ endfunc
 " Test for different ways of setting the 'operatorfunc' option
 func Test_opfunc_callback()
   new
-  func MyopFunc(val, type)
-    let g:OpFuncArgs = [a:val, a:type]
+  func OpFunc1(callnr, type)
+    let g:OpFunc1Args = [a:callnr, a:type]
+  endfunc
+  func OpFunc2(type)
+    let g:OpFunc2Args = [a:type]
   endfunc
 
   let lines =<< trim END
+    #" Test for using a function name
+    LET &opfunc = 'g:OpFunc2'
+    LET g:OpFunc2Args = []
+    normal! g@l
+    call assert_equal(['char'], g:OpFunc2Args)
+
     #" Test for using a function()
-    set opfunc=function('g:MyopFunc',\ [10])
-    LET g:OpFuncArgs = []
+    set opfunc=function('g:OpFunc1',\ [10])
+    LET g:OpFunc1Args = []
     normal! g@l
-    call assert_equal([10, 'char'], g:OpFuncArgs)
+    call assert_equal([10, 'char'], g:OpFunc1Args)
 
     #" Using a funcref variable to set 'operatorfunc'
-    VAR Fn = function('g:MyopFunc', [11])
+    VAR Fn = function('g:OpFunc1', [11])
     LET &opfunc = Fn
-    LET g:OpFuncArgs = []
+    LET g:OpFunc1Args = []
     normal! g@l
-    call assert_equal([11, 'char'], g:OpFuncArgs)
+    call assert_equal([11, 'char'], g:OpFunc1Args)
 
     #" Using a string(funcref_variable) to set 'operatorfunc'
-    LET Fn = function('g:MyopFunc', [12])
+    LET Fn = function('g:OpFunc1', [12])
     LET &operatorfunc = string(Fn)
-    LET g:OpFuncArgs = []
+    LET g:OpFunc1Args = []
     normal! g@l
-    call assert_equal([12, 'char'], g:OpFuncArgs)
+    call assert_equal([12, 'char'], g:OpFunc1Args)
 
     #" Test for using a funcref()
-    set operatorfunc=funcref('g:MyopFunc',\ [13])
-    LET g:OpFuncArgs = []
+    set operatorfunc=funcref('g:OpFunc1',\ [13])
+    LET g:OpFunc1Args = []
     normal! g@l
-    call assert_equal([13, 'char'], g:OpFuncArgs)
+    call assert_equal([13, 'char'], g:OpFunc1Args)
 
     #" Using a funcref variable to set 'operatorfunc'
-    LET Fn = funcref('g:MyopFunc', [14])
+    LET Fn = funcref('g:OpFunc1', [14])
     LET &opfunc = Fn
-    LET g:OpFuncArgs = []
+    LET g:OpFunc1Args = []
     normal! g@l
-    call assert_equal([14, 'char'], g:OpFuncArgs)
+    call assert_equal([14, 'char'], g:OpFunc1Args)
 
     #" Using a string(funcref_variable) to set 'operatorfunc'
-    LET Fn = funcref('g:MyopFunc', [15])
+    LET Fn = funcref('g:OpFunc1', [15])
     LET &opfunc = string(Fn)
-    LET g:OpFuncArgs = []
+    LET g:OpFunc1Args = []
     normal! g@l
-    call assert_equal([15, 'char'], g:OpFuncArgs)
+    call assert_equal([15, 'char'], g:OpFunc1Args)
 
     #" Test for using a lambda function using set
-    VAR optval = "LSTART a LMIDDLE MyopFunc(16, a) LEND"
+    VAR optval = "LSTART a LMIDDLE OpFunc1(16, a) LEND"
     LET optval = substitute(optval, ' ', '\\ ', 'g')
     exe "set opfunc=" .. optval
-    LET g:OpFuncArgs = []
+    LET g:OpFunc1Args = []
     normal! g@l
-    call assert_equal([16, 'char'], g:OpFuncArgs)
+    call assert_equal([16, 'char'], g:OpFunc1Args)
 
     #" Test for using a lambda function using LET
-    LET &opfunc = LSTART a LMIDDLE MyopFunc(17, a) LEND
-    LET g:OpFuncArgs = []
+    LET &opfunc = LSTART a LMIDDLE OpFunc1(17, a) LEND
+    LET g:OpFunc1Args = []
     normal! g@l
-    call assert_equal([17, 'char'], g:OpFuncArgs)
+    call assert_equal([17, 'char'], g:OpFunc1Args)
 
     #" Set 'operatorfunc' to a string(lambda expression)
-    LET &opfunc = 'LSTART a LMIDDLE MyopFunc(18, a) LEND'
-    LET g:OpFuncArgs = []
+    LET &opfunc = 'LSTART a LMIDDLE OpFunc1(18, a) LEND'
+    LET g:OpFunc1Args = []
     normal! g@l
-    call assert_equal([18, 'char'], g:OpFuncArgs)
+    call assert_equal([18, 'char'], g:OpFunc1Args)
 
     #" Set 'operatorfunc' to a variable with a lambda expression
-    VAR Lambda = LSTART a LMIDDLE MyopFunc(19, a) LEND
+    VAR Lambda = LSTART a LMIDDLE OpFunc1(19, a) LEND
     LET &opfunc = Lambda
-    LET g:OpFuncArgs = []
+    LET g:OpFunc1Args = []
     normal! g@l
-    call assert_equal([19, 'char'], g:OpFuncArgs)
+    call assert_equal([19, 'char'], g:OpFunc1Args)
 
     #" Set 'operatorfunc' to a string(variable with a lambda expression)
-    LET Lambda = LSTART a LMIDDLE MyopFunc(20, a) LEND
+    LET Lambda = LSTART a LMIDDLE OpFunc1(20, a) LEND
     LET &opfunc = string(Lambda)
-    LET g:OpFuncArgs = []
+    LET g:OpFunc1Args = []
     normal! g@l
-    call assert_equal([20, 'char'], g:OpFuncArgs)
+    call assert_equal([20, 'char'], g:OpFunc1Args)
 
     #" Try to use 'operatorfunc' after the function is deleted
-    func g:TmpOpFunc(type)
-      LET g:OpFuncArgs = [21, a:type]
+    func g:TmpOpFunc1(type)
+      let g:TmpOpFunc1Args = [21, a:type]
     endfunc
-    LET &opfunc = function('g:TmpOpFunc')
-    delfunc g:TmpOpFunc
+    LET &opfunc = function('g:TmpOpFunc1')
+    delfunc g:TmpOpFunc1
     call test_garbagecollect_now()
-    LET g:OpFuncArgs = []
+    LET g:TmpOpFunc1Args = []
     call assert_fails('normal! g@l', 'E117:')
-    call assert_equal([], g:OpFuncArgs)
+    call assert_equal([], g:TmpOpFunc1Args)
 
     #" Try to use a function with two arguments for 'operatorfunc'
-    func MyopFunc2(x, y)
-      LET g:OpFuncArgs = [a:x, a:y]
+    func g:TmpOpFunc2(x, y)
+      let g:TmpOpFunc2Args = [a:x, a:y]
     endfunc
-    set opfunc=MyopFunc2
-    LET g:OpFuncArgs = []
+    set opfunc=TmpOpFunc2
+    LET g:TmpOpFunc2Args = []
     call assert_fails('normal! g@l', 'E119:')
-    call assert_equal([], g:OpFuncArgs)
+    call assert_equal([], g:TmpOpFunc2Args)
+    delfunc TmpOpFunc2
 
     #" Try to use a lambda function with two arguments for 'operatorfunc'
-    LET &opfunc = LSTART a, b LMIDDLE MyopFunc(22, b) LEND
-    LET g:OpFuncArgs = []
+    LET &opfunc = LSTART a, b LMIDDLE OpFunc1(22, b) LEND
+    LET g:OpFunc1Args = []
     call assert_fails('normal! g@l', 'E119:')
-    call assert_equal([], g:OpFuncArgs)
+    call assert_equal([], g:OpFunc1Args)
 
     #" Test for clearing the 'operatorfunc' option
     set opfunc=''
@@ -572,20 +582,20 @@ func Test_opfunc_callback()
     call assert_fails("set opfunc=funcref('abc')", "E700:")
 
     #" set 'operatorfunc' to a non-existing function
-    LET &opfunc = function('g:MyopFunc', [23])
+    LET &opfunc = function('g:OpFunc1', [23])
     call assert_fails("set opfunc=function('NonExistingFunc')", 'E700:')
     call assert_fails("LET &opfunc = function('NonExistingFunc')", 'E700:')
-    LET g:OpFuncArgs = []
+    LET g:OpFunc1Args = []
     normal! g@l
-    call assert_equal([23, 'char'], g:OpFuncArgs)
+    call assert_equal([23, 'char'], g:OpFunc1Args)
   END
   call CheckTransLegacySuccess(lines)
 
   " Using Vim9 lambda expression in legacy context should fail
-  " set opfunc=(a)\ =>\ MyopFunc(24,\ a)
-  let g:OpFuncArgs = []
+  " set opfunc=(a)\ =>\ OpFunc1(24,\ a)
+  let g:OpFunc1Args = []
   " call assert_fails('normal! g@l', 'E117:')
-  call assert_equal([], g:OpFuncArgs)
+  call assert_equal([], g:OpFunc1Args)
 
   " set 'operatorfunc' to a partial with dict. This used to cause a crash.
   func SetOpFunc()
@@ -606,20 +616,20 @@ func Test_opfunc_callback()
 
     # Test for using a def function with opfunc
     def g:Vim9opFunc(val: number, type: string): void
-      g:OpFuncArgs = [val, type]
+      g:OpFunc1Args = [val, type]
     enddef
     set opfunc=function('g:Vim9opFunc',\ [60])
-    g:OpFuncArgs = []
+    g:OpFunc1Args = []
     normal! g@l
-    assert_equal([60, 'char'], g:OpFuncArgs)
+    assert_equal([60, 'char'], g:OpFunc1Args)
   END
   " call CheckScriptSuccess(lines)
 
   " cleanup
   set opfunc&
-  delfunc MyopFunc
-  delfunc MyopFunc2
-  unlet g:OpFuncArgs
+  delfunc OpFunc1
+  delfunc OpFunc2
+  unlet g:OpFunc1Args g:OpFunc2Args
   %bw!
 endfunc
 
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index a18d6fd6ab..42bdb7bad1 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -5695,6 +5695,13 @@ func Test_qftextfunc_callback()
   let lines =<< trim END
     set efm=%f:%l:%c:%m
 
+    #" Test for using a function name
+    LET &qftf = 'g:Tqfexpr'
+    cexpr "F0:0:0:L0"
+    copen
+    call assert_equal('F0-L0C0-L0', getline(1))
+    cclose
+
     #" Test for using a function()
     set qftf=function('g:Tqfexpr')
     cexpr "F1:1:1:L1"
diff --git a/src/nvim/testdir/test_tagfunc.vim b/src/nvim/testdir/test_tagfunc.vim
index 827159e3c8..7a88723bc0 100644
--- a/src/nvim/testdir/test_tagfunc.vim
+++ b/src/nvim/testdir/test_tagfunc.vim
@@ -127,102 +127,126 @@ endfunc
 
 " Test for different ways of setting the 'tagfunc' option
 func Test_tagfunc_callback()
-  func MytagFunc1(val, pat, flags, info)
-    let g:MytagFunc1_args = [a:val, a:pat, a:flags, a:info]
+  func TagFunc1(callnr, pat, flags, info)
+    let g:TagFunc1Args = [a:callnr, a:pat, a:flags, a:info]
+    return v:null
+  endfunc
+  func TagFunc2(pat, flags, info)
+    let g:TagFunc2Args = [a:pat, a:flags, a:info]
     return v:null
   endfunc
 
   let lines =<< trim END
+    #" Test for using a function name
+    LET &tagfunc = 'g:TagFunc2'
+    new
+    LET g:TagFunc2Args = []
+    call assert_fails('tag a10', 'E433:')
+    call assert_equal(['a10', '', {}], g:TagFunc2Args)
+    bw!
+
     #" Test for using a function()
-    set tagfunc=function('g:MytagFunc1',\ [10])
-    new | only
-    LET g:MytagFunc1_args = []
+    set tagfunc=function('g:TagFunc1',\ [10])
+    new
+    LET g:TagFunc1Args = []
     call assert_fails('tag a11', 'E433:')
-    call assert_equal([10, 'a11', '', {}], g:MytagFunc1_args)
+    call assert_equal([10, 'a11', '', {}], g:TagFunc1Args)
+    bw!
 
     #" Using a funcref variable to set 'tagfunc'
-    VAR Fn = function('g:MytagFunc1', [11])
+    VAR Fn = function('g:TagFunc1', [11])
     LET &tagfunc = Fn
-    new | only
-    LET g:MytagFunc1_args = []
+    new
+    LET g:TagFunc1Args = []
     call assert_fails('tag a12', 'E433:')
-    call assert_equal([11, 'a12', '', {}], g:MytagFunc1_args)
+    call assert_equal([11, 'a12', '', {}], g:TagFunc1Args)
+    bw!
 
     #" Using a string(funcref_variable) to set 'tagfunc'
-    LET Fn = function('g:MytagFunc1', [12])
+    LET Fn = function('g:TagFunc1', [12])
     LET &tagfunc = string(Fn)
-    new | only
-    LET g:MytagFunc1_args = []
+    new
+    LET g:TagFunc1Args = []
     call assert_fails('tag a12', 'E433:')
-    call assert_equal([12, 'a12', '', {}], g:MytagFunc1_args)
+    call assert_equal([12, 'a12', '', {}], g:TagFunc1Args)
+    bw!
 
     #" Test for using a funcref()
-    set tagfunc=funcref('g:MytagFunc1',\ [13])
-    new | only
-    LET g:MytagFunc1_args = []
+    set tagfunc=funcref('g:TagFunc1',\ [13])
+    new
+    LET g:TagFunc1Args = []
     call assert_fails('tag a13', 'E433:')
-    call assert_equal([13, 'a13', '', {}], g:MytagFunc1_args)
+    call assert_equal([13, 'a13', '', {}], g:TagFunc1Args)
+    bw!
 
     #" Using a funcref variable to set 'tagfunc'
-    LET Fn = funcref('g:MytagFunc1', [14])
+    LET Fn = funcref('g:TagFunc1', [14])
     LET &tagfunc = Fn
-    new | only
-    LET g:MytagFunc1_args = []
+    new
+    LET g:TagFunc1Args = []
     call assert_fails('tag a14', 'E433:')
-    call assert_equal([14, 'a14', '', {}], g:MytagFunc1_args)
+    call assert_equal([14, 'a14', '', {}], g:TagFunc1Args)
+    bw!
 
     #" Using a string(funcref_variable) to set 'tagfunc'
-    LET Fn = funcref('g:MytagFunc1', [15])
+    LET Fn = funcref('g:TagFunc1', [15])
     LET &tagfunc = string(Fn)
-    new | only
-    LET g:MytagFunc1_args = []
+    new
+    LET g:TagFunc1Args = []
     call assert_fails('tag a14', 'E433:')
-    call assert_equal([15, 'a14', '', {}], g:MytagFunc1_args)
+    call assert_equal([15, 'a14', '', {}], g:TagFunc1Args)
+    bw!
 
     #" Test for using a lambda function
-    VAR optval = "LSTART a, b, c LMIDDLE MytagFunc1(16, a, b, c) LEND"
+    VAR optval = "LSTART a, b, c LMIDDLE TagFunc1(16, a, b, c) LEND"
     LET optval = substitute(optval, ' ', '\\ ', 'g')
     exe "set tagfunc=" .. optval
-    new | only
-    LET g:MytagFunc1_args = []
+    new
+    LET g:TagFunc1Args = []
     call assert_fails('tag a17', 'E433:')
-    call assert_equal([16, 'a17', '', {}], g:MytagFunc1_args)
+    call assert_equal([16, 'a17', '', {}], g:TagFunc1Args)
+    bw!
 
     #" Set 'tagfunc' to a lambda expression
-    LET &tagfunc = LSTART a, b, c LMIDDLE MytagFunc1(17, a, b, c) LEND
-    new | only
-    LET g:MytagFunc1_args = []
+    LET &tagfunc = LSTART a, b, c LMIDDLE TagFunc1(17, a, b, c) LEND
+    new
+    LET g:TagFunc1Args = []
     call assert_fails('tag a18', 'E433:')
-    call assert_equal([17, 'a18', '', {}], g:MytagFunc1_args)
+    call assert_equal([17, 'a18', '', {}], g:TagFunc1Args)
+    bw!
 
     #" Set 'tagfunc' to a string(lambda expression)
-    LET &tagfunc = 'LSTART a, b, c LMIDDLE MytagFunc1(18, a, b, c) LEND'
-    new | only
-    LET g:MytagFunc1_args = []
+    LET &tagfunc = 'LSTART a, b, c LMIDDLE TagFunc1(18, a, b, c) LEND'
+    new
+    LET g:TagFunc1Args = []
     call assert_fails('tag a18', 'E433:')
-    call assert_equal([18, 'a18', '', {}], g:MytagFunc1_args)
+    call assert_equal([18, 'a18', '', {}], g:TagFunc1Args)
+    bw!
 
     #" Set 'tagfunc' to a variable with a lambda expression
-    VAR Lambda = LSTART a, b, c LMIDDLE MytagFunc1(19, a, b, c) LEND
+    VAR Lambda = LSTART a, b, c LMIDDLE TagFunc1(19, a, b, c) LEND
     LET &tagfunc = Lambda
-    new | only
-    LET g:MytagFunc1_args = []
+    new
+    LET g:TagFunc1Args = []
     call assert_fails("tag a19", "E433:")
-    call assert_equal([19, 'a19', '', {}], g:MytagFunc1_args)
+    call assert_equal([19, 'a19', '', {}], g:TagFunc1Args)
+    bw!
 
     #" Set 'tagfunc' to a string(variable with a lambda expression)
-    LET Lambda = LSTART a, b, c LMIDDLE MytagFunc1(20, a, b, c) LEND
+    LET Lambda = LSTART a, b, c LMIDDLE TagFunc1(20, a, b, c) LEND
     LET &tagfunc = string(Lambda)
-    new | only
-    LET g:MytagFunc1_args = []
+    new
+    LET g:TagFunc1Args = []
     call assert_fails("tag a19", "E433:")
-    call assert_equal([20, 'a19', '', {}], g:MytagFunc1_args)
+    call assert_equal([20, 'a19', '', {}], g:TagFunc1Args)
+    bw!
 
     #" Test for using a lambda function with incorrect return value
     LET Lambda = LSTART a, b, c LMIDDLE strlen(a) LEND
     LET &tagfunc = string(Lambda)
-    new | only
+    new
     call assert_fails("tag a20", "E987:")
+    bw!
 
     #" Test for clearing the 'tagfunc' option
     set tagfunc=''
@@ -231,10 +255,13 @@ func Test_tagfunc_callback()
     call assert_fails("set tagfunc=funcref('abc')", "E700:")
 
     #" set 'tagfunc' to a non-existing function
-    LET &tagfunc = function('g:MytagFunc1', [21])
+    LET &tagfunc = function('g:TagFunc2', [21])
+    LET g:TagFunc2Args = []
     call assert_fails("set tagfunc=function('NonExistingFunc')", 'E700:')
     call assert_fails("LET &tagfunc = function('NonExistingFunc')", 'E700:')
     call assert_fails("tag axb123", 'E426:')
+    call assert_equal([], g:TagFunc2Args)
+    bw!
   END
   call CheckLegacyAndVim9Success(lines)
 
@@ -242,11 +269,11 @@ func Test_tagfunc_callback()
   call assert_fails("echo taglist('a')", "E987:")
 
   " Using Vim9 lambda expression in legacy context should fail
-  " set tagfunc=(a,\ b,\ c)\ =>\ g:MytagFunc1(21,\ a,\ b,\ c)
+  " set tagfunc=(a,\ b,\ c)\ =>\ g:TagFunc1(21,\ a,\ b,\ c)
   new | only
-  let g:MytagFunc1_args = []
+  let g:TagFunc1Args = []
   " call assert_fails("tag a17", "E117:")
-  call assert_equal([], g:MytagFunc1_args)
+  call assert_equal([], g:TagFunc1Args)
 
   " Test for using a script local function
   set tagfunc=ScriptLocalTagFunc
@@ -309,7 +336,8 @@ func Test_tagfunc_callback()
   " call CheckScriptSuccess(lines)
 
   " cleanup
-  delfunc MytagFunc1
+  delfunc TagFunc1
+  delfunc TagFunc2
   set tagfunc&
   %bw!
 endfunc
-- 
cgit 


From bdb98de2d16ce7185a0f53740e06511904fdd814 Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Mon, 7 Nov 2022 10:21:44 +0000
Subject: refactor: more clint (#20910)

---
 src/nvim/eval/executor.c           |   4 +-
 src/nvim/hardcopy.c                |  10 +-
 src/nvim/mbyte.c                   |   2 +-
 src/nvim/memline.c                 |  14 +-
 src/nvim/msgpack_rpc/helpers.c     |   5 +-
 src/nvim/normal.c                  |   2 +-
 src/nvim/regexp.c                  | 269 ++++++-----------
 src/nvim/regexp_bt.c               | 604 ++++++++++++++++---------------------
 src/nvim/regexp_defs.h             |  70 ++---
 src/nvim/regexp_nfa.c              | 414 ++++++++++---------------
 src/nvim/spellfile.c               |   9 +-
 src/nvim/strings.c                 |  20 +-
 src/nvim/syntax.c                  |  19 +-
 src/nvim/viml/parser/expressions.c |  34 +--
 14 files changed, 597 insertions(+), 879 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c
index 0e0d0fe696..e253098df5 100644
--- a/src/nvim/eval/executor.c
+++ b/src/nvim/eval/executor.c
@@ -122,8 +122,8 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, const char *cons
         break;
       }
       const float_T f = (tv2->v_type == VAR_FLOAT
-                           ? tv2->vval.v_float
-                           : (float_T)tv_get_number(tv2));
+                         ? tv2->vval.v_float
+                         : (float_T)tv_get_number(tv2));
       switch (*op) {
       case '+':
         tv1->vval.v_float += f; break;
diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c
index 7345e9cc35..50af6dafe7 100644
--- a/src/nvim/hardcopy.c
+++ b/src/nvim/hardcopy.c
@@ -729,7 +729,7 @@ void ex_hardcopy(exarg_T *eap)
           }
 
           assert(prtpos.bytes_printed <= SIZE_MAX / 100);
-          sprintf((char *)IObuff, _("Printing page %d (%zu%%)"),
+          sprintf((char *)IObuff, _("Printing page %d (%zu%%)"),  // NOLINT(runtime/printf)
                   page_count + 1 + side,
                   prtpos.bytes_printed * 100 / bytes_to_print);
           if (!mch_print_begin_page((char_u *)IObuff)) {
@@ -750,8 +750,7 @@ void ex_hardcopy(exarg_T *eap)
                        prtpos.file_line);
           }
 
-          for (page_line = 0; page_line < settings.lines_per_page;
-               ++page_line) {
+          for (page_line = 0; page_line < settings.lines_per_page; page_line++) {
             prtpos.column = hardcopy_line(&settings,
                                           page_line, &prtpos);
             if (prtpos.column == 0) {
@@ -2440,8 +2439,7 @@ bool mch_print_begin(prt_settings_T *psettings)
     prt_dsc_font_resource("DocumentNeededResources", &prt_ps_courier_font);
   }
   if (prt_out_mbyte) {
-    prt_dsc_font_resource((prt_use_courier ? NULL
-                                           : "DocumentNeededResources"), &prt_ps_mb_font);
+    prt_dsc_font_resource((prt_use_courier ? NULL : "DocumentNeededResources"), &prt_ps_mb_font);
     if (!prt_custom_cmap) {
       prt_dsc_resources(NULL, "cmap", prt_cmap);
     }
@@ -2990,7 +2988,7 @@ int mch_print_text_out(char_u *const textp, size_t len)
         ga_append(&prt_ps_buffer, '\\'); break;
 
       default:
-        sprintf((char *)ch_buff, "%03o", (unsigned int)ch);
+        sprintf((char *)ch_buff, "%03o", (unsigned int)ch);  // NOLINT(runtime/printf)
         ga_append(&prt_ps_buffer, (char)ch_buff[0]);
         ga_append(&prt_ps_buffer, (char)ch_buff[1]);
         ga_append(&prt_ps_buffer, (char)ch_buff[2]);
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 9e34c7e413..14691741d8 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -1550,7 +1550,7 @@ void show_utf8(void)
       }
       clen = utf_ptr2len((char *)line + i);
     }
-    sprintf((char *)IObuff + rlen, "%02x ",
+    sprintf((char *)IObuff + rlen, "%02x ",  // NOLINT(runtime/printf)
             (line[i] == NL) ? NUL : line[i]);          // NUL is stored as NL
     clen--;
     rlen += (int)strlen(IObuff + rlen);
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 225e2aeab1..f9900eb434 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -2135,13 +2135,13 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
         buf->b_ml.ml_stack_top = stack_idx + 1;             // truncate stack
 
         if (lineadd) {
-          --(buf->b_ml.ml_stack_top);
+          (buf->b_ml.ml_stack_top)--;
           // fix line count for rest of blocks in the stack
           ml_lineadd(buf, lineadd);
           // fix stack itself
           buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
             lineadd;
-          ++(buf->b_ml.ml_stack_top);
+          (buf->b_ml.ml_stack_top)++;
         }
 
         // We are finished, break the loop here.
@@ -2428,7 +2428,7 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message)
           buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
             buf->b_ml.ml_locked_lineadd;
         }
-        ++(buf->b_ml.ml_stack_top);
+        (buf->b_ml.ml_stack_top)++;
 
         break;
       }
@@ -2698,11 +2698,11 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action)
         && buf->b_ml.ml_locked_high >= lnum) {
       // remember to update pointer blocks and stack later
       if (action == ML_INSERT) {
-        ++(buf->b_ml.ml_locked_lineadd);
-        ++(buf->b_ml.ml_locked_high);
+        (buf->b_ml.ml_locked_lineadd)++;
+        (buf->b_ml.ml_locked_high)++;
       } else if (action == ML_DELETE) {
-        --(buf->b_ml.ml_locked_lineadd);
-        --(buf->b_ml.ml_locked_high);
+        (buf->b_ml.ml_locked_lineadd)--;
+        (buf->b_ml.ml_locked_high)--;
       }
       return buf->b_ml.ml_locked;
     }
diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c
index ddca9afad0..86babd1c36 100644
--- a/src/nvim/msgpack_rpc/helpers.c
+++ b/src/nvim/msgpack_rpc/helpers.c
@@ -95,9 +95,8 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg)
       dest = conv(((String) { \
       .size = obj->via.attr.size, \
       .data = (obj->via.attr.ptr == NULL || obj->via.attr.size == 0 \
-                   ? xmemdupz("", 0) \
-                   : xmemdupz(obj->via.attr.ptr, obj->via.attr.size)), \
-    })); \
+               ? xmemdupz("", 0) \
+               : xmemdupz(obj->via.attr.ptr, obj->via.attr.size)), })); \
       break; \
   }
       STR_CASE(MSGPACK_OBJECT_STR, str, cur.mobj, *cur.aobj, STRING_OBJ)
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index d142af555a..d7e384334b 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -2266,7 +2266,7 @@ bool find_decl(char_u *ptr, size_t len, bool locally, bool thisblock, int flags_
   // Put "\V" before the pattern to avoid that the special meaning of "."
   // and "~" causes trouble.
   assert(len <= INT_MAX);
-  sprintf((char *)pat, vim_iswordp(ptr) ? "\\V\\<%.*s\\>" : "\\V%.*s",
+  sprintf((char *)pat, vim_iswordp(ptr) ? "\\V\\<%.*s\\>" : "\\V%.*s",  // NOLINT(runtime/printf)
           (int)len, ptr);
   old_pos = curwin->w_cursor;
   save_p_ws = p_ws;
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 7a96889f22..27b5d198ac 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -1,9 +1,7 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
-/*
- * Handling of regular expressions: vim_regcomp(), vim_regexec(), vim_regsub()
- */
+// Handling of regular expressions: vim_regcomp(), vim_regexec(), vim_regsub()
 
 // By default: do not create debugging logs or files related to regular
 // expressions, even when compiling with -DDEBUG.
@@ -41,21 +39,17 @@
 # define BT_REGEXP_DEBUG_LOG_NAME       "bt_regexp_debug.log"
 #endif
 
-/*
- * Magic characters have a special meaning, they don't match literally.
- * Magic characters are negative.  This separates them from literal characters
- * (possibly multi-byte).  Only ASCII characters can be Magic.
- */
+// Magic characters have a special meaning, they don't match literally.
+// Magic characters are negative.  This separates them from literal characters
+// (possibly multi-byte).  Only ASCII characters can be Magic.
 #define Magic(x)        ((int)(x) - 256)
 #define un_Magic(x)     ((x) + 256)
 #define is_Magic(x)     ((x) < 0)
 
-/*
- * We should define ftpr as a pointer to a function returning a pointer to
- * a function returning a pointer to a function ...
- * This is impossible, so we declare a pointer to a function returning a
- * pointer to a function returning void. This should work for all compilers.
- */
+// We should define ftpr as a pointer to a function returning a pointer to
+// a function returning a pointer to a function ...
+// This is impossible, so we declare a pointer to a function returning a
+// pointer to a function returning void. This should work for all compilers.
 typedef void (*(*fptr_T)(int *, int))(void);
 
 static int no_Magic(int x)
@@ -143,28 +137,24 @@ static int re_multi_type(int c)
 
 static char *reg_prev_sub = NULL;
 
-/*
- * REGEXP_INRANGE contains all characters which are always special in a []
- * range after '\'.
- * REGEXP_ABBR contains all characters which act as abbreviations after '\'.
- * These are:
- *  \n  - New line (NL).
- *  \r  - Carriage Return (CR).
- *  \t  - Tab (TAB).
- *  \e  - Escape (ESC).
- *  \b  - Backspace (Ctrl_H).
- *  \d  - Character code in decimal, eg \d123
- *  \o  - Character code in octal, eg \o80
- *  \x  - Character code in hex, eg \x4a
- *  \u  - Multibyte character code, eg \u20ac
- *  \U  - Long multibyte character code, eg \U12345678
- */
+// REGEXP_INRANGE contains all characters which are always special in a []
+// range after '\'.
+// REGEXP_ABBR contains all characters which act as abbreviations after '\'.
+// These are:
+//  \n  - New line (NL).
+//  \r  - Carriage Return (CR).
+//  \t  - Tab (TAB).
+//  \e  - Escape (ESC).
+//  \b  - Backspace (Ctrl_H).
+//  \d  - Character code in decimal, eg \d123
+//  \o  - Character code in octal, eg \o80
+//  \x  - Character code in hex, eg \x4a
+//  \u  - Multibyte character code, eg \u20ac
+//  \U  - Long multibyte character code, eg \U12345678
 static char REGEXP_INRANGE[] = "]^-n\\";
 static char REGEXP_ABBR[] = "nrtebdoxuU";
 
-/*
- * Translate '\x' to its control character, except "\n", which is Magic.
- */
+// Translate '\x' to its control character, except "\n", which is Magic.
 static int backslash_trans(int c)
 {
   switch (c) {
@@ -239,10 +229,8 @@ static int get_char_class(char **pp)
   return CLASS_NONE;
 }
 
-/*
- * Specific version of character class functions.
- * Using a table to keep this fast.
- */
+// Specific version of character class functions.
+// Using a table to keep this fast.
 static int16_t class_tab[256];
 
 #define     RI_DIGIT    0x01
@@ -325,9 +313,7 @@ static int reg_string;          // matching with a string instead of a buffer
                                 // line
 static int reg_strict;          // "[abc" is illegal
 
-/*
- * META contains all characters that may be magic, except '^' and '$'.
- */
+// META contains all characters that may be magic, except '^' and '$'.
 
 // uncrustify:off
 
@@ -391,11 +377,9 @@ int re_multiline(const regprog_T *prog)
   return prog->regflags & RF_HASNL;
 }
 
-/*
- * Check for an equivalence class name "[=a=]".  "pp" points to the '['.
- * Returns a character representing the class. Zero means that no item was
- * recognized.  Otherwise "pp" is advanced to after the item.
- */
+// Check for an equivalence class name "[=a=]".  "pp" points to the '['.
+// Returns a character representing the class. Zero means that no item was
+// recognized.  Otherwise "pp" is advanced to after the item.
 static int get_equi_class(char **pp)
 {
   int c;
@@ -413,12 +397,10 @@ static int get_equi_class(char **pp)
   return 0;
 }
 
-/*
- * Check for a collating element "[.a.]".  "pp" points to the '['.
- * Returns a character. Zero means that no item was recognized.  Otherwise
- * "pp" is advanced to after the item.
- * Currently only single characters are recognized!
- */
+// Check for a collating element "[.a.]".  "pp" points to the '['.
+// Returns a character. Zero means that no item was recognized.  Otherwise
+// "pp" is advanced to after the item.
+// Currently only single characters are recognized!
 static int get_coll_element(char **pp)
 {
   int c;
@@ -562,9 +544,7 @@ static int prevchr_len;    // byte length of previous char
 static int at_start;       // True when on the first character
 static int prev_at_start;  // True when on the second character
 
-/*
- * Start parsing at "str".
- */
+// Start parsing at "str".
 static void initchr(char_u *str)
 {
   regparse = (char *)str;
@@ -574,10 +554,8 @@ static void initchr(char_u *str)
   prev_at_start = false;
 }
 
-/*
- * Save the current parse state, so that it can be restored and parsing
- * starts in the same state again.
- */
+// Save the current parse state, so that it can be restored and parsing
+// starts in the same state again.
 static void save_parse_state(parse_state_T *ps)
 {
   ps->regparse = (char_u *)regparse;
@@ -591,9 +569,7 @@ static void save_parse_state(parse_state_T *ps)
   ps->regnpar = regnpar;
 }
 
-/*
- * Restore a previously saved parse state.
- */
+// Restore a previously saved parse state.
 static void restore_parse_state(parse_state_T *ps)
 {
   regparse = (char *)ps->regparse;
@@ -607,9 +583,7 @@ static void restore_parse_state(parse_state_T *ps)
   regnpar = ps->regnpar;
 }
 
-/*
- * Get the next character without advancing.
- */
+// Get the next character without advancing.
 static int peekchr(void)
 {
   static int after_slash = false;
@@ -736,9 +710,7 @@ static int peekchr(void)
       after_slash--;
       curchr = toggle_Magic(curchr);
     } else if (vim_strchr(REGEXP_ABBR, c)) {
-      /*
-       * Handle abbreviations, like "\t" for TAB -- webb
-       */
+      // Handle abbreviations, like "\t" for TAB -- webb
       curchr = backslash_trans(c);
     } else if (reg_magic == MAGIC_NONE && (c == '$' || c == '^')) {
       curchr = toggle_Magic(c);
@@ -757,9 +729,7 @@ static int peekchr(void)
   return curchr;
 }
 
-/*
- * Eat one lexed character.  Do this in a way that we can undo it.
- */
+// Eat one lexed character.  Do this in a way that we can undo it.
 static void skipchr(void)
 {
   // peekchr() eats a backslash, do the same here
@@ -781,10 +751,8 @@ static void skipchr(void)
   nextchr = -1;
 }
 
-/*
- * Skip a character while keeping the value of prev_at_start for at_start.
- * prevchr and prevprevchr are also kept.
- */
+// Skip a character while keeping the value of prev_at_start for at_start.
+// prevchr and prevprevchr are also kept.
 static void skipchr_keepstart(void)
 {
   int as = prev_at_start;
@@ -797,10 +765,8 @@ static void skipchr_keepstart(void)
   prevprevchr = prpr;
 }
 
-/*
- * Get the next character from the pattern. We know about magic and such, so
- * therefore we need a lexical analyzer.
- */
+// Get the next character from the pattern. We know about magic and such, so
+// therefore we need a lexical analyzer.
 static int getchr(void)
 {
   int chr = peekchr();
@@ -809,9 +775,7 @@ static int getchr(void)
   return chr;
 }
 
-/*
- * put character back.  Works only once!
- */
+// put character back.  Works only once!
 static void ungetchr(void)
 {
   nextchr = curchr;
@@ -825,15 +789,13 @@ static void ungetchr(void)
   regparse -= prevchr_len;
 }
 
-/*
- * Get and return the value of the hex string at the current position.
- * Return -1 if there is no valid hex number.
- * The position is updated:
- *     blahblah\%x20asdf
- *         before-^ ^-after
- * The parameter controls the maximum number of input characters. This will be
- * 2 when reading a \%x20 sequence and 4 when reading a \%u20AC sequence.
- */
+// Get and return the value of the hex string at the current position.
+// Return -1 if there is no valid hex number.
+// The position is updated:
+//     blahblah\%x20asdf
+//         before-^ ^-after
+// The parameter controls the maximum number of input characters. This will be
+// 2 when reading a \%x20 sequence and 4 when reading a \%u20AC sequence.
 static int64_t gethexchrs(int maxinputlen)
 {
   int64_t nr = 0;
@@ -856,10 +818,8 @@ static int64_t gethexchrs(int maxinputlen)
   return nr;
 }
 
-/*
- * Get and return the value of the decimal string immediately after the
- * current position. Return -1 for invalid.  Consumes all digits.
- */
+// Get and return the value of the decimal string immediately after the
+// current position. Return -1 for invalid.  Consumes all digits.
 static int64_t getdecchrs(void)
 {
   int64_t nr = 0;
@@ -883,14 +843,12 @@ static int64_t getdecchrs(void)
   return nr;
 }
 
-/*
- * get and return the value of the octal string immediately after the current
- * position. Return -1 for invalid, or 0-255 for valid. Smart enough to handle
- * numbers > 377 correctly (for example, 400 is treated as 40) and doesn't
- * treat 8 or 9 as recognised characters. Position is updated:
- *     blahblah\%o210asdf
- *         before-^  ^-after
- */
+// get and return the value of the octal string immediately after the current
+// position. Return -1 for invalid, or 0-255 for valid. Smart enough to handle
+// numbers > 377 correctly (for example, 400 is treated as 40) and doesn't
+// treat 8 or 9 as recognised characters. Position is updated:
+//     blahblah\%o210asdf
+//         before-^  ^-after
 static int64_t getoctchrs(void)
 {
   int64_t nr = 0;
@@ -913,12 +871,10 @@ static int64_t getoctchrs(void)
   return nr;
 }
 
-/*
- * read_limits - Read two integers to be taken as a minimum and maximum.
- * If the first character is '-', then the range is reversed.
- * Should end with 'end'.  If minval is missing, zero is default, if maxval is
- * missing, a very big number is the default.
- */
+// read_limits - Read two integers to be taken as a minimum and maximum.
+// If the first character is '-', then the range is reversed.
+// Should end with 'end'.  If minval is missing, zero is default, if maxval is
+// missing, a very big number is the default.
 static int read_limits(long *minval, long *maxval)
 {
   int reverse = false;
@@ -950,10 +906,8 @@ static int read_limits(long *minval, long *maxval)
     EMSG2_RET_FAIL(_("E554: Syntax error in %s{...}"), reg_magic == MAGIC_ALL);
   }
 
-  /*
-   * Reverse the range if there was a '-', or make sure it is in the right
-   * order otherwise.
-   */
+  // Reverse the range if there was a '-', or make sure it is in the right
+  // order otherwise.
   if ((!reverse && *minval > *maxval) || (reverse && *minval < *maxval)) {
     tmp = *minval;
     *minval = *maxval;
@@ -963,13 +917,9 @@ static int read_limits(long *minval, long *maxval)
   return OK;
 }
 
-/*
- * vim_regexec and friends
- */
+// vim_regexec and friends
 
-/*
- * Global work variables for vim_regexec().
- */
+// Global work variables for vim_regexec().
 
 // Sometimes need to save a copy of a line.  Since alloc()/free() is very
 // slow, we keep one allocated piece of memory and only re-allocate it when
@@ -1052,9 +1002,7 @@ static bool reg_iswordc(int c)
   return vim_iswordc_buf(c, rex.reg_buf);
 }
 
-/*
- * Get pointer to the line "lnum", which is relative to "reg_firstlnum".
- */
+// Get pointer to the line "lnum", which is relative to "reg_firstlnum".
 static char_u *reg_getline(linenr_T lnum)
 {
   // when looking behind for a match/no-match lnum is negative.  But we
@@ -1077,9 +1025,7 @@ static lpos_T reg_endzpos[NSUBEXP];     // idem, end pos
 // true if using multi-line regexp.
 #define REG_MULTI       (rex.reg_match == NULL)
 
-/*
- * Create a new extmatch and mark it as referenced once.
- */
+// Create a new extmatch and mark it as referenced once.
 static reg_extmatch_T *make_extmatch(void)
   FUNC_ATTR_NONNULL_RET
 {
@@ -1088,9 +1034,7 @@ static reg_extmatch_T *make_extmatch(void)
   return em;
 }
 
-/*
- * Add a reference to an extmatch.
- */
+// Add a reference to an extmatch.
 reg_extmatch_T *ref_extmatch(reg_extmatch_T *em)
 {
   if (em != NULL) {
@@ -1099,10 +1043,8 @@ reg_extmatch_T *ref_extmatch(reg_extmatch_T *em)
   return em;
 }
 
-/*
- * Remove a reference to an extmatch.  If there are no references left, free
- * the info.
- */
+// Remove a reference to an extmatch.  If there are no references left, free
+// the info.
 void unref_extmatch(reg_extmatch_T *em)
 {
   int i;
@@ -1201,10 +1143,8 @@ static bool reg_match_visual(void)
   return true;
 }
 
-/*
- * Check the regexp program for its magic number.
- * Return true if it's wrong.
- */
+// Check the regexp program for its magic number.
+// Return true if it's wrong.
 static int prog_magic_wrong(void)
 {
   regprog_T *prog;
@@ -1222,11 +1162,9 @@ static int prog_magic_wrong(void)
   return false;
 }
 
-/*
- * Cleanup the subexpressions, if this wasn't done yet.
- * This construction is used to clear the subexpressions only when they are
- * used (to increase speed).
- */
+// Cleanup the subexpressions, if this wasn't done yet.
+// This construction is used to clear the subexpressions only when they are
+// used (to increase speed).
 static void cleanup_subexpr(void)
 {
   if (rex.need_clear_subexpr) {
@@ -1265,12 +1203,10 @@ static void reg_nextline(void)
   fast_breakcheck();
 }
 
-/*
- * Check whether a backreference matches.
- * Returns RA_FAIL, RA_NOMATCH or RA_MATCH.
- * If "bytelen" is not NULL, it is set to the byte length of the match in the
- * last line.
- */
+// Check whether a backreference matches.
+// Returns RA_FAIL, RA_NOMATCH or RA_MATCH.
+// If "bytelen" is not NULL, it is set to the byte length of the match in the
+// last line.
 static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T end_lnum,
                               colnr_T end_col, int *bytelen)
 {
@@ -1449,9 +1385,9 @@ static int cstrncmp(char *s1, char *s2, int *n)
       c1 = mb_ptr2char_adv((const char_u **)&str1);
       c2 = mb_ptr2char_adv((const char_u **)&str2);
 
-      /* decompose the character if necessary, into 'base' characters
-       * because I don't care about Arabic, I will hard-code the Hebrew
-       * which I *do* care about!  So sue me... */
+      // decompose the character if necessary, into 'base' characters
+      // because I don't care about Arabic, I will hard-code the Hebrew
+      // which I *do* care about!  So sue me...
       if (c1 != c2 && (!rex.reg_ic || utf_fold(c1) != utf_fold(c2))) {
         // decomposition necessary?
         mb_decompose(c1, &c11, &junk, &junk);
@@ -1566,7 +1502,7 @@ char *regtilde(char *source, int magic, bool preview)
   int len;
   int prevlen;
 
-  for (p = newsub; *p; ++p) {
+  for (p = newsub; *p; p++) {
     if ((*p == '~' && magic) || (*p == '\\' && *(p + 1) == '~' && !magic)) {
       if (reg_prev_sub != NULL) {
         // length = len(newsub) - 1 + len(prev_sub) + 1
@@ -1871,12 +1807,11 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
             *s = CAR;
           } else if (*s == '\\' && s[1] != NUL) {
             s++;
-            /* Change NL to CR here too, so that this works:
-             * :s/abc\\\ndef/\="aaa\\\nbbb"/  on text:
-             *   abc\
-             *   def
-             * Not when called from vim_regexec_nl().
-             */
+            // Change NL to CR here too, so that this works:
+            // :s/abc\\\ndef/\="aaa\\\nbbb"/  on text:
+            //   abc{backslash}
+            //   def
+            // Not when called from vim_regexec_nl().
             if (*s == NL && !rsm.sm_line_lbr) {
               *s = CAR;
             }
@@ -2172,10 +2107,8 @@ char *reg_submatch(int no)
   if (rsm.sm_match == NULL) {
     ssize_t len;
 
-    /*
-     * First round: compute the length and allocate memory.
-     * Second round: copy the text.
-     */
+    // First round: compute the length and allocate memory.
+    // Second round: copy the text.
     for (round = 1; round <= 2; round++) {
       lnum = rsm.sm_mmatch->startpos[no].lnum;
       if (lnum < 0 || rsm.sm_mmatch->endpos[no].lnum < 0) {
@@ -2216,7 +2149,7 @@ char *reg_submatch(int no)
           len++;
         }
         if (round == 2) {
-          STRNCPY(retval + len, reg_getline_submatch(lnum),
+          STRNCPY(retval + len, reg_getline_submatch(lnum),  // NOLINT(runtime/printf)
                   rsm.sm_mmatch->endpos[no].col);
         }
         len += rsm.sm_mmatch->endpos[no].col;
@@ -2327,12 +2260,10 @@ static char_u regname[][30] = {
 };
 #endif
 
-/*
- * Compile a regular expression into internal code.
- * Returns the program in allocated memory.
- * Use vim_regfree() to free the memory.
- * Returns NULL for an error.
- */
+// Compile a regular expression into internal code.
+// Returns the program in allocated memory.
+// Use vim_regfree() to free the memory.
+// Returns NULL for an error.
 regprog_T *vim_regcomp(char *expr_arg, int re_flags)
 {
   regprog_T *prog = NULL;
@@ -2413,9 +2344,7 @@ regprog_T *vim_regcomp(char *expr_arg, int re_flags)
   return prog;
 }
 
-/*
- * Free a compiled regexp program, returned by vim_regcomp().
- */
+// Free a compiled regexp program, returned by vim_regcomp().
 void vim_regfree(regprog_T *prog)
 {
   if (prog != NULL) {
diff --git a/src/nvim/regexp_bt.c b/src/nvim/regexp_bt.c
index 6f63b38a90..7b5f4cd12a 100644
--- a/src/nvim/regexp_bt.c
+++ b/src/nvim/regexp_bt.c
@@ -1,137 +1,130 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
-/*
- *
- * Backtracking regular expression implementation.
- *
- * This file is included in "regexp.c".
- *
- * NOTICE:
- *
- * This is NOT the original regular expression code as written by Henry
- * Spencer.  This code has been modified specifically for use with the VIM
- * editor, and should not be used separately from Vim.  If you want a good
- * regular expression library, get the original code.  The copyright notice
- * that follows is from the original.
- *
- * END NOTICE
- *
- *      Copyright (c) 1986 by University of Toronto.
- *      Written by Henry Spencer.  Not derived from licensed software.
- *
- *      Permission is granted to anyone to use this software for any
- *      purpose on any computer system, and to redistribute it freely,
- *      subject to the following restrictions:
- *
- *      1. The author is not responsible for the consequences of use of
- *              this software, no matter how awful, even if they arise
- *              from defects in it.
- *
- *      2. The origin of this software must not be misrepresented, either
- *              by explicit claim or by omission.
- *
- *      3. Altered versions must be plainly marked as such, and must not
- *              be misrepresented as being the original software.
- *
- * Beware that some of this code is subtly aware of the way operator
- * precedence is structured in regular expressions.  Serious changes in
- * regular-expression syntax might require a total rethink.
- *
- * Changes have been made by Tony Andrews, Olaf 'Rhialto' Seibert, Robert
- * Webb, Ciaran McCreesh and Bram Moolenaar.
- * Named character class support added by Walter Briscoe (1998 Jul 01)
- */
-
-/*
- * The "internal use only" fields in regexp_defs.h are present to pass info from
- * compile to execute that permits the execute phase to run lots faster on
- * simple cases.  They are:
- *
- * regstart     char that must begin a match; NUL if none obvious; Can be a
- *              multi-byte character.
- * reganch      is the match anchored (at beginning-of-line only)?
- * regmust      string (pointer into program) that match must include, or NULL
- * regmlen      length of regmust string
- * regflags     RF_ values or'ed together
- *
- * Regstart and reganch permit very fast decisions on suitable starting points
- * for a match, cutting down the work a lot.  Regmust permits fast rejection
- * of lines that cannot possibly match.  The regmust tests are costly enough
- * that vim_regcomp() supplies a regmust only if the r.e. contains something
- * potentially expensive (at present, the only such thing detected is * or +
- * at the start of the r.e., which can involve a lot of backup).  Regmlen is
- * supplied because the test in vim_regexec() needs it and vim_regcomp() is
- * computing it anyway.
- */
-
-/*
- * Structure for regexp "program".  This is essentially a linear encoding
- * of a nondeterministic finite-state machine (aka syntax charts or
- * "railroad normal form" in parsing technology).  Each node is an opcode
- * plus a "next" pointer, possibly plus an operand.  "Next" pointers of
- * all nodes except BRANCH and BRACES_COMPLEX implement concatenation; a "next"
- * pointer with a BRANCH on both ends of it is connecting two alternatives.
- * (Here we have one of the subtle syntax dependencies: an individual BRANCH
- * (as opposed to a collection of them) is never concatenated with anything
- * because of operator precedence).  The "next" pointer of a BRACES_COMPLEX
- * node points to the node after the stuff to be repeated.
- * The operand of some types of node is a literal string; for others, it is a
- * node leading into a sub-FSM.  In particular, the operand of a BRANCH node
- * is the first node of the branch.
- * (NB this is *not* a tree structure: the tail of the branch connects to the
- * thing following the set of BRANCHes.)
- *
- * pattern      is coded like:
- *
- *                        +-----------------+
- *                        |                 V
- * \|   BRANCH  BRANCH  --> END
- *                   |      ^    |          ^
- *                   +------+    +----------+
- *
- *
- *                     +------------------+
- *                     V                  |
- * *        BRANCH BRANCH  --> BACK BRANCH --> NOTHING --> END
- *                   |      |               ^                      ^
- *                   |      +---------------+                      |
- *                   +---------------------------------------------+
- *
- *
- *                     +----------------------+
- *                     V                      |
- * \+       BRANCH  --> BRANCH --> BACK  BRANCH --> NOTHING --> END
- *                   |               |           ^                      ^
- *                   |               +-----------+                      |
- *                   +--------------------------------------------------+
- *
- *
- *                                      +-------------------------+
- *                                      V                         |
- * \{}      BRANCH BRACE_LIMITS --> BRACE_COMPLEX  --> BACK  END
- *                   |                              |                ^
- *                   |                              +----------------+
- *                   +-----------------------------------------------+
- *
- *
- * \@!  BRANCH NOMATCH  --> END   --> END
- *                   |       |                ^       ^
- *                   |       +----------------+       |
- *                   +--------------------------------+
- *
- *                                                    +---------+
- *                                                    |         V
- * \z[abc]      BRANCH BRANCH  a  BRANCH  b  BRANCH  c  BRANCH  NOTHING --> END
- *                   |      |          |          |     ^                   ^
- *                   |      |          |          +-----+                   |
- *                   |      |          +----------------+                   |
- *                   |      +---------------------------+                   |
- *                   +------------------------------------------------------+
- *
- * They all start with a BRANCH for "\|" alternatives, even when there is only
- * one alternative.
- */
+// Backtracking regular expression implementation.
+//
+// This file is included in "regexp.c".
+//
+// NOTICE:
+//
+// This is NOT the original regular expression code as written by Henry
+// Spencer.  This code has been modified specifically for use with the VIM
+// editor, and should not be used separately from Vim.  If you want a good
+// regular expression library, get the original code.  The copyright notice
+// that follows is from the original.
+//
+// END NOTICE
+//
+//      Copyright (c) 1986 by University of Toronto.
+//      Written by Henry Spencer.  Not derived from licensed software.
+//
+//      Permission is granted to anyone to use this software for any
+//      purpose on any computer system, and to redistribute it freely,
+//      subject to the following restrictions:
+//
+//      1. The author is not responsible for the consequences of use of
+//              this software, no matter how awful, even if they arise
+//              from defects in it.
+//
+//      2. The origin of this software must not be misrepresented, either
+//              by explicit claim or by omission.
+//
+//      3. Altered versions must be plainly marked as such, and must not
+//              be misrepresented as being the original software.
+//
+// Beware that some of this code is subtly aware of the way operator
+// precedence is structured in regular expressions.  Serious changes in
+// regular-expression syntax might require a total rethink.
+//
+// Changes have been made by Tony Andrews, Olaf 'Rhialto' Seibert, Robert
+// Webb, Ciaran McCreesh and Bram Moolenaar.
+// Named character class support added by Walter Briscoe (1998 Jul 01)
+
+// The "internal use only" fields in regexp_defs.h are present to pass info from
+// compile to execute that permits the execute phase to run lots faster on
+// simple cases.  They are:
+//
+// regstart     char that must begin a match; NUL if none obvious; Can be a
+//              multi-byte character.
+// reganch      is the match anchored (at beginning-of-line only)?
+// regmust      string (pointer into program) that match must include, or NULL
+// regmlen      length of regmust string
+// regflags     RF_ values or'ed together
+//
+// Regstart and reganch permit very fast decisions on suitable starting points
+// for a match, cutting down the work a lot.  Regmust permits fast rejection
+// of lines that cannot possibly match.  The regmust tests are costly enough
+// that vim_regcomp() supplies a regmust only if the r.e. contains something
+// potentially expensive (at present, the only such thing detected is * or +
+// at the start of the r.e., which can involve a lot of backup).  Regmlen is
+// supplied because the test in vim_regexec() needs it and vim_regcomp() is
+// computing it anyway.
+
+// Structure for regexp "program".  This is essentially a linear encoding
+// of a nondeterministic finite-state machine (aka syntax charts or
+// "railroad normal form" in parsing technology).  Each node is an opcode
+// plus a "next" pointer, possibly plus an operand.  "Next" pointers of
+// all nodes except BRANCH and BRACES_COMPLEX implement concatenation; a "next"
+// pointer with a BRANCH on both ends of it is connecting two alternatives.
+// (Here we have one of the subtle syntax dependencies: an individual BRANCH
+// (as opposed to a collection of them) is never concatenated with anything
+// because of operator precedence).  The "next" pointer of a BRACES_COMPLEX
+// node points to the node after the stuff to be repeated.
+// The operand of some types of node is a literal string; for others, it is a
+// node leading into a sub-FSM.  In particular, the operand of a BRANCH node
+// is the first node of the branch.
+// (NB this is *not* a tree structure: the tail of the branch connects to the
+// thing following the set of BRANCHes.)
+//
+// pattern      is coded like:
+//
+//                        +-----------------+
+//                        |                 V
+// \|   BRANCH  BRANCH  --> END
+//                   |      ^    |          ^
+//                   +------+    +----------+
+//
+//
+//                     +------------------+
+//                     V                  |
+// *        BRANCH BRANCH  --> BACK BRANCH --> NOTHING --> END
+//                   |      |               ^                      ^
+//                   |      +---------------+                      |
+//                   +---------------------------------------------+
+//
+//
+//                     +----------------------+
+//                     V                      |
+// \+       BRANCH  --> BRANCH --> BACK  BRANCH --> NOTHING --> END
+//                   |               |           ^                      ^
+//                   |               +-----------+                      |
+//                   +--------------------------------------------------+
+//
+//
+//                                      +-------------------------+
+//                                      V                         |
+// \{}      BRANCH BRACE_LIMITS --> BRACE_COMPLEX  --> BACK  END
+//                   |                              |                ^
+//                   |                              +----------------+
+//                   +-----------------------------------------------+
+//
+//
+// \@!  BRANCH NOMATCH  --> END   --> END
+//                   |       |                ^       ^
+//                   |       +----------------+       |
+//                   +--------------------------------+
+//
+//                                                    +---------+
+//                                                    |         V
+// \z[abc]      BRANCH BRANCH  a  BRANCH  b  BRANCH  c  BRANCH  NOTHING --> END
+//                   |      |          |          |     ^                   ^
+//                   |      |          |          +-----+                   |
+//                   |      |          +----------------+                   |
+//                   |      +---------------------------+                   |
+//                   +------------------------------------------------------+
+//
+// They all start with a BRANCH for "\|" alternatives, even when there is only
+// one alternative.
 
 #include 
 #include 
@@ -141,9 +134,7 @@
 #include "nvim/garray.h"
 #include "nvim/regexp.h"
 
-/*
- * The opcodes are:
- */
+// The opcodes are:
 
 // definition   number             opnd?    meaning
 #define END             0       //      End of program or NOMATCH operand.
@@ -240,9 +231,7 @@
 #define RE_VISUAL       208     //      Match Visual area
 #define RE_COMPOSING    209     // any composing characters
 
-/*
- * Flags to be passed up and down.
- */
+// Flags to be passed up and down.
 #define HASWIDTH        0x1     // Known never to match null string.
 #define SIMPLE          0x2     // Simple enough to be STAR/PLUS operand.
 #define SPSTART         0x4     // Starts with * or +.
@@ -273,10 +262,8 @@ static int classcodes[] = {
   UPPER, NUPPER
 };
 
-/*
- * When regcode is set to this value, code is not emitted and size is computed
- * instead.
- */
+// When regcode is set to this value, code is not emitted and size is computed
+// instead.
 #define JUST_CALC_SIZE  ((char_u *)-1)
 
 // Values for rs_state in regitem_T.
@@ -297,11 +284,9 @@ typedef enum regstate_E {
   RS_STAR_SHORT,  // STAR/PLUS/BRACE_SIMPLE shortest match
 } regstate_T;
 
-/*
- * Structure used to save the current input state, when it needs to be
- * restored after trying a match.  Used by reg_save() and reg_restore().
- * Also stores the length of "backpos".
- */
+// Structure used to save the current input state, when it needs to be
+// restored after trying a match.  Used by reg_save() and reg_restore().
+// Also stores the length of "backpos".
 typedef struct {
   union {
     char_u *ptr;       // rex.input pointer, for single-line regexp
@@ -327,12 +312,10 @@ typedef struct regbehind_S {
   save_se_T save_end[NSUBEXP];
 } regbehind_T;
 
-/*
- * When there are alternatives a regstate_T is put on the regstack to remember
- * what we are doing.
- * Before it may be another type of item, depending on rs_state, to remember
- * more things.
- */
+// When there are alternatives a regstate_T is put on the regstack to remember
+// what we are doing.
+// Before it may be another type of item, depending on rs_state, to remember
+// more things.
 typedef struct regitem_S {
   regstate_T rs_state;         // what we are doing, one of RS_ above
   int16_t rs_no;            // submatch nr or BEHIND/NOBEHIND
@@ -359,69 +342,63 @@ typedef struct backpos_S {
   regsave_T bp_pos;           // last input position
 } backpos_T;
 
-/*
- * "regstack" and "backpos" are used by regmatch().  They are kept over calls
- * to avoid invoking malloc() and free() often.
- * "regstack" is a stack with regitem_T items, sometimes preceded by regstar_T
- * or regbehind_T.
- * "backpos_T" is a table with backpos_T for BACK
- */
+// "regstack" and "backpos" are used by regmatch().  They are kept over calls
+// to avoid invoking malloc() and free() often.
+// "regstack" is a stack with regitem_T items, sometimes preceded by regstar_T
+// or regbehind_T.
+// "backpos_T" is a table with backpos_T for BACK
 static garray_T regstack = GA_EMPTY_INIT_VALUE;
 static garray_T backpos = GA_EMPTY_INIT_VALUE;
 
 static regsave_T behind_pos;
 
-/*
- * Both for regstack and backpos tables we use the following strategy of
- * allocation (to reduce malloc/free calls):
- * - Initial size is fairly small.
- * - When needed, the tables are grown bigger (8 times at first, double after
- *   that).
- * - After executing the match we free the memory only if the array has grown.
- *   Thus the memory is kept allocated when it's at the initial size.
- * This makes it fast while not keeping a lot of memory allocated.
- * A three times speed increase was observed when using many simple patterns.
- */
+// Both for regstack and backpos tables we use the following strategy of
+// allocation (to reduce malloc/free calls):
+// - Initial size is fairly small.
+// - When needed, the tables are grown bigger (8 times at first, double after
+//   that).
+// - After executing the match we free the memory only if the array has grown.
+//   Thus the memory is kept allocated when it's at the initial size.
+// This makes it fast while not keeping a lot of memory allocated.
+// A three times speed increase was observed when using many simple patterns.
 #define REGSTACK_INITIAL        2048
 #define BACKPOS_INITIAL         64
 
-/*
- * Opcode notes:
- *
- * BRANCH       The set of branches constituting a single choice are hooked
- *              together with their "next" pointers, since precedence prevents
- *              anything being concatenated to any individual branch.  The
- *              "next" pointer of the last BRANCH in a choice points to the
- *              thing following the whole choice.  This is also where the
- *              final "next" pointer of each individual branch points; each
- *              branch starts with the operand node of a BRANCH node.
- *
- * BACK         Normal "next" pointers all implicitly point forward; BACK
- *              exists to make loop structures possible.
- *
- * STAR,PLUS    '=', and complex '*' and '+', are implemented as circular
- *              BRANCH structures using BACK.  Simple cases (one character
- *              per match) are implemented with STAR and PLUS for speed
- *              and to minimize recursive plunges.
- *
- * BRACE_LIMITS This is always followed by a BRACE_SIMPLE or BRACE_COMPLEX
- *              node, and defines the min and max limits to be used for that
- *              node.
- *
- * MOPEN,MCLOSE ...are numbered at compile time.
- * ZOPEN,ZCLOSE ...ditto
- */
-
-/*
- * A node is one char of opcode followed by two chars of "next" pointer.
- * "Next" pointers are stored as two 8-bit bytes, high order first.  The
- * value is a positive offset from the opcode of the node containing it.
- * An operand, if any, simply follows the node.  (Note that much of the
- * code generation knows about this implicit relationship.)
- *
- * Using two bytes for the "next" pointer is vast overkill for most things,
- * but allows patterns to get big without disasters.
- */
+// Opcode notes:
+//
+// BRANCH       The set of branches constituting a single choice are hooked
+//              together with their "next" pointers, since precedence prevents
+//              anything being concatenated to any individual branch.  The
+//              "next" pointer of the last BRANCH in a choice points to the
+//              thing following the whole choice.  This is also where the
+//              final "next" pointer of each individual branch points; each
+//              branch starts with the operand node of a BRANCH node.
+//
+// BACK         Normal "next" pointers all implicitly point forward; BACK
+//              exists to make loop structures possible.
+//
+// STAR,PLUS    '=', and complex '*' and '+', are implemented as circular
+//              BRANCH structures using BACK.  Simple cases (one character
+//              per match) are implemented with STAR and PLUS for speed
+//              and to minimize recursive plunges.
+//
+// BRACE_LIMITS This is always followed by a BRACE_SIMPLE or BRACE_COMPLEX
+//              node, and defines the min and max limits to be used for that
+//              node.
+//
+// MOPEN,MCLOSE ...are numbered at compile time.
+// ZOPEN,ZCLOSE ...ditto
+///
+//
+//
+// A node is one char of opcode followed by two chars of "next" pointer.
+// "Next" pointers are stored as two 8-bit bytes, high order first.  The
+// value is a positive offset from the opcode of the node containing it.
+// An operand, if any, simply follows the node.  (Note that much of the
+// code generation knows about this implicit relationship.)
+//
+// Using two bytes for the "next" pointer is vast overkill for most things,
+// but allows patterns to get big without disasters.
 #define OP(p)           ((int)(*(p)))
 #define NEXT(p)         (((*((p) + 1) & 0377) << 8) + (*((p) + 2) & 0377))
 #define OPERAND(p)      ((p) + 3)
@@ -449,9 +426,7 @@ static int regnarrate = 0;
 # include "regexp_bt.c.generated.h"
 #endif
 
-/*
- * Setup to parse the regexp.  Used once to get the length and once to do it.
- */
+// Setup to parse the regexp.  Used once to get the length and once to do it.
 static void regcomp_start(char_u *expr, int re_flags)                        // see vim_regcomp()
 {
   initchr(expr);
@@ -484,9 +459,7 @@ static bool use_multibytecode(int c)
              || utf_iscomposing(c));
 }
 
-/*
- * Emit (if appropriate) a byte of code
- */
+// Emit (if appropriate) a byte of code
 static void regc(int b)
 {
   if (regcode == JUST_CALC_SIZE) {
@@ -496,9 +469,7 @@ static void regc(int b)
   }
 }
 
-/*
- * Emit (if appropriate) a multi-byte character of code
- */
+// Emit (if appropriate) a multi-byte character of code
 static void regmbc(int c)
 {
   if (regcode == JUST_CALC_SIZE) {
@@ -508,11 +479,9 @@ static void regmbc(int c)
   }
 }
 
-/*
- * Produce the bytes for equivalence class "c".
- * Currently only handles latin1, latin9 and utf-8.
- * NOTE: When changing this function, also change nfa_emit_equi_class()
- */
+// Produce the bytes for equivalence class "c".
+// Currently only handles latin1, latin9 and utf-8.
+// NOTE: When changing this function, also change nfa_emit_equi_class()
 static void reg_equi_class(int c)
 {
   {
@@ -1481,10 +1450,8 @@ static void reg_equi_class(int c)
   regmbc(c);
 }
 
-/*
- * Emit a node.
- * Return pointer to generated code.
- */
+// Emit a node.
+// Return pointer to generated code.
 static char_u *regnode(int op)
 {
   char_u *ret;
@@ -1500,9 +1467,7 @@ static char_u *regnode(int op)
   return ret;
 }
 
-/*
- * Write a four bytes number at "p" and return pointer to the next char.
- */
+// Write a four bytes number at "p" and return pointer to the next char.
 static char_u *re_put_uint32(char_u *p, uint32_t val)
 {
   *p++ = (char_u)((val >> 24) & 0377);
@@ -1512,11 +1477,9 @@ static char_u *re_put_uint32(char_u *p, uint32_t val)
   return p;
 }
 
-/*
- * regnext - dig the "next" pointer out of a node
- * Returns NULL when calculating size, when there is no next item and when
- * there is an error.
- */
+// regnext - dig the "next" pointer out of a node
+// Returns NULL when calculating size, when there is no next item and when
+// there is an error.
 static char_u *regnext(char_u *p)
   FUNC_ATTR_NONNULL_ALL
 {
@@ -1573,9 +1536,7 @@ static void regtail(char_u *p, char_u *val)
   }
 }
 
-/*
- * Like regtail, on item after a BRANCH; nop if none.
- */
+// Like regtail, on item after a BRANCH; nop if none.
 static void regoptail(char_u *p, char_u *val)
 {
   // When op is neither BRANCH nor BRACE_COMPLEX0-9, it is "operandless"
@@ -1587,11 +1548,9 @@ static void regoptail(char_u *p, char_u *val)
   regtail(OPERAND(p), val);
 }
 
-/*
- * Insert an operator in front of already-emitted operand
- *
- * Means relocating the operand.
- */
+// Insert an operator in front of already-emitted operand
+//
+// Means relocating the operand.
 static void reginsert(int op, char_u *opnd)
 {
   char_u *src;
@@ -1615,10 +1574,8 @@ static void reginsert(int op, char_u *opnd)
   *place = NUL;
 }
 
-/*
- * Insert an operator in front of already-emitted operand.
- * Add a number to the operator.
- */
+// Insert an operator in front of already-emitted operand.
+// Add a number to the operator.
 static void reginsert_nr(int op, long val, char_u *opnd)
 {
   char_u *src;
@@ -1644,12 +1601,10 @@ static void reginsert_nr(int op, long val, char_u *opnd)
   re_put_uint32(place, (uint32_t)val);
 }
 
-/*
- * Insert an operator in front of already-emitted operand.
- * The operator has the given limit values as operands.  Also set next pointer.
- *
- * Means relocating the operand.
- */
+// Insert an operator in front of already-emitted operand.
+// The operator has the given limit values as operands.  Also set next pointer.
+//
+// Means relocating the operand.
 static void reginsert_limits(int op, long minval, long maxval, char_u *opnd)
 {
   char_u *src;
@@ -1704,13 +1659,11 @@ static int seen_endbrace(int refnum)
   return true;
 }
 
-/*
- * Parse the lowest level.
- *
- * Optimization:  gobbles an entire sequence of ordinary characters so that
- * it can turn them into a single node, which is smaller to store and
- * faster to run.  Don't do this when one_exactly is set.
- */
+// Parse the lowest level.
+//
+// Optimization:  gobbles an entire sequence of ordinary characters so that
+// it can turn them into a single node, which is smaller to store and
+// faster to run.  Don't do this when one_exactly is set.
 static char_u *regatom(int *flagp)
 {
   char_u *ret;
@@ -2289,8 +2242,7 @@ collection:
               if (c_class != 0) {
                 // produce equivalence class
                 reg_equi_class(c_class);
-              } else if ((c_class =
-                            get_coll_element(®parse)) != 0) {
+              } else if ((c_class = get_coll_element(®parse)) != 0) {
                 // produce a collating element
                 regmbc(c_class);
               } else {
@@ -2466,7 +2418,7 @@ do_multibyte:
     for (len = 0; c != NUL && (len == 0
                                || (re_multi_type(peekchr()) == NOT_MULTI
                                    && !one_exactly
-                                   && !is_Magic(c))); ++len) {
+                                   && !is_Magic(c))); len++) {
       c = no_Magic(c);
       {
         regmbc(c);
@@ -2500,15 +2452,13 @@ do_multibyte:
   return ret;
 }
 
-/*
- * Parse something followed by possible [*+=].
- *
- * Note that the branching code sequences used for = and the general cases
- * of * and + are somewhat optimized:  they use the same NOTHING node as
- * both the endmarker for their branch list and the body of the last branch.
- * It might seem that this node could be dispensed with entirely, but the
- * endmarker role is not redundant.
- */
+// Parse something followed by possible [*+=].
+//
+// Note that the branching code sequences used for = and the general cases
+// of * and + are somewhat optimized:  they use the same NOTHING node as
+// both the endmarker for their branch list and the body of the last branch.
+// It might seem that this node could be dispensed with entirely, but the
+// endmarker role is not redundant.
 static char_u *regpiece(int *flagp)
 {
   char_u *ret;
@@ -2644,10 +2594,8 @@ static char_u *regpiece(int *flagp)
   return ret;
 }
 
-/*
- * Parse one alternative of an | or & operator.
- * Implements the concatenation operator.
- */
+// Parse one alternative of an | or & operator.
+// Implements the concatenation operator.
 static char_u *regconcat(int *flagp)
 {
   char_u *first = NULL;
@@ -2722,10 +2670,8 @@ static char_u *regconcat(int *flagp)
   return first;
 }
 
-/*
- * Parse one alternative of an | operator.
- * Implements the & operator.
- */
+// Parse one alternative of an | operator.
+// Implements the & operator.
 static char_u *regbranch(int *flagp)
 {
   char_u *ret;
@@ -2874,27 +2820,25 @@ static char_u *reg(int paren, int *flagp)
   return ret;
 }
 
-/*
- * bt_regcomp() - compile a regular expression into internal code for the
- * traditional back track matcher.
- * Returns the program in allocated space.  Returns NULL for an error.
- *
- * We can't allocate space until we know how big the compiled form will be,
- * but we can't compile it (and thus know how big it is) until we've got a
- * place to put the code.  So we cheat:  we compile it twice, once with code
- * generation turned off and size counting turned on, and once "for real".
- * This also means that we don't allocate space until we are sure that the
- * thing really will compile successfully, and we never have to move the
- * code and thus invalidate pointers into it.  (Note that it has to be in
- * one piece because free() must be able to free it all.)
- *
- * Whether upper/lower case is to be ignored is decided when executing the
- * program, it does not matter here.
- *
- * Beware that the optimization-preparation code in here knows about some
- * of the structure of the compiled regexp.
- * "re_flags": RE_MAGIC and/or RE_STRING.
- */
+// bt_regcomp() - compile a regular expression into internal code for the
+// traditional back track matcher.
+// Returns the program in allocated space.  Returns NULL for an error.
+//
+// We can't allocate space until we know how big the compiled form will be,
+// but we can't compile it (and thus know how big it is) until we've got a
+// place to put the code.  So we cheat:  we compile it twice, once with code
+// generation turned off and size counting turned on, and once "for real".
+// This also means that we don't allocate space until we are sure that the
+// thing really will compile successfully, and we never have to move the
+// code and thus invalidate pointers into it.  (Note that it has to be in
+// one piece because free() must be able to free it all.)
+//
+// Whether upper/lower case is to be ignored is decided when executing the
+// program, it does not matter here.
+//
+// Beware that the optimization-preparation code in here knows about some
+// of the structure of the compiled regexp.
+// "re_flags": RE_MAGIC and/or RE_STRING.
 static regprog_T *bt_regcomp(char_u *expr, int re_flags)
 {
   char_u *scan;
@@ -2999,19 +2943,15 @@ static regprog_T *bt_regcomp(char_u *expr, int re_flags)
   return (regprog_T *)r;
 }
 
-/*
- * Check if during the previous call to vim_regcomp the EOL item "$" has been
- * found.  This is messy, but it works fine.
- */
+// Check if during the previous call to vim_regcomp the EOL item "$" has been
+// found.  This is messy, but it works fine.
 int vim_regcomp_had_eol(void)
 {
   return had_eol;
 }
 
-/*
- * Get a number after a backslash that is inside [].
- * When nothing is recognized return a backslash.
- */
+// Get a number after a backslash that is inside [].
+// When nothing is recognized return a backslash.
 static int coll_get_char(void)
 {
   int64_t nr = -1;
@@ -3037,9 +2977,7 @@ static int coll_get_char(void)
   return (int)nr;
 }
 
-/*
- * Free a compiled regexp program, returned by bt_regcomp().
- */
+// Free a compiled regexp program, returned by bt_regcomp().
 static void bt_regfree(regprog_T *prog)
 {
   xfree(prog);
@@ -3047,11 +2985,9 @@ static void bt_regfree(regprog_T *prog)
 
 #define ADVANCE_REGINPUT() MB_PTR_ADV(rex.input)
 
-/*
- * The arguments from BRACE_LIMITS are stored here.  They are actually local
- * to regmatch(), but they are here to reduce the amount of stack space used
- * (it can be called recursively many times).
- */
+// The arguments from BRACE_LIMITS are stored here.  They are actually local
+// to regmatch(), but they are here to reduce the amount of stack space used
+// (it can be called recursively many times).
 static long bl_minval;
 static long bl_maxval;
 
@@ -3108,13 +3044,11 @@ static bool reg_save_equal(const regsave_T *save)
   else  /* NOLINT */ \
   *(pp) = (savep)->se_u.ptr; }
 
-/*
- * Tentatively set the sub-expression start to the current position (after
- * calling regmatch() they will have changed).  Need to save the existing
- * values for when there is no match.
- * Use se_save() to use pointer (save_se_multi()) or position (save_se_one()),
- * depending on REG_MULTI.
- */
+// Tentatively set the sub-expression start to the current position (after
+// calling regmatch() they will have changed).  Need to save the existing
+// values for when there is no match.
+// Use se_save() to use pointer (save_se_multi()) or position (save_se_one()),
+// depending on REG_MULTI.
 static void save_se_multi(save_se_T *savep, lpos_T *posp)
 {
   savep->se_u.pos = *posp;
@@ -3494,10 +3428,8 @@ do_class:
   return (int)count;
 }
 
-/*
- * Push an item onto the regstack.
- * Returns pointer to new item.  Returns NULL when out of memory.
- */
+// Push an item onto the regstack.
+// Returns pointer to new item.  Returns NULL when out of memory.
 static regitem_T *regstack_push(regstate_T state, char_u *scan)
 {
   regitem_T *rp;
@@ -3516,9 +3448,7 @@ static regitem_T *regstack_push(regstate_T state, char_u *scan)
   return rp;
 }
 
-/*
- * Pop an item from the regstack.
- */
+// Pop an item from the regstack.
 static void regstack_pop(char_u **scan)
 {
   regitem_T *rp;
@@ -4643,7 +4573,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
         // Pop the state.  Restore pointers when there is no match.
         if (status == RA_NOMATCH) {
           reg_restore(&rp->rs_un.regsave, &backpos);
-          --brace_count[rp->rs_no];             // decrement match count
+          brace_count[rp->rs_no]--;             // decrement match count
         }
         regstack_pop(&scan);
         break;
@@ -4653,7 +4583,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
         if (status == RA_NOMATCH) {
           // There was no match, but we did find enough matches.
           reg_restore(&rp->rs_un.regsave, &backpos);
-          --brace_count[rp->rs_no];
+          brace_count[rp->rs_no]--;
           // continue with the items after "\{}"
           status = RA_CONT;
         }
@@ -5247,9 +5177,7 @@ static long bt_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, linenr_T
   return bt_regexec_both(NULL, col, tm, timed_out);
 }
 
-/*
- * Compare a number with the operand of RE_LNUM, RE_COL or RE_VCOL.
- */
+// Compare a number with the operand of RE_LNUM, RE_COL or RE_VCOL.
 static int re_num_cmp(uint32_t val, char_u *scan)
 {
   uint32_t n = (uint32_t)OPERAND_MIN(scan);
@@ -5265,9 +5193,7 @@ static int re_num_cmp(uint32_t val, char_u *scan)
 
 #ifdef BT_REGEXP_DUMP
 
-/*
- * regdump - dump a regexp onto stdout in vaguely comprehensible form
- */
+// regdump - dump a regexp onto stdout in vaguely comprehensible form
 static void regdump(char_u *pattern, bt_regprog_T *r)
 {
   char_u *s;
@@ -5353,9 +5279,7 @@ static void regdump(char_u *pattern, bt_regprog_T *r)
 
 #ifdef REGEXP_DEBUG
 
-/*
- * regprop - printable representation of opcode
- */
+// regprop - printable representation of opcode
 static char_u *regprop(char_u *op)
 {
   char *p;
diff --git a/src/nvim/regexp_defs.h b/src/nvim/regexp_defs.h
index b24ed350e8..ee32b8d13a 100644
--- a/src/nvim/regexp_defs.h
+++ b/src/nvim/regexp_defs.h
@@ -1,13 +1,11 @@
-/*
- * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
- *
- * This is NOT the original regular expression code as written by Henry
- * Spencer.  This code has been modified specifically for use with Vim, and
- * should not be used apart from compiling Vim.  If you want a good regular
- * expression library, get the original code.
- *
- * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
- */
+// NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
+//
+// This is NOT the original regular expression code as written by Henry
+// Spencer.  This code has been modified specifically for use with Vim, and
+// should not be used apart from compiling Vim.  If you want a good regular
+// expression library, get the original code.
+//
+// NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
 
 #ifndef NVIM_REGEXP_DEFS_H
 #define NVIM_REGEXP_DEFS_H
@@ -17,18 +15,14 @@
 #include "nvim/pos.h"
 #include "nvim/types.h"
 
-/*
- * The number of sub-matches is limited to 10.
- * The first one (index 0) is the whole match, referenced with "\0".
- * The second one (index 1) is the first sub-match, referenced with "\1".
- * This goes up to the tenth (index 9), referenced with "\9".
- */
+// The number of sub-matches is limited to 10.
+// The first one (index 0) is the whole match, referenced with "\0".
+// The second one (index 1) is the first sub-match, referenced with "\1".
+// This goes up to the tenth (index 9), referenced with "\9".
 #define NSUBEXP  10
 
-/*
- * In the NFA engine: how many braces are allowed.
- * TODO(RE): Use dynamic memory allocation instead of static, like here
- */
+// In the NFA engine: how many braces are allowed.
+// TODO(RE): Use dynamic memory allocation instead of static, like here
 #define NFA_MAX_BRACES 20
 
 // In the NFA engine: how many states are allowed.
@@ -61,11 +55,9 @@ typedef struct {
 
 #include "nvim/buffer_defs.h"
 
-/*
- * Structure returned by vim_regcomp() to pass on to vim_regexec().
- * This is the general structure. For the actual matcher, two specific
- * structures are used. See code below.
- */
+// Structure returned by vim_regcomp() to pass on to vim_regexec().
+// This is the general structure. For the actual matcher, two specific
+// structures are used. See code below.
 struct regprog {
   regengine_T *engine;
   unsigned regflags;
@@ -74,11 +66,9 @@ struct regprog {
   bool re_in_use;      ///< prog is being executed
 };
 
-/*
- * Structure used by the back track matcher.
- * These fields are only to be used in regexp.c!
- * See regexp.c for an explanation.
- */
+// Structure used by the back track matcher.
+// These fields are only to be used in regexp.c!
+// See regexp.c for an explanation.
 typedef struct {
   // These four members implement regprog_T.
   regengine_T *engine;
@@ -107,9 +97,7 @@ struct nfa_state {
   int val;
 };
 
-/*
- * Structure used by the NFA matcher.
- */
+// Structure used by the NFA matcher.
 typedef struct {
   // These four members implement regprog_T.
   regengine_T *engine;
@@ -133,11 +121,9 @@ typedef struct {
   nfa_state_T state[1];                 // actually longer..
 } nfa_regprog_T;
 
-/*
- * Structure to be used for single-line matching.
- * Sub-match "no" starts at "startp[no]" and ends just before "endp[no]".
- * When there is no match, the pointer is NULL.
- */
+// Structure to be used for single-line matching.
+// Sub-match "no" starts at "startp[no]" and ends just before "endp[no]".
+// When there is no match, the pointer is NULL.
 typedef struct {
   regprog_T *regprog;
   char *startp[NSUBEXP];
@@ -145,11 +131,9 @@ typedef struct {
   bool rm_ic;
 } regmatch_T;
 
-/*
- * Structure used to store external references: "\z\(\)" to "\z\1".
- * Use a reference count to avoid the need to copy this around.  When it goes
- * from 1 to zero the matches need to be freed.
- */
+// Structure used to store external references: "\z\(\)" to "\z\1".
+// Use a reference count to avoid the need to copy this around.  When it goes
+// from 1 to zero the matches need to be freed.
 struct reg_extmatch {
   int16_t refcnt;
   char_u *matches[NSUBEXP];
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index d4d2ed28cc..c4102c40ec 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -1,11 +1,9 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
-/*
- * NFA regular expression implementation.
- *
- * This file is included in "regexp.c".
- */
+// NFA regular expression implementation.
+//
+// This file is included in "regexp.c".
 
 #include 
 #include 
@@ -383,10 +381,8 @@ static void nfa_regcomp_start(char_u *expr, int re_flags)
   regcomp_start(expr, re_flags);
 }
 
-/*
- * Figure out if the NFA state list starts with an anchor, must match at start
- * of the line.
- */
+// Figure out if the NFA state list starts with an anchor, must match at start
+// of the line.
 static int nfa_get_reganch(nfa_state_T *start, int depth)
 {
   nfa_state_T *p = start;
@@ -441,10 +437,8 @@ static int nfa_get_reganch(nfa_state_T *start, int depth)
   return 0;
 }
 
-/*
- * Figure out if the NFA state list starts with a character which must match
- * at start of the match.
- */
+// Figure out if the NFA state list starts with a character which must match
+// at start of the match.
 static int nfa_get_regstart(nfa_state_T *start, int depth)
 {
   nfa_state_T *p = start;
@@ -521,11 +515,9 @@ static int nfa_get_regstart(nfa_state_T *start, int depth)
   return 0;
 }
 
-/*
- * Figure out if the NFA state list contains just literal text and nothing
- * else.  If so return a string in allocated memory with what must match after
- * regstart.  Otherwise return NULL.
- */
+// Figure out if the NFA state list contains just literal text and nothing
+// else.  If so return a string in allocated memory with what must match after
+// regstart.  Otherwise return NULL.
 static char_u *nfa_get_match_text(nfa_state_T *start)
 {
   nfa_state_T *p = start;
@@ -557,10 +549,8 @@ static char_u *nfa_get_match_text(nfa_state_T *start)
   return ret;
 }
 
-/*
- * Allocate more space for post_start.  Called when
- * running above the estimated number of states.
- */
+// Allocate more space for post_start.  Called when
+// running above the estimated number of states.
 static void realloc_post_list(void)
 {
   // For weird patterns the number of states can be very high. Increasing by
@@ -572,16 +562,14 @@ static void realloc_post_list(void)
   post_start = new_start;
 }
 
-/*
- * Search between "start" and "end" and try to recognize a
- * character class in expanded form. For example [0-9].
- * On success, return the id the character class to be emitted.
- * On failure, return 0 (=FAIL)
- * Start points to the first char of the range, while end should point
- * to the closing brace.
- * Keep in mind that 'ignorecase' applies at execution time, thus [a-z] may
- * need to be interpreted as [a-zA-Z].
- */
+// Search between "start" and "end" and try to recognize a
+// character class in expanded form. For example [0-9].
+// On success, return the id the character class to be emitted.
+// On failure, return 0 (=FAIL)
+// Start points to the first char of the range, while end should point
+// to the closing brace.
+// Keep in mind that 'ignorecase' applies at execution time, thus [a-z] may
+// need to be interpreted as [a-zA-Z].
 static int nfa_recognize_char_class(char_u *start, char_u *end, int extra_newl)
 {
 #define CLASS_not            0x80
@@ -700,14 +688,12 @@ static int nfa_recognize_char_class(char_u *start, char_u *end, int extra_newl)
   return FAIL;
 }
 
-/*
- * Produce the bytes for equivalence class "c".
- * Currently only handles latin1, latin9 and utf-8.
- * Emits bytes in postfix notation: 'a,b,NFA_OR,c,NFA_OR' is
- * equivalent to 'a OR b OR c'
- *
- * NOTE! When changing this function, also update reg_equi_class()
- */
+// Produce the bytes for equivalence class "c".
+// Currently only handles latin1, latin9 and utf-8.
+// Emits bytes in postfix notation: 'a,b,NFA_OR,c,NFA_OR' is
+// equivalent to 'a OR b OR c'
+//
+// NOTE! When changing this function, also update reg_equi_class()
 static void nfa_emit_equi_class(int c)
 {
 #define EMIT2(c)   EMIT(c); EMIT(NFA_CONCAT);
@@ -1778,26 +1764,22 @@ static void nfa_emit_equi_class(int c)
 #undef EMIT2
 }
 
-/*
- * Code to parse regular expression.
- *
- * We try to reuse parsing functions in regexp.c to
- * minimize surprise and keep the syntax consistent.
- */
-
-/*
- * Parse the lowest level.
- *
- * An atom can be one of a long list of items.  Many atoms match one character
- * in the text.  It is often an ordinary character or a character class.
- * Braces can be used to make a pattern into an atom.  The "\z(\)" construct
- * is only for syntax highlighting.
- *
- * atom    ::=     ordinary-atom
- *     or  \( pattern \)
- *     or  \%( pattern \)
- *     or  \z( pattern \)
- */
+// Code to parse regular expression.
+//
+// We try to reuse parsing functions in regexp.c to
+// minimize surprise and keep the syntax consistent.
+
+// Parse the lowest level.
+//
+// An atom can be one of a long list of items.  Many atoms match one character
+// in the text.  It is often an ordinary character or a character class.
+// Braces can be used to make a pattern into an atom.  The "\z(\)" construct
+// is only for syntax highlighting.
+//
+// atom    ::=     ordinary-atom
+//     or  \( pattern \)
+//     or  \%( pattern \)
+//     or  \z( pattern \)
 static int nfa_regatom(void)
 {
   int c;
@@ -1862,9 +1844,7 @@ static int nfa_regatom(void)
     // "\_x" is character class plus newline
     FALLTHROUGH;
 
-  /*
-   * Character classes.
-   */
+  // Character classes.
   case Magic('.'):
   case Magic('i'):
   case Magic('I'):
@@ -2228,24 +2208,20 @@ static int nfa_regatom(void)
 
   case Magic('['):
 collection:
-    /*
-     * [abc]  uses NFA_START_COLL - NFA_END_COLL
-     * [^abc] uses NFA_START_NEG_COLL - NFA_END_NEG_COLL
-     * Each character is produced as a regular state, using
-     * NFA_CONCAT to bind them together.
-     * Besides normal characters there can be:
-     * - character classes  NFA_CLASS_*
-     * - ranges, two characters followed by NFA_RANGE.
-     */
+    // [abc]  uses NFA_START_COLL - NFA_END_COLL
+    // [^abc] uses NFA_START_NEG_COLL - NFA_END_NEG_COLL
+    // Each character is produced as a regular state, using
+    // NFA_CONCAT to bind them together.
+    // Besides normal characters there can be:
+    // - character classes  NFA_CLASS_*
+    // - ranges, two characters followed by NFA_RANGE.
 
     p = (char_u *)regparse;
     endp = skip_anyof((char *)p);
     if (*endp == ']') {
-      /*
-       * Try to reverse engineer character classes. For example,
-       * recognize that [0-9] stands for \d and [A-Za-z_] for \h,
-       * and perform the necessary substitutions in the NFA.
-       */
+      // Try to reverse engineer character classes. For example,
+      // recognize that [0-9] stands for \d and [A-Za-z_] for \h,
+      // and perform the necessary substitutions in the NFA.
       int result = nfa_recognize_char_class((char_u *)regparse, endp, extra == NFA_ADD_NL);
       if (result != FAIL) {
         if (result >= NFA_FIRST_NL && result <= NFA_LAST_NL) {
@@ -2259,10 +2235,8 @@ collection:
         MB_PTR_ADV(regparse);
         return OK;
       }
-      /*
-       * Failed to recognize a character class. Use the simple
-       * version that turns [abc] into 'a' OR 'b' OR 'c'
-       */
+      // Failed to recognize a character class. Use the simple
+      // version that turns [abc] into 'a' OR 'b' OR 'c'
       startc = -1;
       negated = false;
       if (*regparse == '^') {                           // negated range
@@ -2554,16 +2528,14 @@ nfa_do_multibyte:
   return OK;
 }
 
-/*
- * Parse something followed by possible [*+=].
- *
- * A piece is an atom, possibly followed by a multi, an indication of how many
- * times the atom can be matched.  Example: "a*" matches any sequence of "a"
- * characters: "", "a", "aa", etc.
- *
- * piece   ::=      atom
- *      or  atom  multi
- */
+// Parse something followed by possible [*+=].
+//
+// A piece is an atom, possibly followed by a multi, an indication of how many
+// times the atom can be matched.  Example: "a*" matches any sequence of "a"
+// characters: "", "a", "aa", etc.
+//
+// piece   ::=      atom
+//      or  atom  multi
 static int nfa_regpiece(void)
 {
   int i;
@@ -2601,17 +2573,15 @@ static int nfa_regpiece(void)
     break;
 
   case Magic('+'):
-    /*
-     * Trick: Normally, (a*)\+ would match the whole input "aaa".  The
-     * first and only submatch would be "aaa". But the backtracking
-     * engine interprets the plus as "try matching one more time", and
-     * a* matches a second time at the end of the input, the empty
-     * string.
-     * The submatch will be the empty string.
-     *
-     * In order to be consistent with the old engine, we replace
-     * + with *
-     */
+    // Trick: Normally, (a*)\+ would match the whole input "aaa".  The
+    // first and only submatch would be "aaa". But the backtracking
+    // engine interprets the plus as "try matching one more time", and
+    // a* matches a second time at the end of the input, the empty
+    // string.
+    // The submatch will be the empty string.
+    //
+    // In order to be consistent with the old engine, we replace
+    // + with *
     restore_parse_state(&old_state);
     curchr = -1;
     if (nfa_regatom() == FAIL) {
@@ -2770,16 +2740,14 @@ static int nfa_regpiece(void)
   return OK;
 }
 
-/*
- * Parse one or more pieces, concatenated.  It matches a match for the
- * first piece, followed by a match for the second piece, etc.  Example:
- * "f[0-9]b", first matches "f", then a digit and then "b".
- *
- * concat  ::=      piece
- *      or  piece piece
- *      or  piece piece piece
- *      etc.
- */
+// Parse one or more pieces, concatenated.  It matches a match for the
+// first piece, followed by a match for the second piece, etc.  Example:
+// "f[0-9]b", first matches "f", then a digit and then "b".
+//
+// concat  ::=      piece
+//      or  piece piece
+//      or  piece piece piece
+//      etc.
 static int nfa_regconcat(void)
 {
   bool cont = true;
@@ -2843,18 +2811,16 @@ static int nfa_regconcat(void)
   return OK;
 }
 
-/*
- * Parse a branch, one or more concats, separated by "\&".  It matches the
- * last concat, but only if all the preceding concats also match at the same
- * position.  Examples:
- *      "foobeep\&..." matches "foo" in "foobeep".
- *      ".*Peter\&.*Bob" matches in a line containing both "Peter" and "Bob"
- *
- * branch ::=       concat
- *              or  concat \& concat
- *              or  concat \& concat \& concat
- *              etc.
- */
+// Parse a branch, one or more concats, separated by "\&".  It matches the
+// last concat, but only if all the preceding concats also match at the same
+// position.  Examples:
+//      "foobeep\&..." matches "foo" in "foobeep".
+//      ".*Peter\&.*Bob" matches in a line containing both "Peter" and "Bob"
+//
+// branch ::=       concat
+//              or  concat \& concat
+//              or  concat \& concat \& concat
+//              etc.
 static int nfa_regbranch(void)
 {
   int old_post_pos;
@@ -3311,9 +3277,7 @@ static FILE *log_fd;
 static char_u e_log_open_failed[] =
   N_("Could not open temporary log file for writing, displaying on stderr... ");
 
-/*
- * Print the postfix notation of the current regexp.
- */
+// Print the postfix notation of the current regexp.
 static void nfa_postfix_dump(char_u *expr, int retval)
 {
   int *p;
@@ -3341,9 +3305,7 @@ static void nfa_postfix_dump(char_u *expr, int retval)
   }
 }
 
-/*
- * Print the NFA starting with a root node "state".
- */
+// Print the NFA starting with a root node "state".
 static void nfa_print_state(FILE *debugf, nfa_state_T *state)
 {
   garray_T indent;
@@ -3413,9 +3375,7 @@ static void nfa_print_state2(FILE *debugf, nfa_state_T *state, garray_T *indent)
   ga_append(indent, NUL);
 }
 
-/*
- * Print the NFA state machine.
- */
+// Print the NFA state machine.
 static void nfa_dump(nfa_regprog_T *prog)
 {
   FILE *debugf = fopen(NFA_REGEXP_DUMP_LOG, "a");
@@ -3437,12 +3397,10 @@ static void nfa_dump(nfa_regprog_T *prog)
     fclose(debugf);
   }
 }
-#endif      /* REGEXP_DEBUG */
+#endif  // REGEXP_DEBUG
 
-/*
- * Parse r.e. @expr and convert it into postfix form.
- * Return the postfix string on success, NULL otherwise.
- */
+// Parse r.e. @expr and convert it into postfix form.
+// Return the postfix string on success, NULL otherwise.
 static int *re2post(void)
 {
   if (nfa_reg(REG_NOPAREN) == FAIL) {
@@ -3454,18 +3412,14 @@ static int *re2post(void)
 
 // NB. Some of the code below is inspired by Russ's.
 
-/*
- * Represents an NFA state plus zero or one or two arrows exiting.
- * if c == MATCH, no arrows out; matching state.
- * If c == SPLIT, unlabeled arrows to out and out1 (if != NULL).
- * If c < 256, labeled arrow with character c to out.
- */
+// Represents an NFA state plus zero or one or two arrows exiting.
+// if c == MATCH, no arrows out; matching state.
+// If c == SPLIT, unlabeled arrows to out and out1 (if != NULL).
+// If c < 256, labeled arrow with character c to out.
 
 static nfa_state_T *state_ptr;  // points to nfa_prog->state
 
-/*
- * Allocate and initialize nfa_state_T.
- */
+// Allocate and initialize nfa_state_T.
 static nfa_state_T *alloc_state(int c, nfa_state_T *out, nfa_state_T *out1)
 {
   nfa_state_T *s;
@@ -3488,16 +3442,12 @@ static nfa_state_T *alloc_state(int c, nfa_state_T *out, nfa_state_T *out1)
   return s;
 }
 
-/*
- * A partially built NFA without the matching state filled in.
- * Frag_T.start points at the start state.
- * Frag_T.out is a list of places that need to be set to the
- * next state for this fragment.
- */
+// A partially built NFA without the matching state filled in.
+// Frag_T.start points at the start state.
+// Frag_T.out is a list of places that need to be set to the
+// next state for this fragment.
 
-/*
- * Initialize a Frag_T struct and return it.
- */
+// Initialize a Frag_T struct and return it.
 static Frag_T frag(nfa_state_T *start, Ptrlist *out)
 {
   Frag_T n;
@@ -3507,9 +3457,7 @@ static Frag_T frag(nfa_state_T *start, Ptrlist *out)
   return n;
 }
 
-/*
- * Create singleton list containing just outp.
- */
+// Create singleton list containing just outp.
 static Ptrlist *list1(nfa_state_T **outp)
 {
   Ptrlist *l;
@@ -3519,9 +3467,7 @@ static Ptrlist *list1(nfa_state_T **outp)
   return l;
 }
 
-/*
- * Patch the list of states at out to point to start.
- */
+// Patch the list of states at out to point to start.
 static void patch(Ptrlist *l, nfa_state_T *s)
 {
   Ptrlist *next;
@@ -3532,9 +3478,7 @@ static void patch(Ptrlist *l, nfa_state_T *s)
   }
 }
 
-/*
- * Join the two lists l1 and l2, returning the combination.
- */
+// Join the two lists l1 and l2, returning the combination.
 static Ptrlist *append(Ptrlist *l1, Ptrlist *l2)
 {
   Ptrlist *oldl1;
@@ -3547,9 +3491,7 @@ static Ptrlist *append(Ptrlist *l1, Ptrlist *l2)
   return oldl1;
 }
 
-/*
- * Stack used for transforming postfix form into NFA.
- */
+// Stack used for transforming postfix form into NFA.
 static Frag_T empty;
 
 static void st_error(int *postfix, int *end, int *p)
@@ -3592,9 +3534,7 @@ static void st_error(int *postfix, int *end, int *p)
   emsg(_("E874: (NFA) Could not pop the stack!"));
 }
 
-/*
- * Push an item onto the stack.
- */
+// Push an item onto the stack.
 static void st_push(Frag_T s, Frag_T **p, Frag_T *stack_end)
 {
   Frag_T *stackp = *p;
@@ -3606,9 +3546,7 @@ static void st_push(Frag_T s, Frag_T **p, Frag_T *stack_end)
   *p = *p + 1;
 }
 
-/*
- * Pop an item from the stack.
- */
+// Pop an item from the stack.
 static Frag_T st_pop(Frag_T **p, Frag_T *stack)
 {
   Frag_T *stackp;
@@ -3621,10 +3559,8 @@ static Frag_T st_pop(Frag_T **p, Frag_T *stack)
   return **p;
 }
 
-/*
- * Estimate the maximum byte length of anything matching "state".
- * When unknown or unlimited return -1.
- */
+// Estimate the maximum byte length of anything matching "state".
+// When unknown or unlimited return -1.
 static int nfa_max_width(nfa_state_T *startstate, int depth)
 {
   int l, r;
@@ -3827,10 +3763,8 @@ static int nfa_max_width(nfa_state_T *startstate, int depth)
   return -1;
 }
 
-/*
- * Convert a postfix form into its equivalent NFA.
- * Return the NFA start state on success, NULL otherwise.
- */
+// Convert a postfix form into its equivalent NFA.
+// Return the NFA start state on success, NULL otherwise.
 static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
 {
   int *p;
@@ -3866,7 +3800,7 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
     stack_end = stack + (nstate + 1);
   }
 
-  for (p = postfix; p < end; ++p) {
+  for (p = postfix; p < end; p++) {
     switch (*p) {
     case NFA_CONCAT:
       // Concatenation.
@@ -4350,15 +4284,13 @@ theend:
 #undef PUSH
 }
 
-/*
- * After building the NFA program, inspect it to add optimization hints.
- */
+// After building the NFA program, inspect it to add optimization hints.
 static void nfa_postprocess(nfa_regprog_T *prog)
 {
   int i;
   int c;
 
-  for (i = 0; i < prog->nstate; ++i) {
+  for (i = 0; i < prog->nstate; i++) {
     c = prog->state[i].c;
     if (c == NFA_START_INVISIBLE
         || c == NFA_START_INVISIBLE_NEG
@@ -4490,9 +4422,7 @@ static void clear_sub(regsub_T *sub)
   sub->in_use = 0;
 }
 
-/*
- * Copy the submatches from "from" to "to".
- */
+// Copy the submatches from "from" to "to".
 static void copy_sub(regsub_T *to, regsub_T *from)
 {
   to->in_use = from->in_use;
@@ -4508,9 +4438,7 @@ static void copy_sub(regsub_T *to, regsub_T *from)
   }
 }
 
-/*
- * Like copy_sub() but exclude the main match.
- */
+// Like copy_sub() but exclude the main match.
 static void copy_sub_off(regsub_T *to, regsub_T *from)
 {
   if (to->in_use < from->in_use) {
@@ -4528,9 +4456,7 @@ static void copy_sub_off(regsub_T *to, regsub_T *from)
   }
 }
 
-/*
- * Like copy_sub() but only do the end of the main match if \ze is present.
- */
+// Like copy_sub() but only do the end of the main match if \ze is present.
 static void copy_ze_off(regsub_T *to, regsub_T *from)
 {
   if (rex.nfa_has_zend) {
@@ -4954,7 +4880,7 @@ static regsubs_T *addstate(nfa_list_T *l, nfa_state_T *state, regsubs_T *subs_ar
         // When called from addstate_here() do insert before
         // existing states.
         if (add_here) {
-          for (k = 0; k < l->n && k < listindex; ++k) {
+          for (k = 0; k < l->n && k < listindex; k++) {
             if (l->t[k].state->id == state->id) {
               found = true;
               break;
@@ -5094,7 +5020,7 @@ skip_add:
         save_in_use = -1;
       } else {
         save_in_use = sub->in_use;
-        for (i = sub->in_use; i < subidx; ++i) {
+        for (i = sub->in_use; i < subidx; i++) {
           sub->list.multi[i].start_lnum = -1;
           sub->list.multi[i].end_lnum = -1;
         }
@@ -5115,7 +5041,7 @@ skip_add:
         save_in_use = -1;
       } else {
         save_in_use = sub->in_use;
-        for (i = sub->in_use; i < subidx; ++i) {
+        for (i = sub->in_use; i < subidx; i++) {
           sub->list.line[i].start = NULL;
           sub->list.line[i].end = NULL;
         }
@@ -5314,9 +5240,7 @@ static regsubs_T *addstate_here(nfa_list_T *l, nfa_state_T *state, regsubs_T *su
   return r;
 }
 
-/*
- * Check character class "class" against current character c.
- */
+// Check character class "class" against current character c.
 static int check_char_class(int class, int c)
 {
   switch (class) {
@@ -5502,11 +5426,9 @@ static int match_zref(int subidx, int *bytelen)
   return false;
 }
 
-/*
- * Save list IDs for all NFA states of "prog" into "list".
- * Also reset the IDs to zero.
- * Only used for the recursive value lastlist[1].
- */
+// Save list IDs for all NFA states of "prog" into "list".
+// Also reset the IDs to zero.
+// Only used for the recursive value lastlist[1].
 static void nfa_save_listids(nfa_regprog_T *prog, int *list)
 {
   int i;
@@ -5521,9 +5443,7 @@ static void nfa_save_listids(nfa_regprog_T *prog, int *list)
   }
 }
 
-/*
- * Restore list IDs from "list" to all NFA states.
- */
+// Restore list IDs from "list" to all NFA states.
 static void nfa_restore_listids(nfa_regprog_T *prog, int *list)
 {
   int i;
@@ -5547,11 +5467,9 @@ static bool nfa_re_num_cmp(uintmax_t val, int op, uintmax_t pos)
   return val == pos;
 }
 
-/*
- * Recursively call nfa_regmatch()
- * "pim" is NULL or contains info about a Postponed Invisible Match (start
- * position).
- */
+// Recursively call nfa_regmatch()
+// "pim" is NULL or contains info about a Postponed Invisible Match (start
+// position).
 static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T *prog,
                               regsubs_T *submatch, regsubs_T *m, int **listids, int *listids_len)
   FUNC_ATTR_NONNULL_ARG(1, 3, 5, 6, 7)
@@ -5691,12 +5609,10 @@ static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T
   return result;
 }
 
-/*
- * Estimate the chance of a match with "state" failing.
- * empty match: 0
- * NFA_ANY: 1
- * specific character: 99
- */
+// Estimate the chance of a match with "state" failing.
+// empty match: 0
+// NFA_ANY: 1
+// specific character: 99
 static int failure_chance(nfa_state_T *state, int depth)
 {
   int c = state->c;
@@ -5851,9 +5767,7 @@ static int failure_chance(nfa_state_T *state, int depth)
   return 50;
 }
 
-/*
- * Skip until the char "c" we know a match must start with.
- */
+// Skip until the char "c" we know a match must start with.
 static int skip_to_start(int c, colnr_T *colp)
 {
   const char_u *const s = cstrchr(rex.line + *colp, c);
@@ -5864,11 +5778,9 @@ static int skip_to_start(int c, colnr_T *colp)
   return OK;
 }
 
-/*
- * Check for a match with match_text.
- * Called after skip_to_start() has found regstart.
- * Returns zero for no match, 1 for a match.
- */
+// Check for a match with match_text.
+// Called after skip_to_start() has found regstart.
+// Returns zero for no match, 1 for a match.
 static long find_match_text(colnr_T startcol, int regstart, char_u *match_text)
 {
 #define PTR2LEN(x) utf_ptr2len(x)
@@ -6038,9 +5950,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
     add_off = clen; \
   }
 
-  /*
-   * Run for each character.
-   */
+  // Run for each character.
   for (;;) {
     int curc = utf_ptr2char((char *)rex.input);
     int clen = utfc_ptr2len((char *)rex.input);
@@ -6086,9 +5996,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
 #ifdef NFA_REGEXP_DEBUG_LOG
     fprintf(debug, "\n-------------------\n");
 #endif
-    /*
-     * If the state lists are empty we can stop.
-     */
+    // If the state lists are empty we can stop.
     if (thislist->n == 0) {
       break;
     }
@@ -6131,10 +6039,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
       }
 #endif
 
-      /*
-       * Handle the possible codes of the current state.
-       * The most important is NFA_MATCH.
-       */
+      // Handle the possible codes of the current state.
+      // The most important is NFA_MATCH.
       add_state = NULL;
       add_here = false;
       add_count = 0;
@@ -7525,10 +7431,8 @@ theend:
   return retval;
 }
 
-/*
- * Compile a regular expression into internal code for the NFA matcher.
- * Returns the program in allocated space.  Returns NULL for an error.
- */
+// Compile a regular expression into internal code for the NFA matcher.
+// Returns the program in allocated space.  Returns NULL for an error.
 static regprog_T *nfa_regcomp(char_u *expr, int re_flags)
 {
   nfa_regprog_T *prog = NULL;
@@ -7554,11 +7458,9 @@ static regprog_T *nfa_regcomp(char_u *expr, int re_flags)
     goto fail;              // Cascaded (syntax?) error
   }
 
-  /*
-   * In order to build the NFA, we parse the input regexp twice:
-   * 1. first pass to count size (so we can allocate space)
-   * 2. second to emit code
-   */
+  // In order to build the NFA, we parse the input regexp twice:
+  // 1. first pass to count size (so we can allocate space)
+  // 2. second to emit code
 #ifdef REGEXP_DEBUG
   {
     FILE *f = fopen(NFA_REGEXP_RUN_LOG, "a");
@@ -7573,10 +7475,8 @@ static regprog_T *nfa_regcomp(char_u *expr, int re_flags)
   }
 #endif
 
-  /*
-   * PASS 1
-   * Count number of NFA states in "nstate". Do not build the NFA.
-   */
+  // PASS 1
+  // Count number of NFA states in "nstate". Do not build the NFA.
   post2nfa(postfix, post_ptr, true);
 
   // allocate the regprog with space for the compiled regexp
@@ -7585,10 +7485,8 @@ static regprog_T *nfa_regcomp(char_u *expr, int re_flags)
   state_ptr = prog->state;
   prog->re_in_use = false;
 
-  /*
-   * PASS 2
-   * Build the NFA
-   */
+  // PASS 2
+  // Build the NFA
   prog->start = post2nfa(postfix, post_ptr, false);
   if (prog->start == NULL) {
     goto fail;
@@ -7632,9 +7530,7 @@ fail:
   goto out;
 }
 
-/*
- * Free a compiled regexp program, returned by nfa_regcomp().
- */
+// Free a compiled regexp program, returned by nfa_regcomp().
 static void nfa_regfree(regprog_T *prog)
 {
   if (prog != NULL) {
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index 7837f242b5..3983192f18 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -2465,9 +2465,9 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
 
             aff_entry->ae_cond = (char_u *)getroom_save(spin, (char_u *)items[4]);
             if (*items[0] == 'P') {
-              sprintf((char *)buf, "^%s", items[4]);
+              sprintf((char *)buf, "^%s", items[4]);  // NOLINT(runtime/printf)
             } else {
-              sprintf((char *)buf, "%s$", items[4]);
+              sprintf((char *)buf, "%s$", items[4]);  // NOLINT(runtime/printf)
             }
             aff_entry->ae_prog = vim_regcomp((char *)buf, RE_MAGIC + RE_STRING + RE_STRICT);
             if (aff_entry->ae_prog == NULL) {
@@ -2514,8 +2514,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
                     onecap_copy((char_u *)items[4], buf, true);
                     aff_entry->ae_cond = (char_u *)getroom_save(spin, buf);
                     if (aff_entry->ae_cond != NULL) {
-                      sprintf((char *)buf, "^%s",
-                              aff_entry->ae_cond);
+                      sprintf((char *)buf, "^%s", aff_entry->ae_cond);  // NOLINT(runtime/printf)
                       vim_regfree(aff_entry->ae_prog);
                       aff_entry->ae_prog = vim_regcomp((char *)buf, RE_MAGIC + RE_STRING);
                     }
@@ -3614,7 +3613,7 @@ static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afff
               if (store_aff_word(spin, newword, ae->ae_flags,
                                  affile, &affile->af_suff, xht,
                                  use_condit & (xht == NULL
-                                    ? ~0 :  ~CONDIT_SUF),
+                                               ? ~0 :  ~CONDIT_SUF),
                                  use_flags, use_pfxlist, pfxlen) == FAIL) {
                 retval = FAIL;
               }
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index 4d1401293b..7086f47e33 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -1035,9 +1035,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
                     : va_arg(ap, long long));  // NOLINT (runtime/int)
             break;
           case 'z':
-            arg = (tvs
-                       ? (ptrdiff_t)tv_nr(tvs, &arg_idx)
-                       : va_arg(ap, ptrdiff_t));
+            arg = (tvs ? (ptrdiff_t)tv_nr(tvs, &arg_idx) : va_arg(ap, ptrdiff_t));
             break;
           }
           if (arg > 0) {
@@ -1049,19 +1047,13 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
           // unsigned
           switch (length_modifier) {
           case '\0':
-            uarg = (unsigned int)(tvs
-                                      ? tv_nr(tvs, &arg_idx)
-                                      : va_arg(ap, unsigned int));
+            uarg = (unsigned int)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned int));
             break;
           case 'h':
-            uarg = (uint16_t)(tvs
-                                  ? tv_nr(tvs, &arg_idx)
-                                  : va_arg(ap, unsigned int));
+            uarg = (uint16_t)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned int));
             break;
           case 'l':
-            uarg = (tvs
-                        ? (unsigned long)tv_nr(tvs, &arg_idx)
-                        : va_arg(ap, unsigned long));
+            uarg = (tvs ? (unsigned long)tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned long));
             break;
           case '2':
             uarg = (uintmax_t)(unsigned long long)(  // NOLINT (runtime/int)
@@ -1071,9 +1063,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
                     : va_arg(ap, unsigned long long));  // NOLINT (runtime/int)
             break;
           case 'z':
-            uarg = (tvs
-                        ? (size_t)tv_nr(tvs, &arg_idx)
-                        : va_arg(ap, size_t));
+            uarg = (tvs ? (size_t)tv_nr(tvs, &arg_idx) : va_arg(ap, size_t));
             break;
           }
           arg_sign = (uarg != 0);
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index fb82df4fe9..7248d1240e 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -1655,13 +1655,12 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
                 && (spp->sp_type == SPTYPE_MATCH
                     || spp->sp_type == SPTYPE_START)
                 && (current_next_list != NULL
-                           ? in_id_list(NULL, current_next_list,
-                                        &spp->sp_syn, 0)
-                           : (cur_si == NULL
-                              ? !(spp->sp_flags & HL_CONTAINED)
-                              : in_id_list(cur_si,
-                                           cur_si->si_cont_list, &spp->sp_syn,
-                                           spp->sp_flags & HL_CONTAINED)))) {
+                    ? in_id_list(NULL, current_next_list, &spp->sp_syn, 0)
+                    : (cur_si == NULL
+                       ? !(spp->sp_flags & HL_CONTAINED)
+                       : in_id_list(cur_si,
+                                    cur_si->si_cont_list, &spp->sp_syn,
+                                    spp->sp_flags & HL_CONTAINED)))) {
               // If we already tried matching in this line, and
               // there isn't a match before next_match_col, skip
               // this item.
@@ -2788,9 +2787,9 @@ static keyentry_T *match_keyword(char *keyword, hashtab_T *ht, stateitem_T *cur_
       if (current_next_list != 0
           ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
           : (cur_si == NULL
-            ? !(kp->flags & HL_CONTAINED)
-            : in_id_list(cur_si, cur_si->si_cont_list,
-                         &kp->k_syn, kp->flags & HL_CONTAINED))) {
+             ? !(kp->flags & HL_CONTAINED)
+             : in_id_list(cur_si, cur_si->si_cont_list,
+                          &kp->k_syn, kp->flags & HL_CONTAINED))) {
         return kp;
       }
     }
diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c
index 8028950c07..77f85b5d2d 100644
--- a/src/nvim/viml/parser/expressions.c
+++ b/src/nvim/viml/parser/expressions.c
@@ -628,8 +628,8 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags)
     GET_CCS(ret, pline);
     ret.data.cmp.inv = (schar == '<');
     ret.data.cmp.type = ((ret.data.cmp.inv ^ haseqsign)
-                           ? kExprCmpGreaterOrEqual
-                           : kExprCmpGreater);
+                         ? kExprCmpGreaterOrEqual
+                         : kExprCmpGreater);
     break;
   }
 
@@ -1963,8 +1963,8 @@ ExprAST viml_pexpr_parse(ParserState *const pstate, const int flags)
                                                 || ((*kv_Z(ast_stack, 1))->type != kExprNodeConcat
                                                     && ((*kv_Z(ast_stack, 1))->type
                                                         != kExprNodeConcatOrSubscript))))
-           ? kELFlagAllowFloat
-           : 0));
+                                           ? kELFlagAllowFloat
+                                           : 0));
     LexExprToken cur_token = viml_pexpr_next_token(pstate,
                                                    want_node_to_lexer_flags[want_node] |
                                                    lexer_additional_flags);
@@ -2031,9 +2031,9 @@ viml_pexpr_parse_process_token:
     const bool node_is_key = (
                               is_concat_or_subscript
                               && (cur_token.type == kExprLexPlainIdentifier
-            ? (!cur_token.data.var.autoload
-               && cur_token.data.var.scope == kExprVarScopeMissing)
-            : (cur_token.type == kExprLexNumber))
+                                  ? (!cur_token.data.var.autoload
+                                     && cur_token.data.var.scope == kExprVarScopeMissing)
+                                  : (cur_token.type == kExprLexNumber))
                               && prev_token.type != kExprLexSpacing);
     if (is_concat_or_subscript && !node_is_key) {
       // Note: in Vim "d. a" (this is the reason behind `prev_token.type !=
@@ -2707,14 +2707,14 @@ viml_pexpr_parse_figure_brace_closing_error:
       break;
     case kExprLexPlainIdentifier: {
       const ExprVarScope scope = (cur_token.type == kExprLexInvalid
-                                    ? kExprVarScopeMissing
-                                    : cur_token.data.var.scope);
+                                  ? kExprVarScopeMissing
+                                  : cur_token.data.var.scope);
       if (want_node == kENodeValue) {
         want_node = kENodeOperator;
         NEW_NODE_WITH_CUR_POS(cur_node,
                               (node_is_key
-                                 ? kExprNodePlainKey
-                                 : kExprNodePlainIdentifier));
+                               ? kExprNodePlainKey
+                               : kExprNodePlainIdentifier));
         cur_node->data.var.scope = scope;
         const size_t scope_shift = (scope == kExprVarScopeMissing ? 0 : 2);
         cur_node->data.var.ident = (pline.data + cur_token.start.col
@@ -2732,8 +2732,8 @@ viml_pexpr_parse_figure_brace_closing_error:
                                                   scope_shift),
                               cur_token.len - scope_shift,
                               (node_is_key
-                                 ? HL(IdentifierKey)
-                                 : HL(IdentifierName)));
+                               ? HL(IdentifierKey)
+                               : HL(IdentifierName)));
       } else {
         if (scope == kExprVarScopeMissing) {
           // uncrustify:off
@@ -2902,15 +2902,15 @@ viml_pexpr_parse_no_paren_closing_error: {}
         // different error numbers: "E114: Missing quote" and
         // "E115: Missing quote".
         ERROR_FROM_TOKEN_AND_MSG(cur_token, (is_double
-                          ? _("E114: Missing double quote: %.*s")
-                          : _("E115: Missing single quote: %.*s")));
+                                             ? _("E114: Missing double quote: %.*s")
+                                             : _("E115: Missing single quote: %.*s")));
       }
       if (want_node == kENodeOperator) {
         OP_MISSING;
       }
       NEW_NODE_WITH_CUR_POS(cur_node, (is_double
-                       ? kExprNodeDoubleQuotedString
-                       : kExprNodeSingleQuotedString));
+                                       ? kExprNodeDoubleQuotedString
+                                       : kExprNodeSingleQuotedString));
       *top_node_p = cur_node;
       parse_quoted_string(pstate, cur_node, cur_token, &ast_stack, is_invalid);
       want_node = kENodeOperator;
-- 
cgit 


From a6f972cb6a5ad47613374570d88df2570ae92b9a Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 7 Nov 2022 19:40:30 +0800
Subject: vim-patch:8.2.2060: check for features implemented with "if"

Problem:    Check for features implemented with "if".
Solution:   Use the Check commands. (Ken Takata, closes vim/vim#7383)

https://github.com/vim/vim/commit/aeb313f355cd67638e3c611354ce401d86f56afe

Cherry-pick test_compiler.vim changes from patch 8.1.2373.
---
 src/nvim/testdir/test_autocmd.vim  |  4 +---
 src/nvim/testdir/test_compiler.vim |  8 +++++---
 src/nvim/testdir/test_delete.vim   | 14 +++++---------
 src/nvim/testdir/test_diffmode.vim |  4 +---
 src/nvim/testdir/test_fold.vim     |  4 +---
 5 files changed, 13 insertions(+), 21 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 1488fe8431..454fb2bdae 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -1975,9 +1975,7 @@ func Test_change_mark_in_autocmds()
 endfunc
 
 func Test_Filter_noshelltemp()
-  if !executable('cat')
-    return
-  endif
+  CheckExecutable cat
 
   enew!
   call setline(1, ['a', 'b', 'c', 'd'])
diff --git a/src/nvim/testdir/test_compiler.vim b/src/nvim/testdir/test_compiler.vim
index 3dc8710d63..ec7d143030 100644
--- a/src/nvim/testdir/test_compiler.vim
+++ b/src/nvim/testdir/test_compiler.vim
@@ -1,9 +1,11 @@
 " Test the :compiler command
 
+source check.vim
+source shared.vim
+
 func Test_compiler()
-  if !executable('perl')
-    return
-  endif
+  CheckExecutable perl
+  CheckFeature quickfix
 
   " $LANG changes the output of Perl.
   if $LANG != ''
diff --git a/src/nvim/testdir/test_delete.vim b/src/nvim/testdir/test_delete.vim
index b23a3bd025..6b49f153c6 100644
--- a/src/nvim/testdir/test_delete.vim
+++ b/src/nvim/testdir/test_delete.vim
@@ -1,5 +1,7 @@
 " Test for delete().
 
+source check.vim
+
 func Test_file_delete()
   split Xfile
   call setline(1, ['a', 'b'])
@@ -41,9 +43,7 @@ func Test_recursive_delete()
 endfunc
 
 func Test_symlink_delete()
-  if !has('unix')
-    return
-  endif
+  CheckUnix
   split Xfile
   call setline(1, ['a', 'b'])
   wq
@@ -56,9 +56,7 @@ func Test_symlink_delete()
 endfunc
 
 func Test_symlink_dir_delete()
-  if !has('unix')
-    return
-  endif
+  CheckUnix
   call mkdir('Xdir1')
   silent !ln -s Xdir1 Xlink
   call assert_true(isdirectory('Xdir1'))
@@ -70,9 +68,7 @@ func Test_symlink_dir_delete()
 endfunc
 
 func Test_symlink_recursive_delete()
-  if !has('unix')
-    return
-  endif
+  CheckUnix
   call mkdir('Xdir3')
   call mkdir('Xdir3/subdir')
   call mkdir('Xdir4')
diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim
index 831efdbfc2..0de5310735 100644
--- a/src/nvim/testdir/test_diffmode.vim
+++ b/src/nvim/testdir/test_diffmode.vim
@@ -621,9 +621,7 @@ func Test_diff_move_to()
 endfunc
 
 func Test_diffexpr()
-  if !executable('diff')
-    return
-  endif
+  CheckExecutable diff
 
   func DiffExpr()
     " Prepend some text to check diff type detection
diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim
index 0a9be310ff..832dd43b65 100644
--- a/src/nvim/testdir/test_fold.vim
+++ b/src/nvim/testdir/test_fold.vim
@@ -95,9 +95,7 @@ func Test_indent_fold2()
 endfunc
 
 func Test_manual_fold_with_filter()
-  if !executable('cat')
-    return
-  endif
+  CheckExecutable cat
   for type in ['manual', 'marker']
     exe 'set foldmethod=' . type
     new
-- 
cgit 


From 609c0513cac7898782c55f5fb20275733cc566e9 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 7 Nov 2022 19:50:51 +0800
Subject: vim-patch:8.2.3626: "au! event" cannot be followed by another command

Problem:    "au!" and "au! event" cannot be followed by another command as
            documented.
Solution:   When a bar is found set nextcmd.

https://github.com/vim/vim/commit/b8e642f7ace5382b4dacb7a8effd86f22b828cc1

Cherry-pick do_autocmd() "eap" argument from patch 8.2.3268.

Co-authored-by: Bram Moolenaar 
---
 src/nvim/autocmd.c                | 4 +++-
 src/nvim/ex_docmd.c               | 2 +-
 src/nvim/testdir/test_autocmd.vim | 9 ++++++---
 3 files changed, 10 insertions(+), 5 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 5b5ea43d86..9845e6be13 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -779,7 +779,7 @@ void au_event_restore(char *old_ei)
 // :autocmd * *.c               show all autocommands for *.c files.
 //
 // Mostly a {group} argument can optionally appear before .
-void do_autocmd(char *arg_in, int forceit)
+void do_autocmd(exarg_T *eap, char *arg_in, int forceit)
 {
   char *arg = arg_in;
   char *envpat = NULL;
@@ -790,6 +790,7 @@ void do_autocmd(char *arg_in, int forceit)
   int group;
 
   if (*arg == '|') {
+    eap->nextcmd = arg + 1;
     arg = "";
     group = AUGROUP_ALL;  // no argument, use all groups
   } else {
@@ -806,6 +807,7 @@ void do_autocmd(char *arg_in, int forceit)
 
   pat = skipwhite(pat);
   if (*pat == '|') {
+    eap->nextcmd = pat + 1;
     pat = "";
     cmd = "";
   } else {
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 5bb7cb2da2..992cd9478d 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -4231,7 +4231,7 @@ static void ex_autocmd(exarg_T *eap)
     secure = 2;
     eap->errmsg = _(e_curdir);
   } else if (eap->cmdidx == CMD_autocmd) {
-    do_autocmd(eap->arg, eap->forceit);
+    do_autocmd(eap, eap->arg, eap->forceit);
   } else {
     do_augroup(eap->arg, eap->forceit);
   }
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 454fb2bdae..1e9406eb87 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -481,17 +481,20 @@ endfunc
 func Test_early_bar()
   " test that a bar is recognized before the {event}
   call s:AddAnAutocmd()
-  augroup vimBarTest | au! | augroup END
+  augroup vimBarTest | au! | let done = 77 | augroup END
   call assert_equal(1, len(split(execute('au vimBarTest'), "\n")))
+  call assert_equal(77, done)
 
   call s:AddAnAutocmd()
-  augroup vimBarTest| au!| augroup END
+  augroup vimBarTest| au!| let done = 88 | augroup END
   call assert_equal(1, len(split(execute('au vimBarTest'), "\n")))
+  call assert_equal(88, done)
 
   " test that a bar is recognized after the {event}
   call s:AddAnAutocmd()
-  augroup vimBarTest| au!BufReadCmd| augroup END
+  augroup vimBarTest| au!BufReadCmd| let done = 99 | augroup END
   call assert_equal(1, len(split(execute('au vimBarTest'), "\n")))
+  call assert_equal(99, done)
 
   " test that a bar is recognized after the {group}
   call s:AddAnAutocmd()
-- 
cgit 


From 3435cdfb94b6f3c72e7f0f16fef9ff2660377cb2 Mon Sep 17 00:00:00 2001
From: Raphael 
Date: Mon, 7 Nov 2022 20:02:00 +0800
Subject: refactor(highlight): rename FloatBorderTitle #20988

requested in https://github.com/neovim/neovim/pull/20184
---
 src/nvim/api/win_config.c  | 4 ++--
 src/nvim/highlight_defs.h  | 2 +-
 src/nvim/highlight_group.c | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index 9f15e5a85b..648048e970 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -139,7 +139,7 @@
 ///       [ {"+", "MyCorner"}, {"x", "MyBorder"} ].
 ///   - title: Title (optional) in window border, String or list.
 ///     List is [text, highlight] tuples. if is string the default
-///     highlight group is `FloatBorderTitle`.
+///     highlight group is `FloatTitle`.
 ///   - title_pos: Title position must set with title option.
 ///     value can be of `left` `center` `right` default is left.
 ///   - noautocmd: If true then no buffer-related autocommand events such as
@@ -364,7 +364,7 @@ static void parse_border_title(Object title, Object title_pos, FloatConfig *fcon
       fconfig->title = false;
       return;
     }
-    int hl_id = syn_check_group(S_LEN("FloatBorderTitle"));
+    int hl_id = syn_check_group(S_LEN("FloatTitle"));
     kv_push(fconfig->title_chunks, ((VirtTextChunk){ .text = xstrdup(title.data.string.data),
                                                      .hl_id = hl_id }));
     fconfig->title_width = (int)mb_string2cells(title.data.string.data);
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index d66bcca57f..2557a248c3 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -179,7 +179,7 @@ EXTERN const char *hlf_names[] INIT(= {
   [HLF_WBR] = "WinBar",
   [HLF_WBRNC] = "WinBarNC",
   [HLF_CU] = "Cursor",
-  [HLF_BTITLE] = "FloatBorderTitle",
+  [HLF_BTITLE] = "FloatTitle",
 });
 
 EXTERN int highlight_attr[HLF_COUNT + 1];     // Highl. attr for each context.
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index a83f4ea969..97debc95a8 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -131,7 +131,7 @@ static const char *highlight_init_both[] = {
   "default link MsgSeparator StatusLine",
   "default link NormalFloat Pmenu",
   "default link FloatBorder WinSeparator",
-  "default link FloatBorderTitle Title",
+  "default link FloatTitle Title",
   "default FloatShadow blend=80 guibg=Black",
   "default FloatShadowThrough blend=100 guibg=Black",
   "RedrawDebugNormal cterm=reverse gui=reverse",
-- 
cgit 


From 894c59ec1f39fde259d5b6959c35549a0281943d Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 7 Nov 2022 21:08:47 +0800
Subject: test(old): make Test_help_tagjump() test order match upstream

---
 src/nvim/testdir/test_help_tagjump.vim | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_help_tagjump.vim b/src/nvim/testdir/test_help_tagjump.vim
index e84726bbfc..f81e4fb1ef 100644
--- a/src/nvim/testdir/test_help_tagjump.vim
+++ b/src/nvim/testdir/test_help_tagjump.vim
@@ -38,18 +38,18 @@ func Test_help_tagjump()
   call assert_true(getline('.') =~ '\*quotestar\*')
   helpclose
 
-  " The test result is different in vim. There ":help ??" will jump to the
-  " falsy operator ??, which hasn't been ported to neovim yet. Instead, neovim
-  " jumps to the tag "g??". This test result needs to be changed if neovim
-  " ports the falsy operator.
-  help ??
+  " help sm?le
+  help ch?ckhealth
   call assert_equal("help", &filetype)
-  call assert_true(getline('.') =~ '\*g??\*')
+  " call assert_true(getline('.') =~ '\*:smile\*')
+  call assert_true(getline('.') =~ '\*:checkhealth\*')
   helpclose
 
-  help ch?ckhealth
+  help ??
   call assert_equal("help", &filetype)
-  call assert_true(getline('.') =~ '\*:checkhealth\*')
+  " *??* tag needs patch 8.2.1794
+  " call assert_true(getline('.') =~ '\*??\*')
+  call assert_true(getline('.') =~ '\*g??\*')
   helpclose
 
   help :?
-- 
cgit 


From c022140ec6a66402e405152054b6ab0141940419 Mon Sep 17 00:00:00 2001
From: Famiu Haque 
Date: Mon, 7 Nov 2022 22:27:37 +0600
Subject: feat(api): add command name to Lua command callback opts

Adds a `name` key to the opts dict passed to Lua command callbacks
created using `nvim_create_user_command()`. This is useful for when
multiple commands use the same callback.

Note that this kind of behavior is not as strange as one might think,
even some internal Neovim commands reuse the same internal C function,
differing their behavior by checking the command name. `substitute`,
`smagic` and `snomagic` are examples of that.

This will also be useful for generalized Lua command preview functions
that can preview a wide range of commands, in which case knowing the
command name is necessary for the preview function to actually be able
to execute the command that it's supposed to preview.
---
 src/nvim/api/command.c  | 1 +
 src/nvim/lua/executor.c | 3 +++
 2 files changed, 4 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c
index 8cd2c0f8b8..752d3868d5 100644
--- a/src/nvim/api/command.c
+++ b/src/nvim/api/command.c
@@ -884,6 +884,7 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin
 /// @param  command Replacement command to execute when this user command is executed. When called
 ///                 from Lua, the command can also be a Lua function. The function is called with a
 ///                 single table argument that contains the following keys:
+///                 - name: (string) Command name
 ///                 - args: (string) The args passed to the command, if any ||
 ///                 - fargs: (table) The args split by unescaped whitespace (when more than one
 ///                 argument is allowed), if any ||
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 9d63fe55f9..9cb42a81d3 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -1985,6 +1985,9 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview)
   nlua_pushref(lstate, preview ? cmd->uc_preview_luaref : cmd->uc_luaref);
 
   lua_newtable(lstate);
+  lua_pushstring(lstate, cmd->uc_name);
+  lua_setfield(lstate, -2, "name");
+
   lua_pushboolean(lstate, eap->forceit == 1);
   lua_setfield(lstate, -2, "bang");
 
-- 
cgit 


From 050b0e30b9d8a073a3b421a6cebd878226249ab6 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Mon, 7 Nov 2022 22:28:28 +0100
Subject: vim-patch:9.0.0843: VHS tape files are not recognized (#20995)

Problem:    VHS tape files are not recognized.
Solution:   Add a filetype pattern. (Carlos Alexandro Becker, closes vim/vim#11452)

https://github.com/vim/vim/commit/1756f4b21837e8596241ecd668f4abbbab4bc7e5

Co-authored-by: Carlos A Becker 
---
 src/nvim/testdir/test_filetype.vim | 1 +
 1 file changed, 1 insertion(+)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index c6f90604e5..69508cb19e 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -612,6 +612,7 @@ let s:filename_checks = {
     \ 'verilogams': ['file.va', 'file.vams'],
     \ 'vgrindefs': ['vgrindefs'],
     \ 'vhdl': ['file.hdl', 'file.vhd', 'file.vhdl', 'file.vbe', 'file.vst', 'file.vhdl_123', 'file.vho', 'some.vhdl_1', 'some.vhdl_1-file'],
+    \ 'vhs': ['file.tape'],
     \ 'vim': ['file.vim', 'file.vba', '.exrc', '_exrc', 'some-vimrc', 'some-vimrc-file', 'vimrc', 'vimrc-file'],
     \ 'viminfo': ['.viminfo', '_viminfo'],
     \ 'vmasm': ['file.mar'],
-- 
cgit 


From 8147d3df284a075f89746f9d5e948b5220c45f0b Mon Sep 17 00:00:00 2001
From: luukvbaal <31730729+luukvbaal@users.noreply.github.com>
Date: Tue, 8 Nov 2022 00:21:22 +0100
Subject: vim-patch:9.0.0844: handling 'statusline' errors is spread out
 (#20992)

Problem:    Handling 'statusline' errors is spread out.
Solution:   Pass the option name to the lower levels so the option can be
            reset there when an error is encountered. (Luuk van Baal,
            closes vim/vim#11467)

https://github.com/vim/vim/commit/7b224fdf4a29f115567d4fc8629c1cef92d8444a
---
 src/nvim/api/vim.c    |  3 ++-
 src/nvim/buffer.c     | 24 ++++---------------
 src/nvim/hardcopy.c   |  4 +---
 src/nvim/screen.c     |  9 -------
 src/nvim/statusline.c | 66 ++++++++++++++++++++++++++-------------------------
 5 files changed, 41 insertions(+), 65 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index beb48b8d7d..d3f8c768a0 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -2223,7 +2223,8 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
                                buf,
                                sizeof(buf),
                                str.data,
-                               false,
+                               NULL,
+                               0,
                                fillchar,
                                maxwidth,
                                hltab_ptr,
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 2c87677925..fa0b2a83c8 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -3203,17 +3203,9 @@ void maketitle(void)
 
     if (*p_titlestring != NUL) {
       if (stl_syntax & STL_IN_TITLE) {
-        int use_sandbox = false;
-        const int called_emsg_before = called_emsg;
-
-        use_sandbox = was_set_insecurely(curwin, "titlestring", 0);
-        build_stl_str_hl(curwin, buf, sizeof(buf),
-                         p_titlestring, use_sandbox,
-                         0, maxlen, NULL, NULL);
+        build_stl_str_hl(curwin, buf, sizeof(buf), p_titlestring,
+                         "titlestring", 0, 0, maxlen, NULL, NULL);
         title_str = buf;
-        if (called_emsg > called_emsg_before) {
-          set_string_option_direct("titlestring", -1, "", OPT_FREE, SID_ERROR);
-        }
       } else {
         title_str = p_titlestring;
       }
@@ -3317,16 +3309,8 @@ void maketitle(void)
     icon_str = buf;
     if (*p_iconstring != NUL) {
       if (stl_syntax & STL_IN_ICON) {
-        int use_sandbox = false;
-        const int called_emsg_before = called_emsg;
-
-        use_sandbox = was_set_insecurely(curwin, "iconstring", 0);
-        build_stl_str_hl(curwin, icon_str, sizeof(buf),
-                         p_iconstring, use_sandbox,
-                         0, 0, NULL, NULL);
-        if (called_emsg > called_emsg_before) {
-          set_string_option_direct("iconstring", -1, "", OPT_FREE, SID_ERROR);
-        }
+        build_stl_str_hl(curwin, icon_str, sizeof(buf), p_iconstring,
+                         "iconstring", 0, 0, 0, NULL, NULL);
       } else {
         icon_str = p_iconstring;
       }
diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c
index 50af6dafe7..6c32e5355f 100644
--- a/src/nvim/hardcopy.c
+++ b/src/nvim/hardcopy.c
@@ -522,7 +522,6 @@ static void prt_header(prt_settings_T *const psettings, const int pagenum, const
 
   if (*p_header != NUL) {
     linenr_T tmp_lnum, tmp_topline, tmp_botline;
-    int use_sandbox = false;
 
     // Need to (temporarily) set current line number and first/last line
     // number on the 'window'.  Since we don't know how long the page is,
@@ -536,9 +535,8 @@ static void prt_header(prt_settings_T *const psettings, const int pagenum, const
     curwin->w_botline = lnum + 63;
     printer_page_num = pagenum;
 
-    use_sandbox = was_set_insecurely(curwin, "printheader", 0);
     build_stl_str_hl(curwin, (char *)tbuf, (size_t)width + IOSIZE,
-                     (char *)p_header, use_sandbox,
+                     (char *)p_header, "printheader", 0,
                      ' ', width, NULL, NULL);
 
     // Reset line numbers
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 377927ba4d..39b3291404 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -999,16 +999,7 @@ void draw_tabline(void)
 
   // Use the 'tabline' option if it's set.
   if (*p_tal != NUL) {
-    int saved_did_emsg = did_emsg;
-
-    // Check for an error.  If there is one we would loop in redrawing the
-    // screen.  Avoid that by making 'tabline' empty.
-    did_emsg = false;
     win_redr_custom(NULL, false, false);
-    if (did_emsg) {
-      set_string_option_direct("tabline", -1, "", OPT_FREE, SID_ERROR);
-    }
-    did_emsg |= saved_did_emsg;
   } else {
     FOR_ALL_TABS(tp) {
       tabcount++;
diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c
index 71f9bde2e9..2c3f7e8375 100644
--- a/src/nvim/statusline.c
+++ b/src/nvim/statusline.c
@@ -214,11 +214,7 @@ void win_redr_ruler(win_T *wp, bool always)
   }
 
   if (*p_ruf && p_ch > 0 && !ui_has(kUIMessages)) {
-    const int called_emsg_before = called_emsg;
     win_redr_custom(wp, false, true);
-    if (called_emsg > called_emsg_before) {
-      set_string_option_direct("rulerformat", -1, "", OPT_FREE, SID_ERROR);
-    }
     return;
   }
 
@@ -389,7 +385,6 @@ int fillchar_status(int *attr, win_T *wp)
 void redraw_custom_statusline(win_T *wp)
 {
   static bool entered = false;
-  int saved_did_emsg = did_emsg;
 
   // When called recursively return.  This can happen when the statusline
   // contains an expression that triggers a redraw.
@@ -398,17 +393,7 @@ void redraw_custom_statusline(win_T *wp)
   }
   entered = true;
 
-  did_emsg = false;
   win_redr_custom(wp, false, false);
-  if (did_emsg) {
-    // When there is an error disable the statusline, otherwise the
-    // display is messed up with errors and a redraw triggers the problem
-    // again and again.
-    set_string_option_direct("statusline", -1, "",
-                             OPT_FREE | (*wp->w_p_stl != NUL
-                                         ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR);
-  }
-  did_emsg |= saved_did_emsg;
   entered = false;
 }
 
@@ -429,9 +414,10 @@ void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
   char buf[MAXPATHL];
   char *stl;
   char *p;
+  char *opt_name;
+  int opt_scope = 0;
   stl_hlrec_t *hltab;
   StlClickRecord *tabtab;
-  int use_sandbox = false;
   win_T *ewp;
   int p_crb_save;
   bool is_stl_global = global_stl_height() > 0;
@@ -454,9 +440,11 @@ void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
     fillchar = ' ';
     attr = HL_ATTR(HLF_TPF);
     maxwidth = Columns;
-    use_sandbox = was_set_insecurely(wp, "tabline", 0);
+    opt_name = "tabline";
   } else if (draw_winbar) {
+    opt_name = "winbar";
     stl = ((*wp->w_p_wbr != NUL) ? wp->w_p_wbr : p_wbr);
+    opt_scope = ((*wp->w_p_wbr != NUL) ? OPT_LOCAL : 0);
     row = -1;  // row zero is first row of text
     col = 0;
     grid = &wp->w_grid;
@@ -469,7 +457,6 @@ void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
     fillchar = wp->w_p_fcs_chars.wbr;
     attr = (wp == curwin) ? win_hl_attr(wp, HLF_WBR) : win_hl_attr(wp, HLF_WBRNC);
     maxwidth = wp->w_width_inner;
-    use_sandbox = was_set_insecurely(wp, "winbar", 0);
     stl_clear_click_defs(wp->w_winbar_click_defs, (long)wp->w_winbar_click_defs_size);
     wp->w_winbar_click_defs = stl_alloc_click_defs(wp->w_winbar_click_defs, maxwidth,
                                                    &wp->w_winbar_click_defs_size);
@@ -483,6 +470,7 @@ void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
 
     if (draw_ruler) {
       stl = p_ruf;
+      opt_name = "rulerformat";
       // advance past any leading group spec - implicit in ru_col
       if (*stl == '%') {
         if (*++stl == '-') {
@@ -509,15 +497,10 @@ void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
         fillchar = ' ';
         attr = HL_ATTR(HLF_MSG);
       }
-
-      use_sandbox = was_set_insecurely(wp, "rulerformat", 0);
     } else {
-      if (*wp->w_p_stl != NUL) {
-        stl = wp->w_p_stl;
-      } else {
-        stl = p_stl;
-      }
-      use_sandbox = was_set_insecurely(wp, "statusline", *wp->w_p_stl == NUL ? 0 : OPT_LOCAL);
+      opt_name = "statusline";
+      stl = ((*wp->w_p_stl != NUL) ? wp->w_p_stl : p_stl);
+      opt_scope = ((*wp->w_p_stl != NUL) ? OPT_LOCAL : 0);
     }
 
     col += is_stl_global ? 0 : wp->w_wincol;
@@ -536,9 +519,8 @@ void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
   // Make a copy, because the statusline may include a function call that
   // might change the option value and free the memory.
   stl = xstrdup(stl);
-  width =
-    build_stl_str_hl(ewp, buf, sizeof(buf), stl, use_sandbox,
-                     fillchar, maxwidth, &hltab, &tabtab);
+  width = build_stl_str_hl(ewp, buf, sizeof(buf), stl, opt_name,
+                           opt_scope, fillchar, maxwidth, &hltab, &tabtab);
   xfree(stl);
   ewp->w_p_crb = p_crb_save;
 
@@ -611,15 +593,16 @@ theend:
 ///             Note: This should not be NameBuff
 /// @param outlen  The length of the output buffer
 /// @param fmt  The statusline format string
-/// @param use_sandbox  Use a sandboxed environment when evaluating fmt
+/// @param opt_name  The option name corresponding to "fmt"
+/// @param opt_scope  The scope corresponding to "opt_name"
 /// @param fillchar  Character to use when filling empty space in the statusline
 /// @param maxwidth  The maximum width to make the statusline
 /// @param hltab  HL attributes (can be NULL)
 /// @param tabtab  Tab clicks definition (can be NULL).
 ///
 /// @return  The final width of the statusline
-int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_sandbox, int fillchar,
-                     int maxwidth, stl_hlrec_t **hltab, StlClickRecord **tabtab)
+int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_name, int opt_scope,
+                     int fillchar, int maxwidth, stl_hlrec_t **hltab, StlClickRecord **tabtab)
 {
   static size_t stl_items_len = 20;  // Initial value, grows as needed.
   static stl_item_t *stl_items = NULL;
@@ -634,6 +617,10 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
   char *usefmt = fmt;
   const int save_must_redraw = must_redraw;
   const int save_redr_type = curwin->w_redr_type;
+  // TODO(Bram): find out why using called_emsg_before makes tests fail, does it
+  // matter?
+  // const int called_emsg_before = called_emsg;
+  const int did_emsg_before = did_emsg;
 
   if (stl_items == NULL) {
     stl_items = xmalloc(sizeof(stl_item_t) * stl_items_len);
@@ -647,6 +634,11 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
     stl_separator_locations = xmalloc(sizeof(int) * stl_items_len);
   }
 
+  // if "fmt" was set insecurely it needs to be evaluated in the sandbox
+  // "opt_name" will be NULL when caller is nvim_eval_statusline()
+  const int use_sandbox = opt_name ? was_set_insecurely(wp, opt_name, opt_scope)
+                                   : false;
+
   // When the format starts with "%!" then evaluate it as an expression and
   // use the result as the actual format string.
   if (fmt[0] == '%' && fmt[1] == '!') {
@@ -1769,5 +1761,15 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
     curwin->w_redr_type = save_redr_type;
   }
 
+  // Check for an error.  If there is one the display will be messed up and
+  // might loop redrawing.  Avoid that by making the corresponding option
+  // empty.
+  // TODO(Bram): find out why using called_emsg_before makes tests fail, does it
+  // matter?
+  // if (called_emsg > called_emsg_before)
+  if (opt_name && did_emsg > did_emsg_before) {
+    set_string_option_direct(opt_name, -1, "", OPT_FREE | opt_scope, SID_ERROR);
+  }
+
   return width;
 }
-- 
cgit 


From d187c00faf4feb76bdc544b87bea71f217d05957 Mon Sep 17 00:00:00 2001
From: luukvbaal <31730729+luukvbaal@users.noreply.github.com>
Date: Tue, 8 Nov 2022 01:41:49 +0100
Subject: refactor: remove stray emsg check after #20992 (#20996)

---
 src/nvim/statusline.c | 12 ------------
 1 file changed, 12 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c
index 2c3f7e8375..039fe5d586 100644
--- a/src/nvim/statusline.c
+++ b/src/nvim/statusline.c
@@ -172,19 +172,7 @@ void win_redr_winbar(win_T *wp)
   if (wp->w_winbar_height == 0 || !redrawing()) {
     // Do nothing.
   } else if (*p_wbr != NUL || *wp->w_p_wbr != NUL) {
-    int saved_did_emsg = did_emsg;
-
-    did_emsg = false;
     win_redr_custom(wp, true, false);
-    if (did_emsg) {
-      // When there is an error disable the winbar, otherwise the
-      // display is messed up with errors and a redraw triggers the problem
-      // again and again.
-      set_string_option_direct("winbar", -1, "",
-                               OPT_FREE | (*wp->w_p_stl != NUL
-                                           ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR);
-    }
-    did_emsg |= saved_did_emsg;
   }
   entered = false;
 }
-- 
cgit 


From f2857dcd5abaf3a5aee9271b79ffa4eee345d21c Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 10 Nov 2022 06:16:54 +0800
Subject: fix(messages): reset msg_grid_scroll_discount when redrawing (#21000)

---
 src/nvim/drawscreen.c | 1 +
 src/nvim/message.c    | 2 +-
 src/nvim/message.h    | 2 ++
 3 files changed, 4 insertions(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index 30abcd1a31..b674f8c235 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -455,6 +455,7 @@ int update_screen(void)
     }
     msg_scrolled = 0;
     msg_scrolled_at_flush = 0;
+    msg_grid_scroll_discount = 0;
     need_wait_return = false;
   }
 
diff --git a/src/nvim/message.c b/src/nvim/message.c
index f274bd7289..314232d4be 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -130,7 +130,6 @@ static bool msg_ext_history_visible = false;
 static bool msg_ext_keep_after_cmdline = false;
 
 static int msg_grid_pos_at_flush = 0;
-static int msg_grid_scroll_discount = 0;
 
 static void ui_ext_msg_set_pos(int row, bool scrolled)
 {
@@ -2445,6 +2444,7 @@ void msg_reset_scroll(void)
   }
   msg_scrolled = 0;
   msg_scrolled_at_flush = 0;
+  msg_grid_scroll_discount = 0;
 }
 
 /// Increment "msg_scrolled".
diff --git a/src/nvim/message.h b/src/nvim/message.h
index 31cd54f18c..191d3b8da7 100644
--- a/src/nvim/message.h
+++ b/src/nvim/message.h
@@ -66,6 +66,8 @@ EXTERN ScreenGrid msg_grid_adj INIT(= SCREEN_GRID_INIT);
 // value of msg_scrolled at latest msg_scroll_flush.
 EXTERN int msg_scrolled_at_flush INIT(= 0);
 
+EXTERN int msg_grid_scroll_discount INIT(= 0);
+
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "message.h.generated.h"
 #endif
-- 
cgit 


From 04c73dbedb8a82ed68ff0b0be32c2bee930fe529 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 10 Nov 2022 13:38:17 +0800
Subject: revert: "oldtests: win: fix buffer pathsep" (#21017)

This reverts commit 40e894f59570a6192aabbe4fe34c456bd00ae871.

No longer needed after #10679
---
 src/nvim/testdir/test_quickfix.vim | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index 42bdb7bad1..5edcf9ce0e 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -3441,9 +3441,9 @@ func Xmultidirstack_tests(cchar)
 
   let l1 = g:Xgetlist({'nr':1, 'items':1})
   let l2 = g:Xgetlist({'nr':2, 'items':1})
-  call assert_equal(expand('Xone/a/one.txt'), bufname(l1.items[1].bufnr))
+  call assert_equal('Xone/a/one.txt', bufname(l1.items[1].bufnr))
   call assert_equal(3, l1.items[1].lnum)
-  call assert_equal(expand('Xtwo/a/two.txt'), bufname(l2.items[1].bufnr))
+  call assert_equal('Xtwo/a/two.txt', bufname(l2.items[1].bufnr))
   call assert_equal(5, l2.items[1].lnum)
 endfunc
 
-- 
cgit 


From 69507c0204cfe284e42865c9c89baec0f351b2c1 Mon Sep 17 00:00:00 2001
From: luukvbaal <31730729+luukvbaal@users.noreply.github.com>
Date: Thu, 10 Nov 2022 12:05:16 +0100
Subject: refactor: move tabline code to statusline.c (#21008)

* refactor: move tabline code to statusline.c

Problem:	Tabline code is closely related to statusline, but still left over in drawscreen.c and screen.c.
Solution:	Move it to statusline.c.

* refactor: add statusline_defs.h
---
 src/nvim/buffer.h          |   1 -
 src/nvim/buffer_defs.h     |   4 +-
 src/nvim/drawscreen.c      |   8 +-
 src/nvim/ex_docmd.c        |   1 +
 src/nvim/grid_defs.h       |  18 --
 src/nvim/main.c            |   1 +
 src/nvim/mouse.c           |   1 +
 src/nvim/screen.c          | 270 ----------------------
 src/nvim/screen.h          |   6 -
 src/nvim/statusline.c      | 564 +++++++++++++++++++++++++++++++++------------
 src/nvim/statusline.h      |   5 +
 src/nvim/statusline_defs.h |  26 +++
 src/nvim/window.c          |   9 +-
 13 files changed, 458 insertions(+), 456 deletions(-)
 create mode 100644 src/nvim/statusline_defs.h

(limited to 'src/nvim')

diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h
index 7380bb045a..84c402d782 100644
--- a/src/nvim/buffer.h
+++ b/src/nvim/buffer.h
@@ -4,7 +4,6 @@
 #include "nvim/eval/typval.h"
 #include "nvim/ex_cmds_defs.h"
 #include "nvim/func_attr.h"
-#include "nvim/grid_defs.h"  // for StlClickRecord
 #include "nvim/macros.h"
 #include "nvim/memline.h"
 #include "nvim/pos.h"  // for linenr_T
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 6448c6b6f6..96cf352067 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -6,8 +6,6 @@
 // for FILE
 #include 
 
-#include "grid_defs.h"
-
 typedef struct file_buffer buf_T;  // Forward declaration
 
 // Reference to a buffer that stores the value of buf_free_count.
@@ -46,6 +44,8 @@ typedef struct {
 #include "nvim/marktree.h"
 // for float window title
 #include "nvim/extmark_defs.h"
+// for click definitions
+#include "nvim/statusline_defs.h"
 
 #define GETFILE_SUCCESS(x)    ((x) <= 0)
 #define MODIFIABLE(buf) (buf->b_p_ma)
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index b674f8c235..fe2c9c3801 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -165,14 +165,10 @@ bool default_grid_alloc(void)
   // size is wrong.
 
   grid_alloc(&default_grid, Rows, Columns, true, true);
-  StlClickDefinition *new_tab_page_click_defs =
-    xcalloc((size_t)Columns, sizeof(*new_tab_page_click_defs));
 
   stl_clear_click_defs(tab_page_click_defs, tab_page_click_defs_size);
-  xfree(tab_page_click_defs);
-
-  tab_page_click_defs = new_tab_page_click_defs;
-  tab_page_click_defs_size = Columns;
+  tab_page_click_defs = stl_alloc_click_defs(tab_page_click_defs, Columns,
+                                             &tab_page_click_defs_size);
 
   default_grid.comp_height = Rows;
   default_grid.comp_width = Columns;
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index e19c5f27d7..31e9401125 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -78,6 +78,7 @@
 #include "nvim/spell.h"
 #include "nvim/spellfile.h"
 #include "nvim/state.h"
+#include "nvim/statusline.h"
 #include "nvim/strings.h"
 #include "nvim/syntax.h"
 #include "nvim/tag.h"
diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h
index 57b3817bc6..db31f7b984 100644
--- a/src/nvim/grid_defs.h
+++ b/src/nvim/grid_defs.h
@@ -110,24 +110,6 @@ struct ScreenGrid {
                            false, 0, 0, NULL, false, true, 0, \
                            0, 0, 0, 0, 0,  false }
 
-/// Status line click definition
-typedef struct {
-  enum {
-    kStlClickDisabled = 0,  ///< Clicks to this area are ignored.
-    kStlClickTabSwitch,     ///< Switch to the given tab.
-    kStlClickTabClose,      ///< Close given tab.
-    kStlClickFuncRun,       ///< Run user function.
-  } type;      ///< Type of the click.
-  int tabnr;   ///< Tab page number.
-  char *func;  ///< Function to run.
-} StlClickDefinition;
-
-/// Used for tabline clicks
-typedef struct {
-  StlClickDefinition def;  ///< Click definition.
-  const char *start;       ///< Location where region starts.
-} StlClickRecord;
-
 typedef struct {
   int args[3];
   int icell;
diff --git a/src/nvim/main.c b/src/nvim/main.c
index fc88739738..e395f8dc78 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -61,6 +61,7 @@
 #include "nvim/shada.h"
 #include "nvim/sign.h"
 #include "nvim/state.h"
+#include "nvim/statusline.h"
 #include "nvim/strings.h"
 #include "nvim/syntax.h"
 #include "nvim/ui.h"
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index ed69197cde..fd1eec692a 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -25,6 +25,7 @@
 #include "nvim/plines.h"
 #include "nvim/search.h"
 #include "nvim/state.h"
+#include "nvim/statusline.h"
 #include "nvim/strings.h"
 #include "nvim/syntax.h"
 #include "nvim/ui.h"
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 39b3291404..2470fd326b 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -582,65 +582,6 @@ void check_for_delay(bool check_msg_scroll)
   }
 }
 
-/// Clear status line, window bar or tab page line click definition table
-///
-/// @param[out]  tpcd  Table to clear.
-/// @param[in]  tpcd_size  Size of the table.
-void stl_clear_click_defs(StlClickDefinition *const click_defs, const long click_defs_size)
-{
-  if (click_defs != NULL) {
-    for (long i = 0; i < click_defs_size; i++) {
-      if (i == 0 || click_defs[i].func != click_defs[i - 1].func) {
-        xfree(click_defs[i].func);
-      }
-    }
-    memset(click_defs, 0, (size_t)click_defs_size * sizeof(click_defs[0]));
-  }
-}
-
-/// Allocate or resize the click definitions array if needed.
-StlClickDefinition *stl_alloc_click_defs(StlClickDefinition *cdp, long width, size_t *size)
-{
-  if (*size < (size_t)width) {
-    xfree(cdp);
-    *size = (size_t)width;
-    cdp = xcalloc(*size, sizeof(StlClickDefinition));
-  }
-  return cdp;
-}
-
-/// Fill the click definitions array if needed.
-void stl_fill_click_defs(StlClickDefinition *click_defs, StlClickRecord *click_recs, char *buf,
-                         int width, bool tabline)
-{
-  if (click_defs == NULL) {
-    return;
-  }
-
-  int col = 0;
-  int len = 0;
-
-  StlClickDefinition cur_click_def = {
-    .type = kStlClickDisabled,
-  };
-  for (int i = 0; click_recs[i].start != NULL; i++) {
-    len += vim_strnsize(buf, (int)(click_recs[i].start - buf));
-    while (col < len) {
-      click_defs[col++] = cur_click_def;
-    }
-    buf = (char *)click_recs[i].start;
-    cur_click_def = click_recs[i].def;
-    if (!tabline && !(cur_click_def.type == kStlClickDisabled
-                      || cur_click_def.type == kStlClickFuncRun)) {
-      // window bar and status line only support click functions
-      cur_click_def.type = kStlClickDisabled;
-    }
-  }
-  while (col < width) {
-    click_defs[col++] = cur_click_def;
-  }
-}
-
 /// Set cursor to its position in the current window.
 void setcursor(void)
 {
@@ -959,217 +900,6 @@ static void recording_mode(int attr)
   }
 }
 
-/// Draw the tab pages line at the top of the Vim window.
-void draw_tabline(void)
-{
-  int tabcount = 0;
-  int tabwidth = 0;
-  int col = 0;
-  int scol = 0;
-  int attr;
-  win_T *wp;
-  win_T *cwp;
-  int wincount;
-  int modified;
-  int c;
-  int len;
-  int attr_nosel = HL_ATTR(HLF_TP);
-  int attr_fill = HL_ATTR(HLF_TPF);
-  char_u *p;
-  int room;
-  int use_sep_chars = (t_colors < 8);
-
-  if (default_grid.chars == NULL) {
-    return;
-  }
-  redraw_tabline = false;
-
-  if (ui_has(kUITabline)) {
-    ui_ext_tabline_update();
-    return;
-  }
-
-  if (tabline_height() < 1) {
-    return;
-  }
-
-  // Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect.
-  assert(Columns == tab_page_click_defs_size);
-  stl_clear_click_defs(tab_page_click_defs, tab_page_click_defs_size);
-
-  // Use the 'tabline' option if it's set.
-  if (*p_tal != NUL) {
-    win_redr_custom(NULL, false, false);
-  } else {
-    FOR_ALL_TABS(tp) {
-      tabcount++;
-    }
-
-    if (tabcount > 0) {
-      tabwidth = (Columns - 1 + tabcount / 2) / tabcount;
-    }
-
-    if (tabwidth < 6) {
-      tabwidth = 6;
-    }
-
-    attr = attr_nosel;
-    tabcount = 0;
-
-    FOR_ALL_TABS(tp) {
-      if (col >= Columns - 4) {
-        break;
-      }
-
-      scol = col;
-
-      if (tp == curtab) {
-        cwp = curwin;
-        wp = firstwin;
-      } else {
-        cwp = tp->tp_curwin;
-        wp = tp->tp_firstwin;
-      }
-
-      if (tp->tp_topframe == topframe) {
-        attr = win_hl_attr(cwp, HLF_TPS);
-      }
-      if (use_sep_chars && col > 0) {
-        grid_putchar(&default_grid, '|', 0, col++, attr);
-      }
-
-      if (tp->tp_topframe != topframe) {
-        attr = win_hl_attr(cwp, HLF_TP);
-      }
-
-      grid_putchar(&default_grid, ' ', 0, col++, attr);
-
-      modified = false;
-
-      for (wincount = 0; wp != NULL; wp = wp->w_next, wincount++) {
-        if (bufIsChanged(wp->w_buffer)) {
-          modified = true;
-        }
-      }
-
-      if (modified || wincount > 1) {
-        if (wincount > 1) {
-          vim_snprintf((char *)NameBuff, MAXPATHL, "%d", wincount);
-          len = (int)strlen(NameBuff);
-          if (col + len >= Columns - 3) {
-            break;
-          }
-          grid_puts_len(&default_grid, NameBuff, len, 0, col,
-                        hl_combine_attr(attr, win_hl_attr(cwp, HLF_T)));
-          col += len;
-        }
-        if (modified) {
-          grid_puts_len(&default_grid, "+", 1, 0, col++, attr);
-        }
-        grid_putchar(&default_grid, ' ', 0, col++, attr);
-      }
-
-      room = scol - col + tabwidth - 1;
-      if (room > 0) {
-        // Get buffer name in NameBuff[]
-        get_trans_bufname(cwp->w_buffer);
-        shorten_dir(NameBuff);
-        len = vim_strsize((char *)NameBuff);
-        p = (char_u *)NameBuff;
-        while (len > room) {
-          len -= ptr2cells((char *)p);
-          MB_PTR_ADV(p);
-        }
-        if (len > Columns - col - 1) {
-          len = Columns - col - 1;
-        }
-
-        grid_puts_len(&default_grid, (char *)p, (int)STRLEN(p), 0, col, attr);
-        col += len;
-      }
-      grid_putchar(&default_grid, ' ', 0, col++, attr);
-
-      // Store the tab page number in tab_page_click_defs[], so that
-      // jump_to_mouse() knows where each one is.
-      tabcount++;
-      while (scol < col) {
-        tab_page_click_defs[scol++] = (StlClickDefinition) {
-          .type = kStlClickTabSwitch,
-          .tabnr = tabcount,
-          .func = NULL,
-        };
-      }
-    }
-
-    if (use_sep_chars) {
-      c = '_';
-    } else {
-      c = ' ';
-    }
-    grid_fill(&default_grid, 0, 1, col, Columns, c, c, attr_fill);
-
-    // Put an "X" for closing the current tab if there are several.
-    if (first_tabpage->tp_next != NULL) {
-      grid_putchar(&default_grid, 'X', 0, Columns - 1, attr_nosel);
-      tab_page_click_defs[Columns - 1] = (StlClickDefinition) {
-        .type = kStlClickTabClose,
-        .tabnr = 999,
-        .func = NULL,
-      };
-    }
-  }
-
-  // Reset the flag here again, in case evaluating 'tabline' causes it to be
-  // set.
-  redraw_tabline = false;
-}
-
-static void ui_ext_tabline_update(void)
-{
-  Arena arena = ARENA_EMPTY;
-
-  size_t n_tabs = 0;
-  FOR_ALL_TABS(tp) {
-    n_tabs++;
-  }
-
-  Array tabs = arena_array(&arena, n_tabs);
-  FOR_ALL_TABS(tp) {
-    Dictionary tab_info = arena_dict(&arena, 2);
-    PUT_C(tab_info, "tab", TABPAGE_OBJ(tp->handle));
-
-    win_T *cwp = (tp == curtab) ? curwin : tp->tp_curwin;
-    get_trans_bufname(cwp->w_buffer);
-    PUT_C(tab_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff))));
-
-    ADD_C(tabs, DICTIONARY_OBJ(tab_info));
-  }
-
-  size_t n_buffers = 0;
-  FOR_ALL_BUFFERS(buf) {
-    n_buffers += buf->b_p_bl ? 1 : 0;
-  }
-
-  Array buffers = arena_array(&arena, n_buffers);
-  FOR_ALL_BUFFERS(buf) {
-    // Do not include unlisted buffers
-    if (!buf->b_p_bl) {
-      continue;
-    }
-
-    Dictionary buffer_info = arena_dict(&arena, 2);
-    PUT_C(buffer_info, "buffer", BUFFER_OBJ(buf->handle));
-
-    get_trans_bufname(buf);
-    PUT_C(buffer_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff))));
-
-    ADD_C(buffers, DICTIONARY_OBJ(buffer_info));
-  }
-
-  ui_call_tabline_update(curtab->handle, tabs, curbuf->handle, buffers);
-  arena_mem_free(arena_finish(&arena));
-}
-
 void get_trans_bufname(buf_T *buf)
 {
   if (buf_spname(buf) != NULL) {
diff --git a/src/nvim/screen.h b/src/nvim/screen.h
index ea1c58cd80..5cee708bd1 100644
--- a/src/nvim/screen.h
+++ b/src/nvim/screen.h
@@ -9,12 +9,6 @@
 
 EXTERN match_T screen_search_hl;       // used for 'hlsearch' highlight matching
 
-/// Array defining what should be done when tabline is clicked
-EXTERN StlClickDefinition *tab_page_click_defs INIT(= NULL);
-
-/// Size of the tab_page_click_defs array
-EXTERN long tab_page_click_defs_size INIT(= 0);
-
 #define W_ENDCOL(wp)   ((wp)->w_wincol + (wp)->w_width)
 #define W_ENDROW(wp)   ((wp)->w_winrow + (wp)->w_height)
 
diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c
index 039fe5d586..93334a1d75 100644
--- a/src/nvim/statusline.c
+++ b/src/nvim/statusline.c
@@ -158,6 +158,243 @@ void win_redr_status(win_T *wp)
   busy = false;
 }
 
+/// Clear status line, window bar or tab page line click definition table
+///
+/// @param[out]  tpcd  Table to clear.
+/// @param[in]  tpcd_size  Size of the table.
+void stl_clear_click_defs(StlClickDefinition *const click_defs, const size_t click_defs_size)
+{
+  if (click_defs != NULL) {
+    for (size_t i = 0; i < click_defs_size; i++) {
+      if (i == 0 || click_defs[i].func != click_defs[i - 1].func) {
+        xfree(click_defs[i].func);
+      }
+    }
+    memset(click_defs, 0, click_defs_size * sizeof(click_defs[0]));
+  }
+}
+
+/// Allocate or resize the click definitions array if needed.
+StlClickDefinition *stl_alloc_click_defs(StlClickDefinition *cdp, long width, size_t *size)
+{
+  if (*size < (size_t)width) {
+    xfree(cdp);
+    *size = (size_t)width;
+    cdp = xcalloc(*size, sizeof(StlClickDefinition));
+  }
+  return cdp;
+}
+
+/// Fill the click definitions array if needed.
+void stl_fill_click_defs(StlClickDefinition *click_defs, StlClickRecord *click_recs, char *buf,
+                         int width, bool tabline)
+{
+  if (click_defs == NULL) {
+    return;
+  }
+
+  int col = 0;
+  int len = 0;
+
+  StlClickDefinition cur_click_def = {
+    .type = kStlClickDisabled,
+  };
+  for (int i = 0; click_recs[i].start != NULL; i++) {
+    len += vim_strnsize(buf, (int)(click_recs[i].start - buf));
+    while (col < len) {
+      click_defs[col++] = cur_click_def;
+    }
+    buf = (char *)click_recs[i].start;
+    cur_click_def = click_recs[i].def;
+    if (!tabline && !(cur_click_def.type == kStlClickDisabled
+                      || cur_click_def.type == kStlClickFuncRun)) {
+      // window bar and status line only support click functions
+      cur_click_def.type = kStlClickDisabled;
+    }
+  }
+  while (col < width) {
+    click_defs[col++] = cur_click_def;
+  }
+}
+
+/// Redraw the status line, window bar or ruler of window "wp".
+/// When "wp" is NULL redraw the tab pages line from 'tabline'.
+static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
+{
+  static bool entered = false;
+  int attr;
+  int curattr;
+  int row;
+  int col = 0;
+  int maxwidth;
+  int width;
+  int n;
+  int len;
+  int fillchar;
+  char buf[MAXPATHL];
+  char *stl;
+  char *p;
+  char *opt_name;
+  int opt_scope = 0;
+  stl_hlrec_t *hltab;
+  StlClickRecord *tabtab;
+  win_T *ewp;
+  int p_crb_save;
+  bool is_stl_global = global_stl_height() > 0;
+
+  ScreenGrid *grid = &default_grid;
+
+  // There is a tiny chance that this gets called recursively: When
+  // redrawing a status line triggers redrawing the ruler or tabline.
+  // Avoid trouble by not allowing recursion.
+  if (entered) {
+    return;
+  }
+  entered = true;
+
+  // setup environment for the task at hand
+  if (wp == NULL) {
+    // Use 'tabline'.  Always at the first line of the screen.
+    stl = p_tal;
+    row = 0;
+    fillchar = ' ';
+    attr = HL_ATTR(HLF_TPF);
+    maxwidth = Columns;
+    opt_name = "tabline";
+  } else if (draw_winbar) {
+    opt_name = "winbar";
+    stl = ((*wp->w_p_wbr != NUL) ? wp->w_p_wbr : p_wbr);
+    opt_scope = ((*wp->w_p_wbr != NUL) ? OPT_LOCAL : 0);
+    row = -1;  // row zero is first row of text
+    col = 0;
+    grid = &wp->w_grid;
+    grid_adjust(&grid, &row, &col);
+
+    if (row < 0) {
+      return;
+    }
+
+    fillchar = wp->w_p_fcs_chars.wbr;
+    attr = (wp == curwin) ? win_hl_attr(wp, HLF_WBR) : win_hl_attr(wp, HLF_WBRNC);
+    maxwidth = wp->w_width_inner;
+    stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size);
+    wp->w_winbar_click_defs = stl_alloc_click_defs(wp->w_winbar_click_defs, maxwidth,
+                                                   &wp->w_winbar_click_defs_size);
+  } else {
+    row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp);
+    fillchar = fillchar_status(&attr, wp);
+    maxwidth = is_stl_global ? Columns : wp->w_width;
+    stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size);
+    wp->w_status_click_defs = stl_alloc_click_defs(wp->w_status_click_defs, maxwidth,
+                                                   &wp->w_status_click_defs_size);
+
+    if (draw_ruler) {
+      stl = p_ruf;
+      opt_name = "rulerformat";
+      // advance past any leading group spec - implicit in ru_col
+      if (*stl == '%') {
+        if (*++stl == '-') {
+          stl++;
+        }
+        if (atoi(stl)) {
+          while (ascii_isdigit(*stl)) {
+            stl++;
+          }
+        }
+        if (*stl++ != '(') {
+          stl = p_ruf;
+        }
+      }
+      col = ru_col - (Columns - maxwidth);
+      if (col < (maxwidth + 1) / 2) {
+        col = (maxwidth + 1) / 2;
+      }
+      maxwidth = maxwidth - col;
+      if (!wp->w_status_height && !is_stl_global) {
+        grid = &msg_grid_adj;
+        row = Rows - 1;
+        maxwidth--;  // writing in last column may cause scrolling
+        fillchar = ' ';
+        attr = HL_ATTR(HLF_MSG);
+      }
+    } else {
+      opt_name = "statusline";
+      stl = ((*wp->w_p_stl != NUL) ? wp->w_p_stl : p_stl);
+      opt_scope = ((*wp->w_p_stl != NUL) ? OPT_LOCAL : 0);
+    }
+
+    col += is_stl_global ? 0 : wp->w_wincol;
+  }
+
+  if (maxwidth <= 0) {
+    goto theend;
+  }
+
+  // Temporarily reset 'cursorbind', we don't want a side effect from moving
+  // the cursor away and back.
+  ewp = wp == NULL ? curwin : wp;
+  p_crb_save = ewp->w_p_crb;
+  ewp->w_p_crb = false;
+
+  // Make a copy, because the statusline may include a function call that
+  // might change the option value and free the memory.
+  stl = xstrdup(stl);
+  width = build_stl_str_hl(ewp, buf, sizeof(buf), stl, opt_name,
+                           opt_scope, fillchar, maxwidth, &hltab, &tabtab);
+  xfree(stl);
+  ewp->w_p_crb = p_crb_save;
+
+  // Make all characters printable.
+  p = transstr(buf, true);
+  len = (int)STRLCPY(buf, p, sizeof(buf));
+  len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1;
+  xfree(p);
+
+  // fill up with "fillchar"
+  while (width < maxwidth && len < (int)sizeof(buf) - 1) {
+    len += utf_char2bytes(fillchar, buf + len);
+    width++;
+  }
+  buf[len] = NUL;
+
+  // Draw each snippet with the specified highlighting.
+  grid_puts_line_start(grid, row);
+
+  curattr = attr;
+  p = buf;
+  for (n = 0; hltab[n].start != NULL; n++) {
+    int textlen = (int)(hltab[n].start - p);
+    grid_puts_len(grid, p, textlen, row, col, curattr);
+    col += vim_strnsize(p, textlen);
+    p = hltab[n].start;
+
+    if (hltab[n].userhl == 0) {
+      curattr = attr;
+    } else if (hltab[n].userhl < 0) {
+      curattr = syn_id2attr(-hltab[n].userhl);
+    } else if (wp != NULL && wp != curwin && wp->w_status_height != 0) {
+      curattr = highlight_stlnc[hltab[n].userhl - 1];
+    } else {
+      curattr = highlight_user[hltab[n].userhl - 1];
+    }
+  }
+  // Make sure to use an empty string instead of p, if p is beyond buf + len.
+  grid_puts(grid, p >= buf + len ? "" : p, row, col, curattr);
+
+  grid_puts_line_flush(false);
+
+  // Fill the tab_page_click_defs, w_status_click_defs or w_winbar_click_defs array for clicking
+  // in the tab page line, status line or window bar
+  StlClickDefinition *click_defs = (wp == NULL) ? tab_page_click_defs
+                                                : draw_winbar ? wp->w_winbar_click_defs
+                                                              : wp->w_status_click_defs;
+
+  stl_fill_click_defs(click_defs, tabtab, buf, maxwidth, wp == NULL);
+
+theend:
+  entered = false;
+}
+
 void win_redr_winbar(win_T *wp)
 {
   static bool entered = false;
@@ -385,182 +622,211 @@ void redraw_custom_statusline(win_T *wp)
   entered = false;
 }
 
-/// Redraw the status line, window bar or ruler of window "wp".
-/// When "wp" is NULL redraw the tab pages line from 'tabline'.
-void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
+static void ui_ext_tabline_update(void)
 {
-  static bool entered = false;
-  int attr;
-  int curattr;
-  int row;
+  Arena arena = ARENA_EMPTY;
+
+  size_t n_tabs = 0;
+  FOR_ALL_TABS(tp) {
+    n_tabs++;
+  }
+
+  Array tabs = arena_array(&arena, n_tabs);
+  FOR_ALL_TABS(tp) {
+    Dictionary tab_info = arena_dict(&arena, 2);
+    PUT_C(tab_info, "tab", TABPAGE_OBJ(tp->handle));
+
+    win_T *cwp = (tp == curtab) ? curwin : tp->tp_curwin;
+    get_trans_bufname(cwp->w_buffer);
+    PUT_C(tab_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff))));
+
+    ADD_C(tabs, DICTIONARY_OBJ(tab_info));
+  }
+
+  size_t n_buffers = 0;
+  FOR_ALL_BUFFERS(buf) {
+    n_buffers += buf->b_p_bl ? 1 : 0;
+  }
+
+  Array buffers = arena_array(&arena, n_buffers);
+  FOR_ALL_BUFFERS(buf) {
+    // Do not include unlisted buffers
+    if (!buf->b_p_bl) {
+      continue;
+    }
+
+    Dictionary buffer_info = arena_dict(&arena, 2);
+    PUT_C(buffer_info, "buffer", BUFFER_OBJ(buf->handle));
+
+    get_trans_bufname(buf);
+    PUT_C(buffer_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff))));
+
+    ADD_C(buffers, DICTIONARY_OBJ(buffer_info));
+  }
+
+  ui_call_tabline_update(curtab->handle, tabs, curbuf->handle, buffers);
+  arena_mem_free(arena_finish(&arena));
+}
+
+/// Draw the tab pages line at the top of the Vim window.
+void draw_tabline(void)
+{
+  int tabcount = 0;
+  int tabwidth = 0;
   int col = 0;
-  int maxwidth;
-  int width;
-  int n;
+  int scol = 0;
+  int attr;
+  win_T *wp;
+  win_T *cwp;
+  int wincount;
+  int modified;
+  int c;
   int len;
-  int fillchar;
-  char buf[MAXPATHL];
-  char *stl;
-  char *p;
-  char *opt_name;
-  int opt_scope = 0;
-  stl_hlrec_t *hltab;
-  StlClickRecord *tabtab;
-  win_T *ewp;
-  int p_crb_save;
-  bool is_stl_global = global_stl_height() > 0;
+  int attr_nosel = HL_ATTR(HLF_TP);
+  int attr_fill = HL_ATTR(HLF_TPF);
+  char_u *p;
+  int room;
+  int use_sep_chars = (t_colors < 8);
 
-  ScreenGrid *grid = &default_grid;
+  if (default_grid.chars == NULL) {
+    return;
+  }
+  redraw_tabline = false;
 
-  // There is a tiny chance that this gets called recursively: When
-  // redrawing a status line triggers redrawing the ruler or tabline.
-  // Avoid trouble by not allowing recursion.
-  if (entered) {
+  if (ui_has(kUITabline)) {
+    ui_ext_tabline_update();
     return;
   }
-  entered = true;
 
-  // setup environment for the task at hand
-  if (wp == NULL) {
-    // Use 'tabline'.  Always at the first line of the screen.
-    stl = p_tal;
-    row = 0;
-    fillchar = ' ';
-    attr = HL_ATTR(HLF_TPF);
-    maxwidth = Columns;
-    opt_name = "tabline";
-  } else if (draw_winbar) {
-    opt_name = "winbar";
-    stl = ((*wp->w_p_wbr != NUL) ? wp->w_p_wbr : p_wbr);
-    opt_scope = ((*wp->w_p_wbr != NUL) ? OPT_LOCAL : 0);
-    row = -1;  // row zero is first row of text
-    col = 0;
-    grid = &wp->w_grid;
-    grid_adjust(&grid, &row, &col);
+  if (tabline_height() < 1) {
+    return;
+  }
 
-    if (row < 0) {
-      return;
+  // Use the 'tabline' option if it's set.
+  if (*p_tal != NUL) {
+    win_redr_custom(NULL, false, false);
+  } else {
+    FOR_ALL_TABS(tp) {
+      tabcount++;
     }
 
-    fillchar = wp->w_p_fcs_chars.wbr;
-    attr = (wp == curwin) ? win_hl_attr(wp, HLF_WBR) : win_hl_attr(wp, HLF_WBRNC);
-    maxwidth = wp->w_width_inner;
-    stl_clear_click_defs(wp->w_winbar_click_defs, (long)wp->w_winbar_click_defs_size);
-    wp->w_winbar_click_defs = stl_alloc_click_defs(wp->w_winbar_click_defs, maxwidth,
-                                                   &wp->w_winbar_click_defs_size);
-  } else {
-    row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp);
-    fillchar = fillchar_status(&attr, wp);
-    maxwidth = is_stl_global ? Columns : wp->w_width;
-    stl_clear_click_defs(wp->w_status_click_defs, (long)wp->w_status_click_defs_size);
-    wp->w_status_click_defs = stl_alloc_click_defs(wp->w_status_click_defs, maxwidth,
-                                                   &wp->w_status_click_defs_size);
+    if (tabcount > 0) {
+      tabwidth = (Columns - 1 + tabcount / 2) / tabcount;
+    }
 
-    if (draw_ruler) {
-      stl = p_ruf;
-      opt_name = "rulerformat";
-      // advance past any leading group spec - implicit in ru_col
-      if (*stl == '%') {
-        if (*++stl == '-') {
-          stl++;
-        }
-        if (atoi(stl)) {
-          while (ascii_isdigit(*stl)) {
-            stl++;
-          }
-        }
-        if (*stl++ != '(') {
-          stl = p_ruf;
-        }
-      }
-      col = ru_col - (Columns - maxwidth);
-      if (col < (maxwidth + 1) / 2) {
-        col = (maxwidth + 1) / 2;
+    if (tabwidth < 6) {
+      tabwidth = 6;
+    }
+
+    attr = attr_nosel;
+    tabcount = 0;
+
+    FOR_ALL_TABS(tp) {
+      if (col >= Columns - 4) {
+        break;
       }
-      maxwidth = maxwidth - col;
-      if (!wp->w_status_height && !is_stl_global) {
-        grid = &msg_grid_adj;
-        row = Rows - 1;
-        maxwidth--;  // writing in last column may cause scrolling
-        fillchar = ' ';
-        attr = HL_ATTR(HLF_MSG);
+
+      scol = col;
+
+      if (tp == curtab) {
+        cwp = curwin;
+        wp = firstwin;
+      } else {
+        cwp = tp->tp_curwin;
+        wp = tp->tp_firstwin;
       }
-    } else {
-      opt_name = "statusline";
-      stl = ((*wp->w_p_stl != NUL) ? wp->w_p_stl : p_stl);
-      opt_scope = ((*wp->w_p_stl != NUL) ? OPT_LOCAL : 0);
-    }
 
-    col += is_stl_global ? 0 : wp->w_wincol;
-  }
+      if (tp->tp_topframe == topframe) {
+        attr = win_hl_attr(cwp, HLF_TPS);
+      }
+      if (use_sep_chars && col > 0) {
+        grid_putchar(&default_grid, '|', 0, col++, attr);
+      }
 
-  if (maxwidth <= 0) {
-    goto theend;
-  }
+      if (tp->tp_topframe != topframe) {
+        attr = win_hl_attr(cwp, HLF_TP);
+      }
 
-  // Temporarily reset 'cursorbind', we don't want a side effect from moving
-  // the cursor away and back.
-  ewp = wp == NULL ? curwin : wp;
-  p_crb_save = ewp->w_p_crb;
-  ewp->w_p_crb = false;
+      grid_putchar(&default_grid, ' ', 0, col++, attr);
 
-  // Make a copy, because the statusline may include a function call that
-  // might change the option value and free the memory.
-  stl = xstrdup(stl);
-  width = build_stl_str_hl(ewp, buf, sizeof(buf), stl, opt_name,
-                           opt_scope, fillchar, maxwidth, &hltab, &tabtab);
-  xfree(stl);
-  ewp->w_p_crb = p_crb_save;
+      modified = false;
 
-  // Make all characters printable.
-  p = transstr(buf, true);
-  len = (int)STRLCPY(buf, p, sizeof(buf));
-  len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1;
-  xfree(p);
+      for (wincount = 0; wp != NULL; wp = wp->w_next, wincount++) {
+        if (bufIsChanged(wp->w_buffer)) {
+          modified = true;
+        }
+      }
 
-  // fill up with "fillchar"
-  while (width < maxwidth && len < (int)sizeof(buf) - 1) {
-    len += utf_char2bytes(fillchar, buf + len);
-    width++;
-  }
-  buf[len] = NUL;
+      if (modified || wincount > 1) {
+        if (wincount > 1) {
+          vim_snprintf((char *)NameBuff, MAXPATHL, "%d", wincount);
+          len = (int)strlen(NameBuff);
+          if (col + len >= Columns - 3) {
+            break;
+          }
+          grid_puts_len(&default_grid, NameBuff, len, 0, col,
+                        hl_combine_attr(attr, win_hl_attr(cwp, HLF_T)));
+          col += len;
+        }
+        if (modified) {
+          grid_puts_len(&default_grid, "+", 1, 0, col++, attr);
+        }
+        grid_putchar(&default_grid, ' ', 0, col++, attr);
+      }
 
-  // Draw each snippet with the specified highlighting.
-  grid_puts_line_start(grid, row);
+      room = scol - col + tabwidth - 1;
+      if (room > 0) {
+        // Get buffer name in NameBuff[]
+        get_trans_bufname(cwp->w_buffer);
+        shorten_dir(NameBuff);
+        len = vim_strsize((char *)NameBuff);
+        p = (char_u *)NameBuff;
+        while (len > room) {
+          len -= ptr2cells((char *)p);
+          MB_PTR_ADV(p);
+        }
+        if (len > Columns - col - 1) {
+          len = Columns - col - 1;
+        }
 
-  curattr = attr;
-  p = buf;
-  for (n = 0; hltab[n].start != NULL; n++) {
-    int textlen = (int)(hltab[n].start - p);
-    grid_puts_len(grid, p, textlen, row, col, curattr);
-    col += vim_strnsize(p, textlen);
-    p = hltab[n].start;
+        grid_puts_len(&default_grid, (char *)p, (int)STRLEN(p), 0, col, attr);
+        col += len;
+      }
+      grid_putchar(&default_grid, ' ', 0, col++, attr);
+
+      // Store the tab page number in tab_page_click_defs[], so that
+      // jump_to_mouse() knows where each one is.
+      tabcount++;
+      while (scol < col) {
+        tab_page_click_defs[scol++] = (StlClickDefinition) {
+          .type = kStlClickTabSwitch,
+          .tabnr = tabcount,
+          .func = NULL,
+        };
+      }
+    }
 
-    if (hltab[n].userhl == 0) {
-      curattr = attr;
-    } else if (hltab[n].userhl < 0) {
-      curattr = syn_id2attr(-hltab[n].userhl);
-    } else if (wp != NULL && wp != curwin && wp->w_status_height != 0) {
-      curattr = highlight_stlnc[hltab[n].userhl - 1];
+    if (use_sep_chars) {
+      c = '_';
     } else {
-      curattr = highlight_user[hltab[n].userhl - 1];
+      c = ' ';
+    }
+    grid_fill(&default_grid, 0, 1, col, Columns, c, c, attr_fill);
+
+    // Put an "X" for closing the current tab if there are several.
+    if (first_tabpage->tp_next != NULL) {
+      grid_putchar(&default_grid, 'X', 0, Columns - 1, attr_nosel);
+      tab_page_click_defs[Columns - 1] = (StlClickDefinition) {
+        .type = kStlClickTabClose,
+        .tabnr = 999,
+        .func = NULL,
+      };
     }
   }
-  // Make sure to use an empty string instead of p, if p is beyond buf + len.
-  grid_puts(grid, p >= buf + len ? "" : p, row, col, curattr);
 
-  grid_puts_line_flush(false);
-
-  // Fill the tab_page_click_defs, w_status_click_defs or w_winbar_click_defs array for clicking
-  // in the tab page line, status line or window bar
-  StlClickDefinition *click_defs = (wp == NULL) ? tab_page_click_defs
-                                                : draw_winbar ? wp->w_winbar_click_defs
-                                                              : wp->w_status_click_defs;
-
-  stl_fill_click_defs(click_defs, tabtab, buf, maxwidth, wp == NULL);
-
-theend:
-  entered = false;
+  // Reset the flag here again, in case evaluating 'tabline' causes it to be
+  // set.
+  redraw_tabline = false;
 }
 
 /// Build a string from the status line items in "fmt".
diff --git a/src/nvim/statusline.h b/src/nvim/statusline.h
index 357a9a821f..dc25dd5e67 100644
--- a/src/nvim/statusline.h
+++ b/src/nvim/statusline.h
@@ -3,6 +3,11 @@
 
 #include "nvim/buffer_defs.h"
 
+/// Array defining what should be done when tabline is clicked
+EXTERN StlClickDefinition *tab_page_click_defs INIT(= NULL);
+/// Size of the tab_page_click_defs array
+EXTERN size_t tab_page_click_defs_size INIT(= 0);
+
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "statusline.h.generated.h"
 #endif
diff --git a/src/nvim/statusline_defs.h b/src/nvim/statusline_defs.h
new file mode 100644
index 0000000000..eac9dfd690
--- /dev/null
+++ b/src/nvim/statusline_defs.h
@@ -0,0 +1,26 @@
+#ifndef NVIM_STATUSLINE_DEFS_H
+#define NVIM_STATUSLINE_DEFS_H
+
+#include 
+
+#include "nvim/macros.h"
+
+/// Status line click definition
+typedef struct {
+  enum {
+    kStlClickDisabled = 0,  ///< Clicks to this area are ignored.
+    kStlClickTabSwitch,     ///< Switch to the given tab.
+    kStlClickTabClose,      ///< Close given tab.
+    kStlClickFuncRun,       ///< Run user function.
+  } type;      ///< Type of the click.
+  int tabnr;   ///< Tab page number.
+  char *func;  ///< Function to run.
+} StlClickDefinition;
+
+/// Used for tabline clicks
+typedef struct {
+  StlClickDefinition def;  ///< Click definition.
+  const char *start;       ///< Location where region starts.
+} StlClickRecord;
+
+#endif  // NVIM_STATUSLINE_DEFS_H
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 3649e6bc16..de5bcb40ea 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -52,6 +52,7 @@
 #include "nvim/regexp.h"
 #include "nvim/search.h"
 #include "nvim/state.h"
+#include "nvim/statusline.h"
 #include "nvim/strings.h"
 #include "nvim/syntax.h"
 #include "nvim/terminal.h"
@@ -5027,10 +5028,10 @@ static void win_free(win_T *wp, tabpage_T *tp)
   xfree(wp->w_localdir);
   xfree(wp->w_prevdir);
 
-  stl_clear_click_defs(wp->w_status_click_defs, (long)wp->w_status_click_defs_size);
+  stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size);
   xfree(wp->w_status_click_defs);
 
-  stl_clear_click_defs(wp->w_winbar_click_defs, (long)wp->w_winbar_click_defs_size);
+  stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size);
   xfree(wp->w_winbar_click_defs);
 
   // Remove the window from the b_wininfo lists, it may happen that the
@@ -6584,7 +6585,7 @@ static void win_remove_status_line(win_T *wp, bool add_hsep)
   }
   comp_col();
 
-  stl_clear_click_defs(wp->w_status_click_defs, (long)wp->w_status_click_defs_size);
+  stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size);
   xfree(wp->w_status_click_defs);
   wp->w_status_click_defs_size = 0;
   wp->w_status_click_defs = NULL;
@@ -6720,7 +6721,7 @@ int set_winbar_win(win_T *wp, bool make_room, bool valid_cursor)
 
     if (winbar_height == 0) {
       // When removing winbar, deallocate the w_winbar_click_defs array
-      stl_clear_click_defs(wp->w_winbar_click_defs, (long)wp->w_winbar_click_defs_size);
+      stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size);
       xfree(wp->w_winbar_click_defs);
       wp->w_winbar_click_defs_size = 0;
       wp->w_winbar_click_defs = NULL;
-- 
cgit 


From ae67706535b23233d2d6f5a81b7c7284c3cc16f9 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 11 Nov 2022 10:16:26 +0800
Subject: vim-patch:9.0.0858: "!!sort" in a closed fold sorts too many lines
 (#21022)

Problem:    "!!sort" in a closed fold sorts too many lines.
Solution:   Round to end of fold after adding the line count. (closes vim/vim#11487)

https://github.com/vim/vim/commit/f00112d558eb9a7d1d5413c096960ddcc52c9f66

N/A patches for version.c:

vim-patch:9.0.0855: comment not located above the code it refers to

Problem:    Comment not located above the code it refers to.
Solution:   Move the comment. (closes vim/vim#11527)

https://github.com/vim/vim/commit/09a93e3e66689c691a00fce25e4ce310d81edaee

vim-patch:9.0.0859: compiler warning for unused variable

Problem:    Compiler warning for unused variable.
Solution:   Add #ifdef.

https://github.com/vim/vim/commit/fd3084b6e298477dec4979515c6b4a8a3c3beeb2

Co-authored-by: Bram Moolenaar 
---
 src/nvim/ex_docmd.c            | 24 +++++++++++++++++++-----
 src/nvim/testdir/test_fold.vim | 36 ++++++++++++++++++++++++++++++++++++
 2 files changed, 55 insertions(+), 5 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 31e9401125..f4100272e1 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -3224,6 +3224,8 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
   char *cmd = skipwhite(*ptr);
   linenr_T lnum = MAXLNUM;
   do {
+    const int base_char = (uint8_t)(*cmd);
+
     switch (*cmd) {
     case '.':                               // '.' - Cursor position
       cmd++;
@@ -3482,9 +3484,10 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
       } else {
         i = (char_u)(*cmd++);
       }
-      if (!ascii_isdigit(*cmd)) {       // '+' is '+1', but '+0' is not '+1'
+      if (!ascii_isdigit(*cmd)) {       // '+' is '+1'
         n = 1;
       } else {
+        // "number", "+number" or "-number"
         n = getdigits_int32(&cmd, false, MAXLNUM);
         if (n == MAXLNUM) {
           emsg(_(e_line_number_out_of_range));
@@ -3499,10 +3502,16 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
       } else if (addr_type == ADDR_LOADED_BUFFERS || addr_type == ADDR_BUFFERS) {
         lnum = compute_buffer_local_count(addr_type, lnum, (i == '-') ? -1 * n : n);
       } else {
-        // Relative line addressing, need to adjust for folded lines
-        // now, but only do it after the first address.
-        if (addr_type == ADDR_LINES && (i == '-' || i == '+')
-            && address_count >= 2) {
+        // Relative line addressing: need to adjust for closed folds
+        // after the first address.
+        // Subtle difference: "number,+number" and "number,-number"
+        // adjusts to end of closed fold before adding/subtracting,
+        // while "number,.+number" adjusts to end of closed fold after
+        // adding to make "!!" expanded into ".,.+N" work correctly.
+        bool adjust_for_folding = addr_type == ADDR_LINES
+                                  && (i == '-' || i == '+')
+                                  && address_count >= 2;
+        if (adjust_for_folding && (i == '-' || base_char != '.')) {
           (void)hasFolding(lnum, NULL, &lnum);
         }
         if (i == '-') {
@@ -3513,6 +3522,11 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
             goto error;
           }
           lnum += n;
+          // ".+number" rounds up to the end of a closed fold after
+          // adding, so that ":!!sort" sorts one closed fold.
+          if (adjust_for_folding && base_char == '.') {
+            (void)hasFolding(lnum, NULL, &lnum);
+          }
         }
       }
     }
diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim
index 832dd43b65..1ee9165308 100644
--- a/src/nvim/testdir/test_fold.vim
+++ b/src/nvim/testdir/test_fold.vim
@@ -986,4 +986,40 @@ func Test_indent_append_blank_small_fold_close()
   bw!
 endfunc
 
+func Test_sort_closed_fold()
+  CheckExecutable sort
+
+  call setline(1, [
+        \ 'Section 1',
+        \ '   how',
+        \ '   now',
+        \ '   brown',
+        \ '   cow',
+        \ 'Section 2',
+        \ '   how',
+        \ '   now',
+        \ '   brown',
+        \ '   cow',
+        \])
+  setlocal foldmethod=indent sw=3
+  normal 2G
+
+  " The "!!" expands to ".,.+3" and must only sort four lines
+  call feedkeys("!!sort\", 'xt')
+  call assert_equal([
+        \ 'Section 1',
+        \ '   brown',
+        \ '   cow',
+        \ '   how',
+        \ '   now',
+        \ 'Section 2',
+        \ '   how',
+        \ '   now',
+        \ '   brown',
+        \ '   cow',
+        \ ], getline(1, 10))
+
+  bwipe!
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From fc7ac688c397b5f748920597fcc70fe46e907944 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 11 Nov 2022 16:46:45 +0800
Subject: fix(messages): don't set cmdline_row when messages have scrolled
 (#21015)

When 'cmdheight' is changed while messages have scrolled, the position
of msg_grid is not moved up, so cmdline_row should not be set based on
the position of msg_grid.
---
 src/nvim/message.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/message.c b/src/nvim/message.c
index 314232d4be..d703f9f260 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -196,7 +196,7 @@ void msg_grid_validate(void)
     msg_grid_set_pos(max_rows, false);
   }
 
-  if (msg_grid.chars && cmdline_row < msg_grid_pos) {
+  if (msg_grid.chars && !msg_scrolled && cmdline_row < msg_grid_pos) {
     // TODO(bfredl): this should already be the case, but fails in some
     // "batched" executions where compute_cmdrow() use stale positions or
     // something.
-- 
cgit 


From 0d8e8d36ec7d3f4967f27389b4b94edf3ba57433 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 11 Nov 2022 17:50:52 +0800
Subject: vim-patch:8.2.1919: assert_fails() setting emsg_silent changes normal
 execution (#20998)

Problem:    Assert_fails() setting emsg_silent changes normal execution.
Solution:   Use a separate flag in_assert_fails.

https://github.com/vim/vim/commit/28ee892ac4197421b3317f195512ca64cc56a5b4

Cherry-pick no_wait_return from patch 9.0.0846.

Co-authored-by: Bram Moolenaar 
---
 src/nvim/buffer.c                 |  2 +-
 src/nvim/change.c                 |  2 +-
 src/nvim/fileio.c                 |  2 +-
 src/nvim/globals.h                |  3 ++-
 src/nvim/insexpand.c              |  2 +-
 src/nvim/message.c                |  2 +-
 src/nvim/normal.c                 |  1 +
 src/nvim/screen.c                 |  3 ++-
 src/nvim/testdir/test_autocmd.vim |  2 +-
 src/nvim/testdir/test_mapping.vim |  2 +-
 src/nvim/testdir/test_popup.vim   |  6 ++----
 src/nvim/testing.c                | 15 +++++++++++----
 src/nvim/ui.c                     |  2 +-
 13 files changed, 26 insertions(+), 18 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index fa0b2a83c8..78b058ea9a 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1842,7 +1842,7 @@ buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags)
     pmap_put(handle_T)(&buffer_handles, buf->b_fnum, buf);
     if (top_file_num < 0) {  // wrap around (may cause duplicates)
       emsg(_("W14: Warning: List of file names overflow"));
-      if (emsg_silent == 0) {
+      if (emsg_silent == 0 && !in_assert_fails) {
         ui_flush();
         os_delay(3001L, true);  // make sure it is noticed
       }
diff --git a/src/nvim/change.c b/src/nvim/change.c
index c6df98651d..a62880dfc1 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -107,7 +107,7 @@ void changed(void)
       // Wait two seconds, to make sure the user reads this unexpected
       // message.  Since we could be anywhere, call wait_return() now,
       // and don't let the emsg() set msg_scroll.
-      if (need_wait_return && emsg_silent == 0) {
+      if (need_wait_return && emsg_silent == 0 && !in_assert_fails) {
         ui_flush();
         os_delay(2002L, true);
         wait_return(true);
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 414a32fab3..e1ffc27d5b 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -4946,7 +4946,7 @@ int buf_check_timestamp(buf_T *buf)
         }
         msg_clr_eos();
         (void)msg_end();
-        if (emsg_silent == 0) {
+        if (emsg_silent == 0 && !in_assert_fails) {
           ui_flush();
           // give the user some time to think about it
           os_delay(1004L, true);
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 14266fc859..76f62fe267 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -195,7 +195,6 @@ EXTERN int emsg_skip INIT(= 0);             // don't display errors for
 EXTERN bool emsg_severe INIT(= false);      // use message of next of several
                                             //  emsg() calls for throw
 // used by assert_fails()
-EXTERN bool emsg_assert_fails_used INIT(= false);
 EXTERN char *emsg_assert_fails_msg INIT(= NULL);
 EXTERN long emsg_assert_fails_lnum INIT(= 0);
 EXTERN char *emsg_assert_fails_context INIT(= NULL);
@@ -667,6 +666,8 @@ EXTERN int emsg_silent INIT(= 0);        // don't print error messages
 EXTERN bool emsg_noredir INIT(= false);  // don't redirect error messages
 EXTERN bool cmd_silent INIT(= false);    // don't echo the command line
 
+EXTERN bool in_assert_fails INIT(= false);  // assert_fails() active
+
 // Values for swap_exists_action: what to do when swap file already exists
 #define SEA_NONE        0       // don't use dialog
 #define SEA_DIALOG      1       // use dialog when possible
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index 2767d7939e..91ac1d5e8c 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -454,7 +454,7 @@ bool check_compl_option(bool dict_opt)
     msg_attr((dict_opt
               ? _("'dictionary' option is empty")
               : _("'thesaurus' option is empty")), HL_ATTR(HLF_E));
-    if (emsg_silent == 0) {
+    if (emsg_silent == 0 && !in_assert_fails) {
       vim_beep(BO_COMPL);
       setcursor();
       ui_flush();
diff --git a/src/nvim/message.c b/src/nvim/message.c
index d703f9f260..9fdd7a16a2 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -664,7 +664,7 @@ static bool emsg_multiline(const char *s, bool multiline)
       return true;
     }
 
-    if (emsg_assert_fails_used && emsg_assert_fails_msg == NULL) {
+    if (in_assert_fails && emsg_assert_fails_msg == NULL) {
       emsg_assert_fails_msg = xstrdup(s);
       emsg_assert_fails_lnum = SOURCING_LNUM;
       xfree(emsg_assert_fails_context);
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 0999476ca4..27a7b1ae04 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -611,6 +611,7 @@ static bool normal_need_redraw_mode_message(NormalState *s)
           && stuff_empty()
           && typebuf_typed()
           && emsg_silent == 0
+          && !in_assert_fails
           && !did_wait_return
           && s->oa.op_type == OP_NOP);
 }
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 2470fd326b..3f27302617 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -572,7 +572,8 @@ void check_for_delay(bool check_msg_scroll)
 {
   if ((emsg_on_display || (check_msg_scroll && msg_scroll))
       && !did_wait_return
-      && emsg_silent == 0) {
+      && emsg_silent == 0
+      && !in_assert_fails) {
     ui_flush();
     os_delay(1006L, true);
     emsg_on_display = false;
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 1e9406eb87..f4c32599f1 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -175,7 +175,7 @@ func Test_autocmd_bufunload_avoiding_SEGV_01()
     exe 'autocmd BufUnload  ' . (lastbuf + 1) . 'bwipeout!'
   augroup END
 
-  call assert_fails('edit bb.txt', ['E937:', 'E517:'])
+  call assert_fails('edit bb.txt', 'E937:')
 
   autocmd! test_autocmd_bufunload
   augroup! test_autocmd_bufunload
diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim
index 560883ba5d..522a51589f 100644
--- a/src/nvim/testdir/test_mapping.vim
+++ b/src/nvim/testdir/test_mapping.vim
@@ -779,7 +779,7 @@ func Test_expr_abbr()
   " invalid  abbreviation
   abbr  hte GetAbbr()
   call assert_fails('normal ihte ', 'E117:')
-  call assert_equal(' ', getline(1))
+  call assert_equal('', getline(1))
   unabbr  hte
 
   close!
diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim
index 7f183f0849..0981329320 100644
--- a/src/nvim/testdir/test_popup.vim
+++ b/src/nvim/testdir/test_popup.vim
@@ -359,7 +359,7 @@ func Test_completefunc_opens_new_window_one()
   /^one
   call assert_fails('call feedkeys("A\\\\", "x")', 'E565:')
   call assert_equal(winid, win_getid())
-  call assert_equal('oneDEF', getline(1))
+  call assert_equal('onedef', getline(1))
   q!
 endfunc
 
@@ -384,9 +384,7 @@ func Test_completefunc_opens_new_window_two()
   /^two
   call assert_fails('call feedkeys("A\\\\", "x")', 'E565:')
   call assert_equal(winid, win_getid())
-  " v8.2.1919 hasn't been ported yet
-  " call assert_equal('twodef', getline(1))
-  call assert_equal('twoDEF', getline(1))
+  call assert_equal('twodef', getline(1))
   q!
 endfunc
 
diff --git a/src/nvim/testing.c b/src/nvim/testing.c
index 9dd41224da..a37ceeb86b 100644
--- a/src/nvim/testing.c
+++ b/src/nvim/testing.c
@@ -6,6 +6,8 @@
 #include "nvim/eval.h"
 #include "nvim/eval/encode.h"
 #include "nvim/ex_docmd.h"
+#include "nvim/globals.h"
+#include "nvim/message.h"
 #include "nvim/os/os.h"
 #include "nvim/runtime.h"
 #include "nvim/testing.h"
@@ -491,8 +493,8 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   // trylevel must be zero for a ":throw" command to be considered failed
   trylevel = 0;
   suppress_errthrow = true;
-  emsg_silent = true;
-  emsg_assert_fails_used = true;
+  in_assert_fails = true;
+  no_wait_return++;
 
   do_cmdline_cmd(cmd);
   if (called_emsg == called_emsg_before) {
@@ -584,9 +586,14 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 theend:
   trylevel = save_trylevel;
   suppress_errthrow = false;
-  emsg_silent = false;
+  in_assert_fails = false;
+  did_emsg = false;
+  msg_col = 0;
+  no_wait_return--;
+  need_wait_return = false;
   emsg_on_display = false;
-  emsg_assert_fails_used = false;
+  msg_reset_scroll();
+  lines_left = Rows;
   XFREE_CLEAR(emsg_assert_fails_msg);
   set_vim_var_string(VV_ERRMSG, NULL, 0);
   if (wrong_arg_msg != NULL) {
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index eb59d72e43..bee8d461a7 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -315,7 +315,7 @@ void vim_beep(unsigned val)
 {
   called_vim_beep = true;
 
-  if (emsg_silent == 0) {
+  if (emsg_silent == 0 && !in_assert_fails) {
     if (!((bo_flags & val) || (bo_flags & BO_ALL))) {
       static int beeps = 0;
       static uint64_t start_time = 0;
-- 
cgit 


From 0d7cc5ee85c2722ab68e276a2bfda336ef9429d5 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 11 Nov 2022 18:18:38 +0800
Subject: vim-patch:9.0.0715: wrong argument for append() gives two error
 messages (#21023)

Problem:    Wrong argument for append() gives two error messages.
Solution:   When getting an error for a number argument don't try using it as
            a string. (closes vim/vim#11335)

https://github.com/vim/vim/commit/801cd35e7e3b21e519e12a1610ee1d721e40893e

Co-authored-by: Bram Moolenaar 
---
 src/nvim/testdir/test_functions.vim | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 0f2066b7c1..aaef01bdf5 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -813,6 +813,8 @@ func Test_append()
 
   " Using $ instead of '$' must give an error
   call assert_fails("call append($, 'foobar')", 'E116:')
+
+  call assert_fails("call append({}, '')", ['E728:', 'E728:'])
 endfunc
 
 " Test for setline()
-- 
cgit 


From eee956051637a5dff02ba6c6083fbffc87c0c96e Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 12 Nov 2022 07:43:36 +0800
Subject: vim-patch:9.0.0861: solution for "!!sort" in closed fold is not
 optimal (#21027)

Problem:    Solution for "!!sort" in closed fold is not optimal.
Solution:   Use a different range instead of the subtle difference in handling
            a range with an offset. (issue vim/vim#11487)

https://github.com/vim/vim/commit/9954dc39ea090cee6bf41c888c41e60d9f52c3b8

Co-authored-by: Bram Moolenaar 
---
 src/nvim/ex_docmd.c            | 21 ++++--------------
 src/nvim/ops.c                 | 13 ++++++++++--
 src/nvim/testdir/test_fold.vim | 48 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 63 insertions(+), 19 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index f4100272e1..b875aafaad 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -3224,8 +3224,6 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
   char *cmd = skipwhite(*ptr);
   linenr_T lnum = MAXLNUM;
   do {
-    const int base_char = (uint8_t)(*cmd);
-
     switch (*cmd) {
     case '.':                               // '.' - Cursor position
       cmd++;
@@ -3502,16 +3500,10 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
       } else if (addr_type == ADDR_LOADED_BUFFERS || addr_type == ADDR_BUFFERS) {
         lnum = compute_buffer_local_count(addr_type, lnum, (i == '-') ? -1 * n : n);
       } else {
-        // Relative line addressing: need to adjust for closed folds
-        // after the first address.
-        // Subtle difference: "number,+number" and "number,-number"
-        // adjusts to end of closed fold before adding/subtracting,
-        // while "number,.+number" adjusts to end of closed fold after
-        // adding to make "!!" expanded into ".,.+N" work correctly.
-        bool adjust_for_folding = addr_type == ADDR_LINES
-                                  && (i == '-' || i == '+')
-                                  && address_count >= 2;
-        if (adjust_for_folding && (i == '-' || base_char != '.')) {
+        // Relative line addressing: need to adjust for lines in a
+        // closed fold after the first address.
+        if (addr_type == ADDR_LINES && (i == '-' || i == '+')
+            && address_count >= 2) {
           (void)hasFolding(lnum, NULL, &lnum);
         }
         if (i == '-') {
@@ -3522,11 +3514,6 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
             goto error;
           }
           lnum += n;
-          // ".+number" rounds up to the end of a closed fold after
-          // adding, so that ":!!sort" sorts one closed fold.
-          if (adjust_for_folding && base_char == '.') {
-            (void)hasFolding(lnum, NULL, &lnum);
-          }
         }
       }
     }
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 65fc42bc08..8656b4265d 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -5555,13 +5555,22 @@ static void op_colon(oparg_T *oap)
     } else {
       stuffnumReadbuff((long)oap->start.lnum);
     }
-    if (oap->end.lnum != oap->start.lnum) {
+
+    // When using !! on a closed fold the range ".!" works best to operate
+    // on, it will be made the whole closed fold later.
+    linenr_T endOfStartFold = oap->start.lnum;
+    (void)hasFolding(oap->start.lnum, NULL, &endOfStartFold);
+    if (oap->end.lnum != oap->start.lnum && oap->end.lnum != endOfStartFold) {
+      // Make it a range with the end line.
       stuffcharReadbuff(',');
       if (oap->end.lnum == curwin->w_cursor.lnum) {
         stuffcharReadbuff('.');
       } else if (oap->end.lnum == curbuf->b_ml.ml_line_count) {
         stuffcharReadbuff('$');
-      } else if (oap->start.lnum == curwin->w_cursor.lnum) {
+      } else if (oap->start.lnum == curwin->w_cursor.lnum
+                 // do not use ".+number" for a closed fold, it would count
+                 // folded lines twice
+                 && !hasFolding(oap->end.lnum, NULL, NULL)) {
         stuffReadbuff(".+");
         stuffnumReadbuff(oap->line_count - 1);
       } else {
diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim
index 1ee9165308..1b979dbc03 100644
--- a/src/nvim/testdir/test_fold.vim
+++ b/src/nvim/testdir/test_fold.vim
@@ -72,6 +72,54 @@ func Test_address_fold()
   quit!
 endfunc
 
+func Test_address_offsets()
+  " check the help for :range-closed-fold
+  enew
+  call setline(1, [
+        \ '1 one',
+        \ '2 two',
+        \ '3 three',
+        \ '4 four FOLDED',
+        \ '5 five FOLDED',
+        \ '6 six',
+        \ '7 seven',
+        \ '8 eight',
+        \])
+  set foldmethod=manual
+  normal 4Gvjzf
+  3,4+2yank
+  call assert_equal([
+        \ '3 three',
+        \ '4 four FOLDED',
+        \ '5 five FOLDED',
+        \ '6 six',
+        \ '7 seven',
+        \ ], getreg(0,1,1))
+
+  enew!
+  call setline(1, [
+        \ '1 one',
+        \ '2 two',
+        \ '3 three FOLDED',
+        \ '4 four FOLDED',
+        \ '5 five FOLDED',
+        \ '6 six FOLDED',
+        \ '7 seven',
+        \ '8 eight',
+        \])
+  normal 3Gv3jzf
+  2,4-1yank
+  call assert_equal([
+        \ '2 two',
+        \ '3 three FOLDED',
+        \ '4 four FOLDED',
+        \ '5 five FOLDED',
+        \ '6 six FOLDED',
+        \ ], getreg(0,1,1))
+
+  bwipe!
+endfunc
+
 func Test_indent_fold()
     new
     call setline(1, ['', 'a', '    b', '    c'])
-- 
cgit 


From 2425fe2dc5e5985779912319433ddf914a20dd6a Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 12 Nov 2022 09:57:29 +0800
Subject: vim-patch:8.2.2207: illegal memory access if popup menu items are
 changed (#21028)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Problem:    Illegal memory access if popup menu items are changed while the
            menu is visible. (Tomáš Janoušek)
Solution:   Make a copy of the text. (closes vim/vim#7537)

https://github.com/vim/vim/commit/38455a921395a56690790c8c1d28c1c43ca04c8a

Co-authored-by: Bram Moolenaar 
---
 src/nvim/popupmenu.c            | 13 +++++++++++--
 src/nvim/testdir/test_popup.vim | 28 ++++++++++++++++++++++++++--
 2 files changed, 37 insertions(+), 4 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c
index db22d50d66..234ce5fcba 100644
--- a/src/nvim/popupmenu.c
+++ b/src/nvim/popupmenu.c
@@ -1043,10 +1043,16 @@ void pum_show_popupmenu(vimmenu_T *menu)
   pumitem_T *array = (pumitem_T *)xcalloc((size_t)pum_size, sizeof(pumitem_T));
 
   for (vimmenu_T *mp = menu->children; mp != NULL; mp = mp->next) {
+    char *s = NULL;
+    // Make a copy of the text, the menu may be redefined in a callback.
     if (menu_is_separator(mp->dname)) {
-      array[idx++].pum_text = (char_u *)"";
+      s = "";
     } else if (mp->modes & mp->enabled & mode) {
-      array[idx++].pum_text = (char_u *)mp->dname;
+      s = mp->dname;
+    }
+    if (s != NULL) {
+      s = xstrdup(s);
+      array[idx++].pum_text = (char_u *)s;
     }
   }
 
@@ -1117,6 +1123,9 @@ void pum_show_popupmenu(vimmenu_T *menu)
     }
   }
 
+  for (idx = 0; idx < pum_size; idx++) {
+    xfree(array[idx].pum_text);
+  }
   xfree(array);
   pum_undisplay(true);
   if (!p_mousemev) {
diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim
index 0981329320..a9b258c5f5 100644
--- a/src/nvim/testdir/test_popup.vim
+++ b/src/nvim/testdir/test_popup.vim
@@ -871,18 +871,30 @@ func Test_popup_command()
   call assert_fails('popup Foo', 'E337:')
   unmenu Test.Foo
 
+  let script =<< trim END
+    func StartTimer()
+      call timer_start(100, {-> ChangeMenu()})
+    endfunc
+    func ChangeMenu()
+      nunmenu PopUp.&Paste
+      nnoremenu 1.40 PopUp.&Paste :echomsg "pasted"
+      echomsg 'changed'
+    endfunc
+  END
+  call writefile(script, 'XtimerScript')
+
   let lines =<< trim END
 	one two three four five
 	and one two Xthree four five
 	one more two three four five
   END
   call writefile(lines, 'Xtest')
-  let buf = RunVimInTerminal('Xtest', {})
+  let buf = RunVimInTerminal('-S XtimerScript Xtest', {})
   call term_sendkeys(buf, ":source $VIMRUNTIME/menu.vim\")
   call term_sendkeys(buf, "/X\:popup PopUp\")
   call VerifyScreenDump(buf, 'Test_popup_command_01', {})
 
-  " Select a word
+  " go to the Paste entry in the menu
   call term_sendkeys(buf, "jj")
   call VerifyScreenDump(buf, 'Test_popup_command_02', {})
 
@@ -891,8 +903,20 @@ func Test_popup_command()
   call VerifyScreenDump(buf, 'Test_popup_command_03', {})
 
   call term_sendkeys(buf, "\")
+
+  " Set a timer to change a menu entry while it's displayed.  The text should
+  " not change but the command does.  Making the screendump also verifies that
+  " "changed" shows up, which means the timer triggered
+  call term_sendkeys(buf, "/X\:call StartTimer() | popup PopUp\")
+  call VerifyScreenDump(buf, 'Test_popup_command_04', {})
+
+  " Select the Paste entry, executes the changed menu item.
+  call term_sendkeys(buf, "jj\")
+  call VerifyScreenDump(buf, 'Test_popup_command_05', {})
+
   call StopVimInTerminal(buf)
   call delete('Xtest')
+  call delete('XtimerScript')
 endfunc
 
 func Test_popup_complete_backwards()
-- 
cgit 


From 7335a67b5754255f0e892303a0f4e3521035e7d8 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 12 Nov 2022 12:29:16 +0800
Subject: vim-patch:9.0.0845: shell command with just space gives strange error
 (#21029)

Problem:    Shell command with just space gives strange error.
Solution:   Skip white space at start of the argument. (Christian Brabandt,
            Shane-XB-Qian, closes vim/vim#11515, closes vim/vim#11495)

https://github.com/vim/vim/commit/4e7590ec00483077daaa567aa2220bc8df912f3c

Co-authored-by: shane.xb.qian 
---
 src/nvim/ex_cmds.c                |  8 ++++---
 src/nvim/testdir/test_cmdline.vim | 47 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 52 insertions(+), 3 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 46870061a9..c22f22366a 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -1135,10 +1135,12 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out
     msg_scroll = scroll_save;
   }
 
-  // Try to find an embedded bang, like in :! ! [args]
-  // (:!! is indicated by the 'forceit' variable)
+  // Try to find an embedded bang, like in ":! ! [args]"
+  // ":!!" is indicated by the 'forceit' variable.
   bool ins_prevcmd = forceit;
-  trailarg = arg;
+
+  // Skip leading white space to avoid a strange error with some shells.
+  trailarg = skipwhite(arg);
   do {
     len = strlen(trailarg) + 1;
     if (newcmd != NULL) {
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index b0be7ab396..2921c8b41a 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -1628,6 +1628,53 @@ func Test_cmd_bang_E135()
   %bwipe!
 endfunc
 
+func Test_cmd_bang_args()
+  new
+  :.!
+  call assert_equal(0, v:shell_error)
+
+  " Note that below there is one space char after the '!'.  This caused a
+  " shell error in the past, see https://github.com/vim/vim/issues/11495.
+  :.! 
+  call assert_equal(0, v:shell_error)
+  bwipe!
+
+  CheckUnix
+  :.!pwd
+  call assert_equal(0, v:shell_error)
+  :.! pwd
+  call assert_equal(0, v:shell_error)
+
+  " Note there is one space after 'pwd'.
+  :.! pwd 
+  call assert_equal(0, v:shell_error)
+
+  " Note there are two spaces after 'pwd'.
+  :.!  pwd  
+  call assert_equal(0, v:shell_error)
+  :.!ls ~
+  call assert_equal(0, v:shell_error)
+
+  " Note there is one space char after '~'.
+  :.!ls  ~ 
+  call assert_equal(0, v:shell_error)
+
+  " Note there are two spaces after '~'.
+  :.!ls  ~  
+  call assert_equal(0, v:shell_error)
+
+  :.!echo "foo"
+  call assert_equal(getline('.'), "foo")
+  :.!echo "foo  "
+  call assert_equal(getline('.'), "foo  ")
+  :.!echo " foo  "
+  call assert_equal(getline('.'), " foo  ")
+  :.!echo  " foo  "
+  call assert_equal(getline('.'), " foo  ")
+
+  %bwipe!
+endfunc
+
 " Test for using ~ for home directory in cmdline completion matches
 func Test_cmdline_expand_home()
   call mkdir('Xdir')
-- 
cgit 


From 7e6d785d19926714615758e75c4d43e856d13a6f Mon Sep 17 00:00:00 2001
From: Thomas Vigouroux 
Date: Tue, 13 Sep 2022 09:44:24 +0200
Subject: feat(extmarks): allow preventing spellchecking with spell = false

---
 src/nvim/api/extmark.c |  8 ++++++--
 src/nvim/decoration.c  | 12 ++++++++----
 src/nvim/decoration.h  |  6 +++---
 src/nvim/drawline.c    |  5 ++---
 src/nvim/extmark.c     |  2 +-
 src/nvim/spell.c       | 21 +++++++++++----------
 src/nvim/types.h       |  3 +++
 7 files changed, 34 insertions(+), 23 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index fee6876469..54eb7d9b6b 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -721,8 +721,12 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
   bool ephemeral = false;
   OPTION_TO_BOOL(ephemeral, ephemeral, false);
 
-  OPTION_TO_BOOL(decor.spell, spell, false);
-  if (decor.spell) {
+  if (opts->spell.type == kObjectTypeNil) {
+    decor.spell = kNone;
+  } else {
+    bool spell = false;
+    OPTION_TO_BOOL(spell, spell, false);
+    decor.spell = spell ? kTrue : kFalse;
     has_decor = true;
   }
 
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index 19e99fa7a6..230d96a15f 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -69,7 +69,11 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start
 void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor)
 {
   if (row2 >= row1) {
-    if (!decor || decor->hl_id || decor_has_sign(decor) || decor->conceal || decor->spell) {
+    if (!decor
+        || decor->hl_id
+        || decor_has_sign(decor)
+        || decor->conceal
+        || decor->spell != kNone) {
       redraw_buf_range_later(buf, row1 + 1, row2 + 1);
     }
   }
@@ -309,7 +313,7 @@ next_mark:
   bool conceal = 0;
   int conceal_char = 0;
   int conceal_attr = 0;
-  bool spell = false;
+  TriState spell = kNone;
 
   for (size_t i = 0; i < kv_size(state->active); i++) {
     DecorRange item = kv_A(state->active, i);
@@ -343,8 +347,8 @@ next_mark:
         conceal_attr = item.attr_id;
       }
     }
-    if (active && item.decor.spell) {
-      spell = true;
+    if (active && item.decor.spell != kNone) {
+      spell = item.decor.spell;
     }
     if ((item.start_row == state->row && item.start_col <= col)
         && decor_virt_pos(item.decor)
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index 9ba621d7a4..8f016c103b 100644
--- a/src/nvim/decoration.h
+++ b/src/nvim/decoration.h
@@ -45,7 +45,7 @@ struct Decoration {
   bool hl_eol;
   bool virt_lines_above;
   bool conceal;
-  bool spell;
+  TriState spell;
   // TODO(bfredl): style, etc
   DecorPriority priority;
   int col;  // fixed col value, like win_col
@@ -61,7 +61,7 @@ struct Decoration {
   bool ui_watched;  // watched for win_extmark
 };
 #define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, \
-                          kHlModeUnknown, false, false, false, false, false, \
+                          kHlModeUnknown, false, false, false, false, kNone, \
                           DECOR_PRIORITY_BASE, 0, 0, NULL, 0, 0, 0, 0, 0, false }
 
 typedef struct {
@@ -91,7 +91,7 @@ typedef struct {
   int conceal_char;
   int conceal_attr;
 
-  bool spell;
+  TriState spell;
 } DecorState;
 
 EXTERN DecorState decor_state INIT(= { 0 });
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index cb7d85a467..6d42b91507 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -33,6 +33,7 @@
 #include "nvim/spell.h"
 #include "nvim/state.h"
 #include "nvim/syntax.h"
+#include "nvim/types.h"
 #include "nvim/undo.h"
 #include "nvim/window.h"
 
@@ -1722,9 +1723,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
             decor_conceal = 2;  // really??
           }
 
-          if (decor_state.spell) {
-            can_spell = true;
-          }
+          can_spell = TRISTATE_TO_BOOL(decor_state.spell, can_spell);
         }
 
         // Check spelling (unless at the end of the line).
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c
index df87cc8ab6..015799be06 100644
--- a/src/nvim/extmark.c
+++ b/src/nvim/extmark.c
@@ -71,7 +71,7 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col
         || decor->conceal
         || decor_has_sign(decor)
         || decor->ui_watched
-        || decor->spell) {
+        || decor->spell != kNone) {
       decor_full = true;
       decor = xmemdup(decor, sizeof *decor);
     }
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index f24af5c3bf..b1daf4ed23 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -1216,7 +1216,14 @@ static bool decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_lnum,
     *decor_lnum = lnum;
   }
   decor_redraw_col(wp->w_buffer, col, col, false, &decor_state);
-  return decor_state.spell;
+  return decor_state.spell == kTrue;
+}
+
+static inline bool can_syn_spell(win_T *wp, linenr_T lnum, int col)
+{
+  bool can_spell;
+  (void)syn_get_id(wp, lnum, col, false, &can_spell, false);
+  return can_spell;
 }
 
 /// Moves to the next spell error.
@@ -1346,15 +1353,9 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
                             : p - buf) > wp->w_cursor.col)) {
             col = (colnr_T)(p - buf);
 
-            bool can_spell = decor_spell_nav_col(wp, lnum, &decor_lnum, col, &decor_error);
-
-            if (!can_spell) {
-              if (has_syntax) {
-                (void)syn_get_id(wp, lnum, col, false, &can_spell, false);
-              } else {
-                can_spell = (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) == 0;
-              }
-            }
+            bool can_spell = (!has_syntax && (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) == 0)
+                             || decor_spell_nav_col(wp, lnum, &decor_lnum, col, &decor_error)
+                             || (has_syntax && can_syn_spell(wp, lnum, col));
 
             if (!can_spell) {
               attr = HLF_COUNT;
diff --git a/src/nvim/types.h b/src/nvim/types.h
index fb10bf21d9..d90c623955 100644
--- a/src/nvim/types.h
+++ b/src/nvim/types.h
@@ -45,6 +45,9 @@ typedef enum {
   kTrue  = 1,
 } TriState;
 
+#define TRISTATE_TO_BOOL(val, \
+                         default) ((val) == kTrue ? true : ((val) == kFalse ? false : (default)))
+
 typedef struct Decoration Decoration;
 
 #endif  // NVIM_TYPES_H
-- 
cgit 


From 7abe8ef42267db861e84ce12023b296d49de6f25 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 12 Nov 2022 23:31:17 +0800
Subject: vim-patch:9.0.0862: default value of 'endoffile' is wrong (#21032)

Problem:    Default value of 'endoffile' is wrong.
Solution:   The default must be 'noendoffile'.

https://github.com/vim/vim/commit/0aad88f073602849d1623122eb3c323f8e252def

Co-authored-by: Bram Moolenaar 
---
 src/nvim/options.lua              |  2 +-
 src/nvim/testdir/test_options.vim | 12 ++++++++++++
 2 files changed, 13 insertions(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index e938760e67..dc0561d560 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -649,7 +649,7 @@ return {
       no_mkrc=true,
       redraw={'statuslines'},
       varname='p_eof',
-      defaults={if_true=true}
+      defaults={if_true=false}
     },
     {
       full_name='endofline', abbreviation='eol',
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index db544e47b8..11c2977a4e 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -1264,5 +1264,17 @@ func Test_keywordprg_empty()
   let &keywordprg = k
 endfunc
 
+" check that the very first buffer created does not have 'endoffile' set
+func Test_endoffile_default()
+  let after =<< trim [CODE]
+    call writefile([execute('set eof?')], 'Xtestout')
+    qall!
+  [CODE]
+  if RunVim([], after, '')
+    call assert_equal(["\nnoendoffile"], readfile('Xtestout'))
+  endif
+  call delete('Xtestout')
+endfunc
+
 
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From 47ad4c8701b4233aa302c1c21ff08a5f223596c7 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 13 Nov 2022 07:06:37 +0800
Subject: vim-patch:9.0.0866: no test for what patch 8.2.2207 fixes (#21034)

Problem:    No test for what patch 8.2.2207 fixes.
Solution:   Add a test case. (closes vim/vim#11531)

https://github.com/vim/vim/commit/f7570f2107d91f35dc67dd0e400fc638585b226c
---
 src/nvim/testdir/test_popup.vim | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim
index a9b258c5f5..791cce4431 100644
--- a/src/nvim/testdir/test_popup.vim
+++ b/src/nvim/testdir/test_popup.vim
@@ -862,7 +862,6 @@ func Test_popup_position()
 endfunc
 
 func Test_popup_command()
-  CheckScreendump
   CheckFeature menu
 
   menu Test.Foo Foo
@@ -870,13 +869,18 @@ func Test_popup_command()
   call assert_fails('popup Test.Foo.X', 'E327:')
   call assert_fails('popup Foo', 'E337:')
   unmenu Test.Foo
+endfunc
+
+func Test_popup_command_dump()
+  CheckFeature menu
+  CheckScreendump
 
   let script =<< trim END
     func StartTimer()
       call timer_start(100, {-> ChangeMenu()})
     endfunc
     func ChangeMenu()
-      nunmenu PopUp.&Paste
+      aunmenu PopUp.&Paste
       nnoremenu 1.40 PopUp.&Paste :echomsg "pasted"
       echomsg 'changed'
     endfunc
-- 
cgit 


From 2cb0860117623368bc53e4d578695136ce6912e0 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 13 Nov 2022 07:17:57 +0800
Subject: vim-patch:8.2.4675: no error for missing expression after :elseif

Problem:    No error for missing expression after :elseif. (Ernie Rael)
Solution:   Check for missing expression. (closes vim/vim#10068)

https://github.com/vim/vim/commit/fa010cdfb115fd2f6bae7ea6f6e63be906b5e347

Co-authored-by: Bram Moolenaar 
---
 src/nvim/ex_eval.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 8c2ac895cb..7ffd7bad7f 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -899,7 +899,13 @@ void ex_else(exarg_T *eap)
 
   if (eap->cmdidx == CMD_elseif) {
     bool error;
-    result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
+    // When skipping we ignore most errors, but a missing expression is
+    // wrong, perhaps it should have been "else".
+    if (skip && ends_excmd(*eap->arg)) {
+      semsg(_(e_invexpr2), eap->arg);
+    } else {
+      result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
+    }
 
     // When throwing error exceptions, we want to throw always the first
     // of several errors in a row.  This is what actually happens when
-- 
cgit 


From bc1dbebe1f18df719b9e357f4d8b9bea3a3581b8 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 13 Nov 2022 07:29:05 +0800
Subject: vim-patch:8.2.4676: test fails with different error

Problem:    Test fails with different error.
Solution:   Add argument for :elseif.

https://github.com/vim/vim/commit/292e1b9f681054a1de8fa22315ae6eedd7acb205

Co-authored-by: Bram Moolenaar 
---
 src/nvim/testdir/test_vimscript.vim | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim
index 44904af160..0fce52f88c 100644
--- a/src/nvim/testdir/test_vimscript.vim
+++ b/src/nvim/testdir/test_vimscript.vim
@@ -3024,7 +3024,7 @@ func Test_nested_if_else_errors()
 
   " :elseif without :if
   let code =<< trim END
-    elseif
+    elseif 1
   END
   call writefile(code, 'Xtest')
   call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')
@@ -3032,7 +3032,7 @@ func Test_nested_if_else_errors()
   " :elseif without :if
   let code =<< trim END
     while 1
-      elseif
+      elseif 1
     endwhile
   END
   call writefile(code, 'Xtest')
@@ -3042,7 +3042,7 @@ func Test_nested_if_else_errors()
   let code =<< trim END
     try
     finally
-      elseif
+      elseif 1
     endtry
   END
   call writefile(code, 'Xtest')
@@ -3051,7 +3051,7 @@ func Test_nested_if_else_errors()
   " :elseif without :if
   let code =<< trim END
     try
-      elseif
+      elseif 1
     endtry
   END
   call writefile(code, 'Xtest')
@@ -3062,7 +3062,7 @@ func Test_nested_if_else_errors()
     try
       throw "a"
     catch /a/
-      elseif
+      elseif 1
     endtry
   END
   call writefile(code, 'Xtest')
-- 
cgit 


From b25197258086faa94ddfaa2a74e1d0eb3695d9b3 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 13 Nov 2022 07:24:44 +0800
Subject: vim-patch:9.0.0869: bogus error when string used after :elseif

Problem:    Bogus error when string used after :elseif.
Solution:   Do not consider a double quote the start of a comment.
            (closes vim/vim#11534)

https://github.com/vim/vim/commit/28c56d501352bd98472d23667bade683877cadcc

Co-authored-by: Bram Moolenaar 
---
 src/nvim/ex_eval.c                  |  3 ++-
 src/nvim/testdir/test_vimscript.vim | 10 ++++++++++
 2 files changed, 12 insertions(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 7ffd7bad7f..bde2f3c801 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -901,7 +901,8 @@ void ex_else(exarg_T *eap)
     bool error;
     // When skipping we ignore most errors, but a missing expression is
     // wrong, perhaps it should have been "else".
-    if (skip && ends_excmd(*eap->arg)) {
+    // A double quote here is the start of a string, not a comment.
+    if (skip && *eap->arg != '"' && ends_excmd(*eap->arg)) {
       semsg(_(e_invexpr2), eap->arg);
     } else {
       result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim
index 0fce52f88c..c93ba9dcfc 100644
--- a/src/nvim/testdir/test_vimscript.vim
+++ b/src/nvim/testdir/test_vimscript.vim
@@ -193,6 +193,16 @@ func Test_if_while()
     call assert_equal('ab3j3b2c2b1f1h1km', g:Xpath)
 endfunc
 
+" Check double quote after skipped "elseif" does not give error E15
+func Test_skipped_elseif()
+  if "foo" ==? "foo"
+      let result = "first"
+  elseif "foo" ==? "foo"
+      let result = "second"
+  endif
+  call assert_equal('first', result)
+endfunc
+
 "-------------------------------------------------------------------------------
 " Test 4:   :return							    {{{1
 "-------------------------------------------------------------------------------
-- 
cgit 


From ec449c27fdad6cc907a6f4835ce28f31990ad519 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 13 Nov 2022 08:20:00 +0800
Subject: vim-patch:9.0.0867: wildmenu redrawing code is spread out (#21035)

Problem:    Wildmenu redrawing code is spread out.
Solution:   Refactor to move code together. (closes vim/vim#11528)

https://github.com/vim/vim/commit/d6e91385f0f7256aec8f70373c9e3399770d22e5

Co-authored-by: Bram Moolenaar 
---
 src/nvim/cmdexpand.c | 243 +++++++++++++++++++++++++++++++++++++++++++++++++--
 src/nvim/screen.c    | 235 -------------------------------------------------
 2 files changed, 238 insertions(+), 240 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index 480f3aa18c..ef742525da 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -20,6 +20,7 @@
 #include "nvim/ex_getln.h"
 #include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/grid.h"
 #include "nvim/help.h"
 #include "nvim/highlight_group.h"
 #include "nvim/locale.h"
@@ -34,6 +35,7 @@
 #include "nvim/screen.h"
 #include "nvim/search.h"
 #include "nvim/sign.h"
+#include "nvim/statusline.h"
 #include "nvim/strings.h"
 #include "nvim/syntax.h"
 #include "nvim/tag.h"
@@ -275,6 +277,236 @@ void cmdline_pum_cleanup(CmdlineInfo *cclp)
   wildmenu_cleanup(cclp);
 }
 
+/// Return the number of characters that should be skipped in the wildmenu
+/// These are backslashes used for escaping.  Do show backslashes in help tags.
+static int skip_wildmenu_char(expand_T *xp, char_u *s)
+{
+  if ((rem_backslash((char *)s) && xp->xp_context != EXPAND_HELP)
+      || ((xp->xp_context == EXPAND_MENUS || xp->xp_context == EXPAND_MENUNAMES)
+          && (s[0] == '\t' || (s[0] == '\\' && s[1] != NUL)))) {
+#ifndef BACKSLASH_IN_FILENAME
+    // TODO(bfredl): Why in the actual fuck are we special casing the
+    // shell variety deep in the redraw logic? Shell special snowflakiness
+    // should already be eliminated multiple layers before reaching the
+    // screen infracstructure.
+    if (xp->xp_shell && csh_like_shell() && s[1] == '\\' && s[2] == '!') {
+      return 2;
+    }
+#endif
+    return 1;
+  }
+  return 0;
+}
+
+/// Get the length of an item as it will be shown in the status line.
+static int wildmenu_match_len(expand_T *xp, char_u *s)
+{
+  int len = 0;
+
+  int emenu = (xp->xp_context == EXPAND_MENUS
+               || xp->xp_context == EXPAND_MENUNAMES);
+
+  // Check for menu separators - replace with '|'.
+  if (emenu && menu_is_separator((char *)s)) {
+    return 1;
+  }
+
+  while (*s != NUL) {
+    s += skip_wildmenu_char(xp, s);
+    len += ptr2cells((char *)s);
+    MB_PTR_ADV(s);
+  }
+
+  return len;
+}
+
+/// Show wildchar matches in the status line.
+/// Show at least the "match" item.
+/// We start at item "first_match" in the list and show all matches that fit.
+///
+/// If inversion is possible we use it. Else '=' characters are used.
+///
+/// @param matches  list of matches
+static void redraw_wildmenu(expand_T *xp, int num_matches, char **matches, int match, int showtail)
+{
+#define L_MATCH(m) (showtail ? showmatches_gettail(matches[m], false) : matches[m])
+  int row;
+  char_u *buf;
+  int len;
+  int clen;                     // length in screen cells
+  int fillchar;
+  int attr;
+  int i;
+  bool highlight = true;
+  char_u *selstart = NULL;
+  int selstart_col = 0;
+  char_u *selend = NULL;
+  static int first_match = 0;
+  bool add_left = false;
+  char_u *s;
+  int emenu;
+  int l;
+
+  if (matches == NULL) {        // interrupted completion?
+    return;
+  }
+
+  buf = xmalloc((size_t)Columns * MB_MAXBYTES + 1);
+
+  if (match == -1) {    // don't show match but original text
+    match = 0;
+    highlight = false;
+  }
+  // count 1 for the ending ">"
+  clen = wildmenu_match_len(xp, (char_u *)L_MATCH(match)) + 3;
+  if (match == 0) {
+    first_match = 0;
+  } else if (match < first_match) {
+    // jumping left, as far as we can go
+    first_match = match;
+    add_left = true;
+  } else {
+    // check if match fits on the screen
+    for (i = first_match; i < match; i++) {
+      clen += wildmenu_match_len(xp, (char_u *)L_MATCH(i)) + 2;
+    }
+    if (first_match > 0) {
+      clen += 2;
+    }
+    // jumping right, put match at the left
+    if ((long)clen > Columns) {
+      first_match = match;
+      // if showing the last match, we can add some on the left
+      clen = 2;
+      for (i = match; i < num_matches; i++) {
+        clen += wildmenu_match_len(xp, (char_u *)L_MATCH(i)) + 2;
+        if ((long)clen >= Columns) {
+          break;
+        }
+      }
+      if (i == num_matches) {
+        add_left = true;
+      }
+    }
+  }
+  if (add_left) {
+    while (first_match > 0) {
+      clen += wildmenu_match_len(xp, (char_u *)L_MATCH(first_match - 1)) + 2;
+      if ((long)clen >= Columns) {
+        break;
+      }
+      first_match--;
+    }
+  }
+
+  fillchar = fillchar_status(&attr, curwin);
+
+  if (first_match == 0) {
+    *buf = NUL;
+    len = 0;
+  } else {
+    STRCPY(buf, "< ");
+    len = 2;
+  }
+  clen = len;
+
+  i = first_match;
+  while (clen + wildmenu_match_len(xp, (char_u *)L_MATCH(i)) + 2 < Columns) {
+    if (i == match) {
+      selstart = buf + len;
+      selstart_col = clen;
+    }
+
+    s = (char_u *)L_MATCH(i);
+    // Check for menu separators - replace with '|'
+    emenu = (xp->xp_context == EXPAND_MENUS
+             || xp->xp_context == EXPAND_MENUNAMES);
+    if (emenu && menu_is_separator((char *)s)) {
+      STRCPY(buf + len, transchar('|'));
+      l = (int)STRLEN(buf + len);
+      len += l;
+      clen += l;
+    } else {
+      for (; *s != NUL; s++) {
+        s += skip_wildmenu_char(xp, s);
+        clen += ptr2cells((char *)s);
+        if ((l = utfc_ptr2len((char *)s)) > 1) {
+          STRNCPY(buf + len, s, l);  // NOLINT(runtime/printf)
+          s += l - 1;
+          len += l;
+        } else {
+          STRCPY(buf + len, transchar_byte(*s));
+          len += (int)STRLEN(buf + len);
+        }
+      }
+    }
+    if (i == match) {
+      selend = buf + len;
+    }
+
+    *(buf + len++) = ' ';
+    *(buf + len++) = ' ';
+    clen += 2;
+    if (++i == num_matches) {
+      break;
+    }
+  }
+
+  if (i != num_matches) {
+    *(buf + len++) = '>';
+    clen++;
+  }
+
+  buf[len] = NUL;
+
+  row = cmdline_row - 1;
+  if (row >= 0) {
+    if (wild_menu_showing == 0 || wild_menu_showing == WM_LIST) {
+      if (msg_scrolled > 0) {
+        // Put the wildmenu just above the command line.  If there is
+        // no room, scroll the screen one line up.
+        if (cmdline_row == Rows - 1) {
+          msg_scroll_up(false, false);
+          msg_scrolled++;
+        } else {
+          cmdline_row++;
+          row++;
+        }
+        wild_menu_showing = WM_SCROLLED;
+      } else {
+        // Create status line if needed by setting 'laststatus' to 2.
+        // Set 'winminheight' to zero to avoid that the window is
+        // resized.
+        if (lastwin->w_status_height == 0 && global_stl_height() == 0) {
+          save_p_ls = (int)p_ls;
+          save_p_wmh = (int)p_wmh;
+          p_ls = 2;
+          p_wmh = 0;
+          last_status(false);
+        }
+        wild_menu_showing = WM_SHOWN;
+      }
+    }
+
+    // Tricky: wildmenu can be drawn either over a status line, or at empty
+    // scrolled space in the message output
+    ScreenGrid *grid = (wild_menu_showing == WM_SCROLLED)
+                        ? &msg_grid_adj : &default_grid;
+
+    grid_puts(grid, (char *)buf, row, 0, attr);
+    if (selstart != NULL && highlight) {
+      *selend = NUL;
+      grid_puts(grid, (char *)selstart, row, selstart_col, HL_ATTR(HLF_WM));
+    }
+
+    grid_fill(grid, row, row + 1, clen, Columns,
+              fillchar, fillchar, attr);
+  }
+
+  win_redraw_last_status(topframe);
+  xfree(buf);
+}
+
 /// Get the next or prev cmdline completion match. The index of the match is set
 /// in "p_findex"
 static char *get_next_or_prev_match(int mode, expand_T *xp, int *p_findex, char *orig_save)
@@ -563,7 +795,8 @@ int showmatches(expand_T *xp, int wildmenu)
 {
   CmdlineInfo *const ccline = get_cmdline_info();
 #define L_SHOWFILE(m) (showtail \
-                       ? sm_gettail(files_found[m], false) : files_found[m])
+                       ? showmatches_gettail(files_found[m], false) \
+                       : files_found[m])
   int num_files;
   char **files_found;
   int i, j, k;
@@ -606,7 +839,7 @@ int showmatches(expand_T *xp, int wildmenu)
         .pum_kind = NULL,
       };
     }
-    char *endpos = (showtail ? sm_gettail(xp->xp_pattern, true) : xp->xp_pattern);
+    char *endpos = showtail ? showmatches_gettail(xp->xp_pattern, true) : xp->xp_pattern;
     if (ui_has(kUICmdline)) {
       compl_startcol = (int)(endpos - ccline->cmdbuff);
     } else {
@@ -739,9 +972,9 @@ int showmatches(expand_T *xp, int wildmenu)
   return EXPAND_OK;
 }
 
-/// Private path_tail for showmatches() (and redraw_wildmenu()):
-/// Find tail of file name path, but ignore trailing "/".
-char *sm_gettail(char *s, bool eager)
+/// path_tail() version for showmatches() and redraw_wildmenu():
+/// Return the tail of file name path "s", ignoring a trailing "/".
+static char *showmatches_gettail(char *s, bool eager)
 {
   char_u *p;
   char_u *t = (char_u *)s;
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 3f27302617..cbd5b96bef 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -15,9 +15,7 @@
 
 #include "nvim/buffer.h"
 #include "nvim/charset.h"
-#include "nvim/cmdexpand.h"
 #include "nvim/cursor.h"
-#include "nvim/drawscreen.h"
 #include "nvim/eval.h"
 #include "nvim/extmark.h"
 #include "nvim/fileio.h"
@@ -27,7 +25,6 @@
 #include "nvim/grid.h"
 #include "nvim/highlight.h"
 #include "nvim/highlight_group.h"
-#include "nvim/menu.h"
 #include "nvim/move.h"
 #include "nvim/option.h"
 #include "nvim/optionstr.h"
@@ -249,238 +246,6 @@ void rl_mirror(char *str)
   }
 }
 
-/// Get the length of an item as it will be shown in the status line.
-static int wildmenu_match_len(expand_T *xp, char_u *s)
-{
-  int len = 0;
-
-  int emenu = (xp->xp_context == EXPAND_MENUS
-               || xp->xp_context == EXPAND_MENUNAMES);
-
-  // Check for menu separators - replace with '|'.
-  if (emenu && menu_is_separator((char *)s)) {
-    return 1;
-  }
-
-  while (*s != NUL) {
-    s += skip_wildmenu_char(xp, s);
-    len += ptr2cells((char *)s);
-    MB_PTR_ADV(s);
-  }
-
-  return len;
-}
-
-/// Return the number of characters that should be skipped in the wildmenu
-/// These are backslashes used for escaping.  Do show backslashes in help tags.
-static int skip_wildmenu_char(expand_T *xp, char_u *s)
-{
-  if ((rem_backslash((char *)s) && xp->xp_context != EXPAND_HELP)
-      || ((xp->xp_context == EXPAND_MENUS
-           || xp->xp_context == EXPAND_MENUNAMES)
-          && (s[0] == '\t'
-              || (s[0] == '\\' && s[1] != NUL)))) {
-#ifndef BACKSLASH_IN_FILENAME
-    // TODO(bfredl): Why in the actual fuck are we special casing the
-    // shell variety deep in the redraw logic? Shell special snowflakiness
-    // should already be eliminated multiple layers before reaching the
-    // screen infracstructure.
-    if (xp->xp_shell && csh_like_shell() && s[1] == '\\' && s[2] == '!') {
-      return 2;
-    }
-#endif
-    return 1;
-  }
-  return 0;
-}
-
-/// Show wildchar matches in the status line.
-/// Show at least the "match" item.
-/// We start at item 'first_match' in the list and show all matches that fit.
-///
-/// If inversion is possible we use it. Else '=' characters are used.
-///
-/// @param matches  list of matches
-void redraw_wildmenu(expand_T *xp, int num_matches, char **matches, int match, int showtail)
-{
-#define L_MATCH(m) (showtail ? sm_gettail(matches[m], false) : matches[m])
-  int row;
-  char_u *buf;
-  int len;
-  int clen;                     // length in screen cells
-  int fillchar;
-  int attr;
-  int i;
-  bool highlight = true;
-  char_u *selstart = NULL;
-  int selstart_col = 0;
-  char_u *selend = NULL;
-  static int first_match = 0;
-  bool add_left = false;
-  char_u *s;
-  int emenu;
-  int l;
-
-  if (matches == NULL) {        // interrupted completion?
-    return;
-  }
-
-  buf = xmalloc((size_t)Columns * MB_MAXBYTES + 1);
-
-  if (match == -1) {    // don't show match but original text
-    match = 0;
-    highlight = false;
-  }
-  // count 1 for the ending ">"
-  clen = wildmenu_match_len(xp, (char_u *)L_MATCH(match)) + 3;
-  if (match == 0) {
-    first_match = 0;
-  } else if (match < first_match) {
-    // jumping left, as far as we can go
-    first_match = match;
-    add_left = true;
-  } else {
-    // check if match fits on the screen
-    for (i = first_match; i < match; i++) {
-      clen += wildmenu_match_len(xp, (char_u *)L_MATCH(i)) + 2;
-    }
-    if (first_match > 0) {
-      clen += 2;
-    }
-    // jumping right, put match at the left
-    if ((long)clen > Columns) {
-      first_match = match;
-      // if showing the last match, we can add some on the left
-      clen = 2;
-      for (i = match; i < num_matches; i++) {
-        clen += wildmenu_match_len(xp, (char_u *)L_MATCH(i)) + 2;
-        if ((long)clen >= Columns) {
-          break;
-        }
-      }
-      if (i == num_matches) {
-        add_left = true;
-      }
-    }
-  }
-  if (add_left) {
-    while (first_match > 0) {
-      clen += wildmenu_match_len(xp, (char_u *)L_MATCH(first_match - 1)) + 2;
-      if ((long)clen >= Columns) {
-        break;
-      }
-      first_match--;
-    }
-  }
-
-  fillchar = fillchar_status(&attr, curwin);
-
-  if (first_match == 0) {
-    *buf = NUL;
-    len = 0;
-  } else {
-    STRCPY(buf, "< ");
-    len = 2;
-  }
-  clen = len;
-
-  i = first_match;
-  while (clen + wildmenu_match_len(xp, (char_u *)L_MATCH(i)) + 2 < Columns) {
-    if (i == match) {
-      selstart = buf + len;
-      selstart_col = clen;
-    }
-
-    s = (char_u *)L_MATCH(i);
-    // Check for menu separators - replace with '|'
-    emenu = (xp->xp_context == EXPAND_MENUS
-             || xp->xp_context == EXPAND_MENUNAMES);
-    if (emenu && menu_is_separator((char *)s)) {
-      STRCPY(buf + len, transchar('|'));
-      l = (int)STRLEN(buf + len);
-      len += l;
-      clen += l;
-    } else {
-      for (; *s != NUL; s++) {
-        s += skip_wildmenu_char(xp, s);
-        clen += ptr2cells((char *)s);
-        if ((l = utfc_ptr2len((char *)s)) > 1) {
-          STRNCPY(buf + len, s, l);  // NOLINT(runtime/printf)
-          s += l - 1;
-          len += l;
-        } else {
-          STRCPY(buf + len, transchar_byte(*s));
-          len += (int)STRLEN(buf + len);
-        }
-      }
-    }
-    if (i == match) {
-      selend = buf + len;
-    }
-
-    *(buf + len++) = ' ';
-    *(buf + len++) = ' ';
-    clen += 2;
-    if (++i == num_matches) {
-      break;
-    }
-  }
-
-  if (i != num_matches) {
-    *(buf + len++) = '>';
-    clen++;
-  }
-
-  buf[len] = NUL;
-
-  row = cmdline_row - 1;
-  if (row >= 0) {
-    if (wild_menu_showing == 0 || wild_menu_showing == WM_LIST) {
-      if (msg_scrolled > 0) {
-        // Put the wildmenu just above the command line.  If there is
-        // no room, scroll the screen one line up.
-        if (cmdline_row == Rows - 1) {
-          msg_scroll_up(false, false);
-          msg_scrolled++;
-        } else {
-          cmdline_row++;
-          row++;
-        }
-        wild_menu_showing = WM_SCROLLED;
-      } else {
-        // Create status line if needed by setting 'laststatus' to 2.
-        // Set 'winminheight' to zero to avoid that the window is
-        // resized.
-        if (lastwin->w_status_height == 0 && global_stl_height() == 0) {
-          save_p_ls = (int)p_ls;
-          save_p_wmh = (int)p_wmh;
-          p_ls = 2;
-          p_wmh = 0;
-          last_status(false);
-        }
-        wild_menu_showing = WM_SHOWN;
-      }
-    }
-
-    // Tricky: wildmenu can be drawn either over a status line, or at empty
-    // scrolled space in the message output
-    ScreenGrid *grid = (wild_menu_showing == WM_SCROLLED)
-                        ? &msg_grid_adj : &default_grid;
-
-    grid_puts(grid, (char *)buf, row, 0, attr);
-    if (selstart != NULL && highlight) {
-      *selend = NUL;
-      grid_puts(grid, (char *)selstart, row, selstart_col, HL_ATTR(HLF_WM));
-    }
-
-    grid_fill(grid, row, row + 1, clen, Columns,
-              fillchar, fillchar, attr);
-  }
-
-  win_redraw_last_status(topframe);
-  xfree(buf);
-}
-
 /// Only call if (wp->w_vsep_width != 0).
 ///
 /// @return  true if the status line of window "wp" is connected to the status
-- 
cgit 


From 849394e4e26f487586761a3640475c27ceca30b9 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 13 Nov 2022 08:29:05 +0800
Subject: vim-patch:9.0.0863: col() and charcol() only work for the current
 window (#21038)

Problem:    col() and charcol() only work for the current window.
Solution:   Add an optional winid argument. (Yegappan Lakshmanan,
            closes vim/vim#11466, closes vim/vim#11461)

https://github.com/vim/vim/commit/4c8d2f02b3ce037bbe1d5ee12887e343c6bde88f

Cherry-pick test_functions.vim change from patch 8.2.0633.

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/eval.lua                     |  4 ++--
 src/nvim/eval/funcs.c                 | 29 ++++++++++++++++++++++++++++-
 src/nvim/eval/typval.c                | 13 +++++++++++++
 src/nvim/testdir/test_cursor_func.vim | 24 ++++++++++++++++++++++--
 src/nvim/testdir/test_functions.vim   | 12 ++++++++++++
 5 files changed, 77 insertions(+), 5 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 8bfa6797d0..dd30f51eb4 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -73,12 +73,12 @@ return {
     chansend={args=2},
     char2nr={args={1, 2}, base=1},
     charclass={args=1, base=1},
-    charcol={args=1, base=1},
+    charcol={args={1, 2}, base=1},
     charidx={args={2, 3}, base=1},
     chdir={args=1, base=1},
     cindent={args=1, base=1},
     clearmatches={args={0, 1}, base=1},
-    col={args=1, base=1},
+    col={args={1, 2}, base=1},
     complete={args=2, base=2},
     complete_add={args=1, base=1},
     complete_check={},
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index be48dc7732..8acdc3256c 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -846,9 +846,32 @@ static void f_char2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 ///          otherwise the byte index of the column.
 static void get_col(typval_T *argvars, typval_T *rettv, bool charcol)
 {
+  if (tv_check_for_string_or_list_arg(argvars, 0) == FAIL
+      || tv_check_for_opt_number_arg(argvars, 1) == FAIL) {
+    return;
+  }
+
+  switchwin_T switchwin;
+  bool winchanged = false;
+
+  if (argvars[1].v_type != VAR_UNKNOWN) {
+    // use the window specified in the second argument
+    tabpage_T *tp;
+    win_T *wp = win_id2wp_tp((int)tv_get_number(&argvars[1]), &tp);
+    if (wp == NULL || tp == NULL) {
+      return;
+    }
+
+    if (switch_win_noblock(&switchwin, wp, tp, true) != OK) {
+      return;
+    }
+
+    check_cursor();
+    winchanged = true;
+  }
+
   colnr_T col = 0;
   int fnum = curbuf->b_fnum;
-
   pos_T *fp = var2fpos(&argvars[0], false, &fnum, charcol);
   if (fp != NULL && fnum == curbuf->b_fnum) {
     if (fp->col == MAXCOL) {
@@ -876,6 +899,10 @@ static void get_col(typval_T *argvars, typval_T *rettv, bool charcol)
     }
   }
   rettv->vval.v_number = col;
+
+  if (winchanged) {
+    restore_win_noblock(&switchwin, true);
+  }
 }
 
 /// "charcol()" function
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index e09fda173b..7e4066adb7 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -46,6 +46,8 @@ static char e_non_empty_string_required_for_argument_nr[]
   = N_("E1175: Non-empty string required for argument %d");
 static char e_number_required_for_argument_nr[]
   = N_("E1210: Number required for argument %d");
+static char e_string_or_list_required_for_argument_nr[]
+  = N_("E1222: String or List required for argument %d");
 
 bool tv_in_free_unref_items = false;
 
@@ -3880,6 +3882,17 @@ int tv_check_for_opt_number_arg(const typval_T *const args, const int idx)
           || tv_check_for_number_arg(args, idx) != FAIL) ? OK : FAIL;
 }
 
+/// Give an error and return FAIL unless "args[idx]" is a string or a list.
+int tv_check_for_string_or_list_arg(const typval_T *const args, const int idx)
+  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+  if (args[idx].v_type != VAR_STRING && args[idx].v_type != VAR_LIST) {
+    semsg(_(e_string_or_list_required_for_argument_nr), idx + 1);
+    return FAIL;
+  }
+  return OK;
+}
+
 /// Get the string value of a "stringish" VimL object.
 ///
 /// @param[in]  tv  Object to get value of.
diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim
index 9801a45915..7f9e74e94b 100644
--- a/src/nvim/testdir/test_cursor_func.vim
+++ b/src/nvim/testdir/test_cursor_func.vim
@@ -241,8 +241,9 @@ endfunc
 
 " Test for the charcol() function
 func Test_charcol()
-  call assert_fails('call charcol({})', 'E731:')
-  call assert_equal(0, charcol(0))
+  call assert_fails('call charcol({})', 'E1222:')
+  call assert_fails('call charcol(".", [])', 'E1210:')
+  call assert_fails('call charcol(0)', 'E1222:')
   new
   call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
 
@@ -298,6 +299,25 @@ func Test_charcol()
   call assert_equal([1, 10, 2, 10, 7], g:InsertCurrentCol)
   iunmap 
 
+  " Test for getting the column number in another window.
+  let winid = win_getid()
+  new
+  call win_execute(winid, 'normal 1G')
+  call assert_equal(1, charcol('.', winid))
+  call assert_equal(1, charcol('$', winid))
+  call win_execute(winid, 'normal 2G6l')
+  call assert_equal(7, charcol('.', winid))
+  call assert_equal(10, charcol('$', winid))
+
+  " calling from another tab page also works
+  tabnew
+  call assert_equal(7, charcol('.', winid))
+  call assert_equal(10, charcol('$', winid))
+  tabclose
+
+  " unknown window ID
+  call assert_equal(0, charcol('.', 10001))
+
   %bw!
 endfunc
 
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index aaef01bdf5..5ff544ab55 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -1288,6 +1288,9 @@ func Test_col()
   call assert_equal(0, col([2, '$']))
   call assert_equal(0, col([1, 100]))
   call assert_equal(0, col([1]))
+  call assert_equal(0, col(v:_null_list))
+  call assert_fails('let c = col({})', 'E1222:')
+  call assert_fails('let c = col(".", [])', 'E1210:')
 
   " test for getting the visual start column
   func T()
@@ -1317,6 +1320,15 @@ func Test_col()
   call assert_equal(4, col('.'))
   set virtualedit&
 
+  " Test for getting the column number in another window
+  let winid = win_getid()
+  new
+  call win_execute(winid, 'normal 1G$')
+  call assert_equal(3, col('.', winid))
+  call win_execute(winid, 'normal 2G')
+  call assert_equal(8, col('$', winid))
+  call assert_equal(0, col('.', 5001))
+
   bw!
 endfunc
 
-- 
cgit 


From 9d7dc5062877bd7e035f1f7a74e2462c2e942864 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 13 Nov 2022 08:35:15 +0800
Subject: vim-patch:9.0.0865: duplicate arguments are not always detected
 (#21036)

Problem:    Duplicate arguments are not always detected.
Solution:   Expand to full path before comparing arguments. (Nir Lichtman,
            closes vim/vim#11505, closes vim/vim#9402)

https://github.com/vim/vim/commit/b3052aa1b555ab5a81b1459a4972290381b0e7e4

Co-authored-by: Nir Lichtman 
---
 src/nvim/arglist.c                | 13 ++++++++++++-
 src/nvim/testdir/test_arglist.vim | 10 ++++++++++
 2 files changed, 22 insertions(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c
index c7af1a71be..bd49bc8a36 100644
--- a/src/nvim/arglist.c
+++ b/src/nvim/arglist.c
@@ -693,8 +693,17 @@ void ex_next(exarg_T *eap)
 void ex_argdedupe(exarg_T *eap FUNC_ATTR_UNUSED)
 {
   for (int i = 0; i < ARGCOUNT; i++) {
+    // Expand each argument to a full path to catch different paths leading
+    // to the same file.
+    char *firstFullname = FullName_save(ARGLIST[i].ae_fname, false);
+
     for (int j = i + 1; j < ARGCOUNT; j++) {
-      if (path_fnamecmp(ARGLIST[i].ae_fname, ARGLIST[j].ae_fname) == 0) {
+      char *secondFullname = FullName_save(ARGLIST[j].ae_fname, false);
+      bool areNamesDuplicate = path_fnamecmp(firstFullname, secondFullname) == 0;
+      xfree(secondFullname);
+
+      if (areNamesDuplicate) {
+        // remove one duplicate argument
         xfree(ARGLIST[j].ae_fname);
         memmove(ARGLIST + j, ARGLIST + j + 1,
                 (size_t)(ARGCOUNT - j - 1) * sizeof(aentry_T));
@@ -709,6 +718,8 @@ void ex_argdedupe(exarg_T *eap FUNC_ATTR_UNUSED)
         j--;
       }
     }
+
+    xfree(firstFullname);
   }
 }
 
diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim
index cae71e10f3..fb8b17cd16 100644
--- a/src/nvim/testdir/test_arglist.vim
+++ b/src/nvim/testdir/test_arglist.vim
@@ -418,15 +418,19 @@ func Test_argdedupe()
   call Reset_arglist()
   argdedupe
   call assert_equal([], argv())
+
   args a a a aa b b a b aa
   argdedupe
   call assert_equal(['a', 'aa', 'b'], argv())
+
   args a b c
   argdedupe
   call assert_equal(['a', 'b', 'c'], argv())
+
   args a
   argdedupe
   call assert_equal(['a'], argv())
+
   args a A b B
   argdedupe
   if has('fname_case')
@@ -434,11 +438,17 @@ func Test_argdedupe()
   else
     call assert_equal(['a', 'b'], argv())
   endif
+
   args a b a c a b
   last
   argdedupe
   next
   call assert_equal('c', expand('%:t'))
+
+  args a ./a
+  argdedupe
+  call assert_equal(['a'], argv())
+
   %argd
 endfunc
 
-- 
cgit 


From d7e7578ada343eab090643eb698bd146d5bbfd27 Mon Sep 17 00:00:00 2001
From: bfredl 
Date: Sat, 12 Nov 2022 16:41:36 +0100
Subject: fix(ui): fix some cases of stale highlight definitions

fixes #20695
---
 src/nvim/decoration_provider.c | 19 +++++++++++++++++++
 src/nvim/highlight_group.c     |  2 ++
 2 files changed, 21 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c
index 48664421a3..f356efffdc 100644
--- a/src/nvim/decoration_provider.c
+++ b/src/nvim/decoration_provider.c
@@ -194,6 +194,25 @@ void decor_providers_invoke_end(DecorProviders *providers, char **err)
   }
 }
 
+/// Mark all cached state of per-namespace highlights as invalid. Revalidate
+/// current namespace.
+///
+/// Expensive! Should on be called by an already throttled validity check
+/// like highlight_changed() (throttled to the next redraw or mode change)
+void decor_provider_invalidate_hl(void)
+{
+  size_t len = kv_size(decor_providers);
+  for (size_t i = 0; i < len; i++) {
+    DecorProvider *item = &kv_A(decor_providers, i);
+    item->hl_cached = false;
+  }
+
+  if (ns_hl_active) {
+    ns_hl_active = -1;
+    hl_check_ns();
+  }
+}
+
 DecorProvider *get_decor_provider(NS ns_id, bool force)
 {
   assert(ns_id > 0);
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index 3c3c834c6c..65e3073303 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -2073,6 +2073,8 @@ void highlight_changed(void)
     }
   }
   highlight_ga.ga_len = hlcnt;
+
+  decor_provider_invalidate_hl();
 }
 
 /// Handle command line completion for :highlight command.
-- 
cgit 


From b433acc3c91a1575fd3cf20086bf4429b0494524 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 14 Nov 2022 07:16:42 +0800
Subject: vim-patch:9.0.0872: code is indented more than needed (#21046)

Problem:    Code is indented more than needed.
Solution:   Return early. (Yegappan Lakshmanan, closes vim/vim#11538)

https://github.com/vim/vim/commit/623e94e13810e109c6aa201bcf3a8278429502f3

Only port the first change to init_history() as Nvim has refactored it.

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/cmdhist.c | 179 ++++++++++++++++++++++++++++-------------------------
 1 file changed, 94 insertions(+), 85 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/cmdhist.c b/src/nvim/cmdhist.c
index b080b817a7..5f80c29d72 100644
--- a/src/nvim/cmdhist.c
+++ b/src/nvim/cmdhist.c
@@ -116,56 +116,58 @@ void init_history(void)
   int newlen = (int)p_hi;
   int oldlen = hislen;
 
+  if (newlen == oldlen) {  // history length didn't change
+    return;
+  }
+
   // If history tables size changed, reallocate them.
   // Tables are circular arrays (current position marked by hisidx[type]).
   // On copying them to the new arrays, we take the chance to reorder them.
-  if (newlen != oldlen) {
-    for (int type = 0; type < HIST_COUNT; type++) {
-      histentry_T *temp = (newlen
-                           ? xmalloc((size_t)newlen * sizeof(*temp))
-                           : NULL);
-
-      int j = hisidx[type];
-      if (j >= 0) {
-        // old array gets partitioned this way:
-        // [0       , i1     ) --> newest entries to be deleted
-        // [i1      , i1 + l1) --> newest entries to be copied
-        // [i1 + l1 , i2     ) --> oldest entries to be deleted
-        // [i2      , i2 + l2) --> oldest entries to be copied
-        int l1 = MIN(j + 1, newlen);             // how many newest to copy
-        int l2 = MIN(newlen, oldlen) - l1;       // how many oldest to copy
-        int i1 = j + 1 - l1;                     // copy newest from here
-        int i2 = MAX(l1, oldlen - newlen + l1);  // copy oldest from here
-
-        // copy as much entries as they fit to new table, reordering them
-        if (newlen) {
-          // copy oldest entries
-          memcpy(&temp[0], &history[type][i2], (size_t)l2 * sizeof(*temp));
-          // copy newest entries
-          memcpy(&temp[l2], &history[type][i1], (size_t)l1 * sizeof(*temp));
-        }
-
-        // delete entries that don't fit in newlen, if any
-        for (int i = 0; i < i1; i++) {
-          hist_free_entry(history[type] + i);
-        }
-        for (int i = i1 + l1; i < i2; i++) {
-          hist_free_entry(history[type] + i);
-        }
+  for (int type = 0; type < HIST_COUNT; type++) {
+    histentry_T *temp = (newlen
+                         ? xmalloc((size_t)newlen * sizeof(*temp))
+                         : NULL);
+
+    int j = hisidx[type];
+    if (j >= 0) {
+      // old array gets partitioned this way:
+      // [0       , i1     ) --> newest entries to be deleted
+      // [i1      , i1 + l1) --> newest entries to be copied
+      // [i1 + l1 , i2     ) --> oldest entries to be deleted
+      // [i2      , i2 + l2) --> oldest entries to be copied
+      int l1 = MIN(j + 1, newlen);             // how many newest to copy
+      int l2 = MIN(newlen, oldlen) - l1;       // how many oldest to copy
+      int i1 = j + 1 - l1;                     // copy newest from here
+      int i2 = MAX(l1, oldlen - newlen + l1);  // copy oldest from here
+
+      // copy as much entries as they fit to new table, reordering them
+      if (newlen) {
+        // copy oldest entries
+        memcpy(&temp[0], &history[type][i2], (size_t)l2 * sizeof(*temp));
+        // copy newest entries
+        memcpy(&temp[l2], &history[type][i1], (size_t)l1 * sizeof(*temp));
       }
 
-      // clear remaining space, if any
-      int l3 = j < 0 ? 0 : MIN(newlen, oldlen);  // number of copied entries
-      if (newlen) {
-        memset(temp + l3, 0, (size_t)(newlen - l3) * sizeof(*temp));
+      // delete entries that don't fit in newlen, if any
+      for (int i = 0; i < i1; i++) {
+        hist_free_entry(history[type] + i);
       }
+      for (int i = i1 + l1; i < i2; i++) {
+        hist_free_entry(history[type] + i);
+      }
+    }
 
-      hisidx[type] = l3 - 1;
-      xfree(history[type]);
-      history[type] = temp;
+    // clear remaining space, if any
+    int l3 = j < 0 ? 0 : MIN(newlen, oldlen);  // number of copied entries
+    if (newlen) {
+      memset(temp + l3, 0, (size_t)(newlen - l3) * sizeof(*temp));
     }
-    hislen = newlen;
+
+    hisidx[type] = l3 - 1;
+    xfree(history[type]);
+    history[type] = temp;
   }
+  hislen = newlen;
 }
 
 static inline void hist_free_entry(histentry_T *hisptr)
@@ -215,24 +217,25 @@ static int in_history(int type, char *str, int move_to_front, int sep)
     }
   } while (i != hisidx[type]);
 
-  if (last_i >= 0) {
-    list_T *const list = history[type][i].additional_elements;
-    str = history[type][i].hisstr;
-    while (i != hisidx[type]) {
-      if (++i >= hislen) {
-        i = 0;
-      }
-      history[type][last_i] = history[type][i];
-      last_i = i;
+  if (last_i < 0) {
+    return false;
+  }
+
+  list_T *const list = history[type][i].additional_elements;
+  str = history[type][i].hisstr;
+  while (i != hisidx[type]) {
+    if (++i >= hislen) {
+      i = 0;
     }
-    tv_list_unref(list);
-    history[type][i].hisnum = ++hisnum[type];
-    history[type][i].hisstr = str;
-    history[type][i].timestamp = os_time();
-    history[type][i].additional_elements = NULL;
-    return true;
-  }
-  return false;
+    history[type][last_i] = history[type][i];
+    last_i = i;
+  }
+  tv_list_unref(list);
+  history[type][i].hisnum = ++hisnum[type];
+  history[type][i].hisstr = str;
+  history[type][i].timestamp = os_time();
+  history[type][i].additional_elements = NULL;
+  return true;
 }
 
 /// Convert history name to its HIST_ equivalent
@@ -304,24 +307,27 @@ void add_to_history(int histype, char *new_entry, int in_map, int sep)
     }
     last_maptick = -1;
   }
-  if (!in_history(histype, new_entry, true, sep)) {
-    if (++hisidx[histype] == hislen) {
-      hisidx[histype] = 0;
-    }
-    hisptr = &history[histype][hisidx[histype]];
-    hist_free_entry(hisptr);
-
-    // Store the separator after the NUL of the string.
-    size_t len = strlen(new_entry);
-    hisptr->hisstr = xstrnsave(new_entry, len + 2);
-    hisptr->timestamp = os_time();
-    hisptr->additional_elements = NULL;
-    hisptr->hisstr[len + 1] = (char)sep;
-
-    hisptr->hisnum = ++hisnum[histype];
-    if (histype == HIST_SEARCH && in_map) {
-      last_maptick = maptick;
-    }
+
+  if (in_history(histype, new_entry, true, sep)) {
+    return;
+  }
+
+  if (++hisidx[histype] == hislen) {
+    hisidx[histype] = 0;
+  }
+  hisptr = &history[histype][hisidx[histype]];
+  hist_free_entry(hisptr);
+
+  // Store the separator after the NUL of the string.
+  size_t len = strlen(new_entry);
+  hisptr->hisstr = xstrnsave(new_entry, len + 2);
+  hisptr->timestamp = os_time();
+  hisptr->additional_elements = NULL;
+  hisptr->hisstr[len + 1] = (char)sep;
+
+  hisptr->hisnum = ++hisnum[histype];
+  if (histype == HIST_SEARCH && in_map) {
+    last_maptick = maptick;
   }
 }
 
@@ -504,16 +510,19 @@ void f_histadd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   }
   const char *str = tv_get_string_chk(&argvars[0]);  // NULL on type error
   histype = str != NULL ? get_histtype(str, strlen(str), false) : HIST_INVALID;
-  if (histype != HIST_INVALID) {
-    char buf[NUMBUFLEN];
-    str = tv_get_string_buf(&argvars[1], buf);
-    if (*str != NUL) {
-      init_history();
-      add_to_history(histype, (char *)str, false, NUL);
-      rettv->vval.v_number = true;
-      return;
-    }
+  if (histype == HIST_INVALID) {
+    return;
   }
+
+  char buf[NUMBUFLEN];
+  str = tv_get_string_buf(&argvars[1], buf);
+  if (*str == NUL) {
+    return;
+  }
+
+  init_history();
+  add_to_history(histype, (char *)str, false, NUL);
+  rettv->vval.v_number = true;
 }
 
 /// "histdel()" function
-- 
cgit 


From f2695919bbc4ffea894a54c1d48632ebf0d543a9 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 14 Nov 2022 08:16:29 +0800
Subject: test(old): add missing lines from Vim patch 8.2.0522 (#21048)

---
 src/nvim/testdir/test_highlight.vim | 10 ++++++++++
 1 file changed, 10 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim
index 2be82f4e3c..d618637d63 100644
--- a/src/nvim/testdir/test_highlight.vim
+++ b/src/nvim/testdir/test_highlight.vim
@@ -741,8 +741,18 @@ endfunc
 " Test for :highlight command errors
 func Test_highlight_cmd_errors()
   if has('gui_running') || has('nvim')
+    " This test doesn't fail in the MS-Windows console version.
+    call assert_fails('hi Xcomment ctermfg=fg', 'E419:')
+    call assert_fails('hi Xcomment ctermfg=bg', 'E420:')
     call assert_fails('hi ' .. repeat('a', 201) .. ' ctermfg=black', 'E1249:')
   endif
+
+  " Try using a very long terminal code. Define a dummy terminal code for this
+  " test.
+  let &t_fo = "\1;"
+  let c = repeat("t_fo,", 100) . "t_fo"
+  " call assert_fails('exe "hi Xgroup1 start=" . c', 'E422:')
+  let &t_fo = ""
 endfunc
 
 " Test for using RGB color values in a highlight group
-- 
cgit 


From 6d996f78ef7e2f4baf19bc2ca6abce33305e2915 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 14 Nov 2022 11:23:33 +0800
Subject: vim-patch:8.2.4001: insert complete code uses global variables

Problem:    Insert complete code uses global variables.
Solution:   Make variables local to the file and use accessor functions.
            (Yegappan Lakshmanan, closes vim/vim#9470)

https://github.com/vim/vim/commit/d94fbfc74a8b8073e7a256c95fa6f39fc527c726

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/insexpand.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index 91ac1d5e8c..bc10785152 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -242,7 +242,6 @@ static expand_T compl_xp;
 
 // List of flags for method of completion.
 static int compl_cont_status = 0;
-
 #define CONT_ADDING    1        ///< "normal" or "adding" expansion
 #define CONT_INTRPT    (2 + 4)  ///< a ^X interrupted the current expansion
                                 ///< it's set only iff N_ADDS is set
@@ -422,7 +421,7 @@ void compl_status_clear(void)
   compl_cont_status = 0;
 }
 
-// @return  true if completion is using the forward direction matches
+/// @return  true if completion is using the forward direction matches
 static bool compl_dir_forward(void)
 {
   return compl_direction == FORWARD;
@@ -3297,7 +3296,7 @@ static int ins_compl_get_exp(pos_T *ini)
   assert(st.ins_buf != NULL);
 
   compl_old_match = compl_curr_match;   // remember the last current match
-  st.cur_match_pos = (compl_dir_forward() ? &st.last_match_pos : &st.first_match_pos);
+  st.cur_match_pos = compl_dir_forward() ? &st.last_match_pos : &st.first_match_pos;
 
   // For ^N/^P loop over all the flags/windows/buffers in 'complete'
   for (;;) {
-- 
cgit 


From c4fcde5063899ebfbffef266ba75eafe935da593 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 14 Nov 2022 12:10:26 +0800
Subject: vim-patch:8.2.4038: various code not used when features are disabled
 (#21049)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Problem:    Various code not used when features are disabled.
Solution:   Add #ifdefs. (Dominique Pellé, closes vim/vim#9491)

https://github.com/vim/vim/commit/748b308eebe8d8860888eb27da08333f175d547d

N/A patches for version.c:

vim-patch:8.2.2186: Vim9: error when using 'opfunc'

Problem:    Vim9: error when using 'opfunc'.
Solution:   Do not expect a return value from 'opfunc'. (closes vim/vim#7510)

https://github.com/vim/vim/commit/5b3d1bb0f5180266c4de4d815b3ea856a2fb3519
---
 src/nvim/eval.c        | 17 ++++++++++-------
 src/nvim/option_defs.h |  2 --
 2 files changed, 10 insertions(+), 9 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index f5f8840350..89569e9714 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -1075,11 +1075,11 @@ int get_spellword(list_T *const list, const char **ret_word)
   return (int)tv_list_find_nr(list, -1, NULL);
 }
 
-// Call some vim script function and return the result in "*rettv".
-// Uses argv[0] to argv[argc-1] for the function arguments. argv[argc]
-// should have type VAR_UNKNOWN.
-//
-// @return  OK or FAIL.
+/// Call some Vim script function and return the result in "*rettv".
+/// Uses argv[0] to argv[argc - 1] for the function arguments. argv[argc]
+/// should have type VAR_UNKNOWN.
+///
+/// @return  OK or FAIL.
 int call_vim_function(const char *func, int argc, typval_T *argv, typval_T *rettv)
   FUNC_ATTR_NONNULL_ALL
 {
@@ -1113,7 +1113,9 @@ fail:
   return ret;
 }
 
-/// Call Vim script function and return the result as a string
+/// Call Vim script function and return the result as a string.
+/// Uses "argv[0]" to "argv[argc - 1]" for the function arguments. "argv[argc]"
+/// should have type VAR_UNKNOWN.
 ///
 /// @param[in]  func  Function name.
 /// @param[in]  argc  Number of arguments.
@@ -1136,7 +1138,8 @@ char *call_func_retstr(const char *const func, int argc, typval_T *argv)
   return retval;
 }
 
-/// Call Vim script function and return the result as a List
+/// Call Vim script function and return the result as a List.
+/// Uses "argv" and "argc" as call_func_retstr().
 ///
 /// @param[in]  func  Function name.
 /// @param[in]  argc  Number of arguments.
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index d505e75be4..8f7e44d23d 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -596,8 +596,6 @@ EXTERN char *p_menc;            // 'makeencoding'
 EXTERN char *p_mef;             // 'makeef'
 EXTERN char_u *p_mp;            // 'makeprg'
 EXTERN char *p_mps;             ///< 'matchpairs'
-EXTERN char_u *p_cc;            // 'colorcolumn'
-EXTERN int p_cc_cols[256];      // array for 'colorcolumn' columns
 EXTERN long p_mat;              // 'matchtime'
 EXTERN long p_mco;              // 'maxcombine'
 EXTERN long p_mfd;              // 'maxfuncdepth'
-- 
cgit 


From 30604320072335122aea0f37890f136b2ba0e445 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 14 Nov 2022 23:18:25 +0800
Subject: vim-patch:9.0.0878: Coverity warns for dead code (#21053)

Problem:    Coverity warns for dead code.
Solution:   Remove the dead code.

https://github.com/vim/vim/commit/b298fe6cbae3b240b10dbd55d9c38d0cc8e033d3

Nvim has refactored this function and does not have the dead code.

Co-authored-by: Bram Moolenaar 
---
 src/nvim/cmdhist.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/cmdhist.c b/src/nvim/cmdhist.c
index 5f80c29d72..0ae586743f 100644
--- a/src/nvim/cmdhist.c
+++ b/src/nvim/cmdhist.c
@@ -124,7 +124,7 @@ void init_history(void)
   // Tables are circular arrays (current position marked by hisidx[type]).
   // On copying them to the new arrays, we take the chance to reorder them.
   for (int type = 0; type < HIST_COUNT; type++) {
-    histentry_T *temp = (newlen
+    histentry_T *temp = (newlen > 0
                          ? xmalloc((size_t)newlen * sizeof(*temp))
                          : NULL);
 
@@ -159,7 +159,7 @@ void init_history(void)
 
     // clear remaining space, if any
     int l3 = j < 0 ? 0 : MIN(newlen, oldlen);  // number of copied entries
-    if (newlen) {
+    if (newlen > 0) {
       memset(temp + l3, 0, (size_t)(newlen - l3) * sizeof(*temp));
     }
 
-- 
cgit 


From f8c671827710c6e9cca3bfd60c32098b2be8239a Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Mon, 14 Nov 2022 18:04:36 +0000
Subject: feat(lua-api): avoid unnecessary allocations (#19877)

Lua makes (or reuses) an internal copy of strings, so we can safely push
buf pointers onto the stack.
---
 src/nvim/api/buffer.c                    | 131 ++++++++++++++++++++++++++-----
 src/nvim/api/buffer.h                    |   3 +
 src/nvim/api/deprecated.c                |   4 +-
 src/nvim/api/options.c                   |  11 ++-
 src/nvim/api/private/helpers.c           |  55 +++----------
 src/nvim/buffer_updates.c                |  15 ++--
 src/nvim/generators/c_grammar.lua        |   1 +
 src/nvim/generators/gen_api_dispatch.lua |  34 +++++++-
 src/nvim/option.c                        |   2 +-
 9 files changed, 171 insertions(+), 85 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 51fedb302a..29c2ed6028 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -271,6 +271,7 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id,
                                    Integer start,
                                    Integer end,
                                    Boolean strict_indexing,
+                                   lua_State *lstate,
                                    Error *err)
   FUNC_API_SINCE(1)
 {
@@ -300,21 +301,18 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id,
     return rv;
   }
 
-  rv.size = (size_t)(end - start);
-  rv.items = xcalloc(rv.size, sizeof(Object));
+  size_t size = (size_t)(end - start);
 
-  if (!buf_collect_lines(buf, rv.size, start,
-                         (channel_id != VIML_INTERNAL_CALL), &rv, err)) {
+  init_line_array(lstate, &rv, size);
+
+  if (!buf_collect_lines(buf, size, (linenr_T)start, (channel_id != VIML_INTERNAL_CALL), &rv,
+                         lstate, err)) {
     goto end;
   }
 
 end:
   if (ERROR_SET(err)) {
-    for (size_t i = 0; i < rv.size; i++) {
-      xfree(rv.items[i].data.string.data);
-    }
-
-    xfree(rv.items);
+    api_free_array(rv);
     rv.items = NULL;
   }
 
@@ -790,7 +788,8 @@ early_end:
 ArrayOf(String) nvim_buf_get_text(uint64_t channel_id, Buffer buffer,
                                   Integer start_row, Integer start_col,
                                   Integer end_row, Integer end_col,
-                                  Dictionary opts, Error *err)
+                                  Dictionary opts, lua_State *lstate,
+                                  Error *err)
   FUNC_API_SINCE(9)
 {
   Array rv = ARRAY_DICT_INIT;
@@ -830,33 +829,38 @@ ArrayOf(String) nvim_buf_get_text(uint64_t channel_id, Buffer buffer,
 
   bool replace_nl = (channel_id != VIML_INTERNAL_CALL);
 
+  size_t size = (size_t)(end_row - start_row) + 1;
+
+  init_line_array(lstate, &rv, size);
+
   if (start_row == end_row) {
-    String line = buf_get_text(buf, start_row, start_col, end_col, replace_nl, err);
+    String line = buf_get_text(buf, start_row, start_col, end_col, err);
     if (ERROR_SET(err)) {
-      return rv;
+      goto end;
     }
-
-    ADD(rv, STRING_OBJ(line));
+    push_linestr(lstate, &rv, line.data, line.size, 0, replace_nl);
     return rv;
   }
 
-  rv.size = (size_t)(end_row - start_row) + 1;
-  rv.items = xcalloc(rv.size, sizeof(Object));
+  String str = buf_get_text(buf, start_row, start_col, MAXCOL - 1, err);
+
+  push_linestr(lstate, &rv, str.data, str.size, 0, replace_nl);
 
-  rv.items[0] = STRING_OBJ(buf_get_text(buf, start_row, start_col, MAXCOL - 1, replace_nl, err));
   if (ERROR_SET(err)) {
     goto end;
   }
 
-  if (rv.size > 2) {
+  if (size > 2) {
     Array tmp = ARRAY_DICT_INIT;
     tmp.items = &rv.items[1];
-    if (!buf_collect_lines(buf, rv.size - 2, start_row + 1, replace_nl, &tmp, err)) {
+    if (!buf_collect_lines(buf, size - 2, (linenr_T)start_row + 1, replace_nl, &tmp, lstate, err)) {
       goto end;
     }
   }
 
-  rv.items[rv.size - 1] = STRING_OBJ(buf_get_text(buf, end_row, 0, end_col, replace_nl, err));
+  str = buf_get_text(buf, end_row, 0, end_col, err);
+  push_linestr(lstate, &rv, str.data, str.size, (int)(size - 1), replace_nl);
+
   if (ERROR_SET(err)) {
     goto end;
   }
@@ -1390,3 +1394,90 @@ static int64_t normalize_index(buf_T *buf, int64_t index, bool end_exclusive, bo
   index++;
   return index;
 }
+
+/// Initialise a string array either:
+/// - on the Lua stack (as a table) (if lstate is not NULL)
+/// - as an API array object (if lstate is NULL).
+///
+/// @param lstate  Lua state. When NULL the Array is initialized instead.
+/// @param a       Array to initialize
+/// @param size    Size of array
+static inline void init_line_array(lua_State *lstate, Array *a, size_t size)
+{
+  if (lstate) {
+    lua_createtable(lstate, (int)size, 0);
+  } else {
+    a->size = size;
+    a->items = xcalloc(a->size, sizeof(Object));
+  }
+}
+
+/// Push a string onto either the Lua stack (as a table element) or an API array object.
+///
+/// For Lua, a table of the correct size must be created first.
+/// API array objects must be pre allocated.
+///
+/// @param lstate      Lua state. When NULL the Array is pushed to instead.
+/// @param a           Array to push onto when not using Lua
+/// @param s           String to push
+/// @param len         Size of string
+/// @param idx         0-based index to place s
+/// @param replace_nl  Replace newlines ('\n') with null ('\0')
+static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len, int idx,
+                         bool replace_nl)
+{
+  if (lstate) {
+    // Vim represents NULs as NLs
+    if (s && replace_nl && strchr(s, '\n')) {
+      char *tmp = xmemdupz(s, len);
+      strchrsub(tmp, '\n', '\0');
+      lua_pushlstring(lstate, tmp, len);
+      xfree(tmp);
+    } else {
+      lua_pushlstring(lstate, s, len);
+    }
+    lua_rawseti(lstate, -2, idx + 1);
+  } else {
+    String str = STRING_INIT;
+    if (s) {
+      str = cbuf_to_string(s, len);
+      if (replace_nl) {
+        // Vim represents NULs as NLs, but this may confuse clients.
+        strchrsub(str.data, '\n', '\0');
+      }
+    }
+
+    a->items[idx] = STRING_OBJ(str);
+  }
+}
+
+/// Collects `n` buffer lines into array `l` and/or lua_State `lstate`, optionally replacing
+/// newlines with NUL.
+///
+/// @param buf Buffer to get lines from
+/// @param n Number of lines to collect
+/// @param replace_nl Replace newlines ("\n") with NUL
+/// @param start Line number to start from
+/// @param[out] l If not NULL, Lines are copied here
+/// @param[out] lstate If not NULL, Lines are pushed into a table onto the stack
+/// @param err[out] Error, if any
+/// @return true unless `err` was set
+bool buf_collect_lines(buf_T *buf, size_t n, linenr_T start, bool replace_nl, Array *l,
+                       lua_State *lstate, Error *err)
+{
+  for (size_t i = 0; i < n; i++) {
+    linenr_T lnum = start + (linenr_T)i;
+
+    if (lnum >= MAXLNUM) {
+      if (err != NULL) {
+        api_set_error(err, kErrorTypeValidation, "Line index is too high");
+      }
+      return false;
+    }
+
+    char *bufstr = ml_get_buf(buf, lnum, false);
+    push_linestr(lstate, l, bufstr, strlen(bufstr), (int)i, replace_nl);
+  }
+
+  return true;
+}
diff --git a/src/nvim/api/buffer.h b/src/nvim/api/buffer.h
index 1c4a93a587..0814da63cd 100644
--- a/src/nvim/api/buffer.h
+++ b/src/nvim/api/buffer.h
@@ -1,7 +1,10 @@
 #ifndef NVIM_API_BUFFER_H
 #define NVIM_API_BUFFER_H
 
+#include 
+
 #include "nvim/api/private/defs.h"
+#include "nvim/buffer_defs.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "api/buffer.h.generated.h"
diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c
index abaac07755..8e1a615bbb 100644
--- a/src/nvim/api/deprecated.c
+++ b/src/nvim/api/deprecated.c
@@ -190,7 +190,7 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err)
   String rv = { .size = 0 };
 
   index = convert_index(index);
-  Array slice = nvim_buf_get_lines(0, buffer, index, index + 1, true, err);
+  Array slice = nvim_buf_get_lines(0, buffer, index, index + 1, true, NULL, err);
 
   if (!ERROR_SET(err) && slice.size) {
     rv = slice.items[0].data.string;
@@ -263,7 +263,7 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer,
 {
   start = convert_index(start) + !include_start;
   end = convert_index(end) + include_end;
-  return nvim_buf_get_lines(0, buffer, start, end, false, err);
+  return nvim_buf_get_lines(0, buffer, start, end, false, NULL, err);
 }
 
 /// Replaces a line range on the buffer
diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c
index 92f20fff48..1b04392d47 100644
--- a/src/nvim/api/options.c
+++ b/src/nvim/api/options.c
@@ -256,7 +256,7 @@ void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err)
 /// @param name     Option name
 /// @param[out] err Error details, if any
 /// @return         Option value (global)
-Object nvim_get_option(String name, Error *err)
+Object nvim_get_option(String name, Arena *arena, Error *err)
   FUNC_API_SINCE(1)
 {
   return get_option_from(NULL, SREQ_GLOBAL, name, err);
@@ -268,7 +268,7 @@ Object nvim_get_option(String name, Error *err)
 /// @param name       Option name
 /// @param[out] err   Error details, if any
 /// @return Option value
-Object nvim_buf_get_option(Buffer buffer, String name, Error *err)
+Object nvim_buf_get_option(Buffer buffer, String name, Arena *arena, Error *err)
   FUNC_API_SINCE(1)
 {
   buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -306,7 +306,7 @@ void nvim_buf_set_option(uint64_t channel_id, Buffer buffer, String name, Object
 /// @param name     Option name
 /// @param[out] err Error details, if any
 /// @return Option value
-Object nvim_win_get_option(Window window, String name, Error *err)
+Object nvim_win_get_option(Window window, String name, Arena *arena, Error *err)
   FUNC_API_SINCE(1)
 {
   win_T *win = find_window_by_handle(window, err);
@@ -346,7 +346,7 @@ void nvim_win_set_option(uint64_t channel_id, Window window, String name, Object
 /// @param name The option name
 /// @param[out] err Details of an error that may have occurred
 /// @return the option value
-Object get_option_from(void *from, int type, String name, Error *err)
+static Object get_option_from(void *from, int type, String name, Error *err)
 {
   Object rv = OBJECT_INIT;
 
@@ -358,8 +358,7 @@ Object get_option_from(void *from, int type, String name, Error *err)
   // Return values
   int64_t numval;
   char *stringval = NULL;
-  int flags = get_option_value_strict(name.data, &numval, &stringval,
-                                      type, from);
+  int flags = get_option_value_strict(name.data, &numval, &stringval, type, from);
 
   if (!flags) {
     api_set_error(err, kErrorTypeValidation, "Invalid option name: '%s'",
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 73b5489d5c..d10d17c88d 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -394,6 +394,12 @@ String cstrn_to_string(const char *str, size_t maxsize)
   return cbuf_to_string(str, STRNLEN(str, maxsize));
 }
 
+String cstrn_as_string(char *str, size_t maxsize)
+  FUNC_ATTR_NONNULL_ALL
+{
+  return cbuf_as_string(str, STRNLEN(str, maxsize));
+}
+
 /// Creates a String using the given C string. Unlike
 /// cstr_to_string this function DOES NOT copy the C string.
 ///
@@ -462,53 +468,15 @@ Array string_to_array(const String input, bool crlf)
   return ret;
 }
 
-/// Collects `n` buffer lines into array `l`, optionally replacing newlines
-/// with NUL.
-///
-/// @param buf Buffer to get lines from
-/// @param n Number of lines to collect
-/// @param replace_nl Replace newlines ("\n") with NUL
-/// @param start Line number to start from
-/// @param[out] l Lines are copied here
-/// @param err[out] Error, if any
-/// @return true unless `err` was set
-bool buf_collect_lines(buf_T *buf, size_t n, int64_t start, bool replace_nl, Array *l, Error *err)
-{
-  for (size_t i = 0; i < n; i++) {
-    int64_t lnum = start + (int64_t)i;
-
-    if (lnum >= MAXLNUM) {
-      if (err != NULL) {
-        api_set_error(err, kErrorTypeValidation, "Line index is too high");
-      }
-      return false;
-    }
-
-    const char *bufstr = ml_get_buf(buf, (linenr_T)lnum, false);
-    Object str = STRING_OBJ(cstr_to_string(bufstr));
-
-    if (replace_nl) {
-      // Vim represents NULs as NLs, but this may confuse clients.
-      strchrsub(str.data.string.data, '\n', '\0');
-    }
-
-    l->items[i] = str;
-  }
-
-  return true;
-}
-
 /// Returns a substring of a buffer line
 ///
 /// @param buf          Buffer handle
 /// @param lnum         Line number (1-based)
 /// @param start_col    Starting byte offset into line (0-based)
 /// @param end_col      Ending byte offset into line (0-based, exclusive)
-/// @param replace_nl   Replace newlines ('\n') with null ('\0')
 /// @param err          Error object
 /// @return The text between start_col and end_col on line lnum of buffer buf
-String buf_get_text(buf_T *buf, int64_t lnum, int64_t start_col, int64_t end_col, bool replace_nl,
-                    Error *err)
+String buf_get_text(buf_T *buf, int64_t lnum, int64_t start_col, int64_t end_col, Error *err)
 {
   String rv = STRING_INIT;
 
@@ -517,7 +485,7 @@ String buf_get_text(buf_T *buf, int64_t lnum, int64_t start_col, int64_t end_col
     return rv;
   }
 
-  const char *bufstr = ml_get_buf(buf, (linenr_T)lnum, false);
+  char *bufstr = ml_get_buf(buf, (linenr_T)lnum, false);
   size_t line_length = strlen(bufstr);
 
   start_col = start_col < 0 ? (int64_t)line_length + start_col + 1 : start_col;
@@ -537,12 +505,7 @@ String buf_get_text(buf_T *buf, int64_t lnum, int64_t start_col, int64_t end_col
     return rv;
   }
 
-  rv = cstrn_to_string(&bufstr[start_col], (size_t)(end_col - start_col));
-  if (replace_nl) {
-    strchrsub(rv.data, '\n', '\0');
-  }
-
-  return rv;
+  return cstrn_as_string((char *)&bufstr[start_col], (size_t)(end_col - start_col));
 }
 
 void api_free_string(String value)
diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c
index 1b3c0bc28f..681d5df047 100644
--- a/src/nvim/buffer_updates.c
+++ b/src/nvim/buffer_updates.c
@@ -1,6 +1,7 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
+#include "nvim/api/buffer.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/assert.h"
 #include "nvim/buffer.h"
@@ -34,12 +35,10 @@ bool buf_updates_register(buf_T *buf, uint64_t channel_id, BufUpdateCallbacks cb
 
   // count how many channels are currently watching the buffer
   size_t size = kv_size(buf->update_channels);
-  if (size) {
-    for (size_t i = 0; i < size; i++) {
-      if (kv_A(buf->update_channels, i) == channel_id) {
-        // buffer is already registered ... nothing to do
-        return true;
-      }
+  for (size_t i = 0; i < size; i++) {
+    if (kv_A(buf->update_channels, i) == channel_id) {
+      // buffer is already registered ... nothing to do
+      return true;
     }
   }
 
@@ -69,7 +68,7 @@ bool buf_updates_register(buf_T *buf, uint64_t channel_id, BufUpdateCallbacks cb
       linedata.size = line_count;
       linedata.items = xcalloc(line_count, sizeof(Object));
 
-      buf_collect_lines(buf, line_count, 1, true, &linedata, NULL);
+      buf_collect_lines(buf, line_count, 1, true, &linedata, NULL, NULL);
     }
 
     args.items[4] = ARRAY_OBJ(linedata);
@@ -231,7 +230,7 @@ void buf_updates_send_changes(buf_T *buf, linenr_T firstline, int64_t num_added,
       linedata.size = (size_t)num_added;
       linedata.items = xcalloc((size_t)num_added, sizeof(Object));
       buf_collect_lines(buf, (size_t)num_added, firstline, true, &linedata,
-                        NULL);
+                        NULL, NULL);
     }
     args.items[4] = ARRAY_OBJ(linedata);
     args.items[5] = BOOLEAN_OBJ(false);
diff --git a/src/nvim/generators/c_grammar.lua b/src/nvim/generators/c_grammar.lua
index d872ffd6a9..3ea5904338 100644
--- a/src/nvim/generators/c_grammar.lua
+++ b/src/nvim/generators/c_grammar.lua
@@ -27,6 +27,7 @@ local c_void = P('void')
 local c_param_type = (
   ((P('Error') * fill * P('*') * fill) * Cc('error')) +
   ((P('Arena') * fill * P('*') * fill) * Cc('arena')) +
+  ((P('lua_State') * fill * P('*') * fill) * Cc('lstate')) +
   C((P('const ') ^ -1) * (c_id) * (ws ^ 1) * P('*')) +
   (C(c_id) * (ws ^ 1))
   )
diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua
index 67b8f5f0f5..6894b7a6df 100644
--- a/src/nvim/generators/gen_api_dispatch.lua
+++ b/src/nvim/generators/gen_api_dispatch.lua
@@ -78,6 +78,10 @@ for i = 6, #arg do
         fn.arena_return = true
         fn.parameters[#fn.parameters] = nil
       end
+      if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'lstate' then
+        fn.has_lua_imp = true
+        fn.parameters[#fn.parameters] = nil
+      end
     end
   end
   input:close()
@@ -329,6 +333,14 @@ for i = 1, #functions do
         output:write(', arena')
     end
 
+    if fn.has_lua_imp then
+      if #args > 0 then
+        output:write(', NULL')
+      else
+        output:write('NULL')
+      end
+    end
+
     if fn.can_fail then
       -- if the function can fail, also pass a pointer to the local error object
       if #args > 0 then
@@ -497,6 +509,10 @@ local function process_function(fn)
     ]])
   end
 
+  if fn.has_lua_imp then
+    cparams = cparams .. 'lstate, '
+  end
+
   if fn.can_fail then
     cparams = cparams .. '&err'
   else
@@ -539,13 +555,27 @@ local function process_function(fn)
     end
     write_shifted_output(output, string.format([[
     const %s ret = %s(%s);
+    ]], fn.return_type, fn.name, cparams))
+
+    if fn.has_lua_imp then
+      -- only push onto the Lua stack if we haven't already
+      write_shifted_output(output, string.format([[
+    if (lua_gettop(lstate) == 0) {
+      nlua_push_%s(lstate, ret, true);
+    }
+      ]], return_type))
+    else
+      write_shifted_output(output, string.format([[
     nlua_push_%s(lstate, ret, true);
+      ]], return_type))
+    end
+
+    write_shifted_output(output, string.format([[
   %s
   %s
   %s
     return 1;
-    ]], fn.return_type, fn.name, cparams, return_type,
-        free_retval, free_at_exit_code, err_throw_code))
+    ]], free_retval, free_at_exit_code, err_throw_code))
   else
     write_shifted_output(output, string.format([[
     %s(%s);
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 23c4a1ccf3..330900a9d6 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -3012,7 +3012,7 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
 
   if (varp != NULL) {
     if (p->flags & P_STRING) {
-      *stringval = xstrdup(*(char **)(varp));
+      *stringval = *(char **)(varp);
     } else if (p->flags & P_NUM) {
       *numval = *(long *)varp;
     } else {
-- 
cgit 


From 2755510f7800ff675a5fbe2cfaa59459ff3ab6b2 Mon Sep 17 00:00:00 2001
From: dundargoc 
Date: Sat, 12 Nov 2022 13:34:14 +0100
Subject: ci(windows): treat compiler warnings as errors

Reduce the warning level from 3 to 1 and fix all warnings.
---
 src/nvim/ex_cmds.c           | 2 +-
 src/nvim/ex_docmd.c          | 1 +
 src/nvim/lua/executor.c      | 2 +-
 src/nvim/main.c              | 6 +++---
 src/nvim/os/pty_conpty_win.c | 4 ++--
 src/nvim/path.c              | 2 +-
 6 files changed, 9 insertions(+), 8 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index c22f22366a..eefe5cf68b 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -1512,7 +1512,7 @@ static char *find_pipe(const char *cmd)
 
   for (const char *p = cmd; *p != NUL; p++) {
     if (!inquote && *p == '|') {
-      return p;
+      return (char *)p;
     }
     if (*p == '"') {
       inquote = !inquote;
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index b875aafaad..f5e8eb1a06 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -1639,6 +1639,7 @@ int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview)
   char *errormsg = NULL;
   int retv = 0;
 
+#undef ERROR
 #define ERROR(msg) \
   do { \
     errormsg = msg; \
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 9cb42a81d3..c27df01342 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -585,7 +585,7 @@ static bool nlua_init_packages(lua_State *lstate)
   lua_getglobal(lstate, "require");
   lua_pushstring(lstate, "vim._init_packages");
   if (nlua_pcall(lstate, 1, 0)) {
-    mch_errmsg(lua_tostring(lstate, -1));
+    mch_errmsg((char *)lua_tostring(lstate, -1));
     mch_errmsg("\n");
     return false;
   }
diff --git a/src/nvim/main.c b/src/nvim/main.c
index e395f8dc78..9ee9803c6d 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -1273,7 +1273,7 @@ scripterror:
             vim_snprintf((char *)IObuff, IOSIZE,
                          _("Attempt to open script file again: \"%s %s\"\n"),
                          argv[-1], argv[0]);
-            mch_errmsg((const char *)IObuff);
+            mch_errmsg(IObuff);
             os_exit(2);
           }
           int error;
@@ -1292,7 +1292,7 @@ scripterror:
             vim_snprintf((char *)IObuff, IOSIZE,
                          _("Cannot open for reading: \"%s\": %s\n"),
                          argv[0], os_strerror(error));
-            mch_errmsg((const char *)IObuff);
+            mch_errmsg(IObuff);
             os_exit(2);
           }
           save_typebuf();
@@ -2055,7 +2055,7 @@ static void mainerr(const char *errstr, const char *str)
   mch_errmsg(_(errstr));
   if (str != NULL) {
     mch_errmsg(": \"");
-    mch_errmsg(str);
+    mch_errmsg((char *)str);
     mch_errmsg("\"");
   }
   mch_errmsg(_("\nMore info with \""));
diff --git a/src/nvim/os/pty_conpty_win.c b/src/nvim/os/pty_conpty_win.c
index f9478d951f..43c89f8865 100644
--- a/src/nvim/os/pty_conpty_win.c
+++ b/src/nvim/os/pty_conpty_win.c
@@ -67,7 +67,7 @@ conpty_t *os_conpty_init(char **in_name, char **out_name, uint16_t width, uint16
                      | PIPE_ACCESS_OUTBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE;
 
   sa.nLength = sizeof(sa);
-  snprintf(buf, sizeof(buf), "\\\\.\\pipe\\nvim-term-in-%d-%d",
+  snprintf(buf, sizeof(buf), "\\\\.\\pipe\\nvim-term-in-%lld-%d",
            os_get_pid(), count);
   *in_name = xstrdup(buf);
   if ((in_read = CreateNamedPipeA(*in_name,
@@ -81,7 +81,7 @@ conpty_t *os_conpty_init(char **in_name, char **out_name, uint16_t width, uint16
     emsg = "create input pipe failed";
     goto failed;
   }
-  snprintf(buf, sizeof(buf), "\\\\.\\pipe\\nvim-term-out-%d-%d",
+  snprintf(buf, sizeof(buf), "\\\\.\\pipe\\nvim-term-out-%lld-%d",
            os_get_pid(), count);
   *out_name = xstrdup(buf);
   if ((out_write = CreateNamedPipeA(*out_name,
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 1413000680..007f55294b 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -1867,7 +1867,7 @@ char *fix_fname(const char *fname)
   fname = xstrdup(fname);
 
 # ifdef USE_FNAME_CASE
-  path_fix_case(fname);  // set correct case for file name
+  path_fix_case((char *)fname);  // set correct case for file name
 # endif
 
   return (char *)fname;
-- 
cgit 


From 2685d27cd65ea255f9bf1fce44a355e235016212 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Tue, 15 Nov 2022 07:23:57 +0800
Subject: vim-patch:9.0.0883: a silent mapping may cause dots on the command
 line (#21061)

Problem:    A silent mapping may cause dots on the command line.
Solution:   Don't show dots for completion if they are not going to be removed
            again. (closes vim/vim#11501)

https://github.com/vim/vim/commit/698a00f55d60043d51b1c98cbbf3f9fd10badd2f

Co-authored-by: Bram Moolenaar 
---
 src/nvim/cmdexpand.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index ef742525da..e0bf562bb8 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -173,7 +173,9 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
     return FAIL;
   }
 
-  if (!(ui_has(kUICmdline) || ui_has(kUIWildmenu))) {
+  // If cmd_silent is set then don't show the dots, because redrawcmd() below
+  // won't remove them.
+  if (!cmd_silent && !(ui_has(kUICmdline) || ui_has(kUIWildmenu))) {
     msg_puts("...");  // show that we are busy
     ui_flush();
   }
-- 
cgit 


From 74399738510cb880507cba14f71acc0a96e14327 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Tue, 15 Nov 2022 07:58:22 +0800
Subject: vim-patch:9.0.0882: using freed memory after SpellFileMissing autocmd
 uses bwipe (#21060)

Problem:    Using freed memory after SpellFileMissing autocmd uses bwipe.
Solution:   Bail out if the window no longer exists.

https://github.com/vim/vim/commit/c3d27ada14acd02db357f2d16347acc22cb17e93

Co-authored-by: Bram Moolenaar 
---
 src/nvim/spell.c                |  5 +++--
 src/nvim/testdir/test_spell.vim | 13 +++++++++++++
 2 files changed, 16 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index b1daf4ed23..b9ea7557c4 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -110,6 +110,7 @@
 #include "nvim/types.h"           // for char_u
 #include "nvim/undo.h"            // for u_save_cursor
 #include "nvim/vim.h"             // for curwin, strlen, STRLCPY, STRNCMP
+#include "nvim/window.h"          // for win_valid_any_tab
 
 // Result values.  Lower number is accepted over higher one.
 enum {
@@ -1965,8 +1966,8 @@ char *did_set_spelllang(win_T *wp)
       } else {
         spell_load_lang((char_u *)lang);
         // SpellFileMissing autocommands may do anything, including
-        // destroying the buffer we are using...
-        if (!bufref_valid(&bufref)) {
+        // destroying the buffer we are using or closing the window.
+        if (!bufref_valid(&bufref) || !win_valid_any_tab(wp)) {
           ret_msg = N_("E797: SpellFileMissing autocommand deleted buffer");
           goto theend;
         }
diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim
index ea18fc5194..a919156066 100644
--- a/src/nvim/testdir/test_spell.vim
+++ b/src/nvim/testdir/test_spell.vim
@@ -159,6 +159,19 @@ func Test_spell_file_missing()
   %bwipe!
 endfunc
 
+func Test_spell_file_missing_bwipe()
+  " this was using a window that was wiped out in a SpellFileMissing autocmd
+  set spelllang=xy
+  au SpellFileMissing * n0
+  set spell
+  au SpellFileMissing * bw
+  snext somefile
+
+  au! SpellFileMissing
+  bwipe!
+  set nospell spelllang=en
+endfunc
+
 func Test_spelldump()
   " In case the spell file is not found avoid getting the download dialog, we
   " would get stuck at the prompt.
-- 
cgit 


From 66360675cf4d091b7460e4a8e1435c13216c1929 Mon Sep 17 00:00:00 2001
From: dundargoc 
Date: Sun, 11 Sep 2022 17:12:44 +0200
Subject: build: allow IWYU to fix includes for all .c files

Allow Include What You Use to remove unnecessary includes and only
include what is necessary. This helps with reducing compilation times
and makes it easier to visualise which dependencies are actually
required.

Work on https://github.com/neovim/neovim/issues/549, but doesn't close
it since this only works fully for .c files and not headers.
---
 src/nvim/api/autocmd.c                   |   9 +++
 src/nvim/api/buffer.c                    |  16 +++--
 src/nvim/api/command.c                   |  21 ++++++-
 src/nvim/api/deprecated.c                |   8 ++-
 src/nvim/api/extmark.c                   |  13 +++-
 src/nvim/api/extmark.h                   |   3 +
 src/nvim/api/options.c                   |  12 ++--
 src/nvim/api/private/converter.c         |   9 ++-
 src/nvim/api/private/dispatch.c          |  29 +--------
 src/nvim/api/private/dispatch.h          |   5 ++
 src/nvim/api/private/helpers.c           |  18 +++---
 src/nvim/api/private/helpers.h           |   6 ++
 src/nvim/api/tabpage.c                   |   3 +-
 src/nvim/api/ui.c                        |  16 +++--
 src/nvim/api/vim.c                       |  31 ++++-----
 src/nvim/api/vimscript.c                 |  17 +++--
 src/nvim/api/win_config.c                |  15 +++--
 src/nvim/api/window.c                    |   9 +--
 src/nvim/arabic.c                        |   5 ++
 src/nvim/arglist.c                       |  15 ++++-
 src/nvim/autocmd.c                       |  27 +++++++-
 src/nvim/autocmd.h                       |  12 ++++
 src/nvim/buffer.c                        |  18 +++++-
 src/nvim/buffer.h                        |  10 ++-
 src/nvim/buffer_defs.h                   |  41 ++++--------
 src/nvim/buffer_updates.c                |  15 ++++-
 src/nvim/buffer_updates.h                |   4 +-
 src/nvim/change.c                        |  23 ++++++-
 src/nvim/change.h                        |   4 +-
 src/nvim/channel.c                       |  24 ++++++-
 src/nvim/channel.h                       |  13 ++++
 src/nvim/charset.c                       |  17 +++--
 src/nvim/charset.h                       |   2 +
 src/nvim/cmdexpand.c                     |  28 ++++++++-
 src/nvim/cmdhist.c                       |  18 +++++-
 src/nvim/cmdhist.h                       |   1 +
 src/nvim/context.c                       |  14 ++++-
 src/nvim/context.h                       |   2 +
 src/nvim/cursor.c                        |   9 ++-
 src/nvim/cursor_shape.c                  |  10 ++-
 src/nvim/debugger.c                      |  16 +++++
 src/nvim/decoration.c                    |   9 +--
 src/nvim/decoration.h                    |   8 +++
 src/nvim/decoration_provider.c           |  13 +++-
 src/nvim/decoration_provider.h           |   5 ++
 src/nvim/diff.c                          |  13 ++++
 src/nvim/diff.h                          |   3 +
 src/nvim/digraph.c                       |  10 +++
 src/nvim/drawline.c                      |  23 +++++--
 src/nvim/drawline.h                      |   6 ++
 src/nvim/drawscreen.c                    |  21 ++++++-
 src/nvim/drawscreen.h                    |   3 +
 src/nvim/edit.c                          |  19 +++---
 src/nvim/eval.c                          |  38 +++++++++--
 src/nvim/eval.h                          |  13 ++--
 src/nvim/eval/decode.c                   |  19 ++++--
 src/nvim/eval/encode.c                   |  11 +++-
 src/nvim/eval/encode.h                   |   5 +-
 src/nvim/eval/executor.c                 |  10 ++-
 src/nvim/eval/funcs.c                    |  59 +++++++++++++----
 src/nvim/eval/funcs.h                    |   5 ++
 src/nvim/eval/gc.c                       |   5 +-
 src/nvim/eval/gc.h                       |   1 +
 src/nvim/eval/typval.c                   |   3 +-
 src/nvim/eval/typval.h                   |   5 ++
 src/nvim/eval/userfunc.c                 |  24 ++++++-
 src/nvim/eval/userfunc.h                 |  10 +++
 src/nvim/eval/vars.c                     |  22 ++++++-
 src/nvim/eval/vars.h                     |   2 +-
 src/nvim/event/libuv_process.c           |   6 +-
 src/nvim/event/libuv_process.h           |   1 +
 src/nvim/event/loop.c                    |   7 ++-
 src/nvim/event/multiqueue.c              |   6 +-
 src/nvim/event/process.c                 |   7 ++-
 src/nvim/event/process.h                 |   9 +++
 src/nvim/event/rstream.c                 |   9 +--
 src/nvim/event/signal.c                  |   1 +
 src/nvim/event/signal.h                  |   3 +
 src/nvim/event/socket.c                  |  11 ++--
 src/nvim/event/socket.h                  |   3 +
 src/nvim/event/stream.c                  |   4 +-
 src/nvim/event/stream.h                  |   3 +
 src/nvim/event/time.h                    |   4 ++
 src/nvim/event/wstream.c                 |   6 +-
 src/nvim/event/wstream.h                 |   3 +
 src/nvim/ex_cmds.c                       |  28 ++++++---
 src/nvim/ex_cmds.h                       |   1 +
 src/nvim/ex_cmds2.c                      |  19 +++---
 src/nvim/ex_cmds_defs.h                  |   2 +-
 src/nvim/ex_docmd.c                      |  41 ++++++------
 src/nvim/ex_docmd.h                      |   3 +
 src/nvim/ex_eval.c                       |  11 ++++
 src/nvim/ex_eval.h                       |   2 +-
 src/nvim/ex_eval_defs.h                  |   2 +-
 src/nvim/ex_getln.c                      |  24 ++++---
 src/nvim/ex_getln.h                      |   6 ++
 src/nvim/ex_session.c                    |  18 +++---
 src/nvim/extmark.c                       |   9 +--
 src/nvim/extmark.h                       |   7 +++
 src/nvim/file_search.c                   |  13 ++--
 src/nvim/file_search.h                   |   6 +-
 src/nvim/fileio.c                        |  34 +++++-----
 src/nvim/fileio.h                        |   1 +
 src/nvim/fold.c                          |  13 +++-
 src/nvim/fold.h                          |   1 +
 src/nvim/garray.c                        |   9 +--
 src/nvim/garray.h                        |   4 +-
 src/nvim/generators/gen_api_dispatch.lua |  29 +++++++++
 src/nvim/generators/gen_eval.lua         |  25 ++++++++
 src/nvim/generators/gen_ex_cmds.lua      |  37 +++++++++++
 src/nvim/getchar.c                       |  14 ++++-
 src/nvim/grid.c                          |  10 +++
 src/nvim/grid.h                          |   3 +
 src/nvim/hardcopy.c                      |  12 +++-
 src/nvim/hardcopy.h                      |   8 +--
 src/nvim/help.c                          |  13 ++++
 src/nvim/highlight.c                     |  13 ++++
 src/nvim/highlight.h                     |   1 +
 src/nvim/highlight_group.c               |  26 +++++++-
 src/nvim/indent.c                        |  11 +++-
 src/nvim/indent_c.c                      |  10 ++-
 src/nvim/input.c                         |  12 +++-
 src/nvim/insexpand.c                     |  19 +++++-
 src/nvim/insexpand.h                     |   3 +
 src/nvim/keycodes.c                      |  11 +++-
 src/nvim/keycodes.h                      |   4 ++
 src/nvim/linematch.c                     |   6 ++
 src/nvim/locale.c                        |   8 ++-
 src/nvim/log.c                           |  10 ++-
 src/nvim/log.h                           |   1 +
 src/nvim/lua/converter.c                 |  13 ++--
 src/nvim/lua/executor.c                  |  25 ++++++--
 src/nvim/lua/executor.h                  |   4 ++
 src/nvim/lua/spell.c                     |  16 ++++-
 src/nvim/lua/stdlib.c                    |  39 +++++-------
 src/nvim/lua/treesitter.c                |  13 ++--
 src/nvim/lua/xdiff.c                     |   9 +--
 src/nvim/main.c                          |  38 ++++++-----
 src/nvim/main.h                          |   2 +
 src/nvim/map.c                           |   5 +-
 src/nvim/map.h                           |   5 ++
 src/nvim/mapping.c                       |  18 ++++--
 src/nvim/mapping.h                       |   4 ++
 src/nvim/mark.c                          |  18 ++++--
 src/nvim/mark.h                          |   5 +-
 src/nvim/marktree.c                      |   5 ++
 src/nvim/marktree.h                      |   5 ++
 src/nvim/match.c                         |  19 +++++-
 src/nvim/math.c                          |   2 +-
 src/nvim/mbyte.c                         |  44 +++++++++----
 src/nvim/mbyte.h                         |   4 +-
 src/nvim/memfile.c                       |  11 +++-
 src/nvim/memline.c                       |  22 +++++--
 src/nvim/memline.h                       |   4 +-
 src/nvim/memory.c                        |  15 +++--
 src/nvim/memory.h                        |   8 +--
 src/nvim/menu.c                          |  17 ++++-
 src/nvim/menu.h                          |   6 +-
 src/nvim/menu_defs.h                     |   2 +-
 src/nvim/message.c                       |  23 ++++---
 src/nvim/mouse.c                         |  17 ++++-
 src/nvim/move.c                          |  12 +++-
 src/nvim/msgpack_rpc/channel.c           |  26 +++++---
 src/nvim/msgpack_rpc/channel.h           |   2 +
 src/nvim/msgpack_rpc/helpers.c           |  16 +++--
 src/nvim/msgpack_rpc/server.c            |  13 ++--
 src/nvim/msgpack_rpc/unpacker.c          |  10 ++-
 src/nvim/msgpack_rpc/unpacker.h          |   4 ++
 src/nvim/normal.c                        |  18 ++++--
 src/nvim/normal.h                        |   2 +-
 src/nvim/ops.c                           |  17 +++--
 src/nvim/ops.h                           |   7 ++-
 src/nvim/option.c                        |  26 +++++---
 src/nvim/option.h                        |   2 +-
 src/nvim/option_defs.h                   |   4 +-
 src/nvim/optionstr.c                     |  15 ++++-
 src/nvim/optionstr.h                     |   2 +-
 src/nvim/os/dl.c                         |   3 +-
 src/nvim/os/env.c                        |  13 ++++
 src/nvim/os/fileio.c                     |  13 ++--
 src/nvim/os/fs.c                         |  20 ++++--
 src/nvim/os/fs.h                         |   4 +-
 src/nvim/os/input.c                      |  16 +++--
 src/nvim/os/lang.c                       |  10 +--
 src/nvim/os/mem.c                        |   1 +
 src/nvim/os/process.c                    |  24 ++++---
 src/nvim/os/pty_process_unix.c           |  15 +++--
 src/nvim/os/pty_process_unix.h           |   2 +
 src/nvim/os/shell.c                      |  21 ++++++-
 src/nvim/os/signal.c                     |   7 +--
 src/nvim/os/stdpaths.c                   |   1 +
 src/nvim/os/time.c                       |  18 +++++-
 src/nvim/os/tty.c                        |   4 +-
 src/nvim/os/unix_defs.h                  |   3 +
 src/nvim/os/users.c                      |   6 +-
 src/nvim/os_unix.c                       |  35 +----------
 src/nvim/os_unix.h                       |   2 +-
 src/nvim/path.c                          |  19 +++---
 src/nvim/plines.c                        |  12 ++--
 src/nvim/plines.h                        |   3 +
 src/nvim/popupmenu.c                     |  14 ++++-
 src/nvim/popupmenu.h                     |   2 +
 src/nvim/profile.c                       |  20 +++++-
 src/nvim/quickfix.c                      |  19 +++++-
 src/nvim/rbuffer.c                       |   5 +-
 src/nvim/rbuffer.h                       |   2 +
 src/nvim/regexp.c                        |  15 ++++-
 src/nvim/regexp.h                        |   3 +-
 src/nvim/regexp_bt.c                     |   1 +
 src/nvim/runtime.c                       |  26 +++++++-
 src/nvim/runtime.h                       |   5 ++
 src/nvim/screen.c                        |  26 +++++---
 src/nvim/screen.h                        |   1 +
 src/nvim/search.c                        |  18 ++++--
 src/nvim/search.h                        |   3 +
 src/nvim/sha256.c                        |  10 +--
 src/nvim/sha256.h                        |   4 +-
 src/nvim/shada.c                         |  22 ++++---
 src/nvim/sign.c                          |  24 ++++++-
 src/nvim/spell.c                         | 105 +++++++++++++++----------------
 src/nvim/spellfile.c                     |  26 ++++++--
 src/nvim/spellsuggest.c                  |  20 +++++-
 src/nvim/state.c                         |  21 +++++--
 src/nvim/state.h                         |   2 +
 src/nvim/statusline.c                    |  23 ++++++-
 src/nvim/statusline.h                    |   4 ++
 src/nvim/strings.c                       |  37 +++--------
 src/nvim/syntax.c                        |  25 +++-----
 src/nvim/syntax.h                        |   1 +
 src/nvim/tag.c                           |  18 +++++-
 src/nvim/terminal.c                      |  22 +++++--
 src/nvim/testing.c                       |  18 ++++++
 src/nvim/textformat.c                    |  10 +++
 src/nvim/textformat.h                    |   4 +-
 src/nvim/textobject.c                    |  10 ++-
 src/nvim/textobject.h                    |   6 +-
 src/nvim/tui/input.c                     |  16 ++++-
 src/nvim/tui/input.h                     |   4 ++
 src/nvim/tui/terminfo.c                  |   6 +-
 src/nvim/tui/tui.c                       |  24 +++----
 src/nvim/ugrid.c                         |   8 +--
 src/nvim/ugrid.h                         |   4 ++
 src/nvim/ui.c                            |  26 ++++----
 src/nvim/ui.h                            |   8 ++-
 src/nvim/ui_bridge.c                     |  12 ++--
 src/nvim/ui_bridge.h                     |   3 +
 src/nvim/ui_client.c                     |  14 +++--
 src/nvim/ui_client.h                     |   3 +
 src/nvim/ui_compositor.c                 |  15 +++--
 src/nvim/undo.c                          |  20 +++++-
 src/nvim/usercmd.c                       |  17 ++++-
 src/nvim/usercmd.h                       |   5 ++
 src/nvim/version.c                       |  22 ++++---
 src/nvim/vim.h                           |   2 +-
 src/nvim/viml/parser/expressions.c       |   6 ++
 src/nvim/viml/parser/expressions.h       |   3 +
 src/nvim/viml/parser/parser.c            |   2 +-
 src/nvim/viml/parser/parser.h            |   1 +
 src/nvim/window.c                        |  26 ++++++--
 src/nvim/window.h                        |   4 ++
 260 files changed, 2364 insertions(+), 861 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c
index 3dfe77ba38..af185c50c9 100644
--- a/src/nvim/api/autocmd.c
+++ b/src/nvim/api/autocmd.c
@@ -1,8 +1,12 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
+#include 
 #include 
+#include 
 #include 
+#include 
+#include 
 
 #include "lauxlib.h"
 #include "nvim/api/autocmd.h"
@@ -12,7 +16,12 @@
 #include "nvim/autocmd.h"
 #include "nvim/buffer.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
+#include "nvim/globals.h"
 #include "nvim/lua/executor.h"
+#include "nvim/memory.h"
+#include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "api/autocmd.c.generated.h"
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 29c2ed6028..1101689391 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -3,25 +3,30 @@
 
 // Some of this code was adapted from 'if_py_both.h' from the original
 // vim source
+
+#include 
 #include 
-#include 
 #include 
+#include 
 #include 
-#include 
+#include 
 
+#include "klib/kvec.h"
+#include "lua.h"
 #include "nvim/api/buffer.h"
 #include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
+#include "nvim/ascii.h"
 #include "nvim/autocmd.h"
 #include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/buffer_updates.h"
 #include "nvim/change.h"
 #include "nvim/cursor.h"
-#include "nvim/decoration.h"
 #include "nvim/drawscreen.h"
 #include "nvim/ex_cmds.h"
-#include "nvim/ex_docmd.h"
 #include "nvim/extmark.h"
+#include "nvim/globals.h"
 #include "nvim/lua/executor.h"
 #include "nvim/mapping.h"
 #include "nvim/mark.h"
@@ -29,9 +34,10 @@
 #include "nvim/memory.h"
 #include "nvim/move.h"
 #include "nvim/ops.h"
+#include "nvim/pos.h"
+#include "nvim/types.h"
 #include "nvim/undo.h"
 #include "nvim/vim.h"
-#include "nvim/window.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "api/buffer.c.generated.h"
diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c
index 752d3868d5..8a7abf0845 100644
--- a/src/nvim/api/command.c
+++ b/src/nvim/api/command.c
@@ -1,21 +1,36 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
+#include 
 #include 
-#include 
-#include 
+#include 
+#include 
 
+#include "klib/kvec.h"
+#include "lauxlib.h"
 #include "nvim/api/command.h"
-#include "nvim/api/private/converter.h"
 #include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
+#include "nvim/ascii.h"
 #include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/decoration.h"
+#include "nvim/ex_cmds.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_eval.h"
+#include "nvim/garray.h"
+#include "nvim/globals.h"
 #include "nvim/lua/executor.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#include "nvim/memory.h"
 #include "nvim/ops.h"
+#include "nvim/pos.h"
 #include "nvim/regexp.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/usercmd.h"
+#include "nvim/vim.h"
 #include "nvim/window.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c
index 8e1a615bbb..332e2b5fc3 100644
--- a/src/nvim/api/deprecated.c
+++ b/src/nvim/api/deprecated.c
@@ -1,7 +1,6 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
-#include 
 #include 
 #include 
 #include 
@@ -11,10 +10,15 @@
 #include "nvim/api/extmark.h"
 #include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
-#include "nvim/api/vim.h"
 #include "nvim/api/vimscript.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/decoration.h"
 #include "nvim/extmark.h"
+#include "nvim/globals.h"
 #include "nvim/lua/executor.h"
+#include "nvim/memory.h"
+#include "nvim/pos.h"
+#include "nvim/types.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "api/deprecated.c.generated.h"
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index 54eb7d9b6b..3a96c42dba 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -1,20 +1,29 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
+#include 
 #include 
 #include 
-#include 
+#include 
 
+#include "klib/kvec.h"
+#include "lauxlib.h"
 #include "nvim/api/extmark.h"
 #include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
+#include "nvim/decoration.h"
 #include "nvim/decoration_provider.h"
 #include "nvim/drawscreen.h"
 #include "nvim/extmark.h"
 #include "nvim/highlight_group.h"
-#include "nvim/lua/executor.h"
+#include "nvim/mbyte.h"
 #include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/pos.h"
+#include "nvim/strings.h"
+#include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "api/extmark.c.generated.h"
diff --git a/src/nvim/api/extmark.h b/src/nvim/api/extmark.h
index 74802c6efb..0a627a889c 100644
--- a/src/nvim/api/extmark.h
+++ b/src/nvim/api/extmark.h
@@ -3,7 +3,10 @@
 
 #include "nvim/api/private/defs.h"
 #include "nvim/decoration.h"
+#include "nvim/macros.h"
 #include "nvim/map.h"
+#include "nvim/map_defs.h"
+#include "nvim/types.h"
 
 EXTERN Map(String, handle_T) namespace_ids INIT(= MAP_INIT);
 EXTERN handle_T next_namespace_id INIT(= 1);
diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c
index 1b04392d47..d705636479 100644
--- a/src/nvim/api/options.c
+++ b/src/nvim/api/options.c
@@ -1,20 +1,20 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
-#include 
 #include 
+#include 
 #include 
-#include 
-#include 
 #include 
 
 #include "nvim/api/options.h"
-#include "nvim/api/private/converter.h"
+#include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/autocmd.h"
-#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/globals.h"
+#include "nvim/memory.h"
 #include "nvim/option.h"
-#include "nvim/option_defs.h"
+#include "nvim/vim.h"
 #include "nvim/window.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c
index b6b3c83f3c..7770ba39d8 100644
--- a/src/nvim/api/private/converter.c
+++ b/src/nvim/api/private/converter.c
@@ -2,17 +2,24 @@
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
 #include 
+#include 
 #include 
+#include 
 #include 
 
+#include "klib/kvec.h"
 #include "nvim/api/private/converter.h"
 #include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/assert.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/eval/userfunc.h"
-#include "nvim/lua/converter.h"
+#include "nvim/garray.h"
 #include "nvim/lua/executor.h"
+#include "nvim/memory.h"
+#include "nvim/types.h"
+#include "nvim/vim.h"
 
 /// Helper structure for vim_to_object
 typedef struct {
diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c
index d6a6fc1219..f427bba00e 100644
--- a/src/nvim/api/private/dispatch.c
+++ b/src/nvim/api/private/dispatch.c
@@ -1,38 +1,11 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
-#include 
-#include 
-#include 
-#include 
+#include 
 
-#include "nvim/api/deprecated.h"
 #include "nvim/api/private/defs.h"
 #include "nvim/api/private/dispatch.h"
 #include "nvim/api/private/helpers.h"
-#include "nvim/log.h"
-#include "nvim/map.h"
-#include "nvim/msgpack_rpc/helpers.h"
-#include "nvim/vim.h"
-
-// ===========================================================================
-// NEW API FILES MUST GO HERE.
-//
-//  When creating a new API file, you must include it here,
-//  so that the dispatcher can find the C functions that you are creating!
-// ===========================================================================
-#include "nvim/api/autocmd.h"
-#include "nvim/api/buffer.h"
-#include "nvim/api/command.h"
-#include "nvim/api/extmark.h"
-#include "nvim/api/options.h"
-#include "nvim/api/tabpage.h"
-#include "nvim/api/ui.h"
-#include "nvim/api/vim.h"
-#include "nvim/api/vimscript.h"
-#include "nvim/api/win_config.h"
-#include "nvim/api/window.h"
-#include "nvim/ui_client.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "api/private/dispatch_wrappers.generated.h"
diff --git a/src/nvim/api/private/dispatch.h b/src/nvim/api/private/dispatch.h
index f0161a53a8..4ae61b2bfb 100644
--- a/src/nvim/api/private/dispatch.h
+++ b/src/nvim/api/private/dispatch.h
@@ -1,7 +1,12 @@
 #ifndef NVIM_API_PRIVATE_DISPATCH_H
 #define NVIM_API_PRIVATE_DISPATCH_H
 
+#include 
+#include 
+
 #include "nvim/api/private/defs.h"
+#include "nvim/memory.h"
+#include "nvim/types.h"
 
 typedef Object (*ApiDispatchWrapper)(uint64_t channel_id, Array args, Arena *arena, Error *error);
 
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index d10d17c88d..b7cd0c82fb 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -3,8 +3,12 @@
 
 #include 
 #include 
+#include 
+#include 
+#include 
 #include 
 #include 
+#include 
 #include 
 #include 
 
@@ -12,28 +16,24 @@
 #include "nvim/api/private/converter.h"
 #include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
-#include "nvim/api/vim.h"
 #include "nvim/ascii.h"
-#include "nvim/assert.h"
-#include "nvim/buffer.h"
-#include "nvim/charset.h"
-#include "nvim/eval.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/eval/typval.h"
-#include "nvim/ex_cmds_defs.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/ex_eval.h"
-#include "nvim/extmark.h"
+#include "nvim/garray.h"
 #include "nvim/highlight_group.h"
 #include "nvim/lua/executor.h"
 #include "nvim/map.h"
-#include "nvim/map_defs.h"
 #include "nvim/mark.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/msgpack_rpc/helpers.h"
+#include "nvim/pos.h"
 #include "nvim/ui.h"
 #include "nvim/version.h"
 #include "nvim/vim.h"
-#include "nvim/window.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "api/private/funcs_metadata.generated.h"
diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h
index 65215fa8c8..ec97ba9ec6 100644
--- a/src/nvim/api/private/helpers.h
+++ b/src/nvim/api/private/helpers.h
@@ -1,11 +1,17 @@
 #ifndef NVIM_API_PRIVATE_HELPERS_H
 #define NVIM_API_PRIVATE_HELPERS_H
 
+#include 
+#include 
+
 #include "klib/kvec.h"
 #include "nvim/api/private/defs.h"
 #include "nvim/decoration.h"
 #include "nvim/ex_eval_defs.h"
 #include "nvim/getchar.h"
+#include "nvim/globals.h"
+#include "nvim/macros.h"
+#include "nvim/map.h"
 #include "nvim/memory.h"
 #include "nvim/vim.h"
 
diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c
index 31f9fab82a..21eb326c3b 100644
--- a/src/nvim/api/tabpage.c
+++ b/src/nvim/api/tabpage.c
@@ -2,13 +2,14 @@
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
 #include 
-#include 
 #include 
 
 #include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/api/tabpage.h"
 #include "nvim/api/vim.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/globals.h"
 #include "nvim/memory.h"
 #include "nvim/window.h"
 
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index f251e0043f..aeccddb9ea 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -2,26 +2,34 @@
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
 #include 
+#include 
+#include 
 #include 
-#include 
 #include 
+#include 
+#include 
 
+#include "klib/kvec.h"
 #include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/api/ui.h"
 #include "nvim/channel.h"
-#include "nvim/cursor_shape.h"
+#include "nvim/event/loop.h"
+#include "nvim/event/wstream.h"
+#include "nvim/globals.h"
 #include "nvim/grid.h"
 #include "nvim/highlight.h"
+#include "nvim/main.h"
 #include "nvim/map.h"
+#include "nvim/mbyte.h"
 #include "nvim/memory.h"
 #include "nvim/msgpack_rpc/channel.h"
+#include "nvim/msgpack_rpc/channel_defs.h"
 #include "nvim/msgpack_rpc/helpers.h"
 #include "nvim/option.h"
-#include "nvim/popupmenu.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/vim.h"
-#include "nvim/window.h"
 
 typedef struct {
   uint64_t channel_id;
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index d3f8c768a0..7a5ea7aa95 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -5,9 +5,13 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
 #include 
 
+#include "klib/kvec.h"
+#include "lauxlib.h"
 #include "nvim/api/buffer.h"
 #include "nvim/api/deprecated.h"
 #include "nvim/api/private/converter.h"
@@ -15,55 +19,52 @@
 #include "nvim/api/private/dispatch.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/api/vim.h"
-#include "nvim/api/window.h"
 #include "nvim/ascii.h"
+#include "nvim/autocmd.h"
 #include "nvim/buffer.h"
-#include "nvim/buffer_defs.h"
-#include "nvim/charset.h"
+#include "nvim/channel.h"
 #include "nvim/context.h"
-#include "nvim/decoration.h"
-#include "nvim/decoration_provider.h"
 #include "nvim/drawscreen.h"
-#include "nvim/edit.h"
 #include "nvim/eval.h"
 #include "nvim/eval/typval.h"
-#include "nvim/eval/userfunc.h"
-#include "nvim/ex_cmds_defs.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_eval.h"
-#include "nvim/file_search.h"
-#include "nvim/fileio.h"
 #include "nvim/getchar.h"
 #include "nvim/globals.h"
 #include "nvim/grid.h"
 #include "nvim/highlight.h"
-#include "nvim/highlight_defs.h"
 #include "nvim/highlight_group.h"
-#include "nvim/insexpand.h"
+#include "nvim/keycodes.h"
+#include "nvim/log.h"
 #include "nvim/lua/executor.h"
+#include "nvim/macros.h"
 #include "nvim/mapping.h"
 #include "nvim/mark.h"
+#include "nvim/mbyte.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/move.h"
 #include "nvim/msgpack_rpc/channel.h"
-#include "nvim/msgpack_rpc/helpers.h"
+#include "nvim/msgpack_rpc/channel_defs.h"
 #include "nvim/msgpack_rpc/unpacker.h"
 #include "nvim/ops.h"
 #include "nvim/option.h"
 #include "nvim/optionstr.h"
 #include "nvim/os/input.h"
+#include "nvim/os/os_defs.h"
 #include "nvim/os/process.h"
 #include "nvim/popupmenu.h"
+#include "nvim/pos.h"
 #include "nvim/runtime.h"
 #include "nvim/state.h"
 #include "nvim/statusline.h"
+#include "nvim/strings.h"
+#include "nvim/terminal.h"
 #include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/vim.h"
-#include "nvim/viml/parser/expressions.h"
-#include "nvim/viml/parser/parser.h"
 #include "nvim/window.h"
 
 #define LINE_BUFFER_MIN_SIZE 4096
diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c
index dccc2d42d8..af1b23b712 100644
--- a/src/nvim/api/vimscript.c
+++ b/src/nvim/api/vimscript.c
@@ -2,26 +2,31 @@
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
 #include 
-#include 
-#include 
+#include 
+#include 
+#include 
+#include 
 
+#include "klib/kvec.h"
 #include "nvim/api/private/converter.h"
 #include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/api/vimscript.h"
 #include "nvim/ascii.h"
-#include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/eval.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/eval/userfunc.h"
 #include "nvim/ex_docmd.h"
-#include "nvim/ops.h"
+#include "nvim/garray.h"
+#include "nvim/globals.h"
+#include "nvim/memory.h"
+#include "nvim/pos.h"
 #include "nvim/runtime.h"
-#include "nvim/strings.h"
 #include "nvim/vim.h"
 #include "nvim/viml/parser/expressions.h"
 #include "nvim/viml/parser/parser.h"
-#include "nvim/window.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "api/vimscript.c.generated.h"
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index 648048e970..532052f9b0 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -1,22 +1,27 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
-#include 
-#include 
 #include 
-#include 
-#include 
+#include 
 
+#include "klib/kvec.h"
 #include "nvim/api/extmark.h"
 #include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/api/win_config.h"
 #include "nvim/ascii.h"
 #include "nvim/buffer_defs.h"
+#include "nvim/decoration.h"
 #include "nvim/drawscreen.h"
+#include "nvim/extmark_defs.h"
+#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
 #include "nvim/highlight_group.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#include "nvim/memory.h"
 #include "nvim/option.h"
-#include "nvim/strings.h"
+#include "nvim/pos.h"
 #include "nvim/syntax.h"
 #include "nvim/ui.h"
 #include "nvim/window.h"
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index 08dcc113da..3f7c734cd1 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -10,16 +10,17 @@
 #include "nvim/api/private/helpers.h"
 #include "nvim/api/window.h"
 #include "nvim/ascii.h"
-#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/cursor.h"
 #include "nvim/drawscreen.h"
 #include "nvim/ex_docmd.h"
+#include "nvim/gettext.h"
 #include "nvim/globals.h"
 #include "nvim/lua/executor.h"
+#include "nvim/memline_defs.h"
 #include "nvim/move.h"
-#include "nvim/option.h"
-#include "nvim/syntax.h"
-#include "nvim/vim.h"
+#include "nvim/pos.h"
+#include "nvim/types.h"
 #include "nvim/window.h"
 
 /// Gets the current buffer in a window
diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c
index ac6269493d..41024cafda 100644
--- a/src/nvim/arabic.c
+++ b/src/nvim/arabic.c
@@ -21,9 +21,14 @@
 /// Stand-Alone - unicode form-B isolated char denoted with  a_s_* (NOT USED)
 
 #include 
+#include 
+#include 
 
 #include "nvim/arabic.h"
 #include "nvim/ascii.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#include "nvim/option_defs.h"
 #include "nvim/vim.h"
 
 // Unicode values for Arabic characters.
diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c
index bd49bc8a36..73c86addac 100644
--- a/src/nvim/arglist.c
+++ b/src/nvim/arglist.c
@@ -5,23 +5,36 @@
 
 #include 
 #include 
+#include 
+#include 
 
+#include "auto/config.h"
 #include "nvim/arglist.h"
+#include "nvim/ascii.h"
 #include "nvim/buffer.h"
 #include "nvim/charset.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/ex_cmds.h"
 #include "nvim/ex_cmds2.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_getln.h"
 #include "nvim/fileio.h"
 #include "nvim/garray.h"
+#include "nvim/gettext.h"
 #include "nvim/globals.h"
+#include "nvim/macros.h"
 #include "nvim/mark.h"
+#include "nvim/memline_defs.h"
 #include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option_defs.h"
 #include "nvim/os/input.h"
 #include "nvim/path.h"
+#include "nvim/pos.h"
 #include "nvim/regexp.h"
-#include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/undo.h"
 #include "nvim/version.h"
 #include "nvim/vim.h"
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 9845e6be13..4df14411c5 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -2,9 +2,13 @@
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
 // autocmd.c: Autocommand related functions
-#include 
 
-#include "lauxlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+
 #include "nvim/api/private/helpers.h"
 #include "nvim/ascii.h"
 #include "nvim/autocmd.h"
@@ -12,26 +16,43 @@
 #include "nvim/charset.h"
 #include "nvim/cursor.h"
 #include "nvim/drawscreen.h"
-#include "nvim/edit.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval.h"
 #include "nvim/eval/userfunc.h"
 #include "nvim/eval/vars.h"
+#include "nvim/event/defs.h"
+#include "nvim/event/loop.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_eval.h"
 #include "nvim/ex_getln.h"
 #include "nvim/fileio.h"
+#include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
 #include "nvim/grid.h"
+#include "nvim/hashtab.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/insexpand.h"
 #include "nvim/lua/executor.h"
+#include "nvim/main.h"
 #include "nvim/map.h"
+#include "nvim/memline_defs.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option_defs.h"
 #include "nvim/optionstr.h"
 #include "nvim/os/input.h"
+#include "nvim/os/os.h"
+#include "nvim/os/time.h"
+#include "nvim/path.h"
 #include "nvim/profile.h"
 #include "nvim/regexp.h"
 #include "nvim/runtime.h"
+#include "nvim/screen.h"
 #include "nvim/search.h"
 #include "nvim/state.h"
+#include "nvim/strings.h"
+#include "nvim/ui.h"
 #include "nvim/ui_compositor.h"
 #include "nvim/vim.h"
 #include "nvim/window.h"
diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h
index 75a8a7aaa1..c159254c29 100644
--- a/src/nvim/autocmd.h
+++ b/src/nvim/autocmd.h
@@ -1,8 +1,20 @@
 #ifndef NVIM_AUTOCMD_H
 #define NVIM_AUTOCMD_H
 
+#include 
+#include 
+
+#include "nvim/api/private/defs.h"
 #include "nvim/buffer_defs.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/ex_cmds_defs.h"
+#include "nvim/macros.h"
+#include "nvim/regexp_defs.h"
+#include "nvim/types.h"
+
+struct AutoCmd_S;
+struct AutoPatCmd_S;
+struct AutoPat_S;
 
 // event_T definition
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 78b058ea9a..485a6669ef 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -20,10 +20,16 @@
 //
 
 #include 
+#include 
 #include 
 #include 
+#include 
 #include 
+#include 
+#include 
 
+#include "auto/config.h"
+#include "klib/kvec.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/arglist.h"
 #include "nvim/ascii.h"
@@ -44,6 +50,7 @@
 #include "nvim/eval/vars.h"
 #include "nvim/ex_cmds.h"
 #include "nvim/ex_cmds2.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_eval.h"
 #include "nvim/ex_getln.h"
@@ -53,21 +60,25 @@
 #include "nvim/fold.h"
 #include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/hashtab.h"
 #include "nvim/help.h"
-#include "nvim/highlight_group.h"
 #include "nvim/indent.h"
 #include "nvim/indent_c.h"
 #include "nvim/main.h"
+#include "nvim/map.h"
 #include "nvim/mapping.h"
 #include "nvim/mark.h"
-#include "nvim/mark_defs.h"
 #include "nvim/mbyte.h"
+#include "nvim/memline_defs.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/move.h"
+#include "nvim/normal.h"
 #include "nvim/option.h"
 #include "nvim/optionstr.h"
+#include "nvim/os/fs_defs.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
 #include "nvim/os/time.h"
@@ -76,11 +87,14 @@
 #include "nvim/quickfix.h"
 #include "nvim/regexp.h"
 #include "nvim/runtime.h"
+#include "nvim/screen.h"
 #include "nvim/sign.h"
 #include "nvim/spell.h"
 #include "nvim/statusline.h"
 #include "nvim/strings.h"
 #include "nvim/syntax.h"
+#include "nvim/terminal.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/undo.h"
 #include "nvim/usercmd.h"
diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h
index 84c402d782..610d9e37ec 100644
--- a/src/nvim/buffer.h
+++ b/src/nvim/buffer.h
@@ -1,12 +1,20 @@
 #ifndef NVIM_BUFFER_H
 #define NVIM_BUFFER_H
 
+#include 
+#include 
+#include 
+
+#include "nvim/buffer_defs.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/ex_cmds_defs.h"
 #include "nvim/func_attr.h"
+#include "nvim/grid_defs.h"  // for StlClickRecord
 #include "nvim/macros.h"
 #include "nvim/memline.h"
-#include "nvim/pos.h"  // for linenr_T
+#include "nvim/memline_defs.h"
+#include "nvim/pos.h"
 
 // Values for buflist_getfile()
 enum getf_values {
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 96cf352067..8b01e23afd 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -3,7 +3,6 @@
 
 #include 
 #include 
-// for FILE
 #include 
 
 typedef struct file_buffer buf_T;  // Forward declaration
@@ -16,36 +15,23 @@ typedef struct {
   int br_buf_free_count;
 } bufref_T;
 
-// for garray_T
+#include "klib/kvec.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/eval/typval.h"
 #include "nvim/garray.h"
-// for ScreenGrid
 #include "nvim/grid_defs.h"
-// for HLF_COUNT
-#include "nvim/highlight_defs.h"
-// for pos_T, lpos_T and linenr_T
-#include "nvim/pos.h"
-// for the number window-local and buffer-local options
-#include "nvim/option_defs.h"
-// for jump list and tag stack sizes in a buffer and mark types
-#include "nvim/mark_defs.h"
-// for u_header_T
-#include "nvim/undo_defs.h"
-// for hashtab_T
 #include "nvim/hashtab.h"
-// for dict_T
-#include "nvim/eval/typval.h"
-// for String
-#include "nvim/api/private/defs.h"
-// for Map(K, V)
+#include "nvim/highlight_defs.h"
 #include "nvim/map.h"
-// for kvec
-#include "klib/kvec.h"
-// for marktree
+#include "nvim/mark_defs.h"
 #include "nvim/marktree.h"
 // for float window title
 #include "nvim/extmark_defs.h"
 // for click definitions
+#include "nvim/option_defs.h"
+#include "nvim/pos.h"
 #include "nvim/statusline_defs.h"
+#include "nvim/undo_defs.h"
 
 #define GETFILE_SUCCESS(x)    ((x) <= 0)
 #define MODIFIABLE(buf) (buf->b_p_ma)
@@ -100,17 +86,12 @@ typedef struct wininfo_S wininfo_T;
 typedef struct frame_S frame_T;
 typedef uint64_t disptick_T;  // display tick type
 
-// for struct memline (it needs memfile_T)
 #include "nvim/memline_defs.h"
-
-// for regprog_T. Needs win_T and buf_T.
+#include "nvim/os/fs_defs.h"
 #include "nvim/regexp_defs.h"
-// for synstate_T (needs reg_extmatch_T, win_T, buf_T)
-#include "nvim/syntax_defs.h"
-// for sign_entry_T
-#include "nvim/os/fs_defs.h"    // for FileID
 #include "nvim/sign_defs.h"
-#include "nvim/terminal.h"      // for Terminal
+#include "nvim/syntax_defs.h"
+#include "nvim/terminal.h"
 
 // The taggy struct is used to store the information about a :tag command.
 typedef struct taggy {
diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c
index 681d5df047..bee7db1e98 100644
--- a/src/nvim/buffer_updates.c
+++ b/src/nvim/buffer_updates.c
@@ -1,18 +1,31 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
+#include 
+#include 
+#include 
+
+#include "klib/kvec.h"
+#include "lauxlib.h"
 #include "nvim/api/buffer.h"
+#include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/assert.h"
 #include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/buffer_updates.h"
 #include "nvim/extmark.h"
+#include "nvim/globals.h"
+#include "nvim/log.h"
 #include "nvim/lua/executor.h"
 #include "nvim/memline.h"
+#include "nvim/memory.h"
 #include "nvim/msgpack_rpc/channel.h"
+#include "nvim/pos.h"
+#include "nvim/types.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "buffer_updates.c.generated.h"
+# include "buffer_updates.c.generated.h"  // IWYU pragma: export
 #endif
 
 // Register a channel. Return True if the channel was added, or already added.
diff --git a/src/nvim/buffer_updates.h b/src/nvim/buffer_updates.h
index 3c2635be71..961fec879b 100644
--- a/src/nvim/buffer_updates.h
+++ b/src/nvim/buffer_updates.h
@@ -1,8 +1,8 @@
 #ifndef NVIM_BUFFER_UPDATES_H
 #define NVIM_BUFFER_UPDATES_H
 
-#include "nvim/buffer_defs.h"  // for buf_T
-#include "nvim/extmark.h"      // for bcount_t
+#include "nvim/buffer_defs.h"
+#include "nvim/extmark.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "buffer_updates.h.generated.h"
diff --git a/src/nvim/change.c b/src/nvim/change.c
index a62880dfc1..461034ba36 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -3,8 +3,16 @@
 
 /// change.c: functions related to changing text
 
+#include 
+#include 
+#include 
+#include 
+
+#include "nvim/ascii.h"
 #include "nvim/assert.h"
+#include "nvim/autocmd.h"
 #include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/buffer_updates.h"
 #include "nvim/change.h"
 #include "nvim/charset.h"
@@ -13,22 +21,35 @@
 #include "nvim/drawscreen.h"
 #include "nvim/edit.h"
 #include "nvim/eval.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/extmark.h"
-#include "nvim/fileio.h"
 #include "nvim/fold.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/indent.h"
 #include "nvim/indent_c.h"
 #include "nvim/insexpand.h"
+#include "nvim/macros.h"
 #include "nvim/mark.h"
+#include "nvim/mbyte.h"
 #include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/move.h"
 #include "nvim/option.h"
+#include "nvim/os/time.h"
 #include "nvim/plines.h"
+#include "nvim/pos.h"
+#include "nvim/screen.h"
 #include "nvim/search.h"
 #include "nvim/state.h"
+#include "nvim/strings.h"
 #include "nvim/textformat.h"
 #include "nvim/ui.h"
 #include "nvim/undo.h"
+#include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "change.c.generated.h"
diff --git a/src/nvim/change.h b/src/nvim/change.h
index fdfa8a29ec..d1d016c630 100644
--- a/src/nvim/change.h
+++ b/src/nvim/change.h
@@ -1,8 +1,8 @@
 #ifndef NVIM_CHANGE_H
 #define NVIM_CHANGE_H
 
-#include "nvim/buffer_defs.h"  // for buf_T
-#include "nvim/pos.h"  // for linenr_T
+#include "nvim/buffer_defs.h"
+#include "nvim/pos.h"
 
 // flags for open_line()
 #define OPENLINE_DELSPACES  0x01  // delete spaces after cursor
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
index 7c5bc1c410..f2e5a37b34 100644
--- a/src/nvim/channel.c
+++ b/src/nvim/channel.c
@@ -1,24 +1,42 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "lauxlib.h"
 #include "nvim/api/private/converter.h"
+#include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
-#include "nvim/api/ui.h"
 #include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/channel.h"
 #include "nvim/eval.h"
 #include "nvim/eval/encode.h"
+#include "nvim/eval/typval.h"
+#include "nvim/event/loop.h"
+#include "nvim/event/rstream.h"
 #include "nvim/event/socket.h"
+#include "nvim/event/wstream.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/log.h"
 #include "nvim/lua/executor.h"
+#include "nvim/main.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/msgpack_rpc/channel.h"
 #include "nvim/msgpack_rpc/server.h"
-#include "nvim/os/fs.h"
+#include "nvim/os/os_defs.h"
 #include "nvim/os/shell.h"
+#include "nvim/rbuffer.h"
 #ifdef MSWIN
 # include "nvim/os/os_win_console.h"
 # include "nvim/os/pty_conpty_win.h"
 #endif
-#include "nvim/ascii.h"
 #include "nvim/path.h"
 
 static bool did_stdio = false;
diff --git a/src/nvim/channel.h b/src/nvim/channel.h
index 0f1b481792..d7f32d8988 100644
--- a/src/nvim/channel.h
+++ b/src/nvim/channel.h
@@ -1,13 +1,26 @@
 #ifndef NVIM_CHANNEL_H
 #define NVIM_CHANNEL_H
 
+#include 
+#include 
+#include 
+
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/event/libuv_process.h"
+#include "nvim/event/multiqueue.h"
 #include "nvim/event/process.h"
 #include "nvim/event/socket.h"
+#include "nvim/event/stream.h"
+#include "nvim/garray.h"
+#include "nvim/macros.h"
 #include "nvim/main.h"
+#include "nvim/map.h"
+#include "nvim/map_defs.h"
 #include "nvim/msgpack_rpc/channel_defs.h"
 #include "nvim/os/pty_process.h"
+#include "nvim/terminal.h"
+#include "nvim/types.h"
 
 #define CHAN_STDIO 1
 #define CHAN_STDERR 2
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index f5db03b0b4..82a4698058 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -6,28 +6,35 @@
 /// Code related to character sets.
 
 #include 
+#include 
 #include 
+#include 
+#include 
 #include 
-#include 
 
+#include "auto/config.h"
 #include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/cursor.h"
-#include "nvim/func_attr.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/garray.h"
+#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
 #include "nvim/indent.h"
-#include "nvim/main.h"
+#include "nvim/keycodes.h"
+#include "nvim/macros.h"
 #include "nvim/mark.h"
 #include "nvim/mbyte.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
 #include "nvim/move.h"
 #include "nvim/option.h"
-#include "nvim/os_unix.h"
 #include "nvim/path.h"
 #include "nvim/plines.h"
+#include "nvim/pos.h"
 #include "nvim/state.h"
-#include "nvim/strings.h"
 #include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/charset.h b/src/nvim/charset.h
index c4e5d9522b..978a357aa7 100644
--- a/src/nvim/charset.h
+++ b/src/nvim/charset.h
@@ -1,6 +1,8 @@
 #ifndef NVIM_CHARSET_H
 #define NVIM_CHARSET_H
 
+#include 
+
 #include "nvim/buffer_defs.h"
 #include "nvim/eval/typval.h"
 #include "nvim/option_defs.h"
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index e0bf562bb8..39865d7508 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -3,9 +3,20 @@
 
 // cmdexpand.c: functions for command-line completion
 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "auto/config.h"
+#include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/arglist.h"
 #include "nvim/ascii.h"
+#include "nvim/autocmd.h"
 #include "nvim/buffer.h"
 #include "nvim/charset.h"
 #include "nvim/cmdexpand.h"
@@ -13,26 +24,39 @@
 #include "nvim/drawscreen.h"
 #include "nvim/eval.h"
 #include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/eval/userfunc.h"
 #include "nvim/ex_cmds.h"
-#include "nvim/ex_cmds2.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_getln.h"
 #include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/grid.h"
+#include "nvim/hashtab.h"
 #include "nvim/help.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/highlight_group.h"
+#include "nvim/keycodes.h"
 #include "nvim/locale.h"
+#include "nvim/log.h"
 #include "nvim/lua/executor.h"
+#include "nvim/macros.h"
 #include "nvim/mapping.h"
+#include "nvim/mbyte.h"
+#include "nvim/memory.h"
 #include "nvim/menu.h"
+#include "nvim/message.h"
 #include "nvim/option.h"
 #include "nvim/os/os.h"
+#include "nvim/path.h"
 #include "nvim/popupmenu.h"
+#include "nvim/pos.h"
 #include "nvim/profile.h"
 #include "nvim/regexp.h"
-#include "nvim/screen.h"
+#include "nvim/runtime.h"
 #include "nvim/search.h"
 #include "nvim/sign.h"
 #include "nvim/statusline.h"
diff --git a/src/nvim/cmdhist.c b/src/nvim/cmdhist.c
index 0ae586743f..1253f9cc2a 100644
--- a/src/nvim/cmdhist.c
+++ b/src/nvim/cmdhist.c
@@ -3,14 +3,30 @@
 
 // cmdhist.c: Functions for the history of the command-line.
 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
 #include "nvim/ascii.h"
 #include "nvim/charset.h"
 #include "nvim/cmdhist.h"
+#include "nvim/eval/typval.h"
 #include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_getln.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/macros.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option_defs.h"
+#include "nvim/pos.h"
 #include "nvim/regexp.h"
 #include "nvim/strings.h"
-#include "nvim/ui.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/cmdhist.h b/src/nvim/cmdhist.h
index 8b7bb7ac24..f86a2f855c 100644
--- a/src/nvim/cmdhist.h
+++ b/src/nvim/cmdhist.h
@@ -2,6 +2,7 @@
 #define NVIM_CMDHIST_H
 
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/ex_cmds_defs.h"
 #include "nvim/os/time.h"
 
diff --git a/src/nvim/context.c b/src/nvim/context.c
index c765b95733..5ea843e023 100644
--- a/src/nvim/context.c
+++ b/src/nvim/context.c
@@ -3,16 +3,28 @@
 
 // Context: snapshot of the entire editor state as one big object/map
 
+#include 
+#include 
+#include 
+
 #include "nvim/api/private/converter.h"
 #include "nvim/api/private/helpers.h"
-#include "nvim/api/vim.h"
 #include "nvim/api/vimscript.h"
 #include "nvim/context.h"
 #include "nvim/eval/encode.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/eval/userfunc.h"
 #include "nvim/ex_docmd.h"
+#include "nvim/gettext.h"
+#include "nvim/hashtab.h"
+#include "nvim/keycodes.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/option.h"
 #include "nvim/shada.h"
+#include "nvim/types.h"
+#include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "context.c.generated.h"
diff --git a/src/nvim/context.h b/src/nvim/context.h
index ae77e66516..7a1224d876 100644
--- a/src/nvim/context.h
+++ b/src/nvim/context.h
@@ -2,6 +2,8 @@
 #define NVIM_CONTEXT_H
 
 #include 
+#include 
+#include 
 
 #include "klib/kvec.h"
 #include "nvim/api/private/defs.h"
diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c
index 6c0475822b..811f397cb8 100644
--- a/src/nvim/cursor.c
+++ b/src/nvim/cursor.c
@@ -1,24 +1,31 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
+#include 
 #include 
 #include 
+#include 
 
 #include "nvim/ascii.h"
 #include "nvim/assert.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/change.h"
 #include "nvim/charset.h"
 #include "nvim/cursor.h"
 #include "nvim/drawscreen.h"
-#include "nvim/extmark.h"
 #include "nvim/fold.h"
+#include "nvim/globals.h"
+#include "nvim/macros.h"
 #include "nvim/mark.h"
+#include "nvim/mbyte.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
 #include "nvim/move.h"
 #include "nvim/option.h"
 #include "nvim/plines.h"
+#include "nvim/pos.h"
 #include "nvim/state.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c
index 68522bdaa0..f21e632036 100644
--- a/src/nvim/cursor_shape.c
+++ b/src/nvim/cursor_shape.c
@@ -1,15 +1,23 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
-#include 
+#include 
 #include 
+#include 
 
+#include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/ascii.h"
 #include "nvim/charset.h"
 #include "nvim/cursor_shape.h"
 #include "nvim/ex_getln.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/highlight_group.h"
+#include "nvim/log.h"
+#include "nvim/macros.h"
+#include "nvim/memory.h"
+#include "nvim/option_defs.h"
 #include "nvim/strings.h"
 #include "nvim/ui.h"
 #include "nvim/vim.h"
diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c
index e7c376621b..71959cfa29 100644
--- a/src/nvim/debugger.c
+++ b/src/nvim/debugger.c
@@ -5,17 +5,33 @@
 ///
 /// Vim script debugger functions
 
+#include 
+#include 
+#include 
+#include 
+
 #include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/debugger.h"
 #include "nvim/drawscreen.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_getln.h"
 #include "nvim/fileio.h"
+#include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
 #include "nvim/globals.h"
+#include "nvim/keycodes.h"
+#include "nvim/macros.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/os/os.h"
+#include "nvim/path.h"
 #include "nvim/pos.h"
 #include "nvim/regexp.h"
 #include "nvim/runtime.h"
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index 230d96a15f..037eb9f0d9 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -1,16 +1,17 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
-#include "nvim/api/ui.h"
+#include 
+
 #include "nvim/buffer.h"
 #include "nvim/decoration.h"
 #include "nvim/drawscreen.h"
 #include "nvim/extmark.h"
 #include "nvim/highlight.h"
 #include "nvim/highlight_group.h"
-#include "nvim/lua/executor.h"
-#include "nvim/move.h"
-#include "nvim/vim.h"
+#include "nvim/memory.h"
+#include "nvim/pos.h"
+#include "nvim/sign_defs.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "decoration.c.generated.h"
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index 8f016c103b..cee1eb2f94 100644
--- a/src/nvim/decoration.h
+++ b/src/nvim/decoration.h
@@ -1,9 +1,17 @@
 #ifndef NVIM_DECORATION_H
 #define NVIM_DECORATION_H
 
+#include 
+#include 
+#include 
+
+#include "klib/kvec.h"
 #include "nvim/buffer_defs.h"
 #include "nvim/extmark_defs.h"
+#include "nvim/macros.h"
+#include "nvim/marktree.h"
 #include "nvim/pos.h"
+#include "nvim/types.h"
 
 // actual Decoration data is in extmark_defs.h
 
diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c
index f356efffdc..ed21474935 100644
--- a/src/nvim/decoration_provider.c
+++ b/src/nvim/decoration_provider.c
@@ -1,14 +1,23 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
+#include 
+#include 
+#include 
+
 #include "klib/kvec.h"
+#include "lauxlib.h"
 #include "nvim/api/extmark.h"
+#include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
-#include "nvim/buffer.h"
-#include "nvim/decoration.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/decoration_provider.h"
+#include "nvim/globals.h"
 #include "nvim/highlight.h"
+#include "nvim/log.h"
 #include "nvim/lua/executor.h"
+#include "nvim/memory.h"
+#include "nvim/pos.h"
 
 static kvec_t(DecorProvider) decor_providers = KV_INITIAL_VALUE;
 
diff --git a/src/nvim/decoration_provider.h b/src/nvim/decoration_provider.h
index 852b1583b9..b91ddabdfd 100644
--- a/src/nvim/decoration_provider.h
+++ b/src/nvim/decoration_provider.h
@@ -1,7 +1,12 @@
 #ifndef NVIM_DECORATION_PROVIDER_H
 #define NVIM_DECORATION_PROVIDER_H
 
+#include 
+
+#include "klib/kvec.h"
 #include "nvim/buffer_defs.h"
+#include "nvim/macros.h"
+#include "nvim/types.h"
 
 typedef struct {
   NS ns_id;
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index b20d0976e4..4b71142c11 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -10,9 +10,15 @@
 /// - Use the compiled-in xdiff library.
 /// - Let 'diffexpr' do the work, using files.
 
+#include 
+#include 
 #include 
 #include 
+#include 
+#include 
+#include 
 
+#include "auto/config.h"
 #include "nvim/ascii.h"
 #include "nvim/autocmd.h"
 #include "nvim/buffer.h"
@@ -23,10 +29,14 @@
 #include "nvim/drawscreen.h"
 #include "nvim/eval.h"
 #include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
+#include "nvim/extmark_defs.h"
 #include "nvim/fileio.h"
 #include "nvim/fold.h"
 #include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/linematch.h"
 #include "nvim/mark.h"
 #include "nvim/mbyte.h"
@@ -37,10 +47,13 @@
 #include "nvim/normal.h"
 #include "nvim/option.h"
 #include "nvim/optionstr.h"
+#include "nvim/os/fs_defs.h"
 #include "nvim/os/os.h"
 #include "nvim/os/shell.h"
 #include "nvim/path.h"
+#include "nvim/pos.h"
 #include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/undo.h"
 #include "nvim/vim.h"
diff --git a/src/nvim/diff.h b/src/nvim/diff.h
index 53fc5aa077..1f64465336 100644
--- a/src/nvim/diff.h
+++ b/src/nvim/diff.h
@@ -1,7 +1,10 @@
 #ifndef NVIM_DIFF_H
 #define NVIM_DIFF_H
 
+#include 
+
 #include "nvim/ex_cmds_defs.h"
+#include "nvim/macros.h"
 #include "nvim/pos.h"
 
 // Value set from 'diffopt'.
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
index c4a36e783f..2ca608ad93 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -8,24 +8,34 @@
 #include 
 #include 
 #include 
+#include 
 
 #include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/digraph.h"
 #include "nvim/drawscreen.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_getln.h"
 #include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
+#include "nvim/keycodes.h"
 #include "nvim/mapping.h"
 #include "nvim/mbyte.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/normal.h"
+#include "nvim/option_defs.h"
 #include "nvim/os/input.h"
 #include "nvim/runtime.h"
 #include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"
 
 typedef int result_T;
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 6d42b91507..9d6c95e20a 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -5,37 +5,50 @@
 // This is the middle level, drawscreen.c is the top and grid.c/screen.c the lower level.
 
 #include 
-#include 
+#include 
 #include 
+#include 
+#include 
 #include 
 
 #include "nvim/arabic.h"
+#include "nvim/ascii.h"
 #include "nvim/buffer.h"
-#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/cursor.h"
 #include "nvim/cursor_shape.h"
 #include "nvim/decoration.h"
+#include "nvim/decoration_provider.h"
 #include "nvim/diff.h"
 #include "nvim/drawline.h"
+#include "nvim/extmark_defs.h"
 #include "nvim/fold.h"
+#include "nvim/garray.h"
+#include "nvim/globals.h"
 #include "nvim/grid.h"
 #include "nvim/highlight.h"
 #include "nvim/highlight_group.h"
 #include "nvim/indent.h"
+#include "nvim/mark.h"
 #include "nvim/match.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline.h"
+#include "nvim/memory.h"
 #include "nvim/move.h"
 #include "nvim/option.h"
 #include "nvim/plines.h"
+#include "nvim/pos.h"
 #include "nvim/quickfix.h"
-#include "nvim/search.h"
+#include "nvim/screen.h"
 #include "nvim/sign.h"
 #include "nvim/spell.h"
 #include "nvim/state.h"
+#include "nvim/strings.h"
 #include "nvim/syntax.h"
+#include "nvim/terminal.h"
 #include "nvim/types.h"
-#include "nvim/undo.h"
-#include "nvim/window.h"
+#include "nvim/ui.h"
+#include "nvim/vim.h"
 
 #define MB_FILLER_CHAR '<'  // character used when a double-width character
                             // doesn't fit.
diff --git a/src/nvim/drawline.h b/src/nvim/drawline.h
index e50969983e..9f60b46e1b 100644
--- a/src/nvim/drawline.h
+++ b/src/nvim/drawline.h
@@ -1,9 +1,15 @@
 #ifndef NVIM_DRAWLINE_H
 #define NVIM_DRAWLINE_H
 
+#include 
+#include 
+
+#include "klib/kvec.h"
 #include "nvim/decoration_provider.h"
 #include "nvim/fold.h"
+#include "nvim/macros.h"
 #include "nvim/screen.h"
+#include "nvim/types.h"
 
 // Maximum columns for terminal highlight attributes
 #define TERM_ATTRS_MAX 1024
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index fe2c9c3801..4a300384e7 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -59,30 +59,47 @@
 #include 
 #include 
 
+#include "klib/kvec.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
 #include "nvim/buffer.h"
-#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/cmdexpand.h"
+#include "nvim/decoration.h"
+#include "nvim/decoration_provider.h"
 #include "nvim/diff.h"
+#include "nvim/drawline.h"
 #include "nvim/drawscreen.h"
 #include "nvim/ex_getln.h"
 #include "nvim/extmark_defs.h"
+#include "nvim/fold.h"
+#include "nvim/globals.h"
 #include "nvim/grid.h"
 #include "nvim/highlight.h"
 #include "nvim/highlight_group.h"
 #include "nvim/insexpand.h"
 #include "nvim/match.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline.h"
+#include "nvim/message.h"
 #include "nvim/move.h"
+#include "nvim/normal.h"
 #include "nvim/option.h"
 #include "nvim/plines.h"
 #include "nvim/popupmenu.h"
+#include "nvim/pos.h"
 #include "nvim/profile.h"
 #include "nvim/regexp.h"
+#include "nvim/screen.h"
 #include "nvim/statusline.h"
 #include "nvim/syntax.h"
+#include "nvim/terminal.h"
+#include "nvim/types.h"
+#include "nvim/ui.h"
 #include "nvim/ui_compositor.h"
-#include "nvim/undo.h"
 #include "nvim/version.h"
+#include "nvim/vim.h"
 #include "nvim/window.h"
 
 /// corner value flags for hsep_connected and vsep_connected
diff --git a/src/nvim/drawscreen.h b/src/nvim/drawscreen.h
index ef99899581..c14703dfa9 100644
--- a/src/nvim/drawscreen.h
+++ b/src/nvim/drawscreen.h
@@ -1,7 +1,10 @@
 #ifndef NVIM_DRAWSCREEN_H
 #define NVIM_DRAWSCREEN_H
 
+#include 
+
 #include "nvim/drawline.h"
+#include "nvim/macros.h"
 
 /// flags for update_screen()
 /// The higher the value, the higher the priority
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 4540ddf4fe..91a67c7c50 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -4,11 +4,14 @@
 // edit.c: functions for Insert mode
 
 #include 
+#include 
 #include 
 #include 
 #include 
+#include 
 
 #include "nvim/ascii.h"
+#include "nvim/autocmd.h"
 #include "nvim/buffer.h"
 #include "nvim/change.h"
 #include "nvim/charset.h"
@@ -17,20 +20,23 @@
 #include "nvim/drawscreen.h"
 #include "nvim/edit.h"
 #include "nvim/eval.h"
-#include "nvim/event/loop.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
-#include "nvim/ex_getln.h"
 #include "nvim/extmark.h"
 #include "nvim/fileio.h"
 #include "nvim/fold.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/grid.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/highlight_group.h"
 #include "nvim/indent.h"
 #include "nvim/indent_c.h"
 #include "nvim/insexpand.h"
 #include "nvim/keycodes.h"
-#include "nvim/main.h"
+#include "nvim/macros.h"
 #include "nvim/mapping.h"
 #include "nvim/mark.h"
 #include "nvim/mbyte.h"
@@ -43,19 +49,18 @@
 #include "nvim/ops.h"
 #include "nvim/option.h"
 #include "nvim/os/input.h"
-#include "nvim/os/time.h"
-#include "nvim/path.h"
 #include "nvim/plines.h"
 #include "nvim/popupmenu.h"
-#include "nvim/quickfix.h"
+#include "nvim/pos.h"
+#include "nvim/screen.h"
 #include "nvim/search.h"
-#include "nvim/spell.h"
 #include "nvim/state.h"
 #include "nvim/strings.h"
 #include "nvim/syntax.h"
 #include "nvim/terminal.h"
 #include "nvim/textformat.h"
 #include "nvim/textobject.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/undo.h"
 #include "nvim/vim.h"
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 89569e9714..4b52cae777 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -3,12 +3,19 @@
 
 // eval.c: Expression evaluation.
 
+#include 
+#include 
+#include 
 #include 
+#include 
 #include 
+#include 
 
+#include "auto/config.h"
+#include "nvim/api/private/defs.h"
 #include "nvim/ascii.h"
-#include "nvim/autocmd.h"
 #include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/change.h"
 #include "nvim/channel.h"
 #include "nvim/charset.h"
@@ -22,39 +29,60 @@
 #include "nvim/eval/typval.h"
 #include "nvim/eval/userfunc.h"
 #include "nvim/eval/vars.h"
+#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
+#include "nvim/event/process.h"
 #include "nvim/ex_cmds.h"
-#include "nvim/ex_cmds2.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_eval.h"
 #include "nvim/ex_getln.h"
 #include "nvim/ex_session.h"
+#include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
 #include "nvim/highlight_group.h"
 #include "nvim/insexpand.h"
+#include "nvim/keycodes.h"
+#include "nvim/lib/queue.h"
 #include "nvim/locale.h"
 #include "nvim/lua/executor.h"
+#include "nvim/macros.h"
+#include "nvim/main.h"
+#include "nvim/map.h"
 #include "nvim/mark.h"
+#include "nvim/mbyte.h"
 #include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/move.h"
+#include "nvim/msgpack_rpc/channel_defs.h"
 #include "nvim/ops.h"
 #include "nvim/option.h"
 #include "nvim/optionstr.h"
-#include "nvim/os/input.h"
+#include "nvim/os/fileio.h"
+#include "nvim/os/fs_defs.h"
+#include "nvim/os/os.h"
 #include "nvim/os/shell.h"
+#include "nvim/os/stdpaths_defs.h"
 #include "nvim/path.h"
+#include "nvim/pos.h"
 #include "nvim/profile.h"
 #include "nvim/quickfix.h"
 #include "nvim/regexp.h"
 #include "nvim/runtime.h"
-#include "nvim/screen.h"
 #include "nvim/search.h"
 #include "nvim/sign.h"
-#include "nvim/syntax.h"
+#include "nvim/strings.h"
 #include "nvim/tag.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/ui_compositor.h"
 #include "nvim/undo.h"
+#include "nvim/usercmd.h"
 #include "nvim/version.h"
+#include "nvim/vim.h"
 #include "nvim/window.h"
 
 // TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index afebdf5acb..61c1363d54 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -1,12 +1,17 @@
 #ifndef NVIM_EVAL_H
 #define NVIM_EVAL_H
 
+#include 
+#include 
+
 #include "nvim/buffer_defs.h"
 #include "nvim/channel.h"
-#include "nvim/event/time.h"  // For TimeWatcher
-#include "nvim/ex_cmds_defs.h"  // For exarg_T
-#include "nvim/os/fileio.h"  // For FileDescriptor
-#include "nvim/os/stdpaths_defs.h"  // For XDGVarType
+#include "nvim/eval/typval_defs.h"
+#include "nvim/event/time.h"
+#include "nvim/ex_cmds_defs.h"
+#include "nvim/hashtab.h"
+#include "nvim/os/fileio.h"
+#include "nvim/os/stdpaths_defs.h"
 
 #define COPYID_INC 2
 #define COPYID_MASK (~0x1)
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c
index f677148e08..cd1479f150 100644
--- a/src/nvim/eval/decode.c
+++ b/src/nvim/eval/decode.c
@@ -1,20 +1,31 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
-#include 
+#include 
+#include 
+#include 
 #include 
+#include 
+#include 
+#include 
 
 #include "klib/kvec.h"
 #include "nvim/ascii.h"
-#include "nvim/charset.h"  // vim_str2nr
+#include "nvim/charset.h"
 #include "nvim/eval.h"
 #include "nvim/eval/decode.h"
 #include "nvim/eval/encode.h"
 #include "nvim/eval/typval.h"
-#include "nvim/globals.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/hashtab.h"
 #include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#include "nvim/memory.h"
 #include "nvim/message.h"
-#include "nvim/vim.h"  // OK, FAIL
+#include "nvim/types.h"
+#include "nvim/vim.h"
 
 /// Helper structure for container_struct
 typedef struct {
diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c
index 82c9f04f8f..3e002ef52c 100644
--- a/src/nvim/eval/encode.c
+++ b/src/nvim/eval/encode.c
@@ -10,23 +10,28 @@
 #include 
 #include 
 #include 
-#include 
+#include 
 #include 
+#include 
+#include 
 
 #include "klib/kvec.h"
+#include "msgpack/pack.h"
 #include "nvim/ascii.h"
-#include "nvim/buffer_defs.h"
-#include "nvim/charset.h"  // vim_isprintc()
 #include "nvim/eval.h"
 #include "nvim/eval/encode.h"
 #include "nvim/eval/typval.h"
 #include "nvim/eval/typval_encode.h"
 #include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/hashtab.h"
 #include "nvim/macros.h"
 #include "nvim/math.h"
 #include "nvim/mbyte.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"  // For _()
 
 const char *const encode_bool_var_names[] = {
diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h
index c0c98e0990..5c217abb5a 100644
--- a/src/nvim/eval/encode.h
+++ b/src/nvim/eval/encode.h
@@ -2,11 +2,14 @@
 #define NVIM_EVAL_ENCODE_H
 
 #include 
+#include 
 #include 
 
 #include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/garray.h"
-#include "nvim/vim.h"  // For STRLEN
+#include "nvim/vim.h"
 
 /// Convert VimL value to msgpack string
 ///
diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c
index e253098df5..86d6063b01 100644
--- a/src/nvim/eval/executor.c
+++ b/src/nvim/eval/executor.c
@@ -1,15 +1,23 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
+#include 
+#include 
+
 #include "nvim/eval.h"
 #include "nvim/eval/executor.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/garray.h"
+#include "nvim/gettext.h"
 #include "nvim/globals.h"
 #include "nvim/message.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "eval/executor.c.generated.h"
+# include "eval/executor.c.generated.h"  // IWYU pragma: export
 #endif
 
 char *e_listidx = N_("E684: list index out of range: %" PRId64);
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 8acdc3256c..244802a183 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -1,25 +1,42 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
+#include 
+#include 
 #include 
+#include 
+#include 
 #include 
-
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "auto/config.h"
 #include "nvim/api/private/converter.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/api/private/dispatch.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/api/vim.h"
-#include "nvim/arglist.h"
 #include "nvim/ascii.h"
 #include "nvim/assert.h"
+#include "nvim/autocmd.h"
 #include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/change.h"
 #include "nvim/channel.h"
 #include "nvim/charset.h"
 #include "nvim/cmdexpand.h"
-#include "nvim/cmdhist.h"
 #include "nvim/context.h"
 #include "nvim/cursor.h"
 #include "nvim/diff.h"
-#include "nvim/digraph.h"
 #include "nvim/edit.h"
 #include "nvim/eval.h"
 #include "nvim/eval/decode.h"
@@ -29,54 +46,71 @@
 #include "nvim/eval/typval.h"
 #include "nvim/eval/userfunc.h"
 #include "nvim/eval/vars.h"
+#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
+#include "nvim/event/process.h"
+#include "nvim/event/time.h"
 #include "nvim/ex_cmds.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_eval.h"
 #include "nvim/ex_getln.h"
 #include "nvim/file_search.h"
 #include "nvim/fileio.h"
-#include "nvim/fold.h"
+#include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
 #include "nvim/globals.h"
+#include "nvim/grid_defs.h"
+#include "nvim/hashtab.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/highlight_group.h"
 #include "nvim/indent.h"
 #include "nvim/indent_c.h"
 #include "nvim/input.h"
-#include "nvim/insexpand.h"
+#include "nvim/keycodes.h"
 #include "nvim/lua/executor.h"
 #include "nvim/macros.h"
-#include "nvim/mapping.h"
+#include "nvim/main.h"
 #include "nvim/mark.h"
-#include "nvim/match.h"
 #include "nvim/math.h"
+#include "nvim/mbyte.h"
+#include "nvim/memfile_defs.h"
 #include "nvim/memline.h"
+#include "nvim/memory.h"
 #include "nvim/menu.h"
+#include "nvim/message.h"
 #include "nvim/mouse.h"
 #include "nvim/move.h"
 #include "nvim/msgpack_rpc/channel.h"
+#include "nvim/msgpack_rpc/channel_defs.h"
 #include "nvim/msgpack_rpc/server.h"
+#include "nvim/normal.h"
 #include "nvim/ops.h"
 #include "nvim/option.h"
 #include "nvim/optionstr.h"
 #include "nvim/os/dl.h"
+#include "nvim/os/fileio.h"
+#include "nvim/os/fs_defs.h"
+#include "nvim/os/os.h"
+#include "nvim/os/pty_process.h"
 #include "nvim/os/shell.h"
+#include "nvim/os/stdpaths_defs.h"
+#include "nvim/os/time.h"
 #include "nvim/path.h"
 #include "nvim/plines.h"
 #include "nvim/popupmenu.h"
+#include "nvim/pos.h"
 #include "nvim/profile.h"
-#include "nvim/quickfix.h"
 #include "nvim/regexp.h"
 #include "nvim/runtime.h"
-#include "nvim/screen.h"
 #include "nvim/search.h"
 #include "nvim/sha256.h"
-#include "nvim/sign.h"
 #include "nvim/spell.h"
 #include "nvim/spellsuggest.h"
 #include "nvim/state.h"
+#include "nvim/strings.h"
 #include "nvim/syntax.h"
 #include "nvim/tag.h"
-#include "nvim/testing.h"
 #include "nvim/ui.h"
 #include "nvim/undo.h"
 #include "nvim/version.h"
@@ -105,6 +139,7 @@ typedef enum {
 PRAGMA_DIAG_PUSH_IGNORE_MISSING_PROTOTYPES
 PRAGMA_DIAG_PUSH_IGNORE_IMPLICIT_FALLTHROUGH
 # include "funcs.generated.h"
+
 PRAGMA_DIAG_POP
 PRAGMA_DIAG_POP
 #endif
diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h
index adff0b2441..1ae031a952 100644
--- a/src/nvim/eval/funcs.h
+++ b/src/nvim/eval/funcs.h
@@ -1,9 +1,14 @@
 #ifndef NVIM_EVAL_FUNCS_H
 #define NVIM_EVAL_FUNCS_H
 
+#include 
+#include 
+
 #include "nvim/api/private/dispatch.h"
 #include "nvim/buffer_defs.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/types.h"
 
 /// Prototype of C function that implements VimL function
 typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, EvalFuncData data);
diff --git a/src/nvim/eval/gc.c b/src/nvim/eval/gc.c
index 633e6abacf..6a54c4ddc1 100644
--- a/src/nvim/eval/gc.c
+++ b/src/nvim/eval/gc.c
@@ -1,11 +1,12 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
+#include 
+
 #include "nvim/eval/gc.h"
-#include "nvim/eval/typval.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "eval/gc.c.generated.h"
+# include "eval/gc.c.generated.h"  // IWYU pragma: export
 #endif
 
 /// Head of list of all dictionaries
diff --git a/src/nvim/eval/gc.h b/src/nvim/eval/gc.h
index c2e862e469..3185750c3b 100644
--- a/src/nvim/eval/gc.h
+++ b/src/nvim/eval/gc.h
@@ -2,6 +2,7 @@
 #define NVIM_EVAL_GC_H
 
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 
 extern dict_T *gc_first_dict;
 extern list_T *gc_first_list;
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 7e4066adb7..f38e07f09d 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -28,9 +28,9 @@
 #include "nvim/lua/executor.h"
 #include "nvim/macros.h"
 #include "nvim/mbyte.h"
+#include "nvim/mbyte_defs.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
-#include "nvim/os/fileio.h"
 #include "nvim/os/input.h"
 #include "nvim/pos.h"
 #include "nvim/types.h"
@@ -3150,6 +3150,7 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, dict_T **const dic
 #define TYPVAL_ENCODE_FIRST_ARG_TYPE const void *const
 #define TYPVAL_ENCODE_FIRST_ARG_NAME ignored
 #include "nvim/eval/typval_encode.c.h"
+
 #undef TYPVAL_ENCODE_SCOPE
 #undef TYPVAL_ENCODE_NAME
 #undef TYPVAL_ENCODE_FIRST_ARG_TYPE
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index 6757d058ef..8ff7f7f43a 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -4,14 +4,19 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 #include "nvim/eval/typval_defs.h"
 #include "nvim/func_attr.h"
+#include "nvim/garray.h"
 #include "nvim/gettext.h"
+#include "nvim/hashtab.h"
+#include "nvim/lib/queue.h"
 #include "nvim/macros.h"
 #include "nvim/mbyte_defs.h"
 #include "nvim/message.h"
+#include "nvim/types.h"
 
 #ifdef LOG_LIST_ACTIONS
 # include "nvim/memory.h"
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index c01bd1e3df..2210a61d7c 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -3,28 +3,48 @@
 
 // User defined function support
 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "lauxlib.h"
 #include "nvim/ascii.h"
+#include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/debugger.h"
-#include "nvim/edit.h"
 #include "nvim/eval.h"
 #include "nvim/eval/encode.h"
 #include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
 #include "nvim/eval/userfunc.h"
 #include "nvim/eval/vars.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_eval.h"
 #include "nvim/ex_getln.h"
-#include "nvim/fileio.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
 #include "nvim/globals.h"
 #include "nvim/insexpand.h"
+#include "nvim/keycodes.h"
 #include "nvim/lua/executor.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline_defs.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option_defs.h"
 #include "nvim/os/input.h"
+#include "nvim/path.h"
 #include "nvim/profile.h"
 #include "nvim/regexp.h"
 #include "nvim/runtime.h"
 #include "nvim/search.h"
+#include "nvim/strings.h"
 #include "nvim/ui.h"
 #include "nvim/vim.h"
 
diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h
index ca09c2a6ec..1255f86103 100644
--- a/src/nvim/eval/userfunc.h
+++ b/src/nvim/eval/userfunc.h
@@ -1,8 +1,18 @@
 #ifndef NVIM_EVAL_USERFUNC_H
 #define NVIM_EVAL_USERFUNC_H
 
+#include 
+#include 
+
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/ex_cmds_defs.h"
+#include "nvim/garray.h"
+#include "nvim/hashtab.h"
+#include "nvim/pos.h"
+#include "nvim/types.h"
+
+struct funccal_entry;
 
 // From user function to hashitem and back.
 #define UF2HIKEY(fp) ((fp)->uf_name)
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index 3a625df988..54ff0a53d3 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -3,24 +3,42 @@
 
 // eval/vars.c: functions for dealing with variables
 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
 #include "nvim/ascii.h"
 #include "nvim/autocmd.h"
-#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/drawscreen.h"
 #include "nvim/eval.h"
 #include "nvim/eval/encode.h"
 #include "nvim/eval/funcs.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/eval/userfunc.h"
 #include "nvim/eval/vars.h"
 #include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_eval.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/hashtab.h"
+#include "nvim/macros.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/ops.h"
 #include "nvim/option.h"
-#include "nvim/screen.h"
+#include "nvim/os/os.h"
 #include "nvim/search.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
+#include "nvim/vim.h"
 #include "nvim/window.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/eval/vars.h b/src/nvim/eval/vars.h
index 73efc4938a..b87c9d62cb 100644
--- a/src/nvim/eval/vars.h
+++ b/src/nvim/eval/vars.h
@@ -1,7 +1,7 @@
 #ifndef NVIM_EVAL_VARS_H
 #define NVIM_EVAL_VARS_H
 
-#include "nvim/ex_cmds_defs.h"  // For exarg_T
+#include "nvim/ex_cmds_defs.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "eval/vars.h.generated.h"
diff --git a/src/nvim/event/libuv_process.c b/src/nvim/event/libuv_process.c
index f012bacdd9..c5d3b94c95 100644
--- a/src/nvim/event/libuv_process.c
+++ b/src/nvim/event/libuv_process.c
@@ -1,14 +1,14 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
-#include 
+#include 
 #include 
 
+#include "nvim/eval/typval.h"
 #include "nvim/event/libuv_process.h"
 #include "nvim/event/loop.h"
 #include "nvim/event/process.h"
-#include "nvim/event/rstream.h"
-#include "nvim/event/wstream.h"
+#include "nvim/event/stream.h"
 #include "nvim/log.h"
 #include "nvim/macros.h"
 #include "nvim/os/os.h"
diff --git a/src/nvim/event/libuv_process.h b/src/nvim/event/libuv_process.h
index 1132ce79ca..8f987847d8 100644
--- a/src/nvim/event/libuv_process.h
+++ b/src/nvim/event/libuv_process.h
@@ -3,6 +3,7 @@
 
 #include 
 
+#include "nvim/event/loop.h"
 #include "nvim/event/process.h"
 
 typedef struct libuv_process {
diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c
index 3329cbd574..934f5fc69a 100644
--- a/src/nvim/event/loop.c
+++ b/src/nvim/event/loop.c
@@ -1,13 +1,16 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
-#include 
+#include 
 #include 
+#include 
 #include 
 
+#include "nvim/event/defs.h"
 #include "nvim/event/loop.h"
-#include "nvim/event/process.h"
 #include "nvim/log.h"
+#include "nvim/memory.h"
+#include "nvim/os/time.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "event/loop.c.generated.h"
diff --git a/src/nvim/event/multiqueue.c b/src/nvim/event/multiqueue.c
index 40d20033e0..e05084b656 100644
--- a/src/nvim/event/multiqueue.c
+++ b/src/nvim/event/multiqueue.c
@@ -46,14 +46,14 @@
 // other sources and focus on a specific channel.
 
 #include 
-#include 
 #include 
-#include 
+#include 
 #include 
 
+#include "nvim/event/defs.h"
 #include "nvim/event/multiqueue.h"
+#include "nvim/lib/queue.h"
 #include "nvim/memory.h"
-#include "nvim/os/time.h"
 
 typedef struct multiqueue_item MultiQueueItem;
 struct multiqueue_item {
diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c
index e029f778f6..e74e95669d 100644
--- a/src/nvim/event/process.c
+++ b/src/nvim/event/process.c
@@ -2,20 +2,23 @@
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
 #include 
+#include 
+#include 
 #include 
 #include 
 
+#include "klib/klist.h"
 #include "nvim/event/libuv_process.h"
 #include "nvim/event/loop.h"
 #include "nvim/event/process.h"
-#include "nvim/event/rstream.h"
-#include "nvim/event/wstream.h"
 #include "nvim/globals.h"
 #include "nvim/log.h"
 #include "nvim/macros.h"
 #include "nvim/os/process.h"
 #include "nvim/os/pty_process.h"
 #include "nvim/os/shell.h"
+#include "nvim/os/time.h"
+#include "nvim/rbuffer.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "event/process.c.generated.h"
diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h
index 30254bfe07..26e03ff4f3 100644
--- a/src/nvim/event/process.h
+++ b/src/nvim/event/process.h
@@ -1,11 +1,20 @@
 #ifndef NVIM_EVENT_PROCESS_H
 #define NVIM_EVENT_PROCESS_H
 
+#include 
+#include 
+#include 
+
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
 #include "nvim/event/rstream.h"
+#include "nvim/event/stream.h"
 #include "nvim/event/wstream.h"
 
+struct process;
+
 typedef enum {
   kProcessTypeUv,
   kProcessTypePty,
diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c
index 2847788ef8..a88d62fd6b 100644
--- a/src/nvim/event/rstream.c
+++ b/src/nvim/event/rstream.c
@@ -3,17 +3,18 @@
 
 #include 
 #include 
+#include 
 #include 
-#include 
 #include 
 
-#include "nvim/ascii.h"
 #include "nvim/event/loop.h"
 #include "nvim/event/rstream.h"
+#include "nvim/event/stream.h"
 #include "nvim/log.h"
+#include "nvim/macros.h"
 #include "nvim/main.h"
-#include "nvim/memory.h"
-#include "nvim/vim.h"
+#include "nvim/os/os_defs.h"
+#include "nvim/rbuffer.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "event/rstream.c.generated.h"
diff --git a/src/nvim/event/signal.c b/src/nvim/event/signal.c
index 4a45a2ec2f..8256ca2091 100644
--- a/src/nvim/event/signal.c
+++ b/src/nvim/event/signal.c
@@ -1,6 +1,7 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
+#include 
 #include 
 
 #include "nvim/event/loop.h"
diff --git a/src/nvim/event/signal.h b/src/nvim/event/signal.h
index 7fe352edef..f9adf62c20 100644
--- a/src/nvim/event/signal.h
+++ b/src/nvim/event/signal.h
@@ -4,6 +4,9 @@
 #include 
 
 #include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
+
+struct signal_watcher;
 
 typedef struct signal_watcher SignalWatcher;
 typedef void (*signal_cb)(SignalWatcher *watcher, int signum, void *data);
diff --git a/src/nvim/event/socket.c b/src/nvim/event/socket.c
index 9ca3dcc276..10756015ad 100644
--- a/src/nvim/event/socket.c
+++ b/src/nvim/event/socket.c
@@ -2,23 +2,24 @@
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
 #include 
-#include 
+#include 
+#include 
+#include 
+#include 
 #include 
 
 #include "nvim/ascii.h"
 #include "nvim/charset.h"
 #include "nvim/event/loop.h"
-#include "nvim/event/rstream.h"
 #include "nvim/event/socket.h"
-#include "nvim/event/wstream.h"
+#include "nvim/event/stream.h"
+#include "nvim/gettext.h"
 #include "nvim/log.h"
 #include "nvim/macros.h"
 #include "nvim/main.h"
 #include "nvim/memory.h"
 #include "nvim/os/os.h"
 #include "nvim/path.h"
-#include "nvim/strings.h"
-#include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "event/socket.c.generated.h"
diff --git a/src/nvim/event/socket.h b/src/nvim/event/socket.h
index d30ae45502..c6fcdec4bb 100644
--- a/src/nvim/event/socket.h
+++ b/src/nvim/event/socket.h
@@ -4,9 +4,12 @@
 #include 
 
 #include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
 #include "nvim/event/rstream.h"
 #include "nvim/event/wstream.h"
 
+struct socket_watcher;
+
 #define ADDRESS_MAX_SIZE 256
 
 typedef struct socket_watcher SocketWatcher;
diff --git a/src/nvim/event/stream.c b/src/nvim/event/stream.c
index bfb7a551b9..0a4918636a 100644
--- a/src/nvim/event/stream.c
+++ b/src/nvim/event/stream.c
@@ -3,9 +3,11 @@
 
 #include 
 #include 
-#include 
+#include 
 #include 
+#include 
 
+#include "nvim/event/loop.h"
 #include "nvim/event/stream.h"
 #include "nvim/log.h"
 #include "nvim/macros.h"
diff --git a/src/nvim/event/stream.h b/src/nvim/event/stream.h
index f2858f0b6d..33d2d6e775 100644
--- a/src/nvim/event/stream.h
+++ b/src/nvim/event/stream.h
@@ -6,8 +6,11 @@
 #include 
 
 #include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
 #include "nvim/rbuffer.h"
 
+struct stream;
+
 typedef struct stream Stream;
 /// Type of function called when the Stream buffer is filled with data
 ///
diff --git a/src/nvim/event/time.h b/src/nvim/event/time.h
index a6de89ad6e..e84488fdd6 100644
--- a/src/nvim/event/time.h
+++ b/src/nvim/event/time.h
@@ -1,9 +1,13 @@
 #ifndef NVIM_EVENT_TIME_H
 #define NVIM_EVENT_TIME_H
 
+#include 
 #include 
 
 #include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
+
+struct time_watcher;
 
 typedef struct time_watcher TimeWatcher;
 typedef void (*time_cb)(TimeWatcher *watcher, void *data);
diff --git a/src/nvim/event/wstream.c b/src/nvim/event/wstream.c
index d81ffa5c15..65391ba5cf 100644
--- a/src/nvim/event/wstream.c
+++ b/src/nvim/event/wstream.c
@@ -3,15 +3,13 @@
 
 #include 
 #include 
-#include 
-#include 
 #include 
 
 #include "nvim/event/loop.h"
+#include "nvim/event/stream.h"
 #include "nvim/event/wstream.h"
-#include "nvim/log.h"
+#include "nvim/macros.h"
 #include "nvim/memory.h"
-#include "nvim/vim.h"
 
 #define DEFAULT_MAXMEM 1024 * 1024 * 2000
 
diff --git a/src/nvim/event/wstream.h b/src/nvim/event/wstream.h
index d599d29913..ef1311c619 100644
--- a/src/nvim/event/wstream.h
+++ b/src/nvim/event/wstream.h
@@ -2,12 +2,15 @@
 #define NVIM_EVENT_WSTREAM_H
 
 #include 
+#include 
 #include 
 #include 
 
 #include "nvim/event/loop.h"
 #include "nvim/event/stream.h"
 
+struct wbuffer;
+
 typedef struct wbuffer WBuffer;
 typedef void (*wbuffer_data_finalizer)(void *data);
 
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index c22f22366a..04af13e55c 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -4,20 +4,27 @@
 // ex_cmds.c: some functions for command line commands
 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
 #include 
+#include 
 
-#include "nvim/api/buffer.h"
-#include "nvim/api/private/defs.h"
+#include "auto/config.h"
+#include "klib/kvec.h"
 #include "nvim/arglist.h"
 #include "nvim/ascii.h"
+#include "nvim/autocmd.h"
 #include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/buffer_updates.h"
 #include "nvim/change.h"
+#include "nvim/channel.h"
 #include "nvim/charset.h"
 #include "nvim/cmdhist.h"
 #include "nvim/cursor.h"
@@ -27,22 +34,26 @@
 #include "nvim/drawscreen.h"
 #include "nvim/edit.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval.h"
 #include "nvim/ex_cmds.h"
 #include "nvim/ex_cmds2.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_eval.h"
 #include "nvim/ex_getln.h"
 #include "nvim/extmark.h"
 #include "nvim/fileio.h"
 #include "nvim/fold.h"
-#include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
 #include "nvim/help.h"
-#include "nvim/highlight.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/highlight_group.h"
 #include "nvim/indent.h"
 #include "nvim/input.h"
-#include "nvim/log.h"
+#include "nvim/macros.h"
 #include "nvim/main.h"
 #include "nvim/mark.h"
 #include "nvim/mbyte.h"
@@ -55,21 +66,22 @@
 #include "nvim/ops.h"
 #include "nvim/option.h"
 #include "nvim/optionstr.h"
+#include "nvim/os/fs_defs.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
 #include "nvim/os/shell.h"
 #include "nvim/os/time.h"
-#include "nvim/os_unix.h"
 #include "nvim/path.h"
 #include "nvim/plines.h"
 #include "nvim/profile.h"
 #include "nvim/quickfix.h"
 #include "nvim/regexp.h"
+#include "nvim/screen.h"
 #include "nvim/search.h"
 #include "nvim/spell.h"
 #include "nvim/strings.h"
-#include "nvim/syntax.h"
-#include "nvim/tag.h"
+#include "nvim/terminal.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/undo.h"
 #include "nvim/vim.h"
diff --git a/src/nvim/ex_cmds.h b/src/nvim/ex_cmds.h
index 3aaba9ce42..39bff3e35d 100644
--- a/src/nvim/ex_cmds.h
+++ b/src/nvim/ex_cmds.h
@@ -5,6 +5,7 @@
 
 #include "nvim/buffer_defs.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/ex_cmds_defs.h"
 #include "nvim/os/time.h"
 #include "nvim/pos.h"
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 1248251556..b6489e97b2 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -6,9 +6,9 @@
 /// Some more functions for command line commands
 
 #include 
-#include 
 #include 
 #include 
+#include 
 #include 
 
 #include "nvim/arglist.h"
@@ -16,30 +16,33 @@
 #include "nvim/autocmd.h"
 #include "nvim/buffer.h"
 #include "nvim/change.h"
-#include "nvim/charset.h"
+#include "nvim/channel.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/eval/vars.h"
 #include "nvim/ex_cmds.h"
 #include "nvim/ex_cmds2.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
-#include "nvim/ex_eval.h"
 #include "nvim/ex_getln.h"
 #include "nvim/fileio.h"
+#include "nvim/gettext.h"
 #include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
+#include "nvim/macros.h"
 #include "nvim/mark.h"
-#include "nvim/mbyte.h"
+#include "nvim/memline_defs.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/move.h"
 #include "nvim/normal.h"
-#include "nvim/ops.h"
 #include "nvim/option.h"
-#include "nvim/os/fs_defs.h"
-#include "nvim/os_unix.h"
+#include "nvim/os/os_defs.h"
 #include "nvim/path.h"
+#include "nvim/pos.h"
 #include "nvim/quickfix.h"
 #include "nvim/runtime.h"
-#include "nvim/strings.h"
 #include "nvim/undo.h"
 #include "nvim/vim.h"
 #include "nvim/window.h"
diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index 378271c107..629aaf14cf 100644
--- a/src/nvim/ex_cmds_defs.h
+++ b/src/nvim/ex_cmds_defs.h
@@ -6,7 +6,7 @@
 
 #include "nvim/eval/typval.h"
 #include "nvim/normal.h"
-#include "nvim/pos.h"      // for linenr_T
+#include "nvim/pos.h"
 #include "nvim/regexp_defs.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index b875aafaad..8e1e5afb2b 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -4,58 +4,57 @@
 // ex_docmd.c: functions for executing an Ex command line.
 
 #include 
+#include 
 #include 
+#include 
 #include 
+#include 
+#include 
 #include 
 #include 
 
+#include "auto/config.h"
 #include "nvim/arglist.h"
 #include "nvim/ascii.h"
+#include "nvim/autocmd.h"
 #include "nvim/buffer.h"
 #include "nvim/change.h"
 #include "nvim/charset.h"
 #include "nvim/cmdexpand.h"
-#include "nvim/cmdhist.h"
 #include "nvim/cursor.h"
 #include "nvim/debugger.h"
-#include "nvim/diff.h"
 #include "nvim/digraph.h"
 #include "nvim/drawscreen.h"
 #include "nvim/edit.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/eval/userfunc.h"
-#include "nvim/eval/vars.h"
-#include "nvim/event/rstream.h"
-#include "nvim/event/wstream.h"
+#include "nvim/event/loop.h"
 #include "nvim/ex_cmds.h"
 #include "nvim/ex_cmds2.h"
 #include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_eval.h"
 #include "nvim/ex_getln.h"
-#include "nvim/ex_session.h"
 #include "nvim/file_search.h"
 #include "nvim/fileio.h"
 #include "nvim/fold.h"
-#include "nvim/func_attr.h"
 #include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
 #include "nvim/globals.h"
-#include "nvim/hardcopy.h"
-#include "nvim/help.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/highlight_group.h"
 #include "nvim/input.h"
 #include "nvim/keycodes.h"
-#include "nvim/locale.h"
-#include "nvim/lua/executor.h"
+#include "nvim/macros.h"
 #include "nvim/main.h"
-#include "nvim/mapping.h"
 #include "nvim/mark.h"
-#include "nvim/match.h"
 #include "nvim/mbyte.h"
+#include "nvim/memfile_defs.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
-#include "nvim/menu.h"
 #include "nvim/message.h"
 #include "nvim/mouse.h"
 #include "nvim/move.h"
@@ -65,29 +64,25 @@
 #include "nvim/optionstr.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
-#include "nvim/os/time.h"
-#include "nvim/os_unix.h"
+#include "nvim/os/shell.h"
 #include "nvim/path.h"
 #include "nvim/popupmenu.h"
+#include "nvim/pos.h"
 #include "nvim/profile.h"
 #include "nvim/quickfix.h"
 #include "nvim/regexp.h"
+#include "nvim/runtime.h"
+#include "nvim/screen.h"
 #include "nvim/search.h"
 #include "nvim/shada.h"
-#include "nvim/sign.h"
-#include "nvim/spell.h"
-#include "nvim/spellfile.h"
 #include "nvim/state.h"
 #include "nvim/statusline.h"
 #include "nvim/strings.h"
-#include "nvim/syntax.h"
 #include "nvim/tag.h"
-#include "nvim/terminal.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/undo.h"
-#include "nvim/undo_defs.h"
 #include "nvim/usercmd.h"
-#include "nvim/version.h"
 #include "nvim/vim.h"
 #include "nvim/window.h"
 
diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h
index 283501cf76..19dd9e96ca 100644
--- a/src/nvim/ex_docmd.h
+++ b/src/nvim/ex_docmd.h
@@ -1,6 +1,9 @@
 #ifndef NVIM_EX_DOCMD_H
 #define NVIM_EX_DOCMD_H
 
+#include 
+
+#include "nvim/buffer_defs.h"
 #include "nvim/ex_cmds_defs.h"
 #include "nvim/globals.h"
 
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index bde2f3c801..cd80da729b 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -10,19 +10,30 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 
 #include "nvim/ascii.h"
 #include "nvim/charset.h"
 #include "nvim/debugger.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/eval/userfunc.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_eval.h"
+#include "nvim/ex_eval_defs.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
+#include "nvim/option_defs.h"
+#include "nvim/pos.h"
 #include "nvim/regexp.h"
 #include "nvim/runtime.h"
 #include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/ex_eval.h b/src/nvim/ex_eval.h
index 9e3ac5e9c1..d3053ae0d4 100644
--- a/src/nvim/ex_eval.h
+++ b/src/nvim/ex_eval.h
@@ -1,7 +1,7 @@
 #ifndef NVIM_EX_EVAL_H
 #define NVIM_EX_EVAL_H
 
-#include "nvim/ex_cmds_defs.h"  // for exarg_T
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_eval_defs.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/ex_eval_defs.h b/src/nvim/ex_eval_defs.h
index 9da0c9ad12..6b3c426722 100644
--- a/src/nvim/ex_eval_defs.h
+++ b/src/nvim/ex_eval_defs.h
@@ -1,7 +1,7 @@
 #ifndef NVIM_EX_EVAL_DEFS_H
 #define NVIM_EX_EVAL_DEFS_H
 
-#include "nvim/pos.h"           // for linenr_T
+#include "nvim/pos.h"
 
 /// There is no CSF_IF, the lack of CSF_WHILE, CSF_FOR and CSF_TRY means ":if"
 /// was used.
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 1d242e4ed2..0520c0e4a0 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -5,44 +5,45 @@
 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
+#include 
 
 #include "klib/kvec.h"
 #include "nvim/api/extmark.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/api/private/helpers.h"
 #include "nvim/api/vim.h"
 #include "nvim/arabic.h"
 #include "nvim/ascii.h"
-#include "nvim/assert.h"
+#include "nvim/autocmd.h"
 #include "nvim/buffer.h"
 #include "nvim/charset.h"
 #include "nvim/cmdexpand.h"
 #include "nvim/cmdhist.h"
 #include "nvim/cursor.h"
-#include "nvim/cursor_shape.h"
 #include "nvim/digraph.h"
 #include "nvim/drawscreen.h"
 #include "nvim/edit.h"
 #include "nvim/eval.h"
-#include "nvim/event/loop.h"
+#include "nvim/eval/typval.h"
 #include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_eval.h"
 #include "nvim/ex_getln.h"
-#include "nvim/fileio.h"
-#include "nvim/func_attr.h"
+#include "nvim/extmark.h"
 #include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
 #include "nvim/globals.h"
 #include "nvim/grid.h"
-#include "nvim/highlight.h"
 #include "nvim/highlight_defs.h"
 #include "nvim/highlight_group.h"
-#include "nvim/indent.h"
 #include "nvim/keycodes.h"
-#include "nvim/log.h"
-#include "nvim/main.h"
+#include "nvim/macros.h"
 #include "nvim/mapping.h"
 #include "nvim/mark.h"
 #include "nvim/mbyte.h"
@@ -51,15 +52,18 @@
 #include "nvim/message.h"
 #include "nvim/mouse.h"
 #include "nvim/move.h"
+#include "nvim/normal.h"
 #include "nvim/ops.h"
 #include "nvim/option.h"
 #include "nvim/optionstr.h"
 #include "nvim/os/input.h"
-#include "nvim/os/time.h"
+#include "nvim/os/os.h"
 #include "nvim/path.h"
 #include "nvim/popupmenu.h"
+#include "nvim/pos.h"
 #include "nvim/profile.h"
 #include "nvim/regexp.h"
+#include "nvim/screen.h"
 #include "nvim/search.h"
 #include "nvim/state.h"
 #include "nvim/strings.h"
diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h
index 919a6c8f11..916b695a35 100644
--- a/src/nvim/ex_getln.h
+++ b/src/nvim/ex_getln.h
@@ -1,10 +1,16 @@
 #ifndef NVIM_EX_GETLN_H
 #define NVIM_EX_GETLN_H
 
+#include 
+
+#include "klib/kvec.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/ex_cmds_defs.h"
 #include "nvim/types.h"
 
+struct cmdline_info;
+
 /// Command-line colors: one chunk
 ///
 /// Defines a region which has the same highlighting.
diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c
index 887ba5400a..2a80537a50 100644
--- a/src/nvim/ex_session.c
+++ b/src/nvim/ex_session.c
@@ -9,32 +9,36 @@
 
 #include 
 #include 
+#include 
 #include 
-#include 
+#include 
 #include 
 
 #include "nvim/arglist.h"
 #include "nvim/ascii.h"
 #include "nvim/buffer.h"
-#include "nvim/cursor.h"
-#include "nvim/edit.h"
 #include "nvim/eval.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_getln.h"
 #include "nvim/ex_session.h"
 #include "nvim/file_search.h"
 #include "nvim/fileio.h"
 #include "nvim/fold.h"
+#include "nvim/garray.h"
+#include "nvim/gettext.h"
 #include "nvim/globals.h"
-#include "nvim/keycodes.h"
+#include "nvim/macros.h"
 #include "nvim/mapping.h"
-#include "nvim/move.h"
+#include "nvim/mark_defs.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/option.h"
-#include "nvim/os/input.h"
 #include "nvim/os/os.h"
-#include "nvim/os/time.h"
 #include "nvim/path.h"
+#include "nvim/pos.h"
 #include "nvim/runtime.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"
 #include "nvim/window.h"
 
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c
index 015799be06..3e059bcc6c 100644
--- a/src/nvim/extmark.c
+++ b/src/nvim/extmark.c
@@ -29,20 +29,21 @@
 // code for redrawing the line with the deleted decoration.
 
 #include 
+#include 
 
-#include "klib/kbtree.h"
-#include "nvim/api/extmark.h"
 #include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/buffer_updates.h"
-#include "nvim/charset.h"
 #include "nvim/decoration.h"
 #include "nvim/extmark.h"
+#include "nvim/extmark_defs.h"
 #include "nvim/globals.h"
 #include "nvim/map.h"
+#include "nvim/marktree.h"
 #include "nvim/memline.h"
+#include "nvim/memory.h"
 #include "nvim/pos.h"
 #include "nvim/undo.h"
-#include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "extmark.c.generated.h"
diff --git a/src/nvim/extmark.h b/src/nvim/extmark.h
index c144504076..657e99a938 100644
--- a/src/nvim/extmark.h
+++ b/src/nvim/extmark.h
@@ -1,11 +1,18 @@
 #ifndef NVIM_EXTMARK_H
 #define NVIM_EXTMARK_H
 
+#include 
+#include 
+#include 
+
+#include "klib/kvec.h"
 #include "nvim/buffer_defs.h"
 #include "nvim/decoration.h"
 #include "nvim/extmark_defs.h"
+#include "nvim/macros.h"
 #include "nvim/marktree.h"
 #include "nvim/pos.h"
+#include "nvim/types.h"
 
 EXTERN int extmark_splice_pending INIT(= 0);
 
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index 1434ba415b..1d891799a5 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -47,25 +47,30 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
 
 #include "nvim/ascii.h"
 #include "nvim/autocmd.h"
-#include "nvim/charset.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/file_search.h"
-#include "nvim/fileio.h"
+#include "nvim/gettext.h"
 #include "nvim/globals.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/option.h"
 #include "nvim/os/fs_defs.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
-#include "nvim/os_unix.h"
 #include "nvim/path.h"
 #include "nvim/strings.h"
-#include "nvim/tag.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"
 #include "nvim/window.h"
 
diff --git a/src/nvim/file_search.h b/src/nvim/file_search.h
index 4d4e723922..b69a6fa154 100644
--- a/src/nvim/file_search.h
+++ b/src/nvim/file_search.h
@@ -1,10 +1,10 @@
 #ifndef NVIM_FILE_SEARCH_H
 #define NVIM_FILE_SEARCH_H
 
-#include   // for size_t
+#include 
 
-#include "nvim/globals.h"  // for CdScope
-#include "nvim/types.h"  // for char_u
+#include "nvim/globals.h"
+#include "nvim/types.h"
 
 // Flags for find_file_*() functions.
 #define FINDFILE_FILE   0       // only files
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index e1ffc27d5b..6c5469d020 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -6,62 +6,68 @@
 #include 
 #include 
 #include 
+#include 
 #include 
+#include 
 #include 
+#include 
 #include 
+#include 
+#include 
 
-#include "nvim/api/private/helpers.h"
+#include "auto/config.h"
 #include "nvim/ascii.h"
+#include "nvim/autocmd.h"
 #include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/buffer_updates.h"
 #include "nvim/change.h"
-#include "nvim/charset.h"
 #include "nvim/cursor.h"
 #include "nvim/diff.h"
 #include "nvim/drawscreen.h"
 #include "nvim/edit.h"
 #include "nvim/eval.h"
-#include "nvim/eval/typval.h"
-#include "nvim/eval/userfunc.h"
 #include "nvim/ex_cmds.h"
-#include "nvim/ex_docmd.h"
 #include "nvim/ex_eval.h"
 #include "nvim/fileio.h"
 #include "nvim/fold.h"
-#include "nvim/func_attr.h"
 #include "nvim/garray.h"
 #include "nvim/getchar.h"
-#include "nvim/hashtab.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/iconv.h"
 #include "nvim/input.h"
+#include "nvim/log.h"
+#include "nvim/macros.h"
 #include "nvim/mbyte.h"
 #include "nvim/memfile.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/move.h"
-#include "nvim/normal.h"
 #include "nvim/option.h"
 #include "nvim/optionstr.h"
+#include "nvim/os/fs_defs.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
-#include "nvim/os/os_defs.h"
 #include "nvim/os/time.h"
 #include "nvim/os_unix.h"
 #include "nvim/path.h"
-#include "nvim/quickfix.h"
+#include "nvim/pos.h"
 #include "nvim/regexp.h"
-#include "nvim/search.h"
+#include "nvim/screen.h"
 #include "nvim/sha256.h"
 #include "nvim/shada.h"
-#include "nvim/state.h"
 #include "nvim/strings.h"
 #include "nvim/types.h"
 #include "nvim/ui.h"
-#include "nvim/ui_compositor.h"
 #include "nvim/undo.h"
 #include "nvim/vim.h"
-#include "nvim/window.h"
+
+#ifdef OPEN_CHR_FILES
+# include "nvim/charset.h"
+#endif
 
 #define BUFSIZE         8192    // size of normal write buffer
 #define SMBUFSIZE       256     // size of emergency write buffer
diff --git a/src/nvim/fileio.h b/src/nvim/fileio.h
index ae3c51f1bc..dabcda5bf2 100644
--- a/src/nvim/fileio.h
+++ b/src/nvim/fileio.h
@@ -3,6 +3,7 @@
 
 #include "nvim/buffer_defs.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/garray.h"
 #include "nvim/os/os.h"
 
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index dd74971ab5..940c26ad02 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -5,10 +5,15 @@
 
 // fold.c: code for folding
 
+#include 
 #include 
+#include 
+#include 
+#include 
 #include 
 
 #include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/buffer_updates.h"
 #include "nvim/change.h"
 #include "nvim/charset.h"
@@ -16,14 +21,17 @@
 #include "nvim/diff.h"
 #include "nvim/drawscreen.h"
 #include "nvim/eval.h"
-#include "nvim/ex_docmd.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/ex_session.h"
 #include "nvim/extmark.h"
 #include "nvim/fold.h"
-#include "nvim/func_attr.h"
 #include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/indent.h"
 #include "nvim/mark.h"
+#include "nvim/mbyte.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
@@ -35,6 +43,7 @@
 #include "nvim/search.h"
 #include "nvim/strings.h"
 #include "nvim/syntax.h"
+#include "nvim/types.h"
 #include "nvim/undo.h"
 #include "nvim/vim.h"
 
diff --git a/src/nvim/fold.h b/src/nvim/fold.h
index 395cd8e30a..9c3a34ab31 100644
--- a/src/nvim/fold.h
+++ b/src/nvim/fold.h
@@ -5,6 +5,7 @@
 
 #include "nvim/buffer_defs.h"
 #include "nvim/garray.h"
+#include "nvim/macros.h"
 #include "nvim/pos.h"
 #include "nvim/types.h"
 
diff --git a/src/nvim/garray.c b/src/nvim/garray.c
index eefbfed3ef..0f11868031 100644
--- a/src/nvim/garray.c
+++ b/src/nvim/garray.c
@@ -5,22 +5,17 @@
 ///
 /// Functions for handling growing arrays.
 
-#include 
 #include 
 
-#include "nvim/ascii.h"
 #include "nvim/garray.h"
 #include "nvim/log.h"
 #include "nvim/memory.h"
 #include "nvim/path.h"
 #include "nvim/strings.h"
-#include "nvim/vim.h"
-
-// #include "nvim/globals.h"
-#include "nvim/memline.h"
+#include "nvim/types.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "garray.c.generated.h"
+# include "garray.c.generated.h"  // IWYU pragma: export
 #endif
 
 /// Clear an allocated growing array.
diff --git a/src/nvim/garray.h b/src/nvim/garray.h
index 0281678925..3e2650b410 100644
--- a/src/nvim/garray.h
+++ b/src/nvim/garray.h
@@ -1,9 +1,11 @@
 #ifndef NVIM_GARRAY_H
 #define NVIM_GARRAY_H
 
-#include   // for size_t
+#include 
+#include 
 
 #include "nvim/log.h"
+#include "nvim/memory.h"
 #include "nvim/types.h"
 
 /// Structure used for growing arrays.
diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua
index 6894b7a6df..d4fe455f82 100644
--- a/src/nvim/generators/gen_api_dispatch.lua
+++ b/src/nvim/generators/gen_api_dispatch.lua
@@ -189,6 +189,35 @@ funcs_metadata_output:close()
 -- start building the dispatch wrapper output
 local output = io.open(dispatch_outputf, 'wb')
 
+
+-- ===========================================================================
+-- NEW API FILES MUST GO HERE.
+--
+--  When creating a new API file, you must include it here,
+--  so that the dispatcher can find the C functions that you are creating!
+-- ===========================================================================
+output:write([[
+#include "nvim/log.h"
+#include "nvim/map.h"
+#include "nvim/msgpack_rpc/helpers.h"
+#include "nvim/vim.h"
+
+#include "nvim/api/autocmd.h"
+#include "nvim/api/buffer.h"
+#include "nvim/api/command.h"
+#include "nvim/api/deprecated.h"
+#include "nvim/api/extmark.h"
+#include "nvim/api/options.h"
+#include "nvim/api/tabpage.h"
+#include "nvim/api/ui.h"
+#include "nvim/api/vim.h"
+#include "nvim/api/vimscript.h"
+#include "nvim/api/win_config.h"
+#include "nvim/api/window.h"
+#include "nvim/ui_client.h"
+
+]])
+
 local function real_type(type)
   local rv = type
   local rmatch = string.match(type, "Dict%(([_%w]+)%)")
diff --git a/src/nvim/generators/gen_eval.lua b/src/nvim/generators/gen_eval.lua
index 8e6d1f2634..9159a3a200 100644
--- a/src/nvim/generators/gen_eval.lua
+++ b/src/nvim/generators/gen_eval.lua
@@ -27,6 +27,31 @@ local hashy = require'generators.hashy'
 
 local hashpipe = io.open(funcsfname, 'wb')
 
+hashpipe:write([[
+#include "nvim/arglist.h"
+#include "nvim/cmdexpand.h"
+#include "nvim/cmdhist.h"
+#include "nvim/digraph.h"
+#include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/vars.h"
+#include "nvim/ex_docmd.h"
+#include "nvim/ex_getln.h"
+#include "nvim/fold.h"
+#include "nvim/getchar.h"
+#include "nvim/insexpand.h"
+#include "nvim/mapping.h"
+#include "nvim/match.h"
+#include "nvim/mbyte.h"
+#include "nvim/menu.h"
+#include "nvim/move.h"
+#include "nvim/quickfix.h"
+#include "nvim/search.h"
+#include "nvim/sign.h"
+#include "nvim/testing.h"
+
+]])
+
 local funcs = require('eval').funcs
 for _, func in pairs(funcs) do
   if func.float_func then
diff --git a/src/nvim/generators/gen_ex_cmds.lua b/src/nvim/generators/gen_ex_cmds.lua
index 255c415a4d..ac60b9f8e9 100644
--- a/src/nvim/generators/gen_ex_cmds.lua
+++ b/src/nvim/generators/gen_ex_cmds.lua
@@ -49,6 +49,43 @@ enumfile:write([[
 typedef enum CMD_index {
 ]])
 defsfile:write(string.format([[
+#include "nvim/arglist.h"
+#include "nvim/autocmd.h"
+#include "nvim/buffer.h"
+#include "nvim/cmdhist.h"
+#include "nvim/debugger.h"
+#include "nvim/diff.h"
+#include "nvim/digraph.h"
+#include "nvim/eval.h"
+#include "nvim/eval/userfunc.h"
+#include "nvim/eval/vars.h"
+#include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds2.h"
+#include "nvim/ex_docmd.h"
+#include "nvim/ex_eval.h"
+#include "nvim/ex_session.h"
+#include "nvim/hardcopy.h"
+#include "nvim/help.h"
+#include "nvim/locale.h"
+#include "nvim/lua/executor.h"
+#include "nvim/mapping.h"
+#include "nvim/mark.h"
+#include "nvim/match.h"
+#include "nvim/menu.h"
+#include "nvim/message.h"
+#include "nvim/ops.h"
+#include "nvim/option.h"
+#include "nvim/profile.h"
+#include "nvim/quickfix.h"
+#include "nvim/runtime.h"
+#include "nvim/sign.h"
+#include "nvim/spell.h"
+#include "nvim/spellfile.h"
+#include "nvim/syntax.h"
+#include "nvim/undo.h"
+#include "nvim/usercmd.h"
+#include "nvim/version.h"
+
 static const int command_count = %u;
 static CommandDefinition cmdnames[%u] = {
 ]], #defs, #defs))
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index f76107c401..bd83624185 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -7,11 +7,15 @@
 #include 
 #include 
 #include 
+#include 
+#include 
+#include 
 #include 
 
+#include "lauxlib.h"
+#include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/ascii.h"
-#include "nvim/assert.h"
 #include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/cursor.h"
@@ -19,16 +23,21 @@
 #include "nvim/edit.h"
 #include "nvim/eval.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
 #include "nvim/ex_cmds.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_getln.h"
 #include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/input.h"
 #include "nvim/insexpand.h"
 #include "nvim/keycodes.h"
 #include "nvim/lua/executor.h"
+#include "nvim/macros.h"
 #include "nvim/main.h"
 #include "nvim/mapping.h"
 #include "nvim/mbyte.h"
@@ -44,8 +53,11 @@
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
 #include "nvim/plines.h"
+#include "nvim/pos.h"
+#include "nvim/screen.h"
 #include "nvim/state.h"
 #include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/undo.h"
 #include "nvim/vim.h"
diff --git a/src/nvim/grid.c b/src/nvim/grid.c
index 2b73ff895d..0320360cd8 100644
--- a/src/nvim/grid.c
+++ b/src/nvim/grid.c
@@ -11,9 +11,19 @@
 //
 // The grid_*() functions write to the screen and handle updating grid->lines[].
 
+#include 
+#include 
+#include 
+
 #include "nvim/arabic.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/globals.h"
 #include "nvim/grid.h"
 #include "nvim/highlight.h"
+#include "nvim/log.h"
+#include "nvim/message.h"
+#include "nvim/option_defs.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/vim.h"
 
diff --git a/src/nvim/grid.h b/src/nvim/grid.h
index 0e79183c14..deb3d3785d 100644
--- a/src/nvim/grid.h
+++ b/src/nvim/grid.h
@@ -2,11 +2,14 @@
 #define NVIM_GRID_H
 
 #include 
+#include 
 
 #include "nvim/ascii.h"
 #include "nvim/buffer_defs.h"
 #include "nvim/grid_defs.h"
+#include "nvim/macros.h"
 #include "nvim/mbyte.h"
+#include "nvim/memory.h"
 
 /// By default, all windows are drawn on a single rectangular grid, represented by
 /// this ScreenGrid instance. In multigrid mode each window will have its own
diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c
index b769f6215c..93976542f7 100644
--- a/src/nvim/hardcopy.c
+++ b/src/nvim/hardcopy.c
@@ -4,20 +4,28 @@
 // hardcopy.c: printing to paper
 
 #include 
-#include 
+#include 
+#include 
+#include 
+#include 
 #include 
 
 #include "nvim/ascii.h"
 #include "nvim/buffer.h"
 #include "nvim/charset.h"
 #include "nvim/eval.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/fileio.h"
 #include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/grid.h"
 #include "nvim/hardcopy.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/highlight_group.h"
 #include "nvim/indent.h"
+#include "nvim/macros.h"
 #include "nvim/mbyte.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
@@ -25,7 +33,9 @@
 #include "nvim/option.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
+#include "nvim/os/time.h"
 #include "nvim/path.h"
+#include "nvim/pos.h"
 #include "nvim/runtime.h"
 #include "nvim/statusline.h"
 #include "nvim/strings.h"
diff --git a/src/nvim/hardcopy.h b/src/nvim/hardcopy.h
index ce562cd3e6..673d559ecf 100644
--- a/src/nvim/hardcopy.h
+++ b/src/nvim/hardcopy.h
@@ -2,11 +2,11 @@
 #define NVIM_HARDCOPY_H
 
 #include 
-#include   // for size_t
+#include 
 
-#include "nvim/ex_cmds_defs.h"  // for exarg_T
-#include "nvim/globals.h"  // for TriState
-#include "nvim/types.h"  // for char_u
+#include "nvim/ex_cmds_defs.h"
+#include "nvim/globals.h"
+#include "nvim/types.h"
 
 // Structure to hold printing color and font attributes.
 typedef struct {
diff --git a/src/nvim/help.c b/src/nvim/help.c
index 5cc2cda52b..7ea7dfb709 100644
--- a/src/nvim/help.c
+++ b/src/nvim/help.c
@@ -3,26 +3,39 @@
 
 // help.c: functions for Vim help
 
+#include 
 #include 
 #include 
+#include 
 
+#include "nvim/ascii.h"
 #include "nvim/buffer.h"
 #include "nvim/charset.h"
 #include "nvim/cmdexpand.h"
 #include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/fileio.h"
 #include "nvim/garray.h"
+#include "nvim/gettext.h"
 #include "nvim/globals.h"
 #include "nvim/help.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline.h"
 #include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/option.h"
 #include "nvim/optionstr.h"
 #include "nvim/os/input.h"
+#include "nvim/os/os.h"
 #include "nvim/path.h"
+#include "nvim/pos.h"
+#include "nvim/runtime.h"
 #include "nvim/strings.h"
 #include "nvim/syntax.h"
 #include "nvim/tag.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"
 #include "nvim/window.h"
 
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index c992731ee3..774551242f 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -3,18 +3,31 @@
 
 // highlight.c: low level code for UI and syntax highlighting
 
+#include 
+#include 
+#include 
+#include 
+
+#include "klib/kvec.h"
+#include "lauxlib.h"
 #include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/decoration_provider.h"
 #include "nvim/drawscreen.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/highlight.h"
 #include "nvim/highlight_defs.h"
 #include "nvim/highlight_group.h"
+#include "nvim/log.h"
 #include "nvim/lua/executor.h"
+#include "nvim/macros.h"
 #include "nvim/map.h"
+#include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/option.h"
 #include "nvim/popupmenu.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/vim.h"
 
diff --git a/src/nvim/highlight.h b/src/nvim/highlight.h
index e85e3859e2..4da7dd65bb 100644
--- a/src/nvim/highlight.h
+++ b/src/nvim/highlight.h
@@ -6,6 +6,7 @@
 #include "nvim/api/private/defs.h"
 #include "nvim/buffer_defs.h"
 #include "nvim/highlight_defs.h"
+#include "nvim/option_defs.h"
 #include "nvim/ui.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index 65e3073303..0c7ff39197 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -3,23 +3,45 @@
 
 // highlight_group.c: code for managing highlight groups
 
+#include 
 #include 
+#include 
+#include 
+#include 
+#include 
 
+#include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
+#include "nvim/ascii.h"
 #include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/cursor_shape.h"
+#include "nvim/decoration_provider.h"
 #include "nvim/drawscreen.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/eval/vars.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
-#include "nvim/fold.h"
+#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
 #include "nvim/highlight.h"
 #include "nvim/highlight_group.h"
 #include "nvim/lua/executor.h"
-#include "nvim/match.h"
+#include "nvim/macros.h"
+#include "nvim/map.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/option.h"
+#include "nvim/os/time.h"
 #include "nvim/runtime.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
+#include "nvim/ui.h"
+#include "nvim/vim.h"
 
 /// \addtogroup SG_SET
 /// @{
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index 0bf36c42f4..f6b1b81780 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -2,8 +2,10 @@
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
 #include 
-#include 
+#include 
 #include 
+#include 
+#include 
 
 #include "nvim/ascii.h"
 #include "nvim/assert.h"
@@ -13,21 +15,28 @@
 #include "nvim/cursor.h"
 #include "nvim/edit.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/extmark.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/indent.h"
 #include "nvim/indent_c.h"
 #include "nvim/mark.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/move.h"
 #include "nvim/option.h"
 #include "nvim/plines.h"
+#include "nvim/pos.h"
 #include "nvim/regexp.h"
 #include "nvim/screen.h"
 #include "nvim/search.h"
 #include "nvim/strings.h"
 #include "nvim/textformat.h"
+#include "nvim/types.h"
 #include "nvim/undo.h"
+#include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "indent.c.generated.h"
diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c
index 5a38fb5aae..77fe176de1 100644
--- a/src/nvim/indent_c.c
+++ b/src/nvim/indent_c.c
@@ -1,21 +1,29 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
-#include 
 #include 
+#include 
+#include 
+#include 
+#include 
 
 #include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/cursor.h"
 #include "nvim/edit.h"
+#include "nvim/globals.h"
 #include "nvim/indent.h"
 #include "nvim/indent_c.h"
+#include "nvim/macros.h"
 #include "nvim/mark.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
 #include "nvim/option.h"
+#include "nvim/pos.h"
 #include "nvim/search.h"
 #include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"
 
 // Find result cache for cpp_baseclass
diff --git a/src/nvim/input.c b/src/nvim/input.c
index 681d9d5f9c..0f0af9d5f0 100644
--- a/src/nvim/input.c
+++ b/src/nvim/input.c
@@ -4,21 +4,29 @@
 // input.c: high level functions for prompting the user or input
 // like yes/no or number prompts.
 
-#include 
 #include 
+#include 
 
+#include "nvim/ascii.h"
+#include "nvim/event/multiqueue.h"
 #include "nvim/func_attr.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/input.h"
+#include "nvim/keycodes.h"
 #include "nvim/mbyte.h"
 #include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/mouse.h"
 #include "nvim/os/input.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "input.c.generated.h"
+# include "input.c.generated.h"  // IWYU pragma: export
 #endif
 
 /// Ask for a reply from the user, 'y' or 'n'
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index bc10785152..ed851683fd 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -4,11 +4,15 @@
 // insexpand.c: functions for Insert mode completion
 
 #include 
-#include 
+#include 
 #include 
+#include 
+#include 
+#include 
 #include 
 
 #include "nvim/ascii.h"
+#include "nvim/autocmd.h"
 #include "nvim/buffer.h"
 #include "nvim/change.h"
 #include "nvim/charset.h"
@@ -18,37 +22,48 @@
 #include "nvim/edit.h"
 #include "nvim/eval.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/eval/userfunc.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_eval.h"
 #include "nvim/ex_getln.h"
 #include "nvim/fileio.h"
+#include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/indent.h"
 #include "nvim/indent_c.h"
 #include "nvim/insexpand.h"
 #include "nvim/keycodes.h"
+#include "nvim/macros.h"
+#include "nvim/mark.h"
 #include "nvim/mbyte.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/move.h"
 #include "nvim/option.h"
+#include "nvim/os/fs.h"
 #include "nvim/os/input.h"
 #include "nvim/os/time.h"
 #include "nvim/path.h"
 #include "nvim/popupmenu.h"
+#include "nvim/pos.h"
 #include "nvim/regexp.h"
+#include "nvim/screen.h"
 #include "nvim/search.h"
 #include "nvim/spell.h"
 #include "nvim/state.h"
 #include "nvim/strings.h"
 #include "nvim/tag.h"
 #include "nvim/textformat.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/undo.h"
 #include "nvim/vim.h"
-#include "nvim/window.h"
 
 // Definitions used for CTRL-X submode.
 // Note: If you change CTRL-X submode, you must also maintain ctrl_x_msgs[]
diff --git a/src/nvim/insexpand.h b/src/nvim/insexpand.h
index c394468a45..83ba14e0d2 100644
--- a/src/nvim/insexpand.h
+++ b/src/nvim/insexpand.h
@@ -1,6 +1,9 @@
 #ifndef NVIM_INSEXPAND_H
 #define NVIM_INSEXPAND_H
 
+#include 
+
+#include "nvim/macros.h"
 #include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/keycodes.c b/src/nvim/keycodes.c
index de3c1dbf84..cec8d1e603 100644
--- a/src/nvim/keycodes.c
+++ b/src/nvim/keycodes.c
@@ -4,16 +4,25 @@
 #include 
 #include 
 #include 
+#include 
+#include 
+#include 
 
 #include "nvim/ascii.h"
 #include "nvim/charset.h"
-#include "nvim/edit.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/eval/vars.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/keycodes.h"
+#include "nvim/log.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/mouse.h"
 #include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/keycodes.h b/src/nvim/keycodes.h
index c4d984ee17..7842dee92c 100644
--- a/src/nvim/keycodes.h
+++ b/src/nvim/keycodes.h
@@ -1,6 +1,10 @@
 #ifndef NVIM_KEYCODES_H
 #define NVIM_KEYCODES_H
 
+#include 
+
+#include "nvim/ascii.h"
+#include "nvim/option_defs.h"
 #include "nvim/strings.h"
 
 // Keycode definitions for special keys.
diff --git a/src/nvim/linematch.c b/src/nvim/linematch.c
index 04209bb836..9897c92ac5 100644
--- a/src/nvim/linematch.c
+++ b/src/nvim/linematch.c
@@ -1,4 +1,10 @@
+#include 
+#include 
+#include 
+#include 
+
 #include "nvim/linematch.h"
+#include "nvim/macros.h"
 #include "nvim/memory.h"
 #include "nvim/vim.h"
 
diff --git a/src/nvim/locale.c b/src/nvim/locale.c
index f2db9fcc59..c07420be1d 100644
--- a/src/nvim/locale.c
+++ b/src/nvim/locale.c
@@ -3,8 +3,10 @@
 
 // locale.c: functions for language/locale configuration
 
-#include "auto/config.h"
+#include 
+#include 
 
+#include "auto/config.h"
 #ifdef HAVE_LOCALE_H
 # include 
 #endif
@@ -13,8 +15,11 @@
 #include "nvim/buffer.h"
 #include "nvim/charset.h"
 #include "nvim/eval.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/garray.h"
+#include "nvim/gettext.h"
 #include "nvim/locale.h"
+#include "nvim/macros.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/option.h"
@@ -23,6 +28,7 @@
 #include "nvim/path.h"
 #include "nvim/profile.h"
 #include "nvim/types.h"
+#include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "locale.c.generated.h"
diff --git a/src/nvim/log.c b/src/nvim/log.c
index 9bdf327430..2c214aa32d 100644
--- a/src/nvim/log.c
+++ b/src/nvim/log.c
@@ -9,21 +9,27 @@
 //
 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
+#include 
+#include 
+#include 
 #include 
 
 #include "auto/config.h"
+#include "nvim/ascii.h"
 #include "nvim/eval.h"
+#include "nvim/globals.h"
 #include "nvim/log.h"
-#include "nvim/main.h"
+#include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/os/os.h"
+#include "nvim/os/stdpaths_defs.h"
 #include "nvim/os/time.h"
 #include "nvim/path.h"
-#include "nvim/types.h"
 
 /// Cached location of the expanded log file path decided by log_path_init().
 static char log_file_path[MAXPATHL + 1] = { 0 };
diff --git a/src/nvim/log.h b/src/nvim/log.h
index cbee0e0f81..14d46c2ea7 100644
--- a/src/nvim/log.h
+++ b/src/nvim/log.h
@@ -11,6 +11,7 @@
 //     NVIM_PROBE(nvim_foo_bar, 1, string.data);
 #if defined(HAVE_SYS_SDT_H)
 # include   // NOLINT
+
 # define NVIM_PROBE(name, n, ...) STAP_PROBE##n(neovim, name, __VA_ARGS__)
 #else
 # define NVIM_PROBE(name, n, ...)
diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c
index d7881350aa..3c129fe7ce 100644
--- a/src/nvim/lua/converter.c
+++ b/src/nvim/lua/converter.c
@@ -4,14 +4,14 @@
 #include 
 #include 
 #include 
-#include 
 #include 
+#include 
 #include 
+#include 
+#include 
 
 #include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
-#include "nvim/assert.h"
-#include "nvim/func_attr.h"
 #include "nvim/memory.h"
 // FIXME: vim.h is not actually needed, but otherwise it states MAXPATHL is
 //        redefined
@@ -19,12 +19,16 @@
 #include "nvim/ascii.h"
 #include "nvim/eval/decode.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/eval/typval_encode.h"
 #include "nvim/eval/userfunc.h"
-#include "nvim/globals.h"
+#include "nvim/garray.h"
+#include "nvim/gettext.h"
 #include "nvim/lua/converter.h"
 #include "nvim/lua/executor.h"
 #include "nvim/macros.h"
 #include "nvim/message.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"
 
 /// Determine, which keys lua table contains
@@ -565,6 +569,7 @@ static bool typval_conv_special = false;
 #define TYPVAL_ENCODE_FIRST_ARG_TYPE lua_State *const
 #define TYPVAL_ENCODE_FIRST_ARG_NAME lstate
 #include "nvim/eval/typval_encode.c.h"
+
 #undef TYPVAL_ENCODE_SCOPE
 #undef TYPVAL_ENCODE_NAME
 #undef TYPVAL_ENCODE_FIRST_ARG_TYPE
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 9cb42a81d3..d2c2f932ec 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -1,18 +1,22 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
+#include 
+#include 
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
+#include 
 
+#include "klib/kvec.h"
 #include "luv/luv.h"
 #include "nvim/api/extmark.h"
 #include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
-#include "nvim/api/vim.h"
 #include "nvim/ascii.h"
-#include "nvim/assert.h"
 #include "nvim/buffer_defs.h"
 #include "nvim/change.h"
 #include "nvim/cursor.h"
@@ -20,28 +24,37 @@
 #include "nvim/eval.h"
 #include "nvim/eval/funcs.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/eval/userfunc.h"
+#include "nvim/event/defs.h"
 #include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
 #include "nvim/event/time.h"
 #include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_getln.h"
-#include "nvim/extmark.h"
-#include "nvim/func_attr.h"
 #include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/keycodes.h"
 #include "nvim/lua/converter.h"
 #include "nvim/lua/executor.h"
 #include "nvim/lua/stdlib.h"
 #include "nvim/lua/treesitter.h"
 #include "nvim/macros.h"
-#include "nvim/map.h"
+#include "nvim/main.h"
 #include "nvim/memline.h"
+#include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/msgpack_rpc/channel.h"
+#include "nvim/option_defs.h"
 #include "nvim/os/os.h"
+#include "nvim/path.h"
+#include "nvim/pos.h"
 #include "nvim/profile.h"
 #include "nvim/runtime.h"
-#include "nvim/screen.h"
+#include "nvim/strings.h"
 #include "nvim/ui.h"
 #include "nvim/ui_compositor.h"
 #include "nvim/undo.h"
diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h
index 78346fd81f..c6747833e5 100644
--- a/src/nvim/lua/executor.h
+++ b/src/nvim/lua/executor.h
@@ -3,13 +3,17 @@
 
 #include 
 #include 
+#include 
 
 #include "nvim/api/private/defs.h"
+#include "nvim/api/private/helpers.h"
 #include "nvim/assert.h"
 #include "nvim/eval/typval.h"
 #include "nvim/ex_cmds_defs.h"
 #include "nvim/func_attr.h"
 #include "nvim/lua/converter.h"
+#include "nvim/macros.h"
+#include "nvim/types.h"
 #include "nvim/usercmd.h"
 
 // Generated by msgpack-gen.lua
diff --git a/src/nvim/lua/spell.c b/src/nvim/lua/spell.c
index 31a2b2d19f..0a566b2f86 100644
--- a/src/nvim/lua/spell.c
+++ b/src/nvim/lua/spell.c
@@ -1,15 +1,25 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
+#include 
 #include 
+#include 
 #include 
-
+#include 
+#include 
+
+#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/lua/spell.h"
+#include "nvim/message.h"
 #include "nvim/spell.h"
-#include "nvim/vim.h"
+#include "nvim/types.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "lua/spell.c.generated.h"
+# include "lua/spell.c.generated.h"  // IWYU pragma: export
 #endif
 
 int nlua_spell_check(lua_State *lstate)
diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c
index 54e2c24523..813f86eeee 100644
--- a/src/nvim/lua/stdlib.c
+++ b/src/nvim/lua/stdlib.c
@@ -1,50 +1,39 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
+#include 
 #include 
 #include 
-#include 
-
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "auto/config.h"
 #include "cjson/lua_cjson.h"
-#include "luv/luv.h"
 #include "mpack/lmpack.h"
 #include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
-#include "nvim/api/vim.h"
 #include "nvim/ascii.h"
-#include "nvim/assert.h"
 #include "nvim/buffer_defs.h"
-#include "nvim/change.h"
-#include "nvim/cursor.h"
 #include "nvim/eval.h"
-#include "nvim/eval/userfunc.h"
-#include "nvim/event/loop.h"
-#include "nvim/event/time.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/ex_eval.h"
-#include "nvim/ex_getln.h"
-#include "nvim/extmark.h"
-#include "nvim/func_attr.h"
-#include "nvim/garray.h"
-#include "nvim/getchar.h"
 #include "nvim/globals.h"
 #include "nvim/lua/converter.h"
-#include "nvim/lua/executor.h"
 #include "nvim/lua/spell.h"
 #include "nvim/lua/stdlib.h"
-#include "nvim/lua/treesitter.h"
 #include "nvim/lua/xdiff.h"
-#include "nvim/macros.h"
 #include "nvim/map.h"
+#include "nvim/mbyte.h"
 #include "nvim/memline.h"
-#include "nvim/message.h"
-#include "nvim/msgpack_rpc/channel.h"
-#include "nvim/os/os.h"
+#include "nvim/memory.h"
+#include "nvim/pos.h"
 #include "nvim/regexp.h"
-#include "nvim/regexp_defs.h"
-#include "nvim/screen.h"
 #include "nvim/types.h"
-#include "nvim/undo.h"
-#include "nvim/version.h"
 #include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c
index 9d871939f8..d77e210c6a 100644
--- a/src/nvim/lua/treesitter.c
+++ b/src/nvim/lua/treesitter.c
@@ -6,23 +6,28 @@
 // trees and nodes, and could be broken out as a reusable lua package
 
 #include 
-#include 
 #include 
+#include 
 #include 
-#include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
 
 #include "klib/kvec.h"
 #include "nvim/api/private/helpers.h"
-#include "nvim/buffer.h"
-#include "nvim/log.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/globals.h"
 #include "nvim/lua/treesitter.h"
+#include "nvim/macros.h"
 #include "nvim/map.h"
 #include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/pos.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
 #include "tree_sitter/api.h"
 
 #define TS_META_PARSER "treesitter_parser"
diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c
index cd542b0e00..857b159af5 100644
--- a/src/nvim/lua/xdiff.c
+++ b/src/nvim/lua/xdiff.c
@@ -1,19 +1,20 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
-#include 
 #include 
 #include 
-#include 
-#include 
-#include 
+#include 
 #include 
 
+#include "luaconf.h"
+#include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/linematch.h"
 #include "nvim/lua/converter.h"
 #include "nvim/lua/executor.h"
 #include "nvim/lua/xdiff.h"
+#include "nvim/macros.h"
+#include "nvim/memory.h"
 #include "nvim/vim.h"
 #include "xdiff/xdiff.h"
 
diff --git a/src/nvim/main.c b/src/nvim/main.c
index e395f8dc78..a74e145eef 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -3,41 +3,52 @@
 
 #define EXTERN
 #include 
-#include 
+#include 
+#include 
 #include 
 #include 
+#include 
+#include 
 #include 
+#include 
 
+#include "auto/config.h"
 #include "nvim/arglist.h"
 #include "nvim/ascii.h"
 #include "nvim/autocmd.h"
 #include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/channel.h"
-#include "nvim/charset.h"
 #include "nvim/decoration.h"
 #include "nvim/decoration_provider.h"
 #include "nvim/diff.h"
 #include "nvim/drawscreen.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/event/multiqueue.h"
+#include "nvim/event/stream.h"
 #include "nvim/ex_cmds.h"
-#include "nvim/ex_cmds2.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_getln.h"
 #include "nvim/fileio.h"
 #include "nvim/fold.h"
 #include "nvim/garray.h"
+#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/grid.h"
 #include "nvim/hashtab.h"
 #include "nvim/highlight.h"
 #include "nvim/highlight_group.h"
-#include "nvim/iconv.h"
+#include "nvim/keycodes.h"
 #include "nvim/locale.h"
 #include "nvim/log.h"
 #include "nvim/lua/executor.h"
+#include "nvim/macros.h"
 #include "nvim/main.h"
-#include "nvim/mapping.h"
 #include "nvim/mark.h"
-#include "nvim/mbyte.h"
+#include "nvim/memfile_defs.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
@@ -46,24 +57,27 @@
 #include "nvim/normal.h"
 #include "nvim/ops.h"
 #include "nvim/option.h"
+#include "nvim/option_defs.h"
 #include "nvim/optionstr.h"
 #include "nvim/os/fileio.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
-#include "nvim/os/os_defs.h"
+#include "nvim/os/pty_process.h"
+#include "nvim/os/stdpaths_defs.h"
 #include "nvim/os/time.h"
-#include "nvim/os_unix.h"
 #include "nvim/path.h"
 #include "nvim/popupmenu.h"
+#include "nvim/pos.h"
 #include "nvim/profile.h"
 #include "nvim/quickfix.h"
 #include "nvim/runtime.h"
 #include "nvim/shada.h"
 #include "nvim/sign.h"
-#include "nvim/state.h"
 #include "nvim/statusline.h"
 #include "nvim/strings.h"
 #include "nvim/syntax.h"
+#include "nvim/terminal.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/ui_client.h"
 #include "nvim/ui_compositor.h"
@@ -73,8 +87,8 @@
 #ifdef MSWIN
 # include "nvim/os/os_win_console.h"
 #endif
+#include "nvim/api/extmark.h"
 #include "nvim/api/private/defs.h"
-#include "nvim/api/private/dispatch.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/api/ui.h"
 #include "nvim/event/loop.h"
@@ -83,10 +97,6 @@
 #include "nvim/msgpack_rpc/helpers.h"
 #include "nvim/msgpack_rpc/server.h"
 #include "nvim/os/signal.h"
-#ifndef MSWIN
-# include "nvim/os/pty_process_unix.h"
-#endif
-#include "nvim/api/extmark.h"
 
 // values for "window_layout"
 enum {
diff --git a/src/nvim/main.h b/src/nvim/main.h
index 0c497a7c0e..780022a9b1 100644
--- a/src/nvim/main.h
+++ b/src/nvim/main.h
@@ -1,6 +1,8 @@
 #ifndef NVIM_MAIN_H
 #define NVIM_MAIN_H
 
+#include 
+
 #include "nvim/event/loop.h"
 
 // Maximum number of commands from + or -c arguments.
diff --git a/src/nvim/map.c b/src/nvim/map.c
index 24478c6091..191a459863 100644
--- a/src/nvim/map.c
+++ b/src/nvim/map.c
@@ -8,17 +8,16 @@
 //       khash.h does not make its own copy of the key or value.
 //
 
-#include 
-#include 
 #include 
 #include 
 #include 
 
+#include "auto/config.h"
 #include "klib/khash.h"
+#include "nvim/gettext.h"
 #include "nvim/map.h"
 #include "nvim/map_defs.h"
 #include "nvim/memory.h"
-#include "nvim/vim.h"
 
 #define cstr_t_hash kh_str_hash_func
 #define cstr_t_eq kh_str_hash_equal
diff --git a/src/nvim/map.h b/src/nvim/map.h
index f5f30f5a85..92f0b32255 100644
--- a/src/nvim/map.h
+++ b/src/nvim/map.h
@@ -2,12 +2,17 @@
 #define NVIM_MAP_H
 
 #include 
+#include 
+#include 
 
+#include "klib/khash.h"
 #include "nvim/api/private/defs.h"
 #include "nvim/extmark_defs.h"
+#include "nvim/gettext.h"
 #include "nvim/highlight_defs.h"
 #include "nvim/map_defs.h"
 #include "nvim/tui/input_defs.h"
+#include "nvim/types.h"
 #include "nvim/ui_client.h"
 
 #if defined(__NetBSD__)
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index 0fff48019b..9b10ea901e 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -5,32 +5,40 @@
 
 #include 
 #include 
+#include 
 #include 
+#include 
+#include 
 #include 
 
 #include "nvim/api/private/converter.h"
+#include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/ascii.h"
-#include "nvim/assert.h"
 #include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/eval.h"
 #include "nvim/eval/typval.h"
-#include "nvim/ex_docmd.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_session.h"
-#include "nvim/func_attr.h"
 #include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/keycodes.h"
 #include "nvim/lua/executor.h"
+#include "nvim/macros.h"
 #include "nvim/mapping.h"
 #include "nvim/mbyte.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
-#include "nvim/option.h"
+#include "nvim/option_defs.h"
+#include "nvim/pos.h"
 #include "nvim/regexp.h"
 #include "nvim/runtime.h"
-#include "nvim/ui.h"
+#include "nvim/strings.h"
 #include "nvim/vim.h"
 
 /// List used for abbreviations.
diff --git a/src/nvim/mapping.h b/src/nvim/mapping.h
index 156187b5d8..7053aa814d 100644
--- a/src/nvim/mapping.h
+++ b/src/nvim/mapping.h
@@ -1,6 +1,10 @@
 #ifndef NVIM_MAPPING_H
 #define NVIM_MAPPING_H
 
+#include 
+#include 
+
+#include "lauxlib.h"
 #include "nvim/buffer_defs.h"
 #include "nvim/ex_cmds_defs.h"
 #include "nvim/types.h"
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index f9ed80e3ce..ad325ae057 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -4,20 +4,26 @@
 // mark.c: functions for setting marks and jumping to them
 
 #include 
-#include 
 #include 
+#include 
 #include 
 
 #include "nvim/ascii.h"
 #include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/cursor.h"
 #include "nvim/diff.h"
 #include "nvim/edit.h"
-#include "nvim/eval.h"
-#include "nvim/ex_cmds.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/extmark.h"
+#include "nvim/extmark_defs.h"
 #include "nvim/fold.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/mark.h"
 #include "nvim/mbyte.h"
 #include "nvim/memline.h"
@@ -25,16 +31,16 @@
 #include "nvim/message.h"
 #include "nvim/move.h"
 #include "nvim/normal.h"
-#include "nvim/option.h"
+#include "nvim/option_defs.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
-#include "nvim/os/time.h"
 #include "nvim/path.h"
 #include "nvim/quickfix.h"
 #include "nvim/sign.h"
 #include "nvim/strings.h"
 #include "nvim/textobject.h"
-#include "nvim/ui.h"
+#include "nvim/types.h"
+#include "nvim/undo_defs.h"
 #include "nvim/vim.h"
 
 // This file contains routines to maintain and manipulate marks.
diff --git a/src/nvim/mark.h b/src/nvim/mark.h
index a3fcf6d7e7..af0abba864 100644
--- a/src/nvim/mark.h
+++ b/src/nvim/mark.h
@@ -1,9 +1,12 @@
 #ifndef NVIM_MARK_H
 #define NVIM_MARK_H
 
+#include 
+#include 
+
 #include "nvim/ascii.h"
 #include "nvim/buffer_defs.h"
-#include "nvim/ex_cmds_defs.h"  // for exarg_T
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/extmark_defs.h"
 #include "nvim/func_attr.h"
 #include "nvim/macros.h"
diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c
index ad1680322c..2036ddd21d 100644
--- a/src/nvim/marktree.c
+++ b/src/nvim/marktree.c
@@ -48,10 +48,15 @@
 // at the repo root.
 
 #include 
+#include 
+#include 
+#include 
 
 #include "klib/kvec.h"
 #include "nvim/garray.h"
 #include "nvim/marktree.h"
+#include "nvim/memory.h"
+#include "nvim/pos.h"
 
 #define T MT_BRANCH_FACTOR
 #define ILEN (sizeof(mtnode_t) + (2 * T) * sizeof(void *))
diff --git a/src/nvim/marktree.h b/src/nvim/marktree.h
index e2e05eebd5..5ce4b2cd24 100644
--- a/src/nvim/marktree.h
+++ b/src/nvim/marktree.h
@@ -2,14 +2,19 @@
 #define NVIM_MARKTREE_H
 
 #include 
+#include 
+#include 
 #include 
 
 #include "nvim/assert.h"
 #include "nvim/garray.h"
 #include "nvim/map.h"
+#include "nvim/map_defs.h"
 #include "nvim/pos.h"
 #include "nvim/types.h"
 
+struct mtnode_s;
+
 #define MT_MAX_DEPTH 20
 #define MT_BRANCH_FACTOR 10
 
diff --git a/src/nvim/match.c b/src/nvim/match.c
index 1e77dc3e91..fc98ad8396 100644
--- a/src/nvim/match.c
+++ b/src/nvim/match.c
@@ -3,22 +3,39 @@
 
 // match.c: functions for highlighting matches
 
+#include 
+#include 
 #include 
+#include 
+#include 
 
+#include "nvim/ascii.h"
 #include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/drawscreen.h"
 #include "nvim/eval.h"
 #include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/fold.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/highlight.h"
 #include "nvim/highlight_group.h"
+#include "nvim/macros.h"
 #include "nvim/match.h"
+#include "nvim/mbyte.h"
 #include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option_defs.h"
+#include "nvim/pos.h"
 #include "nvim/profile.h"
 #include "nvim/regexp.h"
-#include "nvim/runtime.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/math.c b/src/nvim/math.c
index b427688083..31c6b5af69 100644
--- a/src/nvim/math.c
+++ b/src/nvim/math.c
@@ -10,7 +10,7 @@
 #include "nvim/math.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "math.c.generated.h"
+# include "math.c.generated.h"  // IWYU pragma: export
 #endif
 
 int xfpclassify(double d)
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 848b0f29d0..42b3ec0202 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -25,36 +25,55 @@
 /// Vim scripts may contain an ":scriptencoding" command. This has an effect
 /// for some commands, like ":menutrans".
 
-#include 
+#include 
+#include 
+#include 
+#include 
 #include 
+#include 
+#include 
 #include 
 #include 
 #include 
 
-#include "nvim/ascii.h"
-#include "nvim/vim.h"
-#ifdef HAVE_LOCALE_H
-# include 
-#endif
+#include "auto/config.h"
 #include "nvim/arabic.h"
+#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/cursor.h"
 #include "nvim/drawscreen.h"
-#include "nvim/eval.h"
-#include "nvim/fileio.h"
-#include "nvim/func_attr.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
 #include "nvim/iconv.h"
+#include "nvim/keycodes.h"
+#include "nvim/macros.h"
 #include "nvim/mark.h"
 #include "nvim/mbyte.h"
+#include "nvim/mbyte_defs.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
+#include "nvim/option_defs.h"
 #include "nvim/os/os.h"
-#include "nvim/path.h"
+#include "nvim/os/os_defs.h"
+#include "nvim/pos.h"
 #include "nvim/screen.h"
-#include "nvim/spell.h"
 #include "nvim/strings.h"
+#include "nvim/types.h"
+#include "nvim/vim.h"
+
+#ifdef HAVE_LOCALE_H
+# include 
+#endif
+
+#ifdef __STDC_ISO_10646__
+# include 
+#endif
 
 typedef struct {
   int rangeStart;
@@ -68,11 +87,12 @@ struct interval {
   long last;
 };
 
+// uncrustify:off
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "mbyte.c.generated.h"
-
 # include "unicode_tables.generated.h"
 #endif
+// uncrustify:on
 
 static char e_list_item_nr_is_not_list[]
   = N_("E1109: List item %d is not a List");
diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h
index b499f33cc6..780f33e05b 100644
--- a/src/nvim/mbyte.h
+++ b/src/nvim/mbyte.h
@@ -8,8 +8,8 @@
 #include "nvim/eval/typval.h"
 #include "nvim/func_attr.h"
 #include "nvim/mbyte_defs.h"
-#include "nvim/os/os_defs.h"  // For indirect
-#include "nvim/types.h"  // for char_u
+#include "nvim/os/os_defs.h"
+#include "nvim/types.h"
 
 // Return byte length of character that starts with byte "b".
 // Returns 1 for a single-byte character.
diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c
index 608d194cad..f88e51bd83 100644
--- a/src/nvim/memfile.c
+++ b/src/nvim/memfile.c
@@ -43,19 +43,26 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
-#include "nvim/ascii.h"
 #include "nvim/assert.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/fileio.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/macros.h"
 #include "nvim/memfile.h"
+#include "nvim/memfile_defs.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
+#include "nvim/os/fs_defs.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
-#include "nvim/os_unix.h"
 #include "nvim/path.h"
+#include "nvim/pos.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"
 
 #define MEMFILE_PAGE_SIZE 4096       /// default page size
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 1826e5935e..1f601f0668 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -39,19 +39,31 @@
 #include 
 #include 
 #include 
+#include 
 #include 
+#include 
+#include 
 
+#include "auto/config.h"
+#include "klib/kvec.h"
 #include "nvim/ascii.h"
 #include "nvim/autocmd.h"
 #include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/change.h"
 #include "nvim/cursor.h"
 #include "nvim/drawscreen.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/fileio.h"
-#include "nvim/func_attr.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/input.h"
+#include "nvim/macros.h"
 #include "nvim/main.h"
 #include "nvim/mark.h"
 #include "nvim/mbyte.h"
@@ -60,19 +72,21 @@
 #include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/option.h"
+#include "nvim/os/fs_defs.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
 #include "nvim/os/process.h"
-#include "nvim/os_unix.h"
+#include "nvim/os/time.h"
 #include "nvim/path.h"
-#include "nvim/sha256.h"
+#include "nvim/pos.h"
+#include "nvim/screen.h"
 #include "nvim/spell.h"
 #include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/undo.h"
 #include "nvim/version.h"
 #include "nvim/vim.h"
-#include "nvim/window.h"
 
 #ifndef UNIX            // it's in os/unix_defs.h for Unix
 # include 
diff --git a/src/nvim/memline.h b/src/nvim/memline.h
index 441adf3e87..f4190f0210 100644
--- a/src/nvim/memline.h
+++ b/src/nvim/memline.h
@@ -1,8 +1,8 @@
 #ifndef NVIM_MEMLINE_H
 #define NVIM_MEMLINE_H
 
-#include "nvim/buffer_defs.h"  // for buf_T
-#include "nvim/pos.h"  // for pos_T, linenr_T, colnr_T
+#include "nvim/buffer_defs.h"
+#include "nvim/pos.h"
 #include "nvim/types.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 81299d3e25..60c29492bb 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -7,17 +7,23 @@
 #include 
 #include 
 #include 
+#include 
 #include 
+#include 
 
 #include "nvim/api/extmark.h"
 #include "nvim/arglist.h"
+#include "nvim/ascii.h"
 #include "nvim/context.h"
 #include "nvim/decoration_provider.h"
 #include "nvim/eval.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/highlight.h"
 #include "nvim/highlight_group.h"
 #include "nvim/insexpand.h"
 #include "nvim/lua/executor.h"
+#include "nvim/main.h"
 #include "nvim/mapping.h"
 #include "nvim/memfile.h"
 #include "nvim/memory.h"
@@ -25,6 +31,7 @@
 #include "nvim/sign.h"
 #include "nvim/ui.h"
 #include "nvim/ui_compositor.h"
+#include "nvim/usercmd.h"
 #include "nvim/vim.h"
 
 #ifdef UNIT_TESTING
@@ -653,7 +660,6 @@ char *arena_memdupz(Arena *arena, const char *buf, size_t size)
 
 # include "nvim/autocmd.h"
 # include "nvim/buffer.h"
-# include "nvim/charset.h"
 # include "nvim/cmdhist.h"
 # include "nvim/diff.h"
 # include "nvim/edit.h"
@@ -662,23 +668,16 @@ char *arena_memdupz(Arena *arena, const char *buf, size_t size)
 # include "nvim/ex_docmd.h"
 # include "nvim/ex_getln.h"
 # include "nvim/file_search.h"
-# include "nvim/fold.h"
 # include "nvim/getchar.h"
 # include "nvim/grid.h"
 # include "nvim/mark.h"
-# include "nvim/mbyte.h"
-# include "nvim/memline.h"
-# include "nvim/move.h"
 # include "nvim/ops.h"
 # include "nvim/option.h"
 # include "nvim/os/os.h"
-# include "nvim/os_unix.h"
-# include "nvim/path.h"
 # include "nvim/quickfix.h"
 # include "nvim/regexp.h"
 # include "nvim/search.h"
 # include "nvim/spell.h"
-# include "nvim/syntax.h"
 # include "nvim/tag.h"
 # include "nvim/window.h"
 
diff --git a/src/nvim/memory.h b/src/nvim/memory.h
index 5c3d18ac93..5b9798dc0d 100644
--- a/src/nvim/memory.h
+++ b/src/nvim/memory.h
@@ -1,10 +1,10 @@
 #ifndef NVIM_MEMORY_H
 #define NVIM_MEMORY_H
 
-#include   // for bool
-#include   // for size_t
-#include   // for uint8_t
-#include   // for time_t
+#include 
+#include 
+#include 
+#include 
 
 #include "nvim/macros.h"
 
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index 57116170aa..7d16ab01d0 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -5,29 +5,40 @@
 // GUI/Motif support by Robert Webb
 
 #include 
-#include 
+#include 
 #include 
 
 #include "nvim/ascii.h"
 #include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/cursor.h"
 #include "nvim/eval.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/keycodes.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
 #include "nvim/memory.h"
 #include "nvim/menu.h"
+#include "nvim/menu_defs.h"
 #include "nvim/message.h"
+#include "nvim/option_defs.h"
 #include "nvim/popupmenu.h"
-#include "nvim/screen.h"
+#include "nvim/pos.h"
 #include "nvim/state.h"
 #include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
+#include "nvim/undo_defs.h"
 #include "nvim/vim.h"
-#include "nvim/window.h"
 
 #define MENUDEPTH   10          // maximum depth of menus
 
diff --git a/src/nvim/menu.h b/src/nvim/menu.h
index be294a1831..32959cf35f 100644
--- a/src/nvim/menu.h
+++ b/src/nvim/menu.h
@@ -1,11 +1,11 @@
 #ifndef NVIM_MENU_H
 #define NVIM_MENU_H
 
-#include   // for bool
+#include 
 
-#include "nvim/ex_cmds_defs.h"  // for exarg_T
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/menu_defs.h"
-#include "nvim/types.h"  // for char_u and expand_T
+#include "nvim/types.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "menu.h.generated.h"
diff --git a/src/nvim/menu_defs.h b/src/nvim/menu_defs.h
index 5fdb222bde..79b267ae49 100644
--- a/src/nvim/menu_defs.h
+++ b/src/nvim/menu_defs.h
@@ -1,7 +1,7 @@
 #ifndef NVIM_MENU_DEFS_H
 #define NVIM_MENU_DEFS_H
 
-#include   // for bool
+#include 
 
 /// Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode
 /// \addtogroup MENU_INDEX
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 538bcde6a5..01f4612026 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -7,41 +7,50 @@
 #include 
 #include 
 #include 
+#include 
+#include 
+#include 
 #include 
 
 #include "nvim/api/private/helpers.h"
 #include "nvim/ascii.h"
-#include "nvim/assert.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/channel.h"
 #include "nvim/charset.h"
 #include "nvim/drawscreen.h"
 #include "nvim/eval.h"
-#include "nvim/ex_docmd.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/event/defs.h"
+#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_eval.h"
-#include "nvim/ex_getln.h"
 #include "nvim/fileio.h"
-#include "nvim/func_attr.h"
 #include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/grid.h"
 #include "nvim/highlight.h"
 #include "nvim/indent.h"
 #include "nvim/input.h"
 #include "nvim/keycodes.h"
+#include "nvim/log.h"
 #include "nvim/main.h"
 #include "nvim/mbyte.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/mouse.h"
-#include "nvim/normal.h"
 #include "nvim/ops.h"
 #include "nvim/option.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
-#include "nvim/os/time.h"
+#include "nvim/pos.h"
 #include "nvim/regexp.h"
 #include "nvim/runtime.h"
+#include "nvim/screen.h"
 #include "nvim/strings.h"
-#include "nvim/syntax.h"
 #include "nvim/ui.h"
 #include "nvim/ui_compositor.h"
 #include "nvim/vim.h"
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index fd1eec692a..479ffde705 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -1,33 +1,46 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
+#include 
 #include 
+#include 
+#include 
 
 #include "nvim/ascii.h"
 #include "nvim/buffer.h"
 #include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/cursor.h"
-#include "nvim/diff.h"
 #include "nvim/drawscreen.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/fold.h"
 #include "nvim/getchar.h"
+#include "nvim/globals.h"
 #include "nvim/grid.h"
+#include "nvim/keycodes.h"
+#include "nvim/macros.h"
+#include "nvim/mark.h"
+#include "nvim/mbyte.h"
 #include "nvim/memline.h"
 #include "nvim/menu.h"
+#include "nvim/message.h"
 #include "nvim/mouse.h"
 #include "nvim/move.h"
+#include "nvim/normal.h"
 #include "nvim/ops.h"
 #include "nvim/option.h"
-#include "nvim/os_unix.h"
 #include "nvim/plines.h"
+#include "nvim/pos.h"
+#include "nvim/screen.h"
 #include "nvim/search.h"
 #include "nvim/state.h"
 #include "nvim/statusline.h"
 #include "nvim/strings.h"
 #include "nvim/syntax.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/ui_compositor.h"
 #include "nvim/vim.h"
diff --git a/src/nvim/move.c b/src/nvim/move.c
index b79edb50f9..df79b169b8 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -11,8 +11,9 @@
 // The 'scrolloff' option makes this a bit complicated.
 
 #include 
-#include 
+#include 
 #include 
+#include 
 
 #include "nvim/ascii.h"
 #include "nvim/buffer.h"
@@ -23,19 +24,26 @@
 #include "nvim/edit.h"
 #include "nvim/eval.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/fold.h"
 #include "nvim/getchar.h"
+#include "nvim/globals.h"
 #include "nvim/grid.h"
 #include "nvim/highlight.h"
+#include "nvim/macros.h"
 #include "nvim/mbyte.h"
-#include "nvim/memline.h"
+#include "nvim/memline_defs.h"
 #include "nvim/mouse.h"
 #include "nvim/move.h"
 #include "nvim/option.h"
 #include "nvim/plines.h"
 #include "nvim/popupmenu.h"
+#include "nvim/pos.h"
+#include "nvim/screen.h"
 #include "nvim/search.h"
 #include "nvim/strings.h"
+#include "nvim/types.h"
+#include "nvim/vim.h"
 #include "nvim/window.h"
 
 typedef struct {
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index 71ed5ccf81..516af20fe9 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -1,36 +1,42 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
+#include 
 #include 
-#include 
+#include 
+#include 
+#include 
+#include 
 #include 
-#include 
+#include 
+#include 
 #include 
 
 #include "klib/kvec.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/api/private/dispatch.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/api/ui.h"
-#include "nvim/api/vim.h"
-#include "nvim/ascii.h"
 #include "nvim/channel.h"
-#include "nvim/eval.h"
-#include "nvim/event/libuv_process.h"
+#include "nvim/event/defs.h"
 #include "nvim/event/loop.h"
 #include "nvim/event/rstream.h"
-#include "nvim/event/socket.h"
+#include "nvim/event/stream.h"
 #include "nvim/event/wstream.h"
+#include "nvim/globals.h"
 #include "nvim/log.h"
 #include "nvim/main.h"
 #include "nvim/map.h"
 #include "nvim/memory.h"
-#include "nvim/message.h"
 #include "nvim/msgpack_rpc/channel.h"
+#include "nvim/msgpack_rpc/channel_defs.h"
 #include "nvim/msgpack_rpc/helpers.h"
 #include "nvim/msgpack_rpc/unpacker.h"
 #include "nvim/os/input.h"
-#include "nvim/os_unix.h"
+#include "nvim/rbuffer.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
-#include "nvim/vim.h"
+#include "nvim/ui_client.h"
 
 #if MIN_LOG_LEVEL > LOGLVL_DBG
 # define log_client_msg(...)
diff --git a/src/nvim/msgpack_rpc/channel.h b/src/nvim/msgpack_rpc/channel.h
index ac7911bb2c..ce5806930c 100644
--- a/src/nvim/msgpack_rpc/channel.h
+++ b/src/nvim/msgpack_rpc/channel.h
@@ -6,8 +6,10 @@
 
 #include "nvim/api/private/defs.h"
 #include "nvim/channel.h"
+#include "nvim/event/multiqueue.h"
 #include "nvim/event/process.h"
 #include "nvim/event/socket.h"
+#include "nvim/macros.h"
 #include "nvim/vim.h"
 
 #define METHOD_MAXLEN 512
diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c
index 86babd1c36..609fef37d0 100644
--- a/src/nvim/msgpack_rpc/helpers.c
+++ b/src/nvim/msgpack_rpc/helpers.c
@@ -1,21 +1,25 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
-#include 
-#include 
+#include 
+#include 
+#include 
+#include 
 #include 
+#include 
+#include 
 
 #include "klib/kvec.h"
-#include "nvim/api/private/dispatch.h"
+#include "msgpack/pack.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/assert.h"
-#include "nvim/log.h"
+#include "nvim/event/wstream.h"
 #include "nvim/memory.h"
 #include "nvim/msgpack_rpc/helpers.h"
-#include "nvim/vim.h"
+#include "nvim/types.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "keysets.generated.h"
+# include "keysets.generated.h"  // IWYU pragma: export
 # include "msgpack_rpc/helpers.c.generated.h"
 #endif
 
diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c
index 81b58764d7..b1e033d9f1 100644
--- a/src/nvim/msgpack_rpc/server.c
+++ b/src/nvim/msgpack_rpc/server.c
@@ -1,25 +1,22 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
-#include 
 #include 
-#include 
+#include 
+#include 
 #include 
+#include 
 
-#include "nvim/ascii.h"
+#include "nvim/channel.h"
 #include "nvim/eval.h"
 #include "nvim/event/socket.h"
-#include "nvim/fileio.h"
 #include "nvim/garray.h"
 #include "nvim/log.h"
 #include "nvim/main.h"
 #include "nvim/memory.h"
-#include "nvim/msgpack_rpc/channel.h"
 #include "nvim/msgpack_rpc/server.h"
 #include "nvim/os/os.h"
-#include "nvim/path.h"
-#include "nvim/strings.h"
-#include "nvim/vim.h"
+#include "nvim/os/stdpaths_defs.h"
 
 #define MAX_CONNECTIONS 32
 #define ENV_LISTEN "NVIM_LISTEN_ADDRESS"  // deprecated
diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c
index 24480835a1..e5583cf91b 100644
--- a/src/nvim/msgpack_rpc/unpacker.c
+++ b/src/nvim/msgpack_rpc/unpacker.c
@@ -1,9 +1,17 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
+#include 
+#include 
+#include 
+
+#include "klib/kvec.h"
+#include "mpack/conv.h"
 #include "nvim/api/private/helpers.h"
-#include "nvim/log.h"
+#include "nvim/ascii.h"
+#include "nvim/macros.h"
 #include "nvim/memory.h"
+#include "nvim/msgpack_rpc/channel_defs.h"
 #include "nvim/msgpack_rpc/helpers.h"
 #include "nvim/msgpack_rpc/unpacker.h"
 #include "nvim/ui_client.h"
diff --git a/src/nvim/msgpack_rpc/unpacker.h b/src/nvim/msgpack_rpc/unpacker.h
index 35048fb877..b8b2d38d3b 100644
--- a/src/nvim/msgpack_rpc/unpacker.h
+++ b/src/nvim/msgpack_rpc/unpacker.h
@@ -7,10 +7,14 @@
 
 #include "mpack/mpack_core.h"
 #include "mpack/object.h"
+#include "nvim/api/private/defs.h"
 #include "nvim/api/private/dispatch.h"
 #include "nvim/api/private/helpers.h"
+#include "nvim/grid_defs.h"
 #include "nvim/memory.h"
 #include "nvim/msgpack_rpc/channel_defs.h"
+#include "nvim/types.h"
+#include "nvim/ui_client.h"
 
 struct Unpacker {
   mpack_parser_t parser;
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 27a7b1ae04..ed689df91c 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -8,14 +8,21 @@
 //
 
 #include 
+#include 
 #include 
+#include 
 #include 
+#include 
 #include 
 #include 
+#include 
 
+#include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/ascii.h"
+#include "nvim/autocmd.h"
 #include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/change.h"
 #include "nvim/charset.h"
 #include "nvim/cmdhist.h"
@@ -25,8 +32,6 @@
 #include "nvim/drawscreen.h"
 #include "nvim/edit.h"
 #include "nvim/eval.h"
-#include "nvim/eval/userfunc.h"
-#include "nvim/event/loop.h"
 #include "nvim/ex_cmds.h"
 #include "nvim/ex_cmds2.h"
 #include "nvim/ex_docmd.h"
@@ -34,15 +39,16 @@
 #include "nvim/fileio.h"
 #include "nvim/fold.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
 #include "nvim/globals.h"
 #include "nvim/grid.h"
 #include "nvim/help.h"
-#include "nvim/indent.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/keycodes.h"
-#include "nvim/log.h"
-#include "nvim/main.h"
+#include "nvim/macros.h"
 #include "nvim/mapping.h"
 #include "nvim/mark.h"
+#include "nvim/mbyte.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
@@ -56,6 +62,7 @@
 #include "nvim/plines.h"
 #include "nvim/profile.h"
 #include "nvim/quickfix.h"
+#include "nvim/screen.h"
 #include "nvim/search.h"
 #include "nvim/spell.h"
 #include "nvim/spellfile.h"
@@ -66,6 +73,7 @@
 #include "nvim/tag.h"
 #include "nvim/textformat.h"
 #include "nvim/textobject.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/undo.h"
 #include "nvim/vim.h"
diff --git a/src/nvim/normal.h b/src/nvim/normal.h
index 0317080f4f..43d892e49a 100644
--- a/src/nvim/normal.h
+++ b/src/nvim/normal.h
@@ -3,7 +3,7 @@
 
 #include 
 
-#include "nvim/buffer_defs.h"  // for win_T
+#include "nvim/buffer_defs.h"
 #include "nvim/pos.h"
 
 // Values for find_ident_under_cursor()
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 8656b4265d..f61c978f3c 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -5,13 +5,18 @@
 //        op_change, op_yank, do_put, do_join
 
 #include 
+#include 
 #include 
+#include 
 #include 
+#include 
+#include 
 #include 
 
-#include "klib/kvec.h"
+#include "nvim/api/private/defs.h"
 #include "nvim/ascii.h"
 #include "nvim/assert.h"
+#include "nvim/autocmd.h"
 #include "nvim/buffer.h"
 #include "nvim/change.h"
 #include "nvim/charset.h"
@@ -20,16 +25,19 @@
 #include "nvim/edit.h"
 #include "nvim/eval.h"
 #include "nvim/eval/typval.h"
-#include "nvim/ex_cmds.h"
 #include "nvim/ex_cmds2.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_getln.h"
 #include "nvim/extmark.h"
 #include "nvim/fold.h"
+#include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
 #include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/indent.h"
 #include "nvim/indent_c.h"
-#include "nvim/log.h"
+#include "nvim/keycodes.h"
 #include "nvim/macros.h"
 #include "nvim/mark.h"
 #include "nvim/mbyte.h"
@@ -43,13 +51,14 @@
 #include "nvim/option.h"
 #include "nvim/os/input.h"
 #include "nvim/os/time.h"
-#include "nvim/path.h"
 #include "nvim/plines.h"
+#include "nvim/screen.h"
 #include "nvim/search.h"
 #include "nvim/state.h"
 #include "nvim/strings.h"
 #include "nvim/terminal.h"
 #include "nvim/textformat.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/undo.h"
 #include "nvim/vim.h"
diff --git a/src/nvim/ops.h b/src/nvim/ops.h
index 840e33a48c..75ea1853a0 100644
--- a/src/nvim/ops.h
+++ b/src/nvim/ops.h
@@ -2,14 +2,17 @@
 #define NVIM_OPS_H
 
 #include 
+#include 
 
 #include "nvim/ascii.h"
 #include "nvim/eval/typval.h"
-#include "nvim/ex_cmds_defs.h"  // for exarg_T
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/extmark.h"
 #include "nvim/macros.h"
-#include "nvim/normal.h"  // for MotionType and oparg_T
+#include "nvim/normal.h"
 #include "nvim/os/time.h"
+#include "nvim/pos.h"
 #include "nvim/types.h"
 
 typedef int (*Indenter)(void);
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 330900a9d6..827b755ce8 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -21,14 +21,18 @@
 
 #define IN_OPTION_C
 #include 
+#include 
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 
-#include "nvim/arglist.h"
+#include "auto/config.h"
+#include "nvim/api/private/defs.h"
 #include "nvim/ascii.h"
+#include "nvim/autocmd.h"
 #include "nvim/buffer.h"
 #include "nvim/change.h"
 #include "nvim/charset.h"
@@ -36,16 +40,18 @@
 #include "nvim/decoration_provider.h"
 #include "nvim/diff.h"
 #include "nvim/drawscreen.h"
-#include "nvim/edit.h"
 #include "nvim/eval.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_getln.h"
 #include "nvim/ex_session.h"
-#include "nvim/fileio.h"
 #include "nvim/fold.h"
 #include "nvim/garray.h"
-#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
 #include "nvim/hardcopy.h"
 #include "nvim/highlight.h"
 #include "nvim/highlight_group.h"
@@ -54,6 +60,7 @@
 #include "nvim/insexpand.h"
 #include "nvim/keycodes.h"
 #include "nvim/locale.h"
+#include "nvim/log.h"
 #include "nvim/macros.h"
 #include "nvim/mapping.h"
 #include "nvim/mbyte.h"
@@ -66,22 +73,24 @@
 #include "nvim/normal.h"
 #include "nvim/ops.h"
 #include "nvim/option.h"
+#include "nvim/option_defs.h"
 #include "nvim/optionstr.h"
 #include "nvim/os/os.h"
-#include "nvim/os_unix.h"
 #include "nvim/path.h"
 #include "nvim/popupmenu.h"
+#include "nvim/pos.h"
 #include "nvim/regexp.h"
+#include "nvim/runtime.h"
 #include "nvim/screen.h"
-#include "nvim/search.h"
+#include "nvim/sign_defs.h"
 #include "nvim/spell.h"
 #include "nvim/spellfile.h"
 #include "nvim/spellsuggest.h"
 #include "nvim/strings.h"
-#include "nvim/syntax.h"
 #include "nvim/tag.h"
+#include "nvim/terminal.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
-#include "nvim/ui_compositor.h"
 #include "nvim/undo.h"
 #include "nvim/vim.h"
 #include "nvim/window.h"
@@ -90,7 +99,6 @@
 #endif
 #include "nvim/api/extmark.h"
 #include "nvim/api/private/helpers.h"
-#include "nvim/api/vim.h"
 #include "nvim/lua/executor.h"
 #include "nvim/os/input.h"
 #include "nvim/os/lang.h"
diff --git a/src/nvim/option.h b/src/nvim/option.h
index c65d2ee182..6744678044 100644
--- a/src/nvim/option.h
+++ b/src/nvim/option.h
@@ -1,7 +1,7 @@
 #ifndef NVIM_OPTION_H
 #define NVIM_OPTION_H
 
-#include "nvim/ex_cmds_defs.h"  // for exarg_T
+#include "nvim/ex_cmds_defs.h"
 
 /// Returned by get_option_value().
 typedef enum {
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 8f7e44d23d..c60925d485 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -1,8 +1,8 @@
 #ifndef NVIM_OPTION_DEFS_H
 #define NVIM_OPTION_DEFS_H
 
-#include "eval/typval.h"  // For scid_T
-#include "nvim/macros.h"  // For EXTERN
+#include "nvim/eval/typval.h"
+#include "nvim/macros.h"
 #include "nvim/types.h"
 
 // option_defs.h: definition of global variables for settable options
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index b088a4c8c7..5022334582 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -2,14 +2,13 @@
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
 #include 
-#include 
 #include 
-#include 
 #include 
 
 #include "nvim/api/private/helpers.h"
 #include "nvim/ascii.h"
 #include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/cursor.h"
 #include "nvim/cursor_shape.h"
@@ -17,23 +16,35 @@
 #include "nvim/digraph.h"
 #include "nvim/drawscreen.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/eval/vars.h"
 #include "nvim/ex_getln.h"
+#include "nvim/fold.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/hardcopy.h"
 #include "nvim/highlight_group.h"
 #include "nvim/indent.h"
 #include "nvim/indent_c.h"
 #include "nvim/insexpand.h"
 #include "nvim/keycodes.h"
+#include "nvim/macros.h"
 #include "nvim/mapping.h"
+#include "nvim/mbyte.h"
 #include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/mouse.h"
 #include "nvim/move.h"
 #include "nvim/ops.h"
 #include "nvim/option.h"
+#include "nvim/option_defs.h"
 #include "nvim/optionstr.h"
+#include "nvim/os/os.h"
+#include "nvim/pos.h"
 #include "nvim/quickfix.h"
 #include "nvim/runtime.h"
+#include "nvim/screen.h"
 #include "nvim/spell.h"
 #include "nvim/spellfile.h"
 #include "nvim/spellsuggest.h"
diff --git a/src/nvim/optionstr.h b/src/nvim/optionstr.h
index ac8d90e10e..3520cc2061 100644
--- a/src/nvim/optionstr.h
+++ b/src/nvim/optionstr.h
@@ -1,7 +1,7 @@
 #ifndef NVIM_OPTIONSTR_H
 #define NVIM_OPTIONSTR_H
 
-#include "nvim/buffer_defs.h"  // for buf_T, win_T
+#include "nvim/buffer_defs.h"
 #include "nvim/option_defs.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/os/dl.c b/src/nvim/os/dl.c
index 7d095d31e3..519cef7876 100644
--- a/src/nvim/os/dl.c
+++ b/src/nvim/os/dl.c
@@ -4,13 +4,14 @@
 /// Functions for using external native libraries
 
 #include 
+#include 
 #include 
 #include 
 
+#include "nvim/gettext.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/os/dl.h"
-#include "nvim/os/os.h"
 
 /// possible function prototypes that can be called by os_libcall()
 /// int -> int
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index ca6bff662d..8f58f5217e 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -4,19 +4,32 @@
 // Environment inspection
 
 #include 
+#include 
+#include 
+#include 
+#include 
+#include 
 #include 
 
+#include "auto/config.h"
 #include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/cmdexpand.h"
 #include "nvim/eval.h"
+#include "nvim/ex_cmds_defs.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/log.h"
 #include "nvim/macros.h"
 #include "nvim/map.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
+#include "nvim/option_defs.h"
 #include "nvim/os/os.h"
 #include "nvim/path.h"
 #include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/version.h"
 #include "nvim/vim.h"
 
diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c
index 280a9c2bee..bdea82f1ff 100644
--- a/src/nvim/os/fileio.c
+++ b/src/nvim/os/fileio.c
@@ -11,22 +11,19 @@
 #include 
 #include 
 #include 
-
-#include "auto/config.h"
-
-#ifdef HAVE_SYS_UIO_H
-# include 
-#endif
-
+#include 
 #include 
 
-#include "nvim/globals.h"
+#include "auto/config.h"
+#include "nvim/gettext.h"
 #include "nvim/macros.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/os/fileio.h"
 #include "nvim/os/os.h"
+#include "nvim/os/os_defs.h"
 #include "nvim/rbuffer.h"
+#include "nvim/types.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "os/fileio.c.generated.h"
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 0cadabbb47..2ae0a81e3d 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -5,11 +5,23 @@
 #include 
 #include 
 #include 
-#include 
 #include 
 #include 
+#include 
+#include 
+#include 
+#include 
+#include 
 
 #include "auto/config.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/log.h"
+#include "nvim/macros.h"
+#include "nvim/option_defs.h"
+#include "nvim/os/fs_defs.h"
+#include "nvim/types.h"
+#include "nvim/vim.h"
 
 #ifdef HAVE_SYS_UIO_H
 # include 
@@ -18,14 +30,12 @@
 #include 
 
 #include "nvim/ascii.h"
-#include "nvim/assert.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
-#include "nvim/option.h"
 #include "nvim/os/os.h"
-#include "nvim/os/os_defs.h"
 #include "nvim/path.h"
-#include "nvim/strings.h"
+
+struct iovec;
 
 #ifdef MSWIN
 # include "nvim/mbyte.h"  // for utf8_to_utf16, utf16_to_utf8
diff --git a/src/nvim/os/fs.h b/src/nvim/os/fs.h
index c68081da02..75c24b8db2 100644
--- a/src/nvim/os/fs.h
+++ b/src/nvim/os/fs.h
@@ -1,8 +1,8 @@
 #ifndef NVIM_OS_FS_H
 #define NVIM_OS_FS_H
 
-#include "nvim/os/fs_defs.h"  // for uv_*
-#include "nvim/types.h"  // for char_u
+#include "nvim/os/fs_defs.h"
+#include "nvim/types.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "os/fs.h.generated.h"
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index f8c1ee57ea..d6afb1b62a 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -3,25 +3,33 @@
 
 #include 
 #include 
+#include 
+#include 
 #include 
 #include 
 
 #include "nvim/api/private/defs.h"
 #include "nvim/ascii.h"
 #include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
 #include "nvim/event/rstream.h"
+#include "nvim/event/stream.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/keycodes.h"
+#include "nvim/log.h"
+#include "nvim/macros.h"
 #include "nvim/main.h"
-#include "nvim/mbyte.h"
-#include "nvim/memory.h"
 #include "nvim/msgpack_rpc/channel.h"
+#include "nvim/option_defs.h"
 #include "nvim/os/input.h"
+#include "nvim/os/time.h"
 #include "nvim/profile.h"
-#include "nvim/screen.h"
+#include "nvim/rbuffer.h"
 #include "nvim/state.h"
-#include "nvim/ui.h"
 #include "nvim/vim.h"
 
 #define READ_BUFFER_SIZE 0xfff
diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c
index 28f43ff3af..57c82bba86 100644
--- a/src/nvim/os/lang.c
+++ b/src/nvim/os/lang.c
@@ -7,16 +7,16 @@
 # include 
 # undef Boolean
 # undef FileInfo
-#endif
 
-#include "auto/config.h"
+# include "auto/config.h"
+# ifdef HAVE_LOCALE_H
+#  include 
+# endif
+# include "nvim/os/os.h"
 
-#ifdef HAVE_LOCALE_H
-# include 
 #endif
 
 #include "nvim/os/lang.h"
-#include "nvim/os/os.h"
 
 void lang_init(void)
 {
diff --git a/src/nvim/os/mem.c b/src/nvim/os/mem.c
index eccb3c97e5..0b7e8065ef 100644
--- a/src/nvim/os/mem.c
+++ b/src/nvim/os/mem.c
@@ -3,6 +3,7 @@
 
 /// Functions for accessing system memory information.
 
+#include 
 #include 
 
 #include "nvim/os/os.h"
diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c
index 28aea08595..d9273e69da 100644
--- a/src/nvim/os/process.c
+++ b/src/nvim/os/process.c
@@ -6,10 +6,21 @@
 /// psutil is a good reference for cross-platform syscall voodoo:
 /// https://github.com/giampaolo/psutil/tree/master/psutil/arch
 
-#include   // for HANDLE (win32)
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "nvim/log.h"
+#include "nvim/memory.h"
+#include "nvim/os/process.h"
 
 #ifdef MSWIN
-# include   // for CreateToolhelp32Snapshot
+# include 
+
+# include "nvim/api/private/helpers.h"
 #endif
 
 #if defined(__FreeBSD__)  // XXX: OpenBSD ?
@@ -27,15 +38,8 @@
 # include 
 #endif
 
-#include "nvim/api/private/helpers.h"
-#include "nvim/globals.h"
-#include "nvim/log.h"
-#include "nvim/os/os.h"
-#include "nvim/os/os_defs.h"
-#include "nvim/os/process.h"
-
 #ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "os/process.c.generated.h"
+# include "os/process.c.generated.h"  // IWYU pragma: export
 #endif
 
 #ifdef MSWIN
diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c
index 0b7af87267..143f0b3900 100644
--- a/src/nvim/os/pty_process_unix.c
+++ b/src/nvim/os/pty_process_unix.c
@@ -2,13 +2,15 @@
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
 // Some of the code came from pangoterm and libuv
-#include 
+
+#include 
+#include 
+#include 
+#include 
 #include 
 #include 
 #include 
-#include 
 #include 
-#include 
 
 // forkpty is not in POSIX, so headers are platform-specific
 #if defined(__FreeBSD__) || defined(__DragonFly__)
@@ -31,13 +33,16 @@
 
 #include 
 
+#include "auto/config.h"
 #include "klib/klist.h"
+#include "nvim/eval/typval.h"
 #include "nvim/event/loop.h"
 #include "nvim/event/process.h"
-#include "nvim/event/rstream.h"
-#include "nvim/event/wstream.h"
+#include "nvim/event/stream.h"
 #include "nvim/log.h"
 #include "nvim/os/os.h"
+#include "nvim/os/os_defs.h"
+#include "nvim/os/pty_process.h"
 #include "nvim/os/pty_process_unix.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/os/pty_process_unix.h b/src/nvim/os/pty_process_unix.h
index 765490b92b..0cc68cf3e9 100644
--- a/src/nvim/os/pty_process_unix.h
+++ b/src/nvim/os/pty_process_unix.h
@@ -1,8 +1,10 @@
 #ifndef NVIM_OS_PTY_PROCESS_UNIX_H
 #define NVIM_OS_PTY_PROCESS_UNIX_H
 
+#include 
 #include 
 
+#include "nvim/event/loop.h"
 #include "nvim/event/process.h"
 
 typedef struct pty_process {
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index 750d2f342f..b4bee6e550 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -3,30 +3,45 @@
 
 #include 
 #include 
-#include 
+#include 
+#include 
 #include 
 #include 
 
+#include "auto/config.h"
 #include "klib/kvec.h"
 #include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/event/libuv_process.h"
 #include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
+#include "nvim/event/process.h"
 #include "nvim/event/rstream.h"
+#include "nvim/event/stream.h"
+#include "nvim/event/wstream.h"
 #include "nvim/ex_cmds.h"
 #include "nvim/fileio.h"
-#include "nvim/log.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/macros.h"
 #include "nvim/main.h"
+#include "nvim/mbyte.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/option_defs.h"
+#include "nvim/os/fs.h"
+#include "nvim/os/os_defs.h"
 #include "nvim/os/shell.h"
 #include "nvim/os/signal.h"
+#include "nvim/os/time.h"
 #include "nvim/path.h"
+#include "nvim/pos.h"
 #include "nvim/profile.h"
-#include "nvim/screen.h"
+#include "nvim/rbuffer.h"
 #include "nvim/strings.h"
 #include "nvim/tag.h"
 #include "nvim/types.h"
diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c
index 9aa8d8051b..08d24d47e2 100644
--- a/src/nvim/os/signal.c
+++ b/src/nvim/os/signal.c
@@ -3,23 +3,20 @@
 
 #include 
 #include 
-#include 
+#include 
 #ifndef MSWIN
 # include   // for sigset_t
 #endif
 
-#include "nvim/ascii.h"
 #include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/eval.h"
-#include "nvim/event/loop.h"
 #include "nvim/event/signal.h"
 #include "nvim/globals.h"
 #include "nvim/log.h"
 #include "nvim/main.h"
 #include "nvim/memline.h"
-#include "nvim/memory.h"
 #include "nvim/os/signal.h"
-#include "nvim/vim.h"
 
 static SignalWatcher spipe, shup, squit, sterm, susr1, swinch;
 #ifdef SIGPWR
diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c
index 2aaf776fc6..a99a8d25ce 100644
--- a/src/nvim/os/stdpaths.c
+++ b/src/nvim/os/stdpaths.c
@@ -2,6 +2,7 @@
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
 #include 
+#include 
 
 #include "nvim/ascii.h"
 #include "nvim/fileio.h"
diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c
index 161c8d28b8..7ba2bd155e 100644
--- a/src/nvim/os/time.c
+++ b/src/nvim/os/time.c
@@ -1,22 +1,34 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
-#include 
+#include 
 #include 
+#include 
+#include 
+#include 
+#include 
+
 #include 
 
-#include "nvim/assert.h"
+#include "auto/config.h"
 #include "nvim/event/loop.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/log.h"
+#include "nvim/macros.h"
 #include "nvim/main.h"
+#include "nvim/memory.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
 #include "nvim/os/time.h"
 
+struct tm;
+
 static uv_mutex_t delay_mutex;
 static uv_cond_t delay_cond;
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "os/time.c.generated.h"
+# include "os/time.c.generated.h"  // IWYU pragma: export
 #endif
 
 /// Initializes the time module
diff --git a/src/nvim/os/tty.c b/src/nvim/os/tty.c
index 1b15613a93..b5124bd83a 100644
--- a/src/nvim/os/tty.c
+++ b/src/nvim/os/tty.c
@@ -5,11 +5,11 @@
 // Terminal/console utils
 //
 
-#include "nvim/os/os.h"
+#include "nvim/os/os.h"  // IWYU pragma: keep (Windows)
 #include "nvim/os/tty.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "os/tty.c.generated.h"
+# include "os/tty.c.generated.h"  // IWYU pragma: export
 #endif
 
 #ifdef MSWIN
diff --git a/src/nvim/os/unix_defs.h b/src/nvim/os/unix_defs.h
index 4ed3b51694..8d002fc5e9 100644
--- a/src/nvim/os/unix_defs.h
+++ b/src/nvim/os/unix_defs.h
@@ -3,6 +3,9 @@
 
 #include 
 #include 
+#if defined(HAVE_TERMIOS_H)
+# include 
+#endif
 
 // POSIX.1-2008 says that NAME_MAX should be in here
 #include 
diff --git a/src/nvim/os/users.c b/src/nvim/os/users.c
index 33e6563c4c..1865d6789e 100644
--- a/src/nvim/os/users.c
+++ b/src/nvim/os/users.c
@@ -3,6 +3,9 @@
 
 // users.c -- operating system user information
 
+#include 
+#include 
+#include 
 #include 
 
 #include "auto/config.h"
@@ -10,7 +13,8 @@
 #include "nvim/garray.h"
 #include "nvim/memory.h"
 #include "nvim/os/os.h"
-#include "nvim/strings.h"
+#include "nvim/types.h"
+#include "nvim/vim.h"
 #ifdef HAVE_PWD_H
 # include 
 #endif
diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c
index 473bf5072c..3521703fba 100644
--- a/src/nvim/os_unix.c
+++ b/src/nvim/os_unix.c
@@ -1,43 +1,12 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
-#include 
-#include 
-#include 
-#include 
-#include 
-
-#include "nvim/ascii.h"
-#include "nvim/buffer.h"
-#include "nvim/charset.h"
-#include "nvim/eval.h"
-#include "nvim/ex_cmds.h"
-#include "nvim/fileio.h"
-#include "nvim/garray.h"
-#include "nvim/getchar.h"
-#include "nvim/main.h"
-#include "nvim/mbyte.h"
-#include "nvim/memline.h"
-#include "nvim/memory.h"
-#include "nvim/message.h"
-#include "nvim/mouse.h"
-#include "nvim/msgpack_rpc/helpers.h"
-#include "nvim/os/input.h"
-#include "nvim/os/os.h"
-#include "nvim/os/shell.h"
-#include "nvim/os/signal.h"
-#include "nvim/os/time.h"
+#include "nvim/os/os_defs.h"
 #include "nvim/os_unix.h"
-#include "nvim/path.h"
-#include "nvim/screen.h"
-#include "nvim/strings.h"
-#include "nvim/syntax.h"
 #include "nvim/types.h"
-#include "nvim/ui.h"
-#include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "os_unix.c.generated.h"
+# include "os_unix.c.generated.h"  // IWYU pragma: export
 #endif
 
 #if defined(HAVE_ACL)
diff --git a/src/nvim/os_unix.h b/src/nvim/os_unix.h
index aae05f7fcc..31430ee23a 100644
--- a/src/nvim/os_unix.h
+++ b/src/nvim/os_unix.h
@@ -2,7 +2,7 @@
 #define NVIM_OS_UNIX_H
 
 #include "nvim/os/shell.h"
-#include "nvim/types.h"  // for vim_acl_T
+#include "nvim/types.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "os_unix.h.generated.h"
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 1413000680..33f352d163 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -2,11 +2,16 @@
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
 #include 
-#include 
+#include 
+#include 
 #include 
+#include 
 #include 
+#include 
 
+#include "auto/config.h"
 #include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/cmdexpand.h"
 #include "nvim/eval.h"
@@ -14,21 +19,21 @@
 #include "nvim/file_search.h"
 #include "nvim/fileio.h"
 #include "nvim/garray.h"
-#include "nvim/memfile.h"
-#include "nvim/memline.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/option.h"
+#include "nvim/os/fs_defs.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
 #include "nvim/os/shell.h"
-#include "nvim/os_unix.h"
 #include "nvim/path.h"
-#include "nvim/quickfix.h"
+#include "nvim/pos.h"
 #include "nvim/regexp.h"
-#include "nvim/screen.h"
 #include "nvim/strings.h"
-#include "nvim/tag.h"
 #include "nvim/types.h"
 #include "nvim/vim.h"
 #include "nvim/window.h"
diff --git a/src/nvim/plines.c b/src/nvim/plines.c
index 268e57927b..20a6855a16 100644
--- a/src/nvim/plines.c
+++ b/src/nvim/plines.c
@@ -10,25 +10,21 @@
 #include 
 
 #include "nvim/ascii.h"
-#include "nvim/buffer.h"
 #include "nvim/charset.h"
-#include "nvim/cursor.h"
 #include "nvim/decoration.h"
 #include "nvim/diff.h"
 #include "nvim/fold.h"
-#include "nvim/func_attr.h"
+#include "nvim/globals.h"
 #include "nvim/indent.h"
-#include "nvim/main.h"
+#include "nvim/macros.h"
 #include "nvim/mbyte.h"
 #include "nvim/memline.h"
-#include "nvim/memory.h"
 #include "nvim/move.h"
 #include "nvim/option.h"
 #include "nvim/plines.h"
-#include "nvim/screen.h"
-#include "nvim/strings.h"
+#include "nvim/pos.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"
-#include "nvim/window.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "plines.c.generated.h"
diff --git a/src/nvim/plines.h b/src/nvim/plines.h
index f463d82f10..808f6d284e 100644
--- a/src/nvim/plines.h
+++ b/src/nvim/plines.h
@@ -1,6 +1,9 @@
 #ifndef NVIM_PLINES_H
 #define NVIM_PLINES_H
 
+#include 
+
+#include "nvim/buffer_defs.h"
 #include "nvim/vim.h"
 
 // Argument for lbr_chartabsize().
diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c
index 234ce5fcba..567230fab4 100644
--- a/src/nvim/popupmenu.c
+++ b/src/nvim/popupmenu.c
@@ -6,27 +6,35 @@
 /// Popup menu (PUM)
 
 #include 
-#include 
+#include 
 #include 
+#include 
 
+#include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/ascii.h"
 #include "nvim/buffer.h"
 #include "nvim/charset.h"
 #include "nvim/drawscreen.h"
-#include "nvim/edit.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/ex_cmds.h"
+#include "nvim/getchar.h"
+#include "nvim/globals.h"
 #include "nvim/grid.h"
 #include "nvim/highlight.h"
 #include "nvim/insexpand.h"
+#include "nvim/keycodes.h"
+#include "nvim/mbyte.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
 #include "nvim/menu.h"
+#include "nvim/message.h"
 #include "nvim/move.h"
 #include "nvim/option.h"
 #include "nvim/popupmenu.h"
-#include "nvim/search.h"
+#include "nvim/pos.h"
+#include "nvim/screen.h"
 #include "nvim/strings.h"
 #include "nvim/ui.h"
 #include "nvim/ui_compositor.h"
diff --git a/src/nvim/popupmenu.h b/src/nvim/popupmenu.h
index 20b24fc219..2190c560b7 100644
--- a/src/nvim/popupmenu.h
+++ b/src/nvim/popupmenu.h
@@ -1,6 +1,8 @@
 #ifndef NVIM_POPUPMENU_H
 #define NVIM_POPUPMENU_H
 
+#include 
+
 #include "nvim/grid_defs.h"
 #include "nvim/macros.h"
 #include "nvim/types.h"
diff --git a/src/nvim/profile.c b/src/nvim/profile.c
index b588431bda..d54deaf983 100644
--- a/src/nvim/profile.c
+++ b/src/nvim/profile.c
@@ -3,20 +3,34 @@
 
 #include 
 #include 
+#include 
+#include 
 #include 
+#include 
+#include 
 
-#include "nvim/assert.h"
+#include "nvim/ascii.h"
 #include "nvim/charset.h"
 #include "nvim/debugger.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/eval/userfunc.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/fileio.h"
-#include "nvim/func_attr.h"
-#include "nvim/globals.h"  // for the global `time_fd` (startuptime)
+#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/hashtab.h"
+#include "nvim/keycodes.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option_defs.h"
 #include "nvim/os/os.h"
 #include "nvim/os/time.h"
+#include "nvim/pos.h"
 #include "nvim/profile.h"
 #include "nvim/runtime.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 9fdf7b348c..d7590f6f57 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -4,30 +4,43 @@
 // quickfix.c: functions for quickfix mode, using a file with error messages
 
 #include 
+#include 
 #include 
+#include 
 #include 
+#include 
+#include 
 #include 
+#include 
 
-#include "nvim/api/private/helpers.h"
 #include "nvim/arglist.h"
 #include "nvim/ascii.h"
+#include "nvim/autocmd.h"
 #include "nvim/buffer.h"
 #include "nvim/charset.h"
 #include "nvim/cursor.h"
 #include "nvim/drawscreen.h"
 #include "nvim/edit.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/ex_cmds.h"
 #include "nvim/ex_cmds2.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_eval.h"
 #include "nvim/ex_getln.h"
 #include "nvim/fileio.h"
 #include "nvim/fold.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/help.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/highlight_group.h"
+#include "nvim/macros.h"
 #include "nvim/mark.h"
 #include "nvim/mbyte.h"
+#include "nvim/memfile_defs.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
@@ -35,14 +48,16 @@
 #include "nvim/normal.h"
 #include "nvim/option.h"
 #include "nvim/optionstr.h"
+#include "nvim/os/fs_defs.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
-#include "nvim/os_unix.h"
 #include "nvim/path.h"
+#include "nvim/pos.h"
 #include "nvim/quickfix.h"
 #include "nvim/regexp.h"
 #include "nvim/search.h"
 #include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/vim.h"
 #include "nvim/window.h"
diff --git a/src/nvim/rbuffer.c b/src/nvim/rbuffer.c
index 2f718e9c2e..dde6e32306 100644
--- a/src/nvim/rbuffer.c
+++ b/src/nvim/rbuffer.c
@@ -2,15 +2,16 @@
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
 #include 
+#include 
 #include 
 #include 
 
+#include "nvim/macros.h"
 #include "nvim/memory.h"
 #include "nvim/rbuffer.h"
-#include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "rbuffer.c.generated.h"
+# include "rbuffer.c.generated.h"  // IWYU pragma: export
 #endif
 
 /// Creates a new `RBuffer` instance.
diff --git a/src/nvim/rbuffer.h b/src/nvim/rbuffer.h
index 3ebbc9d82c..63d5119004 100644
--- a/src/nvim/rbuffer.h
+++ b/src/nvim/rbuffer.h
@@ -17,6 +17,8 @@
 #include 
 #include 
 
+struct rbuffer;
+
 // Macros that simplify working with the read/write pointers directly by hiding
 // ring buffer wrap logic. Some examples:
 //
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 27b5d198ac..1aa78a3cba 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -13,21 +13,34 @@
 #include 
 #include 
 #include 
+#include 
 
 #include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/eval/userfunc.h"
 #include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/keycodes.h"
+#include "nvim/macros.h"
 #include "nvim/mark.h"
+#include "nvim/mbyte.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
+#include "nvim/option_defs.h"
 #include "nvim/os/input.h"
 #include "nvim/plines.h"
-#include "nvim/profile.h"
+#include "nvim/pos.h"
 #include "nvim/regexp.h"
+#include "nvim/regexp_defs.h"
 #include "nvim/strings.h"
+#include "nvim/types.h"
+#include "nvim/undo_defs.h"
 #include "nvim/vim.h"
 
 #ifdef REGEXP_DEBUG
diff --git a/src/nvim/regexp.h b/src/nvim/regexp.h
index 085f78af54..dcc58fa34c 100644
--- a/src/nvim/regexp.h
+++ b/src/nvim/regexp.h
@@ -17,10 +17,11 @@
 #define REX_ALL       (REX_SET | REX_USE)
 
 // regexp.c
+// uncrustify:off
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "regexp.h.generated.h"
-
 # include "regexp_bt.h.generated.h"
 #endif
+// uncrustify:on
 
 #endif  // NVIM_REGEXP_H
diff --git a/src/nvim/regexp_bt.c b/src/nvim/regexp_bt.c
index 7b5f4cd12a..2ac96997fc 100644
--- a/src/nvim/regexp_bt.c
+++ b/src/nvim/regexp_bt.c
@@ -132,6 +132,7 @@
 #include 
 
 #include "nvim/garray.h"
+#include "nvim/profile.h"
 #include "nvim/regexp.h"
 
 // The opcodes are:
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index 6becd50910..e1a2483438 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -5,25 +5,47 @@
 ///
 /// Management of runtime files (including packages)
 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/ascii.h"
 #include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/cmdexpand.h"
 #include "nvim/debugger.h"
 #include "nvim/eval.h"
 #include "nvim/eval/userfunc.h"
-#include "nvim/ex_cmds.h"
-#include "nvim/ex_cmds2.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_eval.h"
+#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/lua/executor.h"
+#include "nvim/macros.h"
+#include "nvim/map.h"
+#include "nvim/mbyte.h"
 #include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/option.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
+#include "nvim/os/stdpaths_defs.h"
+#include "nvim/path.h"
 #include "nvim/profile.h"
 #include "nvim/runtime.h"
+#include "nvim/strings.h"
+#include "nvim/usercmd.h"
 #include "nvim/vim.h"
 
 /// Structure used to store info for each sourced file.
diff --git a/src/nvim/runtime.h b/src/nvim/runtime.h
index 053c71212e..d40bb6c1c1 100644
--- a/src/nvim/runtime.h
+++ b/src/nvim/runtime.h
@@ -3,10 +3,15 @@
 
 #include 
 
+#include "klib/kvec.h"
 #include "nvim/autocmd.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_eval_defs.h"
+#include "nvim/garray.h"
+#include "nvim/pos.h"
+#include "nvim/types.h"
 
 typedef enum {
   ETYPE_TOP,       ///< toplevel
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index cbd5b96bef..3957cec316 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -10,32 +10,44 @@
 
 #include 
 #include 
+#include 
 #include 
+#include 
+#include 
 #include 
 
+#include "nvim/ascii.h"
 #include "nvim/buffer.h"
 #include "nvim/charset.h"
 #include "nvim/cursor.h"
 #include "nvim/eval.h"
-#include "nvim/extmark.h"
-#include "nvim/fileio.h"
 #include "nvim/fold.h"
-#include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/grid.h"
+#include "nvim/grid_defs.h"
 #include "nvim/highlight.h"
-#include "nvim/highlight_group.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline_defs.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/move.h"
+#include "nvim/normal.h"
 #include "nvim/option.h"
-#include "nvim/optionstr.h"
+#include "nvim/os/os.h"
+#include "nvim/os/time.h"
+#include "nvim/pos.h"
 #include "nvim/profile.h"
 #include "nvim/regexp.h"
 #include "nvim/screen.h"
 #include "nvim/search.h"
 #include "nvim/state.h"
 #include "nvim/statusline.h"
-#include "nvim/ui_compositor.h"
-#include "nvim/undo.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
+#include "nvim/ui.h"
+#include "nvim/vim.h"
 #include "nvim/window.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/screen.h b/src/nvim/screen.h
index 5cee708bd1..1d8de8ca21 100644
--- a/src/nvim/screen.h
+++ b/src/nvim/screen.h
@@ -6,6 +6,7 @@
 #include "nvim/buffer_defs.h"
 #include "nvim/fold.h"
 #include "nvim/grid_defs.h"
+#include "nvim/macros.h"
 
 EXTERN match_T screen_search_hl;       // used for 'hlsearch' highlight matching
 
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 2f3e5a2cb6..378306b8d7 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -5,31 +5,35 @@
 
 #include 
 #include 
-#include              // for INT_MAX on MSVC
+#include 
 #include 
+#include 
+#include 
 #include 
 
 #include "nvim/ascii.h"
+#include "nvim/autocmd.h"
 #include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/change.h"
 #include "nvim/charset.h"
 #include "nvim/cmdhist.h"
 #include "nvim/cursor.h"
 #include "nvim/drawscreen.h"
-#include "nvim/edit.h"
 #include "nvim/eval.h"
-#include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
 #include "nvim/ex_cmds.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_getln.h"
 #include "nvim/fileio.h"
 #include "nvim/fold.h"
-#include "nvim/func_attr.h"
 #include "nvim/getchar.h"
-#include "nvim/indent.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/indent_c.h"
 #include "nvim/insexpand.h"
-#include "nvim/main.h"
+#include "nvim/macros.h"
 #include "nvim/mark.h"
 #include "nvim/mbyte.h"
 #include "nvim/memline.h"
@@ -39,11 +43,13 @@
 #include "nvim/move.h"
 #include "nvim/normal.h"
 #include "nvim/option.h"
+#include "nvim/os/fs.h"
 #include "nvim/os/input.h"
 #include "nvim/os/time.h"
 #include "nvim/path.h"
 #include "nvim/profile.h"
 #include "nvim/regexp.h"
+#include "nvim/screen.h"
 #include "nvim/search.h"
 #include "nvim/strings.h"
 #include "nvim/ui.h"
diff --git a/src/nvim/search.h b/src/nvim/search.h
index ff843bb59e..cd8431c916 100644
--- a/src/nvim/search.h
+++ b/src/nvim/search.h
@@ -6,8 +6,11 @@
 
 #include "nvim/buffer_defs.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/normal.h"
 #include "nvim/os/time.h"
+#include "nvim/pos.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"
 
 // Values for the find_pattern_in_path() function args 'type' and 'action':
diff --git a/src/nvim/sha256.c b/src/nvim/sha256.c
index 6522158f12..72ef74b46c 100644
--- a/src/nvim/sha256.c
+++ b/src/nvim/sha256.c
@@ -13,11 +13,13 @@
 /// Vim specific notes:
 /// sha256_self_test() is implicitly called once.
 
-#include         // for size_t
-#include          // for snprintf().
+#include 
+#include 
+#include 
+#include 
 
-#include "nvim/sha256.h"   // for context_sha256_T
-#include "nvim/vim.h"      // for STRCPY()/strlen().
+#include "nvim/sha256.h"
+#include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "sha256.c.generated.h"
diff --git a/src/nvim/sha256.h b/src/nvim/sha256.h
index b52d300de6..a1d8f670d5 100644
--- a/src/nvim/sha256.h
+++ b/src/nvim/sha256.h
@@ -2,9 +2,9 @@
 #define NVIM_SHA256_H
 
 #include 
-#include       // for uint32_t
+#include 
 
-#include "nvim/types.h"  // for char_u
+#include "nvim/types.h"
 
 #define SHA256_BUFFER_SIZE 64
 #define SHA256_SUM_SIZE    32
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 64029e0799..42f5e3456d 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -2,48 +2,58 @@
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
 #include 
-#include 
 #include 
-#include 
+#include 
+#include 
+#include 
+#include 
 #include 
 #include 
+#include 
 #include 
 #include 
+#include 
 #include 
 
+#include "auto/config.h"
 #include "klib/khash.h"
-#include "klib/kvec.h"
 #include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/ascii.h"
 #include "nvim/buffer.h"
-#include "nvim/buffer_defs.h"
 #include "nvim/cmdhist.h"
+#include "nvim/eval.h"
 #include "nvim/eval/decode.h"
 #include "nvim/eval/encode.h"
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/ex_cmds.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/fileio.h"
 #include "nvim/garray.h"
+#include "nvim/gettext.h"
 #include "nvim/globals.h"
+#include "nvim/hashtab.h"
 #include "nvim/macros.h"
 #include "nvim/mark.h"
+#include "nvim/mbyte.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/msgpack_rpc/helpers.h"
+#include "nvim/normal.h"
 #include "nvim/ops.h"
 #include "nvim/option.h"
 #include "nvim/os/fileio.h"
+#include "nvim/os/fs_defs.h"
 #include "nvim/os/os.h"
 #include "nvim/os/time.h"
 #include "nvim/path.h"
 #include "nvim/pos.h"
-#include "nvim/quickfix.h"
 #include "nvim/regexp.h"
 #include "nvim/search.h"
 #include "nvim/shada.h"
 #include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/version.h"
 #include "nvim/vim.h"
 
@@ -288,8 +298,6 @@ typedef struct {
   } data;
 } ShadaEntry;
 
-struct hm_llist_entry;
-
 /// One entry in sized linked list
 typedef struct hm_llist_entry {
   ShadaEntry data;              ///< Entry data.
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index 8c0ae6dca8..2453e6f766 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -5,20 +5,42 @@
 // sign.c: functions for managing with signs
 //
 
+#include 
+#include 
+#include 
+#include 
+#include 
+
 #include "nvim/ascii.h"
 #include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/cursor.h"
 #include "nvim/drawscreen.h"
 #include "nvim/edit.h"
 #include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/fold.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/hashtab.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/highlight_group.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline_defs.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/move.h"
 #include "nvim/option.h"
+#include "nvim/pos.h"
 #include "nvim/sign.h"
-#include "nvim/syntax.h"
+#include "nvim/sign_defs.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"
 #include "nvim/window.h"
 
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index b9ea7557c4..8ee28fea34 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -56,61 +56,58 @@
 // Use DEBUG_TRIEWALK to print the changes made in suggest_trie_walk() for a
 // specific word.
 
-#include                // for assert
-#include              // for uint32_t, uint16_t, uint8_t
-#include                // for INT_MAX
-#include               // for false, true, bool
-#include                // for NULL, size_t, ptrdiff_t
-#include                 // for snprintf
-#include                // for memmove, strstr, memcpy, memset
-
-#include "nvim/ascii.h"           // for NUL, ascii_isdigit, ascii_iswhite
-#include "nvim/autocmd.h"         // for apply_autocmds
-#include "nvim/buffer.h"          // for bufref_valid, set_bufref, buf_is_empty
-#include "nvim/buffer_defs.h"     // for win_T, synblock_T, buf_T, w_p_...
-#include "nvim/change.h"          // for changed_bytes
-#include "nvim/charset.h"         // for skipwhite, getwhitecols, skipbin
-#include "nvim/cursor.h"          // for get_cursor_line_ptr
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
+#include "nvim/buffer.h"
+#include "nvim/change.h"
+#include "nvim/charset.h"
+#include "nvim/cursor.h"
 #include "nvim/decoration.h"
-#include "nvim/drawscreen.h"      // for NOT_VALID, redraw_later
-#include "nvim/eval/typval.h"     // for semsg
-#include "nvim/ex_cmds.h"         // for do_sub_msg
-#include "nvim/ex_cmds_defs.h"    // for exarg_T
-#include "nvim/ex_docmd.h"        // for do_cmdline_cmd
-#include "nvim/garray.h"          // for garray_T, GA_EMPTY, GA_APPEND_...
-#include "nvim/gettext.h"         // for _, N_
-#include "nvim/hashtab.h"         // for hash_clear_all, hash_init, has...
-#include "nvim/highlight_defs.h"  // for HLF_COUNT, hlf_T, HLF_SPB, HLF...
-#include "nvim/insexpand.h"       // for ins_compl_add_infercase, ins_c...
-#include "nvim/log.h"             // for ELOG
-#include "nvim/macros.h"          // for MB_PTR_ADV, MB_PTR_BACK, ASCII...
-#include "nvim/mark.h"            // for clearpos
-#include "nvim/mbyte.h"           // for utf_ptr2char, utf_char2bytes
-#include "nvim/memline.h"         // for ml_append, ml_get_buf, ml_close
-#include "nvim/memline_defs.h"    // for memline_T
-#include "nvim/memory.h"          // for xfree, xmalloc, xcalloc, xstrdup
-#include "nvim/message.h"         // for emsg, msg_puts, give_warning
-#include "nvim/option.h"          // for copy_option_part, set_option_v...
-#include "nvim/option_defs.h"     // for p_ws, OPT_LOCAL, p_enc, SHM_SE...
-#include "nvim/os/fs.h"           // for os_remove
-#include "nvim/os/input.h"        // for line_breakcheck
-#include "nvim/os/os_defs.h"      // for MAXPATHL
-#include "nvim/path.h"            // for path_full_compare, path_tail...
-#include "nvim/pos.h"             // for pos_T, colnr_T, linenr_T
-#include "nvim/regexp.h"          // for vim_regfree, vim_regexec, vim_...
-#include "nvim/regexp_defs.h"     // for regmatch_T, regprog_T
-#include "nvim/runtime.h"         // for DIP_ALL, do_in_runtimepath
-#include "nvim/search.h"          // for SEARCH_KEEP, for do_search
-#include "nvim/spell.h"           // for FUNC_ATTR_NONNULL_ALL, FUNC_AT...
-#include "nvim/spell_defs.h"      // for slang_T, langp_T, MAXWLEN, sal...
-#include "nvim/spellfile.h"       // for spell_load_file
-#include "nvim/spellsuggest.h"    // for spell_suggest_list
-#include "nvim/strings.h"         // for vim_strchr, vim_snprintf, conc...
-#include "nvim/syntax.h"          // for syn_get_id, syntax_present
-#include "nvim/types.h"           // for char_u
-#include "nvim/undo.h"            // for u_save_cursor
-#include "nvim/vim.h"             // for curwin, strlen, STRLCPY, STRNCMP
-#include "nvim/window.h"          // for win_valid_any_tab
+#include "nvim/decoration_provider.h"
+#include "nvim/drawscreen.h"
+#include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds_defs.h"
+#include "nvim/ex_docmd.h"
+#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/hashtab.h"
+#include "nvim/highlight_defs.h"
+#include "nvim/insexpand.h"
+#include "nvim/log.h"
+#include "nvim/macros.h"
+#include "nvim/mark.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option.h"
+#include "nvim/os/fs.h"
+#include "nvim/os/input.h"
+#include "nvim/os/os_defs.h"
+#include "nvim/path.h"
+#include "nvim/pos.h"
+#include "nvim/regexp.h"
+#include "nvim/runtime.h"
+#include "nvim/search.h"
+#include "nvim/spell.h"
+#include "nvim/spell_defs.h"
+#include "nvim/spellfile.h"
+#include "nvim/spellsuggest.h"
+#include "nvim/strings.h"
+#include "nvim/syntax.h"
+#include "nvim/types.h"
+#include "nvim/undo.h"
+#include "nvim/vim.h"
+#include "nvim/window.h"
 
 // Result values.  Lower number is accepted over higher one.
 enum {
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index d483863fa8..395051453a 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -226,34 +226,52 @@
 //                          stored as an offset to the previous number in as
 //                          few bytes as possible, see offset2bytes())
 
-#include 
+#include 
+#include 
+#include 
+#include 
+#include 
 #include 
-#include 
+#include 
+#include 
+#include 
 
+#include "auto/config.h"
 #include "nvim/arglist.h"
 #include "nvim/ascii.h"
 #include "nvim/buffer.h"
 #include "nvim/charset.h"
 #include "nvim/drawscreen.h"
-#include "nvim/ex_cmds2.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/fileio.h"
+#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/hashtab.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/option.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
+#include "nvim/os/time.h"
 #include "nvim/path.h"
+#include "nvim/pos.h"
 #include "nvim/regexp.h"
 #include "nvim/runtime.h"
 #include "nvim/spell.h"
 #include "nvim/spell_defs.h"
 #include "nvim/spellfile.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/undo.h"
 #include "nvim/vim.h"
 
 #ifndef UNIX            // it's in os/unix_defs.h for Unix
-# include       // for time_t
+# include 
 #endif
 
 // Special byte values for .  Some are only used in the tree for
diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c
index 28bc77c1e5..355233fc5b 100644
--- a/src/nvim/spellsuggest.c
+++ b/src/nvim/spellsuggest.c
@@ -3,30 +3,48 @@
 
 // spellsuggest.c: functions for spelling suggestions
 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
 #include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/change.h"
 #include "nvim/charset.h"
 #include "nvim/cursor.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/fileio.h"
 #include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/hashtab.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/input.h"
+#include "nvim/macros.h"
 #include "nvim/mbyte.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
+#include "nvim/normal.h"
 #include "nvim/option.h"
 #include "nvim/os/fs.h"
 #include "nvim/os/input.h"
+#include "nvim/os/os_defs.h"
+#include "nvim/pos.h"
 #include "nvim/profile.h"
 #include "nvim/screen.h"
 #include "nvim/spell.h"
-#include "nvim/spell_defs.h"
 #include "nvim/spellfile.h"
 #include "nvim/spellsuggest.h"
 #include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/undo.h"
 #include "nvim/vim.h"
diff --git a/src/nvim/state.c b/src/nvim/state.c
index 460a9dd637..9ba5f81776 100644
--- a/src/nvim/state.c
+++ b/src/nvim/state.c
@@ -1,27 +1,38 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
-#include 
+#include 
+#include 
+#include 
 
-#include "klib/kvec.h"
 #include "nvim/ascii.h"
 #include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/drawscreen.h"
 #include "nvim/eval.h"
-#include "nvim/ex_docmd.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/event/defs.h"
+#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
 #include "nvim/getchar.h"
+#include "nvim/globals.h"
 #include "nvim/insexpand.h"
+#include "nvim/keycodes.h"
 #include "nvim/log.h"
+#include "nvim/macros.h"
 #include "nvim/main.h"
 #include "nvim/option.h"
-#include "nvim/option_defs.h"
 #include "nvim/os/input.h"
+#include "nvim/screen.h"
 #include "nvim/state.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "state.c.generated.h"
+# include "state.c.generated.h"  // IWYU pragma: export
 #endif
 
 void state_enter(VimState *s)
diff --git a/src/nvim/state.h b/src/nvim/state.h
index b93d57d035..76a38b0dab 100644
--- a/src/nvim/state.h
+++ b/src/nvim/state.h
@@ -3,6 +3,8 @@
 
 #include 
 
+struct vim_state;
+
 typedef struct vim_state VimState;
 
 typedef int (*state_check_callback)(VimState *state);
diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c
index 93334a1d75..9b9daa3883 100644
--- a/src/nvim/statusline.c
+++ b/src/nvim/statusline.c
@@ -5,22 +5,41 @@
 #include 
 #include 
 #include 
+#include 
+#include 
+#include 
 #include 
 
-#include "nvim/assert.h"
-#include "nvim/autocmd.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/ascii.h"
 #include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/drawscreen.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/eval/vars.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/grid.h"
 #include "nvim/highlight.h"
 #include "nvim/highlight_group.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/move.h"
 #include "nvim/option.h"
 #include "nvim/optionstr.h"
+#include "nvim/os/os.h"
+#include "nvim/path.h"
+#include "nvim/pos.h"
+#include "nvim/screen.h"
 #include "nvim/statusline.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/undo.h"
 #include "nvim/vim.h"
diff --git a/src/nvim/statusline.h b/src/nvim/statusline.h
index dc25dd5e67..f7e36f138c 100644
--- a/src/nvim/statusline.h
+++ b/src/nvim/statusline.h
@@ -1,7 +1,11 @@
 #ifndef NVIM_STATUSLINE_H
 #define NVIM_STATUSLINE_H
 
+#include 
+
 #include "nvim/buffer_defs.h"
+#include "nvim/macros.h"
+#include "nvim/statusline_defs.h"
 
 /// Array defining what should be done when tabline is clicked
 EXTERN StlClickDefinition *tab_page_click_defs INIT(= NULL);
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index dc48273c8e..7fdcaaa355 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -6,48 +6,29 @@
 #include 
 #include 
 #include 
+#include 
+#include 
+#include 
 #include 
 
+#include "auto/config.h"
 #include "nvim/ascii.h"
 #include "nvim/assert.h"
-#include "nvim/buffer.h"
 #include "nvim/charset.h"
-#include "nvim/diff.h"
-#include "nvim/edit.h"
-#include "nvim/eval.h"
 #include "nvim/eval/encode.h"
-#include "nvim/ex_cmds.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/ex_docmd.h"
-#include "nvim/ex_getln.h"
-#include "nvim/file_search.h"
-#include "nvim/fileio.h"
-#include "nvim/fold.h"
-#include "nvim/func_attr.h"
-#include "nvim/getchar.h"
-#include "nvim/mark.h"
+#include "nvim/gettext.h"
+#include "nvim/macros.h"
 #include "nvim/math.h"
 #include "nvim/mbyte.h"
-#include "nvim/memfile.h"
-#include "nvim/memline.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
-#include "nvim/move.h"
-#include "nvim/ops.h"
 #include "nvim/option.h"
-#include "nvim/os/os.h"
-#include "nvim/os/shell.h"
-#include "nvim/os_unix.h"
-#include "nvim/path.h"
-#include "nvim/quickfix.h"
-#include "nvim/regexp.h"
-#include "nvim/screen.h"
-#include "nvim/search.h"
-#include "nvim/spell.h"
 #include "nvim/strings.h"
-#include "nvim/syntax.h"
-#include "nvim/tag.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"
-#include "nvim/window.h"
 
 /// Copy up to `len` bytes of `string` into newly allocated memory and
 /// terminate with a NUL. The allocated memory always has size `len + 1`, even
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index fe1bdf12b1..cafcc4f508 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -4,51 +4,46 @@
 // syntax.c: code for syntax highlighting
 
 #include 
-#include 
 #include 
 #include 
 #include 
 #include 
 
-#include "nvim/api/private/helpers.h"
 #include "nvim/ascii.h"
 #include "nvim/autocmd.h"
 #include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
-#include "nvim/cursor_shape.h"
 #include "nvim/drawscreen.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/eval/vars.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
-#include "nvim/fileio.h"
 #include "nvim/fold.h"
 #include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/hashtab.h"
-#include "nvim/highlight.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/highlight_group.h"
 #include "nvim/indent_c.h"
-#include "nvim/keycodes.h"
-#include "nvim/lua/executor.h"
 #include "nvim/macros.h"
 #include "nvim/mbyte.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
-#include "nvim/option.h"
+#include "nvim/option_defs.h"
 #include "nvim/optionstr.h"
 #include "nvim/os/input.h"
-#include "nvim/os/os.h"
-#include "nvim/os/time.h"
-#include "nvim/os_unix.h"
 #include "nvim/path.h"
+#include "nvim/pos.h"
 #include "nvim/profile.h"
 #include "nvim/regexp.h"
-#include "nvim/sign.h"
+#include "nvim/runtime.h"
 #include "nvim/strings.h"
 #include "nvim/syntax.h"
-#include "nvim/syntax_defs.h"
-#include "nvim/terminal.h"
-#include "nvim/ui.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"
 
 static bool did_syntax_onoff = false;
diff --git a/src/nvim/syntax.h b/src/nvim/syntax.h
index 0d890314c5..0a63392a04 100644
--- a/src/nvim/syntax.h
+++ b/src/nvim/syntax.h
@@ -6,6 +6,7 @@
 #include "nvim/buffer_defs.h"
 #include "nvim/ex_cmds_defs.h"
 #include "nvim/globals.h"
+#include "nvim/macros.h"
 
 #define HL_CONTAINED   0x01    // not used on toplevel
 #define HL_TRANSP      0x02    // has no highlighting
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index b6b8f5c959..35b9d71ea3 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -4,28 +4,38 @@
 // Code to handle tags and the tag stack
 
 #include 
+#include 
 #include 
 #include 
+#include 
+#include 
 #include 
 
 #include "nvim/ascii.h"
+#include "nvim/autocmd.h"
 #include "nvim/buffer.h"
 #include "nvim/charset.h"
 #include "nvim/cmdexpand.h"
 #include "nvim/cursor.h"
 #include "nvim/drawscreen.h"
-#include "nvim/edit.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_docmd.h"
-#include "nvim/ex_getln.h"
 #include "nvim/file_search.h"
 #include "nvim/fileio.h"
 #include "nvim/fold.h"
 #include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/hashtab.h"
 #include "nvim/help.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/input.h"
 #include "nvim/insexpand.h"
+#include "nvim/macros.h"
 #include "nvim/mark.h"
 #include "nvim/mbyte.h"
 #include "nvim/memory.h"
@@ -35,15 +45,17 @@
 #include "nvim/optionstr.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
+#include "nvim/os/os_defs.h"
 #include "nvim/os/time.h"
-#include "nvim/os_unix.h"
 #include "nvim/path.h"
+#include "nvim/pos.h"
 #include "nvim/quickfix.h"
 #include "nvim/regexp.h"
 #include "nvim/runtime.h"
 #include "nvim/search.h"
 #include "nvim/strings.h"
 #include "nvim/tag.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/vim.h"
 #include "nvim/window.h"
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 890b04a614..206d9ac836 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -37,45 +37,57 @@
 // Some code from pangoterm http://www.leonerd.org.uk/code/pangoterm
 
 #include 
+#include 
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
+#include 
 
+#include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/ascii.h"
 #include "nvim/autocmd.h"
 #include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/change.h"
+#include "nvim/channel.h"
 #include "nvim/cursor.h"
+#include "nvim/drawline.h"
 #include "nvim/drawscreen.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
 #include "nvim/event/time.h"
-#include "nvim/ex_cmds.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/getchar.h"
+#include "nvim/globals.h"
 #include "nvim/highlight.h"
 #include "nvim/highlight_group.h"
 #include "nvim/keycodes.h"
-#include "nvim/log.h"
 #include "nvim/macros.h"
 #include "nvim/main.h"
 #include "nvim/map.h"
 #include "nvim/mbyte.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
-#include "nvim/message.h"
 #include "nvim/mouse.h"
 #include "nvim/move.h"
+#include "nvim/msgpack_rpc/channel_defs.h"
+#include "nvim/normal.h"
 #include "nvim/option.h"
 #include "nvim/optionstr.h"
-#include "nvim/os/input.h"
+#include "nvim/pos.h"
+#include "nvim/screen.h"
 #include "nvim/state.h"
 #include "nvim/terminal.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/vim.h"
-#include "nvim/window.h"
 
 typedef struct terminal_state {
   VimState state;
diff --git a/src/nvim/testing.c b/src/nvim/testing.c
index a37ceeb86b..f4ff27c9bc 100644
--- a/src/nvim/testing.c
+++ b/src/nvim/testing.c
@@ -3,14 +3,32 @@
 
 // testing.c: Support for tests
 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "nvim/ascii.h"
 #include "nvim/eval.h"
 #include "nvim/eval/encode.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/ex_docmd.h"
+#include "nvim/garray.h"
+#include "nvim/gettext.h"
 #include "nvim/globals.h"
+#include "nvim/hashtab.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/os/os.h"
 #include "nvim/runtime.h"
+#include "nvim/strings.h"
 #include "nvim/testing.h"
+#include "nvim/types.h"
+#include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "testing.c.generated.h"
diff --git a/src/nvim/textformat.c b/src/nvim/textformat.c
index 61949fec6a..56efd4cc45 100644
--- a/src/nvim/textformat.c
+++ b/src/nvim/textformat.c
@@ -4,20 +4,29 @@
 // textformat.c: text formatting functions
 
 #include 
+#include 
+#include 
 
 #include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/change.h"
 #include "nvim/charset.h"
 #include "nvim/cursor.h"
 #include "nvim/drawscreen.h"
 #include "nvim/edit.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/getchar.h"
 #include "nvim/globals.h"
 #include "nvim/indent.h"
 #include "nvim/indent_c.h"
+#include "nvim/macros.h"
+#include "nvim/mark.h"
 #include "nvim/mbyte.h"
 #include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/move.h"
 #include "nvim/normal.h"
 #include "nvim/ops.h"
@@ -28,6 +37,7 @@
 #include "nvim/strings.h"
 #include "nvim/textformat.h"
 #include "nvim/textobject.h"
+#include "nvim/types.h"
 #include "nvim/undo.h"
 #include "nvim/vim.h"
 #include "nvim/window.h"
diff --git a/src/nvim/textformat.h b/src/nvim/textformat.h
index 3c918a028b..fcc9c2d6f4 100644
--- a/src/nvim/textformat.h
+++ b/src/nvim/textformat.h
@@ -1,8 +1,8 @@
 #ifndef NVIM_TEXTFORMAT_H
 #define NVIM_TEXTFORMAT_H
 
-#include "nvim/normal.h"  // for oparg_T
-#include "nvim/pos.h"  // for linenr_T
+#include "nvim/normal.h"
+#include "nvim/pos.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "textformat.h.generated.h"
diff --git a/src/nvim/textobject.c b/src/nvim/textobject.c
index 8290fe14e5..b9b4d0dfe3 100644
--- a/src/nvim/textobject.c
+++ b/src/nvim/textobject.c
@@ -4,8 +4,11 @@
 // textobject.c: functions for text objects
 
 #include 
+#include 
+#include 
 
 #include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/cursor.h"
 #include "nvim/drawscreen.h"
 #include "nvim/edit.h"
@@ -13,14 +16,19 @@
 #include "nvim/fold.h"
 #include "nvim/globals.h"
 #include "nvim/indent.h"
+#include "nvim/macros.h"
 #include "nvim/mark.h"
 #include "nvim/mbyte.h"
 #include "nvim/memline.h"
+#include "nvim/memory.h"
 #include "nvim/normal.h"
+#include "nvim/option_defs.h"
 #include "nvim/pos.h"
+#include "nvim/screen.h"
 #include "nvim/search.h"
-#include "nvim/textformat.h"
+#include "nvim/strings.h"
 #include "nvim/textobject.h"
+#include "nvim/types.h"
 #include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/textobject.h b/src/nvim/textobject.h
index 26f88613fd..e31557f297 100644
--- a/src/nvim/textobject.h
+++ b/src/nvim/textobject.h
@@ -1,9 +1,9 @@
 #ifndef NVIM_TEXTOBJECT_H
 #define NVIM_TEXTOBJECT_H
 
-#include "nvim/normal.h"  // for oparg_T
-#include "nvim/pos.h"  // for linenr_T
-#include "nvim/vim.h"  // for Direction
+#include "nvim/normal.h"
+#include "nvim/pos.h"
+#include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "textobject.h.generated.h"
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index 2089686e5e..9171f79c37 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -1,20 +1,32 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
+#include 
+#include 
+#include 
+#include 
+
+#include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/api/vim.h"
 #include "nvim/ascii.h"
 #include "nvim/autocmd.h"
 #include "nvim/charset.h"
-#include "nvim/ex_docmd.h"
+#include "nvim/event/defs.h"
+#include "nvim/event/multiqueue.h"
+#include "nvim/globals.h"
+#include "nvim/log.h"
 #include "nvim/macros.h"
 #include "nvim/main.h"
+#include "nvim/map.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/option.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
 #include "nvim/tui/input.h"
+#include "nvim/tui/input_defs.h"
 #include "nvim/tui/tui.h"
-#include "nvim/vim.h"
 #ifdef MSWIN
 # include "nvim/os/os_win_console.h"
 #endif
diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h
index 0b60394850..5df108b107 100644
--- a/src/nvim/tui/input.h
+++ b/src/nvim/tui/input.h
@@ -2,10 +2,14 @@
 #define NVIM_TUI_INPUT_H
 
 #include 
+#include 
 #include 
+#include 
 
+#include "nvim/event/loop.h"
 #include "nvim/event/stream.h"
 #include "nvim/event/time.h"
+#include "nvim/rbuffer.h"
 #include "nvim/tui/input_defs.h"
 #include "nvim/tui/tui.h"
 
diff --git a/src/nvim/tui/terminfo.c b/src/nvim/tui/terminfo.c
index 229e340dc3..0f6ae03d35 100644
--- a/src/nvim/tui/terminfo.c
+++ b/src/nvim/tui/terminfo.c
@@ -8,14 +8,16 @@
 #include 
 
 #include "nvim/globals.h"
-#include "nvim/log.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/option.h"
-#include "nvim/os/os.h"
 #include "nvim/tui/terminfo.h"
 #include "nvim/tui/terminfo_defs.h"
 
+#ifdef __FreeBSD__
+# include "nvim/os/os.h"
+#endif
+
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "tui/terminfo.c.generated.h"
 #endif
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index e2d37860c4..be7658616f 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -4,31 +4,35 @@
 // Terminal UI functions. Invoked (by ui_bridge.c) on the TUI thread.
 
 #include 
-#include 
+#include 
 #include 
 #include 
+#include 
+#include 
 #include 
 #include 
-#if defined(HAVE_TERMIOS_H)
-# include 
-#endif
 
+#include "auto/config.h"
 #include "klib/kvec.h"
-#include "nvim/api/private/helpers.h"
-#include "nvim/api/vim.h"
+#include "nvim/api/private/defs.h"
 #include "nvim/ascii.h"
+#include "nvim/event/defs.h"
 #include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
 #include "nvim/event/signal.h"
-#include "nvim/highlight.h"
+#include "nvim/event/stream.h"
+#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/log.h"
 #include "nvim/main.h"
-#include "nvim/map.h"
+#include "nvim/mbyte.h"
 #include "nvim/memory.h"
+#include "nvim/message.h"
 #include "nvim/option.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
 #include "nvim/os/signal.h"
-#include "nvim/os/tty.h"
 #include "nvim/ui.h"
 #include "nvim/vim.h"
 #ifdef MSWIN
@@ -36,8 +40,6 @@
 #endif
 #include "nvim/cursor_shape.h"
 #include "nvim/macros.h"
-#include "nvim/strings.h"
-#include "nvim/syntax.h"
 #include "nvim/tui/input.h"
 #include "nvim/tui/terminfo.h"
 #include "nvim/tui/tui.h"
diff --git a/src/nvim/ugrid.c b/src/nvim/ugrid.c
index d96da3f2bb..be1746983c 100644
--- a/src/nvim/ugrid.c
+++ b/src/nvim/ugrid.c
@@ -2,14 +2,10 @@
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
 #include 
-#include 
-#include 
-#include 
+#include 
 
-#include "nvim/assert.h"
+#include "nvim/memory.h"
 #include "nvim/ugrid.h"
-#include "nvim/ui.h"
-#include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "ugrid.c.generated.h"
diff --git a/src/nvim/ugrid.h b/src/nvim/ugrid.h
index ae11153c61..a85a6fb4a8 100644
--- a/src/nvim/ugrid.h
+++ b/src/nvim/ugrid.h
@@ -2,8 +2,12 @@
 #define NVIM_UGRID_H
 
 #include "nvim/globals.h"
+#include "nvim/grid_defs.h"
 #include "nvim/ui.h"
 
+struct ucell;
+struct ugrid;
+
 typedef struct ucell UCell;
 typedef struct ugrid UGrid;
 
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index bee8d461a7..4beb4b344a 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -2,37 +2,35 @@
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
 #include 
-#include 
 #include 
 #include 
-#include 
+#include 
+#include 
+#include 
 
+#include "auto/config.h"
+#include "klib/kvec.h"
 #include "nvim/ascii.h"
 #include "nvim/autocmd.h"
-#include "nvim/charset.h"
-#include "nvim/cursor.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/cursor_shape.h"
-#include "nvim/diff.h"
 #include "nvim/drawscreen.h"
+#include "nvim/event/defs.h"
 #include "nvim/event/loop.h"
 #include "nvim/ex_getln.h"
-#include "nvim/fold.h"
-#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/grid.h"
 #include "nvim/highlight.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/log.h"
 #include "nvim/main.h"
-#include "nvim/mbyte.h"
 #include "nvim/memory.h"
-#include "nvim/move.h"
+#include "nvim/message.h"
 #include "nvim/msgpack_rpc/channel.h"
-#include "nvim/normal.h"
 #include "nvim/option.h"
-#include "nvim/os/input.h"
-#include "nvim/os/signal.h"
 #include "nvim/os/time.h"
-#include "nvim/os_unix.h"
-#include "nvim/popupmenu.h"
+#include "nvim/strings.h"
 #include "nvim/ui.h"
 #include "nvim/ui_compositor.h"
 #include "nvim/vim.h"
diff --git a/src/nvim/ui.h b/src/nvim/ui.h
index 9034e7b764..c28393476e 100644
--- a/src/nvim/ui.h
+++ b/src/nvim/ui.h
@@ -6,9 +6,14 @@
 #include 
 
 #include "nvim/api/private/defs.h"
+#include "nvim/event/multiqueue.h"
 #include "nvim/globals.h"
 #include "nvim/highlight_defs.h"
+#include "nvim/macros.h"
 #include "nvim/memory.h"
+#include "nvim/types.h"
+
+struct ui_t;
 
 typedef enum {
   kUICmdline = 0,
@@ -74,11 +79,12 @@ typedef struct ui_event_callback {
   bool ext_widgets[kUIGlobalCount];
 } UIEventCallback;
 
+// uncrustify:off
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "ui.h.generated.h"
-
 # include "ui_events_call.h.generated.h"
 #endif
+// uncrustify:on
 
 EXTERN MultiQueue *resize_events;
 #endif  // NVIM_UI_H
diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c
index 809d278029..25c230a941 100644
--- a/src/nvim/ui_bridge.c
+++ b/src/nvim/ui_bridge.c
@@ -4,19 +4,19 @@
 // UI wrapper that sends requests to the UI thread.
 // Used by the built-in TUI and libnvim-based UIs.
 
-#include 
-#include 
 #include 
-#include 
+#include 
+#include 
 
+#include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
-#include "nvim/log.h"
+#include "nvim/event/loop.h"
+#include "nvim/grid_defs.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/main.h"
 #include "nvim/memory.h"
-#include "nvim/ugrid.h"
 #include "nvim/ui.h"
 #include "nvim/ui_bridge.h"
-#include "nvim/vim.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "ui_bridge.c.generated.h"
diff --git a/src/nvim/ui_bridge.h b/src/nvim/ui_bridge.h
index c18600a857..094367126a 100644
--- a/src/nvim/ui_bridge.h
+++ b/src/nvim/ui_bridge.h
@@ -3,11 +3,14 @@
 #ifndef NVIM_UI_BRIDGE_H
 #define NVIM_UI_BRIDGE_H
 
+#include 
 #include 
 
 #include "nvim/event/defs.h"
 #include "nvim/ui.h"
 
+struct ui_bridge_data;
+
 typedef struct ui_bridge_data UIBridgeData;
 typedef void (*ui_main_fn)(UIBridgeData *bridge, UI *ui);
 struct ui_bridge_data {
diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c
index 265c54f72d..27c63433a7 100644
--- a/src/nvim/ui_client.c
+++ b/src/nvim/ui_client.c
@@ -1,26 +1,28 @@
 // This is an open source non-commercial project. Dear PVS-Studio, please check
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
-#include 
 #include 
 #include 
+#include 
 
-#include "nvim/api/private/dispatch.h"
 #include "nvim/api/private/helpers.h"
+#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
+#include "nvim/globals.h"
 #include "nvim/highlight.h"
 #include "nvim/log.h"
-#include "nvim/map.h"
+#include "nvim/main.h"
+#include "nvim/memory.h"
 #include "nvim/msgpack_rpc/channel.h"
-#include "nvim/screen.h"
 #include "nvim/ui.h"
 #include "nvim/ui_client.h"
-#include "nvim/vim.h"
 
+// uncrustify:off
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "ui_client.c.generated.h"
-
 # include "ui_events_client.generated.h"
 #endif
+// uncrustify:on
 
 void ui_client_init(uint64_t chan)
 {
diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h
index 311dafaa0b..bed73d83d3 100644
--- a/src/nvim/ui_client.h
+++ b/src/nvim/ui_client.h
@@ -1,8 +1,11 @@
 #ifndef NVIM_UI_CLIENT_H
 #define NVIM_UI_CLIENT_H
 
+#include 
+
 #include "nvim/api/private/defs.h"
 #include "nvim/grid_defs.h"
+#include "nvim/macros.h"
 
 typedef struct {
   const char *name;
diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c
index 84e1a5e513..32bdb7d273 100644
--- a/src/nvim/ui_compositor.c
+++ b/src/nvim/ui_compositor.c
@@ -7,25 +7,30 @@
 // Layer-based compositing: https://en.wikipedia.org/wiki/Digital_compositing
 
 #include 
+#include 
 #include 
 #include 
 #include 
+#include 
+#include 
 
 #include "klib/kvec.h"
-#include "nvim/api/private/helpers.h"
+#include "nvim/api/private/defs.h"
 #include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/globals.h"
 #include "nvim/grid.h"
 #include "nvim/highlight.h"
 #include "nvim/highlight_group.h"
 #include "nvim/log.h"
 #include "nvim/lua/executor.h"
-#include "nvim/main.h"
+#include "nvim/macros.h"
 #include "nvim/map.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
-#include "nvim/os/os.h"
-#include "nvim/popupmenu.h"
-#include "nvim/ugrid.h"
+#include "nvim/option_defs.h"
+#include "nvim/os/time.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/ui_compositor.h"
 #include "nvim/vim.h"
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index d450043d2a..054dcfa8d0 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -76,41 +76,57 @@
 #include 
 #include 
 #include 
-#include 
 #include 
+#include 
+#include 
 #include 
+#include 
+#include 
 
 #include "auto/config.h"
 #include "klib/kvec.h"
 #include "nvim/ascii.h"
+#include "nvim/autocmd.h"
 #include "nvim/buffer.h"
 #include "nvim/buffer_updates.h"
 #include "nvim/change.h"
 #include "nvim/cursor.h"
 #include "nvim/drawscreen.h"
 #include "nvim/edit.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
 #include "nvim/ex_getln.h"
 #include "nvim/extmark.h"
 #include "nvim/fileio.h"
 #include "nvim/fold.h"
 #include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
+#include "nvim/macros.h"
 #include "nvim/mark.h"
 #include "nvim/memline.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/option.h"
+#include "nvim/os/fs_defs.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
+#include "nvim/os/os_defs.h"
 #include "nvim/os/time.h"
 #include "nvim/os_unix.h"
 #include "nvim/path.h"
-#include "nvim/pos.h"  // MAXLNUM
+#include "nvim/pos.h"
+#include "nvim/screen.h"
 #include "nvim/sha256.h"
 #include "nvim/state.h"
 #include "nvim/strings.h"
 #include "nvim/types.h"
 #include "nvim/undo.h"
+#include "nvim/undo_defs.h"
+#include "nvim/vim.h"
 
 /// Structure passed around between undofile functions.
 typedef struct {
diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c
index 21a433d855..ebb7c9d6ca 100644
--- a/src/nvim/usercmd.c
+++ b/src/nvim/usercmd.c
@@ -6,19 +6,34 @@
 #include 
 #include 
 #include 
-#include 
+#include 
 #include 
 
+#include "auto/config.h"
+#include "lauxlib.h"
+#include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
 #include "nvim/charset.h"
 #include "nvim/eval.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
+#include "nvim/keycodes.h"
 #include "nvim/lua/executor.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option_defs.h"
 #include "nvim/os/input.h"
 #include "nvim/runtime.h"
+#include "nvim/strings.h"
 #include "nvim/usercmd.h"
+#include "nvim/vim.h"
 #include "nvim/window.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/usercmd.h b/src/nvim/usercmd.h
index 4d2cf0d9de..b6bf6c1e33 100644
--- a/src/nvim/usercmd.h
+++ b/src/nvim/usercmd.h
@@ -1,7 +1,12 @@
 #ifndef NVIM_USERCMD_H
 #define NVIM_USERCMD_H
 
+#include 
+
+#include "nvim/eval/typval_defs.h"
 #include "nvim/ex_cmds_defs.h"
+#include "nvim/garray.h"
+#include "nvim/types.h"
 
 typedef struct ucmd {
   char *uc_name;                // The command name
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 98f34ca11f..6b3279a990 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -7,27 +7,35 @@
 /// Vim originated from Stevie version 3.6 (Fish disk 217) by GRWalter (Fred).
 
 #include 
-#include 
 #include 
+#include 
+#include 
+#include 
+#include 
 
+#include "auto/config.h"
+#include "auto/versiondef.h"  // version info generated by the build system
+#include "auto/versiondef_git.h"
+#include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
 #include "nvim/ascii.h"
 #include "nvim/buffer.h"
 #include "nvim/charset.h"
 #include "nvim/drawscreen.h"
+#include "nvim/ex_cmds_defs.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
 #include "nvim/grid.h"
-#include "nvim/iconv.h"
+#include "nvim/highlight_defs.h"
 #include "nvim/lua/executor.h"
-#include "nvim/memline.h"
-#include "nvim/memory.h"
+#include "nvim/mbyte.h"
 #include "nvim/message.h"
+#include "nvim/option_defs.h"
+#include "nvim/os/os_defs.h"
 #include "nvim/strings.h"
 #include "nvim/version.h"
 #include "nvim/vim.h"
 
-// version info generated by the build system
-#include "auto/versiondef.h"
-
 // for ":version", ":intro", and "nvim --version"
 #ifndef NVIM_VERSION_MEDIUM
 # define NVIM_VERSION_MEDIUM "v" STR(NVIM_VERSION_MAJOR) \
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 4bcda9fc4f..7ac359da9a 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -1,7 +1,7 @@
 #ifndef NVIM_VIM_H
 #define NVIM_VIM_H
 
-#include "nvim/pos.h"  // for linenr_T, MAXCOL, etc...
+#include "nvim/pos.h"
 #include "nvim/types.h"
 
 // Some defines from the old feature.h
diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c
index 77f85b5d2d..24dfb38ae0 100644
--- a/src/nvim/viml/parser/expressions.c
+++ b/src/nvim/viml/parser/expressions.c
@@ -53,6 +53,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
 
 #include "klib/kvec.h"
@@ -60,6 +62,10 @@
 #include "nvim/assert.h"
 #include "nvim/charset.h"
 #include "nvim/eval/typval.h"
+#include "nvim/gettext.h"
+#include "nvim/keycodes.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
 #include "nvim/memory.h"
 #include "nvim/types.h"
 #include "nvim/vim.h"
diff --git a/src/nvim/viml/parser/expressions.h b/src/nvim/viml/parser/expressions.h
index 77fbfa615f..6fe6a784a0 100644
--- a/src/nvim/viml/parser/expressions.h
+++ b/src/nvim/viml/parser/expressions.h
@@ -6,9 +6,12 @@
 #include 
 
 #include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/types.h"
 #include "nvim/viml/parser/parser.h"
 
+struct expr_ast_node;
+
 // Defines whether to ignore case:
 //    ==   kCCStrategyUseOption
 //    ==#  kCCStrategyMatchCase
diff --git a/src/nvim/viml/parser/parser.c b/src/nvim/viml/parser/parser.c
index a41b750e76..1547feba90 100644
--- a/src/nvim/viml/parser/parser.c
+++ b/src/nvim/viml/parser/parser.c
@@ -4,7 +4,7 @@
 #include "nvim/viml/parser/parser.h"
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "viml/parser/parser.c.generated.h"
+# include "viml/parser/parser.c.generated.h"  // IWYU pragma: export
 #endif
 
 void parser_simple_get_line(void *cookie, ParserLine *ret_pline)
diff --git a/src/nvim/viml/parser/parser.h b/src/nvim/viml/parser/parser.h
index 56e8b2d32b..f387301c2d 100644
--- a/src/nvim/viml/parser/parser.h
+++ b/src/nvim/viml/parser/parser.h
@@ -8,6 +8,7 @@
 #include "klib/kvec.h"
 #include "nvim/func_attr.h"
 #include "nvim/mbyte.h"
+#include "nvim/mbyte_defs.h"
 #include "nvim/memory.h"
 
 /// One parsed line
diff --git a/src/nvim/window.c b/src/nvim/window.c
index de5bcb40ea..54ab9a0471 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -2,21 +2,30 @@
 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 
 #include 
+#include 
 #include 
+#include 
 #include 
+#include 
+#include 
+#include 
 #include 
 
+#include "nvim/api/private/defs.h"
 #include "nvim/api/private/helpers.h"
-#include "nvim/api/vim.h"
 #include "nvim/arglist.h"
 #include "nvim/ascii.h"
+#include "nvim/autocmd.h"
 #include "nvim/buffer.h"
 #include "nvim/charset.h"
 #include "nvim/cursor.h"
+#include "nvim/decoration.h"
 #include "nvim/diff.h"
 #include "nvim/drawscreen.h"
 #include "nvim/edit.h"
 #include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
 #include "nvim/eval/vars.h"
 #include "nvim/ex_cmds.h"
 #include "nvim/ex_cmds2.h"
@@ -28,15 +37,19 @@
 #include "nvim/fold.h"
 #include "nvim/garray.h"
 #include "nvim/getchar.h"
+#include "nvim/gettext.h"
 #include "nvim/globals.h"
 #include "nvim/grid.h"
 #include "nvim/hashtab.h"
-#include "nvim/highlight.h"
+#include "nvim/keycodes.h"
+#include "nvim/macros.h"
 #include "nvim/main.h"
-#include "nvim/mapping.h"
+#include "nvim/map.h"
+#include "nvim/mapping.h"  // IWYU pragma: keep (langmap_adjust_mb)
 #include "nvim/mark.h"
 #include "nvim/match.h"
-#include "nvim/memline.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline_defs.h"
 #include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/mouse.h"
@@ -45,17 +58,18 @@
 #include "nvim/option.h"
 #include "nvim/optionstr.h"
 #include "nvim/os/os.h"
-#include "nvim/os_unix.h"
 #include "nvim/path.h"
 #include "nvim/plines.h"
+#include "nvim/pos.h"
 #include "nvim/quickfix.h"
-#include "nvim/regexp.h"
+#include "nvim/screen.h"
 #include "nvim/search.h"
 #include "nvim/state.h"
 #include "nvim/statusline.h"
 #include "nvim/strings.h"
 #include "nvim/syntax.h"
 #include "nvim/terminal.h"
+#include "nvim/types.h"
 #include "nvim/ui.h"
 #include "nvim/ui_compositor.h"
 #include "nvim/undo.h"
diff --git a/src/nvim/window.h b/src/nvim/window.h
index a564a0cfad..8fd11d1ce9 100644
--- a/src/nvim/window.h
+++ b/src/nvim/window.h
@@ -2,10 +2,14 @@
 #define NVIM_WINDOW_H
 
 #include 
+#include 
 
+#include "nvim/buffer.h"
 #include "nvim/buffer_defs.h"
 #include "nvim/mark.h"
 #include "nvim/os/os.h"
+#include "nvim/os/os_defs.h"
+#include "nvim/vim.h"
 
 // Values for file_name_in_line()
 #define FNAME_MESS      1       // give error message
-- 
cgit 


From c70d90dbfdf67bb009d2976a5d0760be4010e533 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Tue, 15 Nov 2022 23:02:48 +0800
Subject: vim-patch:9.0.0884: mouse shape remains in op-pending mode after
 failed change (#21066)

Problem:    Mouse shape remains in op-pending mode after failed change.
Solution:   Reset finish_op and restore it. (closes vim/vim#11545)

https://github.com/vim/vim/commit/cdeb65729d96c90320b9009e583ade305c396f29
---
 src/nvim/ops.c                   | 12 ++++++++----
 src/nvim/testdir/test_normal.vim | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 41 insertions(+), 4 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index f61c978f3c..f1814291b8 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -2483,8 +2483,14 @@ int op_change(oparg_T *oap)
     fix_indent();
   }
 
+  // Reset finish_op now, don't want it set inside edit().
+  const bool save_finish_op = finish_op;
+  finish_op = false;
+
   retval = edit(NUL, false, (linenr_T)1);
 
+  finish_op = save_finish_op;
+
   // In Visual block mode, handle copying the new text to all lines of the
   // block.
   // Don't repeat the insert when Insert mode ended with CTRL-C.
@@ -5635,8 +5641,6 @@ bool set_ref_in_opfunc(int copyID)
 static void op_function(const oparg_T *oap)
   FUNC_ATTR_NONNULL_ALL
 {
-  const TriState save_virtual_op = virtual_op;
-  const bool save_finish_op = finish_op;
   const pos_T orig_start = curbuf->b_op_start;
   const pos_T orig_end = curbuf->b_op_end;
 
@@ -5663,9 +5667,11 @@ static void op_function(const oparg_T *oap)
 
     // Reset virtual_op so that 'virtualedit' can be changed in the
     // function.
+    const TriState save_virtual_op = virtual_op;
     virtual_op = kNone;
 
     // Reset finish_op so that mode() returns the right value.
+    const bool save_finish_op = finish_op;
     finish_op = false;
 
     typval_T rettv;
@@ -6205,8 +6211,6 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
         // Restore linebreak, so that when the user edits it looks as before.
         restore_lbr(lbr_saved);
 
-        // Reset finish_op now, don't want it set inside edit().
-        finish_op = false;
         if (op_change(oap)) {           // will call edit()
           cap->retval |= CA_COMMAND_BUSY;
         }
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index 0d75644920..e5756bd505 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -3713,4 +3713,37 @@ func Test_normal_count_out_of_range()
   bwipe!
 endfunc
 
+" Test that mouse shape is restored to Normal mode after failed "c" operation.
+func Test_mouse_shape_after_failed_change()
+  CheckFeature mouseshape
+  CheckCanRunGui
+
+  let lines =<< trim END
+    set mouseshape+=o:busy
+    setlocal nomodifiable
+    let g:mouse_shapes = []
+
+    func SaveMouseShape(timer)
+      let g:mouse_shapes += [getmouseshape()]
+    endfunc
+
+    func SaveAndQuit(timer)
+      call writefile(g:mouse_shapes, 'Xmouseshapes')
+      quit
+    endfunc
+
+    call timer_start(50, {_ -> feedkeys('c')})
+    call timer_start(100, 'SaveMouseShape')
+    call timer_start(150, {_ -> feedkeys('c')})
+    call timer_start(200, 'SaveMouseShape')
+    call timer_start(250, 'SaveAndQuit')
+  END
+  call writefile(lines, 'Xmouseshape.vim', 'D')
+  call RunVim([], [], "-g -S Xmouseshape.vim")
+  sleep 300m
+  call assert_equal(['busy', 'arrow'], readfile('Xmouseshapes'))
+
+  call delete('Xmouseshapes')
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From fd54194a4fd477d15a12c69106126514952eb563 Mon Sep 17 00:00:00 2001
From: luukvbaal <31730729+luukvbaal@users.noreply.github.com>
Date: Tue, 15 Nov 2022 21:57:30 +0100
Subject: refactor: convert drawline.c draw states to enum (#21067)

---
 src/nvim/drawline.c | 23 +++++++++++++----------
 1 file changed, 13 insertions(+), 10 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 9d6c95e20a..93f3ea569e 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -53,6 +53,18 @@
 #define MB_FILLER_CHAR '<'  // character used when a double-width character
                             // doesn't fit.
 
+/// possible draw states in win_line(), drawn in sequence.
+typedef enum {
+  WL_START = 0,  // nothing done yet
+  WL_CMDLINE,    // cmdline window column
+  WL_FOLD,       // 'foldcolumn'
+  WL_SIGN,       // column for signs
+  WL_NR,         // line number
+  WL_BRI,        // 'breakindent'
+  WL_SBR,        // 'showbreak' or 'diff'
+  WL_LINE,       // text in the line
+} LineDrawState;
+
 /// for line_putchar. Contains the state that needs to be remembered from
 /// putting one character to the next.
 typedef struct {
@@ -604,16 +616,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
   int left_curline_col = 0;
   int right_curline_col = 0;
 
-  // draw_state: items that are drawn in sequence:
-#define WL_START        0               // nothing done yet
-#define WL_CMDLINE      (WL_START + 1)    // cmdline window column
-#define WL_FOLD         (WL_CMDLINE + 1)  // 'foldcolumn'
-#define WL_SIGN         (WL_FOLD + 1)     // column for signs
-#define WL_NR           (WL_SIGN + 1)     // line number
-#define WL_BRI          (WL_NR + 1)       // 'breakindent'
-#define WL_SBR          (WL_BRI + 1)      // 'showbreak' or 'diff'
-#define WL_LINE         (WL_SBR + 1)      // text in the line
-  int draw_state = WL_START;            // what to draw next
+  LineDrawState draw_state = WL_START;  // what to draw next
 
   int syntax_flags    = 0;
   int syntax_seqnr    = 0;
-- 
cgit 


From fa7e1e26019112ff9e2ea42626995f04e2a4e032 Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Tue, 15 Nov 2022 21:27:42 +0000
Subject: fix(api): nvim_buf_get_text regression (#21071)

---
 src/nvim/api/buffer.c     | 14 +++++++-------
 src/nvim/buffer_updates.c |  4 ++--
 2 files changed, 9 insertions(+), 9 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 1101689391..0a31286df7 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -311,7 +311,7 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id,
 
   init_line_array(lstate, &rv, size);
 
-  if (!buf_collect_lines(buf, size, (linenr_T)start, (channel_id != VIML_INTERNAL_CALL), &rv,
+  if (!buf_collect_lines(buf, size, (linenr_T)start, 0, (channel_id != VIML_INTERNAL_CALL), &rv,
                          lstate, err)) {
     goto end;
   }
@@ -857,9 +857,8 @@ ArrayOf(String) nvim_buf_get_text(uint64_t channel_id, Buffer buffer,
   }
 
   if (size > 2) {
-    Array tmp = ARRAY_DICT_INIT;
-    tmp.items = &rv.items[1];
-    if (!buf_collect_lines(buf, size - 2, (linenr_T)start_row + 1, replace_nl, &tmp, lstate, err)) {
+    if (!buf_collect_lines(buf, size - 2, (linenr_T)start_row + 1, 1, replace_nl, &rv, lstate,
+                           err)) {
       goto end;
     }
   }
@@ -1464,12 +1463,13 @@ static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len,
 /// @param n Number of lines to collect
 /// @param replace_nl Replace newlines ("\n") with NUL
 /// @param start Line number to start from
+/// @param start_idx First index to push to
 /// @param[out] l If not NULL, Lines are copied here
 /// @param[out] lstate If not NULL, Lines are pushed into a table onto the stack
 /// @param err[out] Error, if any
 /// @return true unless `err` was set
-bool buf_collect_lines(buf_T *buf, size_t n, linenr_T start, bool replace_nl, Array *l,
-                       lua_State *lstate, Error *err)
+bool buf_collect_lines(buf_T *buf, size_t n, linenr_T start, int start_idx, bool replace_nl,
+                       Array *l, lua_State *lstate, Error *err)
 {
   for (size_t i = 0; i < n; i++) {
     linenr_T lnum = start + (linenr_T)i;
@@ -1482,7 +1482,7 @@ bool buf_collect_lines(buf_T *buf, size_t n, linenr_T start, bool replace_nl, Ar
     }
 
     char *bufstr = ml_get_buf(buf, lnum, false);
-    push_linestr(lstate, l, bufstr, strlen(bufstr), (int)i, replace_nl);
+    push_linestr(lstate, l, bufstr, strlen(bufstr), start_idx + (int)i, replace_nl);
   }
 
   return true;
diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c
index bee7db1e98..2c92fb26b2 100644
--- a/src/nvim/buffer_updates.c
+++ b/src/nvim/buffer_updates.c
@@ -81,7 +81,7 @@ bool buf_updates_register(buf_T *buf, uint64_t channel_id, BufUpdateCallbacks cb
       linedata.size = line_count;
       linedata.items = xcalloc(line_count, sizeof(Object));
 
-      buf_collect_lines(buf, line_count, 1, true, &linedata, NULL, NULL);
+      buf_collect_lines(buf, line_count, 1, 0, true, &linedata, NULL, NULL);
     }
 
     args.items[4] = ARRAY_OBJ(linedata);
@@ -242,7 +242,7 @@ void buf_updates_send_changes(buf_T *buf, linenr_T firstline, int64_t num_added,
       STATIC_ASSERT(SIZE_MAX >= MAXLNUM, "size_t smaller than MAXLNUM");
       linedata.size = (size_t)num_added;
       linedata.items = xcalloc((size_t)num_added, sizeof(Object));
-      buf_collect_lines(buf, (size_t)num_added, firstline, true, &linedata,
+      buf_collect_lines(buf, (size_t)num_added, firstline, 0, true, &linedata,
                         NULL, NULL);
     }
     args.items[4] = ARRAY_OBJ(linedata);
-- 
cgit 


From c7ee0fe38f4d3f41a9421094509a04c7e73835a9 Mon Sep 17 00:00:00 2001
From: Folke Lemaitre 
Date: Wed, 16 Nov 2022 22:45:15 +0100
Subject: fix: don't disable compositor widgets when a GUI with multigrid
 attaches

---
 src/nvim/ui.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 4beb4b344a..7bde8d2f5a 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -204,7 +204,7 @@ void ui_refresh(void)
     width = MIN(ui->width, width);
     height = MIN(ui->height, height);
     for (UIExtension j = 0; (int)j < kUIExtCount; j++) {
-      bool in_compositor = ui->composed && compositor->ui_ext[j];
+      bool in_compositor = (ui->composed || j < kUIGlobalCount) && compositor->ui_ext[j];
       ext_widgets[j] &= (ui->ui_ext[j] || in_compositor || inclusive);
     }
   }
-- 
cgit 


From fedf002cb34d0d7a50c54f84a2f161984db2a4c2 Mon Sep 17 00:00:00 2001
From: Jlll1 
Date: Thu, 17 Nov 2022 00:18:31 +0100
Subject: fix(api): nvim_win_set_cursor redraw cursorcolumn for non-current
 window (#21072)

fix #19063
this fixes the cursorcolumn not being redrawn for non-current windows in `nvim_win_set_cursor()`
---
 src/nvim/api/window.c | 9 +++++++--
 src/nvim/move.c       | 9 ---------
 2 files changed, 7 insertions(+), 11 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index 3f7c734cd1..8d17570077 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -118,8 +118,13 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err)
   // Make sure we stick in this column.
   win->w_set_curswant = true;
 
-  // make sure cursor is in visible range even if win != curwin
-  update_topline_win(win);
+  // make sure cursor is in visible range and
+  // cursorcolumn and cursorline are updated even if win != curwin
+  switchwin_T switchwin;
+  switch_win(&switchwin, win, NULL, true);
+  update_topline(curwin);
+  validate_cursor();
+  restore_win(&switchwin, true);
 
   redraw_later(win, UPD_VALID);
   win->w_redr_status = true;
diff --git a/src/nvim/move.c b/src/nvim/move.c
index df79b169b8..25d27d154b 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -341,15 +341,6 @@ void update_topline(win_T *wp)
   *so_ptr = save_so;
 }
 
-// Update win->w_topline to move the cursor onto the screen.
-void update_topline_win(win_T *win)
-{
-  switchwin_T switchwin;
-  switch_win(&switchwin, win, NULL, true);
-  update_topline(curwin);
-  restore_win(&switchwin, true);
-}
-
 // Return the scrolljump value to use for the current window.
 // When 'scrolljump' is positive use it as-is.
 // When 'scrolljump' is negative use it as a percentage of the window height.
-- 
cgit 


From ed27f0e93e177ffca1def9c64fdfc8c390a2dadd Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 07:51:04 +0800
Subject: vim-patch:9.0.0300: 'cpoptions' tests are flaky (#21081)

Problem:    'cpoptions' tests are flaky.
Solution:   Use a different file name for each test.

https://github.com/vim/vim/commit/a85e4db9780a4cf7a72cbb98c7127922f668cdf6

Co-authored-by: Bram Moolenaar 
---
 src/nvim/testdir/test_cpoptions.vim | 76 ++++++++++++++++++-------------------
 1 file changed, 38 insertions(+), 38 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_cpoptions.vim b/src/nvim/testdir/test_cpoptions.vim
index ef51d955f1..b9307ab30b 100644
--- a/src/nvim/testdir/test_cpoptions.vim
+++ b/src/nvim/testdir/test_cpoptions.vim
@@ -8,19 +8,19 @@ source view_util.vim
 " file name.
 func Test_cpo_a()
   let save_cpo = &cpo
-  call writefile(['one'], 'Xfile')
+  call writefile(['one'], 'XfileCpoA')
   " Wipe out all the buffers, so that the alternate file is empty
   edit Xfoo | %bw
   set cpo-=a
   new
-  read Xfile
+  read XfileCpoA
   call assert_equal('', @#)
   %d
   set cpo+=a
-  read Xfile
-  call assert_equal('Xfile', @#)
+  read XfileCpoA
+  call assert_equal('XfileCpoA', @#)
   close!
-  call delete('Xfile')
+  call delete('XfileCpoA')
   let &cpo = save_cpo
 endfunc
 
@@ -99,31 +99,31 @@ endfunc
 " Test for the 'C' flag in 'cpo' (line continuation)
 func Test_cpo_C()
   let save_cpo = &cpo
-  call writefile(['let l = [', '\ 1,', '\ 2]'], 'Xfile')
+  call writefile(['let l = [', '\ 1,', '\ 2]'], 'XfileCpoC')
   set cpo-=C
-  source Xfile
+  source XfileCpoC
   call assert_equal([1, 2], g:l)
   set cpo+=C
-  call assert_fails('source Xfile', ['E697:', 'E10:'])
-  call delete('Xfile')
+  call assert_fails('source XfileCpoC', ['E697:', 'E10:'])
+  call delete('XfileCpoC')
   let &cpo = save_cpo
 endfunc
 
 " Test for the 'd' flag in 'cpo' (tags relative to the current file)
 func Test_cpo_d()
   let save_cpo = &cpo
-  call mkdir('Xdir')
+  call mkdir('XdirCpoD')
   call writefile(["one\tXfile1\t/^one$/"], 'tags')
-  call writefile(["two\tXfile2\t/^two$/"], 'Xdir/tags')
+  call writefile(["two\tXfile2\t/^two$/"], 'XdirCpoD/tags')
   set tags=./tags
   set cpo-=d
-  edit Xdir/Xfile
+  edit XdirCpoD/Xfile
   call assert_equal('two', taglist('.*')[0].name)
   set cpo+=d
   call assert_equal('one', taglist('.*')[0].name)
   %bw!
   call delete('tags')
-  call delete('Xdir', 'rf')
+  call delete('XdirCpoD', 'rf')
   set tags&
   let &cpo = save_cpo
 endfunc
@@ -204,14 +204,14 @@ func Test_cpo_F()
   let save_cpo = &cpo
   new
   set cpo-=F
-  write Xfile
+  write XfileCpoF
   call assert_equal('', @%)
-  call delete('Xfile')
+  call delete('XfileCpoF')
   set cpo+=F
-  write Xfile
-  call assert_equal('Xfile', @%)
+  write XfileCpoF
+  call assert_equal('XfileCpoF', @%)
   close!
-  call delete('Xfile')
+  call delete('XfileCpoF')
   let &cpo = save_cpo
 endfunc
 
@@ -415,16 +415,16 @@ endfunc
 " Test for the 'O' flag in 'cpo' (overwriting an existing file)
 func Test_cpo_O()
   let save_cpo = &cpo
-  new Xfile
+  new XfileCpoO
   call setline(1, 'one')
-  call writefile(['two'], 'Xfile')
+  call writefile(['two'], 'XfileCpoO')
   set cpo-=O
   call assert_fails('write', 'E13:')
   set cpo+=O
   write
-  call assert_equal(['one'], readfile('Xfile'))
+  call assert_equal(['one'], readfile('XfileCpoO'))
   close!
-  call delete('Xfile')
+  call delete('XfileCpoO')
   let &cpo = save_cpo
 endfunc
 
@@ -434,18 +434,18 @@ endfunc
 " name)
 func Test_cpo_P()
   let save_cpo = &cpo
-  call writefile([], 'Xfile')
+  call writefile([], 'XfileCpoP')
   new
   call setline(1, 'one')
   set cpo+=F
   set cpo-=P
-  write >> Xfile
+  write >> XfileCpoP
   call assert_equal('', @%)
   set cpo+=P
-  write >> Xfile
-  call assert_equal('Xfile', @%)
+  write >> XfileCpoP
+  call assert_equal('XfileCpoP', @%)
   close!
-  call delete('Xfile')
+  call delete('XfileCpoP')
   let &cpo = save_cpo
 endfunc
 
@@ -638,8 +638,8 @@ endfunc
 " Test for the 'Z' flag in 'cpo' (write! resets 'readonly')
 func Test_cpo_Z()
   let save_cpo = &cpo
-  call writefile([], 'Xfile')
-  new Xfile
+  call writefile([], 'XfileCpoZ')
+  new XfileCpoZ
   setlocal readonly
   set cpo-=Z
   write!
@@ -649,7 +649,7 @@ func Test_cpo_Z()
   write!
   call assert_equal(1, &readonly)
   close!
-  call delete('Xfile')
+  call delete('XfileCpoZ')
   let &cpo = save_cpo
 endfunc
 
@@ -728,8 +728,8 @@ endfunc
 " flag)
 func Test_cpo_plus()
   let save_cpo = &cpo
-  call writefile([], 'Xfile')
-  new Xfile
+  call writefile([], 'XfileCpoPlus')
+  new XfileCpoPlus
   call setline(1, 'foo')
   write X1
   call assert_equal(1, &modified)
@@ -737,7 +737,7 @@ func Test_cpo_plus()
   write X2
   call assert_equal(0, &modified)
   close!
-  call delete('Xfile')
+  call delete('XfileCpoPlus')
   call delete('X1')
   call delete('X2')
   let &cpo = save_cpo
@@ -843,17 +843,17 @@ endfunc
 " loaded and ':preserve' is used.
 func Test_cpo_ampersand()
   throw 'Skipped: Nvim does not support cpoptions flag "&"'
-  call writefile(['one'], 'Xfile')
+  call writefile(['one'], 'XfileCpoAmp')
   let after =<< trim [CODE]
     set cpo+=&
     preserve
     qall
   [CODE]
-  if RunVim([], after, 'Xfile')
-    call assert_equal(1, filereadable('.Xfile.swp'))
-    call delete('.Xfile.swp')
+  if RunVim([], after, 'XfileCpoAmp')
+    call assert_equal(1, filereadable('.XfileCpoAmp.swp'))
+    call delete('.XfileCpoAmp.swp')
   endif
-  call delete('Xfile')
+  call delete('XfileCpoAmp')
 endfunc
 
 " Test for the '\' flag in 'cpo' (backslash in a [] range in a search pattern)
-- 
cgit 


From 98bcf49d2692af1f2c21f6d10fd056855d4ea238 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 17:34:13 +0800
Subject: vim-patch:8.2.1260: there is no good test for CursorHold (#21086)

Problem:    There is no good test for CursorHold.
Solution:   Add a test.  Remove duplicated test. (Yegappan Lakshmanan,
            closes vim/vim#6503

https://github.com/vim/vim/commit/7591116acffc45b5880c49244646651badac1629
---
 src/nvim/testdir/test_autocmd.vim | 31 ++++++++++++++++++++++++++++++-
 src/nvim/testdir/test_buffer.vim  |  9 ---------
 src/nvim/testdir/test_normal.vim  | 18 ------------------
 3 files changed, 30 insertions(+), 28 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index f4c32599f1..fb8adbb3a6 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -4,6 +4,7 @@ source shared.vim
 source check.vim
 source term_util.vim
 source screendump.vim
+source load.vim
 
 func s:cleanup_buffers() abort
   for bnr in range(1, bufnr('$'))
@@ -20,8 +21,36 @@ func Test_vim_did_enter()
   " becomes one.
 endfunc
 
+" Test for the CursorHold autocmd
+func Test_CursorHold_autocmd()
+  CheckRunVimInTerminal
+  call writefile(['one', 'two', 'three'], 'Xfile')
+  let before =<< trim END
+    set updatetime=10
+    au CursorHold * call writefile([line('.')], 'Xoutput', 'a')
+  END
+  call writefile(before, 'Xinit')
+  let buf = RunVimInTerminal('-S Xinit Xfile', {})
+  call term_wait(buf)
+  call term_sendkeys(buf, "gg")
+  call term_wait(buf)
+  sleep 50m
+  call term_sendkeys(buf, "j")
+  call term_wait(buf)
+  sleep 50m
+  call term_sendkeys(buf, "j")
+  call term_wait(buf)
+  sleep 50m
+  call StopVimInTerminal(buf)
+
+  call assert_equal(['1', '2', '3'], readfile('Xoutput')[-3:-1])
+
+  call delete('Xinit')
+  call delete('Xoutput')
+  call delete('Xfile')
+endfunc
+
 if has('timers')
-  source load.vim
 
   func ExitInsertMode(id)
     call feedkeys("\")
diff --git a/src/nvim/testdir/test_buffer.vim b/src/nvim/testdir/test_buffer.vim
index 3300278802..549aa691c8 100644
--- a/src/nvim/testdir/test_buffer.vim
+++ b/src/nvim/testdir/test_buffer.vim
@@ -67,15 +67,6 @@ func Test_bunload_with_offset()
   call assert_fails('1,4bunload', 'E16:')
   call assert_fails(',100bunload', 'E16:')
 
-  " Use a try-catch for this test. When assert_fails() is used for this
-  " test, the command fails with E515: instead of E90:
-  let caught_E90 = 0
-  try
-    $bunload
-  catch /E90:/
-    let caught_E90 = 1
-  endtry
-  call assert_equal(1, caught_E90)
   call assert_fails('$bunload', 'E90:')
 endfunc
 
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index e5756bd505..83709420bf 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -3001,24 +3001,6 @@ func Test_normal47_visual_buf_wipe()
   set nomodified
 endfunc
 
-func Test_normal47_autocmd()
-  " disabled, does not seem to be possible currently
-  throw "Skipped: not possible to test cursorhold autocmd while waiting for input in normal_cmd"
-  new
-  call append(0, repeat('-',20))
-  au CursorHold * call feedkeys('2l', '')
-  1
-  set updatetime=20
-  " should delete 12 chars (d12l)
-  call feedkeys('d1', '!')
-  call assert_equal('--------', getline(1))
-
-  " clean up
-  au! CursorHold
-  set updatetime=4000
-  bw!
-endfunc
-
 func Test_normal48_wincmd()
   new
   exe "norm! \c"
-- 
cgit 


From abe1dd308e1f37bb9b06a85da82e066135b86ff9 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 18:13:13 +0800
Subject: vim-patch:8.2.1970: it is easy to make mistakes when cleaning up swap
 files

Problem:    It is easy to make mistakes when cleaning up swap files after the
            system crashed.
Solution:   Warn for the process still running after recovery.  Do not
            automatically delete a swap file created on another system.
            (David Fries, closes vim/vim#7273)

https://github.com/vim/vim/commit/f883508e36c209d60388b944e04e22a3fcf603cf
---
 src/nvim/memline.c             | 26 +++++++++++++++++++++---
 src/nvim/testdir/test_swap.vim | 46 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 69 insertions(+), 3 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 1f601f0668..8b5aef3be1 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -1138,7 +1138,14 @@ void ml_recover(bool checkext)
     } else {
       msg(_("Recovery completed. Buffer contents equals file contents."));
     }
-    msg_puts(_("\nYou may want to delete the .swp file now.\n\n"));
+    msg_puts(_("\nYou may want to delete the .swp file now."));
+    if (os_proc_running((int)char_to_long(b0p->b0_pid))) {
+      // Warn there could be an active Vim on the same file, the user may
+      // want to kill it.
+      msg_puts(_("\nNote: process STILL RUNNING: "));
+      msg_outnum(char_to_long(b0p->b0_pid));
+    }
+    msg_puts("\n\n");
     cmdline_row = msg_row;
   }
   redraw_curbuf_later(UPD_NOT_VALID);
@@ -1533,14 +1540,27 @@ static bool swapfile_unchanged(char *fname)
     ret = false;
   }
 
+  // Host name must be known and must equal the current host name, otherwise
+  // comparing pid is meaningless.
+  if (*(b0.b0_hname) == NUL) {
+    ret = false;
+  } else {
+    char hostname[B0_HNAME_SIZE];
+    os_get_hostname(hostname, B0_HNAME_SIZE);
+    hostname[B0_HNAME_SIZE - 1] = NUL;
+    if (STRICMP(b0.b0_hname, hostname) != 0) {
+      ret = false;
+    }
+  }
+
   // process must be known and not running.
   long pid = char_to_long(b0.b0_pid);
   if (pid == 0L || os_proc_running((int)pid)) {
     ret = false;
   }
 
-  // TODO(bram): Should we check if the swap file was created on the current
-  // system?  And the current user?
+  // We do not check the user, it should be irrelevant for whether the swap
+  // file is still useful.
 
   close(fd);
   return ret;
diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim
index 34d1d585ce..7941f78ced 100644
--- a/src/nvim/testdir/test_swap.vim
+++ b/src/nvim/testdir/test_swap.vim
@@ -421,6 +421,52 @@ func Test_swap_symlink()
   call delete('Xswapdir', 'rf')
 endfunc
 
+func Test_swap_auto_delete()
+  " Create a valid swapfile by editing a file with a special extension.
+  split Xtest.scr
+  call setline(1, ['one', 'two', 'three'])
+  write  " file is written, not modified
+  write  " write again to make sure the swapfile is created
+  " read the swapfile as a Blob
+  let swapfile_name = swapname('%')
+  let swapfile_bytes = readfile(swapfile_name, 'B')
+
+  " Forget about the file, recreate the swap file, then edit it again.  The
+  " swap file should be automatically deleted.
+  bwipe!
+  " change the process ID to avoid the "still running" warning
+  let swapfile_bytes[24] = 0x99
+  call writefile(swapfile_bytes, swapfile_name)
+  edit Xtest.scr
+  " will end up using the same swap file after deleting the existing one
+  call assert_equal(swapfile_name, swapname('%'))
+  bwipe!
+
+  " create the swap file again, but change the host name so that it won't be
+  " deleted
+  autocmd! SwapExists
+  augroup test_swap_recover_ext
+    autocmd!
+    autocmd SwapExists * let v:swapchoice = 'e'
+  augroup END
+
+  " change the host name
+  let swapfile_bytes[28 + 40] = 0x89
+  call writefile(swapfile_bytes, swapfile_name)
+  edit Xtest.scr
+  call assert_equal(1, filereadable(swapfile_name))
+  " will use another same swap file name
+  call assert_notequal(swapfile_name, swapname('%'))
+  bwipe!
+
+  call delete('Xtest.scr')
+  call delete(swapfile_name)
+  augroup test_swap_recover_ext
+    autocmd!
+  augroup END
+  augroup! test_swap_recover_ext
+endfunc
+
 func Test_no_swap_file()
   call assert_equal("\nNo swap file", execute('swapname'))
 endfunc
-- 
cgit 


From f97d88a58a2d3e010d9de75815fb89b9cc7b8a3c Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 18:20:54 +0800
Subject: vim-patch:8.2.2016: swap file test is a little flaky

Problem:    Swap file test is a little flaky.
Solution:   Don't set a byte to a fixed value, increment it.

https://github.com/vim/vim/commit/c6ca9f3a29bfd6f5269749036f79f63ce6289692

Co-authored-by: Bram Moolenaar 
---
 src/nvim/testdir/test_swap.vim | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim
index 7941f78ced..4ab2e0df10 100644
--- a/src/nvim/testdir/test_swap.vim
+++ b/src/nvim/testdir/test_swap.vim
@@ -435,7 +435,7 @@ func Test_swap_auto_delete()
   " swap file should be automatically deleted.
   bwipe!
   " change the process ID to avoid the "still running" warning
-  let swapfile_bytes[24] = 0x99
+  let swapfile_bytes[24] = swapfile_bytes[24] + 1
   call writefile(swapfile_bytes, swapfile_name)
   edit Xtest.scr
   " will end up using the same swap file after deleting the existing one
@@ -451,7 +451,7 @@ func Test_swap_auto_delete()
   augroup END
 
   " change the host name
-  let swapfile_bytes[28 + 40] = 0x89
+  let swapfile_bytes[28 + 40] = swapfile_bytes[28 + 40] + 2
   call writefile(swapfile_bytes, swapfile_name)
   edit Xtest.scr
   call assert_equal(1, filereadable(swapfile_name))
-- 
cgit 


From 1b95eaf84bdc5189b435fe98b365b5f120d99c2d Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 18:23:38 +0800
Subject: vim-patch:8.2.2019: swap file test fails on MS-Windows

Problem:    Swap file test fails on MS-Windows.
Solution:   Add four to the process ID. (Ken Takata, closes vim/vim#7333)

https://github.com/vim/vim/commit/80d868ec25094615f2531a1e01ed1e729366c3bc
---
 src/nvim/testdir/test_swap.vim | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim
index 4ab2e0df10..22a4a17c39 100644
--- a/src/nvim/testdir/test_swap.vim
+++ b/src/nvim/testdir/test_swap.vim
@@ -434,8 +434,9 @@ func Test_swap_auto_delete()
   " Forget about the file, recreate the swap file, then edit it again.  The
   " swap file should be automatically deleted.
   bwipe!
-  " change the process ID to avoid the "still running" warning
-  let swapfile_bytes[24] = swapfile_bytes[24] + 1
+  " Change the process ID to avoid the "still running" warning.  Must add four
+  " for MS-Windows to see it as a different one.
+  let swapfile_bytes[24] = swapfile_bytes[24] + 4
   call writefile(swapfile_bytes, swapfile_name)
   edit Xtest.scr
   " will end up using the same swap file after deleting the existing one
-- 
cgit 


From 8cf38c2fd9408f7de9d9ff1e4db6fc90aba554cf Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 18:21:53 +0800
Subject: vim-patch:8.2.2026: Coverity warns for possibly using not NUL
 terminated string

Problem:    Coverity warns for possibly using not NUL terminated string.
Solution:   Put a NUL in b0_hname just in case.

https://github.com/vim/vim/commit/e79cdb69a4905ccf766494265d4c6f8701d10c39

Co-authored-by: Bram Moolenaar 
---
 src/nvim/memline.c | 1 +
 1 file changed, 1 insertion(+)

(limited to 'src/nvim')

diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 8b5aef3be1..02a7e1f697 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -1548,6 +1548,7 @@ static bool swapfile_unchanged(char *fname)
     char hostname[B0_HNAME_SIZE];
     os_get_hostname(hostname, B0_HNAME_SIZE);
     hostname[B0_HNAME_SIZE - 1] = NUL;
+    b0.b0_hname[B0_HNAME_SIZE - 1] = NUL;  // in case of corruption
     if (STRICMP(b0.b0_hname, hostname) != 0) {
       ret = false;
     }
-- 
cgit 


From 1fe526184f155bbda5d0d3037d683fd935b57c35 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 18:24:50 +0800
Subject: vim-patch:8.2.2044: MS-Windows: swap file test sometimes fails

Problem:    MS-Windows: swap file test sometimes fails.
Solution:   Use a more reliable way to change the process ID. When "timeout"
            fails use "ping" to wait up to ten minutes. (Ken Takata,
            closes vim/vim#7365)

https://github.com/vim/vim/commit/5ee0981fb5259f94900ab25207caddf1fa61010d
---
 src/nvim/testdir/test_swap.vim | 39 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 36 insertions(+), 3 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim
index 22a4a17c39..958e4f814f 100644
--- a/src/nvim/testdir/test_swap.vim
+++ b/src/nvim/testdir/test_swap.vim
@@ -421,6 +421,39 @@ func Test_swap_symlink()
   call delete('Xswapdir', 'rf')
 endfunc
 
+func s:get_unused_pid(base)
+  if has('job')
+    " Execute 'echo' as a temporary job, and return its pid as an unused pid.
+    if has('win32')
+      let cmd = 'cmd /c echo'
+    else
+      let cmd = 'echo'
+    endif
+    let j = job_start(cmd)
+    while job_status(j) ==# 'run'
+      sleep 10m
+    endwhile
+    if job_status(j) ==# 'dead'
+      return job_info(j).process
+    endif
+  endif
+  " Must add four for MS-Windows to see it as a different one.
+  return a:base + 4
+endfunc
+
+func s:blob_to_pid(b)
+  return a:b[3] * 16777216 + a:b[2] * 65536 + a:b[1] * 256 + a:b[0]
+endfunc
+
+func s:pid_to_blob(i)
+  let b = 0z
+  let b[0] = and(a:i, 0xff)
+  let b[1] = and(a:i / 256, 0xff)
+  let b[2] = and(a:i / 65536, 0xff)
+  let b[3] = and(a:i / 16777216, 0xff)
+  return b
+endfunc
+
 func Test_swap_auto_delete()
   " Create a valid swapfile by editing a file with a special extension.
   split Xtest.scr
@@ -434,9 +467,9 @@ func Test_swap_auto_delete()
   " Forget about the file, recreate the swap file, then edit it again.  The
   " swap file should be automatically deleted.
   bwipe!
-  " Change the process ID to avoid the "still running" warning.  Must add four
-  " for MS-Windows to see it as a different one.
-  let swapfile_bytes[24] = swapfile_bytes[24] + 4
+  " Change the process ID to avoid the "still running" warning.
+  let swapfile_bytes[24:27] = s:pid_to_blob(s:get_unused_pid(
+        \ s:blob_to_pid(swapfile_bytes[24:27])))
   call writefile(swapfile_bytes, swapfile_name)
   edit Xtest.scr
   " will end up using the same swap file after deleting the existing one
-- 
cgit 


From a8489308ab00f51bff197f29e76276b16ad0d985 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 18:41:07 +0800
Subject: vim-patch:8.2.3041: detecting if the process of a swap file is
 running fails

Problem:    Detecting if the process of a swap file is running fails if the
            process is owned by another user.
Solution:   Check for the ESRCH error. (closes vim/vim#8436)

https://github.com/vim/vim/commit/44dea9da4b2a21dd1e03f2bd94b4f2679d4613e5

Co-authored-by: Bram Moolenaar 
---
 src/nvim/os/process.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c
index d9273e69da..f4d95e141b 100644
--- a/src/nvim/os/process.c
+++ b/src/nvim/os/process.c
@@ -264,5 +264,16 @@ Dictionary os_proc_info(int pid)
 /// Return true if process `pid` is running.
 bool os_proc_running(int pid)
 {
-  return uv_kill(pid, 0) == 0;
+  int err = uv_kill(pid, 0);
+  // If there is no error the process must be running.
+  if (err == 0) {
+    return true;
+  }
+  // If the error is ESRCH then the process is not running.
+  if (err == UV_ESRCH) {
+    return false;
+  }
+  // If the process is running and owned by another user we get EPERM.  With
+  // other errors the process might be running, assuming it is then.
+  return true;
 }
-- 
cgit 


From 78998bc6c6dcd6945e39f427520f6851707eb344 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 18:44:13 +0800
Subject: vim-patch:8.2.3042: swap file test fails

Problem:    Swap file test fails.
Solution:   Check for a very high process ID instead of one, which should be
            running.

https://github.com/vim/vim/commit/6738fd2000f0bea4d40f4a8651e0e1f4b0503bb3

Co-authored-by: Bram Moolenaar 
---
 src/nvim/testdir/test_swap.vim | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim
index 958e4f814f..284579b084 100644
--- a/src/nvim/testdir/test_swap.vim
+++ b/src/nvim/testdir/test_swap.vim
@@ -203,8 +203,8 @@ func Test_swapfile_delete()
   " This test won't work as root because root can successfully run kill(1, 0)
   if !IsRoot()
     " Write the swapfile with a modified PID, now it will be automatically
-    " deleted. Process one should never be Vim.
-    let swapfile_bytes[24:27] = 0z01000000
+    " deleted. Process 0x3fffffff most likely does not exist.
+    let swapfile_bytes[24:27] = 0zffffff3f
     call writefile(swapfile_bytes, swapfile_name)
     let s:swapname = ''
     split XswapfileText
-- 
cgit 


From ec5c1c35c2b2477d79c20594a6fe64fe1d18ed36 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 19:57:16 +0800
Subject: vim-patch:8.2.2586: process id may be invalid

Problem:    Process id may be invalid.
Solution:   Use sysinfo.uptime to check for recent reboot. (suggested by Hugo
            van der Sanden, closes vim/vim#7947)

https://github.com/vim/vim/commit/f52f0606ed9ea19bcfc3a8343af9958f2d99eaf7

test_override() is N/A.

Co-authored-by: Bram Moolenaar 
---
 src/nvim/memline.c                | 26 ++++++++++---
 src/nvim/testdir/test_recover.vim | 79 ++++++++++++++++++++++++++++++++++-----
 2 files changed, 91 insertions(+), 14 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 02a7e1f697..955074e1f2 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -72,7 +72,7 @@
 #include "nvim/memory.h"
 #include "nvim/message.h"
 #include "nvim/option.h"
-#include "nvim/os/fs_defs.h"
+#include "nvim/os/fs.h"
 #include "nvim/os/input.h"
 #include "nvim/os/os.h"
 #include "nvim/os/process.h"
@@ -698,6 +698,23 @@ static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf)
   }
 }
 
+/// Return true if the process with number "b0p->b0_pid" is still running.
+/// "swap_fname" is the name of the swap file, if it's from before a reboot then
+/// the result is false;
+static bool swapfile_process_running(const ZERO_BL *b0p, const char *swap_fname)
+{
+  FileInfo st;
+  double uptime;
+  // If the system rebooted after when the swap file was written then the
+  // process can't be running now.
+  if (os_fileinfo(swap_fname, &st)
+      && uv_uptime(&uptime) == 0
+      && (Timestamp)st.stat.st_mtim.tv_sec < os_time() - (Timestamp)uptime) {
+    return false;
+  }
+  return os_proc_running((int)char_to_long(b0p->b0_pid));
+}
+
 /// Try to recover curbuf from the .swp file.
 ///
 /// @param checkext  if true, check the extension and detect whether it is a
@@ -1139,7 +1156,7 @@ void ml_recover(bool checkext)
       msg(_("Recovery completed. Buffer contents equals file contents."));
     }
     msg_puts(_("\nYou may want to delete the .swp file now."));
-    if (os_proc_running((int)char_to_long(b0p->b0_pid))) {
+    if (swapfile_process_running(b0p, fname_used)) {
       // Warn there could be an active Vim on the same file, the user may
       // want to kill it.
       msg_puts(_("\nNote: process STILL RUNNING: "));
@@ -1485,7 +1502,7 @@ static time_t swapfile_info(char_u *fname)
         if (char_to_long(b0.b0_pid) != 0L) {
           msg_puts(_("\n        process ID: "));
           msg_outnum(char_to_long(b0.b0_pid));
-          if (os_proc_running((int)char_to_long(b0.b0_pid))) {
+          if (swapfile_process_running(&b0, (const char *)fname)) {
             msg_puts(_(" (STILL RUNNING)"));
             process_still_running = true;
           }
@@ -1555,8 +1572,7 @@ static bool swapfile_unchanged(char *fname)
   }
 
   // process must be known and not running.
-  long pid = char_to_long(b0.b0_pid);
-  if (pid == 0L || os_proc_running((int)pid)) {
+  if (char_to_long(b0.b0_pid) == 0L || swapfile_process_running(&b0, fname)) {
     ret = false;
   }
 
diff --git a/src/nvim/testdir/test_recover.vim b/src/nvim/testdir/test_recover.vim
index fc073cacd2..c3bdde0d71 100644
--- a/src/nvim/testdir/test_recover.vim
+++ b/src/nvim/testdir/test_recover.vim
@@ -1,5 +1,7 @@
 " Test :recover
 
+source check.vim
+
 func Test_recover_root_dir()
   " This used to access invalid memory.
   split Xtest
@@ -23,6 +25,21 @@ func Test_recover_root_dir()
   set dir&
 endfunc
 
+" Make a copy of the current swap file to "Xswap".
+" Return the name of the swap file.
+func CopySwapfile()
+  preserve
+  " get the name of the swap file
+  let swname = split(execute("swapname"))[0]
+  let swname = substitute(swname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', '')
+  " make a copy of the swap file in Xswap
+  set binary
+  exe 'sp ' . swname
+  w! Xswap
+  set nobinary
+  return swname
+endfunc
+
 " Inserts 10000 lines with text to fill the swap file with two levels of pointer
 " blocks.  Then recovers from the swap file and checks all text is restored.
 "
@@ -40,15 +57,9 @@ func Test_swap_file()
     let i += 1
   endwhile
   $delete
-  preserve
-  " get the name of the swap file
-  let swname = split(execute("swapname"))[0]
-  let swname = substitute(swname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', '')
-  " make a copy of the swap file in Xswap
-  set binary
-  exe 'sp ' . swname
-  w! Xswap
-  set nobinary
+
+  let swname = CopySwapfile()
+
   new
   only!
   bwipe! Xtest
@@ -69,3 +80,53 @@ func Test_swap_file()
   set undolevels&
   enew! | only
 endfunc
+
+func Test_nocatch_process_still_running()
+  " assume Unix means sysinfo.uptime can be used
+  CheckUnix
+  CheckNotGui
+
+  " don't intercept existing swap file here
+  au! SwapExists
+
+  " Edit a file and grab its swapfile.
+  edit Xswaptest
+  call setline(1, ['a', 'b', 'c'])
+  let swname = CopySwapfile()
+
+  " Forget we edited this file
+  new
+  only!
+  bwipe! Xswaptest
+
+  call rename('Xswap', swname)
+  call feedkeys('e', 'tL')
+  redir => editOutput
+  edit Xswaptest
+  redir END
+  call assert_match('E325: ATTENTION', editOutput)
+  call assert_match('file name: .*Xswaptest', editOutput)
+  call assert_match('process ID: \d* (STILL RUNNING)', editOutput)
+
+  " Forget we edited this file
+  new
+  only!
+  bwipe! Xswaptest
+
+  " pretend we rebooted
+  call test_override("uptime", 0)
+  sleep 1
+
+  call rename('Xswap', swname)
+  call feedkeys('e', 'tL')
+  redir => editOutput
+  edit Xswaptest
+  redir END
+  call assert_match('E325: ATTENTION', editOutput)
+  call assert_notmatch('(STILL RUNNING)', editOutput)
+
+  call test_override("ALL", 0)
+  call delete(swname)
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From 1ab08e23b3ba8fed10323ef5572c48413cfa7d96 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 20:18:20 +0800
Subject: vim-patch:8.2.2587: recover test fails on FreeBSD

Problem:    Recover test fails on FreeBSD.
Solution:   Check for Linux.

https://github.com/vim/vim/commit/6635ae1437e6e343c0514524a8dfb19d9525b908

Co-authored-by: Bram Moolenaar 
---
 src/nvim/testdir/check.vim        | 8 ++++++++
 src/nvim/testdir/test_recover.vim | 5 +++--
 2 files changed, 11 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim
index 8a1080a2f3..61d3a99a67 100644
--- a/src/nvim/testdir/check.vim
+++ b/src/nvim/testdir/check.vim
@@ -90,6 +90,14 @@ func CheckUnix()
   endif
 endfunc
 
+" Command to check for running on Linux
+command CheckLinux call CheckLinux()
+func CheckLinux()
+  if !has('linux')
+    throw 'Skipped: only works on Linux'
+  endif
+endfunc
+
 " Command to check that making screendumps is supported.
 " Caller must source screendump.vim
 command CheckScreendump call CheckScreendump()
diff --git a/src/nvim/testdir/test_recover.vim b/src/nvim/testdir/test_recover.vim
index c3bdde0d71..60e0fde29e 100644
--- a/src/nvim/testdir/test_recover.vim
+++ b/src/nvim/testdir/test_recover.vim
@@ -82,8 +82,9 @@ func Test_swap_file()
 endfunc
 
 func Test_nocatch_process_still_running()
-  " assume Unix means sysinfo.uptime can be used
-  CheckUnix
+  " sysinfo.uptime probably only works on Linux
+  CheckLinux
+  " the GUI dialog can't be handled
   CheckNotGui
 
   " don't intercept existing swap file here
-- 
cgit 


From 5176ed88f675e839a87dab0a2d20c3bd0269514b Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 20:21:10 +0800
Subject: vim-patch:8.2.2589: recover test hangs in the GUI

Problem:    Recover test hangs in the GUI.
Solution:   Add g:skipped_reason to skip a _nocatch_ test.

https://github.com/vim/vim/commit/776b954622b45125dfdcb4a61243ca90956b0825

Now always skip the test as test_override() is N/A.

Co-authored-by: Bram Moolenaar 
---
 src/nvim/testdir/runtest.vim      |  5 +++++
 src/nvim/testdir/test_recover.vim | 12 ++++++++++--
 2 files changed, 15 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index ce23141c7a..a1b04a4601 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -174,7 +174,12 @@ func RunTheTest(test)
   if a:test =~ 'Test_nocatch_'
     " Function handles errors itself.  This avoids skipping commands after the
     " error.
+    let g:skipped_reason = ''
     exe 'call ' . a:test
+    if g:skipped_reason != ''
+      call add(s:messages, '    Skipped')
+      call add(s:skipped, 'SKIPPED ' . a:test . ': ' . g:skipped_reason)
+    endif
   else
     try
       let s:test = a:test
diff --git a/src/nvim/testdir/test_recover.vim b/src/nvim/testdir/test_recover.vim
index 60e0fde29e..9de4ddc546 100644
--- a/src/nvim/testdir/test_recover.vim
+++ b/src/nvim/testdir/test_recover.vim
@@ -82,10 +82,18 @@ func Test_swap_file()
 endfunc
 
 func Test_nocatch_process_still_running()
+  let g:skipped_reason = 'test_override() is N/A'
+  return
   " sysinfo.uptime probably only works on Linux
-  CheckLinux
+  if !has('linux')
+    let g:skipped_reason = 'only works on Linux'
+    return
+  endif
   " the GUI dialog can't be handled
-  CheckNotGui
+  if has('gui_running')
+    let g:skipped_reason = 'only works in the terminal'
+    return
+  endif
 
   " don't intercept existing swap file here
   au! SwapExists
-- 
cgit 


From 3f75f25f9c077a284aedab09c5813d887a62f464 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 22:40:10 +0800
Subject: vim-patch:8.2.2945: some buffer related code is not tested

Problem:    Some buffer related code is not tested.
Solution:   Add a few more tests. (Yegappan Lakshmanan, closes vim/vim#8320)

https://github.com/vim/vim/commit/59b262362f26b3aaea1eeb0078adc33eed59863e

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/testdir/test_excmd.vim   |   8 +++
 src/nvim/testdir/test_recover.vim | 127 ++++++++++++++++++++++++++++++++++++++
 src/nvim/testdir/test_swap.vim    |  75 ++++++++++++++++++++++
 3 files changed, 210 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim
index 42b1f8ca48..44bed890f5 100644
--- a/src/nvim/testdir/test_excmd.vim
+++ b/src/nvim/testdir/test_excmd.vim
@@ -78,6 +78,14 @@ func Test_file_cmd()
   call assert_fails('3file', 'E474:')
   call assert_fails('0,0file', 'E474:')
   call assert_fails('0file abc', 'E474:')
+  if !has('win32')
+    " Change the name of the buffer to the same name
+    new Xfile1
+    file Xfile1
+    call assert_equal('Xfile1', @%)
+    call assert_equal('Xfile1', @#)
+    bw!
+  endif
 endfunc
 
 " Test for the :drop command
diff --git a/src/nvim/testdir/test_recover.vim b/src/nvim/testdir/test_recover.vim
index 9de4ddc546..61d8684033 100644
--- a/src/nvim/testdir/test_recover.vim
+++ b/src/nvim/testdir/test_recover.vim
@@ -138,4 +138,131 @@ func Test_nocatch_process_still_running()
   call delete(swname)
 endfunc
 
+" Test for :recover with multiple swap files
+func Test_recover_multiple_swap_files()
+  CheckUnix
+  new Xfile1
+  call setline(1, ['a', 'b', 'c'])
+  preserve
+  let b = readblob('.Xfile1.swp')
+  call writefile(b, '.Xfile1.swm')
+  call writefile(b, '.Xfile1.swn')
+  call writefile(b, '.Xfile1.swo')
+  %bw!
+  call feedkeys(":recover Xfile1\3\q", 'xt')
+  call assert_equal(['a', 'b', 'c'], getline(1, '$'))
+
+  call delete('.Xfile1.swm')
+  call delete('.Xfile1.swn')
+  call delete('.Xfile1.swo')
+endfunc
+
+" Test for :recover using an empty swap file
+func Test_recover_empty_swap_file()
+  CheckUnix
+  call writefile([], '.Xfile1.swp')
+  let msg = execute('recover Xfile1')
+  call assert_match('Unable to read block 0 from .Xfile1.swp', msg)
+  call assert_equal('Xfile1', @%)
+  bw!
+  " :recover from an empty buffer
+  call assert_fails('recover', 'E305:')
+  call delete('.Xfile1.swp')
+endfunc
+
+" Test for :recover using a corrupted swap file
+func Test_recover_corrupted_swap_file()
+  CheckUnix
+  " recover using a partial swap file
+  call writefile(0z1234, '.Xfile1.swp')
+  call assert_fails('recover Xfile1', 'E295:')
+  bw!
+
+  " recover using invalid content in the swap file
+  call writefile([repeat('1', 2*1024)], '.Xfile1.swp')
+  call assert_fails('recover Xfile1', 'E307:')
+  call delete('.Xfile1.swp')
+
+  " :recover using a swap file with a corrupted header
+  edit Xfile1
+  preserve
+  let sn = swapname('')
+  let b = readblob(sn)
+  bw!
+  " clear the B0_MAGIC_LONG field
+  let b[1008:1011] = 0z00000000
+  call writefile(b, sn)
+  let msg = execute('recover Xfile1')
+  call assert_match('the file has been damaged', msg)
+  bw!
+  call delete(sn)
+endfunc
+
+" Test for :recover using an encrypted swap file
+func Test_recover_encrypted_swap_file()
+  CheckUnix
+
+  " Recover an encrypted file from the swap file without the original file
+  new Xfile1
+  call feedkeys(":X\vim\vim\", 'xt')
+  call setline(1, ['aaa', 'bbb', 'ccc'])
+  preserve
+  let b = readblob('.Xfile1.swp')
+  call writefile(b, '.Xfile1.swm')
+  bw!
+  call feedkeys(":recover Xfile1\vim\\", 'xt')
+  call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
+  bw!
+  call delete('.Xfile1.swm')
+
+  " Recover an encrypted file from the swap file with the original file
+  new Xfile1
+  call feedkeys(":X\vim\vim\", 'xt')
+  call setline(1, ['aaa', 'bbb', 'ccc'])
+  update
+  call setline(1, ['111', '222', '333'])
+  preserve
+  let b = readblob('.Xfile1.swp')
+  call writefile(b, '.Xfile1.swm')
+  bw!
+  call feedkeys(":recover Xfile1\vim\\", 'xt')
+  call assert_equal(['111', '222', '333'], getline(1, '$'))
+  call assert_true(&modified)
+  bw!
+  call delete('.Xfile1.swm')
+  call delete('Xfile1')
+endfunc
+
+" Test for :recover using a unreadable swap file
+func Test_recover_unreadble_swap_file()
+  CheckUnix
+  CheckNotRoot
+  new Xfile1
+  let b = readblob('.Xfile1.swp')
+  call writefile(b, '.Xfile1.swm')
+  bw!
+  call setfperm('.Xfile1.swm', '-w-------')
+  call assert_fails('recover Xfile1', 'E306:')
+  call delete('.Xfile1.swm')
+endfunc
+
+" Test for using :recover when the original file and the swap file have the
+" same contents.
+func Test_recover_unmodified_file()
+  CheckUnix
+  call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1')
+  edit Xfile1
+  preserve
+  let b = readblob('.Xfile1.swp')
+  %bw!
+  call writefile(b, '.Xfile1.swz')
+  let msg = execute('recover Xfile1')
+  call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
+  call assert_false(&modified)
+  call assert_match('Buffer contents equals file contents', msg)
+  bw!
+  call delete('Xfile1')
+  call delete('.Xfile1.swz')
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim
index 284579b084..10c0fed783 100644
--- a/src/nvim/testdir/test_swap.vim
+++ b/src/nvim/testdir/test_swap.vim
@@ -501,6 +501,81 @@ func Test_swap_auto_delete()
   augroup! test_swap_recover_ext
 endfunc
 
+" Test for renaming a buffer when the swap file is deleted out-of-band
+func Test_missing_swap_file()
+  CheckUnix
+  new Xfile1
+  call delete('.Xfile1.swp')
+  call assert_fails('file Xfile2', 'E301:')
+  call assert_equal('Xfile2', bufname())
+  call assert_true(bufexists('Xfile1'))
+  call assert_true(bufexists('Xfile2'))
+  %bw!
+endfunc
+
+" Test for :preserve command
+func Test_preserve()
+  new Xfile1
+  setlocal noswapfile
+  call assert_fails('preserve', 'E313:')
+  bw!
+endfunc
+
+" Test for the v:swapchoice variable
+func Test_swapchoice()
+  call writefile(['aaa', 'bbb'], 'Xfile1')
+  edit Xfile1
+  preserve
+  let swapfname = swapname('')
+  let b = readblob(swapfname)
+  bw!
+  call writefile(b, swapfname)
+
+  autocmd! SwapExists
+
+  " Test for v:swapchoice = 'o' (readonly)
+  augroup test_swapchoice
+    autocmd!
+    autocmd SwapExists * let v:swapchoice = 'o'
+  augroup END
+  edit Xfile1
+  call assert_true(&readonly)
+  call assert_equal(['aaa', 'bbb'], getline(1, '$'))
+  %bw!
+  call assert_true(filereadable(swapfname))
+
+  " Test for v:swapchoice = 'a' (abort)
+  augroup test_swapchoice
+    autocmd!
+    autocmd SwapExists * let v:swapchoice = 'a'
+  augroup END
+  try
+    edit Xfile1
+  catch /^Vim:Interrupt$/
+  endtry
+  call assert_equal('', @%)
+  call assert_true(bufexists('Xfile1'))
+  %bw!
+  call assert_true(filereadable(swapfname))
+
+  " Test for v:swapchoice = 'd' (delete)
+  augroup test_swapchoice
+    autocmd!
+    autocmd SwapExists * let v:swapchoice = 'd'
+  augroup END
+  edit Xfile1
+  call assert_equal('Xfile1', @%)
+  %bw!
+  call assert_false(filereadable(swapfname))
+
+  call delete('Xfile1')
+  call delete(swapfname)
+  augroup test_swapchoice
+    autocmd!
+  augroup END
+  augroup! test_swapchoice
+endfunc
+
 func Test_no_swap_file()
   call assert_equal("\nNo swap file", execute('swapname'))
 endfunc
-- 
cgit 


From 442a7e89b8de0959c41007f42b1a59cf52b814fe Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 22:52:58 +0800
Subject: vim-patch:8.2.2952: recover test fails on big endian systems

Problem:    Recover test fails on big endian systems.
Solution:   Disable the failing test on big endian systems. (Yegappan
            Lakshmanan, closes vim/vim#8335)

https://github.com/vim/vim/commit/99285550a9957e2c8669f183557944c6513c4875

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/testdir/test_recover.vim | 42 +++++++++++++++++++++++++++++++++------
 src/nvim/testdir/test_swap.vim    |  2 +-
 2 files changed, 37 insertions(+), 7 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_recover.vim b/src/nvim/testdir/test_recover.vim
index 61d8684033..ce9de8b87d 100644
--- a/src/nvim/testdir/test_recover.vim
+++ b/src/nvim/testdir/test_recover.vim
@@ -144,7 +144,7 @@ func Test_recover_multiple_swap_files()
   new Xfile1
   call setline(1, ['a', 'b', 'c'])
   preserve
-  let b = readblob('.Xfile1.swp')
+  let b = readblob(swapname(''))
   call writefile(b, '.Xfile1.swm')
   call writefile(b, '.Xfile1.swn')
   call writefile(b, '.Xfile1.swo')
@@ -173,6 +173,7 @@ endfunc
 " Test for :recover using a corrupted swap file
 func Test_recover_corrupted_swap_file()
   CheckUnix
+
   " recover using a partial swap file
   call writefile(0z1234, '.Xfile1.swp')
   call assert_fails('recover Xfile1', 'E295:')
@@ -188,12 +189,41 @@ func Test_recover_corrupted_swap_file()
   preserve
   let sn = swapname('')
   let b = readblob(sn)
+  let save_b = copy(b)
   bw!
-  " clear the B0_MAGIC_LONG field
-  let b[1008:1011] = 0z00000000
-  call writefile(b, sn)
-  let msg = execute('recover Xfile1')
-  call assert_match('the file has been damaged', msg)
+  " Run these tests only on little-endian systems. These tests fail on a
+  " big-endian system (IBM S390x system).
+  if b[1008:1011] == 0z33323130
+        \ && b[4096:4097] == 0z7470
+        \ && b[8192:8193] == 0z6164
+
+    " clear the B0_MAGIC_LONG field
+    let b[1008:1011] = 0z00000000
+    call writefile(b, sn)
+    let msg = execute('recover Xfile1')
+    call assert_match('the file has been damaged', msg)
+    bw!
+
+    " clear the pointer ID
+    let b = copy(save_b)
+    let b[4096:4097] = 0z0000
+    call writefile(b, sn)
+    call assert_fails('recover Xfile1', 'E310:')
+    bw!
+
+    " clear the data block ID
+    let b = copy(save_b)
+    let b[8192:8193] = 0z0000
+    call writefile(b, sn)
+    call assert_fails('recover Xfile1', 'E312:')
+    bw!
+
+    " remove the data block
+    let b = copy(save_b)
+    call writefile(b[:8191], sn)
+    call assert_fails('recover Xfile1', 'E312:')
+  endif
+
   bw!
   call delete(sn)
 endfunc
diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim
index 10c0fed783..024a3b02bc 100644
--- a/src/nvim/testdir/test_swap.vim
+++ b/src/nvim/testdir/test_swap.vim
@@ -505,7 +505,7 @@ endfunc
 func Test_missing_swap_file()
   CheckUnix
   new Xfile1
-  call delete('.Xfile1.swp')
+  call delete(swapname(''))
   call assert_fails('file Xfile2', 'E301:')
   call assert_equal('Xfile2', bufname())
   call assert_true(bufexists('Xfile1'))
-- 
cgit 


From a8f54a94c11d8ff3da299e54de4cbbcfec1fd4ac Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 22:53:40 +0800
Subject: vim-patch:8.2.2960: swap file recovery not sufficiently tested

Problem:    Swap file recovery not sufficiently tested.
Solution:   Add a few more tests. (Yegappan Lakshmanan, closes vim/vim#8339)

https://github.com/vim/vim/commit/8cf02e5cf8fb14a5009f12e7af0a47617a0ce88d

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/testdir/test_recover.vim | 100 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 100 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_recover.vim b/src/nvim/testdir/test_recover.vim
index ce9de8b87d..89f894a4bb 100644
--- a/src/nvim/testdir/test_recover.vim
+++ b/src/nvim/testdir/test_recover.vim
@@ -151,6 +151,16 @@ func Test_recover_multiple_swap_files()
   %bw!
   call feedkeys(":recover Xfile1\3\q", 'xt')
   call assert_equal(['a', 'b', 'c'], getline(1, '$'))
+  " try using out-of-range number to select a swap file
+  bw!
+  call feedkeys(":recover Xfile1\4\q", 'xt')
+  call assert_equal('Xfile1', @%)
+  call assert_equal([''], getline(1, '$'))
+  bw!
+  call feedkeys(":recover Xfile1\0\q", 'xt')
+  call assert_equal('Xfile1', @%)
+  call assert_equal([''], getline(1, '$'))
+  bw!
 
   call delete('.Xfile1.swm')
   call delete('.Xfile1.swn')
@@ -171,6 +181,8 @@ func Test_recover_empty_swap_file()
 endfunc
 
 " Test for :recover using a corrupted swap file
+" Refer to the comments in the memline.c file for the swap file headers
+" definition.
 func Test_recover_corrupted_swap_file()
   CheckUnix
 
@@ -202,6 +214,18 @@ func Test_recover_corrupted_swap_file()
     call writefile(b, sn)
     let msg = execute('recover Xfile1')
     call assert_match('the file has been damaged', msg)
+    call assert_equal('Xfile1', @%)
+    call assert_equal([''], getline(1, '$'))
+    bw!
+
+    " reduce the page size
+    let b = copy(save_b)
+    let b[12:15] = 0z00010000
+    call writefile(b, sn)
+    let msg = execute('recover Xfile1')
+    call assert_match('page size is smaller than minimum value', msg)
+    call assert_equal('Xfile1', @%)
+    call assert_equal([''], getline(1, '$'))
     bw!
 
     " clear the pointer ID
@@ -209,6 +233,26 @@ func Test_recover_corrupted_swap_file()
     let b[4096:4097] = 0z0000
     call writefile(b, sn)
     call assert_fails('recover Xfile1', 'E310:')
+    call assert_equal('Xfile1', @%)
+    call assert_equal([''], getline(1, '$'))
+    bw!
+
+    " set the number of pointers in a pointer block to zero
+    let b = copy(save_b)
+    let b[4098:4099] = 0z0000
+    call writefile(b, sn)
+    call assert_fails('recover Xfile1', 'E312:')
+    call assert_equal('Xfile1', @%)
+    call assert_equal(['???EMPTY BLOCK'], getline(1, '$'))
+    bw!
+
+    " set the block number in a pointer entry to a negative number
+    let b = copy(save_b)
+    let b[4104:4111] = 0z00000000.00000080
+    call writefile(b, sn)
+    call assert_fails('recover Xfile1', 'E312:')
+    call assert_equal('Xfile1', @%)
+    call assert_equal(['???LINES MISSING'], getline(1, '$'))
     bw!
 
     " clear the data block ID
@@ -216,12 +260,45 @@ func Test_recover_corrupted_swap_file()
     let b[8192:8193] = 0z0000
     call writefile(b, sn)
     call assert_fails('recover Xfile1', 'E312:')
+    call assert_equal('Xfile1', @%)
+    call assert_equal(['???BLOCK MISSING'], getline(1, '$'))
+    bw!
+
+    " set the number of lines in the data block to zero
+    let b = copy(save_b)
+    let b[8208:8211] = 0z00000000
+    call writefile(b, sn)
+    call assert_fails('recover Xfile1', 'E312:')
+    call assert_equal('Xfile1', @%)
+    call assert_equal(['??? from here until ???END lines may have been inserted/deleted',
+          \ '???END'], getline(1, '$'))
+    bw!
+
+    " use an invalid text start for the lines in a data block
+    let b = copy(save_b)
+    let b[8216:8219] = 0z00000000
+    call writefile(b, sn)
+    call assert_fails('recover Xfile1', 'E312:')
+    call assert_equal('Xfile1', @%)
+    call assert_equal(['???'], getline(1, '$'))
+    bw!
+
+    " use an incorrect text end (db_txt_end) for the data block
+    let b = copy(save_b)
+    let b[8204:8207] = 0z80000000
+    call writefile(b, sn)
+    call assert_fails('recover Xfile1', 'E312:')
+    call assert_equal('Xfile1', @%)
+    call assert_equal(['??? from here until ???END lines may be messed up', '',
+          \ '???END'], getline(1, '$'))
     bw!
 
     " remove the data block
     let b = copy(save_b)
     call writefile(b[:8191], sn)
     call assert_fails('recover Xfile1', 'E312:')
+    call assert_equal('Xfile1', @%)
+    call assert_equal(['???MANY LINES MISSING'], getline(1, '$'))
   endif
 
   bw!
@@ -295,4 +372,27 @@ func Test_recover_unmodified_file()
   call delete('.Xfile1.swz')
 endfunc
 
+" Test for recovering a file when editing a symbolically linked file
+func Test_recover_symbolic_link()
+  CheckUnix
+  call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1')
+  silent !ln -s Xfile1 Xfile2
+  edit Xfile2
+  call assert_equal('.Xfile1.swp', fnamemodify(swapname(''), ':t'))
+  preserve
+  let b = readblob('.Xfile1.swp')
+  %bw!
+  call writefile([], 'Xfile1')
+  call writefile(b, '.Xfile1.swp')
+  silent! recover Xfile2
+  call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
+  call assert_true(&modified)
+  update
+  %bw!
+  call assert_equal(['aaa', 'bbb', 'ccc'], readfile('Xfile1'))
+  call delete('Xfile1')
+  call delete('Xfile2')
+  call delete('.Xfile1.swp')
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From f12a45c45eab75578fa18b1c6395f80b8749f7ef Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 22:57:08 +0800
Subject: vim-patch:8.2.2973: fix for recovery and diff mode not tested

Problem:    Fix for recovery and diff mode not tested.
Solution:   Add a few more tests. (Yegappan Lakshmanan, closes vim/vim#8352)

https://github.com/vim/vim/commit/3044324e8dccd470bd854cf7d9457232cc9c220e

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/testdir/test_diffmode.vim      | 117 ++++++++++++++++++++++++++++++++
 src/nvim/testdir/test_prompt_buffer.vim |   2 +
 src/nvim/testdir/test_recover.vim       |  44 ++++++++++++
 3 files changed, 163 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim
index 0de5310735..d83cd505a6 100644
--- a/src/nvim/testdir/test_diffmode.vim
+++ b/src/nvim/testdir/test_diffmode.vim
@@ -243,6 +243,36 @@ func Test_diffput_two()
   bwipe! b
 endfunc
 
+" Test for :diffget/:diffput with a range that is inside a diff chunk
+func Test_diffget_diffput_range()
+  call setline(1, range(1, 10))
+  new
+  call setline(1, range(11, 20))
+  windo diffthis
+  3,5diffget
+  call assert_equal(['13', '14', '15'], getline(3, 5))
+  call setline(1, range(1, 10))
+  4,8diffput
+  wincmd p
+  call assert_equal(['13', '4', '5', '6', '7', '8', '19'], getline(3, 9))
+  %bw!
+endfunc
+
+" Test for :diffget/:diffput with an empty buffer and a non-empty buffer
+func Test_diffget_diffput_empty_buffer()
+  %d _
+  new
+  call setline(1, 'one')
+  windo diffthis
+  diffget
+  call assert_equal(['one'], getline(1, '$'))
+  %d _
+  diffput
+  wincmd p
+  call assert_equal([''], getline(1, '$'))
+  %bw!
+endfunc
+
 " :diffput and :diffget completes names of buffers which
 " are in diff mode and which are different then current buffer.
 " No completion when the current window is not in diff mode.
@@ -645,7 +675,11 @@ func Test_diffexpr()
   call assert_equal(normattr, screenattr(1, 1))
   call assert_equal(normattr, screenattr(2, 1))
   call assert_notequal(normattr, screenattr(3, 1))
+  diffoff!
 
+  " Try using an non-existing function for 'diffexpr'.
+  set diffexpr=NewDiffFunc()
+  call assert_fails('windo diffthis', ['E117:', 'E97:'])
   diffoff!
   %bwipe!
   set diffexpr& diffopt&
@@ -1316,6 +1350,89 @@ func Test_diff_filler_cursorcolumn()
   call delete('Xtest_diff_cuc')
 endfunc
 
+" Test for adding/removing lines inside diff chunks, between diff chunks
+" and before diff chunks
+func Test_diff_modify_chunks()
+  enew!
+  let w2_id = win_getid()
+  call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'])
+  new
+  let w1_id = win_getid()
+  call setline(1, ['a', '2', '3', 'd', 'e', 'f', '7', '8', 'i'])
+  windo diffthis
+
+  " remove a line between two diff chunks and create a new diff chunk
+  call win_gotoid(w2_id)
+  5d
+  call win_gotoid(w1_id)
+  call diff_hlID(5, 1)->synIDattr('name')->assert_equal('DiffAdd')
+
+  " add a line between two diff chunks
+  call win_gotoid(w2_id)
+  normal! 4Goe
+  call win_gotoid(w1_id)
+  call diff_hlID(4, 1)->synIDattr('name')->assert_equal('')
+  call diff_hlID(5, 1)->synIDattr('name')->assert_equal('')
+
+  " remove all the lines in a diff chunk.
+  call win_gotoid(w2_id)
+  7,8d
+  call win_gotoid(w1_id)
+  let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')})
+  call assert_equal(['', 'DiffText', 'DiffText', '', '', '', 'DiffAdd',
+        \ 'DiffAdd', ''], hl)
+
+  " remove lines from one diff chunk to just before the next diff chunk
+  call win_gotoid(w2_id)
+  call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'])
+  2,6d
+  call win_gotoid(w1_id)
+  let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')})
+  call assert_equal(['', 'DiffText', 'DiffText', 'DiffAdd', 'DiffAdd',
+        \ 'DiffAdd', 'DiffAdd', 'DiffAdd', ''], hl)
+
+  " remove lines just before the top of a diff chunk
+  call win_gotoid(w2_id)
+  call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'])
+  5,6d
+  call win_gotoid(w1_id)
+  let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')})
+  call assert_equal(['', 'DiffText', 'DiffText', '', 'DiffText', 'DiffText',
+        \ 'DiffAdd', 'DiffAdd', ''], hl)
+
+  " remove line after the end of a diff chunk
+  call win_gotoid(w2_id)
+  call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'])
+  4d
+  call win_gotoid(w1_id)
+  let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')})
+  call assert_equal(['', 'DiffText', 'DiffText', 'DiffAdd', '', '', 'DiffText',
+        \ 'DiffText', ''], hl)
+
+  " remove lines starting from the end of one diff chunk and ending inside
+  " another diff chunk
+  call win_gotoid(w2_id)
+  call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'])
+  4,7d
+  call win_gotoid(w1_id)
+  let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')})
+  call assert_equal(['', 'DiffText', 'DiffText', 'DiffText', 'DiffAdd',
+        \ 'DiffAdd', 'DiffAdd', 'DiffAdd', ''], hl)
+
+  " removing the only remaining diff chunk should make the files equal
+  call win_gotoid(w2_id)
+  call setline(1, ['a', '2', '3', 'x', 'd', 'e', 'f', 'x', '7', '8', 'i'])
+  8d
+  let hl = range(1, 10)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')})
+  call assert_equal(['', '', '', 'DiffAdd', '', '', '', '', '', ''], hl)
+  call win_gotoid(w2_id)
+  4d
+  call win_gotoid(w1_id)
+  let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')})
+  call assert_equal(['', '', '', '', '', '', '', '', ''], hl)
+
+  %bw!
+endfunc
 
 func Test_diff_binary()
   CheckScreendump
diff --git a/src/nvim/testdir/test_prompt_buffer.vim b/src/nvim/testdir/test_prompt_buffer.vim
index 9b8a776c95..b8f6c5240c 100644
--- a/src/nvim/testdir/test_prompt_buffer.vim
+++ b/src/nvim/testdir/test_prompt_buffer.vim
@@ -180,6 +180,8 @@ func Test_prompt_buffer_edit()
   call assert_beeps('normal! S')
   call assert_beeps("normal! \")
   call assert_beeps("normal! \")
+  call assert_beeps("normal! dp")
+  call assert_beeps("normal! do")
   " pressing CTRL-W in the prompt buffer should trigger the window commands
   call assert_equal(1, winnr())
   exe "normal A\\"
diff --git a/src/nvim/testdir/test_recover.vim b/src/nvim/testdir/test_recover.vim
index 89f894a4bb..90ee10704c 100644
--- a/src/nvim/testdir/test_recover.vim
+++ b/src/nvim/testdir/test_recover.vim
@@ -395,4 +395,48 @@ func Test_recover_symbolic_link()
   call delete('.Xfile1.swp')
 endfunc
 
+" Test for recovering a file when an autocmd moves the cursor to an invalid
+" line. This used to result in an internal error (E315) which is fixed
+" by 8.2.2966.
+func Test_recover_invalid_cursor_pos()
+  call writefile([], 'Xfile1')
+  edit Xfile1
+  preserve
+  let b = readblob('.Xfile1.swp')
+  bw!
+  augroup Test
+    au!
+    au BufReadPost Xfile1 normal! 3G
+  augroup END
+  call writefile(range(1, 3), 'Xfile1')
+  call writefile(b, '.Xfile1.swp')
+  try
+    recover Xfile1
+  catch /E308:/
+    " this test is for the :E315 internal error.
+    " ignore the 'E308: Original file may have been changed' error
+  endtry
+  redraw!
+  augroup Test
+    au!
+  augroup END
+  augroup! Test
+  call delete('Xfile1')
+  call delete('.Xfile1.swp')
+endfunc
+
+" Test for recovering a buffer without a name
+func Test_noname_buffer()
+  new
+  call setline(1, ['one', 'two'])
+  preserve
+  let sn = swapname('')
+  let b = readblob(sn)
+  bw!
+  call writefile(b, sn)
+  exe "recover " .. sn
+  call assert_equal(['one', 'two'], getline(1, '$'))
+  call delete(sn)
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From fa17dc1e1b893db3320b9b7ae50698c05280df92 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 23:00:25 +0800
Subject: vim-patch:8.2.2981: recovery test is not run on big-endian systems

Problem:    Recovery test is not run on big-endian systems.
Solution:   Make it work on big-endian systems. (James McCoy, closes vim/vim#8368)

https://github.com/vim/vim/commit/6654ca702ca64c99965efcad3243ea5f95473252

Co-authored-by: James McCoy 
---
 src/nvim/testdir/test_recover.vim | 193 +++++++++++++++++++-------------------
 1 file changed, 96 insertions(+), 97 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_recover.vim b/src/nvim/testdir/test_recover.vim
index 90ee10704c..10a9e7b4f3 100644
--- a/src/nvim/testdir/test_recover.vim
+++ b/src/nvim/testdir/test_recover.vim
@@ -203,103 +203,102 @@ func Test_recover_corrupted_swap_file()
   let b = readblob(sn)
   let save_b = copy(b)
   bw!
-  " Run these tests only on little-endian systems. These tests fail on a
-  " big-endian system (IBM S390x system).
-  if b[1008:1011] == 0z33323130
-        \ && b[4096:4097] == 0z7470
-        \ && b[8192:8193] == 0z6164
-
-    " clear the B0_MAGIC_LONG field
-    let b[1008:1011] = 0z00000000
-    call writefile(b, sn)
-    let msg = execute('recover Xfile1')
-    call assert_match('the file has been damaged', msg)
-    call assert_equal('Xfile1', @%)
-    call assert_equal([''], getline(1, '$'))
-    bw!
-
-    " reduce the page size
-    let b = copy(save_b)
-    let b[12:15] = 0z00010000
-    call writefile(b, sn)
-    let msg = execute('recover Xfile1')
-    call assert_match('page size is smaller than minimum value', msg)
-    call assert_equal('Xfile1', @%)
-    call assert_equal([''], getline(1, '$'))
-    bw!
-
-    " clear the pointer ID
-    let b = copy(save_b)
-    let b[4096:4097] = 0z0000
-    call writefile(b, sn)
-    call assert_fails('recover Xfile1', 'E310:')
-    call assert_equal('Xfile1', @%)
-    call assert_equal([''], getline(1, '$'))
-    bw!
-
-    " set the number of pointers in a pointer block to zero
-    let b = copy(save_b)
-    let b[4098:4099] = 0z0000
-    call writefile(b, sn)
-    call assert_fails('recover Xfile1', 'E312:')
-    call assert_equal('Xfile1', @%)
-    call assert_equal(['???EMPTY BLOCK'], getline(1, '$'))
-    bw!
-
-    " set the block number in a pointer entry to a negative number
-    let b = copy(save_b)
-    let b[4104:4111] = 0z00000000.00000080
-    call writefile(b, sn)
-    call assert_fails('recover Xfile1', 'E312:')
-    call assert_equal('Xfile1', @%)
-    call assert_equal(['???LINES MISSING'], getline(1, '$'))
-    bw!
-
-    " clear the data block ID
-    let b = copy(save_b)
-    let b[8192:8193] = 0z0000
-    call writefile(b, sn)
-    call assert_fails('recover Xfile1', 'E312:')
-    call assert_equal('Xfile1', @%)
-    call assert_equal(['???BLOCK MISSING'], getline(1, '$'))
-    bw!
-
-    " set the number of lines in the data block to zero
-    let b = copy(save_b)
-    let b[8208:8211] = 0z00000000
-    call writefile(b, sn)
-    call assert_fails('recover Xfile1', 'E312:')
-    call assert_equal('Xfile1', @%)
-    call assert_equal(['??? from here until ???END lines may have been inserted/deleted',
-          \ '???END'], getline(1, '$'))
-    bw!
-
-    " use an invalid text start for the lines in a data block
-    let b = copy(save_b)
-    let b[8216:8219] = 0z00000000
-    call writefile(b, sn)
-    call assert_fails('recover Xfile1', 'E312:')
-    call assert_equal('Xfile1', @%)
-    call assert_equal(['???'], getline(1, '$'))
-    bw!
-
-    " use an incorrect text end (db_txt_end) for the data block
-    let b = copy(save_b)
-    let b[8204:8207] = 0z80000000
-    call writefile(b, sn)
-    call assert_fails('recover Xfile1', 'E312:')
-    call assert_equal('Xfile1', @%)
-    call assert_equal(['??? from here until ???END lines may be messed up', '',
-          \ '???END'], getline(1, '$'))
-    bw!
-
-    " remove the data block
-    let b = copy(save_b)
-    call writefile(b[:8191], sn)
-    call assert_fails('recover Xfile1', 'E312:')
-    call assert_equal('Xfile1', @%)
-    call assert_equal(['???MANY LINES MISSING'], getline(1, '$'))
-  endif
+
+  " Not all fields are written in a system-independent manner.  Detect whether
+  " the test is running on a little or big-endian system, so the correct
+  " corruption values can be set.
+  let little_endian = b[1008:1015] == 0z33323130.00000000
+
+  " clear the B0_MAGIC_LONG field
+  let b[1008:1015] = 0z0000000000000000
+  call writefile(b, sn)
+  let msg = execute('recover Xfile1')
+  call assert_match('the file has been damaged', msg)
+  call assert_equal('Xfile1', @%)
+  call assert_equal([''], getline(1, '$'))
+  bw!
+
+  " reduce the page size
+  let b = copy(save_b)
+  let b[12:15] = 0z00010000
+  call writefile(b, sn)
+  let msg = execute('recover Xfile1')
+  call assert_match('page size is smaller than minimum value', msg)
+  call assert_equal('Xfile1', @%)
+  call assert_equal([''], getline(1, '$'))
+  bw!
+
+  " clear the pointer ID
+  let b = copy(save_b)
+  let b[4096:4097] = 0z0000
+  call writefile(b, sn)
+  call assert_fails('recover Xfile1', 'E310:')
+  call assert_equal('Xfile1', @%)
+  call assert_equal([''], getline(1, '$'))
+  bw!
+
+  " set the number of pointers in a pointer block to zero
+  let b = copy(save_b)
+  let b[4098:4099] = 0z0000
+  call writefile(b, sn)
+  call assert_fails('recover Xfile1', 'E312:')
+  call assert_equal('Xfile1', @%)
+  call assert_equal(['???EMPTY BLOCK'], getline(1, '$'))
+  bw!
+
+  " set the block number in a pointer entry to a negative number
+  let b = copy(save_b)
+  let b[4104:4111] = little_endian ? 0z00000000.00000080 : 0z80000000.00000000
+  call writefile(b, sn)
+  call assert_fails('recover Xfile1', 'E312:')
+  call assert_equal('Xfile1', @%)
+  call assert_equal(['???LINES MISSING'], getline(1, '$'))
+  bw!
+
+  " clear the data block ID
+  let b = copy(save_b)
+  let b[8192:8193] = 0z0000
+  call writefile(b, sn)
+  call assert_fails('recover Xfile1', 'E312:')
+  call assert_equal('Xfile1', @%)
+  call assert_equal(['???BLOCK MISSING'], getline(1, '$'))
+  bw!
+
+  " set the number of lines in the data block to zero
+  let b = copy(save_b)
+  let b[8208:8215] = 0z00000000.00000000
+  call writefile(b, sn)
+  call assert_fails('recover Xfile1', 'E312:')
+  call assert_equal('Xfile1', @%)
+  call assert_equal(['??? from here until ???END lines may have been inserted/deleted',
+        \ '???END'], getline(1, '$'))
+  bw!
+
+  " use an invalid text start for the lines in a data block
+  let b = copy(save_b)
+  let b[8216:8219] = 0z00000000
+  call writefile(b, sn)
+  call assert_fails('recover Xfile1', 'E312:')
+  call assert_equal('Xfile1', @%)
+  call assert_equal(['???'], getline(1, '$'))
+  bw!
+
+  " use an incorrect text end (db_txt_end) for the data block
+  let b = copy(save_b)
+  let b[8204:8207] = little_endian ? 0z80000000 : 0z00000080
+  call writefile(b, sn)
+  call assert_fails('recover Xfile1', 'E312:')
+  call assert_equal('Xfile1', @%)
+  call assert_equal(['??? from here until ???END lines may be messed up', '',
+        \ '???END'], getline(1, '$'))
+  bw!
+
+  " remove the data block
+  let b = copy(save_b)
+  call writefile(b[:8191], sn)
+  call assert_fails('recover Xfile1', 'E312:')
+  call assert_equal('Xfile1', @%)
+  call assert_equal(['???MANY LINES MISSING'], getline(1, '$'))
 
   bw!
   call delete(sn)
-- 
cgit 


From c1c2c7b3163490b5e4374ee370616ea2581d4fe1 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 23:00:50 +0800
Subject: vim-patch:8.2.3080: recover test fails on 32bit systems
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Problem:    Recover test fails on 32bit systems. (Ondřej Súkup)
Solution:   Detect 32/64 bit systems. (Yegappan Lakshmanan, closes vim/vim#8485,
            closes vim/vim#8479)

https://github.com/vim/vim/commit/576cb75ceb38ed077938d4a1c1265095050f6105

Co-authored-by: Yegappan Lakshmanan 
---
 src/nvim/testdir/test_recover.vim | 28 +++++++++++++++++++++++-----
 1 file changed, 23 insertions(+), 5 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_recover.vim b/src/nvim/testdir/test_recover.vim
index 10a9e7b4f3..137c73a194 100644
--- a/src/nvim/testdir/test_recover.vim
+++ b/src/nvim/testdir/test_recover.vim
@@ -207,10 +207,16 @@ func Test_recover_corrupted_swap_file()
   " Not all fields are written in a system-independent manner.  Detect whether
   " the test is running on a little or big-endian system, so the correct
   " corruption values can be set.
-  let little_endian = b[1008:1015] == 0z33323130.00000000
+  let little_endian = b[1008:1011] == 0z33323130
+  " The swap file header fields can be either 32-bit or 64-bit.
+  let system_64bit = b[1012:1015] == 0z00000000
 
   " clear the B0_MAGIC_LONG field
-  let b[1008:1015] = 0z0000000000000000
+  if system_64bit
+    let b[1008:1015] = 0z00000000.00000000
+  else
+    let b[1008:1011] = 0z00000000
+  endif
   call writefile(b, sn)
   let msg = execute('recover Xfile1')
   call assert_match('the file has been damaged', msg)
@@ -248,7 +254,11 @@ func Test_recover_corrupted_swap_file()
 
   " set the block number in a pointer entry to a negative number
   let b = copy(save_b)
-  let b[4104:4111] = little_endian ? 0z00000000.00000080 : 0z80000000.00000000
+  if system_64bit
+    let b[4104:4111] = little_endian ? 0z00000000.00000080 : 0z80000000.00000000
+  else
+    let b[4104:4107] = little_endian ? 0z00000080 : 0z80000000
+  endif
   call writefile(b, sn)
   call assert_fails('recover Xfile1', 'E312:')
   call assert_equal('Xfile1', @%)
@@ -266,7 +276,11 @@ func Test_recover_corrupted_swap_file()
 
   " set the number of lines in the data block to zero
   let b = copy(save_b)
-  let b[8208:8215] = 0z00000000.00000000
+  if system_64bit
+    let b[8208:8215] = 0z00000000.00000000
+  else
+    let b[8208:8211] = 0z00000000
+  endif
   call writefile(b, sn)
   call assert_fails('recover Xfile1', 'E312:')
   call assert_equal('Xfile1', @%)
@@ -276,7 +290,11 @@ func Test_recover_corrupted_swap_file()
 
   " use an invalid text start for the lines in a data block
   let b = copy(save_b)
-  let b[8216:8219] = 0z00000000
+  if system_64bit
+    let b[8216:8219] = 0z00000000
+  else
+    let b[8212:8215] = 0z00000000
+  endif
   call writefile(b, sn)
   call assert_fails('recover Xfile1', 'E312:')
   call assert_equal('Xfile1', @%)
-- 
cgit 


From acfc3d54c6fc5b22adab057962c493b059a91fe7 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 23:02:10 +0800
Subject: vim-patch:8.2.3103: swap test may fail on some systems

Problem:    Swap test may fail on some systems when jobs take longer to exit.
Solution:   Use different file names.

https://github.com/vim/vim/commit/f33cae605064c8bdb908a8069d936f752572cd76

Co-authored-by: Bram Moolenaar 
---
 src/nvim/testdir/test_swap.vim | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim
index 024a3b02bc..cf46b4c5bd 100644
--- a/src/nvim/testdir/test_swap.vim
+++ b/src/nvim/testdir/test_swap.vim
@@ -504,18 +504,18 @@ endfunc
 " Test for renaming a buffer when the swap file is deleted out-of-band
 func Test_missing_swap_file()
   CheckUnix
-  new Xfile1
+  new Xfile2
   call delete(swapname(''))
-  call assert_fails('file Xfile2', 'E301:')
-  call assert_equal('Xfile2', bufname())
-  call assert_true(bufexists('Xfile1'))
+  call assert_fails('file Xfile3', 'E301:')
+  call assert_equal('Xfile3', bufname())
   call assert_true(bufexists('Xfile2'))
+  call assert_true(bufexists('Xfile3'))
   %bw!
 endfunc
 
 " Test for :preserve command
 func Test_preserve()
-  new Xfile1
+  new Xfile4
   setlocal noswapfile
   call assert_fails('preserve', 'E313:')
   bw!
@@ -523,8 +523,8 @@ endfunc
 
 " Test for the v:swapchoice variable
 func Test_swapchoice()
-  call writefile(['aaa', 'bbb'], 'Xfile1')
-  edit Xfile1
+  call writefile(['aaa', 'bbb'], 'Xfile5')
+  edit Xfile5
   preserve
   let swapfname = swapname('')
   let b = readblob(swapfname)
@@ -538,7 +538,7 @@ func Test_swapchoice()
     autocmd!
     autocmd SwapExists * let v:swapchoice = 'o'
   augroup END
-  edit Xfile1
+  edit Xfile5
   call assert_true(&readonly)
   call assert_equal(['aaa', 'bbb'], getline(1, '$'))
   %bw!
@@ -550,11 +550,11 @@ func Test_swapchoice()
     autocmd SwapExists * let v:swapchoice = 'a'
   augroup END
   try
-    edit Xfile1
+    edit Xfile5
   catch /^Vim:Interrupt$/
   endtry
   call assert_equal('', @%)
-  call assert_true(bufexists('Xfile1'))
+  call assert_true(bufexists('Xfile5'))
   %bw!
   call assert_true(filereadable(swapfname))
 
@@ -563,12 +563,12 @@ func Test_swapchoice()
     autocmd!
     autocmd SwapExists * let v:swapchoice = 'd'
   augroup END
-  edit Xfile1
-  call assert_equal('Xfile1', @%)
+  edit Xfile5
+  call assert_equal('Xfile5', @%)
   %bw!
   call assert_false(filereadable(swapfname))
 
-  call delete('Xfile1')
+  call delete('Xfile5')
   call delete(swapfname)
   augroup test_swapchoice
     autocmd!
-- 
cgit 


From d8fc390bd04a69f43983fc355026a2f216410318 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 23:02:47 +0800
Subject: vim-patch:8.2.3440: recover test fails if there is an old swap file

Problem:    Recover test fails if there is an old swap file.
Solution:   Delete old swap files.

https://github.com/vim/vim/commit/f2a8bafa4b815e5b4e50a25c2b3a8a24fbe8aa11

Co-authored-by: Bram Moolenaar 
---
 src/nvim/testdir/test_recover.vim | 6 ++++++
 1 file changed, 6 insertions(+)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_recover.vim b/src/nvim/testdir/test_recover.vim
index 137c73a194..9d38b0ebea 100644
--- a/src/nvim/testdir/test_recover.vim
+++ b/src/nvim/testdir/test_recover.vim
@@ -175,6 +175,12 @@ func Test_recover_empty_swap_file()
   call assert_match('Unable to read block 0 from .Xfile1.swp', msg)
   call assert_equal('Xfile1', @%)
   bw!
+
+  " make sure there are no old swap files laying around
+  for f in glob('.sw?', 0, 1)
+    call delete(f)
+  endfor
+
   " :recover from an empty buffer
   call assert_fails('recover', 'E305:')
   call delete('.Xfile1.swp')
-- 
cgit 


From c269b8dcae08221f1752faf1ecc7dae4d7eed40c Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 23:03:10 +0800
Subject: vim-patch:8.2.3498: recover test may fail on some systems

Problem:    Recover test may fail on some systems.
Solution:   Adjust the little endian and 64 bit detection. (James McCoy,
            closes vim/vim#8941)

https://github.com/vim/vim/commit/37f341d7236ff8a1e886bbb0f0ba0700ad589373

Co-authored-by: James McCoy 
---
 src/nvim/testdir/test_recover.vim | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_recover.vim b/src/nvim/testdir/test_recover.vim
index 9d38b0ebea..f4710c4357 100644
--- a/src/nvim/testdir/test_recover.vim
+++ b/src/nvim/testdir/test_recover.vim
@@ -213,9 +213,11 @@ func Test_recover_corrupted_swap_file()
   " Not all fields are written in a system-independent manner.  Detect whether
   " the test is running on a little or big-endian system, so the correct
   " corruption values can be set.
-  let little_endian = b[1008:1011] == 0z33323130
-  " The swap file header fields can be either 32-bit or 64-bit.
-  let system_64bit = b[1012:1015] == 0z00000000
+  " The B0_MAGIC_LONG field may be 32-bit or 64-bit, depending on the system,
+  " even though the value stored is only 32-bits.  Therefore, need to check
+  " both the high and low 32-bits to compute these values.
+  let little_endian = (b[1008:1011] == 0z33323130) || (b[1012:1015] == 0z33323130)
+  let system_64bit = little_endian ? (b[1012:1015] == 0z00000000) : (b[1008:1011] == 0z00000000)
 
   " clear the B0_MAGIC_LONG field
   if system_64bit
-- 
cgit 


From 51a48d482ea533415b4caa88e03adcd82f654f3a Mon Sep 17 00:00:00 2001
From: dundargoc 
Date: Thu, 17 Nov 2022 15:43:29 +0100
Subject: refactor: remove __STDC_ISO_10646__ check

We can always assume wchar_t values are unicode codepoints for the
systems we support, so this check isn't necessary.
---
 src/nvim/mbyte.c | 10 ----------
 1 file changed, 10 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 42b3ec0202..1a30852e72 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -71,10 +71,6 @@
 # include 
 #endif
 
-#ifdef __STDC_ISO_10646__
-# include 
-#endif
-
 typedef struct {
   int rangeStart;
   int rangeEnd;
@@ -1235,12 +1231,9 @@ int mb_toupper(int a)
     return TOUPPER_ASC(a);
   }
 
-#if defined(__STDC_ISO_10646__)
-  // If towupper() is available and handles Unicode, use it.
   if (!(cmp_flags & CMP_INTERNAL)) {
     return (int)towupper((wint_t)a);
   }
-#endif
 
   // For characters below 128 use locale sensitive toupper().
   if (a < 128) {
@@ -1266,12 +1259,9 @@ int mb_tolower(int a)
     return TOLOWER_ASC(a);
   }
 
-#if defined(__STDC_ISO_10646__)
-  // If towlower() is available and handles Unicode, use it.
   if (!(cmp_flags & CMP_INTERNAL)) {
     return (int)towlower((wint_t)a);
   }
-#endif
 
   // For characters below 128 use locale sensitive tolower().
   if (a < 128) {
-- 
cgit 


From 294910a1ffd11bea0081c2b92632628ef0462eb1 Mon Sep 17 00:00:00 2001
From: Gregory Anders 
Date: Sat, 5 Nov 2022 19:30:48 -0600
Subject: feat(exrc): use vim.secure.read() for 'exrc' option

---
 src/nvim/lua/executor.c           | 24 ++++++++++++++++++++++++
 src/nvim/main.c                   | 12 ++++++++++--
 src/nvim/testdir/test_startup.vim |  1 +
 3 files changed, 35 insertions(+), 2 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 79cc3ed112..43a3b12a98 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -2193,3 +2193,27 @@ plain:
   kv_printf(str, "", ref);
   return str.items;
 }
+
+char *nlua_read_secure(const char *path)
+{
+  lua_State *const lstate = global_lstate;
+  lua_getglobal(lstate, "vim");
+  lua_getfield(lstate, -1, "secure");
+  lua_getfield(lstate, -1, "read");
+  lua_pushstring(lstate, path);
+  lua_call(lstate, 1, 1);
+
+  size_t len = 0;
+  const char *contents = lua_tolstring(lstate, -1, &len);
+  char *buf = NULL;
+  if (contents != NULL) {
+    // Add one to include trailing null byte
+    buf = xcalloc(len + 1, sizeof(char));
+    memcpy(buf, contents, len + 1);
+  }
+
+  // Pop return value, "vim", and "secure"
+  lua_pop(lstate, 3);
+
+  return buf;
+}
diff --git a/src/nvim/main.c b/src/nvim/main.c
index d8570f49eb..e8c1c98c38 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -2002,7 +2002,11 @@ static void source_startup_scripts(const mparm_T *const parmp)
 #endif
       secure = p_secure;
 
-      if (do_source(VIMRC_FILE, true, DOSO_VIMRC) == FAIL) {
+      char *str = nlua_read_secure(VIMRC_FILE);
+      if (str != NULL) {
+        do_source_str(str, VIMRC_FILE);
+        xfree(str);
+      } else {
 #if defined(UNIX)
         // if ".exrc" is not owned by user set 'secure' mode
         if (!os_file_owned(EXRC_FILE)) {
@@ -2011,7 +2015,11 @@ static void source_startup_scripts(const mparm_T *const parmp)
           secure = 0;
         }
 #endif
-        (void)do_source(EXRC_FILE, false, DOSO_NONE);
+        str = nlua_read_secure(EXRC_FILE);
+        if (str != NULL) {
+          do_source_str(str, EXRC_FILE);
+          xfree(str);
+        }
       }
     }
     if (secure == 2) {
diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim
index f9f7c5b492..42467c5508 100644
--- a/src/nvim/testdir/test_startup.vim
+++ b/src/nvim/testdir/test_startup.vim
@@ -1024,6 +1024,7 @@ endfunc
 
 " Test for using the 'exrc' option
 func Test_exrc()
+  throw 'Skipped: Nvim requires user input for the exrc option'
   let after =<< trim [CODE]
     call assert_equal(1, &exrc)
     call assert_equal(1, &secure)
-- 
cgit 


From 6d9c3d903ecee2d1d21d0a0806f1a2bebe628e8e Mon Sep 17 00:00:00 2001
From: Gregory Anders 
Date: Sun, 6 Nov 2022 19:44:30 -0700
Subject: refactor: deprecate 'secure' option

Now that 'exrc' files must be explicitly marked trusted there is no need
to constrain what can be done in them.
---
 src/nvim/ex_cmds.c   |  9 +++------
 src/nvim/ex_docmd.c  |  3 +--
 src/nvim/fileio.c    |  3 +--
 src/nvim/globals.h   |  5 ++---
 src/nvim/main.c      | 23 +----------------------
 src/nvim/mapping.c   |  3 +--
 src/nvim/options.lua |  2 +-
 src/nvim/runtime.c   |  2 +-
 8 files changed, 11 insertions(+), 39 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index c6dd30e549..1efde7ef3f 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -1134,8 +1134,7 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out
   int scroll_save = msg_scroll;
 
   //
-  // Disallow shell commands from .exrc and .vimrc in current directory for
-  // security reasons.
+  // Disallow shell commands in secure mode
   //
   if (check_secure()) {
     return;
@@ -1477,8 +1476,7 @@ filterend:
 /// @param flags  may be SHELL_DOOUT when output is redirected
 void do_shell(char *cmd, int flags)
 {
-  // Disallow shell commands from .exrc and .vimrc in current directory for
-  // security reasons.
+  // Disallow shell commands in secure mode
   if (check_secure()) {
     msg_end();
     return;
@@ -3215,8 +3213,7 @@ void ex_z(exarg_T *eap)
   ex_no_reprint = true;
 }
 
-/// @return  true if the secure flag is set (.exrc or .vimrc in current directory)
-///          and also give an error message.
+/// @return  true if the secure flag is set and also give an error message.
 ///          Otherwise, return false.
 bool check_secure(void)
 {
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index e0e4fa332f..0733bcf683 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -4223,8 +4223,7 @@ theend:
 
 static void ex_autocmd(exarg_T *eap)
 {
-  // Disallow autocommands from .exrc and .vimrc in current
-  // directory for security reasons.
+  // Disallow autocommands in secure mode.
   if (secure) {
     secure = 2;
     eap->errmsg = _(e_curdir);
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 6c5469d020..d6bc861c09 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -2211,8 +2211,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
     return FAIL;
   }
 
-  // Disallow writing from .exrc and .vimrc in current directory for
-  // security reasons.
+  // Disallow writing in secure mode.
   if (check_secure()) {
     return FAIL;
   }
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 76f62fe267..130f3f6c48 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -489,8 +489,7 @@ EXTERN int stdin_fd INIT(= -1);
 // true when doing full-screen output, otherwise only writing some messages.
 EXTERN int full_screen INIT(= false);
 
-/// Non-zero when only "safe" commands are allowed, e.g. when sourcing .exrc or
-/// .vimrc in current directory.
+/// Non-zero when only "safe" commands are allowed
 EXTERN int secure INIT(= 0);
 
 /// Non-zero when changing text and jumping to another window or editing another buffer is not
@@ -864,7 +863,7 @@ EXTERN char e_api_spawn_failed[] INIT(= N_("E903: Could not spawn API job"));
 EXTERN char e_argreq[] INIT(= N_("E471: Argument required"));
 EXTERN char e_backslash[] INIT(= N_("E10: \\ should be followed by /, ? or &"));
 EXTERN char e_cmdwin[] INIT(= N_("E11: Invalid in command-line window;  executes, CTRL-C quits"));
-EXTERN char e_curdir[] INIT(= N_("E12: Command not allowed from exrc/vimrc in current dir or tag search"));
+EXTERN char e_curdir[] INIT(= N_("E12: Command not allowed in secure mode in current dir or tag search"));
 EXTERN char e_command_too_recursive[] INIT(= N_("E169: Command too recursive"));
 EXTERN char e_endif[] INIT(= N_("E171: Missing :endif"));
 EXTERN char e_endtry[] INIT(= N_("E600: Missing :endtry"));
diff --git a/src/nvim/main.c b/src/nvim/main.c
index e8c1c98c38..a369ca0256 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -1989,32 +1989,15 @@ static void source_startup_scripts(const mparm_T *const parmp)
     do_system_initialization();
 
     if (do_user_initialization()) {
-      // Read initialization commands from ".vimrc" or ".exrc" in current
+      // Read initialization commands from ".nvimrc" or ".exrc" in current
       // directory.  This is only done if the 'exrc' option is set.
-      // Because of security reasons we disallow shell and write commands
-      // now, except for unix if the file is owned by the user or 'secure'
-      // option has been reset in environment of global "exrc" or "vimrc".
       // Only do this if VIMRC_FILE is not the same as vimrc file sourced in
       // do_user_initialization.
-#if defined(UNIX)
-      // If vimrc file is not owned by user, set 'secure' mode.
-      if (!os_file_owned(VIMRC_FILE))  // NOLINT(readability/braces)
-#endif
-      secure = p_secure;
-
       char *str = nlua_read_secure(VIMRC_FILE);
       if (str != NULL) {
         do_source_str(str, VIMRC_FILE);
         xfree(str);
       } else {
-#if defined(UNIX)
-        // if ".exrc" is not owned by user set 'secure' mode
-        if (!os_file_owned(EXRC_FILE)) {
-          secure = p_secure;
-        } else {
-          secure = 0;
-        }
-#endif
         str = nlua_read_secure(EXRC_FILE);
         if (str != NULL) {
           do_source_str(str, EXRC_FILE);
@@ -2022,10 +2005,6 @@ static void source_startup_scripts(const mparm_T *const parmp)
         }
       }
     }
-    if (secure == 2) {
-      need_wait_return = true;
-    }
-    secure = 0;
   }
   TIME_MSG("sourcing vimrc file(s)");
 }
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index 9b10ea901e..76a646083e 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -2446,8 +2446,7 @@ void ex_abbreviate(exarg_T *eap)
 /// ":map" and friends.
 void ex_map(exarg_T *eap)
 {
-  // If we are sourcing .exrc or .vimrc in current directory we
-  // print the mappings for security reasons.
+  // If we are in a secure mode we print the mappings for security reasons.
   if (secure) {
     secure = 2;
     msg_outtrans(eap->cmd);
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index dc0561d560..1cf8ab3253 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -2007,7 +2007,7 @@ return {
     },
     {
       full_name='secure',
-      short_desc=N_("mode for reading .vimrc in current dir"),
+      short_desc=N_("No description"),
       type='bool', scope={'global'},
       secure=true,
       varname='p_secure',
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index e1a2483438..db8dc04907 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -1930,7 +1930,7 @@ int do_source(char *fname, int check_other, int is_vimrc)
 
   cookie.fp = fopen_noinh_readbin(fname_exp);
   if (cookie.fp == NULL && check_other) {
-    // Try again, replacing file name ".vimrc" by "_vimrc" or vice versa,
+    // Try again, replacing file name ".nvimrc" by "_nvimrc" or vice versa,
     // and ".exrc" by "_exrc" or vice versa.
     p = path_tail(fname_exp);
     if ((*p == '.' || *p == '_')
-- 
cgit 


From 7139035bfd90698ce7214b507b2f8a7766398a68 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 23:30:30 +0800
Subject: vim-patch:9.0.0895: file renamed twice in test, missing feature check

Problem:    File renamed twice in test; missing feature check.
Solution:   Remove a rename() call.  Add check for cryptv feature.
            (closes vim/vim#11564)

https://github.com/vim/vim/commit/780154bf7a07813e474105837c2b5998009d9c71

Co-authored-by: zeertzjq 
---
 src/nvim/testdir/test_recover.vim | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_recover.vim b/src/nvim/testdir/test_recover.vim
index f4710c4357..92e22687af 100644
--- a/src/nvim/testdir/test_recover.vim
+++ b/src/nvim/testdir/test_recover.vim
@@ -126,7 +126,6 @@ func Test_nocatch_process_still_running()
   call test_override("uptime", 0)
   sleep 1
 
-  call rename('Xswap', swname)
   call feedkeys('e', 'tL')
   redir => editOutput
   edit Xswaptest
@@ -332,6 +331,7 @@ endfunc
 
 " Test for :recover using an encrypted swap file
 func Test_recover_encrypted_swap_file()
+  CheckFeature cryptv
   CheckUnix
 
   " Recover an encrypted file from the swap file without the original file
-- 
cgit 


From 4b7aafc2f42e3eee44ae430fcb5e643fb23d7b4b Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 17 Nov 2022 23:27:39 +0800
Subject: fix(memline): use long instead of linenr_T for db_line_count

---
 src/nvim/memline.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 955074e1f2..5f21a3dd0e 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -131,7 +131,8 @@ struct data_block {
   unsigned db_free;             // free space available
   unsigned db_txt_start;        // byte where text starts
   unsigned db_txt_end;          // byte just after data block
-  linenr_T db_line_count;       // number of lines in this block
+  // linenr_T db_line_count;
+  long db_line_count;           // number of lines in this block
   unsigned db_index[1];         // index for start of line (actually bigger)
                                 // followed by empty space up to db_txt_start
                                 // followed by the text in the lines until
-- 
cgit 


From 510429fc5cc010afa54036d16e049b177cce2e3e Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 18 Nov 2022 06:47:51 +0800
Subject: vim-patch:8.2.1497: CursorHold test is flaky (#21095)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Problem:    CursorHold test is flaky. (Jakub Kądziołka)
Solution:   Use WaitForAssert() (closes vim/vim#6754)

https://github.com/vim/vim/commit/17f67547f36a06220ea4667aaee7bb130108f568

Co-authored-by: Bram Moolenaar 
---
 src/nvim/testdir/test_autocmd.vim | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index fb8adbb3a6..70da0a9ba2 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -31,20 +31,19 @@ func Test_CursorHold_autocmd()
   END
   call writefile(before, 'Xinit')
   let buf = RunVimInTerminal('-S Xinit Xfile', {})
-  call term_wait(buf)
+  call term_sendkeys(buf, "G")
+  call term_wait(buf, 20)
   call term_sendkeys(buf, "gg")
   call term_wait(buf)
-  sleep 50m
+  call WaitForAssert({-> assert_equal(['1'], readfile('Xoutput')[-1:-1])})
   call term_sendkeys(buf, "j")
   call term_wait(buf)
-  sleep 50m
+  call WaitForAssert({-> assert_equal(['1', '2'], readfile('Xoutput')[-2:-1])})
   call term_sendkeys(buf, "j")
   call term_wait(buf)
-  sleep 50m
+  call WaitForAssert({-> assert_equal(['1', '2', '3'], readfile('Xoutput')[-3:-1])})
   call StopVimInTerminal(buf)
 
-  call assert_equal(['1', '2', '3'], readfile('Xoutput')[-3:-1])
-
   call delete('Xinit')
   call delete('Xoutput')
   call delete('Xfile')
-- 
cgit 


From 523b1943c359cf79f29229a3d882c55ea407a237 Mon Sep 17 00:00:00 2001
From: Matthew Gramigna 
Date: Thu, 17 Nov 2022 18:39:31 -0500
Subject: vim-patch:9.0.0897: Clinical Quality Language files are not
 recognized (#21094)

Problem:    Clinical Quality Language files are not recognized.
Solution:   Add the "*.cql" pattern. (Matthew Gramigna, closes vim/vim#11452)

https://github.com/vim/vim/commit/12babe45a389cd1ea8befd5b06239e877b4abbba

Co-authored-by: mgramigna 
---
 src/nvim/testdir/test_filetype.vim | 1 +
 1 file changed, 1 insertion(+)

(limited to 'src/nvim')

diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 69508cb19e..c8d8239757 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -126,6 +126,7 @@ let s:filename_checks = {
     \ 'context': ['tex/context/any/file.tex', 'file.mkii', 'file.mkiv', 'file.mkvi', 'file.mkxl', 'file.mklx'],
     \ 'cook': ['file.cook'],
     \ 'cpp': ['file.cxx', 'file.c++', 'file.hh', 'file.hxx', 'file.hpp', 'file.ipp', 'file.moc', 'file.tcc', 'file.inl', 'file.tlh'],
+    \ 'cqlang': ['file.cql'],
     \ 'crm': ['file.crm'],
     \ 'crontab': ['crontab', 'crontab.file', '/etc/cron.d/file', 'any/etc/cron.d/file'],
     \ 'cs': ['file.cs', 'file.csx'],
-- 
cgit 


From 790d12b95fb755008e00b39c132271eaffbd7f12 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 18 Nov 2022 08:14:17 +0800
Subject: fix(ex_cmds): fix a mistake in the porting of Vim patch 8.1.0306
 (#21096)

---
 src/nvim/ex_cmds.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

(limited to 'src/nvim')

diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 1efde7ef3f..c405b4f4c1 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -1018,7 +1018,9 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
     ml_delete(line1 + extra, true);
   }
   if (!global_busy && num_lines > p_report) {
-    smsg(NGETTEXT("1 line moved", "%" PRId64 " lines moved", num_lines), (int64_t)num_lines);
+    smsg(NGETTEXT("%" PRId64 " line moved",
+                  "%" PRId64 " lines moved", num_lines),
+         (int64_t)num_lines);
   }
 
   extmark_move_region(curbuf, line1 - 1, 0, start_byte,
-- 
cgit 


From ba04fffe9869159efe6660d782f436fa8348aae2 Mon Sep 17 00:00:00 2001
From: Sizhe Zhao 
Date: Fri, 18 Nov 2022 08:43:56 +0800
Subject: feat(l10n): update zh_CN translations (#21085)

---
 src/nvim/po/zh_CN.UTF-8.po | 12273 ++++++++++++++++++++++++++++---------------
 1 file changed, 7925 insertions(+), 4348 deletions(-)

(limited to 'src/nvim')

diff --git a/src/nvim/po/zh_CN.UTF-8.po b/src/nvim/po/zh_CN.UTF-8.po
index afa2f29029..87b67c5d97 100644
--- a/src/nvim/po/zh_CN.UTF-8.po
+++ b/src/nvim/po/zh_CN.UTF-8.po
@@ -8,6 +8,9 @@
 # TRANSLATORS
 #   Edyfox 
 #   Yuheng Xie 
+#   lilydjwg 
+#   Ada (Haowen) Yu 
+#   Sizhe Zhao 
 #
 # Original translations.
 #
@@ -15,2263 +18,2696 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Vim(Simplified Chinese)\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-05-26 14:21+0200\n"
-"PO-Revision-Date: 2006-04-21 14:00+0800\n"
-"Last-Translator: Yuheng Xie \n"
+"POT-Creation-Date: 2022-11-17 08:00+0800\n"
+"PO-Revision-Date: 2022-11-17 22:29+0800\n"
+"Last-Translator: Sizhe Zhao \n"
 "Language-Team: Simplified Chinese \n"
-"Language: \n"
+"Language: zh_CN\n"
 "MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
-"Content-Transfer-Encoding: 8-bit\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 
-#: ../api/private/helpers.c:201
+#: ../arglist.c:67
+msgid "E1156: Cannot change the argument list recursively"
+msgstr "E1156: ä¸èƒ½é€’å½’ä¿®æ”¹å‚æ•°åˆ—表"
+
+#: ../arglist.c:628
+msgid "E163: There is only one file to edit"
+msgstr "E163: åªæœ‰ä¸€ä¸ªæ–‡ä»¶å¯ç¼–辑"
+
+#: ../arglist.c:630
+msgid "E164: Cannot go before first file"
+msgstr "E164: 无法切æ¢ï¼Œå·²æ˜¯ç¬¬ä¸€ä¸ªæ–‡ä»¶"
+
+#: ../arglist.c:632
+msgid "E165: Cannot go beyond last file"
+msgstr "E165: 无法切æ¢ï¼Œå·²æ˜¯æœ€åŽä¸€ä¸ªæ–‡ä»¶"
+
+#: ../arglist.c:782
 #, fuzzy
-msgid "Unable to get option value"
-msgstr "选项傿•°åŽçš„内容无效"
+msgid "E610: No argument to delete"
+msgstr "E144: :z 䏿ޥå—éžæ•°å­—çš„å‚æ•°"
 
-#: ../api/private/helpers.c:204
-msgid "internal error: unknown option type"
-msgstr "内部错误:未知的选项类型"
+#: ../arglist.c:980
+msgid "E249: window layout changed unexpectedly"
+msgstr "E249: 窗å£å¸ƒå±€æ„外地改å˜äº†"
 
-#: ../buffer.c:92
-msgid "[Location List]"
-msgstr "[Location 列表]"
+#: ../autocmd.c:160
+msgid "--Deleted--"
+msgstr "--已删除--"
 
-#: ../buffer.c:93
-msgid "[Quickfix List]"
-msgstr "[Quickfix 列表]"
+#: ../autocmd.c:451
+#, c-format
+msgid "auto-removing autocommand: %s "
+msgstr "自动删除自动命令: %s "
+
+#. the group doesn't exist
+#: ../autocmd.c:501
+#, c-format
+msgid "E367: No such group: \"%s\""
+msgstr "E367: 无此组: \"%s\""
+
+#: ../autocmd.c:505
+#, fuzzy
+msgid "E936: Cannot delete the current group"
+msgstr "E351: ä¸èƒ½åœ¨å½“å‰çš„ 'foldmethod' 下删除折å "
+
+#: ../autocmd.c:513
+msgid "W19: Deleting augroup that is still in use"
+msgstr "W19: åˆ é™¤ä¾æ—§åœ¨ä½¿ç”¨ä¸­çš„自动组"
+
+#. Highlight title
+#: ../autocmd.c:896
+msgid ""
+"\n"
+"--- Autocommands ---"
+msgstr ""
+"\n"
+"--- 自动命令 ---"
+
+#: ../autocmd.c:1106
+#, c-format
+msgid "E680: : invalid buffer number "
+msgstr "E680: : æ— æ•ˆçš„ç¼“å†²åŒºå· "
+
+#: ../autocmd.c:1254
+msgid "E217: Can't execute autocommands for ALL events"
+msgstr "E217: ä¸èƒ½å¯¹æ‰€æœ‰äº‹ä»¶æ‰§è¡Œè‡ªåЍ命令"
+
+#: ../autocmd.c:1276
+#, c-format
+msgid "No matching autocommands: %s"
+msgstr "没有匹é…的自动命令:%s"
+
+#: ../autocmd.c:1693
+msgid "E218: autocommand nesting too deep"
+msgstr "E218: 自动命令嵌套层数过深"
+
+#: ../autocmd.c:2040
+#, c-format
+msgid "%s Autocommands for \"%s\""
+msgstr "%s 自动命令 \"%s\""
+
+#: ../autocmd.c:2049
+#, c-format
+msgid "Executing %s"
+msgstr "执行 %s"
+
+#: ../autocmd.c:2178
+#, c-format
+msgid "autocommand %s"
+msgstr "自动命令 %s"
+
+#: ../autocmd.c:2617
+#, c-format
+msgid "E215: Illegal character after *: %s"
+msgstr "E215: * åŽé¢æœ‰æ— æ•ˆå­—符: %s"
+
+#: ../autocmd.c:2625
+#, c-format
+msgid "E216: No such event: %s"
+msgstr "E216: 无此事件: %s"
+
+#: ../autocmd.c:2627
+#, c-format
+msgid "E216: No such group or event: %s"
+msgstr "E216: 无此组或事件: %s"
 
-#: ../buffer.c:94
+#: ../buffer.c:109
 msgid "E855: Autocommands caused command to abort"
 msgstr "E855: è‡ªåŠ¨å‘½ä»¤å¯¼è‡´å‘½ä»¤è¢«åœæ­¢"
 
-#: ../buffer.c:135
+#: ../buffer.c:111
+#, c-format
+msgid "E937: Attempt to delete a buffer that is in use: %s"
+msgstr "E937: 试图删除使用中的缓冲区:%s"
+
+#: ../buffer.c:219
 msgid "E82: Cannot allocate any buffer, exiting..."
 msgstr "E82: 无法分é…任何缓冲区,退出程åº..."
 
-#: ../buffer.c:138
+#: ../buffer.c:227
 msgid "E83: Cannot allocate buffer, using other one..."
 msgstr "E83: 无法分é…缓冲区,使用å¦ä¸€ä¸ªç¼“冲区..."
 
-#: ../buffer.c:763
+#: ../buffer.c:1070
 msgid "E515: No buffers were unloaded"
 msgstr "E515: 没有释放任何缓冲区"
 
-#: ../buffer.c:765
+#: ../buffer.c:1072
 msgid "E516: No buffers were deleted"
 msgstr "E516: 没有删除任何缓冲区"
 
-#: ../buffer.c:767
+#: ../buffer.c:1074
 msgid "E517: No buffers were wiped out"
 msgstr "E517: 没有清除任何缓冲区"
 
-#: ../buffer.c:772
-msgid "1 buffer unloaded"
-msgstr "释放了 1 个缓冲区"
-
-#: ../buffer.c:774
-#, c-format
-msgid "%d buffers unloaded"
-msgstr "释放了 %d 个缓冲区"
-
-#: ../buffer.c:777
-msgid "1 buffer deleted"
-msgstr "删除了 1 个缓冲区"
-
-#: ../buffer.c:779
-#, c-format
-msgid "%d buffers deleted"
-msgstr "删除了 %d 个缓冲区"
+#: ../buffer.c:1079
+#, fuzzy, c-format
+msgid "%d buffer unloaded"
+msgid_plural "%d buffers unloaded"
+msgstr[0] "释放了 %d 个缓冲区"
 
-#: ../buffer.c:782
-msgid "1 buffer wiped out"
-msgstr "清除了 1 个缓冲区"
+#: ../buffer.c:1082
+#, fuzzy, c-format
+msgid "%d buffer deleted"
+msgid_plural "%d buffers deleted"
+msgstr[0] "删除了 %d 个缓冲区"
 
-#: ../buffer.c:784
-#, c-format
-msgid "%d buffers wiped out"
-msgstr "清除了 %d 个缓冲区"
+#: ../buffer.c:1085
+#, fuzzy, c-format
+msgid "%d buffer wiped out"
+msgid_plural "%d buffers wiped out"
+msgstr[0] "清除了 %d 个缓冲区"
 
-#: ../buffer.c:806
+#: ../buffer.c:1102
 msgid "E90: Cannot unload last buffer"
 msgstr "E90: 无法释放最åŽä¸€ä¸ªç¼“冲区"
 
-#: ../buffer.c:874
+#: ../buffer.c:1191
 msgid "E84: No modified buffer found"
 msgstr "E84: 没有修改过的缓冲区"
 
 #. back where we started, didn't find anything.
-#: ../buffer.c:903
+#: ../buffer.c:1224
 msgid "E85: There is no listed buffer"
 msgstr "E85: 没有å¯åˆ—出的缓冲区"
 
-#: ../buffer.c:913
-#, c-format
-msgid "E86: Buffer % does not exist"
-msgstr "E86: 缓冲区 % ä¸å­˜åœ¨"
-
-#: ../buffer.c:915
+#: ../buffer.c:1237
 msgid "E87: Cannot go beyond last buffer"
 msgstr "E87: 无法切æ¢ï¼Œå·²æ˜¯æœ€åŽä¸€ä¸ªç¼“冲区"
 
-#: ../buffer.c:917
+#: ../buffer.c:1239
 msgid "E88: Cannot go before first buffer"
 msgstr "E88: 无法切æ¢ï¼Œå·²æ˜¯ç¬¬ä¸€ä¸ªç¼“冲区"
 
-#: ../buffer.c:945
+#: ../buffer.c:1277
 #, c-format
 msgid ""
 "E89: No write since last change for buffer % (add ! to override)"
 msgstr "E89: 缓冲区 % 已修改但尚未ä¿å­˜ (请加 ! 强制执行)"
 
+#: ../buffer.c:1290
+#, c-format
+msgid "E89: %s will be killed (add ! to override)"
+msgstr "E89: \"%s\" ä¼šè¢«ç»“æŸ (请加 ! 强制执行)"
+
+#: ../buffer.c:1698
+msgid "E948: Job still running (add ! to end the job)"
+msgstr "E948: 任务ä»åœ¨è¿è¡Œï¼ˆæ·»åŠ  ! æ¥ç»“æŸæ­¤ä»»åŠ¡ï¼‰"
+
+#: ../buffer.c:1700
+msgid "E37: No write since last change (add ! to override)"
+msgstr "E37: 已修改但尚未ä¿å­˜ (å¯ç”¨ ! 强制执行)"
+
+#: ../buffer.c:1709
+msgid "E948: Job still running"
+msgstr "E948: 任务ä»åœ¨è¿è¡Œ"
+
+#: ../buffer.c:1711
+msgid "E37: No write since last change"
+msgstr "E37: 已修改但尚未ä¿å­˜"
+
 #. wrap around (may cause duplicates)
-#: ../buffer.c:1423
+#: ../buffer.c:1858
 msgid "W14: Warning: List of file names overflow"
 msgstr "W14: 警告: 文件å过多"
 
-#: ../buffer.c:1555 ../quickfix.c:3361
+#: ../buffer.c:2032 ../quickfix.c:6290 ../window.c:195
 #, c-format
 msgid "E92: Buffer % not found"
 msgstr "E92: 找ä¸åˆ°ç¼“冲区 %"
 
-#: ../buffer.c:1798
+#: ../buffer.c:2289
 #, c-format
 msgid "E93: More than one match for %s"
 msgstr "E93: æ‰¾åˆ°ä¸æ­¢ä¸€ä¸ª %s"
 
-#: ../buffer.c:1800
+#: ../buffer.c:2291
 #, c-format
 msgid "E94: No matching buffer for %s"
 msgstr "E94: 没有匹é…的缓冲区 %s"
 
-#: ../buffer.c:2161
+#: ../buffer.c:2786
 #, c-format
 msgid "line %"
 msgstr "第 % 行"
 
-#: ../buffer.c:2233
+#: ../buffer.c:2856
 msgid "E95: Buffer with this name already exists"
 msgstr "E95: 已有缓冲区使用该åç§°"
 
-#: ../buffer.c:2498
+#: ../buffer.c:3109
 msgid " [Modified]"
 msgstr " [已修改]"
 
-#: ../buffer.c:2501
+#: ../buffer.c:3111
 msgid "[Not edited]"
 msgstr "[未编辑]"
 
-#: ../buffer.c:2504
-msgid "[New file]"
-msgstr "[新文件]"
-
-#: ../buffer.c:2505
+#: ../buffer.c:3115
 msgid "[Read errors]"
 msgstr "[读错误]"
 
-#: ../buffer.c:2506 ../buffer.c:3217 ../fileio.c:1807 ../screen.c:4895
+#: ../buffer.c:3117 ../fileio.c:1777 ../statusline.c:122 ../statusline.c:1545
 msgid "[RO]"
 msgstr "[åªè¯»]"
 
-#: ../buffer.c:2507 ../fileio.c:1807
+#: ../buffer.c:3117 ../fileio.c:1777
 msgid "[readonly]"
 msgstr "[åªè¯»]"
 
-#: ../buffer.c:2524
-#, c-format
-msgid "1 line --%d%%--"
-msgstr "1 行 --%d%%--"
-
-#: ../buffer.c:2526
-#, c-format
-msgid "% lines --%d%%--"
-msgstr "% 行 --%d%%--"
+#: ../buffer.c:3136
+#, fuzzy, c-format
+msgid "% line --%d%%--"
+msgid_plural "% lines --%d%%--"
+msgstr[0] "% 行 --%d%%--"
 
-#: ../buffer.c:2530
+#: ../buffer.c:3142
 #, c-format
 msgid "line % of % --%d%%-- col "
 msgstr "行 % / % --%d%%-- 列 "
 
-#: ../buffer.c:2632 ../buffer.c:4292 ../memline.c:1554
+#: ../buffer.c:3234 ../buffer.c:4143 ../memline.c:1456
 msgid "[No Name]"
 msgstr "[未命å]"
 
-#. must be a help buffer
-#: ../buffer.c:2667
+#. Must be a help buffer.
+#: ../buffer.c:3279
 msgid "help"
 msgstr "帮助"
 
-#: ../buffer.c:3225 ../screen.c:4883
-msgid "[Help]"
-msgstr "[帮助]"
-
-#: ../buffer.c:3254 ../screen.c:4887
-msgid "[Preview]"
-msgstr "[预览]"
-
-#: ../buffer.c:3528
+#: ../buffer.c:3421
 msgid "All"
 msgstr "全部"
 
-#: ../buffer.c:3528
+#: ../buffer.c:3421
 msgid "Bot"
 msgstr "底端"
 
-#: ../buffer.c:3531
+#: ../buffer.c:3423
 msgid "Top"
 msgstr "顶端"
 
-#: ../buffer.c:4244
-msgid ""
-"\n"
-"# Buffer list:\n"
-msgstr ""
-"\n"
-"# 缓冲区列表:\n"
+#: ../buffer.c:3912
+msgid "E382: Cannot write, 'buftype' option is set"
+msgstr "E382: 无法写入,已设定选项 'buftype'"
 
-#: ../buffer.c:4289
+#: ../buffer.c:3954
+msgid "[Prompt]"
+msgstr "[æç¤ºç¬¦]"
+
+#: ../buffer.c:3956
 msgid "[Scratch]"
-msgstr ""
+msgstr "[涂鸦]"
 
-#: ../buffer.c:4529
-msgid ""
-"\n"
-"--- Signs ---"
+#: ../buffer.h:72
+msgid "[Location List]"
+msgstr "[Location 列表]"
+
+#: ../buffer.h:73
+msgid "[Quickfix List]"
+msgstr "[Quickfix 列表]"
+
+#: ../change.c:68
+msgid "W10: Warning: Changing a readonly file"
+msgstr "W10: 警告: 正在修改åªè¯»æ–‡ä»¶"
+
+#: ../channel.c:499
+msgid "can only be opened in headless mode"
 msgstr ""
-"\n"
-"--- Signs ---"
 
-#: ../buffer.c:4538
-#, c-format
-msgid "Signs for %s:"
-msgstr "%s çš„ Signs:"
+#: ../channel.c:504
+msgid "channel was already open"
+msgstr "channel 已打开"
 
-#: ../buffer.c:4543
-#, c-format
-msgid "    line=%  id=%d  name=%s"
-msgstr "    行=%  id=%d  åç§°=%s"
+#: ../channel.c:550 ../channel.c:564 ../channel.c:574
+msgid "Can't send data to closed stream"
+msgstr "无法将数æ®å‘é€åˆ°å…³é—­çš„ stream"
+
+#: ../channel.c:560 ../channel.c:579
+msgid "Can't send raw data to rpc channel"
+msgstr "无法将 raw data å‘é€åˆ° rpc channel"
+
+#: ../cmdexpand.c:925
+msgid "tagname"
+msgstr "tag å"
+
+#: ../cmdexpand.c:928
+msgid " kind file\n"
+msgstr " 类型 文件\n"
+
+#: ../cmdhist.c:616
+msgid "'history' option is zero"
+msgstr "选项 'history' 为零"
+
+#: ../context.c:335
+msgid "E474: Failed to convert list to msgpack string buffer"
+msgstr ""
 
-#: ../cursor_shape.c:68
+#: ../cursor_shape.c:136
 msgid "E545: Missing colon"
 msgstr "E545: 缺少冒å·"
 
-#: ../cursor_shape.c:70 ../cursor_shape.c:94
+#: ../cursor_shape.c:139 ../cursor_shape.c:164
 msgid "E546: Illegal mode"
 msgstr "E546: 无效的模å¼"
 
-#: ../cursor_shape.c:134
+#: ../cursor_shape.c:197 ../optionstr.c:510
 msgid "E548: digit expected"
 msgstr "E548: æ­¤å¤„éœ€è¦æ•°å­—"
 
-#: ../cursor_shape.c:138
+#: ../cursor_shape.c:202
 msgid "E549: Illegal percentage"
 msgstr "E549: 无效的百分比"
 
-#: ../diff.c:146
+#: ../debugger.c:106
+msgid "Entering Debug mode.  Type \"cont\" to continue."
+msgstr "进入调试模å¼ã€‚输入 \"cont\" ç»§ç»­è¿è¡Œã€‚"
+
+# do not translate
+#: ../debugger.c:109
+#, c-format
+msgid "Oldval = \"%s\""
+msgstr ""
+
+#: ../debugger.c:114
+#, c-format
+msgid "Newval = \"%s\""
+msgstr "新值 = \"%s\""
+
+#: ../debugger.c:124 ../debugger.c:388
+#, c-format
+msgid "line %: %s"
+msgstr "第 % 行: %s"
+
+#: ../debugger.c:126 ../debugger.c:390
 #, c-format
-msgid "E96: Can not diff more than % buffers"
-msgstr "E96: ä¸èƒ½æ¯”较(diff) % 个以上的缓冲区"
+msgid "cmd: %s"
+msgstr "命令: %s"
 
-#: ../diff.c:753
+#: ../debugger.c:347
 #, fuzzy
+msgid "frame is zero"
+msgstr "E726: 步长为零"
+
+#: ../debugger.c:354
+#, c-format
+msgid "frame at highest level: %d"
+msgstr "帧级别最高:%d"
+
+#: ../debugger.c:434
+#, c-format
+msgid "Breakpoint in \"%s%s\" line %"
+msgstr "断点 \"%s%s\" 第 % 行"
+
+#: ../debugger.c:684
+#, c-format
+msgid "E161: Breakpoint not found: %s"
+msgstr "E161: 找ä¸åˆ°æ–­ç‚¹: %s"
+
+#: ../debugger.c:717
+msgid "No breakpoints defined"
+msgstr "没有定义断点"
+
+#: ../debugger.c:725
+#, c-format
+msgid "%3d  %s %s  line %"
+msgstr "%3d  %s %s  第 % 行"
+
+#: ../debugger.c:731
+#, c-format
+msgid "%3d  expr %s"
+msgstr "%3d  è¡¨è¾¾å¼ %s"
+
+#: ../diff.c:206
+#, c-format
+msgid "E96: Cannot diff more than % buffers"
+msgstr "E96: ä¸èƒ½æ¯”较 % 个以上的缓冲区"
+
+#: ../diff.c:767
+#, c-format
+msgid "Not enough memory to use internal diff for buffer \"%s\""
+msgstr "为缓冲区 \"%s\" 使用内部比对(diff)时无足够的内存"
+
+#: ../diff.c:1082
 msgid "E810: Cannot read or write temp files"
-msgstr "E557: 无法打开 termcap 文件"
+msgstr "E810: 无法读写临时文件"
 
-#: ../diff.c:755
+#: ../diff.c:1084
 msgid "E97: Cannot create diffs"
 msgstr "E97: 无法创建 diff"
 
-#: ../diff.c:966
-#, fuzzy
+#: ../diff.c:1125
+msgid "E960: Problem creating the internal diff"
+msgstr "E960: 创建内部 diff æ—¶é‡åˆ°é—®é¢˜"
+
+#: ../diff.c:1284
 msgid "E816: Cannot read patch output"
-msgstr "E98: æ— æ³•è¯»å– diff 的输出"
+msgstr "E816: æ— æ³•è¯»å– patch 的输出"
 
-#: ../diff.c:1220
+#: ../diff.c:1740
 msgid "E98: Cannot read diff output"
 msgstr "E98: æ— æ³•è¯»å– diff 的输出"
 
-#: ../diff.c:2081
+#: ../diff.c:2847
 msgid "E99: Current buffer is not in diff mode"
-msgstr "E99: 当å‰ç¼“冲区ä¸åœ¨ diff 模å¼"
+msgstr "E99: 当å‰ç¼“冲区ä¸åœ¨å·®å¼‚模å¼"
 
-#: ../diff.c:2100
-#, fuzzy
+#: ../diff.c:2867
 msgid "E793: No other buffer in diff mode is modifiable"
-msgstr "E100: 没有其它处于 diff 模å¼çš„缓冲区"
+msgstr "E793: 没有其它处于差异模å¼çš„缓冲区å¯ä¿®æ”¹"
 
-#: ../diff.c:2102
+#: ../diff.c:2869
 msgid "E100: No other buffer in diff mode"
-msgstr "E100: 没有其它处于 diff 模å¼çš„缓冲区"
+msgstr "E100: 没有其它处于差异模å¼çš„缓冲区"
 
-#: ../diff.c:2112
+#: ../diff.c:2880
 msgid "E101: More than two buffers in diff mode, don't know which one to use"
-msgstr "E101: 有两个以上的缓冲区处于 diff 模å¼ï¼Œä¸èƒ½å†³å®šç”¨å“ªä¸€ä¸ª"
+msgstr "E101: 有两个以上的缓冲区处于差异模å¼ï¼Œä¸èƒ½å†³å®šç”¨å“ªä¸€ä¸ª"
 
-#: ../diff.c:2141
+#: ../diff.c:2909
 #, c-format
 msgid "E102: Can't find buffer \"%s\""
 msgstr "E102: 找ä¸åˆ°ç¼“冲区 \"%s\""
 
-#: ../diff.c:2152
+#: ../diff.c:2920
 #, c-format
 msgid "E103: Buffer \"%s\" is not in diff mode"
-msgstr "E103: 缓冲区 \"%s\" ä¸åœ¨ diff 模å¼"
+msgstr "E103: 缓冲区 \"%s\" ä¸å¤„于差异模å¼"
 
-#: ../diff.c:2193
+#: ../diff.c:2960
 msgid "E787: Buffer changed unexpectedly"
 msgstr "E787: æ„外地改å˜äº†ç¼“冲区"
 
-#: ../digraph.c:1598
-msgid "E104: Escape not allowed in digraph"
-msgstr "E104: å¤åˆå­—符(digraph)中ä¸èƒ½ä½¿ç”¨ Escape"
+#: ../digraph.c:50
+#, c-format
+msgid "E1214: Digraph must be just two characters: %s"
+msgstr "E1214: 二åˆå­—ç¬¦å¿…é¡»åªæœ‰ä¸¤ä¸ªå­—符:%s"
 
-#: ../digraph.c:1760
-msgid "E544: Keymap file not found"
-msgstr "E544: 找ä¸åˆ° Keymap 文件"
+#: ../digraph.c:52
+#, c-format
+msgid "E1215: Digraph must be one character: %s"
+msgstr "E1215: 二åˆå­—符必须是一个字符:%s"
 
-#: ../digraph.c:1785
-msgid "E105: Using :loadkeymap not in a sourced file"
-msgstr "E105: 䏿˜¯åœ¨è„šæœ¬æ–‡ä»¶ä¸­ä½¿ç”¨ :loadkeymap "
+#: ../digraph.c:54
+msgid ""
+"E1216: digraph_setlist() argument must be a list of lists with two items"
+msgstr "E1216: digraph_setlist() 傿•°å¿…须是包å«ä¸¤é¡¹çš„列表的列表"
 
-#: ../digraph.c:1821
-msgid "E791: Empty keymap entry"
-msgstr "E791: ç©ºçš„é”®ä½æ˜ å°„项"
+#: ../digraph.c:1664
+msgid "E104: Escape not allowed in digraph"
+msgstr "E104: 二åˆå­—符中ä¸èƒ½ä½¿ç”¨ Escape"
 
-#: ../edit.c:82
-msgid " Keyword completion (^N^P)"
-msgstr " 关键字补全 (^N^P)"
+#: ../digraph.c:1737
+msgid "Custom"
+msgstr "自定义"
 
-#. ctrl_x_mode == 0, ^P/^N compl.
-#: ../edit.c:83
-msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
-msgstr " ^X æ¨¡å¼ (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
+#: ../digraph.c:1794
+msgid "Latin supplement"
+msgstr "æ‹‰ä¸æ–‡è¡¥å……"
 
-#: ../edit.c:85
-msgid " Whole line completion (^L^N^P)"
-msgstr " 整行补全 (^L^N^P)"
+#: ../digraph.c:1795
+msgid "Greek and Coptic"
+msgstr "希腊和科普特文"
 
-#: ../edit.c:86
-msgid " File name completion (^F^N^P)"
-msgstr " 文件å补全 (^F^N^P)"
+#: ../digraph.c:1796
+msgid "Cyrillic"
+msgstr "西里尔文"
 
-#: ../edit.c:87
-msgid " Tag completion (^]^N^P)"
-msgstr " Tag 补全 (^]^N^P)"
+#: ../digraph.c:1797
+msgid "Hebrew"
+msgstr "å¸Œä¼¯æ¥æ–‡"
 
-#: ../edit.c:88
-msgid " Path pattern completion (^N^P)"
-msgstr " 头文件模å¼è¡¥å…¨ (^N^P)"
+#: ../digraph.c:1798
+msgid "Arabic"
+msgstr "阿拉伯文"
 
-#: ../edit.c:89
-msgid " Definition completion (^D^N^P)"
-msgstr " 定义补全 (^D^N^P)"
+#: ../digraph.c:1799
+msgid "Latin extended"
+msgstr "æ‹‰ä¸æ–‡æ‰©å±•"
 
-#: ../edit.c:91
-msgid " Dictionary completion (^K^N^P)"
-msgstr " Dictionary 补全 (^K^N^P)"
+#: ../digraph.c:1800
+msgid "Greek extended"
+msgstr "希腊文扩展"
 
-#: ../edit.c:92
-msgid " Thesaurus completion (^T^N^P)"
-msgstr " Thesaurus 补全 (^T^N^P)"
+#: ../digraph.c:1801
+msgid "Punctuation"
+msgstr "标点符å·"
 
-#: ../edit.c:93
-msgid " Command-line completion (^V^N^P)"
-msgstr " 命令行补全 (^V^N^P)"
+#: ../digraph.c:1802
+msgid "Super- and subscripts"
+msgstr "上下标"
 
-#: ../edit.c:94
-msgid " User defined completion (^U^N^P)"
-msgstr " 用户自定义补全 (^U^N^P)"
+#: ../digraph.c:1803
+msgid "Currency"
+msgstr "è´§å¸ç¬¦å·"
 
-#: ../edit.c:95
-msgid " Omni completion (^O^N^P)"
-msgstr " 全能补全 (^O^N^P)"
+#: ../digraph.c:1804 ../digraph.c:1809 ../digraph.c:1819
+msgid "Other"
+msgstr "å…¶ä»–"
 
-#: ../edit.c:96
-msgid " Spelling suggestion (s^N^P)"
-msgstr " 拼写建议 (s^N^P)"
+#: ../digraph.c:1805
+msgid "Roman numbers"
+msgstr "罗马数字"
 
-#: ../edit.c:97
-msgid " Keyword Local completion (^N^P)"
-msgstr " 关键字局部补全 (^N^P)"
+#: ../digraph.c:1806
+msgid "Arrows"
+msgstr "箭头"
 
-#: ../edit.c:100
-msgid "Hit end of paragraph"
-msgstr "已到段è½ç»“å°¾"
+#: ../digraph.c:1807
+msgid "Mathematical operators"
+msgstr "æ•°å­¦è¿ç®—符"
 
-#: ../edit.c:101
-msgid "E839: Completion function changed window"
-msgstr "E839: 补全函数更改了窗å£"
+#: ../digraph.c:1808
+msgid "Technical"
+msgstr "技术符å·"
 
-#: ../edit.c:102
-msgid "E840: Completion function deleted text"
-msgstr "E840: 补全函数删除了文本"
+#: ../digraph.c:1810
+msgid "Box drawing"
+msgstr "方框绘图"
 
-#: ../edit.c:1847
-msgid "'dictionary' option is empty"
-msgstr "选项 'dictionary' 为空"
+#: ../digraph.c:1811
+msgid "Block elements"
+msgstr "å—状元素"
 
-#: ../edit.c:1848
-msgid "'thesaurus' option is empty"
-msgstr "选项 'thesaurus' 为空"
+#: ../digraph.c:1812
+msgid "Geometric shapes"
+msgstr "几何形状"
 
-#: ../edit.c:2655
-#, c-format
-msgid "Scanning dictionary: %s"
-msgstr "正在扫æ dictionary: %s"
+#: ../digraph.c:1813
+msgid "Symbols"
+msgstr "符å·"
 
-#: ../edit.c:3079
-msgid " (insert) Scroll (^E/^Y)"
-msgstr " (æ’å…¥) Scroll (^E/^Y)"
+#: ../digraph.c:1814
+msgid "Dingbats"
+msgstr "æ‚锦字符"
 
-#: ../edit.c:3081
-msgid " (replace) Scroll (^E/^Y)"
-msgstr " (替æ¢) Scroll (^E/^Y)"
+#: ../digraph.c:1815
+msgid "CJK symbols and punctuation"
+msgstr "中日韩标点符å·"
 
-#: ../edit.c:3587
-#, c-format
-msgid "Scanning: %s"
-msgstr "正在扫æ: %s"
+#: ../digraph.c:1816
+msgid "Hiragana"
+msgstr "日文平å‡å"
 
-#: ../edit.c:3614
-msgid "Scanning tags."
-msgstr "æ‰«ææ ‡ç­¾."
+#: ../digraph.c:1817
+msgid "Katakana"
+msgstr "日文片å‡å"
 
-#: ../edit.c:4519
-msgid " Adding"
-msgstr " 增加"
+#: ../digraph.c:1818
+msgid "Bopomofo"
+msgstr "注音符å·"
 
-#. showmode might reset the internal line pointers, so it must
-#. * be called before line = ml_get(), or when this address is no
-#. * longer needed.  -- Acevedo.
-#.
-#: ../edit.c:4562
-msgid "-- Searching..."
-msgstr "-- 查找中..."
+#: ../digraph.c:2062
+msgid "E544: Keymap file not found"
+msgstr "E544: 找ä¸åˆ° Keymap 文件"
 
-#: ../edit.c:4618
-msgid "Back at original"
-msgstr "回到起点"
-
-#: ../edit.c:4621
-msgid "Word from other line"
-msgstr "å¦ä¸€è¡Œçš„è¯"
-
-#: ../edit.c:4624
-msgid "The only match"
-msgstr "唯一匹é…"
-
-#: ../edit.c:4680
-#, c-format
-msgid "match %d of %d"
-msgstr "åŒ¹é… %d / %d"
-
-#: ../edit.c:4684
-#, c-format
-msgid "match %d"
-msgstr "åŒ¹é… %d"
-
-#: ../eval.c:137
-msgid "E18: Unexpected characters in :let"
-msgstr "E18: :let 中出现异常字符"
-
-#: ../eval.c:138
-#, c-format
-msgid "E684: list index out of range: %"
-msgstr "E684: List 索引超出范围: %"
+#: ../digraph.c:2083
+msgid "E105: Using :loadkeymap not in a sourced file"
+msgstr "E105: 䏿˜¯åœ¨è„šæœ¬æ–‡ä»¶ä¸­ä½¿ç”¨ :loadkeymap"
 
-#: ../eval.c:139
-#, c-format
-msgid "E121: Undefined variable: %s"
-msgstr "E121: 未定义的å˜é‡: %s"
+#: ../digraph.c:2118
+msgid "E791: Empty keymap entry"
+msgstr "E791: ç©ºçš„é”®ä½æ˜ å°„项"
 
-#: ../eval.c:140
+#. TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead
+#. maximum nesting of lists and dicts
+#: ../eval.c:92
 msgid "E111: Missing ']'"
 msgstr "E111: 缺少 ']'"
 
-#: ../eval.c:141
-#, c-format
-msgid "E686: Argument of %s must be a List"
-msgstr "E686: %s çš„å‚æ•°å¿…须是 List"
-
-#: ../eval.c:143
-#, c-format
-msgid "E712: Argument of %s must be a List or Dictionary"
-msgstr "E712: %s çš„å‚æ•°å¿…须是 List 或者 Dictionary"
-
-#: ../eval.c:144
-msgid "E713: Cannot use empty key for Dictionary"
-msgstr "E713: Dictionary 的键ä¸èƒ½ä¸ºç©º"
-
-#: ../eval.c:145
-msgid "E714: List required"
-msgstr "E714: éœ€è¦ List"
-
-#: ../eval.c:146
-msgid "E715: Dictionary required"
-msgstr "E715: éœ€è¦ Dictionary"
-
-#: ../eval.c:147
-#, c-format
-msgid "E118: Too many arguments for function: %s"
-msgstr "E118: å‡½æ•°çš„å‚æ•°è¿‡å¤š: %s"
-
-#: ../eval.c:148
-#, c-format
-msgid "E716: Key not present in Dictionary: %s"
-msgstr "E716: Dictionary 中ä¸å­˜åœ¨é”®: %s"
-
-#: ../eval.c:150
-#, c-format
-msgid "E122: Function %s already exists, add ! to replace it"
-msgstr "E122: 函数 %s 已存在,请加 ! 强制替æ¢"
-
-#: ../eval.c:151
-msgid "E717: Dictionary entry already exists"
-msgstr "E717: Dictionary 项已存在"
-
-#: ../eval.c:152
-msgid "E718: Funcref required"
-msgstr "E718: éœ€è¦ Funcref"
-
-#: ../eval.c:153
+#: ../eval.c:93
 msgid "E719: Cannot use [:] with a Dictionary"
 msgstr "E719: ä¸èƒ½å¯¹ Dictionary 使用 [:]"
 
-#: ../eval.c:154
-#, c-format
-msgid "E734: Wrong variable type for %s="
-msgstr "E734: %s= çš„å˜é‡ç±»åž‹ä¸æ­£ç¡®"
-
-#: ../eval.c:155
-#, c-format
-msgid "E130: Unknown function: %s"
-msgstr "E130: 未知的函数: %s"
+#: ../eval.c:95
+msgid "E274: No white space allowed before parenthesis"
+msgstr "E274: å°æ‹¬å·å‰ä¸å…许空白字符"
 
-#: ../eval.c:156
+#: ../eval.c:96
 #, c-format
-msgid "E461: Illegal variable name: %s"
-msgstr "E461: 无效的å˜é‡å: %s"
+msgid "E80: Error while writing: %s"
+msgstr "E80: 写入时出错:%s"
 
-#: ../eval.c:157
-#, fuzzy
-msgid "E806: using Float as a String"
-msgstr "E730: 将 List 作 String 使用"
+#: ../eval.c:97
+msgid "E1098: String, List or Blob required"
+msgstr "E1098: 需è¦å­—符串ã€åˆ—表或 Blob"
 
-#: ../eval.c:1830
-msgid "E687: Less targets than List items"
-msgstr "E687: 目标比 List 项数少"
+#: ../eval.c:98
+#, c-format
+msgid "E1169: Expression too recursive: %s"
+msgstr "E1169: 表达å¼é€’归层数过多:%s"
 
-#: ../eval.c:1834
-msgid "E688: More targets than List items"
-msgstr "E688: 目标比 List 项数多"
+#: ../eval.c:100
+#, c-format
+msgid "E1203: Dot can only be used on a dictionary: %s"
+msgstr "E1203: 点åªèƒ½ç”¨äºŽå­—典:%s"
 
-#: ../eval.c:1906
-msgid "Double ; in list of variables"
-msgstr "å˜é‡åˆ—表中出现两个 ;"
+#: ../eval.c:1095
+msgid ""
+"E5700: Expression from 'spellsuggest' must yield lists with exactly two "
+"values"
+msgstr ""
 
-#: ../eval.c:2078
+#: ../eval.c:1327 ../eval/vars.c:1112
 #, c-format
-msgid "E738: Can't list variables for %s"
-msgstr "E738: 无法列出 %s çš„å˜é‡"
+msgid "E121: Undefined variable: %.*s"
+msgstr "E121: å˜é‡æœªå®šä¹‰: %.*s"
 
-#: ../eval.c:2391
-msgid "E689: Can only index a List or Dictionary"
-msgstr "E689: åªèƒ½ç´¢å¼• List 或 Dictionary"
+#: ../eval.c:1358
+msgid "E689: Can only index a List, Dictionary or Blob"
+msgstr "E689: åªèƒ½ç´¢å¼•列表ã€å­—典或 Blob"
 
-#: ../eval.c:2396
+#: ../eval.c:1364
 msgid "E708: [:] must come last"
 msgstr "E708: [:] 必须在最åŽ"
 
-#: ../eval.c:2439
-msgid "E709: [:] requires a List value"
+#: ../eval.c:1376
+#, fuzzy
+msgid "E713: Cannot use empty key after ."
+msgstr "E713: Dictionary 的键ä¸èƒ½ä¸ºç©º"
+
+#: ../eval.c:1412
+#, fuzzy
+msgid "E709: [:] requires a List or Blob value"
 msgstr "E709: [:] 需è¦ä¸€ä¸ª List 值"
 
-#: ../eval.c:2674
+#: ../eval.c:1665
+msgid "E972: Blob value does not have the right number of bytes"
+msgstr "E972: Blob 值的字节数ä¸å¯¹"
+
+#: ../eval.c:1728
+msgid "E996: Cannot lock a range"
+msgstr "E996: ä¸èƒ½é”定范围"
+
+#: ../eval.c:1771
 msgid "E710: List value has more items than target"
 msgstr "E710: List 值的项比目标多"
 
-#: ../eval.c:2678
+#: ../eval.c:1776
 msgid "E711: List value has not enough items"
 msgstr "E711: List 值没有足够多的项"
 
-#: ../eval.c:2867
+#: ../eval.c:1784
+msgid "E996: Cannot lock a list or dict"
+msgstr "E996: ä¸èƒ½é”定列表或字典"
+
+#: ../eval.c:1866
 msgid "E690: Missing \"in\" after :for"
 msgstr "E690: :for åŽç¼ºå°‘ \"in\""
 
-#: ../eval.c:3063
-#, c-format
-msgid "E107: Missing parentheses: %s"
-msgstr "E107: 缺少括å·: %s"
-
-#: ../eval.c:3263
-#, c-format
-msgid "E108: No such variable: \"%s\""
-msgstr "E108: æ— æ­¤å˜é‡: \"%s\""
-
-#: ../eval.c:3333
-msgid "E743: variable nested too deep for (un)lock"
-msgstr "E743: (un)lock çš„å˜é‡åµŒå¥—过深"
-
-#: ../eval.c:3630
+#: ../eval.c:2389
 msgid "E109: Missing ':' after '?'"
 msgstr "E109: '?' åŽç¼ºå°‘ ':'"
 
-#: ../eval.c:3893
-msgid "E691: Can only compare List with List"
-msgstr "E691: åªèƒ½æ¯”较 List å’Œ List"
-
-#: ../eval.c:3895
-msgid "E692: Invalid operation for Lists"
-msgstr "E692: 对 List 无效的æ“作"
-
-#: ../eval.c:3915
-msgid "E735: Can only compare Dictionary with Dictionary"
-msgstr "E735: åªèƒ½æ¯”较 Dictionary å’Œ Dictionary"
-
-#: ../eval.c:3917
-msgid "E736: Invalid operation for Dictionary"
-msgstr "E736: 对 Dictionary 无效的æ“作"
-
-#: ../eval.c:3932
-msgid "E693: Can only compare Funcref with Funcref"
-msgstr "E693: åªèƒ½æ¯”较 Funcref å’Œ Funcref"
-
-#: ../eval.c:3934
-msgid "E694: Invalid operation for Funcrefs"
-msgstr "E694: 对 Funcrefs 无效的æ“作"
-
-#: ../eval.c:4277
-#, fuzzy
+#: ../eval.c:2893
 msgid "E804: Cannot use '%' with Float"
-msgstr "E719: ä¸èƒ½å¯¹ Dictionary 使用 [:]"
+msgstr "E804: ä¸èƒ½å¯¹æµ®ç‚¹æ•°ä½¿ç”¨ '%'"
 
-#: ../eval.c:4478
+#: ../eval.c:3037
+msgid "E973: Blob literal should have an even number of hex characters"
+msgstr "E973: Blob å­—é¢é‡åº”è¯¥æœ‰å¶æ•°ä¸ªå六进制字符"
+
+#: ../eval.c:3137
 msgid "E110: Missing ')'"
 msgstr "E110: 缺少 ')'"
 
-#: ../eval.c:4609
+#: ../eval.c:3372
+#, fuzzy
+msgid "E260: Missing name after ->"
+msgstr "E526: <%s> åŽé¢ç¼ºå°‘æ•°å­—"
+
+#: ../eval.c:3431
 msgid "E695: Cannot index a Funcref"
-msgstr "E695: ä¸èƒ½ç´¢å¼•一个 Funcref"
+msgstr "E695: ä¸èƒ½ç´¢å¼• Funcref"
 
-#: ../eval.c:4839
+#: ../eval.c:3442
+msgid "E909: Cannot index a special variable"
+msgstr "E909: ä¸èƒ½ç´¢å¼•特殊å˜é‡"
+
+#: ../eval.c:3730
 #, c-format
 msgid "E112: Option name missing: %s"
-msgstr "E112: 缺少选项åç§°: %s"
+msgstr "E112: 缺少选项å称:%s"
 
-#: ../eval.c:4855
+#: ../eval.c:3751
 #, c-format
 msgid "E113: Unknown option: %s"
-msgstr "E113: 未知的选项: %s"
+msgstr "E113: 未知的选项:%s"
 
-#: ../eval.c:4904
+#: ../eval.c:3798
 #, c-format
 msgid "E114: Missing quote: %s"
-msgstr "E114: 缺少引å·: %s"
+msgstr "E114: 缺少引å·ï¼š%s"
 
-#: ../eval.c:5020
+#: ../eval.c:3936
 #, c-format
 msgid "E115: Missing quote: %s"
-msgstr "E115: 缺少引å·: %s"
+msgstr "E115: 缺少引å·ï¼š%s"
 
-#: ../eval.c:5084
+#: ../eval.c:4031
 #, c-format
 msgid "E696: Missing comma in List: %s"
-msgstr "E696: List 中缺少逗å·: %s"
+msgstr "E696: 列表中缺少逗å·ï¼š%s"
 
-#: ../eval.c:5091
+#: ../eval.c:4038
 #, c-format
 msgid "E697: Missing end of List ']': %s"
-msgstr "E697: List 缺少结æŸç¬¦ ']': %s"
+msgstr "E697: 列表缺少结æŸç¬¦ ']':%s"
 
-#: ../eval.c:6475
+#: ../eval.c:4338
+msgid "Not enough memory to set references, garbage collection aborted!"
+msgstr "没有足够的内存æ¥è®¾ç½®å¼•用,垃圾回收已中止ï¼"
+
+#: ../eval.c:4687
 #, c-format
 msgid "E720: Missing colon in Dictionary: %s"
-msgstr "E720: Dictionary 中缺少冒å·: %s"
+msgstr "E720: 字典中缺少冒å·ï¼š%s"
 
-#: ../eval.c:6499
+#: ../eval.c:4710
 #, c-format
 msgid "E721: Duplicate key in Dictionary: \"%s\""
 msgstr "E721: Dictionary 中出现é‡å¤çš„é”®: \"%s\""
 
-#: ../eval.c:6517
+#: ../eval.c:4728
 #, c-format
 msgid "E722: Missing comma in Dictionary: %s"
-msgstr "E722: Dictionary 中缺少逗å·: %s"
+msgstr "E722: 字典中缺少逗å·ï¼š%s"
 
-#: ../eval.c:6524
+#: ../eval.c:4735
 #, c-format
 msgid "E723: Missing end of Dictionary '}': %s"
-msgstr "E723: Dictionary 缺少结æŸç¬¦ '}': %s"
+msgstr "E723: 字典缺少结æŸç¬¦ '}':%s"
 
-#: ../eval.c:6555
-msgid "E724: variable nested too deep for displaying"
-msgstr "E724: å˜é‡åµŒå¥—过深无法显示"
+#: ../eval.c:4854
+msgid "map() argument"
+msgstr "map() 傿•°"
 
-#: ../eval.c:7188
-#, fuzzy, c-format
-msgid "E740: Too many arguments for function %s"
-msgstr "E118: å‡½æ•°çš„å‚æ•°è¿‡å¤š: %s"
+#: ../eval.c:4855
+msgid "filter() argument"
+msgstr "filter() 傿•°"
 
-#: ../eval.c:7190
-#, fuzzy, c-format
-msgid "E116: Invalid arguments for function %s"
-msgstr "E118: å‡½æ•°çš„å‚æ•°è¿‡å¤š: %s"
+#: ../eval.c:5076
+#, c-format
+msgid "E700: Unknown function: %s"
+msgstr "E700: 未知的函数:%s"
 
-#: ../eval.c:7377
-#, fuzzy, c-format
-msgid "E117: Unknown function: %s"
-msgstr "E130: 未知的函数: %s"
+#: ../eval.c:5112
+msgid "E922: expected a dict"
+msgstr "E922: 需è¦å­—å…¸"
 
-#: ../eval.c:7383
-#, c-format
-msgid "E119: Not enough arguments for function: %s"
-msgstr "E119: 函数 %s çš„å‚æ•°å¤ªå°‘"
+#: ../eval.c:5122
+msgid "E923: Second argument of function() must be a list or a dict"
+msgstr "E923: function() çš„ç¬¬äºŒä¸ªå‚æ•°å¿…须是列表或字典"
 
-#: ../eval.c:7387
-#, c-format
-msgid "E120: Using  not in a script context: %s"
-msgstr "E120:  ä¸èƒ½åœ¨ script 上下文外使用: %s"
+#: ../eval.c:5397
+msgid "E5050: {opts} must be the only argument"
+msgstr "E5050: {opts} å¿…é¡»æ˜¯å”¯ä¸€çš„å‚æ•°"
 
-#: ../eval.c:7391
+#: ../eval.c:5794 ../os/shell.c:718
 #, c-format
-msgid "E725: Calling dict function without Dictionary: %s"
-msgstr "E725: 调用字典函数但是没有字典:%s"
+msgid "Executing command: \"%s\""
+msgstr "执行命令:\"%s\""
 
-#: ../eval.c:7453
-#, fuzzy
-msgid "E808: Number or Float required"
-msgstr "E521: = åŽé¢éœ€è¦æ•°å­—"
-
-#: ../eval.c:7503
-#, fuzzy
-msgid "add() argument"
-msgstr "-c 傿•°"
+#: ../eval.c:5902
+msgid "E921: Invalid callback argument"
+msgstr "E921: å›žè°ƒå‚æ•°æ— æ•ˆ"
 
-#: ../eval.c:7907
-msgid "E699: Too many arguments"
-msgstr "E699: 傿•°è¿‡å¤š"
+#: ../eval.c:7603
+msgid "E698: variable nested too deep for making a copy"
+msgstr "E698: å˜é‡åµŒå¥—过深,无法å¤åˆ¶"
 
-#: ../eval.c:8073
-msgid "E785: complete() can only be used in Insert mode"
-msgstr "E785: complete() åªèƒ½åœ¨æ’入模å¼ä¸­ä½¿ç”¨"
+#: ../eval.c:8078
+msgid ""
+"\n"
+"\tLast set from "
+msgstr ""
+"\n"
+"\t最近修改于 "
 
-#: ../eval.c:8156
-msgid "&Ok"
-msgstr "确定(&O)"
+#: ../eval.c:8690
+msgid "E5009: $VIMRUNTIME is empty or unset"
+msgstr "E5009: $VIMRUNTIME 为空或未设置"
 
-#: ../eval.c:8676
+#: ../eval.c:8694
 #, c-format
-msgid "E737: Key already exists: %s"
-msgstr "E737: 键已存在: %s"
+msgid "E5009: Invalid $VIMRUNTIME: %s"
+msgstr "E5009: $VIMRUNTIME 无效:%s"
 
-#: ../eval.c:8692
-msgid "extend() argument"
-msgstr "extend() 傿•°"
+#: ../eval.c:8696
+msgid "E5009: Invalid 'runtimepath'"
+msgstr "E5009: 'runtimepath' 无效"
 
-#: ../eval.c:8915
-msgid "map() argument"
-msgstr "map() 傿•°"
+#: ../eval.c:8783
+msgid "E977: Can only compare Blob with Blob"
+msgstr "E977: Blob åªèƒ½ä¸Ž Blob 比较"
 
-#: ../eval.c:8916
-msgid "filter() argument"
-msgstr "filter() 傿•°"
+#: ../eval.c:8806
+msgid "E691: Can only compare List with List"
+msgstr "E691: 列表åªèƒ½ä¸Žåˆ—表比较"
+
+#: ../eval.c:8808
+msgid "E692: Invalid operation for List"
+msgstr "E692: æ“作对列表无效"
+
+#: ../eval.c:8829
+msgid "E735: Can only compare Dictionary with Dictionary"
+msgstr "E735: å­—å…¸åªèƒ½ä¸Žå­—典比较"
+
+#: ../eval.c:8831
+msgid "E736: Invalid operation for Dictionary"
+msgstr "E736: æ“作对字典无效"
+
+#: ../eval.c:8845
+msgid "E694: Invalid operation for Funcrefs"
+msgstr "E694: æ“作对 Funcrefs 无效"
 
-#: ../eval.c:9229
+#: ../eval/decode.c:132
 #, c-format
-msgid "+-%s%3ld line: "
-msgid_plural "+-%s%3ld lines: "
-msgstr[0] "+-%s%3ld 行: "
+msgid "E474: Expected comma before list item: %s"
+msgstr "E474: 列表项å‰åº”有逗å·ï¼š%s"
 
-#: ../eval.c:9291
+#: ../eval/decode.c:140
 #, c-format
-msgid "E700: Unknown function: %s"
-msgstr "E700: 未知的函数: %s"
+msgid "E474: Expected colon before dictionary value: %s"
+msgstr "E474: 字典值å‰åº”有冒å·ï¼š%s"
 
-#: ../eval.c:10729
-msgid "called inputrestore() more often than inputsave()"
-msgstr "inputrestore() 的调用次数多于 inputsave()"
+#: ../eval/decode.c:167
+#, c-format
+msgid "E474: Expected string key: %s"
+msgstr "E474: 需è¦å­—符串键:%s"
 
-#: ../eval.c:10771
-#, fuzzy
-msgid "insert() argument"
-msgstr "-c 傿•°"
+#: ../eval/decode.c:173
+#, c-format
+msgid "E474: Expected comma before dictionary key: %s"
+msgstr "E474: 字典键å‰åº”有逗å·ï¼š%s"
 
-#: ../eval.c:10841
-msgid "E786: Range not allowed"
-msgstr "E786: ä¸å…许的范围"
+#: ../eval/decode.c:340
+#, c-format
+msgid "E474: Unfinished escape sequence: %.*s"
+msgstr "E474: 转义åºåˆ—ä¸å®Œæ•´ï¼š%.*s"
 
-#: ../eval.c:11140
-msgid "E701: Invalid type for len()"
-msgstr "E701: len() 的类型无效"
+#: ../eval/decode.c:347
+#, c-format
+msgid "E474: Unfinished unicode escape sequence: %.*s"
+msgstr "E474: Unicode 转义åºåˆ—ä¸å®Œæ•´ï¼š%.*s"
 
-#: ../eval.c:11980
-msgid "E726: Stride is zero"
-msgstr "E726: 步长为零"
+#: ../eval/decode.c:354
+#, c-format
+msgid "E474: Expected four hex digits after \\u: %.*s"
+msgstr "E474: \\u 之åŽåº”有四ä½å六进制数字:%.*s"
 
-#: ../eval.c:11982
-msgid "E727: Start past end"
-msgstr "E727: 起始值在终止值åŽ"
+#: ../eval/decode.c:375
+#, c-format
+msgid "E474: Unknown escape sequence: %.*s"
+msgstr "E474: 未知的转义åºåˆ—:%.*s"
 
-#: ../eval.c:12024 ../eval.c:15297
-msgid ""
-msgstr "<空>"
+#: ../eval/decode.c:382
+#, c-format
+msgid "E474: ASCII control characters cannot be present inside string: %.*s"
+msgstr "E474: 字符串中ä¸èƒ½å‡ºçް ASCII 控制字符:%.*s"
 
-#: ../eval.c:12282
-#, fuzzy
-msgid "remove() argument"
-msgstr "--cmd 傿•°"
+#: ../eval/decode.c:395
+#, c-format
+msgid "E474: Only UTF-8 strings allowed: %.*s"
+msgstr "E474: åªå…许 UTF-8 字符串:%.*s"
 
-#: ../eval.c:12466
-msgid "E655: Too many symbolic links (cycle?)"
-msgstr "E655: 符å·è¿žæŽ¥è¿‡å¤š(循环?)"
+#: ../eval/decode.c:398
+#, c-format
+msgid ""
+"E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: "
+"%.*s"
+msgstr ""
 
-#: ../eval.c:12593
-#, fuzzy
-msgid "reverse() argument"
-msgstr "-c 傿•°"
+#: ../eval/decode.c:409
+#, c-format
+msgid "E474: Expected string end: %.*s"
+msgstr ""
 
-#: ../eval.c:13721
-#, fuzzy
-msgid "sort() argument"
-msgstr "-c 傿•°"
+#: ../eval/decode.c:555
+#, c-format
+msgid "E474: Leading zeroes are not allowed: %.*s"
+msgstr "E474: ä¸å…许å‰å¯¼é›¶ï¼š%.*s"
 
-#: ../eval.c:13721
-#, fuzzy
-msgid "uniq() argument"
-msgstr "-c 傿•°"
+#: ../eval/decode.c:584
+#, c-format
+msgid "E474: Missing number after minus sign: %.*s"
+msgstr "E474: è´Ÿå·åŽç¼ºå°‘数字:%.*s"
 
-#: ../eval.c:13776
-msgid "E702: Sort compare function failed"
-msgstr "E702: Sort 比较函数失败"
+#: ../eval/decode.c:587
+#, c-format
+msgid "E474: Missing number after decimal dot: %.*s"
+msgstr "E474: å°æ•°ç‚¹åŽç¼ºå°‘数字:%.*s"
 
-#: ../eval.c:13806
-msgid "E882: Uniq compare function failed"
-msgstr "E882: Uniq 比较函数失败"
+#: ../eval/decode.c:590
+#, c-format
+msgid "E474: Missing exponent: %.*s"
+msgstr "E474: 缺少指数:%.*s"
 
-#: ../eval.c:14085
-msgid "(Invalid)"
-msgstr "(无效)"
+#: ../eval/decode.c:602
+#, c-format
+msgid ""
+"E685: internal error: while converting number \"%.*s\" to float string2float "
+"consumed %zu bytes in place of %zu"
+msgstr ""
 
-#: ../eval.c:14590
-msgid "E677: Error writing temp file"
-msgstr "E677: 写临时文件出错"
+#: ../eval/decode.c:613
+#, c-format
+msgid ""
+"E685: internal error: while converting number \"%.*s\" to integer vim_str2nr "
+"consumed %i bytes in place of %zu"
+msgstr ""
 
-#: ../eval.c:16159
-#, fuzzy
-msgid "E805: Using a Float as a Number"
-msgstr "E805: å°†æµ®ç‚¹æ•°å½“åšæ•°å­—使用"
+#: ../eval/decode.c:665
+msgid "E474: Attempt to decode a blank string"
+msgstr "E474: 试图解ç ç©ºå­—符串"
 
-#: ../eval.c:16162
-msgid "E703: Using a Funcref as a Number"
-msgstr "E703: å°†å‡½æ•°å½“åšæ•°å­—使用"
+#: ../eval/decode.c:682
+#, c-format
+msgid "E474: No container to close: %.*s"
+msgstr "E474: 没有容器å¯ä»¥å…³é—­ï¼š%.*s"
 
-#: ../eval.c:16170
-msgid "E745: Using a List as a Number"
-msgstr "E745: å°†åˆ—è¡¨å½“åšæ•°å­—使用"
+#: ../eval/decode.c:687
+#, c-format
+msgid "E474: Closing list with curly bracket: %.*s"
+msgstr "E474: 用花括å·ç»“æŸåˆ—表:%.*s"
 
-#: ../eval.c:16173
-msgid "E728: Using a Dictionary as a Number"
-msgstr "E728: å°†å­—å…¸å½“åšæ•°å­—使用"
+#: ../eval/decode.c:690
+#, c-format
+msgid "E474: Closing dictionary with square bracket: %.*s"
+msgstr "E474: 用方括å·ç»“æŸå­—典:%.*s"
 
-#: ../eval.c:16259
-msgid "E729: using Funcref as a String"
-msgstr "E729: 将函数当åšå­—符串使用"
+#: ../eval/decode.c:694
+#, c-format
+msgid "E474: Trailing comma: %.*s"
+msgstr "E474: å°¾éšé€—å·ï¼š%.*s"
 
-#: ../eval.c:16262
-msgid "E730: using List as a String"
-msgstr "E730: 将列表当åšå­—符串使用"
+#: ../eval/decode.c:697
+#, c-format
+msgid "E474: Expected value after colon: %.*s"
+msgstr "E474: 冒å·åŽåº”有值:%.*s"
 
-#: ../eval.c:16265
-msgid "E731: using Dictionary as a String"
-msgstr "E731: 将字典当åšå­—符串使用"
+#: ../eval/decode.c:701
+#, fuzzy, c-format
+msgid "E474: Expected value: %.*s"
+msgstr "E415: ä¸è¯¥æœ‰çš„ç­‰å·: %s"
 
-#: ../eval.c:16619
-#, c-format
-msgid "E706: Variable type mismatch for: %s"
-msgstr "E706: å˜é‡ç±»åž‹ä¸åŒ¹é…: %s"
+#: ../eval/decode.c:720
+#, fuzzy, c-format
+msgid "E474: Comma not inside container: %.*s"
+msgstr "E242: %s 为ä¸èƒ½è¯†åˆ«çš„颜色åç§°"
 
-#: ../eval.c:16705
+#: ../eval/decode.c:725
 #, fuzzy, c-format
-msgid "E795: Cannot delete variable %s"
-msgstr "E738: 无法列出 %s çš„å˜é‡"
+msgid "E474: Duplicate comma: %.*s"
+msgstr "E721: Dictionary 中出现é‡å¤çš„é”®: \"%s\""
 
-#: ../eval.c:16724
+#: ../eval/decode.c:728
 #, c-format
-msgid "E704: Funcref variable name must start with a capital: %s"
-msgstr "E704: Funcref å˜é‡å必须以大写字æ¯å¼€å¤´: %s"
+msgid "E474: Comma after colon: %.*s"
+msgstr "E474: 冒å·åŽæœ‰é€—å·ï¼š%.*s"
 
-#: ../eval.c:16732
+#: ../eval/decode.c:732
 #, c-format
-msgid "E705: Variable name conflicts with existing function: %s"
-msgstr "E705: å˜é‡å与已有函数å冲çª: %s"
+msgid "E474: Using comma in place of colon: %.*s"
+msgstr "E474: 应使用冒å·ï¼Œä½†ç”¨äº†é€—å·ï¼š%.*s"
 
-#: ../eval.c:16763
+#: ../eval/decode.c:740
 #, c-format
-msgid "E741: Value is locked: %s"
-msgstr "E741: 值已é”定: %s"
+msgid "E474: Leading comma: %.*s"
+msgstr "E474: å‰å¯¼é€—å·ï¼š%.*s"
 
-#: ../eval.c:16764 ../eval.c:16769 ../message.c:1839
-msgid "Unknown"
-msgstr "未知"
+#: ../eval/decode.c:748
+#, fuzzy, c-format
+msgid "E474: Colon not inside container: %.*s"
+msgstr "E242: %s 为ä¸èƒ½è¯†åˆ«çš„颜色åç§°"
 
-#: ../eval.c:16768
+#: ../eval/decode.c:753
 #, c-format
-msgid "E742: Cannot change value of %s"
-msgstr "E742: æ— æ³•æ”¹å˜ %s 的值"
+msgid "E474: Using colon not in dictionary: %.*s"
+msgstr "E474: 在字典之外使用冒å·ï¼š%.*s"
 
-#: ../eval.c:16838
-msgid "E698: variable nested too deep for making a copy"
-msgstr "E698: å˜é‡åµŒå¥—过深无法å¤åˆ¶"
+#: ../eval/decode.c:756
+#, fuzzy, c-format
+msgid "E474: Unexpected colon: %.*s"
+msgstr "E415: ä¸è¯¥æœ‰çš„ç­‰å·: %s"
 
-#: ../eval.c:17249
+#: ../eval/decode.c:759
 #, c-format
-msgid "E123: Undefined function: %s"
-msgstr "E123: 函数 %s 尚未定义"
+msgid "E474: Colon after comma: %.*s"
+msgstr "E474: 逗å·åŽæœ‰å†’å·ï¼š%.*s"
 
-#: ../eval.c:17260
+#: ../eval/decode.c:762
 #, c-format
-msgid "E124: Missing '(': %s"
-msgstr "E124: 缺少 '(': %s"
+msgid "E474: Duplicate colon: %.*s"
+msgstr "E474: é‡å¤çš„冒å·ï¼š%.*s"
 
-#: ../eval.c:17293
-#, fuzzy
-msgid "E862: Cannot use g: here"
-msgstr "E284: ä¸èƒ½è®¾å®š IC 值"
-
-#: ../eval.c:17312
-#, c-format
-msgid "E125: Illegal argument: %s"
-msgstr "E125: æ— æ•ˆçš„å‚æ•°: %s"
-
-#: ../eval.c:17323
+#: ../eval/decode.c:775
 #, fuzzy, c-format
-msgid "E853: Duplicate argument name: %s"
-msgstr "E125: æ— æ•ˆçš„å‚æ•°: %s"
-
-#: ../eval.c:17416
-msgid "E126: Missing :endfunction"
-msgstr "E126: 缺少 :endfunction"
+msgid "E474: Expected null: %.*s"
+msgstr "E415: ä¸è¯¥æœ‰çš„ç­‰å·: %s"
 
-#: ../eval.c:17537
+#: ../eval/decode.c:787
 #, fuzzy, c-format
-msgid "E707: Function name conflicts with variable: %s"
-msgstr "E746: 函数å与脚本文件åä¸åŒ¹é…: %s"
-
-#: ../eval.c:17549
-#, c-format
-msgid "E127: Cannot redefine function %s: It is in use"
-msgstr "E127: 函数 %s 正在使用中,ä¸èƒ½é‡æ–°å®šä¹‰"
-
-#: ../eval.c:17604
-#, c-format
-msgid "E746: Function name does not match script file name: %s"
-msgstr "E746: 函数å与脚本文件åä¸åŒ¹é…: %s"
+msgid "E474: Expected true: %.*s"
+msgstr "E415: ä¸è¯¥æœ‰çš„ç­‰å·: %s"
 
-#: ../eval.c:17716
-msgid "E129: Function name required"
-msgstr "E129: 需è¦å‡½æ•°å"
+#: ../eval/decode.c:799
+#, fuzzy, c-format
+msgid "E474: Expected false: %.*s"
+msgstr "E415: ä¸è¯¥æœ‰çš„ç­‰å·: %s"
 
-#: ../eval.c:17824
+#: ../eval/decode.c:879
 #, fuzzy, c-format
-msgid "E128: Function name must start with a capital or \"s:\": %s"
-msgstr "E128: 函数å必须以大写字æ¯å¼€å¤´æˆ–者包å«å†’å·: %s"
+msgid "E474: Unidentified byte: %.*s"
+msgstr "E121: 未定义的å˜é‡: %s"
 
-#: ../eval.c:17833
+#: ../eval/decode.c:898
 #, fuzzy, c-format
-msgid "E884: Function name cannot contain a colon: %s"
-msgstr "E128: 函数å必须以大写字æ¯å¼€å¤´æˆ–者包å«å†’å·: %s"
+msgid "E474: Trailing characters: %.*s"
+msgstr "E488: 多余的尾部字符"
 
-#: ../eval.c:18336
+#: ../eval/decode.c:906
 #, c-format
-msgid "E131: Cannot delete function %s: It is in use"
-msgstr "E131: 无法删除函数 %s: 正在使用中"
-
-#: ../eval.c:18441
-msgid "E132: Function call depth is higher than 'maxfuncdepth'"
-msgstr "E132: 函数调用深度超出 'maxfuncdepth'"
+msgid "E474: Unexpected end of input: %.*s"
+msgstr "E474: 输入æ„外结æŸï¼š%.*s"
 
-#: ../eval.c:18568
+#: ../eval/encode.c:123
 #, c-format
-msgid "calling %s"
-msgstr "调用 %s"
+msgid "key %s"
+msgstr ""
 
-#: ../eval.c:18651
+#: ../eval/encode.c:124
 #, c-format
-msgid "%s aborted"
-msgstr "%s 已中止"
+msgid "key %s at index %i from special map"
+msgstr ""
 
-#: ../eval.c:18653
+#: ../eval/encode.c:125
 #, c-format
-msgid "%s returning #%"
-msgstr "%s 返回 #% "
+msgid "index %i"
+msgstr "索引 %i"
 
-#: ../eval.c:18670
-#, c-format
-msgid "%s returning %s"
-msgstr "%s 返回 %s"
+#: ../eval/encode.c:126
+msgid "partial"
+msgstr "部分"
 
-#: ../eval.c:18691 ../ex_cmds2.c:2695
+#: ../eval/encode.c:127
 #, c-format
-msgid "continuing in %s"
-msgstr "在 %s 中继续"
+msgid "argument %i"
+msgstr "傿•° %i"
 
-#: ../eval.c:18795
-msgid "E133: :return not inside a function"
-msgstr "E133: :return ä¸åœ¨å‡½æ•°ä¸­"
+#: ../eval/encode.c:128
+msgid "partial self dictionary"
+msgstr ""
 
-#: ../eval.c:19159
-msgid ""
-"\n"
-"# global variables:\n"
+#: ../eval/encode.c:203
+msgid "itself"
 msgstr ""
-"\n"
-"# 全局å˜é‡:\n"
 
-#: ../eval.c:19254
-msgid ""
-"\n"
-"\tLast set from "
+#. Only give this message once for a recursive call to avoid
+#. flooding the user with errors.
+#: ../eval/encode.c:453 ../eval/encode.c:533
+msgid "E724: unable to correctly dump variable with self-referencing container"
 msgstr ""
-"\n"
-"\t最近修改于 "
 
-#: ../eval.c:19272
-#, fuzzy
-msgid "No old files"
-msgstr "æ²¡æœ‰åŒ…å«æ–‡ä»¶"
+#: ../eval/encode.c:563
+msgid "E474: Unable to represent NaN value in JSON"
+msgstr "E474: 在 JSON 中无法表示 NaN 值"
 
-#: ../ex_cmds.c:122
+#: ../eval/encode.c:567
+msgid "E474: Unable to represent infinity in JSON"
+msgstr "E474: 在 JSON 中无法表示无穷大"
+
+#: ../eval/encode.c:635
 #, c-format
-msgid "<%s>%s%s  %d,  Hex %02x,  Octal %03o"
-msgstr "<%s>%s%s  %d,  å六进制 %02x,  八进制 %03o"
+msgid ""
+"E474: String \"%.*s\" contains byte that does not start any UTF-8 character"
+msgstr "E474: 字符串 \"%.*s\" 包å«ä¸èµ·å§‹ä»»ä½• UTF-8 字符的字节"
 
-#: ../ex_cmds.c:145
+#: ../eval/encode.c:642
 #, c-format
-msgid "> %d, Hex %04x, Octal %o"
-msgstr "> %d, å六进制 %04x, 八进制 %o"
+msgid ""
+"E474: UTF-8 string contains code point which belongs to a surrogate pair: "
+"%.*s"
+msgstr "E474: UTF-8 字符串包å«å±žäºŽä»£ç†å¯¹çš„ç ç‚¹ï¼š%.*s"
+
+#: ../eval/encode.c:724
+msgid "E474: Unable to convert EXT string to JSON"
+msgstr "E474: 无法将 EXT 字符串转æ¢ä¸º JSON"
 
-#: ../ex_cmds.c:146
+#: ../eval/encode.c:752
 #, c-format
-msgid "> %d, Hex %08x, Octal %o"
-msgstr "> %d, å六进制 %08x, 八进制 %o"
+msgid "E474: Error while dumping %s, %s: attempt to dump function reference"
+msgstr ""
 
-#: ../ex_cmds.c:684
-msgid "E134: Move lines into themselves"
-msgstr "E134: 把行移动到自已中"
+#: ../eval/encode.c:797
+#, fuzzy
+msgid "E474: Invalid key in special dictionary"
+msgstr "E736: 对 Dictionary 无效的æ“作"
 
-#: ../ex_cmds.c:747
-msgid "1 line moved"
-msgstr "移动了 1 行"
+#: ../eval/encode.c:853
+msgid "encode_tv2string() argument"
+msgstr "encode_tv2string() 傿•°"
 
-#: ../ex_cmds.c:749
-#, c-format
-msgid "% lines moved"
-msgstr "移动了 % 行"
+#: ../eval/encode.c:881
+msgid ":echo argument"
+msgstr ":echo 傿•°"
+
+#: ../eval/encode.c:905
+msgid "encode_tv2json() argument"
+msgstr "encode_tv2json() 傿•°"
 
-#: ../ex_cmds.c:1175
+#: ../eval/encode.c:966
 #, c-format
-msgid "% lines filtered"
-msgstr "过滤了 % 行"
+msgid "E5004: Error while dumping %s, %s: attempt to dump function reference"
+msgstr "E5004: dump %s, %s 时出错:试图 dump 函数引用"
 
-#: ../ex_cmds.c:1194
-msgid "E135: *Filter* Autocommands must not change current buffer"
-msgstr "E135: *Filter* 自动命令ä¸å¯ä»¥æ”¹å˜å½“å‰ç¼“冲区"
+#: ../eval/encode.c:1018
+#, c-format
+msgid "E5005: Unable to dump %s: container references itself in %s"
+msgstr "E5005: 无法 dump %s:容器在 %s 中引用了自己"
 
-#: ../ex_cmds.c:1244
-msgid "[No write since last change]\n"
-msgstr "[已修改但尚未ä¿å­˜]\n"
+#: ../eval/executor.c:23
+#, c-format
+msgid "E684: list index out of range: %"
+msgstr "E684: List 索引超出范围: %"
 
-# bad to translate
-#: ../ex_cmds.c:1424
+#: ../eval/funcs.c:147
 #, c-format
-msgid "%sviminfo: %s in line: "
-msgstr "%sviminfo: %s ä½äºŽè¡Œ: "
+msgid "E899: Argument of %s must be a List or Blob"
+msgstr "E899: %s çš„å‚æ•°å¿…须是列表或 Blob"
 
-#: ../ex_cmds.c:1431
-msgid "E136: viminfo: Too many errors, skipping rest of file"
-msgstr "E136: viminfo: 错误过多,忽略文件的剩余部分"
+#: ../eval/funcs.c:148 ../match.c:45
+msgid "E957: Invalid window number"
+msgstr "E957: 无效的窗å£å·"
 
-#: ../ex_cmds.c:1458
+#: ../eval/funcs.c:149
 #, c-format
-msgid "Reading viminfo file \"%s\"%s%s%s"
-msgstr "è¯»å– viminfo 文件 \"%s\"%s%s%s"
+msgid "E998: Reduce of an empty %s with no initial value"
+msgstr "E998: 在没有åˆå§‹å€¼çš„æƒ…况下 reduce 空的 %s"
 
-#: ../ex_cmds.c:1460
-msgid " info"
-msgstr " ä¿¡æ¯"
+#: ../eval/funcs.c:151
+#, c-format
+msgid "E1023: Using a Number as a Bool: %d"
+msgstr "E1023: 将整数作布尔值使用:%d"
 
-#: ../ex_cmds.c:1461
-msgid " marks"
-msgstr " 标记"
+#: ../eval/funcs.c:153
+msgid "E1308: Cannot resize a window in another tab page"
+msgstr "E1308: ä¸èƒ½ä¿®æ”¹å…¶ä»–标签页中窗å£çš„大å°"
 
-#: ../ex_cmds.c:1462
-#, fuzzy
-msgid " oldfiles"
-msgstr "æ²¡æœ‰åŒ…å«æ–‡ä»¶"
+#: ../eval/funcs.c:328 ../eval/funcs.c:7006
+#, c-format
+msgid "Error converting the call result: %s"
+msgstr "转æ¢è°ƒç”¨ç»“果时出错:%s"
 
-#: ../ex_cmds.c:1463
-msgid " FAILED"
-msgstr " 失败"
+#: ../eval/funcs.c:366 ../eval/funcs.c:374
+msgid "add() argument"
+msgstr "add() 傿•°"
 
-#. avoid a wait_return for this message, it's annoying
-#: ../ex_cmds.c:1541
+#: ../eval/funcs.c:679 ../sign.c:1451
 #, c-format
-msgid "E137: Viminfo file is not writable: %s"
-msgstr "E137: Viminfo 文件ä¸å¯å†™å…¥: %s"
+msgid "E158: Invalid buffer name: %s"
+msgstr "E158: ç¼“å†²åŒºåæ— æ•ˆï¼š%s"
 
-#: ../ex_cmds.c:1626
+#: ../eval/funcs.c:814
 #, c-format
-msgid "E138: Can't write viminfo file %s!"
-msgstr "E138: 无法写入 viminfo 文件 %sï¼"
+msgid "Invalid channel stream \"%s\""
+msgstr "channel stream \"%s\" 无效"
 
-#: ../ex_cmds.c:1635
-#, c-format
-msgid "Writing viminfo file \"%s\""
-msgstr "写入 viminfo 文件 \"%s\""
+#: ../eval/funcs.c:1109
+msgid "&Ok"
+msgstr "确定(&O)"
 
-# do not translate to avoid writing Chinese in files
-#. Write the info:
-#: ../ex_cmds.c:1720
-#, fuzzy, c-format
-msgid "# This viminfo file was generated by Vim %s.\n"
-msgstr "# 这个 viminfo 文件是由 Vim %s 生æˆçš„。\n"
+#: ../eval/funcs.c:1239
+msgid "Context stack is empty"
+msgstr "上下文堆栈为空"
 
-# do not translate to avoid writing Chinese in files
-#: ../ex_cmds.c:1722
-#, fuzzy
-msgid ""
-"# You may edit it if you're careful!\n"
-"\n"
-msgstr ""
-"# 如果è¦è‡ªè¡Œä¿®æ”¹è¯·ç‰¹åˆ«å°å¿ƒï¼\n"
-"\n"
+#: ../eval/funcs.c:1483
+msgid "dictwatcheradd() argument"
+msgstr "dictwatcheradd() 傿•°"
 
-# do not translate to avoid writing Chinese in files
-#: ../ex_cmds.c:1723
-#, fuzzy
-msgid "# Value of 'encoding' when this file was written\n"
-msgstr "# 'encoding' 在此文件建立时的值\n"
+#: ../eval/funcs.c:2178
+msgid "E900: maxdepth must be non-negative number"
+msgstr "E900: maxdepth 必须是éžè´Ÿæ•´æ•°"
 
-#: ../ex_cmds.c:1800
-msgid "Illegal starting char"
-msgstr "无效的å¯åŠ¨å­—ç¬¦"
+#: ../eval/funcs.c:2186
+msgid "flatten() argument"
+msgstr "flatten() 傿•°"
 
-#: ../ex_cmds.c:2162
-msgid "Write partial file?"
-msgstr "è¦å†™å…¥éƒ¨åˆ†æ–‡ä»¶å—?"
+#: ../eval/funcs.c:2197 ../eval/typval.c:2458
+msgid "extend() argument"
+msgstr "extend() 傿•°"
 
-#: ../ex_cmds.c:2166
-msgid "E140: Use ! to write partial buffer"
-msgstr "E140: 请使用 ! æ¥å†™å…¥éƒ¨åˆ†ç¼“冲区"
+#: ../eval/funcs.c:2877 ../eval/funcs.c:3891
+msgid "E5000: Cannot find tab number."
+msgstr "E5000: 找ä¸åˆ°æ ‡ç­¾é¡µå·ã€‚"
 
-#: ../ex_cmds.c:2281
-#, c-format
-msgid "Overwrite existing file \"%s\"?"
-msgstr "覆盖已存在的文件 \"%s\" å—?"
+#: ../eval/funcs.c:2885 ../eval/funcs.c:3899
+msgid "E5001: Higher scope cannot be -1 if lower scope is >= 0."
+msgstr "E5001: 低边界 >= 0 时高边界ä¸èƒ½ä¸º -1。"
 
-#: ../ex_cmds.c:2317
-#, c-format
-msgid "Swap file \"%s\" exists, overwrite anyway?"
-msgstr "äº¤æ¢æ–‡ä»¶ \"%s\" 已存在,确实è¦è¦†ç›–å—?"
+#: ../eval/funcs.c:2892 ../eval/funcs.c:3906
+msgid "E5002: Cannot find window number."
+msgstr "E5002: 找ä¸åˆ°çª—å£å·ã€‚"
 
-#: ../ex_cmds.c:2326
-#, c-format
-msgid "E768: Swap file exists: %s (:silent! overrides)"
-msgstr "E768: äº¤æ¢æ–‡ä»¶å·²å­˜åœ¨: %s (:silent! 强制执行)"
+#: ../eval/funcs.c:4120
+msgid "called inputrestore() more often than inputsave()"
+msgstr "inputrestore() 的调用次数多于 inputsave()"
 
-#: ../ex_cmds.c:2381
-#, c-format
-msgid "E141: No file name for buffer %"
-msgstr "E141: 缓冲区 % 没有文件å"
+#: ../eval/funcs.c:4153 ../eval/funcs.c:4190
+msgid "insert() argument"
+msgstr "insert() 傿•°"
 
-#: ../ex_cmds.c:2412
-msgid "E142: File not written: Writing is disabled by 'write' option"
-msgstr "E142: 文件未写入: 写入被 'write' 选项ç¦ç”¨"
+#: ../eval/funcs.c:4260
+msgid "E786: Range not allowed"
+msgstr "E786: ä¸å…许的范围"
+
+#: ../eval/funcs.c:4743
+msgid "E474: Failed to convert list to string"
+msgstr "E474: 无法将列表转æ¢ä¸ºå­—符串"
 
-#: ../ex_cmds.c:2434
+#: ../eval/funcs.c:4760
 #, c-format
-msgid ""
-"'readonly' option is set for \"%s\".\n"
-"Do you wish to write anyway?"
-msgstr ""
-"\"%s\" 已设定 'readonly' 选项。\n"
-"确实è¦è¦†ç›–å—?"
+msgid "E474: Failed to parse %.*s"
+msgstr "E474: æ— æ³•è§£æž %.*s"
 
-#: ../ex_cmds.c:2439
+#: ../eval/funcs.c:4826
+msgid "E701: Invalid type for len()"
+msgstr "E701: len() 的类型无效"
+
+#: ../eval/funcs.c:5348
 #, c-format
-msgid ""
-"File permissions of \"%s\" are read-only.\n"
-"It may still be possible to write it.\n"
-"Do you wish to try?"
-msgstr ""
-"此文件æƒé™ \"%s\" 是åªè¯»çš„。\n"
-"它ä»ç„¶æœ‰å¯èƒ½è¢«å†™å…¥ã€‚\n"
-"你想继续å°è¯•å—?"
+msgid "msgpackdump() argument, index %i"
+msgstr "msgpackdump() 傿•°ï¼Œç´¢å¼• %i"
 
-#: ../ex_cmds.c:2451
-#, fuzzy, c-format
-msgid "E505: \"%s\" is read-only (add ! to override)"
-msgstr "åªè¯» (请加 ! 强制执行)"
+#: ../eval/funcs.c:5517
+msgid "E5070: Character number must not be less than zero"
+msgstr "E5070: 字符数ä¸èƒ½å°äºŽé›¶"
 
-#: ../ex_cmds.c:3120
+#: ../eval/funcs.c:5521
 #, c-format
-msgid "E143: Autocommands unexpectedly deleted new buffer %s"
-msgstr "E143: 自动命令æ„外地删除了新缓冲区 %s"
+msgid "E5071: Character number must not be greater than INT_MAX (%i)"
+msgstr "E5071: 字符数ä¸èƒ½å¤§äºŽ INT_MAX (%i)"
 
-#: ../ex_cmds.c:3313
-msgid "E144: non-numeric argument to :z"
-msgstr "E144: :z 䏿ޥå—éžæ•°å­—çš„å‚æ•°"
+#: ../eval/funcs.c:5907
+msgid "E726: Stride is zero"
+msgstr "E726: 步长为零"
 
-#: ../ex_cmds.c:3498
-msgid "E146: Regular expressions can't be delimited by letters"
-msgstr "E146: 正则表达å¼ä¸èƒ½ç”¨å­—æ¯ä½œåˆ†ç•Œ"
+#: ../eval/funcs.c:5909
+msgid "E727: Start past end"
+msgstr "E727: 起始值在终止值åŽ"
 
-#: ../ex_cmds.c:3964
-#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "替æ¢ä¸º %s (y/n/a/q/l/^E/^Y)?"
+#: ../eval/funcs.c:6006
+msgid ""
+msgstr "<空>"
 
-#: ../ex_cmds.c:4379
-msgid "(Interrupted) "
-msgstr "(已中断) "
+#: ../eval/funcs.c:6329
+msgid "remove() argument"
+msgstr "remove() 傿•°"
 
-#: ../ex_cmds.c:4384
-msgid "1 match"
-msgstr "1 个匹é…,"
+#: ../eval/funcs.c:6447
+msgid "E655: Too many symbolic links (cycle?)"
+msgstr "E655: 符å·è¿žæŽ¥è¿‡å¤š(循环?)"
 
-#: ../ex_cmds.c:4384
-msgid "1 substitution"
-msgstr "1 次替æ¢ï¼Œ"
+#: ../eval/funcs.c:6577
+msgid "reverse() argument"
+msgstr "reverse() 傿•°"
 
-#: ../ex_cmds.c:4387
+#: ../eval/funcs.c:7040
 #, c-format
-msgid "% matches"
-msgstr "% 个匹é…,"
+msgid "E5010: List item %d of the second argument is not a string"
+msgstr "E5010: ç¬¬äºŒä¸ªå‚æ•°çš„列表项 %d 䏿˜¯å­—符串"
 
-#: ../ex_cmds.c:4388
+#: ../eval/funcs.c:7916
 #, c-format
-msgid "% substitutions"
-msgstr "% 次替æ¢ï¼Œ"
-
-#: ../ex_cmds.c:4392
-msgid " on 1 line"
-msgstr "共 1 行"
+msgid "E962: Invalid action: '%s'"
+msgstr "E962: 动作无效:'%s'"
 
-#: ../ex_cmds.c:4395
+#: ../eval/funcs.c:8056
 #, c-format
-msgid " on % lines"
-msgstr "共 % 行"
+msgid "connection failed: %s"
+msgstr "连接失败:%s"
 
-#: ../ex_cmds.c:4438
-msgid "E147: Cannot do :global recursive"
-msgstr "E147: :global ä¸èƒ½é€’归执行"
+#: ../eval/funcs.c:8328
+#, fuzzy, c-format
+msgid "E6100: \"%s\" is not a valid stdpath"
+msgstr "E236: 字体 \"%s\" 䏿˜¯ç­‰å®½å­—体"
 
-#: ../ex_cmds.c:4467
-msgid "E148: Regular expression missing from global"
-msgstr "E148: global 缺少正则表达å¼"
+#: ../eval/funcs.c:8420 ../os/time.c:189
+msgid "(Invalid)"
+msgstr "(无效)"
 
-#: ../ex_cmds.c:4508
+#: ../eval/funcs.c:8774
 #, c-format
-msgid "Pattern found in every line: %s"
-msgstr "æ¯è¡Œéƒ½åŒ¹é…表达å¼: %s"
+msgid "E935: invalid submatch number: %d"
+msgstr "E935: å­åŒ¹é…å·æ— æ•ˆï¼š%d"
 
-#: ../ex_cmds.c:4510
-#, fuzzy, c-format
-msgid "Pattern not found: %s"
-msgstr "找ä¸åˆ°æ¨¡å¼"
+#: ../eval/funcs.c:9213
+msgid "Can only call this function in an unmodified buffer"
+msgstr "åªèƒ½åœ¨æœªä¿®æ”¹çš„缓冲区中调用这个函数"
 
-# do not translate to avoid writing Chinese in files
-#: ../ex_cmds.c:4587
-#, fuzzy
-msgid ""
-"\n"
-"# Last Substitute String:\n"
-"$"
-msgstr ""
-"\n"
-"# 最近的替æ¢å­—符串:\n"
-"$"
+#: ../eval/funcs.c:10026
+msgid "writefile() first argument must be a List or a Blob"
+msgstr "writefile() çš„ç¬¬ä¸€ä¸ªå‚æ•°å¿…须是列表或者 blob"
 
-#: ../ex_cmds.c:4679
-msgid "E478: Don't panic!"
-msgstr "E478: ä¸è¦æ…Œï¼"
+#. Using %s, p and not %c, *p to preserve multibyte characters
+#: ../eval/funcs.c:10053
+#, c-format
+msgid "E5060: Unknown flag: %s"
+msgstr "E5060: 未知的标志:%s"
+
+#: ../eval/funcs.c:10067
+msgid "E482: Can't open file with an empty name"
+msgstr "E482: 无法打开å称为空的文件"
 
-#: ../ex_cmds.c:4717
+#: ../eval/funcs.c:10072
 #, c-format
-msgid "E661: Sorry, no '%s' help for %s"
-msgstr "E661: 抱歉,没有 '%s' 的 %s 的说明"
+msgid "E482: Can't open file %s for writing: %s"
+msgstr ""
 
-#: ../ex_cmds.c:4719
+#: ../eval/funcs.c:10085
 #, c-format
-msgid "E149: Sorry, no help for %s"
-msgstr "E149: 抱歉,没有 %s 的说明"
+msgid "E80: Error when closing file %s: %s"
+msgstr "E80: 关闭文件 %s 时出错: %s"
 
-#: ../ex_cmds.c:4751
+#: ../eval/typval.c:44
 #, c-format
-msgid "Sorry, help file \"%s\" not found"
-msgstr "抱歉,找ä¸åˆ°å¸®åŠ©æ–‡ä»¶ \"%s\""
+msgid "E1174: String required for argument %d"
+msgstr "E1174: 傿•° %d 需è¦å­—符串"
 
-#: ../ex_cmds.c:5323
+#: ../eval/typval.c:46
 #, c-format
-msgid "E150: Not a directory: %s"
-msgstr "E150: 䏿˜¯ç›®å½•: %s"
+msgid "E1175: Non-empty string required for argument %d"
+msgstr "E1175: 傿•° %d 需è¦éžç©ºå­—符串"
 
-#: ../ex_cmds.c:5446
+#: ../eval/typval.c:48
 #, c-format
-msgid "E152: Cannot open %s for writing"
-msgstr "E152: 无法打开并写入 %s"
+msgid "E1210: Number required for argument %d"
+msgstr "E1210: 傿•° %d éœ€è¦æ•´æ•°"
 
-#: ../ex_cmds.c:5471
+#: ../eval/typval.c:50
 #, c-format
-msgid "E153: Unable to open %s for reading"
-msgstr "E153: æ— æ³•æ‰“å¼€å¹¶è¯»å– %s"
+msgid "E1222: String or List required for argument %d"
+msgstr "E1222: 傿•° %d 需è¦å­—符串或列表"
 
-#: ../ex_cmds.c:5500
+#: ../eval/typval.c:76
 #, c-format
-msgid "E670: Mix of help file encodings within a language: %s"
-msgstr "E670: 在一ç§è¯­è¨€ä¸­æ··åˆäº†å¤šç§å¸®åŠ©æ–‡ä»¶ç¼–ç : %s"
+msgid "E5142: Failed to open file %s: %s"
+msgstr "E5142: 无法打开文件 %s:%s"
 
-#: ../ex_cmds.c:5565
+#: ../eval/typval.c:98
 #, c-format
-msgid "E154: Duplicate tag \"%s\" in file %s/%s"
-msgstr "E154: Tag \"%s\" 在文件 %s/%s 中é‡å¤å‡ºçް"
+msgid "E5143: Failed to write to file %s: %s"
+msgstr "E5143: 无法写入文件 %s:%s"
 
-#: ../ex_cmds.c:5687
+#: ../eval/typval.c:109
 #, c-format
-msgid "E160: Unknown sign command: %s"
-msgstr "E160: 未知的 sign 命令: %s"
+msgid "E5144: Failed to close file %s: %s"
+msgstr "E5144: 无法关闭文件 %s:%s"
 
-#: ../ex_cmds.c:5704
-msgid "E156: Missing sign name"
-msgstr "E156: 缺少 sign åç§°"
+#: ../eval/typval.c:1150
+msgid "sort() argument"
+msgstr "sort() 傿•°"
 
-#: ../ex_cmds.c:5746
-msgid "E612: Too many signs defined"
-msgstr "E612: Signs 定义过多"
+#: ../eval/typval.c:1151
+msgid "uniq() argument"
+msgstr "uniq() 傿•°"
 
-#: ../ex_cmds.c:5813
-#, c-format
-msgid "E239: Invalid sign text: %s"
-msgstr "E239: 无效的 sign 文字: %s"
+#: ../eval/typval.c:1242
+msgid "E702: Sort compare function failed"
+msgstr "E702: Sort 比较函数失败"
+
+#: ../eval/typval.c:1265
+msgid "E882: Uniq compare function failed"
+msgstr "E882: Uniq 比较函数失败"
+
+#: ../eval/typval.c:2198
+msgid "E6000: Argument is not a function or function name"
+msgstr ""
 
-#: ../ex_cmds.c:5844 ../ex_cmds.c:6035
+#: ../eval/typval.c:2476
 #, c-format
-msgid "E155: Unknown sign: %s"
-msgstr "E155: 未知的 sign: %s"
+msgid "E737: Key already exists: %s"
+msgstr "E737: 键已存在:%s"
 
-#: ../ex_cmds.c:5877
-msgid "E159: Missing sign number"
-msgstr "E159: 缺少 sign å·"
+#: ../eval/typval.c:3315
+msgid "E743: variable nested too deep for (un)lock"
+msgstr "E743: å˜é‡åµŒå¥—过深,无法é”定/è§£é”"
 
-#: ../ex_cmds.c:5971
+#: ../eval/typval.c:3455
 #, c-format
-msgid "E158: Invalid buffer name: %s"
-msgstr "E158: 无效的缓冲区å: %s"
+msgid "E741: Value is locked: %.*s"
+msgstr "E741: 值已é”定:%.*s"
 
-#: ../ex_cmds.c:6008
+#: ../eval/typval.c:3458
 #, c-format
-msgid "E157: Invalid sign ID: %"
-msgstr "E157: 无效的 sign ID: %"
+msgid "E742: Cannot change value of %.*s"
+msgstr "E742: æ— æ³•æ”¹å˜ %.*s 的值"
 
-#: ../ex_cmds.c:6066
-msgid " (not supported)"
-msgstr " (䏿”¯æŒ)"
+#: ../eval/typval.c:3464 ../message.c:2469
+msgid "Unknown"
+msgstr "未知"
 
-#: ../ex_cmds.c:6169
-msgid "[Deleted]"
-msgstr "[已删除]"
+#: ../eval/typval.c:3591
+msgid "E805: Expected a Number or a String, Float found"
+msgstr "E805: éœ€è¦æ•´æ•°æˆ–字符串,但是æä¾›äº†æµ®ç‚¹æ•°"
 
-#: ../ex_cmds2.c:139
-msgid "Entering Debug mode.  Type \"cont\" to continue."
-msgstr "进入调试模å¼ã€‚输入 \"cont\" ç»§ç»­è¿è¡Œã€‚"
+#: ../eval/typval.c:3595
+msgid "E703: Expected a Number or a String, Funcref found"
+msgstr "E703: éœ€è¦æ•´æ•°æˆ–字符串,但是æä¾›äº† Funcref"
 
-#: ../ex_cmds2.c:143 ../ex_docmd.c:759
-#, c-format
-msgid "line %: %s"
-msgstr "第 % 行: %s"
+#: ../eval/typval.c:3598
+msgid "E745: Expected a Number or a String, List found"
+msgstr "E745: éœ€è¦æ•´æ•°æˆ–字符串,但是æä¾›äº†åˆ—表"
 
-#: ../ex_cmds2.c:145
-#, c-format
-msgid "cmd: %s"
-msgstr "命令: %s"
+#: ../eval/typval.c:3601
+msgid "E728: Expected a Number or a String, Dictionary found"
+msgstr "E728: éœ€è¦æ•´æ•°æˆ–字符串,但是æä¾›äº†å­—å…¸"
 
-#: ../ex_cmds2.c:322
-#, c-format
-msgid "Breakpoint in \"%s%s\" line %"
-msgstr "断点 \"%s%s\" 第 % 行"
+#: ../eval/typval.c:3604
+msgid "E974: Expected a Number or a String, Blob found"
+msgstr "E974: éœ€è¦æ•´æ•°æˆ–字符串,但是æä¾›äº† Blob"
 
-#: ../ex_cmds2.c:581
-#, c-format
-msgid "E161: Breakpoint not found: %s"
-msgstr "E161: 找ä¸åˆ°æ–­ç‚¹: %s"
+#: ../eval/typval.c:3607
+msgid "E5299: Expected a Number or a String, Boolean found"
+msgstr "E5299: éœ€è¦æ•´æ•°æˆ–字符串,但是æä¾›äº†å¸ƒå°”值"
 
-#: ../ex_cmds2.c:611
-msgid "No breakpoints defined"
-msgstr "没有定义断点"
+#: ../eval/typval.c:3610
+msgid "E5300: Expected a Number or a String"
+msgstr "E5300: éœ€è¦æ•´æ•°æˆ–字符串"
 
-#: ../ex_cmds2.c:617
-#, c-format
-msgid "%3d  %s %s  line %"
-msgstr "%3d  %s %s  第 % 行"
+#: ../eval/typval.c:3625
+msgid "E745: Using a List as a Number"
+msgstr "E745: 将列表当作整数使用"
 
-#: ../ex_cmds2.c:942
-#, fuzzy
-msgid "E750: First use \":profile start {fname}\""
-msgstr "E750: 请先使用 :profile start "
+#: ../eval/typval.c:3626
+msgid "E728: Using a Dictionary as a Number"
+msgstr "E728: 将字典当作整数使用"
 
-#: ../ex_cmds2.c:1269
-#, c-format
-msgid "Save changes to \"%s\"?"
-msgstr "将改å˜ä¿å­˜åˆ° \"%s\" å—?"
+#: ../eval/typval.c:3627
+msgid "E805: Using a Float as a Number"
+msgstr "E805: 将浮点数当作整数使用"
 
-#: ../ex_cmds2.c:1271 ../ex_docmd.c:8851
-msgid "Untitled"
-msgstr "未命å"
+#: ../eval/typval.c:3628
+msgid "E974: Using a Blob as a Number"
+msgstr "E974: 将 Blob 当作整数使用"
 
-#: ../ex_cmds2.c:1421
-#, c-format
-msgid "E162: No write since last change for buffer \"%s\""
-msgstr "E162: 缓冲区 \"%s\" 已修改但尚未ä¿å­˜"
+#: ../eval/typval.c:3629
+msgid "E685: using an invalid value as a Number"
+msgstr "E685: 将无效值当作整数使用"
 
-#: ../ex_cmds2.c:1480
-msgid "Warning: Entered other buffer unexpectedly (check autocommands)"
-msgstr "警告: æ„外地进入了其它缓冲区 (请检查自动命令)"
+#: ../eval/typval.c:3670
+msgid "E730: using List as a String"
+msgstr "E730: 将列表当作字符串使用"
 
-#: ../ex_cmds2.c:1826
-msgid "E163: There is only one file to edit"
-msgstr "E163: åªæœ‰ä¸€ä¸ªæ–‡ä»¶å¯ç¼–辑"
+#: ../eval/typval.c:3671
+msgid "E731: using Dictionary as a String"
+msgstr "E731: 将字典当作字符串使用"
 
-#: ../ex_cmds2.c:1828
-msgid "E164: Cannot go before first file"
-msgstr "E164: 无法切æ¢ï¼Œå·²æ˜¯ç¬¬ä¸€ä¸ªæ–‡ä»¶"
+#: ../eval/typval.c:3673
+msgid "E976: using Blob as a String"
+msgstr "E976: 将 Blob 当作字符串使用"
 
-#: ../ex_cmds2.c:1830
-msgid "E165: Cannot go beyond last file"
-msgstr "E165: 无法切æ¢ï¼Œå·²æ˜¯æœ€åŽä¸€ä¸ªæ–‡ä»¶"
+#: ../eval/typval.c:3674
+msgid "E908: using an invalid value as a String"
+msgstr "E908: 将无效值当作字符串使用"
 
-#: ../ex_cmds2.c:2175
-#, c-format
-msgid "E666: compiler not supported: %s"
-msgstr "E666: 䏿”¯æŒç¼–译器: %s"
+#: ../eval/typval.c:3815
+msgid "E891: Using a Funcref as a Float"
+msgstr "E891: 将 Funcref 当作浮点数使用"
 
-#: ../ex_cmds2.c:2257
-#, c-format
-msgid "Searching for \"%s\" in \"%s\""
-msgstr "正在查找 \"%s\",在 \"%s\" 中"
+#: ../eval/typval.c:3818
+msgid "E892: Using a String as a Float"
+msgstr "E892: 将字符串当作浮点数使用"
 
-#: ../ex_cmds2.c:2284
-#, c-format
-msgid "Searching for \"%s\""
-msgstr "正在查找 \"%s\""
+#: ../eval/typval.c:3821
+msgid "E893: Using a List as a Float"
+msgstr "E893: 将列表当作浮点数使用"
 
-#: ../ex_cmds2.c:2307
-#, c-format
-msgid "not found in 'runtimepath': \"%s\""
-msgstr "在 'runtimepath' 中找ä¸åˆ° \"%s\""
+#: ../eval/typval.c:3824
+msgid "E894: Using a Dictionary as a Float"
+msgstr "E894: 将字典当作浮点数使用"
 
-#: ../ex_cmds2.c:2472
-#, c-format
-msgid "Cannot source a directory: \"%s\""
-msgstr "ä¸èƒ½æ‰§è¡Œç›®å½•: \"%s\""
+#: ../eval/typval.c:3827
+msgid "E362: Using a boolean value as a Float"
+msgstr "E362: 将布尔值当作浮点数使用"
 
-#: ../ex_cmds2.c:2518
-#, c-format
-msgid "could not source \"%s\""
-msgstr "ä¸èƒ½æ‰§è¡Œ \"%s\""
+#: ../eval/typval.c:3830
+msgid "E907: Using a special value as a Float"
+msgstr "E907: 将特殊值当作浮点数使用"
 
-#: ../ex_cmds2.c:2520
+#: ../eval/typval.c:3833
+msgid "E975: Using a Blob as a Float"
+msgstr "E975: 将 Blob 当作浮点数使用"
+
+#: ../eval/typval.h:515
+msgid "E808: Number or Float required"
+msgstr "E808: éœ€è¦æ•´æ•°æˆ–浮点数"
+
+#: ../eval/userfunc.c:67
 #, c-format
-msgid "line %: could not source \"%s\""
-msgstr "第 % 行: ä¸èƒ½æ‰§è¡Œ \"%s\""
+msgid "E122: Function %s already exists, add ! to replace it"
+msgstr "E122: 函数 %s 已存在,请加 ! 强制替æ¢"
+
+#: ../eval/userfunc.c:68
+msgid "E717: Dictionary entry already exists"
+msgstr "E717: 字典项已存在"
+
+#: ../eval/userfunc.c:69
+msgid "E718: Funcref required"
+msgstr "E718: éœ€è¦ Funcref"
 
-#: ../ex_cmds2.c:2535
+#: ../eval/userfunc.c:70
 #, c-format
-msgid "sourcing \"%s\""
-msgstr "执行 \"%s\""
+msgid "E130: Unknown function: %s"
+msgstr "E130: 函数未知: %s"
 
-#: ../ex_cmds2.c:2537
+#: ../eval/userfunc.c:72
 #, c-format
-msgid "line %: sourcing \"%s\""
-msgstr "第 % 行: 执行 \"%s\""
+msgid "E1068: No white space allowed before '%s': %s"
+msgstr "E1068: '%s' å‰ä¸å…许有空白:%s"
 
-#: ../ex_cmds2.c:2693
+#: ../eval/userfunc.c:124
 #, c-format
-msgid "finished sourcing %s"
-msgstr "ç»“æŸæ‰§è¡Œ %s"
+msgid "E125: Illegal argument: %s"
+msgstr "E125: 傿•°æ— æ•ˆ: %s"
 
-#: ../ex_cmds2.c:2765
-msgid "modeline"
-msgstr "modeline"
+#: ../eval/userfunc.c:137
+#, c-format
+msgid "E853: Duplicate argument name: %s"
+msgstr "E853: 傿•°åé‡å¤ï¼š%s"
 
-#: ../ex_cmds2.c:2767
-msgid "--cmd argument"
-msgstr "--cmd 傿•°"
+#: ../eval/userfunc.c:171
+msgid "E989: Non-default argument follows default argument"
+msgstr "E989: é»˜è®¤å‚æ•°åŽæœ‰éžé»˜è®¤å‚æ•°"
 
-#: ../ex_cmds2.c:2769
-msgid "-c argument"
-msgstr "-c 傿•°"
+#: ../eval/userfunc.c:498
+#, c-format
+msgid "E740: Too many arguments for function %s"
+msgstr "E740: 函数 %s çš„å‚æ•°è¿‡å¤š"
 
-#: ../ex_cmds2.c:2771
-msgid "environment variable"
-msgstr "环境å˜é‡"
+#: ../eval/userfunc.c:500
+#, c-format
+msgid "E116: Invalid arguments for function %s"
+msgstr "E116: 函数 %s çš„å‚æ•°æ— æ•ˆ"
 
-#: ../ex_cmds2.c:2773
-msgid "error handler"
-msgstr "错误的处ç†ç¨‹åº"
+#: ../eval/userfunc.c:859
+msgid "E132: Function call depth is higher than 'maxfuncdepth'"
+msgstr "E132: 函数调用深度超出 'maxfuncdepth'"
 
-#: ../ex_cmds2.c:3020
-msgid "W15: Warning: Wrong line separator, ^M may be missing"
-msgstr "W15: 警告: 错误的行分隔符,å¯èƒ½æ˜¯å°‘了 ^M"
+#: ../eval/userfunc.c:1039
+#, c-format
+msgid "calling %s"
+msgstr "调用 %s"
 
-#: ../ex_cmds2.c:3139
-msgid "E167: :scriptencoding used outside of a sourced file"
-msgstr "E167: 在脚本文件外使用了 :scriptencoding"
+#: ../eval/userfunc.c:1154
+#, c-format
+msgid "%s aborted"
+msgstr "%s 已中止"
 
-#: ../ex_cmds2.c:3166
-msgid "E168: :finish used outside of a sourced file"
-msgstr "E168: 在脚本文件外使用了 :finish"
+#: ../eval/userfunc.c:1156
+#, c-format
+msgid "%s returning #%"
+msgstr "%s 返回 #% "
 
-#: ../ex_cmds2.c:3389
+#: ../eval/userfunc.c:1173
 #, c-format
-msgid "Current %slanguage: \"%s\""
-msgstr "当å‰çš„ %s语言: \"%s\""
+msgid "%s returning %s"
+msgstr "%s 返回 %s"
 
-#: ../ex_cmds2.c:3404
+#: ../eval/userfunc.c:1196 ../runtime.c:2083
 #, c-format
-msgid "E197: Cannot set language to \"%s\""
-msgstr "E197: ä¸èƒ½è®¾å®šè¯­è¨€ä¸º \"%s\""
+msgid "continuing in %s"
+msgstr "在 %s 中继续"
 
-#. don't redisplay the window
-#. don't wait for return
-#: ../ex_docmd.c:387
-msgid "Entering Ex mode.  Type \"visual\" to go to Normal mode."
-msgstr "进入 Ex 模å¼ã€‚输入 \"visual\" 回到正常模å¼ã€‚"
+#: ../eval/userfunc.c:1391
+msgid "E699: Too many arguments"
+msgstr "E699: 傿•°è¿‡å¤š"
 
-#: ../ex_docmd.c:428
-msgid "E501: At end-of-file"
-msgstr "E501: 已到文件末尾"
+#: ../eval/userfunc.c:1441
+#, c-format
+msgid "E117: Unknown function: %s"
+msgstr "E117: 未知的函数:%s"
 
-#: ../ex_docmd.c:513
-msgid "E169: Command too recursive"
-msgstr "E169: 命令递归层数过多"
+#: ../eval/userfunc.c:1444
+#, c-format
+msgid "E276: Cannot use function as a method: %s"
+msgstr "E276: ä¸èƒ½å°†å‡½æ•°ç”¨ä½œæ–¹æ³•:%s"
 
-#: ../ex_docmd.c:1006
+#: ../eval/userfunc.c:1447
 #, c-format
-msgid "E605: Exception not caught: %s"
-msgstr "E605: 异常没有被æ•获: %s"
+msgid "E933: Function was deleted: %s"
+msgstr "E933: 函数已被删除:%s"
 
-#: ../ex_docmd.c:1085
-msgid "End of sourced file"
-msgstr "脚本文件结æŸ"
+#: ../eval/userfunc.c:1453
+#, c-format
+msgid "E119: Not enough arguments for function: %s"
+msgstr "E119: 函数 %s çš„å‚æ•°ä¸è¶³"
 
-#: ../ex_docmd.c:1086
-msgid "End of function"
-msgstr "函数结æŸ"
+#: ../eval/userfunc.c:1457
+#, c-format
+msgid "E120: Using  not in a script context: %s"
+msgstr "E120:  ä¸èƒ½åœ¨ script 上下文外使用:%s"
 
-#: ../ex_docmd.c:1628
-msgid "E464: Ambiguous use of user-defined command"
-msgstr "E464: ä¸ç¡®å®šçš„用户自定义命令"
+#: ../eval/userfunc.c:1461
+#, c-format
+msgid "E725: Calling dict function without Dictionary: %s"
+msgstr "E725: 调用字典函数但是没有字典:%s"
 
-#: ../ex_docmd.c:1638
-msgid "E492: Not an editor command"
-msgstr "E492: 䏿˜¯ç¼–辑器的命令"
+#: ../eval/userfunc.c:1759
+msgid "E129: Function name required"
+msgstr "E129: 需è¦å‡½æ•°å"
 
-#: ../ex_docmd.c:1729
-msgid "E493: Backwards range given"
-msgstr "E493: 使用了逆å‘的范围"
+#: ../eval/userfunc.c:1895
+#, c-format
+msgid "E128: Function name must start with a capital or \"s:\": %s"
+msgstr "E128: 函数åå¿…é¡»ä»¥å¤§å†™å­—æ¯æˆ– \"s:\" 开头:%s"
 
-#: ../ex_docmd.c:1733
-msgid "Backwards range given, OK to swap"
-msgstr "使用了逆å‘的范围,确定交æ¢å—"
+#: ../eval/userfunc.c:1904
+#, c-format
+msgid "E884: Function name cannot contain a colon: %s"
+msgstr "E884: 函数åä¸èƒ½åŒ…å«å†’å·ï¼š%s"
 
-#. append
-#. typed wrong
-#: ../ex_docmd.c:1787
-msgid "E494: Use w or w>>"
-msgstr "E494: 请使用 w 或 w>>"
+#: ../eval/userfunc.c:1972
+msgid "E454: function list was modified"
+msgstr "E454: 函数列表已被修改"
 
-#: ../ex_docmd.c:3454
-msgid "E319: The command is not available in this version"
-msgstr "E319: 抱歉,命令在此版本中ä¸å¯ç”¨"
+#: ../eval/userfunc.c:2125
+#, c-format
+msgid "E123: Undefined function: %s"
+msgstr "E123: 函数未定义:%s"
 
-#: ../ex_docmd.c:3752
-msgid "E172: Only one file name allowed"
-msgstr "E172: åªå…许一个文件å"
+#: ../eval/userfunc.c:2135
+#, c-format
+msgid "E124: Missing '(': %s"
+msgstr "E124: 缺少 '(':%s"
 
-#: ../ex_docmd.c:4238
-msgid "1 more file to edit.  Quit anyway?"
-msgstr "还有 1 个文件未编辑。确实è¦é€€å‡ºå—?"
+#: ../eval/userfunc.c:2167
+msgid "E862: Cannot use g: here"
+msgstr "E862: 此处ä¸èƒ½ä½¿ç”¨ g:"
 
-#: ../ex_docmd.c:4242
+#: ../eval/userfunc.c:2197
 #, c-format
-msgid "%d more files to edit.  Quit anyway?"
-msgstr "还有 %d 个文件未编辑。确实è¦é€€å‡ºå—?"
+msgid "E932: Closure function should not be at top level: %s"
+msgstr "E932: 闭包函数ä¸èƒ½ä½äºŽé¡¶å±‚:%s"
 
-#: ../ex_docmd.c:4248
-msgid "E173: 1 more file to edit"
-msgstr "E173: 还有 1 个文件未编辑"
+#: ../eval/userfunc.c:2272
+msgid "E126: Missing :endfunction"
+msgstr "E126: 缺少 :endfunction"
 
-#: ../ex_docmd.c:4250
+#: ../eval/userfunc.c:2327
 #, c-format
-msgid "E173: % more files to edit"
-msgstr "E173: 还有 % 个文件未编辑"
+msgid "W22: Text found after :endfunction: %s"
+msgstr "W22: :endfunction åŽæœ‰æ–‡æœ¬ï¼š%s"
 
-#: ../ex_docmd.c:4320
-msgid "E174: Command already exists: add ! to replace it"
-msgstr "E174: 命令已存在: 请加 ! 强制替æ¢"
+#: ../eval/userfunc.c:2363
+msgid "E1058: function nesting too deep"
+msgstr "E1058: 函数嵌套层数过深"
 
-#: ../ex_docmd.c:4432
-msgid ""
-"\n"
-"    Name        Args Range Complete  Definition"
-msgstr ""
-"\n"
-"    åç§°        傿•° 范围  补全      定义      "
+#: ../eval/userfunc.c:2472
+#, c-format
+msgid "E707: Function name conflicts with variable: %s"
+msgstr "E707: 函数å与å˜é‡å冲çªï¼š%s"
 
-#: ../ex_docmd.c:4516
-msgid "No user-defined commands found"
-msgstr "找ä¸åˆ°ç”¨æˆ·è‡ªå®šä¹‰å‘½ä»¤"
+#: ../eval/userfunc.c:2488
+#, c-format
+msgid "E127: Cannot redefine function %s: It is in use"
+msgstr "E127: 函数 %s 正在使用中,ä¸èƒ½é‡æ–°å®šä¹‰"
 
-#: ../ex_docmd.c:4538
-msgid "E175: No attribute specified"
-msgstr "E175: 没有指定属性"
+#: ../eval/userfunc.c:2555
+#, c-format
+msgid "E746: Function name does not match script file name: %s"
+msgstr "E746: 函数å与脚本文件åä¸åŒ¹é…: %s"
 
-#: ../ex_docmd.c:4583
-msgid "E176: Invalid number of arguments"
-msgstr "E176: æ— æ•ˆçš„å‚æ•°ä¸ªæ•°"
+#: ../eval/userfunc.c:2778
+#, c-format
+msgid "E131: Cannot delete function %s: It is in use"
+msgstr "E131: 函数 %s 正在使用中,ä¸èƒ½åˆ é™¤"
 
-#: ../ex_docmd.c:4594
-msgid "E177: Count cannot be specified twice"
-msgstr "E177: ä¸èƒ½æŒ‡å®šä¸¤æ¬¡è®¡æ•°"
+#: ../eval/userfunc.c:2784
+#, c-format
+msgid "Cannot delete function %s: It is being used internally"
+msgstr "无法删除函数 %s,内部使用这个函数"
 
-#: ../ex_docmd.c:4603
-msgid "E178: Invalid default value for count"
-msgstr "E178: 无效的计数默认值"
+#: ../eval/userfunc.c:2916
+msgid "E133: :return not inside a function"
+msgstr "E133: :return 在函数外"
 
-#: ../ex_docmd.c:4625
-msgid "E179: argument required for -complete"
-msgstr "E179: -complete 需è¦å‚æ•°"
+#. TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead
+#. maximum nesting of lists and dicts
+#: ../eval/vars.c:52
+msgid "E18: Unexpected characters in :let"
+msgstr "E18: :let 中出现异常字符"
 
-#: ../ex_docmd.c:4635
+#: ../eval/vars.c:53
 #, c-format
-msgid "E181: Invalid attribute: %s"
-msgstr "E181: 无效的属性: %s"
+msgid "E940: Cannot lock or unlock variable %s"
+msgstr "E940: ä¸èƒ½é”定或解é”å˜é‡ %s"
 
-#: ../ex_docmd.c:4678
-msgid "E182: Invalid command name"
-msgstr "E182: 无效的命令å"
+#: ../eval/vars.c:77
+msgid "E991: cannot use =<< here"
+msgstr "E991: 此处ä¸èƒ½ä½¿ç”¨ =<<"
 
-#: ../ex_docmd.c:4691
-msgid "E183: User defined commands must start with an uppercase letter"
-msgstr "E183: 用户自定义命令必须以大写字æ¯å¼€å¤´"
+#: ../eval/vars.c:109
+msgid "E221: Marker cannot start with lower case letter"
+msgstr "E221: 标记ä¸èƒ½ä»¥å°å†™å­—æ¯å¼€å¤´"
 
-#: ../ex_docmd.c:4696
-#, fuzzy
-msgid "E841: Reserved name, cannot be used for user defined command"
-msgstr "E464: ä¸ç¡®å®šçš„用户自定义命令"
+#: ../eval/vars.c:113
+msgid "E172: Missing marker"
+msgstr "E172: 缺少标记"
 
-#: ../ex_docmd.c:4751
+#: ../eval/vars.c:124
 #, c-format
-msgid "E184: No such user-defined command: %s"
-msgstr "E184: 没有这个用户自定义命令: %s"
+msgid "E990: Missing end marker '%s'"
+msgstr "E990: ç¼ºå°‘ç»“æŸæ ‡è®° '%s'"
 
-#: ../ex_docmd.c:5219
-#, c-format
-msgid "E180: Invalid complete value: %s"
-msgstr "E180: 无效的补全类型: %s"
+#: ../eval/vars.c:311
+msgid "E687: Less targets than List items"
+msgstr "E687: 目标比 List 项数少"
 
-#: ../ex_docmd.c:5225
-msgid "E468: Completion argument only allowed for custom completion"
-msgstr "E468: åªæœ‰ custom 补全æ‰å…è®¸å‚æ•°"
+#: ../eval/vars.c:315
+msgid "E688: More targets than List items"
+msgstr "E688: 目标比 List 项数多"
 
-#: ../ex_docmd.c:5231
-msgid "E467: Custom completion requires a function argument"
-msgstr "E467: Custom 补全需è¦ä¸€ä¸ªå‡½æ•°å‚æ•°"
+#: ../eval/vars.c:391
+msgid "E452: Double ; in list of variables"
+msgstr "E452: å˜é‡åˆ—表中有两个 ;"
 
-#: ../ex_docmd.c:5257
-#, fuzzy, c-format
-msgid "E185: Cannot find color scheme '%s'"
-msgstr "E185: 找ä¸åˆ°é…色方案 %s"
+#: ../eval/vars.c:532
+#, c-format
+msgid "E738: Can't list variables for %s"
+msgstr "E738: 无法列出 %s çš„å˜é‡"
 
-#: ../ex_docmd.c:5263
-msgid "Greetings, Vim user!"
-msgstr "您好,Vim 用户ï¼"
+#: ../eval/vars.c:584
+msgid "E996: Cannot lock an environment variable"
+msgstr "E996: ä¸èƒ½é”定环境å˜é‡"
 
-#: ../ex_docmd.c:5431
-msgid "E784: Cannot close last tab page"
-msgstr "E784: ä¸èƒ½å…³é—­æœ€åŽä¸€ä¸ª tab 页"
+#: ../eval/vars.c:625
+msgid "E996: Cannot lock an option"
+msgstr "E996: ä¸èƒ½é”定选项"
 
-#: ../ex_docmd.c:5462
-msgid "Already only one tab page"
-msgstr "å·²ç»åªå‰©ä¸€ä¸ª tab 页了"
+#: ../eval/vars.c:716
+msgid "E996: Cannot lock a register"
+msgstr "E996: ä¸èƒ½é”定寄存器"
 
-#: ../ex_docmd.c:6004
+#: ../eval/vars.c:1015
 #, c-format
-msgid "Tab page %d"
-msgstr "Tab 页 %d"
+msgid "E108: No such variable: \"%s\""
+msgstr "E108: æ— æ­¤å˜é‡ï¼š\"%s\""
 
-#: ../ex_docmd.c:6295
-msgid "No swap file"
-msgstr "æ— äº¤æ¢æ–‡ä»¶"
+#: ../eval/vars.c:1331
+#, c-format
+msgid "E963: setting %s to value with wrong type"
+msgstr "E963: 将 %s 设置为类型错误的值"
 
-#: ../ex_docmd.c:6478
-msgid "E747: Cannot change directory, buffer is modified (add ! to override)"
-msgstr "E747: ä¸èƒ½æ”¹å˜ç›®å½•,缓冲区已修改 (请加 ! 强制执行)"
+#: ../eval/vars.c:1414
+#, c-format
+msgid "E794: Cannot set variable in the sandbox: \"%.*s\""
+msgstr "E794: ä¸èƒ½åœ¨æ²™ç›’中设置å˜é‡ï¼š\"%.*s\""
 
-#: ../ex_docmd.c:6485
-msgid "E186: No previous directory"
-msgstr "E186: å‰ä¸€ä¸ªç›®å½•ä¸å­˜åœ¨"
+#: ../eval/vars.c:1460
+#, c-format
+msgid "E795: Cannot delete variable %.*s"
+msgstr "E795: 无法删除å˜é‡ %.*s"
 
-#: ../ex_docmd.c:6530
-msgid "E187: Unknown"
-msgstr "E187: 未知"
+#: ../eval/vars.c:1483
+#, c-format
+msgid "E704: Funcref variable name must start with a capital: %s"
+msgstr "E704: Funcref å˜é‡å必须以大写字æ¯å¼€å¤´: %s"
 
-#: ../ex_docmd.c:6610
-msgid "E465: :winsize requires two number arguments"
-msgstr "E465: :winsize 需è¦ä¸¤ä¸ªæ•°å­—傿•°"
+#: ../eval/vars.c:1490
+#, c-format
+msgid "E705: Variable name conflicts with existing function: %s"
+msgstr "E705: å˜é‡å与已有函数å冲çª: %s"
+
+#: ../event/socket.c:220
+msgid "tcp address must be host:port"
+msgstr "TCP 地å€å¿…须为 host:port"
 
-#: ../ex_docmd.c:6655
-msgid "E188: Obtaining window position not implemented for this platform"
-msgstr "E188: 在此平å°ä¸Šä¸èƒ½èŽ·å¾—çª—å£ä½ç½®"
+#: ../event/socket.c:231
+msgid "failed to lookup host or port"
+msgstr "无法查找主机或端å£"
 
-#: ../ex_docmd.c:6662
-msgid "E466: :winpos requires two number arguments"
-msgstr "E466: :winpos 需è¦ä¸¤ä¸ªæ•°å­—傿•°"
+#: ../event/socket.c:256
+msgid "connection refused"
+msgstr "连接被拒ç»"
 
-#: ../ex_docmd.c:7241
+#: ../ex_cmds.c:164
 #, c-format
-msgid "E739: Cannot create directory: %s"
-msgstr "E739: 无法创建目录: %s"
+msgid "<%s>%s%s  %d,  Hex %02x,  Oct %03o, Digr %s"
+msgstr "<%s>%s%s  %d,  å六进制 %02x,  八进制 %03o, 二åˆå­—符 %s"
 
-#: ../ex_docmd.c:7268
+#: ../ex_cmds.c:169
 #, c-format
-msgid "E189: \"%s\" exists (add ! to override)"
-msgstr "E189: \"%s\" 已存在 (请加 ! 强制执行)"
+msgid "<%s>%s%s  %d,  Hex %02x,  Octal %03o"
+msgstr "<%s>%s%s  %d,  å六进制 %02x,  八进制 %03o"
 
-#: ../ex_docmd.c:7273
+#: ../ex_cmds.c:213
 #, c-format
-msgid "E190: Cannot open \"%s\" for writing"
-msgstr "E190: 无法打开并写入 \"%s\""
+msgid "> %d, Hex %04x, Oct %o, Digr %s"
+msgstr "> %d, å六进制 %04x, 八进制 %o, 二åˆå­—符 %s"
+
+#: ../ex_cmds.c:214
+#, c-format
+msgid "> %d, Hex %08x, Oct %o, Digr %s"
+msgstr "> %d, å六进制 %08x, 八进制 %o, 二åˆå­—符 %s"
+
+#: ../ex_cmds.c:220
+#, c-format
+msgid "> %d, Hex %04x, Octal %o"
+msgstr "> %d, å六进制 %04x, 八进制 %o"
+
+#: ../ex_cmds.c:221
+#, c-format
+msgid "> %d, Hex %08x, Octal %o"
+msgstr "> %d, å六进制 %08x, 八进制 %o"
+
+#: ../ex_cmds.c:914
+msgid "E134: Cannot move a range of lines into itself"
+msgstr "E134: ä¸èƒ½æŠŠä¸€ç³»åˆ—行移动到自身当中"
+
+#: ../ex_cmds.c:1021
+#, c-format
+msgid "1 line moved"
+msgid_plural "% lines moved"
+msgstr[0] "移动了 % 行"
+
+#. will call wait_return()
+#: ../ex_cmds.c:1335
+#, c-format
+msgid "E482: Can't create file %s"
+msgstr "E482: 无法创建文件 %s"
+
+#: ../ex_cmds.c:1436
+#, c-format
+msgid "% lines filtered"
+msgstr "过滤了 % 行"
+
+#: ../ex_cmds.c:1458
+msgid "E135: *Filter* Autocommands must not change current buffer"
+msgstr "E135: *Filter* 自动命令ä¸å¯ä»¥æ”¹å˜å½“å‰ç¼“冲区"
+
+#: ../ex_cmds.c:1498
+msgid "[No write since last change]\n"
+msgstr "[已修改但尚未ä¿å­˜]\n"
+
+#: ../ex_cmds.c:1793
+#, c-format
+msgid "E503: \"%s\" is not a file or writable device"
+msgstr "E503: \"%s\" 䏿˜¯æ–‡ä»¶æˆ–å¯å†™çš„设备"
+
+#: ../ex_cmds.c:1877
+msgid "Write partial file?"
+msgstr "è¦å†™å…¥éƒ¨åˆ†æ–‡ä»¶å—?"
+
+#: ../ex_cmds.c:1882
+msgid "E140: Use ! to write partial buffer"
+msgstr "E140: 请使用 ! æ¥å†™å…¥éƒ¨åˆ†ç¼“冲区"
+
+#: ../ex_cmds.c:2006
+#, c-format
+msgid "Overwrite existing file \"%s\"?"
+msgstr "覆盖已存在的文件 \"%s\" å—?"
+
+#: ../ex_cmds.c:2043
+#, c-format
+msgid "Swap file \"%s\" exists, overwrite anyway?"
+msgstr "äº¤æ¢æ–‡ä»¶ \"%s\" 已存在,确实è¦è¦†ç›–å—?"
+
+#: ../ex_cmds.c:2052
+#, c-format
+msgid "E768: Swap file exists: %s (:silent! overrides)"
+msgstr "E768: äº¤æ¢æ–‡ä»¶å·²å­˜åœ¨: %s (:silent! 强制执行)"
+
+#: ../ex_cmds.c:2110
+#, c-format
+msgid "E141: No file name for buffer %"
+msgstr "E141: 缓冲区 % 没有文件å"
+
+#: ../ex_cmds.c:2144
+msgid "E142: File not written: Writing is disabled by 'write' option"
+msgstr "E142: 文件未写入: 写入被 'write' 选项ç¦ç”¨"
+
+#: ../ex_cmds.c:2163
+#, c-format
+msgid ""
+"'readonly' option is set for \"%s\".\n"
+"Do you wish to write anyway?"
+msgstr ""
+"\"%s\" 已设定 'readonly' 选项。\n"
+"确实è¦è¦†ç›–å—?"
+
+#: ../ex_cmds.c:2167
+#, c-format
+msgid ""
+"File permissions of \"%s\" are read-only.\n"
+"It may still be possible to write it.\n"
+"Do you wish to try?"
+msgstr ""
+"此文件æƒé™ \"%s\" 是åªè¯»çš„。\n"
+"它ä»ç„¶æœ‰å¯èƒ½è¢«å†™å…¥ã€‚\n"
+"你想继续å°è¯•å—?"
+
+#: ../ex_cmds.c:2181
+#, c-format
+msgid "E505: \"%s\" is read-only (add ! to override)"
+msgstr "E505: \"%s\" åªè¯» (请加 ! 强制执行)"
+
+#: ../ex_cmds.c:2902
+#, c-format
+msgid "E143: Autocommands unexpectedly deleted new buffer %s"
+msgstr "E143: 自动命令æ„外地删除了新缓冲区 %s"
+
+#: ../ex_cmds.c:3118
+msgid "E144: non-numeric argument to :z"
+msgstr "E144: :z 䏿ޥå—éžæ•°å­—çš„å‚æ•°"
+
+#: ../ex_cmds.c:3429
+msgid "E146: Regular expressions can't be delimited by letters"
+msgstr "E146: 正则表达å¼ä¸èƒ½ç”¨å­—æ¯ä½œåˆ†ç•Œ"
+
+#. Same highlight as wait_return().
+#: ../ex_cmds.c:3952
+#, c-format
+msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
+msgstr "替æ¢ä¸º %s (y/n/a/q/l/^E/^Y)?"
+
+#: ../ex_cmds.c:4462
+msgid "(Interrupted) "
+msgstr "(已中断) "
+
+#: ../ex_cmds.c:4468
+#, fuzzy, c-format
+msgid "% match on % line"
+msgid_plural "% matches on % line"
+msgstr[0] "共 % 行"
+
+#: ../ex_cmds.c:4470
+#, fuzzy, c-format
+msgid "% substitution on % line"
+msgid_plural "% substitutions on % line"
+msgstr[0] "% 次替æ¢ï¼Œ"
+
+#: ../ex_cmds.c:4473
+#, fuzzy, c-format
+msgid "% match on % lines"
+msgid_plural "% matches on % lines"
+msgstr[0] "共 % 行"
+
+#: ../ex_cmds.c:4475
+#, fuzzy, c-format
+msgid "% substitution on % lines"
+msgid_plural "% substitutions on % lines"
+msgstr[0] "% 次替æ¢ï¼Œ"
+
+#. will increment global_busy to break out of the loop
+#: ../ex_cmds.c:4536
+msgid "E147: Cannot do :global recursive with a range"
+msgstr "E147: ä¸èƒ½å¸¦èŒƒå›´é€’归执行 :global"
+
+#: ../ex_cmds.c:4565
+msgid "E148: Regular expression missing from global"
+msgstr "E148: global 缺少正则表达å¼"
+
+#: ../ex_cmds.c:4610
+#, c-format
+msgid "Pattern found in every line: %s"
+msgstr "æ¯è¡Œéƒ½åŒ¹é…表达å¼: %s"
+
+#: ../ex_cmds.c:4612
+#, c-format
+msgid "Pattern not found: %s"
+msgstr "找ä¸åˆ°æ¨¡å¼ï¼š%s"
+
+#: ../ex_cmds.c:4921
+msgid "No old files"
+msgstr "没有旧文件"
+
+#: ../ex_cmds2.c:204
+#, c-format
+msgid "Save changes to \"%s\"?"
+msgstr "将改å˜ä¿å­˜åˆ° \"%s\" å—?"
+
+#: ../ex_cmds2.c:255
+#, c-format
+msgid "Close \"%s\"?"
+msgstr "关闭 \"%s\"?"
+
+#: ../ex_cmds2.c:380
+#, c-format
+msgid "E947: Job still running in buffer \"%s\""
+msgstr "E947: 任务ä»åœ¨ç¼“冲区 \"%s\" 中è¿è¡Œ"
+
+#: ../ex_cmds2.c:381
+#, c-format
+msgid "E162: No write since last change for buffer \"%s\""
+msgstr "E162: 缓冲区 \"%s\" 已修改但尚未ä¿å­˜"
+
+#: ../ex_cmds2.c:441
+msgid "Warning: Entered other buffer unexpectedly (check autocommands)"
+msgstr "警告: æ„外地进入了其它缓冲区 (请检查自动命令)"
+
+#: ../ex_cmds2.c:735
+#, c-format
+msgid "E666: compiler not supported: %s"
+msgstr "E666: 䏿”¯æŒç¼–译器: %s"
+
+#: ../ex_docmd.c:90
+msgid "E464: Ambiguous use of user-defined command"
+msgstr "E464: ä¸ç¡®å®šçš„用户自定义命令"
+
+#: ../ex_docmd.c:92
+msgid "E492: Not an editor command"
+msgstr "E492: 䏿˜¯ç¼–辑器的命令"
+
+#: ../ex_docmd.c:94
+msgid "E498: no :source file name to substitute for \"\""
+msgstr "E498: æ²¡æœ‰ç”¨äºŽæ›¿æ¢ \"\" çš„ :source 文件å"
+
+#: ../ex_docmd.c:96
+msgid "E489: no call stack to substitute for \"\""
+msgstr "E489: æ²¡æœ‰ç”¨äºŽæ›¿æ¢ \"\" 的调用栈"
+
+#: ../ex_docmd.c:98
+msgid "E1274: No script file name to substitute for \"