feat: Option for grouping empty directories (#247)
This commit is contained in:
parent
e0b9882a8a
commit
709d6b968b
@ -34,6 +34,7 @@ let g:nvim_tree_width_allow_resize = 1 "0 by default, will not resize the tree
|
||||
let g:nvim_tree_disable_netrw = 0 "1 by default, disables netrw
|
||||
let g:nvim_tree_hijack_netrw = 0 "1 by default, prevents netrw from automatically opening when opening directories (but lets you keep its other utilities)
|
||||
let g:nvim_tree_add_trailing = 1 "0 by default, append a trailing slash to folder names
|
||||
let g:nvim_tree_group_empty = 1 " 0 by default, compact folders that only contain a single folder into one node in the file tree
|
||||
let g:nvim_tree_show_icons = {
|
||||
\ 'git': 1,
|
||||
\ 'folders': 0,
|
||||
|
||||
@ -203,6 +203,11 @@ functionnalities.
|
||||
Can be 0 or 1. When 1, appends a trailing slash to folder names.
|
||||
0 by default.
|
||||
|
||||
|g:nvim_tree_group_empty| *g:nvim_tree_group_empty*
|
||||
|
||||
Can be 0 or 1. When 1, folders that contain only one folder are grouped
|
||||
together. 0 by default.
|
||||
|
||||
==============================================================================
|
||||
INFORMATIONS *nvim-tree-info*
|
||||
|
||||
|
||||
@ -95,7 +95,7 @@ function M.on_keypress(mode)
|
||||
if node.name == ".." then
|
||||
return lib.change_dir("..")
|
||||
elseif mode == "cd" and node.entries ~= nil then
|
||||
return lib.change_dir(node.absolute_path)
|
||||
return lib.change_dir(lib.get_last_group_node(node).absolute_path)
|
||||
elseif mode == "cd" then
|
||||
return
|
||||
end
|
||||
|
||||
@ -3,6 +3,7 @@ local luv = vim.loop
|
||||
local open_mode = luv.constants.O_CREAT + luv.constants.O_WRONLY + luv.constants.O_TRUNC
|
||||
|
||||
local utils = require'nvim-tree.utils'
|
||||
local lib = require'nvim-tree.lib'
|
||||
local M = {}
|
||||
local clipboard = {
|
||||
move = {},
|
||||
@ -41,6 +42,7 @@ local function get_num_entries(iter)
|
||||
end
|
||||
|
||||
function M.create(node)
|
||||
node = lib.get_last_group_node(node)
|
||||
if node.name == '..' then return end
|
||||
|
||||
local add_into
|
||||
@ -168,6 +170,7 @@ local function do_single_paste(source, dest, action_type, action_fn)
|
||||
end
|
||||
|
||||
local function do_paste(node, action_type, action_fn)
|
||||
node = lib.get_last_group_node(node)
|
||||
if node.name == '..' then return end
|
||||
local clip = clipboard[action_type]
|
||||
if #clip == 0 then return end
|
||||
@ -242,6 +245,7 @@ end
|
||||
|
||||
function M.rename(with_sub)
|
||||
return function(node)
|
||||
node = lib.get_last_group_node(node)
|
||||
if node.name == '..' then return end
|
||||
|
||||
local namelen = node.name:len()
|
||||
|
||||
@ -46,7 +46,7 @@ M.Tree = {
|
||||
|
||||
function M.init(with_open, with_render)
|
||||
M.Tree.cwd = luv.cwd()
|
||||
populate(M.Tree.entries, M.Tree.cwd, M.Tree)
|
||||
populate(M.Tree.entries, M.Tree.cwd)
|
||||
|
||||
local stat = luv.fs_stat(M.Tree.cwd)
|
||||
M.Tree.last_modified = stat.mtime.sec
|
||||
@ -91,13 +91,22 @@ function M.get_node_at_cursor()
|
||||
return get_node_at_line(line)(M.Tree.entries)
|
||||
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.unroll_dir(node)
|
||||
node.open = not node.open
|
||||
if node.has_children then node.has_children = false end
|
||||
if #node.entries > 0 then
|
||||
renderer.draw(M.Tree, true)
|
||||
else
|
||||
populate(node.entries, node.link_to or node.absolute_path)
|
||||
populate(node.entries, node.link_to or node.absolute_path, node)
|
||||
renderer.draw(M.Tree, true)
|
||||
end
|
||||
end
|
||||
@ -113,7 +122,7 @@ end
|
||||
|
||||
-- TODO update only entries where directory has changed
|
||||
local function refresh_nodes(node)
|
||||
refresh_entries(node.entries, node.absolute_path or node.cwd)
|
||||
refresh_entries(node.entries, node.absolute_path or node.cwd, node)
|
||||
for _, entry in ipairs(node.entries) do
|
||||
if entry.entries and entry.open then
|
||||
refresh_nodes(entry)
|
||||
@ -159,7 +168,7 @@ function M.set_index_and_redraw(fname)
|
||||
if fname:match(entry.match_path..'/') ~= nil then
|
||||
if #entry.entries == 0 then
|
||||
reload = true
|
||||
populate(entry.entries, entry.absolute_path)
|
||||
populate(entry.entries, entry.absolute_path, entry)
|
||||
end
|
||||
if entry.open == false then
|
||||
reload = true
|
||||
|
||||
@ -34,6 +34,7 @@ local function dir_new(cwd, name)
|
||||
match_name = path_to_matching_str(name),
|
||||
match_path = path_to_matching_str(absolute_path),
|
||||
open = false,
|
||||
group_next = nil, -- If node is grouped, this points to the next child dir/link node
|
||||
has_children = has_children,
|
||||
entries = {}
|
||||
}
|
||||
@ -78,6 +79,25 @@ local function link_new(cwd, name)
|
||||
}
|
||||
end
|
||||
|
||||
-- Returns true if there is either exactly 1 dir, or exactly 1 symlink dir. Otherwise, false.
|
||||
-- @param cwd Absolute path to the parent directory
|
||||
-- @param dirs List of dir names
|
||||
-- @param files List of file names
|
||||
-- @param links List of symlink names
|
||||
local function should_group(cwd, dirs, files, links)
|
||||
if #dirs == 1 and #files == 0 and #links == 0 then
|
||||
return true
|
||||
end
|
||||
|
||||
if #dirs == 0 and #files == 0 and #links == 1 then
|
||||
local absolute_path = utils.path_join({ cwd, links[1] })
|
||||
local link_to = luv.fs_realpath(absolute_path)
|
||||
return (link_to ~= nil) and luv.fs_stat(link_to).type == 'directory'
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function gen_ignore_check()
|
||||
local ignore_list = {}
|
||||
if vim.g.nvim_tree_ignore and #vim.g.nvim_tree_ignore > 0 then
|
||||
@ -100,7 +120,7 @@ end
|
||||
|
||||
local should_ignore = gen_ignore_check()
|
||||
|
||||
function M.refresh_entries(entries, cwd)
|
||||
function M.refresh_entries(entries, cwd, parent_node)
|
||||
local handle = luv.fs_scandir(cwd)
|
||||
if type(handle) == 'string' then
|
||||
api.nvim_err_writeln(handle)
|
||||
@ -120,10 +140,12 @@ function M.refresh_entries(entries, cwd)
|
||||
local links = {}
|
||||
local files = {}
|
||||
local new_entries = {}
|
||||
local num_new_entries = 0
|
||||
|
||||
while true do
|
||||
local name, t = luv.fs_scandir_next(handle)
|
||||
if not name then break end
|
||||
num_new_entries = num_new_entries + 1
|
||||
|
||||
if not should_ignore(name) then
|
||||
if t == 'directory' then
|
||||
@ -139,6 +161,21 @@ function M.refresh_entries(entries, cwd)
|
||||
end
|
||||
end
|
||||
|
||||
-- Handle grouped dirs
|
||||
local next_node = parent_node.group_next
|
||||
if next_node then
|
||||
next_node.open = parent_node.open
|
||||
if num_new_entries ~= 1 or not new_entries[next_node.name] then
|
||||
-- dir is no longer only containing a group dir, or group dir has been removed
|
||||
-- either way: sever the group link on current dir
|
||||
parent_node.group_next = nil
|
||||
named_entries[next_node.name] = next_node
|
||||
else
|
||||
M.refresh_entries(entries, next_node.absolute_path, next_node)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local idx = 1
|
||||
for _, name in ipairs(cached_entries) do
|
||||
if not new_entries[name] then
|
||||
@ -173,12 +210,18 @@ function M.refresh_entries(entries, cwd)
|
||||
change_prev = false
|
||||
end
|
||||
end
|
||||
if change_prev then prev = name end
|
||||
if change_prev and not (next_node and next_node.name == name) then
|
||||
prev = name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M.populate(entries, cwd)
|
||||
if next_node then
|
||||
table.insert(entries, 1, next_node)
|
||||
end
|
||||
end
|
||||
|
||||
function M.populate(entries, cwd, parent_node)
|
||||
local handle = luv.fs_scandir(cwd)
|
||||
if type(handle) == 'string' then
|
||||
api.nvim_err_writeln(handle)
|
||||
@ -206,6 +249,20 @@ function M.populate(entries, cwd)
|
||||
|
||||
-- Create Nodes --
|
||||
|
||||
-- Group empty dirs
|
||||
if parent_node and vim.g.nvim_tree_group_empty == 1 then
|
||||
if should_group(cwd, dirs, files, links) then
|
||||
local child_node
|
||||
if dirs[1] then child_node = dir_new(cwd, dirs[1]) end
|
||||
if links[1] then child_node = link_new(cwd, links[1]) end
|
||||
if luv.fs_access(child_node.absolute_path, 'R') then
|
||||
parent_node.group_next = child_node
|
||||
M.populate(entries, child_node.absolute_path, child_node)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, dirname in ipairs(dirs) do
|
||||
local dir = dir_new(cwd, dirname)
|
||||
if luv.fs_access(dir.absolute_path, 'R') then
|
||||
|
||||
@ -252,17 +252,23 @@ local function update_draw_data(tree, depth, markers)
|
||||
local git_icon = get_git_icons(node, index, offset, #icon+1) or ""
|
||||
-- INFO: this is mandatory in order to keep gui attributes (bold/italics)
|
||||
local folder_hl = "NvimTreeFolderName"
|
||||
local name = node.name
|
||||
local next = node.group_next
|
||||
while next do
|
||||
name = name .. "/" .. next.name
|
||||
next = next.group_next
|
||||
end
|
||||
if not has_children then folder_hl = "NvimTreeEmptyFolderName" end
|
||||
set_folder_hl(index, offset, #icon, #node.name+#git_icon, folder_hl)
|
||||
set_folder_hl(index, offset, #icon, #name+#git_icon, folder_hl)
|
||||
if git_hl then
|
||||
set_folder_hl(index, offset, #icon, #node.name+#git_icon, git_hl)
|
||||
set_folder_hl(index, offset, #icon, #name+#git_icon, git_hl)
|
||||
end
|
||||
index = index + 1
|
||||
if node.open then
|
||||
table.insert(lines, padding..icon..git_icon..node.name..(vim.g.nvim_tree_add_trailing == 1 and '/' or ''))
|
||||
table.insert(lines, padding..icon..git_icon..name..(vim.g.nvim_tree_add_trailing == 1 and '/' or ''))
|
||||
update_draw_data(node, depth + 2, markers)
|
||||
else
|
||||
table.insert(lines, padding..icon..git_icon..node.name..(vim.g.nvim_tree_add_trailing == 1 and '/' or ''))
|
||||
table.insert(lines, padding..icon..git_icon..name..(vim.g.nvim_tree_add_trailing == 1 and '/' or ''))
|
||||
end
|
||||
elseif node.link_to then
|
||||
local icon = get_symlink_icon()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user