From 7bf2ab4b87e08807ca9fa1901ac915427428a928 Mon Sep 17 00:00:00 2001 From: tao <2471314@gmail.com> Date: Wed, 10 Jun 2026 19:46:55 +0800 Subject: [PATCH] fix(path): `nvim_get_runtime_file` fails on DOS 8.3 filename #40144 Problem: stdpath() may return a DOS 8.3 "shortened" filename, because Windows truncates some long usernames into `6ch~N` names. Then features such as `nvim_get_runtime_file` fail to find the file. Analysis: When expanding an 8.3 filename path like `C:/Users/ADMINI~1/AppData/*`, we treat `~` as a special character and first check whether a directory named `ADMINI~1` exists under `Users`. Since no such directory actually exists, the expansion fails. Solution: Treat `~` as a literal character in `do_path_expand`. Since the `~/` case is already handled in `gen_expand_wildcards`, any remaining `~` is just a literal character and will later be escaped to `\~` by `file_pat_to_reg_pat` if needed. --- src/nvim/path.c | 4 +++- test/functional/api/vim_spec.lua | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/nvim/path.c b/src/nvim/path.c index f5affeaa9b..4bc0b3682b 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -687,7 +687,9 @@ static size_t do_path_expand(garray_T *gap, const char *path, size_t wildoff, in s = p + 1; } else if (path_end >= path + wildoff #ifdef MSWIN - && vim_strchr("*?[~", (uint8_t)(*path_end)) != NULL + // "~" not included here, we want to treat it as literal. + // The "~/" case is already handled in `gen_expand_wildcards`. + && vim_strchr("*?[", (uint8_t)(*path_end)) != NULL #else && (vim_strchr("*?[{~$", (uint8_t)(*path_end)) != NULL || (!p_fic && (flags & EW_ICASE) && mb_isalpha(utf_ptr2char(path_end)))) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index c900c9cf61..16cd24dd53 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -3756,6 +3756,23 @@ describe('API', function() eq(p(val[1]), vimruntime .. '/syntax/vim.vim') eq(p(val[2]), vimruntime .. '/ftplugin/vim.vim') end) + + it('finds files via an 8.3 filename path #25019', function() + skip(not is_os('win'), 'N/A: 8.3 filenames are only available on Windows') + local path = 'Xtest_runtime_path' + mkdir_p(('%s/subdir/lua'):format(path)) + write_file(('%s/subdir/lua/foo.lua'):format(path), '') + finally(function() + rmdir(path) + end) + local path_with_shortname = + p(fn.system(('for %%I in ("%s") do @echo %%~sI'):format(path), ''):gsub('\n', '')) + skip(path == vim.fs.basename(path_with_shortname), 'N/A: 8.3 filenames are disabled') + exec_lua(('vim.opt.rtp:prepend("%s/*")'):format(path_with_shortname)) + local val = api.nvim_get_runtime_file('lua/foo.lua', true) + eq(1, #val) + eq(('%s/subdir/lua/foo.lua'):format(path_with_shortname), val[1]) + end) end) describe('nvim_get_all_options_info', function()