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.rs186
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
3use std::fmt;
4
3use either::Either; 5use either::Either;
4use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, ModuleSource}; 6use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, ModuleSource};
5use ide_db::base_db::{FileId, SourceDatabase}; 7use ide_db::{
8 base_db::{FileId, SourceDatabase},
9 symbol_index::FileSymbolKind,
10};
6use ide_db::{defs::Definition, RootDatabase}; 11use ide_db::{defs::Definition, RootDatabase};
7use syntax::{ 12use 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
14use crate::FileSymbol; 17use crate::FileSymbol;
15 18
16use super::short_label::ShortLabel; 19use super::short_label::ShortLabel;
17 20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub 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)]
24pub struct NavigationTarget { 48pub 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
73impl 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
49pub(crate) trait ToNav { 91pub(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
194pub(crate) trait ToNavFromAst {} 246pub(crate) trait ToNavFromAst {
195impl ToNavFromAst for hir::Function {} 247 const KIND: SymbolKind;
196impl ToNavFromAst for hir::Const {} 248}
197impl ToNavFromAst for hir::Static {} 249impl ToNavFromAst for hir::Function {
198impl ToNavFromAst for hir::Struct {} 250 const KIND: SymbolKind = SymbolKind::Function;
199impl ToNavFromAst for hir::Enum {} 251}
200impl ToNavFromAst for hir::EnumVariant {} 252impl ToNavFromAst for hir::Const {
201impl ToNavFromAst for hir::Union {} 253 const KIND: SymbolKind = SymbolKind::Const;
202impl ToNavFromAst for hir::TypeAlias {} 254}
203impl ToNavFromAst for hir::Trait {} 255impl ToNavFromAst for hir::Static {
256 const KIND: SymbolKind = SymbolKind::Static;
257}
258impl ToNavFromAst for hir::Struct {
259 const KIND: SymbolKind = SymbolKind::Struct;
260}
261impl ToNavFromAst for hir::Enum {
262 const KIND: SymbolKind = SymbolKind::Enum;
263}
264impl ToNavFromAst for hir::EnumVariant {
265 const KIND: SymbolKind = SymbolKind::Variant;
266}
267impl ToNavFromAst for hir::Union {
268 const KIND: SymbolKind = SymbolKind::Union;
269}
270impl ToNavFromAst for hir::TypeAlias {
271 const KIND: SymbolKind = SymbolKind::TypeAlias;
272}
273impl ToNavFromAst for hir::Trait {
274 const KIND: SymbolKind = SymbolKind::Trait;
275}
204 276
205impl<D> ToNav for D 277impl<D> ToNav for D
206where 278where
@@ -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 "#]]