diff options
36 files changed, 711 insertions, 229 deletions
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 508dcacd3c..2d06b1e685 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -1,4 +1,4 @@ -image: freebsd/12.x +image: freebsd/latest packages: - cmake @@ -35,10 +35,6 @@ tasks: - unittest: | cd neovim gmake unittest - -# Unfortunately, oldtest is tanking hard on sourcehut's FreeBSD instance -# and not producing any logs as a result. So don't do this task for now. -# Ref: https://github.com/neovim/neovim/pull/11477#discussion_r352095005. -# - test-oldtest: | -# cd neovim -# gmake oldtest +- oldtest: | + cd neovim + gmake oldtest diff --git a/.builds/openbsd.yml b/.builds/openbsd.yml index 2f9ccdc1be..0aaa003820 100644 --- a/.builds/openbsd.yml +++ b/.builds/openbsd.yml @@ -1,6 +1,6 @@ # sourcehut CI: https://builds.sr.ht/~jmk/neovim -image: openbsd/6.9 +image: openbsd/latest packages: - autoconf-2.71 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3565b20bfc..41a22af538 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,11 @@ concurrency: jobs: lint: + # This job tests two things: it lints the code but also builds neovim using + # system dependencies instead of bundled dependencies. This is to make sure + # we are able to build neovim without pigeonholing ourselves into specifics + # of the bundled dependencies. + if: (github.event_name == 'pull_request' && github.base_ref == 'master' && !github.event.pull_request.draft) || (github.event_name == 'push' && github.ref == 'refs/heads/master') runs-on: ubuntu-20.04 timeout-minutes: 10 @@ -100,7 +105,7 @@ jobs: - name: Cache dependencies run: ./ci/before_cache.sh - unixish: + posix: name: ${{ matrix.runner }} ${{ matrix.flavor }} (cc=${{ matrix.cc }}) strategy: fail-fast: false @@ -120,6 +125,12 @@ jobs: - cc: clang runner: macos-11.0 os: osx + + # The functionaltest-lua test two things simultaneously: + # 1. Check that the tests pass with PUC Lua instead of LuaJIT. + # 2. Use as oldest/minimum versions of dependencies/build tools we + # still explicitly support so we don't accidentally rely on + # features that is only available on later versions. - flavor: functionaltest-lua cc: gcc runner: ubuntu-20.04 @@ -172,8 +183,8 @@ jobs: - name: Install brew packages if: matrix.os == 'osx' run: | - brew update >/dev/null - brew install automake ccache perl cpanminus ninja + brew update --quiet + brew install automake ccache cpanminus ninja - name: Setup interpreter packages run: ./ci/install.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e954b57175..8e0b13d59c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -85,7 +85,7 @@ jobs: fetch-depth: 0 - name: Install brew packages run: | - brew update >/dev/null + brew update --quiet brew install automake ninja - if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name != 'nightly') run: printf 'NVIM_BUILD_TYPE=Release\n' >> $GITHUB_ENV diff --git a/contrib/flake.lock b/contrib/flake.lock index c4d7f120ba..b72e1f8d5f 100644 --- a/contrib/flake.lock +++ b/contrib/flake.lock @@ -2,11 +2,11 @@ "nodes": { "flake-utils": { "locked": { - "lastModified": 1629481132, - "narHash": "sha256-JHgasjPR0/J1J3DRm4KxM4zTyAj4IOJY8vIl75v/kPI=", + "lastModified": 1644229661, + "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=", "owner": "numtide", "repo": "flake-utils", - "rev": "997f7efcb746a9c140ce1f13c72263189225f482", + "rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797", "type": "github" }, "original": { @@ -17,11 +17,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1630074300, - "narHash": "sha256-BFM7OiXRs0RvSUZd6NCGAKWVPn3VodgYQ4TUQXxbMBU=", + "lastModified": 1646254136, + "narHash": "sha256-8nQx02tTzgYO21BP/dy5BCRopE8OwE8Drsw98j+Qoaw=", "owner": "nixos", "repo": "nixpkgs", - "rev": "21c937f8cb1e6adcfeb36dfd6c90d9d9bfab1d28", + "rev": "3e072546ea98db00c2364b81491b893673267827", "type": "github" }, "original": { diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 73536d174a..eefe6e5a47 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -3165,7 +3165,7 @@ nvim_create_autocmd({event}, {*opts}) *nvim_create_autocmd()* • create a |autocmd-buflocal| autocmd. • NOTE: Cannot be used with {pattern} - • group: (string) The augroup name + • group: (string|int) The augroup name or id • once: (boolean) - See |autocmd-once| • nested: (boolean) - See |autocmd-nested| • desc: (string) - Description of the autocmd @@ -3213,7 +3213,7 @@ nvim_do_autocmd({event}, {*opts}) *nvim_do_autocmd()* "*". • NOTE: Cannot be used with {buffer} - • group (string) - autocmd group name + • group (string|int) - autocmd group name or id • modeline (boolean) - Default true, see |<nomodeline>| @@ -3224,7 +3224,8 @@ nvim_get_autocmds({*opts}) *nvim_get_autocmds()* {opts} Optional Parameters: • event : Name or list of name of events to match against - • group (string): Name of group to match against + • group (string|int): Name or id of group to match + against • pattern: Pattern or list of patterns to match against. Cannot be used with {buffer} • buffer: Buffer number or list of buffer numbers diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index 05529dc90a..994f97bba0 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -161,6 +161,11 @@ Q Repeat the last recorded register [count] times. result of evaluating the expression is executed as an Ex command. Mappings are not recognized in these commands. + When the |line-continuation| character (\) is present + at the beginning of a line in a linewise register, + then it is combined with the previous line. This is + useful for yanking and executing parts of a Vim + script. *:@:* :[addr]@: Repeat last command-line. First set cursor at line diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index ddd1147468..2251aca004 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -3,9 +3,11 @@ -- Lua code lives in one of three places: -- 1. runtime/lua/vim/ (the runtime): For "nice to have" features, e.g. the -- `inspect` and `lpeg` modules. --- 2. runtime/lua/vim/shared.lua: Code shared between Nvim and tests. --- (This will go away if we migrate to nvim as the test-runner.) --- 3. runtime/lua/vim/_editor.lua: Compiled-into Nvim itself. +-- 2. runtime/lua/vim/shared.lua: pure lua functions which always +-- are available. Used in the test runner, as well as worker threads +-- and processes launched from Nvim. +-- 3. runtime/lua/vim/_editor.lua: Code which directly interacts with +-- the Nvim editor state. Only available in the main thread. -- -- Guideline: "If in doubt, put it in the runtime". -- @@ -35,43 +37,19 @@ -- - https://github.com/howl-editor/howl/tree/master/lib/howl/util local vim = assert(vim) -assert(vim.inspect) -- These are for loading runtime modules lazily since they aren't available in -- the nvim binary as specified in executor.c -setmetatable(vim, { - __index = function(t, key) - if key == 'treesitter' then - t.treesitter = require('vim.treesitter') - return t.treesitter - elseif key == 'filetype' then - t.filetype = require('vim.filetype') - return t.filetype - elseif key == 'F' then - t.F = require('vim.F') - return t.F - elseif require('vim.uri')[key] ~= nil then - -- Expose all `vim.uri` functions on the `vim` module. - t[key] = require('vim.uri')[key] - return t[key] - elseif key == 'lsp' then - t.lsp = require('vim.lsp') - return t.lsp - elseif key == 'highlight' then - t.highlight = require('vim.highlight') - return t.highlight - elseif key == 'diagnostic' then - t.diagnostic = require('vim.diagnostic') - return t.diagnostic - elseif key == 'keymap' then - t.keymap = require('vim.keymap') - return t.keymap - elseif key == 'ui' then - t.ui = require('vim.ui') - return t.ui - end - end -}) +for k,v in pairs { + treesitter=true; + filetype = true; + F=true; + lsp=true; + highlight=true; + diagnostic=true; + keymap=true; + ui=true; +} do vim._submodules[k] = v end vim.log = { levels = { diff --git a/runtime/lua/vim/_init_packages.lua b/runtime/lua/vim/_init_packages.lua index dcb402287c..7d27741f1b 100644 --- a/runtime/lua/vim/_init_packages.lua +++ b/runtime/lua/vim/_init_packages.lua @@ -50,7 +50,25 @@ table.insert(package.loaders, 2, vim._load_package) -- builtin functions which always should be available require'vim.shared' -vim.inspect = require'vim.inspect' + +vim._submodules = {inspect=true} + +-- These are for loading runtime modules in the vim namespace lazily. +setmetatable(vim, { + __index = function(t, key) + if vim._submodules[key] then + t[key] = require('vim.'..key) + return t[key] + elseif vim.startswith(key, 'uri_') then + local val = require('vim.uri')[key] + if val ~= nil then + -- Expose all `vim.uri` functions on the `vim` module. + t[key] = val + return t[key] + end + end + end +}) --- <Docs described in |vim.empty_dict()| > ---@private diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 48d0bd3672..8124b23eb1 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -1,8 +1,10 @@ -- Functions shared by Nvim and its test-suite. -- --- The singular purpose of this module is to share code with the Nvim --- test-suite. If, in the future, Nvim itself is used to run the test-suite --- instead of "vanilla Lua", these functions could move to runtime/lua/vim/_editor.lua +-- These are "pure" lua functions not depending of the state of the editor. +-- Thus they should always be available whenever nvim-related lua code is run, +-- regardless if it is code in the editor itself, or in worker threads/processes, +-- or the test suite. (Eventually the test suite will be run in a worker process, +-- so this wouldn't be a separate case to consider) local vim = vim or {} diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 9abefbe02a..9f29cae29d 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -342,6 +342,7 @@ add_custom_command( ${LUA_KEYMAP_MODULE_SOURCE} "vim.keymap" DEPENDS ${CHAR_BLOB_GENERATOR} + ${LUA_INIT_PACKAGES_MODULE_SOURCE} ${LUA_EDITOR_MODULE_SOURCE} ${LUA_SHARED_MODULE_SOURCE} ${LUA_INSPECT_MODULE_SOURCE} diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 685667ac64..5ede0e5265 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -40,7 +40,7 @@ static int64_t next_autocmd_id = 1; /// /// @param opts Optional Parameters: /// - event : Name or list of name of events to match against -/// - group (string): Name of group to match against +/// - group (string|int): Name or id of group to match against /// - pattern: Pattern or list of patterns to match against. Cannot be used with {buffer} /// - buffer: Buffer number or list of buffer numbers for buffer local autocommands /// |autocmd-buflocal|. Cannot be used with {pattern} @@ -62,19 +62,27 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) int group = 0; - if (opts->group.type != kObjectTypeNil) { - Object v = opts->group; - if (v.type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, "group must be a string."); - goto cleanup; - } - - group = augroup_find(v.data.string.data); - - if (group < 0) { - api_set_error(err, kErrorTypeValidation, "invalid augroup passed."); + switch (opts->group.type) { + case kObjectTypeNil: + break; + case kObjectTypeString: + group = augroup_find(opts->group.data.string.data); + if (group < 0) { + api_set_error(err, kErrorTypeValidation, "invalid augroup passed."); + goto cleanup; + } + break; + case kObjectTypeInteger: + group = (int)opts->group.data.integer; + char *name = augroup_name(group); + if (!augroup_exists(name)) { + api_set_error(err, kErrorTypeValidation, "invalid augroup passed."); + goto cleanup; + } + break; + default: + api_set_error(err, kErrorTypeValidation, "group must be a string or an integer."); goto cleanup; - } } if (opts->event.type != kObjectTypeNil) { @@ -321,7 +329,7 @@ cleanup: /// - buffer: (bufnr) /// - create a |autocmd-buflocal| autocmd. /// - NOTE: Cannot be used with {pattern} -/// - group: (string) The augroup name +/// - group: (string|int) The augroup name or id /// - once: (boolean) - See |autocmd-once| /// - nested: (boolean) - See |autocmd-nested| /// - desc: (string) - Description of the autocmd @@ -406,23 +414,29 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc bool is_once = api_object_to_bool(opts->once, "once", false, err); bool is_nested = api_object_to_bool(opts->nested, "nested", false, err); - // TODO(tjdevries): accept number for namespace instead - if (opts->group.type != kObjectTypeNil) { - Object *v = &opts->group; - if (v->type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, "'group' must be a string"); - goto cleanup; - } - - au_group = augroup_find(v->data.string.data); - - if (au_group == AUGROUP_ERROR) { - api_set_error(err, - kErrorTypeException, - "invalid augroup: %s", v->data.string.data); - + switch (opts->group.type) { + case kObjectTypeNil: + break; + case kObjectTypeString: + au_group = augroup_find(opts->group.data.string.data); + if (au_group == AUGROUP_ERROR) { + api_set_error(err, + kErrorTypeValidation, + "invalid augroup: %s", opts->group.data.string.data); + goto cleanup; + } + break; + case kObjectTypeInteger: + au_group = (int)opts->group.data.integer; + char *name = augroup_name(au_group); + if (!augroup_exists(name)) { + api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group); + goto cleanup; + } + break; + default: + api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer."); goto cleanup; - } } if (opts->pattern.type != kObjectTypeNil && opts->buffer.type != kObjectTypeNil) { @@ -624,7 +638,7 @@ void nvim_del_augroup_by_name(String name) /// - NOTE: Cannot be used with {pattern} /// - pattern (string|table) - optional, defaults to "*". /// - NOTE: Cannot be used with {buffer} -/// - group (string) - autocmd group name +/// - group (string|int) - autocmd group name or id /// - modeline (boolean) - Default true, see |<nomodeline>| void nvim_do_autocmd(Object event, Dict(do_autocmd) *opts, Error *err) FUNC_API_SINCE(9) @@ -644,21 +658,29 @@ void nvim_do_autocmd(Object event, Dict(do_autocmd) *opts, Error *err) goto cleanup; } - if (opts->group.type != kObjectTypeNil) { - if (opts->group.type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, "'group' must be a string"); - goto cleanup; - } - - au_group = augroup_find(opts->group.data.string.data); - - if (au_group == AUGROUP_ERROR) { - api_set_error(err, - kErrorTypeException, - "invalid augroup: %s", opts->group.data.string.data); - + switch (opts->group.type) { + case kObjectTypeNil: + break; + case kObjectTypeString: + au_group = augroup_find(opts->group.data.string.data); + if (au_group == AUGROUP_ERROR) { + api_set_error(err, + kErrorTypeValidation, + "invalid augroup: %s", opts->group.data.string.data); + goto cleanup; + } + break; + case kObjectTypeInteger: + au_group = (int)opts->group.data.integer; + char *name = augroup_name(au_group); + if (!augroup_exists(name)) { + api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group); + goto cleanup; + } + break; + default: + api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer."); goto cleanup; - } } if (opts->buffer.type != kObjectTypeNil) { diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 7cb493f57d..a850e5c1a0 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -105,15 +105,24 @@ static char_u *old_termresponse = NULL; #define FOR_ALL_AUPATS_IN_EVENT(event, ap) \ for (AutoPat *ap = first_autopat[event]; ap != NULL; ap = ap->next) // NOLINT -// Map of autocmd group names. +// Map of autocmd group names and ids. // name -> ID -static Map(String, int) augroup_map = MAP_INIT; +// ID -> name +static Map(String, int) map_augroup_name_to_id = MAP_INIT; +static Map(int, String) map_augroup_id_to_name = MAP_INIT; -static void augroup_map_del(char *name) +static void augroup_map_del(int id, char *name) { - String key = map_key(String, int)(&augroup_map, cstr_as_string(name)); - map_del(String, int)(&augroup_map, key); - api_free_string(key); + if (name != NULL) { + String key = map_key(String, int)(&map_augroup_name_to_id, cstr_as_string(name)); + map_del(String, int)(&map_augroup_name_to_id, key); + api_free_string(key); + } + if (id > 0) { + String mapped = map_get(int, String)(&map_augroup_id_to_name, id); + api_free_string(mapped); + map_del(int, String)(&map_augroup_id_to_name, id); + } } @@ -382,12 +391,14 @@ int augroup_add(char *name) } if (existing_id == AUGROUP_DELETED) { - augroup_map_del(name); + augroup_map_del(existing_id, name); } int next_id = next_augroup_id++; - String name_copy = cstr_to_string(name); - map_put(String, int)(&augroup_map, name_copy, next_id); + String name_key = cstr_to_string(name); + String name_val = cstr_to_string(name); + map_put(String, int)(&map_augroup_name_to_id, name_key, next_id); + map_put(int, String)(&map_augroup_id_to_name, next_id, name_val); return next_id; } @@ -416,7 +427,8 @@ void augroup_del(char *name, bool stupid_legacy_mode) FOR_ALL_AUPATS_IN_EVENT(event, ap) { if (ap->group == i && ap->pat != NULL) { give_warning((char_u *)_("W19: Deleting augroup that is still in use"), true); - map_put(String, int)(&augroup_map, cstr_as_string(name), AUGROUP_DELETED); + map_put(String, int)(&map_augroup_name_to_id, cstr_as_string(name), AUGROUP_DELETED); + augroup_map_del(ap->group, NULL); return; } } @@ -432,7 +444,7 @@ void augroup_del(char *name, bool stupid_legacy_mode) } // Remove the group because it's not currently in use. - augroup_map_del(name); + augroup_map_del(i, name); au_cleanup(); } } @@ -445,7 +457,7 @@ void augroup_del(char *name, bool stupid_legacy_mode) int augroup_find(const char *name) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - int existing_id = map_get(String, int)(&augroup_map, cstr_as_string((char *)name)); + int existing_id = map_get(String, int)(&map_augroup_name_to_id, cstr_as_string((char *)name)); if (existing_id == AUGROUP_DELETED) { return existing_id; } @@ -487,13 +499,10 @@ char *augroup_name(int group) return NULL; } - String key; - int value; - map_foreach(&augroup_map, key, value, { - if (value == group) { - return key.data; - } - }); + String key = map_get(int, String)(&map_augroup_id_to_name, group); + if (key.data != NULL) { + return key.data; + } // If it's not in the map anymore, then it must have been deleted. return (char *)get_deleted_augroup(); @@ -526,7 +535,7 @@ void do_augroup(char_u *arg, int del_group) String name; int value; - map_foreach(&augroup_map, name, value, { + map_foreach(&map_augroup_name_to_id, name, value, { if (value > 0) { msg_puts(name.data); } else { @@ -556,11 +565,17 @@ void free_all_autocmds(void) // Delete the augroup_map, including free the data String name; int id; - map_foreach(&augroup_map, name, id, { + map_foreach(&map_augroup_name_to_id, name, id, { + (void)id; + api_free_string(name); + }) + map_destroy(String, int)(&map_augroup_name_to_id); + + map_foreach(&map_augroup_id_to_name, id, name, { (void)id; api_free_string(name); }) - map_destroy(String, int)(&augroup_map); + map_destroy(int, String)(&map_augroup_id_to_name); } #endif diff --git a/src/nvim/eval.c b/src/nvim/eval.c index aa7b9e80a4..8cdb03c341 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7789,8 +7789,7 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co break; case kCallbackLua: - ILOG(" We tryin to call dat dang lua ref "); - nlua_call_ref(callback->data.luaref, "aucmd", args, false, NULL); + nlua_call_ref(callback->data.luaref, NULL, args, false, NULL); return false; break; diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index d207dcb527..f47c9c482b 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -11411,7 +11411,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) case 'u': { const size_t len = STRLEN(what); if (len <= 5 || (TOLOWER_ASC(what[5]) == 'l' && len <= 9)) { // underline - p = highlight_has_attr(id, HL_UNDERCURL, modec); + p = highlight_has_attr(id, HL_UNDERLINE, modec); } else if (TOLOWER_ASC(what[5]) == 'c') { // undercurl p = highlight_has_attr(id, HL_UNDERCURL, modec); } else if (len > 9 && TOLOWER_ASC(what[9]) == 'l') { // underlineline diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 2a40e50014..5c040adc1c 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -1698,7 +1698,7 @@ static bool concat_continued_line(garray_T *const ga, const int init_growsize, return false; } if (ga->ga_len > init_growsize) { - ga_set_growsize(ga, MAX(ga->ga_len, 8000)); + ga_set_growsize(ga, MIN(ga->ga_len, 8000)); } ga_concat_len(ga, (const char *)line + 1, len - 1); return true; @@ -1852,7 +1852,7 @@ static void cmd_source_buffer(const exarg_T *const eap) for (linenr_T curr_lnum = eap->line1; curr_lnum <= final_lnum; curr_lnum++) { // Adjust growsize to current length to speed up concatenating many lines. if (ga.ga_len > 400) { - ga_set_growsize(&ga, MAX(ga.ga_len, 8000)); + ga_set_growsize(&ga, MIN(ga.ga_len, 8000)); } ga_concat(&ga, (char *)ml_get(curr_lnum)); ga_append(&ga, NL); diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 29a3c515c2..6aaff100ca 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -565,7 +565,8 @@ static bool nlua_init_packages(lua_State *lstate) lua_getglobal(lstate, "require"); lua_pushstring(lstate, "vim._init_packages"); if (nlua_pcall(lstate, 1, 0)) { - nlua_error(lstate, _("E5106: Error while loading packages: %.*s\n")); + mch_errmsg(lua_tostring(lstate, -1)); + mch_errmsg("\n"); return false; } diff --git a/src/nvim/map.c b/src/nvim/map.c index 091d653046..4e39eb8c07 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -178,6 +178,7 @@ MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER) MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER) MAP_IMPL(String, handle_T, 0) MAP_IMPL(String, int, DEFAULT_INITIALIZER) +MAP_IMPL(int, String, DEFAULT_INITIALIZER) MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER) diff --git a/src/nvim/map.h b/src/nvim/map.h index c9c89bf2fd..00f72386a7 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -47,6 +47,7 @@ MAP_DECLS(String, MsgpackRpcRequestHandler) MAP_DECLS(HlEntry, int) MAP_DECLS(String, handle_T) MAP_DECLS(String, int) +MAP_DECLS(int, String) MAP_DECLS(ColorKey, ColorItem) diff --git a/src/nvim/ops.c b/src/nvim/ops.c index b5c7020dee..3f51210781 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -1023,6 +1023,60 @@ static int stuff_yank(int regname, char_u *p) static int execreg_lastc = NUL; +/// When executing a register as a series of ex-commands, if the +/// line-continuation character is used for a line, then join it with one or +/// more previous lines. Note that lines are processed backwards starting from +/// the last line in the register. +/// +/// @param lines list of lines in the register +/// @param idx index of the line starting with \ or "\. Join this line with all the immediate +/// predecessor lines that start with a \ and the first line that doesn't start +/// with a \. Lines that start with a comment "\ character are ignored. +/// @returns the concatenated line. The index of the line that should be +/// processed next is returned in idx. +static char_u *execreg_line_continuation(char_u **lines, size_t *idx) +{ + size_t i = *idx; + assert(i > 0); + const size_t cmd_end = i; + + garray_T ga; + ga_init(&ga, (int)sizeof(char_u), 400); + + char_u *p; + + // search backwards to find the first line of this command. + // Any line not starting with \ or "\ is the start of the + // command. + while (--i > 0) { + p = skipwhite(lines[i]); + if (*p != '\\' && (p[0] != '"' || p[1] != '\\' || p[2] != ' ')) { + break; + } + } + const size_t cmd_start = i; + + // join all the lines + ga_concat(&ga, (char *)lines[cmd_start]); + for (size_t j = cmd_start + 1; j <= cmd_end; j++) { + p = skipwhite(lines[j]); + if (*p == '\\') { + // Adjust the growsize to the current length to + // speed up concatenating many lines. + if (ga.ga_len > 400) { + ga_set_growsize(&ga, MIN(ga.ga_len, 8000)); + } + ga_concat(&ga, (char *)(p + 1)); + } + } + ga_append(&ga, NUL); + char_u *str = vim_strsave(ga.ga_data); + ga_clear(&ga); + + *idx = i; + return str; +} + /// Execute a yank register: copy it into the stuff buffer /// /// @param colon insert ':' before each line @@ -1111,7 +1165,21 @@ int do_execreg(int regname, int colon, int addcr, int silent) return FAIL; } } - escaped = vim_strsave_escape_ks(reg->y_array[i]); + + // Handle line-continuation for :@<register> + char_u *str = reg->y_array[i]; + bool free_str = false; + if (colon && i > 0) { + p = skipwhite(str); + if (*p == '\\' || (p[0] == '"' && p[1] == '\\' && p[2] == ' ')) { + str = execreg_line_continuation(reg->y_array, &i); + free_str = true; + } + } + escaped = vim_strsave_escape_ks(str); + if (free_str) { + xfree(str); + } retval = ins_typebuf(escaped, remap, 0, true, silent); xfree(escaped); if (retval == FAIL) { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index ee1acd8d03..1cdee7c972 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2890,9 +2890,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } else if (wp->w_p_cul && lnum == wp->w_cursor.lnum && (wp->w_p_culopt_flags & CULOPT_NBR) - && (row == startrow - || wp->w_p_culopt_flags & CULOPT_LINE) - && filler_todo == 0) { + && (row == startrow + filler_lines + || (row > startrow + filler_lines + && wp->w_p_culopt_flags & CULOPT_LINE))) { // When 'cursorline' is set highlight the line number of // the current line differently. // When 'cursorlineopt' has "screenline" only highlight diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim index 53ed4617f7..4229095f9f 100644 --- a/src/nvim/testdir/test_autochdir.vim +++ b/src/nvim/testdir/test_autochdir.vim @@ -26,6 +26,54 @@ func Test_set_filename() call delete('samples/Xtest') endfunc +func Test_set_filename_other_window() + CheckFunction test_autochdir + let cwd = getcwd() + call test_autochdir() + call mkdir('Xa') + call mkdir('Xb') + call mkdir('Xc') + try + args Xa/aaa.txt Xb/bbb.txt + set acd + let winid = win_getid() + snext + call assert_equal('Xb', substitute(getcwd(), '.*/\([^/]*\)$', '\1', '')) + call win_execute(winid, 'file ' .. cwd .. '/Xc/ccc.txt') + call assert_equal('Xb', substitute(getcwd(), '.*/\([^/]*\)$', '\1', '')) + finally + set noacd + call chdir(cwd) + call delete('Xa', 'rf') + call delete('Xb', 'rf') + call delete('Xc', 'rf') + bwipe! aaa.txt + bwipe! bbb.txt + bwipe! ccc.txt + endtry +endfunc + +func Test_acd_win_execute() + CheckFunction test_autochdir + let cwd = getcwd() + set acd + call test_autochdir() + + call mkdir('Xfile') + let winid = win_getid() + new Xfile/file + call assert_match('testdir.Xfile$', getcwd()) + cd .. + call assert_match('testdir$', getcwd()) + call win_execute(winid, 'echo') + call assert_match('testdir$', getcwd()) + + bwipe! + set noacd + call chdir(cwd) + call delete('Xfile', 'rf') +endfunc + func Test_verbose_pwd() CheckFunction test_autochdir let cwd = getcwd() @@ -42,20 +90,27 @@ func Test_verbose_pwd() set acd wincmd w call assert_match('\[autochdir\].*testdir$', execute('verbose pwd')) - execute 'lcd' cwd - call assert_match('\[window\].*testdir$', execute('verbose pwd')) execute 'tcd' cwd call assert_match('\[tabpage\].*testdir$', execute('verbose pwd')) execute 'cd' cwd call assert_match('\[global\].*testdir$', execute('verbose pwd')) + execute 'lcd' cwd + call assert_match('\[window\].*testdir$', execute('verbose pwd')) edit call assert_match('\[autochdir\].*testdir$', execute('verbose pwd')) + enew + wincmd w + call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd')) + wincmd w + call assert_match('\[window\].*testdir$', execute('verbose pwd')) wincmd w call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd')) set noacd call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd')) wincmd w - call assert_match('\[global\].*testdir', execute('verbose pwd')) + call assert_match('\[window\].*testdir$', execute('verbose pwd')) + execute 'cd' cwd + call assert_match('\[global\].*testdir$', execute('verbose pwd')) wincmd w call assert_match('\[window\].*testdir[/\\]Xautodir', execute('verbose pwd')) diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index 482d39056f..10eb979b45 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -1017,6 +1017,32 @@ func Test_diff_with_cursorline() call delete('Xtest_diff_cursorline') endfunc +func Test_diff_with_cursorline_number() + CheckScreendump + + let lines =<< trim END + hi CursorLine ctermbg=red ctermfg=white + hi CursorLineNr ctermbg=white ctermfg=black cterm=underline + set cursorline number + call setline(1, ["baz", "foo", "foo", "bar"]) + 2 + vnew + call setline(1, ["foo", "foo", "bar"]) + windo diffthis + 1wincmd w + END + call writefile(lines, 'Xtest_diff_cursorline_number') + let buf = RunVimInTerminal('-S Xtest_diff_cursorline_number', {}) + + call VerifyScreenDump(buf, 'Test_diff_with_cursorline_number_01', {}) + call term_sendkeys(buf, ":set cursorlineopt=number\r") + call VerifyScreenDump(buf, 'Test_diff_with_cursorline_number_02', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('Xtest_diff_cursorline_number') +endfunc + func Test_diff_with_cursorline_breakindent() CheckScreendump diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index 23e39eba35..ecc65b240b 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -482,6 +482,82 @@ func Test_v_register() bwipe! endfunc +" Test for executing the contents of a register as an Ex command with line +" continuation. +func Test_execute_reg_as_ex_cmd() + " Line continuation with just two lines + let code =<< trim END + let l = [ + \ 1] + END + let @r = code->join("\n") + let l = [] + @r + call assert_equal([1], l) + + " Line continuation with more than two lines + let code =<< trim END + let l = [ + \ 1, + \ 2, + \ 3] + END + let @r = code->join("\n") + let l = [] + @r + call assert_equal([1, 2, 3], l) + + " use comments interspersed with code + let code =<< trim END + let l = [ + "\ one + \ 1, + "\ two + \ 2, + "\ three + \ 3] + END + let @r = code->join("\n") + let l = [] + @r + call assert_equal([1, 2, 3], l) + + " use line continuation in the middle + let code =<< trim END + let a = "one" + let l = [ + \ 1, + \ 2] + let b = "two" + END + let @r = code->join("\n") + let l = [] + @r + call assert_equal([1, 2], l) + call assert_equal("one", a) + call assert_equal("two", b) + + " only one line with a \ + let @r = "\\let l = 1" + call assert_fails('@r', 'E10:') + + " only one line with a "\ + let @r = ' "\ let i = 1' + @r + call assert_false(exists('i')) + + " first line also begins with a \ + let @r = "\\let l = [\n\\ 1]" + call assert_fails('@r', 'E10:') + + " Test with a large number of lines + let @r = "let str = \n" + let @r ..= repeat(" \\ 'abcdefghijklmnopqrstuvwxyz' ..\n", 312) + let @r ..= ' \ ""' + @r + call assert_equal(repeat('abcdefghijklmnopqrstuvwxyz', 312), str) +endfunc + func Test_ve_blockpaste() new set ve=all diff --git a/src/nvim/window.c b/src/nvim/window.c index 83048d911f..d5299202b0 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -6763,9 +6763,6 @@ void restore_win_noblock(switchwin_T *switchwin, bool no_display) curwin = switchwin->sw_curwin; curbuf = curwin->w_buffer; } - // If called by win_execute() and executing the command changed the - // directory, it now has to be restored. - fix_current_dir(); } /// Make "buf" the current buffer. diff --git a/src/nvim/window.h b/src/nvim/window.h index e2fd2c515d..42701f72b4 100644 --- a/src/nvim/window.h +++ b/src/nvim/window.h @@ -5,6 +5,7 @@ #include "nvim/buffer_defs.h" #include "nvim/mark.h" +#include "nvim/os/os.h" // Values for file_name_in_line() #define FNAME_MESS 1 // give error message @@ -47,12 +48,36 @@ typedef struct { do { \ win_T *const wp_ = (wp); \ const pos_T curpos_ = wp_->w_cursor; \ + char_u cwd_[MAXPATHL]; \ + char_u autocwd_[MAXPATHL]; \ + bool apply_acd_ = false; \ + int cwd_status_ = FAIL; \ + /* Getting and setting directory can be slow on some systems, only do */ \ + /* this when the current or target window/tab have a local directory or */ \ + /* 'acd' is set. */ \ + if (curwin != wp \ + && (curwin->w_localdir != NULL || wp->w_localdir != NULL \ + || (curtab != tp && (curtab->tp_localdir != NULL || tp->tp_localdir != NULL)) \ + || p_acd)) { \ + cwd_status_ = os_dirname(cwd_, MAXPATHL); \ + } \ + /* If 'acd' is set, check we are using that directory. If yes, then */ \ + /* apply 'acd' afterwards, otherwise restore the current directory. */ \ + if (cwd_status_ == OK && p_acd) { \ + do_autochdir(); \ + apply_acd_ = os_dirname(autocwd_, MAXPATHL) == OK && STRCMP(cwd_, autocwd_) == 0; \ + } \ switchwin_T switchwin_; \ if (switch_win_noblock(&switchwin_, wp_, (tp), true) == OK) { \ check_cursor(); \ block; \ } \ restore_win_noblock(&switchwin_, true); \ + if (apply_acd_) { \ + do_autochdir(); \ + } else if (cwd_status_ == OK) { \ + os_chdir((char *)cwd_); \ + } \ /* Update the status line if the cursor moved. */ \ if (win_valid(wp_) && !equalpos(curpos_, wp_->w_cursor)) { \ wp_->w_redr_status = true; \ diff --git a/test/functional/api/autocmd_spec.lua b/test/functional/api/autocmd_spec.lua index e8dc284925..372cbf2c30 100644 --- a/test/functional/api/autocmd_spec.lua +++ b/test/functional/api/autocmd_spec.lua @@ -281,6 +281,31 @@ describe('autocmd api', function() eq("Too many buffers. Please limit yourself to 256 or fewer", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = bufs })) end) + + it('should return autocmds when group is specified by id', function() + local auid = meths.create_augroup("nvim_test_augroup", { clear = true }) + meths.create_autocmd("FileType", { group = auid, command = 'echo "1"' }) + meths.create_autocmd("FileType", { group = auid, command = 'echo "2"' }) + + local aus = meths.get_autocmds { group = auid } + eq(2, #aus) + + local aus2 = meths.get_autocmds { group = auid, event = "InsertEnter" } + eq(0, #aus2) + end) + + it('should return autocmds when group is specified by name', function() + local auname = "nvim_test_augroup" + meths.create_augroup(auname, { clear = true }) + meths.create_autocmd("FileType", { group = auname, command = 'echo "1"' }) + meths.create_autocmd("FileType", { group = auname, command = 'echo "2"' }) + + local aus = meths.get_autocmds { group = auname } + eq(2, #aus) + + local aus2 = meths.get_autocmds { group = auname, event = "InsertEnter" } + eq(0, #aus2) + end) end) describe('groups', function() @@ -331,7 +356,7 @@ describe('autocmd api', function() end) describe('groups: 2', function() - it('raises error for undefined augroup', function() + it('raises error for undefined augroup name', function() local success, code = unpack(meths.exec_lua([[ return {pcall(function() vim.api.nvim_create_autocmd("FileType", { @@ -345,6 +370,39 @@ describe('autocmd api', function() eq(false, success) matches('invalid augroup: NotDefined', code) end) + + it('raises error for undefined augroup id', function() + local success, code = unpack(meths.exec_lua([[ + return {pcall(function() + -- Make sure the augroup is deleted + vim.api.nvim_del_augroup_by_id(1) + + vim.api.nvim_create_autocmd("FileType", { + pattern = "*", + group = 1, + command = "echo 'hello'", + }) + end)} + ]], {})) + + eq(false, success) + matches('invalid augroup: 1', code) + end) + + it('raises error for invalid group type', function() + local success, code = unpack(meths.exec_lua([[ + return {pcall(function() + vim.api.nvim_create_autocmd("FileType", { + pattern = "*", + group = true, + command = "echo 'hello'", + }) + end)} + ]], {})) + + eq(false, success) + matches("'group' must be a string or an integer", code) + end) end) describe('patterns', function() @@ -497,6 +555,35 @@ describe('autocmd api', function() meths.do_autocmd("User", { pattern = "TestCommand" }) eq('matched', meths.get_var('matched')) end) + + it('can pass group by id', function() + meths.set_var("group_executed", false) + + local auid = meths.create_augroup("nvim_test_augroup", { clear = true }) + meths.create_autocmd("FileType", { + group = auid, + command = 'let g:group_executed = v:true', + }) + + eq(false, meths.get_var("group_executed")) + meths.do_autocmd("FileType", { group = auid }) + eq(true, meths.get_var("group_executed")) + end) + + it('can pass group by name', function() + meths.set_var("group_executed", false) + + local auname = "nvim_test_augroup" + meths.create_augroup(auname, { clear = true }) + meths.create_autocmd("FileType", { + group = auname, + command = 'let g:group_executed = v:true', + }) + + eq(false, meths.get_var("group_executed")) + meths.do_autocmd("FileType", { group = auname }) + eq(true, meths.get_var("group_executed")) + end) end) describe('nvim_create_augroup', function() diff --git a/test/functional/legacy/autochdir_spec.lua b/test/functional/legacy/autochdir_spec.lua index 37a94476a0..13cb6cd287 100644 --- a/test/functional/legacy/autochdir_spec.lua +++ b/test/functional/legacy/autochdir_spec.lua @@ -1,8 +1,12 @@ local lfs = require('lfs') local helpers = require('test.functional.helpers')(after_each) local clear, eq, matches = helpers.clear, helpers.eq, helpers.matches -local eval, command, call = helpers.eval, helpers.command, helpers.call -local exec_capture = helpers.exec_capture +local eval, command, call, meths = helpers.eval, helpers.command, helpers.call, helpers.meths +local source, exec_capture = helpers.source, helpers.exec_capture + +local function expected_empty() + eq({}, meths.get_vvar('errors')) +end describe('autochdir behavior', function() local dir = 'Xtest_functional_legacy_autochdir' @@ -10,19 +14,66 @@ describe('autochdir behavior', function() before_each(function() lfs.mkdir(dir) clear() + command('set shellslash') end) after_each(function() helpers.rmdir(dir) end) - -- Tests vim/vim/777 without test_autochdir(). + -- Tests vim/vim#777 without test_autochdir(). it('sets filename', function() command('set acd') command('new') command('w '..dir..'/Xtest') eq('Xtest', eval("expand('%')")) - eq(dir, eval([[substitute(getcwd(), '.*[/\\]\(\k*\)', '\1', '')]])) + eq(dir, eval([[substitute(getcwd(), '.*/\(\k*\)', '\1', '')]])) + end) + + it(':file in win_execute() does not cause wrong directory', function() + command('cd '..dir) + source([[ + func Test_set_filename_other_window() + let cwd = getcwd() + call mkdir('Xa') + call mkdir('Xb') + call mkdir('Xc') + try + args Xa/aaa.txt Xb/bbb.txt + set acd + let winid = win_getid() + snext + call assert_equal('Xb', substitute(getcwd(), '.*/\([^/]*\)$', '\1', '')) + call win_execute(winid, 'file ' .. cwd .. '/Xc/ccc.txt') + call assert_equal('Xb', substitute(getcwd(), '.*/\([^/]*\)$', '\1', '')) + finally + set noacd + call chdir(cwd) + call delete('Xa', 'rf') + call delete('Xb', 'rf') + call delete('Xc', 'rf') + bwipe! aaa.txt + bwipe! bbb.txt + bwipe! ccc.txt + endtry + endfunc + ]]) + call('Test_set_filename_other_window') + expected_empty() + end) + + it('win_execute() does not change directory', function() + local subdir = 'Xfile' + command('cd '..dir) + command('set acd') + call('mkdir', subdir) + local winid = eval('win_getid()') + command('new '..subdir..'/file') + matches(dir..'/'..subdir..'$', eval('getcwd()')) + command('cd ..') + matches(dir..'$', eval('getcwd()')) + call('win_execute', winid, 'echo') + matches(dir..'$', eval('getcwd()')) end) it(':verbose pwd shows whether autochdir is used', function() @@ -30,29 +81,36 @@ describe('autochdir behavior', function() command('cd '..dir) local cwd = eval('getcwd()') command('edit global.txt') - matches('%[global%].*'..dir, exec_capture('verbose pwd')) + matches('%[global%].*'..dir..'$', exec_capture('verbose pwd')) call('mkdir', subdir) command('split '..subdir..'/local.txt') command('lcd '..subdir) - matches('%[window%].*'..dir..'[/\\]'..subdir, exec_capture('verbose pwd')) - command('set autochdir') + matches('%[window%].*'..dir..'/'..subdir..'$', exec_capture('verbose pwd')) + command('set acd') command('wincmd w') - matches('%[autochdir%].*'..dir, exec_capture('verbose pwd')) - command('lcd '..cwd) - matches('%[window%].*'..dir, exec_capture('verbose pwd')) + matches('%[autochdir%].*'..dir..'$', exec_capture('verbose pwd')) command('tcd '..cwd) - matches('%[tabpage%].*'..dir, exec_capture('verbose pwd')) + matches('%[tabpage%].*'..dir..'$', exec_capture('verbose pwd')) command('cd '..cwd) - matches('%[global%].*'..dir, exec_capture('verbose pwd')) + matches('%[global%].*'..dir..'$', exec_capture('verbose pwd')) + command('lcd '..cwd) + matches('%[window%].*'..dir..'$', exec_capture('verbose pwd')) command('edit') - matches('%[autochdir%].*'..dir, exec_capture('verbose pwd')) + matches('%[autochdir%].*'..dir..'$', exec_capture('verbose pwd')) + command('enew') command('wincmd w') - matches('%[autochdir%].*'..dir..'[/\\]'..subdir, exec_capture('verbose pwd')) - command('set noautochdir') - matches('%[autochdir%].*'..dir..'[/\\]'..subdir, exec_capture('verbose pwd')) + matches('%[autochdir%].*'..dir..'/'..subdir..'$', exec_capture('verbose pwd')) command('wincmd w') - matches('%[global%].*'..dir, exec_capture('verbose pwd')) + matches('%[window%].*'..dir..'$', exec_capture('verbose pwd')) + command('wincmd w') + matches('%[autochdir%].*'..dir..'/'..subdir..'$', exec_capture('verbose pwd')) + command('set noacd') + matches('%[autochdir%].*'..dir..'/'..subdir..'$', exec_capture('verbose pwd')) + command('wincmd w') + matches('%[window%].*'..dir..'$', exec_capture('verbose pwd')) + command('cd '..cwd) + matches('%[global%].*'..dir..'$', exec_capture('verbose pwd')) command('wincmd w') - matches('%[window%].*'..dir..'[/\\]'..subdir, exec_capture('verbose pwd')) + matches('%[window%].*'..dir..'/'..subdir..'$', exec_capture('verbose pwd')) end) end) diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 3dacb7a114..e66e08d9d0 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -1,6 +1,5 @@ -- Test suite for testing interactions with API bindings local helpers = require('test.functional.helpers')(after_each) -local isCI = require('test.helpers').isCI local Screen = require('test.functional.ui.screen') local funcs = helpers.funcs @@ -2558,9 +2557,6 @@ describe('lua: builtin modules', function() it('does not work when disabled without runtime', function() - if isCI('sourcehut') then - pending('causes a core dump') - end clear{args={'--luamod-dev'}, env={VIMRUNTIME='fixtures/a'}} -- error checking could be better here. just check that --luamod-dev -- does anything at all by breaking with missing runtime.. diff --git a/test/functional/options/autochdir_spec.lua b/test/functional/options/autochdir_spec.lua index 2fce0a5ed9..74959a8e76 100644 --- a/test/functional/options/autochdir_spec.lua +++ b/test/functional/options/autochdir_spec.lua @@ -1,7 +1,9 @@ +local lfs = require('lfs') local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear local eq = helpers.eq -local getcwd = helpers.funcs.getcwd +local funcs = helpers.funcs +local command = helpers.command describe("'autochdir'", function() it('given on the shell gets processed properly', function() @@ -9,11 +11,34 @@ describe("'autochdir'", function() -- By default 'autochdir' is off, thus getcwd() returns the repo root. clear(targetdir..'/tty-test.c') - local rootdir = getcwd() + local rootdir = funcs.getcwd() local expected = rootdir .. '/' .. targetdir -- With 'autochdir' on, we should get the directory of tty-test.c. clear('--cmd', 'set autochdir', targetdir..'/tty-test.c') - eq(helpers.iswin() and expected:gsub('/', '\\') or expected, getcwd()) + eq(helpers.iswin() and expected:gsub('/', '\\') or expected, funcs.getcwd()) + end) + + it('is not overwritten by getwinvar() call #17609',function() + local curdir = string.gsub(lfs.currentdir(), '\\', '/') + local dir_a = curdir..'/Xtest-functional-options-autochdir.dir_a' + local dir_b = curdir..'/Xtest-functional-options-autochdir.dir_b' + lfs.mkdir(dir_a) + lfs.mkdir(dir_b) + clear() + command('set shellslash') + command('set autochdir') + command('edit '..dir_a..'/file1') + eq(dir_a, funcs.getcwd()) + command('lcd '..dir_b) + eq(dir_b, funcs.getcwd()) + command('botright vnew ../file2') + eq(curdir, funcs.getcwd()) + command('wincmd w') + eq(dir_a, funcs.getcwd()) + funcs.getwinvar(2, 'foo') + eq(dir_a, funcs.getcwd()) + helpers.rmdir(dir_a) + helpers.rmdir(dir_b) end) end) diff --git a/test/functional/terminal/highlight_spec.lua b/test/functional/terminal/highlight_spec.lua index ebbae1df14..ab4b4e9147 100644 --- a/test/functional/terminal/highlight_spec.lua +++ b/test/functional/terminal/highlight_spec.lua @@ -305,10 +305,17 @@ describe('synIDattr()', function() eq('79', eval('synIDattr(hlID("Keyword"), "fg")')) end) - it('returns "1" if group has "strikethrough" attribute', function() - eq('', eval('synIDattr(hlID("Normal"), "strikethrough")')) - eq('1', eval('synIDattr(hlID("Keyword"), "strikethrough")')) - eq('1', eval('synIDattr(hlID("Keyword"), "strikethrough", "gui")')) + it('returns "1" if group has given highlight attribute', function() + local hl_attrs = { + 'underline', 'underlineline', 'undercurl', 'underdot', 'underdash', 'strikethrough' + } + for _,hl_attr in ipairs(hl_attrs) do + local context = 'using ' .. hl_attr .. ' attr' + command('highlight Keyword cterm=' .. hl_attr .. ' gui=' .. hl_attr) + eq('', eval('synIDattr(hlID("Normal"), "'.. hl_attr .. '")'), context) + eq('1', eval('synIDattr(hlID("Keyword"), "' .. hl_attr .. '")'), context) + eq('1', eval('synIDattr(hlID("Keyword"), "' .. hl_attr .. '", "gui")'), context) + end end) end) diff --git a/test/functional/ui/diff_spec.lua b/test/functional/ui/diff_spec.lua index bd2692d19a..3a25d7e813 100644 --- a/test/functional/ui/diff_spec.lua +++ b/test/functional/ui/diff_spec.lua @@ -6,7 +6,7 @@ local clear = helpers.clear local command = helpers.command local insert = helpers.insert local write_file = helpers.write_file -local source = helpers.source +local exec = helpers.exec describe('Diff mode screen', function() local fname = 'Xtest-functional-diff-screen-1' @@ -1075,10 +1075,8 @@ it('diff updates line numbers below filler lines', function() [9] = {background = Screen.colors.LightMagenta}, [10] = {bold = true, foreground = Screen.colors.Brown}, [11] = {foreground = Screen.colors.Brown}, - [12] = {foreground = Screen.colors.Brown, bold = true, background = Screen.colors.Red}; - [13] = {background = Screen.colors.Gray90}; }) - source([[ + exec([[ call setline(1, ['a', 'a', 'a', 'y', 'b', 'b', 'b', 'b', 'b']) vnew call setline(1, ['a', 'a', 'a', 'x', 'x', 'x', 'b', 'b', 'b', 'b', 'b']) @@ -1135,24 +1133,6 @@ it('diff updates line numbers below filler lines', function() {3:[No Name] [+] }{7:[No Name] [+] }| | ]]) - command("set signcolumn number tgc cursorline cursorlineopt=number,line") - command("hi CursorLineNr guibg=red") - screen:expect{grid=[[ - {1: }a {3:│}{11: 2 }a | - {1: }a {3:│}{11: 1 }a | - {1: }a {3:│}{12:3 }{13:^a }| - {1: }{8:x}{9: }{3:│}{11: 1 }{8:y}{9: }| - {1: }{4:x }{3:│}{11: }{2:----------------}| - {1: }{4:x }{3:│}{11: }{2:----------------}| - {1: }b {3:│}{11: 2 }b | - {1: }b {3:│}{11: 3 }b | - {1: }b {3:│}{11: 4 }b | - {1: }b {3:│}{11: 5 }b | - {1: }b {3:│}{11: 6 }b | - {6:~ }{3:│}{6:~ }| - {3:[No Name] [+] }{7:[No Name] [+] }| - signcolumn=auto | - ]]} end) it('Align the filler lines when changing text in diff mode', function() @@ -1169,7 +1149,7 @@ it('Align the filler lines when changing text in diff mode', function() [7] = {foreground = Screen.colors.Blue1, bold = true}; [8] = {reverse = true, bold = true}; }) - source([[ + exec([[ call setline(1, range(1, 15)) vnew call setline(1, range(9, 15)) diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 12643eec1e..64f0ba3419 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -840,7 +840,7 @@ describe("'listchars' highlight", function() end) end) -describe('CursorLine highlight', function() +describe('CursorLine and CursorLineNr highlights', function() before_each(clear) it('overridden by Error, ColorColumn if fg not set', function() @@ -1081,7 +1081,7 @@ describe('CursorLine highlight', function() ]]) end) - it('with split-windows in diff-mode', function() + it('with split windows in diff mode', function() local screen = Screen.new(50,12) screen:set_default_attr_ids({ [1] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray}, @@ -1093,7 +1093,6 @@ describe('CursorLine highlight', function() [7] = {background = Screen.colors.Red, foreground = Screen.colors.White}, [8] = {bold = true, foreground = Screen.colors.Blue1}, [9] = {bold = true, reverse = true}, - [10] = {bold = true}, }) screen:attach() @@ -1170,6 +1169,61 @@ describe('CursorLine highlight', function() background = Screen.colors.Red}, }) end) + + it('CursorLineNr shows correctly just below filler lines', function() + local screen = Screen.new(50,12) + screen:set_default_attr_ids({ + [1] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray}, + [2] = {background = Screen.colors.LightCyan1, bold = true, foreground = Screen.colors.Blue1}, + [3] = {reverse = true}, + [4] = {background = Screen.colors.LightBlue}, + [5] = {background = Screen.colors.Red, foreground = Screen.colors.White}, + [6] = {background = Screen.colors.White, bold = true, foreground = Screen.colors.Black}, + [7] = {bold = true, foreground = Screen.colors.Blue1}, + [8] = {bold = true, reverse = true}, + [9] = {foreground = Screen.colors.Brown}, + }) + screen:attach() + + command('hi CursorLine guibg=red guifg=white') + command('hi CursorLineNr guibg=white guifg=black gui=bold') + command('set cursorline number') + command('call setline(1, ["baz", "foo", "foo", "bar"])') + feed('2gg0') + command('vnew') + command('call setline(1, ["foo", "foo", "bar"])') + command('windo diffthis') + command('1wincmd w') + screen:expect([[ + {1: }{9: }{2:-------------------}{3:│}{1: }{9: 1 }{4:baz }| + {1: }{6: 1 }{5:^foo }{3:│}{1: }{6: 2 }{5:foo }| + {1: }{9: 2 }foo {3:│}{1: }{9: 3 }foo | + {1: }{9: 3 }bar {3:│}{1: }{9: 4 }bar | + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {8:[No Name] [+] }{3:[No Name] [+] }| + | + ]]) + command('set cursorlineopt=number') + screen:expect([[ + {1: }{9: }{2:-------------------}{3:│}{1: }{9: 1 }{4:baz }| + {1: }{6: 1 }^foo {3:│}{1: }{6: 2 }{5:foo }| + {1: }{9: 2 }foo {3:│}{1: }{9: 3 }foo | + {1: }{9: 3 }bar {3:│}{1: }{9: 4 }bar | + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {8:[No Name] [+] }{3:[No Name] [+] }| + | + ]]) + end) end) describe('ColorColumn highlight', function() diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index 3c96954c9f..30c4752b87 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -144,8 +144,8 @@ endif() include(ExternalProject) -set(LIBUV_URL https://github.com/libuv/libuv/archive/v1.42.0.tar.gz) -set(LIBUV_SHA256 371e5419708f6aaeb8656671f89400b92a9bba6443369af1bb70bcd6e4b3c764) +set(LIBUV_URL https://github.com/libuv/libuv/archive/v1.44.0.tar.gz) +set(LIBUV_SHA256 f2482d547009d26d4d590ed6588043c540e707c833df52536744cb9a809e6617) set(MSGPACK_URL https://github.com/msgpack/msgpack-c/releases/download/cpp-3.0.0/msgpack-3.0.0.tar.gz) set(MSGPACK_SHA256 bfbb71b7c02f806393bc3cbc491b40523b89e64f83860c58e3e54af47de176e4) @@ -203,8 +203,8 @@ set(LIBICONV_SHA256 ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc891 set(TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/v0.20.1.tar.gz) set(TREESITTER_C_SHA256 ffcc2ef0eded59ad1acec9aec4f9b0c7dd209fc1a85d85f8b0e81298e3dddcc2) -set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/v0.20.3.tar.gz) -set(TREESITTER_SHA256 ab52fe93e0c658cff656b9d10d67cdd29084247052964eba13ed6f0e9fa3bd36) +set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/v0.20.6.tar.gz) +set(TREESITTER_SHA256 4d37eaef8a402a385998ff9aca3e1043b4a3bba899bceeff27a7178e1165b9de) if(USE_BUNDLED_UNIBILIUM) include(BuildUnibilium) diff --git a/third-party/cmake/BuildLibuv.cmake b/third-party/cmake/BuildLibuv.cmake index 1d6a217575..42650308a8 100644 --- a/third-party/cmake/BuildLibuv.cmake +++ b/third-party/cmake/BuildLibuv.cmake @@ -63,10 +63,6 @@ elseif(WIN32) set(BUILD_SHARED ON) elseif(MINGW) set(BUILD_SHARED OFF) - set(LIBUV_PATCH_COMMAND - ${GIT_EXECUTABLE} -C ${DEPS_BUILD_DIR}/src/libuv init - COMMAND ${GIT_EXECUTABLE} -C ${DEPS_BUILD_DIR}/src/libuv apply --ignore-whitespace - ${CMAKE_CURRENT_SOURCE_DIR}/patches/libuv-disable-typedef-MinGW.patch) else() message(FATAL_ERROR "Trying to build libuv in an unsupported system ${CMAKE_SYSTEM_NAME}/${CMAKE_C_COMPILER_ID}") endif() diff --git a/third-party/patches/libuv-disable-typedef-MinGW.patch b/third-party/patches/libuv-disable-typedef-MinGW.patch deleted file mode 100644 index a47893cede..0000000000 --- a/third-party/patches/libuv-disable-typedef-MinGW.patch +++ /dev/null @@ -1,19 +0,0 @@ -diff --git a/include/uv/win.h b/include/uv/win.h -index f5f1d3a3..64a0dfd9 100644 ---- a/include/uv/win.h -+++ b/include/uv/win.h -@@ -45,7 +45,14 @@ typedef struct pollfd { - #endif - - #include <mswsock.h> -+// Disable the typedef in mstcpip.h of MinGW. -+#define _TCP_INITIAL_RTO_PARAMETERS _TCP_INITIAL_RTO_PARAMETERS__ -+#define TCP_INITIAL_RTO_PARAMETERS TCP_INITIAL_RTO_PARAMETERS__ -+#define PTCP_INITIAL_RTO_PARAMETERS PTCP_INITIAL_RTO_PARAMETERS__ - #include <ws2tcpip.h> -+#undef _TCP_INITIAL_RTO_PARAMETERS -+#undef TCP_INITIAL_RTO_PARAMETERS -+#undef PTCP_INITIAL_RTO_PARAMETERS - #include <windows.h> - - #include <process.h> |