diff options
Diffstat (limited to 'crates/ide/src/display')
-rw-r--r-- | crates/ide/src/display/navigation_target.rs | 188 |
1 files changed, 128 insertions, 60 deletions
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs index 48acb8c93..7d0514105 100644 --- a/crates/ide/src/display/navigation_target.rs +++ b/crates/ide/src/display/navigation_target.rs | |||
@@ -1,26 +1,51 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use std::fmt; | ||
4 | |||
3 | use either::Either; | 5 | use either::Either; |
4 | use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, ModuleSource}; | 6 | use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, ModuleSource}; |
5 | use ide_db::base_db::{FileId, SourceDatabase}; | 7 | use ide_db::{ |
8 | base_db::{FileId, SourceDatabase}, | ||
9 | symbol_index::FileSymbolKind, | ||
10 | }; | ||
6 | use ide_db::{defs::Definition, RootDatabase}; | 11 | use ide_db::{defs::Definition, RootDatabase}; |
7 | use syntax::{ | 12 | use syntax::{ |
8 | ast::{self, NameOwner}, | 13 | ast::{self, NameOwner}, |
9 | match_ast, AstNode, SmolStr, | 14 | match_ast, AstNode, SmolStr, TextRange, |
10 | SyntaxKind::{self, IDENT_PAT, LIFETIME_PARAM, TYPE_PARAM}, | ||
11 | TextRange, | ||
12 | }; | 15 | }; |
13 | 16 | ||
14 | use crate::FileSymbol; | 17 | use crate::FileSymbol; |
15 | 18 | ||
16 | use super::short_label::ShortLabel; | 19 | use super::short_label::ShortLabel; |
17 | 20 | ||
21 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||
22 | pub 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 | |||
18 | /// `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 |
19 | /// click on to navigate to a particular piece of code. | 44 | /// click on to navigate to a particular piece of code. |
20 | /// | 45 | /// |
21 | /// Typically, a `NavigationTarget` corresponds to some element in the source | 46 | /// Typically, a `NavigationTarget` corresponds to some element in the source |
22 | /// 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. |
23 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 48 | #[derive(Clone, PartialEq, Eq, Hash)] |
24 | pub struct NavigationTarget { | 49 | pub struct NavigationTarget { |
25 | pub file_id: FileId, | 50 | pub file_id: FileId, |
26 | /// Range which encompasses the whole element. | 51 | /// Range which encompasses the whole element. |
@@ -40,12 +65,30 @@ pub struct NavigationTarget { | |||
40 | /// 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. |
41 | pub focus_range: Option<TextRange>, | 66 | pub focus_range: Option<TextRange>, |
42 | pub name: SmolStr, | 67 | pub name: SmolStr, |
43 | pub kind: SyntaxKind, | 68 | pub kind: Option<SymbolKind>, |
44 | pub container_name: Option<SmolStr>, | 69 | pub container_name: Option<SmolStr>, |
45 | pub description: Option<String>, | 70 | pub description: Option<String>, |
46 | pub docs: Option<Documentation>, | 71 | pub docs: Option<Documentation>, |
47 | } | 72 | } |
48 | 73 | ||
74 | impl 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 | |||
49 | pub(crate) trait ToNav { | 92 | pub(crate) trait ToNav { |
50 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget; | 93 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget; |
51 | } | 94 | } |
@@ -69,7 +112,7 @@ impl NavigationTarget { | |||
69 | name, | 112 | name, |
70 | None, | 113 | None, |
71 | frange.range, | 114 | frange.range, |
72 | src.value.syntax().kind(), | 115 | SymbolKind::Module, |
73 | ); | 116 | ); |
74 | res.docs = module.attrs(db).docs(); | 117 | res.docs = module.attrs(db).docs(); |
75 | res.description = src.value.short_label(); | 118 | res.description = src.value.short_label(); |
@@ -86,8 +129,13 @@ impl NavigationTarget { | |||
86 | 129 | ||
87 | #[cfg(test)] | 130 | #[cfg(test)] |
88 | pub(crate) fn debug_render(&self) -> String { | 131 | pub(crate) fn debug_render(&self) -> String { |
89 | let mut buf = | 132 | let mut buf = format!( |
90 | 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 | ); | ||
91 | if let Some(focus_range) = self.focus_range { | 139 | if let Some(focus_range) = self.focus_range { |
92 | buf.push_str(&format!(" {:?}", focus_range)) | 140 | buf.push_str(&format!(" {:?}", focus_range)) |
93 | } | 141 | } |
@@ -101,6 +149,7 @@ impl NavigationTarget { | |||
101 | pub(crate) fn from_named( | 149 | pub(crate) fn from_named( |
102 | db: &RootDatabase, | 150 | db: &RootDatabase, |
103 | node: InFile<&dyn ast::NameOwner>, | 151 | node: InFile<&dyn ast::NameOwner>, |
152 | kind: SymbolKind, | ||
104 | ) -> NavigationTarget { | 153 | ) -> NavigationTarget { |
105 | let name = | 154 | let name = |
106 | 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("_")); |
@@ -108,13 +157,7 @@ impl NavigationTarget { | |||
108 | 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); |
109 | let frange = node.map(|it| it.syntax()).original_file_range(db); | 158 | let frange = node.map(|it| it.syntax()).original_file_range(db); |
110 | 159 | ||
111 | NavigationTarget::from_syntax( | 160 | NavigationTarget::from_syntax(frange.file_id, name, focus_range, frange.range, kind) |
112 | frange.file_id, | ||
113 | name, | ||
114 | focus_range, | ||
115 | frange.range, | ||
116 | node.value.syntax().kind(), | ||
117 | ) | ||
118 | } | 161 | } |
119 | 162 | ||
120 | fn from_syntax( | 163 | fn from_syntax( |
@@ -122,12 +165,12 @@ impl NavigationTarget { | |||
122 | name: SmolStr, | 165 | name: SmolStr, |
123 | focus_range: Option<TextRange>, | 166 | focus_range: Option<TextRange>, |
124 | full_range: TextRange, | 167 | full_range: TextRange, |
125 | kind: SyntaxKind, | 168 | kind: SymbolKind, |
126 | ) -> NavigationTarget { | 169 | ) -> NavigationTarget { |
127 | NavigationTarget { | 170 | NavigationTarget { |
128 | file_id, | 171 | file_id, |
129 | name, | 172 | name, |
130 | kind, | 173 | kind: Some(kind), |
131 | full_range, | 174 | full_range, |
132 | focus_range, | 175 | focus_range, |
133 | container_name: None, | 176 | container_name: None, |
@@ -142,7 +185,17 @@ impl ToNav for FileSymbol { | |||
142 | NavigationTarget { | 185 | NavigationTarget { |
143 | file_id: self.file_id, | 186 | file_id: self.file_id, |
144 | name: self.name.clone(), | 187 | name: self.name.clone(), |
145 | 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 | }), | ||
146 | full_range: self.range, | 199 | full_range: self.range, |
147 | focus_range: self.name_range, | 200 | focus_range: self.name_range, |
148 | container_name: self.container_name.clone(), | 201 | container_name: self.container_name.clone(), |
@@ -191,16 +244,36 @@ impl TryToNav for hir::ModuleDef { | |||
191 | } | 244 | } |
192 | } | 245 | } |
193 | 246 | ||
194 | pub(crate) trait ToNavFromAst {} | 247 | pub(crate) trait ToNavFromAst { |
195 | impl ToNavFromAst for hir::Function {} | 248 | const KIND: SymbolKind; |
196 | impl ToNavFromAst for hir::Const {} | 249 | } |
197 | impl ToNavFromAst for hir::Static {} | 250 | impl ToNavFromAst for hir::Function { |
198 | impl ToNavFromAst for hir::Struct {} | 251 | const KIND: SymbolKind = SymbolKind::Function; |
199 | impl ToNavFromAst for hir::Enum {} | 252 | } |
200 | impl ToNavFromAst for hir::EnumVariant {} | 253 | impl ToNavFromAst for hir::Const { |
201 | impl ToNavFromAst for hir::Union {} | 254 | const KIND: SymbolKind = SymbolKind::Const; |
202 | impl ToNavFromAst for hir::TypeAlias {} | 255 | } |
203 | impl ToNavFromAst for hir::Trait {} | 256 | impl ToNavFromAst for hir::Static { |
257 | const KIND: SymbolKind = SymbolKind::Static; | ||
258 | } | ||
259 | impl ToNavFromAst for hir::Struct { | ||
260 | const KIND: SymbolKind = SymbolKind::Struct; | ||
261 | } | ||
262 | impl ToNavFromAst for hir::Enum { | ||
263 | const KIND: SymbolKind = SymbolKind::Enum; | ||
264 | } | ||
265 | impl ToNavFromAst for hir::EnumVariant { | ||
266 | const KIND: SymbolKind = SymbolKind::Variant; | ||
267 | } | ||
268 | impl ToNavFromAst for hir::Union { | ||
269 | const KIND: SymbolKind = SymbolKind::Union; | ||
270 | } | ||
271 | impl ToNavFromAst for hir::TypeAlias { | ||
272 | const KIND: SymbolKind = SymbolKind::TypeAlias; | ||
273 | } | ||
274 | impl ToNavFromAst for hir::Trait { | ||
275 | const KIND: SymbolKind = SymbolKind::Trait; | ||
276 | } | ||
204 | 277 | ||
205 | impl<D> ToNav for D | 278 | impl<D> ToNav for D |
206 | where | 279 | where |
@@ -209,8 +282,11 @@ where | |||
209 | { | 282 | { |
210 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | 283 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { |
211 | let src = self.source(db); | 284 | let src = self.source(db); |
212 | let mut res = | 285 | let mut res = NavigationTarget::from_named( |
213 | 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 | ); | ||
214 | res.docs = self.docs(db); | 290 | res.docs = self.docs(db); |
215 | res.description = src.value.short_label(); | 291 | res.description = src.value.short_label(); |
216 | res | 292 | res |
@@ -228,7 +304,7 @@ impl ToNav for hir::Module { | |||
228 | } | 304 | } |
229 | }; | 305 | }; |
230 | let frange = src.with_value(syntax).original_file_range(db); | 306 | let frange = src.with_value(syntax).original_file_range(db); |
231 | 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) |
232 | } | 308 | } |
233 | } | 309 | } |
234 | 310 | ||
@@ -252,7 +328,7 @@ impl ToNav for hir::Impl { | |||
252 | "impl".into(), | 328 | "impl".into(), |
253 | focus_range, | 329 | focus_range, |
254 | frange.range, | 330 | frange.range, |
255 | src.value.syntax().kind(), | 331 | SymbolKind::Impl, |
256 | ) | 332 | ) |
257 | } | 333 | } |
258 | } | 334 | } |
@@ -263,7 +339,8 @@ impl ToNav for hir::Field { | |||
263 | 339 | ||
264 | match &src.value { | 340 | match &src.value { |
265 | FieldSource::Named(it) => { | 341 | FieldSource::Named(it) => { |
266 | 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); | ||
267 | res.docs = self.docs(db); | 344 | res.docs = self.docs(db); |
268 | res.description = it.short_label(); | 345 | res.description = it.short_label(); |
269 | res | 346 | res |
@@ -275,7 +352,7 @@ impl ToNav for hir::Field { | |||
275 | "".into(), | 352 | "".into(), |
276 | None, | 353 | None, |
277 | frange.range, | 354 | frange.range, |
278 | it.syntax().kind(), | 355 | SymbolKind::Field, |
279 | ) | 356 | ) |
280 | } | 357 | } |
281 | } | 358 | } |
@@ -286,8 +363,11 @@ impl ToNav for hir::MacroDef { | |||
286 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | 363 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { |
287 | let src = self.source(db); | 364 | let src = self.source(db); |
288 | log::debug!("nav target {:#?}", src.value.syntax()); | 365 | log::debug!("nav target {:#?}", src.value.syntax()); |
289 | let mut res = | 366 | let mut res = NavigationTarget::from_named( |
290 | 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 | ); | ||
291 | res.docs = self.docs(db); | 371 | res.docs = self.docs(db); |
292 | res | 372 | res |
293 | } | 373 | } |
@@ -327,10 +407,11 @@ impl ToNav for hir::Local { | |||
327 | Some(it) => it.to_string().into(), | 407 | Some(it) => it.to_string().into(), |
328 | None => "".into(), | 408 | None => "".into(), |
329 | }; | 409 | }; |
410 | let kind = if self.is_param(db) { SymbolKind::ValueParam } else { SymbolKind::Local }; | ||
330 | NavigationTarget { | 411 | NavigationTarget { |
331 | file_id: full_range.file_id, | 412 | file_id: full_range.file_id, |
332 | name, | 413 | name, |
333 | kind: IDENT_PAT, | 414 | kind: Some(kind), |
334 | full_range: full_range.range, | 415 | full_range: full_range.range, |
335 | focus_range: None, | 416 | focus_range: None, |
336 | container_name: None, | 417 | container_name: None, |
@@ -354,7 +435,7 @@ impl ToNav for hir::TypeParam { | |||
354 | NavigationTarget { | 435 | NavigationTarget { |
355 | file_id: src.file_id.original_file(db), | 436 | file_id: src.file_id.original_file(db), |
356 | name: self.name(db).to_string().into(), | 437 | name: self.name(db).to_string().into(), |
357 | kind: TYPE_PARAM, | 438 | kind: Some(SymbolKind::TypeParam), |
358 | full_range, | 439 | full_range, |
359 | focus_range, | 440 | focus_range, |
360 | container_name: None, | 441 | container_name: None, |
@@ -371,7 +452,7 @@ impl ToNav for hir::LifetimeParam { | |||
371 | NavigationTarget { | 452 | NavigationTarget { |
372 | file_id: src.file_id.original_file(db), | 453 | file_id: src.file_id.original_file(db), |
373 | name: self.name(db).to_string().into(), | 454 | name: self.name(db).to_string().into(), |
374 | kind: LIFETIME_PARAM, | 455 | kind: Some(SymbolKind::LifetimeParam), |
375 | full_range, | 456 | full_range, |
376 | focus_range: Some(full_range), | 457 | focus_range: Some(full_range), |
377 | container_name: None, | 458 | container_name: None, |
@@ -428,34 +509,21 @@ fn foo() { enum FooInner { } } | |||
428 | 0, | 509 | 0, |
429 | ), | 510 | ), |
430 | full_range: 0..17, | 511 | full_range: 0..17, |
431 | focus_range: Some( | 512 | focus_range: 5..13, |
432 | 5..13, | ||
433 | ), | ||
434 | name: "FooInner", | 513 | name: "FooInner", |
435 | kind: ENUM, | 514 | kind: Enum, |
436 | container_name: None, | 515 | description: "enum FooInner", |
437 | description: Some( | ||
438 | "enum FooInner", | ||
439 | ), | ||
440 | docs: None, | ||
441 | }, | 516 | }, |
442 | NavigationTarget { | 517 | NavigationTarget { |
443 | file_id: FileId( | 518 | file_id: FileId( |
444 | 0, | 519 | 0, |
445 | ), | 520 | ), |
446 | full_range: 29..46, | 521 | full_range: 29..46, |
447 | focus_range: Some( | 522 | focus_range: 34..42, |
448 | 34..42, | ||
449 | ), | ||
450 | name: "FooInner", | 523 | name: "FooInner", |
451 | kind: ENUM, | 524 | kind: Enum, |
452 | container_name: Some( | 525 | container_name: "foo", |
453 | "foo", | 526 | description: "enum FooInner", |
454 | ), | ||
455 | description: Some( | ||
456 | "enum FooInner", | ||
457 | ), | ||
458 | docs: None, | ||
459 | }, | 527 | }, |
460 | ] | 528 | ] |
461 | "#]] | 529 | "#]] |