From 6c7578bd7a67b0f8fd1fdb6a043c8523104c5807 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov <aleksey.kladov@gmail.com>
Date: Thu, 2 Jul 2020 16:47:42 +0200
Subject: [PATCH] Move cargo metadata off the main loop

---
 crates/ra_project_model/src/lib.rs            |  4 +-
 crates/rust-analyzer/src/main_loop.rs         | 15 +++-
 crates/rust-analyzer/src/reload.rs            | 79 ++++++++++---------
 .../rust-analyzer/tests/heavy_tests/main.rs   |  1 +
 4 files changed, 56 insertions(+), 43 deletions(-)

diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index 8dbf4e6ead..464c3b2e3e 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -150,7 +150,7 @@ impl ProjectManifest {
 impl ProjectWorkspace {
     pub fn load(
         manifest: ProjectManifest,
-        cargo_features: &CargoConfig,
+        cargo_config: &CargoConfig,
         with_sysroot: bool,
     ) -> Result<ProjectWorkspace> {
         let res = match manifest {
@@ -166,7 +166,7 @@ impl ProjectWorkspace {
                 ProjectWorkspace::Json { project }
             }
             ProjectManifest::CargoToml(cargo_toml) => {
-                let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features)
+                let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_config)
                     .with_context(|| {
                         format!(
                             "Failed to read Cargo metadata from Cargo.toml file {}",
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index d4d18a8082..cfde55431e 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -21,6 +21,7 @@ use crate::{
     lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress},
     Result,
 };
+use ra_project_model::ProjectWorkspace;
 
 pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
     log::info!("initial config: {:#?}", config);
@@ -58,6 +59,7 @@ enum Event {
 pub(crate) enum Task {
     Response(Response),
     Diagnostics(Vec<(FileId, Vec<lsp_types::Diagnostic>)>),
+    Workspaces(Vec<anyhow::Result<ProjectWorkspace>>),
     Unit,
 }
 
@@ -111,6 +113,14 @@ impl GlobalState {
     }
 
     fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> {
+        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(),
+            );
+        };
+
         let registration_options = lsp_types::TextDocumentRegistrationOptions {
             document_selector: Some(vec![
                 lsp_types::DocumentFilter {
@@ -140,7 +150,7 @@ impl GlobalState {
             |_, _| (),
         );
 
-        self.reload();
+        self.fetch_workspaces();
 
         while let Some(event) = self.next_event(&inbox) {
             if let Event::Lsp(lsp_server::Message::Notification(not)) = &event {
@@ -182,6 +192,7 @@ impl GlobalState {
                             self.diagnostics.set_native_diagnostics(file_id, diagnostics)
                         }
                     }
+                    Task::Workspaces(workspaces) => self.switch_workspaces(workspaces),
                     Task::Unit => (),
                 }
                 self.analysis_host.maybe_collect_garbage();
@@ -320,7 +331,7 @@ impl GlobalState {
         self.register_request(&req, request_received);
 
         RequestDispatcher { req: Some(req), global_state: self }
-            .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.reload()))?
+            .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.fetch_workspaces()))?
             .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))?
             .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))?
             .on_sync::<lsp_types::request::Shutdown>(|_, ()| Ok(()))?
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 9fc56349cc..74c3344dfe 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -11,6 +11,7 @@ use vfs::{file_set::FileSetConfig, AbsPath};
 use crate::{
     config::{Config, FilesWatcher, LinkedProject},
     global_state::{GlobalState, Handle},
+    main_loop::Task,
 };
 
 impl GlobalState {
@@ -20,51 +21,51 @@ impl GlobalState {
             self.analysis_host.update_lru_capacity(old_config.lru_capacity);
         }
         if self.config.linked_projects != old_config.linked_projects {
-            self.reload()
+            self.fetch_workspaces()
         } else if self.config.flycheck != old_config.flycheck {
             self.reload_flycheck();
         }
     }
-    pub(crate) fn reload(&mut self) {
-        log::info!("reloading projects: {:?}", self.config.linked_projects);
-        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(),
-            );
-        };
-
-        let workspaces = {
-            self.config
-                .linked_projects
-                .iter()
-                .map(|project| match project {
-                    LinkedProject::ProjectManifest(manifest) => {
-                        ra_project_model::ProjectWorkspace::load(
-                            manifest.clone(),
-                            &self.config.cargo,
-                            self.config.with_sysroot,
-                        )
-                    }
-                    LinkedProject::InlineJsonProject(it) => {
-                        Ok(ra_project_model::ProjectWorkspace::Json { project: it.clone() })
-                    }
-                })
-                .collect::<Vec<_>>()
-                .into_iter()
-                .filter_map(|res| {
-                    res.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),
-                        );
+    pub(crate) fn fetch_workspaces(&mut self) {
+        self.task_pool.handle.spawn({
+            let linked_projects = self.config.linked_projects.clone();
+            let cargo_config = self.config.cargo.clone();
+            let with_sysroot = self.config.with_sysroot.clone();
+            move || {
+                let workspaces = linked_projects
+                    .iter()
+                    .map(|project| match project {
+                        LinkedProject::ProjectManifest(manifest) => {
+                            ra_project_model::ProjectWorkspace::load(
+                                manifest.clone(),
+                                &cargo_config,
+                                with_sysroot,
+                            )
+                        }
+                        LinkedProject::InlineJsonProject(it) => {
+                            Ok(ra_project_model::ProjectWorkspace::Json { project: it.clone() })
+                        }
                     })
-                    .ok()
+                    .collect::<Vec<_>>();
+                Task::Workspaces(workspaces)
+            }
+        });
+    }
+    pub(crate) fn switch_workspaces(&mut self, workspaces: Vec<anyhow::Result<ProjectWorkspace>>) {
+        log::info!("reloading projects: {:?}", self.config.linked_projects);
+        let workspaces = workspaces
+            .into_iter()
+            .filter_map(|res| {
+                res.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),
+                    );
                 })
-                .collect::<Vec<_>>()
-        };
+                .ok()
+            })
+            .collect::<Vec<_>>();
 
         if let FilesWatcher::Client = self.config.files.watcher {
             let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index cc079790ee..7b908d30c2 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -447,6 +447,7 @@ version = \"0.0.0\"
 ",
     )
     .server();
+    server.wait_until_workspace_is_loaded();
 
     server.request::<OnEnter>(
         TextDocumentPositionParams {