Note that there two things that we are referring to when we say "actions":
- An item you can pick in one of arbor's pickers that calls a function
- Those underlying functions which also are also plug & play with arbor's events (i.e. hooks and autocmd callbacks).
Actions are designed such that they conform as valid function apis for hooks, autocmd callbacks (for arbor's autocmds), or as entries in any of the pickers.
Note that just because a particular action is compliant as all three, it doesn't mean that it's well suited or designed to be used in that way.
If you want to see a full list of functions, each one has it's own dedicated file in the repo under lua/arbor/actions. This also makes it easy to see what the action is doing.
All actions conform to the same base requirements, they receive the git_info table as the first argument. The git_info table has the following definition.
---@class arbor.git.info
---@field operation_opts? arbor.opts -- opts passed to the current feature (add/pick/remove)
---@field branch string -- the starting branch
---@field cwd string -- vim's cwd
---@field toplevel string -- git top level from the cwd (nearest parent .git)
---@field repo_type arbor.git.repo_type -- "normal" or "bare"
---@field common_dir string -- git common dir path
---@field resolved_base? string -- resolved repo base based on your current config
---@field branch_info? arbor.git.branch -- information about the selected branch
---@field new_path? string -- new path if relevant
---@field new_branch? string -- new branch if relevant
Note that some actions may take additional arguments, and can be wrapped in order to pass those arguments. For example, with the fetch action:
-- this is from lua/arbor/actions/fetch.lua
local function git_fetch(info, opts)
opts = opts or {}
local args = opts.fetch_args or {}
table.insert(args, 1, "fetch")
local job = require("arbor.git").job({
args = args,
cwd = opts.cwd or (info and info.common_dir),
--- parts omitted for brevity...
})
job:sync()
end
You can pass options to it like so:
local function fetch_hook(info)
return git_fetch(info, {
-- pass your options here
cwd = my_cwd_function()
fetch_args = { "--all" }
})
end
You can also wrap an action in a function to add even more custom behavior around it, just note that that wrapper must accept an arbor.git.info? as the first argument, and depending on the context, not all of the fields may be populated (those that are marked as non-null will always be available though).
local function is_valid_switch(info)
return not (info.repo_type ~= "bare" and info.cwd == info.resolved_base and info.cwd == info.branch_info.new_path)
end
function M.arbor_post_switch(info)
if is_valid_switch(info) then
if info.new_path then
require("arbor").actions.cd_new_path(info)
else
require("arbor").actions.cd_existing_worktree(info)
end
M.arbor_set_dashboard(info) -- Set the dashboard with worktree name (function omitted for brevity)
vim.cmd("bufdo bd")
else
vim.notify("Already on this worktree")
end
end
Because Arbor offers several ways to handle events, it may be important for you to know how they work if you are using multiple in the same context.
The main lifecycle:
- Get the main choice from the picker
- If main choice was an action, just run the action and don't do anything else
- If main choice was a branch, get any other input needed to resolve it.
- If Arbor<context>Pre event is enabled, execute that autocmd
- If preserve_default_hooks=true in opts and you've provided a pre hook in the opts, run it.
- If you have provided a pre hook in the opts run it.
- Run the context's main action
- If preserve_default_hooks=true in opts and you've provided a post hook in the opts, run it.
- If you have provided a post hook in the opts run it.
- If Arbor<context>Post event is enabled, execute that autocmd
Note the order of the hooks, your hooks can return an arbor.git.info table to overwrite what get's passed into the next event in the lifecycle.
This means you can use hooks to modify what gets passed to the main action and the Arbor<context>Post autocmd.
Side note: I was debating whether the Pre event should be allowed to be rewritten by hooks, but it isn't useful, since you will likely disrupt what get's passed to the main context. You can also exec an autocmd from your hook if you really need to.
In order to enable this high level of plug & play, there are some minor sacrifices that we had to make to the actions api.
First and foremost, all actions must expect arbor.git.info as the first argument. The action can allow this argument to be optional, but in order to be compliant as a hook, action, or arbor autocmd, this must be the first argument.
You can add any other arguments you want to an action, however they must be optional, otherwise they won't be plug & play.
A good example of both of these is arbor.actions.add_new_branch