From bea500dbeb3eba9b43a5ac8b14c04056234b82fd Mon Sep 17 00:00:00 2001 From: Sean Dewar <6256228+seandewar@users.noreply.github.com> Date: Sat, 20 Dec 2025 16:30:28 +0000 Subject: [PATCH] fix(autocmd): parsing of comma-separated buflocal patterns Problem: patterns after a buffer-local pattern in a comma-separated aupat are ignored. Solution: ensure pat refers to the original buffer after each pattern, not the buflocal_pat buffer, and when printing make sure it's normalized for each pattern, not just the first. Also simplify the logic when printing all autocommands for an event, and ensure headings aren't repeated for each event when printing autocommands. (cherry picked from commit 6d2330f50d07dd2d3c4c01ee37f918b5d320d998) --- src/nvim/autocmd.c | 53 ++++++++---------- test/functional/autocmd/autocmd_spec.lua | 69 ++++++++++++++++++++++++ test/functional/autocmd/show_spec.lua | 12 +++++ 3 files changed, 104 insertions(+), 30 deletions(-) diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 189e9d558b..90ce68410d 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -145,6 +145,7 @@ static void au_show_for_all_events(int group, const char *pat) } static void au_show_for_event(int group, event_T event, const char *pat) + FUNC_ATTR_NONNULL_ALL { AutoCmdVec *const acs = &autocmds[(int)event]; // Return early if there are no autocmds for this event @@ -152,10 +153,23 @@ static void au_show_for_event(int group, event_T event, const char *pat) return; } - char buflocal_pat[BUFLOCAL_PAT_LEN]; // for "" - int patlen; + // Empty pattern shows all autocommands for this event + int patlen = 0; if (*pat != NUL) { patlen = (int)aucmd_pattern_length(pat); + if (patlen == 0) { // Don't show if it contains only commas + return; + } + } + + char buflocal_pat[BUFLOCAL_PAT_LEN]; // for "" + int last_group = AUGROUP_ERROR; + const char *last_group_name = NULL; + + // Loop through all the specified patterns. + do { + AutoPat *last_ap = NULL; + const char *endpat = pat + patlen; // detect special buffer-local patterns if (aupat_is_buflocal(pat, patlen)) { @@ -165,21 +179,6 @@ static void au_show_for_event(int group, event_T event, const char *pat) patlen = (int)strlen(buflocal_pat); } - if (patlen == 0) { - return; - } - assert(*pat != NUL); - } else { - pat = NULL; - patlen = 0; - } - - // Loop through all the specified patterns. - while (true) { - AutoPat *last_ap = NULL; - int last_group = AUGROUP_ERROR; - const char *last_group_name = NULL; - for (size_t i = 0; i < kv_size(*acs); i++) { AutoCmd *const ac = &kv_A(*acs, i); @@ -195,7 +194,7 @@ static void au_show_for_event(int group, event_T event, const char *pat) // For , this condition works because we normalize // all buffer-local patterns. if ((group != AUGROUP_ALL && ac->pat->group != group) - || (pat != NULL + || (patlen && (ac->pat->patlen != patlen || strncmp(pat, ac->pat->pat, (size_t)patlen) != 0))) { continue; } @@ -280,17 +279,9 @@ static void au_show_for_event(int group, event_T event, const char *pat) } } - // If a pattern is provided, find next pattern. Otherwise exit after single iteration. - if (pat != NULL) { - pat = aucmd_next_pattern(pat, (size_t)patlen); - patlen = (int)aucmd_pattern_length(pat); - if (patlen == 0) { - break; - } - } else { - break; - } - } + pat = aucmd_next_pattern(endpat, 0); + patlen = (int)aucmd_pattern_length(pat); + } while (patlen); } // Delete autocommand. @@ -917,6 +908,8 @@ int do_autocmd_event(event_T event, const char *pat, bool once, int nested, cons // Loop through all the specified patterns. int patlen = (int)aucmd_pattern_length(pat); while (patlen) { + const char *endpat = pat + patlen; + // detect special buffer-local patterns bool is_buflocal = aupat_is_buflocal(pat, patlen); if (is_buflocal) { @@ -959,7 +952,7 @@ int do_autocmd_event(event_T event, const char *pat, bool once, int nested, cons autocmd_register(0, event, pat, patlen, group, once, nested, NULL, cmd, &handler_fn); } - pat = aucmd_next_pattern(pat, (size_t)patlen); + pat = aucmd_next_pattern(endpat, 0); patlen = (int)aucmd_pattern_length(pat); } diff --git a/test/functional/autocmd/autocmd_spec.lua b/test/functional/autocmd/autocmd_spec.lua index 9b5c1b5250..bd9cf872cf 100644 --- a/test/functional/autocmd/autocmd_spec.lua +++ b/test/functional/autocmd/autocmd_spec.lua @@ -712,4 +712,73 @@ describe('autocmd', function() ]] eq('flarb', fn.bufname()) end) + + it('does not ignore comma-separated patterns after a buffer-local pattern', function() + exec [[ + edit baz " reuses buffer 1 + edit bazinga + edit bar + edit boop + edit foo + edit floob + + let g:events1 = [] + autocmd BufEnter ,,boop,bar let g:events1 += [expand('')] + let g:events2 = [] + augroup flobby + autocmd BufEnter ,foo let g:events2 += [expand('')] + autocmd BufEnter foo, let g:events2 += ['flobby ' .. expand('')] + augroup END + ]] + eq( + dedent([=[ + + --- Autocommands --- + BufEnter + + let g:events1 += [expand('')] + + let g:events1 += [expand('')] + boop let g:events1 += [expand('')] + bar let g:events1 += [expand('')] + flobby BufEnter + + let g:events2 += [expand('')] + foo let g:events2 += [expand('')] + let g:events2 += ['flobby ' .. expand('')] + + let g:events2 += ['flobby ' .. expand('')]]=]), + fn.execute('autocmd BufEnter') + ) + command('bufdo "') + eq({ 'baz', 'bar', 'boop', 'floob' }, eval('g:events1')) + eq({ 'bazinga', 'flobby bar', 'foo', 'flobby foo' }, eval('g:events2')) + + -- Also make sure it doesn't repeat the group/event name or pattern for each printed event. + -- Do however repeat the pattern if the user specified it multiple times to be printed. + -- These conditions aim to make the output consistent with Vim. + eq( + dedent([=[ + + --- Autocommands --- + BufEnter + + let g:events1 += [expand('')] + bar let g:events1 += [expand('')] + bar let g:events1 += [expand('')] + flobby BufEnter + foo let g:events2 += [expand('')] + let g:events2 += ['flobby ' .. expand('')] + foo let g:events2 += [expand('')] + let g:events2 += ['flobby ' .. expand('')] + BufEnter + + let g:events1 += [expand('')] + + let g:events1 += [expand('')] + + let g:events1 += [expand('')]]=]), + fn.execute('autocmd BufEnter ,bar,bar,foo,foo,,,') + ) + end) end) diff --git a/test/functional/autocmd/show_spec.lua b/test/functional/autocmd/show_spec.lua index fcac706349..395e6d5144 100644 --- a/test/functional/autocmd/show_spec.lua +++ b/test/functional/autocmd/show_spec.lua @@ -188,6 +188,18 @@ describe(':autocmd', function() B echo "B3"]]), fn.execute('autocmd test_3 * B') ) + eq( + dedent([[ + + --- Autocommands ---]]), + fn.execute('autocmd * ,') + ) + eq( + dedent([[ + + --- Autocommands ---]]), + fn.execute('autocmd * ,,,') + ) end) it('should skip consecutive patterns', function()