diff --git a/lua/nvim-tree/actions/finders/find-file.lua b/lua/nvim-tree/actions/finders/find-file.lua index fa5eea51..34a8ee90 100644 --- a/lua/nvim-tree/actions/finders/find-file.lua +++ b/lua/nvim-tree/actions/finders/find-file.lua @@ -2,6 +2,8 @@ local log = require("nvim-tree.log") local view = require("nvim-tree.view") local utils = require("nvim-tree.utils") local core = require("nvim-tree.core") + +local DirectoryNode = require("nvim-tree.node.directory") local Iterator = require("nvim-tree.iterators.node-iterator") local M = {} @@ -59,19 +61,27 @@ function M.fn(path) local link_match = node.link_to and vim.startswith(path_real, node.link_to .. utils.path_separator) if abs_match or link_match then - if not node.group_next then - node.open = true - end - if #node.nodes == 0 then - core.get_explorer():expand(node) - if node.group_next and incremented_line then - line = line - 1 + local dir = node:as(DirectoryNode) + if dir then + if not dir.group_next then + dir.open = true + end + if #dir.nodes == 0 then + core.get_explorer():expand(dir) + if dir.group_next and incremented_line then + line = line - 1 + end end end end end) :recursor(function(node) - return node.group_next and { node.group_next } or (node.open and #node.nodes > 0 and node.nodes) + node = node and node:as(DirectoryNode) + if node then + return node.group_next and { node.group_next } or (node.open and #node.nodes > 0 and node.nodes) + else + return nil + end end) :iterate() diff --git a/lua/nvim-tree/actions/moves/item.lua b/lua/nvim-tree/actions/moves/item.lua index 99799631..1cc6a3e5 100644 --- a/lua/nvim-tree/actions/moves/item.lua +++ b/lua/nvim-tree/actions/moves/item.lua @@ -72,7 +72,7 @@ local function move(explorer, where, what, skip_gitignored) end end ----@param node Node +---@param node DirectoryNode local function expand_node(node) if node:is(DirectoryNode) and not node.open then ---@cast node DirectoryNode @@ -101,9 +101,9 @@ local function move_next_recursive(explorer, what, skip_gitignored) if node_init.name ~= ".." then -- root node cannot have a status valid = status_is_valid(node_init, what, skip_gitignored) end - if node_init:is(DirectoryNode) and valid and not node_init.open then - ---@cast node_init DirectoryNode - node_init:expand_or_collapse(false) + local node_dir = node_init:as(DirectoryNode) + if node_dir and valid and not node_dir.open then + node_dir:expand_or_collapse(false) end move(explorer, "next", what, skip_gitignored) @@ -120,20 +120,15 @@ local function move_next_recursive(explorer, what, skip_gitignored) -- i is used to limit iterations. local i = 0 - local is_dir = node_cur.nodes ~= nil - while is_dir and i < MAX_DEPTH do - expand_node(node_cur) + local dir_cur = node_cur:as(DirectoryNode) + while dir_cur and i < MAX_DEPTH do + expand_node(dir_cur) move(explorer, "next", what, skip_gitignored) -- Save current node. node_cur = explorer:get_node_at_cursor() - -- Update is_dir. - if node_cur then - is_dir = node_cur.nodes ~= nil - else - is_dir = false - end + dir_cur = node_cur and node_cur:as(DirectoryNode) i = i + 1 end @@ -187,8 +182,10 @@ local function move_prev_recursive(explorer, what, skip_gitignored) end -- 4.2) - local node_dir = node_cur - expand_node(node_dir) + local node_dir = node_cur:as(DirectoryNode) + if node_dir then + expand_node(node_dir) + end -- 4.3) if node_init.name == ".." then -- root node diff --git a/lua/nvim-tree/actions/node/open-file.lua b/lua/nvim-tree/actions/node/open-file.lua index 3e5a697a..25c74451 100644 --- a/lua/nvim-tree/actions/node/open-file.lua +++ b/lua/nvim-tree/actions/node/open-file.lua @@ -331,9 +331,9 @@ local function open_in_new_window(filename, mode) local fname if M.relative_path then - fname = vim.fn.fnameescape(utils.path_relative(filename, vim.fn.getcwd())) + fname = utils.escape_special_chars(vim.fn.fnameescape(utils.path_relative(filename, vim.fn.getcwd()))) else - fname = vim.fn.fnameescape(filename) + fname = utils.escape_special_chars(vim.fn.fnameescape(filename)) end local command @@ -370,28 +370,27 @@ end ---@param mode string ---@param filename string function M.fn(mode, filename) - local fname = utils.escape_special_chars(filename) if type(mode) ~= "string" then mode = "" end if mode == "tabnew" then - return open_file_in_tab(fname) + return open_file_in_tab(filename) end if mode == "drop" then - return drop(fname) + return drop(filename) end if mode == "tab_drop" then - return tab_drop(fname) + return tab_drop(filename) end if mode == "edit_in_place" then - return edit_in_current_buf(fname) + return edit_in_current_buf(filename) end - local buf_loaded = is_already_loaded(fname) + local buf_loaded = is_already_loaded(filename) local found_win = utils.get_win_buf_from_path(filename) if found_win and (mode == "preview" or mode == "preview_no_picker") then @@ -399,7 +398,7 @@ function M.fn(mode, filename) end if not found_win then - open_in_new_window(fname, mode) + open_in_new_window(filename, mode) else vim.api.nvim_set_current_win(found_win) vim.bo.bufhidden = "" diff --git a/lua/nvim-tree/lib.lua b/lua/nvim-tree/lib.lua index 8e6902e5..abd9d01e 100644 --- a/lua/nvim-tree/lib.lua +++ b/lua/nvim-tree/lib.lua @@ -65,7 +65,7 @@ end ---@param items_short string[] ---@param items_long string[] ---@param kind string|nil ----@param callback fun(item_short: string) +---@param callback fun(item_short: string|nil) function M.prompt(prompt_input, prompt_select, items_short, items_long, kind, callback) local function format_item(short) for i, s in ipairs(items_short) do diff --git a/lua/nvim-tree/node/init.lua b/lua/nvim-tree/node/init.lua index a2af3afe..5b7f7b1a 100644 --- a/lua/nvim-tree/node/init.lua +++ b/lua/nvim-tree/node/init.lua @@ -2,10 +2,6 @@ local git = require("nvim-tree.git") local Class = require("nvim-tree.class") ----TODO #2886 ----TODO remove all @cast ----TODO remove all references to directory fields: - ---Abstract Node class. ---Uses the abstract factory pattern to instantiate child instances. ---@class (exact) Node: Class diff --git a/lua/nvim-tree/utils.lua b/lua/nvim-tree/utils.lua index b48e989b..936982ec 100644 --- a/lua/nvim-tree/utils.lua +++ b/lua/nvim-tree/utils.lua @@ -59,6 +59,17 @@ function M.path_basename(path) return path:sub(i + 1, #path) end +--- Check if there are parentheses before brackets, it causes problems for windows. +--- Refer to issue #2862 and #2961 for more details. +local function has_parentheses_and_brackets(path) + local _, i_parentheses = path:find("(", 1, true) + local _, i_brackets = path:find("[", 1, true) + if i_parentheses and i_brackets then + return true + end + return false +end + --- Get a path relative to another path. ---@param path string ---@param relative_to string|nil @@ -68,13 +79,18 @@ function M.path_relative(path, relative_to) return path end - local _, r = path:find(M.path_add_trailing(relative_to), 1, true) - local p = path + local norm_path = path + if M.is_windows and has_parentheses_and_brackets(path) then + norm_path = path:gsub("/", "\\") + end + + local _, r = norm_path:find(M.path_add_trailing(relative_to), 1, true) + local p = norm_path if r then -- take the relative path starting after '/' -- if somehow given a completely matching path, -- returns "" - p = path:sub(r + 1) + p = norm_path:sub(r + 1) end return p end @@ -272,6 +288,14 @@ function M.canonical_path(path) return path end +--- Escapes special characters in string for windows, refer to issue #2862 and #2961 for more details. +local function escape_special_char_for_windows(path) + if has_parentheses_and_brackets(path) then + return path:gsub("\\", "/"):gsub("/ ", "\\ ") + end + return path:gsub("%(", "\\("):gsub("%)", "\\)") +end + --- Escapes special characters in string if windows else returns unmodified string. ---@param path string ---@return string|nil @@ -279,7 +303,7 @@ function M.escape_special_chars(path) if path == nil then return path end - return M.is_windows and path:gsub("\\", "/") or path + return M.is_windows and escape_special_char_for_windows(path) or path end --- Create empty sub-tables if not present