A simple statusline/winbar component that uses LSP to show your current code context. Named after the Indian satellite navigation system.
You might also be interested in nvim-navbuddy. Paired with nvim-navic, it will give you complete breadcrumbs experience like in an IDE!
- Neovim >= 0.7.0
- nvim-lspconfig
Install the plugin with your preferred package manager:
use {
"SmiteshP/nvim-navic",
requires = "neovim/nvim-lspconfig"
}
Plug "neovim/nvim-lspconfig"
Plug "SmiteshP/nvim-navic"
For nvim-navic to work, it needs attach to the lsp server. You can pass the nvim-navic's attach
function as on_attach
while setting up the lsp server. You can skip this step if you have enabled auto_attach option during setup.
Note: nvim-navic can attach to only one server per buffer.
Example:
local navic = require("nvim-navic")
require("lspconfig").clangd.setup {
on_attach = function(client, bufnr)
navic.attach(client, bufnr)
end
}
If you're sharing your on-attach
function between lspconfigs, better wrap nvim-navic's attach
function to make sure documentSymbolProvider
is enabled:
Example:
local on_attach = function(client, bufnr)
...
if client.server_capabilities.documentSymbolProvider then
navic.attach(client, bufnr)
end
...
end
require("lspconfig").clangd.setup {
on_attach = on_attach
}
NOTE: You can set
vim.g.navic_silence = true
to supress error messages thrown by nvim-navic. However this is not recommended as the error messages indicate that there is problem in your setup. That is, you are attaching nvim-navic to servers that don't support documentSymbol or are attaching navic to multiple servers for a single buffer.
NOTE: You can set
vim.b.navic_lazy_update_context = true
for specific buffers, where you want the the updates to not occur on everyCursorMoved
event. It should help if you are facing performance issues in large files. Read the docs for example usage of this variable.
Use the setup
function to modify default parameters.
icons
: Indicate the type of symbol captured. Default icons assume you have nerd-fonts.highlight
: If set to true, will add colors to icons and text as defined by highlight groupsNavicIcons*
(NavicIconsFile
,NavicIconsModule
.. etc.),NavicText
andNavicSeparator
.depth_limit
: Maximum depth of context to be shown. If the context hits this depth limit, it is truncated.depth_limit_indicator
: Icon to indicate thatdepth_limit
was hit and the shown context is truncated.safe_output
: Sanitize the output for use in statusline and winbar.lsp
:auto_attach
: Enable to have nvim-navic automatically attach to every LSP for current buffer. Its disabled by default.preference
: Table ranking lsp_servers. Lower the index, higher the priority of the server. If there are more than one server attached to a buffer, nvim-navic will refer to this list to make a decision on which one to use. For example - In case a buffer is attached to clangd and ccls both and the preference list is{ "clangd", "pyright" }
. Then clangd will be preferred.
navic.setup {
icons = {
File = " ",
Module = " ",
Namespace = " ",
Package = " ",
Class = " ",
Method = " ",
Property = " ",
Field = " ",
Constructor = " ",
Enum = "練",
Interface = "練",
Function = " ",
Variable = " ",
Constant = " ",
String = " ",
Number = " ",
Boolean = "◩ ",
Array = " ",
Object = " ",
Key = " ",
Null = "ﳠ ",
EnumMember = " ",
Struct = " ",
Event = " ",
Operator = " ",
TypeParameter = " ",
},
lsp = {
auto_attach = false,
preference = nil,
},
highlight = false,
separator = " > ",
depth_limit = 0,
depth_limit_indicator = "..",
safe_output = true
}
For highlights to work, highlight groups must be defined. These may be defined in your colourscheme, if not you can define them yourself too as shown in below code snippet.
Example highlight definitions
vim.api.nvim_set_hl(0, "NavicIconsFile", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsModule", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsNamespace", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsPackage", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsClass", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsMethod", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsProperty", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsField", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsConstructor", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsEnum", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsInterface", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsFunction", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsVariable", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsConstant", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsString", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsNumber", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsBoolean", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsArray", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsObject", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsKey", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsNull", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsEnumMember", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsStruct", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsEvent", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsOperator", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicIconsTypeParameter", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicText", {default = true, bg = "#000000", fg = "#ffffff"})
vim.api.nvim_set_hl(0, "NavicSeparator", {default = true, bg = "#000000", fg = "#ffffff"})
If you have a font patched with codicon.ttf, you can replicate the look of VSCode breadcrumbs using the following icons
VSCode like icons
navic.setup {
icons = {
File = ' ',
Module = ' ',
Namespace = ' ',
Package = ' ',
Class = ' ',
Method = ' ',
Property = ' ',
Field = ' ',
Constructor = ' ',
Enum = ' ',
Interface = ' ',
Function = ' ',
Variable = ' ',
Constant = ' ',
String = ' ',
Number = ' ',
Boolean = ' ',
Array = ' ',
Object = ' ',
Key = ' ',
Null = ' ',
EnumMember = ' ',
Struct = ' ',
Event = ' ',
Operator = ' ',
TypeParameter = ' '
}
}
nvim-navic does not alter your statusline or winbar on its own. Instead, you are provided with these two functions and its left up to you how you want to incorporate this into your setup.
is_available(bufnr)
: Returns boolean value indicating whether output can be provided.bufnr
is optional, default is current.get_location(opts, bufnr)
: Returns a pretty string with context information. Usingopts
table you can override any of the options, format same as the table forsetup
function. You can also provide abufnr
value to determine which buffer is used to get the code context information, if not provided the current buffer will be used.
Examples
Lua
vim.o.statusline = "%{%v:lua.require'nvim-navic'.get_location()%}"
-- OR
vim.o.winbar = "%{%v:lua.require'nvim-navic'.get_location()%}"
Vimscript
set statusline+=%{%v:lua.require'nvim-navic'.get_location()%}
" OR
set winbar+=%{%v:lua.require'nvim-navic'.get_location()%}
An example feline setup
local navic = require("nvim-navic")
table.insert(components.active[1], {
provider = function()
return navic.get_location()
end,
enabled = function()
return navic.is_available()
end
})
require("feline").setup({components = components})
-- OR
require("feline").winbar.setup({components = components})
An example lualine setup
local navic = require("nvim-navic")
require("lualine").setup({
sections = {
lualine_c = {
{
function()
return navic.get_location()
end,
cond = function()
return navic.is_available()
end
},
}
}
})
An example galaxyline setup
local navic = require("nvim-navic")
local gl = require("galaxyline")
gl.section.right[1]= {
nvimNavic = {
provider = function()
return navic.get_location()
end,
condition = function()
return navic.is_available()
end
}
}
If you have a creative use case and want the raw context data to work with, you can use the following function
get_data(bufnr)
: Returns a table of intermediate representation of data. Table of tables that contain 'kind', 'name' and 'icon' for each context.bufnr
is optional argument, defaults to current buffer.
An example output of get_data
function:
{
{
name = "myclass",
type = "Class",
icon = " ",
kind = 5,
scope = {
start = { line = 1, character = 0 },
end = { line = 10, character = 0 }
}
},
{
name = "mymethod",
type = "Method",
icon = " ",
kind = 6,
scope = {
start = { line = 2, character = 4 },
end = { line = 5, character = 4 }
}
}
}