Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
142 commits
Select commit Hold shift + click to select a range
44c3fdb
bench(ide): add real multi-file project benchmark
hongjr03 Aug 14, 2026
a39e56d
fix(hir-def): build empty item tree for non-compilation-unit roots
hongjr03 Aug 14, 2026
bf0a52f
bench(ide): decompose module-index build cost
hongjr03 Aug 14, 2026
dddc059
perf(preproc): compute include scopes in O(events) with monotonic stack
hongjr03 Aug 14, 2026
eea12d8
refactor(utils): drop (dev,ino) identity from path identity index
hongjr03 Aug 14, 2026
0d96ac1
perf(preproc-expand): memoize path_file_ids per revision
hongjr03 Aug 14, 2026
cfdf9e6
fix(hir-def): lower KeywordName in subroutine signatures
hongjr03 Aug 14, 2026
8413adc
fix(slang-sys): degrade instead of aborting on incomplete macro metadata
hongjr03 Aug 14, 2026
c09db86
bench(ide): measure semantic-index per-file cost
hongjr03 Aug 14, 2026
000fe6a
perf(ide): salsa-track workspace symbol index queries
hongjr03 Aug 14, 2026
3dc6e76
perf(preproc): complete parse-query LRU plumbing, raise default cap
hongjr03 Aug 14, 2026
8ad8f6b
perf(ide): split reference index from module-edge index
hongjr03 Aug 15, 2026
577ec00
perf(ide): incremental reference index via persistent cache
hongjr03 Aug 15, 2026
3b99516
perf(preproc): parse roots standalone with injected $unit macros
hongjr03 Aug 15, 2026
efebf60
perf(preproc): split parse tree from preprocessor trace
hongjr03 Aug 15, 2026
a31d999
perf(ide): patch cached reference index instead of re-merging
hongjr03 Aug 15, 2026
d60c3be
refactor(hir): thread resolution context through nameres
hongjr03 Aug 15, 2026
170279d
perf(ide): reuse resolution context for reference metadata
hongjr03 Aug 15, 2026
12c18a8
perf(preproc): cache per-file literal include scans
hongjr03 Aug 15, 2026
809b078
perf(ide): index plain source tokens directly
hongjr03 Aug 15, 2026
de2425b
perf(ide): patch cached reference index in place
hongjr03 Aug 15, 2026
6370e7b
bench(ide): measure real-project request latency
hongjr03 Aug 15, 2026
3b9adc8
bench(ide): isolate unit-scope validation cost
hongjr03 Aug 15, 2026
b0424b9
perf(ide): reuse resolution context across requests
hongjr03 Aug 15, 2026
170d73b
bench(ide): isolate post-edit aggregate queries
hongjr03 Aug 15, 2026
7d4e161
perf(ide): cache request file reference indexes
hongjr03 Aug 15, 2026
c620522
perf(ide): reuse cached unit index for completion
hongjr03 Aug 15, 2026
ff4aeb7
perf(ide): cache module edges across requests
hongjr03 Aug 15, 2026
41b4ece
perf(ide): bypass preproc lookup for plain files
hongjr03 Aug 15, 2026
e96c92f
perf(ide): cache macro-origin editability checks
hongjr03 Aug 15, 2026
c060503
perf(ide): cache syntax trees for completion requests
hongjr03 Aug 15, 2026
0f772ca
refactor(ide): separate revision caches from semantic artifacts
hongjr03 Aug 16, 2026
e290d13
perf(ide): materialize revision products by structure epoch
hongjr03 Aug 16, 2026
7532b27
perf(ide): prioritize revision product computation
hongjr03 Aug 16, 2026
533e763
refactor(ide): decouple revision cache from Salsa database
hongjr03 Aug 16, 2026
a72588f
refactor(ide): formalize structure epoch and drop backtick heuristic
hongjr03 Aug 16, 2026
57168cf
chore: clippy, fmt
hongjr03 Aug 16, 2026
c0298f5
refactor(ide): replace RevisionCache with a two-clock ProductStore
hongjr03 Aug 17, 2026
e2d7469
feat(bench): replace ignored ide benches with an LSP comparison harness
hongjr03 Aug 17, 2026
5f1a5a5
fix(bench): keep server samples when one method is unsupported
hongjr03 Aug 17, 2026
b618fda
refactor(ide): replace resolved FileSemanticIndex with a name table
hongjr03 Aug 17, 2026
f4f7051
fix(ide): recover name-table tokens by emitted id, not heuristics
hongjr03 Aug 17, 2026
1b4879d
fix(ide): use navigation_precedence in the preproc-ownership test
hongjr03 Aug 17, 2026
d095c04
refactor(hir-def): build $unit design units from the owner table
hongjr03 Aug 17, 2026
ab8d8a7
refactor(ide): keep ModuleIndex as item-tree identity
hongjr03 Aug 17, 2026
994c5b4
perf(ide): do not expand the workspace to jump to a module name
hongjr03 Aug 17, 2026
00ac5df
revert(ide): keep the parse LRU at 1024
hongjr03 Aug 17, 2026
0a0c57c
refactor(hir-def): extract an L0 declaration shard without keeping th…
hongjr03 Aug 17, 2026
a671657
perf(ide): resolve a design-unit name from the current-file L0 shard
hongjr03 Aug 17, 2026
c347c30
perf(ide): decide the structure epoch from L0 shards, not item_tree
hongjr03 Aug 17, 2026
4d68677
perf(preproc): parse a file from its static include closure
hongjr03 Aug 17, 2026
81a617d
perf(ide): invalidate only emitted include dependents
hongjr03 Aug 17, 2026
aa28fe0
fix(ide): select design-unit names in definition links
hongjr03 Aug 17, 2026
542acbe
perf(preproc): return parse dependencies with the tree
hongjr03 Aug 17, 2026
c1bf2d7
perf(diagnostics): isolate profile compilation in a worker
hongjr03 Aug 17, 2026
03b3ad8
perf(syntax): share the preprocessor trace instead of copying it
hongjr03 Aug 17, 2026
dd40c02
perf(slang-sys): look up a token's emitted position in constant time
hongjr03 Aug 17, 2026
a2c3471
fix(xtask): measure cold latency only after the server can answer
hongjr03 Aug 17, 2026
7a385c6
perf(preproc): invert include edges instead of every root's preproc m…
hongjr03 Aug 17, 2026
a48b6c0
perf(utils): identify a path by its spelling, not by asking the files…
hongjr03 Aug 17, 2026
2e2d645
perf(ide): diagnose the open file, hover a design-unit from its L0 he…
hongjr03 Aug 17, 2026
ed22ca1
perf(ide): find design-unit references from L0 instantiations
hongjr03 Aug 17, 2026
5c19570
perf(ide): join design-unit references through unit_index
hongjr03 Aug 17, 2026
986a626
perf(hir-def): detect L0 preprocessor activity from directive trivia
hongjr03 Aug 17, 2026
054e0ea
refactor(design-graph): extract FileFacts as UnitId
hongjr03 Aug 17, 2026
e34459d
perf(ide): record generated units from a paid artifact
hongjr03 Aug 17, 2026
97710e5
refactor(ide): answer compilation-unit names from DesignGraph
hongjr03 Aug 17, 2026
1c57398
refactor(ide): drop the item-tree ModuleIndex
hongjr03 Aug 17, 2026
3eda5bf
perf(preproc): decide preprocessor activity without a full Trace
hongjr03 Aug 18, 2026
ccb6401
perf(ide): emit design-graph fold and hit spans
hongjr03 Aug 18, 2026
692d9a6
perf(ide): fold file_facts in parallel and stop recompiling on didOpen
hongjr03 Aug 18, 2026
a5861e4
chore: clippy, fmt
hongjr03 Aug 18, 2026
c4b3036
perf(ide): upsert the design graph per file and stop blocking edits o…
hongjr03 Aug 18, 2026
713bcf5
fix(preproc): issue include buffers under slang's lookup path
hongjr03 Aug 18, 2026
c5f30d4
perf(ide): do not fold the design graph to hover a CU name
hongjr03 Aug 18, 2026
4a773b3
refactor(hir-def): drop unused DeclarationSkeleton
hongjr03 Aug 18, 2026
03993bf
refactor(ide): answer call hierarchy from FileFacts and DesignGraph
hongjr03 Aug 18, 2026
c8bb501
refactor(ide): collapse SemanticSnapshotInputs onto ResolutionContext
hongjr03 Aug 18, 2026
e004fa1
refactor(ide): find reference candidates in FileFacts mentions
hongjr03 Aug 18, 2026
1b2a775
refactor(ide): resolve module names through the injected design graph
hongjr03 Aug 18, 2026
a00b54b
refactor(hir): drop the salsa source_design_graph fallback
hongjr03 Aug 18, 2026
7675fee
chore: remove the comparison bench harness
hongjr03 Aug 18, 2026
9b7d3d1
chore: clippy, fmt
hongjr03 Aug 18, 2026
caa3576
test(ide): a renamed generated CU must not stay on the graph
hongjr03 Aug 18, 2026
d844bad
fix(ide): generated names must die with their artifact fingerprint
hongjr03 Aug 18, 2026
bb8ea61
fix(ide): profile cache is slang only so didOpen cannot double Vide
hongjr03 Aug 18, 2026
b37d8f0
fix(ide): a hung slang worker must die instead of pinning the server
hongjr03 Aug 18, 2026
9582334
fix(ide): a project-config change is a new store even when files are …
hongjr03 Aug 18, 2026
d01291b
bench(ide): restore a fold and post-edit timer the tree can run
hongjr03 Aug 18, 2026
746fb3c
docs(preproc): the two unexpanded parses cannot share one salsa query
hongjr03 Aug 18, 2026
8854428
refactor: drop dead seams that duplicated live incrementality
hongjr03 Aug 18, 2026
6d8b201
refactor: the name directory is a catalog, and Resolution is one type
hongjr03 Aug 18, 2026
6a06621
perf(design-graph): split decls from mentions and keep the epoch
hongjr03 Aug 18, 2026
fee486b
refactor(ide): one store transition, one ResolutionContext clock
hongjr03 Aug 18, 2026
6474c73
refactor(preproc): plan roots are SystemVerilog or library maps
hongjr03 Aug 18, 2026
40dbf45
test(preproc): a clean profile file must not ship its source text
hongjr03 Aug 18, 2026
27a32f0
perf(preproc): ship dirty overlays only and drop diagnostic Wire types
hongjr03 Aug 18, 2026
5be0447
fix(ide): job overlay text is optional and plan roots are typed
hongjr03 Aug 18, 2026
7703989
fix(ide): republish Vide through Result::unwrap_or_default
hongjr03 Aug 18, 2026
d116e7e
perf(base-db): do not rewrite file_kind on a text-only change
hongjr03 Aug 19, 2026
80c70f4
bench(ide): LRU evicts at a revision boundary, not during a fold
hongjr03 Aug 19, 2026
50d791e
perf(design-graph): stop evicting file_decls with the parse LRU
hongjr03 Aug 19, 2026
d79101b
refactor(ide): production catalog is salsa source plus overlay
hongjr03 Aug 19, 2026
42e6a6f
test(ide): resolution rebuilds the package export closure every call
hongjr03 Aug 19, 2026
2a6c1d5
perf(hir-def): package export closure is a salsa query
hongjr03 Aug 19, 2026
baa76d3
feat(xtask): include-shape is a rerunnable corpus metric
hongjr03 Aug 19, 2026
094a9a1
refactor(preproc): three unexpanded parses, one independence predicate
hongjr03 Aug 19, 2026
981923b
feat(slang-sys): class-member lookup is the T4 slang slice
hongjr03 Aug 19, 2026
071ad09
perf(hir-def): owner lookup is indexed by name and kind
hongjr03 Aug 19, 2026
d86f53f
fix(xtask): UVM Unbalanced was typedef-class and extern-function noise
hongjr03 Aug 19, 2026
3f5e4eb
test(ide): a salsa hit is still one closure per request
hongjr03 Aug 19, 2026
49dea0c
fix(ide): the §3.7 walk is test-only
hongjr03 Aug 19, 2026
a16f3e0
fix(ide): T4 consistency is slang versus TypeSystem
hongjr03 Aug 19, 2026
3b98bc7
test(ide): shipped resolution still projects L0 names through the ove…
hongjr03 Aug 19, 2026
bce5d4a
refactor(hir-def): resolution locates files and uses paid-parse owners
hongjr03 Aug 19, 2026
2599803
feat(slang-sys): class-member lookup identifies the file once
hongjr03 Aug 19, 2026
c45ad6f
feat(ide): hover asks a resident compilation, not a fresh one
hongjr03 Aug 19, 2026
ae0d89d
refactor(hir-def): a class is a syntax record, not an owner
hongjr03 Aug 19, 2026
60e075c
feat(ide): qihe facts stay on SourceAstId across edits
hongjr03 Aug 19, 2026
16a5d4e
feat(slang-sys): elaborated instances carry a hierarchical path
hongjr03 Aug 19, 2026
440a5e4
fix(slang-sys): buffer identity is the assigned path
hongjr03 Aug 19, 2026
629f832
feat(ide): instance facts hang on a hierarchical path
hongjr03 Aug 19, 2026
e4af860
feat(slang-sys): look up the symbol at a source offset
hongjr03 Aug 19, 2026
b5e6bdb
feat(ide): hover types and class scope come from slang
hongjr03 Aug 19, 2026
6f605c7
refactor(hir-def): pathres does not resolve ::
hongjr03 Aug 19, 2026
f2ca580
feat(ide): package and class :: come from slang
hongjr03 Aug 20, 2026
32206f2
feat(slang-sys): members and types of a selected name or span
hongjr03 Aug 20, 2026
e348524
feat(ide): member completion asks elaboration, not TypeSystem
hongjr03 Aug 20, 2026
554af11
feat(ide): extract-variable types come from slang
hongjr03 Aug 20, 2026
0204086
refactor(hir-ty): delete unused TypeSystem inference
hongjr03 Aug 20, 2026
efabec7
style: rustfmt so CI fmt --check passes
hongjr03 Aug 20, 2026
8b5d5c3
fix: clippy -D warnings after TypeSystem removal
hongjr03 Aug 20, 2026
556690d
fix(preproc-expand): Windows include path spelling
hongjr03 Aug 20, 2026
cf220c5
fix(slang-sys): lookupName with selectors must not abort
hongjr03 Aug 20, 2026
092001c
test: snapshots after dropped typed filter
hongjr03 Aug 20, 2026
95111f8
fix(ide): do not open a slang section on hover
hongjr03 Aug 20, 2026
564d8a0
refactor(ide): one query pipeline for the elaboration service
hongjr03 Aug 20, 2026
20ef279
fix(ide): elaboration degradation is recorded, and tree reuse is removed
hongjr03 Aug 20, 2026
62ec6a9
fix(slang-sys): ask slang how a name resolves instead of catching its…
hongjr03 Aug 20, 2026
af99caa
chore: move hir-ty as part of ide render
hongjr03 Aug 20, 2026
aec02c7
chore: ignore the local docs directory
hongjr03 Aug 20, 2026
8b2a18e
chore: clippy, fmt
hongjr03 Aug 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ tags

