local core = require("nvim-tree.core") local utils = require("nvim-tree.utils") local actions = require("nvim-tree.actions") local appearance_hi_test = require("nvim-tree.appearance.hi-test") local events = require("nvim-tree.events") local help = require("nvim-tree.help") local keymap = require("nvim-tree.keymap") local notify = require("nvim-tree.notify") local DirectoryNode = require("nvim-tree.node.directory") local FileLinkNode = require("nvim-tree.node.file-link") local RootNode = require("nvim-tree.node.root") local UserDecorator = require("nvim-tree.renderer.decorator.user") local Api = { tree = {}, node = { navigate = { sibling = {}, git = {}, diagnostics = {}, opened = {}, }, run = {}, open = {}, buffer = {}, }, events = {}, marks = { bulk = {}, navigate = {}, }, fs = { copy = {}, }, git = {}, live_filter = {}, config = { mappings = {}, }, commands = {}, diagnostics = {}, decorator = {}, } ---Print error when setup not called. ---@param fn fun(...): any ---@return fun(...): any local function wrap(fn) return function(...) if vim.g.NvimTreeSetup == 1 then return fn(...) else notify.error("nvim-tree setup not called") end end end ---Invoke a method on the singleton explorer. ---Print error when setup not called. ---@param explorer_method string explorer method name ---@return fun(...): any local function wrap_explorer(explorer_method) return wrap(function(...) local explorer = core.get_explorer() if explorer then return explorer[explorer_method](explorer, ...) end end) end ---Inject the node as the first argument if present otherwise do nothing. ---@param fn fun(node: Node, ...): any ---@return fun(node: Node?, ...): any local function wrap_node(fn) return function(node, ...) node = node or wrap_explorer("get_node_at_cursor")() if node then return fn(node, ...) end end end ---Inject the node or nil as the first argument if absent. ---@param fn fun(node: Node?, ...): any ---@return fun(node: Node?, ...): any local function wrap_node_or_nil(fn) return function(node, ...) node = node or wrap_explorer("get_node_at_cursor")() return fn(node, ...) end end ---Invoke a member's method on the singleton explorer. ---Print error when setup not called. ---@param explorer_member string explorer member name ---@param member_method string method name to invoke on member ---@param ... any passed to method ---@return fun(...): any local function wrap_explorer_member_args(explorer_member, member_method, ...) local method_args = ... return wrap(function(...) local explorer = core.get_explorer() if explorer then return explorer[explorer_member][member_method](explorer[explorer_member], method_args, ...) end end) end ---Invoke a member's method on the singleton explorer. ---Print error when setup not called. ---@param explorer_member string explorer member name ---@param member_method string method name to invoke on member ---@return fun(...): any local function wrap_explorer_member(explorer_member, member_method) return wrap(function(...) local explorer = core.get_explorer() if explorer then return explorer[explorer_member][member_method](explorer[explorer_member], ...) end end) end --- ---@class NodeEditOpts ---@field quit_on_open boolean|nil default false ---@field focus boolean|nil default true ---@param mode string ---@param node Node ---@param edit_opts NodeEditOpts? local function edit(mode, node, edit_opts) local file_link = node:as(FileLinkNode) local path = file_link and file_link.link_to or node.absolute_path local cur_tabpage = vim.api.nvim_get_current_tabpage() local explorer = core.get_explorer() actions.node.open_file.fn(mode, path) edit_opts = edit_opts or {} local mode_unsupported_quit_on_open = mode == "drop" or mode == "tab_drop" or mode == "edit_in_place" if not mode_unsupported_quit_on_open and edit_opts.quit_on_open then if explorer then explorer.view:close(cur_tabpage, "api.edit " .. mode) end end local mode_unsupported_focus = mode == "drop" or mode == "tab_drop" or mode == "edit_in_place" local focus = edit_opts.focus == nil or edit_opts.focus == true if not mode_unsupported_focus and not focus then -- if mode == "tabnew" a new tab will be opened and we need to focus back to the previous tab if mode == "tabnew" then vim.cmd(":tabprev") end if explorer then explorer.view:focus() end end end ---@class ApiTreeOpenOpts ---@field path string|nil path ---@field current_window boolean|nil default false ---@field winid number|nil ---@field find_file boolean|nil default false ---@field update_root boolean|nil default false Api.tree.open = wrap(actions.tree.open.fn) Api.tree.focus = Api.tree.open ---@class ApiTreeToggleOpts ---@field path string|nil ---@field current_window boolean|nil default false ---@field winid number|nil ---@field find_file boolean|nil default false ---@field update_root boolean|nil default false ---@field focus boolean|nil default true Api.tree.toggle = wrap(actions.tree.toggle.fn) Api.tree.close = wrap_explorer_member("view", "close") Api.tree.close_in_this_tab = wrap_explorer_member("view", "close_this_tab_only") Api.tree.close_in_all_tabs = wrap_explorer_member("view", "close_all_tabs") Api.tree.reload = wrap_explorer("reload_explorer") ---@class ApiTreeResizeOpts ---@field width string|function|number|table|nil ---@field absolute number|nil ---@field relative number|nil Api.tree.resize = wrap(actions.tree.resize.fn) Api.tree.change_root = wrap(function(...) require("nvim-tree").change_dir(...) end) Api.tree.change_root_to_node = wrap_node(function(node) if node.name == ".." or node:is(RootNode) then actions.root.change_dir.fn("..") else node = node:as(DirectoryNode) if node then actions.root.change_dir.fn(node:last_group_node().absolute_path) end end end) Api.tree.change_root_to_parent = wrap_node(actions.root.dir_up.fn) Api.tree.get_node_under_cursor = wrap_explorer("get_node_at_cursor") Api.tree.get_nodes = wrap_explorer("get_nodes") ---@class ApiTreeFindFileOpts ---@field buf string|number|nil ---@field open boolean|nil default false ---@field current_window boolean|nil default false ---@field winid number|nil ---@field update_root boolean|nil default false ---@field focus boolean|nil default false Api.tree.find_file = wrap(actions.tree.find_file.fn) Api.tree.search_node = wrap(actions.finders.search_node.fn) ---@class ApiCollapseOpts ---@field keep_buffers boolean|nil default false Api.tree.collapse_all = wrap(actions.tree.modifiers.collapse.all) ---@class ApiTreeExpandAllOpts ---@field descend_until (fun(expansion_count: integer, node: Node): boolean)|nil Api.tree.expand_all = wrap_node(actions.tree.modifiers.expand.all) Api.tree.toggle_descend_until = wrap_node(function(node, descend_until) local dir = node:as(DirectoryNode) if dir then if node.open then dir:expand_or_collapse(nil) else actions.tree.modifiers.expand.all(node, { descend_until = descend_until }) end else edit("edit", node) end end) Api.tree.toggle_enable_filters = wrap_explorer_member("filters", "toggle") Api.tree.toggle_gitignore_filter = wrap_explorer_member_args("filters", "toggle", "git_ignored") Api.tree.toggle_git_clean_filter = wrap_explorer_member_args("filters", "toggle", "git_clean") Api.tree.toggle_no_buffer_filter = wrap_explorer_member_args("filters", "toggle", "no_buffer") Api.tree.toggle_custom_filter = wrap_explorer_member_args("filters", "toggle", "custom") Api.tree.toggle_hidden_filter = wrap_explorer_member_args("filters", "toggle", "dotfiles") Api.tree.toggle_no_bookmark_filter = wrap_explorer_member_args("filters", "toggle", "no_bookmark") Api.tree.toggle_help = wrap(help.toggle) Api.tree.is_tree_buf = wrap(utils.is_nvim_tree_buf) ---@class ApiTreeIsVisibleOpts ---@field tabpage number|nil ---@field any_tabpage boolean|nil default false Api.tree.is_visible = wrap_explorer_member("view", "is_visible") ---@class ApiTreeWinIdOpts ---@field tabpage number|nil default nil Api.tree.winid = wrap_explorer_member("view", "api_winid") Api.fs.create = wrap_node_or_nil(actions.fs.create_file.fn) Api.fs.remove = wrap_node(actions.fs.remove_file.fn) Api.fs.trash = wrap_node(actions.fs.trash.fn) Api.fs.rename_node = wrap_node(actions.fs.rename_file.fn(":t")) Api.fs.rename = wrap_node(actions.fs.rename_file.fn(":t")) Api.fs.rename_sub = wrap_node(actions.fs.rename_file.fn(":p:h")) Api.fs.rename_basename = wrap_node(actions.fs.rename_file.fn(":t:r")) Api.fs.rename_full = wrap_node(actions.fs.rename_file.fn(":p")) Api.fs.cut = wrap_node(wrap_explorer_member("clipboard", "cut")) Api.fs.paste = wrap_node(wrap_explorer_member("clipboard", "paste")) Api.fs.clear_clipboard = wrap_explorer_member("clipboard", "clear_clipboard") Api.fs.print_clipboard = wrap_explorer_member("clipboard", "print_clipboard") Api.fs.copy.node = wrap_node(wrap_explorer_member("clipboard", "copy")) Api.fs.copy.absolute_path = wrap_node(wrap_explorer_member("clipboard", "copy_absolute_path")) Api.fs.copy.filename = wrap_node(wrap_explorer_member("clipboard", "copy_filename")) Api.fs.copy.basename = wrap_node(wrap_explorer_member("clipboard", "copy_basename")) Api.fs.copy.relative_path = wrap_node(wrap_explorer_member("clipboard", "copy_path")) ---@param mode string ---@param toggle_group boolean? ---@return fun(node: Node, edit_opts: NodeEditOpts?) local function open_or_expand_or_dir_up(mode, toggle_group) ---@param node Node ---@param edit_opts NodeEditOpts? return function(node, edit_opts) local root = node:as(RootNode) local dir = node:as(DirectoryNode) if root or node.name == ".." then actions.root.change_dir.fn("..") elseif dir then dir:expand_or_collapse(toggle_group) elseif not toggle_group then edit(mode, node, edit_opts) end end end Api.node.open.edit = wrap_node(open_or_expand_or_dir_up("edit")) Api.node.open.drop = wrap_node(open_or_expand_or_dir_up("drop")) Api.node.open.tab_drop = wrap_node(open_or_expand_or_dir_up("tab_drop")) Api.node.open.replace_tree_buffer = wrap_node(open_or_expand_or_dir_up("edit_in_place")) Api.node.open.no_window_picker = wrap_node(open_or_expand_or_dir_up("edit_no_picker")) Api.node.open.vertical = wrap_node(open_or_expand_or_dir_up("vsplit")) Api.node.open.vertical_no_picker = wrap_node(open_or_expand_or_dir_up("vsplit_no_picker")) Api.node.open.horizontal = wrap_node(open_or_expand_or_dir_up("split")) Api.node.open.horizontal_no_picker = wrap_node(open_or_expand_or_dir_up("split_no_picker")) Api.node.open.tab = wrap_node(open_or_expand_or_dir_up("tabnew")) Api.node.open.toggle_group_empty = wrap_node(open_or_expand_or_dir_up("toggle_group_empty", true)) Api.node.open.preview = wrap_node(open_or_expand_or_dir_up("preview")) Api.node.open.preview_no_picker = wrap_node(open_or_expand_or_dir_up("preview_no_picker")) Api.node.show_info_popup = wrap_node(actions.node.file_popup.toggle_file_info) Api.node.run.cmd = wrap_node(actions.node.run_command.run_file_command) Api.node.run.system = wrap_node(actions.node.system_open.fn) Api.node.navigate.sibling.next = wrap_node(actions.moves.sibling.fn("next")) Api.node.navigate.sibling.prev = wrap_node(actions.moves.sibling.fn("prev")) Api.node.navigate.sibling.first = wrap_node(actions.moves.sibling.fn("first")) Api.node.navigate.sibling.last = wrap_node(actions.moves.sibling.fn("last")) Api.node.navigate.parent = wrap_node(actions.moves.parent.fn(false)) Api.node.navigate.parent_close = wrap_node(actions.moves.parent.fn(true)) Api.node.navigate.git.next = wrap_node(actions.moves.item.fn({ where = "next", what = "git" })) Api.node.navigate.git.next_skip_gitignored = wrap_node(actions.moves.item.fn({ where = "next", what = "git", skip_gitignored = true })) Api.node.navigate.git.next_recursive = wrap_node(actions.moves.item.fn({ where = "next", what = "git", recurse = true })) Api.node.navigate.git.prev = wrap_node(actions.moves.item.fn({ where = "prev", what = "git" })) Api.node.navigate.git.prev_skip_gitignored = wrap_node(actions.moves.item.fn({ where = "prev", what = "git", skip_gitignored = true })) Api.node.navigate.git.prev_recursive = wrap_node(actions.moves.item.fn({ where = "prev", what = "git", recurse = true })) Api.node.navigate.diagnostics.next = wrap_node(actions.moves.item.fn({ where = "next", what = "diag" })) Api.node.navigate.diagnostics.next_recursive = wrap_node(actions.moves.item.fn({ where = "next", what = "diag", recurse = true })) Api.node.navigate.diagnostics.prev = wrap_node(actions.moves.item.fn({ where = "prev", what = "diag" })) Api.node.navigate.diagnostics.prev_recursive = wrap_node(actions.moves.item.fn({ where = "prev", what = "diag", recurse = true })) Api.node.navigate.opened.next = wrap_node(actions.moves.item.fn({ where = "next", what = "opened" })) Api.node.navigate.opened.prev = wrap_node(actions.moves.item.fn({ where = "prev", what = "opened" })) Api.node.expand = wrap_node(actions.tree.modifiers.expand.node) Api.node.collapse = wrap_node(actions.tree.modifiers.collapse.node) ---@class ApiNodeDeleteWipeBufferOpts ---@field force boolean|nil default false Api.node.buffer.delete = wrap_node(function(node, opts) actions.node.buffer.delete(node, opts) end) Api.node.buffer.wipe = wrap_node(function(node, opts) actions.node.buffer.wipe(node, opts) end) Api.git.reload = wrap_explorer("reload_git") Api.events.subscribe = events.subscribe Api.events.Event = events.Event Api.live_filter.start = wrap_explorer_member("live_filter", "start_filtering") Api.live_filter.clear = wrap_explorer_member("live_filter", "clear_filter") Api.marks.get = wrap_node(wrap_explorer_member("marks", "get")) Api.marks.list = wrap_explorer_member("marks", "list") Api.marks.toggle = wrap_node(wrap_explorer_member("marks", "toggle")) Api.marks.clear = wrap_explorer_member("marks", "clear") Api.marks.bulk.delete = wrap_explorer_member("marks", "bulk_delete") Api.marks.bulk.trash = wrap_explorer_member("marks", "bulk_trash") Api.marks.bulk.move = wrap_explorer_member("marks", "bulk_move") Api.marks.navigate.next = wrap_explorer_member("marks", "navigate_next") Api.marks.navigate.prev = wrap_explorer_member("marks", "navigate_prev") Api.marks.navigate.select = wrap_explorer_member("marks", "navigate_select") Api.config.mappings.get_keymap = wrap(keymap.get_keymap) Api.config.mappings.get_keymap_default = wrap(keymap.get_keymap_default) Api.config.mappings.default_on_attach = keymap.default_on_attach Api.diagnostics.hi_test = wrap(appearance_hi_test) Api.commands.get = wrap(function() return require("nvim-tree.commands").get() end) ---Create a decorator class by calling :extend() ---See :help nvim-tree-decorators ---@type nvim_tree.api.decorator.UserDecorator Api.decorator.UserDecorator = UserDecorator --[[@as nvim_tree.api.decorator.UserDecorator]] return Api