refactor: Change aerial config to use setup function (#37)

This change is backwards-compatible, though the old global-variable
method will be removed at some point. The motivation for this change is
that looking up (and potentially processing) the values from global
variable on each call to `config.<var>` is slow (in a way that matters
for large files). We're fixing this by requiring a call to `setup()`
before using aerial and preprocessing all the options that need it. Now
config lookups are as fast as a table access.
This commit is contained in:
Steven Arcangeli 2021-12-31 21:32:55 -08:00
parent 15f9f054ff
commit 40a638680b
8 changed files with 470 additions and 512 deletions

View file

@ -9,6 +9,21 @@ from typing import List
HERE = os.path.dirname(__file__)
ROOT = os.path.abspath(os.path.join(HERE, os.path.pardir))
README = os.path.join(ROOT, "README.md")
DOC = os.path.join(ROOT, "doc", "aerial.txt")
def indent(lines: List[str], amount: int) -> List[str]:
ret = []
for line in lines:
if amount >= 0:
ret.append(" " * amount + line)
else:
space = re.match(r"[ \t]+", line)
if space:
ret.append(line[min(abs(amount), space.span()[1]) :])
else:
ret.append(line)
return ret
def replace_section(file: str, start_pat: str, end_pat: str, lines: List[str]) -> None:
@ -62,9 +77,11 @@ def update_treesitter_languages():
def update_config_options():
config_file = os.path.join(ROOT, "lua", "aerial", "config.lua")
opt_lines = ["\n", "```lua\n", "vim.g.aerial = {\n"]
opt_lines += read_section(config_file, r"^\s*local default_options =", r"^}$")
replace_section(README, r"^## Options", r"^}$", opt_lines)
opt_lines = read_section(config_file, r"^\s*local default_options =", r"^}$")
replace_section(README, r'^require\("aerial"\).setup\(', r"^}\)$", opt_lines)
replace_section(
DOC, r'^\s*require\("aerial"\)\.setup', r"^\s*}\)$", indent(opt_lines, 4)
)
def update_default_bindings():

100
README.md
View file

@ -190,10 +190,12 @@ Command | arg | description
`AerialInfo` | | Print out debug info related to aerial
## Options
If you want to change the default behavior of aerial, call the `setup` function:
```lua
vim.g.aerial = {
-- Priority list of preferred backends for aerial
require("aerial").setup({
-- Priority list of preferred backends for aerial.
-- This can be a filetype map (see :help aerial-filetype-map)
backends = { "lsp", "treesitter", "markdown" },
-- Enum: persist, close, auto, global
@ -217,6 +219,8 @@ vim.g.aerial = {
disable_max_lines = 10000,
-- 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",
@ -229,18 +233,35 @@ vim.g.aerial = {
-- Enum: split_width, full_width, last, none
-- 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",
-- When jumping to a symbol, highlight the line for this many ms
-- Set to 0 or false to disable
-- When jumping to a symbol, highlight the line for this many ms.
-- Set to false to disable
highlight_on_jump = 300,
-- Fold code when folding the tree. Only works when manage_folds is enabled
link_tree_to_folds = true,
-- 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, aerial will use it for icons.
icons = {},
-- Fold the tree when folding code. Only works when manage_folds is enabled
-- 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,
-- Use symbol tree for folding. Set to true or false to enable/disable
-- 'auto' will manage folds if your previous foldmethod was 'manual'
manage_folds = false,
@ -252,11 +273,12 @@ vim.g.aerial = {
-- To disable dynamic resizing, set this to be equal to max_width
min_width = 10,
-- Set default symbol icons to use Nerd Font icons (see https://www.nerdfonts.com/)
-- 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",
-- Whether to open aerial automatically when entering a buffer.
-- Can also be specified per-filetype as a map (see below)
-- If true, open aerial automatically when entering a new buffer.
-- This can be a filetype map (see :help aerial-filetype-map)
open_automatic = false,
-- If open_automatic is true, only open aerial if the source buffer is at
@ -273,7 +295,7 @@ vim.g.aerial = {
-- Run this command after jumping to a symbol (false will disable)
post_jump_cmd = "normal! zz",
-- If close_on_select is true, aerial will automatically close after jumping to a symbol
-- When true, aerial will automatically close after jumping to a symbol
close_on_select = false,
-- Options for opening aerial in a floating win
@ -313,62 +335,8 @@ vim.g.aerial = {
-- How long to wait (in ms) after a buffer change before updating
update_delay = 300,
},
}
})
-- open_automatic can be specified as a filetype map. For example, the below
-- configuration will open automatically in all filetypes except python and rust
vim.g.aerial = {
open_automatic = {
-- use underscore to specify the default behavior
['_'] = true,
python = false,
rust = false,
}
}
-- backends can also be specified as a filetype map.
vim.g.aerial = {
backends = {
-- use underscore to specify the default behavior
['_'] = {'lsp', 'treesitter'},
python = {'treesitter'},
rust = {'lsp'},
}
}
-- filter_kind can also be specified as a filetype map.
vim.g.aerial = {
filter_kind = {
-- use underscore to specify the default behavior
['_'] = {"Class", "Function", "Interface", "Method", "Struct"},
c = {"Namespace", "Function", "Struct", "Enum"}
}
}
-- You can also override the default icons.
-- Note that if you are using lspkind-nvim, aerial will use it for icons
-- https://github.com/onsails/lspkind-nvim
vim.g.aerial = {
icons = {
Class = '';
-- The icon to use when a class has been collapsed in the tree
ClassCollapsed = '喇';
Function = '';
Constant = '[c]'
-- The default icon to use when any symbol is collapsed in the tree
Collapsed = '▶';
}
}
```
Setting options in vimscript works the same way
```vim
" You can specify with global variables prefixed with 'aerial_'
let g:aerial_default_direction = 'left'
" Or you can set the g:aerial dict all at once
let g:aerial = {
\ 'default_direction': 'left',
\}
```
All possible SymbolKind values can be found [in the LSP

View file

@ -5,8 +5,9 @@ CONTENTS *aerial-contents
1. Commands........................................|aerial-commands|
2. Options.........................................|aerial-options|
3. Functions.......................................|aerial-functions|
4. FAQ.............................................|aerial-faq|
3. Notes...........................................|aerial-notes|
4. Functions.......................................|aerial-functions|
5. FAQ.............................................|aerial-faq|
===============================================================================
COMMANDS *aerial-commands*
@ -87,166 +88,167 @@ COMMANDS *aerial-commands
===============================================================================
OPTIONS *aerial-options*
Note that the options can be specified individually as listed below, or in a
single dict. If using a single dict, remove the "aerial_" prefix.
Configure aerial by calling the setup() function.
>
let g:aerial = {
\ 'default_direction': 'left',
\ 'min_width': 20,
\}
g:aerial_backends *g:aerial_backends*
Priority list of preferred backends for aerial. Built-in backends are "lsp"
and "treesitter". This can also be a |dict| mapping of filetypes. A key of
"_" will be used as the default if the filetype is not present.
>
let g:aerial_backends = {
\ '_': ["lsp", "treesitter"],
\ 'python': ["treesitter"],
\ 'rust': ["lsp"],
\}
g:aerial_close_behavior *g:aerial_close_behavior*
How to decide when to close the aerial window. Valid values are:
persist Remain open until manually closed
close Close once the original source file is no longer visible
in the tabpage
auto Stay open as long as there is a visible buffer to attach
to (default)
global Same as "persist", and will always show symbols for the
current buffer
g:aerial_default_bindings *g:aerial_default_bindings*
If `false`, don't set up the default keybindings in the aerial buffer.
g:aerial_default_direction *g:aerial_default_direction*
The default direction to open the window. Valid values are:
left Open the split to the left
right Open the split to the right
prefer_left Open to the left unless there are other windows left and
none to the right
prefer_right Open to the right unless there are other windows right
and none to the left (default)
float Open in a floating window
g:aerial_placement_editor_edge *g:aerial_placement_editor_edge*
If `true`, only open aerial at the far right/left of the editor. Default
behavior will open aerial as far right/left as possible while remaining
adjacent to a window containing the source buffer.
g:aerial_lsp_diagnostics_trigger_update *g:aerial_lsp_diagnostics_trigger_update*
Call |vim.lsp.buf.document_symbol()| to update symbols whenenever the LSP
client receives diagnostics. Default `true`.
g:aerial_filter_kind *g:aerial_filter_kind*
A list of all |SymbolKind| values to display. Set to `false` to show all
symbols. Default is "Class", "Constructor", "Enum", "Function",
"Interface", "Method", and "Struct". This can also be a |dict| mapping of
filetypes. A key of "_" will be used as the default if the filetype is not
present.
>
let g:aerial_filter_kind = {
\ '_': ["Class", "Function", "Interface", "Method", "Struct"],
\ 'c': ["Namespace", "Function", "Struct", "Enum"],
\}
g:aerial_highlight_mode *g:aerial_highlight_mode*
Valid values are "split_width", "full_width", "last", or "none".
split_width Each open buffer 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.
(default)
full_width Each open buffer 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.
g:aerial_highlight_on_jump *g:aerial_highlight_on_jump*
Briefly highlight the line jumped from |aerial.jump_to_loc()|. This value
is the number of milliseconds the highlight remains active for (default
300). It can also be set to `v:true` for the default, or `v:false` to
disable the highlight.
g:aerial_link_folds_to_tree *g:aerial_link_folds_to_tree*
When you fold code with |za|, |zo|, or |zc|, update the tree as well.
Requires |g:aerial_manage_folds| to be enabled. Default `false`.
g:aerial_link_tree_to_folds *g:aerial_link_tree_to_folds*
Update your code folds when you open/collapse symbols in the tree.
Requires |g:aerial_manage_folds| to be enabled. Default `true`.
g:aerial_manage_folds *g:aerial_manage_folds*
If `true`, will automatically configure your windows to use the symbols
tree for code folding. This is equivalent to setting 'foldmethod'=expr and
'foldexpr'=aerial#foldexpr() (which you can set manually if you prefer).
`true` Use aerial's foldexpr
`false` Do not modify fold settings
"auto" Manage folds if your previous 'foldmethod' was "manual" (default)
g:aerial_max_width *g:aerial_max_width*
The maximum width of the aerial window. Default 40.
g:aerial_min_width *g:aerial_min_width*
The minimum width of the aerial window. Default 10. If you want to disable
the dynamic resizing of the aerial window, set this to the same value as
|g:aerial_man_width|.
g:aerial_nerd_font *g:aerial_nerd_font*
If true the default icons will use Nerd Font icons. Valid values are:
`true` Use Nerd Font icons
`false` Do not use Nerd Font icons
"auto" Use Nerd Font icons if nvim-web-devicons is installed (default)
g:aerial_open_automatic *g:aerial_open_automatic*
If `true`, open aerial automatically when entering a new buffer. This can
be a boolean or a |dict| mapping of filetypes. A key of "_" will be used
as the default if the filetype is not present.
>
let g:aerial_open_automatic = {
\ '_': v:true,
\ 'python': v:false,
\ 'rust': v:false,
\}
g:aerial_open_automatic_min_lines *g:aerial_open_automatic_min_lines*
When |g:aerial_open_automatic| = `true`, you can set this value to only
automatically open aerial on files greater than a certain length.
g:aerial_open_automatic_min_symbols *g:aerial_open_automatic_min_symbols*
When |g:aerial_open_automatic| = `true`, you can set this value to only
automatically open aerial when there are at least this many document
symbols.
g:aerial_post_jump_cmd *g:aerial_post_jump_cmd*
Run this command after jumping to a symbol. Set to '' to disable.
Default "zvzz"
g:aerial_lsp_update_when_errors *g:aerial_lsp_update_when_errors*
Update the aerial buffer even when your file has LSP errors. Default `true`.
g:aerial_treesitter_update_delay *g:aerial_treesitter_update_delay*
How long to wait (in ms) after a buffer change before updating.
g:aerial_markdown_update_delay *g:aerial_markdown_update_delay*
How long to wait (in ms) after a buffer change before updating.
g:aerial_icons *g:aerial_icons*
A map of |SymbolKind| to icons. You can also specify "<Symbol>Collapsed"
to change the icon when the tree is collapsed at this symbol, or
"Collapsed" to specify a default collapsed icon.
>
let g:aerial_icons = {
\ 'Class' : '';
\ 'ClassCollapsed' : '喇';
\ 'Function' : '';
\ 'Constant' : '[c]'
\ 'Collapsed' : '▶';
\}
require("aerial").setup({
-- Priority list of preferred backends for aerial.
-- This can be a filetype map (see :help aerial-filetype-map)
backends = { "lsp", "treesitter", "markdown" },
-- Enum: persist, close, auto, global
-- persist - aerial window will stay open until closed
-- close - aerial window will close when original file is no longer visible
-- auto - aerial window will stay open as long as there is a visible
-- buffer to attach to
-- global - same as 'persist', and will always show symbols for the current buffer
close_behavior = "auto",
-- Set to false to remove the default keybindings for the aerial buffer
default_bindings = true,
-- Enum: prefer_right, prefer_left, right, left, float
-- 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
default_direction = "prefer_right",
-- Disable aerial on files with this many lines
disable_max_lines = 10000,
-- 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",
"Method",
"Struct",
},
-- Enum: split_width, full_width, last, none
-- 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",
-- When jumping to a symbol, highlight the line for this many ms.
-- Set to false to disable
highlight_on_jump = 300,
-- 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, aerial will use it for icons.
icons = {},
-- 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,
-- Use symbol tree for folding. Set to true or false to enable/disable
-- 'auto' will manage folds if your previous foldmethod was 'manual'
manage_folds = false,
-- The maximum width of the aerial window
max_width = 40,
-- The minimum width of the aerial window.
-- To disable dynamic resizing, set this to be equal to max_width
min_width = 10,
-- 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",
-- If true, open aerial automatically when entering a new buffer.
-- This can be a filetype map (see :help aerial-filetype-map)
open_automatic = false,
-- If open_automatic is true, only open aerial if the source buffer is at
-- least this long
open_automatic_min_lines = 0,
-- If open_automatic is true, only open aerial if there are at least this many symbols
open_automatic_min_symbols = 0,
-- Set to true to only open aerial at the far right/left of the editor
-- Default behavior opens aerial relative to current window
placement_editor_edge = false,
-- Run this command after jumping to a symbol (false will disable)
post_jump_cmd = "normal! zz",
-- When true, aerial will automatically close after jumping to a symbol
close_on_select = false,
-- Options for opening aerial in a floating win
float = {
-- Controls border appearance. Passed to nvim_open_win
border = "rounded",
-- Controls row offset from cursor. Passed to nvim_open_win
row = 1,
-- Controls col offset from cursor. Passed to nvim_open_win
col = 0,
-- The maximum height of the floating aerial window
max_height = 100,
-- The minimum height of the floating aerial window
-- To disable dynamic resizing, set this to be equal to max_height
min_height = 4,
},
lsp = {
-- Fetch document symbols when LSP diagnostics change.
-- If you set this to false, you will need to manually fetch symbols
diagnostics_trigger_update = true,
-- Set to false to not update the symbols when there are LSP errors
update_when_errors = true,
},
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,
},
})
<
===============================================================================
NOTES *aerial-notes*
*aerial-filetype-map*
Certain options can be configured per-filetype by passing in a table. "_" will
be used as the default if the filetype is not present. >
>
backends = {
'_' = {"lsp", "treesitter"},
'python' = {"treesitter"},
'rust' = {"lsp"},
}
<
*SymbolKind* *symbol*
A quick note on SymbolKind. An authoritative list of valid SymbolKinds can be
found in the LSP spec:

