]> git.lizzy.rs Git - rust.git/blobdiff - crates/rust-analyzer/src/main_loop.rs
clippy::redudant_borrow
[rust.git] / crates / rust-analyzer / src / main_loop.rs
index 984790d354cdc5b02c9155b875f9280b202e1ecb..fa5fc6fbfdb54c89593f5243778ac16a80adc9e0 100644 (file)
@@ -2,16 +2,17 @@
 //! requests/replies and notifications back to the client.
 use std::{
     env, fmt,
+    sync::Arc,
     time::{Duration, Instant},
 };
 
 use always_assert::always;
 use crossbeam_channel::{select, Receiver};
-use ide::PrimeCachesProgress;
-use ide::{Canceled, FileId};
+use ide::{FileId, PrimeCachesProgress};
 use ide_db::base_db::VfsPath;
 use lsp_server::{Connection, Notification, Request, Response};
 use lsp_types::notification::Notification as _;
+use project_model::BuildDataCollector;
 use vfs::ChangeKind;
 
 use crate::{
@@ -19,9 +20,9 @@
     dispatch::{NotificationDispatcher, RequestDispatcher},
     document::DocumentData,
     from_proto,
-    global_state::{file_id_to_url, url_to_file_id, GlobalState, Status},
+    global_state::{file_id_to_url, url_to_file_id, GlobalState},
     handlers, lsp_ext,
-    lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress},
+    lsp_utils::{apply_document_changes, is_cancelled, notification_is, Progress},
     reload::{BuildDataProgress, ProjectWorkspaceProgress},
     Result,
 };
