mirror of
https://github.com/neovim/neovim.git
synced 2026-01-04 18:37:02 +10:00
fix(autocmd): skip empty comma-separated patterns properly
Problem: empty comma-separated patterns in an aupat aren't skipped correctly. Solution: skip consecutive commas in an aupat. Also simplify the logic.
This commit is contained in:
@@ -823,12 +823,10 @@ static Array get_patterns_from_pattern_or_buf(Object pattern, bool has_buffer, B
|
||||
if (pattern.type != kObjectTypeNil) {
|
||||
if (pattern.type == kObjectTypeString) {
|
||||
const char *pat = pattern.data.string.data;
|
||||
size_t patlen = aucmd_pattern_length(pat);
|
||||
size_t patlen = aucmd_span_pattern(pat, &pat);
|
||||
while (patlen) {
|
||||
kvi_push(patterns, CBUF_TO_ARENA_OBJ(arena, pat, patlen));
|
||||
|
||||
pat = aucmd_next_pattern(pat, patlen);
|
||||
patlen = aucmd_pattern_length(pat);
|
||||
patlen = aucmd_span_pattern(pat + patlen, &pat);
|
||||
}
|
||||
} else if (pattern.type == kObjectTypeArray) {
|
||||
if (!check_string_array(pattern.data.array, "pattern", true, err)) {
|
||||
@@ -838,12 +836,10 @@ static Array get_patterns_from_pattern_or_buf(Object pattern, bool has_buffer, B
|
||||
Array array = pattern.data.array;
|
||||
FOREACH_ITEM(array, entry, {
|
||||
const char *pat = entry.data.string.data;
|
||||
size_t patlen = aucmd_pattern_length(pat);
|
||||
size_t patlen = aucmd_span_pattern(pat, &pat);
|
||||
while (patlen) {
|
||||
kvi_push(patterns, CBUF_TO_ARENA_OBJ(arena, pat, patlen));
|
||||
|
||||
pat = aucmd_next_pattern(pat, patlen);
|
||||
patlen = aucmd_pattern_length(pat);
|
||||
patlen = aucmd_span_pattern(pat + patlen, &pat);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -165,7 +165,7 @@ static void au_show_for_event(int group, event_T event, const char *pat)
|
||||
// Empty pattern shows all autocommands for this event
|
||||
int patlen = 0;
|
||||
if (*pat != NUL) {
|
||||
patlen = (int)aucmd_pattern_length(pat);
|
||||
patlen = (int)aucmd_span_pattern(pat, &pat);
|
||||
if (patlen == 0) { // Don't show if it contains only commas
|
||||
return;
|
||||
}
|
||||
@@ -288,8 +288,7 @@ static void au_show_for_event(int group, event_T event, const char *pat)
|
||||
}
|
||||
}
|
||||
|
||||
pat = aucmd_next_pattern(endpat, 0);
|
||||
patlen = (int)aucmd_pattern_length(pat);
|
||||
patlen = (int)aucmd_span_pattern(endpat, &pat);
|
||||
} while (patlen);
|
||||
}
|
||||
|
||||
@@ -920,7 +919,7 @@ 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);
|
||||
int patlen = (int)aucmd_span_pattern(pat, &pat);
|
||||
while (patlen) {
|
||||
const char *endpat = pat + patlen;
|
||||
|
||||
@@ -966,8 +965,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(endpat, 0);
|
||||
patlen = (int)aucmd_pattern_length(pat);
|
||||
patlen = (int)aucmd_span_pattern(endpat, &pat);
|
||||
}
|
||||
|
||||
au_cleanup(); // may really delete removed patterns/commands now
|
||||
@@ -1105,46 +1103,28 @@ int autocmd_register(int64_t id, event_T event, const char *pat, int patlen, int
|
||||
return OK;
|
||||
}
|
||||
|
||||
size_t aucmd_pattern_length(const char *pat)
|
||||
FUNC_ATTR_PURE
|
||||
size_t aucmd_span_pattern(const char *pat, const char **start)
|
||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
if (*pat == NUL) {
|
||||
return 0;
|
||||
// Skip leading commas.
|
||||
while (*pat == ',') {
|
||||
pat++;
|
||||
}
|
||||
|
||||
const char *endpat;
|
||||
|
||||
for (; *pat; pat = endpat + 1) {
|
||||
// Find end of the pattern.
|
||||
// Watch out for a comma in braces, like "*.\{obj,o\}".
|
||||
endpat = pat;
|
||||
// ignore single comma
|
||||
if (*endpat == ',') {
|
||||
continue;
|
||||
// Find end of the pattern.
|
||||
// Watch out for a comma in braces, like "*.\{obj,o\}".
|
||||
const char *p = pat;
|
||||
int brace_level = 0;
|
||||
for (; *p && (*p != ',' || brace_level || (p > pat && p[-1] == '\\')); p++) {
|
||||
if (*p == '{') {
|
||||
brace_level++;
|
||||
} else if (*p == '}') {
|
||||
brace_level--;
|
||||
}
|
||||
int brace_level = 0;
|
||||
for (; *endpat && (*endpat != ',' || brace_level || endpat[-1] == '\\'); endpat++) {
|
||||
if (*endpat == '{') {
|
||||
brace_level++;
|
||||
} else if (*endpat == '}') {
|
||||
brace_level--;
|
||||
}
|
||||
}
|
||||
|
||||
return (size_t)(endpat - pat);
|
||||
}
|
||||
|
||||
return strlen(pat);
|
||||
}
|
||||
|
||||
const char *aucmd_next_pattern(const char *pat, size_t patlen)
|
||||
FUNC_ATTR_PURE
|
||||
{
|
||||
pat = pat + patlen;
|
||||
if (*pat == ',') {
|
||||
pat = pat + 1;
|
||||
}
|
||||
return pat;
|
||||
*start = pat;
|
||||
return (size_t)(p - pat);
|
||||
}
|
||||
|
||||
/// Implementation of ":doautocmd [group] event [fname]".
|
||||
|
||||
@@ -800,4 +800,51 @@ describe('autocmd', function()
|
||||
fn.execute('autocmd BufEnter <buffer=1>,bar,bar,foo,foo,<buffer>,<buffer=6>,<buffer>')
|
||||
)
|
||||
end)
|
||||
|
||||
it('parses empty comma-delimited patterns correctly', function()
|
||||
exec [[
|
||||
autocmd User , "
|
||||
autocmd User ,, "
|
||||
autocmd User ,,according,to,,all,known,,,laws,, "
|
||||
]]
|
||||
api.nvim_create_autocmd('User', { pattern = ',,of,,,aviation,,,,there,,', command = '' })
|
||||
api.nvim_create_autocmd('User', {
|
||||
pattern = { ',,,,is,,no', ',,way,,,', 'a,,bee{,should be, able to,},fly' },
|
||||
command = '',
|
||||
})
|
||||
eq(
|
||||
{
|
||||
'according',
|
||||
'to',
|
||||
'all',
|
||||
'known',
|
||||
'laws',
|
||||
'of',
|
||||
'aviation',
|
||||
'there',
|
||||
'is',
|
||||
'no',
|
||||
'way',
|
||||
'a',
|
||||
'bee{,should be, able to,}',
|
||||
'fly',
|
||||
},
|
||||
exec_lua(function()
|
||||
return vim.tbl_map(function(v)
|
||||
return v.pattern
|
||||
end, vim.api.nvim_get_autocmds({ event = 'User' }))
|
||||
end)
|
||||
)
|
||||
eq(
|
||||
dedent([[
|
||||
|
||||
--- Autocommands ---
|
||||
User
|
||||
there
|
||||
is
|
||||
a
|
||||
fly]]),
|
||||
fn.execute('autocmd User ,,,there,is,,a,fly,,')
|
||||
)
|
||||
end)
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user