Skip to content

Commit ba4f507

Browse files
committed
docs(#3088): split api pre and post, add more comments
1 parent 6b38f1c commit ba4f507

2 files changed

Lines changed: 345 additions & 0 deletions

File tree

lua/nvim-tree/api/impl/post.lua

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
local core = require("nvim-tree.core")
2+
local view = require("nvim-tree.view")
3+
local utils = require("nvim-tree.utils")
4+
local actions = require("nvim-tree.actions")
5+
local appearance_hi_test = require("nvim-tree.appearance.hi-test")
6+
local help = require("nvim-tree.help")
7+
local keymap = require("nvim-tree.keymap")
8+
9+
local DirectoryNode = require("nvim-tree.node.directory")
10+
local FileNode = require("nvim-tree.node.file")
11+
local FileLinkNode = require("nvim-tree.node.file-link")
12+
local RootNode = require("nvim-tree.node.root")
13+
14+
---Hydrate all implementations barring those that were called during hydrate_pre
15+
---@param api table
16+
local function hydrate_post(api)
17+
---Invoke a method on the singleton explorer.
18+
---Print error when setup not called.
19+
---@param explorer_method string explorer method name
20+
---@return fun(...): any
21+
local function wrap_explorer(explorer_method)
22+
return function(...)
23+
local explorer = core.get_explorer()
24+
if explorer then
25+
return explorer[explorer_method](explorer, ...)
26+
end
27+
end
28+
end
29+
30+
---Inject the node as the first argument if present otherwise do nothing.
31+
---@param fn fun(node: Node, ...): any
32+
---@return fun(node: Node?, ...): any
33+
local function wrap_node(fn)
34+
return function(node, ...)
35+
node = node or wrap_explorer("get_node_at_cursor")()
36+
if node then
37+
return fn(node, ...)
38+
end
39+
end
40+
end
41+
42+
---Inject the node or nil as the first argument if absent.
43+
---@param fn fun(node: Node?, ...): any
44+
---@return fun(node: Node?, ...): any
45+
local function wrap_node_or_nil(fn)
46+
return function(node, ...)
47+
node = node or wrap_explorer("get_node_at_cursor")()
48+
return fn(node, ...)
49+
end
50+
end
51+
52+
---Invoke a member's method on the singleton explorer.
53+
---Print error when setup not called.
54+
---@param explorer_member string explorer member name
55+
---@param member_method string method name to invoke on member
56+
---@param ... any passed to method
57+
---@return fun(...): any
58+
local function wrap_explorer_member_args(explorer_member, member_method, ...)
59+
local method_args = ...
60+
return function(...)
61+
local explorer = core.get_explorer()
62+
if explorer then
63+
return explorer[explorer_member][member_method](explorer[explorer_member], method_args, ...)
64+
end
65+
end
66+
end
67+
68+
---Invoke a member's method on the singleton explorer.
69+
---Print error when setup not called.
70+
---@param explorer_member string explorer member name
71+
---@param member_method string method name to invoke on member
72+
---@return fun(...): any
73+
local function wrap_explorer_member(explorer_member, member_method)
74+
return function(...)
75+
local explorer = core.get_explorer()
76+
if explorer then
77+
return explorer[explorer_member][member_method](explorer[explorer_member], ...)
78+
end
79+
end
80+
end
81+
82+
api.tree.open = actions.tree.open.fn
83+
api.tree.focus = api.tree.open
84+
85+
api.tree.toggle = actions.tree.toggle.fn
86+
api.tree.close = view.close
87+
api.tree.close_in_this_tab = view.close_this_tab_only
88+
api.tree.close_in_all_tabs = view.close_all_tabs
89+
api.tree.reload = wrap_explorer("reload_explorer")
90+
91+
api.tree.resize = actions.tree.resize.fn
92+
93+
api.tree.change_root = require("nvim-tree").change_dir
94+
95+
api.tree.change_root_to_node = wrap_node(function(node)
96+
if node.name == ".." or node:is(RootNode) then
97+
actions.root.change_dir.fn("..")
98+
return
99+
end
100+
101+
if node:is(FileNode) and node.parent ~= nil then
102+
actions.root.change_dir.fn(node.parent:last_group_node().absolute_path)
103+
return
104+
end
105+
106+
if node:is(DirectoryNode) then
107+
actions.root.change_dir.fn(node:last_group_node().absolute_path)
108+
return
109+
end
110+
end)
111+
112+
api.tree.change_root_to_parent = wrap_node(wrap_explorer("dir_up"))
113+
api.tree.get_node_under_cursor = wrap_explorer("get_node_at_cursor")
114+
api.tree.get_nodes = wrap_explorer("get_nodes")
115+
116+
api.tree.find_file = actions.tree.find_file.fn
117+
api.tree.search_node = actions.finders.search_node.fn
118+
119+
api.tree.collapse_all = actions.tree.modifiers.collapse.all
120+
121+
api.tree.expand_all = wrap_node(actions.tree.modifiers.expand.all)
122+
api.tree.toggle_help = help.toggle
123+
api.tree.is_tree_buf = utils.is_nvim_tree_buf
124+
125+
api.tree.is_visible = view.is_visible
126+
127+
api.tree.winid = view.winid
128+
129+
api.fs.create = wrap_node_or_nil(actions.fs.create_file.fn)
130+
api.fs.remove = wrap_node(actions.fs.remove_file.fn)
131+
api.fs.trash = wrap_node(actions.fs.trash.fn)
132+
api.fs.rename_node = wrap_node(actions.fs.rename_file.fn(":t"))
133+
api.fs.rename = wrap_node(actions.fs.rename_file.fn(":t"))
134+
api.fs.rename_sub = wrap_node(actions.fs.rename_file.fn(":p:h"))
135+
api.fs.rename_basename = wrap_node(actions.fs.rename_file.fn(":t:r"))
136+
api.fs.rename_full = wrap_node(actions.fs.rename_file.fn(":p"))
137+
api.fs.cut = wrap_node(wrap_explorer_member("clipboard", "cut"))
138+
api.fs.paste = wrap_node(wrap_explorer_member("clipboard", "paste"))
139+
api.fs.clear_clipboard = wrap_explorer_member("clipboard", "clear_clipboard")
140+
api.fs.print_clipboard = wrap_explorer_member("clipboard", "print_clipboard")
141+
api.fs.copy.node = wrap_node(wrap_explorer_member("clipboard", "copy"))
142+
api.fs.copy.absolute_path = wrap_node(wrap_explorer_member("clipboard", "copy_absolute_path"))
143+
api.fs.copy.filename = wrap_node(wrap_explorer_member("clipboard", "copy_filename"))
144+
api.fs.copy.basename = wrap_node(wrap_explorer_member("clipboard", "copy_basename"))
145+
api.fs.copy.relative_path = wrap_node(wrap_explorer_member("clipboard", "copy_path"))
146+
---
147+
---@class NodeEditOpts
148+
---@field quit_on_open boolean|nil default false
149+
---@field focus boolean|nil default true
150+
151+
---@param mode string
152+
---@param node Node
153+
---@param edit_opts NodeEditOpts?
154+
local function edit(mode, node, edit_opts)
155+
local file_link = node:as(FileLinkNode)
156+
local path = file_link and file_link.link_to or node.absolute_path
157+
local cur_tabpage = vim.api.nvim_get_current_tabpage()
158+
159+
actions.node.open_file.fn(mode, path)
160+
161+
edit_opts = edit_opts or {}
162+
163+
local mode_unsupported_quit_on_open = mode == "drop" or mode == "tab_drop" or mode == "edit_in_place"
164+
if not mode_unsupported_quit_on_open and edit_opts.quit_on_open then
165+
view.close(cur_tabpage)
166+
end
167+
168+
local mode_unsupported_focus = mode == "drop" or mode == "tab_drop" or mode == "edit_in_place"
169+
local focus = edit_opts.focus == nil or edit_opts.focus == true
170+
if not mode_unsupported_focus and not focus then
171+
-- if mode == "tabnew" a new tab will be opened and we need to focus back to the previous tab
172+
if mode == "tabnew" then
173+
vim.cmd(":tabprev")
174+
end
175+
view.focus()
176+
end
177+
end
178+
179+
---@param mode string
180+
---@param toggle_group boolean?
181+
---@return fun(node: Node, edit_opts: NodeEditOpts?)
182+
local function open_or_expand_or_dir_up(mode, toggle_group)
183+
---@param node Node
184+
---@param edit_opts NodeEditOpts?
185+
return function(node, edit_opts)
186+
local root = node:as(RootNode)
187+
local dir = node:as(DirectoryNode)
188+
189+
if root or node.name == ".." then
190+
actions.root.change_dir.fn("..")
191+
elseif dir then
192+
dir:expand_or_collapse(toggle_group)
193+
elseif not toggle_group then
194+
edit(mode, node, edit_opts)
195+
end
196+
end
197+
end
198+
199+
api.node.open.edit = wrap_node(open_or_expand_or_dir_up("edit"))
200+
api.node.open.drop = wrap_node(open_or_expand_or_dir_up("drop"))
201+
api.node.open.tab_drop = wrap_node(open_or_expand_or_dir_up("tab_drop"))
202+
api.node.open.replace_tree_buffer = wrap_node(open_or_expand_or_dir_up("edit_in_place"))
203+
api.node.open.no_window_picker = wrap_node(open_or_expand_or_dir_up("edit_no_picker"))
204+
api.node.open.vertical = wrap_node(open_or_expand_or_dir_up("vsplit"))
205+
api.node.open.vertical_no_picker = wrap_node(open_or_expand_or_dir_up("vsplit_no_picker"))
206+
api.node.open.horizontal = wrap_node(open_or_expand_or_dir_up("split"))
207+
api.node.open.horizontal_no_picker = wrap_node(open_or_expand_or_dir_up("split_no_picker"))
208+
api.node.open.tab = wrap_node(open_or_expand_or_dir_up("tabnew"))
209+
api.node.open.toggle_group_empty = wrap_node(open_or_expand_or_dir_up("toggle_group_empty", true))
210+
api.node.open.preview = wrap_node(open_or_expand_or_dir_up("preview"))
211+
api.node.open.preview_no_picker = wrap_node(open_or_expand_or_dir_up("preview_no_picker"))
212+
213+
api.node.show_info_popup = wrap_node(actions.node.file_popup.toggle_file_info)
214+
api.node.run.cmd = wrap_node(actions.node.run_command.run_file_command)
215+
api.node.run.system = wrap_node(actions.node.system_open.fn)
216+
217+
api.node.navigate.sibling.next = wrap_node(actions.moves.sibling.fn("next"))
218+
api.node.navigate.sibling.prev = wrap_node(actions.moves.sibling.fn("prev"))
219+
api.node.navigate.sibling.first = wrap_node(actions.moves.sibling.fn("first"))
220+
api.node.navigate.sibling.last = wrap_node(actions.moves.sibling.fn("last"))
221+
api.node.navigate.parent = wrap_node(actions.moves.parent.fn(false))
222+
api.node.navigate.parent_close = wrap_node(actions.moves.parent.fn(true))
223+
api.node.navigate.git.next = wrap_node(actions.moves.item.fn({ where = "next", what = "git" }))
224+
api.node.navigate.git.next_skip_gitignored = wrap_node(actions.moves.item.fn({ where = "next", what = "git", skip_gitignored = true }))
225+
api.node.navigate.git.next_recursive = wrap_node(actions.moves.item.fn({ where = "next", what = "git", recurse = true }))
226+
api.node.navigate.git.prev = wrap_node(actions.moves.item.fn({ where = "prev", what = "git" }))
227+
api.node.navigate.git.prev_skip_gitignored = wrap_node(actions.moves.item.fn({ where = "prev", what = "git", skip_gitignored = true }))
228+
api.node.navigate.git.prev_recursive = wrap_node(actions.moves.item.fn({ where = "prev", what = "git", recurse = true }))
229+
api.node.navigate.diagnostics.next = wrap_node(actions.moves.item.fn({ where = "next", what = "diag" }))
230+
api.node.navigate.diagnostics.next_recursive = wrap_node(actions.moves.item.fn({ where = "next", what = "diag", recurse = true }))
231+
api.node.navigate.diagnostics.prev = wrap_node(actions.moves.item.fn({ where = "prev", what = "diag" }))
232+
api.node.navigate.diagnostics.prev_recursive = wrap_node(actions.moves.item.fn({ where = "prev", what = "diag", recurse = true }))
233+
api.node.navigate.opened.next = wrap_node(actions.moves.item.fn({ where = "next", what = "opened" }))
234+
api.node.navigate.opened.prev = wrap_node(actions.moves.item.fn({ where = "prev", what = "opened" }))
235+
236+
api.node.expand = wrap_node(actions.tree.modifiers.expand.node)
237+
api.node.collapse = wrap_node(actions.tree.modifiers.collapse.node)
238+
239+
api.node.buffer.delete = wrap_node(function(node, opts)
240+
actions.node.buffer.delete(node, opts)
241+
end)
242+
api.node.buffer.wipe = wrap_node(function(node, opts)
243+
actions.node.buffer.wipe(node, opts)
244+
end)
245+
246+
api.tree.reload_git = wrap_explorer("reload_git")
247+
248+
api.filter.live.start = wrap_explorer_member("live_filter", "start_filtering")
249+
api.filter.live.clear = wrap_explorer_member("live_filter", "clear_filter")
250+
api.filter.toggle = wrap_explorer_member("filters", "toggle")
251+
api.filter.git.ignored.toggle = wrap_explorer_member_args("filters", "toggle", "git_ignored")
252+
api.filter.git.clean.toggle = wrap_explorer_member_args("filters", "toggle", "git_clean")
253+
api.filter.no_buffer.toggle = wrap_explorer_member_args("filters", "toggle", "no_buffer")
254+
api.filter.custom.toggle = wrap_explorer_member_args("filters", "toggle", "custom")
255+
api.filter.dotfiles.toggle = wrap_explorer_member_args("filters", "toggle", "dotfiles")
256+
api.filter.no_bookmark.toggle = wrap_explorer_member_args("filters", "toggle", "no_bookmark")
257+
258+
api.marks.get = wrap_node(wrap_explorer_member("marks", "get"))
259+
api.marks.list = wrap_explorer_member("marks", "list")
260+
api.marks.toggle = wrap_node(wrap_explorer_member("marks", "toggle"))
261+
api.marks.clear = wrap_explorer_member("marks", "clear")
262+
api.marks.bulk.delete = wrap_explorer_member("marks", "bulk_delete")
263+
api.marks.bulk.trash = wrap_explorer_member("marks", "bulk_trash")
264+
api.marks.bulk.move = wrap_explorer_member("marks", "bulk_move")
265+
api.marks.navigate.next = wrap_explorer_member("marks", "navigate_next")
266+
api.marks.navigate.prev = wrap_explorer_member("marks", "navigate_prev")
267+
api.marks.navigate.select = wrap_explorer_member("marks", "navigate_select")
268+
269+
api.map.get_keymap = keymap.get_keymap
270+
api.map.get_keymap_default = keymap.get_keymap_default
271+
272+
api.health.hi_test = appearance_hi_test
273+
274+
api.commands.get = require("nvim-tree.commands").get
275+
end
276+
277+
---Hydrates all API functions with concrete implementations.
278+
---All "nvim-tree setup not called" error functions will be replaced.
279+
---
280+
---Call this after nvim-tree setup
281+
---
282+
---This is expensive as there are many cascading requires and is avoided
283+
---until after setup has been called, so that the user may require API cheaply.
284+
---@param api table
285+
return function(api)
286+
-- All concrete implementations
287+
hydrate_post(api)
288+
289+
-- (Re)hydrate any legacy by mapping to function set above
290+
require("nvim-tree.legacy").map_api(api)
291+
end

lua/nvim-tree/api/impl/pre.lua

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
local events = require("nvim-tree.events")
2+
local keymap = require("nvim-tree.keymap")
3+
local notify = require("nvim-tree.notify")
4+
5+
local UserDecorator = require("nvim-tree.renderer.decorator.user")
6+
7+
---Walk the api, hydrating all functions with the error notification
8+
---@param t table api root or sub-module
9+
local function hydrate_notify(t)
10+
for k, v in pairs(t) do
11+
if type(v) == "function" then
12+
t[k] = function()
13+
notify.error("nvim-tree setup not called")
14+
end
15+
elseif type(v) == "table" then
16+
hydrate_notify(v)
17+
end
18+
end
19+
end
20+
21+
---Hydrate implementations that may be called pre setup
22+
---@param api table
23+
local function hydrate_pre(api)
24+
api.events.subscribe = events.subscribe
25+
api.events.Event = events.Event
26+
27+
api.map.default_on_attach = keymap.default_on_attach
28+
29+
api.decorator = {}
30+
---Create a decorator class by calling :extend()
31+
---See :help nvim-tree-decorators
32+
---@type nvim_tree.api.decorator.UserDecorator
33+
api.decorator.UserDecorator = UserDecorator --[[@as nvim_tree.api.decorator.UserDecorator]]
34+
end
35+
36+
--Hydrates meta api empty definition functions with a new function:
37+
-- - Default: error notification "nvim-tree setup not called".
38+
-- - Exceptions: concrete implementation for API that can be called before setup.
39+
--
40+
--Call it once when api is first required
41+
--
42+
--This should not include any requires beyond that which is absolutely essential,
43+
--as the user should be able to require api cheaply.
44+
---@param api table
45+
return function(api)
46+
-- Default: error
47+
hydrate_notify(api)
48+
49+
-- Exceptions: may be called
50+
hydrate_pre(api)
51+
52+
-- Hydrate any legacy by mapping to function set above
53+
require("nvim-tree.legacy").map_api(api)
54+
end

0 commit comments

Comments
 (0)