summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authortao <2471314@gmail.com>2026-04-25 01:20:25 +0800
committerGitHub <noreply@github.com>2026-04-24 13:20:25 -0400
commitf130922744657b731c24a842a85fd8a9f8058073 (patch)
tree9540b465b8a5d937638e2f9b8a7a0d2e920f593b
parent27191e0f4f4f9086180a8fbe3e52c1c280a70b09 (diff)
fix(path): normalize path slashes on Windows #37729
Problem: On Windows, path separators may become inconsistent for various reasons, which makes normalization quite painful. Solution: Normalize paths to `/` at the entry boundaries and always use it internally, converting back only in rare cases where `\` is really needed (e.g. cmd.exe/bat scripts?). This is the first commit in a series of incremental steps. Note: * some funcs won't respect shellslash. e.g. `expand/fnamemodify` * some funcs still respect shellslash, but will be updated in a follow PR. e.g. `ex_pwd/f_chdir/f_getcwd` * uv's built-in funcs always return `\`. e.g. `uv.cwd/uv.exepath` Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
-rw-r--r--src/nvim/arglist.c3
-rw-r--r--src/nvim/ascii_defs.h9
-rw-r--r--src/nvim/cmdexpand.c10
-rw-r--r--src/nvim/cmdexpand.h2
-rw-r--r--src/nvim/eval/fs.c8
-rw-r--r--src/nvim/event/libuv_proc.c3
-rw-r--r--src/nvim/ex_docmd.c6
-rw-r--r--src/nvim/file_search.c8
-rw-r--r--src/nvim/insexpand.c35
-rw-r--r--src/nvim/main.c3
-rw-r--r--src/nvim/memline.c2
-rw-r--r--src/nvim/option.c34
-rw-r--r--src/nvim/os/env.c17
-rw-r--r--src/nvim/os/fs.c8
-rw-r--r--src/nvim/os/pty_proc_win.c4
-rw-r--r--src/nvim/os/stdpaths.c36
-rw-r--r--src/nvim/path.c43
-rw-r--r--src/nvim/path.h10
-rw-r--r--src/nvim/quickfix.c7
-rw-r--r--src/nvim/runtime.c5
-rw-r--r--src/nvim/shada.c2
-rw-r--r--src/nvim/syntax.c2
-rw-r--r--src/nvim/tag.c16
-rw-r--r--test/functional/api/vim_spec.lua2
-rw-r--r--test/functional/core/path_spec.lua3
-rw-r--r--test/functional/core/startup_spec.lua2
-rw-r--r--test/functional/ex_cmds/mksession_spec.lua6
-rw-r--r--test/functional/ex_cmds/source_spec.lua15
-rw-r--r--test/functional/ex_cmds/verbose_spec.lua8
-rw-r--r--test/functional/legacy/097_glob_path_spec.lua23
-rw-r--r--test/functional/options/defaults_spec.lua20
-rw-r--r--test/functional/terminal/ex_terminal_spec.lua6
-rw-r--r--test/functional/ui/title_spec.lua12
-rw-r--r--test/functional/vimscript/buf_functions_spec.lua5
-rw-r--r--test/functional/vimscript/eval_spec.lua2
-rw-r--r--test/functional/vimscript/executable_spec.lua8
-rw-r--r--test/functional/vimscript/fnamemodify_spec.lua5
-rw-r--r--test/old/testdir/test_expand_func.vim4
-rw-r--r--test/old/testdir/test_functions.vim5
-rw-r--r--test/old/testdir/test_ins_complete.vim14
-rw-r--r--test/old/testdir/test_windows_home.vim2
41 files changed, 201 insertions, 214 deletions
diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c
index abe81202da..bf9883ce24 100644
--- a/src/nvim/arglist.c
+++ b/src/nvim/arglist.c
@@ -216,9 +216,6 @@ void alist_add(alist_T *al, char *fname, int set_fnum)
arglist_locked = true;
wp->w_locked++;
-#ifdef BACKSLASH_IN_FILENAME
- slash_adjust(fname);
-#endif
AARGLIST(al)[al->al_ga.ga_len].ae_fname = fname;
if (set_fnum > 0) {
AARGLIST(al)[al->al_ga.ga_len].ae_fnum =
diff --git a/src/nvim/ascii_defs.h b/src/nvim/ascii_defs.h
index 7bd0b21889..fb8546833f 100644
--- a/src/nvim/ascii_defs.h
+++ b/src/nvim/ascii_defs.h
@@ -75,13 +75,8 @@
#define Ctrl__ 31
// Character that separates dir names in a path.
-#ifdef BACKSLASH_IN_FILENAME
-# define PATHSEP psepc
-# define PATHSEPSTR pseps
-#else
-# define PATHSEP '/'
-# define PATHSEPSTR "/"
-#endif
+#define PATHSEP '/'
+#define PATHSEPSTR "/"
/// Checks if `c` is a space or tab character.
///
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index cfcc9c033e..9c30f516cf 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -316,6 +316,7 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
}
// Translate string into pattern and expand it.
const int use_options = (options
+ | WILD_USE_COMPLETESLASH
| WILD_HOME_REPLACE
| WILD_ADD_SLASH
| WILD_SILENT
@@ -2725,16 +2726,13 @@ static int expand_files_and_dirs(expand_T *xp, char *pat, char ***matches, int *
xfree(pat);
}
#ifdef BACKSLASH_IN_FILENAME
- if (p_csl[0] != NUL && (options & WILD_IGNORE_COMPLETESLASH) == 0) {
+ if ((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];
- while (*ptr != NUL) {
- if (p_csl[0] == 's' && *ptr == '\\') {
- *ptr = '/';
- } else if (p_csl[0] == 'b' && *ptr == '/') {
+ for (; *ptr; ptr++) {
+ if (*ptr == '/') {
*ptr = '\\';
}
- ptr += utfc_ptr2len(ptr);
}
}
}
diff --git a/src/nvim/cmdexpand.h b/src/nvim/cmdexpand.h
index 51b90b311e..198b178f71 100644
--- a/src/nvim/cmdexpand.h
+++ b/src/nvim/cmdexpand.h
@@ -36,7 +36,7 @@ enum {
WILD_ESCAPE = 0x80,
WILD_ICASE = 0x100,
WILD_ALLLINKS = 0x200,
- WILD_IGNORE_COMPLETESLASH = 0x400,
+ WILD_USE_COMPLETESLASH = 0x400,
WILD_NOERROR = 0x800, ///< sets EW_NOERROR
WILD_BUFLASTUSED = 0x1000,
BUF_DIFF_FILTER = 0x2000,
diff --git a/src/nvim/eval/fs.c b/src/nvim/eval/fs.c
index 7e4dc1801b..abec37c9c8 100644
--- a/src/nvim/eval/fs.c
+++ b/src/nvim/eval/fs.c
@@ -462,12 +462,6 @@ void f_exepath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
os_can_exe(tv_get_string(&argvars[0]), &path, true);
-#ifdef BACKSLASH_IN_FILENAME
- if (path != NULL) {
- slash_adjust(path);
- }
-#endif
-
rettv->v_type = VAR_STRING;
rettv->vval.v_string = path;
}
@@ -878,7 +872,7 @@ void f_glob(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "globpath()" function
void f_globpath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- int flags = WILD_IGNORE_COMPLETESLASH; // Flags for globpath.
+ int flags = 0; // Flags for globpath.
bool error = false;
// Return a string, or a list if the optional third argument is non-zero.
diff --git a/src/nvim/event/libuv_proc.c b/src/nvim/event/libuv_proc.c
index 01076670c3..c12e7b0509 100644
--- a/src/nvim/event/libuv_proc.c
+++ b/src/nvim/event/libuv_proc.c
@@ -11,6 +11,7 @@
#include "nvim/log.h"
#include "nvim/os/os.h"
#include "nvim/os/os_defs.h"
+#include "nvim/path.h"
#include "nvim/types_defs.h"
#include "nvim/ui_client.h"
@@ -29,6 +30,8 @@ int libuv_proc_spawn(LibuvProc *uvproc)
// expects a different syntax (must be prepared by the caller before now).
if (os_shell_is_cmdexe(proc->argv[0])) {
uvproc->uvopts.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS;
+ // cmd.exe compatibility: backslashes required for path.
+ TO_BACKSLASH(proc->argv[0]);
}
if (proc->detach) {
uvproc->uvopts.flags |= UV_PROCESS_DETACHED;
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 3a761136c0..373e111eec 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -4145,6 +4145,8 @@ int expand_filename(exarg_T *eap, char **cmdlinep, const char **errormsgp)
}
repl_cmdline(eap, eap->arg, strlen(eap->arg), p, cmdlinep);
xfree(p);
+ } else {
+ TO_SLASH(eap->arg);
}
}
return OK;
@@ -5673,7 +5675,7 @@ int expand_findfunc(char *pat, char ***files, int *numMatches)
int idx = 0;
TV_LIST_ITER_CONST(l, li, {
if (TV_LIST_ITEM_TV(li)->v_type == VAR_STRING) {
- (*files)[idx] = xstrdup(TV_LIST_ITEM_TV(li)->vval.v_string);
+ (*files)[idx] = TO_SLASH_SAVE(TV_LIST_ITEM_TV(li)->vval.v_string);
idx++;
}
});
@@ -5704,7 +5706,7 @@ static char *findfunc_find_file(char *findarg, size_t findarg_len, int count)
} else {
listitem_T *li = tv_list_find(fname_list, count - 1);
if (li != NULL && TV_LIST_ITEM_TV(li)->v_type == VAR_STRING) {
- ret_fname = xstrdup(TV_LIST_ITEM_TV(li)->vval.v_string);
+ ret_fname = TO_SLASH_SAVE(TV_LIST_ITEM_TV(li)->vval.v_string);
}
}
}
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index a043da3f17..66254a15f1 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -1593,6 +1593,7 @@ theend:
char *grab_file_name(int count, linenr_T *file_lnum)
{
int options = FNAME_MESS | FNAME_EXP | FNAME_REL | FNAME_UNESC;
+ char *fname;
if (VIsual_active) {
size_t len;
char *ptr;
@@ -1605,9 +1606,12 @@ char *grab_file_name(int count, linenr_T *file_lnum)
*file_lnum = getdigits_int32(&p, false, 0);
}
- return find_file_name_in_path(ptr, len, options, count, curbuf->b_ffname);
+ fname = find_file_name_in_path(ptr, len, options, count, curbuf->b_ffname);
+ } else {
+ fname = file_name_at_cursor(options | FNAME_HYP, count, file_lnum);
}
- return file_name_at_cursor(options | FNAME_HYP, count, file_lnum);
+ TO_SLASH(fname);
+ return fname;
}
/// Return the file name under or after the cursor.
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index 97b84a0e8b..ce676a8639 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -4038,30 +4038,9 @@ static void get_next_filename_completion(void)
int max_score = 0;
Direction dir = compl_direction;
-#ifdef BACKSLASH_IN_FILENAME
- char pathsep = (curbuf->b_p_csl[0] == 's')
- ? '/' : (curbuf->b_p_csl[0] == 'b') ? '\\' : PATHSEP;
-#else
- char pathsep = PATHSEP;
-#endif
-
if (in_fuzzy_collect) {
-#ifdef BACKSLASH_IN_FILENAME
- if (curbuf->b_p_csl[0] == 's') {
- for (size_t i = 0; i < leader_len; i++) {
- if (leader[i] == '\\') {
- leader[i] = '/';
- }
- }
- } else if (curbuf->b_p_csl[0] == 'b') {
- for (size_t i = 0; i < leader_len; i++) {
- if (leader[i] == '/') {
- leader[i] = '\\';
- }
- }
- }
-#endif
- char *last_sep = strrchr(leader, pathsep);
+ TO_SLASH(leader);
+ char *last_sep = strrchr(leader, PATHSEP);
if (last_sep == NULL) {
// No path separator or separator is the last character,
// fuzzy match the whole leader
@@ -4093,16 +4072,12 @@ static void get_next_filename_completion(void)
// May change home directory back to "~".
tilde_replace(compl_pattern.data, num_matches, matches);
#ifdef BACKSLASH_IN_FILENAME
- if (curbuf->b_p_csl[0] != NUL) {
+ if ((curbuf->b_p_csl[0] == NUL && !p_ssl) || curbuf->b_p_csl[0] == 'b') {
for (int i = 0; i < num_matches; i++) {
- char *ptr = matches[i];
- while (*ptr != NUL) {
- if (curbuf->b_p_csl[0] == 's' && *ptr == '\\') {
- *ptr = '/';
- } else if (curbuf->b_p_csl[0] == 'b' && *ptr == '/') {
+ for (char *ptr = matches[i]; *ptr; ptr++) {
+ if (*ptr == '/') {
*ptr = '\\';
}
- ptr += utfc_ptr2len(ptr);
}
}
}
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 08e741b3a7..a1320648a4 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -256,6 +256,7 @@ int main(int argc, char **argv)
#endif
{
argv0 = argv[0];
+ TO_SLASH(argv0);
if (!appname_is_valid()) {
fprintf(stderr, "$NVIM_APPNAME must be a name or relative path.\n");
@@ -1461,6 +1462,7 @@ scripterror:
break;
case 'u': // "-u {vimrc}" vim inits file
parmp->use_vimrc = argv[0];
+ TO_SLASH(argv[0]);
break;
case 'U': // "-U {gvimrc}" gvim inits file
break;
@@ -1505,6 +1507,7 @@ scripterror:
xfree(p);
p = tilde_expanded;
}
+ TO_SLASH(p);
#endif
if (parmp->diff_mode && os_isdir(p) && GARGCOUNT > 0
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index b5ac0cdb28..334ab935d2 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -913,6 +913,7 @@ void ml_recover(bool checkext)
// If .swp file name given directly, use name from swapfile for buffer.
if (directly) {
+ TO_SLASH(b0p->b0_fname);
expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
if (setfname(curbuf, NameBuff, NULL, true) == FAIL) {
goto theend;
@@ -3510,6 +3511,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
fd = os_open(fname, O_RDONLY, 0);
if (fd >= 0) {
if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) {
+ TO_SLASH(b0.b0_fname);
proc_running = swapfile_proc_running(&b0, fname);
// If the swapfile has the same directory as the
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 4644fba49a..a392412d53 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -365,7 +365,7 @@ void set_init_1(bool clean_arg)
memmove(backupdir + 2, backupdir, backupdir_len + 1);
memmove(backupdir, ".,", 2);
set_string_default(kOptBackupdir, backupdir, true);
- set_string_default(kOptViewdir, stdpaths_user_state_subpath("view", 2, true),
+ set_string_default(kOptViewdir, stdpaths_user_state_subpath("view", 2, false),
true);
set_string_default(kOptDirectory, stdpaths_user_state_subpath("swap", 2, true),
true);
@@ -2650,6 +2650,8 @@ static const char *did_set_scrollbind(optset_T *args)
#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) {
@@ -2662,10 +2664,11 @@ static const char *did_set_shellslash(optset_T *args FUNC_ATTR_UNUSED)
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();
+ // buflist_slash_adjust();
+ // alist_slash_adjust();
+ // scriptnames_slash_adjust();
return NULL;
}
#endif
@@ -3903,6 +3906,29 @@ static const char *set_option(const OptIndex opt_idx, OptVal value, int opt_flag
}
}
+#ifdef BACKSLASH_IN_FILENAME
+ // Ensure "/" slashes in various options.
+ uint32_t flags = options[opt_idx].flags;
+ if ((flags & kOptFlagExpand)
+ && opt_idx != kOptEqualprg
+ && opt_idx != kOptFormatprg
+ && opt_idx != kOptGrepprg
+ && opt_idx != kOptKeywordprg
+ && opt_idx != kOptMakeprg
+ && opt_idx != kOptShell) {
+ bool allow_comma = flags & kOptFlagComma;
+ bool allow_space = (opt_idx == kOptCdpath || opt_idx == kOptPath || opt_idx == kOptTags);
+ for (char *p = value.data.string.data; *p; p++) {
+ if (*p != '\\'
+ || (p[1] == ',' && allow_comma)
+ || (p[1] == ' ' && allow_space)) {
+ continue;
+ }
+ *p = '/';
+ }
+ }
+#endif
+
vimoption_T *opt = &options[opt_idx];
const bool scope_local = opt_flags & OPT_LOCAL;
const bool scope_global = opt_flags & OPT_GLOBAL;
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index 329e6ebadb..8e21a8c7db 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -618,6 +618,7 @@ size_t expand_env_esc(const char *restrict srcp, char *restrict dst, int dstlen,
#endif
*var = NUL;
var = vim_getenv(dst);
+ TO_SLASH(var);
mustfree = true;
#ifdef UNIX
}
@@ -661,21 +662,6 @@ size_t expand_env_esc(const char *restrict srcp, char *restrict dst, int dstlen,
#endif // UNIX
}
-#ifdef BACKSLASH_IN_FILENAME
- // If 'shellslash' is set change backslashes to forward slashes.
- // Can't use slash_adjust(), p_ssl may be set temporarily.
- if (p_ssl && var != NULL && vim_strchr(var, '\\') != NULL) {
- char *p = xstrdup(var);
-
- if (mustfree) {
- xfree(var);
- }
- var = p;
- mustfree = true;
- forward_slash(var);
- }
-#endif
-
// If "var" contains white space, escape it with a backslash.
// Required for ":e ~/tt" when $HOME includes a space.
if (esc && var != NULL && strpbrk(var, " \t") != NULL) {
@@ -1034,6 +1020,7 @@ size_t home_replace(const buf_T *const buf, const char *src, char *const dst, si
homedir_env = os_getenv("USERPROFILE");
}
#endif
+ TO_SLASH(homedir_env);
char *homedir_env_mod = homedir_env;
bool must_free = false;
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 032e4e32f0..1d1f26349d 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -113,6 +113,7 @@ int os_dirname(char *buf, size_t len)
xstrlcpy(buf, uv_strerror(error_number), len);
return FAIL;
}
+ TO_SLASH(buf);
return OK;
}
@@ -224,7 +225,9 @@ int os_nodetype(const char *name)
int os_exepath(char *buffer, size_t *size)
FUNC_ATTR_NONNULL_ALL
{
- return uv_exepath(buffer, size);
+ int r = uv_exepath(buffer, size);
+ TO_SLASH(buffer);
+ return r;
}
/// Checks if the file `name` is executable.
@@ -1112,6 +1115,7 @@ int os_mkdtemp(const char *templ, char *path)
int result = uv_fs_mkdtemp(NULL, &request, templ, NULL);
if (result == kLibuvSuccess) {
xstrlcpy(path, request.path, TEMP_FILE_PATH_MAXLEN);
+ TO_SLASH(path);
}
uv_fs_req_cleanup(&request);
return result;
@@ -1345,6 +1349,7 @@ char *os_realpath(const char *name, char *buf, size_t len)
buf = xmalloc(len);
}
xstrlcpy(buf, request.ptr, len);
+ TO_SLASH(buf);
}
uv_fs_req_cleanup(&request);
return result == kLibuvSuccess ? buf : NULL;
@@ -1430,6 +1435,7 @@ shortcut_end:
}
CoUninitialize();
+ TO_SLASH(rfname);
return rfname;
}
diff --git a/src/nvim/os/pty_proc_win.c b/src/nvim/os/pty_proc_win.c
index 14aacc9800..8cc66bf319 100644
--- a/src/nvim/os/pty_proc_win.c
+++ b/src/nvim/os/pty_proc_win.c
@@ -11,6 +11,7 @@
#include "nvim/os/os.h"
#include "nvim/os/pty_conpty_win.h"
#include "nvim/os/pty_proc_win.h"
+#include "nvim/path.h"
#include "os/pty_proc_win.c.generated.h"
@@ -261,6 +262,9 @@ static int build_cmd_line(char **argv, wchar_t **cmd_line, bool is_cmdexe)
arg_node->arg = xmalloc(buf_len);
if (is_cmdexe) {
xstrlcpy(arg_node->arg, *argv, buf_len);
+ if (argc == 0) {
+ TO_BACKSLASH(arg_node->arg);
+ }
} else {
quote_cmd_arg(arg_node->arg, buf_len, *argv);
}
diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c
index 19f541d252..907f7259c8 100644
--- a/src/nvim/os/stdpaths.c
+++ b/src/nvim/os/stdpaths.c
@@ -43,10 +43,10 @@ static const char *const xdg_defaults_env_vars[] = {
/// Used in case environment variables contain nothing. Need to be expanded.
static const char *const xdg_defaults[] = {
#ifdef MSWIN
- [kXDGConfigHome] = "~\\AppData\\Local",
- [kXDGDataHome] = "~\\AppData\\Local",
- [kXDGCacheHome] = "~\\AppData\\Local\\Temp",
- [kXDGStateHome] = "~\\AppData\\Local",
+ [kXDGConfigHome] = "~/AppData/Local",
+ [kXDGDataHome] = "~/AppData/Local",
+ [kXDGCacheHome] = "~/AppData/Local/Temp",
+ [kXDGStateHome] = "~/AppData/Local",
[kXDGRuntimeDir] = NULL, // Decided by vim_mktempdir().
[kXDGConfigDirs] = NULL,
[kXDGDataDirs] = NULL,
@@ -166,6 +166,7 @@ char *stdpaths_get_xdg_var(const XDGVarType idx)
env_val = xstrdup("");
}
#endif
+ TO_SLASH(env_val);
char *ret = NULL;
if (env_val != NULL) {
@@ -213,10 +214,6 @@ char *get_xdg_home(const XDGVarType idx)
}
#endif
dir = concat_fnames_realloc(dir, IObuff, true);
-
-#ifdef BACKSLASH_IN_FILENAME
- slash_adjust(dir);
-#endif
}
return dir;
}
@@ -266,21 +263,22 @@ char *stdpaths_user_state_subpath(const char *fname, const size_t trailing_paths
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
{
char *ret = concat_fnames_realloc(get_xdg_home(kXDGStateHome), fname, true);
- const size_t len = strlen(ret);
+ size_t len = strlen(ret);
const size_t numcommas = (escape_commas ? memcnt(ret, ',', len) : 0);
if (numcommas || trailing_pathseps) {
- ret = xrealloc(ret, len + trailing_pathseps + numcommas + 1);
- for (size_t i = 0; i < len + numcommas; i++) {
- if (ret[i] == ',') {
- memmove(ret + i + 1, ret + i, len - i + numcommas);
- ret[i] = '\\';
- i++;
+ size_t newlen = len + numcommas + trailing_pathseps;
+ ret = xrealloc(ret, newlen + 1);
+ ret[newlen] = NUL;
+
+ memset(ret + len + numcommas, PATHSEP, trailing_pathseps);
+ newlen -= trailing_pathseps;
+
+ while (numcommas && len > 0) {
+ ret[--newlen] = ret[--len];
+ if (ret[newlen] == ',') {
+ ret[--newlen] = '\\';
}
}
- if (trailing_pathseps) {
- memset(ret + len + numcommas, PATHSEP, trailing_pathseps);
- }
- ret[len + trailing_pathseps + numcommas] = NUL;
}
return ret;
}
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 999194895b..295993f971 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -537,7 +537,7 @@ char *FullName_save(const char *fname, bool force)
char *buf = xmalloc(MAXPATHL);
if (vim_FullName(fname, buf, MAXPATHL, force) == FAIL) {
xfree(buf);
- return xstrdup(fname);
+ return TO_SLASH_SAVE(fname);
}
return buf;
}
@@ -551,7 +551,7 @@ char *save_abs_path(const char *name)
if (!path_is_absolute(name)) {
return FullName_save(name, true);
}
- return xstrdup(name);
+ return TO_SLASH_SAVE(name);
}
/// Checks if a path has a wildcard character including '~', unless at the end.
@@ -1516,6 +1516,30 @@ void slash_adjust(char *p)
}
#endif
+/// Convert all slashes to backslashes in-place.
+char *path_to_backslash(char *p)
+{
+ if (p != NULL) {
+ strchrsub(p, PATHSEP, '\\');
+ }
+ return p;
+}
+
+/// Convert all backslashes to forward slashes in-place.
+char *path_to_slash(char *p)
+{
+ if (p != NULL) {
+ strchrsub(p, '\\', PATHSEP);
+ }
+ return p;
+}
+
+/// Get an allocated copy of path to convert backslashes.
+char *path_to_slash_save(const char *p)
+{
+ return p == NULL ? NULL : path_to_slash(xstrdup(p));
+}
+
/// Add a file to a file list. Accepted flags:
/// EW_DIR add directories
/// EW_FILE add files
@@ -1561,9 +1585,7 @@ void addfile(garray_T *gap, char *f, int flags)
char *p = xmalloc(strlen(f) + 1 + isdir);
STRCPY(p, f);
-#ifdef BACKSLASH_IN_FILENAME
- slash_adjust(p);
-#endif
+ TO_SLASH(p);
// Append a slash or backslash after directory names if none is present.
if (isdir && (flags & EW_ADDSLASH)) {
add_pathsep(p);
@@ -1849,9 +1871,7 @@ int vim_FullName(const char *fname, char *buf, size_t len, bool force)
if (strlen(fname) > (len - 1)) {
xstrlcpy(buf, fname, len); // truncate
-#ifdef MSWIN
- slash_adjust(buf);
-#endif
+ TO_SLASH(buf);
return FAIL;
}
@@ -1864,9 +1884,7 @@ int vim_FullName(const char *fname, char *buf, size_t len, bool force)
if (rv == FAIL) {
xstrlcpy(buf, fname, len); // something failed; use the filename
}
-#ifdef MSWIN
- slash_adjust(buf);
-#endif
+ TO_SLASH(buf);
return rv;
}
@@ -1899,7 +1917,7 @@ char *fix_fname(const char *fname)
return FullName_save(fname, false);
}
- fname = xstrdup(fname);
+ fname = TO_SLASH_SAVE(fname);
# ifdef CASE_INSENSITIVE_FILENAME
path_fix_case((char *)fname); // set correct case for file name
@@ -2476,6 +2494,7 @@ void path_guess_exepath(const char *argv0, char *buf, size_t bufsize)
xstrlcat(NameBuff, argv0, sizeof(NameBuff));
if (os_can_exe(NameBuff, NULL, false)) {
xstrlcpy(buf, NameBuff, bufsize);
+ TO_SLASH(buf);
return;
}
} while (iter != NULL);
diff --git a/src/nvim/path.h b/src/nvim/path.h
index ff898d3550..48f5250e53 100644
--- a/src/nvim/path.h
+++ b/src/nvim/path.h
@@ -41,4 +41,14 @@ typedef enum file_comparison {
kEqualFileNames = 7, ///< Both don't exist and file names are same.
} FileComparison;
+#ifdef BACKSLASH_IN_FILENAME
+# define TO_SLASH(p) path_to_slash(p)
+# define TO_SLASH_SAVE(p) path_to_slash_save(p)
+# define TO_BACKSLASH(p) path_to_backslash(p)
+#else
+# define TO_SLASH(...)
+# define TO_SLASH_SAVE(p) xstrdup(p)
+# define TO_BACKSLASH(...)
+#endif
+
#include "path.h.generated.h"
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 9e90aabc5d..4a2f158232 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -2314,12 +2314,6 @@ static int qf_get_fnum(qf_list_T *qfl, char *directory, char *fname)
return 0;
}
-#ifdef BACKSLASH_IN_FILENAME
- if (directory != NULL) {
- slash_adjust(directory);
- }
- slash_adjust(fname);
-#endif
String fname_str = cstr_as_string(fname);
if (directory != NULL && !vim_isAbsName(fname)) {
ptr = concat_fnames(cstr_as_string(directory), fname_str, true);
@@ -2341,6 +2335,7 @@ static int qf_get_fnum(qf_list_T *qfl, char *directory, char *fname)
} else {
bufname = fname_str;
}
+ TO_SLASH(bufname.data);
if (qf_last_bufname != NULL && strcmp(bufname.data, qf_last_bufname) == 0
&& bufref_valid(&qf_last_bufref)) {
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index e8fbda49b9..ec3c8ac9d8 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -710,6 +710,7 @@ static ArrayOf(String) runtime_get_named_common(bool lua, Array pat, bool all,
item->path, pat_item.data.string.data);
if (size < buf_len) {
if (os_file_is_readable(buf)) {
+ TO_SLASH(buf);
ADD_C(rv, CSTR_TO_ARENA_OBJ(arena, buf));
if (!all) {
goto done;
@@ -1843,10 +1844,11 @@ char *runtimepath_default(bool clean_arg)
char *const config_home = clean_arg
? NULL
: stdpaths_get_xdg_var(kXDGConfigHome);
- char *const vimruntime = vim_getenv("VIMRUNTIME");
char *const libdir = get_lib_dir();
char *const data_dirs = stdpaths_get_xdg_var(kXDGDataDirs);
char *const config_dirs = stdpaths_get_xdg_var(kXDGConfigDirs);
+ char *vimruntime = vim_getenv("VIMRUNTIME");
+ TO_SLASH(vimruntime);
#define SITE_SIZE (sizeof("site") - 1)
#define AFTER_SIZE (sizeof("after") - 1)
size_t data_len = 0;
@@ -2537,6 +2539,7 @@ void ex_scriptnames(exarg_T *eap)
} else {
expand_env(eap->arg, NameBuff, MAXPATHL);
eap->arg = NameBuff;
+ TO_SLASH(eap->arg);
}
do_exedit(eap, NULL);
}
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index afeecd9542..7034c84908 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -3272,6 +3272,7 @@ shada_read_next_item_start:
}
if (HAS_KEY(&it, _shada_mark, f)) {
entry->data.filemark.fname = xmemdupz(it.f.data, it.f.size);
+ TO_SLASH(entry->data.filemark.fname);
}
if (entry->data.filemark.fname == NULL) {
@@ -3458,6 +3459,7 @@ shada_read_next_item_start:
}
if (HAS_KEY(&it, _shada_buflist_item, f)) {
e->fname = xmemdupz(it.f.data, it.f.size);
+ TO_SLASH(e->fname);
}
if (e->pos.lnum <= 0) {
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 9dd3231b48..29f2d1de9f 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -3985,6 +3985,8 @@ static void syn_cmd_include(exarg_T *eap, int syncing)
}
return;
}
+ } else {
+ TO_SLASH(eap->arg);
}
// Save and restore the existing top-level grouplist id and ":syn
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 78f2a53f7b..dc4c59e767 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -2017,11 +2017,6 @@ static void findtags_add_match(findtags_state_T *st, tagptrs_T *tagpp, findtags_
char *p = mfp;
p[0] = (char)(mtt + 1);
STRCPY(p + 1, st->tag_fname);
-#ifdef BACKSLASH_IN_FILENAME
- // Ignore differences in slashes, avoid adding
- // both path/file and path\file.
- slash_adjust(p + 1);
-#endif
p[tag_fname_len + 1] = TAG_SEP;
char *s = p + 1 + tag_fname_len + 1;
STRCPY(s, st->lbuf);
@@ -2438,9 +2433,6 @@ static bool found_tagfile_cb(int num_fnames, char **fnames, bool all, void *cook
for (int i = 0; i < num_fnames; i++) {
char *const tag_fname = xstrdup(fnames[i]);
-#ifdef BACKSLASH_IN_FILENAME
- slash_adjust(tag_fname);
-#endif
simplify_filename(tag_fname);
GA_APPEND(char *, &tag_fnames, tag_fname);
@@ -2502,9 +2494,6 @@ int get_tagfname(tagname_T *tnp, int first, char *buf)
tnp->tn_hf_idx++;
xstrlcpy(buf, p_hf, MAXPATHL - STRLEN_LITERAL("tags"));
STRCPY(path_tail(buf), "tags");
-#ifdef BACKSLASH_IN_FILENAME
- slash_adjust(buf);
-#endif
simplify_filename(buf);
for (int i = 0; i < tag_fnames.ga_len; i++) {
@@ -3075,6 +3064,8 @@ static char *expand_tag_fname(char *fname, char *const tag_fname, const bool exp
char *expanded_fname = NULL;
expand_T xpc;
+ fname = TO_SLASH_SAVE(fname);
+
// Expand file name (for environment variables) when needed.
// Disallow backticks, they could execute arbitrary shell
// commands. This is not needed for tag filenames.
@@ -3084,6 +3075,7 @@ static char *expand_tag_fname(char *fname, char *const tag_fname, const bool exp
expanded_fname = ExpandOne(&xpc, fname, NULL,
WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE);
if (expanded_fname != NULL) {
+ xfree(fname);
fname = expanded_fname;
}
}
@@ -3101,7 +3093,7 @@ static char *expand_tag_fname(char *fname, char *const tag_fname, const bool exp
retval = xstrdup(fname);
}
- xfree(expanded_fname);
+ xfree(fname);
return retval;
}
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index fc91a655e0..74434225fc 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -5395,7 +5395,7 @@ describe('API', function()
end, { nargs = 1 })
]])
eq(
- uv.cwd(),
+ t.fix_slashes(assert(uv.cwd())),
api.nvim_cmd(
{ cmd = 'Foo', args = { '%:p:h' }, magic = { file = true } },
{ output = true }
diff --git a/test/functional/core/path_spec.lua b/test/functional/core/path_spec.lua
index 9128cd8aab..8a48cfb7d5 100644
--- a/test/functional/core/path_spec.lua
+++ b/test/functional/core/path_spec.lua
@@ -14,8 +14,7 @@ local rmdir = n.rmdir
local write_file = t.write_file
local function join_path(...)
- local pathsep = (is_os('win') and '\\' or '/')
- return table.concat({ ... }, pathsep)
+ return table.concat({ ... }, '/')
end
describe('path collapse', function()
diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua
index c627a69f33..f7e167f10b 100644
--- a/test/functional/core/startup_spec.lua
+++ b/test/functional/core/startup_spec.lua
@@ -1628,7 +1628,7 @@ end)
describe('runtime:', function()
local xhome = 'Xhome'
- local pathsep = n.get_pathsep()
+ local pathsep = '/'
local xconfig = xhome .. pathsep .. 'Xconfig'
local xdata = xhome .. pathsep .. 'Xdata'
local xenv = { XDG_CONFIG_HOME = xconfig, XDG_DATA_HOME = xdata }
diff --git a/test/functional/ex_cmds/mksession_spec.lua b/test/functional/ex_cmds/mksession_spec.lua
index 71961a2f7b..d205881fd3 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 = fn.getcwd()
+ local cwd_dir = t.fix_slashes(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 .. get_pathsep() .. tmpfile_base .. '1', fn.expand('%:p'))
+ eq(cwd_dir .. '/' .. tmpfile_base .. '1', fn.expand('%:p'))
command('tabnext 2')
- eq(cwd_dir .. get_pathsep() .. tmpfile_base .. '2', fn.expand('%:p'))
+ eq(cwd_dir .. '/' .. tmpfile_base .. '2', fn.expand('%:p'))
end)
it('restores CWD for :terminal buffers #11288', function()
diff --git a/test/functional/ex_cmds/source_spec.lua b/test/functional/ex_cmds/source_spec.lua
index f7351dbf0c..7ce5c95fa6 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' changes the result of expand()", function()
+ it("changing 'shellslash' doesn't affect 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')
@@ -326,14 +326,13 @@ describe(':source', function()
end)
it('$HOME is not shortened in filepath in v:stacktrace from sourced file', function()
- local sep = n.get_pathsep()
- local xhome = table.concat({ vim.uv.cwd(), 'Xhome' }, sep)
+ local xhome = t.fix_slashes(assert(vim.uv.cwd())) .. '/Xhome'
mkdir(xhome)
clear({ env = { HOME = xhome } })
finally(function()
rmdir(xhome)
end)
- local filepath = table.concat({ xhome, 'Xstacktrace.vim' }, sep)
+ local filepath = xhome .. '/Xstacktrace.vim'
local script = [[
func Xfunc()
throw 'Exception from Xfunc'
diff --git a/test/functional/ex_cmds/verbose_spec.lua b/test/functional/ex_cmds/verbose_spec.lua
index 67ce9d894c..148059c12a 100644
--- a/test/functional/ex_cmds/verbose_spec.lua
+++ b/test/functional/ex_cmds/verbose_spec.lua
@@ -17,9 +17,9 @@ 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 = fn.getcwd()
+ local current_dir = assert(t.fix_slashes(fn.getcwd()))
current_dir = fn.fnamemodify(current_dir, ':~')
- script_location = table.concat({ current_dir, n.get_pathsep(), script_file })
+ script_location = current_dir .. '/' .. script_file
write_file(
script_file,
@@ -326,9 +326,9 @@ describe(':verbose when using API from Vimscript', function()
setup(function()
clear()
script_file = 'test_verbose.vim'
- local current_dir = fn.getcwd()
+ local current_dir = assert(t.fix_slashes(fn.getcwd()))
current_dir = fn.fnamemodify(current_dir, ':~')
- script_location = table.concat({ current_dir, n.get_pathsep(), script_file })
+ script_location = current_dir .. '/' .. script_file
write_file(
script_file,
diff --git a/test/functional/legacy/097_glob_path_spec.lua b/test/functional/legacy/097_glob_path_spec.lua
index 4d4bb9e78f..b2d22e2a89 100644
--- a/test/functional/legacy/097_glob_path_spec.lua
+++ b/test/functional/legacy/097_glob_path_spec.lua
@@ -41,15 +41,6 @@ describe('glob() and globpath()', function()
command([[$put =string(globpath('sautest\autoload', '*.vim'))]])
command([[$put =string(globpath('sautest\autoload', '*.vim', 0, 1))]])
- expect([=[
-
-
-
- Xxx{
- Xxx$
- 'sautest\autoload\Test104.vim
- sautest\autoload\footest.vim'
- ['sautest\autoload\Test104.vim', 'sautest\autoload\footest.vim']]=])
else
command([[$put =glob('Xxx\{')]])
command([[$put =glob('Xxx\$')]])
@@ -61,16 +52,16 @@ describe('glob() and globpath()', function()
command("$put =string(globpath('sautest/autoload', '*.vim'))")
command("$put =string(globpath('sautest/autoload', '*.vim', 0, 1))")
- expect([=[
+ end
+ expect([=[
- Xxx{
- Xxx$
- 'sautest/autoload/Test104.vim
- sautest/autoload/footest.vim'
- ['sautest/autoload/Test104.vim', 'sautest/autoload/footest.vim']]=])
- end
+ Xxx{
+ Xxx$
+ 'sautest/autoload/Test104.vim
+ sautest/autoload/footest.vim'
+ ['sautest/autoload/Test104.vim', 'sautest/autoload/footest.vim']]=])
end)
teardown(function()
diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua
index ca8bbd370e..f87cfa747d 100644
--- a/test/functional/options/defaults_spec.lua
+++ b/test/functional/options/defaults_spec.lua
@@ -291,10 +291,8 @@ describe('XDG defaults', function()
clear()
local rtp = eval('split(&runtimepath, ",")')
local rv = {}
- local expected = (
- is_os('win') and { [[\nvim-data\site]], [[\nvim-data\site\after]] }
- or { '/nvim/site', '/nvim/site/after' }
- )
+ local data_dir = is_os('win') and '/nvim-data' or '/nvim'
+ local expected = { data_dir .. '/site', data_dir .. '/site/after' }
for _, v in ipairs(rtp) do
local m = string.match(v, [=[[/\]nvim[^/\]*[/\]site.*$]=])
@@ -695,7 +693,7 @@ describe('XDG defaults', function()
it('are escaped properly', function()
local vimruntime, libdir = vimruntime_and_libdir()
- local path_sep = is_os('win') and '\\' or '/'
+ local path_sep = '/'
eq(
(
'\\, \\, \\,'
@@ -869,17 +867,7 @@ describe('XDG defaults', function()
.. (path_sep):rep(2),
api.nvim_get_option_value('undodir', {})
)
- eq(
- '\\,=\\,=\\,'
- .. path_sep
- .. ''
- .. state_dir
- .. ''
- .. path_sep
- .. 'view'
- .. (path_sep):rep(2),
- api.nvim_get_option_value('viewdir', {})
- )
+ eq(',=,=,/' .. state_dir .. '/view//', api.nvim_get_option_value('viewdir', {}))
end)
end)
end)
diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua
index d6ecd816e1..62f238e6b5 100644
--- a/test/functional/terminal/ex_terminal_spec.lua
+++ b/test/functional/terminal/ex_terminal_spec.lua
@@ -314,11 +314,7 @@ local function test_terminal_with_fake_shell(backslash)
eq('term://', string.match(eval('bufname("%")'), '^term://'))
feed([[<C-\><C-N>]])
command([[find */Xuniquefile]])
- if is_os('win') then
- eq('Xsomedir\\Xuniquefile', eval('bufname("%")'))
- else
- eq('Xsomedir/Xuniquefile', eval('bufname("%")'))
- end
+ eq('Xsomedir/Xuniquefile', eval('bufname("%")'))
end)
it('works with gf', function()
diff --git a/test/functional/ui/title_spec.lua b/test/functional/ui/title_spec.lua
index 1004c1e77b..e2a0881569 100644
--- a/test/functional/ui/title_spec.lua
+++ b/test/functional/ui/title_spec.lua
@@ -21,7 +21,7 @@ describe('title', function()
end)
it('defaults to "No Name" and the PWD in the title if the buffer is unnamed', function()
- local expected = (is_os('win') and '[No Name] (C:\\) - Nvim' or '[No Name] (/) - Nvim')
+ local expected = (is_os('win') and '[No Name] (C:/) - Nvim' or '[No Name] (/) - Nvim')
command(is_os('win') and 'cd C:\\' or 'cd /')
command('set title')
screen:expect(function()
@@ -32,7 +32,7 @@ describe('title', function()
it(
'defaults to the filename and its directory in the title if the buffer is named as a path outside the PWD',
function()
- local expected = (is_os('win') and 'myfile (C:\\mydir) - Nvim' or 'myfile (/mydir) - Nvim')
+ local expected = (is_os('win') and 'myfile (C:/mydir) - Nvim' or 'myfile (/mydir) - Nvim')
command('set title')
command(is_os('win') and 'file C:\\mydir\\myfile' or 'file /mydir/myfile')
screen:expect(function()
@@ -44,7 +44,7 @@ describe('title', function()
it(
'defaults to the filename and the PWD in the title if the buffer is a file in the PWD',
function()
- local expected = (is_os('win') and 'myfile (C:\\) - Nvim' or 'myfile (/) - Nvim')
+ local expected = (is_os('win') and 'myfile (C:/) - Nvim' or 'myfile (/) - Nvim')
command('set title')
command(is_os('win') and 'cd C:\\' or 'cd /')
command('file myfile')
@@ -58,12 +58,12 @@ describe('title', function()
command(is_os('win') and 'cd C:\\' or 'cd /')
api.nvim_set_option_value('title', true, {})
screen:expect(function()
- local expected = (is_os('win') and '[No Name] (C:\\) - Nvim' or '[No Name] (/) - Nvim')
+ local expected = (is_os('win') and '[No Name] (C:/) - Nvim' or '[No Name] (/) - Nvim')
eq(expected, screen.title)
end)
feed('ifoo')
screen:expect(function()
- local expected = (is_os('win') and '[No Name] + (C:\\) - Nvim' or '[No Name] + (/) - Nvim')
+ local expected = (is_os('win') and '[No Name] + (C:/) - Nvim' or '[No Name] + (/) - Nvim')
eq(expected, screen.title)
end)
feed('<Esc>')
@@ -126,7 +126,7 @@ describe('title', function()
describe('is not changed by', function()
local file1 = is_os('win') and 'C:\\mydir\\myfile1' or '/mydir/myfile1'
local file2 = is_os('win') and 'C:\\mydir\\myfile2' or '/mydir/myfile2'
- local expected = (is_os('win') and 'myfile1 (C:\\mydir) - Nvim' or 'myfile1 (/mydir) - Nvim')
+ local expected = (is_os('win') and 'myfile1 (C:/mydir) - Nvim' or 'myfile1 (/mydir) - Nvim')
local buf2
before_each(function()
diff --git a/test/functional/vimscript/buf_functions_spec.lua b/test/functional/vimscript/buf_functions_spec.lua
index c73f4b5197..a4389ca97a 100644
--- a/test/functional/vimscript/buf_functions_spec.lua
+++ b/test/functional/vimscript/buf_functions_spec.lua
@@ -6,7 +6,6 @@ local clear = n.clear
local fn = n.fn
local api = n.api
local command = n.command
-local get_pathsep = n.get_pathsep
local rmdir = n.rmdir
local pcall_err = t.pcall_err
local mkdir = t.mkdir
@@ -81,8 +80,8 @@ describe('bufname() function', function()
it('returns expected buffer name', function()
eq('', fn.bufname('%')) -- Buffer has no name yet
command('file ' .. fname)
- local wd = vim.uv.cwd()
- local sep = get_pathsep()
+ local wd = assert(t.fix_slashes(assert(vim.uv.cwd())))
+ local sep = '/'
local curdirname = fn.fnamemodify(wd, ':t')
for _, arg in ipairs({ '%', 1, 'X', wd }) do
eq(fname, fn.bufname(arg))
diff --git a/test/functional/vimscript/eval_spec.lua b/test/functional/vimscript/eval_spec.lua
index eeebdd9aa0..415780e030 100644
--- a/test/functional/vimscript/eval_spec.lua
+++ b/test/functional/vimscript/eval_spec.lua
@@ -85,7 +85,7 @@ describe('backtick expansion', function()
eq({ 'file2' }, eval('argv()'))
if t.is_os('win') then
command(':silent args `dir /s/b *4`')
- eq({ 'subdir\\file4' }, eval('map(argv(), \'fnamemodify(v:val, ":.")\')'))
+ eq({ 'subdir/file4' }, eval('map(argv(), \'fnamemodify(v:val, ":.")\')'))
else
command(':silent args `echo */*4`')
eq({ 'subdir/file4' }, eval('argv()'))
diff --git a/test/functional/vimscript/executable_spec.lua b/test/functional/vimscript/executable_spec.lua
index 943e2e10d0..bb52c861f0 100644
--- a/test/functional/vimscript/executable_spec.lua
+++ b/test/functional/vimscript/executable_spec.lua
@@ -19,13 +19,13 @@ describe('executable()', function()
end)
if is_os('win') then
- it('exepath respects shellslash', function()
+ 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)
command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")')
eq(
- [[test\functional\fixtures\bin\null.CMD]],
+ [[test/functional/fixtures/bin/null.CMD]],
call('fnamemodify', call('exepath', 'null'), ':.')
)
command('set shellslash')
@@ -35,12 +35,12 @@ describe('executable()', function()
)
end)
- it('stdpath respects shellslash', function()
+ 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)
t.matches(
- [[build\Xtest_xdg[%w_]*\share\nvim%-data]],
+ [[build/Xtest_xdg[%w_]*/share/nvim%-data]],
call('fnamemodify', call('stdpath', 'data'), ':.')
)
command('set shellslash')
diff --git a/test/functional/vimscript/fnamemodify_spec.lua b/test/functional/vimscript/fnamemodify_spec.lua
index 380471320e..7b581c225f 100644
--- a/test/functional/vimscript/fnamemodify_spec.lua
+++ b/test/functional/vimscript/fnamemodify_spec.lua
@@ -29,21 +29,20 @@ describe('fnamemodify()', function()
end)
it('handles the root path', function()
- local root = n.pathroot()
+ local root = assert(t.fix_slashes(n.pathroot()))
eq(root, fnamemodify([[/]], ':p:h'))
eq(root, fnamemodify([[/]], ':p'))
if is_os('win') then
eq(root, fnamemodify([[\]], ':p:h'))
eq(root, fnamemodify([[\]], ':p'))
command('set shellslash')
- root = string.sub(root, 1, -2) .. '/'
eq(root, fnamemodify([[\]], ':p:h'))
eq(root, fnamemodify([[\]], ':p'))
eq(root, fnamemodify([[/]], ':p:h'))
eq(root, fnamemodify([[/]], ':p'))
local letter_colon = root:sub(1, 2)
- local old_dir = getcwd() .. '/'
+ local old_dir = t.fix_slashes(getcwd()) .. '/'
local foo_dir = old_dir .. 'foo/'
eq(old_dir, fnamemodify(letter_colon, ':p'))
eq(old_dir, fnamemodify(letter_colon .. '.', ':p'))
diff --git a/test/old/testdir/test_expand_func.vim b/test/old/testdir/test_expand_func.vim
index 281e7c13e2..a6488f07f2 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('<stack>')
END
call writefile(lines, 'Xshellslash/Xstack')
- " Test that changing 'shellslash' always affects the result of expand()
+ " Test that changing 'shellslash' doesn't affect the result of expand()
" when sourcing a script multiple times.
for i in range(2)
source Xshellslash/Xstack
call assert_match('\<Xshellslash/Xstack\[1\]$', g:stack1)
- call assert_match('\<Xshellslash\\Xstack\[3\]$', g:stack2)
+ call assert_match('\<Xshellslash/Xstack\[3\]$', g:stack2)
call assert_match('\<Xshellslash/Xstack\[5\]$', g:stack3)
unlet g:stack1
unlet g:stack2
diff --git a/test/old/testdir/test_functions.vim b/test/old/testdir/test_functions.vim
index 1482514db3..6622e8e46a 100644
--- a/test/old/testdir/test_functions.vim
+++ b/test/old/testdir/test_functions.vim
@@ -3671,9 +3671,10 @@ func Test_glob2()
call assert_equal([], (glob('abc[glob]def\*', 0, 1)))
call assert_equal([], (glob('\[XglobDir]\*', 0, 1)))
call assert_equal([], (glob('abc\[glob]def\*', 0, 1)))
+ " Test that changing 'shellslash' doesn't affect the result of glob()
set noshellslash
- call assert_equal(['[XglobDir]\Xglob'], (glob('[[]XglobDir]/*', 0, 1)))
- call assert_equal(['abc[glob]def\Xglob'], (glob('abc[[]glob]def/*', 0, 1)))
+ call assert_equal(['[XglobDir]/Xglob'], (glob('[[]XglobDir]/*', 0, 1)))
+ call assert_equal(['abc[glob]def/Xglob'], (glob('abc[[]glob]def/*', 0, 1)))
set shellslash
call assert_equal(['[XglobDir]/Xglob'], (glob('[[]XglobDir]/*', 0, 1)))
call assert_equal(['abc[glob]def/Xglob'], (glob('abc[[]glob]def/*', 0, 1)))
diff --git a/test/old/testdir/test_ins_complete.vim b/test/old/testdir/test_ins_complete.vim
index 0c12381cad..cdeb88acaf 100644
--- a/test/old/testdir/test_ins_complete.vim
+++ b/test/old/testdir/test_ins_complete.vim
@@ -940,9 +940,10 @@ func Test_ins_completeslash()
%bw!
call delete('Xdir', 'rf')
+ " globpath() should always return forward slash separator
set noshellslash
- set completeslash=slash
- call assert_true(stridx(globpath(&rtp, 'syntax/*.vim', 1, 1)[0], '\') != -1)
+ set completeslash=backslash
+ call assert_true(stridx(globpath(&rtp, 'syntax/*.vim', 1, 1)[0], '\') == -1)
let &shellslash = orig_shellslash
set completeslash=
@@ -1553,13 +1554,10 @@ endfunc
func Test_issue_7021()
CheckMSWindows
- let orig_shellslash = &shellslash
- set noshellslash
-
- set completeslash=slash
- call assert_false(expand('~') =~ '/')
+ " Test that 'completeslash' doesn't affect the result of expand()
+ set completeslash=backslash
+ call assert_false(expand('~') =~ '\\')
- let &shellslash = orig_shellslash
set completeslash=
endfunc
diff --git a/test/old/testdir/test_windows_home.vim b/test/old/testdir/test_windows_home.vim
index 0f86124d3e..012554ba0a 100644
--- a/test/old/testdir/test_windows_home.vim
+++ b/test/old/testdir/test_windows_home.vim
@@ -38,7 +38,7 @@ endfunc
func CheckHome(exp, ...)
call assert_equal(a:exp, $HOME)
- call assert_equal(a:exp, expand('~', ':p'))
+ call assert_equal(substitute(a:exp, '\\', '/', 'g'), expand('~', ':p'))
if !a:0
call CheckHomeIsMissingFromSubprocessEnvironment()
else