changeset 28449:80ed5ad30d28 v8.2.4749

patch 8.2.4749: <script> is not expanded in autocmd context Commit: https://github.com/vim/vim/commit/eca7c60d68e63001dbe3c8e5d240b0895e607fc3 Author: LemonBoy <thatlemon@gmail.com> Date: Thu Apr 14 15:39:43 2022 +0100 patch 8.2.4749: <script> is not expanded in autocmd context Problem: <script> is not expanded in autocmd context. Solution: Add the context to the pattern struct. (closes https://github.com/vim/vim/issues/10144) Rename AutoPatCmd to AutoPatCmd_T.
author Bram Moolenaar <Bram@vim.org>
date Thu, 14 Apr 2022 16:45:02 +0200
parents 68c4651c8dfc
children 4d88ce67e7ad
files src/autocmd.c src/proto/autocmd.pro src/scriptfile.c src/structs.h src/testdir/test_expand.vim src/version.c
diffstat 6 files changed, 85 insertions(+), 47 deletions(-) [+]
line wrap: on
line diff
--- a/src/autocmd.c
+++ b/src/autocmd.c
@@ -55,7 +55,7 @@ typedef struct AutoCmd
     char	    once;		// "One shot": removed after execution
     char	    nested;		// If autocommands nest here.
     char	    last;		// last command in list
-    sctx_T	    script_ctx;		// script context where defined
+    sctx_T	    script_ctx;		// script context where it is defined
     struct AutoCmd  *next;		// next AutoCmd in list
 } AutoCmd;
 
@@ -234,12 +234,13 @@ struct AutoPatCmd_S
     char_u	*sfname;	// sfname to match with
     char_u	*tail;		// tail of fname
     event_T	event;		// current event
+    sctx_T	script_ctx;	// script context where it is defined
     int		arg_bufnr;	// Initially equal to <abuf>, set to zero when
 				// buf is deleted.
-    AutoPatCmd   *next;		// chain of active apc-s for auto-invalidation
+    AutoPatCmd_T *next;		// chain of active apc-s for auto-invalidation
 };
 
-static AutoPatCmd *active_apc_list = NULL; // stack of active autocommands
+static AutoPatCmd_T *active_apc_list = NULL; // stack of active autocommands
 
 // Macro to loop over all the patterns for an autocmd event
 #define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
@@ -264,7 +265,7 @@ static char_u *event_nr2name(event_T eve
 static int au_get_grouparg(char_u **argp);
 static int do_autocmd_event(event_T event, char_u *pat, int once, int nested, char_u *cmd, int forceit, int group, int flags);
 static int apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, int force, int group, buf_T *buf, exarg_T *eap);
-static void auto_next_pat(AutoPatCmd *apc, int stop_at_last);
+static void auto_next_pat(AutoPatCmd_T *apc, int stop_at_last);
 static int au_find_group(char_u *name);
 
 static event_T	last_event;
