Skip to content

Commit 648cf4e

Browse files
seandewargithub-actions[bot]
authored andcommitted
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. (cherry picked from commit 1cde712)
1 parent bea500d commit 648cf4e

File tree

3 files changed

+71
-48
lines changed

3 files changed

+71
-48
lines changed

src/nvim/api/autocmd.c

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -823,12 +823,10 @@ static Array get_patterns_from_pattern_or_buf(Object pattern, bool has_buffer, B
823823
if (pattern.type != kObjectTypeNil) {
824824
if (pattern.type == kObjectTypeString) {
825825
const char *pat = pattern.data.string.data;
826-
size_t patlen = aucmd_pattern_length(pat);
826+
size_t patlen = aucmd_span_pattern(pat, &pat);
827827
while (patlen) {
828828
kvi_push(patterns, CBUF_TO_ARENA_OBJ(arena, pat, patlen));
829-
830-
pat = aucmd_next_pattern(pat, patlen);
831-
patlen = aucmd_pattern_length(pat);
829+
patlen = aucmd_span_pattern(pat + patlen, &pat);
832830
}
833831
} else if (pattern.type == kObjectTypeArray) {
834832
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
838836
Array array = pattern.data.array;
839837
FOREACH_ITEM(array, entry, {
840838
const char *pat = entry.data.string.data;
841-
size_t patlen = aucmd_pattern_length(pat);
839+
size_t patlen = aucmd_span_pattern(pat, &pat);
842840
while (patlen) {
843841
kvi_push(patterns, CBUF_TO_ARENA_OBJ(arena, pat, patlen));
844-
845-
pat = aucmd_next_pattern(pat, patlen);
846-
patlen = aucmd_pattern_length(pat);
842+
patlen = aucmd_span_pattern(pat + patlen, &pat);
847843
}
848844
})
849845
} else {

src/nvim/autocmd.c

Lines changed: 20 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ static void au_show_for_event(int group, event_T event, const char *pat)
156156
// Empty pattern shows all autocommands for this event
157157
int patlen = 0;
158158
if (*pat != NUL) {
159-
patlen = (int)aucmd_pattern_length(pat);
159+
patlen = (int)aucmd_span_pattern(pat, &pat);
160160
if (patlen == 0) { // Don't show if it contains only commas
161161
return;
162162
}
@@ -279,8 +279,7 @@ static void au_show_for_event(int group, event_T event, const char *pat)
279279
}
280280
}
281281

282-
pat = aucmd_next_pattern(endpat, 0);
283-
patlen = (int)aucmd_pattern_length(pat);
282+
patlen = (int)aucmd_span_pattern(endpat, &pat);
284283
} while (patlen);
285284
}
286285

@@ -906,7 +905,7 @@ int do_autocmd_event(event_T event, const char *pat, bool once, int nested, cons
906905
}
907906

908907
// Loop through all the specified patterns.
909-
int patlen = (int)aucmd_pattern_length(pat);
908+
int patlen = (int)aucmd_span_pattern(pat, &pat);
910909
while (patlen) {
911910
const char *endpat = pat + patlen;
912911

@@ -952,8 +951,7 @@ int do_autocmd_event(event_T event, const char *pat, bool once, int nested, cons
952951
autocmd_register(0, event, pat, patlen, group, once, nested, NULL, cmd, &handler_fn);
953952
}
954953

955-
pat = aucmd_next_pattern(endpat, 0);
956-
patlen = (int)aucmd_pattern_length(pat);
954+
patlen = (int)aucmd_span_pattern(endpat, &pat);
957955
}
958956

959957
au_cleanup(); // may really delete removed patterns/commands now
@@ -1091,46 +1089,28 @@ int autocmd_register(int64_t id, event_T event, const char *pat, int patlen, int
10911089
return OK;
10921090
}
10931091

1094-
size_t aucmd_pattern_length(const char *pat)
1095-
FUNC_ATTR_PURE
1092+
size_t aucmd_span_pattern(const char *pat, const char **start)
1093+
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
10961094
{
1097-
if (*pat == NUL) {
1098-
return 0;
1095+
// Skip leading commas.
1096+
while (*pat == ',') {
1097+
pat++;
10991098
}
11001099

1101-
const char *endpat;
1102-
1103-
for (; *pat; pat = endpat + 1) {
1104-
// Find end of the pattern.
1105-
// Watch out for a comma in braces, like "*.\{obj,o\}".
1106-
endpat = pat;
1107-
// ignore single comma
1108-
if (*endpat == ',') {
1109-
continue;
1100+
// Find end of the pattern.
1101+
// Watch out for a comma in braces, like "*.\{obj,o\}".
1102+
const char *p = pat;
1103+
int brace_level = 0;
1104+
for (; *p && (*p != ',' || brace_level || (p > pat && p[-1] == '\\')); p++) {
1105+
if (*p == '{') {
1106+
brace_level++;
1107+
} else if (*p == '}') {
1108+
brace_level--;
11101109
}
1111-
int brace_level = 0;
1112-
for (; *endpat && (*endpat != ',' || brace_level || endpat[-1] == '\\'); endpat++) {
1113-
if (*endpat == '{') {
1114-
brace_level++;
1115-
} else if (*endpat == '}') {
1116-
brace_level--;
1117-
}
1118-
}
1119-
1120-
return (size_t)(endpat - pat);
11211110
}
11221111

1123-
return strlen(pat);
1124-
}
1125-
1126-
const char *aucmd_next_pattern(const char *pat, size_t patlen)
1127-
FUNC_ATTR_PURE
1128-
{
1129-
pat = pat + patlen;
1130-
if (*pat == ',') {
1131-
pat = pat + 1;
1132-
}
1133-
return pat;
1112+
*start = pat;
1113+
return (size_t)(p - pat);
11341114
}
11351115

11361116
/// Implementation of ":doautocmd [group] event [fname]".

test/functional/autocmd/autocmd_spec.lua

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,4 +781,51 @@ describe('autocmd', function()
781781
fn.execute('autocmd BufEnter <buffer=1>,bar,bar,foo,foo,<buffer>,<buffer=6>,<buffer>')
782782
)
783783
end)
784+
785+
it('parses empty comma-delimited patterns correctly', function()
786+
exec [[
787+
autocmd User , "
788+
autocmd User ,, "
789+
autocmd User ,,according,to,,all,known,,,laws,, "
790+
]]
791+
api.nvim_create_autocmd('User', { pattern = ',,of,,,aviation,,,,there,,', command = '' })
792+
api.nvim_create_autocmd('User', {
793+
pattern = { ',,,,is,,no', ',,way,,,', 'a,,bee{,should be, able to,},fly' },
794+
command = '',
795+
})
796+
eq(
797+
{
798+
'according',
799+
'to',
800+
'all',
801+
'known',
802+
'laws',
803+
'of',
804+
'aviation',
805+
'there',
806+
'is',
807+
'no',
808+
'way',
809+
'a',
810+
'bee{,should be, able to,}',
811+
'fly',
812+
},
813+
exec_lua(function()
814+
return vim.tbl_map(function(v)
815+
return v.pattern
816+
end, vim.api.nvim_get_autocmds({ event = 'User' }))
817+
end)
818+
)
819+
eq(
820+
dedent([[
821+
822+
--- Autocommands ---
823+
User
824+
there
825+
is
826+
a
827+
fly]]),
828+
fn.execute('autocmd User ,,,there,is,,a,fly,,')
829+
)
830+
end)
784831
end)

0 commit comments

Comments
 (0)