use fst;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::iter;
use std::path::{Path, PathBuf};
use std::time::SystemTime;
use crate::raw::{CrateId, DefKind};
use crate::{Id, Span, SymbolQuery};
use span::{Column, Row, ZeroIndexed};
pub(crate) struct Analysis {
pub per_crate: HashMap<CrateId, PerCrateAnalysis>,
pub(crate) aliased_imports: HashSet<Id>,
pub(crate) crate_names: HashMap<String, Vec<CrateId>>,
pub doc_url_base: String,
pub src_url_base: String,
}
pub struct PerCrateAnalysis {
pub def_id_for_span: HashMap<Span, Ref>,
pub defs: HashMap<Id, Def>,
pub defs_per_file: HashMap<PathBuf, Vec<Id>>,
pub children: HashMap<Id, HashSet<Id>>,
pub def_names: HashMap<String, Vec<Id>>,
pub def_fst: fst::Map,
pub def_fst_values: Vec<Vec<Id>>,
pub ref_spans: HashMap<Id, Vec<Span>>,
pub globs: HashMap<Span, Glob>,
pub impls: HashMap<Id, Vec<Span>>,
pub idents: HashMap<PathBuf, IdentsByLine>,
pub root_id: Option<Id>,
pub timestamp: SystemTime,
pub path: Option<PathBuf>,
pub global_crate_num: u32,
}
#[derive(Debug, Clone)]
pub enum Ref {
Id(Id),
Double(Id, Id),
Multi(Id, usize),
}
impl Ref {
pub fn some_id(&self) -> Id {
match *self {
Ref::Id(id) => id,
Ref::Double(id, _) => id,
Ref::Multi(id, _) => id,
}
}
pub fn add_id(&self, def_id: Id) -> Ref {
match *self {
Ref::Id(id) => Ref::Double(id, def_id),
Ref::Double(id, _) => Ref::Multi(id, 3),
Ref::Multi(id, n) => Ref::Multi(id, n + 1),
}
}
}
#[derive(Debug, Clone)]
pub struct Def {
pub kind: DefKind,
pub span: Span,
pub name: String,
pub qualname: String,
pub distro_crate: bool,
pub parent: Option<Id>,
pub value: String,
pub docs: String,
}
pub type Idents = HashMap<PathBuf, IdentsByLine>;
pub type IdentsByLine = BTreeMap<Row<ZeroIndexed>, IdentsByColumn>;
pub type IdentsByColumn = BTreeMap<Column<ZeroIndexed>, IdentBound>;
#[derive(new, Clone, Debug)]
pub struct IdentBound {
pub column_end: Column<ZeroIndexed>,
pub id: Id,
pub kind: IdentKind,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum IdentKind {
Def,
Ref,
}
#[derive(new, Clone, Debug)]
pub struct Ident {
pub span: Span,
pub id: Id,
pub kind: IdentKind,
}
#[derive(Debug, Clone)]
pub struct Signature {
pub span: Span,
pub text: String,
pub ident_start: u32,
pub ident_end: u32,
pub defs: Vec<SigElement>,
pub refs: Vec<SigElement>,
}
#[derive(Debug, Clone)]
pub struct SigElement {
pub id: Id,
pub start: usize,
pub end: usize,
}
#[derive(Debug)]
pub struct Glob {
pub value: String,
}
impl PerCrateAnalysis {
pub fn new(timestamp: SystemTime, path: Option<PathBuf>) -> PerCrateAnalysis {
let empty_fst = fst::Map::from_iter(iter::empty::<(String, u64)>()).unwrap();
PerCrateAnalysis {
def_id_for_span: HashMap::new(),
defs: HashMap::new(),
defs_per_file: HashMap::new(),
children: HashMap::new(),
def_names: HashMap::new(),
def_fst: empty_fst,
def_fst_values: Vec::new(),
ref_spans: HashMap::new(),
globs: HashMap::new(),
impls: HashMap::new(),
idents: HashMap::new(),
root_id: None,
timestamp,
path,
global_crate_num: 0,
}
}
pub(crate) fn has_congruent_def(&self, local_id: u32, span: &Span) -> bool {
let id = Id::from_crate_and_local(self.global_crate_num, local_id);
match self.defs.get(&id) {
Some(existing) => span == &existing.span,
None => false,
}
}
#[cfg(feature = "idents")]
fn idents(&self, span: &Span) -> Vec<Ident> {
self.idents
.get(&span.file)
.map(|by_line| {
(span.range.row_start..=span.range.row_end)
.flat_map(|line| {
let vec = by_line
.get(&line)
.iter()
.flat_map(|by_col| {
by_col.into_iter().filter_map(|(col_start, id)| {
if col_start <= &span.range.col_end
&& id.column_end >= span.range.col_start
{
Some(Ident::new(
Span::new(
line,
line,
*col_start,
id.column_end,
span.file.clone(),
),
id.id,
id.kind,
))
} else {
None
}
})
})
.collect::<Vec<Ident>>();
vec.into_iter()
})
.collect::<Vec<Ident>>()
})
.unwrap_or_else(Vec::new)
}
}
impl Analysis {
pub fn new() -> Analysis {
Analysis {
per_crate: HashMap::new(),
aliased_imports: HashSet::new(),
crate_names: HashMap::new(),
doc_url_base: "https://doc.rust-lang.org/nightly".to_owned(),
src_url_base: "https://github.com/rust-lang/rust/blob/master".to_owned(),
}
}
pub fn timestamps(&self) -> HashMap<PathBuf, SystemTime> {
self.per_crate
.values()
.filter(|c| c.path.is_some())
.map(|c| (c.path.as_ref().unwrap().clone(), c.timestamp))
.collect()
}
pub fn update(&mut self, crate_id: CrateId, per_crate: PerCrateAnalysis) {
self.per_crate.insert(crate_id, per_crate);
}
pub fn has_def(&self, id: Id) -> bool {
self.per_crate.values().any(|c| c.defs.contains_key(&id))
}
pub fn for_each_crate<F, T>(&self, f: F) -> Option<T>
where
F: Fn(&PerCrateAnalysis) -> Option<T>,
{
let mut result = vec![];
for per_crate in self.per_crate.values() {
if let Some(t) = f(per_crate) {
result.push(t);
}
}
result.into_iter().nth(0)
}
pub fn for_all_crates<F, T>(&self, f: F) -> Vec<T>
where
F: Fn(&PerCrateAnalysis) -> Option<Vec<T>>,
{
let mut result = vec![];
for per_crate in self.per_crate.values() {
if let Some(this_crate) = f(per_crate) {
result.extend(this_crate);
}
}
result
}
pub fn def_id_for_span(&self, span: &Span) -> Option<Id> {
self.ref_for_span(span).map(|r| r.some_id())
}
pub fn ref_for_span(&self, span: &Span) -> Option<Ref> {
self.for_each_crate(|c| c.def_id_for_span.get(span).cloned())
}
pub fn local_def_id_for_span(&self, span: &Span) -> Option<Id> {
self.for_each_crate(|c| {
c.def_id_for_span.get(span).map(Ref::some_id).and_then(|id| {
if c.defs.contains_key(&id) {
Some(id)
} else {
None
}
})
})
}
pub fn with_defs<F, T>(&self, id: Id, f: F) -> Option<T>
where
F: Fn(&Def) -> T,
{
self.for_each_crate(|c| c.defs.get(&id).map(&f))
}
pub fn with_defs_and_then<F, T>(&self, id: Id, f: F) -> Option<T>
where
F: Fn(&Def) -> Option<T>,
{
self.for_each_crate(|c| c.defs.get(&id).and_then(&f))
}
pub fn with_globs<F, T>(&self, span: &Span, f: F) -> Option<T>
where
F: Fn(&Glob) -> T,
{
self.for_each_crate(|c| c.globs.get(span).map(&f))
}
pub fn for_each_child<F, T>(&self, id: Id, mut f: F) -> Option<Vec<T>>
where
F: FnMut(Id, &Def) -> T,
{
for per_crate in self.per_crate.values() {
if let Some(children) = per_crate.children.get(&id) {
return Some(
children
.iter()
.filter_map(|id| {
let def = per_crate.defs.get(id);
if def.is_none() {
info!("def not found for {}", id);
}
def.map(|def| f(*id, def))
})
.collect(),
);
}
}
Some(vec![])
}
pub fn with_ref_spans<F, T>(&self, id: Id, f: F) -> Option<T>
where
F: Fn(&Vec<Span>) -> Option<T>,
{
self.for_each_crate(|c| c.ref_spans.get(&id).and_then(&f))
}
pub fn with_defs_per_file<F, T>(&self, file: &Path, f: F) -> Option<T>
where
F: Fn(&Vec<Id>) -> T,
{
self.for_each_crate(|c| c.defs_per_file.get(file).map(&f))
}
#[cfg(feature = "idents")]
pub fn idents(&self, span: &Span) -> Vec<Ident> {
self.for_each_crate(|c| {
let result = c.idents(span);
if result.is_empty() {
None
} else {
Some(result)
}
})
.unwrap_or_else(Vec::new)
}
pub fn query_defs(&self, query: SymbolQuery) -> Vec<Def> {
let mut crates = Vec::with_capacity(self.per_crate.len());
let stream = query.build_stream(self.per_crate.values().map(|c| {
crates.push(c);
&c.def_fst
}));
query.search_stream(stream, |acc, e| {
let c = &crates[e.index];
let ids = &c.def_fst_values[e.value as usize];
acc.extend(ids.iter().flat_map(|id| c.defs.get(id)).cloned());
})
}
pub fn with_def_names<F, T>(&self, name: &str, f: F) -> Vec<T>
where
F: Fn(&Vec<Id>) -> Vec<T>,
{
self.for_all_crates(|c| c.def_names.get(name).map(&f))
}
}