feat: support configurable tabline status label
This commit is contained in:
@@ -44,6 +44,20 @@ 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 status label
|
||||
|
||||
Set `config.tabline_label` to a plain string for the right side of the top tab line:
|
||||
|
||||
```ruby
|
||||
Vamp.configure do |config|
|
||||
config.tabline_label = ENV.fetch("VAMP_STATUS", "")
|
||||
end
|
||||
```
|
||||
|
||||
The default `""` disables the label. A nonempty value shows the tab line even with one tab, using the existing accent background and left density fade. The label yields to the active tab when both cannot fit. Unicode is supported; control characters are replaced rather than interpreted as terminal escapes. Non-string values reject configuration.
|
||||
|
||||
Configuration runs once at startup. For live values such as battery charge, load a [Ruby plugin](plugins.md#tabline-status-updates) that supplies updates; the editor does not collect battery data or run periodic shell commands itself.
|
||||
|
||||
## Actions and modes
|
||||
|
||||
`config.core` exposes the built-in actions below as immutable `Action` values; `config.modes` exposes immutable `Mode` values. Misspelled methods fail during configuration evaluation, and `bind` rejects the wrong value type. Built-in action strings, canonical Ex names (such as `"quit"`), shorthand aliases (such as `"q"`), and mode strings are not accepted. Only plugin-owned commands use exact string names (`"greet"`, without `:` or arguments) and can only be bound in Normal mode. Interactive Ex commands remain textual. Ruby provides runtime checks, not compile-time checking; the editor still validates action/mode compatibility.
|
||||
|
||||
@@ -220,6 +220,8 @@ Use `:tab.new` to open the current buffer in a new, focused tab with one pane. E
|
||||
|
||||
Multiple tabs show a single top row with numbered filenames and dirty markers, with padding inside each tab's styling. The active tab has an accent-colored background fading into the terminal background through `░▒▓` / `▓▒░`, except the first tab has no left fade; inactive tabs remain unfilled. Narrow terminals omit the fade before clipping the active title. The status bar uses the same fade around its mode and right-aligned caret position, leaving file metadata unfilled. New/close actions have no default keybinding; for example, `config.bind "leader t", config.core.tab.new`.
|
||||
|
||||
An optional [configured status label](configuration.md#tabline-status-label) occupies the right edge of this same row, with a left fade. It is session-wide and can update through plugins without editing buffers. A nonempty label also shows the row with a single tab; narrow layouts hide the label rather than overlap the active tab.
|
||||
|
||||
Closing a tab refuses dirty buffers whose final displayed owner would disappear, including a comparison's working and return buffers. Buffers displayed in another tab remain open with their edits, even on force-close. Other hidden buffers remain loaded. Closing the last tab quits only if every open buffer is clean; force-close quits without saving.
|
||||
|
||||
## Buffers
|
||||
|
||||
+21
-3
@@ -22,7 +22,7 @@ Callbacks receive an immutable primary-only context snapshot: `editor.text`, `ed
|
||||
|
||||
The Ruby host and examples use [YARD types](https://solargraph.org/guides/yard) for Solargraph navigation and hover. `Vamp.command` declares typed block arguments (`Vamp::Editor`, `String`); the example also annotates them explicitly. Context readers and configuration methods are annotated in [`src/ruby_config.rb`](../src/ruby_config.rb); asynchronous callback/effect contracts live in [`src/ruby_plugin.rb`](../src/ruby_plugin.rb). These comments do not enforce types at runtime; existing boundary validation still does.
|
||||
|
||||
`editor.insert(text)`, `editor.replace(text)`, and `editor.message(text)` queue effects. The context remains the pre-command snapshot even after queuing an effect. All effects are validated before the backend changes anything; buffer changes from one successful invocation form one undo action. Exceptions, process exits, timeouts, malformed replies, and invalid effects disable that plugin and discard its effects. Other plugins and built-ins remain available. Plugin `puts`, explicit `STDOUT.write`, and stderr cannot overwrite the TUI; diagnostic output is discarded, while exceptions appear in the status message.
|
||||
`editor.insert(text)`, `editor.replace(text)`, `editor.message(text)`, and `editor.tabline_label(text)` queue effects. The context remains the pre-command snapshot even after queuing an effect. All effects are validated before the backend changes anything; buffer changes from one successful invocation form one undo action. Exceptions, process exits, timeouts, malformed replies, and invalid effects disable that plugin and discard its effects. Other plugins and built-ins remain available. Plugin `puts`, explicit `STDOUT.write`, and stderr cannot overwrite the TUI; diagnostic output is discarded, while exceptions appear in the status message.
|
||||
|
||||
This is crash isolation, **not a security sandbox**: Ruby configuration and plugins have your user account's filesystem/process permissions. Load only trusted scripts. On Unix, hosts run in their own process groups and their groups are killed on shutdown.
|
||||
|
||||
@@ -40,15 +40,33 @@ A feature declares all capabilities it needs. Registration is atomic: if any req
|
||||
|
||||
`Vamp.on_document { |event| ... }` receives a deeply frozen hash with `"event"` (`"open"`, `"change"`, `"save"`, or `"close"`) and `"document"` (`"id"`, `"revision"`, `"path"`, `"root"`, `"text"`). Already-open documents are delivered at registration; changes include hidden-buffer edits, undo/redo, and plugin edits. Save-as sends close/open for the changed path. `Vamp.on_shutdown { ... }` supplies a shutdown hook. Callbacks must enqueue slow work rather than blocking host input.
|
||||
|
||||
Worker threads call `Vamp.publish(effects, request_id: nil)`. Background effects are `message`, revision-tagged `diagnostics`, or request-correlated `navigate`/`hover`; they cannot edit text. Diagnostics carry `buffer_id`, `revision`, and `items`, each with end-exclusive `start`/`end` positions, `severity` (`error`, `warning`, `information`, `hint`), and `message`. Positions are **zero-based grapheme coordinates**, not bytes, terminal cells, or UTF-16. Empty item lists clear the owner's annotations.
|
||||
Worker threads call `Vamp.publish(effects, request_id: nil)`. Background effects are `message`, `tabline_label`, revision-tagged `diagnostics`, or request-correlated `navigate`/`hover`; they cannot edit text. Diagnostics carry `buffer_id`, `revision`, and `items`, each with end-exclusive `start`/`end` positions, `severity` (`error`, `warning`, `information`, `hint`), and `message`. Positions are **zero-based grapheme coordinates**, not bytes, terminal cells, or UTF-16. Empty item lists clear the owner's annotations.
|
||||
|
||||
Navigation carries the source `buffer_id`/`revision`, destination `path`/`row`/`column`, and the invocation's `editor.request_id` passed to `publish`. Only the latest still-current source request may move the primary. Closed/stale revisions are ignored; malformed current updates retire their owner. Failed navigation reports an error without discarding buffer edits. Hosts use bounded queues/frames; protocol failure or host exit retires commands, bindings, pending requests, and owned diagnostics.
|
||||
|
||||
`hover` carries source `buffer_id`/`revision` and multiline `text`, with the invocation's `editor.request_id` passed to `publish`. Only applicable current requests in Normal mode can show a popup; stale results and replies while another modal list is focused are ignored. Text validates before a batch changes state; empty text clears the popup, and owner retirement dismisses its content.
|
||||
|
||||
## Tabline status updates
|
||||
|
||||
`editor.tabline_label(text)` sets the same session-wide label as `config.tabline_label`. It is a presentation effect: it does not edit text, move carets, change undo history, or dismiss completion. It also works in read-only diff views.
|
||||
|
||||
```ruby
|
||||
Vamp.plugin "tabline_status"
|
||||
|
||||
# @param editor [Vamp::Editor]
|
||||
# @param args [String]
|
||||
Vamp.command "tablabel" do |editor, args|
|
||||
editor.tabline_label(args)
|
||||
end
|
||||
```
|
||||
|
||||
Load this plugin with `config.plugin`, then use `:tablabel bat 38%`; `:tablabel` clears it. For asynchronous data, an `async: true` plugin's worker can publish `{ type: "tabline_label", text: label }` through `Vamp.publish`, without a request ID. Collect data and choose the refresh interval in the plugin, not in a render callback; stop worker threads in `Vamp.on_shutdown`. Updates redraw while idle and persist across tabs, without depending on the originating buffer or revision. The last value remains until overwritten or cleared, including after plugin retirement.
|
||||
|
||||
Labels are plain strings: control characters become replacement glyphs, and an empty string clears the label. Updates share the existing bounded protocol; rejected batches cannot partially change the label.
|
||||
|
||||
## Messaging
|
||||
|
||||
Rust and Ruby exchange UTF-8, newline-delimited JSON objects with `version: 1`. Startup returns `manifest` (name, required capabilities, commands, async opt-in) or `config` (plugin paths/options and bindings). `invoke` requests carry an ID, command, arguments, and context; `result` replies carry the matching ID and typed text effects (`insert`, `replace`, `message`). Async hosts also receive `document`/`shutdown` and send `update` frames with optional request IDs and typed presentation/navigation effects. Errors use `error` frames. Unknown fields, wrong versions, and mismatched replies are rejected. Frames and aggregate effect text are bounded to 4 MiB.
|
||||
Rust and Ruby exchange UTF-8, newline-delimited JSON objects with `version: 1`. Startup returns `manifest` (name, required capabilities, commands, async opt-in) or `config` (plugin paths/options and bindings). `invoke` requests carry an ID, command, arguments, and context; `result` replies carry the matching ID and typed effects (`insert`, `replace`, `message`, `tabline_label`). Async hosts also receive `document`/`shutdown` and send `update` frames with optional request IDs and typed presentation/navigation effects. Errors use `error` frames. Unknown fields, wrong versions, and mismatched replies are rejected. Frames and aggregate effect text are bounded to 4 MiB.
|
||||
|
||||
Before loading a script, Rust supplies a bounded startup JSON envelope containing `core_actions` and `options`. The action catalog initializes the Ruby interface; only the separate user-options object is exposed through `Vamp.options`, including when user options contain a key named `core_actions`.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user