Skip to content

Commit e5c8b01

Browse files
committed
feat(#2827): Multi Instance: Refactor: nvim-tree.live-filter
1 parent 4e396b2 commit e5c8b01

File tree

3 files changed

+210
-2
lines changed

3 files changed

+210
-2
lines changed

lua/nvim-tree/core.lua

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
local events = require "nvim-tree.events"
22
local explorer = require "nvim-tree.explorer"
3-
local live_filter = require "nvim-tree.live-filter"
43
local view = require "nvim-tree.view"
54
local log = require "nvim-tree.log"
65

@@ -45,7 +44,7 @@ function M.get_nodes_starting_line()
4544
if view.is_root_folder_visible(M.get_cwd()) then
4645
offset = offset + 1
4746
end
48-
if live_filter.filter then
47+
if TreeExplorer and TreeExplorer.live_filter.filter then
4948
return offset + 1
5049
end
5150
return offset

lua/nvim-tree/explorer/init.lua

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ local notify = require "nvim-tree.notify"
33
local watch = require "nvim-tree.explorer.watch"
44
local explorer_node = require "nvim-tree.explorer.node"
55
local Marks = require "nvim-tree.marks"
6+
local LiveFilter = require "nvim-tree.live-filter"
67

78
local M = {}
89

@@ -38,6 +39,7 @@ function Explorer.new(path)
3839
absolute_path = path,
3940
nodes = {},
4041
open = true,
42+
live_filter = LiveFilter:new(M.config),
4143
marks = Marks:new(),
4244
}, Explorer)
4345
explorer.watcher = watch.create_watcher(explorer)
+207
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
local view = require "nvim-tree.view"
2+
local utils = require "nvim-tree.utils"
3+
local Iterator = require "nvim-tree.iterators.node-iterator"
4+
local filters = require "nvim-tree.explorer.filters"
5+
6+
local LiveFilter = {}
7+
8+
function LiveFilter:new(opts)
9+
local o = {}
10+
setmetatable(o, self)
11+
self.__index = self
12+
o.config = vim.deepcopy(opts.live_filter)
13+
o.filter = nil
14+
return o
15+
end
16+
17+
local function redraw()
18+
require("nvim-tree.renderer").draw()
19+
end
20+
21+
---@param node_ Node|nil
22+
local function reset_filter(node_)
23+
node_ = node_ or require("nvim-tree.core").get_explorer()
24+
25+
if node_ == nil then
26+
return
27+
end
28+
29+
Iterator.builder(node_.nodes)
30+
:hidden()
31+
:applier(function(node)
32+
node.hidden = false
33+
end)
34+
:iterate()
35+
end
36+
37+
local overlay_bufnr = 0
38+
local overlay_winnr = 0
39+
40+
local function remove_overlay()
41+
if view.View.float.enable and view.View.float.quit_on_focus_loss then
42+
-- return to normal nvim-tree float behaviour when filter window is closed
43+
vim.api.nvim_create_autocmd("WinLeave", {
44+
pattern = "NvimTree_*",
45+
group = vim.api.nvim_create_augroup("NvimTree", { clear = false }),
46+
callback = function()
47+
if utils.is_nvim_tree_buf(0) then
48+
view.close()
49+
end
50+
end,
51+
})
52+
end
53+
54+
vim.api.nvim_win_close(overlay_winnr, true)
55+
vim.api.nvim_buf_delete(overlay_bufnr, { force = true })
56+
overlay_bufnr = 0
57+
overlay_winnr = 0
58+
59+
local explorer = require("nvim-tree.core").get_explorer()
60+
if explorer and explorer.live_filter.filter == "" then
61+
explorer.live_filter.clear_filter()
62+
end
63+
end
64+
65+
---@param node Node
66+
---@return boolean
67+
local function matches(self, node)
68+
if not filters.config.enable then
69+
return true
70+
end
71+
72+
local path = node.absolute_path
73+
local name = vim.fn.fnamemodify(path, ":t")
74+
return vim.regex(self.filter):match_str(name) ~= nil
75+
end
76+
77+
---@param node_ Node|nil
78+
function LiveFilter:apply_filter(node_)
79+
if not self.filter or self.filter == "" then
80+
reset_filter(node_)
81+
return
82+
end
83+
84+
-- TODO(kiyan): this iterator cannot yet be refactored with the Iterator module
85+
-- since the node mapper is based on its children
86+
local function iterate(node)
87+
local filtered_nodes = 0
88+
local nodes = node.group_next and { node.group_next } or node.nodes
89+
90+
if nodes then
91+
for _, n in pairs(nodes) do
92+
iterate(n)
93+
if n.hidden then
94+
filtered_nodes = filtered_nodes + 1
95+
end
96+
end
97+
end
98+
99+
local has_nodes = nodes and (self.config.always_show_folders or #nodes > filtered_nodes)
100+
local ok, is_match = pcall(matches, node)
101+
node.hidden = not (has_nodes or (ok and is_match))
102+
end
103+
104+
iterate(node_ or require("nvim-tree.core").get_explorer())
105+
end
106+
107+
local function record_char()
108+
vim.schedule(function()
109+
local explorer = require("nvim-tree.core").get_explorer()
110+
if explorer then
111+
explorer.live_filter.filter = vim.api.nvim_buf_get_lines(overlay_bufnr, 0, -1, false)[1]
112+
explorer.live_filter.apply_filter()
113+
redraw()
114+
end
115+
end)
116+
end
117+
118+
local function configure_buffer_overlay()
119+
overlay_bufnr = vim.api.nvim_create_buf(false, true)
120+
121+
vim.api.nvim_buf_attach(overlay_bufnr, true, {
122+
on_lines = record_char,
123+
})
124+
125+
vim.api.nvim_create_autocmd("InsertLeave", {
126+
callback = remove_overlay,
127+
once = true,
128+
})
129+
130+
vim.api.nvim_buf_set_keymap(overlay_bufnr, "i", "<CR>", "<cmd>stopinsert<CR>", {})
131+
end
132+
133+
---@return integer
134+
local function calculate_overlay_win_width(explorer)
135+
local wininfo = vim.fn.getwininfo(view.get_winnr())[1]
136+
137+
if wininfo then
138+
return wininfo.width - wininfo.textoff - #explorer.live_filter.prefix
139+
end
140+
141+
return 20
142+
end
143+
144+
local function create_overlay()
145+
local explorer = require("nvim-tree.core").get_explorer()
146+
if not explorer then
147+
return
148+
end
149+
if view.View.float.enable then
150+
-- don't close nvim-tree float when focus is changed to filter window
151+
vim.api.nvim_clear_autocmds {
152+
event = "WinLeave",
153+
pattern = "NvimTree_*",
154+
group = vim.api.nvim_create_augroup("NvimTree", { clear = false }),
155+
}
156+
end
157+
158+
configure_buffer_overlay()
159+
overlay_winnr = vim.api.nvim_open_win(overlay_bufnr, true, {
160+
col = 1,
161+
row = 0,
162+
relative = "cursor",
163+
width = calculate_overlay_win_width(explorer),
164+
height = 1,
165+
border = "none",
166+
style = "minimal",
167+
})
168+
169+
if vim.fn.has "nvim-0.10" == 1 then
170+
vim.api.nvim_set_option_value("modifiable", true, { buf = overlay_bufnr })
171+
else
172+
vim.api.nvim_buf_set_option(overlay_bufnr, "modifiable", true) ---@diagnostic disable-line: deprecated
173+
end
174+
175+
vim.api.nvim_buf_set_lines(overlay_bufnr, 0, -1, false, { explorer.live_filter.filter })
176+
vim.cmd "startinsert"
177+
vim.api.nvim_win_set_cursor(overlay_winnr, { 1, #explorer.live_filter.filter + 1 })
178+
end
179+
180+
function LiveFilter:start_filtering()
181+
view.View.live_filter.prev_focused_node = require("nvim-tree.lib").get_node_at_cursor()
182+
self.filter = self.filter or ""
183+
184+
redraw()
185+
local row = require("nvim-tree.core").get_nodes_starting_line() - 1
186+
local col = #self.prefix > 0 and #self.prefix - 1 or 1
187+
view.set_cursor { row, col }
188+
-- needs scheduling to let the cursor move before initializing the window
189+
vim.schedule(create_overlay)
190+
end
191+
192+
function LiveFilter:clear_filter()
193+
local node = require("nvim-tree.lib").get_node_at_cursor()
194+
local last_node = view.View.live_filter.prev_focused_node
195+
196+
self.filter = nil
197+
reset_filter()
198+
redraw()
199+
200+
if node then
201+
utils.focus_file(node.absolute_path)
202+
elseif last_node then
203+
utils.focus_file(last_node.absolute_path)
204+
end
205+
end
206+
207+
return LiveFilter

0 commit comments

Comments
 (0)