Use new language server info on remote servers (#35682)

* Straightens out the `*_ext.rs` workflow for clangd and rust-analyzer:
no need to asynchronously query for the language server, as we sync that
information already.
* Fixes inlay hints editor menu toggle not being shown in the remote
sessions

Release Notes:

- Fixed inlay hints editor menu toggle not being shown in the remote
sessions
This commit is contained in:
Kirill Bulatov 2025-08-06 02:24:40 +03:00 committed by GitHub
parent cc93175256
commit 9caa9d042a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 176 additions and 291 deletions

View file

@ -3284,6 +3284,16 @@ impl InlayHints {
})
.unwrap_or(false)
}
pub fn check_capabilities(capabilities: &ServerCapabilities) -> bool {
capabilities
.inlay_hint_provider
.as_ref()
.is_some_and(|inlay_hint_provider| match inlay_hint_provider {
lsp::OneOf::Left(enabled) => *enabled,
lsp::OneOf::Right(_) => true,
})
}
}
#[async_trait(?Send)]
@ -3297,17 +3307,7 @@ impl LspCommand for InlayHints {
}
fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
let Some(inlay_hint_provider) = &capabilities.server_capabilities.inlay_hint_provider
else {
return false;
};
match inlay_hint_provider {
lsp::OneOf::Left(enabled) => *enabled,
lsp::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
lsp::InlayHintServerCapabilities::Options(_) => true,
lsp::InlayHintServerCapabilities::RegistrationOptions(_) => false,
},
}
Self::check_capabilities(&capabilities.server_capabilities)
}
fn to_lsp(

View file

@ -3671,7 +3671,6 @@ impl LspStore {
client.add_entity_request_handler(Self::handle_apply_additional_edits_for_completion);
client.add_entity_request_handler(Self::handle_register_buffer_with_language_servers);
client.add_entity_request_handler(Self::handle_rename_project_entry);
client.add_entity_request_handler(Self::handle_language_server_id_for_name);
client.add_entity_request_handler(Self::handle_pull_workspace_diagnostics);
client.add_entity_request_handler(Self::handle_lsp_command::<GetCodeActions>);
client.add_entity_request_handler(Self::handle_lsp_command::<GetCompletions>);
@ -8745,34 +8744,6 @@ impl LspStore {
Ok(proto::Ack {})
}
async fn handle_language_server_id_for_name(
lsp_store: Entity<Self>,
envelope: TypedEnvelope<proto::LanguageServerIdForName>,
mut cx: AsyncApp,
) -> Result<proto::LanguageServerIdForNameResponse> {
let name = &envelope.payload.name;
let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
lsp_store
.update(&mut cx, |lsp_store, cx| {
let buffer = lsp_store.buffer_store.read(cx).get_existing(buffer_id)?;
let server_id = buffer.update(cx, |buffer, cx| {
lsp_store
.language_servers_for_local_buffer(buffer, cx)
.find_map(|(adapter, server)| {
if adapter.name.0.as_ref() == name {
Some(server.server_id())
} else {
None
}
})
});
Ok(server_id)
})?
.map(|server_id| proto::LanguageServerIdForNameResponse {
server_id: server_id.map(|id| id.to_proto()),
})
}
async fn handle_rename_project_entry(
this: Entity<Self>,
envelope: TypedEnvelope<proto::RenameProjectEntry>,

View file

@ -3,12 +3,12 @@ use std::sync::Arc;
use ::serde::{Deserialize, Serialize};
use gpui::WeakEntity;
use language::{CachedLspAdapter, Diagnostic, DiagnosticSourceKind};
use lsp::LanguageServer;
use lsp::{LanguageServer, LanguageServerName};
use util::ResultExt as _;
use crate::LspStore;
pub const CLANGD_SERVER_NAME: &str = "clangd";
pub const CLANGD_SERVER_NAME: LanguageServerName = LanguageServerName::new_static("clangd");
const INACTIVE_REGION_MESSAGE: &str = "inactive region";
const INACTIVE_DIAGNOSTIC_SEVERITY: lsp::DiagnosticSeverity = lsp::DiagnosticSeverity::INFORMATION;
@ -34,7 +34,7 @@ pub fn is_inactive_region(diag: &Diagnostic) -> bool {
&& diag
.source
.as_ref()
.is_some_and(|v| v == CLANGD_SERVER_NAME)
.is_some_and(|v| v == &CLANGD_SERVER_NAME.0)
}
pub fn is_lsp_inactive_region(diag: &lsp::Diagnostic) -> bool {
@ -43,7 +43,7 @@ pub fn is_lsp_inactive_region(diag: &lsp::Diagnostic) -> bool {
&& diag
.source
.as_ref()
.is_some_and(|v| v == CLANGD_SERVER_NAME)
.is_some_and(|v| v == &CLANGD_SERVER_NAME.0)
}
pub fn register_notifications(
@ -51,7 +51,7 @@ pub fn register_notifications(
language_server: &LanguageServer,
adapter: Arc<CachedLspAdapter>,
) {
if language_server.name().0 != CLANGD_SERVER_NAME {
if language_server.name() != CLANGD_SERVER_NAME {
return;
}
let server_id = language_server.server_id();

View file

@ -2,12 +2,12 @@ use ::serde::{Deserialize, Serialize};
use anyhow::Context as _;
use gpui::{App, Entity, Task, WeakEntity};
use language::ServerHealth;
use lsp::LanguageServer;
use lsp::{LanguageServer, LanguageServerName};
use rpc::proto;
use crate::{LspStore, LspStoreEvent, Project, ProjectPath, lsp_store};
pub const RUST_ANALYZER_NAME: &str = "rust-analyzer";
pub const RUST_ANALYZER_NAME: LanguageServerName = LanguageServerName::new_static("rust-analyzer");
pub const CARGO_DIAGNOSTICS_SOURCE_NAME: &str = "rustc";
/// Experimental: Informs the end user about the state of the server
@ -97,13 +97,9 @@ pub fn cancel_flycheck(
cx.spawn(async move |cx| {
let buffer = buffer.await?;
let Some(rust_analyzer_server) = project
.update(cx, |project, cx| {
buffer.update(cx, |buffer, cx| {
project.language_server_id_for_name(buffer, RUST_ANALYZER_NAME, cx)
})
})?
.await
let Some(rust_analyzer_server) = project.read_with(cx, |project, cx| {
project.language_server_id_for_name(buffer.read(cx), &RUST_ANALYZER_NAME, cx)
})?
else {
return Ok(());
};
@ -148,13 +144,9 @@ pub fn run_flycheck(
cx.spawn(async move |cx| {
let buffer = buffer.await?;
let Some(rust_analyzer_server) = project
.update(cx, |project, cx| {
buffer.update(cx, |buffer, cx| {
project.language_server_id_for_name(buffer, RUST_ANALYZER_NAME, cx)
})
})?
.await
let Some(rust_analyzer_server) = project.read_with(cx, |project, cx| {
project.language_server_id_for_name(buffer.read(cx), &RUST_ANALYZER_NAME, cx)
})?
else {
return Ok(());
};
@ -204,13 +196,9 @@ pub fn clear_flycheck(
cx.spawn(async move |cx| {
let buffer = buffer.await?;
let Some(rust_analyzer_server) = project
.update(cx, |project, cx| {
buffer.update(cx, |buffer, cx| {
project.language_server_id_for_name(buffer, RUST_ANALYZER_NAME, cx)
})
})?
.await
let Some(rust_analyzer_server) = project.read_with(cx, |project, cx| {
project.language_server_id_for_name(buffer.read(cx), &RUST_ANALYZER_NAME, cx)
})?
else {
return Ok(());
};

View file

@ -5002,63 +5002,53 @@ impl Project {
}
pub fn any_language_server_supports_inlay_hints(&self, buffer: &Buffer, cx: &mut App) -> bool {
self.lsp_store.update(cx, |this, cx| {
this.language_servers_for_local_buffer(buffer, cx)
.any(
|(_, server)| match server.capabilities().inlay_hint_provider {
Some(lsp::OneOf::Left(enabled)) => enabled,
Some(lsp::OneOf::Right(_)) => true,
None => false,
},
)
let Some(language) = buffer.language().cloned() else {
return false;
};
self.lsp_store.update(cx, |lsp_store, _| {
let relevant_language_servers = lsp_store
.languages
.lsp_adapters(&language.name())
.into_iter()
.map(|lsp_adapter| lsp_adapter.name())
.collect::<HashSet<_>>();
lsp_store
.language_server_statuses()
.filter_map(|(server_id, server_status)| {
relevant_language_servers
.contains(&server_status.name)
.then_some(server_id)
})
.filter_map(|server_id| lsp_store.lsp_server_capabilities.get(&server_id))
.any(InlayHints::check_capabilities)
})
}
pub fn language_server_id_for_name(
&self,
buffer: &Buffer,
name: &str,
cx: &mut App,
) -> Task<Option<LanguageServerId>> {
if self.is_local() {
Task::ready(self.lsp_store.update(cx, |lsp_store, cx| {
lsp_store
.language_servers_for_local_buffer(buffer, cx)
.find_map(|(adapter, server)| {
if adapter.name.0 == name {
Some(server.server_id())
} else {
None
}
})
}))
} else if let Some(project_id) = self.remote_id() {
let request = self.client.request(proto::LanguageServerIdForName {
project_id,
buffer_id: buffer.remote_id().to_proto(),
name: name.to_string(),
});
cx.background_spawn(async move {
let response = request.await.log_err()?;
response.server_id.map(LanguageServerId::from_proto)
})
} else if let Some(ssh_client) = self.ssh_client.as_ref() {
let request =
ssh_client
.read(cx)
.proto_client()
.request(proto::LanguageServerIdForName {
project_id: SSH_PROJECT_ID,
buffer_id: buffer.remote_id().to_proto(),
name: name.to_string(),
});
cx.background_spawn(async move {
let response = request.await.log_err()?;
response.server_id.map(LanguageServerId::from_proto)
})
} else {
Task::ready(None)
name: &LanguageServerName,
cx: &App,
) -> Option<LanguageServerId> {
let language = buffer.language()?;
let relevant_language_servers = self
.languages
.lsp_adapters(&language.name())
.into_iter()
.map(|lsp_adapter| lsp_adapter.name())
.collect::<HashSet<_>>();
if !relevant_language_servers.contains(name) {
return None;
}
self.language_server_statuses(cx)
.filter(|(_, server_status)| relevant_language_servers.contains(&server_status.name))
.find_map(|(server_id, server_status)| {
if &server_status.name == name {
Some(server_id)
} else {
None
}
})
}
pub fn has_language_servers_for(&self, buffer: &Buffer, cx: &mut App) -> bool {