# generated files
generated.rs
docs/public/vide-lab/
docs/public/schemas/
docs/hir-def/
/docs/
editors/zed/extension.wasm
editors/zed/grammars/systemverilog/

Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ description = "Language server for Verilog and System-Verilog"
members = [
".",
"crates/base-db",
"crates/design-graph",
"crates/hir-def",
"crates/hir-semantics",
"crates/hir-ty",
"crates/ide",
"crates/preproc",
"crates/preproc-expand",
Expand Down Expand Up @@ -78,9 +78,9 @@ triomphe.workspace = true

[workspace.dependencies]
base-db = { path = "./crates/base-db/", version = "0.0.0" }
design-graph = { path = "./crates/design-graph/", version = "0.0.0" }
hir-def = { path = "./crates/hir-def/", version = "0.0.0" }
hir-semantics = { path = "./crates/hir-semantics/", version = "0.0.0" }
hir-ty = { path = "./crates/hir-ty/", version = "0.0.0" }
ide = { path = "./crates/ide/", version = "0.0.0" }
preproc = { path = "./crates/preproc/", version = "0.0.0" }
preproc-expand = { path = "./crates/preproc-expand/", version = "0.0.0" }
Expand Down
9 changes: 8 additions & 1 deletion crates/base-db/src/change.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,14 @@ impl Change {
}

let text = changed_file.text().unwrap_or_else(|| Arc::from(""));
db.set_file_kind_with_durability(file_id, kind, durability);
// Salsa treats every input write as a new revision, even when the
// value is unchanged. Rewriting kind on a body-only Modify dirties
// every query that reads `file_kind` (workspace catalogs,
// `unit_scope`, fold filters). Skip the write when the salsa
// input already exists and already holds this kind.
if !db.files().contains(&file_id) || db.file_kind(file_id) != kind {
db.set_file_kind_with_durability(file_id, kind, durability);
}
db.set_file_text_with_durability(file_id, text, durability);
}

