Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Allow to expand until certain condition is met #2790

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 33 additions & 17 deletions lua/nvim-tree/actions/tree/modifiers/expand-all.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ local renderer = require "nvim-tree.renderer"
local Iterator = require "nvim-tree.iterators.node-iterator"
local notify = require "nvim-tree.notify"
local lib = require "nvim-tree.lib"
local git = require "nvim-tree.git"
ghostbuster91 marked this conversation as resolved.
Show resolved Hide resolved

local M = {}

Expand All @@ -18,42 +19,56 @@ local function to_lookup_table(list)
end

---@param node Node
local function expand(node)
node = lib.get_last_group_node(node)
node.open = true
local function populate_node(node)
-- noop if it is a file
if node.nodes == nil then
return
end
if #node.nodes == 0 then
core.get_explorer():expand(node)
local cwd = node.link_to or node.absolute_path
local handle = vim.loop.fs_scandir(cwd)
if not handle then
return
end
local status = git.load_project_status(cwd)
core.get_explorer():expand(node, status)
end
end

---@param expansion_count integer
---@param node Node
---@param populate_node function
---@return boolean
local function should_expand(expansion_count, node)
-- luacheck: push ignore populate_node
local function expand_until_max_or_empty(expansion_count, node, populate_node)
local should_halt = expansion_count >= M.MAX_FOLDER_DISCOVERY
local should_exclude = M.EXCLUDE[node.name]
return not should_halt and node.nodes and not node.open and not should_exclude
local result = not should_halt and node.nodes and not node.open and not should_exclude
return result
end
-- luacheck: pop

local function gen_iterator()
local function gen_iterator(should_expand_fn)
local expansion_count = 0

return function(parent)
if parent.parent and parent.nodes and not parent.open then
expansion_count = expansion_count + 1
expand(parent)
end
populate_node(parent)
parent.open = true

Iterator.builder(parent.nodes)
Iterator.builder({ parent })
:hidden()
:applier(function(node)
if should_expand(expansion_count, node) then
local should_expand = should_expand_fn(expansion_count, node, populate_node)
if should_expand then
expansion_count = expansion_count + 1
expand(node)
populate_node(node)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not quite following the logic here, but it looks like we're not actually making many changes e.g.

  • expand was renamed to populate_node
  • unnecessary should_expand boolean was extracted
  • get_last_group_node was moved out of expand

Please can you refactor this down to the minimum number of changes necessary. Integration with the existing structure will result in simpler code that is more easily reviewable and maintainable.

I appreciate that we've been through many revisions to get to this point, with complexity from earlier commits removed, however we need to revert some structural changes. Perhaps consider how you'd make these changes from scratch, knowing what you have learned over the course of this PR.

Copy link
Contributor Author

@ghostbuster91 ghostbuster91 Sep 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please can you refactor this down to the minimum number of changes necessary. Integration with the existing structure will result in simpler code that is more easily reviewable and maintainable.

I totally agree with such approach. I think I was able to cut down amount of changes to the bare minimum.

There is one thing that I would like to discuss - changes in the recursor function. Before, when the logic was only to expand everything recursively, the condition was to check if node.open==true (putting expansion_count aside). In my case that is invalid as some nodes have been opened but they shouldn't be traversed further.

In both cases reusing should_expand function results in correct behavior. First of all, this doesn't have to be true in general. Maybe sometimes the logic for should_recurse will be different than the logic for should_expand.

Second. So, for each node we call should_expand twice, once to check if we should expand and second time to check if we should recurse. Between these calls, if should should_expand returns true, we call expand that changes the state of a given node. This means that users must not relay on node.open in their should_expand implementation as this would result in incorrect behavior.

I don't have enough experience in developing nvim plugins to judge if this is a problem, but wanted to bring this to your attention. Maybe this should at least be mentioned in the documentation?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In both cases reusing should_expand function results in correct behavior. First of all, this doesn't have to be true in general. Maybe sometimes the logic for should_recurse will be different than the logic for should_expand.

I like the consistency, it's more reason-able and will make maintenance easier.

This means that users must not relay on node.open in their should_expand implementation as this would result in incorrect behavior.

So long as we document that we are providing a consistent user experience.

Is node.nodes correct; can they test that?

node = lib.get_last_group_node(node)
node.open = true
end
end)
:recursor(function(node)
return expansion_count < M.MAX_FOLDER_DISCOVERY and (node.group_next and { node.group_next } or (node.open and node.nodes))
local should_recurse = should_expand_fn(expansion_count - 1, node, populate_node)
return expansion_count < M.MAX_FOLDER_DISCOVERY and (should_recurse and node.open and node.nodes)
end)
:iterate()

Expand All @@ -64,9 +79,10 @@ local function gen_iterator()
end

---@param base_node table
function M.fn(base_node)
function M.fn(base_node, expand_until)
ghostbuster91 marked this conversation as resolved.
Show resolved Hide resolved
expand_until = expand_until or expand_until_max_or_empty
local node = base_node.nodes and base_node or core.get_explorer()
if gen_iterator()(node) then
if gen_iterator(expand_until)(node) then
notify.warn("expansion iteration was halted after " .. M.MAX_FOLDER_DISCOVERY .. " discovered folders")
end
renderer.draw()
Expand Down
2 changes: 1 addition & 1 deletion lua/nvim-tree/explorer/node.lua
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ end
---@param node Node
---@return boolean
function M.has_one_child_folder(node)
return #node.nodes == 1 and node.nodes[1].nodes and vim.loop.fs_access(node.nodes[1].absolute_path, "R") or false
return node.nodes ~= nil and #node.nodes == 1 and node.nodes[1].nodes and vim.loop.fs_access(node.nodes[1].absolute_path, "R") or false
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: guarding against calling this function with a file node

end

---@param node Node
Expand Down
Loading