diff options
91 files changed, 1447 insertions, 831 deletions
diff --git a/ci/build.ps1 b/ci/build.ps1 index 08fc76393d..dbc43aecf3 100644 --- a/ci/build.ps1 +++ b/ci/build.ps1 @@ -91,7 +91,14 @@ if ($compiler -eq 'MINGW') { & C:\msys64\usr\bin\mkdir -p /var/cache/pacman/pkg # Build third-party dependencies - C:\msys64\usr\bin\bash -lc "pacman --verbose --noconfirm -Su" ; exitIfFailed + C:\msys64\usr\bin\bash -lc "curl -O http://repo.msys2.org/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz" ; exitIfFailed + C:\msys64\usr\bin\bash -lc "curl -O http://repo.msys2.org/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig" ; exitIfFailed + C:\msys64\usr\bin\bash -lc "pacman-key --verify msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig" ; exitIfFailed + C:\msys64\usr\bin\bash -lc "pacman --verbose --noconfirm -U msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz" ; exitIfFailed + # If there are still processes using msys-2.0.dll, after the base system update is finished, it will wait for input from the user. + # To prevent this, we will terminate all processes that use msys-2.0.dll. + Get-Process | Where-Object { $_.path -like 'C:\msys64*' } | Stop-Process + C:\msys64\usr\bin\bash -lc "pacman --verbose --noconfirm -Syu" ; exitIfFailed C:\msys64\usr\bin\bash -lc "pacman --verbose --noconfirm --needed -S $mingwPackages" ; exitIfFailed } elseif ($compiler -eq 'MSVC') { diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index 275d18a5a9..c2195fa02d 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -91,19 +91,19 @@ function! provider#clipboard#Executable() abort let s:paste['*'] = s:paste['+'] let s:cache_enabled = 0 return 'pbcopy' - elseif exists('$WAYLAND_DISPLAY') && executable('wl-copy') && executable('wl-paste') + elseif !empty($WAYLAND_DISPLAY) && executable('wl-copy') && executable('wl-paste') let s:copy['+'] = ['wl-copy', '--foreground', '--type', 'text/plain'] let s:paste['+'] = ['wl-paste', '--no-newline'] let s:copy['*'] = ['wl-copy', '--foreground', '--primary', '--type', 'text/plain'] let s:paste['*'] = ['wl-paste', '--no-newline', '--primary'] return 'wl-copy' - elseif exists('$DISPLAY') && executable('xclip') + elseif !empty($DISPLAY) && executable('xclip') let s:copy['+'] = ['xclip', '-quiet', '-i', '-selection', 'clipboard'] let s:paste['+'] = ['xclip', '-o', '-selection', 'clipboard'] let s:copy['*'] = ['xclip', '-quiet', '-i', '-selection', 'primary'] let s:paste['*'] = ['xclip', '-o', '-selection', 'primary'] return 'xclip' - elseif exists('$DISPLAY') && executable('xsel') && s:cmd_ok('xsel -o -b') + elseif !empty($DISPLAY) && executable('xsel') && s:cmd_ok('xsel -o -b') let s:copy['+'] = ['xsel', '--nodetach', '-i', '-b'] let s:paste['+'] = ['xsel', '-o', '-b'] let s:copy['*'] = ['xsel', '--nodetach', '-i', '-p'] @@ -132,7 +132,7 @@ function! provider#clipboard#Executable() abort let s:copy['*'] = s:copy['+'] let s:paste['*'] = s:paste['+'] return 'win32yank' - elseif exists('$TMUX') && executable('tmux') + elseif !empty($TMUX) && executable('tmux') let s:copy['+'] = ['tmux', 'load-buffer', '-'] let s:paste['+'] = ['tmux', 'save-buffer', '-'] let s:copy['*'] = s:copy['+'] diff --git a/runtime/doc/indent.txt b/runtime/doc/indent.txt index 1df0331239..f2278f8453 100644 --- a/runtime/doc/indent.txt +++ b/runtime/doc/indent.txt @@ -566,9 +566,15 @@ The examples below assume a 'shiftwidth' of 4. with "#" does not work. + PN When N is non-zero recognize C pragmas, and indent them like any + other code; does not concern other preprocessor directives. + When N is zero (default): don't recognize C pragmas, treating + them like every other preprocessor directive. + + The defaults, spelled out in full, are: cinoptions=>s,e0,n0,f0,{0,}0,^0,L-1,:s,=s,l0,b0,gs,hs,N0,E0,ps,ts,is,+s, - c3,C0,/0,(2s,us,U0,w0,W0,k0,m0,j0,J0,)20,*70,#0 + c3,C0,/0,(2s,us,U0,w0,W0,k0,m0,j0,J0,)20,*70,#0,P0 Vim puts a line in column 1 if: - It starts with '#' (preprocessor directives), if 'cinkeys' contains '#0'. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 448df31798..b83d2c4484 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2918,6 +2918,8 @@ A jump table for the options with a short description can be found at |Q_op|. *'go-c'* 'c' Use console dialogs instead of popup dialogs for simple choices. + *'go-d'* + 'd' Use dark theme variant if available. *'go-e'* 'e' Add tab pages when indicated with 'showtabline'. 'guitablabel' can be used to change the text in the labels. @@ -5795,7 +5797,7 @@ A jump table for the options with a short description can be found at |Q_op|. normal text. Each status line item is of the form: %-0{minwid}.{maxwid}{item} All fields except the {item} are optional. A single percent sign can - be given as "%%". Up to 80 items can be specified. *E541* + be given as "%%". When the option starts with "%!" then it is used as an expression, evaluated and the result is used as the option value. Example: > diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index 7f644486f7..aaf13d1640 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -136,6 +136,15 @@ tsnode:has_error() *tsnode:has_error()* tsnode:sexpr() *tsnode:sexpr()* Get an S-expression representing the node as a string. +tsnode:id() *tsnode:id()* + Get an unique identier for the node inside its own tree. + + No guarantees are made about this identifer's internal representation, + except for being a primitive lua type with value equality (so not a table). + Presently it is a (non-printable) string. + + NB: the id is not guaranteed to be unique for nodes from different trees. + tsnode:descendant_for_range({start_row}, {start_col}, {end_row}, {end_col}) *tsnode:descendant_for_range()* Get the smallest node within this node that spans the given range of diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 2773f59b45..70862320c5 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -632,15 +632,18 @@ function protocol.make_client_capabilities() codeActionLiteralSupport = { codeActionKind = { - valueSet = {}; + valueSet = vim.tbl_values(protocol.CodeActionKind); }; }; }; completion = { dynamicRegistration = false; completionItem = { + -- Until we can actually expand snippet, move cursor and allow for true snippet experience, + -- this should be disabled out of the box. + -- However, users can turn this back on if they have a snippet plugin. + snippetSupport = false; - snippetSupport = true; commitCharactersSupport = false; preselectSupport = false; deprecatedSupport = false; @@ -940,11 +943,9 @@ function protocol.resolve_capabilities(server_capabilities) if server_capabilities.codeActionProvider == nil then general_properties.code_action = false - elseif type(server_capabilities.codeActionProvider) == 'boolean' then + elseif type(server_capabilities.codeActionProvider) == 'boolean' + or type(server_capabilities.codeActionProvider) == 'table' then general_properties.code_action = server_capabilities.codeActionProvider - elseif type(server_capabilities.codeActionProvider) == 'table' then - -- TODO(ashkan) support CodeActionKind - general_properties.code_action = false else error("The server sent invalid codeActionProvider") end diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 749a51fecc..17c411f952 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -42,13 +42,28 @@ local function is_dir(filename) end local NIL = vim.NIL + +--@private +local recursive_convert_NIL +recursive_convert_NIL = function(v, tbl_processed) + if v == NIL then + return nil + elseif not tbl_processed[v] and type(v) == 'table' then + tbl_processed[v] = true + return vim.tbl_map(function(x) + return recursive_convert_NIL(x, tbl_processed) + end, v) + end + + return v +end + --@private --- Returns its argument, but converts `vim.NIL` to Lua `nil`. --@param v (any) Argument --@returns (any) local function convert_NIL(v) - if v == NIL then return nil end - return v + return recursive_convert_NIL(v, {}) end --@private diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 775932c7fd..9ed19b938d 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -214,13 +214,16 @@ end function M.apply_text_document_edit(text_document_edit) local text_document = text_document_edit.textDocument local bufnr = vim.uri_to_bufnr(text_document.uri) - if text_document.version then - -- `VersionedTextDocumentIdentifier`s version may be null https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier - if text_document.version ~= vim.NIL and M.buf_versions[bufnr] ~= nil and M.buf_versions[bufnr] > text_document.version then - print("Buffer ", text_document.uri, " newer than edits.") - return - end + + -- `VersionedTextDocumentIdentifier`s version may be null + -- https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier + if text_document.version + and M.buf_versions[bufnr] + and M.buf_versions[bufnr] > text_document.version then + print("Buffer ", text_document.uri, " newer than edits.") + return end + M.apply_text_edits(text_document_edit.edits, bufnr) end @@ -492,10 +495,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help) --=== 0`. Whenever possible implementors should make an active decision about --the active signature and shouldn't rely on a default value. local contents = {} - local active_signature = signature_help.activeSignature - if active_signature == vim.NIL or active_signature == nil then - active_signature = 0 - end + local active_signature = signature_help.activeSignature or 0 -- If the activeSignature is not inside the valid range, then clip it. if active_signature >= #signature_help.signatures then active_signature = 0 @@ -535,7 +535,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help) } --]=] -- TODO highlight parameter - if parameter.documentation and parameter.documentation ~= vim.NIL then + if parameter.documentation then M.convert_input_to_markdown_lines(parameter.documentation, contents) end end diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index decde08019..6714bb6354 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -157,7 +157,7 @@ local function on_line_impl(self, buf, line) a.nvim_buf_set_extmark(buf, ns, start_row, start_col, { end_line = end_row, end_col = end_col, hl_group = hl, - ephemeral = true + ephemeral = true, }) end if start_row > line then diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh index 8287958ab5..551b8fb691 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -211,6 +211,18 @@ preprocess_patch() { LC_ALL=C sed -e 's/\( [ab]\/src\)/\1\/nvim/g' \ "$file" > "$file".tmp && mv "$file".tmp "$file" + # Rename evalfunc.c to eval/funcs.c + LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/evalfunc\.c/\1\/eval\/funcs\.c/g' \ + "$file" > "$file".tmp && mv "$file".tmp "$file" + + # Rename userfunc.c to eval/userfunc.c + LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/userfunc\.c/\1\/eval\/userfunc\.c/g' \ + "$file" > "$file".tmp && mv "$file".tmp "$file" + + # Rename session.c to ex_session.c + LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/session\(\.[ch]\)/\1\/ex_session\2/g' \ + "$file" > "$file".tmp && mv "$file".tmp "$file" + # Rename test_urls.vim to check_urls.vim LC_ALL=C sed -e 's@\( [ab]\)/runtime/doc/test\(_urls.vim\)@\1/scripts/check\2@g' \ "$file" > "$file".tmp && mv "$file".tmp "$file" diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index cad4c8314f..4569ebc713 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -974,9 +974,9 @@ void nvim_buf_delete(Buffer buffer, Dictionary opts, Error *err) String k = opts.items[i].key; Object v = opts.items[i].value; if (strequal("force", k.data)) { - force = api_coerce_to_bool(v, "force", false, err); + force = api_object_to_bool(v, "force", false, err); } else if (strequal("unload", k.data)) { - unload = api_coerce_to_bool(v, "unload", false, err); + unload = api_object_to_bool(v, "unload", false, err); } else { api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); return; @@ -1441,7 +1441,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, goto error; } } else if (strequal("ephemeral", k.data)) { - ephemeral = api_coerce_to_bool(*v, "ephemeral", false, err); + ephemeral = api_object_to_bool(*v, "ephemeral", false, err); if (ERROR_SET(err)) { goto error; } diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 981d41ae6e..a9b1676879 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1625,11 +1625,8 @@ free_exit: /// @param what The name of the object, used for error message /// @param nil_value What to return if the type is nil. /// @param err Set if there was an error in converting to a bool -bool api_coerce_to_bool( - Object obj, - const char *what, - bool nil_value, - Error *err) +bool api_object_to_bool(Object obj, const char *what, + bool nil_value, Error *err) { if (obj.type == kObjectTypeBoolean) { return obj.data.boolean; @@ -1654,3 +1651,30 @@ const char *describe_ns(NS ns_id) }) return "(UNKNOWN PLUGIN)"; } + +DecorationProvider *get_provider(NS ns_id, bool force) +{ + ssize_t i; + for (i = 0; i < (ssize_t)kv_size(decoration_providers); i++) { + DecorationProvider *item = &kv_A(decoration_providers, i); + if (item->ns_id == ns_id) { + return item; + } else if (item->ns_id > ns_id) { + break; + } + } + + if (!force) { + return NULL; + } + + for (ssize_t j = (ssize_t)kv_size(decoration_providers)-1; j >= i; j++) { + // allocates if needed: + (void)kv_a(decoration_providers, (size_t)j+1); + kv_A(decoration_providers, (size_t)j+1) = kv_A(decoration_providers, j); + } + DecorationProvider *item = &kv_a(decoration_providers, (size_t)i); + *item = DECORATION_PROVIDER_INIT(ns_id); + + return item; +} diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index df3a263dcf..7c6f07402b 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -52,7 +52,8 @@ .type = kObjectTypeLuaRef, \ .data.luaref = r }) -#define NIL ((Object) {.type = kObjectTypeNil}) +#define NIL ((Object)OBJECT_INIT) +#define NULL_STRING ((String)STRING_INIT) #define PUT(dict, k, v) \ kv_push(dict, ((KeyValuePair) { .key = cstr_to_string(k), .value = v })) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 876b052a8e..725847886a 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -199,6 +199,69 @@ Integer nvim_get_hl_id_by_name(String name) return syn_check_group((const char_u *)name.data, (int)name.size); } +Dictionary nvim__get_hl_defs(Integer ns_id, Error *err) +{ + if (ns_id == 0) { + return get_global_hl_defs(); + } + abort(); +} + +/// Set a highlight group. +/// +/// @param ns_id number of namespace for this highlight +/// @param name highlight group name, like ErrorMsg +/// @param val highlight definiton map, like |nvim_get_hl_by_name|. +/// @param[out] err Error details, if any +/// +/// TODO: ns_id = 0, should modify :highlight namespace +/// TODO val should take update vs reset flag +void nvim_set_hl(Integer ns_id, String name, Dictionary val, Error *err) + FUNC_API_SINCE(7) +{ + int hl_id = syn_check_group( (char_u *)(name.data), (int)name.size); + int link_id = -1; + + HlAttrs attrs = dict2hlattrs(val, true, &link_id, err); + if (!ERROR_SET(err)) { + ns_hl_def((NS)ns_id, hl_id, attrs, link_id); + } +} + +/// Set active namespace for highlights. +/// +/// NB: this function can be called from async contexts, but the +/// semantics are not yet well-defined. To start with +/// |nvim_set_decoration_provider| on_win and on_line callbacks +/// are explicitly allowed to change the namespace during a redraw cycle. +/// +/// @param ns_id the namespace to activate +/// @param[out] err Error details, if any +void nvim_set_hl_ns(Integer ns_id, Error *err) + FUNC_API_SINCE(7) + FUNC_API_FAST +{ + if (ns_id >= 0) { + ns_hl_active = (NS)ns_id; + } + + // TODO(bfredl): this is a little bit hackish. Eventually we want a standard + // event path for redraws caused by "fast" events. This could tie in with + // better throttling of async events causing redraws, such as non-batched + // nvim_buf_set_extmark calls from async contexts. + if (!updating_screen && !ns_hl_changed) { + multiqueue_put(main_loop.events, on_redraw_event, 0); + } + ns_hl_changed = true; +} + +static void on_redraw_event(void **argv) + FUNC_API_NOEXPORT +{ + redraw_all_later(NOT_VALID); +} + + /// Sends input-keys to Nvim, subject to various quirks controlled by `mode` /// flags. This is a blocking call, unlike |nvim_input()|. /// @@ -678,7 +741,11 @@ Integer nvim_strwidth(String text, Error *err) ArrayOf(String) nvim_list_runtime_paths(void) FUNC_API_SINCE(1) { + // TODO(bfredl): this should just work: + // return nvim_get_runtime_file(NULL_STRING, true); + Array rv = ARRAY_DICT_INIT; + char_u *rtp = p_rtp; if (*rtp == NUL) { @@ -725,22 +792,29 @@ ArrayOf(String) nvim_list_runtime_paths(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) +ArrayOf(String) nvim_get_runtime_file(String name, Boolean all, Error *err) FUNC_API_SINCE(7) { Array rv = ARRAY_DICT_INIT; - if (!name.data) { + + // TODO(bfredl): + if (name.size == 0) { + api_set_error(err, kErrorTypeValidation, "not yet implemented"); return rv; } + int flags = DIP_START | (all ? DIP_ALL : 0); - do_in_runtimepath((char_u *)name.data, flags, find_runtime_cb, &rv); + do_in_runtimepath(name.size ? (char_u *)name.data : NULL, + flags, find_runtime_cb, &rv); return rv; } static void find_runtime_cb(char_u *fname, void *cookie) { Array *rv = (Array *)cookie; - ADD(*rv, STRING_OBJ(cstr_to_string((char *)fname))); + if (fname != NULL) { + ADD(*rv, STRING_OBJ(cstr_to_string((char *)fname))); + } } String nvim__get_lib_dir(void) @@ -1477,7 +1551,7 @@ void nvim_unsubscribe(uint64_t channel_id, String event) Integer nvim_get_color_by_name(String name) FUNC_API_SINCE(1) { - return name_to_color((char_u *)name.data); + return name_to_color(name.data); } /// Returns a map of color names and RGB values. @@ -2610,33 +2684,6 @@ void nvim__screenshot(String path) ui_call_screenshot(path); } -static DecorationProvider *get_provider(NS ns_id, bool force) -{ - ssize_t i; - for (i = 0; i < (ssize_t)kv_size(decoration_providers); i++) { - DecorationProvider *item = &kv_A(decoration_providers, i); - if (item->ns_id == ns_id) { - return item; - } else if (item->ns_id > ns_id) { - break; - } - } - - if (!force) { - return NULL; - } - - for (ssize_t j = (ssize_t)kv_size(decoration_providers)-1; j >= i; j++) { - // allocates if needed: - (void)kv_a(decoration_providers, (size_t)j+1); - kv_A(decoration_providers, (size_t)j+1) = kv_A(decoration_providers, j); - } - DecorationProvider *item = &kv_a(decoration_providers, (size_t)i); - *item = DECORATION_PROVIDER_INIT(ns_id); - - return item; -} - static void clear_provider(DecorationProvider *p) { NLUA_CLEAR_REF(p->redraw_start); @@ -2695,7 +2742,7 @@ void nvim_set_decoration_provider(Integer ns_id, DictionaryOf(LuaRef) opts, clear_provider(p); // regardless of what happens, it seems good idea to redraw - redraw_later(NOT_VALID); // TODO(bfredl): too soon? + redraw_all_later(NOT_VALID); // TODO(bfredl): too soon? struct { const char *name; @@ -2706,6 +2753,7 @@ void nvim_set_decoration_provider(Integer ns_id, DictionaryOf(LuaRef) opts, { "on_win", &p->redraw_win }, { "on_line", &p->redraw_line }, { "on_end", &p->redraw_end }, + { "_on_hl_def", &p->hl_def }, { NULL, NULL }, }; diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index ba43bc6cb2..f09a03f592 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -142,7 +142,7 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err) // make sure cursor is in visible range even if win != curwin update_topline_win(win); - redraw_win_later(win, VALID); + redraw_later(win, VALID); } /// Gets the window height @@ -471,7 +471,7 @@ void nvim_win_set_config(Window window, Dictionary config, Error *err) if (!win_new_float(win, fconfig, err)) { return; } - redraw_later(NOT_VALID); + redraw_later(win, NOT_VALID); } else { win_config_float(win, fconfig); win->w_pos_changed = true; diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 8f631ae13b..469542ec9f 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1623,7 +1623,7 @@ void enter_buffer(buf_T *buf) } curbuf->b_last_used = time(NULL); - redraw_later(NOT_VALID); + redraw_later(curwin, NOT_VALID); } // Change to the directory of the current buffer. @@ -3440,31 +3440,17 @@ int build_stl_str_hl( int use_sandbox, char_u fillchar, int maxwidth, - struct stl_hlrec *hltab, - StlClickRecord *tabtab + stl_hlrec_t **hltab, + StlClickRecord **tabtab ) { - int groupitems[STL_MAX_ITEM]; - struct stl_item { - // Where the item starts in the status line output buffer - char_u *start; - // Function to run for ClickFunc items. - char *cmd; - // The minimum width of the item - int minwid; - // The maximum width of the item - int maxwid; - enum { - Normal, - Empty, - Group, - Separate, - Highlight, - TabPage, - ClickFunc, - Trunc - } type; - } items[STL_MAX_ITEM]; + static size_t stl_items_len = 20; // Initial value, grows as needed. + static stl_item_t *stl_items = NULL; + static int *stl_groupitems = NULL; + static stl_hlrec_t *stl_hltab = NULL; + static StlClickRecord *stl_tabtab = NULL; + static int *stl_separator_locations = NULL; + #define TMPLEN 70 char_u buf_tmp[TMPLEN]; char_u win_tmp[TMPLEN]; @@ -3472,6 +3458,14 @@ int build_stl_str_hl( const int save_must_redraw = must_redraw; const int save_redr_type = curwin->w_redr_type; + if (stl_items == NULL) { + stl_items = xmalloc(sizeof(stl_item_t) * stl_items_len); + stl_groupitems = xmalloc(sizeof(int) * stl_items_len); + stl_hltab = xmalloc(sizeof(stl_hlrec_t) * stl_items_len); + stl_tabtab = xmalloc(sizeof(stl_hlrec_t) * stl_items_len); + stl_separator_locations = xmalloc(sizeof(int) * stl_items_len); + } + // When the format starts with "%!" then evaluate it as an expression and // use the result as the actual format string. if (fmt[0] == '%' && fmt[1] == '!') { @@ -3540,14 +3534,17 @@ int build_stl_str_hl( // Proceed character by character through the statusline format string // fmt_p is the current positon in the input buffer for (char_u *fmt_p = usefmt; *fmt_p; ) { - if (curitem == STL_MAX_ITEM) { - // There are too many items. Add the error code to the statusline - // to give the user a hint about what went wrong. - if (out_p + 5 < out_end_p) { - memmove(out_p, " E541", (size_t)5); - out_p += 5; - } - break; + if (curitem == (int)stl_items_len) { + size_t new_len = stl_items_len * 3 / 2; + + stl_items = xrealloc(stl_items, sizeof(stl_item_t) * new_len); + stl_groupitems = xrealloc(stl_groupitems, sizeof(int) * new_len); + stl_hltab = xrealloc(stl_hltab, sizeof(stl_hlrec_t) * new_len); + stl_tabtab = xrealloc(stl_tabtab, sizeof(StlClickRecord) * new_len); + stl_separator_locations = + xrealloc(stl_separator_locations, sizeof(int) * new_len); + + stl_items_len = new_len; } if (*fmt_p != NUL && *fmt_p != '%') { @@ -3591,16 +3588,16 @@ int build_stl_str_hl( if (groupdepth > 0) { continue; } - items[curitem].type = Separate; - items[curitem++].start = out_p; + stl_items[curitem].type = Separate; + stl_items[curitem++].start = out_p; continue; } // STL_TRUNCMARK: Where to begin truncating if the statusline is too long. if (*fmt_p == STL_TRUNCMARK) { fmt_p++; - items[curitem].type = Trunc; - items[curitem++].start = out_p; + stl_items[curitem].type = Trunc; + stl_items[curitem++].start = out_p; continue; } @@ -3616,7 +3613,7 @@ int build_stl_str_hl( // Determine how long the group is. // Note: We set the current output position to null // so `vim_strsize` will work. - char_u *t = items[groupitems[groupdepth]].start; + char_u *t = stl_items[stl_groupitems[groupdepth]].start; *out_p = NUL; long group_len = vim_strsize(t); @@ -3626,40 +3623,40 @@ int build_stl_str_hl( // move the output pointer back to where the group started. // Note: This erases any non-item characters that were in the group. // Otherwise there would be no reason to do this step. - if (curitem > groupitems[groupdepth] + 1 - && items[groupitems[groupdepth]].minwid == 0) { + if (curitem > stl_groupitems[groupdepth] + 1 + && stl_items[stl_groupitems[groupdepth]].minwid == 0) { // remove group if all items are empty and highlight group // doesn't change int group_start_userhl = 0; int group_end_userhl = 0; int n; - for (n = groupitems[groupdepth] - 1; n >= 0; n--) { - if (items[n].type == Highlight) { - group_start_userhl = group_end_userhl = items[n].minwid; + for (n = stl_groupitems[groupdepth] - 1; n >= 0; n--) { + if (stl_items[n].type == Highlight) { + group_start_userhl = group_end_userhl = stl_items[n].minwid; break; } } - for (n = groupitems[groupdepth] + 1; n < curitem; n++) { - if (items[n].type == Normal) { + for (n = stl_groupitems[groupdepth] + 1; n < curitem; n++) { + if (stl_items[n].type == Normal) { break; } - if (items[n].type == Highlight) { - group_end_userhl = items[n].minwid; + if (stl_items[n].type == Highlight) { + group_end_userhl = stl_items[n].minwid; } } if (n == curitem && group_start_userhl == group_end_userhl) { // empty group out_p = t; group_len = 0; - for (n = groupitems[groupdepth] + 1; n < curitem; n++) { + for (n = stl_groupitems[groupdepth] + 1; n < curitem; n++) { // do not use the highlighting from the removed group - if (items[n].type == Highlight) { - items[n].type = Empty; + if (stl_items[n].type == Highlight) { + stl_items[n].type = Empty; } // adjust the start position of TabPage to the next // item position - if (items[n].type == TabPage) { - items[n].start = out_p; + if (stl_items[n].type == TabPage) { + stl_items[n].start = out_p; } } } @@ -3667,18 +3664,19 @@ int build_stl_str_hl( // If the group is longer than it is allowed to be // truncate by removing bytes from the start of the group text. - if (group_len > items[groupitems[groupdepth]].maxwid) { + if (group_len > stl_items[stl_groupitems[groupdepth]].maxwid) { // { Determine the number of bytes to remove long n; if (has_mbyte) { // Find the first character that should be included. n = 0; - while (group_len >= items[groupitems[groupdepth]].maxwid) { + while (group_len >= stl_items[stl_groupitems[groupdepth]].maxwid) { group_len -= ptr2cells(t + n); n += (*mb_ptr2len)(t + n); } } else { - n = (long)(out_p - t) - items[groupitems[groupdepth]].maxwid + 1; + n = (long)(out_p - t) + - stl_items[stl_groupitems[groupdepth]].maxwid + 1; } // } @@ -3689,25 +3687,26 @@ int build_stl_str_hl( memmove(t + 1, t + n, (size_t)(out_p - (t + n))); out_p = out_p - n + 1; // Fill up space left over by half a double-wide char. - while (++group_len < items[groupitems[groupdepth]].minwid) { + while (++group_len < stl_items[stl_groupitems[groupdepth]].minwid) { *out_p++ = fillchar; } // } // correct the start of the items for the truncation - for (int idx = groupitems[groupdepth] + 1; idx < curitem; idx++) { + for (int idx = stl_groupitems[groupdepth] + 1; idx < curitem; idx++) { // Shift everything back by the number of removed bytes - items[idx].start -= n; + stl_items[idx].start -= n; // If the item was partially or completely truncated, set its // start to the start of the group - if (items[idx].start < t) { - items[idx].start = t; + if (stl_items[idx].start < t) { + stl_items[idx].start = t; } } // If the group is shorter than the minimum width, add padding characters. - } else if (abs(items[groupitems[groupdepth]].minwid) > group_len) { - long min_group_width = items[groupitems[groupdepth]].minwid; + } else if ( + abs(stl_items[stl_groupitems[groupdepth]].minwid) > group_len) { + long min_group_width = stl_items[stl_groupitems[groupdepth]].minwid; // If the group is left-aligned, add characters to the right. if (min_group_width < 0) { min_group_width = 0 - min_group_width; @@ -3726,8 +3725,8 @@ int build_stl_str_hl( // } // Adjust item start positions - for (int n = groupitems[groupdepth] + 1; n < curitem; n++) { - items[n].start += group_len; + for (int n = stl_groupitems[groupdepth] + 1; n < curitem; n++) { + stl_items[n].start += group_len; } // Prepend the fill characters @@ -3763,9 +3762,9 @@ int build_stl_str_hl( // User highlight groups override the min width field // to denote the styling to use. if (*fmt_p == STL_USER_HL) { - items[curitem].type = Highlight; - items[curitem].start = out_p; - items[curitem].minwid = minwid > 9 ? 1 : minwid; + stl_items[curitem].type = Highlight; + stl_items[curitem].start = out_p; + stl_items[curitem].minwid = minwid > 9 ? 1 : minwid; fmt_p++; curitem++; continue; @@ -3799,8 +3798,8 @@ int build_stl_str_hl( if (minwid == 0) { // %X ends the close label, go back to the previous tab label nr. for (long n = curitem - 1; n >= 0; n--) { - if (items[n].type == TabPage && items[n].minwid >= 0) { - minwid = items[n].minwid; + if (stl_items[n].type == TabPage && stl_items[n].minwid >= 0) { + minwid = stl_items[n].minwid; break; } } @@ -3809,9 +3808,9 @@ int build_stl_str_hl( minwid = -minwid; } } - items[curitem].type = TabPage; - items[curitem].start = out_p; - items[curitem].minwid = minwid; + stl_items[curitem].type = TabPage; + stl_items[curitem].start = out_p; + stl_items[curitem].minwid = minwid; fmt_p++; curitem++; continue; @@ -3826,10 +3825,10 @@ int build_stl_str_hl( if (*fmt_p != STL_CLICK_FUNC) { break; } - items[curitem].type = ClickFunc; - items[curitem].start = out_p; - items[curitem].cmd = xmemdupz(t, (size_t)(((char *)fmt_p - t))); - items[curitem].minwid = minwid; + stl_items[curitem].type = ClickFunc; + stl_items[curitem].start = out_p; + stl_items[curitem].cmd = xmemdupz(t, (size_t)(((char *)fmt_p - t))); + stl_items[curitem].minwid = minwid; fmt_p++; curitem++; continue; @@ -3850,11 +3849,11 @@ int build_stl_str_hl( // Denotes the start of a new group if (*fmt_p == '(') { - groupitems[groupdepth++] = curitem; - items[curitem].type = Group; - items[curitem].start = out_p; - items[curitem].minwid = minwid; - items[curitem].maxwid = maxwid; + stl_groupitems[groupdepth++] = curitem; + stl_items[curitem].type = Group; + stl_items[curitem].start = out_p; + stl_items[curitem].minwid = minwid; + stl_items[curitem].maxwid = maxwid; fmt_p++; curitem++; continue; @@ -4149,9 +4148,9 @@ int build_stl_str_hl( // Create a highlight item based on the name if (*fmt_p == '#') { - items[curitem].type = Highlight; - items[curitem].start = out_p; - items[curitem].minwid = -syn_namen2id(t, (int)(fmt_p - t)); + stl_items[curitem].type = Highlight; + stl_items[curitem].start = out_p; + stl_items[curitem].minwid = -syn_namen2id(t, (int)(fmt_p - t)); curitem++; fmt_p++; } @@ -4162,8 +4161,8 @@ int build_stl_str_hl( // If we made it this far, the item is normal and starts at // our current position in the output buffer. // Non-normal items would have `continued`. - items[curitem].start = out_p; - items[curitem].type = Normal; + stl_items[curitem].start = out_p; + stl_items[curitem].type = Normal; // Copy the item string into the output buffer if (str != NULL && *str) { @@ -4321,7 +4320,7 @@ int build_stl_str_hl( // Otherwise, there was nothing to print so mark the item as empty } else { - items[curitem].type = Empty; + stl_items[curitem].type = Empty; } // Only free the string buffer if we allocated it. @@ -4362,13 +4361,13 @@ int build_stl_str_hl( // Otherwise, look for the truncation item } else { // Default to truncating at the first item - trunc_p = items[0].start; + trunc_p = stl_items[0].start; item_idx = 0; for (int i = 0; i < itemcnt; i++) { - if (items[i].type == Trunc) { - // Truncate at %< items. - trunc_p = items[i].start; + if (stl_items[i].type == Trunc) { + // Truncate at %< stl_items. + trunc_p = stl_items[i].start; item_idx = i; break; } @@ -4403,7 +4402,7 @@ int build_stl_str_hl( // Ignore any items in the statusline that occur after // the truncation point for (int i = 0; i < itemcnt; i++) { - if (items[i].start > trunc_p) { + if (stl_items[i].start > trunc_p) { itemcnt = i; break; } @@ -4458,12 +4457,12 @@ int build_stl_str_hl( for (int i = item_idx; i < itemcnt; i++) { // Items starting at or after the end of the truncated section need // to be moved backwards. - if (items[i].start >= trunc_end_p) { - items[i].start -= item_offset; + if (stl_items[i].start >= trunc_end_p) { + stl_items[i].start -= item_offset; // Anything inside the truncated area is set to start // at the `<` truncation character. } else { - items[i].start = trunc_p; + stl_items[i].start = trunc_p; } } // } @@ -4479,7 +4478,7 @@ int build_stl_str_hl( // figuring out how many groups there are. int num_separators = 0; for (int i = 0; i < itemcnt; i++) { - if (items[i].type == Separate) { + if (stl_items[i].type == Separate) { num_separators++; } } @@ -4488,11 +4487,10 @@ int build_stl_str_hl( if (num_separators) { // Create an array of the start location for each // separator mark. - int separator_locations[STL_MAX_ITEM]; int index = 0; for (int i = 0; i < itemcnt; i++) { - if (items[i].type == Separate) { - separator_locations[index] = i; + if (stl_items[i].type == Separate) { + stl_separator_locations[index] = i; index++; } } @@ -4504,16 +4502,17 @@ int build_stl_str_hl( for (int i = 0; i < num_separators; i++) { int dislocation = (i == (num_separators - 1)) ? final_spaces : standard_spaces; - char_u *seploc = items[separator_locations[i]].start + dislocation; - STRMOVE(seploc, items[separator_locations[i]].start); - for (char_u *s = items[separator_locations[i]].start; s < seploc; s++) { + char_u *start = stl_items[stl_separator_locations[i]].start; + char_u *seploc = start + dislocation; + STRMOVE(seploc, start); + for (char_u *s = start; s < seploc; s++) { *s = fillchar; } - for (int item_idx = separator_locations[i] + 1; + for (int item_idx = stl_separator_locations[i] + 1; item_idx < itemcnt; item_idx++) { - items[item_idx].start += dislocation; + stl_items[item_idx].start += dislocation; } } @@ -4523,11 +4522,12 @@ int build_stl_str_hl( // Store the info about highlighting. if (hltab != NULL) { - struct stl_hlrec *sp = hltab; + *hltab = stl_hltab; + stl_hlrec_t *sp = stl_hltab; for (long l = 0; l < itemcnt; l++) { - if (items[l].type == Highlight) { - sp->start = items[l].start; - sp->userhl = items[l].minwid; + if (stl_items[l].type == Highlight) { + sp->start = stl_items[l].start; + sp->userhl = stl_items[l].minwid; sp++; } } @@ -4537,16 +4537,17 @@ int build_stl_str_hl( // Store the info about tab pages labels. if (tabtab != NULL) { - StlClickRecord *cur_tab_rec = tabtab; + *tabtab = stl_tabtab; + StlClickRecord *cur_tab_rec = stl_tabtab; for (long l = 0; l < itemcnt; l++) { - if (items[l].type == TabPage) { - cur_tab_rec->start = (char *)items[l].start; - if (items[l].minwid == 0) { + if (stl_items[l].type == TabPage) { + cur_tab_rec->start = (char *)stl_items[l].start; + if (stl_items[l].minwid == 0) { cur_tab_rec->def.type = kStlClickDisabled; cur_tab_rec->def.tabnr = 0; } else { - int tabnr = items[l].minwid; - if (items[l].minwid > 0) { + int tabnr = stl_items[l].minwid; + if (stl_items[l].minwid > 0) { cur_tab_rec->def.type = kStlClickTabSwitch; } else { cur_tab_rec->def.type = kStlClickTabClose; @@ -4556,11 +4557,11 @@ int build_stl_str_hl( } cur_tab_rec->def.func = NULL; cur_tab_rec++; - } else if (items[l].type == ClickFunc) { - cur_tab_rec->start = (char *)items[l].start; + } else if (stl_items[l].type == ClickFunc) { + cur_tab_rec->start = (char *)stl_items[l].start; cur_tab_rec->def.type = kStlClickFuncRun; - cur_tab_rec->def.tabnr = items[l].minwid; - cur_tab_rec->def.func = items[l].cmd; + cur_tab_rec->def.tabnr = stl_items[l].minwid; + cur_tab_rec->def.func = stl_items[l].cmd; cur_tab_rec++; } } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 8e855cb644..4efc341875 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -361,14 +361,36 @@ struct mapblock { sctx_T m_script_ctx; // SCTX where map was defined }; -/* - * Used for highlighting in the status line. - */ +/// Used for highlighting in the status line. +typedef struct stl_hlrec stl_hlrec_t; struct stl_hlrec { char_u *start; int userhl; // 0: no HL, 1-9: User HL, < 0 for syn ID }; +/// Used for building the status line. +typedef struct stl_item stl_item_t; +struct stl_item { + // Where the item starts in the status line output buffer + char_u *start; + // Function to run for ClickFunc items. + char *cmd; + // The minimum width of the item + int minwid; + // The maximum width of the item + int maxwid; + enum { + Normal, + Empty, + Group, + Separate, + Highlight, + TabPage, + ClickFunc, + Trunc + } type; +}; + // values for b_syn_spell: what to do with toplevel text #define SYNSPL_DEFAULT 0 // spell check if @Spell not defined #define SYNSPL_TOP 1 // spell check toplevel text @@ -773,6 +795,7 @@ struct file_buffer { int b_ind_cpp_namespace; int b_ind_if_for_while; int b_ind_cpp_extern_c; + int b_ind_pragma; linenr_T b_no_eol_lnum; /* non-zero lnum when last line of next binary * write should not have an end-of-line */ diff --git a/src/nvim/change.c b/src/nvim/change.c index be52750c44..9ee987b45d 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -294,7 +294,7 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, // change. if (wp->w_p_rnu || (wp->w_p_cul && lnum <= wp->w_last_cursorline)) { - redraw_win_later(wp, SOME_VALID); + redraw_later(wp, SOME_VALID); } } } @@ -348,7 +348,7 @@ void changed_bytes(linenr_T lnum, colnr_T col) FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_p_diff && wp != curwin) { - redraw_win_later(wp, VALID); + redraw_later(wp, VALID); wlnum = diff_lnum_win(lnum, wp); if (wlnum > 0) { changedOneline(wp->w_buffer, wlnum); @@ -475,7 +475,7 @@ changed_lines( FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_p_diff && wp != curwin) { - redraw_win_later(wp, VALID); + redraw_later(wp, VALID); wlnum = diff_lnum_win(lnum, wp); if (wlnum > 0) { changed_lines_buf(wp->w_buffer, wlnum, diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 036ae32589..d3ffab1759 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -482,7 +482,7 @@ bool leftcol_changed(void) if (retval) curwin->w_set_curswant = true; - redraw_later(NOT_VALID); + redraw_later(curwin, NOT_VALID); return retval; } diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 3de5fc49bd..b9c293f6c8 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -643,7 +643,7 @@ void diff_redraw(bool dofold) if (!wp->w_p_diff) { continue; } - redraw_win_later(wp, SOME_VALID); + redraw_later(wp, SOME_VALID); if (dofold && foldmethodIsDiff(wp)) { foldUpdateAll(wp); } @@ -1415,7 +1415,7 @@ void diff_win_options(win_T *wp, int addbuf) if (addbuf) { diff_buf_add(wp->w_buffer); } - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); } /// Set options not to show diffs. For the current window or all windows. diff --git a/src/nvim/edit.c b/src/nvim/edit.c index ac0e6cc9f6..3e62ed9036 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -8571,7 +8571,7 @@ static void ins_up( if (old_topline != curwin->w_topline || old_topfill != curwin->w_topfill ) - redraw_later(VALID); + redraw_later(curwin, VALID); start_arrow(&tpos); can_cindent = true; } else { @@ -8619,7 +8619,7 @@ static void ins_down( if (old_topline != curwin->w_topline || old_topfill != curwin->w_topfill ) - redraw_later(VALID); + redraw_later(curwin, VALID); start_arrow(&tpos); can_cindent = true; } else { @@ -9013,7 +9013,7 @@ static int ins_ctrl_ey(int tc) scrolldown_clamp(); else scrollup_clamp(); - redraw_later(VALID); + redraw_later(curwin, VALID); } else { c = ins_copychar(curwin->w_cursor.lnum + (c == Ctrl_Y ? -1 : 1)); if (c != NUL) { diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 3669cbbd2d..41137bf2db 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3721,8 +3721,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, update_topline(); validate_cursor(); update_screen(SOME_VALID); - highlight_match = FALSE; - redraw_later(SOME_VALID); + highlight_match = false; + redraw_later(curwin, SOME_VALID); curwin->w_p_fen = save_p_fen; if (msg_row == Rows - 1) @@ -5751,7 +5751,7 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, } xfree(str); - redraw_later(SOME_VALID); + redraw_later(curwin, SOME_VALID); win_enter(save_curwin, false); // Return to original window update_topline(); diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 503fd8e0d0..713d18b44d 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2363,7 +2363,7 @@ void ex_compiler(exarg_T *eap) do_unlet(S_LEN("b:current_compiler"), true); snprintf((char *)buf, bufsize, "compiler/%s.vim", eap->arg); - if (source_runtime(buf, DIP_ALL) == FAIL) { + if (source_in_path(p_rtp, buf, DIP_ALL) == FAIL) { EMSG2(_("E666: compiler not supported: %s"), eap->arg); } xfree(buf); @@ -2581,6 +2581,7 @@ int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback, /// return FAIL when no file could be sourced, OK otherwise. int source_runtime(char_u *name, int flags) { + flags |= (flags & DIP_NORTP) ? 0 : DIP_START; return source_in_path(p_rtp, name, flags); } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 211791c19d..d7ed2fe97d 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1251,7 +1251,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, char_u *errormsg = NULL; // error message char_u *after_modifier = NULL; exarg_T ea; - int save_msg_scroll = msg_scroll; + const int save_msg_scroll = msg_scroll; cmdmod_T save_cmdmod; const int save_reg_executing = reg_executing; char_u *cmd; @@ -2003,34 +2003,10 @@ doend: ? cmdnames[(int)ea.cmdidx].cmd_name : (char_u *)NULL); - if (ea.verbose_save >= 0) { - p_verbose = ea.verbose_save; - } - free_cmdmod(); - + undo_cmdmod(&ea, save_msg_scroll); cmdmod = save_cmdmod; reg_executing = save_reg_executing; - if (ea.save_msg_silent != -1) { - // messages could be enabled for a serious error, need to check if the - // counters don't become negative - if (!did_emsg || msg_silent > ea.save_msg_silent) { - msg_silent = ea.save_msg_silent; - } - emsg_silent -= ea.did_esilent; - if (emsg_silent < 0) { - emsg_silent = 0; - } - // Restore msg_scroll, it's set by file I/O commands, even when no - // message is actually displayed. - msg_scroll = save_msg_scroll; - - /* "silent reg" or "silent echo x" inside "redir" leaves msg_col - * somewhere in the line. Put it back in the first column. */ - if (redirecting()) - msg_col = 0; - } - if (ea.did_sandbox) { sandbox--; } @@ -2298,9 +2274,14 @@ int parse_command_modifiers(exarg_T *eap, char_u **errormsg, bool skip_only) return OK; } -// Free contents of "cmdmod". -static void free_cmdmod(void) +// Undo and free contents of "cmdmod". +static void undo_cmdmod(const exarg_T *eap, int save_msg_scroll) + FUNC_ATTR_NONNULL_ALL { + if (eap->verbose_save >= 0) { + p_verbose = eap->verbose_save; + } + if (cmdmod.save_ei != NULL) { /* Restore 'eventignore' to the value before ":noautocmd". */ set_string_option_direct((char_u *)"ei", -1, cmdmod.save_ei, @@ -2308,8 +2289,27 @@ static void free_cmdmod(void) free_string_option(cmdmod.save_ei); } - if (cmdmod.filter_regmatch.regprog != NULL) { - vim_regfree(cmdmod.filter_regmatch.regprog); + vim_regfree(cmdmod.filter_regmatch.regprog); + + if (eap->save_msg_silent != -1) { + // messages could be enabled for a serious error, need to check if the + // counters don't become negative + if (!did_emsg || msg_silent > eap->save_msg_silent) { + msg_silent = eap->save_msg_silent; + } + emsg_silent -= eap->did_esilent; + if (emsg_silent < 0) { + emsg_silent = 0; + } + // Restore msg_scroll, it's set by file I/O commands, even when no + // message is actually displayed. + msg_scroll = save_msg_scroll; + + // "silent reg" or "silent echo x" inside "redir" leaves msg_col + // somewhere in the line. Put it back in the first column. + if (redirecting()) { + msg_col = 0; + } } } @@ -4446,6 +4446,9 @@ void separate_nextcmd(exarg_T *eap) else if (p[0] == '`' && p[1] == '=' && (eap->argt & XFILE)) { p += 2; (void)skip_expr(&p); + if (*p == NUL) { // stop at NUL after CTRL-V + break; + } } /* Check for '"': start of comment or '|': next command */ /* :@" does not start a comment! @@ -7379,7 +7382,7 @@ static void ex_syncbind(exarg_T *eap) else scrolldown(-y, TRUE); curwin->w_scbind_pos = topline; - redraw_later(VALID); + redraw_later(curwin, VALID); cursor_correct(); curwin->w_redr_status = TRUE; } @@ -8504,7 +8507,7 @@ static void ex_pedit(exarg_T *eap) if (curwin != curwin_save && win_valid(curwin_save)) { // Return cursor to where we were validate_cursor(); - redraw_later(VALID); + redraw_later(curwin, VALID); win_enter(curwin_save, true); } g_do_tagpreview = 0; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index d67e9b2d7e..53feffd2d7 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -6483,7 +6483,7 @@ static int open_cmdwin(void) ccline.redraw_state = kCmdRedrawNone; ui_call_cmdline_hide(ccline.level); } - redraw_later(SOME_VALID); + redraw_later(curwin, SOME_VALID); // Save the command line info, can be used recursively. save_cmdline(&save_ccline); diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index 6b00b986dc..42a9ef08f9 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -897,8 +897,8 @@ void ex_mkrc(exarg_T *eap) if (!failed && view_session) { if (put_line(fd, - "let s:so_save = &so | let s:siso_save = &siso" - " | set so=0 siso=0") == FAIL) { + "let s:so_save = &g:so | let s:siso_save = &g:siso" + " | setg so=0 siso=0 | setl so=-1 siso=-1") == FAIL) { failed = true; } if (eap->cmdidx == CMD_mksession) { @@ -949,7 +949,7 @@ void ex_mkrc(exarg_T *eap) } if (fprintf(fd, "%s", - "let &so = s:so_save | let &siso = s:siso_save\n" + "let &g:so = s:so_save | let &g:siso = s:siso_save\n" "doautoall SessionLoadPost\n") < 0) { failed = true; diff --git a/src/nvim/extmark_defs.h b/src/nvim/extmark_defs.h index f5ca0ebbb0..382aaf6ed3 100644 --- a/src/nvim/extmark_defs.h +++ b/src/nvim/extmark_defs.h @@ -50,10 +50,13 @@ typedef struct { LuaRef redraw_win; LuaRef redraw_line; LuaRef redraw_end; + LuaRef hl_def; + int hl_valid; } DecorationProvider; #define DECORATION_PROVIDER_INIT(ns_id) (DecorationProvider) \ { ns_id, false, LUA_NOREF, LUA_NOREF, \ - LUA_NOREF, LUA_NOREF, LUA_NOREF } + LUA_NOREF, LUA_NOREF, LUA_NOREF, \ + LUA_NOREF, -1 } #endif // NVIM_EXTMARK_DEFS_H diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 197aedabec..57001f77ed 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -862,7 +862,7 @@ void foldUpdateAfterInsert(void) void foldUpdateAll(win_T *win) { win->w_foldinvalid = true; - redraw_win_later(win, NOT_VALID); + redraw_later(win, NOT_VALID); } // foldMoveTo() {{{2 diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 2db8689a56..31dd3fc848 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -92,6 +92,10 @@ EXTERN struct nvim_stats_s { EXTERN int Rows INIT(= DFLT_ROWS); // nr of rows in the screen EXTERN int Columns INIT(= DFLT_COLS); // nr of columns in the screen +EXTERN NS ns_hl_active INIT(= 0); // current ns that defines highlights +EXTERN bool ns_hl_changed INIT(= false); // highlight need update + + // We use 64-bit file functions here, if available. E.g. ftello() returns // off_t instead of long, which helps if long is 32 bit and off_t is 64 bit. // We assume that when fseeko() is available then ftello() is too. @@ -941,8 +945,10 @@ EXTERN char_u e_readonly[] INIT(= N_( EXTERN char_u e_readonlyvar[] INIT(= N_( "E46: Cannot change read-only variable \"%.*s\"")); EXTERN char_u e_dictreq[] INIT(= N_("E715: Dictionary required")); -EXTERN char_u e_toomanyarg[] INIT(= N_("E118: Too many arguments for function: %s")); -EXTERN char_u e_dictkey[] INIT(= N_("E716: Key not present in Dictionary: %s")); +EXTERN char_u e_toomanyarg[] INIT(= N_( + "E118: Too many arguments for function: %s")); +EXTERN char_u e_dictkey[] INIT(= N_( + "E716: Key not present in Dictionary: \"%s\"")); EXTERN char_u e_listreq[] INIT(= N_("E714: List required")); EXTERN char_u e_listdictarg[] INIT(= N_( "E712: Argument of %s must be a List or Dictionary")); diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index 262afba07a..8403615166 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -14,6 +14,7 @@ #include "nvim/ui.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" +#include "nvim/lua/executor.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "highlight.c.generated.h" @@ -28,12 +29,16 @@ static Map(int, int) *combine_attr_entries; static Map(int, int) *blend_attr_entries; static Map(int, int) *blendthrough_attr_entries; +/// highlight entries private to a namespace +static Map(ColorKey, ColorItem) *ns_hl; + void highlight_init(void) { attr_entry_ids = map_new(HlEntry, int)(); combine_attr_entries = map_new(int, int)(); blend_attr_entries = map_new(int, int)(); blendthrough_attr_entries = map_new(int, int)(); + ns_hl = map_new(ColorKey, ColorItem)(); // index 0 is no attribute, add dummy entry: kv_push(attr_entries, ((HlEntry){ .attr = HLATTRS_INIT, .kind = kHlUnknown, @@ -129,21 +134,114 @@ void ui_send_all_hls(UI *ui) } /// Get attribute code for a syntax group. -int hl_get_syn_attr(int idx, HlAttrs at_en) +int hl_get_syn_attr(int ns_id, int idx, HlAttrs at_en) { // TODO(bfredl): should we do this unconditionally if (at_en.cterm_fg_color != 0 || at_en.cterm_bg_color != 0 || at_en.rgb_fg_color != -1 || at_en.rgb_bg_color != -1 || at_en.rgb_sp_color != -1 || at_en.cterm_ae_attr != 0 - || at_en.rgb_ae_attr != 0) { + || at_en.rgb_ae_attr != 0 || ns_id != 0) { return get_attr_entry((HlEntry){ .attr = at_en, .kind = kHlSyntax, - .id1 = idx, .id2 = 0 }); + .id1 = idx, .id2 = ns_id }); } else { // If all the fields are cleared, clear the attr field back to default value return 0; } } +static ColorKey colored_key(NS ns_id, int syn_id) +{ + return (ColorKey){ .ns_id = (int)ns_id, .syn_id = syn_id }; +} + +void ns_hl_def(NS ns_id, int hl_id, HlAttrs attrs, int link_id) +{ + DecorationProvider *p = get_provider(ns_id, true); + int attr_id = link_id > 0 ? -1 : hl_get_syn_attr(ns_id, hl_id, attrs); + ColorItem it = { .attr_id = attr_id, + .link_id = link_id, + .version = p->hl_valid }; + map_put(ColorKey, ColorItem)(ns_hl, colored_key(ns_id, hl_id), it); +} + +int ns_get_hl(NS ns_id, int hl_id, bool link) +{ + static int recursive = 0; + + if (ns_id < 0) { + if (ns_hl_active <= 0) { + return -1; + } + ns_id = ns_hl_active; + } + + DecorationProvider *p = get_provider(ns_id, true); + ColorItem it = map_get(ColorKey, ColorItem)(ns_hl, colored_key(ns_id, hl_id)); + // TODO(bfredl): map_ref true even this? + bool valid_cache = it.version >= p->hl_valid; + + if (!valid_cache && p->hl_def != LUA_NOREF && !recursive) { + FIXED_TEMP_ARRAY(args, 3); + args.items[0] = INTEGER_OBJ((Integer)ns_id); + args.items[1] = STRING_OBJ(cstr_to_string((char *)syn_id2name(hl_id))); + args.items[2] = BOOLEAN_OBJ(link); + // TODO(bfredl): preload the "global" attr dict? + + Error err = ERROR_INIT; + recursive++; + Object ret = nlua_call_ref(p->hl_def, "hl_def", args, true, &err); + recursive--; + + // TODO(bfredl): or "inherit", combine with global value? + bool fallback = true; + int tmp = false; + HlAttrs attrs = HLATTRS_INIT; + if (ret.type == kObjectTypeDictionary) { + Dictionary dict = ret.data.dictionary; + fallback = false; + attrs = dict2hlattrs(dict, true, &it.link_id, &err); + for (size_t i = 0; i < dict.size; i++) { + char *key = dict.items[i].key.data; + Object val = dict.items[i].value; + bool truthy = api_object_to_bool(val, key, false, &err); + + if (strequal(key, "fallback")) { + fallback = truthy; + } else if (strequal(key, "temp")) { + tmp = truthy; + } + } + if (it.link_id >= 0) { + fallback = true; + } + } + + it.attr_id = fallback ? -1 : hl_get_syn_attr((int)ns_id, hl_id, attrs); + it.version = p->hl_valid-tmp; + map_put(ColorKey, ColorItem)(ns_hl, colored_key(ns_id, hl_id), it); + } + + if (link) { + return it.attr_id >= 0 ? -1 : it.link_id; + } else { + return it.attr_id; + } +} + + +bool win_check_ns_hl(win_T *wp) +{ + if (ns_hl_changed) { + highlight_changed(); + if (wp) { + update_window_hl(wp, true); + } + ns_hl_changed = false; + return true; + } + return false; +} + /// Get attribute code for a builtin highlight group. /// /// The final syntax group could be modified by hi-link or 'winhighlight'. @@ -204,6 +302,17 @@ void update_window_hl(win_T *wp, bool invalid) wp->w_hl_attr_normal = float_win ? HL_ATTR(HLF_NFLOAT) : 0; } + // NOOOO! You cannot just pretend that "Normal" is just like any other + // syntax group! It needs at least 10 layers of special casing! Noooooo! + // + // haha, theme engine go brrr + int normality = syn_check_group((const char_u *)S_LEN("Normal")); + int ns_attr = ns_get_hl(-1, normality, false); + if (ns_attr > 0) { + // TODO(bfredl): hantera NormalNC and so on + wp->w_hl_attr_normal = ns_attr; + } + // if blend= attribute is not set, 'winblend' value overrides it. if (wp->w_floating && wp->w_p_winbl > 0) { HlEntry entry = kv_A(attr_entries, wp->w_hl_attr_normal); @@ -275,6 +384,7 @@ void clear_hl_tables(bool reinit) map_free(int, int)(combine_attr_entries); map_free(int, int)(blend_attr_entries); map_free(int, int)(blendthrough_attr_entries); + map_free(ColorKey, ColorItem)(ns_hl); } } @@ -663,6 +773,103 @@ Dictionary hlattrs2dict(HlAttrs ae, bool use_rgb) return hl; } +HlAttrs dict2hlattrs(Dictionary dict, bool use_rgb, int *link_id, Error *err) +{ + HlAttrs hlattrs = HLATTRS_INIT; + + int32_t fg = -1, bg = -1, sp = -1; + int16_t mask = 0; + for (size_t i = 0; i < dict.size; i++) { + char *key = dict.items[i].key.data; + Object val = dict.items[i].value; + + struct { + const char *name; + int16_t flag; + } flags[] = { + { "bold", HL_BOLD }, + { "standout", HL_STANDOUT }, + { "underline", HL_UNDERLINE }, + { "undercurl", HL_UNDERCURL }, + { "italic", HL_ITALIC }, + { "reverse", HL_INVERSE }, + { NULL, 0 }, + }; + + int j; + for (j = 0; flags[j].name; j++) { + if (strequal(flags[j].name, key)) { + if (api_object_to_bool(val, key, false, err)) { + mask = mask | flags[j].flag; + } + break; + } + } + + struct { + const char *name; + const char *shortname; + int *dest; + } colors[] = { + { "foreground", "fg", &fg }, + { "background", "bg", &bg }, + { "special", "sp", &sp }, + { NULL, NULL, NULL }, + }; + + int k; + for (k = 0; (!flags[j].name) && colors[k].name; k++) { + if (strequal(colors[k].name, key) || strequal(colors[k].shortname, key)) { + if (val.type == kObjectTypeInteger) { + *colors[k].dest = (int)val.data.integer; + } else if (val.type == kObjectTypeString) { + String str = val.data.string; + // TODO(bfredl): be more fancy with "bg", "fg" etc + if (str.size) { + *colors[k].dest = name_to_color(str.data); + } + } else { + api_set_error(err, kErrorTypeValidation, + "'%s' must be string or integer", key); + } + break; + } + } + + + if (flags[j].name || colors[k].name) { + // handled above + } else if (link_id && strequal(key, "link")) { + if (val.type == kObjectTypeString) { + String str = val.data.string; + *link_id = syn_check_group((const char_u *)str.data, (int)str.size); + } else if (val.type == kObjectTypeInteger) { + // TODO(bfredl): validate range? + *link_id = (int)val.data.integer; + } else { + api_set_error(err, kErrorTypeValidation, + "'link' must be string or integer"); + } + } + + if (ERROR_SET(err)) { + return hlattrs; // error set, caller should not use retval + } + } + + if (use_rgb) { + hlattrs.rgb_ae_attr = mask; + hlattrs.rgb_bg_color = bg; + hlattrs.rgb_fg_color = fg; + hlattrs.rgb_sp_color = sp; + } else { + hlattrs.cterm_ae_attr = mask; + hlattrs.cterm_bg_color = bg == -1 ? cterm_normal_bg_color : bg + 1; + hlattrs.cterm_fg_color = fg == -1 ? cterm_normal_fg_color : fg + 1; + } + + return hlattrs; +} Array hl_inspect(int attr) { Array ret = ARRAY_DICT_INIT; diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index 36f3181674..6a5c593ab1 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -4,6 +4,7 @@ #include <inttypes.h> #include "nvim/macros.h" +#include "nvim/types.h" typedef int32_t RgbValue; @@ -180,6 +181,20 @@ typedef struct { HlKind kind; int id1; int id2; + int winid; } HlEntry; +typedef struct { + int ns_id; + int syn_id; +} ColorKey; + +typedef struct { + int attr_id; + int link_id; + int version; +} ColorItem; +#define COLOR_ITEM_INITIALIZER { .attr_id = -1, .link_id = -1, .version = -1 } + + #endif // NVIM_HIGHLIGHT_DEFS_H diff --git a/src/nvim/indent.c b/src/nvim/indent.c index bb0fdfec01..9e6693afdf 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -295,13 +295,17 @@ int set_indent(int size, int flags) // Replace the line (unless undo fails). if (!(flags & SIN_UNDO) || (u_savesub(curwin->w_cursor.lnum) == OK)) { + const colnr_T old_offset = (colnr_T)(p - oldline); + const colnr_T new_offset = (colnr_T)(s - newline); + + // this may free "newline" ml_replace(curwin->w_cursor.lnum, newline, false); if (!(flags & SIN_NOMARK)) { extmark_splice_cols(curbuf, (int)curwin->w_cursor.lnum-1, skipcols, - (int)(p-oldline) - skipcols, - (int)(s-newline) - skipcols, + old_offset - skipcols, + new_offset - skipcols, kExtmarkUndo); } @@ -311,15 +315,14 @@ int set_indent(int size, int flags) // Correct saved cursor position if it is in this line. if (saved_cursor.lnum == curwin->w_cursor.lnum) { - if (saved_cursor.col >= (colnr_T)(p - oldline)) { + if (saved_cursor.col >= old_offset) { // Cursor was after the indent, adjust for the number of // bytes added/removed. - saved_cursor.col += ind_len - (colnr_T)(p - oldline); - - } else if (saved_cursor.col >= (colnr_T)(s - newline)) { + saved_cursor.col += ind_len - old_offset; + } else if (saved_cursor.col >= new_offset) { // Cursor was in the indent, and is now after it, put it back // at the start of the indent (replacing spaces with TAB). - saved_cursor.col = (colnr_T)(s - newline); + saved_cursor.col = new_offset; } } retval = true; diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index bb443161ef..9298e57411 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -1676,6 +1676,9 @@ void parse_cino(buf_T *buf) // Handle C++ extern "C" or "C++" buf->b_ind_cpp_extern_c = 0; + // Handle C #pragma directives + buf->b_ind_pragma = 0; + for (p = buf->b_p_cino; *p; ) { l = p++; if (*p == '-') { @@ -1747,6 +1750,7 @@ void parse_cino(buf_T *buf) case 'N': buf->b_ind_cpp_namespace = n; break; case 'k': buf->b_ind_if_for_while = n; break; case 'E': buf->b_ind_cpp_extern_c = n; break; + case 'P': buf->b_ind_pragma = n; break; } if (*p == ',') ++p; @@ -1858,12 +1862,14 @@ int get_c_indent(void) goto laterend; } - /* - * #defines and so on always go at the left when included in 'cinkeys'. - */ + // #defines and so on go at the left when included in 'cinkeys', + // exluding pragmas when customized in 'cinoptions' if (*theline == '#' && (*linecopy == '#' || in_cinkeys('#', ' ', true))) { - amount = curbuf->b_ind_hash_comment; - goto theend; + const char_u *const directive = skipwhite(theline + 1); + if (curbuf->b_ind_pragma == 0 || STRNCMP(directive, "pragma", 6) != 0) { + amount = curbuf->b_ind_hash_comment; + goto theend; + } } /* diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 5c665920b5..a095f298f2 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -549,14 +549,6 @@ static lua_State *nlua_enter(void) // stack: (empty) lua_getglobal(lstate, "vim"); // stack: vim - lua_getfield(lstate, -1, "_update_package_paths"); - // stack: vim, vim._update_package_paths - if (lua_pcall(lstate, 0, 0, 0)) { - // stack: vim, error - nlua_error(lstate, _("E5117: Error while updating package paths: %.*s")); - // stack: vim - } - // stack: vim lua_pop(lstate, 1); // stack: (empty) last_p_rtp = (const void *)p_rtp; @@ -564,14 +556,6 @@ static lua_State *nlua_enter(void) return lstate; } -/// Force an update of lua's package paths if runtime path has changed. -bool nlua_update_package_path(void) -{ - lua_State *const lstate = nlua_enter(); - - return !!lstate; -} - static void nlua_print_event(void **argv) { char *str = argv[0]; @@ -891,6 +875,17 @@ LuaRef nlua_newref(lua_State *lstate, LuaRef original_ref) return new_ref; } +LuaRef api_new_luaref(LuaRef original_ref) +{ + if (original_ref == LUA_NOREF) { + return LUA_NOREF; + } + + lua_State *const lstate = nlua_enter(); + return nlua_newref(lstate, original_ref); +} + + /// Evaluate lua string /// /// Used for luaeval(). diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index 5258352e72..c53e208b00 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -58,6 +58,7 @@ static struct luaL_Reg node_meta[] = { { "__tostring", node_tostring }, { "__eq", node_eq }, { "__len", node_child_count }, + { "id", node_id }, { "range", node_range }, { "start", node_start }, { "end_", node_end }, @@ -176,10 +177,11 @@ int tslua_add_language(lua_State *L) } uint32_t lang_version = ts_language_version(lang); - if (lang_version < TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION) { + if (lang_version < TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION + || lang_version > TREE_SITTER_LANGUAGE_VERSION) { return luaL_error( L, - "ABI version mismatch : expected %" PRIu32 ", found %" PRIu32, + "ABI version mismatch : expected %d, found %d", TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION, lang_version); } @@ -246,7 +248,12 @@ int tslua_push_parser(lua_State *L) } TSParser *parser = ts_parser_new(); - ts_parser_set_language(parser, lang); + + if (!ts_parser_set_language(parser, lang)) { + ts_parser_delete(parser); + return luaL_error(L, "Failed to load language : %s", lang_name); + } + TSLua_parser *p = lua_newuserdata(L, sizeof(TSLua_parser)); // [udata] p->parser = parser; p->tree = NULL; @@ -342,7 +349,7 @@ static int parser_parse(lua_State *L) return 0; } - TSTree *new_tree; + TSTree *new_tree = NULL; size_t len; const char *str; long bufnr; @@ -374,6 +381,12 @@ static int parser_parse(lua_State *L) return luaL_error(L, "invalid argument to parser:parse()"); } + // Sometimes parsing fails (timeout, or wrong parser ABI) + // In those case, just return an error. + if (!new_tree) { + return luaL_error(L, "An error occured when parsing."); + } + uint32_t n_ranges = 0; TSRange *changed = p->tree ? ts_tree_get_changed_ranges(p->tree, new_tree, &n_ranges) : NULL; @@ -621,6 +634,17 @@ static int node_eq(lua_State *L) return 1; } +static int node_id(lua_State *L) +{ + TSNode node; + if (!node_check(L, 1, &node)) { + return 0; + } + + lua_pushlstring(L, (const char *)&node.id, sizeof node.id); + return 1; +} + static int node_range(lua_State *L) { TSNode node; diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index bfa8b91208..85d39eaef4 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -92,67 +92,48 @@ function vim._os_proc_children(ppid) return children end --- TODO(ZyX-I): Create compatibility layer. ---{{{1 package.path updater function --- Last inserted paths. Used to clear out items from package.[c]path when they --- are no longer in &runtimepath. -local last_nvim_paths = {} -function vim._update_package_paths() - local cur_nvim_paths = {} - local rtps = vim.api.nvim_list_runtime_paths() - local sep = package.config:sub(1, 1) - for _, key in ipairs({'path', 'cpath'}) do - local orig_str = package[key] .. ';' - local pathtrails_ordered = {} - local orig = {} - -- Note: ignores trailing item without trailing `;`. Not using something - -- simpler in order to preserve empty items (stand for default path). - for s in orig_str:gmatch('[^;]*;') do - s = s:sub(1, -2) -- Strip trailing semicolon - orig[#orig + 1] = s - end - if key == 'path' then - -- /?.lua and /?/init.lua - pathtrails_ordered = {sep .. '?.lua', sep .. '?' .. sep .. 'init.lua'} - else - local pathtrails = {} - for _, s in ipairs(orig) do - -- Find out path patterns. pathtrail should contain something like - -- /?.so, \?.dll. This allows not to bother determining what correct - -- suffixes are. - local pathtrail = s:match('[/\\][^/\\]*%?.*$') - if pathtrail and not pathtrails[pathtrail] then - pathtrails[pathtrail] = true - pathtrails_ordered[#pathtrails_ordered + 1] = pathtrail - end - end - end - local new = {} - for _, rtp in ipairs(rtps) do - if not rtp:match(';') then - for _, pathtrail in pairs(pathtrails_ordered) do - local new_path = rtp .. sep .. 'lua' .. pathtrail - -- Always keep paths from &runtimepath at the start: - -- append them here disregarding orig possibly containing one of them. - new[#new + 1] = new_path - cur_nvim_paths[new_path] = true - end - end +local pathtrails = {} +vim._so_trails = {} +for s in (package.cpath..';'):gmatch('[^;]*;') do + s = s:sub(1, -2) -- Strip trailing semicolon + -- Find out path patterns. pathtrail should contain something like + -- /?.so, \?.dll. This allows not to bother determining what correct + -- suffixes are. + local pathtrail = s:match('[/\\][^/\\]*%?.*$') + if pathtrail and not pathtrails[pathtrail] then + pathtrails[pathtrail] = true + table.insert(vim._so_trails, pathtrail) + end +end + +function vim._load_package(name) + -- tricky: when debugging this function we must let vim.inspect + -- module to be loaded first: + --local inspect = (name == "vim.inspect") and tostring or vim.inspect + + local basename = name:gsub('%.', '/') + local paths = {"lua/"..basename..".lua", "lua/"..basename.."/init.lua"} + for _,path in ipairs(paths) do + local found = vim.api.nvim_get_runtime_file(path, false) + if #found > 0 then + return loadfile(found[1]) end - for _, orig_path in ipairs(orig) do - -- Handle removing obsolete paths originating from &runtimepath: such - -- paths either belong to cur_nvim_paths and were already added above or - -- to last_nvim_paths and should not be added at all if corresponding - -- entry was removed from &runtimepath list. - if not (cur_nvim_paths[orig_path] or last_nvim_paths[orig_path]) then - new[#new + 1] = orig_path - end + end + + for _,trail in ipairs(vim._so_trails) do + local path = "lua/"..trail:gsub('?',basename) + local found = vim.api.nvim_get_runtime_file(path, false) + if #found > 0 then + return package.loadlib(found[1]) end - package[key] = table.concat(new, ';') end - last_nvim_paths = cur_nvim_paths + return nil end +table.insert(package.loaders, 1, vim._load_package) + +-- TODO(ZyX-I): Create compatibility layer. + --- Return a human-readable representation of the given object. --- --@see https://github.com/kikito/inspect.lua diff --git a/src/nvim/main.c b/src/nvim/main.c index a22df9cc69..27bf5c45c6 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -446,7 +446,7 @@ int main(int argc, char **argv) if (exmode_active || use_remote_ui || use_builtin_ui) { // Don't clear the screen when starting in Ex mode, or when a UI might have // displayed messages. - redraw_later(VALID); + redraw_later(curwin, VALID); } else { screenclear(); // clear screen TIME_MSG("clearing screen"); diff --git a/src/nvim/map.c b/src/nvim/map.c index ca8ea76333..7d97b7f13d 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -12,6 +12,9 @@ #include <stdbool.h> #include <string.h> +#include <lua.h> +#include <lauxlib.h> + #include "nvim/map.h" #include "nvim/map_defs.h" #include "nvim/vim.h" @@ -173,6 +176,20 @@ static inline bool HlEntry_eq(HlEntry ae1, HlEntry ae2) return memcmp(&ae1, &ae2, sizeof(ae1)) == 0; } +static inline khint_t ColorKey_hash(ColorKey ae) +{ + const uint8_t *data = (const uint8_t *)&ae; + khint_t h = 0; + for (size_t i = 0; i < sizeof(ae); i++) { + h = (h << 5) - h + data[i]; + } + return h; +} + +static inline bool ColorKey_eq(ColorKey ae1, ColorKey ae2) +{ + return memcmp(&ae1, &ae2, sizeof(ae1)) == 0; +} MAP_IMPL(int, int, DEFAULT_INITIALIZER) @@ -191,6 +208,7 @@ MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER) MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER) MAP_IMPL(String, handle_T, 0) +MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER) /// Deletes a key:value pair from a string:pointer map, and frees the /// storage of both key and value. diff --git a/src/nvim/map.h b/src/nvim/map.h index 63a18f4129..7bd3d31330 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -55,6 +55,8 @@ MAP_DECLS(String, MsgpackRpcRequestHandler) MAP_DECLS(HlEntry, int) MAP_DECLS(String, handle_T) +MAP_DECLS(ColorKey, ColorItem) + #define map_new(T, U) map_##T##_##U##_new #define map_free(T, U) map_##T##_##U##_free #define map_get(T, U) map_##T##_##U##_get diff --git a/src/nvim/menu.c b/src/nvim/menu.c index b060464383..4ba2e36656 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -193,7 +193,7 @@ ex_menu(exarg_T *eap) vimmenu_T **root_menu_ptr = get_root_menu(menu_path); if (root_menu_ptr == &curwin->w_winbar) { // Assume the window toolbar menu will change. - redraw_later(NOT_VALID); + redraw_later(curwin, NOT_VALID); } if (enable != kNone) { diff --git a/src/nvim/message.c b/src/nvim/message.c index 6cd5616acf..06ba607323 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1216,7 +1216,7 @@ void wait_return(int redraw) ui_refresh(); } else if (!skip_redraw) { if (redraw == true || (msg_scrolled != 0 && redraw != -1)) { - redraw_later(VALID); + redraw_later(curwin, VALID); } if (ui_has(kUIMessages)) { msg_ext_clear(true); @@ -2222,7 +2222,7 @@ void msg_scroll_up(bool may_throttle) /// /// Probably message scrollback storage should reimplented as a file_buffer, and /// message scrolling in TUI be reimplemented as a modal floating window. Then -/// we get throttling "for free" using standard redraw_win_later code paths. +/// we get throttling "for free" using standard redraw_later code paths. void msg_scroll_flush(void) { if (msg_grid.throttled) { diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index fcd9ee4f75..d8ff83f479 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -286,7 +286,7 @@ retnomove: check_topfill(curwin, false); curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); - redraw_later(VALID); + redraw_later(curwin, VALID); row = 0; } else if (row >= curwin->w_height_inner) { count = 0; @@ -316,7 +316,7 @@ retnomove: } } check_topfill(curwin, false); - redraw_later(VALID); + redraw_later(curwin, VALID); curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); row = curwin->w_height_inner - 1; diff --git a/src/nvim/move.c b/src/nvim/move.c index e2a304efa5..98a7792a09 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -107,7 +107,7 @@ void redraw_for_cursorline(win_T *wp) && !pum_visible()) { if (wp->w_p_rnu) { // win_line() will redraw the number column only. - redraw_win_later(wp, VALID); + redraw_later(wp, VALID); } if (win_cursorline_standout(wp)) { if (wp->w_redr_type <= VALID && wp->w_last_cursorline != 0) { @@ -117,7 +117,7 @@ void redraw_for_cursorline(win_T *wp) redrawWinline(wp, wp->w_last_cursorline); redrawWinline(wp, wp->w_cursor.lnum); } else { - redraw_win_later(wp, SOME_VALID); + redraw_later(wp, SOME_VALID); } } } @@ -172,7 +172,7 @@ void update_topline(void) // If the buffer is empty, always set topline to 1. if (BUFEMPTY()) { // special case - file is empty if (curwin->w_topline != 1) { - redraw_later(NOT_VALID); + redraw_later(curwin, NOT_VALID); } curwin->w_topline = 1; curwin->w_botline = 2; @@ -326,12 +326,14 @@ void update_topline(void) dollar_vcol = -1; if (curwin->w_skipcol != 0) { curwin->w_skipcol = 0; - redraw_later(NOT_VALID); - } else - redraw_later(VALID); - /* May need to set w_skipcol when cursor in w_topline. */ - if (curwin->w_cursor.lnum == curwin->w_topline) + redraw_later(curwin, NOT_VALID); + } else { + redraw_later(curwin, VALID); + } + // May need to set w_skipcol when cursor in w_topline. + if (curwin->w_cursor.lnum == curwin->w_topline) { validate_cursor(); + } } *so_ptr = save_so; @@ -439,7 +441,7 @@ void changed_window_setting_win(win_T *wp) wp->w_lines_valid = 0; changed_line_abv_curs_win(wp); wp->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP|VALID_TOPLINE); - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); } /* @@ -455,8 +457,8 @@ void set_topline(win_T *wp, linenr_T lnum) wp->w_topline_was_set = true; wp->w_topfill = 0; wp->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_TOPLINE); - /* Don't set VALID_TOPLINE here, 'scrolloff' needs to be checked. */ - redraw_later(VALID); + // Don't set VALID_TOPLINE here, 'scrolloff' needs to be checked. + redraw_later(wp, VALID); } /* @@ -634,7 +636,7 @@ void validate_virtcol_win(win_T *wp) if (wp->w_p_cuc && !pum_visible() ) - redraw_win_later(wp, SOME_VALID); + redraw_later(wp, SOME_VALID); } } @@ -830,7 +832,7 @@ void curs_columns( curwin->w_leftcol = new_leftcol; win_check_anchored_floats(curwin); // screen has to be redrawn with new curwin->w_leftcol - redraw_later(NOT_VALID); + redraw_later(curwin, NOT_VALID); } } curwin->w_wcol -= curwin->w_leftcol; @@ -934,13 +936,14 @@ void curs_columns( } else { curwin->w_skipcol = 0; } - if (prev_skipcol != curwin->w_skipcol) - redraw_later(NOT_VALID); + if (prev_skipcol != curwin->w_skipcol) { + redraw_later(curwin, NOT_VALID); + } /* Redraw when w_virtcol changes and 'cursorcolumn' is set */ if (curwin->w_p_cuc && (curwin->w_valid & VALID_VIRTCOL) == 0 && !pum_visible()) { - redraw_later(SOME_VALID); + redraw_later(curwin, SOME_VALID); } // now w_leftcol is valid, avoid check_cursor_moved() thinking otherwise @@ -2013,7 +2016,7 @@ int onepage(Direction dir, long count) } } - redraw_later(VALID); + redraw_later(curwin, VALID); return retval; } @@ -2199,7 +2202,7 @@ void halfpage(bool flag, linenr_T Prenum) check_topfill(curwin, !flag); cursor_correct(); beginline(BL_SOL | BL_FIX); - redraw_later(VALID); + redraw_later(curwin, VALID); } void do_check_cursorbind(void) @@ -2247,7 +2250,7 @@ void do_check_cursorbind(void) } // Correct cursor for multi-byte character. mb_adjust_cursor(); - redraw_later(VALID); + redraw_later(curwin, VALID); // Only scroll when 'scrollbind' hasn't done this. if (!curwin->w_p_scb) { diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 92ca29209e..68ef4cd41e 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -262,11 +262,9 @@ static void parse_msgpack(Channel *channel) call_set_error(channel, buf, ERROR_LOG_LEVEL); } msgpack_unpacked_destroy(&unpacked); - // Bail out from this event loop iteration - return; + } else { + handle_request(channel, &unpacked.data); } - - handle_request(channel, &unpacked.data); } if (result == MSGPACK_UNPACK_NOMEM_ERROR) { diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 1cc400166c..11e8d354e4 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -903,6 +903,10 @@ normal_end: msg_nowait = false; + if (finish_op) { + set_reg_var(get_default_register_name()); + } + // Reset finish_op, in case it was set s->c = finish_op; finish_op = false; @@ -3601,7 +3605,7 @@ void check_scrollbind(linenr_T topline_diff, long leftcol_diff) scrolldown(-y, false); } - redraw_later(VALID); + redraw_later(curwin, VALID); cursor_correct(); curwin->w_redr_status = true; } @@ -4143,7 +4147,7 @@ void scroll_redraw(int up, long count) if (curwin->w_cursor.lnum != prev_lnum) coladvance(curwin->w_curswant); curwin->w_viewport_invalid = true; - redraw_later(VALID); + redraw_later(curwin, VALID); } /* @@ -4241,7 +4245,7 @@ dozet: FALLTHROUGH; case 't': scroll_cursor_top(0, true); - redraw_later(VALID); + redraw_later(curwin, VALID); set_fraction(curwin); break; @@ -4250,7 +4254,7 @@ dozet: FALLTHROUGH; case 'z': scroll_cursor_halfway(true); - redraw_later(VALID); + redraw_later(curwin, VALID); set_fraction(curwin); break; @@ -4271,7 +4275,7 @@ dozet: FALLTHROUGH; case 'b': scroll_cursor_bot(0, true); - redraw_later(VALID); + redraw_later(curwin, VALID); set_fraction(curwin); break; @@ -4318,7 +4322,7 @@ dozet: col = 0; if (curwin->w_leftcol != col) { curwin->w_leftcol = col; - redraw_later(NOT_VALID); + redraw_later(curwin, NOT_VALID); } } break; @@ -4337,7 +4341,7 @@ dozet: } if (curwin->w_leftcol != col) { curwin->w_leftcol = col; - redraw_later(NOT_VALID); + redraw_later(curwin, NOT_VALID); } } break; @@ -4690,7 +4694,7 @@ static void nv_clear(cmdarg_T *cap) FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { wp->w_s->b_syn_slow = false; } - redraw_later(CLEAR); + redraw_later(curwin, CLEAR); } } diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 939cde0ba1..9e7d81fc82 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2652,7 +2652,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) xfree(reg->y_array); } if (curwin->w_p_rnu) { - redraw_later(SOME_VALID); // cursor moved to start + redraw_later(curwin, SOME_VALID); // cursor moved to start } if (message) { // Display message about yank? if (yank_type == kMTCharWise && yanklines == 1) { diff --git a/src/nvim/option.c b/src/nvim/option.c index ca902d5669..fcc051ef1a 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2566,7 +2566,7 @@ static bool valid_spellfile(const char_u *val) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { for (const char_u *s = val; *s != NUL; s++) { - if (!vim_isfilec(*s) && *s != ',') { + if (!vim_isfilec(*s) && *s != ',' && *s != ' ') { return false; } } @@ -3121,7 +3121,7 @@ ambw_end: } else { if (curwin->w_status_height) { curwin->w_redr_status = true; - redraw_later(VALID); + redraw_later(curwin, VALID); } curbuf->b_help = (curbuf->b_p_bt[0] == 'h'); redraw_titles(); @@ -3364,10 +3364,6 @@ ambw_end: if (!parse_winhl_opt(curwin)) { errmsg = e_invarg; } - } else if (varp == &p_rtp) { // 'runtimepath' - if (!nlua_update_package_path()) { - errmsg = (char_u *)N_("E970: Failed to initialize lua interpreter"); - } } else { // Options that are a list of flags. p = NULL; @@ -3751,11 +3747,10 @@ static char_u *set_chars_option(win_T *wp, char_u **varp, bool set) /// Return error message or NULL. char_u *check_stl_option(char_u *s) { - int itemcnt = 0; int groupdepth = 0; static char_u errbuf[80]; - while (*s && itemcnt < STL_MAX_ITEM) { + while (*s) { // Check for valid keys after % sequences while (*s && *s != '%') { s++; @@ -3764,9 +3759,6 @@ char_u *check_stl_option(char_u *s) break; } s++; - if (*s != '%' && *s != ')') { - itemcnt++; - } if (*s == '%' || *s == STL_TRUNCMARK || *s == STL_SEPARATE) { s++; continue; @@ -3808,9 +3800,6 @@ char_u *check_stl_option(char_u *s) } } } - if (itemcnt >= STL_MAX_ITEM) { - return (char_u *)N_("E541: too many items"); - } if (groupdepth != 0) { return (char_u *)N_("E542: unbalanced groups"); } @@ -4702,7 +4691,7 @@ static void check_redraw(uint32_t flags) redraw_curbuf_later(NOT_VALID); } if (flags & P_RWINONLY) { - redraw_later(NOT_VALID); + redraw_later(curwin, NOT_VALID); } if (doclear) { redraw_all_later(CLEAR); @@ -5716,12 +5705,12 @@ void unset_global_local_option(char *name, void *from) case PV_LCS: clear_string_option(&((win_T *)from)->w_p_lcs); set_chars_option((win_T *)from, &((win_T *)from)->w_p_lcs, true); - redraw_win_later((win_T *)from, NOT_VALID); + redraw_later((win_T *)from, NOT_VALID); break; case PV_FCS: clear_string_option(&((win_T *)from)->w_p_fcs); set_chars_option((win_T *)from, &((win_T *)from)->w_p_fcs, true); - redraw_win_later((win_T *)from, NOT_VALID); + redraw_later((win_T *)from, NOT_VALID); break; } } diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 4042b79262..af0ea7f4a2 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -188,6 +188,7 @@ enum { #define GO_ASELML 'A' // autoselect modeless selection #define GO_BOT 'b' // use bottom scrollbar #define GO_CONDIALOG 'c' // use console dialog +#define GO_DARKTHEME 'd' // use dark theme variant #define GO_TABLINE 'e' // may show tabline #define GO_FORG 'f' // start GUI in foreground #define GO_GREY 'g' // use grey menu items @@ -205,7 +206,7 @@ enum { #define GO_FOOTER 'F' // add footer #define GO_VERTICAL 'v' // arrange dialog buttons vertically #define GO_KEEPWINSIZE 'k' // keep GUI window size -#define GO_ALL "aAbcefFghilmMprTvk" // all possible flags for 'go' +#define GO_ALL "aAbcdefFghilmMprTvk" // all possible flags for 'go' // flags for 'comments' option #define COM_NEST 'n' // comments strings nest diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h index c8ac4218f6..bd5f2b889d 100644 --- a/src/nvim/os/os_defs.h +++ b/src/nvim/os/os_defs.h @@ -16,10 +16,11 @@ #define BASENAMELEN (NAME_MAX - 5) // Use the system path length if it makes sense. -#if defined(PATH_MAX) && (PATH_MAX > 1024) +# define DEFAULT_MAXPATHL 4096 +#if defined(PATH_MAX) && (PATH_MAX > DEFAULT_MAXPATHL) # define MAXPATHL PATH_MAX #else -# define MAXPATHL 1024 +# define MAXPATHL DEFAULT_MAXPATHL #endif // Command-processing buffer. Use large buffers for all platforms. diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index d2d20852aa..ac0c1f6eb1 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -786,7 +786,7 @@ static int pum_set_selected(int n, int repeat) // Return cursor to where we were validate_cursor(); - redraw_later(SOME_VALID); + redraw_later(curwin, SOME_VALID); // When the preview window was resized we need to // update the view on the buffer. Only go back to diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 1cd7879dc0..6ca97c3072 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3707,7 +3707,7 @@ static void qf_win_goto(win_T *win, linenr_T lnum) curwin->w_cursor.coladd = 0; curwin->w_curswant = 0; update_topline(); // scroll to show the line - redraw_later(VALID); + redraw_later(curwin, VALID); curwin->w_redr_status = true; // update ruler curwin = old_curwin; curbuf = curwin->w_buffer; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index df0c5ce791..36a6ae59f8 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -25,7 +25,7 @@ // // Commands that scroll a window change w_topline and must call // check_cursor() to move the cursor into the visible part of the window, and -// call redraw_later(VALID) to have the window displayed by update_screen() +// call redraw_later(wp, VALID) to have the window displayed by update_screen() // later. // // Commands that change text in the buffer must call changed_bytes() or @@ -37,7 +37,7 @@ // // Commands that change how a window is displayed (e.g., setting 'list') or // invalidate the contents of a window in another way (e.g., change fold -// settings), must call redraw_later(NOT_VALID) to have the whole window +// settings), must call redraw_later(wp, NOT_VALID) to have the whole window // redisplayed by update_screen() later. // // Commands that change how a buffer is displayed (e.g., setting 'tabstop') @@ -45,11 +45,11 @@ // buffer redisplayed by update_screen() later. // // Commands that change highlighting and possibly cause a scroll too must call -// redraw_later(SOME_VALID) to update the whole window but still use scrolling -// to avoid redrawing everything. But the length of displayed lines must not -// change, use NOT_VALID then. +// redraw_later(wp, SOME_VALID) to update the whole window but still use +// scrolling to avoid redrawing everything. But the length of displayed lines +// must not change, use NOT_VALID then. // -// Commands that move the window position must call redraw_later(NOT_VALID). +// Commands that move the window position must call redraw_later(wp, NOT_VALID). // TODO(neovim): should minimize redrawing by scrolling when possible. // // Commands that change everything (e.g., resizing the screen) must call @@ -176,7 +176,7 @@ static bool provider_invoke(NS ns_id, const char *name, LuaRef ref, textlock--; if (!ERROR_SET(&err) - && api_coerce_to_bool(ret, "provider %s retval", default_true, &err)) { + && api_object_to_bool(ret, "provider %s retval", default_true, &err)) { return true; } @@ -195,17 +195,11 @@ static bool provider_invoke(NS ns_id, const char *name, LuaRef ref, return false; } -/* - * Redraw the current window later, with update_screen(type). - * Set must_redraw only if not already set to a higher value. - * e.g. if must_redraw is CLEAR, type NOT_VALID will do nothing. - */ -void redraw_later(int type) -{ - redraw_win_later(curwin, type); -} - -void redraw_win_later(win_T *wp, int type) +/// Redraw a window later, with update_screen(type). +/// +/// Set must_redraw only if not already set to a higher value. +/// e.g. if must_redraw is CLEAR, type NOT_VALID will do nothing. +void redraw_later(win_T *wp, int type) FUNC_ATTR_NONNULL_ALL { if (!exiting && wp->w_redr_type < type) { @@ -223,7 +217,7 @@ void redraw_win_later(win_T *wp, int type) void redraw_all_later(int type) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - redraw_win_later(wp, type); + redraw_later(wp, type); } // This may be needed when switching tabs. if (must_redraw < type) { @@ -234,7 +228,7 @@ void redraw_all_later(int type) void screen_invalidate_highlights(void) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); wp->w_grid.valid = false; } } @@ -251,7 +245,7 @@ void redraw_buf_later(buf_T *buf, int type) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_buffer == buf) { - redraw_win_later(wp, type); + redraw_later(wp, type); } } } @@ -277,7 +271,7 @@ void redraw_buf_range_later(buf_T *buf, linenr_T firstline, linenr_T lastline) if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lastline) { wp->w_redraw_bot = lastline; } - redraw_win_later(wp, VALID); + redraw_later(wp, VALID); } } } @@ -305,7 +299,7 @@ redrawWinline( if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) { wp->w_redraw_bot = lnum; } - redraw_win_later(wp, VALID); + redraw_later(wp, VALID); } } @@ -359,7 +353,6 @@ int update_screen(int type) /* Postpone the redrawing when it's not needed and when being called * recursively. */ if (!redrawing() || updating_screen) { - redraw_later(type); /* remember type for next time */ must_redraw = type; if (type > INVERTED_ALL) { curwin->w_lines_valid = 0; // don't use w_lines[].wl_size now @@ -501,6 +494,12 @@ int update_screen(int type) } } + // "start" callback could have changed highlights for global elements + if (win_check_ns_hl(NULL)) { + redraw_cmdline = true; + redraw_tabline = true; + } + if (clear_cmdline) /* going to clear cmdline (done below) */ check_for_delay(FALSE); @@ -1331,6 +1330,8 @@ static void win_update(win_T *wp, Providers *providers) } } + win_check_ns_hl(wp); + for (;; ) { /* stop updating when reached the end of the window (check for _past_ @@ -2185,6 +2186,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, // return 'false' or error: skip rest of this window kv_A(*providers, k) = NULL; } + + win_check_ns_hl(wp); } } @@ -4649,8 +4652,8 @@ void status_redraw_all(void) FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_status_height) { - wp->w_redr_status = TRUE; - redraw_later(VALID); + wp->w_redr_status = true; + redraw_later(wp, VALID); } } } @@ -4667,7 +4670,7 @@ void status_redraw_buf(buf_T *buf) FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_status_height != 0 && wp->w_buffer == buf) { wp->w_redr_status = true; - redraw_later(VALID); + redraw_later(wp, VALID); } } } @@ -5191,8 +5194,8 @@ win_redr_custom ( char_u buf[MAXPATHL]; char_u *stl; char_u *p; - struct stl_hlrec hltab[STL_MAX_ITEM]; - StlClickRecord tabtab[STL_MAX_ITEM]; + stl_hlrec_t *hltab; + StlClickRecord *tabtab; int use_sandbox = false; win_T *ewp; int p_crb_save; @@ -5270,9 +5273,9 @@ win_redr_custom ( /* Make a copy, because the statusline may include a function call that * might change the option value and free the memory. */ stl = vim_strsave(stl); - width = build_stl_str_hl(ewp, buf, sizeof(buf), - stl, use_sandbox, - fillchar, maxwidth, hltab, tabtab); + width = + build_stl_str_hl(ewp, buf, sizeof(buf), stl, use_sandbox, + fillchar, maxwidth, &hltab, &tabtab); xfree(stl); ewp->w_p_crb = p_crb_save; diff --git a/src/nvim/search.c b/src/nvim/search.c index 9458e6333f..b25333c9fa 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -4923,7 +4923,7 @@ search_line: && curwin != curwin_save && win_valid(curwin_save)) { /* Return cursor to where we were */ validate_cursor(); - redraw_later(VALID); + redraw_later(curwin, VALID); win_enter(curwin_save, true); } break; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 1984a357c3..636c71657d 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -2258,7 +2258,7 @@ char_u *did_set_spelllang(win_T *wp) theend: xfree(spl_copy); recursive = false; - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); return ret_msg; } @@ -6877,7 +6877,7 @@ void ex_spelldump(exarg_T *eap) if (curbuf->b_ml.ml_line_count > 1) { ml_delete(curbuf->b_ml.ml_line_count, false); } - redraw_later(NOT_VALID); + redraw_later(curwin, NOT_VALID); } // Go through all possible words and: diff --git a/src/nvim/spell_defs.h b/src/nvim/spell_defs.h index 034c580b3e..05667f060e 100644 --- a/src/nvim/spell_defs.h +++ b/src/nvim/spell_defs.h @@ -119,6 +119,7 @@ struct slang_S { bool sl_add; // true if it's a .add file. char_u *sl_fbyts; // case-folded word bytes + long sl_fbyts_len; // length of sl_fbyts idx_T *sl_fidxs; // case-folded word indexes char_u *sl_kbyts; // keep-case word bytes idx_T *sl_kidxs; // keep-case word indexes diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 09d8646c6d..b415a4635b 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -764,20 +764,24 @@ truncerr: } // <LWORDTREE> - res = spell_read_tree(fd, &lp->sl_fbyts, &lp->sl_fidxs, false, 0); - if (res != 0) + res = spell_read_tree(fd, &lp->sl_fbyts, &lp->sl_fbyts_len, + &lp->sl_fidxs, false, 0); + if (res != 0) { goto someerror; + } // <KWORDTREE> - res = spell_read_tree(fd, &lp->sl_kbyts, &lp->sl_kidxs, false, 0); - if (res != 0) + res = spell_read_tree(fd, &lp->sl_kbyts, NULL, &lp->sl_kidxs, false, 0); + if (res != 0) { goto someerror; + } // <PREFIXTREE> - res = spell_read_tree(fd, &lp->sl_pbyts, &lp->sl_pidxs, true, - lp->sl_prefixcnt); - if (res != 0) + res = spell_read_tree(fd, &lp->sl_pbyts, NULL, &lp->sl_pidxs, true, + lp->sl_prefixcnt); + if (res != 0) { goto someerror; + } // For a new file link it in the list of spell files. if (old_lp == NULL && lang != NULL) { @@ -920,8 +924,8 @@ void suggest_load_files(void) // <SUGWORDTREE>: <wordtree> // Read the trie with the soundfolded words. - if (spell_read_tree(fd, &slang->sl_sbyts, &slang->sl_sidxs, - false, 0) != 0) { + if (spell_read_tree(fd, &slang->sl_sbyts, NULL, &slang->sl_sidxs, + false, 0) != 0) { someerror: EMSG2(_("E782: error while reading .sug file: %s"), slang->sl_fname); @@ -1630,10 +1634,12 @@ static int spell_read_tree ( FILE *fd, char_u **bytsp, + long *bytsp_len, idx_T **idxsp, bool prefixtree, // true for the prefix tree int prefixcnt // when "prefixtree" is true: prefix count ) + FUNC_ATTR_NONNULL_ARG(1, 2, 4) { int idx; char_u *bp; @@ -1653,6 +1659,9 @@ spell_read_tree ( // Allocate the byte array. bp = xmalloc(len); *bytsp = bp; + if (bytsp_len != NULL) { + *bytsp_len = len; + } // Allocate the index array. ip = xcalloc(len, sizeof(*ip)); @@ -4850,10 +4859,10 @@ static int sug_filltree(spellinfo_T *spin, slang_T *slang) spin->si_blocks_cnt = 0; // Skip over any other NUL bytes (same word with different - // flags). - while (byts[n + 1] == 0) { - ++n; - ++curi[depth]; + // flags). But don't go over the end. + while (n + 1 < slang->sl_fbyts_len && byts[n + 1] == 0) { + n++; + curi[depth]++; } } else { // Normal char, go one level deeper. diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 9a9cc45c6b..ec6accd473 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -73,9 +73,9 @@ struct hl_group { RgbValue sg_rgb_fg; ///< RGB foreground color RgbValue sg_rgb_bg; ///< RGB background color RgbValue sg_rgb_sp; ///< RGB special color - uint8_t *sg_rgb_fg_name; ///< RGB foreground color name - uint8_t *sg_rgb_bg_name; ///< RGB background color name - uint8_t *sg_rgb_sp_name; ///< RGB special color name + char *sg_rgb_fg_name; ///< RGB foreground color name + char *sg_rgb_bg_name; ///< RGB background color name + char *sg_rgb_sp_name; ///< RGB special color name int sg_blend; ///< blend level (0-100 inclusive), -1 if unset }; @@ -3147,7 +3147,7 @@ static void syn_cmd_spell(exarg_T *eap, int syncing) } // assume spell checking changed, force a redraw - redraw_win_later(curwin, NOT_VALID); + redraw_later(curwin, NOT_VALID); } /// Handle ":syntax iskeyword" command. @@ -3187,7 +3187,7 @@ static void syn_cmd_iskeyword(exarg_T *eap, int syncing) curbuf->b_p_isk = save_isk; } } - redraw_win_later(curwin, NOT_VALID); + redraw_later(curwin, NOT_VALID); } /* @@ -4297,7 +4297,7 @@ static void syn_cmd_include(exarg_T *eap, int syncing) prev_toplvl_grp = curwin->w_s->b_syn_topgrp; curwin->w_s->b_syn_topgrp = sgl_id; if (source ? do_source(eap->arg, false, DOSO_NONE) == FAIL - : source_runtime(eap->arg, DIP_ALL) == FAIL) { + : source_in_path(p_rtp, eap->arg, DIP_ALL) == FAIL) { EMSG2(_(e_notopen), eap->arg); } curwin->w_s->b_syn_topgrp = prev_toplvl_grp; @@ -6899,7 +6899,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) } } } else if (strcmp(key, "GUIFG") == 0) { - char_u **const namep = &HL_TABLE()[idx].sg_rgb_fg_name; + char **namep = &HL_TABLE()[idx].sg_rgb_fg_name; if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { if (!init) { @@ -6909,8 +6909,8 @@ void do_highlight(const char *line, const bool forceit, const bool init) if (*namep == NULL || STRCMP(*namep, arg) != 0) { xfree(*namep); if (strcmp(arg, "NONE") != 0) { - *namep = (char_u *)xstrdup(arg); - HL_TABLE()[idx].sg_rgb_fg = name_to_color((char_u *)arg); + *namep = xstrdup(arg); + HL_TABLE()[idx].sg_rgb_fg = name_to_color(arg); } else { *namep = NULL; HL_TABLE()[idx].sg_rgb_fg = -1; @@ -6923,7 +6923,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) normal_fg = HL_TABLE()[idx].sg_rgb_fg; } } else if (STRCMP(key, "GUIBG") == 0) { - char_u **const namep = &HL_TABLE()[idx].sg_rgb_bg_name; + char **const namep = &HL_TABLE()[idx].sg_rgb_bg_name; if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { if (!init) { @@ -6933,8 +6933,8 @@ void do_highlight(const char *line, const bool forceit, const bool init) if (*namep == NULL || STRCMP(*namep, arg) != 0) { xfree(*namep); if (STRCMP(arg, "NONE") != 0) { - *namep = (char_u *)xstrdup(arg); - HL_TABLE()[idx].sg_rgb_bg = name_to_color((char_u *)arg); + *namep = xstrdup(arg); + HL_TABLE()[idx].sg_rgb_bg = name_to_color(arg); } else { *namep = NULL; HL_TABLE()[idx].sg_rgb_bg = -1; @@ -6947,7 +6947,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) normal_bg = HL_TABLE()[idx].sg_rgb_bg; } } else if (strcmp(key, "GUISP") == 0) { - char_u **const namep = &HL_TABLE()[idx].sg_rgb_sp_name; + char **const namep = &HL_TABLE()[idx].sg_rgb_sp_name; if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { if (!init) { @@ -6957,8 +6957,8 @@ void do_highlight(const char *line, const bool forceit, const bool init) if (*namep == NULL || STRCMP(*namep, arg) != 0) { xfree(*namep); if (strcmp(arg, "NONE") != 0) { - *namep = (char_u *)xstrdup(arg); - HL_TABLE()[idx].sg_rgb_sp = name_to_color((char_u *)arg); + *namep = xstrdup(arg); + HL_TABLE()[idx].sg_rgb_sp = name_to_color(arg); } else { *namep = NULL; HL_TABLE()[idx].sg_rgb_sp = -1; @@ -7152,13 +7152,32 @@ static void highlight_list_one(const int id) msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name); } - if (!didh) - highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", ""); + if (!didh) { + highlight_list_arg(id, didh, LIST_STRING, 0, "cleared", ""); + } if (p_verbose > 0) { last_set_msg(sgp->sg_script_ctx); } } +Dictionary get_global_hl_defs(void) +{ + Dictionary rv = ARRAY_DICT_INIT; + for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) { + Dictionary attrs = ARRAY_DICT_INIT; + struct hl_group *h = &HL_TABLE()[i - 1]; + if (h->sg_attr > 0) { + attrs = hlattrs2dict(syn_attr2entry(h->sg_attr), true); + } else if (h->sg_link > 0) { + const char *link = (const char *)HL_TABLE()[h->sg_link - 1].sg_name; + PUT(attrs, "link", STRING_OBJ(cstr_to_string(link))); + } + PUT(rv, (const char *)h->sg_name, DICTIONARY_OBJ(attrs)); + } + + return rv; +} + /// Outputs a highlight when doing ":hi MyHighlight" /// /// @param type one of \ref LIST_XXX @@ -7166,15 +7185,15 @@ static void highlight_list_one(const int id) /// @param sarg string used if \p type == LIST_STRING static bool highlight_list_arg( const int id, bool didh, const int type, int iarg, - char_u *const sarg, const char *const name) + char *const sarg, const char *const name) { - char_u buf[100]; + char buf[100]; if (got_int) { return false; } if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0)) { - char_u *ts = buf; + char *ts = buf; if (type == LIST_INT) { snprintf((char *)buf, sizeof(buf), "%d", iarg - 1); } else if (type == LIST_STRING) { @@ -7191,15 +7210,15 @@ static bool highlight_list_arg( } } - (void)syn_list_header(didh, (int)(vim_strsize(ts) + STRLEN(name) + 1), id, - false); + (void)syn_list_header(didh, (int)(vim_strsize((char_u *)ts) + STRLEN(name) + + 1), id, false); didh = true; if (!got_int) { if (*name != NUL) { MSG_PUTS_ATTR(name, HL_ATTR(HLF_D)); MSG_PUTS_ATTR("=", HL_ATTR(HLF_D)); } - msg_outtrans(ts); + msg_outtrans((char_u *)ts); } } return didh; @@ -7378,7 +7397,7 @@ static void set_hl_attr(int idx) at_en.rgb_sp_color = sgp->sg_rgb_sp_name ? sgp->sg_rgb_sp : -1; at_en.hl_blend = sgp->sg_blend; - sgp->sg_attr = hl_get_syn_attr(idx+1, at_en); + sgp->sg_attr = hl_get_syn_attr(0, idx+1, at_en); // a cursor style uses this syn_id, make sure its attribute is updated. if (cursor_mode_uses_syn_id(idx+1)) { @@ -7541,11 +7560,17 @@ int syn_id2attr(int hl_id) struct hl_group *sgp; hl_id = syn_get_final_id(hl_id); - sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */ + int attr = ns_get_hl(-1, hl_id, false); + if (attr >= 0) { + return attr; + } + sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one return sgp->sg_attr; } + + /* * Translate a group ID to the final group ID (following links). */ @@ -7562,9 +7587,22 @@ int syn_get_final_id(int hl_id) * Look out for loops! Break after 100 links. */ for (count = 100; --count >= 0; ) { - sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */ - if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len) + // ACHTUNG: when using "tmp" attribute (no link) the function might be + // called twice. it needs be smart enough to remember attr only to + // syn_id2attr time + int check = ns_get_hl(-1, hl_id, true); + if (check == 0) { + return 0; // how dare! it broke the link! + } else if (check > 0) { + hl_id = check; + continue; + } + + + sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one + if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len) { break; + } hl_id = sgp->sg_link; } @@ -8494,7 +8532,7 @@ color_name_table_T color_name_table[] = { /// /// @param[in] name string value to convert to RGB /// return the hex value or -1 if could not find a correct value -RgbValue name_to_color(const char_u *name) +RgbValue name_to_color(const char *name) { if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2]) diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 81d1ef4c9f..c6b1a0d04c 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -2903,7 +2903,7 @@ static int jumpto_tag( && curwin != curwin_save && win_valid(curwin_save)) { /* Return cursor to where we were */ validate_cursor(); - redraw_later(VALID); + redraw_later(curwin, VALID); win_enter(curwin_save, true); } diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 52d3eef810..39e2ca6171 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -233,7 +233,7 @@ Terminal *terminal_open(TerminalOptions opts) snprintf(var, sizeof(var), "terminal_color_%d", i); char *name = get_config_string(var); if (name) { - color_val = name_to_color((uint8_t *)name); + color_val = name_to_color(name); xfree(name); if (color_val != -1) { @@ -1060,7 +1060,7 @@ static bool send_mouse_event(Terminal *term, int c) curwin->w_redr_status = true; curwin = save_curwin; curbuf = curwin->w_buffer; - redraw_win_later(mouse_win, NOT_VALID); + redraw_later(mouse_win, NOT_VALID); invalidate_terminal(term, -1, -1); // Only need to exit focus if the scrolled window is the terminal window return mouse_win == curwin; diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim index debc9da46d..b6c2d1467e 100644 --- a/src/nvim/testdir/test_cindent.vim +++ b/src/nvim/testdir/test_cindent.vim @@ -127,4 +127,40 @@ func Test_cindent_case() bwipe! endfunc +func Test_cindent_pragma() + new + setl cindent ts=4 sw=4 + setl cino=Ps + + let code =<< trim [CODE] + { + #pragma omp parallel + { + #pragma omp task + foo(); + # pragma omp taskwait + } + } + [CODE] + + call append(0, code) + normal gg + normal =G + + let expected =<< trim [CODE] + { + #pragma omp parallel + { + #pragma omp task + foo(); + # pragma omp taskwait + } + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 871143699a..e3c42a4fe3 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -722,7 +722,7 @@ func Test_verbosefile() endfunc func Test_verbose_option() - " See test/functional/ui/cmdline_spec.lua + " See test/functional/legacy/cmdline_spec.lua CheckScreendump let lines =<< trim [SCRIPT] @@ -842,6 +842,25 @@ func Test_buffers_lastused() bwipeout bufc endfunc +func Test_cmdlineclear_tabenter() + " See test/functional/legacy/cmdline_spec.lua + CheckScreendump + + let lines =<< trim [SCRIPT] + call setline(1, range(30)) + [SCRIPT] + + call writefile(lines, 'XtestCmdlineClearTabenter') + let buf = RunVimInTerminal('-S XtestCmdlineClearTabenter', #{rows: 10}) + call term_wait(buf, 50) + " in one tab make the command line higher with CTRL-W - + call term_sendkeys(buf, ":tabnew\<cr>\<C-w>-\<C-w>-gtgt") + call VerifyScreenDump(buf, 'Test_cmdlineclear_tabenter', {}) + + call StopVimInTerminal(buf) + call delete('XtestCmdlineClearTabenter') +endfunc + " test that ";" works to find a match at the start of the first line func Test_zero_line_search() new diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim index 15c718b243..abad6983dc 100644 --- a/src/nvim/testdir/test_edit.vim +++ b/src/nvim/testdir/test_edit.vim @@ -1587,4 +1587,11 @@ func Test_edit_browse() bwipe! endfunc +func Test_read_invalid() + " set encoding=latin1 + " This was not properly checking for going past the end. + call assert_fails('r`=', 'E484') + set encoding=utf-8 +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim index 31a8b48d37..8e2a987e74 100644 --- a/src/nvim/testdir/test_listdict.vim +++ b/src/nvim/testdir/test_listdict.vim @@ -199,9 +199,9 @@ func Test_dict_big() try let n = d[1500] catch - let str=substitute(v:exception, '\v(.{14}).*( \d{4}).*', '\1\2', '') + let str = substitute(v:exception, '\v(.{14}).*( "\d{4}").*', '\1\2', '') endtry - call assert_equal('Vim(let):E716: 1500', str) + call assert_equal('Vim(let):E716: "1500"', str) " lookup each items for i in range(1500) diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim index ca14494248..30239a90c2 100644 --- a/src/nvim/testdir/test_messages.vim +++ b/src/nvim/testdir/test_messages.vim @@ -1,20 +1,16 @@ " Tests for :messages, :echomsg, :echoerr -function Test_messages() +source shared.vim + +func Test_messages() let oldmore = &more try set nomore - " Avoid the "message maintainer" line. - let $LANG = '' - let $LC_ALL = '' - let $LC_MESSAGES = '' - let $LC_COLLATE = '' let arr = map(range(10), '"hello" . v:val') for s in arr echomsg s | redraw endfor - let result = '' " get last two messages redir => result @@ -25,22 +21,17 @@ function Test_messages() " clear messages without last one 1messages clear - redir => result - redraw | messages - redir END - let msg_list = split(result, "\n") + let msg_list = GetMessages() call assert_equal(['hello9'], msg_list) " clear all messages messages clear - redir => result - redraw | messages - redir END - call assert_equal('', result) + let msg_list = GetMessages() + call assert_equal([], msg_list) finally let &more = oldmore endtry -endfunction +endfunc " Patch 7.4.1696 defined the "clearmode()" command for clearing the mode " indicator (e.g., "-- INSERT --") when ":stopinsert" is invoked. Message diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index 3243edbf55..215065f941 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -470,4 +470,17 @@ func Test_mkvimrc() call delete('Xtestvimrc') endfunc +func Test_scrolloff() + set sessionoptions+=localoptions + setlocal so=1 siso=1 + mksession! Xtest_mks.out + setlocal so=-1 siso=-1 + source Xtest_mks.out + call assert_equal(1, &l:so) + call assert_equal(1, &l:siso) + call delete('Xtest_mks.out') + setlocal so& siso& + set sessionoptions& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 9e8da74db7..10e16f4198 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -267,7 +267,6 @@ func Test_set_errors() call assert_fails('set commentstring=x', 'E537:') call assert_fails('set complete=x', 'E539:') call assert_fails('set statusline=%{', 'E540:') - call assert_fails('set statusline=' . repeat("%p", 81), 'E541:') call assert_fails('set statusline=%(', 'E542:') if has('cursorshape') " This invalid value for 'guicursor' used to cause Vim to crash. diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index d20f8d1eef..19a7c6c9d0 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -167,6 +167,75 @@ func Test_set_register() enew! endfunc +func Test_v_register() + enew + call setline(1, 'nothing') + + func s:Put() + let s:register = v:register + exec 'normal! "' .. v:register .. 'P' + endfunc + nnoremap <buffer> <plug>(test) :<c-u>call s:Put()<cr> + nmap <buffer> S <plug>(test) + + let @z = "testz\n" + let @" = "test@\n" + + let s:register = '' + call feedkeys('"_ddS', 'mx') + call assert_equal('test@', getline('.')) " fails before 8.2.0929 + call assert_equal('"', s:register) " fails before 8.2.0929 + + let s:register = '' + call feedkeys('"zS', 'mx') + call assert_equal('z', s:register) + + let s:register = '' + call feedkeys('"zSS', 'mx') + call assert_equal('"', s:register) + + let s:register = '' + call feedkeys('"_S', 'mx') + call assert_equal('_', s:register) + + let s:register = '' + normal "_ddS + call assert_equal('"', s:register) " fails before 8.2.0929 + call assert_equal('test@', getline('.')) " fails before 8.2.0929 + + let s:register = '' + execute 'normal "z:call' "s:Put()\n" + call assert_equal('z', s:register) + call assert_equal('testz', getline('.')) + + " Test operator and omap + let @b = 'testb' + func s:OpFunc(...) + let s:register2 = v:register + endfunc + set opfunc=s:OpFunc + + normal "bg@l + normal S + call assert_equal('"', s:register) " fails before 8.2.0929 + call assert_equal('b', s:register2) + + func s:Motion() + let s:register1 = v:register + normal! l + endfunc + onoremap <buffer> Q :<c-u>call s:Motion()<cr> + + normal "bg@Q + normal S + call assert_equal('"', s:register) + call assert_equal('b', s:register1) + call assert_equal('"', s:register2) + + set opfunc& + bwipe! +endfunc + func Test_ve_blockpaste() new set ve=all diff --git a/src/nvim/testdir/test_ruby.vim b/src/nvim/testdir/test_ruby.vim index 07ad8561c3..9c74c35049 100644 --- a/src/nvim/testdir/test_ruby.vim +++ b/src/nvim/testdir/test_ruby.vim @@ -1,8 +1,7 @@ " Tests for ruby interface -if !has('ruby') - finish -end +source check.vim +CheckFeature ruby func Test_ruby_change_buffer() call setline(line('$'), ['1 line 1']) @@ -36,7 +35,7 @@ func Test_rubyfile() call delete(tempfile) endfunc -func Test_set_cursor() +func Test_ruby_set_cursor() " Check that setting the cursor position works. new call setline(1, ['first line', 'second line']) @@ -56,7 +55,7 @@ func Test_set_cursor() endfunc " Test buffer.count and buffer.length (number of lines in buffer) -func Test_buffer_count() +func Test_ruby_buffer_count() new call setline(1, ['one', 'two', 'three']) call assert_equal(3, rubyeval('$curbuf.count')) @@ -65,7 +64,7 @@ func Test_buffer_count() endfunc " Test buffer.name (buffer name) -func Test_buffer_name() +func Test_ruby_buffer_name() new Xfoo call assert_equal(expand('%:p'), rubyeval('$curbuf.name')) bwipe @@ -73,7 +72,7 @@ func Test_buffer_name() endfunc " Test buffer.number (number of the buffer). -func Test_buffer_number() +func Test_ruby_buffer_number() new call assert_equal(bufnr('%'), rubyeval('$curbuf.number')) new @@ -83,7 +82,7 @@ func Test_buffer_number() endfunc " Test buffer.delete({n}) (delete line {n}) -func Test_buffer_delete() +func Test_ruby_buffer_delete() new call setline(1, ['one', 'two', 'three']) ruby $curbuf.delete(2) @@ -97,7 +96,7 @@ func Test_buffer_delete() endfunc " Test buffer.append({str}, str) (append line {str} after line {n}) -func Test_buffer_append() +func Test_ruby_buffer_append() new ruby $curbuf.append(0, 'one') ruby $curbuf.append(1, 'three') @@ -117,7 +116,7 @@ func Test_buffer_append() endfunc " Test buffer.line (get or set the current line) -func Test_buffer_line() +func Test_ruby_buffer_line() new call setline(1, ['one', 'two', 'three']) 2 @@ -130,7 +129,7 @@ func Test_buffer_line() endfunc " Test buffer.line_number (get current line number) -func Test_buffer_line_number() +func Test_ruby_buffer_line_number() new call setline(1, ['one', 'two', 'three']) 2 @@ -139,7 +138,7 @@ func Test_buffer_line_number() bwipe! endfunc -func Test_buffer_get() +func Test_ruby_buffer_get() new call setline(1, ['one', 'two']) call assert_equal('one', rubyeval('$curbuf[1]')) @@ -153,7 +152,7 @@ func Test_buffer_get() bwipe! endfunc -func Test_buffer_set() +func Test_ruby_buffer_set() new call setline(1, ['one', 'two']) ruby $curbuf[2] = 'TWO' @@ -169,7 +168,7 @@ func Test_buffer_set() endfunc " Test window.width (get or set window height). -func Test_window_height() +func Test_ruby_window_height() new " Test setting window height @@ -183,7 +182,7 @@ func Test_window_height() endfunc " Test window.width (get or set window width). -func Test_window_width() +func Test_ruby_window_width() vnew " Test setting window width @@ -197,7 +196,7 @@ func Test_window_width() endfunc " Test window.buffer (get buffer object of a window object). -func Test_window_buffer() +func Test_ruby_window_buffer() new Xfoo1 new Xfoo2 ruby $b2 = $curwin.buffer @@ -216,14 +215,14 @@ func Test_window_buffer() endfunc " Test Vim::Window.current (get current window object) -func Test_Vim_window_current() +func Test_ruby_Vim_window_current() let cw = rubyeval('$curwin.to_s') " call assert_equal(cw, rubyeval('Vim::Window.current')) call assert_match('^#<Neovim::Window:0x\x\+>$', cw) endfunc " Test Vim::Window.count (number of windows) -func Test_Vim_window_count() +func Test_ruby_Vim_window_count() new Xfoo1 new Xfoo2 split @@ -233,7 +232,7 @@ func Test_Vim_window_count() endfunc " Test Vim::Window[n] (get window object of window n) -func Test_Vim_window_get() +func Test_ruby_Vim_window_get() new Xfoo1 new Xfoo2 call assert_match('Xfoo2$', rubyeval('Vim::Window[0].buffer.name')) @@ -245,14 +244,14 @@ func Test_Vim_window_get() endfunc " Test Vim::Buffer.current (return the buffer object of current buffer) -func Test_Vim_buffer_current() +func Test_ruby_Vim_buffer_current() let cb = rubyeval('$curbuf.to_s') " call assert_equal(cb, rubyeval('Vim::Buffer.current')) call assert_match('^#<Neovim::Buffer:0x\x\+>$', cb) endfunc " Test Vim::Buffer:.count (return the number of buffers) -func Test_Vim_buffer_count() +func Test_ruby_Vim_buffer_count() new Xfoo1 new Xfoo2 call assert_equal(3, rubyeval('Vim::Buffer.count')) @@ -261,7 +260,7 @@ func Test_Vim_buffer_count() endfunc " Test Vim::buffer[n] (return the buffer object of buffer number n) -func Test_Vim_buffer_get() +func Test_ruby_Vim_buffer_get() new Xfoo1 new Xfoo2 @@ -276,7 +275,7 @@ endfunc " Test Vim::command({cmd}) (execute a Ex command)) " Test Vim::command({cmd}) -func Test_Vim_command() +func Test_ruby_Vim_command() new call setline(1, ['one', 'two', 'three', 'four']) ruby Vim::command('2,3d') @@ -285,7 +284,7 @@ func Test_Vim_command() endfunc " Test Vim::set_option (set a vim option) -func Test_Vim_set_option() +func Test_ruby_Vim_set_option() call assert_equal(0, &number) ruby Vim::set_option('number') call assert_equal(1, &number) @@ -293,14 +292,16 @@ func Test_Vim_set_option() call assert_equal(0, &number) endfunc -func Test_Vim_evaluate() +func Test_ruby_Vim_evaluate() call assert_equal(123, rubyeval('Vim::evaluate("123")')) " Vim::evaluate("123").class gives Integer or Fixnum depending " on versions of Ruby. call assert_match('^Integer\|Fixnum$', rubyeval('Vim::evaluate("123").class')) - call assert_equal(1.23, rubyeval('Vim::evaluate("1.23")')) - call assert_equal('Float', rubyeval('Vim::evaluate("1.23").class')) + if has('float') + call assert_equal(1.23, rubyeval('Vim::evaluate("1.23")')) + call assert_equal('Float', rubyeval('Vim::evaluate("1.23").class')) + endif call assert_equal('foo', rubyeval('Vim::evaluate("\"foo\"")')) call assert_equal('String', rubyeval('Vim::evaluate("\"foo\"").class')) @@ -323,7 +324,7 @@ func Test_Vim_evaluate() call assert_equal('FalseClass',rubyeval('Vim::evaluate("v:false").class')) endfunc -func Test_Vim_evaluate_list() +func Test_ruby_Vim_evaluate_list() call setline(line('$'), ['2 line 2']) ruby Vim.command("normal /^2\n") let l = ["abc", "def"] @@ -337,7 +338,7 @@ EOF call assert_equal('abc/def', getline('$')) endfunc -func Test_Vim_evaluate_dict() +func Test_ruby_Vim_evaluate_dict() let d = {'a': 'foo', 'b': 123} redir => l:out ruby d = Vim.evaluate("d"); print d @@ -346,14 +347,14 @@ func Test_Vim_evaluate_dict() endfunc " Test Vim::message({msg}) (display message {msg}) -func Test_Vim_message() +func Test_ruby_Vim_message() throw 'skipped: TODO: ' ruby Vim::message('A message') let messages = split(execute('message'), "\n") call assert_equal('A message', messages[-1]) endfunc -func Test_print() +func Test_ruby_print() func RubyPrint(expr) return trim(execute('ruby print ' . a:expr)) endfunc @@ -372,9 +373,9 @@ func Test_print() delfunc RubyPrint endfunc -func Test_p() +func Test_ruby_p() ruby p 'Just a test' - let messages = split(execute('message'), "\n") + let messages = GetMessages() call assert_equal('"Just a test"', messages[-1]) " Check return values of p method @@ -387,6 +388,6 @@ func Test_p() messages clear call assert_equal(v:true, rubyeval('p() == nil')) - let messages = split(execute('message'), "\n") + let messages = GetMessages() call assert_equal(0, len(messages)) endfunc diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim index 7efd181d04..4e38f7ebd8 100644 --- a/src/nvim/testdir/test_statusline.vim +++ b/src/nvim/testdir/test_statusline.vim @@ -354,6 +354,21 @@ func Test_statusline() delfunc GetNested delfunc GetStatusLine + " Test statusline works with 80+ items + function! StatusLabel() + redrawstatus + return '[label]' + endfunc + let statusline = '%{StatusLabel()}' + for i in range(150) + let statusline .= '%#TabLine' . (i % 2 == 0 ? 'Fill' : 'Sel') . '#' . string(i)[0] + endfor + let &statusline = statusline + redrawstatus + set statusline& + delfunc StatusLabel + + " Check statusline in current and non-current window " with the 'fillchars' option. set fillchars=stl:^,stlnc:=,vert:\|,fold:-,diff:- diff --git a/src/nvim/types.h b/src/nvim/types.h index a3d87f35ca..17f7e16740 100644 --- a/src/nvim/types.h +++ b/src/nvim/types.h @@ -21,7 +21,7 @@ typedef int handle_T; // absent callback etc. typedef int LuaRef; -typedef uint64_t NS; +typedef handle_T NS; typedef struct expand expand_T; diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index e582d8f859..325ad31eaf 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -26,6 +26,7 @@ #include "nvim/screen.h" #include "nvim/syntax.h" #include "nvim/api/private/helpers.h" +#include "nvim/lua/executor.h" #include "nvim/os/os.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 6c5a6cdb46..a8b8f7aa50 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -2455,7 +2455,7 @@ static void u_undo_end( { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_buffer == curbuf && wp->w_p_cole > 0) { - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); } } } diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 832703e83d..873bef32d3 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -259,7 +259,6 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext() #define PERROR(msg) (void) emsgf("%s: %s", msg, strerror(errno)) #define SHOWCMD_COLS 10 // columns needed by shown command -#define STL_MAX_ITEM 80 // max nr of %<flag> in statusline #include "nvim/path.h" diff --git a/src/nvim/window.c b/src/nvim/window.c index 4931221e7a..2147bd4fc7 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -606,7 +606,7 @@ win_T *win_new_float(win_T *wp, FloatConfig fconfig, Error *err) win_config_float(wp, fconfig); wp->w_pos_changed = true; - redraw_win_later(wp, VALID); + redraw_later(wp, VALID); return wp; } @@ -679,7 +679,7 @@ void win_config_float(win_T *wp, FloatConfig fconfig) wp->w_pos_changed = true; if (change_external) { wp->w_hl_needs_update = true; - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); } } @@ -764,7 +764,7 @@ static void ui_ext_win_position(win_T *wp) wp->w_grid.focusable = wp->w_float_config.focusable; if (!valid) { wp->w_grid.valid = false; - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); } } } else { @@ -1492,8 +1492,8 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) // Both windows need redrawing. Update all status lines, in case they // show something related to the window count or position. - redraw_win_later(wp, NOT_VALID); - redraw_win_later(oldwin, NOT_VALID); + redraw_later(wp, NOT_VALID); + redraw_later(oldwin, NOT_VALID); status_redraw_all(); if (need_status) { @@ -1823,8 +1823,8 @@ static void win_exchange(long Prenum) (void)win_comp_pos(); /* recompute window positions */ win_enter(wp, true); - redraw_later(NOT_VALID); - redraw_win_later(wp, NOT_VALID); + redraw_later(curwin, NOT_VALID); + redraw_later(wp, NOT_VALID); } // rotate windows: if upwards true the second window becomes the first one @@ -1996,8 +1996,8 @@ void win_move_after(win_T *win1, win_T *win2) win_append(win2, win1); frame_append(win2->w_frame, win1->w_frame); - (void)win_comp_pos(); /* recompute w_winrow for all windows */ - redraw_later(NOT_VALID); + (void)win_comp_pos(); // recompute w_winrow for all windows + redraw_later(curwin, NOT_VALID); } win_enter(win1, false); @@ -3635,7 +3635,7 @@ void curwin_init(void) void win_init_empty(win_T *wp) { - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); wp->w_lines_valid = 0; wp->w_cursor.lnum = 1; wp->w_curswant = wp->w_cursor.col = 0; @@ -4049,7 +4049,7 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au prevwin = next_prevwin; last_status(false); // status line may appear or disappear - (void)win_comp_pos(); // recompute w_winrow for all windows + const int row = win_comp_pos(); // recompute w_winrow for all windows diff_need_scrollbind = true; /* The tabpage line may have appeared or disappeared, may need to resize @@ -4060,11 +4060,20 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au clear_cmdline = true; } p_ch = curtab->tp_ch_used; - if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow - )) + + // When cmdheight is changed in a tab page with '<C-w>-', cmdline_row is + // changed but p_ch and tp_ch_used are not changed. Thus we also need to + // check cmdline_row. + if ((row < cmdline_row) && (cmdline_row <= Rows - p_ch)) { + clear_cmdline = true; + } + + if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow)) { shell_new_rows(); - if (curtab->tp_old_Columns != Columns && starting == 0) - shell_new_columns(); /* update window widths */ + } + if (curtab->tp_old_Columns != Columns && starting == 0) { + shell_new_columns(); // update window widths + } lastused_tabpage = old_curtab; @@ -4587,10 +4596,11 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, } maketitle(); - curwin->w_redr_status = TRUE; - redraw_tabline = TRUE; - if (restart_edit) - redraw_later(VALID); /* causes status line redraw */ + curwin->w_redr_status = true; + redraw_tabline = true; + if (restart_edit) { + redraw_later(curwin, VALID); // causes status line redraw + } if (HL_ATTR(HLF_INACTIVE) || (prevwin && prevwin->w_hl_ids[HLF_INACTIVE]) @@ -5056,7 +5066,7 @@ static void frame_comp_pos(frame_T *topfrp, int *row, int *col) /* position changed, redraw */ wp->w_winrow = *row; wp->w_wincol = *col; - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); wp->w_redr_status = true; wp->w_pos_changed = true; } @@ -5109,7 +5119,7 @@ void win_setheight_win(int height, win_T *win) if (win->w_floating) { win->w_float_config.height = height; win_config_float(win, win->w_float_config); - redraw_win_later(win, NOT_VALID); + redraw_later(win, NOT_VALID); } else { frame_setheight(win->w_frame, height + win->w_status_height); @@ -5312,7 +5322,7 @@ void win_setwidth_win(int width, win_T *wp) if (wp->w_floating) { wp->w_float_config.width = width; win_config_float(wp, wp->w_float_config); - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); } else { frame_setwidth(wp->w_frame, width + wp->w_vsep_width); @@ -5845,8 +5855,8 @@ void scroll_to_fraction(win_T *wp, int prev_height) } win_comp_scroll(wp); - redraw_win_later(wp, SOME_VALID); - wp->w_redr_status = TRUE; + redraw_later(wp, SOME_VALID); + wp->w_redr_status = true; invalidate_botline_win(wp); } @@ -5885,7 +5895,7 @@ void win_set_inner_size(win_T *wp) if (!exiting) { scroll_to_fraction(wp, prev_height); } - redraw_win_later(wp, NOT_VALID); // SOME_VALID?? + redraw_later(wp, NOT_VALID); // SOME_VALID?? } if (width != wp->w_width_inner) { @@ -5897,7 +5907,7 @@ void win_set_inner_size(win_T *wp) update_topline(); curs_columns(true); // validate w_wrow } - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); } if (wp->w_buffer->terminal) { @@ -6741,7 +6751,7 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, prev->next = m; m->next = cur; - redraw_win_later(wp, rtype); + redraw_later(wp, rtype); return id; fail: @@ -6799,7 +6809,7 @@ int match_delete(win_T *wp, int id, int perr) rtype = VALID; } xfree(cur); - redraw_win_later(wp, rtype); + redraw_later(wp, rtype); return 0; } @@ -6817,7 +6827,7 @@ void clear_matches(win_T *wp) xfree(wp->w_match_head); wp->w_match_head = m; } - redraw_win_later(wp, SOME_VALID); + redraw_later(wp, SOME_VALID); } /* diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index 9b0668f9e6..3bfae5f3d7 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -7,6 +7,7 @@ local ok = helpers.ok local eq = helpers.eq local matches = helpers.matches local eval = helpers.eval +local exec_lua = helpers.exec_lua local feed = helpers.feed local funcs = helpers.funcs local mkdir = helpers.mkdir @@ -305,6 +306,27 @@ describe('startup', function() '+q' }) eq('[\'+q\'] 1', out) end) + + local function pack_clear(cmd) + clear('--cmd', 'set packpath=test/functional/fixtures', '--cmd', cmd) + end + + + it("handles &packpath during startup", function() + pack_clear [[ let g:x = bar#test() ]] + eq(-3, eval 'g:x') + + pack_clear [[ lua _G.y = require'bar'.doit() ]] + eq(9003, exec_lua [[ return _G.y ]]) + end) + + it("handles :packadd during startup", function() + pack_clear [[ packadd! bonus | let g:x = bonus#secret() ]] + eq('halloj', eval 'g:x') + + pack_clear [[ packadd! bonus | lua _G.y = require'bonus'.launch() ]] + eq('CPE 1704 TKS', exec_lua [[ return _G.y ]]) + end) end) describe('sysinit', function() diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index db0a706319..fa8f7d873f 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -132,7 +132,7 @@ describe('NULL', function() end) describe('dict', function() it('does not crash when indexing NULL dict', function() - eq('\nE716: Key not present in Dictionary: test\nE15: Invalid expression: v:_null_dict.test', + eq('\nE716: Key not present in Dictionary: "test"\nE15: Invalid expression: v:_null_dict.test', redir_exec('echo v:_null_dict.test')) end) null_expr_test('makes extend error out', 'extend(D, {})', 'E742: Cannot change value of extend() argument', 0) diff --git a/test/functional/ex_cmds/packadd_spec.lua b/test/functional/ex_cmds/packadd_spec.lua deleted file mode 100644 index 2b0810cf9b..0000000000 --- a/test/functional/ex_cmds/packadd_spec.lua +++ /dev/null @@ -1,74 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) - -local clear = helpers.clear -local eq = helpers.eq -local exec_lua = helpers.exec_lua - -describe('packadd', function() - before_each(function() - -- Primarily taken from test/functional/legacy/packadd_spec.lua - clear() - exec_lua [[ - TopDirectory = vim.fn.expand(vim.fn.getcwd() .. '/Xdir_lua') - PlugDirectory = TopDirectory .. '/pack/mine/opt/mytest' - - vim.o.packpath = TopDirectory - - function FindPathsContainingDir(dir) - return vim.fn.filter( - vim.split(package.path, ';'), - function(k, v) - return string.find(v, 'mytest') ~= nil - end - ) - end - ]] - end) - - after_each(function() - exec_lua [[ - vim.fn.delete(TopDirectory, 'rf') - ]] - end) - - it('should immediately update package.path in lua', function() - local count_of_paths = exec_lua [[ - vim.fn.mkdir(PlugDirectory .. '/lua/', 'p') - - local num_paths_before = #FindPathsContainingDir('mytest') - - vim.cmd("packadd mytest") - - local num_paths_after = #FindPathsContainingDir('mytest') - - return { num_paths_before, num_paths_after } - ]] - - eq({0, 2}, count_of_paths) - end) - - it('should immediately update package.path in lua even if lua directory does not exist', function() - local count_of_paths = exec_lua [[ - vim.fn.mkdir(PlugDirectory .. '/plugin/', 'p') - - local num_paths_before = #FindPathsContainingDir('mytest') - - vim.cmd("packadd mytest") - - local num_paths_after = #FindPathsContainingDir('mytest') - - return { num_paths_before, num_paths_after } - ]] - - eq({0, 2}, count_of_paths) - end) - - it('should error for invalid paths', function() - local count_of_paths = exec_lua [[ - local ok, err = pcall(vim.cmd, "packadd asdf") - return ok - ]] - - eq(false, count_of_paths) - end) -end) diff --git a/test/functional/fixtures/pack/foo/opt/bonus/autoload/bonus.vim b/test/functional/fixtures/pack/foo/opt/bonus/autoload/bonus.vim new file mode 100644 index 0000000000..5ed8b1b887 --- /dev/null +++ b/test/functional/fixtures/pack/foo/opt/bonus/autoload/bonus.vim @@ -0,0 +1,3 @@ +func bonus#secret() + return "halloj" +endfunc diff --git a/test/functional/fixtures/pack/foo/opt/bonus/lua/bonus.lua b/test/functional/fixtures/pack/foo/opt/bonus/lua/bonus.lua new file mode 100644 index 0000000000..52cb0bc118 --- /dev/null +++ b/test/functional/fixtures/pack/foo/opt/bonus/lua/bonus.lua @@ -0,0 +1 @@ +return {launch=function() return "CPE 1704 TKS" end} diff --git a/test/functional/fixtures/pack/foo/start/bar/autoload/bar.vim b/test/functional/fixtures/pack/foo/start/bar/autoload/bar.vim new file mode 100644 index 0000000000..405e7be71c --- /dev/null +++ b/test/functional/fixtures/pack/foo/start/bar/autoload/bar.vim @@ -0,0 +1,3 @@ +func bar#test() + return -3 +endfunc diff --git a/test/functional/fixtures/pack/foo/start/bar/lua/bar.lua b/test/functional/fixtures/pack/foo/start/bar/lua/bar.lua new file mode 100644 index 0000000000..a7e9a61e35 --- /dev/null +++ b/test/functional/fixtures/pack/foo/start/bar/lua/bar.lua @@ -0,0 +1 @@ +return {doit=function() return 9003 end} diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 0bd378d832..d85a6a3cfe 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -751,6 +751,14 @@ module.curbufmeths = module.create_callindex(module.curbuf) module.curwinmeths = module.create_callindex(module.curwin) module.curtabmeths = module.create_callindex(module.curtab) +function module.exec(code) + return module.meths.exec(code, false) +end + +function module.exec_capture(code) + return module.meths.exec(code, true) +end + function module.exec_lua(code, ...) return module.meths.exec_lua(code, {...}) end diff --git a/test/functional/legacy/055_list_and_dict_types_spec.lua b/test/functional/legacy/055_list_and_dict_types_spec.lua index 91ba8bb106..4d71a526c1 100644 --- a/test/functional/legacy/055_list_and_dict_types_spec.lua +++ b/test/functional/legacy/055_list_and_dict_types_spec.lua @@ -229,7 +229,7 @@ describe('list and dictionary types', function() try let n = d[1500] catch - $put =substitute(v:exception, '\v(.{14}).*( \d{4}).*', '\1\2', '') + $put = substitute(v:exception, '\v(.{14}).*( \"\d{4}\").*', '\1\2', '') endtry " Lookup each items. for i in range(1500) @@ -260,7 +260,7 @@ describe('list and dictionary types', function() expect([[ 3000 2900 2001 1600 1501 - Vim(let):E716: 1500 + Vim(let):E716: "1500" NONE 2999 33=999 {'33': 999}]]) diff --git a/test/functional/legacy/cmdline_spec.lua b/test/functional/legacy/cmdline_spec.lua new file mode 100644 index 0000000000..9ebe9aeb91 --- /dev/null +++ b/test/functional/legacy/cmdline_spec.lua @@ -0,0 +1,66 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear = helpers.clear +local feed = helpers.feed +local feed_command = helpers.feed_command +local source = helpers.source + +describe('cmdline', function() + before_each(clear) + + it('is cleared when switching tabs', function() + local screen = Screen.new(30, 10) + screen:attach() + feed_command([[call setline(1, range(30))]]) + screen:expect([[ + ^0 | + 1 | + 2 | + 3 | + 4 | + 5 | + 6 | + 7 | + 8 | + :call setline(1, range(30)) | + ]]) + feed([[:tabnew<cr><C-w>-<C-w>-gtgt]]) + screen:expect([[ + + [No Name] [No Name] X| + ^ | + ~ | + ~ | + ~ | + ~ | + ~ | + 6 | + 7 | + | + ]]) + end) + + it('prints every executed Ex command if verbose >= 16', function() + local screen = Screen.new(60, 12) + screen:attach() + source([[ + command DoSomething echo 'hello' |set ts=4 |let v = '123' |echo v + call feedkeys("\r", 't') " for the hit-enter prompt + set verbose=20 + ]]) + feed_command('DoSomething') + screen:expect([[ + | + ~ | + ~ | + | + Executing: DoSomething | + Executing: echo 'hello' |set ts=4 |let v = '123' |echo v | + hello | + Executing: set ts=4 |let v = '123' |echo v | + Executing: let v = '123' |echo v | + Executing: echo v | + 123 | + Press ENTER or type command to continue^ | + ]]) + end) +end) diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua index 1bccc02847..5fbba63736 100644 --- a/test/functional/lua/overrides_spec.lua +++ b/test/functional/lua/overrides_spec.lua @@ -285,119 +285,6 @@ describe('debug.debug', function() end) end) -describe('package.path/package.cpath', function() - local sl = alter_slashes - - local function get_new_paths(sufs, runtimepaths) - runtimepaths = runtimepaths or meths.list_runtime_paths() - local new_paths = {} - local sep = package.config:sub(1, 1) - for _, v in ipairs(runtimepaths) do - for _, suf in ipairs(sufs) do - new_paths[#new_paths + 1] = v .. sep .. 'lua' .. suf - end - end - return new_paths - end - local function eval_lua(expr, ...) - return meths.exec_lua('return '..expr, {...}) - end - local function set_path(which, value) - return exec_lua('package[select(1, ...)] = select(2, ...)', which, value) - end - - it('contains directories from &runtimepath on first invocation', function() - local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}) - local new_paths_str = table.concat(new_paths, ';') - eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str)) - - local new_cpaths = get_new_paths(iswin() and {'\\?.dll'} or {'/?.so'}) - local new_cpaths_str = table.concat(new_cpaths, ';') - eq(new_cpaths_str, eval_lua('package.cpath'):sub(1, #new_cpaths_str)) - end) - it('puts directories from &runtimepath always at the start', function() - meths.set_option('runtimepath', 'a,b') - local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {'a', 'b'}) - local new_paths_str = table.concat(new_paths, ';') - eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str)) - - set_path('path', sl'foo/?.lua;foo/?/init.lua;' .. new_paths_str) - - neq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str)) - - command('set runtimepath+=c') - new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {'a', 'b', 'c'}) - new_paths_str = table.concat(new_paths, ';') - eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str)) - end) - it('understands uncommon suffixes', function() - set_path('cpath', './?/foo/bar/baz/x.nlua') - meths.set_option('runtimepath', 'a') - local new_paths = get_new_paths({'/?/foo/bar/baz/x.nlua'}, {'a'}) - local new_paths_str = table.concat(new_paths, ';') - eq(new_paths_str, eval_lua('package.cpath'):sub(1, #new_paths_str)) - - set_path('cpath', './yyy?zzz/x') - meths.set_option('runtimepath', 'b') - new_paths = get_new_paths({'/yyy?zzz/x'}, {'b'}) - new_paths_str = table.concat(new_paths, ';') - eq(new_paths_str, eval_lua('package.cpath'):sub(1, #new_paths_str)) - - set_path('cpath', './yyy?zzz/123?ghi/x') - meths.set_option('runtimepath', 'b') - new_paths = get_new_paths({'/yyy?zzz/123?ghi/x'}, {'b'}) - new_paths_str = table.concat(new_paths, ';') - eq(new_paths_str, eval_lua('package.cpath'):sub(1, #new_paths_str)) - end) - it('preserves empty items', function() - local many_empty_path = ';;;;;;' - local many_empty_cpath = ';;;;;;./?.luaso' - set_path('path', many_empty_path) - set_path('cpath', many_empty_cpath) - meths.set_option('runtimepath', 'a') - local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {'a'}) - local new_paths_str = table.concat(new_paths, ';') - eq(new_paths_str .. ';' .. many_empty_path, eval_lua('package.path')) - local new_cpaths = get_new_paths({'/?.luaso'}, {'a'}) - local new_cpaths_str = table.concat(new_cpaths, ';') - eq(new_cpaths_str .. ';' .. many_empty_cpath, eval_lua('package.cpath')) - end) - it('preserves empty value', function() - set_path('path', '') - meths.set_option('runtimepath', 'a') - local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {'a'}) - local new_paths_str = table.concat(new_paths, ';') - eq(new_paths_str .. ';', eval_lua('package.path')) - end) - it('purges out all additions if runtimepath is set to empty', function() - local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}) - local new_paths_str = table.concat(new_paths, ';') - local path = eval_lua('package.path') - eq(new_paths_str, path:sub(1, #new_paths_str)) - - local new_cpaths = get_new_paths(iswin() and {'\\?.dll'} or {'/?.so'}) - local new_cpaths_str = table.concat(new_cpaths, ';') - local cpath = eval_lua('package.cpath') - eq(new_cpaths_str, cpath:sub(1, #new_cpaths_str)) - - meths.set_option('runtimepath', '') - eq(path:sub(#new_paths_str + 2, -1), eval_lua('package.path')) - eq(cpath:sub(#new_cpaths_str + 2, -1), eval_lua('package.cpath')) - end) - it('works with paths with escaped commas', function() - meths.set_option('runtimepath', '\\,') - local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {','}) - local new_paths_str = table.concat(new_paths, ';') - eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str)) - end) - it('ignores paths with semicolons', function() - meths.set_option('runtimepath', 'foo;bar,\\,') - local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {','}) - local new_paths_str = table.concat(new_paths, ';') - eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str)) - end) -end) - describe('os.getenv', function() it('returns nothing for undefined env var', function() eq(NIL, funcs.luaeval('os.getenv("XTEST_1")')) diff --git a/test/functional/lua/treesitter_spec.lua b/test/functional/lua/treesitter_spec.lua index 3526b64395..9eb5c8b1dd 100644 --- a/test/functional/lua/treesitter_spec.lua +++ b/test/functional/lua/treesitter_spec.lua @@ -80,13 +80,6 @@ describe('treesitter API with C parser', function() eq({1,2,1,12}, exec_lua("return {descendant:range()}")) eq("(declaration type: (primitive_type) declarator: (init_declarator declarator: (identifier) value: (number_literal)))", exec_lua("return descendant:sexpr()")) - eq(true, exec_lua("return child == child")) - -- separate lua object, but represents same node - eq(true, exec_lua("return child == root:child(0)")) - eq(false, exec_lua("return child == descendant2")) - eq(false, exec_lua("return child == nil")) - eq(false, exec_lua("return child == tree")) - feed("2G7|ay") exec_lua([[ tree2 = parser:parse() @@ -98,6 +91,21 @@ describe('treesitter API with C parser', function() eq("<node declaration>", exec_lua("return tostring(descendant2)")) eq({1,2,1,13}, exec_lua("return {descendant2:range()}")) + eq(true, exec_lua("return child == child")) + -- separate lua object, but represents same node + eq(true, exec_lua("return child == root:child(0)")) + eq(false, exec_lua("return child == descendant2")) + eq(false, exec_lua("return child == nil")) + eq(false, exec_lua("return child == tree")) + + eq("string", exec_lua("return type(child:id())")) + eq(true, exec_lua("return child:id() == child:id()")) + -- separate lua object, but represents same node + eq(true, exec_lua("return child:id() == root:child(0):id()")) + eq(false, exec_lua("return child:id() == descendant2:id()")) + eq(false, exec_lua("return child:id() == nil")) + eq(false, exec_lua("return child:id() == tree")) + -- orginal tree did not change eq({1,2,1,12}, exec_lua("return {descendant:range()}")) diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua index 01f0d8a4d7..21c01b3458 100644 --- a/test/functional/ui/cmdline_spec.lua +++ b/test/functional/ui/cmdline_spec.lua @@ -3,7 +3,6 @@ local Screen = require('test.functional.ui.screen') local clear, feed = helpers.clear, helpers.feed local source = helpers.source local command = helpers.command -local feed_command = helpers.feed_command local function new_screen(opt) local screen = Screen.new(25, 5) @@ -843,34 +842,3 @@ describe('cmdline redraw', function() ]], unchanged=true} end) end) - -describe('cmdline', function() - before_each(function() - clear() - end) - - it('prints every executed Ex command if verbose >= 16', function() - local screen = Screen.new(50, 12) - screen:attach() - source([[ - command DoSomething echo 'hello' |set ts=4 |let v = '123' |echo v - call feedkeys("\r", 't') " for the hit-enter prompt - set verbose=20 - ]]) - feed_command('DoSomething') - screen:expect([[ - | - ~ | - | - Executing: DoSomething | - Executing: echo 'hello' |set ts=4 |let v = '123' || - echo v | - hello | - Executing: set ts=4 |let v = '123' |echo v | - Executing: let v = '123' |echo v | - Executing: echo v | - 123 | - Press ENTER or type command to continue^ | - ]]) - end) -end) diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 304c5aecb1..4182090732 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -5,20 +5,32 @@ local clear = helpers.clear local feed = helpers.feed local insert = helpers.insert local exec_lua = helpers.exec_lua +local exec = helpers.exec local expect_events = helpers.expect_events +local meths = helpers.meths -describe('decorations provider', function() +describe('decorations providers', function() local screen before_each(function() clear() screen = Screen.new(40, 8) screen:attach() - screen:set_default_attr_ids({ - [1] = {bold=true, foreground=Screen.colors.Blue}, - }) + screen:set_default_attr_ids { + [1] = {bold=true, foreground=Screen.colors.Blue}; + [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}; + [3] = {foreground = Screen.colors.Brown}; + [4] = {foreground = Screen.colors.Blue1}; + [5] = {foreground = Screen.colors.Magenta}; + [6] = {bold = true, foreground = Screen.colors.Brown}; + [7] = {background = Screen.colors.Gray90}; + [8] = {bold = true, reverse = true}; + [9] = {reverse = true}; + [10] = {italic = true, background = Screen.colors.Magenta}; + [11] = {foreground = Screen.colors.Red, background = tonumber('0x005028')}; + } end) - local mudholland = [[ + local mulholland = [[ // just to see if there was an accident // on Mulholland Drive try_start(); @@ -28,21 +40,21 @@ describe('decorations provider', function() restore_buffer(&save_buf); ]] local function setup_provider(code) - exec_lua ([[ + return exec_lua ([[ local a = vim.api - test1 = a.nvim_create_namespace "test1" + _G.ns1 = a.nvim_create_namespace "ns1" ]] .. (code or [[ beamtrace = {} - function on_do(kind, ...) + local function on_do(kind, ...) table.insert(beamtrace, {kind, ...}) end ]]) .. [[ - a.nvim_set_decoration_provider( - test1, { + a.nvim_set_decoration_provider(_G.ns1, { on_start = on_do; on_buf = on_do; on_win = on_do; on_line = on_do; on_end = on_do; }) + return _G.ns1 ]]) end @@ -51,8 +63,8 @@ describe('decorations provider', function() expect_events(expected, actual, "beam trace") end - it('leaves a trace', function() - insert(mudholland) + it('leave a trace', function() + insert(mulholland) setup_provider() @@ -99,11 +111,12 @@ describe('decorations provider', function() } end) - it('single provider', function() - insert(mudholland) + it('can have single provider', function() + insert(mulholland) setup_provider [[ local hl = a.nvim_get_hl_id_by_name "ErrorMsg" - function do_it(event, ...) + local test_ns = a.nvim_create_namespace "mulholland" + function on_do(event, ...) if event == "line" then local win, buf, line = ... a.nvim_buf_set_extmark(buf, test_ns, line, line, @@ -114,5 +127,104 @@ describe('decorations provider', function() end end ]] + + screen:expect{grid=[[ + {2:/}/ just to see if there was an accident | + /{2:/} on Mulholland Drive | + tr{2:y}_start(); | + buf{2:r}ef_T save_buf; | + swit{2:c}h_buffer(&save_buf, buf); | + posp {2:=} getmark(mark, false); | + restor{2:e}_buffer(&save_buf);^ | + | + ]]} + end) + + it('can predefine highlights', function() + screen:try_resize(40, 16) + insert(mulholland) + exec [[ + 3 + set ft=c + syntax on + set number cursorline + split + ]] + local ns1 = setup_provider() + + for k,v in pairs { + LineNr = {italic=true, bg="Magenta"}; + Comment = {fg="#FF0000", bg = 80*256+40}; + CursorLine = {link="ErrorMsg"}; + } do meths.set_hl(ns1, k, v) end + + screen:expect{grid=[[ + {3: 1 }{4:// just to see if there was an accid}| + {3: }{4:ent} | + {3: 2 }{4:// on Mulholland Drive} | + {6: 3 }{7:^try_start(); }| + {3: 4 }bufref_T save_buf; | + {3: 5 }switch_buffer(&save_buf, buf); | + {3: 6 }posp = getmark(mark, {5:false}); | + {8:[No Name] [+] }| + {3: 2 }{4:// on Mulholland Drive} | + {6: 3 }{7:try_start(); }| + {3: 4 }bufref_T save_buf; | + {3: 5 }switch_buffer(&save_buf, buf); | + {3: 6 }posp = getmark(mark, {5:false}); | + {3: 7 }restore_buffer(&save_buf); | + {9:[No Name] [+] }| + | + ]]} + + meths.set_hl_ns(ns1) + screen:expect{grid=[[ + {10: 1 }{11:// just to see if there was an accid}| + {10: }{11:ent} | + {10: 2 }{11:// on Mulholland Drive} | + {6: 3 }{2:^try_start(); }| + {10: 4 }bufref_T save_buf; | + {10: 5 }switch_buffer(&save_buf, buf); | + {10: 6 }posp = getmark(mark, {5:false}); | + {8:[No Name] [+] }| + {10: 2 }{11:// on Mulholland Drive} | + {6: 3 }{2:try_start(); }| + {10: 4 }bufref_T save_buf; | + {10: 5 }switch_buffer(&save_buf, buf); | + {10: 6 }posp = getmark(mark, {5:false}); | + {10: 7 }restore_buffer(&save_buf); | + {9:[No Name] [+] }| + | + ]]} + + exec_lua [[ + local a = vim.api + local thewin = a.nvim_get_current_win() + local ns2 = a.nvim_create_namespace 'ns2' + a.nvim_set_decoration_provider (ns2, { + on_win = function (_, win, buf) + a.nvim_set_hl_ns(win == thewin and _G.ns1 or ns2) + end; + }) + ]] + screen:expect{grid=[[ + {10: 1 }{11:// just to see if there was an accid}| + {10: }{11:ent} | + {10: 2 }{11:// on Mulholland Drive} | + {6: 3 }{2:^try_start(); }| + {10: 4 }bufref_T save_buf; | + {10: 5 }switch_buffer(&save_buf, buf); | + {10: 6 }posp = getmark(mark, {5:false}); | + {8:[No Name] [+] }| + {3: 2 }{4:// on Mulholland Drive} | + {6: 3 }{7:try_start(); }| + {3: 4 }bufref_T save_buf; | + {3: 5 }switch_buffer(&save_buf, buf); | + {3: 6 }posp = getmark(mark, {5:false}); | + {3: 7 }restore_buffer(&save_buf); | + {9:[No Name] [+] }| + | + ]]} + end) end) diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index bf979e89f4..8fa9fcc42f 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -1289,7 +1289,7 @@ local function fmt_ext_state(name, state) for k,v in pairs(state) do str = (str.." ["..k.."] = {win = {id = "..v.win.id.."}, topline = " ..v.topline..", botline = "..v.botline..", curline = "..v.curline - ..", curcol = "..v.curcol.."},\n") + ..", curcol = "..v.curcol.."};\n") end return str .. "}" else @@ -1316,7 +1316,7 @@ function Screen:print_snapshot(attrs, ignore) dict = "{"..self:_pprint_attrs(a).."}" end local keyval = (type(i) == "number") and "["..tostring(i).."]" or i - table.insert(attrstrs, " "..keyval.." = "..dict..",") + table.insert(attrstrs, " "..keyval.." = "..dict..";") end attrstr = (", attr_ids={\n"..table.concat(attrstrs, "\n").."\n}") end diff --git a/test/unit/buffer_spec.lua b/test/unit/buffer_spec.lua index bf4e5a0e6d..3692e19379 100644 --- a/test/unit/buffer_spec.lua +++ b/test/unit/buffer_spec.lua @@ -212,7 +212,7 @@ describe('buffer functions', function() describe('build_stl_str_hl', function() local buffer_byte_size = 100 - local STL_MAX_ITEM = 80 + local STL_INITIAL_ITEMS = 20 local output_buffer = '' -- This function builds the statusline @@ -431,31 +431,23 @@ describe('buffer functions', function() 'aaaa%=b%=c%=d%=e%=fg%=hi%=jk%=lmnop%=qrstuv%=wxyz', 'aaaa b c d e fg hi jk lmnop qrstuv wxyz') - -- maximum stl item testing - statusline_test('should handle a much larger amount of = than buffer locations', 20, - ('%='):rep(STL_MAX_ITEM - 1), - ' ') -- Should be fine, because within limit - statusline_test('should handle a much larger amount of = than stl max item', 20, - ('%='):rep(STL_MAX_ITEM + 1), - ' E541') -- Should show the VIM error + -- stl item testing + local tabline = '' + for i= 1, 1000 do + tabline = tabline .. (i % 2 == 0 and '%#TabLineSel#' or '%#TabLineFill#') .. tostring(i % 2) + end + statusline_test('should handle a large amount of any items', 20, + tabline, + '<1010101010101010101') -- Should not show any error + statusline_test('should handle a larger amount of = than stl initial item', 20, + ('%='):rep(STL_INITIAL_ITEMS * 5), + ' ') -- Should not show any error statusline_test('should handle many extra characters', 20, - 'a' .. ('a'):rep(STL_MAX_ITEM * 4), - '<aaaaaaaaaaaaaaaaaaa') -- Does not show the error because there are no items - statusline_test('should handle almost maximum of characters and flags', 20, - 'a' .. ('%=a'):rep(STL_MAX_ITEM - 1), - 'a<aaaaaaaaaaaaaaaaaa') -- Should not show the VIM error - statusline_test('should handle many extra characters and flags', 20, - 'a' .. ('%=a'):rep(STL_MAX_ITEM), - 'a<aaaaaaaaaaaaa E541') -- Should show the VIM error + 'a' .. ('a'):rep(STL_INITIAL_ITEMS * 5), + '<aaaaaaaaaaaaaaaaaaa') -- Does not show any error statusline_test('should handle many extra characters and flags', 20, - 'a' .. ('%=a'):rep(STL_MAX_ITEM * 2), - 'a<aaaaaaaaaaaaa E541') -- Should show the VIM error - statusline_test('should handle many extra characters and flags with truncation', 20, - 'aaa%<' .. ('%=a'):rep(STL_MAX_ITEM), - 'aaa<aaaaaaaaaaa E541') -- Should show the VIM error - statusline_test('should handle many characters and flags before and after truncation', 20, - 'a%=a%=a%<' .. ('%=a'):rep(STL_MAX_ITEM), - 'aaa<aaaaaaaaaaa E541') -- Should show the VIM error + 'a' .. ('%=a'):rep(STL_INITIAL_ITEMS * 2), + 'a<aaaaaaaaaaaaaaaaaa') -- Should not show any error -- multi-byte testing diff --git a/third-party/cmake/GetBinaryDeps.cmake b/third-party/cmake/GetBinaryDeps.cmake index f262ae7159..982bf62265 100644 --- a/third-party/cmake/GetBinaryDeps.cmake +++ b/third-party/cmake/GetBinaryDeps.cmake @@ -39,7 +39,6 @@ function(GetBinaryDep) -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake CONFIGURE_COMMAND "" BUILD_IN_SOURCE 1 - CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_INSTALL_DIR}/bin COMMAND "${_gettool_INSTALL_COMMAND}") |