use std::borrow::Cow;
use std::collections::VecDeque;
use crate::MagicString;
impl<'text> MagicString<'text> {
pub fn trim(&mut self, char_type: Option<&str>) -> &mut Self {
self.trim_start(char_type);
self.trim_end(char_type);
self
}
pub fn trim_start(&mut self, char_type: Option<&str>) -> &mut Self {
self.trim_start_aborted(char_type);
self
}
pub fn trim_end(&mut self, char_type: Option<&str>) -> &mut Self {
self.trim_end_aborted(char_type);
self
}
pub fn trim_lines(&mut self) -> &mut Self {
self.trim(Some("[\r\n]"))
}
fn trim_start_aborted(&mut self, char_type: Option<&str>) -> bool {
let pattern = char_type.unwrap_or("\\s");
if trim_deque_start(&mut self.intro, pattern) {
return true;
}
let mut chunk_idx = Some(self.first_chunk_idx);
while let Some(idx) = chunk_idx {
let chunk = &self.chunks[idx];
let next_idx = chunk.next;
let content = if let Some(ref edited) = chunk.edited_content {
edited.as_ref().to_string()
} else {
chunk.span.text(&self.source).to_string()
};
let chunk = &mut self.chunks[idx];
if trim_deque_start(&mut chunk.intro, pattern) {
return true;
}
let trimmed_content = trim_start_pattern(&content, pattern);
if content.is_empty() {
chunk_idx = next_idx;
continue;
}
if trimmed_content.len() == content.len() {
return true;
}
if !trimmed_content.is_empty() {
chunk.edited_content = Some(trimmed_content.to_string().into());
return true;
}
chunk.edited_content = Some("".into());
if trim_deque_start(&mut chunk.outro, pattern) {
return true;
}
chunk_idx = next_idx;
}
false
}
fn trim_end_aborted(&mut self, char_type: Option<&str>) -> bool {
let pattern = char_type.unwrap_or("\\s");
if trim_deque_end(&mut self.outro, pattern) {
return true;
}
let mut chunk_idx = Some(self.last_chunk_idx);
while let Some(idx) = chunk_idx {
let chunk = &self.chunks[idx];
let prev_idx = chunk.prev;
let content = if let Some(ref edited) = chunk.edited_content {
edited.as_ref().to_string()
} else {
chunk.span.text(&self.source).to_string()
};
let chunk = &mut self.chunks[idx];
if trim_deque_end(&mut chunk.outro, pattern) {
return true;
}
let trimmed_content = trim_end_pattern(&content, pattern);
if content.is_empty() {
chunk_idx = prev_idx;
continue;
}
if trimmed_content.len() == content.len() {
return true;
}
if !trimmed_content.is_empty() {
chunk.edited_content = Some(trimmed_content.to_string().into());
return true;
}
chunk.edited_content = Some("".into());
if trim_deque_end(&mut chunk.intro, pattern) {
return true;
}
chunk_idx = prev_idx;
}
false
}
}
fn trim_deque_start<'a>(deque: &mut VecDeque<Cow<'a, str>>, pattern: &str) -> bool {
let old_deque = std::mem::take(deque);
let mut found_non_match = false;
for s in old_deque {
if found_non_match {
deque.push_back(s);
} else {
let trimmed = trim_start_pattern(s.as_ref(), pattern);
if !trimmed.is_empty() {
deque.push_back(Cow::Owned(trimmed.to_string()));
found_non_match = true;
}
}
}
!deque.is_empty()
}
fn trim_deque_end<'a>(deque: &mut VecDeque<Cow<'a, str>>, pattern: &str) -> bool {
let old_deque = std::mem::take(deque);
let mut found_non_match = false;
for s in old_deque.into_iter().rev() {
if found_non_match {
deque.push_front(s);
} else {
let trimmed = trim_end_pattern(s.as_ref(), pattern);
if !trimmed.is_empty() {
deque.push_front(Cow::Owned(trimmed.to_string()));
found_non_match = true;
}
}
}
!deque.is_empty()
}
fn trim_start_pattern<'a>(s: &'a str, pattern: &str) -> &'a str {
match pattern {
"\\s" => return s.trim_start(),
"[\\r\\n]" | "[\r\n]" => return s.trim_start_matches(['\r', '\n']),
_ => {}
}
let regex_pattern = format!("^({pattern})+");
match regex::Regex::new(®ex_pattern) {
Ok(re) => {
if let Some(m) = re.find(s) {
&s[m.end()..]
} else {
s
}
}
Err(_) => s.trim_start(), }
}
fn trim_end_pattern<'a>(s: &'a str, pattern: &str) -> &'a str {
match pattern {
"\\s" => return s.trim_end(),
"[\\r\\n]" | "[\r\n]" => return s.trim_end_matches(['\r', '\n']),
_ => {}
}
let regex_pattern = format!("({pattern})+$");
match regex::Regex::new(®ex_pattern) {
Ok(re) => {
if let Some(m) = re.find(s) {
&s[..m.start()]
} else {
s
}
}
Err(_) => s.trim_end(), }
}