aboutsummaryrefslogtreecommitdiff
path: root/crates/libanalysis
diff options
context:
space:
mode:
Diffstat (limited to 'crates/libanalysis')
-rw-r--r--crates/libanalysis/src/db/imp.rs3
-rw-r--r--crates/libanalysis/src/db/mod.rs42
-rw-r--r--crates/libanalysis/src/descriptors.rs5
-rw-r--r--crates/libanalysis/src/imp.rs22
-rw-r--r--crates/libanalysis/src/lib.rs20
-rw-r--r--crates/libanalysis/src/module_map.rs386
-rw-r--r--crates/libanalysis/src/module_map_db/mod.rs159
-rw-r--r--crates/libanalysis/src/queries.rs28
-rw-r--r--crates/libanalysis/src/roots.rs114
9 files changed, 239 insertions, 540 deletions
diff --git a/crates/libanalysis/src/db/imp.rs b/crates/libanalysis/src/db/imp.rs
index 1b4ee5cf3..f26be1046 100644
--- a/crates/libanalysis/src/db/imp.rs
+++ b/crates/libanalysis/src/db/imp.rs
@@ -122,9 +122,6 @@ impl QueryRegistry {
122 let config = config.with_query(id, q.f()); 122 let config = config.with_query(id, q.f());
123 self.config= Some(config); 123 self.config= Some(config);
124 } 124 }
125 pub(super) fn finish(mut self) -> salsa::QueryConfig<State, Data> {
126 self.config.take().unwrap()
127 }
128} 125}
129 126
130fn hash<T: Hash>(x: &T) -> u64 { 127fn hash<T: Hash>(x: &T) -> u64 {
diff --git a/crates/libanalysis/src/db/mod.rs b/crates/libanalysis/src/db/mod.rs
index 1111a4f87..22769d112 100644
--- a/crates/libanalysis/src/db/mod.rs
+++ b/crates/libanalysis/src/db/mod.rs
@@ -29,6 +29,12 @@ pub(crate) struct QueryRegistry {
29 imp: imp::QueryRegistry, 29 imp: imp::QueryRegistry,
30} 30}
31 31
32impl Default for Db {
33 fn default() -> Db {
34 Db::new()
35 }
36}
37
32impl Db { 38impl Db {
33 pub(crate) fn new() -> Db { 39 pub(crate) fn new() -> Db {
34 let reg = QueryRegistry::new(); 40 let reg = QueryRegistry::new();
@@ -44,6 +50,7 @@ impl Db {
44 let ctx = QueryCtx { imp: &self.imp.imp.query_ctx() }; 50 let ctx = QueryCtx { imp: &self.imp.imp.query_ctx() };
45 f(ctx) 51 f(ctx)
46 } 52 }
53 #[allow(unused)]
47 pub(crate) fn trace_query<F: FnOnce(QueryCtx) -> R, R>(&self, f: F) -> (R, Vec<&'static str>) { 54 pub(crate) fn trace_query<F: FnOnce(QueryCtx) -> R, R>(&self, f: F) -> (R, Vec<&'static str>) {
48 let ctx = QueryCtx { imp: &self.imp.imp.query_ctx() }; 55 let ctx = QueryCtx { imp: &self.imp.imp.query_ctx() };
49 let res = f(ctx); 56 let res = f(ctx);
@@ -65,42 +72,11 @@ pub(crate) fn file_text(ctx: QueryCtx, file_id: FileId) -> Arc<String> {
65pub(crate) fn file_set(ctx: QueryCtx) -> Arc<(Vec<FileId>, FileResolverImp)> { 72pub(crate) fn file_set(ctx: QueryCtx) -> Arc<(Vec<FileId>, FileResolverImp)> {
66 imp::file_set(ctx) 73 imp::file_set(ctx)
67} 74}
68pub(crate) use self::queries::file_syntax;
69
70mod queries {
71 use std::sync::Arc;
72 use libsyntax2::File;
73 use libeditor::LineIndex;
74 use {FileId};
75 use super::{Query, QueryCtx, QueryRegistry, file_text};
76
77 pub(crate) fn register_queries(reg: &mut QueryRegistry) {
78 reg.add(FILE_SYNTAX, "FILE_SYNTAX");
79 reg.add(FILE_LINES, "FILE_LINES");
80 }
81
82 pub(crate) fn file_syntax(ctx: QueryCtx, file_id: FileId) -> File {
83 (&*ctx.get(FILE_SYNTAX, file_id)).clone()
84 }
85 pub(crate) fn file_lines(ctx: QueryCtx, file_id: FileId) -> Arc<LineIndex> {
86 ctx.get(FILE_LINES, file_id)
87 }
88
89 pub(super) const FILE_SYNTAX: Query<FileId, File> = Query(16, |ctx, file_id: &FileId| {
90 let text = file_text(ctx, *file_id);
91 File::parse(&*text)
92 });
93 pub(super) const FILE_LINES: Query<FileId, LineIndex> = Query(17, |ctx, file_id: &FileId| {
94 let text = file_text(ctx, *file_id);
95 LineIndex::new(&*text)
96 });
97}
98
99impl QueryRegistry { 75impl QueryRegistry {
100 fn new() -> QueryRegistry { 76 fn new() -> QueryRegistry {
101 let mut reg = QueryRegistry { imp: imp::QueryRegistry::new() }; 77 let mut reg = QueryRegistry { imp: imp::QueryRegistry::new() };
102 queries::register_queries(&mut reg); 78 ::queries::register_queries(&mut reg);
103 ::module_map_db::register_queries(&mut reg); 79 ::module_map::register_queries(&mut reg);
104 reg 80 reg
105 } 81 }
106 pub(crate) fn add<Q: imp::EvalQuery>(&mut self, q: Q, name: &'static str) { 82 pub(crate) fn add<Q: imp::EvalQuery>(&mut self, q: Q, name: &'static str) {
diff --git a/crates/libanalysis/src/descriptors.rs b/crates/libanalysis/src/descriptors.rs
index 200f21576..93a4158e4 100644
--- a/crates/libanalysis/src/descriptors.rs
+++ b/crates/libanalysis/src/descriptors.rs
@@ -140,7 +140,7 @@ impl ModuleTreeDescriptor {
140 .links 140 .links
141 .iter() 141 .iter()
142 .filter(|it| it.name(self) == name) 142 .filter(|it| it.name(self) == name)
143 .map(|link| link.owner(self)) 143 .flat_map(|link| link.points_to(self).iter().map(|&node| self.node(node).file_id))
144 .collect() 144 .collect()
145 } 145 }
146 pub(crate) fn problems<'a, 'b>(&'b self, file_id: FileId, root: ast::Root<'a>) -> Vec<(ast::Name<'a>, &'b Problem)> { 146 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 {
172 let owner = tree.link(self).owner; 172 let owner = tree.link(self).owner;
173 tree.node(owner).file_id 173 tree.node(owner).file_id
174 } 174 }
175 fn points_to(self, tree: &ModuleTreeDescriptor) -> &[Node] {
176 &tree.link(self).points_to
177 }
175 pub(crate) fn bind_source<'a>(self, tree: &ModuleTreeDescriptor, root: ast::Root<'a>) -> ast::Module<'a> { 178 pub(crate) fn bind_source<'a>(self, tree: &ModuleTreeDescriptor, root: ast::Root<'a>) -> ast::Module<'a> {
176 modules(root) 179 modules(root)
177 .filter(|(name, _)| name == &tree.link(self).name) 180 .filter(|(name, _)| name == &tree.link(self).name)
diff --git a/crates/libanalysis/src/imp.rs b/crates/libanalysis/src/imp.rs
index 8734813f4..6f3191fe7 100644
--- a/crates/libanalysis/src/imp.rs
+++ b/crates/libanalysis/src/imp.rs
@@ -5,6 +5,7 @@ use std::{
5 }, 5 },
6 fmt, 6 fmt,
7 collections::{HashSet, VecDeque}, 7 collections::{HashSet, VecDeque},
8 iter,
8}; 9};
9 10
10use relative_path::RelativePath; 11use relative_path::RelativePath;
@@ -75,14 +76,12 @@ impl AnalysisHostImpl {
75 } 76 }
76 pub fn change_files(&mut self, changes: &mut dyn Iterator<Item=(FileId, Option<String>)>) { 77 pub fn change_files(&mut self, changes: &mut dyn Iterator<Item=(FileId, Option<String>)>) {
77 let data = self.data_mut(); 78 let data = self.data_mut();
78 for (file_id, text) in changes { 79 data.root = Arc::new(data.root.apply_changes(changes, None));
79 data.root.update(file_id, text);
80 }
81 } 80 }
82 pub fn set_file_resolver(&mut self, resolver: FileResolverImp) { 81 pub fn set_file_resolver(&mut self, resolver: FileResolverImp) {
83 let data = self.data_mut(); 82 let data = self.data_mut();
84 data.file_resolver = resolver.clone(); 83 data.file_resolver = resolver.clone();
85 data.root.set_file_resolver(resolver); 84 data.root = Arc::new(data.root.apply_changes(&mut iter::empty(), Some(resolver)));
86 } 85 }
87 pub fn set_crate_graph(&mut self, graph: CrateGraph) { 86 pub fn set_crate_graph(&mut self, graph: CrateGraph) {
88 let mut visited = HashSet::new(); 87 let mut visited = HashSet::new();
@@ -124,18 +123,17 @@ impl Clone for AnalysisImpl {
124impl AnalysisImpl { 123impl AnalysisImpl {
125 fn root(&self, file_id: FileId) -> &SourceRoot { 124 fn root(&self, file_id: FileId) -> &SourceRoot {
126 if self.data.root.contains(file_id) { 125 if self.data.root.contains(file_id) {
127 return &self.data.root; 126 return &*self.data.root;
128 } 127 }
129 &**self.data.libs.iter().find(|it| it.contains(file_id)).unwrap() 128 &**self.data.libs.iter().find(|it| it.contains(file_id)).unwrap()
130 } 129 }
131 pub fn file_syntax(&self, file_id: FileId) -> &File { 130 pub fn file_syntax(&self, file_id: FileId) -> File {
132 self.root(file_id).syntax(file_id) 131 self.root(file_id).syntax(file_id)
133 } 132 }
134 pub fn file_line_index(&self, file_id: FileId) -> &LineIndex { 133 pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> {
135 self.root(file_id).lines(file_id) 134 self.root(file_id).lines(file_id)
136 } 135 }
137 pub fn world_symbols(&self, query: Query, token: &JobToken) -> Vec<(FileId, FileSymbol)> { 136 pub fn world_symbols(&self, query: Query, token: &JobToken) -> Vec<(FileId, FileSymbol)> {
138 self.reindex();
139 let mut buf = Vec::new(); 137 let mut buf = Vec::new();
140 if query.libs { 138 if query.libs {
141 self.data.libs.iter() 139 self.data.libs.iter()
@@ -308,19 +306,13 @@ impl AnalysisImpl {
308 }; 306 };
309 module_tree.child_module_by_name(file_id, name.as_str()) 307 module_tree.child_module_by_name(file_id, name.as_str())
310 } 308 }
311
312 fn reindex(&self) {
313 if self.needs_reindex.compare_and_swap(true, false, SeqCst) {
314 self.data.root.reindex();
315 }
316 }
317} 309}
318 310
319#[derive(Default, Clone, Debug)] 311#[derive(Default, Clone, Debug)]
320struct WorldData { 312struct WorldData {
321 file_resolver: FileResolverImp, 313 file_resolver: FileResolverImp,
322 crate_graph: CrateGraph, 314 crate_graph: CrateGraph,
323 root: WritableSourceRoot, 315 root: Arc<WritableSourceRoot>,
324 libs: Vec<Arc<ReadonlySourceRoot>>, 316 libs: Vec<Arc<ReadonlySourceRoot>>,
325} 317}
326 318
diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs
index ea3a28702..b4b7a6a30 100644
--- a/crates/libanalysis/src/lib.rs
+++ b/crates/libanalysis/src/lib.rs
@@ -14,11 +14,11 @@ extern crate salsa;
14 14
15mod symbol_index; 15mod symbol_index;
16mod module_map; 16mod module_map;
17pub(crate) mod module_map_db;
18mod imp; 17mod imp;
19mod job; 18mod job;
20mod roots; 19mod roots;
21mod db; 20mod db;
21mod queries;
22mod descriptors; 22mod descriptors;
23 23
24use std::{ 24use std::{
@@ -166,8 +166,8 @@ impl Analysis {
166 pub fn file_syntax(&self, file_id: FileId) -> File { 166 pub fn file_syntax(&self, file_id: FileId) -> File {
167 self.imp.file_syntax(file_id).clone() 167 self.imp.file_syntax(file_id).clone()
168 } 168 }
169 pub fn file_line_index(&self, file_id: FileId) -> LineIndex { 169 pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> {
170 self.imp.file_line_index(file_id).clone() 170 self.imp.file_line_index(file_id)
171 } 171 }
172 pub fn extend_selection(&self, file: &File, range: TextRange) -> TextRange { 172 pub fn extend_selection(&self, file: &File, range: TextRange) -> TextRange {
173 libeditor::extend_selection(file, range).unwrap_or(range) 173 libeditor::extend_selection(file, range).unwrap_or(range)
@@ -177,19 +177,19 @@ impl Analysis {
177 } 177 }
178 pub fn syntax_tree(&self, file_id: FileId) -> String { 178 pub fn syntax_tree(&self, file_id: FileId) -> String {
179 let file = self.imp.file_syntax(file_id); 179 let file = self.imp.file_syntax(file_id);
180 libeditor::syntax_tree(file) 180 libeditor::syntax_tree(&file)
181 } 181 }
182 pub fn join_lines(&self, file_id: FileId, range: TextRange) -> SourceChange { 182 pub fn join_lines(&self, file_id: FileId, range: TextRange) -> SourceChange {
183 let file = self.imp.file_syntax(file_id); 183 let file = self.imp.file_syntax(file_id);
184 SourceChange::from_local_edit(file_id, "join lines", libeditor::join_lines(file, range)) 184 SourceChange::from_local_edit(file_id, "join lines", libeditor::join_lines(&file, range))
185 } 185 }
186 pub fn on_eq_typed(&self, file_id: FileId, offset: TextUnit) -> Option<SourceChange> { 186 pub fn on_eq_typed(&self, file_id: FileId, offset: TextUnit) -> Option<SourceChange> {
187 let file = self.imp.file_syntax(file_id); 187 let file = self.imp.file_syntax(file_id);
188 Some(SourceChange::from_local_edit(file_id, "add semicolon", libeditor::on_eq_typed(file, offset)?)) 188 Some(SourceChange::from_local_edit(file_id, "add semicolon", libeditor::on_eq_typed(&file, offset)?))
189 } 189 }
190 pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> { 190 pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> {
191 let file = self.imp.file_syntax(file_id); 191 let file = self.imp.file_syntax(file_id);
192 libeditor::file_structure(file) 192 libeditor::file_structure(&file)
193 } 193 }
194 pub fn symbol_search(&self, query: Query, token: &JobToken) -> Vec<(FileId, FileSymbol)> { 194 pub fn symbol_search(&self, query: Query, token: &JobToken) -> Vec<(FileId, FileSymbol)> {
195 self.imp.world_symbols(query, token) 195 self.imp.world_symbols(query, token)
@@ -208,15 +208,15 @@ impl Analysis {
208 } 208 }
209 pub fn runnables(&self, file_id: FileId) -> Vec<Runnable> { 209 pub fn runnables(&self, file_id: FileId) -> Vec<Runnable> {
210 let file = self.imp.file_syntax(file_id); 210 let file = self.imp.file_syntax(file_id);
211 libeditor::runnables(file) 211 libeditor::runnables(&file)
212 } 212 }
213 pub fn highlight(&self, file_id: FileId) -> Vec<HighlightedRange> { 213 pub fn highlight(&self, file_id: FileId) -> Vec<HighlightedRange> {
214 let file = self.imp.file_syntax(file_id); 214 let file = self.imp.file_syntax(file_id);
215 libeditor::highlight(file) 215 libeditor::highlight(&file)
216 } 216 }
217 pub fn completions(&self, file_id: FileId, offset: TextUnit) -> Option<Vec<CompletionItem>> { 217 pub fn completions(&self, file_id: FileId, offset: TextUnit) -> Option<Vec<CompletionItem>> {
218 let file = self.imp.file_syntax(file_id); 218 let file = self.imp.file_syntax(file_id);
219 libeditor::scope_completion(file, offset) 219 libeditor::scope_completion(&file, offset)
220 } 220 }
221 pub fn assists(&self, file_id: FileId, range: TextRange) -> Vec<SourceChange> { 221 pub fn assists(&self, file_id: FileId, range: TextRange) -> Vec<SourceChange> {
222 self.imp.assists(file_id, range) 222 self.imp.assists(file_id, range)
diff --git a/crates/libanalysis/src/module_map.rs b/crates/libanalysis/src/module_map.rs
index 79b88cac2..a21f55fff 100644
--- a/crates/libanalysis/src/module_map.rs
+++ b/crates/libanalysis/src/module_map.rs
@@ -1,281 +1,157 @@
1use relative_path::RelativePathBuf; 1use std::sync::Arc;
2use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; 2use {
3use libsyntax2::{ 3 FileId,
4 File, 4 db::{
5 ast::{self, AstNode, NameOwner}, 5 Query, QueryRegistry, QueryCtx,
6 SyntaxNode, SmolStr, 6 file_set
7 },
8 queries::file_syntax,
9 descriptors::{ModuleDescriptor, ModuleTreeDescriptor},
7}; 10};
8use {FileId, imp::FileResolverImp};
9
10type SyntaxProvider<'a> = dyn Fn(FileId) -> &'a File + 'a;
11
12#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
13pub struct ModuleId(FileId);
14
15#[derive(Debug, Default)]
16pub struct ModuleMap {
17 state: RwLock<State>,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum ChangeKind {
22 Delete, Insert, Update
23}
24
25impl Clone for ModuleMap {
26 fn clone(&self) -> ModuleMap {
27 let state = self.state.read().clone();
28 ModuleMap { state: RwLock::new(state) }
29 }
30}
31
32#[derive(Clone, Debug, Default)]
33struct State {
34 file_resolver: FileResolverImp,
35 changes: Vec<(FileId, ChangeKind)>,
36 links: Vec<Link>,
37}
38 11
39#[derive(Clone, Debug)] 12pub(crate) fn register_queries(reg: &mut QueryRegistry) {
40struct Link { 13 reg.add(MODULE_DESCR, "MODULE_DESCR");
41 owner: ModuleId, 14 reg.add(MODULE_TREE, "MODULE_TREE");
42 syntax: SyntaxNode,
43 points_to: Vec<ModuleId>,
44 problem: Option<Problem>,
45} 15}
46 16
47#[derive(Clone, Debug)] 17pub(crate) fn module_tree(ctx: QueryCtx) -> Arc<ModuleTreeDescriptor> {
48pub enum Problem { 18 ctx.get(MODULE_TREE, ())
49 UnresolvedModule {
50 candidate: RelativePathBuf,
51 },
52 NotDirOwner {
53 move_to: RelativePathBuf,
54 candidate: RelativePathBuf,
55 }
56} 19}
57 20
58impl ModuleMap { 21const MODULE_DESCR: Query<FileId, ModuleDescriptor> = Query(30, |ctx, &file_id| {
59 pub fn new() -> ModuleMap { 22 let file = file_syntax(ctx, file_id);
60 Default::default() 23 ModuleDescriptor::new(file.ast())
61 } 24});
62 pub fn update_file(&mut self, file_id: FileId, change_kind: ChangeKind) { 25
63 self.state.get_mut().changes.push((file_id, change_kind)); 26const MODULE_TREE: Query<(), ModuleTreeDescriptor> = Query(31, |ctx, _| {
64 } 27 let file_set = file_set(ctx);
65 pub(crate) fn set_file_resolver(&mut self, file_resolver: FileResolverImp) { 28 let mut files = Vec::new();
66 self.state.get_mut().file_resolver = file_resolver; 29 for &file_id in file_set.0.iter() {
67 } 30 let module_descr = ctx.get(MODULE_DESCR, file_id);
68 pub fn module2file(&self, m: ModuleId) -> FileId { 31 files.push((file_id, module_descr));
69 m.0 32 }
70 } 33 ModuleTreeDescriptor::new(files.iter().map(|(file_id, descr)| (*file_id, &**descr)), &file_set.1)
71 pub fn file2module(&self, file_id: FileId) -> ModuleId { 34});
72 ModuleId(file_id) 35
73 } 36#[cfg(test)]
74 pub fn child_module_by_name<'a>( 37mod tests {
75 &self, 38 use std::collections::HashMap;
76 parent_mod: ModuleId, 39 use im;
77 child_mod: &str, 40 use relative_path::{RelativePath, RelativePathBuf};
78 syntax_provider: &SyntaxProvider, 41 use {
79 ) -> Vec<ModuleId> { 42 db::{Db},
80 self.links(syntax_provider) 43 imp::FileResolverImp,
81 .links 44 FileId, FileResolver,
82 .iter() 45 };
83 .filter(|link| link.owner == parent_mod) 46 use super::*;
84 .filter(|link| link.name() == child_mod) 47
85 .filter_map(|it| it.points_to.first()) 48 #[derive(Debug)]
86 .map(|&it| it) 49 struct FileMap(im::HashMap<FileId, RelativePathBuf>);
87 .collect() 50
88 } 51 impl FileResolver for FileMap {
89 52 fn file_stem(&self, file_id: FileId) -> String {
90 pub fn parent_modules( 53 self.0[&file_id].file_stem().unwrap().to_string()
91 &self, 54 }
92 m: ModuleId, 55 fn resolve(&self, file_id: FileId, rel: &RelativePath) -> Option<FileId> {
93 syntax_provider: &SyntaxProvider, 56 let path = self.0[&file_id].join(rel).normalize();
94 ) -> Vec<(ModuleId, SmolStr, SyntaxNode)> { 57 self.0.iter()
95 let mut res = Vec::new(); 58 .filter_map(|&(id, ref p)| Some(id).filter(|_| p == &path))
96 self.for_each_parent_link(m, syntax_provider, |link| { 59 .next()
97 res.push( 60 }
98 (link.owner, link.name().clone(), link.syntax.clone())
99 )
100 });
101 res
102 }
103
104 pub fn parent_module_ids(
105 &self,
106 m: ModuleId,
107 syntax_provider: &SyntaxProvider,
108 ) -> Vec<ModuleId> {
109 let mut res = Vec::new();
110 self.for_each_parent_link(m, syntax_provider, |link| res.push(link.owner));
111 res
112 }
113
114 fn for_each_parent_link(
115 &self,
116 m: ModuleId,
117 syntax_provider: &SyntaxProvider,
118 f: impl FnMut(&Link)
119 ) {
120 self.links(syntax_provider)
121 .links
122 .iter()
123 .filter(move |link| link.points_to.iter().any(|&it| it == m))
124 .for_each(f)
125 } 61 }
126 62
127 pub fn problems( 63 struct Fixture {
128 &self, 64 next_file_id: u32,
129 file: FileId, 65 fm: im::HashMap<FileId, RelativePathBuf>,
130 syntax_provider: &SyntaxProvider, 66 db: Db,
131 mut cb: impl FnMut(ast::Name, &Problem),
132 ) {
133 let module = self.file2module(file);
134 let links = self.links(syntax_provider);
135 links
136 .links
137 .iter()
138 .filter(|link| link.owner == module)
139 .filter_map(|link| {
140 let problem = link.problem.as_ref()?;
141 Some((link, problem))
142 })
143 .for_each(|(link, problem)| cb(link.name_node(), problem))
144 } 67 }
145 68
146 fn links( 69 impl Fixture {
147 &self, 70 fn new() -> Fixture {
148 syntax_provider: &SyntaxProvider, 71 Fixture {
149 ) -> RwLockReadGuard<State> { 72 next_file_id: 1,
150 { 73 fm: im::HashMap::new(),
151 let guard = self.state.read(); 74 db: Db::new(),
152 if guard.changes.is_empty() {
153 return guard;
154 } 75 }
155 } 76 }
156 let mut guard = self.state.write(); 77 fn add_file(&mut self, path: &str, text: &str) -> FileId {
157 if !guard.changes.is_empty() { 78 assert!(path.starts_with("/"));
158 guard.apply_changes(syntax_provider); 79 let file_id = FileId(self.next_file_id);
80 self.next_file_id += 1;
81 self.fm.insert(file_id, RelativePathBuf::from(&path[1..]));
82 let mut new_state = self.db.state().clone();
83 new_state.file_map.insert(file_id, Arc::new(text.to_string()));
84 new_state.file_resolver = FileResolverImp::new(
85 Arc::new(FileMap(self.fm.clone()))
86 );
87 self.db = self.db.with_changes(new_state, &[file_id], true);
88 file_id
159 } 89 }
160 assert!(guard.changes.is_empty()); 90 fn remove_file(&mut self, file_id: FileId) {
161 RwLockWriteGuard::downgrade(guard) 91 self.fm.remove(&file_id);
162 } 92 let mut new_state = self.db.state().clone();
163} 93 new_state.file_map.remove(&file_id);
164 94 new_state.file_resolver = FileResolverImp::new(
165impl State { 95 Arc::new(FileMap(self.fm.clone()))
166 pub fn apply_changes( 96 );
167 &mut self, 97 self.db = self.db.with_changes(new_state, &[file_id], true);
168 syntax_provider: &SyntaxProvider,
169 ) {
170 let mut reresolve = false;
171 for (file_id, kind) in self.changes.drain(..) {
172 let mod_id = ModuleId(file_id);
173 self.links.retain(|link| link.owner != mod_id);
174 match kind {
175 ChangeKind::Delete => {
176 for link in self.links.iter_mut() {
177 link.points_to.retain(|&x| x != mod_id);
178 }
179 }
180 ChangeKind::Insert => {
181 let file = syntax_provider(file_id);
182 self.links.extend(
183 file
184 .ast()
185 .modules()
186 .filter_map(|it| Link::new(mod_id, it))
187 );
188 reresolve = true;
189 }
190 ChangeKind::Update => {
191 let file = syntax_provider(file_id);
192 let resolver = &self.file_resolver;
193 self.links.extend(
194 file
195 .ast()
196 .modules()
197 .filter_map(|it| Link::new(mod_id, it))
198 .map(|mut link| {
199 link.resolve(resolver);
200 link
201 })
202 );
203 }
204 }
205 } 98 }
206 if reresolve { 99 fn change_file(&mut self, file_id: FileId, new_text: &str) {
207 for link in self.links.iter_mut() { 100 let mut new_state = self.db.state().clone();
208 link.resolve(&self.file_resolver) 101 new_state.file_map.insert(file_id, Arc::new(new_text.to_string()));
209 } 102 self.db = self.db.with_changes(new_state, &[file_id], false);
210 } 103 }
211 } 104 fn check_parent_modules(
212} 105 &self,
106 file_id: FileId,
107 expected: &[FileId],
108 queries: &[(&'static str, u64)]
109 ) {
110 let (tree, events) = self.db.trace_query(|ctx| module_tree(ctx));
111 let actual = tree.parent_modules(file_id)
112 .into_iter()
113 .map(|link| link.owner(&tree))
114 .collect::<Vec<_>>();
115 assert_eq!(actual.as_slice(), expected);
116 let mut counts = HashMap::new();
117 events.into_iter()
118 .for_each(|event| *counts.entry(event).or_insert(0) += 1);
119 for &(query_id, expected_count) in queries.iter() {
120 let actual_count = *counts.get(&query_id).unwrap_or(&0);
121 assert_eq!(
122 actual_count,
123 expected_count,
124 "counts for {} differ",
125 query_id,
126 )
127 }
213 128
214impl Link {
215 fn new(owner: ModuleId, module: ast::Module) -> Option<Link> {
216 if module.name().is_none() {
217 return None;
218 } 129 }
219 let link = Link {
220 owner,
221 syntax: module.syntax().owned(),
222 points_to: Vec::new(),
223 problem: None,
224 };
225 Some(link)
226 } 130 }
227 131
228 fn name(&self) -> SmolStr { 132 #[test]
229 self.name_node().text() 133 fn test_parent_module() {
230 } 134 let mut f = Fixture::new();
135 let foo = f.add_file("/foo.rs", "");
136 f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 1)]);
231 137
232 fn name_node(&self) -> ast::Name { 138 let lib = f.add_file("/lib.rs", "mod foo;");
233 self.ast().name().unwrap() 139 f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 1)]);
234 } 140 f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 0)]);
235 141
236 fn ast(&self) -> ast::Module { 142 f.change_file(lib, "");
237 ast::Module::cast(self.syntax.borrowed()) 143 f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 1)]);
238 .unwrap()
239 }
240 144
241 fn resolve(&mut self, file_resolver: &FileResolverImp) { 145 f.change_file(lib, "mod foo;");
242 if !self.ast().has_semi() { 146 f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 1)]);
243 self.problem = None;
244 self.points_to = Vec::new();
245 return;
246 }
247 let (points_to, problem) = resolve_submodule(self.owner.0, &self.name(), file_resolver);
248 self.problem = problem;
249 self.points_to = points_to.into_iter().map(ModuleId).collect();
250 }
251}
252 147
253pub(crate) fn resolve_submodule(file_id: FileId, name: &SmolStr, file_resolver: &FileResolverImp) -> (Vec<FileId>, Option<Problem>) { 148 f.change_file(lib, "mod bar;");
254 let mod_name = file_resolver.file_stem(file_id); 149 f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 1)]);
255 let is_dir_owner =
256 mod_name == "mod" || mod_name == "lib" || mod_name == "main";
257 150
258 let file_mod = RelativePathBuf::from(format!("../{}.rs", name)); 151 f.change_file(lib, "mod foo;");
259 let dir_mod = RelativePathBuf::from(format!("../{}/mod.rs", name)); 152 f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 1)]);
260 let points_to: Vec<FileId>; 153
261 let problem: Option<Problem>; 154 f.remove_file(lib);
262 if is_dir_owner { 155 f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 0)]);
263 points_to = [&file_mod, &dir_mod].iter()
264 .filter_map(|path| file_resolver.resolve(file_id, path))
265 .collect();
266 problem = if points_to.is_empty() {
267 Some(Problem::UnresolvedModule {
268 candidate: file_mod,
269 })
270 } else {
271 None
272 }
273 } else {
274 points_to = Vec::new();
275 problem = Some(Problem::NotDirOwner {
276 move_to: RelativePathBuf::from(format!("../{}/mod.rs", mod_name)),
277 candidate: file_mod,
278 });
279 } 156 }
280 (points_to, problem)
281} 157}
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 adad943da..000000000
--- a/crates/libanalysis/src/module_map_db/mod.rs
+++ /dev/null
@@ -1,159 +0,0 @@
1use std::sync::Arc;
2use {
3 FileId,
4 db::{
5 Query, QueryRegistry, QueryCtx,
6 file_syntax, file_set
7 },
8 descriptors::{ModuleDescriptor, ModuleTreeDescriptor}
9};
10
11pub(crate) fn register_queries(reg: &mut QueryRegistry) {
12 reg.add(MODULE_DESCR, "MODULE_DESCR");
13}
14
15pub(crate) fn module_tree(ctx: QueryCtx) -> Arc<ModuleTreeDescriptor> {
16 ctx.get(MODULE_TREE, ())
17}
18
19impl<'a> QueryCtx<'a> {
20 fn module_descr(&self, file_id: FileId) -> Arc<ModuleDescriptor> {
21 self.get(MODULE_DESCR, file_id)
22 }
23}
24
25const MODULE_DESCR: Query<FileId, ModuleDescriptor> = Query(30, |ctx, &file_id| {
26 let file = file_syntax(ctx, file_id);
27 ModuleDescriptor::new(file.ast())
28});
29
30const MODULE_TREE: Query<(), ModuleTreeDescriptor> = Query(31, |ctx, _| {
31 let file_set = file_set(ctx);
32 let mut files = Vec::new();
33 for &file_id in file_set.0.iter() {
34 let module_descr = ctx.get(MODULE_DESCR, file_id);
35 files.push((file_id, module_descr));
36 }
37 ModuleTreeDescriptor::new(files.iter().map(|(file_id, descr)| (*file_id, &**descr)), &file_set.1)
38});
39
40#[cfg(test)]
41mod tests {
42 use std::collections::HashMap;
43 use im;
44 use relative_path::{RelativePath, RelativePathBuf};
45 use {
46 db::{Query, Db, State},
47 imp::FileResolverImp,
48 FileId, FileResolver,
49 };
50 use super::*;
51
52 #[derive(Debug)]
53 struct FileMap(im::HashMap<FileId, RelativePathBuf>);
54
55 impl FileResolver for FileMap {
56 fn file_stem(&self, file_id: FileId) -> String {
57 self.0[&file_id].file_stem().unwrap().to_string()
58 }
59 fn resolve(&self, file_id: FileId, rel: &RelativePath) -> Option<FileId> {
60 let path = self.0[&file_id].join(rel).normalize();
61 self.0.iter()
62 .filter_map(|&(id, ref p)| Some(id).filter(|_| p == &path))
63 .next()
64 }
65 }
66
67 struct Fixture {
68 next_file_id: u32,
69 fm: im::HashMap<FileId, RelativePathBuf>,
70 db: Db,
71 }
72
73 impl Fixture {
74 fn new() -> Fixture {
75 Fixture {
76 next_file_id: 1,
77 fm: im::HashMap::new(),
78 db: Db::new(),
79 }
80 }
81 fn add_file(&mut self, path: &str, text: &str) -> FileId {
82 assert!(path.starts_with("/"));
83 let file_id = FileId(self.next_file_id);
84 self.next_file_id += 1;
85 self.fm.insert(file_id, RelativePathBuf::from(&path[1..]));
86 let mut new_state = self.db.state().clone();
87 new_state.file_map.insert(file_id, Arc::new(text.to_string()));
88 new_state.file_resolver = FileResolverImp::new(
89 Arc::new(FileMap(self.fm.clone()))
90 );
91 self.db = self.db.with_changes(new_state, &[file_id], true);
92 file_id
93 }
94 fn remove_file(&mut self, file_id: FileId) {
95 self.fm.remove(&file_id);
96 let mut new_state = self.db.state().clone();
97 new_state.file_map.remove(&file_id);
98 new_state.file_resolver = FileResolverImp::new(
99 Arc::new(FileMap(self.fm.clone()))
100 );
101 self.db = self.db.with_changes(new_state, &[file_id], true);
102 }
103 fn change_file(&mut self, file_id: FileId, new_text: &str) {
104 let mut new_state = self.db.state().clone();
105 new_state.file_map.insert(file_id, Arc::new(new_text.to_string()));
106 self.db = self.db.with_changes(new_state, &[file_id], false);
107 }
108 fn check_parent_modules(
109 &self,
110 file_id: FileId,
111 expected: &[FileId],
112 queries: &[(&'static str, u64)]
113 ) {
114 let (actual, events) = self.db.trace_query(|ctx| {
115 ctx.get(PARENT_MODULE, file_id)
116 });
117 assert_eq!(actual.as_slice(), expected);
118 let mut counts = HashMap::new();
119 events.into_iter()
120 .for_each(|event| *counts.entry(event).or_insert(0) += 1);
121 for &(query_id, expected_count) in queries.iter() {
122 let actual_count = *counts.get(&query_id).unwrap_or(&0);
123 assert_eq!(
124 actual_count,
125 expected_count,
126 "counts for {} differ",
127 query_id,
128 )
129 }
130
131 }
132 }
133
134 #[test]
135 fn test_parent_module() {
136 let mut f = Fixture::new();
137 let foo = f.add_file("/foo.rs", "");
138 f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 1)]);
139
140 let lib = f.add_file("/lib.rs", "mod foo;");
141 f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 1)]);
142 f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 0)]);
143
144 f.change_file(lib, "");
145 f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 1)]);
146
147 f.change_file(lib, "mod foo;");
148 f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 1)]);
149
150 f.change_file(lib, "mod bar;");
151 f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 1)]);
152
153 f.change_file(lib, "mod foo;");
154 f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 1)]);
155
156 f.remove_file(lib);
157 f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 0)]);
158 }
159}
diff --git a/crates/libanalysis/src/queries.rs b/crates/libanalysis/src/queries.rs
new file mode 100644
index 000000000..9d6754fd4
--- /dev/null
+++ b/crates/libanalysis/src/queries.rs
@@ -0,0 +1,28 @@
1use std::sync::Arc;
2use libsyntax2::File;
3use libeditor::LineIndex;
4use {
5 FileId,
6 db::{Query, QueryCtx, QueryRegistry, file_text},
7};
8
9pub(crate) fn register_queries(reg: &mut QueryRegistry) {
10 reg.add(FILE_SYNTAX, "FILE_SYNTAX");
11 reg.add(FILE_LINES, "FILE_LINES");
12}
13
14pub(crate) fn file_syntax(ctx: QueryCtx, file_id: FileId) -> File {
15 (&*ctx.get(FILE_SYNTAX, file_id)).clone()
16}
17pub(crate) fn file_lines(ctx: QueryCtx, file_id: FileId) -> Arc<LineIndex> {
18 ctx.get(FILE_LINES, file_id)
19}
20
21const FILE_SYNTAX: Query<FileId, File> = Query(16, |ctx, file_id: &FileId| {
22 let text = file_text(ctx, *file_id);
23 File::parse(&*text)
24});
25const FILE_LINES: Query<FileId, LineIndex> = Query(17, |ctx, file_id: &FileId| {
26 let text = file_text(ctx, *file_id);
27 LineIndex::new(&*text)
28});
diff --git a/crates/libanalysis/src/roots.rs b/crates/libanalysis/src/roots.rs
index e9527eeff..0e7253ba2 100644
--- a/crates/libanalysis/src/roots.rs
+++ b/crates/libanalysis/src/roots.rs
@@ -1,6 +1,5 @@
1use std::{ 1use std::{
2 collections::HashMap, 2 collections::HashMap,
3 time::Instant,
4 sync::Arc, 3 sync::Arc,
5 panic, 4 panic,
6}; 5};
@@ -13,83 +12,74 @@ use libsyntax2::File;
13use { 12use {
14 FileId, 13 FileId,
15 imp::FileResolverImp, 14 imp::FileResolverImp,
16 module_map::{ModuleMap, ChangeKind},
17 symbol_index::SymbolIndex, 15 symbol_index::SymbolIndex,
18 descriptors::{ModuleDescriptor, ModuleTreeDescriptor}, 16 descriptors::{ModuleDescriptor, ModuleTreeDescriptor},
17 db::Db,
19}; 18};
20 19
21pub(crate) trait SourceRoot { 20pub(crate) trait SourceRoot {
22 fn contains(&self, file_id: FileId) -> bool; 21 fn contains(&self, file_id: FileId) -> bool;
23 fn module_tree(&self) -> Arc<ModuleTreeDescriptor> { 22 fn module_tree(&self) -> Arc<ModuleTreeDescriptor>;
24 unimplemented!() 23 fn lines(&self, file_id: FileId) -> Arc<LineIndex>;
25 } 24 fn syntax(&self, file_id: FileId) -> File;
26 // fn module_map(&self) -> &ModuleMap;
27 fn lines(&self, file_id: FileId) -> &LineIndex;
28 fn syntax(&self, file_id: FileId) -> &File;
29 fn symbols<'a>(&'a self, acc: &mut Vec<&'a SymbolIndex>); 25 fn symbols<'a>(&'a self, acc: &mut Vec<&'a SymbolIndex>);
30} 26}
31 27
32#[derive(Clone, Default, Debug)] 28#[derive(Default, Debug)]
33pub(crate) struct WritableSourceRoot { 29pub(crate) struct WritableSourceRoot {
34 file_map: HashMap<FileId, Arc<(FileData, OnceCell<SymbolIndex>)>>, 30 db: Db,
35 module_map: ModuleMap,
36} 31}
37 32
38impl WritableSourceRoot { 33impl WritableSourceRoot {
39 pub fn update(&mut self, file_id: FileId, text: Option<String>) { 34 pub fn apply_changes(
40 let change_kind = if self.file_map.remove(&file_id).is_some() { 35 &self,
41 if text.is_some() { 36 changes: &mut dyn Iterator<Item=(FileId, Option<String>)>,
42 ChangeKind::Update 37 file_resolver: Option<FileResolverImp>,
43 } else { 38 ) -> WritableSourceRoot {
44 ChangeKind::Delete 39 let resolver_changed = file_resolver.is_some();
40 let mut changed_files = Vec::new();
41 let mut new_state = self.db.state().clone();
42
43 for (file_id, text) in changes {
44 changed_files.push(file_id);
45 match text {
46 Some(text) => {
47 new_state.file_map.insert(file_id, Arc::new(text));
48 },
49 None => {
50 new_state.file_map.remove(&file_id);
51 }
45 } 52 }
46 } else {
47 ChangeKind::Insert
48 };
49 self.module_map.update_file(file_id, change_kind);
50 self.file_map.remove(&file_id);
51 if let Some(text) = text {
52 let file_data = FileData::new(text);
53 self.file_map.insert(file_id, Arc::new((file_data, Default::default())));
54 } 53 }
55 } 54 if let Some(file_resolver) = file_resolver {
56 pub fn set_file_resolver(&mut self, file_resolver: FileResolverImp) { 55 new_state.file_resolver = file_resolver
57 self.module_map.set_file_resolver(file_resolver) 56 }
58 } 57 WritableSourceRoot {
59 pub fn reindex(&self) { 58 db: self.db.with_changes(new_state, &changed_files, resolver_changed)
60 let now = Instant::now();
61 self.file_map
62 .par_iter()
63 .for_each(|(&file_id, data)| {
64 symbols(file_id, data);
65 });
66 info!("parallel indexing took {:?}", now.elapsed());
67
68 }
69 fn data(&self, file_id: FileId) -> &FileData {
70 match self.file_map.get(&file_id) {
71 Some(data) => &data.0,
72 None => panic!("unknown file: {:?}", file_id),
73 } 59 }
74 } 60 }
75} 61}
76 62
77impl SourceRoot for WritableSourceRoot { 63impl SourceRoot for WritableSourceRoot {
64 fn module_tree(&self) -> Arc<ModuleTreeDescriptor> {
65 self.db.make_query(::module_map::module_tree)
66 }
67
78 fn contains(&self, file_id: FileId) -> bool { 68 fn contains(&self, file_id: FileId) -> bool {
79 self.file_map.contains_key(&file_id) 69 self.db.state().file_map.contains_key(&file_id)
80 } 70 }
81 fn lines(&self, file_id: FileId) -> &LineIndex { 71 fn lines(&self, file_id: FileId) -> Arc<LineIndex> {
82 self.data(file_id).lines() 72 self.db.make_query(|ctx| ::queries::file_lines(ctx, file_id))
83 } 73 }
84 fn syntax(&self, file_id: FileId) -> &File { 74 fn syntax(&self, file_id: FileId) -> File {
85 self.data(file_id).syntax() 75 self.db.make_query(|ctx| ::queries::file_syntax(ctx, file_id))
86 } 76 }
87 fn symbols<'a>(&'a self, acc: &mut Vec<&'a SymbolIndex>) { 77 fn symbols<'a>(&'a self, acc: &mut Vec<&'a SymbolIndex>) {
88 acc.extend( 78 // acc.extend(
89 self.file_map 79 // self.file_map
90 .iter() 80 // .iter()
91 .map(|(&file_id, data)| symbols(file_id, data)) 81 // .map(|(&file_id, data)| symbols(file_id, data))
92 ) 82 // )
93 } 83 }
94} 84}
95 85
@@ -101,7 +91,7 @@ fn symbols(file_id: FileId, (data, symbols): &(FileData, OnceCell<SymbolIndex>))
101#[derive(Debug)] 91#[derive(Debug)]
102struct FileData { 92struct FileData {
103 text: String, 93 text: String,
104 lines: OnceCell<LineIndex>, 94 lines: OnceCell<Arc<LineIndex>>,
105 syntax: OnceCell<File>, 95 syntax: OnceCell<File>,
106} 96}
107 97
@@ -113,8 +103,8 @@ impl FileData {
113 lines: OnceCell::new(), 103 lines: OnceCell::new(),
114 } 104 }
115 } 105 }
116 fn lines(&self) -> &LineIndex { 106 fn lines(&self) -> &Arc<LineIndex> {
117 self.lines.get_or_init(|| LineIndex::new(&self.text)) 107 self.lines.get_or_init(|| Arc::new(LineIndex::new(&self.text)))
118 } 108 }
119 fn syntax(&self) -> &File { 109 fn syntax(&self) -> &File {
120 let text = &self.text; 110 let text = &self.text;
@@ -127,10 +117,6 @@ impl FileData {
127 } 117 }
128 } 118 }
129 } 119 }
130 fn syntax_transient(&self) -> File {
131 self.syntax.get().map(|s| s.clone())
132 .unwrap_or_else(|| File::parse(&self.text))
133 }
134} 120}
135 121
136#[derive(Debug)] 122#[derive(Debug)]
@@ -184,11 +170,11 @@ impl SourceRoot for ReadonlySourceRoot {
184 fn contains(&self, file_id: FileId) -> bool { 170 fn contains(&self, file_id: FileId) -> bool {
185 self.file_map.contains_key(&file_id) 171 self.file_map.contains_key(&file_id)
186 } 172 }
187 fn lines(&self, file_id: FileId) -> &LineIndex { 173 fn lines(&self, file_id: FileId) -> Arc<LineIndex> {
188 self.data(file_id).lines() 174 Arc::clone(self.data(file_id).lines())
189 } 175 }
190 fn syntax(&self, file_id: FileId) -> &File { 176 fn syntax(&self, file_id: FileId) -> File {
191 self.data(file_id).syntax() 177 self.data(file_id).syntax().clone()
192 } 178 }
193 fn symbols<'a>(&'a self, acc: &mut Vec<&'a SymbolIndex>) { 179 fn symbols<'a>(&'a self, acc: &mut Vec<&'a SymbolIndex>) {
194 acc.push(&self.symbol_index) 180 acc.push(&self.symbol_index)