Another Neovim statusline written in Lua.
The goal is to provide a visual pleasing and efficient statusline. It started with writing my own statusline. Reason for writing was to learn more about the Neovim ecosystem and having exactly what I want, function and aesthetic wise.
In the meantime it is a quite generic and configurable alternative to other popular statuslines.
Here are some screenshots that might be a bit outdated. See recipes for config examples.
Component | Description |
---|---|
mode | Current mode. Automatically sets vim.opt.showmode = false |
path | Filename and the relative path as well as modified / read-only info. The directory path will be truncated. Can be disabled and configured |
git | Branch and Diff infos (requires gitsigns, mini.diff (diff only) or vim-fugitive (branch only). Uses the git CLI as fallback to retrieve the branch) |
diagnostics | vim.diagnostic infos. This component is event driven and will not poll the information on every statusline draw. |
filetype_lsp | File type and attached LSPs. Attached LSPs are evaluated event driven on LSP attach / detach events. LSP names can be mapped to custom names or disabled using configs.filetype_lsp.map_lsps . |
progress | File progress in %, overall number of lines as well as the cursor column |
recording | Register being used when recording a macro |
searchcount | Search result occurences |
selectioncount | Visually selected characters/lines or matrix |
Which components to show in which section (left
, right
, center
) can be configured.
The components
entries accept function calls and strings so that you can create custom comonents.
See Custom components for an introduction.
Components have a flow direction which means that components on the left have their primary part on the left side and components on the right have their primary part on their right side.
Feel free to create an issue/PR if you want to see anything else implemented.
{
-- Calls `require('slimline').setup({})`
"sschleemilch/slimline.nvim",
opts = {}
},
MiniDeps.now(function()
MiniDeps.add('sschleemilch/slimline.nvim')
require('slimline').setup({})
end)
Optional dependencies:
- gitsigns if you want the
git
component. Otherwise, it will just not be rendered - mini.icons if you want filetype icons
You'll also need to have a patched nerd font for icons and separators.
Tip
You can decide whether you would like to have a global statusline or one for each split by setting vim.opt.laststatus
accordingly
in your settings.
If you decide to not use a global one then you can configure via components_inactive
what will be rendered in the inactive one.
By default, all components
will be shown. Inactive components will use the secondary highlighting for primary parts.
shows an example using default options.
Default Options
{
bold = false, -- makes primary parts bold
-- Global style. Can be overwritten using `configs.<component>.style`
style = 'bg', -- or "fg"
-- Component placement
components = {
left = {
'mode',
'path',
'git',
},
center = {},
right = {
'diagnostics',
'filetype_lsp',
'progress',
},
},
-- Inactive components
-- Uses all `components` by default.
-- E.g. for only showing `path`:
-- components_inactive = {
-- left = { 'path' },
-- right = {},
-- },
components_inactive = {},
-- Component configuration
-- `<component>.style` can be used to overwrite the global 'style'
-- `<component>.sep` can be used to overwrite the global 'sep.left' and `sep.right`
-- `<component>.hl = { primary = ..., secondary = ...}` can be used to overwrite global ones
-- `<component>.follow` can point to another component name to follow its style (e.g. 'progress' following 'mode' by default). Follow can be disabled by setting it to `false`
-- `<component>.trunc_width` can be used to hide a component completely once the window width drops below that value
configs = {
mode = {
verbose = false, -- Selects the `verbose` format
hl = {
normal = 'Type',
visual = 'Keyword',
insert = 'Function',
replace = 'Statement',
command = 'String',
other = 'Function',
},
format = {
['n'] = { verbose = 'NORMAL', short = 'N' },
['v'] = { verbose = 'VISUAL', short = 'V' },
['V'] = { verbose = 'V-LINE', short = 'V-L' },
['\22'] = { verbose = 'V-BLOCK', short = 'V-B' },
['s'] = { verbose = 'SELECT', short = 'S' },
['S'] = { verbose = 'S-LINE', short = 'S-L' },
['\19'] = { verbose = 'S-BLOCK', short = 'S-B' },
['i'] = { verbose = 'INSERT', short = 'I' },
['R'] = { verbose = 'REPLACE', short = 'R' },
['c'] = { verbose = 'COMMAND', short = 'C' },
['r'] = { verbose = 'PROMPT', short = 'P' },
['!'] = { verbose = 'SHELL', short = 'S' },
['t'] = { verbose = 'TERMINAL', short = 'T' },
['U'] = { verbose = 'UNKNOWN', short = 'U' },
},
},
path = {
trunc_width = 60,
directory = true, -- Whether to show the directory
-- truncates the directory path. Can be disabled by setting `truncate = false`
truncate = {
chars = 1, -- number of characters for each path component
full_dirs = 2, -- how many path components to keep unshortened
},
icons = {
folder = ' ',
modified = '',
read_only = '',
},
},
git = {
trunc_width = 120,
icons = {
branch = '',
added = '+',
modified = '~',
removed = '-',
},
},
diagnostics = {
trunc_width = 75,
workspace = false, -- Whether diagnostics should show workspace diagnostics instead of current buffer
icons = {
ERROR = ' ',
WARN = ' ',
HINT = ' ',
INFO = ' ',
},
},
filetype_lsp = {
trunc_width = 95,
-- Map lsp client names to custom names or ignore them by setting to `false`
-- E.g. { ['tsserver'] = 'TS', ['pyright'] = 'Python', ['GitHub Copilot'] = false }
map_lsps = {},
},
selectioncount = {
hl = {
primary = 'Special',
},
icon = ' ',
},
searchcount = {
hl = {
primary = 'Special',
},
icon = ' ',
-- Options to be passed to vim.fn.searchcount, see :h searchcount
options = {
recompute = true,
},
},
progress = {
follow = 'mode',
column = false, -- Enables a secondary section with the cursor column
icon = ' ',
},
recording = {
icon = ' ',
hl = {
primary = 'Special',
},
},
},
-- Spacing configuration
spaces = {
components = ' ', -- string between components
left = ' ', -- string at the start of the line
right = ' ', -- string at the end of the line
},
-- Seperator configuartion
sep = {
hide = {
first = false, -- hides the first separator of the line
last = false, -- hides the last separator of the line
},
left = '', -- left separator of components
right = '', -- right separator of components
},
-- Global highlights
hl = {
base = 'Normal', -- highlight of the background
primary = 'Normal', -- highlight of primary parts (e.g. filename)
secondary = 'Comment', -- highlight of secondary parts (e.g. filepath)
},
-- Hide statusline on filetypes
disabled_filetypes = {},
}
Slimline creates highlight groups with the base highlights chosen in the hl
section of the config.
The default ones should be a safe choice to work well with most colorschemes but of course you can adapt
them to your liking. Depending on the chosen style
(fg or bg) the color will be used as a foreground
or as a background color.
Note
When using a transparent colorscheme and using style=bg
it means that the actual
background will be used as a foreground color for text. Since a transparent theme has
no background color, Slimline will fall back to #000000
for dark themes and to #ffffff
for white themes
Instead of specifying the base highlight group in the config you can also tweak target highlight groups directly.
Make sure to do that before slimline's setup()
.
Internal highlight groups
Highlight Group | Description |
---|---|
Slimline | Background of the line |
SlimlineModeSecondary | Secondary mode content (mode has no secondary content but progress has, which is following mode) |
SlimlineModeNormal | Normal mode |
SlimlineModeNormalSep | Normal mode separator |
SlimlineModeNormalSep2Sec | Normal mode separator to secondary |
SlimlineModeVisual | Visual mode |
SlimlineModeVisualSep | Visual mode separator |
SlimlineModeVisualSep2Sec | Visual mode separator to secondary |
SlimlineModeInsert | Insert mode |
SlimlineModeInsertSep | Insert mode separator |
SlimlineModeInsertSep2Sec | Insert mode separator to secondary |
SlimlineModeReplace | Replace mode |
SlimlineModeReplaceSep | Replace mode separator |
SlimlineModeReplaceSep2Sec | Replace mode separator to secondary |
SlimlineModeCommand | Command mode |
SlimlineModeCommandSep | Command mode separator |
SlimlineModeCommandSep2Sec | Command mode separator to secondary |
SlimlineModeOther | Other mode |
SlimlineModeOtherSep | Other mode separator |
SlimlineModeOtherSep2Sec | Other mode separator to secondary |
SlimlinePathPrimary | Path primary |
SlimlinePathPrimarySep | Path primary separator |
SlimlinePathPrimarySep2Sec | Path primary separator to secondary |
SlimlinePathSecondary | Path secondary |
SlimlinePathSecondarySep | Path secondary separator |
SlimlineGitPrimary | Git primary |
SlimlineGitPrimarySep | Git primary separator |
SlimlineGitPrimarySep2Sec | Git primary separator to secondary |
SlimlineGitSecondary | Git secondary |
SlimlineGitSecondarySep | Git secondary separator |
SlimlineFiletype_lspPrimary | Filetype lsp primary |
SlimlineFiletype_lspPrimarySep | Filetype lsp primary separator |
SlimlineFiletype_lspPrimarySep2Sec | Filetype lsp primary separator to secondary |
SlimlineFiletype_lspSecondary | Filetype lsp secondary |
SlimlineFiletype_lspSecondarySep | Filetype lsp secondary separator |
SlimlineProgressPrimary | Progress primary |
SlimlineProgressPrimarySep | Progress primary separator |
SlimlineProgressPrimarySep2Sec | Progress primary separator to secondary |
SlimlineProgressSecondary | Progress secondary |
SlimlineProgressSecondarySep | Progress secondary separator |
SlimlineRecordingPrimary | Recording primary |
SlimlineRecordingPrimarySep | Recording primary separator |
SlimlineRecordingPrimarySep2Sec | Recording primary separator to secondary |
SlimlineRecordingSecondary | Recording secondary |
SlimlineRecordingSecondarySep | Recording secondary separator |
SlimlineSearchcountPrimary | Searchcount primary |
SlimlineSearchcountPrimarySep | Searchcount primary separator |
SlimlineSearchcountPrimarySep2Sec | Searchcount primary separator to secondary |
SlimlineSearchcountSecondary | Searchcount secondary |
SlimlineSearchcountSecondarySep | Searchcount secondary separator |
SlimlineSelectioncountPrimary | Selectioncount primary |
SlimlineSelectioncountPrimarySep | Selectioncount primary separator |
SlimlineSelectioncountPrimarySep2Sec | Selectioncount primary separator to secondary |
SlimlineSelectioncountSecondary | Selectioncount secondary |
SlimlineSelectioncountSecondarySep | Selectioncount secondary separator |
SlimlineDiagnosticHint | Diagnostic hints |
SlimlineDiagnosticInfo | Diagnostic infos |
SlimlineDiagnosticWarn | Diagnostic warnings |
SlimlineDiagnosticError | Diagnostic errors |
SlimlineDiagnosticVirtualTextHint | Diagnostic hints (for style = bg only) |
SlimlineDiagnosticVirtualTextInfo | Diagnostic infos (for style = bg only) |
SlimlineDiagnosticVirtualTextWarn | Diagnostic warnings (for style = bg only) |
SlimlineDiagnosticVirtualTextError | Diagnostic errors (for style = bg only) |
opts = {
style = 'fg',
bold = true,
hl = {
secondary = 'Comment',
},
configs = {
mode = {
hl = {
normal = 'Type',
visual = 'Keyword',
insert = 'Function',
replace = 'Statement',
command = 'String',
other = 'Function',
},
},
path = {
hl = {
primary = 'Label',
},
},
git = {
hl = {
primary = 'Function',
},
},
filetype_lsp = {
hl = {
primary = 'String',
},
},
},
}
opts = {
style = 'bg',
configs = {
path = {
hl = {
primary = 'Define',
},
},
git = {
hl = {
primary = 'Function',
},
},
filetype_lsp = {
hl = {
primary = 'String',
},
},
},
}
opts = {
style = "fg"
}
opts = {
spaces = {
components = "",
left = "",
right = "",
},
sep = {
hide = {
first = true,
last = true,
},
left = "",
right = "",
},
}
opts = {
spaces = {
components = "─",
left = "─",
right = "─",
},
},
And adding fillchars stl
nvim setting:
vim.opt.fillchars = {
stl = "─",
}
The components
part of the config accepts function calls.
This opens the door extending Slimline with your own content.
Warning
This section uses internal APIs. Since I am not committing to stable internal APIs yet, it can change! Be carfeul when using it. The section will be updated accordingly when interfaces change though.
Let's create a center component using a function like this directly in the config:
opts = {
components = {
center = {
function ()
return "Hello World"
end
},
}
}
It will render to something like this (depending on your colorscheme):
If you want to use internal render functionality of a component (here of the path
component) you can do it like that:
function(active)
return Slimline.highlights.hl_component(
{ primary = 'Hello', secondary = 'World' },
Slimline.highlights.hls.components['path'],
Slimline.get_sep('path'),
'right', -- flow direction (on which side the secondary part will be rendered)
active -- whether the component is active or not
)
end,
Warning
The component to use the highlights and seperator from needs to be configured in your components
since slimline only creates highlights for used ones.
It will now render to that (depending on the config)
Of course you can use Slimline*
highlight groups on your own to create your own styled component
The following lua table is required to be passed if you want to use hl_component()
:
hl = {
primary = {
text = '',
sep = '',
sep2sec = '',
},
secondary = {
text = '',
sep = '',
}
}