Skip to content

Commit dce16de

Browse files
committed
refactor(#2830): multi instance marks
1 parent ad0b95d commit dce16de

File tree

7 files changed

+165
-63
lines changed

7 files changed

+165
-63
lines changed

lua/nvim-tree.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -840,7 +840,7 @@ function M.setup(conf)
840840
require("nvim-tree.view").setup(opts)
841841
require("nvim-tree.lib").setup(opts)
842842
require("nvim-tree.renderer").setup(opts)
843-
require("nvim-tree.marks").setup(opts)
843+
require("nvim-tree.marks").setup()
844844
require("nvim-tree.buffers").setup(opts)
845845
require("nvim-tree.help").setup(opts)
846846
require("nvim-tree.watcher").setup(opts)

lua/nvim-tree/api.lua

+7-22
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@ local appearance_diagnostics = require "nvim-tree.appearance.diagnostics"
77
local events = require "nvim-tree.events"
88
local help = require "nvim-tree.help"
99
local marks_navigation = require "nvim-tree.marks.navigation"
10-
local marks_bulk_delete = require "nvim-tree.marks.bulk-delete"
11-
local marks_bulk_trash = require "nvim-tree.marks.bulk-trash"
12-
local marks_bulk_move = require "nvim-tree.marks.bulk-move"
1310
local keymap = require "nvim-tree.keymap"
1411
local notify = require "nvim-tree.notify"
1512

@@ -76,18 +73,6 @@ local function wrap_node_or_nil(fn)
7673
end
7774
end
7875

79-
---Inject the explorer as the first argument if present otherwise do nothing.
80-
---@param fn function function to invoke
81-
---@return fun(...) : any
82-
local function wrap_explorer(fn)
83-
return function(...)
84-
local explorer = core.get_explorer()
85-
if explorer then
86-
return fn(explorer, ...)
87-
end
88-
end
89-
end
90-
9176
---Invoke a member's method on the singleton explorer.
9277
---Print error when setup not called.
9378
---@param explorer_member string explorer member name
@@ -267,13 +252,13 @@ Api.events.Event = events.Event
267252
Api.live_filter.start = wrap_explorer_member("live_filter", "start_filtering")
268253
Api.live_filter.clear = wrap_explorer_member("live_filter", "clear_filter")
269254

270-
Api.marks.get = wrap_node(wrap_explorer_member("marks", "get_mark"))
271-
Api.marks.list = wrap_explorer_member("marks", "get_marks")
272-
Api.marks.toggle = wrap_node(wrap_explorer_member("marks", "toggle_mark"))
273-
Api.marks.clear = wrap_explorer_member("marks", "clear_marks")
274-
Api.marks.bulk.delete = wrap_explorer(marks_bulk_delete.bulk_delete)
275-
Api.marks.bulk.trash = wrap_explorer(marks_bulk_trash.bulk_trash)
276-
Api.marks.bulk.move = wrap_explorer(marks_bulk_move.bulk_move)
255+
Api.marks.get = wrap_node(wrap_explorer_member("marks", "get"))
256+
Api.marks.list = wrap_explorer_member("marks", "list")
257+
Api.marks.toggle = wrap_node(wrap_explorer_member("marks", "toggle"))
258+
Api.marks.clear = wrap_explorer_member("marks", "clear")
259+
Api.marks.bulk.delete = wrap_explorer_member("marks", "delete")
260+
Api.marks.bulk.trash = wrap_explorer_member("marks", "trash")
261+
Api.marks.bulk.move = wrap_explorer_member("marks", "move")
277262
Api.marks.navigate.next = wrap(marks_navigation.next)
278263
Api.marks.navigate.prev = wrap(marks_navigation.prev)
279264
Api.marks.navigate.select = wrap(marks_navigation.select)

lua/nvim-tree/explorer/filters.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ function Filters:prepare(git_status)
194194

195195
local explorer = require("nvim-tree.core").get_explorer()
196196
if explorer then
197-
for _, node in pairs(explorer.marks:get_marks()) do
197+
for _, node in pairs(explorer.marks:list()) do
198198
status.bookmarks[node.absolute_path] = node.type
199199
end
200200
end

lua/nvim-tree/explorer/init.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@ function Explorer.new(path)
4444
absolute_path = path,
4545
nodes = {},
4646
open = true,
47-
marks = Marks:new(),
4847
sorters = Sorters:new(M.config),
4948
}, Explorer)
5049
explorer.watcher = watch.create_watcher(explorer)
5150
explorer.filters = Filters:new(M.config, explorer)
5251
explorer.live_filter = LiveFilter:new(M.config, explorer)
52+
explorer.marks = Marks:new(M.config, explorer)
5353
explorer:_load(explorer)
5454
return explorer
5555
end

