aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src')
-rw-r--r--crates/ra_analysis/src/completion.rs2
-rw-r--r--crates/ra_analysis/src/completion/complete_scope.rs2
-rw-r--r--crates/ra_analysis/src/db.rs16
-rw-r--r--crates/ra_analysis/src/extend_selection.rs10
-rw-r--r--crates/ra_analysis/src/imp.rs255
-rw-r--r--crates/ra_analysis/src/lib.rs194
-rw-r--r--crates/ra_analysis/src/macros.rs75
-rw-r--r--crates/ra_analysis/src/runnables.rs106
-rw-r--r--crates/ra_analysis/src/symbol_index.rs165
-rw-r--r--crates/ra_analysis/src/syntax_highlighting.rs30
10 files changed, 462 insertions, 393 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/completion/complete_scope.rs b/crates/ra_analysis/src/completion/complete_scope.rs
index daf666505..4dead3689 100644
--- a/crates/ra_analysis/src/completion/complete_scope.rs
+++ b/crates/ra_analysis/src/completion/complete_scope.rs
@@ -27,7 +27,7 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) ->
27 match res.import { 27 match res.import {
28 None => true, 28 None => true,
29 Some(import) => { 29 Some(import) => {
30 let range = import.range(ctx.db, module.source().file_id()); 30 let range = import.range(ctx.db, module.file_id());
31 !range.is_subrange(&ctx.leaf.range()) 31 !range.is_subrange(&ctx.leaf.range())
32 } 32 }
33 } 33 }
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs
index b072a5eba..d7740f0c4 100644
--- a/crates/ra_analysis/src/db.rs
+++ b/crates/ra_analysis/src/db.rs
@@ -1,7 +1,6 @@
1use std::{fmt, sync::Arc}; 1use std::{fmt, sync::Arc};
2use salsa::{self, Database}; 2use salsa::{self, Database};
3use ra_db::{LocationIntener, BaseDatabase}; 3use ra_db::{LocationIntener, BaseDatabase};
4use hir::{self, DefId, DefLoc};
5 4
6use crate::{ 5use crate::{
7 symbol_index, 6 symbol_index,
@@ -15,7 +14,8 @@ pub(crate) struct RootDatabase {
15 14
16#[derive(Default)] 15#[derive(Default)]
17struct IdMaps { 16struct IdMaps {
18 defs: LocationIntener<DefLoc, DefId>, 17 defs: LocationIntener<hir::DefLoc, hir::DefId>,
18 macros: LocationIntener<hir::MacroCallLoc, hir::MacroCallId>,
19} 19}
20 20
21impl fmt::Debug for IdMaps { 21impl fmt::Debug for IdMaps {
@@ -59,12 +59,18 @@ impl salsa::ParallelDatabase for RootDatabase {
59 59
60impl BaseDatabase for RootDatabase {} 60impl BaseDatabase for RootDatabase {}
61 61
62impl AsRef<LocationIntener<DefLoc, DefId>> for RootDatabase { 62impl AsRef<LocationIntener<hir::DefLoc, hir::DefId>> for RootDatabase {
63 fn as_ref(&self) -> &LocationIntener<DefLoc, DefId> { 63 fn as_ref(&self) -> &LocationIntener<hir::DefLoc, hir::DefId> {
64 &self.id_maps.defs 64 &self.id_maps.defs
65 } 65 }
66} 66}
67 67
68impl AsRef<LocationIntener<hir::MacroCallLoc, hir::MacroCallId>> for RootDatabase {
69 fn as_ref(&self) -> &LocationIntener<hir::MacroCallLoc, hir::MacroCallId> {
70 &self.id_maps.macros
71 }
72}
73
68salsa::database_storage! { 74salsa::database_storage! {
69 pub(crate) struct RootDatabaseStorage for RootDatabase { 75 pub(crate) struct RootDatabaseStorage for RootDatabase {
70 impl ra_db::FilesDatabase { 76 impl ra_db::FilesDatabase {
@@ -85,6 +91,8 @@ salsa::database_storage! {
85 fn library_symbols() for symbol_index::LibrarySymbolsQuery; 91 fn library_symbols() for symbol_index::LibrarySymbolsQuery;
86 } 92 }
87 impl hir::db::HirDatabase { 93 impl hir::db::HirDatabase {
94 fn hir_source_file() for hir::db::HirSourceFileQuery;
95 fn expand_macro_invocation() for hir::db::ExpandMacroCallQuery;
88 fn module_tree() for hir::db::ModuleTreeQuery; 96 fn module_tree() for hir::db::ModuleTreeQuery;
89 fn fn_scopes() for hir::db::FnScopesQuery; 97 fn fn_scopes() for hir::db::FnScopesQuery;
90 fn file_items() for hir::db::SourceFileItemsQuery; 98 fn file_items() for hir::db::SourceFileItemsQuery;
diff --git a/crates/ra_analysis/src/extend_selection.rs b/crates/ra_analysis/src/extend_selection.rs
index 805e9059e..f1b77f981 100644
--- a/crates/ra_analysis/src/extend_selection.rs
+++ b/crates/ra_analysis/src/extend_selection.rs
@@ -18,15 +18,15 @@ pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRang
18} 18}
19 19
20fn extend_selection_in_macro( 20fn extend_selection_in_macro(
21 db: &RootDatabase, 21 _db: &RootDatabase,
22 source_file: &SourceFileNode, 22 source_file: &SourceFileNode,
23 frange: FileRange, 23 frange: FileRange,
24) -> Option<TextRange> { 24) -> Option<TextRange> {
25 let macro_call = find_macro_call(source_file.syntax(), frange.range)?; 25 let macro_call = find_macro_call(source_file.syntax(), frange.range)?;
26 let exp = crate::macros::expand(db, frange.file_id, macro_call)?; 26 let (off, exp) = hir::MacroDef::ast_expand(macro_call)?;
27 let dst_range = exp.map_range_forward(frange.range)?; 27 let dst_range = exp.map_range_forward(frange.range - off)?;
28 let dst_range = ra_editor::extend_selection(exp.source_file().syntax(), dst_range)?; 28 let dst_range = ra_editor::extend_selection(exp.syntax().borrowed(), dst_range)?;
29 let src_range = exp.map_range_back(dst_range)?; 29 let src_range = exp.map_range_back(dst_range)? + off;
30 Some(src_range) 30 Some(src_range)
31} 31}
32 32
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index 5669aa94d..ff13247de 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, FileSymbol, 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},
@@ -21,39 +17,26 @@ use ra_syntax::{
21 17
22use crate::{ 18use crate::{
23 AnalysisChange, 19 AnalysisChange,
24 Cancelable, 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}, 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,147 +90,67 @@ 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>> {
186 let descr = match source_binder::module_from_position(&*self.db, position)? {
187 None => return Ok(None),
188 Some(it) => it,
189 };
190 let name = match descr.name() {
191 None => return Ok(None),
192 Some(it) => it.to_string(),
193 };
194
195 let modules = descr.path_to_root();
196
197 let path = modules
198 .iter()
199 .filter_map(|s| s.name())
200 .skip(1) // name is already part of the string.
201 .fold(name, |path, it| format!("{}::{}", it, path));
202
203 Ok(Some(path.to_string()))
204 }
205
206 /// This returns `Vec` because a module may be included from several places. We 103 /// 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. 104 /// 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<(FileId, FileSymbol)>> { 105 pub(crate) fn parent_module(
209 let descr = match source_binder::module_from_position(&*self.db, position)? { 106 &self,
107 position: FilePosition,
108 ) -> Cancelable<Vec<NavigationTarget>> {
109 let descr = match source_binder::module_from_position(self, position)? {
210 None => return Ok(Vec::new()), 110 None => return Ok(Vec::new()),
211 Some(it) => it, 111 Some(it) => it,
212 }; 112 };
213 let (file_id, decl) = match descr.parent_link_source(&*self.db) { 113 let (file_id, decl) = match descr.parent_link_source(self) {
214 None => return Ok(Vec::new()), 114 None => return Ok(Vec::new()),
215 Some(it) => it, 115 Some(it) => it,
216 }; 116 };
217 let decl = decl.borrowed(); 117 let decl = decl.borrowed();
218 let decl_name = decl.name().unwrap(); 118 let decl_name = decl.name().unwrap();
219 let sym = FileSymbol { 119 let symbol = FileSymbol {
220 name: decl_name.text(), 120 name: decl_name.text(),
221 node_range: decl_name.syntax().range(), 121 node_range: decl_name.syntax().range(),
222 kind: MODULE, 122 kind: MODULE,
223 }; 123 };
224 Ok(vec![(file_id, sym)]) 124 Ok(vec![NavigationTarget { file_id, symbol }])
225 } 125 }
226 /// Returns `Vec` for the same reason as `parent_module` 126 /// Returns `Vec` for the same reason as `parent_module`
227 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { 127 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)? { 128 let descr = match source_binder::module_from_file_id(self, file_id)? {
229 None => return Ok(Vec::new()), 129 None => return Ok(Vec::new()),
230 Some(it) => it, 130 Some(it) => it,
231 }; 131 };
232 let root = descr.crate_root(); 132 let root = descr.crate_root();
233 let file_id = root.source().file_id(); 133 let file_id = root.file_id();
234 134
235 let crate_graph = self.db.crate_graph(); 135 let crate_graph = self.crate_graph();
236 let crate_id = crate_graph.crate_id_for_crate_root(file_id); 136 let crate_id = crate_graph.crate_id_for_crate_root(file_id);
237 Ok(crate_id.into_iter().collect()) 137 Ok(crate_id.into_iter().collect())
238 } 138 }
239 pub fn crate_root(&self, crate_id: CrateId) -> FileId { 139 pub(crate) fn crate_root(&self, crate_id: CrateId) -> FileId {
240 self.db.crate_graph().crate_root(crate_id) 140 self.crate_graph().crate_root(crate_id)
241 }
242 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> {
243 let completions = completions(&self.db, position)?;
244 Ok(completions.map(|it| it.into()))
245 } 141 }
246 pub fn approximately_resolve_symbol( 142 pub(crate) fn approximately_resolve_symbol(
247 &self, 143 &self,
248 position: FilePosition, 144 position: FilePosition,
249 ) -> Cancelable<Option<ReferenceResolution>> { 145 ) -> Cancelable<Option<ReferenceResolution>> {
250 let file = self.db.source_file(position.file_id); 146 let file = self.source_file(position.file_id);
251 let syntax = file.syntax(); 147 let syntax = file.syntax();
252 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) { 148 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) {
253 let mut rr = ReferenceResolution::new(name_ref.syntax().range()); 149 let mut rr = ReferenceResolution::new(name_ref.syntax().range());
254 if let Some(fn_descr) = source_binder::function_from_child_node( 150 if let Some(fn_descr) =
255 &*self.db, 151 source_binder::function_from_child_node(self, position.file_id, name_ref.syntax())?
256 position.file_id, 152 {
257 name_ref.syntax(), 153 let scope = fn_descr.scopes(self);
258 )? {
259 let scope = fn_descr.scopes(&*self.db);
260 // First try to resolve the symbol locally 154 // First try to resolve the symbol locally
261 if let Some(entry) = scope.resolve_local_name(name_ref) { 155 if let Some(entry) = scope.resolve_local_name(name_ref) {
262 rr.add_resolution( 156 rr.add_resolution(
@@ -281,9 +175,9 @@ impl AnalysisImpl {
281 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { 175 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
282 if module.has_semi() { 176 if module.has_semi() {
283 if let Some(child_module) = 177 if let Some(child_module) =
284 source_binder::module_from_declaration(&*self.db, position.file_id, module)? 178 source_binder::module_from_declaration(self, position.file_id, module)?
285 { 179 {
286 let file_id = child_module.source().file_id(); 180 let file_id = child_module.file_id();
287 let name = match child_module.name() { 181 let name = match child_module.name() {
288 Some(name) => name.to_string().into(), 182 Some(name) => name.to_string().into(),
289 None => "".into(), 183 None => "".into(),
@@ -302,10 +196,13 @@ impl AnalysisImpl {
302 Ok(None) 196 Ok(None)
303 } 197 }
304 198
305 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { 199 pub(crate) fn find_all_refs(
306 let file = self.db.source_file(position.file_id); 200 &self,
201 position: FilePosition,
202 ) -> Cancelable<Vec<(FileId, TextRange)>> {
203 let file = self.source_file(position.file_id);
307 // Find the binding associated with the offset 204 // Find the binding associated with the offset
308 let (binding, descr) = match find_binding(&self.db, &file, position)? { 205 let (binding, descr) = match find_binding(self, &file, position)? {
309 None => return Ok(Vec::new()), 206 None => return Ok(Vec::new()),
310 Some(it) => it, 207 Some(it) => it,
311 }; 208 };
@@ -317,7 +214,7 @@ impl AnalysisImpl {
317 .collect::<Vec<_>>(); 214 .collect::<Vec<_>>();
318 ret.extend( 215 ret.extend(
319 descr 216 descr
320 .scopes(&*self.db) 217 .scopes(self)
321 .find_all_refs(binding) 218 .find_all_refs(binding)
322 .into_iter() 219 .into_iter()
323 .map(|ref_desc| (position.file_id, ref_desc.range)), 220 .map(|ref_desc| (position.file_id, ref_desc.range)),
@@ -355,9 +252,9 @@ impl AnalysisImpl {
355 Ok(Some((binding, descr))) 252 Ok(Some((binding, descr)))
356 } 253 }
357 } 254 }
358 pub fn doc_text_for(&self, file_id: FileId, symbol: FileSymbol) -> Cancelable<Option<String>> { 255 pub(crate) fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> {
359 let file = self.db.source_file(file_id); 256 let file = self.source_file(nav.file_id);
360 let result = match (symbol.description(&file), symbol.docs(&file)) { 257 let result = match (nav.symbol.description(&file), nav.symbol.docs(&file)) {
361 (Some(desc), Some(docs)) => { 258 (Some(desc), Some(docs)) => {
362 Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs) 259 Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs)
363 } 260 }
@@ -369,8 +266,8 @@ impl AnalysisImpl {
369 Ok(result) 266 Ok(result)
370 } 267 }
371 268
372 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { 269 pub(crate) fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> {
373 let syntax = self.db.source_file(file_id); 270 let syntax = self.source_file(file_id);
374 271
375 let mut res = ra_editor::diagnostics(&syntax) 272 let mut res = ra_editor::diagnostics(&syntax)
376 .into_iter() 273 .into_iter()
@@ -381,9 +278,9 @@ impl AnalysisImpl {
381 fix: d.fix.map(|fix| SourceChange::from_local_edit(file_id, fix)), 278 fix: d.fix.map(|fix| SourceChange::from_local_edit(file_id, fix)),
382 }) 279 })
383 .collect::<Vec<_>>(); 280 .collect::<Vec<_>>();
384 if let Some(m) = source_binder::module_from_file_id(&*self.db, file_id)? { 281 if let Some(m) = source_binder::module_from_file_id(self, file_id)? {
385 for (name_node, problem) in m.problems(&*self.db) { 282 for (name_node, problem) in m.problems(self) {
386 let source_root = self.db.file_source_root(file_id); 283 let source_root = self.file_source_root(file_id);
387 let diag = match problem { 284 let diag = match problem {
388 Problem::UnresolvedModule { candidate } => { 285 Problem::UnresolvedModule { candidate } => {
389 let create_file = FileSystemEdit::CreateFile { 286 let create_file = FileSystemEdit::CreateFile {
@@ -433,8 +330,8 @@ impl AnalysisImpl {
433 Ok(res) 330 Ok(res)
434 } 331 }
435 332
436 pub fn assists(&self, frange: FileRange) -> Vec<SourceChange> { 333 pub(crate) fn assists(&self, frange: FileRange) -> Vec<SourceChange> {
437 let file = self.file_syntax(frange.file_id); 334 let file = self.source_file(frange.file_id);
438 let offset = frange.range.start(); 335 let offset = frange.range.start();
439 let actions = vec![ 336 let actions = vec![
440 ra_editor::flip_comma(&file, offset).map(|f| f()), 337 ra_editor::flip_comma(&file, offset).map(|f| f()),
@@ -451,11 +348,11 @@ impl AnalysisImpl {
451 .collect() 348 .collect()
452 } 349 }
453 350
454 pub fn resolve_callable( 351 pub(crate) fn resolve_callable(
455 &self, 352 &self,
456 position: FilePosition, 353 position: FilePosition,
457 ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> { 354 ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> {
458 let file = self.db.source_file(position.file_id); 355 let file = self.source_file(position.file_id);
459 let syntax = file.syntax(); 356 let syntax = file.syntax();
460 357
461 // Find the calling expression and it's NameRef 358 // Find the calling expression and it's NameRef
@@ -466,12 +363,12 @@ impl AnalysisImpl {
466 let file_symbols = self.index_resolve(name_ref)?; 363 let file_symbols = self.index_resolve(name_ref)?;
467 for (fn_file_id, fs) in file_symbols { 364 for (fn_file_id, fs) in file_symbols {
468 if fs.kind == FN_DEF { 365 if fs.kind == FN_DEF {
469 let fn_file = self.db.source_file(fn_file_id); 366 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()) { 367 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( 368 let descr = ctry!(source_binder::function_from_source(
472 &*self.db, fn_file_id, fn_def 369 self, fn_file_id, fn_def
473 )?); 370 )?);
474 if let Some(descriptor) = descr.signature_info(&*self.db) { 371 if let Some(descriptor) = descr.signature_info(self) {
475 // If we have a calling expression let's find which argument we are on 372 // If we have a calling expression let's find which argument we are on
476 let mut current_parameter = None; 373 let mut current_parameter = None;
477 374
@@ -518,20 +415,20 @@ impl AnalysisImpl {
518 Ok(None) 415 Ok(None)
519 } 416 }
520 417
521 pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> { 418 pub(crate) fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> {
522 let file = self.db.source_file(frange.file_id); 419 let file = self.source_file(frange.file_id);
523 let syntax = file.syntax(); 420 let syntax = file.syntax();
524 let node = find_covering_node(syntax, frange.range); 421 let node = find_covering_node(syntax, frange.range);
525 let parent_fn = ctry!(node.ancestors().find_map(FnDef::cast)); 422 let parent_fn = ctry!(node.ancestors().find_map(FnDef::cast));
526 let function = ctry!(source_binder::function_from_source( 423 let function = ctry!(source_binder::function_from_source(
527 &*self.db, 424 self,
528 frange.file_id, 425 frange.file_id,
529 parent_fn 426 parent_fn
530 )?); 427 )?);
531 let infer = function.infer(&*self.db)?; 428 let infer = function.infer(self)?;
532 Ok(infer.type_of_node(node).map(|t| t.to_string())) 429 Ok(infer.type_of_node(node).map(|t| t.to_string()))
533 } 430 }
534 pub fn rename( 431 pub(crate) fn rename(
535 &self, 432 &self,
536 position: FilePosition, 433 position: FilePosition,
537 new_name: &str, 434 new_name: &str,
@@ -555,7 +452,7 @@ impl AnalysisImpl {
555 let mut query = Query::new(name.to_string()); 452 let mut query = Query::new(name.to_string());
556 query.exact(); 453 query.exact();
557 query.limit(4); 454 query.limit(4);
558 self.world_symbols(query) 455 crate::symbol_index::world_symbols(self, query)
559 } 456 }
560} 457}
561 458
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index e6cfaecc3..a01febf4e 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -19,33 +19,30 @@ mod runnables;
19 19
20mod extend_selection; 20mod extend_selection;
21mod syntax_highlighting; 21mod syntax_highlighting;
22mod macros;
23 22
24use std::{fmt, sync::Arc}; 23use std::{fmt, sync::Arc};
25 24
26use rustc_hash::FxHashMap; 25use rustc_hash::FxHashMap;
27use ra_syntax::{SourceFileNode, TextRange, TextUnit}; 26use ra_syntax::{SourceFileNode, TextRange, TextUnit, SmolStr, SyntaxKind};
28use ra_text_edit::TextEdit; 27use ra_text_edit::TextEdit;
29use rayon::prelude::*; 28use rayon::prelude::*;
30use relative_path::RelativePathBuf; 29use relative_path::RelativePathBuf;
30use salsa::ParallelDatabase;
31 31
32use crate::{ 32use crate::symbol_index::{SymbolIndex, FileSymbol};
33 imp::{AnalysisHostImpl, AnalysisImpl},
34 symbol_index::SymbolIndex,
35};
36 33
37pub use crate::{ 34pub use crate::{
38 completion::{CompletionItem, CompletionItemKind, InsertText}, 35 completion::{CompletionItem, CompletionItemKind, InsertText},
39 runnables::{Runnable, RunnableKind} 36 runnables::{Runnable, RunnableKind},
40}; 37};
41pub use ra_editor::{ 38pub use ra_editor::{
42 FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, StructureNode, Severity 39 Fold, FoldKind, HighlightedRange, LineIndex, StructureNode, Severity
43}; 40};
44pub use hir::FnSignatureInfo; 41pub use hir::FnSignatureInfo;
45 42
46pub use ra_db::{ 43pub use ra_db::{
47 Canceled, Cancelable, FilePosition, FileRange, 44 Canceled, Cancelable, FilePosition, FileRange,
48 CrateGraph, CrateId, SourceRootId, FileId 45 CrateGraph, CrateId, SourceRootId, FileId, SyntaxDatabase, FilesDatabase
49}; 46};
50 47
51#[derive(Default)] 48#[derive(Default)]
@@ -151,27 +148,6 @@ impl AnalysisChange {
151 } 148 }
152} 149}
153 150
154/// `AnalysisHost` stores the current state of the world.
155#[derive(Debug, Default)]
156pub struct AnalysisHost {
157 imp: AnalysisHostImpl,
158}
159
160impl AnalysisHost {
161 /// Returns a snapshot of the current state, which you can query for
162 /// semantic information.
163 pub fn analysis(&self) -> Analysis {
164 Analysis {
165 imp: self.imp.analysis(),
166 }
167 }
168 /// Applies changes to the current state of the world. If there are
169 /// outstanding snapshots, they will be canceled.
170 pub fn apply_change(&mut self, change: AnalysisChange) {
171 self.imp.apply_change(change)
172 }
173}
174
175#[derive(Debug)] 151#[derive(Debug)]
176pub struct SourceChange { 152pub struct SourceChange {
177 pub label: String, 153 pub label: String,
@@ -243,6 +219,27 @@ impl Query {
243 } 219 }
244} 220}
245 221
222#[derive(Debug)]
223pub struct NavigationTarget {
224 file_id: FileId,
225 symbol: FileSymbol,
226}
227
228impl NavigationTarget {
229 pub fn name(&self) -> SmolStr {
230 self.symbol.name.clone()
231 }
232 pub fn kind(&self) -> SyntaxKind {
233 self.symbol.kind
234 }
235 pub fn file_id(&self) -> FileId {
236 self.file_id
237 }
238 pub fn range(&self) -> TextRange {
239 self.symbol.node_range
240 }
241}
242
246/// Result of "goto def" query. 243/// Result of "goto def" query.
247#[derive(Debug)] 244#[derive(Debug)]
248pub struct ReferenceResolution { 245pub struct ReferenceResolution {
@@ -251,7 +248,7 @@ pub struct ReferenceResolution {
251 /// client where the reference was. 248 /// client where the reference was.
252 pub reference_range: TextRange, 249 pub reference_range: TextRange,
253 /// What this reference resolves to. 250 /// What this reference resolves to.
254 pub resolves_to: Vec<(FileId, FileSymbol)>, 251 pub resolves_to: Vec<NavigationTarget>,
255} 252}
256 253
257impl ReferenceResolution { 254impl ReferenceResolution {
@@ -263,7 +260,28 @@ impl ReferenceResolution {
263 } 260 }
264 261
265 fn add_resolution(&mut self, file_id: FileId, symbol: FileSymbol) { 262 fn add_resolution(&mut self, file_id: FileId, symbol: FileSymbol) {
266 self.resolves_to.push((file_id, symbol)) 263 self.resolves_to.push(NavigationTarget { file_id, symbol })
264 }
265}
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)
267 } 285 }
268} 286}
269 287
@@ -273,112 +291,146 @@ impl ReferenceResolution {
273/// `Analysis` are canceled (most method return `Err(Canceled)`). 291/// `Analysis` are canceled (most method return `Err(Canceled)`).
274#[derive(Debug)] 292#[derive(Debug)]
275pub struct Analysis { 293pub struct Analysis {
276 pub(crate) imp: AnalysisImpl, 294 db: salsa::Snapshot<db::RootDatabase>,
277} 295}
278 296
279impl Analysis { 297impl Analysis {
298 /// Gets the text of the source file.
280 pub fn file_text(&self, file_id: FileId) -> Arc<String> { 299 pub fn file_text(&self, file_id: FileId) -> Arc<String> {
281 self.imp.file_text(file_id) 300 self.db.file_text(file_id)
282 } 301 }
302 /// Gets the syntax tree of the file.
283 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { 303 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode {
284 self.imp.file_syntax(file_id).clone() 304 self.db.source_file(file_id).clone()
285 } 305 }
306 /// Gets the file's `LineIndex`: data structure to convert between absolute
307 /// offsets and line/column representation.
286 pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> { 308 pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> {
287 self.imp.file_line_index(file_id) 309 self.db.file_lines(file_id)
288 } 310 }
311 /// Selects the next syntactic nodes encopasing the range.
289 pub fn extend_selection(&self, frange: FileRange) -> TextRange { 312 pub fn extend_selection(&self, frange: FileRange) -> TextRange {
290 extend_selection::extend_selection(&self.imp.db, frange) 313 extend_selection::extend_selection(&self.db, frange)
291 } 314 }
315 /// Returns position of the mathcing brace (all types of braces are
316 /// supported).
292 pub fn matching_brace(&self, file: &SourceFileNode, offset: TextUnit) -> Option<TextUnit> { 317 pub fn matching_brace(&self, file: &SourceFileNode, offset: TextUnit) -> Option<TextUnit> {
293 ra_editor::matching_brace(file, offset) 318 ra_editor::matching_brace(file, offset)
294 } 319 }
320 /// Returns a syntax tree represented as `String`, for debug purposes.
321 // FIXME: use a better name here.
295 pub fn syntax_tree(&self, file_id: FileId) -> String { 322 pub fn syntax_tree(&self, file_id: FileId) -> String {
296 let file = self.imp.file_syntax(file_id); 323 let file = self.db.source_file(file_id);
297 ra_editor::syntax_tree(&file) 324 ra_editor::syntax_tree(&file)
298 } 325 }
326 /// Returns an edit to remove all newlines in the range, cleaning up minor
327 /// stuff like trailing commas.
299 pub fn join_lines(&self, frange: FileRange) -> SourceChange { 328 pub fn join_lines(&self, frange: FileRange) -> SourceChange {
300 let file = self.imp.file_syntax(frange.file_id); 329 let file = self.db.source_file(frange.file_id);
301 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))
302 } 331 }
332 /// Returns an edit which should be applied when opening a new line, fixing
333 /// up minor stuff like continuing the comment.
303 pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> { 334 pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> {
304 let file = self.imp.file_syntax(position.file_id); 335 let file = self.db.source_file(position.file_id);
305 let edit = ra_editor::on_enter(&file, position.offset)?; 336 let edit = ra_editor::on_enter(&file, position.offset)?;
306 let res = SourceChange::from_local_edit(position.file_id, edit); 337 Some(SourceChange::from_local_edit(position.file_id, edit))
307 Some(res)
308 } 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.
309 pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> { 342 pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> {
310 let file = self.imp.file_syntax(position.file_id); 343 let file = self.db.source_file(position.file_id);
311 Some(SourceChange::from_local_edit( 344 let edit = ra_editor::on_eq_typed(&file, position.offset)?;
312 position.file_id, 345 Some(SourceChange::from_local_edit(position.file_id, edit))
313 ra_editor::on_eq_typed(&file, position.offset)?,
314 ))
315 } 346 }
347 /// Returns a tree representation of symbols in the file. Useful to draw a
348 /// file outline.
316 pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> { 349 pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> {
317 let file = self.imp.file_syntax(file_id); 350 let file = self.db.source_file(file_id);
318 ra_editor::file_structure(&file) 351 ra_editor::file_structure(&file)
319 } 352 }
353 /// Returns the set of folding ranges.
320 pub fn folding_ranges(&self, file_id: FileId) -> Vec<Fold> { 354 pub fn folding_ranges(&self, file_id: FileId) -> Vec<Fold> {
321 let file = self.imp.file_syntax(file_id); 355 let file = self.db.source_file(file_id);
322 ra_editor::folding_ranges(&file) 356 ra_editor::folding_ranges(&file)
323 } 357 }
324 pub fn symbol_search(&self, query: Query) -> Cancelable<Vec<(FileId, FileSymbol)>> { 358 /// Fuzzy searches for a symbol.
325 self.imp.world_symbols(query) 359 pub fn symbol_search(&self, query: Query) -> Cancelable<Vec<NavigationTarget>> {
360 let res = symbol_index::world_symbols(&*self.db, query)?
361 .into_iter()
362 .map(|(file_id, symbol)| NavigationTarget { file_id, symbol })
363 .collect();
364 Ok(res)
326 } 365 }
366 /// Resolves reference to definition, but does not gurantee correctness.
327 pub fn approximately_resolve_symbol( 367 pub fn approximately_resolve_symbol(
328 &self, 368 &self,
329 position: FilePosition, 369 position: FilePosition,
330 ) -> Cancelable<Option<ReferenceResolution>> { 370 ) -> Cancelable<Option<ReferenceResolution>> {
331 self.imp.approximately_resolve_symbol(position) 371 self.db.approximately_resolve_symbol(position)
332 } 372 }
373 /// Finds all usages of the reference at point.
333 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)>> {
334 self.imp.find_all_refs(position) 375 self.db.find_all_refs(position)
335 }
336 pub fn doc_text_for(&self, file_id: FileId, symbol: FileSymbol) -> Cancelable<Option<String>> {
337 self.imp.doc_text_for(file_id, symbol)
338 } 376 }
339 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { 377 /// Returns documentation string for a given target.
340 self.imp.parent_module(position) 378 pub fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> {
379 self.db.doc_text_for(nav)
341 } 380 }
342 pub fn module_path(&self, position: FilePosition) -> Cancelable<Option<String>> { 381 /// Returns a `mod name;` declaration whihc created the current module.
343 self.imp.module_path(position) 382 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> {
383 self.db.parent_module(position)
344 } 384 }
385 /// Returns crates this file belongs too.
345 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { 386 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
346 self.imp.crate_for(file_id) 387 self.db.crate_for(file_id)
347 } 388 }
389 /// Returns the root file of the given crate.
348 pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> { 390 pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> {
349 Ok(self.imp.crate_root(crate_id)) 391 Ok(self.db.crate_root(crate_id))
350 } 392 }
393 /// Returns the set of possible targets to run for the current file.
351 pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> { 394 pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> {
352 let file = self.imp.file_syntax(file_id); 395 runnables::runnables(&*self.db, file_id)
353 Ok(runnables::runnables(self, &file, file_id))
354 } 396 }
397 /// Computes syntax highlighting for the given file.
355 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { 398 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
356 syntax_highlighting::highlight(&*self.imp.db, file_id) 399 syntax_highlighting::highlight(&*self.db, file_id)
357 } 400 }
401 /// Computes completions at the given position.
358 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> { 402 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> {
359 self.imp.completions(position) 403 let completions = completion::completions(&self.db, position)?;
404 Ok(completions.map(|it| it.into()))
360 } 405 }
406 /// Computes assists (aks code actons aka intentions) for the given
407 /// position.
361 pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<SourceChange>> { 408 pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<SourceChange>> {
362 Ok(self.imp.assists(frange)) 409 Ok(self.db.assists(frange))
363 } 410 }
411 /// Computes the set of diagnostics for the given file.
364 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { 412 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> {
365 self.imp.diagnostics(file_id) 413 self.db.diagnostics(file_id)
366 } 414 }
415 /// Computes parameter information for the given call expression.
367 pub fn resolve_callable( 416 pub fn resolve_callable(
368 &self, 417 &self,
369 position: FilePosition, 418 position: FilePosition,
370 ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> { 419 ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> {
371 self.imp.resolve_callable(position) 420 self.db.resolve_callable(position)
372 } 421 }
422 /// Computes the type of the expression at the given position.
373 pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> { 423 pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> {
374 self.imp.type_of(frange) 424 self.db.type_of(frange)
375 } 425 }
426 /// Returns the edit required to rename reference at the position to the new
427 /// name.
376 pub fn rename( 428 pub fn rename(
377 &self, 429 &self,
378 position: FilePosition, 430 position: FilePosition,
379 new_name: &str, 431 new_name: &str,
380 ) -> Cancelable<Vec<SourceFileEdit>> { 432 ) -> Cancelable<Vec<SourceFileEdit>> {
381 self.imp.rename(position, new_name) 433 self.db.rename(position, new_name)
382 } 434 }
383} 435}
384 436
diff --git a/crates/ra_analysis/src/macros.rs b/crates/ra_analysis/src/macros.rs
deleted file mode 100644
index b9feb7fad..000000000
--- a/crates/ra_analysis/src/macros.rs
+++ /dev/null
@@ -1,75 +0,0 @@
1/// Begining of macro expansion.
2///
3/// This code should be moved out of ra_analysis into hir (?) ideally.
4use ra_syntax::{ast, AstNode, SourceFileNode, TextRange};
5
6use crate::{db::RootDatabase, FileId};
7
8pub(crate) fn expand(
9 _db: &RootDatabase,
10 _file_id: FileId,
11 macro_call: ast::MacroCall,
12) -> Option<MacroExpansion> {
13 let path = macro_call.path()?;
14 if path.qualifier().is_some() {
15 return None;
16 }
17 let name_ref = path.segment()?.name_ref()?;
18 if name_ref.text() != "ctry" {
19 return None;
20 }
21
22 let arg = macro_call.token_tree()?;
23 let text = format!(
24 r"
25 fn dummy() {{
26 match {} {{
27 None => return Ok(None),
28 Some(it) => it,
29 }}
30 }}",
31 arg.syntax().text()
32 );
33 let file = SourceFileNode::parse(&text);
34 let match_expr = file.syntax().descendants().find_map(ast::MatchExpr::cast)?;
35 let match_arg = match_expr.expr()?;
36 let ranges_map = vec![(arg.syntax().range(), match_arg.syntax().range())];
37 let res = MacroExpansion {
38 source_file: file,
39 ranges_map,
40 };
41 Some(res)
42}
43
44pub(crate) struct MacroExpansion {
45 pub(crate) source_file: SourceFileNode,
46 pub(crate) ranges_map: Vec<(TextRange, TextRange)>,
47}
48
49impl MacroExpansion {
50 pub(crate) fn source_file(&self) -> &SourceFileNode {
51 &self.source_file
52 }
53 pub(crate) fn map_range_back(&self, tgt_range: TextRange) -> Option<TextRange> {
54 for (s_range, t_range) in self.ranges_map.iter() {
55 if tgt_range.is_subrange(&t_range) {
56 let tgt_at_zero_range = tgt_range - tgt_range.start();
57 let tgt_range_offset = tgt_range.start() - t_range.start();
58 let src_range = tgt_at_zero_range + tgt_range_offset + s_range.start();
59 return Some(src_range);
60 }
61 }
62 None
63 }
64 pub(crate) fn map_range_forward(&self, src_range: TextRange) -> Option<TextRange> {
65 for (s_range, t_range) in self.ranges_map.iter() {
66 if src_range.is_subrange(&s_range) {
67 let src_at_zero_range = src_range - src_range.start();
68 let src_range_offset = src_range.start() - s_range.start();
69 let src_range = src_at_zero_range + src_range_offset + t_range.start();
70 return Some(src_range);
71 }
72 }
73 None
74 }
75}
diff --git a/crates/ra_analysis/src/runnables.rs b/crates/ra_analysis/src/runnables.rs
index 61ca0930a..474267605 100644
--- a/crates/ra_analysis/src/runnables.rs
+++ b/crates/ra_analysis/src/runnables.rs
@@ -1,11 +1,11 @@
1use itertools::Itertools;
1use ra_syntax::{ 2use ra_syntax::{
2 ast::{self, AstNode, NameOwner, ModuleItemOwner}, 3 ast::{self, AstNode, NameOwner, ModuleItemOwner},
3 SourceFileNode, TextRange, SyntaxNodeRef, 4 TextRange, SyntaxNodeRef,
4 TextUnit,
5};
6use crate::{
7 Analysis, FileId, FilePosition
8}; 5};
6use ra_db::{Cancelable, SyntaxDatabase};
7
8use crate::{db::RootDatabase, FileId};
9 9
10#[derive(Debug)] 10#[derive(Debug)]
11pub struct Runnable { 11pub struct Runnable {
@@ -20,53 +20,67 @@ pub enum RunnableKind {
20 Bin, 20 Bin,
21} 21}
22 22
23pub fn runnables( 23pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<Runnable>> {
24 analysis: &Analysis, 24 let source_file = db.source_file(file_id);
25 file_node: &SourceFileNode, 25 let res = source_file
26 file_id: FileId,
27) -> Vec<Runnable> {
28 file_node
29 .syntax() 26 .syntax()
30 .descendants() 27 .descendants()
31 .filter_map(|i| runnable(analysis, i, file_id)) 28 .filter_map(|i| runnable(db, file_id, i))
32 .collect() 29 .collect();
30 Ok(res)
33} 31}
34 32
35fn runnable<'a>(analysis: &Analysis, item: SyntaxNodeRef<'a>, file_id: FileId) -> Option<Runnable> { 33fn runnable(db: &RootDatabase, file_id: FileId, item: SyntaxNodeRef) -> Option<Runnable> {
36 if let Some(f) = ast::FnDef::cast(item) { 34 if let Some(fn_def) = ast::FnDef::cast(item) {
37 let name = f.name()?.text(); 35 runnable_fn(fn_def)
38 let kind = if name == "main" {
39 RunnableKind::Bin
40 } else if f.has_atom_attr("test") {
41 RunnableKind::Test {
42 name: name.to_string(),
43 }
44 } else {
45 return None;
46 };
47 Some(Runnable {
48 range: f.syntax().range(),
49 kind,
50 })
51 } else if let Some(m) = ast::Module::cast(item) { 36 } else if let Some(m) = ast::Module::cast(item) {
52 if m.item_list()? 37 runnable_mod(db, file_id, m)
53 .items()
54 .map(ast::ModuleItem::syntax)
55 .filter_map(ast::FnDef::cast)
56 .any(|f| f.has_atom_attr("test"))
57 {
58 let postition = FilePosition {
59 file_id,
60 offset: m.syntax().range().start() + TextUnit::from_usize(1),
61 };
62 analysis.module_path(postition).ok()?.map(|path| Runnable {
63 range: m.syntax().range(),
64 kind: RunnableKind::TestMod { path },
65 })
66 } else {
67 None
68 }
69 } else { 38 } else {
70 None 39 None
71 } 40 }
72} 41}
42
43fn runnable_fn(fn_def: ast::FnDef) -> Option<Runnable> {
44 let name = fn_def.name()?.text();
45 let kind = if name == "main" {
46 RunnableKind::Bin
47 } else if fn_def.has_atom_attr("test") {
48 RunnableKind::Test {
49 name: name.to_string(),
50 }
51 } else {
52 return None;
53 };
54 Some(Runnable {
55 range: fn_def.syntax().range(),
56 kind,
57 })
58}
59
60fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option<Runnable> {
61 let has_test_function = module
62 .item_list()?
63 .items()
64 .filter_map(|it| match it {
65 ast::ModuleItem::FnDef(it) => Some(it),
66 _ => None,
67 })
68 .any(|f| f.has_atom_attr("test"));
69 if !has_test_function {
70 return None;
71 }
72 let range = module.syntax().range();
73 let module =
74 hir::source_binder::module_from_child_node(db, file_id, module.syntax()).ok()??;
75 let path = module
76 .path_to_root()
77 .into_iter()
78 .rev()
79 .into_iter()
80 .filter_map(|it| it.name().map(Clone::clone))
81 .join("::");
82 Some(Runnable {
83 range,
84 kind: RunnableKind::TestMod { path },
85 })
86}
diff --git a/crates/ra_analysis/src/symbol_index.rs b/crates/ra_analysis/src/symbol_index.rs
index e5bdf0aa1..ddcf3d052 100644
--- a/crates/ra_analysis/src/symbol_index.rs
+++ b/crates/ra_analysis/src/symbol_index.rs
@@ -4,17 +4,19 @@ use std::{
4}; 4};
5 5
6use fst::{self, Streamer}; 6use fst::{self, Streamer};
7use ra_editor::{self, FileSymbol};
8use ra_syntax::{ 7use ra_syntax::{
9 SourceFileNode, 8 AstNode, SyntaxNodeRef, SourceFileNode, SmolStr, TextRange,
9 algo::visit::{visitor, Visitor},
10 SyntaxKind::{self, *}, 10 SyntaxKind::{self, *},
11 ast::{self, NameOwner, DocCommentsOwner},
11}; 12};
12use ra_db::{SyntaxDatabase, SourceRootId}; 13use ra_db::{SyntaxDatabase, SourceRootId, FilesDatabase};
14use salsa::ParallelDatabase;
13use rayon::prelude::*; 15use rayon::prelude::*;
14 16
15use crate::{ 17use crate::{
16 Cancelable, 18 Cancelable, FileId, Query,
17 FileId, Query, 19 db::RootDatabase,
18}; 20};
19 21
20salsa::query_group! { 22salsa::query_group! {
@@ -35,6 +37,41 @@ fn file_symbols(db: &impl SyntaxDatabase, file_id: FileId) -> Cancelable<Arc<Sym
35 Ok(Arc::new(SymbolIndex::for_file(file_id, syntax))) 37 Ok(Arc::new(SymbolIndex::for_file(file_id, syntax)))
36} 38}
37 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
38#[derive(Default, Debug)] 75#[derive(Default, Debug)]
39pub(crate) struct SymbolIndex { 76pub(crate) struct SymbolIndex {
40 symbols: Vec<(FileId, FileSymbol)>, 77 symbols: Vec<(FileId, FileSymbol)>,
@@ -65,8 +102,9 @@ impl SymbolIndex {
65 ) -> SymbolIndex { 102 ) -> SymbolIndex {
66 let mut symbols = files 103 let mut symbols = files
67 .flat_map(|(file_id, file)| { 104 .flat_map(|(file_id, file)| {
68 ra_editor::file_symbols(&file) 105 file.syntax()
69 .into_iter() 106 .descendants()
107 .filter_map(to_symbol)
70 .map(move |symbol| (symbol.name.as_str().to_lowercase(), (file_id, symbol))) 108 .map(move |symbol| (symbol.name.as_str().to_lowercase(), (file_id, symbol)))
71 .collect::<Vec<_>>() 109 .collect::<Vec<_>>()
72 }) 110 })
@@ -121,3 +159,116 @@ fn is_type(kind: SyntaxKind) -> bool {
121 _ => false, 159 _ => false,
122 } 160 }
123} 161}
162
163#[derive(Debug, Clone, PartialEq, Eq, Hash)]
164pub(crate) struct FileSymbol {
165 pub(crate) name: SmolStr,
166 pub(crate) node_range: TextRange,
167 pub(crate) kind: SyntaxKind,
168}
169
170impl FileSymbol {
171 pub(crate) fn docs(&self, file: &SourceFileNode) -> Option<String> {
172 file.syntax()
173 .descendants()
174 .filter(|node| node.kind() == self.kind && node.range() == self.node_range)
175 .filter_map(|node: SyntaxNodeRef| {
176 fn doc_comments<'a, N: DocCommentsOwner<'a>>(node: N) -> Option<String> {
177 let comments = node.doc_comment_text();
178 if comments.is_empty() {
179 None
180 } else {
181 Some(comments)
182 }
183 }
184
185 visitor()
186 .visit(doc_comments::<ast::FnDef>)
187 .visit(doc_comments::<ast::StructDef>)
188 .visit(doc_comments::<ast::EnumDef>)
189 .visit(doc_comments::<ast::TraitDef>)
190 .visit(doc_comments::<ast::Module>)
191 .visit(doc_comments::<ast::TypeDef>)
192 .visit(doc_comments::<ast::ConstDef>)
193 .visit(doc_comments::<ast::StaticDef>)
194 .accept(node)?
195 })
196 .nth(0)
197 }
198 /// Get a description of this node.
199 ///
200 /// e.g. `struct Name`, `enum Name`, `fn Name`
201 pub(crate) fn description(&self, file: &SourceFileNode) -> Option<String> {
202 // TODO: After type inference is done, add type information to improve the output
203 file.syntax()
204 .descendants()
205 .filter(|node| node.kind() == self.kind && node.range() == self.node_range)
206 .filter_map(|node: SyntaxNodeRef| {
207 // TODO: Refactor to be have less repetition
208 visitor()
209 .visit(|node: ast::FnDef| {
210 let mut string = "fn ".to_string();
211 node.name()?.syntax().text().push_to(&mut string);
212 Some(string)
213 })
214 .visit(|node: ast::StructDef| {
215 let mut string = "struct ".to_string();
216 node.name()?.syntax().text().push_to(&mut string);
217 Some(string)
218 })
219 .visit(|node: ast::EnumDef| {
220 let mut string = "enum ".to_string();
221 node.name()?.syntax().text().push_to(&mut string);
222 Some(string)
223 })
224 .visit(|node: ast::TraitDef| {
225 let mut string = "trait ".to_string();
226 node.name()?.syntax().text().push_to(&mut string);
227 Some(string)
228 })
229 .visit(|node: ast::Module| {
230 let mut string = "mod ".to_string();
231 node.name()?.syntax().text().push_to(&mut string);
232 Some(string)
233 })
234 .visit(|node: ast::TypeDef| {
235 let mut string = "type ".to_string();
236 node.name()?.syntax().text().push_to(&mut string);
237 Some(string)
238 })
239 .visit(|node: ast::ConstDef| {
240 let mut string = "const ".to_string();
241 node.name()?.syntax().text().push_to(&mut string);
242 Some(string)
243 })
244 .visit(|node: ast::StaticDef| {
245 let mut string = "static ".to_string();
246 node.name()?.syntax().text().push_to(&mut string);
247 Some(string)
248 })
249 .accept(node)?
250 })
251 .nth(0)
252 }
253}
254
255fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> {
256 fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<FileSymbol> {
257 let name = node.name()?;
258 Some(FileSymbol {
259 name: name.text(),
260 node_range: node.syntax().range(),
261 kind: node.syntax().kind(),
262 })
263 }
264 visitor()
265 .visit(decl::<ast::FnDef>)
266 .visit(decl::<ast::StructDef>)
267 .visit(decl::<ast::EnumDef>)
268 .visit(decl::<ast::TraitDef>)
269 .visit(decl::<ast::Module>)
270 .visit(decl::<ast::TypeDef>)
271 .visit(decl::<ast::ConstDef>)
272 .visit(decl::<ast::StaticDef>)
273 .accept(node)?
274}
diff --git a/crates/ra_analysis/src/syntax_highlighting.rs b/crates/ra_analysis/src/syntax_highlighting.rs
index 7e9139a74..ccea4aee3 100644
--- a/crates/ra_analysis/src/syntax_highlighting.rs
+++ b/crates/ra_analysis/src/syntax_highlighting.rs
@@ -15,13 +15,13 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<Hi
15 .descendants() 15 .descendants()
16 .filter_map(ast::MacroCall::cast) 16 .filter_map(ast::MacroCall::cast)
17 { 17 {
18 if let Some(exp) = crate::macros::expand(db, file_id, macro_call) { 18 if let Some((off, exp)) = hir::MacroDef::ast_expand(macro_call) {
19 let mapped_ranges = ra_editor::highlight(exp.source_file().syntax()) 19 let mapped_ranges = ra_editor::highlight(exp.syntax().borrowed())
20 .into_iter() 20 .into_iter()
21 .filter_map(|r| { 21 .filter_map(|r| {
22 let mapped_range = exp.map_range_back(r.range)?; 22 let mapped_range = exp.map_range_back(r.range)?;
23 let res = HighlightedRange { 23 let res = HighlightedRange {
24 range: mapped_range, 24 range: mapped_range + off,
25 tag: r.tag, 25 tag: r.tag,
26 }; 26 };
27 Some(res) 27 Some(res)
@@ -44,7 +44,7 @@ mod tests {
44 fn main() { 44 fn main() {
45 ctry!({ let x = 92; x}); 45 ctry!({ let x = 92; x});
46 } 46 }
47 ", 47 ",
48 ); 48 );
49 let highlights = analysis.highlight(file_id).unwrap(); 49 let highlights = analysis.highlight(file_id).unwrap();
50 assert_eq_dbg( 50 assert_eq_dbg(
@@ -60,4 +60,26 @@ mod tests {
60 &highlights, 60 &highlights,
61 ) 61 )
62 } 62 }
63
64 // FIXME: this test is not really necessary: artifact of the inital hacky
65 // macros implementation.
66 #[test]
67 fn highlight_query_group_macro() {
68 let (analysis, file_id) = single_file(
69 "
70 salsa::query_group! {
71 pub trait HirDatabase: SyntaxDatabase {}
72 }
73 ",
74 );
75 let highlights = analysis.highlight(file_id).unwrap();
76 assert_eq_dbg(
77 r#"[HighlightedRange { range: [20; 32), tag: "macro" },
78 HighlightedRange { range: [13; 18), tag: "text" },
79 HighlightedRange { range: [51; 54), tag: "keyword" },
80 HighlightedRange { range: [55; 60), tag: "keyword" },
81 HighlightedRange { range: [61; 72), tag: "function" }]"#,
82 &highlights,
83 )
84 }
63} 85}