feat: Option for grouping empty directories (#247)

This commit is contained in:
Sindre T. Strøm 2021-03-26 19:24:03 +01:00 committed by GitHub
parent e0b9882a8a
commit 709d6b968b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 94 additions and 12 deletions

View File

@ -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,

View File

@ -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*

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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
if next_node then
table.insert(entries, 1, next_node)
end
end
function M.populate(entries, cwd)
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

View File

@ -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()