lua/nvim-tree/marks/init.lua

+150-33
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,195 @@
1+
local core = {} -- circular dependency
2+
local lib = {} -- circular dependency
3+
local notify = require "nvim-tree.notify"
4+
local remove_file = {} -- circular dependency
5+
local rename_file = {} -- circular dependency
6+
local trash = {} -- circular dependency
17
local renderer = {} -- circular dependency
8+
local utils = require "nvim-tree.utils"
29

310
---@class Marks
4-
---@field private marks Node[]
11+
---@field config table hydrated user opts.filters
12+
---@field private explorer Explorer
13+
---@field private marks table<string, Node> by absolute path
514
local Marks = {}
615

716
---@return Marks
8-
function Marks:new()
9-
local o = {}
17+
---@param opts table user options
18+
---@param explorer Explorer
19+
function Marks:new(opts, explorer)
20+
local o = {
21+
explorer = explorer,
22+
config = {
23+
ui = opts.ui,
24+
filesystem_watchers = opts.filesystem_watchers,
25+
},
26+
marks = {},
27+
}
28+
1029
setmetatable(o, self)
1130
self.__index = self
12-
13-
o.marks = {}
14-
1531
return o
1632
end
1733

34+
---Clear all marks and reload if watchers disabled
1835
---@private
19-
---@param node Node
20-
function Marks:add_mark(node)
21-
self.marks[node.absolute_path] = node
22-
23-
renderer.draw()
36+
function Marks:clear_reload()
37+
self:clear()
38+
if not self.config.filesystem_watchers.enable then
39+
require("nvim-tree.actions.reloaders").reload_explorer()
40+
end
2441
end
2542

26-
---@private
27-
---@param node Node
28-
function Marks:remove_mark(node)
29-
self.marks[node.absolute_path] = nil
30-
43+
---Clear all marks and redraw
44+
---@public
45+
function Marks:clear()
46+
self.marks = {}
3147
renderer.draw()
3248
end
3349

50+
---@public
3451
---@param node Node
35-
function Marks:toggle_mark(node)
52+
function Marks:toggle(node)
3653
if node.absolute_path == nil then
3754
return
3855
end
3956

40-
if self:get_mark(node) then
41-
self:remove_mark(node)
57+
if self:get(node) then
58+
self.marks[node.absolute_path] = nil
4259
else
43-
self:add_mark(node)
60+
self.marks[node.absolute_path] = node
4461
end
4562

4663
renderer.draw()
4764
end
4865

49-
function Marks:clear_marks()
50-
self.marks = {}
51-
52-
renderer.draw()
53-
end
54-
66+
---Return node if marked
67+
---@public
5568
---@param node Node
5669
---@return Node|nil
57-
function Marks:get_mark(node)
70+
function Marks:get(node)
5871
return node and self.marks[node.absolute_path]
5972
end
6073

74+
---List marked nodes
75+
---@public
6176
---@return Node[]
62-
function Marks:get_marks()
77+
function Marks:list()
6378
local list = {}
6479
for _, node in pairs(self.marks) do
6580
table.insert(list, node)
6681
end
6782
return list
6883
end
6984

70-
function Marks.setup(opts)
71-
renderer = require "nvim-tree.renderer"
85+
---Delete marked; each removal will be optionally notified
86+
---@public
87+
function Marks:delete()
88+
if not next(self.marks) then
89+
notify.warn "No bookmarks to delete."
90+
return
91+
end
7292

