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: ungrouping empty directories #2647

Merged
merged 5 commits into from
Feb 4, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
5 changes: 5 additions & 0 deletions doc/nvim-tree-lua.txt
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ Show the mappings: `g?`
`I` Toggle Filter: Git Ignore |nvim-tree-api.tree.toggle_gitignore_filter()|
`J` Last Sibling |nvim-tree-api.node.navigate.sibling.last()|
`K` First Sibling |nvim-tree-api.node.navigate.sibling.first()|
`L` Group or Ungroup Folders |nvim-tree-api.node.tree.toggle_grouped_folders()|
`M` Toggle Filter: No Bookmark |nvim-tree-api.tree.toggle_no_bookmark_filter()|
`m` Toggle Bookmark |nvim-tree-api.marks.toggle()|
`o` Open |nvim-tree-api.node.open.edit()|
Expand Down Expand Up @@ -1848,6 +1849,9 @@ node.open.vertical() *nvim-tree-api.node.open.vertical()*
node.open.horizontal() *nvim-tree-api.node.open.horizontal()*
|nvim-tree-api.node.edit()|, file will be opened in a new horizontal split.

node.open.toggle_grouped_folders() *nvim-tree-api.node.open.toggle_grouped_folders()*
Copy link
Member

Choose a reason for hiding this comment

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

This is actually specific - it groups/ungroups one folder and somewhat persists that, which is desirable.

Perhaps:

                                *nvim-tree-api.node.open.toggle_group_empty()*
node.open.toggle_group_empty()
    Toggle |nvim-tree.renderer.group_empty| for a specific folder.
    Does nothing on files.
    Needs |nvim-tree.renderer.group_empty| set.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thanks for the nice name

|nvim-tree-api.node.edit()|, empty folders would be grouped or ungrouped.

node.open.drop() *nvim-tree-api.node.open.drop()*
Switch to window with selected file if it exists.
Open file otherwise.
Expand Down Expand Up @@ -2196,6 +2200,7 @@ You are encouraged to copy these to your own |nvim-tree.on_attach| function.
vim.keymap.set('n', 'I', api.tree.toggle_gitignore_filter, opts('Toggle Filter: Git Ignore'))
vim.keymap.set('n', 'J', api.node.navigate.sibling.last, opts('Last Sibling'))
vim.keymap.set('n', 'K', api.node.navigate.sibling.first, opts('First Sibling'))
vim.keymap.set('n', 'L', api.node.open.toggle_grouped_folders,opts('Toggle Grouped Folders'))
Copy link
Member

@alex-courtis alex-courtis Feb 3, 2024

Choose a reason for hiding this comment

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

Suggested change
vim.keymap.set('n', 'L', api.node.open.toggle_grouped_folders,opts('Toggle Grouped Folders'))
vim.keymap.set('n', 'L', api.node.open.toggle_grouped_folders,opts('Toggle Group Empty'))

L seems like the only viable mapping.

vim.keymap.set('n', 'M', api.tree.toggle_no_bookmark_filter, opts('Toggle Filter: No Bookmark'))
vim.keymap.set('n', 'm', api.marks.toggle, opts('Toggle Bookmark'))
vim.keymap.set('n', 'o', api.node.open.edit, opts('Open'))
Expand Down
5 changes: 3 additions & 2 deletions lua/nvim-tree/api.lua
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,12 @@ end

