mir_analyzer/indexing.rs
1//! Chunked, cancellable workspace-indexing primitives.
2//!
3//! These types support the rust-analyzer-style **eager background indexing**
4//! model: at session start the consumer enumerates every project + vendor file
5//! (see [`crate::composer::Psr4Map::all_vendor_files`]) and pumps them through
6//! [`crate::AnalysisSession::index_batch`] in bounded chunks. Each chunk takes
7//! one short write window and merges its declarations into the workspace symbol
8//! index incrementally, so the analyzer stays responsive (no multi-second
9//! freeze) while the index fills, and the input set becomes static afterward —
10//! no per-edit churn of the warm cache.
11//!
12//! The library owns **no** background thread. The consumer drives the pump:
13//! an LSP server runs it on a worker thread; a single-threaded wasm host pumps
14//! one chunk per `requestIdleCallback`/`setTimeout(0)` tick. Both pass the same
15//! API; only [`IndexParallelism`] differs.
16
17use std::sync::atomic::{AtomicBool, Ordering};
18use std::sync::Arc;
19
20/// Cooperative cancellation flag shared between a consumer's driver and the
21/// indexing/analysis calls it makes.
22///
23/// Salsa's own cancellation is query-granular and does not unwind the
24/// plain-Rust body-analysis walk, so long-running loops here check this flag at
25/// chunk / file boundaries instead. On each new edit the consumer should drop
26/// the old flag and create a fresh one for the new work rather than reusing a
27/// single flag.
28#[derive(Clone, Default)]
29pub struct IndexCancel(Arc<AtomicBool>);
30
31impl IndexCancel {
32 /// A fresh, un-cancelled token.
33 pub fn new() -> Self {
34 Self::default()
35 }
36
37 /// Request cancellation. In-flight chunks finish their current bounded unit
38 /// and stop at the next boundary.
39 pub fn cancel(&self) {
40 self.0.store(true, Ordering::Relaxed);
41 }
42
43 /// Whether cancellation has been requested.
44 pub fn is_cancelled(&self) -> bool {
45 self.0.load(Ordering::Relaxed)
46 }
47}
48
49/// How an [`crate::AnalysisSession::index_batch`] call parses the files in a
50/// chunk. `Sequential` is required on wasm (no threads / no rayon); `Rayon`
51/// parallelises the parse across the global thread pool on native consumers.
52#[derive(Clone, Copy, PartialEq, Eq, Debug)]
53pub enum IndexParallelism {
54 Sequential,
55 Rayon,
56}
57
58/// Result of one [`crate::AnalysisSession::index_batch`] call.
59#[derive(Clone, Copy, Debug, Default)]
60pub struct IndexBatchOutcome {
61 /// Files newly registered as salsa inputs by this batch (already-registered
62 /// paths are updated in place and not counted).
63 pub registered: usize,
64 /// `true` if the cancel flag was observed; the batch may be partial.
65 pub cancelled: bool,
66 /// The workspace generation epoch after this batch (see
67 /// [`crate::AnalysisSession::index_generation`]). The consumer records this
68 /// alongside published diagnostics; when it later advances, affected open
69 /// files become candidates for re-analysis.
70 pub generation: u64,
71}