2018-08-13 13:24:22 +02:00
|
|
|
use smol_str::SmolStr;
|
2018-08-11 11:28:59 +02:00
|
|
|
use libsyntax2::{
|
|
|
|
SyntaxKind, SyntaxNodeRef, SyntaxRoot, AstNode,
|
|
|
|
ast::{self, NameOwner},
|
|
|
|
algo::{
|
|
|
|
visit::{visitor, Visitor},
|
2018-08-14 10:20:09 +02:00
|
|
|
walk::{walk, WalkEvent, preorder},
|
2018-08-11 11:28:59 +02:00
|
|
|
},
|
|
|
|
};
|
|
|
|
use TextRange;
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2018-08-14 10:20:09 +02:00
|
|
|
pub struct StructureNode {
|
2018-08-11 11:28:59 +02:00
|
|
|
pub parent: Option<usize>,
|
2018-08-14 10:20:09 +02:00
|
|
|
pub label: String,
|
|
|
|
pub navigation_range: TextRange,
|
2018-08-11 11:28:59 +02:00
|
|
|
pub node_range: TextRange,
|
|
|
|
pub kind: SyntaxKind,
|
|
|
|
}
|
|
|
|
|
2018-08-14 10:20:09 +02:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct FileSymbol {
|
|
|
|
pub name: SmolStr,
|
|
|
|
pub node_range: TextRange,
|
|
|
|
pub kind: SyntaxKind,
|
|
|
|
}
|
2018-08-11 11:28:59 +02:00
|
|
|
|
|
|
|
pub fn file_symbols(file: &ast::File) -> Vec<FileSymbol> {
|
2018-08-16 11:51:40 +02:00
|
|
|
preorder(file.syntax_ref())
|
2018-08-14 10:20:09 +02:00
|
|
|
.filter_map(to_symbol)
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> {
|
|
|
|
fn decl<'a, N: NameOwner<&'a SyntaxRoot>>(node: N) -> Option<FileSymbol> {
|
|
|
|
let name = node.name()?;
|
|
|
|
Some(FileSymbol {
|
|
|
|
name: name.text(),
|
|
|
|
node_range: node.syntax().range(),
|
|
|
|
kind: node.syntax().kind(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
visitor()
|
|
|
|
.visit(decl::<ast::FnDef<_>>)
|
|
|
|
.visit(decl::<ast::StructDef<_>>)
|
|
|
|
.visit(decl::<ast::EnumDef<_>>)
|
|
|
|
.visit(decl::<ast::TraitDef<_>>)
|
|
|
|
.visit(decl::<ast::Module<_>>)
|
|
|
|
.visit(decl::<ast::TypeDef<_>>)
|
|
|
|
.visit(decl::<ast::ConstDef<_>>)
|
|
|
|
.visit(decl::<ast::StaticDef<_>>)
|
|
|
|
.accept(node)?
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn file_structure(file: &ast::File) -> Vec<StructureNode> {
|
2018-08-11 11:28:59 +02:00
|
|
|
let mut res = Vec::new();
|
|
|
|
let mut stack = Vec::new();
|
|
|
|
|
2018-08-16 11:51:40 +02:00
|
|
|
for event in walk(file.syntax_ref()) {
|
2018-08-11 11:28:59 +02:00
|
|
|
match event {
|
|
|
|
WalkEvent::Enter(node) => {
|
2018-08-14 10:20:09 +02:00
|
|
|
match structure_node(node) {
|
2018-08-11 11:28:59 +02:00
|
|
|
Some(mut symbol) => {
|
|
|
|
symbol.parent = stack.last().map(|&n| n);
|
|
|
|
stack.push(res.len());
|
|
|
|
res.push(symbol);
|
|
|
|
}
|
|
|
|
None => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
WalkEvent::Exit(node) => {
|
2018-08-14 10:20:09 +02:00
|
|
|
if structure_node(node).is_some() {
|
2018-08-11 11:28:59 +02:00
|
|
|
stack.pop().unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
2018-08-14 10:20:09 +02:00
|
|
|
fn structure_node(node: SyntaxNodeRef) -> Option<StructureNode> {
|
|
|
|
fn decl<'a, N: NameOwner<&'a SyntaxRoot>>(node: N) -> Option<StructureNode> {
|
2018-08-11 11:28:59 +02:00
|
|
|
let name = node.name()?;
|
2018-08-14 10:20:09 +02:00
|
|
|
Some(StructureNode {
|
2018-08-11 11:28:59 +02:00
|
|
|
parent: None,
|
2018-08-14 10:20:09 +02:00
|
|
|
label: name.text().to_string(),
|
|
|
|
navigation_range: name.syntax().range(),
|
2018-08-11 11:28:59 +02:00
|
|
|
node_range: node.syntax().range(),
|
|
|
|
kind: node.syntax().kind(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
visitor()
|
2018-08-13 17:36:16 +02:00
|
|
|
.visit(decl::<ast::FnDef<_>>)
|
|
|
|
.visit(decl::<ast::StructDef<_>>)
|
2018-08-14 13:45:56 +02:00
|
|
|
.visit(decl::<ast::NamedField<_>>)
|
2018-08-13 17:36:16 +02:00
|
|
|
.visit(decl::<ast::EnumDef<_>>)
|
|
|
|
.visit(decl::<ast::TraitDef<_>>)
|
2018-08-11 11:28:59 +02:00
|
|
|
.visit(decl::<ast::Module<_>>)
|
2018-08-13 17:36:16 +02:00
|
|
|
.visit(decl::<ast::TypeDef<_>>)
|
|
|
|
.visit(decl::<ast::ConstDef<_>>)
|
|
|
|
.visit(decl::<ast::StaticDef<_>>)
|
2018-08-14 10:20:09 +02:00
|
|
|
.visit(|im: ast::ImplItem<_>| {
|
2018-08-14 11:38:20 +02:00
|
|
|
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(),
|
|
|
|
),
|
|
|
|
};
|
2018-08-14 10:20:09 +02:00
|
|
|
|
|
|
|
let node = StructureNode {
|
|
|
|
parent: None,
|
|
|
|
label,
|
2018-08-14 11:38:20 +02:00
|
|
|
navigation_range: target_type.syntax().range(),
|
2018-08-14 10:20:09 +02:00
|
|
|
node_range: im.syntax().range(),
|
|
|
|
kind: im.syntax().kind(),
|
|
|
|
};
|
|
|
|
Some(node)
|
|
|
|
})
|
2018-08-11 11:28:59 +02:00
|
|
|
.accept(node)?
|
|
|
|
}
|