Mercurial > vim
changeset 28403:2655935b5ccc v8.2.4726
patch 8.2.4726: cannot use expand() to get the script name
Commit: https://github.com/vim/vim/commit/6013d0045dec7ca7c0068fbe186c42d754a7368b
Author: LemonBoy <thatlemon@gmail.com>
Date: Sat Apr 9 21:42:10 2022 +0100
patch 8.2.4726: cannot use expand() to get the script name
Problem: Cannot use expand() to get the script name.
Solution: Support expand('<script>'). (closes https://github.com/vim/vim/issues/10121)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sat, 09 Apr 2022 22:45:03 +0200 |
parents | 401c4206d38c |
children | 95ce25a7224a |
files | runtime/doc/cmdline.txt src/errors.h src/ex_docmd.c src/scriptfile.c src/testdir/test_expand.vim src/version.c src/vim.h |
diffstat | 7 files changed, 114 insertions(+), 10 deletions(-) [+] |
line wrap: on
line diff
--- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -939,7 +939,7 @@ Note: these are typed literally, they ar file name of the sourced file. *E498* When executing a legacy function, is replaced with the call stack, as with <stack> (this is for backwards - compatibility, using <stack> is preferred). + compatibility, using <stack> or <script> is preferred). In Vim9 script using <sfile> in a function gives error *E1245* . Note that filename-modifiers are useless when <sfile> is @@ -951,6 +951,12 @@ Note: these are typed literally, they ar ".." in between items. E.g.: "function {function-name1}[{lnum}]..{function-name2}[{lnum}]" If there is no call stack you get error *E489* . + *:<script>* *<script>* + <script> When executing a `:source` command, is replaced with the file + name of the sourced file. When executing a function, is + replaced with the file name of the script where it is + defined. + If the file name cannot be determined you get error *E1274* . *:<slnum>* *<slnum>* <slnum> When executing a ":source" command, is replaced with the line number. *E842*
--- a/src/errors.h +++ b/src/errors.h @@ -3258,3 +3258,5 @@ EXTERN char e_using_type_not_in_script_c #endif EXTERN char e_nfa_regexp_missing_value_in_chr[] INIT(= N_("E1273: (NFA regexp) missing value in '\\%%%c'")); +EXTERN char e_no_script_file_name_to_substitute_for_script[] + INIT(= N_("E1274: No script file name to substitute for \"<script>\""));
--- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -8957,8 +8957,10 @@ find_cmdline_var(char_u *src, int *usedl #define SPEC_SLNUM (SPEC_SFILE + 1) "<stack>", // call stack #define SPEC_STACK (SPEC_SLNUM + 1) + "<script>", // script file name +#define SPEC_SCRIPT (SPEC_STACK + 1) "<afile>", // autocommand file name -#define SPEC_AFILE (SPEC_STACK + 1) +#define SPEC_AFILE (SPEC_SCRIPT + 1) "<abuf>", // autocommand buffer number #define SPEC_ABUF (SPEC_AFILE + 1) "<amatch>", // autocommand match name @@ -9226,14 +9228,28 @@ eval_vars( break; case SPEC_SFILE: // file name for ":so" command - case SPEC_STACK: // call stack - result = estack_sfile(spec_idx == SPEC_SFILE - ? ESTACK_SFILE : ESTACK_STACK); + result = estack_sfile(ESTACK_SFILE); if (result == NULL) { - *errormsg = spec_idx == SPEC_SFILE - ? _(e_no_source_file_name_to_substitute_for_sfile) - : _(e_no_call_stack_to_substitute_for_stack); + *errormsg = _(e_no_source_file_name_to_substitute_for_sfile); + return NULL; + } + resultbuf = result; // remember allocated string + break; + case SPEC_STACK: // call stack + result = estack_sfile(ESTACK_STACK); + if (result == NULL) + { + *errormsg = _(e_no_call_stack_to_substitute_for_stack); + return NULL; + } + resultbuf = result; // remember allocated string + break; + case SPEC_SCRIPT: // script file name + result = estack_sfile(ESTACK_SCRIPT); + if (result == NULL) + { + *errormsg = _(e_no_script_file_name_to_substitute_for_script); return NULL; } resultbuf = result; // remember allocated string
--- a/src/scriptfile.c +++ b/src/scriptfile.c @@ -118,7 +118,8 @@ estack_pop(void) /* * Get the current value for <sfile> in allocated memory. - * "which" is ESTACK_SFILE for <sfile> and ESTACK_STACK for <stack>. + * "which" is ESTACK_SFILE for <sfile>, ESTACK_STACK for <stack> or + * ESTACK_SCRIPT for <script>. */ char_u * estack_sfile(estack_arg_T which UNUSED) @@ -156,6 +157,32 @@ estack_sfile(estack_arg_T which UNUSED) return NULL; } + // If evaluated in a function return the path of the script where the + // function is defined, at script level the current script path is returned + // instead. + if (which == ESTACK_SCRIPT) + { + if (entry->es_type == ETYPE_UFUNC) + { + sctx_T *def_ctx = &entry->es_info.ufunc->uf_script_ctx; + + if (def_ctx->sc_sid > 0) + return vim_strsave(SCRIPT_ITEM(def_ctx->sc_sid)->sn_name); + } + else if (exestack.ga_len > 0) + { + // Walk the stack backwards, starting from the current frame. + for (idx = exestack.ga_len - 1; idx; --idx) + { + entry = ((estack_T *)exestack.ga_data) + idx; + + if (entry->es_type == ETYPE_SCRIPT) + return vim_strsave(entry->es_name); + } + } + return NULL; + } + // Give information about each stack entry up to the root. // For a function we compose the call stack, as it was done in the past: // "function One[123]..Two[456]..Three"
--- a/src/testdir/test_expand.vim +++ b/src/testdir/test_expand.vim @@ -159,4 +159,54 @@ func Test_expandcmd_shell_nonomatch() call assert_equal('$*', expandcmd('$*')) endfunc +func Test_expand_script_source() + let lines0 =<< trim [SCRIPT] + let g:script_level[0] = expand('<script>:t') + so Xscript1 + func F0() + let g:func_level[0] = expand('<script>:t') + endfunc + [SCRIPT] + + let lines1 =<< trim [SCRIPT] + let g:script_level[1] = expand('<script>:t') + so Xscript2 + func F1() + let g:func_level[1] = expand('<script>:t') + endfunc + [SCRIPT] + + let lines2 =<< trim [SCRIPT] + let g:script_level[2] = expand('<script>:t') + func F2() + let g:func_level[2] = expand('<script>:t') + endfunc + [SCRIPT] + + call writefile(lines0, 'Xscript0') + call writefile(lines1, 'Xscript1') + call writefile(lines2, 'Xscript2') + + " Check the expansion of <script> at script and function level. + let g:script_level = ['', '', ''] + let g:func_level = ['', '', ''] + + so Xscript0 + call F0() + call F1() + call F2() + + call assert_equal(['Xscript0', 'Xscript1', 'Xscript2'], g:script_level) + call assert_equal(['Xscript0', 'Xscript1', 'Xscript2'], g:func_level) + + unlet g:script_level g:func_level + delfunc F0 + delfunc F1 + delfunc F2 + + call delete('Xscript0') + call delete('Xscript1') + call delete('Xscript2') +endfunc + " vim: shiftwidth=2 sts=2 expandtab