fix: surface rejected configuration in a floating startup diagnostic

This commit is contained in:
2026-10-09 19:49:50 +02:00
parent de6b4d47f3
commit 788638f34e
6 changed files with 301 additions and 129 deletions
+66 -21
View File
@@ -46,8 +46,8 @@ pub struct EditorSession {
}
impl EditorSession {
/// File errors fail opening; config and plugin errors leave core editing
/// available and are reported in the editor's message.
/// File errors fail opening; rejected configuration leaves core editing
/// available and presents a persistent startup diagnostic.
pub fn open(
file: Option<PathBuf>,
capabilities: Vec<String>,
@@ -57,7 +57,10 @@ impl EditorSession {
let mut notices = Vec::new();
let config = match config {
Some(path) => load_config(path).unwrap_or_else(|error| {
notices.push(format!("config disabled: {error}"));
editor.show_startup_diagnostic(format!(
"{}\n\nConfiguration was rejected; using default settings and bindings.\n\n{error}",
path.display()
));
Config::default()
}),
None => Config::default(),
@@ -2317,7 +2320,17 @@ end
let mut session = fixture.open(&format!(
"Vamp.configure do |config|\n config.leader = \",\"\n config.bind \"i\", nil\n {expression}\nend"
));
assert!(session.editor().message().contains(error), "{expression}");
assert!(
session
.editor()
.startup_diagnostic()
.unwrap()
.lines
.iter()
.any(|line| line.contains(error)),
"{expression}"
);
session.handle(Input::Enter);
session.handle(Input::Char('i'));
session.handle(Input::Char('界'));
session.handle(Input::Escape);
@@ -2338,24 +2351,56 @@ end
}
#[test]
fn broken_config_falls_back_to_core_without_failing_the_session() {
let fixture = Fixture::new();
let mut session = fixture.open("raise 'broken config'");
assert!(
session
fn rejected_config_diagnostic_persists_until_dismissed_and_default_editing_survives() {
for (config, reason, dismiss) in [
("raise 'broken config'", "broken config", Input::Enter),
(
"Vamp.configure { |config| config.lsp['ruby'] = true }",
"ruby",
Input::Escape,
),
] {
let fixture = Fixture::new();
let mut session = fixture.open(config);
let diagnostic = session
.editor()
.message()
.contains("config disabled: Ruby config: RuntimeError: broken config")
);
session.handle(Input::Char('i'));
session.handle(Input::Char('x'));
session.handle(Input::Escape);
session.execute("wq");
assert!(session.editor().should_quit());
assert_eq!(
fs::read_to_string(fixture.0.join("buffer.txt")).unwrap(),
"x"
);
.startup_diagnostic()
.expect("config failure must be visible");
let text = diagnostic.lines.join("\n");
assert!(text.contains(&fixture.0.join("config.rb").display().to_string()));
assert!(text.contains(reason));
session.editor.set_message("unrelated background message");
session.poll();
for input in [
Input::Char('i'),
Input::Paste("accidental edit".into()),
Input::Ctrl('p'),
] {
session.handle(input);
}
assert_eq!(
session
.editor()
.startup_diagnostic()
.unwrap()
.lines
.join("\n"),
text
);
assert_eq!(session.editor().mode(), Mode::Normal);
assert_eq!(session.editor().lines(), &[""]);
session.handle(dismiss);
assert!(session.editor().startup_diagnostic().is_none());
session.handle(Input::Char('i'));
session.handle(Input::Char('x'));
session.handle(Input::Escape);
session.execute("wq");
assert!(session.editor().should_quit());
assert_eq!(
fs::read_to_string(fixture.0.join("buffer.txt")).unwrap(),
"x"
);
}
}
fn drain_until(session: &mut EditorSession, ready: impl Fn(&EditorSession) -> bool) {