A sidebar with a tree-like outline of symbols from your code, powered by LSP.
https://github.com/hedyhli/outline.nvim/assets/50042066/f66fa661-b66a-4b48-84e8-37920a3d8d2c
Features
:h mods
)Still not sure whether to use this? Read about related plugins.
"hedyhli/outline.nvim"
url = "https://git.sr.ht/~hedy/outline.nvim"
(an
equivalent key to url
for your plugin manager)Lazy.nvim example:
{
"hedyhli/outline.nvim",
config = function()
-- Example mapping to toggle outline
vim.keymap.set("n", "<leader>o", "<cmd>Outline<CR>",
{ desc = "Toggle Outline" })
require("outline").setup {
-- Your setup opts here (leave empty to use defaults)
}
end,
},
Lazy.nvim with lazy-loading example:
{
"hedyhli/outline.nvim",
lazy = true,
cmd = { "Outline", "OutlineOpen" },
keys = { -- Example mapping to toggle outline
{ "<leader>o", "<cmd>Outline<CR>", desc = "Toggle outline" },
},
opts = {
-- Your setup opts here
},
},
This allows Lazy.nvim to lazy-load the plugin on commands Outline
,
OutlineOpen
, and your keybindings.
Call the setup function with your configuration options.
Note that a call to .setup()
is required for this plugin to work
(otherwise you might see this error:
simrat39/symbols-outline.nvim#213).
require("outline").setup({})
The configuration structure has been heavily improved and refactored in this plugin. If you're migrating from the original symbols-outline, see #12 on github under "breaking changes" section.
Check this list if you have any confusion with the terms used in the configuration.
Pass a table to the setup call with your configuration options.
To find out exactly what some of the options do, please see the recipes section at the bottom for screen-recordings/shots.
filter
Include all symbols except kinds String and Variable:
symbols.filter = { 'String', 'Variable', exclude=true }
Include only Function symbols:
symbols.filter = { 'Function' }
Per-filetype filtering example:
symbols.filter = {
default = { 'String', exclude=true },
python = { 'Function', 'Class' },
}
Note how the python filter list and the default filter list is NOT merged.
Setting any filter list to nil
or false
means include all symbols, where a
filter list is an array of strings with an optional exclude
field.
icons
The order in which the sources for icons are checked is:
lspkind
is supported for this option as of now)A fallback is always used if the previous candidate returned a falsey value.
The current list of tested providers are:
javascript
parser for treesitter is requirednorg
parser for treesitter)External providers:
:Outline[!] (✓ bang ✓ mods)
Toggle outline. With bang (!
) the cursor focus stays in your
original window after opening the outline window. Set
outline_window.focus_on_open = false
to always use this behaviour.
You can use command modifiers topleft
/aboveleft
/botright
/belowright
on this command to control how the outline window split is created. Other
modifiers are ignored.
Example:
" in config: position='right'
:topleft Outline " opens with 'topleft vsplit'
:belowright Outline " opens with 'belowright vsplit'
:Outline " opens with 'botright vsplit'
:OutlineOpen[!] (✓ bang ✓ mods)
Open outline. With bang (!
) the cursor focus stays in your original
window after opening the outline window. Set outline_window.focus_on_open = false
to always use this behaviour.
You can use command modifiers topleft
/aboveleft
/botright
/belowright
on this command to control how the outline window split is created. Other
modifiers are ignored.
" in config: position='left'
:aboveleft OutlineOpen " opens with 'aboveleft vsplit'
:belowright OutlineOpen " opens with 'belowright vsplit'
:OutlineOpen " opens with 'topleft vsplit'
If the outline is already open, running this command without bang will focus on the outline window.
:OutlineClose: Close outline
:OutlineFocus: Toggle focus between outline and code/source window
:OutlineFocusOutline: Focus on outline
:OutlineFocusCode: Focus on source window
:OutlineStatus: Display provider and outline window status in a floating window, similar to :LspInfo
:OutlineFollow[!] (✓ bang × mods)
Go to corresponding node in outline based on cursor position in code, and focus on the outline window.
With bang (!
), retain focus on the code window.
This can be understood as the converse of goto_location
(see keymaps).
goto_location
sets cursor of code window to the position of outline window,
whereas this command sets position in outline window to the cursor position of
code window.
With bang, it can be understood as the converse of peek_location
.
This is automatically triggered on events
outline_items.auto_update_events.follow
.
You can also trigger this manually using the restore_location
keymap
(default <C-g>
) from the outline window.
:OutlineRefresh
Trigger refresh of symbols from provider and update outline items.
This is automatically triggered on events
outline_items.auto_update_events.refresh
.
These mappings are active only for the outline window.
You can open a floating window showing the following list of keymaps using the ?
key by default from the outline window.
Key | Action |
---|---|
Esc / q | Close outline |
Enter | Go to symbol location in code |
o | Go to symbol location in code without losing focus |
Shift+Enter | Go to symbol location in code and close outline |
Ctrl+g | Update outline window to focus on code location |
K | Toggles the current symbol preview |
Ctrl+Space | Hover current symbol (provider action) |
r | Rename symbol |
a | Code actions |
h | Fold symbol or parent symbol |
Tab | Toggle fold under cursor |
Shift+Tab | Toggle all folds |
l | Unfold symbol |
W | Fold all symbols |
E | Unfold all symbols |
R | Reset all folding |
Ctrl+k | Go up and peek location |
Ctrl+j | Go down and peek location |
? | Show current keymaps in a floating window |
If you frequently use horizontal splits and need <C-k/j>
to navigate them,
you may want to remap:
keymaps = {
up_and_jump = '<C-p>',
down_and_jump = '<C-n>',
}
Or if you never use arrow keys to move around, you can use:
keymaps = {
up_and_jump = '<up>',
down_and_jump = '<down>',
}
Default:
outline_window = {
winhl = '',
},
Possible highlight groups for the outline window:
Highlight | Description |
---|---|
OutlineCurrent | Current symbol under cursor |
OutlineGuides | Guide markers section in each line of the outline |
OutlineFoldMarker | Fold markers in the outline |
OutlineDetails | Symbol details in virtual text |
OutlineLineno | The Lineno column virtual text |
You can customize any other highlight groups using winhl
, this option is
passed directly to the winhl
vim option unprocessed.
If any of the above highlights have not already been set before outline.setup is called (say by a theme), the following links are used:
Highlight | Link |
---|---|
OutlineGuides | Comment |
OutlineFoldMarker | Normal |
OutlineDetails | Comment |
OutlineLineno | LineNr |
For OutlineCurrent
, foreground is set to String and background CursorLine.
To customize colors of the symbol icons, use the symbols.icons
table. See
config.
preview_window = {
winhl = 'NormalFloat:',
},
Highlight | Link |
---|---|
OutlineHelpTip | Comment |
OutlineStatusFt | Type |
OutlineStatusError | ErrorMsg |
OutlineStatusProvider | Special |
OutlineKeymapHelpKey | Special |
OutlineKeymapHelpDisabled | Comment |
Help windows include:
?
in the outline window:OutlineStatus
Highlight | Description |
---|---|
OutlineJumpHighlight | Indicating cursor position when jumping/focusing, defaults to Visual |
You can also use outline_window.jump_highlight_duration
to customize in milliseconds,
how long the highlight is applied for.
Outline.nvim provides the following public API for use in lua.
require'outline'
setup(opts)
toggle(opts)
Toggle opening/closing of outline window.
If opts.focus_outline=false
, keep focus on previous window.
open(opts)
Open the outline window.
If opts.focus_outline=false
, keep focus on previous window.
close()
Close the outline window.
focus_toggle()
Toggle cursor focus between code and outline window.
focus_outline()
Focus cursor on the outline window.
focus_code()
Focus cursor on the window which the outline is derived from.
is_open()
Return whether the outline window is open.
show_status()
Display provider and outline window status in a floating window.
has_provider()
Returns whether a provider is available.
follow_cursor(opts)
Go to corresponding node in outline based on cursor position in code, and focus on the outline window.
With opts.focus_outline=false
, cursor focus will remain on code window.
This is automatically called on events
outline_items.auto_update_events.follow
from config.
has_focus()
Return whether outline is open and current focus is in outline.
refresh()
Re-request symbols from provider and update outline items.
This is automatically called on events
outline_items.auto_update_events.refresh
from config.
get_breadcrumb(opts)
Return a string concatenated from hovered symbols hierarchy representing code location.
Optional opts table fields:
>
): String for separatorget_symbol(opts)
Return the symbol name of the deepest hovered symbol representing code location.
Optional opts table fields:
To open the outline but don't focus on it, you can use :Outline!
or
:OutlineOpen!
.
This is useful in autocmds, say you have a filetype that, whenever a buffer with that filetype is opened you want to open the outline.
After navigating around in the outline window, you can use <C-g>
(default
mapping for restore_location
) to go back to the corresponding outline
location based on the code location.
To customize the background colors, text colors, and borders, you can use
outline_window.winhl
for the outline window or preview_window.winhl
for the
preview floating window. See highlights.
To fix symbol icon related issues, there are several options. If you use
lspkind
, you can set symbols.icon_source = 'lspkind'
to use lspkind for
fetching icons. You can also use your own function symbols.icon_fetcher
that
takes a string and should return an icon. Otherwise, you can edit the
symbols.icons
table for specifying icons.
The order in which the sources of icons are checked is:
A fallback is always used if the previous candidate returned falsey value.
You can hide an icon for a specific type by returning ""
.
Below is an example where icons are disabled for kind 'Package', and for other icons use lspkind.
symbols = {
icon_fetcher = function(k)
if k == 'Package' then
return ""
end
return false
end,
icon_source = 'lspkind',
}
The icon_fetcher
function may also accept a second parameter, the buffer
number of the code buffer. For example, you can use it to determine the icon
to use based on the filetype.
symbols = {
icon_fetcher = function(kind, bufnr)
local ft = vim.api.nvim_buf_get_option(bufnr, 'ft')
-- ...
end,
}
See this section for other examples of this function.
You can customize the split command used for creating the outline window split
using outline_window.split_command
, such as "topleft vsp"
. See :h windows
Is the outline window too slow when first opening a file? This is usually due to the LSP not being ready when you open outline, hence we have to wait for the LSP response before the outline can be shown. If LSP is ready generally the outline latency is almost negligible.
Behaviour you may want to achieve and the combination of configuration options to achieve it.
Code snippets in this section are to be placed in .setup({ <HERE> })
directly
unless specified otherwise.
(Now a default behaviour, different to symbols-outline.nvim.)
Unfold all others except currently hovered item.
symbol_folding = {
autofold_depth = 1,
auto_unfold = {
hovered = true,
},
},
symbol_folding = {
autofold_depth = false,
},
symbol_folding = {
auto_unfold = {
only = 2,
},
},
auto_unfold.only = 2
:
https://github.com/hedyhli/outline.nvim/assets/50042066/035fadac-ecee-4427-9ee1-795dac215cea
auto_unfold.only = 1
:
https://github.com/hedyhli/outline.nvim/assets/50042066/3a123b7e-ccf6-4278-9a8c-41d2e1865d83
In words "auto unfold nodes when there is only 2 nodes shown in the outline."
For auto_unfold.only = true
: "auto unfold nodes when the root node is the only node left visible in the outline."
Use outline window as a quick-jump window
preview_window = {
auto_preview = true,
},
https://github.com/hedyhli/outline.nvim/assets/50042066/a473d791-d1b9-48e9-917f-b816b564a645
Note that auto-resizing of the preview window is only enabled for auto-preview. Otherwise, close and reopen the preview after resizing the code window.
https://github.com/hedyhli/outline.nvim/assets/50042066/b7f6d2b6-98b3-4557-8143-e49583e99d3b
Alternatively, if you want to automatically navigate to the corresponding code location directly and not use the preview window:
outline_window = {
auto_jump = true,
},
https://github.com/hedyhli/outline.nvim/assets/50042066/3d06e342-97ac-400c-8598-97a9235de66c
Or, you can use keys <C-j>
and <C-k>
to achieve the same effect, whilst not
having auto_jump
on by default.
Hide the extra details after each symbol name
outline_items = {
show_symbol_details = false,
},
You can customize its highlight group by setting OutlineDetails
in
outline_window.winhl
.
Show line numbers next to each symbol to jump to that symbol quickly
outline_items = {
show_symbol_lineno = true,
},
The default highlight group for the line numbers is LineNr
, you can customize
it using outline_window.winhl
: please see highlights.
Hide the cursor within cursorline. This setting changes the cursor color to be
that of Cursorline
when focus is in outline window. As of now guicursor
is
a global option, so outline.nvim has to set and reset responsibly hence this
feature may be unstable. You can inspect
require('outline').state.original_cursor
and set guicursor
accordingly,
though you should almost never need to do this.
outline_window = {
show_cursorline = true,
hide_cursor = true,
}
This will be how the outline window looks like when focused:
Some may find this unhelpful, but one may argue that elements in each row of the outline becomes more readable this way, hence this is an option.
You can write your own function for fetching icons. Here is one such example that simply returns in plain text, the first letter of the given kind.
symbols = {
icon_fetcher = function(kind, bufnr) return kind:sub(1,1) end,
}
The fetcher function, if provided, is checked first before using icon_source
and icons
as fallback.
symbols = {
icon_fetcher = function(kind, bufnr)
local ft = vim.api.nvim_buf_get_option(bufnr, 'ft')
-- ...
end,
}
Disable all icons:
symbols = {
icon_fetcher = function() return "" end,
}
Disable icons for specific kinds, and for others use lspkind:
symbols = {
icon_fetcher = function(k, buf)
if k == 'String' then
return ""
end
return false
end,
icon_source = 'lspkind',
}
In this example, icons are disabled for markdown, and lspkind
is used for
other filetypes.
symbols = {
icon_fetcher = function(k, buf)
local ft = vim.api.nvim_buf_get_option(buf, "ft")
if ft == 'markdown' then
return ""
end
return false
end,
icon_source = "lspkind",
}
Press K
to open the preview, press K
again to focus on the preview window
to make any quick edits, similar to VS Code's reference window "peek editor".
Then use :q
to close the window, and continue browsing the outline.
preview_window = {
live = true,
}
Note that this feature is experimental and may be unstable.
https://github.com/hedyhli/outline.nvim/assets/50042066/183fc5f9-b369-41e2-a831-a4185704d76d
Auto-preview with the feature is also supported, set auto_preview = true
and
press K
to focus on the auto-opened preview window. :q
to quit the window.
Any other recipes you think others may also find useful? Feel free to open a PR.
The following features and fixes are not included in Neovim 0.7.
Sometimes the preview window could be slow in loading. This could be due to the code buffer being large. As of now there are no good solutions in circumventing this problem — currently the entire code buffer is read, and then put into the preview buffer. If only the required portion to preview is read and set instead, there would be highlighting issues (say the calculated starting line was within a markdown code block, so what was previously not supposed to be code is now highlighted as code).
If this poses a problem for you, you should try out the live-preview feature, which uses the code buffer directly for displaying the preview.
Outline.nvim supports opening independent outline windows for different tabpages, but does not support multiple outline windows in the same tabpage as of now. However, this feature is planned. Alternatively, you can use a single outline that auto-updates on buffer switches, which is turned on by default.
The most obvious plugin alternative to Outline.nvim would be Aerial. It provides an outline window with a lot of features that outline.nvim does not have (but might add in the future). That said, outline.nvim also has features that Aerial does not support. I do not find it productive to be listing out the exact details of which, as a table here, since both plugins are in active development and the table would get out of date quickly; Instead, I have listed a few example use-cases where you may want to use Aerial, and others Outline.nvim.
Aerial does a great job at supercharging vim's built-in outline (gO
). It
supports treesitter and manpages which Outline.nvim does not provide by
default. (Note that Aerial also supports Norg through
treesitter like Outline.nvim, but as of writing it does not support JSX like
Outline.nvim does.)
In addition to these, Aerial also supports a AerialNav
window which gives
you a miller column view of symbol hierarchy similar to
nvim-navbuddy. This feature
might never be supported in Outline.nvim because I personally feel that it is
out of scope of a "outline window" plugin, and to keep the codebase simple.
If you don't want to install a second plugin for this feature, you should use
Aerial.
nvim-navic gives you fully customizable breadcrumb section for you winbar/statusline. However, as far as I am aware it only supports LSP. To have other providers built-in you can try Aerial, or dropbar.nvim.
Miller columns popup for LSP navigation. Again as far as I know only LSP is supported.
Clickable breadcrumbs section with support for many sources in addition to LSP. However, it requires Neovim nightly as of writing.
I've heard that this plugin gives you many features previously described all in one plugin. However I have not used this myself so I cannot comment on it more, other than it might only support LSP.
Extremely interesting plugin that gives you a floating window for navigation and quick-edits of locations provided by LSP. However it solves a different problem to Outline.nvim: navigating references and definitions.
Unfortunately I have not used this myself, but it looks pretty good. It might only support LSP.
Treesitter (inspect/edit)
The built-in treesitter module has a :InspectTree
feature that can follow
your cursor around and let you jump to locations by navigating the tree.
Compared to Outline.nvim it may not be as customizable, but it uses
treesitter and can highlight entire ranges of symbols.
If you've read this much, maybe you should subscribe to the breaking changes announcements to get updates when there are breaking changes. It's low-volume, I promise ;)