use std::{ sync::Arc, panic, }; use once_cell::sync::OnceCell; use rayon::prelude::*; use rustc_hash::FxHashMap; use ra_editor::LineIndex; use ra_syntax::File; use crate::{ FileId, imp::FileResolverImp, symbol_index::SymbolIndex, descriptors::{ModuleDescriptor, ModuleTreeDescriptor}, db::Db, }; pub(crate) trait SourceRoot { fn contains(&self, file_id: FileId) -> bool; fn module_tree(&self) -> Arc; fn lines(&self, file_id: FileId) -> Arc; fn syntax(&self, file_id: FileId) -> File; fn symbols(&self, acc: &mut Vec>); } #[derive(Default, Debug)] pub(crate) struct WritableSourceRoot { db: Db, } impl WritableSourceRoot { pub fn apply_changes( &self, changes: &mut dyn Iterator)>, file_resolver: Option, ) -> WritableSourceRoot { let resolver_changed = file_resolver.is_some(); let mut changed_files = Vec::new(); let mut new_state = self.db.state().clone(); for (file_id, text) in changes { changed_files.push(file_id); match text { Some(text) => { new_state.file_map.insert(file_id, Arc::new(text)); }, None => { new_state.file_map.remove(&file_id); } } } if let Some(file_resolver) = file_resolver { new_state.file_resolver = file_resolver } WritableSourceRoot { db: self.db.with_changes(new_state, &changed_files, resolver_changed) } } } impl SourceRoot for WritableSourceRoot { fn module_tree(&self) -> Arc { self.db.make_query(crate::module_map::module_tree) } fn contains(&self, file_id: FileId) -> bool { self.db.state().file_map.contains_key(&file_id) } fn lines(&self, file_id: FileId) -> Arc { self.db.make_query(|ctx| crate::queries::file_lines(ctx, file_id)) } fn syntax(&self, file_id: FileId) -> File { self.db.make_query(|ctx| crate::queries::file_syntax(ctx, file_id)) } fn symbols<'a>(&'a self, acc: &mut Vec>) { self.db.make_query(|ctx| { let file_set = crate::queries::file_set(ctx); let syms = file_set.0.iter() .map(|file_id| crate::queries::file_symbols(ctx, *file_id)); acc.extend(syms); }); } } #[derive(Debug)] struct FileData { text: String, lines: OnceCell>, syntax: OnceCell, } impl FileData { fn new(text: String) -> FileData { FileData { text, syntax: OnceCell::new(), lines: OnceCell::new(), } } fn lines(&self) -> &Arc { self.lines.get_or_init(|| Arc::new(LineIndex::new(&self.text))) } fn syntax(&self) -> &File { let text = &self.text; let syntax = &self.syntax; match panic::catch_unwind(panic::AssertUnwindSafe(|| syntax.get_or_init(|| File::parse(text)))) { Ok(file) => file, Err(err) => { error!("Parser paniced on:\n------\n{}\n------\n", text); panic::resume_unwind(err) } } } } #[derive(Debug)] pub(crate) struct ReadonlySourceRoot { symbol_index: Arc, file_map: FxHashMap, module_tree: Arc, } impl ReadonlySourceRoot { pub(crate) fn new(files: Vec<(FileId, String)>, file_resolver: FileResolverImp) -> ReadonlySourceRoot { let modules = files.par_iter() .map(|(file_id, text)| { let syntax = File::parse(text); let mod_descr = ModuleDescriptor::new(syntax.ast()); (*file_id, syntax, mod_descr) }) .collect::>(); let module_tree = ModuleTreeDescriptor::new( modules.iter().map(|it| (it.0, &it.2)), &file_resolver, ); let symbol_index = SymbolIndex::for_files( modules.par_iter().map(|it| (it.0, it.1.clone())) ); let file_map: FxHashMap = files .into_iter() .map(|(id, text)| (id, FileData::new(text))) .collect(); ReadonlySourceRoot { symbol_index: Arc::new(symbol_index), file_map, module_tree: Arc::new(module_tree), } } fn data(&self, file_id: FileId) -> &FileData { match self.file_map.get(&file_id) { Some(data) => data, None => panic!("unknown file: {:?}", file_id), } } } impl SourceRoot for ReadonlySourceRoot { fn module_tree(&self) -> Arc { Arc::clone(&self.module_tree) } fn contains(&self, file_id: FileId) -> bool { self.file_map.contains_key(&file_id) } fn lines(&self, file_id: FileId) -> Arc { Arc::clone(self.data(file_id).lines()) } fn syntax(&self, file_id: FileId) -> File { self.data(file_id).syntax().clone() } fn symbols(&self, acc: &mut Vec>) { acc.push(Arc::clone(&self.symbol_index)) } }