@@ -303,11 +303,51 @@ function M.rename_loaded_buffers(old_path, new_path)
303303 end
304304end
305305
306+ local is_windows_drive = function (path )
307+ return (M .is_windows ) and (path :match (" ^%a:\\ $" ) ~= nil )
308+ end
309+
306310--- @param path string path to file or directory
307311--- @return boolean
308312function M .file_exists (path )
309- local _ , error = vim .loop .fs_stat (path )
310- return error == nil
313+ if not (M .is_windows or M .is_wsl ) then
314+ local _ , error = vim .loop .fs_stat (path )
315+ return error == nil
316+ end
317+
318+ -- Windows is case-insensetive, but case-preserving
319+ -- If a file's name is being changed into itself
320+ -- with different casing, windows will falsely
321+ -- report that file is already existing, so a hand-rolled
322+ -- implementation of checking for existance is needed.
323+ -- Same holds for WSL, since it can sometimes
324+ -- access Windows files directly.
325+ -- For more details see (#3117).
326+
327+ if is_windows_drive (path ) then
328+ return vim .fn .isdirectory (path ) == 1
329+ end
330+
331+ local parent = vim .fn .fnamemodify (path , " :h" )
332+ local filename = vim .fn .fnamemodify (path , " :t" )
333+
334+ local handle = vim .loop .fs_scandir (parent )
335+ if not handle then
336+ -- File can not exist if its parent directory does not exist
337+ return false
338+ end
339+
340+ while true do
341+ local name , _ = vim .loop .fs_scandir_next (handle )
342+ if not name then
343+ break
344+ end
345+ if name == filename then
346+ return true
347+ end
348+ end
349+
350+ return false
311351end
312352
313353--- @param path string
0 commit comments