wdl_format/token/
pre.rs

1//! Tokens emitted during the formatting of particular elements.
2
3use std::rc::Rc;
4
5use wdl_ast::SyntaxKind;
6use wdl_ast::SyntaxTokenExt;
7
8use crate::Comment;
9use crate::Token;
10use crate::TokenStream;
11use crate::Trivia;
12use crate::TriviaBlankLineSpacingPolicy;
13
14/// A token that can be written by elements.
15///
16/// These are tokens that are intended to be written directly by elements to a
17/// [`TokenStream`](super::TokenStream) consisting of [`PreToken`]s. Note that
18/// this will transformed into a [`TokenStream`](super::TokenStream) of
19/// [`PostToken`](super::PostToken)s by a
20/// [`Postprocessor`](super::Postprocessor) (authors of elements are never
21/// expected to write [`PostToken`](super::PostToken)s directly).
22#[derive(Clone, Debug, Eq, PartialEq)]
23pub enum PreToken {
24    /// A blank line.
25    BlankLine,
26
27    /// The end of a line.
28    LineEnd,
29
30    /// The end of a word.
31    WordEnd,
32
33    /// The start of an indented block.
34    IndentStart,
35
36    /// The end of an indented block.
37    IndentEnd,
38
39    /// How to handle trivial blank lines from this point onwards.
40    LineSpacingPolicy(TriviaBlankLineSpacingPolicy),
41
42    /// Literal text.
43    Literal(Rc<String>, SyntaxKind),
44
45    /// Trivia.
46    Trivia(Trivia),
47
48    /// A temporary indent start. Used in command section formatting.
49    TempIndentStart,
50
51    /// A temporary indent end. Used in command section formatting.
52    TempIndentEnd,
53}
54
55impl std::fmt::Display for PreToken {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        match self {
58            PreToken::BlankLine => write!(f, "<BlankLine>"),
59            PreToken::LineEnd => write!(f, "<EndOfLine>"),
60            PreToken::WordEnd => write!(f, "<WordEnd>"),
61            PreToken::IndentStart => write!(f, "<IndentStart>"),
62            PreToken::IndentEnd => write!(f, "<IndentEnd>"),
63            PreToken::LineSpacingPolicy(policy) => {
64                write!(f, "<LineSpacingPolicy@{:?}>", policy)
65            }
66            PreToken::Literal(value, kind) => {
67                write!(f, "<Literal-{:?}@{}>", kind, value,)
68            }
69            PreToken::Trivia(trivia) => match trivia {
70                Trivia::BlankLine => {
71                    write!(f, "<OptionalBlankLine>")
72                }
73                Trivia::Comment(comment) => match comment {
74                    Comment::Preceding(value) => {
75                        write!(f, "<Comment-Preceding@{}>", value,)
76                    }
77                    Comment::Inline(value) => {
78                        write!(f, "<Comment-Inline@{}>", value,)
79                    }
80                },
81            },
82            PreToken::TempIndentStart => write!(f, "<TempIndentStart>"),
83            PreToken::TempIndentEnd => write!(f, "<TempIndentEnd>"),
84        }
85    }
86}
87
88impl Token for PreToken {
89    /// Returns a displayable version of the token.
90    fn display<'a>(&'a self, _config: &'a crate::Config) -> impl std::fmt::Display {
91        self
92    }
93}
94
95impl TokenStream<PreToken> {
96    /// Inserts a blank line token to the stream if the stream does not already
97    /// end with a blank line. This will replace any [`Trivia::BlankLine`]
98    /// tokens with [`PreToken::BlankLine`].
99    pub fn blank_line(&mut self) {
100        self.trim_while(|t| matches!(t, PreToken::BlankLine | PreToken::Trivia(Trivia::BlankLine)));
101        self.0.push(PreToken::BlankLine);
102    }
103
104    /// Inserts an end of line token to the stream if the stream does not
105    /// already end with an end of line token.
106    ///
107    /// This will also trim any trailing [`PreToken::WordEnd`] tokens.
108    pub fn end_line(&mut self) {
109        self.trim_while(|t| matches!(t, PreToken::WordEnd | PreToken::LineEnd));
110        self.0.push(PreToken::LineEnd);
111    }
112
113    /// Inserts a word end token to the stream if the stream does not already
114    /// end with a word end token.
115    pub fn end_word(&mut self) {
116        self.trim_end(&PreToken::WordEnd);
117        self.0.push(PreToken::WordEnd);
118    }
119
120    /// Inserts an indent start token to the stream. This will also end the
121    /// current line.
122    pub fn increment_indent(&mut self) {
123        self.end_line();
124        self.0.push(PreToken::IndentStart);
125    }
126
127    /// Inserts an indent end token to the stream. This will also end the
128    /// current line.
129    pub fn decrement_indent(&mut self) {
130        self.end_line();
131        self.0.push(PreToken::IndentEnd);
132    }
133
134    /// Inserts a trivial blank lines "always allowed" context change.
135    pub fn blank_lines_allowed(&mut self) {
136        self.0.push(PreToken::LineSpacingPolicy(
137            TriviaBlankLineSpacingPolicy::Always,
138        ));
139    }
140
141    /// Inserts a trivial blank lines "not allowed after comments" context
142    /// change.
143    pub fn blank_lines_allowed_between_comments(&mut self) {
144        self.0.push(PreToken::LineSpacingPolicy(
145            TriviaBlankLineSpacingPolicy::RemoveTrailingBlanks,
146        ));
147    }
148
149    /// Inserts any preceding trivia into the stream.
150    fn push_preceding_trivia(&mut self, token: &wdl_ast::Token) {
151        assert!(!token.inner().kind().is_trivia());
152        let preceding_trivia = token.inner().preceding_trivia();
153        for token in preceding_trivia {
154            match token.kind() {
155                SyntaxKind::Whitespace => {
156                    if !self.0.last().is_some_and(|t| {
157                        matches!(t, PreToken::BlankLine | PreToken::Trivia(Trivia::BlankLine))
158                    }) {
159                        self.0.push(PreToken::Trivia(Trivia::BlankLine));
160                    }
161                }
162                SyntaxKind::Comment => {
163                    let comment = PreToken::Trivia(Trivia::Comment(Comment::Preceding(Rc::new(
164                        token.text().trim_end().to_owned(),
165                    ))));
166                    self.0.push(comment);
167                }
168                _ => unreachable!("unexpected trivia: {:?}", token),
169            };
170        }
171    }
172
173    /// Inserts any inline trivia into the stream.
174    fn push_inline_trivia(&mut self, token: &wdl_ast::Token) {
175        assert!(!token.inner().kind().is_trivia());
176        if let Some(token) = token.inner().inline_comment() {
177            let inline_comment = PreToken::Trivia(Trivia::Comment(Comment::Inline(Rc::new(
178                token.text().trim_end().to_owned(),
179            ))));
180            self.0.push(inline_comment);
181        }
182    }
183
184    /// Pushes an AST token into the stream.
185    ///
186    /// This will also push any preceding or inline trivia into the stream.
187    /// Any token may have preceding or inline trivia, unless that token is
188    /// itself trivia (i.e. trivia cannot have trivia).
189    pub fn push_ast_token(&mut self, token: &wdl_ast::Token) {
190        self.push_preceding_trivia(token);
191        self.0.push(PreToken::Literal(
192            Rc::new(token.inner().text().to_owned()),
193            token.inner().kind(),
194        ));
195        self.push_inline_trivia(token);
196    }
197
198    /// Pushes a literal string into the stream in place of an AST token.
199    /// This will insert any trivia that would have been inserted with the AST
200    /// token.
201    pub fn push_literal_in_place_of_token(&mut self, token: &wdl_ast::Token, replacement: String) {
202        self.push_preceding_trivia(token);
203        self.0.push(PreToken::Literal(
204            Rc::new(replacement),
205            token.inner().kind(),
206        ));
207        self.push_inline_trivia(token);
208    }
209
210    /// Pushes a literal string into the stream.
211    /// This will not insert any trivia.
212    pub fn push_literal(&mut self, value: String, kind: SyntaxKind) {
213        self.0.push(PreToken::Literal(Rc::new(value), kind));
214    }
215
216    /// Returns the kind of the last literal token in the stream.
217    pub fn last_literal_kind(&self) -> Option<SyntaxKind> {
218        match self.0.last_chunk::<3>() {
219            Some([_, _, PreToken::Literal(_, kind)]) => Some(*kind),
220            Some([_, PreToken::Literal(_, kind), _]) => Some(*kind),
221            Some([PreToken::Literal(_, kind), _, _]) => Some(*kind),
222            _ => None,
223        }
224    }
225}