]> git.lizzy.rs Git - rust.git/blobdiff - crates/rust-analyzer/src/global_state.rs
Replaced fold with for loop
[rust.git] / crates / rust-analyzer / src / global_state.rs
index 6f2f482c150636de271fc84efe0aa92e36c7165e..bef943464a87a0865ffd665e7003f22b29aafd31 100644 (file)
@@ -7,27 +7,25 @@
 
 use crossbeam_channel::{unbounded, Receiver, Sender};
 use flycheck::FlycheckHandle;
-use ide::{Analysis, AnalysisHost, Cancelable, Change, FileId};
-use ide_db::base_db::{CrateId, VfsPath};
+use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId};
+use ide_db::base_db::{CrateId, FileLoader, SourceDatabase};
 use lsp_types::{SemanticTokens, Url};
 use parking_lot::{Mutex, RwLock};
-use project_model::{
-    BuildDataCollector, BuildDataResult, CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target,
-};
+use proc_macro_api::ProcMacroServer;
+use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
 use rustc_hash::FxHashMap;
 use vfs::AnchoredPathBuf;
 
 use crate::{
     config::Config,
     diagnostics::{CheckFixes, DiagnosticCollection},
-    document::DocumentData,
     from_proto,
     line_index::{LineEndings, LineIndex},
     lsp_ext,
     main_loop::Task,
+    mem_docs::MemDocs,
     op_queue::OpQueue,
-    reload::SourceRootConfig,
-    request_metrics::{LatestRequests, RequestMetrics},
+    reload::{self, SourceRootConfig},
     thread_pool::TaskPool,
     to_proto::url_from_abs_path,
     Result,
@@ -57,12 +55,13 @@ pub(crate) struct GlobalState {
     pub(crate) config: Arc<Config>,
     pub(crate) analysis_host: AnalysisHost,
     pub(crate) diagnostics: DiagnosticCollection,
-    pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>,
+    pub(crate) mem_docs: MemDocs,
     pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
     pub(crate) shutdown_requested: bool,
+    pub(crate) proc_macro_changed: bool,
     pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>,
     pub(crate) source_root_config: SourceRootConfig,
-    pub(crate) proc_macro_client: Option<ProcMacroClient>,
+    pub(crate) proc_macro_client: Option<ProcMacroServer>,
 
     pub(crate) flycheck: Vec<FlycheckHandle>,
     pub(crate) flycheck_sender: Sender<flycheck::Message>,
@@ -74,19 +73,37 @@ pub(crate) struct GlobalState {
     pub(crate) vfs_progress_n_total: usize,
     pub(crate) vfs_progress_n_done: usize,
 
-    /// For both `workspaces` and `workspace_build_data`, the field stores the
-    /// data we actually use, while the `OpQueue` stores the result of the last
-    /// fetch.
+    /// `workspaces` field stores the data we actually use, while the `OpQueue`
+    /// stores the result of the last fetch.
+    ///
+    /// If the fetch (partially) fails, we do not update the current value.
+    ///
+    /// The handling of build data is subtle. We fetch workspace in two phases:
+    ///
+    /// *First*, we run `cargo metadata`, which gives us fast results for
+    /// initial analysis.
+    ///
+    /// *Second*, we run `cargo check` which runs build scripts and compiles
+    /// proc macros.
     ///
-    /// If the fetch (partially) fails, we do not update the values.
+    /// We need both for the precise analysis, but we want rust-analyzer to be
+    /// at least partially available just after the first phase. That's because
+    /// first phase is much faster, and is much less likely to fail.
+    ///
+    /// This creates a complication -- by the time the second phase completes,
+    /// the results of the fist phase could be invalid. That is, while we run
+    /// `cargo check`, the user edits `Cargo.toml`, we notice this, and the new
+    /// `cargo metadata` completes before `cargo check`.
+    ///
+    /// An additional complication is that we want to avoid needless work. When
+    /// the user just adds comments or whitespace to Cargo.toml, we do not want
+    /// to invalidate any salsa caches.
     pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
-    pub(crate) fetch_workspaces_queue: OpQueue<(), Vec<anyhow::Result<ProjectWorkspace>>>,
-    pub(crate) workspace_build_data: Option<BuildDataResult>,
+    pub(crate) fetch_workspaces_queue: OpQueue<Vec<anyhow::Result<ProjectWorkspace>>>,
     pub(crate) fetch_build_data_queue:
-        OpQueue<BuildDataCollector, Option<anyhow::Result<BuildDataResult>>>,
-    pub(crate) prime_caches_queue: OpQueue<(), ()>,
+        OpQueue<(Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>,
 
-    latest_requests: Arc<RwLock<LatestRequests>>,
+    pub(crate) prime_caches_queue: OpQueue<()>,
 }
 
 /// An immutable snapshot of the world's state at a point in time.
@@ -94,13 +111,14 @@ pub(crate) struct GlobalStateSnapshot {
     pub(crate) config: Arc<Config>,
     pub(crate) analysis: Analysis,
     pub(crate) check_fixes: CheckFixes,
-    pub(crate) latest_requests: Arc<RwLock<LatestRequests>>,
-    mem_docs: FxHashMap<VfsPath, DocumentData>,
+    mem_docs: MemDocs,
     pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
     vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
     pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
 }
 
+impl std::panic::UnwindSafe for GlobalStateSnapshot {}
+
 impl GlobalState {
     pub(crate) fn new(sender: Sender<lsp_server::Message>, config: Config) -> GlobalState {
         let loader = {
@@ -119,17 +137,18 @@ pub(crate) fn new(sender: Sender<lsp_server::Message>, config: Config) -> Global
 
         let analysis_host = AnalysisHost::new(config.lru_capacity());
         let (flycheck_sender, flycheck_receiver) = unbounded();
-        GlobalState {
+        let mut this = GlobalState {
             sender,
             req_queue: ReqQueue::default(),
             task_pool,
             loader,
-            config: Arc::new(config),
+            config: Arc::new(config.clone()),
             analysis_host,
             diagnostics: Default::default(),
-            mem_docs: FxHashMap::default(),
+            mem_docs: MemDocs::default(),
             semantic_tokens_cache: Arc::new(Default::default()),
             shutdown_requested: false,
+            proc_macro_changed: false,
             last_reported_status: None,
             source_root_config: SourceRootConfig::default(),
             proc_macro_client: None,
@@ -146,18 +165,20 @@ pub(crate) fn new(sender: Sender<lsp_server::Message>, config: Config) -> Global
 
             workspaces: Arc::new(Vec::new()),
             fetch_workspaces_queue: OpQueue::default(),
-            workspace_build_data: None,
             prime_caches_queue: OpQueue::default(),
 
             fetch_build_data_queue: OpQueue::default(),
-            latest_requests: Default::default(),
-        }
+        };
+        // Apply any required database inputs from the config.
+        this.update_configuration(config);
+        this
     }
 
     pub(crate) fn process_changes(&mut self) -> bool {
         let _p = profile::span("GlobalState::process_changes");
         let mut fs_changes = Vec::new();
-        let mut has_fs_changes = false;
+        // A file was added or deleted
+        let mut has_structure_changes = false;
 
         let change = {
             let mut change = Change::new();
@@ -168,10 +189,23 @@ pub(crate) fn process_changes(&mut self) -> bool {
             }
 
             for file in changed_files {
-                if file.is_created_or_deleted() {
-                    if let Some(path) = vfs.file_path(file.file_id).as_path() {
-                        fs_changes.push((path.to_path_buf(), file.change_kind));
-                        has_fs_changes = true;
+                if !file.is_created_or_deleted() {
+                    let crates = self.analysis_host.raw_database().relevant_crates(file.file_id);
+                    let crate_graph = self.analysis_host.raw_database().crate_graph();
+
+                    if crates.iter().any(|&krate| !crate_graph[krate].proc_macro.is_empty()) {
+                        self.proc_macro_changed = true;
+                    }
+                }
+
+                if let Some(path) = vfs.file_path(file.file_id).as_path() {
+                    let path = path.to_path_buf();
+                    if reload::should_refresh_for_change(&path, file.change_kind) {
+                        self.fetch_workspaces_queue.request_op();
+                    }
+                    fs_changes.push((path, file.change_kind));
+                    if file.is_created_or_deleted() {
+                        has_structure_changes = true;
                     }
                 }
 
@@ -190,15 +224,14 @@ pub(crate) fn process_changes(&mut self) -> bool {
                 };
                 change.change_file(file.file_id, text);
             }
-            if has_fs_changes {
-                let roots = self.source_root_config.partition(&vfs);
+            if has_structure_changes {
+                let roots = self.source_root_config.partition(vfs);
                 change.set_roots(roots);
             }
             change
         };
 
         self.analysis_host.apply_change(change);
-        self.maybe_refresh(&fs_changes);
         true
     }
 
@@ -208,7 +241,6 @@ pub(crate) fn snapshot(&self) -> GlobalStateSnapshot {
             workspaces: Arc::clone(&self.workspaces),
             analysis: self.analysis_host.analysis(),
             vfs: Arc::clone(&self.vfs),
-            latest_requests: Arc::clone(&self.latest_requests),
             check_fixes: Arc::clone(&self.diagnostics.check_fixes),
             mem_docs: self.mem_docs.clone(),
             semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache),
@@ -247,10 +279,14 @@ pub(crate) fn register_request(
     }
     pub(crate) fn respond(&mut self, response: lsp_server::Response) {
         if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) {
+            if let Some(err) = &response.error {
+                if err.message.starts_with("server panicked") {
+                    self.poke_rust_analyzer_developer(format!("{}, check the log", err.message))
+                }
+            }
+
             let duration = start.elapsed();
-            log::info!("handled req#{} in {:?}", response.id, duration);
-            let metrics = RequestMetrics { id: response.id.clone(), method, duration };
-            self.latest_requests.write().record(metrics);
+            tracing::info!("handled {} - ({}) in {:0.2?}", method, response.id, duration);
             self.send(response.into());
         }
     }
@@ -280,7 +316,7 @@ pub(crate) fn file_id_to_url(&self, id: FileId) -> Url {
         file_id_to_url(&self.vfs.read().0, id)
     }
 
-    pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancelable<LineIndex> {
+    pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable<LineIndex> {
         let endings = self.vfs.read().1[&file_id];
         let index = self.analysis.file_line_index(file_id)?;
         let res = LineIndex { index, endings, encoding: self.config.offset_encoding() };
@@ -288,7 +324,7 @@ pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancelable<LineIndex> {
     }
 
     pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> {
-        let path = from_proto::vfs_path(&url).ok()?;
+        let path = from_proto::vfs_path(url).ok()?;
         Some(self.mem_docs.get(&path)?.version)
     }
 
@@ -297,7 +333,7 @@ pub(crate) fn anchored_path(&self, path: &AnchoredPathBuf) -> Url {
         base.pop();
         let path = base.join(&path.path).unwrap();
         let path = path.as_path().unwrap();
-        url_from_abs_path(&path)
+        url_from_abs_path(path)
     }
 
     pub(crate) fn cargo_target_for_crate_root(
@@ -309,9 +345,10 @@ pub(crate) fn cargo_target_for_crate_root(
         let path = path.as_path()?;
         self.workspaces.iter().find_map(|ws| match ws {
             ProjectWorkspace::Cargo { cargo, .. } => {
-                cargo.target_by_root(&path).map(|it| (cargo, it))
+                cargo.target_by_root(path).map(|it| (cargo, it))
             }
             ProjectWorkspace::Json { .. } => None,
+            ProjectWorkspace::DetachedFiles { .. } => None,
         })
     }
 }
@@ -319,7 +356,7 @@ pub(crate) fn cargo_target_for_crate_root(
 pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url {
     let path = vfs.file_path(id);
     let path = path.as_path().unwrap();
-    url_from_abs_path(&path)
+    url_from_abs_path(path)
 }
 
 pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> Result<FileId> {