aerial.nvim/lua/aerial/config.lua
2024-07-15 16:09:29 -07:00

644 lines
21 KiB
Lua

local default_options = {
-- Priority list of preferred backends for aerial.
-- This can be a filetype map (see :help aerial-filetype-map)
backends = { "treesitter", "lsp", "markdown", "asciidoc", "man" },
layout = {
-- These control the width of the aerial window.
-- They can be integers or a float between 0 and 1 (e.g. 0.4 for 40%)
-- min_width and max_width can be a list of mixed types.
-- max_width = {40, 0.2} means "the lesser of 40 columns or 20% of total"
max_width = { 40, 0.2 },
width = nil,
min_width = 10,
-- key-value pairs of window-local options for aerial window (e.g. winhl)
win_opts = {},
-- Determines the default direction to open the aerial window. The 'prefer'
-- options will open the window in the other direction *if* there is a
-- different buffer in the way of the preferred direction
-- Enum: prefer_right, prefer_left, right, left, float
default_direction = "prefer_right",
-- Determines where the aerial window will be opened
-- edge - open aerial at the far right/left of the editor
-- window - open aerial to the right/left of the current window
placement = "window",
-- When the symbols change, resize the aerial window (within min/max constraints) to fit
resize_to_content = true,
-- Preserve window size equality with (:help CTRL-W_=)
preserve_equality = false,
},
-- Determines how the aerial window decides which buffer to display symbols for
-- window - aerial window will display symbols for the buffer in the window from which it was opened
-- global - aerial window will display symbols for the current window
attach_mode = "window",
-- List of enum values that configure when to auto-close the aerial window
-- unfocus - close aerial when you leave the original source window
-- switch_buffer - close aerial when you change buffers in the source window
-- unsupported - close aerial when attaching to a buffer that has no symbol source
close_automatic_events = {},
-- Keymaps in aerial window. Can be any value that `vim.keymap.set` accepts OR a table of keymap
-- options with a `callback` (e.g. { callback = function() ... end, desc = "", nowait = true })
-- Additionally, if it is a string that matches "actions.<name>",
-- it will use the mapping at require("aerial.actions").<name>
-- Set to `false` to remove a keymap
keymaps = {
["?"] = "actions.show_help",
["g?"] = "actions.show_help",
["<CR>"] = "actions.jump",
["<2-LeftMouse>"] = "actions.jump",
["<C-v>"] = "actions.jump_vsplit",
["<C-s>"] = "actions.jump_split",
["p"] = "actions.scroll",
["<C-j>"] = "actions.down_and_scroll",
["<C-k>"] = "actions.up_and_scroll",
["{"] = "actions.prev",
["}"] = "actions.next",
["[["] = "actions.prev_up",
["]]"] = "actions.next_up",
["q"] = "actions.close",
["o"] = "actions.tree_toggle",
["za"] = "actions.tree_toggle",
["O"] = "actions.tree_toggle_recursive",
["zA"] = "actions.tree_toggle_recursive",
["l"] = "actions.tree_open",
["zo"] = "actions.tree_open",
["L"] = "actions.tree_open_recursive",
["zO"] = "actions.tree_open_recursive",
["h"] = "actions.tree_close",
["zc"] = "actions.tree_close",
["H"] = "actions.tree_close_recursive",
["zC"] = "actions.tree_close_recursive",
["zr"] = "actions.tree_increase_fold_level",
["zR"] = "actions.tree_open_all",
["zm"] = "actions.tree_decrease_fold_level",
["zM"] = "actions.tree_close_all",
["zx"] = "actions.tree_sync_folds",
["zX"] = "actions.tree_sync_folds",
},
-- When true, don't load aerial until a command or function is called
-- Defaults to true, unless `on_attach` is provided, then it defaults to false
lazy_load = true,
-- Disable aerial on files with this many lines
disable_max_lines = 10000,
-- Disable aerial on files this size or larger (in bytes)
disable_max_size = 2000000, -- Default 2MB
-- A list of all symbols to display. Set to false to display all symbols.
-- This can be a filetype map (see :help aerial-filetype-map)
-- To see all available values, see :help SymbolKind
filter_kind = {
"Class",
"Constructor",
"Enum",
"Function",
"Interface",
"Module",
"Method",
"Struct",
},
-- Determines line highlighting mode when multiple splits are visible.
-- split_width Each open window will have its cursor location marked in the
-- aerial buffer. Each line will only be partially highlighted
-- to indicate which window is at that location.
-- full_width Each open window will have its cursor location marked as a
-- full-width highlight in the aerial buffer.
-- last Only the most-recently focused window will have its location
-- marked in the aerial buffer.
-- none Do not show the cursor locations in the aerial window.
highlight_mode = "split_width",
-- Highlight the closest symbol if the cursor is not exactly on one.
highlight_closest = true,
-- Highlight the symbol in the source buffer when cursor is in the aerial win
highlight_on_hover = false,
-- When jumping to a symbol, highlight the line for this many ms.
-- Set to false to disable
highlight_on_jump = 300,
-- Jump to symbol in source window when the cursor moves
autojump = false,
-- Define symbol icons. You can also specify "<Symbol>Collapsed" to change the
-- icon when the tree is collapsed at that symbol, or "Collapsed" to specify a
-- default collapsed icon. The default icon set is determined by the
-- "nerd_font" option below.
-- If you have lspkind-nvim installed, it will be the default icon set.
-- This can be a filetype map (see :help aerial-filetype-map)
icons = {},
-- Control which windows and buffers aerial should ignore.
-- Aerial will not open when these are focused, and existing aerial windows will not be updated
ignore = {
-- Ignore unlisted buffers. See :help buflisted
unlisted_buffers = false,
-- Ignore diff windows (setting to false will allow aerial in diff windows)
diff_windows = true,
-- List of filetypes to ignore.
filetypes = {},
-- Ignored buftypes.
-- Can be one of the following:
-- false or nil - No buftypes are ignored.
-- "special" - All buffers other than normal, help and man page buffers are ignored.
-- table - A list of buftypes to ignore. See :help buftype for the
-- possible values.
-- function - A function that returns true if the buffer should be
-- ignored or false if it should not be ignored.
-- Takes two arguments, `bufnr` and `buftype`.
buftypes = "special",
-- Ignored wintypes.
-- Can be one of the following:
-- false or nil - No wintypes are ignored.
-- "special" - All windows other than normal windows are ignored.
-- table - A list of wintypes to ignore. See :help win_gettype() for the
-- possible values.
-- function - A function that returns true if the window should be
-- ignored or false if it should not be ignored.
-- Takes two arguments, `winid` and `wintype`.
wintypes = "special",
},
-- Use symbol tree for folding. Set to true or false to enable/disable
-- Set to "auto" to manage folds if your previous foldmethod was 'manual'
-- This can be a filetype map (see :help aerial-filetype-map)
manage_folds = false,
-- When you fold code with za, zo, or zc, update the aerial tree as well.
-- Only works when manage_folds = true
link_folds_to_tree = false,
-- Fold code when you open/collapse symbols in the tree.
-- Only works when manage_folds = true
link_tree_to_folds = true,
-- Set default symbol icons to use patched font icons (see https://www.nerdfonts.com/)
-- "auto" will set it to true if nvim-web-devicons or lspkind-nvim is installed.
nerd_font = "auto",
-- Call this function when aerial attaches to a buffer.
on_attach = function(bufnr) end,
-- Call this function when aerial first sets symbols on a buffer.
on_first_symbols = function(bufnr) end,
-- Automatically open aerial when entering supported buffers.
-- This can be a function (see :help aerial-open-automatic)
open_automatic = false,
-- Run this command after jumping to a symbol (false will disable)
post_jump_cmd = "normal! zz",
-- Invoked after each symbol is parsed, can be used to modify the parsed item,
-- or to filter it by returning false.
--
-- bufnr: a neovim buffer number
-- item: of type aerial.Symbol
-- ctx: a record containing the following fields:
-- * backend_name: treesitter, lsp, man...
-- * lang: info about the language
-- * symbols?: specific to the lsp backend
-- * symbol?: specific to the lsp backend
-- * syntax_tree?: specific to the treesitter backend
-- * match?: specific to the treesitter backend, TS query match
post_parse_symbol = function(bufnr, item, ctx)
return true
end,
-- Invoked after all symbols have been parsed and post-processed,
-- allows to modify the symbol structure before final display
--
-- bufnr: a neovim buffer number
-- items: a collection of aerial.Symbol items, organized in a tree,
-- with 'parent' and 'children' fields
-- ctx: a record containing the following fields:
-- * backend_name: treesitter, lsp, man...
-- * lang: info about the language
-- * symbols?: specific to the lsp backend
-- * syntax_tree?: specific to the treesitter backend
post_add_all_symbols = function(bufnr, items, ctx)
return items
end,
-- When true, aerial will automatically close after jumping to a symbol
close_on_select = false,
-- The autocmds that trigger symbols update (not used for LSP backend)
update_events = "TextChanged,InsertLeave",
-- Show box drawing characters for the tree hierarchy
show_guides = false,
-- Customize the characters used when show_guides = true
guides = {
-- When the child item has a sibling below it
mid_item = "├─",
-- When the child item is the last in the list
last_item = "└─",
-- When there are nested child guides to the right
nested_top = "",
-- Raw indentation
whitespace = " ",
},
-- Set this function to override the highlight groups for certain symbols
get_highlight = function(symbol, is_icon, is_collapsed)
-- return "MyHighlight" .. symbol.kind
end,
-- Options for opening aerial in a floating win
float = {
-- Controls border appearance. Passed to nvim_open_win
border = "rounded",
-- Determines location of floating window
-- cursor - Opens float on top of the cursor
-- editor - Opens float centered in the editor
-- win - Opens float centered in the window
relative = "cursor",
-- These control the height of the floating window.
-- They can be integers or a float between 0 and 1 (e.g. 0.4 for 40%)
-- min_height and max_height can be a list of mixed types.
-- min_height = {8, 0.1} means "the greater of 8 rows or 10% of total"
max_height = 0.9,
height = nil,
min_height = { 8, 0.1 },
override = function(conf, source_winid)
-- This is the config that will be passed to nvim_open_win.
-- Change values here to customize the layout
return conf
end,
},
-- Options for the floating nav windows
nav = {
border = "rounded",
max_height = 0.9,
min_height = { 10, 0.1 },
max_width = 0.5,
min_width = { 0.2, 20 },
win_opts = {
cursorline = true,
winblend = 10,
},
-- Jump to symbol in source window when the cursor moves
autojump = false,
-- Show a preview of the code in the right column, when there are no child symbols
preview = false,
-- Keymaps in the nav window
keymaps = {
["<CR>"] = "actions.jump",
["<2-LeftMouse>"] = "actions.jump",
["<C-v>"] = "actions.jump_vsplit",
["<C-s>"] = "actions.jump_split",
["h"] = "actions.left",
["l"] = "actions.right",
["<C-c>"] = "actions.close",
},
},
lsp = {
-- If true, fetch document symbols when LSP diagnostics update.
diagnostics_trigger_update = false,
-- Set to false to not update the symbols when there are LSP errors
update_when_errors = true,
-- How long to wait (in ms) after a buffer change before updating
-- Only used when diagnostics_trigger_update = false
update_delay = 300,
-- Map of LSP client name to priority. Default value is 10.
-- Clients with higher (larger) priority will be used before those with lower priority.
-- Set to -1 to never use the client.
priority = {
-- pyright = 10,
},
},
treesitter = {
-- How long to wait (in ms) after a buffer change before updating
update_delay = 300,
},
markdown = {
-- How long to wait (in ms) after a buffer change before updating
update_delay = 300,
},
asciidoc = {
-- How long to wait (in ms) after a buffer change before updating
update_delay = 300,
},
man = {
-- How long to wait (in ms) after a buffer change before updating
update_delay = 300,
},
}
-- stylua: ignore
local plain_icons = {
Array = "[a]",
Boolean = "[b]",
Class = "[C]",
Constant = "[const]",
Constructor = "[Co]",
Enum = "[E]",
EnumMember = "[em]",
Event = "[Ev]",
Field = "[Fld]",
File = "[File]",
Function = "[F]",
Interface = "[I]",
Key = "[K]",
Method = "[M]",
Module = "[Mod]",
Namespace = "[NS]",
Null = "[-]",
Number = "[n]",
Object = "[o]",
Operator = "[+]",
Package = "[Pkg]",
Property = "[P]",
String = "[str]",
Struct = "[S]",
TypeParameter = "[T]",
Variable = "[V]",
Collapsed = "",
}
-- stylua: ignore
local nerd_icons = {
Array = "󱡠 ",
Boolean = "󰨙 ",
Class = "󰆧 ",
Constant = "󰏿 ",
Constructor = "",
Enum = "",
EnumMember = "",
Event = "",
Field = "",
File = "󰈙 ",
Function = "󰊕 ",
Interface = "",
Key = "󰌋 ",
Method = "󰊕 ",
Module = "",
Namespace = "󰦮 ",
Null = "󰟢 ",
Number = "󰎠 ",
Object = "",
Operator = "󰆕 ",
Package = "",
Property = "",
String = "",
Struct = "󰆼 ",
TypeParameter = "󰗴 ",
Variable = "󰀫 ",
Collapsed = "",
}
---@diagnostic disable-next-line: deprecated
local islist = vim.islist or vim.tbl_islist
local M = {}
M.get_filetypes = function(bufnr)
local ft = vim.bo[bufnr or 0].filetype
return vim.split(ft, "%.")
end
---@param name string
---@param option any
---@param default any
local function create_filetype_opt_getter(name, option, default)
local buffer_option_name = string.format("aerial_%s", name)
if type(option) ~= "table" or islist(option) then
return function(bufnr)
local has_buf_option, buf_option = pcall(vim.api.nvim_buf_get_var, bufnr, buffer_option_name)
if has_buf_option then
return buf_option
else
return option
end
end
else
return function(bufnr)
local has_buf_option, buf_option = pcall(vim.api.nvim_buf_get_var, bufnr, buffer_option_name)
if has_buf_option then
return buf_option
end
for _, ft in ipairs(M.get_filetypes(bufnr)) do
if option[ft] ~= nil then
return option[ft]
end
end
if option["_"] ~= nil then
return option["_"]
else
return default
end
end
end
end
---@param value string
---@param values string[]
---@param opts? {allow_nil: boolean}
local function assert_enum(value, values, opts)
opts = opts or {}
local valid
if value == nil then
valid = opts.allow_nil
else
valid = vim.tbl_contains(values, value)
end
if valid then
return value
else
vim.notify(
string.format("Aerial got '%s', expected one of %s", value, table.concat(values, ", ")),
vim.log.levels.WARN
)
return values[1]
end
end
M.setup = function(opts)
opts = opts or {}
local newconf = vim.tbl_deep_extend("force", default_options, opts)
-- Asserts for all enum values
newconf.layout.default_direction = assert_enum(
newconf.layout.default_direction,
{ "prefer_right", "prefer_left", "right", "left", "float" }
)
newconf.layout.placement = assert_enum(newconf.layout.placement, { "window", "edge" })
newconf.attach_mode = assert_enum(newconf.attach_mode, { "window", "global" })
for i, v in ipairs(newconf.close_automatic_events) do
newconf.close_automatic_events[i] =
assert_enum(v, { "unfocus", "switch_buffer", "unsupported" })
end
newconf.highlight_mode =
assert_enum(newconf.highlight_mode, { "split_width", "full_width", "last", "none" })
if newconf.nerd_font == "auto" then
local has_devicons = pcall(require, "nvim-web-devicons")
local has_lspkind = pcall(require, "lspkind")
pcall(require, "mini.icons")
local has_miniicons = vim.tbl_get(_G, "MiniIcons", "config", "style") == "glyph"
newconf.nerd_font = has_devicons or has_lspkind or has_miniicons
end
-- Add lookup to close_automatic_events
for i, v in ipairs(newconf.close_automatic_events) do
newconf.close_automatic_events[v] = i
end
-- Undocumented use_icon_provider option for tests. End users can simply provide
-- their own icons
if newconf.use_icon_provider == nil then
newconf.use_icon_provider = true
end
newconf.default_icons = newconf.nerd_font and nerd_icons or plain_icons
if type(newconf.open_automatic) == "boolean" then
local open_automatic = newconf.open_automatic
newconf.open_automatic = function(bufnr)
return open_automatic and not require("aerial.util").is_ignored_buf(bufnr)
end
elseif type(newconf.open_automatic) ~= "function" then
vim.notify_once(
"aerial.config.open_automatic should be a boolean or function. See :help aerial-open-automatic.",
vim.log.levels.WARN
)
newconf.open_automatic = function(bufnr)
return false
end
end
for k, v in pairs(newconf) do
M[k] = v
end
M.manage_folds =
create_filetype_opt_getter("manage_folds", M.manage_folds, default_options.manage_folds)
M.backends = create_filetype_opt_getter("backends", M.backends, default_options.backends)
local get_filter_kind_list =
create_filetype_opt_getter("filter_kind", M.filter_kind, default_options.filter_kind)
---@param bufnr integer
M.get_filter_kind_map = function(bufnr)
local fk = get_filter_kind_list(bufnr)
if fk == false or fk == 0 then
return setmetatable({}, {
__index = function()
return true
end,
__tostring = function()
return "all symbols"
end,
})
else
local ret = {}
for _, kind in ipairs(fk) do
ret[kind] = true
end
return setmetatable(ret, {
__tostring = function()
return table.concat(fk, ", ")
end,
})
end
end
end
local function get_icon(kind, filetypes)
for _, ft in ipairs(filetypes) do
local icon_map = M.icons[ft]
local icon = icon_map and icon_map[kind]
if icon then
return icon
end
end
return M.icons[kind]
end
local function get_icon_provider()
if not M.use_icon_provider then -- skip if icon provider not used
return false
end
-- prefer mini.icons
local _, mini_icons = pcall(require, "mini.icons")
---@diagnostic disable-next-line: undefined-field
if _G.MiniIcons then -- `_G.MiniIcons` is a better check to see if the module is setup
return function(kind)
return mini_icons.get("lsp", kind)
end
end
-- fallback to `lspkind`
local has_lspkind, lspkind = pcall(require, "lspkind")
if has_lspkind then
return function(kind)
return lspkind.symbolic(kind, { mode = "symbolic " })
end
end
return false -- no icon provider
end
local icon_provider
M.get_icon = function(bufnr, kind, collapsed)
if collapsed then
kind = kind .. "Collapsed"
end
local icon
-- Slight optimization for users that don't specify icons
if not vim.tbl_isempty(M.icons) then
local filetypes = M.get_filetypes(bufnr)
table.insert(filetypes, "_")
icon = get_icon(kind, filetypes)
if not icon and collapsed then
icon = get_icon("Collapsed", filetypes)
end
if icon then
return icon
end
end
if icon_provider == nil then
icon_provider = get_icon_provider()
end
if icon_provider and not collapsed then
icon = icon_provider(kind)
if icon and icon ~= "" then
return icon
end
end
icon = M.default_icons[kind]
if not icon and collapsed then
icon = M.default_icons.Collapsed
end
return icon or " "
end
return M