From 543e0256c19f397921a332e06b423215fd9aecb5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 30 Nov 2023 15:51:05 +0800 Subject: build: don't define FUNC_ATTR_* as empty in headers (#26317) FUNC_ATTR_* should only be used in .c files with generated headers. Defining FUNC_ATTR_* as empty in headers causes misuses of them to be silently ignored. Instead don't define them by default, and only define them as empty after a .c file has included its generated header. --- src/nvim/api/autocmd.c | 1 - src/nvim/api/buffer.c | 1 - src/nvim/api/command.c | 1 - src/nvim/api/deprecated.c | 1 - src/nvim/api/extmark.c | 1 - src/nvim/api/options.c | 1 - src/nvim/api/private/converter.c | 1 - src/nvim/api/private/helpers.c | 1 - src/nvim/api/tabpage.c | 5 ++++- src/nvim/api/ui.c | 1 - src/nvim/api/vim.c | 1 - src/nvim/api/vimscript.c | 1 - src/nvim/api/win_config.c | 1 - src/nvim/api/window.c | 5 ++++- 14 files changed, 8 insertions(+), 14 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 08d9d8e117..2ce08bdf40 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -17,7 +17,6 @@ #include "nvim/buffer.h" #include "nvim/eval/typval.h" #include "nvim/ex_cmds_defs.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/lua/executor.h" #include "nvim/memory.h" diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 0df231868d..9d0ac5d6ef 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -24,7 +24,6 @@ #include "nvim/drawscreen.h" #include "nvim/ex_cmds.h" #include "nvim/extmark.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/lua/executor.h" #include "nvim/mapping.h" diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index 2a57ce9a19..7116f4bce0 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -18,7 +18,6 @@ #include "nvim/ex_cmds.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/globals.h" #include "nvim/lua/executor.h" diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 2ec11236d7..11795033cc 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -14,7 +14,6 @@ #include "nvim/buffer_defs.h" #include "nvim/decoration.h" #include "nvim/extmark.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/highlight.h" #include "nvim/highlight_group.h" diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index d71498d6ed..80b1546329 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -17,7 +17,6 @@ #include "nvim/decoration_provider.h" #include "nvim/drawscreen.h" #include "nvim/extmark.h" -#include "nvim/func_attr.h" #include "nvim/grid.h" #include "nvim/highlight_group.h" #include "nvim/marktree.h" diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index c012a69c7b..9cf91bad42 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -11,7 +11,6 @@ #include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/eval/window.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/macros_defs.h" #include "nvim/memory.h" diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index 90023171e5..ef57bde32d 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -11,7 +11,6 @@ #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" -#include "nvim/func_attr.h" #include "nvim/lua/executor.h" #include "nvim/memory.h" #include "nvim/types_defs.h" diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index be39836a5b..b23684aee9 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -20,7 +20,6 @@ #include "nvim/eval/typval_defs.h" #include "nvim/eval/vars.h" #include "nvim/ex_eval.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/globals.h" #include "nvim/highlight_group.h" diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c index c854a22477..303f2ca817 100644 --- a/src/nvim/api/tabpage.c +++ b/src/nvim/api/tabpage.c @@ -6,11 +6,14 @@ #include "nvim/api/tabpage.h" #include "nvim/api/vim.h" #include "nvim/buffer_defs.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/memory.h" #include "nvim/window.h" +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "api/tabpage.c.generated.h" +#endif + /// Gets the windows in a tabpage /// /// @param tabpage Tabpage handle, or 0 for current tabpage diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 836a68546c..7e64ce9cd1 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -17,7 +17,6 @@ #include "nvim/eval.h" #include "nvim/event/loop.h" #include "nvim/event/wstream.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/highlight.h" diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index d631b10af9..270f2e4432 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -31,7 +31,6 @@ #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/getchar.h" #include "nvim/globals.h" #include "nvim/grid.h" diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index c75bf21572..25a34f769c 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -15,7 +15,6 @@ #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" #include "nvim/ex_docmd.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/globals.h" #include "nvim/memory.h" diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 4e23717dc6..825a0583ef 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -13,7 +13,6 @@ #include "nvim/buffer_defs.h" #include "nvim/decoration.h" #include "nvim/drawscreen.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/highlight_group.h" diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index de5b40940f..d18971c756 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -15,7 +15,6 @@ #include "nvim/drawscreen.h" #include "nvim/eval/window.h" #include "nvim/ex_docmd.h" -#include "nvim/func_attr.h" #include "nvim/gettext.h" #include "nvim/globals.h" #include "nvim/lua/executor.h" @@ -27,6 +26,10 @@ #include "nvim/types_defs.h" #include "nvim/window.h" +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "api/window.c.generated.h" +#endif + /// Gets the current buffer in a window /// /// @param window Window handle, or 0 for current window -- cgit From ce56e0a845d68862118f44cb66c5f080e3c1bbed Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 30 Nov 2023 17:16:57 +0800 Subject: refactor(IWYU): move UI and LineFlags to ui_defs.h (#26318) --- src/nvim/api/private/dispatch.h | 12 ++++++------ src/nvim/api/ui.h | 3 +-- 2 files changed, 7 insertions(+), 8 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/private/dispatch.h b/src/nvim/api/private/dispatch.h index 6a2c9eaf54..88f846f813 100644 --- a/src/nvim/api/private/dispatch.h +++ b/src/nvim/api/private/dispatch.h @@ -14,12 +14,12 @@ typedef Object (*ApiDispatchWrapper)(uint64_t channel_id, Array args, Arena *are struct MsgpackRpcRequestHandler { const char *name; ApiDispatchWrapper fn; - bool fast; // Function is safe to be executed immediately while running the - // uv loop (the loop is run very frequently due to breakcheck). - // If "fast" is false, the function is deferred, i e the call will - // be put in the event queue, for safe handling later. - bool arena_return; // return value is allocated in the arena (or statically) - // and should not be freed as such. + bool fast; ///< Function is safe to be executed immediately while running the + ///< uv loop (the loop is run very frequently due to breakcheck). + ///< If "fast" is false, the function is deferred, i e the call will + ///< be put in the event queue, for safe handling later. + bool arena_return; ///< return value is allocated in the arena (or statically) + ///< and should not be freed as such. }; extern const MsgpackRpcRequestHandler method_handlers[]; diff --git a/src/nvim/api/ui.h b/src/nvim/api/ui.h index 26a91d0dbc..b1f4ff97d9 100644 --- a/src/nvim/api/ui.h +++ b/src/nvim/api/ui.h @@ -4,9 +4,8 @@ #include "nvim/api/private/defs.h" // IWYU pragma: keep #include "nvim/highlight_defs.h" // IWYU pragma: keep -#include "nvim/map_defs.h" #include "nvim/types_defs.h" // IWYU pragma: keep -#include "nvim/ui.h" +#include "nvim/ui_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/ui.h.generated.h" -- cgit From 76a30f2bd0f4e5abe906eabcdce092931d712be3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 1 Dec 2023 16:37:09 +0800 Subject: refactor: move float_relative_str[] to nvim_win_get_config() (#26344) It's only used in one place, as it's usually conveyed as non-string. --- src/nvim/api/win_config.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/nvim/api') diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 825a0583ef..856fac5585 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -311,6 +311,9 @@ static Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig, Dictionary nvim_win_get_config(Window window, Error *err) FUNC_API_SINCE(6) { + /// Keep in sync with FloatRelative in buffer_defs.h + static const char *const float_relative_str[] = { "editor", "win", "cursor", "mouse" }; + Dictionary rv = ARRAY_DICT_INIT; win_T *wp = find_window_by_handle(window, err); -- cgit From f6e5366d0077e9f171651f37282cb5c47629d1b6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 2 Dec 2023 07:55:44 +0800 Subject: refactor: free more reachable memory with EXITFREE (#26349) Discovered using __sanitizer_print_memory_profile(). --- src/nvim/api/ui.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 7e64ce9cd1..b73c026d57 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -106,21 +106,36 @@ static void mpack_str(char **buf, const char *str) *buf += len; } +static void remote_ui_destroy(UI *ui) + FUNC_ATTR_NONNULL_ALL +{ + UIData *data = ui->data; + kv_destroy(data->call_buf); + XFREE_CLEAR(ui->term_name); + xfree(ui); +} + void remote_ui_disconnect(uint64_t channel_id) { UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); if (!ui) { return; } - UIData *data = ui->data; - kv_destroy(data->call_buf); pmap_del(uint64_t)(&connected_uis, channel_id, NULL); ui_detach_impl(ui, channel_id); + remote_ui_destroy(ui); +} - // Destroy `ui`. - XFREE_CLEAR(ui->term_name); - xfree(ui); +#ifdef EXITFREE +void remote_ui_free_all_mem(void) +{ + UI *ui; + map_foreach_value(&connected_uis, ui, { + remote_ui_destroy(ui); + }); + map_destroy(uint64_t, &connected_uis); } +#endif /// Wait until ui has connected on stdio channel if only_stdio /// is true, otherwise any channel. -- cgit From 66f1563c7a48d76f99c89e32de030e57af2abfb4 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 4 Dec 2023 20:29:51 +0800 Subject: refactor(terminal): only remove const qualifier when necessary (#26386) --- src/nvim/api/vim.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 270f2e4432..db08cb8cf0 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1017,18 +1017,19 @@ Integer nvim_open_term(Buffer buffer, DictionaryOf(LuaRef) opts, Error *err) } } - TerminalOptions topts; Channel *chan = channel_alloc(kChannelStreamInternal); chan->stream.internal.cb = cb; chan->stream.internal.closed = false; - topts.data = chan; - // NB: overridden in terminal_check_size if a window is already - // displaying the buffer - topts.width = (uint16_t)MAX(curwin->w_width_inner - win_col_off(curwin), 0); - topts.height = (uint16_t)curwin->w_height_inner; - topts.write_cb = term_write; - topts.resize_cb = term_resize; - topts.close_cb = term_close; + TerminalOptions topts = { + .data = chan, + // NB: overridden in terminal_check_size if a window is already + // displaying the buffer + .width = (uint16_t)MAX(curwin->w_width_inner - win_col_off(curwin), 0), + .height = (uint16_t)curwin->w_height_inner, + .write_cb = term_write, + .resize_cb = term_resize, + .close_cb = term_close, + }; channel_incref(chan); terminal_open(&chan->term, buf, topts); if (chan->term != NULL) { @@ -1038,7 +1039,7 @@ Integer nvim_open_term(Buffer buffer, DictionaryOf(LuaRef) opts, Error *err) return (Integer)chan->id; } -static void term_write(char *buf, size_t size, void *data) // NOLINT(readability-non-const-parameter) +static void term_write(const char *buf, size_t size, void *data) { Channel *chan = data; LuaRef cb = chan->stream.internal.cb; @@ -1048,7 +1049,7 @@ static void term_write(char *buf, size_t size, void *data) // NOLINT(readabilit MAXSIZE_TEMP_ARRAY(args, 3); ADD_C(args, INTEGER_OBJ((Integer)chan->id)); ADD_C(args, BUFFER_OBJ(terminal_buf(chan->term))); - ADD_C(args, STRING_OBJ(((String){ .data = buf, .size = size }))); + ADD_C(args, STRING_OBJ(((String){ .data = (char *)buf, .size = size }))); textlock++; nlua_call_ref(cb, "input", args, false, NULL); textlock--; -- cgit From 45fe4d11add933df76a2ea4bf52ce8904f4a778b Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 4 Dec 2023 13:31:57 -0800 Subject: build: enable lintlua for src/ dir #26395 Problem: Not all Lua code is checked by stylua. Automating code-style is an important mechanism for reducing time spent on accidental (non-essential) complexity. Solution: - Enable lintlua for `src/` directory. followup to 517f0cc634b985057da5b95cf4ad659ee456a77e --- src/nvim/api/dispatch_deprecated.lua | 132 +++++++++++++++++------------------ 1 file changed, 66 insertions(+), 66 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/dispatch_deprecated.lua b/src/nvim/api/dispatch_deprecated.lua index 5650a77ac0..7a92789f79 100644 --- a/src/nvim/api/dispatch_deprecated.lua +++ b/src/nvim/api/dispatch_deprecated.lua @@ -1,69 +1,69 @@ local deprecated_aliases = { - nvim_buf_add_highlight="buffer_add_highlight", - nvim_buf_clear_highlight="buffer_clear_highlight", - nvim_buf_get_lines="buffer_get_lines", - nvim_buf_get_mark="buffer_get_mark", - nvim_buf_get_name="buffer_get_name", - nvim_buf_get_number="buffer_get_number", - nvim_buf_get_option="buffer_get_option", - nvim_buf_get_var="buffer_get_var", - nvim_buf_is_valid="buffer_is_valid", - nvim_buf_line_count="buffer_line_count", - nvim_buf_set_lines="buffer_set_lines", - nvim_buf_set_name="buffer_set_name", - nvim_buf_set_option="buffer_set_option", - nvim_call_function="vim_call_function", - nvim_command="vim_command", - nvim_command_output="vim_command_output", - nvim_del_current_line="vim_del_current_line", - nvim_err_write="vim_err_write", - nvim_err_writeln="vim_report_error", - nvim_eval="vim_eval", - nvim_feedkeys="vim_feedkeys", - nvim_get_api_info="vim_get_api_info", - nvim_get_color_by_name="vim_name_to_color", - nvim_get_color_map="vim_get_color_map", - nvim_get_current_buf="vim_get_current_buffer", - nvim_get_current_line="vim_get_current_line", - nvim_get_current_tabpage="vim_get_current_tabpage", - nvim_get_current_win="vim_get_current_window", - nvim_get_option="vim_get_option", - nvim_get_var="vim_get_var", - nvim_get_vvar="vim_get_vvar", - nvim_input="vim_input", - nvim_list_bufs="vim_get_buffers", - nvim_list_runtime_paths="vim_list_runtime_paths", - nvim_list_tabpages="vim_get_tabpages", - nvim_list_wins="vim_get_windows", - nvim_out_write="vim_out_write", - nvim_replace_termcodes="vim_replace_termcodes", - nvim_set_current_buf="vim_set_current_buffer", - nvim_set_current_dir="vim_change_directory", - nvim_set_current_line="vim_set_current_line", - nvim_set_current_tabpage="vim_set_current_tabpage", - nvim_set_current_win="vim_set_current_window", - nvim_set_option="vim_set_option", - nvim_strwidth="vim_strwidth", - nvim_subscribe="vim_subscribe", - nvim_tabpage_get_var="tabpage_get_var", - nvim_tabpage_get_win="tabpage_get_window", - nvim_tabpage_is_valid="tabpage_is_valid", - nvim_tabpage_list_wins="tabpage_get_windows", - nvim_ui_detach="ui_detach", - nvim_ui_try_resize="ui_try_resize", - nvim_unsubscribe="vim_unsubscribe", - nvim_win_get_buf="window_get_buffer", - nvim_win_get_cursor="window_get_cursor", - nvim_win_get_height="window_get_height", - nvim_win_get_option="window_get_option", - nvim_win_get_position="window_get_position", - nvim_win_get_tabpage="window_get_tabpage", - nvim_win_get_var="window_get_var", - nvim_win_get_width="window_get_width", - nvim_win_is_valid="window_is_valid", - nvim_win_set_cursor="window_set_cursor", - nvim_win_set_height="window_set_height", - nvim_win_set_option="window_set_option", - nvim_win_set_width="window_set_width", + nvim_buf_add_highlight = 'buffer_add_highlight', + nvim_buf_clear_highlight = 'buffer_clear_highlight', + nvim_buf_get_lines = 'buffer_get_lines', + nvim_buf_get_mark = 'buffer_get_mark', + nvim_buf_get_name = 'buffer_get_name', + nvim_buf_get_number = 'buffer_get_number', + nvim_buf_get_option = 'buffer_get_option', + nvim_buf_get_var = 'buffer_get_var', + nvim_buf_is_valid = 'buffer_is_valid', + nvim_buf_line_count = 'buffer_line_count', + nvim_buf_set_lines = 'buffer_set_lines', + nvim_buf_set_name = 'buffer_set_name', + nvim_buf_set_option = 'buffer_set_option', + nvim_call_function = 'vim_call_function', + nvim_command = 'vim_command', + nvim_command_output = 'vim_command_output', + nvim_del_current_line = 'vim_del_current_line', + nvim_err_write = 'vim_err_write', + nvim_err_writeln = 'vim_report_error', + nvim_eval = 'vim_eval', + nvim_feedkeys = 'vim_feedkeys', + nvim_get_api_info = 'vim_get_api_info', + nvim_get_color_by_name = 'vim_name_to_color', + nvim_get_color_map = 'vim_get_color_map', + nvim_get_current_buf = 'vim_get_current_buffer', + nvim_get_current_line = 'vim_get_current_line', + nvim_get_current_tabpage = 'vim_get_current_tabpage', + nvim_get_current_win = 'vim_get_current_window', + nvim_get_option = 'vim_get_option', + nvim_get_var = 'vim_get_var', + nvim_get_vvar = 'vim_get_vvar', + nvim_input = 'vim_input', + nvim_list_bufs = 'vim_get_buffers', + nvim_list_runtime_paths = 'vim_list_runtime_paths', + nvim_list_tabpages = 'vim_get_tabpages', + nvim_list_wins = 'vim_get_windows', + nvim_out_write = 'vim_out_write', + nvim_replace_termcodes = 'vim_replace_termcodes', + nvim_set_current_buf = 'vim_set_current_buffer', + nvim_set_current_dir = 'vim_change_directory', + nvim_set_current_line = 'vim_set_current_line', + nvim_set_current_tabpage = 'vim_set_current_tabpage', + nvim_set_current_win = 'vim_set_current_window', + nvim_set_option = 'vim_set_option', + nvim_strwidth = 'vim_strwidth', + nvim_subscribe = 'vim_subscribe', + nvim_tabpage_get_var = 'tabpage_get_var', + nvim_tabpage_get_win = 'tabpage_get_window', + nvim_tabpage_is_valid = 'tabpage_is_valid', + nvim_tabpage_list_wins = 'tabpage_get_windows', + nvim_ui_detach = 'ui_detach', + nvim_ui_try_resize = 'ui_try_resize', + nvim_unsubscribe = 'vim_unsubscribe', + nvim_win_get_buf = 'window_get_buffer', + nvim_win_get_cursor = 'window_get_cursor', + nvim_win_get_height = 'window_get_height', + nvim_win_get_option = 'window_get_option', + nvim_win_get_position = 'window_get_position', + nvim_win_get_tabpage = 'window_get_tabpage', + nvim_win_get_var = 'window_get_var', + nvim_win_get_width = 'window_get_width', + nvim_win_is_valid = 'window_is_valid', + nvim_win_set_cursor = 'window_set_cursor', + nvim_win_set_height = 'window_set_height', + nvim_win_set_option = 'window_set_option', + nvim_win_set_width = 'window_set_width', } return deprecated_aliases -- cgit From 0b74ad0a641f28d9d3da5353e98372d87078bd9d Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Tue, 5 Dec 2023 12:33:57 +0100 Subject: refactor(api): complete conversion from `Dictionary` to `Dict(opts)` (#26365) --- src/nvim/api/buffer.c | 110 +++++++++++++------------------------------- src/nvim/api/command.c | 6 +-- src/nvim/api/deprecated.c | 7 +-- src/nvim/api/deprecated.h | 1 + src/nvim/api/extmark.c | 26 ++--------- src/nvim/api/keysets_defs.h | 32 +++++++++++++ src/nvim/api/vim.c | 27 +++-------- 7 files changed, 79 insertions(+), 130 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 9d0ac5d6ef..e0dd265167 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -152,7 +152,7 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err) /// @return False if attach failed (invalid parameter, or buffer isn't loaded); /// otherwise True. TODO: LUA_API_NO_EVAL Boolean nvim_buf_attach(uint64_t channel_id, Buffer buffer, Boolean send_buffer, - DictionaryOf(LuaRef) opts, Error *err) + Dict(buf_attach) *opts, Error *err) FUNC_API_SINCE(4) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -161,64 +161,40 @@ Boolean nvim_buf_attach(uint64_t channel_id, Buffer buffer, Boolean send_buffer, return false; } - bool is_lua = (channel_id == LUA_INTERNAL_CALL); BufUpdateCallbacks cb = BUF_UPDATE_CALLBACKS_INIT; - struct { - const char *name; - LuaRef *dest; - } cbs[] = { - { "on_lines", &cb.on_lines }, - { "on_bytes", &cb.on_bytes }, - { "on_changedtick", &cb.on_changedtick }, - { "on_detach", &cb.on_detach }, - { "on_reload", &cb.on_reload }, - { NULL, NULL }, - }; - - for (size_t i = 0; i < opts.size; i++) { - String k = opts.items[i].key; - Object *v = &opts.items[i].value; - bool key_used = false; - if (is_lua) { - for (size_t j = 0; cbs[j].name; j++) { - if (strequal(cbs[j].name, k.data)) { - VALIDATE_T(cbs[j].name, kObjectTypeLuaRef, v->type, { - goto error; - }); - *(cbs[j].dest) = v->data.luaref; - v->data.luaref = LUA_NOREF; - key_used = true; - break; - } - } - if (key_used) { - continue; - } else if (strequal("utf_sizes", k.data)) { - VALIDATE_T("utf_sizes", kObjectTypeBoolean, v->type, { - goto error; - }); - cb.utf_sizes = v->data.boolean; - key_used = true; - } else if (strequal("preview", k.data)) { - VALIDATE_T("preview", kObjectTypeBoolean, v->type, { - goto error; - }); - cb.preview = v->data.boolean; - key_used = true; - } + if (channel_id == LUA_INTERNAL_CALL) { + if (HAS_KEY(opts, buf_attach, on_lines)) { + cb.on_lines = opts->on_lines; + opts->on_lines = LUA_NOREF; } - VALIDATE_S(key_used, "'opts' key", k.data, { - goto error; - }); + if (HAS_KEY(opts, buf_attach, on_bytes)) { + cb.on_bytes = opts->on_bytes; + opts->on_bytes = LUA_NOREF; + } + + if (HAS_KEY(opts, buf_attach, on_changedtick)) { + cb.on_changedtick = opts->on_changedtick; + opts->on_changedtick = LUA_NOREF; + } + + if (HAS_KEY(opts, buf_attach, on_detach)) { + cb.on_detach = opts->on_detach; + opts->on_detach = LUA_NOREF; + } + + if (HAS_KEY(opts, buf_attach, on_reload)) { + cb.on_reload = opts->on_reload; + opts->on_reload = LUA_NOREF; + } + + cb.utf_sizes = opts->utf_sizes; + + cb.preview = opts->preview; } return buf_updates_register(buf, channel_id, cb, send_buffer); - -error: - buffer_update_callbacks_free(cb); - return false; } /// Deactivates buffer-update events on the channel. @@ -781,16 +757,12 @@ early_end: ArrayOf(String) nvim_buf_get_text(uint64_t channel_id, Buffer buffer, Integer start_row, Integer start_col, Integer end_row, Integer end_col, - Dictionary opts, lua_State *lstate, + Dict(empty) *opts, lua_State *lstate, Error *err) FUNC_API_SINCE(9) { Array rv = ARRAY_DICT_INIT; - VALIDATE((opts.size == 0), "%s", "opts dict isn't empty", { - return rv; - }); - buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { @@ -1081,7 +1053,7 @@ Boolean nvim_buf_is_loaded(Buffer buffer) /// @param opts Optional parameters. Keys: /// - force: Force deletion and ignore unsaved changes. /// - unload: Unloaded only, do not delete. See |:bunload| -void nvim_buf_delete(Buffer buffer, Dictionary opts, Error *err) +void nvim_buf_delete(Buffer buffer, Dict(buf_delete) *opts, Error *err) FUNC_API_SINCE(7) FUNC_API_TEXTLOCK { @@ -1091,25 +1063,9 @@ void nvim_buf_delete(Buffer buffer, Dictionary opts, Error *err) return; } - bool force = false; - bool unload = false; - for (size_t i = 0; i < opts.size; i++) { - String k = opts.items[i].key; - Object v = opts.items[i].value; - if (strequal("force", k.data)) { - force = api_object_to_bool(v, "force", false, err); - } else if (strequal("unload", k.data)) { - unload = api_object_to_bool(v, "unload", false, err); - } else { - VALIDATE_S(false, "'opts' key", k.data, { - return; - }); - } - } + bool force = opts->force; - if (ERROR_SET(err)) { - return; - } + bool unload = opts->unload; int result = do_buffer(unload ? DOBUF_UNLOAD : DOBUF_WIPE, DOBUF_FIRST, @@ -1193,7 +1149,7 @@ Boolean nvim_buf_del_mark(Buffer buffer, String name, Error *err) /// @return true if the mark was set, else false. /// @see |nvim_buf_del_mark()| /// @see |nvim_buf_get_mark()| -Boolean nvim_buf_set_mark(Buffer buffer, String name, Integer line, Integer col, Dictionary opts, +Boolean nvim_buf_set_mark(Buffer buffer, String name, Integer line, Integer col, Dict(empty) *opts, Error *err) FUNC_API_SINCE(8) { diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index 7116f4bce0..abb589ecdf 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -94,15 +94,11 @@ /// - "belowright": |:belowright|. /// - "topleft": |:topleft|. /// - "botright": |:botright|. -Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err) +Dictionary nvim_parse_cmd(String str, Dict(empty) *opts, Error *err) FUNC_API_SINCE(10) FUNC_API_FAST { Dictionary result = ARRAY_DICT_INIT; - VALIDATE((opts.size == 0), "%s", "opts dict isn't empty", { - return result; - }); - // Parse command line exarg_T ea; CmdParseInfo cmdinfo; diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 11795033cc..d6a76617a7 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -126,7 +126,7 @@ void nvim_buf_clear_highlight(Buffer buffer, Integer ns_id, Integer line_start, /// @param[out] err Error details, if any /// @return The ns_id that was used Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, Array chunks, - Dictionary opts, Error *err) + Dict(empty) *opts, Error *err) FUNC_API_SINCE(5) FUNC_API_DEPRECATED_SINCE(8) { @@ -140,11 +140,6 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, A return 0; } - if (opts.size > 0) { - api_set_error(err, kErrorTypeValidation, "opts dict isn't empty"); - return 0; - } - uint32_t ns_id = src2ns(&src_id); int width; diff --git a/src/nvim/api/deprecated.h b/src/nvim/api/deprecated.h index e20d8304e0..c879794bb3 100644 --- a/src/nvim/api/deprecated.h +++ b/src/nvim/api/deprecated.h @@ -2,6 +2,7 @@ #include // IWYU pragma: keep +#include "nvim/api/keysets_defs.h" // IWYU pragma: keep #include "nvim/api/private/defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 80b1546329..ec47d7227e 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -187,7 +187,7 @@ static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_na /// @return 0-indexed (row, col) tuple or empty list () if extmark id was /// absent ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, - Integer id, Dictionary opts, + Integer id, Dict(get_extmark) *opts, Error *err) FUNC_API_SINCE(7) { @@ -203,27 +203,9 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, return rv; }); - bool details = false; - bool hl_name = true; - for (size_t i = 0; i < opts.size; i++) { - String k = opts.items[i].key; - Object *v = &opts.items[i].value; - if (strequal("details", k.data)) { - details = api_object_to_bool(*v, "details", false, err); - if (ERROR_SET(err)) { - return rv; - } - } else if (strequal("hl_name", k.data)) { - hl_name = api_object_to_bool(*v, "hl_name", false, err); - if (ERROR_SET(err)) { - return rv; - } - } else { - VALIDATE_S(false, "'opts' key", k.data, { - return rv; - }); - } - } + bool details = opts->details; + + bool hl_name = GET_BOOL_OR_TRUE(opts, get_extmark, hl_name); MTPair extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id); if (extmark.start.pos.row < 0) { diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index e59eda5686..5aae88f7b3 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -51,6 +51,12 @@ typedef struct { Boolean undo_restore; } Dict(set_extmark); +typedef struct { + OptionalKeys is_set__get_extmark_; + Boolean details; + Boolean hl_name; +} Dict(get_extmark); + typedef struct { OptionalKeys is_set__get_extmarks_; Integer limit; @@ -313,3 +319,29 @@ typedef struct { typedef struct { Boolean output; } Dict(exec_opts); + +typedef struct { + OptionalKeys is_set__buf_attach_; + LuaRef on_lines; + LuaRef on_bytes; + LuaRef on_changedtick; + LuaRef on_detach; + LuaRef on_reload; + Boolean utf_sizes; + Boolean preview; +} Dict(buf_attach); + +typedef struct { + OptionalKeys is_set__buf_delete_; + Boolean force; + Boolean unload; +} Dict(buf_delete); + +typedef struct { + OptionalKeys is_set__empty_; +} Dict(empty); + +typedef struct { + OptionalKeys is_set__open_term_; + LuaRef on_input; +} Dict(open_term); diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index db08cb8cf0..0842469c59 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -987,7 +987,7 @@ fail: /// ["input", term, bufnr, data] /// @param[out] err Error details, if any /// @return Channel id, or 0 on error -Integer nvim_open_term(Buffer buffer, DictionaryOf(LuaRef) opts, Error *err) +Integer nvim_open_term(Buffer buffer, Dict(open_term) *opts, Error *err) FUNC_API_SINCE(7) FUNC_API_TEXTLOCK_ALLOW_CMDWIN { @@ -1002,19 +1002,10 @@ Integer nvim_open_term(Buffer buffer, DictionaryOf(LuaRef) opts, Error *err) } LuaRef cb = LUA_NOREF; - for (size_t i = 0; i < opts.size; i++) { - String k = opts.items[i].key; - Object *v = &opts.items[i].value; - if (strequal("on_input", k.data)) { - VALIDATE_T("on_input", kObjectTypeLuaRef, v->type, { - return 0; - }); - cb = v->data.luaref; - v->data.luaref = LUA_NOREF; - break; - } else { - VALIDATE_S(false, "'opts' key", k.data, {}); - } + + if (HAS_KEY(opts, open_term, on_input)) { + cb = opts->on_input; + opts->on_input = LUA_NOREF; } Channel *chan = channel_alloc(kChannelStreamInternal); @@ -1941,14 +1932,10 @@ Object nvim_get_proc(Integer pid, Error *err) /// @param finish Finish the completion and dismiss the popup menu. Implies {insert}. /// @param opts Optional parameters. Reserved for future use. /// @param[out] err Error details, if any -void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish, Dictionary opts, +void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish, Dict(empty) *opts, Error *err) FUNC_API_SINCE(6) { - VALIDATE((opts.size == 0), "%s", "opts dict isn't empty", { - return; - }); - if (finish) { insert = true; } @@ -2049,7 +2036,7 @@ Boolean nvim_del_mark(String name, Error *err) /// not set. /// @see |nvim_buf_set_mark()| /// @see |nvim_del_mark()| -Array nvim_get_mark(String name, Dictionary opts, Error *err) +Array nvim_get_mark(String name, Dict(empty) *opts, Error *err) FUNC_API_SINCE(8) { Array rv = ARRAY_DICT_INIT; -- cgit From 14572727261278e5bf68080c9369a8507f3d564f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 5 Dec 2023 20:05:12 +0800 Subject: refactor(IWYU): move marktree types to marktree_defs.h (#26402) --- src/nvim/api/buffer.c | 1 + src/nvim/api/keysets_defs.h | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index e0dd265167..99d261e73d 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -12,6 +12,7 @@ #include "nvim/api/buffer.h" #include "nvim/api/keysets_defs.h" #include "nvim/api/private/defs.h" +#include "nvim/api/private/dispatch.h" #include "nvim/api/private/helpers.h" #include "nvim/api/private/validate.h" #include "nvim/ascii_defs.h" diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index 5aae88f7b3..d1cbe43de0 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -2,6 +2,10 @@ #include "nvim/api/private/defs.h" +typedef struct { + OptionalKeys is_set__empty_; +} Dict(empty); + typedef struct { OptionalKeys is_set__context_; Array types; @@ -337,10 +341,6 @@ typedef struct { Boolean unload; } Dict(buf_delete); -typedef struct { - OptionalKeys is_set__empty_; -} Dict(empty); - typedef struct { OptionalKeys is_set__open_term_; LuaRef on_input; -- cgit From e718866358652020e57fdb43bd9322f0c5732432 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Wed, 6 Dec 2023 13:39:36 +0600 Subject: refactor(options): remove SOPT type enums (#26417) Problem: SOPT type enums (`SOPT_NUM`, `SOPT_BOOL`, `SOPT_STRING`) are no longer used anywhere. Solution: Remove them. --- src/nvim/api/options.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index 9cf91bad42..ef7c11131a 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -400,7 +400,7 @@ int get_option_attrs(char *name) vimoption_T *opt = get_option(opt_idx); if (is_tty_option(opt->fullname)) { - return SOPT_STRING | SOPT_GLOBAL; + return SOPT_GLOBAL; } // Hidden option @@ -410,14 +410,6 @@ int get_option_attrs(char *name) int attrs = 0; - if (opt->flags & P_BOOL) { - attrs |= SOPT_BOOL; - } else if (opt->flags & P_NUM) { - attrs |= SOPT_NUM; - } else if (opt->flags & P_STRING) { - attrs |= SOPT_STRING; - } - if (opt->indir == PV_NONE || (opt->indir & PV_BOTH)) { attrs |= SOPT_GLOBAL; } -- cgit From cca6c4c6986abc67cd213ad1d32d329384a57790 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 6 Dec 2023 17:02:13 +0100 Subject: feat(rpc): allow empty string key in msgpack => Vim conversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Since e057b38e7037 #20757 we support empty key in JSON encode/decode, but we don't allow it in RPC object => Vim dict conversion. But empty string is a valid key in Vim dicts and the msgpack spec. Empty string key was disallowed in 7c01d5ff9286 (2014) but that commit/PR doesn't explicitly discuss it, so presumably it was a "seems reasonable" decision (or Vimscript didn't allow empty keys until later). Solution: Remove the check in `object_to_vim()`. Note that `tv_dict_item_alloc_len` will invoke `memcpy(…, 0)` but that's allowed by the C spec: https://stackoverflow.com/a/3751937/152142 --- src/nvim/api/private/converter.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index ef57bde32d..1e11683c38 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -328,15 +328,6 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) for (uint32_t i = 0; i < obj.data.dictionary.size; i++) { KeyValuePair item = obj.data.dictionary.items[i]; String key = item.key; - - if (key.size == 0) { - api_set_error(err, kErrorTypeValidation, - "Empty dictionary keys aren't allowed"); - // cleanup - tv_dict_free(dict); - return false; - } - dictitem_T *const di = tv_dict_item_alloc(key.data); if (!object_to_vim(item.value, &di->di_tv, err)) { -- cgit From a16218d4c6773c474011149661470af6e8c90892 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 7 Dec 2023 13:01:42 +0100 Subject: refactor: object_to_vim() cannot fail Since the parent commit, object_to_vim() can't fail, so callers don't need to check its result. --- src/nvim/api/private/converter.c | 22 +++------------------- src/nvim/api/private/helpers.c | 4 +--- src/nvim/api/vimscript.c | 9 ++------- 3 files changed, 6 insertions(+), 29 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index 1e11683c38..e7b8934c97 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -258,9 +258,7 @@ Object vim_to_object(typval_T *obj) /// @param tv Conversion result is placed here. On failure member v_type is /// set to VAR_UNKNOWN (no allocation was made for this variable). /// @param err Error object. -/// -/// @returns true if conversion is successful, otherwise false. -bool object_to_vim(Object obj, typval_T *tv, Error *err) +void object_to_vim(Object obj, typval_T *tv, Error *err) { tv->v_type = VAR_UNKNOWN; tv->v_lock = VAR_UNLOCKED; @@ -307,12 +305,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) for (uint32_t i = 0; i < obj.data.array.size; i++) { Object item = obj.data.array.items[i]; typval_T li_tv; - - if (!object_to_vim(item, &li_tv, err)) { - tv_list_free(list); - return false; - } - + object_to_vim(item, &li_tv, err); tv_list_append_owned_tv(list, li_tv); } tv_list_ref(list); @@ -329,14 +322,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) KeyValuePair item = obj.data.dictionary.items[i]; String key = item.key; dictitem_T *const di = tv_dict_item_alloc(key.data); - - if (!object_to_vim(item.value, &di->di_tv, err)) { - // cleanup - tv_dict_item_free(di); - tv_dict_free(dict); - return false; - } - + object_to_vim(item.value, &di->di_tv, err); tv_dict_add(dict, di); } dict->dv_refcount++; @@ -353,6 +339,4 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) break; } } - - return true; } diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index b23684aee9..2772fa8b59 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -253,9 +253,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva typval_T tv; // Convert the object to a vimscript type in the temporary variable - if (!object_to_vim(value, &tv, err)) { - return rv; - } + object_to_vim(value, &tv, err); typval_T oldtv = TV_INITIAL_VALUE; diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index 25a34f769c..3bca988030 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -207,9 +207,7 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err) typval_T vim_args[MAX_FUNC_ARGS + 1]; size_t i = 0; // also used for freeing the variables for (; i < args.size; i++) { - if (!object_to_vim(args.items[i], &vim_args[i], err)) { - goto free_vim_args; - } + object_to_vim(args.items[i], &vim_args[i], err); } // Initialize `force_abort` and `suppress_errthrow` at the top level. @@ -243,7 +241,6 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err) tv_clear(&rettv); recursive--; -free_vim_args: while (i > 0) { tv_clear(&vim_args[--i]); } @@ -297,9 +294,7 @@ Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err) mustfree = true; break; case kObjectTypeDictionary: - if (!object_to_vim(dict, &rettv, err)) { - goto end; - } + object_to_vim(dict, &rettv, err); break; default: api_set_error(err, kErrorTypeValidation, -- cgit From 4a34da82c18e6da1e46d6bf3d21082a6b6c8b947 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 6 Dec 2023 13:34:19 +0100 Subject: perf(column): keep track of number of lines that hold up the 'signcolumn' Problem: The entire marktree needs to be traversed each time a sign is removed from the sentinel line. Solution: Remove sentinel line and instead keep track of the number of lines that hold up the 'signcolumn' in "max_count". Adjust this number for added/removed signs, and set it to 0 when the maximum number of signs on a line changes. Only when "max_count" is decremented to 0 due to sign removal do we need to check the entire buffer. Also replace "invalid_top" and "invalid_bot" with a map of invalid ranges, further reducing the number of lines to be checked. Also improve tree traversal when counting the number of signs. Instead of looping over the to be checked range and counting the overlap for each row, keep track of the overlap in an array and add this to the count. --- src/nvim/api/vim.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 0842469c59..2c937113e3 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2189,7 +2189,6 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * int cul_id = 0; int num_id = 0; linenr_T lnum = statuscol_lnum; - wp->w_scwidth = win_signcol_count(wp); decor_redraw_signs(wp, wp->w_buffer, lnum - 1, sattrs, &line_id, &cul_id, &num_id); statuscol.sattrs = sattrs; -- cgit From 9ae7d36ff5ebaf75597b442e10890bd77df01fbe Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Thu, 7 Dec 2023 00:40:48 +0600 Subject: refactor(options): split `get_option_value()` into smaller functions Problem: Currently, `get_option_value()` returns 3 separate things: The actual value of the option, whether the option is hidden, and the option flags. This makes the function difficult to refactor, modify or otherwise reason about. Solution: Split `get_option_value()` into 3 functions, each with a single purpose. This also affects `get_option_value_for()`. --- src/nvim/api/options.c | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index ef7c11131a..53fd8af8b5 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -152,23 +152,20 @@ static buf_T *do_ft_buf(char *filetype, aco_save_T *aco, Error *err) Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) FUNC_API_SINCE(9) { - Object rv = OBJECT_INIT; - OptVal value = NIL_OPTVAL; - int scope = 0; OptReqScope req_scope = kOptReqGlobal; void *from = NULL; char *filetype = NULL; if (!validate_option_value_args(opts, name.data, &scope, &req_scope, &from, &filetype, err)) { - goto err; + return (Object)OBJECT_INIT; } aco_save_T aco; buf_T *ftbuf = do_ft_buf(filetype, &aco, err); if (ERROR_SET(err)) { - goto err; + return (Object)OBJECT_INIT; } if (ftbuf != NULL) { @@ -176,8 +173,9 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) from = ftbuf; } - bool hidden; - value = get_option_value_for(name.data, NULL, scope, &hidden, req_scope, from, err); + int opt_idx = findoption(name.data); + OptVal value = get_option_value_for(opt_idx, scope, req_scope, from, err); + bool hidden = is_option_hidden(opt_idx); if (ftbuf != NULL) { // restore curwin/curbuf and a few other things @@ -198,7 +196,7 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) return optval_as_object(value); err: optval_free(value); - return rv; + return (Object)OBJECT_INIT; } /// Sets the value of an option. The behavior of this function matches that of @@ -391,6 +389,10 @@ static void restore_option_context(void *const ctx, OptReqScope req_scope) /// See SOPT_* in option_defs.h for other flags. int get_option_attrs(char *name) { + if (is_tty_option(name)) { + return SOPT_GLOBAL; + } + int opt_idx = findoption(name); if (opt_idx < 0) { @@ -399,10 +401,6 @@ int get_option_attrs(char *name) vimoption_T *opt = get_option(opt_idx); - if (is_tty_option(opt->fullname)) { - return SOPT_GLOBAL; - } - // Hidden option if (opt->var == NULL) { return 0; @@ -470,14 +468,11 @@ static bool option_has_scope(char *name, OptReqScope req_scope) /// buffer-local value depending on opt_scope). OptVal get_option_value_strict(char *name, OptReqScope req_scope, void *from, Error *err) { - OptVal retv = NIL_OPTVAL; - if (!option_has_scope(name, req_scope)) { - return retv; + return NIL_OPTVAL; } - if (get_tty_option(name, &retv.data.string.data)) { - retv.type = kOptValTypeString; - return retv; + if (is_tty_option(name)) { + return get_tty_option(name); } int opt_idx = findoption(name); @@ -490,11 +485,11 @@ OptVal get_option_value_strict(char *name, OptReqScope req_scope, void *from, Er : (req_scope == kOptReqBuf ? (void *)&aco : NULL); bool switched = switch_option_context(ctx, req_scope, from, err); if (ERROR_SET(err)) { - return retv; + return NIL_OPTVAL; } char *varp = get_varp_scope(opt, req_scope == kOptReqGlobal ? OPT_GLOBAL : OPT_LOCAL); - retv = optval_from_varp(opt_idx, varp); + OptVal retv = optval_from_varp(opt_idx, varp); if (switched) { restore_option_context(ctx, req_scope); @@ -505,7 +500,7 @@ OptVal get_option_value_strict(char *name, OptReqScope req_scope, void *from, Er /// Get option value for buffer / window. /// -/// @param[in] name Option name. +/// @param opt_idx Option index in options[] table. /// @param[out] flagsp Set to the option flags (P_xxxx) (if not NULL). /// @param[in] scope Option scope (can be OPT_LOCAL, OPT_GLOBAL or a combination). /// @param[out] hidden Whether option is hidden. @@ -514,8 +509,8 @@ OptVal get_option_value_strict(char *name, OptReqScope req_scope, void *from, Er /// @param[out] err Error message, if any. /// /// @return Option value. Must be freed by caller. -OptVal get_option_value_for(const char *const name, uint32_t *flagsp, int scope, bool *hidden, - const OptReqScope req_scope, void *const from, Error *err) +OptVal get_option_value_for(int opt_idx, int scope, const OptReqScope req_scope, void *const from, + Error *err) { switchwin_T switchwin; aco_save_T aco; @@ -527,7 +522,7 @@ OptVal get_option_value_for(const char *const name, uint32_t *flagsp, int scope, return NIL_OPTVAL; } - OptVal retv = get_option_value(name, flagsp, scope, hidden); + OptVal retv = get_option_value(opt_idx, scope); if (switched) { restore_option_context(ctx, req_scope); -- cgit From 6346987601a28b00564295ee8be0a8b00d9ff911 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Thu, 7 Dec 2023 23:46:57 +0600 Subject: refactor(options): reduce `findoption()` usage Problem: Many places in the code use `findoption()` to access an option using its name, even if the option index is available. This is very slow because it requires looping through the options array over and over. Solution: Use option index instead of name wherever possible. Also introduce an `OptIndex` enum which contains the index for every option as enum constants, this eliminates the need to pass static option names as strings. --- src/nvim/api/deprecated.c | 11 ++++---- src/nvim/api/options.c | 65 +++++++++++++++++++++-------------------------- src/nvim/api/vim.c | 6 ++--- 3 files changed, 38 insertions(+), 44 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index d6a76617a7..34bf029483 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -643,7 +643,7 @@ static Object get_option_from(void *from, OptReqScope req_scope, String name, Er return (Object)OBJECT_INIT; }); - OptVal value = get_option_value_strict(name.data, req_scope, from, err); + OptVal value = get_option_value_strict(findoption(name.data), req_scope, from, err); if (ERROR_SET(err)) { return (Object)OBJECT_INIT; } @@ -669,8 +669,8 @@ static void set_option_to(uint64_t channel_id, void *to, OptReqScope req_scope, return; }); - int flags = get_option_attrs(name.data); - VALIDATE_S(flags != 0, "option name", name.data, { + int opt_idx = findoption(name.data); + VALIDATE_S(opt_idx >= 0, "option name", name.data, { return; }); @@ -685,13 +685,14 @@ static void set_option_to(uint64_t channel_id, void *to, OptReqScope req_scope, return; }); + int attrs = get_option_attrs(opt_idx); // For global-win-local options -> setlocal // For win-local options -> setglobal and setlocal (opt_flags == 0) - const int opt_flags = (req_scope == kOptReqWin && !(flags & SOPT_GLOBAL)) + const int opt_flags = (req_scope == kOptReqWin && !(attrs & SOPT_GLOBAL)) ? 0 : (req_scope == kOptReqGlobal) ? OPT_GLOBAL : OPT_LOCAL; WITH_SCRIPT_CONTEXT(channel_id, { - set_option_value_for(name.data, optval, opt_flags, req_scope, to, err); + set_option_value_for(name.data, opt_idx, optval, opt_flags, req_scope, to, err); }); } diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index 53fd8af8b5..0d18c7871c 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -23,7 +23,7 @@ # include "api/options.c.generated.h" #endif -static int validate_option_value_args(Dict(option) *opts, char *name, int *scope, +static int validate_option_value_args(Dict(option) *opts, char *name, int *opt_idx, int *scope, OptReqScope *req_scope, void **from, char **filetype, Error *err) { @@ -79,7 +79,8 @@ static int validate_option_value_args(Dict(option) *opts, char *name, int *scope return FAIL; }); - int flags = get_option_attrs(name); + *opt_idx = findoption(name); + int flags = get_option_attrs(*opt_idx); if (flags == 0) { // hidden or unknown option api_set_error(err, kErrorTypeValidation, "Unknown option '%s'", name); @@ -119,10 +120,10 @@ static buf_T *do_ft_buf(char *filetype, aco_save_T *aco, Error *err) aucmd_prepbuf(aco, ftbuf); TRY_WRAP(err, { - set_option_value("bufhidden", STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL); - set_option_value("buftype", STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL); - set_option_value("swapfile", BOOLEAN_OPTVAL(false), OPT_LOCAL); - set_option_value("modeline", BOOLEAN_OPTVAL(false), OPT_LOCAL); // 'nomodeline' + set_option_value(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL); + set_option_value(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL); + set_option_value(kOptSwapfile, BOOLEAN_OPTVAL(false), OPT_LOCAL); + set_option_value(kOptModeline, BOOLEAN_OPTVAL(false), OPT_LOCAL); // 'nomodeline' ftbuf->b_p_ft = xstrdup(filetype); do_filetype_autocmd(ftbuf, false); @@ -152,12 +153,14 @@ static buf_T *do_ft_buf(char *filetype, aco_save_T *aco, Error *err) Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) FUNC_API_SINCE(9) { + int opt_idx = 0; int scope = 0; OptReqScope req_scope = kOptReqGlobal; void *from = NULL; char *filetype = NULL; - if (!validate_option_value_args(opts, name.data, &scope, &req_scope, &from, &filetype, err)) { + if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &from, &filetype, + err)) { return (Object)OBJECT_INIT; } @@ -173,7 +176,6 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) from = ftbuf; } - int opt_idx = findoption(name.data); OptVal value = get_option_value_for(opt_idx, scope, req_scope, from, err); bool hidden = is_option_hidden(opt_idx); @@ -217,10 +219,11 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict( Error *err) FUNC_API_SINCE(9) { + int opt_idx = 0; int scope = 0; OptReqScope req_scope = kOptReqGlobal; void *to = NULL; - if (!validate_option_value_args(opts, name.data, &scope, &req_scope, &to, NULL, err)) { + if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &to, NULL, err)) { return; } @@ -231,7 +234,7 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict( // // Then force scope to local since we don't want to change the global option if (req_scope == kOptReqWin && scope == 0) { - int flags = get_option_attrs(name.data); + int flags = get_option_attrs(opt_idx); if (flags & SOPT_GLOBAL) { scope = OPT_LOCAL; } @@ -249,7 +252,7 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict( }); WITH_SCRIPT_CONTEXT(channel_id, { - set_option_value_for(name.data, optval, scope, req_scope, to, err); + set_option_value_for(name.data, opt_idx, optval, scope, req_scope, to, err); }); } @@ -303,10 +306,12 @@ Dictionary nvim_get_all_options_info(Error *err) Dictionary nvim_get_option_info2(String name, Dict(option) *opts, Error *err) FUNC_API_SINCE(11) { + int opt_idx = 0; int scope = 0; OptReqScope req_scope = kOptReqGlobal; void *from = NULL; - if (!validate_option_value_args(opts, name.data, &scope, &req_scope, &from, NULL, err)) { + if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &from, NULL, + err)) { return (Dictionary)ARRAY_DICT_INIT; } @@ -382,19 +387,13 @@ static void restore_option_context(void *const ctx, OptReqScope req_scope) /// Get attributes for an option. /// -/// @param name Option name. +/// @param opt_idx Option index in options[] table. /// /// @return Option attributes. /// 0 for hidden or unknown option. /// See SOPT_* in option_defs.h for other flags. -int get_option_attrs(char *name) +int get_option_attrs(int opt_idx) { - if (is_tty_option(name)) { - return SOPT_GLOBAL; - } - - int opt_idx = findoption(name); - if (opt_idx < 0) { return 0; } @@ -422,14 +421,12 @@ int get_option_attrs(char *name) /// Check if option has a value in the requested scope. /// -/// @param name Option name. +/// @param opt_idx Option index in options[] table. /// @param req_scope Requested option scope. See OptReqScope in option.h. /// /// @return true if option has a value in the requested scope, false otherwise. -static bool option_has_scope(char *name, OptReqScope req_scope) +static bool option_has_scope(int opt_idx, OptReqScope req_scope) { - int opt_idx = findoption(name); - if (opt_idx < 0) { return false; } @@ -458,7 +455,7 @@ static bool option_has_scope(char *name, OptReqScope req_scope) /// Get the option value in the requested scope. /// -/// @param name Option name. +/// @param opt_idx Option index in options[] table. /// @param req_scope Requested option scope. See OptReqScope in option.h. /// @param[in] from Pointer to buffer or window for local option value. /// @param[out] err Error message, if any. @@ -466,17 +463,11 @@ static bool option_has_scope(char *name, OptReqScope req_scope) /// @return Option value in the requested scope. Returns a Nil option value if option is not found, /// hidden or if it isn't present in the requested scope. (i.e. has no global, window-local or /// buffer-local value depending on opt_scope). -OptVal get_option_value_strict(char *name, OptReqScope req_scope, void *from, Error *err) +OptVal get_option_value_strict(int opt_idx, OptReqScope req_scope, void *from, Error *err) { - if (!option_has_scope(name, req_scope)) { + if (opt_idx < 0 || !option_has_scope(opt_idx, req_scope)) { return NIL_OPTVAL; } - if (is_tty_option(name)) { - return get_tty_option(name); - } - - int opt_idx = findoption(name); - assert(opt_idx != 0); // option_has_scope() already verifies if option name is valid. vimoption_T *opt = get_option(opt_idx); switchwin_T switchwin; @@ -533,14 +524,16 @@ OptVal get_option_value_for(int opt_idx, int scope, const OptReqScope req_scope, /// Set option value for buffer / window. /// -/// @param[in] name Option name. +/// @param name Option name. +/// @param opt_idx Option index in options[] table. /// @param[in] value Option value. /// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both). /// @param req_scope Requested option scope. See OptReqScope in option.h. /// @param[in] from Target buffer/window. /// @param[out] err Error message, if any. -void set_option_value_for(const char *const name, OptVal value, const int opt_flags, +void set_option_value_for(const char *name, int opt_idx, OptVal value, const int opt_flags, const OptReqScope req_scope, void *const from, Error *err) + FUNC_ATTR_NONNULL_ARG(1) { switchwin_T switchwin; aco_save_T aco; @@ -552,7 +545,7 @@ void set_option_value_for(const char *const name, OptVal value, const int opt_fl return; } - const char *const errmsg = set_option_value(name, value, opt_flags); + const char *const errmsg = set_option_value_handle_tty(name, opt_idx, value, opt_flags); if (errmsg) { api_set_error(err, kErrorTypeException, "%s", errmsg); } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 2c937113e3..a52d7493e3 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -948,8 +948,8 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err) buf_copy_options(buf, BCO_ENTER | BCO_NOHELP); if (scratch) { - set_string_option_direct_in_buf(buf, "bufhidden", -1, "hide", OPT_LOCAL, 0); - set_string_option_direct_in_buf(buf, "buftype", -1, "nofile", OPT_LOCAL, 0); + set_string_option_direct_in_buf(buf, kOptBufhidden, "hide", OPT_LOCAL, 0); + set_string_option_direct_in_buf(buf, kOptBuftype, "nofile", OPT_LOCAL, 0); assert(buf->b_ml.ml_mfp->mf_fd < 0); // ml_open() should not have opened swapfile already buf->b_p_swf = false; buf->b_p_ml = false; @@ -2239,7 +2239,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * buf, sizeof(buf), str.data, - NULL, + -1, 0, fillchar, maxwidth, -- cgit From bf3bc1cec9f00b9644815001a8732ecedf3ce07f Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Thu, 7 Dec 2023 23:59:30 +0600 Subject: refactor(options): convert `opt_idx` variables to `OptIndex` --- src/nvim/api/deprecated.c | 4 ++-- src/nvim/api/options.c | 34 +++++++++++++++++----------------- 2 files changed, 19 insertions(+), 19 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 34bf029483..371361c5a1 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -669,8 +669,8 @@ static void set_option_to(uint64_t channel_id, void *to, OptReqScope req_scope, return; }); - int opt_idx = findoption(name.data); - VALIDATE_S(opt_idx >= 0, "option name", name.data, { + OptIndex opt_idx = findoption(name.data); + VALIDATE_S(opt_idx != kOptInvalid, "option name", name.data, { return; }); diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index 0d18c7871c..54a2fbf385 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -23,9 +23,9 @@ # include "api/options.c.generated.h" #endif -static int validate_option_value_args(Dict(option) *opts, char *name, int *opt_idx, int *scope, - OptReqScope *req_scope, void **from, char **filetype, - Error *err) +static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *opt_idxp, + int *scope, OptReqScope *req_scope, void **from, + char **filetype, Error *err) { #define HAS_KEY_X(d, v) HAS_KEY(d, option, v) if (HAS_KEY_X(opts, scope)) { @@ -79,8 +79,8 @@ static int validate_option_value_args(Dict(option) *opts, char *name, int *opt_i return FAIL; }); - *opt_idx = findoption(name); - int flags = get_option_attrs(*opt_idx); + *opt_idxp = findoption(name); + int flags = get_option_attrs(*opt_idxp); if (flags == 0) { // hidden or unknown option api_set_error(err, kErrorTypeValidation, "Unknown option '%s'", name); @@ -153,7 +153,7 @@ static buf_T *do_ft_buf(char *filetype, aco_save_T *aco, Error *err) Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) FUNC_API_SINCE(9) { - int opt_idx = 0; + OptIndex opt_idx = 0; int scope = 0; OptReqScope req_scope = kOptReqGlobal; void *from = NULL; @@ -219,7 +219,7 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict( Error *err) FUNC_API_SINCE(9) { - int opt_idx = 0; + OptIndex opt_idx = 0; int scope = 0; OptReqScope req_scope = kOptReqGlobal; void *to = NULL; @@ -306,7 +306,7 @@ Dictionary nvim_get_all_options_info(Error *err) Dictionary nvim_get_option_info2(String name, Dict(option) *opts, Error *err) FUNC_API_SINCE(11) { - int opt_idx = 0; + OptIndex opt_idx = 0; int scope = 0; OptReqScope req_scope = kOptReqGlobal; void *from = NULL; @@ -392,9 +392,9 @@ static void restore_option_context(void *const ctx, OptReqScope req_scope) /// @return Option attributes. /// 0 for hidden or unknown option. /// See SOPT_* in option_defs.h for other flags. -int get_option_attrs(int opt_idx) +int get_option_attrs(OptIndex opt_idx) { - if (opt_idx < 0) { + if (opt_idx == kOptInvalid) { return 0; } @@ -425,9 +425,9 @@ int get_option_attrs(int opt_idx) /// @param req_scope Requested option scope. See OptReqScope in option.h. /// /// @return true if option has a value in the requested scope, false otherwise. -static bool option_has_scope(int opt_idx, OptReqScope req_scope) +static bool option_has_scope(OptIndex opt_idx, OptReqScope req_scope) { - if (opt_idx < 0) { + if (opt_idx == kOptInvalid) { return false; } @@ -463,9 +463,9 @@ static bool option_has_scope(int opt_idx, OptReqScope req_scope) /// @return Option value in the requested scope. Returns a Nil option value if option is not found, /// hidden or if it isn't present in the requested scope. (i.e. has no global, window-local or /// buffer-local value depending on opt_scope). -OptVal get_option_value_strict(int opt_idx, OptReqScope req_scope, void *from, Error *err) +OptVal get_option_value_strict(OptIndex opt_idx, OptReqScope req_scope, void *from, Error *err) { - if (opt_idx < 0 || !option_has_scope(opt_idx, req_scope)) { + if (opt_idx == kOptInvalid || !option_has_scope(opt_idx, req_scope)) { return NIL_OPTVAL; } @@ -500,8 +500,8 @@ OptVal get_option_value_strict(int opt_idx, OptReqScope req_scope, void *from, E /// @param[out] err Error message, if any. /// /// @return Option value. Must be freed by caller. -OptVal get_option_value_for(int opt_idx, int scope, const OptReqScope req_scope, void *const from, - Error *err) +OptVal get_option_value_for(OptIndex opt_idx, int scope, const OptReqScope req_scope, + void *const from, Error *err) { switchwin_T switchwin; aco_save_T aco; @@ -531,7 +531,7 @@ OptVal get_option_value_for(int opt_idx, int scope, const OptReqScope req_scope, /// @param req_scope Requested option scope. See OptReqScope in option.h. /// @param[in] from Target buffer/window. /// @param[out] err Error message, if any. -void set_option_value_for(const char *name, int opt_idx, OptVal value, const int opt_flags, +void set_option_value_for(const char *name, OptIndex opt_idx, OptVal value, const int opt_flags, const OptReqScope req_scope, void *const from, Error *err) FUNC_ATTR_NONNULL_ARG(1) { -- cgit From 619407eb548c7df56bc99b945338e9446f846fbb Mon Sep 17 00:00:00 2001 From: Raphael Date: Thu, 14 Dec 2023 16:08:00 +0800 Subject: feat(nvim_open_term): convert LF => CRLF (#26384) Problem: Unlike termopen(), nvim_open_term() PTYs do not carriage-return the cursor on newline ("\n") input. nvim --clean :let chan_id = nvim_open_term(1, {}) :call chansend(chan_id, ["here", "are", "some", "lines"]) Actual behavior: here are some lines Expected behaviour: here are some lines Solution: Add `force_crlf` option, and enable it by default. --- src/nvim/api/keysets_defs.h | 1 + src/nvim/api/vim.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index d1cbe43de0..c0daa0ca74 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -344,4 +344,5 @@ typedef struct { typedef struct { OptionalKeys is_set__open_term_; LuaRef on_input; + Boolean force_crlf; } Dict(open_term); diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index a52d7493e3..2f3d527b9e 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -985,6 +985,7 @@ fail: /// as a "\r", not as a "\n". |textlock| applies. It is possible /// to call |nvim_chan_send()| directly in the callback however. /// ["input", term, bufnr, data] +/// - force_crlf: (boolean, default true) Convert "\n" to "\r\n". /// @param[out] err Error details, if any /// @return Channel id, or 0 on error Integer nvim_open_term(Buffer buffer, Dict(open_term) *opts, Error *err) @@ -1002,7 +1003,6 @@ Integer nvim_open_term(Buffer buffer, Dict(open_term) *opts, Error *err) } LuaRef cb = LUA_NOREF; - if (HAS_KEY(opts, open_term, on_input)) { cb = opts->on_input; opts->on_input = LUA_NOREF; @@ -1020,6 +1020,7 @@ Integer nvim_open_term(Buffer buffer, Dict(open_term) *opts, Error *err) .write_cb = term_write, .resize_cb = term_resize, .close_cb = term_close, + .force_crlf = GET_BOOL_OR_TRUE(opts, open_term, force_crlf), }; channel_incref(chan); terminal_open(&chan->term, buf, topts); -- cgit From e38027ef69f75653ee953b16ebf4a8652a3fb748 Mon Sep 17 00:00:00 2001 From: mathew Date: Tue, 15 Aug 2023 18:47:14 +0800 Subject: feat(ui): completeopt support popup like vim --- src/nvim/api/keysets_defs.h | 5 +++++ src/nvim/api/vim.c | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) (limited to 'src/nvim/api') diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index c0daa0ca74..fa9a2cb013 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -346,3 +346,8 @@ typedef struct { LuaRef on_input; Boolean force_crlf; } Dict(open_term); + +typedef struct { + OptionalKeys is_set__complete_set_; + String info; +} Dict(complete_set); diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 2f3d527b9e..91f908bb88 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2300,3 +2300,29 @@ void nvim_error_event(uint64_t channel_id, Integer lvl, String data) // if we fork nvim processes as async workers ELOG("async error on channel %" PRId64 ": %s", channel_id, data.size ? data.data : ""); } + +/// Set info for the completion candidate index. +/// if the info was shown in a window, then the +/// window and buffer ids are returned for further +/// customization. If the text was not shown, an +/// empty dict is returned. +/// +/// @param index the completion candidate index +/// @param opts Optional parameters. +/// - info: (string) info text. +/// @return Dictionary containing these keys: +/// - winid: (number) floating window id +/// - bufnr: (number) buffer id in floating window +Dictionary nvim_complete_set(Integer index, Dict(complete_set) *opts) + FUNC_API_SINCE(12) +{ + Dictionary rv = ARRAY_DICT_INIT; + if (HAS_KEY(opts, complete_set, info)) { + win_T *wp = pum_set_info((int)index, opts->info.data); + if (wp) { + PUT(rv, "winid", WINDOW_OBJ(wp->handle)); + PUT(rv, "bufnr", BUFFER_OBJ(wp->w_buffer->handle)); + } + } + return rv; +} -- cgit From c18f3cfcdb8fc5a39b7c898eea619e3de94096b4 Mon Sep 17 00:00:00 2001 From: notomo Date: Sat, 16 Dec 2023 22:58:04 +0900 Subject: fix(api): crash after nvim_win_set_config title/footer validation error (#26606) --- src/nvim/api/win_config.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 856fac5585..4ffe3478d7 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -431,18 +431,36 @@ static bool parse_float_bufpos(Array bufpos, lpos_T *out) static void parse_bordertext(Object bordertext, BorderTextType bordertext_type, FloatConfig *fconfig, Error *err) { + if (bordertext.type != kObjectTypeString && bordertext.type != kObjectTypeArray) { + api_set_error(err, kErrorTypeValidation, "title/footer must be string or array"); + return; + } + + if (bordertext.type == kObjectTypeArray && bordertext.data.array.size == 0) { + api_set_error(err, kErrorTypeValidation, "title/footer cannot be an empty array"); + return; + } + bool *is_present; VirtText *chunks; int *width; int default_hl_id; switch (bordertext_type) { case kBorderTextTitle: + if (fconfig->title) { + clear_virttext(&fconfig->title_chunks); + } + is_present = &fconfig->title; chunks = &fconfig->title_chunks; width = &fconfig->title_width; default_hl_id = syn_check_group(S_LEN("FloatTitle")); break; case kBorderTextFooter: + if (fconfig->footer) { + clear_virttext(&fconfig->footer_chunks); + } + is_present = &fconfig->footer; chunks = &fconfig->footer_chunks; width = &fconfig->footer_width; @@ -462,16 +480,6 @@ static void parse_bordertext(Object bordertext, BorderTextType bordertext_type, return; } - if (bordertext.type != kObjectTypeArray) { - api_set_error(err, kErrorTypeValidation, "title must be string or array"); - return; - } - - if (bordertext.data.array.size == 0) { - api_set_error(err, kErrorTypeValidation, "title cannot be an empty array"); - return; - } - *width = 0; *chunks = parse_virt_text(bordertext.data.array, err, width); @@ -774,10 +782,6 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, return false; } - if (fconfig->title) { - clear_virttext(&fconfig->title_chunks); - } - parse_bordertext(config->title, kBorderTextTitle, fconfig, err); if (ERROR_SET(err)) { return false; @@ -801,10 +805,6 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, return false; } - if (fconfig->footer) { - clear_virttext(&fconfig->footer_chunks); - } - parse_bordertext(config->footer, kBorderTextFooter, fconfig, err); if (ERROR_SET(err)) { return false; -- cgit From 8f08b1efbd096850c04c2e8e2890d993bd4d9f95 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Sun, 17 Dec 2023 05:23:33 +0600 Subject: refactor(options): use hashy for finding options (#26573) Problem: `findoption()` searches through the options[] table linearly for option names, even though hashy can be used to generate a compile-time hash table for it. Solution: Use hashy to generate a compile time hash table for finding options. This also allows handling option aliases, so we don't need separate options[] table entries for things like 'viminfo'. --- src/nvim/api/deprecated.c | 4 ++-- src/nvim/api/options.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 371361c5a1..27af581ef4 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -643,7 +643,7 @@ static Object get_option_from(void *from, OptReqScope req_scope, String name, Er return (Object)OBJECT_INIT; }); - OptVal value = get_option_value_strict(findoption(name.data), req_scope, from, err); + OptVal value = get_option_value_strict(find_option(name.data), req_scope, from, err); if (ERROR_SET(err)) { return (Object)OBJECT_INIT; } @@ -669,7 +669,7 @@ static void set_option_to(uint64_t channel_id, void *to, OptReqScope req_scope, return; }); - OptIndex opt_idx = findoption(name.data); + OptIndex opt_idx = find_option(name.data); VALIDATE_S(opt_idx != kOptInvalid, "option name", name.data, { return; }); diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index 54a2fbf385..a3ea7d9d47 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -79,7 +79,7 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex * return FAIL; }); - *opt_idxp = findoption(name); + *opt_idxp = find_option(name); int flags = get_option_attrs(*opt_idxp); if (flags == 0) { // hidden or unknown option -- cgit From 69bc519b53ebf78fd95c8256468e7d538ebcb948 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Tue, 12 Dec 2023 15:40:21 +0100 Subject: refactor: move non-symbols to defs.h headers --- src/nvim/api/options.c | 1 - src/nvim/api/private/dispatch.h | 2 +- src/nvim/api/private/helpers.c | 1 - src/nvim/api/private/helpers.h | 8 +------- src/nvim/api/ui.c | 2 +- src/nvim/api/vim.c | 1 + 6 files changed, 4 insertions(+), 11 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index a3ea7d9d47..ea617dffaf 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -15,7 +15,6 @@ #include "nvim/macros_defs.h" #include "nvim/memory.h" #include "nvim/option.h" -#include "nvim/option_vars.h" #include "nvim/vim_defs.h" #include "nvim/window.h" diff --git a/src/nvim/api/private/dispatch.h b/src/nvim/api/private/dispatch.h index 88f846f813..b627db6b00 100644 --- a/src/nvim/api/private/dispatch.h +++ b/src/nvim/api/private/dispatch.h @@ -27,5 +27,5 @@ extern const MsgpackRpcRequestHandler method_handlers[]; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/private/dispatch.h.generated.h" # include "api/private/dispatch_wrappers.h.generated.h" // IWYU pragma: export -# include "keysets_defs.generated.h" +# include "keysets_defs.generated.h" // IWYU pragma: export #endif diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 2772fa8b59..70e63d0ad5 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -17,7 +17,6 @@ #include "nvim/ascii_defs.h" #include "nvim/buffer_defs.h" #include "nvim/eval/typval.h" -#include "nvim/eval/typval_defs.h" #include "nvim/eval/vars.h" #include "nvim/ex_eval.h" #include "nvim/garray.h" diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index e61dd5f992..64558f0410 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -2,21 +2,15 @@ #include #include -#include #include "klib/kvec.h" #include "nvim/api/private/defs.h" -#include "nvim/api/private/dispatch.h" -#include "nvim/decoration.h" -#include "nvim/eval/typval_defs.h" #include "nvim/ex_eval_defs.h" -#include "nvim/getchar.h" #include "nvim/gettext.h" #include "nvim/globals.h" #include "nvim/macros_defs.h" #include "nvim/map_defs.h" -#include "nvim/memory.h" -#include "nvim/message.h" +#include "nvim/message_defs.h" // IWYU pragma: keep #define OBJECT_OBJ(o) o diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index b73c026d57..678d23fbeb 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -15,7 +15,7 @@ #include "nvim/autocmd.h" #include "nvim/channel.h" #include "nvim/eval.h" -#include "nvim/event/loop.h" +#include "nvim/event/defs.h" #include "nvim/event/wstream.h" #include "nvim/globals.h" #include "nvim/grid.h" diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 91f908bb88..70e6b840de 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -54,6 +54,7 @@ #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/input.h" +#include "nvim/os/os_defs.h" #include "nvim/os/process.h" #include "nvim/popupmenu.h" #include "nvim/pos_defs.h" -- cgit From d82a586a9e39f1d346c1aea78167a85c586ed3f4 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 18 Dec 2023 06:18:11 +0800 Subject: refactor: move some anonymous enums back to non-defs headers (#26622) It isn't really useful to put anonymous enums only used as arguments to functions calls in _defs.h headers, as they will only be used by a file that calls those functions, which requires including a non-defs header. Also move os_msg() and os_errmsg() back to message.h, as on Windows they are actual functions instead of macros. Also remove gettext.h and globals.h from private/helpers.h. --- src/nvim/api/private/helpers.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 64558f0410..701ce91257 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -5,9 +5,9 @@ #include "klib/kvec.h" #include "nvim/api/private/defs.h" +#include "nvim/buffer_defs.h" // IWYU pragma: keep +#include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/ex_eval_defs.h" -#include "nvim/gettext.h" -#include "nvim/globals.h" #include "nvim/macros_defs.h" #include "nvim/map_defs.h" #include "nvim/message_defs.h" // IWYU pragma: keep -- cgit From 6abdc1ac1f904173d01efcf440d1460c001bc299 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 18 Dec 2023 09:05:59 +0800 Subject: refactor: split WIN_EXECUTE() into two functions (#26627) --- src/nvim/api/window.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index d18971c756..7321d52e17 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -435,10 +435,12 @@ Object nvim_win_call(Window window, LuaRef fun, Error *err) try_start(); Object res = OBJECT_INIT; - WIN_EXECUTE(win, tabpage, { + win_execute_T win_execute_args; + if (win_execute_before(&win_execute_args, win, tabpage)) { Array args = ARRAY_DICT_INIT; res = nlua_call_ref(fun, NULL, args, true, err); - }); + } + win_execute_after(&win_execute_args); try_end(err); return res; } -- cgit From 428edcde7068ab44040e19b43343741e5ca59770 Mon Sep 17 00:00:00 2001 From: Amanda Graven Date: Tue, 28 Nov 2023 21:05:33 +0100 Subject: feat(api): add forward and back mouse buttons --- src/nvim/api/vim.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 70e6b840de..aed286165a 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -345,7 +345,8 @@ Integer nvim_input(String keys) /// mouse input in a GUI. The deprecated pseudokey form /// ("") of |nvim_input()| has the same limitation. /// -/// @param button Mouse button: one of "left", "right", "middle", "wheel", "move". +/// @param button Mouse button: one of "left", "right", "middle", "wheel", "move", +/// "x1", "x2". /// @param action For ordinary buttons, one of "press", "drag", "release". /// For the wheel, one of "up", "down", "left", "right". Ignored for "move". /// @param modifier String of modifiers each represented by a single char. @@ -376,6 +377,10 @@ void nvim_input_mouse(String button, String action, String modifier, Integer gri code = KE_RIGHTMOUSE; } else if (strequal(button.data, "wheel")) { code = KE_MOUSEDOWN; + } else if (strequal(button.data, "x1")) { + code = KE_X1MOUSE; + } else if (strequal(button.data, "x2")) { + code = KE_X2MOUSE; } else if (strequal(button.data, "move")) { code = KE_MOUSEMOVE; } else { -- cgit From 1cf51a07a6bfb827efc36911cd018da3a3cb863b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 19 Dec 2023 08:01:05 +0800 Subject: fix(api): don't set coladd of mark (#26648) --- src/nvim/api/private/helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 70e63d0ad5..23ec815346 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1044,7 +1044,7 @@ bool set_mark(buf_T *buf, String name, Integer line, Integer col, Error *err) } } assert(INT32_MIN <= line && line <= INT32_MAX); - pos_T pos = { (linenr_T)line, (int)col, (int)col }; + pos_T pos = { (linenr_T)line, (int)col, 0 }; res = setmark_pos(*name.data, &pos, buf->handle, NULL); if (!res) { if (deleting) { -- cgit From 0c120307ca1ab613e63865c634d7e10ad67fb0ba Mon Sep 17 00:00:00 2001 From: dundargoc Date: Wed, 20 Dec 2023 14:32:22 +0100 Subject: refactor: eliminate cyclic includes --- src/nvim/api/autocmd.c | 1 + src/nvim/api/deprecated.c | 1 + src/nvim/api/options.c | 1 + src/nvim/api/vimscript.c | 1 + src/nvim/api/win_config.c | 1 + 5 files changed, 5 insertions(+) (limited to 'src/nvim/api') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 2ce08bdf40..88d7d985e8 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -20,6 +20,7 @@ #include "nvim/globals.h" #include "nvim/lua/executor.h" #include "nvim/memory.h" +#include "nvim/types_defs.h" #include "nvim/vim_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 27af581ef4..f7bbd61cd3 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -21,6 +21,7 @@ #include "nvim/memory.h" #include "nvim/option.h" #include "nvim/pos_defs.h" +#include "nvim/types_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/deprecated.c.generated.h" diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index ea617dffaf..fb461152b4 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -15,6 +15,7 @@ #include "nvim/macros_defs.h" #include "nvim/memory.h" #include "nvim/option.h" +#include "nvim/types_defs.h" #include "nvim/vim_defs.h" #include "nvim/window.h" diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index 3bca988030..6f3f0127d1 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -11,6 +11,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/vimscript.h" #include "nvim/ascii_defs.h" +#include "nvim/buffer_defs.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 4ffe3478d7..be18dba315 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -23,6 +23,7 @@ #include "nvim/pos_defs.h" #include "nvim/strings.h" #include "nvim/syntax.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/window.h" #include "nvim/winfloat.h" -- cgit From 8533adb4844b771b84dac2141fa2fa60e0487b47 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 21 Dec 2023 16:50:05 +0800 Subject: refactor(IWYU): move decor provider types to decoration_defs.h (#26692) --- src/nvim/api/buffer.c | 1 - src/nvim/api/buffer.h | 3 ++- src/nvim/api/vim.c | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 99d261e73d..bbb6b92986 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -18,7 +18,6 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" -#include "nvim/buffer_defs.h" #include "nvim/buffer_updates.h" #include "nvim/change.h" #include "nvim/cursor.h" diff --git a/src/nvim/api/buffer.h b/src/nvim/api/buffer.h index f3971c1d30..fe2d104058 100644 --- a/src/nvim/api/buffer.h +++ b/src/nvim/api/buffer.h @@ -5,7 +5,8 @@ #include "nvim/api/keysets_defs.h" // IWYU pragma: keep #include "nvim/api/private/defs.h" // IWYU pragma: keep -#include "nvim/buffer_defs.h" // IWYU pragma: keep +#include "nvim/pos_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/buffer.h.generated.h" diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index aed286165a..9ae5244fa1 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -64,7 +64,6 @@ #include "nvim/statusline.h" #include "nvim/strings.h" #include "nvim/terminal.h" -#include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/vim_defs.h" #include "nvim/window.h" -- cgit From af93a74a0f4afa9a3a4f55ffdf28141eaf776d22 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 18 Dec 2023 10:55:23 +0100 Subject: refactor: run IWYU on entire repo Reference: https://github.com/neovim/neovim/issues/6371. --- src/nvim/api/private/converter.h | 2 +- src/nvim/api/private/dispatch.h | 2 +- src/nvim/api/private/helpers.h | 2 +- src/nvim/api/private/validate.h | 2 +- src/nvim/api/ui.c | 3 ++- src/nvim/api/vim.c | 1 + 6 files changed, 7 insertions(+), 5 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/private/converter.h b/src/nvim/api/private/converter.h index fc82abf332..a5acc56c7c 100644 --- a/src/nvim/api/private/converter.h +++ b/src/nvim/api/private/converter.h @@ -1,6 +1,6 @@ #pragma once -#include "nvim/api/private/defs.h" // IWYU pragma: keep +#include "nvim/api/private/defs.h" // IWYU pragma: export #include "nvim/eval/typval_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/api/private/dispatch.h b/src/nvim/api/private/dispatch.h index b627db6b00..d322c1ceca 100644 --- a/src/nvim/api/private/dispatch.h +++ b/src/nvim/api/private/dispatch.h @@ -3,7 +3,7 @@ #include #include -#include "nvim/api/private/defs.h" +#include "nvim/api/private/defs.h" // IWYU pragma: export #include "nvim/memory_defs.h" #include "nvim/types_defs.h" diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 701ce91257..1ee66f906b 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -4,7 +4,7 @@ #include #include "klib/kvec.h" -#include "nvim/api/private/defs.h" +#include "nvim/api/private/defs.h" // IWYU pragma: export #include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/ex_eval_defs.h" diff --git a/src/nvim/api/private/validate.h b/src/nvim/api/private/validate.h index d1c977cd6e..692ea46176 100644 --- a/src/nvim/api/private/validate.h +++ b/src/nvim/api/private/validate.h @@ -3,7 +3,7 @@ #include #include -#include "nvim/api/private/defs.h" +#include "nvim/api/private/defs.h" // IWYU pragma: export #include "nvim/api/private/helpers.h" #include "nvim/assert_defs.h" #include "nvim/macros_defs.h" diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 678d23fbeb..82d42d652d 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -15,7 +15,8 @@ #include "nvim/autocmd.h" #include "nvim/channel.h" #include "nvim/eval.h" -#include "nvim/event/defs.h" +#include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" #include "nvim/event/wstream.h" #include "nvim/globals.h" #include "nvim/grid.h" diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 9ae5244fa1..aed286165a 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -64,6 +64,7 @@ #include "nvim/statusline.h" #include "nvim/strings.h" #include "nvim/terminal.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/vim_defs.h" #include "nvim/window.h" -- cgit From ba0fa4fa197330687b06c74a50b2ccd4800f5881 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 22 Dec 2023 13:32:46 +0800 Subject: refactor(IWYU): add "private" pragma to more generated headers (#26706) "export" only prevents IWYU from adding these headers if the headers that export them are included, while "private" ensures that IWYU never adds these headers. --- src/nvim/api/private/dispatch.h | 4 ++-- src/nvim/api/ui.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/private/dispatch.h b/src/nvim/api/private/dispatch.h index d322c1ceca..9b167a7b9e 100644 --- a/src/nvim/api/private/dispatch.h +++ b/src/nvim/api/private/dispatch.h @@ -26,6 +26,6 @@ extern const MsgpackRpcRequestHandler method_handlers[]; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/private/dispatch.h.generated.h" -# include "api/private/dispatch_wrappers.h.generated.h" // IWYU pragma: export -# include "keysets_defs.generated.h" // IWYU pragma: export +# include "api/private/dispatch_wrappers.h.generated.h" +# include "keysets_defs.generated.h" #endif diff --git a/src/nvim/api/ui.h b/src/nvim/api/ui.h index b1f4ff97d9..9147f1d870 100644 --- a/src/nvim/api/ui.h +++ b/src/nvim/api/ui.h @@ -9,5 +9,5 @@ #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/ui.h.generated.h" -# include "ui_events_remote.h.generated.h" // IWYU pragma: export +# include "ui_events_remote.h.generated.h" #endif -- cgit From 66ac327db27c8097cfa6c1f136dca96151b074f4 Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 22 Nov 2023 19:58:17 +0100 Subject: refactor(drawline): remove LineDrawState and wlv->saved_n_extra We do not need an enum to keep track of what place in win_line() we currently are at. We already have a variable which keeps track where in the code we currently are (and thus what part of the line we are currently rendering), it is called the _program counter_. When we need non-linear or self-referential control-flow anyway for a laugh, we have a mechanism for that, it is called _function calls_. Do not "save" and "restore" the wlv->n_extra state every time the columns are to be drawn. This sort of thing needs to go away. Instead of setting the n_extra variables and then going to the outer while loop, the text in the columns can be rendered by just simply putting the text into the cells of the screen line, right away. Even in nvim this can be tricky sometimes, luckily we can use function calls to abstract this logic, which means that this handy data structure called the _call stack_ is handling saving away state temporarily, and restoring it back when we need it again. Lastly, but not least, as we now have direct control how signs are rendered, these can be stored as schar_T[2] and be directly put on screen as such. --- src/nvim/api/extmark.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index ec47d7227e..fa3c5afcc6 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -665,9 +665,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } if (HAS_KEY(opts, set_extmark, sign_text)) { - sign.text.ptr = NULL; - VALIDATE_S(init_sign_text(NULL, &sign.text.ptr, opts->sign_text.data), - "sign_text", "", { + sign.text[0] = 0; + VALIDATE_S(init_sign_text(NULL, sign.text, opts->sign_text.data), "sign_text", "", { goto error; }); sign.flags |= kSHIsSign; @@ -785,7 +784,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer uint32_t decor_indexed = DECOR_ID_INVALID; if (sign.flags & kSHIsSign) { decor_indexed = decor_put_sh(sign); - if (sign.text.ptr != NULL) { + if (sign.text[0]) { decor_flags |= MT_FLAG_DECOR_SIGNTEXT; } if (sign.number_hl_id || sign.line_hl_id || sign.cursorline_hl_id) { -- cgit From 2b0acacb3c2cdd67436846d5117ae323ea7a8fd4 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 19 Dec 2023 13:55:02 +0000 Subject: fix(decor): allow adding providers during redraw Fixes: #26652 --- src/nvim/api/extmark.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index fa3c5afcc6..b2ddef1a43 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -1050,7 +1050,7 @@ void nvim_set_decoration_provider(Integer ns_id, Dict(set_decoration_provider) * *v = LUA_NOREF; } - p->active = true; + p->state = kDecorProviderActive; p->hl_valid++; p->hl_cached = false; } -- cgit From 4d98ec2fa45a100292c490acc606dfb55a0573c1 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Mon, 18 Dec 2023 04:10:41 +0600 Subject: refactor(options): move some functions from options.c to option.c --- src/nvim/api/options.c | 235 ------------------------------------------------- 1 file changed, 235 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index fb461152b4..ff1741558d 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -10,7 +10,6 @@ #include "nvim/api/private/validate.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" -#include "nvim/eval/window.h" #include "nvim/globals.h" #include "nvim/macros_defs.h" #include "nvim/memory.h" @@ -320,237 +319,3 @@ Dictionary nvim_get_option_info2(String name, Dict(option) *opts, Error *err) return get_vimoption(name, scope, buf, win, err); } - -/// Switch current context to get/set option value for window/buffer. -/// -/// @param[out] ctx Current context. switchwin_T for window and aco_save_T for buffer. -/// @param req_scope Requested option scope. See OptReqScope in option.h. -/// @param[in] from Target buffer/window. -/// @param[out] err Error message, if any. -/// -/// @return true if context was switched, false otherwise. -static bool switch_option_context(void *const ctx, OptReqScope req_scope, void *const from, - Error *err) -{ - switch (req_scope) { - case kOptReqWin: { - win_T *const win = (win_T *)from; - switchwin_T *const switchwin = (switchwin_T *)ctx; - - if (win == curwin) { - return false; - } - - if (switch_win_noblock(switchwin, win, win_find_tabpage(win), true) - == FAIL) { - restore_win_noblock(switchwin, true); - - if (try_end(err)) { - return false; - } - api_set_error(err, kErrorTypeException, "Problem while switching windows"); - return false; - } - return true; - } - case kOptReqBuf: { - buf_T *const buf = (buf_T *)from; - aco_save_T *const aco = (aco_save_T *)ctx; - - if (buf == curbuf) { - return false; - } - aucmd_prepbuf(aco, buf); - return true; - } - case kOptReqGlobal: - return false; - } - UNREACHABLE; -} - -/// Restore context after getting/setting option for window/buffer. See switch_option_context() for -/// params. -static void restore_option_context(void *const ctx, OptReqScope req_scope) -{ - switch (req_scope) { - case kOptReqWin: - restore_win_noblock((switchwin_T *)ctx, true); - break; - case kOptReqBuf: - aucmd_restbuf((aco_save_T *)ctx); - break; - case kOptReqGlobal: - break; - } -} - -/// Get attributes for an option. -/// -/// @param opt_idx Option index in options[] table. -/// -/// @return Option attributes. -/// 0 for hidden or unknown option. -/// See SOPT_* in option_defs.h for other flags. -int get_option_attrs(OptIndex opt_idx) -{ - if (opt_idx == kOptInvalid) { - return 0; - } - - vimoption_T *opt = get_option(opt_idx); - - // Hidden option - if (opt->var == NULL) { - return 0; - } - - int attrs = 0; - - if (opt->indir == PV_NONE || (opt->indir & PV_BOTH)) { - attrs |= SOPT_GLOBAL; - } - if (opt->indir & PV_WIN) { - attrs |= SOPT_WIN; - } else if (opt->indir & PV_BUF) { - attrs |= SOPT_BUF; - } - - return attrs; -} - -/// Check if option has a value in the requested scope. -/// -/// @param opt_idx Option index in options[] table. -/// @param req_scope Requested option scope. See OptReqScope in option.h. -/// -/// @return true if option has a value in the requested scope, false otherwise. -static bool option_has_scope(OptIndex opt_idx, OptReqScope req_scope) -{ - if (opt_idx == kOptInvalid) { - return false; - } - - vimoption_T *opt = get_option(opt_idx); - - // Hidden option. - if (opt->var == NULL) { - return false; - } - // TTY option. - if (is_tty_option(opt->fullname)) { - return req_scope == kOptReqGlobal; - } - - switch (req_scope) { - case kOptReqGlobal: - return opt->var != VAR_WIN; - case kOptReqBuf: - return opt->indir & PV_BUF; - case kOptReqWin: - return opt->indir & PV_WIN; - } - UNREACHABLE; -} - -/// Get the option value in the requested scope. -/// -/// @param opt_idx Option index in options[] table. -/// @param req_scope Requested option scope. See OptReqScope in option.h. -/// @param[in] from Pointer to buffer or window for local option value. -/// @param[out] err Error message, if any. -/// -/// @return Option value in the requested scope. Returns a Nil option value if option is not found, -/// hidden or if it isn't present in the requested scope. (i.e. has no global, window-local or -/// buffer-local value depending on opt_scope). -OptVal get_option_value_strict(OptIndex opt_idx, OptReqScope req_scope, void *from, Error *err) -{ - if (opt_idx == kOptInvalid || !option_has_scope(opt_idx, req_scope)) { - return NIL_OPTVAL; - } - - vimoption_T *opt = get_option(opt_idx); - switchwin_T switchwin; - aco_save_T aco; - void *ctx = req_scope == kOptReqWin ? (void *)&switchwin - : (req_scope == kOptReqBuf ? (void *)&aco : NULL); - bool switched = switch_option_context(ctx, req_scope, from, err); - if (ERROR_SET(err)) { - return NIL_OPTVAL; - } - - char *varp = get_varp_scope(opt, req_scope == kOptReqGlobal ? OPT_GLOBAL : OPT_LOCAL); - OptVal retv = optval_from_varp(opt_idx, varp); - - if (switched) { - restore_option_context(ctx, req_scope); - } - - return retv; -} - -/// Get option value for buffer / window. -/// -/// @param opt_idx Option index in options[] table. -/// @param[out] flagsp Set to the option flags (P_xxxx) (if not NULL). -/// @param[in] scope Option scope (can be OPT_LOCAL, OPT_GLOBAL or a combination). -/// @param[out] hidden Whether option is hidden. -/// @param req_scope Requested option scope. See OptReqScope in option.h. -/// @param[in] from Target buffer/window. -/// @param[out] err Error message, if any. -/// -/// @return Option value. Must be freed by caller. -OptVal get_option_value_for(OptIndex opt_idx, int scope, const OptReqScope req_scope, - void *const from, Error *err) -{ - switchwin_T switchwin; - aco_save_T aco; - void *ctx = req_scope == kOptReqWin ? (void *)&switchwin - : (req_scope == kOptReqBuf ? (void *)&aco : NULL); - - bool switched = switch_option_context(ctx, req_scope, from, err); - if (ERROR_SET(err)) { - return NIL_OPTVAL; - } - - OptVal retv = get_option_value(opt_idx, scope); - - if (switched) { - restore_option_context(ctx, req_scope); - } - - return retv; -} - -/// Set option value for buffer / window. -/// -/// @param name Option name. -/// @param opt_idx Option index in options[] table. -/// @param[in] value Option value. -/// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both). -/// @param req_scope Requested option scope. See OptReqScope in option.h. -/// @param[in] from Target buffer/window. -/// @param[out] err Error message, if any. -void set_option_value_for(const char *name, OptIndex opt_idx, OptVal value, const int opt_flags, - const OptReqScope req_scope, void *const from, Error *err) - FUNC_ATTR_NONNULL_ARG(1) -{ - switchwin_T switchwin; - aco_save_T aco; - void *ctx = req_scope == kOptReqWin ? (void *)&switchwin - : (req_scope == kOptReqBuf ? (void *)&aco : NULL); - - bool switched = switch_option_context(ctx, req_scope, from, err); - if (ERROR_SET(err)) { - return; - } - - const char *const errmsg = set_option_value_handle_tty(name, opt_idx, value, opt_flags); - if (errmsg) { - api_set_error(err, kErrorTypeException, "%s", errmsg); - } - - if (switched) { - restore_option_context(ctx, req_scope); - } -} -- cgit From c16d5729b52d2f878cd035341b951b1f185b45c9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 23 Dec 2023 15:53:28 +0800 Subject: refactor: remove CPO_TO_CPO_FLAGS() (#26718) Just pass p_cpo to replace_termcodes() directly. This allows removing option_vars.h from keycodes.h, and also avoids the mistake of passing 0 as cpo_flags. --- src/nvim/api/vim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index aed286165a..860cca582c 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -461,7 +461,7 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Bool } char *ptr = NULL; - replace_termcodes(str.data, str.size, &ptr, 0, flags, NULL, CPO_TO_CPO_FLAGS); + replace_termcodes(str.data, str.size, &ptr, 0, flags, NULL, p_cpo); return cstr_as_string(ptr); } -- cgit From bbd5c6363c25e8fbbfb962f8f6c5ea1800d431ca Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Tue, 26 Dec 2023 00:16:03 +0100 Subject: feat(extmarks): add virt_text_repeat_linebreak flag (#26625) Problem: Unable to predict which byte-offset to place virtual text to make it repeat visually in the wrapped part of a line. Solution: Add a flag to nvim_buf_set_extmark() that causes virtual text to repeat in wrapped lines. --- src/nvim/api/extmark.c | 5 ++++- src/nvim/api/keysets_defs.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index b2ddef1a43..0cf16294b6 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -391,6 +391,8 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// text is selected or hidden because of /// scrolling with 'nowrap' or 'smoothscroll'. /// Currently only affects "overlay" virt_text. +/// - virt_text_repeat_linebreak : repeat the virtual text on +/// wrapped lines. /// - hl_mode : control how highlights are combined with the /// highlights of the text. Currently only affects /// virt_text highlights, but might affect `hl_group` @@ -613,7 +615,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } hl.flags |= opts->hl_eol ? kSHHlEol : 0; - virt_text.flags |= opts->virt_text_hide ? kVTHide : 0; + virt_text.flags |= ((opts->virt_text_hide ? kVTHide : 0) + | (opts->virt_text_repeat_linebreak ? kVTRepeatLinebreak : 0)); if (HAS_KEY(opts, set_extmark, hl_mode)) { String str = opts->hl_mode; diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index fa9a2cb013..897d546f3d 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -33,6 +33,7 @@ typedef struct { String virt_text_pos; Integer virt_text_win_col; Boolean virt_text_hide; + Boolean virt_text_repeat_linebreak; Boolean hl_eol; String hl_mode; Boolean invalidate; -- cgit From 714b075197c02d27d5c05133759ae97bed279b50 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Thu, 28 Dec 2023 03:28:48 +0100 Subject: docs: small fixes (#26651) Co-authored-by: Gregory Anders Co-authored-by: WillLillis --- src/nvim/api/win_config.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index be18dba315..64eda43689 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -68,7 +68,6 @@ /// ```lua /// vim.api.nvim_open_win(0, false, /// {relative='win', width=12, height=3, bufpos={100,10}}) -/// }) /// ``` /// /// @param buffer Buffer to display, or 0 for current buffer -- cgit From c89292fcb7f2ebf06efb7c1d00c28f34c6f68fec Mon Sep 17 00:00:00 2001 From: dundargoc Date: Thu, 28 Dec 2023 13:42:24 +0100 Subject: refactor: follow style guide --- src/nvim/api/private/helpers.c | 2 +- src/nvim/api/vim.c | 2 +- src/nvim/api/vimscript.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 23ec815346..06addbecb7 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -138,7 +138,7 @@ bool try_end(Error *err) api_set_error(err, kErrorTypeException, "Keyboard interrupt"); got_int = false; } else if (msg_list != NULL && *msg_list != NULL) { - int should_free; + bool should_free; char *msg = get_exception_string(*msg_list, ET_ERROR, NULL, diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 860cca582c..2bb3f0fac7 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -599,7 +599,7 @@ ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, E if (opts->do_source) { for (size_t i = 0; i < res.size; i++) { String name = res.items[i].data.string; - (void)do_source(name.data, false, DOSO_NONE, NULL); + do_source(name.data, false, DOSO_NONE, NULL); } } diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index 6f3f0127d1..953bd80b0f 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -231,8 +231,8 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err) TRY_WRAP(err, { // call_func() retval is deceptive, ignore it. Instead we set `msg_list` // (see above) to capture abort-causing non-exception errors. - (void)call_func(fn.data, (int)fn.size, &rettv, (int)args.size, - vim_args, &funcexe); + call_func(fn.data, (int)fn.size, &rettv, (int)args.size, + vim_args, &funcexe); }); if (!ERROR_SET(err)) { -- cgit From 3faace199583f3a36470b717ce3e386e476f3a30 Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Thu, 7 Dec 2023 21:01:50 +0900 Subject: docs: clarify on_bytes arguments based on extmark_splice doc --- src/nvim/api/buffer.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index bbb6b92986..3cb1512bb6 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -126,11 +126,13 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err) /// - start column of the changed text /// - byte offset of the changed text (from the start of /// the buffer) -/// - old end row of the changed text +/// - old end row of the changed text (offset from start row) /// - old end column of the changed text +/// (if old end row = 0, offset from start column) /// - old end byte length of the changed text -/// - new end row of the changed text +/// - new end row of the changed text (offset from start row) /// - new end column of the changed text +/// (if new end row = 0, offset from start column) /// - new end byte length of the changed text /// - on_changedtick: Lua callback invoked on changedtick /// increment without text change. Args: -- cgit From 3f35c69b23d1bc951f158e4e3d8e554f89bd1037 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 2 Jan 2024 12:33:41 -0800 Subject: refactor(api): redundant `ml_mfp` check #26059 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit buf_ensure_loaded already checks `(buf->b_ml.ml_mfp != NULL)`. #25823 TODO: - #10070 #13201 All buffer-related API functions except `nvim_buf_is_loaded` (and `nvim_buf_is_valid`?) should always call `buf_ensure_loaded`. Because the _common case_ is that plugins expect the buffer to "just work"—and for the uncomon, performance-sensitive case, the script can check `nvim_buf_is_loaded` to avoid implicitly loading a buffer. - Update documentation to clarify the above semantics. --- src/nvim/api/buffer.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 3cb1512bb6..c07e760469 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -335,12 +335,10 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ return; } - // load buffer first if it's not loaded - if (buf->b_ml.ml_mfp == NULL) { - if (!buf_ensure_loaded(buf)) { - api_set_error(err, kErrorTypeException, "Failed to load buffer"); - return; - } + // Load buffer if necessary. #22670 + if (!buf_ensure_loaded(buf)) { + api_set_error(err, kErrorTypeException, "Failed to load buffer"); + return; } bool oob = false; @@ -517,12 +515,10 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In return; } - // load buffer first if it's not loaded - if (buf->b_ml.ml_mfp == NULL) { - if (!buf_ensure_loaded(buf)) { - api_set_error(err, kErrorTypeException, "Failed to load buffer"); - return; - } + // Load buffer if necessary. #22670 + if (!buf_ensure_loaded(buf)) { + api_set_error(err, kErrorTypeException, "Failed to load buffer"); + return; } bool oob = false; -- cgit From dc48a98f9ac614dc94739637c967aa29e064807e Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Mon, 25 Dec 2023 02:31:47 +0900 Subject: fix(decorations): validate botline for on_win Problem: Many decoration providers (treesitter injection highlighting, semantic token highlighting, inlay hint) rely on the correctness of the `botline` argument of `on_win` callback. However, `botline` can be smaller than the actual line number of the last displayed line if some lines are folded. In such cases, some decorations will be missing in the lines not covered by `botline`. Solution: Validate `botline` when invoking `on_win`. NOTE: It seems that the old code was deliberately avoiding this presumably due to performance reasons. However, I haven't experienced noticeable lag after this change, and I believe the cost of botline computation would be much smaller than the cost of decoration providers. --- src/nvim/api/extmark.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 0cf16294b6..e66e25e527 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -1009,10 +1009,8 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start, /// - on_buf: called for each buffer being redrawn (before /// window callbacks) /// ["buf", bufnr, tick] -/// - on_win: called when starting to redraw a -/// specific window. botline_guess is an approximation -/// that does not exceed the last line number. -/// ["win", winid, bufnr, topline, botline_guess] +/// - on_win: called when starting to redraw a specific window. +/// ["win", winid, bufnr, topline, botline] /// - on_line: called for each buffer line being redrawn. /// (The interaction with fold lines is subject to change) /// ["win", winid, bufnr, row] -- cgit From aeb053907d2f27713764e345b00a6618e23220d8 Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 3 Jan 2024 13:31:39 +0100 Subject: refactor(options): use schar_T representation for fillchars and listchars A bit big, but practically it was a lot simpler to change over all fillchars and all listchars at once, to not need to maintain two parallel implementations. This is mostly an internal refactor, but it also removes an arbitrary limitation: that 'fillchars' and 'listchars' values can only be single-codepoint characters. Now any character which fits into a single screen cell can be used. --- src/nvim/api/ui.c | 26 +++++++++++++------------- src/nvim/api/vim.c | 22 ++++++++-------------- 2 files changed, 21 insertions(+), 27 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 82d42d652d..271e58b851 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -41,7 +41,7 @@ static PMap(uint64_t) connected_uis = MAP_INIT; -#define mpack_w(b, byte) *(*b)++ = (char)(byte); +#define mpack_w(b, byte) *(*(b))++ = (char)(byte); static void mpack_w2(char **b, uint32_t v) { *(*b)++ = (char)((v >> 8) & 0xff); @@ -98,10 +98,9 @@ static char *mpack_array_dyn16(char **buf) return pos; } -static void mpack_str(char **buf, const char *str) +static void mpack_str(char **buf, const char *str, size_t len) { assert(sizeof(schar_T) - 1 < 0x20); - size_t len = strlen(str); mpack_w(buf, 0xa0 | len); memcpy(*buf, str, len); *buf += len; @@ -566,7 +565,7 @@ static void flush_event(UIData *data) // [2, "redraw", [...]] mpack_array(buf, 3); mpack_uint(buf, 2); - mpack_str(buf, "redraw"); + mpack_str(buf, S_LEN("redraw")); data->nevents_pos = mpack_array_dyn16(buf); } } @@ -607,7 +606,7 @@ static bool prepare_call(UI *ui, const char *name) data->cur_event = name; char **buf = &data->buf_wptr; data->ncalls_pos = mpack_array_dyn16(buf); - mpack_str(buf, name); + mpack_str(buf, name, strlen(name)); data->nevents++; data->ncalls = 1; return true; @@ -640,17 +639,18 @@ static void push_call(UI *ui, const char *name, Array args) remote_ui_flush_buf(ui); } - if (data->pack_totlen > UI_BUF_SIZE - strlen(name) - 20) { + size_t name_len = strlen(name); + if (data->pack_totlen > UI_BUF_SIZE - name_len - 20) { // TODO(bfredl): manually testable by setting UI_BUF_SIZE to 1024 (mode_info_set) - data->temp_buf = xmalloc(20 + strlen(name) + data->pack_totlen); + data->temp_buf = xmalloc(20 + name_len + data->pack_totlen); data->buf_wptr = data->temp_buf; char **buf = &data->buf_wptr; mpack_array(buf, 3); mpack_uint(buf, 2); - mpack_str(buf, "redraw"); + mpack_str(buf, S_LEN("redraw")); mpack_array(buf, 1); mpack_array(buf, 2); - mpack_str(buf, name); + mpack_str(buf, name, name_len); } else { prepare_call(ui, name); } @@ -895,9 +895,9 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int uint32_t csize = (repeat > 1) ? 3 : ((attrs[i] != last_hl) ? 2 : 1); nelem++; mpack_array(buf, csize); - char sc_buf[MAX_SCHAR_SIZE]; - schar_get(sc_buf, chunk[i]); - mpack_str(buf, sc_buf); + char *size_byte = (*buf)++; + size_t len = schar_get_adv(buf, chunk[i]); + *size_byte = (char)(0xa0 | len); if (csize >= 2) { mpack_uint(buf, (uint32_t)attrs[i]); if (csize >= 3) { @@ -916,7 +916,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int nelem++; data->ncells_pending += 1; mpack_array(buf, 3); - mpack_str(buf, " "); + mpack_str(buf, S_LEN(" ")); mpack_uint(buf, (uint32_t)clearattr); mpack_uint(buf, (uint32_t)(clearcol - endcol)); } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 2bb3f0fac7..f683789945 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2132,7 +2132,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * Dictionary result = ARRAY_DICT_INIT; int maxwidth; - int fillchar = 0; + schar_T fillchar = 0; int statuscol_lnum = 0; Window window = 0; @@ -2148,11 +2148,13 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * } if (HAS_KEY(opts, eval_statusline, fillchar)) { VALIDATE_EXP((*opts->fillchar.data != 0 - && ((size_t)utf_ptr2len(opts->fillchar.data) == opts->fillchar.size)), + && ((size_t)utfc_ptr2len(opts->fillchar.data) == opts->fillchar.size)), "fillchar", "single character", NULL, { return result; }); - fillchar = utf_ptr2char(opts->fillchar.data); + int c; + fillchar = utfc_ptr2schar(opts->fillchar.data, &c); + // TODO(bfredl): actually check c is single width } int use_bools = (int)opts->use_winbar + (int)opts->use_tabline; @@ -2181,7 +2183,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * SignTextAttrs sattrs[SIGN_SHOW_MAX] = { 0 }; if (opts->use_tabline) { - fillchar = ' '; + fillchar = schar_from_ascii(' '); } else { if (fillchar == 0) { if (opts->use_winbar) { @@ -2242,16 +2244,8 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * int p_crb_save = wp->w_p_crb; wp->w_p_crb = false; - int width = build_stl_str_hl(wp, - buf, - sizeof(buf), - str.data, - -1, - 0, - fillchar, - maxwidth, - opts->highlights ? &hltab : NULL, - NULL, + int width = build_stl_str_hl(wp, buf, sizeof(buf), str.data, -1, 0, fillchar, maxwidth, + opts->highlights ? &hltab : NULL, NULL, statuscol_lnum ? &statuscol : NULL); PUT(result, "width", INTEGER_OBJ(width)); -- cgit From 1813661a6197c76ea6621284570aca1d56597099 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Thu, 4 Jan 2024 15:38:16 +0100 Subject: refactor(IWYU): fix headers Remove `export` pramgas from defs headers as it causes IWYU to believe that the definitions from the defs headers comes from main header, which is not what we really want. --- src/nvim/api/autocmd.c | 3 +++ src/nvim/api/buffer.c | 7 +++++++ src/nvim/api/command.c | 4 +++- src/nvim/api/deprecated.c | 4 +++- src/nvim/api/extmark.c | 1 + src/nvim/api/options.c | 4 ++-- src/nvim/api/private/converter.h | 2 +- src/nvim/api/private/dispatch.h | 2 +- src/nvim/api/private/helpers.c | 4 +++- src/nvim/api/private/helpers.h | 2 +- src/nvim/api/private/validate.h | 2 +- src/nvim/api/ui.c | 6 ++++++ src/nvim/api/vim.c | 15 ++++++++++++++- src/nvim/api/vimscript.c | 3 +++ src/nvim/api/win_config.c | 5 ++++- src/nvim/api/window.c | 4 ++-- 16 files changed, 55 insertions(+), 13 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 88d7d985e8..78628c4154 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -14,8 +14,11 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/private/validate.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/globals.h" #include "nvim/lua/executor.h" diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index c07e760469..8a091f39e4 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -17,25 +17,32 @@ #include "nvim/api/private/validate.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/buffer_updates.h" #include "nvim/change.h" #include "nvim/cursor.h" #include "nvim/drawscreen.h" #include "nvim/ex_cmds.h" #include "nvim/extmark.h" +#include "nvim/extmark_defs.h" #include "nvim/globals.h" #include "nvim/lua/executor.h" #include "nvim/mapping.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/memline.h" +#include "nvim/memline_defs.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/move.h" #include "nvim/ops.h" #include "nvim/pos_defs.h" #include "nvim/state_defs.h" #include "nvim/types_defs.h" #include "nvim/undo.h" +#include "nvim/undo_defs.h" #include "nvim/vim_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index abb589ecdf..db0a918f5c 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -13,12 +13,14 @@ #include "nvim/api/private/validate.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer_defs.h" #include "nvim/cmdexpand_defs.h" -#include "nvim/ex_cmds.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/globals.h" #include "nvim/lua/executor.h" #include "nvim/macros_defs.h" diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index f7bbd61cd3..b09645a819 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -6,20 +6,22 @@ #include "nvim/api/deprecated.h" #include "nvim/api/extmark.h" #include "nvim/api/keysets_defs.h" -#include "nvim/api/options.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/api/private/validate.h" #include "nvim/api/vimscript.h" #include "nvim/buffer_defs.h" #include "nvim/decoration.h" +#include "nvim/decoration_defs.h" #include "nvim/extmark.h" #include "nvim/globals.h" #include "nvim/highlight.h" #include "nvim/highlight_group.h" #include "nvim/lua/executor.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/pos_defs.h" #include "nvim/types_defs.h" diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index e66e25e527..9dec4a6c70 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -20,6 +20,7 @@ #include "nvim/grid.h" #include "nvim/highlight_group.h" #include "nvim/marktree.h" +#include "nvim/marktree_defs.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index ff1741558d..fce4a85804 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -9,14 +9,14 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/private/validate.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/globals.h" -#include "nvim/macros_defs.h" #include "nvim/memory.h" #include "nvim/option.h" #include "nvim/types_defs.h" #include "nvim/vim_defs.h" -#include "nvim/window.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/options.c.generated.h" diff --git a/src/nvim/api/private/converter.h b/src/nvim/api/private/converter.h index a5acc56c7c..fc82abf332 100644 --- a/src/nvim/api/private/converter.h +++ b/src/nvim/api/private/converter.h @@ -1,6 +1,6 @@ #pragma once -#include "nvim/api/private/defs.h" // IWYU pragma: export +#include "nvim/api/private/defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/api/private/dispatch.h b/src/nvim/api/private/dispatch.h index 9b167a7b9e..6d051176ac 100644 --- a/src/nvim/api/private/dispatch.h +++ b/src/nvim/api/private/dispatch.h @@ -3,7 +3,7 @@ #include #include -#include "nvim/api/private/defs.h" // IWYU pragma: export +#include "nvim/api/private/defs.h" // IWYU pragma: keep #include "nvim/memory_defs.h" #include "nvim/types_defs.h" diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 06addbecb7..677e1ce9fd 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -19,7 +19,7 @@ #include "nvim/eval/typval.h" #include "nvim/eval/vars.h" #include "nvim/ex_eval.h" -#include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/globals.h" #include "nvim/highlight_group.h" #include "nvim/lua/executor.h" @@ -27,11 +27,13 @@ #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/message.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/pos_defs.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/version.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 1ee66f906b..8a56d1704f 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -4,7 +4,7 @@ #include #include "klib/kvec.h" -#include "nvim/api/private/defs.h" // IWYU pragma: export +#include "nvim/api/private/defs.h" // IWYU pragma: keep #include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/ex_eval_defs.h" diff --git a/src/nvim/api/private/validate.h b/src/nvim/api/private/validate.h index 692ea46176..2c1d1a241d 100644 --- a/src/nvim/api/private/validate.h +++ b/src/nvim/api/private/validate.h @@ -3,7 +3,7 @@ #include #include -#include "nvim/api/private/defs.h" // IWYU pragma: export +#include "nvim/api/private/defs.h" // IWYU pragma: keep #include "nvim/api/private/helpers.h" #include "nvim/assert_defs.h" #include "nvim/macros_defs.h" diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 271e58b851..b42c274411 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -13,20 +13,26 @@ #include "nvim/api/private/validate.h" #include "nvim/api/ui.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/channel.h" +#include "nvim/channel_defs.h" #include "nvim/eval.h" +#include "nvim/event/defs.h" #include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" #include "nvim/event/wstream.h" #include "nvim/globals.h" #include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/highlight.h" #include "nvim/macros_defs.h" #include "nvim/main.h" #include "nvim/map_defs.h" #include "nvim/mbyte.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/msgpack_rpc/channel.h" +#include "nvim/msgpack_rpc/channel_defs.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/option.h" #include "nvim/types_defs.h" diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index f683789945..e5a5cc059f 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -21,20 +21,26 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/channel.h" +#include "nvim/channel_defs.h" #include "nvim/context.h" #include "nvim/cursor.h" #include "nvim/decoration.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/fold.h" #include "nvim/getchar.h" +#include "nvim/getchar_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/keycodes.h" #include "nvim/log.h" @@ -42,15 +48,20 @@ #include "nvim/macros_defs.h" #include "nvim/mapping.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/message.h" +#include "nvim/message_defs.h" #include "nvim/move.h" #include "nvim/msgpack_rpc/channel.h" +#include "nvim/msgpack_rpc/channel_defs.h" #include "nvim/msgpack_rpc/unpacker.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/input.h" @@ -59,9 +70,11 @@ #include "nvim/popupmenu.h" #include "nvim/pos_defs.h" #include "nvim/runtime.h" -#include "nvim/sign.h" +#include "nvim/sign_defs.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/statusline.h" +#include "nvim/statusline_defs.h" #include "nvim/strings.h" #include "nvim/terminal.h" #include "nvim/types_defs.h" diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index 953bd80b0f..56541bb8b8 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -14,15 +14,18 @@ #include "nvim/buffer_defs.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" #include "nvim/ex_docmd.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/globals.h" #include "nvim/memory.h" #include "nvim/runtime.h" #include "nvim/vim_defs.h" #include "nvim/viml/parser/expressions.h" #include "nvim/viml/parser/parser.h" +#include "nvim/viml/parser/parser_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/vimscript.c.generated.h" diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 64eda43689..ccbd341fd2 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -10,11 +10,13 @@ #include "nvim/api/win_config.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer_defs.h" #include "nvim/decoration.h" +#include "nvim/decoration_defs.h" #include "nvim/drawscreen.h" #include "nvim/globals.h" -#include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/highlight_group.h" #include "nvim/macros_defs.h" #include "nvim/mbyte.h" @@ -25,6 +27,7 @@ #include "nvim/syntax.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/window.h" #include "nvim/winfloat.h" diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 7321d52e17..00126c64f1 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -15,10 +15,10 @@ #include "nvim/drawscreen.h" #include "nvim/eval/window.h" #include "nvim/ex_docmd.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/lua/executor.h" -#include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/message.h" #include "nvim/move.h" #include "nvim/plines.h" -- cgit From 4f544b31197e7d543e0fa315948991e830b41ba8 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Sat, 13 Jan 2024 17:09:59 +0100 Subject: refactor(marktree): unpaired marktree_get_alt() returns itself Avoids checking for invalid mark at callsite. --- src/nvim/api/extmark.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 9dec4a6c70..fd07ec96fe 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -151,7 +151,7 @@ static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_na PUT(dict, "right_gravity", BOOLEAN_OBJ(mt_right(start))); - if (extmark.end_pos.row >= 0) { + if (mt_paired(start)) { PUT(dict, "end_row", INTEGER_OBJ(extmark.end_pos.row)); PUT(dict, "end_col", INTEGER_OBJ(extmark.end_pos.col)); PUT(dict, "end_right_gravity", BOOLEAN_OBJ(extmark.end_right_gravity)); -- cgit From 2645c9d576e430e62b60d1902d4a6b887e46a670 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 17 Jan 2024 15:55:00 +0100 Subject: docs(extmarks): add undocumented "details" array fields --- src/nvim/api/extmark.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index fd07ec96fe..d7e1aa2d0f 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -215,8 +215,8 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, return extmark_to_array(extmark, false, details, hl_name); } -/// Gets |extmarks| (including |signs|) in "traversal order" from a |charwise| -/// region defined by buffer positions (inclusive, 0-indexed |api-indexing|). +/// Gets |extmarks| in "traversal order" from a |charwise| region defined by +/// buffer positions (inclusive, 0-indexed |api-indexing|). /// /// Region can be given as (row,col) tuples, or valid extmark ids (whose /// positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1) @@ -234,6 +234,10 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, /// the `overlap` option might be useful. Otherwise only the start position /// of an extmark will be considered. /// +/// Note: legacy signs placed through the |:sign| commands are implemented +/// as extmarks and will show up here. Their details array will contain a +/// `sign_name` field. +/// /// Example: /// /// ```lua @@ -434,7 +438,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// if text around the mark was deleted and then restored by undo. /// Defaults to true. /// - invalidate : boolean that indicates whether to hide the -/// extmark if the entirety of its range is deleted. If +/// extmark if the entirety of its range is deleted. For +/// hidden marks, an "invalid" key is added to the "details" +/// array of |nvim_buf_get_extmarks()| and family. If /// "undo_restore" is false, the extmark is deleted instead. /// - priority: a priority value for the highlight group or sign /// attribute. For example treesitter highlighting uses a -- cgit From d3a8e9217f39c59dd7762bd22a76b8bd03ca85ff Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Fri, 19 Jan 2024 14:51:10 -0600 Subject: feat(ui): add chdir UI event (#27093) When an embedded Nvim instance changes its current directory a "chdir" UI event is emitted. Attached UIs can use this information however they wish. In the TUI it is used to synchronize the cwd of the TUI process with the cwd of the embedded Nvim process. --- src/nvim/api/ui_events.in.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nvim/api') diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index bda0c72423..c2f02c34f8 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -39,6 +39,8 @@ void screenshot(String path) FUNC_API_SINCE(7); void option_set(String name, Object value) FUNC_API_SINCE(4); +void chdir(String path) + FUNC_API_SINCE(12); // Stop event is not exported as such, represented by EOF in the msgpack stream. void stop(void) FUNC_API_NOEXPORT; -- cgit From 646fdf1073433e2bdeec3433f6cbdf8f4be37098 Mon Sep 17 00:00:00 2001 From: glepnir Date: Wed, 17 Jan 2024 20:14:26 +0800 Subject: refactor(api): use hl id directly in nvim_buf_set_extmark --- src/nvim/api/extmark.c | 38 +++++++++----------------------------- src/nvim/api/keysets_defs.h | 10 +++++----- src/nvim/api/private/defs.h | 1 + src/nvim/api/private/helpers.c | 21 +++++++++++++++++---- 4 files changed, 32 insertions(+), 38 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index fd07ec96fe..4e4befee1d 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -14,6 +14,7 @@ #include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/decoration.h" +#include "nvim/decoration_defs.h" #include "nvim/decoration_provider.h" #include "nvim/drawscreen.h" #include "nvim/extmark.h" @@ -540,36 +541,15 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer col2 = (int)val; } - // uncrustify:off + hl.hl_id = (int)opts->hl_group; + has_hl = hl.hl_id > 0; + sign.hl_id = (int)opts->sign_hl_group; + sign.cursorline_hl_id = (int)opts->cursorline_hl_group; + sign.number_hl_id = (int)opts->number_hl_group; + sign.line_hl_id = (int)opts->line_hl_group; - // TODO(bfredl): keyset type alias for hl_group? (nil|int|string) - struct { - const char *name; - Object *opt; - int *dest; - } hls[] = { - { "hl_group" , &opts->hl_group , &hl.hl_id }, - { "sign_hl_group" , &opts->sign_hl_group , &sign.hl_id }, - { "number_hl_group" , &opts->number_hl_group , &sign.number_hl_id }, - { "line_hl_group" , &opts->line_hl_group , &sign.line_hl_id }, - { "cursorline_hl_group", &opts->cursorline_hl_group, &sign.cursorline_hl_id }, - { NULL, NULL, NULL }, - }; - - // uncrustify:on - - for (int j = 0; hls[j].name && hls[j].dest; j++) { - if (hls[j].opt->type != kObjectTypeNil) { - if (j > 0) { - sign.flags |= kSHIsSign; - } else { - has_hl = true; - } - *hls[j].dest = object_to_hl_id(*hls[j].opt, hls[j].name, err); - if (ERROR_SET(err)) { - goto error; - } - } + if (sign.hl_id || sign.cursorline_hl_id || sign.number_hl_id || sign.line_hl_id) { + sign.flags |= kSHIsSign; } if (HAS_KEY(opts, set_extmark, conceal)) { diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index 897d546f3d..aad8ba3238 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -28,7 +28,7 @@ typedef struct { Integer end_line; Integer end_row; Integer end_col; - Object hl_group; + Integer hl_group; Array virt_text; String virt_text_pos; Integer virt_text_win_col; @@ -46,10 +46,10 @@ typedef struct { Boolean virt_lines_leftcol; Boolean strict; String sign_text; - Object sign_hl_group; - Object number_hl_group; - Object line_hl_group; - Object cursorline_hl_group; + Integer sign_hl_group; + Integer number_hl_group; + Integer line_hl_group; + Integer cursorline_hl_group; String conceal; Boolean spell; Boolean ui_watched; diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 25c8377518..84ded1e647 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -135,6 +135,7 @@ typedef struct { size_t ptr_off; ObjectType type; // kObjectTypeNil == untyped int opt_index; + bool is_hlgroup; } KeySetLink; typedef KeySetLink *(*FieldHashfn)(const char *str, size_t len); diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 677e1ce9fd..8f30ac7c8f 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -936,13 +936,26 @@ bool api_dict_to_keydict(void *retval, FieldHashfn hashy, Dictionary dict, Error char *mem = ((char *)retval + field->ptr_off); Object *value = &dict.items[i].value; + if (field->type == kObjectTypeNil) { *(Object *)mem = *value; } else if (field->type == kObjectTypeInteger) { - VALIDATE_T(field->str, kObjectTypeInteger, value->type, { - return false; - }); - *(Integer *)mem = value->data.integer; + if (field->is_hlgroup) { + int hl_id = 0; + if (value->type != kObjectTypeNil) { + hl_id = object_to_hl_id(*value, k.data, err); + if (ERROR_SET(err)) { + return false; + } + } + *(Integer *)mem = hl_id; + } else { + VALIDATE_T(field->str, kObjectTypeInteger, value->type, { + return false; + }); + + *(Integer *)mem = value->data.integer; + } } else if (field->type == kObjectTypeFloat) { Float *val = (Float *)mem; if (value->type == kObjectTypeInteger) { -- cgit From d66ed4ea468d411668713c3777ad3658f18badf3 Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 22 Jan 2024 08:49:45 +0100 Subject: refactor(api): give "hl_group" more accurate _meta type These can either be number or string in lua, so we can specify this directly as "number|string". --- src/nvim/api/keysets_defs.h | 10 +++++----- src/nvim/api/private/defs.h | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index aad8ba3238..b2f0039eb9 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -28,7 +28,7 @@ typedef struct { Integer end_line; Integer end_row; Integer end_col; - Integer hl_group; + HLGroupID hl_group; Array virt_text; String virt_text_pos; Integer virt_text_win_col; @@ -46,10 +46,10 @@ typedef struct { Boolean virt_lines_leftcol; Boolean strict; String sign_text; - Integer sign_hl_group; - Integer number_hl_group; - Integer line_hl_group; - Integer cursorline_hl_group; + HLGroupID sign_hl_group; + HLGroupID number_hl_group; + HLGroupID line_hl_group; + HLGroupID cursorline_hl_group; String conceal; Boolean spell; Boolean ui_watched; diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 84ded1e647..0cdc90e50f 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -124,6 +124,7 @@ struct key_value_pair { }; typedef uint64_t OptionalKeys; +typedef Integer HLGroupID; // this is the prefix of all keysets with optional keys typedef struct { -- cgit From 9af2be292db3db7b28a6210263f719a6bbc4059f Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 12 Jan 2024 14:38:18 +0100 Subject: perf(extmarks): add metadata for efficient filtering of special decorations This expands on the global "don't pay for what you don't use" rules for these special extmark decorations: - inline virtual text, which needs to be processed in plines.c when we calculate the size of text on screen - virtual lines, which are needed when calculating "filler" lines - signs, with text and/or highlights, both of which needs to be processed for the entire line already at the beginning of a line. This adds a count to each node of the marktree, for how many special marks of each kind can be found in the subtree for this node. This makes it possible to quickly skip over these extra checks, when working in regions of the buffer not containing these kind of marks, instead of before where this could just be skipped if the entire _buffer_ didn't contain such marks. --- src/nvim/api/buffer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 8a091f39e4..7e79087f10 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -32,6 +32,7 @@ #include "nvim/mapping.h" #include "nvim/mark.h" #include "nvim/mark_defs.h" +#include "nvim/marktree.h" #include "nvim/memline.h" #include "nvim/memline_defs.h" #include "nvim/memory.h" @@ -1282,7 +1283,7 @@ Dictionary nvim__buf_stats(Buffer buffer, Error *err) // this exists to debug issues PUT(rv, "dirty_bytes", INTEGER_OBJ((Integer)buf->deleted_bytes)); PUT(rv, "dirty_bytes2", INTEGER_OBJ((Integer)buf->deleted_bytes2)); - PUT(rv, "virt_blocks", INTEGER_OBJ((Integer)buf->b_virt_line_blocks)); + PUT(rv, "virt_blocks", INTEGER_OBJ((Integer)buf_meta_total(buf, kMTMetaLines))); u_header_T *uhp = NULL; if (buf->b_u_curhead != NULL) { -- cgit From 6ea6b3fee27d51607ca4a5ace46dbc38a4481bcb Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Wed, 24 Jan 2024 16:36:25 -0600 Subject: feat(ui): add support for OSC 8 hyperlinks (#27109) Extmarks can contain URLs which can then be drawn in any supporting UI. In the TUI, for example, URLs are "drawn" by emitting the OSC 8 control sequence to the TTY. On terminals which support the OSC 8 sequence this will create clickable hyperlinks. URLs are treated as inline highlights in the decoration subsystem, so are included in the `DecorSignHighlight` structure. However, unlike other inline highlights they use allocated memory which must be freed, so they set the `ext` flag in `DecorInline` so that their lifetimes are managed along with other allocated memory like virtual text. The decoration subsystem then adds the URLs as a new highlight attribute. The highlight subsystem maintains a set of unique URLs to avoid duplicating allocations for the same string. To attach a URL to an existing highlight attribute we call `hl_add_url` which finds the URL in the set (allocating and adding it if it does not exist) and sets the `url` highlight attribute to the index of the URL in the set (using an index helps keep the size of the `HlAttrs` struct small). This has the potential to lead to an increase in highlight attributes if a URL is used over a range that contains many different highlight attributes, because now each existing attribute must be combined with the URL. In practice, however, URLs typically span a range containing a single highlight (e.g. link text in Markdown), so this is likely just a pathological edge case. When a new highlight attribute is defined with a URL it is copied to all attached UIs with the `hl_attr_define` UI event. The TUI manages its own set of URLs (just like the highlight subsystem) to minimize allocations. The TUI keeps track of which URL is "active" for the cell it is printing. If no URL is active and a cell containing a URL is printed, the opening OSC 8 sequence is emitted and that URL becomes the actively tracked URL. If the cursor is moved while in the middle of a URL span, we emit the terminating OSC sequence to prevent the hyperlink from spanning multiple lines. This does not support nested hyperlinks, but that is a rare (and, frankly, bizarre) use case. If a valid use case for nested hyperlinks ever presents itself we can address that issue then. --- src/nvim/api/extmark.c | 23 +++++++++++++++++++++++ src/nvim/api/keysets_defs.h | 2 ++ src/nvim/api/ui.c | 8 ++++++++ src/nvim/api/vim.c | 6 ++++++ 4 files changed, 39 insertions(+) (limited to 'src/nvim/api') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 1f0e867162..27a4b7854f 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -481,6 +481,8 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// by a UI. When set, the UI will receive win_extmark events. /// Note: the mark is positioned by virt_text attributes. Can be /// used together with virt_text. +/// - url: A URL to associate with this extmark. In the TUI, the OSC 8 control +/// sequence is used to generate a clickable hyperlink to this URL. /// /// @param[out] err Error details, if any /// @return Id of the created/updated extmark @@ -494,6 +496,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer DecorSignHighlight sign = DECOR_SIGN_HIGHLIGHT_INIT; DecorVirtText virt_text = DECOR_VIRT_TEXT_INIT; DecorVirtText virt_lines = DECOR_VIRT_LINES_INIT; + char *url = NULL; bool has_hl = false; buf_T *buf = find_buffer_by_handle(buffer, err); @@ -678,6 +681,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer has_hl = true; } + if (HAS_KEY(opts, set_extmark, url)) { + url = string_to_cstr(opts->url); + } + if (opts->ui_watched) { hl.flags |= kSHUIWatched; if (virt_text.pos == kVPosOverlay) { @@ -747,6 +754,11 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer if (kv_size(virt_lines.data.virt_lines)) { decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_lines, NULL), true); } + if (url != NULL) { + DecorSignHighlight sh = DECOR_SIGN_HIGHLIGHT_INIT; + sh.url = url; + decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, 0, 0); + } if (has_hl) { DecorSignHighlight sh = decor_sh_from_inline(hl); decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, (uint32_t)ns_id, id); @@ -772,7 +784,14 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } uint32_t decor_indexed = DECOR_ID_INVALID; + if (url != NULL) { + DecorSignHighlight sh = DECOR_SIGN_HIGHLIGHT_INIT; + sh.url = url; + sh.next = decor_indexed; + decor_indexed = decor_put_sh(sh); + } if (sign.flags & kSHIsSign) { + sign.next = decor_indexed; decor_indexed = decor_put_sh(sign); if (sign.text[0]) { decor_flags |= MT_FLAG_DECOR_SIGNTEXT; @@ -814,6 +833,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer error: clear_virttext(&virt_text.data.virt_text); clear_virtlines(&virt_lines.data.virt_lines); + if (url != NULL) { + xfree(url); + } + return 0; } diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index b2f0039eb9..811f60f4d6 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -54,6 +54,7 @@ typedef struct { Boolean spell; Boolean ui_watched; Boolean undo_restore; + String url; } Dict(set_extmark); typedef struct { @@ -183,6 +184,7 @@ typedef struct { Boolean fg_indexed; Boolean bg_indexed; Boolean force; + String url; } Dict(highlight); typedef struct { diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index b42c274411..f955b315a8 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -784,6 +784,14 @@ void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cte MAXSIZE_TEMP_DICT(cterm, HLATTRS_DICT_SIZE); hlattrs2dict(&rgb, NULL, rgb_attrs, true, false); hlattrs2dict(&cterm, NULL, rgb_attrs, false, false); + + // URLs are not added in hlattrs2dict since they are used only by UIs and not by the highlight + // system. So we add them here. + if (rgb_attrs.url >= 0) { + const char *url = hl_get_url((uint32_t)rgb_attrs.url); + PUT_C(rgb, "url", STRING_OBJ(cstr_as_string((char *)url))); + } + ADD_C(args, DICTIONARY_OBJ(rgb)); ADD_C(args, DICTIONARY_OBJ(cterm)); diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index e5a5cc059f..eea9b54a5c 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -176,6 +176,12 @@ void nvim_set_hl(Integer ns_id, String name, Dict(highlight) *val, Error *err) }); int link_id = -1; + // Setting URLs directly through highlight attributes is not supported + if (HAS_KEY(val, highlight, url)) { + api_free_string(val->url); + val->url = NULL_STRING; + } + HlAttrs attrs = dict2hlattrs(val, true, &link_id, err); if (!ERROR_SET(err)) { ns_hl_def((NS)ns_id, hl_id, attrs, link_id, val); -- cgit From 4e59422e1d4950a3042bad41a7b81c8db4f8b648 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 25 Jan 2024 07:57:21 +0800 Subject: refactor: IWYU (#27186) --- src/nvim/api/buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 7e79087f10..7af2b7241c 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -32,7 +32,7 @@ #include "nvim/mapping.h" #include "nvim/mark.h" #include "nvim/mark_defs.h" -#include "nvim/marktree.h" +#include "nvim/marktree_defs.h" #include "nvim/memline.h" #include "nvim/memline_defs.h" #include "nvim/memory.h" -- cgit From 7367838359bfb5fadf72ea2aeea2f84efb34590e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 27 Jan 2024 14:03:44 +0800 Subject: fix(api): limit depth of nvim_cmd (#27225) --- src/nvim/api/private/helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 8f30ac7c8f..9ce1786fa0 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -151,7 +151,7 @@ bool try_end(Error *err) if (should_free) { xfree(msg); } - } else if (did_throw) { + } else if (did_throw || need_rethrow) { if (*current_exception->throw_name != NUL) { if (current_exception->throw_lnum != 0) { api_set_error(err, kErrorTypeException, "%s, line %" PRIdLINENR ": %s", -- cgit From cf140fb25b94c556396fe942a4af3e8db9effa37 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Thu, 27 Jul 2023 01:38:23 +0100 Subject: vim-patch:9.1.0047: issues with temp curwin/buf while cmdwin is open Problem: Things that temporarily change/restore curwin/buf (e.g: win_execute, some autocmds) may break assumptions that curwin/buf is the cmdwin when "cmdwin_type != 0", causing issues. Solution: Expose the cmdwin's real win/buf and check that instead. Also try to ensure these variables are NULL if "cmdwin_type == 0", allowing them to be used directly in most cases without checking cmdwin_type. (Sean Dewar) Reset and save `cmdwin_old_curwin` in a similar fashion. Apply suitable changes for API functions and add Lua tests. https://github.com/vim/vim/commit/988f74311c26ea9917e84fbae608de226dba7e5f --- src/nvim/api/vim.c | 2 +- src/nvim/api/win_config.c | 2 +- src/nvim/api/window.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index eea9b54a5c..4bd8f551db 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1022,7 +1022,7 @@ Integer nvim_open_term(Buffer buffer, Dict(open_term) *opts, Error *err) return 0; } - if (cmdwin_type != 0 && buf == curbuf) { + if (buf == cmdwin_buf) { api_set_error(err, kErrorTypeException, "%s", e_cmdwin); return 0; } diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index ccbd341fd2..6df86683c1 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -181,7 +181,7 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E if (!buf) { return 0; } - if (cmdwin_type != 0 && (enter || buf == curbuf)) { + if ((cmdwin_type != 0 && enter) || buf == cmdwin_buf) { api_set_error(err, kErrorTypeException, "%s", e_cmdwin); return 0; } diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 00126c64f1..4ac7e47832 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -61,7 +61,7 @@ void nvim_win_set_buf(Window window, Buffer buffer, Error *err) if (!win || !buf) { return; } - if (cmdwin_type != 0 && (win == curwin || win == cmdwin_old_curwin || buf == curbuf)) { + if (win == cmdwin_win || win == cmdwin_old_curwin || buf == cmdwin_buf) { api_set_error(err, kErrorTypeException, "%s", e_cmdwin); return; } -- cgit From 15e77a56b711102fdc123e15b3f37d49bc0b1df1 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Sun, 28 Jan 2024 21:13:58 -0600 Subject: feat(extmarks): subpriorities (relative to declaration order) (#27131) The "priority" field of extmarks can be used to set priorities of extmarks which dictates which highlight group a range will actually have when there are multiple extmarks applied. However, when multiple extmarks have the same priority, the only way to enforce an actual priority is through the order in which the extmarks are set. It is not always possible or desirable to set extmarks in a specific order, however, so we add a new "subpriority" field that explicitly enforces the ordering of extmarks that have the same priority. For now this will be used only to enforce priority of treesitter highlights. A single node in a treesitter tree may match multiple captures, in which case that node will have multiple extmarks set. The order in which captures are returned from the treesitter API is not _necessarily_ in the same order they are defined in a query file, so we use the new subpriority field to force that ordering. For now subpriorites are not documented and are not meant to be used by external code, and it only applies to ephemeral extmarks. We indicate the "private" nature of subpriorities by prefixing the field name with an "_". --- src/nvim/api/extmark.c | 20 ++++++++++++++++---- src/nvim/api/keysets_defs.h | 2 ++ 2 files changed, 18 insertions(+), 4 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 27a4b7854f..4e84b41a02 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -748,20 +748,32 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer col2 = c; } + DecorPriority subpriority = DECOR_PRIORITY_BASE; + if (HAS_KEY(opts, set_extmark, _subpriority)) { + VALIDATE_RANGE((opts->_subpriority >= 0 && opts->_subpriority <= UINT16_MAX), + "_subpriority", { + goto error; + }); + subpriority = (DecorPriority)opts->_subpriority; + } + if (kv_size(virt_text.data.virt_text)) { - decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_text, NULL), true); + decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_text, NULL), true, + subpriority); } if (kv_size(virt_lines.data.virt_lines)) { - decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_lines, NULL), true); + decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_lines, NULL), true, + subpriority); } if (url != NULL) { DecorSignHighlight sh = DECOR_SIGN_HIGHLIGHT_INIT; sh.url = url; - decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, 0, 0); + decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, 0, 0, subpriority); } if (has_hl) { DecorSignHighlight sh = decor_sh_from_inline(hl); - decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, (uint32_t)ns_id, id); + decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, (uint32_t)ns_id, id, + subpriority); } } else { if (opts->ephemeral) { diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index 811f60f4d6..85896c1fa7 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -55,6 +55,8 @@ typedef struct { Boolean ui_watched; Boolean undo_restore; String url; + + Integer _subpriority; } Dict(set_extmark); typedef struct { -- cgit From ca9f6f56949d66f0f6467fa64b215f861fe0a3bf Mon Sep 17 00:00:00 2001 From: Will Hopkins Date: Sun, 28 Jan 2024 23:18:33 -0800 Subject: feat(api): add nvim_tabpage_set_win (#27222) Allows setting the current window of a non-current tabpage without switching tabpages. --- src/nvim/api/tabpage.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'src/nvim/api') diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c index 303f2ca817..fadc03b3e5 100644 --- a/src/nvim/api/tabpage.c +++ b/src/nvim/api/tabpage.c @@ -122,6 +122,37 @@ Window nvim_tabpage_get_win(Tabpage tabpage, Error *err) abort(); } +/// Sets the current window in a tabpage +/// +/// @param tabpage Tabpage handle, or 0 for current tabpage +/// @param win Window handle, must already belong to {tabpage} +/// @param[out] err Error details, if any +void nvim_tabpage_set_win(Tabpage tabpage, Window win, Error *err) + FUNC_API_SINCE(12) +{ + tabpage_T *tp = find_tab_by_handle(tabpage, err); + if (!tp) { + return; + } + + win_T *wp = find_window_by_handle(win, err); + if (!wp) { + return; + } + + if (!tabpage_win_valid(tp, wp)) { + api_set_error(err, kErrorTypeException, "Window does not belong to tabpage %d", tp->handle); + return; + } + + if (tp == curtab) { + win_enter(wp, true); + } else if (tp->tp_curwin != wp) { + tp->tp_prevwin = tp->tp_curwin; + tp->tp_curwin = wp; + } +} + /// Gets the tabpage number /// /// @param tabpage Tabpage handle, or 0 for current tabpage -- cgit From 6bba4becedaea5a330c0c9d9427fb495e8092dac Mon Sep 17 00:00:00 2001 From: Will Hopkins Date: Wed, 31 Jan 2024 19:43:35 -0800 Subject: feat(api): make nvim_open_win support non-floating windows (#25550) Adds support to `nvim_open_win` and `nvim_win_set_config` for creating and manipulating split (non-floating) windows. --- src/nvim/api/keysets_defs.h | 2 + src/nvim/api/win_config.c | 454 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 396 insertions(+), 60 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index 85896c1fa7..2f1b38d1e5 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -115,10 +115,12 @@ typedef struct { Integer height; String anchor; String relative; + String split; Window win; Array bufpos; Boolean external; Boolean focusable; + Boolean vertical; Integer zindex; Object border; Object title; diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 6df86683c1..5089367114 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -7,6 +7,7 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/dispatch.h" #include "nvim/api/private/helpers.h" +#include "nvim/api/tabpage.h" #include "nvim/api/win_config.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" @@ -15,6 +16,8 @@ #include "nvim/decoration.h" #include "nvim/decoration_defs.h" #include "nvim/drawscreen.h" +#include "nvim/eval/window.h" +#include "nvim/extmark_defs.h" #include "nvim/globals.h" #include "nvim/grid_defs.h" #include "nvim/highlight_group.h" @@ -22,12 +25,15 @@ #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/option.h" +#include "nvim/option_vars.h" #include "nvim/pos_defs.h" #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_compositor.h" #include "nvim/ui_defs.h" +#include "nvim/vim_defs.h" #include "nvim/window.h" #include "nvim/winfloat.h" @@ -35,9 +41,9 @@ # include "api/win_config.c.generated.h" #endif -/// Open a new window. +/// Opens a new split window, or a floating window if `relative` is specified, +/// or an external window (managed by the UI) if `external` is specified. /// -/// Currently this is used to open floating and external windows. /// Floats are windows that are drawn above the split layout, at some anchor /// position in some other window. Floats can be drawn internally or by external /// GUI with the |ui-multigrid| extension. External windows are only supported @@ -45,8 +51,17 @@ /// /// For a general overview of floats, see |api-floatwin|. /// -/// Exactly one of `external` and `relative` must be specified. The `width` and -/// `height` of the new window must be specified. +/// The `width` and `height` of the new window must be specified when opening +/// a floating window, but are optional for normal windows. +/// +/// If `relative` and `external` are omitted, a normal "split" window is created. +/// The `win` property determines which window will be split. If no `win` is +/// provided or `win == 0`, a window will be created adjacent to the current window. +/// If -1 is provided, a top-level split will be created. `vertical` and `split` are +/// only valid for normal windows, and are used to control split direction. For `vertical`, +/// the exact direction is determined by |'splitright'| and |'splitbelow'|. +/// Split windows cannot have `bufpos`/`row`/`col`/`border`/`title`/`footer` +/// properties. /// /// With relative=editor (row=0,col=0) refers to the top-left corner of the /// screen-grid and (row=Lines-1,col=Columns-1) refers to the bottom-right @@ -73,6 +88,15 @@ /// {relative='win', width=12, height=3, bufpos={100,10}}) /// ``` /// +/// Example (Lua): vertical split left of the current window +/// +/// ```lua +/// vim.api.nvim_open_win(0, false, { +/// split = 'left', +/// win = 0 +/// }) +/// ``` +/// /// @param buffer Buffer to display, or 0 for current buffer /// @param enter Enter the window (make it the current window) /// @param config Map defining the window configuration. Keys: @@ -82,7 +106,8 @@ /// - "win" Window given by the `win` field, or current window. /// - "cursor" Cursor position in current window. /// - "mouse" Mouse position -/// - win: |window-ID| for relative="win". +/// - win: |window-ID| window to split, or relative window when creating a +/// float (relative="win"). /// - anchor: Decides which corner of the float to place at (row,col): /// - "NW" northwest (default) /// - "NE" northeast @@ -169,13 +194,14 @@ /// - fixed: If true when anchor is NW or SW, the float window /// would be kept fixed even if the window would be truncated. /// - hide: If true the floating window will be hidden. +/// - vertical: Split vertically |:vertical|. +/// - split: Split direction: "left", "right", "above", "below". /// /// @param[out] err Error details, if any /// /// @return Window handle, or 0 on error Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, Error *err) - FUNC_API_SINCE(6) - FUNC_API_TEXTLOCK_ALLOW_CMDWIN + FUNC_API_SINCE(6) FUNC_API_TEXTLOCK_ALLOW_CMDWIN { buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { @@ -190,22 +216,67 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E if (!parse_float_config(config, &fconfig, false, true, err)) { return 0; } - win_T *wp = win_new_float(NULL, false, fconfig, err); + + bool is_split = HAS_KEY(config, float_config, split) || HAS_KEY(config, float_config, vertical); + + win_T *wp = NULL; + tabpage_T *tp = curtab; + if (is_split) { + win_T *parent = NULL; + if (!HAS_KEY(config, float_config, win) || config->win != -1) { + parent = find_window_by_handle(fconfig.window, err); + if (!parent) { + // find_window_by_handle has already set the error + return 0; + } else if (parent->w_floating) { + api_set_error(err, kErrorTypeException, "Cannot split a floating window"); + return 0; + } + } + + if (HAS_KEY(config, float_config, vertical) && !HAS_KEY(config, float_config, split)) { + if (config->vertical) { + fconfig.split = p_spr ? kWinSplitRight : kWinSplitLeft; + } else { + fconfig.split = p_sb ? kWinSplitBelow : kWinSplitAbove; + } + } + int flags = win_split_flags(fconfig.split, parent == NULL) | WSP_NOENTER; + + if (parent == NULL) { + wp = win_split_ins(0, flags, NULL, 0); + } else { + tp = win_find_tabpage(parent); + switchwin_T switchwin; + // `parent` is valid in `tp`, so switch_win should not fail. + const int result = switch_win(&switchwin, parent, tp, true); + (void)result; + assert(result == OK); + wp = win_split_ins(0, flags, NULL, 0); + restore_win(&switchwin, true); + } + if (wp) { + wp->w_float_config = fconfig; + } + } else { + wp = win_new_float(NULL, false, fconfig, err); + } if (!wp) { + api_set_error(err, kErrorTypeException, "Failed to create window"); return 0; } + switchwin_T switchwin; + if (switch_win_noblock(&switchwin, wp, tp, true) == OK) { + apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf); + } + restore_win_noblock(&switchwin, true); if (enter) { - win_enter(wp, false); + goto_tabpage_win(tp, wp); } - // autocmds in win_enter or win_set_buf below may close the window - if (win_valid(wp) && buffer > 0) { - Boolean noautocmd = !enter || fconfig.noautocmd; - win_set_buf(wp, buf, noautocmd, err); - if (!fconfig.noautocmd) { - apply_autocmds(EVENT_WINNEW, NULL, NULL, false, buf); - } + if (win_valid_any_tab(wp) && buf != wp->w_buffer) { + win_set_buf(wp, buf, !enter || fconfig.noautocmd, err); } - if (!win_valid(wp)) { + if (!win_valid_any_tab(wp)) { api_set_error(err, kErrorTypeException, "Window was closed immediately"); return 0; } @@ -217,6 +288,36 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E return wp->handle; } +static WinSplit win_split_dir(win_T *win) +{ + if (win->w_frame == NULL || win->w_frame->fr_parent == NULL) { + return kWinSplitLeft; + } + + char layout = win->w_frame->fr_parent->fr_layout; + if (layout == FR_COL) { + return win->w_frame->fr_next ? kWinSplitAbove : kWinSplitBelow; + } else { + return win->w_frame->fr_next ? kWinSplitLeft : kWinSplitRight; + } +} + +static int win_split_flags(WinSplit split, bool toplevel) +{ + int flags = 0; + if (split == kWinSplitAbove || split == kWinSplitBelow) { + flags |= WSP_HOR; + } else { + flags |= WSP_VERT; + } + if (split == kWinSplitAbove || split == kWinSplitLeft) { + flags |= toplevel ? WSP_TOP : WSP_ABOVE; + } else { + flags |= toplevel ? WSP_BOT : WSP_BELOW; + } + return flags; +} + /// Configures window layout. Currently only for floating and external windows /// (including changing a split window to those layouts). /// @@ -236,18 +337,195 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err) if (!win) { return; } - bool new_float = !win->w_floating; + tabpage_T *win_tp = win_find_tabpage(win); + bool was_split = !win->w_floating; + bool has_split = HAS_KEY(config, float_config, split); + bool has_vertical = HAS_KEY(config, float_config, vertical); // reuse old values, if not overridden - FloatConfig fconfig = new_float ? FLOAT_CONFIG_INIT : win->w_float_config; + FloatConfig fconfig = win->w_float_config; + + bool to_split = (!HAS_KEY(config, float_config, relative) || striequal(config->relative.data, "")) + && ((!HAS_KEY(config, float_config, external) && !fconfig.external) + || !config->external) + && (has_split || has_vertical || was_split); - if (!parse_float_config(config, &fconfig, !new_float, false, err)) { + if (!parse_float_config(config, &fconfig, !was_split || to_split, false, err)) { return; } - if (new_float) { + if (was_split && !to_split) { if (!win_new_float(win, false, fconfig, err)) { return; } redraw_later(win, UPD_NOT_VALID); + } else if (to_split) { + win_T *parent = NULL; + if (!HAS_KEY(config, float_config, win) || config->win != -1) { + parent = find_window_by_handle(fconfig.window, err); + if (!parent) { + return; + } else if (parent->w_floating) { + api_set_error(err, kErrorTypeException, "Cannot split a floating window"); + return; + } + } + + WinSplit old_split = win_split_dir(win); + if (has_vertical && !has_split) { + if (config->vertical) { + if (old_split == kWinSplitRight || p_spr) { + fconfig.split = kWinSplitRight; + } else { + fconfig.split = kWinSplitLeft; + } + } else { + if (old_split == kWinSplitBelow || p_sb) { + fconfig.split = kWinSplitBelow; + } else { + fconfig.split = kWinSplitAbove; + } + } + } + win->w_float_config = fconfig; + + // If there's no vertical or split set, or if the split is the same as the old split, + // then we can just change the size of the window. + if ((!has_vertical && !has_split) + || (was_split + && !HAS_KEY(config, float_config, + win) && ((!has_split && !has_vertical) || old_split == fconfig.split))) { + if (HAS_KEY(config, float_config, width)) { + win_setwidth_win(fconfig.width, win); + } + if (HAS_KEY(config, float_config, height)) { + win_setheight_win(fconfig.height, win); + } + redraw_later(win, UPD_NOT_VALID); + return; + } + + if (was_split) { + win_T *new_curwin = NULL; + + // If the window is the last in the tabpage or `fconfig.win` is + // a handle to itself, we can't split it. + if (win->w_frame->fr_parent == NULL) { + // FIXME(willothy): if the window is the last in the tabpage but there is another tabpage + // and the target window is in that other tabpage, should we move the window to that + // tabpage and close the previous one, or just error? + api_set_error(err, kErrorTypeValidation, "Cannot move last window"); + return; + } else if (parent != NULL && parent->handle == win->handle) { + int n_frames = 0; + for (frame_T *fr = win->w_frame->fr_parent->fr_child; fr != NULL; fr = fr->fr_next) { + n_frames++; + } + + win_T *neighbor = NULL; + + if (n_frames > 2) { + // There are three or more windows in the frame, we need to split a neighboring window. + frame_T *frame = win->w_frame->fr_parent; + + if (frame->fr_parent) { + // ┌──────────────┐ + // │ A │ + // ├────┬────┬────┤ + // │ B │ C │ D │ + // └────┴────┴────┘ + // || + // \/ + // ┌───────────────────┐ + // │ A │ + // ├─────────┬─────────┤ + // │ │ C │ + // │ B ├─────────┤ + // │ │ D │ + // └─────────┴─────────┘ + if (fconfig.split == kWinSplitAbove || fconfig.split == kWinSplitLeft) { + neighbor = win->w_next; + } else { + neighbor = win->w_prev; + } + } + // If the frame doesn't have a parent, the old frame + // was the root frame and we need to create a top-level split. + int dir; + new_curwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp); + } else if (n_frames == 2) { + // There are two windows in the frame, we can just rotate it. + int dir; + neighbor = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp); + new_curwin = neighbor; + } else { + // There is only one window in the frame, we can't split it. + api_set_error(err, kErrorTypeValidation, "Cannot split window into itself"); + return; + } + // Set the parent to whatever the correct + // neighbor window was determined to be. + parent = neighbor; + } else { + int dir; + new_curwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp); + } + // move to neighboring window if we're moving the current window to a new tabpage + if (curwin == win && parent != NULL && new_curwin != NULL + && win_tp != win_find_tabpage(parent)) { + win_enter(new_curwin, true); + } + win_remove(win, win_tp == curtab ? NULL : win_tp); + } else { + win_remove(win, win_tp == curtab ? NULL : win_tp); + ui_comp_remove_grid(&win->w_grid_alloc); + if (win->w_float_config.external) { + for (tabpage_T *tp = first_tabpage; tp != NULL; tp = tp->tp_next) { + if (tp == curtab) { + continue; + } + if (tp->tp_curwin == win) { + tp->tp_curwin = tp->tp_firstwin; + } + } + } + win->w_pos_changed = true; + } + + int flags = win_split_flags(fconfig.split, parent == NULL); + + if (parent == NULL) { + if (!win_split_ins(0, flags, win, 0)) { + // TODO(willothy): What should this error message say? + api_set_error(err, kErrorTypeException, "Failed to split window"); + return; + } + } else { + win_execute_T args; + + tabpage_T *tp = win_find_tabpage(parent); + if (!win_execute_before(&args, parent, tp)) { + // TODO(willothy): how should we handle this / what should the message be? + api_set_error(err, kErrorTypeException, "Failed to switch to tabpage %d", tp->handle); + win_execute_after(&args); + return; + } + // This should return the same ptr to `win`, but we check for + // NULL to detect errors. + win_T *res = win_split_ins(0, flags, win, 0); + win_execute_after(&args); + if (!res) { + // TODO(willothy): What should this error message say? + api_set_error(err, kErrorTypeException, "Failed to split window"); + return; + } + } + if (HAS_KEY(config, float_config, width)) { + win_setwidth_win(fconfig.width, win); + } + if (HAS_KEY(config, float_config, height)) { + win_setheight_win(fconfig.height, win); + } + redraw_later(win, UPD_NOT_VALID); + return; } else { win_config_float(win, fconfig); win->w_pos_changed = true; @@ -317,6 +595,9 @@ Dictionary nvim_win_get_config(Window window, Error *err) /// Keep in sync with FloatRelative in buffer_defs.h static const char *const float_relative_str[] = { "editor", "win", "cursor", "mouse" }; + /// Keep in sync with WinSplit in buffer_defs.h + static const char *const win_split_str[] = { "left", "right", "above", "below" }; + Dictionary rv = ARRAY_DICT_INIT; win_T *wp = find_window_by_handle(window, err); @@ -373,11 +654,18 @@ Dictionary nvim_win_get_config(Window window, Error *err) rv = config_put_bordertext(rv, config, kBorderTextFooter); } } + } else if (!config->external) { + PUT(rv, "width", INTEGER_OBJ(wp->w_width)); + PUT(rv, "height", INTEGER_OBJ(wp->w_height)); + WinSplit split = win_split_dir(wp); + PUT(rv, "split", CSTR_TO_OBJ(win_split_str[split])); } - const char *rel = (wp->w_floating && !config->external - ? float_relative_str[config->relative] : ""); - PUT(rv, "relative", CSTR_TO_OBJ(rel)); + if (wp->w_floating && !config->external) { + PUT(rv, "relative", CSTR_TO_OBJ(float_relative_str[config->relative])); + } else { + PUT(rv, "relative", CSTR_TO_OBJ("")); + } return rv; } @@ -419,10 +707,26 @@ static bool parse_float_relative(String relative, FloatRelative *out) return true; } +static bool parse_config_split(String split, WinSplit *out) +{ + char *str = split.data; + if (striequal(str, "left")) { + *out = kWinSplitLeft; + } else if (striequal(str, "right")) { + *out = kWinSplitRight; + } else if (striequal(str, "above")) { + *out = kWinSplitAbove; + } else if (striequal(str, "below")) { + *out = kWinSplitBelow; + } else { + return false; + } + return true; +} + static bool parse_float_bufpos(Array bufpos, lpos_T *out) { - if (bufpos.size != 2 - || bufpos.items[0].type != kObjectTypeInteger + if (bufpos.size != 2 || bufpos.items[0].type != kObjectTypeInteger || bufpos.items[1].type != kObjectTypeInteger) { return false; } @@ -529,7 +833,7 @@ static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertex return true; } -static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) +static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) { struct { const char *name; @@ -544,7 +848,7 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) { NULL, { { NUL } }, false }, }; - char (*chars)[MAX_SCHAR_SIZE] = fconfig->border_chars; + char(*chars)[MAX_SCHAR_SIZE] = fconfig->border_chars; int *hl_ids = fconfig->border_hl_ids; fconfig->border = true; @@ -553,8 +857,7 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) Array arr = style.data.array; size_t size = arr.size; if (!size || size > 8 || (size & (size - 1))) { - api_set_error(err, kErrorTypeValidation, - "invalid number of border chars"); + api_set_error(err, kErrorTypeValidation, "invalid number of border chars"); return; } for (size_t i = 0; i < size; i++) { @@ -584,10 +887,8 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) api_set_error(err, kErrorTypeValidation, "invalid border char"); return; } - if (string.size - && mb_string2cells_len(string.data, string.size) > 1) { - api_set_error(err, kErrorTypeValidation, - "border chars must be one cell"); + if (string.size && mb_string2cells_len(string.data, string.size) > 1) { + api_set_error(err, kErrorTypeValidation, "border chars must be one cell"); return; } size_t len = MIN(string.size, sizeof(*chars) - 1); @@ -606,8 +907,7 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) || (chars[1][0] && chars[3][0] && !chars[2][0]) || (chars[3][0] && chars[5][0] && !chars[4][0]) || (chars[5][0] && chars[7][0] && !chars[6][0])) { - api_set_error(err, kErrorTypeValidation, - "corner between used edges must be specified"); + api_set_error(err, kErrorTypeValidation, "corner between used edges must be specified"); } } else if (style.type == kObjectTypeString) { String str = style.data.string; @@ -634,8 +934,7 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) return; } } - api_set_error(err, kErrorTypeValidation, - "invalid border style \"%s\"", str.data); + api_set_error(err, kErrorTypeValidation, "invalid border style \"%s\"", str.data); } } @@ -643,17 +942,16 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, bool new_win, Error *err) { #define HAS_KEY_X(d, key) HAS_KEY(d, float_config, key) - bool has_relative = false, relative_is_win = false; - // ignore empty string, to match nvim_win_get_config - if (HAS_KEY_X(config, relative) && config->relative.size > 0) { + bool has_relative = false, relative_is_win = false, is_split = false; + if (HAS_KEY_X(config, relative) && !striequal(config->relative.data, "")) { if (!parse_float_relative(config->relative, &fconfig->relative)) { api_set_error(err, kErrorTypeValidation, "Invalid value of 'relative' key"); return false; } - if (!(HAS_KEY_X(config, row) && HAS_KEY_X(config, col)) && !HAS_KEY_X(config, bufpos)) { - api_set_error(err, kErrorTypeValidation, - "'relative' requires 'row'/'col' or 'bufpos'"); + if (config->relative.size > 0 && !(HAS_KEY_X(config, row) && HAS_KEY_X(config, col)) + && !HAS_KEY_X(config, bufpos)) { + api_set_error(err, kErrorTypeValidation, "'relative' requires 'row'/'col' or 'bufpos'"); return false; } @@ -663,6 +961,32 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, relative_is_win = true; fconfig->bufpos.lnum = -1; } + } else if (!HAS_KEY_X(config, external) || !config->external) { + if (HAS_KEY_X(config, vertical) || HAS_KEY_X(config, split)) { + is_split = true; + } else if (new_win) { + api_set_error(err, kErrorTypeValidation, + "Must specify 'relative' or 'external' when creating a float"); + return false; + } + } + + if (HAS_KEY_X(config, vertical)) { + if (!is_split) { + api_set_error(err, kErrorTypeValidation, "floating windows cannot have 'vertical'"); + return false; + } + } + + if (HAS_KEY_X(config, split)) { + if (!is_split) { + api_set_error(err, kErrorTypeValidation, "floating windows cannot have 'split'"); + return false; + } + if (!parse_config_split(config->split, &fconfig->split)) { + api_set_error(err, kErrorTypeValidation, "Invalid value of 'split' key"); + return false; + } } if (HAS_KEY_X(config, anchor)) { @@ -673,7 +997,7 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, } if (HAS_KEY_X(config, row)) { - if (!has_relative) { + if (!has_relative || is_split) { api_set_error(err, kErrorTypeValidation, "non-float cannot have 'row'"); return false; } @@ -681,7 +1005,7 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, } if (HAS_KEY_X(config, col)) { - if (!has_relative) { + if (!has_relative || is_split) { api_set_error(err, kErrorTypeValidation, "non-float cannot have 'col'"); return false; } @@ -689,7 +1013,7 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, } if (HAS_KEY_X(config, bufpos)) { - if (!has_relative) { + if (!has_relative || is_split) { api_set_error(err, kErrorTypeValidation, "non-float cannot have 'bufpos'"); return false; } else { @@ -714,7 +1038,7 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, api_set_error(err, kErrorTypeValidation, "'width' key must be a positive Integer"); return false; } - } else if (!reconf) { + } else if (!reconf && !is_split) { api_set_error(err, kErrorTypeValidation, "Must specify 'width'"); return false; } @@ -726,21 +1050,22 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, api_set_error(err, kErrorTypeValidation, "'height' key must be a positive Integer"); return false; } - } else if (!reconf) { + } else if (!reconf && !is_split) { api_set_error(err, kErrorTypeValidation, "Must specify 'height'"); return false; } - if (relative_is_win) { + if (relative_is_win || is_split) { fconfig->window = curwin->handle; if (HAS_KEY_X(config, win)) { if (config->win > 0) { fconfig->window = config->win; } } - } else { + } else if (has_relative) { if (HAS_KEY_X(config, win)) { - api_set_error(err, kErrorTypeValidation, "'win' key is only valid with relative='win'"); + api_set_error(err, kErrorTypeValidation, + "'win' key is only valid with relative='win' and relative=''"); return false; } } @@ -753,23 +1078,20 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, return false; } if (fconfig->external && !ui_has(kUIMultigrid)) { - api_set_error(err, kErrorTypeValidation, - "UI doesn't support external windows"); + api_set_error(err, kErrorTypeValidation, "UI doesn't support external windows"); return false; } } - if (!reconf && (!has_relative && !fconfig->external)) { - api_set_error(err, kErrorTypeValidation, - "One of 'relative' and 'external' must be used"); - return false; - } - if (HAS_KEY_X(config, focusable)) { fconfig->focusable = config->focusable; } if (HAS_KEY_X(config, zindex)) { + if (is_split) { + api_set_error(err, kErrorTypeValidation, "non-float cannot have 'zindex'"); + return false; + } if (config->zindex > 0) { fconfig->zindex = (int)config->zindex; } else { @@ -779,6 +1101,10 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, } if (HAS_KEY_X(config, title)) { + if (is_split) { + api_set_error(err, kErrorTypeValidation, "non-float cannot have 'title'"); + return false; + } // title only work with border if (!HAS_KEY_X(config, border) && !fconfig->border) { api_set_error(err, kErrorTypeException, "title requires border to be set"); @@ -802,6 +1128,10 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, } if (HAS_KEY_X(config, footer)) { + if (is_split) { + api_set_error(err, kErrorTypeValidation, "non-float cannot have 'footer'"); + return false; + } // footer only work with border if (!HAS_KEY_X(config, border) && !fconfig->border) { api_set_error(err, kErrorTypeException, "footer requires border to be set"); @@ -825,6 +1155,10 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, } if (HAS_KEY_X(config, border)) { + if (is_split) { + api_set_error(err, kErrorTypeValidation, "non-float cannot have 'border'"); + return false; + } parse_border_style(config->border, fconfig, err); if (ERROR_SET(err)) { return false; -- cgit From cc197d04fc36a6ee4219f20772a7b39dd976edfd Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 1 Feb 2024 18:05:06 +0800 Subject: refactor(api): remove some unnecessary HAS_KEY() (#27283) Since keydicts are already zero-initialized, HAS_KEY() isn't needed if the zero-initialized value can satisfy some other condition. --- src/nvim/api/vim.c | 6 ++---- src/nvim/api/win_config.c | 41 +++++++++++++++++++++-------------------- 2 files changed, 23 insertions(+), 24 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 4bd8f551db..edee50f411 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2153,7 +2153,6 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * int maxwidth; schar_T fillchar = 0; int statuscol_lnum = 0; - Window window = 0; if (str.size < 2 || memcmp(str.data, "%!", 2) != 0) { const char *const errmsg = check_stl_option(str.data); @@ -2162,9 +2161,8 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * }); } - if (HAS_KEY(opts, eval_statusline, winid)) { - window = opts->winid; - } + Window window = opts->winid; + if (HAS_KEY(opts, eval_statusline, fillchar)) { VALIDATE_EXP((*opts->fillchar.data != 0 && ((size_t)utfc_ptr2len(opts->fillchar.data) == opts->fillchar.size)), diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 5089367114..041aaed976 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -203,6 +203,7 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, Error *err) FUNC_API_SINCE(6) FUNC_API_TEXTLOCK_ALLOW_CMDWIN { +#define HAS_KEY_X(d, key) HAS_KEY(d, float_config, key) buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { return 0; @@ -217,13 +218,13 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E return 0; } - bool is_split = HAS_KEY(config, float_config, split) || HAS_KEY(config, float_config, vertical); + bool is_split = HAS_KEY_X(config, split) || HAS_KEY_X(config, vertical); win_T *wp = NULL; tabpage_T *tp = curtab; if (is_split) { win_T *parent = NULL; - if (!HAS_KEY(config, float_config, win) || config->win != -1) { + if (config->win != -1) { parent = find_window_by_handle(fconfig.window, err); if (!parent) { // find_window_by_handle has already set the error @@ -234,7 +235,7 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E } } - if (HAS_KEY(config, float_config, vertical) && !HAS_KEY(config, float_config, split)) { + if (HAS_KEY_X(config, vertical) && !HAS_KEY_X(config, split)) { if (config->vertical) { fconfig.split = p_spr ? kWinSplitRight : kWinSplitLeft; } else { @@ -286,6 +287,7 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E didset_window_options(wp, true); } return wp->handle; +#undef HAS_KEY_X } static WinSplit win_split_dir(win_T *win) @@ -333,20 +335,20 @@ static int win_split_flags(WinSplit split, bool toplevel) void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err) FUNC_API_SINCE(6) { +#define HAS_KEY_X(d, key) HAS_KEY(d, float_config, key) win_T *win = find_window_by_handle(window, err); if (!win) { return; } tabpage_T *win_tp = win_find_tabpage(win); bool was_split = !win->w_floating; - bool has_split = HAS_KEY(config, float_config, split); - bool has_vertical = HAS_KEY(config, float_config, vertical); + bool has_split = HAS_KEY_X(config, split); + bool has_vertical = HAS_KEY_X(config, vertical); // reuse old values, if not overridden FloatConfig fconfig = win->w_float_config; - bool to_split = (!HAS_KEY(config, float_config, relative) || striequal(config->relative.data, "")) - && ((!HAS_KEY(config, float_config, external) && !fconfig.external) - || !config->external) + bool to_split = config->relative.size == 0 + && !(HAS_KEY_X(config, external) ? config->external : fconfig.external) && (has_split || has_vertical || was_split); if (!parse_float_config(config, &fconfig, !was_split || to_split, false, err)) { @@ -359,7 +361,7 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err) redraw_later(win, UPD_NOT_VALID); } else if (to_split) { win_T *parent = NULL; - if (!HAS_KEY(config, float_config, win) || config->win != -1) { + if (config->win != -1) { parent = find_window_by_handle(fconfig.window, err); if (!parent) { return; @@ -387,16 +389,14 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err) } win->w_float_config = fconfig; - // If there's no vertical or split set, or if the split is the same as the old split, + // If there's no "vertical" or "split" set, or if "split" is unchanged, // then we can just change the size of the window. if ((!has_vertical && !has_split) - || (was_split - && !HAS_KEY(config, float_config, - win) && ((!has_split && !has_vertical) || old_split == fconfig.split))) { - if (HAS_KEY(config, float_config, width)) { + || (was_split && !HAS_KEY_X(config, win) && old_split == fconfig.split)) { + if (HAS_KEY_X(config, width)) { win_setwidth_win(fconfig.width, win); } - if (HAS_KEY(config, float_config, height)) { + if (HAS_KEY_X(config, height)) { win_setheight_win(fconfig.height, win); } redraw_later(win, UPD_NOT_VALID); @@ -518,10 +518,10 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err) return; } } - if (HAS_KEY(config, float_config, width)) { + if (HAS_KEY_X(config, width)) { win_setwidth_win(fconfig.width, win); } - if (HAS_KEY(config, float_config, height)) { + if (HAS_KEY_X(config, height)) { win_setheight_win(fconfig.height, win); } redraw_later(win, UPD_NOT_VALID); @@ -530,12 +530,13 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err) win_config_float(win, fconfig); win->w_pos_changed = true; } - if (HAS_KEY(config, float_config, style)) { + if (HAS_KEY_X(config, style)) { if (fconfig.style == kWinStyleMinimal) { win_set_minimal_style(win); didset_window_options(win, true); } } +#undef HAS_KEY_X } static Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig, @@ -943,7 +944,7 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, { #define HAS_KEY_X(d, key) HAS_KEY(d, float_config, key) bool has_relative = false, relative_is_win = false, is_split = false; - if (HAS_KEY_X(config, relative) && !striequal(config->relative.data, "")) { + if (config->relative.size > 0) { if (!parse_float_relative(config->relative, &fconfig->relative)) { api_set_error(err, kErrorTypeValidation, "Invalid value of 'relative' key"); return false; @@ -961,7 +962,7 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, relative_is_win = true; fconfig->bufpos.lnum = -1; } - } else if (!HAS_KEY_X(config, external) || !config->external) { + } else if (!config->external) { if (HAS_KEY_X(config, vertical) || HAS_KEY_X(config, split)) { is_split = true; } else if (new_win) { -- cgit From a090d43d61b5de1add5624fc55e4a9dead5b7ece Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Mon, 5 Feb 2024 14:39:54 +0200 Subject: fix: splitting of big UI messages Determine the needed buffer space first, instead of trying to revert the effect of prepare_call if message does not fit. The previous code did not revert the full state, which caused corrupted messages to be sent. So, rather than trying to fix all of that, with fragile and hard to read code as a result, the code is now much more simple, although slightly slower. --- src/nvim/api/ui.c | 101 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 53 insertions(+), 48 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index f955b315a8..03e7e11601 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -585,7 +585,6 @@ static inline int write_cb(void *vdata, const char *buf, size_t len) data->pack_totlen += len; if (!data->temp_buf && UI_BUF_SIZE - BUF_POS(data) < len) { - data->buf_overflow = true; return 0; } @@ -595,14 +594,42 @@ static inline int write_cb(void *vdata, const char *buf, size_t len) return 0; } -static bool prepare_call(UI *ui, const char *name) +static inline int size_cb(void *vdata, const char *buf, size_t len) +{ + UIData *data = (UIData *)vdata; + if (!buf) { + return 0; + } + + data->pack_totlen += len; + return 0; +} + +static void prepare_call(UI *ui, const char *name, size_t size_needed) { UIData *data = ui->data; + size_t name_len = strlen(name); + const size_t overhead = name_len + 20; + bool oversized_message = size_needed + overhead > UI_BUF_SIZE; - if (BUF_POS(data) > UI_BUF_SIZE - EVENT_BUF_SIZE) { + if (oversized_message || BUF_POS(data) > UI_BUF_SIZE - size_needed - overhead) { remote_ui_flush_buf(ui); } + if (oversized_message) { + // TODO(bfredl): manually testable by setting UI_BUF_SIZE to 1024 (mode_info_set) + data->temp_buf = xmalloc(20 + name_len + size_needed); + data->buf_wptr = data->temp_buf; + char **buf = &data->buf_wptr; + mpack_array(buf, 3); + mpack_uint(buf, 2); + mpack_str(buf, S_LEN("redraw")); + mpack_array(buf, 1); + mpack_array(buf, 2); + mpack_str(buf, name, name_len); + return; + } + // To optimize data transfer(especially for "grid_line"), we bundle adjacent // calls to same method together, so only add a new call entry if the last // method call is different from "name" @@ -615,64 +642,42 @@ static bool prepare_call(UI *ui, const char *name) mpack_str(buf, name, strlen(name)); data->nevents++; data->ncalls = 1; - return true; + return; } +} - return false; +static void send_oversized_message(UIData *data) +{ + if (data->temp_buf) { + size_t size = (size_t)(data->buf_wptr - data->temp_buf); + WBuffer *buf = wstream_new_buffer(data->temp_buf, size, 1, xfree); + rpc_write_raw(data->channel_id, buf); + data->temp_buf = NULL; + data->buf_wptr = data->buf; + data->nevents_pos = NULL; + } } /// Pushes data into UI.UIData, to be consumed later by remote_ui_flush(). static void push_call(UI *ui, const char *name, Array args) { UIData *data = ui->data; - bool pending = data->nevents_pos; - char *buf_pos_save = data->buf_wptr; - - bool new_event = prepare_call(ui, name); msgpack_packer pac; data->pack_totlen = 0; - data->buf_overflow = false; + // First determine the needed size + msgpack_packer_init(&pac, data, size_cb); + msgpack_rpc_from_array(args, &pac); + // Then send the actual message + prepare_call(ui, name, data->pack_totlen); msgpack_packer_init(&pac, data, write_cb); msgpack_rpc_from_array(args, &pac); - if (data->buf_overflow) { - data->buf_wptr = buf_pos_save; - if (new_event) { - data->cur_event = NULL; - data->nevents--; - } - if (pending) { - remote_ui_flush_buf(ui); - } - size_t name_len = strlen(name); - if (data->pack_totlen > UI_BUF_SIZE - name_len - 20) { - // TODO(bfredl): manually testable by setting UI_BUF_SIZE to 1024 (mode_info_set) - data->temp_buf = xmalloc(20 + name_len + data->pack_totlen); - data->buf_wptr = data->temp_buf; - char **buf = &data->buf_wptr; - mpack_array(buf, 3); - mpack_uint(buf, 2); - mpack_str(buf, S_LEN("redraw")); - mpack_array(buf, 1); - mpack_array(buf, 2); - mpack_str(buf, name, name_len); - } else { - prepare_call(ui, name); - } - data->pack_totlen = 0; - data->buf_overflow = false; - msgpack_rpc_from_array(args, &pac); - - if (data->temp_buf) { - size_t size = (size_t)(data->buf_wptr - data->temp_buf); - WBuffer *buf = wstream_new_buffer(data->temp_buf, size, 1, xfree); - rpc_write_raw(data->channel_id, buf); - data->temp_buf = NULL; - data->buf_wptr = data->buf; - data->nevents_pos = NULL; - } + // Oversized messages need to be sent immediately + if (data->temp_buf) { + send_oversized_message(data); } + data->ncalls++; } @@ -867,7 +872,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int { UIData *data = ui->data; if (ui->ui_ext[kUILinegrid]) { - prepare_call(ui, "grid_line"); + prepare_call(ui, "grid_line", EVENT_BUF_SIZE); data->ncalls++; char **buf = &data->buf_wptr; @@ -896,7 +901,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int mpack_bool(buf, false); remote_ui_flush_buf(ui); - prepare_call(ui, "grid_line"); + prepare_call(ui, "grid_line", EVENT_BUF_SIZE); data->ncalls++; mpack_array(buf, 5); mpack_uint(buf, (uint32_t)grid); -- cgit From f9d81c43d2296d212c9cebcbdce401cd76cf0f1f Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 31 Jan 2024 22:02:06 +0100 Subject: refactor(api): use keydict and arena for more api return values Implement api_keydict_to_dict as the complement to api_dict_to_keydict Fix a conversion error when nvim_get_win_config gets called from lua, where Float values "x" and "y" didn't get converted to lua numbers. --- src/nvim/api/extmark.c | 81 ++++++++++++++++++++----------------- src/nvim/api/private/helpers.c | 48 ++++++++++++++++++++++ src/nvim/api/private/helpers.h | 4 ++ src/nvim/api/win_config.c | 92 +++++++++++++++++++++--------------------- 4 files changed, 142 insertions(+), 83 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 4e84b41a02..0948abf065 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -105,73 +105,81 @@ bool ns_initialized(uint32_t ns) return ns < (uint32_t)next_namespace_id; } -Array virt_text_to_array(VirtText vt, bool hl_name) +Array virt_text_to_array(VirtText vt, bool hl_name, Arena *arena) { - Array chunks = ARRAY_DICT_INIT; - Array hl_array = ARRAY_DICT_INIT; + Array chunks = arena_array(arena, kv_size(vt)); for (size_t i = 0; i < kv_size(vt); i++) { - char *text = kv_A(vt, i).text; - int hl_id = kv_A(vt, i).hl_id; - if (text == NULL) { + size_t j = i; + for (; j < kv_size(vt); j++) { + if (kv_A(vt, j).text != NULL) { + break; + } + } + + Array hl_array = arena_array(arena, i < j ? j - i + 1 : 0); + for (; i < j; i++) { + int hl_id = kv_A(vt, i).hl_id; if (hl_id > 0) { - ADD(hl_array, hl_group_name(hl_id, hl_name)); + ADD_C(hl_array, hl_group_name(hl_id, hl_name)); } - continue; } - Array chunk = ARRAY_DICT_INIT; - ADD(chunk, CSTR_TO_OBJ(text)); + + char *text = kv_A(vt, i).text; + int hl_id = kv_A(vt, i).hl_id; + Array chunk = arena_array(arena, 2); + ADD_C(chunk, CSTR_AS_OBJ(text)); if (hl_array.size > 0) { if (hl_id > 0) { - ADD(hl_array, hl_group_name(hl_id, hl_name)); + ADD_C(hl_array, hl_group_name(hl_id, hl_name)); } - ADD(chunk, ARRAY_OBJ(hl_array)); - hl_array = (Array)ARRAY_DICT_INIT; + ADD_C(chunk, ARRAY_OBJ(hl_array)); } else if (hl_id > 0) { - ADD(chunk, hl_group_name(hl_id, hl_name)); + ADD_C(chunk, hl_group_name(hl_id, hl_name)); } - ADD(chunks, ARRAY_OBJ(chunk)); + ADD_C(chunks, ARRAY_OBJ(chunk)); } - assert(hl_array.size == 0); return chunks; } -static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_name) +static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_name, Arena *arena) { MTKey start = extmark.start; - Array rv = ARRAY_DICT_INIT; + Array rv = arena_array(arena, 4); if (id) { - ADD(rv, INTEGER_OBJ((Integer)start.id)); + ADD_C(rv, INTEGER_OBJ((Integer)start.id)); } - ADD(rv, INTEGER_OBJ(start.pos.row)); - ADD(rv, INTEGER_OBJ(start.pos.col)); + ADD_C(rv, INTEGER_OBJ(start.pos.row)); + ADD_C(rv, INTEGER_OBJ(start.pos.col)); if (add_dict) { - Dictionary dict = ARRAY_DICT_INIT; + // TODO(bfredl): coding the size like this is a bit fragile. + // We want ArrayOf(Dict(set_extmark)) as the return type.. + Dictionary dict = arena_dict(arena, ARRAY_SIZE(set_extmark_table)); - PUT(dict, "ns_id", INTEGER_OBJ((Integer)start.ns)); + PUT_C(dict, "ns_id", INTEGER_OBJ((Integer)start.ns)); - PUT(dict, "right_gravity", BOOLEAN_OBJ(mt_right(start))); + PUT_C(dict, "right_gravity", BOOLEAN_OBJ(mt_right(start))); if (mt_paired(start)) { - PUT(dict, "end_row", INTEGER_OBJ(extmark.end_pos.row)); - PUT(dict, "end_col", INTEGER_OBJ(extmark.end_pos.col)); - PUT(dict, "end_right_gravity", BOOLEAN_OBJ(extmark.end_right_gravity)); + PUT_C(dict, "end_row", INTEGER_OBJ(extmark.end_pos.row)); + PUT_C(dict, "end_col", INTEGER_OBJ(extmark.end_pos.col)); + PUT_C(dict, "end_right_gravity", BOOLEAN_OBJ(extmark.end_right_gravity)); } if (mt_no_undo(start)) { - PUT(dict, "undo_restore", BOOLEAN_OBJ(false)); + PUT_C(dict, "undo_restore", BOOLEAN_OBJ(false)); } if (mt_invalidate(start)) { - PUT(dict, "invalidate", BOOLEAN_OBJ(true)); + PUT_C(dict, "invalidate", BOOLEAN_OBJ(true)); } if (mt_invalid(start)) { - PUT(dict, "invalid", BOOLEAN_OBJ(true)); + PUT_C(dict, "invalid", BOOLEAN_OBJ(true)); } - decor_to_dict_legacy(&dict, mt_decor(start), hl_name); + decor_to_dict_legacy(&dict, mt_decor(start), hl_name, arena); - ADD(rv, DICTIONARY_OBJ(dict)); + ADD_C(rv, DICTIONARY_OBJ(dict)); } return rv; @@ -190,7 +198,7 @@ static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_na /// absent ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, Integer id, Dict(get_extmark) *opts, - Error *err) + Arena *arena, Error *err) FUNC_API_SINCE(7) { Array rv = ARRAY_DICT_INIT; @@ -213,7 +221,7 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, if (extmark.start.pos.row < 0) { return rv; } - return extmark_to_array(extmark, false, details, hl_name); + return extmark_to_array(extmark, false, details, hl_name, arena); } /// Gets |extmarks| in "traversal order" from a |charwise| region defined by @@ -272,7 +280,7 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, /// @param[out] err Error details, if any /// @return List of [extmark_id, row, col] tuples in "traversal order". Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object end, - Dict(get_extmarks) *opts, Error *err) + Dict(get_extmarks) *opts, Arena *arena, Error *err) FUNC_API_SINCE(7) { Array rv = ARRAY_DICT_INIT; @@ -336,8 +344,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e ExtmarkInfoArray marks = extmark_get(buf, (uint32_t)ns_id, l_row, l_col, u_row, u_col, (int64_t)limit, reverse, type, opts->overlap); + rv = arena_array(arena, kv_size(marks)); for (size_t i = 0; i < kv_size(marks); i++) { - ADD(rv, ARRAY_OBJ(extmark_to_array(kv_A(marks, i), true, details, hl_name))); + ADD(rv, ARRAY_OBJ(extmark_to_array(kv_A(marks, i), true, details, hl_name, arena))); } kv_destroy(marks); diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 9ce1786fa0..cc95f46baf 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1014,6 +1014,54 @@ bool api_dict_to_keydict(void *retval, FieldHashfn hashy, Dictionary dict, Error return true; } +Dictionary api_keydict_to_dict(void *value, KeySetLink *table, size_t max_size, Arena *arena) +{ + Dictionary rv = arena_dict(arena, max_size); + for (size_t i = 0; table[i].str; i++) { + KeySetLink *field = &table[i]; + bool is_set = true; + if (field->opt_index >= 0) { + OptKeySet *ks = (OptKeySet *)value; + is_set = ks->is_set_ & (1ULL << field->opt_index); + } + + if (!is_set) { + continue; + } + + char *mem = ((char *)value + field->ptr_off); + Object val = NIL; + + if (field->type == kObjectTypeNil) { + val = *(Object *)mem; + } else if (field->type == kObjectTypeInteger) { + val = INTEGER_OBJ(*(Integer *)mem); + } else if (field->type == kObjectTypeFloat) { + val = FLOAT_OBJ(*(Float *)mem); + } else if (field->type == kObjectTypeBoolean) { + val = BOOLEAN_OBJ(*(Boolean *)mem); + } else if (field->type == kObjectTypeString) { + val = STRING_OBJ(*(String *)mem); + } else if (field->type == kObjectTypeArray) { + val = ARRAY_OBJ(*(Array *)mem); + } else if (field->type == kObjectTypeDictionary) { + val = DICTIONARY_OBJ(*(Dictionary *)mem); + } else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow + || field->type == kObjectTypeTabpage) { + val.data.integer = *(Integer *)mem; + val.type = field->type; + } else if (field->type == kObjectTypeLuaRef) { + // do nothing + } else { + abort(); + } + + PUT_C(rv, field->str, val); + } + + return rv; +} + void api_free_keydict(void *dict, KeySetLink *table) { for (size_t i = 0; table[i].str; i++) { diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 8a56d1704f..0dbe3d2fb6 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -32,6 +32,7 @@ #define CSTR_AS_OBJ(s) STRING_OBJ(cstr_as_string(s)) #define CSTR_TO_OBJ(s) STRING_OBJ(cstr_to_string(s)) +#define CSTR_TO_ARENA_OBJ(arena, s) STRING_OBJ(arena_string(arena, cstr_as_string(s))) #define BUFFER_OBJ(s) ((Object) { \ .type = kObjectTypeBuffer, \ @@ -70,6 +71,9 @@ #define PUT_C(dict, k, v) \ kv_push_c(dict, ((KeyValuePair) { .key = cstr_as_string(k), .value = v })) +#define PUT_KEY(d, typ, key, v) \ + do { (d).is_set__##typ##_ |= (1 << KEYSET_OPTIDX_##typ##__##key); (d).key = v; } while (0) + #define ADD(array, item) \ kv_push(array, item) diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 041aaed976..a9d7a8d05c 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -539,30 +539,24 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err) #undef HAS_KEY_X } -static Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig, - BorderTextType bordertext_type) +#define PUT_KEY_X(d, key, value) PUT_KEY(d, float_config, key, value) +static void config_put_bordertext(Dict(float_config) *config, FloatConfig *fconfig, + BorderTextType bordertext_type, Arena *arena) { VirtText vt; AlignTextPos align; - char *field_name; - char *field_pos_name; switch (bordertext_type) { case kBorderTextTitle: vt = fconfig->title_chunks; align = fconfig->title_pos; - field_name = "title"; - field_pos_name = "title_pos"; break; case kBorderTextFooter: vt = fconfig->footer_chunks; align = fconfig->footer_pos; - field_name = "footer"; - field_pos_name = "footer_pos"; break; } - Array bordertext = virt_text_to_array(vt, true); - PUT(config, field_name, ARRAY_OBJ(bordertext)); + Array bordertext = virt_text_to_array(vt, true, arena); char *pos; switch (align) { @@ -576,9 +570,16 @@ static Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig, pos = "right"; break; } - PUT(config, field_pos_name, CSTR_TO_OBJ(pos)); - return config; + switch (bordertext_type) { + case kBorderTextTitle: + PUT_KEY_X(*config, title, ARRAY_OBJ(bordertext)); + PUT_KEY_X(*config, title_pos, cstr_as_string(pos)); + break; + case kBorderTextFooter: + PUT_KEY_X(*config, footer, ARRAY_OBJ(bordertext)); + PUT_KEY_X(*config, footer_pos, cstr_as_string(pos)); + } } /// Gets window configuration. @@ -590,7 +591,7 @@ static Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig, /// @param window Window handle, or 0 for current window /// @param[out] err Error details, if any /// @return Map defining the window configuration, see |nvim_open_win()| -Dictionary nvim_win_get_config(Window window, Error *err) +Dict(float_config) nvim_win_get_config(Window window, Arena *arena, Error *err) FUNC_API_SINCE(6) { /// Keep in sync with FloatRelative in buffer_defs.h @@ -599,7 +600,7 @@ Dictionary nvim_win_get_config(Window window, Error *err) /// Keep in sync with WinSplit in buffer_defs.h static const char *const win_split_str[] = { "left", "right", "above", "below" }; - Dictionary rv = ARRAY_DICT_INIT; + Dict(float_config) rv = { 0 }; win_T *wp = find_window_by_handle(window, err); if (!wp) { @@ -608,65 +609,62 @@ Dictionary nvim_win_get_config(Window window, Error *err) FloatConfig *config = &wp->w_float_config; - PUT(rv, "focusable", BOOLEAN_OBJ(config->focusable)); - PUT(rv, "external", BOOLEAN_OBJ(config->external)); - PUT(rv, "hide", BOOLEAN_OBJ(config->hide)); + PUT_KEY_X(rv, focusable, config->focusable); + PUT_KEY_X(rv, external, config->external); + PUT_KEY_X(rv, hide, config->hide); if (wp->w_floating) { - PUT(rv, "width", INTEGER_OBJ(config->width)); - PUT(rv, "height", INTEGER_OBJ(config->height)); + PUT_KEY_X(rv, width, config->width); + PUT_KEY_X(rv, height, config->height); if (!config->external) { if (config->relative == kFloatRelativeWindow) { - PUT(rv, "win", INTEGER_OBJ(config->window)); + PUT_KEY_X(rv, win, config->window); if (config->bufpos.lnum >= 0) { - Array pos = ARRAY_DICT_INIT; - ADD(pos, INTEGER_OBJ(config->bufpos.lnum)); - ADD(pos, INTEGER_OBJ(config->bufpos.col)); - PUT(rv, "bufpos", ARRAY_OBJ(pos)); + Array pos = arena_array(arena, 2); + ADD_C(pos, INTEGER_OBJ(config->bufpos.lnum)); + ADD_C(pos, INTEGER_OBJ(config->bufpos.col)); + PUT_KEY_X(rv, bufpos, pos); } } - PUT(rv, "anchor", CSTR_TO_OBJ(float_anchor_str[config->anchor])); - PUT(rv, "row", FLOAT_OBJ(config->row)); - PUT(rv, "col", FLOAT_OBJ(config->col)); - PUT(rv, "zindex", INTEGER_OBJ(config->zindex)); + PUT_KEY_X(rv, anchor, cstr_as_string((char *)float_anchor_str[config->anchor])); + PUT_KEY_X(rv, row, config->row); + PUT_KEY_X(rv, col, config->col); + PUT_KEY_X(rv, zindex, config->zindex); } if (config->border) { - Array border = ARRAY_DICT_INIT; + Array border = arena_array(arena, 8); for (size_t i = 0; i < 8; i++) { - Array tuple = ARRAY_DICT_INIT; - - String s = cstrn_to_string(config->border_chars[i], MAX_SCHAR_SIZE); + String s = cstrn_as_string(config->border_chars[i], MAX_SCHAR_SIZE); int hi_id = config->border_hl_ids[i]; char *hi_name = syn_id2name(hi_id); if (hi_name[0]) { - ADD(tuple, STRING_OBJ(s)); - ADD(tuple, CSTR_TO_OBJ(hi_name)); - ADD(border, ARRAY_OBJ(tuple)); + Array tuple = arena_array(arena, 2); + ADD_C(tuple, STRING_OBJ(s)); + ADD_C(tuple, CSTR_AS_OBJ(hi_name)); + ADD_C(border, ARRAY_OBJ(tuple)); } else { - ADD(border, STRING_OBJ(s)); + ADD_C(border, STRING_OBJ(s)); } } - PUT(rv, "border", ARRAY_OBJ(border)); + PUT_KEY_X(rv, border, ARRAY_OBJ(border)); if (config->title) { - rv = config_put_bordertext(rv, config, kBorderTextTitle); + config_put_bordertext(&rv, config, kBorderTextTitle, arena); } if (config->footer) { - rv = config_put_bordertext(rv, config, kBorderTextFooter); + config_put_bordertext(&rv, config, kBorderTextFooter, arena); } } } else if (!config->external) { - PUT(rv, "width", INTEGER_OBJ(wp->w_width)); - PUT(rv, "height", INTEGER_OBJ(wp->w_height)); + PUT_KEY_X(rv, width, wp->w_width); + PUT_KEY_X(rv, height, wp->w_height); WinSplit split = win_split_dir(wp); - PUT(rv, "split", CSTR_TO_OBJ(win_split_str[split])); + PUT_KEY_X(rv, split, cstr_as_string((char *)win_split_str[split])); } - if (wp->w_floating && !config->external) { - PUT(rv, "relative", CSTR_TO_OBJ(float_relative_str[config->relative])); - } else { - PUT(rv, "relative", CSTR_TO_OBJ("")); - } + const char *rel = (wp->w_floating && !config->external + ? float_relative_str[config->relative] : ""); + PUT_KEY_X(rv, relative, cstr_as_string((char *)rel)); return rv; } -- cgit From b162adbb7ca4f980e938a4a06a49218a9ed496cf Mon Sep 17 00:00:00 2001 From: nikolightsaber <103886134+nikolightsaber@users.noreply.github.com> Date: Thu, 8 Feb 2024 11:59:31 +0100 Subject: feat(api): pass 0 to nvim_get_chan_info for current channel (#27321) Getting current channel info was kind of annoying via RPC. Two functions had to be called: 1. `nvim_get_api_info` which returns `[channel_id, meta_data]`. - This results in `channel_id = api.nvim_get_api_info()[0]`. - Here the meta_data is sent but never used. 2. Finally call `nvim_get_chan_info(channel_id)`. This commit reduces the need for `nvim_get_api_info` as passing 0 returns current channel info. --- src/nvim/api/vim.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index edee50f411..b1bfdd79e7 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1612,6 +1612,7 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version, /// Gets information about a channel. /// +/// @param chan channel_id, or 0 for current channel /// @returns Dictionary describing a channel, with these keys: /// - "id" Channel id. /// - "argv" (optional) Job arguments list. @@ -1633,12 +1634,17 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version, /// the RPC channel), if provided by it via /// |nvim_set_client_info()|. /// -Dictionary nvim_get_chan_info(Integer chan, Error *err) +Dictionary nvim_get_chan_info(uint64_t channel_id, Integer chan, Error *err) FUNC_API_SINCE(4) { if (chan < 0) { return (Dictionary)ARRAY_DICT_INIT; } + + if (chan == 0 && !is_internal_call(channel_id)) { + assert(channel_id <= INT64_MAX); + chan = (Integer)channel_id; + } return channel_info((uint64_t)chan); } -- cgit From af5beac1bd7a68ff0a4e1a944853bacd6a6c0745 Mon Sep 17 00:00:00 2001 From: bfredl Date: Thu, 8 Feb 2024 13:40:35 +0100 Subject: refactor(api): refactor more api functions to use arena return Currently having two separate memory strategies for API return values is a bit unnecessary, and mostly a consequence of converting the hot spot cases which needed it first. But there is really no downside to using arena everywhere (which implies also directly using strings which are allocated earlier or even statically, without copy). There only restriction is we need to know the size of arrays in advance, but this info can often be passed on from some earlier stage if it is missing. This collects some "small" cases. The more complex stuff will get a PR each. --- src/nvim/api/buffer.c | 21 ++++++------- src/nvim/api/extmark.c | 6 ++-- src/nvim/api/vim.c | 82 ++++++++++++++++++++++++++------------------------ 3 files changed, 55 insertions(+), 54 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 7af2b7241c..751fc1c32d 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -1262,28 +1262,27 @@ Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err) return res; } -Dictionary nvim__buf_stats(Buffer buffer, Error *err) +Dictionary nvim__buf_stats(Buffer buffer, Arena *arena, Error *err) { - Dictionary rv = ARRAY_DICT_INIT; - buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { - return rv; + return (Dictionary)ARRAY_DICT_INIT; } + Dictionary rv = arena_dict(arena, 7); // Number of times the cached line was flushed. // This should generally not increase while editing the same // line in the same mode. - PUT(rv, "flush_count", INTEGER_OBJ(buf->flush_count)); + PUT_C(rv, "flush_count", INTEGER_OBJ(buf->flush_count)); // lnum of current line - PUT(rv, "current_lnum", INTEGER_OBJ(buf->b_ml.ml_line_lnum)); + PUT_C(rv, "current_lnum", INTEGER_OBJ(buf->b_ml.ml_line_lnum)); // whether the line has unflushed changes. - PUT(rv, "line_dirty", BOOLEAN_OBJ(buf->b_ml.ml_flags & ML_LINE_DIRTY)); + PUT_C(rv, "line_dirty", BOOLEAN_OBJ(buf->b_ml.ml_flags & ML_LINE_DIRTY)); // NB: this should be zero at any time API functions are called, // this exists to debug issues - PUT(rv, "dirty_bytes", INTEGER_OBJ((Integer)buf->deleted_bytes)); - PUT(rv, "dirty_bytes2", INTEGER_OBJ((Integer)buf->deleted_bytes2)); - PUT(rv, "virt_blocks", INTEGER_OBJ((Integer)buf_meta_total(buf, kMTMetaLines))); + PUT_C(rv, "dirty_bytes", INTEGER_OBJ((Integer)buf->deleted_bytes)); + PUT_C(rv, "dirty_bytes2", INTEGER_OBJ((Integer)buf->deleted_bytes2)); + PUT_C(rv, "virt_blocks", INTEGER_OBJ((Integer)buf_meta_total(buf, kMTMetaLines))); u_header_T *uhp = NULL; if (buf->b_u_curhead != NULL) { @@ -1292,7 +1291,7 @@ Dictionary nvim__buf_stats(Buffer buffer, Error *err) uhp = buf->b_u_newhead; } if (uhp) { - PUT(rv, "uhp_extmark_size", INTEGER_OBJ((Integer)kv_size(uhp->uh_extmark))); + PUT_C(rv, "uhp_extmark_size", INTEGER_OBJ((Integer)kv_size(uhp->uh_extmark))); } return rv; diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 0948abf065..94f6059014 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -70,15 +70,15 @@ Integer nvim_create_namespace(String name) /// Gets existing, non-anonymous |namespace|s. /// /// @return dict that maps from names to namespace ids. -Dictionary nvim_get_namespaces(void) +Dictionary nvim_get_namespaces(Arena *arena) FUNC_API_SINCE(5) { - Dictionary retval = ARRAY_DICT_INIT; + Dictionary retval = arena_dict(arena, map_size(&namespace_ids)); String name; handle_T id; map_foreach(&namespace_ids, name, id, { - PUT(retval, name.data, INTEGER_OBJ(id)); + PUT_C(retval, name.data, INTEGER_OBJ(id)); }) return retval; diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index b1bfdd79e7..1453de30d4 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1351,14 +1351,13 @@ Integer nvim_get_color_by_name(String name) /// (e.g. 65535). /// /// @return Map of color names and RGB values. -Dictionary nvim_get_color_map(void) +Dictionary nvim_get_color_map(Arena *arena) FUNC_API_SINCE(1) { - Dictionary colors = ARRAY_DICT_INIT; + Dictionary colors = arena_dict(arena, ARRAY_SIZE(color_name_table)); for (int i = 0; color_name_table[i].name != NULL; i++) { - PUT(colors, color_name_table[i].name, - INTEGER_OBJ(color_name_table[i].color)); + PUT_C(colors, color_name_table[i].name, INTEGER_OBJ(color_name_table[i].color)); } return colors; } @@ -1438,16 +1437,16 @@ Object nvim_load_context(Dictionary dict, Error *err) /// "blocking" is true if Nvim is waiting for input. /// /// @returns Dictionary { "mode": String, "blocking": Boolean } -Dictionary nvim_get_mode(void) +Dictionary nvim_get_mode(Arena *arena) FUNC_API_SINCE(2) FUNC_API_FAST { - Dictionary rv = ARRAY_DICT_INIT; - char modestr[MODE_MAX_LENGTH]; + Dictionary rv = arena_dict(arena, 2); + char *modestr = arena_alloc(arena, MODE_MAX_LENGTH, false); get_mode(modestr); bool blocked = input_blocking(); - PUT(rv, "mode", CSTR_TO_OBJ(modestr)); - PUT(rv, "blocking", BOOLEAN_OBJ(blocked)); + PUT_C(rv, "mode", CSTR_AS_OBJ(modestr)); + PUT_C(rv, "blocking", BOOLEAN_OBJ(blocked)); return rv; } @@ -1848,14 +1847,14 @@ Float nvim__id_float(Float flt) /// Gets internal stats. /// /// @return Map of various internal stats. -Dictionary nvim__stats(void) -{ - Dictionary rv = ARRAY_DICT_INIT; - PUT(rv, "fsync", INTEGER_OBJ(g_stats.fsync)); - PUT(rv, "log_skip", INTEGER_OBJ(g_stats.log_skip)); - PUT(rv, "lua_refcount", INTEGER_OBJ(nlua_get_global_ref_count())); - PUT(rv, "redraw", INTEGER_OBJ(g_stats.redraw)); - PUT(rv, "arena_alloc_count", INTEGER_OBJ((Integer)arena_alloc_count)); +Dictionary nvim__stats(Arena *arena) +{ + Dictionary rv = arena_dict(arena, 5); + PUT_C(rv, "fsync", INTEGER_OBJ(g_stats.fsync)); + PUT_C(rv, "log_skip", INTEGER_OBJ(g_stats.log_skip)); + PUT_C(rv, "lua_refcount", INTEGER_OBJ(nlua_get_global_ref_count())); + PUT_C(rv, "redraw", INTEGER_OBJ(g_stats.redraw)); + PUT_C(rv, "arena_alloc_count", INTEGER_OBJ((Integer)arena_alloc_count)); return rv; } @@ -2151,7 +2150,7 @@ Array nvim_get_mark(String name, Dict(empty) *opts, Error *err) /// |Dictionary| with these keys: /// - start: (number) Byte index (0-based) of first character that uses the highlight. /// - group: (string) Name of highlight group. -Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *err) +Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena, Error *err) FUNC_API_SINCE(8) FUNC_API_FAST { Dictionary result = ARRAY_DICT_INIT; @@ -2260,58 +2259,61 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * && global_stl_height() > 0)) ? Columns : wp->w_width; } - char buf[MAXPATHL]; + result = arena_dict(arena, 3); + char *buf = arena_alloc(arena, MAXPATHL, false); stl_hlrec_t *hltab; + size_t hltab_len = 0; // Temporarily reset 'cursorbind' to prevent side effects from moving the cursor away and back. int p_crb_save = wp->w_p_crb; wp->w_p_crb = false; - int width = build_stl_str_hl(wp, buf, sizeof(buf), str.data, -1, 0, fillchar, maxwidth, - opts->highlights ? &hltab : NULL, NULL, + int width = build_stl_str_hl(wp, buf, MAXPATHL, str.data, -1, 0, fillchar, maxwidth, + opts->highlights ? &hltab : NULL, &hltab_len, NULL, statuscol_lnum ? &statuscol : NULL); - PUT(result, "width", INTEGER_OBJ(width)); + PUT_C(result, "width", INTEGER_OBJ(width)); // Restore original value of 'cursorbind' wp->w_p_crb = p_crb_save; if (opts->highlights) { - Array hl_values = ARRAY_DICT_INIT; - const char *grpname; + Array hl_values = arena_array(arena, hltab_len + 1); char user_group[15]; // strlen("User") + strlen("2147483647") + NUL // If first character doesn't have a defined highlight, // add the default highlight at the beginning of the highlight list if (hltab->start == NULL || (hltab->start - buf) != 0) { - Dictionary hl_info = ARRAY_DICT_INIT; - grpname = get_default_stl_hl(opts->use_tabline ? NULL : wp, opts->use_winbar, stc_hl_id); + Dictionary hl_info = arena_dict(arena, 2); + const char *grpname = get_default_stl_hl(opts->use_tabline ? NULL : wp, + opts->use_winbar, stc_hl_id); - PUT(hl_info, "start", INTEGER_OBJ(0)); - PUT(hl_info, "group", CSTR_TO_OBJ(grpname)); + PUT_C(hl_info, "start", INTEGER_OBJ(0)); + PUT_C(hl_info, "group", CSTR_AS_OBJ((char *)grpname)); - ADD(hl_values, DICTIONARY_OBJ(hl_info)); + ADD_C(hl_values, DICTIONARY_OBJ(hl_info)); } for (stl_hlrec_t *sp = hltab; sp->start != NULL; sp++) { - Dictionary hl_info = ARRAY_DICT_INIT; + Dictionary hl_info = arena_dict(arena, 2); - PUT(hl_info, "start", INTEGER_OBJ(sp->start - buf)); + PUT_C(hl_info, "start", INTEGER_OBJ(sp->start - buf)); + const char *grpname; if (sp->userhl == 0) { grpname = get_default_stl_hl(opts->use_tabline ? NULL : wp, opts->use_winbar, stc_hl_id); } else if (sp->userhl < 0) { grpname = syn_id2name(-sp->userhl); } else { snprintf(user_group, sizeof(user_group), "User%d", sp->userhl); - grpname = user_group; + grpname = arena_memdupz(arena, user_group, strlen(user_group)); } - PUT(hl_info, "group", CSTR_TO_OBJ(grpname)); - ADD(hl_values, DICTIONARY_OBJ(hl_info)); + PUT_C(hl_info, "group", CSTR_AS_OBJ((char *)grpname)); + ADD_C(hl_values, DICTIONARY_OBJ(hl_info)); } - PUT(result, "highlights", ARRAY_OBJ(hl_values)); + PUT_C(result, "highlights", ARRAY_OBJ(hl_values)); } - PUT(result, "str", CSTR_TO_OBJ(buf)); + PUT_C(result, "str", CSTR_AS_OBJ(buf)); return result; } @@ -2336,15 +2338,15 @@ void nvim_error_event(uint64_t channel_id, Integer lvl, String data) /// @return Dictionary containing these keys: /// - winid: (number) floating window id /// - bufnr: (number) buffer id in floating window -Dictionary nvim_complete_set(Integer index, Dict(complete_set) *opts) +Dictionary nvim_complete_set(Integer index, Dict(complete_set) *opts, Arena *arena) FUNC_API_SINCE(12) { - Dictionary rv = ARRAY_DICT_INIT; + Dictionary rv = arena_dict(arena, 2); if (HAS_KEY(opts, complete_set, info)) { win_T *wp = pum_set_info((int)index, opts->info.data); if (wp) { - PUT(rv, "winid", WINDOW_OBJ(wp->handle)); - PUT(rv, "bufnr", BUFFER_OBJ(wp->w_buffer->handle)); + PUT_C(rv, "winid", WINDOW_OBJ(wp->handle)); + PUT_C(rv, "bufnr", BUFFER_OBJ(wp->w_buffer->handle)); } } return rv; -- cgit From e0e5b7f0ba1b0440bdc2b557e2b2cfae24706cbd Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 9 Feb 2024 11:42:40 +0100 Subject: refactor(api): make cstr_as_string accept "const char*" In the context a String inside an Object/Dictionary etc is consumed, it is considered to be read-only. --- src/nvim/api/private/helpers.c | 4 ++-- src/nvim/api/ui.c | 4 ++-- src/nvim/api/vim.c | 4 ++-- src/nvim/api/win_config.c | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index cc95f46baf..8b45af7c71 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -425,12 +425,12 @@ String cstrn_as_string(char *str, size_t maxsize) /// @param str the C string to use /// @return The resulting String, or an empty String if /// str was NULL -String cstr_as_string(char *str) FUNC_ATTR_PURE +String cstr_as_string(const char *str) FUNC_ATTR_PURE { if (str == NULL) { return (String)STRING_INIT; } - return (String){ .data = str, .size = strlen(str) }; + return (String){ .data = (char *)str, .size = strlen(str) }; } /// Return the owned memory of a ga as a String diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index f955b315a8..c6e868ca88 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -789,7 +789,7 @@ void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cte // system. So we add them here. if (rgb_attrs.url >= 0) { const char *url = hl_get_url((uint32_t)rgb_attrs.url); - PUT_C(rgb, "url", STRING_OBJ(cstr_as_string((char *)url))); + PUT_C(rgb, "url", CSTR_AS_OBJ(url)); } ADD_C(args, DICTIONARY_OBJ(rgb)); @@ -857,7 +857,7 @@ void remote_ui_put(UI *ui, const char *cell) UIData *data = ui->data; data->client_col++; Array args = data->call_buf; - ADD_C(args, CSTR_AS_OBJ((char *)cell)); + ADD_C(args, CSTR_AS_OBJ(cell)); push_call(ui, "put", args); } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 1453de30d4..6b46f579e4 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2289,7 +2289,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena * opts->use_winbar, stc_hl_id); PUT_C(hl_info, "start", INTEGER_OBJ(0)); - PUT_C(hl_info, "group", CSTR_AS_OBJ((char *)grpname)); + PUT_C(hl_info, "group", CSTR_AS_OBJ(grpname)); ADD_C(hl_values, DICTIONARY_OBJ(hl_info)); } @@ -2308,7 +2308,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena * snprintf(user_group, sizeof(user_group), "User%d", sp->userhl); grpname = arena_memdupz(arena, user_group, strlen(user_group)); } - PUT_C(hl_info, "group", CSTR_AS_OBJ((char *)grpname)); + PUT_C(hl_info, "group", CSTR_AS_OBJ(grpname)); ADD_C(hl_values, DICTIONARY_OBJ(hl_info)); } PUT_C(result, "highlights", ARRAY_OBJ(hl_values)); diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index a9d7a8d05c..8e299d264c 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -626,7 +626,7 @@ Dict(float_config) nvim_win_get_config(Window window, Arena *arena, Error *err) PUT_KEY_X(rv, bufpos, pos); } } - PUT_KEY_X(rv, anchor, cstr_as_string((char *)float_anchor_str[config->anchor])); + PUT_KEY_X(rv, anchor, cstr_as_string(float_anchor_str[config->anchor])); PUT_KEY_X(rv, row, config->row); PUT_KEY_X(rv, col, config->col); PUT_KEY_X(rv, zindex, config->zindex); @@ -659,12 +659,12 @@ Dict(float_config) nvim_win_get_config(Window window, Arena *arena, Error *err) PUT_KEY_X(rv, width, wp->w_width); PUT_KEY_X(rv, height, wp->w_height); WinSplit split = win_split_dir(wp); - PUT_KEY_X(rv, split, cstr_as_string((char *)win_split_str[split])); + PUT_KEY_X(rv, split, cstr_as_string(win_split_str[split])); } const char *rel = (wp->w_floating && !config->external ? float_relative_str[config->relative] : ""); - PUT_KEY_X(rv, relative, cstr_as_string((char *)rel)); + PUT_KEY_X(rv, relative, cstr_as_string(rel)); return rv; } -- cgit From 44ec4b5b18d1ba856dc3305d8dfb0e8d9507dd50 Mon Sep 17 00:00:00 2001 From: Will Hopkins Date: Fri, 9 Feb 2024 08:17:10 -0800 Subject: refactor: rename FloatConfig to WinConfig #27397 `FloatConfig` is no longer used only for floats, so the name is counterintuitive. Followup to #25550 --- src/nvim/api/keysets_defs.h | 4 ++-- src/nvim/api/win_config.c | 34 +++++++++++++++++----------------- 2 files changed, 19 insertions(+), 19 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index 2f1b38d1e5..0ba33ca9a7 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -108,7 +108,7 @@ typedef struct { } Dict(user_command); typedef struct { - OptionalKeys is_set__float_config_; + OptionalKeys is_set__win_config_; Float row; Float col; Integer width; @@ -131,7 +131,7 @@ typedef struct { Boolean noautocmd; Boolean fixed; Boolean hide; -} Dict(float_config); +} Dict(win_config); typedef struct { Boolean is_lua; diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index a9d7a8d05c..22a321149b 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -200,10 +200,10 @@ /// @param[out] err Error details, if any /// /// @return Window handle, or 0 on error -Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, Error *err) +Window nvim_open_win(Buffer buffer, Boolean enter, Dict(win_config) *config, Error *err) FUNC_API_SINCE(6) FUNC_API_TEXTLOCK_ALLOW_CMDWIN { -#define HAS_KEY_X(d, key) HAS_KEY(d, float_config, key) +#define HAS_KEY_X(d, key) HAS_KEY(d, win_config, key) buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { return 0; @@ -213,7 +213,7 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E return 0; } - FloatConfig fconfig = FLOAT_CONFIG_INIT; + WinConfig fconfig = WIN_CONFIG_INIT; if (!parse_float_config(config, &fconfig, false, true, err)) { return 0; } @@ -332,10 +332,10 @@ static int win_split_flags(WinSplit split, bool toplevel) /// @param config Map defining the window configuration, /// see |nvim_open_win()| /// @param[out] err Error details, if any -void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err) +void nvim_win_set_config(Window window, Dict(win_config) *config, Error *err) FUNC_API_SINCE(6) { -#define HAS_KEY_X(d, key) HAS_KEY(d, float_config, key) +#define HAS_KEY_X(d, key) HAS_KEY(d, win_config, key) win_T *win = find_window_by_handle(window, err); if (!win) { return; @@ -345,7 +345,7 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err) bool has_split = HAS_KEY_X(config, split); bool has_vertical = HAS_KEY_X(config, vertical); // reuse old values, if not overridden - FloatConfig fconfig = win->w_float_config; + WinConfig fconfig = win->w_float_config; bool to_split = config->relative.size == 0 && !(HAS_KEY_X(config, external) ? config->external : fconfig.external) @@ -539,8 +539,8 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err) #undef HAS_KEY_X } -#define PUT_KEY_X(d, key, value) PUT_KEY(d, float_config, key, value) -static void config_put_bordertext(Dict(float_config) *config, FloatConfig *fconfig, +#define PUT_KEY_X(d, key, value) PUT_KEY(d, win_config, key, value) +static void config_put_bordertext(Dict(win_config) *config, WinConfig *fconfig, BorderTextType bordertext_type, Arena *arena) { VirtText vt; @@ -591,7 +591,7 @@ static void config_put_bordertext(Dict(float_config) *config, FloatConfig *fconf /// @param window Window handle, or 0 for current window /// @param[out] err Error details, if any /// @return Map defining the window configuration, see |nvim_open_win()| -Dict(float_config) nvim_win_get_config(Window window, Arena *arena, Error *err) +Dict(win_config) nvim_win_get_config(Window window, Arena *arena, Error *err) FUNC_API_SINCE(6) { /// Keep in sync with FloatRelative in buffer_defs.h @@ -600,14 +600,14 @@ Dict(float_config) nvim_win_get_config(Window window, Arena *arena, Error *err) /// Keep in sync with WinSplit in buffer_defs.h static const char *const win_split_str[] = { "left", "right", "above", "below" }; - Dict(float_config) rv = { 0 }; + Dict(win_config) rv = { 0 }; win_T *wp = find_window_by_handle(window, err); if (!wp) { return rv; } - FloatConfig *config = &wp->w_float_config; + WinConfig *config = &wp->w_float_config; PUT_KEY_X(rv, focusable, config->focusable); PUT_KEY_X(rv, external, config->external); @@ -734,8 +734,8 @@ static bool parse_float_bufpos(Array bufpos, lpos_T *out) return true; } -static void parse_bordertext(Object bordertext, BorderTextType bordertext_type, - FloatConfig *fconfig, Error *err) +static void parse_bordertext(Object bordertext, BorderTextType bordertext_type, WinConfig *fconfig, + Error *err) { if (bordertext.type != kObjectTypeString && bordertext.type != kObjectTypeArray) { api_set_error(err, kErrorTypeValidation, "title/footer must be string or array"); @@ -793,7 +793,7 @@ static void parse_bordertext(Object bordertext, BorderTextType bordertext_type, } static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertext_type, - FloatConfig *fconfig, Error *err) + WinConfig *fconfig, Error *err) { AlignTextPos *align; switch (bordertext_type) { @@ -832,7 +832,7 @@ static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertex return true; } -static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) +static void parse_border_style(Object style, WinConfig *fconfig, Error *err) { struct { const char *name; @@ -937,10 +937,10 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) } } -static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, bool reconf, +static bool parse_float_config(Dict(win_config) *config, WinConfig *fconfig, bool reconf, bool new_win, Error *err) { -#define HAS_KEY_X(d, key) HAS_KEY(d, float_config, key) +#define HAS_KEY_X(d, key) HAS_KEY(d, win_config, key) bool has_relative = false, relative_is_win = false, is_split = false; if (config->relative.size > 0) { if (!parse_float_relative(config->relative, &fconfig->relative)) { -- cgit From e9510211f0b957606685344be97350c29e3ea638 Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 9 Feb 2024 12:40:48 +0100 Subject: refactor(api): use arena for nvim_list_uis() --- src/nvim/api/ui.c | 11 ++--------- src/nvim/api/vim.c | 4 ++-- 2 files changed, 4 insertions(+), 11 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index c6e868ca88..5c8ebfb861 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -247,10 +247,9 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona void ui_attach(uint64_t channel_id, Integer width, Integer height, Boolean enable_rgb, Error *err) FUNC_API_DEPRECATED_SINCE(1) { - Dictionary opts = ARRAY_DICT_INIT; - PUT(opts, "rgb", BOOLEAN_OBJ(enable_rgb)); + MAXSIZE_TEMP_DICT(opts, 1); + PUT_C(opts, "rgb", BOOLEAN_OBJ(enable_rgb)); nvim_ui_attach(channel_id, width, height, opts, err); - api_free_dictionary(opts); } /// Tells the nvim server if focus was gained or lost by the GUI @@ -1113,9 +1112,3 @@ void remote_ui_event(UI *ui, char *name, Array args) free_ret: arena_mem_free(arena_finish(&arena)); } - -void remote_ui_inspect(UI *ui, Dictionary *info) -{ - UIData *data = ui->data; - PUT(*info, "chan", INTEGER_OBJ((Integer)data->channel_id)); -} diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 6b46f579e4..5a106288fd 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1866,10 +1866,10 @@ Dictionary nvim__stats(Arena *arena) /// - "rgb" true if the UI uses RGB colors (false implies |cterm-colors|) /// - "ext_..." Requested UI extensions, see |ui-option| /// - "chan" |channel-id| of remote UI -Array nvim_list_uis(void) +Array nvim_list_uis(Arena *arena) FUNC_API_SINCE(4) { - return ui_array(); + return ui_array(arena); } /// Gets the immediate children of process `pid`. -- cgit From 4cd43933d355b5b39b1107bfd08ad9205681f2f4 Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 9 Feb 2024 12:41:14 +0100 Subject: refactor(api): use arena for hlstate inspection --- src/nvim/api/vim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 5a106288fd..a47694a0cf 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2005,7 +2005,7 @@ Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Arena *arena, E ADD_C(ret, DICTIONARY_OBJ(hl_get_attr_by_id(attr, true, arena, err))); // will not work first time if (!highlight_use_hlstate()) { - ADD_C(ret, ARRAY_OBJ(hl_inspect(attr))); + ADD_C(ret, ARRAY_OBJ(hl_inspect(attr, arena))); } return ret; } -- cgit From 2d8d35f838e0f9737a8b0231e94d35315e54ca8f Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 9 Feb 2024 10:06:50 +0100 Subject: refactor(api): use arena for nvim_parse_cmd() --- src/nvim/api/command.c | 127 +++++++++++++++++++---------------------- src/nvim/api/private/helpers.h | 3 +- 2 files changed, 61 insertions(+), 69 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index db0a918f5c..bafc45e543 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -96,15 +96,15 @@ /// - "belowright": |:belowright|. /// - "topleft": |:topleft|. /// - "botright": |:botright|. -Dictionary nvim_parse_cmd(String str, Dict(empty) *opts, Error *err) +Dict(cmd) nvim_parse_cmd(String str, Dict(empty) *opts, Arena *arena, Error *err) FUNC_API_SINCE(10) FUNC_API_FAST { - Dictionary result = ARRAY_DICT_INIT; + Dict(cmd) result = { 0 }; // Parse command line exarg_T ea; CmdParseInfo cmdinfo; - char *cmdline = string_to_cstr(str); + char *cmdline = arena_memdupz(arena, str.data, str.size); const char *errormsg = NULL; if (!parse_cmdline(cmdline, &ea, &cmdinfo, &errormsg)) { @@ -124,22 +124,23 @@ Dictionary nvim_parse_cmd(String str, Dict(empty) *opts, Error *err) // otherwise split arguments by whitespace. if (ea.argt & EX_NOSPC) { if (*ea.arg != NUL) { - ADD(args, STRING_OBJ(cstrn_to_string(ea.arg, length))); + args = arena_array(arena, 1); + ADD_C(args, STRING_OBJ(cstrn_as_string(ea.arg, length))); } } else { size_t end = 0; size_t len = 0; - char *buf = xcalloc(length, sizeof(char)); + char *buf = arena_alloc(arena, length + 1, false); bool done = false; + args = arena_array(arena, uc_nargs_upper_bound(ea.arg, length)); while (!done) { done = uc_split_args_iter(ea.arg, length, &end, buf, &len); if (len > 0) { - ADD(args, STRING_OBJ(cstrn_to_string(buf, len))); + ADD_C(args, STRING_OBJ(cstrn_as_string(buf, len))); + buf += len + 1; } } - - xfree(buf); } ucmd_T *cmd = NULL; @@ -149,40 +150,32 @@ Dictionary nvim_parse_cmd(String str, Dict(empty) *opts, Error *err) cmd = USER_CMD_GA(&curbuf->b_ucmds, ea.useridx); } - if (cmd != NULL) { - PUT(result, "cmd", CSTR_TO_OBJ(cmd->uc_name)); - } else { - PUT(result, "cmd", CSTR_TO_OBJ(get_command_name(NULL, ea.cmdidx))); - } + char *name = (cmd != NULL ? cmd->uc_name : get_command_name(NULL, ea.cmdidx)); + PUT_KEY(result, cmd, cmd, cstr_as_string(name)); if (ea.argt & EX_RANGE) { - Array range = ARRAY_DICT_INIT; + Array range = arena_array(arena, 2); if (ea.addr_count > 0) { if (ea.addr_count > 1) { - ADD(range, INTEGER_OBJ(ea.line1)); + ADD_C(range, INTEGER_OBJ(ea.line1)); } - ADD(range, INTEGER_OBJ(ea.line2)); + ADD_C(range, INTEGER_OBJ(ea.line2)); } - PUT(result, "range", ARRAY_OBJ(range)); + PUT_KEY(result, cmd, range, range); } if (ea.argt & EX_COUNT) { - if (ea.addr_count > 0) { - PUT(result, "count", INTEGER_OBJ(ea.line2)); - } else if (cmd != NULL) { - PUT(result, "count", INTEGER_OBJ(cmd->uc_def)); - } else { - PUT(result, "count", INTEGER_OBJ(0)); - } + Integer count = ea.addr_count > 0 ? ea.line2 : (cmd != NULL ? cmd->uc_def : 0); + PUT_KEY(result, cmd, count, count); } if (ea.argt & EX_REGSTR) { char reg[2] = { (char)ea.regname, NUL }; - PUT(result, "reg", CSTR_TO_OBJ(reg)); + PUT_KEY(result, cmd, reg, CSTR_TO_ARENA_STR(arena, reg)); } - PUT(result, "bang", BOOLEAN_OBJ(ea.forceit)); - PUT(result, "args", ARRAY_OBJ(args)); + PUT_KEY(result, cmd, bang, ea.forceit); + PUT_KEY(result, cmd, args, args); char nargs[2]; if (ea.argt & EX_EXTRA) { @@ -201,9 +194,9 @@ Dictionary nvim_parse_cmd(String str, Dict(empty) *opts, Error *err) nargs[0] = '0'; } nargs[1] = '\0'; - PUT(result, "nargs", CSTR_TO_OBJ(nargs)); + PUT_KEY(result, cmd, nargs, CSTR_TO_ARENA_OBJ(arena, nargs)); - const char *addr; + char *addr; switch (ea.addr_type) { case ADDR_LINES: addr = "line"; @@ -233,38 +226,37 @@ Dictionary nvim_parse_cmd(String str, Dict(empty) *opts, Error *err) addr = "?"; break; } - PUT(result, "addr", CSTR_TO_OBJ(addr)); - PUT(result, "nextcmd", CSTR_TO_OBJ(ea.nextcmd)); - - Dictionary mods = ARRAY_DICT_INIT; - - Dictionary filter = ARRAY_DICT_INIT; - PUT(filter, "pattern", cmdinfo.cmdmod.cmod_filter_pat - ? CSTR_TO_OBJ(cmdinfo.cmdmod.cmod_filter_pat) - : STATIC_CSTR_TO_OBJ("")); - PUT(filter, "force", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_filter_force)); - PUT(mods, "filter", DICTIONARY_OBJ(filter)); - - PUT(mods, "silent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_SILENT)); - PUT(mods, "emsg_silent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_ERRSILENT)); - PUT(mods, "unsilent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_UNSILENT)); - PUT(mods, "sandbox", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_SANDBOX)); - PUT(mods, "noautocmd", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_NOAUTOCMD)); - PUT(mods, "tab", INTEGER_OBJ(cmdinfo.cmdmod.cmod_tab - 1)); - PUT(mods, "verbose", INTEGER_OBJ(cmdinfo.cmdmod.cmod_verbose - 1)); - PUT(mods, "browse", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_BROWSE)); - PUT(mods, "confirm", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_CONFIRM)); - PUT(mods, "hide", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_HIDE)); - PUT(mods, "keepalt", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPALT)); - PUT(mods, "keepjumps", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPJUMPS)); - PUT(mods, "keepmarks", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPMARKS)); - PUT(mods, "keeppatterns", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPPATTERNS)); - PUT(mods, "lockmarks", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_LOCKMARKS)); - PUT(mods, "noswapfile", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_NOSWAPFILE)); - PUT(mods, "vertical", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_split & WSP_VERT)); - PUT(mods, "horizontal", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_split & WSP_HOR)); - - const char *split; + PUT_KEY(result, cmd, addr, CSTR_AS_OBJ(addr)); + PUT_KEY(result, cmd, nextcmd, CSTR_AS_OBJ(ea.nextcmd)); + + // TODO(bfredl): nested keydict would be nice.. + Dictionary mods = arena_dict(arena, 20); + + Dictionary filter = arena_dict(arena, 2); + PUT_C(filter, "pattern", CSTR_TO_ARENA_OBJ(arena, cmdinfo.cmdmod.cmod_filter_pat)); + PUT_C(filter, "force", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_filter_force)); + PUT_C(mods, "filter", DICTIONARY_OBJ(filter)); + + PUT_C(mods, "silent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_SILENT)); + PUT_C(mods, "emsg_silent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_ERRSILENT)); + PUT_C(mods, "unsilent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_UNSILENT)); + PUT_C(mods, "sandbox", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_SANDBOX)); + PUT_C(mods, "noautocmd", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_NOAUTOCMD)); + PUT_C(mods, "tab", INTEGER_OBJ(cmdinfo.cmdmod.cmod_tab - 1)); + PUT_C(mods, "verbose", INTEGER_OBJ(cmdinfo.cmdmod.cmod_verbose - 1)); + PUT_C(mods, "browse", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_BROWSE)); + PUT_C(mods, "confirm", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_CONFIRM)); + PUT_C(mods, "hide", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_HIDE)); + PUT_C(mods, "keepalt", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPALT)); + PUT_C(mods, "keepjumps", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPJUMPS)); + PUT_C(mods, "keepmarks", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPMARKS)); + PUT_C(mods, "keeppatterns", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPPATTERNS)); + PUT_C(mods, "lockmarks", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_LOCKMARKS)); + PUT_C(mods, "noswapfile", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_NOSWAPFILE)); + PUT_C(mods, "vertical", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_split & WSP_VERT)); + PUT_C(mods, "horizontal", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_split & WSP_HOR)); + + char *split; if (cmdinfo.cmdmod.cmod_split & WSP_BOT) { split = "botright"; } else if (cmdinfo.cmdmod.cmod_split & WSP_TOP) { @@ -276,18 +268,17 @@ Dictionary nvim_parse_cmd(String str, Dict(empty) *opts, Error *err) } else { split = ""; } - PUT(mods, "split", CSTR_TO_OBJ(split)); + PUT_C(mods, "split", CSTR_AS_OBJ(split)); - PUT(result, "mods", DICTIONARY_OBJ(mods)); + PUT_KEY(result, cmd, mods, mods); - Dictionary magic = ARRAY_DICT_INIT; - PUT(magic, "file", BOOLEAN_OBJ(cmdinfo.magic.file)); - PUT(magic, "bar", BOOLEAN_OBJ(cmdinfo.magic.bar)); - PUT(result, "magic", DICTIONARY_OBJ(magic)); + Dictionary magic = arena_dict(arena, 2); + PUT_C(magic, "file", BOOLEAN_OBJ(cmdinfo.magic.file)); + PUT_C(magic, "bar", BOOLEAN_OBJ(cmdinfo.magic.bar)); + PUT_KEY(result, cmd, magic, magic); undo_cmdmod(&cmdinfo.cmdmod); end: - xfree(cmdline); return result; } diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 0dbe3d2fb6..9ee812f45c 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -32,7 +32,8 @@ #define CSTR_AS_OBJ(s) STRING_OBJ(cstr_as_string(s)) #define CSTR_TO_OBJ(s) STRING_OBJ(cstr_to_string(s)) -#define CSTR_TO_ARENA_OBJ(arena, s) STRING_OBJ(arena_string(arena, cstr_as_string(s))) +#define CSTR_TO_ARENA_STR(arena, s) arena_string(arena, cstr_as_string(s)) +#define CSTR_TO_ARENA_OBJ(arena, s) STRING_OBJ(CSTR_TO_ARENA_STR(arena, s)) #define BUFFER_OBJ(s) ((Object) { \ .type = kObjectTypeBuffer, \ -- cgit From 2d0e29614b4417e9764b9f7d588a50fe7f752749 Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 9 Feb 2024 11:35:02 +0100 Subject: refactor(api): use arena for nvim_get_option_info() --- src/nvim/api/deprecated.c | 4 ++-- src/nvim/api/options.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index b09645a819..27f0589e53 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -514,11 +514,11 @@ static int64_t convert_index(int64_t index) /// @param name Option name /// @param[out] err Error details, if any /// @return Option Information -Dictionary nvim_get_option_info(String name, Error *err) +Dictionary nvim_get_option_info(String name, Arena *arena, Error *err) FUNC_API_SINCE(7) FUNC_API_DEPRECATED_SINCE(11) { - return get_vimoption(name, OPT_GLOBAL, curbuf, curwin, err); + return get_vimoption(name, OPT_GLOBAL, curbuf, curwin, arena, err); } /// Sets the global value of an option. diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index fce4a85804..8128fdf67b 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -263,10 +263,10 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict( /// @see |nvim_get_commands()| /// /// @return dictionary of all options -Dictionary nvim_get_all_options_info(Error *err) +Dictionary nvim_get_all_options_info(Arena *arena, Error *err) FUNC_API_SINCE(7) { - return get_all_vimoptions(); + return get_all_vimoptions(arena); } /// Gets the option information for one option from arbitrary buffer or window @@ -302,7 +302,7 @@ Dictionary nvim_get_all_options_info(Error *err) /// Implies {scope} is "local". /// @param[out] err Error details, if any /// @return Option Information -Dictionary nvim_get_option_info2(String name, Dict(option) *opts, Error *err) +Dictionary nvim_get_option_info2(String name, Dict(option) *opts, Arena *arena, Error *err) FUNC_API_SINCE(11) { OptIndex opt_idx = 0; @@ -317,5 +317,5 @@ Dictionary nvim_get_option_info2(String name, Dict(option) *opts, Error *err) buf_T *buf = (req_scope == kOptReqBuf) ? (buf_T *)from : curbuf; win_T *win = (req_scope == kOptReqWin) ? (win_T *)from : curwin; - return get_vimoption(name, scope, buf, win, err); + return get_vimoption(name, scope, buf, win, arena, err); } -- cgit From f1f8fa850f741b7c35b8ff9c02ac2810a75e25b1 Mon Sep 17 00:00:00 2001 From: Will Hopkins Date: Sat, 10 Feb 2024 13:06:01 -0800 Subject: refactor: rename w_float_config to w_config #27419 Follows up on rename of `FloatConfig` to `WinConfig` in #27397. --- src/nvim/api/win_config.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 8841bd225b..52372b838e 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -257,7 +257,7 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(win_config) *config, Err restore_win(&switchwin, true); } if (wp) { - wp->w_float_config = fconfig; + wp->w_config = fconfig; } } else { wp = win_new_float(NULL, false, fconfig, err); @@ -345,7 +345,7 @@ void nvim_win_set_config(Window window, Dict(win_config) *config, Error *err) bool has_split = HAS_KEY_X(config, split); bool has_vertical = HAS_KEY_X(config, vertical); // reuse old values, if not overridden - WinConfig fconfig = win->w_float_config; + WinConfig fconfig = win->w_config; bool to_split = config->relative.size == 0 && !(HAS_KEY_X(config, external) ? config->external : fconfig.external) @@ -387,7 +387,7 @@ void nvim_win_set_config(Window window, Dict(win_config) *config, Error *err) } } } - win->w_float_config = fconfig; + win->w_config = fconfig; // If there's no "vertical" or "split" set, or if "split" is unchanged, // then we can just change the size of the window. @@ -477,7 +477,7 @@ void nvim_win_set_config(Window window, Dict(win_config) *config, Error *err) } else { win_remove(win, win_tp == curtab ? NULL : win_tp); ui_comp_remove_grid(&win->w_grid_alloc); - if (win->w_float_config.external) { + if (win->w_config.external) { for (tabpage_T *tp = first_tabpage; tp != NULL; tp = tp->tp_next) { if (tp == curtab) { continue; @@ -607,7 +607,7 @@ Dict(win_config) nvim_win_get_config(Window window, Arena *arena, Error *err) return rv; } - WinConfig *config = &wp->w_float_config; + WinConfig *config = &wp->w_config; PUT_KEY_X(rv, focusable, config->focusable); PUT_KEY_X(rv, external, config->external); -- cgit From 930d6e38d4eb61adbb4f0d7328f55b31408b7cd1 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sat, 10 Feb 2024 13:13:52 +0100 Subject: refactor(api): use an arena for nvim_buf_get_lines and buffer updates Refactor some earlier "temporary Array" code in buffer_updates.c to use the modern style of MAXSIZE_TEMP_ARRAY and ADD_C --- src/nvim/api/buffer.c | 75 ++++++++++++++++------------------------------- src/nvim/api/deprecated.c | 9 +++--- src/nvim/api/vim.c | 4 +-- 3 files changed, 32 insertions(+), 56 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 751fc1c32d..183483b329 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -264,6 +264,7 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id, Integer start, Integer end, Boolean strict_indexing, + Arena *arena, lua_State *lstate, Error *err) FUNC_API_SINCE(1) @@ -295,18 +296,10 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id, size_t size = (size_t)(end - start); - init_line_array(lstate, &rv, size); + init_line_array(lstate, &rv, size, arena); - if (!buf_collect_lines(buf, size, (linenr_T)start, 0, (channel_id != VIML_INTERNAL_CALL), &rv, - lstate, err)) { - goto end; - } - -end: - if (ERROR_SET(err)) { - api_free_array(rv); - rv.items = NULL; - } + buf_collect_lines(buf, size, (linenr_T)start, 0, (channel_id != VIML_INTERNAL_CALL), &rv, + lstate, arena); return rv; } @@ -763,8 +756,8 @@ early_end: ArrayOf(String) nvim_buf_get_text(uint64_t channel_id, Buffer buffer, Integer start_row, Integer start_col, Integer end_row, Integer end_col, - Dict(empty) *opts, lua_State *lstate, - Error *err) + Dict(empty) *opts, + Arena *arena, lua_State *lstate, Error *err) FUNC_API_SINCE(9) { Array rv = ARRAY_DICT_INIT; @@ -799,44 +792,38 @@ ArrayOf(String) nvim_buf_get_text(uint64_t channel_id, Buffer buffer, size_t size = (size_t)(end_row - start_row) + 1; - init_line_array(lstate, &rv, size); + init_line_array(lstate, &rv, size, arena); if (start_row == end_row) { String line = buf_get_text(buf, start_row, start_col, end_col, err); if (ERROR_SET(err)) { goto end; } - push_linestr(lstate, &rv, line.data, line.size, 0, replace_nl); + push_linestr(lstate, &rv, line.data, line.size, 0, replace_nl, arena); return rv; } String str = buf_get_text(buf, start_row, start_col, MAXCOL - 1, err); - - push_linestr(lstate, &rv, str.data, str.size, 0, replace_nl); - if (ERROR_SET(err)) { goto end; } + push_linestr(lstate, &rv, str.data, str.size, 0, replace_nl, arena); + if (size > 2) { - if (!buf_collect_lines(buf, size - 2, (linenr_T)start_row + 1, 1, replace_nl, &rv, lstate, - err)) { - goto end; - } + buf_collect_lines(buf, size - 2, (linenr_T)start_row + 1, 1, replace_nl, &rv, lstate, arena); } str = buf_get_text(buf, end_row, 0, end_col, err); - push_linestr(lstate, &rv, str.data, str.size, (int)(size - 1), replace_nl); - if (ERROR_SET(err)) { goto end; } + push_linestr(lstate, &rv, str.data, str.size, (int)(size - 1), replace_nl, arena); + end: if (ERROR_SET(err)) { - api_free_array(rv); - rv.size = 0; - rv.items = NULL; + return (Array)ARRAY_DICT_INIT; } return rv; @@ -1394,13 +1381,12 @@ static void fix_cursor_cols(win_T *win, linenr_T start_row, colnr_T start_col, l /// @param lstate Lua state. When NULL the Array is initialized instead. /// @param a Array to initialize /// @param size Size of array -static inline void init_line_array(lua_State *lstate, Array *a, size_t size) +static inline void init_line_array(lua_State *lstate, Array *a, size_t size, Arena *arena) { if (lstate) { lua_createtable(lstate, (int)size, 0); } else { - a->size = size; - a->items = xcalloc(a->size, sizeof(Object)); + *a = arena_array(arena, size); } } @@ -1413,14 +1399,15 @@ static inline void init_line_array(lua_State *lstate, Array *a, size_t size) /// @param a Array to push onto when not using Lua /// @param s String to push /// @param len Size of string -/// @param idx 0-based index to place s +/// @param idx 0-based index to place s (only used for Lua) /// @param replace_nl Replace newlines ('\n') with null ('\0') static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len, int idx, - bool replace_nl) + bool replace_nl, Arena *arena) { if (lstate) { // Vim represents NULs as NLs if (s && replace_nl && strchr(s, '\n')) { + // TODO(bfredl): could manage scratch space in the arena, for the NUL case char *tmp = xmemdupz(s, len); strchrsub(tmp, '\n', '\0'); lua_pushlstring(lstate, tmp, len); @@ -1431,15 +1418,15 @@ static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len, lua_rawseti(lstate, -2, idx + 1); } else { String str = STRING_INIT; - if (s) { - str = cbuf_to_string(s, len); + if (len > 0) { + str = arena_string(arena, cbuf_as_string((char *)s, len)); if (replace_nl) { // Vim represents NULs as NLs, but this may confuse clients. strchrsub(str.data, '\n', '\0'); } } - a->items[idx] = STRING_OBJ(str); + ADD_C(*a, STRING_OBJ(str)); } } @@ -1450,27 +1437,17 @@ static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len, /// @param n Number of lines to collect /// @param replace_nl Replace newlines ("\n") with NUL /// @param start Line number to start from -/// @param start_idx First index to push to +/// @param start_idx First index to push to (only used for Lua) /// @param[out] l If not NULL, Lines are copied here /// @param[out] lstate If not NULL, Lines are pushed into a table onto the stack /// @param err[out] Error, if any /// @return true unless `err` was set -bool buf_collect_lines(buf_T *buf, size_t n, linenr_T start, int start_idx, bool replace_nl, - Array *l, lua_State *lstate, Error *err) +void buf_collect_lines(buf_T *buf, size_t n, linenr_T start, int start_idx, bool replace_nl, + Array *l, lua_State *lstate, Arena *arena) { for (size_t i = 0; i < n; i++) { linenr_T lnum = start + (linenr_T)i; - - if (lnum >= MAXLNUM) { - if (err != NULL) { - api_set_error(err, kErrorTypeValidation, "Line index is too high"); - } - return false; - } - char *bufstr = ml_get_buf(buf, lnum); - push_linestr(lstate, l, bufstr, strlen(bufstr), start_idx + (int)i, replace_nl); + push_linestr(lstate, l, bufstr, strlen(bufstr), start_idx + (int)i, replace_nl, arena); } - - return true; } diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 27f0589e53..dccaeb6922 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -245,20 +245,18 @@ void buffer_insert(Buffer buffer, Integer lnum, ArrayOf(String) lines, Error *er /// @param index Line index /// @param[out] err Error details, if any /// @return Line string -String buffer_get_line(Buffer buffer, Integer index, Error *err) +String buffer_get_line(Buffer buffer, Integer index, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { String rv = { .size = 0 }; index = convert_index(index); - Array slice = nvim_buf_get_lines(0, buffer, index, index + 1, true, NULL, err); + Array slice = nvim_buf_get_lines(0, buffer, index, index + 1, true, arena, NULL, err); if (!ERROR_SET(err) && slice.size) { rv = slice.items[0].data.string; } - xfree(slice.items); - return rv; } @@ -319,12 +317,13 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer, Integer end, Boolean include_start, Boolean include_end, + Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { start = convert_index(start) + !include_start; end = convert_index(end) + include_end; - return nvim_buf_get_lines(0, buffer, start, end, false, NULL, err); + return nvim_buf_get_lines(0, buffer, start, end, false, arena, NULL, err); } /// Replaces a line range on the buffer diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index a47694a0cf..78c2561bbd 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -656,10 +656,10 @@ void nvim_set_current_dir(String dir, Error *err) /// /// @param[out] err Error details, if any /// @return Current line string -String nvim_get_current_line(Error *err) +String nvim_get_current_line(Arena *arena, Error *err) FUNC_API_SINCE(1) { - return buffer_get_line(curbuf->handle, curwin->w_cursor.lnum - 1, err); + return buffer_get_line(curbuf->handle, curwin->w_cursor.lnum - 1, arena, err); } /// Sets the current line. -- cgit From 0353dd3029f9ce31c3894530385443a90f6677ee Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 11 Feb 2024 15:46:14 +0100 Subject: refactor(lua): use Arena when converting from lua stack to API args and for return value of nlua_exec/nlua_call_ref, as this uses the same family of functions. NB: the handling of luaref:s is a bit of a mess. add api_luarefs_free_XX functions as a stop-gap as refactoring luarefs is a can of worms for another PR:s. as a minor feature/bug-fix, nvim_buf_call and nvim_win_call now preserves arbitrary return values. --- src/nvim/api/autocmd.c | 3 ++- src/nvim/api/buffer.c | 7 +++-- src/nvim/api/command.c | 14 +++++----- src/nvim/api/deprecated.c | 4 +-- src/nvim/api/private/helpers.c | 50 ++++++++++++++++++++++++++++------- src/nvim/api/private/helpers.h | 3 +++ src/nvim/api/vim.c | 60 ++++++++++++++++++++---------------------- src/nvim/api/win_config.c | 2 +- src/nvim/api/window.c | 5 ++-- 9 files changed, 91 insertions(+), 57 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 78628c4154..4f67f682f1 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -431,7 +431,8 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc }); cb.type = kCallbackLua; - cb.data.luaref = api_new_luaref(callback->data.luaref); + cb.data.luaref = callback->data.luaref; + callback->data.luaref = LUA_NOREF; break; case kObjectTypeString: cb.type = kCallbackFuncref; diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 183483b329..993e290b2d 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -1227,8 +1227,7 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err) /// @param fun Function to call inside the buffer (currently Lua callable /// only) /// @param[out] err Error details, if any -/// @return Return value of function. NB: will deepcopy Lua values -/// currently, use upvalues to send Lua references in and out. +/// @return Return value of function. Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err) FUNC_API_SINCE(7) FUNC_API_LUA_ONLY @@ -1242,7 +1241,7 @@ Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err) aucmd_prepbuf(&aco, buf); Array args = ARRAY_DICT_INIT; - Object res = nlua_call_ref(fun, NULL, args, true, err); + Object res = nlua_call_ref(fun, NULL, args, kRetLuaref, NULL, err); aucmd_restbuf(&aco); try_end(err); @@ -1419,7 +1418,7 @@ static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len, } else { String str = STRING_INIT; if (len > 0) { - str = arena_string(arena, cbuf_as_string((char *)s, len)); + str = CBUF_TO_ARENA_STR(arena, s, len); if (replace_nl) { // Vim represents NULs as NLs, but this may confuse clients. strchrsub(str.data, '\n', '\0'); diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index bafc45e543..0ac3d42231 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -99,7 +99,7 @@ Dict(cmd) nvim_parse_cmd(String str, Dict(empty) *opts, Arena *arena, Error *err) FUNC_API_SINCE(10) FUNC_API_FAST { - Dict(cmd) result = { 0 }; + Dict(cmd) result = KEYDICT_INIT; // Parse command line exarg_T ea; @@ -514,7 +514,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error VALIDATE_MOD((!ea.forceit || (ea.argt & EX_BANG)), "bang", cmd->cmd.data); if (HAS_KEY(cmd, cmd, magic)) { - Dict(cmd_magic) magic[1] = { 0 }; + Dict(cmd_magic) magic[1] = KEYDICT_INIT; if (!api_dict_to_keydict(magic, KeyDict_cmd_magic_get_field, cmd->magic, err)) { goto end; } @@ -532,13 +532,13 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error } if (HAS_KEY(cmd, cmd, mods)) { - Dict(cmd_mods) mods[1] = { 0 }; + Dict(cmd_mods) mods[1] = KEYDICT_INIT; if (!api_dict_to_keydict(mods, KeyDict_cmd_mods_get_field, cmd->mods, err)) { goto end; } if (HAS_KEY(mods, cmd_mods, filter)) { - Dict(cmd_mods_filter) filter[1] = { 0 }; + Dict(cmd_mods_filter) filter[1] = KEYDICT_INIT; if (!api_dict_to_keydict(&filter, KeyDict_cmd_mods_filter_get_field, mods->filter, err)) { @@ -1103,7 +1103,8 @@ void create_user_command(uint64_t channel_id, String name, Object command, Dict( if (opts->complete.type == kObjectTypeLuaRef) { context = EXPAND_USER_LUA; - compl_luaref = api_new_luaref(opts->complete.data.luaref); + compl_luaref = opts->complete.data.luaref; + opts->complete.data.luaref = LUA_NOREF; } else if (opts->complete.type == kObjectTypeString) { VALIDATE_S(OK == parse_compl_arg(opts->complete.data.string.data, (int)opts->complete.data.string.size, &context, &argt, @@ -1123,7 +1124,8 @@ void create_user_command(uint64_t channel_id, String name, Object command, Dict( }); argt |= EX_PREVIEW; - preview_luaref = api_new_luaref(opts->preview.data.luaref); + preview_luaref = opts->preview.data.luaref; + opts->preview.data.luaref = LUA_NOREF; } switch (command.type) { diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index dccaeb6922..c9a6036b8f 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -51,12 +51,12 @@ String nvim_command_output(uint64_t channel_id, String command, Error *err) /// @deprecated Use nvim_exec_lua() instead. /// @see nvim_exec_lua -Object nvim_execute_lua(String code, Array args, Error *err) +Object nvim_execute_lua(String code, Array args, Arena *arena, Error *err) FUNC_API_SINCE(3) FUNC_API_DEPRECATED_SINCE(7) FUNC_API_REMOTE_ONLY { - return nlua_exec(code, args, err); + return nlua_exec(code, args, kRetObject, arena, err); } /// Gets the buffer number diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 8b45af7c71..7bf0d87603 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -576,7 +576,7 @@ String arena_string(Arena *arena, String str) if (str.size) { return cbuf_as_string(arena_memdupz(arena, str.data, str.size), str.size); } else { - return (String)STRING_INIT; + return (String){ .data = arena ? "" : xstrdup(""), .size = 0 }; } } @@ -1062,24 +1062,56 @@ Dictionary api_keydict_to_dict(void *value, KeySetLink *table, size_t max_size, return rv; } -void api_free_keydict(void *dict, KeySetLink *table) +void api_luarefs_free_object(Object value) +{ + // TODO(bfredl): this is more complicated than it needs to be. + // we should be able to lock down more specifically where luarefs can be + switch (value.type) { + case kObjectTypeLuaRef: + api_free_luaref(value.data.luaref); + break; + + case kObjectTypeArray: + api_luarefs_free_array(value.data.array); + break; + + case kObjectTypeDictionary: + api_luarefs_free_dict(value.data.dictionary); + break; + + default: + break; + } +} + +void api_luarefs_free_keydict(void *dict, KeySetLink *table) { for (size_t i = 0; table[i].str; i++) { char *mem = ((char *)dict + table[i].ptr_off); if (table[i].type == kObjectTypeNil) { - api_free_object(*(Object *)mem); - } else if (table[i].type == kObjectTypeString) { - api_free_string(*(String *)mem); - } else if (table[i].type == kObjectTypeArray) { - api_free_array(*(Array *)mem); - } else if (table[i].type == kObjectTypeDictionary) { - api_free_dictionary(*(Dictionary *)mem); + api_luarefs_free_object(*(Object *)mem); } else if (table[i].type == kObjectTypeLuaRef) { api_free_luaref(*(LuaRef *)mem); + } else if (table[i].type == kObjectTypeDictionary) { + api_luarefs_free_dict(*(Dictionary *)mem); } } } +void api_luarefs_free_array(Array value) +{ + for (size_t i = 0; i < value.size; i++) { + api_luarefs_free_object(value.items[i]); + } +} + +void api_luarefs_free_dict(Dictionary value) +{ + for (size_t i = 0; i < value.size; i++) { + api_luarefs_free_object(value.items[i].value); + } +} + /// Set a named mark /// buffer and mark name must be validated already /// @param buffer Buffer to set the mark on diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 9ee812f45c..11abb8f801 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -34,6 +34,7 @@ #define CSTR_TO_OBJ(s) STRING_OBJ(cstr_to_string(s)) #define CSTR_TO_ARENA_STR(arena, s) arena_string(arena, cstr_as_string(s)) #define CSTR_TO_ARENA_OBJ(arena, s) STRING_OBJ(CSTR_TO_ARENA_STR(arena, s)) +#define CBUF_TO_ARENA_STR(arena, s, len) arena_string(arena, cbuf_as_string((char *)(s), len)) #define BUFFER_OBJ(s) ((Object) { \ .type = kObjectTypeBuffer, \ @@ -119,6 +120,8 @@ #define api_init_array = ARRAY_DICT_INIT #define api_init_dictionary = ARRAY_DICT_INIT +#define KEYDICT_INIT { 0 } + #define api_free_boolean(value) #define api_free_integer(value) #define api_free_float(value) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 78c2561bbd..769537ac98 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -496,11 +496,12 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Bool /// or executing the Lua code. /// /// @return Return value of Lua code if present or NIL. -Object nvim_exec_lua(String code, Array args, Error *err) +Object nvim_exec_lua(String code, Array args, Arena *arena, Error *err) FUNC_API_SINCE(7) FUNC_API_REMOTE_ONLY { - return nlua_exec(code, args, err); + // TODO(bfredl): convert directly from msgpack to lua and then back again + return nlua_exec(code, args, kRetObject, arena, err); } /// Notify the user with a message @@ -512,7 +513,7 @@ Object nvim_exec_lua(String code, Array args, Error *err) /// @param log_level The log level /// @param opts Reserved for future use. /// @param[out] err Error details, if any -Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err) +Object nvim_notify(String msg, Integer log_level, Dictionary opts, Arena *arena, Error *err) FUNC_API_SINCE(7) { MAXSIZE_TEMP_ARRAY(args, 3); @@ -520,7 +521,7 @@ Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err) ADD_C(args, INTEGER_OBJ(log_level)); ADD_C(args, DICTIONARY_OBJ(opts)); - return NLUA_EXEC_STATIC("return vim.notify(...)", args, err); + return NLUA_EXEC_STATIC("return vim.notify(...)", args, kRetObject, arena, err); } /// Calculates the number of display cells occupied by `text`. @@ -603,7 +604,8 @@ String nvim__get_lib_dir(void) /// @param all whether to return all matches or only the first /// @param opts is_lua: only search Lua subdirs /// @return list of absolute paths to the found files -ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, Error *err) +ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, Arena *arena, + Error *err) FUNC_API_SINCE(8) FUNC_API_FAST { @@ -613,7 +615,7 @@ ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, E return (Array)ARRAY_DICT_INIT; } - ArrayOf(String) res = runtime_get_named(opts->is_lua, pat, all); + ArrayOf(String) res = runtime_get_named(opts->is_lua, pat, all, arena); if (opts->do_source) { for (size_t i = 0; i < res.size; i++) { @@ -1068,7 +1070,7 @@ static void term_write(const char *buf, size_t size, void *data) ADD_C(args, BUFFER_OBJ(terminal_buf(chan->term))); ADD_C(args, STRING_OBJ(((String){ .data = (char *)buf, .size = size }))); textlock++; - nlua_call_ref(cb, "input", args, false, NULL); + nlua_call_ref(cb, "input", args, kRetNilBool, NULL, NULL); textlock--; } @@ -1189,7 +1191,7 @@ void nvim_set_current_tabpage(Tabpage tabpage, Error *err) /// @return /// - true: Client may continue pasting. /// - false: Client must cancel the paste. -Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err) +Boolean nvim_paste(String data, Boolean crlf, Integer phase, Arena *arena, Error *err) FUNC_API_SINCE(6) FUNC_API_TEXTLOCK_ALLOW_CMDWIN { @@ -1199,19 +1201,18 @@ Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err) VALIDATE_INT((phase >= -1 && phase <= 3), "phase", phase, { return false; }); - Array args = ARRAY_DICT_INIT; - Object rv = OBJECT_INIT; + Array lines = ARRAY_DICT_INIT; if (phase == -1 || phase == 1) { // Start of paste-stream. draining = false; } else if (draining) { // Skip remaining chunks. Report error only once per "stream". goto theend; } - Array lines = string_to_array(data, crlf); - ADD(args, ARRAY_OBJ(lines)); - ADD(args, INTEGER_OBJ(phase)); - rv = nvim_exec_lua(STATIC_CSTR_AS_STRING("return vim.paste(...)"), args, - err); + lines = string_to_array(data, crlf); + MAXSIZE_TEMP_ARRAY(args, 2); + ADD_C(args, ARRAY_OBJ(lines)); + ADD_C(args, INTEGER_OBJ(phase)); + Object rv = NLUA_EXEC_STATIC("return vim.paste(...)", args, kRetNilBool, arena, err); if (ERROR_SET(err)) { draining = true; goto theend; @@ -1238,8 +1239,7 @@ Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err) AppendCharToRedobuff(ESC); // Dot-repeat. } theend: - api_free_object(rv); - api_free_array(args); + api_free_array(lines); if (cancel || phase == -1 || phase == 3) { // End of paste-stream. draining = false; } @@ -1875,7 +1875,7 @@ Array nvim_list_uis(Arena *arena) /// Gets the immediate children of process `pid`. /// /// @return Array of child process ids, empty if process not found. -Array nvim_get_proc_children(Integer pid, Error *err) +Array nvim_get_proc_children(Integer pid, Arena *arena, Error *err) FUNC_API_SINCE(4) { Array rvobj = ARRAY_DICT_INIT; @@ -1892,7 +1892,7 @@ Array nvim_get_proc_children(Integer pid, Error *err) DLOG("fallback to vim._os_proc_children()"); MAXSIZE_TEMP_ARRAY(a, 1); ADD(a, INTEGER_OBJ(pid)); - Object o = NLUA_EXEC_STATIC("return vim._os_proc_children(...)", a, err); + Object o = NLUA_EXEC_STATIC("return vim._os_proc_children(...)", a, kRetObject, arena, err); if (o.type == kObjectTypeArray) { rvobj = o.data.array; } else if (!ERROR_SET(err)) { @@ -1900,11 +1900,11 @@ Array nvim_get_proc_children(Integer pid, Error *err) "Failed to get process children. pid=%" PRId64 " error=%d", pid, rv); } - goto end; - } - - for (size_t i = 0; i < proc_count; i++) { - ADD(rvobj, INTEGER_OBJ(proc_list[i])); + } else { + rvobj = arena_array(arena, proc_count); + for (size_t i = 0; i < proc_count; i++) { + ADD(rvobj, INTEGER_OBJ(proc_list[i])); + } } end: @@ -1915,19 +1915,17 @@ end: /// Gets info describing process `pid`. /// /// @return Map of process properties, or NIL if process not found. -Object nvim_get_proc(Integer pid, Error *err) +Object nvim_get_proc(Integer pid, Arena *arena, Error *err) FUNC_API_SINCE(4) { - Object rvobj = OBJECT_INIT; - rvobj.data.dictionary = (Dictionary)ARRAY_DICT_INIT; - rvobj.type = kObjectTypeDictionary; + Object rvobj = NIL; VALIDATE_INT((pid > 0 && pid <= INT_MAX), "pid", pid, { return NIL; }); #ifdef MSWIN - rvobj.data.dictionary = os_proc_info((int)pid); + rvobj = DICTIONARY_OBJ(os_proc_info((int)pid)); if (rvobj.data.dictionary.size == 0) { // Process not found. return NIL; } @@ -1935,11 +1933,11 @@ Object nvim_get_proc(Integer pid, Error *err) // Cross-platform process info APIs are miserable, so use `ps` instead. MAXSIZE_TEMP_ARRAY(a, 1); ADD(a, INTEGER_OBJ(pid)); - Object o = NLUA_EXEC_STATIC("return vim._os_proc_info(...)", a, err); + Object o = NLUA_EXEC_STATIC("return vim._os_proc_info(...)", a, kRetObject, arena, err); if (o.type == kObjectTypeArray && o.data.array.size == 0) { return NIL; // Process not found. } else if (o.type == kObjectTypeDictionary) { - rvobj.data.dictionary = o.data.dictionary; + rvobj = o; } else if (!ERROR_SET(err)) { api_set_error(err, kErrorTypeException, "Failed to get process info. pid=%" PRId64, pid); diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 52372b838e..e76db82c61 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -600,7 +600,7 @@ Dict(win_config) nvim_win_get_config(Window window, Arena *arena, Error *err) /// Keep in sync with WinSplit in buffer_defs.h static const char *const win_split_str[] = { "left", "right", "above", "below" }; - Dict(win_config) rv = { 0 }; + Dict(win_config) rv = KEYDICT_INIT; win_T *wp = find_window_by_handle(window, err); if (!wp) { diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 4ac7e47832..93c9dfa049 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -421,8 +421,7 @@ void nvim_win_close(Window window, Boolean force, Error *err) /// @param fun Function to call inside the window (currently Lua callable /// only) /// @param[out] err Error details, if any -/// @return Return value of function. NB: will deepcopy Lua values -/// currently, use upvalues to send Lua references in and out. +/// @return Return value of function. Object nvim_win_call(Window window, LuaRef fun, Error *err) FUNC_API_SINCE(7) FUNC_API_LUA_ONLY @@ -438,7 +437,7 @@ Object nvim_win_call(Window window, LuaRef fun, Error *err) win_execute_T win_execute_args; if (win_execute_before(&win_execute_args, win, tabpage)) { Array args = ARRAY_DICT_INIT; - res = nlua_call_ref(fun, NULL, args, true, err); + res = nlua_call_ref(fun, NULL, args, kRetLuaref, NULL, err); } win_execute_after(&win_execute_args); try_end(err); -- cgit From 1a3a8d903e9705ce41867e1cbc629a85c7cb6252 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 11 Feb 2024 19:13:38 +0100 Subject: refactor(lua): use a keyset for vim.diff opts parsing --- src/nvim/api/keysets_defs.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'src/nvim/api') diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index 0ba33ca9a7..c5aa5ce0f1 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -358,3 +358,19 @@ typedef struct { OptionalKeys is_set__complete_set_; String info; } Dict(complete_set); + +typedef struct { + OptionalKeys is_set__xdl_diff_; + LuaRef on_hunk; + String result_type; + String algorithm; + Integer ctxlen; + Integer interhunkctxlen; + Object linematch; + Boolean ignore_whitespace; + Boolean ignore_whitespace_change; + Boolean ignore_whitespace_change_at_eol; + Boolean ignore_cr_at_eol; + Boolean ignore_blank_lines; + Boolean indent_heuristic; +} Dict(xdl_diff); -- cgit From d60412b18e4e21f301baa2ac3f3fb7be89655e4b Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 12 Feb 2024 20:40:27 +0100 Subject: refactor(eval): use arena when converting typvals to Object Note: this contains two _temporary_ changes which can be reverted once the Arena vs no-Arena distinction in API wrappers has been removed. Both nlua_push_Object and object_to_vim_take_luaref() has been changed to take the object argument as a pointer. This is not going to be necessary once these are only used with arena (or not at all) allocated Objects. The object_to_vim() variant which leaves luaref untouched might need to stay for a little longer. --- src/nvim/api/buffer.c | 15 ++++--- src/nvim/api/command.c | 32 ++++++-------- src/nvim/api/deprecated.c | 32 +++++++------- src/nvim/api/private/converter.c | 95 +++++++++++++++++++++++----------------- src/nvim/api/private/helpers.c | 11 ++--- src/nvim/api/private/helpers.h | 1 + src/nvim/api/tabpage.c | 8 ++-- src/nvim/api/ui.c | 8 ++-- src/nvim/api/vim.c | 29 ++++++------ src/nvim/api/vimscript.c | 16 +++---- src/nvim/api/window.c | 15 ++++--- 11 files changed, 139 insertions(+), 123 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 993e290b2d..110154f8de 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -869,7 +869,7 @@ Integer nvim_buf_get_offset(Buffer buffer, Integer index, Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Variable value -Object nvim_buf_get_var(Buffer buffer, String name, Error *err) +Object nvim_buf_get_var(Buffer buffer, String name, Arena *arena, Error *err) FUNC_API_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -878,7 +878,7 @@ Object nvim_buf_get_var(Buffer buffer, String name, Error *err) return (Object)OBJECT_INIT; } - return dict_get_value(buf->b_vars, name, err); + return dict_get_value(buf->b_vars, name, arena, err); } /// Gets a changed tick of a buffer @@ -957,7 +957,7 @@ void nvim_buf_set_var(Buffer buffer, String name, Object value, Error *err) return; } - dict_set_var(buf->b_vars, name, value, false, false, err); + dict_set_var(buf->b_vars, name, value, false, false, NULL, err); } /// Removes a buffer-scoped (b:) variable @@ -974,7 +974,7 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err) return; } - dict_set_var(buf->b_vars, name, NIL, true, false, err); + dict_set_var(buf->b_vars, name, NIL, true, false, NULL, err); } /// Gets the full file name for the buffer @@ -1175,7 +1175,7 @@ Boolean nvim_buf_set_mark(Buffer buffer, String name, Integer line, Integer col, /// uppercase/file mark set in another buffer. /// @see |nvim_buf_set_mark()| /// @see |nvim_buf_del_mark()| -ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err) +ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Arena *arena, Error *err) FUNC_API_SINCE(1) { Array rv = ARRAY_DICT_INIT; @@ -1205,8 +1205,9 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err) pos = fm->mark; } - ADD(rv, INTEGER_OBJ(pos.lnum)); - ADD(rv, INTEGER_OBJ(pos.col)); + rv = arena_array(arena, 2); + ADD_C(rv, INTEGER_OBJ(pos.lnum)); + ADD_C(rv, INTEGER_OBJ(pos.col)); return rv; } diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index 0ac3d42231..a21c9e70a7 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -305,7 +305,7 @@ end: /// - output: (boolean, default false) Whether to return command output. /// @param[out] err Error details, if any. /// @return Command output (non-error, non-shell |:!|) if `output` is true, else empty string. -String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error *err) +String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Arena *arena, Error *err) FUNC_API_SINCE(10) { exarg_T ea; @@ -343,7 +343,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error goto end; }); - cmdname = string_to_cstr(cmd->cmd); + cmdname = arena_string(arena, cmd->cmd).data; ea.cmd = cmdname; char *p = find_ex_command(&ea, NULL); @@ -352,9 +352,8 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error // autocommands defined, trigger the matching autocommands. if (p != NULL && ea.cmdidx == CMD_SIZE && ASCII_ISUPPER(*ea.cmd) && has_event(EVENT_CMDUNDEFINED)) { - p = xstrdup(cmdname); + p = arena_string(arena, cmd->cmd).data; int ret = apply_autocmds(EVENT_CMDUNDEFINED, p, p, true, NULL); - xfree(p); // If the autocommands did something and didn't cause an error, try // finding the command again. p = (ret && !aborting()) ? find_ex_command(&ea, NULL) : ea.cmd; @@ -383,28 +382,31 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error if (HAS_KEY(cmd, cmd, args)) { // Process all arguments. Convert non-String arguments to String and check if String arguments // have non-whitespace characters. + args = arena_array(arena, cmd->args.size); for (size_t i = 0; i < cmd->args.size; i++) { Object elem = cmd->args.items[i]; char *data_str; switch (elem.type) { case kObjectTypeBoolean: - data_str = xcalloc(2, sizeof(char)); + data_str = arena_alloc(arena, 2, false); data_str[0] = elem.data.boolean ? '1' : '0'; data_str[1] = '\0'; + ADD_C(args, CSTR_AS_OBJ(data_str)); break; case kObjectTypeBuffer: case kObjectTypeWindow: case kObjectTypeTabpage: case kObjectTypeInteger: - data_str = xcalloc(NUMBUFLEN, sizeof(char)); + data_str = arena_alloc(arena, NUMBUFLEN, false); snprintf(data_str, NUMBUFLEN, "%" PRId64, elem.data.integer); + ADD_C(args, CSTR_AS_OBJ(data_str)); break; case kObjectTypeString: VALIDATE_EXP(!string_iswhite(elem.data.string), "command arg", "non-whitespace", NULL, { goto end; }); - data_str = string_to_cstr(elem.data.string); + ADD_C(args, elem); break; default: VALIDATE_EXP(false, "command arg", "valid type", api_typename(elem.type), { @@ -412,8 +414,6 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error }); break; } - - ADD(args, CSTR_AS_OBJ(data_str)); } bool argc_valid; @@ -666,26 +666,20 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error } if (opts->output && capture_local.ga_len > 1) { - retv = (String){ - .data = capture_local.ga_data, - .size = (size_t)capture_local.ga_len, - }; + // TODO(bfredl): if there are more cases like this we might want custom xfree-list in arena + retv = CBUF_TO_ARENA_STR(arena, capture_local.ga_data, (size_t)capture_local.ga_len); // redir usually (except :echon) prepends a newline. if (retv.data[0] == '\n') { - memmove(retv.data, retv.data + 1, retv.size - 1); - retv.data[retv.size - 1] = '\0'; - retv.size = retv.size - 1; + retv.data++; + retv.size--; } - goto end; } clear_ga: if (opts->output) { ga_clear(&capture_local); } end: - api_free_array(args); xfree(cmdline); - xfree(cmdname); xfree(ea.args); xfree(ea.arglens); diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index c9a6036b8f..9b8cacd7c2 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -362,7 +362,7 @@ void buffer_set_line_slice(Buffer buffer, Integer start, Integer end, Boolean in /// /// @warning It may return nil if there was no previous value /// or if previous value was `v:null`. -Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) +Object buffer_set_var(Buffer buffer, String name, Object value, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -371,7 +371,7 @@ Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) return NIL; } - return dict_set_var(buf->b_vars, name, value, false, true, err); + return dict_set_var(buf->b_vars, name, value, false, true, arena, err); } /// Removes a buffer-scoped (b:) variable @@ -382,7 +382,7 @@ Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Old value -Object buffer_del_var(Buffer buffer, String name, Error *err) +Object buffer_del_var(Buffer buffer, String name, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -391,7 +391,7 @@ Object buffer_del_var(Buffer buffer, String name, Error *err) return NIL; } - return dict_set_var(buf->b_vars, name, NIL, true, true, err); + return dict_set_var(buf->b_vars, name, NIL, true, true, arena, err); } /// Sets a window-scoped (w:) variable @@ -406,7 +406,7 @@ Object buffer_del_var(Buffer buffer, String name, Error *err) /// /// @warning It may return nil if there was no previous value /// or if previous value was `v:null`. -Object window_set_var(Window window, String name, Object value, Error *err) +Object window_set_var(Window window, String name, Object value, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -415,7 +415,7 @@ Object window_set_var(Window window, String name, Object value, Error *err) return NIL; } - return dict_set_var(win->w_vars, name, value, false, true, err); + return dict_set_var(win->w_vars, name, value, false, true, arena, err); } /// Removes a window-scoped (w:) variable @@ -426,7 +426,7 @@ Object window_set_var(Window window, String name, Object value, Error *err) /// @param name variable name /// @param[out] err Error details, if any /// @return Old value -Object window_del_var(Window window, String name, Error *err) +Object window_del_var(Window window, String name, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -435,7 +435,7 @@ Object window_del_var(Window window, String name, Error *err) return NIL; } - return dict_set_var(win->w_vars, name, NIL, true, true, err); + return dict_set_var(win->w_vars, name, NIL, true, true, arena, err); } /// Sets a tab-scoped (t:) variable @@ -450,7 +450,7 @@ Object window_del_var(Window window, String name, Error *err) /// /// @warning It may return nil if there was no previous value /// or if previous value was `v:null`. -Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) +Object tabpage_set_var(Tabpage tabpage, String name, Object value, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -459,7 +459,7 @@ Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) return NIL; } - return dict_set_var(tab->tp_vars, name, value, false, true, err); + return dict_set_var(tab->tp_vars, name, value, false, true, arena, err); } /// Removes a tab-scoped (t:) variable @@ -470,7 +470,7 @@ Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Old value -Object tabpage_del_var(Tabpage tabpage, String name, Error *err) +Object tabpage_del_var(Tabpage tabpage, String name, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -479,7 +479,7 @@ Object tabpage_del_var(Tabpage tabpage, String name, Error *err) return NIL; } - return dict_set_var(tab->tp_vars, name, NIL, true, true, err); + return dict_set_var(tab->tp_vars, name, NIL, true, true, arena, err); } /// @deprecated @@ -487,18 +487,18 @@ Object tabpage_del_var(Tabpage tabpage, String name, Error *err) /// @warning May return nil if there was no previous value /// OR if previous value was `v:null`. /// @return Old value or nil if there was no previous value. -Object vim_set_var(String name, Object value, Error *err) +Object vim_set_var(String name, Object value, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { - return dict_set_var(&globvardict, name, value, false, true, err); + return dict_set_var(&globvardict, name, value, false, true, arena, err); } /// @deprecated /// @see nvim_del_var -Object vim_del_var(String name, Error *err) +Object vim_del_var(String name, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { - return dict_set_var(&globvardict, name, NIL, true, true, err); + return dict_set_var(&globvardict, name, NIL, true, true, arena, err); } static int64_t convert_index(int64_t index) diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index e7b8934c97..a70ef1e50b 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -19,6 +19,8 @@ /// Helper structure for vim_to_object typedef struct { kvec_withinit_t(Object, 2) stack; ///< Object stack. + Arena *arena; ///< arena where objects will be allocated + bool reuse_strdata; } EncodedData; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -41,12 +43,21 @@ typedef struct { #define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ kvi_push(edata->stack, FLOAT_OBJ((Float)(flt))) +static Object typval_cbuf_to_obj(EncodedData *edata, const char *data, size_t len) +{ + if (edata->reuse_strdata) { + return STRING_OBJ(cbuf_as_string((char *)(len ? data : ""), len)); + } else { + return CBUF_TO_ARENA_OBJ(edata->arena, data, len); + } +} + #define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \ do { \ const size_t len_ = (size_t)(len); \ const char *const str_ = (str); \ assert(len_ == 0 || str_ != NULL); \ - kvi_push(edata->stack, STRING_OBJ(cbuf_to_string((len_ ? str_ : ""), len_))); \ + kvi_push(edata->stack, typval_cbuf_to_obj(edata, str_, len_)); \ } while (0) #define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING @@ -58,10 +69,7 @@ typedef struct { do { \ const size_t len_ = (size_t)(len); \ const blob_T *const blob_ = (blob); \ - kvi_push(edata->stack, STRING_OBJ(((String) { \ - .data = len_ != 0 ? xmemdupz(blob_->bv_ga.ga_data, len_) : xstrdup(""), \ - .size = len_ \ - }))); \ + kvi_push(edata->stack, typval_cbuf_to_obj(edata, len_ ? blob_->bv_ga.ga_data : "", len_)); \ } while (0) #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ @@ -90,11 +98,7 @@ typedef struct { static inline void typval_encode_list_start(EncodedData *const edata, const size_t len) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL { - kvi_push(edata->stack, ARRAY_OBJ(((Array) { - .capacity = len, - .size = 0, - .items = xmalloc(len * sizeof(*((Object)OBJECT_INIT).data.array.items)), - }))); + kvi_push(edata->stack, ARRAY_OBJ(arena_array(edata->arena, len))); } #define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \ @@ -109,7 +113,7 @@ static inline void typval_encode_between_list_items(EncodedData *const edata) Object *const list = &kv_last(edata->stack); assert(list->type == kObjectTypeArray); assert(list->data.array.size < list->data.array.capacity); - list->data.array.items[list->data.array.size++] = item; + ADD_C(list->data.array, item); } #define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \ @@ -131,11 +135,7 @@ static inline void typval_encode_list_end(EncodedData *const edata) static inline void typval_encode_dict_start(EncodedData *const edata, const size_t len) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL { - kvi_push(edata->stack, DICTIONARY_OBJ(((Dictionary) { - .capacity = len, - .size = 0, - .items = xmalloc(len * sizeof(*((Object)OBJECT_INIT).data.dictionary.items)), - }))); + kvi_push(edata->stack, DICTIONARY_OBJ(arena_dict(edata->arena, len))); } #define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \ @@ -156,9 +156,8 @@ static inline void typval_encode_after_key(EncodedData *const edata) dict->data.dictionary.items[dict->data.dictionary.size].key = key.data.string; } else { - api_free_object(key); dict->data.dictionary.items[dict->data.dictionary.size].key - = STATIC_CSTR_TO_STRING("__INVALID_KEY__"); + = STATIC_CSTR_AS_STRING("__INVALID_KEY__"); } } @@ -233,17 +232,22 @@ static inline void typval_encode_dict_end(EncodedData *const edata) #undef TYPVAL_ENCODE_CONV_RECURSE #undef TYPVAL_ENCODE_ALLOW_SPECIALS -/// Convert a vim object to an `Object` instance, recursively expanding +/// Convert a vim object to an `Object` instance, recursively converting /// Arrays/Dictionaries. /// /// @param obj The source object +/// @param arena if NULL, use direct allocation +/// @param reuse_strdata when true, don't copy string data to Arena but reference +/// typval strings directly. takes no effect when arena is +/// NULL /// @return The converted value -Object vim_to_object(typval_T *obj) +Object vim_to_object(typval_T *obj, Arena *arena, bool reuse_strdata) { EncodedData edata; kvi_init(edata.stack); - const int evo_ret = encode_vim_to_object(&edata, obj, - "vim_to_object argument"); + edata.arena = arena; + edata.reuse_strdata = reuse_strdata; + const int evo_ret = encode_vim_to_object(&edata, obj, "vim_to_object argument"); (void)evo_ret; assert(evo_ret == OK); Object ret = kv_A(edata.stack, 0); @@ -259,11 +263,19 @@ Object vim_to_object(typval_T *obj) /// set to VAR_UNKNOWN (no allocation was made for this variable). /// @param err Error object. void object_to_vim(Object obj, typval_T *tv, Error *err) +{ + object_to_vim_take_luaref(&obj, tv, false, err); +} + +/// same as object_to_vim but consumes all luarefs (nested) in `obj` +/// +/// useful when `obj` is allocated on an arena +void object_to_vim_take_luaref(Object *obj, typval_T *tv, bool take_luaref, Error *err) { tv->v_type = VAR_UNKNOWN; tv->v_lock = VAR_UNLOCKED; - switch (obj.type) { + switch (obj->type) { case kObjectTypeNil: tv->v_type = VAR_SPECIAL; tv->vval.v_special = kSpecialVarNull; @@ -271,41 +283,40 @@ void object_to_vim(Object obj, typval_T *tv, Error *err) case kObjectTypeBoolean: tv->v_type = VAR_BOOL; - tv->vval.v_bool = obj.data.boolean ? kBoolVarTrue : kBoolVarFalse; + tv->vval.v_bool = obj->data.boolean ? kBoolVarTrue : kBoolVarFalse; break; case kObjectTypeBuffer: case kObjectTypeWindow: case kObjectTypeTabpage: case kObjectTypeInteger: - STATIC_ASSERT(sizeof(obj.data.integer) <= sizeof(varnumber_T), + STATIC_ASSERT(sizeof(obj->data.integer) <= sizeof(varnumber_T), "Integer size must be <= Vimscript number size"); tv->v_type = VAR_NUMBER; - tv->vval.v_number = (varnumber_T)obj.data.integer; + tv->vval.v_number = (varnumber_T)obj->data.integer; break; case kObjectTypeFloat: tv->v_type = VAR_FLOAT; - tv->vval.v_float = obj.data.floating; + tv->vval.v_float = obj->data.floating; break; case kObjectTypeString: tv->v_type = VAR_STRING; - if (obj.data.string.data == NULL) { + if (obj->data.string.data == NULL) { tv->vval.v_string = NULL; } else { - tv->vval.v_string = xmemdupz(obj.data.string.data, - obj.data.string.size); + tv->vval.v_string = xmemdupz(obj->data.string.data, + obj->data.string.size); } break; case kObjectTypeArray: { - list_T *const list = tv_list_alloc((ptrdiff_t)obj.data.array.size); + list_T *const list = tv_list_alloc((ptrdiff_t)obj->data.array.size); - for (uint32_t i = 0; i < obj.data.array.size; i++) { - Object item = obj.data.array.items[i]; + for (uint32_t i = 0; i < obj->data.array.size; i++) { typval_T li_tv; - object_to_vim(item, &li_tv, err); + object_to_vim_take_luaref(&obj->data.array.items[i], &li_tv, take_luaref, err); tv_list_append_owned_tv(list, li_tv); } tv_list_ref(list); @@ -318,11 +329,11 @@ void object_to_vim(Object obj, typval_T *tv, Error *err) case kObjectTypeDictionary: { dict_T *const dict = tv_dict_alloc(); - for (uint32_t i = 0; i < obj.data.dictionary.size; i++) { - KeyValuePair item = obj.data.dictionary.items[i]; - String key = item.key; + for (uint32_t i = 0; i < obj->data.dictionary.size; i++) { + KeyValuePair *item = &obj->data.dictionary.items[i]; + String key = item->key; dictitem_T *const di = tv_dict_item_alloc(key.data); - object_to_vim(item.value, &di->di_tv, err); + object_to_vim_take_luaref(&item->value, &di->di_tv, take_luaref, err); tv_dict_add(dict, di); } dict->dv_refcount++; @@ -333,7 +344,13 @@ void object_to_vim(Object obj, typval_T *tv, Error *err) } case kObjectTypeLuaRef: { - char *name = register_luafunc(api_new_luaref(obj.data.luaref)); + LuaRef ref = obj->data.luaref; + if (take_luaref) { + obj->data.luaref = LUA_NOREF; + } else { + ref = api_new_luaref(ref); + } + char *name = register_luafunc(ref); tv->v_type = VAR_FUNC; tv->vval.v_string = xstrdup(name); break; diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 7bf0d87603..1446683b0c 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -175,7 +175,7 @@ bool try_end(Error *err) /// @param dict The vimscript dict /// @param key The key /// @param[out] err Details of an error that may have occurred -Object dict_get_value(dict_T *dict, String key, Error *err) +Object dict_get_value(dict_T *dict, String key, Arena *arena, Error *err) { dictitem_T *const di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size); @@ -184,7 +184,7 @@ Object dict_get_value(dict_T *dict, String key, Error *err) return (Object)OBJECT_INIT; } - return vim_to_object(&di->di_tv); + return vim_to_object(&di->di_tv, arena, true); } dictitem_T *dict_check_writable(dict_T *dict, String key, bool del, Error *err) @@ -221,7 +221,8 @@ dictitem_T *dict_check_writable(dict_T *dict, String key, bool del, Error *err) /// @param retval If true the old value will be converted and returned. /// @param[out] err Details of an error that may have occurred /// @return The old value if `retval` is true and the key was present, else NIL -Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retval, Error *err) +Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retval, Arena *arena, + Error *err) { Object rv = OBJECT_INIT; dictitem_T *di = dict_check_writable(dict, key, del, err); @@ -244,7 +245,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva } // Return the old value if (retval) { - rv = vim_to_object(&di->di_tv); + rv = vim_to_object(&di->di_tv, arena, false); } // Delete the entry tv_dict_item_remove(dict, di); @@ -265,7 +266,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva } else { // Return the old value if (retval) { - rv = vim_to_object(&di->di_tv); + rv = vim_to_object(&di->di_tv, arena, false); } bool type_error = false; if (dict == &vimvardict diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 11abb8f801..395c5a9d1f 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -35,6 +35,7 @@ #define CSTR_TO_ARENA_STR(arena, s) arena_string(arena, cstr_as_string(s)) #define CSTR_TO_ARENA_OBJ(arena, s) STRING_OBJ(CSTR_TO_ARENA_STR(arena, s)) #define CBUF_TO_ARENA_STR(arena, s, len) arena_string(arena, cbuf_as_string((char *)(s), len)) +#define CBUF_TO_ARENA_OBJ(arena, s, len) STRING_OBJ(CBUF_TO_ARENA_STR(arena, s, len)) #define BUFFER_OBJ(s) ((Object) { \ .type = kObjectTypeBuffer, \ diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c index fadc03b3e5..109075df8e 100644 --- a/src/nvim/api/tabpage.c +++ b/src/nvim/api/tabpage.c @@ -49,7 +49,7 @@ ArrayOf(Window) nvim_tabpage_list_wins(Tabpage tabpage, Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Variable value -Object nvim_tabpage_get_var(Tabpage tabpage, String name, Error *err) +Object nvim_tabpage_get_var(Tabpage tabpage, String name, Arena *arena, Error *err) FUNC_API_SINCE(1) { tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -58,7 +58,7 @@ Object nvim_tabpage_get_var(Tabpage tabpage, String name, Error *err) return (Object)OBJECT_INIT; } - return dict_get_value(tab->tp_vars, name, err); + return dict_get_value(tab->tp_vars, name, arena, err); } /// Sets a tab-scoped (t:) variable @@ -76,7 +76,7 @@ void nvim_tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err return; } - dict_set_var(tab->tp_vars, name, value, false, false, err); + dict_set_var(tab->tp_vars, name, value, false, false, NULL, err); } /// Removes a tab-scoped (t:) variable @@ -93,7 +93,7 @@ void nvim_tabpage_del_var(Tabpage tabpage, String name, Error *err) return; } - dict_set_var(tab->tp_vars, name, NIL, true, false, err); + dict_set_var(tab->tp_vars, name, NIL, true, false, NULL, err); } /// Gets the current window in a tabpage diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index c7280253c2..140b520cb5 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -1029,12 +1029,12 @@ static Array translate_contents(UI *ui, Array contents, Arena *arena) if (attr) { Dictionary rgb_attrs = arena_dict(arena, HLATTRS_DICT_SIZE); hlattrs2dict(&rgb_attrs, NULL, syn_attr2entry(attr), ui->rgb, false); - ADD(new_item, DICTIONARY_OBJ(rgb_attrs)); + ADD_C(new_item, DICTIONARY_OBJ(rgb_attrs)); } else { - ADD(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); + ADD_C(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); } - ADD(new_item, item.items[1]); - ADD(new_contents, ARRAY_OBJ(new_item)); + ADD_C(new_item, item.items[1]); + ADD_C(new_contents, ARRAY_OBJ(new_item)); } return new_contents; } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 769537ac98..b7cb14867d 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -690,7 +690,7 @@ void nvim_del_current_line(Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Variable value -Object nvim_get_var(String name, Error *err) +Object nvim_get_var(String name, Arena *arena, Error *err) FUNC_API_SINCE(1) { dictitem_T *di = tv_dict_find(&globvardict, name.data, (ptrdiff_t)name.size); @@ -704,7 +704,7 @@ Object nvim_get_var(String name, Error *err) VALIDATE((di != NULL), "Key not found: %s", name.data, { return (Object)OBJECT_INIT; }); - return vim_to_object(&di->di_tv); + return vim_to_object(&di->di_tv, arena, true); } /// Sets a global (g:) variable. @@ -715,7 +715,7 @@ Object nvim_get_var(String name, Error *err) void nvim_set_var(String name, Object value, Error *err) FUNC_API_SINCE(1) { - dict_set_var(&globvardict, name, value, false, false, err); + dict_set_var(&globvardict, name, value, false, false, NULL, err); } /// Removes a global (g:) variable. @@ -725,7 +725,7 @@ void nvim_set_var(String name, Object value, Error *err) void nvim_del_var(String name, Error *err) FUNC_API_SINCE(1) { - dict_set_var(&globvardict, name, NIL, true, false, err); + dict_set_var(&globvardict, name, NIL, true, false, NULL, err); } /// Gets a v: variable. @@ -733,10 +733,10 @@ void nvim_del_var(String name, Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Variable value -Object nvim_get_vvar(String name, Error *err) +Object nvim_get_vvar(String name, Arena *arena, Error *err) FUNC_API_SINCE(1) { - return dict_get_value(&vimvardict, name, err); + return dict_get_value(&vimvardict, name, arena, err); } /// Sets a v: variable, if it is not readonly. @@ -747,7 +747,7 @@ Object nvim_get_vvar(String name, Error *err) void nvim_set_vvar(String name, Object value, Error *err) FUNC_API_SINCE(6) { - dict_set_var(&vimvardict, name, value, false, false, err); + dict_set_var(&vimvardict, name, value, false, false, NULL, err); } /// Echo a message. @@ -1370,7 +1370,7 @@ Dictionary nvim_get_color_map(Arena *arena) /// @param[out] err Error details, if any /// /// @return map of global |context|. -Dictionary nvim_get_context(Dict(context) *opts, Error *err) +Dictionary nvim_get_context(Dict(context) *opts, Arena *arena, Error *err) FUNC_API_SINCE(6) { Array types = ARRAY_DICT_INIT; @@ -1406,7 +1406,7 @@ Dictionary nvim_get_context(Dict(context) *opts, Error *err) Context ctx = CONTEXT_INIT; ctx_save(&ctx, int_types); - Dictionary dict = ctx_to_dict(&ctx); + Dictionary dict = ctx_to_dict(&ctx, arena); ctx_free(&ctx); return dict; } @@ -2065,7 +2065,7 @@ Boolean nvim_del_mark(String name, Error *err) /// not set. /// @see |nvim_buf_set_mark()| /// @see |nvim_del_mark()| -Array nvim_get_mark(String name, Dict(empty) *opts, Error *err) +Array nvim_get_mark(String name, Dict(empty) *opts, Arena *arena, Error *err) FUNC_API_SINCE(8) { Array rv = ARRAY_DICT_INIT; @@ -2113,10 +2113,11 @@ Array nvim_get_mark(String name, Dict(empty) *opts, Error *err) col = pos.col; } - ADD(rv, INTEGER_OBJ(row)); - ADD(rv, INTEGER_OBJ(col)); - ADD(rv, INTEGER_OBJ(bufnr)); - ADD(rv, CSTR_TO_OBJ(filename)); + rv = arena_array(arena, 4); + ADD_C(rv, INTEGER_OBJ(row)); + ADD_C(rv, INTEGER_OBJ(col)); + ADD_C(rv, INTEGER_OBJ(bufnr)); + ADD_C(rv, CSTR_TO_ARENA_OBJ(arena, filename)); if (allocated) { xfree(filename); diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index 56541bb8b8..949356acc6 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -148,7 +148,7 @@ void nvim_command(String command, Error *err) /// @param expr Vimscript expression string /// @param[out] err Error details, if any /// @return Evaluation result or expanded object -Object nvim_eval(String expr, Error *err) +Object nvim_eval(String expr, Arena *arena, Error *err) FUNC_API_SINCE(1) { static int recursive = 0; // recursion depth @@ -179,7 +179,7 @@ Object nvim_eval(String expr, Error *err) api_set_error(err, kErrorTypeException, "Failed to evaluate expression: '%.*s'", 256, expr.data); } else { - rv = vim_to_object(&rettv); + rv = vim_to_object(&rettv, arena, false); } } @@ -196,7 +196,7 @@ Object nvim_eval(String expr, Error *err) /// @param self `self` dict, or NULL for non-dict functions /// @param[out] err Error details, if any /// @return Result of the function call -static Object _call_function(String fn, Array args, dict_T *self, Error *err) +static Object _call_function(String fn, Array args, dict_T *self, Arena *arena, Error *err) { static int recursive = 0; // recursion depth Object rv = OBJECT_INIT; @@ -239,7 +239,7 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err) }); if (!ERROR_SET(err)) { - rv = vim_to_object(&rettv); + rv = vim_to_object(&rettv, arena, false); } tv_clear(&rettv); @@ -260,10 +260,10 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err) /// @param args Function arguments packed in an Array /// @param[out] err Error details, if any /// @return Result of the function call -Object nvim_call_function(String fn, Array args, Error *err) +Object nvim_call_function(String fn, Array args, Arena *arena, Error *err) FUNC_API_SINCE(1) { - return _call_function(fn, args, NULL, err); + return _call_function(fn, args, NULL, arena, err); } /// Calls a Vimscript |Dictionary-function| with the given arguments. @@ -275,7 +275,7 @@ Object nvim_call_function(String fn, Array args, Error *err) /// @param args Function arguments packed in an Array /// @param[out] err Error details, if any /// @return Result of the function call -Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err) +Object nvim_call_dict_function(Object dict, String fn, Array args, Arena *arena, Error *err) FUNC_API_SINCE(4) { Object rv = OBJECT_INIT; @@ -337,7 +337,7 @@ Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err) goto end; } - rv = _call_function(fn, args, self_dict, err); + rv = _call_function(fn, args, self_dict, arena, err); end: if (mustfree) { tv_clear(&rettv); diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 93c9dfa049..ed1ad5b583 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -237,7 +237,7 @@ void nvim_win_set_width(Window window, Integer width, Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Variable value -Object nvim_win_get_var(Window window, String name, Error *err) +Object nvim_win_get_var(Window window, String name, Arena *arena, Error *err) FUNC_API_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -246,7 +246,7 @@ Object nvim_win_get_var(Window window, String name, Error *err) return (Object)OBJECT_INIT; } - return dict_get_value(win->w_vars, name, err); + return dict_get_value(win->w_vars, name, arena, err); } /// Sets a window-scoped (w:) variable @@ -264,7 +264,7 @@ void nvim_win_set_var(Window window, String name, Object value, Error *err) return; } - dict_set_var(win->w_vars, name, value, false, false, err); + dict_set_var(win->w_vars, name, value, false, false, NULL, err); } /// Removes a window-scoped (w:) variable @@ -281,7 +281,7 @@ void nvim_win_del_var(Window window, String name, Error *err) return; } - dict_set_var(win->w_vars, name, NIL, true, false, err); + dict_set_var(win->w_vars, name, NIL, true, false, NULL, err); } /// Gets the window position in display cells. First position is zero. @@ -289,15 +289,16 @@ void nvim_win_del_var(Window window, String name, Error *err) /// @param window Window handle, or 0 for current window /// @param[out] err Error details, if any /// @return (row, col) tuple with the window position -ArrayOf(Integer, 2) nvim_win_get_position(Window window, Error *err) +ArrayOf(Integer, 2) nvim_win_get_position(Window window, Arena *arena, Error *err) FUNC_API_SINCE(1) { Array rv = ARRAY_DICT_INIT; win_T *win = find_window_by_handle(window, err); if (win) { - ADD(rv, INTEGER_OBJ(win->w_winrow)); - ADD(rv, INTEGER_OBJ(win->w_wincol)); + rv = arena_array(arena, 2); + ADD_C(rv, INTEGER_OBJ(win->w_winrow)); + ADD_C(rv, INTEGER_OBJ(win->w_wincol)); } return rv; -- cgit From 55a4aa41bb27aa9439fea11c4ebcfec2ee87844d Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Thu, 15 Feb 2024 19:56:58 -0600 Subject: docs: document breaking change for nvim_create_autocmd callback (#27484) https://github.com/neovim/neovim/pull/27428 changed the semantics of callbacks passed to nvim_create_autocmd such that any truthy value will delete the autocommand (rather than just the literal boolean value `true`). Update the documentation accordingly and add an entry to `news.txt`. The behavior is now consistent between nvim_create_autocmd and nvim_buf_attach. --- src/nvim/api/autocmd.c | 5 +++-- src/nvim/api/buffer.c | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 4f67f682f1..90b7f3a5c6 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -378,8 +378,9 @@ cleanup: /// |autocmd-buflocal|. Cannot be used with {pattern}. /// - desc (string) optional: description (for documentation and troubleshooting). /// - callback (function|string) optional: Lua function (or Vimscript function name, if -/// string) called when the event(s) is triggered. Lua callback can return true to -/// delete the autocommand, and receives a table argument with these keys: +/// string) called when the event(s) is triggered. Lua callback can return a truthy +/// value (not `false` or `nil`) to delete the autocommand. Receives a table argument +/// with these keys: /// - id: (number) autocommand id /// - event: (string) name of the triggered event |autocmd-events| /// - group: (number|nil) autocommand group id, if any diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 110154f8de..257e5e6b05 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -112,7 +112,7 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err) /// Not for Lua callbacks. /// @param opts Optional parameters. /// - on_lines: Lua callback invoked on change. -/// Return `true` to detach. Args: +/// Return a truthy value (not `false` or `nil`) to detach. Args: /// - the string "lines" /// - buffer handle /// - b:changedtick @@ -125,8 +125,7 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err) /// - on_bytes: Lua callback invoked on change. /// This callback receives more granular information about the /// change compared to on_lines. -/// Return `true` to detach. -/// Args: +/// Return a truthy value (not `false` or `nil`) to detach. Args: /// - the string "bytes" /// - buffer handle /// - b:changedtick -- cgit From 5fd1bac65ed07310eda5fd976b651cc493002849 Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Sat, 17 Feb 2024 00:44:31 +0100 Subject: fix(api): don't use stl 'fillchar' for "use_statuscol_lnum" (#27501) Problem: nvim_eval_statusline() uses "stl" from 'fillchars' with "use_statuscol_lnum". Solution: Reorder "fillchar" else chain. --- src/nvim/api/vim.c | 73 +++++++++++++++++++++++++----------------------------- 1 file changed, 34 insertions(+), 39 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index b7cb14867d..1184eb7441 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2203,49 +2203,44 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena * statuscol_T statuscol = { 0 }; SignTextAttrs sattrs[SIGN_SHOW_MAX] = { 0 }; - if (opts->use_tabline) { - fillchar = schar_from_ascii(' '); - } else { - if (fillchar == 0) { - if (opts->use_winbar) { - fillchar = wp->w_p_fcs_chars.wbr; - } else { - int attr; - fillchar = fillchar_status(&attr, wp); + if (statuscol_lnum) { + int line_id = 0; + int cul_id = 0; + int num_id = 0; + linenr_T lnum = statuscol_lnum; + decor_redraw_signs(wp, wp->w_buffer, lnum - 1, sattrs, &line_id, &cul_id, &num_id); + + statuscol.sattrs = sattrs; + statuscol.foldinfo = fold_info(wp, lnum); + wp->w_cursorline = win_cursorline_standout(wp) ? wp->w_cursor.lnum : 0; + + if (wp->w_p_cul) { + if (statuscol.foldinfo.fi_level != 0 && statuscol.foldinfo.fi_lines > 0) { + wp->w_cursorline = statuscol.foldinfo.fi_lnum; } + statuscol.use_cul = lnum == wp->w_cursorline && (wp->w_p_culopt_flags & CULOPT_NBR); } - if (statuscol_lnum) { - int line_id = 0; - int cul_id = 0; - int num_id = 0; - linenr_T lnum = statuscol_lnum; - decor_redraw_signs(wp, wp->w_buffer, lnum - 1, sattrs, &line_id, &cul_id, &num_id); - - statuscol.sattrs = sattrs; - statuscol.foldinfo = fold_info(wp, lnum); - wp->w_cursorline = win_cursorline_standout(wp) ? wp->w_cursor.lnum : 0; - - if (wp->w_p_cul) { - if (statuscol.foldinfo.fi_level != 0 && statuscol.foldinfo.fi_lines > 0) { - wp->w_cursorline = statuscol.foldinfo.fi_lnum; - } - statuscol.use_cul = lnum == wp->w_cursorline && (wp->w_p_culopt_flags & CULOPT_NBR); - } - statuscol.sign_cul_id = statuscol.use_cul ? cul_id : 0; - if (num_id) { - stc_hl_id = num_id; - } else if (statuscol.use_cul) { - stc_hl_id = HLF_CLN + 1; - } else if (wp->w_p_rnu) { - stc_hl_id = (lnum < wp->w_cursor.lnum ? HLF_LNA : HLF_LNB) + 1; - } else { - stc_hl_id = HLF_N + 1; - } + statuscol.sign_cul_id = statuscol.use_cul ? cul_id : 0; + if (num_id) { + stc_hl_id = num_id; + } else if (statuscol.use_cul) { + stc_hl_id = HLF_CLN + 1; + } else if (wp->w_p_rnu) { + stc_hl_id = (lnum < wp->w_cursor.lnum ? HLF_LNA : HLF_LNB) + 1; + } else { + stc_hl_id = HLF_N + 1; + } - set_vim_var_nr(VV_LNUM, lnum); - set_vim_var_nr(VV_RELNUM, labs(get_cursor_rel_lnum(wp, lnum))); - set_vim_var_nr(VV_VIRTNUM, 0); + set_vim_var_nr(VV_LNUM, lnum); + set_vim_var_nr(VV_RELNUM, labs(get_cursor_rel_lnum(wp, lnum))); + set_vim_var_nr(VV_VIRTNUM, 0); + } else if (fillchar == 0 && !opts->use_tabline) { + if (opts->use_winbar) { + fillchar = wp->w_p_fcs_chars.wbr; + } else { + int attr; + fillchar = fillchar_status(&attr, wp); } } -- cgit From f25fcc68a34c2d51b0715fadc62cb50509de338b Mon Sep 17 00:00:00 2001 From: bfredl Date: Sat, 17 Feb 2024 20:31:21 +0100 Subject: refactor(api): use an arena for mappings --- src/nvim/api/autocmd.c | 2 +- src/nvim/api/buffer.c | 4 ++-- src/nvim/api/extmark.c | 2 +- src/nvim/api/private/helpers.c | 9 +++++++++ src/nvim/api/private/helpers.h | 2 ++ src/nvim/api/vim.c | 4 ++-- src/nvim/api/window.c | 7 ++++--- 7 files changed, 21 insertions(+), 9 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 90b7f3a5c6..1b5d1927ad 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -294,7 +294,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) break; case kCallbackFuncref: case kCallbackPartial: - PUT(autocmd_info, "callback", CSTR_AS_OBJ(callback_to_string(cb))); + PUT(autocmd_info, "callback", CSTR_AS_OBJ(callback_to_string(cb, NULL))); break; case kCallbackNone: abort(); diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 257e5e6b05..ddca85945a 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -905,7 +905,7 @@ Integer nvim_buf_get_changedtick(Buffer buffer, Error *err) /// @param[out] err Error details, if any /// @returns Array of |maparg()|-like dictionaries describing mappings. /// The "buffer" key holds the associated buffer handle. -ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err) +ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Arena *arena, Error *err) FUNC_API_SINCE(3) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -914,7 +914,7 @@ ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err) return (Array)ARRAY_DICT_INIT; } - return keymap_array(mode, buf); + return keymap_array(mode, buf, arena); } /// Sets a buffer-local |mapping| for the given mode. diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 94f6059014..f6f7d332ec 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -346,7 +346,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e rv = arena_array(arena, kv_size(marks)); for (size_t i = 0; i < kv_size(marks); i++) { - ADD(rv, ARRAY_OBJ(extmark_to_array(kv_A(marks, i), true, details, hl_name, arena))); + ADD_C(rv, ARRAY_OBJ(extmark_to_array(kv_A(marks, i), true, details, hl_name, arena))); } kv_destroy(marks); diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 1446683b0c..3cbf16fa72 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -581,6 +581,15 @@ String arena_string(Arena *arena, String str) } } +Array arena_take_arraybuilder(Arena *arena, ArrayBuilder *arr) +{ + Array ret = arena_array(arena, kv_size(*arr)); + ret.size = kv_size(*arr); + memcpy((ret).items, (*arr).items, sizeof((ret).items[0]) * (ret).size); + kvi_destroy(*arr); + return ret; +} + void api_free_object(Object value) { switch (value.type) { diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 395c5a9d1f..20bc889a0a 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -95,6 +95,8 @@ name.capacity = maxsize; \ name.items = name##__items; \ +typedef kvec_withinit_t(Object, 16) ArrayBuilder; + #define cbuf_as_string(d, s) ((String) { .data = d, .size = s }) #define STATIC_CSTR_AS_STRING(s) ((String) { .data = s, .size = sizeof("" s) - 1 }) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 1184eb7441..195da4366e 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1456,10 +1456,10 @@ Dictionary nvim_get_mode(Arena *arena) /// @param mode Mode short-name ("n", "i", "v", ...) /// @returns Array of |maparg()|-like dictionaries describing mappings. /// The "buffer" key is always zero. -ArrayOf(Dictionary) nvim_get_keymap(String mode) +ArrayOf(Dictionary) nvim_get_keymap(String mode, Arena *arena) FUNC_API_SINCE(3) { - return keymap_array(mode, NULL); + return keymap_array(mode, NULL, arena); } /// Sets a global |mapping| for the given mode. diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index ed1ad5b583..c41c5d4b07 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -77,15 +77,16 @@ void nvim_win_set_buf(Window window, Buffer buffer, Error *err) /// @param window Window handle, or 0 for current window /// @param[out] err Error details, if any /// @return (row, col) tuple -ArrayOf(Integer, 2) nvim_win_get_cursor(Window window, Error *err) +ArrayOf(Integer, 2) nvim_win_get_cursor(Window window, Arena *arena, Error *err) FUNC_API_SINCE(1) { Array rv = ARRAY_DICT_INIT; win_T *win = find_window_by_handle(window, err); if (win) { - ADD(rv, INTEGER_OBJ(win->w_cursor.lnum)); - ADD(rv, INTEGER_OBJ(win->w_cursor.col)); + rv = arena_array(arena, 2); + ADD_C(rv, INTEGER_OBJ(win->w_cursor.lnum)); + ADD_C(rv, INTEGER_OBJ(win->w_cursor.col)); } return rv; -- cgit From 146333ca123ab85397eb089345569cd9ed2d405a Mon Sep 17 00:00:00 2001 From: bfredl Date: Sat, 17 Feb 2024 20:33:11 +0100 Subject: refactor(api): use arena for channel info and terminal info --- src/nvim/api/ui.c | 2 +- src/nvim/api/vim.c | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 140b520cb5..d5843caa96 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -1046,7 +1046,7 @@ static Array translate_firstarg(UI *ui, Array args, Arena *arena) ADD_C(new_args, ARRAY_OBJ(translate_contents(ui, contents, arena))); for (size_t i = 1; i < args.size; i++) { - ADD(new_args, args.items[i]); + ADD_C(new_args, args.items[i]); } return new_args; } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 195da4366e..7a19ccf295 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1633,7 +1633,7 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version, /// the RPC channel), if provided by it via /// |nvim_set_client_info()|. /// -Dictionary nvim_get_chan_info(uint64_t channel_id, Integer chan, Error *err) +Dictionary nvim_get_chan_info(uint64_t channel_id, Integer chan, Arena *arena, Error *err) FUNC_API_SINCE(4) { if (chan < 0) { @@ -1644,17 +1644,17 @@ Dictionary nvim_get_chan_info(uint64_t channel_id, Integer chan, Error *err) assert(channel_id <= INT64_MAX); chan = (Integer)channel_id; } - return channel_info((uint64_t)chan); + return channel_info((uint64_t)chan, arena); } /// Get information about all open channels. /// /// @returns Array of Dictionaries, each describing a channel with /// the format specified at |nvim_get_chan_info()|. -Array nvim_list_chans(void) +Array nvim_list_chans(Arena *arena) FUNC_API_SINCE(4) { - return channel_all_info(); + return channel_all_info(arena); } /// Calls many API methods atomically. @@ -1891,7 +1891,7 @@ Array nvim_get_proc_children(Integer pid, Arena *arena, Error *err) // syscall failed (possibly because of kernel options), try shelling out. DLOG("fallback to vim._os_proc_children()"); MAXSIZE_TEMP_ARRAY(a, 1); - ADD(a, INTEGER_OBJ(pid)); + ADD_C(a, INTEGER_OBJ(pid)); Object o = NLUA_EXEC_STATIC("return vim._os_proc_children(...)", a, kRetObject, arena, err); if (o.type == kObjectTypeArray) { rvobj = o.data.array; @@ -1903,7 +1903,7 @@ Array nvim_get_proc_children(Integer pid, Arena *arena, Error *err) } else { rvobj = arena_array(arena, proc_count); for (size_t i = 0; i < proc_count; i++) { - ADD(rvobj, INTEGER_OBJ(proc_list[i])); + ADD_C(rvobj, INTEGER_OBJ(proc_list[i])); } } @@ -1925,7 +1925,7 @@ Object nvim_get_proc(Integer pid, Arena *arena, Error *err) }); #ifdef MSWIN - rvobj = DICTIONARY_OBJ(os_proc_info((int)pid)); + rvobj = DICTIONARY_OBJ(os_proc_info((int)pid, arena)); if (rvobj.data.dictionary.size == 0) { // Process not found. return NIL; } -- cgit From 97531be1f766e6cad79e6360ae9cb827434cff3c Mon Sep 17 00:00:00 2001 From: bfredl Date: Sat, 17 Feb 2024 20:33:58 +0100 Subject: refactor(api): use arena for autocmds --- src/nvim/api/autocmd.c | 217 +++++++++++++++++++---------------------- src/nvim/api/private/helpers.c | 2 +- 2 files changed, 102 insertions(+), 117 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 1b5d1927ad..7e33a77d24 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -23,6 +23,7 @@ #include "nvim/globals.h" #include "nvim/lua/executor.h" #include "nvim/memory.h" +#include "nvim/strings.h" #include "nvim/types_defs.h" #include "nvim/vim_defs.h" @@ -33,12 +34,12 @@ #define AUCMD_MAX_PATTERNS 256 // Copy string or array of strings into an empty array. -// Get the event number, unless it is an error. Then goto `goto_name`. -#define GET_ONE_EVENT(event_nr, event_str, goto_name) \ +// Get the event number, unless it is an error. Then do `or_else`. +#define GET_ONE_EVENT(event_nr, event_str, or_else) \ event_T event_nr = \ event_name2nr_str(event_str.data.string); \ VALIDATE_S((event_nr < NUM_EVENTS), "event", event_str.data.string.data, { \ - goto goto_name; \ + or_else; \ }); // ID for associating autocmds created via nvim_create_autocmd @@ -88,14 +89,14 @@ static int64_t next_autocmd_id = 1; /// If the autocommand is buffer local |autocmd-buffer-local|: /// - buflocal (boolean): true if the autocommand is buffer local. /// - buffer (number): the buffer number. -Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) +Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err) FUNC_API_SINCE(9) { // TODO(tjdevries): Would be cool to add nvim_get_autocmds({ id = ... }) - Array autocmd_list = ARRAY_DICT_INIT; + ArrayBuilder autocmd_list = KV_INITIAL_VALUE; + kvi_init(autocmd_list); char *pattern_filters[AUCMD_MAX_PATTERNS]; - char pattern_buflocal[BUFLOCAL_PAT_LEN]; Array buffers = ARRAY_DICT_INIT; @@ -131,7 +132,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) Object v = opts->event; if (v.type == kObjectTypeString) { - GET_ONE_EVENT(event_nr, v, cleanup); + GET_ONE_EVENT(event_nr, v, goto cleanup); event_set[event_nr] = true; } else if (v.type == kObjectTypeArray) { FOREACH_ITEM(v.data.array, event_v, { @@ -139,7 +140,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) goto cleanup; }); - GET_ONE_EVENT(event_nr, event_v, cleanup); + GET_ONE_EVENT(event_nr, event_v, goto cleanup); event_set[event_nr] = true; }) } else { @@ -187,8 +188,9 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) goto cleanup; } - snprintf(pattern_buflocal, BUFLOCAL_PAT_LEN, "", (int)buf->handle); - ADD(buffers, CSTR_TO_OBJ(pattern_buflocal)); + String pat = arena_printf(arena, "", (int)buf->handle); + buffers = arena_array(arena, 1); + ADD_C(buffers, STRING_OBJ(pat)); } else if (opts->buffer.type == kObjectTypeArray) { if (opts->buffer.data.array.size > AUCMD_MAX_PATTERNS) { api_set_error(err, kErrorTypeValidation, "Too many buffers (maximum of %d)", @@ -196,6 +198,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) goto cleanup; } + buffers = arena_array(arena, kv_size(opts->buffer.data.array)); FOREACH_ITEM(opts->buffer.data.array, bufnr, { VALIDATE_EXP((bufnr.type == kObjectTypeInteger || bufnr.type == kObjectTypeBuffer), "buffer", "Integer", api_typename(bufnr.type), { @@ -207,8 +210,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) goto cleanup; } - snprintf(pattern_buflocal, BUFLOCAL_PAT_LEN, "", (int)buf->handle); - ADD(buffers, CSTR_TO_OBJ(pattern_buflocal)); + ADD_C(buffers, STRING_OBJ(arena_printf(arena, "", (int)buf->handle))); }); } else if (HAS_KEY(opts, get_autocmds, buffer)) { VALIDATE_EXP(false, "buffer", "Integer or Array", api_typename(opts->buffer.type), { @@ -250,6 +252,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) char *pat = pattern_filters[j]; int patlen = (int)strlen(pat); + char pattern_buflocal[BUFLOCAL_PAT_LEN]; if (aupat_is_buflocal(pat, patlen)) { aupat_normalize_buflocal_pat(pattern_buflocal, pat, patlen, aupat_get_buflocal_nr(pat, patlen)); @@ -267,51 +270,51 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) } } - Dictionary autocmd_info = ARRAY_DICT_INIT; + Dictionary autocmd_info = arena_dict(arena, 11); if (ap->group != AUGROUP_DEFAULT) { - PUT(autocmd_info, "group", INTEGER_OBJ(ap->group)); - PUT(autocmd_info, "group_name", CSTR_TO_OBJ(augroup_name(ap->group))); + PUT_C(autocmd_info, "group", INTEGER_OBJ(ap->group)); + PUT_C(autocmd_info, "group_name", CSTR_AS_OBJ(augroup_name(ap->group))); } if (ac->id > 0) { - PUT(autocmd_info, "id", INTEGER_OBJ(ac->id)); + PUT_C(autocmd_info, "id", INTEGER_OBJ(ac->id)); } if (ac->desc != NULL) { - PUT(autocmd_info, "desc", CSTR_TO_OBJ(ac->desc)); + PUT_C(autocmd_info, "desc", CSTR_AS_OBJ(ac->desc)); } if (ac->exec.type == CALLABLE_CB) { - PUT(autocmd_info, "command", STRING_OBJ(STRING_INIT)); + PUT_C(autocmd_info, "command", STRING_OBJ(STRING_INIT)); Callback *cb = &ac->exec.callable.cb; switch (cb->type) { case kCallbackLua: if (nlua_ref_is_function(cb->data.luaref)) { - PUT(autocmd_info, "callback", LUAREF_OBJ(api_new_luaref(cb->data.luaref))); + PUT_C(autocmd_info, "callback", LUAREF_OBJ(api_new_luaref(cb->data.luaref))); } break; case kCallbackFuncref: case kCallbackPartial: - PUT(autocmd_info, "callback", CSTR_AS_OBJ(callback_to_string(cb, NULL))); + PUT_C(autocmd_info, "callback", CSTR_AS_OBJ(callback_to_string(cb, arena))); break; case kCallbackNone: abort(); } } else { - PUT(autocmd_info, "command", CSTR_TO_OBJ(ac->exec.callable.cmd)); + PUT_C(autocmd_info, "command", CSTR_AS_OBJ(ac->exec.callable.cmd)); } - PUT(autocmd_info, "pattern", CSTR_TO_OBJ(ap->pat)); - PUT(autocmd_info, "event", CSTR_TO_OBJ(event_nr2name(event))); - PUT(autocmd_info, "once", BOOLEAN_OBJ(ac->once)); + PUT_C(autocmd_info, "pattern", CSTR_AS_OBJ(ap->pat)); + PUT_C(autocmd_info, "event", CSTR_AS_OBJ(event_nr2name(event))); + PUT_C(autocmd_info, "once", BOOLEAN_OBJ(ac->once)); if (ap->buflocal_nr) { - PUT(autocmd_info, "buflocal", BOOLEAN_OBJ(true)); - PUT(autocmd_info, "buffer", INTEGER_OBJ(ap->buflocal_nr)); + PUT_C(autocmd_info, "buflocal", BOOLEAN_OBJ(true)); + PUT_C(autocmd_info, "buffer", INTEGER_OBJ(ap->buflocal_nr)); } else { - PUT(autocmd_info, "buflocal", BOOLEAN_OBJ(false)); + PUT_C(autocmd_info, "buflocal", BOOLEAN_OBJ(false)); } // TODO(sctx): It would be good to unify script_ctx to actually work with lua @@ -328,16 +331,15 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) // Once we do that, we can put these into the autocmd_info, but I don't think it's // useful to do that at this time. // - // PUT(autocmd_info, "sid", INTEGER_OBJ(ac->script_ctx.sc_sid)); - // PUT(autocmd_info, "lnum", INTEGER_OBJ(ac->script_ctx.sc_lnum)); + // PUT_C(autocmd_info, "sid", INTEGER_OBJ(ac->script_ctx.sc_sid)); + // PUT_C(autocmd_info, "lnum", INTEGER_OBJ(ac->script_ctx.sc_lnum)); - ADD(autocmd_list, DICTIONARY_OBJ(autocmd_info)); + kvi_push(autocmd_list, DICTIONARY_OBJ(autocmd_info)); } } cleanup: - api_free_array(buffers); - return autocmd_list; + return arena_take_arraybuilder(arena, &autocmd_list); } /// Creates an |autocommand| event handler, defined by `callback` (Lua function or Vimscript @@ -399,17 +401,16 @@ cleanup: /// @see |autocommand| /// @see |nvim_del_autocmd()| Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autocmd) *opts, - Error *err) + Arena *arena, Error *err) FUNC_API_SINCE(9) { int64_t autocmd_id = -1; char *desc = NULL; - Array patterns = ARRAY_DICT_INIT; - Array event_array = ARRAY_DICT_INIT; AucmdExecutable aucmd = AUCMD_EXECUTABLE_INIT; Callback cb = CALLBACK_NONE; - if (!unpack_string_or_array(&event_array, &event, "event", true, err)) { + Array event_array = unpack_string_or_array(event, "event", true, arena, err); + if (ERROR_SET(err)) { goto cleanup; } @@ -469,7 +470,9 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc goto cleanup; }); - if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, has_buffer, opts->buffer, err)) { + Array patterns = get_patterns_from_pattern_or_buf(opts->pattern, has_buffer, opts->buffer, "*", + arena, err); + if (ERROR_SET(err)) { goto cleanup; } @@ -477,17 +480,13 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc desc = opts->desc.data; } - if (patterns.size == 0) { - ADD(patterns, STATIC_CSTR_TO_OBJ("*")); - } - VALIDATE_R((event_array.size > 0), "event", { goto cleanup; }); autocmd_id = next_autocmd_id++; FOREACH_ITEM(event_array, event_str, { - GET_ONE_EVENT(event_nr, event_str, cleanup); + GET_ONE_EVENT(event_nr, event_str, goto cleanup); int retval; @@ -514,8 +513,6 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc cleanup: aucmd_exec_free(&aucmd); - api_free_array(event_array); - api_free_array(patterns); return autocmd_id; } @@ -555,7 +552,7 @@ void nvim_del_autocmd(Integer id, Error *err) /// - group: (string|int) The augroup name or id. /// - NOTE: If not passed, will only delete autocmds *not* in any group. /// -void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Error *err) +void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Arena *arena, Error *err) FUNC_API_SINCE(9) { // TODO(tjdevries): Future improvements: @@ -564,33 +561,29 @@ void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Error *err) // - group: Allow passing "*" or true or something like that to force doing all // autocmds, regardless of their group. - Array patterns = ARRAY_DICT_INIT; - Array event_array = ARRAY_DICT_INIT; - - if (!unpack_string_or_array(&event_array, &opts->event, "event", false, err)) { - goto cleanup; + Array event_array = unpack_string_or_array(opts->event, "event", false, arena, err); + if (ERROR_SET(err)) { + return; } bool has_buffer = HAS_KEY(opts, clear_autocmds, buffer); VALIDATE((!HAS_KEY(opts, clear_autocmds, pattern) || !has_buffer), "%s", "Cannot use both 'pattern' and 'buffer'", { - goto cleanup; + return; }); int au_group = get_augroup_from_object(opts->group, err); if (au_group == AUGROUP_ERROR) { - goto cleanup; - } - - if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, has_buffer, opts->buffer, err)) { - goto cleanup; + return; } // When we create the autocmds, we want to say that they are all matched, so that's * // but when we clear them, we want to say that we didn't pass a pattern, so that's NUL - if (patterns.size == 0) { - ADD(patterns, STATIC_CSTR_TO_OBJ("")); + Array patterns = get_patterns_from_pattern_or_buf(opts->pattern, has_buffer, opts->buffer, "", + arena, err); + if (ERROR_SET(err)) { + return; } // If we didn't pass any events, that means clear all events. @@ -599,26 +592,22 @@ void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Error *err) FOREACH_ITEM(patterns, pat_object, { char *pat = pat_object.data.string.data; if (!clear_autocmd(event, pat, au_group, err)) { - goto cleanup; + return; } }); } } else { FOREACH_ITEM(event_array, event_str, { - GET_ONE_EVENT(event_nr, event_str, cleanup); + GET_ONE_EVENT(event_nr, event_str, return ); FOREACH_ITEM(patterns, pat_object, { char *pat = pat_object.data.string.data; if (!clear_autocmd(event_nr, pat, au_group, err)) { - goto cleanup; + return; } }); }); } - -cleanup: - api_free_array(event_array); - api_free_array(patterns); } /// Create or get an autocommand group |autocmd-groups|. @@ -709,7 +698,7 @@ void nvim_del_augroup_by_name(String name, Error *err) /// - data (any): arbitrary data to send to the autocommand callback. See /// |nvim_create_autocmd()| for details. /// @see |:doautocmd| -void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) +void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Arena *arena, Error *err) FUNC_API_SINCE(9) { int au_group = AUGROUP_ALL; @@ -717,13 +706,11 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) buf_T *buf = curbuf; - Array patterns = ARRAY_DICT_INIT; - Array event_array = ARRAY_DICT_INIT; - Object *data = NULL; - if (!unpack_string_or_array(&event_array, &event, "event", true, err)) { - goto cleanup; + Array event_array = unpack_string_or_array(event, "event", true, arena, err); + if (ERROR_SET(err)) { + return; } switch (opts->group.type) { @@ -732,19 +719,19 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) case kObjectTypeString: au_group = augroup_find(opts->group.data.string.data); VALIDATE_S((au_group != AUGROUP_ERROR), "group", opts->group.data.string.data, { - goto cleanup; + return; }); break; case kObjectTypeInteger: au_group = (int)opts->group.data.integer; char *name = au_group == 0 ? NULL : augroup_name(au_group); VALIDATE_INT(augroup_exists(name), "group", (int64_t)au_group, { - goto cleanup; + return; }); break; default: VALIDATE_EXP(false, "group", "String or Integer", api_typename(opts->group.type), { - goto cleanup; + return; }); } @@ -752,23 +739,21 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) if (HAS_KEY(opts, exec_autocmds, buffer)) { VALIDATE((!HAS_KEY(opts, exec_autocmds, pattern)), "%s", "Cannot use both 'pattern' and 'buffer' for the same autocmd", { - goto cleanup; + return; }); has_buffer = true; buf = find_buffer_by_handle(opts->buffer, err); if (ERROR_SET(err)) { - goto cleanup; + return; } } - if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, has_buffer, opts->buffer, err)) { - goto cleanup; - } - - if (patterns.size == 0) { - ADD(patterns, STATIC_CSTR_TO_OBJ("")); + Array patterns = get_patterns_from_pattern_or_buf(opts->pattern, has_buffer, opts->buffer, "", + arena, err); + if (ERROR_SET(err)) { + return; } if (HAS_KEY(opts, exec_autocmds, data)) { @@ -779,7 +764,7 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) bool did_aucmd = false; FOREACH_ITEM(event_array, event_str, { - GET_ONE_EVENT(event_nr, event_str, cleanup) + GET_ONE_EVENT(event_nr, event_str, return ) FOREACH_ITEM(patterns, pat, { char *fname = !has_buffer ? pat.data.string.data : NULL; @@ -790,28 +775,26 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) if (did_aucmd && modeline) { do_modelines(0); } - -cleanup: - api_free_array(event_array); - api_free_array(patterns); } -static bool unpack_string_or_array(Array *array, Object *v, char *k, bool required, Error *err) +static Array unpack_string_or_array(Object v, char *k, bool required, Arena *arena, Error *err) { - if (v->type == kObjectTypeString) { - ADD(*array, copy_object(*v, NULL)); - } else if (v->type == kObjectTypeArray) { - if (!check_string_array(v->data.array, k, true, err)) { - return false; + if (v.type == kObjectTypeString) { + Array arr = arena_array(arena, 1); + ADD_C(arr, v); + return arr; + } else if (v.type == kObjectTypeArray) { + if (!check_string_array(v.data.array, k, true, err)) { + return (Array)ARRAY_DICT_INIT; } - *array = copy_array(v->data.array, NULL); + return v.data.array; } else { - VALIDATE_EXP(!required, k, "Array or String", api_typename(v->type), { - return false; + VALIDATE_EXP(!required, k, "Array or String", api_typename(v.type), { + return (Array)ARRAY_DICT_INIT; }); } - return true; + return (Array)ARRAY_DICT_INIT; } // Returns AUGROUP_ERROR if there was a problem with {group} @@ -843,55 +826,57 @@ static int get_augroup_from_object(Object group, Error *err) } } -static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, bool has_buffer, - Buffer buffer, Error *err) +static Array get_patterns_from_pattern_or_buf(Object pattern, bool has_buffer, Buffer buffer, + char *fallback, Arena *arena, Error *err) { - const char pattern_buflocal[BUFLOCAL_PAT_LEN]; + ArrayBuilder patterns = ARRAY_DICT_INIT; + kvi_init(patterns); if (pattern.type != kObjectTypeNil) { - Object *v = &pattern; - - if (v->type == kObjectTypeString) { - const char *pat = v->data.string.data; + if (pattern.type == kObjectTypeString) { + const char *pat = pattern.data.string.data; size_t patlen = aucmd_pattern_length(pat); while (patlen) { - ADD(*patterns, STRING_OBJ(cbuf_to_string(pat, patlen))); + kvi_push(patterns, CBUF_TO_ARENA_OBJ(arena, pat, patlen)); pat = aucmd_next_pattern(pat, patlen); patlen = aucmd_pattern_length(pat); } - } else if (v->type == kObjectTypeArray) { - if (!check_string_array(v->data.array, "pattern", true, err)) { - return false; + } else if (pattern.type == kObjectTypeArray) { + if (!check_string_array(pattern.data.array, "pattern", true, err)) { + return (Array)ARRAY_DICT_INIT; } - Array array = v->data.array; + Array array = pattern.data.array; FOREACH_ITEM(array, entry, { const char *pat = entry.data.string.data; size_t patlen = aucmd_pattern_length(pat); while (patlen) { - ADD(*patterns, STRING_OBJ(cbuf_to_string(pat, patlen))); + kvi_push(patterns, CBUF_TO_ARENA_OBJ(arena, pat, patlen)); pat = aucmd_next_pattern(pat, patlen); patlen = aucmd_pattern_length(pat); } }) } else { - VALIDATE_EXP(false, "pattern", "String or Table", api_typename(v->type), { - return false; + VALIDATE_EXP(false, "pattern", "String or Table", api_typename(pattern.type), { + return (Array)ARRAY_DICT_INIT; }); } } else if (has_buffer) { buf_T *buf = find_buffer_by_handle(buffer, err); if (ERROR_SET(err)) { - return false; + return (Array)ARRAY_DICT_INIT; } - snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "", (int)buf->handle); - ADD(*patterns, CSTR_TO_OBJ(pattern_buflocal)); + kvi_push(patterns, STRING_OBJ(arena_printf(arena, "", (int)buf->handle))); } - return true; + if (kv_size(patterns) == 0 && fallback) { + kvi_push(patterns, CSTR_AS_OBJ(fallback)); + } + + return arena_take_arraybuilder(arena, &patterns); } static bool clear_autocmd(event_T event, char *pat, int au_group, Error *err) diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 3cbf16fa72..983f1a4fed 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -585,7 +585,7 @@ Array arena_take_arraybuilder(Arena *arena, ArrayBuilder *arr) { Array ret = arena_array(arena, kv_size(*arr)); ret.size = kv_size(*arr); - memcpy((ret).items, (*arr).items, sizeof((ret).items[0]) * (ret).size); + memcpy(ret.items, arr->items, sizeof(ret.items[0]) * ret.size); kvi_destroy(*arr); return ret; } -- cgit From bbf6d4a4bc44c8e61f807d23cc4ff2c2683e1ef4 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 18 Feb 2024 12:51:27 +0100 Subject: refactor(api): use arena for metadata; msgpack_rpc_to_object delenda est Note: kSDItemHeader is something is _written_ by nvim in the shada file to identify it for debugging purposes outside of nvim. But this data wasn't ever used by neovim after reading the file back, So I removed the parsing of it for now. --- src/nvim/api/private/helpers.c | 116 ++++++++++++++++++----------------------- src/nvim/api/vim.c | 4 +- 2 files changed, 53 insertions(+), 67 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 983f1a4fed..63c5689109 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -30,6 +30,7 @@ #include "nvim/memory_defs.h" #include "nvim/message.h" #include "nvim/msgpack_rpc/helpers.h" +#include "nvim/msgpack_rpc/unpacker.h" #include "nvim/pos_defs.h" #include "nvim/types_defs.h" #include "nvim/ui.h" @@ -650,102 +651,87 @@ void api_clear_error(Error *value) value->type = kErrorTypeNone; } +// initialized once, never freed +static ArenaMem mem_for_metadata = NULL; + /// @returns a shared value. caller must not modify it! Dictionary api_metadata(void) { static Dictionary metadata = ARRAY_DICT_INIT; if (!metadata.size) { - PUT(metadata, "version", DICTIONARY_OBJ(version_dict())); - init_function_metadata(&metadata); - init_ui_event_metadata(&metadata); - init_error_type_metadata(&metadata); - init_type_metadata(&metadata); + Arena arena = ARENA_EMPTY; + Error err = ERROR_INIT; + metadata = arena_dict(&arena, 6); + PUT_C(metadata, "version", DICTIONARY_OBJ(version_dict(&arena))); + PUT_C(metadata, "functions", + unpack((char *)funcs_metadata, sizeof(funcs_metadata), &arena, &err)); + if (ERROR_SET(&err)) { + abort(); + } + PUT_C(metadata, "ui_events", + unpack((char *)ui_events_metadata, sizeof(ui_events_metadata), &arena, &err)); + if (ERROR_SET(&err)) { + abort(); + } + PUT_C(metadata, "ui_options", ARRAY_OBJ(ui_options_metadata(&arena))); + PUT_C(metadata, "error_types", DICTIONARY_OBJ(error_type_metadata(&arena))); + PUT_C(metadata, "types", DICTIONARY_OBJ(type_metadata(&arena))); + mem_for_metadata = arena_finish(&arena); } return metadata; } -static void init_function_metadata(Dictionary *metadata) +static Array ui_options_metadata(Arena *arena) { - msgpack_unpacked unpacked; - msgpack_unpacked_init(&unpacked); - if (msgpack_unpack_next(&unpacked, - (const char *)funcs_metadata, - sizeof(funcs_metadata), - NULL) != MSGPACK_UNPACK_SUCCESS) { - abort(); - } - Object functions; - msgpack_rpc_to_object(&unpacked.data, &functions); - msgpack_unpacked_destroy(&unpacked); - PUT(*metadata, "functions", functions); -} - -static void init_ui_event_metadata(Dictionary *metadata) -{ - msgpack_unpacked unpacked; - msgpack_unpacked_init(&unpacked); - if (msgpack_unpack_next(&unpacked, - (const char *)ui_events_metadata, - sizeof(ui_events_metadata), - NULL) != MSGPACK_UNPACK_SUCCESS) { - abort(); - } - Object ui_events; - msgpack_rpc_to_object(&unpacked.data, &ui_events); - msgpack_unpacked_destroy(&unpacked); - PUT(*metadata, "ui_events", ui_events); - Array ui_options = ARRAY_DICT_INIT; - ADD(ui_options, CSTR_TO_OBJ("rgb")); + Array ui_options = arena_array(arena, kUIExtCount + 1); + ADD_C(ui_options, CSTR_AS_OBJ("rgb")); for (UIExtension i = 0; i < kUIExtCount; i++) { if (ui_ext_names[i][0] != '_') { - ADD(ui_options, CSTR_TO_OBJ(ui_ext_names[i])); + ADD_C(ui_options, CSTR_AS_OBJ(ui_ext_names[i])); } } - PUT(*metadata, "ui_options", ARRAY_OBJ(ui_options)); + return ui_options; } -static void init_error_type_metadata(Dictionary *metadata) +static Dictionary error_type_metadata(Arena *arena) { - Dictionary types = ARRAY_DICT_INIT; + Dictionary types = arena_dict(arena, 2); - Dictionary exception_metadata = ARRAY_DICT_INIT; - PUT(exception_metadata, "id", INTEGER_OBJ(kErrorTypeException)); + Dictionary exception_metadata = arena_dict(arena, 1); + PUT_C(exception_metadata, "id", INTEGER_OBJ(kErrorTypeException)); - Dictionary validation_metadata = ARRAY_DICT_INIT; - PUT(validation_metadata, "id", INTEGER_OBJ(kErrorTypeValidation)); + Dictionary validation_metadata = arena_dict(arena, 1); + PUT_C(validation_metadata, "id", INTEGER_OBJ(kErrorTypeValidation)); - PUT(types, "Exception", DICTIONARY_OBJ(exception_metadata)); - PUT(types, "Validation", DICTIONARY_OBJ(validation_metadata)); + PUT_C(types, "Exception", DICTIONARY_OBJ(exception_metadata)); + PUT_C(types, "Validation", DICTIONARY_OBJ(validation_metadata)); - PUT(*metadata, "error_types", DICTIONARY_OBJ(types)); + return types; } -static void init_type_metadata(Dictionary *metadata) +static Dictionary type_metadata(Arena *arena) { - Dictionary types = ARRAY_DICT_INIT; + Dictionary types = arena_dict(arena, 3); - Dictionary buffer_metadata = ARRAY_DICT_INIT; - PUT(buffer_metadata, "id", - INTEGER_OBJ(kObjectTypeBuffer - EXT_OBJECT_TYPE_SHIFT)); - PUT(buffer_metadata, "prefix", CSTR_TO_OBJ("nvim_buf_")); + Dictionary buffer_metadata = arena_dict(arena, 2); + PUT_C(buffer_metadata, "id", INTEGER_OBJ(kObjectTypeBuffer - EXT_OBJECT_TYPE_SHIFT)); + PUT_C(buffer_metadata, "prefix", CSTR_AS_OBJ("nvim_buf_")); - Dictionary window_metadata = ARRAY_DICT_INIT; - PUT(window_metadata, "id", - INTEGER_OBJ(kObjectTypeWindow - EXT_OBJECT_TYPE_SHIFT)); - PUT(window_metadata, "prefix", CSTR_TO_OBJ("nvim_win_")); + Dictionary window_metadata = arena_dict(arena, 2); + PUT_C(window_metadata, "id", INTEGER_OBJ(kObjectTypeWindow - EXT_OBJECT_TYPE_SHIFT)); + PUT_C(window_metadata, "prefix", CSTR_AS_OBJ("nvim_win_")); - Dictionary tabpage_metadata = ARRAY_DICT_INIT; - PUT(tabpage_metadata, "id", - INTEGER_OBJ(kObjectTypeTabpage - EXT_OBJECT_TYPE_SHIFT)); - PUT(tabpage_metadata, "prefix", CSTR_TO_OBJ("nvim_tabpage_")); + Dictionary tabpage_metadata = arena_dict(arena, 2); + PUT_C(tabpage_metadata, "id", INTEGER_OBJ(kObjectTypeTabpage - EXT_OBJECT_TYPE_SHIFT)); + PUT_C(tabpage_metadata, "prefix", CSTR_AS_OBJ("nvim_tabpage_")); - PUT(types, "Buffer", DICTIONARY_OBJ(buffer_metadata)); - PUT(types, "Window", DICTIONARY_OBJ(window_metadata)); - PUT(types, "Tabpage", DICTIONARY_OBJ(tabpage_metadata)); + PUT_C(types, "Buffer", DICTIONARY_OBJ(buffer_metadata)); + PUT_C(types, "Window", DICTIONARY_OBJ(window_metadata)); + PUT_C(types, "Tabpage", DICTIONARY_OBJ(tabpage_metadata)); - PUT(*metadata, "types", DICTIONARY_OBJ(types)); + return types; } // all the copy_[object] functions allow arena=NULL, diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 7a19ccf295..d161dca050 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2022,10 +2022,10 @@ void nvim__invalidate_glyph_cache(void) must_redraw = UPD_CLEAR; } -Object nvim__unpack(String str, Error *err) +Object nvim__unpack(String str, Arena *arena, Error *err) FUNC_API_FAST { - return unpack(str.data, str.size, err); + return unpack(str.data, str.size, arena, err); } /// Deletes an uppercase/file named mark. See |mark-motions|. -- cgit From 1d95abc66b0fcbd468117cd52b41b6b9321cc522 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 18 Feb 2024 11:10:05 +0100 Subject: refactor(api): use an arena for user commands --- src/nvim/api/command.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index a21c9e70a7..f2d5342a5f 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -1166,10 +1166,10 @@ err: /// @param[out] err Error details, if any. /// /// @returns Map of maps describing commands. -Dictionary nvim_get_commands(Dict(get_commands) *opts, Error *err) +Dictionary nvim_get_commands(Dict(get_commands) *opts, Arena *arena, Error *err) FUNC_API_SINCE(4) { - return nvim_buf_get_commands(-1, opts, err); + return nvim_buf_get_commands(-1, opts, arena, err); } /// Gets a map of buffer-local |user-commands|. @@ -1179,7 +1179,7 @@ Dictionary nvim_get_commands(Dict(get_commands) *opts, Error *err) /// @param[out] err Error details, if any. /// /// @returns Map of maps describing commands. -Dictionary nvim_buf_get_commands(Buffer buffer, Dict(get_commands) *opts, Error *err) +Dictionary nvim_buf_get_commands(Buffer buffer, Dict(get_commands) *opts, Arena *arena, Error *err) FUNC_API_SINCE(4) { bool global = (buffer == -1); @@ -1192,12 +1192,12 @@ Dictionary nvim_buf_get_commands(Buffer buffer, Dict(get_commands) *opts, Error api_set_error(err, kErrorTypeValidation, "builtin=true not implemented"); return (Dictionary)ARRAY_DICT_INIT; } - return commands_array(NULL); + return commands_array(NULL, arena); } buf_T *buf = find_buffer_by_handle(buffer, err); if (opts->builtin || !buf) { return (Dictionary)ARRAY_DICT_INIT; } - return commands_array(buf); + return commands_array(buf, arena); } -- cgit From abb8dcd8898d0a30db6b503682e4cea337c942a6 Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 19 Feb 2024 12:09:07 +0100 Subject: refactor(api): use arena for runtime and client info --- src/nvim/api/vim.c | 54 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 21 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index d161dca050..0da7cec4ab 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -543,17 +543,22 @@ Integer nvim_strwidth(String text, Error *err) /// Gets the paths contained in |runtime-search-path|. /// /// @return List of paths -ArrayOf(String) nvim_list_runtime_paths(Error *err) +ArrayOf(String) nvim_list_runtime_paths(Arena *arena, Error *err) FUNC_API_SINCE(1) { - return nvim_get_runtime_file(NULL_STRING, true, err); + return nvim_get_runtime_file(NULL_STRING, true, arena, err); } -Array nvim__runtime_inspect(void) +Array nvim__runtime_inspect(Arena *arena) { - return runtime_inspect(); + return runtime_inspect(arena); } +typedef struct { + ArrayBuilder rv; + Arena *arena; +} RuntimeCookie; + /// Find files in runtime directories /// /// "name" can contain wildcards. For example @@ -566,25 +571,27 @@ Array nvim__runtime_inspect(void) /// @param name pattern of files to search for /// @param all whether to return all matches or only the first /// @return list of absolute paths to the found files -ArrayOf(String) nvim_get_runtime_file(String name, Boolean all, Error *err) +ArrayOf(String) nvim_get_runtime_file(String name, Boolean all, Arena *arena, Error *err) FUNC_API_SINCE(7) FUNC_API_FAST { - Array rv = ARRAY_DICT_INIT; + RuntimeCookie cookie = { .rv = ARRAY_DICT_INIT, .arena = arena, }; + kvi_init(cookie.rv); int flags = DIP_DIRFILE | (all ? DIP_ALL : 0); TRY_WRAP(err, { - do_in_runtimepath((name.size ? name.data : ""), flags, find_runtime_cb, &rv); + do_in_runtimepath((name.size ? name.data : ""), flags, find_runtime_cb, &cookie); }); - return rv; + return arena_take_arraybuilder(arena, &cookie.rv); } -static bool find_runtime_cb(int num_fnames, char **fnames, bool all, void *cookie) +static bool find_runtime_cb(int num_fnames, char **fnames, bool all, void *c) { - Array *rv = (Array *)cookie; + RuntimeCookie *cookie = (RuntimeCookie *)c; for (int i = 0; i < num_fnames; i++) { - ADD(*rv, CSTR_TO_OBJ(fnames[i])); + // TODO(bfredl): consider memory management of gen_expand_wildcards() itself + kvi_push(cookie->rv, CSTR_TO_ARENA_OBJ(cookie->arena, fnames[i])); if (!all) { return true; } @@ -1583,13 +1590,12 @@ Array nvim_get_api_info(uint64_t channel_id, Arena *arena) /// /// @param[out] err Error details, if any void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version, String type, - Dictionary methods, Dictionary attributes, Error *err) + Dictionary methods, Dictionary attributes, Arena *arena, Error *err) FUNC_API_SINCE(4) FUNC_API_REMOTE_ONLY { - Dictionary info = ARRAY_DICT_INIT; - PUT(info, "name", copy_object(STRING_OBJ(name), NULL)); + MAXSIZE_TEMP_DICT(info, 5); + PUT_C(info, "name", STRING_OBJ(name)); - version = copy_dictionary(version, NULL); bool has_major = false; for (size_t i = 0; i < version.size; i++) { if (strequal(version.items[i].key.data, "major")) { @@ -1598,15 +1604,21 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version, } } if (!has_major) { - PUT(version, "major", INTEGER_OBJ(0)); + Dictionary v = arena_dict(arena, version.size + 1); + if (version.size) { + memcpy(v.items, version.items, version.size * sizeof(v.items[0])); + v.size = version.size; + } + PUT_C(v, "major", INTEGER_OBJ(0)); + version = v; } - PUT(info, "version", DICTIONARY_OBJ(version)); + PUT_C(info, "version", DICTIONARY_OBJ(version)); - PUT(info, "type", copy_object(STRING_OBJ(type), NULL)); - PUT(info, "methods", DICTIONARY_OBJ(copy_dictionary(methods, NULL))); - PUT(info, "attributes", DICTIONARY_OBJ(copy_dictionary(attributes, NULL))); + PUT_C(info, "type", STRING_OBJ(type)); + PUT_C(info, "methods", DICTIONARY_OBJ(methods)); + PUT_C(info, "attributes", DICTIONARY_OBJ(attributes)); - rpc_set_client_info(channel_id, info); + rpc_set_client_info(channel_id, copy_dictionary(info, NULL)); } /// Gets information about a channel. -- cgit From d14eefaee9519dae76bfaf4dd42e3bd7a05c7a1e Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 19 Feb 2024 10:44:57 +0100 Subject: refactor(api): use arena when listing objects --- src/nvim/api/tabpage.c | 10 +++++----- src/nvim/api/vim.c | 33 +++++++++++++++------------------ 2 files changed, 20 insertions(+), 23 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c index 109075df8e..040abb1e3f 100644 --- a/src/nvim/api/tabpage.c +++ b/src/nvim/api/tabpage.c @@ -19,7 +19,7 @@ /// @param tabpage Tabpage handle, or 0 for current tabpage /// @param[out] err Error details, if any /// @return List of windows in `tabpage` -ArrayOf(Window) nvim_tabpage_list_wins(Tabpage tabpage, Error *err) +ArrayOf(Window) nvim_tabpage_list_wins(Tabpage tabpage, Arena *arena, Error *err) FUNC_API_SINCE(1) { Array rv = ARRAY_DICT_INIT; @@ -29,15 +29,15 @@ ArrayOf(Window) nvim_tabpage_list_wins(Tabpage tabpage, Error *err) return rv; } + size_t n = 0; FOR_ALL_WINDOWS_IN_TAB(wp, tab) { - rv.size++; + n++; } - rv.items = xmalloc(sizeof(Object) * rv.size); - size_t i = 0; + rv = arena_array(arena, n); FOR_ALL_WINDOWS_IN_TAB(wp, tab) { - rv.items[i++] = WINDOW_OBJ(wp->handle); + ADD_C(rv, WINDOW_OBJ(wp->handle)); } return rv; diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 0da7cec4ab..073541d687 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -832,20 +832,19 @@ void nvim_err_writeln(String str) /// Use |nvim_buf_is_loaded()| to check if a buffer is loaded. /// /// @return List of buffer handles -ArrayOf(Buffer) nvim_list_bufs(void) +ArrayOf(Buffer) nvim_list_bufs(Arena *arena) FUNC_API_SINCE(1) { - Array rv = ARRAY_DICT_INIT; + size_t n = 0; FOR_ALL_BUFFERS(b) { - rv.size++; + n++; } - rv.items = xmalloc(sizeof(Object) * rv.size); - size_t i = 0; + Array rv = arena_array(arena, n); FOR_ALL_BUFFERS(b) { - rv.items[i++] = BUFFER_OBJ(b->handle); + ADD_C(rv, BUFFER_OBJ(b->handle)); } return rv; @@ -887,20 +886,19 @@ void nvim_set_current_buf(Buffer buffer, Error *err) /// Gets the current list of window handles. /// /// @return List of window handles -ArrayOf(Window) nvim_list_wins(void) +ArrayOf(Window) nvim_list_wins(Arena *arena) FUNC_API_SINCE(1) { - Array rv = ARRAY_DICT_INIT; + size_t n = 0; FOR_ALL_TAB_WINDOWS(tp, wp) { - rv.size++; + n++; } - rv.items = xmalloc(sizeof(Object) * rv.size); - size_t i = 0; + Array rv = arena_array(arena, n); FOR_ALL_TAB_WINDOWS(tp, wp) { - rv.items[i++] = WINDOW_OBJ(wp->handle); + ADD_C(rv, WINDOW_OBJ(wp->handle)); } return rv; @@ -1124,20 +1122,19 @@ void nvim_chan_send(Integer chan, String data, Error *err) /// Gets the current list of tabpage handles. /// /// @return List of tabpage handles -ArrayOf(Tabpage) nvim_list_tabpages(void) +ArrayOf(Tabpage) nvim_list_tabpages(Arena *arena) FUNC_API_SINCE(1) { - Array rv = ARRAY_DICT_INIT; + size_t n = 0; FOR_ALL_TABS(tp) { - rv.size++; + n++; } - rv.items = xmalloc(sizeof(Object) * rv.size); - size_t i = 0; + Array rv = arena_array(arena, n); FOR_ALL_TABS(tp) { - rv.items[i++] = TABPAGE_OBJ(tp->handle); + ADD_C(rv, TABPAGE_OBJ(tp->handle)); } return rv; -- cgit From 404707c7606389ccb6c6062bfe9e2ff30a2552af Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 19 Feb 2024 12:07:16 +0100 Subject: refactor(api): use arena for nvim_put and nvim_paste --- src/nvim/api/private/helpers.c | 16 +++++++--------- src/nvim/api/vim.c | 23 ++++++++++------------- 2 files changed, 17 insertions(+), 22 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 63c5689109..14f4046a56 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -456,9 +456,10 @@ String ga_take_string(garray_T *ga) /// @param input Binary string /// @param crlf Also break lines at CR and CRLF. /// @return [allocated] String array -Array string_to_array(const String input, bool crlf) +Array string_to_array(const String input, bool crlf, Arena *arena) { - Array ret = ARRAY_DICT_INIT; + ArrayBuilder ret = ARRAY_DICT_INIT; + kvi_init(ret); for (size_t i = 0; i < input.size; i++) { const char *start = input.data + i; const char *end = start; @@ -473,20 +474,17 @@ Array string_to_array(const String input, bool crlf) if (crlf && *end == CAR && i + 1 < input.size && *(end + 1) == NL) { i += 1; // Advance past CRLF. } - String s = { - .size = line_len, - .data = xmemdupz(start, line_len), - }; + String s = CBUF_TO_ARENA_STR(arena, start, line_len); memchrsub(s.data, NUL, NL, line_len); - ADD(ret, STRING_OBJ(s)); + kvi_push(ret, STRING_OBJ(s)); // If line ends at end-of-buffer, add empty final item. // This is "readfile()-style", see also ":help channel-lines". if (i + 1 == input.size && (*end == NL || (crlf && *end == CAR))) { - ADD(ret, STRING_OBJ(STRING_INIT)); + kvi_push(ret, STRING_OBJ(STRING_INIT)); } } - return ret; + return arena_take_arraybuilder(arena, &ret); } /// Normalizes 0-based indexes to buffer line numbers. diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 073541d687..e472f5d160 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1205,14 +1205,13 @@ Boolean nvim_paste(String data, Boolean crlf, Integer phase, Arena *arena, Error VALIDATE_INT((phase >= -1 && phase <= 3), "phase", phase, { return false; }); - Array lines = ARRAY_DICT_INIT; if (phase == -1 || phase == 1) { // Start of paste-stream. draining = false; } else if (draining) { // Skip remaining chunks. Report error only once per "stream". goto theend; } - lines = string_to_array(data, crlf); + Array lines = string_to_array(data, crlf, arena); MAXSIZE_TEMP_ARRAY(args, 2); ADD_C(args, ARRAY_OBJ(lines)); ADD_C(args, INTEGER_OBJ(phase)); @@ -1243,7 +1242,6 @@ Boolean nvim_paste(String data, Boolean crlf, Integer phase, Arena *arena, Error AppendCharToRedobuff(ESC); // Dot-repeat. } theend: - api_free_array(lines); if (cancel || phase == -1 || phase == 3) { // End of paste-stream. draining = false; } @@ -1264,24 +1262,27 @@ theend: /// @param after If true insert after cursor (like |p|), or before (like |P|). /// @param follow If true place cursor at end of inserted text. /// @param[out] err Error details, if any -void nvim_put(ArrayOf(String) lines, String type, Boolean after, Boolean follow, Error *err) +void nvim_put(ArrayOf(String) lines, String type, Boolean after, Boolean follow, Arena *arena, + Error *err) FUNC_API_SINCE(6) FUNC_API_TEXTLOCK_ALLOW_CMDWIN { - yankreg_T *reg = xcalloc(1, sizeof(yankreg_T)); + yankreg_T reg[1] = { 0 }; VALIDATE_S((prepare_yankreg_from_object(reg, type, lines.size)), "type", type.data, { - goto cleanup; + return; }); if (lines.size == 0) { - goto cleanup; // Nothing to do. + return; // Nothing to do. } + reg->y_array = arena_alloc(arena, lines.size * sizeof(uint8_t *), true); + reg->y_size = lines.size; for (size_t i = 0; i < lines.size; i++) { VALIDATE_T("line", kObjectTypeString, lines.items[i].type, { - goto cleanup; + return; }); String line = lines.items[i].data.string; - reg->y_array[i] = xmemdupz(line.data, line.size); + reg->y_array[i] = arena_memdupz(arena, line.data, line.size); memchrsub(reg->y_array[i], NUL, NL, line.size); } @@ -1294,10 +1295,6 @@ void nvim_put(ArrayOf(String) lines, String type, Boolean after, Boolean follow, msg_silent--; VIsual_active = VIsual_was_active; }); - -cleanup: - free_register(reg); - xfree(reg); } /// Subscribes to event broadcasts. -- cgit From f2c73e9ee2bd094f21f55dc97c5ad8d2f3a51621 Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 19 Feb 2024 12:00:26 +0100 Subject: refactor(api): reduce temporary allocations when replacing lines The way ml_replace_buf is implemented makes it unfriendly for being used in a loop: every call allocates a scratch buffer for putting the line into the "dirty" state. This then immediately needs to be freed as the next ml_replace_buf and/or ml_append_buf call will flush that buffer. It's better to later pay the price of allocating the scratch buffer only if the line is being immediately edited (likely when using the API to only change one line) with an extra memcpy, than allocating that buffer multiple times every time the API is called. Of course, a separate xmalloc/xfree cycle for each time the dirty line changes is unwanted to begin with. But fixing that is a later refactor. --- src/nvim/api/buffer.c | 67 +++++++++++++++-------------------------------- src/nvim/api/deprecated.c | 17 ++++++------ src/nvim/api/vim.c | 8 +++--- 3 files changed, 34 insertions(+), 58 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index ddca85945a..ad25451054 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -325,7 +325,8 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id, /// @param replacement Array of lines to use as replacement /// @param[out] err Error details, if any void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integer end, - Boolean strict_indexing, ArrayOf(String) replacement, Error *err) + Boolean strict_indexing, ArrayOf(String) replacement, Arena *arena, + Error *err) FUNC_API_SINCE(1) FUNC_API_TEXTLOCK_ALLOW_CMDWIN { @@ -360,14 +361,14 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ size_t new_len = replacement.size; size_t old_len = (size_t)(end - start); ptrdiff_t extra = 0; // lines added to text, can be negative - char **lines = (new_len != 0) ? xcalloc(new_len, sizeof(char *)) : NULL; + char **lines = (new_len != 0) ? arena_alloc(arena, new_len * sizeof(char *), true) : NULL; for (size_t i = 0; i < new_len; i++) { const String l = replacement.items[i].data.string; // Fill lines[i] with l's contents. Convert NULs to newlines as required by // NL-used-for-NUL. - lines[i] = xmemdupz(l.data, l.size); + lines[i] = arena_memdupz(arena, l.data, l.size); memchrsub(lines[i], NUL, NL, l.size); } @@ -412,15 +413,12 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ goto end; }); - if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false) == FAIL) { + if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false, true) == FAIL) { api_set_error(err, kErrorTypeException, "Failed to replace line"); goto end; } inserted_bytes += (bcount_t)strlen(lines[i]) + 1; - // Mark lines that haven't been passed to the buffer as they need - // to be freed later - lines[i] = NULL; } // Now we may need to insert the remaining new old_len @@ -438,9 +436,6 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ inserted_bytes += (bcount_t)strlen(lines[i]) + 1; - // Same as with replacing, but we also need to free lines - xfree(lines[i]); - lines[i] = NULL; extra++; } @@ -463,11 +458,6 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ } end: - for (size_t i = 0; i < new_len; i++) { - xfree(lines[i]); - } - - xfree(lines); try_end(err); } @@ -500,7 +490,8 @@ end: /// @param replacement Array of lines to use as replacement /// @param[out] err Error details, if any void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, Integer start_col, - Integer end_row, Integer end_col, ArrayOf(String) replacement, Error *err) + Integer end_row, Integer end_col, ArrayOf(String) replacement, Arena *arena, + Error *err) FUNC_API_SINCE(7) FUNC_API_TEXTLOCK_ALLOW_CMDWIN { @@ -535,33 +526,31 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In return; }); - char *str_at_start = NULL; - char *str_at_end = NULL; - - // Another call to ml_get_buf() may free the line, so make a copy. - str_at_start = xstrdup(ml_get_buf(buf, (linenr_T)start_row)); + // Another call to ml_get_buf() may free the lines, so we make copies + char *str_at_start = ml_get_buf(buf, (linenr_T)start_row); size_t len_at_start = strlen(str_at_start); + str_at_start = arena_memdupz(arena, str_at_start, len_at_start); start_col = start_col < 0 ? (int64_t)len_at_start + start_col + 1 : start_col; VALIDATE_RANGE((start_col >= 0 && (size_t)start_col <= len_at_start), "start_col", { - goto early_end; + return; }); - // Another call to ml_get_buf() may free the line, so make a copy. - str_at_end = xstrdup(ml_get_buf(buf, (linenr_T)end_row)); + char *str_at_end = ml_get_buf(buf, (linenr_T)end_row); size_t len_at_end = strlen(str_at_end); + str_at_end = arena_memdupz(arena, str_at_end, len_at_end); end_col = end_col < 0 ? (int64_t)len_at_end + end_col + 1 : end_col; VALIDATE_RANGE((end_col >= 0 && (size_t)end_col <= len_at_end), "end_col", { - goto early_end; + return; }); VALIDATE((start_row <= end_row && !(end_row == start_row && start_col > end_col)), "%s", "'start' is higher than 'end'", { - goto early_end; + return; }); bool disallow_nl = (channel_id != VIML_INTERNAL_CALL); if (!check_string_array(replacement, "replacement string", disallow_nl, err)) { - goto early_end; + return; } size_t new_len = replacement.size; @@ -591,7 +580,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In if (replacement.size == 1) { firstlen += last_part_len; } - char *first = xmallocz(firstlen); + char *first = arena_allocz(arena, firstlen); char *last = NULL; memcpy(first, str_at_start, (size_t)start_col); memcpy(first + start_col, first_item.data, first_item.size); @@ -599,13 +588,13 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In if (replacement.size == 1) { memcpy(first + start_col + first_item.size, str_at_end + end_col, last_part_len); } else { - last = xmallocz(last_item.size + last_part_len); + last = arena_allocz(arena, last_item.size + last_part_len); memcpy(last, last_item.data, last_item.size); memchrsub(last, NUL, NL, last_item.size); memcpy(last + last_item.size, str_at_end + end_col, last_part_len); } - char **lines = xcalloc(new_len, sizeof(char *)); + char **lines = arena_alloc(arena, new_len * sizeof(char *), true); lines[0] = first; new_byte += (bcount_t)(first_item.size); for (size_t i = 1; i < new_len - 1; i++) { @@ -613,7 +602,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In // Fill lines[i] with l's contents. Convert NULs to newlines as required by // NL-used-for-NUL. - lines[i] = xmemdupz(l.data, l.size); + lines[i] = arena_memdupz(arena, l.data, l.size); memchrsub(lines[i], NUL, NL, l.size); new_byte += (bcount_t)(l.size) + 1; } @@ -665,13 +654,10 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In goto end; }); - if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false) == FAIL) { + if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false, true) == FAIL) { api_set_error(err, kErrorTypeException, "Failed to replace line"); goto end; } - // Mark lines that haven't been passed to the buffer as they need - // to be freed later - lines[i] = NULL; } // Now we may need to insert the remaining new old_len @@ -687,9 +673,6 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In goto end; } - // Same as with replacing, but we also need to free lines - xfree(lines[i]); - lines[i] = NULL; extra++; } @@ -722,15 +705,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In } end: - for (size_t i = 0; i < new_len; i++) { - xfree(lines[i]); - } - xfree(lines); try_end(err); - -early_end: - xfree(str_at_start); - xfree(str_at_end); } /// Gets a range from the buffer. diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 9b8cacd7c2..f805c945f3 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -225,12 +225,12 @@ Dictionary nvim_get_hl_by_name(String name, Boolean rgb, Arena *arena, Error *er /// the end of the buffer. /// @param lines Array of lines /// @param[out] err Error details, if any -void buffer_insert(Buffer buffer, Integer lnum, ArrayOf(String) lines, Error *err) +void buffer_insert(Buffer buffer, Integer lnum, ArrayOf(String) lines, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { // "lnum" will be the index of the line after inserting, // no matter if it is negative or not - nvim_buf_set_lines(0, buffer, lnum, lnum, true, lines, err); + nvim_buf_set_lines(0, buffer, lnum, lnum, true, lines, arena, err); } /// Gets a buffer line @@ -272,13 +272,13 @@ String buffer_get_line(Buffer buffer, Integer index, Arena *arena, Error *err) /// @param index Line index /// @param line Contents of the new line /// @param[out] err Error details, if any -void buffer_set_line(Buffer buffer, Integer index, String line, Error *err) +void buffer_set_line(Buffer buffer, Integer index, String line, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { Object l = STRING_OBJ(line); Array array = { .items = &l, .size = 1 }; index = convert_index(index); - nvim_buf_set_lines(0, buffer, index, index + 1, true, array, err); + nvim_buf_set_lines(0, buffer, index, index + 1, true, array, arena, err); } /// Deletes a buffer line @@ -291,12 +291,12 @@ void buffer_set_line(Buffer buffer, Integer index, String line, Error *err) /// @param buffer buffer handle /// @param index line index /// @param[out] err Error details, if any -void buffer_del_line(Buffer buffer, Integer index, Error *err) +void buffer_del_line(Buffer buffer, Integer index, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { Array array = ARRAY_DICT_INIT; index = convert_index(index); - nvim_buf_set_lines(0, buffer, index, index + 1, true, array, err); + nvim_buf_set_lines(0, buffer, index, index + 1, true, array, arena, err); } /// Retrieves a line range from the buffer @@ -342,12 +342,13 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer, // array will delete the line range) /// @param[out] err Error details, if any void buffer_set_line_slice(Buffer buffer, Integer start, Integer end, Boolean include_start, - Boolean include_end, ArrayOf(String) replacement, Error *err) + Boolean include_end, ArrayOf(String) replacement, Arena *arena, + Error *err) FUNC_API_DEPRECATED_SINCE(1) { start = convert_index(start) + !include_start; end = convert_index(end) + include_end; - nvim_buf_set_lines(0, buffer, start, end, false, replacement, err); + nvim_buf_set_lines(0, buffer, start, end, false, replacement, arena, err); } /// Sets a buffer-scoped (b:) variable diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index e472f5d160..c6043a2871 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -675,21 +675,21 @@ String nvim_get_current_line(Arena *arena, Error *err) /// /// @param line Line contents /// @param[out] err Error details, if any -void nvim_set_current_line(String line, Error *err) +void nvim_set_current_line(String line, Arena *arena, Error *err) FUNC_API_SINCE(1) FUNC_API_TEXTLOCK_ALLOW_CMDWIN { - buffer_set_line(curbuf->handle, curwin->w_cursor.lnum - 1, line, err); + buffer_set_line(curbuf->handle, curwin->w_cursor.lnum - 1, line, arena, err); } /// Deletes the current line. /// /// @param[out] err Error details, if any -void nvim_del_current_line(Error *err) +void nvim_del_current_line(Arena *arena, Error *err) FUNC_API_SINCE(1) FUNC_API_TEXTLOCK_ALLOW_CMDWIN { - buffer_del_line(curbuf->handle, curwin->w_cursor.lnum - 1, err); + buffer_del_line(curbuf->handle, curwin->w_cursor.lnum - 1, arena, err); } /// Gets a global (g:) variable. -- cgit From acd9b54f08453bdaa324e95232e166545c04160f Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 19 Feb 2024 14:54:20 +0100 Subject: refactor(api): use an arena for nvim_parse_expression --- src/nvim/api/vimscript.c | 237 ++++++++++++----------------------------------- 1 file changed, 60 insertions(+), 177 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index 949356acc6..db32eac3d1 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -429,7 +429,8 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; /// - "svalue": String, value for "SingleQuotedString" and /// "DoubleQuotedString" nodes. /// @param[out] err Error details, if any -Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, Error *err) +Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, Arena *arena, + Error *err) FUNC_API_SINCE(4) FUNC_API_FAST { int pflags = 0; @@ -471,82 +472,40 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E + (size_t)(east.err.msg != NULL) // "error" + (size_t)highlight // "highlight" + 0); - Dictionary ret = { - .items = xmalloc(ret_size * sizeof(ret.items[0])), - .size = 0, - .capacity = ret_size, - }; - ret.items[ret.size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("ast"), - .value = NIL, - }; - ret.items[ret.size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("len"), - .value = INTEGER_OBJ((Integer)(pstate.pos.line == 1 - ? parser_lines[0].size - : pstate.pos.col)), - }; + + Dictionary ret = arena_dict(arena, ret_size); + PUT_C(ret, "len", INTEGER_OBJ((Integer)(pstate.pos.line == 1 + ? parser_lines[0].size + : pstate.pos.col))); if (east.err.msg != NULL) { - Dictionary err_dict = { - .items = xmalloc(2 * sizeof(err_dict.items[0])), - .size = 2, - .capacity = 2, - }; - err_dict.items[0] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("message"), - .value = CSTR_TO_OBJ(east.err.msg), - }; - if (east.err.arg == NULL) { - err_dict.items[1] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("arg"), - .value = STRING_OBJ(STRING_INIT), - }; - } else { - err_dict.items[1] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("arg"), - .value = STRING_OBJ(((String) { - .data = xmemdupz(east.err.arg, (size_t)east.err.arg_len), - .size = (size_t)east.err.arg_len, - })), - }; - } - ret.items[ret.size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("error"), - .value = DICTIONARY_OBJ(err_dict), - }; + Dictionary err_dict = arena_dict(arena, 2); + PUT_C(err_dict, "message", CSTR_TO_ARENA_OBJ(arena, east.err.msg)); + PUT_C(err_dict, "arg", CBUF_TO_ARENA_OBJ(arena, east.err.arg, (size_t)east.err.arg_len)); + PUT_C(ret, "error", DICTIONARY_OBJ(err_dict)); } if (highlight) { - Array hl = (Array) { - .items = xmalloc(kv_size(colors) * sizeof(hl.items[0])), - .capacity = kv_size(colors), - .size = kv_size(colors), - }; + Array hl = arena_array(arena, kv_size(colors)); for (size_t i = 0; i < kv_size(colors); i++) { const ParserHighlightChunk chunk = kv_A(colors, i); - Array chunk_arr = (Array) { - .items = xmalloc(4 * sizeof(chunk_arr.items[0])), - .capacity = 4, - .size = 4, - }; - chunk_arr.items[0] = INTEGER_OBJ((Integer)chunk.start.line); - chunk_arr.items[1] = INTEGER_OBJ((Integer)chunk.start.col); - chunk_arr.items[2] = INTEGER_OBJ((Integer)chunk.end_col); - chunk_arr.items[3] = CSTR_TO_OBJ(chunk.group); - hl.items[i] = ARRAY_OBJ(chunk_arr); + Array chunk_arr = arena_array(arena, 4); + ADD_C(chunk_arr, INTEGER_OBJ((Integer)chunk.start.line)); + ADD_C(chunk_arr, INTEGER_OBJ((Integer)chunk.start.col)); + ADD_C(chunk_arr, INTEGER_OBJ((Integer)chunk.end_col)); + ADD_C(chunk_arr, CSTR_AS_OBJ(chunk.group)); + + ADD_C(hl, ARRAY_OBJ(chunk_arr)); } - ret.items[ret.size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("highlight"), - .value = ARRAY_OBJ(hl), - }; + PUT_C(ret, "highlight", ARRAY_OBJ(hl)); } kvi_destroy(colors); // Walk over the AST, freeing nodes in process. ExprASTConvStack ast_conv_stack; kvi_init(ast_conv_stack); + Object ast = NIL; kvi_push(ast_conv_stack, ((ExprASTConvStackItem) { .node_p = &east.root, - .ret_node_p = &ret.items[0].value, + .ret_node_p = &ast, })); while (kv_size(ast_conv_stack)) { ExprASTConvStackItem cur_item = kv_last(ast_conv_stack); @@ -573,28 +532,17 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E || node->type == kExprNodeSingleQuotedString) // "svalue" + (node->type == kExprNodeAssignment) // "augmentation" + 0); - Dictionary ret_node = { - .items = xmalloc(items_size * sizeof(ret_node.items[0])), - .capacity = items_size, - .size = 0, - }; + Dictionary ret_node = arena_dict(arena, items_size); *cur_item.ret_node_p = DICTIONARY_OBJ(ret_node); } Dictionary *ret_node = &cur_item.ret_node_p->data.dictionary; if (node->children != NULL) { const size_t num_children = 1 + (node->children->next != NULL); - Array children_array = { - .items = xmalloc(num_children * sizeof(children_array.items[0])), - .capacity = num_children, - .size = num_children, - }; + Array children_array = arena_array(arena, num_children); for (size_t i = 0; i < num_children; i++) { - children_array.items[i] = NIL; + ADD_C(children_array, NIL); } - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("children"), - .value = ARRAY_OBJ(children_array), - }; + PUT_C(*ret_node, "children", ARRAY_OBJ(children_array)); kvi_push(ast_conv_stack, ((ExprASTConvStackItem) { .node_p = &node->children, .ret_node_p = &children_array.items[0], @@ -606,126 +554,60 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E })); } else { kv_drop(ast_conv_stack, 1); - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("type"), - .value = CSTR_TO_OBJ(east_node_type_tab[node->type]), - }; - Array start_array = { - .items = xmalloc(2 * sizeof(start_array.items[0])), - .capacity = 2, - .size = 2, - }; - start_array.items[0] = INTEGER_OBJ((Integer)node->start.line); - start_array.items[1] = INTEGER_OBJ((Integer)node->start.col); - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("start"), - .value = ARRAY_OBJ(start_array), - }; - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("len"), - .value = INTEGER_OBJ((Integer)node->len), - }; + PUT_C(*ret_node, "type", CSTR_AS_OBJ(east_node_type_tab[node->type])); + Array start_array = arena_array(arena, 2); + ADD_C(start_array, INTEGER_OBJ((Integer)node->start.line)); + ADD_C(start_array, INTEGER_OBJ((Integer)node->start.col)); + PUT_C(*ret_node, "start", ARRAY_OBJ(start_array)); + + PUT_C(*ret_node, "len", INTEGER_OBJ((Integer)node->len)); switch (node->type) { case kExprNodeDoubleQuotedString: - case kExprNodeSingleQuotedString: - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("svalue"), - .value = STRING_OBJ(((String) { - .data = node->data.str.value, - .size = node->data.str.size, - })), - }; + case kExprNodeSingleQuotedString: { + Object str = CBUF_TO_ARENA_OBJ(arena, node->data.str.value, node->data.str.size); + PUT_C(*ret_node, "svalue", str); + xfree(node->data.str.value); break; + } case kExprNodeOption: - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("scope"), - .value = INTEGER_OBJ(node->data.opt.scope), - }; - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("ident"), - .value = STRING_OBJ(((String) { - .data = xmemdupz(node->data.opt.ident, - node->data.opt.ident_len), - .size = node->data.opt.ident_len, - })), - }; + PUT_C(*ret_node, "scope", INTEGER_OBJ(node->data.opt.scope)); + PUT_C(*ret_node, "ident", CBUF_TO_ARENA_OBJ(arena, node->data.opt.ident, + node->data.opt.ident_len)); break; case kExprNodePlainIdentifier: - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("scope"), - .value = INTEGER_OBJ(node->data.var.scope), - }; - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("ident"), - .value = STRING_OBJ(((String) { - .data = xmemdupz(node->data.var.ident, - node->data.var.ident_len), - .size = node->data.var.ident_len, - })), - }; + PUT_C(*ret_node, "scope", INTEGER_OBJ(node->data.var.scope)); + PUT_C(*ret_node, "ident", CBUF_TO_ARENA_OBJ(arena, node->data.var.ident, + node->data.var.ident_len)); break; case kExprNodePlainKey: - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("ident"), - .value = STRING_OBJ(((String) { - .data = xmemdupz(node->data.var.ident, - node->data.var.ident_len), - .size = node->data.var.ident_len, - })), - }; + PUT_C(*ret_node, "ident", CBUF_TO_ARENA_OBJ(arena, node->data.var.ident, + node->data.var.ident_len)); break; case kExprNodeEnvironment: - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("ident"), - .value = STRING_OBJ(((String) { - .data = xmemdupz(node->data.env.ident, - node->data.env.ident_len), - .size = node->data.env.ident_len, - })), - }; + PUT_C(*ret_node, "ident", CBUF_TO_ARENA_OBJ(arena, node->data.env.ident, + node->data.env.ident_len)); break; case kExprNodeRegister: - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("name"), - .value = INTEGER_OBJ(node->data.reg.name), - }; + PUT_C(*ret_node, "name", INTEGER_OBJ(node->data.reg.name)); break; case kExprNodeComparison: - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("cmp_type"), - .value = CSTR_TO_OBJ(eltkn_cmp_type_tab[node->data.cmp.type]), - }; - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("ccs_strategy"), - .value = CSTR_TO_OBJ(ccs_tab[node->data.cmp.ccs]), - }; - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("invert"), - .value = BOOLEAN_OBJ(node->data.cmp.inv), - }; + PUT_C(*ret_node, "cmp_type", CSTR_AS_OBJ(eltkn_cmp_type_tab[node->data.cmp.type])); + PUT_C(*ret_node, "ccs_strategy", CSTR_AS_OBJ(ccs_tab[node->data.cmp.ccs])); + PUT_C(*ret_node, "invert", BOOLEAN_OBJ(node->data.cmp.inv)); break; case kExprNodeFloat: - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("fvalue"), - .value = FLOAT_OBJ(node->data.flt.value), - }; + PUT_C(*ret_node, "fvalue", FLOAT_OBJ(node->data.flt.value)); break; case kExprNodeInteger: - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("ivalue"), - .value = INTEGER_OBJ((Integer)(node->data.num.value > API_INTEGER_MAX - ? API_INTEGER_MAX - : (Integer)node->data.num.value)), - }; + PUT_C(*ret_node, "ivalue", INTEGER_OBJ((Integer)(node->data.num.value > API_INTEGER_MAX + ? API_INTEGER_MAX + : (Integer)node->data.num.value))); break; case kExprNodeAssignment: { const ExprAssignmentType asgn_type = node->data.ass.type; - ret_node->items[ret_node->size++] = (KeyValuePair) { - .key = STATIC_CSTR_TO_STRING("augmentation"), - .value = STRING_OBJ(asgn_type == kExprAsgnPlain - ? (String)STRING_INIT - : cstr_to_string(expr_asgn_type_tab[asgn_type])), - }; + String str = (asgn_type == kExprAsgnPlain + ? (String)STRING_INIT : cstr_as_string(expr_asgn_type_tab[asgn_type])); + PUT_C(*ret_node, "augmentation", STRING_OBJ(str)); break; } case kExprNodeMissing: @@ -766,6 +648,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E } } kvi_destroy(ast_conv_stack); + PUT_C(ret, "ast", ast); assert(ret.size == ret.capacity); // Should be a no-op actually, leaving it in case non-nodes will need to be -- cgit From a0790558c3097f2813c56e404af30c3e2d8b8983 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 20 Feb 2024 19:53:49 +0800 Subject: fix(extmarks): priority order of inline and non-inline virt_text (#27532) --- src/nvim/api/extmark.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index f6f7d332ec..0b5f3471fb 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -452,9 +452,10 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// hidden marks, an "invalid" key is added to the "details" /// array of |nvim_buf_get_extmarks()| and family. If /// "undo_restore" is false, the extmark is deleted instead. -/// - priority: a priority value for the highlight group or sign -/// attribute. For example treesitter highlighting uses a -/// value of 100. +/// - priority: a priority value for the highlight group, sign +/// attribute or virtual text. For virtual text, item with +/// highest priority is drawn last. For example treesitter +/// highlighting uses a value of 100. /// - strict: boolean that indicates extmark should not be placed /// if the line or column value is past the end of the /// buffer or end of the line respectively. Defaults to true. -- cgit From 3cc54586be7760652e8bad88cae82ce74ef9432e Mon Sep 17 00:00:00 2001 From: bfredl Date: Tue, 20 Feb 2024 13:44:50 +0100 Subject: refactor(api): make freeing of return-value opt-in instead of opt out As only a few API functions make use of explicit freeing of the return value, make it opt-in instead. The arena is always present under the hood, so `Arena *arena` arg now doesn't mean anything other than getting access to this arena. Also it is in principle possible to return an allocated value while still using the arena as scratch space for other stuff (unlikely, but there no reason to not allow it). --- src/nvim/api/buffer.c | 2 +- src/nvim/api/deprecated.c | 14 +++++++------- src/nvim/api/extmark.c | 2 +- src/nvim/api/options.c | 2 +- src/nvim/api/private/dispatch.h | 4 ++-- src/nvim/api/private/helpers.h | 7 ------- src/nvim/api/vim.c | 17 +++++++++-------- src/nvim/api/vimscript.c | 2 +- 8 files changed, 22 insertions(+), 28 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index ad25451054..41a3743895 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -956,7 +956,7 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err) /// @param buffer Buffer handle, or 0 for current buffer /// @param[out] err Error details, if any /// @return Buffer name -String nvim_buf_get_name(Buffer buffer, Arena *arena, Error *err) +String nvim_buf_get_name(Buffer buffer, Error *err) FUNC_API_SINCE(1) { String rv = STRING_INIT; diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index f805c945f3..404e85cf91 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -32,8 +32,8 @@ /// @deprecated Use nvim_exec2() instead. /// @see nvim_exec2 String nvim_exec(uint64_t channel_id, String src, Boolean output, Error *err) - FUNC_API_SINCE(7) - FUNC_API_DEPRECATED_SINCE(11) + FUNC_API_SINCE(7) FUNC_API_DEPRECATED_SINCE(11) + FUNC_API_RET_ALLOC { Dict(exec_opts) opts = { .output = output }; return exec_impl(channel_id, src, &opts, err); @@ -42,8 +42,8 @@ String nvim_exec(uint64_t channel_id, String src, Boolean output, Error *err) /// @deprecated /// @see nvim_exec2 String nvim_command_output(uint64_t channel_id, String command, Error *err) - FUNC_API_SINCE(1) - FUNC_API_DEPRECATED_SINCE(7) + FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(7) + FUNC_API_RET_ALLOC { Dict(exec_opts) opts = { .output = true }; return exec_impl(channel_id, command, &opts, err); @@ -541,7 +541,7 @@ void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err) /// @param name Option name /// @param[out] err Error details, if any /// @return Option value (global) -Object nvim_get_option(String name, Arena *arena, Error *err) +Object nvim_get_option(String name, Error *err) FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(11) { @@ -555,7 +555,7 @@ Object nvim_get_option(String name, Arena *arena, Error *err) /// @param name Option name /// @param[out] err Error details, if any /// @return Option value -Object nvim_buf_get_option(Buffer buffer, String name, Arena *arena, Error *err) +Object nvim_buf_get_option(Buffer buffer, String name, Error *err) FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(11) { @@ -597,7 +597,7 @@ void nvim_buf_set_option(uint64_t channel_id, Buffer buffer, String name, Object /// @param name Option name /// @param[out] err Error details, if any /// @return Option value -Object nvim_win_get_option(Window window, String name, Arena *arena, Error *err) +Object nvim_win_get_option(Window window, String name, Error *err) FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(11) { diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 0b5f3471fb..fb729be23e 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -1203,7 +1203,7 @@ free_exit: } String nvim__buf_debug_extmarks(Buffer buffer, Boolean keys, Boolean dot, Error *err) - FUNC_API_SINCE(7) + FUNC_API_SINCE(7) FUNC_API_RET_ALLOC { buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index 8128fdf67b..7deadb8eb5 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -150,7 +150,7 @@ static buf_T *do_ft_buf(char *filetype, aco_save_T *aco, Error *err) /// @param[out] err Error details, if any /// @return Option value Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) - FUNC_API_SINCE(9) + FUNC_API_SINCE(9) FUNC_API_RET_ALLOC { OptIndex opt_idx = 0; int scope = 0; diff --git a/src/nvim/api/private/dispatch.h b/src/nvim/api/private/dispatch.h index 6d051176ac..288f368fba 100644 --- a/src/nvim/api/private/dispatch.h +++ b/src/nvim/api/private/dispatch.h @@ -18,8 +18,8 @@ struct MsgpackRpcRequestHandler { ///< uv loop (the loop is run very frequently due to breakcheck). ///< If "fast" is false, the function is deferred, i e the call will ///< be put in the event queue, for safe handling later. - bool arena_return; ///< return value is allocated in the arena (or statically) - ///< and should not be freed as such. + bool ret_alloc; ///< return value is allocated and should be freed using api_free_object + ///< otherwise it uses arena and/or static memory }; extern const MsgpackRpcRequestHandler method_handlers[]; diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 20bc889a0a..7eda8ffaf6 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -125,13 +125,6 @@ typedef kvec_withinit_t(Object, 16) ArrayBuilder; #define KEYDICT_INIT { 0 } -#define api_free_boolean(value) -#define api_free_integer(value) -#define api_free_float(value) -#define api_free_buffer(value) -#define api_free_window(value) -#define api_free_tabpage(value) - EXTERN PMap(int) buffer_handles INIT( = MAP_INIT); EXTERN PMap(int) window_handles INIT( = MAP_INIT); EXTERN PMap(int) tabpage_handles INIT( = MAP_INIT); diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index c6043a2871..2888656a7a 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -461,7 +461,7 @@ error: /// @see replace_termcodes /// @see cpoptions String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Boolean special) - FUNC_API_SINCE(1) + FUNC_API_SINCE(1) FUNC_API_RET_ALLOC { if (str.size == 0) { // Empty string @@ -601,6 +601,7 @@ static bool find_runtime_cb(int num_fnames, char **fnames, bool all, void *c) } String nvim__get_lib_dir(void) + FUNC_API_RET_ALLOC { return cstr_as_string(get_lib_dir()); } @@ -1726,7 +1727,7 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Arena *arena, Error *er // directly here. But `result` might become invalid when next api function // is called in the loop. ADD_C(results, copy_object(result, arena)); - if (!handler.arena_return) { + if (handler.ret_alloc) { api_free_object(result); } } @@ -1806,9 +1807,9 @@ static void write_msg(String message, bool to_err, bool writeln) /// @param[in] obj Object to return. /// /// @return its argument. -Object nvim__id(Object obj) +Object nvim__id(Object obj, Arena *arena) { - return copy_object(obj, NULL); + return copy_object(obj, arena); } /// Returns array given as argument. @@ -1819,9 +1820,9 @@ Object nvim__id(Object obj) /// @param[in] arr Array to return. /// /// @return its argument. -Array nvim__id_array(Array arr) +Array nvim__id_array(Array arr, Arena *arena) { - return copy_array(arr, NULL); + return copy_array(arr, arena); } /// Returns dictionary given as argument. @@ -1832,9 +1833,9 @@ Array nvim__id_array(Array arr) /// @param[in] dct Dictionary to return. /// /// @return its argument. -Dictionary nvim__id_dictionary(Dictionary dct) +Dictionary nvim__id_dictionary(Dictionary dct, Arena *arena) { - return copy_dictionary(dct, NULL); + return copy_dictionary(dct, arena); } /// Returns floating-point value given as argument. diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index db32eac3d1..7239971c21 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -51,7 +51,7 @@ /// @return Dictionary containing information about execution, with these keys: /// - output: (string|nil) Output if `opts.output` is true. Dictionary nvim_exec2(uint64_t channel_id, String src, Dict(exec_opts) *opts, Error *err) - FUNC_API_SINCE(11) + FUNC_API_SINCE(11) FUNC_API_RET_ALLOC { Dictionary result = ARRAY_DICT_INIT; -- cgit From 6d8bbfe19df2175637a1e47ac1aafb0e96e35b38 Mon Sep 17 00:00:00 2001 From: rktjmp Date: Thu, 22 Feb 2024 00:25:08 +1100 Subject: docs: remove mention of foreground/background/special keys in nvim_set_hl (#27558) To align the output of `nvim_get_hl` with its documentation -- which points to `nvim_set_hl`, remove mentions of the keys `foreground`, `background` and `special`. The long keys are are still supported (via fallback checks inside `dict2hlattrs`), but the `fg`, `bg` and `sp` keys are preferenced. --- src/nvim/api/vim.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 2888656a7a..b3d38cde69 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -141,9 +141,9 @@ Dictionary nvim_get_hl(Integer ns_id, Dict(get_highlight) *opts, Arena *arena, E /// |nvim_set_hl_ns()| or |nvim_win_set_hl_ns()| to activate them. /// @param name Highlight group name, e.g. "ErrorMsg" /// @param val Highlight definition map, accepts the following keys: -/// - fg (or foreground): color name or "#RRGGBB", see note. -/// - bg (or background): color name or "#RRGGBB", see note. -/// - sp (or special): color name or "#RRGGBB" +/// - fg: color name or "#RRGGBB", see note. +/// - bg: color name or "#RRGGBB", see note. +/// - sp: color name or "#RRGGBB" /// - blend: integer between 0 and 100 /// - bold: boolean /// - standout: boolean -- cgit From 1c032ad703a19cd5c8498ee95f9352df87a91139 Mon Sep 17 00:00:00 2001 From: altermo <107814000+altermo@users.noreply.github.com> Date: Tue, 6 Feb 2024 11:52:42 +0100 Subject: feat(extmark): window scoped extmark Co-authored-by: zeertzjq --- src/nvim/api/deprecated.c | 2 +- src/nvim/api/extmark.c | 85 +++++++++++++++++++++++++++++++++++++++++++-- src/nvim/api/keysets_defs.h | 1 + 3 files changed, 85 insertions(+), 3 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 404e85cf91..166b04adf0 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -169,7 +169,7 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, A DecorInline decor = { .ext = true, .data.ext.vt = vt, .data.ext.sh_idx = DECOR_ID_INVALID }; extmark_set(buf, ns_id, NULL, (int)line, 0, -1, -1, decor, 0, true, - false, false, false, NULL); + false, false, false, false, NULL); return src_id; } diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index fb729be23e..96ee37e6aa 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -20,6 +20,7 @@ #include "nvim/extmark.h" #include "nvim/grid.h" #include "nvim/highlight_group.h" +#include "nvim/map_defs.h" #include "nvim/marktree.h" #include "nvim/marktree_defs.h" #include "nvim/mbyte.h" @@ -177,6 +178,10 @@ static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_na PUT_C(dict, "invalid", BOOLEAN_OBJ(true)); } + if (mt_scoped(start)) { + PUT_C(dict, "scoped", BOOLEAN_OBJ(true)); + } + decor_to_dict_legacy(&dict, mt_decor(start), hl_name, arena); ADD_C(rv, DICTIONARY_OBJ(dict)); @@ -493,6 +498,8 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// used together with virt_text. /// - url: A URL to associate with this extmark. In the TUI, the OSC 8 control /// sequence is used to generate a clickable hyperlink to this URL. +/// - scoped: boolean that indicates that the extmark should only be +/// displayed in the namespace scope. (experimental) /// /// @param[out] err Error details, if any /// @return Id of the created/updated extmark @@ -751,6 +758,11 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } if (opts->ephemeral && decor_state.win && decor_state.win->w_buffer == buf) { + if (opts->scoped) { + api_set_error(err, kErrorTypeException, "not yet implemented"); + goto error; + } + int r = (int)line; int c = (int)col; if (line2 == -1) { @@ -843,7 +855,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer extmark_set(buf, (uint32_t)ns_id, &id, (int)line, (colnr_T)col, line2, col2, decor, decor_flags, right_gravity, opts->end_right_gravity, !GET_BOOL_OR_TRUE(opts, set_extmark, undo_restore), - opts->invalidate, err); + opts->invalidate, opts->scoped, err); if (ERROR_SET(err)) { decor_free(decor); return 0; @@ -969,7 +981,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In decor.data.hl.hl_id = hl_id; extmark_set(buf, ns, NULL, (int)line, (colnr_T)col_start, end_line, (colnr_T)col_end, - decor, MT_FLAG_DECOR_HL, true, false, false, false, NULL); + decor, MT_FLAG_DECOR_HL, true, false, false, false, false, NULL); return ns_id; } @@ -1212,3 +1224,72 @@ String nvim__buf_debug_extmarks(Buffer buffer, Boolean keys, Boolean dot, Error return mt_inspect(buf->b_marktree, keys, dot); } + +/// Adds the namespace scope to the window. +/// +/// @param window Window handle, or 0 for current window +/// @param ns_id the namespace to add +/// @return true if the namespace was added, else false +Boolean nvim_win_add_ns(Window window, Integer ns_id, Error *err) + FUNC_API_SINCE(12) +{ + win_T *win = find_window_by_handle(window, err); + if (!win) { + return false; + } + + VALIDATE_INT(ns_initialized((uint32_t)ns_id), "ns_id", ns_id, { + return false; + }); + + set_put(uint32_t, &win->w_ns_set, (uint32_t)ns_id); + + redraw_all_later(UPD_NOT_VALID); // TODO(altermo): only need to redraw the window + + return true; +} + +/// Gets all the namespaces scopes associated with a window. +/// +/// @param window Window handle, or 0 for current window +/// @return a list of namespaces ids +ArrayOf(Integer) nvim_win_get_ns(Window window, Error *err) + FUNC_API_SINCE(12) +{ + Array rv = ARRAY_DICT_INIT; + + win_T *win = find_window_by_handle(window, err); + if (!win) { + return rv; + } + uint32_t i; + set_foreach(&win->w_ns_set, i, { + ADD(rv, INTEGER_OBJ((Integer)(i))); + }); + + return rv; +} + +/// Removes the namespace scope from the window. +/// +/// @param window Window handle, or 0 for current window +/// @param ns_id the namespace to remove +/// @return true if the namespace was removed, else false +Boolean nvim_win_remove_ns(Window window, Integer ns_id, Error *err) + FUNC_API_SINCE(12) +{ + win_T *win = find_window_by_handle(window, err); + if (!win) { + return false; + } + + if (!set_has(uint32_t, &win->w_ns_set, (uint32_t)ns_id)) { + return false; + } + + set_del_uint32_t(&win->w_ns_set, (uint32_t)ns_id); + + redraw_later(win, UPD_NOT_VALID); + + return true; +} diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index c5aa5ce0f1..fe91d9760d 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -55,6 +55,7 @@ typedef struct { Boolean ui_watched; Boolean undo_restore; String url; + Boolean scoped; Integer _subpriority; } Dict(set_extmark); -- cgit From 2137edbd4f15781a3a17091d38688a8ecbc02988 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 22 Feb 2024 09:05:52 +0800 Subject: fix(extmarks): redraw properly with scoped inline virt_text (#27569) --- src/nvim/api/extmark.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 96ee37e6aa..23a42798d9 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -26,6 +26,7 @@ #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" +#include "nvim/move.h" #include "nvim/pos_defs.h" #include "nvim/sign.h" @@ -1244,7 +1245,7 @@ Boolean nvim_win_add_ns(Window window, Integer ns_id, Error *err) set_put(uint32_t, &win->w_ns_set, (uint32_t)ns_id); - redraw_all_later(UPD_NOT_VALID); // TODO(altermo): only need to redraw the window + changed_window_setting_win(win); return true; } @@ -1289,7 +1290,7 @@ Boolean nvim_win_remove_ns(Window window, Integer ns_id, Error *err) set_del_uint32_t(&win->w_ns_set, (uint32_t)ns_id); - redraw_later(win, UPD_NOT_VALID); + changed_window_setting_win(win); return true; } -- cgit From 711a2f5ff67d245ee9a1322f9554329537977989 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 22 Feb 2024 09:13:07 +0800 Subject: fix(api): don't leak memory with nvim_win_get_ns (#27570) --- src/nvim/api/extmark.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 23a42798d9..5dcda42b02 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -1254,18 +1254,18 @@ Boolean nvim_win_add_ns(Window window, Integer ns_id, Error *err) /// /// @param window Window handle, or 0 for current window /// @return a list of namespaces ids -ArrayOf(Integer) nvim_win_get_ns(Window window, Error *err) +ArrayOf(Integer) nvim_win_get_ns(Window window, Arena *arena, Error *err) FUNC_API_SINCE(12) { - Array rv = ARRAY_DICT_INIT; - win_T *win = find_window_by_handle(window, err); if (!win) { - return rv; + return (Array)ARRAY_DICT_INIT; } + + Array rv = arena_array(arena, set_size(&win->w_ns_set)); uint32_t i; set_foreach(&win->w_ns_set, i, { - ADD(rv, INTEGER_OBJ((Integer)(i))); + ADD_C(rv, INTEGER_OBJ((Integer)(i))); }); return rv; @@ -1288,7 +1288,7 @@ Boolean nvim_win_remove_ns(Window window, Integer ns_id, Error *err) return false; } - set_del_uint32_t(&win->w_ns_set, (uint32_t)ns_id); + set_del(uint32_t, &win->w_ns_set, (uint32_t)ns_id); changed_window_setting_win(win); -- cgit From c2ddef30e743d8c1de6d960bf230b8646cb49c7c Mon Sep 17 00:00:00 2001 From: Evgeni Chasnovski Date: Sun, 25 Feb 2024 02:45:34 +0200 Subject: docs: fix several misleading and superfluous wordings (#27609) --- src/nvim/api/extmark.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 5dcda42b02..653140a7ae 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -467,25 +467,15 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// buffer or end of the line respectively. Defaults to true. /// - sign_text: string of length 1-2 used to display in the /// sign column. -/// Note: ranges are unsupported and decorations are only -/// applied to start_row /// - sign_hl_group: name of the highlight group used to /// highlight the sign column text. -/// Note: ranges are unsupported and decorations are only -/// applied to start_row /// - number_hl_group: name of the highlight group used to /// highlight the number column. -/// Note: ranges are unsupported and decorations are only -/// applied to start_row /// - line_hl_group: name of the highlight group used to /// highlight the whole line. -/// Note: ranges are unsupported and decorations are only -/// applied to start_row /// - cursorline_hl_group: name of the highlight group used to -/// highlight the line when the cursor is on the same line -/// as the mark and 'cursorline' is enabled. -/// Note: ranges are unsupported and decorations are only -/// applied to start_row +/// highlight the sign column text when the cursor is on +/// the same line as the mark and 'cursorline' is enabled. /// - conceal: string which should be either empty or a single /// character. Enable concealing similar to |:syn-conceal|. /// When a character is supplied it is used as |:syn-cchar|. @@ -1058,7 +1048,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start, /// ["win", winid, bufnr, topline, botline] /// - on_line: called for each buffer line being redrawn. /// (The interaction with fold lines is subject to change) -/// ["win", winid, bufnr, row] +/// ["line", winid, bufnr, row] /// - on_end: called at the end of a redraw cycle /// ["end", tick] void nvim_set_decoration_provider(Integer ns_id, Dict(set_decoration_provider) *opts, Error *err) -- cgit From 9beb40a4db5613601fc1a4b828a44e5977eca046 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 15 Feb 2024 17:16:04 +0000 Subject: feat(docs): replace lua2dox.lua Problem: The documentation flow (`gen_vimdoc.py`) has several issues: - it's not very versatile - depends on doxygen - doesn't work well with Lua code as it requires an awkward filter script to convert it into pseudo-C. - The intermediate XML files and filters makes it too much like a rube goldberg machine. Solution: Re-implement the flow using Lua, LPEG and treesitter. - `gen_vimdoc.py` is now replaced with `gen_vimdoc.lua` and replicates a portion of the logic. - `lua2dox.lua` is gone! - No more XML files. - Doxygen is now longer used and instead we now use: - LPEG for comment parsing (see `scripts/luacats_grammar.lua` and `scripts/cdoc_grammar.lua`). - LPEG for C parsing (see `scripts/cdoc_parser.lua`) - Lua patterns for Lua parsing (see `scripts/luacats_parser.lua`). - Treesitter for Markdown parsing (see `scripts/text_utils.lua`). - The generated `runtime/doc/*.mpack` files have been removed. - `scripts/gen_eval_files.lua` now instead uses `scripts/cdoc_parser.lua` directly. - Text wrapping is implemented in `scripts/text_utils.lua` and appears to produce more consistent results (the main contributer to the diff of this change). --- src/nvim/api/autocmd.c | 12 ++++++------ src/nvim/api/buffer.c | 10 ++++++---- src/nvim/api/command.c | 2 +- src/nvim/api/deprecated.c | 8 +++----- src/nvim/api/extmark.c | 1 + src/nvim/api/options.c | 24 ++++++++++++------------ src/nvim/api/ui.c | 4 ++-- src/nvim/api/vim.c | 25 +++++++++++++++---------- src/nvim/api/vimscript.c | 6 ++---- src/nvim/api/win_config.c | 36 +++++++++++++++++++++++------------- src/nvim/api/window.c | 1 + 11 files changed, 72 insertions(+), 57 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 7e33a77d24..06f21919f9 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -75,7 +75,7 @@ static int64_t next_autocmd_id = 1; /// - buffer: Buffer number or list of buffer numbers for buffer local autocommands /// |autocmd-buflocal|. Cannot be used with {pattern} /// @return Array of autocommands matching the criteria, with each item -/// containing the following fields: +/// containing the following fields: /// - id (number): the autocommand id (only when defined with the API). /// - group (integer): the autocommand group id. /// - group_name (string): the autocommand group name. @@ -83,10 +83,10 @@ static int64_t next_autocmd_id = 1; /// - event (string): the autocommand event. /// - command (string): the autocommand command. Note: this will be empty if a callback is set. /// - callback (function|string|nil): Lua function or name of a Vim script function -/// which is executed when this autocommand is triggered. +/// which is executed when this autocommand is triggered. /// - once (boolean): whether the autocommand is only run once. /// - pattern (string): the autocommand pattern. -/// If the autocommand is buffer local |autocmd-buffer-local|: +/// If the autocommand is buffer local |autocmd-buffer-local|: /// - buflocal (boolean): true if the autocommand is buffer local. /// - buffer (number): the buffer number. Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err) @@ -536,9 +536,9 @@ void nvim_del_autocmd(Integer id, Error *err) /// @param opts Parameters /// - event: (string|table) /// Examples: -/// - event: "pat1" -/// - event: { "pat1" } -/// - event: { "pat1", "pat2", "pat3" } +/// - event: "pat1" +/// - event: { "pat1" } +/// - event: { "pat1", "pat2", "pat3" } /// - pattern: (string|table) /// - pattern or patterns to match exactly. /// - For example, if you have `*.py` as that pattern for the autocmd, diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 41a3743895..7f195de959 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -50,9 +50,8 @@ # include "api/buffer.c.generated.h" #endif -/// \defgroup api-buffer -/// -/// \brief For more information on buffers, see |buffers| +/// @brief
help
+/// For more information on buffers, see |buffers|.
 ///
 /// Unloaded Buffers: ~
 ///
@@ -64,6 +63,7 @@
 ///
 /// You can use |nvim_buf_is_loaded()| or |nvim_buf_line_count()| to check
 /// whether a buffer is loaded.
+/// 
/// Returns the number of lines in the given buffer. /// @@ -229,6 +229,7 @@ Boolean nvim_buf_detach(uint64_t channel_id, Buffer buffer, Error *err) return true; } +/// @nodoc void nvim__buf_redraw_range(Buffer buffer, Integer first, Integer last, Error *err) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -875,8 +876,8 @@ Integer nvim_buf_get_changedtick(Buffer buffer, Error *err) /// Gets a list of buffer-local |mapping| definitions. /// -/// @param mode Mode short-name ("n", "i", "v", ...) /// @param buffer Buffer handle, or 0 for current buffer +/// @param mode Mode short-name ("n", "i", "v", ...) /// @param[out] err Error details, if any /// @returns Array of |maparg()|-like dictionaries describing mappings. /// The "buffer" key holds the associated buffer handle. @@ -1223,6 +1224,7 @@ Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err) return res; } +/// @nodoc Dictionary nvim__buf_stats(Buffer buffer, Arena *arena, Error *err) { buf_T *buf = find_buffer_by_handle(buffer, err); diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index f2d5342a5f..e08035b0eb 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -67,7 +67,7 @@ /// - file: (boolean) The command expands filenames. Which means characters such as "%", /// "#" and wildcards are expanded. /// - bar: (boolean) The "|" character is treated as a command separator and the double -/// quote character (\") is treated as the start of a comment. +/// quote character (") is treated as the start of a comment. /// - mods: (dictionary) |:command-modifiers|. /// - filter: (dictionary) |:filter|. /// - pattern: (string) Filter pattern. Empty string if there is no filter. diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 166b04adf0..6254e9fbd8 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -61,8 +61,7 @@ Object nvim_execute_lua(String code, Array args, Arena *arena, Error *err) /// Gets the buffer number /// -/// @deprecated The buffer number now is equal to the object id, -/// so there is no need to use this function. +/// @deprecated The buffer number now is equal to the object id /// /// @param buffer Buffer handle, or 0 for current buffer /// @param[out] err Error details, if any @@ -100,8 +99,7 @@ void nvim_buf_clear_highlight(Buffer buffer, Integer ns_id, Integer line_start, /// Set the virtual text (annotation) for a buffer line. /// -/// @deprecated use nvim_buf_set_extmark to use full virtual text -/// functionality. +/// @deprecated use nvim_buf_set_extmark to use full virtual text functionality. /// /// The text will be placed after the buffer text. Virtual text will never /// cause reflow, rather virtual text will be truncated at the end of the screen @@ -119,7 +117,7 @@ void nvim_buf_clear_highlight(Buffer buffer, Integer ns_id, Integer line_start, /// virtual text, the allocated id is then returned. /// /// @param buffer Buffer handle, or 0 for current buffer -/// @param ns_id Namespace to use or 0 to create a namespace, +/// @param src_id Namespace to use or 0 to create a namespace, /// or -1 for a ungrouped annotation /// @param line Line to annotate with virtual text (zero-indexed) /// @param chunks A list of [text, hl_group] arrays, each representing a diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 653140a7ae..e35840915f 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -1205,6 +1205,7 @@ free_exit: return virt_text; } +/// @nodoc String nvim__buf_debug_extmarks(Buffer buffer, Boolean keys, Boolean dot, Error *err) FUNC_API_SINCE(7) FUNC_API_RET_ALLOC { diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index 7deadb8eb5..d9bc0ccc92 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -272,21 +272,21 @@ Dictionary nvim_get_all_options_info(Arena *arena, Error *err) /// Gets the option information for one option from arbitrary buffer or window /// /// Resulting dictionary has keys: -/// - name: Name of the option (like 'filetype') -/// - shortname: Shortened name of the option (like 'ft') -/// - type: type of option ("string", "number" or "boolean") -/// - default: The default value for the option -/// - was_set: Whether the option was set. +/// - name: Name of the option (like 'filetype') +/// - shortname: Shortened name of the option (like 'ft') +/// - type: type of option ("string", "number" or "boolean") +/// - default: The default value for the option +/// - was_set: Whether the option was set. /// -/// - last_set_sid: Last set script id (if any) -/// - last_set_linenr: line number where option was set -/// - last_set_chan: Channel where option was set (0 for local) +/// - last_set_sid: Last set script id (if any) +/// - last_set_linenr: line number where option was set +/// - last_set_chan: Channel where option was set (0 for local) /// -/// - scope: one of "global", "win", or "buf" -/// - global_local: whether win or buf option has a global value +/// - scope: one of "global", "win", or "buf" +/// - global_local: whether win or buf option has a global value /// -/// - commalist: List of comma separated values -/// - flaglist: List of single char flags +/// - commalist: List of comma separated values +/// - flaglist: List of single char flags /// /// When {scope} is not provided, the last set information applies to the local /// value in the current buffer or window if it is available, otherwise the diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index d5843caa96..0f016d2f29 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -165,7 +165,7 @@ void remote_ui_wait_for_attach(bool only_stdio) /// Activates UI events on the channel. /// -/// Entry point of all UI clients. Allows |\-\-embed| to continue startup. +/// Entry point of all UI clients. Allows |--embed| to continue startup. /// Implies that the client is ready to show the UI. Adds the client to the /// list of UIs. |nvim_list_uis()| /// @@ -541,7 +541,7 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Floa /// /// @param channel_id /// @param event Event name -/// @param payload Event payload +/// @param value Event payload /// @param[out] err Error details, if any. void nvim_ui_term_event(uint64_t channel_id, String event, Object value, Error *err) FUNC_API_SINCE(12) FUNC_API_REMOTE_ONLY diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index b3d38cde69..54bf290e6b 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -123,7 +123,7 @@ Dictionary nvim_get_hl(Integer ns_id, Dict(get_highlight) *opts, Arena *arena, E /// /// @note Unlike the `:highlight` command which can update a highlight group, /// this function completely replaces the definition. For example: -/// ``nvim_set_hl(0, 'Visual', {})`` will clear the highlight group +/// `nvim_set_hl(0, 'Visual', {})` will clear the highlight group /// 'Visual'. /// /// @note The fg and bg keys also accept the string values `"fg"` or `"bg"` @@ -549,6 +549,7 @@ ArrayOf(String) nvim_list_runtime_paths(Arena *arena, Error *err) return nvim_get_runtime_file(NULL_STRING, true, arena, err); } +/// @nodoc Array nvim__runtime_inspect(Arena *arena) { return runtime_inspect(arena); @@ -600,6 +601,7 @@ static bool find_runtime_cb(int num_fnames, char **fnames, bool all, void *c) return num_fnames > 0; } +/// @nodoc String nvim__get_lib_dir(void) FUNC_API_RET_ALLOC { @@ -1547,14 +1549,14 @@ Array nvim_get_api_info(uint64_t channel_id, Arena *arena) /// @param channel_id /// @param name Short name for the connected client /// @param version Dictionary describing the version, with these -/// (optional) keys: +/// (optional) keys: /// - "major" major version (defaults to 0 if not set, for no release yet) /// - "minor" minor version /// - "patch" patch number /// - "prerelease" string describing a prerelease, like "dev" or "beta1" /// - "commit" hash or similar identifier of commit /// @param type Must be one of the following values. Client libraries should -/// default to "remote" unless overridden by the user. +/// default to "remote" unless overridden by the user. /// - "remote" remote client connected "Nvim flavored" MessagePack-RPC (responses /// must be in reverse order of requests). |msgpack-rpc| /// - "msgpack-rpc" remote client connected to Nvim via fully MessagePack-RPC @@ -1565,12 +1567,12 @@ Array nvim_get_api_info(uint64_t channel_id, Arena *arena) /// - "host" plugin host, typically started by nvim /// - "plugin" single plugin, started by nvim /// @param methods Builtin methods in the client. For a host, this does not -/// include plugin methods which will be discovered later. -/// The key should be the method name, the values are dicts with -/// these (optional) keys (more keys may be added in future -/// versions of Nvim, thus unknown keys are ignored. Clients -/// must only use keys defined in this or later versions of -/// Nvim): +/// include plugin methods which will be discovered later. +/// The key should be the method name, the values are dicts with +/// these (optional) keys (more keys may be added in future +/// versions of Nvim, thus unknown keys are ignored. Clients +/// must only use keys defined in this or later versions of +/// Nvim): /// - "async" if true, send as a notification. If false or unspecified, /// use a blocking request /// - "nargs" Number of arguments. Could be a single integer or an array @@ -1979,7 +1981,7 @@ void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish, Di pum_ext_select_item((int)item, insert, finish); } -/// NB: if your UI doesn't use hlstate, this will not return hlstate first time +/// NB: if your UI doesn't use hlstate, this will not return hlstate first time. Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Arena *arena, Error *err) { Array ret = ARRAY_DICT_INIT; @@ -2015,6 +2017,7 @@ Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Arena *arena, E return ret; } +/// @nodoc void nvim__screenshot(String path) FUNC_API_FAST { @@ -2029,6 +2032,7 @@ void nvim__invalidate_glyph_cache(void) must_redraw = UPD_CLEAR; } +/// @nodoc Object nvim__unpack(String str, Arena *arena, Error *err) FUNC_API_FAST { @@ -2319,6 +2323,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena * return result; } +/// @nodoc void nvim_error_event(uint64_t channel_id, Integer lvl, String data) FUNC_API_REMOTE_ONLY { diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index 7239971c21..8f57e61c76 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -351,9 +351,7 @@ typedef struct { Object *ret_node_p; } ExprASTConvStackItem; -/// @cond DOXYGEN_NOT_A_FUNCTION typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; -/// @endcond /// Parse a Vimscript expression. /// @@ -387,8 +385,8 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; /// - "arg": String, error message argument. /// - "len": Amount of bytes successfully parsed. With flags equal to "" /// that should be equal to the length of expr string. -/// (“Successfully parsed” here means “participated in AST -/// creation”, not “till the first error”.) +/// ("Successfully parsed" here means "participated in AST +/// creation", not "till the first error".) /// - "ast": AST, either nil or a dictionary with these keys: /// - "type": node type, one of the value names from ExprASTNodeType /// stringified without "kExprNode" prefix. diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index e76db82c61..3cc520dc78 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -116,12 +116,12 @@ /// - width: Window width (in character cells). Minimum of 1. /// - height: Window height (in character cells). Minimum of 1. /// - bufpos: Places float relative to buffer text (only when -/// relative="win"). Takes a tuple of zero-indexed [line, column]. -/// `row` and `col` if given are applied relative to this -/// position, else they default to: -/// - `row=1` and `col=0` if `anchor` is "NW" or "NE" -/// - `row=0` and `col=0` if `anchor` is "SW" or "SE" -/// (thus like a tooltip near the buffer text). +/// relative="win"). Takes a tuple of zero-indexed [line, column]. +/// `row` and `col` if given are applied relative to this +/// position, else they default to: +/// - `row=1` and `col=0` if `anchor` is "NW" or "NE" +/// - `row=0` and `col=0` if `anchor` is "SW" or "SE" +/// (thus like a tooltip near the buffer text). /// - row: Row position in units of "screen cell height", may be fractional. /// - col: Column position in units of "screen cell width", may be /// fractional. @@ -153,7 +153,7 @@ /// 'fillchars' to a space char, and clearing the /// |hl-EndOfBuffer| region in 'winhighlight'. /// - border: Style of (optional) window border. This can either be a string -/// or an array. The string values are +/// or an array. The string values are /// - "none": No border (default). /// - "single": A single line box. /// - "double": A double line box. @@ -161,21 +161,31 @@ /// - "solid": Adds padding by a single whitespace cell. /// - "shadow": A drop shadow effect by blending with the background. /// - If it is an array, it should have a length of eight or any divisor of -/// eight. The array will specify the eight chars building up the border -/// in a clockwise fashion starting with the top-left corner. As an -/// example, the double box style could be specified as +/// eight. The array will specify the eight chars building up the border +/// in a clockwise fashion starting with the top-left corner. As an +/// example, the double box style could be specified as: +/// ``` /// [ "╔", "═" ,"╗", "║", "╝", "═", "╚", "║" ]. -/// If the number of chars are less than eight, they will be repeated. Thus -/// an ASCII border could be specified as +/// ``` +/// If the number of chars are less than eight, they will be repeated. Thus +/// an ASCII border could be specified as +/// ``` /// [ "/", "-", \"\\\\\", "|" ], -/// or all chars the same as +/// ``` +/// or all chars the same as +/// ``` /// [ "x" ]. +/// ``` /// An empty string can be used to turn off a specific border, for instance, +/// ``` /// [ "", "", "", ">", "", "", "", "<" ] +/// ``` /// will only make vertical borders but not horizontal ones. /// By default, `FloatBorder` highlight is used, which links to `WinSeparator` /// when not defined. It could also be specified by character: +/// ``` /// [ ["+", "MyCorner"], ["x", "MyBorder"] ]. +/// ``` /// - title: Title (optional) in window border, string or list. /// List should consist of `[text, highlight]` tuples. /// If string, the default highlight group is `FloatTitle`. diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index c41c5d4b07..ed51eedf1b 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -452,6 +452,7 @@ Object nvim_win_call(Window window, LuaRef fun, Error *err) /// /// This takes precedence over the 'winhighlight' option. /// +/// @param window /// @param ns_id the namespace to use /// @param[out] err Error details, if any void nvim_win_set_hl_ns(Window window, Integer ns_id, Error *err) -- cgit From de5cf09cf98e20d8d3296ad6933ff2741acf83f7 Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 26 Feb 2024 18:00:46 +0100 Subject: refactor(metadata): generate all metadata in lua Then we can just load metadata in C as a single msgpack blob. Which also can be used directly as binarly data, instead of first unpacking all the functions and ui_events metadata to immediately pack it again, which was a bit of a silly walk (and one extra usecase of `msgpack_rpc_from_object` which will get yak shaved in the next PR) --- src/nvim/api/private/helpers.c | 74 +++++------------------------------------- src/nvim/api/ui.h | 14 ++++++++ src/nvim/api/vim.c | 2 +- 3 files changed, 23 insertions(+), 67 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 14f4046a56..97ba15e871 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -38,9 +38,8 @@ #include "nvim/version.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "api/private/funcs_metadata.generated.h" +# include "api/private/api_metadata.generated.h" # include "api/private/helpers.c.generated.h" -# include "api/private/ui_events_metadata.generated.h" #endif /// Start block that may cause Vimscript exceptions while evaluating another code @@ -653,83 +652,26 @@ void api_clear_error(Error *value) static ArenaMem mem_for_metadata = NULL; /// @returns a shared value. caller must not modify it! -Dictionary api_metadata(void) +Object api_metadata(void) { - static Dictionary metadata = ARRAY_DICT_INIT; + static Object metadata = OBJECT_INIT; - if (!metadata.size) { + if (metadata.type == kObjectTypeNil) { Arena arena = ARENA_EMPTY; Error err = ERROR_INIT; - metadata = arena_dict(&arena, 6); - PUT_C(metadata, "version", DICTIONARY_OBJ(version_dict(&arena))); - PUT_C(metadata, "functions", - unpack((char *)funcs_metadata, sizeof(funcs_metadata), &arena, &err)); - if (ERROR_SET(&err)) { + metadata = unpack((char *)packed_api_metadata, sizeof(packed_api_metadata), &arena, &err); + if (ERROR_SET(&err) || metadata.type != kObjectTypeDictionary) { abort(); } - PUT_C(metadata, "ui_events", - unpack((char *)ui_events_metadata, sizeof(ui_events_metadata), &arena, &err)); - if (ERROR_SET(&err)) { - abort(); - } - PUT_C(metadata, "ui_options", ARRAY_OBJ(ui_options_metadata(&arena))); - PUT_C(metadata, "error_types", DICTIONARY_OBJ(error_type_metadata(&arena))); - PUT_C(metadata, "types", DICTIONARY_OBJ(type_metadata(&arena))); mem_for_metadata = arena_finish(&arena); } return metadata; } -static Array ui_options_metadata(Arena *arena) -{ - Array ui_options = arena_array(arena, kUIExtCount + 1); - ADD_C(ui_options, CSTR_AS_OBJ("rgb")); - for (UIExtension i = 0; i < kUIExtCount; i++) { - if (ui_ext_names[i][0] != '_') { - ADD_C(ui_options, CSTR_AS_OBJ(ui_ext_names[i])); - } - } - return ui_options; -} - -static Dictionary error_type_metadata(Arena *arena) +String api_metadata_raw(void) { - Dictionary types = arena_dict(arena, 2); - - Dictionary exception_metadata = arena_dict(arena, 1); - PUT_C(exception_metadata, "id", INTEGER_OBJ(kErrorTypeException)); - - Dictionary validation_metadata = arena_dict(arena, 1); - PUT_C(validation_metadata, "id", INTEGER_OBJ(kErrorTypeValidation)); - - PUT_C(types, "Exception", DICTIONARY_OBJ(exception_metadata)); - PUT_C(types, "Validation", DICTIONARY_OBJ(validation_metadata)); - - return types; -} - -static Dictionary type_metadata(Arena *arena) -{ - Dictionary types = arena_dict(arena, 3); - - Dictionary buffer_metadata = arena_dict(arena, 2); - PUT_C(buffer_metadata, "id", INTEGER_OBJ(kObjectTypeBuffer - EXT_OBJECT_TYPE_SHIFT)); - PUT_C(buffer_metadata, "prefix", CSTR_AS_OBJ("nvim_buf_")); - - Dictionary window_metadata = arena_dict(arena, 2); - PUT_C(window_metadata, "id", INTEGER_OBJ(kObjectTypeWindow - EXT_OBJECT_TYPE_SHIFT)); - PUT_C(window_metadata, "prefix", CSTR_AS_OBJ("nvim_win_")); - - Dictionary tabpage_metadata = arena_dict(arena, 2); - PUT_C(tabpage_metadata, "id", INTEGER_OBJ(kObjectTypeTabpage - EXT_OBJECT_TYPE_SHIFT)); - PUT_C(tabpage_metadata, "prefix", CSTR_AS_OBJ("nvim_tabpage_")); - - PUT_C(types, "Buffer", DICTIONARY_OBJ(buffer_metadata)); - PUT_C(types, "Window", DICTIONARY_OBJ(window_metadata)); - PUT_C(types, "Tabpage", DICTIONARY_OBJ(tabpage_metadata)); - - return types; + return cbuf_as_string((char *)packed_api_metadata, sizeof(packed_api_metadata)); } // all the copy_[object] functions allow arena=NULL, diff --git a/src/nvim/api/ui.h b/src/nvim/api/ui.h index 9147f1d870..cdccc27ba4 100644 --- a/src/nvim/api/ui.h +++ b/src/nvim/api/ui.h @@ -7,6 +7,20 @@ #include "nvim/types_defs.h" // IWYU pragma: keep #include "nvim/ui_defs.h" // IWYU pragma: keep +/// Keep in sync with UIExtension in ui_defs.h +EXTERN const char *ui_ext_names[] INIT( = { + "ext_cmdline", + "ext_popupmenu", + "ext_tabline", + "ext_wildmenu", + "ext_messages", + "ext_linegrid", + "ext_multigrid", + "ext_hlstate", + "ext_termcolors", + "_debug_float", +}); + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/ui.h.generated.h" # include "ui_events_remote.h.generated.h" diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 54bf290e6b..6a3c6bab67 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1529,7 +1529,7 @@ Array nvim_get_api_info(uint64_t channel_id, Arena *arena) assert(channel_id <= INT64_MAX); ADD_C(rv, INTEGER_OBJ((int64_t)channel_id)); - ADD_C(rv, DICTIONARY_OBJ(api_metadata())); + ADD_C(rv, api_metadata()); return rv; } -- cgit From dc37c1550bed46fffbb677d343cdc5bc94056219 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 25 Feb 2024 15:02:48 +0100 Subject: refactor(msgpack): allow flushing buffer while packing msgpack Before, we needed to always pack an entire msgpack_rpc Object to a continous memory buffer before sending it out to a channel. But this is generally wasteful. it is better to just flush whatever is in the buffer and then continue packing to a new buffer. This is also done for the UI event packer where there are some extra logic to "finish" of an existing batch of nevents/ncalls. This doesn't really stop us from flushing the buffer, just that we need to update the state machine accordingly so the next call to prepare_call() always will start with a new event (even though the buffer might contain overflow data from a large event). --- src/nvim/api/private/defs.h | 8 ++ src/nvim/api/private/helpers.c | 3 +- src/nvim/api/ui.c | 235 ++++++++++++----------------------------- 3 files changed, 79 insertions(+), 167 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 0cdc90e50f..ca088d7a55 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -105,6 +105,14 @@ typedef enum { kObjectTypeTabpage, } ObjectType; +/// Value by which objects represented as EXT type are shifted +/// +/// Subtracted when packing, added when unpacking. Used to allow moving +/// buffer/window/tabpage block inside ObjectType enum. This block yet cannot be +/// split or reordered. +#define EXT_OBJECT_TYPE_SHIFT kObjectTypeBuffer +#define EXT_OBJECT_TYPE_MAX (kObjectTypeTabpage - EXT_OBJECT_TYPE_SHIFT) + struct object { ObjectType type; union { diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 97ba15e871..1cd98aa0c4 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -29,7 +29,6 @@ #include "nvim/memory.h" #include "nvim/memory_defs.h" #include "nvim/message.h" -#include "nvim/msgpack_rpc/helpers.h" #include "nvim/msgpack_rpc/unpacker.h" #include "nvim/pos_defs.h" #include "nvim/types_defs.h" @@ -984,7 +983,7 @@ Dictionary api_keydict_to_dict(void *value, KeySetLink *table, size_t max_size, val = DICTIONARY_OBJ(*(Dictionary *)mem); } else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow || field->type == kObjectTypeTabpage) { - val.data.integer = *(Integer *)mem; + val.data.integer = *(handle_T *)mem; val.type = field->type; } else if (field->type == kObjectTypeLuaRef) { // do nothing diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 0f016d2f29..638a4fdcc4 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -12,6 +12,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/private/validate.h" #include "nvim/api/ui.h" +#include "nvim/assert_defs.h" #include "nvim/autocmd.h" #include "nvim/autocmd_defs.h" #include "nvim/channel.h" @@ -33,12 +34,12 @@ #include "nvim/memory_defs.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/msgpack_rpc/channel_defs.h" -#include "nvim/msgpack_rpc/helpers.h" +#include "nvim/msgpack_rpc/packer.h" #include "nvim/option.h" #include "nvim/types_defs.h" #include "nvim/ui.h" -#define BUF_POS(data) ((size_t)((data)->buf_wptr - (data)->buf)) +#define BUF_POS(data) ((size_t)((data)->packer.ptr - (data)->packer.startptr)) #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/ui.c.generated.h" @@ -47,55 +48,6 @@ static PMap(uint64_t) connected_uis = MAP_INIT; -#define mpack_w(b, byte) *(*(b))++ = (char)(byte); -static void mpack_w2(char **b, uint32_t v) -{ - *(*b)++ = (char)((v >> 8) & 0xff); - *(*b)++ = (char)(v & 0xff); -} - -static void mpack_w4(char **b, uint32_t v) -{ - *(*b)++ = (char)((v >> 24) & 0xff); - *(*b)++ = (char)((v >> 16) & 0xff); - *(*b)++ = (char)((v >> 8) & 0xff); - *(*b)++ = (char)(v & 0xff); -} - -static void mpack_uint(char **buf, uint32_t val) -{ - if (val > 0xffff) { - mpack_w(buf, 0xce); - mpack_w4(buf, val); - } else if (val > 0xff) { - mpack_w(buf, 0xcd); - mpack_w2(buf, val); - } else if (val > 0x7f) { - mpack_w(buf, 0xcc); - mpack_w(buf, val); - } else { - mpack_w(buf, val); - } -} - -static void mpack_bool(char **buf, bool val) -{ - mpack_w(buf, 0xc2 | (val ? 1 : 0)); -} - -static void mpack_array(char **buf, uint32_t len) -{ - if (len < 0x10) { - mpack_w(buf, 0x90 | len); - } else if (len < 0x10000) { - mpack_w(buf, 0xdc); - mpack_w2(buf, len); - } else { - mpack_w(buf, 0xdd); - mpack_w4(buf, len); - } -} - static char *mpack_array_dyn16(char **buf) { mpack_w(buf, 0xdc); @@ -104,9 +56,9 @@ static char *mpack_array_dyn16(char **buf) return pos; } -static void mpack_str(char **buf, const char *str, size_t len) +static void mpack_str_small(char **buf, const char *str, size_t len) { - assert(sizeof(schar_T) - 1 < 0x20); + assert(len < 0x20); mpack_w(buf, 0xa0 | len); memcpy(*buf, str, len); *buf += len; @@ -117,6 +69,7 @@ static void remote_ui_destroy(UI *ui) { UIData *data = ui->data; kv_destroy(data->call_buf); + xfree(data->packer.startptr); XFREE_CLEAR(ui->term_name); xfree(ui); } @@ -231,8 +184,13 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona data->ncalls_pos = NULL; data->ncalls = 0; data->ncells_pending = 0; - data->buf_wptr = data->buf; - data->temp_buf = NULL; + data->packer = (PackerBuffer) { + .startptr = NULL, + .ptr = NULL, + .endptr = NULL, + .packer_flush = ui_flush_callback, + .anydata = data, + }; data->wildmenu_active = false; data->call_buf = (Array)ARRAY_DICT_INIT; kv_ensure_space(data->call_buf, 16); @@ -561,72 +519,29 @@ void nvim_ui_term_event(uint64_t channel_id, String event, Object value, Error * static void flush_event(UIData *data) { if (data->cur_event) { - mpack_w2(&data->ncalls_pos, data->ncalls); + mpack_w2(&data->ncalls_pos, 1 + data->ncalls); data->cur_event = NULL; + data->ncalls_pos = NULL; + data->ncalls = 0; } - if (!data->nevents_pos) { - assert(BUF_POS(data) == 0); - char **buf = &data->buf_wptr; - // [2, "redraw", [...]] - mpack_array(buf, 3); - mpack_uint(buf, 2); - mpack_str(buf, S_LEN("redraw")); - data->nevents_pos = mpack_array_dyn16(buf); - } -} - -static inline int write_cb(void *vdata, const char *buf, size_t len) -{ - UIData *data = (UIData *)vdata; - if (!buf) { - return 0; - } - - data->pack_totlen += len; - if (!data->temp_buf && UI_BUF_SIZE - BUF_POS(data) < len) { - return 0; - } - - memcpy(data->buf_wptr, buf, len); - data->buf_wptr += len; - - return 0; } -static inline int size_cb(void *vdata, const char *buf, size_t len) +static void ui_alloc_buf(UIData *data) { - UIData *data = (UIData *)vdata; - if (!buf) { - return 0; - } - - data->pack_totlen += len; - return 0; + data->packer.startptr = alloc_block(); + data->packer.ptr = data->packer.startptr; + data->packer.endptr = data->packer.startptr + UI_BUF_SIZE; } -static void prepare_call(UI *ui, const char *name, size_t size_needed) +static void prepare_call(UI *ui, const char *name) { UIData *data = ui->data; - size_t name_len = strlen(name); - const size_t overhead = name_len + 20; - bool oversized_message = size_needed + overhead > UI_BUF_SIZE; - - if (oversized_message || BUF_POS(data) > UI_BUF_SIZE - size_needed - overhead) { - remote_ui_flush_buf(ui); + if (data->packer.startptr && BUF_POS(data) > UI_BUF_SIZE - EVENT_BUF_SIZE) { + ui_flush_buf(data); } - if (oversized_message) { - // TODO(bfredl): manually testable by setting UI_BUF_SIZE to 1024 (mode_info_set) - data->temp_buf = xmalloc(20 + name_len + size_needed); - data->buf_wptr = data->temp_buf; - char **buf = &data->buf_wptr; - mpack_array(buf, 3); - mpack_uint(buf, 2); - mpack_str(buf, S_LEN("redraw")); - mpack_array(buf, 1); - mpack_array(buf, 2); - mpack_str(buf, name, name_len); - return; + if (data->packer.startptr == NULL) { + ui_alloc_buf(data); } // To optimize data transfer(especially for "grid_line"), we bundle adjacent @@ -634,26 +549,23 @@ static void prepare_call(UI *ui, const char *name, size_t size_needed) // method call is different from "name" if (!data->cur_event || !strequal(data->cur_event, name)) { + char **buf = &data->packer.ptr; + if (!data->nevents_pos) { + // [2, "redraw", [...]] + mpack_array(buf, 3); + mpack_uint(buf, 2); + mpack_str_small(buf, S_LEN("redraw")); + data->nevents_pos = mpack_array_dyn16(buf); + assert(data->cur_event == NULL); + } flush_event(data); data->cur_event = name; - char **buf = &data->buf_wptr; data->ncalls_pos = mpack_array_dyn16(buf); - mpack_str(buf, name, strlen(name)); + mpack_str_small(buf, name, strlen(name)); data->nevents++; data->ncalls = 1; - return; - } -} - -static void send_oversized_message(UIData *data) -{ - if (data->temp_buf) { - size_t size = (size_t)(data->buf_wptr - data->temp_buf); - WBuffer *buf = wstream_new_buffer(data->temp_buf, size, 1, xfree); - rpc_write_raw(data->channel_id, buf); - data->temp_buf = NULL; - data->buf_wptr = data->buf; - data->nevents_pos = NULL; + } else { + data->ncalls++; } } @@ -661,23 +573,15 @@ static void send_oversized_message(UIData *data) static void push_call(UI *ui, const char *name, Array args) { UIData *data = ui->data; + prepare_call(ui, name); + mpack_object_array(args, &data->packer); +} - msgpack_packer pac; - data->pack_totlen = 0; - // First determine the needed size - msgpack_packer_init(&pac, data, size_cb); - msgpack_rpc_from_array(args, &pac); - // Then send the actual message - prepare_call(ui, name, data->pack_totlen); - msgpack_packer_init(&pac, data, write_cb); - msgpack_rpc_from_array(args, &pac); - - // Oversized messages need to be sent immediately - if (data->temp_buf) { - send_oversized_message(data); - } - - data->ncalls++; +static void ui_flush_callback(PackerBuffer *packer) +{ + UIData *data = packer->anydata; + ui_flush_buf(data); + ui_alloc_buf(data); } void remote_ui_grid_clear(UI *ui, Integer grid) @@ -869,12 +773,15 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int Integer clearcol, Integer clearattr, LineFlags flags, const schar_T *chunk, const sattr_T *attrs) { + // If MAX_SCHAR_SIZE is made larger, we need to refactor implementation below + // to not only use FIXSTR (only up to 0x20 bytes) + STATIC_ASSERT(MAX_SCHAR_SIZE - 1 < 0x20, "SCHAR doesn't fit in fixstr"); + UIData *data = ui->data; if (ui->ui_ext[kUILinegrid]) { - prepare_call(ui, "grid_line", EVENT_BUF_SIZE); - data->ncalls++; + prepare_call(ui, "grid_line"); - char **buf = &data->buf_wptr; + char **buf = &data->packer.ptr; mpack_array(buf, 5); mpack_uint(buf, (uint32_t)grid); mpack_uint(buf, (uint32_t)row); @@ -898,10 +805,9 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int // We only ever set the wrap field on the final "grid_line" event for the line. mpack_bool(buf, false); - remote_ui_flush_buf(ui); + ui_flush_buf(data); - prepare_call(ui, "grid_line", EVENT_BUF_SIZE); - data->ncalls++; + prepare_call(ui, "grid_line"); mpack_array(buf, 5); mpack_uint(buf, (uint32_t)grid); mpack_uint(buf, (uint32_t)row); @@ -934,7 +840,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int nelem++; data->ncells_pending += 1; mpack_array(buf, 3); - mpack_str(buf, S_LEN(" ")); + mpack_str_small(buf, S_LEN(" ")); mpack_uint(buf, (uint32_t)clearattr); mpack_uint(buf, (uint32_t)(clearcol - endcol)); } @@ -943,7 +849,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int if (data->ncells_pending > 500) { // pass off cells to UI to let it start processing them - remote_ui_flush_buf(ui); + ui_flush_buf(data); } } else { for (int i = 0; i < endcol - startcol; i++) { @@ -977,28 +883,27 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int /// /// This might happen multiple times before the actual ui_flush, if the /// total redraw size is large! -void remote_ui_flush_buf(UI *ui) +static void ui_flush_buf(UIData *data) { - UIData *data = ui->data; - if (!data->nevents_pos) { + if (!data->packer.startptr || !BUF_POS(data)) { return; } - if (data->cur_event) { - flush_event(data); + + flush_event(data); + if (data->nevents_pos != NULL) { + mpack_w2(&data->nevents_pos, data->nevents); + data->nevents = 0; + data->nevents_pos = NULL; } - mpack_w2(&data->nevents_pos, data->nevents); - data->nevents = 0; - data->nevents_pos = NULL; - // TODO(bfredl): elide copy by a length one free-list like the arena - size_t size = BUF_POS(data); - WBuffer *buf = wstream_new_buffer(xmemdup(data->buf, size), size, 1, xfree); + WBuffer *buf = wstream_new_buffer(data->packer.startptr, BUF_POS(data), 1, free_block); rpc_write_raw(data->channel_id, buf); - data->buf_wptr = data->buf; - // we have sent events to the client, but possibly not yet the final "flush" - // event. - data->flushed_events = true; + data->packer.startptr = NULL; + data->packer.ptr = NULL; + + // we have sent events to the client, but possibly not yet the final "flush" event. + data->flushed_events = true; data->ncells_pending = 0; } @@ -1014,7 +919,7 @@ void remote_ui_flush(UI *ui) remote_ui_cursor_goto(ui, data->cursor_row, data->cursor_col); } push_call(ui, "flush", (Array)ARRAY_DICT_INIT); - remote_ui_flush_buf(ui); + ui_flush_buf(data); data->flushed_events = false; } } -- cgit From e534ec47db4bf5e110c828ca3d875f5685dcfdde Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 8 Mar 2024 09:15:21 +0100 Subject: refactor(ui): remove outdated UI vs UIData distinction Just some basic spring cleaning. In the distant past, not all UI:s where remote UI:s. They still aren't, but both of the "UI" and "UIData" structs are now only for remote UI:s. Thus join them as "RemoteUI". --- src/nvim/api/ui.c | 279 +++++++++++++++++++++++++----------------------------- 1 file changed, 131 insertions(+), 148 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 638a4fdcc4..02433b037c 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -39,7 +39,7 @@ #include "nvim/types_defs.h" #include "nvim/ui.h" -#define BUF_POS(data) ((size_t)((data)->packer.ptr - (data)->packer.startptr)) +#define BUF_POS(ui) ((size_t)((ui)->packer.ptr - (ui)->packer.startptr)) #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/ui.c.generated.h" @@ -64,19 +64,18 @@ static void mpack_str_small(char **buf, const char *str, size_t len) *buf += len; } -static void remote_ui_destroy(UI *ui) +static void remote_ui_destroy(RemoteUI *ui) FUNC_ATTR_NONNULL_ALL { - UIData *data = ui->data; - kv_destroy(data->call_buf); - xfree(data->packer.startptr); + kv_destroy(ui->call_buf); + xfree(ui->packer.startptr); XFREE_CLEAR(ui->term_name); xfree(ui); } void remote_ui_disconnect(uint64_t channel_id) { - UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); + RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); if (!ui) { return; } @@ -88,7 +87,7 @@ void remote_ui_disconnect(uint64_t channel_id) #ifdef EXITFREE void remote_ui_free_all_mem(void) { - UI *ui; + RemoteUI *ui; map_foreach_value(&connected_uis, ui, { remote_ui_destroy(ui); }); @@ -146,7 +145,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona "Expected width > 0 and height > 0"); return; } - UI *ui = xcalloc(1, sizeof(UI)); + RemoteUI *ui = xcalloc(1, sizeof(RemoteUI)); ui->width = (int)width; ui->height = (int)height; ui->pum_row = -1.0; @@ -173,27 +172,26 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona ui->ui_ext[kUICmdline] = true; } - UIData *data = ui->data; - data->channel_id = channel_id; - data->cur_event = NULL; - data->hl_id = 0; - data->client_col = -1; - data->nevents_pos = NULL; - data->nevents = 0; - data->flushed_events = false; - data->ncalls_pos = NULL; - data->ncalls = 0; - data->ncells_pending = 0; - data->packer = (PackerBuffer) { + ui->channel_id = channel_id; + ui->cur_event = NULL; + ui->hl_id = 0; + ui->client_col = -1; + ui->nevents_pos = NULL; + ui->nevents = 0; + ui->flushed_events = false; + ui->ncalls_pos = NULL; + ui->ncalls = 0; + ui->ncells_pending = 0; + ui->packer = (PackerBuffer) { .startptr = NULL, .ptr = NULL, .endptr = NULL, .packer_flush = ui_flush_callback, - .anydata = data, + .anydata = ui, }; - data->wildmenu_active = false; - data->call_buf = (Array)ARRAY_DICT_INIT; - kv_ensure_space(data->call_buf, 16); + ui->wildmenu_active = false; + ui->call_buf = (Array)ARRAY_DICT_INIT; + kv_ensure_space(ui->call_buf, 16); pmap_put(uint64_t)(&connected_uis, channel_id, ui); ui_attach_impl(ui, channel_id); @@ -245,7 +243,7 @@ void nvim_ui_detach(uint64_t channel_id, Error *err) } // TODO(bfredl): use me to detach a specific ui from the server -void remote_ui_stop(UI *ui) +void remote_ui_stop(RemoteUI *ui) { } @@ -264,7 +262,7 @@ void nvim_ui_try_resize(uint64_t channel_id, Integer width, Integer height, Erro return; } - UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); + RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); ui->width = (int)width; ui->height = (int)height; ui_refresh(); @@ -278,12 +276,12 @@ void nvim_ui_set_option(uint64_t channel_id, String name, Object value, Error *e "UI not attached to channel: %" PRId64, channel_id); return; } - UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); + RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); ui_set_option(ui, false, name, value, error); } -static void ui_set_option(UI *ui, bool init, String name, Object value, Error *err) +static void ui_set_option(RemoteUI *ui, bool init, String name, Object value, Error *err) { if (strequal(name.data, "override")) { VALIDATE_T("override", kObjectTypeBoolean, value.type, { @@ -431,7 +429,7 @@ void nvim_ui_pum_set_height(uint64_t channel_id, Integer height, Error *err) return; } - UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); + RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); if (!ui->ui_ext[kUIPopupmenu]) { api_set_error(err, kErrorTypeValidation, "It must support the ext_popupmenu option"); @@ -467,7 +465,7 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Floa return; } - UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); + RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); if (!ui->ui_ext[kUIPopupmenu]) { api_set_error(err, kErrorTypeValidation, "UI must support the ext_popupmenu option"); @@ -516,78 +514,75 @@ void nvim_ui_term_event(uint64_t channel_id, String event, Object value, Error * } } -static void flush_event(UIData *data) +static void flush_event(RemoteUI *ui) { - if (data->cur_event) { - mpack_w2(&data->ncalls_pos, 1 + data->ncalls); - data->cur_event = NULL; - data->ncalls_pos = NULL; - data->ncalls = 0; + if (ui->cur_event) { + mpack_w2(&ui->ncalls_pos, 1 + ui->ncalls); + ui->cur_event = NULL; + ui->ncalls_pos = NULL; + ui->ncalls = 0; } } -static void ui_alloc_buf(UIData *data) +static void ui_alloc_buf(RemoteUI *ui) { - data->packer.startptr = alloc_block(); - data->packer.ptr = data->packer.startptr; - data->packer.endptr = data->packer.startptr + UI_BUF_SIZE; + ui->packer.startptr = alloc_block(); + ui->packer.ptr = ui->packer.startptr; + ui->packer.endptr = ui->packer.startptr + UI_BUF_SIZE; } -static void prepare_call(UI *ui, const char *name) +static void prepare_call(RemoteUI *ui, const char *name) { - UIData *data = ui->data; - if (data->packer.startptr && BUF_POS(data) > UI_BUF_SIZE - EVENT_BUF_SIZE) { - ui_flush_buf(data); + if (ui->packer.startptr && BUF_POS(ui) > UI_BUF_SIZE - EVENT_BUF_SIZE) { + ui_flush_buf(ui); } - if (data->packer.startptr == NULL) { - ui_alloc_buf(data); + if (ui->packer.startptr == NULL) { + ui_alloc_buf(ui); } // To optimize data transfer(especially for "grid_line"), we bundle adjacent // calls to same method together, so only add a new call entry if the last // method call is different from "name" - if (!data->cur_event || !strequal(data->cur_event, name)) { - char **buf = &data->packer.ptr; - if (!data->nevents_pos) { + if (!ui->cur_event || !strequal(ui->cur_event, name)) { + char **buf = &ui->packer.ptr; + if (!ui->nevents_pos) { // [2, "redraw", [...]] mpack_array(buf, 3); mpack_uint(buf, 2); mpack_str_small(buf, S_LEN("redraw")); - data->nevents_pos = mpack_array_dyn16(buf); - assert(data->cur_event == NULL); + ui->nevents_pos = mpack_array_dyn16(buf); + assert(ui->cur_event == NULL); } - flush_event(data); - data->cur_event = name; - data->ncalls_pos = mpack_array_dyn16(buf); + flush_event(ui); + ui->cur_event = name; + ui->ncalls_pos = mpack_array_dyn16(buf); mpack_str_small(buf, name, strlen(name)); - data->nevents++; - data->ncalls = 1; + ui->nevents++; + ui->ncalls = 1; } else { - data->ncalls++; + ui->ncalls++; } } -/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush(). -static void push_call(UI *ui, const char *name, Array args) +/// Pushes data into RemoteUI, to be consumed later by remote_ui_flush(). +static void push_call(RemoteUI *ui, const char *name, Array args) { - UIData *data = ui->data; prepare_call(ui, name); - mpack_object_array(args, &data->packer); + mpack_object_array(args, &ui->packer); } static void ui_flush_callback(PackerBuffer *packer) { - UIData *data = packer->anydata; - ui_flush_buf(data); - ui_alloc_buf(data); + RemoteUI *ui = packer->anydata; + ui_flush_buf(ui); + ui_alloc_buf(ui); } -void remote_ui_grid_clear(UI *ui, Integer grid) +void remote_ui_grid_clear(RemoteUI *ui, Integer grid) { - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; if (ui->ui_ext[kUILinegrid]) { ADD_C(args, INTEGER_OBJ(grid)); } @@ -595,14 +590,13 @@ void remote_ui_grid_clear(UI *ui, Integer grid) push_call(ui, name, args); } -void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer height) +void remote_ui_grid_resize(RemoteUI *ui, Integer grid, Integer width, Integer height) { - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; if (ui->ui_ext[kUILinegrid]) { ADD_C(args, INTEGER_OBJ(grid)); } else { - data->client_col = -1; // force cursor update + ui->client_col = -1; // force cursor update } ADD_C(args, INTEGER_OBJ(width)); ADD_C(args, INTEGER_OBJ(height)); @@ -610,12 +604,11 @@ void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer height) push_call(ui, name, args); } -void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integer left, +void remote_ui_grid_scroll(RemoteUI *ui, Integer grid, Integer top, Integer bot, Integer left, Integer right, Integer rows, Integer cols) { - UIData *data = ui->data; if (ui->ui_ext[kUILinegrid]) { - Array args = data->call_buf; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(grid)); ADD_C(args, INTEGER_OBJ(top)); ADD_C(args, INTEGER_OBJ(bot)); @@ -625,20 +618,20 @@ void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integ ADD_C(args, INTEGER_OBJ(cols)); push_call(ui, "grid_scroll", args); } else { - Array args = data->call_buf; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(top)); ADD_C(args, INTEGER_OBJ(bot - 1)); ADD_C(args, INTEGER_OBJ(left)); ADD_C(args, INTEGER_OBJ(right - 1)); push_call(ui, "set_scroll_region", args); - args = data->call_buf; + args = ui->call_buf; ADD_C(args, INTEGER_OBJ(rows)); push_call(ui, "scroll", args); // some clients have "clear" being affected by scroll region, // so reset it. - args = data->call_buf; + args = ui->call_buf; ADD_C(args, INTEGER_OBJ(0)); ADD_C(args, INTEGER_OBJ(ui->height - 1)); ADD_C(args, INTEGER_OBJ(0)); @@ -647,14 +640,13 @@ void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integ } } -void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, +void remote_ui_default_colors_set(RemoteUI *ui, Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, Integer cterm_fg, Integer cterm_bg) { if (!ui->ui_ext[kUITermColors]) { HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp); } - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(rgb_fg)); ADD_C(args, INTEGER_OBJ(rgb_bg)); ADD_C(args, INTEGER_OBJ(rgb_sp)); @@ -664,29 +656,28 @@ void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg, Intege // Deprecated if (!ui->ui_ext[kUILinegrid]) { - args = data->call_buf; + args = ui->call_buf; ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1)); push_call(ui, "update_fg", args); - args = data->call_buf; + args = ui->call_buf; ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_bg : cterm_bg - 1)); push_call(ui, "update_bg", args); - args = data->call_buf; + args = ui->call_buf; ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_sp : -1)); push_call(ui, "update_sp", args); } } -void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs, +void remote_ui_hl_attr_define(RemoteUI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs, Array info) { if (!ui->ui_ext[kUILinegrid]) { return; } - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(id)); MAXSIZE_TEMP_DICT(rgb, HLATTRS_DICT_SIZE); MAXSIZE_TEMP_DICT(cterm, HLATTRS_DICT_SIZE); @@ -712,15 +703,14 @@ void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cte push_call(ui, "hl_attr_define", args); } -void remote_ui_highlight_set(UI *ui, int id) +void remote_ui_highlight_set(RemoteUI *ui, int id) { - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; - if (data->hl_id == id) { + if (ui->hl_id == id) { return; } - data->hl_id = id; + ui->hl_id = id; MAXSIZE_TEMP_DICT(dict, HLATTRS_DICT_SIZE); hlattrs2dict(&dict, NULL, syn_attr2entry(id), ui->rgb, false); ADD_C(args, DICTIONARY_OBJ(dict)); @@ -728,48 +718,44 @@ void remote_ui_highlight_set(UI *ui, int id) } /// "true" cursor used only for input focus -void remote_ui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Integer col) +void remote_ui_grid_cursor_goto(RemoteUI *ui, Integer grid, Integer row, Integer col) { if (ui->ui_ext[kUILinegrid]) { - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(grid)); ADD_C(args, INTEGER_OBJ(row)); ADD_C(args, INTEGER_OBJ(col)); push_call(ui, "grid_cursor_goto", args); } else { - UIData *data = ui->data; - data->cursor_row = row; - data->cursor_col = col; + ui->cursor_row = row; + ui->cursor_col = col; remote_ui_cursor_goto(ui, row, col); } } /// emulated cursor used both for drawing and for input focus -void remote_ui_cursor_goto(UI *ui, Integer row, Integer col) +void remote_ui_cursor_goto(RemoteUI *ui, Integer row, Integer col) { - UIData *data = ui->data; - if (data->client_row == row && data->client_col == col) { + if (ui->client_row == row && ui->client_col == col) { return; } - data->client_row = row; - data->client_col = col; - Array args = data->call_buf; + ui->client_row = row; + ui->client_col = col; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(row)); ADD_C(args, INTEGER_OBJ(col)); push_call(ui, "cursor_goto", args); } -void remote_ui_put(UI *ui, const char *cell) +void remote_ui_put(RemoteUI *ui, const char *cell) { - UIData *data = ui->data; - data->client_col++; - Array args = data->call_buf; + ui->client_col++; + Array args = ui->call_buf; ADD_C(args, CSTR_AS_OBJ(cell)); push_call(ui, "put", args); } -void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Integer endcol, +void remote_ui_raw_line(RemoteUI *ui, Integer grid, Integer row, Integer startcol, Integer endcol, Integer clearcol, Integer clearattr, LineFlags flags, const schar_T *chunk, const sattr_T *attrs) { @@ -777,11 +763,10 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int // to not only use FIXSTR (only up to 0x20 bytes) STATIC_ASSERT(MAX_SCHAR_SIZE - 1 < 0x20, "SCHAR doesn't fit in fixstr"); - UIData *data = ui->data; if (ui->ui_ext[kUILinegrid]) { prepare_call(ui, "grid_line"); - char **buf = &data->packer.ptr; + char **buf = &ui->packer.ptr; mpack_array(buf, 5); mpack_uint(buf, (uint32_t)grid); mpack_uint(buf, (uint32_t)row); @@ -796,7 +781,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int for (size_t i = 0; i < ncells; i++) { repeat++; if (i == ncells - 1 || attrs[i] != attrs[i + 1] || chunk[i] != chunk[i + 1]) { - if (UI_BUF_SIZE - BUF_POS(data) < 2 * (1 + 2 + sizeof(schar_T) + 5 + 5) + 1) { + if (UI_BUF_SIZE - BUF_POS(ui) < 2 * (1 + 2 + sizeof(schar_T) + 5 + 5) + 1) { // close to overflowing the redraw buffer. finish this event, // flush, and start a new "grid_line" event at the current position. // For simplicity leave place for the final "clear" element @@ -805,7 +790,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int // We only ever set the wrap field on the final "grid_line" event for the line. mpack_bool(buf, false); - ui_flush_buf(data); + ui_flush_buf(ui); prepare_call(ui, "grid_line"); mpack_array(buf, 5); @@ -828,7 +813,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int mpack_uint(buf, repeat); } } - data->ncells_pending += MIN(repeat, 2); + ui->ncells_pending += MIN(repeat, 2); last_hl = attrs[i]; repeat = 0; was_space = chunk[i] == schar_from_ascii(' '); @@ -838,7 +823,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int // no more cells to clear, so there is no ambiguity about what to clear. if (endcol < clearcol || was_space) { nelem++; - data->ncells_pending += 1; + ui->ncells_pending += 1; mpack_array(buf, 3); mpack_str_small(buf, S_LEN(" ")); mpack_uint(buf, (uint32_t)clearattr); @@ -847,9 +832,9 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int mpack_w2(&lenpos, nelem); mpack_bool(buf, flags & kLineFlagWrap); - if (data->ncells_pending > 500) { + if (ui->ncells_pending > 500) { // pass off cells to UI to let it start processing them - ui_flush_buf(data); + ui_flush_buf(ui); } } else { for (int i = 0; i < endcol - startcol; i++) { @@ -859,7 +844,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int schar_get(sc_buf, chunk[i]); remote_ui_put(ui, sc_buf); if (utf_ambiguous_width(utf_ptr2char(sc_buf))) { - data->client_col = -1; // force cursor update + ui->client_col = -1; // force cursor update } } if (endcol < clearcol) { @@ -883,48 +868,47 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int /// /// This might happen multiple times before the actual ui_flush, if the /// total redraw size is large! -static void ui_flush_buf(UIData *data) +static void ui_flush_buf(RemoteUI *ui) { - if (!data->packer.startptr || !BUF_POS(data)) { + if (!ui->packer.startptr || !BUF_POS(ui)) { return; } - flush_event(data); - if (data->nevents_pos != NULL) { - mpack_w2(&data->nevents_pos, data->nevents); - data->nevents = 0; - data->nevents_pos = NULL; + flush_event(ui); + if (ui->nevents_pos != NULL) { + mpack_w2(&ui->nevents_pos, ui->nevents); + ui->nevents = 0; + ui->nevents_pos = NULL; } - WBuffer *buf = wstream_new_buffer(data->packer.startptr, BUF_POS(data), 1, free_block); - rpc_write_raw(data->channel_id, buf); + WBuffer *buf = wstream_new_buffer(ui->packer.startptr, BUF_POS(ui), 1, free_block); + rpc_write_raw(ui->channel_id, buf); - data->packer.startptr = NULL; - data->packer.ptr = NULL; + ui->packer.startptr = NULL; + ui->packer.ptr = NULL; // we have sent events to the client, but possibly not yet the final "flush" event. - data->flushed_events = true; - data->ncells_pending = 0; + ui->flushed_events = true; + ui->ncells_pending = 0; } /// An intentional flush (vsync) when Nvim is finished redrawing the screen /// /// Clients can know this happened by a final "flush" event at the end of the /// "redraw" batch. -void remote_ui_flush(UI *ui) +void remote_ui_flush(RemoteUI *ui) { - UIData *data = ui->data; - if (data->nevents > 0 || data->flushed_events) { + if (ui->nevents > 0 || ui->flushed_events) { if (!ui->ui_ext[kUILinegrid]) { - remote_ui_cursor_goto(ui, data->cursor_row, data->cursor_col); + remote_ui_cursor_goto(ui, ui->cursor_row, ui->cursor_col); } push_call(ui, "flush", (Array)ARRAY_DICT_INIT); - ui_flush_buf(data); - data->flushed_events = false; + ui_flush_buf(ui); + ui->flushed_events = false; } } -static Array translate_contents(UI *ui, Array contents, Arena *arena) +static Array translate_contents(RemoteUI *ui, Array contents, Arena *arena) { Array new_contents = arena_array(arena, contents.size); for (size_t i = 0; i < contents.size; i++) { @@ -944,7 +928,7 @@ static Array translate_contents(UI *ui, Array contents, Arena *arena) return new_contents; } -static Array translate_firstarg(UI *ui, Array args, Arena *arena) +static Array translate_firstarg(RemoteUI *ui, Array args, Arena *arena) { Array new_args = arena_array(arena, args.size); Array contents = args.items[0].data.array; @@ -956,10 +940,9 @@ static Array translate_firstarg(UI *ui, Array args, Arena *arena) return new_args; } -void remote_ui_event(UI *ui, char *name, Array args) +void remote_ui_event(RemoteUI *ui, char *name, Array args) { Arena arena = ARENA_EMPTY; - UIData *data = ui->data; if (!ui->ui_ext[kUILinegrid]) { // the representation of highlights in cmdline changed, translate back // never consumes args @@ -968,7 +951,7 @@ void remote_ui_event(UI *ui, char *name, Array args) push_call(ui, name, new_args); goto free_ret; } else if (strequal(name, "cmdline_block_show")) { - Array new_args = data->call_buf; + Array new_args = ui->call_buf; Array block = args.items[0].data.array; Array new_block = arena_array(&arena, block.size); for (size_t i = 0; i < block.size; i++) { @@ -987,10 +970,10 @@ void remote_ui_event(UI *ui, char *name, Array args) // Back-compat: translate popupmenu_xx to legacy wildmenu_xx. if (ui->ui_ext[kUIWildmenu]) { if (strequal(name, "popupmenu_show")) { - data->wildmenu_active = (args.items[4].data.integer == -1) - || !ui->ui_ext[kUIPopupmenu]; - if (data->wildmenu_active) { - Array new_args = data->call_buf; + ui->wildmenu_active = (args.items[4].data.integer == -1) + || !ui->ui_ext[kUIPopupmenu]; + if (ui->wildmenu_active) { + Array new_args = ui->call_buf; Array items = args.items[0].data.array; Array new_items = arena_array(&arena, items.size); for (size_t i = 0; i < items.size; i++) { @@ -999,18 +982,18 @@ void remote_ui_event(UI *ui, char *name, Array args) ADD_C(new_args, ARRAY_OBJ(new_items)); push_call(ui, "wildmenu_show", new_args); if (args.items[1].data.integer != -1) { - Array new_args2 = data->call_buf; + Array new_args2 = ui->call_buf; ADD_C(new_args2, args.items[1]); push_call(ui, "wildmenu_select", new_args2); } goto free_ret; } } else if (strequal(name, "popupmenu_select")) { - if (data->wildmenu_active) { + if (ui->wildmenu_active) { name = "wildmenu_select"; } } else if (strequal(name, "popupmenu_hide")) { - if (data->wildmenu_active) { + if (ui->wildmenu_active) { name = "wildmenu_hide"; } } -- cgit From ade1b12f49c3b3914c74847d791eb90ea90b56b7 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 8 Mar 2024 12:25:18 +0000 Subject: docs: support inline markdown - Tags are now created with `[tag]()` - References are now created with `[tag]` - Code spans are no longer wrapped --- src/nvim/api/autocmd.c | 10 +++++----- src/nvim/api/command.c | 26 +++++++++++++------------- src/nvim/api/extmark.c | 20 +++++++++++++++----- src/nvim/api/ui.c | 2 +- src/nvim/api/vim.c | 30 +++++++++++++++--------------- src/nvim/api/vimscript.c | 6 +++--- src/nvim/api/win_config.c | 2 +- 7 files changed, 53 insertions(+), 43 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 06f21919f9..d71bcc4bcf 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -386,10 +386,10 @@ cleanup: /// - id: (number) autocommand id /// - event: (string) name of the triggered event |autocmd-events| /// - group: (number|nil) autocommand group id, if any -/// - match: (string) expanded value of || -/// - buf: (number) expanded value of || -/// - file: (string) expanded value of || -/// - data: (any) arbitrary data passed from |nvim_exec_autocmds()| +/// - match: (string) expanded value of [] +/// - buf: (number) expanded value of [] +/// - file: (string) expanded value of [] +/// - data: (any) arbitrary data passed from [nvim_exec_autocmds()] /// - command (string) optional: Vim command to execute on event. Cannot be used with /// {callback} /// - once (boolean) optional: defaults to false. Run the autocommand @@ -694,7 +694,7 @@ void nvim_del_augroup_by_name(String name, Error *err) /// - buffer (integer) optional: buffer number |autocmd-buflocal|. Cannot be used with /// {pattern}. /// - modeline (bool) optional: defaults to true. Process the -/// modeline after the autocommands ||. +/// modeline after the autocommands []. /// - data (any): arbitrary data to send to the autocommand callback. See /// |nvim_create_autocmd()| for details. /// @see |:doautocmd| diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index e08035b0eb..779e216c74 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -48,16 +48,16 @@ /// @param[out] err Error details, if any. /// @return Dictionary containing command information, with these keys: /// - cmd: (string) Command name. -/// - range: (array) (optional) Command range (|| ||). +/// - range: (array) (optional) Command range ([] []). /// Omitted if command doesn't accept a range. /// Otherwise, has no elements if no range was specified, one element if /// only a single range item was specified, or two elements if both range /// items were specified. -/// - count: (number) (optional) Command ||. +/// - count: (number) (optional) Command []. /// Omitted if command cannot take a count. -/// - reg: (string) (optional) Command ||. +/// - reg: (string) (optional) Command []. /// Omitted if command cannot take a register. -/// - bang: (boolean) Whether command contains a || (!) modifier. +/// - bang: (boolean) Whether command contains a [] (!) modifier. /// - args: (array) Command arguments. /// - addr: (string) Value of |:command-addr|. Uses short name or "line" for -addr=lines. /// - nargs: (string) Value of |:command-nargs|. @@ -853,17 +853,17 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin /// from Lua, the command can also be a Lua function. The function is called with a /// single table argument that contains the following keys: /// - name: (string) Command name -/// - args: (string) The args passed to the command, if any || +/// - args: (string) The args passed to the command, if any [] /// - fargs: (table) The args split by unescaped whitespace (when more than one -/// argument is allowed), if any || +/// argument is allowed), if any [] /// - nargs: (string) Number of arguments |:command-nargs| -/// - bang: (boolean) "true" if the command was executed with a ! modifier || -/// - line1: (number) The starting line of the command range || -/// - line2: (number) The final line of the command range || -/// - range: (number) The number of items in the command range: 0, 1, or 2 || -/// - count: (number) Any count supplied || -/// - reg: (string) The optional register, if specified || -/// - mods: (string) Command modifiers, if any || +/// - bang: (boolean) "true" if the command was executed with a ! modifier [] +/// - line1: (number) The starting line of the command range [] +/// - line2: (number) The final line of the command range [] +/// - range: (number) The number of items in the command range: 0, 1, or 2 [] +/// - count: (number) Any count supplied [] +/// - reg: (string) The optional register, if specified [] +/// - mods: (string) Command modifiers, if any [] /// - smods: (table) Command modifiers in a structured format. Has the same /// structure as the "mods" key of |nvim_parse_cmd()|. /// @param opts Optional |command-attributes|. diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index e35840915f..1b03a97edb 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -43,7 +43,7 @@ void api_extmark_free_all_mem(void) map_destroy(String, &namespace_ids); } -/// Creates a new namespace or gets an existing one. \*namespace\* +/// Creates a new namespace or gets an existing one. [namespace]() /// /// Namespaces are used for buffer highlights and virtual text, see /// |nvim_buf_add_highlight()| and |nvim_buf_set_extmark()|. @@ -284,7 +284,7 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, /// their start position is less than `start` /// - type: Filter marks by type: "highlight", "sign", "virt_text" and "virt_lines" /// @param[out] err Error details, if any -/// @return List of [extmark_id, row, col] tuples in "traversal order". +/// @return List of `[extmark_id, row, col]` tuples in "traversal order". Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object end, Dict(get_extmarks) *opts, Arena *arena, Error *err) FUNC_API_SINCE(7) @@ -390,7 +390,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// of the screen line (just like for diff and /// cursorline highlight). /// - virt_text : virtual text to link to this mark. -/// A list of [text, highlight] tuples, each representing a +/// A list of `[text, highlight]` tuples, each representing a /// text chunk with specified highlight. `highlight` element /// can either be a single highlight group, or an array of /// multiple highlight groups that will be stacked @@ -425,7 +425,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// /// - virt_lines : virtual lines to add next to this mark /// This should be an array over lines, where each line in -/// turn is an array over [text, highlight] tuples. In +/// turn is an array over `[text, highlight]` tuples. In /// general, buffer and window options do not affect the /// display of the text. In particular 'wrap' /// and 'linebreak' options do not take effect, so @@ -1040,17 +1040,27 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start, /// @param ns_id Namespace id from |nvim_create_namespace()| /// @param opts Table of callbacks: /// - on_start: called first on each screen redraw +/// ``` /// ["start", tick] +/// ``` /// - on_buf: called for each buffer being redrawn (before -/// window callbacks) +/// window callbacks) +/// ``` /// ["buf", bufnr, tick] +/// ``` /// - on_win: called when starting to redraw a specific window. +/// ``` /// ["win", winid, bufnr, topline, botline] +/// ``` /// - on_line: called for each buffer line being redrawn. /// (The interaction with fold lines is subject to change) +/// ``` /// ["line", winid, bufnr, row] +/// ``` /// - on_end: called at the end of a redraw cycle +/// ``` /// ["end", tick] +/// ``` void nvim_set_decoration_provider(Integer ns_id, Dict(set_decoration_provider) *opts, Error *err) FUNC_API_SINCE(7) FUNC_API_LUA_ONLY { diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 02433b037c..692e3f95fc 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -410,7 +410,7 @@ void nvim_ui_try_resize_grid(uint64_t channel_id, Integer grid, Integer width, I } /// Tells Nvim the number of elements displaying in the popupmenu, to decide -/// and movement. +/// [] and [] movement. /// /// @param channel_id /// @param height Popupmenu height, must be greater than zero. diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 6a3c6bab67..84a2f24dbc 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -249,7 +249,7 @@ void nvim_set_hl_ns_fast(Integer ns_id, Error *err) /// /// On execution error: does not fail, but updates v:errmsg. /// -/// To input sequences like use |nvim_replace_termcodes()| (typically +/// To input sequences like [] use |nvim_replace_termcodes()| (typically /// with escape_ks=false) to replace |keycodes|, then pass the result to /// nvim_feedkeys(). /// @@ -337,11 +337,11 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_ks) /// /// On execution error: does not fail, but updates v:errmsg. /// -/// @note |keycodes| like are translated, so "<" is special. -/// To input a literal "<", send . +/// @note |keycodes| like [] are translated, so "<" is special. +/// To input a literal "<", send []. /// /// @note For mouse events use |nvim_input_mouse()|. The pseudokey form -/// "" is deprecated since |api-level| 6. +/// `` is deprecated since |api-level| 6. /// /// @param keys to be typed /// @return Number of bytes actually written (can be fewer than @@ -362,7 +362,7 @@ Integer nvim_input(String keys) /// by calling it multiple times in a loop: the intermediate mouse /// positions will be ignored. It should be used to implement real-time /// mouse input in a GUI. The deprecated pseudokey form -/// ("") of |nvim_input()| has the same limitation. +/// (``) of |nvim_input()| has the same limitation. /// /// @param button Mouse button: one of "left", "right", "middle", "wheel", "move", /// "x1", "x2". @@ -451,13 +451,13 @@ error: "invalid button or action"); } -/// Replaces terminal codes and |keycodes| (, , ...) in a string with +/// Replaces terminal codes and |keycodes| ([], [], ...) in a string with /// the internal representation. /// /// @param str String to be converted. /// @param from_part Legacy Vim parameter. Usually true. -/// @param do_lt Also translate . Ignored if `special` is false. -/// @param special Replace |keycodes|, e.g. becomes a "\r" char. +/// @param do_lt Also translate []. Ignored if `special` is false. +/// @param special Replace |keycodes|, e.g. [] becomes a "\r" char. /// @see replace_termcodes /// @see cpoptions String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Boolean special) @@ -525,7 +525,7 @@ Object nvim_notify(String msg, Integer log_level, Dictionary opts, Arena *arena, } /// Calculates the number of display cells occupied by `text`. -/// Control characters including count as one cell. +/// Control characters including [] count as one cell. /// /// @param text Some text /// @param[out] err Error details, if any @@ -762,7 +762,7 @@ void nvim_set_vvar(String name, Object value, Error *err) /// Echo a message. /// -/// @param chunks A list of [text, hl_group] arrays, each representing a +/// @param chunks A list of `[text, hl_group]` arrays, each representing a /// text chunk with specified highlight. `hl_group` element /// can be omitted for no highlight. /// @param history if true, add to |message-history|. @@ -1019,7 +1019,7 @@ fail: /// master end. For instance, a carriage return is sent /// as a "\r", not as a "\n". |textlock| applies. It is possible /// to call |nvim_chan_send()| directly in the callback however. -/// ["input", term, bufnr, data] +/// `["input", term, bufnr, data]` /// - force_crlf: (boolean, default true) Convert "\n" to "\r\n". /// @param[out] err Error details, if any /// @return Channel id, or 0 on error @@ -1471,7 +1471,7 @@ ArrayOf(Dictionary) nvim_get_keymap(String mode, Arena *arena) /// To set a buffer-local mapping, use |nvim_buf_set_keymap()|. /// /// Unlike |:map|, leading/trailing whitespace is accepted as part of the {lhs} or {rhs}. -/// Empty {rhs} is ||. |keycodes| are replaced as usual. +/// Empty {rhs} is []. |keycodes| are replaced as usual. /// /// Example: /// @@ -1491,7 +1491,7 @@ ArrayOf(Dictionary) nvim_get_keymap(String mode, Arena *arena) /// "ia", "ca" or "!a" for abbreviation in Insert mode, Cmdline mode, or both, respectively /// @param lhs Left-hand-side |{lhs}| of the mapping. /// @param rhs Right-hand-side |{rhs}| of the mapping. -/// @param opts Optional parameters map: Accepts all |:map-arguments| as keys except ||, +/// @param opts Optional parameters map: Accepts all |:map-arguments| as keys except [], /// values are booleans (default false). Also: /// - "noremap" disables |recursive_mapping|, like |:noremap| /// - "desc" human-readable description. @@ -1521,7 +1521,7 @@ void nvim_del_keymap(uint64_t channel_id, String mode, String lhs, Error *err) /// Returns a 2-tuple (Array), where item 0 is the current channel id and item /// 1 is the |api-metadata| map (Dictionary). /// -/// @returns 2-tuple [{channel-id}, {api-metadata}] +/// @returns 2-tuple `[{channel-id}, {api-metadata}]` Array nvim_get_api_info(uint64_t channel_id, Arena *arena) FUNC_API_SINCE(1) FUNC_API_FAST FUNC_API_REMOTE_ONLY { @@ -1960,7 +1960,7 @@ Object nvim_get_proc(Integer pid, Arena *arena, Error *err) /// If neither |ins-completion| nor |cmdline-completion| popup menu is active /// this API call is silently ignored. /// Useful for an external UI using |ui-popupmenu| to control the popup menu with the mouse. -/// Can also be used in a mapping; use |:map-cmd| or a Lua mapping to ensure the mapping +/// Can also be used in a mapping; use [] |:map-cmd| or a Lua mapping to ensure the mapping /// doesn't end completion mode. /// /// @param item Index (zero-based) of the item to select. Value of -1 selects nothing diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index 8f57e61c76..477cbe2428 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -365,8 +365,8 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; /// - "l" when needing to start parsing with lvalues for /// ":let" or ":for". /// Common flag sets: -/// - "m" to parse like for ":echo". -/// - "E" to parse like for "=". +/// - "m" to parse like for `":echo"`. +/// - "E" to parse like for `"="`. /// - empty string for ":call". /// - "lm" to parse for ":let". /// @param[in] highlight If true, return value will also include "highlight" @@ -390,7 +390,7 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; /// - "ast": AST, either nil or a dictionary with these keys: /// - "type": node type, one of the value names from ExprASTNodeType /// stringified without "kExprNode" prefix. -/// - "start": a pair [line, column] describing where node is "started" +/// - "start": a pair `[line, column]` describing where node is "started" /// where "line" is always 0 (will not be 0 if you will be /// using this API on e.g. ":let", but that is not /// present yet). Both elements are Integers. diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 3cc520dc78..543c7b8113 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -116,7 +116,7 @@ /// - width: Window width (in character cells). Minimum of 1. /// - height: Window height (in character cells). Minimum of 1. /// - bufpos: Places float relative to buffer text (only when -/// relative="win"). Takes a tuple of zero-indexed [line, column]. +/// relative="win"). Takes a tuple of zero-indexed `[line, column]`. /// `row` and `col` if given are applied relative to this /// position, else they default to: /// - `row=1` and `col=0` if `anchor` is "NW" or "NE" -- cgit