Start to split out initialization and registration (#21787)
Still TODO: * [x] Factor out `start_language_server` so we can call it on register (instead of on detect language) * [x] Only call register in singleton editors (or when editing/go-to-definition etc. in a multibuffer?) * [x] Refcount on register so we can unregister when no buffer remain * [ ] (maybe) Stop language servers that are no longer needed after some time Release Notes: - Fixed language servers starting when doing project search - Fixed high CPU usage when ignoring warnings in the diagnostics view --------- Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Co-authored-by: Cole <cole@zed.dev>
This commit is contained in:
parent
de89f8cf83
commit
13a81e454a
19 changed files with 2200 additions and 1848 deletions
|
@ -1,4 +1,5 @@
|
|||
use crate::{
|
||||
lsp_store::OpenLspBufferHandle,
|
||||
search::SearchQuery,
|
||||
worktree_store::{WorktreeStore, WorktreeStoreEvent},
|
||||
ProjectItem as _, ProjectPath,
|
||||
|
@ -47,6 +48,7 @@ pub struct BufferStore {
|
|||
struct SharedBuffer {
|
||||
buffer: Model<Buffer>,
|
||||
unstaged_changes: Option<Model<BufferChangeSet>>,
|
||||
lsp_handle: Option<OpenLspBufferHandle>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -1571,6 +1573,21 @@ impl BufferStore {
|
|||
})?
|
||||
}
|
||||
|
||||
pub fn register_shared_lsp_handle(
|
||||
&mut self,
|
||||
peer_id: proto::PeerId,
|
||||
buffer_id: BufferId,
|
||||
handle: OpenLspBufferHandle,
|
||||
) {
|
||||
if let Some(shared_buffers) = self.shared_buffers.get_mut(&peer_id) {
|
||||
if let Some(buffer) = shared_buffers.get_mut(&buffer_id) {
|
||||
buffer.lsp_handle = Some(handle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
debug_panic!("tried to register shared lsp handle, but buffer was not shared")
|
||||
}
|
||||
|
||||
pub fn handle_synchronize_buffers(
|
||||
&mut self,
|
||||
envelope: TypedEnvelope<proto::SynchronizeBuffers>,
|
||||
|
@ -1597,6 +1614,7 @@ impl BufferStore {
|
|||
.or_insert_with(|| SharedBuffer {
|
||||
buffer: buffer.clone(),
|
||||
unstaged_changes: None,
|
||||
lsp_handle: None,
|
||||
});
|
||||
|
||||
let buffer = buffer.read(cx);
|
||||
|
@ -2017,6 +2035,7 @@ impl BufferStore {
|
|||
SharedBuffer {
|
||||
buffer: buffer.clone(),
|
||||
unstaged_changes: None,
|
||||
lsp_handle: None,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1254,6 +1254,10 @@ impl Project {
|
|||
self.buffer_store.read(cx).buffers().collect()
|
||||
}
|
||||
|
||||
pub fn environment(&self) -> &Model<ProjectEnvironment> {
|
||||
&self.environment
|
||||
}
|
||||
|
||||
pub fn cli_environment(&self, cx: &AppContext) -> Option<HashMap<String, String>> {
|
||||
self.environment.read(cx).get_cli_environment()
|
||||
}
|
||||
|
@ -1843,6 +1847,19 @@ impl Project {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn open_local_buffer_with_lsp(
|
||||
&mut self,
|
||||
abs_path: impl AsRef<Path>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<(Model<Buffer>, lsp_store::OpenLspBufferHandle)>> {
|
||||
if let Some((worktree, relative_path)) = self.find_worktree(abs_path.as_ref(), cx) {
|
||||
self.open_buffer_with_lsp((worktree.read(cx).id(), relative_path), cx)
|
||||
} else {
|
||||
Task::ready(Err(anyhow!("no such path")))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_buffer(
|
||||
&mut self,
|
||||
path: impl Into<ProjectPath>,
|
||||
|
@ -1857,6 +1874,23 @@ impl Project {
|
|||
})
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn open_buffer_with_lsp(
|
||||
&mut self,
|
||||
path: impl Into<ProjectPath>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<(Model<Buffer>, lsp_store::OpenLspBufferHandle)>> {
|
||||
let buffer = self.open_buffer(path, cx);
|
||||
let lsp_store = self.lsp_store().clone();
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
let buffer = buffer.await?;
|
||||
let handle = lsp_store.update(&mut cx, |lsp_store, cx| {
|
||||
lsp_store.register_buffer_with_language_servers(&buffer, cx)
|
||||
})?;
|
||||
Ok((buffer, handle))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn open_unstaged_changes(
|
||||
&mut self,
|
||||
buffer: Model<Buffer>,
|
||||
|
|
|
@ -442,17 +442,17 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
|
|||
);
|
||||
|
||||
// Open a buffer without an associated language server.
|
||||
let toml_buffer = project
|
||||
let (toml_buffer, _handle) = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer("/the-root/Cargo.toml", cx)
|
||||
project.open_local_buffer_with_lsp("/the-root/Cargo.toml", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Open a buffer with an associated language server before the language for it has been loaded.
|
||||
let rust_buffer = project
|
||||
let (rust_buffer, _handle2) = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer("/the-root/test.rs", cx)
|
||||
project.open_local_buffer_with_lsp("/the-root/test.rs", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -513,9 +513,9 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
|
|||
);
|
||||
|
||||
// Open a third buffer with a different associated language server.
|
||||
let json_buffer = project
|
||||
let (json_buffer, _json_handle) = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer("/the-root/package.json", cx)
|
||||
project.open_local_buffer_with_lsp("/the-root/package.json", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -550,9 +550,9 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
// When opening another buffer whose language server is already running,
|
||||
// it is also configured based on the existing language server's capabilities.
|
||||
let rust_buffer2 = project
|
||||
let (rust_buffer2, _handle4) = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer("/the-root/test2.rs", cx)
|
||||
project.open_local_buffer_with_lsp("/the-root/test2.rs", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -765,7 +765,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
|
|||
);
|
||||
|
||||
// Close notifications are reported only to servers matching the buffer's language.
|
||||
cx.update(|_| drop(json_buffer));
|
||||
cx.update(|_| drop(_json_handle));
|
||||
let close_message = lsp::DidCloseTextDocumentParams {
|
||||
text_document: lsp::TextDocumentIdentifier::new(
|
||||
lsp::Url::from_file_path("/the-root/package.json").unwrap(),
|
||||
|
@ -827,9 +827,9 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon
|
|||
cx.executor().run_until_parked();
|
||||
|
||||
// Start the language server by opening a buffer with a compatible file extension.
|
||||
let _buffer = project
|
||||
let _ = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer("/the-root/src/a.rs", cx)
|
||||
project.open_local_buffer_with_lsp("/the-root/src/a.rs", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -1239,8 +1239,10 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) {
|
|||
let worktree_id = project.update(cx, |p, cx| p.worktrees(cx).next().unwrap().read(cx).id());
|
||||
|
||||
// Cause worktree to start the fake language server
|
||||
let _buffer = project
|
||||
.update(cx, |project, cx| project.open_local_buffer("/dir/b.rs", cx))
|
||||
let _ = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer_with_lsp("/dir/b.rs", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -1259,6 +1261,7 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) {
|
|||
fake_server
|
||||
.start_progress(format!("{}/0", progress_token))
|
||||
.await;
|
||||
assert_eq!(events.next().await.unwrap(), Event::RefreshInlayHints);
|
||||
assert_eq!(
|
||||
events.next().await.unwrap(),
|
||||
Event::DiskBasedDiagnosticsStarted {
|
||||
|
@ -1365,8 +1368,10 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC
|
|||
|
||||
let worktree_id = project.update(cx, |p, cx| p.worktrees(cx).next().unwrap().read(cx).id());
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
|
||||
let (buffer, _handle) = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer_with_lsp("/dir/a.rs", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -1390,6 +1395,7 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC
|
|||
Some(worktree_id)
|
||||
)
|
||||
);
|
||||
assert_eq!(events.next().await.unwrap(), Event::RefreshInlayHints);
|
||||
fake_server.start_progress(progress_token).await;
|
||||
assert_eq!(
|
||||
events.next().await.unwrap(),
|
||||
|
@ -1438,8 +1444,10 @@ async fn test_restarting_server_with_diagnostics_published(cx: &mut gpui::TestAp
|
|||
language_registry.add(rust_lang());
|
||||
let mut fake_servers = language_registry.register_fake_lsp("Rust", FakeLspAdapter::default());
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
|
||||
let (buffer, _) = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer_with_lsp("/dir/a.rs", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -1517,8 +1525,10 @@ async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui::T
|
|||
language_registry.add(rust_lang());
|
||||
let mut fake_servers = language_registry.register_fake_lsp("Rust", FakeLspAdapter::default());
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
|
||||
let (buffer, _handle) = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer_with_lsp("/dir/a.rs", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -1565,8 +1575,10 @@ async fn test_cancel_language_server_work(cx: &mut gpui::TestAppContext) {
|
|||
},
|
||||
);
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
|
||||
let (buffer, _handle) = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer_with_lsp("/dir/a.rs", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -1634,11 +1646,15 @@ async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) {
|
|||
language_registry.add(js_lang());
|
||||
|
||||
let _rs_buffer = project
|
||||
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer_with_lsp("/dir/a.rs", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
let _js_buffer = project
|
||||
.update(cx, |project, cx| project.open_local_buffer("/dir/b.js", cx))
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer_with_lsp("/dir/b.js", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -1734,6 +1750,7 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
|
|||
fs.insert_tree("/dir", json!({ "a.rs": text })).await;
|
||||
|
||||
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
|
||||
let lsp_store = project.read_with(cx, |project, _| project.lsp_store());
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||
|
||||
language_registry.add(rust_lang());
|
||||
|
@ -1750,6 +1767,10 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
let _handle = lsp_store.update(cx, |lsp_store, cx| {
|
||||
lsp_store.register_buffer_with_language_servers(&buffer, cx)
|
||||
});
|
||||
|
||||
let mut fake_server = fake_servers.next().await.unwrap();
|
||||
let open_notification = fake_server
|
||||
.receive_notification::<lsp::notification::DidOpenTextDocument>()
|
||||
|
@ -2162,8 +2183,10 @@ async fn test_edits_from_lsp2_with_past_version(cx: &mut gpui::TestAppContext) {
|
|||
language_registry.add(rust_lang());
|
||||
let mut fake_servers = language_registry.register_fake_lsp("Rust", FakeLspAdapter::default());
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
|
||||
let (buffer, _handle) = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer_with_lsp("/dir/a.rs", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -2533,8 +2556,10 @@ async fn test_definition(cx: &mut gpui::TestAppContext) {
|
|||
language_registry.add(rust_lang());
|
||||
let mut fake_servers = language_registry.register_fake_lsp("Rust", FakeLspAdapter::default());
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| project.open_local_buffer("/dir/b.rs", cx))
|
||||
let (buffer, _handle) = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer_with_lsp("/dir/b.rs", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -2638,8 +2663,8 @@ async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) {
|
|||
},
|
||||
);
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |p, cx| p.open_local_buffer("/dir/a.ts", cx))
|
||||
let (buffer, _handle) = project
|
||||
.update(cx, |p, cx| p.open_local_buffer_with_lsp("/dir/a.ts", cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -2730,8 +2755,8 @@ async fn test_completions_with_carriage_returns(cx: &mut gpui::TestAppContext) {
|
|||
},
|
||||
);
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |p, cx| p.open_local_buffer("/dir/a.ts", cx))
|
||||
let (buffer, _handle) = project
|
||||
.update(cx, |p, cx| p.open_local_buffer_with_lsp("/dir/a.ts", cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -2793,8 +2818,8 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) {
|
|||
},
|
||||
);
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |p, cx| p.open_local_buffer("/dir/a.ts", cx))
|
||||
let (buffer, _handle) = project
|
||||
.update(cx, |p, cx| p.open_local_buffer_with_lsp("/dir/a.ts", cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -3984,7 +4009,7 @@ async fn test_lsp_rename_notifications(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
let _ = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer("/dir/one.rs", cx)
|
||||
project.open_local_buffer_with_lsp("/dir/one.rs", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -4086,9 +4111,9 @@ async fn test_rename(cx: &mut gpui::TestAppContext) {
|
|||
},
|
||||
);
|
||||
|
||||
let buffer = project
|
||||
let (buffer, _handle) = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer("/dir/one.rs", cx)
|
||||
project.open_local_buffer_with_lsp("/dir/one.rs", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -4951,8 +4976,8 @@ async fn test_multiple_language_server_hovers(cx: &mut gpui::TestAppContext) {
|
|||
),
|
||||
];
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |p, cx| p.open_local_buffer("/dir/a.tsx", cx))
|
||||
let (buffer, _handle) = project
|
||||
.update(cx, |p, cx| p.open_local_buffer_with_lsp("/dir/a.tsx", cx))
|
||||
.await
|
||||
.unwrap();
|
||||
cx.executor().run_until_parked();
|
||||
|
@ -5060,8 +5085,8 @@ async fn test_hovers_with_empty_parts(cx: &mut gpui::TestAppContext) {
|
|||
},
|
||||
);
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |p, cx| p.open_local_buffer("/dir/a.ts", cx))
|
||||
let (buffer, _handle) = project
|
||||
.update(cx, |p, cx| p.open_local_buffer_with_lsp("/dir/a.ts", cx))
|
||||
.await
|
||||
.unwrap();
|
||||
cx.executor().run_until_parked();
|
||||
|
@ -5130,8 +5155,8 @@ async fn test_code_actions_only_kinds(cx: &mut gpui::TestAppContext) {
|
|||
},
|
||||
);
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |p, cx| p.open_local_buffer("/dir/a.ts", cx))
|
||||
let (buffer, _handle) = project
|
||||
.update(cx, |p, cx| p.open_local_buffer_with_lsp("/dir/a.ts", cx))
|
||||
.await
|
||||
.unwrap();
|
||||
cx.executor().run_until_parked();
|
||||
|
@ -5251,8 +5276,8 @@ async fn test_multiple_language_server_actions(cx: &mut gpui::TestAppContext) {
|
|||
),
|
||||
];
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |p, cx| p.open_local_buffer("/dir/a.tsx", cx))
|
||||
let (buffer, _handle) = project
|
||||
.update(cx, |p, cx| p.open_local_buffer_with_lsp("/dir/a.tsx", cx))
|
||||
.await
|
||||
.unwrap();
|
||||
cx.executor().run_until_parked();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue