Skip to content

Conversation

@alex-courtis
Copy link
Member

@alex-courtis alex-courtis commented Jan 17, 2026

fixes #3088
fixes #2668
addresses #3231 step 2

follows #3235

No AI tooling was used.

Scope

  • Create meta lua/nvim-tree/_meta/api/ and lua/nvim-tree/_meta/api.lua for
    • Empty API functions
    • API option classes
  • Expose above to user as the result of require("nvim-tree.api")
  • Hydrate functions with implementations
    • on require("nvim-tree.api") call pre.lua (fast)
      • error "nvim-tree setup not called" (removed wrap) OR
      • pre-setup implementations e.g. api.map.default_on_attach
    • after setup call post.lua (slow) to completes all remaining implementations
  • Documented node and git classes in annotations and help
  • Some API was refactored and grouped for clarity, retaining compatibility via legacy.lua map_api
  • Corrected filter function names: hidden -> dotfiles
  • Rewrote *nvim-tree-commands* help

#3231 step 2 was added to scope as the api hydration was changed anyway and needed to be tested.

Startup timing:

Nothing required:

007.335  000.055  000.055: require('nvim-tree.commands')
007.389  000.117  000.062: sourcing /tmp/nd/local/share/nvim/site/pack/packer/start/nvim-tree.lua.dev/plugin/nvim-tree.lua

api

require("nvim-tree.api")
003.078  000.014  000.014: require('nvim-tree._meta.api.commands')
003.092  000.013  000.013: require('nvim-tree._meta.api.events')
003.112  000.019  000.019: require('nvim-tree._meta.api.filter')
003.150  000.018  000.018: require('nvim-tree._meta.api.fs.copy')
003.152  000.040  000.021: require('nvim-tree._meta.api.fs')
003.166  000.013  000.013: require('nvim-tree._meta.api.health')
003.181  000.014  000.014: require('nvim-tree._meta.api.map')
003.203  000.022  000.022: require('nvim-tree._meta.api.marks')
003.258  000.033  000.033: require('nvim-tree._meta.api.node.navigate')
003.283  000.025  000.025: require('nvim-tree._meta.api.node.open')
003.285  000.081  000.024: require('nvim-tree._meta.api.node')
003.320  000.035  000.035: require('nvim-tree._meta.api.tree')
003.414  000.060  000.060: require('nvim-tree.commands')
003.509  000.046  000.046: require('nvim-tree.notify')
003.513  000.099  000.054: require('nvim-tree.events')
003.604  000.090  000.090: require('nvim-tree.keymap')
003.688  000.033  000.033: require('nvim-tree.classic')
003.691  000.074  000.041: require('nvim-tree.renderer.decorator')
003.693  000.088  000.015: require('nvim-tree.renderer.decorator.user')
003.694  000.373  000.035: require('nvim-tree.api.impl.pre')
003.807  000.090  000.090: require('nvim-tree.legacy')
003.809  000.765  000.049: require('nvim-tree.api')
003.891  000.082  000.082: require('nvim-tree.log')
008.729  000.060  000.060: sourcing /tmp/nd/local/share/nvim/site/pack/packer/start/nvim-tree.lua.dev/plugin/nvim-tree.lua

setup

