sway_fmt/
code_builder_helpers.rs

1use super::code_line::CodeLine;
2use crate::constants::{ALREADY_FORMATTED_LINE_PATTERN, NEW_LINE_PATTERN};
3use std::{
4    iter::{Enumerate, Peekable},
5    str::Chars,
6};
7
8pub fn is_comment(line: &str) -> bool {
9    let mut chars = line.trim().chars();
10    chars.next() == Some('/') && chars.next() == Some('/')
11}
12
13pub fn is_else_statement_next(line: &str) -> bool {
14    let trimmed = line.trim();
15    trimmed.len() >= 4 && &trimmed[0..4] == "else"
16}
17
18pub fn is_multiline_comment(line: &str) -> bool {
19    let mut chars = line.trim().chars();
20    chars.next() == Some('/') && chars.next() == Some('*')
21}
22
23/// checks for newline only, ignores an empty space
24pub fn is_newline_incoming(line: &str) -> bool {
25    let chars = line.chars();
26
27    for c in chars {
28        match c {
29            '\n' => return true,
30            ' ' => {}
31            _ => return false,
32        }
33    }
34
35    false
36}
37
38pub fn handle_multiline_comment_case(
39    code_line: &mut CodeLine,
40    current_char: char,
41    iter: &mut Peekable<Enumerate<Chars>>,
42) {
43    code_line.push_char(current_char);
44
45    if current_char == '*' {
46        // end multiline comment and reset to default type
47        if let Some((_, '/')) = iter.peek() {
48            code_line.push_char('/');
49            iter.next();
50            code_line.become_default();
51        }
52    }
53}
54
55// if it's a string just keep pushing the characters
56pub fn handle_string_case(code_line: &mut CodeLine, current_char: char) {
57    code_line.push_char(current_char);
58    if current_char == '"' {
59        let previous_char = code_line.text.chars().last();
60        // end of the string
61        if previous_char != Some('\\') {
62            code_line.become_default();
63        }
64    }
65}
66
67pub fn handle_logical_not_case(code_line: &mut CodeLine, iter: &mut Peekable<Enumerate<Chars>>) {
68    code_line.push_char('!');
69    clean_all_whitespace(iter);
70}
71
72pub fn handle_whitespace_case(code_line: &mut CodeLine, iter: &mut Peekable<Enumerate<Chars>>) {
73    clean_all_whitespace(iter);
74
75    if let Some((_, next_char)) = iter.peek() {
76        let next_char = *next_char;
77
78        match next_char {
79            '(' | ';' | ':' | ')' | ',' | '}' => {} // do nothing, handle it in next turn
80            _ => {
81                // add whitespace if it is not already there
82                code_line.append_whitespace();
83            }
84        }
85    }
86}
87
88pub fn handle_assignment_case(code_line: &mut CodeLine, iter: &mut Peekable<Enumerate<Chars>>) {
89    if let Some((_, next_char)) = iter.peek() {
90        let next_char = *next_char;
91        if next_char == '=' {
92            // it's equality operator
93            code_line.append_with_whitespace("== ");
94            iter.next();
95        } else if next_char == '>' {
96            // it's fat arrow
97            code_line.append_with_whitespace("=> ");
98            iter.next();
99        } else {
100            code_line.append_equal_sign();
101        }
102    } else {
103        code_line.append_with_whitespace("= ");
104    }
105}
106
107pub fn handle_plus_case(code_line: &mut CodeLine, iter: &mut Peekable<Enumerate<Chars>>) {
108    if let Some((_, next_char)) = iter.peek() {
109        let next_char = *next_char;
110        if next_char == '=' {
111            // it's a += operator
112            code_line.append_with_whitespace("+= ");
113            iter.next();
114        } else {
115            code_line.append_with_whitespace("+ ");
116        }
117    } else {
118        code_line.append_with_whitespace("+ ");
119    }
120}
121
122pub fn handle_colon_case(code_line: &mut CodeLine, iter: &mut Peekable<Enumerate<Chars>>) {
123    if let Some((_, next_char)) = iter.peek() {
124        let next_char = *next_char;
125        if next_char == ':' {
126            // it's :: operator
127            code_line.push_str("::");
128            iter.next();
129        } else {
130            code_line.push_str(": ");
131        }
132    } else {
133        code_line.push_str(": ");
134    }
135}
136
137pub fn handle_dash_case(code_line: &mut CodeLine, iter: &mut Peekable<Enumerate<Chars>>) {
138    if let Some((_, next_char)) = iter.peek() {
139        if *next_char == '>' {
140            // it's a return arrow
141            code_line.append_with_whitespace("-> ");
142            iter.next();
143        } else if *next_char == '=' {
144            // it's a -= operator
145            code_line.append_with_whitespace("-= ");
146            iter.next();
147        } else {
148            // it's just a single '-'
149            code_line.append_with_whitespace("- ");
150        }
151    } else {
152        code_line.append_with_whitespace("- ");
153    }
154}
155
156pub fn handle_multiply_case(code_line: &mut CodeLine, iter: &mut Peekable<Enumerate<Chars>>) {
157    if let Some((_, next_char)) = iter.peek() {
158        let next_char = *next_char;
159        if next_char == '=' {
160            // it's a *= operator
161            code_line.append_with_whitespace("*= ");
162            iter.next();
163        } else {
164            code_line.append_with_whitespace("* ");
165        }
166    } else {
167        code_line.append_with_whitespace("* ");
168    }
169}
170
171pub fn handle_pipe_case(code_line: &mut CodeLine, iter: &mut Peekable<Enumerate<Chars>>) {
172    if let Some((_, next_char)) = iter.peek() {
173        if *next_char == '|' {
174            // it's OR operator
175            code_line.append_with_whitespace("|| ");
176            iter.next();
177        } else {
178            // it's just a single '|'
179            code_line.append_with_whitespace("| ");
180        }
181    } else {
182        code_line.append_with_whitespace("| ");
183    }
184}
185
186pub fn handle_forward_slash_case(code_line: &mut CodeLine, iter: &mut Peekable<Enumerate<Chars>>) {
187    // Handles non-comment related /.
188    if let Some((_, next_char)) = iter.peek() {
189        let next_char = *next_char;
190        if next_char == '=' {
191            // it's a /= operator
192            code_line.append_with_whitespace("/= ");
193            iter.next();
194        } else {
195            code_line.append_with_whitespace("/ ");
196        }
197    } else {
198        code_line.append_with_whitespace("/ ");
199    }
200}
201
202pub fn handle_ampersand_case(code_line: &mut CodeLine, iter: &mut Peekable<Enumerate<Chars>>) {
203    if let Some((_, next_char)) = iter.peek() {
204        if *next_char == '&' {
205            // it's AND operator
206            code_line.append_with_whitespace("&& ");
207            iter.next();
208        } else {
209            // it's just a single '&'
210            code_line.append_with_whitespace("& ");
211        }
212    } else {
213        code_line.append_with_whitespace("& ");
214    }
215}
216
217/// cleans whitespace, including newlines
218pub fn clean_all_whitespace(iter: &mut Peekable<Enumerate<Chars>>) {
219    while let Some((_, next_char)) = iter.peek() {
220        if next_char.is_whitespace() {
221            iter.next();
222        } else {
223            break;
224        }
225    }
226}
227
228/// checks does next part of the line contain "add new line" pattern,
229/// if it does it returns the rest of the line
230pub fn get_new_line_pattern(line: &str) -> Option<&str> {
231    let pattern_len = NEW_LINE_PATTERN.len();
232
233    if line.len() >= pattern_len && &line[0..pattern_len] == NEW_LINE_PATTERN {
234        return Some(&line[pattern_len..]);
235    }
236
237    None
238}
239
240/// checks does beginning of the new line contain "already formatted" pattern
241/// if it does it splits and returns the already formatted line and the rest after it
242pub fn get_already_formatted_line_pattern(line: &str) -> Option<(&str, &str)> {
243    let pattern_len = ALREADY_FORMATTED_LINE_PATTERN.len();
244
245    if line.starts_with(ALREADY_FORMATTED_LINE_PATTERN) {
246        let char_idxs = vec![
247            line.find(';').unwrap_or(0),
248            line.rfind(',').unwrap_or(0),
249            line.rfind('}').unwrap_or(0),
250            line.rfind('{').unwrap_or(0),
251        ];
252
253        let end = char_idxs.iter().max().unwrap();
254
255        let formatted_line = &line[pattern_len..end + 1];
256        // rest, if any
257        let rest = &line[end + 1..];
258
259        return Some((formatted_line, rest));
260    }
261
262    None
263}