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.rs188
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
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, 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
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)]
24pub struct NavigationTarget { 49pub 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
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
49pub(crate) trait ToNav { 92pub(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
194pub(crate) trait ToNavFromAst {} 247pub(crate) trait ToNavFromAst {
195impl ToNavFromAst for hir::Function {} 248 const KIND: SymbolKind;
196impl ToNavFromAst for hir::Const {} 249}
197impl ToNavFromAst for hir::Static {} 250impl ToNavFromAst for hir::Function {
198impl ToNavFromAst for hir::Struct {} 251 const KIND: SymbolKind = SymbolKind::Function;
199impl ToNavFromAst for hir::Enum {} 252}
200impl ToNavFromAst for hir::EnumVariant {} 253impl ToNavFromAst for hir::Const {
201impl ToNavFromAst for hir::Union {} 254 const KIND: SymbolKind = SymbolKind::Const;
202impl ToNavFromAst for hir::TypeAlias {} 255}
203impl 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::EnumVariant {
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}
204 277
205impl<D> ToNav for D 278impl<D> ToNav for D
206where 279where
@@ -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 "#]]