diff --git a/Cargo.lock b/Cargo.lock index 903141eee9a..790dbdf1345 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1597,6 +1597,7 @@ dependencies = [ "rayon", "rustc-hash", "scip", + "semver", "serde", "serde_json", "sourcegen", diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 766606be7be..e6984d6f41b 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -42,6 +42,7 @@ triomphe.workspace = true nohash-hasher.workspace = true always-assert = "0.2.0" walkdir = "2.3.2" +semver.workspace = true cfg.workspace = true flycheck.workspace = true diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 07e04a83661..e747ec87b1c 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -16,6 +16,7 @@ use anyhow::Context; use lsp_server::Connection; use rust_analyzer::{cli::flags, config::Config, from_json}; +use semver::Version; use tracing_subscriber::fmt::writer::BoxMakeWriter; use vfs::AbsPathBuf; @@ -193,10 +194,18 @@ fn run_server() -> anyhow::Result<()> { } }; - let mut is_visual_studio_code = false; + let mut visual_studio_code_version = None; if let Some(client_info) = client_info { - tracing::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default()); - is_visual_studio_code = client_info.name.starts_with("Visual Studio Code"); + tracing::info!( + "Client '{}' {}", + client_info.name, + client_info.version.as_deref().unwrap_or_default() + ); + visual_studio_code_version = client_info + .name + .starts_with("Visual Studio Code") + .then(|| client_info.version.as_deref().map(Version::parse).and_then(Result::ok)) + .flatten(); } let workspace_roots = workspace_folders @@ -210,7 +219,8 @@ fn run_server() -> anyhow::Result<()> { }) .filter(|workspaces| !workspaces.is_empty()) .unwrap_or_else(|| vec![root_path.clone()]); - let mut config = Config::new(root_path, capabilities, workspace_roots, is_visual_studio_code); + let mut config = + Config::new(root_path, capabilities, workspace_roots, visual_studio_code_version); if let Some(json) = initialization_options { if let Err(e) = config.update(json) { use lsp_types::{ diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs index 8fd59d159c9..1061a433a58 100644 --- a/crates/rust-analyzer/src/cli/scip.rs +++ b/crates/rust-analyzer/src/cli/scip.rs @@ -32,8 +32,8 @@ pub fn run(self) -> anyhow::Result<()> { let mut config = crate::config::Config::new( root.clone(), lsp_types::ClientCapabilities::default(), - /* workspace_roots = */ vec![], - /* is_visual_studio_code = */ false, + vec![], + None, ); if let Some(p) = self.config_path { diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 9e81c8dd665..cbf15246590 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -31,6 +31,7 @@ CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource, }; use rustc_hash::{FxHashMap, FxHashSet}; +use semver::Version; use serde::{de::DeserializeOwned, Deserialize}; use stdx::format_to_acc; use vfs::{AbsPath, AbsPathBuf}; @@ -624,7 +625,7 @@ pub struct Config { data: ConfigData, detached_files: Vec, snippets: Vec, - is_visual_studio_code: bool, + visual_studio_code_version: Option, } type ParallelCachePrimingNumThreads = u8; @@ -823,7 +824,7 @@ pub fn new( root_path: AbsPathBuf, caps: ClientCapabilities, workspace_roots: Vec, - is_visual_studio_code: bool, + visual_studio_code_version: Option, ) -> Self { Config { caps, @@ -833,7 +834,7 @@ pub fn new( root_path, snippets: Default::default(), workspace_roots, - is_visual_studio_code, + visual_studio_code_version, } } @@ -1778,10 +1779,10 @@ pub fn typing_autoclose_angle(&self) -> bool { self.data.typing_autoClosingAngleBrackets_enable } - // FIXME: VSCode seems to work wrong sometimes, see https://github.com/microsoft/vscode/issues/193124 - // hence, distinguish it for now. - pub fn is_visual_studio_code(&self) -> bool { - self.is_visual_studio_code + // VSCode is our reference implementation, so we allow ourselves to work around issues by + // special casing certain versions + pub fn visual_studio_code_version(&self) -> Option<&Version> { + self.visual_studio_code_version.as_ref() } } // Deserialization definitions @@ -2694,7 +2695,7 @@ fn proc_macro_srv_null() { AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![], - false, + None, ); config .update(serde_json::json!({ @@ -2710,7 +2711,7 @@ fn proc_macro_srv_abs() { AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![], - false, + None, ); config .update(serde_json::json!({ @@ -2726,7 +2727,7 @@ fn proc_macro_srv_rel() { AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![], - false, + None, ); config .update(serde_json::json!({ @@ -2745,7 +2746,7 @@ fn cargo_target_dir_unset() { AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![], - false, + None, ); config .update(serde_json::json!({ @@ -2764,7 +2765,7 @@ fn cargo_target_dir_subdir() { AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![], - false, + None, ); config .update(serde_json::json!({ @@ -2783,7 +2784,7 @@ fn cargo_target_dir_relative_dir() { AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![], - false, + None, ); config .update(serde_json::json!({ diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index e900f2601d8..6e6cc53c251 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -542,7 +542,7 @@ fn check_with_config(config: DiagnosticsMapConfig, diagnostics_json: &str, expec workspace_root.to_path_buf(), ClientCapabilities::default(), Vec::new(), - false, + None, ), ); let snap = state.snapshot(); diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index e2b55f4a5c5..2b0528c9bd6 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -15,6 +15,7 @@ }; use ide_db::rust_doc::format_docs; use itertools::Itertools; +use semver::VersionReq; use serde_json::to_value; use vfs::AbsPath; @@ -443,17 +444,24 @@ pub(crate) fn inlay_hint( file_id: FileId, inlay_hint: InlayHint, ) -> Cancellable { - let is_visual_studio_code = snap.config.is_visual_studio_code(); let needs_resolve = inlay_hint.needs_resolve; let (label, tooltip, mut something_to_resolve) = inlay_hint_label(snap, fields_to_resolve, needs_resolve, inlay_hint.label)?; - let text_edits = - if !is_visual_studio_code && needs_resolve && fields_to_resolve.resolve_text_edits { - something_to_resolve |= inlay_hint.text_edit.is_some(); - None - } else { - inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)) - }; + + let text_edits = if snap + .config + .visual_studio_code_version() + // https://github.com/microsoft/vscode/issues/193124 + .map_or(true, |version| VersionReq::parse(">=1.86.0").unwrap().matches(version)) + && needs_resolve + && fields_to_resolve.resolve_text_edits + { + something_to_resolve |= inlay_hint.text_edit.is_some(); + None + } else { + inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)) + }; + let data = if needs_resolve && something_to_resolve { Some(to_value(lsp_ext::InlayHintResolveData { file_id: file_id.index() }).unwrap()) } else { diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs index dfd25abc70f..1d831b8b105 100644 --- a/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/crates/rust-analyzer/tests/slow-tests/support.rs @@ -171,7 +171,7 @@ pub(crate) fn server(self) -> Server { ..Default::default() }, roots, - false, + None, ); config.update(self.config).expect("invalid config"); config.rediscover_workspaces();