use std::sync::Arc;
use mir_types::Name;
use rustc_hash::FxHashMap;
use crate::db::{collect_file_definitions, MirDatabase, SourceFile};
#[salsa::input]
pub struct WorkspaceRevision {
pub revision: u64,
}
#[salsa::tracked]
pub fn workspace_classes(db: &dyn MirDatabase) -> Arc<[Arc<str>]> {
let rev = db
.workspace_revision()
.expect("WorkspaceRevision not initialized");
let _ = rev.revision(db);
let files = db.all_source_files();
let mut out: Vec<Arc<str>> = Vec::new();
for file in files.iter() {
let defs = collect_file_definitions(db, *file);
for c in defs.slice.classes.iter() {
out.push(c.fqcn.clone());
}
for i in defs.slice.interfaces.iter() {
out.push(i.fqcn.clone());
}
for t in defs.slice.traits.iter() {
out.push(t.fqcn.clone());
}
for e in defs.slice.enums.iter() {
out.push(e.fqcn.clone());
}
}
Arc::from(out)
}
#[salsa::tracked]
pub fn workspace_functions(db: &dyn MirDatabase) -> Arc<[Arc<str>]> {
let rev = db
.workspace_revision()
.expect("WorkspaceRevision not initialized");
let _ = rev.revision(db);
let files = db.all_source_files();
let mut out: Vec<Arc<str>> = Vec::new();
for file in files.iter() {
let defs = collect_file_definitions(db, *file);
for f in defs.slice.functions.iter() {
out.push(f.fqn.clone());
}
}
Arc::from(out)
}
#[derive(Clone)]
pub struct FileDeclarations {
pub class_like: Vec<(Name, SymbolLoc)>,
pub functions: Vec<(Name, SymbolLoc)>,
pub constants: Vec<(Name, SymbolLoc)>,
}
impl PartialEq for FileDeclarations {
fn eq(&self, other: &Self) -> bool {
self.class_like.len() == other.class_like.len()
&& self
.class_like
.iter()
.zip(&other.class_like)
.all(|(a, b)| a.0 == b.0)
&& self.functions.len() == other.functions.len()
&& self
.functions
.iter()
.zip(&other.functions)
.all(|(a, b)| a.0 == b.0)
&& self.constants.len() == other.constants.len()
&& self
.constants
.iter()
.zip(&other.constants)
.all(|(a, b)| a.0 == b.0)
}
}
unsafe impl salsa::Update for FileDeclarations {
unsafe fn maybe_update(old_ptr: *mut Self, new_val: Self) -> bool {
let old = unsafe { &mut *old_ptr };
if *old == new_val {
return false;
}
*old = new_val;
true
}
}
#[salsa::tracked]
pub fn collect_file_declarations(db: &dyn MirDatabase, file: SourceFile) -> FileDeclarations {
let defs = collect_file_definitions(db, file);
let mut class_like = Vec::new();
let mut functions = Vec::new();
let mut constants = Vec::new();
for (idx, c) in defs.slice.classes.iter().enumerate() {
class_like.push((
Name::new(c.fqcn.as_ref()).ascii_lowercase(),
SymbolLoc::Class { file, idx },
));
}
for (idx, i) in defs.slice.interfaces.iter().enumerate() {
class_like.push((
Name::new(i.fqcn.as_ref()).ascii_lowercase(),
SymbolLoc::Interface { file, idx },
));
}
for (idx, t) in defs.slice.traits.iter().enumerate() {
class_like.push((
Name::new(t.fqcn.as_ref()).ascii_lowercase(),
SymbolLoc::Trait { file, idx },
));
}
for (idx, e) in defs.slice.enums.iter().enumerate() {
class_like.push((
Name::new(e.fqcn.as_ref()).ascii_lowercase(),
SymbolLoc::Enum { file, idx },
));
}
for (idx, f) in defs.slice.functions.iter().enumerate() {
functions.push((
Name::new(f.fqn.as_ref()).ascii_lowercase(),
SymbolLoc::Function { file, idx },
));
}
for (idx, (name, _)) in defs.slice.constants.iter().enumerate() {
constants.push((Name::new(name.as_ref()), SymbolLoc::Constant { file, idx }));
}
FileDeclarations {
class_like,
functions,
constants,
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum SymbolLoc {
Class { file: SourceFile, idx: usize },
Interface { file: SourceFile, idx: usize },
Trait { file: SourceFile, idx: usize },
Enum { file: SourceFile, idx: usize },
Function { file: SourceFile, idx: usize },
Constant { file: SourceFile, idx: usize },
}
impl SymbolLoc {
pub fn file(&self) -> SourceFile {
match self {
SymbolLoc::Class { file, .. }
| SymbolLoc::Interface { file, .. }
| SymbolLoc::Trait { file, .. }
| SymbolLoc::Enum { file, .. }
| SymbolLoc::Function { file, .. }
| SymbolLoc::Constant { file, .. } => *file,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum SymbolTier {
NativeStub = 0,
UserFile = 1,
UserStub = 2,
}
#[derive(Default, Clone)]
pub struct IndexDeclCounts {
pub class_like: FxHashMap<Name, u32>,
pub functions: FxHashMap<Name, u32>,
pub constants: FxHashMap<Name, u32>,
}
#[salsa::input]
pub struct WorkspaceSymbolIndexSingleton {
pub index: WorkspaceSymbolIndex,
pub revision: u64,
}
#[derive(Clone, Default)]
pub struct WorkspaceSymbolIndex {
pub class_like: Arc<FxHashMap<Name, SymbolLoc>>,
pub functions: Arc<FxHashMap<Name, SymbolLoc>>,
pub constants: Arc<FxHashMap<Name, SymbolLoc>>,
}
impl PartialEq for WorkspaceSymbolIndex {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.class_like, &other.class_like)
&& Arc::ptr_eq(&self.functions, &other.functions)
&& Arc::ptr_eq(&self.constants, &other.constants)
}
}
unsafe impl salsa::Update for WorkspaceSymbolIndex {
unsafe fn maybe_update(old_ptr: *mut Self, new_val: Self) -> bool {
let old = unsafe { &mut *old_ptr };
if *old == new_val {
return false;
}
*old = new_val;
true
}
}
pub fn workspace_index(db: &dyn MirDatabase) -> WorkspaceSymbolIndex {
if let Some(s) = db.workspace_symbol_index_singleton() {
s.index(db)
} else {
workspace_symbol_index(db)
}
}
#[salsa::tracked]
pub fn workspace_symbol_index(db: &dyn MirDatabase) -> WorkspaceSymbolIndex {
let rev = db
.workspace_revision()
.expect("WorkspaceRevision not initialized");
let _ = rev.revision(db);
let files = db.all_source_files();
let mut class_like: FxHashMap<Name, SymbolLoc> = FxHashMap::default();
let mut functions: FxHashMap<Name, SymbolLoc> = FxHashMap::default();
let mut constants: FxHashMap<Name, SymbolLoc> = FxHashMap::default();
let user_stub_set: std::collections::HashSet<_> =
db.user_stub_source_files().into_iter().collect();
let (native_stubs, user_files): (Vec<SourceFile>, Vec<SourceFile>) = files
.into_iter()
.partition(|f| f.path(db).starts_with("stubs/"));
for file in &native_stubs {
let decls = collect_file_declarations(db, *file);
for (key, loc) in &decls.class_like {
class_like.entry(*key).or_insert(*loc);
}
for (key, loc) in &decls.functions {
functions.entry(*key).or_insert(*loc);
}
for (key, loc) in &decls.constants {
constants.entry(*key).or_insert(*loc);
}
}
for file in &user_files {
if user_stub_set.contains(file) {
continue; }
let decls = collect_file_declarations(db, *file);
for (key, loc) in decls.class_like {
class_like.insert(key, loc);
}
for (key, loc) in decls.functions {
functions.insert(key, loc);
}
for (key, loc) in decls.constants {
constants.insert(key, loc);
}
}
for file in &user_stub_set {
let decls = collect_file_declarations(db, *file);
for (key, loc) in decls.class_like {
class_like.insert(key, loc);
}
for (key, loc) in decls.functions {
functions.insert(key, loc);
}
for (key, loc) in decls.constants {
constants.insert(key, loc);
}
}
WorkspaceSymbolIndex {
class_like: Arc::new(class_like),
functions: Arc::new(functions),
constants: Arc::new(constants),
}
}
#[derive(Clone, Default, Debug)]
pub struct GlobalVarMap(pub Arc<FxHashMap<Arc<str>, mir_types::Type>>);
impl PartialEq for GlobalVarMap {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.0, &other.0)
}
}
unsafe impl salsa::Update for GlobalVarMap {
unsafe fn maybe_update(old_ptr: *mut Self, new_val: Self) -> bool {
let old = unsafe { &mut *old_ptr };
if *old == new_val {
return false;
}
*old = new_val;
true
}
}
#[salsa::tracked]
pub fn workspace_global_vars(db: &dyn MirDatabase) -> GlobalVarMap {
let rev = db
.workspace_revision()
.expect("WorkspaceRevision not initialized");
let _ = rev.revision(db);
let files = db.all_source_files();
let mut out: FxHashMap<Arc<str>, mir_types::Type> = FxHashMap::default();
for file in files.iter() {
let defs = collect_file_definitions(db, *file);
for (name, ty) in &defs.slice.global_vars {
let gname: Arc<str> = Arc::from(name.strip_prefix('$').unwrap_or(name.as_ref()));
out.entry(gname).or_insert_with(|| ty.clone());
}
}
GlobalVarMap(Arc::new(out))
}