diff options
39 files changed, 887 insertions, 759 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index df345e4981..a8b4bc7dd2 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -1251,7 +1251,7 @@ nvim_open_term({buffer}, {opts}) *nvim_open_term()* For instance, for a floating display, first create an empty buffer using |nvim_create_buf()|, then display it using |nvim_open_win()|, and then call this function. Then - |nvim_chan_send()| cal be called immediately to process + |nvim_chan_send()| can be called immediately to process sequences in a virtual terminal having the intended size. Parameters: ~ diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index d6f72b68f7..d9ecdb40de 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -3419,6 +3419,8 @@ complete_info([{what}]) *complete_info()* "" Not in completion mode "keyword" Keyword completion |i_CTRL-X_CTRL-N| "ctrl_x" Just pressed CTRL-X |i_CTRL-X| + "scroll" Scrolling with |i_CTRL-X_CTRL-E| or + |i_CTRL-X_CTRL-Y| "whole_line" Whole lines |i_CTRL-X_CTRL-L| "files" File names |i_CTRL-X_CTRL-F| "tags" Tags |i_CTRL-X_CTRL-]| @@ -6863,29 +6865,31 @@ mode([expr]) Return a string that indicates the current mode. niR Normal using |i_CTRL-O| in |Replace-mode| niV Normal using |i_CTRL-O| in |Virtual-Replace-mode| v Visual by character + vs Visual by character using |v_CTRL-O| in Select mode V Visual by line + Vs Visual by line using |v_CTRL-O| in Select mode CTRL-V Visual blockwise + CTRL-Vs Visual blockwise using |v_CTRL-O| in Select mode s Select by character S Select by line CTRL-S Select blockwise - vs Visual by character using |v_CTRL-O| from - Select mode - Vs Visual by line using |v_CTRL-O| from Select mode - CTRL-Vs Visual blockwise using |v_CTRL-O| from Select mode i Insert ic Insert mode completion |compl-generic| ix Insert mode |i_CTRL-X| completion R Replace |R| Rc Replace mode completion |compl-generic| - Rv Virtual Replace |gR| Rx Replace mode |i_CTRL-X| completion + Rv Virtual Replace |gR| + Rvc Virtual Replace mode completion |compl-generic| + Rvx Virtual Replace mode |i_CTRL-X| completion c Command-line editing cv Vim Ex mode |Q| or |gQ| r Hit-enter prompt rm The -- more -- prompt - r? |:confirm| query of some sort + r? A |:confirm| query of some sort ! Shell or external command is executing t Terminal mode: keys go to the job + This is useful in the 'statusline' option or when used with |remote_expr()| In most other places it always returns "c" or "n". diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 22e323baa7..f6daa70b4c 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -1373,20 +1373,25 @@ pesc({s}) *vim.pesc()* See also: ~ https://github.com/rxi/lume -split({s}, {sep}, {plain}) *vim.split()* +split({s}, {sep}, {kwargs}) *vim.split()* Splits a string at each instance of a separator. Examples: > - split(":aa::b:", ":") --> {'','aa','','b',''} - split("axaby", "ab?") --> {'','x','y'} - split(x*yz*o, "*", true) --> {'x','yz','o'} + + split(":aa::b:", ":") --> {'','aa','','b',''} + split("axaby", "ab?") --> {'','x','y'} + split("x*yz*o", "*", {plain=true}) --> {'x','yz','o'} + split("|x|y|z|", "|", {trimempty=true}) --> {'x', 'y', 'z'} < Parameters: ~ - {s} String to split - {sep} Separator string or pattern - {plain} If `true` use `sep` literally (passed to - String.find) + {s} String to split + {sep} Separator string or pattern + {kwargs} Keyword arguments: + • plain: (boolean) If `true` use `sep` literally + (passed to string.find) + • trimempty: (boolean) If `true` remove empty + items from the front and back of the list Return: ~ List-like table of the split components. diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index 87a48e6d2a..bb775ec884 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -90,10 +90,11 @@ argument. See |info-message| about capturing the text. *--clean* ---clean Equivalent to "-u NONE -i NONE": +--clean Mimics a fresh install of Nvim: - Skips initializations from files and environment variables. - No 'shada' file is read or written. - Excludes user directories from 'runtimepath' + - Loads builtin plugins, unlike "-u NONE -i NONE". *--noplugin* --noplugin Skip loading plugins. Resets the 'loadplugins' option. diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 5e0c6fb32e..322215e1de 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -872,6 +872,9 @@ au BufNewFile,BufRead *.json-patch setf json " Jupyter Notebook is also json au BufNewFile,BufRead *.ipynb setf json +" Other files that look like json +au BufNewFile,BufRead .babelrc,.eslintrc,.prettierrc,.firebaserc setf json + " JSONC au BufNewFile,BufRead *.jsonc setf jsonc diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 18c1e21049..b57b7ad4ad 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -98,17 +98,53 @@ end --- <pre> --- split(":aa::b:", ":") --> {'','aa','','b',''} --- split("axaby", "ab?") --> {'','x','y'} ---- split(x*yz*o, "*", true) --> {'x','yz','o'} +--- split("x*yz*o", "*", {plain=true}) --> {'x','yz','o'} +--- split("|x|y|z|", "|", {trimempty=true}) --> {'x', 'y', 'z'} --- </pre> --- +--- ---@see |vim.gsplit()| --- ---@param s String to split ---@param sep Separator string or pattern ----@param plain If `true` use `sep` literally (passed to String.find) +---@param kwargs Keyword arguments: +--- - plain: (boolean) If `true` use `sep` literally (passed to string.find) +--- - trimempty: (boolean) If `true` remove empty items from the front +--- and back of the list ---@returns List-like table of the split components. -function vim.split(s,sep,plain) - local t={} for c in vim.gsplit(s, sep, plain) do table.insert(t,c) end +function vim.split(s, sep, kwargs) + local plain + local trimempty = false + if type(kwargs) == 'boolean' then + -- Support old signature for backward compatibility + plain = kwargs + else + vim.validate { kwargs = {kwargs, 't', true} } + kwargs = kwargs or {} + plain = kwargs.plain + trimempty = kwargs.trimempty + end + + local t = {} + local skip = trimempty + for c in vim.gsplit(s, sep, plain) do + if c ~= "" then + skip = false + end + + if not skip then + table.insert(t, c) + end + end + + if trimempty then + for i = #t, 1, -1 do + if t[i] ~= "" then + break + end + table.remove(t, i) + end + end + return t end diff --git a/runtime/plugin/diagnostic.vim b/runtime/plugin/diagnostic.vim deleted file mode 100644 index 2183623ac8..0000000000 --- a/runtime/plugin/diagnostic.vim +++ /dev/null @@ -1,26 +0,0 @@ -" :help vim.diagnostic - -hi default DiagnosticError ctermfg=1 guifg=Red -hi default DiagnosticWarn ctermfg=3 guifg=Orange -hi default DiagnosticInfo ctermfg=4 guifg=LightBlue -hi default DiagnosticHint ctermfg=7 guifg=LightGrey - -hi default DiagnosticUnderlineError cterm=underline gui=underline guisp=Red -hi default DiagnosticUnderlineWarn cterm=underline gui=underline guisp=Orange -hi default DiagnosticUnderlineInfo cterm=underline gui=underline guisp=LightBlue -hi default DiagnosticUnderlineHint cterm=underline gui=underline guisp=LightGrey - -hi default link DiagnosticVirtualTextError DiagnosticError -hi default link DiagnosticVirtualTextWarn DiagnosticWarn -hi default link DiagnosticVirtualTextInfo DiagnosticInfo -hi default link DiagnosticVirtualTextHint DiagnosticHint - -hi default link DiagnosticFloatingError DiagnosticError -hi default link DiagnosticFloatingWarn DiagnosticWarn -hi default link DiagnosticFloatingInfo DiagnosticInfo -hi default link DiagnosticFloatingHint DiagnosticHint - -hi default link DiagnosticSignError DiagnosticError -hi default link DiagnosticSignWarn DiagnosticWarn -hi default link DiagnosticSignInfo DiagnosticInfo -hi default link DiagnosticSignHint DiagnosticHint diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 332fc0ba96..907d09e5b7 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -165,6 +165,7 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, A /// @param lines Array of lines /// @param[out] err Error details, if any void buffer_insert(Buffer buffer, Integer lnum, ArrayOf(String) lines, 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 @@ -184,6 +185,7 @@ void buffer_insert(Buffer buffer, Integer lnum, ArrayOf(String) lines, Error *er /// @param[out] err Error details, if any /// @return Line string String buffer_get_line(Buffer buffer, Integer index, Error *err) + FUNC_API_DEPRECATED_SINCE(1) { String rv = { .size = 0 }; @@ -212,6 +214,7 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err) /// @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) + FUNC_API_DEPRECATED_SINCE(1) { Object l = STRING_OBJ(line); Array array = { .items = &l, .size = 1 }; @@ -230,6 +233,7 @@ void buffer_set_line(Buffer buffer, Integer index, String line, Error *err) /// @param index line index /// @param[out] err Error details, if any void buffer_del_line(Buffer buffer, Integer index, Error *err) + FUNC_API_DEPRECATED_SINCE(1) { Array array = ARRAY_DICT_INIT; index = convert_index(index); @@ -255,6 +259,7 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer, Boolean include_start, Boolean include_end, Error *err) + FUNC_API_DEPRECATED_SINCE(1) { start = convert_index(start) + !include_start; end = convert_index(end) + include_end; @@ -278,6 +283,7 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer, /// @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) + FUNC_API_DEPRECATED_SINCE(1) { start = convert_index(start) + !include_start; end = convert_index(end) + include_end; @@ -298,6 +304,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) + FUNC_API_DEPRECATED_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -317,6 +324,7 @@ Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) /// @param[out] err Error details, if any /// @return Old value Object buffer_del_var(Buffer buffer, String name, Error *err) + FUNC_API_DEPRECATED_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -340,6 +348,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) + FUNC_API_DEPRECATED_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -359,6 +368,7 @@ Object window_set_var(Window window, String name, Object value, Error *err) /// @param[out] err Error details, if any /// @return Old value Object window_del_var(Window window, String name, Error *err) + FUNC_API_DEPRECATED_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -382,6 +392,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) + FUNC_API_DEPRECATED_SINCE(1) { tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -401,6 +412,7 @@ Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) /// @param[out] err Error details, if any /// @return Old value Object tabpage_del_var(Tabpage tabpage, String name, Error *err) + FUNC_API_DEPRECATED_SINCE(1) { tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -417,6 +429,7 @@ Object tabpage_del_var(Tabpage tabpage, String name, Error *err) /// 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) + FUNC_API_DEPRECATED_SINCE(1) { return dict_set_var(&globvardict, name, value, false, true, err); } @@ -424,6 +437,7 @@ Object vim_set_var(String name, Object value, Error *err) /// @deprecated /// @see nvim_del_var Object vim_del_var(String name, Error *err) + FUNC_API_DEPRECATED_SINCE(1) { return dict_set_var(&globvardict, name, NIL, true, true, err); } diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c index 5e93ccce17..ee0cdc4c07 100644 --- a/src/nvim/api/private/dispatch.c +++ b/src/nvim/api/private/dispatch.c @@ -15,6 +15,7 @@ #include "nvim/api/ui.h" #include "nvim/api/vim.h" #include "nvim/api/window.h" +#include "nvim/api/win_config.h" #include "nvim/log.h" #include "nvim/map.h" #include "nvim/msgpack_rpc/helpers.h" diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 24fac0a916..7b70c183c3 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -396,7 +396,7 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object return; } - stringval = (char *)value.data.string.data; + stringval = value.data.string.data; } const sctx_T save_current_sctx = current_sctx; @@ -1648,361 +1648,6 @@ const char *describe_ns(NS ns_id) return "(UNKNOWN PLUGIN)"; } -static bool parse_float_anchor(String anchor, FloatAnchor *out) -{ - if (anchor.size == 0) { - *out = (FloatAnchor)0; - } - char *str = anchor.data; - if (striequal(str, "NW")) { - *out = 0; // NW is the default - } else if (striequal(str, "NE")) { - *out = kFloatAnchorEast; - } else if (striequal(str, "SW")) { - *out = kFloatAnchorSouth; - } else if (striequal(str, "SE")) { - *out = kFloatAnchorSouth | kFloatAnchorEast; - } else { - return false; - } - return true; -} - -static bool parse_float_relative(String relative, FloatRelative *out) -{ - char *str = relative.data; - if (striequal(str, "editor")) { - *out = kFloatRelativeEditor; - } else if (striequal(str, "win")) { - *out = kFloatRelativeWindow; - } else if (striequal(str, "cursor")) { - *out = kFloatRelativeCursor; - } else { - return false; - } - return true; -} - -static bool parse_float_bufpos(Array bufpos, lpos_T *out) -{ - if (bufpos.size != 2 - || bufpos.items[0].type != kObjectTypeInteger - || bufpos.items[1].type != kObjectTypeInteger) { - return false; - } - out->lnum = bufpos.items[0].data.integer; - out->col = (colnr_T)bufpos.items[1].data.integer; - return true; -} - -static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) -{ - struct { - const char *name; - schar_T chars[8]; - bool shadow_color; - } defaults[] = { - { "double", { "╔", "═", "╗", "║", "╝", "═", "╚", "║" }, false }, - { "single", { "┌", "─", "┐", "│", "┘", "─", "└", "│" }, false }, - { "shadow", { "", "", " ", " ", " ", " ", " ", "" }, true }, - { "rounded", { "╭", "─", "╮", "│", "╯", "─", "╰", "│" }, false }, - { "solid", { " ", " ", " ", " ", " ", " ", " ", " " }, false }, - { NULL, { { NUL } }, false }, - }; - - schar_T *chars = fconfig->border_chars; - int *hl_ids = fconfig->border_hl_ids; - - fconfig->border = true; - - if (style.type == kObjectTypeArray) { - 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"); - return; - } - for (size_t i = 0; i < size; i++) { - Object iytem = arr.items[i]; - String string; - int hl_id = 0; - if (iytem.type == kObjectTypeArray) { - Array iarr = iytem.data.array; - if (!iarr.size || iarr.size > 2) { - api_set_error(err, kErrorTypeValidation, "invalid border char"); - return; - } - if (iarr.items[0].type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, "invalid border char"); - return; - } - string = iarr.items[0].data.string; - if (iarr.size == 2) { - hl_id = object_to_hl_id(iarr.items[1], "border char highlight", err); - if (ERROR_SET(err)) { - return; - } - } - } else if (iytem.type == kObjectTypeString) { - string = iytem.data.string; - } else { - api_set_error(err, kErrorTypeValidation, "invalid border char"); - return; - } - if (string.size - && mb_string2cells_len((char_u *)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); - if (len) { - memcpy(chars[i], string.data, len); - } - chars[i][len] = NUL; - hl_ids[i] = hl_id; - } - while (size < 8) { - memcpy(chars+size, chars, sizeof(*chars) * size); - memcpy(hl_ids+size, hl_ids, sizeof(*hl_ids) * size); - size <<= 1; - } - if ((chars[7][0] && chars[1][0] && !chars[0][0]) - || (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"); - } - } else if (style.type == kObjectTypeString) { - String str = style.data.string; - if (str.size == 0 || strequal(str.data, "none")) { - fconfig->border = false; - return; - } - for (size_t i = 0; defaults[i].name; i++) { - if (strequal(str.data, defaults[i].name)) { - memcpy(chars, defaults[i].chars, sizeof(defaults[i].chars)); - memset(hl_ids, 0, 8 * sizeof(*hl_ids)); - if (defaults[i].shadow_color) { - int hl_blend = SYN_GROUP_STATIC("FloatShadow"); - int hl_through = SYN_GROUP_STATIC("FloatShadowThrough"); - hl_ids[2] = hl_through; - hl_ids[3] = hl_blend; - hl_ids[4] = hl_blend; - hl_ids[5] = hl_blend; - hl_ids[6] = hl_through; - } - return; - } - } - api_set_error(err, kErrorTypeValidation, - "invalid border style \"%s\"", str.data); - } -} - -bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, bool reconf, bool new_win, - Error *err) -{ - bool has_relative = false, relative_is_win = false; - if (config->relative.type == kObjectTypeString) { - // ignore empty string, to match nvim_win_get_config - if (config->relative.data.string.size > 0) { - if (!parse_float_relative(config->relative.data.string, &fconfig->relative)) { - api_set_error(err, kErrorTypeValidation, "Invalid value of 'relative' key"); - return false; - } - - if (!(HAS_KEY(config->row) && HAS_KEY(config->col)) && !HAS_KEY(config->bufpos)) { - api_set_error(err, kErrorTypeValidation, - "'relative' requires 'row'/'col' or 'bufpos'"); - return false; - } - - has_relative = true; - fconfig->external = false; - if (fconfig->relative == kFloatRelativeWindow) { - relative_is_win = true; - fconfig->bufpos.lnum = -1; - } - } - } else if (HAS_KEY(config->relative)) { - api_set_error(err, kErrorTypeValidation, "'relative' key must be String"); - return false; - } - - if (config->anchor.type == kObjectTypeString) { - if (!parse_float_anchor(config->anchor.data.string, &fconfig->anchor)) { - api_set_error(err, kErrorTypeValidation, "Invalid value of 'anchor' key"); - return false; - } - } else if (HAS_KEY(config->anchor)) { - api_set_error(err, kErrorTypeValidation, "'anchor' key must be String"); - return false; - } - - if (HAS_KEY(config->row)) { - if (!has_relative) { - api_set_error(err, kErrorTypeValidation, "non-float cannot have 'row'"); - return false; - } else if (config->row.type == kObjectTypeInteger) { - fconfig->row = (double)config->row.data.integer; - } else if (config->row.type == kObjectTypeFloat) { - fconfig->row = config->row.data.floating; - } else { - api_set_error(err, kErrorTypeValidation, - "'row' key must be Integer or Float"); - return false; - } - } - - if (HAS_KEY(config->col)) { - if (!has_relative) { - api_set_error(err, kErrorTypeValidation, "non-float cannot have 'col'"); - return false; - } else if (config->col.type == kObjectTypeInteger) { - fconfig->col = (double)config->col.data.integer; - } else if (config->col.type == kObjectTypeFloat) { - fconfig->col = config->col.data.floating; - } else { - api_set_error(err, kErrorTypeValidation, - "'col' key must be Integer or Float"); - return false; - } - } - - if (HAS_KEY(config->bufpos)) { - if (!has_relative) { - api_set_error(err, kErrorTypeValidation, "non-float cannot have 'bufpos'"); - return false; - } else if (config->bufpos.type == kObjectTypeArray) { - if (!parse_float_bufpos(config->bufpos.data.array, &fconfig->bufpos)) { - api_set_error(err, kErrorTypeValidation, "Invalid value of 'bufpos' key"); - return false; - } - - if (!HAS_KEY(config->row)) { - fconfig->row = (fconfig->anchor & kFloatAnchorSouth) ? 0 : 1; - } - if (!HAS_KEY(config->col)) { - fconfig->col = 0; - } - } else { - api_set_error(err, kErrorTypeValidation, "'bufpos' key must be Array"); - return false; - } - } - - if (config->width.type == kObjectTypeInteger && config->width.data.integer > 0) { - fconfig->width = (int)config->width.data.integer; - } else if (HAS_KEY(config->width)) { - api_set_error(err, kErrorTypeValidation, "'width' key must be a positive Integer"); - return false; - } else if (!reconf) { - api_set_error(err, kErrorTypeValidation, "Must specify 'width'"); - return false; - } - - if (config->height.type == kObjectTypeInteger && config->height.data.integer > 0) { - fconfig->height = (int)config->height.data.integer; - } else if (HAS_KEY(config->height)) { - api_set_error(err, kErrorTypeValidation, "'height' key must be a positive Integer"); - return false; - } else if (!reconf) { - api_set_error(err, kErrorTypeValidation, "Must specify 'height'"); - return false; - } - - if (relative_is_win) { - fconfig->window = curwin->handle; - if (config->win.type == kObjectTypeInteger || config->win.type == kObjectTypeWindow) { - if (config->win.data.integer > 0) { - fconfig->window = (Window)config->win.data.integer; - } - } else if (HAS_KEY(config->win)) { - api_set_error(err, kErrorTypeValidation, "'win' key must be Integer or Window"); - return false; - } - } else { - if (HAS_KEY(config->win)) { - api_set_error(err, kErrorTypeValidation, "'win' key is only valid with relative='win'"); - return false; - } - } - - if (HAS_KEY(config->external)) { - fconfig->external = api_object_to_bool(config->external, "'external' key", false, err); - if (ERROR_SET(err)) { - return false; - } - if (has_relative && fconfig->external) { - api_set_error(err, kErrorTypeValidation, - "Only one of 'relative' and 'external' must be used"); - return false; - } - if (fconfig->external && !ui_has(kUIMultigrid)) { - 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(config->focusable)) { - fconfig->focusable = api_object_to_bool(config->focusable, "'focusable' key", false, err); - if (ERROR_SET(err)) { - return false; - } - } - - if (config->zindex.type == kObjectTypeInteger && config->zindex.data.integer > 0) { - fconfig->zindex = (int)config->zindex.data.integer; - } else if (HAS_KEY(config->zindex)) { - api_set_error(err, kErrorTypeValidation, "'zindex' key must be a positive Integer"); - return false; - } - - if (HAS_KEY(config->border)) { - parse_border_style(config->border, fconfig, err); - if (ERROR_SET(err)) { - return false; - } - } - - if (config->style.type == kObjectTypeString) { - if (config->style.data.string.data[0] == NUL) { - fconfig->style = kWinStyleUnused; - } else if (striequal(config->style.data.string.data, "minimal")) { - fconfig->style = kWinStyleMinimal; - } else { - api_set_error(err, kErrorTypeValidation, "Invalid value of 'style' key"); - } - } else if (HAS_KEY(config->style)) { - api_set_error(err, kErrorTypeValidation, "'style' key must be String"); - return false; - } - - if (HAS_KEY(config->noautocmd)) { - if (!new_win) { - api_set_error(err, kErrorTypeValidation, "Invalid key: 'noautocmd'"); - return false; - } - fconfig->noautocmd = api_object_to_bool(config->noautocmd, "'noautocmd' key", false, err); - if (ERROR_SET(err)) { - return false; - } - } - - return true; -} - bool api_dict_to_keydict(void *rv, field_hash hashy, Dictionary dict, Error *err) { for (size_t i = 0; i < dict.size; i++) { diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 9b200dcba2..d86aecc318 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -40,7 +40,6 @@ typedef struct { static PMap(uint64_t) connected_uis = MAP_INIT; void remote_ui_disconnect(uint64_t channel_id) - FUNC_API_NOEXPORT { UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); if (!ui) { @@ -57,7 +56,6 @@ void remote_ui_disconnect(uint64_t channel_id) /// Wait until ui has connected on stdio channel. void remote_ui_wait_for_attach(void) - FUNC_API_NOEXPORT { Channel *channel = find_channel(CHAN_STDIO); if (!channel) { @@ -172,6 +170,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona /// @deprecated 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)); diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index f80d605d9a..45fca8b1c5 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -59,7 +59,6 @@ #endif void api_vim_free_all_mem(void) - FUNC_API_NOEXPORT { String name; handle_T id; @@ -264,7 +263,6 @@ void nvim__set_hl_ns(Integer ns_id, Error *err) } static void on_redraw_event(void **argv) - FUNC_API_NOEXPORT { redraw_all_later(NOT_VALID); } @@ -1221,7 +1219,7 @@ fail: /// buffer in a configured window before calling this. For instance, for a /// floating display, first create an empty buffer using |nvim_create_buf()|, /// then display it using |nvim_open_win()|, and then call this function. -/// Then |nvim_chan_send()| cal be called immediately to process sequences +/// Then |nvim_chan_send()| can be called immediately to process sequences /// in a virtual terminal having the intended size. /// /// @param buffer the buffer to use (expected to be empty) @@ -1305,156 +1303,6 @@ void nvim_chan_send(Integer chan, String data, Error *err) } } -/// Open a new window. -/// -/// 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 -/// with multigrid GUIs, and are displayed as separate top-level windows. -/// -/// 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. -/// -/// 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 -/// corner. Fractional values are allowed, but the builtin implementation -/// (used by non-multigrid UIs) will always round down to nearest integer. -/// -/// Out-of-bounds values, and configurations that make the float not fit inside -/// the main editor, are allowed. The builtin implementation truncates values -/// so floats are fully within the main screen grid. External GUIs -/// could let floats hover outside of the main window like a tooltip, but -/// this should not be used to specify arbitrary WM screen positions. -/// -/// Example (Lua): window-relative float -/// <pre> -/// vim.api.nvim_open_win(0, false, -/// {relative='win', row=3, col=3, width=12, height=3}) -/// </pre> -/// -/// Example (Lua): buffer-relative float (travels as buffer is scrolled) -/// <pre> -/// vim.api.nvim_open_win(0, false, -/// {relative='win', width=12, height=3, bufpos={100,10}}) -/// </pre> -/// -/// @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: -/// - `relative`: Sets the window layout to "floating", placed at (row,col) -/// coordinates relative to: -/// - "editor" The global editor grid -/// - "win" Window given by the `win` field, or current window. -/// - "cursor" Cursor position in current window. -/// - `win`: |window-ID| for relative="win". -/// - `anchor`: Decides which corner of the float to place at (row,col): -/// - "NW" northwest (default) -/// - "NE" northeast -/// - "SW" southwest -/// - "SE" southeast -/// - `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). -/// - `row`: Row position in units of "screen cell height", may be fractional. -/// - `col`: Column position in units of "screen cell width", may be -/// fractional. -/// - `focusable`: Enable focus by user actions (wincmds, mouse events). -/// Defaults to true. Non-focusable windows can be entered by -/// |nvim_set_current_win()|. -/// - `external`: GUI should display the window as an external -/// top-level window. Currently accepts no other positioning -/// configuration together with this. -/// - `zindex`: Stacking order. floats with higher `zindex` go on top on -/// floats with lower indices. Must be larger than zero. The -/// following screen elements have hard-coded z-indices: -/// - 100: insert completion popupmenu -/// - 200: message scrollback -/// - 250: cmdline completion popupmenu (when wildoptions+=pum) -/// The default value for floats are 50. In general, values below 100 are -/// recommended, unless there is a good reason to overshadow builtin -/// elements. -/// - `style`: Configure the appearance of the window. Currently only takes -/// one non-empty value: -/// - "minimal" Nvim will display the window with many UI options -/// disabled. This is useful when displaying a temporary -/// float where the text should not be edited. Disables -/// 'number', 'relativenumber', 'cursorline', 'cursorcolumn', -/// 'foldcolumn', 'spell' and 'list' options. 'signcolumn' -/// is changed to `auto` and 'colorcolumn' is cleared. The -/// end-of-buffer region is hidden by setting `eob` flag of -/// 'fillchars' to a space char, and clearing the -/// |EndOfBuffer| region in 'winhighlight'. -/// - `border`: Style of (optional) window border. This can either be a string -/// or an array. The string values are -/// - "none": No border (default). -/// - "single": A single line box. -/// - "double": A double line box. -/// - "rounded": Like "single", but with rounded corners ("╭" etc.). -/// - "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 specifify 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 -/// [ "/", "-", "\\", "|" ], -/// 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 `VertSplit` -/// when not defined. It could also be specified by character: -/// [ {"+", "MyCorner"}, {"x", "MyBorder"} ]. -/// - `noautocmd`: If true then no buffer-related autocommand events such as -/// |BufEnter|, |BufLeave| or |BufWinEnter| may fire from -/// calling this function. -/// -/// @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_CHECK_TEXTLOCK -{ - FloatConfig fconfig = FLOAT_CONFIG_INIT; - if (!parse_float_config(config, &fconfig, false, true, err)) { - return 0; - } - win_T *wp = win_new_float(NULL, fconfig, err); - if (!wp) { - return 0; - } - if (enter) { - win_enter(wp, false); - } - // autocmds in win_enter or win_set_buf below may close the window - if (win_valid(wp) && buffer > 0) { - win_set_buf(wp->handle, buffer, fconfig.noautocmd, err); - } - if (!win_valid(wp)) { - api_set_error(err, kErrorTypeException, "Window was closed immediately"); - return 0; - } - - if (fconfig.style == kWinStyleMinimal) { - win_set_minimal_style(wp); - didset_window_options(wp); - } - return wp->handle; -} - /// Gets the current list of tabpage handles. /// /// @return List of tabpage handles diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c new file mode 100644 index 0000000000..d82e7e8a03 --- /dev/null +++ b/src/nvim/api/win_config.c @@ -0,0 +1,639 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +#include <assert.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include "nvim/api/private/defs.h" +#include "nvim/api/private/helpers.h" +#include "nvim/api/win_config.h" +#include "nvim/ascii.h" +#include "nvim/option.h" +#include "nvim/screen.h" +#include "nvim/strings.h" +#include "nvim/syntax.h" +#include "nvim/ui.h" +#include "nvim/window.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "api/win_config.c.generated.h" +#endif + + +/// Open a new window. +/// +/// 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 +/// with multigrid GUIs, and are displayed as separate top-level windows. +/// +/// 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. +/// +/// 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 +/// corner. Fractional values are allowed, but the builtin implementation +/// (used by non-multigrid UIs) will always round down to nearest integer. +/// +/// Out-of-bounds values, and configurations that make the float not fit inside +/// the main editor, are allowed. The builtin implementation truncates values +/// so floats are fully within the main screen grid. External GUIs +/// could let floats hover outside of the main window like a tooltip, but +/// this should not be used to specify arbitrary WM screen positions. +/// +/// Example (Lua): window-relative float +/// <pre> +/// vim.api.nvim_open_win(0, false, +/// {relative='win', row=3, col=3, width=12, height=3}) +/// </pre> +/// +/// Example (Lua): buffer-relative float (travels as buffer is scrolled) +/// <pre> +/// vim.api.nvim_open_win(0, false, +/// {relative='win', width=12, height=3, bufpos={100,10}}) +/// </pre> +/// +/// @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: +/// - `relative`: Sets the window layout to "floating", placed at (row,col) +/// coordinates relative to: +/// - "editor" The global editor grid +/// - "win" Window given by the `win` field, or current window. +/// - "cursor" Cursor position in current window. +/// - `win`: |window-ID| for relative="win". +/// - `anchor`: Decides which corner of the float to place at (row,col): +/// - "NW" northwest (default) +/// - "NE" northeast +/// - "SW" southwest +/// - "SE" southeast +/// - `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). +/// - `row`: Row position in units of "screen cell height", may be fractional. +/// - `col`: Column position in units of "screen cell width", may be +/// fractional. +/// - `focusable`: Enable focus by user actions (wincmds, mouse events). +/// Defaults to true. Non-focusable windows can be entered by +/// |nvim_set_current_win()|. +/// - `external`: GUI should display the window as an external +/// top-level window. Currently accepts no other positioning +/// configuration together with this. +/// - `zindex`: Stacking order. floats with higher `zindex` go on top on +/// floats with lower indices. Must be larger than zero. The +/// following screen elements have hard-coded z-indices: +/// - 100: insert completion popupmenu +/// - 200: message scrollback +/// - 250: cmdline completion popupmenu (when wildoptions+=pum) +/// The default value for floats are 50. In general, values below 100 are +/// recommended, unless there is a good reason to overshadow builtin +/// elements. +/// - `style`: Configure the appearance of the window. Currently only takes +/// one non-empty value: +/// - "minimal" Nvim will display the window with many UI options +/// disabled. This is useful when displaying a temporary +/// float where the text should not be edited. Disables +/// 'number', 'relativenumber', 'cursorline', 'cursorcolumn', +/// 'foldcolumn', 'spell' and 'list' options. 'signcolumn' +/// is changed to `auto` and 'colorcolumn' is cleared. The +/// end-of-buffer region is hidden by setting `eob` flag of +/// 'fillchars' to a space char, and clearing the +/// |EndOfBuffer| region in 'winhighlight'. +/// - `border`: Style of (optional) window border. This can either be a string +/// or an array. The string values are +/// - "none": No border (default). +/// - "single": A single line box. +/// - "double": A double line box. +/// - "rounded": Like "single", but with rounded corners ("╭" etc.). +/// - "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 specifify 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 +/// [ "/", "-", "\\", "|" ], +/// 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 `VertSplit` +/// when not defined. It could also be specified by character: +/// [ {"+", "MyCorner"}, {"x", "MyBorder"} ]. +/// - `noautocmd`: If true then no buffer-related autocommand events such as +/// |BufEnter|, |BufLeave| or |BufWinEnter| may fire from +/// calling this function. +/// +/// @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_CHECK_TEXTLOCK +{ + FloatConfig fconfig = FLOAT_CONFIG_INIT; + if (!parse_float_config(config, &fconfig, false, true, err)) { + return 0; + } + win_T *wp = win_new_float(NULL, fconfig, err); + if (!wp) { + return 0; + } + if (enter) { + win_enter(wp, false); + } + // autocmds in win_enter or win_set_buf below may close the window + if (win_valid(wp) && buffer > 0) { + win_set_buf(wp->handle, buffer, fconfig.noautocmd, err); + } + if (!win_valid(wp)) { + api_set_error(err, kErrorTypeException, "Window was closed immediately"); + return 0; + } + + if (fconfig.style == kWinStyleMinimal) { + win_set_minimal_style(wp); + didset_window_options(wp); + } + return wp->handle; +} + +/// Configures window layout. Currently only for floating and external windows +/// (including changing a split window to those layouts). +/// +/// When reconfiguring a floating window, absent option keys will not be +/// changed. `row`/`col` and `relative` must be reconfigured together. +/// +/// @see |nvim_open_win()| +/// +/// @param window Window handle, or 0 for current window +/// @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) + FUNC_API_SINCE(6) +{ + win_T *win = find_window_by_handle(window, err); + if (!win) { + return; + } + bool new_float = !win->w_floating; + // reuse old values, if not overridden + FloatConfig fconfig = new_float ? FLOAT_CONFIG_INIT : win->w_float_config; + + if (!parse_float_config(config, &fconfig, !new_float, false, err)) { + return; + } + if (new_float) { + if (!win_new_float(win, fconfig, err)) { + return; + } + redraw_later(win, NOT_VALID); + } else { + win_config_float(win, fconfig); + win->w_pos_changed = true; + } + if (fconfig.style == kWinStyleMinimal) { + win_set_minimal_style(win); + didset_window_options(win); + } +} + +/// Gets window configuration. +/// +/// The returned value may be given to |nvim_open_win()|. +/// +/// `relative` is empty for normal windows. +/// +/// @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) + FUNC_API_SINCE(6) +{ + Dictionary rv = ARRAY_DICT_INIT; + + win_T *wp = find_window_by_handle(window, err); + if (!wp) { + return rv; + } + + FloatConfig *config = &wp->w_float_config; + + PUT(rv, "focusable", BOOLEAN_OBJ(config->focusable)); + PUT(rv, "external", BOOLEAN_OBJ(config->external)); + + if (wp->w_floating) { + PUT(rv, "width", INTEGER_OBJ(config->width)); + PUT(rv, "height", INTEGER_OBJ(config->height)); + if (!config->external) { + if (config->relative == kFloatRelativeWindow) { + PUT(rv, "win", INTEGER_OBJ(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)); + } + } + PUT(rv, "anchor", STRING_OBJ(cstr_to_string(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)); + } + if (config->border) { + Array border = ARRAY_DICT_INIT; + for (size_t i = 0; i < 8; i++) { + Array tuple = ARRAY_DICT_INIT; + + String s = cstrn_to_string((const char *)config->border_chars[i], sizeof(schar_T)); + + int hi_id = config->border_hl_ids[i]; + char_u *hi_name = syn_id2name(hi_id); + if (hi_name[0]) { + ADD(tuple, STRING_OBJ(s)); + ADD(tuple, STRING_OBJ(cstr_to_string((const char *)hi_name))); + ADD(border, ARRAY_OBJ(tuple)); + } else { + ADD(border, STRING_OBJ(s)); + } + } + PUT(rv, "border", ARRAY_OBJ(border)); + } + } + + const char *rel = (wp->w_floating && !config->external + ? float_relative_str[config->relative] : ""); + PUT(rv, "relative", STRING_OBJ(cstr_to_string(rel))); + + return rv; +} + +static bool parse_float_anchor(String anchor, FloatAnchor *out) +{ + if (anchor.size == 0) { + *out = (FloatAnchor)0; + } + char *str = anchor.data; + if (striequal(str, "NW")) { + *out = 0; // NW is the default + } else if (striequal(str, "NE")) { + *out = kFloatAnchorEast; + } else if (striequal(str, "SW")) { + *out = kFloatAnchorSouth; + } else if (striequal(str, "SE")) { + *out = kFloatAnchorSouth | kFloatAnchorEast; + } else { + return false; + } + return true; +} + +static bool parse_float_relative(String relative, FloatRelative *out) +{ + char *str = relative.data; + if (striequal(str, "editor")) { + *out = kFloatRelativeEditor; + } else if (striequal(str, "win")) { + *out = kFloatRelativeWindow; + } else if (striequal(str, "cursor")) { + *out = kFloatRelativeCursor; + } else { + return false; + } + return true; +} + +static bool parse_float_bufpos(Array bufpos, lpos_T *out) +{ + if (bufpos.size != 2 + || bufpos.items[0].type != kObjectTypeInteger + || bufpos.items[1].type != kObjectTypeInteger) { + return false; + } + out->lnum = bufpos.items[0].data.integer; + out->col = (colnr_T)bufpos.items[1].data.integer; + return true; +} + +static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) +{ + struct { + const char *name; + schar_T chars[8]; + bool shadow_color; + } defaults[] = { + { "double", { "╔", "═", "╗", "║", "╝", "═", "╚", "║" }, false }, + { "single", { "┌", "─", "┐", "│", "┘", "─", "└", "│" }, false }, + { "shadow", { "", "", " ", " ", " ", " ", " ", "" }, true }, + { "rounded", { "╭", "─", "╮", "│", "╯", "─", "╰", "│" }, false }, + { "solid", { " ", " ", " ", " ", " ", " ", " ", " " }, false }, + { NULL, { { NUL } }, false }, + }; + + schar_T *chars = fconfig->border_chars; + int *hl_ids = fconfig->border_hl_ids; + + fconfig->border = true; + + if (style.type == kObjectTypeArray) { + 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"); + return; + } + for (size_t i = 0; i < size; i++) { + Object iytem = arr.items[i]; + String string; + int hl_id = 0; + if (iytem.type == kObjectTypeArray) { + Array iarr = iytem.data.array; + if (!iarr.size || iarr.size > 2) { + api_set_error(err, kErrorTypeValidation, "invalid border char"); + return; + } + if (iarr.items[0].type != kObjectTypeString) { + api_set_error(err, kErrorTypeValidation, "invalid border char"); + return; + } + string = iarr.items[0].data.string; + if (iarr.size == 2) { + hl_id = object_to_hl_id(iarr.items[1], "border char highlight", err); + if (ERROR_SET(err)) { + return; + } + } + } else if (iytem.type == kObjectTypeString) { + string = iytem.data.string; + } else { + api_set_error(err, kErrorTypeValidation, "invalid border char"); + return; + } + if (string.size + && mb_string2cells_len((char_u *)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); + if (len) { + memcpy(chars[i], string.data, len); + } + chars[i][len] = NUL; + hl_ids[i] = hl_id; + } + while (size < 8) { + memcpy(chars+size, chars, sizeof(*chars) * size); + memcpy(hl_ids+size, hl_ids, sizeof(*hl_ids) * size); + size <<= 1; + } + if ((chars[7][0] && chars[1][0] && !chars[0][0]) + || (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"); + } + } else if (style.type == kObjectTypeString) { + String str = style.data.string; + if (str.size == 0 || strequal(str.data, "none")) { + fconfig->border = false; + return; + } + for (size_t i = 0; defaults[i].name; i++) { + if (strequal(str.data, defaults[i].name)) { + memcpy(chars, defaults[i].chars, sizeof(defaults[i].chars)); + memset(hl_ids, 0, 8 * sizeof(*hl_ids)); + if (defaults[i].shadow_color) { + int hl_blend = SYN_GROUP_STATIC("FloatShadow"); + int hl_through = SYN_GROUP_STATIC("FloatShadowThrough"); + hl_ids[2] = hl_through; + hl_ids[3] = hl_blend; + hl_ids[4] = hl_blend; + hl_ids[5] = hl_blend; + hl_ids[6] = hl_through; + } + return; + } + } + api_set_error(err, kErrorTypeValidation, + "invalid border style \"%s\"", str.data); + } +} + +static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, bool reconf, + bool new_win, Error *err) +{ + bool has_relative = false, relative_is_win = false; + if (config->relative.type == kObjectTypeString) { + // ignore empty string, to match nvim_win_get_config + if (config->relative.data.string.size > 0) { + if (!parse_float_relative(config->relative.data.string, &fconfig->relative)) { + api_set_error(err, kErrorTypeValidation, "Invalid value of 'relative' key"); + return false; + } + + if (!(HAS_KEY(config->row) && HAS_KEY(config->col)) && !HAS_KEY(config->bufpos)) { + api_set_error(err, kErrorTypeValidation, + "'relative' requires 'row'/'col' or 'bufpos'"); + return false; + } + + has_relative = true; + fconfig->external = false; + if (fconfig->relative == kFloatRelativeWindow) { + relative_is_win = true; + fconfig->bufpos.lnum = -1; + } + } + } else if (HAS_KEY(config->relative)) { + api_set_error(err, kErrorTypeValidation, "'relative' key must be String"); + return false; + } + + if (config->anchor.type == kObjectTypeString) { + if (!parse_float_anchor(config->anchor.data.string, &fconfig->anchor)) { + api_set_error(err, kErrorTypeValidation, "Invalid value of 'anchor' key"); + return false; + } + } else if (HAS_KEY(config->anchor)) { + api_set_error(err, kErrorTypeValidation, "'anchor' key must be String"); + return false; + } + + if (HAS_KEY(config->row)) { + if (!has_relative) { + api_set_error(err, kErrorTypeValidation, "non-float cannot have 'row'"); + return false; + } else if (config->row.type == kObjectTypeInteger) { + fconfig->row = (double)config->row.data.integer; + } else if (config->row.type == kObjectTypeFloat) { + fconfig->row = config->row.data.floating; + } else { + api_set_error(err, kErrorTypeValidation, + "'row' key must be Integer or Float"); + return false; + } + } + + if (HAS_KEY(config->col)) { + if (!has_relative) { + api_set_error(err, kErrorTypeValidation, "non-float cannot have 'col'"); + return false; + } else if (config->col.type == kObjectTypeInteger) { + fconfig->col = (double)config->col.data.integer; + } else if (config->col.type == kObjectTypeFloat) { + fconfig->col = config->col.data.floating; + } else { + api_set_error(err, kErrorTypeValidation, + "'col' key must be Integer or Float"); + return false; + } + } + + if (HAS_KEY(config->bufpos)) { + if (!has_relative) { + api_set_error(err, kErrorTypeValidation, "non-float cannot have 'bufpos'"); + return false; + } else if (config->bufpos.type == kObjectTypeArray) { + if (!parse_float_bufpos(config->bufpos.data.array, &fconfig->bufpos)) { + api_set_error(err, kErrorTypeValidation, "Invalid value of 'bufpos' key"); + return false; + } + + if (!HAS_KEY(config->row)) { + fconfig->row = (fconfig->anchor & kFloatAnchorSouth) ? 0 : 1; + } + if (!HAS_KEY(config->col)) { + fconfig->col = 0; + } + } else { + api_set_error(err, kErrorTypeValidation, "'bufpos' key must be Array"); + return false; + } + } + + if (config->width.type == kObjectTypeInteger && config->width.data.integer > 0) { + fconfig->width = (int)config->width.data.integer; + } else if (HAS_KEY(config->width)) { + api_set_error(err, kErrorTypeValidation, "'width' key must be a positive Integer"); + return false; + } else if (!reconf) { + api_set_error(err, kErrorTypeValidation, "Must specify 'width'"); + return false; + } + + if (config->height.type == kObjectTypeInteger && config->height.data.integer > 0) { + fconfig->height = (int)config->height.data.integer; + } else if (HAS_KEY(config->height)) { + api_set_error(err, kErrorTypeValidation, "'height' key must be a positive Integer"); + return false; + } else if (!reconf) { + api_set_error(err, kErrorTypeValidation, "Must specify 'height'"); + return false; + } + + if (relative_is_win) { + fconfig->window = curwin->handle; + if (config->win.type == kObjectTypeInteger || config->win.type == kObjectTypeWindow) { + if (config->win.data.integer > 0) { + fconfig->window = (Window)config->win.data.integer; + } + } else if (HAS_KEY(config->win)) { + api_set_error(err, kErrorTypeValidation, "'win' key must be Integer or Window"); + return false; + } + } else { + if (HAS_KEY(config->win)) { + api_set_error(err, kErrorTypeValidation, "'win' key is only valid with relative='win'"); + return false; + } + } + + if (HAS_KEY(config->external)) { + fconfig->external = api_object_to_bool(config->external, "'external' key", false, err); + if (ERROR_SET(err)) { + return false; + } + if (has_relative && fconfig->external) { + api_set_error(err, kErrorTypeValidation, + "Only one of 'relative' and 'external' must be used"); + return false; + } + if (fconfig->external && !ui_has(kUIMultigrid)) { + 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(config->focusable)) { + fconfig->focusable = api_object_to_bool(config->focusable, "'focusable' key", false, err); + if (ERROR_SET(err)) { + return false; + } + } + + if (config->zindex.type == kObjectTypeInteger && config->zindex.data.integer > 0) { + fconfig->zindex = (int)config->zindex.data.integer; + } else if (HAS_KEY(config->zindex)) { + api_set_error(err, kErrorTypeValidation, "'zindex' key must be a positive Integer"); + return false; + } + + if (HAS_KEY(config->border)) { + parse_border_style(config->border, fconfig, err); + if (ERROR_SET(err)) { + return false; + } + } + + if (config->style.type == kObjectTypeString) { + if (config->style.data.string.data[0] == NUL) { + fconfig->style = kWinStyleUnused; + } else if (striequal(config->style.data.string.data, "minimal")) { + fconfig->style = kWinStyleMinimal; + } else { + api_set_error(err, kErrorTypeValidation, "Invalid value of 'style' key"); + } + } else if (HAS_KEY(config->style)) { + api_set_error(err, kErrorTypeValidation, "'style' key must be String"); + return false; + } + + if (HAS_KEY(config->noautocmd)) { + if (!new_win) { + api_set_error(err, kErrorTypeValidation, "Invalid key: 'noautocmd'"); + return false; + } + fconfig->noautocmd = api_object_to_bool(config->noautocmd, "'noautocmd' key", false, err); + if (ERROR_SET(err)) { + return false; + } + } + + return true; +} diff --git a/src/nvim/api/win_config.h b/src/nvim/api/win_config.h new file mode 100644 index 0000000000..9271c35f23 --- /dev/null +++ b/src/nvim/api/win_config.h @@ -0,0 +1,11 @@ +#ifndef NVIM_API_WIN_CONFIG_H +#define NVIM_API_WIN_CONFIG_H + +#include <stdint.h> + +#include "nvim/api/private/defs.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "api/win_config.h.generated.h" +#endif +#endif // NVIM_API_WIN_CONFIG_H diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 95eae1af2d..6e68c057dc 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -373,117 +373,6 @@ Boolean nvim_win_is_valid(Window window) } -/// Configures window layout. Currently only for floating and external windows -/// (including changing a split window to those layouts). -/// -/// When reconfiguring a floating window, absent option keys will not be -/// changed. `row`/`col` and `relative` must be reconfigured together. -/// -/// @see |nvim_open_win()| -/// -/// @param window Window handle, or 0 for current window -/// @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) - FUNC_API_SINCE(6) -{ - win_T *win = find_window_by_handle(window, err); - if (!win) { - return; - } - bool new_float = !win->w_floating; - // reuse old values, if not overridden - FloatConfig fconfig = new_float ? FLOAT_CONFIG_INIT : win->w_float_config; - - if (!parse_float_config(config, &fconfig, !new_float, false, err)) { - return; - } - if (new_float) { - if (!win_new_float(win, fconfig, err)) { - return; - } - redraw_later(win, NOT_VALID); - } else { - win_config_float(win, fconfig); - win->w_pos_changed = true; - } - if (fconfig.style == kWinStyleMinimal) { - win_set_minimal_style(win); - didset_window_options(win); - } -} - -/// Gets window configuration. -/// -/// The returned value may be given to |nvim_open_win()|. -/// -/// `relative` is empty for normal windows. -/// -/// @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) - FUNC_API_SINCE(6) -{ - Dictionary rv = ARRAY_DICT_INIT; - - win_T *wp = find_window_by_handle(window, err); - if (!wp) { - return rv; - } - - FloatConfig *config = &wp->w_float_config; - - PUT(rv, "focusable", BOOLEAN_OBJ(config->focusable)); - PUT(rv, "external", BOOLEAN_OBJ(config->external)); - - if (wp->w_floating) { - PUT(rv, "width", INTEGER_OBJ(config->width)); - PUT(rv, "height", INTEGER_OBJ(config->height)); - if (!config->external) { - if (config->relative == kFloatRelativeWindow) { - PUT(rv, "win", INTEGER_OBJ(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)); - } - } - PUT(rv, "anchor", STRING_OBJ(cstr_to_string(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)); - } - if (config->border) { - Array border = ARRAY_DICT_INIT; - for (size_t i = 0; i < 8; i++) { - Array tuple = ARRAY_DICT_INIT; - - String s = cstrn_to_string((const char *)config->border_chars[i], sizeof(schar_T)); - - int hi_id = config->border_hl_ids[i]; - char_u *hi_name = syn_id2name(hi_id); - if (hi_name[0]) { - ADD(tuple, STRING_OBJ(s)); - ADD(tuple, STRING_OBJ(cstr_to_string((const char *)hi_name))); - ADD(border, ARRAY_OBJ(tuple)); - } else { - ADD(border, STRING_OBJ(s)); - } - } - PUT(rv, "border", ARRAY_OBJ(border)); - } - } - - const char *rel = (wp->w_floating && !config->external - ? float_relative_str[config->relative] : ""); - PUT(rv, "relative", STRING_OBJ(cstr_to_string(rel))); - - return rv; -} - /// Closes the window and hide the buffer it contains (like |:hide| with a /// |window-ID|). /// diff --git a/src/nvim/charset.c b/src/nvim/charset.c index f899ebf57c..d2f95ad81c 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -412,7 +412,7 @@ char *transstr(const char *const s, bool untab) { // Compute the length of the result, taking account of unprintable // multi-byte characters. - const size_t len = transstr_len((const char *)s, untab) + 1; + const size_t len = transstr_len(s, untab) + 1; char *const buf = xmalloc(len); transstr_buf(s, buf, len, untab); return buf; diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 5c43b2498e..3a7bd21c70 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -1110,7 +1110,7 @@ static int diff_file(diffio_T *dio) // Build the diff command and execute it. Always use -a, binary // differences are of no use. Ignore errors, diff returns // non-zero when differences have been found. - vim_snprintf((char *)cmd, len, "diff %s%s%s%s%s%s%s%s %s", + vim_snprintf(cmd, len, "diff %s%s%s%s%s%s%s%s %s", diff_a_works == kFalse ? "" : "-a ", "", (diff_flags & DIFF_IWHITE) ? "-b " : "", diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 45edbec4a6..5ac733285d 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -116,7 +116,7 @@ static char *ctrl_x_msgs[] = static char *ctrl_x_mode_names[] = { "keyword", "ctrl_x", - "unknown", // CTRL_X_SCROLL + "scroll", "whole_line", "files", "tags", @@ -3329,7 +3329,7 @@ void get_complete_info(list_T *what_list, dict_T *retdict) // Return Insert completion mode name string static char_u *ins_compl_mode(void) { - if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET || compl_started) { + if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET || ctrl_x_mode == CTRL_X_SCROLL || compl_started) { return (char_u *)ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT]; } return (char_u *)""; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 768b82b464..a281c09042 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -514,8 +514,8 @@ int var_redir_start(char_u *name, int append) ga_init(&redir_ga, (int)sizeof(char), 500); // Parse the variable name (can be a dict or list entry). - redir_endp = (char_u *)get_lval(redir_varname, NULL, redir_lval, false, false, - 0, FNE_CHECK_START); + redir_endp = get_lval(redir_varname, NULL, redir_lval, false, false, + 0, FNE_CHECK_START); if (redir_endp == NULL || redir_lval->ll_name == NULL || *redir_endp != NUL) { clear_lval(redir_lval); @@ -597,8 +597,8 @@ void var_redir_stop(void) tv.vval.v_string = redir_ga.ga_data; // Call get_lval() again, if it's inside a Dict or List it may // have changed. - redir_endp = (char_u *)get_lval(redir_varname, NULL, redir_lval, - false, false, 0, FNE_CHECK_START); + redir_endp = get_lval(redir_varname, NULL, redir_lval, + false, false, 0, FNE_CHECK_START); if (redir_endp != NULL && redir_lval->ll_name != NULL) { set_var_lval(redir_lval, redir_endp, &tv, false, false, "."); } @@ -1711,7 +1711,7 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) if (tofree != NULL) { name = tofree; } - if (get_var_tv((const char *)name, len, &tv, NULL, true, false) + if (get_var_tv(name, len, &tv, NULL, true, false) == FAIL) { error = true; } else { @@ -2023,7 +2023,7 @@ char_u *get_lval(char_u *const name, typval_T *const rettv, lval_T *const lp, co } lp->ll_exp_name = (char *)make_expanded_name(name, expr_start, expr_end, - (char_u *)p); + p); lp->ll_name = lp->ll_exp_name; if (lp->ll_exp_name == NULL) { /* Report an invalid expression in braces, unless the @@ -2419,7 +2419,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, co // handle +=, -=, *=, /=, %= and .= di = NULL; - if (get_var_tv((const char *)lp->ll_name, (int)STRLEN(lp->ll_name), + if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name), &tv, &di, true, false) == OK) { if ((di == NULL || (!var_check_ro(di->di_flags, lp->ll_name, TV_CSTRING) @@ -3004,12 +3004,12 @@ int do_unlet(const char *const name, const size_t name_len, const bool forceit) hashitem_T *hi = hash_find(ht, (const char_u *)varname); if (HASHITEM_EMPTY(hi)) { - hi = find_hi_in_scoped_ht((const char *)name, &ht); + hi = find_hi_in_scoped_ht(name, &ht); } if (hi != NULL && !HASHITEM_EMPTY(hi)) { dictitem_T *const di = TV_DICT_HI2DI(hi); - if (var_check_fixed(di->di_flags, (const char *)name, TV_CSTRING) - || var_check_ro(di->di_flags, (const char *)name, TV_CSTRING) + if (var_check_fixed(di->di_flags, name, TV_CSTRING) + || var_check_ro(di->di_flags, name, TV_CSTRING) || var_check_lock(d->dv_lock, name, TV_CSTRING)) { return FAIL; } @@ -3069,7 +3069,7 @@ static int do_lock_var(lval_T *lp, char_u *name_end FUNC_ATTR_UNUSED, exarg_T *e ret = FAIL; } else { // Normal name or expanded name. - dictitem_T *const di = find_var((const char *)lp->ll_name, lp->ll_name_len, NULL, + dictitem_T *const di = find_var(lp->ll_name, lp->ll_name_len, NULL, true); if (di == NULL) { ret = FAIL; @@ -4348,7 +4348,7 @@ static int call_func_rettv(char_u **const arg, typval_T *const rettv, const bool funcexe.selfdict = selfdict; funcexe.basetv = basetv; const int ret = get_func_tv(funcname, is_lua ? *arg - funcname : -1, rettv, - (char_u **)arg, &funcexe); + arg, &funcexe); // Clear the funcref afterwards, so that deleting it while // evaluating the arguments is possible (see test55). @@ -9444,7 +9444,7 @@ static void set_var_const(const char *name, const size_t name_len, typval_T *con // Search in parent scope which is possible to reference from lambda if (v == NULL) { - v = find_var_in_scoped_ht((const char *)name, name_len, true); + v = find_var_in_scoped_ht(name, name_len, true); } if (tv_is_func(*tv) && !var_check_func_name(name, v == NULL)) { @@ -9654,7 +9654,7 @@ bool var_check_func_name(const char *const name, const bool new_var) // Don't allow hiding a function. When "v" is not NULL we might be // assigning another function to the same var, the type is checked // below. - if (new_var && function_exists((const char *)name, false)) { + if (new_var && function_exists(name, false)) { EMSG2(_("E705: Variable name conflicts with existing function: %s"), name); return false; @@ -11325,7 +11325,7 @@ bool var_exists(const char *var) // get_name_len() takes care of expanding curly braces const char *name = var; - const int len = get_name_len((const char **)&var, &tofree, true, false); + const int len = get_name_len(&var, &tofree, true, false); if (len > 0) { typval_T tv; diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 1c0afc89f5..5ef0045659 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -268,9 +268,8 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s || TV_LIST_ITEM_TV(state->li)->vval.v_string != NULL); for (size_t i = state->offset; i < state->li_length && p < buf_end; i++) { assert(TV_LIST_ITEM_TV(state->li)->vval.v_string != NULL); - const char ch = (char)( - TV_LIST_ITEM_TV(state->li)->vval.v_string[state->offset++]); - *p++ = (char)((char)ch == (char)NL ? (char)NUL : (char)ch); + const char ch = (char)(TV_LIST_ITEM_TV(state->li)->vval.v_string[state->offset++]); + *p++ = (char)(ch == (char)NL ? (char)NUL : ch); } if (p < buf_end) { state->li = TV_LIST_ITEM_NEXT(state->list, state->li); diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 5569d74413..50e0b0b258 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -5149,7 +5149,7 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_trailing)); } else { if (lv.ll_tv == NULL) { - di = find_var((const char *)lv.ll_name, lv.ll_name_len, NULL, true); + di = find_var(lv.ll_name, lv.ll_name_len, NULL, true); if (di != NULL) { // Consider a variable locked when: // 1. the variable itself is locked @@ -5984,7 +5984,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr) FUNC_ATTR_NONNULL_ALL { - const char *const str = (const char *)tv_get_string_chk(&argvars[0]); + const char *const str = tv_get_string_chk(&argvars[0]); if (str == NULL) { return; } @@ -11358,7 +11358,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (new_cwd && *new_cwd != NUL) { cwd = new_cwd; // The new cwd must be a directory. - if (!os_isdir_executable((const char *)cwd)) { + if (!os_isdir_executable(cwd)) { EMSG2(_(e_invarg2), "expected valid directory"); shell_free_argv(argv); return; diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 075b50a366..4c8789964f 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -3260,7 +3260,7 @@ const char *tv_get_string(const typval_T *const tv) const char *tv_get_string_buf(const typval_T *const tv, char *const buf) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { - const char *const res = (const char *)tv_get_string_buf_chk(tv, buf); + const char *const res = tv_get_string_buf_chk(tv, buf); return res != NULL ? res : ""; } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index a9990df58f..e0ff9bab3a 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -3037,7 +3037,7 @@ static void ui_ext_cmdline_show(CmdlineInfo *line) line->cmdindent, line->level); if (line->special_char) { - ui_call_cmdline_special_char(cchar_to_string((char)(line->special_char)), + ui_call_cmdline_special_char(cchar_to_string(line->special_char), line->special_shift, line->level); } @@ -3135,7 +3135,7 @@ void putcmdline(char c, int shift) } msg_no_more = false; } else if (ccline.redraw_state != kCmdRedrawAll) { - ui_call_cmdline_special_char(cchar_to_string((char)(c)), shift, + ui_call_cmdline_special_char(cchar_to_string(c), shift, ccline.level); } cursorcmd(); diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index 6ed2e5dab8..e38a0af0ec 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -33,6 +33,10 @@ local function_names = {} local c_grammar = require('generators.c_grammar') +local function startswith(String,Start) + return string.sub(String,1,string.len(Start))==Start +end + -- read each input file, parse and append to the api metadata for i = 6, #arg do local full_path = arg[i] @@ -47,7 +51,8 @@ for i = 6, #arg do local tmp = c_grammar.grammar:match(input:read('*all')) for j = 1, #tmp do local fn = tmp[j] - if not fn.noexport then + local public = startswith(fn.name, "nvim_") or fn.deprecated_since + if public and not fn.noexport then functions[#functions + 1] = tmp[j] function_names[fn.name] = true if #fn.parameters ~= 0 and fn.parameters[1][2] == 'channel_id' then @@ -76,10 +81,6 @@ local function shallowcopy(orig) return copy end -local function startswith(String,Start) - return string.sub(String,1,string.len(Start))==Start -end - -- Export functions under older deprecated names. -- These will be removed eventually. local deprecated_aliases = require("api.dispatch_deprecated") @@ -108,9 +109,10 @@ for _,f in ipairs(shallowcopy(functions)) do f.lua = f.lua_only or not f.remote_only f.eval = (not f.lua_only) and (not f.remote_only) else + f.deprecated_since = tonumber(f.deprecated_since) + assert(f.deprecated_since == 1) f.remote = true f.since = 0 - f.deprecated_since = 1 end f.method = ismethod local newname = deprecated_aliases[f.name] diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 15acd73aa5..f5f8a307d1 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -523,7 +523,7 @@ void restoreRedobuff(save_redo_T *save_redo) void AppendToRedobuff(const char *s) { if (!block_redo) { - add_buff(&redobuff, (const char *)s, -1L); + add_buff(&redobuff, s, -1L); } } @@ -2861,7 +2861,7 @@ int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T } char_u *lhs = (char_u *)&args->lhs; - char_u *rhs = (char_u *)args->rhs; + char_u *rhs = args->rhs; char_u *orig_rhs = args->orig_rhs; // check arguments and translate function keys diff --git a/src/nvim/main.c b/src/nvim/main.c index f801351d2d..069c253bff 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -345,7 +345,8 @@ int main(int argc, char **argv) // Reset 'loadplugins' for "-u NONE" before "--cmd" arguments. // Allows for setting 'loadplugins' there. if (params.use_vimrc != NULL && strequal(params.use_vimrc, "NONE")) { - p_lpl = false; + // When using --clean we still want to load plugins + p_lpl = params.clean; } // Execute --cmd arguments. diff --git a/src/nvim/message.c b/src/nvim/message.c index ed673b52d3..edde51e770 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -3191,7 +3191,7 @@ static void redir_write(const char *const str, const ptrdiff_t maxlen) size_t len = maxlen == -1 ? STRLEN(s) : (size_t)maxlen; if (capture_ga) { - ga_concat_len(capture_ga, (const char *)str, len); + ga_concat_len(capture_ga, str, len); } if (redir_reg) { write_reg_contents(redir_reg, s, len, true); diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index bbf70a4830..9675cfbb0f 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -134,7 +134,7 @@ bool os_isdir(const char_u *name) bool os_isdir_executable(const char *name) FUNC_ATTR_NONNULL_ALL { - int32_t mode = os_getperm((const char *)name); + int32_t mode = os_getperm(name); if (mode < 0) { return false; } diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index eb0ba874f4..046e6dbd12 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1823,7 +1823,7 @@ static int qf_add_entry(qf_list_T *qfl, char_u *dir, char_u *fname, char_u *modu if (type != 1 && !vim_isprintc(type)) { // only printable chars allowed type = 0; } - qfp->qf_type = (char_u)type; + qfp->qf_type = type; qfp->qf_valid = valid; lastp = &qfl->qf_last; diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index e1669a8c19..0e2d23e69b 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -335,7 +335,7 @@ int do_in_path_and_pp(char_u *path, char_u *name, int flags, DoInRuntimepathCB c static void push_path(RuntimeSearchPath *search_path, Map(String, handle_T) *rtp_used, char *entry, bool after) { - handle_T h = map_get(String, handle_T)(rtp_used, cstr_as_string((char *)entry)); + handle_T h = map_get(String, handle_T)(rtp_used, cstr_as_string(entry)); if (h == 0) { char *allocated = xstrdup(entry); map_put(String, handle_T)(rtp_used, cstr_as_string(allocated), 1); @@ -776,7 +776,7 @@ static bool pack_has_entries(char_u *buf) { int num_files; char_u **files; - char_u *(pat[]) = { (char_u *)buf }; + char_u *(pat[]) = { buf }; if (gen_expand_wildcards(1, pat, &num_files, &files, EW_DIR) == OK) { FreeWild(num_files, files); } diff --git a/src/nvim/shada.c b/src/nvim/shada.c index bf245bec51..2d65db8437 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -65,25 +65,10 @@ KHASH_SET_INIT_INT64(bufset) KHASH_MAP_INIT_STR(fnamebufs, buf_T *) KHASH_SET_INIT_STR(strset) -#define copy_option_part(src, dest, ...) \ - ((char *)copy_option_part((char_u **)src, (char_u *)dest, __VA_ARGS__)) #define find_shada_parameter(...) \ ((const char *)find_shada_parameter(__VA_ARGS__)) -#define home_replace_save(a, b) \ - ((char *)home_replace_save(a, (char_u *)b)) -#define home_replace(a, b, c, d, e) \ - home_replace(a, (char_u *)b, (char_u *)c, d, e) -#define vim_rename(a, b) \ - (vim_rename((char_u *)a, (char_u *)b)) -#define mb_strnicmp(a, b, c) \ - (mb_strnicmp((char_u *)a, (char_u *)b, c)) #define path_try_shorten_fname(b) \ ((char *)path_try_shorten_fname((char_u *)b)) -#define buflist_new(ffname, sfname, ...) \ - (buflist_new((char_u *)ffname, (char_u *)sfname, __VA_ARGS__)) -#define os_isdir(f) (os_isdir((char_u *)f)) -#define regtilde(s, m) ((char *)regtilde((char_u *)s, m)) -#define path_tail_with_sep(f) ((char *)path_tail_with_sep((char_u *)f)) #define SEARCH_KEY_MAGIC "sm" #define SEARCH_KEY_SMARTCASE "sc" @@ -1271,7 +1256,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) // string is close to useless: you can only use it with :& or :~ and // that’s all because s//~ is not available until the first call to // regtilde. Vim was not calling this for some reason. - (void)regtilde(cur_entry.data.sub_string.sub, p_magic); + (void)(char *)regtilde((char_u *)cur_entry.data.sub_string.sub, p_magic); // Do not free shada entry: its allocated memory was saved above. break; case kSDItemHistoryEntry: @@ -1362,8 +1347,9 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) case kSDItemBufferList: for (size_t i = 0; i < cur_entry.data.buffer_list.size; i++) { char *const sfname = path_try_shorten_fname(cur_entry.data.buffer_list.buffers[i].fname); - buf_T *const buf = buflist_new(cur_entry.data.buffer_list.buffers[i].fname, sfname, 0, - BLN_LISTED); + buf_T *const buf = + buflist_new((char_u *)cur_entry.data.buffer_list.buffers[i].fname, (char_u *)sfname, 0, + BLN_LISTED); if (buf != NULL) { RESET_FMARK(&buf->b_last_cursor, cur_entry.data.buffer_list.buffers[i].pos, 0); @@ -2694,7 +2680,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef .timestamp = sub.timestamp, .data = { .sub_string = { - .sub = (char *)sub.sub, + .sub = sub.sub, .additional_elements = sub.additional_elements, } } @@ -3037,11 +3023,11 @@ shada_write_file_open: {} } if (nomerge) { shada_write_file_nomerge: {} - char *const tail = path_tail_with_sep(fname); + char *const tail = (char *)path_tail_with_sep((char_u *)fname); if (tail != fname) { const char tail_save = *tail; *tail = NUL; - if (!os_isdir(fname)) { + if (!os_isdir((char_u *)fname)) { int ret; char *failed_dir; if ((ret = os_mkdir_recurse(fname, 0700, &failed_dir)) != 0) { @@ -3092,7 +3078,7 @@ shada_write_file_nomerge: {} // overwrite a user’s viminfo file after a "su root", with a // viminfo file that the user can't read. FileInfo old_info; - if (os_fileinfo((char *)fname, &old_info)) { + if (os_fileinfo(fname, &old_info)) { if (getuid() == ROOT_UID) { if (old_info.stat.st_uid != ROOT_UID || old_info.stat.st_gid != getgid()) { @@ -3116,7 +3102,7 @@ shada_write_file_nomerge: {} } } #endif - if (vim_rename(tempname, fname) == -1) { + if (vim_rename((char_u *)tempname, (char_u *)fname) == -1) { EMSG3(_(RNERR "Can't rename ShaDa file from %s to %s!"), tempname, fname); } else { @@ -4015,13 +4001,13 @@ static bool shada_removable(const char *name) char part[MAXPATHL + 1]; bool retval = false; - char *new_name = home_replace_save(NULL, name); + char *new_name = (char *)home_replace_save(NULL, (char_u *)name); for (p = (char *)p_shada; *p; ) { - (void)copy_option_part(&p, part, ARRAY_SIZE(part), ", "); + (void)(char *)copy_option_part((char_u **)&p, (char_u *)part, ARRAY_SIZE(part), ", "); if (part[0] == 'r') { - home_replace(NULL, part + 1, NameBuff, MAXPATHL, true); + home_replace(NULL, (char_u *)(part + 1), (char_u *)NameBuff, MAXPATHL, true); size_t n = STRLEN(NameBuff); - if (mb_strnicmp(NameBuff, new_name, n) == 0) { + if (mb_strnicmp((char_u *)NameBuff, (char_u *)new_name, n) == 0) { retval = true; break; } diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 3e56ad561b..450ec891ad 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -6073,7 +6073,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) wordlen = 0; for (const char_u *s = inword; *s != NUL; ) { const char_u *t = s; - c = mb_cptr2char_adv((const char_u **)&s); + c = mb_cptr2char_adv(&s); if (slang->sl_rem_accents) { if (utf_class(c) == 0) { if (did_white) { diff --git a/src/nvim/state.c b/src/nvim/state.c index a9f3d67849..04271d750c 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -161,6 +161,11 @@ char *get_mode(void) if (State & VREPLACE_FLAG) { buf[0] = 'R'; buf[1] = 'v'; + if (ins_compl_active()) { + buf[2] = 'c'; + } else if (ctrl_x_mode_not_defined_yet()) { + buf[2] = 'x'; + } } else { if (State & REPLACE_FLAG) { buf[0] = 'R'; diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index d2c94d9fe8..49f54dcfe1 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -6217,6 +6217,26 @@ static const char *highlight_init_both[] = { "default link Delimiter Special", "default link SpecialComment Special", "default link Debug Special", + "default DiagnosticError ctermfg=1 guifg=Red", + "default DiagnosticWarn ctermfg=3 guifg=Orange", + "default DiagnosticInfo ctermfg=4 guifg=LightBlue", + "default DiagnosticHint ctermfg=7 guifg=LightGrey", + "default DiagnosticUnderlineError cterm=underline gui=underline guisp=Red", + "default DiagnosticUnderlineWarn cterm=underline gui=underline guisp=Orange", + "default DiagnosticUnderlineInfo cterm=underline gui=underline guisp=LightBlue", + "default DiagnosticUnderlineHint cterm=underline gui=underline guisp=LightGrey", + "default link DiagnosticVirtualTextError DiagnosticError", + "default link DiagnosticVirtualTextWarn DiagnosticWarn", + "default link DiagnosticVirtualTextInfo DiagnosticInfo", + "default link DiagnosticVirtualTextHint DiagnosticHint", + "default link DiagnosticFloatingError DiagnosticError", + "default link DiagnosticFloatingWarn DiagnosticWarn", + "default link DiagnosticFloatingInfo DiagnosticInfo", + "default link DiagnosticFloatingHint DiagnosticHint", + "default link DiagnosticSignError DiagnosticError", + "default link DiagnosticSignWarn DiagnosticWarn", + "default link DiagnosticSignInfo DiagnosticInfo", + "default link DiagnosticSignHint DiagnosticHint", NULL }; diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 9eb64f68ef..4a9cb4a8d8 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -259,7 +259,7 @@ let s:filename_checks = { \ 'jgraph': ['file.jgr'], \ 'jovial': ['file.jov', 'file.j73', 'file.jovial'], \ 'jproperties': ['file.properties', 'file.properties_xx', 'file.properties_xx_xx', 'some.properties_xx_xx_file'], - \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb'], + \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.babelrc', '.eslintrc', '.prettierrc', '.firebaserc'], \ 'jsonc': ['file.jsonc'], \ 'jsp': ['file.jsp'], \ 'julia': ['file.jl'], diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index e82fefc7fc..366615821c 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -585,6 +585,8 @@ func Test_mode() exe "normal iabc\<C-X>\<C-L>\<F2>\<Esc>u" call assert_equal('i-ic', g:current_modes) + exe "normal R\<F2>\<Esc>" + call assert_equal('R-R', g:current_modes) " R_CTRL-P: Multiple matches exe "normal RBa\<C-P>\<F2>\<Esc>u" call assert_equal('R-Rc', g:current_modes) @@ -619,6 +621,42 @@ func Test_mode() exe "normal Rabc\<C-X>\<C-L>\<F2>\<Esc>u" call assert_equal('R-Rc', g:current_modes) + exe "normal gR\<F2>\<Esc>" + call assert_equal('R-Rv', g:current_modes) + " gR_CTRL-P: Multiple matches + exe "normal gRBa\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rvc', g:current_modes) + " gR_CTRL-P: Single match + exe "normal gRBro\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rvc', g:current_modes) + " gR_CTRL-X + exe "normal gRBa\<C-X>\<F2>\<Esc>u" + call assert_equal('R-Rvx', g:current_modes) + " gR_CTRL-X CTRL-P: Multiple matches + exe "normal gRBa\<C-X>\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rvc', g:current_modes) + " gR_CTRL-X CTRL-P: Single match + exe "normal gRBro\<C-X>\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rvc', g:current_modes) + " gR_CTRL-X CTRL-P + CTRL-P: Single match + exe "normal gRBro\<C-X>\<C-P>\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rvc', g:current_modes) + " gR_CTRL-X CTRL-L: Multiple matches + exe "normal gR\<C-X>\<C-L>\<F2>\<Esc>u" + call assert_equal('R-Rvc', g:current_modes) + " gR_CTRL-X CTRL-L: Single match + exe "normal gRBlu\<C-X>\<C-L>\<F2>\<Esc>u" + call assert_equal('R-Rvc', g:current_modes) + " gR_CTRL-P: No match + exe "normal gRCom\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rvc', g:current_modes) + " gR_CTRL-X CTRL-P: No match + exe "normal gRCom\<C-X>\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rvc', g:current_modes) + " gR_CTRL-X CTRL-L: No match + exe "normal gRabc\<C-X>\<C-L>\<F2>\<Esc>u" + call assert_equal('R-Rvc', g:current_modes) + call assert_equal('n', mode(0)) call assert_equal('n', mode(1)) diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim index 710450293c..eb367cfe5c 100644 --- a/src/nvim/testdir/test_popup.vim +++ b/src/nvim/testdir/test_popup.vim @@ -950,6 +950,10 @@ func Test_popup_complete_info_01() \ ["\<C-X>", 'ctrl_x'], \ ["\<C-X>\<C-N>", 'keyword'], \ ["\<C-X>\<C-P>", 'keyword'], + \ ["\<C-X>\<C-E>", 'scroll'], + \ ["\<C-X>\<C-Y>", 'scroll'], + \ ["\<C-X>\<C-E>\<C-E>\<C-Y>", 'scroll'], + \ ["\<C-X>\<C-Y>\<C-E>\<C-Y>", 'scroll'], \ ["\<C-X>\<C-L>", 'whole_line'], \ ["\<C-X>\<C-F>", 'files'], \ ["\<C-X>\<C-]>", 'tags'], diff --git a/src/nvim/undo.c b/src/nvim/undo.c index af214815f8..d5857ff8b9 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -1407,7 +1407,7 @@ void u_read_undo(char *name, const char_u *hash, const char_u *orig_name FUNC_AT FileInfo file_info_orig; FileInfo file_info_undo; if (os_fileinfo((const char *)orig_name, &file_info_orig) - && os_fileinfo((char *)file_name, &file_info_undo) + && os_fileinfo(file_name, &file_info_undo) && file_info_orig.stat.st_uid != file_info_undo.stat.st_uid && file_info_undo.stat.st_uid != getuid()) { if (p_verbose > 0) { @@ -1420,7 +1420,7 @@ void u_read_undo(char *name, const char_u *hash, const char_u *orig_name FUNC_AT } #endif } else { - file_name = (char *)name; + file_name = name; } if (p_verbose > 0) { diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index a066cfbc10..8651a38025 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -237,29 +237,34 @@ describe('lua stdlib', function() end) it("vim.split", function() - local split = function(str, sep, plain) - return exec_lua('return vim.split(...)', str, sep, plain) + local split = function(str, sep, kwargs) + return exec_lua('return vim.split(...)', str, sep, kwargs) end local tests = { - { "a,b", ",", false, { 'a', 'b' } }, - { ":aa::bb:", ":", false, { '', 'aa', '', 'bb', '' } }, - { "::ee::ff:", ":", false, { '', '', 'ee', '', 'ff', '' } }, - { "ab", ".", false, { '', '', '' } }, - { "a1b2c", "[0-9]", false, { 'a', 'b', 'c' } }, - { "xy", "", false, { 'x', 'y' } }, - { "here be dragons", " ", false, { "here", "be", "dragons"} }, - { "axaby", "ab?", false, { '', 'x', 'y' } }, - { "f v2v v3v w2w ", "([vw])2%1", false, { 'f ', ' v3v ', ' ' } }, - { "", "", false, {} }, - { "", "a", false, { '' } }, - { "x*yz*oo*l", "*", true, { 'x', 'yz', 'oo', 'l' } }, + { "a,b", ",", false, false, { 'a', 'b' } }, + { ":aa::bb:", ":", false, false, { '', 'aa', '', 'bb', '' } }, + { ":aa::bb:", ":", false, true, { 'aa', '', 'bb' } }, + { "::ee::ff:", ":", false, false, { '', '', 'ee', '', 'ff', '' } }, + { "::ee::ff:", ":", false, true, { 'ee', '', 'ff' } }, + { "ab", ".", false, false, { '', '', '' } }, + { "a1b2c", "[0-9]", false, false, { 'a', 'b', 'c' } }, + { "xy", "", false, false, { 'x', 'y' } }, + { "here be dragons", " ", false, false, { "here", "be", "dragons"} }, + { "axaby", "ab?", false, false, { '', 'x', 'y' } }, + { "f v2v v3v w2w ", "([vw])2%1", false, false, { 'f ', ' v3v ', ' ' } }, + { "", "", false, false, {} }, + { "", "a", false, false, { '' } }, + { "x*yz*oo*l", "*", true, false, { 'x', 'yz', 'oo', 'l' } }, } for _, t in ipairs(tests) do - eq(t[4], split(t[1], t[2], t[3])) + eq(t[5], split(t[1], t[2], {plain=t[3], trimempty=t[4]})) end + -- Test old signature + eq({'x', 'yz', 'oo', 'l'}, split("x*yz*oo*l", "*", true)) + local loops = { { "abc", ".-" }, } @@ -283,9 +288,8 @@ describe('lua stdlib', function() vim/shared.lua:0: in function <vim/shared.lua:0>]]), pcall_err(split, 'string', 1)) eq(dedent([[ - Error executing lua: vim/shared.lua:0: plain: expected boolean, got number + Error executing lua: vim/shared.lua:0: kwargs: expected table, got number stack traceback: - vim/shared.lua:0: in function 'gsplit' vim/shared.lua:0: in function <vim/shared.lua:0>]]), pcall_err(split, 'string', 'string', 1)) end) |