aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src/display/navigation_target.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api/src/display/navigation_target.rs')
-rw-r--r--crates/ra_ide_api/src/display/navigation_target.rs398
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
3use hir::{AssocItem, FieldSource, HasSource, ModuleSource}; 3use hir::{AssocItem, Either, FieldSource, HasSource, ModuleSource};
4use ra_db::{FileId, SourceDatabase}; 4use ra_db::{FileId, SourceDatabase};
5use ra_syntax::{ 5use 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
32fn find_range_from_node( 32pub(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
47impl NavigationTarget { 36impl 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
186impl 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
201pub(crate) trait ToNavFromAst {}
202impl ToNavFromAst for hir::Function {}
203impl ToNavFromAst for hir::Const {}
204impl ToNavFromAst for hir::Static {}
205impl ToNavFromAst for hir::Struct {}
206impl ToNavFromAst for hir::Enum {}
207impl ToNavFromAst for hir::EnumVariant {}
208impl ToNavFromAst for hir::Union {}
209impl ToNavFromAst for hir::TypeAlias {}
210impl ToNavFromAst for hir::Trait {}
211
212impl<D> ToNav for D
213where
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
229impl 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
264impl 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
281impl 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
309impl 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
317impl 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
327impl 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
337impl 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
363fn 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
362pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<String> { 378pub(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());