1use ide_db::SymbolKind;
2use syntax::{
3 AstNode, AstToken, NodeOrToken, SourceFile, SyntaxNode, SyntaxToken, TextRange, WalkEvent,
4 ast::{self, HasAttrs, HasGenericParams, HasName},
5 match_ast,
6};
7
8#[derive(Debug, Clone)]
9pub struct StructureNode {
10 pub parent: Option<usize>,
11 pub label: String,
12 pub navigation_range: TextRange,
13 pub node_range: TextRange,
14 pub kind: StructureNodeKind,
15 pub detail: Option<String>,
16 pub deprecated: bool,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
20pub enum StructureNodeKind {
21 SymbolKind(SymbolKind),
22 ExternBlock,
23 Region,
24}
25
26#[derive(Debug, Clone)]
27pub struct FileStructureConfig {
28 pub exclude_locals: bool,
29}
30
31pub(crate) fn file_structure(
45 file: &SourceFile,
46 config: &FileStructureConfig,
47) -> Vec<StructureNode> {
48 let mut res = Vec::new();
49 let mut stack = Vec::new();
50
51 for event in file.syntax().preorder_with_tokens() {
52 match event {
53 WalkEvent::Enter(NodeOrToken::Node(node)) => {
54 if let Some(mut symbol) = structure_node(&node, config) {
55 symbol.parent = stack.last().copied();
56 stack.push(res.len());
57 res.push(symbol);
58 }
59 }
60 WalkEvent::Leave(NodeOrToken::Node(node)) => {
61 if structure_node(&node, config).is_some() {
62 stack.pop().unwrap();
63 }
64 }
65 WalkEvent::Enter(NodeOrToken::Token(token)) => {
66 if let Some(mut symbol) = structure_token(token) {
67 symbol.parent = stack.last().copied();
68 stack.push(res.len());
69 res.push(symbol);
70 }
71 }
72 WalkEvent::Leave(NodeOrToken::Token(token)) => {
73 if structure_token(token).is_some() {
74 stack.pop().unwrap();
75 }
76 }
77 }
78 }
79 res
80}
81
82fn structure_node(node: &SyntaxNode, config: &FileStructureConfig) -> Option<StructureNode> {
83 fn decl<N: HasName + HasAttrs>(node: N, kind: StructureNodeKind) -> Option<StructureNode> {
84 decl_with_detail(&node, None, kind)
85 }
86
87 fn decl_with_type_ref<N: HasName + HasAttrs>(
88 node: &N,
89 type_ref: Option<ast::Type>,
90 kind: StructureNodeKind,
91 ) -> Option<StructureNode> {
92 let detail = type_ref.map(|type_ref| {
93 let mut detail = String::new();
94 collapse_ws(type_ref.syntax(), &mut detail);
95 detail
96 });
97 decl_with_detail(node, detail, kind)
98 }
99
100 fn decl_with_detail<N: HasName + HasAttrs>(
101 node: &N,
102 detail: Option<String>,
103 kind: StructureNodeKind,
104 ) -> Option<StructureNode> {
105 let name = node.name()?;
106
107 Some(StructureNode {
108 parent: None,
109 label: name.text().to_string(),
110 navigation_range: name.syntax().text_range(),
111 node_range: node.syntax().text_range(),
112 kind,
113 detail,
114 deprecated: node.attrs().filter_map(|x| x.simple_name()).any(|x| x == "deprecated"),
115 })
116 }
117
118 fn collapse_ws(node: &SyntaxNode, output: &mut String) {
119 let mut can_insert_ws = false;
120 node.text().for_each_chunk(|chunk| {
121 for line in chunk.lines() {
122 let line = line.trim();
123 if line.is_empty() {
124 if can_insert_ws {
125 output.push(' ');
126 can_insert_ws = false;
127 }
128 } else {
129 output.push_str(line);
130 can_insert_ws = true;
131 }
132 }
133 })
134 }
135
136 match_ast! {
137 match node {
138 ast::Fn(it) => {
139 let mut detail = String::from("fn");
140 if let Some(type_param_list) = it.generic_param_list() {
141 collapse_ws(type_param_list.syntax(), &mut detail);
142 }
143 let has_self_param = if let Some(param_list) = it.param_list() {
144 collapse_ws(param_list.syntax(), &mut detail);
145 param_list.self_param().is_some()
146 } else {
147 false
148 };
149 if let Some(ret_type) = it.ret_type() {
150 detail.push(' ');
151 collapse_ws(ret_type.syntax(), &mut detail);
152 }
153
154 decl_with_detail(&it, Some(detail), StructureNodeKind::SymbolKind(if has_self_param {
155 SymbolKind::Method
156 } else {
157 SymbolKind::Function
158 }))
159 },
160 ast::Struct(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Struct)),
161 ast::Union(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Union)),
162 ast::Enum(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Enum)),
163 ast::Variant(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Variant)),
164 ast::Trait(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Trait)),
165 ast::Module(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Module)),
166 ast::Macro(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Macro)),
167 ast::TypeAlias(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::TypeAlias)),
168 ast::RecordField(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Field)),
169 ast::Const(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Const)),
170 ast::Static(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Static)),
171 ast::Impl(it) => {
172 let target_type = it.self_ty()?;
173 let target_trait = it.trait_();
174 let label = match target_trait {
175 None => format!("impl {}", target_type.syntax().text()),
176 Some(t) => {
177 format!("impl {}{} for {}",
178 it.excl_token().map(|x| x.to_string()).unwrap_or_default(),
179 t.syntax().text(),
180 target_type.syntax().text(),
181 )
182 }
183 };
184
185 let node = StructureNode {
186 parent: None,
187 label,
188 navigation_range: target_type.syntax().text_range(),
189 node_range: it.syntax().text_range(),
190 kind: StructureNodeKind::SymbolKind(SymbolKind::Impl),
191 detail: None,
192 deprecated: false,
193 };
194 Some(node)
195 },
196 ast::LetStmt(it) => {
197 if config.exclude_locals {
198 return None;
199 }
200
201 let pat = it.pat()?;
202
203 let mut label = String::new();
204 collapse_ws(pat.syntax(), &mut label);
205
206 let node = StructureNode {
207 parent: None,
208 label,
209 navigation_range: pat.syntax().text_range(),
210 node_range: it.syntax().text_range(),
211 kind: StructureNodeKind::SymbolKind(SymbolKind::Local),
212 detail: it.ty().map(|ty| ty.to_string()),
213 deprecated: false,
214 };
215 Some(node)
216 },
217 ast::ExternBlock(it) => {
218 let mut label = "extern".to_owned();
219 let abi = it.abi()?;
220 if let Some(abi) = abi.string_token() {
221 label.push(' ');
222 label.push_str(abi.text());
223 }
224 Some(StructureNode {
225 parent: None,
226 label,
227 navigation_range: abi.syntax().text_range(),
228 node_range: it.syntax().text_range(),
229 kind: StructureNodeKind::ExternBlock,
230 detail: None,
231 deprecated: false,
232 })
233 },
234 _ => None,
235 }
236 }
237}
238
239fn structure_token(token: SyntaxToken) -> Option<StructureNode> {
240 if let Some(comment) = ast::Comment::cast(token) {
241 let text = comment.text().trim();
242
243 if let Some(region_name) =
244 text.strip_prefix("// region:").map(str::trim).filter(|it| !it.is_empty())
245 {
246 return Some(StructureNode {
247 parent: None,
248 label: region_name.to_owned(),
249 navigation_range: comment.syntax().text_range(),
250 node_range: comment.syntax().text_range(),
251 kind: StructureNodeKind::Region,
252 detail: None,
253 deprecated: false,
254 });
255 }
256 }
257
258 None
259}
260
261#[cfg(test)]
262mod tests {
263 use expect_test::{Expect, expect};
264
265 use super::*;
266
267 const DEFAULT_CONFIG: FileStructureConfig = FileStructureConfig { exclude_locals: true };
268
269 fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
270 check_with_config(ra_fixture, &DEFAULT_CONFIG, expect);
271 }
272
273 fn check_with_config(
274 #[rust_analyzer::rust_fixture] ra_fixture: &str,
275 config: &FileStructureConfig,
276 expect: Expect,
277 ) {
278 let file = SourceFile::parse(ra_fixture, span::Edition::CURRENT).ok().unwrap();
279 let structure = file_structure(&file, config);
280 expect.assert_debug_eq(&structure)
281 }
282
283 #[test]
284 fn test_negative_trait_bound() {
285 let txt = r#"impl !Unpin for Test {}"#;
286 check(
287 txt,
288 expect![[r#"
289 [
290 StructureNode {
291 parent: None,
292 label: "impl !Unpin for Test",
293 navigation_range: 16..20,
294 node_range: 0..23,
295 kind: SymbolKind(
296 Impl,
297 ),
298 detail: None,
299 deprecated: false,
300 },
301 ]
302 "#]],
303 );
304 }
305
306 #[test]
307 fn test_file_structure() {
308 check(
309 r#"
310struct Foo {
311 x: i32
312}
313
314mod m {
315 fn bar1() {}
316 fn bar2<T>(t: T) -> T {}
317 fn bar3<A,
318 B>(a: A,
319 b: B) -> Vec<
320 u32
321 > {}
322}
323
324enum E { X, Y(i32) }
325type T = ();
326static S: i32 = 92;
327const C: i32 = 92;
328trait Tr {}
329trait Alias = Tr;
330
331impl E {}
332
333impl fmt::Debug for E {}
334
335macro_rules! mc {
336 () => {}
337}
338
339#[macro_export]
340macro_rules! mcexp {
341 () => {}
342}
343
344/// Doc comment
345macro_rules! mcexp {
346 () => {}
347}
348
349#[deprecated]
350fn obsolete() {}
351
352#[deprecated(note = "for a while")]
353fn very_obsolete() {}
354
355// region: Some region name
356// endregion
357
358// region: dontpanic
359mod m {
360fn f() {}
361// endregion
362fn g() {}
363}
364
365extern "C" {}
366
367fn let_statements() {
368 let x = 42;
369 let mut y = x;
370 let Foo {
371 ..
372 } = Foo { x };
373 if let None = Some(x) {}
374 _ = ();
375 let _ = g();
376}
377"#,
378 expect![[r#"
379 [
380 StructureNode {
381 parent: None,
382 label: "Foo",
383 navigation_range: 8..11,
384 node_range: 1..26,
385 kind: SymbolKind(
386 Struct,
387 ),
388 detail: None,
389 deprecated: false,
390 },
391 StructureNode {
392 parent: Some(
393 0,
394 ),
395 label: "x",
396 navigation_range: 18..19,
397 node_range: 18..24,
398 kind: SymbolKind(
399 Field,
400 ),
401 detail: Some(
402 "i32",
403 ),
404 deprecated: false,
405 },
406 StructureNode {
407 parent: None,
408 label: "m",
409 navigation_range: 32..33,
410 node_range: 28..158,
411 kind: SymbolKind(
412 Module,
413 ),
414 detail: None,
415 deprecated: false,
416 },
417 StructureNode {
418 parent: Some(
419 2,
420 ),
421 label: "bar1",
422 navigation_range: 43..47,
423 node_range: 40..52,
424 kind: SymbolKind(
425 Function,
426 ),
427 detail: Some(
428 "fn()",
429 ),
430 deprecated: false,
431 },
432 StructureNode {
433 parent: Some(
434 2,
435 ),
436 label: "bar2",
437 navigation_range: 60..64,
438 node_range: 57..81,
439 kind: SymbolKind(
440 Function,
441 ),
442 detail: Some(
443 "fn<T>(t: T) -> T",
444 ),
445 deprecated: false,
446 },
447 StructureNode {
448 parent: Some(
449 2,
450 ),
451 label: "bar3",
452 navigation_range: 89..93,
453 node_range: 86..156,
454 kind: SymbolKind(
455 Function,
456 ),
457 detail: Some(
458 "fn<A, B>(a: A, b: B) -> Vec< u32 >",
459 ),
460 deprecated: false,
461 },
462 StructureNode {
463 parent: None,
464 label: "E",
465 navigation_range: 165..166,
466 node_range: 160..180,
467 kind: SymbolKind(
468 Enum,
469 ),
470 detail: None,
471 deprecated: false,
472 },
473 StructureNode {
474 parent: Some(
475 6,
476 ),
477 label: "X",
478 navigation_range: 169..170,
479 node_range: 169..170,
480 kind: SymbolKind(
481 Variant,
482 ),
483 detail: None,
484 deprecated: false,
485 },
486 StructureNode {
487 parent: Some(
488 6,
489 ),
490 label: "Y",
491 navigation_range: 172..173,
492 node_range: 172..178,
493 kind: SymbolKind(
494 Variant,
495 ),
496 detail: None,
497 deprecated: false,
498 },
499 StructureNode {
500 parent: None,
501 label: "T",
502 navigation_range: 186..187,
503 node_range: 181..193,
504 kind: SymbolKind(
505 TypeAlias,
506 ),
507 detail: Some(
508 "()",
509 ),
510 deprecated: false,
511 },
512 StructureNode {
513 parent: None,
514 label: "S",
515 navigation_range: 201..202,
516 node_range: 194..213,
517 kind: SymbolKind(
518 Static,
519 ),
520 detail: Some(
521 "i32",
522 ),
523 deprecated: false,
524 },
525 StructureNode {
526 parent: None,
527 label: "C",
528 navigation_range: 220..221,
529 node_range: 214..232,
530 kind: SymbolKind(
531 Const,
532 ),
533 detail: Some(
534 "i32",
535 ),
536 deprecated: false,
537 },
538 StructureNode {
539 parent: None,
540 label: "Tr",
541 navigation_range: 239..241,
542 node_range: 233..244,
543 kind: SymbolKind(
544 Trait,
545 ),
546 detail: None,
547 deprecated: false,
548 },
549 StructureNode {
550 parent: None,
551 label: "Alias",
552 navigation_range: 251..256,
553 node_range: 245..262,
554 kind: SymbolKind(
555 Trait,
556 ),
557 detail: None,
558 deprecated: false,
559 },
560 StructureNode {
561 parent: None,
562 label: "impl E",
563 navigation_range: 269..270,
564 node_range: 264..273,
565 kind: SymbolKind(
566 Impl,
567 ),
568 detail: None,
569 deprecated: false,
570 },
571 StructureNode {
572 parent: None,
573 label: "impl fmt::Debug for E",
574 navigation_range: 295..296,
575 node_range: 275..299,
576 kind: SymbolKind(
577 Impl,
578 ),
579 detail: None,
580 deprecated: false,
581 },
582 StructureNode {
583 parent: None,
584 label: "mc",
585 navigation_range: 314..316,
586 node_range: 301..333,
587 kind: SymbolKind(
588 Macro,
589 ),
590 detail: None,
591 deprecated: false,
592 },
593 StructureNode {
594 parent: None,
595 label: "mcexp",
596 navigation_range: 364..369,
597 node_range: 335..386,
598 kind: SymbolKind(
599 Macro,
600 ),
601 detail: None,
602 deprecated: false,
603 },
604 StructureNode {
605 parent: None,
606 label: "mcexp",
607 navigation_range: 417..422,
608 node_range: 388..439,
609 kind: SymbolKind(
610 Macro,
611 ),
612 detail: None,
613 deprecated: false,
614 },
615 StructureNode {
616 parent: None,
617 label: "obsolete",
618 navigation_range: 458..466,
619 node_range: 441..471,
620 kind: SymbolKind(
621 Function,
622 ),
623 detail: Some(
624 "fn()",
625 ),
626 deprecated: true,
627 },
628 StructureNode {
629 parent: None,
630 label: "very_obsolete",
631 navigation_range: 512..525,
632 node_range: 473..530,
633 kind: SymbolKind(
634 Function,
635 ),
636 detail: Some(
637 "fn()",
638 ),
639 deprecated: true,
640 },
641 StructureNode {
642 parent: None,
643 label: "Some region name",
644 navigation_range: 532..559,
645 node_range: 532..559,
646 kind: Region,
647 detail: None,
648 deprecated: false,
649 },
650 StructureNode {
651 parent: None,
652 label: "m",
653 navigation_range: 599..600,
654 node_range: 574..637,
655 kind: SymbolKind(
656 Module,
657 ),
658 detail: None,
659 deprecated: false,
660 },
661 StructureNode {
662 parent: Some(
663 22,
664 ),
665 label: "dontpanic",
666 navigation_range: 574..594,
667 node_range: 574..594,
668 kind: Region,
669 detail: None,
670 deprecated: false,
671 },
672 StructureNode {
673 parent: Some(
674 22,
675 ),
676 label: "f",
677 navigation_range: 606..607,
678 node_range: 603..612,
679 kind: SymbolKind(
680 Function,
681 ),
682 detail: Some(
683 "fn()",
684 ),
685 deprecated: false,
686 },
687 StructureNode {
688 parent: Some(
689 22,
690 ),
691 label: "g",
692 navigation_range: 629..630,
693 node_range: 613..635,
694 kind: SymbolKind(
695 Function,
696 ),
697 detail: Some(
698 "fn()",
699 ),
700 deprecated: false,
701 },
702 StructureNode {
703 parent: None,
704 label: "extern \"C\"",
705 navigation_range: 639..649,
706 node_range: 639..652,
707 kind: ExternBlock,
708 detail: None,
709 deprecated: false,
710 },
711 StructureNode {
712 parent: None,
713 label: "let_statements",
714 navigation_range: 657..671,
715 node_range: 654..814,
716 kind: SymbolKind(
717 Function,
718 ),
719 detail: Some(
720 "fn()",
721 ),
722 deprecated: false,
723 },
724 ]
725 "#]],
726 );
727 }
728
729 #[test]
730 fn test_file_structure_include_locals() {
731 check_with_config(
732 r#"
733struct Foo {
734 x: i32
735}
736
737mod m {
738 fn bar1() {}
739 fn bar2<T>(t: T) -> T {}
740 fn bar3<A,
741 B>(a: A,
742 b: B) -> Vec<
743 u32
744 > {}
745}
746
747enum E { X, Y(i32) }
748type T = ();
749static S: i32 = 42;
750const C: i32 = 42;
751trait Tr {}
752trait Alias = Tr;
753
754macro_rules! mc {
755 () => {}
756}
757
758fn let_statements() {
759 let x = 42;
760 let mut y = x;
761 let Foo {
762 ..
763 } = Foo { x };
764 _ = ();
765 let _ = g();
766}
767"#,
768 &FileStructureConfig { exclude_locals: false },
769 expect![[r#"
770 [
771 StructureNode {
772 parent: None,
773 label: "Foo",
774 navigation_range: 8..11,
775 node_range: 1..26,
776 kind: SymbolKind(
777 Struct,
778 ),
779 detail: None,
780 deprecated: false,
781 },
782 StructureNode {
783 parent: Some(
784 0,
785 ),
786 label: "x",
787 navigation_range: 18..19,
788 node_range: 18..24,
789 kind: SymbolKind(
790 Field,
791 ),
792 detail: Some(
793 "i32",
794 ),
795 deprecated: false,
796 },
797 StructureNode {
798 parent: None,
799 label: "m",
800 navigation_range: 32..33,
801 node_range: 28..158,
802 kind: SymbolKind(
803 Module,
804 ),
805 detail: None,
806 deprecated: false,
807 },
808 StructureNode {
809 parent: Some(
810 2,
811 ),
812 label: "bar1",
813 navigation_range: 43..47,
814 node_range: 40..52,
815 kind: SymbolKind(
816 Function,
817 ),
818 detail: Some(
819 "fn()",
820 ),
821 deprecated: false,
822 },
823 StructureNode {
824 parent: Some(
825 2,
826 ),
827 label: "bar2",
828 navigation_range: 60..64,
829 node_range: 57..81,
830 kind: SymbolKind(
831 Function,
832 ),
833 detail: Some(
834 "fn<T>(t: T) -> T",
835 ),
836 deprecated: false,
837 },
838 StructureNode {
839 parent: Some(
840 2,
841 ),
842 label: "bar3",
843 navigation_range: 89..93,
844 node_range: 86..156,
845 kind: SymbolKind(
846 Function,
847 ),
848 detail: Some(
849 "fn<A, B>(a: A, b: B) -> Vec< u32 >",
850 ),
851 deprecated: false,
852 },
853 StructureNode {
854 parent: None,
855 label: "E",
856 navigation_range: 165..166,
857 node_range: 160..180,
858 kind: SymbolKind(
859 Enum,
860 ),
861 detail: None,
862 deprecated: false,
863 },
864 StructureNode {
865 parent: Some(
866 6,
867 ),
868 label: "X",
869 navigation_range: 169..170,
870 node_range: 169..170,
871 kind: SymbolKind(
872 Variant,
873 ),
874 detail: None,
875 deprecated: false,
876 },
877 StructureNode {
878 parent: Some(
879 6,
880 ),
881 label: "Y",
882 navigation_range: 172..173,
883 node_range: 172..178,
884 kind: SymbolKind(
885 Variant,
886 ),
887 detail: None,
888 deprecated: false,
889 },
890 StructureNode {
891 parent: None,
892 label: "T",
893 navigation_range: 186..187,
894 node_range: 181..193,
895 kind: SymbolKind(
896 TypeAlias,
897 ),
898 detail: Some(
899 "()",
900 ),
901 deprecated: false,
902 },
903 StructureNode {
904 parent: None,
905 label: "S",
906 navigation_range: 201..202,
907 node_range: 194..213,
908 kind: SymbolKind(
909 Static,
910 ),
911 detail: Some(
912 "i32",
913 ),
914 deprecated: false,
915 },
916 StructureNode {
917 parent: None,
918 label: "C",
919 navigation_range: 220..221,
920 node_range: 214..232,
921 kind: SymbolKind(
922 Const,
923 ),
924 detail: Some(
925 "i32",
926 ),
927 deprecated: false,
928 },
929 StructureNode {
930 parent: None,
931 label: "Tr",
932 navigation_range: 239..241,
933 node_range: 233..244,
934 kind: SymbolKind(
935 Trait,
936 ),
937 detail: None,
938 deprecated: false,
939 },
940 StructureNode {
941 parent: None,
942 label: "Alias",
943 navigation_range: 251..256,
944 node_range: 245..262,
945 kind: SymbolKind(
946 Trait,
947 ),
948 detail: None,
949 deprecated: false,
950 },
951 StructureNode {
952 parent: None,
953 label: "mc",
954 navigation_range: 277..279,
955 node_range: 264..296,
956 kind: SymbolKind(
957 Macro,
958 ),
959 detail: None,
960 deprecated: false,
961 },
962 StructureNode {
963 parent: None,
964 label: "let_statements",
965 navigation_range: 301..315,
966 node_range: 298..429,
967 kind: SymbolKind(
968 Function,
969 ),
970 detail: Some(
971 "fn()",
972 ),
973 deprecated: false,
974 },
975 StructureNode {
976 parent: Some(
977 15,
978 ),
979 label: "x",
980 navigation_range: 328..329,
981 node_range: 324..335,
982 kind: SymbolKind(
983 Local,
984 ),
985 detail: None,
986 deprecated: false,
987 },
988 StructureNode {
989 parent: Some(
990 15,
991 ),
992 label: "mut y",
993 navigation_range: 344..349,
994 node_range: 340..354,
995 kind: SymbolKind(
996 Local,
997 ),
998 detail: None,
999 deprecated: false,
1000 },
1001 StructureNode {
1002 parent: Some(
1003 15,
1004 ),
1005 label: "Foo { .. }",
1006 navigation_range: 363..385,
1007 node_range: 359..398,
1008 kind: SymbolKind(
1009 Local,
1010 ),
1011 detail: None,
1012 deprecated: false,
1013 },
1014 StructureNode {
1015 parent: Some(
1016 15,
1017 ),
1018 label: "_",
1019 navigation_range: 419..420,
1020 node_range: 415..427,
1021 kind: SymbolKind(
1022 Local,
1023 ),
1024 detail: None,
1025 deprecated: false,
1026 },
1027 ]
1028 "#]],
1029 );
1030 }
1031}