Expand Down
8 changes: 3 additions & 5 deletions crates/hir-ty/Cargo.toml → crates/design-graph/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
[package]
name = "hir-ty"
name = "design-graph"
version = "0.0.0"
description = "Compilation-unit design-unit facts and name join"
edition.workspace = true

[dependencies]
base-db.workspace = true
hir-def.workspace = true
rustc-hash.workspace = true
salsa.workspace = true
smallvec.workspace = true
smol_str.workspace = true
syntax.workspace = true
triomphe.workspace = true
utils.workspace = true
vfs.workspace = true

[dev-dependencies]
preproc-expand.workspace = true
126 changes: 126 additions & 0 deletions crates/design-graph/src/db.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
//! Salsa `file_facts` over an unexpanded parse.

use std::cell::Cell;

use base_db::{salsa, source_db::SourceRootDb};
use syntax::{SyntaxTree, SyntaxTreeOptions};
use triomphe::Arc;
use vfs::FileId;

use crate::{
facts::{DeclIndex, FileFacts, extract},
graph::{GeneratedUnits, UnitCatalog},
};

thread_local! {
pub static SOURCE_CATALOG_RUNS: Cell<u32> = const { Cell::new(0) };
}

#[salsa::interned(unsafe(no_lifetime), revisions = usize::MAX, debug)]
pub struct FileFactsKey {
#[returns(copy)]
pub file_id: FileId,
}

