407 lines
9.4 KiB
Lua
407 lines
9.4 KiB
Lua
local api = vim.api
|
|
local luv = vim.loop
|
|
|
|
local renderer = require'nvim-tree.renderer'
|
|
local diagnostics = require'nvim-tree.diagnostics'
|
|
local pops = require'nvim-tree.populate'
|
|
local utils = require'nvim-tree.utils'
|
|
local view = require'nvim-tree.view'
|
|
local events = require'nvim-tree.events'
|
|
local git = require'nvim-tree.git'
|
|
local populate = pops.populate
|
|
local refresh_entries = pops.refresh_entries
|
|
|
|
local first_init_done = false
|
|
|
|
local M = {}
|
|
|
|
M.Tree = {
|
|
entries = {},
|
|
cwd = nil,
|
|
target_winid = nil,
|
|
}
|
|
|
|
local function load_children(cwd, children, parent)
|
|
git.load_project_status(cwd, function(git_statuses)
|
|
populate(children, cwd, parent, git_statuses)
|
|
M.redraw()
|
|
end)
|
|
end
|
|
|
|
function M.init(with_open, foldername)
|
|
M.Tree.entries = {}
|
|
M.Tree.cwd = foldername or luv.cwd()
|
|
|
|
if with_open then
|
|
M.open()
|
|
end
|
|
|
|
load_children(M.Tree.cwd, M.Tree.entries)
|
|
|
|
if not first_init_done then
|
|
events._dispatch_ready()
|
|
first_init_done = true
|
|
end
|
|
end
|
|
|
|
function M.redraw()
|
|
renderer.draw(M.Tree, true)
|
|
end
|
|
|
|
local function get_node_at_line(line)
|
|
local index = view.View.hide_root_folder and 1 or 2
|
|
local function iter(entries)
|
|
for _, node in ipairs(entries) do
|
|
if index == line then
|
|
return node
|
|
end
|
|
index = index + 1
|
|
if node.open == true then
|
|
local child = iter(node.entries)
|
|
if child ~= nil then return child end
|
|
end
|
|
end
|
|
end
|
|
return iter
|
|
end
|
|
|
|
local function get_line_from_node(node, find_parent)
|
|
local node_path = node.absolute_path
|
|
|
|
if find_parent then
|
|
node_path = node.absolute_path:match("(.*)"..utils.path_separator)
|
|
end
|
|
|
|
local line = 2
|
|
local function iter(entries, recursive)
|
|
for _, entry in ipairs(entries) do
|
|
local n = M.get_last_group_node(entry)
|
|
if node_path == n.absolute_path then
|
|
return line, entry
|
|
end
|
|
|
|
line = line + 1
|
|
if entry.open == true and recursive then
|
|
local _, child = iter(entry.entries, recursive)
|
|
if child ~= nil then return line, child end
|
|
end
|
|
end
|
|
end
|
|
return iter
|
|
end
|
|
|
|
function M.get_node_at_cursor()
|
|
local winnr = view.get_winnr()
|
|
local hide_root_folder = view.View.hide_root_folder
|
|
if not winnr then
|
|
return
|
|
end
|
|
local cursor = api.nvim_win_get_cursor(view.get_winnr())
|
|
local line = cursor[1]
|
|
if view.is_help_ui() then
|
|
local help_lines = require'nvim-tree.renderer.help'.compute_lines()
|
|
local help_text = get_node_at_line(line+1)(help_lines)
|
|
return {name = help_text}
|
|
else
|
|
if line == 1 and M.Tree.cwd ~= "/" and not hide_root_folder then
|
|
return { name = ".." }
|
|
end
|
|
|
|
if M.Tree.cwd == "/" then
|
|
line = line + 1
|
|
end
|
|
return get_node_at_line(line)(M.Tree.entries)
|
|
end
|
|
end
|
|
|
|
-- If node is grouped, return the last node in the group. Otherwise, return the given node.
|
|
function M.get_last_group_node(node)
|
|
local next = node
|
|
while next.group_next do
|
|
next = next.group_next
|
|
end
|
|
return next
|
|
end
|
|
|
|
function M.expand_or_collapse(node)
|
|
node.open = not node.open
|
|
if node.has_children then node.has_children = false end
|
|
if #node.entries == 0 then
|
|
load_children(
|
|
node.link_to or node.absolute_path,
|
|
node.entries,
|
|
node
|
|
)
|
|
else
|
|
M.redraw()
|
|
end
|
|
|
|
diagnostics.update()
|
|
end
|
|
|
|
local function refresh_nodes(node, projects)
|
|
local project_root = git.get_project_root(node.absolute_path or node.cwd)
|
|
refresh_entries(node.entries, node.absolute_path or node.cwd, node, projects[project_root] or {})
|
|
for _, entry in ipairs(node.entries) do
|
|
if entry.entries and entry.open then
|
|
refresh_nodes(entry, projects)
|
|
end
|
|
end
|
|
end
|
|
|
|
local event_running = false
|
|
function M.refresh_tree()
|
|
if event_running or not M.Tree.cwd or vim.v.exiting ~= vim.NIL then
|
|
return
|
|
end
|
|
event_running = true
|
|
|
|
git.reload(function(projects)
|
|
refresh_nodes(M.Tree, projects)
|
|
if view.win_open() then
|
|
M.redraw()
|
|
end
|
|
diagnostics.update()
|
|
event_running = false
|
|
end)
|
|
end
|
|
|
|
local function reload_node_status(parent_node, projects)
|
|
local project_root = git.get_project_root(parent_node.absolute_path or parent_node.cwd)
|
|
local status = projects[project_root] or {}
|
|
for _, node in ipairs(parent_node.entries) do
|
|
if node.entries then
|
|
node.git_status = status.dirs and status.dirs[node.absolute_path]
|
|
else
|
|
node.git_status = status.files and status.files[node.absolute_path]
|
|
end
|
|
if node.entries and #node.entries > 0 then
|
|
reload_node_status(node, projects)
|
|
end
|
|
end
|
|
end
|
|
|
|
function M.reload_git()
|
|
if not git.config.enable or event_running then
|
|
return
|
|
end
|
|
event_running = true
|
|
|
|
git.reload(function(projects)
|
|
reload_node_status(M.Tree, projects)
|
|
M.redraw()
|
|
event_running = false
|
|
end)
|
|
end
|
|
|
|
function M.set_index_and_redraw(fname)
|
|
local i
|
|
local hide_root_folder = view.View.hide_root_folder
|
|
if M.Tree.cwd == '/' or hide_root_folder then
|
|
i = 0
|
|
else
|
|
i = 1
|
|
end
|
|
|
|
local tree_altered = false
|
|
|
|
local function iterate_nodes(nodes)
|
|
for _, node in ipairs(nodes) do
|
|
i = i + 1
|
|
if node.absolute_path == fname then
|
|
return i
|
|
end
|
|
|
|
local path_matches = utils.str_find(fname, node.absolute_path..utils.path_separator)
|
|
if path_matches then
|
|
if #node.entries == 0 then
|
|
node.open = true
|
|
populate(node.entries, node.absolute_path, node, {})
|
|
git.load_project_status(node.absolute_path, function(status)
|
|
if status.dirs or status.files then
|
|
reload_node_status(node, git.projects)
|
|
end
|
|
M.redraw()
|
|
end)
|
|
end
|
|
if node.open == false then
|
|
node.open = true
|
|
tree_altered = true
|
|
end
|
|
if iterate_nodes(node.entries) ~= nil then
|
|
return i
|
|
end
|
|
elseif node.open == true then
|
|
iterate_nodes(node.entries)
|
|
end
|
|
end
|
|
end
|
|
|
|
local index = iterate_nodes(M.Tree.entries)
|
|
if tree_altered then
|
|
M.redraw()
|
|
end
|
|
if index and view.win_open() then
|
|
view.set_cursor({index, 0})
|
|
end
|
|
end
|
|
|
|
function M.collapse_all()
|
|
local function iter(nodes)
|
|
for _, node in pairs(nodes) do
|
|
if node.open then
|
|
node.open = false
|
|
end
|
|
if node.entries then
|
|
iter(node.entries)
|
|
end
|
|
end
|
|
end
|
|
|
|
iter(M.Tree.entries)
|
|
M.redraw()
|
|
end
|
|
|
|
local current_tab = api.nvim_get_current_tabpage()
|
|
|
|
function M.change_dir(name)
|
|
local foldername = name == '..' and vim.fn.fnamemodify(M.Tree.cwd, ':h') or name
|
|
local no_cwd_change = vim.fn.expand(foldername) == M.Tree.cwd
|
|
local new_tab = api.nvim_get_current_tabpage()
|
|
local is_window = vim.v.event.scope == "window" and new_tab == current_tab
|
|
if no_cwd_change or is_window then
|
|
return
|
|
end
|
|
current_tab = new_tab
|
|
|
|
if vim.g.nvim_tree_change_dir_global == 1 then
|
|
vim.cmd('cd '..vim.fn.fnameescape(foldername))
|
|
else
|
|
vim.cmd('lcd '..vim.fn.fnameescape(foldername))
|
|
end
|
|
M.init(false, foldername)
|
|
end
|
|
|
|
function M.set_target_win()
|
|
local id = api.nvim_get_current_win()
|
|
local tree_id = view.get_winnr()
|
|
if tree_id and id == tree_id then
|
|
M.Tree.target_winid = 0
|
|
return
|
|
end
|
|
|
|
M.Tree.target_winid = id
|
|
end
|
|
|
|
function M.open()
|
|
M.set_target_win()
|
|
|
|
local cwd = vim.fn.getcwd()
|
|
local should_redraw = view.open()
|
|
|
|
local respect_buf_cwd = vim.g.nvim_tree_respect_buf_cwd or 0
|
|
if respect_buf_cwd == 1 and cwd ~= M.Tree.cwd then
|
|
M.change_dir(cwd)
|
|
end
|
|
if should_redraw then
|
|
M.redraw()
|
|
end
|
|
end
|
|
|
|
function M.sibling(node, direction)
|
|
if node.name == '..' or not direction then return end
|
|
|
|
local iter = get_line_from_node(node, true)
|
|
local node_path = node.absolute_path
|
|
|
|
local line = 0
|
|
local parent, _
|
|
|
|
-- Check if current node is already at root entries
|
|
for index, entry in ipairs(M.Tree.entries) do
|
|
if node_path == entry.absolute_path then
|
|
line = index
|
|
end
|
|
end
|
|
|
|
if line > 0 then
|
|
parent = M.Tree
|
|
else
|
|
_, parent = iter(M.Tree.entries, true)
|
|
if parent ~= nil and #parent.entries > 1 then
|
|
line, _ = get_line_from_node(node)(parent.entries)
|
|
end
|
|
|
|
-- Ignore parent line count
|
|
line = line - 1
|
|
end
|
|
|
|
local index = line + direction
|
|
if index < 1 then
|
|
index = 1
|
|
elseif index > #parent.entries then
|
|
index = #parent.entries
|
|
end
|
|
local target_node = parent.entries[index]
|
|
|
|
line, _ = get_line_from_node(target_node)(M.Tree.entries, true)
|
|
view.set_cursor({line, 0})
|
|
end
|
|
|
|
function M.close_node(node)
|
|
M.parent_node(node, true)
|
|
end
|
|
|
|
function M.parent_node(node, should_close)
|
|
if node.name == '..' then return end
|
|
|
|
should_close = should_close or false
|
|
local altered_tree = false
|
|
|
|
local iter = get_line_from_node(node, true)
|
|
if node.open == true and should_close then
|
|
node.open = false
|
|
altered_tree = true
|
|
else
|
|
local line, parent = iter(M.Tree.entries, true)
|
|
if parent == nil then
|
|
line = 1
|
|
elseif should_close then
|
|
parent.open = false
|
|
altered_tree = true
|
|
end
|
|
view.set_cursor({line, 0})
|
|
end
|
|
|
|
if altered_tree then
|
|
M.redraw()
|
|
end
|
|
end
|
|
|
|
function M.toggle_ignored()
|
|
pops.config.filter_ignored = not pops.config.filter_ignored
|
|
return M.refresh_tree()
|
|
end
|
|
|
|
function M.toggle_dotfiles()
|
|
pops.config.filter_dotfiles = not pops.config.filter_dotfiles
|
|
return M.refresh_tree()
|
|
end
|
|
|
|
function M.toggle_help()
|
|
view.toggle_help()
|
|
return M.redraw()
|
|
end
|
|
|
|
function M.dir_up(node)
|
|
if not node or node.name == ".." then
|
|
return M.change_dir('..')
|
|
else
|
|
local newdir = vim.fn.fnamemodify(M.Tree.cwd, ':h')
|
|
M.change_dir(newdir)
|
|
return M.set_index_and_redraw(node.absolute_path)
|
|
end
|
|
end
|
|
|
|
return M
|