# HG changeset patch # User Bram Moolenaar # Date 1562245207 -7200 # Node ID 984eef966002fe8698519a8d05fdd3d381573a27 # Parent 62c322fcbd201888ce6230e320ea84d8ca9a16a9 patch 8.1.1625: script line numbers are not exactly right commit https://github.com/vim/vim/commit/bc2cfe4672d370330b8698d4d025697a9a6ec569 Author: Bram Moolenaar Date: Thu Jul 4 14:57:12 2019 +0200 patch 8.1.1625: script line numbers are not exactly right Problem: Script line numbers are not exactly right. Solution: Handle heredoc and continuation lines better. (Ozaki Kiichi, closes #4611, closes #4511) diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -3269,20 +3269,21 @@ cmd_source(char_u *fname, exarg_T *eap) */ struct source_cookie { - FILE *fp; /* opened file for sourcing */ - char_u *nextline; /* if not NULL: line that was read ahead */ - int finished; /* ":finish" used */ + FILE *fp; // opened file for sourcing + 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 #ifdef USE_CRNL - int fileformat; /* EOL_UNKNOWN, EOL_UNIX or EOL_DOS */ - int error; /* TRUE if LF found after CR-LF */ + int fileformat; // EOL_UNKNOWN, EOL_UNIX or EOL_DOS + int error; // TRUE if LF found after CR-LF #endif #ifdef FEAT_EVAL - linenr_T breakpoint; /* next line with breakpoint or zero */ - char_u *fname; /* name of sourced file */ - int dbg_tick; /* debug_tick when breakpoint was set */ - int level; /* top nesting level of sourced file */ + linenr_T breakpoint; // next line with breakpoint or zero + char_u *fname; // name of sourced file + int dbg_tick; // debug_tick when breakpoint was set + int level; // top nesting level of sourced file #endif - vimconv_T conv; /* type of conversion */ + vimconv_T conv; // type of conversion }; #ifdef FEAT_EVAL @@ -3346,7 +3347,6 @@ fopen_noinh_readbin(char *filename) } #endif - /* * do_source: Read the file "fname" and execute its lines as EX commands. * @@ -3495,6 +3495,7 @@ do_source( #endif cookie.nextline = NULL; + cookie.sourcing_lnum = 0; cookie.finished = FALSE; #ifdef FEAT_EVAL @@ -3790,6 +3791,14 @@ free_scriptnames(void) #endif + linenr_T +get_sourced_lnum(char_u *(*fgetline)(int, void *, int, int), void *cookie) +{ + return fgetline == getsourceline + ? ((struct source_cookie *)cookie)->sourcing_lnum + : sourcing_lnum; +} + /* * Get one full line from a sourced file. * Called by do_cmdline() when it's called from do_source(). @@ -3816,6 +3825,10 @@ getsourceline(int c UNUSED, void *cookie script_line_end(); # endif #endif + + // Set the current sourcing line number. + sourcing_lnum = sp->sourcing_lnum + 1; + /* * Get current line. If there is a read-ahead line, use it, otherwise get * one now. @@ -3828,7 +3841,7 @@ getsourceline(int c UNUSED, void *cookie { line = sp->nextline; sp->nextline = NULL; - ++sourcing_lnum; + ++sp->sourcing_lnum; } #ifdef FEAT_PROFILE if (line != NULL && do_profiling == PROF_YES) @@ -3840,7 +3853,7 @@ getsourceline(int c UNUSED, void *cookie if (line != NULL && do_concat && vim_strchr(p_cpo, CPO_CONCAT) == NULL) { /* compensate for the one line read-ahead */ - --sourcing_lnum; + --sp->sourcing_lnum; // Get the next line and concatenate it when it starts with a // backslash. We always need to read the next line, keep it in @@ -3931,7 +3944,7 @@ get_one_sourceline(struct source_cookie /* * Loop until there is a finished line (or end-of-file). */ - sourcing_lnum++; + ++sp->sourcing_lnum; for (;;) { /* make room to read at least 120 (more) characters */ @@ -4001,7 +4014,7 @@ get_one_sourceline(struct source_cookie ; if ((len & 1) != (c & 1)) /* escaped NL, read more */ { - sourcing_lnum++; + ++sp->sourcing_lnum; continue; } diff --git a/src/proto/ex_cmds2.pro b/src/proto/ex_cmds2.pro --- a/src/proto/ex_cmds2.pro +++ b/src/proto/ex_cmds2.pro @@ -81,6 +81,7 @@ void ex_scriptnames(exarg_T *eap); void scriptnames_slash_adjust(void); char_u *get_scriptname(scid_T id); void free_scriptnames(void); +linenr_T get_sourced_lnum(char_u *(*fgetline)(int, void *, int, int), void *cookie); char_u *getsourceline(int c, void *cookie, int indent, int do_concat); void script_line_start(void); void script_line_exec(void); diff --git a/src/testdir/test_vimscript.vim b/src/testdir/test_vimscript.vim --- a/src/testdir/test_vimscript.vim +++ b/src/testdir/test_vimscript.vim @@ -1676,6 +1676,76 @@ func Test_funccall_garbage_collect() delfunc Func endfunc +func Test_function_defined_line() + if has('gui_running') + " Can't catch the output of gvim. + return + endif + + let lines =<< trim [CODE] + " F1 + func F1() + " F2 + func F2() + " + " + " + return + endfunc + " F3 + execute "func F3()\n\n\n\nreturn\nendfunc" + " F4 + execute "func F4()\n + \\n + \\n + \\n + \return\n + \endfunc" + endfunc + " F5 + execute "func F5()\n\n\n\nreturn\nendfunc" + " F6 + execute "func F6()\n + \\n + \\n + \\n + \return\n + \endfunc" + call F1() + verbose func F1 + verbose func F2 + verbose func F3 + verbose func F4 + verbose func F5 + verbose func F6 + qall! + [CODE] + + call writefile(lines, 'Xtest.vim') + let res = system(v:progpath .. ' --clean -es -X -S Xtest.vim') + call assert_equal(0, v:shell_error) + + let m = matchstr(res, 'function F1()[^[:print:]]*[[:print:]]*') + call assert_match(' line 2$', m) + + let m = matchstr(res, 'function F2()[^[:print:]]*[[:print:]]*') + call assert_match(' line 4$', m) + + let m = matchstr(res, 'function F3()[^[:print:]]*[[:print:]]*') + call assert_match(' line 11$', m) + + let m = matchstr(res, 'function F4()[^[:print:]]*[[:print:]]*') + call assert_match(' line 13$', m) + + let m = matchstr(res, 'function F5()[^[:print:]]*[[:print:]]*') + call assert_match(' line 21$', m) + + let m = matchstr(res, 'function F6()[^[:print:]]*[[:print:]]*') + call assert_match(' line 23$', m) + + call delete('Xtest.vim') +endfunc + "------------------------------------------------------------------------------- " Modelines {{{1 " vim: ts=8 sw=4 tw=80 fdm=marker diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -2008,7 +2008,8 @@ ex_function(exarg_T *eap) int todo; hashitem_T *hi; int do_concat = TRUE; - int sourcing_lnum_off; + linenr_T sourcing_lnum_off; + linenr_T sourcing_lnum_top; /* * ":function" without argument: list functions. @@ -2275,6 +2276,9 @@ ex_function(exarg_T *eap) cmdline_row = msg_row; } + // Save the starting line number. + sourcing_lnum_top = sourcing_lnum; + indent = 2; nesting = 0; for (;;) @@ -2285,7 +2289,6 @@ ex_function(exarg_T *eap) saved_wait_return = FALSE; } need_wait_return = FALSE; - sourcing_lnum_off = sourcing_lnum; if (line_arg != NULL) { @@ -2318,8 +2321,9 @@ ex_function(exarg_T *eap) } /* Detect line continuation: sourcing_lnum increased more than one. */ - if (sourcing_lnum > sourcing_lnum_off + 1) - sourcing_lnum_off = sourcing_lnum - sourcing_lnum_off - 1; + sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie); + if (sourcing_lnum < sourcing_lnum_off) + sourcing_lnum_off -= sourcing_lnum; else sourcing_lnum_off = 0; @@ -2670,7 +2674,7 @@ ex_function(exarg_T *eap) fp->uf_flags = flags; fp->uf_calls = 0; fp->uf_script_ctx = current_sctx; - fp->uf_script_ctx.sc_lnum += sourcing_lnum - newlines.ga_len - 1; + fp->uf_script_ctx.sc_lnum += sourcing_lnum_top; goto ret_free; erret: diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -778,6 +778,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1625, +/**/ 1624, /**/ 1623,