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