1use 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#[derive(Clone, Debug, Eq, PartialEq)]
23pub enum PreToken {
24 BlankLine,
26
27 LineEnd,
29
30 WordEnd,
32
33 IndentStart,
35
36 IndentEnd,
38
39 LineSpacingPolicy(TriviaBlankLineSpacingPolicy),
41
42 Literal(Rc<String>, SyntaxKind),
44
45 Trivia(Trivia),
47
48 TempIndentStart,
50
51 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 fn display<'a>(&'a self, _config: &'a crate::Config) -> impl std::fmt::Display {
91 self
92 }
93}
94
95impl TokenStream<PreToken> {
96 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 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 pub fn end_word(&mut self) {
116 self.trim_end(&PreToken::WordEnd);
117 self.0.push(PreToken::WordEnd);
118 }
119
120 pub fn increment_indent(&mut self) {
123 self.end_line();
124 self.0.push(PreToken::IndentStart);
125 }
126
127 pub fn decrement_indent(&mut self) {
130 self.end_line();
131 self.0.push(PreToken::IndentEnd);
132 }
133
134 pub fn blank_lines_allowed(&mut self) {
136 self.0.push(PreToken::LineSpacingPolicy(
137 TriviaBlankLineSpacingPolicy::Always,
138 ));
139 }
140
141 pub fn blank_lines_allowed_between_comments(&mut self) {
144 self.0.push(PreToken::LineSpacingPolicy(
145 TriviaBlankLineSpacingPolicy::RemoveTrailingBlanks,
146 ));
147 }
148
149 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 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 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 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 pub fn push_literal(&mut self, value: String, kind: SyntaxKind) {
213 self.0.push(PreToken::Literal(Rc::new(value), kind));
214 }
215
216 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}