@@ -453,9 +454,9 @@ au_cleanup(void)
     void
 aubuflocal_remove(buf_T *buf)
 {
-    AutoPat	*ap;
-    event_T	event;
-    AutoPatCmd	*apc;
+    AutoPat	    *ap;
+    event_T	    event;
+    AutoPatCmd_T    *apc;
 
     // invalidate currently executing autocommands
     for (apc = active_apc_list; apc; apc = apc->next)
@@ -1914,7 +1915,7 @@ apply_autocmds_group(
     int		save_autocmd_busy;
     int		save_autocmd_nested;
     static int	nesting = 0;
-    AutoPatCmd	patcmd;
+    AutoPatCmd_T patcmd;
     AutoPat	*ap;
     sctx_T	save_current_sctx;
 #ifdef FEAT_EVAL
@@ -2173,15 +2174,14 @@ apply_autocmds_group(
     tail = gettail(fname);
 
     // Find first autocommand that matches
+    CLEAR_FIELD(patcmd);
     patcmd.curpat = first_autopat[(int)event];
-    patcmd.nextcmd = NULL;
     patcmd.group = group;
     patcmd.fname = fname;
     patcmd.sfname = sfname;
     patcmd.tail = tail;
     patcmd.event = event;
     patcmd.arg_bufnr = autocmd_bufnr;
-    patcmd.next = NULL;
     auto_next_pat(&patcmd, FALSE);
 
     // found one, start executing the autocommands
@@ -2363,16 +2363,21 @@ is_autocmd_blocked(void)
  */
     static void
 auto_next_pat(
-    AutoPatCmd	*apc,
+    AutoPatCmd_T *apc,
     int		stop_at_last)	    // stop when 'last' flag is set
 {
     AutoPat	*ap;
     AutoCmd	*cp;
     char_u	*name;
     char	*s;
-    char_u	**sourcing_namep = &SOURCING_NAME;
+    estack_T	*entry;
+    char_u	*namep;
 
-    VIM_CLEAR(*sourcing_namep);
+    entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
+
+    // Clear the exestack entry for this ETYPE_AUCMD entry.
+    VIM_CLEAR(entry->es_name);
+    entry->es_info.aucmd = NULL;
 
     for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
     {
@@ -2392,20 +2397,22 @@ auto_next_pat(
 	    {
 		name = event_nr2name(apc->event);
 		s = _("%s Autocommands for \"%s\"");
-		*sourcing_namep = alloc(STRLEN(s)
-					      + STRLEN(name) + ap->patlen + 1);
-		if (*sourcing_namep != NULL)
+		namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1);
+		if (namep != NULL)
 		{
-		    sprintf((char *)*sourcing_namep, s,
-					       (char *)name, (char *)ap->pat);
+		    sprintf((char *)namep, s, (char *)name, (char *)ap->pat);
 		    if (p_verbose >= 8)
 		    {
 			verbose_enter();
-			smsg(_("Executing %s"), *sourcing_namep);
+			smsg(_("Executing %s"), namep);
 			verbose_leave();
 		    }
 		}
 
+		// Update the exestack entry for this autocmd.
+		entry->es_name = namep;
+		entry->es_info.aucmd = apc;
+
 		apc->curpat = ap;
 		apc->nextcmd = ap->cmds;
 		// mark last command
@@ -2423,6 +2430,15 @@ auto_next_pat(
 }
 
 /*
+ * Get the script context where autocommand "acp" is defined.
+ */
+    sctx_T *
+acp_script_ctx(AutoPatCmd_T *acp)
+{
+    return &acp->script_ctx;
+}
+
+/*
  * Get next autocommand command.
  * Called by do_cmdline() to get the next line for ":if".
  * Returns allocated string, or NULL for end of autocommands.
@@ -2434,7 +2450,7 @@ getnextac(
 	int indent UNUSED,
 	getline_opt_T options UNUSED)
 {
-    AutoPatCmd	    *acp = (AutoPatCmd *)cookie;
+    AutoPatCmd_T    *acp = (AutoPatCmd_T *)cookie;
     char_u	    *retval;
     AutoCmd	    *ac;
 
@@ -2481,6 +2497,7 @@ getnextac(
 	au_del_cmd(ac);
     autocmd_nested = ac->nested;
     current_sctx = ac->script_ctx;
+    acp->script_ctx = current_sctx;
     if (ac->last)
 	acp->nextcmd = NULL;
     else
--- a/src/proto/autocmd.pro
+++ b/src/proto/autocmd.pro
@@ -7,7 +7,7 @@ int check_ei(void);
 char_u *au_event_disable(char *what);
 void au_event_restore(char_u *old_ei);
 void do_autocmd(exarg_T *eap, char_u *arg_in, int forceit);
-int do_doautocmd(char_u *arg, int do_msg, int *did_something);
+int do_doautocmd(char_u *arg_start, int do_msg, int *did_something);
 void ex_doautoall(exarg_T *eap);
 int check_nomodeline(char_u **argp);
 void aucmd_prepbuf(aco_save_T *aco, buf_T *buf);
@@ -16,6 +16,7 @@ int apply_autocmds(event_T event, char_u
 int apply_autocmds_exarg(event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf, exarg_T *eap);
 int apply_autocmds_retval(event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf, int *retval);
 int trigger_cursorhold(void);
+int has_winscrolled(void);
 int has_cursormoved(void);
 int has_cursormovedI(void);
 int has_textchanged(void);
@@ -26,10 +27,10 @@ int has_cmdundefined(void);
 int has_textyankpost(void);
 int has_completechanged(void);
 int has_modechanged(void);
-int has_winscrolled(void);
 void block_autocmds(void);
 void unblock_autocmds(void);
 int is_autocmd_blocked(void);
+sctx_T *acp_script_ctx(AutoPatCmd_T *acp);
 char_u *getnextac(int c, void *cookie, int indent, getline_opt_T options);
 int has_autocmd(event_T event, char_u *sfname, buf_T *buf);
 char_u *get_augroup_name(expand_T *xp, int idx);
--- a/src/scriptfile.c
+++ b/src/scriptfile.c
@@ -157,27 +157,36 @@ 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
+    // If evaluated in a function or autocommand, return the path of the script
+    // where it is defined, at script level the current script path is returned
     // instead.
     if (which == ESTACK_SCRIPT)
     {
-	if (entry->es_type == ETYPE_UFUNC)
+	entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
+	// Walk the stack backwards, starting from the current frame.
+	for (idx = exestack.ga_len - 1; idx >= 0; --idx, --entry)
 	{
-	    sctx_T *def_ctx = &entry->es_info.ufunc->uf_script_ctx;
+	    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)
+		if (def_ctx->sc_sid > 0)
+		    return vim_strsave(SCRIPT_ITEM(def_ctx->sc_sid)->sn_name);
+		else
+		    return NULL;
+	    }
+	    else if (entry->es_type == ETYPE_AUCMD)
 	    {
-		entry = ((estack_T *)exestack.ga_data) + idx;
+		sctx_T *def_ctx = acp_script_ctx(entry->es_info.aucmd);
 
-		if (entry->es_type == ETYPE_SCRIPT)
-		    return vim_strsave(entry->es_name);
+		if (def_ctx->sc_sid > 0)
+		    return vim_strsave(SCRIPT_ITEM(def_ctx->sc_sid)->sn_name);
+		else
+		    return NULL;
+	    }
+	    else if (entry->es_type == ETYPE_SCRIPT)
+	    {
+		return vim_strsave(entry->es_name);
 	    }
 	}
 	return NULL;
--- a/src/structs.h
+++ b/src/structs.h
@@ -2073,7 +2073,7 @@ struct partial_S
     dict_T	*pt_dict;	// dict for "self"
 };
 
-typedef struct AutoPatCmd_S AutoPatCmd;
+typedef struct AutoPatCmd_S AutoPatCmd_T;
 
 /*
  * Entry in the execution stack "exestack".
@@ -2100,7 +2100,7 @@ typedef struct {
 #if defined(FEAT_EVAL)
 	ufunc_T *ufunc;     // function info
 #endif
-	AutoPatCmd *aucmd;  // autocommand info
+	AutoPatCmd_T *aucmd;  // autocommand info
 	except_T   *except; // exception info
     } es_info;
 #if defined(FEAT_EVAL)
--- a/src/testdir/test_expand.vim
+++ b/src/testdir/test_expand.vim
@@ -169,43 +169,52 @@ endfunc
 
 func Test_expand_script_source()
   let lines0 =<< trim [SCRIPT]
-    let g:script_level[0] = expand('<script>:t')
+    call extend(g:script_level, [expand('<script>:t')])
     so Xscript1
     func F0()
-      let g:func_level[0] = expand('<script>:t')
+      call extend(g:func_level, [expand('<script>:t')])
     endfunc
+
+    au User * call extend(g:au_level, [expand('<script>:t')])
   [SCRIPT]
 
   let lines1 =<< trim [SCRIPT]
-    let g:script_level[1] = expand('<script>:t')
+    call extend(g:script_level, [expand('<script>:t')])
     so Xscript2
     func F1()
-      let g:func_level[1] = expand('<script>:t')
+      call extend(g:func_level, [expand('<script>:t')])
     endfunc
+
+    au User * call extend(g:au_level, [expand('<script>:t')])
   [SCRIPT]
 
   let lines2 =<< trim [SCRIPT]
-    let g:script_level[2] = expand('<script>:t')
+    call extend(g:script_level, [expand('<script>:t')])
     func F2()
-      let g:func_level[2] = expand('<script>:t')
+      call extend(g:func_level, [expand('<script>:t')])
     endfunc
+
+    au User * call extend(g:au_level, [expand('<script>:t')])
   [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 = ['', '', '']
+  " Check the expansion of <script> at different levels.
+  let g:script_level = []
+  let g:func_level = []
+  let g:au_level = []
 
   so Xscript0
   call F0()
   call F1()
   call F2()
+  doautocmd User
 
   call assert_equal(['Xscript0', 'Xscript1', 'Xscript2'], g:script_level)
   call assert_equal(['Xscript0', 'Xscript1', 'Xscript2'], g:func_level)
+  call assert_equal(['Xscript2', 'Xscript1', 'Xscript0'], g:au_level)
 
   unlet g:script_level g:func_level
   delfunc F0
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    4749,
+/**/
     4748,
 /**/
     4747,