use std::collections::HashMap;
use crate::Span;
use crate::pr::Path;
use super::Project;
pub struct SymbolInfo {
pub source_span: Span,
pub target_path: Option<Path>,
pub target_span: Option<Span>,
}
impl Project {
pub fn find_by_span(&self, source_id: u16, offset: u32) -> Option<SymbolInfo> {
let (target, source_span) = self.target_map.find_at(source_id, offset)?;
Some(match target {
TargetSpan::Global(path) => {
let target_span = self.root_module.get(path).and_then(|d| d.span_name);
SymbolInfo {
source_span,
target_path: Some(path.clone()),
target_span,
}
}
TargetSpan::Span(target_span) => SymbolInfo {
source_span,
target_path: None,
target_span: Some(*target_span),
},
})
}
}
#[derive(Debug, Default)]
pub struct TargetMap {
by_source: HashMap<u16, Vec<TargetEntry>>,
}
#[derive(Debug)]
struct TargetEntry {
start: u32,
len: u16,
target: TargetSpan,
}
#[derive(Debug)]
pub enum TargetSpan {
Global(Path),
Span(Span),
}
impl TargetMap {
pub fn build(entries: Vec<(Span, TargetSpan)>) -> Self {
let mut by_source: HashMap<u16, Vec<TargetEntry>> = HashMap::new();
for (span, target) in entries {
let entry = by_source.entry(span.source_id).or_default();
entry.push(TargetEntry {
start: span.start,
len: span.len,
target,
});
}
for vec in by_source.values_mut() {
vec.sort_by_key(|e| e.start);
}
TargetMap { by_source }
}
pub fn find_at(&self, source_id: u16, offset: u32) -> Option<(&TargetSpan, Span)> {
let entries = self.by_source.get(&source_id)?;
let end = entries.partition_point(|e| e.start <= offset);
entries[..end]
.iter()
.filter(|e| e.start + e.len as u32 > offset)
.min_by_key(|e| e.len)
.map(|e| {
let span = Span {
source_id,
start: e.start,
len: e.len,
};
(&e.target, span)
})
}
}