From 1edcefb6859069cebf8abef16f8c069912f273e9 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 29 Apr 2023 19:45:43 +0200 Subject: [PATCH] fix: Force InitializeParams windows path drives to uppercase --- crates/rust-analyzer/src/bin/main.rs | 69 ++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 9b416d1ef00..b255a440538 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -7,7 +7,11 @@ mod logger; mod rustc_wrapper; -use std::{env, fs, path::Path, process}; +use std::{ + env, fs, + path::{Path, PathBuf}, + process, +}; use lsp_server::Connection; use rust_analyzer::{cli::flags, config::Config, from_json, Result}; @@ -149,12 +153,18 @@ fn run_server() -> Result<()> { let (initialize_id, initialize_params) = connection.initialize_start()?; tracing::info!("InitializeParams: {}", initialize_params); - let initialize_params = - from_json::("InitializeParams", &initialize_params)?; + let lsp_types::InitializeParams { + root_uri, + capabilities, + workspace_folders, + initialization_options, + client_info, + .. + } = from_json::("InitializeParams", &initialize_params)?; - let root_path = match initialize_params - .root_uri + let root_path = match root_uri .and_then(|it| it.to_file_path().ok()) + .map(patch_path_prefix) .and_then(|it| AbsPathBuf::try_from(it).ok()) { Some(it) => it, @@ -164,19 +174,19 @@ fn run_server() -> Result<()> { } }; - let workspace_roots = initialize_params - .workspace_folders + let workspace_roots = workspace_folders .map(|workspaces| { workspaces .into_iter() .filter_map(|it| it.uri.to_file_path().ok()) + .map(patch_path_prefix) .filter_map(|it| AbsPathBuf::try_from(it).ok()) .collect::>() }) .filter(|workspaces| !workspaces.is_empty()) .unwrap_or_else(|| vec![root_path.clone()]); - let mut config = Config::new(root_path, initialize_params.capabilities, workspace_roots); - if let Some(json) = initialize_params.initialization_options { + let mut config = Config::new(root_path, capabilities, workspace_roots); + if let Some(json) = initialization_options { if let Err(e) = config.update(json) { use lsp_types::{ notification::{Notification, ShowMessage}, @@ -205,7 +215,7 @@ fn run_server() -> Result<()> { connection.initialize_finish(initialize_id, initialize_result)?; - if let Some(client_info) = initialize_params.client_info { + if let Some(client_info) = client_info { tracing::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default()); } @@ -219,3 +229,42 @@ fn run_server() -> Result<()> { tracing::info!("server did shut down"); Ok(()) } + +fn patch_path_prefix(path: PathBuf) -> PathBuf { + use std::path::{Component, Prefix}; + if cfg!(windows) { + // VSCode might report paths with the file drive in lowercase, but this can mess + // with env vars set by tools and build scripts executed by r-a such that it invalidates + // cargo's compilations unnecessarily. https://github.com/rust-lang/rust-analyzer/issues/14683 + // So we just uppercase the drive letter here unconditionally. + // (doing it conditionally is a pain because std::path::Prefix always reports uppercase letters on windows) + let mut comps = path.components(); + match comps.next() { + Some(Component::Prefix(prefix)) => { + let prefix = match prefix.kind() { + Prefix::Disk(d) => { + format!("{}:", d.to_ascii_uppercase() as char) + } + Prefix::VerbatimDisk(d) => { + format!(r"\\?\{}:\", d.to_ascii_uppercase() as char) + } + _ => return path, + }; + let mut path = PathBuf::new(); + path.push(prefix); + path.extend(comps); + path + } + _ => path, + } + } else { + path + } +} + +#[test] +#[cfg(windows)] +fn patch_path_prefix_works() { + assert_eq!(patch_path_prefix(r"c:\foo\bar".into()), PathBuf::from(r"C:\foo\bar")); + assert_eq!(patch_path_prefix(r"\\?\c:\foo\bar".into()), PathBuf::from(r"\\?\C:\foo\bar")); +}