Skip to main content

badness_parser/ast/
tokens.rs

1//! Typed [`AstToken`] wrappers over CST *tokens*. Only [`ControlWord`] exists
2//! today — the rest of the lexer's tokens (`L_BRACE`, `WORD`, trivia, …) are
3//! matched raw by the formatter's token loops, which is idiomatic and should stay
4//! that way. Add a wrapper here only when a token grows a named accessor consumer.
5
6use rowan::TextRange;
7
8use super::AstToken;
9use crate::syntax::{SyntaxKind, SyntaxToken};
10
11/// The `CONTROL_WORD` token leading a `COMMAND` node — `\foo` (backslash + ASCII
12/// letters). Carries the command's name and its precise range.
13#[derive(Debug, Clone, PartialEq, Eq, Hash)]
14pub struct ControlWord {
15    syntax: SyntaxToken,
16}
17
18impl AstToken for ControlWord {
19    fn can_cast(kind: SyntaxKind) -> bool {
20        kind == SyntaxKind::CONTROL_WORD
21    }
22
23    fn cast(syntax: SyntaxToken) -> Option<Self> {
24        Self::can_cast(syntax.kind()).then_some(Self { syntax })
25    }
26
27    fn syntax(&self) -> &SyntaxToken {
28        &self.syntax
29    }
30}
31
32impl ControlWord {
33    /// The control-word name with the leading `\` stripped (`section` for
34    /// `\section`).
35    pub fn name(&self) -> String {
36        self.syntax.text().trim_start_matches('\\').to_string()
37    }
38
39    /// The byte range of the `\foo` token, backslash included.
40    pub fn range(&self) -> TextRange {
41        self.syntax.text_range()
42    }
43}