mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	Merge #9706 from justinmk/aucmd-once
This commit is contained in:
		| @@ -40,15 +40,18 @@ effects.  Be careful not to destroy your text. | |||||||
| 2. Defining autocommands				*autocmd-define* | 2. Defining autocommands				*autocmd-define* | ||||||
|  |  | ||||||
| 							*:au* *:autocmd* | 							*:au* *:autocmd* | ||||||
| :au[tocmd] [group] {event} {pat} [nested] {cmd} | :au[tocmd] [group] {event} {pat} [once] [nested] {cmd} | ||||||
| 			Add {cmd} to the list of commands that Vim will | 			Add {cmd} to the list of commands that Vim will | ||||||
| 			execute automatically on {event} for a file matching | 			execute automatically on {event} for a file matching | ||||||
| 			{pat} |autocmd-patterns|. | 			{pat} |autocmd-patterns|. | ||||||
| 			Note: A quote character is seen as argument to the | 			Note: A quote character is seen as argument to the | ||||||
| 			:autocmd and won't start a comment. | 			:autocmd and won't start a comment. | ||||||
| 			Vim always adds the {cmd} after existing autocommands, | 			Nvim always adds {cmd} after existing autocommands so | ||||||
| 			so that the autocommands execute in the order in which | 			they execute in the order in which they were defined. | ||||||
| 			they were given.  See |autocmd-nested| for [nested]. | 			See |autocmd-nested| for [nested]. | ||||||
|  | 							*autocmd-once* | ||||||
|  | 			If [once] is supplied the command is executed once, | ||||||
|  | 			then removed ("one shot"). | ||||||
|  |  | ||||||
| The special pattern <buffer> or <buffer=N> defines a buffer-local autocommand. | The special pattern <buffer> or <buffer=N> defines a buffer-local autocommand. | ||||||
| See |autocmd-buflocal|. | See |autocmd-buflocal|. | ||||||
| @@ -116,10 +119,11 @@ prompt.  When one command outputs two messages this can happen anyway. | |||||||
| ============================================================================== | ============================================================================== | ||||||
| 3. Removing autocommands				*autocmd-remove* | 3. Removing autocommands				*autocmd-remove* | ||||||
|  |  | ||||||
| :au[tocmd]! [group] {event} {pat} [nested] {cmd} | :au[tocmd]! [group] {event} {pat} [once] [nested] {cmd} | ||||||
| 			Remove all autocommands associated with {event} and | 			Remove all autocommands associated with {event} and | ||||||
| 			{pat}, and add the command {cmd}.  See | 			{pat}, and add the command {cmd}. | ||||||
| 			|autocmd-nested| for [nested]. | 			See |autocmd-once| for [once]. | ||||||
|  | 			See |autocmd-nested| for [nested]. | ||||||
|  |  | ||||||
| :au[tocmd]! [group] {event} {pat} | :au[tocmd]! [group] {event} {pat} | ||||||
| 			Remove all autocommands associated with {event} and | 			Remove all autocommands associated with {event} and | ||||||
| @@ -1323,8 +1327,7 @@ option will not cause any commands to be executed. | |||||||
| 			another extension.  Example: > | 			another extension.  Example: > | ||||||
| 				:au BufEnter *.cpp so ~/.config/nvim/init_cpp.vim | 				:au BufEnter *.cpp so ~/.config/nvim/init_cpp.vim | ||||||
| 				:au BufEnter *.cpp doau BufEnter x.c | 				:au BufEnter *.cpp doau BufEnter x.c | ||||||
| <			Be careful to avoid endless loops.  See | <			Be careful to avoid endless loops.  |autocmd-nested| | ||||||
| 			|autocmd-nested|. |  | ||||||
|  |  | ||||||
| 			When the [group] argument is not given, Vim executes | 			When the [group] argument is not given, Vim executes | ||||||
| 			the autocommands for all groups.  When the [group] | 			the autocommands for all groups.  When the [group] | ||||||
|   | |||||||
| @@ -133,6 +133,7 @@ Command-line highlighting: | |||||||
|   removed in the future). |   removed in the future). | ||||||
|  |  | ||||||
| Commands: | Commands: | ||||||
|  |   |:autocmd| accepts the `once` flag | ||||||
|   |:checkhealth| |   |:checkhealth| | ||||||
|   |:cquit| can use [count] to set the exit code |   |:cquit| can use [count] to set the exit code | ||||||
|   |:drop| is always available |   |:drop| is always available | ||||||
|   | |||||||
| @@ -20160,7 +20160,7 @@ void ex_function(exarg_T *eap) | |||||||
|         skip_until = vim_strsave((char_u *)"."); |         skip_until = vim_strsave((char_u *)"."); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       // Check for ":python <<EOF", ":lua <<EOF", etc. |       // heredoc: Check for ":python <<EOF", ":lua <<EOF", etc. | ||||||
|       arg = skipwhite(skiptowhite(p)); |       arg = skipwhite(skiptowhite(p)); | ||||||
|       if (arg[0] == '<' && arg[1] =='<' |       if (arg[0] == '<' && arg[1] =='<' | ||||||
|           && ((p[0] == 'p' && p[1] == 'y' |           && ((p[0] == 'p' && p[1] == 'y' | ||||||
|   | |||||||
| @@ -99,8 +99,9 @@ | |||||||
| //   defined and will have to be executed. | //   defined and will have to be executed. | ||||||
| // | // | ||||||
| typedef struct AutoCmd { | typedef struct AutoCmd { | ||||||
|   char_u          *cmd;                 // The command to be executed (NULL |   char_u          *cmd;                 // Command to be executed (NULL when | ||||||
|                                         // when command has been removed) |                                         // command has been removed) | ||||||
|  |   bool once;                            // "One shot": removed after execution | ||||||
|   char nested;                          // If autocommands nest here |   char nested;                          // If autocommands nest here | ||||||
|   char last;                            // last command in list |   char last;                            // last command in list | ||||||
|   scid_T scriptID;                      // script ID where defined |   scid_T scriptID;                      // script ID where defined | ||||||
| @@ -121,20 +122,20 @@ typedef struct AutoPat { | |||||||
|   char last;                            // last pattern for apply_autocmds() |   char last;                            // last pattern for apply_autocmds() | ||||||
| } AutoPat; | } AutoPat; | ||||||
|  |  | ||||||
| /* | /// | ||||||
|  * struct used to keep status while executing autocommands for an event. | /// Struct used to keep status while executing autocommands for an event. | ||||||
|  */ | /// | ||||||
| typedef struct AutoPatCmd { | typedef struct AutoPatCmd { | ||||||
|   AutoPat     *curpat;          /* next AutoPat to examine */ |   AutoPat     *curpat;          // next AutoPat to examine | ||||||
|   AutoCmd     *nextcmd;         /* next AutoCmd to execute */ |   AutoCmd     *nextcmd;         // next AutoCmd to execute | ||||||
|   int group;                    /* group being used */ |   int group;                    // group being used | ||||||
|   char_u      *fname;           /* fname to match with */ |   char_u      *fname;           // fname to match with | ||||||
|   char_u      *sfname;          /* sfname to match with */ |   char_u      *sfname;          // sfname to match with | ||||||
|   char_u      *tail;            /* tail of fname */ |   char_u      *tail;            // tail of fname | ||||||
|   event_T event;                /* current event */ |   event_T event;                // current event | ||||||
|   int arg_bufnr;                /* initially equal to <abuf>, set to zero when |   int arg_bufnr;                // initially equal to <abuf>, set to zero when | ||||||
|                                    buf is deleted */ |                                 // buf is deleted | ||||||
|   struct AutoPatCmd   *next;    /* chain of active apc-s for auto-invalidation*/ |   struct AutoPatCmd   *next;    // chain of active apc-s for auto-invalidation | ||||||
| } AutoPatCmd; | } AutoPatCmd; | ||||||
|  |  | ||||||
| #define AUGROUP_DEFAULT    -1      /* default autocmd group */ | #define AUGROUP_DEFAULT    -1      /* default autocmd group */ | ||||||
| @@ -5563,29 +5564,31 @@ static void show_autocmd(AutoPat *ap, event_T event) | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | // Mark an autocommand handler for deletion. | ||||||
|  * Mark an autocommand pattern for deletion. |  | ||||||
|  */ |  | ||||||
| static void au_remove_pat(AutoPat *ap) | static void au_remove_pat(AutoPat *ap) | ||||||
| { | { | ||||||
|   xfree(ap->pat); |   xfree(ap->pat); | ||||||
|   ap->pat = NULL; |   ap->pat = NULL; | ||||||
|   ap->buflocal_nr = -1; |   ap->buflocal_nr = -1; | ||||||
|   au_need_clean = TRUE; |   au_need_clean = true; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | // Mark all commands for a pattern for deletion. | ||||||
|  * Mark all commands for a pattern for deletion. |  | ||||||
|  */ |  | ||||||
| static void au_remove_cmds(AutoPat *ap) | static void au_remove_cmds(AutoPat *ap) | ||||||
| { | { | ||||||
|   AutoCmd *ac; |   for (AutoCmd *ac = ap->cmds; ac != NULL; ac = ac->next) { | ||||||
|  |  | ||||||
|   for (ac = ap->cmds; ac != NULL; ac = ac->next) { |  | ||||||
|     xfree(ac->cmd); |     xfree(ac->cmd); | ||||||
|     ac->cmd = NULL; |     ac->cmd = NULL; | ||||||
|   } |   } | ||||||
|   au_need_clean = TRUE; |   au_need_clean = true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Delete one command from an autocmd pattern. | ||||||
|  | static void au_del_cmd(AutoCmd *ac) | ||||||
|  | { | ||||||
|  |   xfree(ac->cmd); | ||||||
|  |   ac->cmd = NULL; | ||||||
|  |   au_need_clean = true; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
| @@ -5674,18 +5677,18 @@ void aubuflocal_remove(buf_T *buf) | |||||||
|   au_cleanup(); |   au_cleanup(); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | // Add an autocmd group name. | ||||||
|  * Add an autocmd group name. | // Return its ID.  Returns AUGROUP_ERROR (< 0) for error. | ||||||
|  * Return it's ID.  Returns AUGROUP_ERROR (< 0) for error. |  | ||||||
|  */ |  | ||||||
| static int au_new_group(char_u *name) | static int au_new_group(char_u *name) | ||||||
| { | { | ||||||
|   int i = au_find_group(name); |   int i = au_find_group(name); | ||||||
|   if (i == AUGROUP_ERROR) {     /* the group doesn't exist yet, add it */ |   if (i == AUGROUP_ERROR) {     // the group doesn't exist yet, add it. | ||||||
|     /* First try using a free entry. */ |     // First try using a free entry. | ||||||
|     for (i = 0; i < augroups.ga_len; ++i) |     for (i = 0; i < augroups.ga_len; i++) { | ||||||
|       if (AUGROUP_NAME(i) == NULL) |       if (AUGROUP_NAME(i) == NULL) { | ||||||
|         break; |         break; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|     if (i == augroups.ga_len) { |     if (i == augroups.ga_len) { | ||||||
|       ga_grow(&augroups, 1); |       ga_grow(&augroups, 1); | ||||||
|     } |     } | ||||||
| @@ -5701,9 +5704,7 @@ static int au_new_group(char_u *name) | |||||||
|  |  | ||||||
| static void au_del_group(char_u *name) | static void au_del_group(char_u *name) | ||||||
| { | { | ||||||
|   int i; |   int i = au_find_group(name); | ||||||
|  |  | ||||||
|   i = au_find_group(name); |  | ||||||
|   if (i == AUGROUP_ERROR) {      // the group doesn't exist |   if (i == AUGROUP_ERROR) {      // the group doesn't exist | ||||||
|     EMSG2(_("E367: No such group: \"%s\""), name); |     EMSG2(_("E367: No such group: \"%s\""), name); | ||||||
|   } else if (i == current_augroup) { |   } else if (i == current_augroup) { | ||||||
| @@ -5760,23 +5761,22 @@ bool au_has_group(const char_u *name) | |||||||
|   return au_find_group(name) != AUGROUP_ERROR; |   return au_find_group(name) != AUGROUP_ERROR; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | /// ":augroup {name}". | ||||||
|  * ":augroup {name}". |  | ||||||
|  */ |  | ||||||
| void do_augroup(char_u *arg, int del_group) | void do_augroup(char_u *arg, int del_group) | ||||||
| { | { | ||||||
|   if (del_group) { |   if (del_group) { | ||||||
|     if (*arg == NUL) |     if (*arg == NUL) { | ||||||
|       EMSG(_(e_argreq)); |       EMSG(_(e_argreq)); | ||||||
|     else |     } else { | ||||||
|       au_del_group(arg); |       au_del_group(arg); | ||||||
|   } else if (STRICMP(arg, "end") == 0)   /* ":aug end": back to group 0 */ |     } | ||||||
|  |   } else if (STRICMP(arg, "end") == 0) {  // ":aug end": back to group 0 | ||||||
|     current_augroup = AUGROUP_DEFAULT; |     current_augroup = AUGROUP_DEFAULT; | ||||||
|   else if (*arg) {                  /* ":aug xxx": switch to group xxx */ |   } else if (*arg) {  // ":aug xxx": switch to group xxx | ||||||
|     int i = au_new_group(arg); |     int i = au_new_group(arg); | ||||||
|     if (i != AUGROUP_ERROR) |     if (i != AUGROUP_ERROR) | ||||||
|       current_augroup = i; |       current_augroup = i; | ||||||
|   } else {                        /* ":aug": list the group names */ |   } else {  // ":aug": list the group names | ||||||
|     msg_start(); |     msg_start(); | ||||||
|     for (int i = 0; i < augroups.ga_len; ++i) { |     for (int i = 0; i < augroups.ga_len; ++i) { | ||||||
|       if (AUGROUP_NAME(i) != NULL) { |       if (AUGROUP_NAME(i) != NULL) { | ||||||
| @@ -5957,38 +5957,38 @@ void au_event_restore(char_u *old_ei) | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | // Implements :autocmd. | ||||||
|  * do_autocmd() -- implements the :autocmd command.  Can be used in the | // Defines an autocmd (does not execute; cf. apply_autocmds_group). | ||||||
|  *  following ways: | // | ||||||
|  * | // Can be used in the following ways: | ||||||
|  * :autocmd <event> <pat> <cmd>	    Add <cmd> to the list of commands that | // | ||||||
|  *				    will be automatically executed for <event> | // :autocmd <event> <pat> <cmd>     Add <cmd> to the list of commands that | ||||||
|  *				    when editing a file matching <pat>, in | //                                  will be automatically executed for <event> | ||||||
|  *				    the current group. | //                                  when editing a file matching <pat>, in | ||||||
|  * :autocmd <event> <pat>	    Show the autocommands associated with | //                                  the current group. | ||||||
|  *				    <event> and <pat>. | // :autocmd <event> <pat>           Show the autocommands associated with | ||||||
|  * :autocmd <event>		    Show the autocommands associated with | //                                  <event> and <pat>. | ||||||
|  *				    <event>. | // :autocmd <event>                 Show the autocommands associated with | ||||||
|  * :autocmd			    Show all autocommands. | //                                  <event>. | ||||||
|  * :autocmd! <event> <pat> <cmd>    Remove all autocommands associated with | // :autocmd                         Show all autocommands. | ||||||
|  *				    <event> and <pat>, and add the command | // :autocmd! <event> <pat> <cmd>    Remove all autocommands associated with | ||||||
|  *				    <cmd>, for the current group. | //                                  <event> and <pat>, and add the command | ||||||
|  * :autocmd! <event> <pat>	    Remove all autocommands associated with | //                                  <cmd>, for the current group. | ||||||
|  *				    <event> and <pat> for the current group. | // :autocmd! <event> <pat>          Remove all autocommands associated with | ||||||
|  * :autocmd! <event>		    Remove all autocommands associated with | //                                  <event> and <pat> for the current group. | ||||||
|  *				    <event> for the current group. | // :autocmd! <event>                Remove all autocommands associated with | ||||||
|  * :autocmd!			    Remove ALL autocommands for the current | //                                  <event> for the current group. | ||||||
|  *				    group. | // :autocmd!                        Remove ALL autocommands for the current | ||||||
|  * | //                                  group. | ||||||
|  *  Multiple events and patterns may be given separated by commas.  Here are | // | ||||||
|  *  some examples: | //  Multiple events and patterns may be given separated by commas.  Here are | ||||||
|  * :autocmd bufread,bufenter *.c,*.h	set tw=0 smartindent noic | //  some examples: | ||||||
|  * :autocmd bufleave	     *		set tw=79 nosmartindent ic infercase | // :autocmd bufread,bufenter *.c,*.h    set tw=0 smartindent noic | ||||||
|  * | // :autocmd bufleave         *          set tw=79 nosmartindent ic infercase | ||||||
|  * :autocmd * *.c		show all autocommands for *.c files. | // | ||||||
|  * | // :autocmd * *.c               show all autocommands for *.c files. | ||||||
|  * Mostly a {group} argument can optionally appear before <event>. | // | ||||||
|  */ | // Mostly a {group} argument can optionally appear before <event>. | ||||||
| void do_autocmd(char_u *arg_in, int forceit) | void do_autocmd(char_u *arg_in, int forceit) | ||||||
| { | { | ||||||
|   char_u      *arg = arg_in; |   char_u      *arg = arg_in; | ||||||
| @@ -5997,6 +5997,7 @@ void do_autocmd(char_u *arg_in, int forceit) | |||||||
|   char_u      *cmd; |   char_u      *cmd; | ||||||
|   int need_free = false; |   int need_free = false; | ||||||
|   int nested = false; |   int nested = false; | ||||||
|  |   bool once = false; | ||||||
|   int group; |   int group; | ||||||
|  |  | ||||||
|   if (*arg == '|') { |   if (*arg == '|') { | ||||||
| @@ -6046,6 +6047,14 @@ void do_autocmd(char_u *arg_in, int forceit) | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // Check for "once" flag. | ||||||
|  |     cmd = skipwhite(cmd); | ||||||
|  |     if (*cmd != NUL && STRNCMP(cmd, "once", 4) == 0 | ||||||
|  |         && ascii_iswhite(cmd[4])) { | ||||||
|  |       once = true; | ||||||
|  |       cmd = skipwhite(cmd + 4); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // Check for "nested" flag. |     // Check for "nested" flag. | ||||||
|     cmd = skipwhite(cmd); |     cmd = skipwhite(cmd); | ||||||
|     if (*cmd != NUL && STRNCMP(cmd, "nested", 6) == 0 |     if (*cmd != NUL && STRNCMP(cmd, "nested", 6) == 0 | ||||||
| @@ -6081,7 +6090,8 @@ void do_autocmd(char_u *arg_in, int forceit) | |||||||
|   if (*arg == '*' || *arg == NUL || *arg == '|') { |   if (*arg == '*' || *arg == NUL || *arg == '|') { | ||||||
|     for (event_T event = (event_T)0; (int)event < (int)NUM_EVENTS; |     for (event_T event = (event_T)0; (int)event < (int)NUM_EVENTS; | ||||||
|          event = (event_T)((int)event + 1)) { |          event = (event_T)((int)event + 1)) { | ||||||
|       if (do_autocmd_event(event, pat, nested, cmd, forceit, group) == FAIL) { |       if (do_autocmd_event(event, pat, once, nested, cmd, forceit, group) | ||||||
|  |           == FAIL) { | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| @@ -6089,7 +6099,8 @@ void do_autocmd(char_u *arg_in, int forceit) | |||||||
|     while (*arg && *arg != '|' && !ascii_iswhite(*arg)) { |     while (*arg && *arg != '|' && !ascii_iswhite(*arg)) { | ||||||
|       event_T event = event_name2nr(arg, &arg); |       event_T event = event_name2nr(arg, &arg); | ||||||
|       assert(event < NUM_EVENTS); |       assert(event < NUM_EVENTS); | ||||||
|       if (do_autocmd_event(event, pat, nested, cmd, forceit, group) == FAIL) { |       if (do_autocmd_event(event, pat, once, nested, cmd, forceit, group) | ||||||
|  |           == FAIL) { | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| @@ -6127,14 +6138,15 @@ static int au_get_grouparg(char_u **argp) | |||||||
|   return group; |   return group; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | // do_autocmd() for one event. | ||||||
|  * do_autocmd() for one event. | // Defines an autocmd (does not execute; cf. apply_autocmds_group). | ||||||
|  * If *pat == NUL do for all patterns. | // | ||||||
|  * If *cmd == NUL show entries. | // If *pat == NUL: do for all patterns. | ||||||
|  * If forceit == TRUE delete entries. | // If *cmd == NUL: show entries. | ||||||
|  * If group is not AUGROUP_ALL, only use this group. | // If forceit == TRUE: delete entries. | ||||||
|  */ | // If group is not AUGROUP_ALL: only use this group. | ||||||
| static int do_autocmd_event(event_T event, char_u *pat, int nested, char_u *cmd, int forceit, int group) | static int do_autocmd_event(event_T event, char_u *pat, bool once, int nested, | ||||||
|  |                             char_u *cmd, int forceit, int group) | ||||||
| { | { | ||||||
|   AutoPat     *ap; |   AutoPat     *ap; | ||||||
|   AutoPat     **prev_ap; |   AutoPat     **prev_ap; | ||||||
| @@ -6333,6 +6345,7 @@ static int do_autocmd_event(event_T event, char_u *pat, int nested, char_u *cmd, | |||||||
|       ac->scriptID = current_SID; |       ac->scriptID = current_SID; | ||||||
|       ac->next = NULL; |       ac->next = NULL; | ||||||
|       *prev_ac = ac; |       *prev_ac = ac; | ||||||
|  |       ac->once = once; | ||||||
|       ac->nested = nested; |       ac->nested = nested; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -6996,7 +7009,7 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, | |||||||
|   patcmd.event = event; |   patcmd.event = event; | ||||||
|   patcmd.arg_bufnr = autocmd_bufnr; |   patcmd.arg_bufnr = autocmd_bufnr; | ||||||
|   patcmd.next = NULL; |   patcmd.next = NULL; | ||||||
|   auto_next_pat(&patcmd, FALSE); |   auto_next_pat(&patcmd, false); | ||||||
|  |  | ||||||
|   /* found one, start executing the autocommands */ |   /* found one, start executing the autocommands */ | ||||||
|   if (patcmd.curpat != NULL) { |   if (patcmd.curpat != NULL) { | ||||||
| @@ -7020,8 +7033,11 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, | |||||||
|     } |     } | ||||||
|     ap->last = true; |     ap->last = true; | ||||||
|     check_lnums(true);  // make sure cursor and topline are valid |     check_lnums(true);  // make sure cursor and topline are valid | ||||||
|  |  | ||||||
|  |     // Execute the autocmd. The `getnextac` callback handles iteration. | ||||||
|     do_cmdline(NULL, getnextac, (void *)&patcmd, |     do_cmdline(NULL, getnextac, (void *)&patcmd, | ||||||
|                DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT); |                DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT); | ||||||
|  |  | ||||||
|     if (eap != NULL) { |     if (eap != NULL) { | ||||||
|       (void)set_cmdarg(NULL, save_cmdarg); |       (void)set_cmdarg(NULL, save_cmdarg); | ||||||
|       set_vim_var_nr(VV_CMDBANG, save_cmdbang); |       set_vim_var_nr(VV_CMDBANG, save_cmdbang); | ||||||
| @@ -7233,12 +7249,18 @@ char_u *getnextac(int c, void *cookie, int indent) | |||||||
|     verbose_leave_scroll(); |     verbose_leave_scroll(); | ||||||
|   } |   } | ||||||
|   retval = vim_strsave(ac->cmd); |   retval = vim_strsave(ac->cmd); | ||||||
|  |   // Remove one-shot ("once") autocmd in anticipation of its execution. | ||||||
|  |   if (ac->once) { | ||||||
|  |     au_del_cmd(ac); | ||||||
|  |   } | ||||||
|   autocmd_nested = ac->nested; |   autocmd_nested = ac->nested; | ||||||
|   current_SID = ac->scriptID; |   current_SID = ac->scriptID; | ||||||
|   if (ac->last) |   if (ac->last) { | ||||||
|     acp->nextcmd = NULL; |     acp->nextcmd = NULL; | ||||||
|   else |   } else { | ||||||
|     acp->nextcmd = ac->next; |     acp->nextcmd = ac->next; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   return retval; |   return retval; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,15 +1,18 @@ | |||||||
| local helpers = require('test.functional.helpers')(after_each) | local helpers = require('test.functional.helpers')(after_each) | ||||||
|  |  | ||||||
|  | local dedent = helpers.dedent | ||||||
| local eq = helpers.eq | local eq = helpers.eq | ||||||
| local eval = helpers.eval | local eval = helpers.eval | ||||||
|  | local feed = helpers.feed | ||||||
| local clear = helpers.clear | local clear = helpers.clear | ||||||
| local meths = helpers.meths | local meths = helpers.meths | ||||||
|  | local funcs = helpers.funcs | ||||||
| local expect = helpers.expect | local expect = helpers.expect | ||||||
| local command = helpers.command | local command = helpers.command | ||||||
| local exc_exec = helpers.exc_exec | local exc_exec = helpers.exc_exec | ||||||
| local curbufmeths = helpers.curbufmeths | local curbufmeths = helpers.curbufmeths | ||||||
|  |  | ||||||
| describe('autocmds:', function() | describe('autocmd', function() | ||||||
|   before_each(clear) |   before_each(clear) | ||||||
|  |  | ||||||
|   it(':tabnew triggers events in the correct order', function() |   it(':tabnew triggers events in the correct order', function() | ||||||
| @@ -55,4 +58,72 @@ describe('autocmds:', function() | |||||||
|         end of test file xx]]) |         end of test file xx]]) | ||||||
|     end) |     end) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
|  |   it('once', function()  -- :help autocmd-once | ||||||
|  |     -- | ||||||
|  |     -- ":autocmd ... once" executes its handler once, then removes the handler. | ||||||
|  |     -- | ||||||
|  |     local expected = { | ||||||
|  |       'Many1', | ||||||
|  |       'Once1', | ||||||
|  |       'Once2', | ||||||
|  |       'Many2', | ||||||
|  |       'Once3', | ||||||
|  |       'Many1', | ||||||
|  |       'Many2', | ||||||
|  |       'Many1', | ||||||
|  |       'Many2', | ||||||
|  |     } | ||||||
|  |     command('let g:foo = []') | ||||||
|  |     command('autocmd TabNew * :call add(g:foo, "Many1")') | ||||||
|  |     command('autocmd TabNew * once :call add(g:foo, "Once1")') | ||||||
|  |     command('autocmd TabNew * once :call add(g:foo, "Once2")') | ||||||
|  |     command('autocmd TabNew * :call add(g:foo, "Many2")') | ||||||
|  |     command('autocmd TabNew * once :call add(g:foo, "Once3")') | ||||||
|  |     eq(dedent([[ | ||||||
|  |  | ||||||
|  |        --- Autocommands --- | ||||||
|  |        TabNew | ||||||
|  |            *         :call add(g:foo, "Many1") | ||||||
|  |                      :call add(g:foo, "Once1") | ||||||
|  |                      :call add(g:foo, "Once2") | ||||||
|  |                      :call add(g:foo, "Many2") | ||||||
|  |                      :call add(g:foo, "Once3")]]), | ||||||
|  |        funcs.execute('autocmd Tabnew')) | ||||||
|  |     command('tabnew') | ||||||
|  |     command('tabnew') | ||||||
|  |     command('tabnew') | ||||||
|  |     eq(expected, eval('g:foo')) | ||||||
|  |     eq(dedent([[ | ||||||
|  |  | ||||||
|  |        --- Autocommands --- | ||||||
|  |        TabNew | ||||||
|  |            *         :call add(g:foo, "Many1") | ||||||
|  |                      :call add(g:foo, "Many2")]]), | ||||||
|  |        funcs.execute('autocmd Tabnew')) | ||||||
|  |  | ||||||
|  |     -- | ||||||
|  |     -- ":autocmd ... once" handlers can be deleted. | ||||||
|  |     -- | ||||||
|  |     expected = {} | ||||||
|  |     command('let g:foo = []') | ||||||
|  |     command('autocmd TabNew * once :call add(g:foo, "Once1")') | ||||||
|  |     command('autocmd! TabNew') | ||||||
|  |     command('tabnew') | ||||||
|  |     eq(expected, eval('g:foo')) | ||||||
|  |  | ||||||
|  |     -- | ||||||
|  |     -- ":autocmd ... <buffer> once nested" | ||||||
|  |     -- | ||||||
|  |     expected = { | ||||||
|  |       'OptionSet-Once', | ||||||
|  |       'CursorMoved-Once', | ||||||
|  |     } | ||||||
|  |     command('let g:foo = []') | ||||||
|  |     command('autocmd OptionSet binary once nested :call add(g:foo, "OptionSet-Once")') | ||||||
|  |     command('autocmd CursorMoved <buffer> once nested setlocal binary|:call add(g:foo, "CursorMoved-Once")') | ||||||
|  |     command("put ='foo bar baz'") | ||||||
|  |     feed('0llhlh') | ||||||
|  |     eq(expected, eval('g:foo')) | ||||||
|  |   end) | ||||||
| end) | end) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Justin M. Keyes
					Justin M. Keyes