dprint_core/formatting/ir_helpers/
helpers.rs1use std::rc::Rc;
2
3use crate::formatting::actions;
4use crate::formatting::condition_helpers;
5
6use super::super::conditions;
7use super::super::print_items::*;
8
9pub fn surround_with_new_lines(item: PrintItems) -> PrintItems {
10 if item.is_empty() {
11 return item;
12 }
13
14 let mut items = PrintItems::new();
15 items.push_signal(Signal::NewLine);
16 items.extend(item);
17 items.push_signal(Signal::NewLine);
18 items
19}
20
21pub fn with_indent(item: PrintItems) -> PrintItems {
22 with_indent_times(item, 1)
23}
24
25pub fn with_queued_indent(item: PrintItems) -> PrintItems {
26 if item.is_empty() {
27 return item;
28 }
29
30 let mut items = PrintItems::new();
31 items.push_signal(Signal::QueueStartIndent);
32 items.extend(item);
33 items.push_signal(Signal::FinishIndent);
34 items
35}
36
37pub fn with_indent_times(item: PrintItems, times: u32) -> PrintItems {
38 if item.is_empty() {
39 return item;
40 }
41
42 let mut items = PrintItems::new();
43 for _ in 0..times {
44 items.push_signal(Signal::StartIndent);
45 }
46 items.extend(item);
47 for _ in 0..times {
48 items.push_signal(Signal::FinishIndent);
49 }
50 items
51}
52
53pub fn with_no_new_lines(item: PrintItems) -> PrintItems {
54 if item.is_empty() {
55 return item;
56 }
57
58 let mut items = PrintItems::new();
59 items.push_signal(Signal::StartForceNoNewLines);
60 items.extend(item);
61 items.push_signal(Signal::FinishForceNoNewLines);
62 items
63}
64
65pub fn new_line_group(item: PrintItems) -> PrintItems {
66 if item.is_empty() {
67 return item;
68 }
69
70 let mut items = PrintItems::new();
71 items.push_signal(Signal::StartNewLineGroup);
72 items.extend(item);
73 items.push_signal(Signal::FinishNewLineGroup);
74 items
75}
76
77pub fn gen_from_raw_string(text: &str) -> PrintItems {
79 gen_from_raw_string_lines(text, gen_from_string_line)
80}
81
82pub fn gen_from_raw_string_trim_line_ends(text: &str) -> PrintItems {
84 gen_from_raw_string_lines(text, |line_text| gen_from_string_line(line_text.trim_end()))
85}
86
87fn gen_from_raw_string_lines(text: &str, gen_line: impl Fn(&str) -> PrintItems) -> PrintItems {
88 let has_newline = text.contains('\n');
89 let mut items = PrintItems::new();
90 if has_newline {
91 items.push_signal(Signal::StartIgnoringIndent);
92 items.extend(gen_string_lines(text, gen_line));
93 items.push_signal(Signal::FinishIgnoringIndent);
94 } else {
95 items.extend(gen_line(text));
96 }
97 items
98}
99
100pub fn gen_from_string(text: &str) -> PrintItems {
102 gen_string_lines(text, gen_from_string_line)
103}
104
105pub fn gen_from_string_trim_line_ends(text: &str) -> PrintItems {
107 gen_string_lines(text, |line_text| gen_from_string_line(line_text.trim_end()))
108}
109
110fn gen_string_lines(text: &str, gen_line: impl Fn(&str) -> PrintItems) -> PrintItems {
111 let mut items = PrintItems::new();
112
113 for (i, line) in text.lines().enumerate() {
114 if i > 0 {
115 items.push_signal(Signal::NewLine);
116 }
117
118 items.extend(gen_line(line));
119 }
120
121 if text.ends_with('\n') {
123 items.push_signal(Signal::NewLine)
124 }
125
126 items
127}
128
129fn gen_from_string_line(line: &str) -> PrintItems {
130 let mut items = PrintItems::new();
131 for (i, part) in line.split('\t').enumerate() {
132 if i > 0 {
133 items.push_signal(Signal::Tab);
134 }
135 if !part.is_empty() {
136 items.push_string(part.to_string());
137 }
138 }
139 items
140}
141
142pub fn surround_with_newlines_indented_if_multi_line(inner_items: PrintItems, indent_width: u8) -> PrintItems {
146 if inner_items.is_empty() {
147 return inner_items;
148 }
149
150 let mut items = PrintItems::new();
151 let start_name = "surroundWithNewLinesIndentedIfMultiLineStart";
152 let start_ln = LineNumber::new(start_name);
153 let end_ln = LineNumber::new("surroundWithNewLinesIndentedIfMultiLineEnd");
154 let inner_items = inner_items.into_rc_path();
155
156 items.push_info(start_ln);
157 items.push_anchor(LineNumberAnchor::new(end_ln));
158 items.extend(actions::if_column_number_changes(move |context| {
159 context.clear_info(end_ln);
160 }));
161 let mut condition = Condition::new(
162 "newlineIfMultiLine",
163 ConditionProperties {
164 true_path: Some(surround_with_new_lines(with_indent(inner_items.into()))),
165 false_path: Some({
166 let mut items = PrintItems::new();
167 items.push_condition(conditions::if_above_width(indent_width, Signal::PossibleNewLine.into()));
168 items.extend(inner_items.into());
169 items
170 }),
171 condition: Rc::new(move |context| condition_helpers::is_multiple_lines(context, start_ln, end_ln)),
172 },
173 );
174 let condition_reevaluation = condition.create_reevaluation();
175 items.push_condition(condition);
176 items.push_info(end_ln);
177 items.push_reevaluation(condition_reevaluation);
178
179 items
180}
181
182pub fn gen_js_like_comment_line(text: &str, force_space_after_slashes: bool) -> PrintItems {
184 let mut items = PrintItems::new();
185 items.extend(gen_from_raw_string(&get_comment_text(text, force_space_after_slashes)));
186 items.push_signal(Signal::ExpectNewLine);
187 return with_no_new_lines(items);
188
189 fn get_comment_text(original_text: &str, force_space_after_slashes: bool) -> String {
190 let non_slash_index = get_first_non_slash_index(original_text);
191 let skip_space = force_space_after_slashes && original_text.chars().nth(non_slash_index) == Some(' ');
192 let start_text_index = if skip_space { non_slash_index + 1 } else { non_slash_index };
193 let comment_text_original = &original_text[start_text_index..]; let comment_text = comment_text_original.trim_end();
195 let prefix = format!("//{}", original_text.chars().take(non_slash_index).collect::<String>());
196
197 return if comment_text.is_empty() {
198 prefix
199 } else {
200 format!("{}{}{}", prefix, if force_space_after_slashes { " " } else { "" }, comment_text)
201 };
202
203 fn get_first_non_slash_index(text: &str) -> usize {
204 let mut i: usize = 0;
205 for c in text.chars() {
206 if c != '/' {
207 return i;
208 }
209 i += 1;
210 }
211
212 i
213 }
214 }
215}
216
217pub fn gen_js_like_comment_block(text: &str) -> PrintItems {
219 const LEADING_STAR_SLASH: StringContainer = StringContainer::proc_macro_new_with_char_count("/*", 2);
221 const TRAILING_STAR_SLASH: StringContainer = StringContainer::proc_macro_new_with_char_count("*/", 2);
222
223 let mut items = PrintItems::new();
224 let add_ignore_indent = text.contains('\n');
225 let last_line_trailing_whitespace = get_last_line_trailing_whitespace(text);
226
227 items.push_sc(&LEADING_STAR_SLASH);
228 if add_ignore_indent {
229 items.push_signal(Signal::StartIgnoringIndent);
230 }
231 items.extend(gen_from_string_trim_line_ends(text));
232
233 if !last_line_trailing_whitespace.is_empty() {
235 items.extend(gen_from_raw_string(last_line_trailing_whitespace));
236 }
237
238 if add_ignore_indent {
239 items.push_signal(Signal::FinishIgnoringIndent);
240 }
241 items.push_sc(&TRAILING_STAR_SLASH);
242
243 return items;
244
245 fn get_last_line_trailing_whitespace(text: &str) -> &str {
246 let end_text = &text[text.trim_end().len()..];
247 if let Some(last_index) = end_text.rfind('\n') {
248 &end_text[last_index + 1..]
249 } else {
250 end_text
251 }
252 }
253}
254
255pub fn text_has_dprint_ignore(text: &str, searching_text: &str) -> bool {
257 let pos = text.find(searching_text);
258 if let Some(pos) = pos {
259 let end = pos + searching_text.len();
260 if pos > 0 && is_alpha_numeric_at_pos(text, pos - 1) {
261 return false;
262 }
263 if is_alpha_numeric_at_pos(text, end) {
264 return false;
265 }
266 return true;
267 } else {
268 return false;
269 }
270
271 fn is_alpha_numeric_at_pos(text: &str, pos: usize) -> bool {
272 if let Some(chars_after) = text.get(pos..) {
273 if let Some(char_after) = chars_after.chars().next() {
274 return char_after.is_alphanumeric();
275 }
276 }
277 false
278 }
279}