diff options
Diffstat (limited to 'crates/ra_analysis/src/imp.rs')
-rw-r--r-- | crates/ra_analysis/src/imp.rs | 462 |
1 files changed, 232 insertions, 230 deletions
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index 5ed374c79..eae73c2c4 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs | |||
@@ -1,59 +1,42 @@ | |||
1 | use std::{ | 1 | use std::sync::Arc; |
2 | fmt, | ||
3 | sync::Arc, | ||
4 | }; | ||
5 | 2 | ||
6 | use rayon::prelude::*; | 3 | use salsa::Database; |
7 | use salsa::{Database, ParallelDatabase}; | ||
8 | 4 | ||
9 | use hir::{ | 5 | use hir::{ |
10 | self, FnSignatureInfo, Problem, source_binder, | 6 | self, FnSignatureInfo, Problem, source_binder, |
11 | }; | 7 | }; |
12 | use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; | 8 | use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; |
13 | use ra_editor::{self, FileSymbol, find_node_at_offset, LineIndex, LocalEdit, Severity}; | 9 | use ra_editor::{self, find_node_at_offset, assists, LocalEdit, Severity}; |
14 | use ra_syntax::{ | 10 | use ra_syntax::{ |
15 | algo::find_covering_node, | 11 | algo::{find_covering_node, visit::{visitor, Visitor}}, |
16 | ast::{self, ArgListOwner, Expr, FnDef, NameOwner}, | 12 | ast::{self, ArgListOwner, Expr, FnDef, NameOwner}, |
17 | AstNode, SourceFileNode, | 13 | AstNode, SourceFileNode, |
18 | SyntaxKind::*, | 14 | SyntaxKind::*, |
19 | SyntaxNodeRef, TextRange, TextUnit, | 15 | SyntaxNode, SyntaxNodeRef, TextRange, TextUnit, |
20 | }; | 16 | }; |
21 | 17 | ||
22 | use crate::{ | 18 | use 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)] | 26 | impl db::RootDatabase { |
32 | pub(crate) struct AnalysisHostImpl { | 27 | pub(crate) fn apply_change(&mut self, change: AnalysisChange) { |
33 | db: db::RootDatabase, | ||
34 | } | ||
35 | |||
36 | impl 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,142 +90,86 @@ 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 | ||
131 | pub(crate) struct AnalysisImpl { | 102 | impl db::RootDatabase { |
132 | pub(crate) db: salsa::Snapshot<db::RootDatabase>, | ||
133 | } | ||
134 | |||
135 | impl 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 | |||
142 | impl 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 | /// 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 |
185 | /// 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. |
186 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { | 105 | pub(crate) fn parent_module( |
187 | 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)? { | ||
188 | None => return Ok(Vec::new()), | 110 | None => return Ok(Vec::new()), |
189 | Some(it) => it, | 111 | Some(it) => it, |
190 | }; | 112 | }; |
191 | let (file_id, decl) = match descr.parent_link_source(&*self.db) { | 113 | let (file_id, decl) = match descr.parent_link_source(self) { |
192 | None => return Ok(Vec::new()), | 114 | None => return Ok(Vec::new()), |
193 | Some(it) => it, | 115 | Some(it) => it, |
194 | }; | 116 | }; |
195 | let decl = decl.borrowed(); | 117 | let decl = decl.borrowed(); |
196 | let decl_name = decl.name().unwrap(); | 118 | let decl_name = decl.name().unwrap(); |
197 | let sym = FileSymbol { | 119 | Ok(vec![NavigationTarget { |
120 | file_id, | ||
198 | name: decl_name.text(), | 121 | name: decl_name.text(), |
199 | node_range: decl_name.syntax().range(), | 122 | range: decl_name.syntax().range(), |
200 | kind: MODULE, | 123 | kind: MODULE, |
201 | }; | 124 | ptr: None, |
202 | Ok(vec![(file_id, sym)]) | 125 | }]) |
203 | } | 126 | } |
204 | /// Returns `Vec` for the same reason as `parent_module` | 127 | /// Returns `Vec` for the same reason as `parent_module` |
205 | pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { | 128 | pub(crate) fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { |
206 | let descr = match source_binder::module_from_file_id(&*self.db, file_id)? { | 129 | let descr = match source_binder::module_from_file_id(self, file_id)? { |
207 | None => return Ok(Vec::new()), | 130 | None => return Ok(Vec::new()), |
208 | Some(it) => it, | 131 | Some(it) => it, |
209 | }; | 132 | }; |
210 | let root = descr.crate_root(); | 133 | let root = descr.crate_root(); |
211 | let file_id = root.source().file_id(); | 134 | let file_id = root.file_id(); |
212 | 135 | ||
213 | let crate_graph = self.db.crate_graph(); | 136 | let crate_graph = self.crate_graph(); |
214 | let crate_id = crate_graph.crate_id_for_crate_root(file_id); | 137 | let crate_id = crate_graph.crate_id_for_crate_root(file_id); |
215 | Ok(crate_id.into_iter().collect()) | 138 | Ok(crate_id.into_iter().collect()) |
216 | } | 139 | } |
217 | pub fn crate_root(&self, crate_id: CrateId) -> FileId { | 140 | pub(crate) fn crate_root(&self, crate_id: CrateId) -> FileId { |
218 | self.db.crate_graph().crate_root(crate_id) | 141 | self.crate_graph().crate_root(crate_id) |
219 | } | 142 | } |
220 | pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> { | 143 | pub(crate) fn approximately_resolve_symbol( |
221 | let completions = completions(&self.db, position)?; | ||
222 | Ok(completions.map(|it| it.into())) | ||
223 | } | ||
224 | pub fn approximately_resolve_symbol( | ||
225 | &self, | 144 | &self, |
226 | position: FilePosition, | 145 | position: FilePosition, |
227 | ) -> Cancelable<Option<ReferenceResolution>> { | 146 | ) -> Cancelable<Option<ReferenceResolution>> { |
228 | let file = self.db.source_file(position.file_id); | 147 | let file = self.source_file(position.file_id); |
229 | let syntax = file.syntax(); | 148 | let syntax = file.syntax(); |
230 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) { | 149 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) { |
231 | let mut rr = ReferenceResolution::new(name_ref.syntax().range()); | 150 | let mut rr = ReferenceResolution::new(name_ref.syntax().range()); |
232 | if let Some(fn_descr) = source_binder::function_from_child_node( | 151 | if let Some(fn_descr) = |
233 | &*self.db, | 152 | source_binder::function_from_child_node(self, position.file_id, name_ref.syntax())? |
234 | position.file_id, | 153 | { |
235 | name_ref.syntax(), | 154 | let scope = fn_descr.scopes(self); |
236 | )? { | ||
237 | let scope = fn_descr.scopes(&*self.db); | ||
238 | // First try to resolve the symbol locally | 155 | // First try to resolve the symbol locally |
239 | if let Some(entry) = scope.resolve_local_name(name_ref) { | 156 | if let Some(entry) = scope.resolve_local_name(name_ref) { |
240 | rr.add_resolution( | 157 | rr.resolves_to.push(NavigationTarget { |
241 | position.file_id, | 158 | file_id: position.file_id, |
242 | FileSymbol { | 159 | name: entry.name().to_string().into(), |
243 | name: entry.name().to_string().into(), | 160 | range: entry.ptr().range(), |
244 | node_range: entry.ptr().range(), | 161 | kind: NAME, |
245 | kind: NAME, | 162 | ptr: None, |
246 | }, | 163 | }); |
247 | ); | ||
248 | return Ok(Some(rr)); | 164 | return Ok(Some(rr)); |
249 | }; | 165 | }; |
250 | } | 166 | } |
251 | // If that fails try the index based approach. | 167 | // If that fails try the index based approach. |
252 | for (file_id, symbol) in self.index_resolve(name_ref)? { | 168 | rr.resolves_to.extend( |
253 | rr.add_resolution(file_id, symbol); | 169 | self.index_resolve(name_ref)? |
254 | } | 170 | .into_iter() |
171 | .map(NavigationTarget::from_symbol), | ||
172 | ); | ||
255 | return Ok(Some(rr)); | 173 | return Ok(Some(rr)); |
256 | } | 174 | } |
257 | if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) { | 175 | if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) { |
@@ -259,19 +177,21 @@ impl AnalysisImpl { | |||
259 | if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { | 177 | if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { |
260 | if module.has_semi() { | 178 | if module.has_semi() { |
261 | if let Some(child_module) = | 179 | if let Some(child_module) = |
262 | source_binder::module_from_declaration(&*self.db, position.file_id, module)? | 180 | source_binder::module_from_declaration(self, position.file_id, module)? |
263 | { | 181 | { |
264 | let file_id = child_module.source().file_id(); | 182 | let file_id = child_module.file_id(); |
265 | let name = match child_module.name() { | 183 | let name = match child_module.name() { |
266 | Some(name) => name.to_string().into(), | 184 | Some(name) => name.to_string().into(), |
267 | None => "".into(), | 185 | None => "".into(), |
268 | }; | 186 | }; |
269 | let symbol = FileSymbol { | 187 | let symbol = NavigationTarget { |
188 | file_id, | ||
270 | name, | 189 | name, |
271 | node_range: TextRange::offset_len(0.into(), 0.into()), | 190 | range: TextRange::offset_len(0.into(), 0.into()), |
272 | kind: MODULE, | 191 | kind: MODULE, |
192 | ptr: None, | ||
273 | }; | 193 | }; |
274 | rr.add_resolution(file_id, symbol); | 194 | rr.resolves_to.push(symbol); |
275 | return Ok(Some(rr)); | 195 | return Ok(Some(rr)); |
276 | } | 196 | } |
277 | } | 197 | } |
@@ -280,10 +200,13 @@ impl AnalysisImpl { | |||
280 | Ok(None) | 200 | Ok(None) |
281 | } | 201 | } |
282 | 202 | ||
283 | pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { | 203 | pub(crate) fn find_all_refs( |
284 | let file = self.db.source_file(position.file_id); | 204 | &self, |
205 | position: FilePosition, | ||
206 | ) -> Cancelable<Vec<(FileId, TextRange)>> { | ||
207 | let file = self.source_file(position.file_id); | ||
285 | // Find the binding associated with the offset | 208 | // Find the binding associated with the offset |
286 | let (binding, descr) = match find_binding(&self.db, &file, position)? { | 209 | let (binding, descr) = match find_binding(self, &file, position)? { |
287 | None => return Ok(Vec::new()), | 210 | None => return Ok(Vec::new()), |
288 | Some(it) => it, | 211 | Some(it) => it, |
289 | }; | 212 | }; |
@@ -295,7 +218,7 @@ impl AnalysisImpl { | |||
295 | .collect::<Vec<_>>(); | 218 | .collect::<Vec<_>>(); |
296 | ret.extend( | 219 | ret.extend( |
297 | descr | 220 | descr |
298 | .scopes(&*self.db) | 221 | .scopes(self) |
299 | .find_all_refs(binding) | 222 | .find_all_refs(binding) |
300 | .into_iter() | 223 | .into_iter() |
301 | .map(|ref_desc| (position.file_id, ref_desc.range)), | 224 | .map(|ref_desc| (position.file_id, ref_desc.range)), |
@@ -333,9 +256,8 @@ impl AnalysisImpl { | |||
333 | Ok(Some((binding, descr))) | 256 | Ok(Some((binding, descr))) |
334 | } | 257 | } |
335 | } | 258 | } |
336 | pub fn doc_text_for(&self, file_id: FileId, symbol: FileSymbol) -> Cancelable<Option<String>> { | 259 | pub(crate) fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> { |
337 | let file = self.db.source_file(file_id); | 260 | let result = match (nav.description(self), nav.docs(self)) { |
338 | let result = match (symbol.description(&file), symbol.docs(&file)) { | ||
339 | (Some(desc), Some(docs)) => { | 261 | (Some(desc), Some(docs)) => { |
340 | Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs) | 262 | Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs) |
341 | } | 263 | } |
@@ -347,8 +269,8 @@ impl AnalysisImpl { | |||
347 | Ok(result) | 269 | Ok(result) |
348 | } | 270 | } |
349 | 271 | ||
350 | pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { | 272 | pub(crate) fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { |
351 | let syntax = self.db.source_file(file_id); | 273 | let syntax = self.source_file(file_id); |
352 | 274 | ||
353 | let mut res = ra_editor::diagnostics(&syntax) | 275 | let mut res = ra_editor::diagnostics(&syntax) |
354 | .into_iter() | 276 | .into_iter() |
@@ -359,9 +281,9 @@ impl AnalysisImpl { | |||
359 | fix: d.fix.map(|fix| SourceChange::from_local_edit(file_id, fix)), | 281 | fix: d.fix.map(|fix| SourceChange::from_local_edit(file_id, fix)), |
360 | }) | 282 | }) |
361 | .collect::<Vec<_>>(); | 283 | .collect::<Vec<_>>(); |
362 | if let Some(m) = source_binder::module_from_file_id(&*self.db, file_id)? { | 284 | if let Some(m) = source_binder::module_from_file_id(self, file_id)? { |
363 | for (name_node, problem) in m.problems(&*self.db) { | 285 | for (name_node, problem) in m.problems(self) { |
364 | let source_root = self.db.file_source_root(file_id); | 286 | let source_root = self.file_source_root(file_id); |
365 | let diag = match problem { | 287 | let diag = match problem { |
366 | Problem::UnresolvedModule { candidate } => { | 288 | Problem::UnresolvedModule { candidate } => { |
367 | let create_file = FileSystemEdit::CreateFile { | 289 | let create_file = FileSystemEdit::CreateFile { |
@@ -411,29 +333,19 @@ impl AnalysisImpl { | |||
411 | Ok(res) | 333 | Ok(res) |
412 | } | 334 | } |
413 | 335 | ||
414 | pub fn assists(&self, frange: FileRange) -> Vec<SourceChange> { | 336 | pub(crate) fn assists(&self, frange: FileRange) -> Vec<SourceChange> { |
415 | let file = self.file_syntax(frange.file_id); | 337 | let file = self.source_file(frange.file_id); |
416 | let offset = frange.range.start(); | 338 | assists::assists(&file, frange.range) |
417 | let actions = vec![ | ||
418 | ra_editor::flip_comma(&file, offset).map(|f| f()), | ||
419 | ra_editor::add_derive(&file, offset).map(|f| f()), | ||
420 | ra_editor::add_impl(&file, offset).map(|f| f()), | ||
421 | ra_editor::make_pub_crate(&file, offset).map(|f| f()), | ||
422 | ra_editor::introduce_variable(&file, frange.range).map(|f| f()), | ||
423 | ]; | ||
424 | actions | ||
425 | .into_iter() | 339 | .into_iter() |
426 | .filter_map(|local_edit| { | 340 | .map(|local_edit| SourceChange::from_local_edit(frange.file_id, local_edit)) |
427 | Some(SourceChange::from_local_edit(frange.file_id, local_edit?)) | ||
428 | }) | ||
429 | .collect() | 341 | .collect() |
430 | } | 342 | } |
431 | 343 | ||
432 | pub fn resolve_callable( | 344 | pub(crate) fn resolve_callable( |
433 | &self, | 345 | &self, |
434 | position: FilePosition, | 346 | position: FilePosition, |
435 | ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> { | 347 | ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> { |
436 | let file = self.db.source_file(position.file_id); | 348 | let file = self.source_file(position.file_id); |
437 | let syntax = file.syntax(); | 349 | let syntax = file.syntax(); |
438 | 350 | ||
439 | // Find the calling expression and it's NameRef | 351 | // Find the calling expression and it's NameRef |
@@ -442,53 +354,55 @@ impl AnalysisImpl { | |||
442 | 354 | ||
443 | // Resolve the function's NameRef (NOTE: this isn't entirely accurate). | 355 | // Resolve the function's NameRef (NOTE: this isn't entirely accurate). |
444 | let file_symbols = self.index_resolve(name_ref)?; | 356 | let file_symbols = self.index_resolve(name_ref)?; |
445 | for (fn_file_id, fs) in file_symbols { | 357 | for symbol in file_symbols { |
446 | if fs.kind == FN_DEF { | 358 | if symbol.ptr.kind() == FN_DEF { |
447 | let fn_file = self.db.source_file(fn_file_id); | 359 | let fn_file = self.source_file(symbol.file_id); |
448 | if let Some(fn_def) = find_node_at_offset(fn_file.syntax(), fs.node_range.start()) { | 360 | let fn_def = symbol.ptr.resolve(&fn_file); |
449 | let descr = ctry!(source_binder::function_from_source( | 361 | let fn_def = ast::FnDef::cast(fn_def.borrowed()).unwrap(); |
450 | &*self.db, fn_file_id, fn_def | 362 | let descr = ctry!(source_binder::function_from_source( |
451 | )?); | 363 | self, |
452 | if let Some(descriptor) = descr.signature_info(&*self.db) { | 364 | symbol.file_id, |
453 | // If we have a calling expression let's find which argument we are on | 365 | fn_def |
454 | let mut current_parameter = None; | 366 | )?); |
455 | 367 | if let Some(descriptor) = descr.signature_info(self) { | |
456 | let num_params = descriptor.params.len(); | 368 | // If we have a calling expression let's find which argument we are on |
457 | let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some(); | 369 | let mut current_parameter = None; |
458 | 370 | ||
459 | if num_params == 1 { | 371 | let num_params = descriptor.params.len(); |
460 | if !has_self { | 372 | let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some(); |
461 | current_parameter = Some(0); | 373 | |
462 | } | 374 | if num_params == 1 { |
463 | } else if num_params > 1 { | 375 | if !has_self { |
464 | // Count how many parameters into the call we are. | 376 | current_parameter = Some(0); |
465 | // TODO: This is best effort for now and should be fixed at some point. | ||
466 | // It may be better to see where we are in the arg_list and then check | ||
467 | // where offset is in that list (or beyond). | ||
468 | // Revisit this after we get documentation comments in. | ||
469 | if let Some(ref arg_list) = calling_node.arg_list() { | ||
470 | let start = arg_list.syntax().range().start(); | ||
471 | |||
472 | let range_search = TextRange::from_to(start, position.offset); | ||
473 | let mut commas: usize = arg_list | ||
474 | .syntax() | ||
475 | .text() | ||
476 | .slice(range_search) | ||
477 | .to_string() | ||
478 | .matches(',') | ||
479 | .count(); | ||
480 | |||
481 | // If we have a method call eat the first param since it's just self. | ||
482 | if has_self { | ||
483 | commas += 1; | ||
484 | } | ||
485 | |||
486 | current_parameter = Some(commas); | ||
487 | } | ||
488 | } | 377 | } |
378 | } else if num_params > 1 { | ||
379 | // Count how many parameters into the call we are. | ||
380 | // TODO: This is best effort for now and should be fixed at some point. | ||
381 | // It may be better to see where we are in the arg_list and then check | ||
382 | // where offset is in that list (or beyond). | ||
383 | // Revisit this after we get documentation comments in. | ||
384 | if let Some(ref arg_list) = calling_node.arg_list() { | ||
385 | let start = arg_list.syntax().range().start(); | ||
386 | |||
387 | let range_search = TextRange::from_to(start, position.offset); | ||
388 | let mut commas: usize = arg_list | ||
389 | .syntax() | ||
390 | .text() | ||
391 | .slice(range_search) | ||
392 | .to_string() | ||
393 | .matches(',') | ||
394 | .count(); | ||
395 | |||
396 | // If we have a method call eat the first param since it's just self. | ||
397 | if has_self { | ||
398 | commas += 1; | ||
399 | } | ||
489 | 400 | ||
490 | return Ok(Some((descriptor, current_parameter))); | 401 | current_parameter = Some(commas); |
402 | } | ||
491 | } | 403 | } |
404 | |||
405 | return Ok(Some((descriptor, current_parameter))); | ||
492 | } | 406 | } |
493 | } | 407 | } |
494 | } | 408 | } |
@@ -496,20 +410,20 @@ impl AnalysisImpl { | |||
496 | Ok(None) | 410 | Ok(None) |
497 | } | 411 | } |
498 | 412 | ||
499 | pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> { | 413 | pub(crate) fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> { |
500 | let file = self.db.source_file(frange.file_id); | 414 | let file = self.source_file(frange.file_id); |
501 | let syntax = file.syntax(); | 415 | let syntax = file.syntax(); |
502 | let node = find_covering_node(syntax, frange.range); | 416 | let node = find_covering_node(syntax, frange.range); |
503 | let parent_fn = ctry!(node.ancestors().find_map(FnDef::cast)); | 417 | let parent_fn = ctry!(node.ancestors().find_map(FnDef::cast)); |
504 | let function = ctry!(source_binder::function_from_source( | 418 | let function = ctry!(source_binder::function_from_source( |
505 | &*self.db, | 419 | self, |
506 | frange.file_id, | 420 | frange.file_id, |
507 | parent_fn | 421 | parent_fn |
508 | )?); | 422 | )?); |
509 | let infer = function.infer(&*self.db)?; | 423 | let infer = function.infer(self)?; |
510 | Ok(infer.type_of_node(node).map(|t| t.to_string())) | 424 | Ok(infer.type_of_node(node).map(|t| t.to_string())) |
511 | } | 425 | } |
512 | pub fn rename( | 426 | pub(crate) fn rename( |
513 | &self, | 427 | &self, |
514 | position: FilePosition, | 428 | position: FilePosition, |
515 | new_name: &str, | 429 | new_name: &str, |
@@ -520,7 +434,7 @@ impl AnalysisImpl { | |||
520 | .map(|(file_id, text_range)| SourceFileEdit { | 434 | .map(|(file_id, text_range)| SourceFileEdit { |
521 | file_id: *file_id, | 435 | file_id: *file_id, |
522 | edit: { | 436 | edit: { |
523 | let mut builder = ra_text_edit::TextEditBuilder::new(); | 437 | let mut builder = ra_text_edit::TextEditBuilder::default(); |
524 | builder.replace(*text_range, new_name.into()); | 438 | builder.replace(*text_range, new_name.into()); |
525 | builder.finish() | 439 | builder.finish() |
526 | }, | 440 | }, |
@@ -528,12 +442,12 @@ impl AnalysisImpl { | |||
528 | .collect::<Vec<_>>(); | 442 | .collect::<Vec<_>>(); |
529 | Ok(res) | 443 | Ok(res) |
530 | } | 444 | } |
531 | fn index_resolve(&self, name_ref: ast::NameRef) -> Cancelable<Vec<(FileId, FileSymbol)>> { | 445 | fn index_resolve(&self, name_ref: ast::NameRef) -> Cancelable<Vec<FileSymbol>> { |
532 | let name = name_ref.text(); | 446 | let name = name_ref.text(); |
533 | let mut query = Query::new(name.to_string()); | 447 | let mut query = Query::new(name.to_string()); |
534 | query.exact(); | 448 | query.exact(); |
535 | query.limit(4); | 449 | query.limit(4); |
536 | self.world_symbols(query) | 450 | crate::symbol_index::world_symbols(self, query) |
537 | } | 451 | } |
538 | } | 452 | } |
539 | 453 | ||
@@ -592,3 +506,91 @@ impl<'a> FnCallNode<'a> { | |||
592 | } | 506 | } |
593 | } | 507 | } |
594 | } | 508 | } |
509 | |||
510 | impl NavigationTarget { | ||
511 | fn node(&self, db: &db::RootDatabase) -> Option<SyntaxNode> { | ||
512 | let source_file = db.source_file(self.file_id); | ||
513 | let source_file = source_file.syntax(); | ||
514 | let node = source_file | ||
515 | .descendants() | ||
516 | .find(|node| node.kind() == self.kind && node.range() == self.range)? | ||
517 | .owned(); | ||
518 | Some(node) | ||
519 | } | ||
520 | |||
521 | fn docs(&self, db: &db::RootDatabase) -> Option<String> { | ||
522 | let node = self.node(db)?; | ||
523 | let node = node.borrowed(); | ||
524 | fn doc_comments<'a, N: ast::DocCommentsOwner<'a>>(node: N) -> Option<String> { | ||
525 | let comments = node.doc_comment_text(); | ||
526 | if comments.is_empty() { | ||
527 | None | ||
528 | } else { | ||
529 | Some(comments) | ||
530 | } | ||
531 | } | ||
532 | |||
533 | visitor() | ||
534 | .visit(doc_comments::<ast::FnDef>) | ||
535 | .visit(doc_comments::<ast::StructDef>) | ||
536 | .visit(doc_comments::<ast::EnumDef>) | ||
537 | .visit(doc_comments::<ast::TraitDef>) | ||
538 | .visit(doc_comments::<ast::Module>) | ||
539 | .visit(doc_comments::<ast::TypeDef>) | ||
540 | .visit(doc_comments::<ast::ConstDef>) | ||
541 | .visit(doc_comments::<ast::StaticDef>) | ||
542 | .accept(node)? | ||
543 | } | ||
544 | |||
545 | /// Get a description of this node. | ||
546 | /// | ||
547 | /// e.g. `struct Name`, `enum Name`, `fn Name` | ||
548 | fn description(&self, db: &db::RootDatabase) -> Option<String> { | ||
549 | // TODO: After type inference is done, add type information to improve the output | ||
550 | let node = self.node(db)?; | ||
551 | let node = node.borrowed(); | ||
552 | // TODO: Refactor to be have less repetition | ||
553 | visitor() | ||
554 | .visit(|node: ast::FnDef| { | ||
555 | let mut string = "fn ".to_string(); | ||
556 | node.name()?.syntax().text().push_to(&mut string); | ||
557 | Some(string) | ||
558 | }) | ||
559 | .visit(|node: ast::StructDef| { | ||
560 | let mut string = "struct ".to_string(); | ||
561 | node.name()?.syntax().text().push_to(&mut string); | ||
562 | Some(string) | ||
563 | }) | ||
564 | .visit(|node: ast::EnumDef| { | ||
565 | let mut string = "enum ".to_string(); | ||
566 | node.name()?.syntax().text().push_to(&mut string); | ||
567 | Some(string) | ||
568 | }) | ||
569 | .visit(|node: ast::TraitDef| { | ||
570 | let mut string = "trait ".to_string(); | ||
571 | node.name()?.syntax().text().push_to(&mut string); | ||
572 | Some(string) | ||
573 | }) | ||
574 | .visit(|node: ast::Module| { | ||
575 | let mut string = "mod ".to_string(); | ||
576 | node.name()?.syntax().text().push_to(&mut string); | ||
577 | Some(string) | ||
578 | }) | ||
579 | .visit(|node: ast::TypeDef| { | ||
580 | let mut string = "type ".to_string(); | ||
581 | node.name()?.syntax().text().push_to(&mut string); | ||
582 | Some(string) | ||
583 | }) | ||
584 | .visit(|node: ast::ConstDef| { | ||
585 | let mut string = "const ".to_string(); | ||
586 | node.name()?.syntax().text().push_to(&mut string); | ||
587 | Some(string) | ||
588 | }) | ||
589 | .visit(|node: ast::StaticDef| { | ||
590 | let mut string = "static ".to_string(); | ||
591 | node.name()?.syntax().text().push_to(&mut string); | ||
592 | Some(string) | ||
593 | }) | ||
594 | .accept(node)? | ||
595 | } | ||
596 | } | ||