php_lsp/db/input.rs
1//! Salsa inputs.
2
3use std::sync::Arc;
4
5use mir_analyzer::PhpVersion;
6
7use crate::index::file_index::FileIndex;
8
9/// Immortal per-file text input. One per unique URI ever seen.
10/// Holds the raw source text and the optional K2 on-disk cache seed.
11///
12/// `cached_index` (Phase K2): when `Some`, holds a pre-computed `FileIndex`
13/// loaded from the on-disk cache. `file_index` checks this field first and
14/// returns the cached index instead of parsing + extracting. Cleared back to
15/// `None` on any text edit — see `DocumentStore::mirror_text` — so a stale
16/// cached index cannot mask a real change. Seeded by workspace scan via
17/// `DocumentStore::seed_cached_index` before the first `file_index` call for
18/// that file.
19#[salsa::input]
20pub struct FileText {
21 pub text: Arc<str>,
22 pub cached_index: Option<Arc<FileIndex>>,
23}
24
25/// Tracked struct representing one active workspace file.
26/// Produced by `workspace_files`; salsa GC's it (and all downstream memos)
27/// when the file is removed from the workspace.
28/// Identity fields: uri + text_input (both stable per unique URI).
29#[salsa::tracked]
30pub struct SourceFile<'db> {
31 pub uri: Arc<str>,
32 pub text_input: FileText,
33}
34
35/// Workspace-level input: the set of active (non-deleted) files.
36/// Each entry is (uri_arc, FileText_handle) — sorted by uri for stable ordering.
37/// Only changed when files are added or removed, not on text edits.
38///
39/// Uses `durability = HIGH` conceptually — the file list changes rarely
40/// (workspace scan, deletions), not on every edit. Salsa's default durability
41/// is LOW; backend can opt into HIGH via `set_files` if churn becomes an issue.
42#[salsa::input]
43pub struct Workspace {
44 pub files: Arc<[(Arc<str>, FileText)]>,
45 /// Target PHP version for analysis. Changing this invalidates all
46 /// `semantic_issues` queries so diagnostics are re-evaluated against the
47 /// new version's rules.
48 pub php_version: PhpVersion,
49}
50
51/// Produce SourceFile tracked structs for all active workspace files.
52/// Salsa GC removes SourceFile entities (and cascades to parsed_doc, file_index,
53/// symbol_map, parse_error_count) when they are absent from this function's output.
54#[salsa::tracked]
55pub fn workspace_files<'db>(db: &'db dyn salsa::Database, ws: Workspace) -> Vec<SourceFile<'db>> {
56 ws.files(db)
57 .iter()
58 .map(|(uri, ft)| SourceFile::new(db, uri.clone(), *ft))
59 .collect()
60}
61
62/// O(log N) lookup of a single `SourceFile` by URI string.
63///
64/// `Workspace::files` is kept sorted by URI (see `DocumentStore::sync_workspace_files`),
65/// so binary search gives the index in O(log N). `workspace_files` outputs in the
66/// same 1:1 order, so the same index addresses both slices.
67pub fn find_source_file<'db>(
68 db: &'db dyn salsa::Database,
69 ws: Workspace,
70 uri_str: &str,
71) -> Option<SourceFile<'db>> {
72 let ws_files = ws.files(db);
73 let idx = ws_files
74 .binary_search_by(|(u, _)| u.as_ref().cmp(uri_str))
75 .ok()?;
76 workspace_files(db, ws).into_iter().nth(idx)
77}