From 85b43c6cb7d56919e245622f4e42db6d8bee4194 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Mon, 21 Mar 2022 19:45:17 +0000 Subject: [PATCH 01/18] patch 8.2.4603: sourcing buffer lines is too complicated Problem: Sourcing buffer lines is too complicated. Solution: Simplify the code. Make it possible to source Vim9 script lines. (Yegappan Lakshmanan, closes #9974) --- runtime/doc/repeat.txt | 16 +- src/ex_docmd.c | 2 +- src/proto/scriptfile.pro | 2 +- src/scriptfile.c | 412 ++++++++++++------------------------ src/structs.h | 3 + src/testdir/test_source.vim | 266 +++++++++++++++++++++++ src/version.c | 2 + 7 files changed, 417 insertions(+), 286 deletions(-) diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index a775af03a6..f7756c1c06 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -201,7 +201,13 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|. :[range]so[urce] Read Ex commands from the [range] of lines in the current buffer. When sourcing commands from the current buffer, the same script-ID || is used - even if the buffer is sourced multiple times. + even if the buffer is sourced multiple times. If a + buffer is sourced more than once, then the functions + in the buffer are redefined again. + Sourcing a buffer with a Vim9 script more than once + works like |vim9-reload|. + To source a script in the Vim9 context, the |:vim9cmd| + modifier can be used. *:source!* :so[urce]! {file} Read Vim commands from {file}. These are commands @@ -425,10 +431,10 @@ An alternative is to put the commands in a file, and execute them with the ':source!' command. Useful for long command sequences. Can be combined with the ':map' command to put complicated commands under a function key. -The ':source' command reads Ex commands from a file line by line. You will -have to type any needed keyboard input. The ':source!' command reads from a -script file character by character, interpreting each character as if you -typed it. +The ':source' command reads Ex commands from a file or a buffer line by line. +You will have to type any needed keyboard input. The ':source!' command reads +from a script file character by character, interpreting each character as if +you typed it. Example: When you give the ":!ls" command you get the |hit-enter| prompt. If you ':source' a file with the line "!ls" in it, you will have to type the diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 463d840780..c33dcbca01 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -2572,7 +2572,7 @@ do_one_cmd( #ifdef FEAT_EVAL // Set flag that any command was executed, used by ex_vim9script(). // Not if this was a command that wasn't executed or :endif. - if (getline_equal(ea.getline, ea.cookie, getsourceline) + if (sourcing_a_script(&ea) && current_sctx.sc_sid > 0 && ea.cmdidx != CMD_endif && (cstack->cs_idx < 0 diff --git a/src/proto/scriptfile.pro b/src/proto/scriptfile.pro index d0ebdda103..510fb56688 100644 --- a/src/proto/scriptfile.pro +++ b/src/proto/scriptfile.pro @@ -32,6 +32,7 @@ void free_scriptnames(void); void free_autoload_scriptnames(void); linenr_T get_sourced_lnum(char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie); char_u *getsourceline(int c, void *cookie, int indent, getline_opt_T options); +int sourcing_a_script(exarg_T *eap); void ex_scriptencoding(exarg_T *eap); void ex_scriptversion(exarg_T *eap); void ex_finish(exarg_T *eap); @@ -42,5 +43,4 @@ char_u *get_autoload_prefix(scriptitem_T *si); char_u *may_prefix_autoload(char_u *name); char_u *autoload_name(char_u *name); int script_autoload(char_u *name, int reload); -int sourcing_a_script(exarg_T *eap); /* vim: set ft=c : */ diff --git a/src/scriptfile.c b/src/scriptfile.c index 9778843423..3faed4a700 100644 --- a/src/scriptfile.c +++ b/src/scriptfile.c @@ -23,6 +23,8 @@ static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL}; static int last_current_SID_seq = 0; #endif +static int do_source_ext(char_u *fname, int check_other, int is_vimrc, int *ret_sid, exarg_T *eap); + /* * Initialize the execution stack. */ @@ -1079,251 +1081,6 @@ ExpandPackAddDir( return OK; } -/* - * Cookie used to source Ex commands from a buffer. - */ -typedef struct -{ - garray_T lines_to_source; - int lnum; - linenr_T sourcing_lnum; -} bufline_cookie_T; - -/* - * Concatenate a Vim script line if it starts with a line continuation into a - * growarray (excluding the continuation chars and leading whitespace). - * Growsize of the growarray may be changed to speed up concatenations! - * - * Returns TRUE if this line did begin with a continuation (the next line - * should also be considered, if it exists); FALSE otherwise. - */ - static int -concat_continued_line( - garray_T *ga, - int init_growsize, - char_u *nextline, - int options) -{ - int comment_char = in_vim9script() ? '#' : '"'; - char_u *p = skipwhite(nextline); - int contline; - int do_vim9_all = in_vim9script() - && options == GETLINE_CONCAT_ALL; - int do_bar_cont = do_vim9_all - || options == GETLINE_CONCAT_CONTBAR; - - if (*p == NUL) - return FALSE; - - // Concatenate the next line when it starts with a backslash. - /* Also check for a comment in between continuation lines: "\ */ - // Also check for a Vim9 comment, empty line, line starting with '|', - // but not "||". - if ((p[0] == comment_char && p[1] == '\\' && p[2] == ' ') - || (do_vim9_all && (*p == NUL - || vim9_comment_start(p)))) - return TRUE; - - contline = (*p == '\\' || (do_bar_cont && p[0] == '|' && p[1] != '|')); - if (!contline) - return FALSE; - - // Adjust the growsize to the current length to speed up concatenating many - // lines. - if (ga->ga_len > init_growsize) - ga->ga_growsize = ga->ga_len > 8000 ? 8000 : ga->ga_len; - if (*p == '\\') - ga_concat(ga, (char_u *)p + 1); - else if (*p == '|') - { - ga_concat(ga, (char_u *)" "); - ga_concat(ga, p); - } - - return TRUE; -} - -/* - * Get one full line from a sourced string (in-memory, no file). - * Called by do_cmdline() when it's called from source_using_linegetter(). - * - * Returns a pointer to allocated line, or NULL for end-of-file. - */ - static char_u * -source_getbufline( - int c UNUSED, - void *cookie, - int indent UNUSED, - getline_opt_T opts) -{ - bufline_cookie_T *p = cookie; - char_u *line; - garray_T ga; - - SOURCING_LNUM = p->sourcing_lnum + 1; - - if (p->lnum >= p->lines_to_source.ga_len) - return NULL; - line = ((char_u **)p->lines_to_source.ga_data)[p->lnum]; - - ga_init2(&ga, sizeof(char_u), 400); - ga_concat(&ga, (char_u *)line); - p->lnum++; - - if ((opts != GETLINE_NONE) && vim_strchr(p_cpo, CPO_CONCAT) == NULL) - { - while (p->lnum < p->lines_to_source.ga_len) - { - line = ((char_u **)p->lines_to_source.ga_data)[p->lnum]; - if (!concat_continued_line(&ga, 400, line, opts)) - break; - p->sourcing_lnum++; - p->lnum++; - } - } - ga_append(&ga, NUL); - p->sourcing_lnum++; - - return ga.ga_data; -} - -/* - * Source Ex commands from the lines in 'cookie'. - */ - static int -do_sourcebuffer( - void *cookie, - char_u *scriptname) -{ - char_u *save_sourcing_name = SOURCING_NAME; - linenr_T save_sourcing_lnum = SOURCING_LNUM; - char_u sourcing_name_buf[256]; - sctx_T save_current_sctx; -#ifdef FEAT_EVAL - int sid; - funccal_entry_T funccalp_entry; - int save_estack_compiling = estack_compiling; - scriptitem_T *si = NULL; -#endif - int save_sticky_cmdmod_flags = sticky_cmdmod_flags; - int retval = FAIL; - ESTACK_CHECK_DECLARATION - - if (save_sourcing_name == NULL) - SOURCING_NAME = (char_u *)scriptname; - else - { - vim_snprintf((char *)sourcing_name_buf, sizeof(sourcing_name_buf), - "%s called at %s:%ld", scriptname, save_sourcing_name, - save_sourcing_lnum); - SOURCING_NAME = sourcing_name_buf; - } - SOURCING_LNUM = 0; - - // Keep the sourcing name/lnum, for recursive calls. - estack_push(ETYPE_SCRIPT, scriptname, 0); - ESTACK_CHECK_SETUP - - // "legacy" does not apply to commands in the script - sticky_cmdmod_flags = 0; - - save_current_sctx = current_sctx; - current_sctx.sc_version = 1; // default script version -#ifdef FEAT_EVAL - estack_compiling = FALSE; - // Always use a new sequence number. - current_sctx.sc_seq = ++last_current_SID_seq; - current_sctx.sc_lnum = save_sourcing_lnum; - save_funccal(&funccalp_entry); - - sid = find_script_by_name(scriptname); - if (sid < 0) - { - int error = OK; - - // First time sourcing this buffer, create a new script item. - - sid = get_new_scriptitem(&error); - if (error == FAIL) - goto theend; - current_sctx.sc_sid = sid; - si = SCRIPT_ITEM(current_sctx.sc_sid); - si->sn_name = vim_strsave(scriptname); - si->sn_state = SN_STATE_NEW; - } - else - { - // the buffer was sourced previously, reuse the script ID. - current_sctx.sc_sid = sid; - si = SCRIPT_ITEM(current_sctx.sc_sid); - si->sn_state = SN_STATE_RELOAD; - } -#endif - - retval = do_cmdline(NULL, source_getbufline, cookie, - DOCMD_VERBOSE | DOCMD_NOWAIT | DOCMD_REPEAT); - - if (got_int) - emsg(_(e_interrupted)); - -#ifdef FEAT_EVAL -theend: -#endif - ESTACK_CHECK_NOW - estack_pop(); - current_sctx = save_current_sctx; - SOURCING_LNUM = save_sourcing_lnum; - SOURCING_NAME = save_sourcing_name; - sticky_cmdmod_flags = save_sticky_cmdmod_flags; -#ifdef FEAT_EVAL - restore_funccal(); - estack_compiling = save_estack_compiling; -#endif - - return retval; -} - -/* - * :source Ex commands from the current buffer - */ - static void -cmd_source_buffer(exarg_T *eap) -{ - char_u *line = NULL; - linenr_T curr_lnum; - bufline_cookie_T cp; - char_u sname[32]; - - if (curbuf == NULL) - return; - - // Use ":source buffer=" as the script name - vim_snprintf((char *)sname, sizeof(sname), ":source buffer=%d", - curbuf->b_fnum); - - ga_init2(&cp.lines_to_source, sizeof(char_u *), 100); - - // Copy the lines from the buffer into a grow array - for (curr_lnum = eap->line1; curr_lnum <= eap->line2; curr_lnum++) - { - line = vim_strsave(ml_get(curr_lnum)); - if (line == NULL) - goto errret; - if (ga_add_string(&cp.lines_to_source, line) == FAIL) - goto errret; - line = NULL; - } - cp.sourcing_lnum = 0; - cp.lnum = 0; - - // Execute the Ex commands - do_sourcebuffer((void *)&cp, (char_u *)sname); - -errret: - vim_free(line); - ga_clear_strings(&cp.lines_to_source); -} - static void cmd_source(char_u *fname, exarg_T *eap) { @@ -1341,7 +1098,7 @@ cmd_source(char_u *fname, exarg_T *eap) emsg(_(e_argument_required)); else // source ex commands from the current buffer - cmd_source_buffer(eap); + do_source_ext(NULL, FALSE, FALSE, NULL, eap); } else if (eap != NULL && eap->forceit) // ":source!": read Normal mode commands @@ -1480,21 +1237,73 @@ fopen_noinh_readbin(char *filename) #endif /* - * do_source: Read the file "fname" and execute its lines as EX commands. + * Initialization for sourcing lines from the current buffer. Reads all the + * lines from the buffer and stores it in the cookie grow array. + * Returns a pointer to the name ":source buffer=" on success and NULL on + * failure. + */ + static char_u * +do_source_buffer_init(source_cookie_T *sp, exarg_T *eap) +{ + linenr_T curr_lnum; + char_u *line = NULL; + char_u *fname; + + CLEAR_FIELD(*sp); + + if (curbuf == NULL) + return NULL; + + // Use ":source buffer=" as the script name + vim_snprintf((char *)IObuff, IOSIZE, ":source buffer=%d", curbuf->b_fnum); + fname = vim_strsave(IObuff); + if (fname == NULL) + return NULL; + + ga_init2(&sp->buflines, sizeof(char_u *), 100); + + // Copy the lines from the buffer into a grow array + for (curr_lnum = eap->line1; curr_lnum <= eap->line2; curr_lnum++) + { + line = vim_strsave(ml_get(curr_lnum)); + if (line == NULL) + goto errret; + if (ga_add_string(&sp->buflines, line) == FAIL) + goto errret; + line = NULL; + } + sp->buf_lnum = 0; + sp->source_from_buf = TRUE; + + return fname; + +errret: + vim_free(fname); + vim_free(line); + ga_clear_strings(&sp->buflines); + return NULL; +} + +/* + * Read the file "fname" and execute its lines as EX commands. * When "ret_sid" is not NULL and we loaded the script before, don't load it * again. * + * The 'eap' argument is used when sourcing lines from a buffer instead of a + * file. + * * This function may be called recursively! * * Return FAIL if file could not be opened, OK otherwise. * If a scriptitem_T was found or created "*ret_sid" is set to the SID. */ - int -do_source( + static int +do_source_ext( char_u *fname, int check_other, // check for .vimrc and _vimrc int is_vimrc, // DOSO_ value - int *ret_sid UNUSED) + int *ret_sid UNUSED, + exarg_T *eap) { source_cookie_T cookie; char_u *p; @@ -1520,17 +1329,28 @@ do_source( int trigger_source_post = FALSE; ESTACK_CHECK_DECLARATION - p = expand_env_save(fname); - if (p == NULL) - return retval; - fname_exp = fix_fname(p); - vim_free(p); - if (fname_exp == NULL) - return retval; - if (mch_isdir(fname_exp)) + CLEAR_FIELD(cookie); + if (fname == NULL) { - smsg(_("Cannot source a directory: \"%s\""), fname); - goto theend; + // sourcing lines from a buffer + fname_exp = do_source_buffer_init(&cookie, eap); + if (fname_exp == NULL) + return FAIL; + } + else + { + p = expand_env_save(fname); + if (p == NULL) + return retval; + fname_exp = fix_fname(p); + vim_free(p); + if (fname_exp == NULL) + return retval; + if (mch_isdir(fname_exp)) + { + smsg(_("Cannot source a directory: \"%s\""), fname); + goto theend; + } } #ifdef FEAT_EVAL estack_compiling = FALSE; @@ -1567,11 +1387,14 @@ do_source( // Apply SourcePre autocommands, they may get the file. apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, FALSE, curbuf); + if (!cookie.source_from_buf) + { #ifdef USE_FOPEN_NOINH - cookie.fp = fopen_noinh_readbin((char *)fname_exp); + cookie.fp = fopen_noinh_readbin((char *)fname_exp); #else - cookie.fp = mch_fopen((char *)fname_exp, READBIN); + cookie.fp = mch_fopen((char *)fname_exp, READBIN); #endif + } if (cookie.fp == NULL && check_other) { // Try again, replacing file name ".vimrc" by "_vimrc" or vice versa, @@ -1594,7 +1417,7 @@ do_source( } } - if (cookie.fp == NULL) + if (cookie.fp == NULL && !cookie.source_from_buf) { if (p_verbose > 0) { @@ -1632,12 +1455,14 @@ do_source( cookie.fileformat = EOL_DOS; else cookie.fileformat = EOL_UNKNOWN; - cookie.error = FALSE; #endif - cookie.nextline = NULL; - cookie.sourcing_lnum = 0; - cookie.finished = FALSE; + if (fname == NULL) + // When sourcing a range of lines from a buffer, use the buffer line + // number. + cookie.sourcing_lnum = eap->line1 - 1; + else + cookie.sourcing_lnum = 0; #ifdef FEAT_EVAL // Check if this script has a breakpoint. @@ -1661,7 +1486,12 @@ do_source( sticky_cmdmod_flags = 0; save_current_sctx = current_sctx; - current_sctx.sc_version = 1; // default script version + if (cmdmod.cmod_flags & CMOD_VIM9CMD) + // When the ":vim9cmd" command modifier is used, source the script as a + // Vim9 script. + current_sctx.sc_version = SCRIPT_VERSION_VIM9; + else + current_sctx.sc_version = 1; // default script version #ifdef FEAT_EVAL # ifdef FEAT_PROFILE @@ -1874,7 +1704,10 @@ almosttheend: #endif current_sctx = save_current_sctx; - fclose(cookie.fp); + if (cookie.fp != NULL) + fclose(cookie.fp); + if (cookie.source_from_buf) + ga_clear_strings(&cookie.buflines); vim_free(cookie.nextline); vim_free(firstline); convert_setup(&cookie.conv, NULL, NULL); @@ -1891,6 +1724,17 @@ theend: return retval; } + int +do_source( + char_u *fname, + int check_other, // check for .vimrc and _vimrc + int is_vimrc, // DOSO_ value + int *ret_sid UNUSED) +{ + return do_source_ext(fname, check_other, is_vimrc, ret_sid, NULL); +} + + #if defined(FEAT_EVAL) || defined(PROTO) /* @@ -2038,11 +1882,21 @@ get_one_sourceline(source_cookie_T *sp) // make room to read at least 120 (more) characters if (ga_grow(&ga, 120) == FAIL) break; - buf = (char_u *)ga.ga_data; - - if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len, - sp->fp) == NULL) - break; + if (sp->source_from_buf) + { + if (sp->buf_lnum >= sp->buflines.ga_len) + break; // all the lines are processed + ga_concat(&ga, ((char_u **)sp->buflines.ga_data)[sp->buf_lnum]); + sp->buf_lnum++; + buf = (char_u *)ga.ga_data; + } + else + { + buf = (char_u *)ga.ga_data; + if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len, + sp->fp) == NULL) + break; + } len = ga.ga_len + (int)STRLEN(buf + ga.ga_len); #ifdef USE_CRNL // Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the @@ -2145,7 +1999,7 @@ getsourceline( #ifdef FEAT_EVAL // If breakpoints have been added/deleted need to check for it. - if (sp->dbg_tick < debug_tick) + if ((sp->dbg_tick < debug_tick) && !sp->source_from_buf) { sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, SOURCING_LNUM); sp->dbg_tick = debug_tick; @@ -2161,7 +2015,7 @@ getsourceline( // Get current line. If there is a read-ahead line, use it, otherwise get // one now. "fp" is NULL if actually using a string. - if (sp->finished || sp->fp == NULL) + if (sp->finished || (!sp->source_from_buf && sp->fp == NULL)) line = NULL; else if (sp->nextline == NULL) line = get_one_sourceline(sp); @@ -2265,7 +2119,8 @@ getsourceline( #ifdef FEAT_EVAL // Did we encounter a breakpoint? - if (sp->breakpoint != 0 && sp->breakpoint <= SOURCING_LNUM) + if (!sp->source_from_buf && sp->breakpoint != 0 + && sp->breakpoint <= SOURCING_LNUM) { dbg_breakpoint(sp->fname, SOURCING_LNUM); // Find next breakpoint. @@ -2284,8 +2139,7 @@ getsourceline( int sourcing_a_script(exarg_T *eap) { - return (getline_equal(eap->getline, eap->cookie, getsourceline) - || getline_equal(eap->getline, eap->cookie, source_getbufline)); + return (getline_equal(eap->getline, eap->cookie, getsourceline)); } /* diff --git a/src/structs.h b/src/structs.h index 1a98004527..a83d9ea643 100644 --- a/src/structs.h +++ b/src/structs.h @@ -4426,6 +4426,9 @@ typedef struct { char_u *nextline; // if not NULL: line that was read ahead linenr_T sourcing_lnum; // line number of the source file int finished; // ":finish" used + int source_from_buf;// TRUE if sourcing from current buffer + int buf_lnum; // line number in the current buffer + garray_T buflines; // lines in the current buffer #ifdef USE_CRNL int fileformat; // EOL_UNKNOWN, EOL_UNIX or EOL_DOS int error; // TRUE if LF found after CR-LF diff --git a/src/testdir/test_source.vim b/src/testdir/test_source.vim index bdf50e1beb..251625aab1 100644 --- a/src/testdir/test_source.vim +++ b/src/testdir/test_source.vim @@ -146,6 +146,23 @@ func Test_source_buffer() 2,3source call assert_equal(90, g:a) + " Make sure the script line number is correct when sourcing a range of + " lines. + %d _ + let lines =<< trim END + Line 1 + Line 2 + func Xtestfunc() + return expand("") + endfunc + Line 3 + Line 4 + END + call setline(1, lines) + 3,5source + call assert_equal('4', Xtestfunc()) + delfunc Xtestfunc + " Source a script with line continuation lines %d _ let lines =<< trim END @@ -327,6 +344,63 @@ func Test_source_buffer() call assert_equal("three", Xtestfunc()) delfunc Xtestfunc + " test for using try/catch + %d _ + let lines =<< trim END + let Trace = '1' + try + let a1 = b1 + catch + let Trace ..= '2' + finally + let Trace ..= '3' + endtry + END + call setline(1, lines) + source + call assert_equal("123", g:Trace) + + " test with the finish command + %d _ + let lines =<< trim END + let g:Color = 'blue' + finish + let g:Color = 'green' + END + call setline(1, lines) + source + call assert_equal('blue', g:Color) + + " Test for the SourcePre and SourcePost autocmds + augroup Xtest + au! + au SourcePre * let g:XsourcePre=4 + \ | let g:XsourcePreFile = expand("") + au SourcePost * let g:XsourcePost=6 + \ | let g:XsourcePostFile = expand("") + augroup END + %d _ + let lines =<< trim END + let a = 1 + END + call setline(1, lines) + source + call assert_equal(4, g:XsourcePre) + call assert_equal(6, g:XsourcePost) + call assert_equal(':source buffer=' .. bufnr(), g:XsourcePreFile) + call assert_equal(':source buffer=' .. bufnr(), g:XsourcePostFile) + augroup Xtest + au! + augroup END + augroup! Xtest + + %bw! +endfunc + +" Test for sourcing a Vim9 script from the current buffer +func Test_source_buffer_vim9() + new + " test for sourcing a Vim9 script %d _ let lines =<< trim END @@ -342,6 +416,198 @@ func Test_source_buffer() source call assert_equal(10, Xtestfunc()) + " test for sourcing a vim9 script with line continuation + %d _ + let lines =<< trim END + vim9script + + g:Str1 = "hello " + .. "world" + .. ", how are you?" + g:Colors = [ + 'red', + # comment + 'blue' + ] + g:Dict = { + a: 22, + # comment + b: 33 + } + + # calling a function with line continuation + def Sum(...values: list): number + var sum: number = 0 + for v in values + sum += v + endfor + return sum + enddef + g:Total1 = Sum(10, + 20, + 30) + + var i: number = 0 + while i < 10 + # while loop + i += + 1 + endwhile + g:Count1 = i + + # for loop + g:Count2 = 0 + for j in range(10, 20) + g:Count2 += + i + endfor + + g:Total2 = 10 + + 20 - + 5 + + g:Result1 = g:Total2 > 1 + ? 'red' + : 'blue' + + g:Str2 = 'x' + ->repeat(10) + ->trim() + ->strpart(4) + + g:Result2 = g:Dict + .a + + augroup Test + au! + au BufNewFile Xfile g:readFile = 1 + | g:readExtra = 2 + augroup END + g:readFile = 0 + g:readExtra = 0 + new Xfile + bwipe! + augroup Test + au! + augroup END + END + call setline(1, lines) + source + call assert_equal("hello world, how are you?", g:Str1) + call assert_equal(['red', 'blue'], g:Colors) + call assert_equal(#{a: 22, b: 33}, g:Dict) + call assert_equal(60, g:Total1) + call assert_equal(10, g:Count1) + call assert_equal(110, g:Count2) + call assert_equal(25, g:Total2) + call assert_equal('red', g:Result1) + call assert_equal('xxxxxx', g:Str2) + call assert_equal(22, g:Result2) + call assert_equal(1, g:readFile) + call assert_equal(2, g:readExtra) + + " test for sourcing the same buffer multiple times after changing a function + %d _ + let lines =<< trim END + vim9script + def g:Xtestfunc(): string + return "one" + enddef + END + call setline(1, lines) + source + call assert_equal("one", Xtestfunc()) + call setline(3, ' return "two"') + source + call assert_equal("two", Xtestfunc()) + call setline(3, ' return "three"') + source + call assert_equal("three", Xtestfunc()) + delfunc Xtestfunc + + " Test for sourcing a range of lines. Make sure the script line number is + " correct. + %d _ + let lines =<< trim END + Line 1 + Line 2 + vim9script + def g:Xtestfunc(): string + return expand("") + enddef + Line 3 + Line 4 + END + call setline(1, lines) + 3,6source + call assert_equal('5', Xtestfunc()) + delfunc Xtestfunc + + " test for sourcing a heredoc + %d _ + let lines =<< trim END + vim9script + var a = 1 + g:heredoc =<< trim DATA + red + green + blue + DATA + var b = 2 + END + call setline(1, lines) + source + call assert_equal(['red', ' green', 'blue'], g:heredoc) + + " test for using the :vim9cmd modifier + %d _ + let lines =<< trim END + first line + g:Math = { + pi: 3.12, + e: 2.71828 + } + g:Editors = [ + 'vim', + # comment + 'nano' + ] + last line + END + call setline(1, lines) + vim9cmd :2,10source + call assert_equal(#{pi: 3.12, e: 2.71828}, g:Math) + call assert_equal(['vim', 'nano'], g:Editors) + + " test for using try/catch + %d _ + let lines =<< trim END + vim9script + g:Trace = '1' + try + a1 = b1 + catch + g:Trace ..= '2' + finally + g:Trace ..= '3' + endtry + END + call setline(1, lines) + source + call assert_equal('123', g:Trace) + + " test with the finish command + %d _ + let lines =<< trim END + vim9script + g:Color = 'red' + finish + g:Color = 'blue' + END + call setline(1, lines) + source + call assert_equal('red', g:Color) + %bw! endfunc diff --git a/src/version.c b/src/version.c index e262953d74..3874b93c29 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4603, /**/ 4602, /**/ From 6d877fe0181bdaf382bfc87898e1ddf6e3c78e66 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 21 Mar 2022 19:47:31 +0000 Subject: [PATCH 02/18] patch 8.2.4604: error for redefining a script item may be confusing Problem: Error for redefining a script item may be confusing. Solution: Put quotes around the name. --- src/errors.h | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/errors.h b/src/errors.h index 7bc4b1d49f..949a532794 100644 --- a/src/errors.h +++ b/src/errors.h @@ -2715,7 +2715,7 @@ EXTERN char e_cannot_use_scriptversion_after_vim9script[] INIT(= N_("E1040: Cannot use :scriptversion after :vim9script")); #ifdef FEAT_EVAL EXTERN char e_redefining_script_item_str[] - INIT(= N_("E1041: Redefining script item %s")); + INIT(= N_("E1041: Redefining script item: \"%s\"")); EXTERN char e_export_can_only_be_used_in_vim9script[] INIT(= N_("E1042: Export can only be used in vim9script")); EXTERN char e_invalid_command_after_export[] diff --git a/src/version.c b/src/version.c index 3874b93c29..f5d7b32800 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4604, /**/ 4603, /**/ From e18acb02bb58b2e07f3a52ce619752ba39c05ea2 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 21 Mar 2022 20:40:35 +0000 Subject: [PATCH 03/18] patch 8.2.4606: test fails because of changed error message Problem: Test fails because of changed error message. Solution: Update the expected error message --- src/clientserver.c | 4 ++-- src/testdir/test_vim9_import.vim | 4 ++-- src/version.c | 4 ++++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/clientserver.c b/src/clientserver.c index caa6fdc668..15e490fd50 100644 --- a/src/clientserver.c +++ b/src/clientserver.c @@ -794,6 +794,7 @@ f_remote_expr(typval_T *argvars UNUSED, typval_T *rettv) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; +#ifdef FEAT_CLIENTSERVER if (in_vim9script() && (check_for_string_arg(argvars, 0) == FAIL || check_for_string_arg(argvars, 1) == FAIL @@ -802,7 +803,6 @@ f_remote_expr(typval_T *argvars UNUSED, typval_T *rettv) && check_for_opt_number_arg(argvars, 3) == FAIL))) return; -#ifdef FEAT_CLIENTSERVER remote_common(argvars, rettv, TRUE); #endif } @@ -945,13 +945,13 @@ f_remote_send(typval_T *argvars UNUSED, typval_T *rettv) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; +#ifdef FEAT_CLIENTSERVER if (in_vim9script() && (check_for_string_arg(argvars, 0) == FAIL || check_for_string_arg(argvars, 1) == FAIL || check_for_opt_string_arg(argvars, 2) == FAIL)) return; -#ifdef FEAT_CLIENTSERVER remote_common(argvars, rettv, FALSE); #endif } diff --git a/src/testdir/test_vim9_import.vim b/src/testdir/test_vim9_import.vim index c9ae612ca0..2ce825f2ba 100644 --- a/src/testdir/test_vim9_import.vim +++ b/src/testdir/test_vim9_import.vim @@ -2156,7 +2156,7 @@ def Test_vim9script_autoload_duplicate() export var Func = 'asdf' END writefile(lines, 'Xdir/autoload/dup3func.vim') - assert_fails('source Xdir/autoload/dup3func.vim', 'E1041: Redefining script item Func') + assert_fails('source Xdir/autoload/dup3func.vim', 'E1041: Redefining script item: "Func"') lines =<< trim END vim9script @@ -2189,7 +2189,7 @@ def Test_vim9script_autoload_duplicate() var Func = 'asdf' END writefile(lines, 'Xdir/autoload/dup6func.vim') - assert_fails('source Xdir/autoload/dup6func.vim', 'E1041: Redefining script item Func') + assert_fails('source Xdir/autoload/dup6func.vim', 'E1041: Redefining script item: "Func"') delete('Xdir', 'rf') enddef diff --git a/src/version.c b/src/version.c index f5d7b32800..6d5e1a685c 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,10 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4606, +/**/ + 4605, /**/ 4604, /**/ From 35dc17634dd6da5b90bd1b0160c4ed9e394f4b87 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Tue, 22 Mar 2022 12:13:54 +0000 Subject: [PATCH 04/18] patch 8.2.4607: sourcing buffer lines may lead to errors for conflicts Problem: Sourcing buffer lines may lead to errors for conflicts. Solution: Add the ++clear argument. (Yegappan Lakshmanan, closes #9991) --- runtime/doc/repeat.txt | 39 +++++++++++++++++------- src/proto/vim9script.pro | 1 + src/scriptfile.c | 60 ++++++++++++++++++++++++++----------- src/testdir/test_source.vim | 28 +++++++++++++++++ src/version.c | 2 ++ src/vim9script.c | 29 +++++++++++------- 6 files changed, 121 insertions(+), 38 deletions(-) diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index f7756c1c06..1ee547ce04 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -198,16 +198,35 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|. start with a ":". Triggers the |SourcePre| autocommand. -:[range]so[urce] Read Ex commands from the [range] of lines in the - current buffer. When sourcing commands from the - current buffer, the same script-ID || is used - even if the buffer is sourced multiple times. If a - buffer is sourced more than once, then the functions - in the buffer are redefined again. - Sourcing a buffer with a Vim9 script more than once - works like |vim9-reload|. - To source a script in the Vim9 context, the |:vim9cmd| - modifier can be used. +:[range]so[urce] [++clear] + Read Ex commands from the [range] of lines in the + current buffer. + + When sourcing commands from the current buffer, the + same script-ID || is used even if the buffer is + sourced multiple times. If a buffer is sourced more + than once, then the functions in the buffer are + defined again. + + To source a range of lines that doesn't start with the + |:vim9script| command in Vim9 script context, the + |:vim9cmd| modifier can be used. + + When a range of lines in a buffer is sourced in the + Vim9 script context, the previously defined + script-local variables and functions are not cleared. + This works like the range started with the + ":vim9script noclear" command. The "++clear" argument + can be used to clear the script-local variables and + functions before sourcing the script. This works like + the range started with the |:vimscript| command + without the "noclear" argument. See |vim9-reload| for + more information. + Examples: > + + :4,5source + :vim9cmd :'<,'>source + :10,18source ++clear *:source!* :so[urce]! {file} Read Vim commands from {file}. These are commands diff --git a/src/proto/vim9script.pro b/src/proto/vim9script.pro index 6295dc2cca..bc1e23275c 100644 --- a/src/proto/vim9script.pro +++ b/src/proto/vim9script.pro @@ -2,6 +2,7 @@ int in_vim9script(void); int in_old_script(int max_version); int current_script_is_vim9(void); +void clear_vim9_scriptlocal_vars(int sid); void ex_vim9script(exarg_T *eap); int not_in_vim9(exarg_T *eap); int vim9_bad_comment(char_u *p); diff --git a/src/scriptfile.c b/src/scriptfile.c index 3faed4a700..ae46e7a86d 100644 --- a/src/scriptfile.c +++ b/src/scriptfile.c @@ -23,7 +23,7 @@ static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL}; static int last_current_SID_seq = 0; #endif -static int do_source_ext(char_u *fname, int check_other, int is_vimrc, int *ret_sid, exarg_T *eap); +static int do_source_ext(char_u *fname, int check_other, int is_vimrc, int *ret_sid, exarg_T *eap, int clearvars); /* * Initialize the execution stack. @@ -1084,6 +1084,20 @@ ExpandPackAddDir( static void cmd_source(char_u *fname, exarg_T *eap) { + int clearvars = FALSE; + + if (*fname != NUL && STRNCMP(fname, "++clear", 7) == 0) + { + // ++clear argument is supplied + clearvars = TRUE; + fname = fname + 7; + if (*fname != NUL) + { + semsg(_(e_invalid_argument_str), eap->arg); + return; + } + } + if (*fname != NUL && eap != NULL && eap->addr_count > 0) { // if a filename is specified to :source, then a range is not allowed @@ -1098,7 +1112,7 @@ cmd_source(char_u *fname, exarg_T *eap) emsg(_(e_argument_required)); else // source ex commands from the current buffer - do_source_ext(NULL, FALSE, FALSE, NULL, eap); + do_source_ext(NULL, FALSE, FALSE, NULL, eap, clearvars); } else if (eap != NULL && eap->forceit) // ":source!": read Normal mode commands @@ -1292,6 +1306,10 @@ errret: * The 'eap' argument is used when sourcing lines from a buffer instead of a * file. * + * If 'clearvars' is TRUE, then for scripts which are loaded more than + * once, clear all the functions and variables previously defined in that + * script. + * * This function may be called recursively! * * Return FAIL if file could not be opened, OK otherwise. @@ -1303,7 +1321,8 @@ do_source_ext( int check_other, // check for .vimrc and _vimrc int is_vimrc, // DOSO_ value int *ret_sid UNUSED, - exarg_T *eap) + exarg_T *eap, + int clearvars UNUSED) { source_cookie_T cookie; char_u *p; @@ -1527,20 +1546,25 @@ do_source_ext( { si->sn_state = SN_STATE_RELOAD; - // Script-local variables remain but "const" can be set again. - // In Vim9 script variables will be cleared when "vim9script" is - // encountered without the "noclear" argument. - ht = &SCRIPT_VARS(sid); - todo = (int)ht->ht_used; - for (hi = ht->ht_array; todo > 0; ++hi) - if (!HASHITEM_EMPTY(hi)) - { - --todo; - di = HI2DI(hi); - di->di_flags |= DI_FLAGS_RELOAD; - } - // imports can be redefined once - mark_imports_for_reload(sid); + if (!clearvars) + { + // Script-local variables remain but "const" can be set again. + // In Vim9 script variables will be cleared when "vim9script" + // is encountered without the "noclear" argument. + ht = &SCRIPT_VARS(sid); + todo = (int)ht->ht_used; + for (hi = ht->ht_array; todo > 0; ++hi) + if (!HASHITEM_EMPTY(hi)) + { + --todo; + di = HI2DI(hi); + di->di_flags |= DI_FLAGS_RELOAD; + } + // imports can be redefined once + mark_imports_for_reload(sid); + } + else + clear_vim9_scriptlocal_vars(sid); // reset version, "vim9script" may have been added or removed. si->sn_version = 1; @@ -1731,7 +1755,7 @@ do_source( int is_vimrc, // DOSO_ value int *ret_sid UNUSED) { - return do_source_ext(fname, check_other, is_vimrc, ret_sid, NULL); + return do_source_ext(fname, check_other, is_vimrc, ret_sid, NULL, FALSE); } diff --git a/src/testdir/test_source.vim b/src/testdir/test_source.vim index 251625aab1..010401084c 100644 --- a/src/testdir/test_source.vim +++ b/src/testdir/test_source.vim @@ -608,6 +608,34 @@ func Test_source_buffer_vim9() source call assert_equal('red', g:Color) + " test for ++clear argument to clear all the functions/variables + %d _ + let lines =<< trim END + g:ScriptVarFound = exists("color") + g:MyFuncFound = exists('*Myfunc') + if g:MyFuncFound + finish + endif + var color = 'blue' + def Myfunc() + enddef + END + call setline(1, lines) + vim9cmd source + call assert_false(g:MyFuncFound) + call assert_false(g:ScriptVarFound) + vim9cmd source + call assert_true(g:MyFuncFound) + call assert_true(g:ScriptVarFound) + vim9cmd source ++clear + call assert_false(g:MyFuncFound) + call assert_false(g:ScriptVarFound) + vim9cmd source ++clear + call assert_false(g:MyFuncFound) + call assert_false(g:ScriptVarFound) + call assert_fails('vim9cmd source ++clearx', 'E475:') + call assert_fails('vim9cmd source ++abcde', 'E484:') + %bw! endfunc diff --git a/src/version.c b/src/version.c index 6d5e1a685c..2c2ac28bf3 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4607, /**/ 4606, /**/ diff --git a/src/vim9script.c b/src/vim9script.c index 870056fcc4..3e8fe2f653 100644 --- a/src/vim9script.c +++ b/src/vim9script.c @@ -59,6 +59,24 @@ current_script_is_vim9(void) } #endif +#ifdef FEAT_EVAL +/* + * Clear Vim9 script-local variables and functions. + */ + void +clear_vim9_scriptlocal_vars(int sid) +{ + hashtab_T *ht = &SCRIPT_VARS(sid); + + hashtab_free_contents(ht); + hash_init(ht); + delete_script_functions(sid); + + // old imports and script variables are no longer valid + free_imports_and_script_vars(sid); +} +#endif + /* * ":vim9script". */ @@ -103,18 +121,9 @@ ex_vim9script(exarg_T *eap UNUSED) } if (si->sn_state == SN_STATE_RELOAD && !found_noclear) - { - hashtab_T *ht = &SCRIPT_VARS(sid); - // Reloading a script without the "noclear" argument: clear // script-local variables and functions. - hashtab_free_contents(ht); - hash_init(ht); - delete_script_functions(sid); - - // old imports and script variables are no longer valid - free_imports_and_script_vars(sid); - } + clear_vim9_scriptlocal_vars(sid); si->sn_state = SN_STATE_HAD_COMMAND; // Store the prefix with the script, it is used to find exported functions. From e7dd0fa2c61fe71f12c72b0dcb7bb6415eb048fb Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Tue, 22 Mar 2022 16:06:31 +0000 Subject: [PATCH 05/18] patch 8.2.4608: getcompletion() does not work when 'wildoptions' has "fuzzy" Problem: getcompletion() does not work properly when 'wildoptions contains "fuzzy". Solution: Do not use addstar(). (Yegappan Lakshmanan, closes #9992, closes #9986) --- runtime/doc/builtin.txt | 4 ++++ src/cmdexpand.c | 7 ++++++- src/testdir/test_cmdline.vim | 16 ++++++++++++++++ src/version.c | 2 ++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 062ccc345d..602d9098e6 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -3273,6 +3273,10 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()* is applied to filter the results. Otherwise all the matches are returned. The 'wildignorecase' option always applies. + If the 'wildoptions' option contains 'fuzzy', then fuzzy + matching is used to get the completion matches. Otherwise + regular expression matching is used. + If {type} is "cmdline", then the |cmdline-completion| result is returned. For example, to complete the possible values after a ":call" command: > diff --git a/src/cmdexpand.c b/src/cmdexpand.c index 5ba6b084d3..defc282dbb 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -3707,7 +3707,12 @@ f_getcompletion(typval_T *argvars, typval_T *rettv) # endif } - pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context); + if (cmdline_fuzzy_completion_supported(&xpc)) + // when fuzzy matching, don't modify the search string + pat = vim_strsave(xpc.xp_pattern); + else + pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context); + if ((rettv_list_alloc(rettv) != FAIL) && (pat != NULL)) { int i; diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index dc781f8e10..299210b878 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -552,6 +552,22 @@ func Test_getcompletion() call assert_fails('call getcompletion("abc", [])', 'E475:') endfunc +" Test for getcompletion() with "fuzzy" in 'wildoptions' +func Test_getcompletion_wildoptions() + let save_wildoptions = &wildoptions + set wildoptions& + let l = getcompletion('space', 'option') + call assert_equal([], l) + let l = getcompletion('ier', 'command') + call assert_equal([], l) + set wildoptions=fuzzy + let l = getcompletion('space', 'option') + call assert_true(index(l, 'backspace') >= 0) + let l = getcompletion('ier', 'command') + call assert_true(index(l, 'compiler') >= 0) + let &wildoptions = save_wildoptions +endfunc + func Test_complete_autoload_error() let save_rtp = &rtp let lines =<< trim END diff --git a/src/version.c b/src/version.c index 2c2ac28bf3..2180ebf264 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4608, /**/ 4607, /**/ From 6f2465d336a9d4afe392db4084ef7e9db17e67c1 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 22 Mar 2022 18:13:01 +0000 Subject: [PATCH 06/18] patch 8.2.4609: :unhide does not check for failing to close a window Problem: :unhide does not check for failing to close a window. Solution: When closing a window fails continue with the next one. Do not try closing the autocmd window. (closes #9984) --- src/buffer.c | 24 ++++++++++++++---------- src/proto/window.pro | 1 + src/testdir/test_autocmd.vim | 15 +++++++++++++++ src/version.c | 2 ++ src/window.c | 3 +-- 5 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index 8e68d94248..2dac4874c5 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -5330,17 +5330,21 @@ ex_buffer_all(exarg_T *eap) { wpnext = wp->w_next; if ((wp->w_buffer->b_nwindows > 1 - || ((cmdmod.cmod_split & WSP_VERT) - ? wp->w_height + wp->w_status_height < Rows - p_ch - - tabline_height() - : wp->w_width != Columns) - || (had_tab > 0 && wp != firstwin)) && !ONE_WINDOW - && !(wp->w_closing || wp->w_buffer->b_locked > 0)) + || ((cmdmod.cmod_split & WSP_VERT) + ? wp->w_height + wp->w_status_height < Rows - p_ch + - tabline_height() + : wp->w_width != Columns) + || (had_tab > 0 && wp != firstwin)) + && !ONE_WINDOW + && !(wp->w_closing || wp->w_buffer->b_locked > 0) + && !win_unlisted(wp)) { - win_close(wp, FALSE); - wpnext = firstwin; // just in case an autocommand does - // something strange with windows - tpnext = first_tabpage; // start all over... + if (win_close(wp, FALSE) == FAIL) + break; + // Just in case an autocommand does something strange with + // windows: start all over... + wpnext = firstwin; + tpnext = first_tabpage; open_wins = 0; } else diff --git a/src/proto/window.pro b/src/proto/window.pro index 11c2d47c70..1954dfd0ba 100644 --- a/src/proto/window.pro +++ b/src/proto/window.pro @@ -46,6 +46,7 @@ win_T *win_horz_neighbor(tabpage_T *tp, win_T *wp, int left, long count); void win_enter(win_T *wp, int undo_sync); win_T *buf_jump_open_win(buf_T *buf); win_T *buf_jump_open_tab(buf_T *buf); +int win_unlisted(win_T *wp); void win_free_popup(win_T *win); void win_remove(win_T *wp, tabpage_T *tp); int win_alloc_lines(win_T *wp); diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim index 7be0c1815a..688508a856 100644 --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -3,6 +3,7 @@ source shared.vim source check.vim source term_util.vim +import './vim9.vim' as v9 func s:cleanup_buffers() abort for bnr in range(1, bufnr('$')) @@ -2975,4 +2976,18 @@ func Test_Changed_ChangedI() bw! endfunc +func Test_closing_autocmd_window() + let lines =<< trim END + edit Xa.txt + tabnew Xb.txt + autocmd BufEnter Xa.txt unhide 1 + doautoall BufEnter + END + call v9.CheckScriptFailure(lines, 'E814:') + au! BufEnter + only! + bwipe Xa.txt + bwipe Xb.txt +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 2180ebf264..cd3d9cdeed 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4609, /**/ 4608, /**/ diff --git a/src/window.c b/src/window.c index cf11acf893..435ad037ac 100644 --- a/src/window.c +++ b/src/window.c @@ -43,7 +43,6 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin); static int may_open_tabpage(void); static int win_enter_ext(win_T *wp, int flags); static void win_free(win_T *wp, tabpage_T *tp); -static int win_unlisted(win_T *wp); static void win_append(win_T *after, win_T *wp); static void frame_append(frame_T *after, frame_T *frp); static void frame_insert(frame_T *before, frame_T *frp); @@ -5233,7 +5232,7 @@ win_free( * Return TRUE if "wp" is not in the list of windows: the autocmd window or a * popup window. */ - static int + int win_unlisted(win_T *wp) { return wp == aucmd_win || WIN_IS_POPUP(wp); From fe154990c1c57fac6d5a4b1bfb682e27adb4eb8c Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 22 Mar 2022 20:42:12 +0000 Subject: [PATCH 07/18] patch 8.2.4610: some conditions are always true Problem: Some conditions are always true. Solution: Remove the useless conditions. (closes #9993) --- src/clientserver.c | 2 +- src/drawline.c | 3 +-- src/drawscreen.c | 5 ++--- src/ex_cmds.c | 3 +-- src/fileio.c | 2 +- src/message.c | 2 +- src/misc2.c | 2 +- src/ops.c | 2 +- src/sign.c | 2 +- src/spell.c | 7 +++---- src/version.c | 2 ++ src/vim9cmds.c | 3 +-- src/window.c | 6 ++---- 13 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/clientserver.c b/src/clientserver.c index 15e490fd50..bb2108d7d4 100644 --- a/src/clientserver.c +++ b/src/clientserver.c @@ -420,7 +420,7 @@ cmdsrv_main( * For --remote-wait: Wait until the server did edit each * file. Also detect that the server no longer runs. */ - if (ret >= 0 && argtype == ARGTYPE_EDIT_WAIT) + if (argtype == ARGTYPE_EDIT_WAIT) { int numFiles = *argc - i - 1; int j; diff --git a/src/drawline.c b/src/drawline.c index d8b7c4aad1..747a1e33b0 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -1226,8 +1226,7 @@ win_line( { draw_state = WL_BRI; // if need_showbreak is set, breakindent also applies - if (wp->w_p_bri && n_extra == 0 - && (row != startrow || need_showbreak) + if (wp->w_p_bri && (row != startrow || need_showbreak) # ifdef FEAT_DIFF && filler_lines == 0 # endif diff --git a/src/drawscreen.c b/src/drawscreen.c index cdfcac04dc..a562c4d84a 100644 --- a/src/drawscreen.c +++ b/src/drawscreen.c @@ -1950,9 +1950,8 @@ win_update(win_T *wp) if (VIsual_active) { - if (VIsual_active - && (VIsual_mode != wp->w_old_visual_mode - || type == INVERTED_ALL)) + if (VIsual_mode != wp->w_old_visual_mode + || type == INVERTED_ALL) { // If the type of Visual selection changed, redraw the whole // selection. Also when the ownership of the X selection is diff --git a/src/ex_cmds.c b/src/ex_cmds.c index 13cde84fd8..be35845356 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -2411,8 +2411,7 @@ getfile( if (curbufIsChanged()) #endif { - if (other) - --no_wait_return; + --no_wait_return; no_write_message(); retval = GETFILE_NOT_WRITTEN; // file has been changed goto theend; diff --git a/src/fileio.c b/src/fileio.c index 21417c27fe..9ef2dbd3bc 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -4933,7 +4933,7 @@ readdir_core( break; } - if (!ignore && checkitem != NULL) + if (checkitem != NULL) { int r = checkitem(context, item); diff --git a/src/message.c b/src/message.c index af91bd8130..2499723e4d 100644 --- a/src/message.c +++ b/src/message.c @@ -1803,7 +1803,7 @@ str2special( int len = (*mb_ptr2len)(str); // For multi-byte characters check for an illegal byte. - if (has_mbyte && MB_BYTE2LEN(*str) > len) + if (MB_BYTE2LEN(*str) > len) { transchar_nonprint(curbuf, buf, c); *sp = str + 1; diff --git a/src/misc2.c b/src/misc2.c index 38da7e59c4..c23c689c00 100644 --- a/src/misc2.c +++ b/src/misc2.c @@ -596,7 +596,7 @@ check_cursor_col_win(win_T *win) // Make sure that coladd is not more than the char width. // Not for the last character, coladd is then used when the cursor // is actually after the last character. - if (win->w_cursor.col + 1 < len && win->w_cursor.coladd > 0) + if (win->w_cursor.col + 1 < len) { int cs, ce; diff --git a/src/ops.c b/src/ops.c index 1c13dac64a..30e88c7989 100644 --- a/src/ops.c +++ b/src/ops.c @@ -1310,7 +1310,7 @@ op_tilde(oparg_T *oap) changed_lines(oap->start.lnum, oap->start.col, oap->end.lnum + 1, 0L); #ifdef FEAT_NETBEANS_INTG - if (netbeans_active() && did_change) + if (netbeans_active()) { char_u *ptr; int count; diff --git a/src/sign.c b/src/sign.c index e1aae5121f..3d9c387d44 100644 --- a/src/sign.c +++ b/src/sign.c @@ -847,7 +847,7 @@ sign_mark_adjust( if (sign->se_lnum < line1) continue; new_lnum = sign->se_lnum; - if (sign->se_lnum >= line1 && sign->se_lnum <= line2) + if (sign->se_lnum <= line2) { if (amount != MAXLNUM) new_lnum += amount; diff --git a/src/spell.c b/src/spell.c index 81ba049772..5145ce198e 100644 --- a/src/spell.c +++ b/src/spell.c @@ -1371,11 +1371,10 @@ spell_move_to( // the cursor. if (dir == BACKWARD || lnum != wp->w_cursor.lnum - || (lnum == wp->w_cursor.lnum - && (wrapped - || (colnr_T)(curline ? p - buf + len + || (wrapped + || (colnr_T)(curline ? p - buf + len : p - buf) - > wp->w_cursor.col))) + > wp->w_cursor.col)) { #ifdef FEAT_SYN_HL if (has_syntax) diff --git a/src/version.c b/src/version.c index cd3d9cdeed..c6425673da 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4610, /**/ 4609, /**/ diff --git a/src/vim9cmds.c b/src/vim9cmds.c index cc2c041583..072a106a50 100644 --- a/src/vim9cmds.c +++ b/src/vim9cmds.c @@ -1613,8 +1613,7 @@ compile_endtry(char_u *arg, cctx_T *cctx) // End :catch or :finally scope: set instruction index in ISN_TRY // instruction try_isn->isn_arg.tryref.try_ref->try_endtry = instr->ga_len; - if (cctx->ctx_skip != SKIP_YES - && generate_instr(cctx, ISN_ENDTRY) == NULL) + if (generate_instr(cctx, ISN_ENDTRY) == NULL) return NULL; #ifdef FEAT_PROFILE if (cctx->ctx_compile_type == CT_PROFILE) diff --git a/src/window.c b/src/window.c index 435ad037ac..5bda6add97 100644 --- a/src/window.c +++ b/src/window.c @@ -5691,8 +5691,7 @@ frame_setheight(frame_T *curfrp, int height) break; if (run == 2 || curfrp->fr_width == Columns) { - if (height > room + room_cmdline) - height = room + room_cmdline; + height = room + room_cmdline; break; } frame_setheight(curfrp->fr_parent, height @@ -5876,8 +5875,7 @@ frame_setwidth(frame_T *curfrp, int width) break; if (run == 2 || curfrp->fr_height >= ROWS_AVAIL) { - if (width > room) - width = room; + width = room; break; } frame_setwidth(curfrp->fr_parent, width From 81b573d7e55bd48988f298ce8e652d902e9bdeba Mon Sep 17 00:00:00 2001 From: Dominique Pelle Date: Tue, 22 Mar 2022 21:14:55 +0000 Subject: [PATCH 08/18] patch 8.2.4611: typos in tests; one lua line not covered by test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Typos in tests; one lua line not covered by test. Solution: Fix typos. Add test case. (Dominique Pellé, closes #9994) --- src/testdir/test_breakindent.vim | 2 +- src/testdir/test_crypt.vim | 2 +- src/testdir/test_cursorline.vim | 2 +- src/testdir/test_digraph.vim | 2 +- src/testdir/test_gui.vim | 12 ++++++------ src/testdir/test_lua.vim | 12 +++++++++++- src/testdir/test_regexp_latin.vim | 2 +- src/testdir/test_signals.vim | 4 ++-- src/testdir/test_spell.vim | 2 +- src/testdir/test_statusline.vim | 2 +- src/testdir/test_vim9_disassemble.vim | 2 +- src/testdir/test_vim9_expr.vim | 20 ++++++++++---------- src/testdir/test_vimscript.vim | 2 +- src/version.c | 2 ++ 14 files changed, 40 insertions(+), 28 deletions(-) diff --git a/src/testdir/test_breakindent.vim b/src/testdir/test_breakindent.vim index d60b2f4a19..9b6106d7be 100644 --- a/src/testdir/test_breakindent.vim +++ b/src/testdir/test_breakindent.vim @@ -731,7 +731,7 @@ func Test_breakindent20_list() \ "shall make no law ", \ ] call s:compare_lines(expect, lines) - " set mininum indent + " set minimum indent setl briopt=min:5 redraw! let lines = s:screen_lines2(1, 6, 20) diff --git a/src/testdir/test_crypt.vim b/src/testdir/test_crypt.vim index f43c22af01..f3750b3da4 100644 --- a/src/testdir/test_crypt.vim +++ b/src/testdir/test_crypt.vim @@ -172,7 +172,7 @@ func Test_uncrypt_xchacha20_2() call assert_equal(range(1, 4000)->map( {_, v -> string(v)}), getline(1,'$')) set key= w! ++ff=unix - " enryption removed (on MS-Windows the .* matches [unix]) + " encryption removed (on MS-Windows the .* matches [unix]) call assert_match('"Xcrypt_sodium.txt".*4000L, 18893B written', execute(':message')) bw! call delete('Xcrypt_sodium.txt') diff --git a/src/testdir/test_cursorline.vim b/src/testdir/test_cursorline.vim index 2b29007351..c2505e3d58 100644 --- a/src/testdir/test_cursorline.vim +++ b/src/testdir/test_cursorline.vim @@ -162,7 +162,7 @@ func Test_cursorline_screenline() call term_sendkeys(buf, "gj") call VerifyScreenDump(buf, 'Test_'. filename. '_12', {}) if exists("+foldcolumn") && exists("+signcolumn") && exists("+breakindent") - " test with set foldcolumn signcoloumn and breakindent + " test with set foldcolumn signcolumn and breakindent call term_sendkeys(buf, "gg0") call term_sendkeys(buf, ":set breakindent foldcolumn=2 signcolumn=yes\") call VerifyScreenDump(buf, 'Test_'. filename. '_13', {}) diff --git a/src/testdir/test_digraph.vim b/src/testdir/test_digraph.vim index 9cb946c45f..8420b53198 100644 --- a/src/testdir/test_digraph.vim +++ b/src/testdir/test_digraph.vim @@ -51,7 +51,7 @@ func Test_digraphs() call Put_Dig("'e") call Put_Dig("b'") " not defined call assert_equal(["á", "é", "'"], getline(line('.')-2,line('.'))) - " Cicumflex + " Circumflex call Put_Dig("a>") call Put_Dig(">e") call Put_Dig("b>") " not defined diff --git a/src/testdir/test_gui.vim b/src/testdir/test_gui.vim index c2ce50d0d1..7947288c72 100644 --- a/src/testdir/test_gui.vim +++ b/src/testdir/test_gui.vim @@ -274,7 +274,7 @@ func Test_set_balloonexpr() " Multiline balloon using NL new func MyBalloonFuncForMultilineUsingNL() - return "Multiline\nSuppported\nBalloon\nusing NL" + return "Multiline\nSupported\nBalloon\nusing NL" endfunc setl balloonexpr=MyBalloonFuncForMultilineUsingNL() setl ballooneval @@ -289,7 +289,7 @@ func Test_set_balloonexpr() " Multiline balloon using List new func MyBalloonFuncForMultilineUsingList() - return [ 'Multiline', 'Suppported', 'Balloon', 'using List' ] + return [ 'Multiline', 'Supported', 'Balloon', 'using List' ] endfunc setl balloonexpr=MyBalloonFuncForMultilineUsingList() setl ballooneval @@ -1453,25 +1453,25 @@ func Test_gui_findrepl() call test_gui_event('findrepl', args) call assert_equal(['ONE TWO ONE', 'Twoo ONE TWO ONEo'], getline(1, '$')) - " Find next occurance of a string (in a find dialog) + " Find next occurrence of a string (in a find dialog) call cursor(1, 11) let args = #{find_text: 'TWO', repl_text: '', flags: 0x11, forward: 1} call test_gui_event('findrepl', args) call assert_equal([2, 10], [line('.'), col('.')]) - " Find previous occurances of a string (in a find dialog) + " Find previous occurrences of a string (in a find dialog) call cursor(1, 11) let args = #{find_text: 'TWO', repl_text: '', flags: 0x11, forward: 0} call test_gui_event('findrepl', args) call assert_equal([1, 5], [line('.'), col('.')]) - " Find next occurance of a string (in a replace dialog) + " Find next occurrence of a string (in a replace dialog) call cursor(1, 1) let args = #{find_text: 'Twoo', repl_text: '', flags: 0x2, forward: 1} call test_gui_event('findrepl', args) call assert_equal([2, 1], [line('.'), col('.')]) - " Replace only the next occurance of a string (once) + " Replace only the next occurrence of a string (once) call cursor(1, 5) let args = #{find_text: 'TWO', repl_text: 'two', flags: 0x3, forward: 1} call test_gui_event('findrepl', args) diff --git a/src/testdir/test_lua.vim b/src/testdir/test_lua.vim index 1f2df10949..e45da6afc7 100644 --- a/src/testdir/test_lua.vim +++ b/src/testdir/test_lua.vim @@ -1208,11 +1208,21 @@ func Test_lua_debug() call WaitForAssert({-> assert_equal('42', term_getline(buf, 9))}) call WaitForAssert({-> assert_equal('lua_debug> ', term_getline(buf, 10))}) + call term_sendkeys(buf, "-\n") + call WaitForAssert({-> assert_equal("(debug command):1: unexpected symbol near '-'", + \ term_getline(buf, 9))}) + call WaitForAssert({-> assert_equal('lua_debug> ', term_getline(buf, 10))}) + call term_sendkeys(buf, "cont\n") call WaitForAssert({-> assert_match(' All$', term_getline(buf, 10))}) + " Entering an empty line also exits the debugger. + call term_sendkeys(buf, ":lua debug.debug()\n") + call WaitForAssert({-> assert_equal('lua_debug> ', term_getline(buf, 10))}) + call term_sendkeys(buf, "\n") + call WaitForAssert({-> assert_match(' All$', term_getline(buf, 10))}) + call StopVimInTerminal(buf) - call delete('XtestLuaDebug.vim') endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_regexp_latin.vim b/src/testdir/test_regexp_latin.vim index 166488b538..71915be9ed 100644 --- a/src/testdir/test_regexp_latin.vim +++ b/src/testdir/test_regexp_latin.vim @@ -799,7 +799,7 @@ func Test_matchstr_with_ze() bwipe! endfunc -" Check a pattern with a look beind crossing a line boundary +" Check a pattern with a look behind crossing a line boundary func Test_lookbehind_across_line() new call append(0, ['Behind:', 'asdfasd 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 term_sendkeys(buf, ":call setline(1, 'INTERRUPTED')\n") + call WaitForAssert({-> assert_equal('INTERRUPTED', term_getline(buf, 1))}) call StopVimInTerminal(buf) endfunc diff --git a/src/testdir/test_spell.vim b/src/testdir/test_spell.vim index 804c5dede7..94a072eb8c 100644 --- a/src/testdir/test_spell.vim +++ b/src/testdir/test_spell.vim @@ -417,7 +417,7 @@ func Test_spellsuggest_option_expr() bwipe! endfunc -" Test for 'spellsuggest' expr errrors +" Test for 'spellsuggest' expr errors func Test_spellsuggest_expr_errors() " 'spellsuggest' func MySuggest() diff --git a/src/testdir/test_statusline.vim b/src/testdir/test_statusline.vim index 28d8798fe8..eaf152593d 100644 --- a/src/testdir/test_statusline.vim +++ b/src/testdir/test_statusline.vim @@ -260,7 +260,7 @@ func Test_statusline() call assert_match('^vimLineComment\s*$', s:get_statusline()) syntax off - "%{%expr%}: evaluates enxpressions present in result of expr + "%{%expr%}: evaluates expressions present in result of expr func! Inner_eval() return '%n some other text' endfunc diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim index 26ecddedb2..77c4675888 100644 --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -2004,7 +2004,7 @@ def s:FalsyOp() echo "" ?? "empty string" enddef -def Test_dsassemble_falsy_op() +def Test_disassemble_falsy_op() var res = execute('disass s:FalsyOp') assert_match('\\d*_FalsyOp\_s*' .. 'echo g:flag ?? "yes"\_s*' .. diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index dcce616de1..41e398aea4 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -563,7 +563,7 @@ let ablob = 0z01ab let alist = [2, 3, 4] let adict = #{aaa: 2, bbb: 8} -" test == comperator +" test == comparator def Test_expr4_equal() var lines =<< trim END var trueVar = true @@ -902,7 +902,7 @@ def Test_expr4_wrong_type() 'echo n < true'], 'E1072:', 2) enddef -" test != comperator +" test != comparator def Test_expr4_notequal() var lines =<< trim END var trueVar = true @@ -987,7 +987,7 @@ def Test_expr4_notequal() v9.CheckDefAndScriptSuccess(lines) enddef -" test > comperator +" test > comparator def Test_expr4_greater() var lines =<< trim END assert_true(2 > 0) @@ -1013,7 +1013,7 @@ def Test_expr4_greater() v9.CheckDefAndScriptSuccess(lines) enddef -" test >= comperator +" test >= comparator def Test_expr4_greaterequal() var lines =<< trim END assert_true(2 >= 0) @@ -1034,7 +1034,7 @@ def Test_expr4_greaterequal() v9.CheckDefAndScriptSuccess(lines) enddef -" test < comperator +" test < comparator def Test_expr4_smaller() var lines =<< trim END assert_false(2 < 0) @@ -1056,7 +1056,7 @@ def Test_expr4_smaller() v9.CheckDefAndScriptSuccess(lines) enddef -" test <= comperator +" test <= comparator def Test_expr4_smallerequal() var lines =<< trim END assert_false(2 <= 0) @@ -1081,7 +1081,7 @@ def Test_expr4_smallerequal() v9.CheckDefAndScriptSuccess(lines) enddef -" test =~ comperator +" test =~ comparator def Test_expr4_match() var lines =<< trim END assert_equal(false, '2' =~ '0') @@ -1098,7 +1098,7 @@ def Test_expr4_match() v9.CheckDefAndScriptSuccess(lines) enddef -" test !~ comperator +" test !~ comparator def Test_expr4_nomatch() var lines =<< trim END assert_equal(true, '2' !~ '0') @@ -1110,7 +1110,7 @@ def Test_expr4_nomatch() v9.CheckDefAndScriptSuccess(lines) enddef -" test is comperator +" test is comparator def Test_expr4_is() var lines =<< trim END var mylist = [2] @@ -1128,7 +1128,7 @@ def Test_expr4_is() v9.CheckDefAndScriptSuccess(lines) enddef -" test isnot comperator +" test isnot comparator def Test_expr4_isnot() var lines =<< trim END var mylist = [2] diff --git a/src/testdir/test_vimscript.vim b/src/testdir/test_vimscript.vim index ce3dbca681..38b2585a3a 100644 --- a/src/testdir/test_vimscript.vim +++ b/src/testdir/test_vimscript.vim @@ -3631,7 +3631,7 @@ endfunc " exceptions. "------------------------------------------------------------------------------- -func Test_execption_info_for_error() +func Test_exception_info_for_error() CheckEnglish let test =<< trim [CODE] diff --git a/src/version.c b/src/version.c index c6425673da..48eb0c5b01 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4611, /**/ 4610, /**/ From a915fa010330ee7212e06d3511acd363d04d2d28 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 23 Mar 2022 11:29:15 +0000 Subject: [PATCH 09/18] patch 8.2.4612: Vim9: cannot use a recursive call in a nested function Problem: Vim9: cannot use a recursive call in a nested function. (Sergey Vlasov) Solution: Define the funcref before compiling the function. (closes #9989) --- src/proto/vim9instr.pro | 2 +- src/testdir/test_vim9_func.vim | 19 +++++++++++++++ src/version.c | 2 ++ src/vim9compile.c | 43 ++++++++++++++++++++-------------- src/vim9expr.c | 2 +- src/vim9instr.c | 5 +++- 6 files changed, 52 insertions(+), 21 deletions(-) diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro index 8da559745a..76f3b21029 100644 --- a/src/proto/vim9instr.pro +++ b/src/proto/vim9instr.pro @@ -38,7 +38,7 @@ int generate_OLDSCRIPT(cctx_T *cctx, isntype_T isn_type, char_u *name, int sid, int generate_VIM9SCRIPT(cctx_T *cctx, isntype_T isn_type, int sid, int idx, type_T *type); int generate_NEWLIST(cctx_T *cctx, int count); int generate_NEWDICT(cctx_T *cctx, int count); -int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc); +int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp); int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name); int generate_DEF(cctx_T *cctx, char_u *name, size_t len); int generate_JUMP(cctx_T *cctx, jumpwhen_T when, int where); diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index e884f0e3c4..06168e477e 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -876,6 +876,25 @@ def Test_nested_function() END v9.CheckScriptSuccess(lines) + # nested function with recursive call + lines =<< trim END + vim9script + + def MyFunc(): number + def Fib(n: number): number + if n < 2 + return 1 + endif + return Fib(n - 2) + Fib(n - 1) + enddef + + return Fib(5) + enddef + + assert_equal(8, MyFunc()) + END + v9.CheckScriptSuccess(lines) + lines =<< trim END vim9script def Outer() diff --git a/src/version.c b/src/version.c index 48eb0c5b01..f304ef3e6f 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4612, /**/ 4611, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index fde818882e..1eb6ce22fc 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -818,6 +818,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free) ufunc_T *ufunc; int r = FAIL; compiletype_T compile_type; + isn_T *funcref_isn = NULL; if (eap->forceit) { @@ -913,6 +914,27 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free) } } + // Define the funcref before compiling, so that it is found by any + // recursive call. + if (is_global) + { + r = generate_NEWFUNC(cctx, lambda_name, func_name); + func_name = NULL; + lambda_name = NULL; + } + else + { + // Define a local variable for the function reference. + lvar_T *lvar = reserve_local(cctx, func_name, name_end - name_start, + TRUE, ufunc->uf_func_type); + + if (lvar == NULL) + goto theend; + if (generate_FUNCREF(cctx, ufunc, &funcref_isn) == FAIL) + goto theend; + r = generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL); + } + compile_type = get_compile_type(ufunc); #ifdef FEAT_PROFILE // If the outer function is profiled, also compile the nested function for @@ -934,24 +956,9 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free) compile_def_function(ufunc, FALSE, CT_NONE, cctx); #endif - if (is_global) - { - r = generate_NEWFUNC(cctx, lambda_name, func_name); - func_name = NULL; - lambda_name = NULL; - } - else - { - // Define a local variable for the function reference. - lvar_T *lvar = reserve_local(cctx, func_name, name_end - name_start, - TRUE, ufunc->uf_func_type); - - if (lvar == NULL) - goto theend; - if (generate_FUNCREF(cctx, ufunc) == FAIL) - goto theend; - r = generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL); - } + // If a FUNCREF instruction was generated, set the index after compiling. + if (funcref_isn != NULL && ufunc->uf_def_status == UF_COMPILED) + funcref_isn->isn_arg.funcref.fr_dfunc_idx = ufunc->uf_dfunc_idx; theend: vim_free(lambda_name); diff --git a/src/vim9expr.c b/src/vim9expr.c index 3a329ccf43..8c7d0b0e95 100644 --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -1040,7 +1040,7 @@ compile_lambda(char_u **arg, cctx_T *cctx) // The function reference count will be 1. When the ISN_FUNCREF // instruction is deleted the reference count is decremented and the // function is freed. - return generate_FUNCREF(cctx, ufunc); + return generate_FUNCREF(cctx, ufunc, NULL); } func_ptr_unref(ufunc); diff --git a/src/vim9instr.c b/src/vim9instr.c index 7f23884f3c..f0206211d8 100644 --- a/src/vim9instr.c +++ b/src/vim9instr.c @@ -1172,9 +1172,10 @@ generate_NEWDICT(cctx_T *cctx, int count) /* * Generate an ISN_FUNCREF instruction. + * "isnp" is set to the instruction, so that fr_dfunc_idx can be set later. */ int -generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc) +generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp) { isn_T *isn; type_T *type; @@ -1182,6 +1183,8 @@ generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc) RETURN_OK_IF_SKIP(cctx); if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL) return FAIL; + if (isnp != NULL) + *isnp = isn; if (ufunc->uf_def_status == UF_NOT_COMPILED) isn->isn_arg.funcref.fr_func_name = vim_strsave(ufunc->uf_name); else From 3c5999e53d9f35a30abefb7224f66a75c8ffb009 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 23 Mar 2022 13:54:51 +0000 Subject: [PATCH 10/18] patch 8.2.4613: return type of swapfile_unchanged() is wrong Problem: Return type of swapfile_unchanged() is wrong. Solution: Use "int". (closes #10000 Yeah!) --- src/memline.c | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/memline.c b/src/memline.c index e1a4cb340d..7b453cd474 100644 --- a/src/memline.c +++ b/src/memline.c @@ -2261,7 +2261,7 @@ swapfile_info(char_u *fname) * Return TRUE if the swap file looks OK and there are no changes, thus it can * be safely deleted. */ - static time_t + static int swapfile_unchanged(char_u *fname) { stat_T st; diff --git a/src/version.c b/src/version.c index f304ef3e6f..695ddaf0c7 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4613, /**/ 4612, /**/ From c20e46a4e3efcd408ef132872238144ea34f7ae5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 23 Mar 2022 14:55:23 +0000 Subject: [PATCH 11/18] patch 8.2.4614: redrawing too much when 'cursorline' is set Problem: Redrawing too much when 'cursorline' is set and jumping around. Solution: Rely on win_update() to redraw the current and previous cursor line, do not mark lines as modified. (closes #9996) --- src/drawline.c | 7 +------ src/drawscreen.c | 13 ++++++++----- src/move.c | 28 ++-------------------------- src/option.c | 5 ----- src/proto/move.pro | 1 - src/version.c | 2 ++ 6 files changed, 13 insertions(+), 43 deletions(-) diff --git a/src/drawline.c b/src/drawline.c index 747a1e33b0..679e4cae4e 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -945,8 +945,7 @@ win_line( if (wp->w_p_cul && lnum == wp->w_cursor.lnum) { // Do not show the cursor line in the text when Visual mode is active, - // because it's not clear what is selected then. Do update - // w_last_cursorline. + // because it's not clear what is selected then. if (!(wp == curwin && VIsual_active) && wp->w_p_culopt_flags != CULOPT_NBR) { @@ -971,18 +970,14 @@ win_line( else # endif line_attr = cul_attr; - wp->w_last_cursorline = wp->w_cursor.lnum; } else { line_attr_save = line_attr; - wp->w_last_cursorline = 0; margin_columns_win(wp, &left_curline_col, &right_curline_col); } area_highlighting = TRUE; } - else - wp->w_last_cursorline = wp->w_cursor.lnum; } #endif diff --git a/src/drawscreen.c b/src/drawscreen.c index a562c4d84a..a902397cd7 100644 --- a/src/drawscreen.c +++ b/src/drawscreen.c @@ -1468,9 +1468,6 @@ win_update(win_T *wp) # define DID_FOLD 3 // updated a folded line int did_update = DID_NONE; linenr_T syntax_last_parsed = 0; // last parsed text line - // remember the current w_last_cursorline, it changes when drawing the new - // cursor line - linenr_T last_cursorline = wp->w_last_cursorline; #endif linenr_T mod_top = 0; linenr_T mod_bot = 0; @@ -2245,8 +2242,8 @@ win_update(win_T *wp) #endif )))) #ifdef FEAT_SYN_HL - || (wp->w_p_cul && (lnum == wp->w_cursor.lnum - || lnum == last_cursorline)) + || (wp->w_p_cul && lnum == wp->w_cursor.lnum) + || lnum == wp->w_last_cursorline #endif ) { @@ -2551,6 +2548,12 @@ win_update(win_T *wp) // End of loop over all window lines. +#ifdef FEAT_SYN_HL + // Now that the window has been redrawn with the old and new cursor line, + // update w_last_cursorline. + wp->w_last_cursorline = wp->w_p_cul ? wp->w_cursor.lnum : 0; +#endif + #ifdef FEAT_VTP // Rewrite the character at the end of the screen line. // See the version that was fixed. diff --git a/src/move.c b/src/move.c index 1c88e6ebcc..571ec8c317 100644 --- a/src/move.c +++ b/src/move.c @@ -115,14 +115,6 @@ comp_botline(win_T *wp) set_empty_rows(wp, done); } -#ifdef FEAT_SYN_HL - void -reset_cursorline(void) -{ - curwin->w_last_cursorline = 0; -} -#endif - /* * Redraw when w_cline_row changes and 'relativenumber' or 'cursorline' is * set. @@ -138,24 +130,8 @@ redraw_for_cursorline(win_T *wp) && (wp->w_valid & VALID_CROW) == 0 && !pum_visible()) { - if (wp->w_p_rnu) - // win_line() will redraw the number column only. - redraw_win_later(wp, VALID); -#ifdef FEAT_SYN_HL - if (wp->w_p_cul) - { - if (wp->w_redr_type <= VALID && wp->w_last_cursorline != 0) - { - // "w_last_cursorline" may be outdated, worst case we redraw - // too much. This is optimized for moving the cursor around in - // the current window. - redrawWinline(wp, wp->w_last_cursorline); - redrawWinline(wp, wp->w_cursor.lnum); - } - else - redraw_win_later(wp, SOME_VALID); - } -#endif + // win_line() will redraw the number column and cursorline only. + redraw_win_later(wp, VALID); } } diff --git a/src/option.c b/src/option.c index 936906649d..71b71ba53c 100644 --- a/src/option.c +++ b/src/option.c @@ -2782,11 +2782,6 @@ set_bool_option( p_lrm = !p_lnr; #endif -#ifdef FEAT_SYN_HL - else if ((int *)varp == &curwin->w_p_cul && !value && old_value) - reset_cursorline(); -#endif - #ifdef FEAT_PERSISTENT_UNDO // 'undofile' else if ((int *)varp == &curbuf->b_p_udf || (int *)varp == &p_udf) diff --git a/src/proto/move.pro b/src/proto/move.pro index d78623acc5..e6a06011c4 100644 --- a/src/proto/move.pro +++ b/src/proto/move.pro @@ -1,5 +1,4 @@ /* move.c */ -void reset_cursorline(void); void redraw_for_cursorline(win_T *wp); void update_topline_redraw(void); void update_topline(void); diff --git a/src/version.c b/src/version.c index 695ddaf0c7..ad4f3c8139 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4614, /**/ 4613, /**/ From ac48506ac62b2ece523d5af6ea6c95b699d70b94 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 23 Mar 2022 19:45:01 +0000 Subject: [PATCH 12/18] patch 8.2.4615: mapping with escaped bar does not work in :def function Problem: Mapping with escaped bar does not work in :def function. (Sergey Vlasov) Solution: Do not remove the backslash. (closes #10002) --- src/ex_docmd.c | 14 +++++++++----- src/proto/ex_docmd.pro | 2 +- src/syntax.c | 2 +- src/testdir/test_vim9_cmd.vim | 15 +++++++++++++-- src/version.c | 2 ++ src/vim9cmds.c | 2 +- 6 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/ex_docmd.c b/src/ex_docmd.c index c33dcbca01..a35924a570 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -2275,7 +2275,7 @@ do_one_cmd( */ if ((ea.argt & EX_TRLBAR) && !ea.usefilter) { - separate_nextcmd(&ea); + separate_nextcmd(&ea, FALSE); } else if (ea.cmdidx == CMD_bang || ea.cmdidx == CMD_terminal @@ -5081,9 +5081,10 @@ repl_cmdline( /* * Check for '|' to separate commands and '"' to start comments. + * If "keep_backslash" is TRUE do not remove any backslash. */ void -separate_nextcmd(exarg_T *eap) +separate_nextcmd(exarg_T *eap, int keep_backslash) { char_u *p; @@ -5097,7 +5098,7 @@ separate_nextcmd(exarg_T *eap) { if (*p == Ctrl_V) { - if (eap->argt & (EX_CTRLV | EX_XFILE)) + if ((eap->argt & (EX_CTRLV | EX_XFILE)) || keep_backslash) ++p; // skip CTRL-V and next char else // remove CTRL-V and skip next char @@ -5144,8 +5145,11 @@ separate_nextcmd(exarg_T *eap) if ((vim_strchr(p_cpo, CPO_BAR) == NULL || !(eap->argt & EX_CTRLV)) && *(p - 1) == '\\') { - STRMOVE(p - 1, p); // remove the '\' - --p; + if (!keep_backslash) + { + STRMOVE(p - 1, p); // remove the '\' + --p; + } } else { diff --git a/src/proto/ex_docmd.pro b/src/proto/ex_docmd.pro index 3be7471070..40f2649829 100644 --- a/src/proto/ex_docmd.pro +++ b/src/proto/ex_docmd.pro @@ -26,7 +26,7 @@ long excmd_get_argt(cmdidx_T idx); char_u *skip_range(char_u *cmd_start, int skip_star, int *ctx); void ex_ni(exarg_T *eap); int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp); -void separate_nextcmd(exarg_T *eap); +void separate_nextcmd(exarg_T *eap, int keep_backslash); char_u *skip_cmd_arg(char_u *p, int rembs); int get_bad_opt(char_u *p, exarg_T *eap); int ends_excmd(int c); diff --git a/src/syntax.c b/src/syntax.c index 6683d2a3ed..74687cb4d9 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -4764,7 +4764,7 @@ syn_cmd_include(exarg_T *eap, int syncing UNUSED) * filename to include. */ eap->argt |= (EX_XFILE | EX_NOSPC); - separate_nextcmd(eap); + separate_nextcmd(eap, FALSE); if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg)) { // For an absolute path, "$VIM/..." or ".." we ":source" the diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index b15028adf4..38ee7f23fe 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -1178,8 +1178,19 @@ def Test_map_command() nnoremap :echo 'hit F3 #' assert_equal(":echo 'hit F3 #'", maparg("", "n")) END - v9.CheckDefSuccess(lines) - v9.CheckScriptSuccess(['vim9script'] + lines) + v9.CheckDefAndScriptSuccess(lines) + + # backslash before bar is not removed + lines =<< trim END + vim9script + + def Init() + noremap MyFunc('a') \| MyFunc('b') + enddef + Init() + unmap + END + v9.CheckScriptSuccess(lines) enddef def Test_normal_command() diff --git a/src/version.c b/src/version.c index ad4f3c8139..dd2330e4a7 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4615, /**/ 4614, /**/ diff --git a/src/vim9cmds.c b/src/vim9cmds.c index 072a106a50..483b1f34b2 100644 --- a/src/vim9cmds.c +++ b/src/vim9cmds.c @@ -1848,7 +1848,7 @@ compile_exec(char_u *line_arg, exarg_T *eap, cctx_T *cctx) if ((argt & EX_TRLBAR) && !usefilter) { eap->argt = argt; - separate_nextcmd(eap); + separate_nextcmd(eap, TRUE); if (eap->nextcmd != NULL) nextcmd = eap->nextcmd; } From 98b7fe725ec342d28d7c86293098b233c57c4af9 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 23 Mar 2022 21:36:27 +0000 Subject: [PATCH 13/18] patch 8.2.4616: Vim9: Declarations in a {} block of a user command remain Problem: Vim9: Declarations in a {} block of a user command do not use Vim9 rules if defined in a legacy script. (Yegappan Lakshmanan) Solution: Pretend the script is Vim9 script. --- src/testdir/test_usercommands.vim | 36 +++++++++++++++++++++++++++++++ src/usercmd.c | 20 +++++++++++++++++ src/version.c | 2 ++ 3 files changed, 58 insertions(+) diff --git a/src/testdir/test_usercommands.vim b/src/testdir/test_usercommands.vim index 00bbde4906..2feeef03f9 100644 --- a/src/testdir/test_usercommands.vim +++ b/src/testdir/test_usercommands.vim @@ -798,4 +798,40 @@ func Test_multibyte_in_usercmd() delcommand SubJapanesePeriodToDot endfunc +" Declaring a variable in a {} uses Vim9 script rules, even when defined in a +" legacy script. +func Test_block_declaration_legacy_script() + let lines =<< trim END + command -range Rename { + var save = @a + @a = 'something' + g:someExpr = @a + @a = save + } + END + call writefile(lines, 'Xlegacy') + source Xlegacy + + let lines =<< trim END + let @a = 'saved' + Rename + call assert_equal('something', g:someExpr) + call assert_equal('saved', @a) + + let g:someExpr = 'xxx' + let @a = 'also' + Rename + call assert_equal('something', g:someExpr) + call assert_equal('also', @a) + END + call writefile(lines, 'Xother') + source Xother + + unlet g:someExpr + call delete('Xlegacy') + call delete('Xother') + delcommand Rename +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/usercmd.c b/src/usercmd.c index 70dbbb03ba..40d951f6dc 100644 --- a/src/usercmd.c +++ b/src/usercmd.c @@ -22,6 +22,7 @@ typedef struct ucmd int uc_compl; // completion type cmd_addr_T uc_addr_type; // The command's address type sctx_T uc_script_ctx; // SCTX where the command was defined + int uc_flags; // some UC_ flags # ifdef FEAT_EVAL char_u *uc_compl_arg; // completion argument if any # endif @@ -1038,6 +1039,7 @@ uc_add_command( cmd->uc_script_ctx = current_sctx; if (flags & UC_VIM9) cmd->uc_script_ctx.sc_version = SCRIPT_VERSION_VIM9; + cmd->uc_flags = flags & UC_VIM9; #ifdef FEAT_EVAL cmd->uc_script_ctx.sc_lnum += SOURCING_LNUM; cmd->uc_compl_arg = compl_arg; @@ -1725,6 +1727,9 @@ do_ucmd(exarg_T *eap) ucmd_T *cmd; sctx_T save_current_sctx; int restore_current_sctx = FALSE; +#ifdef FEAT_EVAL + int restore_script_version = 0; +#endif if (eap->cmdidx == CMD_USER) cmd = USER_CMD(eap->useridx); @@ -1830,6 +1835,14 @@ do_ucmd(exarg_T *eap) current_sctx.sc_version = cmd->uc_script_ctx.sc_version; #ifdef FEAT_EVAL current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid; + if (cmd->uc_flags & UC_VIM9) + { + // In a {} block variables use Vim9 script rules, even in a legacy + // script. + restore_script_version = + SCRIPT_ITEM(current_sctx.sc_sid)->sn_version; + SCRIPT_ITEM(current_sctx.sc_sid)->sn_version = SCRIPT_VERSION_VIM9; + } #endif } @@ -1839,7 +1852,14 @@ do_ucmd(exarg_T *eap) // Careful: Do not use "cmd" here, it may have become invalid if a user // command was added. if (restore_current_sctx) + { +#ifdef FEAT_EVAL + if (restore_script_version != 0) + SCRIPT_ITEM(current_sctx.sc_sid)->sn_version = + restore_script_version; +#endif current_sctx = save_current_sctx; + } vim_free(buf); vim_free(split_buf); } diff --git a/src/version.c b/src/version.c index dd2330e4a7..ba16773e1d 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4616, /**/ 4615, /**/ From 454ce6737cadb82886f1fc0eb9e8666cc59ae42b Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Thu, 24 Mar 2022 11:22:13 +0000 Subject: [PATCH 14/18] patch 8.2.4617: no completion for :scriptnames Problem: No completion for :scriptnames. Solution: Implement :scriptnames completion. (Yegappan Lakshmanan, closes #10005) --- runtime/doc/builtin.txt | 6 ++- src/cmdexpand.c | 73 ++++++++++++++++++++++++++++------- src/ex_cmds.h | 2 +- src/scriptfile.c | 12 ++++-- src/testdir/test_cmdline.vim | 27 +++++++++++++ src/testdir/test_quickfix.vim | 35 +++++++++++++++++ src/usercmd.c | 1 + src/version.c | 2 + src/vim.h | 1 + 9 files changed, 139 insertions(+), 20 deletions(-) diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 602d9098e6..2d615c5ac2 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -3256,6 +3256,7 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()* messages |:messages| suboptions option options packadd optional package |pack-add| names + scriptnames sourced script names |:scriptnames| shellcmd Shell command sign |:sign| suboptions syntax syntax file names |'syntax'| @@ -3275,7 +3276,10 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()* If the 'wildoptions' option contains 'fuzzy', then fuzzy matching is used to get the completion matches. Otherwise - regular expression matching is used. + regular expression matching is used. Thus this function + follows the user preference, what happens on the command line. + If you do not want this you can make 'wildoptions' empty + before calling getcompletion() and restore it afterwards. If {type} is "cmdline", then the |cmdline-completion| result is returned. For example, to complete the possible values after diff --git a/src/cmdexpand.c b/src/cmdexpand.c index defc282dbb..ca669c0798 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -1709,6 +1709,24 @@ set_context_in_breakadd_cmd(expand_T *xp, char_u *arg, cmdidx_T cmdidx) return NULL; } + + static char_u * +set_context_in_scriptnames_cmd(expand_T *xp, char_u *arg) +{ + char_u *p; + + xp->xp_context = EXPAND_NOTHING; + xp->xp_pattern = NULL; + + p = skipwhite(arg); + if (VIM_ISDIGIT(*p)) + return NULL; + + xp->xp_context = EXPAND_SCRIPTNAMES; + xp->xp_pattern = p; + + return NULL; +} #endif /* @@ -2072,6 +2090,9 @@ set_context_by_cmdname( case CMD_profdel: case CMD_breakdel: return set_context_in_breakadd_cmd(xp, arg, cmdidx); + + case CMD_scriptnames: + return set_context_in_scriptnames_cmd(xp, arg); #endif default: @@ -2495,6 +2516,23 @@ get_breakadd_arg(expand_T *xp UNUSED, int idx) } return NULL; } + +/* + * Function given to ExpandGeneric() to obtain the possible arguments for the + * ":scriptnames" command. + */ + static char_u * +get_scriptnames_arg(expand_T *xp UNUSED, int idx) +{ + scriptitem_T *si; + + if (!SCRIPT_ID_VALID(idx + 1)) + return NULL; + + si = SCRIPT_ITEM(idx + 1); + home_replace(NULL, si->sn_name, NameBuff, MAXPATHL, TRUE); + return NameBuff; +} #endif /* @@ -2584,6 +2622,7 @@ ExpandOther( {EXPAND_ARGLIST, get_arglist_name, TRUE, FALSE}, #ifdef FEAT_EVAL {EXPAND_BREAKPOINT, get_breakadd_arg, TRUE, TRUE}, + {EXPAND_SCRIPTNAMES, get_scriptnames_arg, TRUE, FALSE}, #endif }; int i; @@ -2791,6 +2830,8 @@ ExpandGeneric( int score = 0; int fuzzy; int match; + int sort_matches = FALSE; + int funcsort = FALSE; fuzzy = cmdline_fuzzy_complete(pat); *matches = NULL; @@ -2878,14 +2919,25 @@ ExpandGeneric( if (ga.ga_len == 0) return OK; - // Sort the results. Keep menu's in the specified order. + // sort the matches when using regular expression matching and sorting + // applies to the completion context. Menus and scriptnames should be kept + // in the specified order. if (!fuzzy && xp->xp_context != EXPAND_MENUNAMES - && xp->xp_context != EXPAND_MENUS) + && xp->xp_context != EXPAND_MENUS + && xp->xp_context != EXPAND_SCRIPTNAMES) + sort_matches = TRUE; + + // functions should be sorted to the end. + if (xp->xp_context == EXPAND_EXPRESSION + || xp->xp_context == EXPAND_FUNCTIONS + || xp->xp_context == EXPAND_USER_FUNC + || xp->xp_context == EXPAND_DISASSEMBLE) + funcsort = TRUE; + + // Sort the matches. + if (sort_matches) { - if (xp->xp_context == EXPAND_EXPRESSION - || xp->xp_context == EXPAND_FUNCTIONS - || xp->xp_context == EXPAND_USER_FUNC - || xp->xp_context == EXPAND_DISASSEMBLE) + if (funcsort) // functions should be sorted to the end. qsort((void *)ga.ga_data, (size_t)ga.ga_len, sizeof(char_u *), sort_func_compare); @@ -2900,15 +2952,6 @@ ExpandGeneric( } else { - int funcsort = FALSE; - - if (xp->xp_context == EXPAND_EXPRESSION - || xp->xp_context == EXPAND_FUNCTIONS - || xp->xp_context == EXPAND_USER_FUNC - || xp->xp_context == EXPAND_DISASSEMBLE) - // functions should be sorted to the end. - funcsort = TRUE; - if (fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len, funcsort) == FAIL) return FAIL; diff --git a/src/ex_cmds.h b/src/ex_cmds.h index c8ebcf6d94..e4f9bb05db 100644 --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -1356,7 +1356,7 @@ EXCMD(CMD_sbrewind, "sbrewind", ex_brewind, EX_CMDARG|EX_TRLBAR, ADDR_NONE), EXCMD(CMD_scriptnames, "scriptnames", ex_scriptnames, - EX_BANG|EX_RANGE|EX_COUNT|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, + EX_BANG|EX_FILES|EX_RANGE|EX_COUNT|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, ADDR_OTHER), EXCMD(CMD_scriptencoding, "scriptencoding", ex_scriptencoding, EX_WORD1|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, diff --git a/src/scriptfile.c b/src/scriptfile.c index ae46e7a86d..a334b2f9fc 100644 --- a/src/scriptfile.c +++ b/src/scriptfile.c @@ -1769,14 +1769,20 @@ ex_scriptnames(exarg_T *eap) { int i; - if (eap->addr_count > 0) + if (eap->addr_count > 0 || *eap->arg != NUL) { // :script {scriptId}: edit the script - if (!SCRIPT_ID_VALID(eap->line2)) + if (eap->addr_count > 0 && !SCRIPT_ID_VALID(eap->line2)) emsg(_(e_invalid_argument)); else { - eap->arg = SCRIPT_ITEM(eap->line2)->sn_name; + if (eap->addr_count > 0) + eap->arg = SCRIPT_ITEM(eap->line2)->sn_name; + else + { + expand_env(eap->arg, NameBuff, MAXPATHL); + eap->arg = NameBuff; + } do_exedit(eap, NULL); } return; diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index 299210b878..d337442f99 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -3257,4 +3257,31 @@ func Test_cmdline_complete_breakdel() call assert_equal("\"breakdel here ", @:) endfunc +" Test for :scriptnames argument completion +func Test_cmdline_complete_scriptnames() + set wildmenu + call writefile(['let a = 1'], 'Xa1b2c3.vim') + source Xa1b2c3.vim + call feedkeys(":script \\\\\"\", 'tx') + call assert_match("\"script .*Xa1b2c3.vim$", @:) + call feedkeys(":script \\\\\"\", 'tx') + call assert_match("\"script .*Xa1b2c3.vim$", @:) + call feedkeys(":script b2c3\\\"\", 'tx') + call assert_equal("\"script b2c3", @:) + call feedkeys(":script 2\\\"\", 'tx') + call assert_match("\"script 2\$", @:) + call feedkeys(":script \\\ \\\"\", 'tx') + call assert_match("\"script .*Xa1b2c3.vim $", @:) + call feedkeys(":script \\\\"\", 'tx') + call assert_equal("\"script ", @:) + call assert_match('Xa1b2c3.vim$', getcompletion('.*Xa1b2.*', 'scriptnames')[0]) + call assert_equal([], getcompletion('Xa1b2', 'scriptnames')) + new + call feedkeys(":script \\\\", 'tx') + call assert_equal('Xa1b2c3.vim', fnamemodify(@%, ':t')) + bw! + call delete('Xa1b2c3.vim') + set wildmenu& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index 42b8193767..f5f385f8ca 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -6202,4 +6202,39 @@ 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 + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/usercmd.c b/src/usercmd.c index 40d951f6dc..0e9d712e31 100644 --- a/src/usercmd.c +++ b/src/usercmd.c @@ -93,6 +93,7 @@ static struct {EXPAND_USER_VARS, "var"}, #if defined(FEAT_EVAL) {EXPAND_BREAKPOINT, "breakpoint"}, + {EXPAND_SCRIPTNAMES, "scriptnames"}, #endif {0, NULL} }; diff --git a/src/version.c b/src/version.c index ba16773e1d..17d15c0f89 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4617, /**/ 4616, /**/ diff --git a/src/vim.h b/src/vim.h index d7a2464656..0a923f16d0 100644 --- a/src/vim.h +++ b/src/vim.h @@ -802,6 +802,7 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring); #define EXPAND_DIFF_BUFFERS 49 #define EXPAND_DISASSEMBLE 50 #define EXPAND_BREAKPOINT 51 +#define EXPAND_SCRIPTNAMES 52 // Values for exmode_active (0 is no exmode) #define EXMODE_NORMAL 1 From f4f0525c34d2aa32f214155b0dadcd274ed05dd1 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 24 Mar 2022 13:08:36 +0000 Subject: [PATCH 15/18] patch 8.2.4618: cmdline completion does not recognize single letter commands Problem: Command line completion does not recognize single letter commands. Solution: Use the condition from find_ex_command(). --- src/ex_docmd.c | 65 +++++++++++++++++++++++++++++++------------------- src/version.c | 2 ++ 2 files changed, 42 insertions(+), 25 deletions(-) diff --git a/src/ex_docmd.c b/src/ex_docmd.c index a35924a570..7c2fe3deb7 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -3420,6 +3420,38 @@ skip_option_env_lead(char_u *start) } #endif +/* + * Return TRUE and set "*idx" if "p" points to a one letter command. + * If not in Vim9 script: + * - The 'k' command can directly be followed by any character. + * - The 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r' + * but :sre[wind] is another command, as are :scr[iptnames], + * :scs[cope], :sim[alt], :sig[ns] and :sil[ent]. + */ + static int +one_letter_cmd(char_u *p, cmdidx_T *idx) +{ + if (!in_vim9script()) + return FALSE; + if (*p == 'k') + { + *idx = CMD_k; + return TRUE; + } + if (p[0] == 's' + && ((p[1] == 'c' && (p[2] == NUL || (p[2] != 's' && p[2] != 'r' + && (p[3] == NUL || (p[3] != 'i' && p[4] != 'p'))))) + || p[1] == 'g' + || (p[1] == 'i' && p[2] != 'm' && p[2] != 'l' && p[2] != 'g') + || p[1] == 'I' + || (p[1] == 'r' && p[2] != 'e'))) + { + *idx = CMD_substitute; + return TRUE; + } + return FALSE; +} + /* * Find an Ex command by its name, either built-in or user. * Start of the name can be found at eap->cmd. @@ -3654,30 +3686,10 @@ find_ex_command( /* * Isolate the command and search for it in the command table. - * Exceptions: - * - The 'k' command can directly be followed by any character. - * But it is not used in Vim9 script. - * - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r' - * but :sre[wind] is another command, as are :scr[iptnames], - * :scs[cope], :sim[alt], :sig[ns] and :sil[ent]. - * - the "d" command can directly be followed by 'l' or 'p' flag. */ p = eap->cmd; - if (!vim9 && *p == 'k') + if (one_letter_cmd(p, &eap->cmdidx)) { - eap->cmdidx = CMD_k; - ++p; - } - else if (!vim9 - && p[0] == 's' - && ((p[1] == 'c' && (p[2] == NUL || (p[2] != 's' && p[2] != 'r' - && (p[3] == NUL || (p[3] != 'i' && p[4] != 'p'))))) - || p[1] == 'g' - || (p[1] == 'i' && p[2] != 'm' && p[2] != 'l' && p[2] != 'g') - || p[1] == 'I' - || (p[1] == 'r' && p[2] != 'e'))) - { - eap->cmdidx = CMD_substitute; ++p; } else @@ -3702,6 +3714,8 @@ find_ex_command( if (p == eap->cmd && vim_strchr((char_u *)"@*!=><&~#}", *p) != NULL) ++p; len = (int)(p - eap->cmd); + // The "d" command can directly be followed by 'l' or 'p' flag, when + // not in Vim9 script. if (!vim9 && *eap->cmd == 'd' && (p[-1] == 'l' || p[-1] == 'p')) { // Check for ":dl", ":dell", etc. to ":deletel": that's @@ -3955,10 +3969,11 @@ excmd_get_cmdidx(char_u *cmd, int len) { cmdidx_T idx; - for (idx = (cmdidx_T)0; (int)idx < (int)CMD_SIZE; - idx = (cmdidx_T)((int)idx + 1)) - if (STRNCMP(cmdnames[(int)idx].cmd_name, cmd, (size_t)len) == 0) - break; + if (!one_letter_cmd(cmd, &idx)) + for (idx = (cmdidx_T)0; (int)idx < (int)CMD_SIZE; + idx = (cmdidx_T)((int)idx + 1)) + if (STRNCMP(cmdnames[(int)idx].cmd_name, cmd, (size_t)len) == 0) + break; return idx; } diff --git a/src/version.c b/src/version.c index 17d15c0f89..39eefb5f64 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4618, /**/ 4617, /**/ From f8e43f6107f3a83b8c74a84c4c8f99598e2dc4c0 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 24 Mar 2022 15:15:15 +0000 Subject: [PATCH 16/18] patch 8.2.4619: mapping is cancelled when mouse moves and popup is visible Problem: Mapping is cancelled when mouse moves and popup is visible. Solution: Only generate mouse moved events when a popup may use them. (closes #10004) --- src/globals.h | 3 +++ src/gui.c | 2 +- src/popupwin.c | 27 ++++++++++++++++++++++++++- src/version.c | 2 ++ 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/globals.h b/src/globals.h index 1a50649a11..cd8893b307 100644 --- a/src/globals.h +++ b/src/globals.h @@ -736,6 +736,9 @@ EXTERN win_T *popup_dragwin INIT(= NULL); // popup window being dragged // Set to TRUE if there is any visible popup window. EXTERN int popup_visible INIT(= FALSE); +// Set to TRUE if a visible popup window may use a MOUSE_MOVE event +EXTERN int popup_uses_mouse_move INIT(= FALSE); + EXTERN int text_prop_frozen INIT(= 0); #endif diff --git a/src/gui.c b/src/gui.c index fb589fc1c7..3e383a4b4e 100644 --- a/src/gui.c +++ b/src/gui.c @@ -4968,7 +4968,7 @@ gui_mouse_moved(int x, int y) gui_mouse_focus(x, y); #ifdef FEAT_PROP_POPUP - if (popup_visible) + if (popup_uses_mouse_move) // Generate a mouse-moved event, so that the popup can perhaps be // closed, just like in the terminal. gui_send_mouse_event(MOUSE_MOVE, x, y, FALSE, 0); diff --git a/src/popupwin.c b/src/popupwin.c index 0aca7d3246..55f98f8a6f 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -150,6 +150,29 @@ set_mousemoved_values(win_T *wp) wp->w_popup_mouse_maxcol = mouse_col; } + static void +update_popup_uses_mouse_move(void) +{ + popup_uses_mouse_move = FALSE; + if (popup_visible) + { + win_T *wp; + + FOR_ALL_POPUPWINS(wp) + if (wp->w_popup_mouse_row != 0) + { + popup_uses_mouse_move = TRUE; + return; + } + FOR_ALL_POPUPWINS_IN_TAB(curtab, wp) + if (wp->w_popup_mouse_row != 0) + { + popup_uses_mouse_move = TRUE; + return; + } + } +} + /* * Used when popup options contain "moved" with "word" or "WORD". */ @@ -3586,7 +3609,7 @@ popup_need_position_adjust(win_T *wp) /* * Update "popup_mask" if needed. * Also recomputes the popup size and positions. - * Also updates "popup_visible". + * Also updates "popup_visible" and "popup_uses_mouse_move". * Also marks window lines for redrawing. */ void @@ -3755,6 +3778,8 @@ may_update_popup_mask(int type) vim_free(plines_cache); } + + update_popup_uses_mouse_move(); } /* diff --git a/src/version.c b/src/version.c index 39eefb5f64..693cebc06a 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4619, /**/ 4618, /**/ From 1e2c4175dce903986dac66ba9326ae562b159421 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 24 Mar 2022 15:24:45 +0000 Subject: [PATCH 17/18] patch 8.2.4620: two letter substitute commands don't work Problem: Two letter substitute commands don't work. (Yegappan Lakshmanan) Solution: Invert condition. --- src/ex_docmd.c | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 7c2fe3deb7..65f4221f0d 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -3431,7 +3431,7 @@ skip_option_env_lead(char_u *start) static int one_letter_cmd(char_u *p, cmdidx_T *idx) { - if (!in_vim9script()) + if (in_vim9script()) return FALSE; if (*p == 'k') { diff --git a/src/version.c b/src/version.c index 693cebc06a..6405e3b855 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4620, /**/ 4619, /**/ From 9dd42a631162a8561bd7c4b0e89afd21f7994d8c Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 24 Mar 2022 18:04:49 +0000 Subject: [PATCH 18/18] patch 8.2.4621: crash when using the tabline right-click menu Problem: Crash when using the tabline right-click menu. Solution: Use XtPointer for XmNuserData. (closes #10009) --- src/gui_motif.c | 12 ++++++------ src/version.c | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/gui_motif.c b/src/gui_motif.c index 740288827b..56d3f35723 100644 --- a/src/gui_motif.c +++ b/src/gui_motif.c @@ -142,12 +142,12 @@ tabline_button_cb( XtPointer client_data UNUSED, XtPointer call_data UNUSED) { - int cmd, tab_idx; + XtPointer cmd, tab_idx; XtVaGetValues(w, XmNuserData, &cmd, NULL); XtVaGetValues(tabLine_menu, XmNuserData, &tab_idx, NULL); - send_tabline_menu_event(tab_idx, cmd); + send_tabline_menu_event((int)(long)tab_idx, (int)(long)cmd); } /* @@ -254,7 +254,7 @@ tabline_menu_cb( XtVaGetValues(tab_w, XmNpageNumber, &tab_idx, NULL); } - XtVaSetValues(tabLine_menu, XmNuserData, tab_idx, NULL); + XtVaSetValues(tabLine_menu, XmNuserData, (XtPointer)(long)tab_idx, NULL); XtVaGetValues(tabLine_menu, XmNchildren, &children, XmNnumChildren, &numChildren, NULL); XtManageChildren(children, numChildren); @@ -517,7 +517,7 @@ gui_x11_create_widgets(void) // Add the buttons to the tabline popup menu n = 0; - XtSetArg(args[n], XmNuserData, TABLINE_MENU_CLOSE); n++; + XtSetArg(args[n], XmNuserData, (XtPointer)TABLINE_MENU_CLOSE); n++; xms = XmStringCreate((char *)"Close tab", STRING_TAG); XtSetArg(args[n], XmNlabelString, xms); n++; button = XmCreatePushButton(tabLine_menu, "Close", args, n); @@ -526,7 +526,7 @@ gui_x11_create_widgets(void) XmStringFree(xms); n = 0; - XtSetArg(args[n], XmNuserData, TABLINE_MENU_NEW); n++; + XtSetArg(args[n], XmNuserData, (XtPointer)TABLINE_MENU_NEW); n++; xms = XmStringCreate((char *)"New Tab", STRING_TAG); XtSetArg(args[n], XmNlabelString, xms); n++; button = XmCreatePushButton(tabLine_menu, "New Tab", args, n); @@ -535,7 +535,7 @@ gui_x11_create_widgets(void) XmStringFree(xms); n = 0; - XtSetArg(args[n], XmNuserData, TABLINE_MENU_OPEN); n++; + XtSetArg(args[n], XmNuserData, (XtPointer)TABLINE_MENU_OPEN); n++; xms = XmStringCreate((char *)"Open tab...", STRING_TAG); XtSetArg(args[n], XmNlabelString, xms); n++; button = XmCreatePushButton(tabLine_menu, "Open tab...", args, n); diff --git a/src/version.c b/src/version.c index 6405e3b855..bcb70e8d4d 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4621, /**/ 4620, /**/