---@param mode string
---@return fun(node: table)
local function open_or_expand_or_dir_up(mode)
local function open_or_expand_or_dir_up(mode, toggle_group)
return function(node)
if node.name == ".." then
actions.root.change_dir.fn ".."
elseif node.nodes then
lib.expand_or_collapse(node)
lib.expand_or_collapse(node, toggle_group)
else
edit(mode, node)
juefeiyan marked this conversation as resolved.
Show resolved Hide resolved
end
Expand All @@ -195,6 +195,7 @@ Api.node.open.no_window_picker = wrap_node(open_or_expand_or_dir_up "edit_no_pic
Api.node.open.vertical = wrap_node(open_or_expand_or_dir_up "vsplit")
Api.node.open.horizontal = wrap_node(open_or_expand_or_dir_up "split")
Api.node.open.tab = wrap_node(open_or_expand_or_dir_up "tabnew")
Api.node.open.toggle_grouped_folders = wrap_node(open_or_expand_or_dir_up("toggle_grouped_folders", true))
Api.node.open.preview = wrap_node(open_or_expand_or_dir_up "preview")
Api.node.open.preview_no_picker = wrap_node(open_or_expand_or_dir_up "preview_no_picker")

Expand Down
1 change: 1 addition & 0 deletions lua/nvim-tree/keymap.lua
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ function M.default_on_attach(bufnr)
vim.keymap.set('n', 'I', api.tree.toggle_gitignore_filter, opts('Toggle Filter: Git Ignore'))
vim.keymap.set('n', 'J', api.node.navigate.sibling.last, opts('Last Sibling'))
vim.keymap.set('n', 'K', api.node.navigate.sibling.first, opts('First Sibling'))
vim.keymap.set('n', 'L', api.node.open.toggle_grouped_folders,opts('Toggle Grouped Folders'))
vim.keymap.set('n', 'M', api.tree.toggle_no_bookmark_filter, opts('Toggle Filter: No Bookmark'))
vim.keymap.set('n', 'm', api.marks.toggle, opts('Toggle Bookmark'))
vim.keymap.set('n', 'o', api.node.open.edit, opts('Open'))
Expand Down
61 changes: 58 additions & 3 deletions lua/nvim-tree/lib.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ local view = require "nvim-tree.view"
local core = require "nvim-tree.core"
local utils = require "nvim-tree.utils"
local events = require "nvim-tree.events"
local explorer_node = require "nvim-tree.explorer.node"

---@class LibOpenOpts
---@field path string|nil path
Expand Down Expand Up @@ -86,6 +87,34 @@ function M.get_last_group_node(node)
return node
end

---Group empty folders
-- Recursively group nodes
---@param node Node
---@return Node[]
function M.group_empty_folders(node)
local is_root = not node.parent
local child_folder_only = explorer_node.has_one_child_folder(node) and node.nodes[1]
if M.group_empty and not is_root and child_folder_only then
node.group_next = child_folder_only
local ns = M.group_empty_folders(child_folder_only)
node.nodes = ns or {}
return ns
end
return node.nodes
end

---Ungroup empty folders
-- If a node is grouped, ungroup it: put node.group_next to the node.nodes and set node.group_next to nil
---@param node Node
function M.ungroup_empty_folders(node)
local cur = node
while cur and cur.group_next do
cur.nodes = { cur.group_next }
cur.group_next = nil
cur = cur.nodes[1]
end
end

---@param node Node
---@return Node[]
function M.get_all_nodes_in_group(node)
Expand All @@ -98,8 +127,26 @@ function M.get_all_nodes_in_group(node)
return nodes
end

-- If a folder is grouped and closed -> group folder and open
-- If a folder is grouped and opened -> ungroup folder and open
-- If a folder is ungrouped and opened -> group folder and close
---@param head_node Node
---@param open boolean
---@return boolean
local function toggle_group_folders(head_node, open)
local is_grouped = head_node.group_next ~= nil

if open and is_grouped then
M.ungroup_empty_folders(head_node)
elseif open then
M.group_empty_folders(head_node)
end
return is_grouped or not open
end

---@param node Node
function M.expand_or_collapse(node)
function M.expand_or_collapse(node, toggle_group)
toggle_group = toggle_group or false
if node.has_children then
node.has_children = false
end
Expand All @@ -108,8 +155,15 @@ function M.expand_or_collapse(node)
core.get_explorer():expand(node)
end

local open = not M.get_last_group_node(node).open
for _, n in ipairs(M.get_all_nodes_in_group(node)) do
local head_node = utils.get_parent_of_group(node)
local open = M.get_last_group_node(node).open
if toggle_group then
open = toggle_group_folders(head_node, open)
else
open = not open
end

for _, n in ipairs(M.get_all_nodes_in_group(head_node)) do
n.open = open
end

Expand Down Expand Up @@ -213,6 +267,7 @@ function M.setup(opts)
M.hijack_directories = opts.hijack_directories
M.respect_buf_cwd = opts.respect_buf_cwd
M.select_prompts = opts.select_prompts
M.group_empty = opts.renderer.group_empty
end

return M
Loading