neo-tree.nvim icon indicating copy to clipboard operation
neo-tree.nvim copied to clipboard

fix: handle more edge cases for Windows path escaping

Open bwpge opened this issue 9 months ago • 2 comments

This PR fixes #1448, among other edge cases that weren't previously handled.

Relevant notes were captured in the discussion for #1448, but the gist is that certain punctuation ([, ], backticks, $, etc.) causes Neovim to drop an escaped path separator on Windows. This requires us to use an additional \ (e.g. X:\[foo]\\\(bar)\baz.txt).

bwpge avatar May 04 '24 21:05 bwpge

For anyone who wants to verify/test the logic, the following script was ran in Neovim with :luafile %:

local dpunc = {
  "()",
  "[]",
  -- "{}",
  "^",
  "&",
  ";",
  "`",
  -- these are left out because this causes exponentially longer runtime.
  -- they should have similar behavior to the above
  -- "!", "#", "$", "%", "+", ",", "-", "=", "@", "_", "~"
}
local fpunc = [[!#$%&'()+,-;=@[]^_`{}~ ]]

-- testing implementation for this function
local function escape_path_for_cmd(path)
  local escaped_path = vim.fn.fnameescape(path)
  if true then -- assume is windows
    local need_extra_esc = path:find("[%[%]`%$~]")
    local esc = need_extra_esc and "\\\\" or "\\"
    escaped_path = escaped_path:gsub("\\[%(%)%^&;]", esc .. "%1")
    if need_extra_esc then
      escaped_path = escaped_path:gsub("\\\\['` ]", "\\%1")
    end
  end
  return escaped_path
end

-- makes a value like `[foo]` from part='foo' and p='[]'
local function make_part(part, p)
  local open = p:sub(1, 1)
  local close = p:sub(2, 2)
  if close == "" then
    close = open
  end
  return (open .. part .. close)
end

-- create nested directories with leading/trailing punctuation
local dirs = {}
for _, p1 in ipairs(dpunc) do
  for _, p2 in ipairs(dpunc) do
    local part1 = make_part("foo", p1)
    local part2 = make_part("bar", p2)
    local path = string.format(".\\tests\\%s\\%s", part1, part2)
    vim.fn.mkdir(path, "p")

    table.insert(dirs, path)
  end
end

local files = { "baz.txt" }
for c in string.gmatch(fpunc, ".") do
  table.insert(files, c .. "baz.txt")
end

-- try to write files with weird path
for _, d in ipairs(dirs) do
  for _, f in ipairs(files) do
    local p = escape_path_for_cmd(d .. "\\" .. f)
    local ok, err = pcall(vim.cmd, "silent w " .. p)
    if not ok then
      print("fail:", p, "->", err)
    end
  end
end

bwpge avatar May 04 '24 21:05 bwpge

Thank you so much for working in this @bwpge !!

Passing this to @cseickel .

pysan3 avatar May 06 '24 02:05 pysan3

@pysan3 wanted to ping you on this one.

I'm sure things are a bit hectic with the changeover in responsibilities so no rush, just a bump to keep on the radar :)

bwpge avatar May 18 '24 20:05 bwpge

Thanks mate, I totally forgot this one my bad.

Merging! Thanks for your amazing work.

pysan3 avatar May 19 '24 01:05 pysan3

Happy to help, have a good weekend!

bwpge avatar May 19 '24 01:05 bwpge

Don't know why but still not working for me.

SuperSheykh avatar May 20 '24 10:05 SuperSheykh

@SuperSheykh It's not released as a version yet. Are you sure you are on the latest master branch?

pysan3 avatar May 20 '24 10:05 pysan3

My bad it actually works fine. Thanks so much guys!

SuperSheykh avatar May 20 '24 11:05 SuperSheykh