From 0a88de809f13f3b4abe0ffa11ff87c6f845050bd Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 3 Jun 2020 11:44:51 +0200 Subject: Move project discovery --- crates/ra_project_model/src/lib.rs | 16 ++++++++++++++-- crates/rust-analyzer/src/main_loop.rs | 8 ++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 9b30bef8d..d5f82f17a 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -14,7 +14,7 @@ use std::{ use anyhow::{bail, Context, Result}; use ra_cfg::CfgOptions; use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap, FxHashSet}; use serde_json::from_reader; pub use crate::{ @@ -57,7 +57,7 @@ impl PackageRoot { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] pub enum ProjectRoot { ProjectJson(PathBuf), CargoToml(PathBuf), @@ -128,6 +128,18 @@ impl ProjectRoot { .collect() } } + + pub fn discover_all(paths: &[impl AsRef]) -> Vec { + let mut res = paths + .iter() + .filter_map(|it| ProjectRoot::discover(it.as_ref()).ok()) + .flatten() + .collect::>() + .into_iter() + .collect::>(); + res.sort(); + res + } } impl ProjectWorkspace { diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 35f2d7001..82348c6fc 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -28,7 +28,7 @@ use lsp_types::{ use ra_flycheck::{CheckTask, Status}; use ra_ide::{Canceled, FileId, LibraryData, LineIndex, SourceRootId}; use ra_prof::profile; -use ra_project_model::{PackageRoot, ProjectWorkspace}; +use ra_project_model::{PackageRoot, ProjectRoot, ProjectWorkspace}; use ra_vfs::{VfsFile, VfsTask, Watch}; use relative_path::RelativePathBuf; use rustc_hash::FxHashSet; @@ -96,11 +96,7 @@ pub fn main_loop(ws_roots: Vec, config: Config, connection: Connection) let mut global_state = { let workspaces = { // FIXME: support dynamic workspace loading. - let project_roots: FxHashSet<_> = ws_roots - .iter() - .filter_map(|it| ra_project_model::ProjectRoot::discover(it).ok()) - .flatten() - .collect(); + let project_roots = ProjectRoot::discover_all(&ws_roots); if project_roots.is_empty() && config.notifications.cargo_toml_not_found { show_message( -- cgit v1.2.3 From 03a76191a18b450a8e4ae309296fd7c0614d14d2 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 3 Jun 2020 12:05:50 +0200 Subject: Rename ProjectRoot -> ProjectManifest --- crates/ra_project_model/src/lib.rs | 30 +++++++++++++++--------------- crates/rust-analyzer/src/cli/load_cargo.rs | 4 ++-- crates/rust-analyzer/src/main_loop.rs | 4 ++-- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index d5f82f17a..a99612690 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -58,24 +58,24 @@ impl PackageRoot { } #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub enum ProjectRoot { +pub enum ProjectManifest { ProjectJson(PathBuf), CargoToml(PathBuf), } -impl ProjectRoot { - pub fn from_manifest_file(path: PathBuf) -> Result { +impl ProjectManifest { + pub fn from_manifest_file(path: PathBuf) -> Result { if path.ends_with("rust-project.json") { - return Ok(ProjectRoot::ProjectJson(path)); + return Ok(ProjectManifest::ProjectJson(path)); } if path.ends_with("Cargo.toml") { - return Ok(ProjectRoot::CargoToml(path)); + return Ok(ProjectManifest::CargoToml(path)); } bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) } - pub fn discover_single(path: &Path) -> Result { - let mut candidates = ProjectRoot::discover(path)?; + pub fn discover_single(path: &Path) -> Result { + let mut candidates = ProjectManifest::discover(path)?; let res = match candidates.pop() { None => bail!("no projects"), Some(it) => it, @@ -87,12 +87,12 @@ impl ProjectRoot { Ok(res) } - pub fn discover(path: &Path) -> io::Result> { + pub fn discover(path: &Path) -> io::Result> { if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { - return Ok(vec![ProjectRoot::ProjectJson(project_json)]); + return Ok(vec![ProjectManifest::ProjectJson(project_json)]); } return find_cargo_toml(path) - .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); + .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect()); fn find_cargo_toml(path: &Path) -> io::Result> { match find_in_parent_dirs(path, "Cargo.toml") { @@ -129,10 +129,10 @@ impl ProjectRoot { } } - pub fn discover_all(paths: &[impl AsRef]) -> Vec { + pub fn discover_all(paths: &[impl AsRef]) -> Vec { let mut res = paths .iter() - .filter_map(|it| ProjectRoot::discover(it.as_ref()).ok()) + .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok()) .flatten() .collect::>() .into_iter() @@ -144,12 +144,12 @@ impl ProjectRoot { impl ProjectWorkspace { pub fn load( - root: ProjectRoot, + root: ProjectManifest, cargo_features: &CargoConfig, with_sysroot: bool, ) -> Result { let res = match root { - ProjectRoot::ProjectJson(project_json) => { + ProjectManifest::ProjectJson(project_json) => { let file = File::open(&project_json).with_context(|| { format!("Failed to open json file {}", project_json.display()) })?; @@ -160,7 +160,7 @@ impl ProjectWorkspace { })?, } } - ProjectRoot::CargoToml(cargo_toml) => { + ProjectManifest::CargoToml(cargo_toml) => { let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) .with_context(|| { format!( diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 8eaf75ff6..67491b42a 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -8,7 +8,7 @@ use crossbeam_channel::{unbounded, Receiver}; use ra_db::{ExternSourceId, FileId, SourceRootId}; use ra_ide::{AnalysisChange, AnalysisHost}; use ra_project_model::{ - get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectRoot, ProjectWorkspace, + get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectManifest, ProjectWorkspace, }; use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; use rustc_hash::{FxHashMap, FxHashSet}; @@ -28,7 +28,7 @@ pub fn load_cargo( with_proc_macro: bool, ) -> Result<(AnalysisHost, FxHashMap)> { let root = std::env::current_dir()?.join(root); - let root = ProjectRoot::discover_single(&root)?; + let root = ProjectManifest::discover_single(&root)?; let ws = ProjectWorkspace::load( root, &CargoConfig { load_out_dirs_from_check, ..Default::default() }, diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 82348c6fc..ea5b4c91c 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -28,7 +28,7 @@ use lsp_types::{ use ra_flycheck::{CheckTask, Status}; use ra_ide::{Canceled, FileId, LibraryData, LineIndex, SourceRootId}; use ra_prof::profile; -use ra_project_model::{PackageRoot, ProjectRoot, ProjectWorkspace}; +use ra_project_model::{PackageRoot, ProjectManifest, ProjectWorkspace}; use ra_vfs::{VfsFile, VfsTask, Watch}; use relative_path::RelativePathBuf; use rustc_hash::FxHashSet; @@ -96,7 +96,7 @@ pub fn main_loop(ws_roots: Vec, config: Config, connection: Connection) let mut global_state = { let workspaces = { // FIXME: support dynamic workspace loading. - let project_roots = ProjectRoot::discover_all(&ws_roots); + let project_roots = ProjectManifest::discover_all(&ws_roots); if project_roots.is_empty() && config.notifications.cargo_toml_not_found { show_message( -- cgit v1.2.3 From 8baa4c5d07690dc908b6299471c936c0d87ad871 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 3 Jun 2020 12:22:01 +0200 Subject: Groundwork for specifying the set of projects via config --- crates/rust-analyzer/src/bin/main.rs | 41 ++++++++++++++++------- crates/rust-analyzer/src/cli/load_cargo.rs | 3 +- crates/rust-analyzer/src/config.rs | 25 ++++++++++++-- crates/rust-analyzer/src/main_loop.rs | 29 +++++++--------- crates/rust-analyzer/tests/heavy_tests/support.rs | 31 +++++++++-------- 5 files changed, 83 insertions(+), 46 deletions(-) diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index e82fd57de..8d071ab1c 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -4,9 +4,14 @@ mod args; use lsp_server::Connection; -use rust_analyzer::{cli, config::Config, from_json, Result}; +use rust_analyzer::{ + cli, + config::{Config, LinkedProject}, + from_json, Result, +}; use crate::args::HelpPrinted; +use ra_project_model::ProjectManifest; fn main() -> Result<()> { setup_logging()?; @@ -97,17 +102,6 @@ fn run_server() -> Result<()> { log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default()); } - let cwd = std::env::current_dir()?; - let root = initialize_params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd); - - let workspace_roots = initialize_params - .workspace_folders - .map(|workspaces| { - workspaces.into_iter().filter_map(|it| it.uri.to_file_path().ok()).collect::>() - }) - .filter(|workspaces| !workspaces.is_empty()) - .unwrap_or_else(|| vec![root]); - let config = { let mut config = Config::default(); if let Some(value) = &initialize_params.initialization_options { @@ -115,10 +109,31 @@ fn run_server() -> Result<()> { } config.update_caps(&initialize_params.capabilities); + if config.linked_projects.is_empty() { + let cwd = std::env::current_dir()?; + let root = + initialize_params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd); + let workspace_roots = initialize_params + .workspace_folders + .map(|workspaces| { + workspaces + .into_iter() + .filter_map(|it| it.uri.to_file_path().ok()) + .collect::>() + }) + .filter(|workspaces| !workspaces.is_empty()) + .unwrap_or_else(|| vec![root]); + + config.linked_projects = ProjectManifest::discover_all(&workspace_roots) + .into_iter() + .map(LinkedProject::from) + .collect(); + } + config }; - rust_analyzer::main_loop(workspace_roots, config, connection)?; + rust_analyzer::main_loop(config, connection)?; log::info!("shutting down IO..."); io_threads.join()?; diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 67491b42a..c7e86fe0c 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -8,7 +8,8 @@ use crossbeam_channel::{unbounded, Receiver}; use ra_db::{ExternSourceId, FileId, SourceRootId}; use ra_ide::{AnalysisChange, AnalysisHost}; use ra_project_model::{ - get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectManifest, ProjectWorkspace, + get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectManifest, + ProjectWorkspace, }; use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; use rustc_hash::{FxHashMap, FxHashSet}; diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 9c6e369d2..761bc9c2d 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -12,14 +12,13 @@ use std::{ffi::OsString, path::PathBuf}; use lsp_types::ClientCapabilities; use ra_flycheck::FlycheckConfig; use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig}; -use ra_project_model::CargoConfig; +use ra_project_model::{CargoConfig, JsonProject, ProjectManifest}; use serde::Deserialize; #[derive(Debug, Clone)] pub struct Config { pub client_caps: ClientCapsConfig, - pub with_sysroot: bool, pub publish_diagnostics: bool, pub lru_capacity: Option, pub proc_macro_srv: Option<(PathBuf, Vec)>, @@ -35,6 +34,27 @@ pub struct Config { pub assist: AssistConfig, pub call_info_full: bool, pub lens: LensConfig, + + pub with_sysroot: bool, + pub linked_projects: Vec, +} + +#[derive(Debug, Clone)] +pub enum LinkedProject { + ProjectManifest(ProjectManifest), + JsonProject(JsonProject), +} + +impl From for LinkedProject { + fn from(v: ProjectManifest) -> Self { + LinkedProject::ProjectManifest(v) + } +} + +impl From for LinkedProject { + fn from(v: JsonProject) -> Self { + LinkedProject::JsonProject(v) + } } #[derive(Clone, Debug, PartialEq, Eq)] @@ -141,6 +161,7 @@ impl Default for Config { assist: AssistConfig::default(), call_info_full: true, lens: LensConfig::default(), + linked_projects: Vec::new(), } } } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index ea5b4c91c..d901f21d7 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -12,13 +12,11 @@ use std::{ fmt, ops::Range, panic, - path::PathBuf, sync::Arc, time::{Duration, Instant}, }; use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; -use itertools::Itertools; use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; use lsp_types::{ DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress, @@ -28,7 +26,7 @@ use lsp_types::{ use ra_flycheck::{CheckTask, Status}; use ra_ide::{Canceled, FileId, LibraryData, LineIndex, SourceRootId}; use ra_prof::profile; -use ra_project_model::{PackageRoot, ProjectManifest, ProjectWorkspace}; +use ra_project_model::{PackageRoot, ProjectWorkspace}; use ra_vfs::{VfsFile, VfsTask, Watch}; use relative_path::RelativePathBuf; use rustc_hash::FxHashSet; @@ -36,7 +34,7 @@ use serde::{de::DeserializeOwned, Serialize}; use threadpool::ThreadPool; use crate::{ - config::{Config, FilesWatcher}, + config::{Config, FilesWatcher, LinkedProject}, diagnostics::{to_proto::url_from_path_with_drive_lowercasing, DiagnosticTask}, from_proto, global_state::{GlobalState, GlobalStateSnapshot}, @@ -70,7 +68,7 @@ impl fmt::Display for LspError { impl Error for LspError {} -pub fn main_loop(ws_roots: Vec, config: Config, connection: Connection) -> Result<()> { +pub fn main_loop(config: Config, connection: Connection) -> Result<()> { log::info!("initial config: {:#?}", config); // Windows scheduler implements priority boosts: if thread waits for an @@ -95,25 +93,24 @@ pub fn main_loop(ws_roots: Vec, config: Config, connection: Connection) let mut loop_state = LoopState::default(); let mut global_state = { let workspaces = { - // FIXME: support dynamic workspace loading. - let project_roots = ProjectManifest::discover_all(&ws_roots); - - if project_roots.is_empty() && config.notifications.cargo_toml_not_found { + if config.linked_projects.is_empty() && config.notifications.cargo_toml_not_found { show_message( lsp_types::MessageType::Error, - format!( - "rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}", - ws_roots.iter().format_with(", ", |it, f| f(&it.display())) - ), + "rust-analyzer failed to discover workspace".to_string(), &connection.sender, ); }; - project_roots - .into_iter() + config + .linked_projects + .iter() + .filter_map(|project| match project { + LinkedProject::ProjectManifest(it) => Some(it), + LinkedProject::JsonProject(_) => None, + }) .filter_map(|root| { ra_project_model::ProjectWorkspace::load( - root, + root.clone(), &config.cargo, config.with_sysroot, ) diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs index 66a6f4d54..30d03b622 100644 --- a/crates/rust-analyzer/tests/heavy_tests/support.rs +++ b/crates/rust-analyzer/tests/heavy_tests/support.rs @@ -19,8 +19,9 @@ use serde_json::{to_string_pretty, Value}; use tempfile::TempDir; use test_utils::{find_mismatch, parse_fixture}; +use ra_project_model::ProjectManifest; use rust_analyzer::{ - config::{ClientCapsConfig, Config}, + config::{ClientCapsConfig, Config, LinkedProject}, main_loop, }; @@ -42,7 +43,7 @@ impl<'a> Project<'a> { self } - pub fn root(mut self, path: &str) -> Project<'a> { + pub(crate) fn root(mut self, path: &str) -> Project<'a> { self.roots.push(path.into()); self } @@ -74,7 +75,16 @@ impl<'a> Project<'a> { paths.push((path, entry.text)); } - let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect(); + let mut roots = + self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect::>(); + if roots.is_empty() { + roots.push(tmp_dir.path().to_path_buf()); + } + let linked_projects = roots + .into_iter() + .map(|it| ProjectManifest::discover_single(&it).unwrap()) + .map(LinkedProject::from) + .collect::>(); let mut config = Config { client_caps: ClientCapsConfig { @@ -84,6 +94,7 @@ impl<'a> Project<'a> { ..Default::default() }, with_sysroot: self.with_sysroot, + linked_projects, ..Config::default() }; @@ -91,7 +102,7 @@ impl<'a> Project<'a> { f(&mut config) } - Server::new(tmp_dir, config, roots, paths) + Server::new(tmp_dir, config, paths) } } @@ -109,20 +120,12 @@ pub struct Server { } impl Server { - fn new( - dir: TempDir, - config: Config, - roots: Vec, - files: Vec<(PathBuf, String)>, - ) -> Server { - let path = dir.path().to_path_buf(); - - let roots = if roots.is_empty() { vec![path] } else { roots }; + fn new(dir: TempDir, config: Config, files: Vec<(PathBuf, String)>) -> Server { let (connection, client) = Connection::memory(); let _thread = jod_thread::Builder::new() .name("test server".to_string()) - .spawn(move || main_loop(roots, config, connection).unwrap()) + .spawn(move || main_loop(config, connection).unwrap()) .expect("failed to spawn a thread"); let res = -- cgit v1.2.3