feat: configure interchangeable top and bottom bars

This commit is contained in:
Valdemar Lorenzen
2026-10-12 11:47:31 +02:00
parent 4cfd068f1c
commit 0d3fbc01f8
12 changed files with 776 additions and 340 deletions
+17 -11
View File
@@ -44,13 +44,23 @@ Plugin paths resolve relative to the config file, not the shell's working direct
`config.diff_context_lines = 10` sets how many unchanged aligned lines appear above and below each diff change; it defaults to `10` and accepts nonnegative integers, including `0` for changes only. `config.diff_expand_lines = 15` sets how many additional lines each directional expansion reveals per keypress; it defaults to `15` and accepts positive integers. Both are bounded by the platform's Rust `usize` maximum (`18446744073709551615` on 64-bit systems). Floats, strings, booleans, negative or oversized values, and a zero expansion step reject the whole Ruby configuration.
### Tabline and statusline segments
### Top and bottom bars
`config.tabline` and `config.statusline` each accept ordered `left`, `center`, and `right` arrays of segment names. Left and right groups anchor to their respective edges; the center group is centered on the whole row, not on the gap remaining between the other groups. Array order is display order within each group. Omitted groups and segment options retain defaults; an empty array removes that group.
`config.bar.top` and `config.bar.bottom` each accept ordered `left`, `center`, and `right` arrays of segment names. Both bars accept every builtin and custom segment, with the same anchors, title templates, padding, styled edges, and collision handling. Left and right groups anchor to their respective edges; the center group is centered on the whole row, not on the gap remaining between the other groups. Array order is display order within each group. Omitted groups and segment options retain that bar's defaults; an empty array removes that group.
Use the two setters independently; a combined `config.bar = { ... }` assignment is not supported.
Defaults are unchanged: the top has `left: [:tabs]`; the bottom has `left: [:mode, :pending, :buffer, :diagnostics, :path, :selections, :count, :language]` and `right: [:position]`; other groups are empty. Set the bars independently, as in this swapped layout with status information above and tabs below:
```ruby
Vamp.configure do |config|
config.tabline = {
config.bar.top = {
left: [:mode, :path, :language, :diagnostics, :selections, :count, :pending, :buffer],
center: [], right: [:position],
mode: { padding: 1, leading_edge: "", trailing_edge: "▓▒░" },
position: { padding: 1, leading_edge: "░▒▓", trailing_edge: "" }
}
config.bar.bottom = {
left: [:tabs], center: [], right: [:battery],
tabs: {
title: "{index} {name}{modified}", padding: 1,
@@ -58,23 +68,19 @@ Vamp.configure do |config|
},
battery: { padding: 1, leading_edge: "░▒▓", trailing_edge: "" }
}
config.statusline = {
left: [:mode, :path, :language, :diagnostics, :selections, :count, :pending, :buffer],
center: [], right: [:position],
mode: { padding: 1, leading_edge: "", trailing_edge: "▓▒░" },
position: { padding: 1, leading_edge: "░▒▓", trailing_edge: "" }
}
config.labels = { battery: "bat 38%" }
end
```
The top line's builtin is `:tabs`; the bottom line's builtins are `:mode`, `:path`, `:position`, `:language`, `:diagnostics`, `:selections`, `:count`, `:pending`, and `:buffer`. They provide the mode, file path/dirty state, caret position, detected language, diagnostics, selection count, numeric prefix, pending key sequence, and buffer identity/count. Using a builtin on the wrong line rejects configuration. Defaults retain the existing information and accent styling: the top has `left: [:tabs]`; the bottom has `left: [:mode, :pending, :buffer, :diagnostics, :path, :selections, :count, :language]` and `right: [:position]`; other groups are empty.
Builtins are `:tabs`, `:mode`, `:path`, `:position`, `:language`, `:diagnostics`, `:selections`, `:count`, `:pending`, and `:buffer`, in either bar. They provide tab titles, mode, file path/dirty state, caret position, detected language, diagnostics, selection count, numeric prefix, pending key sequence, and buffer identity/count.
A bar with no visible content consumes no row. For example, `config.bar.top = { left: [], center: [], right: [] }` removes the top bar. A bar whose only visible content is `:tabs` is hidden with a single tab, whether above or below the workspace. Narrow layouts first omit inactive tabs, then optional segments from group ends to avoid overlap; the active tab drops decorations before its title is clipped. The command/search prompt and message area remain separate from both bars.
Each segment's `title` is a literal template supporting only `{index}`, `{name}`, `{path}`, and `{modified}`: the one-based tab index, filename, file path, and dirty marker (`" [+]"` when dirty, `""` when clean). Tabs use each tab's buffer metadata; other segments use the active buffer. Tabs default to `"{index} {name}{modified}"`; an empty title on another segment uses its builtin content or named label value. Templates are not Ruby code and never invoke a render callback.
`padding` adds spaces inside the segment's styling, on both sides of its title, and accepts integers from `0` through `64` (default `1`). `leading_edge` and `trailing_edge` are separate styled glyph strings, not padding; `""` removes an edge. Tabs default to `"░▒▓"` / `"▓▒░"` edges; mode has only the trailing `"▓▒░"` edge, position only the leading `"░▒▓"` edge, and other segments have none. `first_leading_edge` optionally overrides the first occurrence's leading glyphs: for tabs this is the actual first tab, not the first tab remaining after clipping; other segments have one occurrence. Tabs default this override to `""`, suppressing the first tab's left fade. Unicode widths are measured in terminal cells. Edge strings must be single-line and contain no control characters. Unknown style keys, invalid templates, and wrong types reject configuration.
Any other segment name is a custom named value, such as `:battery`. Set initial strings with `config.labels`; an empty value hides the custom segment, including its title override, padding, and edges. Names match `[A-Za-z_][A-Za-z0-9_]*`. Labels are plain text: control characters are replaced rather than interpreted as terminal escapes. The same named value can appear on either line.
Any other segment name is a custom named value, such as `:battery`. Set initial strings with `config.labels`; an empty value hides the custom segment, including its title override, padding, and edges. Names match `[A-Za-z_][A-Za-z0-9_]*`. Labels are plain text: control characters are replaced rather than interpreted as terminal escapes. The same named value can appear in either bar.
Configuration runs once at startup. For live values, use [`editor.label(name, text)` or asynchronous plugin updates](plugins.md#named-label-updates). The editor does not collect battery data, run periodic shell commands, or accept render callbacks; a plugin supplies values and chooses when to refresh them.