remote dev: Allow canceling language server work in editor (#19946)

Release Notes:

- Added ability to cancel language server work in remote development.

Demo:



https://github.com/user-attachments/assets/c9ca91a5-617f-4886-a458-87c563c5a247
This commit is contained in:
Thorsten Ball 2024-10-30 13:27:11 +01:00 committed by GitHub
parent 774a8bf039
commit f6cd97f6fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 325 additions and 66 deletions

View file

@ -528,6 +528,172 @@ async fn test_remote_lsp(cx: &mut TestAppContext, server_cx: &mut TestAppContext
})
}
#[gpui::test]
async fn test_remote_cancel_language_server_work(
cx: &mut TestAppContext,
server_cx: &mut TestAppContext,
) {
let fs = FakeFs::new(server_cx.executor());
fs.insert_tree(
"/code",
json!({
"project1": {
".git": {},
"README.md": "# project 1",
"src": {
"lib.rs": "fn one() -> usize { 1 }"
}
},
}),
)
.await;
let (project, headless) = init_test(&fs, cx, server_cx).await;
fs.insert_tree(
"/code/project1/.zed",
json!({
"settings.json": r#"
{
"languages": {"Rust":{"language_servers":["rust-analyzer"]}},
"lsp": {
"rust-analyzer": {
"binary": {
"path": "~/.cargo/bin/rust-analyzer"
}
}
}
}"#
}),
)
.await;
cx.update_model(&project, |project, _| {
project.languages().register_test_language(LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".into()],
..Default::default()
},
..Default::default()
});
project.languages().register_fake_lsp_adapter(
"Rust",
FakeLspAdapter {
name: "rust-analyzer",
..Default::default()
},
)
});
let mut fake_lsp = server_cx.update(|cx| {
headless.read(cx).languages.register_fake_language_server(
LanguageServerName("rust-analyzer".into()),
Default::default(),
None,
)
});
cx.run_until_parked();
let worktree_id = project
.update(cx, |project, cx| {
project.find_or_create_worktree("/code/project1", true, cx)
})
.await
.unwrap()
.0
.read_with(cx, |worktree, _| worktree.id());
cx.run_until_parked();
let buffer = project
.update(cx, |project, cx| {
project.open_buffer((worktree_id, Path::new("src/lib.rs")), cx)
})
.await
.unwrap();
cx.run_until_parked();
let mut fake_lsp = fake_lsp.next().await.unwrap();
// Cancelling all language server work for a given buffer
{
// Two operations, one cancellable and one not.
fake_lsp
.start_progress_with(
"another-token",
lsp::WorkDoneProgressBegin {
cancellable: Some(false),
..Default::default()
},
)
.await;
let progress_token = "the-progress-token";
fake_lsp
.start_progress_with(
progress_token,
lsp::WorkDoneProgressBegin {
cancellable: Some(true),
..Default::default()
},
)
.await;
cx.executor().run_until_parked();
project.update(cx, |project, cx| {
project.cancel_language_server_work_for_buffers([buffer.clone()], cx)
});
cx.executor().run_until_parked();
// Verify the cancellation was received on the server side
let cancel_notification = fake_lsp
.receive_notification::<lsp::notification::WorkDoneProgressCancel>()
.await;
assert_eq!(
cancel_notification.token,
lsp::NumberOrString::String(progress_token.into())
);
}
// Cancelling work by server_id and token
{
let server_id = fake_lsp.server.server_id();
let progress_token = "the-progress-token";
fake_lsp
.start_progress_with(
progress_token,
lsp::WorkDoneProgressBegin {
cancellable: Some(true),
..Default::default()
},
)
.await;
cx.executor().run_until_parked();
project.update(cx, |project, cx| {
project.cancel_language_server_work(server_id, Some(progress_token.into()), cx)
});
cx.executor().run_until_parked();
// Verify the cancellation was received on the server side
let cancel_notification = fake_lsp
.receive_notification::<lsp::notification::WorkDoneProgressCancel>()
.await;
assert_eq!(
cancel_notification.token,
lsp::NumberOrString::String(progress_token.into())
);
}
}
#[gpui::test]
async fn test_remote_reload(cx: &mut TestAppContext, server_cx: &mut TestAppContext) {
let fs = FakeFs::new(server_cx.executor());