Last active
December 27, 2022 17:56
-
-
Save MunifTanjim/6f5fcdc9649d52f28403250b26b4d867 to your computer and use it in GitHub Desktop.
Neovim Statusline
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local mod = {} | |
local result = { | |
statusline = {}, | |
tabline = {}, | |
winbar = {}, | |
} | |
local heirline = require("heirline") | |
local heirline_eval_statusline = heirline.eval_statusline | |
local function heirline_eval_statusline_profiler() | |
local start_time = vim.loop.hrtime() | |
local ret = heirline_eval_statusline() | |
local end_time = vim.loop.hrtime() | |
if not result['statusline'][1] then | |
result['statusline'][1] = {} | |
end | |
table.insert(result['statusline'][1], end_time - start_time) | |
return ret | |
end | |
function mod.start() | |
heirline.eval_statusline = heirline_eval_statusline_profiler | |
result.statusline = {} | |
result.tabline = {} | |
result.winbar = {} | |
end | |
function mod.stop() | |
heirline.eval_statusline = heirline_eval_statusline_profiler | |
end | |
function mod.result() | |
for _, bar_type in ipairs({ "statusline", "tabline", "winbar" }) do | |
for id, bar in pairs(result[bar_type]) do | |
local redraw = #bar | |
local total_time_ns = 0 | |
for _, time_ns in ipairs(bar) do | |
total_time_ns = total_time_ns + time_ns | |
end | |
local total_time_ms = total_time_ns / 1e6 | |
print( | |
string.format( | |
"%s(id: %2s) redraw(total: %5s per_ms: %9.6f) time(total: %12.6f per_redraw: %8.6f)", | |
bar_type, | |
id, | |
redraw, | |
redraw / total_time_ms, | |
total_time_ms, | |
total_time_ms / redraw | |
) | |
) | |
end | |
end | |
end | |
return mod |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local conditions = require("heirline.conditions") | |
local utils = require("heirline.utils") | |
local colors = { | |
bright_bg = utils.get_highlight("Folded").bg, | |
bright_fg = utils.get_highlight("Folded").fg, | |
red = utils.get_highlight("DiagnosticError").fg, | |
dark_red = utils.get_highlight("DiffDelete").bg, | |
green = utils.get_highlight("String").fg, | |
blue = utils.get_highlight("Function").fg, | |
gray = utils.get_highlight("NonText").fg, | |
orange = utils.get_highlight("Constant").fg, | |
purple = utils.get_highlight("Statement").fg, | |
cyan = utils.get_highlight("Special").fg, | |
diag_warn = utils.get_highlight("DiagnosticWarn").fg, | |
diag_error = utils.get_highlight("DiagnosticError").fg, | |
diag_hint = utils.get_highlight("DiagnosticHint").fg, | |
diag_info = utils.get_highlight("DiagnosticInfo").fg, | |
-- git_del = utils.get_highlight("diffDeleted").fg, | |
-- git_add = utils.get_highlight("diffAdded").fg, | |
-- git_change = utils.get_highlight("diffChanged").fg, | |
} | |
require("heirline").load_colors(colors) | |
local ViMode = { | |
-- get vim current mode, this information will be required by the provider | |
-- and the highlight functions, so we compute it only once per component | |
-- evaluation and store it as a component attribute | |
init = function(self) | |
self.mode = vim.fn.mode(1) -- :h mode() | |
-- execute this only once, this is required if you want the ViMode | |
-- component to be updated on operator pending mode | |
if not self.once then | |
vim.api.nvim_create_autocmd("ModeChanged", { | |
pattern = "*:*o", | |
command = "redrawstatus", | |
}) | |
self.once = true | |
end | |
end, | |
-- Now we define some dictionaries to map the output of mode() to the | |
-- corresponding string and color. We can put these into `static` to compute | |
-- them at initialisation time. | |
static = { | |
mode_names = { -- change the strings if you like it vvvvverbose! | |
n = "N", | |
no = "N?", | |
nov = "N?", | |
noV = "N?", | |
["no\22"] = "N?", | |
niI = "Ni", | |
niR = "Nr", | |
niV = "Nv", | |
nt = "Nt", | |
v = "V", | |
vs = "Vs", | |
V = "V_", | |
Vs = "Vs", | |
["\22"] = "^V", | |
["\22s"] = "^V", | |
s = "S", | |
S = "S_", | |
["\19"] = "^S", | |
i = "I", | |
ic = "Ic", | |
ix = "Ix", | |
R = "R", | |
Rc = "Rc", | |
Rx = "Rx", | |
Rv = "Rv", | |
Rvc = "Rv", | |
Rvx = "Rv", | |
c = "C", | |
cv = "Ex", | |
r = "...", | |
rm = "M", | |
["r?"] = "?", | |
["!"] = "!", | |
t = "T", | |
}, | |
mode_colors = { | |
n = "red", | |
i = "green", | |
v = "cyan", | |
V = "cyan", | |
["\22"] = "cyan", | |
c = "orange", | |
s = "purple", | |
S = "purple", | |
["\19"] = "purple", | |
R = "orange", | |
r = "orange", | |
["!"] = "red", | |
t = "red", | |
}, | |
}, | |
-- We can now access the value of mode() that, by now, would have been | |
-- computed by `init()` and use it to index our strings dictionary. | |
-- note how `static` fields become just regular attributes once the | |
-- component is instantiated. | |
-- To be extra meticulous, we can also add some vim statusline syntax to | |
-- control the padding and make sure our string is always at least 2 | |
-- characters long. Plus a nice Icon. | |
provider = function(self) | |
return " %2(" .. self.mode_names[self.mode] .. "%)" | |
end, | |
-- Same goes for the highlight. Now the foreground will change according to the current mode. | |
hl = function(self) | |
local mode = self.mode:sub(1, 1) -- get only the first mode character | |
return { fg = self.mode_colors[mode], bold = true } | |
end, | |
-- Re-evaluate the component only on ModeChanged event! | |
-- This is not required in any way, but it's there, and it's a small | |
-- performance improvement. | |
update = { | |
"ModeChanged", | |
}, | |
} | |
local FileNameBlock = { | |
-- let's first set up some attributes needed by this component and it's children | |
init = function(self) | |
self.filename = vim.api.nvim_buf_get_name(0) | |
end, | |
} | |
-- We can now define some children separately and add them later | |
local FileIcon = { | |
init = function(self) | |
local filename = self.filename | |
local extension = vim.fn.fnamemodify(filename, ":e") | |
self.icon, self.icon_color = require("nvim-web-devicons").get_icon_color(filename, extension, { default = true }) | |
end, | |
provider = function(self) | |
return self.icon and (self.icon .. " ") | |
end, | |
hl = function(self) | |
return { fg = self.icon_color } | |
end, | |
} | |
local FileName = { | |
provider = function(self) | |
-- first, trim the pattern relative to the current directory. For other | |
-- options, see :h filename-modifers | |
local filename = vim.fn.fnamemodify(self.filename, ":.") | |
if filename == "" then | |
return "[No Name]" | |
end | |
-- now, if the filename would occupy more than 1/4th of the available | |
-- space, we trim the file path to its initials | |
-- See Flexible Components section below for dynamic truncation | |
if not conditions.width_percent_below(#filename, 0.25) then | |
filename = vim.fn.pathshorten(filename) | |
end | |
return filename | |
end, | |
hl = { fg = utils.get_highlight("Directory").fg }, | |
} | |
local FileFlags = { | |
{ | |
condition = function() | |
return vim.bo.modified | |
end, | |
provider = "[+]", | |
hl = { fg = "green" }, | |
}, | |
{ | |
condition = function() | |
return not vim.bo.modifiable or vim.bo.readonly | |
end, | |
provider = "", | |
hl = { fg = "orange" }, | |
}, | |
} | |
-- Now, let's say that we want the filename color to change if the buffer is | |
-- modified. Of course, we could do that directly using the FileName.hl field, | |
-- but we'll see how easy it is to alter existing components using a "modifier" | |
-- component | |
local FileNameModifer = { | |
hl = function() | |
if vim.bo.modified then | |
-- use `force` because we need to override the child's hl foreground | |
return { fg = "cyan", bold = true, force = true } | |
end | |
end, | |
} | |
-- let's add the children to our FileNameBlock component | |
FileNameBlock = utils.insert( | |
FileNameBlock, | |
FileIcon, | |
utils.insert(FileNameModifer, FileName), -- a new table where FileName is a child of FileNameModifier | |
FileFlags, | |
{ provider = "%<" } -- this means that the statusline is cut here when there's not enough space | |
) | |
local FileType = { | |
provider = function() | |
return string.upper(vim.bo.filetype) | |
end, | |
hl = { fg = utils.get_highlight("Type").fg, bold = true }, | |
} | |
local Ruler = { | |
provider = "%7(%l/%3L%):%2c %P", | |
} | |
ViMode = utils.surround({ "", "" }, "bright_bg", { ViMode }) | |
local Align = { provider = "%=" } | |
local Space = { provider = " " } | |
local DefaultStatusline = { | |
ViMode, | |
Space, | |
FileNameBlock, | |
Space, | |
Space, | |
Align, | |
Space, | |
Space, | |
Space, | |
FileType, | |
Space, | |
Ruler, | |
Space, | |
} | |
local StatusLines = { | |
hl = function() | |
if conditions.is_active() then | |
return "StatusLine" | |
else | |
return "StatusLineNC" | |
end | |
end, | |
-- the first statusline with no condition, or which condition returns true is used. | |
-- think of it as a switch case with breaks to stop fallthrough. | |
fallthrough = false, | |
DefaultStatusline, | |
} | |
require("heirline").setup(StatusLines) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local color = require("config.color") | |
local core = require("nui.bar.core") | |
local Bar = require("nougat.bar") | |
local bar_util = require("nougat.bar.util") | |
local Item = require("nougat.item") | |
local sep = require("nougat.separator") | |
local nut = { | |
buf = { | |
diagnostic_count = require("nougat.nut.buf.diagnostic_count").create, | |
fileencoding = require("nougat.nut.buf.fileencoding").create, | |
fileformat = require("nougat.nut.buf.fileformat").create, | |
filename = require("nougat.nut.buf.filename").create, | |
filestatus = require("nougat.nut.buf.filestatus").create, | |
filetype = require("nougat.nut.buf.filetype").create, | |
wordcount = require("nougat.nut.buf.wordcount").create, | |
}, | |
git = { | |
branch = require("nougat.nut.git.branch").create, | |
}, | |
tab = { | |
tablist = { | |
tabs = require("nougat.nut.tab.tablist").create, | |
close = require("nougat.nut.tab.tablist.close").create, | |
icon = require("nougat.nut.tab.tablist.icon").create, | |
label = require("nougat.nut.tab.tablist.label").create, | |
modified = require("nougat.nut.tab.tablist.modified").create, | |
}, | |
}, | |
mode = require("nougat.nut.mode").create, | |
ruler = require("nougat.nut.ruler").create, | |
spacer = require("nougat.nut.spacer").create, | |
} | |
vim.o.rulerformat = table.concat({ | |
core.code("p"), | |
"%% L:", | |
core.code("l"), | |
"/", | |
core.code("L"), | |
" C:", | |
core.code("v"), | |
}) | |
local breakpoint = { l = 1, m = 2, s = 3 } | |
local breakpoints = { [breakpoint.l] = math.huge, [breakpoint.m] = 128, [breakpoint.s] = 80 } | |
local stl = Bar("statusline", { breakpoints = breakpoints }) | |
local mode = nut.mode({ | |
prefix = " ", | |
suffix = " ", | |
config = { | |
highlight = { | |
normal = { | |
fg = color.dark.bg, | |
}, | |
visual = { | |
bg = color.dark.orange, | |
fg = color.dark.bg, | |
}, | |
insert = { | |
bg = color.dark.blue, | |
fg = color.dark.bg, | |
}, | |
replace = { | |
bg = color.dark.purple, | |
fg = color.dark.bg, | |
}, | |
commandline = { | |
bg = color.dark.green, | |
fg = color.dark.bg, | |
}, | |
terminal = { | |
bg = color.dark.accent.green, | |
fg = color.dark.bg, | |
}, | |
inactive = {}, | |
}, | |
}, | |
}) | |
stl:add_item(mode) | |
stl:add_item(core.truncation_point()) | |
stl:add_item(nut.git.branch({ | |
hl = { bg = color.dark.bg3, fg = color.dark.fg1 }, | |
prefix = { " ", " " }, | |
suffix = " ", | |
})) | |
local filestatus = nut.buf.filestatus({ | |
prefix = " ", | |
config = { | |
modified = "", | |
nomodifiable = "", | |
readonly = "", | |
sep = " ", | |
}, | |
}) | |
stl:add_item(filestatus) | |
stl:add_item(nut.buf.filename({ | |
prefix = " ", | |
suffix = " ", | |
config = { | |
modifier = ":.", | |
[breakpoint.m] = { | |
format = function(name) | |
return table.concat({ vim.fn.pathshorten(vim.fn.fnamemodify(name, ":h")), "/", vim.fn.fnamemodify(name, ":t") }) | |
end, | |
}, | |
[breakpoint.s] = { modifier = ":t", format = false }, | |
}, | |
})) | |
stl:add_item(core.truncation_point()) | |
stl:add_item(nut.spacer()) | |
stl:add_item(nut.buf.filetype({ | |
prefix = " ", | |
suffix = " ", | |
})) | |
stl:add_item(nut.buf.diagnostic_count({ | |
hidden = function(item, ctx) | |
return item.cache[ctx.bufnr][item:config(ctx).severity] == 0 | |
end, | |
hl = { bg = color.dark.bg3 }, | |
prefix = " ", | |
suffix = " ", | |
config = { | |
error = { prefix = " ", fg = color.dark.red }, | |
warn = { prefix = " ", fg = color.dark.yellow }, | |
info = { prefix = " ", fg = color.dark.blue }, | |
hint = { prefix = " ", fg = color.dark.green }, | |
}, | |
})) | |
stl:add_item(nut.buf.fileencoding({ | |
hidden = function(_, ctx) | |
return vim.bo[ctx.bufnr].fileencoding == "utf-8" | |
end, | |
prefix = " ", | |
suffix = " ", | |
})) | |
stl:add_item(nut.buf.fileformat({ | |
hidden = function(_, ctx) | |
return vim.bo[ctx.bufnr].fileformat == "unix" | |
end, | |
hl = { bg = color.dark.bg3, fg = "fg" }, | |
prefix = " ", | |
suffix = " ", | |
config = { | |
text = { | |
dos = "", | |
unix = "", | |
mac = "", | |
}, | |
}, | |
})) | |
local wordcount_enabled = { | |
markdown = true, | |
} | |
stl:add_item(nut.buf.wordcount({ | |
hidden = function(_, ctx) | |
return not wordcount_enabled[vim.bo[ctx.bufnr].filetype] | |
end, | |
hl = mode, | |
sep_left = sep.space(), | |
config = { | |
format = function(count) | |
return string.format("%d Word%s", count, count > 1 and "s" or "") | |
end, | |
}, | |
})) | |
stl:add_item(nut.ruler({ | |
hl = mode, | |
sep_left = sep.space(), | |
suffix = " ", | |
})) | |
local stl_inactive = Bar("statusline") | |
stl_inactive:add_item(mode) | |
stl_inactive:add_item(core.truncation_point()) | |
stl_inactive:add_item(filestatus) | |
stl_inactive:add_item(nut.buf.filename({ | |
prefix = " ", | |
suffix = " ", | |
})) | |
bar_util.set_statusline(function(ctx) | |
return ctx.is_focused and stl or stl_inactive | |
end) |
Author
MunifTanjim
commented
Dec 27, 2022
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment