use crate::whitespace::{common_leading_whitespace_prefix_len, is_ktav_whitespace};
#[derive(Copy, Clone)]
pub(super) enum MultilineMode {
Stripped,
Verbatim,
}
impl MultilineMode {
pub(super) fn terminator(self) -> &'static str {
match self {
MultilineMode::Stripped => ")",
MultilineMode::Verbatim => "))",
}
}
}
pub(super) struct Collecting<'a> {
pub(super) mode: MultilineMode,
pub(super) lines: Vec<&'a str>,
}
impl<'a> Collecting<'a> {
pub(super) fn new(mode: MultilineMode) -> Self {
Self {
mode,
lines: Vec::with_capacity(8),
}
}
pub(super) fn is_terminator(&self, trimmed: &str) -> bool {
trimmed == self.mode.terminator()
}
pub(super) fn finish(self) -> String {
match self.mode {
MultilineMode::Verbatim => {
if self.lines.len() == 1 {
self.lines[0].to_string()
} else {
self.lines.join("\n")
}
}
MultilineMode::Stripped => {
if self.lines.len() == 1 {
let only = self.lines[0];
if only.trim_matches(is_ktav_whitespace).is_empty() {
String::new()
} else {
only.trim_start_matches(is_ktav_whitespace)
.trim_end_matches(is_ktav_whitespace)
.to_string()
}
} else {
dedent(&self.lines)
}
}
}
}
}
fn dedent(lines: &[&str]) -> String {
let common_len = common_leading_whitespace_prefix_len(lines.iter().copied());
let mut cap: usize = lines
.iter()
.filter(|l| !l.trim_matches(is_ktav_whitespace).is_empty())
.map(|l| l.len() - common_len)
.sum();
cap = cap.saturating_add(lines.len());
let mut out = String::with_capacity(cap);
for (i, l) in lines.iter().enumerate() {
if i > 0 {
out.push('\n');
}
if l.trim_matches(is_ktav_whitespace).is_empty() {
} else if common_len > 0 && l.len() >= common_len {
out.push_str(l[common_len..].trim_end_matches(is_ktav_whitespace));
} else {
out.push_str(l.trim_end_matches(is_ktav_whitespace));
}
}
out
}