From 29d74cf5268326ac24f1b9e7cca4b2e8280b6509 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 25 Jun 2022 19:50:06 +0200 Subject: [PATCH 1/3] feat(api): support pattern array for exec_autocmds --- src/nvim/api/autocmd.c | 36 ++++++++++++---------------- test/functional/api/autocmd_spec.lua | 22 ++++++++++++++++- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 01f76c762e..5967c71509 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -743,11 +743,8 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) bool modeline = true; buf_T *buf = curbuf; - bool set_buf = false; - - char_u *pattern = NULL; - bool set_pattern = false; + Array patterns = ARRAY_DICT_INIT; Array event_array = ARRAY_DICT_INIT; if (!unpack_string_or_array(&event_array, &event, "event", true, err)) { @@ -787,35 +784,32 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) } buf = find_buffer_by_handle((Buffer)buf_obj.data.integer, err); - set_buf = true; if (ERROR_SET(err)) { goto cleanup; } } - if (opts->pattern.type != kObjectTypeNil) { - if (opts->pattern.type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, "'pattern' must be a string"); - goto cleanup; - } + if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, opts->buffer, err)) { + goto cleanup; + } - pattern = vim_strsave((char_u *)opts->pattern.data.string.data); - set_pattern = true; + if (patterns.size == 0) { + ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING("*"))); } modeline = api_object_to_bool(opts->modeline, "modeline", true, err); - if (set_pattern && set_buf) { - api_set_error(err, kErrorTypeValidation, "must not set 'buffer' and 'pattern'"); - goto cleanup; - } - bool did_aucmd = false; FOREACH_ITEM(event_array, event_str, { GET_ONE_EVENT(event_nr, event_str, cleanup) - did_aucmd |= apply_autocmds_group(event_nr, pattern, NULL, true, au_group, buf, NULL); + for (size_t i = 0; i < patterns.size; i++) { + Object pat = patterns.items[i]; + char_u *fname = opts->buffer.type == kObjectTypeNil ? (char_u *)pat.data.string.data : NULL; + did_aucmd |= + apply_autocmds_group(event_nr, fname, NULL, true, au_group, buf, NULL); + } }) if (did_aucmd && modeline) { @@ -824,7 +818,7 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) cleanup: api_free_array(event_array); - XFREE_CLEAR(pattern); + api_free_array(patterns); } static bool check_autocmd_string_array(Array arr, char *k, Error *err) @@ -945,11 +939,11 @@ static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Ob } else { api_set_error(err, kErrorTypeValidation, - "'pattern' must be a string"); + "'pattern' must be a string or table"); return false; } } else if (buffer.type != kObjectTypeNil) { - if (buffer.type != kObjectTypeInteger) { + if (buffer.type != kObjectTypeInteger && buffer.type != kObjectTypeBuffer) { api_set_error(err, kErrorTypeValidation, "'buffer' must be an integer"); diff --git a/test/functional/api/autocmd_spec.lua b/test/functional/api/autocmd_spec.lua index 50990c41f5..259ed3f961 100644 --- a/test/functional/api/autocmd_spec.lua +++ b/test/functional/api/autocmd_spec.lua @@ -610,6 +610,26 @@ describe('autocmd api', function() eq(true, meths.get_var("autocmd_executed")) end) + it("can trigger multiple patterns", function() + meths.set_var("autocmd_executed", 0) + + meths.create_autocmd("BufReadPost", { + pattern = "*", + command = "let g:autocmd_executed += 1", + }) + + meths.exec_autocmds("BufReadPost", { pattern = { "*.lua", "*.vim" } }) + eq(2, meths.get_var("autocmd_executed")) + + meths.create_autocmd("BufReadPre", { + pattern = { "bar", "foo" }, + command = "let g:autocmd_executed += 10", + }) + + meths.exec_autocmds("BufReadPre", { pattern = { "foo", "bar", "baz", "frederick" }}) + eq(22, meths.get_var("autocmd_executed")) + end) + it("can pass the buffer", function() meths.set_var("buffer_executed", -1) eq(-1, meths.get_var("buffer_executed")) @@ -666,7 +686,7 @@ describe('autocmd api', function() meths.exec_autocmds("CursorHoldI", { buffer = 1 }) eq('none', meths.get_var("filename_executed")) - meths.exec_autocmds("CursorHoldI", { buffer = tonumber(meths.get_current_buf()) }) + meths.exec_autocmds("CursorHoldI", { buffer = meths.get_current_buf() }) eq('__init__.py', meths.get_var("filename_executed")) -- Reset filename From dbe4270db7e8686b88976ad487f28f2ca5c5219e Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sun, 26 Jun 2022 17:57:45 +0200 Subject: [PATCH 2/3] refactor(api): use FOREACH_ITEM macro in autocmd --- src/nvim/api/autocmd.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 5967c71509..9575dc9900 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -510,9 +510,7 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc int retval; - for (size_t i = 0; i < patterns.size; i++) { - Object pat = patterns.items[i]; - + FOREACH_ITEM(patterns, pat, { // See: TODO(sctx) WITH_SCRIPT_CONTEXT(channel_id, { retval = autocmd_register(autocmd_id, @@ -530,7 +528,7 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc api_set_error(err, kErrorTypeException, "Failed to set autocmd"); goto cleanup; } - } + }) }); @@ -804,13 +802,12 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) FOREACH_ITEM(event_array, event_str, { GET_ONE_EVENT(event_nr, event_str, cleanup) - for (size_t i = 0; i < patterns.size; i++) { - Object pat = patterns.items[i]; - char_u *fname = opts->buffer.type == kObjectTypeNil ? (char_u *)pat.data.string.data : NULL; - did_aucmd |= + FOREACH_ITEM(patterns, pat, { + char_u *fname = opts->buffer.type == kObjectTypeNil ? (char_u *)pat.data.string.data : NULL; + did_aucmd |= apply_autocmds_group(event_nr, fname, NULL, true, au_group, buf, NULL); - } - }) + }) + }) if (did_aucmd && modeline) { do_modelines(0); @@ -823,8 +820,8 @@ cleanup: static bool check_autocmd_string_array(Array arr, char *k, Error *err) { - for (size_t i = 0; i < arr.size; i++) { - if (arr.items[i].type != kObjectTypeString) { + FOREACH_ITEM(arr, entry, { + if (entry.type != kObjectTypeString) { api_set_error(err, kErrorTypeValidation, "All entries in '%s' must be strings", @@ -833,13 +830,13 @@ static bool check_autocmd_string_array(Array arr, char *k, Error *err) } // Disallow newlines in the middle of the line. - const String l = arr.items[i].data.string; + const String l = entry.data.string; if (memchr(l.data, NL, l.size)) { api_set_error(err, kErrorTypeValidation, "String cannot contain newlines"); return false; } - } + }) return true; } @@ -926,8 +923,8 @@ static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Ob } Array array = v->data.array; - for (size_t i = 0; i < array.size; i++) { - char_u *pat = (char_u *)array.items[i].data.string.data; + FOREACH_ITEM(array, entry, { + char_u *pat = (char_u *)entry.data.string.data; size_t patlen = aucmd_pattern_length(pat); while (patlen) { ADD(*patterns, STRING_OBJ(cbuf_to_string((char *)pat, patlen))); @@ -935,7 +932,7 @@ static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Ob pat = aucmd_next_pattern(pat, patlen); patlen = aucmd_pattern_length(pat); } - } + }) } else { api_set_error(err, kErrorTypeValidation, From b7c0c8cde87a7f94ca2e320589e747e4c98c2389 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Mon, 27 Jun 2022 01:51:33 -0600 Subject: [PATCH 3/3] fix(api): change default value of 'pattern' in nvim_exec_autocmds (#19115) Omitting 'pattern' in nvim_exec_autocmds should be equivalent to omitting the 'fname' argument in :doautoall, which is equivalent to using an empty string as the pattern. Fixes regression introduced in #19091. --- src/nvim/api/autocmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 9575dc9900..99886020e9 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -793,7 +793,7 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) } if (patterns.size == 0) { - ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING("*"))); + ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING(""))); } modeline = api_object_to_bool(opts->modeline, "modeline", true, err);