Skip to content

Commit c1948cb

Browse files
committed
feat: Allow to expand nodes until certain condition is met
1 parent b0b4955 commit c1948cb

File tree

2 files changed

+97
-50
lines changed

2 files changed

+97
-50
lines changed

lua/nvim-tree/actions/tree/modifiers/expand.lua

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,30 @@ end
3030
---@param expansion_count integer
3131
---@param node Node
3232
---@return boolean
33-
local function should_expand(expansion_count, node)
33+
local function descend_until_max_or_empty(expansion_count, node)
3434
local dir = node:as(DirectoryNode)
3535
if not dir then
3636
return false
3737
end
38+
3839
local should_halt = expansion_count >= M.MAX_FOLDER_DISCOVERY
3940
local should_exclude = M.EXCLUDE[dir.name]
40-
return not should_halt and not dir.open and not should_exclude
41+
42+
return not should_halt and not should_exclude
43+
end
44+
45+
---@param expansion_count integer
46+
---@param node Node
47+
---@param should_descend fun(expansion_count: integer, node: Node): boolean
48+
---@return boolean
49+
local function should_expand(expansion_count, node, should_descend)
50+
local dir = node:as(DirectoryNode)
51+
return dir and not dir.open and should_descend(expansion_count, node)
4152
end
4253

43-
local function gen_iterator()
54+
55+
---@param should_descend fun(expansion_count: integer, node: Node): boolean
56+
local function gen_iterator(should_descend)
4457
local expansion_count = 0
4558

4659
return function(parent)
@@ -52,7 +65,7 @@ local function gen_iterator()
5265
Iterator.builder(parent.nodes)
5366
:hidden()
5467
:applier(function(node)
55-
if should_expand(expansion_count, node) then
68+
if should_expand(expansion_count, node, should_descend) then
5669
expansion_count = expansion_count + 1
5770
node = node:as(DirectoryNode)
5871
if node then
@@ -61,7 +74,19 @@ local function gen_iterator()
6174
end
6275
end)
6376
:recursor(function(node)
64-
return expansion_count < M.MAX_FOLDER_DISCOVERY and (node.group_next and { node.group_next } or (node.open and node.nodes))
77+
if not should_descend(expansion_count, node) then
78+
return nil
79+
end
80+
81+
if node.group_next then
82+
return { node.group_next }
83+
end
84+
85+
if node.open and node.nodes then
86+
return node.nodes
87+
end
88+
89+
return nil
6590
end)
6691
:iterate()
6792

@@ -72,12 +97,13 @@ local function gen_iterator()
7297
end
7398

7499
---@param node Node?
75-
local function expand_node(node)
100+
---@param expand_opts ApiTreeExpandAllOpts?
101+
local function expand_node(node, expand_opts)
76102
if not node then
77103
return
78104
end
79-
80-
if gen_iterator()(node) then
105+
local descend_until = (expand_opts and expand_opts.descend_until) or descend_until_max_or_empty
106+
if gen_iterator(descend_until)(node) then
81107
notify.warn("expansion iteration was halted after " .. M.MAX_FOLDER_DISCOVERY .. " discovered folders")
82108
end
83109

@@ -89,18 +115,20 @@ end
89115

90116
---Expand the directory node or the root
91117
---@param node Node
92-
function M.all(node)
93-
expand_node(node and node:as(DirectoryNode) or core.get_explorer())
118+
---@param expand_opts ApiTreeExpandAllOpts?
119+
function M.all(node, expand_opts)
120+
expand_node(node and node:as(DirectoryNode) or core.get_explorer(), expand_opts)
94121
end
95122

96123
---Expand the directory node or parent node
97124
---@param node Node
98-
function M.node(node)
125+
---@param expand_opts ApiTreeExpandAllOpts?
126+
function M.node(node, expand_opts)
99127
if not node then
100128
return
101129
end
102130

103-
expand_node(node:is(FileNode) and node.parent or node:as(DirectoryNode))
131+
expand_node(node:is(FileNode) and node.parent or node:as(DirectoryNode), expand_opts)
104132
end
105133

106134
function M.setup(opts)

lua/nvim-tree/api.lua

Lines changed: 57 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,45 @@ local function wrap_explorer_member(explorer_member, member_method)
121121
end)
122122
end
123123