require("nvim-tree.api")
require("nvim-tree").setup(config)
003.039  000.016  000.016: require('nvim-tree._meta.api.commands')
003.051  000.012  000.012: require('nvim-tree._meta.api.events')
003.071  000.019  000.019: require('nvim-tree._meta.api.filter')
003.111  000.017  000.017: require('nvim-tree._meta.api.fs.copy')
003.113  000.042  000.024: require('nvim-tree._meta.api.fs')
003.128  000.014  000.014: require('nvim-tree._meta.api.health')
003.141  000.013  000.013: require('nvim-tree._meta.api.map')
003.164  000.022  000.022: require('nvim-tree._meta.api.marks')
003.231  000.033  000.033: require('nvim-tree._meta.api.node.navigate')
003.258  000.027  000.027: require('nvim-tree._meta.api.node.open')
003.260  000.096  000.035: require('nvim-tree._meta.api.node')
003.294  000.034  000.034: require('nvim-tree._meta.api.tree')
003.385  000.058  000.058: require('nvim-tree.commands')
003.482  000.045  000.045: require('nvim-tree.notify')
003.485  000.100  000.054: require('nvim-tree.events')
003.572  000.087  000.087: require('nvim-tree.keymap')
003.662  000.037  000.037: require('nvim-tree.classic')
003.665  000.080  000.044: require('nvim-tree.renderer.decorator')
003.667  000.094  000.014: require('nvim-tree.renderer.decorator.user')
003.668  000.373  000.035: require('nvim-tree.api.impl.pre')
003.785  000.099  000.099: require('nvim-tree.legacy')
003.787  000.782  000.043: require('nvim-tree.api')
003.867  000.080  000.080: require('nvim-tree.log')
007.762  000.163  000.163: require('nvim-tree.utils')
007.770  000.406  000.243: require('nvim-tree.view')
007.868  000.027  000.027: require('nvim-tree.core')
008.021  000.066  000.066: require('nvim-tree.git.utils')
008.040  000.019  000.019: require('nvim-tree.renderer.components.devicons')
008.079  000.038  000.038: require('nvim-tree.node')
008.082  000.213  000.091: require('nvim-tree.node.directory')
008.112  000.029  000.029: require('nvim-tree.iterators.node-iterator')
008.113  000.312  000.042: require('nvim-tree.actions.finders.find-file')
008.159  000.045  000.045: require('nvim-tree.actions.finders.search-node')
008.159  000.371  000.014: require('nvim-tree.actions.finders')
008.271  000.052  000.052: require('nvim-tree.node.file')
008.272  000.097  000.045: require('nvim-tree.actions.fs.create-file')
008.392  000.056  000.056: require('nvim-tree.lib')
008.438  000.013  000.013: require('nvim-tree.node.link')
008.441  000.048  000.034: require('nvim-tree.node.directory-link')
008.442  000.169  000.066: require('nvim-tree.actions.fs.remove-file')
008.507  000.064  000.064: require('nvim-tree.actions.fs.rename-file')
008.564  000.056  000.056: require('nvim-tree.actions.fs.trash')
008.564  000.405  000.018: require('nvim-tree.actions.fs')
008.718  000.079  000.079: require('nvim-tree.diagnostics')
008.719  000.142  000.063: require('nvim-tree.actions.moves.item')
008.744  000.024  000.024: require('nvim-tree.actions.moves.parent')
008.769  000.025  000.025: require('nvim-tree.actions.moves.sibling')
008.770  000.205  000.014: require('nvim-tree.actions.moves')
008.825  000.039  000.039: require('nvim-tree.actions.node.file-popup')
009.028  000.056  000.056: require('nvim-tree.renderer.components.full-name')
009.030  000.204  000.148: require('nvim-tree.actions.node.open-file')
009.046  000.015  000.015: require('nvim-tree.actions.node.run-command')
009.089  000.042  000.042: require('nvim-tree.actions.node.system-open')
009.115  000.026  000.026: require('nvim-tree.actions.node.buffer')
009.115  000.345  000.019: require('nvim-tree.actions.node')
009.159  000.028  000.028: require('nvim-tree.actions.tree.find-file')
009.207  000.034  000.034: require('nvim-tree.actions.tree.modifiers.collapse')
009.263  000.055  000.055: require('nvim-tree.actions.tree.modifiers.expand')
009.264  000.104  000.014: require('nvim-tree.actions.tree.modifiers')
009.290  000.025  000.025: require('nvim-tree.actions.tree.open')
009.315  000.025  000.025: require('nvim-tree.actions.tree.toggle')
009.334  000.018  000.018: require('nvim-tree.actions.tree.resize')
009.335  000.219  000.018: require('nvim-tree.actions.tree')
009.336  001.566  000.021: require('nvim-tree.actions')
009.351  002.370  000.399: require('nvim-tree')
009.954  000.086  000.086: require('nvim-tree.appearance')
010.412  000.027  000.027: require('nvim-tree.buffers')
010.620  000.089  000.089: require('nvim-tree.git.runner')
010.737  000.116  000.116: require('nvim-tree.watcher')
010.739  000.325  000.121: require('nvim-tree.git')
010.796  000.032  000.032: require('nvim-tree.node.file-link')
010.797  000.057  000.025: require('nvim-tree.node.factory')
010.814  000.016  000.016: require('nvim-tree.node.root')
010.906  000.011  000.011: require('nvim-tree.enum')
010.909  000.094  000.083: require('nvim-tree.explorer.filters')
011.037  000.122  000.122: require('nvim-tree.marks')
011.156  000.118  000.118: require('nvim-tree.explorer.live-filter')
011.265  000.108  000.108: require('nvim-tree.explorer.sorter')
011.414  000.149  000.149: require('nvim-tree.actions.fs.clipboard')
011.678  000.042  000.042: require('nvim-tree.renderer.decorator.bookmarks')
011.695  000.017  000.017: require('nvim-tree.renderer.decorator.copied')
011.720  000.024  000.024: require('nvim-tree.renderer.decorator.cut')
011.773  000.052  000.052: require('nvim-tree.renderer.decorator.diagnostics')
011.857  000.084  000.084: require('nvim-tree.renderer.decorator.git')
011.887  000.029  000.029: require('nvim-tree.renderer.decorator.hidden')
011.920  000.032  000.032: require('nvim-tree.renderer.decorator.modified')
011.952  000.032  000.032: require('nvim-tree.renderer.decorator.opened')
012.028  000.075  000.075: require('nvim-tree.renderer.components.padding')
012.032  000.557  000.170: require('nvim-tree.renderer.builder')
012.035  000.620  000.063: require('nvim-tree.renderer')
012.044  001.929  000.294: require('nvim-tree.explorer')
012.104  000.059  000.059: require('nvim-tree.explorer.watch')
012.130  000.022  000.022: require('nvim-tree.renderer.components')
014.898  000.120  000.120: require('nvim-tree.help')
015.038  000.113  000.113: require('nvim-tree.api.impl.post')
016.452  000.054  000.054: sourcing /tmp/nd/local/share/nvim/site/pack/packer/start/nvim-tree.lua.dev/plugin/nvim-tree.lua