/// Workspace database that can extract unexpanded design-unit facts.
#[salsa::db]
pub trait DesignGraphDb: SourceRootDb {}

fn default_source_buffer_path(db: &dyn SourceRootDb, file_id: FileId) -> String {
db.file_path(file_id).map(|path| path.to_string()).unwrap_or_else(|| {
if cfg!(windows) {
format!(r"C:\__vide_virtual__\{}", file_id.index())
} else {
format!("/__vide_virtual__/{}", file_id.index())
}
})
}

#[salsa::tracked(lru = 256, returns(clone))]
pub fn file_facts_query(db: &dyn DesignGraphDb, key: FileFactsKey) -> Arc<FileFacts> {
let file_id = key.file_id(db);
let text = db.file_text(file_id);
let path = default_source_buffer_path(db, file_id);
let name =
db.file_path(file_id).map(|path| path.to_string()).unwrap_or_else(|| "source".into());
let profile_id = db.file_compilation_profile(file_id);
let predefines = db.project_config().preprocess_for_profile(profile_id).predefine_strings();
// U2: profile predefines, no include expansion. This is not
// `SyntaxTreeOptions::without_include_expansion()`: that helper ships
// empty predefines so `source_model` (U1) stays file-local. FileFacts
// must see the same `ifdef` view the profile will compile, or gated
// units disappear from the name catalog. It also cannot share U3
// (`literal_include_targets`): that scan needs a preprocessor `Trace`,
// and attaching a Trace here would make every L0 fact pay for include
// resolution. Sharing one salsa query would hide gated units, invalidate
// the file-local preprocessor model, or both.
//
// `preprocessor_independent` is `syntax::preprocessor_independent` —
// the same directive-trivia walk U1 uses. The boolean cannot diverge;
// the trees can, because predefines differ.
let options = SyntaxTreeOptions {
predefines,
include_paths: Vec::new(),
include_buffers: Vec::new(),
expand_includes: false,
collect_expected_syntax: false,
expected_syntax_offset: None,
};
syntax::record_unexpanded_parse("file_facts");
let tree = SyntaxTree::from_file_in_memory_with_options(&text, &name, &path, &options);
Arc::new(extract::from_tree(file_id, &tree, &text))
}

