aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_analysis/src/completion.rs2
-rw-r--r--crates/ra_analysis/src/imp.rs224
-rw-r--r--crates/ra_analysis/src/lib.rs153
-rw-r--r--crates/ra_analysis/src/symbol_index.rs42
4 files changed, 203 insertions, 218 deletions
diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs
index b4ee092b5..739ca6ae8 100644
--- a/crates/ra_analysis/src/completion.rs
+++ b/crates/ra_analysis/src/completion.rs
@@ -58,6 +58,6 @@ fn check_completion(code: &str, expected_completions: &str, kind: CompletionKind
58 } else { 58 } else {
59 single_file_with_position(code) 59 single_file_with_position(code)
60 }; 60 };
61 let completions = completions(&analysis.imp.db, position).unwrap().unwrap(); 61 let completions = completions(&analysis.db, position).unwrap().unwrap();
62 completions.assert_match(expected_completions, kind); 62 completions.assert_match(expected_completions, kind);
63} 63}
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index 8071554a7..0faf8b85d 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -1,16 +1,12 @@
1use std::{ 1use std::sync::Arc;
2 fmt,
3 sync::Arc,
4};
5 2
6use rayon::prelude::*; 3use salsa::Database;
7use salsa::{Database, ParallelDatabase};
8 4
9use hir::{ 5use hir::{
10 self, FnSignatureInfo, Problem, source_binder, 6 self, FnSignatureInfo, Problem, source_binder,
11}; 7};
12use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; 8use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase};
13use ra_editor::{self, find_node_at_offset, LineIndex, LocalEdit, Severity}; 9use ra_editor::{self, find_node_at_offset, LocalEdit, Severity};
14use ra_syntax::{ 10use ra_syntax::{
15 algo::find_covering_node, 11 algo::find_covering_node,
16 ast::{self, ArgListOwner, Expr, FnDef, NameOwner}, 12 ast::{self, ArgListOwner, Expr, FnDef, NameOwner},
@@ -22,38 +18,25 @@ use ra_syntax::{
22use crate::{ 18use crate::{
23 AnalysisChange, 19 AnalysisChange,
24 Cancelable, NavigationTarget, 20 Cancelable, NavigationTarget,
25 completion::{CompletionItem, completions},
26 CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit, 21 CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit,
27 Query, ReferenceResolution, RootChange, SourceChange, SourceFileEdit, 22 Query, ReferenceResolution, RootChange, SourceChange, SourceFileEdit,
28 symbol_index::{LibrarySymbolsQuery, SymbolIndex, SymbolsDatabase, FileSymbol}, 23 symbol_index::{LibrarySymbolsQuery, FileSymbol},
29}; 24};
30 25
31#[derive(Debug, Default)] 26impl db::RootDatabase {
32pub(crate) struct AnalysisHostImpl { 27 pub(crate) fn apply_change(&mut self, change: AnalysisChange) {
33 db: db::RootDatabase,
34}
35
36impl AnalysisHostImpl {
37 pub fn analysis(&self) -> AnalysisImpl {
38 AnalysisImpl {
39 db: self.db.snapshot(),
40 }
41 }
42 pub fn apply_change(&mut self, change: AnalysisChange) {
43 log::info!("apply_change {:?}", change); 28 log::info!("apply_change {:?}", change);
44 // self.gc_syntax_trees(); 29 // self.gc_syntax_trees();
45 if !change.new_roots.is_empty() { 30 if !change.new_roots.is_empty() {
46 let mut local_roots = Vec::clone(&self.db.local_roots()); 31 let mut local_roots = Vec::clone(&self.local_roots());
47 for (root_id, is_local) in change.new_roots { 32 for (root_id, is_local) in change.new_roots {
48 self.db 33 self.query_mut(ra_db::SourceRootQuery)
49 .query_mut(ra_db::SourceRootQuery)
50 .set(root_id, Default::default()); 34 .set(root_id, Default::default());
51 if is_local { 35 if is_local {
52 local_roots.push(root_id); 36 local_roots.push(root_id);
53 } 37 }
54 } 38 }
55 self.db 39 self.query_mut(ra_db::LocalRootsQuery)
56 .query_mut(ra_db::LocalRootsQuery)
57 .set((), Arc::new(local_roots)); 40 .set((), Arc::new(local_roots));
58 } 41 }
59 42
@@ -61,53 +44,44 @@ impl AnalysisHostImpl {
61 self.apply_root_change(root_id, root_change); 44 self.apply_root_change(root_id, root_change);
62 } 45 }
63 for (file_id, text) in change.files_changed { 46 for (file_id, text) in change.files_changed {
64 self.db.query_mut(ra_db::FileTextQuery).set(file_id, text) 47 self.query_mut(ra_db::FileTextQuery).set(file_id, text)
65 } 48 }
66 if !change.libraries_added.is_empty() { 49 if !change.libraries_added.is_empty() {
67 let mut libraries = Vec::clone(&self.db.library_roots()); 50 let mut libraries = Vec::clone(&self.library_roots());
68 for library in change.libraries_added { 51 for library in change.libraries_added {
69 libraries.push(library.root_id); 52 libraries.push(library.root_id);
70 self.db 53 self.query_mut(ra_db::SourceRootQuery)
71 .query_mut(ra_db::SourceRootQuery)
72 .set(library.root_id, Default::default()); 54 .set(library.root_id, Default::default());
73 self.db 55 self.query_mut(LibrarySymbolsQuery)
74 .query_mut(LibrarySymbolsQuery)
75 .set_constant(library.root_id, Arc::new(library.symbol_index)); 56 .set_constant(library.root_id, Arc::new(library.symbol_index));
76 self.apply_root_change(library.root_id, library.root_change); 57 self.apply_root_change(library.root_id, library.root_change);
77 } 58 }
78 self.db 59 self.query_mut(ra_db::LibraryRootsQuery)
79 .query_mut(ra_db::LibraryRootsQuery)
80 .set((), Arc::new(libraries)); 60 .set((), Arc::new(libraries));
81 } 61 }
82 if let Some(crate_graph) = change.crate_graph { 62 if let Some(crate_graph) = change.crate_graph {
83 self.db 63 self.query_mut(ra_db::CrateGraphQuery)
84 .query_mut(ra_db::CrateGraphQuery)
85 .set((), Arc::new(crate_graph)) 64 .set((), Arc::new(crate_graph))
86 } 65 }
87 } 66 }
88 67
89 fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) { 68 fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) {
90 let mut source_root = SourceRoot::clone(&self.db.source_root(root_id)); 69 let mut source_root = SourceRoot::clone(&self.source_root(root_id));
91 for add_file in root_change.added { 70 for add_file in root_change.added {
92 self.db 71 self.query_mut(ra_db::FileTextQuery)
93 .query_mut(ra_db::FileTextQuery)
94 .set(add_file.file_id, add_file.text); 72 .set(add_file.file_id, add_file.text);
95 self.db 73 self.query_mut(ra_db::FileRelativePathQuery)
96 .query_mut(ra_db::FileRelativePathQuery)
97 .set(add_file.file_id, add_file.path.clone()); 74 .set(add_file.file_id, add_file.path.clone());
98 self.db 75 self.query_mut(ra_db::FileSourceRootQuery)
99 .query_mut(ra_db::FileSourceRootQuery)
100 .set(add_file.file_id, root_id); 76 .set(add_file.file_id, root_id);
101 source_root.files.insert(add_file.path, add_file.file_id); 77 source_root.files.insert(add_file.path, add_file.file_id);
102 } 78 }
103 for remove_file in root_change.removed { 79 for remove_file in root_change.removed {
104 self.db 80 self.query_mut(ra_db::FileTextQuery)
105 .query_mut(ra_db::FileTextQuery)
106 .set(remove_file.file_id, Default::default()); 81 .set(remove_file.file_id, Default::default());
107 source_root.files.remove(&remove_file.path); 82 source_root.files.remove(&remove_file.path);
108 } 83 }
109 self.db 84 self.query_mut(ra_db::SourceRootQuery)
110 .query_mut(ra_db::SourceRootQuery)
111 .set(root_id, Arc::new(source_root)); 85 .set(root_id, Arc::new(source_root));
112 } 86 }
113 87
@@ -116,74 +90,18 @@ impl AnalysisHostImpl {
116 /// syntax trees. However, if we actually do that, everything is recomputed 90 /// syntax trees. However, if we actually do that, everything is recomputed
117 /// for some reason. Needs investigation. 91 /// for some reason. Needs investigation.
118 fn gc_syntax_trees(&mut self) { 92 fn gc_syntax_trees(&mut self) {
119 self.db 93 self.query(ra_db::SourceFileQuery)
120 .query(ra_db::SourceFileQuery)
121 .sweep(salsa::SweepStrategy::default().discard_values()); 94 .sweep(salsa::SweepStrategy::default().discard_values());
122 self.db 95 self.query(hir::db::SourceFileItemsQuery)
123 .query(hir::db::SourceFileItemsQuery)
124 .sweep(salsa::SweepStrategy::default().discard_values()); 96 .sweep(salsa::SweepStrategy::default().discard_values());
125 self.db 97 self.query(hir::db::FileItemQuery)
126 .query(hir::db::FileItemQuery)
127 .sweep(salsa::SweepStrategy::default().discard_values()); 98 .sweep(salsa::SweepStrategy::default().discard_values());
128 } 99 }
129} 100}
130 101
131pub(crate) struct AnalysisImpl { 102impl db::RootDatabase {
132 pub(crate) db: salsa::Snapshot<db::RootDatabase>,
133}
134
135impl fmt::Debug for AnalysisImpl {
136 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
137 let db: &db::RootDatabase = &self.db;
138 fmt.debug_struct("AnalysisImpl").field("db", db).finish()
139 }
140}
141
142impl AnalysisImpl {
143 pub fn file_text(&self, file_id: FileId) -> Arc<String> {
144 self.db.file_text(file_id)
145 }
146 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode {
147 self.db.source_file(file_id)
148 }
149 pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> {
150 self.db.file_lines(file_id)
151 }
152 pub fn world_symbols(&self, query: Query) -> Cancelable<Vec<(FileId, FileSymbol)>> {
153 /// Need to wrap Snapshot to provide `Clone` impl for `map_with`
154 struct Snap(salsa::Snapshot<db::RootDatabase>);
155 impl Clone for Snap {
156 fn clone(&self) -> Snap {
157 Snap(self.0.snapshot())
158 }
159 }
160
161 let buf: Vec<Arc<SymbolIndex>> = if query.libs {
162 let snap = Snap(self.db.snapshot());
163 self.db
164 .library_roots()
165 .par_iter()
166 .map_with(snap, |db, &lib_id| db.0.library_symbols(lib_id))
167 .collect()
168 } else {
169 let mut files = Vec::new();
170 for &root in self.db.local_roots().iter() {
171 let sr = self.db.source_root(root);
172 files.extend(sr.files.values().map(|&it| it))
173 }
174
175 let snap = Snap(self.db.snapshot());
176 files
177 .par_iter()
178 .map_with(snap, |db, &file_id| db.0.file_symbols(file_id))
179 .filter_map(|it| it.ok())
180 .collect()
181 };
182 Ok(query.search(&buf))
183 }
184
185 pub(crate) fn module_path(&self, position: FilePosition) -> Cancelable<Option<String>> { 103 pub(crate) fn module_path(&self, position: FilePosition) -> Cancelable<Option<String>> {
186 let descr = match source_binder::module_from_position(&*self.db, position)? { 104 let descr = match source_binder::module_from_position(self, position)? {
187 None => return Ok(None), 105 None => return Ok(None),
188 Some(it) => it, 106 Some(it) => it,
189 }; 107 };
@@ -205,12 +123,15 @@ impl AnalysisImpl {
205 123
206 /// This returns `Vec` because a module may be included from several places. We 124 /// This returns `Vec` because a module may be included from several places. We
207 /// don't handle this case yet though, so the Vec has length at most one. 125 /// don't handle this case yet though, so the Vec has length at most one.
208 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> { 126 pub(crate) fn parent_module(
209 let descr = match source_binder::module_from_position(&*self.db, position)? { 127 &self,
128 position: FilePosition,
129 ) -> Cancelable<Vec<NavigationTarget>> {
130 let descr = match source_binder::module_from_position(self, position)? {
210 None => return Ok(Vec::new()), 131 None => return Ok(Vec::new()),
211 Some(it) => it, 132 Some(it) => it,
212 }; 133 };
213 let (file_id, decl) = match descr.parent_link_source(&*self.db) { 134 let (file_id, decl) = match descr.parent_link_source(self) {
214 None => return Ok(Vec::new()), 135 None => return Ok(Vec::new()),
215 Some(it) => it, 136 Some(it) => it,
216 }; 137 };
@@ -224,39 +145,33 @@ impl AnalysisImpl {
224 Ok(vec![NavigationTarget { file_id, symbol }]) 145 Ok(vec![NavigationTarget { file_id, symbol }])
225 } 146 }
226 /// Returns `Vec` for the same reason as `parent_module` 147 /// Returns `Vec` for the same reason as `parent_module`
227 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { 148 pub(crate) fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
228 let descr = match source_binder::module_from_file_id(&*self.db, file_id)? { 149 let descr = match source_binder::module_from_file_id(self, file_id)? {
229 None => return Ok(Vec::new()), 150 None => return Ok(Vec::new()),
230 Some(it) => it, 151 Some(it) => it,
231 }; 152 };
232 let root = descr.crate_root(); 153 let root = descr.crate_root();
233 let file_id = root.file_id(); 154 let file_id = root.file_id();
234 155
235 let crate_graph = self.db.crate_graph(); 156 let crate_graph = self.crate_graph();
236 let crate_id = crate_graph.crate_id_for_crate_root(file_id); 157 let crate_id = crate_graph.crate_id_for_crate_root(file_id);
237 Ok(crate_id.into_iter().collect()) 158 Ok(crate_id.into_iter().collect())
238 } 159 }
239 pub fn crate_root(&self, crate_id: CrateId) -> FileId { 160 pub(crate) fn crate_root(&self, crate_id: CrateId) -> FileId {
240 self.db.crate_graph().crate_root(crate_id) 161 self.crate_graph().crate_root(crate_id)
241 } 162 }
242 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> { 163 pub(crate) fn approximately_resolve_symbol(
243 let completions = completions(&self.db, position)?;
244 Ok(completions.map(|it| it.into()))
245 }
246 pub fn approximately_resolve_symbol(
247 &self, 164 &self,
248 position: FilePosition, 165 position: FilePosition,
249 ) -> Cancelable<Option<ReferenceResolution>> { 166 ) -> Cancelable<Option<ReferenceResolution>> {
250 let file = self.db.source_file(position.file_id); 167 let file = self.source_file(position.file_id);
251 let syntax = file.syntax(); 168 let syntax = file.syntax();
252 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) { 169 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) {
253 let mut rr = ReferenceResolution::new(name_ref.syntax().range()); 170 let mut rr = ReferenceResolution::new(name_ref.syntax().range());
254 if let Some(fn_descr) = source_binder::function_from_child_node( 171 if let Some(fn_descr) =
255 &*self.db, 172 source_binder::function_from_child_node(self, position.file_id, name_ref.syntax())?
256 position.file_id, 173 {
257 name_ref.syntax(), 174 let scope = fn_descr.scopes(self);
258 )? {
259 let scope = fn_descr.scopes(&*self.db);
260 // First try to resolve the symbol locally 175 // First try to resolve the symbol locally
261 if let Some(entry) = scope.resolve_local_name(name_ref) { 176 if let Some(entry) = scope.resolve_local_name(name_ref) {
262 rr.add_resolution( 177 rr.add_resolution(
@@ -281,7 +196,7 @@ impl AnalysisImpl {
281 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { 196 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
282 if module.has_semi() { 197 if module.has_semi() {
283 if let Some(child_module) = 198 if let Some(child_module) =
284 source_binder::module_from_declaration(&*self.db, position.file_id, module)? 199 source_binder::module_from_declaration(self, position.file_id, module)?
285 { 200 {
286 let file_id = child_module.file_id(); 201 let file_id = child_module.file_id();
287 let name = match child_module.name() { 202 let name = match child_module.name() {
@@ -302,10 +217,13 @@ impl AnalysisImpl {
302 Ok(None) 217 Ok(None)
303 } 218 }
304 219
305 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { 220 pub(crate) fn find_all_refs(
306 let file = self.db.source_file(position.file_id); 221 &self,
222 position: FilePosition,
223 ) -> Cancelable<Vec<(FileId, TextRange)>> {
224 let file = self.source_file(position.file_id);
307 // Find the binding associated with the offset 225 // Find the binding associated with the offset
308 let (binding, descr) = match find_binding(&self.db, &file, position)? { 226 let (binding, descr) = match find_binding(self, &file, position)? {
309 None => return Ok(Vec::new()), 227 None => return Ok(Vec::new()),
310 Some(it) => it, 228 Some(it) => it,
311 }; 229 };
@@ -317,7 +235,7 @@ impl AnalysisImpl {
317 .collect::<Vec<_>>(); 235 .collect::<Vec<_>>();
318 ret.extend( 236 ret.extend(
319 descr 237 descr
320 .scopes(&*self.db) 238 .scopes(self)
321 .find_all_refs(binding) 239 .find_all_refs(binding)
322 .into_iter() 240 .into_iter()
323 .map(|ref_desc| (position.file_id, ref_desc.range)), 241 .map(|ref_desc| (position.file_id, ref_desc.range)),
@@ -355,8 +273,8 @@ impl AnalysisImpl {
355 Ok(Some((binding, descr))) 273 Ok(Some((binding, descr)))
356 } 274 }
357 } 275 }
358 pub fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> { 276 pub(crate) fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> {
359 let file = self.db.source_file(nav.file_id); 277 let file = self.source_file(nav.file_id);
360 let result = match (nav.symbol.description(&file), nav.symbol.docs(&file)) { 278 let result = match (nav.symbol.description(&file), nav.symbol.docs(&file)) {
361 (Some(desc), Some(docs)) => { 279 (Some(desc), Some(docs)) => {
362 Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs) 280 Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs)
@@ -369,8 +287,8 @@ impl AnalysisImpl {
369 Ok(result) 287 Ok(result)
370 } 288 }
371 289
372 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { 290 pub(crate) fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> {
373 let syntax = self.db.source_file(file_id); 291 let syntax = self.source_file(file_id);
374 292
375 let mut res = ra_editor::diagnostics(&syntax) 293 let mut res = ra_editor::diagnostics(&syntax)
376 .into_iter() 294 .into_iter()
@@ -381,9 +299,9 @@ impl AnalysisImpl {
381 fix: d.fix.map(|fix| SourceChange::from_local_edit(file_id, fix)), 299 fix: d.fix.map(|fix| SourceChange::from_local_edit(file_id, fix)),
382 }) 300 })
383 .collect::<Vec<_>>(); 301 .collect::<Vec<_>>();
384 if let Some(m) = source_binder::module_from_file_id(&*self.db, file_id)? { 302 if let Some(m) = source_binder::module_from_file_id(self, file_id)? {
385 for (name_node, problem) in m.problems(&*self.db) { 303 for (name_node, problem) in m.problems(self) {
386 let source_root = self.db.file_source_root(file_id); 304 let source_root = self.file_source_root(file_id);
387 let diag = match problem { 305 let diag = match problem {
388 Problem::UnresolvedModule { candidate } => { 306 Problem::UnresolvedModule { candidate } => {
389 let create_file = FileSystemEdit::CreateFile { 307 let create_file = FileSystemEdit::CreateFile {
@@ -433,8 +351,8 @@ impl AnalysisImpl {
433 Ok(res) 351 Ok(res)
434 } 352 }
435 353
436 pub fn assists(&self, frange: FileRange) -> Vec<SourceChange> { 354 pub(crate) fn assists(&self, frange: FileRange) -> Vec<SourceChange> {
437 let file = self.file_syntax(frange.file_id); 355 let file = self.source_file(frange.file_id);
438 let offset = frange.range.start(); 356 let offset = frange.range.start();
439 let actions = vec![ 357 let actions = vec![
440 ra_editor::flip_comma(&file, offset).map(|f| f()), 358 ra_editor::flip_comma(&file, offset).map(|f| f()),
@@ -451,11 +369,11 @@ impl AnalysisImpl {
451 .collect() 369 .collect()
452 } 370 }
453 371
454 pub fn resolve_callable( 372 pub(crate) fn resolve_callable(
455 &self, 373 &self,
456 position: FilePosition, 374 position: FilePosition,
457 ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> { 375 ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> {
458 let file = self.db.source_file(position.file_id); 376 let file = self.source_file(position.file_id);
459 let syntax = file.syntax(); 377 let syntax = file.syntax();
460 378
461 // Find the calling expression and it's NameRef 379 // Find the calling expression and it's NameRef
@@ -466,12 +384,12 @@ impl AnalysisImpl {
466 let file_symbols = self.index_resolve(name_ref)?; 384 let file_symbols = self.index_resolve(name_ref)?;
467 for (fn_file_id, fs) in file_symbols { 385 for (fn_file_id, fs) in file_symbols {
468 if fs.kind == FN_DEF { 386 if fs.kind == FN_DEF {
469 let fn_file = self.db.source_file(fn_file_id); 387 let fn_file = self.source_file(fn_file_id);
470 if let Some(fn_def) = find_node_at_offset(fn_file.syntax(), fs.node_range.start()) { 388 if let Some(fn_def) = find_node_at_offset(fn_file.syntax(), fs.node_range.start()) {
471 let descr = ctry!(source_binder::function_from_source( 389 let descr = ctry!(source_binder::function_from_source(
472 &*self.db, fn_file_id, fn_def 390 self, fn_file_id, fn_def
473 )?); 391 )?);
474 if let Some(descriptor) = descr.signature_info(&*self.db) { 392 if let Some(descriptor) = descr.signature_info(self) {
475 // If we have a calling expression let's find which argument we are on 393 // If we have a calling expression let's find which argument we are on
476 let mut current_parameter = None; 394 let mut current_parameter = None;
477 395
@@ -518,20 +436,20 @@ impl AnalysisImpl {
518 Ok(None) 436 Ok(None)
519 } 437 }
520 438
521 pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> { 439 pub(crate) fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> {
522 let file = self.db.source_file(frange.file_id); 440 let file = self.source_file(frange.file_id);
523 let syntax = file.syntax(); 441 let syntax = file.syntax();
524 let node = find_covering_node(syntax, frange.range); 442 let node = find_covering_node(syntax, frange.range);
525 let parent_fn = ctry!(node.ancestors().find_map(FnDef::cast)); 443 let parent_fn = ctry!(node.ancestors().find_map(FnDef::cast));
526 let function = ctry!(source_binder::function_from_source( 444 let function = ctry!(source_binder::function_from_source(
527 &*self.db, 445 self,
528 frange.file_id, 446 frange.file_id,
529 parent_fn 447 parent_fn
530 )?); 448 )?);
531 let infer = function.infer(&*self.db)?; 449 let infer = function.infer(self)?;
532 Ok(infer.type_of_node(node).map(|t| t.to_string())) 450 Ok(infer.type_of_node(node).map(|t| t.to_string()))
533 } 451 }
534 pub fn rename( 452 pub(crate) fn rename(
535 &self, 453 &self,
536 position: FilePosition, 454 position: FilePosition,
537 new_name: &str, 455 new_name: &str,
@@ -555,7 +473,7 @@ impl AnalysisImpl {
555 let mut query = Query::new(name.to_string()); 473 let mut query = Query::new(name.to_string());
556 query.exact(); 474 query.exact();
557 query.limit(4); 475 query.limit(4);
558 self.world_symbols(query) 476 crate::symbol_index::world_symbols(self, query)
559 } 477 }
560} 478}
561 479
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index 9576453ab..54eb2f4d3 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -27,11 +27,9 @@ use ra_syntax::{SourceFileNode, TextRange, TextUnit, SmolStr, SyntaxKind};
27use ra_text_edit::TextEdit; 27use ra_text_edit::TextEdit;
28use rayon::prelude::*; 28use rayon::prelude::*;
29use relative_path::RelativePathBuf; 29use relative_path::RelativePathBuf;
30use salsa::ParallelDatabase;
30 31
31use crate::{ 32use crate::symbol_index::{SymbolIndex, FileSymbol};
32 imp::{AnalysisHostImpl, AnalysisImpl},
33 symbol_index::{SymbolIndex, FileSymbol},
34};
35 33
36pub use crate::{ 34pub use crate::{
37 completion::{CompletionItem, CompletionItemKind, InsertText}, 35 completion::{CompletionItem, CompletionItemKind, InsertText},
@@ -44,7 +42,7 @@ pub use hir::FnSignatureInfo;
44 42
45pub use ra_db::{ 43pub use ra_db::{
46 Canceled, Cancelable, FilePosition, FileRange, 44 Canceled, Cancelable, FilePosition, FileRange,
47 CrateGraph, CrateId, SourceRootId, FileId 45 CrateGraph, CrateId, SourceRootId, FileId, SyntaxDatabase, FilesDatabase
48}; 46};
49 47
50#[derive(Default)] 48#[derive(Default)]
@@ -150,27 +148,6 @@ impl AnalysisChange {
150 } 148 }
151} 149}
152 150
153/// `AnalysisHost` stores the current state of the world.
154#[derive(Debug, Default)]
155pub struct AnalysisHost {
156 imp: AnalysisHostImpl,
157}
158
159impl AnalysisHost {
160 /// Returns a snapshot of the current state, which you can query for
161 /// semantic information.
162 pub fn analysis(&self) -> Analysis {
163 Analysis {
164 imp: self.imp.analysis(),
165 }
166 }
167 /// Applies changes to the current state of the world. If there are
168 /// outstanding snapshots, they will be canceled.
169 pub fn apply_change(&mut self, change: AnalysisChange) {
170 self.imp.apply_change(change)
171 }
172}
173
174#[derive(Debug)] 151#[derive(Debug)]
175pub struct SourceChange { 152pub struct SourceChange {
176 pub label: String, 153 pub label: String,
@@ -287,124 +264,178 @@ impl ReferenceResolution {
287 } 264 }
288} 265}
289 266
267/// `AnalysisHost` stores the current state of the world.
268#[derive(Debug, Default)]
269pub struct AnalysisHost {
270 db: db::RootDatabase,
271}
272
273impl AnalysisHost {
274 /// Returns a snapshot of the current state, which you can query for
275 /// semantic information.
276 pub fn analysis(&self) -> Analysis {
277 Analysis {
278 db: self.db.snapshot(),
279 }
280 }
281 /// Applies changes to the current state of the world. If there are
282 /// outstanding snapshots, they will be canceled.
283 pub fn apply_change(&mut self, change: AnalysisChange) {
284 self.db.apply_change(change)
285 }
286}
287
290/// Analysis is a snapshot of a world state at a moment in time. It is the main 288/// Analysis is a snapshot of a world state at a moment in time. It is the main
291/// entry point for asking semantic information about the world. When the world 289/// entry point for asking semantic information about the world. When the world
292/// state is advanced using `AnalysisHost::apply_change` method, all existing 290/// state is advanced using `AnalysisHost::apply_change` method, all existing
293/// `Analysis` are canceled (most method return `Err(Canceled)`). 291/// `Analysis` are canceled (most method return `Err(Canceled)`).
294#[derive(Debug)] 292#[derive(Debug)]
295pub struct Analysis { 293pub struct Analysis {
296 pub(crate) imp: AnalysisImpl, 294 db: salsa::Snapshot<db::RootDatabase>,
297} 295}
298 296
299impl Analysis { 297impl Analysis {
298 /// Gets the text of the source file.
300 pub fn file_text(&self, file_id: FileId) -> Arc<String> { 299 pub fn file_text(&self, file_id: FileId) -> Arc<String> {
301 self.imp.file_text(file_id) 300 self.db.file_text(file_id)
302 } 301 }
302 /// Gets the syntax tree of the file.
303 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { 303 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode {
304 self.imp.file_syntax(file_id).clone() 304 self.db.source_file(file_id).clone()
305 } 305 }
306 /// Gets the file's `LineIndex`: data structure to convert between absolute
307 /// offsets and line/column representation.
306 pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> { 308 pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> {
307 self.imp.file_line_index(file_id) 309 self.db.file_lines(file_id)
308 } 310 }
311 /// Selects the next syntactic nodes encopasing the range.
309 pub fn extend_selection(&self, frange: FileRange) -> TextRange { 312 pub fn extend_selection(&self, frange: FileRange) -> TextRange {
310 extend_selection::extend_selection(&self.imp.db, frange) 313 extend_selection::extend_selection(&self.db, frange)
311 } 314 }
315 /// Returns position of the mathcing brace (all types of braces are
316 /// supported).
312 pub fn matching_brace(&self, file: &SourceFileNode, offset: TextUnit) -> Option<TextUnit> { 317 pub fn matching_brace(&self, file: &SourceFileNode, offset: TextUnit) -> Option<TextUnit> {
313 ra_editor::matching_brace(file, offset) 318 ra_editor::matching_brace(file, offset)
314 } 319 }
320 /// Returns a syntax tree represented as `String`, for debug purposes.
321 // FIXME: use a better name here.
315 pub fn syntax_tree(&self, file_id: FileId) -> String { 322 pub fn syntax_tree(&self, file_id: FileId) -> String {
316 let file = self.imp.file_syntax(file_id); 323 let file = self.db.source_file(file_id);
317 ra_editor::syntax_tree(&file) 324 ra_editor::syntax_tree(&file)
318 } 325 }
326 /// Returns an edit to remove all newlines in the range, cleaning up minor
327 /// stuff like trailing commas.
319 pub fn join_lines(&self, frange: FileRange) -> SourceChange { 328 pub fn join_lines(&self, frange: FileRange) -> SourceChange {
320 let file = self.imp.file_syntax(frange.file_id); 329 let file = self.db.source_file(frange.file_id);
321 SourceChange::from_local_edit(frange.file_id, ra_editor::join_lines(&file, frange.range)) 330 SourceChange::from_local_edit(frange.file_id, ra_editor::join_lines(&file, frange.range))
322 } 331 }
332 /// Returns an edit which should be applied when opening a new line, fixing
333 /// up minor stuff like continuing the comment.
323 pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> { 334 pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> {
324 let file = self.imp.file_syntax(position.file_id); 335 let file = self.db.source_file(position.file_id);
325 let edit = ra_editor::on_enter(&file, position.offset)?; 336 let edit = ra_editor::on_enter(&file, position.offset)?;
326 let res = SourceChange::from_local_edit(position.file_id, edit); 337 Some(SourceChange::from_local_edit(position.file_id, edit))
327 Some(res)
328 } 338 }
339 /// Returns an edit which should be applied after `=` was typed. Primaraly,
340 /// this works when adding `let =`.
341 // FIXME: use a snippet completion instead of this hack here.
329 pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> { 342 pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> {
330 let file = self.imp.file_syntax(position.file_id); 343 let file = self.db.source_file(position.file_id);
331 Some(SourceChange::from_local_edit( 344 let edit = ra_editor::on_eq_typed(&file, position.offset)?;
332 position.file_id, 345 Some(SourceChange::from_local_edit(position.file_id, edit))
333 ra_editor::on_eq_typed(&file, position.offset)?,
334 ))
335 } 346 }
347 /// Returns a tree representation of symbols in the file. Useful to draw a
348 /// file outline.
336 pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> { 349 pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> {
337 let file = self.imp.file_syntax(file_id); 350 let file = self.db.source_file(file_id);
338 ra_editor::file_structure(&file) 351 ra_editor::file_structure(&file)
339 } 352 }
353 /// Returns the set of folding ranges.
340 pub fn folding_ranges(&self, file_id: FileId) -> Vec<Fold> { 354 pub fn folding_ranges(&self, file_id: FileId) -> Vec<Fold> {
341 let file = self.imp.file_syntax(file_id); 355 let file = self.db.source_file(file_id);
342 ra_editor::folding_ranges(&file) 356 ra_editor::folding_ranges(&file)
343 } 357 }
358 /// Fuzzy searches for a symbol.
344 pub fn symbol_search(&self, query: Query) -> Cancelable<Vec<NavigationTarget>> { 359 pub fn symbol_search(&self, query: Query) -> Cancelable<Vec<NavigationTarget>> {
345 let res = self 360 let res = symbol_index::world_symbols(&*self.db, query)?
346 .imp
347 .world_symbols(query)?
348 .into_iter() 361 .into_iter()
349 .map(|(file_id, symbol)| NavigationTarget { file_id, symbol }) 362 .map(|(file_id, symbol)| NavigationTarget { file_id, symbol })
350 .collect(); 363 .collect();
351 Ok(res) 364 Ok(res)
352 } 365 }
366 /// Resolves reference to definition, but does not gurantee correctness.
353 pub fn approximately_resolve_symbol( 367 pub fn approximately_resolve_symbol(
354 &self, 368 &self,
355 position: FilePosition, 369 position: FilePosition,
356 ) -> Cancelable<Option<ReferenceResolution>> { 370 ) -> Cancelable<Option<ReferenceResolution>> {
357 self.imp.approximately_resolve_symbol(position) 371 self.db.approximately_resolve_symbol(position)
358 } 372 }
373 /// Finds all usages of the reference at point.
359 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { 374 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> {
360 self.imp.find_all_refs(position) 375 self.db.find_all_refs(position)
361 } 376 }
377 /// Returns documentation string for a given target.
362 pub fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> { 378 pub fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> {
363 self.imp.doc_text_for(nav) 379 self.db.doc_text_for(nav)
364 } 380 }
381 /// Returns a `mod name;` declaration whihc created the current module.
365 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> { 382 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> {
366 self.imp.parent_module(position) 383 self.db.parent_module(position)
367 } 384 }
385 /// Returns `::` separated path to the current module from the crate root.
368 pub fn module_path(&self, position: FilePosition) -> Cancelable<Option<String>> { 386 pub fn module_path(&self, position: FilePosition) -> Cancelable<Option<String>> {
369 self.imp.module_path(position) 387 self.db.module_path(position)
370 } 388 }
389 /// Returns crates this file belongs too.
371 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { 390 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
372 self.imp.crate_for(file_id) 391 self.db.crate_for(file_id)
373 } 392 }
393 /// Returns the root file of the given crate.
374 pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> { 394 pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> {
375 Ok(self.imp.crate_root(crate_id)) 395 Ok(self.db.crate_root(crate_id))
376 } 396 }
397 /// Returns the set of possible targets to run for the current file.
377 pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> { 398 pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> {
378 let file = self.imp.file_syntax(file_id); 399 let file = self.db.source_file(file_id);
379 Ok(runnables::runnables(self, &file, file_id)) 400 Ok(runnables::runnables(self, &file, file_id))
380 } 401 }
402 /// Computes syntax highlighting for the given file.
381 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { 403 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
382 syntax_highlighting::highlight(&*self.imp.db, file_id) 404 syntax_highlighting::highlight(&*self.db, file_id)
383 } 405 }
406 /// Computes completions at the given position.
384 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> { 407 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> {
385 self.imp.completions(position) 408 let completions = completion::completions(&self.db, position)?;
409 Ok(completions.map(|it| it.into()))
386 } 410 }
411 /// Computes assists (aks code actons aka intentions) for the given
412 /// position.
387 pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<SourceChange>> { 413 pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<SourceChange>> {
388 Ok(self.imp.assists(frange)) 414 Ok(self.db.assists(frange))
389 } 415 }
416 /// Computes the set of diagnostics for the given file.
390 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { 417 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> {
391 self.imp.diagnostics(file_id) 418 self.db.diagnostics(file_id)
392 } 419 }
420 /// Computes parameter information for the given call expression.
393 pub fn resolve_callable( 421 pub fn resolve_callable(
394 &self, 422 &self,
395 position: FilePosition, 423 position: FilePosition,
396 ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> { 424 ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> {
397 self.imp.resolve_callable(position) 425 self.db.resolve_callable(position)
398 } 426 }
427 /// Computes the type of the expression at the given position.
399 pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> { 428 pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> {
400 self.imp.type_of(frange) 429 self.db.type_of(frange)
401 } 430 }
431 /// Returns the edit required to rename reference at the position to the new
432 /// name.
402 pub fn rename( 433 pub fn rename(
403 &self, 434 &self,
404 position: FilePosition, 435 position: FilePosition,
405 new_name: &str, 436 new_name: &str,
406 ) -> Cancelable<Vec<SourceFileEdit>> { 437 ) -> Cancelable<Vec<SourceFileEdit>> {
407 self.imp.rename(position, new_name) 438 self.db.rename(position, new_name)
408 } 439 }
409} 440}
410 441
diff --git a/crates/ra_analysis/src/symbol_index.rs b/crates/ra_analysis/src/symbol_index.rs
index 56a84a850..ddcf3d052 100644
--- a/crates/ra_analysis/src/symbol_index.rs
+++ b/crates/ra_analysis/src/symbol_index.rs
@@ -10,12 +10,13 @@ use ra_syntax::{
10 SyntaxKind::{self, *}, 10 SyntaxKind::{self, *},
11 ast::{self, NameOwner, DocCommentsOwner}, 11 ast::{self, NameOwner, DocCommentsOwner},
12}; 12};
13use ra_db::{SyntaxDatabase, SourceRootId}; 13use ra_db::{SyntaxDatabase, SourceRootId, FilesDatabase};
14use salsa::ParallelDatabase;
14use rayon::prelude::*; 15use rayon::prelude::*;
15 16
16use crate::{ 17use crate::{
17 Cancelable, 18 Cancelable, FileId, Query,
18 FileId, Query, 19 db::RootDatabase,
19}; 20};
20 21
21salsa::query_group! { 22salsa::query_group! {
@@ -36,6 +37,41 @@ fn file_symbols(db: &impl SyntaxDatabase, file_id: FileId) -> Cancelable<Arc<Sym
36 Ok(Arc::new(SymbolIndex::for_file(file_id, syntax))) 37 Ok(Arc::new(SymbolIndex::for_file(file_id, syntax)))
37} 38}
38 39
40pub(crate) fn world_symbols(
41 db: &RootDatabase,
42 query: Query,
43) -> Cancelable<Vec<(FileId, FileSymbol)>> {
44 /// Need to wrap Snapshot to provide `Clone` impl for `map_with`
45 struct Snap(salsa::Snapshot<RootDatabase>);
46 impl Clone for Snap {
47 fn clone(&self) -> Snap {
48 Snap(self.0.snapshot())
49 }
50 }
51
52 let buf: Vec<Arc<SymbolIndex>> = if query.libs {
53 let snap = Snap(db.snapshot());
54 db.library_roots()
55 .par_iter()
56 .map_with(snap, |db, &lib_id| db.0.library_symbols(lib_id))
57 .collect()
58 } else {
59 let mut files = Vec::new();
60 for &root in db.local_roots().iter() {
61 let sr = db.source_root(root);
62 files.extend(sr.files.values().map(|&it| it))
63 }
64
65 let snap = Snap(db.snapshot());
66 files
67 .par_iter()
68 .map_with(snap, |db, &file_id| db.0.file_symbols(file_id))
69 .filter_map(|it| it.ok())
70 .collect()
71 };
72 Ok(query.search(&buf))
73}
74
39#[derive(Default, Debug)] 75#[derive(Default, Debug)]
40pub(crate) struct SymbolIndex { 76pub(crate) struct SymbolIndex {
41 symbols: Vec<(FileId, FileSymbol)>, 77 symbols: Vec<(FileId, FileSymbol)>,