View file

@ -19,8 +19,10 @@ aerial aerial.txt /*aerial*
aerial-commands aerial.txt /*aerial-commands*
aerial-contents aerial.txt /*aerial-contents*
aerial-faq aerial.txt /*aerial-faq*
aerial-filetype-map aerial.txt /*aerial-filetype-map*
aerial-functions aerial.txt /*aerial-functions*
aerial-loading aerial.txt /*aerial-loading*
aerial-notes aerial.txt /*aerial-notes*
aerial-options aerial.txt /*aerial-options*
aerial.close() aerial.txt /*aerial.close()*
aerial.focus() aerial.txt /*aerial.focus()*
@ -39,27 +41,5 @@ aerial.tree_cmd() aerial.txt /*aerial.tree_cmd()*
aerial.tree_open_all() aerial.txt /*aerial.tree_open_all()*
aerial.txt aerial.txt /*aerial.txt*
aerial.up() aerial.txt /*aerial.up()*
g:aerial_backends aerial.txt /*g:aerial_backends*
g:aerial_close_behavior aerial.txt /*g:aerial_close_behavior*
g:aerial_default_bindings aerial.txt /*g:aerial_default_bindings*
g:aerial_default_direction aerial.txt /*g:aerial_default_direction*
g:aerial_filter_kind aerial.txt /*g:aerial_filter_kind*
g:aerial_highlight_mode aerial.txt /*g:aerial_highlight_mode*
g:aerial_highlight_on_jump aerial.txt /*g:aerial_highlight_on_jump*
g:aerial_icons aerial.txt /*g:aerial_icons*
g:aerial_link_folds_to_tree aerial.txt /*g:aerial_link_folds_to_tree*
g:aerial_link_tree_to_folds aerial.txt /*g:aerial_link_tree_to_folds*
g:aerial_lsp_diagnostics_trigger_update aerial.txt /*g:aerial_lsp_diagnostics_trigger_update*
g:aerial_lsp_update_when_errors aerial.txt /*g:aerial_lsp_update_when_errors*
g:aerial_manage_folds aerial.txt /*g:aerial_manage_folds*
g:aerial_markdown_update_delay aerial.txt /*g:aerial_markdown_update_delay*
g:aerial_max_width aerial.txt /*g:aerial_max_width*
g:aerial_min_width aerial.txt /*g:aerial_min_width*
g:aerial_nerd_font aerial.txt /*g:aerial_nerd_font*
g:aerial_open_automatic aerial.txt /*g:aerial_open_automatic*
g:aerial_open_automatic_min_lines aerial.txt /*g:aerial_open_automatic_min_lines*
g:aerial_open_automatic_min_symbols aerial.txt /*g:aerial_open_automatic_min_symbols*
g:aerial_placement_editor_edge aerial.txt /*g:aerial_placement_editor_edge*
g:aerial_post_jump_cmd aerial.txt /*g:aerial_post_jump_cmd*
g:aerial_treesitter_update_delay aerial.txt /*g:aerial_treesitter_update_delay*
if aerial.txt /*if*
symbol aerial.txt /*symbol*

View file

@ -1,7 +1,9 @@
local has_devicons = pcall(require, "nvim-web-devicons")
local HAS_DEVICONS = pcall(require, "nvim-web-devicons")
local HAS_LSPKIND, lspkind = pcall(require, "lspkind")
local default_options = {
-- Priority list of preferred backends for aerial
-- Priority list of preferred backends for aerial.
-- This can be a filetype map (see :help aerial-filetype-map)
backends = { "lsp", "treesitter", "markdown" },
-- Enum: persist, close, auto, global
@ -25,6 +27,8 @@ local default_options = {
disable_max_lines = 10000,
-- 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",
@ -37,18 +41,35 @@ local default_options = {
-- Enum: split_width, full_width, last, none
-- 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",
-- When jumping to a symbol, highlight the line for this many ms
-- Set to 0 or false to disable
-- When jumping to a symbol, highlight the line for this many ms.
-- Set to false to disable
highlight_on_jump = 300,
-- Fold code when folding the tree. Only works when manage_folds is enabled
link_tree_to_folds = true,
-- 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, aerial will use it for icons.
icons = {},
-- Fold the tree when folding code. Only works when manage_folds is enabled
-- 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,
-- Use symbol tree for folding. Set to true or false to enable/disable
-- 'auto' will manage folds if your previous foldmethod was 'manual'
manage_folds = false,
@ -60,11 +81,12 @@ local default_options = {
-- To disable dynamic resizing, set this to be equal to max_width
min_width = 10,
-- Set default symbol icons to use Nerd Font icons (see https://www.nerdfonts.com/)
-- 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",
-- Whether to open aerial automatically when entering a buffer.
-- Can also be specified per-filetype as a map (see below)
-- If true, open aerial automatically when entering a new buffer.
-- This can be a filetype map (see :help aerial-filetype-map)
open_automatic = false,
-- If open_automatic is true, only open aerial if the source buffer is at
@ -81,7 +103,7 @@ local default_options = {
-- Run this command after jumping to a symbol (false will disable)
post_jump_cmd = "normal! zz",
-- If close_on_select is true, aerial will automatically close after jumping to a symbol
-- When true, aerial will automatically close after jumping to a symbol
close_on_select = false,
-- Options for opening aerial in a floating win
@ -123,198 +145,35 @@ local default_options = {
},
}
local function split(string, pattern)
local ret = {}
for token in string.gmatch(string, "[^" .. pattern .. "]+") do
table.insert(ret, token)
end
return ret
end
local function getkey(t, path)
local cur = t
for _, piece in ipairs(path) do
if cur == nil then
return nil
end
cur = cur[piece]
end
return cur
end
-- config options that are valid as bools, but don't have bools as the default
local addl_bool_opts = {
highlight_mode = true,
highlight_on_jump = true,
manage_folds = true,
nerd_font = true,
post_jump_cmd = true,
}
-- Returns (sanitized) value or the default if value is nil
local function option_or_default(path, value)
-- People are used to using 1/0 for v:true/v:false in vimscript
local default_value = getkey(default_options, path)
if type(default_value) == "boolean" or getkey(addl_bool_opts, path) then
if value == 0 then
value = false
elseif value == 1 then
return true
end
end
if value == nil then
return default_value
else
return value
end
end
local function get_option(path)
-- First look in the g:aerial_<name> variables
local varname = "aerial_" .. table.concat(path, "_")
local ret = vim.g[varname]
-- This is for backwards compatibility with lsp options that used to be in the
-- global namespace
local no_lsp_path
if ret == nil and path[1] == "lsp" then
no_lsp_path = vim.list_slice(path)
table.remove(no_lsp_path, 1)
varname = "aerial_" .. table.concat(no_lsp_path, "_")
ret = vim.g[varname]
end
if ret == nil then
ret = getkey((vim.g.aerial or {}), path)
end
-- For the same backwards compatibility as above
if ret == nil and path[1] == "lsp" then
ret = getkey((vim.g.aerial or {}), no_lsp_path)
end
return option_or_default(path, ret)
end
local Config = {}
function Config:new(path)
return setmetatable({
__path = path or {},
}, {
__index = function(t, key)
local ret = rawget(Config, key)
if ret then
return ret
end
local keypath = vim.list_extend({}, t.__path)
vim.list_extend(keypath, split(key, "\\."))
ret = get_option(keypath)
if type(ret) == "table" and (vim.tbl_isempty(ret) or not vim.tbl_islist(ret)) then
return t:new(keypath)
end
return ret
end,
})
end
local M = Config:new()
M.get_filetypes = function(bufnr)
local ft = vim.api.nvim_buf_get_option(bufnr or 0, "filetype")
return split(ft, "\\.")
end
local function create_filetype_opt_getter(path)
if type(path) ~= "table" then
path = { path }
end
return function(bufnr)
local ret = get_option(path)
if type(ret) == "table" then
local found = false
for _, ft in ipairs(M.get_filetypes(bufnr)) do
if ret[ft] then
found = true
ret = ret[ft]
break
end
end
if not found then
ret = ret["_"] or default_options[path]
end
end
return option_or_default(path, ret)
end
end
M.backends = create_filetype_opt_getter("backends")
M.open_automatic = create_filetype_opt_getter("open_automatic")
M.get_filter_kind_map = function(bufnr)
local fk = M.filter_kind
if type(fk) == "table" and not vim.tbl_islist(fk) then
local found = false
for _, filetype in ipairs(M.get_filetypes(bufnr)) do
if fk[filetype] then
fk = fk[filetype]
found = true
break
end
end
if not found then
fk = fk["_"] or default_options.filter_kind
end
end
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
-- 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 = '';
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
@ -325,7 +184,7 @@ local nerd_icons = {
Constructor = "",
Enum = "",
EnumMember = "",
Event = "",
Event = " ",
Field = "",
File = "",
Folder = "",
@ -339,48 +198,189 @@ local nerd_icons = {
Property = "",
Reference = "",
Snippet = "",
String = "s]";
String = "s]",
Struct = "",
Text = "",
Unit = "",
Value = "",
Variable = "",
Collapsed = "";
Collapsed = "",
}
local function get_table_default(tab, key, default_key, default)
if type(tab) ~= "table" or vim.tbl_islist(tab) then
return tab
local M = {}
local function split(string, pattern)
local ret = {}
for token in string.gmatch(string, "[^" .. pattern .. "]+") do
table.insert(ret, token)
end
local ret = tab[key]
if ret == nil and default_key then
ret = tab[default_key]
end
if ret == nil then
return default
return ret
end
M.get_filetypes = function(bufnr)
local ft = vim.api.nvim_buf_get_option(bufnr or 0, "filetype")
return split(ft, "\\.")
end
local function create_filetype_opt_getter(option, default)
if type(option) ~= "table" or vim.tbl_islist(option) then
return function()
return option
end
else
return function(bufnr)
for _, ft in ipairs(M.get_filetypes(bufnr)) do
if option[ft] ~= nil then
return option[ft]
end
end
return option["_"] and option["_"] or default
end
end
end
M.setup = function(opts)
local newconf = vim.tbl_deep_extend("force", default_options, opts or {})
if newconf.nerd_font == "auto" then
newconf.nerd_font = HAS_DEVICONS or HAS_LSPKIND
end
-- TODO for backwards compatibility
for k, _ in pairs(default_options.lsp) do
if newconf[k] ~= nil then
newconf.lsp[k] = newconf[k]
newconf[k] = nil
end
end
newconf.icons = vim.tbl_deep_extend(
"keep",
newconf.icons or {},
newconf.nerd_font and nerd_icons or plain_icons
)
for k, v in pairs(newconf) do
M[k] = v
end
M.open_automatic = create_filetype_opt_getter(M.open_automatic, default_options.open_automatic)
M.backends = create_filetype_opt_getter(M.backends, default_options.backends)
local get_filter_kind_list = create_filetype_opt_getter(
M.filter_kind,
default_options.filter_kind
)
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
-- Clear the metatable that looks up the vim.g.aerial values
setmetatable(M, {})
end
local bool_opts = {
close_on_select = true,
default_bindings = true,
diagnostics_trigger_update = true,
highlight_mode = true,
highlight_on_jump = true,
link_folds_to_tree = true,
link_tree_to_folds = true,
manage_folds = true,
nerd_font = true,
open_automatic = true,
placement_editor_edge = true,
post_jump_cmd = true,
update_when_errors = true,
}
local function calculate_opts()
local opts
local found_var = false
if vim.g.aerial then
opts = vim.g.aerial
found_var = true
else
opts = vim.deepcopy(default_options)
end
local function walk(prefix, obj)
for k, v in pairs(obj) do
local found, var = pcall(vim.api.nvim_get_var, prefix .. k)
-- This is for backwards compatibility with lsp options that used to be in the
-- global namespace
if not found and prefix == "aerial_lsp_" then
found, var = pcall(vim.api.nvim_get_var, "aerial_" .. k)
end
if found then
found_var = true
-- Convert 0/1 to true/false for backwards compatibility
if bool_opts[k] and type(var) ~= "boolean" then
vim.notify(
string.format(
"Deprecated: aerial expects a boolean for option '%s'",
k,
vim.log.levels.WARN
)
)
var = var ~= 0
end
obj[k] = var
elseif type(v) == "table" and not vim.tbl_islist(v) then
walk(prefix .. k .. "_", v)
end
end
end
walk("aerial_", opts)
if found_var then
vim.notify(
"Deprecated: aerial should no longer be configured with g:aerial, you should use require('aerial').setup(). See :help aerial for more details",
vim.log.levels.WARN
)
end
return opts
end
-- For backwards compatibility: if we search for config values and we haven't
-- yet called setup(), call setup with the config values pulled from global vars
setmetatable(M, {
__index = function(t, key)
M.setup(calculate_opts())
return rawget(M, key)
end,
})
-- Exposed for tests
M._get_icon = function(kind, collapsed)
if collapsed then
kind = kind .. "Collapsed"
end
local ret = M.icons[kind]
if ret ~= nil then
return ret
end
if collapsed then
ret = M.icons["Collapsed"]
end
return ret or " "
end
-- Only exposed for tests
M._get_icons = function()
local default
local nerd_font = M.nerd_font
if nerd_font == "auto" then
nerd_font = has_devicons
end
if nerd_font then
default = vim.tbl_extend("keep", nerd_icons, plain_icons)
else
default = plain_icons
end
return vim.tbl_extend("keep", get_option({ "icons" }) or {}, default)
end
local HAS_LSPKIND, lspkind = pcall(require, "lspkind")
local _last_checked = 0
local _last_icons = {}
M.get_icon = function(kind, collapsed)
if HAS_LSPKIND and not collapsed then
local icon = lspkind.symbolic(kind, { with_text = false })
@ -388,19 +388,7 @@ M.get_icon = function(kind, collapsed)
return icon
end
end
local icons = _last_icons
if os.time() - _last_checked > 5 then
icons = M._get_icons()
_last_icons = icons
_last_checked = os.time()
end
if collapsed then
return get_table_default(icons, kind .. "Collapsed", "Collapsed", kind)
else
return get_table_default(icons, kind, nil, kind)
end
return M._get_icon(kind, collapsed)
end
return M

View file

@ -10,6 +10,8 @@ local window = require("aerial.window")
local M = {}
M.setup = config.setup
M.is_open = function(bufnr)
return window.is_open(bufnr)
end

View file

@ -295,8 +295,6 @@ M.throttle = function(func, opts)
end
end
M.split = config.split
M.get_filetypes = config.get_filetypes
return M

View file

@ -1,8 +1,14 @@
local config = require("aerial.config")
local function reset()
package.loaded["aerial.config"] = nil
config = require("aerial.config")
end
describe("config", function()
before_each(function()
pcall(vim.api.nvim_del_var, "aerial")
reset()
end)
it("falls back to default options", function()
@ -28,15 +34,12 @@ describe("config", function()
row = 10,
},
}
assert.equals(config["float.row"], 10)
assert.equals(config.float.row, 10)
assert.equals(config["float.col"], 0)
assert.equals(config.float.col, 0)
end)
it("merges nested options with g:aerial_<name> vars", function()
vim.g.aerial_float_row = 10
assert.equals(config["float.row"], 10)
assert.equals(config.float.row, 10)
vim.api.nvim_del_var("aerial_float_row")
end)
@ -48,7 +51,7 @@ describe("config", function()
it("reads the filetype default value for filetype map option", function()
vim.g.aerial = {
open_automatic = {
["_"] = 1,
["_"] = true,
},
}
assert.equals(config.open_automatic(), true)
@ -56,7 +59,7 @@ describe("config", function()
it("reads the filetype value for filetype map option", function()
vim.g.aerial = {
open_automatic = {
fake_ft = 1,
fake_ft = true,
},
}
vim.api.nvim_buf_set_option(0, "filetype", "fake_ft")
@ -65,7 +68,7 @@ describe("config", function()
it("reads the filetype value when using a compound filetype", function()
vim.g.aerial = {
open_automatic = {
fake_ft = 1,
fake_ft = true,
},
}
vim.api.nvim_buf_set_option(0, "filetype", "fake_ft.extension")
@ -95,7 +98,7 @@ describe("config", function()
filter_kind = { foo = 0 },
}
vim.api.nvim_buf_set_option(0, "filetype", "foo")
local fk = config.get_filter_kind_map("foo")
local fk = config.get_filter_kind_map()
assert.equals(true, fk.Class)
assert.equals(true, fk.Function)
end)
@ -105,7 +108,7 @@ describe("config", function()
vim.g.aerial = {
nerd_font = true,
}
assert.equals("", config._get_icons()["Function"])
assert.equals("", config._get_icon("Function", false))
end)
it("reads icons from g:aerial dict var", function()
vim.g.aerial = {
@ -114,24 +117,24 @@ describe("config", function()
Function = "*",
},
}
assert.equals("*", config._get_icons()["Function"])
assert.equals("", config._get_icons()["Method"])
assert.equals("*", config._get_icon("Function", false))
assert.equals("", config._get_icon("Method", false))
end)
-- This is for backwards compatibility with lsp options that used to be in the
-- global namespace
it("reads lsp_ options from g:aerial dict var", function()
assert.equals(config["lsp.update_when_errors"], true)
assert.equals(config.lsp.update_when_errors, true)
vim.g.aerial = {
update_when_errors = false,
}
assert.equals(config["lsp.update_when_errors"], false)
reset()
assert.equals(config.lsp.update_when_errors, false)
end)
it("reads lsp_ options from g:aerial_<name> vars", function()
assert.equals(config["lsp.update_when_errors"], true)
assert.equals(config.lsp.update_when_errors, true)
vim.g.aerial_update_when_errors = false
assert.equals(config["lsp.update_when_errors"], false)
reset()
assert.equals(config.lsp.update_when_errors, false)
vim.api.nvim_del_var("aerial_update_when_errors")
end)