use std::collections::BTreeMap;
use std::sync::Arc;
use ra_ap_hir::Crate;
use ra_ap_ide::{Analysis, FileId};
use super::SemanticContext;
use super::snapshot::{SemanticSnapshotMismatch, VerifiedSnapshot, VerifiedSource, VerifiedUnit};
use super::targets::{EdgeSite, SemanticEdge};
#[derive(Clone)]
pub(crate) struct SemanticSite {
pub(crate) unit: usize,
pub(crate) path: Arc<str>,
pub(crate) line: u32,
pub(crate) column: u32,
}
pub(crate) struct SemanticDefinitionEdge {
pub(crate) source: SemanticSite,
pub(crate) target: SemanticSite,
pub(crate) enumerated: bool,
}
pub(crate) struct SemanticDefinitionTargets {
pub(crate) fingerprint: [u8; 32],
pub(crate) edges: Box<[SemanticDefinitionEdge]>,
}
type SourceKey = (ra_ap_ide::Crate, FileId);
type SourceIndex = BTreeMap<SourceKey, (usize, Arc<str>)>;
fn source_key(krate: Crate, file: FileId) -> SourceKey {
(krate.base(), file)
}
struct Projection<'a> {
context: &'a SemanticContext,
analysis: Analysis,
sources: SourceIndex,
}
pub(super) fn collect(
context: &SemanticContext,
verified: &VerifiedSnapshot,
) -> Result<SemanticDefinitionTargets, SemanticSnapshotMismatch> {
let projection = Projection {
context,
analysis: context.host.analysis(),
sources: source_index(context, verified)?,
};
let mut edges: Vec<SemanticDefinitionEdge> = Vec::new();
for unit in verified.units.iter().enumerate() {
projection.add_unit(unit, &mut edges)?;
}
Ok(SemanticDefinitionTargets {
fingerprint: verified.fingerprint,
edges: edges.into_boxed_slice(),
})
}
fn source_index(
context: &SemanticContext,
verified: &VerifiedSnapshot,
) -> Result<SourceIndex, SemanticSnapshotMismatch> {
let mut sources = SourceIndex::new();
for unit in verified.units.iter().enumerate() {
add_unit_sources(context, unit, &mut sources)?;
}
Ok(sources)
}
fn add_unit_sources(
context: &SemanticContext,
unit: (usize, &VerifiedUnit),
sources: &mut SourceIndex,
) -> Result<(), SemanticSnapshotMismatch> {
let (index, verified_unit) = unit;
for source in &verified_unit.sources {
let Some(file) = context.file_id(&source.absolute) else {
return Err(SemanticSnapshotMismatch::SourceAbsent {
path: Box::from(&*source.path),
});
};
sources.insert(
source_key(verified_unit.krate(), file),
(index, Arc::clone(&source.path)),
);
}
Ok(())
}
impl Projection<'_> {
fn add_unit(
&self,
unit: (usize, &VerifiedUnit),
edges: &mut Vec<SemanticDefinitionEdge>,
) -> Result<(), SemanticSnapshotMismatch> {
let (_, verified_unit) = unit;
for source in &verified_unit.sources {
self.add_source(unit, source, edges)?;
}
Ok(())
}
fn add_source(
&self,
unit: (usize, &VerifiedUnit),
source: &VerifiedSource,
edges: &mut Vec<SemanticDefinitionEdge>,
) -> Result<(), SemanticSnapshotMismatch> {
let (index, verified_unit) = unit;
let path = &source.path;
let Some(analysis) = self.context.analyze_snapshot_file(path, &source.absolute) else {
return Err(SemanticSnapshotMismatch::SourceAnalysis {
path: Box::from(&**path),
});
};
let stated = analysis
.semantic_edges()
.iter()
.filter(|edge| edge.source.krate == verified_unit.krate());
for edge in stated {
edges.extend(self.project((index, path), edge)?);
}
Ok(())
}
fn project(
&self,
source: (usize, &Arc<str>),
edge: &SemanticEdge,
) -> Result<Option<SemanticDefinitionEdge>, SemanticSnapshotMismatch> {
let (unit, path) = source;
let Some(target) = self.site(edge.target)? else {
return Ok(None);
};
let (line, column) = self.position(path, edge.source)?;
Ok(Some(SemanticDefinitionEdge {
source: SemanticSite {
unit,
path: Arc::clone(path),
line,
column,
},
target,
enumerated: edge.enumerated,
}))
}
fn site(&self, site: EdgeSite) -> Result<Option<SemanticSite>, SemanticSnapshotMismatch> {
let Some((unit, path)) = self.sources.get(&source_key(site.krate, site.file)) else {
return Ok(None);
};
let (line, column) = self.position(path, site)?;
Ok(Some(SemanticSite {
unit: *unit,
path: Arc::clone(path),
line,
column,
}))
}
fn position(&self, path: &str, site: EdgeSite) -> Result<(u32, u32), SemanticSnapshotMismatch> {
let found = self
.analysis
.file_line_index(site.file)
.ok()
.and_then(|index| index.try_line_col(site.offset));
match found {
Some(position) => Ok((position.line, position.col)),
None => Err(SemanticSnapshotMismatch::SourceCoordinates {
path: Box::from(path),
}),
}
}
}