diff --git a/crates/libanalysis/src/db/imp.rs b/crates/libanalysis/src/db/imp.rs index 1b4ee5cf31..f26be10463 100644 --- a/crates/libanalysis/src/db/imp.rs +++ b/crates/libanalysis/src/db/imp.rs @@ -122,9 +122,6 @@ impl QueryRegistry { let config = config.with_query(id, q.f()); self.config= Some(config); } - pub(super) fn finish(mut self) -> salsa::QueryConfig { - self.config.take().unwrap() - } } fn hash(x: &T) -> u64 { diff --git a/crates/libanalysis/src/db/mod.rs b/crates/libanalysis/src/db/mod.rs index 1111a4f874..22769d1120 100644 --- a/crates/libanalysis/src/db/mod.rs +++ b/crates/libanalysis/src/db/mod.rs @@ -29,6 +29,12 @@ pub(crate) struct QueryRegistry { imp: imp::QueryRegistry, } +impl Default for Db { + fn default() -> Db { + Db::new() + } +} + impl Db { pub(crate) fn new() -> Db { let reg = QueryRegistry::new(); @@ -44,6 +50,7 @@ impl Db { let ctx = QueryCtx { imp: &self.imp.imp.query_ctx() }; f(ctx) } + #[allow(unused)] pub(crate) fn trace_query R, R>(&self, f: F) -> (R, Vec<&'static str>) { let ctx = QueryCtx { imp: &self.imp.imp.query_ctx() }; let res = f(ctx); @@ -65,42 +72,11 @@ pub(crate) fn file_text(ctx: QueryCtx, file_id: FileId) -> Arc { pub(crate) fn file_set(ctx: QueryCtx) -> Arc<(Vec, FileResolverImp)> { imp::file_set(ctx) } -pub(crate) use self::queries::file_syntax; - -mod queries { - use std::sync::Arc; - use libsyntax2::File; - use libeditor::LineIndex; - use {FileId}; - use super::{Query, QueryCtx, QueryRegistry, file_text}; - - pub(crate) fn register_queries(reg: &mut QueryRegistry) { - reg.add(FILE_SYNTAX, "FILE_SYNTAX"); - reg.add(FILE_LINES, "FILE_LINES"); - } - - pub(crate) fn file_syntax(ctx: QueryCtx, file_id: FileId) -> File { - (&*ctx.get(FILE_SYNTAX, file_id)).clone() - } - pub(crate) fn file_lines(ctx: QueryCtx, file_id: FileId) -> Arc { - ctx.get(FILE_LINES, file_id) - } - - pub(super) const FILE_SYNTAX: Query = Query(16, |ctx, file_id: &FileId| { - let text = file_text(ctx, *file_id); - File::parse(&*text) - }); - pub(super) const FILE_LINES: Query = Query(17, |ctx, file_id: &FileId| { - let text = file_text(ctx, *file_id); - LineIndex::new(&*text) - }); -} - impl QueryRegistry { fn new() -> QueryRegistry { let mut reg = QueryRegistry { imp: imp::QueryRegistry::new() }; - queries::register_queries(&mut reg); - ::module_map_db::register_queries(&mut reg); + ::queries::register_queries(&mut reg); + ::module_map::register_queries(&mut reg); reg } pub(crate) fn add(&mut self, q: Q, name: &'static str) { diff --git a/crates/libanalysis/src/descriptors.rs b/crates/libanalysis/src/descriptors.rs index 200f21576b..93a4158e44 100644 --- a/crates/libanalysis/src/descriptors.rs +++ b/crates/libanalysis/src/descriptors.rs @@ -140,7 +140,7 @@ impl ModuleTreeDescriptor { .links .iter() .filter(|it| it.name(self) == name) - .map(|link| link.owner(self)) + .flat_map(|link| link.points_to(self).iter().map(|&node| self.node(node).file_id)) .collect() } pub(crate) fn problems<'a, 'b>(&'b self, file_id: FileId, root: ast::Root<'a>) -> Vec<(ast::Name<'a>, &'b Problem)> { @@ -172,6 +172,9 @@ impl Link { let owner = tree.link(self).owner; tree.node(owner).file_id } + fn points_to(self, tree: &ModuleTreeDescriptor) -> &[Node] { + &tree.link(self).points_to + } pub(crate) fn bind_source<'a>(self, tree: &ModuleTreeDescriptor, root: ast::Root<'a>) -> ast::Module<'a> { modules(root) .filter(|(name, _)| name == &tree.link(self).name) diff --git a/crates/libanalysis/src/imp.rs b/crates/libanalysis/src/imp.rs index 8734813f49..6f3191fe73 100644 --- a/crates/libanalysis/src/imp.rs +++ b/crates/libanalysis/src/imp.rs @@ -5,6 +5,7 @@ use std::{ }, fmt, collections::{HashSet, VecDeque}, + iter, }; use relative_path::RelativePath; @@ -75,14 +76,12 @@ impl AnalysisHostImpl { } pub fn change_files(&mut self, changes: &mut dyn Iterator)>) { let data = self.data_mut(); - for (file_id, text) in changes { - data.root.update(file_id, text); - } + data.root = Arc::new(data.root.apply_changes(changes, None)); } pub fn set_file_resolver(&mut self, resolver: FileResolverImp) { let data = self.data_mut(); data.file_resolver = resolver.clone(); - data.root.set_file_resolver(resolver); + data.root = Arc::new(data.root.apply_changes(&mut iter::empty(), Some(resolver))); } pub fn set_crate_graph(&mut self, graph: CrateGraph) { let mut visited = HashSet::new(); @@ -124,18 +123,17 @@ impl Clone for AnalysisImpl { impl AnalysisImpl { fn root(&self, file_id: FileId) -> &SourceRoot { if self.data.root.contains(file_id) { - return &self.data.root; + return &*self.data.root; } &**self.data.libs.iter().find(|it| it.contains(file_id)).unwrap() } - pub fn file_syntax(&self, file_id: FileId) -> &File { + pub fn file_syntax(&self, file_id: FileId) -> File { self.root(file_id).syntax(file_id) } - pub fn file_line_index(&self, file_id: FileId) -> &LineIndex { + pub fn file_line_index(&self, file_id: FileId) -> Arc { self.root(file_id).lines(file_id) } pub fn world_symbols(&self, query: Query, token: &JobToken) -> Vec<(FileId, FileSymbol)> { - self.reindex(); let mut buf = Vec::new(); if query.libs { self.data.libs.iter() @@ -308,19 +306,13 @@ impl AnalysisImpl { }; module_tree.child_module_by_name(file_id, name.as_str()) } - - fn reindex(&self) { - if self.needs_reindex.compare_and_swap(true, false, SeqCst) { - self.data.root.reindex(); - } - } } #[derive(Default, Clone, Debug)] struct WorldData { file_resolver: FileResolverImp, crate_graph: CrateGraph, - root: WritableSourceRoot, + root: Arc, libs: Vec>, } diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs index ea3a287022..b4b7a6a304 100644 --- a/crates/libanalysis/src/lib.rs +++ b/crates/libanalysis/src/lib.rs @@ -14,11 +14,11 @@ extern crate salsa; mod symbol_index; mod module_map; -pub(crate) mod module_map_db; mod imp; mod job; mod roots; mod db; +mod queries; mod descriptors; use std::{ @@ -166,8 +166,8 @@ impl Analysis { pub fn file_syntax(&self, file_id: FileId) -> File { self.imp.file_syntax(file_id).clone() } - pub fn file_line_index(&self, file_id: FileId) -> LineIndex { - self.imp.file_line_index(file_id).clone() + pub fn file_line_index(&self, file_id: FileId) -> Arc { + self.imp.file_line_index(file_id) } pub fn extend_selection(&self, file: &File, range: TextRange) -> TextRange { libeditor::extend_selection(file, range).unwrap_or(range) @@ -177,19 +177,19 @@ impl Analysis { } pub fn syntax_tree(&self, file_id: FileId) -> String { let file = self.imp.file_syntax(file_id); - libeditor::syntax_tree(file) + libeditor::syntax_tree(&file) } pub fn join_lines(&self, file_id: FileId, range: TextRange) -> SourceChange { let file = self.imp.file_syntax(file_id); - SourceChange::from_local_edit(file_id, "join lines", libeditor::join_lines(file, range)) + SourceChange::from_local_edit(file_id, "join lines", libeditor::join_lines(&file, range)) } pub fn on_eq_typed(&self, file_id: FileId, offset: TextUnit) -> Option { let file = self.imp.file_syntax(file_id); - Some(SourceChange::from_local_edit(file_id, "add semicolon", libeditor::on_eq_typed(file, offset)?)) + Some(SourceChange::from_local_edit(file_id, "add semicolon", libeditor::on_eq_typed(&file, offset)?)) } pub fn file_structure(&self, file_id: FileId) -> Vec { let file = self.imp.file_syntax(file_id); - libeditor::file_structure(file) + libeditor::file_structure(&file) } pub fn symbol_search(&self, query: Query, token: &JobToken) -> Vec<(FileId, FileSymbol)> { self.imp.world_symbols(query, token) @@ -208,15 +208,15 @@ impl Analysis { } pub fn runnables(&self, file_id: FileId) -> Vec { let file = self.imp.file_syntax(file_id); - libeditor::runnables(file) + libeditor::runnables(&file) } pub fn highlight(&self, file_id: FileId) -> Vec { let file = self.imp.file_syntax(file_id); - libeditor::highlight(file) + libeditor::highlight(&file) } pub fn completions(&self, file_id: FileId, offset: TextUnit) -> Option> { let file = self.imp.file_syntax(file_id); - libeditor::scope_completion(file, offset) + libeditor::scope_completion(&file, offset) } pub fn assists(&self, file_id: FileId, range: TextRange) -> Vec { self.imp.assists(file_id, range) diff --git a/crates/libanalysis/src/module_map.rs b/crates/libanalysis/src/module_map.rs index 79b88cac2f..a21f55fff1 100644 --- a/crates/libanalysis/src/module_map.rs +++ b/crates/libanalysis/src/module_map.rs @@ -1,281 +1,157 @@ -use relative_path::RelativePathBuf; -use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; -use libsyntax2::{ - File, - ast::{self, AstNode, NameOwner}, - SyntaxNode, SmolStr, -}; -use {FileId, imp::FileResolverImp}; - -type SyntaxProvider<'a> = dyn Fn(FileId) -> &'a File + 'a; - -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] -pub struct ModuleId(FileId); - -#[derive(Debug, Default)] -pub struct ModuleMap { - state: RwLock, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ChangeKind { - Delete, Insert, Update -} - -impl Clone for ModuleMap { - fn clone(&self) -> ModuleMap { - let state = self.state.read().clone(); - ModuleMap { state: RwLock::new(state) } - } -} - -#[derive(Clone, Debug, Default)] -struct State { - file_resolver: FileResolverImp, - changes: Vec<(FileId, ChangeKind)>, - links: Vec, -} - -#[derive(Clone, Debug)] -struct Link { - owner: ModuleId, - syntax: SyntaxNode, - points_to: Vec, - problem: Option, -} - -#[derive(Clone, Debug)] -pub enum Problem { - UnresolvedModule { - candidate: RelativePathBuf, +use std::sync::Arc; +use { + FileId, + db::{ + Query, QueryRegistry, QueryCtx, + file_set }, - NotDirOwner { - move_to: RelativePathBuf, - candidate: RelativePathBuf, - } + queries::file_syntax, + descriptors::{ModuleDescriptor, ModuleTreeDescriptor}, +}; + +pub(crate) fn register_queries(reg: &mut QueryRegistry) { + reg.add(MODULE_DESCR, "MODULE_DESCR"); + reg.add(MODULE_TREE, "MODULE_TREE"); } -impl ModuleMap { - pub fn new() -> ModuleMap { - Default::default() +pub(crate) fn module_tree(ctx: QueryCtx) -> Arc { + ctx.get(MODULE_TREE, ()) +} + +const MODULE_DESCR: Query = Query(30, |ctx, &file_id| { + let file = file_syntax(ctx, file_id); + ModuleDescriptor::new(file.ast()) +}); + +const MODULE_TREE: Query<(), ModuleTreeDescriptor> = Query(31, |ctx, _| { + let file_set = file_set(ctx); + let mut files = Vec::new(); + for &file_id in file_set.0.iter() { + let module_descr = ctx.get(MODULE_DESCR, file_id); + files.push((file_id, module_descr)); } - pub fn update_file(&mut self, file_id: FileId, change_kind: ChangeKind) { - self.state.get_mut().changes.push((file_id, change_kind)); - } - pub(crate) fn set_file_resolver(&mut self, file_resolver: FileResolverImp) { - self.state.get_mut().file_resolver = file_resolver; - } - pub fn module2file(&self, m: ModuleId) -> FileId { - m.0 - } - pub fn file2module(&self, file_id: FileId) -> ModuleId { - ModuleId(file_id) - } - pub fn child_module_by_name<'a>( - &self, - parent_mod: ModuleId, - child_mod: &str, - syntax_provider: &SyntaxProvider, - ) -> Vec { - self.links(syntax_provider) - .links - .iter() - .filter(|link| link.owner == parent_mod) - .filter(|link| link.name() == child_mod) - .filter_map(|it| it.points_to.first()) - .map(|&it| it) - .collect() + ModuleTreeDescriptor::new(files.iter().map(|(file_id, descr)| (*file_id, &**descr)), &file_set.1) +}); + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + use im; + use relative_path::{RelativePath, RelativePathBuf}; + use { + db::{Db}, + imp::FileResolverImp, + FileId, FileResolver, + }; + use super::*; + + #[derive(Debug)] + struct FileMap(im::HashMap); + + impl FileResolver for FileMap { + fn file_stem(&self, file_id: FileId) -> String { + self.0[&file_id].file_stem().unwrap().to_string() + } + fn resolve(&self, file_id: FileId, rel: &RelativePath) -> Option { + let path = self.0[&file_id].join(rel).normalize(); + self.0.iter() + .filter_map(|&(id, ref p)| Some(id).filter(|_| p == &path)) + .next() + } } - pub fn parent_modules( - &self, - m: ModuleId, - syntax_provider: &SyntaxProvider, - ) -> Vec<(ModuleId, SmolStr, SyntaxNode)> { - let mut res = Vec::new(); - self.for_each_parent_link(m, syntax_provider, |link| { - res.push( - (link.owner, link.name().clone(), link.syntax.clone()) - ) - }); - res + struct Fixture { + next_file_id: u32, + fm: im::HashMap, + db: Db, } - pub fn parent_module_ids( - &self, - m: ModuleId, - syntax_provider: &SyntaxProvider, - ) -> Vec { - let mut res = Vec::new(); - self.for_each_parent_link(m, syntax_provider, |link| res.push(link.owner)); - res - } - - fn for_each_parent_link( - &self, - m: ModuleId, - syntax_provider: &SyntaxProvider, - f: impl FnMut(&Link) - ) { - self.links(syntax_provider) - .links - .iter() - .filter(move |link| link.points_to.iter().any(|&it| it == m)) - .for_each(f) - } - - pub fn problems( - &self, - file: FileId, - syntax_provider: &SyntaxProvider, - mut cb: impl FnMut(ast::Name, &Problem), - ) { - let module = self.file2module(file); - let links = self.links(syntax_provider); - links - .links - .iter() - .filter(|link| link.owner == module) - .filter_map(|link| { - let problem = link.problem.as_ref()?; - Some((link, problem)) - }) - .for_each(|(link, problem)| cb(link.name_node(), problem)) - } - - fn links( - &self, - syntax_provider: &SyntaxProvider, - ) -> RwLockReadGuard { - { - let guard = self.state.read(); - if guard.changes.is_empty() { - return guard; + impl Fixture { + fn new() -> Fixture { + Fixture { + next_file_id: 1, + fm: im::HashMap::new(), + db: Db::new(), } } - let mut guard = self.state.write(); - if !guard.changes.is_empty() { - guard.apply_changes(syntax_provider); + fn add_file(&mut self, path: &str, text: &str) -> FileId { + assert!(path.starts_with("/")); + let file_id = FileId(self.next_file_id); + self.next_file_id += 1; + self.fm.insert(file_id, RelativePathBuf::from(&path[1..])); + let mut new_state = self.db.state().clone(); + new_state.file_map.insert(file_id, Arc::new(text.to_string())); + new_state.file_resolver = FileResolverImp::new( + Arc::new(FileMap(self.fm.clone())) + ); + self.db = self.db.with_changes(new_state, &[file_id], true); + file_id } - assert!(guard.changes.is_empty()); - RwLockWriteGuard::downgrade(guard) - } -} - -impl State { - pub fn apply_changes( - &mut self, - syntax_provider: &SyntaxProvider, - ) { - let mut reresolve = false; - for (file_id, kind) in self.changes.drain(..) { - let mod_id = ModuleId(file_id); - self.links.retain(|link| link.owner != mod_id); - match kind { - ChangeKind::Delete => { - for link in self.links.iter_mut() { - link.points_to.retain(|&x| x != mod_id); - } - } - ChangeKind::Insert => { - let file = syntax_provider(file_id); - self.links.extend( - file - .ast() - .modules() - .filter_map(|it| Link::new(mod_id, it)) - ); - reresolve = true; - } - ChangeKind::Update => { - let file = syntax_provider(file_id); - let resolver = &self.file_resolver; - self.links.extend( - file - .ast() - .modules() - .filter_map(|it| Link::new(mod_id, it)) - .map(|mut link| { - link.resolve(resolver); - link - }) - ); - } - } - } - if reresolve { - for link in self.links.iter_mut() { - link.resolve(&self.file_resolver) + fn remove_file(&mut self, file_id: FileId) { + self.fm.remove(&file_id); + let mut new_state = self.db.state().clone(); + new_state.file_map.remove(&file_id); + new_state.file_resolver = FileResolverImp::new( + Arc::new(FileMap(self.fm.clone())) + ); + self.db = self.db.with_changes(new_state, &[file_id], true); + } + fn change_file(&mut self, file_id: FileId, new_text: &str) { + let mut new_state = self.db.state().clone(); + new_state.file_map.insert(file_id, Arc::new(new_text.to_string())); + self.db = self.db.with_changes(new_state, &[file_id], false); + } + fn check_parent_modules( + &self, + file_id: FileId, + expected: &[FileId], + queries: &[(&'static str, u64)] + ) { + let (tree, events) = self.db.trace_query(|ctx| module_tree(ctx)); + let actual = tree.parent_modules(file_id) + .into_iter() + .map(|link| link.owner(&tree)) + .collect::>(); + assert_eq!(actual.as_slice(), expected); + let mut counts = HashMap::new(); + events.into_iter() + .for_each(|event| *counts.entry(event).or_insert(0) += 1); + for &(query_id, expected_count) in queries.iter() { + let actual_count = *counts.get(&query_id).unwrap_or(&0); + assert_eq!( + actual_count, + expected_count, + "counts for {} differ", + query_id, + ) } + } } + + #[test] + fn test_parent_module() { + let mut f = Fixture::new(); + let foo = f.add_file("/foo.rs", ""); + f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 1)]); + + let lib = f.add_file("/lib.rs", "mod foo;"); + f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 1)]); + f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 0)]); + + f.change_file(lib, ""); + f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 1)]); + + f.change_file(lib, "mod foo;"); + f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 1)]); + + f.change_file(lib, "mod bar;"); + f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 1)]); + + f.change_file(lib, "mod foo;"); + f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 1)]); + + f.remove_file(lib); + f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 0)]); + } } - -impl Link { - fn new(owner: ModuleId, module: ast::Module) -> Option { - if module.name().is_none() { - return None; - } - let link = Link { - owner, - syntax: module.syntax().owned(), - points_to: Vec::new(), - problem: None, - }; - Some(link) - } - - fn name(&self) -> SmolStr { - self.name_node().text() - } - - fn name_node(&self) -> ast::Name { - self.ast().name().unwrap() - } - - fn ast(&self) -> ast::Module { - ast::Module::cast(self.syntax.borrowed()) - .unwrap() - } - - fn resolve(&mut self, file_resolver: &FileResolverImp) { - if !self.ast().has_semi() { - self.problem = None; - self.points_to = Vec::new(); - return; - } - let (points_to, problem) = resolve_submodule(self.owner.0, &self.name(), file_resolver); - self.problem = problem; - self.points_to = points_to.into_iter().map(ModuleId).collect(); - } -} - -pub(crate) fn resolve_submodule(file_id: FileId, name: &SmolStr, file_resolver: &FileResolverImp) -> (Vec, Option) { - let mod_name = file_resolver.file_stem(file_id); - let is_dir_owner = - mod_name == "mod" || mod_name == "lib" || mod_name == "main"; - - let file_mod = RelativePathBuf::from(format!("../{}.rs", name)); - let dir_mod = RelativePathBuf::from(format!("../{}/mod.rs", name)); - let points_to: Vec; - let problem: Option; - if is_dir_owner { - points_to = [&file_mod, &dir_mod].iter() - .filter_map(|path| file_resolver.resolve(file_id, path)) - .collect(); - problem = if points_to.is_empty() { - Some(Problem::UnresolvedModule { - candidate: file_mod, - }) - } else { - None - } - } else { - points_to = Vec::new(); - problem = Some(Problem::NotDirOwner { - move_to: RelativePathBuf::from(format!("../{}/mod.rs", mod_name)), - candidate: file_mod, - }); - } - (points_to, problem) -} diff --git a/crates/libanalysis/src/module_map_db/mod.rs b/crates/libanalysis/src/module_map_db/mod.rs deleted file mode 100644 index adad943da5..0000000000 --- a/crates/libanalysis/src/module_map_db/mod.rs +++ /dev/null @@ -1,159 +0,0 @@ -use std::sync::Arc; -use { - FileId, - db::{ - Query, QueryRegistry, QueryCtx, - file_syntax, file_set - }, - descriptors::{ModuleDescriptor, ModuleTreeDescriptor} -}; - -pub(crate) fn register_queries(reg: &mut QueryRegistry) { - reg.add(MODULE_DESCR, "MODULE_DESCR"); -} - -pub(crate) fn module_tree(ctx: QueryCtx) -> Arc { - ctx.get(MODULE_TREE, ()) -} - -impl<'a> QueryCtx<'a> { - fn module_descr(&self, file_id: FileId) -> Arc { - self.get(MODULE_DESCR, file_id) - } -} - -const MODULE_DESCR: Query = Query(30, |ctx, &file_id| { - let file = file_syntax(ctx, file_id); - ModuleDescriptor::new(file.ast()) -}); - -const MODULE_TREE: Query<(), ModuleTreeDescriptor> = Query(31, |ctx, _| { - let file_set = file_set(ctx); - let mut files = Vec::new(); - for &file_id in file_set.0.iter() { - let module_descr = ctx.get(MODULE_DESCR, file_id); - files.push((file_id, module_descr)); - } - ModuleTreeDescriptor::new(files.iter().map(|(file_id, descr)| (*file_id, &**descr)), &file_set.1) -}); - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - use im; - use relative_path::{RelativePath, RelativePathBuf}; - use { - db::{Query, Db, State}, - imp::FileResolverImp, - FileId, FileResolver, - }; - use super::*; - - #[derive(Debug)] - struct FileMap(im::HashMap); - - impl FileResolver for FileMap { - fn file_stem(&self, file_id: FileId) -> String { - self.0[&file_id].file_stem().unwrap().to_string() - } - fn resolve(&self, file_id: FileId, rel: &RelativePath) -> Option { - let path = self.0[&file_id].join(rel).normalize(); - self.0.iter() - .filter_map(|&(id, ref p)| Some(id).filter(|_| p == &path)) - .next() - } - } - - struct Fixture { - next_file_id: u32, - fm: im::HashMap, - db: Db, - } - - impl Fixture { - fn new() -> Fixture { - Fixture { - next_file_id: 1, - fm: im::HashMap::new(), - db: Db::new(), - } - } - fn add_file(&mut self, path: &str, text: &str) -> FileId { - assert!(path.starts_with("/")); - let file_id = FileId(self.next_file_id); - self.next_file_id += 1; - self.fm.insert(file_id, RelativePathBuf::from(&path[1..])); - let mut new_state = self.db.state().clone(); - new_state.file_map.insert(file_id, Arc::new(text.to_string())); - new_state.file_resolver = FileResolverImp::new( - Arc::new(FileMap(self.fm.clone())) - ); - self.db = self.db.with_changes(new_state, &[file_id], true); - file_id - } - fn remove_file(&mut self, file_id: FileId) { - self.fm.remove(&file_id); - let mut new_state = self.db.state().clone(); - new_state.file_map.remove(&file_id); - new_state.file_resolver = FileResolverImp::new( - Arc::new(FileMap(self.fm.clone())) - ); - self.db = self.db.with_changes(new_state, &[file_id], true); - } - fn change_file(&mut self, file_id: FileId, new_text: &str) { - let mut new_state = self.db.state().clone(); - new_state.file_map.insert(file_id, Arc::new(new_text.to_string())); - self.db = self.db.with_changes(new_state, &[file_id], false); - } - fn check_parent_modules( - &self, - file_id: FileId, - expected: &[FileId], - queries: &[(&'static str, u64)] - ) { - let (actual, events) = self.db.trace_query(|ctx| { - ctx.get(PARENT_MODULE, file_id) - }); - assert_eq!(actual.as_slice(), expected); - let mut counts = HashMap::new(); - events.into_iter() - .for_each(|event| *counts.entry(event).or_insert(0) += 1); - for &(query_id, expected_count) in queries.iter() { - let actual_count = *counts.get(&query_id).unwrap_or(&0); - assert_eq!( - actual_count, - expected_count, - "counts for {} differ", - query_id, - ) - } - - } - } - - #[test] - fn test_parent_module() { - let mut f = Fixture::new(); - let foo = f.add_file("/foo.rs", ""); - f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 1)]); - - let lib = f.add_file("/lib.rs", "mod foo;"); - f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 1)]); - f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 0)]); - - f.change_file(lib, ""); - f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 1)]); - - f.change_file(lib, "mod foo;"); - f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 1)]); - - f.change_file(lib, "mod bar;"); - f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 1)]); - - f.change_file(lib, "mod foo;"); - f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 1)]); - - f.remove_file(lib); - f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 0)]); - } -} diff --git a/crates/libanalysis/src/queries.rs b/crates/libanalysis/src/queries.rs new file mode 100644 index 0000000000..9d6754fd47 --- /dev/null +++ b/crates/libanalysis/src/queries.rs @@ -0,0 +1,28 @@ +use std::sync::Arc; +use libsyntax2::File; +use libeditor::LineIndex; +use { + FileId, + db::{Query, QueryCtx, QueryRegistry, file_text}, +}; + +pub(crate) fn register_queries(reg: &mut QueryRegistry) { + reg.add(FILE_SYNTAX, "FILE_SYNTAX"); + reg.add(FILE_LINES, "FILE_LINES"); +} + +pub(crate) fn file_syntax(ctx: QueryCtx, file_id: FileId) -> File { + (&*ctx.get(FILE_SYNTAX, file_id)).clone() +} +pub(crate) fn file_lines(ctx: QueryCtx, file_id: FileId) -> Arc { + ctx.get(FILE_LINES, file_id) +} + +const FILE_SYNTAX: Query = Query(16, |ctx, file_id: &FileId| { + let text = file_text(ctx, *file_id); + File::parse(&*text) +}); +const FILE_LINES: Query = Query(17, |ctx, file_id: &FileId| { + let text = file_text(ctx, *file_id); + LineIndex::new(&*text) +}); diff --git a/crates/libanalysis/src/roots.rs b/crates/libanalysis/src/roots.rs index e9527eeff4..0e7253ba2f 100644 --- a/crates/libanalysis/src/roots.rs +++ b/crates/libanalysis/src/roots.rs @@ -1,6 +1,5 @@ use std::{ collections::HashMap, - time::Instant, sync::Arc, panic, }; @@ -13,83 +12,74 @@ use libsyntax2::File; use { FileId, imp::FileResolverImp, - module_map::{ModuleMap, ChangeKind}, symbol_index::SymbolIndex, descriptors::{ModuleDescriptor, ModuleTreeDescriptor}, + db::Db, }; pub(crate) trait SourceRoot { fn contains(&self, file_id: FileId) -> bool; - fn module_tree(&self) -> Arc { - unimplemented!() - } - // fn module_map(&self) -> &ModuleMap; - fn lines(&self, file_id: FileId) -> &LineIndex; - fn syntax(&self, file_id: FileId) -> &File; + fn module_tree(&self) -> Arc; + fn lines(&self, file_id: FileId) -> Arc; + fn syntax(&self, file_id: FileId) -> File; fn symbols<'a>(&'a self, acc: &mut Vec<&'a SymbolIndex>); } -#[derive(Clone, Default, Debug)] +#[derive(Default, Debug)] pub(crate) struct WritableSourceRoot { - file_map: HashMap)>>, - module_map: ModuleMap, + db: Db, } impl WritableSourceRoot { - pub fn update(&mut self, file_id: FileId, text: Option) { - let change_kind = if self.file_map.remove(&file_id).is_some() { - if text.is_some() { - ChangeKind::Update - } else { - ChangeKind::Delete - } - } else { - ChangeKind::Insert - }; - self.module_map.update_file(file_id, change_kind); - self.file_map.remove(&file_id); - if let Some(text) = text { - let file_data = FileData::new(text); - self.file_map.insert(file_id, Arc::new((file_data, Default::default()))); - } - } - pub fn set_file_resolver(&mut self, file_resolver: FileResolverImp) { - self.module_map.set_file_resolver(file_resolver) - } - pub fn reindex(&self) { - let now = Instant::now(); - self.file_map - .par_iter() - .for_each(|(&file_id, data)| { - symbols(file_id, data); - }); - info!("parallel indexing took {:?}", now.elapsed()); + 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(); - } - fn data(&self, file_id: FileId) -> &FileData { - match self.file_map.get(&file_id) { - Some(data) => &data.0, - None => panic!("unknown file: {:?}", file_id), + 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(::module_map::module_tree) + } + fn contains(&self, file_id: FileId) -> bool { - self.file_map.contains_key(&file_id) + self.db.state().file_map.contains_key(&file_id) } - fn lines(&self, file_id: FileId) -> &LineIndex { - self.data(file_id).lines() + fn lines(&self, file_id: FileId) -> Arc { + self.db.make_query(|ctx| ::queries::file_lines(ctx, file_id)) } - fn syntax(&self, file_id: FileId) -> &File { - self.data(file_id).syntax() + fn syntax(&self, file_id: FileId) -> File { + self.db.make_query(|ctx| ::queries::file_syntax(ctx, file_id)) } fn symbols<'a>(&'a self, acc: &mut Vec<&'a SymbolIndex>) { - acc.extend( - self.file_map - .iter() - .map(|(&file_id, data)| symbols(file_id, data)) - ) + // acc.extend( + // self.file_map + // .iter() + // .map(|(&file_id, data)| symbols(file_id, data)) + // ) } } @@ -101,7 +91,7 @@ fn symbols(file_id: FileId, (data, symbols): &(FileData, OnceCell)) #[derive(Debug)] struct FileData { text: String, - lines: OnceCell, + lines: OnceCell>, syntax: OnceCell, } @@ -113,8 +103,8 @@ impl FileData { lines: OnceCell::new(), } } - fn lines(&self) -> &LineIndex { - self.lines.get_or_init(|| LineIndex::new(&self.text)) + fn lines(&self) -> &Arc { + self.lines.get_or_init(|| Arc::new(LineIndex::new(&self.text))) } fn syntax(&self) -> &File { let text = &self.text; @@ -127,10 +117,6 @@ impl FileData { } } } - fn syntax_transient(&self) -> File { - self.syntax.get().map(|s| s.clone()) - .unwrap_or_else(|| File::parse(&self.text)) - } } #[derive(Debug)] @@ -184,11 +170,11 @@ impl SourceRoot for ReadonlySourceRoot { fn contains(&self, file_id: FileId) -> bool { self.file_map.contains_key(&file_id) } - fn lines(&self, file_id: FileId) -> &LineIndex { - self.data(file_id).lines() + 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() + fn syntax(&self, file_id: FileId) -> File { + self.data(file_id).syntax().clone() } fn symbols<'a>(&'a self, acc: &mut Vec<&'a SymbolIndex>) { acc.push(&self.symbol_index)