@@ -102,6 +103,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 impl GlobalState {
     fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> {
         if self.config.linked_projects().is_empty()
+            && self.config.detached_files().is_empty()
             && self.config.notifications().cargo_toml_not_found
         {
             self.show_message(
@@ -187,7 +189,7 @@ fn handle_event(&mut self, event: Event) -> Result<()> {
             log::info!("task queue len: {}", task_queue_len);
         }
 
-        let mut new_status = self.status;
+        let was_quiescent = self.is_quiescent();
         match event {
             Event::Lsp(msg) => match msg {
                 lsp_server::Message::Request(req) => self.on_request(loop_start, req)?,
@@ -227,12 +229,26 @@ fn handle_event(&mut self, event: Event) -> Result<()> {
                                     (Progress::Report, Some(msg))
                                 }
                                 ProjectWorkspaceProgress::End(workspaces) => {
-                                    self.fetch_workspaces_completed();
-                                    self.switch_workspaces(workspaces, None);
+                                    self.fetch_workspaces_completed(workspaces);
+
+                                    let old = Arc::clone(&self.workspaces);
+                                    self.switch_workspaces();
+                                    let workspaces_updated = !Arc::ptr_eq(&old, &self.workspaces);
+
+                                    if self.config.run_build_scripts() && workspaces_updated {
+                                        let mut collector =
+                                            BuildDataCollector::new(self.config.wrap_rustc());
+                                        for ws in self.workspaces.iter() {
+                                            ws.collect_build_data_configs(&mut collector);
+                                        }
+                                        self.fetch_build_data_request(collector)
+                                    }
+
                                     (Progress::End, None)
                                 }
                             };
-                            self.report_progress("fetching", state, msg, None);
+
+                            self.report_progress("Fetching", state, msg, None);
                         }
                         Task::FetchBuildData(progress) => {
                             let (state, msg) = match progress {
@@ -240,22 +256,21 @@ fn handle_event(&mut self, event: Event) -> Result<()> {
                                 BuildDataProgress::Report(msg) => {
                                     (Some(Progress::Report), Some(msg))
                                 }
-                                BuildDataProgress::End(collector) => {
-                                    self.fetch_build_data_completed();
-                                    let workspaces = (*self.workspaces)
-                                        .clone()
-                                        .into_iter()
-                                        .map(|it| Ok(it))
-                                        .collect();
-                                    self.switch_workspaces(workspaces, Some(collector));
+                                BuildDataProgress::End(build_data_result) => {
+                                    self.fetch_build_data_completed(build_data_result);
+
+                                    self.switch_workspaces();
+
                                     (Some(Progress::End), None)
                                 }
                             };
+
                             if let Some(state) = state {
-                                self.report_progress("loading", state, msg, None);
+                                self.report_progress("Loading", state, msg, None);
                             }
                         }
                     }
+
                     // Coalesce multiple task events into one loop turn
                     task = match self.task_pool.receiver.try_recv() {
                         Ok(task) => task,
@@ -280,10 +295,12 @@ fn handle_event(&mut self, event: Event) -> Result<()> {
                             state = Progress::End;
                             message = None;
                             fraction = 1.0;
+
+                            self.prime_caches_queue.op_completed(());
                         }
                     };
 
-                    self.report_progress("indexing", state, message, Some(fraction));
+                    self.report_progress("Indexing", state, message, Some(fraction));
                 }
             }
             Event::Vfs(mut task) => {
@@ -301,30 +318,25 @@ fn handle_event(&mut self, event: Event) -> Result<()> {
                         }
                         vfs::loader::Message::Progress { n_total, n_done, config_version } => {
                             always!(config_version <= self.vfs_config_version);
-                            if n_total == 0 {
-                                new_status = Status::Invalid;
+
+                            self.vfs_progress_config_version = config_version;
+                            self.vfs_progress_n_total = n_total;
+                            self.vfs_progress_n_done = n_done;
+
+                            let state = if n_done == 0 {
+                                Progress::Begin
+                            } else if n_done < n_total {
+                                Progress::Report
                             } else {
-                                let state = if n_done == 0 {
-                                    new_status = Status::Loading;
-                                    Progress::Begin
-                                } else if n_done < n_total {
-                                    Progress::Report
-                                } else {
-                                    assert_eq!(n_done, n_total);
-                                    new_status = Status::Ready {
-                                        partial: self.config.run_build_scripts()
-                                            && self.workspace_build_data.is_none()
-                                            || config_version < self.vfs_config_version,
-                                    };
-                                    Progress::End
-                                };
-                                self.report_progress(
-                                    "roots scanned",
-                                    state,
-                                    Some(format!("{}/{}", n_done, n_total)),
-                                    Some(Progress::fraction(n_done, n_total)),
-                                )
-                            }
+                                assert_eq!(n_done, n_total);
+                                Progress::End
+                            };
+                            self.report_progress(
+                                "Roots Scanned",
+                                state,
+                                Some(format!("{}/{}", n_done, n_total)),
+                                Some(Progress::fraction(n_done, n_total)),
+                            )
                         }
                     }
                     // Coalesce many VFS event into a single loop turn
@@ -400,18 +412,14 @@ fn handle_event(&mut self, event: Event) -> Result<()> {
         }
 
         let state_changed = self.process_changes();
-        let prev_status = self.status;
-        if prev_status != new_status {
-            self.transition(new_status);
-        }
-        let is_ready = matches!(self.status, Status::Ready { .. });
-        if prev_status == Status::Loading && is_ready {
+
+        if self.is_quiescent() && !was_quiescent {
             for flycheck in &self.flycheck {
                 flycheck.update();
             }
         }
 
-        if is_ready && (state_changed || prev_status == Status::Loading) {
+        if self.is_quiescent() && (!was_quiescent || state_changed) {
             self.update_file_notifications_on_threadpool();
 
             // Refresh semantic tokens if the client supports it.
@@ -440,9 +448,13 @@ fn handle_event(&mut self, event: Event) -> Result<()> {
             }
         }
 
-        self.fetch_workspaces_if_needed();
+        if self.config.cargo_autoreload() {
+            self.fetch_workspaces_if_needed();
+        }
         self.fetch_build_data_if_needed();
 
+        self.report_new_status_if_needed();
+
         let loop_duration = loop_start.elapsed();
         if loop_duration > Duration::from_millis(100) {
             log::warn!("overly long loop turn: {:?}", loop_duration);
@@ -469,18 +481,23 @@ fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()>
             return Ok(());
         }
 
-        if self.status == Status::Loading && req.method != "shutdown" {
+        // Avoid flashing a bunch of unresolved references during initial load.
+        if self.workspaces.is_empty() && !self.is_quiescent() {
             self.respond(lsp_server::Response::new_err(
                 req.id,
                 // FIXME: i32 should impl From<ErrorCode> (from() guarantees lossless conversion)
                 lsp_server::ErrorCode::ContentModified as i32,
-                "Rust Analyzer is still loading...".to_owned(),
+                "waiting for cargo metadata or cargo check".to_owned(),
             ));
             return Ok(());
         }
 
         RequestDispatcher { req: Some(req), global_state: self }
-            .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.fetch_workspaces_request()))?
+            .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| {
+                s.fetch_workspaces_request();
+                s.fetch_workspaces_if_needed();
+                Ok(())
+            })?
             .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>(|s, ()| {
@@ -497,6 +514,8 @@ fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()>
             .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)
             .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)
             .on::<lsp_ext::ViewHir>(handlers::handle_view_hir)
+            .on::<lsp_ext::ViewCrateGraph>(handlers::handle_view_crate_graph)
+            .on::<lsp_ext::ViewItemTree>(handlers::handle_view_item_tree)
             .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro)
             .on::<lsp_ext::ParentModule>(handlers::handle_parent_module)
             .on::<lsp_ext::Runnables>(handlers::handle_runnables)
@@ -507,9 +526,10 @@ fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()>
             .on::<lsp_ext::HoverRequest>(handlers::handle_hover)
             .on::<lsp_ext::ExternalDocs>(handlers::handle_open_docs)
             .on::<lsp_ext::OpenCargoToml>(handlers::handle_open_cargo_toml)
+            .on::<lsp_ext::MoveItem>(handlers::handle_move_item)
+            .on::<lsp_ext::WorkspaceSymbol>(handlers::handle_workspace_symbol)
             .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)
             .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)