/// Position-free and small: must not share the parse LRU with `file_facts`.
/// A workspace larger than that LRU would otherwise re-extract every evicted
/// file's decls on the next revision, which re-parses `file_facts` with them.
#[salsa::tracked(returns(clone))]
pub fn file_decls_query(db: &dyn DesignGraphDb, key: FileFactsKey) -> Arc<DeclIndex> {
Arc::new(file_facts_query(db, key).decls())
}

#[salsa::interned(unsafe(no_lifetime), revisions = usize::MAX, debug)]
pub struct UnitCatalogKey {
#[returns(copy)]
pub _unit: (),
}

/// L0 name catalog of source decls. Production resolution uses this as a
/// name → file locator. Generated names are not merged here; they live on
/// the paid-parse owner table (`HirFileId::Macro`).
#[salsa::tracked(lru = 4, returns(clone))]
pub fn source_unit_catalog_query(
db: &dyn DesignGraphDb,
_key: UnitCatalogKey,
) -> triomphe::Arc<UnitCatalog> {
SOURCE_CATALOG_RUNS.with(|runs| runs.set(runs.get() + 1));
let decls: Vec<_> = db
.files()
.iter()
.copied()
.filter(|&file_id| db.file_kind(file_id).is_semantic_compilation_unit())
.map(|file_id| db.file_decls(file_id))
.collect();
triomphe::Arc::new(UnitCatalog::from_decls(
decls.iter().map(std::convert::AsRef::as_ref),
&GeneratedUnits::default(),
))
}

