aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/display
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/display')
-rw-r--r--crates/ra_ide/src/display/function_signature.rs334
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs152
-rw-r--r--crates/ra_ide/src/display/short_label.rs2
-rw-r--r--crates/ra_ide/src/display/structure.rs440
4 files changed, 99 insertions, 829 deletions
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs
deleted file mode 100644
index a98264fb3..000000000
--- a/crates/ra_ide/src/display/function_signature.rs
+++ /dev/null
@@ -1,334 +0,0 @@
1//! FIXME: write short doc here
2
3// FIXME: this modules relies on strings and AST way too much, and it should be
4// rewritten (matklad 2020-05-07)
5use std::{
6 convert::From,
7 fmt::{self, Display},
8};
9
10use hir::{Docs, Documentation, HasSource, HirDisplay};
11use ra_ide_db::RootDatabase;
12use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
13use stdx::{split_delim, SepBy};
14
15use crate::display::{generic_parameters, where_predicates};
16
17#[derive(Debug)]
18pub enum CallableKind {
19 Function,
20 StructConstructor,
21 VariantConstructor,
22 Macro,
23}
24
25/// Contains information about a function signature
26#[derive(Debug)]
27pub struct FunctionSignature {
28 pub kind: CallableKind,
29 /// Optional visibility
30 pub visibility: Option<String>,
31 /// Qualifiers like `async`, `unsafe`, ...
32 pub qualifier: FunctionQualifier,
33 /// Name of the function
34 pub name: Option<String>,
35 /// Documentation for the function
36 pub doc: Option<Documentation>,
37 /// Generic parameters
38 pub generic_parameters: Vec<String>,
39 /// Parameters of the function
40 pub parameters: Vec<String>,
41 /// Parameter names of the function
42 pub parameter_names: Vec<String>,
43 /// Parameter types of the function
44 pub parameter_types: Vec<String>,
45 /// Optional return type
46 pub ret_type: Option<String>,
47 /// Where predicates
48 pub where_predicates: Vec<String>,
49 /// Self param presence
50 pub has_self_param: bool,
51}
52
53#[derive(Debug, Default)]
54pub struct FunctionQualifier {
55 // `async` and `const` are mutually exclusive. Do we need to enforcing it here?
56 pub is_async: bool,
57 pub is_const: bool,
58 pub is_unsafe: bool,
59 /// The string `extern ".."`
60 pub extern_abi: Option<String>,
61}
62
63impl FunctionSignature {
64 pub(crate) fn with_doc_opt(mut self, doc: Option<Documentation>) -> Self {
65 self.doc = doc;
66 self
67 }
68
69 pub(crate) fn from_hir(db: &RootDatabase, function: hir::Function) -> Self {
70 let doc = function.docs(db);
71 let ast_node = function.source(db).value;
72 FunctionSignature::from(&ast_node).with_doc_opt(doc)
73 }
74
75 pub(crate) fn from_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> {
76 let node: ast::StructDef = st.source(db).value;
77 if let ast::StructKind::Record(_) = node.kind() {
78 return None;
79 };
80
81 let mut params = vec![];
82 let mut parameter_types = vec![];
83 for field in st.fields(db).into_iter() {
84 let ty = field.signature_ty(db);
85 let raw_param = format!("{}", ty.display(db));
86
87 if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) {
88 parameter_types.push(param_type.to_string());
89 } else {
90 // useful when you have tuple struct
91 parameter_types.push(raw_param.clone());
92 }
93 params.push(raw_param);
94 }
95
96 Some(
97 FunctionSignature {
98 kind: CallableKind::StructConstructor,
99 visibility: node.visibility().map(|n| n.syntax().text().to_string()),
100 // Do we need `const`?
101 qualifier: Default::default(),
102 name: node.name().map(|n| n.text().to_string()),
103 ret_type: node.name().map(|n| n.text().to_string()),
104 parameters: params,
105 parameter_names: vec![],
106 parameter_types,
107 generic_parameters: generic_parameters(&node),
108 where_predicates: where_predicates(&node),
109 doc: None,
110 has_self_param: false,
111 }
112 .with_doc_opt(st.docs(db)),
113 )
114 }
115
116 pub(crate) fn from_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> {
117 let node: ast::EnumVariant = variant.source(db).value;
118 match node.kind() {
119 ast::StructKind::Record(_) | ast::StructKind::Unit => return None,
120 _ => (),
121 };
122
123 let parent_name = variant.parent_enum(db).name(db).to_string();
124
125 let name = format!("{}::{}", parent_name, variant.name(db));
126
127 let mut params = vec![];
128 let mut parameter_types = vec![];
129 for field in variant.fields(db).into_iter() {
130 let ty = field.signature_ty(db);
131 let raw_param = format!("{}", ty.display(db));
132 if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) {
133 parameter_types.push(param_type.to_string());
134 } else {
135 // The unwrap_or_else is useful when you have tuple
136 parameter_types.push(raw_param);
137 }
138 let name = field.name(db);
139
140 params.push(format!("{}: {}", name, ty.display(db)));
141 }
142
143 Some(
144 FunctionSignature {
145 kind: CallableKind::VariantConstructor,
146 visibility: None,
147 // Do we need `const`?
148 qualifier: Default::default(),
149 name: Some(name),
150 ret_type: None,
151 parameters: params,
152 parameter_names: vec![],
153 parameter_types,
154 generic_parameters: vec![],
155 where_predicates: vec![],
156 doc: None,
157 has_self_param: false,
158 }
159 .with_doc_opt(variant.docs(db)),
160 )
161 }
162
163 pub(crate) fn from_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> {
164 let node: ast::MacroCall = macro_def.source(db).value;
165
166 let params = vec![];
167
168 Some(
169 FunctionSignature {
170 kind: CallableKind::Macro,
171 visibility: None,
172 qualifier: Default::default(),
173 name: node.name().map(|n| n.text().to_string()),
174 ret_type: None,
175 parameters: params,
176 parameter_names: vec![],
177 parameter_types: vec![],
178 generic_parameters: vec![],
179 where_predicates: vec![],
180 doc: None,
181 has_self_param: false,
182 }
183 .with_doc_opt(macro_def.docs(db)),
184 )
185 }
186}
187
188impl From<&'_ ast::FnDef> for FunctionSignature {
189 fn from(node: &ast::FnDef) -> FunctionSignature {
190 fn param_list(node: &ast::FnDef) -> (bool, Vec<String>, Vec<String>) {
191 let mut res = vec![];
192 let mut res_types = vec![];
193 let mut has_self_param = false;
194 if let Some(param_list) = node.param_list() {
195 if let Some(self_param) = param_list.self_param() {
196 has_self_param = true;
197 let raw_param = self_param.syntax().text().to_string();
198
199 res_types.push(
200 raw_param
201 .split(':')
202 .nth(1)
203 .and_then(|it| it.get(1..))
204 .unwrap_or_else(|| "Self")
205 .to_string(),
206 );
207 res.push(raw_param);
208 }
209
210 // macro-generated functions are missing whitespace
211 fn fmt_param(param: ast::Param) -> String {
212 let text = param.syntax().text().to_string();
213 match split_delim(&text, ':') {
214 Some((left, right)) => format!("{}: {}", left.trim(), right.trim()),
215 _ => text,
216 }
217 }
218
219 res.extend(param_list.params().map(fmt_param));
220 res_types.extend(param_list.params().map(|param| {
221 let param_text = param.syntax().text().to_string();
222 match param_text.split(':').nth(1).and_then(|it| it.get(1..)) {
223 Some(it) => it.to_string(),
224 None => param_text,
225 }
226 }));
227 }
228 (has_self_param, res, res_types)
229 }
230
231 fn param_name_list(node: &ast::FnDef) -> Vec<String> {
232 let mut res = vec![];
233 if let Some(param_list) = node.param_list() {
234 if let Some(self_param) = param_list.self_param() {
235 res.push(self_param.syntax().text().to_string())
236 }
237
238 res.extend(
239 param_list
240 .params()
241 .map(|param| {
242 Some(
243 param
244 .pat()?
245 .syntax()
246 .descendants()
247 .find_map(ast::Name::cast)?
248 .text()
249 .to_string(),
250 )
251 })
252 .map(|param| param.unwrap_or_default()),
253 );
254 }
255 res
256 }
257
258 let (has_self_param, parameters, parameter_types) = param_list(node);
259
260 FunctionSignature {
261 kind: CallableKind::Function,
262 visibility: node.visibility().map(|n| n.syntax().text().to_string()),
263 qualifier: FunctionQualifier {
264 is_async: node.async_token().is_some(),
265 is_const: node.const_token().is_some(),
266 is_unsafe: node.unsafe_token().is_some(),
267 extern_abi: node.abi().map(|n| n.to_string()),
268 },
269 name: node.name().map(|n| n.text().to_string()),
270 ret_type: node
271 .ret_type()
272 .and_then(|r| r.type_ref())
273 .map(|n| n.syntax().text().to_string()),
274 parameters,
275 parameter_names: param_name_list(node),
276 parameter_types,
277 generic_parameters: generic_parameters(node),
278 where_predicates: where_predicates(node),
279 // docs are processed separately
280 doc: None,
281 has_self_param,
282 }
283 }
284}
285
286impl Display for FunctionSignature {
287 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
288 if let Some(t) = &self.visibility {
289 write!(f, "{} ", t)?;
290 }
291
292 if self.qualifier.is_async {
293 write!(f, "async ")?;
294 }
295
296 if self.qualifier.is_const {
297 write!(f, "const ")?;
298 }
299
300 if self.qualifier.is_unsafe {
301 write!(f, "unsafe ")?;
302 }
303
304 if let Some(extern_abi) = &self.qualifier.extern_abi {
305 // Keyword `extern` is included in the string.
306 write!(f, "{} ", extern_abi)?;
307 }
308
309 if let Some(name) = &self.name {
310 match self.kind {
311 CallableKind::Function => write!(f, "fn {}", name)?,
312 CallableKind::StructConstructor => write!(f, "struct {}", name)?,
313 CallableKind::VariantConstructor => write!(f, "{}", name)?,
314 CallableKind::Macro => write!(f, "{}!", name)?,
315 }
316 }
317
318 if !self.generic_parameters.is_empty() {
319 write!(f, "{}", self.generic_parameters.iter().sep_by(", ").surround_with("<", ">"))?;
320 }
321
322 write!(f, "{}", self.parameters.iter().sep_by(", ").surround_with("(", ")"))?;
323
324 if let Some(t) = &self.ret_type {
325 write!(f, " -> {}", t)?;
326 }
327
328 if !self.where_predicates.is_empty() {
329 write!(f, "\nwhere {}", self.where_predicates.iter().sep_by(",\n "))?;
330 }
331
332 Ok(())
333 }
334}
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs
index 8bf2428ed..fd245705c 100644
--- a/crates/ra_ide/src/display/navigation_target.rs
+++ b/crates/ra_ide/src/display/navigation_target.rs
@@ -22,15 +22,28 @@ use super::short_label::ShortLabel;
22/// code, like a function or a struct, but this is not strictly required. 22/// code, like a function or a struct, but this is not strictly required.
23#[derive(Debug, Clone, PartialEq, Eq, Hash)] 23#[derive(Debug, Clone, PartialEq, Eq, Hash)]
24pub struct NavigationTarget { 24pub struct NavigationTarget {
25 // FIXME: use FileRange? 25 pub file_id: FileId,
26 file_id: FileId, 26 /// Range which encompasses the whole element.
27 full_range: TextRange, 27 ///
28 name: SmolStr, 28 /// Should include body, doc comments, attributes, etc.
29 kind: SyntaxKind, 29 ///
30 focus_range: Option<TextRange>, 30 /// Clients should use this range to answer "is the cursor inside the
31 container_name: Option<SmolStr>, 31 /// element?" question.
32 description: Option<String>, 32 pub full_range: TextRange,
33 docs: Option<String>, 33 /// A "most interesting" range withing the `full_range`.
34 ///
35 /// Typically, `full_range` is the whole syntax node, including doc
36 /// comments, and `focus_range` is the range of the identifier. "Most
37 /// interesting" range within the full range, typically the range of
38 /// identifier.
39 ///
40 /// Clients should place the cursor on this range when navigating to this target.
41 pub focus_range: Option<TextRange>,
42 pub name: SmolStr,
43 pub kind: SyntaxKind,
44 pub container_name: Option<SmolStr>,
45 pub description: Option<String>,
46 pub docs: Option<String>,
34} 47}
35 48
36pub(crate) trait ToNav { 49pub(crate) trait ToNav {
@@ -42,44 +55,9 @@ pub(crate) trait TryToNav {
42} 55}
43 56
44impl NavigationTarget { 57impl NavigationTarget {
45 /// When `focus_range` is specified, returns it. otherwise 58 pub fn focus_or_full_range(&self) -> TextRange {
46 /// returns `full_range`
47 pub fn range(&self) -> TextRange {
48 self.focus_range.unwrap_or(self.full_range) 59 self.focus_range.unwrap_or(self.full_range)
49 } 60 }
50 /// A "most interesting" range withing the `full_range`.
51 ///
52 /// Typically, `full_range` is the whole syntax node,
53 /// including doc comments, and `focus_range` is the range of the identifier.
54 pub fn focus_range(&self) -> Option<TextRange> {
55 self.focus_range
56 }
57 pub fn full_range(&self) -> TextRange {
58 self.full_range
59 }
60 pub fn file_id(&self) -> FileId {
61 self.file_id
62 }
63
64 pub fn name(&self) -> &SmolStr {
65 &self.name
66 }
67
68 pub fn container_name(&self) -> Option<&SmolStr> {
69 self.container_name.as_ref()
70 }
71
72 pub fn kind(&self) -> SyntaxKind {
73 self.kind
74 }
75
76 pub fn docs(&self) -> Option<&str> {
77 self.docs.as_deref()
78 }
79
80 pub fn description(&self) -> Option<&str> {
81 self.description.as_deref()
82 }
83 61
84 pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget { 62 pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
85 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); 63 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
@@ -107,17 +85,12 @@ impl NavigationTarget {
107 85
108 #[cfg(test)] 86 #[cfg(test)]
109 pub(crate) fn debug_render(&self) -> String { 87 pub(crate) fn debug_render(&self) -> String {
110 let mut buf = format!( 88 let mut buf =
111 "{} {:?} {:?} {:?}", 89 format!("{} {:?} {:?} {:?}", self.name, self.kind, self.file_id, self.full_range);
112 self.name(), 90 if let Some(focus_range) = self.focus_range {
113 self.kind(),
114 self.file_id(),
115 self.full_range()
116 );
117 if let Some(focus_range) = self.focus_range() {
118 buf.push_str(&format!(" {:?}", focus_range)) 91 buf.push_str(&format!(" {:?}", focus_range))
119 } 92 }
120 if let Some(container_name) = self.container_name() { 93 if let Some(container_name) = &self.container_name {
121 buf.push_str(&format!(" {}", container_name)) 94 buf.push_str(&format!(" {}", container_name))
122 } 95 }
123 buf 96 buf
@@ -445,3 +418,74 @@ pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) ->
445 } 418 }
446 } 419 }
447} 420}
421
422#[cfg(test)]
423mod tests {
424 use expect::expect;
425
426 use crate::{mock_analysis::single_file, Query};
427
428 #[test]
429 fn test_nav_for_symbol() {
430 let (analysis, _) = single_file(
431 r#"
432enum FooInner { }
433fn foo() { enum FooInner { } }
434"#,
435 );
436
437 let navs = analysis.symbol_search(Query::new("FooInner".to_string())).unwrap();
438 expect![[r#"
439 [
440 NavigationTarget {
441 file_id: FileId(
442 1,
443 ),
444 full_range: 0..17,
445 focus_range: Some(
446 5..13,
447 ),
448 name: "FooInner",
449 kind: ENUM_DEF,
450 container_name: None,
451 description: Some(
452 "enum FooInner",
453 ),
454 docs: None,
455 },
456 NavigationTarget {
457 file_id: FileId(
458 1,
459 ),
460 full_range: 29..46,
461 focus_range: Some(
462 34..42,
463 ),
464 name: "FooInner",
465 kind: ENUM_DEF,
466 container_name: Some(
467 "foo",
468 ),
469 description: Some(
470 "enum FooInner",
471 ),
472 docs: None,
473 },
474 ]
475 "#]]
476 .assert_debug_eq(&navs);
477 }
478
479 #[test]
480 fn test_world_symbols_are_case_sensitive() {
481 let (analysis, _) = single_file(
482 r#"
483fn foo() {}
484struct Foo;
485"#,
486 );
487
488 let navs = analysis.symbol_search(Query::new("foo".to_string())).unwrap();
489 assert_eq!(navs.len(), 2)
490 }
491}
diff --git a/crates/ra_ide/src/display/short_label.rs b/crates/ra_ide/src/display/short_label.rs
index d37260e96..5588130a1 100644
--- a/crates/ra_ide/src/display/short_label.rs
+++ b/crates/ra_ide/src/display/short_label.rs
@@ -9,7 +9,7 @@ pub(crate) trait ShortLabel {
9 9
10impl ShortLabel for ast::FnDef { 10impl ShortLabel for ast::FnDef {
11 fn short_label(&self) -> Option<String> { 11 fn short_label(&self) -> Option<String> {
12 Some(crate::display::function_label(self)) 12 Some(crate::display::function_declaration(self))
13 } 13 }
14} 14}
15 15
diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs
deleted file mode 100644
index c22a5d17b..000000000
--- a/crates/ra_ide/src/display/structure.rs
+++ /dev/null
@@ -1,440 +0,0 @@
1use ra_syntax::{
2 ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner},
3 match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, WalkEvent,
4};
5
6#[derive(Debug, Clone)]
7pub struct StructureNode {
8 pub parent: Option<usize>,
9 pub label: String,
10 pub navigation_range: TextRange,
11 pub node_range: TextRange,
12 pub kind: SyntaxKind,
13 pub detail: Option<String>,
14 pub deprecated: bool,
15}
16
17// Feature: File Structure
18//
19// Provides a tree of the symbols defined in the file. Can be used to
20//
21// * fuzzy search symbol in a file (super useful)
22// * draw breadcrumbs to describe the context around the cursor
23// * draw outline of the file
24//
25// |===
26// | Editor | Shortcut
27//
28// | VS Code | kbd:[Ctrl+Shift+O]
29// |===
30pub fn file_structure(file: &SourceFile) -> Vec<StructureNode> {
31 let mut res = Vec::new();
32 let mut stack = Vec::new();
33
34 for event in file.syntax().preorder() {
35 match event {
36 WalkEvent::Enter(node) => {
37 if let Some(mut symbol) = structure_node(&node) {
38 symbol.parent = stack.last().copied();
39 stack.push(res.len());
40 res.push(symbol);
41 }
42 }
43 WalkEvent::Leave(node) => {
44 if structure_node(&node).is_some() {
45 stack.pop().unwrap();
46 }
47 }
48 }
49 }
50 res
51}
52
53fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
54 fn decl<N: NameOwner + AttrsOwner>(node: N) -> Option<StructureNode> {
55 decl_with_detail(node, None)
56 }
57
58 fn decl_with_ascription<N: NameOwner + AttrsOwner + TypeAscriptionOwner>(
59 node: N,
60 ) -> Option<StructureNode> {
61 let ty = node.ascribed_type();
62 decl_with_type_ref(node, ty)
63 }
64
65 fn decl_with_type_ref<N: NameOwner + AttrsOwner>(
66 node: N,
67 type_ref: Option<ast::TypeRef>,
68 ) -> Option<StructureNode> {
69 let detail = type_ref.map(|type_ref| {
70 let mut detail = String::new();
71 collapse_ws(type_ref.syntax(), &mut detail);
72 detail
73 });
74 decl_with_detail(node, detail)
75 }
76
77 fn decl_with_detail<N: NameOwner + AttrsOwner>(
78 node: N,
79 detail: Option<String>,
80 ) -> Option<StructureNode> {
81 let name = node.name()?;
82
83 Some(StructureNode {
84 parent: None,
85 label: name.text().to_string(),
86 navigation_range: name.syntax().text_range(),
87 node_range: node.syntax().text_range(),
88 kind: node.syntax().kind(),
89 detail,
90 deprecated: node.attrs().filter_map(|x| x.simple_name()).any(|x| x == "deprecated"),
91 })
92 }
93
94 fn collapse_ws(node: &SyntaxNode, output: &mut String) {
95 let mut can_insert_ws = false;
96 node.text().for_each_chunk(|chunk| {
97 for line in chunk.lines() {
98 let line = line.trim();
99 if line.is_empty() {
100 if can_insert_ws {
101 output.push(' ');
102 can_insert_ws = false;
103 }
104 } else {
105 output.push_str(line);
106 can_insert_ws = true;
107 }
108 }
109 })
110 }
111
112 match_ast! {
113 match node {
114 ast::FnDef(it) => {
115 let mut detail = String::from("fn");
116 if let Some(type_param_list) = it.type_param_list() {
117 collapse_ws(type_param_list.syntax(), &mut detail);
118 }
119 if let Some(param_list) = it.param_list() {
120 collapse_ws(param_list.syntax(), &mut detail);
121 }
122 if let Some(ret_type) = it.ret_type() {
123 detail.push_str(" ");
124 collapse_ws(ret_type.syntax(), &mut detail);
125 }
126
127 decl_with_detail(it, Some(detail))
128 },
129 ast::StructDef(it) => decl(it),
130 ast::EnumDef(it) => decl(it),
131 ast::EnumVariant(it) => decl(it),
132 ast::TraitDef(it) => decl(it),
133 ast::Module(it) => decl(it),
134 ast::TypeAliasDef(it) => {
135 let ty = it.type_ref();
136 decl_with_type_ref(it, ty)
137 },
138 ast::RecordFieldDef(it) => decl_with_ascription(it),
139 ast::ConstDef(it) => decl_with_ascription(it),
140 ast::StaticDef(it) => decl_with_ascription(it),
141 ast::ImplDef(it) => {
142 let target_type = it.target_type()?;
143 let target_trait = it.target_trait();
144 let label = match target_trait {
145 None => format!("impl {}", target_type.syntax().text()),
146 Some(t) => {
147 format!("impl {} for {}", t.syntax().text(), target_type.syntax().text(),)
148 }
149 };
150
151 let node = StructureNode {
152 parent: None,
153 label,
154 navigation_range: target_type.syntax().text_range(),
155 node_range: it.syntax().text_range(),
156 kind: it.syntax().kind(),
157 detail: None,
158 deprecated: false,
159 };
160 Some(node)
161 },
162 ast::MacroCall(it) => {
163 match it.path().and_then(|it| it.segment()).and_then(|it| it.name_ref()) {
164 Some(path_segment) if path_segment.text() == "macro_rules"
165 => decl(it),
166 _ => None,
167 }
168 },
169 _ => None,
170 }
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use expect::{expect, Expect};
177
178 use super::*;
179
180 fn check(ra_fixture: &str, expect: Expect) {
181 let file = SourceFile::parse(ra_fixture).ok().unwrap();
182 let structure = file_structure(&file);
183 expect.assert_debug_eq(&structure)
184 }
185
186 #[test]
187 fn test_file_structure() {
188 check(
189 r#"
190struct Foo {
191 x: i32
192}
193
194mod m {
195 fn bar1() {}
196 fn bar2<T>(t: T) -> T {}
197 fn bar3<A,
198 B>(a: A,
199 b: B) -> Vec<
200 u32
201 > {}
202}
203
204enum E { X, Y(i32) }
205type T = ();
206static S: i32 = 92;
207const C: i32 = 92;
208
209impl E {}
210
211impl fmt::Debug for E {}
212
213macro_rules! mc {
214 () => {}
215}
216
217#[macro_export]
218macro_rules! mcexp {
219 () => {}
220}
221
222/// Doc comment
223macro_rules! mcexp {
224 () => {}
225}
226
227#[deprecated]
228fn obsolete() {}
229
230#[deprecated(note = "for awhile")]
231fn very_obsolete() {}
232"#,
233 expect![[r#"
234 [
235 StructureNode {
236 parent: None,
237 label: "Foo",
238 navigation_range: 8..11,
239 node_range: 1..26,
240 kind: STRUCT_DEF,
241 detail: None,
242 deprecated: false,
243 },
244 StructureNode {
245 parent: Some(
246 0,
247 ),
248 label: "x",
249 navigation_range: 18..19,
250 node_range: 18..24,
251 kind: RECORD_FIELD_DEF,
252 detail: Some(
253 "i32",
254 ),
255 deprecated: false,
256 },
257 StructureNode {
258 parent: None,
259 label: "m",
260 navigation_range: 32..33,
261 node_range: 28..158,
262 kind: MODULE,
263 detail: None,
264 deprecated: false,
265 },
266 StructureNode {
267 parent: Some(
268 2,
269 ),
270 label: "bar1",
271 navigation_range: 43..47,
272 node_range: 40..52,
273 kind: FN_DEF,
274 detail: Some(
275 "fn()",
276 ),
277 deprecated: false,
278 },
279 StructureNode {
280 parent: Some(
281 2,
282 ),
283 label: "bar2",
284 navigation_range: 60..64,
285 node_range: 57..81,
286 kind: FN_DEF,
287 detail: Some(
288 "fn<T>(t: T) -> T",
289 ),
290 deprecated: false,
291 },
292 StructureNode {
293 parent: Some(
294 2,
295 ),
296 label: "bar3",
297 navigation_range: 89..93,
298 node_range: 86..156,
299 kind: FN_DEF,
300 detail: Some(
301 "fn<A, B>(a: A, b: B) -> Vec< u32 >",
302 ),
303 deprecated: false,
304 },
305 StructureNode {
306 parent: None,
307 label: "E",
308 navigation_range: 165..166,
309 node_range: 160..180,
310 kind: ENUM_DEF,
311 detail: None,
312 deprecated: false,
313 },
314 StructureNode {
315 parent: Some(
316 6,
317 ),
318 label: "X",
319 navigation_range: 169..170,
320 node_range: 169..170,
321 kind: ENUM_VARIANT,
322 detail: None,
323 deprecated: false,
324 },
325 StructureNode {
326 parent: Some(
327 6,
328 ),
329 label: "Y",
330 navigation_range: 172..173,
331 node_range: 172..178,
332 kind: ENUM_VARIANT,
333 detail: None,
334 deprecated: false,
335 },
336 StructureNode {
337 parent: None,
338 label: "T",
339 navigation_range: 186..187,
340 node_range: 181..193,
341 kind: TYPE_ALIAS_DEF,
342 detail: Some(
343 "()",
344 ),
345 deprecated: false,
346 },
347 StructureNode {
348 parent: None,
349 label: "S",
350 navigation_range: 201..202,
351 node_range: 194..213,
352 kind: STATIC_DEF,
353 detail: Some(
354 "i32",
355 ),
356 deprecated: false,
357 },
358 StructureNode {
359 parent: None,
360 label: "C",
361 navigation_range: 220..221,
362 node_range: 214..232,
363 kind: CONST_DEF,
364 detail: Some(
365 "i32",
366 ),
367 deprecated: false,
368 },
369 StructureNode {
370 parent: None,
371 label: "impl E",
372 navigation_range: 239..240,
373 node_range: 234..243,
374 kind: IMPL_DEF,
375 detail: None,
376 deprecated: false,
377 },
378 StructureNode {
379 parent: None,
380 label: "impl fmt::Debug for E",
381 navigation_range: 265..266,
382 node_range: 245..269,
383 kind: IMPL_DEF,
384 detail: None,
385 deprecated: false,
386 },
387 StructureNode {
388 parent: None,
389 label: "mc",
390 navigation_range: 284..286,
391 node_range: 271..303,
392 kind: MACRO_CALL,
393 detail: None,
394 deprecated: false,
395 },
396 StructureNode {
397 parent: None,
398 label: "mcexp",
399 navigation_range: 334..339,
400 node_range: 305..356,
401 kind: MACRO_CALL,
402 detail: None,
403 deprecated: false,
404 },
405 StructureNode {
406 parent: None,
407 label: "mcexp",
408 navigation_range: 387..392,
409 node_range: 358..409,
410 kind: MACRO_CALL,
411 detail: None,
412 deprecated: false,
413 },
414 StructureNode {
415 parent: None,
416 label: "obsolete",
417 navigation_range: 428..436,
418 node_range: 411..441,
419 kind: FN_DEF,
420 detail: Some(
421 "fn()",
422 ),
423 deprecated: true,
424 },
425 StructureNode {
426 parent: None,
427 label: "very_obsolete",
428 navigation_range: 481..494,
429 node_range: 443..499,
430 kind: FN_DEF,
431 detail: Some(
432 "fn()",
433 ),
434 deprecated: true,
435 },
436 ]
437 "#]],
438 );
439 }
440}