feat: support configurable tabline status label

This commit is contained in:
Valdemar Lorenzen
2026-10-12 10:56:30 +02:00
parent 05d62aa4a5
commit d55b7b65d2
10 changed files with 526 additions and 32 deletions
+83 -4
View File
@@ -220,6 +220,7 @@ impl EditorSession {
));
}
editor.set_diff_context_options(config.diff_context_lines, config.diff_expand_lines);
editor.set_tabline_label(config.tabline_label);
let mut plugins = BTreeMap::new();
for configured in config.plugins {
let path = configured.path;
@@ -756,6 +757,11 @@ impl EditorSession {
}
UpdateEffect::Completion { .. } => {}
UpdateEffect::Message { .. } => {}
UpdateEffect::TablineLabel { text } => {
if text.len() > 4 * 1024 * 1024 {
return Err("Tabline label exceeds 4 MiB".into());
}
}
}
}
Ok(())
@@ -851,6 +857,9 @@ impl EditorSession {
changed |= self.editor.message() != text;
self.editor.set_message(text);
}
UpdateEffect::TablineLabel { text } => {
changed |= self.editor.set_tabline_label(text);
}
}
}
self.editor.refresh_live_diff();
@@ -878,13 +887,19 @@ impl EditorSession {
}
if effects
.iter()
.all(|effect| matches!(effect, Effect::Message(_)))
.all(|effect| matches!(effect, Effect::Message(_) | Effect::TablineLabel(_)))
{
let mut changed = false;
for effect in effects {
if let Effect::Message(text) = effect {
changed |= self.editor.message() != text;
self.editor.set_message(text);
match effect {
Effect::Message(text) => {
changed |= self.editor.message() != text;
self.editor.set_message(text);
}
Effect::TablineLabel(text) => {
changed |= self.editor.set_tabline_label(text);
}
_ => unreachable!("presentation-only effects checked above"),
}
}
return Ok(changed);
@@ -2980,6 +2995,70 @@ end
drain_until(session, |session| session.editor().message() == delivered);
}
#[test]
fn tabline_updates_survive_tab_switches_and_remain_atomic_in_readonly_views() {
let fixture = Fixture::new();
let mut session = fixture.asynchronous(None);
publish(
&mut session,
vec![serde_json::json!({
"type": "tabline_label", "text": "bat 37%"
})],
);
drain_until(&mut session, |session| {
session.editor().tabline_label() == "bat 37%"
});
let gate = fixture.0.join("label.gate");
hold(
&mut session,
&gate,
vec![serde_json::json!({
"type": "tabline_label", "text": "bat 38%"
})],
);
barrier(&mut session, "label request received");
session.execute("tab.new");
release(&mut session, &gate);
assert_eq!(session.editor().tabline_label(), "bat 38%");
session.execute("tab.previous");
assert_eq!(session.editor().tabline_label(), "bat 38%");
let update = |text: &str| PluginUpdate {
request_id: None,
effects: vec![UpdateEffect::TablineLabel { text: text.into() }],
};
assert!(
!session
.apply_update("generic_fixture", update("bat 38%"))
.unwrap()
);
let rejected = PluginUpdate {
request_id: None,
effects: vec![
UpdateEffect::TablineLabel {
text: "must not leak".into(),
},
UpdateEffect::SemanticTokens {
buffer_id: session.editor().buffer_id(),
revision: session.editor().text_revision(),
tokens: None,
},
],
};
assert!(session.apply_update("generic_fixture", rejected).is_err());
assert_eq!(session.editor().tabline_label(), "bat 38%");
session.editor.execute("diff");
assert!(session.editor.mutation_blocked());
assert!(
session
.apply_update("generic_fixture", update("bat\t39%\n"))
.unwrap()
);
assert_eq!(session.editor().tabline_label(), "bat�39%�");
assert_eq!(session.editor().lines(), [""]);
assert!(session.apply_update("generic_fixture", update("")).unwrap());
assert_eq!(session.editor().tabline_label(), "");
}
#[test]
fn async_documents_include_unnamed_hidden_versions_real_saves_renames_and_closes() {
let fixture = Fixture::new();