Stop sending redundant LSP proto requests (#35581)
Before, each time any LSP feature was used on client remote, it always produced a `proto::` request that always had been sent to the host, from where returned as an empty response. Instead, propagate more language server-related data to the client, `lsp::ServerCapability`, so Zed client can omit certain requests if those are not supported. On top of that, rework the approach Zed uses to query for the data refreshes: before, editors tried to fetch the data when the server start was reported (locally and remotely). Now, a later event is selected: on each `textDocument/didOpen` for the buffer contained in this editor, we will query for new LSP data, reusing the cache if needed. Before, servers could reject unregistered files' LSP queries, or process them slowly when starting up. Now, such refreshes are happening later and should be cached. This requires a collab DB change, to restore server data on rejoin. Release Notes: - Fixed excessive LSP requests sent during remote sessions
This commit is contained in:
parent
5b40b3618f
commit
22473fc611
19 changed files with 793 additions and 351 deletions
|
@ -2154,6 +2154,16 @@ impl LspCommand for GetHover {
|
|||
}
|
||||
}
|
||||
|
||||
impl GetCompletions {
|
||||
pub fn can_resolve_completions(capabilities: &lsp::ServerCapabilities) -> bool {
|
||||
capabilities
|
||||
.completion_provider
|
||||
.as_ref()
|
||||
.and_then(|options| options.resolve_provider)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspCommand for GetCompletions {
|
||||
type Response = CoreCompletionResponse;
|
||||
|
@ -2762,6 +2772,23 @@ impl GetCodeActions {
|
|||
}
|
||||
}
|
||||
|
||||
impl OnTypeFormatting {
|
||||
pub fn supports_on_type_formatting(trigger: &str, capabilities: &ServerCapabilities) -> bool {
|
||||
let Some(on_type_formatting_options) = &capabilities.document_on_type_formatting_provider
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
on_type_formatting_options
|
||||
.first_trigger_character
|
||||
.contains(trigger)
|
||||
|| on_type_formatting_options
|
||||
.more_trigger_character
|
||||
.iter()
|
||||
.flatten()
|
||||
.any(|chars| chars.contains(trigger))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspCommand for OnTypeFormatting {
|
||||
type Response = Option<Transaction>;
|
||||
|
@ -2773,20 +2800,7 @@ impl LspCommand for OnTypeFormatting {
|
|||
}
|
||||
|
||||
fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
|
||||
let Some(on_type_formatting_options) = &capabilities
|
||||
.server_capabilities
|
||||
.document_on_type_formatting_provider
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
on_type_formatting_options
|
||||
.first_trigger_character
|
||||
.contains(&self.trigger)
|
||||
|| on_type_formatting_options
|
||||
.more_trigger_character
|
||||
.iter()
|
||||
.flatten()
|
||||
.any(|chars| chars.contains(&self.trigger))
|
||||
Self::supports_on_type_formatting(&self.trigger, &capabilities.server_capabilities)
|
||||
}
|
||||
|
||||
fn to_lsp(
|
||||
|
@ -4221,8 +4235,9 @@ impl LspCommand for GetDocumentColor {
|
|||
server_capabilities
|
||||
.server_capabilities
|
||||
.color_provider
|
||||
.as_ref()
|
||||
.is_some_and(|capability| match capability {
|
||||
lsp::ColorProviderCapability::Simple(supported) => supported,
|
||||
lsp::ColorProviderCapability::Simple(supported) => *supported,
|
||||
lsp::ColorProviderCapability::ColorProvider(..) => true,
|
||||
lsp::ColorProviderCapability::Options(..) => true,
|
||||
})
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -277,6 +277,13 @@ pub enum Event {
|
|||
LanguageServerAdded(LanguageServerId, LanguageServerName, Option<WorktreeId>),
|
||||
LanguageServerRemoved(LanguageServerId),
|
||||
LanguageServerLog(LanguageServerId, LanguageServerLogType, String),
|
||||
// [`lsp::notification::DidOpenTextDocument`] was sent to this server using the buffer data.
|
||||
// Zed's buffer-related data is updated accordingly.
|
||||
LanguageServerBufferRegistered {
|
||||
server_id: LanguageServerId,
|
||||
buffer_id: BufferId,
|
||||
buffer_abs_path: PathBuf,
|
||||
},
|
||||
Toast {
|
||||
notification_id: SharedString,
|
||||
message: String,
|
||||
|
@ -2931,8 +2938,8 @@ impl Project {
|
|||
}
|
||||
LspStoreEvent::LanguageServerUpdate {
|
||||
language_server_id,
|
||||
message,
|
||||
name,
|
||||
message,
|
||||
} => {
|
||||
if self.is_local() {
|
||||
self.enqueue_buffer_ordered_message(
|
||||
|
@ -2944,6 +2951,32 @@ impl Project {
|
|||
)
|
||||
.ok();
|
||||
}
|
||||
|
||||
match message {
|
||||
proto::update_language_server::Variant::MetadataUpdated(update) => {
|
||||
if let Some(capabilities) = update
|
||||
.capabilities
|
||||
.as_ref()
|
||||
.and_then(|capabilities| serde_json::from_str(capabilities).ok())
|
||||
{
|
||||
self.lsp_store.update(cx, |lsp_store, _| {
|
||||
lsp_store
|
||||
.lsp_server_capabilities
|
||||
.insert(*language_server_id, capabilities);
|
||||
});
|
||||
}
|
||||
}
|
||||
proto::update_language_server::Variant::RegisteredForBuffer(update) => {
|
||||
if let Some(buffer_id) = BufferId::new(update.buffer_id).ok() {
|
||||
cx.emit(Event::LanguageServerBufferRegistered {
|
||||
buffer_id,
|
||||
server_id: *language_server_id,
|
||||
buffer_abs_path: PathBuf::from(&update.buffer_abs_path),
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
LspStoreEvent::Notification(message) => cx.emit(Event::Toast {
|
||||
notification_id: "lsp".into(),
|
||||
|
@ -3476,20 +3509,6 @@ impl Project {
|
|||
})
|
||||
}
|
||||
|
||||
fn document_highlights_impl(
|
||||
&mut self,
|
||||
buffer: &Entity<Buffer>,
|
||||
position: PointUtf16,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<Vec<DocumentHighlight>>> {
|
||||
self.request_lsp(
|
||||
buffer.clone(),
|
||||
LanguageServerToQuery::FirstCapable,
|
||||
GetDocumentHighlights { position },
|
||||
cx,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn document_highlights<T: ToPointUtf16>(
|
||||
&mut self,
|
||||
buffer: &Entity<Buffer>,
|
||||
|
@ -3497,7 +3516,12 @@ impl Project {
|
|||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<Vec<DocumentHighlight>>> {
|
||||
let position = position.to_point_utf16(buffer.read(cx));
|
||||
self.document_highlights_impl(buffer, position, cx)
|
||||
self.request_lsp(
|
||||
buffer.clone(),
|
||||
LanguageServerToQuery::FirstCapable,
|
||||
GetDocumentHighlights { position },
|
||||
cx,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn document_symbols(
|
||||
|
@ -3598,14 +3622,14 @@ impl Project {
|
|||
.update(cx, |lsp_store, cx| lsp_store.hover(buffer, position, cx))
|
||||
}
|
||||
|
||||
pub fn linked_edit(
|
||||
pub fn linked_edits(
|
||||
&self,
|
||||
buffer: &Entity<Buffer>,
|
||||
position: Anchor,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<Vec<Range<Anchor>>>> {
|
||||
self.lsp_store.update(cx, |lsp_store, cx| {
|
||||
lsp_store.linked_edit(buffer, position, cx)
|
||||
lsp_store.linked_edits(buffer, position, cx)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -3697,19 +3721,6 @@ impl Project {
|
|||
})
|
||||
}
|
||||
|
||||
fn prepare_rename_impl(
|
||||
&mut self,
|
||||
buffer: Entity<Buffer>,
|
||||
position: PointUtf16,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<PrepareRenameResponse>> {
|
||||
self.request_lsp(
|
||||
buffer,
|
||||
LanguageServerToQuery::FirstCapable,
|
||||
PrepareRename { position },
|
||||
cx,
|
||||
)
|
||||
}
|
||||
pub fn prepare_rename<T: ToPointUtf16>(
|
||||
&mut self,
|
||||
buffer: Entity<Buffer>,
|
||||
|
@ -3717,7 +3728,12 @@ impl Project {
|
|||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<PrepareRenameResponse>> {
|
||||
let position = position.to_point_utf16(buffer.read(cx));
|
||||
self.prepare_rename_impl(buffer, position, cx)
|
||||
self.request_lsp(
|
||||
buffer,
|
||||
LanguageServerToQuery::FirstCapable,
|
||||
PrepareRename { position },
|
||||
cx,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn perform_rename<T: ToPointUtf16>(
|
||||
|
|
|
@ -1100,7 +1100,7 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon
|
|||
let fake_server = fake_servers.next().await.unwrap();
|
||||
let (server_id, server_name) = lsp_store.read_with(cx, |lsp_store, _| {
|
||||
let (id, status) = lsp_store.language_server_statuses().next().unwrap();
|
||||
(id, LanguageServerName::from(status.name.as_str()))
|
||||
(id, status.name.clone())
|
||||
});
|
||||
|
||||
// Simulate jumping to a definition in a dependency outside of the worktree.
|
||||
|
@ -1698,7 +1698,7 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC
|
|||
name: "the-language-server",
|
||||
disk_based_diagnostics_sources: vec!["disk".into()],
|
||||
disk_based_diagnostics_progress_token: Some(progress_token.into()),
|
||||
..Default::default()
|
||||
..FakeLspAdapter::default()
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -1710,6 +1710,7 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC
|
|||
})
|
||||
.await
|
||||
.unwrap();
|
||||
let buffer_id = buffer.read_with(cx, |buffer, _| buffer.remote_id());
|
||||
// Simulate diagnostics starting to update.
|
||||
let fake_server = fake_servers.next().await.unwrap();
|
||||
fake_server.start_progress(progress_token).await;
|
||||
|
@ -1736,6 +1737,14 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC
|
|||
);
|
||||
assert_eq!(events.next().await.unwrap(), Event::RefreshInlayHints);
|
||||
fake_server.start_progress(progress_token).await;
|
||||
assert_eq!(
|
||||
events.next().await.unwrap(),
|
||||
Event::LanguageServerBufferRegistered {
|
||||
server_id: LanguageServerId(1),
|
||||
buffer_id,
|
||||
buffer_abs_path: PathBuf::from(path!("/dir/a.rs")),
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
events.next().await.unwrap(),
|
||||
Event::DiskBasedDiagnosticsStarted {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue