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) -> &str {
36 self.syntax.text().trim_start_matches('\\')
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}