73-
require("nvim-tree.marks.bulk-delete").setup(opts)
74-
require("nvim-tree.marks.bulk-trash").setup(opts)
75-
require("nvim-tree.marks.bulk-move").setup(opts)
93+
local function execute()
94+
for _, node in pairs(self.marks) do
95+
remove_file.remove(node)
96+
end
97+
self:clear_reload()
98+
end
99+
100+
if self.config.ui.confirm.remove then
101+
local prompt_select = "Remove bookmarked ?"
102+
local prompt_input = prompt_select .. " y/N: "
103+
lib.prompt(prompt_input, prompt_select, { "", "y" }, { "No", "Yes" }, "nvimtree_bulk_delete", function(item_short)
104+
utils.clear_prompt()
105+
if item_short == "y" then
106+
execute()
107+
end
108+
end)
109+
else
110+
execute()
111+
end
112+
end
113+
114+
---Trash marked; each removal will be optionally notified
115+
---@public
116+
function Marks:trash()
117+
if not next(self.marks) then
118+
notify.warn "No bookmarks to trash."
119+
return
120+
end
121+
122+
local function execute()
123+
for _, node in pairs(self.marks) do
124+
trash.remove(node)
125+
end
126+
self:clear_reload()
127+
end
128+
129+
if self.config.ui.confirm.trash then
130+
local prompt_select = "Trash bookmarked ?"
131+
local prompt_input = prompt_select .. " y/N: "
132+
lib.prompt(prompt_input, prompt_select, { "", "y" }, { "No", "Yes" }, "nvimtree_bulk_trash", function(item_short)
133+
utils.clear_prompt()
134+
if item_short == "y" then
135+
execute()
136+
end
137+
end)
138+
else
139+
execute()
140+
end
141+
end
142+
143+
---Move marked
144+
---@public
145+
function Marks:move()
146+
if not next(self.marks) then
147+
notify.warn "No bookmarks to move."
148+
return
149+
end
150+
151+
local node_at_cursor = lib.get_node_at_cursor()
152+
local default_path = core.get_cwd()
153+
154+
if node_at_cursor and node_at_cursor.type == "directory" then
155+
default_path = node_at_cursor.absolute_path
156+
elseif node_at_cursor and node_at_cursor.parent then
157+
default_path = node_at_cursor.parent.absolute_path
158+
end
159+
160+
local input_opts = {
161+
prompt = "Move to: ",
162+
default = default_path,
163+
completion = "dir",
164+
}
165+
166+
vim.ui.input(input_opts, function(location)
167+
utils.clear_prompt()
168+
if not location or location == "" then
169+
return
170+
end
171+
if vim.fn.filewritable(location) ~= 2 then
172+
notify.warn(location .. " is not writable, cannot move.")
173+
return
174+
end
175+
176+
for _, node in pairs(self.marks) do
177+
local head = vim.fn.fnamemodify(node.absolute_path, ":t")
178+
local to = utils.path_join { location, head }
179+
rename_file.rename(node, to)
180+
end
181+
182+
self:clear_reload()
183+
end)
184+
end
185+
186+
function Marks.setup()
187+
core = require "nvim-tree.core"
188+
lib = require "nvim-tree.lib"
189+
remove_file = require "nvim-tree.actions.fs.remove-file"
190+
rename_file = require "nvim-tree.actions.fs.rename-file"
191+
renderer = require "nvim-tree.renderer"
192+
trash = require "nvim-tree.actions.fs.trash"
76193
end
77194

78195
return Marks

lua/nvim-tree/marks/navigation.lua

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ local function get_nearest(node, where)
2626
return
2727
end
2828

29-
if not explorer.marks:get_mark(n) then
29+
if not explorer.marks:get(n) then
3030
return
3131
end
3232

@@ -95,15 +95,15 @@ function M.select()
9595

9696
local list = vim.tbl_map(function(n)
9797
return n.absolute_path
98-
end, explorer.marks:get_marks())
98+
end, explorer.marks:list())
9999

100100
vim.ui.select(list, {
101101
prompt = "Select a file to open or a folder to focus",
102102
}, function(choice)
103103
if not choice or choice == "" then
104104
return
105105
end
106-
local node = explorer.marks:get_mark { absolute_path = choice }
106+
local node = explorer.marks:get { absolute_path = choice }
107107
open_or_focus(node)
108108
end)
109109
end

lua/nvim-tree/renderer/decorator/bookmarks.lua

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ end
3434
---@param node Node
3535
---@return HighlightedString[]|nil icons
3636
function DecoratorBookmarks:calculate_icons(node)
37-
if core.get_explorer() and core.get_explorer().marks:get_mark(node) then
37+
if core.get_explorer() and core.get_explorer().marks:get(node) then
3838
return { self.icon }
3939
end
4040
end
@@ -43,7 +43,7 @@ end
4343
---@param node Node
4444
---@return string|nil group
4545
function DecoratorBookmarks:calculate_highlight(node)
46-
if self.hl_pos ~= HL_POSITION.none and core.get_explorer() and core.get_explorer().marks:get_mark(node) then
46+
if self.hl_pos ~= HL_POSITION.none and core.get_explorer() and core.get_explorer().marks:get(node) then
4747
return "NvimTreeBookmarkHL"
4848
end
4949
end

0 commit comments

Comments
 (0)