use crate::TextRange; use ra_syntax::{ algo::visit::{visitor, Visitor}, ast::{self, NameOwner}, AstNode, SourceFile, SyntaxKind, SyntaxNode, WalkEvent, }; #[derive(Debug, Clone)] pub struct StructureNode { pub parent: Option, pub label: String, pub navigation_range: TextRange, pub node_range: TextRange, pub kind: SyntaxKind, } pub fn file_structure(file: &SourceFile) -> Vec { let mut res = Vec::new(); let mut stack = Vec::new(); for event in file.syntax().preorder() { match event { WalkEvent::Enter(node) => { if let Some(mut symbol) = structure_node(node) { symbol.parent = stack.last().map(|&n| n); stack.push(res.len()); res.push(symbol); } } WalkEvent::Leave(node) => { if structure_node(node).is_some() { stack.pop().unwrap(); } } } } res } fn structure_node(node: &SyntaxNode) -> Option { fn decl(node: &N) -> Option { let name = node.name()?; Some(StructureNode { parent: None, label: name.text().to_string(), navigation_range: name.syntax().range(), node_range: node.syntax().range(), kind: node.syntax().kind(), }) } visitor() .visit(decl::) .visit(decl::) .visit(decl::) .visit(decl::) .visit(decl::) .visit(decl::) .visit(decl::) .visit(decl::) .visit(decl::) .visit(|im: &ast::ImplBlock| { let target_type = im.target_type()?; let target_trait = im.target_trait(); let label = match target_trait { None => format!("impl {}", target_type.syntax().text()), Some(t) => format!( "impl {} for {}", t.syntax().text(), target_type.syntax().text(), ), }; let node = StructureNode { parent: None, label, navigation_range: target_type.syntax().range(), node_range: im.syntax().range(), kind: im.syntax().kind(), }; Some(node) }) .accept(node)? } #[cfg(test)] mod tests { use super::*; use insta::assert_debug_snapshot_matches; #[test] fn test_file_structure() { let file = SourceFile::parse( r#" struct Foo { x: i32 } mod m { fn bar() {} } enum E { X, Y(i32) } type T = (); static S: i32 = 92; const C: i32 = 92; impl E {} impl fmt::Debug for E {} "#, ); let structure = file_structure(&file); assert_debug_snapshot_matches!("file_structure", structure); } }