diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 1bc164b157a7..90857a307310 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -1070,6 +1070,7 @@ struct ClientInfo { version: Option, } +/// The configuration of this rust-analyzer instance. #[derive(Clone)] pub struct Config { /// Projects that have a Cargo.toml or a rust-project.json in a @@ -1079,11 +1080,16 @@ pub struct Config { /// Projects whose configuration was generated by a command /// configured in discoverConfig. discovered_projects_from_command: Vec, - /// The workspace roots as registered by the LSP client + /// The workspace roots as registered by the LSP client. workspace_roots: Vec, caps: ClientCapabilities, - /// The LSP root path, deprecated in favor of `workspace_roots` + + /// The root of the first project encountered. This is deprecated + /// because rust-analyzer might be handling multiple projects. + /// + /// Prefer `workspace_roots` and `workspace_root_for()`. root_path: AbsPathBuf, + snippets: Vec, client_info: Option, @@ -1787,9 +1793,23 @@ fn sort_objects_by_field(json: &mut serde_json::Value) { s } - pub fn root_path(&self) -> &AbsPathBuf { - // We should probably use `workspace_roots` here if set - &self.root_path + /// Find the workspace root that contains the given path, using the + /// longest prefix match. + pub fn workspace_root_for(&self, path: &AbsPath) -> &AbsPathBuf { + self.workspace_roots + .iter() + .filter(|root| path.starts_with(root.as_path())) + .max_by_key(|root| root.as_str().len()) + .unwrap_or(self.default_root_path()) + } + + /// Best-effort root path for the current project. + /// + /// Use `workspace_root_for` where possible, because + /// `default_root_path` may return the wrong path when a user has + /// multiple workspaces. + pub fn default_root_path(&self) -> &AbsPathBuf { + self.workspace_roots.first().unwrap_or(&self.root_path) } pub fn caps(&self) -> &ClientCapabilities { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index c24591b7ab75..9c2e0a5f321b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -2453,7 +2453,14 @@ fn run_rustfmt( let cmd_path = if command.contains(std::path::MAIN_SEPARATOR) || (cfg!(windows) && command.contains('/')) { - snap.config.root_path().join(cmd).into() + let project_root = Utf8PathBuf::from_path_buf(current_dir.clone()) + .ok() + .and_then(|p| AbsPathBuf::try_from(p).ok()); + let project_root = project_root + .as_ref() + .map(|dir| snap.config.workspace_root_for(dir)) + .unwrap_or(snap.config.default_root_path()); + project_root.join(cmd).into() } else { cmd }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 7c494de6f73d..a8c3d062d041 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -830,12 +830,19 @@ fn handle_task(&mut self, prime_caches_progress: &mut Vec, let command = cfg.command.clone(); let discover = DiscoverCommand::new(self.discover_sender.clone(), command); + let discover_path = match &arg { + DiscoverProjectParam::Buildfile(it) => it, + DiscoverProjectParam::Path(it) => it, + }; + let current_dir = + self.config.workspace_root_for(discover_path.as_path()).clone(); + let arg = match arg { DiscoverProjectParam::Buildfile(it) => DiscoverArgument::Buildfile(it), DiscoverProjectParam::Path(it) => DiscoverArgument::Path(it), }; - match discover.spawn(arg, self.config.root_path().as_ref()) { + match discover.spawn(arg, current_dir.as_ref()) { Ok(handle) => { if self.discover_jobs_active == 0 { let title = &cfg.progress_label.clone(); @@ -953,7 +960,7 @@ fn handle_vfs_msg( if let Some(dir) = dir { message += &format!( ": {}", - match dir.strip_prefix(self.config.root_path()) { + match dir.strip_prefix(self.config.workspace_root_for(&dir)) { Some(relative_path) => relative_path.as_utf8_path(), None => dir.as_ref(), } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 83f4a19b39fa..71accbed4ef1 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -390,7 +390,7 @@ pub(crate) fn fetch_build_data(&mut self, cause: Cause) { info!(%cause, "will fetch build data"); let workspaces = Arc::clone(&self.workspaces); let config = self.config.cargo(None); - let root_path = self.config.root_path().clone(); + let root_path = self.config.default_root_path().clone(); self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| { sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap(); @@ -883,7 +883,7 @@ fn reload_flycheck(&mut self) { config, crate::flycheck::FlycheckConfigJson::default(), None, - self.config.root_path().clone(), + self.config.default_root_path().clone(), None, None, )]