diff options
author | TJ DeVries <devries.timothyj@gmail.com> | 2021-05-28 15:45:34 -0400 |
---|---|---|
committer | bfredl <bjorn.linse@gmail.com> | 2022-02-28 19:53:50 +0100 |
commit | 0f613482b389ad259dd53d893907b024a115352e (patch) | |
tree | 0f0e998db32c33139809bce38b18e6ae54639165 /src | |
parent | 6732cd9e57bf39d1a0f5c4fa08fa54c1561f2d2f (diff) | |
download | rneovim-0f613482b389ad259dd53d893907b024a115352e.tar.gz rneovim-0f613482b389ad259dd53d893907b024a115352e.tar.bz2 rneovim-0f613482b389ad259dd53d893907b024a115352e.zip |
feat(lua): add missing changes to autocmds lost in the rebase
Note: some of these changes are breaking, like change of API signatures
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/autocmd.c | 221 | ||||
-rw-r--r-- | src/nvim/api/keysets.lua | 4 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 5 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.h | 16 | ||||
-rw-r--r-- | src/nvim/autocmd.c | 54 | ||||
-rw-r--r-- | src/nvim/autocmd.h | 2 | ||||
-rw-r--r-- | src/nvim/eval/typval.c | 24 | ||||
-rw-r--r-- | src/nvim/lua/executor.c | 3 |
8 files changed, 197 insertions, 132 deletions
diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index deb8ec8cf3..6b89eb1770 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -17,34 +17,7 @@ #define AUCMD_MAX_PATTERNS 256 -// Check whether every item in the array is a kObjectTypeString -#define CHECK_STRING_ARRAY(__array, k, v, goto_name) \ - for (size_t j = 0; j < __array.size; j++) { \ - Object item = __array.items[j]; \ - if (item.type != kObjectTypeString) { \ - api_set_error(err, \ - kErrorTypeValidation, \ - "All entries in '%s' must be strings", \ - k); \ - goto goto_name; \ - } \ - } - // Copy string or array of strings into an empty array. -#define UNPACK_STRING_OR_ARRAY(__array, k, v, goto_name) \ - if (v->type == kObjectTypeString) { \ - ADD(__array, copy_object(*v)); \ - } else if (v->type == kObjectTypeArray) { \ - CHECK_STRING_ARRAY(__array, k, v, goto_name); \ - __array = copy_array(v->data.array); \ - } else { \ - api_set_error(err, \ - kErrorTypeValidation, \ - "'%s' must be an array or a string.", \ - k); \ - goto goto_name; \ - } - // Get the event number, unless it is an error. Then goto `goto_name`. #define GET_ONE_EVENT(event_nr, event_str, goto_name) \ char_u *__next_ev; \ @@ -61,16 +34,18 @@ static int64_t next_autocmd_id = 1; /// Get autocmds that match the requirements passed to {opts}. -/// group -/// event -/// pattern /// -/// -- @param {string} event - event or events to match against -/// vim.api.nvim_get_autocmds({ event = "FileType" }) +/// @param opts Optional Parameters: +/// - event : Name or list of name of events to match against +/// - group (string): Name of group to match against +/// - pattern: Pattern or list of patterns to match against /// +/// @return A list of autocmds that match Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) FUNC_API_SINCE(9) { + // TODO(tjdevries): Would be cool to add nvim_get_autocmds({ id = ... }) + Array autocmd_list = ARRAY_DICT_INIT; char_u *pattern_filters[AUCMD_MAX_PATTERNS]; char_u pattern_buflocal[BUFLOCAL_PAT_LEN]; @@ -199,6 +174,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) if (aucmd_exec_is_deleted(ac->exec)) { continue; } + Dictionary autocmd_info = ARRAY_DICT_INIT; if (ap->group != AUGROUP_DEFAULT) { @@ -256,40 +232,42 @@ cleanup: return autocmd_list; } -/// Define an autocmd. -/// @param opts Dictionary +/// Create an autocmd. +/// +/// @param event The event or events to register this autocmd /// Required keys: /// event: string | ArrayOf(string) -/// event = "pat1,pat2,pat3", -/// event = "pat1" -/// event = {"pat1"} -/// event = {"pat1", "pat2", "pat3"} -/// -/// -/// -- @param {string} name - augroup name -/// -- @param {string | table} event - event or events to match against -/// -- @param {string | table} pattern - pattern or patterns to match against -/// -- @param {string | function} callback - function or string to execute on autocmd -/// -- @param {string} command - optional, vimscript command -/// Eg. command = "let g:value_set = v:true" -/// -- @param {boolean} once - optional, defaults to false /// -/// -- pattern = comma delimited list of patterns | pattern | { pattern, ... } +/// Examples: +/// - event: "pat1,pat2,pat3", +/// - event: "pat1" +/// - event: { "pat1" } +/// - event: { "pat1", "pat2", "pat3" } /// -/// pattern = "*.py,*.pyi" -/// pattern = "*.py" -/// pattern = {"*.py"} -/// pattern = { "*.py", "*.pyi" } +/// @param opts Optional Parameters: +/// - callback: (string|function) +/// - (string): The name of the viml function to execute when triggering this autocmd +/// - (function): The lua function to execute when triggering this autocmd +/// - NOTE: Cannot be used with {command} +/// - command: (string) command +/// - vimscript command +/// - NOTE: Cannot be used with {callback} +/// Eg. command = "let g:value_set = v:true" +/// - pattern: (string|table) +/// - pattern or patterns to match against +/// - defaults to "*". +/// - NOTE: Cannot be used with {buffer} +/// - buffer: (bufnr) +/// - create a |autocmd-buflocal| autocmd. +/// - NOTE: Cannot be used with {pattern} +/// - group: (string) The augroup name +/// - once: (boolean) - See |autocmd-once| +/// - nested: (boolean) - See |autocmd-nested| +/// - desc: (string) - Description of the autocmd /// -/// -- not supported -/// pattern = {"*.py,*.pyi"} -/// -/// -- event = string | string[] -/// event = "FileType,CursorHold" -/// event = "BufPreWrite" -/// event = {"BufPostWrite"} -/// event = {"CursorHold", "BufPreWrite", "BufPostWrite"} -Integer nvim_create_autocmd(uint64_t channel_id, Dict(create_autocmd) *opts, Error *err) +/// @returns opaque value to use with nvim_del_autocmd +Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autocmd) *opts, + Error *err) FUNC_API_SINCE(9) { int64_t autocmd_id = -1; @@ -304,6 +282,11 @@ Integer nvim_create_autocmd(uint64_t channel_id, Dict(create_autocmd) *opts, Err AucmdExecutable aucmd = AUCMD_EXECUTABLE_INIT; Callback cb = CALLBACK_NONE; + + if (!unpack_string_or_array(&event_array, &event, "event", err)) { + goto cleanup; + } + if (opts->callback.type != kObjectTypeNil && opts->command.type != kObjectTypeNil) { api_set_error(err, kErrorTypeValidation, "cannot pass both: 'callback' and 'command' for the same autocmd"); @@ -359,14 +342,10 @@ Integer nvim_create_autocmd(uint64_t channel_id, Dict(create_autocmd) *opts, Err goto cleanup; } - if (opts->event.type != kObjectTypeNil) { - UNPACK_STRING_OR_ARRAY(event_array, "event", (&opts->event), cleanup) - } - bool is_once = api_object_to_bool(opts->once, "once", false, err); bool is_nested = api_object_to_bool(opts->nested, "nested", false, err); - // TOOD: accept number for namespace instead + // TODO(tjdevries): accept number for namespace instead if (opts->group.type != kObjectTypeNil) { Object *v = &opts->group; if (v->type != kObjectTypeString) { @@ -402,7 +381,9 @@ Integer nvim_create_autocmd(uint64_t channel_id, Dict(create_autocmd) *opts, Err patlen = aucmd_pattern_length(pat); } } else if (v->type == kObjectTypeArray) { - CHECK_STRING_ARRAY(patterns, "pattern", v, cleanup); + if (!check_autocmd_string_array(patterns, "pattern", err)) { + goto cleanup; + } Array array = v->data.array; for (size_t i = 0; i < array.size; i++) { @@ -503,8 +484,9 @@ cleanup: return autocmd_id; } -/// Delete an autocmd by ID. Autocmds only return IDs when created -/// via the API. +/// Delete an autocmd by {id}. Autocmds only return IDs when created +/// via the API. Will not error if called and no autocmds match +/// the {id}. /// /// @param id Integer The ID returned by nvim_create_autocmd void nvim_del_autocmd(Integer id) @@ -517,28 +499,28 @@ void nvim_del_autocmd(Integer id) /// /// To get an existing augroup ID, do: /// <pre> -/// local id = vim.api.nvim_create_augroup({ name = name, clear = false }); +/// local id = vim.api.nvim_create_augroup(name, { +/// clear = false +/// }) /// </pre> /// +/// @param name String: The name of the augroup to create /// @param opts Parameters -/// - name (string): The name of the augroup /// - clear (bool): Whether to clear existing commands or not. -// Defaults to true. +/// Defaults to true. /// See |autocmd-groups| -Integer nvim_create_augroup(uint64_t channel_id, Dict(create_augroup) *opts, Error *err) +/// +/// @returns opaque value to use with nvim_del_augroup_by_id +Integer nvim_create_augroup(uint64_t channel_id, String name, Dict(create_augroup) *opts, + Error *err) FUNC_API_SINCE(9) { + char *augroup_name = name.data; bool clear_autocmds = api_object_to_bool(opts->clear, "clear", true, err); - if (opts->name.type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, "'name' is required and must be a string"); - return -1; - } - char *name = opts->name.data.string.data; - int augroup = -1; WITH_SCRIPT_CONTEXT(channel_id, { - augroup = augroup_add(name); + augroup = augroup_add(augroup_name); if (augroup == AUGROUP_ERROR) { api_set_error(err, kErrorTypeException, "Failed to set augroup"); return -1; @@ -554,7 +536,11 @@ Integer nvim_create_augroup(uint64_t channel_id, Dict(create_augroup) *opts, Err return augroup; } +/// Delete an augroup by {id}. {id} can only be returned when augroup was +/// created with |nvim_create_augroup|. +/// /// NOTE: behavior differs from augroup-delete. +/// /// When deleting an augroup, autocmds contained by this augroup will also be deleted and cleared. /// This augroup will no longer exist void nvim_del_augroup_by_id(Integer id) @@ -564,7 +550,10 @@ void nvim_del_augroup_by_id(Integer id) augroup_del(name, false); } +/// Delete an augroup by {name}. +/// /// NOTE: behavior differs from augroup-delete. +/// /// When deleting an augroup, autocmds contained by this augroup will also be deleted and cleared. /// This augroup will no longer exist void nvim_del_augroup_by_name(String name) @@ -573,12 +562,17 @@ void nvim_del_augroup_by_name(String name) augroup_del(name.data, false); } -/// -- @param {string} group - autocmd group name -/// -- @param {number} buffer - buffer number -/// -- @param {string | table} event - event or events to match against -/// -- @param {string | table} pattern - optional, defaults to "*". -/// vim.api.nvim_do_autcmd({ group, buffer, pattern, event, modeline }) -void nvim_do_autocmd(Dict(do_autocmd) *opts, Error *err) +/// Do one autocmd. +/// +/// @param event The event or events to execute +/// @param opts Optional Parameters: +/// - buffer (number) - buffer number +/// - NOTE: Cannot be used with {pattern} +/// - pattern (string|table) - optional, defaults to "*". +/// - NOTE: Cannot be used with {buffer} +/// - group (string) - autocmd group name +/// - modeline (boolean) - Default true, see |<nomodeline>| +void nvim_do_autocmd(Object event, Dict(do_autocmd) *opts, Error *err) FUNC_API_SINCE(9) { int au_group = AUGROUP_ALL; @@ -592,6 +586,10 @@ void nvim_do_autocmd(Dict(do_autocmd) *opts, Error *err) Array event_array = ARRAY_DICT_INIT; + if (!unpack_string_or_array(&event_array, &event, "event", err)) { + goto cleanup; + } + if (opts->group.type != kObjectTypeNil) { if (opts->group.type != kObjectTypeString) { api_set_error(err, kErrorTypeValidation, "'group' must be a string"); @@ -634,13 +632,7 @@ void nvim_do_autocmd(Dict(do_autocmd) *opts, Error *err) set_pattern = true; } - if (opts->event.type != kObjectTypeNil) { - UNPACK_STRING_OR_ARRAY(event_array, "event", (&opts->event), cleanup) - } - - if (opts->modeline.type != kObjectTypeNil) { - modeline = api_object_to_bool(opts->modeline, "modeline", true, err); - } + 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'"); @@ -663,7 +655,46 @@ cleanup: XFREE_CLEAR(pattern); } +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) { + api_set_error(err, + kErrorTypeValidation, + "All entries in '%s' must be strings", + k); + return false; + } + + // Disallow newlines in the middle of the line. + const String l = arr.items[i].data.string; + if (memchr(l.data, NL, l.size)) { + api_set_error(err, kErrorTypeValidation, + "String cannot contain newlines"); + return false; + } + } + return true; +} + +static bool unpack_string_or_array(Array *array, Object *v, char *k, Error *err) +{ + if (v->type == kObjectTypeString) { + ADD(*array, copy_object(*v)); + } else if (v->type == kObjectTypeArray) { + if (!check_autocmd_string_array(v->data.array, k, err)) { + return false; + } + *array = copy_array(v->data.array); + } else { + api_set_error(err, + kErrorTypeValidation, + "'%s' must be an array or a string.", + k); + return false; + } + + return true; +} -#undef UNPACK_STRING_OR_ARRAY -#undef CHECK_STRING_ARRAY #undef GET_ONE_EVENT diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index be71c446b1..b05816f8ac 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -116,7 +116,6 @@ return { "callback"; "command"; "desc"; - "event"; "group"; "once"; "nested"; @@ -124,7 +123,6 @@ return { }; do_autocmd = { "buffer"; - "event"; "group"; "modeline"; "pattern"; @@ -132,12 +130,10 @@ return { get_autocmds = { "event"; "group"; - "id"; "pattern"; }; create_augroup = { "clear"; - "name"; }; } diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 3d4a04f096..c2106855d3 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -406,6 +406,7 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object }); } + buf_T *find_buffer_by_handle(Buffer buffer, Error *err) { if (buffer == 0) { @@ -1614,8 +1615,8 @@ int find_sid(uint64_t channel_id) { switch (channel_id) { case VIML_INTERNAL_CALL: - // TODO(autocmd): Figure out what this should be - // return SID_API_CLIENT; + // TODO(autocmd): Figure out what this should be + // return SID_API_CLIENT; case LUA_INTERNAL_CALL: return SID_LUA; default: diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 6969994c3b..bc7c2e6a60 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -152,13 +152,15 @@ typedef struct { #endif #define WITH_SCRIPT_CONTEXT(channel_id, code) \ - const sctx_T save_current_sctx = current_sctx; \ - current_sctx.sc_sid = \ - (channel_id) == LUA_INTERNAL_CALL ? SID_LUA : SID_API_CLIENT; \ - current_sctx.sc_lnum = 0; \ - current_channel_id = channel_id; \ - code; \ - current_sctx = save_current_sctx; + do { \ + const sctx_T save_current_sctx = current_sctx; \ + current_sctx.sc_sid = \ + (channel_id) == LUA_INTERNAL_CALL ? SID_LUA : SID_API_CLIENT; \ + current_sctx.sc_lnum = 0; \ + current_channel_id = channel_id; \ + code; \ + current_sctx = save_current_sctx; \ + } while (0); #endif // NVIM_API_PRIVATE_HELPERS_H diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index a9e1978ac0..5d53ac6b17 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -4,13 +4,10 @@ // autocmd.c: Autocommand related functions #include <signal.h> -#include "nvim/autocmd.h" - -// #include "nvim/api/private/handle.h" - #include "lauxlib.h" #include "nvim/api/private/helpers.h" #include "nvim/ascii.h" +#include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cursor.h" @@ -108,17 +105,6 @@ static char_u *old_termresponse = NULL; #define FOR_ALL_AUPATS_IN_EVENT(event, ap) \ for (AutoPat *ap = first_autopat[event]; ap != NULL; ap = ap->next) // NOLINT -/// Handles grabbing arguments from `:autocmd` such as ++once and ++nested -#define ARG_GET_FLAG(errored, cmd, flag, pattern, len) \ - if (STRNCMP(cmd, pattern, len) == 0 && ascii_iswhite((cmd)[len])) { \ - if (flag) { \ - semsg(_(e_duparg2), pattern); \ - (errored) = true; \ - } \ - (flag) = true; \ - (cmd) = skipwhite((cmd) + (len)); \ - } - // Map of autocmd group names. // name -> ID static Map(String, int) augroup_map = MAP_INIT; @@ -408,6 +394,16 @@ int augroup_add(char *name) } /// Delete the augroup that matches name. +/// @param stupid_legacy_mode bool: This paremeter determines whether to run the augroup +/// deletion in the same fashion as `:augroup! {name}` where if there are any remaining +/// autocmds left in the augroup, it will change the name of the augroup to `--- DELETED ---` +/// but leave the autocmds existing. These are _separate_ augroups, so if you do this for +/// multiple augroups, you will have a bunch of `--- DELETED ---` augroups at the same time. +/// There is no way, as far as I could tell, how to actually delete them at this point as a user +/// +/// I did not consider this good behavior, so now when NOT in stupid_legacy_mode, we actually +/// delete these groups and their commands, like you would expect (and don't leave hanging +/// `--- DELETED ---` groups around) void augroup_del(char *name, bool stupid_legacy_mode) { int i = augroup_find(name); @@ -723,7 +719,7 @@ void do_autocmd(char_u *arg_in, int forceit) char_u *envpat = NULL; char_u *cmd; int need_free = false; - int nested = false; + bool nested = false; bool once = false; int group; @@ -778,11 +774,11 @@ void do_autocmd(char_u *arg_in, int forceit) bool invalid_flags = false; for (size_t i = 0; i < 2; i++) { if (*cmd != NUL) { - ARG_GET_FLAG(invalid_flags, cmd, once, "++once", 6); - ARG_GET_FLAG(invalid_flags, cmd, nested, "++nested", 8); + invalid_flags |= arg_autocmd_flag_get(&once, &cmd, "++once", 6); + invalid_flags |= arg_autocmd_flag_get(&nested, &cmd, "++nested", 8); // Check the deprecated "nested" flag. - ARG_GET_FLAG(invalid_flags, cmd, nested, "nested", 6); + invalid_flags |= arg_autocmd_flag_get(&nested, &cmd, "nested", 6); } } @@ -2050,6 +2046,8 @@ char_u *getnextac(int c, void *cookie, int indent, bool do_concat) typval_T rettv = TV_INITIAL_VALUE; callback_call(&ac->exec.callable.cb, 0, &argsin, &rettv); + // TODO(tjdevries): + // // Major Hack Alert: // We just return "not-null" and continue going. // This would be a good candidate for a refactor. You would need to refactor: @@ -2057,7 +2055,7 @@ char_u *getnextac(int c, void *cookie, int indent, bool do_concat) // OR // 2. make where we call do_cmdline for autocmds not have to return anything, // and instead we loop over all the matches and just execute one-by-one. - // However, my expectation woudl be that could be expensive. + // However, my expectation would be that could be expensive. retval = vim_strsave((char_u *)""); } else { retval = vim_strsave(ac->exec.callable.cmd); @@ -2544,6 +2542,22 @@ static int arg_augroup_get(char_u **argp) return group; } +/// Handles grabbing arguments from `:autocmd` such as ++once and ++nested +static bool arg_autocmd_flag_get(bool *flag, char_u **cmd_ptr, char *pattern, int len) +{ + if (STRNCMP(*cmd_ptr, pattern, len) == 0 && ascii_iswhite((*cmd_ptr)[len])) { + if (*flag) { + semsg(_(e_duparg2), pattern); + return true; + } + + *flag = true; + *cmd_ptr = skipwhite((*cmd_ptr) + len); + } + + return false; +} + // UI Enter void do_autocmd_uienter(uint64_t chanid, bool attached) diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h index 0a1f036183..53ec7f2bb7 100644 --- a/src/nvim/autocmd.h +++ b/src/nvim/autocmd.h @@ -27,7 +27,7 @@ typedef struct AutoCmd { bool once; // "One shot": removed after execution bool nested; // If autocommands nest here bool last; // last command in list - int64_t id; // TODO(tjdevries): Explain + int64_t id; // ID used for uniquely tracking an autocmd. sctx_T script_ctx; // script context where defined char *desc; // Description for the autocmd. struct AutoCmd *next; // Next AutoCmd in list diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index fbda7fbc3c..44b003d106 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -8,6 +8,7 @@ #include <stdlib.h> #include <string.h> +#include "lauxlib.h" #include "nvim/ascii.h" #include "nvim/assert.h" #include "nvim/charset.h" @@ -1157,7 +1158,22 @@ void callback_free(Callback *callback) /// Check if callback is freed bool callback_is_freed(Callback callback) { - return false; + switch (callback.type) { + case kCallbackFuncref: + return false; + break; + case kCallbackPartial: + return false; + break; + case kCallbackLua: + return callback.data.luaref == LUA_NOREF; + break; + case kCallbackNone: + return true; + break; + } + + return true; } /// Copy a callback into a typval_T. @@ -1176,8 +1192,10 @@ void callback_put(Callback *cb, typval_T *tv) func_ref(cb->data.funcref); break; case kCallbackLua: - // TODO(tjdevries): I'm not even sure if this is strictly necessary? - abort(); + // TODO(tjdevries): Unified Callback. + // At this point this isn't possible, but it'd be nice to put + // these handled more neatly in one place. + // So instead, we just do the default and put nil default: tv->v_type = VAR_SPECIAL; tv->vval.v_special = kSpecialVarNull; diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 18abf04ff6..07071d4e1e 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1354,6 +1354,9 @@ bool nlua_ref_is_function(LuaRef ref) { lua_State *const lstate = global_lstate; nlua_pushref(lstate, ref); + + // TODO(tjdevries): This should probably check for callable tables as well. + // We should put some work maybe into simplifying how all of that works bool is_function = (lua_type(lstate, -1) == LUA_TFUNCTION); lua_pop(lstate, 1); |