Skip to content

Commit e25eb7f

Browse files
feat(#2225): add renderer.hidden_display to show a summary of hidden files below the tree (#2856)
* feat(icon_placement): Allow right_align icon_placemente for decorator using ext_marks nvim api * feat(icon_placement): Allow right_align icon_placemente for decorator using ext_marks nvim api feat(icon_placement): Allow right_align icon_placemente for decorator using ext_marks nvim api * feat(icon_placement): consolidate doc * fix: extra namespace added to avoid colision between right_align and full_name features * feat(hidden_display): Allow fine grained rendering of hidden files in a folder * feat(hidden_display): update defaults in Builder to allow rendering * feat(hidden_display): Rename opts function name for the feature * feat(#2349): add "right_align" option for renderer.icons.*_placement (#2846) * feat(icon_placement): Allow right_align icon_placemente for decorator using ext_marks nvim api * feat(icon_placement): Allow right_align icon_placemente for decorator using ext_marks nvim api feat(icon_placement): Allow right_align icon_placemente for decorator using ext_marks nvim api * feat(icon_placement): consolidate doc * fix: extra namespace added to avoid colision between right_align and full_name features * style: rename namespace_id --------- Co-authored-by: Alexander Courtis <[email protected]> * docs: update docs * feat(hidden_display): Simplification and better performance by not sorting and grouping virtual lines * Update doc/nvim-tree-lua.txt Co-authored-by: Alexander Courtis <[email protected]> * style: hidden_stats is better * docs: change to hidden_stats * add separate namespace for virtual lines * help: add highlight group --------- Co-authored-by: Alexander Courtis <[email protected]>
1 parent 48d0e82 commit e25eb7f

File tree

14 files changed

+258
-10
lines changed

14 files changed

+258
-10
lines changed

doc/nvim-tree-lua.txt

+48
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,7 @@ Following is the default configuration. See |nvim-tree-opts| for details.
423423
root_folder_label = ":~:s?$?/..?",
424424
indent_width = 2,
425425
special_files = { "Cargo.toml", "Makefile", "README.md", "readme.md" },
426+
hidden_display = "none",
426427
symlink_destination = true,
427428
highlight_git = "none",
428429
highlight_diagnostics = "none",
@@ -878,6 +879,49 @@ Number of spaces for an each tree nesting level. Minimum 1.
878879
A list of filenames that gets highlighted with `NvimTreeSpecialFile`.
879880
Type: `table`, Default: `{ "Cargo.toml", "Makefile", "README.md", "readme.md", }`
880881

882+
*nvim-tree.renderer.hidden_display*
883+
Show a summary of hidden files below the tree using `NvimTreeHiddenDisplay
884+
Type: `function | string`, Default: `"none"`
885+
886+
Possible string values are:
887+
- `"none"`: Doesn't inform anything about hidden files.
888+
- `"simple"`: Shows how many hidden files are in a folder.
889+
- `"all"`: Shows how many files are hidden and the number of hidden
890+
files per reason why they're hidden.
891+
892+
Example `"all"`:
893+
If a folder has 14 hidden items for various reasons, the display might
894+
show: >
895+
(14 total git: 5, dotfile: 9)
896+
<
897+
If a function is provided, it receives a table `hidden_stats` where keys are
898+
reasons and values are the count of hidden files for that reason.
899+
900+
The `hidden_stats` argument is structured as follows, where <num> is the
901+
number of hidden files related to the field: >
902+
hidden_stats = {
903+
bookmark = <num>,
904+
buf = <num>,
905+
custom = <num>,
906+
dotfile = <num>,
907+
git = <num>,
908+
live_filter = <num>,
909+
}
910+
<
911+
Example of function that can be passed: >
912+
function(hidden_stats)
913+
local total_count = 0
914+
for reason, count in pairs(hidden_stats) do
915+
total_count = total_count + count
916+
end
917+
918+
if total_count > 0 then
919+
return "(" .. tostring(total_count) .. " hidden)"
920+
end
921+
return nil
922+
end
923+
<
924+
881925
*nvim-tree.renderer.symlink_destination*
882926
Whether to show the destination of the symlink.
883927
Type: `boolean`, Default: `true`
@@ -2461,6 +2505,9 @@ Hidden: >
24612505
NvimTreeModifiedFileHL NvimTreeHiddenIcon
24622506
NvimTreeModifiedFolderHL NvimTreeHiddenFileHL
24632507
<
2508+
Hidden Display: >
2509+
NvimTreeHiddenDisplay Conceal
2510+
<
24642511
Opened: >
24652512
NvimTreeOpenedHL Special
24662513
<
@@ -2872,6 +2919,7 @@ highlight group is not, hard linking as follows: >
28722919
|nvim-tree.renderer.add_trailing|
28732920
|nvim-tree.renderer.full_name|
28742921
|nvim-tree.renderer.group_empty|
2922+
|nvim-tree.renderer.hidden_display|
28752923
|nvim-tree.renderer.highlight_bookmarks|
28762924
|nvim-tree.renderer.highlight_clipboard|
28772925
|nvim-tree.renderer.highlight_diagnostics|

lua/nvim-tree.lua

+3
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
398398
root_folder_label = ":~:s?$?/..?",
399399
indent_width = 2,
400400
special_files = { "Cargo.toml", "Makefile", "README.md", "readme.md" },
401+
hidden_display = "none",
401402
symlink_destination = true,
402403
highlight_git = "none",
403404
highlight_diagnostics = "none",
@@ -647,6 +648,7 @@ local ACCEPTED_TYPES = {
647648
},
648649
},
649650
renderer = {
651+
hidden_display = { "function", "string" },
650652
group_empty = { "boolean", "function" },
651653
root_folder_label = { "function", "string", "boolean" },
652654
},
@@ -680,6 +682,7 @@ local ACCEPTED_STRINGS = {
680682
signcolumn = { "yes", "no", "auto" },
681683
},
682684
renderer = {
685+
hidden_display = { "none", "simple", "all" },
683686
highlight_git = { "none", "icon", "name", "all" },
684687
highlight_opened_files = { "none", "icon", "name", "all" },
685688
highlight_modified = { "none", "icon", "name", "all" },

lua/nvim-tree/appearance/init.lua

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ M.HIGHLIGHT_GROUPS = {
1414
-- Standard
1515
{ group = "NvimTreeNormal", link = "Normal" },
1616
{ group = "NvimTreeNormalFloat", link = "NormalFloat" },
17+
{ group = "NvimTreeNormalFloatBorder", link = "FloatBorder" },
1718
{ group = "NvimTreeNormalNC", link = "NvimTreeNormal" },
1819

1920
{ group = "NvimTreeLineNr", link = "LineNr" },
@@ -81,6 +82,9 @@ M.HIGHLIGHT_GROUPS = {
8182
{ group = "NvimTreeHiddenFileHL", link = "NvimTreeHiddenIcon" },
8283
{ group = "NvimTreeHiddenFolderHL", link = "NvimTreeHiddenFileHL" },
8384

85+
-- Hidden Display
86+
{ group = "NvimTreeHiddenDisplay", link = "Conceal" },
87+
8488
-- Opened
8589
{ group = "NvimTreeOpenedHL", link = "Special" },
8690

lua/nvim-tree/enum.lua

+11
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,15 @@ M.ICON_PLACEMENT = {
1919
right_align = 4,
2020
}
2121

22+
---Reason for filter in filter.lua
23+
---@enum FILTER_REASON
24+
M.FILTER_REASON = {
25+
none = 0, -- It's not filtered
26+
git = 1,
27+
buf = 2,
28+
dotfile = 4,
29+
custom = 8,
30+
bookmark = 16,
31+
}
32+
2233
return M

lua/nvim-tree/explorer/explore.lua

+19-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ local git = require "nvim-tree.git"
55
local live_filter = require "nvim-tree.live-filter"
66
local log = require "nvim-tree.log"
77

8+
local FILTER_REASON = require("nvim-tree.enum").FILTER_REASON
89
local Watcher = require "nvim-tree.watcher"
910

1011
local M = {}
@@ -17,7 +18,17 @@ local M = {}
1718
local function populate_children(handle, cwd, node, git_status, parent)
1819
local node_ignored = explorer_node.is_git_ignored(node)
1920
local nodes_by_path = utils.bool_record(node.nodes, "absolute_path")
21+
2022
local filter_status = parent.filters:prepare(git_status)
23+
24+
node.hidden_stats = vim.tbl_deep_extend("force", node.hidden_stats or {}, {
25+
git = 0,
26+
buf = 0,
27+
dotfile = 0,
28+
custom = 0,
29+
bookmark = 0,
30+
})
31+
2132
while true do
2233
local name, t = vim.loop.fs_scandir_next(handle)
2334
if not name then
@@ -29,8 +40,8 @@ local function populate_children(handle, cwd, node, git_status, parent)
2940

3041
---@type uv.fs_stat.result|nil
3142
local stat = vim.loop.fs_stat(abs)
32-
33-
if not parent.filters:should_filter(abs, stat, filter_status) and not nodes_by_path[abs] and Watcher.is_fs_event_capable(abs) then
43+
local filter_reason = parent.filters:should_filter_as_reason(abs, stat, filter_status)
44+
if filter_reason == FILTER_REASON.none and not nodes_by_path[abs] and Watcher.is_fs_event_capable(abs) then
3445
local child = nil
3546
if t == "directory" and vim.loop.fs_access(abs, "R") then
3647
child = builders.folder(node, abs, name, stat)
@@ -47,6 +58,12 @@ local function populate_children(handle, cwd, node, git_status, parent)
4758
nodes_by_path[child.absolute_path] = true
4859
explorer_node.update_git_status(child, node_ignored, git_status)
4960
end
61+
else
62+
for reason, value in pairs(FILTER_REASON) do
63+
if filter_reason == value then
64+
node.hidden_stats[reason] = node.hidden_stats[reason] + 1
65+
end
66+
end
5067
end
5168

5269
log.profile_end(profile)

lua/nvim-tree/explorer/filters.lua

+30
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
local utils = require "nvim-tree.utils"
2+
local FILTER_REASON = require("nvim-tree.enum").FILTER_REASON
23

34
---@class Filters to handle all opts.filters and related API
45
---@field config table hydrated user opts.filters
@@ -223,4 +224,33 @@ function Filters:should_filter(path, fs_stat, status)
223224
or bookmark(self, path, fs_stat and fs_stat.type, status.bookmarks)
224225
end
225226

227+
--- Check if the given path should be filtered, and provide the reason why it was
228+
---@param path string Absolute path
229+
---@param fs_stat uv.fs_stat.result|nil fs_stat of file
230+
---@param status table from prepare
231+
---@return FILTER_REASON
232+
function Filters:should_filter_as_reason(path, fs_stat, status)
233+
if not self.config.enable then
234+
return FILTER_REASON.none
235+
end
236+
237+
if is_excluded(self, path) then
238+
return FILTER_REASON.none
239+
end
240+
241+
if git(self, path, status.git_status) then
242+
return FILTER_REASON.git
243+
elseif buf(self, path, status.bufinfo) then
244+
return FILTER_REASON.buf
245+
elseif dotfile(self, path) then
246+
return FILTER_REASON.dotfile
247+
elseif custom(self, path) then
248+
return FILTER_REASON.custom
249+
elseif bookmark(self, path, fs_stat and fs_stat.type, status.bookmarks) then
250+
return FILTER_REASON.bookmark
251+
else
252+
return FILTER_REASON.none
253+
end
254+
end
255+
226256
return Filters

lua/nvim-tree/explorer/reload.lua

+19-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ local live_filter = require "nvim-tree.live-filter"
55
local git = require "nvim-tree.git"
66
local log = require "nvim-tree.log"
77

8+
local FILTER_REASON = require("nvim-tree.enum").FILTER_REASON
89
local NodeIterator = require "nvim-tree.iterators.node-iterator"
910
local Watcher = require "nvim-tree.watcher"
1011

@@ -92,6 +93,16 @@ function M.reload(node, git_status)
9293
local node_ignored = explorer_node.is_git_ignored(node)
9394
---@type table<string, Node>
9495
local nodes_by_path = utils.key_by(node.nodes, "absolute_path")
96+
97+
-- To reset we must 'zero' everything that we use
98+
node.hidden_stats = vim.tbl_deep_extend("force", node.hidden_stats or {}, {
99+
git = 0,
100+
buf = 0,
101+
dotfile = 0,
102+
custom = 0,
103+
bookmark = 0,
104+
})
105+
95106
while true do
96107
local name, t = vim.loop.fs_scandir_next(handle)
97108
if not name then
@@ -102,7 +113,8 @@ function M.reload(node, git_status)
102113
---@type uv.fs_stat.result|nil
103114
local stat = vim.loop.fs_stat(abs)
104115

105-
if not explorer.filters:should_filter(abs, stat, filter_status) then
116+
local filter_reason = explorer.filters:should_filter_as_reason(abs, stat, filter_status)
117+
if filter_reason == FILTER_REASON.none then
106118
remain_childs[abs] = true
107119

108120
-- Recreate node if type changes.
@@ -139,6 +151,12 @@ function M.reload(node, git_status)
139151
n.fs_stat = stat
140152
end
141153
end
154+
else
155+
for reason, value in pairs(FILTER_REASON) do
156+
if filter_reason == value then
157+
node.hidden_stats[reason] = node.hidden_stats[reason] + 1
158+
end
159+
end
142160
end
143161
end
144162

lua/nvim-tree/live-filter.lua

+13
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,17 @@ local function reset_filter(node_)
1818
return
1919
end
2020

21+
node_.hidden_stats = vim.tbl_deep_extend("force", node_.hidden_stats or {}, {
22+
live_filter = 0,
23+
})
24+
2125
Iterator.builder(node_.nodes)
2226
:hidden()
2327
:applier(function(node)
2428
node.hidden = false
29+
node.hidden_stats = vim.tbl_deep_extend("force", node.hidden_stats or {}, {
30+
live_filter = 0,
31+
})
2532
end)
2633
:iterate()
2734
end
@@ -79,6 +86,10 @@ function M.apply_filter(node_)
7986
local filtered_nodes = 0
8087
local nodes = node.group_next and { node.group_next } or node.nodes
8188

89+
node.hidden_stats = vim.tbl_deep_extend("force", node.hidden_stats or {}, {
90+
live_filter = 0,
91+
})
92+
8293
if nodes then
8394
for _, n in pairs(nodes) do
8495
iterate(n)
@@ -88,6 +99,8 @@ function M.apply_filter(node_)
8899
end
89100
end
90101

102+
node.hidden_stats.live_filter = filtered_nodes
103+
91104
local has_nodes = nodes and (M.always_show_folders or #nodes > filtered_nodes)
92105
local ok, is_match = pcall(matches, node)
93106
node.hidden = not (has_nodes or (ok and is_match))

lua/nvim-tree/node.lua

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
---@field group_next Node|nil
2222
---@field nodes Node[]
2323
---@field open boolean
24+
---@field hidden_stats table -- Each field of this table is a key for source and value for count
2425

2526
---@class FileNode: BaseNode
2627
---@field extension string

0 commit comments

Comments
 (0)