aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/display
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src/display')
-rw-r--r--crates/ide/src/display/navigation_target.rs247
1 files changed, 139 insertions, 108 deletions
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs
index 54b33a98e..cd8ec54fa 100644
--- a/crates/ide/src/display/navigation_target.rs
+++ b/crates/ide/src/display/navigation_target.rs
@@ -1,28 +1,51 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::fmt;
4
3use either::Either; 5use either::Either;
4use hir::{ 6use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, ModuleSource};
5 AssocItem, Documentation, FieldSource, HasAttrs, HasSource, HirFileId, InFile, ModuleSource, 7use ide_db::{
8 base_db::{FileId, SourceDatabase},
9 symbol_index::FileSymbolKind,
6}; 10};
7use ide_db::base_db::{FileId, SourceDatabase};
8use ide_db::{defs::Definition, RootDatabase}; 11use ide_db::{defs::Definition, RootDatabase};
9use syntax::{ 12use syntax::{
10 ast::{self, NameOwner}, 13 ast::{self, NameOwner},
11 match_ast, AstNode, SmolStr, 14 match_ast, AstNode, SmolStr, TextRange,
12 SyntaxKind::{self, IDENT_PAT, LIFETIME_PARAM, TYPE_PARAM},
13 TextRange,
14}; 15};
15 16
16use crate::FileSymbol; 17use crate::FileSymbol;
17 18
18use super::short_label::ShortLabel; 19use super::short_label::ShortLabel;
19 20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
22pub enum SymbolKind {
23 Module,
24 Impl,
25 Field,
26 TypeParam,
27 LifetimeParam,
28 ValueParam,
29 SelfParam,
30 Local,
31 Function,
32 Const,
33 Static,
34 Struct,
35 Enum,
36 Variant,
37 Union,
38 TypeAlias,
39 Trait,
40 Macro,
41}
42
20/// `NavigationTarget` represents and element in the editor's UI which you can 43/// `NavigationTarget` represents and element in the editor's UI which you can
21/// click on to navigate to a particular piece of code. 44/// click on to navigate to a particular piece of code.
22/// 45///
23/// Typically, a `NavigationTarget` corresponds to some element in the source 46/// Typically, a `NavigationTarget` corresponds to some element in the source
24/// code, like a function or a struct, but this is not strictly required. 47/// code, like a function or a struct, but this is not strictly required.
25#[derive(Debug, Clone, PartialEq, Eq, Hash)] 48#[derive(Clone, PartialEq, Eq, Hash)]
26pub struct NavigationTarget { 49pub struct NavigationTarget {
27 pub file_id: FileId, 50 pub file_id: FileId,
28 /// Range which encompasses the whole element. 51 /// Range which encompasses the whole element.
@@ -42,12 +65,30 @@ pub struct NavigationTarget {
42 /// Clients should place the cursor on this range when navigating to this target. 65 /// Clients should place the cursor on this range when navigating to this target.
43 pub focus_range: Option<TextRange>, 66 pub focus_range: Option<TextRange>,
44 pub name: SmolStr, 67 pub name: SmolStr,
45 pub kind: SyntaxKind, 68 pub kind: Option<SymbolKind>,
46 pub container_name: Option<SmolStr>, 69 pub container_name: Option<SmolStr>,
47 pub description: Option<String>, 70 pub description: Option<String>,
48 pub docs: Option<Documentation>, 71 pub docs: Option<Documentation>,
49} 72}
50 73
74impl fmt::Debug for NavigationTarget {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 let mut f = f.debug_struct("NavigationTarget");
77 macro_rules! opt {
78 ($($name:ident)*) => {$(
79 if let Some(it) = &self.$name {
80 f.field(stringify!($name), it);
81 }
82 )*}
83 }
84 f.field("file_id", &self.file_id).field("full_range", &self.full_range);
85 opt!(focus_range);
86 f.field("name", &self.name);
87 opt!(kind container_name description docs);
88 f.finish()
89 }
90}
91
51pub(crate) trait ToNav { 92pub(crate) trait ToNav {
52 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget; 93 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget;
53} 94}
@@ -71,7 +112,7 @@ impl NavigationTarget {
71 name, 112 name,
72 None, 113 None,
73 frange.range, 114 frange.range,
74 src.value.syntax().kind(), 115 SymbolKind::Module,
75 ); 116 );
76 res.docs = module.attrs(db).docs(); 117 res.docs = module.attrs(db).docs();
77 res.description = src.value.short_label(); 118 res.description = src.value.short_label();
@@ -88,8 +129,13 @@ impl NavigationTarget {
88 129
89 #[cfg(test)] 130 #[cfg(test)]
90 pub(crate) fn debug_render(&self) -> String { 131 pub(crate) fn debug_render(&self) -> String {
91 let mut buf = 132 let mut buf = format!(
92 format!("{} {:?} {:?} {:?}", self.name, self.kind, self.file_id, self.full_range); 133 "{} {:?} {:?} {:?}",
134 self.name,
135 self.kind.unwrap(),
136 self.file_id,
137 self.full_range
138 );
93 if let Some(focus_range) = self.focus_range { 139 if let Some(focus_range) = self.focus_range {
94 buf.push_str(&format!(" {:?}", focus_range)) 140 buf.push_str(&format!(" {:?}", focus_range))
95 } 141 }
@@ -103,6 +149,7 @@ impl NavigationTarget {
103 pub(crate) fn from_named( 149 pub(crate) fn from_named(
104 db: &RootDatabase, 150 db: &RootDatabase,
105 node: InFile<&dyn ast::NameOwner>, 151 node: InFile<&dyn ast::NameOwner>,
152 kind: SymbolKind,
106 ) -> NavigationTarget { 153 ) -> NavigationTarget {
107 let name = 154 let name =
108 node.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_")); 155 node.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_"));
@@ -110,32 +157,7 @@ impl NavigationTarget {
110 node.value.name().map(|it| node.with_value(it.syntax()).original_file_range(db).range); 157 node.value.name().map(|it| node.with_value(it.syntax()).original_file_range(db).range);
111 let frange = node.map(|it| it.syntax()).original_file_range(db); 158 let frange = node.map(|it| it.syntax()).original_file_range(db);
112 159
113 NavigationTarget::from_syntax( 160 NavigationTarget::from_syntax(frange.file_id, name, focus_range, frange.range, kind)
114 frange.file_id,
115 name,
116 focus_range,
117 frange.range,
118 node.value.syntax().kind(),
119 )
120 }
121
122 /// Allows `NavigationTarget` to be created from a `DocCommentsOwner` and a `NameOwner`
123 pub(crate) fn from_doc_commented(
124 db: &RootDatabase,
125 named: InFile<&dyn ast::NameOwner>,
126 node: InFile<&dyn ast::DocCommentsOwner>,
127 ) -> NavigationTarget {
128 let name =
129 named.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_"));
130 let frange = node.map(|it| it.syntax()).original_file_range(db);
131
132 NavigationTarget::from_syntax(
133 frange.file_id,
134 name,
135 None,
136 frange.range,
137 node.value.syntax().kind(),
138 )
139 } 161 }
140 162
141 fn from_syntax( 163 fn from_syntax(
@@ -143,12 +165,12 @@ impl NavigationTarget {
143 name: SmolStr, 165 name: SmolStr,
144 focus_range: Option<TextRange>, 166 focus_range: Option<TextRange>,
145 full_range: TextRange, 167 full_range: TextRange,
146 kind: SyntaxKind, 168 kind: SymbolKind,
147 ) -> NavigationTarget { 169 ) -> NavigationTarget {
148 NavigationTarget { 170 NavigationTarget {
149 file_id, 171 file_id,
150 name, 172 name,
151 kind, 173 kind: Some(kind),
152 full_range, 174 full_range,
153 focus_range, 175 focus_range,
154 container_name: None, 176 container_name: None,
@@ -163,12 +185,22 @@ impl ToNav for FileSymbol {
163 NavigationTarget { 185 NavigationTarget {
164 file_id: self.file_id, 186 file_id: self.file_id,
165 name: self.name.clone(), 187 name: self.name.clone(),
166 kind: self.kind, 188 kind: Some(match self.kind {
189 FileSymbolKind::Function => SymbolKind::Function,
190 FileSymbolKind::Struct => SymbolKind::Struct,
191 FileSymbolKind::Enum => SymbolKind::Enum,
192 FileSymbolKind::Trait => SymbolKind::Trait,
193 FileSymbolKind::Module => SymbolKind::Module,
194 FileSymbolKind::TypeAlias => SymbolKind::TypeAlias,
195 FileSymbolKind::Const => SymbolKind::Const,
196 FileSymbolKind::Static => SymbolKind::Static,
197 FileSymbolKind::Macro => SymbolKind::Macro,
198 }),
167 full_range: self.range, 199 full_range: self.range,
168 focus_range: self.name_range, 200 focus_range: self.name_range,
169 container_name: self.container_name.clone(), 201 container_name: self.container_name.clone(),
170 description: description_from_symbol(db, self), 202 description: description_from_symbol(db, self),
171 docs: docs_from_symbol(db, self), 203 docs: None,
172 } 204 }
173 } 205 }
174} 206}
@@ -176,7 +208,15 @@ impl ToNav for FileSymbol {
176impl TryToNav for Definition { 208impl TryToNav for Definition {
177 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { 209 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
178 match self { 210 match self {
179 Definition::Macro(it) => Some(it.to_nav(db)), 211 Definition::Macro(it) => {
212 // FIXME: Currently proc-macro do not have ast-node,
213 // such that it does not have source
214 // more discussion: https://github.com/rust-analyzer/rust-analyzer/issues/6913
215 if it.is_proc_macro() {
216 return None;
217 }
218 Some(it.to_nav(db))
219 }
180 Definition::Field(it) => Some(it.to_nav(db)), 220 Definition::Field(it) => Some(it.to_nav(db)),
181 Definition::ModuleDef(it) => it.try_to_nav(db), 221 Definition::ModuleDef(it) => it.try_to_nav(db),
182 Definition::SelfType(it) => Some(it.to_nav(db)), 222 Definition::SelfType(it) => Some(it.to_nav(db)),
@@ -193,7 +233,7 @@ impl TryToNav for hir::ModuleDef {
193 hir::ModuleDef::Module(it) => it.to_nav(db), 233 hir::ModuleDef::Module(it) => it.to_nav(db),
194 hir::ModuleDef::Function(it) => it.to_nav(db), 234 hir::ModuleDef::Function(it) => it.to_nav(db),
195 hir::ModuleDef::Adt(it) => it.to_nav(db), 235 hir::ModuleDef::Adt(it) => it.to_nav(db),
196 hir::ModuleDef::EnumVariant(it) => it.to_nav(db), 236 hir::ModuleDef::Variant(it) => it.to_nav(db),
197 hir::ModuleDef::Const(it) => it.to_nav(db), 237 hir::ModuleDef::Const(it) => it.to_nav(db),
198 hir::ModuleDef::Static(it) => it.to_nav(db), 238 hir::ModuleDef::Static(it) => it.to_nav(db),
199 hir::ModuleDef::Trait(it) => it.to_nav(db), 239 hir::ModuleDef::Trait(it) => it.to_nav(db),
@@ -204,16 +244,36 @@ impl TryToNav for hir::ModuleDef {
204 } 244 }
205} 245}
206 246
207pub(crate) trait ToNavFromAst {} 247pub(crate) trait ToNavFromAst {
208impl ToNavFromAst for hir::Function {} 248 const KIND: SymbolKind;
209impl ToNavFromAst for hir::Const {} 249}
210impl ToNavFromAst for hir::Static {} 250impl ToNavFromAst for hir::Function {
211impl ToNavFromAst for hir::Struct {} 251 const KIND: SymbolKind = SymbolKind::Function;
212impl ToNavFromAst for hir::Enum {} 252}
213impl ToNavFromAst for hir::EnumVariant {} 253impl ToNavFromAst for hir::Const {
214impl ToNavFromAst for hir::Union {} 254 const KIND: SymbolKind = SymbolKind::Const;
215impl ToNavFromAst for hir::TypeAlias {} 255}
216impl ToNavFromAst for hir::Trait {} 256impl ToNavFromAst for hir::Static {
257 const KIND: SymbolKind = SymbolKind::Static;
258}
259impl ToNavFromAst for hir::Struct {
260 const KIND: SymbolKind = SymbolKind::Struct;
261}
262impl ToNavFromAst for hir::Enum {
263 const KIND: SymbolKind = SymbolKind::Enum;
264}
265impl ToNavFromAst for hir::Variant {
266 const KIND: SymbolKind = SymbolKind::Variant;
267}
268impl ToNavFromAst for hir::Union {
269 const KIND: SymbolKind = SymbolKind::Union;
270}
271impl ToNavFromAst for hir::TypeAlias {
272 const KIND: SymbolKind = SymbolKind::TypeAlias;
273}
274impl ToNavFromAst for hir::Trait {
275 const KIND: SymbolKind = SymbolKind::Trait;
276}
217 277
218impl<D> ToNav for D 278impl<D> ToNav for D
219where 279where
@@ -222,8 +282,11 @@ where
222{ 282{
223 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 283 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
224 let src = self.source(db); 284 let src = self.source(db);
225 let mut res = 285 let mut res = NavigationTarget::from_named(
226 NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner)); 286 db,
287 src.as_ref().map(|it| it as &dyn ast::NameOwner),
288 D::KIND,
289 );
227 res.docs = self.docs(db); 290 res.docs = self.docs(db);
228 res.description = src.value.short_label(); 291 res.description = src.value.short_label();
229 res 292 res
@@ -241,7 +304,7 @@ impl ToNav for hir::Module {
241 } 304 }
242 }; 305 };
243 let frange = src.with_value(syntax).original_file_range(db); 306 let frange = src.with_value(syntax).original_file_range(db);
244 NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, syntax.kind()) 307 NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, SymbolKind::Module)
245 } 308 }
246} 309}
247 310
@@ -265,7 +328,7 @@ impl ToNav for hir::Impl {
265 "impl".into(), 328 "impl".into(),
266 focus_range, 329 focus_range,
267 frange.range, 330 frange.range,
268 src.value.syntax().kind(), 331 SymbolKind::Impl,
269 ) 332 )
270 } 333 }
271} 334}
@@ -276,7 +339,8 @@ impl ToNav for hir::Field {
276 339
277 match &src.value { 340 match &src.value {
278 FieldSource::Named(it) => { 341 FieldSource::Named(it) => {
279 let mut res = NavigationTarget::from_named(db, src.with_value(it)); 342 let mut res =
343 NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field);
280 res.docs = self.docs(db); 344 res.docs = self.docs(db);
281 res.description = it.short_label(); 345 res.description = it.short_label();
282 res 346 res
@@ -288,7 +352,7 @@ impl ToNav for hir::Field {
288 "".into(), 352 "".into(),
289 None, 353 None,
290 frange.range, 354 frange.range,
291 it.syntax().kind(), 355 SymbolKind::Field,
292 ) 356 )
293 } 357 }
294 } 358 }
@@ -299,8 +363,11 @@ impl ToNav for hir::MacroDef {
299 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 363 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
300 let src = self.source(db); 364 let src = self.source(db);
301 log::debug!("nav target {:#?}", src.value.syntax()); 365 log::debug!("nav target {:#?}", src.value.syntax());
302 let mut res = 366 let mut res = NavigationTarget::from_named(
303 NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner)); 367 db,
368 src.as_ref().map(|it| it as &dyn ast::NameOwner),
369 SymbolKind::Macro,
370 );
304 res.docs = self.docs(db); 371 res.docs = self.docs(db);
305 res 372 res
306 } 373 }
@@ -340,10 +407,11 @@ impl ToNav for hir::Local {
340 Some(it) => it.to_string().into(), 407 Some(it) => it.to_string().into(),
341 None => "".into(), 408 None => "".into(),
342 }; 409 };
410 let kind = if self.is_param(db) { SymbolKind::ValueParam } else { SymbolKind::Local };
343 NavigationTarget { 411 NavigationTarget {
344 file_id: full_range.file_id, 412 file_id: full_range.file_id,
345 name, 413 name,
346 kind: IDENT_PAT, 414 kind: Some(kind),
347 full_range: full_range.range, 415 full_range: full_range.range,
348 focus_range: None, 416 focus_range: None,
349 container_name: None, 417 container_name: None,
@@ -367,7 +435,7 @@ impl ToNav for hir::TypeParam {
367 NavigationTarget { 435 NavigationTarget {
368 file_id: src.file_id.original_file(db), 436 file_id: src.file_id.original_file(db),
369 name: self.name(db).to_string().into(), 437 name: self.name(db).to_string().into(),
370 kind: TYPE_PARAM, 438 kind: Some(SymbolKind::TypeParam),
371 full_range, 439 full_range,
372 focus_range, 440 focus_range,
373 container_name: None, 441 container_name: None,
@@ -384,7 +452,7 @@ impl ToNav for hir::LifetimeParam {
384 NavigationTarget { 452 NavigationTarget {
385 file_id: src.file_id.original_file(db), 453 file_id: src.file_id.original_file(db),
386 name: self.name(db).to_string().into(), 454 name: self.name(db).to_string().into(),
387 kind: LIFETIME_PARAM, 455 kind: Some(SymbolKind::LifetimeParam),
388 full_range, 456 full_range,
389 focus_range: Some(full_range), 457 focus_range: Some(full_range),
390 container_name: None, 458 container_name: None,
@@ -394,30 +462,6 @@ impl ToNav for hir::LifetimeParam {
394 } 462 }
395} 463}
396 464
397pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<Documentation> {
398 let parse = db.parse(symbol.file_id);
399 let node = symbol.ptr.to_node(parse.tree().syntax());
400 let file_id = HirFileId::from(symbol.file_id);
401
402 let it = match_ast! {
403 match node {
404 ast::Fn(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
405 ast::Struct(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
406 ast::Enum(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
407 ast::Trait(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
408 ast::Module(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
409 ast::TypeAlias(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
410 ast::Const(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
411 ast::Static(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
412 ast::RecordField(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
413 ast::Variant(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
414 ast::MacroCall(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)),
415 _ => return None,
416 }
417 };
418 it.docs()
419}
420
421/// Get a description of a symbol. 465/// Get a description of a symbol.
422/// 466///
423/// e.g. `struct Name`, `enum Name`, `fn Name` 467/// e.g. `struct Name`, `enum Name`, `fn Name`
@@ -465,34 +509,21 @@ fn foo() { enum FooInner { } }
465 0, 509 0,
466 ), 510 ),
467 full_range: 0..17, 511 full_range: 0..17,
468 focus_range: Some( 512 focus_range: 5..13,
469 5..13,
470 ),
471 name: "FooInner", 513 name: "FooInner",
472 kind: ENUM, 514 kind: Enum,
473 container_name: None, 515 description: "enum FooInner",
474 description: Some(
475 "enum FooInner",
476 ),
477 docs: None,
478 }, 516 },
479 NavigationTarget { 517 NavigationTarget {
480 file_id: FileId( 518 file_id: FileId(
481 0, 519 0,
482 ), 520 ),
483 full_range: 29..46, 521 full_range: 29..46,
484 focus_range: Some( 522 focus_range: 34..42,
485 34..42,
486 ),
487 name: "FooInner", 523 name: "FooInner",
488 kind: ENUM, 524 kind: Enum,
489 container_name: Some( 525 container_name: "foo",
490 "foo", 526 description: "enum FooInner",
491 ),
492 description: Some(
493 "enum FooInner",
494 ),
495 docs: None,
496 }, 527 },
497 ] 528 ]
498 "#]] 529 "#]]