pub fn set_file_facts_lru_capacity(db: &mut dyn DesignGraphDb, capacity: usize) {
file_facts_query::set_lru_capacity(db, capacity);
}

impl dyn DesignGraphDb + '_ {
pub fn file_facts(&self, file_id: FileId) -> Arc<FileFacts> {
file_facts_query(self, FileFactsKey::new(self, file_id))
}

pub fn file_decls(&self, file_id: FileId) -> Arc<DeclIndex> {
file_decls_query(self, FileFactsKey::new(self, file_id))
}

pub fn source_unit_catalog(&self) -> triomphe::Arc<UnitCatalog> {
source_unit_catalog_query(self, UnitCatalogKey::new(self, ()))
}
}
182 changes: 182 additions & 0 deletions crates/design-graph/src/facts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
//! Per-file unexpanded design-unit facts.

use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use smol_str::SmolStr;
use syntax::TokenKind;
use utils::line_index::{TextRange, TextSize};
use vfs::FileId;

use crate::unit::{InstantiationRole, UnitId, UnitNode, UnitOrigin};

pub mod extract;

/// One name-like token, unresolved.
///
/// `emitted` is the preprocessor-trace index when the extract tree assigned
/// one. Macro-expanded tokens share display ranges, so later recovery on the
/// authoritative parse needs this identity when the two traces agree.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Mention {
pub name: smol_str::SmolStr,
pub kind: TokenKind,
pub range: TextRange,
pub emitted: Option<u32>,
}

/// Instantiation type-name token. Primitive instantiations are not recorded.
///
/// `container` is the compilation-unit that directly contains the site.
/// Nested-module bodies leave it empty — those are not CU graph edges.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InstantiationSite {
pub file: FileId,
pub name: smol_str::SmolStr,
pub range: TextRange,
pub role: InstantiationRole,
pub emitted: Option<u32>,
pub container: Option<UnitId>,
}

/// `import p::x` / `import p::*`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ImportSpec {
pub package: smol_str::SmolStr,
pub item: Option<smol_str::SmolStr>,
/// Package-name token in display coordinates.
pub range: TextRange,
}

/// Left identifier of a non-dot `ScopedName` (`p::y`).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PackageRefSite {
pub name: smol_str::SmolStr,
pub range: TextRange,
pub emitted: Option<u32>,
}

