mirror of
https://github.com/neovim/neovim.git
synced 2026-01-06 11:27:26 +10:00
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.
This commit is contained in:
@@ -154,6 +154,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
|
||||
@@ -161,10 +162,23 @@ static void au_show_for_event(int group, event_T event, const char *pat)
|
||||
return;
|
||||
}
|
||||
|
||||
char buflocal_pat[BUFLOCAL_PAT_LEN]; // for "<buffer=X>"
|
||||
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 "<buffer=X>"
|
||||
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[=X]> buffer-local patterns
|
||||
if (aupat_is_buflocal(pat, patlen)) {
|
||||
@@ -174,21 +188,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);
|
||||
|
||||
@@ -204,7 +203,7 @@ static void au_show_for_event(int group, event_T event, const char *pat)
|
||||
// For <buffer[=X]>, 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;
|
||||
}
|
||||
@@ -289,17 +288,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.
|
||||
@@ -931,6 +922,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[=X]> buffer-local patterns
|
||||
bool is_buflocal = aupat_is_buflocal(pat, patlen);
|
||||
if (is_buflocal) {
|
||||
@@ -973,7 +966,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -731,4 +731,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 <buffer>,<buffer=1>,boop,bar let g:events1 += [expand('<afile>')]
|
||||
let g:events2 = []
|
||||
augroup flobby
|
||||
autocmd BufEnter <buffer=2>,foo let g:events2 += [expand('<afile>')]
|
||||
autocmd BufEnter foo,<buffer=3> let g:events2 += ['flobby ' .. expand('<afile>')]
|
||||
augroup END
|
||||
]]
|
||||
eq(
|
||||
dedent([=[
|
||||
|
||||
--- Autocommands ---
|
||||
BufEnter
|
||||
<buffer=6>
|
||||
let g:events1 += [expand('<afile>')]
|
||||
<buffer=1>
|
||||
let g:events1 += [expand('<afile>')]
|
||||
boop let g:events1 += [expand('<afile>')]
|
||||
bar let g:events1 += [expand('<afile>')]
|
||||
flobby BufEnter
|
||||
<buffer=2>
|
||||
let g:events2 += [expand('<afile>')]
|
||||
foo let g:events2 += [expand('<afile>')]
|
||||
let g:events2 += ['flobby ' .. expand('<afile>')]
|
||||
<buffer=3>
|
||||
let g:events2 += ['flobby ' .. expand('<afile>')]]=]),
|
||||
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
|
||||
<buffer=1>
|
||||
let g:events1 += [expand('<afile>')]
|
||||
bar let g:events1 += [expand('<afile>')]
|
||||
bar let g:events1 += [expand('<afile>')]
|
||||
flobby BufEnter
|
||||
foo let g:events2 += [expand('<afile>')]
|
||||
let g:events2 += ['flobby ' .. expand('<afile>')]
|
||||
foo let g:events2 += [expand('<afile>')]
|
||||
let g:events2 += ['flobby ' .. expand('<afile>')]
|
||||
BufEnter
|
||||
<buffer=6>
|
||||
let g:events1 += [expand('<afile>')]
|
||||
<buffer=6>
|
||||
let g:events1 += [expand('<afile>')]
|
||||
<buffer=6>
|
||||
let g:events1 += [expand('<afile>')]]=]),
|
||||
fn.execute('autocmd BufEnter <buffer=1>,bar,bar,foo,foo,<buffer>,<buffer=6>,<buffer>')
|
||||
)
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user