From ae595611fb2225f2041996c042aa4e4b8663b41e Mon Sep 17 00:00:00 2001 From: Rami Elwan Date: Sat, 14 Jun 2025 09:26:58 +0200 Subject: [PATCH] feat(#3132): add api.node.expand and api.node.collapse (#3133) * feat: allow passing node to collapse all * refactor: use snake case * feat: handle api legacy calls and update signature * refactor: make sure open is a boolean * doc: collapse_all * Revert "doc: collapse_all" This reverts commit d243da3e14455c2d0b0f77b77ed617dd570cc025. * add api.node.collapse * add api.node.expand * add api.node.expand --------- Co-authored-by: Alexander Courtis --- doc/nvim-tree-lua.txt | 35 ++++++++++++++--- .../{collapse-all.lua => collapse.lua} | 38 +++++++++++++++---- .../modifiers/{expand-all.lua => expand.lua} | 30 +++++++++++---- lua/nvim-tree/actions/tree/modifiers/init.lua | 6 +-- lua/nvim-tree/api.lua | 11 +++++- lua/nvim-tree/keymap.lua | 2 +- 6 files changed, 96 insertions(+), 26 deletions(-) rename lua/nvim-tree/actions/tree/modifiers/{collapse-all.lua => collapse.lua} (55%) rename lua/nvim-tree/actions/tree/modifiers/{expand-all.lua => expand.lua} (83%) diff --git a/doc/nvim-tree-lua.txt b/doc/nvim-tree-lua.txt index 571e16d3..df57ca03 100644 --- a/doc/nvim-tree-lua.txt +++ b/doc/nvim-tree-lua.txt @@ -206,7 +206,7 @@ Show the mappings: `g?` `S` Search |nvim-tree-api.tree.search_node()| `u` Rename: Full Path |nvim-tree-api.fs.rename_full()| `U` Toggle Filter: Hidden |nvim-tree-api.tree.toggle_custom_filter()| -`W` Collapse |nvim-tree-api.tree.collapse_all()| +`W` Collapse All |nvim-tree-api.tree.collapse_all()| `x` Cut |nvim-tree-api.fs.cut()| `y` Copy Name |nvim-tree-api.fs.copy.filename()| `Y` Copy Relative Path |nvim-tree-api.fs.copy.relative_path()| @@ -341,7 +341,7 @@ See |nvim-tree-highlight| for details. See |nvim-tree-api.tree.collapse_all()| - Calls: `api.tree.collapse_all(false)` + Calls: `api.tree.collapse_all({ keep_buffers = false })` *:NvimTreeCollapseKeepBuffers* @@ -350,7 +350,7 @@ See |nvim-tree-highlight| for details. See |nvim-tree-api.tree.collapse_all()| - Calls: `api.tree.collapse_all(true)` + Calls: `api.tree.collapse_all({ keep_buffers = true })` *:NvimTreeHiTest* @@ -1470,7 +1470,8 @@ vim |current-directory| behaviour. Type: `boolean`, Default: `false` *nvim-tree.actions.expand_all* -Configuration for expand_all behaviour. +Configuration for |nvim-tree-api.tree.expand_all()| and +|nvim-tree-api.node.expand()| *nvim-tree.actions.expand_all.max_folder_discovery* Limit the number of folders being explored when expanding every folders. @@ -1831,10 +1832,13 @@ tree.find_file({opts}) *nvim-tree-api.tree.find_file()* tree.search_node() *nvim-tree-api.tree.search_node()* Open the search dialogue as per the search_node action. -tree.collapse_all({keep_buffers}) *nvim-tree-api.tree.collapse_all()* +tree.collapse_all({opts}) *nvim-tree-api.tree.collapse_all()* Collapse the tree. Parameters: ~ + • {opts} (table) optional parameters + + Options: ~ • {keep_buffers} (boolean) do not collapse nodes with open buffers. tree.expand_all({node}) *nvim-tree-api.tree.expand_all()* @@ -2275,6 +2279,23 @@ node.buffer.wipe({node}, {opts}) *nvim-tree-api.node.buffer.wipe()* Options: ~ • {force} (boolean) wipe even if buffer is modified, default false +node.expand({node}) *nvim-tree-api.node.expand()* + Recursively expand all nodes under a directory or a file's parent + directory. + + Parameters: ~ + • {node} (Node|nil) file or folder + +node.collapse({node}, {opts}) *nvim-tree-api.node.collapse()* + Collapse the tree under a directory or a file's parent directory. + + Parameters: ~ + • {node} (Node|nil) file or folder + • {opts} (table) optional parameters + + Options: ~ + • {keep_buffers} (boolean) do not collapse nodes with open buffers. + ============================================================================== 6.4 API GIT *nvim-tree-api.git* @@ -2529,7 +2550,7 @@ You are encouraged to copy these to your own |nvim-tree.on_attach| function. >lu vim.keymap.set("n", "S", api.tree.search_node, opts("Search")) vim.keymap.set("n", "u", api.fs.rename_full, opts("Rename: Full Path")) vim.keymap.set("n", "U", api.tree.toggle_custom_filter, opts("Toggle Filter: Hidden")) - vim.keymap.set("n", "W", api.tree.collapse_all, opts("Collapse")) + vim.keymap.set("n", "W", api.tree.collapse_all, opts("Collapse All")) vim.keymap.set("n", "x", api.fs.cut, opts("Cut")) vim.keymap.set("n", "y", api.fs.copy.filename, opts("Copy Name")) vim.keymap.set("n", "Y", api.fs.copy.relative_path, opts("Copy Relative Path")) @@ -3301,6 +3322,8 @@ highlight group is not, hard linking as follows: > |nvim-tree-api.marks.toggle()| |nvim-tree-api.node.buffer.delete()| |nvim-tree-api.node.buffer.wipe()| +|nvim-tree-api.node.collapse()| +|nvim-tree-api.node.expand()| |nvim-tree-api.node.navigate.diagnostics.next()| |nvim-tree-api.node.navigate.diagnostics.next_recursive()| |nvim-tree-api.node.navigate.diagnostics.prev()| diff --git a/lua/nvim-tree/actions/tree/modifiers/collapse-all.lua b/lua/nvim-tree/actions/tree/modifiers/collapse.lua similarity index 55% rename from lua/nvim-tree/actions/tree/modifiers/collapse-all.lua rename to lua/nvim-tree/actions/tree/modifiers/collapse.lua index 049be2bb..62da5f9a 100644 --- a/lua/nvim-tree/actions/tree/modifiers/collapse-all.lua +++ b/lua/nvim-tree/actions/tree/modifiers/collapse.lua @@ -2,6 +2,7 @@ local utils = require("nvim-tree.utils") local core = require("nvim-tree.core") local Iterator = require("nvim-tree.iterators.node-iterator") +local FileNode = require("nvim-tree.node.file") local DirectoryNode = require("nvim-tree.node.directory") local M = {} @@ -23,26 +24,30 @@ local function buf_match() end end ----@param keep_buffers boolean -function M.fn(keep_buffers) +---Collapse a node, root if nil +---@param node Node? +---@param opts ApiCollapseOpts +local function collapse(node, opts) local explorer = core.get_explorer() if not explorer then return end - local node = explorer:get_node_at_cursor() - if not node then + node = node or explorer + + local node_at_cursor = explorer:get_node_at_cursor() + if not node_at_cursor then return end local matches = buf_match() - Iterator.builder(explorer.nodes) + Iterator.builder({ node:is(FileNode) and node.parent or node:as(DirectoryNode) }) :hidden() :applier(function(n) local dir = n:as(DirectoryNode) if dir then - dir.open = keep_buffers and matches(dir.absolute_path) + dir.open = opts.keep_buffers == true and matches(dir.absolute_path) end end) :recursor(function(n) @@ -51,7 +56,26 @@ function M.fn(keep_buffers) :iterate() explorer.renderer:draw() - utils.focus_node_or_parent(node) + utils.focus_node_or_parent(node_at_cursor) +end + + +---@param opts ApiCollapseOpts|boolean|nil legacy -> opts.keep_buffers +function M.all(opts) + -- legacy arguments + if type(opts) == "boolean" then + opts = { + keep_buffers = opts, + } + end + + collapse(nil, opts or {}) +end + +---@param node Node +---@param opts ApiCollapseOpts? +function M.node(node, opts) + collapse(node, opts or {}) end return M diff --git a/lua/nvim-tree/actions/tree/modifiers/expand-all.lua b/lua/nvim-tree/actions/tree/modifiers/expand.lua similarity index 83% rename from lua/nvim-tree/actions/tree/modifiers/expand-all.lua rename to lua/nvim-tree/actions/tree/modifiers/expand.lua index 6032c686..385ff72b 100644 --- a/lua/nvim-tree/actions/tree/modifiers/expand-all.lua +++ b/lua/nvim-tree/actions/tree/modifiers/expand.lua @@ -2,6 +2,7 @@ local core = require("nvim-tree.core") local Iterator = require("nvim-tree.iterators.node-iterator") local notify = require("nvim-tree.notify") +local FileNode = require("nvim-tree.node.file") local DirectoryNode = require("nvim-tree.node.directory") local M = {} @@ -70,23 +71,38 @@ local function gen_iterator() end end ----Expand the directory node or the root ----@param node Node -function M.fn(node) - local explorer = core.get_explorer() - local parent = node:as(DirectoryNode) or explorer - if not parent then +---@param node Node? +local function expand_node(node) + if not node then return end - if gen_iterator()(parent) then + if gen_iterator()(node) then notify.warn("expansion iteration was halted after " .. M.MAX_FOLDER_DISCOVERY .. " discovered folders") end + + local explorer = core.get_explorer() if explorer then explorer.renderer:draw() end end +---Expand the directory node or the root +---@param node Node +function M.all(node) + expand_node(node and node:as(DirectoryNode) or core.get_explorer()) +end + +---Expand the directory node or parent node +---@param node Node +function M.node(node) + if not node then + return + end + + expand_node(node:is(FileNode) and node.parent or node:as(DirectoryNode)) +end + function M.setup(opts) M.MAX_FOLDER_DISCOVERY = opts.actions.expand_all.max_folder_discovery M.EXCLUDE = to_lookup_table(opts.actions.expand_all.exclude) diff --git a/lua/nvim-tree/actions/tree/modifiers/init.lua b/lua/nvim-tree/actions/tree/modifiers/init.lua index f3ce27fc..cf1ae40b 100644 --- a/lua/nvim-tree/actions/tree/modifiers/init.lua +++ b/lua/nvim-tree/actions/tree/modifiers/init.lua @@ -1,10 +1,10 @@ local M = {} -M.collapse_all = require("nvim-tree.actions.tree.modifiers.collapse-all") -M.expand_all = require("nvim-tree.actions.tree.modifiers.expand-all") +M.collapse = require("nvim-tree.actions.tree.modifiers.collapse") +M.expand = require("nvim-tree.actions.tree.modifiers.expand") function M.setup(opts) - M.expand_all.setup(opts) + M.expand.setup(opts) end return M diff --git a/lua/nvim-tree/api.lua b/lua/nvim-tree/api.lua index 822dbf20..39fba07d 100644 --- a/lua/nvim-tree/api.lua +++ b/lua/nvim-tree/api.lua @@ -182,8 +182,12 @@ Api.tree.get_nodes = wrap_explorer("get_nodes") Api.tree.find_file = wrap(actions.tree.find_file.fn) Api.tree.search_node = wrap(actions.finders.search_node.fn) -Api.tree.collapse_all = wrap(actions.tree.modifiers.collapse_all.fn) -Api.tree.expand_all = wrap_node(actions.tree.modifiers.expand_all.fn) + +---@class ApiCollapseOpts +---@field keep_buffers boolean|nil default false + +Api.tree.collapse_all = wrap(actions.tree.modifiers.collapse.all) +Api.tree.expand_all = wrap_node(actions.tree.modifiers.expand.all) Api.tree.toggle_enable_filters = wrap_explorer_member("filters", "toggle") Api.tree.toggle_gitignore_filter = wrap_explorer_member_args("filters", "toggle", "git_ignored") Api.tree.toggle_git_clean_filter = wrap_explorer_member_args("filters", "toggle", "git_clean") @@ -312,6 +316,9 @@ Api.node.navigate.diagnostics.prev_recursive = wrap_node(actions.moves.item.fn({ Api.node.navigate.opened.next = wrap_node(actions.moves.item.fn({ where = "next", what = "opened" })) Api.node.navigate.opened.prev = wrap_node(actions.moves.item.fn({ where = "prev", what = "opened" })) +Api.node.expand = wrap_node(actions.tree.modifiers.expand.node) +Api.node.collapse = wrap_node(actions.tree.modifiers.collapse.node) + ---@class ApiNodeDeleteWipeBufferOpts ---@field force boolean|nil default false diff --git a/lua/nvim-tree/keymap.lua b/lua/nvim-tree/keymap.lua index 94029de5..f1993c49 100644 --- a/lua/nvim-tree/keymap.lua +++ b/lua/nvim-tree/keymap.lua @@ -96,7 +96,7 @@ function M.default_on_attach(bufnr) vim.keymap.set("n", "S", api.tree.search_node, opts("Search")) vim.keymap.set("n", "u", api.fs.rename_full, opts("Rename: Full Path")) vim.keymap.set("n", "U", api.tree.toggle_custom_filter, opts("Toggle Filter: Hidden")) - vim.keymap.set("n", "W", api.tree.collapse_all, opts("Collapse")) + vim.keymap.set("n", "W", api.tree.collapse_all, opts("Collapse All")) vim.keymap.set("n", "x", api.fs.cut, opts("Cut")) vim.keymap.set("n", "y", api.fs.copy.filename, opts("Copy Name")) vim.keymap.set("n", "Y", api.fs.copy.relative_path, opts("Copy Relative Path"))