Skip to main content

php_lsp/db/
mir_queries.rs

1//! php-lsp salsa queries retargeted onto mir's shared database
2//! (DB_CONVERGENCE_PLAN.md, Phase 2). These mirror the queries in `parse.rs` /
3//! `index.rs` / `symbol_map.rs` but key on mir's `SourceFile` input + run over
4//! `&dyn MirDatabase`, so they live in the single converged db.
5//!
6//! During the transition the originals in `parse.rs` etc. remain for the
7//! consumers not yet migrated (`file_index`, `workspace_index`); they are
8//! deleted once every consumer reads from here.
9
10use std::sync::Arc;
11
12use mir_analyzer::db::{MirDatabase, SourceFile};
13use tower_lsp::lsp_types::Url;
14
15use crate::db::index::IndexArc;
16use crate::db::parse::ParsedArc;
17use crate::db::symbol_map::SymbolMapArc;
18use crate::db::workspace_index::{WorkspaceIndexArc, WorkspaceIndexData, build_maps};
19use crate::document::ast::ParsedDoc;
20use crate::index::file_index::FileIndex;
21use crate::types::symbol_map::SymbolMap;
22
23/// Per-file php-lsp input on the converged db: pairs mir's shared `SourceFile`
24/// (the text source of truth) with the optional warm-start `cached_index`
25/// (a `FileIndex` loaded from the on-disk cache). `file_index` keys on this so
26/// the cached fast-path survives the merge; an edit updates `source`'s text and
27/// clears `cached_index`, both invalidating `file_index`.
28#[salsa::input]
29pub struct LspWsFile {
30    pub source: SourceFile,
31    pub cached_index: Option<Arc<FileIndex>>,
32}
33
34/// php-lsp's workspace scoping input on the converged db: the set of project
35/// files to aggregate into the workspace index (mir's stub/vendor source files
36/// are intentionally excluded). Changes only on file add/remove.
37#[salsa::input]
38pub struct LspWorkspace {
39    pub files: Arc<[LspWsFile]>,
40}
41
42/// Parse the file's source text into an arena-backed `ParsedDoc`, memoized on
43/// the converged db. Counterpart to [`crate::db::parse::parsed_doc`] keyed on
44/// mir's `SourceFile`.
45#[salsa::tracked(no_eq, lru = 2048)]
46pub fn parsed_doc(db: &dyn MirDatabase, file: SourceFile) -> ParsedArc {
47    ParsedArc(Arc::new(ParsedDoc::parse(file.text(db))))
48}
49
50/// Build the symbol map for a file. Shares the converged-db [`parsed_doc`], so
51/// a file open via `get_doc_salsa` and queried for completion parses once.
52#[salsa::tracked(no_eq, lru = 2048)]
53pub fn symbol_map(db: &dyn MirDatabase, file: SourceFile) -> SymbolMapArc {
54    let doc = parsed_doc(db, file);
55    SymbolMapArc(Arc::new(SymbolMap::build(doc.get())))
56}
57
58/// Parse-error count for a file, derived from [`parsed_doc`].
59#[salsa::tracked(lru = 2048)]
60pub fn parse_error_count(db: &dyn MirDatabase, file: SourceFile) -> usize {
61    parsed_doc(db, file).get().errors.len()
62}
63
64/// Compact per-file declaration index. Warm-start fast path: if a disk-cached
65/// index was seeded onto `wf`, return it without parsing. Otherwise share the
66/// converged-db [`parsed_doc`] and extract.
67#[salsa::tracked]
68pub fn file_index(db: &dyn MirDatabase, wf: LspWsFile) -> IndexArc {
69    if let Some(cached) = wf.cached_index(db) {
70        return IndexArc(cached);
71    }
72    let doc = parsed_doc(db, wf.source(db));
73    IndexArc(Arc::new(FileIndex::extract(doc.get())))
74}
75
76/// Aggregate workspace index over the project files in `ws`. Depends on each
77/// file's [`file_index`]; salsa invalidates only the touched file's contribution
78/// on an edit.
79#[salsa::tracked(no_eq)]
80pub fn workspace_index(db: &dyn MirDatabase, ws: LspWorkspace) -> WorkspaceIndexArc {
81    let ws_files = ws.files(db);
82    let mut files: Vec<(Url, Arc<FileIndex>)> = Vec::with_capacity(ws_files.len());
83    for wf in ws_files.iter() {
84        let Ok(url) = Url::parse(&wf.source(db).path(db)) else {
85            continue;
86        };
87        let idx = file_index(db, *wf).0.clone();
88        files.push((url, idx));
89    }
90    let (classes_by_name, subtypes_of, decls_by_name) = build_maps(&files);
91    WorkspaceIndexArc(Arc::new(WorkspaceIndexData {
92        files,
93        classes_by_name,
94        subtypes_of,
95        decls_by_name,
96    }))
97}