-            .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)
             .on::<lsp_types::request::GotoDefinition>(handlers::handle_goto_definition)
             .on::<lsp_types::request::GotoImplementation>(handlers::handle_goto_implementation)
             .on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)
@@ -523,6 +543,7 @@ fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()>
             .on::<lsp_types::request::Rename>(handlers::handle_rename)
             .on::<lsp_types::request::References>(handlers::handle_references)
             .on::<lsp_types::request::Formatting>(handlers::handle_formatting)
+            .on::<lsp_types::request::RangeFormatting>(handlers::handle_range_formatting)
             .on::<lsp_types::request::DocumentHighlightRequest>(handlers::handle_document_highlight)
             .on::<lsp_types::request::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)
             .on::<lsp_types::request::CallHierarchyIncomingCalls>(
@@ -555,6 +576,12 @@ fn on_notification(&mut self, not: Notification) -> Result<()> {
                 this.cancel(id);
                 Ok(())
             })?
+            .on::<lsp_types::notification::WorkDoneProgressCancel>(|_this, _params| {
+                // Just ignore this. It is OK to continue sending progress
+                // notifications for this token, as the client can't know when
+                // we accepted notification.
+                Ok(())
+            })?
             .on::<lsp_types::notification::DidOpenTextDocument>(|this, params| {
                 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
                     if this
@@ -689,18 +716,23 @@ fn on_notification(&mut self, not: Notification) -> Result<()> {
     }
     fn update_file_notifications_on_threadpool(&mut self) {
         self.maybe_update_diagnostics();
+
+        // Ensure that only one cache priming task can run at a time
+        self.prime_caches_queue.request_op(());
+        if self.prime_caches_queue.should_start_op().is_none() {
+            return;
+        }
+
         self.task_pool.handle.spawn_with_sender({
             let snap = self.snapshot();
             move |sender| {
-                snap.analysis
-                    .prime_caches(|progress| {
-                        sender.send(Task::PrimeCaches(progress)).unwrap();
-                    })
-                    .unwrap_or_else(|_: Canceled| {
-                        // Pretend that we're done, so that the progress bar is removed. Otherwise
-                        // the editor may complain about it already existing.
-                        sender.send(Task::PrimeCaches(PrimeCachesProgress::Finished)).unwrap()
-                    });
+                let cb = |progress| {
+                    sender.send(Task::PrimeCaches(progress)).unwrap();
+                };
+                match snap.analysis.prime_caches(cb) {
+                    Ok(()) => (),
+                    Err(_canceled) => (),
+                }
             }
         });
     }
@@ -708,7 +740,7 @@ fn maybe_update_diagnostics(&mut self) {
         let subscriptions = self
             .mem_docs
             .keys()
-            .map(|path| self.vfs.read().0.file_id(&path).unwrap())
+            .map(|path| self.vfs.read().0.file_id(path).unwrap())
             .collect::<Vec<_>>();
 
         log::trace!("updating notifications for {:?}", subscriptions);
@@ -720,7 +752,7 @@ fn maybe_update_diagnostics(&mut self) {
                     .filter_map(|file_id| {
                         handlers::publish_diagnostics(&snapshot, file_id)
                             .map_err(|err| {
-                                if !is_canceled(&*err) {
+                                if !is_cancelled(&*err) {
                                     log::error!("failed to compute diagnostics: {:?}", err);
                                 }
                                 ()