use std::collections::HashMap;
use std::sync::Arc;
use tower_lsp_server::ls_types::Uri;
use crate::index::file_index::FileIndex;
#[derive(Debug, Clone, Copy)]
pub struct ClassRef {
pub file: u32,
pub class: u32,
}
#[derive(Debug, Clone, Copy)]
pub struct FunctionRef {
pub file: u32,
pub function: u32,
}
pub struct WorkspaceIndexData {
pub files: Vec<(Uri, Arc<FileIndex>)>,
pub(crate) file_paths: Vec<Arc<str>>,
pub(crate) path_to_file_idx: HashMap<Arc<str>, u32>,
pub classes_by_lowercase_name: Vec<(Box<str>, ClassRef)>,
}
pub(crate) fn build_maps(files: &[(Uri, Arc<FileIndex>)]) -> Vec<(Box<str>, ClassRef)> {
let mut first_by_name: HashMap<Box<str>, ClassRef> = HashMap::new();
for (file_idx, (_, idx)) in files.iter().enumerate() {
let file_idx = file_idx as u32;
for (cls_idx, cls) in idx.classes.iter().enumerate() {
first_by_name
.entry(cls.name.as_ref().into())
.or_insert(ClassRef {
file: file_idx,
class: cls_idx as u32,
});
}
}
let mut classes_by_lowercase_name: Vec<(Box<str>, ClassRef)> = first_by_name
.into_iter()
.map(|(name, cr)| (name.to_lowercase().into(), cr))
.collect();
classes_by_lowercase_name.sort_unstable_by(|a, b| a.0.cmp(&b.0));
classes_by_lowercase_name
}
impl WorkspaceIndexData {
pub fn at(&self, r: ClassRef) -> Option<(&Uri, &crate::index::file_index::ClassDef)> {
let (uri, idx) = self.files.get(r.file as usize)?;
let cls = idx.classes.get(r.class as usize)?;
Some((uri, cls))
}
pub fn function_at(
&self,
r: FunctionRef,
) -> Option<(&Uri, &crate::index::file_index::FunctionDef)> {
let (uri, idx) = self.files.get(r.file as usize)?;
let function = idx.functions.get(r.function as usize)?;
Some((uri, function))
}
pub fn unique_class_fqn_by_short_name(&self, name: &str) -> Option<&str> {
let mut candidate = None;
for (_, index) in &self.files {
for class in &index.classes {
if !class.name.eq_ignore_ascii_case(name) {
continue;
}
match candidate {
None => candidate = Some(class.fqn.as_ref()),
Some(fqn) if fqn.eq_ignore_ascii_case(&class.fqn) => {}
Some(_) => return None,
}
}
}
candidate
}
pub fn for_each_class(&self, mut f: impl FnMut(&Uri, &crate::index::file_index::ClassDef)) {
for (uri, idx) in &self.files {
for cls in &idx.classes {
f(uri, cls);
}
}
}
pub fn for_each_class_in_uris(
&self,
uris: &[Uri],
mut f: impl FnMut(&Uri, &crate::index::file_index::ClassDef),
) {
for uri in uris {
let Some(&file_idx) = self.path_to_file_idx.get(uri.as_str()) else {
continue;
};
let Some((stored_uri, idx)) = self.files.get(file_idx as usize) else {
continue;
};
for cls in &idx.classes {
f(stored_uri, cls);
}
}
}
pub fn from_files(files: Vec<(Uri, Arc<FileIndex>)>) -> Self {
let classes_by_lowercase_name = build_maps(&files);
let file_paths: Vec<Arc<str>> = files.iter().map(|(u, _)| Arc::from(u.as_str())).collect();
let path_to_file_idx = file_paths
.iter()
.enumerate()
.map(|(i, p)| (Arc::clone(p), i as u32))
.collect();
Self {
files,
file_paths,
path_to_file_idx,
classes_by_lowercase_name,
}
}
}
#[derive(Clone)]
pub struct WorkspaceIndexArc(pub Arc<WorkspaceIndexData>);
impl WorkspaceIndexArc {
#[cfg(test)]
pub fn get(&self) -> &WorkspaceIndexData {
&self.0
}
}