From 4a0859631407bc9279a935f3219faabc0991a826 Mon Sep 17 00:00:00 2001 From: tao <2471314@gmail.com> Date: Mon, 13 Jul 2026 01:23:44 +0800 Subject: [PATCH] fix(path): normalize separators (slashes) #39155 Problem: Continue separators normalization, and try to keep it at the nvim <-> external boundary, e.g., `fn.xxx`, `api.xxx`, `:xxx` Solution: some key changes - normalize `$HOME-windows` - normalize entry points of changing directory - `nvim_set_current_dir()`, `chdir()` - normalize the named pipe - `--listen` arg, `--server` arg, `:restart` - `serverstart()`, `sockconnect()`, `serverstop()` - make `expand()` respect 'shellslash' again - clean up `did_set_shellslash` - replace `forward_slash` with `TO_SLASH` - remove obsolete `TMP_PATHSEPSTR` - remove `slash_adjust` in `do_autocmd_dirchanged`? - make `fnamemodify()` always return `/` (except when substituting separators via `:s`, `:gs` ?) Note: - these funcs still apply `slash_adjust` on return, as before: `:pwd`, `chdir()`, `exepath()`, `getcwd()` - these funcs alwarys return `/`, unlike before the normalization PRs - `getcompletion()`, `finddir()`, `findfile()` Also - clean up code, comments, formatting and tests - move `TO_SLASH` from `f_chdir` to `changedir_func` - move `TO_SLASH` from `f_bufadd` to `buflist_new` Co-authored-by: Justin M. Keyes --- runtime/doc/options.txt | 3 +- runtime/doc/vimfn.txt | 2 +- runtime/lua/vim/_meta/vimfn.gen.lua | 2 +- src/nvim/api/vim.c | 6 +- src/nvim/arglist.c | 24 -- src/nvim/autocmd.c | 35 +- src/nvim/buffer.c | 25 +- src/nvim/cmdexpand.c | 19 +- src/nvim/cmdexpand.h | 1 + src/nvim/eval.lua | 2 +- src/nvim/eval/fs.c | 19 +- src/nvim/eval/funcs.c | 21 +- src/nvim/event/socket.c | 5 +- src/nvim/ex_docmd.c | 25 +- src/nvim/file_search.c | 7 - src/nvim/fileio.c | 16 - src/nvim/globals.h | 6 - src/nvim/main.c | 6 +- src/nvim/msgpack_rpc/server.c | 7 +- src/nvim/option.c | 25 -- src/nvim/options.lua | 4 +- src/nvim/os/env.c | 10 +- src/nvim/os/stdpaths.c | 6 +- src/nvim/path.c | 11 +- src/nvim/runtime.c | 13 - src/nvim/search.c | 1 + test/functional/api/buffer_spec.lua | 234 ++++++++++- test/functional/api/server_requests_spec.lua | 16 +- test/functional/api/vim_spec.lua | 45 +- test/functional/autocmd/autocmd_spec.lua | 13 + test/functional/core/main_spec.lua | 22 - test/functional/core/path_spec.lua | 63 ++- test/functional/core/server_spec.lua | 40 +- test/functional/core/spellfile_spec.lua | 4 +- test/functional/core/startup_spec.lua | 126 +++--- test/functional/editor/completion_spec.lua | 14 + test/functional/editor/runtime_spec.lua | 233 ++++------- test/functional/ex_cmds/mksession_spec.lua | 9 +- test/functional/ex_cmds/source_spec.lua | 10 +- test/functional/ex_cmds/verbose_spec.lua | 4 +- test/functional/lua/uri_spec.lua | 2 +- test/functional/options/defaults_spec.lua | 393 +++++------------- test/functional/plugin/pack_spec.lua | 4 +- test/functional/terminal/edit_spec.lua | 2 +- test/functional/terminal/tui_spec.lua | 4 + .../vimscript/ctx_functions_spec.lua | 2 +- test/functional/vimscript/executable_spec.lua | 48 +-- .../functional/vimscript/fnamemodify_spec.lua | 46 +- test/old/testdir/test_expand_func.vim | 4 +- test/old/testdir/test_plugin_netrw.vim | 12 +- test/old/testdir/test_windows_home.vim | 4 +- 51 files changed, 802 insertions(+), 853 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index d3a1923d6f..b261a5ca59 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -3459,8 +3459,7 @@ A jump table for the options with a short description can be found at |Q_op|. attempt to set 'guifontwide' to a matching double-width font. *'helpfile'* *'hf'* -'helpfile' 'hf' string (default (MS-Windows) "$VIMRUNTIME\doc\help.txt" - (others) "$VIMRUNTIME/doc/help.txt") +'helpfile' 'hf' string (default "$VIMRUNTIME/doc/help.txt") global Disallowed in |modeline|. |no-modeline-option| Name of the main help file. All distributed help files should be diff --git a/runtime/doc/vimfn.txt b/runtime/doc/vimfn.txt index 371779adfa..32dc585afe 100644 --- a/runtime/doc/vimfn.txt +++ b/runtime/doc/vimfn.txt @@ -9215,7 +9215,7 @@ serverstart([{address}]) *serverstart()* < Example named pipe: >vim if has('win32') - echo serverstart('\\.\pipe\nvim-pipe-1234') + echo serverstart('//./pipe/nvim-pipe-1234') else echo serverstart('nvim.sock') endif diff --git a/runtime/lua/vim/_meta/vimfn.gen.lua b/runtime/lua/vim/_meta/vimfn.gen.lua index b7ba4d138d..ec0a4f6d71 100644 --- a/runtime/lua/vim/_meta/vimfn.gen.lua +++ b/runtime/lua/vim/_meta/vimfn.gen.lua @@ -8363,7 +8363,7 @@ function vim.fn.serverlist(opts) end --- --- vim --- if has('win32') ---- echo serverstart('\\.\pipe\nvim-pipe-1234') +--- echo serverstart('//./pipe/nvim-pipe-1234') --- else --- echo serverstart('nvim.sock') --- endif diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 888ae8b42a..66956fc946 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -695,12 +695,8 @@ void nvim_set_current_dir(String dir, Error *err) return; }); - char string[MAXPATHL]; - memcpy(string, dir.data, dir.size); - string[dir.size] = NUL; - TRY_WRAP(err, { - changedir_func(string, kCdScopeGlobal); + changedir_func(dir.data, kCdScopeGlobal); }); } diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c index deb783e2c8..7ef6a7429b 100644 --- a/src/nvim/arglist.c +++ b/src/nvim/arglist.c @@ -228,30 +228,6 @@ void alist_add(alist_T *al, char *fname, int set_fnum) wp->w_locked--; } -#ifdef BACKSLASH_IN_FILENAME - -/// Adjust slashes in file names. Called after 'shellslash' was set. -void alist_slash_adjust(void) -{ - for (int i = 0; i < GARGCOUNT; i++) { - if (GARGLIST[i].ae_fname != NULL) { - slash_adjust(GARGLIST[i].ae_fname); - } - } - - FOR_ALL_TAB_WINDOWS(tp, wp) { - if (wp->w_alist != &global_alist) { - for (int i = 0; i < WARGCOUNT(wp); i++) { - if (WARGLIST(wp)[i].ae_fname != NULL) { - slash_adjust(WARGLIST(wp)[i].ae_fname); - } - } - } - } -} - -#endif - /// Isolate one argument, taking backticks. /// Changes the argument in-place, puts a NUL after it. Backticks remain. /// diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index ba5b60bfa9..1bfcb681da 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -789,18 +789,9 @@ void do_autocmd(exarg_T *eap, char *arg_in, int forceit) *cmd++ = NUL; } - // Expand environment variables in the pattern. Set 'shellslash', we want - // forward slashes here. + // Expand environment variables in the pattern. if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL) { -#ifdef BACKSLASH_IN_FILENAME - int p_ssl_save = p_ssl; - - p_ssl = true; -#endif envpat = expand_env_save(pat); -#ifdef BACKSLASH_IN_FILENAME - p_ssl = p_ssl_save; -#endif if (envpat != NULL) { pat = envpat; } @@ -1583,7 +1574,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force } fname = xstrdup(fname); // make a copy, so we can change it } else { - sfname = xstrdup(fname); + sfname = TO_SLASH_SAVE(fname); // Don't try expanding the following events. if (event == EVENT_CMDLINECHANGED || event == EVENT_CMDLINEENTER @@ -1616,7 +1607,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force || event == EVENT_WINCLOSED || event == EVENT_WINRESIZED || event == EVENT_WINSCROLLED) { - fname = xstrdup(fname); + fname = TO_SLASH_SAVE(fname); autocmd_fname_full = true; // don't expand it later } else { fname = FullName_save(fname, false); @@ -1635,15 +1626,6 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force save_changed = curbuf->b_changed; old_curbuf = curbuf; -#ifdef BACKSLASH_IN_FILENAME - // Replace all backslashes with forward slashes. This makes the - // autocommand patterns portable between Unix and Windows. - if (sfname != NULL) { - forward_slash(sfname); - } - forward_slash(fname); -#endif - // Set the name to be used for . autocmd_match = fname; @@ -2079,14 +2061,6 @@ bool has_autocmd(event_T event, char *sfname, buf_T *buf) return false; } -#ifdef BACKSLASH_IN_FILENAME - // Replace all backslashes with forward slashes. This makes the - // autocommand patterns portable between Unix and Windows. - sfname = xstrdup(sfname); - forward_slash(sfname); - forward_slash(fname); -#endif - AutoCmdVec *const acs = &autocmds[(int)event]; for (size_t i = 0; i < kv_size(*acs); i++) { AutoPat *const ap = kv_A(*acs, i).pat; @@ -2100,9 +2074,6 @@ bool has_autocmd(event_T event, char *sfname, buf_T *buf) } xfree(fname); -#ifdef BACKSLASH_IN_FILENAME - xfree(sfname); -#endif return retval; } diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 991e850002..8f10c93173 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -2038,6 +2038,7 @@ buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags) assert(sfname != NULL); buf->b_ffname = ffname; buf->b_sfname = xstrdup(sfname); + TO_SLASH(buf->b_sfname); } clear_wininfo(buf); @@ -3119,6 +3120,7 @@ int setfname(buf_T *buf, char *ffname_arg, char *sfname_arg, bool message) } sfname = xstrdup(sfname); #ifdef CASE_INSENSITIVE_FILENAME + TO_SLASH(sfname); path_fix_case(sfname); // set correct case for short file name #endif if (buf->b_sfname != buf->b_ffname) { @@ -3224,22 +3226,6 @@ int buflist_add(char *fname, int flags) return 0; } -#ifdef BACKSLASH_IN_FILENAME -/// Adjust slashes in file names. Called after 'shellslash' was set. -void buflist_slash_adjust(void) -{ - FOR_ALL_BUFFERS(bp) { - if (bp->b_ffname != NULL) { - slash_adjust(bp->b_ffname); - } - if (bp->b_sfname != NULL) { - slash_adjust(bp->b_sfname); - } - } -} - -#endif - /// Set alternate cursor position for the current buffer and window "win". /// Also save the local window option values. void buflist_altfpos(win_T *win) @@ -3471,10 +3457,17 @@ void maketitle(void) } } else { // Format: "fname + (path) (1 of 2) - Nvim". +#ifdef MSWIN + int p_ssl_save = p_ssl; + p_ssl = true; +#endif char *default_titlestring = "%t%( %M%)%( (%{expand('%:p:~:h')})%)%a - Nvim"; build_stl_str_hl(curwin, buf, sizeof(buf), default_titlestring, kOptTitlestring, 0, 0, maxlen, NULL, NULL, NULL, NULL); title_str = buf; +#ifdef MSWIN + p_ssl = p_ssl_save; +#endif } } bool mustset = value_change(title_str, &lasttitle); diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index 54a8762feb..01e2eb6d9e 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -2768,11 +2768,13 @@ static int expand_files_and_dirs(expand_T *xp, char *pat, char ***matches, int * xfree(pat); } #ifdef BACKSLASH_IN_FILENAME - if ((options & WILD_USE_COMPLETESLASH) && ((p_csl[0] == NUL && !p_ssl) || p_csl[0] == 'b')) { + if (((options & WILD_USE_SHELLSLASH) && !p_ssl) + || ((options & WILD_USE_COMPLETESLASH) + && ((p_csl[0] == NUL && !p_ssl) || p_csl[0] == 'b'))) { for (int j = 0; j < *numMatches; j++) { char *ptr = (*matches)[j]; for (; *ptr; ptr++) { - if (*ptr == '/') { + if (*ptr == PATHSEP) { *ptr = '\\'; } } @@ -3721,14 +3723,6 @@ void globpath(char *path, char *file, garray_T *ga, int expand_options, bool dir size_t filelen = strlen(file); -#ifdef MSWIN - // Using the platform's path separator (\) makes vim incorrectly - // treat it as an escape character, use '/' instead. -# define TMP_PATHSEPSTR "/" -#else -# define TMP_PATHSEPSTR PATHSEPSTR -#endif - // Loop over all entries in {path}. while (*path != NUL) { // Copy one item of the path to buf[] and concatenate the file name. @@ -3736,11 +3730,11 @@ void globpath(char *path, char *file, garray_T *ga, int expand_options, bool dir // length of the path portion of buf (including trailing slash). size_t pathlen = copy_option_part(&path, buf, MAXPATHL, ","); size_t seplen = (*buf != NUL && !after_pathsep(buf, buf + pathlen)) - ? STRLEN_LITERAL(TMP_PATHSEPSTR) : 0; + ? STRLEN_LITERAL(PATHSEPSTR) : 0; if (pathlen + seplen + filelen + 1 <= MAXPATHL) { if (seplen > 0) { - xmemcpyz(buf + pathlen, S_LEN(TMP_PATHSEPSTR)); + xmemcpyz(buf + pathlen, S_LEN(PATHSEPSTR)); pathlen += seplen; } xmemcpyz(buf + pathlen, file, filelen); @@ -3765,7 +3759,6 @@ void globpath(char *path, char *file, garray_T *ga, int expand_options, bool dir xfree(buf); } -#undef TMP_PATHSEPSTR /// Translate some keys pressed when 'wildmenu' is used. int wildmenu_translate_key(CmdlineInfo *cclp, int key, expand_T *xp, bool did_wild_list) diff --git a/src/nvim/cmdexpand.h b/src/nvim/cmdexpand.h index c8e77682bd..0094bdcdd3 100644 --- a/src/nvim/cmdexpand.h +++ b/src/nvim/cmdexpand.h @@ -44,6 +44,7 @@ enum { WILD_MAY_EXPAND_PATTERN = 0x8000, WILD_FUNC_TRIGGER = 0x10000, ///< called from wildtrigger() WILD_NOINSERT = 0x20000, + WILD_USE_SHELLSLASH = 0x40000, }; #include "cmdexpand.h.generated.h" diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index a14d498d28..89b731f8b4 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -10047,7 +10047,7 @@ M.funcs = { vim if has('win32') - echo serverstart('\\.\pipe\nvim-pipe-1234') + echo serverstart('//./pipe/nvim-pipe-1234') else echo serverstart('nvim.sock') endif diff --git a/src/nvim/eval/fs.c b/src/nvim/eval/fs.c index 808bfa4699..d320229e2b 100644 --- a/src/nvim/eval/fs.c +++ b/src/nvim/eval/fs.c @@ -64,14 +64,16 @@ static const char e_error_while_writing_str[] = N_("E80: Error while writing: %s /// @param fnamep file name so far /// @param bufp buffer for allocated file name or NULL /// @param fnamelen length of fnamep +/// @param use_shellslash adjust separators in `*fnamep` according to 'shellslash' int modify_fname(char *src, bool tilde_file, size_t *usedlen, char **fnamep, char **bufp, - size_t *fnamelen) + size_t *fnamelen, bool use_shellslash) { int valid = 0; char *s, *p, *pbuf; char dirname[MAXPATHL]; bool has_fullname = false; bool has_homerelative = false; + bool didit = false; repeat: // ":p" - full path/file_name @@ -303,13 +305,20 @@ repeat: *usedlen += 2; } +#ifdef BACKSLASH_IN_FILENAME + if (!didit && use_shellslash && *fnamep != NULL) { + *fnamep = xstrdup(*fnamep); + slash_adjust(*fnamep); + xfree(*bufp); + *bufp = *fnamep; + } +#endif + // ":s?pat?foo?" - substitute // ":gs?pat?foo?" - global substitute if (src[*usedlen] == ':' && (src[*usedlen + 1] == 's' || (src[*usedlen + 1] == 'g' && src[*usedlen + 2] == 's'))) { - bool didit = false; - char *flags = ""; s = src + *usedlen + 2; if (src[*usedlen + 1] == 'g') { @@ -612,11 +621,13 @@ void f_fnamemodify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (mods == NULL || fname == NULL) { fname = NULL; } else { + fbuf = TO_SLASH_SAVE(fname); + fname = fbuf; len = strlen(fname); if (*mods != NUL) { size_t usedlen = 0; modify_fname((char *)mods, false, &usedlen, - (char **)&fname, &fbuf, &len); + (char **)&fname, &fbuf, &len, false); } } diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 6ad986ec91..542b616285 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -1402,14 +1402,8 @@ static void f_exists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// "expand()" function static void f_expand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND; + int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND|WILD_USE_SHELLSLASH; bool error = false; -#ifdef BACKSLASH_IN_FILENAME - char *p_csl_save = p_csl; - - // avoid using 'completeslash' here - p_csl = empty_string_option; -#endif rettv->v_type = VAR_STRING; if (argvars[1].v_type != VAR_UNKNOWN @@ -1469,9 +1463,6 @@ static void f_expand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) rettv->vval.v_string = NULL; } } -#ifdef BACKSLASH_IN_FILENAME - p_csl = p_csl_save; -#endif } /// "menu_get(path [, modes])" function @@ -6394,7 +6385,7 @@ static void f_serverstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) emsg(_(e_invarg)); return; } - address = xstrdup(tv_get_string(argvars)); + address = TO_SLASH_SAVE(tv_get_string(argvars)); } else { address = server_address_new(NULL); } @@ -6859,7 +6850,7 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } const char *mode = tv_get_string(&argvars[0]); - const char *address = tv_get_string(&argvars[1]); + const char *address = TO_SLASH_SAVE(tv_get_string(&argvars[1])); bool tcp; if (strcmp(mode, "tcp") == 0) { @@ -6868,7 +6859,7 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) tcp = false; } else { semsg(_(e_invarg2), "invalid mode"); - return; + goto cleanup; } bool rpc = false; @@ -6878,7 +6869,7 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) rpc = tv_dict_get_number(opts, "rpc") != 0; if (!tv_dict_get_callback(opts, S_LEN("on_data"), &on_data.cb)) { - return; + goto cleanup; } on_data.buffered = tv_dict_get_number(opts, "data_buffered"); if (on_data.buffered && on_data.cb.type == kCallbackNone) { @@ -6895,6 +6886,8 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) rettv->vval.v_number = (varnumber_T)id; rettv->v_type = VAR_NUMBER; +cleanup: + XFREE_CLEAR(address); } /// "stdioopen()" function diff --git a/src/nvim/event/socket.c b/src/nvim/event/socket.c index 7ad7a824a4..038a9461b7 100644 --- a/src/nvim/event/socket.c +++ b/src/nvim/event/socket.c @@ -33,9 +33,8 @@ char *socket_address_tcp_host_end(const char *address) return NULL; } - // Windows drive letter path: "X:\..." or "X:/..." is a local path, not TCP. - if (ASCII_ISALPHA((uint8_t)address[0]) && address[1] == ':' - && (address[2] == '\\' || address[2] == '/')) { + // Windows drive letter path: "X:/..." is a local path, not TCP. + if (ASCII_ISALPHA((uint8_t)address[0]) && address[1] == ':' && address[2] == '/') { return NULL; } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 1ebc0c3122..838f340de1 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5030,7 +5030,7 @@ static void ex_restart(exarg_T *eap) if (next_li != NULL) { const char *addr = tv_get_string(TV_LIST_ITEM_TV(next_li)); if (strstr(addr, ":") || strstr(addr, "/") || strstr(addr, "\\")) { - listen_arg = addr; + listen_arg = TO_SLASH_SAVE(addr); #ifdef MSWIN // On Windows, don't pass --listen to new server (named pipe can't be reused immediately). // Instead pass the address via RPC; new server rebinds after startup. @@ -5060,14 +5060,12 @@ static void ex_restart(exarg_T *eap) // On Windows, --listen is omitted from child argv because the named pipe can't be reused immediately. // Recover the canonical address from the Lua module state (set by the previous rebind_after_restart() call), // and keep the current listener alive (new server reclaims it). - char *listen_arg_alloc = NULL; if (listen_arg == NULL) { Error lua_err = ERROR_INIT; Object rv = NLUA_EXEC_STATIC("return require('vim._core.server').restart_canonical_addr", (Array)ARRAY_DICT_INIT, kRetObject, NULL, &lua_err); if (!ERROR_SET(&lua_err) && rv.type == kObjectTypeString && rv.data.string.size > 0) { - listen_arg_alloc = xstrdup(rv.data.string.data); - listen_arg = listen_arg_alloc; + listen_arg = xstrdup(rv.data.string.data); } api_free_object(rv); api_clear_error(&lua_err); @@ -5222,9 +5220,7 @@ fail_1: if (server_stopped && server_start(listen_arg) != 0) { semsg("couldn't resume listening on %s", listen_arg); } -#ifdef MSWIN - xfree(listen_arg_alloc); -#endif + XFREE_CLEAR(listen_arg); } /// ":close": close current window, unless it is the last one @@ -5713,6 +5709,7 @@ static char *findfunc_find_file(char *findarg, size_t findarg_len, int count) findarg[findarg_len] = cc; + TO_SLASH(ret_fname); return ret_fname; } @@ -6511,15 +6508,19 @@ bool changedir_func(char *new_dir, CdScope scope) new_dir = NameBuff; } + new_dir = TO_SLASH_SAVE(new_dir); + bool dir_differs = pdir == NULL || pathcmp(pdir, new_dir, -1) != 0; if (dir_differs) { do_autocmd_dirchanged(new_dir, scope, kCdCauseManual, true); if (vim_chdir(new_dir) != 0) { emsg(_(e_failed)); + xfree(new_dir); xfree(pdir); return false; } } + xfree(new_dir); char **pp; switch (scope) { @@ -7733,6 +7734,8 @@ char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnum bool tilde_file = false; bool skip_mod = false; char strbuf[30]; + // we want 'shellslash' for path expansions like "%", "#", "", etc. + bool use_shellslash = false; *errormsg = NULL; if (escaped != NULL) { @@ -7785,6 +7788,7 @@ char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnum result = curbuf->b_fname; tilde_file = strcmp(result, "~") == 0; } + use_shellslash = true; break; case SPEC_HASH: // '#' or "#99": alternate file @@ -7798,6 +7802,7 @@ char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnum skip_mod = true; break; } + use_shellslash = true; char *s = src + 1; if (*s == '<') { // "#<99" uses v:oldfiles. s++; @@ -7852,6 +7857,7 @@ char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnum break; case SPEC_AFILE: // file name for autocommand + use_shellslash = !autocmd_fname_full; if (autocmd_fname != NULL && !autocmd_fname_full) { // Still need to turn the fname into a full path. It was // postponed to avoid a delay when is not used. @@ -7887,6 +7893,7 @@ char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnum break; case SPEC_SFILE: // file name for ":so" command + use_shellslash = true; result = estack_sfile(ESTACK_SFILE); if (result == NULL) { *errormsg = _(e_no_source_file_name_to_substitute_for_sfile); @@ -7895,6 +7902,7 @@ char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnum resultbuf = result; // remember allocated string break; case SPEC_STACK: // call stack + use_shellslash = true; result = estack_sfile(ESTACK_STACK); if (result == NULL) { *errormsg = _(e_no_call_stack_to_substitute_for_stack); @@ -7903,6 +7911,7 @@ char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnum resultbuf = result; // remember allocated string break; case SPEC_SCRIPT: // script file name + use_shellslash = true; result = estack_sfile(ESTACK_SCRIPT); if (result == NULL) { *errormsg = _(e_no_script_file_name_to_substitute_for_script); @@ -7957,7 +7966,7 @@ char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnum } } else if (!skip_mod) { valid |= modify_fname(src, tilde_file, usedlen, &result, - &resultbuf, &resultlen); + &resultbuf, &resultlen, use_shellslash); if (result == NULL) { *errormsg = ""; return NULL; diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index eb13e80290..08fc008ca2 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1858,13 +1858,6 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause, bool pre abort(); } -#ifdef BACKSLASH_IN_FILENAME - char new_dir_buf[MAXPATHL]; - STRCPY(new_dir_buf, new_dir); - slash_adjust(new_dir_buf); - new_dir = new_dir_buf; -#endif - if (pre) { tv_dict_add_str(dict, S_LEN("directory"), new_dir); } else { diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 72a0b9f24a..77b293babc 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -3284,22 +3284,6 @@ void write_lnum_adjust(linenr_T offset) } } -#ifdef BACKSLASH_IN_FILENAME -/// Convert all backslashes in fname to forward slashes in-place, -/// unless when it looks like a URL. -void forward_slash(char *fname) -{ - if (path_with_url(fname)) { - return; - } - for (char *p = fname; *p != NUL; p++) { - if (*p == '\\') { - *p = '/'; - } - } -} -#endif - /// Path to Nvim's own temp dir. Ends in a slash. static char *vim_tempdir = NULL; #ifdef HAVE_DIRFD_AND_FLOCK diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 647aba4a68..964eb9cce4 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -761,12 +761,6 @@ EXTERN bool no_hlsearch INIT( = false); EXTERN bool typebuf_was_filled INIT( = false); // received text from client // or from feedkeys() -#ifdef BACKSLASH_IN_FILENAME -EXTERN char psepc INIT( = '\\'); // normal path separator character -EXTERN char psepcN INIT( = '/'); // abnormal path separator character -EXTERN char pseps[2] INIT( = { '\\', 0 }); // normal path separator string -#endif - // Set to kTrue when an operator is being executed with virtual editing // kNone when no operator is being executed, kFalse otherwise. EXTERN TriState virtual_op INIT( = kNone); diff --git a/src/nvim/main.c b/src/nvim/main.c index 2c66f6e8f8..0f7976dce9 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1402,9 +1402,11 @@ static void command_line_scan(mparm_T *parmp) parmp->pre_commands[parmp->n_pre_commands++] = argv[0]; } else if (strequal(argv[-1], "--listen")) { // "--listen {address}" + TO_SLASH(argv[0]); parmp->listen_addr = argv[0]; } else if (strequal(argv[-1], "--server")) { // "--server {address}" + TO_SLASH(argv[0]); parmp->server_addr = argv[0]; } // "--startuptime " already handled @@ -1490,14 +1492,14 @@ scripterror: // On Windows expand "~\" or "~/" prefix in file names to profile directory. #ifdef MSWIN - if (*p == '~' && (p[1] == '\\' || p[1] == '/')) { + TO_SLASH(p); + if (*p == '~' && p[1] == '/') { size_t size = strlen(os_homedir()) + strlen(p); char *tilde_expanded = xmalloc(size); snprintf(tilde_expanded, size, "%s%s", os_homedir(), p + 1); xfree(p); p = tilde_expanded; } - TO_SLASH(p); #endif if (parmp->diff_mode && os_isdir(p) && GARGCOUNT > 0 diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c index 2cd7207ff7..5f1e96873b 100644 --- a/src/nvim/msgpack_rpc/server.c +++ b/src/nvim/msgpack_rpc/server.c @@ -43,7 +43,7 @@ bool server_init(const char *listen_addr) if (!listen_addr || listen_addr[0] == '\0') { if (os_env_exists(ENV_LISTEN, true)) { user_arg = kFalse; // User-provided env var. - listen_addr = os_getenv(ENV_LISTEN); + listen_addr = TO_SLASH_SAVE(os_getenv_noalloc(ENV_LISTEN)); } else { user_arg = kNone; // Autogenerated server address. listen_addr = server_address_new(NULL); @@ -111,7 +111,7 @@ void server_teardown(void) /// Generates unique address for local server. /// /// Named pipe format: -/// - Windows: "\\.\pipe\.." +/// - Windows: "//./pipe/.." /// - Other: "/tmp/nvim.user/xxx/.." char *server_address_new(const char *name) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET @@ -120,7 +120,7 @@ char *server_address_new(const char *name) char fmt[ADDRESS_MAX_SIZE]; #ifdef MSWIN (void)get_appname(true); // Writes appname to NameBuf. - int r = snprintf(fmt, sizeof(fmt), "\\\\.\\pipe\\%s.%" PRIu64 ".%" PRIu32, + int r = snprintf(fmt, sizeof(fmt), "//./pipe/%s.%" PRIu64 ".%" PRIu32, name ? name : NameBuff, os_get_pid(), count++); #else char *dir = stdpaths_get_xdg_var(kXDGRuntimeDir); @@ -222,6 +222,7 @@ bool server_stop(const char *endpoint, bool keep_vservername) // Trim to `ADDRESS_MAX_SIZE` xstrlcpy(addr, endpoint, sizeof(addr)); + TO_SLASH(addr); int i = 0; // Index of the server whose address equals addr. for (; i < watchers.ga_len; i++) { diff --git a/src/nvim/option.c b/src/nvim/option.c index 4640a49a4e..86629174bc 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2687,31 +2687,6 @@ static const char *did_set_scrollbind(optset_T *args) return NULL; } -#ifdef BACKSLASH_IN_FILENAME -/// Process the updated 'shellslash' option value. -/// TODO(ntdiary): Remove this once we're confident that the `shellslash` -/// option is no longer needed. -static const char *did_set_shellslash(optset_T *args FUNC_ATTR_UNUSED) -{ - if (p_ssl) { - psepc = '/'; - psepcN = '\\'; - pseps[0] = '/'; - } else { - psepc = '\\'; - psepcN = '/'; - pseps[0] = '\\'; - } - - // TODO(ntdiary): Remove these in the follow PR. - // need to adjust the file name arguments and buffer names. - // buflist_slash_adjust(); - // alist_slash_adjust(); - // scriptnames_slash_adjust(); - return NULL; -} -#endif - /// Process the new 'shiftwidth' or the 'tabstop' option value. static const char *did_set_shiftwidth_tabstop(optset_T *args) { diff --git a/src/nvim/options.lua b/src/nvim/options.lua index ebd1e3048d..990b063152 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -4259,8 +4259,7 @@ local options = { cb = 'did_set_helpfile', defaults = { if_true = macros('DFLT_HELPFILE', 'string'), - doc = [[(MS-Windows) "$VIMRUNTIME\doc\help.txt" - (others) "$VIMRUNTIME/doc/help.txt"]], + doc = [["$VIMRUNTIME/doc/help.txt"]], }, desc = [=[ Name of the main help file. All distributed help files should be @@ -7868,7 +7867,6 @@ local options = { }, { abbreviation = 'ssl', - cb = 'did_set_shellslash', defaults = { condition = 'MSWIN', if_true = false, diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index feaca0e159..f93ee24081 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -412,7 +412,7 @@ void init_homedir(void) char *homedrive = os_getenv("HOMEDRIVE"); char *homepath = os_getenv("HOMEPATH"); if (homepath == NULL) { - homepath = xstrdup("\\"); + homepath = xstrdup(PATHSEPSTR); } if (homedrive != NULL && strlen(homedrive) + strlen(homepath) < MAXPATHL) { @@ -473,7 +473,7 @@ void init_homedir(void) } #endif if (var != NULL) { - homedir = xstrdup(var); + homedir = TO_SLASH_SAVE(var); } xfree(tofree); } @@ -619,6 +619,10 @@ size_t expand_env_esc(const char *restrict srcp, char *restrict dst, int dstlen, #endif *var = NUL; var = vim_getenv(dst); + // Backslashes in `srcp` might just be used for escaping. Expanded env + // vars represent paths, so their backslashes can be safely normalized. + // Autocmd file patterns require this normalization. + TO_SLASH(var); mustfree = true; #ifdef UNIX } @@ -1042,7 +1046,7 @@ size_t home_replace(const buf_T *const buf, const char *src, char *const dst, si size_t usedlen = 0; size_t flen = strlen(homedir_env_mod); char *fbuf = NULL; - modify_fname(":p", false, &usedlen, &homedir_env_mod, &fbuf, &flen); + modify_fname(":p", false, &usedlen, &homedir_env_mod, &fbuf, &flen, false); flen = strlen(homedir_env_mod); assert(homedir_env_mod != homedir_env); if (vim_ispathsep(homedir_env_mod[flen - 1])) { diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c index 609790a01e..ae8f09895c 100644 --- a/src/nvim/os/stdpaths.c +++ b/src/nvim/os/stdpaths.c @@ -75,6 +75,8 @@ const char *get_appname(bool namelike) xstrlcpy(NameBuff, "nvim", sizeof(NameBuff)); } + TO_SLASH(NameBuff); + if (namelike) { // Appname may be a relative path, replace slashes to make it name-like. memchrsub(NameBuff, '/', '-', sizeof(NameBuff)); @@ -94,10 +96,6 @@ bool appname_is_valid(void) || strequal(appname, "\\") || strequal(appname, ".") || strequal(appname, "..") -#ifdef BACKSLASH_IN_FILENAME - || strstr(appname, "\\..") != NULL - || strstr(appname, "..\\") != NULL -#endif || strstr(appname, "/..") != NULL || strstr(appname, "../") != NULL) { return false; diff --git a/src/nvim/path.c b/src/nvim/path.c index 79391674c7..b3ffed1a51 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -1546,9 +1546,11 @@ void slash_adjust(char *p) } } + char from = p_ssl ? '\\' : PATHSEP; + char to = p_ssl ? PATHSEP : '\\'; while (*p) { - if (*p == psepcN) { - *p = psepc; + if (*p == from) { + *p = to; } MB_PTR_ADV(p); } @@ -1564,10 +1566,11 @@ char *path_to_backslash(char *p) return p; } -/// Convert all backslashes to forward slashes in-place. +/// Convert all backslashes to forward slashes in-place, +/// unless when it looks like a URL (e.g. `term://xxxC:\cmd.exe`). char *path_to_slash(char *p) { - if (p != NULL) { + if (p != NULL && !path_with_url(p)) { strchrsub(p, '\\', PATHSEP); } return p; diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 2e474ce866..28e79a922f 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -2562,19 +2562,6 @@ void ex_scriptnames(exarg_T *eap) } } -#ifdef BACKSLASH_IN_FILENAME -/// Fix slashes in the list of script names for 'shellslash'. -void scriptnames_slash_adjust(void) -{ - for (int i = 1; i <= script_items.ga_len; i++) { - if (SCRIPT_ITEM(i)->sn_name != NULL) { - slash_adjust(SCRIPT_ITEM(i)->sn_name); - } - } -} - -#endif - /// Get a pointer to a script name. Used for ":verbose set". /// Message appended to "Last set from " /// diff --git a/src/nvim/search.c b/src/nvim/search.c index 2140a51d3f..3117fbaa77 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -3048,6 +3048,7 @@ void find_pattern_in_path(char *ptr, Direction dir, size_t len, bool whole, bool FNAME_EXP|FNAME_INCL|FNAME_REL, 1, p_fname, NULL); } + TO_SLASH(new_fname); bool already_searched = false; if (new_fname != NULL) { // Check whether we have already searched in this file diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index ec8f8248e5..4750bcb4fd 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -9,7 +9,7 @@ local describe_lua_and_rpc = n.describe_lua_and_rpc(describe) local api = n.api local fn = n.fn local request = n.request -local exec_lua = n.exec_lua +local exec, exec_lua = n.exec, n.exec_lua local insert = n.insert local NIL = vim.NIL local command = n.command @@ -2800,3 +2800,235 @@ describe('api/buf', function() end) end) end) + +describe('path sep in ffname/sfname #39382,', function() + if not t.is_os('win') then + return + end + + local cwd, files = vim.uv.cwd(), {} + local expected_cwd = vim.fs.normalize(cwd) + for i = 1, 3 do + files[i] = ([[%s\Xtest\file%s]]):format(cwd, i) + end + + local sfname_eq = function(expected, nr) + command(('buffer %s'):format(nr or '')) + eq(expected, api.nvim_eval_statusline('%f', {}).str) + end + + setup(function() + t.mkdir('Xtest') + end) + + teardown(function() + n.rmdir('Xtest') + end) + + before_each(function() + clear() + end) + + it('normalizes startup file arguments', function() + clear { args = files } + for i = 1, 3 do + eq(('%s/Xtest/file%s'):format(expected_cwd, i), api.nvim_buf_get_name(i)) + sfname_eq(('Xtest/file%s'):format(i), i) + end + end) + + it('normalizes :next arguments (FILES flag)', function() + command(('next %s'):format(table.concat(files, ' '))) + for i = 1, 3 do + eq(('%s/Xtest/file%s'):format(expected_cwd, i), api.nvim_buf_get_name(i)) + sfname_eq(('%s/Xtest/file%s'):format(expected_cwd, i), i) + end + end) + + it('normalizes :edit arguments (FILE1 flag)', function() + -- tilde prefix + command([[edit ~\foo\bar]]) + eq(('%s/foo/bar'):format(fn.getenv('HOME')), api.nvim_buf_get_name(0)) + sfname_eq('~/foo/bar') + + -- absolute path + command(('edit %s'):format(files[1])) + eq(('%s/Xtest/file1'):format(expected_cwd), api.nvim_buf_get_name(0)) + sfname_eq(('%s/Xtest/file1'):format(expected_cwd)) + end) + + it('normalizes bufadd() arguments', function() + local n = fn.bufadd(files[1]) + eq(('%s/Xtest/file1'):format(expected_cwd), api.nvim_buf_get_name(n)) + sfname_eq(('%s/Xtest/file1'):format(expected_cwd), n) + end) + + it('normalizes bufnr() arguments', function() + local n = fn.bufnr(files[1], 1) + eq(('%s/Xtest/file1'):format(expected_cwd), api.nvim_buf_get_name(n)) + sfname_eq(('%s/Xtest/file1'):format(expected_cwd), n) + end) + + it('normalizes temp paths, and for :diffpatch', function() + t.write_file('Xtest/patch', [[diff file]]) + t.write_file('Xtest/test.txt', [[input file]]) + command('edit Xtest/test.txt') + exec([[ + fun MyPatch() + call writefile(['output file'], v:fname_out) + endfunc + set patchexpr=MyPatch() + ]]) + exec_lua([[ + vim.api.nvim_create_autocmd('BufAdd', { + pattern = '*', + callback = function(ev) + vim.g.test_file = ev.file + vim.g.test_match = ev.match + end + }) + ]]) + command('diffpatch Xtest/patch') + eq(('%s/Xtest/test.txt.new'):format(expected_cwd), api.nvim_buf_get_name(0)) + sfname_eq('Xtest/test.txt.new') + local tmp = fn.getenv('TMPDIR') + local afile = fn.eval('g:test_file') ---@type string + local amatch = fn.eval('g:test_match') ---@type string + t.matches(tmp, afile, true) + t.not_matches('\\', afile, true) + t.matches(tmp, amatch, true) + t.not_matches('\\', amatch, true) + end) + + it('normalizes :find results', function() + t.write_file('Xtest/file1', '') + command([[find Xtest\file1]]) + eq(('%s/Xtest/file1'):format(expected_cwd), api.nvim_buf_get_name(0)) + sfname_eq('Xtest/file1') + + -- 'findfunc' + exec(([[ + func FindFile(cmdarg, cmdcomplete) + return ['%s'] + endfunc + set findfunc=FindFile + ]]):format(files[2])) + command([[find file2]]) + eq(('%s/Xtest/file2'):format(expected_cwd), api.nvim_buf_get_name(0)) + sfname_eq(('%s/Xtest/file2'):format(expected_cwd)) + end) + + it('normalizes :scriptnames arguments', function() + command(('scriptnames %s'):format(files[1])) + eq(('%s/Xtest/file1'):format(expected_cwd), api.nvim_buf_get_name(0)) + sfname_eq(('%s/Xtest/file1'):format(expected_cwd)) + end) + + it('normalizes gf targets', function() + t.write_file('Xtest/file1', '') + feed(('i%s0gf'):format(files[1])) + eq(('%s/Xtest/file1'):format(expected_cwd), api.nvim_buf_get_name(0)) + sfname_eq(('%s/Xtest/file1'):format(expected_cwd)) + command('bw') + feed('f') + eq(('%s/Xtest/file1'):format(expected_cwd), api.nvim_buf_get_name(0)) + sfname_eq(('%s/Xtest/file1'):format(expected_cwd)) + end) + + it('normalizes macro definition targets and completions', function() + t.mkdir('Xtest/sub') + t.write_file( + 'Xtest/sub/bar.h', + [[ + #define MIN 0; + #define MAX 10; + ]] + ) + t.write_file( + 'Xtest/foo.c', + [[ + #include "sub\bar.h"; + void main() { + int i = MIN; + } + ]] + ) + local screen = Screen.new(30, 10) + command([[set include=^\\s*#\\s*include]]) + command([[set define=^\\s*#\\s*define]]) + command('e Xtest/foo.c') + command('djump MIN') + eq(('%s/Xtest/sub/bar.h'):format(expected_cwd), api.nvim_buf_get_name(0)) + sfname_eq('Xtest/sub/bar.h') + command('bw') + feed('/MINlxxi') + screen:expect({ + any = { + 'MIN Xtest/sub/bar.h', + 'MAX Xtest/sub/bar.h', + }, + }) + end) + + it('normalizes tag targets', function() + t.write_file( + 'Xtest/tags', + [[ + main .\\foo.c /^void main() /;" f typeref:typename:void + main2 $DIR\foo.c /^void main2() /;" f typeref:typename:void + main3 ./foo.c /^void main3() /;" f typeref:typename:void + ]] + ) + t.write_file( + 'Xtest/foo.c', + [[ + void main() {} + void main2() {}; + void main3() {}; + ]] + ) + + command('cd Xtest') + command('tjump main') + eq(('%s/Xtest/foo.c'):format(expected_cwd), api.nvim_buf_get_name(0)) + sfname_eq('.//foo.c') + + command('bw') + fn.setenv('DIR', ([[%s\Xtest]]):format(cwd)) + command('tjump main2') + eq(('%s/Xtest/foo.c'):format(expected_cwd), api.nvim_buf_get_name(0)) + sfname_eq(('%s/Xtest/foo.c'):format(expected_cwd)) + + command('bw') + command('tjump main3') + eq(('%s/Xtest/foo.c'):format(expected_cwd), api.nvim_buf_get_name(0)) + sfname_eq('./foo.c') + end) + + it('normalizes quickfix targets', function() + t.write_file( + 'Xtest/error', + [[ + foo.c + .\Xtest\foo.c(3): error C2143: syntax error: missing ';' before 'return' + ]] + ) + local screen = Screen.new(40, 10) + command('cg Xtest/error') + command('copen') + screen:expect({ + any = './Xtest/foo.c', + }) + feed('') + eq(('%s/Xtest/foo.c'):format(expected_cwd), api.nvim_buf_get_name(0)) + sfname_eq('./Xtest/foo.c') + end) + + it('preserves backslashes in URIs', function() + local exe_path = fn.exepath('cmd') + t.not_matches('/', exe_path, true) + command(('e term://%s'):format(exe_path)) + t.matches(exe_path, api.nvim_buf_get_name(0), true) + t.matches(exe_path, api.nvim_eval_statusline('%f', {}).str, true) + end) +end) diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua index e848df4b63..8c6c5ba1b4 100644 --- a/test/functional/api/server_requests_spec.lua +++ b/test/functional/api/server_requests_spec.lua @@ -9,7 +9,7 @@ local nvim_prog, command, fn = n.nvim_prog, n.command, n.fn local source, next_msg = n.source, n.next_msg local ok = t.ok local api = n.api -local set_session = n.set_session +local new_session, set_session = n.new_session, n.set_session local pcall_err = t.pcall_err local assert_alive = n.assert_alive @@ -328,7 +328,19 @@ describe('server -> client', function() set_session(server) local address = fn.serverlist()[1] local first = string.sub(address, 1, 1) - ok(first == '/' or first == '\\') + ok(first == '/') + connect_test(server, 'pipe', address) + end) + + it('via named pipe containing backslashes #39382', function() + t.skip(not t.is_os('win'), 'N/A for non-Windows') + local address = [[\\.\pipe\Xtest]] + local server = new_session(false, { + args_rm = { '--listen' }, + args = { '--listen', address }, + }) + set_session(server) + eq(vim.fs.normalize(address), api.nvim_get_vvar('servername')) connect_test(server, 'pipe', address) end) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index d081495dab..2702fd17fd 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1793,12 +1793,11 @@ describe('API', function() eq(NIL, api.nvim_get_var('Unknown_script_func')) -- Check if autoload works properly - local pathsep = n.get_pathsep() local xhome = 'Xhome_api' - local xconfig = xhome .. pathsep .. 'Xconfig' - local xdata = xhome .. pathsep .. 'Xdata' - local autoload_folder = table.concat({ xconfig, 'nvim', 'autoload' }, pathsep) - local autoload_file = table.concat({ autoload_folder, 'testload.vim' }, pathsep) + local xconfig = ('%s/Xconfig'):format(xhome) + local xdata = ('%s/Xdata'):format(xhome) + local autoload_folder = ('%s/nvim/autoload'):format(xconfig) + local autoload_file = ('%s/testload.vim'):format(autoload_folder) mkdir_p(autoload_folder) write_file(autoload_file, [[let testload#value = 2]]) @@ -2845,7 +2844,7 @@ describe('API', function() filter(map(add( getjumplist()[0], { 'bufnr': bufnr('%'), 'lnum': getcurpos()[1] }), 'filter( - { "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum }, + { "f": expand("#".v:val.bufnr.":p:gs?\\?/?"), "l": v:val.lnum }, { k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)') ]]):gsub('\n', ''))), @@ -3414,9 +3413,8 @@ describe('API', function() local test_dir = 'Xtest_list_runtime_paths' setup(function() - local pathsep = n.get_pathsep() - mkdir_p(test_dir .. pathsep .. 'a') - mkdir_p(test_dir .. pathsep .. 'b') + mkdir_p(('%s/a'):format(test_dir)) + mkdir_p(('%s/b'):format(test_dir)) end) teardown(function() rmdir(test_dir) @@ -3934,7 +3932,6 @@ describe('API', function() end) describe('nvim_get_runtime_file', function() - local p = t.fix_slashes it('can find files', function() eq({}, api.nvim_get_runtime_file('bork.borkbork', false)) eq({}, api.nvim_get_runtime_file('bork.borkbork', true)) @@ -3943,36 +3940,36 @@ describe('API', function() local val = api.nvim_get_runtime_file('autoload/remote/*.vim', true) eq(2, #val) if endswith(val[1], 'define.vim') then - ok(endswith(p(val[1]), 'autoload/remote/define.vim')) - ok(endswith(p(val[2]), 'autoload/remote/host.vim')) + ok(endswith(val[1], 'autoload/remote/define.vim')) + ok(endswith(val[2], 'autoload/remote/host.vim')) else - ok(endswith(p(val[1]), 'autoload/remote/host.vim')) - ok(endswith(p(val[2]), 'autoload/remote/define.vim')) + ok(endswith(val[1], 'autoload/remote/host.vim')) + ok(endswith(val[2], 'autoload/remote/define.vim')) end val = api.nvim_get_runtime_file('autoload/remote/*.vim', false) eq(1, #val) ok( - endswith(p(val[1]), 'autoload/remote/define.vim') - or endswith(p(val[1]), 'autoload/remote/host.vim') + endswith(val[1], 'autoload/remote/define.vim') + or endswith(val[1], 'autoload/remote/host.vim') ) val = api.nvim_get_runtime_file('lua', true) eq(1, #val) - ok(endswith(p(val[1]), 'lua')) + ok(endswith(val[1], 'lua')) val = api.nvim_get_runtime_file('lua/vim', true) eq(1, #val) - ok(endswith(p(val[1]), 'lua/vim')) + ok(endswith(val[1], 'lua/vim')) end) it('can find directories', function() local val = api.nvim_get_runtime_file('lua/', true) eq(1, #val) - ok(endswith(p(val[1]), 'lua/')) + ok(endswith(val[1], 'lua/')) val = api.nvim_get_runtime_file('lua/vim/', true) eq(1, #val) - ok(endswith(p(val[1]), 'lua/vim/')) + ok(endswith(val[1], 'lua/vim/')) eq({}, api.nvim_get_runtime_file('foobarlang/', true)) end) @@ -3993,8 +3990,8 @@ describe('API', function() local val = api.nvim_get_runtime_file('vim.vim', true) eq(2, #val) - eq(p(val[1]), vimruntime .. '/syntax/vim.vim') - eq(p(val[2]), vimruntime .. '/ftplugin/vim.vim') + eq(val[1], ('%s/syntax/vim.vim'):format(vimruntime)) + eq(val[2], ('%s/ftplugin/vim.vim'):format(vimruntime)) end) it('finds files via an 8.3 filename path #25019', function() @@ -4006,8 +4003,8 @@ describe('API', function() finally(function() rmdir(path) end) - local path_with_shortname = - p(fn.system(('for %%I in ("%s") do @echo %%~sI'):format(path), ''):gsub('\n', '')) + local path_with_shortname = fn.system(('for %%I in ("%s") do @echo %%~sI'):format(path), '') + path_with_shortname = vim.fs.normalize(vim.trim(path_with_shortname)) eq('XTEST_~1', vim.fs.basename(path_with_shortname)) exec_lua(('vim.opt.rtp:prepend("%s/*")'):format(path_with_shortname)) local val = api.nvim_get_runtime_file('lua/foo.lua', true) diff --git a/test/functional/autocmd/autocmd_spec.lua b/test/functional/autocmd/autocmd_spec.lua index 215dc3708f..9fc529992a 100644 --- a/test/functional/autocmd/autocmd_spec.lua +++ b/test/functional/autocmd/autocmd_spec.lua @@ -855,4 +855,17 @@ describe('autocmd', function() fn.execute('autocmd User ,,,there,is,,a,fly,,') ) end) + + it('normalizes path sep (slashes) in env var `pattern` #39382', function() + local path = t.is_os('win') and [[C:\foo\bar]] or 'C:/foo/bar' + fn.setenv('FOOBAR', path) + exec [[ + autocmd User ~,$FOOBAR " + ]] + local cmds = exec_lua(function() + return vim.api.nvim_get_autocmds({ event = 'User' }) + end) + eq(vim.fs.normalize('~'), cmds[1].pattern) + eq(vim.fs.normalize(path), cmds[2].pattern) + end) end) diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua index 90affba31b..3041b82abb 100644 --- a/test/functional/core/main_spec.lua +++ b/test/functional/core/main_spec.lua @@ -178,28 +178,6 @@ describe('command-line option', function() matches('Run "nvim %-V1 %-v"', n.spawn_wait('-v').stdout) matches('fall%-back for %$VIM: .*Run :checkhealth', n.spawn_wait('-V1', '-v').stdout) end) - - if is_os('win') then - for _, prefix in ipairs({ '~/', '~\\' }) do - it('expands ' .. prefix .. ' on Windows', function() - local fname = os.getenv('USERPROFILE') .. '\\nvim_test.txt' - finally(function() - os.remove(fname) - end) - write_file(fname, 'some text') - eq( - 'some text', - fn.system({ - n.nvim_prog, - '-es', - '+%print', - '+q', - prefix .. 'nvim_test.txt', - }):gsub('\n', '') - ) - end) - end - end end) describe('vim._core', function() diff --git a/test/functional/core/path_spec.lua b/test/functional/core/path_spec.lua index 2b076e1ea9..68fb1dfecb 100644 --- a/test/functional/core/path_spec.lua +++ b/test/functional/core/path_spec.lua @@ -12,6 +12,7 @@ local is_os = t.is_os local mkdir = t.mkdir local rmdir = n.rmdir local write_file = t.write_file +local api = n.api local function join_path(...) return table.concat({ ... }, '/') @@ -69,6 +70,64 @@ describe('path', function() end) end) +describe('startup', function() + local home, foo, bar = vim.fs.normalize('~'), 'Xtest-foo.lua', 'Xtest-bar.lua' + + setup(function() + write_file(('%s/%s'):format(home, foo), 'local foo = 1;') + write_file(('%s/%s'):format(home, bar), '') + end) + + teardown(function() + os.remove(('%s/%s'):format(home, foo)) + os.remove(('%s/%s'):format(home, bar)) + end) + + it('expands tilde-prefixed paths #29380', function() + t.skip(not is_os('win'), 'N/A: shell expands ~ on Unix') + clear { + args = { ('~/%s'):format(foo), ('~\\%s'):format(bar) }, + } + local expected = { ('%s/%s'):format(home, foo), ('%s/%s'):format(home, bar) } + eq(expected, vim.tbl_map(fn.bufname, api.nvim_list_bufs())) + eq(expected, fn.argv()) + end) + + it('normalizes v:progpath #39382', function() + clear() + eq( + n.nvim_prog, + fn.system({ + is_os('win') and n.nvim_prog:gsub('/', '\\') or n.nvim_prog, + '--clean', + '--headless', + '+echo v:progpath', + '+q', + }) + ) + end) + + it('normalizes -u arguments and triggers autocmd events #39382', function() + local file = ('%s/%s'):format(home, foo) + if is_os('win') then + file = file:gsub('/', '\\') + end + clear { + args_rm = { '--cmd', '-u' }, + args = { + '--cmd', + ('autocmd SourcePre */%s let g:matched = 1'):format(foo), + '-u', + file, + }, + } + local scripts = fn.getscriptinfo({ name = foo }) + eq(1, #scripts) + eq(('%s/%s'):format(home, foo), scripts[1].name) + t.ok(fn.eval('g:matched')) + end) +end) + describe('expand wildcard', function() before_each(clear) @@ -82,9 +141,9 @@ describe('expand wildcard', function() } for _, folder in ipairs(folders) do mkdir(folder) - local file = join_path(folder, 'file.txt') + local file = ('%s%sfile.txt'):format(folder, n.get_pathsep()) write_file(file, '') - eq(file, eval('expand("' .. folder .. '/*")')) + eq(file, fn.expand(('%s/*'):format(folder))) rmdir(folder) end end) diff --git a/test/functional/core/server_spec.lua b/test/functional/core/server_spec.lua index efe5dcc689..dd136cdaaa 100644 --- a/test/functional/core/server_spec.lua +++ b/test/functional/core/server_spec.lua @@ -87,7 +87,7 @@ describe('server', function() -- v:servername and $NVIM take the next available server. local servername = ( - is_os('win') and [[\\.\pipe\Xtest-functional-server-pipe]] + is_os('win') and [[//./pipe/Xtest-functional-server-pipe]] or './Xtest-functional-server-socket' ) fn.serverstart(servername) @@ -170,7 +170,7 @@ describe('server', function() -- Add some servers. local servs = ( - is_os('win') and { [[\\.\pipe\Xtest-pipe0934]], [[\\.\pipe\Xtest-pipe4324]] } + is_os('win') and { [[//./pipe/Xtest-pipe0934]], [[//./pipe/Xtest-pipe4324]] } or { [[./Xtest-pipe0934]], [[./Xtest-pipe4324]] } ) for _, s in ipairs(servs) do @@ -316,6 +316,40 @@ describe('server', function() matches('Failed.*listen', result.stderr) fn.serverstop(socket_path) end) + + it('normalizes sep in named pipe paths #39382', function() + t.skip(not is_os('win'), 'N/A: Named pipe is Windows feature') + + local name, named_pipe = 'Xtest-server', [[\\.\pipe\Xtest-server]] + -- default address + clear { args_rm = { '--listen' } } + matches('//./pipe/', fn.eval('v:servername'), true) + + clear({ args_rm = { '--listen' }, env = { NVIM_LISTEN_ADDRESS = named_pipe } }) + eq(vim.fs.normalize(named_pipe), fn.eval('v:servername')) + + clear({ args_rm = { '--listen' }, args = { '--listen', name } }) + matches(vim.fs.normalize(named_pipe), fn.eval('v:servername'), true) + + clear({ args_rm = { '--listen' }, args = { '--listen', named_pipe } }) + eq(vim.fs.normalize(named_pipe), fn.eval('v:servername')) + + local server = n.get_session() + local client = n.new_session(true) + n.set_session(client) + local res = fn.system({ + n.nvim_prog, + '--clean', + '-es', + '--server', + named_pipe, + '--remote-expr', + 'v:servername', + }) + eq(vim.fs.normalize(named_pipe), res) + client:close() + n.set_session(server) + end) end) describe('startup --listen', function() @@ -400,7 +434,7 @@ describe('startup --listen', function() end) it('sets v:servername, overrides $NVIM_LISTEN_ADDRESS', function() - local addr = (is_os('win') and [[\\.\pipe\Xtest-listen-pipe]] or './Xtest-listen-pipe') + local addr = (is_os('win') and [[//./pipe/Xtest-listen-pipe]] or './Xtest-listen-pipe') clear({ env = { NVIM_LISTEN_ADDRESS = './Xtest-env-pipe' }, args = { '--listen', addr } }) eq('', eval('$NVIM_LISTEN_ADDRESS')) -- Cleared on startup. eq(addr, api.nvim_get_vvar('servername')) diff --git a/test/functional/core/spellfile_spec.lua b/test/functional/core/spellfile_spec.lua index f06eb76ad1..42e83c2969 100644 --- a/test/functional/core/spellfile_spec.lua +++ b/test/functional/core/spellfile_spec.lua @@ -127,8 +127,8 @@ describe('spellfile', function() n.insert('abc') n.feed('zg') eq( - t.fix_slashes(fn.stdpath('data') .. '/site/spell/en.utf-8.add'), - t.fix_slashes(api.nvim_get_option_value('spellfile', {})) + ('%s/site/spell/en.utf-8.add'):format(fn.stdpath('data')), + api.nvim_get_option_value('spellfile', {}) ) end) diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index cb7a7a1432..982a7859b7 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -39,16 +39,10 @@ local testlog = 'Xtest-startupspec-log' describe('startup', function() it('--clean', function() clear() - matches( - vim.pesc(t.fix_slashes(fn.stdpath('config'))), - t.fix_slashes(api.nvim_get_option_value('runtimepath', {})) - ) + matches(vim.pesc(fn.stdpath('config')), api.nvim_get_option_value('runtimepath', {})) clear('--clean') - ok( - not t.fix_slashes(api.nvim_get_option_value('runtimepath', {})) - :match(vim.pesc(t.fix_slashes(fn.stdpath('config')))) - ) + ok(not api.nvim_get_option_value('runtimepath', {}):match(vim.pesc(fn.stdpath('config')))) end) it('prevents remote UI infinite loop', function() @@ -1280,7 +1274,6 @@ describe('sysinit', function() local xdgdir = 'Xxdg' local vimdir = 'Xvim' local xhome = 'Xhome' - local pathsep = n.get_pathsep() before_each(function() rmdir(xdgdir) @@ -1288,9 +1281,9 @@ describe('sysinit', function() rmdir(xhome) mkdir(xdgdir) - mkdir(xdgdir .. pathsep .. 'nvim') + mkdir(('%s/nvim'):format(xdgdir)) write_file( - table.concat({ xdgdir, 'nvim', 'sysinit.vim' }, pathsep), + ('%s/nvim/sysinit.vim'):format(xdgdir), [[ let g:loaded = get(g:, "loaded", 0) + 1 let g:xdg = 1 @@ -1299,7 +1292,7 @@ describe('sysinit', function() mkdir(vimdir) write_file( - table.concat({ vimdir, 'sysinit.vim' }, pathsep), + ('%s/sysinit.vim'):format(vimdir), [[ let g:loaded = get(g:, "loaded", 0) + 1 let g:vim = 1 @@ -1340,11 +1333,8 @@ describe('sysinit', function() it('respects NVIM_APPNAME in XDG_CONFIG_DIRS', function() local appname = 'mysysinitapp' - mkdir(xdgdir .. pathsep .. appname) - write_file( - table.concat({ xdgdir, appname, 'sysinit.vim' }, pathsep), - [[let g:appname_sysinit = 1]] - ) + mkdir(('%s/%s'):format(xdgdir, appname)) + write_file(('%s/%s/sysinit.vim'):format(xdgdir, appname), [[let g:appname_sysinit = 1]]) clear { args_rm = { '-u' }, env = { HOME = xhome, XDG_CONFIG_DIRS = xdgdir, NVIM_APPNAME = appname }, @@ -1357,16 +1347,15 @@ end) describe('user config init', function() local xhome = 'Xhome' - local pathsep = n.get_pathsep() - local xconfig = xhome .. pathsep .. 'Xconfig' - local xdata = xhome .. pathsep .. 'Xdata' - local init_lua_path = table.concat({ xconfig, 'nvim', 'init.lua' }, pathsep) + local xconfig = ('%s/Xconfig'):format(xhome) + local xdata = ('%s/Xdata'):format(xhome) + local init_lua_path = ('%s/nvim/init.lua'):format(xconfig) local xenv = { XDG_CONFIG_HOME = xconfig, XDG_DATA_HOME = xdata } before_each(function() rmdir(xhome) - mkdir_p(xconfig .. pathsep .. 'nvim') + mkdir_p(('%s/nvim'):format(xconfig)) mkdir_p(xdata) write_file(init_lua_path, [[vim.g.lua_rc = 1]]) @@ -1430,7 +1419,7 @@ describe('user config init', function() vim.g.exrc_file = '---' ]] ) - mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim')) + mkdir_p(('%s/%s'):format(xstate, is_os('win') and 'nvim-data' or 'nvim')) end) after_each(function() @@ -1556,7 +1545,7 @@ describe('user config init', function() end) describe('with explicitly provided config', function() - local custom_lua_path = table.concat({ xhome, 'custom.lua' }, pathsep) + local custom_lua_path = ('%s/custom.lua'):format(xhome) before_each(function() write_file( custom_lua_path, @@ -1576,7 +1565,7 @@ describe('user config init', function() describe('VIMRC also exists', function() before_each(function() write_file( - table.concat({ xconfig, 'nvim', 'init.vim' }, pathsep), + ('%s/nvim/init.vim'):format(xconfig), [[ let g:vim_rc = 1 ]] @@ -1600,7 +1589,7 @@ describe('user config init', function() -- Remove init.lua from XDG_CONFIG_HOME so nvim falls back to XDG_CONFIG_DIRS os.remove(init_lua_path) rmdir(xdgdir) - mkdir_p(xdgdir .. pathsep .. 'nvim') + mkdir_p(('%s/nvim'):format(xdgdir)) end) after_each(function() @@ -1608,24 +1597,18 @@ describe('user config init', function() end) it('loads init.lua from XDG_CONFIG_DIRS when no config in XDG_CONFIG_HOME', function() - write_file( - table.concat({ xdgdir, 'nvim', 'init.lua' }, pathsep), - [[vim.g.xdg_config_dirs_lua = 1]] - ) + write_file(('%s/nvim/init.lua'):format(xdgdir), [[vim.g.xdg_config_dirs_lua = 1]]) clear { args_rm = { '-u' }, env = { XDG_CONFIG_HOME = xconfig, XDG_DATA_HOME = xdata, XDG_CONFIG_DIRS = xdgdir }, } eq(1, eval('g:xdg_config_dirs_lua')) - eq( - fn.fnamemodify(table.concat({ xdgdir, 'nvim', 'init.lua' }, pathsep), ':p'), - eval('$MYVIMRC') - ) + eq(fn.fnamemodify(('%s/nvim/init.lua'):format(xdgdir), ':p'), eval('$MYVIMRC')) end) it('prefers init.lua over init.vim, shows E5422', function() - write_file(table.concat({ xdgdir, 'nvim', 'init.lua' }, pathsep), [[vim.g.xdg_lua = 1]]) - write_file(table.concat({ xdgdir, 'nvim', 'init.vim' }, pathsep), [[let g:xdg_vim = 1]]) + write_file(('%s/nvim/init.lua'):format(xdgdir), [[vim.g.xdg_lua = 1]]) + write_file(('%s/nvim/init.vim'):format(xdgdir), [[let g:xdg_vim = 1]]) clear { args_rm = { '-u' }, env = { XDG_CONFIG_HOME = xconfig, XDG_DATA_HOME = xdata, XDG_CONFIG_DIRS = xdgdir }, @@ -1636,7 +1619,7 @@ describe('user config init', function() end) it('falls back to init.vim when no init.lua', function() - write_file(table.concat({ xdgdir, 'nvim', 'init.vim' }, pathsep), [[let g:xdg_vim = 1]]) + write_file(('%s/nvim/init.vim'):format(xdgdir), [[let g:xdg_vim = 1]]) clear { args_rm = { '-u' }, env = { XDG_CONFIG_HOME = xconfig, XDG_DATA_HOME = xdata, XDG_CONFIG_DIRS = xdgdir }, @@ -1646,10 +1629,10 @@ describe('user config init', function() it('respects NVIM_APPNAME', function() local appname = 'mytestapp' - mkdir_p(xdgdir .. pathsep .. appname) + mkdir_p(('%s/%s'):format(xdgdir, appname)) -- Also create nvim/ with a config that should NOT be loaded - write_file(table.concat({ xdgdir, 'nvim', 'init.lua' }, pathsep), [[vim.g.wrong = 1]]) - write_file(table.concat({ xdgdir, appname, 'init.lua' }, pathsep), [[vim.g.appname_lua = 1]]) + write_file(('%s/nvim/init.lua'):format(xdgdir), [[vim.g.wrong = 1]]) + write_file(('%s/%s/init.lua'):format(xdgdir, appname), [[vim.g.appname_lua = 1]]) clear { args_rm = { '-u' }, env = { @@ -1661,24 +1644,20 @@ describe('user config init', function() } eq(1, eval('g:appname_lua')) eq(0, eval('get(g:, "wrong", 0)')) - eq( - fn.fnamemodify(table.concat({ xdgdir, appname, 'init.lua' }, pathsep), ':p'), - eval('$MYVIMRC') - ) + eq(fn.fnamemodify(('%s/%s/init.lua'):format(xdgdir, appname), ':p'), eval('$MYVIMRC')) end) end) end) describe('runtime:', function() local xhome = 'Xhome' - local pathsep = '/' - local xconfig = xhome .. pathsep .. 'Xconfig' - local xdata = xhome .. pathsep .. 'Xdata' + local xconfig = ('%s/Xconfig'):format(xhome) + local xdata = ('%s/Xdata'):format(xhome) local xenv = { XDG_CONFIG_HOME = xconfig, XDG_DATA_HOME = xdata } setup(function() rmdir(xhome) - mkdir_p(xconfig .. pathsep .. 'nvim') + mkdir_p(('%s/nvim'):format(xconfig)) mkdir_p(xdata) end) @@ -1687,8 +1666,8 @@ describe('runtime:', function() end) it('loads plugin/*.lua from XDG config home', function() - local plugin_folder_path = table.concat({ xconfig, 'nvim', 'plugin' }, pathsep) - local plugin_file_path = table.concat({ plugin_folder_path, 'plugin.lua' }, pathsep) + local plugin_folder_path = ('%s/nvim/plugin'):format(xconfig) + local plugin_file_path = ('%s/plugin.lua'):format(plugin_folder_path) mkdir_p(plugin_folder_path) finally(function() rmdir(plugin_folder_path) @@ -1701,10 +1680,9 @@ describe('runtime:', function() end) it('loads plugin/*.lua from start packages', function() - local plugin_path = - table.concat({ xconfig, 'nvim', 'pack', 'category', 'start', 'test_plugin' }, pathsep) - local plugin_folder_path = table.concat({ plugin_path, 'plugin' }, pathsep) - local plugin_file_path = table.concat({ plugin_folder_path, 'plugin.lua' }, pathsep) + local plugin_path = ('%s/nvim/pack/category/start/test_plugin'):format(xconfig) + local plugin_folder_path = ('%s/plugin'):format(plugin_path) + local plugin_file_path = ('%s/plugin.lua'):format(plugin_folder_path) local profiler_file = 'test_startuptime.log' mkdir_p(plugin_folder_path) finally(function() @@ -1734,12 +1712,11 @@ describe('runtime:', function() it('loads plugin/*.lua from site packages', function() local nvimdata = is_os('win') and 'nvim-data' or 'nvim' - local plugin_path = - table.concat({ xdata, nvimdata, 'site', 'pack', 'xa', 'start', 'yb' }, pathsep) - local plugin_folder_path = table.concat({ plugin_path, 'plugin' }, pathsep) - local plugin_after_path = table.concat({ plugin_path, 'after', 'plugin' }, pathsep) - local plugin_file_path = table.concat({ plugin_folder_path, 'plugin.lua' }, pathsep) - local plugin_after_file_path = table.concat({ plugin_after_path, 'helloo.lua' }, pathsep) + local plugin_path = ('%s/%s/site/pack/xa/start/yb'):format(xdata, nvimdata) + local plugin_folder_path = ('%s/plugin'):format(plugin_path) + local plugin_after_path = ('%s/after/plugin'):format(plugin_path) + local plugin_file_path = ('%s/plugin.lua'):format(plugin_folder_path) + local plugin_after_file_path = ('%s/helloo.lua'):format(plugin_after_path) mkdir_p(plugin_folder_path) mkdir_p(plugin_after_path) finally(function() @@ -1755,21 +1732,21 @@ describe('runtime:', function() end) it('no crash setting &rtp in plugins with :packloadall called before #18315', function() - local plugin_folder_path = table.concat({ xconfig, 'nvim', 'plugin' }, pathsep) + local plugin_folder_path = ('%s/nvim/plugin'):format(xconfig) mkdir_p(plugin_folder_path) finally(function() rmdir(plugin_folder_path) end) write_file( - table.concat({ plugin_folder_path, 'plugin.vim' }, pathsep), + ('%s/plugin.vim'):format(plugin_folder_path), [[ let &runtimepath = &runtimepath let g:vim_plugin = 1 ]] ) write_file( - table.concat({ plugin_folder_path, 'plugin.lua' }, pathsep), + ('%s/plugin.lua'):format(plugin_folder_path), [[ vim.o.runtimepath = vim.o.runtimepath vim.g.lua_plugin = 1 @@ -1783,29 +1760,29 @@ describe('runtime:', function() end) it("loads ftdetect/*.{vim,lua} respecting 'rtp' order", function() - local rtp_folder = table.concat({ xconfig, 'nvim' }, pathsep) - local after_rtp_folder = table.concat({ rtp_folder, 'after' }, pathsep) - local ftdetect_folder = table.concat({ rtp_folder, 'ftdetect' }, pathsep) - local after_ftdetect_folder = table.concat({ after_rtp_folder, 'ftdetect' }, pathsep) + local rtp_folder = ('%s/nvim'):format(xconfig) + local after_rtp_folder = ('%s/after'):format(rtp_folder) + local ftdetect_folder = ('%s/ftdetect'):format(rtp_folder) + local after_ftdetect_folder = ('%s/ftdetect'):format(after_rtp_folder) mkdir_p(ftdetect_folder) mkdir_p(after_ftdetect_folder) finally(function() rmdir(ftdetect_folder) rmdir(after_ftdetect_folder) end) - write_file(table.concat({ rtp_folder, 'scripts.vim' }, pathsep), [[let g:aseq ..= 'S']]) - write_file(table.concat({ after_rtp_folder, 'scripts.vim' }, pathsep), [[let g:aseq ..= 's']]) + write_file(('%s/scripts.vim'):format(rtp_folder), [[let g:aseq ..= 'S']]) + write_file(('%s/scripts.vim'):format(after_rtp_folder), [[let g:aseq ..= 's']]) -- A .lua file is loaded after a .vim file if they only differ in extension. -- All files in after/ftdetect/ are loaded after all files in ftdetect/. write_file( - table.concat({ ftdetect_folder, 'new-ft.vim' }, pathsep), + ('%s/new-ft.vim'):format(ftdetect_folder), [[ let g:seq ..= 'A' autocmd BufRead,BufNewFile FTDETECT let g:aseq ..= 'A' ]] ) write_file( - table.concat({ ftdetect_folder, 'new-ft.lua' }, pathsep), + ('%s/new-ft.lua'):format(ftdetect_folder), [[ vim.g.seq = vim.g.seq .. 'B' vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile' }, { @@ -1815,14 +1792,14 @@ describe('runtime:', function() ]] ) write_file( - table.concat({ after_ftdetect_folder, 'new-ft.vim' }, pathsep), + ('%s/new-ft.vim'):format(after_ftdetect_folder), [[ let g:seq ..= 'a' autocmd BufRead,BufNewFile FTDETECT let g:aseq ..= 'a' ]] ) write_file( - table.concat({ after_ftdetect_folder, 'new-ft.lua' }, pathsep), + ('%s/new-ft.lua'):format(after_ftdetect_folder), [[ vim.g.seq = vim.g.seq .. 'b' vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile' }, { @@ -1853,8 +1830,7 @@ end) describe('user session', function() local xhome = 'Xhome' - local pathsep = n.get_pathsep() - local session_file = table.concat({ xhome, 'session.lua' }, pathsep) + local session_file = ('%s/session.lua'):format(xhome) before_each(function() rmdir(xhome) diff --git a/test/functional/editor/completion_spec.lua b/test/functional/editor/completion_spec.lua index a75cc33697..22336ae165 100644 --- a/test/functional/editor/completion_spec.lua +++ b/test/functional/editor/completion_spec.lua @@ -1851,4 +1851,18 @@ describe('completion', function() {5:-- File name completion (^F^N^P) }{9:Pattern not found} | ]]) end) + + it('returns all entries in the directory #25791', function() + n.mkdir_p('Xtest/sub1') + n.mkdir_p('Xtest/sub2') + t.write_file('Xtest/A', '') + t.write_file('Xtest/sub1/B', '') + t.write_file('Xtest/sub2/C', '') + finally(function() + n.rmdir('Xtest') + end) + local path = ('%s/Xtest/**'):format(vim.fs.normalize(vim.uv.cwd())) + command(('set path=%s'):format(path)) + eq({ 'A', 'B', 'C', 'sub1/', 'sub2/' }, fn.getcompletion('find ', 'cmdline')) + end) end) diff --git a/test/functional/editor/runtime_spec.lua b/test/functional/editor/runtime_spec.lua index ce46d25a54..c3c2055f45 100644 --- a/test/functional/editor/runtime_spec.lua +++ b/test/functional/editor/runtime_spec.lua @@ -13,7 +13,6 @@ local write_file = t.write_file describe('runtime:', function() local plug_dir = 'Test_Plugin' - local sep = n.get_pathsep() local init = 'dummy_init.lua' -- All test cases below use the same Nvim instance. @@ -46,13 +45,13 @@ describe('runtime:', function() end) describe('colors', function() - local colorscheme_folder = plug_dir .. sep .. 'colors' + local colorscheme_folder = ('%s/colors'):format(plug_dir) before_each(function() mkdir_p(colorscheme_folder) end) it('Lua colorschemes work and are included in cmdline completion', function() - local colorscheme_file = table.concat({ colorscheme_folder, 'new_colorscheme.lua' }, sep) + local colorscheme_file = ('%s/new_colorscheme.lua'):format(colorscheme_folder) write_file(colorscheme_file, [[vim.g.lua_colorscheme = 1]]) eq({ 'new_colorscheme' }, fn.getcompletion('new_c', 'color')) @@ -71,51 +70,51 @@ describe('runtime:', function() end) exec('set pp+=' .. pack_dir) - local pack_opt_dir = table.concat({ pack_dir, 'pack', 'some_name', 'opt' }, sep) - local colors_opt_dir = table.concat({ pack_opt_dir, 'some_pack', 'colors' }, sep) + local pack_opt_dir = ('%s/pack/some_name/opt'):format(pack_dir) + local colors_opt_dir = ('%s/some_pack/colors'):format(pack_opt_dir) mkdir_p(colors_opt_dir) - local after_colorscheme_folder = table.concat({ plug_dir, 'after', 'colors' }, sep) + local after_colorscheme_folder = ('%s/after/colors'):format(plug_dir) mkdir_p(after_colorscheme_folder) exec('set rtp+=' .. plug_dir .. '/after') write_file( - table.concat({ colors_opt_dir, 'new_colorscheme.lua' }, sep), + ('%s/new_colorscheme.lua'):format(colors_opt_dir), [[vim.g.colorscheme = 'lua_pp']] ) exec('colorscheme new_colorscheme') eq('lua_pp', eval('g:colorscheme')) write_file( - table.concat({ colors_opt_dir, 'new_colorscheme.vim' }, sep), + ('%s/new_colorscheme.vim'):format(colors_opt_dir), [[let g:colorscheme = 'vim_pp']] ) exec('colorscheme new_colorscheme') eq('vim_pp', eval('g:colorscheme')) write_file( - table.concat({ after_colorscheme_folder, 'new_colorscheme.lua' }, sep), + ('%s/new_colorscheme.lua'):format(after_colorscheme_folder), [[vim.g.colorscheme = 'lua_rtp_after']] ) exec('colorscheme new_colorscheme') eq('lua_rtp_after', eval('g:colorscheme')) write_file( - table.concat({ after_colorscheme_folder, 'new_colorscheme.vim' }, sep), + ('%s/new_colorscheme.vim'):format(after_colorscheme_folder), [[let g:colorscheme = 'vim_rtp_after']] ) exec('colorscheme new_colorscheme') eq('vim_rtp_after', eval('g:colorscheme')) write_file( - table.concat({ colorscheme_folder, 'new_colorscheme.lua' }, sep), + ('%s/new_colorscheme.lua'):format(colorscheme_folder), [[vim.g.colorscheme = 'lua_rtp']] ) exec('colorscheme new_colorscheme') eq('lua_rtp', eval('g:colorscheme')) write_file( - table.concat({ colorscheme_folder, 'new_colorscheme.vim' }, sep), + ('%s/new_colorscheme.vim'):format(colorscheme_folder), [[let g:colorscheme = 'vim_rtp']] ) exec('colorscheme new_colorscheme') @@ -124,13 +123,13 @@ describe('runtime:', function() end) describe('compiler', function() - local compiler_folder = table.concat({ plug_dir, 'compiler' }, sep) + local compiler_folder = ('%s/compiler'):format(plug_dir) before_each(function() mkdir_p(compiler_folder) end) it('Lua compilers work and are included in cmdline completion', function() - local compiler_file = compiler_folder .. sep .. 'new_compiler.lua' + local compiler_file = ('%s/new_compiler.lua'):format(compiler_folder) write_file(compiler_file, [[vim.b.lua_compiler = 1]]) eq({ 'new_compiler' }, fn.getcompletion('new_c', 'compiler')) @@ -142,24 +141,18 @@ describe('runtime:', function() end) it("'rtp' order is respected", function() - local after_compiler_folder = table.concat({ plug_dir, 'after', 'compiler' }, sep) - mkdir_p(table.concat({ compiler_folder, 'new_compiler' }, sep)) - mkdir_p(table.concat({ after_compiler_folder, 'new_compiler' }, sep)) + local after_compiler_folder = ('%s/after/compiler'):format(plug_dir) + mkdir_p(('%s/new_compiler'):format(compiler_folder)) + mkdir_p(('%s/new_compiler'):format(after_compiler_folder)) exec('set rtp+=' .. plug_dir .. '/after') exec('let g:seq = ""') -- A .lua file is loaded after a .vim file if they only differ in extension. -- All files in after/compiler/ are loaded after all files in compiler/. - write_file(table.concat({ compiler_folder, 'new_compiler.vim' }, sep), [[let g:seq ..= 'A']]) + write_file(('%s/new_compiler.vim'):format(compiler_folder), [[let g:seq ..= 'A']]) + write_file(('%s/new_compiler.lua'):format(compiler_folder), [[vim.g.seq = vim.g.seq .. 'B']]) + write_file(('%s/new_compiler.vim'):format(after_compiler_folder), [[let g:seq ..= 'a']]) write_file( - table.concat({ compiler_folder, 'new_compiler.lua' }, sep), - [[vim.g.seq = vim.g.seq .. 'B']] - ) - write_file( - table.concat({ after_compiler_folder, 'new_compiler.vim' }, sep), - [[let g:seq ..= 'a']] - ) - write_file( - table.concat({ after_compiler_folder, 'new_compiler.lua' }, sep), + ('%s/new_compiler.lua'):format(after_compiler_folder), [[vim.g.seq = vim.g.seq .. 'b']] ) exec('compiler new_compiler') @@ -168,11 +161,11 @@ describe('runtime:', function() end) describe('ftplugin', function() - local ftplugin_folder = table.concat({ plug_dir, 'ftplugin' }, sep) + local ftplugin_folder = ('%s/ftplugin'):format(plug_dir) it('Lua ftplugins work and are included in cmdline completion', function() mkdir_p(ftplugin_folder) - local ftplugin_file = table.concat({ ftplugin_folder, 'new-ft.lua' }, sep) + local ftplugin_file = ('%s/new-ft.lua'):format(ftplugin_folder) write_file(ftplugin_file, [[vim.b.lua_ftplugin = 1]]) eq({ 'new-ft' }, fn.getcompletion('new-f', 'filetype')) @@ -183,47 +176,29 @@ describe('runtime:', function() end) it("'rtp' order is respected", function() - local after_ftplugin_folder = table.concat({ plug_dir, 'after', 'ftplugin' }, sep) - mkdir_p(table.concat({ ftplugin_folder, 'new-ft' }, sep)) - mkdir_p(table.concat({ after_ftplugin_folder, 'new-ft' }, sep)) + local after_ftplugin_folder = ('%s/after/ftplugin'):format(plug_dir) + mkdir_p(('%s/new-ft'):format(ftplugin_folder)) + mkdir_p(('%s/new-ft'):format(after_ftplugin_folder)) exec('set rtp+=' .. plug_dir .. '/after') exec('let g:seq = ""') -- A .lua file is loaded after a .vim file if they only differ in extension. -- All files in after/ftplugin/ are loaded after all files in ftplugin/. - write_file(table.concat({ ftplugin_folder, 'new-ft.vim' }, sep), [[let g:seq ..= 'A']]) + write_file(('%s/new-ft.vim'):format(ftplugin_folder), [[let g:seq ..= 'A']]) + write_file(('%s/new-ft.lua'):format(ftplugin_folder), [[vim.g.seq = vim.g.seq .. 'B']]) + write_file(('%s/new-ft_a.vim'):format(ftplugin_folder), [[let g:seq ..= 'C']]) + write_file(('%s/new-ft_a.lua'):format(ftplugin_folder), [[vim.g.seq = vim.g.seq .. 'D']]) + write_file(('%s/new-ft/a.vim'):format(ftplugin_folder), [[let g:seq ..= 'E']]) + write_file(('%s/new-ft/a.lua'):format(ftplugin_folder), [[vim.g.seq = vim.g.seq .. 'F']]) + write_file(('%s/new-ft.vim'):format(after_ftplugin_folder), [[let g:seq ..= 'a']]) + write_file(('%s/new-ft.lua'):format(after_ftplugin_folder), [[vim.g.seq = vim.g.seq .. 'b']]) + write_file(('%s/new-ft_a.vim'):format(after_ftplugin_folder), [[let g:seq ..= 'c']]) write_file( - table.concat({ ftplugin_folder, 'new-ft.lua' }, sep), - [[vim.g.seq = vim.g.seq .. 'B']] - ) - write_file(table.concat({ ftplugin_folder, 'new-ft_a.vim' }, sep), [[let g:seq ..= 'C']]) - write_file( - table.concat({ ftplugin_folder, 'new-ft_a.lua' }, sep), - [[vim.g.seq = vim.g.seq .. 'D']] - ) - write_file(table.concat({ ftplugin_folder, 'new-ft', 'a.vim' }, sep), [[let g:seq ..= 'E']]) - write_file( - table.concat({ ftplugin_folder, 'new-ft', 'a.lua' }, sep), - [[vim.g.seq = vim.g.seq .. 'F']] - ) - write_file(table.concat({ after_ftplugin_folder, 'new-ft.vim' }, sep), [[let g:seq ..= 'a']]) - write_file( - table.concat({ after_ftplugin_folder, 'new-ft.lua' }, sep), - [[vim.g.seq = vim.g.seq .. 'b']] - ) - write_file( - table.concat({ after_ftplugin_folder, 'new-ft_a.vim' }, sep), - [[let g:seq ..= 'c']] - ) - write_file( - table.concat({ after_ftplugin_folder, 'new-ft_a.lua' }, sep), + ('%s/new-ft_a.lua'):format(after_ftplugin_folder), [[vim.g.seq = vim.g.seq .. 'd']] ) + write_file(('%s/new-ft/a.vim'):format(after_ftplugin_folder), [[let g:seq ..= 'e']]) write_file( - table.concat({ after_ftplugin_folder, 'new-ft', 'a.vim' }, sep), - [[let g:seq ..= 'e']] - ) - write_file( - table.concat({ after_ftplugin_folder, 'new-ft', 'a.lua' }, sep), + ('%s/new-ft/a.lua'):format(after_ftplugin_folder), [[vim.g.seq = vim.g.seq .. 'f']] ) exec('setfiletype new-ft') @@ -232,47 +207,29 @@ describe('runtime:', function() it("'rtp' order is respected with 'fileignorecase'", function() exec('set fileignorecase') - local after_ftplugin_folder = table.concat({ plug_dir, 'after', 'ftplugin' }, sep) - mkdir_p(table.concat({ ftplugin_folder, 'new-ft' }, sep)) - mkdir_p(table.concat({ after_ftplugin_folder, 'new-ft' }, sep)) + local after_ftplugin_folder = ('%s/after/ftplugin'):format(plug_dir) + mkdir_p(('%s/new-ft'):format(ftplugin_folder)) + mkdir_p(('%s/new-ft'):format(after_ftplugin_folder)) exec('set rtp+=' .. plug_dir .. '/after') exec('let g:seq = ""') -- A .lua file is loaded after a .vim file if they only differ in extension. -- All files in after/ftplugin/ are loaded after all files in ftplugin/. - write_file(table.concat({ ftplugin_folder, 'new-ft.VIM' }, sep), [[let g:seq ..= 'A']]) + write_file(('%s/new-ft.VIM'):format(ftplugin_folder), [[let g:seq ..= 'A']]) + write_file(('%s/new-ft.LUA'):format(ftplugin_folder), [[vim.g.seq = vim.g.seq .. 'B']]) + write_file(('%s/new-ft_a.vim'):format(ftplugin_folder), [[let g:seq ..= 'C']]) + write_file(('%s/new-ft_a.lua'):format(ftplugin_folder), [[vim.g.seq = vim.g.seq .. 'D']]) + write_file(('%s/new-ft/a.VIM'):format(ftplugin_folder), [[let g:seq ..= 'E']]) + write_file(('%s/new-ft/a.LUA'):format(ftplugin_folder), [[vim.g.seq = vim.g.seq .. 'F']]) + write_file(('%s/new-ft.vim'):format(after_ftplugin_folder), [[let g:seq ..= 'a']]) + write_file(('%s/new-ft.lua'):format(after_ftplugin_folder), [[vim.g.seq = vim.g.seq .. 'b']]) + write_file(('%s/new-ft_a.VIM'):format(after_ftplugin_folder), [[let g:seq ..= 'c']]) write_file( - table.concat({ ftplugin_folder, 'new-ft.LUA' }, sep), - [[vim.g.seq = vim.g.seq .. 'B']] - ) - write_file(table.concat({ ftplugin_folder, 'new-ft_a.vim' }, sep), [[let g:seq ..= 'C']]) - write_file( - table.concat({ ftplugin_folder, 'new-ft_a.lua' }, sep), - [[vim.g.seq = vim.g.seq .. 'D']] - ) - write_file(table.concat({ ftplugin_folder, 'new-ft', 'a.VIM' }, sep), [[let g:seq ..= 'E']]) - write_file( - table.concat({ ftplugin_folder, 'new-ft', 'a.LUA' }, sep), - [[vim.g.seq = vim.g.seq .. 'F']] - ) - write_file(table.concat({ after_ftplugin_folder, 'new-ft.vim' }, sep), [[let g:seq ..= 'a']]) - write_file( - table.concat({ after_ftplugin_folder, 'new-ft.lua' }, sep), - [[vim.g.seq = vim.g.seq .. 'b']] - ) - write_file( - table.concat({ after_ftplugin_folder, 'new-ft_a.VIM' }, sep), - [[let g:seq ..= 'c']] - ) - write_file( - table.concat({ after_ftplugin_folder, 'new-ft_a.LUA' }, sep), + ('%s/new-ft_a.LUA'):format(after_ftplugin_folder), [[vim.g.seq = vim.g.seq .. 'd']] ) + write_file(('%s/new-ft/a.vim'):format(after_ftplugin_folder), [[let g:seq ..= 'e']]) write_file( - table.concat({ after_ftplugin_folder, 'new-ft', 'a.vim' }, sep), - [[let g:seq ..= 'e']] - ) - write_file( - table.concat({ after_ftplugin_folder, 'new-ft', 'a.lua' }, sep), + ('%s/new-ft/a.lua'):format(after_ftplugin_folder), [[vim.g.seq = vim.g.seq .. 'f']] ) exec('setfiletype new-ft') @@ -281,11 +238,11 @@ describe('runtime:', function() end) describe('indent', function() - local indent_folder = table.concat({ plug_dir, 'indent' }, sep) + local indent_folder = ('%s/indent'):format(plug_dir) it('Lua indents work and are included in cmdline completion', function() mkdir_p(indent_folder) - local indent_file = table.concat({ indent_folder, 'new-ft.lua' }, sep) + local indent_file = ('%s/new-ft.lua'):format(indent_folder) write_file(indent_file, [[vim.b.lua_indent = 1]]) eq({ 'new-ft' }, fn.getcompletion('new-f', 'filetype')) @@ -296,34 +253,28 @@ describe('runtime:', function() end) it("'rtp' order is respected", function() - local after_indent_folder = table.concat({ plug_dir, 'after', 'indent' }, sep) - mkdir_p(table.concat({ indent_folder, 'new-ft' }, sep)) - mkdir_p(table.concat({ after_indent_folder, 'new-ft' }, sep)) + local after_indent_folder = ('%s/after/indent'):format(plug_dir) + mkdir_p(('%s/new-ft'):format(indent_folder)) + mkdir_p(('%s/new-ft'):format(after_indent_folder)) exec('set rtp+=' .. plug_dir .. '/after') exec('let g:seq = ""') -- A .lua file is loaded after a .vim file if they only differ in extension. -- All files in after/indent/ are loaded after all files in indent/. - write_file(table.concat({ indent_folder, 'new-ft.vim' }, sep), [[let g:seq ..= 'A']]) - write_file( - table.concat({ indent_folder, 'new-ft.lua' }, sep), - [[vim.g.seq = vim.g.seq .. 'B']] - ) - write_file(table.concat({ after_indent_folder, 'new-ft.vim' }, sep), [[let g:seq ..= 'a']]) - write_file( - table.concat({ after_indent_folder, 'new-ft.lua' }, sep), - [[vim.g.seq = vim.g.seq .. 'b']] - ) + write_file(('%s/new-ft.vim'):format(indent_folder), [[let g:seq ..= 'A']]) + write_file(('%s/new-ft.lua'):format(indent_folder), [[vim.g.seq = vim.g.seq .. 'B']]) + write_file(('%s/new-ft.vim'):format(after_indent_folder), [[let g:seq ..= 'a']]) + write_file(('%s/new-ft.lua'):format(after_indent_folder), [[vim.g.seq = vim.g.seq .. 'b']]) exec('setfiletype new-ft') eq('ABab', eval('g:seq')) end) end) describe('syntax', function() - local syntax_folder = table.concat({ plug_dir, 'syntax' }, sep) + local syntax_folder = ('%s/syntax'):format(plug_dir) before_each(function() mkdir_p(syntax_folder) - local syntax_file = table.concat({ syntax_folder, 'my-lang.lua' }, sep) + local syntax_file = ('%s/my-lang.lua'):format(syntax_folder) write_file(syntax_file, [[vim.b.current_syntax = 'my-lang']]) exec([[let b:current_syntax = '']]) end) @@ -351,36 +302,21 @@ describe('runtime:', function() end) it("'rtp' order is respected", function() - local after_syntax_folder = table.concat({ plug_dir, 'after', 'syntax' }, sep) - mkdir_p(table.concat({ syntax_folder, 'my-lang' }, sep)) - mkdir_p(table.concat({ after_syntax_folder, 'my-lang' }, sep)) + local after_syntax_folder = ('%s/after/syntax'):format(plug_dir) + mkdir_p(('%s/my-lang'):format(syntax_folder)) + mkdir_p(('%s/my-lang'):format(after_syntax_folder)) exec('set rtp+=' .. plug_dir .. '/after') exec('let g:seq = ""') -- A .lua file is loaded after a .vim file if they only differ in extension. -- All files in after/syntax/ are loaded after all files in syntax/. - write_file(table.concat({ syntax_folder, 'my-lang.vim' }, sep), [[let g:seq ..= 'A']]) - write_file( - table.concat({ syntax_folder, 'my-lang.lua' }, sep), - [[vim.g.seq = vim.g.seq .. 'B']] - ) - write_file(table.concat({ syntax_folder, 'my-lang', 'a.vim' }, sep), [[let g:seq ..= 'C']]) - write_file( - table.concat({ syntax_folder, 'my-lang', 'a.lua' }, sep), - [[vim.g.seq = vim.g.seq .. 'D']] - ) - write_file(table.concat({ after_syntax_folder, 'my-lang.vim' }, sep), [[let g:seq ..= 'a']]) - write_file( - table.concat({ after_syntax_folder, 'my-lang.lua' }, sep), - [[vim.g.seq = vim.g.seq .. 'b']] - ) - write_file( - table.concat({ after_syntax_folder, 'my-lang', 'a.vim' }, sep), - [[let g:seq ..= 'c']] - ) - write_file( - table.concat({ after_syntax_folder, 'my-lang', 'a.lua' }, sep), - [[vim.g.seq = vim.g.seq .. 'd']] - ) + write_file(('%s/my-lang.vim'):format(syntax_folder), [[let g:seq ..= 'A']]) + write_file(('%s/my-lang.lua'):format(syntax_folder), [[vim.g.seq = vim.g.seq .. 'B']]) + write_file(('%s/my-lang/a.vim'):format(syntax_folder), [[let g:seq ..= 'C']]) + write_file(('%s/my-lang/a.lua'):format(syntax_folder), [[vim.g.seq = vim.g.seq .. 'D']]) + write_file(('%s/my-lang.vim'):format(after_syntax_folder), [[let g:seq ..= 'a']]) + write_file(('%s/my-lang.lua'):format(after_syntax_folder), [[vim.g.seq = vim.g.seq .. 'b']]) + write_file(('%s/my-lang/a.vim'):format(after_syntax_folder), [[let g:seq ..= 'c']]) + write_file(('%s/my-lang/a.lua'):format(after_syntax_folder), [[vim.g.seq = vim.g.seq .. 'd']]) exec('setfiletype my-lang') eq('ABCDabcd', eval('g:seq')) end) @@ -388,21 +324,18 @@ describe('runtime:', function() describe('spell', function() it("loads spell/LANG.{vim,lua} respecting 'rtp' order", function() - local spell_folder = table.concat({ plug_dir, 'spell' }, sep) - local after_spell_folder = table.concat({ plug_dir, 'after', 'spell' }, sep) - mkdir_p(table.concat({ spell_folder, 'Xtest' }, sep)) - mkdir_p(table.concat({ after_spell_folder, 'Xtest' }, sep)) + local spell_folder = ('%s/spell'):format(plug_dir) + local after_spell_folder = ('%s/after/spell'):format(plug_dir) + mkdir_p(('%s/Xtest'):format(spell_folder)) + mkdir_p(('%s/Xtest'):format(after_spell_folder)) exec('set rtp+=' .. plug_dir .. '/after') exec('let g:seq = ""') -- A .lua file is loaded after a .vim file if they only differ in extension. -- All files in after/spell/ are loaded after all files in spell/. - write_file(table.concat({ spell_folder, 'Xtest.vim' }, sep), [[let g:seq ..= 'A']]) - write_file(table.concat({ spell_folder, 'Xtest.lua' }, sep), [[vim.g.seq = vim.g.seq .. 'B']]) - write_file(table.concat({ after_spell_folder, 'Xtest.vim' }, sep), [[let g:seq ..= 'a']]) - write_file( - table.concat({ after_spell_folder, 'Xtest.lua' }, sep), - [[vim.g.seq = vim.g.seq .. 'b']] - ) + write_file(('%s/Xtest.vim'):format(spell_folder), [[let g:seq ..= 'A']]) + write_file(('%s/Xtest.lua'):format(spell_folder), [[vim.g.seq = vim.g.seq .. 'B']]) + write_file(('%s/Xtest.vim'):format(after_spell_folder), [[let g:seq ..= 'a']]) + write_file(('%s/Xtest.lua'):format(after_spell_folder), [[vim.g.seq = vim.g.seq .. 'b']]) exec('set spelllang=Xtest') eq('ABab', eval('g:seq')) end) @@ -411,7 +344,7 @@ describe('runtime:', function() it('Lua file loaded by :runtime has proper script ID #32598', function() local test_file = 'Xtest_runtime_cmd.lua' write_file( - table.concat({ plug_dir, test_file }, sep), + ('%s/%s'):format(plug_dir, test_file), [[ vim.g.script_id = tonumber(vim.fn.expand(''):match('(%d+)_')) vim.o.mouse = 'nv' diff --git a/test/functional/ex_cmds/mksession_spec.lua b/test/functional/ex_cmds/mksession_spec.lua index f4819a1bc2..37a88a8a11 100644 --- a/test/functional/ex_cmds/mksession_spec.lua +++ b/test/functional/ex_cmds/mksession_spec.lua @@ -145,7 +145,7 @@ describe(':mksession', function() it('restores buffers with tab-local CWD', function() local tmpfile_base = file_prefix .. '-tmpfile' - local cwd_dir = t.fix_slashes(fn.getcwd()) + local cwd_dir = vim.fs.normalize(fn.getcwd()) local session_path = cwd_dir .. get_pathsep() .. session_file command('edit ' .. tmpfile_base .. '1') @@ -161,9 +161,9 @@ describe(':mksession', function() -- Use :silent to avoid hit-enter prompt due to long path command('silent source ' .. session_path) command('tabnext 1') - eq(cwd_dir .. '/' .. tmpfile_base .. '1', fn.expand('%:p')) + eq(('%s/%s1'):format(cwd_dir, tmpfile_base), vim.fs.normalize(fn.expand('%:p'))) command('tabnext 2') - eq(cwd_dir .. '/' .. tmpfile_base .. '2', fn.expand('%:p')) + eq(('%s/%s2'):format(cwd_dir, tmpfile_base), vim.fs.normalize(fn.expand('%:p'))) end) it('restores symlinked directory buffer names', function() @@ -187,8 +187,7 @@ describe(':mksession', function() end) it('restores CWD for :terminal buffers #11288', function() - local cwd_dir = fn.fnamemodify('.', ':p:~'):gsub([[[\/]*$]], '') - cwd_dir = t.fix_slashes(cwd_dir) -- :mksession always uses unix slashes. + local cwd_dir = fn.fnamemodify('.', ':p:~'):gsub([[/*$]], '') local session_path = cwd_dir .. '/' .. session_file command('cd ' .. tab_dir) diff --git a/test/functional/ex_cmds/source_spec.lua b/test/functional/ex_cmds/source_spec.lua index 7ce5c95fa6..79cbd6bdd8 100644 --- a/test/functional/ex_cmds/source_spec.lua +++ b/test/functional/ex_cmds/source_spec.lua @@ -47,7 +47,7 @@ describe(':source', function() os.remove(test_file) end) - it("changing 'shellslash' doesn't affect the result of expand()", function() + it("changing 'shellslash' changes the result of expand()", function() t.skip(not is_os('win'), "N/A: 'shellslash' only works on Windows") api.nvim_set_option_value('shellslash', false, {}) @@ -66,9 +66,9 @@ describe(':source', function() for _ = 1, 2 do command([[source Xshellslash/Xstack.vim]]) - matches([[Xshellslash/Xstack%.vim]], api.nvim_get_var('stack1')) + matches([[Xshellslash\Xstack%.vim]], api.nvim_get_var('stack1')) matches([[Xshellslash/Xstack%.vim]], api.nvim_get_var('stack2')) - matches([[Xshellslash/Xstack%.vim]], api.nvim_get_var('stack3')) + matches([[Xshellslash\Xstack%.vim]], api.nvim_get_var('stack3')) end write_file( @@ -84,9 +84,9 @@ describe(':source', function() for _ = 1, 2 do command([[source Xshellslash/Xstack.lua]]) - matches([[Xshellslash/Xstack%.lua]], api.nvim_get_var('stack1')) + matches([[Xshellslash\Xstack%.lua]], api.nvim_get_var('stack1')) matches([[Xshellslash/Xstack%.lua]], api.nvim_get_var('stack2')) - matches([[Xshellslash/Xstack%.lua]], api.nvim_get_var('stack3')) + matches([[Xshellslash\Xstack%.lua]], api.nvim_get_var('stack3')) end rmdir('Xshellslash') diff --git a/test/functional/ex_cmds/verbose_spec.lua b/test/functional/ex_cmds/verbose_spec.lua index 148059c12a..cc3af84c87 100644 --- a/test/functional/ex_cmds/verbose_spec.lua +++ b/test/functional/ex_cmds/verbose_spec.lua @@ -17,7 +17,7 @@ local function last_set_lua_verbose_tests(cmd, v1) setup(function() clear(v1 and { args = { '-V1' } } or nil) script_file = 'test_verbose.lua' - local current_dir = assert(t.fix_slashes(fn.getcwd())) + local current_dir = fn.getcwd() current_dir = fn.fnamemodify(current_dir, ':~') script_location = current_dir .. '/' .. script_file @@ -326,7 +326,7 @@ describe(':verbose when using API from Vimscript', function() setup(function() clear() script_file = 'test_verbose.vim' - local current_dir = assert(t.fix_slashes(fn.getcwd())) + local current_dir = fn.getcwd() current_dir = fn.fnamemodify(current_dir, ':~') script_location = current_dir .. '/' .. script_file diff --git a/test/functional/lua/uri_spec.lua b/test/functional/lua/uri_spec.lua index e1cd546fdc..76e11d65a7 100644 --- a/test/functional/lua/uri_spec.lua +++ b/test/functional/lua/uri_spec.lua @@ -217,7 +217,7 @@ describe('URI methods', function() ]], file ) - local expected_uri = 'file:///' .. t.fix_slashes(file) + local expected_uri = ('file:///%s'):format(file) eq(expected_uri, exec_lua(test_case)) os.remove(file) end) diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua index 36bd00ab7c..0f2db3fb4e 100644 --- a/test/functional/options/defaults_spec.lua +++ b/test/functional/options/defaults_spec.lua @@ -265,7 +265,7 @@ describe('startup defaults', function() NVIM_LOG_FILE = '', -- Empty is invalid. }, }) - eq(xdgstatedir .. '/logs/nvim.log', t.fix_slashes(eval('$NVIM_LOG_FILE'))) + eq(('%s/logs/nvim.log'):format(xdgstatedir), eval('$NVIM_LOG_FILE')) end) it('defaults to stdpath("log")/nvim.log if invalid', function() @@ -276,7 +276,7 @@ describe('startup defaults', function() NVIM_LOG_FILE = '.', -- Any directory is invalid. }, }) - eq(xdgstatedir .. '/logs/nvim.log', t.fix_slashes(eval('$NVIM_LOG_FILE'))) + eq(('%s/logs/nvim.log'):format(xdgstatedir), eval('$NVIM_LOG_FILE')) -- Avoid "failed to open $NVIM_LOG_FILE" noise in test output. expect_exit(command, 'qall!') end) @@ -382,158 +382,48 @@ describe('XDG defaults', function() local vimruntime, libdir = vimruntime_and_libdir() - eq( - ( - t.fix_slashes( - root_path - .. ('/x'):rep(4096) - .. '/nvim' - .. ',' - .. root_path - .. ('/a'):rep(2048) - .. '/nvim' - .. ',' - .. root_path - .. ('/b'):rep(2048) - .. '/nvim' - .. (',' .. root_path .. '/c/nvim') - .. ',' - .. root_path - .. ('/X'):rep(4096) - .. '/' - .. data_dir - .. '/site' - .. ',' - .. root_path - .. ('/A'):rep(2048) - .. '/nvim/site' - .. ',' - .. root_path - .. ('/B'):rep(2048) - .. '/nvim/site' - .. (',' .. root_path .. '/C/nvim/site') - .. ',' - .. vimruntime - .. ',' - .. libdir - .. (',' .. root_path .. '/C/nvim/site/after') - .. ',' - .. root_path - .. ('/B'):rep(2048) - .. '/nvim/site/after' - .. ',' - .. root_path - .. ('/A'):rep(2048) - .. '/nvim/site/after' - .. ',' - .. root_path - .. ('/X'):rep(4096) - .. '/' - .. data_dir - .. '/site/after' - .. (',' .. root_path .. '/c/nvim/after') - .. ',' - .. root_path - .. ('/b'):rep(2048) - .. '/nvim/after' - .. ',' - .. root_path - .. ('/a'):rep(2048) - .. '/nvim/after' - .. ',' - .. root_path - .. ('/x'):rep(4096) - .. '/nvim/after' - ) - ), - t.fix_slashes(api.nvim_get_option_value('runtimepath', {})) - ) + local expected_paths = table.concat({ + ('%s%s/nvim'):format(root_path, ('/x'):rep(4096)), + ('%s%s/nvim'):format(root_path, ('/a'):rep(2048)), + ('%s%s/nvim'):format(root_path, ('/b'):rep(2048)), + ('%s/c/nvim'):format(root_path), + ('%s%s/%s/site'):format(root_path, ('/X'):rep(4096), data_dir), + ('%s%s/nvim/site'):format(root_path, ('/A'):rep(2048)), + ('%s%s/nvim/site'):format(root_path, ('/B'):rep(2048)), + ('%s/C/nvim/site'):format(root_path), + vimruntime, + libdir, + ('%s/C/nvim/site/after'):format(root_path), + ('%s%s/nvim/site/after'):format(root_path, ('/B'):rep(2048)), + ('%s%s/nvim/site/after'):format(root_path, ('/A'):rep(2048)), + ('%s%s/%s/site/after'):format(root_path, ('/X'):rep(4096), data_dir), + ('%s/c/nvim/after'):format(root_path), + ('%s%s/nvim/after'):format(root_path, ('/b'):rep(2048)), + ('%s%s/nvim/after'):format(root_path, ('/a'):rep(2048)), + ('%s%s/nvim/after'):format(root_path, ('/x'):rep(4096)), + }, ',') + eq(expected_paths, api.nvim_get_option_value('runtimepath', {})) command('set runtimepath&') command('set backupdir&') command('set directory&') command('set undodir&') command('set viewdir&') + eq(expected_paths, api.nvim_get_option_value('runtimepath', {})) eq( - ( - t.fix_slashes( - root_path - .. ('/x'):rep(4096) - .. '/nvim' - .. ',' - .. root_path - .. ('/a'):rep(2048) - .. '/nvim' - .. ',' - .. root_path - .. ('/b'):rep(2048) - .. '/nvim' - .. (',' .. root_path .. '/c/nvim') - .. ',' - .. root_path - .. ('/X'):rep(4096) - .. '/' - .. data_dir - .. '/site' - .. ',' - .. root_path - .. ('/A'):rep(2048) - .. '/nvim/site' - .. ',' - .. root_path - .. ('/B'):rep(2048) - .. '/nvim/site' - .. (',' .. root_path .. '/C/nvim/site') - .. ',' - .. vimruntime - .. ',' - .. libdir - .. (',' .. root_path .. '/C/nvim/site/after') - .. ',' - .. root_path - .. ('/B'):rep(2048) - .. '/nvim/site/after' - .. ',' - .. root_path - .. ('/A'):rep(2048) - .. '/nvim/site/after' - .. ',' - .. root_path - .. ('/X'):rep(4096) - .. '/' - .. data_dir - .. '/site/after' - .. (',' .. root_path .. '/c/nvim/after') - .. ',' - .. root_path - .. ('/b'):rep(2048) - .. '/nvim/after' - .. ',' - .. root_path - .. ('/a'):rep(2048) - .. '/nvim/after' - .. ',' - .. root_path - .. ('/x'):rep(4096) - .. '/nvim/after' - ) - ), - t.fix_slashes(api.nvim_get_option_value('runtimepath', {})) + ('.,%s%s/%s/backup//'):format(root_path, ('/X'):rep(4096), state_dir), + api.nvim_get_option_value('backupdir', {}) ) eq( - '.,' .. root_path .. ('/X'):rep(4096) .. '/' .. state_dir .. '/backup//', - t.fix_slashes(api.nvim_get_option_value('backupdir', {})) + ('%s%s/%s/swap//'):format(root_path, ('/X'):rep(4096), state_dir), + api.nvim_get_option_value('directory', {}) ) eq( - root_path .. ('/X'):rep(4096) .. '/' .. state_dir .. '/swap//', - t.fix_slashes(api.nvim_get_option_value('directory', {})) + ('%s%s/%s/undo//'):format(root_path, ('/X'):rep(4096), state_dir), + api.nvim_get_option_value('undodir', {}) ) eq( - root_path .. ('/X'):rep(4096) .. '/' .. state_dir .. '/undo//', - t.fix_slashes(api.nvim_get_option_value('undodir', {})) - ) - eq( - root_path .. ('/X'):rep(4096) .. '/' .. state_dir .. '/view//', - t.fix_slashes(api.nvim_get_option_value('viewdir', {})) + ('%s%s/%s/view//'):format(root_path, ('/X'):rep(4096), state_dir), + api.nvim_get_option_value('viewdir', {}) ) end) end) @@ -573,111 +463,47 @@ describe('XDG defaults', function() end local vimruntime, libdir = vimruntime_and_libdir() - eq( - ( - t.fix_slashes( - '$XDG_DATA_HOME/nvim' - .. ',$XDG_DATA_DIRS/nvim' - .. ',$XDG_CONFIG_HOME/' - .. data_dir - .. '/site' - .. ',$XDG_CONFIG_DIRS/nvim/site' - .. ',' - .. vimruntime - .. ',' - .. libdir - .. ',$XDG_CONFIG_DIRS/nvim/site/after' - .. ',$XDG_CONFIG_HOME/' - .. data_dir - .. '/site/after' - .. ',$XDG_DATA_DIRS/nvim/after' - .. ',$XDG_DATA_HOME/nvim/after' - ) - ), - t.fix_slashes(api.nvim_get_option_value('runtimepath', {})) - ) + local expected_paths = table.concat({ + '$XDG_DATA_HOME/nvim', + '$XDG_DATA_DIRS/nvim', + ('$XDG_CONFIG_HOME/%s/site'):format(data_dir), + '$XDG_CONFIG_DIRS/nvim/site', + vimruntime, + libdir, + '$XDG_CONFIG_DIRS/nvim/site/after', + ('$XDG_CONFIG_HOME/%s/site/after'):format(data_dir), + '$XDG_DATA_DIRS/nvim/after', + '$XDG_DATA_HOME/nvim/after', + }, ',') + eq(expected_paths, api.nvim_get_option_value('runtimepath', {})) command('set runtimepath&') command('set backupdir&') command('set directory&') command('set undodir&') command('set viewdir&') + eq(expected_paths, api.nvim_get_option_value('runtimepath', {})) eq( - ( - t.fix_slashes( - '$XDG_DATA_HOME/nvim' - .. ',$XDG_DATA_DIRS/nvim' - .. ',$XDG_CONFIG_HOME/' - .. data_dir - .. '/site' - .. ',$XDG_CONFIG_DIRS/nvim/site' - .. ',' - .. vimruntime - .. ',' - .. libdir - .. ',$XDG_CONFIG_DIRS/nvim/site/after' - .. ',$XDG_CONFIG_HOME/' - .. data_dir - .. '/site/after' - .. ',$XDG_DATA_DIRS/nvim/after' - .. ',$XDG_DATA_HOME/nvim/after' - ) - ), - t.fix_slashes(api.nvim_get_option_value('runtimepath', {})) + ('.,$XDG_CONFIG_HOME/%s/backup//'):format(state_dir), + api.nvim_get_option_value('backupdir', {}) ) eq( - ('.,$XDG_CONFIG_HOME/' .. state_dir .. '/backup//'), - t.fix_slashes(api.nvim_get_option_value('backupdir', {})) - ) - eq( - ('$XDG_CONFIG_HOME/' .. state_dir .. '/swap//'), - t.fix_slashes(api.nvim_get_option_value('directory', {})) - ) - eq( - ('$XDG_CONFIG_HOME/' .. state_dir .. '/undo//'), - t.fix_slashes(api.nvim_get_option_value('undodir', {})) - ) - eq( - ('$XDG_CONFIG_HOME/' .. state_dir .. '/view//'), - t.fix_slashes(api.nvim_get_option_value('viewdir', {})) + ('$XDG_CONFIG_HOME/%s/swap//'):format(state_dir), + api.nvim_get_option_value('directory', {}) ) + eq(('$XDG_CONFIG_HOME/%s/undo//'):format(state_dir), api.nvim_get_option_value('undodir', {})) + eq(('$XDG_CONFIG_HOME/%s/view//'):format(state_dir), api.nvim_get_option_value('viewdir', {})) command('set all&') + eq(expected_paths, api.nvim_get_option_value('runtimepath', {})) eq( - t.fix_slashes( - '$XDG_DATA_HOME/nvim' - .. ',$XDG_DATA_DIRS/nvim' - .. ',$XDG_CONFIG_HOME/' - .. data_dir - .. '/site' - .. ',$XDG_CONFIG_DIRS/nvim/site' - .. ',' - .. vimruntime - .. ',' - .. libdir - .. ',$XDG_CONFIG_DIRS/nvim/site/after' - .. ',$XDG_CONFIG_HOME/' - .. data_dir - .. '/site/after' - .. ',$XDG_DATA_DIRS/nvim/after' - .. ',$XDG_DATA_HOME/nvim/after' - ), - t.fix_slashes(api.nvim_get_option_value('runtimepath', {})) + ('.,$XDG_CONFIG_HOME/%s/backup//'):format(state_dir), + api.nvim_get_option_value('backupdir', {}) ) eq( - ('.,$XDG_CONFIG_HOME/' .. state_dir .. '/backup//'), - t.fix_slashes(api.nvim_get_option_value('backupdir', {})) - ) - eq( - ('$XDG_CONFIG_HOME/' .. state_dir .. '/swap//'), - t.fix_slashes(api.nvim_get_option_value('directory', {})) - ) - eq( - ('$XDG_CONFIG_HOME/' .. state_dir .. '/undo//'), - t.fix_slashes(api.nvim_get_option_value('undodir', {})) - ) - eq( - ('$XDG_CONFIG_HOME/' .. state_dir .. '/view//'), - t.fix_slashes(api.nvim_get_option_value('viewdir', {})) + ('$XDG_CONFIG_HOME/%s/swap//'):format(state_dir), + api.nvim_get_option_value('directory', {}) ) + eq(('$XDG_CONFIG_HOME/%s/undo//'):format(state_dir), api.nvim_get_option_value('undodir', {})) + eq(('$XDG_CONFIG_HOME/%s/view//'):format(state_dir), api.nvim_get_option_value('viewdir', {})) eq(nil, (fn.tempname()):match('XDG_RUNTIME_DIR')) end) end) @@ -953,6 +779,11 @@ describe('stdpath()', function() test_appname('..', 1) test_appname('.', 1) test_appname('/', 1) + test_appname('\\', 1) + if is_os('win') then + test_appname('..\\a', 1) + test_appname('a\\..', 1) + end test_appname(is_os('win') and 'C:/a/b' or '/a/b', 1) -- Valid appnames: test_appname('a/b', 0) @@ -980,10 +811,10 @@ describe('stdpath()', function() }, }) - t.matches(vim.pesc(tmpdir), t.fix_slashes(fn.tempname())) + t.matches(vim.pesc(tmpdir), fn.tempname()) t.assert_nolog('tempdir', testlog, 100) t.assert_nolog('TMPDIR', testlog, 100) - t.matches([=[[/\\]relative%-appname.[^/\\]+]=], api.nvim_get_vvar('servername')) + t.matches([[/relative%-appname.%d+]], api.nvim_get_vvar('servername')) end) describe('returns a String', function() @@ -994,16 +825,16 @@ describe('stdpath()', function() XDG_CONFIG_HOME = '/home/docwhat/.config', }, }) - eq('/home/docwhat/.config/nvim', t.fix_slashes(fn.stdpath('config'))) + eq('/home/docwhat/.config/nvim', fn.stdpath('config')) end) it('handles changes during runtime', function() clear({ env = { XDG_CONFIG_HOME = '/home/original', } }) - eq('/home/original/nvim', t.fix_slashes(fn.stdpath('config'))) + eq('/home/original/nvim', fn.stdpath('config')) command("let $XDG_CONFIG_HOME='/home/new'") - eq('/home/new/nvim', t.fix_slashes(fn.stdpath('config'))) + eq('/home/new/nvim', fn.stdpath('config')) end) it("doesn't expand $VARIABLES", function() @@ -1013,14 +844,14 @@ describe('stdpath()', function() VARIABLES = 'this-should-not-happen', }, }) - eq('$VARIABLES/nvim', t.fix_slashes(fn.stdpath('config'))) + eq('$VARIABLES/nvim', fn.stdpath('config')) end) it("doesn't expand ~/", function() clear({ env = { XDG_CONFIG_HOME = '~/frobnitz', } }) - eq('~/frobnitz/nvim', t.fix_slashes(fn.stdpath('config'))) + eq('~/frobnitz/nvim', fn.stdpath('config')) end) end) @@ -1029,16 +860,16 @@ describe('stdpath()', function() clear({ env = { XDG_DATA_HOME = '/home/docwhat/.local', } }) - eq('/home/docwhat/.local/' .. datadir, t.fix_slashes(fn.stdpath('data'))) + eq(('/home/docwhat/.local/%s'):format(datadir), fn.stdpath('data')) end) it('handles changes during runtime', function() clear({ env = { XDG_DATA_HOME = '/home/original', } }) - eq('/home/original/' .. datadir, t.fix_slashes(fn.stdpath('data'))) + eq(('/home/original/%s'):format(datadir), fn.stdpath('data')) command("let $XDG_DATA_HOME='/home/new'") - eq('/home/new/' .. datadir, t.fix_slashes(fn.stdpath('data'))) + eq(('/home/new/%s'):format(datadir), fn.stdpath('data')) end) it("doesn't expand $VARIABLES", function() @@ -1048,14 +879,14 @@ describe('stdpath()', function() VARIABLES = 'this-should-not-happen', }, }) - eq('$VARIABLES/' .. datadir, t.fix_slashes(fn.stdpath('data'))) + eq(('$VARIABLES/%s'):format(datadir), fn.stdpath('data')) end) it("doesn't expand ~/", function() clear({ env = { XDG_DATA_HOME = '~/frobnitz', } }) - eq('~/frobnitz/' .. datadir, t.fix_slashes(fn.stdpath('data'))) + eq(('~/frobnitz/%s'):format(datadir), fn.stdpath('data')) end) end) @@ -1066,16 +897,16 @@ describe('stdpath()', function() XDG_STATE_HOME = '/home/docwhat/.local', }, }) - eq('/home/docwhat/.local/' .. statedir, t.fix_slashes(fn.stdpath('state'))) + eq(('/home/docwhat/.local/%s'):format(statedir), fn.stdpath('state')) end) it('handles changes during runtime', function() clear({ env = { XDG_STATE_HOME = '/home/original', } }) - eq('/home/original/' .. statedir, t.fix_slashes(fn.stdpath('state'))) + eq(('/home/original/%s'):format(statedir), fn.stdpath('state')) command("let $XDG_STATE_HOME='" .. '/home/new' .. "'") - eq('/home/new/' .. statedir, t.fix_slashes(fn.stdpath('state'))) + eq(('/home/new/%s'):format(statedir), fn.stdpath('state')) end) it("doesn't expand $VARIABLES", function() @@ -1085,14 +916,14 @@ describe('stdpath()', function() VARIABLES = 'this-should-not-happen', }, }) - eq('$VARIABLES/' .. statedir, t.fix_slashes(fn.stdpath('state'))) + eq(('$VARIABLES/%s'):format(statedir), fn.stdpath('state')) end) it("doesn't expand ~/", function() clear({ env = { XDG_STATE_HOME = '~/frobnitz', } }) - eq('~/frobnitz/' .. statedir, t.fix_slashes(fn.stdpath('state'))) + eq(('~/frobnitz/%s'):format(statedir), fn.stdpath('state')) end) end) @@ -1103,16 +934,16 @@ describe('stdpath()', function() XDG_CACHE_HOME = '/home/docwhat/.cache', }, }) - eq('/home/docwhat/.cache/nvim', t.fix_slashes(fn.stdpath('cache'))) + eq('/home/docwhat/.cache/nvim', fn.stdpath('cache')) end) it('handles changes during runtime', function() clear({ env = { XDG_CACHE_HOME = '/home/original', } }) - eq('/home/original/nvim', t.fix_slashes(fn.stdpath('cache'))) + eq('/home/original/nvim', fn.stdpath('cache')) command("let $XDG_CACHE_HOME='" .. '/home/new' .. "'") - eq('/home/new/nvim', t.fix_slashes(fn.stdpath('cache'))) + eq('/home/new/nvim', fn.stdpath('cache')) end) it("doesn't expand $VARIABLES", function() @@ -1122,14 +953,14 @@ describe('stdpath()', function() VARIABLES = 'this-should-not-happen', }, }) - eq('$VARIABLES/nvim', t.fix_slashes(fn.stdpath('cache'))) + eq('$VARIABLES/nvim', fn.stdpath('cache')) end) it("doesn't expand ~/", function() clear({ env = { XDG_CACHE_HOME = '~/frobnitz', } }) - eq('~/frobnitz/nvim', t.fix_slashes(fn.stdpath('cache'))) + eq('~/frobnitz/nvim', fn.stdpath('cache')) end) end) end) @@ -1178,7 +1009,7 @@ describe('stdpath()', function() describe(msg, function() it('set via system', function() set_paths_via_system(env_var_name, paths) - eq(expected_paths, t.fix_slashes(fn.stdpath(stdpath_arg))) + eq(expected_paths, fn.stdpath(stdpath_arg)) if not is_os('win') then assert_log( '$TMPDIR tempdir not a directory[^\n]*TMPDIR%-should%-be%-ignored', @@ -1190,7 +1021,7 @@ describe('stdpath()', function() it('set at runtime', function() set_paths_at_runtime(env_var_name, paths) - eq(expected_paths, t.fix_slashes(fn.stdpath(stdpath_arg))) + eq(expected_paths, fn.stdpath(stdpath_arg)) if not is_os('win') then assert_log( '$TMPDIR tempdir not a directory[^\n]*TMPDIR%-should%-be%-ignored', @@ -1208,10 +1039,10 @@ describe('stdpath()', function() 'config_dirs', 'XDG_CONFIG_DIRS', { - t.fix_slashes('/home/docwhat/.config'), + '/home/docwhat/.config', }, { - t.fix_slashes('/home/docwhat/.config/nvim'), + '/home/docwhat/.config/nvim', } ) @@ -1220,12 +1051,12 @@ describe('stdpath()', function() 'config_dirs', 'XDG_CONFIG_DIRS', { - t.fix_slashes('/home/docwhat/.config'), - t.fix_slashes('/etc/config'), + '/home/docwhat/.config', + '/etc/config', }, { - t.fix_slashes('/home/docwhat/.config/nvim'), - t.fix_slashes('/etc/config/nvim'), + '/home/docwhat/.config/nvim', + '/etc/config/nvim', } ) @@ -1235,25 +1066,25 @@ describe('stdpath()', function() 'XDG_CONFIG_DIRS', { '$HOME', '$TMP' }, { - t.fix_slashes('$HOME/nvim'), - t.fix_slashes('$TMP/nvim'), + '$HOME/nvim', + '$TMP/nvim', } ) behaves_like_dir_list_env("doesn't expand ~/", 'config_dirs', 'XDG_CONFIG_DIRS', { - t.fix_slashes('~/.oldconfig'), - t.fix_slashes('~/.olderconfig'), + '~/.oldconfig', + '~/.olderconfig', }, { - t.fix_slashes('~/.oldconfig/nvim'), - t.fix_slashes('~/.olderconfig/nvim'), + '~/.oldconfig/nvim', + '~/.olderconfig/nvim', }) end) describe('with "data_dirs"', function() behaves_like_dir_list_env('knows XDG_DATA_DIRS with one path', 'data_dirs', 'XDG_DATA_DIRS', { - t.fix_slashes('/home/docwhat/.data'), + '/home/docwhat/.data', }, { - t.fix_slashes('/home/docwhat/.data/nvim'), + '/home/docwhat/.data/nvim', }) behaves_like_dir_list_env( @@ -1261,12 +1092,12 @@ describe('stdpath()', function() 'data_dirs', 'XDG_DATA_DIRS', { - t.fix_slashes('/home/docwhat/.data'), - t.fix_slashes('/etc/local'), + '/home/docwhat/.data', + '/etc/local', }, { - t.fix_slashes('/home/docwhat/.data/nvim'), - t.fix_slashes('/etc/local/nvim'), + '/home/docwhat/.data/nvim', + '/etc/local/nvim', } ) @@ -1276,17 +1107,17 @@ describe('stdpath()', function() 'XDG_DATA_DIRS', { '$HOME', '$TMP' }, { - t.fix_slashes('$HOME/nvim'), - t.fix_slashes('$TMP/nvim'), + '$HOME/nvim', + '$TMP/nvim', } ) behaves_like_dir_list_env("doesn't expand ~/", 'data_dirs', 'XDG_DATA_DIRS', { - t.fix_slashes('~/.oldconfig'), - t.fix_slashes('~/.olderconfig'), + '~/.oldconfig', + '~/.olderconfig', }, { - t.fix_slashes('~/.oldconfig/nvim'), - t.fix_slashes('~/.olderconfig/nvim'), + '~/.oldconfig/nvim', + '~/.olderconfig/nvim', }) end) end) diff --git a/test/functional/plugin/pack_spec.lua b/test/functional/plugin/pack_spec.lua index cf3f365b92..2b0f00e747 100644 --- a/test/functional/plugin/pack_spec.lua +++ b/test/functional/plugin/pack_spec.lua @@ -850,7 +850,7 @@ describe('vim.pack', function() eq('basic feat-branch', out) - local rtp = vim.tbl_map(t.fix_slashes, api.nvim_list_runtime_paths()) + local rtp = api.nvim_list_runtime_paths() local plug_path = pack_get_plug_path('basic') local after_dir = vim.fs.joinpath(plug_path, 'after') eq(true, vim.tbl_contains(rtp, plug_path)) @@ -989,7 +989,7 @@ describe('vim.pack', function() eq(ref, out) -- Should add necessary directories to runtimepath regardless of `opts.load` - local rtp = vim.tbl_map(t.fix_slashes, api.nvim_list_runtime_paths()) + local rtp = api.nvim_list_runtime_paths() local plug_path = pack_get_plug_path('plugin % dirs') local after_dir = vim.fs.joinpath(plug_path, 'after') eq(true, vim.tbl_contains(rtp, plug_path)) diff --git a/test/functional/terminal/edit_spec.lua b/test/functional/terminal/edit_spec.lua index 765e629f31..a9073b822a 100644 --- a/test/functional/terminal/edit_spec.lua +++ b/test/functional/terminal/edit_spec.lua @@ -25,7 +25,7 @@ describe(':edit term://*', function() command('edit term://') local termopen_runs = api.nvim_get_var('termopen_runs') eq(1, #termopen_runs) - local cwd = fn.fnamemodify('.', ':p:~'):gsub([[[\/]*$]], '') + local cwd = fn.fnamemodify('.', ':p:~'):gsub([[/*$]], '') matches('^term://' .. pesc(cwd) .. '//%d+:$', termopen_runs[1]) end) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 1e81a8cf11..962e3dd441 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -837,6 +837,10 @@ describe('TUI :connect', function() tt.feed_data('iThis is server 2.\027') screen2:expect({ any = vim.pesc('This is server 2^.') }) + if is_os('win') then + -- still supports backslashes + server1 = server1:gsub('/', '\\') + end tt.feed_data(':connect ' .. server1 .. '\013') screen2:expect({ any = vim.pesc('This is server 1^.') }) diff --git a/test/functional/vimscript/ctx_functions_spec.lua b/test/functional/vimscript/ctx_functions_spec.lua index 85a74c0ab6..6bda21b15f 100644 --- a/test/functional/vimscript/ctx_functions_spec.lua +++ b/test/functional/vimscript/ctx_functions_spec.lua @@ -322,7 +322,7 @@ describe('context functions', function() filter(map(add( getjumplist()[0], { 'bufnr': bufnr('%'), 'lnum': getcurpos()[1] }), 'filter( - { "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum }, + { "f": expand("#".v:val.bufnr.":p:gs?\\?/?"), "l": v:val.lnum }, { k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)') ]]):gsub('\n', ''))), } diff --git a/test/functional/vimscript/executable_spec.lua b/test/functional/vimscript/executable_spec.lua index bb52c861f0..4101d8f32d 100644 --- a/test/functional/vimscript/executable_spec.lua +++ b/test/functional/vimscript/executable_spec.lua @@ -2,7 +2,7 @@ local t = require('test.testutil') local n = require('test.functional.testnvim')() local eq, clear, call, write_file, command = t.eq, n.clear, n.call, t.write_file, n.command -local eval = n.eval +local eval, fn = n.eval, n.fn local is_os = t.is_os local pcall_err = t.pcall_err @@ -18,38 +18,26 @@ describe('executable()', function() eq(1, call('executable', 'false')) end) - if is_os('win') then - it('exepath returns consistent slashes #13787', function() - -- test/ cannot be a symlink in this test. - n.api.nvim_set_current_dir(t.paths.test_source_path) + it('exepath respects shellslash #13787', function() + t.skip(not is_os('win'), 'N/A for non-Windows') + -- test/ cannot be a symlink in this test. + n.api.nvim_set_current_dir(t.paths.test_source_path) - command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")') - eq( - [[test/functional/fixtures/bin/null.CMD]], - call('fnamemodify', call('exepath', 'null'), ':.') - ) - command('set shellslash') - eq( - 'test/functional/fixtures/bin/null.CMD', - call('fnamemodify', call('exepath', 'null'), ':.') - ) - end) + command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")') + eq(([[%s\test\functional\fixtures\bin\null.CMD]]):format(fn.getcwd()), fn.exepath('null')) + command('set shellslash') + eq(('%s/test/functional/fixtures/bin/null.CMD'):format(fn.getcwd()), fn.exepath('null')) + end) - it('stdpath returns consistent slashes #13787', function() - -- Needs to check paths relative to repo root dir. - n.api.nvim_set_current_dir(t.paths.test_source_path) + it('stdpath returns consistent slashes #13787', function() + t.skip(not is_os('win'), 'N/A for non-Windows') + -- Needs to check paths relative to repo root dir. + n.api.nvim_set_current_dir(t.paths.test_source_path) - t.matches( - [[build/Xtest_xdg[%w_]*/share/nvim%-data]], - call('fnamemodify', call('stdpath', 'data'), ':.') - ) - command('set shellslash') - t.matches( - 'build/Xtest_xdg[%w_]*/share/nvim%-data', - call('fnamemodify', call('stdpath', 'data'), ':.') - ) - end) - end + t.matches('build/Xtest_xdg[%w_]*/share/nvim%-data', fn.stdpath('data')) + command('set shellslash') + t.matches('build/Xtest_xdg[%w_]*/share/nvim%-data', fn.stdpath('data')) + end) it('fails for invalid values', function() for _, input in ipairs({ 'v:null', 'v:true', 'v:false', '{}', '[]' }) do diff --git a/test/functional/vimscript/fnamemodify_spec.lua b/test/functional/vimscript/fnamemodify_spec.lua index b74176a637..f2b4b4950f 100644 --- a/test/functional/vimscript/fnamemodify_spec.lua +++ b/test/functional/vimscript/fnamemodify_spec.lua @@ -10,10 +10,6 @@ local write_file = t.write_file local is_os = t.is_os local chdir = n.fn.chdir -local function eq_slashconvert(expected, got) - eq(t.fix_slashes(expected), t.fix_slashes(got)) -end - describe('fnamemodify()', function() setup(function() write_file('Xtest-fnamemodify.txt', [[foobar]]) @@ -42,7 +38,7 @@ describe('fnamemodify()', function() eq(root, fnamemodify([[/]], ':p')) local letter_colon = root:sub(1, 2) - local old_dir = t.fix_slashes(getcwd()) .. '/' + local old_dir = ('%s/'):format(getcwd()) local foo_dir = old_dir .. 'foo/' eq(old_dir, fnamemodify(letter_colon, ':p')) eq(old_dir, fnamemodify(letter_colon .. '.', ':p')) @@ -65,28 +61,28 @@ describe('fnamemodify()', function() n.api.nvim_set_current_dir(t.paths.test_source_path) local filename = 'src/version.c' - local cwd = getcwd() + local cwd = vim.fs.normalize(getcwd()) - eq_slashconvert(cwd .. '/src/version.c', fnamemodify(filename, ':p')) + eq(('%s/src/version.c'):format(cwd), fnamemodify(filename, ':p')) - eq_slashconvert('src/version.c', fnamemodify(filename, ':p:.')) - eq_slashconvert(cwd .. '/src', fnamemodify(filename, ':p:h')) - eq_slashconvert(cwd .. '', fnamemodify(filename, ':p:h:h')) + eq('src/version.c', fnamemodify(filename, ':p:.')) + eq(('%s/src'):format(cwd), fnamemodify(filename, ':p:h')) + eq(cwd, fnamemodify(filename, ':p:h:h')) eq('version.c', fnamemodify(filename, ':p:t')) - eq_slashconvert(cwd .. '/src/version', fnamemodify(filename, ':p:r')) + eq(('%s/src/version'):format(cwd), fnamemodify(filename, ':p:r')) - eq_slashconvert(cwd .. '/src/main.c', fnamemodify(filename, ':s?version?main?:p')) + eq(('%s/src/main.c'):format(cwd), fnamemodify(filename, ':s?version?main?:p')) local converted_cwd = cwd:gsub('/', '\\') eq(converted_cwd .. '\\src\\version.c', fnamemodify(filename, ':p:gs?/?\\\\?')) eq('src', fnamemodify(filename, ':h')) eq('version.c', fnamemodify(filename, ':t')) - eq_slashconvert('src/version', fnamemodify(filename, ':r')) + eq('src/version', fnamemodify(filename, ':r')) eq('version', fnamemodify(filename, ':t:r')) eq('c', fnamemodify(filename, ':e')) - eq_slashconvert('src/main.c', fnamemodify(filename, ':s?version?main?')) + eq('src/main.c', fnamemodify(filename, ':s?version?main?')) end) it('handles advanced examples from ":help filename-modifiers"', function() @@ -98,11 +94,11 @@ describe('fnamemodify()', function() eq('c', fnamemodify(filename, ':e:e:r')) - eq_slashconvert('src/version.c', fnamemodify(filename, ':r')) + eq('src/version.c', fnamemodify(filename, ':r')) eq('c', fnamemodify(filename, ':r:e')) - eq_slashconvert('src/version', fnamemodify(filename, ':r:r')) - eq_slashconvert('src/version', fnamemodify(filename, ':r:r:r')) + eq('src/version', fnamemodify(filename, ':r:r')) + eq('src/version', fnamemodify(filename, ':r:r:r')) end) it('handles :h', function() @@ -136,11 +132,11 @@ describe('fnamemodify()', function() eq('//server///share', fnamemodify('//server///share', ':h')) eq('//server/share/', fnamemodify('//server/share/foo', ':h')) - eq([[\\foo\C$]], fnamemodify([[\\foo\C$]], ':h')) - eq([[\\foo\C$\]], fnamemodify([[\\foo\C$\]], ':h')) - eq([[\\foo\C$\]], fnamemodify([[\\foo\C$\bar]], ':h')) - eq([[\\foo\C$\]], fnamemodify([[\\foo\C$\bar]], ':h:h')) - eq([[//foo\C$/]], fnamemodify([[//foo\C$/bar]], ':h')) + eq([[//foo/C$]], fnamemodify([[\\foo\C$]], ':h')) + eq([[//foo/C$/]], fnamemodify([[\\foo\C$\]], ':h')) + eq([[//foo/C$/]], fnamemodify([[\\foo\C$\bar]], ':h')) + eq([[//foo/C$/]], fnamemodify([[\\foo\C$\bar]], ':h:h')) + eq([[//foo/C$/]], fnamemodify([[//foo\C$/bar]], ':h')) -- `C$` is a share name, not a file name eq('', fnamemodify('//foo/C$/bar', ':h:t')) @@ -165,17 +161,17 @@ describe('fnamemodify()', function() it('handles :t', function() eq('hello.txt', fnamemodify('hello.txt', ':t')) - eq_slashconvert('hello.txt', fnamemodify('path/to/hello.txt', ':t')) + eq('hello.txt', fnamemodify('path/to/hello.txt', ':t')) end) it('handles :r', function() eq('hello', fnamemodify('hello.txt', ':r')) - eq_slashconvert('path/to/hello', fnamemodify('path/to/hello.txt', ':r')) + eq('path/to/hello', fnamemodify('path/to/hello.txt', ':r')) end) it('handles :e', function() eq('txt', fnamemodify('hello.txt', ':e')) - eq_slashconvert('txt', fnamemodify('path/to/hello.txt', ':e')) + eq('txt', fnamemodify('path/to/hello.txt', ':e')) end) it('handles regex replacements', function() diff --git a/test/old/testdir/test_expand_func.vim b/test/old/testdir/test_expand_func.vim index a6488f07f2..281e7c13e2 100644 --- a/test/old/testdir/test_expand_func.vim +++ b/test/old/testdir/test_expand_func.vim @@ -72,12 +72,12 @@ func Test_expand_sfile_and_stack() let g:stack3 = expand('') END call writefile(lines, 'Xshellslash/Xstack') - " Test that changing 'shellslash' doesn't affect the result of expand() + " Test that changing 'shellslash' always affects the result of expand() " when sourcing a script multiple times. for i in range(2) source Xshellslash/Xstack call assert_match('\