feat: redesign status chrome and floating prompts
This commit is contained in:
+145
-4
@@ -91,7 +91,20 @@ loop do
|
||||
'tokenModifiers' => ['custom', 'readonly', 'declaration', 'async', 'defaultLibrary']},
|
||||
'range' => true, 'full' => mode.include?('range') ? false : {'delta' => true}}
|
||||
end
|
||||
result(initialize_id, {'capabilities' => capabilities})
|
||||
response = {'capabilities' => capabilities}
|
||||
response['serverInfo'] = {'name' => "Fixture λ #{File.basename(log, '.jsonl')}"}
|
||||
case mode
|
||||
when 'name-absent' then response.delete('serverInfo')
|
||||
when 'name-empty' then response['serverInfo']['name'] = ''
|
||||
when 'name-blank' then response['serverInfo']['name'] = ' '
|
||||
when 'name-controls' then response['serverInfo']['name'] = "bad\e\nname"
|
||||
when 'name-edge-controls' then response['serverInfo']['name'] = "\nFixture\n"
|
||||
when 'name-type' then response['serverInfo']['name'] = 42
|
||||
when 'initialize-error'
|
||||
emit({'id' => initialize_id, 'error' => {'code' => -32603, 'message' => 'fixture initialization failed'}})
|
||||
when 'initialize-invalid' then response['capabilities'] = nil
|
||||
end
|
||||
result(initialize_id, response) unless mode == 'initialize-error'
|
||||
initialized_sent = true
|
||||
end
|
||||
if !held.empty? && File.exist?(release) && ['stale', 'hover-stale'].include?(mode)
|
||||
@@ -300,7 +313,7 @@ impl Fixture {
|
||||
fn open(&self, mode: &str) -> EditorSession {
|
||||
let server = self.root.join("server.rb");
|
||||
fs::write(&server, SERVER).unwrap();
|
||||
let command = if mode == "spawn" {
|
||||
let mut command = if mode == "spawn" {
|
||||
json!([self.root.join("unavailable-language-server")])
|
||||
} else {
|
||||
json!([
|
||||
@@ -314,6 +327,12 @@ impl Fixture {
|
||||
self.alternate
|
||||
])
|
||||
};
|
||||
if mode == "name-absent" {
|
||||
command
|
||||
.as_array_mut()
|
||||
.unwrap()
|
||||
.insert(0, json!("/usr/bin/env"));
|
||||
}
|
||||
let options = json!({
|
||||
"extensions": if mode == "irrelevant" { vec!["myl"] } else { vec!["rs"] },
|
||||
"command": command,
|
||||
@@ -492,6 +511,106 @@ fn edit_target(session: &mut EditorSession, fixture: &Fixture) -> usize {
|
||||
id
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_ready_names_wait_for_initialization_and_follow_only_the_focused_buffer() {
|
||||
let fixture = Fixture::new();
|
||||
let mut session = fixture.open("controlled-gated");
|
||||
assert!(session.editor().lsp_names().is_empty());
|
||||
assert_eq!(session.editor().lsp_label(), "");
|
||||
until(&mut session, &fixture, |_| {
|
||||
fixture.method_count("initialize") == 1
|
||||
});
|
||||
assert!(session.editor().lsp_names().is_empty());
|
||||
|
||||
let unserved = fixture.root.join("notes.txt");
|
||||
fs::write(&unserved, "unserved\n").unwrap();
|
||||
session.execute(&format!("edit {}", unserved.display()));
|
||||
let unserved_id = session.editor().buffer_id();
|
||||
fixture.release();
|
||||
until(&mut session, &fixture, |_| {
|
||||
fixture.method_count("textDocument/didOpen") == 1
|
||||
});
|
||||
session.poll();
|
||||
assert!(session.editor().lsp_names().is_empty());
|
||||
assert_eq!(session.editor().lsp_label(), "");
|
||||
|
||||
session.execute("buffer 1");
|
||||
assert_eq!(session.editor().lsp_names(), ["Fixture λ protocol"]);
|
||||
assert_eq!(session.editor().lsp_label(), "Fixture λ protocol");
|
||||
let revision = session.editor().text_revision();
|
||||
let cursor = session.editor().cursor();
|
||||
session.poll();
|
||||
assert_eq!(session.editor().text_revision(), revision);
|
||||
assert_eq!(session.editor().cursor(), cursor);
|
||||
assert_eq!(session.editor().mode(), Mode::Normal);
|
||||
|
||||
session.execute(&format!("buffer {unserved_id}"));
|
||||
assert!(session.editor().lsp_names().is_empty());
|
||||
assert_eq!(session.editor().lsp_label(), "");
|
||||
session.execute("buffer 1");
|
||||
assert_eq!(session.editor().lsp_names(), ["Fixture λ protocol"]);
|
||||
fixture.control(0, 1, json!({"action": "die"}));
|
||||
until(&mut session, &fixture, |session| {
|
||||
session.editor().message().contains("server stopped")
|
||||
});
|
||||
assert!(session.editor().lsp_names().is_empty());
|
||||
assert_eq!(session.editor().lsp_label(), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_readiness_alone_requests_a_redraw_without_document_or_message_changes() {
|
||||
let fixture = Fixture::new();
|
||||
let mut session = fixture.open("controlled-gated");
|
||||
until(&mut session, &fixture, |_| {
|
||||
fixture.method_count("initialize") == 1
|
||||
});
|
||||
assert!(session.editor().lsp_names().is_empty());
|
||||
let message = session.editor().message().to_owned();
|
||||
let revision = session.editor().text_revision();
|
||||
let cursor = session.editor().cursor();
|
||||
fixture.release();
|
||||
let deadline = Instant::now() + Duration::from_secs(5);
|
||||
loop {
|
||||
let changed = session.poll();
|
||||
if !session.editor().lsp_names().is_empty() {
|
||||
assert!(changed, "readiness must redraw even without plugin effects");
|
||||
break;
|
||||
}
|
||||
assert!(
|
||||
Instant::now() < deadline,
|
||||
"fixture server did not become ready"
|
||||
);
|
||||
thread::sleep(Duration::from_millis(2));
|
||||
}
|
||||
assert_eq!(session.editor().lsp_names(), ["Fixture λ protocol"]);
|
||||
assert_eq!(session.editor().message(), message);
|
||||
assert_eq!(session.editor().text_revision(), revision);
|
||||
assert_eq!(session.editor().cursor(), cursor);
|
||||
assert_eq!(session.editor().mode(), Mode::Normal);
|
||||
assert_eq!(session.editor().diagnostics().count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_ready_names_fall_back_to_executable_only_for_missing_or_invalid_server_names() {
|
||||
for mode in [
|
||||
"name-absent",
|
||||
"name-empty",
|
||||
"name-blank",
|
||||
"name-controls",
|
||||
"name-edge-controls",
|
||||
"name-type",
|
||||
] {
|
||||
let fixture = Fixture::new();
|
||||
let mut session = fixture.open(mode);
|
||||
until(&mut session, &fixture, |session| {
|
||||
!session.editor().lsp_names().is_empty()
|
||||
});
|
||||
let expected = if mode == "name-absent" { "env" } else { "ruby" };
|
||||
assert_eq!(session.editor().lsp_names(), [expected], "{mode}");
|
||||
assert_eq!(session.editor().lsp_label(), expected, "{mode}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_framing_lifecycle_diagnostics_and_unicode_dirty_definition() {
|
||||
let fixture = Fixture::new();
|
||||
@@ -885,6 +1004,8 @@ fn lsp_server_failures_and_unsupported_results_leave_native_editor_available() {
|
||||
"spawn",
|
||||
"malformed",
|
||||
"dead",
|
||||
"initialize-error",
|
||||
"initialize-invalid",
|
||||
"unsupported",
|
||||
"remote",
|
||||
"bounds",
|
||||
@@ -892,7 +1013,10 @@ fn lsp_server_failures_and_unsupported_results_leave_native_editor_available() {
|
||||
] {
|
||||
let fixture = Fixture::new();
|
||||
let mut session = fixture.open(mode);
|
||||
if matches!(mode, "spawn" | "malformed" | "dead") {
|
||||
if matches!(
|
||||
mode,
|
||||
"spawn" | "malformed" | "dead" | "initialize-error" | "initialize-invalid"
|
||||
) {
|
||||
until(&mut session, &fixture, |session| {
|
||||
session.editor().message().contains(if mode == "spawn" {
|
||||
"failed to start"
|
||||
@@ -901,6 +1025,8 @@ fn lsp_server_failures_and_unsupported_results_leave_native_editor_available() {
|
||||
})
|
||||
});
|
||||
assert_eq!(session.editor().diagnostics().count(), 0);
|
||||
assert!(session.editor().lsp_names().is_empty(), "{mode}");
|
||||
assert_eq!(session.editor().lsp_label(), "", "{mode}");
|
||||
} else {
|
||||
until(&mut session, &fixture, |session| {
|
||||
session.editor().diagnostics().count() == 2
|
||||
@@ -1285,11 +1411,13 @@ fn publish(
|
||||
fn lsp_python_stack_merges_in_assignment_order_and_routes_positions_only_to_ty() {
|
||||
let fixture = Fixture::new();
|
||||
let mut session = fixture.open_stack(["controlled-gated", "controlled"], true);
|
||||
until(&mut session, &fixture, |_| {
|
||||
until(&mut session, &fixture, |session| {
|
||||
fixture.worker_count(0, "initialize") == 1
|
||||
&& fixture.worker_count(1, "textDocument/didOpen") == 1
|
||||
&& session.editor().lsp_names() == ["Fixture λ protocol-1"]
|
||||
});
|
||||
assert_eq!(fixture.worker_count(0, "textDocument/didOpen"), 0);
|
||||
assert_eq!(session.editor().lsp_label(), "Fixture λ protocol-1");
|
||||
session.handle(Input::Char('A'));
|
||||
session.handle(Input::Char('!'));
|
||||
session.handle(Input::Escape);
|
||||
@@ -1298,9 +1426,14 @@ fn lsp_python_stack_merges_in_assignment_order_and_routes_positions_only_to_ty()
|
||||
fixture.release();
|
||||
until(&mut session, &fixture, |session| {
|
||||
session.editor().hover().is_some()
|
||||
&& session.editor().lsp_names() == ["Fixture λ protocol", "Fixture λ protocol-1"]
|
||||
});
|
||||
assert_eq!(fixture.worker_count(0, "textDocument/hover"), 1);
|
||||
assert_eq!(fixture.worker_count(1, "textDocument/hover"), 0);
|
||||
assert_eq!(
|
||||
session.editor().lsp_label(),
|
||||
"Fixture λ protocol + Fixture λ protocol-1"
|
||||
);
|
||||
for worker in 0..2 {
|
||||
let frames = fixture.worker_frames(worker);
|
||||
let initialize = frames
|
||||
@@ -1551,8 +1684,16 @@ fn lsp_python_stack_failures_retire_only_the_offender_in_active_and_parked_buffe
|
||||
});
|
||||
}
|
||||
assert_eq!(diagnostic_messages(&session), ["healthy target"]);
|
||||
let name = if survivor == 0 {
|
||||
"Fixture λ protocol"
|
||||
} else {
|
||||
"Fixture λ protocol-1"
|
||||
};
|
||||
assert_eq!(session.editor().lsp_names(), [name]);
|
||||
assert_eq!(session.editor().lsp_label(), name);
|
||||
session.execute("buffer 1");
|
||||
assert_eq!(diagnostic_messages(&session), ["healthy source"]);
|
||||
assert_eq!(session.editor().lsp_names(), [name]);
|
||||
session.handle(Input::Char('A'));
|
||||
session.handle(Input::Char('!'));
|
||||
session.handle(Input::Escape);
|
||||
|
||||
Reference in New Issue
Block a user