124+
---
125+
---@class NodeEditOpts
126+
---@field quit_on_open boolean|nil default false
127+
---@field focus boolean|nil default true
128+
129+
---@param mode string
130+
---@param node Node
131+
---@param edit_opts NodeEditOpts?
132+
local function edit(mode, node, edit_opts)
133+
local file_link = node:as(FileLinkNode)
134+
local path = file_link and file_link.link_to or node.absolute_path
135+
local cur_tabpage = vim.api.nvim_get_current_tabpage()
136+
137+
local explorer = core.get_explorer()
138+
139+
actions.node.open_file.fn(mode, path)
140+
141+
edit_opts = edit_opts or {}
142+
143+
local mode_unsupported_quit_on_open = mode == "drop" or mode == "tab_drop" or mode == "edit_in_place"
144+
if not mode_unsupported_quit_on_open and edit_opts.quit_on_open then
145+
if explorer then
146+
explorer.view:close(cur_tabpage, "api.edit " .. mode)
147+
end
148+
end
149+
150+
local mode_unsupported_focus = mode == "drop" or mode == "tab_drop" or mode == "edit_in_place"
151+
local focus = edit_opts.focus == nil or edit_opts.focus == true
152+
if not mode_unsupported_focus and not focus then
153+
-- if mode == "tabnew" a new tab will be opened and we need to focus back to the previous tab
154+
if mode == "tabnew" then
155+
vim.cmd(":tabprev")
156+
end
157+
if explorer then
158+
explorer.view:focus()
159+
end
160+
end
161+
end
162+
124163
---@class ApiTreeOpenOpts
125164
---@field path string|nil path
126165
---@field current_window boolean|nil default false
@@ -186,7 +225,25 @@ Api.tree.search_node = wrap(actions.finders.search_node.fn)
186225
---@field keep_buffers boolean|nil default false
187226

188227
Api.tree.collapse_all = wrap(actions.tree.modifiers.collapse.all)
228+
229+
---@class ApiTreeExpandAllOpts
230+
---@field descend_until (fun(expansion_count: integer, node: Node): boolean)|nil
231+
189232
Api.tree.expand_all = wrap_node(actions.tree.modifiers.expand.all)
233+
234+
Api.tree.toggle_descend_until = wrap_node(function(node, descend_until)
235+
if node.open then
236+
local dir = node:as(DirectoryNode)
237+
dir:expand_or_collapse("edit")
238+
else
239+
if node.nodes then
240+
actions.tree.modifiers.expand.all(node, { descend_until = descend_until })
241+
else
242+
edit("edit", node)
243+
end
244+
end
245+
end)
246+
190247
Api.tree.toggle_enable_filters = wrap_explorer_member("filters", "toggle")
191248
Api.tree.toggle_gitignore_filter = wrap_explorer_member_args("filters", "toggle", "git_ignored")
192249
Api.tree.toggle_git_clean_filter = wrap_explorer_member_args("filters", "toggle", "git_clean")
@@ -225,44 +282,6 @@ Api.fs.copy.absolute_path = wrap_node(wrap_explorer_member("clipboard", "copy_ab
225282
Api.fs.copy.filename = wrap_node(wrap_explorer_member("clipboard", "copy_filename"))
226283
Api.fs.copy.basename = wrap_node(wrap_explorer_member("clipboard", "copy_basename"))
227284
Api.fs.copy.relative_path = wrap_node(wrap_explorer_member("clipboard", "copy_path"))
228-
---
229-
---@class NodeEditOpts
230-
---@field quit_on_open boolean|nil default false
231-
---@field focus boolean|nil default true
232-
233-
---@param mode string
234-
---@param node Node
235-
---@param edit_opts NodeEditOpts?
236-
local function edit(mode, node, edit_opts)
237-
local file_link = node:as(FileLinkNode)
238-
local path = file_link and file_link.link_to or node.absolute_path
239-
local cur_tabpage = vim.api.nvim_get_current_tabpage()
240-
241-
local explorer = core.get_explorer()
242-
243-
actions.node.open_file.fn(mode, path)
244-
245-
edit_opts = edit_opts or {}
246-
247-
local mode_unsupported_quit_on_open = mode == "drop" or mode == "tab_drop" or mode == "edit_in_place"
248-
if not mode_unsupported_quit_on_open and edit_opts.quit_on_open then
249-
if explorer then
250-
explorer.view:close(cur_tabpage, "api.edit " .. mode)
251-
end
252-
end
253-
254-
local mode_unsupported_focus = mode == "drop" or mode == "tab_drop" or mode == "edit_in_place"
255-
local focus = edit_opts.focus == nil or edit_opts.focus == true
256-
if not mode_unsupported_focus and not focus then
257-
-- if mode == "tabnew" a new tab will be opened and we need to focus back to the previous tab
258-
if mode == "tabnew" then
259-
vim.cmd(":tabprev")
260-
end
261-
if explorer then
262-
explorer.view:focus()
263-
end
264-
end
265-
end
266285

267286
---@param mode string
268287
---@param toggle_group boolean?

0 commit comments

Comments
 (0)