From 72fb712dff63a518e5b607030266e30ea7542756 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 25 Jun 2020 23:44:58 +0200 Subject: Add new module for project loading stuff --- crates/rust-analyzer/src/cli/load_cargo.rs | 2 +- crates/rust-analyzer/src/config.rs | 10 +- crates/rust-analyzer/src/global_state.rs | 244 ++--------------------------- crates/rust-analyzer/src/lib.rs | 1 + crates/rust-analyzer/src/reload.rs | 241 ++++++++++++++++++++++++++++ 5 files changed, 260 insertions(+), 238 deletions(-) create mode 100644 crates/rust-analyzer/src/reload.rs (limited to 'crates') diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index c5cf5ff27..b1250f2fe 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -9,7 +9,7 @@ use ra_ide::{AnalysisChange, AnalysisHost}; use ra_project_model::{CargoConfig, ProcMacroClient, ProjectManifest, ProjectWorkspace}; use vfs::{loader::Handle, AbsPath}; -use crate::global_state::{ProjectFolders, SourceRootConfig}; +use crate::reload::{ProjectFolders, SourceRootConfig}; pub fn load_cargo( root: &Path, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 435bbbb6b..6b17ce18b 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -30,7 +30,7 @@ pub struct Config { pub cargo: CargoConfig, pub rustfmt: RustfmtConfig, - pub check: Option, + pub flycheck: Option, pub inlay_hints: InlayHintsConfig, pub completion: CompletionConfig, @@ -147,7 +147,7 @@ impl Config { cargo: CargoConfig::default(), rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() }, - check: Some(FlycheckConfig::CargoCommand { + flycheck: Some(FlycheckConfig::CargoCommand { command: "check".to_string(), all_targets: true, all_features: false, @@ -227,14 +227,14 @@ impl Config { if let Some(false) = get(value, "/checkOnSave/enable") { // check is disabled - self.check = None; + self.flycheck = None; } else { // check is enabled match get::>(value, "/checkOnSave/overrideCommand") { // first see if the user has completely overridden the command Some(mut args) if !args.is_empty() => { let command = args.remove(0); - self.check = Some(FlycheckConfig::CustomCommand { + self.flycheck = Some(FlycheckConfig::CustomCommand { command, args, }); @@ -242,7 +242,7 @@ impl Config { // otherwise configure command customizations _ => { if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets, all_features, features }) - = &mut self.check + = &mut self.flycheck { set(value, "/checkOnSave/extraArgs", extra_args); set(value, "/checkOnSave/command", command); diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index ca4a248f1..8486da627 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -3,24 +3,25 @@ //! //! Each tick provides an immutable snapshot of the state as `WorldSnapshot`. -use std::{convert::TryFrom, sync::Arc}; +use std::sync::Arc; use crossbeam_channel::{unbounded, Receiver, Sender}; -use flycheck::{FlycheckConfig, FlycheckHandle}; -use lsp_types::{request::Request, Url}; +use flycheck::FlycheckHandle; +use lsp_types::Url; use parking_lot::RwLock; -use ra_db::{CrateId, SourceRoot, VfsPath}; -use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId}; -use ra_project_model::{CargoWorkspace, PackageRoot, ProcMacroClient, ProjectWorkspace, Target}; +use ra_db::{CrateId, VfsPath}; +use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId}; +use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target}; use stdx::format_to; -use vfs::{file_set::FileSetConfig, loader::Handle, AbsPath, AbsPathBuf}; +use vfs::loader::Handle; use crate::{ - config::{Config, FilesWatcher, LinkedProject}, + config::Config, diagnostics::{CheckFixes, DiagnosticCollection}, from_proto, line_endings::LineEndings, main_loop::{ReqQueue, Task}, + reload::SourceRootConfig, request_metrics::{LatestRequests, RequestMetrics}, show_message, thread_pool::TaskPool, @@ -29,26 +30,6 @@ use crate::{ }; use rustc_hash::{FxHashMap, FxHashSet}; -fn create_flycheck( - workspaces: &[ProjectWorkspace], - config: &FlycheckConfig, -) -> Option<(FlycheckHandle, Receiver)> { - // FIXME: Figure out the multi-workspace situation - workspaces.iter().find_map(move |w| match w { - ProjectWorkspace::Cargo { cargo, .. } => { - let (sender, receiver) = unbounded(); - let sender = Box::new(move |msg| sender.send(msg).unwrap()); - let cargo_project_root = cargo.workspace_root().to_path_buf(); - let flycheck = FlycheckHandle::spawn(sender, config.clone(), cargo_project_root.into()); - Some((flycheck, receiver)) - } - ProjectWorkspace::Json { .. } => { - log::warn!("Cargo check watching only supported for cargo workspaces, disabling"); - None - } - }) -} - #[derive(Eq, PartialEq)] pub(crate) enum Status { Loading, @@ -79,10 +60,10 @@ pub(crate) struct GlobalState { pub(crate) vfs: Arc)>>, pub(crate) status: Status, pub(crate) req_queue: ReqQueue, + pub(crate) source_root_config: SourceRootConfig, + pub(crate) proc_macro_client: ProcMacroClient, + pub(crate) workspaces: Arc>, latest_requests: Arc>, - source_root_config: SourceRootConfig, - proc_macro_client: ProcMacroClient, - workspaces: Arc>, } /// An immutable snapshot of the world's state at a point in time. @@ -135,132 +116,6 @@ impl GlobalState { } } - pub(crate) fn reload(&mut self) { - let workspaces = { - if self.config.linked_projects.is_empty() - && self.config.notifications.cargo_toml_not_found - { - self.show_message( - lsp_types::MessageType::Error, - "rust-analyzer failed to discover workspace".to_string(), - ); - }; - - self.config - .linked_projects - .iter() - .filter_map(|project| match project { - LinkedProject::ProjectManifest(manifest) => { - ra_project_model::ProjectWorkspace::load( - manifest.clone(), - &self.config.cargo, - self.config.with_sysroot, - ) - .map_err(|err| { - log::error!("failed to load workspace: {:#}", err); - self.show_message( - lsp_types::MessageType::Error, - format!("rust-analyzer failed to load workspace: {:#}", err), - ); - }) - .ok() - } - LinkedProject::InlineJsonProject(it) => { - Some(ra_project_model::ProjectWorkspace::Json { project: it.clone() }) - } - }) - .collect::>() - }; - - if let FilesWatcher::Client = self.config.files.watcher { - let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions { - watchers: workspaces - .iter() - .flat_map(ProjectWorkspace::to_roots) - .filter(PackageRoot::is_member) - .map(|root| format!("{}/**/*.rs", root.path().display())) - .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None }) - .collect(), - }; - let registration = lsp_types::Registration { - id: "file-watcher".to_string(), - method: "workspace/didChangeWatchedFiles".to_string(), - register_options: Some(serde_json::to_value(registration_options).unwrap()), - }; - let params = lsp_types::RegistrationParams { registrations: vec![registration] }; - let request = self.req_queue.outgoing.register( - lsp_types::request::RegisterCapability::METHOD.to_string(), - params, - |_, _| (), - ); - self.send(request.into()); - } - - let mut change = AnalysisChange::new(); - - let project_folders = ProjectFolders::new(&workspaces); - - self.proc_macro_client = match &self.config.proc_macro_srv { - None => ProcMacroClient::dummy(), - Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) { - Ok(it) => it, - Err(err) => { - log::error!( - "Failed to run ra_proc_macro_srv from path {}, error: {:?}", - path.display(), - err - ); - ProcMacroClient::dummy() - } - }, - }; - let watch = match self.config.files.watcher { - FilesWatcher::Client => vec![], - FilesWatcher::Notify => project_folders.watch, - }; - self.loader.set_config(vfs::loader::Config { load: project_folders.load, watch }); - - // Create crate graph from all the workspaces - let crate_graph = { - let mut crate_graph = CrateGraph::default(); - let vfs = &mut self.vfs.write().0; - let loader = &mut self.loader; - let mut load = |path: &AbsPath| { - let contents = loader.load_sync(path); - let path = vfs::VfsPath::from(path.to_path_buf()); - vfs.set_file_contents(path.clone(), contents); - vfs.file_id(&path) - }; - for ws in workspaces.iter() { - crate_graph.extend(ws.to_crate_graph( - self.config.cargo.target.as_deref(), - &self.proc_macro_client, - &mut load, - )); - } - - crate_graph - }; - change.set_crate_graph(crate_graph); - - self.flycheck = self.config.check.as_ref().and_then(|c| create_flycheck(&workspaces, c)); - self.source_root_config = project_folders.source_root_config; - self.workspaces = Arc::new(workspaces); - - self.analysis_host.apply_change(change); - self.process_changes(); - } - - pub(crate) fn update_configuration(&mut self, config: Config) { - self.analysis_host.update_lru_capacity(config.lru_capacity); - if config.check != self.config.check { - self.flycheck = - config.check.as_ref().and_then(|it| create_flycheck(&self.workspaces, it)); - } - - self.config = config; - } - pub(crate) fn process_changes(&mut self) -> bool { let change = { let mut change = AnalysisChange::new(); @@ -408,78 +263,3 @@ pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url { let path = path.as_path().unwrap(); url_from_abs_path(&path) } - -#[derive(Default)] -pub(crate) struct ProjectFolders { - pub(crate) load: Vec, - pub(crate) watch: Vec, - pub(crate) source_root_config: SourceRootConfig, -} - -impl ProjectFolders { - pub(crate) fn new(workspaces: &[ProjectWorkspace]) -> ProjectFolders { - let mut res = ProjectFolders::default(); - let mut fsc = FileSetConfig::builder(); - let mut local_filesets = vec![]; - - for root in workspaces.iter().flat_map(|it| it.to_roots()) { - let path = root.path().to_owned(); - - let mut file_set_roots: Vec = vec![]; - - let entry = if root.is_member() { - vfs::loader::Entry::local_cargo_package(path.to_path_buf()) - } else { - vfs::loader::Entry::cargo_package_dependency(path.to_path_buf()) - }; - res.load.push(entry); - if root.is_member() { - res.watch.push(res.load.len() - 1); - } - - if let Some(out_dir) = root.out_dir() { - let out_dir = AbsPathBuf::try_from(out_dir.to_path_buf()).unwrap(); - res.load.push(vfs::loader::Entry::rs_files_recursively(out_dir.clone())); - if root.is_member() { - res.watch.push(res.load.len() - 1); - } - file_set_roots.push(out_dir.into()); - } - file_set_roots.push(path.to_path_buf().into()); - - if root.is_member() { - local_filesets.push(fsc.len()); - } - fsc.add_file_set(file_set_roots) - } - - let fsc = fsc.build(); - res.source_root_config = SourceRootConfig { fsc, local_filesets }; - - res - } -} - -#[derive(Default, Debug)] -pub(crate) struct SourceRootConfig { - pub(crate) fsc: FileSetConfig, - pub(crate) local_filesets: Vec, -} - -impl SourceRootConfig { - pub(crate) fn partition(&self, vfs: &vfs::Vfs) -> Vec { - self.fsc - .partition(vfs) - .into_iter() - .enumerate() - .map(|(idx, file_set)| { - let is_local = self.local_filesets.contains(&idx); - if is_local { - SourceRoot::new_local(file_set) - } else { - SourceRoot::new_library(file_set) - } - }) - .collect() - } -} diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index d503fe96e..a24dfe58c 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs @@ -18,6 +18,7 @@ macro_rules! eprintln { } mod global_state; +mod reload; mod main_loop; mod dispatch; mod handlers; diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs new file mode 100644 index 000000000..1981a97da --- /dev/null +++ b/crates/rust-analyzer/src/reload.rs @@ -0,0 +1,241 @@ +//! Project loading & configuration updates +use std::sync::Arc; + +use crossbeam_channel::unbounded; +use flycheck::FlycheckHandle; +use lsp_types::request::Request; +use ra_db::{CrateGraph, SourceRoot, VfsPath}; +use ra_ide::AnalysisChange; +use ra_project_model::{PackageRoot, ProcMacroClient, ProjectWorkspace}; +use vfs::{file_set::FileSetConfig, AbsPath}; + +use crate::{ + config::{Config, FilesWatcher, LinkedProject}, + global_state::GlobalState, +}; + +impl GlobalState { + pub(crate) fn update_configuration(&mut self, new_config: Config) { + self.analysis_host.update_lru_capacity(new_config.lru_capacity); + if new_config.flycheck != self.config.flycheck { + self.reload_flycheck(); + } + self.config = new_config; + } + pub(crate) fn reload(&mut self) { + let workspaces = { + if self.config.linked_projects.is_empty() + && self.config.notifications.cargo_toml_not_found + { + self.show_message( + lsp_types::MessageType::Error, + "rust-analyzer failed to discover workspace".to_string(), + ); + }; + + self.config + .linked_projects + .iter() + .filter_map(|project| match project { + LinkedProject::ProjectManifest(manifest) => { + ra_project_model::ProjectWorkspace::load( + manifest.clone(), + &self.config.cargo, + self.config.with_sysroot, + ) + .map_err(|err| { + log::error!("failed to load workspace: {:#}", err); + self.show_message( + lsp_types::MessageType::Error, + format!("rust-analyzer failed to load workspace: {:#}", err), + ); + }) + .ok() + } + LinkedProject::InlineJsonProject(it) => { + Some(ra_project_model::ProjectWorkspace::Json { project: it.clone() }) + } + }) + .collect::>() + }; + + if let FilesWatcher::Client = self.config.files.watcher { + let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions { + watchers: workspaces + .iter() + .flat_map(ProjectWorkspace::to_roots) + .filter(PackageRoot::is_member) + .map(|root| format!("{}/**/*.rs", root.path().display())) + .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None }) + .collect(), + }; + let registration = lsp_types::Registration { + id: "file-watcher".to_string(), + method: "workspace/didChangeWatchedFiles".to_string(), + register_options: Some(serde_json::to_value(registration_options).unwrap()), + }; + let params = lsp_types::RegistrationParams { registrations: vec![registration] }; + let request = self.req_queue.outgoing.register( + lsp_types::request::RegisterCapability::METHOD.to_string(), + params, + |_, _| (), + ); + self.send(request.into()); + } + + let mut change = AnalysisChange::new(); + + let project_folders = ProjectFolders::new(&workspaces); + + self.proc_macro_client = match &self.config.proc_macro_srv { + None => ProcMacroClient::dummy(), + Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) { + Ok(it) => it, + Err(err) => { + log::error!( + "Failed to run ra_proc_macro_srv from path {}, error: {:?}", + path.display(), + err + ); + ProcMacroClient::dummy() + } + }, + }; + let watch = match self.config.files.watcher { + FilesWatcher::Client => vec![], + FilesWatcher::Notify => project_folders.watch, + }; + self.loader.set_config(vfs::loader::Config { load: project_folders.load, watch }); + + // Create crate graph from all the workspaces + let crate_graph = { + let mut crate_graph = CrateGraph::default(); + let vfs = &mut self.vfs.write().0; + let loader = &mut self.loader; + let mut load = |path: &AbsPath| { + let contents = loader.load_sync(path); + let path = vfs::VfsPath::from(path.to_path_buf()); + vfs.set_file_contents(path.clone(), contents); + vfs.file_id(&path) + }; + for ws in workspaces.iter() { + crate_graph.extend(ws.to_crate_graph( + self.config.cargo.target.as_deref(), + &self.proc_macro_client, + &mut load, + )); + } + + crate_graph + }; + change.set_crate_graph(crate_graph); + + self.source_root_config = project_folders.source_root_config; + self.workspaces = Arc::new(workspaces); + + self.analysis_host.apply_change(change); + self.process_changes(); + self.reload_flycheck(); + } + + fn reload_flycheck(&mut self) { + let config = match self.config.flycheck.clone() { + Some(it) => it, + None => { + self.flycheck = None; + return; + } + }; + + // FIXME: Figure out the multi-workspace situation + self.flycheck = self.workspaces.iter().find_map(move |w| match w { + ProjectWorkspace::Cargo { cargo, .. } => { + let (sender, receiver) = unbounded(); + let sender = Box::new(move |msg| sender.send(msg).unwrap()); + let cargo_project_root = cargo.workspace_root().to_path_buf(); + let flycheck = + FlycheckHandle::spawn(sender, config.clone(), cargo_project_root.into()); + Some((flycheck, receiver)) + } + ProjectWorkspace::Json { .. } => { + log::warn!("Cargo check watching only supported for cargo workspaces, disabling"); + None + } + }) + } +} + +#[derive(Default)] +pub(crate) struct ProjectFolders { + pub(crate) load: Vec, + pub(crate) watch: Vec, + pub(crate) source_root_config: SourceRootConfig, +} + +impl ProjectFolders { + pub(crate) fn new(workspaces: &[ProjectWorkspace]) -> ProjectFolders { + let mut res = ProjectFolders::default(); + let mut fsc = FileSetConfig::builder(); + let mut local_filesets = vec![]; + + for root in workspaces.iter().flat_map(|it| it.to_roots()) { + let path = root.path().to_owned(); + + let mut file_set_roots: Vec = vec![]; + + let entry = if root.is_member() { + vfs::loader::Entry::local_cargo_package(path.to_path_buf()) + } else { + vfs::loader::Entry::cargo_package_dependency(path.to_path_buf()) + }; + res.load.push(entry); + if root.is_member() { + res.watch.push(res.load.len() - 1); + } + + if let Some(out_dir) = root.out_dir() { + let out_dir = out_dir.to_path_buf(); + res.load.push(vfs::loader::Entry::rs_files_recursively(out_dir.clone())); + if root.is_member() { + res.watch.push(res.load.len() - 1); + } + file_set_roots.push(out_dir.into()); + } + file_set_roots.push(path.to_path_buf().into()); + + if root.is_member() { + local_filesets.push(fsc.len()); + } + fsc.add_file_set(file_set_roots) + } + + let fsc = fsc.build(); + res.source_root_config = SourceRootConfig { fsc, local_filesets }; + + res + } +} + +#[derive(Default, Debug)] +pub(crate) struct SourceRootConfig { + pub(crate) fsc: FileSetConfig, + pub(crate) local_filesets: Vec, +} + +impl SourceRootConfig { + pub(crate) fn partition(&self, vfs: &vfs::Vfs) -> Vec { + self.fsc + .partition(vfs) + .into_iter() + .enumerate() + .map(|(idx, file_set)| { + let is_local = self.local_filesets.contains(&idx); + if is_local { + SourceRoot::new_local(file_set) + } else { + SourceRoot::new_library(file_set) + } + }) + .collect() + } +} -- cgit v1.2.3