brk_string_wizard/magic_string/
trim.rs1use std::borrow::Cow;
2use std::collections::VecDeque;
3
4use crate::MagicString;
5
6impl<'text> MagicString<'text> {
7 pub fn trim(&mut self, char_type: Option<&str>) -> &mut Self {
9 self.trim_start(char_type);
10 self.trim_end(char_type);
11 self
12 }
13
14 pub fn trim_start(&mut self, char_type: Option<&str>) -> &mut Self {
16 self.trim_start_aborted(char_type);
17 self
18 }
19
20 pub fn trim_end(&mut self, char_type: Option<&str>) -> &mut Self {
22 self.trim_end_aborted(char_type);
23 self
24 }
25
26 pub fn trim_lines(&mut self) -> &mut Self {
28 self.trim(Some("[\r\n]"))
29 }
30
31 fn trim_start_aborted(&mut self, char_type: Option<&str>) -> bool {
33 let pattern = char_type.unwrap_or("\\s");
34
35 if trim_deque_start(&mut self.intro, pattern) {
37 return true;
38 }
39
40 let mut chunk_idx = Some(self.first_chunk_idx);
42 while let Some(idx) = chunk_idx {
43 let chunk = &self.chunks[idx];
44 let next_idx = chunk.next;
45
46 let content = if let Some(ref edited) = chunk.edited_content {
48 edited.as_ref().to_string()
49 } else {
50 chunk.span.text(&self.source).to_string()
51 };
52
53 let chunk = &mut self.chunks[idx];
55 if trim_deque_start(&mut chunk.intro, pattern) {
56 return true;
57 }
58
59 let trimmed_content = trim_start_pattern(&content, pattern);
61
62 if content.is_empty() {
63 chunk_idx = next_idx;
65 continue;
66 }
67
68 if trimmed_content.len() == content.len() {
69 return true;
71 }
72
73 if !trimmed_content.is_empty() {
74 chunk.edited_content = Some(trimmed_content.to_string().into());
76 return true;
77 }
78
79 chunk.edited_content = Some("".into());
81
82 if trim_deque_start(&mut chunk.outro, pattern) {
84 return true;
85 }
86
87 chunk_idx = next_idx;
88 }
89
90 false
91 }
92
93 fn trim_end_aborted(&mut self, char_type: Option<&str>) -> bool {
95 let pattern = char_type.unwrap_or("\\s");
96
97 if trim_deque_end(&mut self.outro, pattern) {
99 return true;
100 }
101
102 let mut chunk_idx = Some(self.last_chunk_idx);
104 while let Some(idx) = chunk_idx {
105 let chunk = &self.chunks[idx];
106 let prev_idx = chunk.prev;
107
108 let content = if let Some(ref edited) = chunk.edited_content {
110 edited.as_ref().to_string()
111 } else {
112 chunk.span.text(&self.source).to_string()
113 };
114
115 let chunk = &mut self.chunks[idx];
117 if trim_deque_end(&mut chunk.outro, pattern) {
118 return true;
119 }
120
121 let trimmed_content = trim_end_pattern(&content, pattern);
123
124 if content.is_empty() {
125 chunk_idx = prev_idx;
127 continue;
128 }
129
130 if trimmed_content.len() == content.len() {
131 return true;
133 }
134
135 if !trimmed_content.is_empty() {
136 chunk.edited_content = Some(trimmed_content.to_string().into());
138 return true;
139 }
140
141 chunk.edited_content = Some("".into());
143
144 if trim_deque_end(&mut chunk.intro, pattern) {
146 return true;
147 }
148
149 chunk_idx = prev_idx;
150 }
151
152 false
153 }
154}
155
156fn trim_deque_start<'a>(deque: &mut VecDeque<Cow<'a, str>>, pattern: &str) -> bool {
159 let old_deque = std::mem::take(deque);
160 let mut found_non_match = false;
161
162 for s in old_deque {
163 if found_non_match {
164 deque.push_back(s);
165 } else {
166 let trimmed = trim_start_pattern(s.as_ref(), pattern);
167 if !trimmed.is_empty() {
168 deque.push_back(Cow::Owned(trimmed.to_string()));
169 found_non_match = true;
170 }
171 }
172 }
173
174 !deque.is_empty()
175}
176
177fn trim_deque_end<'a>(deque: &mut VecDeque<Cow<'a, str>>, pattern: &str) -> bool {
180 let old_deque = std::mem::take(deque);
181 let mut found_non_match = false;
182
183 for s in old_deque.into_iter().rev() {
184 if found_non_match {
185 deque.push_front(s);
186 } else {
187 let trimmed = trim_end_pattern(s.as_ref(), pattern);
188 if !trimmed.is_empty() {
189 deque.push_front(Cow::Owned(trimmed.to_string()));
190 found_non_match = true;
191 }
192 }
193 }
194
195 !deque.is_empty()
196}
197
198fn trim_start_pattern<'a>(s: &'a str, pattern: &str) -> &'a str {
204 match pattern {
206 "\\s" => return s.trim_start(),
207 "[\\r\\n]" | "[\r\n]" => return s.trim_start_matches(['\r', '\n']),
208 _ => {}
209 }
210
211 let regex_pattern = format!("^({pattern})+");
213 match regex::Regex::new(®ex_pattern) {
214 Ok(re) => {
215 if let Some(m) = re.find(s) {
216 &s[m.end()..]
217 } else {
218 s
219 }
220 }
221 Err(_) => s.trim_start(), }
223}
224
225fn trim_end_pattern<'a>(s: &'a str, pattern: &str) -> &'a str {
231 match pattern {
233 "\\s" => return s.trim_end(),
234 "[\\r\\n]" | "[\r\n]" => return s.trim_end_matches(['\r', '\n']),
235 _ => {}
236 }
237
238 let regex_pattern = format!("({pattern})+$");
240 match regex::Regex::new(®ex_pattern) {
241 Ok(re) => {
242 if let Some(m) = re.find(s) {
243 &s[..m.start()]
244 } else {
245 s
246 }
247 }
248 Err(_) => s.trim_end(), }
250}