* refactor(#2875): multi instance renderer * refactor(#2875): multi instance renderer * refactor(#2875): multi instance renderer * refactor(#2875): multi instance renderer * node classes and constructors * node methods * refactor(#2875): multi instance renderer * node classes and constructors * explorer is a directory node * extract methods from explore_node * extract methods from explore_node * extract methods from explore_node * extract methods from lib * use .. name for root node for compatibility * use node.explorer * extract node factory, remove unused code * factories for all nodes, add RootNode * factories for all nodes, add RootNode * use factory pattern for decorators * note regression and commit * fix dir git status regression * destroy nodes, not explorer * add BaseNode:is * revert changes to create-file, handle in #2924 * extract methods from explorer * extract methods from explorer * extract methods from explorer * use Node everywhere in luadoc * extract methods from lib * extract methods from lib * lint * remove unused code * don't call methods on fake root node * get_node_at_cursor returns explorer (root) node instead of { name = '..' } * remove unused inject_node * refactor(#2875): multi instance renderer * refactor(#2875): multi instance renderer * refactor(#2875): multi instance renderer * extract methods from lib * node factory uses stat only * temporary DirectoryNode casting until method extraction into child classes * lua-language-server 3.10.5 -> 3.11.0 * explicitly call Explorer constructor * normalise explorer RootNode new call, tidy annotations
This commit is contained in:
parent
c9104a5d07
commit
38aac09151
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -69,7 +69,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
nvim_version: [ stable, nightly ]
|
nvim_version: [ stable, nightly ]
|
||||||
luals_version: [ 3.10.5 ]
|
luals_version: [ 3.11.0 ]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|||||||
@ -125,7 +125,7 @@ function M.place_cursor_on_node()
|
|||||||
if not node or node.name == ".." then
|
if not node or node.name == ".." then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
node = utils.get_parent_of_group(node)
|
node = node:get_parent_of_group()
|
||||||
|
|
||||||
local line = vim.api.nvim_get_current_line()
|
local line = vim.api.nvim_get_current_line()
|
||||||
local cursor = vim.api.nvim_win_get_cursor(0)
|
local cursor = vim.api.nvim_win_get_cursor(0)
|
||||||
@ -849,7 +849,7 @@ function M.setup(conf)
|
|||||||
require("nvim-tree.keymap").setup(opts)
|
require("nvim-tree.keymap").setup(opts)
|
||||||
require("nvim-tree.appearance").setup()
|
require("nvim-tree.appearance").setup()
|
||||||
require("nvim-tree.diagnostics").setup(opts)
|
require("nvim-tree.diagnostics").setup(opts)
|
||||||
require("nvim-tree.explorer").setup(opts)
|
require("nvim-tree.explorer"):setup(opts)
|
||||||
require("nvim-tree.git").setup(opts)
|
require("nvim-tree.git").setup(opts)
|
||||||
require("nvim-tree.git.utils").setup(opts)
|
require("nvim-tree.git.utils").setup(opts)
|
||||||
require("nvim-tree.view").setup(opts)
|
require("nvim-tree.view").setup(opts)
|
||||||
|
|||||||
@ -217,10 +217,10 @@ end
|
|||||||
---@param action ACTION
|
---@param action ACTION
|
||||||
---@param action_fn fun(source: string, dest: string)
|
---@param action_fn fun(source: string, dest: string)
|
||||||
function Clipboard:do_paste(node, action, action_fn)
|
function Clipboard:do_paste(node, action, action_fn)
|
||||||
node = lib.get_last_group_node(node)
|
if node.name == ".." then
|
||||||
local explorer = core.get_explorer()
|
node = self.explorer
|
||||||
if node.name == ".." and explorer then
|
else
|
||||||
node = explorer
|
node = node:last_group_node()
|
||||||
end
|
end
|
||||||
local clip = self.data[action]
|
local clip = self.data[action]
|
||||||
if #clip == 0 then
|
if #clip == 0 then
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
local utils = require("nvim-tree.utils")
|
local utils = require("nvim-tree.utils")
|
||||||
local events = require("nvim-tree.events")
|
local events = require("nvim-tree.events")
|
||||||
local lib = require("nvim-tree.lib")
|
|
||||||
local core = require("nvim-tree.core")
|
local core = require("nvim-tree.core")
|
||||||
local notify = require("nvim-tree.notify")
|
local notify = require("nvim-tree.notify")
|
||||||
|
|
||||||
@ -40,14 +39,13 @@ local function get_containing_folder(node)
|
|||||||
return node.absolute_path:sub(0, -node_name_size - 1)
|
return node.absolute_path:sub(0, -node_name_size - 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param node Node|nil
|
---@param node Node?
|
||||||
function M.fn(node)
|
function M.fn(node)
|
||||||
local cwd = core.get_cwd()
|
local cwd = core.get_cwd()
|
||||||
if cwd == nil then
|
if cwd == nil then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
node = node and lib.get_last_group_node(node)
|
|
||||||
if not node or node.name == ".." then
|
if not node or node.name == ".." then
|
||||||
node = {
|
node = {
|
||||||
absolute_path = cwd,
|
absolute_path = cwd,
|
||||||
@ -55,6 +53,8 @@ function M.fn(node)
|
|||||||
nodes = core.get_explorer().nodes,
|
nodes = core.get_explorer().nodes,
|
||||||
open = true,
|
open = true,
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
node = node:last_group_node()
|
||||||
end
|
end
|
||||||
|
|
||||||
local containing_folder = get_containing_folder(node)
|
local containing_folder = get_containing_folder(node)
|
||||||
|
|||||||
@ -120,7 +120,7 @@ function M.fn(default_modifier)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
node = lib.get_last_group_node(node)
|
node = node:last_group_node()
|
||||||
if node.name == ".." then
|
if node.name == ".." then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|||||||
@ -2,7 +2,6 @@ local utils = require("nvim-tree.utils")
|
|||||||
local view = require("nvim-tree.view")
|
local view = require("nvim-tree.view")
|
||||||
local core = require("nvim-tree.core")
|
local core = require("nvim-tree.core")
|
||||||
local lib = require("nvim-tree.lib")
|
local lib = require("nvim-tree.lib")
|
||||||
local explorer_node = require("nvim-tree.explorer.node")
|
|
||||||
local diagnostics = require("nvim-tree.diagnostics")
|
local diagnostics = require("nvim-tree.diagnostics")
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
@ -16,7 +15,7 @@ local MAX_DEPTH = 100
|
|||||||
---@return boolean
|
---@return boolean
|
||||||
local function status_is_valid(node, what, skip_gitignored)
|
local function status_is_valid(node, what, skip_gitignored)
|
||||||
if what == "git" then
|
if what == "git" then
|
||||||
local git_status = explorer_node.get_git_status(node)
|
local git_status = node:get_git_status()
|
||||||
return git_status ~= nil and (not skip_gitignored or git_status[1] ~= "!!")
|
return git_status ~= nil and (not skip_gitignored or git_status[1] ~= "!!")
|
||||||
elseif what == "diag" then
|
elseif what == "diag" then
|
||||||
local diag_status = diagnostics.get_diag_status(node)
|
local diag_status = diagnostics.get_diag_status(node)
|
||||||
@ -75,7 +74,7 @@ local function expand_node(node)
|
|||||||
if not node.open then
|
if not node.open then
|
||||||
-- Expand the node.
|
-- Expand the node.
|
||||||
-- Should never collapse since we checked open.
|
-- Should never collapse since we checked open.
|
||||||
lib.expand_or_collapse(node)
|
node:expand_or_collapse()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -98,7 +97,7 @@ local function move_next_recursive(what, skip_gitignored)
|
|||||||
valid = status_is_valid(node_init, what, skip_gitignored)
|
valid = status_is_valid(node_init, what, skip_gitignored)
|
||||||
end
|
end
|
||||||
if node_init.nodes ~= nil and valid and not node_init.open then
|
if node_init.nodes ~= nil and valid and not node_init.open then
|
||||||
lib.expand_or_collapse(node_init)
|
node_init:expand_or_collapse()
|
||||||
end
|
end
|
||||||
|
|
||||||
move("next", what, skip_gitignored)
|
move("next", what, skip_gitignored)
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
local view = require("nvim-tree.view")
|
local view = require("nvim-tree.view")
|
||||||
local utils = require("nvim-tree.utils")
|
local utils = require("nvim-tree.utils")
|
||||||
local core = require("nvim-tree.core")
|
local core = require("nvim-tree.core")
|
||||||
local lib = require("nvim-tree.lib")
|
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
@ -12,7 +11,7 @@ function M.fn(should_close)
|
|||||||
|
|
||||||
return function(node)
|
return function(node)
|
||||||
local explorer = core.get_explorer()
|
local explorer = core.get_explorer()
|
||||||
node = lib.get_last_group_node(node)
|
node = node:last_group_node()
|
||||||
if should_close and node.open then
|
if should_close and node.open then
|
||||||
node.open = false
|
node.open = false
|
||||||
if explorer then
|
if explorer then
|
||||||
@ -21,7 +20,7 @@ function M.fn(should_close)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local parent = utils.get_parent_of_group(node).parent
|
local parent = node:get_parent_of_group().parent
|
||||||
|
|
||||||
if not parent or not parent.parent then
|
if not parent or not parent.parent then
|
||||||
return view.set_cursor({ 1, 0 })
|
return view.set_cursor({ 1, 0 })
|
||||||
|
|||||||
@ -15,7 +15,7 @@ function M.fn(direction)
|
|||||||
local first, last, next, prev = nil, nil, nil, nil
|
local first, last, next, prev = nil, nil, nil, nil
|
||||||
local found = false
|
local found = false
|
||||||
local parent = node.parent or core.get_explorer()
|
local parent = node.parent or core.get_explorer()
|
||||||
Iterator.builder(parent.nodes)
|
Iterator.builder(parent and parent.nodes or {})
|
||||||
:recursor(function()
|
:recursor(function()
|
||||||
return nil
|
return nil
|
||||||
end)
|
end)
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
local core = require("nvim-tree.core")
|
local core = require("nvim-tree.core")
|
||||||
local Iterator = require("nvim-tree.iterators.node-iterator")
|
local Iterator = require("nvim-tree.iterators.node-iterator")
|
||||||
local notify = require("nvim-tree.notify")
|
local notify = require("nvim-tree.notify")
|
||||||
local lib = require("nvim-tree.lib")
|
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
@ -18,7 +17,7 @@ end
|
|||||||
|
|
||||||
---@param node Node
|
---@param node Node
|
||||||
local function expand(node)
|
local function expand(node)
|
||||||
node = lib.get_last_group_node(node)
|
node = node:last_group_node()
|
||||||
node.open = true
|
node.open = true
|
||||||
if #node.nodes == 0 then
|
if #node.nodes == 0 then
|
||||||
core.get_explorer():expand(node)
|
core.get_explorer():expand(node)
|
||||||
@ -62,10 +61,10 @@ local function gen_iterator()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param base_node table
|
---@param node Node
|
||||||
function M.fn(base_node)
|
function M.fn(node)
|
||||||
local explorer = core.get_explorer()
|
local explorer = core.get_explorer()
|
||||||
local node = base_node.nodes and base_node or explorer
|
node = node.nodes and node or explorer
|
||||||
if gen_iterator()(node) then
|
if gen_iterator()(node) then
|
||||||
notify.warn("expansion iteration was halted after " .. M.MAX_FOLDER_DISCOVERY .. " discovered folders")
|
notify.warn("expansion iteration was halted after " .. M.MAX_FOLDER_DISCOVERY .. " discovered folders")
|
||||||
end
|
end
|
||||||
|
|||||||
@ -138,7 +138,7 @@ Api.tree.change_root_to_node = wrap_node(function(node)
|
|||||||
if node.name == ".." then
|
if node.name == ".." then
|
||||||
actions.root.change_dir.fn("..")
|
actions.root.change_dir.fn("..")
|
||||||
elseif node.nodes ~= nil then
|
elseif node.nodes ~= nil then
|
||||||
actions.root.change_dir.fn(lib.get_last_group_node(node).absolute_path)
|
actions.root.change_dir.fn(node:last_group_node().absolute_path)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@ -198,7 +198,7 @@ Api.fs.copy.basename = wrap_node(wrap_explorer_member("clipboard", "copy_basenam
|
|||||||
Api.fs.copy.relative_path = wrap_node(wrap_explorer_member("clipboard", "copy_path"))
|
Api.fs.copy.relative_path = wrap_node(wrap_explorer_member("clipboard", "copy_path"))
|
||||||
|
|
||||||
---@param mode string
|
---@param mode string
|
||||||
---@param node table
|
---@param node Node
|
||||||
local function edit(mode, node)
|
local function edit(mode, node)
|
||||||
local path = node.absolute_path
|
local path = node.absolute_path
|
||||||
if node.link_to and not node.nodes then
|
if node.link_to and not node.nodes then
|
||||||
@ -214,7 +214,7 @@ local function open_or_expand_or_dir_up(mode, toggle_group)
|
|||||||
if node.name == ".." then
|
if node.name == ".." then
|
||||||
actions.root.change_dir.fn("..")
|
actions.root.change_dir.fn("..")
|
||||||
elseif node.nodes then
|
elseif node.nodes then
|
||||||
lib.expand_or_collapse(node, toggle_group)
|
node:expand_or_collapse(toggle_group)
|
||||||
elseif not toggle_group then
|
elseif not toggle_group then
|
||||||
edit(mode, node)
|
edit(mode, node)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -21,7 +21,7 @@ function M.reload_modified()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param node table
|
---@param node Node
|
||||||
---@return boolean
|
---@return boolean
|
||||||
function M.is_modified(node)
|
function M.is_modified(node)
|
||||||
return node
|
return node
|
||||||
@ -32,7 +32,7 @@ function M.is_modified(node)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---A buffer exists for the node's absolute path
|
---A buffer exists for the node's absolute path
|
||||||
---@param node table
|
---@param node Node
|
||||||
---@return boolean
|
---@return boolean
|
||||||
function M.is_opened(node)
|
function M.is_opened(node)
|
||||||
return node and vim.fn.bufloaded(node.absolute_path) > 0
|
return node and vim.fn.bufloaded(node.absolute_path) > 0
|
||||||
|
|||||||
@ -15,7 +15,7 @@ function M.init(foldername)
|
|||||||
if TreeExplorer then
|
if TreeExplorer then
|
||||||
TreeExplorer:destroy()
|
TreeExplorer:destroy()
|
||||||
end
|
end
|
||||||
TreeExplorer = require("nvim-tree.explorer"):new(foldername)
|
TreeExplorer = require("nvim-tree.explorer"):create(foldername)
|
||||||
if not first_init_done then
|
if not first_init_done then
|
||||||
events._dispatch_ready()
|
events._dispatch_ready()
|
||||||
first_init_done = true
|
first_init_done = true
|
||||||
|
|||||||
@ -1,5 +1,13 @@
|
|||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
|
---Must be synced with uv.fs_stat.result as it is compared with it
|
||||||
|
---@enum (key) NODE_TYPE
|
||||||
|
M.NODE_TYPE = {
|
||||||
|
directory = 1,
|
||||||
|
file = 2,
|
||||||
|
link = 4,
|
||||||
|
}
|
||||||
|
|
||||||
---Setup options for "highlight_*"
|
---Setup options for "highlight_*"
|
||||||
---@enum HL_POSITION
|
---@enum HL_POSITION
|
||||||
M.HL_POSITION = {
|
M.HL_POSITION = {
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
local builders = require("nvim-tree.explorer.node-builders")
|
|
||||||
local git = require("nvim-tree.git")
|
local git = require("nvim-tree.git")
|
||||||
local log = require("nvim-tree.log")
|
local log = require("nvim-tree.log")
|
||||||
local notify = require("nvim-tree.notify")
|
local notify = require("nvim-tree.notify")
|
||||||
local utils = require("nvim-tree.utils")
|
local utils = require("nvim-tree.utils")
|
||||||
local view = require("nvim-tree.view")
|
local view = require("nvim-tree.view")
|
||||||
local watch = require("nvim-tree.explorer.watch")
|
local node_factory = require("nvim-tree.node.factory")
|
||||||
local explorer_node = require("nvim-tree.explorer.node")
|
|
||||||
|
local RootNode = require("nvim-tree.node.root")
|
||||||
|
local Watcher = require("nvim-tree.watcher")
|
||||||
|
|
||||||
local Iterator = require("nvim-tree.iterators.node-iterator")
|
local Iterator = require("nvim-tree.iterators.node-iterator")
|
||||||
local NodeIterator = require("nvim-tree.iterators.node-iterator")
|
local NodeIterator = require("nvim-tree.iterators.node-iterator")
|
||||||
local Watcher = require("nvim-tree.watcher")
|
|
||||||
|
|
||||||
local Filters = require("nvim-tree.explorer.filters")
|
local Filters = require("nvim-tree.explorer.filters")
|
||||||
local Marks = require("nvim-tree.marks")
|
local Marks = require("nvim-tree.marks")
|
||||||
@ -22,23 +22,20 @@ local FILTER_REASON = require("nvim-tree.enum").FILTER_REASON
|
|||||||
|
|
||||||
local config
|
local config
|
||||||
|
|
||||||
---@class Explorer
|
---@class (exact) Explorer: RootNode
|
||||||
---@field opts table user options
|
---@field opts table user options
|
||||||
---@field absolute_path string
|
|
||||||
---@field nodes Node[]
|
|
||||||
---@field open boolean
|
|
||||||
---@field watcher Watcher|nil
|
|
||||||
---@field renderer Renderer
|
---@field renderer Renderer
|
||||||
---@field filters Filters
|
---@field filters Filters
|
||||||
---@field live_filter LiveFilter
|
---@field live_filter LiveFilter
|
||||||
---@field sorters Sorter
|
---@field sorters Sorter
|
||||||
---@field marks Marks
|
---@field marks Marks
|
||||||
---@field clipboard Clipboard
|
---@field clipboard Clipboard
|
||||||
local Explorer = {}
|
local Explorer = RootNode:new()
|
||||||
|
|
||||||
---@param path string|nil
|
---Static factory method
|
||||||
---@return Explorer|nil
|
---@param path string?
|
||||||
function Explorer:new(path)
|
---@return Explorer?
|
||||||
|
function Explorer:create(path)
|
||||||
local err
|
local err
|
||||||
|
|
||||||
if path then
|
if path then
|
||||||
@ -48,21 +45,22 @@ function Explorer:new(path)
|
|||||||
end
|
end
|
||||||
if not path then
|
if not path then
|
||||||
notify.error(err)
|
notify.error(err)
|
||||||
return
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local o = {
|
---@type Explorer
|
||||||
opts = config,
|
local explorer_placeholder = nil
|
||||||
absolute_path = path,
|
|
||||||
nodes = {},
|
|
||||||
open = true,
|
|
||||||
sorters = Sorters:new(config),
|
|
||||||
}
|
|
||||||
|
|
||||||
setmetatable(o, self)
|
local o = RootNode:create(explorer_placeholder, path, "..", nil)
|
||||||
self.__index = self
|
|
||||||
|
|
||||||
o.watcher = watch.create_watcher(o)
|
o = self:new(o) --[[@as Explorer]]
|
||||||
|
|
||||||
|
o.explorer = o
|
||||||
|
|
||||||
|
o.open = true
|
||||||
|
o.opts = config
|
||||||
|
|
||||||
|
o.sorters = Sorters:new(config)
|
||||||
o.renderer = Renderer:new(config, o)
|
o.renderer = Renderer:new(config, o)
|
||||||
o.filters = Filters:new(config, o)
|
o.filters = Filters:new(config, o)
|
||||||
o.live_filter = LiveFilter:new(config, o)
|
o.live_filter = LiveFilter:new(config, o)
|
||||||
@ -79,18 +77,6 @@ function Explorer:expand(node)
|
|||||||
self:_load(node)
|
self:_load(node)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Explorer:destroy()
|
|
||||||
local function iterate(node)
|
|
||||||
explorer_node.node_destroy(node)
|
|
||||||
if node.nodes then
|
|
||||||
for _, child in pairs(node.nodes) do
|
|
||||||
iterate(child)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
iterate(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param node Node
|
---@param node Node
|
||||||
---@param git_status table|nil
|
---@param git_status table|nil
|
||||||
function Explorer:reload(node, git_status)
|
function Explorer:reload(node, git_status)
|
||||||
@ -111,7 +97,7 @@ function Explorer:reload(node, git_status)
|
|||||||
|
|
||||||
local remain_childs = {}
|
local remain_childs = {}
|
||||||
|
|
||||||
local node_ignored = explorer_node.is_git_ignored(node)
|
local node_ignored = node:is_git_ignored()
|
||||||
---@type table<string, Node>
|
---@type table<string, Node>
|
||||||
local nodes_by_path = utils.key_by(node.nodes, "absolute_path")
|
local nodes_by_path = utils.key_by(node.nodes, "absolute_path")
|
||||||
|
|
||||||
@ -138,32 +124,19 @@ function Explorer:reload(node, git_status)
|
|||||||
if filter_reason == FILTER_REASON.none then
|
if filter_reason == FILTER_REASON.none then
|
||||||
remain_childs[abs] = true
|
remain_childs[abs] = true
|
||||||
|
|
||||||
-- Type must come from fs_stat and not fs_scandir_next to maintain sshfs compatibility
|
|
||||||
local t = stat and stat.type or nil
|
|
||||||
|
|
||||||
-- Recreate node if type changes.
|
-- Recreate node if type changes.
|
||||||
if nodes_by_path[abs] then
|
if nodes_by_path[abs] then
|
||||||
local n = nodes_by_path[abs]
|
local n = nodes_by_path[abs]
|
||||||
|
|
||||||
if n.type ~= t then
|
if not stat or n.type ~= stat.type then
|
||||||
utils.array_remove(node.nodes, n)
|
utils.array_remove(node.nodes, n)
|
||||||
explorer_node.node_destroy(n)
|
n:destroy()
|
||||||
nodes_by_path[abs] = nil
|
nodes_by_path[abs] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not nodes_by_path[abs] then
|
if not nodes_by_path[abs] then
|
||||||
local new_child = nil
|
local new_child = node_factory.create_node(self, node, abs, stat, name)
|
||||||
if t == "directory" and vim.loop.fs_access(abs, "R") and Watcher.is_fs_event_capable(abs) then
|
|
||||||
new_child = builders.folder(node, abs, name, stat)
|
|
||||||
elseif t == "file" then
|
|
||||||
new_child = builders.file(node, abs, name, stat)
|
|
||||||
elseif t == "link" then
|
|
||||||
local link = builders.link(node, abs, name, stat)
|
|
||||||
if link.link_to ~= nil then
|
|
||||||
new_child = link
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if new_child then
|
if new_child then
|
||||||
table.insert(node.nodes, new_child)
|
table.insert(node.nodes, new_child)
|
||||||
nodes_by_path[abs] = new_child
|
nodes_by_path[abs] = new_child
|
||||||
@ -171,7 +144,7 @@ function Explorer:reload(node, git_status)
|
|||||||
else
|
else
|
||||||
local n = nodes_by_path[abs]
|
local n = nodes_by_path[abs]
|
||||||
if n then
|
if n then
|
||||||
n.executable = builders.is_executable(abs) or false
|
n.executable = utils.is_executable(abs) or false
|
||||||
n.fs_stat = stat
|
n.fs_stat = stat
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -190,14 +163,14 @@ function Explorer:reload(node, git_status)
|
|||||||
if remain_childs[n.absolute_path] then
|
if remain_childs[n.absolute_path] then
|
||||||
return remain_childs[n.absolute_path]
|
return remain_childs[n.absolute_path]
|
||||||
else
|
else
|
||||||
explorer_node.node_destroy(n)
|
n:destroy()
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end, node.nodes)
|
end, node.nodes)
|
||||||
)
|
)
|
||||||
|
|
||||||
local is_root = not node.parent
|
local is_root = not node.parent
|
||||||
local child_folder_only = explorer_node.has_one_child_folder(node) and node.nodes[1]
|
local child_folder_only = node:has_one_child_folder() and node.nodes[1]
|
||||||
if config.renderer.group_empty and not is_root and child_folder_only then
|
if config.renderer.group_empty and not is_root and child_folder_only then
|
||||||
node.group_next = child_folder_only
|
node.group_next = child_folder_only
|
||||||
local ns = self:reload(child_folder_only, git_status)
|
local ns = self:reload(child_folder_only, git_status)
|
||||||
@ -212,26 +185,6 @@ function Explorer:reload(node, git_status)
|
|||||||
return node.nodes
|
return node.nodes
|
||||||
end
|
end
|
||||||
|
|
||||||
---TODO #2837 #2871 move this and similar to node
|
|
||||||
---Refresh contents and git status for a single node
|
|
||||||
---@param node Node
|
|
||||||
---@param callback function
|
|
||||||
function Explorer:refresh_node(node, callback)
|
|
||||||
if type(node) ~= "table" then
|
|
||||||
callback()
|
|
||||||
end
|
|
||||||
|
|
||||||
local parent_node = utils.get_parent_of_group(node)
|
|
||||||
|
|
||||||
self:reload_and_get_git_project(node.absolute_path, function(toplevel, project)
|
|
||||||
self:reload(parent_node, project)
|
|
||||||
|
|
||||||
self:update_parent_statuses(parent_node, project, toplevel)
|
|
||||||
|
|
||||||
callback()
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
---Refresh contents of all nodes to a path: actual directory and links.
|
---Refresh contents of all nodes to a path: actual directory and links.
|
||||||
---Groups will be expanded if needed.
|
---Groups will be expanded if needed.
|
||||||
---@param path string absolute path
|
---@param path string absolute path
|
||||||
@ -259,7 +212,7 @@ function Explorer:refresh_parent_nodes_for_path(path)
|
|||||||
local project = git.get_project(toplevel) or {}
|
local project = git.get_project(toplevel) or {}
|
||||||
|
|
||||||
self:reload(node, project)
|
self:reload(node, project)
|
||||||
self:update_parent_statuses(node, project, toplevel)
|
node:update_parent_statuses(project, toplevel)
|
||||||
end
|
end
|
||||||
|
|
||||||
log.profile_end(profile)
|
log.profile_end(profile)
|
||||||
@ -274,65 +227,19 @@ function Explorer:_load(node)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
---@param nodes_by_path table
|
---@param nodes_by_path Node[]
|
||||||
---@param node_ignored boolean
|
---@param node_ignored boolean
|
||||||
---@param status table|nil
|
---@param status table|nil
|
||||||
---@return fun(node: Node): table
|
---@return fun(node: Node): table
|
||||||
function Explorer:update_status(nodes_by_path, node_ignored, status)
|
function Explorer:update_status(nodes_by_path, node_ignored, status)
|
||||||
return function(node)
|
return function(node)
|
||||||
if nodes_by_path[node.absolute_path] then
|
if nodes_by_path[node.absolute_path] then
|
||||||
explorer_node.update_git_status(node, node_ignored, status)
|
node:update_git_status(node_ignored, status)
|
||||||
end
|
end
|
||||||
return node
|
return node
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---TODO #2837 #2871 move this and similar to node
|
|
||||||
---@private
|
|
||||||
---@param path string
|
|
||||||
---@param callback fun(toplevel: string|nil, project: table|nil)
|
|
||||||
function Explorer:reload_and_get_git_project(path, callback)
|
|
||||||
local toplevel = git.get_toplevel(path)
|
|
||||||
|
|
||||||
git.reload_project(toplevel, path, function()
|
|
||||||
callback(toplevel, git.get_project(toplevel) or {})
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
---TODO #2837 #2871 move this and similar to node
|
|
||||||
---@private
|
|
||||||
---@param node Node
|
|
||||||
---@param project table|nil
|
|
||||||
---@param root string|nil
|
|
||||||
function Explorer:update_parent_statuses(node, project, root)
|
|
||||||
while project and node do
|
|
||||||
-- step up to the containing project
|
|
||||||
if node.absolute_path == root then
|
|
||||||
-- stop at the top of the tree
|
|
||||||
if not node.parent then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
root = git.get_toplevel(node.parent.absolute_path)
|
|
||||||
|
|
||||||
-- stop when no more projects
|
|
||||||
if not root then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
-- update the containing project
|
|
||||||
project = git.get_project(root)
|
|
||||||
git.reload_project(root, node.absolute_path, nil)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- update status
|
|
||||||
explorer_node.update_git_status(node, explorer_node.is_git_ignored(node.parent), project)
|
|
||||||
|
|
||||||
-- maybe parent
|
|
||||||
node = node.parent
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
---@param handle uv.uv_fs_t
|
---@param handle uv.uv_fs_t
|
||||||
---@param cwd string
|
---@param cwd string
|
||||||
@ -340,7 +247,7 @@ end
|
|||||||
---@param git_status table
|
---@param git_status table
|
||||||
---@param parent Explorer
|
---@param parent Explorer
|
||||||
function Explorer:populate_children(handle, cwd, node, git_status, parent)
|
function Explorer:populate_children(handle, cwd, node, git_status, parent)
|
||||||
local node_ignored = explorer_node.is_git_ignored(node)
|
local node_ignored = node:is_git_ignored()
|
||||||
local nodes_by_path = utils.bool_record(node.nodes, "absolute_path")
|
local nodes_by_path = utils.bool_record(node.nodes, "absolute_path")
|
||||||
|
|
||||||
local filter_status = parent.filters:prepare(git_status)
|
local filter_status = parent.filters:prepare(git_status)
|
||||||
@ -368,23 +275,11 @@ function Explorer:populate_children(handle, cwd, node, git_status, parent)
|
|||||||
local stat = vim.loop.fs_lstat(abs)
|
local stat = vim.loop.fs_lstat(abs)
|
||||||
local filter_reason = parent.filters:should_filter_as_reason(abs, stat, filter_status)
|
local filter_reason = parent.filters:should_filter_as_reason(abs, stat, filter_status)
|
||||||
if filter_reason == FILTER_REASON.none and not nodes_by_path[abs] then
|
if filter_reason == FILTER_REASON.none and not nodes_by_path[abs] then
|
||||||
-- Type must come from fs_stat and not fs_scandir_next to maintain sshfs compatibility
|
local child = node_factory.create_node(self, node, abs, stat, name)
|
||||||
local t = stat and stat.type or nil
|
|
||||||
local child = nil
|
|
||||||
if t == "directory" and vim.loop.fs_access(abs, "R") then
|
|
||||||
child = builders.folder(node, abs, name, stat)
|
|
||||||
elseif t == "file" then
|
|
||||||
child = builders.file(node, abs, name, stat)
|
|
||||||
elseif t == "link" then
|
|
||||||
local link = builders.link(node, abs, name, stat)
|
|
||||||
if link.link_to ~= nil then
|
|
||||||
child = link
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if child then
|
if child then
|
||||||
table.insert(node.nodes, child)
|
table.insert(node.nodes, child)
|
||||||
nodes_by_path[child.absolute_path] = true
|
nodes_by_path[child.absolute_path] = true
|
||||||
explorer_node.update_git_status(child, node_ignored, git_status)
|
child:update_git_status(node_ignored, git_status)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
for reason, value in pairs(FILTER_REASON) do
|
for reason, value in pairs(FILTER_REASON) do
|
||||||
@ -416,7 +311,7 @@ function Explorer:explore(node, status, parent)
|
|||||||
self:populate_children(handle, cwd, node, status, parent)
|
self:populate_children(handle, cwd, node, status, parent)
|
||||||
|
|
||||||
local is_root = not node.parent
|
local is_root = not node.parent
|
||||||
local child_folder_only = explorer_node.has_one_child_folder(node) and node.nodes[1]
|
local child_folder_only = node:has_one_child_folder() and node.nodes[1]
|
||||||
if config.renderer.group_empty and not is_root and child_folder_only then
|
if config.renderer.group_empty and not is_root and child_folder_only then
|
||||||
local child_cwd = child_folder_only.link_to or child_folder_only.absolute_path
|
local child_cwd = child_folder_only.link_to or child_folder_only.absolute_path
|
||||||
local child_status = git.load_project_status(child_cwd)
|
local child_status = git.load_project_status(child_cwd)
|
||||||
@ -473,14 +368,13 @@ function Explorer:reload_git()
|
|||||||
event_running = true
|
event_running = true
|
||||||
|
|
||||||
local projects = git.reload()
|
local projects = git.reload()
|
||||||
explorer_node.reload_node_status(self, projects)
|
self:reload_node_status(projects)
|
||||||
self.renderer:draw()
|
self.renderer:draw()
|
||||||
event_running = false
|
event_running = false
|
||||||
end
|
end
|
||||||
|
|
||||||
function Explorer.setup(opts)
|
function Explorer:setup(opts)
|
||||||
config = opts
|
config = opts
|
||||||
require("nvim-tree.explorer.node").setup(opts)
|
|
||||||
require("nvim-tree.explorer.watch").setup(opts)
|
require("nvim-tree.explorer.watch").setup(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,7 @@ function LiveFilter:new(opts, explorer)
|
|||||||
return o
|
return o
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param node_ Node|nil
|
---@param node_ Node?
|
||||||
local function reset_filter(self, node_)
|
local function reset_filter(self, node_)
|
||||||
node_ = node_ or self.explorer
|
node_ = node_ or self.explorer
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ local function matches(self, node)
|
|||||||
return vim.regex(self.filter):match_str(name) ~= nil
|
return vim.regex(self.filter):match_str(name) ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param node_ Node|nil
|
---@param node_ Node?
|
||||||
function LiveFilter:apply_filter(node_)
|
function LiveFilter:apply_filter(node_)
|
||||||
if not self.filter or self.filter == "" then
|
if not self.filter or self.filter == "" then
|
||||||
reset_filter(self, node_)
|
reset_filter(self, node_)
|
||||||
|
|||||||
@ -1,107 +0,0 @@
|
|||||||
local utils = require("nvim-tree.utils")
|
|
||||||
local watch = require("nvim-tree.explorer.watch")
|
|
||||||
|
|
||||||
local M = {}
|
|
||||||
|
|
||||||
---@param parent Node
|
|
||||||
---@param absolute_path string
|
|
||||||
---@param name string
|
|
||||||
---@param fs_stat uv.fs_stat.result|nil
|
|
||||||
---@return Node
|
|
||||||
function M.folder(parent, absolute_path, name, fs_stat)
|
|
||||||
local handle = vim.loop.fs_scandir(absolute_path)
|
|
||||||
local has_children = handle and vim.loop.fs_scandir_next(handle) ~= nil
|
|
||||||
|
|
||||||
local node = {
|
|
||||||
type = "directory",
|
|
||||||
absolute_path = absolute_path,
|
|
||||||
fs_stat = fs_stat,
|
|
||||||
group_next = nil, -- If node is grouped, this points to the next child dir/link node
|
|
||||||
has_children = has_children,
|
|
||||||
name = name,
|
|
||||||
nodes = {},
|
|
||||||
open = false,
|
|
||||||
parent = parent,
|
|
||||||
}
|
|
||||||
|
|
||||||
node.watcher = watch.create_watcher(node)
|
|
||||||
|
|
||||||
return node
|
|
||||||
end
|
|
||||||
|
|
||||||
--- path is an executable file or directory
|
|
||||||
---@param absolute_path string
|
|
||||||
---@return boolean|nil
|
|
||||||
function M.is_executable(absolute_path)
|
|
||||||
if utils.is_windows or utils.is_wsl then
|
|
||||||
--- executable detection on windows is buggy and not performant hence it is disabled
|
|
||||||
return false
|
|
||||||
else
|
|
||||||
return vim.loop.fs_access(absolute_path, "X")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param parent Node
|
|
||||||
---@param absolute_path string
|
|
||||||
---@param name string
|
|
||||||
---@param fs_stat uv.fs_stat.result|nil
|
|
||||||
---@return Node
|
|
||||||
function M.file(parent, absolute_path, name, fs_stat)
|
|
||||||
local ext = string.match(name, ".?[^.]+%.(.*)") or ""
|
|
||||||
|
|
||||||
return {
|
|
||||||
type = "file",
|
|
||||||
absolute_path = absolute_path,
|
|
||||||
executable = M.is_executable(absolute_path),
|
|
||||||
extension = ext,
|
|
||||||
fs_stat = fs_stat,
|
|
||||||
name = name,
|
|
||||||
parent = parent,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- TODO-INFO: sometimes fs_realpath returns nil
|
|
||||||
-- I expect this be a bug in glibc, because it fails to retrieve the path for some
|
|
||||||
-- links (for instance libr2.so in /usr/lib) and thus even with a C program realpath fails
|
|
||||||
-- when it has no real reason to. Maybe there is a reason, but errno is definitely wrong.
|
|
||||||
-- So we need to check for link_to ~= nil when adding new links to the main tree
|
|
||||||
---@param parent Node
|
|
||||||
---@param absolute_path string
|
|
||||||
---@param name string
|
|
||||||
---@param fs_stat uv.fs_stat.result|nil
|
|
||||||
---@return Node
|
|
||||||
function M.link(parent, absolute_path, name, fs_stat)
|
|
||||||
--- I dont know if this is needed, because in my understanding, there isn't hard links in windows, but just to be sure i changed it.
|
|
||||||
local link_to = vim.loop.fs_realpath(absolute_path)
|
|
||||||
local open, nodes, has_children
|
|
||||||
|
|
||||||
local is_dir_link = (link_to ~= nil) and vim.loop.fs_stat(link_to).type == "directory"
|
|
||||||
|
|
||||||
if is_dir_link and link_to then
|
|
||||||
local handle = vim.loop.fs_scandir(link_to)
|
|
||||||
has_children = handle and vim.loop.fs_scandir_next(handle) ~= nil
|
|
||||||
open = false
|
|
||||||
nodes = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
local node = {
|
|
||||||
type = "link",
|
|
||||||
absolute_path = absolute_path,
|
|
||||||
fs_stat = fs_stat,
|
|
||||||
group_next = nil, -- If node is grouped, this points to the next child dir/link node
|
|
||||||
has_children = has_children,
|
|
||||||
link_to = link_to,
|
|
||||||
name = name,
|
|
||||||
nodes = nodes,
|
|
||||||
open = open,
|
|
||||||
parent = parent,
|
|
||||||
}
|
|
||||||
|
|
||||||
if is_dir_link then
|
|
||||||
node.watcher = watch.create_watcher(node)
|
|
||||||
end
|
|
||||||
|
|
||||||
return node
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
||||||
@ -1,183 +0,0 @@
|
|||||||
local git = {} -- circular dependencies
|
|
||||||
|
|
||||||
local M = {}
|
|
||||||
|
|
||||||
---@class GitStatus
|
|
||||||
---@field file string|nil
|
|
||||||
---@field dir table|nil
|
|
||||||
|
|
||||||
---@param parent_ignored boolean
|
|
||||||
---@param status table|nil
|
|
||||||
---@param absolute_path string
|
|
||||||
---@return GitStatus|nil
|
|
||||||
local function get_dir_git_status(parent_ignored, status, absolute_path)
|
|
||||||
if parent_ignored then
|
|
||||||
return { file = "!!" }
|
|
||||||
end
|
|
||||||
|
|
||||||
if status then
|
|
||||||
return {
|
|
||||||
file = status.files and status.files[absolute_path],
|
|
||||||
dir = status.dirs and {
|
|
||||||
direct = status.dirs.direct[absolute_path],
|
|
||||||
indirect = status.dirs.indirect[absolute_path],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param parent_ignored boolean
|
|
||||||
---@param status table
|
|
||||||
---@param absolute_path string
|
|
||||||
---@return GitStatus
|
|
||||||
local function get_git_status(parent_ignored, status, absolute_path)
|
|
||||||
local file_status = parent_ignored and "!!" or (status and status.files and status.files[absolute_path])
|
|
||||||
return { file = file_status }
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param node Node
|
|
||||||
---@return boolean
|
|
||||||
function M.has_one_child_folder(node)
|
|
||||||
return #node.nodes == 1 and node.nodes[1].nodes and vim.loop.fs_access(node.nodes[1].absolute_path, "R") or false
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param node Node
|
|
||||||
---@param parent_ignored boolean
|
|
||||||
---@param status table|nil
|
|
||||||
function M.update_git_status(node, parent_ignored, status)
|
|
||||||
local get_status
|
|
||||||
if node.nodes then
|
|
||||||
get_status = get_dir_git_status
|
|
||||||
else
|
|
||||||
get_status = get_git_status
|
|
||||||
end
|
|
||||||
|
|
||||||
-- status of the node's absolute path
|
|
||||||
node.git_status = get_status(parent_ignored, status, node.absolute_path)
|
|
||||||
|
|
||||||
-- status of the link target, if the link itself is not dirty
|
|
||||||
if node.link_to and not node.git_status then
|
|
||||||
node.git_status = get_status(parent_ignored, status, node.link_to)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param node Node
|
|
||||||
---@return GitStatus|nil
|
|
||||||
function M.get_git_status(node)
|
|
||||||
local git_status = node and node.git_status
|
|
||||||
if not git_status then
|
|
||||||
-- status doesn't exist
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
if not node.nodes then
|
|
||||||
-- file
|
|
||||||
return git_status.file and { git_status.file }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- dir
|
|
||||||
if not M.config.git.show_on_dirs then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local status = {}
|
|
||||||
if not require("nvim-tree.lib").get_last_group_node(node).open or M.config.git.show_on_open_dirs then
|
|
||||||
-- dir is closed or we should show on open_dirs
|
|
||||||
if git_status.file ~= nil then
|
|
||||||
table.insert(status, git_status.file)
|
|
||||||
end
|
|
||||||
if git_status.dir ~= nil then
|
|
||||||
if git_status.dir.direct ~= nil then
|
|
||||||
for _, s in pairs(node.git_status.dir.direct) do
|
|
||||||
table.insert(status, s)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if git_status.dir.indirect ~= nil then
|
|
||||||
for _, s in pairs(node.git_status.dir.indirect) do
|
|
||||||
table.insert(status, s)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- dir is open and we shouldn't show on open_dirs
|
|
||||||
if git_status.file ~= nil then
|
|
||||||
table.insert(status, git_status.file)
|
|
||||||
end
|
|
||||||
if git_status.dir ~= nil and git_status.dir.direct ~= nil then
|
|
||||||
local deleted = {
|
|
||||||
[" D"] = true,
|
|
||||||
["D "] = true,
|
|
||||||
["RD"] = true,
|
|
||||||
["DD"] = true,
|
|
||||||
}
|
|
||||||
for _, s in pairs(node.git_status.dir.direct) do
|
|
||||||
if deleted[s] then
|
|
||||||
table.insert(status, s)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if #status == 0 then
|
|
||||||
return nil
|
|
||||||
else
|
|
||||||
return status
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param parent_node Node|nil
|
|
||||||
---@param projects table
|
|
||||||
function M.reload_node_status(parent_node, projects)
|
|
||||||
if parent_node == nil then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local toplevel = git.get_toplevel(parent_node.absolute_path)
|
|
||||||
local status = projects[toplevel] or {}
|
|
||||||
for _, node in ipairs(parent_node.nodes) do
|
|
||||||
M.update_git_status(node, M.is_git_ignored(parent_node), status)
|
|
||||||
if node.nodes and #node.nodes > 0 then
|
|
||||||
M.reload_node_status(node, projects)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param node Node
|
|
||||||
---@return boolean
|
|
||||||
function M.is_git_ignored(node)
|
|
||||||
return node and node.git_status ~= nil and node.git_status.file == "!!"
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param node Node
|
|
||||||
---@return boolean
|
|
||||||
function M.is_dotfile(node)
|
|
||||||
if node == nil then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
if node.is_dot or (node.name and (node.name:sub(1, 1) == ".")) or M.is_dotfile(node.parent) then
|
|
||||||
node.is_dot = true
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param node Node
|
|
||||||
function M.node_destroy(node)
|
|
||||||
if not node then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if node.watcher then
|
|
||||||
node.watcher:destroy()
|
|
||||||
node.watcher = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.setup(opts)
|
|
||||||
M.config = {
|
|
||||||
git = opts.git,
|
|
||||||
}
|
|
||||||
|
|
||||||
git = require("nvim-tree.git")
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
||||||
@ -111,7 +111,7 @@ local function split_merge(t, first, last, comparator)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---Perform a merge sort using sorter option.
|
---Perform a merge sort using sorter option.
|
||||||
---@param t table nodes
|
---@param t Node[]
|
||||||
function Sorter:sort(t)
|
function Sorter:sort(t)
|
||||||
if self.user then
|
if self.user then
|
||||||
local t_user = {}
|
local t_user = {}
|
||||||
|
|||||||
@ -76,12 +76,7 @@ function M.create_watcher(node)
|
|||||||
else
|
else
|
||||||
log.line("watcher", "node event executing refresh '%s'", node.absolute_path)
|
log.line("watcher", "node event executing refresh '%s'", node.absolute_path)
|
||||||
end
|
end
|
||||||
local explorer = require("nvim-tree.core").get_explorer()
|
node:refresh()
|
||||||
if explorer then
|
|
||||||
explorer:refresh_node(node, function()
|
|
||||||
explorer.renderer:draw()
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,10 @@ local git_utils = require("nvim-tree.git.utils")
|
|||||||
local Runner = require("nvim-tree.git.runner")
|
local Runner = require("nvim-tree.git.runner")
|
||||||
local Watcher = require("nvim-tree.watcher").Watcher
|
local Watcher = require("nvim-tree.watcher").Watcher
|
||||||
local Iterator = require("nvim-tree.iterators.node-iterator")
|
local Iterator = require("nvim-tree.iterators.node-iterator")
|
||||||
local explorer_node = require("nvim-tree.explorer.node")
|
|
||||||
|
---@class GitStatus
|
||||||
|
---@field file string|nil
|
||||||
|
---@field dir table|nil
|
||||||
|
|
||||||
local M = {
|
local M = {
|
||||||
config = {},
|
config = {},
|
||||||
@ -208,18 +211,15 @@ local function reload_tree_at(toplevel)
|
|||||||
Iterator.builder(root_node.nodes)
|
Iterator.builder(root_node.nodes)
|
||||||
:hidden()
|
:hidden()
|
||||||
:applier(function(node)
|
:applier(function(node)
|
||||||
local parent_ignored = explorer_node.is_git_ignored(node.parent)
|
local parent_ignored = node.parent and node.parent:is_git_ignored() or false
|
||||||
explorer_node.update_git_status(node, parent_ignored, git_status)
|
node:update_git_status(parent_ignored, git_status)
|
||||||
end)
|
end)
|
||||||
:recursor(function(node)
|
:recursor(function(node)
|
||||||
return node.nodes and #node.nodes > 0 and node.nodes
|
return node.nodes and #node.nodes > 0 and node.nodes
|
||||||
end)
|
end)
|
||||||
:iterate()
|
:iterate()
|
||||||
|
|
||||||
local explorer = require("nvim-tree.core").get_explorer()
|
root_node.explorer.renderer:draw()
|
||||||
if explorer then
|
|
||||||
explorer.renderer:draw()
|
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -283,6 +283,35 @@ function M.load_project_status(path)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param parent_ignored boolean
|
||||||
|
---@param status table|nil
|
||||||
|
---@param absolute_path string
|
||||||
|
---@return GitStatus|nil
|
||||||
|
function M.git_status_dir(parent_ignored, status, absolute_path)
|
||||||
|
if parent_ignored then
|
||||||
|
return { file = "!!" }
|
||||||
|
end
|
||||||
|
|
||||||
|
if status then
|
||||||
|
return {
|
||||||
|
file = status.files and status.files[absolute_path],
|
||||||
|
dir = status.dirs and {
|
||||||
|
direct = status.dirs.direct[absolute_path],
|
||||||
|
indirect = status.dirs.indirect[absolute_path],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param parent_ignored boolean
|
||||||
|
---@param status table|nil
|
||||||
|
---@param absolute_path string
|
||||||
|
---@return GitStatus
|
||||||
|
function M.git_status_file(parent_ignored, status, absolute_path)
|
||||||
|
local file_status = parent_ignored and "!!" or (status and status.files and status.files[absolute_path])
|
||||||
|
return { file = file_status }
|
||||||
|
end
|
||||||
|
|
||||||
function M.purge_state()
|
function M.purge_state()
|
||||||
log.line("git", "purge_state")
|
log.line("git", "purge_state")
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,6 @@ local core = require("nvim-tree.core")
|
|||||||
local utils = require("nvim-tree.utils")
|
local utils = require("nvim-tree.utils")
|
||||||
local events = require("nvim-tree.events")
|
local events = require("nvim-tree.events")
|
||||||
local notify = require("nvim-tree.notify")
|
local notify = require("nvim-tree.notify")
|
||||||
local explorer_node = require("nvim-tree.explorer.node")
|
|
||||||
|
|
||||||
---@class LibOpenOpts
|
---@class LibOpenOpts
|
||||||
---@field path string|nil path
|
---@field path string|nil path
|
||||||
@ -15,6 +14,7 @@ local M = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
---Cursor position as per vim.api.nvim_win_get_cursor
|
---Cursor position as per vim.api.nvim_win_get_cursor
|
||||||
|
---nil on no explorer or invalid view win
|
||||||
---@return integer[]|nil
|
---@return integer[]|nil
|
||||||
function M.get_cursor_position()
|
function M.get_cursor_position()
|
||||||
if not core.get_explorer() then
|
if not core.get_explorer() then
|
||||||
@ -31,154 +31,28 @@ end
|
|||||||
|
|
||||||
---@return Node|nil
|
---@return Node|nil
|
||||||
function M.get_node_at_cursor()
|
function M.get_node_at_cursor()
|
||||||
|
local explorer = core.get_explorer()
|
||||||
|
if not explorer then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local cursor = M.get_cursor_position()
|
local cursor = M.get_cursor_position()
|
||||||
if not cursor then
|
if not cursor then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if cursor[1] == 1 and view.is_root_folder_visible(core.get_cwd()) then
|
if cursor[1] == 1 and view.is_root_folder_visible(core.get_cwd()) then
|
||||||
return { name = ".." }
|
return explorer
|
||||||
end
|
end
|
||||||
|
|
||||||
return utils.get_nodes_by_line(core.get_explorer().nodes, core.get_nodes_starting_line())[cursor[1]]
|
return utils.get_nodes_by_line(explorer.nodes, core.get_nodes_starting_line())[cursor[1]]
|
||||||
end
|
|
||||||
|
|
||||||
---Create a sanitized partial copy of a node, populating children recursively.
|
|
||||||
---@param node Node|nil
|
|
||||||
---@return Node|nil cloned node
|
|
||||||
local function clone_node(node)
|
|
||||||
if not node then
|
|
||||||
node = core.get_explorer()
|
|
||||||
if not node then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local n = {
|
|
||||||
absolute_path = node.absolute_path,
|
|
||||||
executable = node.executable,
|
|
||||||
extension = node.extension,
|
|
||||||
git_status = node.git_status,
|
|
||||||
has_children = node.has_children,
|
|
||||||
hidden = node.hidden,
|
|
||||||
link_to = node.link_to,
|
|
||||||
name = node.name,
|
|
||||||
open = node.open,
|
|
||||||
type = node.type,
|
|
||||||
fs_stat = node.fs_stat,
|
|
||||||
}
|
|
||||||
|
|
||||||
if type(node.nodes) == "table" then
|
|
||||||
n.nodes = {}
|
|
||||||
for _, child in ipairs(node.nodes) do
|
|
||||||
table.insert(n.nodes, clone_node(child))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return n
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---Api.tree.get_nodes
|
---Api.tree.get_nodes
|
||||||
---@return Node[]|nil
|
---@return Node[]?
|
||||||
function M.get_nodes()
|
function M.get_nodes()
|
||||||
return clone_node(core.get_explorer())
|
|
||||||
end
|
|
||||||
|
|
||||||
-- If node is grouped, return the last node in the group. Otherwise, return the given node.
|
|
||||||
---@param node Node
|
|
||||||
---@return Node
|
|
||||||
function M.get_last_group_node(node)
|
|
||||||
while node and node.group_next do
|
|
||||||
node = node.group_next
|
|
||||||
end
|
|
||||||
|
|
||||||
return node ---@diagnostic disable-line: return-type-mismatch -- it can't be nil
|
|
||||||
end
|
|
||||||
|
|
||||||
---Group empty folders
|
|
||||||
-- Recursively group nodes
|
|
||||||
---@param node Node
|
|
||||||
---@return Node[]
|
|
||||||
function M.group_empty_folders(node)
|
|
||||||
local is_root = not node.parent
|
|
||||||
local child_folder_only = explorer_node.has_one_child_folder(node) and node.nodes[1]
|
|
||||||
if M.group_empty and not is_root and child_folder_only then
|
|
||||||
node.group_next = child_folder_only
|
|
||||||
local ns = M.group_empty_folders(child_folder_only)
|
|
||||||
node.nodes = ns or {}
|
|
||||||
return ns
|
|
||||||
end
|
|
||||||
return node.nodes
|
|
||||||
end
|
|
||||||
|
|
||||||
---Ungroup empty folders
|
|
||||||
-- If a node is grouped, ungroup it: put node.group_next to the node.nodes and set node.group_next to nil
|
|
||||||
---@param node Node
|
|
||||||
function M.ungroup_empty_folders(node)
|
|
||||||
local cur = node
|
|
||||||
while cur and cur.group_next do
|
|
||||||
cur.nodes = { cur.group_next }
|
|
||||||
cur.group_next = nil
|
|
||||||
cur = cur.nodes[1]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param node Node
|
|
||||||
---@return Node[]
|
|
||||||
function M.get_all_nodes_in_group(node)
|
|
||||||
local next_node = utils.get_parent_of_group(node)
|
|
||||||
local nodes = {}
|
|
||||||
while next_node do
|
|
||||||
table.insert(nodes, next_node)
|
|
||||||
next_node = next_node.group_next
|
|
||||||
end
|
|
||||||
return nodes
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Toggle group empty folders
|
|
||||||
---@param head_node Node
|
|
||||||
local function toggle_group_folders(head_node)
|
|
||||||
local is_grouped = head_node.group_next ~= nil
|
|
||||||
|
|
||||||
if is_grouped then
|
|
||||||
M.ungroup_empty_folders(head_node)
|
|
||||||
else
|
|
||||||
M.group_empty_folders(head_node)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param node Node
|
|
||||||
function M.expand_or_collapse(node, toggle_group)
|
|
||||||
local explorer = core.get_explorer()
|
local explorer = core.get_explorer()
|
||||||
|
return explorer and explorer:clone()
|
||||||
toggle_group = toggle_group or false
|
|
||||||
if node.has_children then
|
|
||||||
node.has_children = false
|
|
||||||
end
|
|
||||||
|
|
||||||
if #node.nodes == 0 and explorer then
|
|
||||||
explorer:expand(node)
|
|
||||||
end
|
|
||||||
|
|
||||||
local head_node = utils.get_parent_of_group(node)
|
|
||||||
if toggle_group then
|
|
||||||
toggle_group_folders(head_node)
|
|
||||||
end
|
|
||||||
|
|
||||||
local open = M.get_last_group_node(node).open
|
|
||||||
local next_open
|
|
||||||
if toggle_group then
|
|
||||||
next_open = open
|
|
||||||
else
|
|
||||||
next_open = not open
|
|
||||||
end
|
|
||||||
for _, n in ipairs(M.get_all_nodes_in_group(head_node)) do
|
|
||||||
n.open = next_open
|
|
||||||
end
|
|
||||||
|
|
||||||
if explorer then
|
|
||||||
explorer.renderer:draw()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.set_target_win()
|
function M.set_target_win()
|
||||||
|
|||||||
@ -71,7 +71,7 @@ end
|
|||||||
--- Write to log file the inspection of a node
|
--- Write to log file the inspection of a node
|
||||||
--- defaults to the node under cursor if none is provided
|
--- defaults to the node under cursor if none is provided
|
||||||
---@param typ string as per log.types config
|
---@param typ string as per log.types config
|
||||||
---@param node table|nil node to be inspected
|
---@param node Node? node to be inspected
|
||||||
---@param fmt string for string.format
|
---@param fmt string for string.format
|
||||||
---@vararg any arguments for string.format
|
---@vararg any arguments for string.format
|
||||||
function M.node(typ, node, fmt, ...)
|
function M.node(typ, node, fmt, ...)
|
||||||
|
|||||||
@ -8,6 +8,8 @@ local rename_file = require("nvim-tree.actions.fs.rename-file")
|
|||||||
local trash = require("nvim-tree.actions.fs.trash")
|
local trash = require("nvim-tree.actions.fs.trash")
|
||||||
local utils = require("nvim-tree.utils")
|
local utils = require("nvim-tree.utils")
|
||||||
|
|
||||||
|
local DirectoryNode = require("nvim-tree.node.directory")
|
||||||
|
|
||||||
---@class Marks
|
---@class Marks
|
||||||
---@field config table hydrated user opts.filters
|
---@field config table hydrated user opts.filters
|
||||||
---@field private explorer Explorer
|
---@field private explorer Explorer
|
||||||
@ -152,7 +154,7 @@ function Marks:bulk_move()
|
|||||||
local node_at_cursor = lib.get_node_at_cursor()
|
local node_at_cursor = lib.get_node_at_cursor()
|
||||||
local default_path = core.get_cwd()
|
local default_path = core.get_cwd()
|
||||||
|
|
||||||
if node_at_cursor and node_at_cursor.type == "directory" then
|
if node_at_cursor and node_at_cursor:is(DirectoryNode) then
|
||||||
default_path = node_at_cursor.absolute_path
|
default_path = node_at_cursor.absolute_path
|
||||||
elseif node_at_cursor and node_at_cursor.parent then
|
elseif node_at_cursor and node_at_cursor.parent then
|
||||||
default_path = node_at_cursor.parent.absolute_path
|
default_path = node_at_cursor.parent.absolute_path
|
||||||
|
|||||||
@ -1,36 +0,0 @@
|
|||||||
---@meta
|
|
||||||
|
|
||||||
---@class ParentNode
|
|
||||||
---@field name string
|
|
||||||
|
|
||||||
---@class BaseNode
|
|
||||||
---@field absolute_path string
|
|
||||||
---@field executable boolean
|
|
||||||
---@field fs_stat uv.fs_stat.result|nil
|
|
||||||
---@field git_status GitStatus|nil
|
|
||||||
---@field hidden boolean
|
|
||||||
---@field is_dot boolean
|
|
||||||
---@field name string
|
|
||||||
---@field parent DirNode
|
|
||||||
---@field type string
|
|
||||||
---@field watcher function|nil
|
|
||||||
---@field diag_status DiagStatus|nil
|
|
||||||
|
|
||||||
---@class DirNode: BaseNode
|
|
||||||
---@field has_children boolean
|
|
||||||
---@field group_next Node|nil
|
|
||||||
---@field nodes Node[]
|
|
||||||
---@field open boolean
|
|
||||||
---@field hidden_stats table -- Each field of this table is a key for source and value for count
|
|
||||||
|
|
||||||
---@class FileNode: BaseNode
|
|
||||||
---@field extension string
|
|
||||||
|
|
||||||
---@class SymlinkDirNode: DirNode
|
|
||||||
---@field link_to string
|
|
||||||
|
|
||||||
---@class SymlinkFileNode: FileNode
|
|
||||||
---@field link_to string
|
|
||||||
|
|
||||||
---@alias SymlinkNode SymlinkDirNode|SymlinkFileNode
|
|
||||||
---@alias Node ParentNode|DirNode|FileNode|SymlinkNode|Explorer
|
|
||||||
79
lua/nvim-tree/node/directory.lua
Normal file
79
lua/nvim-tree/node/directory.lua
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
local watch = require("nvim-tree.explorer.watch")
|
||||||
|
|
||||||
|
local BaseNode = require("nvim-tree.node")
|
||||||
|
|
||||||
|
---@class (exact) DirectoryNode: BaseNode
|
||||||
|
---@field has_children boolean
|
||||||
|
---@field group_next Node? -- If node is grouped, this points to the next child dir/link node
|
||||||
|
---@field nodes Node[]
|
||||||
|
---@field open boolean
|
||||||
|
---@field hidden_stats table? -- Each field of this table is a key for source and value for count
|
||||||
|
local DirectoryNode = BaseNode:new()
|
||||||
|
|
||||||
|
---Static factory method
|
||||||
|
---@param explorer Explorer
|
||||||
|
---@param parent Node?
|
||||||
|
---@param absolute_path string
|
||||||
|
---@param name string
|
||||||
|
---@param fs_stat uv.fs_stat.result|nil
|
||||||
|
---@return DirectoryNode
|
||||||
|
function DirectoryNode:create(explorer, parent, absolute_path, name, fs_stat)
|
||||||
|
local handle = vim.loop.fs_scandir(absolute_path)
|
||||||
|
local has_children = handle and vim.loop.fs_scandir_next(handle) ~= nil or false
|
||||||
|
|
||||||
|
---@type DirectoryNode
|
||||||
|
local o = {
|
||||||
|
type = "directory",
|
||||||
|
explorer = explorer,
|
||||||
|
absolute_path = absolute_path,
|
||||||
|
executable = false,
|
||||||
|
fs_stat = fs_stat,
|
||||||
|
git_status = nil,
|
||||||
|
hidden = false,
|
||||||
|
is_dot = false,
|
||||||
|
name = name,
|
||||||
|
parent = parent,
|
||||||
|
watcher = nil,
|
||||||
|
diag_status = nil,
|
||||||
|
|
||||||
|
has_children = has_children,
|
||||||
|
group_next = nil,
|
||||||
|
nodes = {},
|
||||||
|
open = false,
|
||||||
|
hidden_stats = nil,
|
||||||
|
}
|
||||||
|
o = self:new(o) --[[@as DirectoryNode]]
|
||||||
|
|
||||||
|
o.watcher = watch.create_watcher(o)
|
||||||
|
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
|
||||||
|
function DirectoryNode:destroy()
|
||||||
|
BaseNode.destroy(self)
|
||||||
|
if self.nodes then
|
||||||
|
for _, node in pairs(self.nodes) do
|
||||||
|
node:destroy()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---Create a sanitized partial copy of a node, populating children recursively.
|
||||||
|
---@return DirectoryNode cloned
|
||||||
|
function DirectoryNode:clone()
|
||||||
|
local clone = BaseNode.clone(self) --[[@as DirectoryNode]]
|
||||||
|
|
||||||
|
clone.has_children = self.has_children
|
||||||
|
clone.group_next = nil
|
||||||
|
clone.nodes = {}
|
||||||
|
clone.open = self.open
|
||||||
|
clone.hidden_stats = nil
|
||||||
|
|
||||||
|
for _, child in ipairs(self.nodes) do
|
||||||
|
table.insert(clone.nodes, child:clone())
|
||||||
|
end
|
||||||
|
|
||||||
|
return clone
|
||||||
|
end
|
||||||
|
|
||||||
|
return DirectoryNode
|
||||||
31
lua/nvim-tree/node/factory.lua
Normal file
31
lua/nvim-tree/node/factory.lua
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
local DirectoryNode = require("nvim-tree.node.directory")
|
||||||
|
local LinkNode = require("nvim-tree.node.link")
|
||||||
|
local FileNode = require("nvim-tree.node.file")
|
||||||
|
local Watcher = require("nvim-tree.watcher")
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
---Factory function to create the appropriate Node
|
||||||
|
---@param explorer Explorer
|
||||||
|
---@param parent Node
|
||||||
|
---@param abs string
|
||||||
|
---@param stat uv.fs_stat.result? -- on nil stat return nil Node
|
||||||
|
---@param name string
|
||||||
|
---@return Node?
|
||||||
|
function M.create_node(explorer, parent, abs, stat, name)
|
||||||
|
if not stat then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if stat.type == "directory" and vim.loop.fs_access(abs, "R") and Watcher.is_fs_event_capable(abs) then
|
||||||
|
return DirectoryNode:create(explorer, parent, abs, name, stat)
|
||||||
|
elseif stat.type == "file" then
|
||||||
|
return FileNode:create(explorer, parent, abs, name, stat)
|
||||||
|
elseif stat.type == "link" then
|
||||||
|
return LinkNode:create(explorer, parent, abs, name, stat)
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
49
lua/nvim-tree/node/file.lua
Normal file
49
lua/nvim-tree/node/file.lua
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
local utils = require("nvim-tree.utils")
|
||||||
|
|
||||||
|
local BaseNode = require("nvim-tree.node")
|
||||||
|
|
||||||
|
---@class (exact) FileNode: BaseNode
|
||||||
|
---@field extension string
|
||||||
|
local FileNode = BaseNode:new()
|
||||||
|
|
||||||
|
---Static factory method
|
||||||
|
---@param explorer Explorer
|
||||||
|
---@param parent Node
|
||||||
|
---@param absolute_path string
|
||||||
|
---@param name string
|
||||||
|
---@param fs_stat uv.fs_stat.result?
|
||||||
|
---@return FileNode
|
||||||
|
function FileNode:create(explorer, parent, absolute_path, name, fs_stat)
|
||||||
|
---@type FileNode
|
||||||
|
local o = {
|
||||||
|
type = "file",
|
||||||
|
explorer = explorer,
|
||||||
|
absolute_path = absolute_path,
|
||||||
|
executable = utils.is_executable(absolute_path),
|
||||||
|
fs_stat = fs_stat,
|
||||||
|
git_status = nil,
|
||||||
|
hidden = false,
|
||||||
|
is_dot = false,
|
||||||
|
name = name,
|
||||||
|
parent = parent,
|
||||||
|
watcher = nil,
|
||||||
|
diag_status = nil,
|
||||||
|
|
||||||
|
extension = string.match(name, ".?[^.]+%.(.*)") or "",
|
||||||
|
}
|
||||||
|
o = self:new(o) --[[@as FileNode]]
|
||||||
|
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
|
||||||
|
---Create a sanitized partial copy of a node, populating children recursively.
|
||||||
|
---@return FileNode cloned
|
||||||
|
function FileNode:clone()
|
||||||
|
local clone = BaseNode.clone(self) --[[@as FileNode]]
|
||||||
|
|
||||||
|
clone.extension = self.extension
|
||||||
|
|
||||||
|
return clone
|
||||||
|
end
|
||||||
|
|
||||||
|
return FileNode
|
||||||
347
lua/nvim-tree/node/init.lua
Normal file
347
lua/nvim-tree/node/init.lua
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
local git = require("nvim-tree.git")
|
||||||
|
|
||||||
|
---Abstract Node class.
|
||||||
|
---Uses the abstract factory pattern to instantiate child instances.
|
||||||
|
---@class (exact) BaseNode
|
||||||
|
---@field private __index? table
|
||||||
|
---@field type NODE_TYPE
|
||||||
|
---@field explorer Explorer
|
||||||
|
---@field absolute_path string
|
||||||
|
---@field executable boolean
|
||||||
|
---@field fs_stat uv.fs_stat.result?
|
||||||
|
---@field git_status GitStatus?
|
||||||
|
---@field hidden boolean
|
||||||
|
---@field is_dot boolean
|
||||||
|
---@field name string
|
||||||
|
---@field parent Node?
|
||||||
|
---@field watcher Watcher?
|
||||||
|
---@field diag_status DiagStatus?
|
||||||
|
local BaseNode = {}
|
||||||
|
|
||||||
|
---@alias Node RootNode|BaseNode|DirectoryNode|FileNode|LinkNode
|
||||||
|
|
||||||
|
---@param o BaseNode?
|
||||||
|
---@return BaseNode
|
||||||
|
function BaseNode:new(o)
|
||||||
|
o = o or {}
|
||||||
|
|
||||||
|
setmetatable(o, self)
|
||||||
|
self.__index = self
|
||||||
|
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
|
||||||
|
function BaseNode:destroy()
|
||||||
|
if self.watcher then
|
||||||
|
self.watcher:destroy()
|
||||||
|
self.watcher = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---From plenary
|
||||||
|
---Checks if the object is an instance
|
||||||
|
---This will start with the lowest class and loop over all the superclasses.
|
||||||
|
---@param self BaseNode
|
||||||
|
---@param T BaseNode
|
||||||
|
---@return boolean
|
||||||
|
function BaseNode:is(T)
|
||||||
|
local mt = getmetatable(self)
|
||||||
|
while mt do
|
||||||
|
if mt == T then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
mt = getmetatable(mt)
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return boolean
|
||||||
|
function BaseNode:has_one_child_folder()
|
||||||
|
return #self.nodes == 1 and self.nodes[1].nodes and vim.loop.fs_access(self.nodes[1].absolute_path, "R") or false
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param parent_ignored boolean
|
||||||
|
---@param status table|nil
|
||||||
|
function BaseNode:update_git_status(parent_ignored, status)
|
||||||
|
local get_status
|
||||||
|
if self.nodes then
|
||||||
|
get_status = git.git_status_dir
|
||||||
|
else
|
||||||
|
get_status = git.git_status_file
|
||||||
|
end
|
||||||
|
|
||||||
|
-- status of the node's absolute path
|
||||||
|
self.git_status = get_status(parent_ignored, status, self.absolute_path)
|
||||||
|
|
||||||
|
-- status of the link target, if the link itself is not dirty
|
||||||
|
if self.link_to and not self.git_status then
|
||||||
|
self.git_status = get_status(parent_ignored, status, self.link_to)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return GitStatus|nil
|
||||||
|
function BaseNode:get_git_status()
|
||||||
|
if not self.git_status then
|
||||||
|
-- status doesn't exist
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if not self.nodes then
|
||||||
|
-- file
|
||||||
|
return self.git_status.file and { self.git_status.file }
|
||||||
|
end
|
||||||
|
|
||||||
|
-- dir
|
||||||
|
if not self.explorer.opts.git.show_on_dirs then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local status = {}
|
||||||
|
if not self:last_group_node().open or self.explorer.opts.git.show_on_open_dirs then
|
||||||
|
-- dir is closed or we should show on open_dirs
|
||||||
|
if self.git_status.file ~= nil then
|
||||||
|
table.insert(status, self.git_status.file)
|
||||||
|
end
|
||||||
|
if self.git_status.dir ~= nil then
|
||||||
|
if self.git_status.dir.direct ~= nil then
|
||||||
|
for _, s in pairs(self.git_status.dir.direct) do
|
||||||
|
table.insert(status, s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if self.git_status.dir.indirect ~= nil then
|
||||||
|
for _, s in pairs(self.git_status.dir.indirect) do
|
||||||
|
table.insert(status, s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- dir is open and we shouldn't show on open_dirs
|
||||||
|
if self.git_status.file ~= nil then
|
||||||
|
table.insert(status, self.git_status.file)
|
||||||
|
end
|
||||||
|
if self.git_status.dir ~= nil and self.git_status.dir.direct ~= nil then
|
||||||
|
local deleted = {
|
||||||
|
[" D"] = true,
|
||||||
|
["D "] = true,
|
||||||
|
["RD"] = true,
|
||||||
|
["DD"] = true,
|
||||||
|
}
|
||||||
|
for _, s in pairs(self.git_status.dir.direct) do
|
||||||
|
if deleted[s] then
|
||||||
|
table.insert(status, s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #status == 0 then
|
||||||
|
return nil
|
||||||
|
else
|
||||||
|
return status
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param projects table
|
||||||
|
function BaseNode:reload_node_status(projects)
|
||||||
|
local toplevel = git.get_toplevel(self.absolute_path)
|
||||||
|
local status = projects[toplevel] or {}
|
||||||
|
for _, node in ipairs(self.nodes) do
|
||||||
|
node:update_git_status(self:is_git_ignored(), status)
|
||||||
|
if node.nodes and #node.nodes > 0 then
|
||||||
|
self:reload_node_status(projects)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return boolean
|
||||||
|
function BaseNode:is_git_ignored()
|
||||||
|
return self.git_status ~= nil and self.git_status.file == "!!"
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return boolean
|
||||||
|
function BaseNode:is_dotfile()
|
||||||
|
if
|
||||||
|
self.is_dot --
|
||||||
|
or (self.name and (self.name:sub(1, 1) == ".")) --
|
||||||
|
or (self.parent and self.parent:is_dotfile())
|
||||||
|
then
|
||||||
|
self.is_dot = true
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If node is grouped, return the last node in the group. Otherwise, return the given node.
|
||||||
|
---@return Node
|
||||||
|
function BaseNode:last_group_node()
|
||||||
|
local node = self --[[@as BaseNode]]
|
||||||
|
|
||||||
|
while node.group_next do
|
||||||
|
node = node.group_next
|
||||||
|
end
|
||||||
|
|
||||||
|
return node
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param project table|nil
|
||||||
|
---@param root string|nil
|
||||||
|
function BaseNode:update_parent_statuses(project, root)
|
||||||
|
local node = self
|
||||||
|
while project and node do
|
||||||
|
-- step up to the containing project
|
||||||
|
if node.absolute_path == root then
|
||||||
|
-- stop at the top of the tree
|
||||||
|
if not node.parent then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
root = git.get_toplevel(node.parent.absolute_path)
|
||||||
|
|
||||||
|
-- stop when no more projects
|
||||||
|
if not root then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
-- update the containing project
|
||||||
|
project = git.get_project(root)
|
||||||
|
git.reload_project(root, node.absolute_path, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- update status
|
||||||
|
node:update_git_status(node.parent and node.parent:is_git_ignored() or false, project)
|
||||||
|
|
||||||
|
-- maybe parent
|
||||||
|
node = node.parent
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---Refresh contents and git status for a single node
|
||||||
|
function BaseNode:refresh()
|
||||||
|
local parent_node = self:get_parent_of_group()
|
||||||
|
local toplevel = git.get_toplevel(self.absolute_path)
|
||||||
|
|
||||||
|
git.reload_project(toplevel, self.absolute_path, function()
|
||||||
|
local project = git.get_project(toplevel) or {}
|
||||||
|
|
||||||
|
self.explorer:reload(parent_node, project)
|
||||||
|
|
||||||
|
parent_node:update_parent_statuses(project, toplevel)
|
||||||
|
|
||||||
|
self.explorer.renderer:draw()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
---Get the highest parent of grouped nodes
|
||||||
|
---@return Node node or parent
|
||||||
|
function BaseNode:get_parent_of_group()
|
||||||
|
local node = self
|
||||||
|
while node and node.parent and node.parent.group_next do
|
||||||
|
node = node.parent or node
|
||||||
|
end
|
||||||
|
return node
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return Node[]
|
||||||
|
function BaseNode:get_all_nodes_in_group()
|
||||||
|
local next_node = self:get_parent_of_group()
|
||||||
|
local nodes = {}
|
||||||
|
while next_node do
|
||||||
|
table.insert(nodes, next_node)
|
||||||
|
next_node = next_node.group_next
|
||||||
|
end
|
||||||
|
return nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Toggle group empty folders
|
||||||
|
function BaseNode:toggle_group_folders()
|
||||||
|
local is_grouped = self.group_next ~= nil
|
||||||
|
|
||||||
|
if is_grouped then
|
||||||
|
self:ungroup_empty_folders()
|
||||||
|
else
|
||||||
|
self:group_empty_folders()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---Group empty folders
|
||||||
|
-- Recursively group nodes
|
||||||
|
---@return Node[]
|
||||||
|
function BaseNode:group_empty_folders()
|
||||||
|
local is_root = not self.parent
|
||||||
|
local child_folder_only = self:has_one_child_folder() and self.nodes[1]
|
||||||
|
if self.explorer.opts.renderer.group_empty and not is_root and child_folder_only then
|
||||||
|
---@cast self DirectoryNode -- TODO move this to the class
|
||||||
|
self.group_next = child_folder_only
|
||||||
|
local ns = child_folder_only:group_empty_folders()
|
||||||
|
self.nodes = ns or {}
|
||||||
|
return ns
|
||||||
|
end
|
||||||
|
return self.nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
---Ungroup empty folders
|
||||||
|
-- If a node is grouped, ungroup it: put node.group_next to the node.nodes and set node.group_next to nil
|
||||||
|
function BaseNode:ungroup_empty_folders()
|
||||||
|
local cur = self
|
||||||
|
while cur and cur.group_next do
|
||||||
|
cur.nodes = { cur.group_next }
|
||||||
|
cur.group_next = nil
|
||||||
|
cur = cur.nodes[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function BaseNode:expand_or_collapse(toggle_group)
|
||||||
|
toggle_group = toggle_group or false
|
||||||
|
if self.has_children then
|
||||||
|
---@cast self DirectoryNode -- TODO move this to the class
|
||||||
|
self.has_children = false
|
||||||
|
end
|
||||||
|
|
||||||
|
if #self.nodes == 0 then
|
||||||
|
self.explorer:expand(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
local head_node = self:get_parent_of_group()
|
||||||
|
if toggle_group then
|
||||||
|
head_node:toggle_group_folders()
|
||||||
|
end
|
||||||
|
|
||||||
|
local open = self:last_group_node().open
|
||||||
|
local next_open
|
||||||
|
if toggle_group then
|
||||||
|
next_open = open
|
||||||
|
else
|
||||||
|
next_open = not open
|
||||||
|
end
|
||||||
|
for _, n in ipairs(head_node:get_all_nodes_in_group()) do
|
||||||
|
n.open = next_open
|
||||||
|
end
|
||||||
|
|
||||||
|
self.explorer.renderer:draw()
|
||||||
|
end
|
||||||
|
|
||||||
|
---Create a sanitized partial copy of a node, populating children recursively.
|
||||||
|
---@return BaseNode cloned
|
||||||
|
function BaseNode:clone()
|
||||||
|
---@type Explorer
|
||||||
|
local explorer_placeholder = nil
|
||||||
|
|
||||||
|
---@type BaseNode
|
||||||
|
local clone = {
|
||||||
|
type = self.type,
|
||||||
|
explorer = explorer_placeholder,
|
||||||
|
absolute_path = self.absolute_path,
|
||||||
|
executable = self.executable,
|
||||||
|
fs_stat = self.fs_stat,
|
||||||
|
git_status = self.git_status,
|
||||||
|
hidden = self.hidden,
|
||||||
|
is_dot = self.is_dot,
|
||||||
|
name = self.name,
|
||||||
|
parent = nil,
|
||||||
|
watcher = nil,
|
||||||
|
diag_status = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
return clone
|
||||||
|
end
|
||||||
|
|
||||||
|
return BaseNode
|
||||||
89
lua/nvim-tree/node/link.lua
Normal file
89
lua/nvim-tree/node/link.lua
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
local watch = require("nvim-tree.explorer.watch")
|
||||||
|
|
||||||
|
local BaseNode = require("nvim-tree.node")
|
||||||
|
|
||||||
|
---@class (exact) LinkNode: BaseNode
|
||||||
|
---@field has_children boolean
|
||||||
|
---@field group_next Node? -- If node is grouped, this points to the next child dir/link node
|
||||||
|
---@field link_to string absolute path
|
||||||
|
---@field nodes Node[]
|
||||||
|
---@field open boolean
|
||||||
|
local LinkNode = BaseNode:new()
|
||||||
|
|
||||||
|
---Static factory method
|
||||||
|
---@param explorer Explorer
|
||||||
|
---@param parent Node
|
||||||
|
---@param absolute_path string
|
||||||
|
---@param name string
|
||||||
|
---@param fs_stat uv.fs_stat.result?
|
||||||
|
---@return LinkNode? nil on vim.loop.fs_realpath failure
|
||||||
|
function LinkNode:create(explorer, parent, absolute_path, name, fs_stat)
|
||||||
|
-- INFO: sometimes fs_realpath returns nil
|
||||||
|
-- I expect this be a bug in glibc, because it fails to retrieve the path for some
|
||||||
|
-- links (for instance libr2.so in /usr/lib) and thus even with a C program realpath fails
|
||||||
|
-- when it has no real reason to. Maybe there is a reason, but errno is definitely wrong.
|
||||||
|
local link_to = vim.loop.fs_realpath(absolute_path)
|
||||||
|
if not link_to then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local open, nodes, has_children
|
||||||
|
local is_dir_link = (link_to ~= nil) and vim.loop.fs_stat(link_to).type == "directory"
|
||||||
|
|
||||||
|
if is_dir_link and link_to then
|
||||||
|
local handle = vim.loop.fs_scandir(link_to)
|
||||||
|
has_children = handle and vim.loop.fs_scandir_next(handle) ~= nil or false
|
||||||
|
open = false
|
||||||
|
nodes = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
---@type LinkNode
|
||||||
|
local o = {
|
||||||
|
type = "link",
|
||||||
|
explorer = explorer,
|
||||||
|
absolute_path = absolute_path,
|
||||||
|
executable = false,
|
||||||
|
fs_stat = fs_stat,
|
||||||
|
hidden = false,
|
||||||
|
is_dot = false,
|
||||||
|
name = name,
|
||||||
|
parent = parent,
|
||||||
|
watcher = nil,
|
||||||
|
diag_status = nil,
|
||||||
|
|
||||||
|
has_children = has_children,
|
||||||
|
group_next = nil,
|
||||||
|
link_to = link_to,
|
||||||
|
nodes = nodes,
|
||||||
|
open = open,
|
||||||
|
}
|
||||||
|
o = self:new(o) --[[@as LinkNode]]
|
||||||
|
|
||||||
|
if is_dir_link then
|
||||||
|
o.watcher = watch.create_watcher(o)
|
||||||
|
end
|
||||||
|
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
|
||||||
|
---Create a sanitized partial copy of a node, populating children recursively.
|
||||||
|
---@return LinkNode cloned
|
||||||
|
function LinkNode:clone()
|
||||||
|
local clone = BaseNode.clone(self) --[[@as LinkNode]]
|
||||||
|
|
||||||
|
clone.has_children = self.has_children
|
||||||
|
clone.group_next = nil
|
||||||
|
clone.link_to = self.link_to
|
||||||
|
clone.nodes = {}
|
||||||
|
clone.open = self.open
|
||||||
|
|
||||||
|
if self.nodes then
|
||||||
|
for _, child in ipairs(self.nodes) do
|
||||||
|
table.insert(clone.nodes, child:clone())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return clone
|
||||||
|
end
|
||||||
|
|
||||||
|
return LinkNode
|
||||||
20
lua/nvim-tree/node/root.lua
Normal file
20
lua/nvim-tree/node/root.lua
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
local DirectoryNode = require("nvim-tree.node.directory")
|
||||||
|
|
||||||
|
---@class (exact) RootNode: DirectoryNode
|
||||||
|
local RootNode = DirectoryNode:new()
|
||||||
|
|
||||||
|
---Static factory method
|
||||||
|
---@param explorer Explorer
|
||||||
|
---@param absolute_path string
|
||||||
|
---@param name string
|
||||||
|
---@param fs_stat uv.fs_stat.result|nil
|
||||||
|
---@return RootNode
|
||||||
|
function RootNode:create(explorer, absolute_path, name, fs_stat)
|
||||||
|
local o = DirectoryNode:create(explorer, nil, absolute_path, name, fs_stat)
|
||||||
|
|
||||||
|
o = self:new(o) --[[@as RootNode]]
|
||||||
|
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
|
||||||
|
return RootNode
|
||||||
@ -68,14 +68,14 @@ function Builder:new(opts, explorer)
|
|||||||
virtual_lines = {},
|
virtual_lines = {},
|
||||||
decorators = {
|
decorators = {
|
||||||
-- priority order
|
-- priority order
|
||||||
DecoratorCut:new(opts, explorer),
|
DecoratorCut:create(opts, explorer),
|
||||||
DecoratorCopied:new(opts, explorer),
|
DecoratorCopied:create(opts, explorer),
|
||||||
DecoratorDiagnostics:new(opts, explorer),
|
DecoratorDiagnostics:create(opts, explorer),
|
||||||
DecoratorBookmarks:new(opts, explorer),
|
DecoratorBookmarks:create(opts, explorer),
|
||||||
DecoratorModified:new(opts, explorer),
|
DecoratorModified:create(opts, explorer),
|
||||||
DecoratorHidden:new(opts, explorer),
|
DecoratorHidden:create(opts, explorer),
|
||||||
DecoratorOpened:new(opts, explorer),
|
DecoratorOpened:create(opts, explorer),
|
||||||
DecoratorGit:new(opts, explorer),
|
DecoratorGit:create(opts, explorer),
|
||||||
},
|
},
|
||||||
hidden_display = Builder:setup_hidden_display_function(opts),
|
hidden_display = Builder:setup_hidden_display_function(opts),
|
||||||
}
|
}
|
||||||
@ -137,7 +137,7 @@ function Builder:unwrap_highlighted_strings(highlighted_strings)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
---@param node table
|
---@param node Node
|
||||||
---@return HighlightedString icon
|
---@return HighlightedString icon
|
||||||
---@return HighlightedString name
|
---@return HighlightedString name
|
||||||
function Builder:build_folder(node)
|
function Builder:build_folder(node)
|
||||||
@ -189,7 +189,7 @@ function Builder:build_symlink(node)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
---@param node table
|
---@param node Node
|
||||||
---@return HighlightedString icon
|
---@return HighlightedString icon
|
||||||
---@return HighlightedString name
|
---@return HighlightedString name
|
||||||
function Builder:build_file(node)
|
function Builder:build_file(node)
|
||||||
@ -369,7 +369,7 @@ function Builder:build_line(node, idx, num_children)
|
|||||||
|
|
||||||
self.index = self.index + 1
|
self.index = self.index + 1
|
||||||
|
|
||||||
node = require("nvim-tree.lib").get_last_group_node(node)
|
node = node:last_group_node()
|
||||||
if node.open then
|
if node.open then
|
||||||
self.depth = self.depth + 1
|
self.depth = self.depth + 1
|
||||||
self:build_lines(node)
|
self:build_lines(node)
|
||||||
@ -487,7 +487,7 @@ function Builder:build()
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
---TODO refactor back to function; this was left here to reduce PR noise
|
---@private
|
||||||
---@param opts table
|
---@param opts table
|
||||||
---@return fun(node: Node): string|nil
|
---@return fun(node: Node): string|nil
|
||||||
function Builder:setup_hidden_display_function(opts)
|
function Builder:setup_hidden_display_function(opts)
|
||||||
|
|||||||
@ -14,7 +14,7 @@ local M = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
---Diagnostics highlight group and position when highlight_diagnostics.
|
---Diagnostics highlight group and position when highlight_diagnostics.
|
||||||
---@param node table
|
---@param node Node
|
||||||
---@return HL_POSITION position none when no status
|
---@return HL_POSITION position none when no status
|
||||||
---@return string|nil group only when status
|
---@return string|nil group only when status
|
||||||
function M.get_highlight(node)
|
function M.get_highlight(node)
|
||||||
@ -38,7 +38,7 @@ function M.get_highlight(node)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---diagnostics icon if there is a status
|
---diagnostics icon if there is a status
|
||||||
---@param node table
|
---@param node Node
|
||||||
---@return HighlightedString|nil modified icon
|
---@return HighlightedString|nil modified icon
|
||||||
function M.get_icon(node)
|
function M.get_icon(node)
|
||||||
if node and M.config.diagnostics.enable and M.config.renderer.icons.show.diagnostics then
|
if node and M.config.diagnostics.enable and M.config.renderer.icons.show.diagnostics then
|
||||||
|
|||||||
@ -59,7 +59,7 @@ end
|
|||||||
---@param depth integer
|
---@param depth integer
|
||||||
---@param idx integer
|
---@param idx integer
|
||||||
---@param nodes_number integer
|
---@param nodes_number integer
|
||||||
---@param node table
|
---@param node Node
|
||||||
---@param markers table
|
---@param markers table
|
||||||
---@return HighlightedString[]
|
---@return HighlightedString[]
|
||||||
function M.get_indent_markers(depth, idx, nodes_number, node, markers, early_stop)
|
function M.get_indent_markers(depth, idx, nodes_number, node, markers, early_stop)
|
||||||
@ -79,7 +79,7 @@ function M.get_indent_markers(depth, idx, nodes_number, node, markers, early_sto
|
|||||||
return { str = str, hl = { "NvimTreeIndentMarker" } }
|
return { str = str, hl = { "NvimTreeIndentMarker" } }
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param node table
|
---@param node Node
|
||||||
---@return HighlightedString[]|nil
|
---@return HighlightedString[]|nil
|
||||||
function M.get_arrows(node)
|
function M.get_arrows(node)
|
||||||
if not M.config.icons.show.folder_arrow then
|
if not M.config.icons.show.folder_arrow then
|
||||||
|
|||||||
@ -4,20 +4,22 @@ local ICON_PLACEMENT = require("nvim-tree.enum").ICON_PLACEMENT
|
|||||||
local Decorator = require("nvim-tree.renderer.decorator")
|
local Decorator = require("nvim-tree.renderer.decorator")
|
||||||
|
|
||||||
---@class (exact) DecoratorBookmarks: Decorator
|
---@class (exact) DecoratorBookmarks: Decorator
|
||||||
---@field icon HighlightedString
|
---@field icon HighlightedString?
|
||||||
local DecoratorBookmarks = Decorator:new()
|
local DecoratorBookmarks = Decorator:new()
|
||||||
|
|
||||||
|
---Static factory method
|
||||||
---@param opts table
|
---@param opts table
|
||||||
---@param explorer Explorer
|
---@param explorer Explorer
|
||||||
---@return DecoratorBookmarks
|
---@return DecoratorBookmarks
|
||||||
function DecoratorBookmarks:new(opts, explorer)
|
function DecoratorBookmarks:create(opts, explorer)
|
||||||
local o = Decorator.new(self, {
|
---@type DecoratorBookmarks
|
||||||
|
local o = {
|
||||||
explorer = explorer,
|
explorer = explorer,
|
||||||
enabled = true,
|
enabled = true,
|
||||||
hl_pos = HL_POSITION[opts.renderer.highlight_bookmarks] or HL_POSITION.none,
|
hl_pos = HL_POSITION[opts.renderer.highlight_bookmarks] or HL_POSITION.none,
|
||||||
icon_placement = ICON_PLACEMENT[opts.renderer.icons.bookmarks_placement] or ICON_PLACEMENT.none,
|
icon_placement = ICON_PLACEMENT[opts.renderer.icons.bookmarks_placement] or ICON_PLACEMENT.none,
|
||||||
})
|
}
|
||||||
---@cast o DecoratorBookmarks
|
o = self:new(o) --[[@as DecoratorBookmarks]]
|
||||||
|
|
||||||
if opts.renderer.icons.show.bookmarks then
|
if opts.renderer.icons.show.bookmarks then
|
||||||
o.icon = {
|
o.icon = {
|
||||||
|
|||||||
@ -4,21 +4,22 @@ local ICON_PLACEMENT = require("nvim-tree.enum").ICON_PLACEMENT
|
|||||||
local Decorator = require("nvim-tree.renderer.decorator")
|
local Decorator = require("nvim-tree.renderer.decorator")
|
||||||
|
|
||||||
---@class (exact) DecoratorCopied: Decorator
|
---@class (exact) DecoratorCopied: Decorator
|
||||||
---@field enabled boolean
|
---@field icon HighlightedString?
|
||||||
---@field icon HighlightedString|nil
|
|
||||||
local DecoratorCopied = Decorator:new()
|
local DecoratorCopied = Decorator:new()
|
||||||
|
|
||||||
|
---Static factory method
|
||||||
---@param opts table
|
---@param opts table
|
||||||
---@param explorer Explorer
|
---@param explorer Explorer
|
||||||
---@return DecoratorCopied
|
---@return DecoratorCopied
|
||||||
function DecoratorCopied:new(opts, explorer)
|
function DecoratorCopied:create(opts, explorer)
|
||||||
local o = Decorator.new(self, {
|
---@type DecoratorCopied
|
||||||
|
local o = {
|
||||||
explorer = explorer,
|
explorer = explorer,
|
||||||
enabled = true,
|
enabled = true,
|
||||||
hl_pos = HL_POSITION[opts.renderer.highlight_clipboard] or HL_POSITION.none,
|
hl_pos = HL_POSITION[opts.renderer.highlight_clipboard] or HL_POSITION.none,
|
||||||
icon_placement = ICON_PLACEMENT.none,
|
icon_placement = ICON_PLACEMENT.none,
|
||||||
})
|
}
|
||||||
---@cast o DecoratorCopied
|
o = self:new(o) --[[@as DecoratorCopied]]
|
||||||
|
|
||||||
return o
|
return o
|
||||||
end
|
end
|
||||||
|
|||||||
@ -4,21 +4,21 @@ local ICON_PLACEMENT = require("nvim-tree.enum").ICON_PLACEMENT
|
|||||||
local Decorator = require("nvim-tree.renderer.decorator")
|
local Decorator = require("nvim-tree.renderer.decorator")
|
||||||
|
|
||||||
---@class (exact) DecoratorCut: Decorator
|
---@class (exact) DecoratorCut: Decorator
|
||||||
---@field enabled boolean
|
|
||||||
---@field icon HighlightedString|nil
|
|
||||||
local DecoratorCut = Decorator:new()
|
local DecoratorCut = Decorator:new()
|
||||||
|
|
||||||
|
---Static factory method
|
||||||
---@param opts table
|
---@param opts table
|
||||||
---@param explorer Explorer
|
---@param explorer Explorer
|
||||||
---@return DecoratorCut
|
---@return DecoratorCut
|
||||||
function DecoratorCut:new(opts, explorer)
|
function DecoratorCut:create(opts, explorer)
|
||||||
local o = Decorator.new(self, {
|
---@type DecoratorCut
|
||||||
|
local o = {
|
||||||
explorer = explorer,
|
explorer = explorer,
|
||||||
enabled = true,
|
enabled = true,
|
||||||
hl_pos = HL_POSITION[opts.renderer.highlight_clipboard] or HL_POSITION.none,
|
hl_pos = HL_POSITION[opts.renderer.highlight_clipboard] or HL_POSITION.none,
|
||||||
icon_placement = ICON_PLACEMENT.none,
|
icon_placement = ICON_PLACEMENT.none,
|
||||||
})
|
}
|
||||||
---@cast o DecoratorCut
|
o = self:new(o) --[[@as DecoratorCut]]
|
||||||
|
|
||||||
return o
|
return o
|
||||||
end
|
end
|
||||||
|
|||||||
@ -33,20 +33,22 @@ local ICON_KEYS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
---@class (exact) DecoratorDiagnostics: Decorator
|
---@class (exact) DecoratorDiagnostics: Decorator
|
||||||
---@field icons HighlightedString[]
|
---@field icons HighlightedString[]?
|
||||||
local DecoratorDiagnostics = Decorator:new()
|
local DecoratorDiagnostics = Decorator:new()
|
||||||
|
|
||||||
|
---Static factory method
|
||||||
---@param opts table
|
---@param opts table
|
||||||
---@param explorer Explorer
|
---@param explorer Explorer
|
||||||
---@return DecoratorDiagnostics
|
---@return DecoratorDiagnostics
|
||||||
function DecoratorDiagnostics:new(opts, explorer)
|
function DecoratorDiagnostics:create(opts, explorer)
|
||||||
local o = Decorator.new(self, {
|
---@type DecoratorDiagnostics
|
||||||
|
local o = {
|
||||||
explorer = explorer,
|
explorer = explorer,
|
||||||
enabled = opts.diagnostics.enable,
|
enabled = opts.diagnostics.enable,
|
||||||
hl_pos = HL_POSITION[opts.renderer.highlight_diagnostics] or HL_POSITION.none,
|
hl_pos = HL_POSITION[opts.renderer.highlight_diagnostics] or HL_POSITION.none,
|
||||||
icon_placement = ICON_PLACEMENT[opts.renderer.icons.diagnostics_placement] or ICON_PLACEMENT.none,
|
icon_placement = ICON_PLACEMENT[opts.renderer.icons.diagnostics_placement] or ICON_PLACEMENT.none,
|
||||||
})
|
}
|
||||||
---@cast o DecoratorDiagnostics
|
o = self:new(o) --[[@as DecoratorDiagnostics]]
|
||||||
|
|
||||||
if not o.enabled then
|
if not o.enabled then
|
||||||
return o
|
return o
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
local notify = require("nvim-tree.notify")
|
local notify = require("nvim-tree.notify")
|
||||||
local explorer_node = require("nvim-tree.explorer.node")
|
|
||||||
|
|
||||||
local HL_POSITION = require("nvim-tree.enum").HL_POSITION
|
local HL_POSITION = require("nvim-tree.enum").HL_POSITION
|
||||||
local ICON_PLACEMENT = require("nvim-tree.enum").ICON_PLACEMENT
|
local ICON_PLACEMENT = require("nvim-tree.enum").ICON_PLACEMENT
|
||||||
@ -10,23 +9,25 @@ local Decorator = require("nvim-tree.renderer.decorator")
|
|||||||
---@field ord number decreasing priority
|
---@field ord number decreasing priority
|
||||||
|
|
||||||
---@class (exact) DecoratorGit: Decorator
|
---@class (exact) DecoratorGit: Decorator
|
||||||
---@field file_hl table<string, string> by porcelain status e.g. "AM"
|
---@field file_hl table<string, string>? by porcelain status e.g. "AM"
|
||||||
---@field folder_hl table<string, string> by porcelain status
|
---@field folder_hl table<string, string>? by porcelain status
|
||||||
---@field icons_by_status HighlightedStringGit[] by human status
|
---@field icons_by_status HighlightedStringGit[]? by human status
|
||||||
---@field icons_by_xy table<string, HighlightedStringGit[]> by porcelain status
|
---@field icons_by_xy table<string, HighlightedStringGit[]>? by porcelain status
|
||||||
local DecoratorGit = Decorator:new()
|
local DecoratorGit = Decorator:new()
|
||||||
|
|
||||||
|
---Static factory method
|
||||||
---@param opts table
|
---@param opts table
|
||||||
---@param explorer Explorer
|
---@param explorer Explorer
|
||||||
---@return DecoratorGit
|
---@return DecoratorGit
|
||||||
function DecoratorGit:new(opts, explorer)
|
function DecoratorGit:create(opts, explorer)
|
||||||
local o = Decorator.new(self, {
|
---@type DecoratorGit
|
||||||
|
local o = {
|
||||||
explorer = explorer,
|
explorer = explorer,
|
||||||
enabled = opts.git.enable,
|
enabled = opts.git.enable,
|
||||||
hl_pos = HL_POSITION[opts.renderer.highlight_git] or HL_POSITION.none,
|
hl_pos = HL_POSITION[opts.renderer.highlight_git] or HL_POSITION.none,
|
||||||
icon_placement = ICON_PLACEMENT[opts.renderer.icons.git_placement] or ICON_PLACEMENT.none,
|
icon_placement = ICON_PLACEMENT[opts.renderer.icons.git_placement] or ICON_PLACEMENT.none,
|
||||||
})
|
}
|
||||||
---@cast o DecoratorGit
|
o = self:new(o) --[[@as DecoratorGit]]
|
||||||
|
|
||||||
if not o.enabled then
|
if not o.enabled then
|
||||||
return o
|
return o
|
||||||
@ -147,7 +148,7 @@ function DecoratorGit:calculate_icons(node)
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local git_status = explorer_node.get_git_status(node)
|
local git_status = node:get_git_status()
|
||||||
if git_status == nil then
|
if git_status == nil then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
@ -208,7 +209,7 @@ function DecoratorGit:calculate_highlight(node)
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local git_status = explorer_node.get_git_status(node)
|
local git_status = node:get_git_status()
|
||||||
if not git_status then
|
if not git_status then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,23 +1,24 @@
|
|||||||
local HL_POSITION = require("nvim-tree.enum").HL_POSITION
|
local HL_POSITION = require("nvim-tree.enum").HL_POSITION
|
||||||
local ICON_PLACEMENT = require("nvim-tree.enum").ICON_PLACEMENT
|
local ICON_PLACEMENT = require("nvim-tree.enum").ICON_PLACEMENT
|
||||||
local explorer_node = require("nvim-tree.explorer.node")
|
|
||||||
local Decorator = require("nvim-tree.renderer.decorator")
|
local Decorator = require("nvim-tree.renderer.decorator")
|
||||||
|
|
||||||
---@class (exact) DecoratorHidden: Decorator
|
---@class (exact) DecoratorHidden: Decorator
|
||||||
---@field icon HighlightedString|nil
|
---@field icon HighlightedString?
|
||||||
local DecoratorHidden = Decorator:new()
|
local DecoratorHidden = Decorator:new()
|
||||||
|
|
||||||
|
---Static factory method
|
||||||
---@param opts table
|
---@param opts table
|
||||||
---@param explorer Explorer
|
---@param explorer Explorer
|
||||||
---@return DecoratorHidden
|
---@return DecoratorHidden
|
||||||
function DecoratorHidden:new(opts, explorer)
|
function DecoratorHidden:create(opts, explorer)
|
||||||
local o = Decorator.new(self, {
|
---@type DecoratorHidden
|
||||||
|
local o = {
|
||||||
explorer = explorer,
|
explorer = explorer,
|
||||||
enabled = true,
|
enabled = true,
|
||||||
hl_pos = HL_POSITION[opts.renderer.highlight_hidden] or HL_POSITION.none,
|
hl_pos = HL_POSITION[opts.renderer.highlight_hidden] or HL_POSITION.none,
|
||||||
icon_placement = ICON_PLACEMENT[opts.renderer.icons.hidden_placement] or ICON_PLACEMENT.none,
|
icon_placement = ICON_PLACEMENT[opts.renderer.icons.hidden_placement] or ICON_PLACEMENT.none,
|
||||||
})
|
}
|
||||||
---@cast o DecoratorHidden
|
o = self:new(o) --[[@as DecoratorHidden]]
|
||||||
|
|
||||||
if opts.renderer.icons.show.hidden then
|
if opts.renderer.icons.show.hidden then
|
||||||
o.icon = {
|
o.icon = {
|
||||||
@ -34,7 +35,7 @@ end
|
|||||||
---@param node Node
|
---@param node Node
|
||||||
---@return HighlightedString[]|nil icons
|
---@return HighlightedString[]|nil icons
|
||||||
function DecoratorHidden:calculate_icons(node)
|
function DecoratorHidden:calculate_icons(node)
|
||||||
if self.enabled and explorer_node.is_dotfile(node) then
|
if self.enabled and node:is_dotfile() then
|
||||||
return { self.icon }
|
return { self.icon }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -43,7 +44,7 @@ end
|
|||||||
---@param node Node
|
---@param node Node
|
||||||
---@return string|nil group
|
---@return string|nil group
|
||||||
function DecoratorHidden:calculate_highlight(node)
|
function DecoratorHidden:calculate_highlight(node)
|
||||||
if not self.enabled or self.hl_pos == HL_POSITION.none or (not explorer_node.is_dotfile(node)) then
|
if not self.enabled or self.hl_pos == HL_POSITION.none or not node:is_dotfile() then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
local HL_POSITION = require("nvim-tree.enum").HL_POSITION
|
local HL_POSITION = require("nvim-tree.enum").HL_POSITION
|
||||||
local ICON_PLACEMENT = require("nvim-tree.enum").ICON_PLACEMENT
|
local ICON_PLACEMENT = require("nvim-tree.enum").ICON_PLACEMENT
|
||||||
|
|
||||||
|
---Abstract Decorator
|
||||||
|
---Uses the factory pattern to instantiate child instances.
|
||||||
---@class (exact) Decorator
|
---@class (exact) Decorator
|
||||||
---@field private __index? table
|
---@field private __index? table
|
||||||
---@field protected explorer Explorer
|
---@field protected explorer Explorer
|
||||||
|
|||||||
@ -9,17 +9,19 @@ local Decorator = require("nvim-tree.renderer.decorator")
|
|||||||
---@field icon HighlightedString|nil
|
---@field icon HighlightedString|nil
|
||||||
local DecoratorModified = Decorator:new()
|
local DecoratorModified = Decorator:new()
|
||||||
|
|
||||||
|
---Static factory method
|
||||||
---@param opts table
|
---@param opts table
|
||||||
---@param explorer Explorer
|
---@param explorer Explorer
|
||||||
---@return DecoratorModified
|
---@return DecoratorModified
|
||||||
function DecoratorModified:new(opts, explorer)
|
function DecoratorModified:create(opts, explorer)
|
||||||
local o = Decorator.new(self, {
|
---@type DecoratorModified
|
||||||
|
local o = {
|
||||||
explorer = explorer,
|
explorer = explorer,
|
||||||
enabled = opts.modified.enable,
|
enabled = opts.modified.enable,
|
||||||
hl_pos = HL_POSITION[opts.renderer.highlight_modified] or HL_POSITION.none,
|
hl_pos = HL_POSITION[opts.renderer.highlight_modified] or HL_POSITION.none,
|
||||||
icon_placement = ICON_PLACEMENT[opts.renderer.icons.modified_placement] or ICON_PLACEMENT.none,
|
icon_placement = ICON_PLACEMENT[opts.renderer.icons.modified_placement] or ICON_PLACEMENT.none,
|
||||||
})
|
}
|
||||||
---@cast o DecoratorModified
|
o = self:new(o) --[[@as DecoratorModified]]
|
||||||
|
|
||||||
if not o.enabled then
|
if not o.enabled then
|
||||||
return o
|
return o
|
||||||
|
|||||||
@ -6,21 +6,22 @@ local ICON_PLACEMENT = require("nvim-tree.enum").ICON_PLACEMENT
|
|||||||
local Decorator = require("nvim-tree.renderer.decorator")
|
local Decorator = require("nvim-tree.renderer.decorator")
|
||||||
|
|
||||||
---@class (exact) DecoratorOpened: Decorator
|
---@class (exact) DecoratorOpened: Decorator
|
||||||
---@field enabled boolean
|
|
||||||
---@field icon HighlightedString|nil
|
---@field icon HighlightedString|nil
|
||||||
local DecoratorOpened = Decorator:new()
|
local DecoratorOpened = Decorator:new()
|
||||||
|
|
||||||
|
---Static factory method
|
||||||
---@param opts table
|
---@param opts table
|
||||||
---@param explorer Explorer
|
---@param explorer Explorer
|
||||||
---@return DecoratorOpened
|
---@return DecoratorOpened
|
||||||
function DecoratorOpened:new(opts, explorer)
|
function DecoratorOpened:create(opts, explorer)
|
||||||
local o = Decorator.new(self, {
|
---@type DecoratorOpened
|
||||||
|
local o = {
|
||||||
explorer = explorer,
|
explorer = explorer,
|
||||||
enabled = true,
|
enabled = true,
|
||||||
hl_pos = HL_POSITION[opts.renderer.highlight_opened_files] or HL_POSITION.none,
|
hl_pos = HL_POSITION[opts.renderer.highlight_opened_files] or HL_POSITION.none,
|
||||||
icon_placement = ICON_PLACEMENT.none,
|
icon_placement = ICON_PLACEMENT.none,
|
||||||
})
|
}
|
||||||
---@cast o DecoratorOpened
|
o = self:new(o) --[[@as DecoratorOpened]]
|
||||||
|
|
||||||
return o
|
return o
|
||||||
end
|
end
|
||||||
|
|||||||
@ -112,8 +112,7 @@ function M.find_node(nodes, fn)
|
|||||||
end)
|
end)
|
||||||
:iterate()
|
:iterate()
|
||||||
i = require("nvim-tree.view").is_root_folder_visible() and i or i - 1
|
i = require("nvim-tree.view").is_root_folder_visible() and i or i - 1
|
||||||
local explorer = require("nvim-tree.core").get_explorer()
|
if node and node.explorer.live_filter.filter then
|
||||||
if explorer and explorer.live_filter.filter then
|
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end
|
end
|
||||||
return node, i
|
return node, i
|
||||||
@ -121,7 +120,7 @@ end
|
|||||||
|
|
||||||
-- Find the line number of a node.
|
-- Find the line number of a node.
|
||||||
-- Return -1 is node is nil or not found.
|
-- Return -1 is node is nil or not found.
|
||||||
---@param node Node|nil
|
---@param node Node?
|
||||||
---@return integer
|
---@return integer
|
||||||
function M.find_node_line(node)
|
function M.find_node_line(node)
|
||||||
if not node then
|
if not node then
|
||||||
@ -174,16 +173,6 @@ function M.get_node_from_path(path)
|
|||||||
:iterate()
|
:iterate()
|
||||||
end
|
end
|
||||||
|
|
||||||
---Get the highest parent of grouped nodes
|
|
||||||
---@param node Node
|
|
||||||
---@return Node node or parent
|
|
||||||
function M.get_parent_of_group(node)
|
|
||||||
while node and node.parent and node.parent.group_next do
|
|
||||||
node = node.parent
|
|
||||||
end
|
|
||||||
return node
|
|
||||||
end
|
|
||||||
|
|
||||||
M.default_format_hidden_count = function(hidden_count, simple)
|
M.default_format_hidden_count = function(hidden_count, simple)
|
||||||
local parts = {}
|
local parts = {}
|
||||||
local total_count = 0
|
local total_count = 0
|
||||||
@ -473,7 +462,7 @@ end
|
|||||||
---Focus node passed as parameter if visible, otherwise focus first visible parent.
|
---Focus node passed as parameter if visible, otherwise focus first visible parent.
|
||||||
---If none of the parents is visible focus root.
|
---If none of the parents is visible focus root.
|
||||||
---If node is nil do nothing.
|
---If node is nil do nothing.
|
||||||
---@param node Node|nil node to focus
|
---@param node Node? node to focus
|
||||||
function M.focus_node_or_parent(node)
|
function M.focus_node_or_parent(node)
|
||||||
local explorer = require("nvim-tree.core").get_explorer()
|
local explorer = require("nvim-tree.core").get_explorer()
|
||||||
|
|
||||||
@ -549,14 +538,6 @@ function M.array_remove_nils(array)
|
|||||||
end, array)
|
end, array)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param f fun(node: Node|nil)
|
|
||||||
---@return function
|
|
||||||
function M.inject_node(f)
|
|
||||||
return function()
|
|
||||||
f(require("nvim-tree.lib").get_node_at_cursor())
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Is the buffer named NvimTree_[0-9]+ a tree? filetype is "NvimTree" or not readable file.
|
--- Is the buffer named NvimTree_[0-9]+ a tree? filetype is "NvimTree" or not readable file.
|
||||||
--- This is cheap, as the readable test should only ever be needed when resuming a vim session.
|
--- This is cheap, as the readable test should only ever be needed when resuming a vim session.
|
||||||
---@param bufnr number|nil may be 0 or nil for current
|
---@param bufnr number|nil may be 0 or nil for current
|
||||||
@ -578,4 +559,16 @@ function M.is_nvim_tree_buf(bufnr)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- path is an executable file or directory
|
||||||
|
---@param absolute_path string
|
||||||
|
---@return boolean
|
||||||
|
function M.is_executable(absolute_path)
|
||||||
|
if M.is_windows or M.is_wsl then
|
||||||
|
--- executable detection on windows is buggy and not performant hence it is disabled
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return vim.loop.fs_access(absolute_path, "X") or false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user