Manual testing:

---@diagnostic disable: unused-local, unused-function

local api = require("nvim-tree.api")
local bufnr = -1
local path = "/home/alex"
local event_type = api.events.Event.NodeRenamed
local callback = function() print("NodeRenamed") end

-- rg -IN "^function" lua/nvim-tree/_meta/api | sort | uniq | sed -E 's/\(node\)/()/g; s/\(node, opts\)/()/g ; s/\(opts\)/()/g ; s/function nvim_tree.//g ; s/ end//g' | wl-copy

local function exec1()
  print(vim.inspect(api.commands.get()))
  api.events.subscribe(event_type, callback)
end

local function exec2()
  api.filter.custom.toggle()
  api.filter.dotfiles.toggle()
  api.filter.git.clean.toggle()
  api.filter.git.ignored.toggle()
  ---- execute these via keybindings
  -- api.filter.live.clear()
  -- api.filter.live.start()
  api.filter.no_bookmark.toggle()
  api.filter.no_buffer.toggle()
  api.filter.toggle()
end

local function exec3()
  api.fs.clear_clipboard()
  api.fs.copy.absolute_path()
  api.fs.copy.basename()
  api.fs.copy.filename()
  api.fs.copy.node()
  api.fs.copy.relative_path()
  ---- execute these via keybindings
  -- api.fs.create()
  -- api.fs.cut()
  -- api.fs.paste()
  print(vim.inspect(api.fs.print_clipboard()))
  ---- execute these via keybindings
  -- api.fs.remove()
  -- api.fs.rename_basename()
  -- api.fs.rename_full()
  -- api.fs.rename()
  -- api.fs.rename_node()
  -- api.fs.rename_sub()
  -- api.fs.trash()
end

