use rowan::{NodeOrToken, TextRange, TextSize};
use super::{AstNode, AstToken, child, children};
use crate::ast::tokens::ControlWord;
use crate::syntax::{SyntaxKind, SyntaxNode};
macro_rules! ast_node {
($(#[$meta:meta])* $name:ident, $kind:ident) => {
$(#[$meta])*
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct $name {
syntax: SyntaxNode,
}
impl AstNode for $name {
fn can_cast(kind: SyntaxKind) -> bool {
kind == SyntaxKind::$kind
}
fn cast(syntax: SyntaxNode) -> Option<Self> {
Self::can_cast(syntax.kind()).then_some(Self { syntax })
}
fn syntax(&self) -> &SyntaxNode {
&self.syntax
}
}
};
}
ast_node!(
Command, COMMAND
);
ast_node!(
Group, GROUP
);
ast_node!(
Optional, OPTIONAL
);
ast_node!(
NameGroup, NAME_GROUP
);
ast_node!(
Begin, BEGIN
);
ast_node!(
End, END
);
ast_node!(
Environment, ENVIRONMENT
);
impl Command {
pub fn control_word(&self) -> Option<ControlWord> {
self.syntax
.children_with_tokens()
.filter_map(NodeOrToken::into_token)
.find_map(ControlWord::cast)
}
pub fn name(&self) -> Option<String> {
self.control_word().map(|cw| cw.name())
}
pub fn control_word_range(&self) -> Option<TextRange> {
self.control_word().map(|cw| cw.range())
}
pub fn nth_group(&self, n: usize) -> Option<Group> {
self.groups().nth(n)
}
pub fn groups(&self) -> impl Iterator<Item = Group> {
children::<Group>(&self.syntax)
}
pub fn optionals(&self) -> impl Iterator<Item = Optional> {
children::<Optional>(&self.syntax)
}
pub fn nth_group_text(&self, n: usize) -> Option<String> {
self.nth_group(n)?.inner_text()
}
pub fn nth_group_inner(&self, n: usize) -> Option<(TextRange, String)> {
self.nth_group(n)?.inner()
}
pub fn first_group_range(&self) -> TextRange {
match self.nth_group(0) {
Some(group) => TextRange::new(
self.syntax.text_range().start(),
group.syntax.text_range().end(),
),
None => self.syntax.text_range(),
}
}
}
impl Group {
pub fn inner_text(&self) -> Option<String> {
let mut text = String::new();
for element in self.syntax.children_with_tokens() {
match element {
NodeOrToken::Token(token) => match token.kind() {
SyntaxKind::L_BRACE | SyntaxKind::R_BRACE => {}
_ => text.push_str(token.text()),
},
NodeOrToken::Node(_) => return None,
}
}
Some(text)
}
pub fn inner(&self) -> Option<(TextRange, String)> {
let mut text = String::new();
let mut start: Option<TextSize> = None;
let mut end: Option<TextSize> = None;
let mut after_l_brace = self.syntax.text_range().start();
for element in self.syntax.children_with_tokens() {
match element {
NodeOrToken::Token(token) => match token.kind() {
SyntaxKind::L_BRACE => after_l_brace = token.text_range().end(),
SyntaxKind::R_BRACE => {}
_ => {
let range = token.text_range();
start.get_or_insert(range.start());
end = Some(range.end());
text.push_str(token.text());
}
},
NodeOrToken::Node(_) => return None,
}
}
let range = match (start, end) {
(Some(start), Some(end)) => TextRange::new(start, end),
_ => TextRange::empty(after_l_brace),
};
Some((range, text))
}
pub fn inner_source(&self) -> String {
inner_source_of(&self.syntax)
}
pub fn command(&self) -> Option<Command> {
child::<Command>(&self.syntax)
}
pub fn command_name(&self) -> Option<String> {
self.command()?.name()
}
}
pub(crate) fn inner_source_of(node: &SyntaxNode) -> String {
let mut text = String::new();
for element in node.descendants_with_tokens() {
if let NodeOrToken::Token(token) = element {
text.push_str(token.text());
}
}
let inner = text.strip_prefix('{').unwrap_or(&text);
inner.strip_suffix('}').unwrap_or(inner).to_string()
}
impl NameGroup {
pub fn text(&self) -> Option<String> {
let mut text = String::new();
for element in self.syntax.children_with_tokens() {
match element {
NodeOrToken::Token(token) => match token.kind() {
SyntaxKind::L_BRACE | SyntaxKind::R_BRACE => {}
_ => text.push_str(token.text()),
},
NodeOrToken::Node(_) => return None,
}
}
Some(text)
}
pub fn range(&self) -> Option<TextRange> {
let mut start: Option<TextSize> = None;
let mut end: Option<TextSize> = None;
for element in self.syntax.children_with_tokens() {
match element {
NodeOrToken::Token(token) => match token.kind() {
SyntaxKind::L_BRACE | SyntaxKind::R_BRACE => {}
_ => {
let range = token.text_range();
start.get_or_insert(range.start());
end = Some(range.end());
}
},
NodeOrToken::Node(_) => return None,
}
}
Some(TextRange::new(start?, end?))
}
}
impl Begin {
pub fn name_group(&self) -> Option<NameGroup> {
child::<NameGroup>(&self.syntax)
}
pub fn name(&self) -> Option<String> {
self.name_group()?.text()
}
pub fn name_range(&self) -> Option<TextRange> {
self.name_group()?.range()
}
}
impl End {
pub fn name_group(&self) -> Option<NameGroup> {
child::<NameGroup>(&self.syntax)
}
pub fn name(&self) -> Option<String> {
self.name_group()?.text()
}
pub fn name_range(&self) -> Option<TextRange> {
self.name_group()?.range()
}
}
impl Environment {
pub fn begin(&self) -> Option<Begin> {
child::<Begin>(&self.syntax)
}
pub fn end(&self) -> Option<End> {
child::<End>(&self.syntax)
}
pub fn name(&self) -> Option<String> {
self.begin()?.name()
}
}