diff options
45 files changed, 438 insertions, 191 deletions
diff --git a/.github/workflows/notes.md b/.github/workflows/notes.md index 86f9e8e618..d752f10609 100644 --- a/.github/workflows/notes.md +++ b/.github/workflows/notes.md @@ -21,8 +21,9 @@ ${NVIM_VERSION} ### macOS 1. Download **nvim-macos.tar.gz** -2. Extract: `tar xzvf nvim-macos.tar.gz` -3. Run `./nvim-macos/bin/nvim` +2. Run `xattr -c ./nvim-macos.tar.gz` (to avoid "unknown developer" warning) +3. Extract: `tar xzvf nvim-macos.tar.gz` +4. Run `./nvim-macos/bin/nvim` ### Linux (x64) diff --git a/CMakeLists.txt b/CMakeLists.txt index dad0093cf0..8434a8824c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -444,7 +444,7 @@ if(TS_HAS_SET_ALLOCATOR) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNVIM_TS_HAS_SET_ALLOCATOR") endif() -# Note: The test lib requires LuaJIT; it will be skipped if LuaJIT is missing. +# The unit test lib requires LuaJIT; it will be skipped if LuaJIT is missing. option(PREFER_LUA "Prefer Lua over LuaJIT in the nvim executable." OFF) if(PREFER_LUA) @@ -573,11 +573,7 @@ endif() message(STATUS "Using Lua interpreter: ${LUA_PRG}") -if(DEBUG) - option(COMPILE_LUA "Pre-compile Lua sources into bytecode (for sources that are included in the binary)" OFF) -else() - option(COMPILE_LUA "Pre-compile Lua sources into bytecode (for sources that are included in the binary)" ON) -endif() +option(COMPILE_LUA "Pre-compile Lua sources into bytecode (for sources that are included in the binary)" ON) if(COMPILE_LUA AND NOT WIN32) if(PREFER_LUA) @@ -645,7 +641,7 @@ endif() include(InstallHelpers) install_helper( - FILES ${CMAKE_SOURCE_DIR}/man/nvim.1 + FILES ${CMAKE_SOURCE_DIR}/src/man/nvim.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) # @@ -678,7 +674,7 @@ if(BUSTED_PRG) list(APPEND TEST_TARGET_ARGS "USES_TERMINAL") set(UNITTEST_PREREQS nvim-test unittest-headers) - set(FUNCTIONALTEST_PREREQS nvim printenv-test printargs-test shell-test streams-test tty-test ${GENERATED_HELP_TAGS}) + set(FUNCTIONALTEST_PREREQS nvim printenv-test printargs-test shell-test pwsh-test streams-test tty-test ${GENERATED_HELP_TAGS}) set(BENCHMARK_PREREQS nvim tty-test) check_lua_module(${LUA_PRG} "ffi" LUA_HAS_FFI) diff --git a/MAINTAIN.md b/MAINTAIN.md index c644b88ad7..3bf5ab3c85 100644 --- a/MAINTAIN.md +++ b/MAINTAIN.md @@ -52,7 +52,7 @@ has a major bug: 3. Cut a release from `release-x.y`. - Run `./scripts/release.sh` - Update (force-push) the remote `stable` tag. - - The [nightly job](https://github.com/neovim/neovim/blob/master/.github/workflows/release.yml#L4) + - The [CI job](https://github.com/neovim/neovim/blob/3d45706478cd030c3ee05b4f336164bb96138095/.github/workflows/release.yml#L11-L13) will update the release assets based on the `stable` tag. The neovim repository includes a backport [github action](https://github.com/zeebe-io/backport-action). diff --git a/contrib/luarc.json b/contrib/luarc.json index 770b023ac6..8d76d1261d 100644 --- a/contrib/luarc.json +++ b/contrib/luarc.json @@ -5,6 +5,7 @@ "globals": [ "vim", "describe", + "pending", "it", "before_each", "after_each", diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 9f8d4a8479..b79699c89b 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -2064,14 +2064,55 @@ add({filetypes}) *vim.filetype.add()* {filetypes} (table) A table containing new filetype maps (see example). -match({name}, {bufnr}) *vim.filetype.match()* - Find the filetype for the given filename and buffer. +match({arg}) *vim.filetype.match()* + Perform filetype detection. + + The filetype can be detected using one of three methods: + 1. Using an existing buffer + 2. Using only a file name + 3. Using only file contents + + Of these, option 1 provides the most accurate result as it + uses both the buffer's filename and (optionally) the buffer + contents. Options 2 and 3 can be used without an existing + buffer, but may not always provide a match in cases where the + filename (or contents) cannot unambiguously determine the + filetype. + + Each of the three options is specified using a key to the + single argument of this function. Example: +> + + -- Using a buffer number + vim.filetype.match({ buf = 42 }) + + -- Override the filename of the given buffer + vim.filetype.match({ buf = 42, filename = 'foo.c' }) + + -- Using a filename without a buffer + vim.filetype.match({ filename = 'main.lua' }) + + -- Using file contents + vim.filetype.match({ contents = {'#!/usr/bin/env bash'} }) +< Parameters: ~ - {name} (string) File name (can be an absolute or - relative path) - {bufnr} (number|nil) The buffer to set the filetype for. - Defaults to the current buffer. + {arg} (table) Table specifying which matching strategy to + use. Accepted keys are: + • buf (number): Buffer number to use for matching. + Mutually exclusive with {contents} + • filename (string): Filename to use for matching. + When {buf} is given, defaults to the filename of + the given buffer number. The file need not + actually exist in the filesystem. When used + without {buf} only the name of the file is used + for filetype matching. This may result in failure + to detect the filetype in cases where the + filename alone is not enough to disambiguate the + filetype. + • contents (table): An array of lines representing + file contents to use for matching. Can be used + with {filename}. Mutually exclusive with {buf}. Return: ~ (string|nil) If a match was found, the matched filetype. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 20805377d8..4d86f792da 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -5292,9 +5292,9 @@ A jump table for the options with a short description can be found at |Q_op|. unescaping, so to keep yourself sane use |:let-&| like shown above. *shell-powershell* To use PowerShell: > - let &shell = has('win32') ? 'powershell' : 'pwsh' + let &shell = executable('pwsh') ? 'pwsh' : 'powershell' let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command [Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;' - let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode' + let &shellredir = '-RedirectStandardOutput %s -NoNewWindow -Wait' let &shellpipe = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode' set shellquote= shellxquote= diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index 562faeaa2c..9eb6470962 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -346,7 +346,11 @@ g8 Print the hex values of the bytes used in the Only string variables can be used. After the redirection starts, if the variable is removed or locked or the variable type is changed, then further - command output messages will cause errors. + command output messages will cause errors. When using + a local variable (l:var in a function or s:var in a + script) and another `:redir` causes the current one to + end, the scope might be different and the assignment + fails. To get the output of one command the |execute()| function can be used instead of redirection. diff --git a/runtime/filetype.lua b/runtime/filetype.lua index b002b8971b..35bb31edce 100644 --- a/runtime/filetype.lua +++ b/runtime/filetype.lua @@ -12,7 +12,7 @@ vim.api.nvim_create_augroup('filetypedetect', { clear = false }) vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile' }, { group = 'filetypedetect', callback = function(args) - local ft, on_detect = vim.filetype.match(args.file, args.buf) + local ft, on_detect = vim.filetype.match({ buf = args.buf }) if ft then vim.api.nvim_buf_set_option(args.buf, 'filetype', ft) if on_detect then diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 6c4894208f..73605413ee 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -2047,7 +2047,7 @@ local pattern = { end end, { priority = -math.huge + 1 }), ['XF86Config.*'] = starsetf(function(path, bufnr) - return require('vim.filetype.detect').xfree86(bufnr) + return require('vim.filetype.detect').xfree86() end), ['%.zcompdump.*'] = starsetf('zsh'), -- .zlog* and zlog* @@ -2185,17 +2185,24 @@ end local function dispatch(ft, path, bufnr, ...) local on_detect if type(ft) == 'function' then - ft, on_detect = ft(path, bufnr, ...) + if bufnr then + ft, on_detect = ft(path, bufnr, ...) + else + -- If bufnr is nil (meaning we are matching only against the filename), set it to an invalid + -- value (-1) and catch any errors from the filetype detection function. If the function tries + -- to use the buffer then it will fail, but this enables functions which do not need a buffer + -- to still work. + local ok + ok, ft, on_detect = pcall(ft, path, -1, ...) + if not ok then + return + end + end end if type(ft) == 'string' then return ft, on_detect end - - -- Any non-falsey value (that is, anything other than 'nil' or 'false') will - -- end filetype matching. This is useful for e.g. the dist#ft functions that - -- return 0, but set the buffer's filetype themselves - return ft end ---@private @@ -2214,29 +2221,86 @@ local function match_pattern(name, path, tail, pat) return matches end ---- Find the filetype for the given filename and buffer. +--- Perform filetype detection. +--- +--- The filetype can be detected using one of three methods: +--- 1. Using an existing buffer +--- 2. Using only a file name +--- 3. Using only file contents +--- +--- Of these, option 1 provides the most accurate result as it uses both the buffer's filename and +--- (optionally) the buffer contents. Options 2 and 3 can be used without an existing buffer, but +--- may not always provide a match in cases where the filename (or contents) cannot unambiguously +--- determine the filetype. +--- +--- Each of the three options is specified using a key to the single argument of this function. +--- Example: +--- +--- <pre> +--- -- Using a buffer number +--- vim.filetype.match({ buf = 42 }) +--- +--- -- Override the filename of the given buffer +--- vim.filetype.match({ buf = 42, filename = 'foo.c' }) --- ----@param name string File name (can be an absolute or relative path) ----@param bufnr number|nil The buffer to set the filetype for. Defaults to the current buffer. +--- -- Using a filename without a buffer +--- vim.filetype.match({ filename = 'main.lua' }) +--- +--- -- Using file contents +--- vim.filetype.match({ contents = {'#!/usr/bin/env bash'} }) +--- </pre> +--- +---@param arg table Table specifying which matching strategy to use. Accepted keys are: +--- * buf (number): Buffer number to use for matching. Mutually exclusive with +--- {contents} +--- * filename (string): Filename to use for matching. When {buf} is given, +--- defaults to the filename of the given buffer number. The +--- file need not actually exist in the filesystem. When used +--- without {buf} only the name of the file is used for +--- filetype matching. This may result in failure to detect +--- the filetype in cases where the filename alone is not +--- enough to disambiguate the filetype. +--- * contents (table): An array of lines representing file contents to use for +--- matching. Can be used with {filename}. Mutually exclusive +--- with {buf}. ---@return string|nil If a match was found, the matched filetype. ---@return function|nil A function that modifies buffer state when called (for example, to set some --- filetype specific buffer variables). The function accepts a buffer number as --- its only argument. -function M.match(name, bufnr) +function M.match(arg) vim.validate({ - name = { name, 's' }, - bufnr = { bufnr, 'n', true }, + arg = { arg, 't' }, }) - -- When fired from the main filetypedetect autocommand the {bufnr} argument is omitted, so we use - -- the current buffer. The {bufnr} argument is provided to allow extensibility in case callers - -- wish to perform filetype detection on buffers other than the current one. - bufnr = bufnr or api.nvim_get_current_buf() + if not (arg.buf or arg.filename or arg.contents) then + error('At least one of "buf", "filename", or "contents" must be given') + end + + if arg.buf and arg.contents then + error('Only one of "buf" or "contents" must be given') + end + + local bufnr = arg.buf + local name = arg.filename + local contents = arg.contents + + if bufnr and not name then + name = api.nvim_buf_get_name(bufnr) + end - name = normalize_path(name) + if name then + name = normalize_path(name) + end local ft, on_detect + if contents then + -- Sanity check: this should not happen + assert(not bufnr, '"buf" and "contents" are mutually exclusive') + -- TODO: "scripts.lua" content matching + return + end + -- First check for the simple case where the full path exists as a key local path = vim.fn.resolve(vim.fn.fnamemodify(name, ':p')) ft, on_detect = dispatch(filename[path], path, bufnr) diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index f76bdebe9b..c4e90eb373 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -2,7 +2,7 @@ " " Author: Bram Moolenaar " Copyright: Vim license applies, see ":help license" -" Last Change: 2022 Jun 22 +" Last Change: 2022 Jun 24 " " WORK IN PROGRESS - The basics works stable, more to come " Note: In general you need at least GDB 7.12 because this provides the @@ -539,6 +539,7 @@ func TermDebugSendCommand(cmd) endif sleep 10m endif + " TODO: should we prepend CTRL-U to clear the command? call chansend(s:gdb_job_id, a:cmd . "\r") if do_continue Continue diff --git a/man/Makefile b/src/man/Makefile index 3c0457e2ab..3c0457e2ab 100644 --- a/man/Makefile +++ b/src/man/Makefile diff --git a/man/nvim.1 b/src/man/nvim.1 index 9f35014ee8..9f35014ee8 100644 --- a/man/nvim.1 +++ b/src/man/nvim.1 diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 3a35e49dc8..bf6402f938 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -549,9 +549,7 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc int retval; - for (size_t i = 0; i < patterns.size; i++) { - Object pat = patterns.items[i]; - + FOREACH_ITEM(patterns, pat, { // See: TODO(sctx) WITH_SCRIPT_CONTEXT(channel_id, { retval = autocmd_register(autocmd_id, @@ -569,7 +567,7 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc api_set_error(err, kErrorTypeException, "Failed to set autocmd"); goto cleanup; } - } + }) }); cleanup: @@ -781,14 +779,12 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) bool modeline = true; buf_T *buf = curbuf; - bool set_buf = false; - - char *pattern = NULL; - Object *data = NULL; - bool set_pattern = false; + Array patterns = ARRAY_DICT_INIT; Array event_array = ARRAY_DICT_INIT; + Object *data = NULL; + if (!unpack_string_or_array(&event_array, &event, "event", true, err)) { goto cleanup; } @@ -826,21 +822,18 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) } buf = find_buffer_by_handle((Buffer)buf_obj.data.integer, err); - set_buf = true; if (ERROR_SET(err)) { goto cleanup; } } - if (opts->pattern.type != kObjectTypeNil) { - if (opts->pattern.type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, "'pattern' must be a string"); - goto cleanup; - } + if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, opts->buffer, err)) { + goto cleanup; + } - pattern = string_to_cstr(opts->pattern.data.string); - set_pattern = true; + if (patterns.size == 0) { + ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING(""))); } if (opts->data.type != kObjectTypeNil) { @@ -849,16 +842,15 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) modeline = api_object_to_bool(opts->modeline, "modeline", true, err); - if (set_pattern && set_buf) { - api_set_error(err, kErrorTypeValidation, "must not set 'buffer' and 'pattern'"); - goto cleanup; - } - bool did_aucmd = false; FOREACH_ITEM(event_array, event_str, { GET_ONE_EVENT(event_nr, event_str, cleanup) - did_aucmd |= apply_autocmds_group(event_nr, pattern, NULL, true, au_group, buf, NULL, data); + FOREACH_ITEM(patterns, pat, { + char *fname = opts->buffer.type == kObjectTypeNil ? pat.data.string.data : NULL; + did_aucmd |= + apply_autocmds_group(event_nr, fname, NULL, true, au_group, buf, NULL, data); + }) }) if (did_aucmd && modeline) { @@ -867,13 +859,13 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) cleanup: api_free_array(event_array); - XFREE_CLEAR(pattern); + api_free_array(patterns); } static bool check_autocmd_string_array(Array arr, char *k, Error *err) { - for (size_t i = 0; i < arr.size; i++) { - if (arr.items[i].type != kObjectTypeString) { + FOREACH_ITEM(arr, entry, { + if (entry.type != kObjectTypeString) { api_set_error(err, kErrorTypeValidation, "All entries in '%s' must be strings", @@ -882,13 +874,13 @@ static bool check_autocmd_string_array(Array arr, char *k, Error *err) } // Disallow newlines in the middle of the line. - const String l = arr.items[i].data.string; + const String l = entry.data.string; if (memchr(l.data, NL, l.size)) { api_set_error(err, kErrorTypeValidation, "String cannot contain newlines"); return false; } - } + }) return true; } @@ -975,8 +967,8 @@ static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Ob } Array array = v->data.array; - for (size_t i = 0; i < array.size; i++) { - char *pat = array.items[i].data.string.data; + FOREACH_ITEM(array, entry, { + char *pat = entry.data.string.data; size_t patlen = aucmd_pattern_length(pat); while (patlen) { ADD(*patterns, STRING_OBJ(cbuf_to_string((char *)pat, patlen))); @@ -984,15 +976,15 @@ static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Ob pat = aucmd_next_pattern(pat, patlen); patlen = aucmd_pattern_length(pat); } - } + }) } else { api_set_error(err, kErrorTypeValidation, - "'pattern' must be a string"); + "'pattern' must be a string or table"); return false; } } else if (buffer.type != kObjectTypeNil) { - if (buffer.type != kObjectTypeInteger) { + if (buffer.type != kObjectTypeInteger && buffer.type != kObjectTypeBuffer) { api_set_error(err, kErrorTypeValidation, "'buffer' must be an integer"); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index be2df9488e..7b6e954b3a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -469,7 +469,9 @@ void eval_clear(void) hash_clear(&compat_hashtab); free_scriptnames(); +# ifdef HAVE_WORKING_LIBINTL free_locales(); +# endif // global variables vars_clear(&globvarht); diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 5b0ce1418e..92c1b5d2c7 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1568,14 +1568,18 @@ char *make_filter_cmd(char *cmd, char *itmp, char *otmp) #else false; #endif + bool is_pwsh = STRNCMP(invocation_path_tail(p_sh, NULL), "pwsh", 4) == 0 + || STRNCMP(invocation_path_tail(p_sh, NULL), "powershell", 10) == 0; size_t len = STRLEN(cmd) + 1; // At least enough space for cmd + NULL. len += is_fish_shell ? sizeof("begin; " "; end") - 1 - : sizeof("(" ")") - 1; + : is_pwsh ? sizeof("Start-Process ") + : sizeof("(" ")") - 1; if (itmp != NULL) { - len += STRLEN(itmp) + sizeof(" { " " < " " } ") - 1; + len += is_pwsh ? STRLEN(itmp) + sizeof(" -RedirectStandardInput ") + : STRLEN(itmp) + sizeof(" { " " < " " } ") - 1; } if (otmp != NULL) { len += STRLEN(otmp) + STRLEN(p_srr) + 2; // two extra spaces (" "), @@ -1585,7 +1589,10 @@ char *make_filter_cmd(char *cmd, char *itmp, char *otmp) #if defined(UNIX) // Put delimiters around the command (for concatenated commands) when // redirecting input and/or output. - if (itmp != NULL || otmp != NULL) { + if (is_pwsh) { + xstrlcpy(buf, "Start-Process ", len); + xstrlcat(buf, cmd, len); + } else if (itmp != NULL || otmp != NULL) { char *fmt = is_fish_shell ? "begin; %s; end" : "(%s)"; vim_snprintf(buf, len, fmt, cmd); @@ -1594,13 +1601,22 @@ char *make_filter_cmd(char *cmd, char *itmp, char *otmp) } if (itmp != NULL) { - xstrlcat(buf, " < ", len - 1); - xstrlcat(buf, (const char *)itmp, len - 1); + if (is_pwsh) { + xstrlcat(buf, " -RedirectStandardInput ", len - 1); + } else { + xstrlcat(buf, " < ", len - 1); + } + xstrlcat(buf, itmp, len - 1); } #else // For shells that don't understand braces around commands, at least allow // the use of commands in a pipe. - xstrlcpy(buf, (char *)cmd, len); + if (is_pwsh) { + xstrlcpy(buf, "Start-Process ", len); + xstrlcat(buf, cmd, len); + } else { + xstrlcpy(buf, cmd, len); + } if (itmp != NULL) { // If there is a pipe, we have to put the '<' in front of it. // Don't do this when 'shellquote' is not empty, otherwise the @@ -1611,10 +1627,14 @@ char *make_filter_cmd(char *cmd, char *itmp, char *otmp) *p = NUL; } } - xstrlcat(buf, " < ", len); - xstrlcat(buf, (const char *)itmp, len); + if (is_pwsh) { + xstrlcat(buf, " -RedirectStandardInput ", len); + } else { + xstrlcat(buf, " < ", len); + } + xstrlcat(buf, itmp, len); if (*p_shq == NUL) { - const char *const p = find_pipe((const char *)cmd); + const char *const p = find_pipe(cmd); if (p != NULL) { xstrlcat(buf, " ", len - 1); // Insert a space before the '|' for DOS xstrlcat(buf, p, len - 1); diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 671e83def6..00aadc2af5 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -82,8 +82,13 @@ #include "nvim/vim.h" #include "nvim/window.h" -static char *e_no_such_user_defined_command_str = N_("E184: No such user-defined command: %s"); -static char *e_no_such_user_defined_command_in_current_buffer_str +static char e_no_such_user_defined_command_str[] + = N_("E184: No such user-defined command: %s"); +static char e_ambiguous_use_of_user_defined_command[] + = N_("E464: Ambiguous use of user-defined command"); +static char e_not_an_editor_command[] + = N_("E492: Not an editor command"); +static char e_no_such_user_defined_command_in_current_buffer_str[] = N_("E1237: No such user-defined command in current buffer: %s"); static int quitmore = 0; @@ -1439,6 +1444,10 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er eap->cmd = skipwhite(eap->cmd + 1); } p = find_ex_command(eap, NULL); + if (p == NULL) { + *errormsg = _(e_ambiguous_use_of_user_defined_command); + return false; + } // Set command address type and parse command range set_cmd_addr_type(eap, (char_u *)p); @@ -1455,7 +1464,7 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er } // Fail if command is invalid if (eap->cmdidx == CMD_SIZE) { - STRCPY(IObuff, _("E492: Not an editor command")); + STRCPY(IObuff, _(e_not_an_editor_command)); // If the modifier was parsed OK the error must be in the following command char *cmdname = after_modifier ? after_modifier : cmdline; append_command(cmdname); @@ -1886,14 +1895,14 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter if (p == NULL) { if (!ea.skip) { - errormsg = _("E464: Ambiguous use of user-defined command"); + errormsg = _(e_ambiguous_use_of_user_defined_command); } goto doend; } // Check for wrong commands. if (ea.cmdidx == CMD_SIZE) { if (!ea.skip) { - STRCPY(IObuff, _("E492: Not an editor command")); + STRCPY(IObuff, _(e_not_an_editor_command)); // If the modifier was parsed OK the error must be in the following // command char *cmdname = after_modifier ? after_modifier : *cmdlinep; diff --git a/src/nvim/option.c b/src/nvim/option.c index 633fbe0517..8cd9b5aead 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2596,6 +2596,8 @@ ambw_end: // only encoding=utf-8 allowed if (STRCMP(p_enc, "utf-8") != 0) { errmsg = e_unsupportedoption; + } else { + spell_reload(); } } } diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 8f84204481..8ae846e074 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -3663,6 +3663,12 @@ static void suggest_try_change(suginfo_T *su) p = su->su_badptr + su->su_badlen; (void)spell_casefold(curwin, p, (int)STRLEN(p), fword + n, MAXWLEN - n); + // Make sure the resulting text is not longer than the original text. + n = (int)STRLEN(su->su_badptr); + if (n < MAXWLEN) { + fword[n] = NUL; + } + for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) { lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi); @@ -4375,7 +4381,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so #endif ++depth; sp = &stack[depth]; - ++sp->ts_fidx; + if (fword[sp->ts_fidx] != NUL) { + sp->ts_fidx++; + } tword[sp->ts_twordlen++] = c; sp->ts_arridx = idxs[arridx]; if (newscore == SCORE_SUBST) { @@ -4391,7 +4399,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so sp->ts_fcharstart = sp->ts_fidx - 1; sp->ts_isdiff = (newscore != 0) ? DIFF_YES : DIFF_NONE; - } else if (sp->ts_isdiff == DIFF_INSERT) { + } else if (sp->ts_isdiff == DIFF_INSERT && sp->ts_fidx > 0) { // When inserting trail bytes don't advance in the // bad word. sp->ts_fidx--; diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 887c8e1593..8d1746be2f 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -305,7 +305,7 @@ func Test_getcompletion() call assert_equal([], l) let l = getcompletion('', 'dir') - call assert_true(index(l, expand('sautest/')) >= 0) + call assert_true(index(l, 'sautest/') >= 0) let l = getcompletion('NoMatch', 'dir') call assert_equal([], l) @@ -415,7 +415,7 @@ func Test_getcompletion() " Command line completion tests let l = getcompletion('cd ', 'cmdline') - call assert_true(index(l, expand('sautest/')) >= 0) + call assert_true(index(l, 'sautest/') >= 0) let l = getcompletion('cd NoMatch', 'cmdline') call assert_equal([], l) let l = getcompletion('let v:n', 'cmdline') @@ -539,7 +539,7 @@ func Test_expand_star_star() call mkdir('a/b', 'p') call writefile(['asdfasdf'], 'a/b/fileXname') call feedkeys(":find **/fileXname\<Tab>\<CR>", 'xt') - call assert_equal('find '.expand('a/b/fileXname'), getreg(':')) + call assert_equal('find a/b/fileXname', getreg(':')) bwipe! call delete('a', 'rf') endfunc diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index e43db4d692..5826666cbb 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -1060,17 +1060,17 @@ func s:dir_stack_tests(cchar) let qf = g:Xgetlist() - call assert_equal(expand('dir1/a/habits2.txt'), bufname(qf[1].bufnr)) + call assert_equal('dir1/a/habits2.txt', bufname(qf[1].bufnr)) call assert_equal(1, qf[1].lnum) - call assert_equal(expand('dir1/a/b/habits3.txt'), bufname(qf[3].bufnr)) + call assert_equal('dir1/a/b/habits3.txt', bufname(qf[3].bufnr)) call assert_equal(2, qf[3].lnum) - call assert_equal(expand('dir1/a/habits2.txt'), bufname(qf[4].bufnr)) + call assert_equal('dir1/a/habits2.txt', bufname(qf[4].bufnr)) call assert_equal(7, qf[4].lnum) - call assert_equal(expand('dir1/c/habits4.txt'), bufname(qf[6].bufnr)) + call assert_equal('dir1/c/habits4.txt', bufname(qf[6].bufnr)) call assert_equal(3, qf[6].lnum) call assert_equal('habits1.txt', bufname(qf[9].bufnr)) call assert_equal(4, qf[9].lnum) - call assert_equal(expand('dir2/habits5.txt'), bufname(qf[11].bufnr)) + call assert_equal('dir2/habits5.txt', bufname(qf[11].bufnr)) call assert_equal(5, qf[11].lnum) let &efm=save_efm @@ -1300,7 +1300,7 @@ func Test_efm2() call assert_equal(8, len(l)) call assert_equal(89, l[4].lnum) call assert_equal(1, l[4].valid) - call assert_equal(expand('unittests/dbfacadeTest.py'), bufname(l[4].bufnr)) + call assert_equal('unittests/dbfacadeTest.py', bufname(l[4].bufnr)) call assert_equal('W', l[4].type) " Test for %o @@ -2074,11 +2074,11 @@ func Test_two_windows() laddexpr 'one.txt:3:one one one' let loc_one = getloclist(one_id) - call assert_equal(expand('Xone/a/one.txt'), bufname(loc_one[1].bufnr)) + call assert_equal('Xone/a/one.txt', bufname(loc_one[1].bufnr)) call assert_equal(3, loc_one[1].lnum) let loc_two = getloclist(two_id) - call assert_equal(expand('Xtwo/a/two.txt'), bufname(loc_two[1].bufnr)) + call assert_equal('Xtwo/a/two.txt', bufname(loc_two[1].bufnr)) call assert_equal(5, loc_two[1].lnum) call win_gotoid(one_id) diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim index ce21b8bdc9..215d4387d6 100644 --- a/src/nvim/testdir/test_spell.vim +++ b/src/nvim/testdir/test_spell.vim @@ -72,6 +72,16 @@ func Test_z_equal_on_invalid_utf8_word() bwipe! endfunc +func Test_z_equal_on_single_character() + " this was decrementing the index below zero + new + norm a0\ + norm zW + norm z= + + bwipe! +endfunc + " Test spellbadword() with argument func Test_spellbadword() set spell diff --git a/src/nvim/testdir/test_spell_utf8.vim b/src/nvim/testdir/test_spell_utf8.vim index 1d323df67e..3c07e0782b 100644 --- a/src/nvim/testdir/test_spell_utf8.vim +++ b/src/nvim/testdir/test_spell_utf8.vim @@ -576,7 +576,6 @@ endfunc "Compound words func Test_spell_compound() - throw 'skipped: TODO: ' call LoadAffAndDic(g:test_data_aff3, g:test_data_dic3) call RunGoodBad("foo m\u00EF foobar foofoobar barfoo barbarfoo", \ "bad: bar la foom\u00EF barm\u00EF m\u00EFfoo m\u00EFbar m\u00EFm\u00EF lala m\u00EFla lam\u00EF foola labar", @@ -624,14 +623,14 @@ endfunc " Test affix flags with two characters func Test_spell_affix() - throw 'skipped: TODO: ' + CheckNotMSWindows " FIXME: Why does this fail with MSVC? call LoadAffAndDic(g:test_data_aff5, g:test_data_dic5) call RunGoodBad("fooa1 fooa\u00E9 bar prebar barbork prebarbork startprebar start end startend startmiddleend nouend", \ "bad: foo fooa2 prabar probarbirk middle startmiddle middleend endstart startprobar startnouend", \ ["bar", "barbork", "end", "fooa1", "fooa\u00E9", "nouend", "prebar", "prebarbork", "start"], \ [ \ ["bad", ["bar", "end", "fooa1"]], - \ ["foo", ["fooa1", "fooa\u00E9", "bar"]], + \ ["foo", ["fooa1", "bar", "end"]], \ ["fooa2", ["fooa1", "fooa\u00E9", "bar"]], \ ["prabar", ["prebar", "bar", "bar bar"]], \ ["probarbirk", ["prebarbork"]], @@ -649,7 +648,7 @@ func Test_spell_affix() \ ["bar", "barbork", "end", "lead", "meea1", "meea\u00E9", "prebar", "prebarbork"], \ [ \ ["bad", ["bar", "end", "lead"]], - \ ["mee", ["meea1", "meea\u00E9", "bar"]], + \ ["mee", ["meea1", "bar", "end"]], \ ["meea2", ["meea1", "meea\u00E9", "lead"]], \ ["prabar", ["prebar", "bar", "leadbar"]], \ ["probarbirk", ["prebarbork"]], @@ -666,7 +665,7 @@ func Test_spell_affix() \ ["bar", "barmeat", "lead", "meea1", "meea\u00E9", "meezero", "prebar", "prebarmeat", "tail"], \ [ \ ["bad", ["bar", "lead", "tail"]], - \ ["mee", ["meea1", "meea\u00E9", "bar"]], + \ ["mee", ["meea1", "bar", "lead"]], \ ["meea2", ["meea1", "meea\u00E9", "lead"]], \ ["prabar", ["prebar", "bar", "leadbar"]], \ ["probarmaat", ["prebarmeat"]], @@ -700,7 +699,6 @@ endfunc " Affix flags func Test_spell_affix_flags() - throw 'skipped: TODO: ' call LoadAffAndDic(g:test_data_aff10, g:test_data_dic10) call RunGoodBad("drink drinkable drinkables drinktable drinkabletable", \ "bad: drinks drinkstable drinkablestable", @@ -761,11 +759,30 @@ func Test_spell_sal_and_addition() set spl=Xtest_ca.utf-8.spl call assert_equal("elequint", FirstSpellWord()) call assert_equal("elekwint", SecondSpellWord()) + + set spellfile= + set spl& endfunc func Test_spellfile_value() set spellfile=Xdir/Xtest.utf-8.add set spellfile=Xdir/Xtest.utf-8.add,Xtest_other.add + set spellfile= +endfunc + +func Test_no_crash_with_weird_text() + new + let lines =<< trim END + r<sfile> + + + + + END + call setline(1, lines) + exe "%norm \<C-v>ez=>\<C-v>wzG" + + bwipe! endfunc " Invalid bytes may cause trouble when creating the word list. @@ -773,5 +790,18 @@ func Test_check_for_valid_word() call assert_fails("spellgood! 0\xac", 'E1280:') endfunc +" This was going over the end of the word +func Test_word_index() + new + norm R0 + spellgood! fl0 + sil norm z= + + bwipe! + " clear the word list + set enc=utf-8 + call delete('Xtmpfile') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/functional/api/autocmd_spec.lua b/test/functional/api/autocmd_spec.lua index 5dfcd09438..a923f5df0e 100644 --- a/test/functional/api/autocmd_spec.lua +++ b/test/functional/api/autocmd_spec.lua @@ -686,6 +686,26 @@ describe('autocmd api', function() eq(true, meths.get_var("autocmd_executed")) end) + it("can trigger multiple patterns", function() + meths.set_var("autocmd_executed", 0) + + meths.create_autocmd("BufReadPost", { + pattern = "*", + command = "let g:autocmd_executed += 1", + }) + + meths.exec_autocmds("BufReadPost", { pattern = { "*.lua", "*.vim" } }) + eq(2, meths.get_var("autocmd_executed")) + + meths.create_autocmd("BufReadPre", { + pattern = { "bar", "foo" }, + command = "let g:autocmd_executed += 10", + }) + + meths.exec_autocmds("BufReadPre", { pattern = { "foo", "bar", "baz", "frederick" }}) + eq(22, meths.get_var("autocmd_executed")) + end) + it("can pass the buffer", function() meths.set_var("buffer_executed", -1) eq(-1, meths.get_var("buffer_executed")) @@ -742,7 +762,7 @@ describe('autocmd api', function() meths.exec_autocmds("CursorHoldI", { buffer = 1 }) eq('none', meths.get_var("filename_executed")) - meths.exec_autocmds("CursorHoldI", { buffer = tonumber(meths.get_current_buf()) }) + meths.exec_autocmds("CursorHoldI", { buffer = meths.get_current_buf() }) eq('__init__.py', meths.get_var("filename_executed")) -- Reset filename diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index ef6798dea3..c05345dd4c 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -3520,6 +3520,9 @@ describe('API', function() pcall_err(meths.parse_cmd, 'Fubar!', {})) eq('Error while parsing command line: E481: No range allowed', pcall_err(meths.parse_cmd, '4,6Fubar', {})) + command('command! Foobar echo foo') + eq('Error while parsing command line: E464: Ambiguous use of user-defined command', + pcall_err(meths.parse_cmd, 'F', {})) end) end) describe('nvim_cmd', function() diff --git a/test/functional/autocmd/termxx_spec.lua b/test/functional/autocmd/termxx_spec.lua index 1e8f981437..c418a12faf 100644 --- a/test/functional/autocmd/termxx_spec.lua +++ b/test/functional/autocmd/termxx_spec.lua @@ -1,8 +1,8 @@ local luv = require('luv') local helpers = require('test.functional.helpers')(after_each) -local clear, command, nvim, nvim_dir = - helpers.clear, helpers.command, helpers.nvim, helpers.nvim_dir +local clear, command, nvim, testprg = + helpers.clear, helpers.command, helpers.nvim, helpers.testprg local eval, eq, neq, retry = helpers.eval, helpers.eq, helpers.neq, helpers.retry local ok = helpers.ok @@ -12,7 +12,7 @@ local iswin = helpers.iswin describe('autocmd TermClose', function() before_each(function() clear() - nvim('set_option', 'shell', nvim_dir .. '/shell-test') + nvim('set_option', 'shell', testprg('shell-test')) command('set shellcmdflag=EXE shellredir= shellpipe= shellquote= shellxquote=') end) diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 461a69f357..a6763ba3c7 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -1,9 +1,9 @@ local helpers = require('test.functional.helpers')(after_each) local clear, eq, eval, exc_exec, feed_command, feed, insert, neq, next_msg, nvim, - nvim_dir, ok, source, write_file, mkdir, rmdir = helpers.clear, + testprg, ok, source, write_file, mkdir, rmdir = helpers.clear, helpers.eq, helpers.eval, helpers.exc_exec, helpers.feed_command, helpers.feed, helpers.insert, helpers.neq, helpers.next_msg, helpers.nvim, - helpers.nvim_dir, helpers.ok, helpers.source, + helpers.testprg, helpers.ok, helpers.source, helpers.write_file, helpers.mkdir, helpers.rmdir local assert_alive = helpers.assert_alive local command = helpers.command @@ -1043,8 +1043,7 @@ describe('jobs', function() return a:data endfunction ]]) - local ext = iswin() and '.exe' or '' - insert(nvim_dir..'/tty-test'..ext) -- Full path to tty-test. + insert(testprg('tty-test')) nvim('command', 'let g:job_opts.pty = 1') nvim('command', 'let exec = [expand("<cfile>:p")]') nvim('command', "let j = jobstart(exec, g:job_opts)") diff --git a/test/functional/ex_cmds/ls_spec.lua b/test/functional/ex_cmds/ls_spec.lua index 9853084c47..2583d80269 100644 --- a/test/functional/ex_cmds/ls_spec.lua +++ b/test/functional/ex_cmds/ls_spec.lua @@ -5,7 +5,7 @@ local eq = helpers.eq local eval = helpers.eval local feed = helpers.feed local nvim = helpers.nvim -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg local retry = helpers.retry describe(':ls', function() @@ -14,7 +14,7 @@ describe(':ls', function() end) it('R, F for :terminal buffers', function() - nvim('set_option', 'shell', string.format('"%s" INTERACT', nvim_dir..'/shell-test')) + nvim('set_option', 'shell', string.format('"%s" INTERACT', testprg('shell-test'))) command('edit foo') command('set hidden') diff --git a/test/functional/ex_cmds/make_spec.lua b/test/functional/ex_cmds/make_spec.lua index 3b4d22ab38..bf585ee44c 100644 --- a/test/functional/ex_cmds/make_spec.lua +++ b/test/functional/ex_cmds/make_spec.lua @@ -4,7 +4,7 @@ local eval = helpers.eval local has_powershell = helpers.has_powershell local matches = helpers.matches local nvim = helpers.nvim -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg describe(':make', function() clear() @@ -22,7 +22,7 @@ describe(':make', function() end) it('captures stderr & non zero exit code #14349', function () - nvim('set_option', 'makeprg', nvim_dir..'/shell-test foo') + nvim('set_option', 'makeprg', testprg('shell-test')..' foo') local out = eval('execute("make")') -- Make program exit code correctly captured matches('\nshell returned 3', out) @@ -31,7 +31,7 @@ describe(':make', function() end) it('captures stderr & zero exit code #14349', function () - nvim('set_option', 'makeprg', nvim_dir..'/shell-test') + nvim('set_option', 'makeprg', testprg('shell-test')) local out = eval('execute("make")') -- Ensure there are no "shell returned X" messages between -- command and last line (indicating zero exit) diff --git a/test/functional/fixtures/CMakeLists.txt b/test/functional/fixtures/CMakeLists.txt index 6010fcaf1e..a5410c2f8c 100644 --- a/test/functional/fixtures/CMakeLists.txt +++ b/test/functional/fixtures/CMakeLists.txt @@ -2,6 +2,8 @@ add_executable(tty-test EXCLUDE_FROM_ALL tty-test.c) target_link_libraries(tty-test ${LIBUV_LIBRARIES}) add_executable(shell-test EXCLUDE_FROM_ALL shell-test.c) +# Fake pwsh (powershell) for testing make_filter_cmd(). #16271 +add_executable(pwsh-test EXCLUDE_FROM_ALL shell-test.c) add_executable(printargs-test EXCLUDE_FROM_ALL printargs-test.c) add_executable(printenv-test EXCLUDE_FROM_ALL printenv-test.c) if(MINGW) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index d31d337b63..0c616e73fb 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -515,9 +515,17 @@ function module.has_powershell() return module.eval('executable("'..(iswin() and 'powershell' or 'pwsh')..'")') == 1 end -function module.set_shell_powershell() - local shell = iswin() and 'powershell' or 'pwsh' - assert(module.has_powershell()) +--- Sets Nvim shell to powershell. +--- +--- @param fake (boolean) If true, a fake will be used if powershell is not +--- found on the system. +--- @returns true if powershell was found on the system, else false. +function module.set_shell_powershell(fake) + local found = module.has_powershell() + if not fake then + assert(found) + end + local shell = found and (iswin() and 'powershell' or 'pwsh') or module.testprg('pwsh-test') local set_encoding = '[Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;' local cmd = set_encoding..'Remove-Item -Force '..table.concat(iswin() and {'alias:cat', 'alias:echo', 'alias:sleep'} @@ -525,10 +533,11 @@ function module.set_shell_powershell() module.exec([[ let &shell = ']]..shell..[[' set shellquote= shellxquote= - let &shellpipe = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode' - let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode' let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command ]]..cmd..[[' + let &shellpipe = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode' + let &shellredir = '-RedirectStandardOutput %s -NoNewWindow -Wait' ]]) + return found end function module.nvim(method, ...) @@ -784,11 +793,21 @@ function module.get_pathsep() return iswin() and '\\' or '/' end +--- Gets the filesystem root dir, namely "/" or "C:/". function module.pathroot() local pathsep = package.config:sub(1,1) return iswin() and (module.nvim_dir:sub(1,2)..pathsep) or '/' end +--- Gets the full `…/build/bin/{name}` path of a test program produced by +--- `test/functional/fixtures/CMakeLists.txt`. +--- +--- @param name (string) Name of the test program. +function module.testprg(name) + local ext = module.iswin() and '.exe' or '' + return ('%s/%s%s'):format(module.nvim_dir, name, ext) +end + -- Returns a valid, platform-independent Nvim listen address. -- Useful for communicating with child instances. function module.new_pipename() diff --git a/test/functional/lua/filetype_spec.lua b/test/functional/lua/filetype_spec.lua index d0cef53c4b..be57b2db31 100644 --- a/test/functional/lua/filetype_spec.lua +++ b/test/functional/lua/filetype_spec.lua @@ -3,6 +3,7 @@ local exec_lua = helpers.exec_lua local eq = helpers.eq local clear = helpers.clear local pathroot = helpers.pathroot +local command = helpers.command local root = pathroot() @@ -23,7 +24,7 @@ describe('vim.filetype', function() rs = 'radicalscript', }, }) - return vim.filetype.match('main.rs') + return vim.filetype.match({ filename = 'main.rs' }) ]]) end) @@ -37,7 +38,7 @@ describe('vim.filetype', function() ['main.rs'] = 'somethingelse', }, }) - return vim.filetype.match('main.rs') + return vim.filetype.match({ filename = 'main.rs' }) ]]) end) @@ -48,7 +49,7 @@ describe('vim.filetype', function() ['s_O_m_e_F_i_l_e'] = 'nim', }, }) - return vim.filetype.match('s_O_m_e_F_i_l_e') + return vim.filetype.match({ filename = 's_O_m_e_F_i_l_e' }) ]]) eq('dosini', exec_lua([[ @@ -59,7 +60,7 @@ describe('vim.filetype', function() [root .. '/.config/fun/config'] = 'dosini', }, }) - return vim.filetype.match(root .. '/.config/fun/config') + return vim.filetype.match({ filename = root .. '/.config/fun/config' }) ]], root)) end) @@ -72,11 +73,13 @@ describe('vim.filetype', function() ['~/blog/.*%.txt'] = 'markdown', } }) - return vim.filetype.match('~/blog/why_neovim_is_awesome.txt') + return vim.filetype.match({ filename = '~/blog/why_neovim_is_awesome.txt' }) ]], root)) end) it('works with functions', function() + command('new') + command('file relevant_to_me') eq('foss', exec_lua [[ vim.filetype.add({ pattern = { @@ -87,7 +90,7 @@ describe('vim.filetype', function() end, } }) - return vim.filetype.match('relevant_to_me') + return vim.filetype.match({ buf = 0 }) ]]) end) end) diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua index 6e06304acd..2d1c790d2f 100644 --- a/test/functional/terminal/cursor_spec.lua +++ b/test/functional/terminal/cursor_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local thelpers = require('test.functional.terminal.helpers') local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim -local nvim_dir, command = helpers.nvim_dir, helpers.command +local testprg, command = helpers.testprg, helpers.command local nvim_prog = helpers.nvim_prog local eq, eval = helpers.eq, helpers.eval local matches = helpers.matches @@ -150,7 +150,7 @@ describe('cursor with customized highlighting', function() [3] = {bold = true}, }) screen:attach({rgb=false}) - command('call termopen(["'..nvim_dir..'/tty-test"])') + command('call termopen(["'..testprg('tty-test')..'"])') feed_command('startinsert') end) diff --git a/test/functional/terminal/edit_spec.lua b/test/functional/terminal/edit_spec.lua index e7025d6739..aeb4b7cc2e 100644 --- a/test/functional/terminal/edit_spec.lua +++ b/test/functional/terminal/edit_spec.lua @@ -3,7 +3,7 @@ local screen = require('test.functional.ui.screen') local curbufmeths = helpers.curbufmeths local curwinmeths = helpers.curwinmeths -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg local command = helpers.command local funcs = helpers.funcs local meths = helpers.meths @@ -21,7 +21,7 @@ describe(':edit term://*', function() before_each(function() clear() - meths.set_option('shell', nvim_dir .. '/shell-test') + meths.set_option('shell', testprg('shell-test')) meths.set_option('shellcmdflag', 'EXE') end) diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index b4f29a586a..23b69319f0 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local assert_alive = helpers.assert_alive local clear, poke_eventloop, nvim = helpers.clear, helpers.poke_eventloop, helpers.nvim -local nvim_dir, source, eq = helpers.nvim_dir, helpers.source, helpers.eq +local testprg, source, eq = helpers.testprg, helpers.source, helpers.eq local feed = helpers.feed local feed_command, eval = helpers.feed_command, helpers.eval local funcs = helpers.funcs @@ -28,7 +28,7 @@ describe(':terminal', function() echomsg "msg3" ]]) -- Invoke a command that emits frequent terminal activity. - feed([[:terminal "]]..nvim_dir..[[/shell-test" REP 9999 !terminal_output!<cr>]]) + feed([[:terminal "]]..testprg('shell-test')..[[" REP 9999 !terminal_output!<cr>]]) feed([[<C-\><C-N>]]) poke_eventloop() -- Wait for some terminal activity. @@ -131,7 +131,7 @@ describe(':terminal (with fake shell)', function() screen = Screen.new(50, 4) screen:attach({rgb=false}) -- shell-test.c is a fake shell that prints its arguments and exits. - nvim('set_option', 'shell', nvim_dir..'/shell-test') + nvim('set_option', 'shell', testprg('shell-test')) nvim('set_option', 'shellcmdflag', 'EXE') end) @@ -167,7 +167,7 @@ describe(':terminal (with fake shell)', function() it("with no argument, but 'shell' has arguments, acts like termopen()", function() if helpers.pending_win32(pending) then return end - nvim('set_option', 'shell', nvim_dir..'/shell-test -t jeff') + nvim('set_option', 'shell', testprg('shell-test')..' -t jeff') terminal_with_fake_shell() screen:expect([[ ^jeff $ | @@ -191,7 +191,7 @@ describe(':terminal (with fake shell)', function() it("executes a given command through the shell, when 'shell' has arguments", function() if helpers.pending_win32(pending) then return end - nvim('set_option', 'shell', nvim_dir..'/shell-test -t jeff') + nvim('set_option', 'shell', testprg('shell-test')..' -t jeff') command('set shellxquote=') -- win: avoid extra quotes terminal_with_fake_shell('echo hi') screen:expect([[ diff --git a/test/functional/terminal/helpers.lua b/test/functional/terminal/helpers.lua index 51ecae663a..bcfd3559e6 100644 --- a/test/functional/terminal/helpers.lua +++ b/test/functional/terminal/helpers.lua @@ -3,7 +3,7 @@ -- operate on the _host_ session, _not_ the child session. local helpers = require('test.functional.helpers')(nil) local Screen = require('test.functional.ui.screen') -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg local feed_command, nvim = helpers.feed_command, helpers.nvim local function feed_data(data) @@ -37,7 +37,7 @@ local function clear_attrs() feed_termcode('[0;10m') end local function enable_mouse() feed_termcode('[?1002h') end local function disable_mouse() feed_termcode('[?1002l') end -local default_command = '["'..nvim_dir..'/tty-test'..'"]' +local default_command = '["'..testprg('tty-test')..'"]' local function screen_setup(extra_rows, command, cols, opts) extra_rows = extra_rows and extra_rows or 0 diff --git a/test/functional/terminal/highlight_spec.lua b/test/functional/terminal/highlight_spec.lua index 2a63971d48..1eb7223dce 100644 --- a/test/functional/terminal/highlight_spec.lua +++ b/test/functional/terminal/highlight_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local thelpers = require('test.functional.terminal.helpers') local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim -local nvim_dir, command = helpers.nvim_dir, helpers.command +local testprg, command = helpers.testprg, helpers.command local nvim_prog_abs = helpers.nvim_prog_abs local eq, eval = helpers.eq, helpers.eval local funcs = helpers.funcs @@ -28,7 +28,7 @@ describe(':terminal highlight', function() [11] = {background = 11}, }) screen:attach({rgb=false}) - command('enew | call termopen(["'..nvim_dir..'/tty-test"])') + command(("enew | call termopen(['%s'])"):format(testprg('tty-test'))) feed('i') screen:expect([[ tty ready | @@ -173,7 +173,7 @@ describe(':terminal highlight forwarding', function() [4] = {{foreground = tonumber('0xff8000')}, {}}, }) screen:attach() - command('enew | call termopen(["'..nvim_dir..'/tty-test"])') + command(("enew | call termopen(['%s'])"):format(testprg('tty-test'))) feed('i') screen:expect([[ tty ready | @@ -225,7 +225,7 @@ describe(':terminal highlight with custom palette', function() }) screen:attach({rgb=true}) nvim('set_var', 'terminal_color_3', '#123456') - command('enew | call termopen(["'..nvim_dir..'/tty-test"])') + command(("enew | call termopen(['%s'])"):format(testprg('tty-test'))) feed('i') screen:expect([[ tty ready | diff --git a/test/functional/terminal/scrollback_spec.lua b/test/functional/terminal/scrollback_spec.lua index b1d3b502b2..b491cb2735 100644 --- a/test/functional/terminal/scrollback_spec.lua +++ b/test/functional/terminal/scrollback_spec.lua @@ -2,7 +2,7 @@ local Screen = require('test.functional.ui.screen') local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') local clear, eq, curbuf = helpers.clear, helpers.eq, helpers.curbuf -local feed, nvim_dir, feed_command = helpers.feed, helpers.nvim_dir, helpers.feed_command +local feed, testprg, feed_command = helpers.feed, helpers.testprg, helpers.feed_command local iswin = helpers.iswin local eval = helpers.eval local command = helpers.command @@ -350,7 +350,7 @@ describe(':terminal prints more lines than the screen height and exits', functio clear() local screen = Screen.new(30, 7) screen:attach({rgb=false}) - feed_command('call termopen(["'..nvim_dir..'/tty-test", "10"]) | startinsert') + feed_command(("call termopen(['%s', '10']) | startinsert"):format(testprg('tty-test'))) poke_eventloop() screen:expect([[ line6 | @@ -382,7 +382,7 @@ describe("'scrollback' option", function() local function set_fake_shell() -- shell-test.c is a fake shell that prints its arguments and exits. - nvim('set_option', 'shell', nvim_dir..'/shell-test') + nvim('set_option', 'shell', testprg('shell-test')) nvim('set_option', 'shellcmdflag', 'EXE') end @@ -403,7 +403,7 @@ describe("'scrollback' option", function() end curbufmeths.set_option('scrollback', 0) - feed_data(nvim_dir..'/shell-test REP 31 line'..(iswin() and '\r' or '\n')) + feed_data(('%s REP 31 line%s'):format(testprg('shell-test'), iswin() and '\r' or '\n')) screen:expect{any='30: line '} retry(nil, nil, function() expect_lines(7) end) end) @@ -423,7 +423,7 @@ describe("'scrollback' option", function() -- Wait for prompt. screen:expect{any='%$'} - feed_data(nvim_dir.."/shell-test REP 31 line"..(iswin() and '\r' or '\n')) + feed_data(('%s REP 31 line%s'):format(testprg('shell-test'), iswin() and '\r' or '\n')) screen:expect{any='30: line '} retry(nil, nil, function() expect_lines(33, 2) end) @@ -436,7 +436,7 @@ describe("'scrollback' option", function() -- 'scrollback' option is synchronized with the internal sb_buffer. command('sleep 100m') - feed_data(nvim_dir.."/shell-test REP 41 line"..(iswin() and '\r' or '\n')) + feed_data(('%s REP 41 line%s'):format(testprg('shell-test'), iswin() and '\r' or '\n')) if iswin() then screen:expect{grid=[[ 37: line | diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 8c6cba4def..89704be820 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -14,7 +14,7 @@ local feed_command = helpers.feed_command local feed_data = thelpers.feed_data local clear = helpers.clear local command = helpers.command -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg local retry = helpers.retry local nvim_prog = helpers.nvim_prog local nvim_set = helpers.nvim_set @@ -385,7 +385,7 @@ describe('TUI', function() return end feed_data(':set statusline=^^^^^^^\n') - feed_data(':terminal '..nvim_dir..'/tty-test\n') + feed_data(':terminal '..testprg('tty-test')..'\n') feed_data('i') screen:expect{grid=[[ tty ready | @@ -903,7 +903,7 @@ describe('TUI', function() feed_data(':set statusline=^^^^^^^\n') feed_data(':set termguicolors\n') - feed_data(':terminal '..nvim_dir..'/tty-test\n') + feed_data(':terminal '..testprg('tty-test')..'\n') -- Depending on platform the above might or might not fit in the cmdline -- so clear it for consistent behavior. feed_data(':\027') @@ -1130,7 +1130,7 @@ describe('TUI FocusGained/FocusLost', function() end) it('in terminal-mode', function() - feed_data(':set shell='..nvim_dir..'/shell-test\n') + feed_data(':set shell='..testprg('shell-test')..'\n') feed_data(':set noshowmode laststatus=0\n') feed_data(':terminal\n') diff --git a/test/functional/ui/hlstate_spec.lua b/test/functional/ui/hlstate_spec.lua index 925af11627..df7f34aa7f 100644 --- a/test/functional/ui/hlstate_spec.lua +++ b/test/functional/ui/hlstate_spec.lua @@ -5,7 +5,7 @@ local clear, insert = helpers.clear, helpers.insert local command = helpers.command local meths = helpers.meths local iswin = helpers.iswin -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg local thelpers = require('test.functional.terminal.helpers') describe('ext_hlstate detailed highlights', function() @@ -191,7 +191,7 @@ describe('ext_hlstate detailed highlights', function() [6] = {{foreground = tonumber('0x40ffff'), fg_indexed=true}, {5, 1}}, [7] = {{}, {{hi_name = "MsgArea", ui_name = "MsgArea", kind = "ui"}}}, }) - command('enew | call termopen(["'..nvim_dir..'/tty-test"])') + command(("enew | call termopen(['%s'])"):format(testprg('tty-test'))) screen:expect([[ ^tty ready | {1: } | diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index 98ed564966..7a42e72732 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -17,7 +17,7 @@ local source = helpers.source local poke_eventloop = helpers.poke_eventloop local nvim = helpers.nvim local sleep = helpers.sleep -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg local assert_alive = helpers.assert_alive local default_text = [[ @@ -2875,7 +2875,7 @@ it(':substitute with inccommand during :terminal activity', function() clear() command("set cmdwinheight=3") - feed([[:terminal "]]..nvim_dir..[[/shell-test" REP 5000 xxx<cr>]]) + feed(([[:terminal "%s" REP 5000 xxx<cr>]]):format(testprg('shell-test'))) command('file term') feed('G') -- Follow :terminal output. command('new') diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua index 50e5dfac84..71c6410013 100644 --- a/test/functional/ui/output_spec.lua +++ b/test/functional/ui/output_spec.lua @@ -9,6 +9,7 @@ local feed_command = helpers.feed_command local iswin = helpers.iswin local clear = helpers.clear local command = helpers.command +local testprg = helpers.testprg local nvim_dir = helpers.nvim_dir local has_powershell = helpers.has_powershell local set_shell_powershell = helpers.set_shell_powershell @@ -54,7 +55,7 @@ describe("shell command :!", function() if 'openbsd' == helpers.uname() then pending('FIXME #10804') end - child_session.feed_data(":!"..nvim_dir.."/shell-test REP 30001 foo\n") + child_session.feed_data((":!%s REP 30001 foo\n"):format(testprg('shell-test'))) -- If we observe any line starting with a dot, then throttling occurred. -- Avoid false failure on slow systems. @@ -207,12 +208,7 @@ describe("shell command :!", function() it('handles multibyte sequences split over buffer boundaries', function() command('cd '..nvim_dir) - local cmd - if iswin() then - cmd = '!shell-test UTF-8 ' - else - cmd = '!./shell-test UTF-8' - end + local cmd = iswin() and '!shell-test UTF-8 ' or '!./shell-test UTF-8' feed_command(cmd) -- Note: only the first example of split composed char works screen:expect([[ diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua index cdb6256f77..c3b9af5e72 100644 --- a/test/functional/ui/searchhl_spec.lua +++ b/test/functional/ui/searchhl_spec.lua @@ -5,7 +5,7 @@ local command = helpers.command local feed_command = helpers.feed_command local eq = helpers.eq local eval = helpers.eval -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg describe('search highlighting', function() local screen @@ -305,7 +305,7 @@ describe('search highlighting', function() end) it('is preserved during :terminal activity', function() - feed([[:terminal "]]..nvim_dir..[[/shell-test" REP 5000 foo<cr>]]) + feed((':terminal "%s" REP 5000 foo<cr>'):format(testprg('shell-test'))) feed(':file term<CR>') feed('G') -- Follow :terminal output. diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua index 65c6fabfa8..98398bc7a1 100644 --- a/test/functional/ui/wildmode_spec.lua +++ b/test/functional/ui/wildmode_spec.lua @@ -7,7 +7,7 @@ local meths = helpers.meths local eq = helpers.eq local eval = helpers.eval local retry = helpers.retry -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg describe("'wildmenu'", function() local screen @@ -114,7 +114,7 @@ describe("'wildmenu'", function() it('is preserved during :terminal activity', function() command('set wildmenu wildmode=full') command('set scrollback=4') - feed([[:terminal "]]..nvim_dir..[[/shell-test" REP 5000 !terminal_output!<cr>]]) + feed((':terminal "%s" REP 5000 !terminal_output!<cr>'):format(testprg('shell-test'))) feed('G') -- Follow :terminal output. feed([[:sign <Tab>]]) -- Invoke wildmenu. -- NB: in earlier versions terminal output was redrawn during cmdline mode. diff --git a/test/functional/vimscript/let_spec.lua b/test/functional/vimscript/let_spec.lua index 6e93655e32..85c9c690f9 100644 --- a/test/functional/vimscript/let_spec.lua +++ b/test/functional/vimscript/let_spec.lua @@ -7,7 +7,7 @@ local eval = helpers.eval local meths = helpers.meths local exec_capture = helpers.exec_capture local source = helpers.source -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg before_each(clear) @@ -59,7 +59,7 @@ describe(':let', function() end) it("multibyte env var to child process #8398 #9267", function() - local cmd_get_child_env = "let g:env_from_child = system(['"..nvim_dir.."/printenv-test', 'NVIM_TEST_LET'])" + local cmd_get_child_env = ("let g:env_from_child = system(['%s', 'NVIM_TEST_LET'])"):format(testprg('printenv-test')) command("let $NVIM_TEST_LET = 'AìaB'") command(cmd_get_child_env) eq(eval('$NVIM_TEST_LET'), eval('g:env_from_child')) diff --git a/test/functional/vimscript/system_spec.lua b/test/functional/vimscript/system_spec.lua index 9cc6424d31..c915556c57 100644 --- a/test/functional/vimscript/system_spec.lua +++ b/test/functional/vimscript/system_spec.lua @@ -1,11 +1,13 @@ local helpers = require('test.functional.helpers')(after_each) local assert_alive = helpers.assert_alive -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg local eq, call, clear, eval, feed_command, feed, nvim = helpers.eq, helpers.call, helpers.clear, helpers.eval, helpers.feed_command, helpers.feed, helpers.nvim local command = helpers.command +local insert = helpers.insert +local expect = helpers.expect local exc_exec = helpers.exc_exec local iswin = helpers.iswin local os_kill = helpers.os_kill @@ -30,10 +32,6 @@ describe('system()', function() before_each(clear) describe('command passed as a List', function() - local function printargs_path() - return nvim_dir..'/printargs-test' .. (iswin() and '.exe' or '') - end - it('throws error if cmd[0] is not executable', function() eq("Vim:E475: Invalid value for argument cmd: 'this-should-not-exist' is not executable", pcall_err(call, 'system', { 'this-should-not-exist' })) @@ -66,23 +64,23 @@ describe('system()', function() it('quotes arguments correctly #5280', function() local out = call('system', - { printargs_path(), [[1]], [[2 "3]], [[4 ' 5]], [[6 ' 7']] }) + { testprg('printargs-test'), [[1]], [[2 "3]], [[4 ' 5]], [[6 ' 7']] }) eq(0, eval('v:shell_error')) eq([[arg1=1;arg2=2 "3;arg3=4 ' 5;arg4=6 ' 7';]], out) - out = call('system', { printargs_path(), [['1]], [[2 "3]] }) + out = call('system', { testprg('printargs-test'), [['1]], [[2 "3]] }) eq(0, eval('v:shell_error')) eq([[arg1='1;arg2=2 "3;]], out) - out = call('system', { printargs_path(), "A\nB" }) + out = call('system', { testprg('printargs-test'), "A\nB" }) eq(0, eval('v:shell_error')) eq("arg1=A\nB;", out) end) it('calls executable in $PATH', function() - if 0 == eval("executable('python')") then pending("missing `python`") end - eq("foo\n", eval([[system(['python', '-c', 'print("foo")'])]])) + if 0 == eval("executable('python3')") then pending("missing `python3`") end + eq("foo\n", eval([[system(['python3', '-c', 'print("foo")'])]])) eq(0, eval('v:shell_error')) end) @@ -167,7 +165,7 @@ describe('system()', function() end end) - it('works with powershell', function() + it('with powershell', function() helpers.set_shell_powershell() eq('a\nb\n', eval([[system('Write-Output a b')]])) eq('C:\\\n', eval([[system('cd c:\; (Get-Location).Path')]])) @@ -175,12 +173,11 @@ describe('system()', function() end) end - it('works with powershell w/ UTF-8 text (#13713)', function() + it('powershell w/ UTF-8 text #13713', function() if not helpers.has_powershell() then - pending("not tested; powershell was not found", function() end) + pending("powershell not found", function() end) return end - -- Should work with recommended config used in helper helpers.set_shell_powershell() eq('ああ\n', eval([[system('Write-Output "ああ"')]])) -- Sanity test w/ default encoding @@ -430,7 +427,7 @@ describe('system()', function() end) it("with a program that doesn't close stdout will exit properly after passing input", function() - local out = eval(string.format("system('%s', 'clip-data')", nvim_dir..'/streams-test')) + local out = eval(string.format("system('%s', 'clip-data')", testprg('streams-test'))) assert(out:sub(0, 5) == 'pid: ', out) os_kill(out:match("%d+")) end) @@ -609,17 +606,16 @@ describe('systemlist()', function() end) it("with a program that doesn't close stdout will exit properly after passing input", function() - local out = eval(string.format("systemlist('%s', 'clip-data')", nvim_dir..'/streams-test')) + local out = eval(string.format("systemlist('%s', 'clip-data')", testprg('streams-test'))) assert(out[1]:sub(0, 5) == 'pid: ', out) os_kill(out[1]:match("%d+")) end) - it('works with powershell w/ UTF-8 text (#13713)', function() + it('powershell w/ UTF-8 text #13713', function() if not helpers.has_powershell() then - pending("not tested; powershell was not found", function() end) + pending("powershell not found", function() end) return end - -- Should work with recommended config used in helper helpers.set_shell_powershell() eq({iswin() and 'あ\r' or 'あ'}, eval([[systemlist('Write-Output あ')]])) -- Sanity test w/ default encoding @@ -630,3 +626,31 @@ describe('systemlist()', function() end) end) + +describe('shell :!', function() + before_each(clear) + + it(':{range}! with powershell filter/redirect #16271', function() + local screen = Screen.new(500, 8) + screen:attach() + local found = helpers.set_shell_powershell(true) + insert([[ + 3 + 1 + 4 + 2]]) + feed(':4verbose %!sort<cr>') + screen:expect{ + any=[[Executing command: .?Start%-Process sort %-RedirectStandardInput .* %-RedirectStandardOutput .* %-NoNewWindow %-Wait]] + } + feed('<CR>') + if found then + -- Not using fake powershell, so we can test the result. + expect([[ + 1 + 2 + 3 + 4]]) + end + end) +end) |