|  | use ide_db::SymbolKind; | 
|  | use syntax::{ | 
|  | AstNode, AstToken, NodeOrToken, SourceFile, SyntaxNode, SyntaxToken, TextRange, WalkEvent, | 
|  | ast::{self, HasAttrs, HasGenericParams, HasName}, | 
|  | match_ast, | 
|  | }; | 
|  |  | 
|  | #[derive(Debug, Clone)] | 
|  | pub struct StructureNode { | 
|  | pub parent: Option<usize>, | 
|  | pub label: String, | 
|  | pub navigation_range: TextRange, | 
|  | pub node_range: TextRange, | 
|  | pub kind: StructureNodeKind, | 
|  | pub detail: Option<String>, | 
|  | pub deprecated: bool, | 
|  | } | 
|  |  | 
|  | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] | 
|  | pub enum StructureNodeKind { | 
|  | SymbolKind(SymbolKind), | 
|  | ExternBlock, | 
|  | Region, | 
|  | } | 
|  |  | 
|  | #[derive(Debug, Clone)] | 
|  | pub struct FileStructureConfig { | 
|  | pub exclude_locals: bool, | 
|  | } | 
|  |  | 
|  | // Feature: File Structure | 
|  | // | 
|  | // Provides a tree of the symbols defined in the file. Can be used to | 
|  | // | 
|  | // * fuzzy search symbol in a file (super useful) | 
|  | // * draw breadcrumbs to describe the context around the cursor | 
|  | // * draw outline of the file | 
|  | // | 
|  | // | Editor  | Shortcut | | 
|  | // |---------|----------| | 
|  | // | VS Code | <kbd>Ctrl+Shift+O</kbd> | | 
|  | // | 
|  | //  | 
|  | pub(crate) fn file_structure( | 
|  | file: &SourceFile, | 
|  | config: &FileStructureConfig, | 
|  | ) -> Vec<StructureNode> { | 
|  | let mut res = Vec::new(); | 
|  | let mut stack = Vec::new(); | 
|  |  | 
|  | for event in file.syntax().preorder_with_tokens() { | 
|  | match event { | 
|  | WalkEvent::Enter(NodeOrToken::Node(node)) => { | 
|  | if let Some(mut symbol) = structure_node(&node, config) { | 
|  | symbol.parent = stack.last().copied(); | 
|  | stack.push(res.len()); | 
|  | res.push(symbol); | 
|  | } | 
|  | } | 
|  | WalkEvent::Leave(NodeOrToken::Node(node)) => { | 
|  | if structure_node(&node, config).is_some() { | 
|  | stack.pop().unwrap(); | 
|  | } | 
|  | } | 
|  | WalkEvent::Enter(NodeOrToken::Token(token)) => { | 
|  | if let Some(mut symbol) = structure_token(token) { | 
|  | symbol.parent = stack.last().copied(); | 
|  | stack.push(res.len()); | 
|  | res.push(symbol); | 
|  | } | 
|  | } | 
|  | WalkEvent::Leave(NodeOrToken::Token(token)) => { | 
|  | if structure_token(token).is_some() { | 
|  | stack.pop().unwrap(); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | res | 
|  | } | 
|  |  | 
|  | fn structure_node(node: &SyntaxNode, config: &FileStructureConfig) -> Option<StructureNode> { | 
|  | fn decl<N: HasName + HasAttrs>(node: N, kind: StructureNodeKind) -> Option<StructureNode> { | 
|  | decl_with_detail(&node, None, kind) | 
|  | } | 
|  |  | 
|  | fn decl_with_type_ref<N: HasName + HasAttrs>( | 
|  | node: &N, | 
|  | type_ref: Option<ast::Type>, | 
|  | kind: StructureNodeKind, | 
|  | ) -> Option<StructureNode> { | 
|  | let detail = type_ref.map(|type_ref| { | 
|  | let mut detail = String::new(); | 
|  | collapse_ws(type_ref.syntax(), &mut detail); | 
|  | detail | 
|  | }); | 
|  | decl_with_detail(node, detail, kind) | 
|  | } | 
|  |  | 
|  | fn decl_with_detail<N: HasName + HasAttrs>( | 
|  | node: &N, | 
|  | detail: Option<String>, | 
|  | kind: StructureNodeKind, | 
|  | ) -> Option<StructureNode> { | 
|  | let name = node.name()?; | 
|  |  | 
|  | Some(StructureNode { | 
|  | parent: None, | 
|  | label: name.text().to_string(), | 
|  | navigation_range: name.syntax().text_range(), | 
|  | node_range: node.syntax().text_range(), | 
|  | kind, | 
|  | detail, | 
|  | deprecated: node.attrs().filter_map(|x| x.simple_name()).any(|x| x == "deprecated"), | 
|  | }) | 
|  | } | 
|  |  | 
|  | fn collapse_ws(node: &SyntaxNode, output: &mut String) { | 
|  | let mut can_insert_ws = false; | 
|  | node.text().for_each_chunk(|chunk| { | 
|  | for line in chunk.lines() { | 
|  | let line = line.trim(); | 
|  | if line.is_empty() { | 
|  | if can_insert_ws { | 
|  | output.push(' '); | 
|  | can_insert_ws = false; | 
|  | } | 
|  | } else { | 
|  | output.push_str(line); | 
|  | can_insert_ws = true; | 
|  | } | 
|  | } | 
|  | }) | 
|  | } | 
|  |  | 
|  | match_ast! { | 
|  | match node { | 
|  | ast::Fn(it) => { | 
|  | let mut detail = String::from("fn"); | 
|  | if let Some(type_param_list) = it.generic_param_list() { | 
|  | collapse_ws(type_param_list.syntax(), &mut detail); | 
|  | } | 
|  | let has_self_param = if let Some(param_list) = it.param_list() { | 
|  | collapse_ws(param_list.syntax(), &mut detail); | 
|  | param_list.self_param().is_some() | 
|  | } else { | 
|  | false | 
|  | }; | 
|  | if let Some(ret_type) = it.ret_type() { | 
|  | detail.push(' '); | 
|  | collapse_ws(ret_type.syntax(), &mut detail); | 
|  | } | 
|  |  | 
|  | decl_with_detail(&it, Some(detail), StructureNodeKind::SymbolKind(if has_self_param { | 
|  | SymbolKind::Method | 
|  | } else { | 
|  | SymbolKind::Function | 
|  | })) | 
|  | }, | 
|  | ast::Struct(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Struct)), | 
|  | ast::Union(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Union)), | 
|  | ast::Enum(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Enum)), | 
|  | ast::Variant(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Variant)), | 
|  | ast::Trait(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Trait)), | 
|  | ast::TraitAlias(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::TraitAlias)), | 
|  | ast::Module(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Module)), | 
|  | ast::Macro(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Macro)), | 
|  | ast::TypeAlias(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::TypeAlias)), | 
|  | ast::RecordField(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Field)), | 
|  | ast::Const(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Const)), | 
|  | ast::Static(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Static)), | 
|  | ast::Impl(it) => { | 
|  | let target_type = it.self_ty()?; | 
|  | let target_trait = it.trait_(); | 
|  | let label = match target_trait { | 
|  | None => format!("impl {}", target_type.syntax().text()), | 
|  | Some(t) => { | 
|  | format!("impl {}{} for {}", | 
|  | it.excl_token().map(|x| x.to_string()).unwrap_or_default(), | 
|  | t.syntax().text(), | 
|  | target_type.syntax().text(), | 
|  | ) | 
|  | } | 
|  | }; | 
|  |  | 
|  | let node = StructureNode { | 
|  | parent: None, | 
|  | label, | 
|  | navigation_range: target_type.syntax().text_range(), | 
|  | node_range: it.syntax().text_range(), | 
|  | kind: StructureNodeKind::SymbolKind(SymbolKind::Impl), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }; | 
|  | Some(node) | 
|  | }, | 
|  | ast::LetStmt(it) => { | 
|  | if config.exclude_locals { | 
|  | return None; | 
|  | } | 
|  |  | 
|  | let pat = it.pat()?; | 
|  |  | 
|  | let mut label = String::new(); | 
|  | collapse_ws(pat.syntax(), &mut label); | 
|  |  | 
|  | let node = StructureNode { | 
|  | parent: None, | 
|  | label, | 
|  | navigation_range: pat.syntax().text_range(), | 
|  | node_range: it.syntax().text_range(), | 
|  | kind: StructureNodeKind::SymbolKind(SymbolKind::Local), | 
|  | detail: it.ty().map(|ty| ty.to_string()), | 
|  | deprecated: false, | 
|  | }; | 
|  |  | 
|  | Some(node) | 
|  | }, | 
|  | ast::ExternBlock(it) => { | 
|  | let mut label = "extern".to_owned(); | 
|  | let abi = it.abi()?; | 
|  | if let Some(abi) = abi.string_token() { | 
|  | label.push(' '); | 
|  | label.push_str(abi.text()); | 
|  | } | 
|  | Some(StructureNode { | 
|  | parent: None, | 
|  | label, | 
|  | navigation_range: abi.syntax().text_range(), | 
|  | node_range: it.syntax().text_range(), | 
|  | kind: StructureNodeKind::ExternBlock, | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }) | 
|  | }, | 
|  | _ => None, | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | fn structure_token(token: SyntaxToken) -> Option<StructureNode> { | 
|  | if let Some(comment) = ast::Comment::cast(token) { | 
|  | let text = comment.text().trim(); | 
|  |  | 
|  | if let Some(region_name) = | 
|  | text.strip_prefix("// region:").map(str::trim).filter(|it| !it.is_empty()) | 
|  | { | 
|  | return Some(StructureNode { | 
|  | parent: None, | 
|  | label: region_name.to_owned(), | 
|  | navigation_range: comment.syntax().text_range(), | 
|  | node_range: comment.syntax().text_range(), | 
|  | kind: StructureNodeKind::Region, | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }); | 
|  | } | 
|  | } | 
|  |  | 
|  | None | 
|  | } | 
|  |  | 
|  | #[cfg(test)] | 
|  | mod tests { | 
|  | use expect_test::{Expect, expect}; | 
|  |  | 
|  | use super::*; | 
|  |  | 
|  | const DEFAULT_CONFIG: FileStructureConfig = FileStructureConfig { exclude_locals: true }; | 
|  |  | 
|  | fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { | 
|  | check_with_config(ra_fixture, &DEFAULT_CONFIG, expect); | 
|  | } | 
|  |  | 
|  | fn check_with_config( | 
|  | #[rust_analyzer::rust_fixture] ra_fixture: &str, | 
|  | config: &FileStructureConfig, | 
|  | expect: Expect, | 
|  | ) { | 
|  | let file = SourceFile::parse(ra_fixture, span::Edition::CURRENT).ok().unwrap(); | 
|  | let structure = file_structure(&file, config); | 
|  | expect.assert_debug_eq(&structure) | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_negative_trait_bound() { | 
|  | let txt = r#"impl !Unpin for Test {}"#; | 
|  | check( | 
|  | txt, | 
|  | expect![[r#" | 
|  | [ | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "impl !Unpin for Test", | 
|  | navigation_range: 16..20, | 
|  | node_range: 0..23, | 
|  | kind: SymbolKind( | 
|  | Impl, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | ] | 
|  | "#]], | 
|  | ); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_file_structure() { | 
|  | check( | 
|  | r#" | 
|  | struct Foo { | 
|  | x: i32 | 
|  | } | 
|  |  | 
|  | mod m { | 
|  | fn bar1() {} | 
|  | fn bar2<T>(t: T) -> T {} | 
|  | fn bar3<A, | 
|  | B>(a: A, | 
|  | b: B) -> Vec< | 
|  | u32 | 
|  | > {} | 
|  | } | 
|  |  | 
|  | enum E { X, Y(i32) } | 
|  | type T = (); | 
|  | static S: i32 = 92; | 
|  | const C: i32 = 92; | 
|  | trait Tr {} | 
|  | trait Alias = Tr; | 
|  |  | 
|  | impl E {} | 
|  |  | 
|  | impl fmt::Debug for E {} | 
|  |  | 
|  | macro_rules! mc { | 
|  | () => {} | 
|  | } | 
|  |  | 
|  | #[macro_export] | 
|  | macro_rules! mcexp { | 
|  | () => {} | 
|  | } | 
|  |  | 
|  | /// Doc comment | 
|  | macro_rules! mcexp { | 
|  | () => {} | 
|  | } | 
|  |  | 
|  | #[deprecated] | 
|  | fn obsolete() {} | 
|  |  | 
|  | #[deprecated(note = "for a while")] | 
|  | fn very_obsolete() {} | 
|  |  | 
|  | // region: Some region name | 
|  | // endregion | 
|  |  | 
|  | // region: dontpanic | 
|  | mod m { | 
|  | fn f() {} | 
|  | // endregion | 
|  | fn g() {} | 
|  | } | 
|  |  | 
|  | extern "C" {} | 
|  |  | 
|  | fn let_statements() { | 
|  | let x = 42; | 
|  | let mut y = x; | 
|  | let Foo { | 
|  | .. | 
|  | } = Foo { x }; | 
|  | if let None = Some(x) {} | 
|  | _ = (); | 
|  | let _ = g(); | 
|  | } | 
|  | "#, | 
|  | expect![[r#" | 
|  | [ | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "Foo", | 
|  | navigation_range: 8..11, | 
|  | node_range: 1..26, | 
|  | kind: SymbolKind( | 
|  | Struct, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: Some( | 
|  | 0, | 
|  | ), | 
|  | label: "x", | 
|  | navigation_range: 18..19, | 
|  | node_range: 18..24, | 
|  | kind: SymbolKind( | 
|  | Field, | 
|  | ), | 
|  | detail: Some( | 
|  | "i32", | 
|  | ), | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "m", | 
|  | navigation_range: 32..33, | 
|  | node_range: 28..158, | 
|  | kind: SymbolKind( | 
|  | Module, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: Some( | 
|  | 2, | 
|  | ), | 
|  | label: "bar1", | 
|  | navigation_range: 43..47, | 
|  | node_range: 40..52, | 
|  | kind: SymbolKind( | 
|  | Function, | 
|  | ), | 
|  | detail: Some( | 
|  | "fn()", | 
|  | ), | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: Some( | 
|  | 2, | 
|  | ), | 
|  | label: "bar2", | 
|  | navigation_range: 60..64, | 
|  | node_range: 57..81, | 
|  | kind: SymbolKind( | 
|  | Function, | 
|  | ), | 
|  | detail: Some( | 
|  | "fn<T>(t: T) -> T", | 
|  | ), | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: Some( | 
|  | 2, | 
|  | ), | 
|  | label: "bar3", | 
|  | navigation_range: 89..93, | 
|  | node_range: 86..156, | 
|  | kind: SymbolKind( | 
|  | Function, | 
|  | ), | 
|  | detail: Some( | 
|  | "fn<A, B>(a: A, b: B) -> Vec< u32 >", | 
|  | ), | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "E", | 
|  | navigation_range: 165..166, | 
|  | node_range: 160..180, | 
|  | kind: SymbolKind( | 
|  | Enum, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: Some( | 
|  | 6, | 
|  | ), | 
|  | label: "X", | 
|  | navigation_range: 169..170, | 
|  | node_range: 169..170, | 
|  | kind: SymbolKind( | 
|  | Variant, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: Some( | 
|  | 6, | 
|  | ), | 
|  | label: "Y", | 
|  | navigation_range: 172..173, | 
|  | node_range: 172..178, | 
|  | kind: SymbolKind( | 
|  | Variant, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "T", | 
|  | navigation_range: 186..187, | 
|  | node_range: 181..193, | 
|  | kind: SymbolKind( | 
|  | TypeAlias, | 
|  | ), | 
|  | detail: Some( | 
|  | "()", | 
|  | ), | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "S", | 
|  | navigation_range: 201..202, | 
|  | node_range: 194..213, | 
|  | kind: SymbolKind( | 
|  | Static, | 
|  | ), | 
|  | detail: Some( | 
|  | "i32", | 
|  | ), | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "C", | 
|  | navigation_range: 220..221, | 
|  | node_range: 214..232, | 
|  | kind: SymbolKind( | 
|  | Const, | 
|  | ), | 
|  | detail: Some( | 
|  | "i32", | 
|  | ), | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "Tr", | 
|  | navigation_range: 239..241, | 
|  | node_range: 233..244, | 
|  | kind: SymbolKind( | 
|  | Trait, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "Alias", | 
|  | navigation_range: 251..256, | 
|  | node_range: 245..262, | 
|  | kind: SymbolKind( | 
|  | TraitAlias, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "impl E", | 
|  | navigation_range: 269..270, | 
|  | node_range: 264..273, | 
|  | kind: SymbolKind( | 
|  | Impl, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "impl fmt::Debug for E", | 
|  | navigation_range: 295..296, | 
|  | node_range: 275..299, | 
|  | kind: SymbolKind( | 
|  | Impl, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "mc", | 
|  | navigation_range: 314..316, | 
|  | node_range: 301..333, | 
|  | kind: SymbolKind( | 
|  | Macro, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "mcexp", | 
|  | navigation_range: 364..369, | 
|  | node_range: 335..386, | 
|  | kind: SymbolKind( | 
|  | Macro, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "mcexp", | 
|  | navigation_range: 417..422, | 
|  | node_range: 388..439, | 
|  | kind: SymbolKind( | 
|  | Macro, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "obsolete", | 
|  | navigation_range: 458..466, | 
|  | node_range: 441..471, | 
|  | kind: SymbolKind( | 
|  | Function, | 
|  | ), | 
|  | detail: Some( | 
|  | "fn()", | 
|  | ), | 
|  | deprecated: true, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "very_obsolete", | 
|  | navigation_range: 512..525, | 
|  | node_range: 473..530, | 
|  | kind: SymbolKind( | 
|  | Function, | 
|  | ), | 
|  | detail: Some( | 
|  | "fn()", | 
|  | ), | 
|  | deprecated: true, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "Some region name", | 
|  | navigation_range: 532..559, | 
|  | node_range: 532..559, | 
|  | kind: Region, | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "m", | 
|  | navigation_range: 599..600, | 
|  | node_range: 574..637, | 
|  | kind: SymbolKind( | 
|  | Module, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: Some( | 
|  | 22, | 
|  | ), | 
|  | label: "dontpanic", | 
|  | navigation_range: 574..594, | 
|  | node_range: 574..594, | 
|  | kind: Region, | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: Some( | 
|  | 22, | 
|  | ), | 
|  | label: "f", | 
|  | navigation_range: 606..607, | 
|  | node_range: 603..612, | 
|  | kind: SymbolKind( | 
|  | Function, | 
|  | ), | 
|  | detail: Some( | 
|  | "fn()", | 
|  | ), | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: Some( | 
|  | 22, | 
|  | ), | 
|  | label: "g", | 
|  | navigation_range: 629..630, | 
|  | node_range: 613..635, | 
|  | kind: SymbolKind( | 
|  | Function, | 
|  | ), | 
|  | detail: Some( | 
|  | "fn()", | 
|  | ), | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "extern \"C\"", | 
|  | navigation_range: 639..649, | 
|  | node_range: 639..652, | 
|  | kind: ExternBlock, | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "let_statements", | 
|  | navigation_range: 657..671, | 
|  | node_range: 654..814, | 
|  | kind: SymbolKind( | 
|  | Function, | 
|  | ), | 
|  | detail: Some( | 
|  | "fn()", | 
|  | ), | 
|  | deprecated: false, | 
|  | }, | 
|  | ] | 
|  | "#]], | 
|  | ); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_file_structure_include_locals() { | 
|  | check_with_config( | 
|  | r#" | 
|  | struct Foo { | 
|  | x: i32 | 
|  | } | 
|  |  | 
|  | mod m { | 
|  | fn bar1() {} | 
|  | fn bar2<T>(t: T) -> T {} | 
|  | fn bar3<A, | 
|  | B>(a: A, | 
|  | b: B) -> Vec< | 
|  | u32 | 
|  | > {} | 
|  | } | 
|  |  | 
|  | enum E { X, Y(i32) } | 
|  | type T = (); | 
|  | static S: i32 = 42; | 
|  | const C: i32 = 42; | 
|  | trait Tr {} | 
|  | trait Alias = Tr; | 
|  |  | 
|  | macro_rules! mc { | 
|  | () => {} | 
|  | } | 
|  |  | 
|  | fn let_statements() { | 
|  | let x = 42; | 
|  | let mut y = x; | 
|  | let Foo { | 
|  | .. | 
|  | } = Foo { x }; | 
|  | _ = (); | 
|  | let _ = g(); | 
|  | } | 
|  | "#, | 
|  | &FileStructureConfig { exclude_locals: false }, | 
|  | expect![[r#" | 
|  | [ | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "Foo", | 
|  | navigation_range: 8..11, | 
|  | node_range: 1..26, | 
|  | kind: SymbolKind( | 
|  | Struct, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: Some( | 
|  | 0, | 
|  | ), | 
|  | label: "x", | 
|  | navigation_range: 18..19, | 
|  | node_range: 18..24, | 
|  | kind: SymbolKind( | 
|  | Field, | 
|  | ), | 
|  | detail: Some( | 
|  | "i32", | 
|  | ), | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "m", | 
|  | navigation_range: 32..33, | 
|  | node_range: 28..158, | 
|  | kind: SymbolKind( | 
|  | Module, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: Some( | 
|  | 2, | 
|  | ), | 
|  | label: "bar1", | 
|  | navigation_range: 43..47, | 
|  | node_range: 40..52, | 
|  | kind: SymbolKind( | 
|  | Function, | 
|  | ), | 
|  | detail: Some( | 
|  | "fn()", | 
|  | ), | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: Some( | 
|  | 2, | 
|  | ), | 
|  | label: "bar2", | 
|  | navigation_range: 60..64, | 
|  | node_range: 57..81, | 
|  | kind: SymbolKind( | 
|  | Function, | 
|  | ), | 
|  | detail: Some( | 
|  | "fn<T>(t: T) -> T", | 
|  | ), | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: Some( | 
|  | 2, | 
|  | ), | 
|  | label: "bar3", | 
|  | navigation_range: 89..93, | 
|  | node_range: 86..156, | 
|  | kind: SymbolKind( | 
|  | Function, | 
|  | ), | 
|  | detail: Some( | 
|  | "fn<A, B>(a: A, b: B) -> Vec< u32 >", | 
|  | ), | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "E", | 
|  | navigation_range: 165..166, | 
|  | node_range: 160..180, | 
|  | kind: SymbolKind( | 
|  | Enum, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: Some( | 
|  | 6, | 
|  | ), | 
|  | label: "X", | 
|  | navigation_range: 169..170, | 
|  | node_range: 169..170, | 
|  | kind: SymbolKind( | 
|  | Variant, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: Some( | 
|  | 6, | 
|  | ), | 
|  | label: "Y", | 
|  | navigation_range: 172..173, | 
|  | node_range: 172..178, | 
|  | kind: SymbolKind( | 
|  | Variant, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "T", | 
|  | navigation_range: 186..187, | 
|  | node_range: 181..193, | 
|  | kind: SymbolKind( | 
|  | TypeAlias, | 
|  | ), | 
|  | detail: Some( | 
|  | "()", | 
|  | ), | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "S", | 
|  | navigation_range: 201..202, | 
|  | node_range: 194..213, | 
|  | kind: SymbolKind( | 
|  | Static, | 
|  | ), | 
|  | detail: Some( | 
|  | "i32", | 
|  | ), | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "C", | 
|  | navigation_range: 220..221, | 
|  | node_range: 214..232, | 
|  | kind: SymbolKind( | 
|  | Const, | 
|  | ), | 
|  | detail: Some( | 
|  | "i32", | 
|  | ), | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "Tr", | 
|  | navigation_range: 239..241, | 
|  | node_range: 233..244, | 
|  | kind: SymbolKind( | 
|  | Trait, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "Alias", | 
|  | navigation_range: 251..256, | 
|  | node_range: 245..262, | 
|  | kind: SymbolKind( | 
|  | TraitAlias, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "mc", | 
|  | navigation_range: 277..279, | 
|  | node_range: 264..296, | 
|  | kind: SymbolKind( | 
|  | Macro, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: None, | 
|  | label: "let_statements", | 
|  | navigation_range: 301..315, | 
|  | node_range: 298..429, | 
|  | kind: SymbolKind( | 
|  | Function, | 
|  | ), | 
|  | detail: Some( | 
|  | "fn()", | 
|  | ), | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: Some( | 
|  | 15, | 
|  | ), | 
|  | label: "x", | 
|  | navigation_range: 328..329, | 
|  | node_range: 324..335, | 
|  | kind: SymbolKind( | 
|  | Local, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: Some( | 
|  | 15, | 
|  | ), | 
|  | label: "mut y", | 
|  | navigation_range: 344..349, | 
|  | node_range: 340..354, | 
|  | kind: SymbolKind( | 
|  | Local, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: Some( | 
|  | 15, | 
|  | ), | 
|  | label: "Foo { .. }", | 
|  | navigation_range: 363..385, | 
|  | node_range: 359..398, | 
|  | kind: SymbolKind( | 
|  | Local, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | StructureNode { | 
|  | parent: Some( | 
|  | 15, | 
|  | ), | 
|  | label: "_", | 
|  | navigation_range: 419..420, | 
|  | node_range: 415..427, | 
|  | kind: SymbolKind( | 
|  | Local, | 
|  | ), | 
|  | detail: None, | 
|  | deprecated: false, | 
|  | }, | 
|  | ] | 
|  | "#]], | 
|  | ); | 
|  | } | 
|  | } |