/// Position-free CU declaration index. This is what salsa backdates;
/// ranges live on [`Mentions`] and must not enter the global catalog.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct DeclIndex {
pub units: Box<[DeclUnit]>,
pub imports: Box<[(SmolStr, Option<SmolStr>)]>,
pub preprocessor_independent: bool,
pub has_compilation_unit_locals: bool,
}

/// One CU declaration without source ranges.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DeclUnit {
pub id: UnitId,
pub origin: UnitOrigin,
pub header_fingerprint: u64,
}

/// Name-like tokens of one file, with a name → offset inverted index.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Mentions {
pub entries: Box<[Mention]>,
by_name: FxHashMap<SmolStr, SmallVec<[u32; 2]>>,
}

impl Mentions {
pub fn from_entries(entries: Box<[Mention]>) -> Self {
let mut by_name: FxHashMap<SmolStr, SmallVec<[u32; 2]>> = FxHashMap::default();
for (index, mention) in entries.iter().enumerate() {
by_name.entry(mention.name.clone()).or_default().push(index as u32);
}
Self { entries, by_name }
}

pub fn mentions_name(&self, name: &str) -> bool {
self.by_name.contains_key(name)
}

pub fn mentions_of(&self, name: &str) -> impl Iterator<Item = &Mention> {
self.by_name.get(name).into_iter().flatten().map(|&index| &self.entries[index as usize])
}
}

/// Compact unexpanded slice of one file. No syntax tree, no interned owner.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct FileFacts {
pub units: Box<[UnitNode]>,
pub mentions: Mentions,
pub imports: Box<[ImportSpec]>,
pub instantiations: Box<[InstantiationSite]>,
pub package_refs: Box<[PackageRefSite]>,
pub preprocessor_independent: bool,
pub has_compilation_unit_locals: bool,
}

impl FileFacts {
pub fn decls(&self) -> DeclIndex {
DeclIndex {
units: self
.units
.iter()
.map(|unit| DeclUnit {
id: unit.id.clone(),
origin: unit.origin,
header_fingerprint: unit.header_fingerprint,
})
.collect::<Vec<_>>()
.into_boxed_slice(),
imports: self
.imports
.iter()
.map(|import| (import.package.clone(), import.item.clone()))
.collect::<Vec<_>>()
.into_boxed_slice(),
preprocessor_independent: self.preprocessor_independent,
has_compilation_unit_locals: self.has_compilation_unit_locals,
}
}

pub fn mentions_name(&self, name: &str) -> bool {
self.mentions.mentions_name(name)
}

pub fn mentions_of(&self, name: &str) -> impl Iterator<Item = &Mention> {
self.mentions.mentions_of(name)
}

pub fn has_compilation_unit_locals(&self) -> bool {
self.has_compilation_unit_locals
}

/// Design-unit whose recorded name token covers `offset`.
pub fn design_unit_at(&self, offset: TextSize) -> Option<&UnitNode> {
self.units.iter().find(|unit| unit.name_range.is_some_and(|range| range.contains(offset)))
}

pub fn unit(&self, id: UnitId) -> Option<&UnitNode> {
self.units.iter().find(|unit| unit.id == id)
}

pub fn instantiation_at(&self, offset: TextSize) -> Option<&InstantiationSite> {
self.instantiations.iter().find(|site| site.range.contains(offset))
}

pub fn unit_at_name_range(&self, range: TextRange) -> Option<&UnitNode> {
self.units.iter().find(|unit| unit.name_range == Some(range))
}

/// Import package token or `::` left ident covering `offset`.
pub fn package_token_at(&self, offset: TextSize) -> Option<(smol_str::SmolStr, TextRange)> {
if let Some(import) = self.imports.iter().find(|import| import.range.contains(offset)) {
return Some((import.package.clone(), import.range));
}
self.package_refs
.iter()
.find(|site| site.range.contains(offset))
.map(|site| (site.name.clone(), site.range))
}

/// Whether CU units and import *names* match. Mentions, instantiations,
/// package-ref sites, and source ranges do not move the structure clock.
pub fn same_structure(&self, other: &Self) -> bool {
self.decls() == other.decls()
}
}
Loading
Loading