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