diff options
Diffstat (limited to 'crates/ide/src/display')
-rw-r--r-- | crates/ide/src/display/navigation_target.rs | 186 |
1 files changed, 126 insertions, 60 deletions
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs index 48acb8c93..cbdd4ecc2 100644 --- a/crates/ide/src/display/navigation_target.rs +++ b/crates/ide/src/display/navigation_target.rs | |||
@@ -1,26 +1,50 @@ | |||
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)] | ||
22 | pub enum SymbolKind { | ||
23 | Module, | ||
24 | Impl, | ||
25 | Field, | ||
26 | TypeParam, | ||
27 | LifetimeParam, | ||
28 | SelfParam, | ||
29 | Local, | ||
30 | Function, | ||
31 | Const, | ||
32 | Static, | ||
33 | Struct, | ||
34 | Enum, | ||
35 | Variant, | ||
36 | Union, | ||
37 | TypeAlias, | ||
38 | Trait, | ||
39 | Macro, | ||
40 | } | ||
41 | |||
18 | /// `NavigationTarget` represents and element in the editor's UI which you can | 42 | /// `NavigationTarget` represents and element in the editor's UI which you can |
19 | /// click on to navigate to a particular piece of code. | 43 | /// click on to navigate to a particular piece of code. |
20 | /// | 44 | /// |
21 | /// Typically, a `NavigationTarget` corresponds to some element in the source | 45 | /// Typically, a `NavigationTarget` corresponds to some element in the source |
22 | /// code, like a function or a struct, but this is not strictly required. | 46 | /// code, like a function or a struct, but this is not strictly required. |
23 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 47 | #[derive(Clone, PartialEq, Eq, Hash)] |
24 | pub struct NavigationTarget { | 48 | pub struct NavigationTarget { |
25 | pub file_id: FileId, | 49 | pub file_id: FileId, |
26 | /// Range which encompasses the whole element. | 50 | /// Range which encompasses the whole element. |
@@ -40,12 +64,30 @@ pub struct NavigationTarget { | |||
40 | /// Clients should place the cursor on this range when navigating to this target. | 64 | /// Clients should place the cursor on this range when navigating to this target. |
41 | pub focus_range: Option<TextRange>, | 65 | pub focus_range: Option<TextRange>, |
42 | pub name: SmolStr, | 66 | pub name: SmolStr, |
43 | pub kind: SyntaxKind, | 67 | pub kind: Option<SymbolKind>, |
44 | pub container_name: Option<SmolStr>, | 68 | pub container_name: Option<SmolStr>, |
45 | pub description: Option<String>, | 69 | pub description: Option<String>, |
46 | pub docs: Option<Documentation>, | 70 | pub docs: Option<Documentation>, |
47 | } | 71 | } |
48 | 72 | ||
73 | impl fmt::Debug for NavigationTarget { | ||
74 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
75 | let mut f = f.debug_struct("NavigationTarget"); | ||
76 | macro_rules! opt { | ||
77 | ($($name:ident)*) => {$( | ||
78 | if let Some(it) = &self.$name { | ||
79 | f.field(stringify!($name), it); | ||
80 | } | ||
81 | )*} | ||
82 | } | ||
83 | f.field("file_id", &self.file_id).field("full_range", &self.full_range); | ||
84 | opt!(focus_range); | ||
85 | f.field("name", &self.name); | ||
86 | opt!(kind container_name description docs); | ||
87 | f.finish() | ||
88 | } | ||
89 | } | ||
90 | |||
49 | pub(crate) trait ToNav { | 91 | pub(crate) trait ToNav { |
50 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget; | 92 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget; |
51 | } | 93 | } |
@@ -69,7 +111,7 @@ impl NavigationTarget { | |||
69 | name, | 111 | name, |
70 | None, | 112 | None, |
71 | frange.range, | 113 | frange.range, |
72 | src.value.syntax().kind(), | 114 | SymbolKind::Module, |
73 | ); | 115 | ); |
74 | res.docs = module.attrs(db).docs(); | 116 | res.docs = module.attrs(db).docs(); |
75 | res.description = src.value.short_label(); | 117 | res.description = src.value.short_label(); |
@@ -86,8 +128,13 @@ impl NavigationTarget { | |||
86 | 128 | ||
87 | #[cfg(test)] | 129 | #[cfg(test)] |
88 | pub(crate) fn debug_render(&self) -> String { | 130 | pub(crate) fn debug_render(&self) -> String { |
89 | let mut buf = | 131 | let mut buf = format!( |
90 | format!("{} {:?} {:?} {:?}", self.name, self.kind, self.file_id, self.full_range); | 132 | "{} {:?} {:?} {:?}", |
133 | self.name, | ||
134 | self.kind.unwrap(), | ||
135 | self.file_id, | ||
136 | self.full_range | ||
137 | ); | ||
91 | if let Some(focus_range) = self.focus_range { | 138 | if let Some(focus_range) = self.focus_range { |
92 | buf.push_str(&format!(" {:?}", focus_range)) | 139 | buf.push_str(&format!(" {:?}", focus_range)) |
93 | } | 140 | } |
@@ -101,6 +148,7 @@ impl NavigationTarget { | |||
101 | pub(crate) fn from_named( | 148 | pub(crate) fn from_named( |
102 | db: &RootDatabase, | 149 | db: &RootDatabase, |
103 | node: InFile<&dyn ast::NameOwner>, | 150 | node: InFile<&dyn ast::NameOwner>, |
151 | kind: SymbolKind, | ||
104 | ) -> NavigationTarget { | 152 | ) -> NavigationTarget { |
105 | let name = | 153 | let name = |
106 | node.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_")); | 154 | node.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_")); |
@@ -108,13 +156,7 @@ impl NavigationTarget { | |||
108 | node.value.name().map(|it| node.with_value(it.syntax()).original_file_range(db).range); | 156 | 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); | 157 | let frange = node.map(|it| it.syntax()).original_file_range(db); |
110 | 158 | ||
111 | NavigationTarget::from_syntax( | 159 | 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 | } | 160 | } |
119 | 161 | ||
120 | fn from_syntax( | 162 | fn from_syntax( |
@@ -122,12 +164,12 @@ impl NavigationTarget { | |||
122 | name: SmolStr, | 164 | name: SmolStr, |
123 | focus_range: Option<TextRange>, | 165 | focus_range: Option<TextRange>, |
124 | full_range: TextRange, | 166 | full_range: TextRange, |
125 | kind: SyntaxKind, | 167 | kind: SymbolKind, |
126 | ) -> NavigationTarget { | 168 | ) -> NavigationTarget { |
127 | NavigationTarget { | 169 | NavigationTarget { |
128 | file_id, | 170 | file_id, |
129 | name, | 171 | name, |
130 | kind, | 172 | kind: Some(kind), |
131 | full_range, | 173 | full_range, |
132 | focus_range, | 174 | focus_range, |
133 | container_name: None, | 175 | container_name: None, |
@@ -142,7 +184,17 @@ impl ToNav for FileSymbol { | |||
142 | NavigationTarget { | 184 | NavigationTarget { |
143 | file_id: self.file_id, | 185 | file_id: self.file_id, |
144 | name: self.name.clone(), | 186 | name: self.name.clone(), |
145 | kind: self.kind, | 187 | kind: Some(match self.kind { |
188 | FileSymbolKind::Function => SymbolKind::Function, | ||
189 | FileSymbolKind::Struct => SymbolKind::Struct, | ||
190 | FileSymbolKind::Enum => SymbolKind::Enum, | ||
191 | FileSymbolKind::Trait => SymbolKind::Trait, | ||
192 | FileSymbolKind::Module => SymbolKind::Module, | ||
193 | FileSymbolKind::TypeAlias => SymbolKind::TypeAlias, | ||
194 | FileSymbolKind::Const => SymbolKind::Const, | ||
195 | FileSymbolKind::Static => SymbolKind::Static, | ||
196 | FileSymbolKind::Macro => SymbolKind::Macro, | ||
197 | }), | ||
146 | full_range: self.range, | 198 | full_range: self.range, |
147 | focus_range: self.name_range, | 199 | focus_range: self.name_range, |
148 | container_name: self.container_name.clone(), | 200 | container_name: self.container_name.clone(), |
@@ -191,16 +243,36 @@ impl TryToNav for hir::ModuleDef { | |||
191 | } | 243 | } |
192 | } | 244 | } |
193 | 245 | ||
194 | pub(crate) trait ToNavFromAst {} | 246 | pub(crate) trait ToNavFromAst { |
195 | impl ToNavFromAst for hir::Function {} | 247 | const KIND: SymbolKind; |
196 | impl ToNavFromAst for hir::Const {} | 248 | } |
197 | impl ToNavFromAst for hir::Static {} | 249 | impl ToNavFromAst for hir::Function { |
198 | impl ToNavFromAst for hir::Struct {} | 250 | const KIND: SymbolKind = SymbolKind::Function; |
199 | impl ToNavFromAst for hir::Enum {} | 251 | } |
200 | impl ToNavFromAst for hir::EnumVariant {} | 252 | impl ToNavFromAst for hir::Const { |
201 | impl ToNavFromAst for hir::Union {} | 253 | const KIND: SymbolKind = SymbolKind::Const; |
202 | impl ToNavFromAst for hir::TypeAlias {} | 254 | } |
203 | impl ToNavFromAst for hir::Trait {} | 255 | impl ToNavFromAst for hir::Static { |
256 | const KIND: SymbolKind = SymbolKind::Static; | ||
257 | } | ||
258 | impl ToNavFromAst for hir::Struct { | ||
259 | const KIND: SymbolKind = SymbolKind::Struct; | ||
260 | } | ||
261 | impl ToNavFromAst for hir::Enum { | ||
262 | const KIND: SymbolKind = SymbolKind::Enum; | ||
263 | } | ||
264 | impl ToNavFromAst for hir::EnumVariant { | ||
265 | const KIND: SymbolKind = SymbolKind::Variant; | ||
266 | } | ||
267 | impl ToNavFromAst for hir::Union { | ||
268 | const KIND: SymbolKind = SymbolKind::Union; | ||
269 | } | ||
270 | impl ToNavFromAst for hir::TypeAlias { | ||
271 | const KIND: SymbolKind = SymbolKind::TypeAlias; | ||
272 | } | ||
273 | impl ToNavFromAst for hir::Trait { | ||
274 | const KIND: SymbolKind = SymbolKind::Trait; | ||
275 | } | ||
204 | 276 | ||
205 | impl<D> ToNav for D | 277 | impl<D> ToNav for D |
206 | where | 278 | where |
@@ -209,8 +281,11 @@ where | |||
209 | { | 281 | { |
210 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | 282 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { |
211 | let src = self.source(db); | 283 | let src = self.source(db); |
212 | let mut res = | 284 | let mut res = NavigationTarget::from_named( |
213 | NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner)); | 285 | db, |
286 | src.as_ref().map(|it| it as &dyn ast::NameOwner), | ||
287 | D::KIND, | ||
288 | ); | ||
214 | res.docs = self.docs(db); | 289 | res.docs = self.docs(db); |
215 | res.description = src.value.short_label(); | 290 | res.description = src.value.short_label(); |
216 | res | 291 | res |
@@ -228,7 +303,7 @@ impl ToNav for hir::Module { | |||
228 | } | 303 | } |
229 | }; | 304 | }; |
230 | let frange = src.with_value(syntax).original_file_range(db); | 305 | let frange = src.with_value(syntax).original_file_range(db); |
231 | NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, syntax.kind()) | 306 | NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, SymbolKind::Module) |
232 | } | 307 | } |
233 | } | 308 | } |
234 | 309 | ||
@@ -252,7 +327,7 @@ impl ToNav for hir::Impl { | |||
252 | "impl".into(), | 327 | "impl".into(), |
253 | focus_range, | 328 | focus_range, |
254 | frange.range, | 329 | frange.range, |
255 | src.value.syntax().kind(), | 330 | SymbolKind::Impl, |
256 | ) | 331 | ) |
257 | } | 332 | } |
258 | } | 333 | } |
@@ -263,7 +338,8 @@ impl ToNav for hir::Field { | |||
263 | 338 | ||
264 | match &src.value { | 339 | match &src.value { |
265 | FieldSource::Named(it) => { | 340 | FieldSource::Named(it) => { |
266 | let mut res = NavigationTarget::from_named(db, src.with_value(it)); | 341 | let mut res = |
342 | NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field); | ||
267 | res.docs = self.docs(db); | 343 | res.docs = self.docs(db); |
268 | res.description = it.short_label(); | 344 | res.description = it.short_label(); |
269 | res | 345 | res |
@@ -275,7 +351,7 @@ impl ToNav for hir::Field { | |||
275 | "".into(), | 351 | "".into(), |
276 | None, | 352 | None, |
277 | frange.range, | 353 | frange.range, |
278 | it.syntax().kind(), | 354 | SymbolKind::Field, |
279 | ) | 355 | ) |
280 | } | 356 | } |
281 | } | 357 | } |
@@ -286,8 +362,11 @@ impl ToNav for hir::MacroDef { | |||
286 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | 362 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { |
287 | let src = self.source(db); | 363 | let src = self.source(db); |
288 | log::debug!("nav target {:#?}", src.value.syntax()); | 364 | log::debug!("nav target {:#?}", src.value.syntax()); |
289 | let mut res = | 365 | let mut res = NavigationTarget::from_named( |
290 | NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner)); | 366 | db, |
367 | src.as_ref().map(|it| it as &dyn ast::NameOwner), | ||
368 | SymbolKind::Macro, | ||
369 | ); | ||
291 | res.docs = self.docs(db); | 370 | res.docs = self.docs(db); |
292 | res | 371 | res |
293 | } | 372 | } |
@@ -330,7 +409,7 @@ impl ToNav for hir::Local { | |||
330 | NavigationTarget { | 409 | NavigationTarget { |
331 | file_id: full_range.file_id, | 410 | file_id: full_range.file_id, |
332 | name, | 411 | name, |
333 | kind: IDENT_PAT, | 412 | kind: Some(SymbolKind::Local), |
334 | full_range: full_range.range, | 413 | full_range: full_range.range, |
335 | focus_range: None, | 414 | focus_range: None, |
336 | container_name: None, | 415 | container_name: None, |
@@ -354,7 +433,7 @@ impl ToNav for hir::TypeParam { | |||
354 | NavigationTarget { | 433 | NavigationTarget { |
355 | file_id: src.file_id.original_file(db), | 434 | file_id: src.file_id.original_file(db), |
356 | name: self.name(db).to_string().into(), | 435 | name: self.name(db).to_string().into(), |
357 | kind: TYPE_PARAM, | 436 | kind: Some(SymbolKind::TypeParam), |
358 | full_range, | 437 | full_range, |
359 | focus_range, | 438 | focus_range, |
360 | container_name: None, | 439 | container_name: None, |
@@ -371,7 +450,7 @@ impl ToNav for hir::LifetimeParam { | |||
371 | NavigationTarget { | 450 | NavigationTarget { |
372 | file_id: src.file_id.original_file(db), | 451 | file_id: src.file_id.original_file(db), |
373 | name: self.name(db).to_string().into(), | 452 | name: self.name(db).to_string().into(), |
374 | kind: LIFETIME_PARAM, | 453 | kind: Some(SymbolKind::LifetimeParam), |
375 | full_range, | 454 | full_range, |
376 | focus_range: Some(full_range), | 455 | focus_range: Some(full_range), |
377 | container_name: None, | 456 | container_name: None, |
@@ -428,34 +507,21 @@ fn foo() { enum FooInner { } } | |||
428 | 0, | 507 | 0, |
429 | ), | 508 | ), |
430 | full_range: 0..17, | 509 | full_range: 0..17, |
431 | focus_range: Some( | 510 | focus_range: 5..13, |
432 | 5..13, | ||
433 | ), | ||
434 | name: "FooInner", | 511 | name: "FooInner", |
435 | kind: ENUM, | 512 | kind: Enum, |
436 | container_name: None, | 513 | description: "enum FooInner", |
437 | description: Some( | ||
438 | "enum FooInner", | ||
439 | ), | ||
440 | docs: None, | ||
441 | }, | 514 | }, |
442 | NavigationTarget { | 515 | NavigationTarget { |
443 | file_id: FileId( | 516 | file_id: FileId( |
444 | 0, | 517 | 0, |
445 | ), | 518 | ), |
446 | full_range: 29..46, | 519 | full_range: 29..46, |
447 | focus_range: Some( | 520 | focus_range: 34..42, |
448 | 34..42, | ||
449 | ), | ||
450 | name: "FooInner", | 521 | name: "FooInner", |
451 | kind: ENUM, | 522 | kind: Enum, |
452 | container_name: Some( | 523 | container_name: "foo", |
453 | "foo", | 524 | description: "enum FooInner", |
454 | ), | ||
455 | description: Some( | ||
456 | "enum FooInner", | ||
457 | ), | ||
458 | docs: None, | ||
459 | }, | 525 | }, |
460 | ] | 526 | ] |
461 | "#]] | 527 | "#]] |