diff options
author | Seivan Heidari <[email protected]> | 2019-11-11 13:31:09 +0000 |
---|---|---|
committer | Seivan Heidari <[email protected]> | 2019-11-11 13:31:09 +0000 |
commit | 68a5ff050faf514e9d122212a66703ca8ce66ab7 (patch) | |
tree | 0c389d2680aae82c1805a52f8315312724134341 /crates/ra_ide_api/src/display | |
parent | 7cd075ff0beb97039cd1d1c6c021abf89339731b (diff) | |
parent | a599147b4232c0d4f6b071a3a96e86f903f4cf52 (diff) |
Merge branch 'master' of https://github.com/rust-analyzer/rust-analyzer into feature/themes
Diffstat (limited to 'crates/ra_ide_api/src/display')
-rw-r--r-- | crates/ra_ide_api/src/display/navigation_target.rs | 398 |
1 files changed, 207 insertions, 191 deletions
diff --git a/crates/ra_ide_api/src/display/navigation_target.rs b/crates/ra_ide_api/src/display/navigation_target.rs index 1bf81e7d5..f7ad08515 100644 --- a/crates/ra_ide_api/src/display/navigation_target.rs +++ b/crates/ra_ide_api/src/display/navigation_target.rs | |||
@@ -1,11 +1,11 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{AssocItem, FieldSource, HasSource, ModuleSource}; | 3 | use hir::{AssocItem, Either, FieldSource, HasSource, ModuleSource}; |
4 | use ra_db::{FileId, SourceDatabase}; | 4 | use ra_db::{FileId, SourceDatabase}; |
5 | use ra_syntax::{ | 5 | use ra_syntax::{ |
6 | ast::{self, DocCommentsOwner}, | 6 | ast::{self, DocCommentsOwner, NameOwner}, |
7 | match_ast, AstNode, AstPtr, SmolStr, | 7 | match_ast, AstNode, SmolStr, |
8 | SyntaxKind::{self, NAME}, | 8 | SyntaxKind::{self, BIND_PAT}, |
9 | SyntaxNode, TextRange, | 9 | SyntaxNode, TextRange, |
10 | }; | 10 | }; |
11 | 11 | ||
@@ -29,19 +29,8 @@ pub struct NavigationTarget { | |||
29 | docs: Option<String>, | 29 | docs: Option<String>, |
30 | } | 30 | } |
31 | 31 | ||
32 | fn find_range_from_node( | 32 | pub(crate) trait ToNav { |
33 | db: &RootDatabase, | 33 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget; |
34 | src: hir::HirFileId, | ||
35 | node: &SyntaxNode, | ||
36 | ) -> (FileId, TextRange) { | ||
37 | let text_range = node.text_range(); | ||
38 | let (file_id, text_range) = src | ||
39 | .expansion_info(db) | ||
40 | .and_then(|expansion_info| expansion_info.find_range(text_range)) | ||
41 | .unwrap_or((src, text_range)); | ||
42 | |||
43 | // FIXME: handle recursive macro generated macro | ||
44 | (file_id.original_file(db), text_range) | ||
45 | } | 34 | } |
46 | 35 | ||
47 | impl NavigationTarget { | 36 | impl NavigationTarget { |
@@ -87,88 +76,6 @@ impl NavigationTarget { | |||
87 | self.focus_range | 76 | self.focus_range |
88 | } | 77 | } |
89 | 78 | ||
90 | pub(crate) fn from_bind_pat( | ||
91 | db: &RootDatabase, | ||
92 | file_id: FileId, | ||
93 | pat: &ast::BindPat, | ||
94 | ) -> NavigationTarget { | ||
95 | NavigationTarget::from_named(db, file_id.into(), pat, None, None) | ||
96 | } | ||
97 | |||
98 | pub(crate) fn from_symbol(db: &RootDatabase, symbol: FileSymbol) -> NavigationTarget { | ||
99 | NavigationTarget { | ||
100 | file_id: symbol.file_id, | ||
101 | name: symbol.name.clone(), | ||
102 | kind: symbol.ptr.kind(), | ||
103 | full_range: symbol.ptr.range(), | ||
104 | focus_range: symbol.name_range, | ||
105 | container_name: symbol.container_name.clone(), | ||
106 | description: description_from_symbol(db, &symbol), | ||
107 | docs: docs_from_symbol(db, &symbol), | ||
108 | } | ||
109 | } | ||
110 | |||
111 | pub(crate) fn from_pat( | ||
112 | db: &RootDatabase, | ||
113 | file_id: FileId, | ||
114 | pat: AstPtr<ast::BindPat>, | ||
115 | ) -> NavigationTarget { | ||
116 | let parse = db.parse(file_id); | ||
117 | let pat = pat.to_node(parse.tree().syntax()); | ||
118 | NavigationTarget::from_bind_pat(db, file_id, &pat) | ||
119 | } | ||
120 | |||
121 | pub(crate) fn from_self_param( | ||
122 | file_id: FileId, | ||
123 | par: AstPtr<ast::SelfParam>, | ||
124 | ) -> NavigationTarget { | ||
125 | let (name, full_range) = ("self".into(), par.syntax_node_ptr().range()); | ||
126 | |||
127 | NavigationTarget { | ||
128 | file_id, | ||
129 | name, | ||
130 | full_range, | ||
131 | focus_range: None, | ||
132 | kind: NAME, | ||
133 | container_name: None, | ||
134 | description: None, //< No document node for SelfParam | ||
135 | docs: None, //< No document node for SelfParam | ||
136 | } | ||
137 | } | ||
138 | |||
139 | pub(crate) fn from_module(db: &RootDatabase, module: hir::Module) -> NavigationTarget { | ||
140 | let src = module.definition_source(db); | ||
141 | let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); | ||
142 | match src.ast { | ||
143 | ModuleSource::SourceFile(node) => { | ||
144 | let (file_id, text_range) = find_range_from_node(db, src.file_id, node.syntax()); | ||
145 | |||
146 | NavigationTarget::from_syntax( | ||
147 | file_id, | ||
148 | name, | ||
149 | None, | ||
150 | text_range, | ||
151 | node.syntax(), | ||
152 | None, | ||
153 | None, | ||
154 | ) | ||
155 | } | ||
156 | ModuleSource::Module(node) => { | ||
157 | let (file_id, text_range) = find_range_from_node(db, src.file_id, node.syntax()); | ||
158 | |||
159 | NavigationTarget::from_syntax( | ||
160 | file_id, | ||
161 | name, | ||
162 | None, | ||
163 | text_range, | ||
164 | node.syntax(), | ||
165 | node.doc_comment_text(), | ||
166 | node.short_label(), | ||
167 | ) | ||
168 | } | ||
169 | } | ||
170 | } | ||
171 | |||
172 | pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget { | 79 | pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget { |
173 | let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); | 80 | let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); |
174 | if let Some(src) = module.declaration_source(db) { | 81 | if let Some(src) = module.declaration_source(db) { |
@@ -183,55 +90,7 @@ impl NavigationTarget { | |||
183 | src.ast.short_label(), | 90 | src.ast.short_label(), |
184 | ); | 91 | ); |
185 | } | 92 | } |
186 | NavigationTarget::from_module(db, module) | 93 | module.to_nav(db) |
187 | } | ||
188 | |||
189 | pub(crate) fn from_field(db: &RootDatabase, field: hir::StructField) -> NavigationTarget { | ||
190 | let src = field.source(db); | ||
191 | match src.ast { | ||
192 | FieldSource::Named(it) => NavigationTarget::from_named( | ||
193 | db, | ||
194 | src.file_id, | ||
195 | &it, | ||
196 | it.doc_comment_text(), | ||
197 | it.short_label(), | ||
198 | ), | ||
199 | FieldSource::Pos(it) => { | ||
200 | let (file_id, text_range) = find_range_from_node(db, src.file_id, it.syntax()); | ||
201 | NavigationTarget::from_syntax( | ||
202 | file_id, | ||
203 | "".into(), | ||
204 | None, | ||
205 | text_range, | ||
206 | it.syntax(), | ||
207 | None, | ||
208 | None, | ||
209 | ) | ||
210 | } | ||
211 | } | ||
212 | } | ||
213 | |||
214 | pub(crate) fn from_def_source<A, D>(db: &RootDatabase, def: D) -> NavigationTarget | ||
215 | where | ||
216 | D: HasSource<Ast = A>, | ||
217 | A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel, | ||
218 | { | ||
219 | let src = def.source(db); | ||
220 | NavigationTarget::from_named( | ||
221 | db, | ||
222 | src.file_id, | ||
223 | &src.ast, | ||
224 | src.ast.doc_comment_text(), | ||
225 | src.ast.short_label(), | ||
226 | ) | ||
227 | } | ||
228 | |||
229 | pub(crate) fn from_adt_def(db: &RootDatabase, adt_def: hir::Adt) -> NavigationTarget { | ||
230 | match adt_def { | ||
231 | hir::Adt::Struct(it) => NavigationTarget::from_def_source(db, it), | ||
232 | hir::Adt::Union(it) => NavigationTarget::from_def_source(db, it), | ||
233 | hir::Adt::Enum(it) => NavigationTarget::from_def_source(db, it), | ||
234 | } | ||
235 | } | 94 | } |
236 | 95 | ||
237 | pub(crate) fn from_def( | 96 | pub(crate) fn from_def( |
@@ -239,14 +98,14 @@ impl NavigationTarget { | |||
239 | module_def: hir::ModuleDef, | 98 | module_def: hir::ModuleDef, |
240 | ) -> Option<NavigationTarget> { | 99 | ) -> Option<NavigationTarget> { |
241 | let nav = match module_def { | 100 | let nav = match module_def { |
242 | hir::ModuleDef::Module(module) => NavigationTarget::from_module(db, module), | 101 | hir::ModuleDef::Module(module) => module.to_nav(db), |
243 | hir::ModuleDef::Function(func) => NavigationTarget::from_def_source(db, func), | 102 | hir::ModuleDef::Function(it) => it.to_nav(db), |
244 | hir::ModuleDef::Adt(it) => NavigationTarget::from_adt_def(db, it), | 103 | hir::ModuleDef::Adt(it) => it.to_nav(db), |
245 | hir::ModuleDef::Const(it) => NavigationTarget::from_def_source(db, it), | 104 | hir::ModuleDef::Const(it) => it.to_nav(db), |
246 | hir::ModuleDef::Static(it) => NavigationTarget::from_def_source(db, it), | 105 | hir::ModuleDef::Static(it) => it.to_nav(db), |
247 | hir::ModuleDef::EnumVariant(it) => NavigationTarget::from_def_source(db, it), | 106 | hir::ModuleDef::EnumVariant(it) => it.to_nav(db), |
248 | hir::ModuleDef::Trait(it) => NavigationTarget::from_def_source(db, it), | 107 | hir::ModuleDef::Trait(it) => it.to_nav(db), |
249 | hir::ModuleDef::TypeAlias(it) => NavigationTarget::from_def_source(db, it), | 108 | hir::ModuleDef::TypeAlias(it) => it.to_nav(db), |
250 | hir::ModuleDef::BuiltinType(..) => { | 109 | hir::ModuleDef::BuiltinType(..) => { |
251 | return None; | 110 | return None; |
252 | } | 111 | } |
@@ -254,41 +113,6 @@ impl NavigationTarget { | |||
254 | Some(nav) | 113 | Some(nav) |
255 | } | 114 | } |
256 | 115 | ||
257 | pub(crate) fn from_impl_block( | ||
258 | db: &RootDatabase, | ||
259 | impl_block: hir::ImplBlock, | ||
260 | ) -> NavigationTarget { | ||
261 | let src = impl_block.source(db); | ||
262 | let (file_id, text_range) = find_range_from_node(db, src.file_id, src.ast.syntax()); | ||
263 | |||
264 | NavigationTarget::from_syntax( | ||
265 | file_id, | ||
266 | "impl".into(), | ||
267 | None, | ||
268 | text_range, | ||
269 | src.ast.syntax(), | ||
270 | None, | ||
271 | None, | ||
272 | ) | ||
273 | } | ||
274 | |||
275 | pub(crate) fn from_assoc_item( | ||
276 | db: &RootDatabase, | ||
277 | assoc_item: hir::AssocItem, | ||
278 | ) -> NavigationTarget { | ||
279 | match assoc_item { | ||
280 | AssocItem::Function(it) => NavigationTarget::from_def_source(db, it), | ||
281 | AssocItem::Const(it) => NavigationTarget::from_def_source(db, it), | ||
282 | AssocItem::TypeAlias(it) => NavigationTarget::from_def_source(db, it), | ||
283 | } | ||
284 | } | ||
285 | |||
286 | pub(crate) fn from_macro_def(db: &RootDatabase, macro_call: hir::MacroDef) -> NavigationTarget { | ||
287 | let src = macro_call.source(db); | ||
288 | log::debug!("nav target {:#?}", src.ast.syntax()); | ||
289 | NavigationTarget::from_named(db, src.file_id, &src.ast, src.ast.doc_comment_text(), None) | ||
290 | } | ||
291 | |||
292 | #[cfg(test)] | 116 | #[cfg(test)] |
293 | pub(crate) fn assert_match(&self, expected: &str) { | 117 | pub(crate) fn assert_match(&self, expected: &str) { |
294 | let actual = self.debug_render(); | 118 | let actual = self.debug_render(); |
@@ -359,6 +183,198 @@ impl NavigationTarget { | |||
359 | } | 183 | } |
360 | } | 184 | } |
361 | 185 | ||
186 | impl ToNav for FileSymbol { | ||
187 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | ||
188 | NavigationTarget { | ||
189 | file_id: self.file_id, | ||
190 | name: self.name.clone(), | ||
191 | kind: self.ptr.kind(), | ||
192 | full_range: self.ptr.range(), | ||
193 | focus_range: self.name_range, | ||
194 | container_name: self.container_name.clone(), | ||
195 | description: description_from_symbol(db, self), | ||
196 | docs: docs_from_symbol(db, self), | ||
197 | } | ||
198 | } | ||
199 | } | ||
200 | |||
201 | pub(crate) trait ToNavFromAst {} | ||
202 | impl ToNavFromAst for hir::Function {} | ||
203 | impl ToNavFromAst for hir::Const {} | ||
204 | impl ToNavFromAst for hir::Static {} | ||
205 | impl ToNavFromAst for hir::Struct {} | ||
206 | impl ToNavFromAst for hir::Enum {} | ||
207 | impl ToNavFromAst for hir::EnumVariant {} | ||
208 | impl ToNavFromAst for hir::Union {} | ||
209 | impl ToNavFromAst for hir::TypeAlias {} | ||
210 | impl ToNavFromAst for hir::Trait {} | ||
211 | |||
212 | impl<D> ToNav for D | ||
213 | where | ||
214 | D: HasSource + ToNavFromAst + Copy, | ||
215 | D::Ast: ast::DocCommentsOwner + ast::NameOwner + ShortLabel, | ||
216 | { | ||
217 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | ||
218 | let src = self.source(db); | ||
219 | NavigationTarget::from_named( | ||
220 | db, | ||
221 | src.file_id, | ||
222 | &src.ast, | ||
223 | src.ast.doc_comment_text(), | ||
224 | src.ast.short_label(), | ||
225 | ) | ||
226 | } | ||
227 | } | ||
228 | |||
229 | impl ToNav for hir::Module { | ||
230 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | ||
231 | let src = self.definition_source(db); | ||
232 | let name = self.name(db).map(|it| it.to_string().into()).unwrap_or_default(); | ||
233 | match src.ast { | ||
234 | ModuleSource::SourceFile(node) => { | ||
235 | let (file_id, text_range) = find_range_from_node(db, src.file_id, node.syntax()); | ||
236 | |||
237 | NavigationTarget::from_syntax( | ||
238 | file_id, | ||
239 | name, | ||
240 | None, | ||
241 | text_range, | ||
242 | node.syntax(), | ||
243 | None, | ||
244 | None, | ||
245 | ) | ||
246 | } | ||
247 | ModuleSource::Module(node) => { | ||
248 | let (file_id, text_range) = find_range_from_node(db, src.file_id, node.syntax()); | ||
249 | |||
250 | NavigationTarget::from_syntax( | ||
251 | file_id, | ||
252 | name, | ||
253 | None, | ||
254 | text_range, | ||
255 | node.syntax(), | ||
256 | node.doc_comment_text(), | ||
257 | node.short_label(), | ||
258 | ) | ||
259 | } | ||
260 | } | ||
261 | } | ||
262 | } | ||
263 | |||
264 | impl ToNav for hir::ImplBlock { | ||
265 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | ||
266 | let src = self.source(db); | ||
267 | let (file_id, text_range) = find_range_from_node(db, src.file_id, src.ast.syntax()); | ||
268 | |||
269 | NavigationTarget::from_syntax( | ||
270 | file_id, | ||
271 | "impl".into(), | ||
272 | None, | ||
273 | text_range, | ||
274 | src.ast.syntax(), | ||
275 | None, | ||
276 | None, | ||
277 | ) | ||
278 | } | ||
279 | } | ||
280 | |||
281 | impl ToNav for hir::StructField { | ||
282 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | ||
283 | let src = self.source(db); | ||
284 | |||
285 | match src.ast { | ||
286 | FieldSource::Named(it) => NavigationTarget::from_named( | ||
287 | db, | ||
288 | src.file_id, | ||
289 | &it, | ||
290 | it.doc_comment_text(), | ||
291 | it.short_label(), | ||
292 | ), | ||
293 | FieldSource::Pos(it) => { | ||
294 | let (file_id, text_range) = find_range_from_node(db, src.file_id, it.syntax()); | ||
295 | NavigationTarget::from_syntax( | ||
296 | file_id, | ||
297 | "".into(), | ||
298 | None, | ||
299 | text_range, | ||
300 | it.syntax(), | ||
301 | None, | ||
302 | None, | ||
303 | ) | ||
304 | } | ||
305 | } | ||
306 | } | ||
307 | } | ||
308 | |||
309 | impl ToNav for hir::MacroDef { | ||
310 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | ||
311 | let src = self.source(db); | ||
312 | log::debug!("nav target {:#?}", src.ast.syntax()); | ||
313 | NavigationTarget::from_named(db, src.file_id, &src.ast, src.ast.doc_comment_text(), None) | ||
314 | } | ||
315 | } | ||
316 | |||
317 | impl ToNav for hir::Adt { | ||
318 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | ||
319 | match self { | ||
320 | hir::Adt::Struct(it) => it.to_nav(db), | ||
321 | hir::Adt::Union(it) => it.to_nav(db), | ||
322 | hir::Adt::Enum(it) => it.to_nav(db), | ||
323 | } | ||
324 | } | ||
325 | } | ||
326 | |||
327 | impl ToNav for hir::AssocItem { | ||
328 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | ||
329 | match self { | ||
330 | AssocItem::Function(it) => it.to_nav(db), | ||
331 | AssocItem::Const(it) => it.to_nav(db), | ||
332 | AssocItem::TypeAlias(it) => it.to_nav(db), | ||
333 | } | ||
334 | } | ||
335 | } | ||
336 | |||
337 | impl ToNav for hir::Local { | ||
338 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | ||
339 | let src = self.source(db); | ||
340 | let (full_range, focus_range) = match src.ast { | ||
341 | Either::A(it) => { | ||
342 | (it.syntax().text_range(), it.name().map(|it| it.syntax().text_range())) | ||
343 | } | ||
344 | Either::B(it) => (it.syntax().text_range(), Some(it.self_kw_token().text_range())), | ||
345 | }; | ||
346 | let name = match self.name(db) { | ||
347 | Some(it) => it.to_string().into(), | ||
348 | None => "".into(), | ||
349 | }; | ||
350 | NavigationTarget { | ||
351 | file_id: src.file_id.original_file(db), | ||
352 | name, | ||
353 | kind: BIND_PAT, | ||
354 | full_range, | ||
355 | focus_range, | ||
356 | container_name: None, | ||
357 | description: None, | ||
358 | docs: None, | ||
359 | } | ||
360 | } | ||
361 | } | ||
362 | |||
363 | fn find_range_from_node( | ||
364 | db: &RootDatabase, | ||
365 | src: hir::HirFileId, | ||
366 | node: &SyntaxNode, | ||
367 | ) -> (FileId, TextRange) { | ||
368 | let text_range = node.text_range(); | ||
369 | let (file_id, text_range) = src | ||
370 | .expansion_info(db) | ||
371 | .and_then(|expansion_info| expansion_info.find_range(text_range)) | ||
372 | .unwrap_or((src, text_range)); | ||
373 | |||
374 | // FIXME: handle recursive macro generated macro | ||
375 | (file_id.original_file(db), text_range) | ||
376 | } | ||
377 | |||
362 | pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<String> { | 378 | pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<String> { |
363 | let parse = db.parse(symbol.file_id); | 379 | let parse = db.parse(symbol.file_id); |
364 | let node = symbol.ptr.to_node(parse.tree().syntax()); | 380 | let node = symbol.ptr.to_node(parse.tree().syntax()); |