Skip to content

Commit 04655a2

Browse files
committed
feat(live-filter): add ability to live filter out nodes in the tree
1 parent 08c5766 commit 04655a2

File tree

2 files changed

+129
-1
lines changed

2 files changed

+129
-1
lines changed

lua/nvim-tree/live-filter.lua

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
-- TODO
2+
-- getting the node list should apply the filter first (for get_node_at_cursor for instance)
3+
-- custom configuration
4+
-- placement bug with window (see todo/fixme)
5+
-- documentation
6+
-- more advanced matching algorithm
7+
-- asynchronous node discovery when folders are not yet loaded: (might involve performance issues -> max depth search could fix that)
8+
local a = vim.api
9+
10+
local view = require "nvim-tree.view"
11+
12+
local M = {
13+
filter = nil,
14+
prefix = "Filtering on: ",
15+
}
16+
17+
local function redraw()
18+
require("nvim-tree.renderer").draw()
19+
end
20+
21+
function M.cancel_filter()
22+
M.filter = nil
23+
M.remove_overlay()
24+
vim.cmd "stopinsert"
25+
redraw()
26+
end
27+
28+
local overlay_bufnr = nil
29+
local overlay_winnr = nil
30+
31+
function M.remove_overlay()
32+
vim.cmd "augroup NvimTreeRecordFilter"
33+
vim.cmd "au!"
34+
vim.cmd "augroup END"
35+
36+
a.nvim_win_close(overlay_winnr, { force = true })
37+
overlay_bufnr = nil
38+
overlay_winnr = nil
39+
end
40+
41+
local function record_char()
42+
vim.schedule(function()
43+
M.filter = a.nvim_buf_get_lines(overlay_bufnr, 0, -1, false)[1]
44+
redraw()
45+
end)
46+
end
47+
48+
local function configure_buffer_overloay()
49+
overlay_bufnr = a.nvim_create_buf(false, true)
50+
51+
a.nvim_buf_attach(overlay_bufnr, true, {
52+
on_lines = record_char,
53+
})
54+
vim.cmd "augroup NvimTreeRecordFilter"
55+
vim.cmd "au!"
56+
vim.cmd "au InsertLeave * lua require'nvim-tree.live-filter'.remove_overlay()"
57+
vim.cmd "augroup END"
58+
59+
a.nvim_buf_set_keymap(overlay_bufnr, "i", "<CR>", "<cmd>stopinsert<CR>", {})
60+
a.nvim_buf_set_keymap(overlay_bufnr, "i", "<Esc>", "<cmd>lua require'nvim-tree.live-filter'.cancel_filter()<cr>", {})
61+
a.nvim_buf_set_keymap(overlay_bufnr, "i", "<C-c>", "<cmd>lua require'nvim-tree.live-filter'.cancel_filter()<cr>", {})
62+
end
63+
64+
-- TODO/FIXME: find why it doesn't properly place the column when the screen is split...
65+
local function create_overlay(row, col)
66+
configure_buffer_overloay()
67+
overlay_winnr = a.nvim_open_win(overlay_bufnr, true, {
68+
col = col + 2,
69+
row = row,
70+
relative = "win",
71+
anchor = "NW",
72+
win = view.get_winnr(),
73+
width = 100,
74+
height = 1,
75+
border = "none",
76+
style = "minimal",
77+
})
78+
a.nvim_buf_set_option(overlay_bufnr, "modifiable", true)
79+
a.nvim_buf_set_lines(overlay_bufnr, 0, -1, false, { M.filter })
80+
vim.cmd "startinsert"
81+
a.nvim_win_set_cursor(overlay_winnr, { 1, #M.filter + 1 })
82+
end
83+
84+
function M.start_filtering()
85+
M.filter = M.filter or ""
86+
87+
redraw()
88+
local row = view.hide_root_folder or TreeExplorer.cwd == "/" and 1 or 2
89+
a.nvim_win_set_cursor(view.get_winnr(), { row, #M.prefix })
90+
create_overlay(row - 1, #M.prefix)
91+
end
92+
93+
local function matches(node, filter)
94+
local path = node.cwd or node.link_to or node.absolute_path
95+
local name = require("nvim-tree.utils").path_relative(path, vim.loop.cwd())
96+
return vim.fn.match(name, filter) ~= -1
97+
end
98+
99+
function M.filter_nodes()
100+
if not M.filter or M.filter == "" then
101+
return TreeExplorer
102+
end
103+
104+
local function iterate(node)
105+
if node.nodes then
106+
node.nodes = vim.tbl_filter(function(_node)
107+
return iterate(_node) ~= nil
108+
end, node.nodes)
109+
end
110+
111+
local has_nodes = node.nodes and #node.nodes > 0
112+
if has_nodes or matches(node, M.filter) then
113+
return node
114+
end
115+
116+
return nil
117+
end
118+
119+
return iterate(vim.deepcopy(TreeExplorer)) or { cwd = TreeExplorer.cwd, nodes = {} }
120+
end
121+
122+
return M

lua/nvim-tree/renderer/init.lua

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ local view = require "nvim-tree.view"
33
local _padding = require "nvim-tree.renderer.padding"
44
local _help = require "nvim-tree.renderer.help"
55
local _icons = require "nvim-tree.renderer.icons"
6+
local live_filter = require "nvim-tree.live-filter"
67

78
local api = vim.api
89

@@ -306,6 +307,10 @@ local function update_draw_data(tree, depth, markers)
306307
table.insert(hl, { "NvimTreeRootFolder", index, 0, string.len(root_name) })
307308
index = 1
308309
end
310+
if tree.cwd and live_filter.filter then
311+
table.insert(lines, live_filter.prefix .. live_filter.filter)
312+
index = index + 1
313+
end
309314

310315
for idx, node in ipairs(tree.nodes) do
311316
local padding = _padding.get_padding(depth, idx, tree, node, markers)
@@ -419,7 +424,8 @@ function M.draw()
419424
and icon_state.show_folder_icon
420425
and icon_state.show_folder_arrows
421426
_padding.reload_padding_function()
422-
update_draw_data(TreeExplorer, show_arrows and 2 or 0, {})
427+
local nodes = require("nvim-tree.live-filter").filter_nodes()
428+
update_draw_data(nodes, show_arrows and 2 or 0, {})
423429

424430
if view.is_help_ui() then
425431
lines, hl = _help.compute_lines()

0 commit comments

Comments
 (0)