From 73d73077febba921918b5611574bf514eae63006 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 25 Jun 2020 23:11:59 +0200 Subject: Separate creation and initialization of global state --- crates/rust-analyzer/src/global_state.rs | 121 +++++++++++++++++-------------- 1 file changed, 65 insertions(+), 56 deletions(-) (limited to 'crates') diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 149b1b5f9..be5a3f8a7 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -81,7 +81,7 @@ pub(crate) struct GlobalState { pub(crate) req_queue: ReqQueue, latest_requests: Arc>, source_root_config: SourceRootConfig, - _proc_macro_client: ProcMacroClient, + proc_macro_client: ProcMacroClient, workspaces: Arc>, } @@ -103,61 +103,14 @@ impl GlobalState { config: Config, req_queue: ReqQueue, ) -> GlobalState { - let mut change = AnalysisChange::new(); - - let project_folders = ProjectFolders::new(&workspaces); - let (task_sender, task_receiver) = unbounded::(); - let mut vfs = vfs::Vfs::default(); - - let proc_macro_client = match &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 mut loader = { + let loader = { let loader = vfs_notify::NotifyHandle::spawn(Box::new(move |msg| { task_sender.send(msg).unwrap() })); Box::new(loader) }; - let watch = match config.files.watcher { - FilesWatcher::Client => vec![], - FilesWatcher::Notify => project_folders.watch, - }; - loader.set_config(vfs::loader::Config { load: project_folders.load, watch }); - - // Create crate graph from all the workspaces - let mut crate_graph = CrateGraph::default(); - 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( - config.cargo.target.as_deref(), - &proc_macro_client, - &mut load, - )); - } - change.set_crate_graph(crate_graph); - - let flycheck = config.check.as_ref().and_then(|c| create_flycheck(&workspaces, c)); - - let mut analysis_host = AnalysisHost::new(lru_capacity); - analysis_host.apply_change(change); let task_pool = { let (sender, receiver) = unbounded(); @@ -168,24 +121,80 @@ impl GlobalState { sender, config, task_pool, - analysis_host, + analysis_host: AnalysisHost::new(lru_capacity), loader, task_receiver, - flycheck, + flycheck: None, diagnostics: Default::default(), mem_docs: FxHashSet::default(), - vfs: Arc::new(RwLock::new((vfs, FxHashMap::default()))), + vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))), status: Status::default(), req_queue, latest_requests: Default::default(), - source_root_config: project_folders.source_root_config, - _proc_macro_client: proc_macro_client, - workspaces: Arc::new(workspaces), + source_root_config: SourceRootConfig::default(), + proc_macro_client: ProcMacroClient::dummy(), + workspaces: Arc::new(Vec::new()), }; - res.process_changes(); + res.reload(workspaces); res } + pub(crate) fn reload(&mut self, workspaces: Vec) { + 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 { -- cgit v1.2.3 From 3d0f78213879be78064d70a54411e40a6392a224 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 25 Jun 2020 23:26:21 +0200 Subject: Prep dynamic workspace loading --- crates/rust-analyzer/src/global_state.rs | 80 +++++++++++++++++++++++---- crates/rust-analyzer/src/main_loop.rs | 95 ++++---------------------------- 2 files changed, 79 insertions(+), 96 deletions(-) (limited to 'crates') diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index be5a3f8a7..ca4a248f1 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -7,16 +7,16 @@ use std::{convert::TryFrom, sync::Arc}; use crossbeam_channel::{unbounded, Receiver, Sender}; use flycheck::{FlycheckConfig, FlycheckHandle}; -use lsp_types::Url; +use lsp_types::{request::Request, Url}; use parking_lot::RwLock; use ra_db::{CrateId, SourceRoot, VfsPath}; use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId}; -use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target}; +use ra_project_model::{CargoWorkspace, PackageRoot, ProcMacroClient, ProjectWorkspace, Target}; use stdx::format_to; use vfs::{file_set::FileSetConfig, loader::Handle, AbsPath, AbsPathBuf}; use crate::{ - config::{Config, FilesWatcher}, + config::{Config, FilesWatcher, LinkedProject}, diagnostics::{CheckFixes, DiagnosticCollection}, from_proto, line_endings::LineEndings, @@ -98,10 +98,8 @@ pub(crate) struct GlobalStateSnapshot { impl GlobalState { pub(crate) fn new( sender: Sender, - workspaces: Vec, lru_capacity: Option, config: Config, - req_queue: ReqQueue, ) -> GlobalState { let (task_sender, task_receiver) = unbounded::(); @@ -117,7 +115,7 @@ impl GlobalState { (TaskPool::new(sender), receiver) }; - let mut res = GlobalState { + GlobalState { sender, config, task_pool, @@ -129,17 +127,75 @@ impl GlobalState { mem_docs: FxHashSet::default(), vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))), status: Status::default(), - req_queue, + req_queue: ReqQueue::default(), latest_requests: Default::default(), source_root_config: SourceRootConfig::default(), proc_macro_client: ProcMacroClient::dummy(), workspaces: Arc::new(Vec::new()), - }; - res.reload(workspaces); - res + } } - pub(crate) fn reload(&mut self, workspaces: Vec) { + 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); @@ -275,7 +331,7 @@ impl GlobalState { self.send(response.into()); } } - pub(crate) fn show_message(&mut self, typ: lsp_types::MessageType, message: String) { + pub(crate) fn show_message(&self, typ: lsp_types::MessageType, message: String) { show_message(typ, message, &self.sender) } } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 1bd9d6389..6ac50745a 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -11,17 +11,14 @@ use lsp_types::{notification::Notification as _, request::Request as _}; use ra_db::VfsPath; use ra_ide::{Canceled, FileId}; use ra_prof::profile; -use ra_project_model::{PackageRoot, ProjectWorkspace}; use crate::{ - config::{Config, FilesWatcher, LinkedProject}, + config::Config, dispatch::{NotificationDispatcher, RequestDispatcher}, from_proto, global_state::{file_id_to_url, GlobalState, Status}, handlers, lsp_ext, - lsp_utils::{ - apply_document_changes, is_canceled, notification_is, notification_new, show_message, - }, + lsp_utils::{apply_document_changes, is_canceled, notification_is, notification_new}, Result, }; @@ -47,81 +44,8 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { SetThreadPriority(thread, thread_priority_above_normal); } - let global_state = { - let workspaces = { - if config.linked_projects.is_empty() && config.notifications.cargo_toml_not_found { - show_message( - lsp_types::MessageType::Error, - "rust-analyzer failed to discover workspace".to_string(), - &connection.sender, - ); - }; - - config - .linked_projects - .iter() - .filter_map(|project| match project { - LinkedProject::ProjectManifest(manifest) => { - ra_project_model::ProjectWorkspace::load( - manifest.clone(), - &config.cargo, - config.with_sysroot, - ) - .map_err(|err| { - log::error!("failed to load workspace: {:#}", err); - show_message( - lsp_types::MessageType::Error, - format!("rust-analyzer failed to load workspace: {:#}", err), - &connection.sender, - ); - }) - .ok() - } - LinkedProject::InlineJsonProject(it) => { - Some(ra_project_model::ProjectWorkspace::Json { project: it.clone() }) - } - }) - .collect::>() - }; - - let mut req_queue = ReqQueue::default(); - - if let FilesWatcher::Client = 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 = req_queue.outgoing.register( - lsp_types::request::RegisterCapability::METHOD.to_string(), - params, - DO_NOTHING, - ); - connection.sender.send(request.into()).unwrap(); - } - - GlobalState::new( - connection.sender.clone(), - workspaces, - config.lru_capacity, - config, - req_queue, - ) - }; - - log::info!("server initialized, serving requests"); - global_state.run(connection.receiver)?; - Ok(()) + GlobalState::new(connection.sender.clone(), config.lru_capacity, config) + .run(connection.receiver) } enum Event { @@ -188,23 +112,26 @@ impl GlobalState { } fn run(mut self, inbox: Receiver) -> Result<()> { + self.reload(); + while let Some(event) = self.next_event(&inbox) { if let Event::Lsp(lsp_server::Message::Notification(not)) = &event { if not.method == lsp_types::notification::Exit::METHOD { return Ok(()); } } - self.loop_turn(event)? + self.handle_event(event)? } + Err("client exited without proper shutdown sequence")? } - fn loop_turn(&mut self, event: Event) -> Result<()> { + fn handle_event(&mut self, event: Event) -> Result<()> { let loop_start = Instant::now(); // NOTE: don't count blocking select! call as a loop-turn time - let _p = profile("main_loop_inner/loop-turn"); + let _p = profile("GlobalState::handle_event"); - log::info!("loop turn = {:?}", event); + log::info!("handle_event({:?})", event); let queue_count = self.task_pool.0.len(); if queue_count > 0 { log::info!("queued count = {}", queue_count); -- cgit v1.2.3 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 From e70f7dc10c622e0ffec4b264235ad203b4047171 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 26 Jun 2020 00:27:39 +0200 Subject: Minor --- crates/rust-analyzer/src/dispatch.rs | 2 +- crates/rust-analyzer/src/global_state.rs | 38 ++++++++++++++++++-------------- crates/rust-analyzer/src/main_loop.rs | 20 ++++++++--------- crates/rust-analyzer/src/reload.rs | 10 ++++----- 4 files changed, 38 insertions(+), 32 deletions(-) (limited to 'crates') diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs index 03b373dee..891fdb96d 100644 --- a/crates/rust-analyzer/src/dispatch.rs +++ b/crates/rust-analyzer/src/dispatch.rs @@ -59,7 +59,7 @@ impl<'a> RequestDispatcher<'a> { } }; - self.global_state.task_pool.0.spawn({ + self.global_state.task_pool.handle.spawn({ let world = self.global_state.snapshot(); move || { let result = f(world, params); diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 8486da627..17de2a075 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -13,7 +13,7 @@ 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::loader::Handle; +use vfs::loader::Handle as _; use crate::{ config::Config, @@ -42,19 +42,26 @@ impl Default for Status { } } +// Enforces drop order +pub(crate) struct Handle { + pub(crate) handle: H, + pub(crate) receiver: C, +} + /// `GlobalState` is the primary mutable state of the language server /// /// The most interesting components are `vfs`, which stores a consistent /// snapshot of the file systems, and `analysis_host`, which stores our /// incremental salsa database. +/// +/// Note that this struct has more than on impl in various modules! pub(crate) struct GlobalState { sender: Sender, + pub(crate) task_pool: Handle, Receiver>, + pub(crate) loader: Handle, Receiver>, + pub(crate) flycheck: Option>>, pub(crate) config: Config, - pub(crate) task_pool: (TaskPool, Receiver), pub(crate) analysis_host: AnalysisHost, - pub(crate) loader: Box, - pub(crate) task_receiver: Receiver, - pub(crate) flycheck: Option<(FlycheckHandle, Receiver)>, pub(crate) diagnostics: DiagnosticCollection, pub(crate) mem_docs: FxHashSet, pub(crate) vfs: Arc)>>, @@ -82,37 +89,36 @@ impl GlobalState { lru_capacity: Option, config: Config, ) -> GlobalState { - let (task_sender, task_receiver) = unbounded::(); - let loader = { - let loader = vfs_notify::NotifyHandle::spawn(Box::new(move |msg| { - task_sender.send(msg).unwrap() - })); - Box::new(loader) + let (sender, receiver) = unbounded::(); + let handle = + vfs_notify::NotifyHandle::spawn(Box::new(move |msg| sender.send(msg).unwrap())); + let handle = Box::new(handle) as Box; + Handle { handle, receiver } }; let task_pool = { let (sender, receiver) = unbounded(); - (TaskPool::new(sender), receiver) + let handle = TaskPool::new(sender); + Handle { handle, receiver } }; GlobalState { sender, - config, task_pool, - analysis_host: AnalysisHost::new(lru_capacity), loader, - task_receiver, + config, + analysis_host: AnalysisHost::new(lru_capacity), flycheck: None, diagnostics: Default::default(), mem_docs: FxHashSet::default(), vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))), status: Status::default(), req_queue: ReqQueue::default(), - latest_requests: Default::default(), source_root_config: SourceRootConfig::default(), proc_macro_client: ProcMacroClient::dummy(), workspaces: Arc::new(Vec::new()), + latest_requests: Default::default(), } } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 6ac50745a..d4879283d 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -100,13 +100,13 @@ impl GlobalState { recv(inbox) -> msg => msg.ok().map(Event::Lsp), - recv(self.task_pool.1) -> task => + recv(self.task_pool.receiver) -> task => Some(Event::Task(task.unwrap())), - recv(self.task_receiver) -> task => + recv(self.loader.receiver) -> task => Some(Event::Vfs(task.unwrap())), - recv(self.flycheck.as_ref().map_or(&never(), |it| &it.1)) -> task => + recv(self.flycheck.as_ref().map_or(&never(), |it| &it.receiver)) -> task => Some(Event::Flycheck(task.unwrap())), } } @@ -132,7 +132,7 @@ impl GlobalState { let _p = profile("GlobalState::handle_event"); log::info!("handle_event({:?})", event); - let queue_count = self.task_pool.0.len(); + let queue_count = self.task_pool.handle.len(); if queue_count > 0 { log::info!("queued count = {}", queue_count); } @@ -233,7 +233,7 @@ impl GlobalState { let state_changed = self.process_changes(); if became_ready { if let Some(flycheck) = &self.flycheck { - flycheck.0.update(); + flycheck.handle.update(); } } @@ -370,7 +370,7 @@ impl GlobalState { log::error!("orphan DidCloseTextDocument: {}", path) } if let Some(path) = path.as_path() { - this.loader.invalidate(path.to_path_buf()); + this.loader.handle.invalidate(path.to_path_buf()); } } let params = lsp_types::PublishDiagnosticsParams { @@ -384,7 +384,7 @@ impl GlobalState { })? .on::(|this, _params| { if let Some(flycheck) = &this.flycheck { - flycheck.0.update(); + flycheck.handle.update(); } Ok(()) })? @@ -427,7 +427,7 @@ impl GlobalState { .on::(|this, params| { for change in params.changes { if let Ok(path) = from_proto::abs_path(&change.uri) { - this.loader.invalidate(path); + this.loader.handle.invalidate(path); } } Ok(()) @@ -440,7 +440,7 @@ impl GlobalState { if self.config.publish_diagnostics { let snapshot = self.snapshot(); let subscriptions = subscriptions.clone(); - self.task_pool.0.spawn(move || { + self.task_pool.handle.spawn(move || { let diagnostics = subscriptions .into_iter() .filter_map(|file_id| { @@ -458,7 +458,7 @@ impl GlobalState { Task::Diagnostics(diagnostics) }) } - self.task_pool.0.spawn({ + self.task_pool.handle.spawn({ let subs = subscriptions; let snap = self.snapshot(); move || { diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 1981a97da..a22d3e262 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -11,7 +11,7 @@ use vfs::{file_set::FileSetConfig, AbsPath}; use crate::{ config::{Config, FilesWatcher, LinkedProject}, - global_state::GlobalState, + global_state::{GlobalState, Handle}, }; impl GlobalState { @@ -105,7 +105,7 @@ impl GlobalState { FilesWatcher::Client => vec![], FilesWatcher::Notify => project_folders.watch, }; - self.loader.set_config(vfs::loader::Config { load: project_folders.load, watch }); + self.loader.handle.set_config(vfs::loader::Config { load: project_folders.load, watch }); // Create crate graph from all the workspaces let crate_graph = { @@ -113,7 +113,7 @@ impl GlobalState { let vfs = &mut self.vfs.write().0; let loader = &mut self.loader; let mut load = |path: &AbsPath| { - let contents = loader.load_sync(path); + let contents = loader.handle.load_sync(path); let path = vfs::VfsPath::from(path.to_path_buf()); vfs.set_file_contents(path.clone(), contents); vfs.file_id(&path) @@ -153,9 +153,9 @@ impl GlobalState { 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 = + let handle = FlycheckHandle::spawn(sender, config.clone(), cargo_project_root.into()); - Some((flycheck, receiver)) + Some(Handle { handle, receiver }) } ProjectWorkspace::Json { .. } => { log::warn!("Cargo check watching only supported for cargo workspaces, disabling"); -- cgit v1.2.3