local function exec4()
  api.marks.clear()
  api.marks.get()
  api.marks.list()
  api.marks.navigate.next()
  api.marks.navigate.prev()
  -- api.marks.navigate.select()
  api.marks.toggle()
end

local function exec5()
  api.node.expand()
  api.node.collapse()
  api.node.expand()
end

local function exec6()
  api.node.navigate.diagnostics.next()
  api.node.navigate.diagnostics.next_recursive()
  api.node.navigate.diagnostics.prev()
  api.node.navigate.diagnostics.prev_recursive()
  api.node.navigate.git.next()
  api.node.navigate.git.next_recursive()
  api.node.navigate.git.next_skip_gitignored()
  api.node.navigate.git.prev()
  api.node.navigate.git.prev_recursive()
  api.node.navigate.git.prev_skip_gitignored()
  api.node.navigate.opened.next()
  api.node.navigate.opened.prev()
  api.node.navigate.parent_close()
  api.node.navigate.parent()
  api.node.navigate.sibling.first()
  api.node.navigate.sibling.last()
  api.node.navigate.sibling.next()
  api.node.navigate.sibling.prev()
end

---- executed implicitly
-- api.map.default_on_attach(bufnr)
---- execute manually
---- execute via maps
-- api.marks.bulk.delete()
-- api.marks.bulk.move()
-- api.marks.bulk.trash()
-- api.node.buffer.delete()
-- api.node.buffer.wipe()
---- execute via maps
-- api.node.open.edit()
-- api.node.open.horizontal()
-- api.node.open.horizontal_no_picker()
-- api.node.open.no_window_picker()
-- api.node.open.preview()
-- api.node.open.preview_no_picker()
-- api.node.open.replace_tree_buffer()
-- api.node.open.tab_drop()
-- api.node.open.tab()
-- api.node.open.toggle_group_empty()
-- api.node.open.vertical()
-- api.node.open.vertical_no_picker()
-- api.node.run.cmd()



-- uncomment and run each

-- exec1()
-- exec2()
-- exec3()
-- api.health.hi_test()
-- print(vim.inspect(api.map.get_keymap_default()))
-- print(vim.inspect(api.map.get_keymap()))
-- exec4()
-- exec5()
-- exec6()
-- api.node.run.system()
-- api.node.show_info_popup()
-- api.tree.change_root(path)
-- api.tree.change_root_to_node()
-- api.tree.change_root_to_parent()
-- api.tree.close()
-- api.tree.close_in_all_tabs()
-- api.tree.close_in_this_tab()
-- api.tree.expand_all()
-- api.tree.collapse_all()
-- api.tree.find_file()
-- api.tree.focus()
-- print(vim.inspect(api.tree.get_nodes(), { depth = 2 }))
-- print(vim.inspect(api.tree.get_node_under_cursor(), { depth = 2 }))
-- print(api.tree.is_tree_buf(1))
-- print(api.tree.is_tree_buf(2))
-- print(api.tree.is_visible())
-- api.tree.open()
-- api.tree.reload()
-- api.tree.reload_git()
-- api.tree.resize({ width = 50})
-- api.tree.search_node()
-- api.tree.toggle_help()
-- api.tree.toggle()
-- print(api.tree.winid())
print(vim.inspect(api.decorator.UserDecorator:extend()))

…_decorator.lua changes, retain api opts classes but make them exact
end
end)

api.tree.change_root_to_node = wrap_node(wrap_explorer("change_dir_to_node"))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change has paid off already @Uanela

No need to rewrite this for lazy loading.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's great

@alex-courtis alex-courtis changed the title docs(#3088): add nvim-tree.api* function and class meta, generate help documentation docs(#3088): add nvim-tree.api* function and class meta, generate help documentation, lazy load api Jan 24, 2026
# TODO #3241 ensure that decorator is checked - all meta should be valid
luacheck:
luacheck --codes --quiet lua --exclude-files "**/_meta/api_decorator.lua"
luacheck --codes --quiet lua --exclude-files "**/_meta/**"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

luacheck just can't cope with meta. luals is fine so just skip it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Documentation: API luals Annotations And Help Full And Sanitized Node Documentation

2 participants