1use std::collections::HashSet;
4use std::rc::Rc;
5
6use wdl_ast::DOC_COMMENT_PREFIX;
7use wdl_ast::Directive;
8use wdl_ast::SyntaxKind;
9use wdl_ast::SyntaxTokenExt;
10
11use crate::Comment;
12use crate::NEWLINE;
13use crate::Token;
14use crate::TokenStream;
15use crate::Trivia;
16use crate::TriviaBlankLineSpacingPolicy;
17
18#[derive(Clone, Debug, Eq, PartialEq)]
27pub enum PreToken {
28 BlankLine,
33
34 LineEnd,
36
37 WordEnd,
39
40 IndentStart,
42
43 IndentEnd,
45
46 LineSpacingPolicy(TriviaBlankLineSpacingPolicy),
48
49 Literal(Rc<String>, SyntaxKind),
51
52 Trivia(Trivia),
54
55 TempIndentStart(Rc<String>),
61
62 TempIndentEnd,
66}
67
68impl std::fmt::Display for PreToken {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 match self {
71 PreToken::BlankLine => write!(f, "<BlankLine>"),
72 PreToken::LineEnd => write!(f, "<EndOfLine>"),
73 PreToken::WordEnd => write!(f, "<WordEnd>"),
74 PreToken::IndentStart => write!(f, "<IndentStart>"),
75 PreToken::IndentEnd => write!(f, "<IndentEnd>"),
76 PreToken::LineSpacingPolicy(policy) => {
77 write!(f, "<LineSpacingPolicy@{policy:?}>")
78 }
79 PreToken::Literal(value, kind) => {
80 write!(f, "<Literal-{kind:?}@{value}>")
81 }
82 PreToken::Trivia(trivia) => match trivia {
83 Trivia::BlankLine => {
84 write!(f, "<OptionalBlankLine>")
85 }
86 Trivia::Comment(comment) => match comment {
87 Comment::Directive(directive) => {
88 write!(f, "<Comment-Directive@{directive:?}>")
89 }
90 Comment::Documentation(documentation) => {
91 write!(f, "<Comment-Documentation@{documentation}>")
92 }
93 Comment::Preceding(value) => {
94 write!(f, "<Comment-Preceding@{value}>")
95 }
96 Comment::Inline(value) => {
97 write!(f, "<Comment-Inline@{value}>")
98 }
99 },
100 },
101 PreToken::TempIndentStart(value) => write!(f, "<TempIndentStart@{value}>"),
102 PreToken::TempIndentEnd => write!(f, "<TempIndentEnd>"),
103 }
104 }
105}
106
107impl Token for PreToken {
108 fn display<'a>(&'a self, _config: &'a crate::Config) -> impl std::fmt::Display {
110 self
111 }
112}
113
114impl TokenStream<PreToken> {
115 pub fn blank_line(&mut self) {
119 self.trim_while(|t| matches!(t, PreToken::BlankLine | PreToken::Trivia(Trivia::BlankLine)));
120 self.0.push(PreToken::BlankLine);
121 }
122
123 pub fn end_line(&mut self) {
128 self.trim_while(|t| matches!(t, PreToken::WordEnd | PreToken::LineEnd));
129 self.0.push(PreToken::LineEnd);
130 }
131
132 pub fn end_word(&mut self) {
135 self.trim_end(&PreToken::WordEnd);
136 self.0.push(PreToken::WordEnd);
137 }
138
139 pub fn increment_indent(&mut self) {
142 self.end_line();
143 self.0.push(PreToken::IndentStart);
144 }
145
146 pub fn decrement_indent(&mut self) {
149 self.end_line();
150 self.0.push(PreToken::IndentEnd);
151 }
152
153 pub fn allow_blank_lines(&mut self) {
155 self.0.push(PreToken::LineSpacingPolicy(
156 TriviaBlankLineSpacingPolicy::Always,
157 ));
158 }
159
160 pub fn ignore_trailing_blank_lines(&mut self) {
163 self.0.push(PreToken::LineSpacingPolicy(
164 TriviaBlankLineSpacingPolicy::RemoveTrailingBlanks,
165 ));
166 }
167
168 fn push_preceding_trivia(&mut self, token: &wdl_ast::Token) {
178 assert!(!token.inner().kind().is_trivia());
179 let preceding_trivia = token.inner().preceding_trivia();
180 let mut documentation = String::new();
181 let mut trivia = Vec::new();
182 let mut exceptions = HashSet::new();
183 for token in preceding_trivia {
184 match token.kind() {
185 SyntaxKind::Whitespace => {
186 if !self.0.last().is_some_and(|t| {
187 matches!(t, PreToken::BlankLine | PreToken::Trivia(Trivia::BlankLine))
188 }) {
189 trivia.push(PreToken::Trivia(Trivia::BlankLine));
190 }
191 }
192 SyntaxKind::Comment => {
193 if let Some(t) = token.text().strip_prefix(DOC_COMMENT_PREFIX) {
194 documentation.push_str(t);
197 documentation.push_str(NEWLINE);
198 } else if let Ok(directive) = token.text().parse::<Directive>() {
199 match directive {
200 Directive::Except(e) => exceptions.extend(e),
201 }
202 } else {
203 let comment = PreToken::Trivia(Trivia::Comment(Comment::Preceding(
204 Rc::new(token.text().trim_end().to_string()),
205 )));
206 trivia.push(comment);
207 }
208 }
209 _ => unreachable!("unexpected trivia: {:?}", token),
210 };
211 }
212
213 let mut trivia = trivia.into_iter().peekable();
214 if let Some(PreToken::Trivia(Trivia::BlankLine)) = trivia.peek() {
216 self.0.push(trivia.next().unwrap());
217 }
218 let mut docs_present = false;
219 if !documentation.is_empty() {
220 docs_present = true;
221 let comment = PreToken::Trivia(Trivia::Comment(Comment::Documentation(Rc::new(
222 documentation,
223 ))));
224 self.0.push(comment);
225
226 if let Some(PreToken::Trivia(Trivia::BlankLine)) = trivia.peek() {
228 let _ = trivia.next();
229 }
230 }
231 for token in trivia {
232 self.0.push(token);
233 }
234 if docs_present && let Some(PreToken::Trivia(Trivia::BlankLine)) = self.0.last() {
235 self.0.pop();
237 }
238 if !exceptions.is_empty() {
239 let comment = PreToken::Trivia(Trivia::Comment(Comment::Directive(Rc::new(
240 Directive::Except(exceptions),
241 ))));
242 self.0.push(comment);
243 }
244 }
245
246 fn push_inline_trivia(&mut self, token: &wdl_ast::Token) {
253 assert!(!token.inner().kind().is_trivia());
254 if let Some(token) = token.inner().inline_comment() {
255 let inline_comment = PreToken::Trivia(Trivia::Comment(Comment::Inline(Rc::new(
256 token.text().trim_end().to_owned(),
257 ))));
258 self.0.push(inline_comment);
259 }
260 }
261
262 pub fn push_ast_token(&mut self, token: &wdl_ast::Token) {
272 self.push_preceding_trivia(token);
273 self.0.push(PreToken::Literal(
274 Rc::new(token.inner().text().to_owned()),
275 token.inner().kind(),
276 ));
277 self.push_inline_trivia(token);
278 }
279
280 pub fn push_literal_in_place_of_token(&mut self, token: &wdl_ast::Token, replacement: String) {
289 self.push_preceding_trivia(token);
290 self.0.push(PreToken::Literal(
291 Rc::new(replacement),
292 token.inner().kind(),
293 ));
294 self.push_inline_trivia(token);
295 }
296
297 pub fn push_literal(&mut self, value: String, kind: SyntaxKind) {
301 self.0.push(PreToken::Literal(Rc::new(value), kind));
302 }
303}