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_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_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_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 = {
|
let g:nvim_tree_show_icons = {
|
||||||
\ 'git': 1,
|
\ 'git': 1,
|
||||||
\ 'folders': 0,
|
\ 'folders': 0,
|
||||||
|
|||||||
@ -203,6 +203,11 @@ functionnalities.
|
|||||||
Can be 0 or 1. When 1, appends a trailing slash to folder names.
|
Can be 0 or 1. When 1, appends a trailing slash to folder names.
|
||||||
0 by default.
|
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*
|
INFORMATIONS *nvim-tree-info*
|
||||||
|
|
||||||
|
|||||||
@ -95,7 +95,7 @@ function M.on_keypress(mode)
|
|||||||
if node.name == ".." then
|
if node.name == ".." then
|
||||||
return lib.change_dir("..")
|
return lib.change_dir("..")
|
||||||
elseif mode == "cd" and node.entries ~= nil then
|
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
|
elseif mode == "cd" then
|
||||||
return
|
return
|
||||||
end
|
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 open_mode = luv.constants.O_CREAT + luv.constants.O_WRONLY + luv.constants.O_TRUNC
|
||||||
|
|
||||||
local utils = require'nvim-tree.utils'
|
local utils = require'nvim-tree.utils'
|
||||||
|
local lib = require'nvim-tree.lib'
|
||||||
local M = {}
|
local M = {}
|
||||||
local clipboard = {
|
local clipboard = {
|
||||||
move = {},
|
move = {},
|
||||||
@ -41,6 +42,7 @@ local function get_num_entries(iter)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function M.create(node)
|
function M.create(node)
|
||||||
|
node = lib.get_last_group_node(node)
|
||||||
if node.name == '..' then return end
|
if node.name == '..' then return end
|
||||||
|
|
||||||
local add_into
|
local add_into
|
||||||
@ -168,6 +170,7 @@ local function do_single_paste(source, dest, action_type, action_fn)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function do_paste(node, action_type, action_fn)
|
local function do_paste(node, action_type, action_fn)
|
||||||
|
node = lib.get_last_group_node(node)
|
||||||
if node.name == '..' then return end
|
if node.name == '..' then return end
|
||||||
local clip = clipboard[action_type]
|
local clip = clipboard[action_type]
|
||||||
if #clip == 0 then return end
|
if #clip == 0 then return end
|
||||||
@ -242,6 +245,7 @@ end
|
|||||||
|
|
||||||
function M.rename(with_sub)
|
function M.rename(with_sub)
|
||||||
return function(node)
|
return function(node)
|
||||||
|
node = lib.get_last_group_node(node)
|
||||||
if node.name == '..' then return end
|
if node.name == '..' then return end
|
||||||
|
|
||||||
local namelen = node.name:len()
|
local namelen = node.name:len()
|
||||||
|
|||||||
@ -46,7 +46,7 @@ M.Tree = {
|
|||||||
|
|
||||||
function M.init(with_open, with_render)
|
function M.init(with_open, with_render)
|
||||||
M.Tree.cwd = luv.cwd()
|
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)
|
local stat = luv.fs_stat(M.Tree.cwd)
|
||||||
M.Tree.last_modified = stat.mtime.sec
|
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)
|
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.unroll_dir(node)
|
function M.unroll_dir(node)
|
||||||
node.open = not node.open
|
node.open = not node.open
|
||||||
if node.has_children then node.has_children = false end
|
if node.has_children then node.has_children = false end
|
||||||
if #node.entries > 0 then
|
if #node.entries > 0 then
|
||||||
renderer.draw(M.Tree, true)
|
renderer.draw(M.Tree, true)
|
||||||
else
|
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)
|
renderer.draw(M.Tree, true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -113,7 +122,7 @@ end
|
|||||||
|
|
||||||
-- TODO update only entries where directory has changed
|
-- TODO update only entries where directory has changed
|
||||||
local function refresh_nodes(node)
|
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
|
for _, entry in ipairs(node.entries) do
|
||||||
if entry.entries and entry.open then
|
if entry.entries and entry.open then
|
||||||
refresh_nodes(entry)
|
refresh_nodes(entry)
|
||||||
@ -159,7 +168,7 @@ function M.set_index_and_redraw(fname)
|
|||||||
if fname:match(entry.match_path..'/') ~= nil then
|
if fname:match(entry.match_path..'/') ~= nil then
|
||||||
if #entry.entries == 0 then
|
if #entry.entries == 0 then
|
||||||
reload = true
|
reload = true
|
||||||
populate(entry.entries, entry.absolute_path)
|
populate(entry.entries, entry.absolute_path, entry)
|
||||||
end
|
end
|
||||||
if entry.open == false then
|
if entry.open == false then
|
||||||
reload = true
|
reload = true
|
||||||
|
|||||||
@ -34,6 +34,7 @@ local function dir_new(cwd, name)
|
|||||||
match_name = path_to_matching_str(name),
|
match_name = path_to_matching_str(name),
|
||||||
match_path = path_to_matching_str(absolute_path),
|
match_path = path_to_matching_str(absolute_path),
|
||||||
open = false,
|
open = false,
|
||||||
|
group_next = nil, -- If node is grouped, this points to the next child dir/link node
|
||||||
has_children = has_children,
|
has_children = has_children,
|
||||||
entries = {}
|
entries = {}
|
||||||
}
|
}
|
||||||
@ -78,6 +79,25 @@ local function link_new(cwd, name)
|
|||||||
}
|
}
|
||||||
end
|
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 function gen_ignore_check()
|
||||||
local ignore_list = {}
|
local ignore_list = {}
|
||||||
if vim.g.nvim_tree_ignore and #vim.g.nvim_tree_ignore > 0 then
|
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()
|
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)
|
local handle = luv.fs_scandir(cwd)
|
||||||
if type(handle) == 'string' then
|
if type(handle) == 'string' then
|
||||||
api.nvim_err_writeln(handle)
|
api.nvim_err_writeln(handle)
|
||||||
@ -120,10 +140,12 @@ function M.refresh_entries(entries, cwd)
|
|||||||
local links = {}
|
local links = {}
|
||||||
local files = {}
|
local files = {}
|
||||||
local new_entries = {}
|
local new_entries = {}
|
||||||
|
local num_new_entries = 0
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local name, t = luv.fs_scandir_next(handle)
|
local name, t = luv.fs_scandir_next(handle)
|
||||||
if not name then break end
|
if not name then break end
|
||||||
|
num_new_entries = num_new_entries + 1
|
||||||
|
|
||||||
if not should_ignore(name) then
|
if not should_ignore(name) then
|
||||||
if t == 'directory' then
|
if t == 'directory' then
|
||||||
@ -139,6 +161,21 @@ function M.refresh_entries(entries, cwd)
|
|||||||
end
|
end
|
||||||
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
|
local idx = 1
|
||||||
for _, name in ipairs(cached_entries) do
|
for _, name in ipairs(cached_entries) do
|
||||||
if not new_entries[name] then
|
if not new_entries[name] then
|
||||||
@ -173,12 +210,18 @@ function M.refresh_entries(entries, cwd)
|
|||||||
change_prev = false
|
change_prev = false
|
||||||
end
|
end
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if next_node then
|
||||||
|
table.insert(entries, 1, next_node)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.populate(entries, cwd)
|
function M.populate(entries, cwd, parent_node)
|
||||||
local handle = luv.fs_scandir(cwd)
|
local handle = luv.fs_scandir(cwd)
|
||||||
if type(handle) == 'string' then
|
if type(handle) == 'string' then
|
||||||
api.nvim_err_writeln(handle)
|
api.nvim_err_writeln(handle)
|
||||||
@ -206,6 +249,20 @@ function M.populate(entries, cwd)
|
|||||||
|
|
||||||
-- Create Nodes --
|
-- 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
|
for _, dirname in ipairs(dirs) do
|
||||||
local dir = dir_new(cwd, dirname)
|
local dir = dir_new(cwd, dirname)
|
||||||
if luv.fs_access(dir.absolute_path, 'R') then
|
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 ""
|
local git_icon = get_git_icons(node, index, offset, #icon+1) or ""
|
||||||
-- INFO: this is mandatory in order to keep gui attributes (bold/italics)
|
-- INFO: this is mandatory in order to keep gui attributes (bold/italics)
|
||||||
local folder_hl = "NvimTreeFolderName"
|
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
|
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
|
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
|
end
|
||||||
index = index + 1
|
index = index + 1
|
||||||
if node.open then
|
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)
|
update_draw_data(node, depth + 2, markers)
|
||||||
else
|
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
|
end
|
||||||
elseif node.link_to then
|
elseif node.link_to then
|
||||||
local icon = get_symlink_icon()
|
local icon = get_symlink_icon()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user