1use std::collections::HashSet;
4use std::rc::Rc;
5
6use wdl_ast::AstToken;
7use wdl_ast::DOC_COMMENT_PREFIX;
8use wdl_ast::Directive;
9use wdl_ast::SyntaxKind;
10use wdl_ast::SyntaxTokenExt;
11
12use crate::Comment;
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('\n');
198 } else if let Some(comment) = wdl_ast::Comment::cast(token.clone())
199 && let Some(directive) = comment.directive()
200 {
201 match directive {
202 Directive::Except(e) => exceptions.extend(e),
203 }
204 } else {
205 let comment = PreToken::Trivia(Trivia::Comment(Comment::Preceding(
206 Rc::new(token.text().trim_end().to_string()),
207 )));
208 trivia.push(comment);
209 }
210 }
211 _ => unreachable!("unexpected trivia: {:?}", token),
212 };
213 }
214
215 let mut trivia = trivia.into_iter().peekable();
216 if let Some(PreToken::Trivia(Trivia::BlankLine)) = trivia.peek() {
218 self.0.push(trivia.next().unwrap());
219 }
220 let mut docs_present = false;
221 if !documentation.is_empty() {
222 docs_present = true;
223 let comment = PreToken::Trivia(Trivia::Comment(Comment::Documentation(Rc::new(
224 documentation,
225 ))));
226 self.0.push(comment);
227
228 if let Some(PreToken::Trivia(Trivia::BlankLine)) = trivia.peek() {
230 let _ = trivia.next();
231 }
232 }
233 for token in trivia {
234 self.0.push(token);
235 }
236 if docs_present && let Some(PreToken::Trivia(Trivia::BlankLine)) = self.0.last() {
237 self.0.pop();
239 }
240 if !exceptions.is_empty() {
241 let comment = PreToken::Trivia(Trivia::Comment(Comment::Directive(Rc::new(
242 Directive::Except(exceptions),
243 ))));
244 self.0.push(comment);
245 }
246 }
247
248 fn push_inline_trivia(&mut self, token: &wdl_ast::Token) {
255 assert!(!token.inner().kind().is_trivia());
256 if let Some(token) = token.inner().inline_comment() {
257 let inline_comment = PreToken::Trivia(Trivia::Comment(Comment::Inline(Rc::new(
258 token.text().trim_end().to_owned(),
259 ))));
260 self.0.push(inline_comment);
261 }
262 }
263
264 pub fn push_ast_token(&mut self, token: &wdl_ast::Token) {
274 self.push_preceding_trivia(token);
275 self.0.push(PreToken::Literal(
276 Rc::new(token.inner().text().to_owned()),
277 token.inner().kind(),
278 ));
279 self.push_inline_trivia(token);
280 }
281
282 pub fn push_literal_in_place_of_token(&mut self, token: &wdl_ast::Token, replacement: String) {
291 self.push_preceding_trivia(token);
292 self.0.push(PreToken::Literal(
293 Rc::new(replacement),
294 token.inner().kind(),
295 ));
296 self.push_inline_trivia(token);
297 }
298
299 pub fn push_literal(&mut self, value: String, kind: SyntaxKind) {
303 self.0.push(PreToken::Literal(Rc::new(value), kind));
304 }
305}