lsp: Fix language servers not starting up on save (#32156)
Closes #24349 Release Notes: - Fixed language servers not starting up when a buffer is saved. --------- Co-authored-by: 张小白 <364772080@qq.com>
This commit is contained in:
parent
c71791d64e
commit
d082cfdbec
2 changed files with 89 additions and 5 deletions
|
@ -3960,6 +3960,15 @@ impl LspStore {
|
||||||
let buffer_id = buffer.read(cx).remote_id();
|
let buffer_id = buffer.read(cx).remote_id();
|
||||||
let handle = cx.new(|_| buffer.clone());
|
let handle = cx.new(|_| buffer.clone());
|
||||||
if let Some(local) = self.as_local_mut() {
|
if let Some(local) = self.as_local_mut() {
|
||||||
|
let refcount = local.registered_buffers.entry(buffer_id).or_insert(0);
|
||||||
|
if !ignore_refcounts {
|
||||||
|
*refcount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We run early exits on non-existing buffers AFTER we mark the buffer as registered in order to handle buffer saving.
|
||||||
|
// When a new unnamed buffer is created and saved, we will start loading it's language. Once the language is loaded, we go over all "language-less" buffers and try to fit that new language
|
||||||
|
// with them. However, we do that only for the buffers that we think are open in at least one editor; thus, we need to keep tab of unnamed buffers as well, even though they're not actually registered with any language
|
||||||
|
// servers in practice (we don't support non-file URI schemes in our LSP impl).
|
||||||
let Some(file) = File::from_dyn(buffer.read(cx).file()) else {
|
let Some(file) = File::from_dyn(buffer.read(cx).file()) else {
|
||||||
return handle;
|
return handle;
|
||||||
};
|
};
|
||||||
|
@ -3967,11 +3976,6 @@ impl LspStore {
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
let refcount = local.registered_buffers.entry(buffer_id).or_insert(0);
|
|
||||||
if !ignore_refcounts {
|
|
||||||
*refcount += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ignore_refcounts || *refcount == 1 {
|
if ignore_refcounts || *refcount == 1 {
|
||||||
local.register_buffer_with_language_servers(buffer, cx);
|
local.register_buffer_with_language_servers(buffer, cx);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3584,6 +3584,86 @@ async fn test_save_file(cx: &mut gpui::TestAppContext) {
|
||||||
assert_eq!(new_text, buffer.update(cx, |buffer, _| buffer.text()));
|
assert_eq!(new_text, buffer.update(cx, |buffer, _| buffer.text()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test(iterations = 10)]
|
||||||
|
async fn test_save_file_spawns_language_server(cx: &mut gpui::TestAppContext) {
|
||||||
|
// Issue: #24349
|
||||||
|
init_test(cx);
|
||||||
|
|
||||||
|
let fs = FakeFs::new(cx.executor());
|
||||||
|
fs.insert_tree(path!("/dir"), json!({})).await;
|
||||||
|
|
||||||
|
let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await;
|
||||||
|
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||||
|
|
||||||
|
language_registry.add(rust_lang());
|
||||||
|
let mut fake_rust_servers = language_registry.register_fake_lsp(
|
||||||
|
"Rust",
|
||||||
|
FakeLspAdapter {
|
||||||
|
name: "the-rust-language-server",
|
||||||
|
capabilities: lsp::ServerCapabilities {
|
||||||
|
completion_provider: Some(lsp::CompletionOptions {
|
||||||
|
trigger_characters: Some(vec![".".to_string(), "::".to_string()]),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
text_document_sync: Some(lsp::TextDocumentSyncCapability::Options(
|
||||||
|
lsp::TextDocumentSyncOptions {
|
||||||
|
save: Some(lsp::TextDocumentSyncSaveOptions::Supported(true)),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let buffer = project
|
||||||
|
.update(cx, |this, cx| this.create_buffer(cx))
|
||||||
|
.unwrap()
|
||||||
|
.await;
|
||||||
|
project.update(cx, |this, cx| {
|
||||||
|
this.register_buffer_with_language_servers(&buffer, cx);
|
||||||
|
buffer.update(cx, |buffer, cx| {
|
||||||
|
assert!(!this.has_language_servers_for(buffer, cx));
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
project
|
||||||
|
.update(cx, |this, cx| {
|
||||||
|
let worktree_id = this.worktrees(cx).next().unwrap().read(cx).id();
|
||||||
|
this.save_buffer_as(
|
||||||
|
buffer.clone(),
|
||||||
|
ProjectPath {
|
||||||
|
worktree_id,
|
||||||
|
path: Arc::from("file.rs".as_ref()),
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
// A server is started up, and it is notified about Rust files.
|
||||||
|
let mut fake_rust_server = fake_rust_servers.next().await.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
fake_rust_server
|
||||||
|
.receive_notification::<lsp::notification::DidOpenTextDocument>()
|
||||||
|
.await
|
||||||
|
.text_document,
|
||||||
|
lsp::TextDocumentItem {
|
||||||
|
uri: lsp::Url::from_file_path(path!("/dir/file.rs")).unwrap(),
|
||||||
|
version: 0,
|
||||||
|
text: "".to_string(),
|
||||||
|
language_id: "rust".to_string(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
project.update(cx, |this, cx| {
|
||||||
|
buffer.update(cx, |buffer, cx| {
|
||||||
|
assert!(this.has_language_servers_for(buffer, cx));
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test(iterations = 30)]
|
#[gpui::test(iterations = 30)]
|
||||||
async fn test_file_changes_multiple_times_on_disk(cx: &mut gpui::TestAppContext) {
|
async fn test_file_changes_multiple_times_on_disk(cx: &mut gpui::TestAppContext) {
|
||||||
init_test(cx);
|
init_test(cx);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue