Skip to content

Commit 8cbb1db

Browse files
juefeiyanjuefei yanalex-courtis
authored
feat: add node.open.toggle_group_empty, default mapping L (#2647)
* feat: ungrouping empty directories * add a new api to toggle empty folders * solve comments * solve comments * update help --------- Co-authored-by: juefei yan <[email protected]> Co-authored-by: Alexander Courtis <[email protected]>
1 parent f39f7b6 commit 8cbb1db

File tree

4 files changed

+71
-7
lines changed

4 files changed

+71
-7
lines changed

doc/nvim-tree-lua.txt

+8
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ Show the mappings: `g?`
181181
`I` Toggle Filter: Git Ignore |nvim-tree-api.tree.toggle_gitignore_filter()|
182182
`J` Last Sibling |nvim-tree-api.node.navigate.sibling.last()|
183183
`K` First Sibling |nvim-tree-api.node.navigate.sibling.first()|
184+
`L` Toggle Group Empty |nvim-tree-api.node.open.toggle_group_empty()|
184185
`M` Toggle Filter: No Bookmark |nvim-tree-api.tree.toggle_no_bookmark_filter()|
185186
`m` Toggle Bookmark |nvim-tree-api.marks.toggle()|
186187
`o` Open |nvim-tree-api.node.open.edit()|
@@ -1848,6 +1849,12 @@ node.open.vertical() *nvim-tree-api.node.open.vertical()*
18481849
node.open.horizontal() *nvim-tree-api.node.open.horizontal()*
18491850
|nvim-tree-api.node.edit()|, file will be opened in a new horizontal split.
18501851

1852+
*nvim-tree-api.node.open.toggle_group_empty()*
1853+
node.open.toggle_group_empty()
1854+
Toggle |nvim-tree.renderer.group_empty| for a specific folder.
1855+
Does nothing on files.
1856+
Needs |nvim-tree.renderer.group_empty| set.
1857+
18511858
node.open.drop() *nvim-tree-api.node.open.drop()*
18521859
Switch to window with selected file if it exists.
18531860
Open file otherwise.
@@ -2196,6 +2203,7 @@ You are encouraged to copy these to your own |nvim-tree.on_attach| function.
21962203
vim.keymap.set('n', 'I', api.tree.toggle_gitignore_filter, opts('Toggle Filter: Git Ignore'))
21972204
vim.keymap.set('n', 'J', api.node.navigate.sibling.last, opts('Last Sibling'))
21982205
vim.keymap.set('n', 'K', api.node.navigate.sibling.first, opts('First Sibling'))
2206+
vim.keymap.set('n', 'L', api.node.open.toggle_group_empty, opts('Toggle Group Empty'))
21992207
vim.keymap.set('n', 'M', api.tree.toggle_no_bookmark_filter, opts('Toggle Filter: No Bookmark'))
22002208
vim.keymap.set('n', 'm', api.marks.toggle, opts('Toggle Bookmark'))
22012209
vim.keymap.set('n', 'o', api.node.open.edit, opts('Open'))

lua/nvim-tree/api.lua

+4-3
Original file line numberDiff line numberDiff line change
@@ -175,13 +175,13 @@ end
175175

176176
---@param mode string
177177
---@return fun(node: table)
178-
local function open_or_expand_or_dir_up(mode)
178+
local function open_or_expand_or_dir_up(mode, toggle_group)
179179
return function(node)
180180
if node.name == ".." then
181181
actions.root.change_dir.fn ".."
182182
elseif node.nodes then
183-
lib.expand_or_collapse(node)
184-
else
183+
lib.expand_or_collapse(node, toggle_group)
184+
elseif not toggle_group then
185185
edit(mode, node)
186186
end
187187
end
@@ -195,6 +195,7 @@ Api.node.open.no_window_picker = wrap_node(open_or_expand_or_dir_up "edit_no_pic
195195
Api.node.open.vertical = wrap_node(open_or_expand_or_dir_up "vsplit")
196196
Api.node.open.horizontal = wrap_node(open_or_expand_or_dir_up "split")
197197
Api.node.open.tab = wrap_node(open_or_expand_or_dir_up "tabnew")
198+
Api.node.open.toggle_group_empty = wrap_node(open_or_expand_or_dir_up("toggle_group_empty", true))
198199
Api.node.open.preview = wrap_node(open_or_expand_or_dir_up "preview")
199200
Api.node.open.preview_no_picker = wrap_node(open_or_expand_or_dir_up "preview_no_picker")
200201

lua/nvim-tree/keymap.lua

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ function M.default_on_attach(bufnr)
7272
vim.keymap.set('n', 'I', api.tree.toggle_gitignore_filter, opts('Toggle Filter: Git Ignore'))
7373
vim.keymap.set('n', 'J', api.node.navigate.sibling.last, opts('Last Sibling'))
7474
vim.keymap.set('n', 'K', api.node.navigate.sibling.first, opts('First Sibling'))
75+
vim.keymap.set('n', 'L', api.node.open.toggle_group_empty, opts('Toggle Group Empty'))
7576
vim.keymap.set('n', 'M', api.tree.toggle_no_bookmark_filter, opts('Toggle Filter: No Bookmark'))
7677
vim.keymap.set('n', 'm', api.marks.toggle, opts('Toggle Bookmark'))
7778
vim.keymap.set('n', 'o', api.node.open.edit, opts('Open'))

lua/nvim-tree/lib.lua

+58-4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ local view = require "nvim-tree.view"
33
local core = require "nvim-tree.core"
44
local utils = require "nvim-tree.utils"
55
local events = require "nvim-tree.events"
6+
local explorer_node = require "nvim-tree.explorer.node"
67

78
---@class LibOpenOpts
89
---@field path string|nil path
@@ -86,6 +87,34 @@ function M.get_last_group_node(node)
8687
return node
8788
end
8889

90+
---Group empty folders
91+
-- Recursively group nodes
92+
---@param node Node
93+
---@return Node[]
94+
function M.group_empty_folders(node)
95+
local is_root = not node.parent
96+
local child_folder_only = explorer_node.has_one_child_folder(node) and node.nodes[1]
97+
if M.group_empty and not is_root and child_folder_only then
98+
node.group_next = child_folder_only
99+
local ns = M.group_empty_folders(child_folder_only)
100+
node.nodes = ns or {}
101+
return ns
102+
end
103+
return node.nodes
104+
end
105+
106+
---Ungroup empty folders
107+
-- If a node is grouped, ungroup it: put node.group_next to the node.nodes and set node.group_next to nil
108+
---@param node Node
109+
function M.ungroup_empty_folders(node)
110+
local cur = node
111+
while cur and cur.group_next do
112+
cur.nodes = { cur.group_next }
113+
cur.group_next = nil
114+
cur = cur.nodes[1]
115+
end
116+
end
117+
89118
---@param node Node
90119
---@return Node[]
91120
function M.get_all_nodes_in_group(node)
@@ -98,8 +127,21 @@ function M.get_all_nodes_in_group(node)
98127
return nodes
99128
end
100129

130+
-- Toggle group empty folders
131+
---@param head_node Node
132+
local function toggle_group_folders(head_node)
133+
local is_grouped = head_node.group_next ~= nil
134+
135+
if is_grouped then
136+
M.ungroup_empty_folders(head_node)
137+
else
138+
M.group_empty_folders(head_node)
139+
end
140+
end
141+
101142
---@param node Node
102-
function M.expand_or_collapse(node)
143+
function M.expand_or_collapse(node, toggle_group)
144+
toggle_group = toggle_group or false
103145
if node.has_children then
104146
node.has_children = false
105147
end
@@ -108,9 +150,20 @@ function M.expand_or_collapse(node)
108150
core.get_explorer():expand(node)
109151
end
110152

111-
local open = not M.get_last_group_node(node).open
112-
for _, n in ipairs(M.get_all_nodes_in_group(node)) do
113-
n.open = open
153+
local head_node = utils.get_parent_of_group(node)
154+
if toggle_group then
155+
toggle_group_folders(head_node)
156+
end
157+
158+
local open = M.get_last_group_node(node).open
159+
local next_open
160+
if toggle_group then
161+
next_open = open
162+
else
163+
next_open = not open
164+
end
165+
for _, n in ipairs(M.get_all_nodes_in_group(head_node)) do
166+
n.open = next_open
114167
end
115168

116169
renderer.draw()
@@ -213,6 +266,7 @@ function M.setup(opts)
213266
M.hijack_directories = opts.hijack_directories
214267
M.respect_buf_cwd = opts.respect_buf_cwd
215268
M.select_prompts = opts.select_prompts
269+
M.group_empty = opts.renderer.group_empty
216270
end
217271

218272
return M

0 commit comments

Comments
 (0)