Merge remote-tracking branch 'origin/master' into 2826-multi-instance-view-amc
This commit is contained in:
@@ -40,21 +40,10 @@ local function usable_win_ids()
|
||||
end
|
||||
|
||||
local win_config = vim.api.nvim_win_get_config(id)
|
||||
return id ~= tree_winid and win_config.focusable and not win_config.external or false
|
||||
return id ~= tree_winid and win_config.focusable and not win_config.hide and not win_config.external or false
|
||||
end, win_ids)
|
||||
end
|
||||
|
||||
---Find the first window in the tab that is not NvimTree.
|
||||
---@return integer -1 if none available
|
||||
local function first_win_id()
|
||||
local selectable = usable_win_ids()
|
||||
if #selectable > 0 then
|
||||
return selectable[1]
|
||||
else
|
||||
return -1
|
||||
end
|
||||
end
|
||||
|
||||
---Get user to pick a window in the tab that is not NvimTree.
|
||||
---@return integer|nil -- If a valid window was picked, return its id. If an
|
||||
--- invalid window was picked / user canceled, return nil. If there are
|
||||
@@ -259,9 +248,14 @@ local function get_target_winid(mode)
|
||||
local target_winid
|
||||
if not M.window_picker.enable or string.find(mode, "no_picker") then
|
||||
target_winid = lib.target_winid
|
||||
-- first available window
|
||||
if not vim.tbl_contains(vim.api.nvim_tabpage_list_wins(0), target_winid) then
|
||||
target_winid = first_win_id()
|
||||
local usable_wins = usable_win_ids()
|
||||
-- first available usable window
|
||||
if not vim.tbl_contains(usable_wins, target_winid) then
|
||||
if #usable_wins > 0 then
|
||||
target_winid = usable_wins[1]
|
||||
else
|
||||
target_winid = -1
|
||||
end
|
||||
end
|
||||
else
|
||||
-- pick a window
|
||||
|
||||
@@ -2,6 +2,7 @@ local utils = require("nvim-tree.utils")
|
||||
local core = require("nvim-tree.core")
|
||||
local Iterator = require("nvim-tree.iterators.node-iterator")
|
||||
|
||||
local FileNode = require("nvim-tree.node.file")
|
||||
local DirectoryNode = require("nvim-tree.node.directory")
|
||||
|
||||
local M = {}
|
||||
@@ -23,26 +24,30 @@ local function buf_match()
|
||||
end
|
||||
end
|
||||
|
||||
---@param keep_buffers boolean
|
||||
function M.fn(keep_buffers)
|
||||
---Collapse a node, root if nil
|
||||
---@param node Node?
|
||||
---@param opts ApiCollapseOpts
|
||||
local function collapse(node, opts)
|
||||
local explorer = core.get_explorer()
|
||||
if not explorer then
|
||||
return
|
||||
end
|
||||
|
||||
local node = explorer:get_node_at_cursor()
|
||||
if not node then
|
||||
node = node or explorer
|
||||
|
||||
local node_at_cursor = explorer:get_node_at_cursor()
|
||||
if not node_at_cursor then
|
||||
return
|
||||
end
|
||||
|
||||
local matches = buf_match()
|
||||
|
||||
Iterator.builder(explorer.nodes)
|
||||
Iterator.builder({ node:is(FileNode) and node.parent or node:as(DirectoryNode) })
|
||||
:hidden()
|
||||
:applier(function(n)
|
||||
local dir = n:as(DirectoryNode)
|
||||
if dir then
|
||||
dir.open = keep_buffers and matches(dir.absolute_path)
|
||||
dir.open = opts.keep_buffers == true and matches(dir.absolute_path)
|
||||
end
|
||||
end)
|
||||
:recursor(function(n)
|
||||
@@ -51,7 +56,26 @@ function M.fn(keep_buffers)
|
||||
:iterate()
|
||||
|
||||
explorer.renderer:draw()
|
||||
utils.focus_node_or_parent(node)
|
||||
utils.focus_node_or_parent(node_at_cursor)
|
||||
end
|
||||
|
||||
|
||||
---@param opts ApiCollapseOpts|boolean|nil legacy -> opts.keep_buffers
|
||||
function M.all(opts)
|
||||
-- legacy arguments
|
||||
if type(opts) == "boolean" then
|
||||
opts = {
|
||||
keep_buffers = opts,
|
||||
}
|
||||
end
|
||||
|
||||
collapse(nil, opts or {})
|
||||
end
|
||||
|
||||
---@param node Node
|
||||
---@param opts ApiCollapseOpts?
|
||||
function M.node(node, opts)
|
||||
collapse(node, opts or {})
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -2,6 +2,7 @@ local core = require("nvim-tree.core")
|
||||
local Iterator = require("nvim-tree.iterators.node-iterator")
|
||||
local notify = require("nvim-tree.notify")
|
||||
|
||||
local FileNode = require("nvim-tree.node.file")
|
||||
local DirectoryNode = require("nvim-tree.node.directory")
|
||||
|
||||
local M = {}
|
||||
@@ -70,23 +71,38 @@ local function gen_iterator()
|
||||
end
|
||||
end
|
||||
|
||||
---Expand the directory node or the root
|
||||
---@param node Node
|
||||
function M.fn(node)
|
||||
local explorer = core.get_explorer()
|
||||
local parent = node:as(DirectoryNode) or explorer
|
||||
if not parent then
|
||||
---@param node Node?
|
||||
local function expand_node(node)
|
||||
if not node then
|
||||
return
|
||||
end
|
||||
|
||||
if gen_iterator()(parent) then
|
||||
if gen_iterator()(node) then
|
||||
notify.warn("expansion iteration was halted after " .. M.MAX_FOLDER_DISCOVERY .. " discovered folders")
|
||||
end
|
||||
|
||||
local explorer = core.get_explorer()
|
||||
if explorer then
|
||||
explorer.renderer:draw()
|
||||
end
|
||||
end
|
||||
|
||||
---Expand the directory node or the root
|
||||
---@param node Node
|
||||
function M.all(node)
|
||||
expand_node(node and node:as(DirectoryNode) or core.get_explorer())
|
||||
end
|
||||
|
||||
---Expand the directory node or parent node
|
||||
---@param node Node
|
||||
function M.node(node)
|
||||
if not node then
|
||||
return
|
||||
end
|
||||
|
||||
expand_node(node:is(FileNode) and node.parent or node:as(DirectoryNode))
|
||||
end
|
||||
|
||||
function M.setup(opts)
|
||||
M.MAX_FOLDER_DISCOVERY = opts.actions.expand_all.max_folder_discovery
|
||||
M.EXCLUDE = to_lookup_table(opts.actions.expand_all.exclude)
|
||||
@@ -1,10 +1,10 @@
|
||||
local M = {}
|
||||
|
||||
M.collapse_all = require("nvim-tree.actions.tree.modifiers.collapse-all")
|
||||
M.expand_all = require("nvim-tree.actions.tree.modifiers.expand-all")
|
||||
M.collapse = require("nvim-tree.actions.tree.modifiers.collapse")
|
||||
M.expand = require("nvim-tree.actions.tree.modifiers.expand")
|
||||
|
||||
function M.setup(opts)
|
||||
M.expand_all.setup(opts)
|
||||
M.expand.setup(opts)
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
@@ -181,8 +181,12 @@ Api.tree.get_nodes = wrap_explorer("get_nodes")
|
||||
|
||||
Api.tree.find_file = wrap(actions.tree.find_file.fn)
|
||||
Api.tree.search_node = wrap(actions.finders.search_node.fn)
|
||||
Api.tree.collapse_all = wrap(actions.tree.modifiers.collapse_all.fn)
|
||||
Api.tree.expand_all = wrap_node(actions.tree.modifiers.expand_all.fn)
|
||||
|
||||
---@class ApiCollapseOpts
|
||||
---@field keep_buffers boolean|nil default false
|
||||
|
||||
Api.tree.collapse_all = wrap(actions.tree.modifiers.collapse.all)
|
||||
Api.tree.expand_all = wrap_node(actions.tree.modifiers.expand.all)
|
||||
Api.tree.toggle_enable_filters = wrap_explorer_member("filters", "toggle")
|
||||
Api.tree.toggle_gitignore_filter = wrap_explorer_member_args("filters", "toggle", "git_ignored")
|
||||
Api.tree.toggle_git_clean_filter = wrap_explorer_member_args("filters", "toggle", "git_clean")
|
||||
@@ -317,6 +321,9 @@ Api.node.navigate.diagnostics.prev_recursive = wrap_node(actions.moves.item.fn({
|
||||
Api.node.navigate.opened.next = wrap_node(actions.moves.item.fn({ where = "next", what = "opened" }))
|
||||
Api.node.navigate.opened.prev = wrap_node(actions.moves.item.fn({ where = "prev", what = "opened" }))
|
||||
|
||||
Api.node.expand = wrap_node(actions.tree.modifiers.expand.node)
|
||||
Api.node.collapse = wrap_node(actions.tree.modifiers.collapse.node)
|
||||
|
||||
---@class ApiNodeDeleteWipeBufferOpts
|
||||
---@field force boolean|nil default false
|
||||
|
||||
|
||||
@@ -384,14 +384,7 @@ function View:grow()
|
||||
local count = vim.fn.strchars(l)
|
||||
-- also add space for right-aligned icons
|
||||
local extmarks = vim.api.nvim_buf_get_extmarks(self:get_bufnr(), ns_id, { line_nr, 0 }, { line_nr, -1 }, { details = true })
|
||||
for _, extmark in ipairs(extmarks) do
|
||||
local virt_texts = extmark[4].virt_text
|
||||
if virt_texts then
|
||||
for _, virt_text in ipairs(virt_texts) do
|
||||
count = count + vim.fn.strchars(virt_text[1])
|
||||
end
|
||||
end
|
||||
end
|
||||
count = count + utils.extmarks_length(extmarks)
|
||||
if resizing_width < count then
|
||||
resizing_width = count
|
||||
end
|
||||
|
||||
@@ -96,7 +96,7 @@ function M.default_on_attach(bufnr)
|
||||
vim.keymap.set("n", "S", api.tree.search_node, opts("Search"))
|
||||
vim.keymap.set("n", "u", api.fs.rename_full, opts("Rename: Full Path"))
|
||||
vim.keymap.set("n", "U", api.tree.toggle_custom_filter, opts("Toggle Filter: Hidden"))
|
||||
vim.keymap.set("n", "W", api.tree.collapse_all, opts("Collapse"))
|
||||
vim.keymap.set("n", "W", api.tree.collapse_all, opts("Collapse All"))
|
||||
vim.keymap.set("n", "x", api.fs.cut, opts("Cut"))
|
||||
vim.keymap.set("n", "y", api.fs.copy.filename, opts("Copy Name"))
|
||||
vim.keymap.set("n", "Y", api.fs.copy.relative_path, opts("Copy Relative Path"))
|
||||
|
||||
@@ -60,6 +60,13 @@ local function refactored(opts)
|
||||
end
|
||||
end
|
||||
utils.move_missing_val(opts, "update_focused_file", "ignore_list", opts, "update_focused_file.update_root", "ignore_list", true)
|
||||
|
||||
-- 2025/04/30
|
||||
if opts.renderer and opts.renderer.icons and type(opts.renderer.icons.padding) == "string" then
|
||||
local icons_padding = opts.renderer.icons.padding
|
||||
opts.renderer.icons.padding = {}
|
||||
opts.renderer.icons.padding.icon = icons_padding
|
||||
end
|
||||
end
|
||||
|
||||
local function deprecated(opts)
|
||||
|
||||
@@ -134,12 +134,12 @@ end
|
||||
function Builder:format_line(indent_markers, arrows, icon, name, node)
|
||||
local added_len = 0
|
||||
local function add_to_end(t1, t2)
|
||||
if not t2 then
|
||||
if not t2 or vim.tbl_isempty(t2) then
|
||||
return
|
||||
end
|
||||
for _, v in ipairs(t2) do
|
||||
if added_len > 0 then
|
||||
table.insert(t1, { str = self.explorer.opts.renderer.icons.padding })
|
||||
table.insert(t1, { str = self.explorer.opts.renderer.icons.padding.icon })
|
||||
end
|
||||
table.insert(t1, v)
|
||||
end
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
local M = {}
|
||||
|
||||
local utils = require("nvim-tree.utils")
|
||||
local view = require("nvim-tree.view")
|
||||
|
||||
local function hide(win)
|
||||
if win then
|
||||
@@ -32,7 +33,7 @@ local function effective_win_width()
|
||||
return win_width - win_info[1].textoff
|
||||
end
|
||||
|
||||
local function show()
|
||||
local function show(opts)
|
||||
local line_nr = vim.api.nvim_win_get_cursor(0)[1]
|
||||
if vim.wo.wrap then
|
||||
return
|
||||
@@ -52,6 +53,11 @@ local function show()
|
||||
local text_width = vim.fn.strdisplaywidth(vim.fn.substitute(line, "[^[:print:]]*$", "", "g"))
|
||||
local win_width = effective_win_width()
|
||||
|
||||
-- windows width reduced by right aligned icons
|
||||
local icon_ns_id = vim.api.nvim_get_namespaces()["NvimTreeExtmarks"]
|
||||
local icon_extmarks = vim.api.nvim_buf_get_extmarks(0, icon_ns_id, { line_nr - 1, 0 }, { line_nr - 1, -1 }, { details = true })
|
||||
win_width = win_width - utils.extmarks_length(icon_extmarks)
|
||||
|
||||
if text_width < win_width then
|
||||
return
|
||||
end
|
||||
@@ -66,6 +72,7 @@ local function show()
|
||||
style = "minimal",
|
||||
border = "none"
|
||||
})
|
||||
vim.wo[M.popup_win].winhl = view.View.winopts.winhl
|
||||
|
||||
local ns_id = vim.api.nvim_get_namespaces()["NvimTreeHighlights"]
|
||||
local extmarks = vim.api.nvim_buf_get_extmarks(0, ns_id, { line_nr - 1, 0 }, { line_nr - 1, -1 }, { details = true })
|
||||
@@ -88,7 +95,10 @@ local function show()
|
||||
end
|
||||
end
|
||||
end
|
||||
vim.cmd([[ setlocal nowrap cursorline noswapfile nobuflisted buftype=nofile bufhidden=wipe ]])
|
||||
vim.cmd([[ setlocal nowrap noswapfile nobuflisted buftype=nofile bufhidden=wipe ]])
|
||||
if opts.view.cursorline then
|
||||
vim.cmd([[ setlocal cursorline cursorlineopt=both ]])
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -114,7 +124,7 @@ M.setup = function(opts)
|
||||
pattern = { "NvimTree_*" },
|
||||
callback = function()
|
||||
if utils.is_nvim_tree_buf(0) then
|
||||
show()
|
||||
show(opts)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
@@ -95,15 +95,15 @@ function M.get_arrows(node)
|
||||
local dir = node:as(DirectoryNode)
|
||||
if dir then
|
||||
if dir.open then
|
||||
str = M.config.icons.glyphs.folder["arrow_open"] .. " "
|
||||
str = M.config.icons.glyphs.folder["arrow_open"] .. M.config.icons.padding.folder_arrow
|
||||
hl = "NvimTreeFolderArrowOpen"
|
||||
else
|
||||
str = M.config.icons.glyphs.folder["arrow_closed"] .. " "
|
||||
str = M.config.icons.glyphs.folder["arrow_closed"] .. M.config.icons.padding.folder_arrow
|
||||
end
|
||||
elseif M.config.indent_markers.enable then
|
||||
str = ""
|
||||
else
|
||||
str = " "
|
||||
str = " " .. string.rep(" ", #M.config.icons.padding.folder_arrow)
|
||||
end
|
||||
|
||||
return { str = str, hl = { hl } }
|
||||
|
||||
@@ -112,9 +112,8 @@ function Decorator:define_sign(icon)
|
||||
vim.fn.sign_undefine(name)
|
||||
end
|
||||
|
||||
-- don't use sign if not defined
|
||||
-- don't render sign if empty
|
||||
if #icon.str < 1 then
|
||||
self.icon_placement = "none"
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
local Iterator = require("nvim-tree.iterators.node-iterator")
|
||||
local notify = require("nvim-tree.notify")
|
||||
|
||||
local M = {
|
||||
debouncers = {},
|
||||
@@ -179,6 +178,21 @@ function M.find_node_line(node)
|
||||
return -1
|
||||
end
|
||||
|
||||
---@param extmarks vim.api.keyset.get_extmark_item[] as per vim.api.nvim_buf_get_extmarks
|
||||
---@return number
|
||||
function M.extmarks_length(extmarks)
|
||||
local length = 0
|
||||
for _, extmark in ipairs(extmarks) do
|
||||
local details = extmark[4]
|
||||
if details and details.virt_text then
|
||||
for _, text in ipairs(details.virt_text) do
|
||||
length = length + vim.fn.strchars(text[1])
|
||||
end
|
||||
end
|
||||
end
|
||||
return length
|
||||
end
|
||||
|
||||
-- get the node in the tree state depending on the absolute path of the node
|
||||
-- (grouped or hidden too)
|
||||
---@param path string
|
||||
@@ -295,11 +309,51 @@ function M.rename_loaded_buffers(old_path, new_path)
|
||||
end
|
||||
end
|
||||
|
||||
local is_windows_drive = function(path)
|
||||
return (M.is_windows) and (path:match("^%a:\\$") ~= nil)
|
||||
end
|
||||
|
||||
---@param path string path to file or directory
|
||||
---@return boolean
|
||||
function M.file_exists(path)
|
||||
local _, error = vim.loop.fs_stat(path)
|
||||
return error == nil
|
||||
if not (M.is_windows or M.is_wsl) then
|
||||
local _, error = vim.loop.fs_stat(path)
|
||||
return error == nil
|
||||
end
|
||||
|
||||
-- Windows is case-insensetive, but case-preserving
|
||||
-- If a file's name is being changed into itself
|
||||
-- with different casing, windows will falsely
|
||||
-- report that file is already existing, so a hand-rolled
|
||||
-- implementation of checking for existance is needed.
|
||||
-- Same holds for WSL, since it can sometimes
|
||||
-- access Windows files directly.
|
||||
-- For more details see (#3117).
|
||||
|
||||
if is_windows_drive(path) then
|
||||
return vim.fn.isdirectory(path) == 1
|
||||
end
|
||||
|
||||
local parent = vim.fn.fnamemodify(path, ":h")
|
||||
local filename = vim.fn.fnamemodify(path, ":t")
|
||||
|
||||
local handle = vim.loop.fs_scandir(parent)
|
||||
if not handle then
|
||||
-- File can not exist if its parent directory does not exist
|
||||
return false
|
||||
end
|
||||
|
||||
while true do
|
||||
local name, _ = vim.loop.fs_scandir_next(handle)
|
||||
if not name then
|
||||
break
|
||||
end
|
||||
if name == filename then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
---@param path string
|
||||
@@ -355,20 +409,6 @@ end
|
||||
---@param dst_pos string value pos
|
||||
---@param remove boolean
|
||||
function M.move_missing_val(src, src_path, src_pos, dst, dst_path, dst_pos, remove)
|
||||
local ok, err = pcall(vim.validate, {
|
||||
src = { src, "table" },
|
||||
src_path = { src_path, "string" },
|
||||
src_pos = { src_pos, "string" },
|
||||
dst = { dst, "table" },
|
||||
dst_path = { dst_path, "string" },
|
||||
dst_pos = { dst_pos, "string" },
|
||||
remove = { remove, "boolean" },
|
||||
})
|
||||
if not ok then
|
||||
notify.warn("move_missing_val: " .. (err or "invalid arguments"))
|
||||
return
|
||||
end
|
||||
|
||||
for pos in string.gmatch(src_path, "([^%.]+)%.*") do
|
||||
if src[pos] and type(src[pos]) == "table" then
|
||||
src = src[pos]
|
||||
|
||||
Reference in New Issue
Block a user