use std::iter;
use std::num::NonZeroU16;
use crate::cells::{TAB_WIDTH, text_width};
use crate::unsafe_chars::{LINE_SEPARATOR, PARAGRAPH_SEPARATOR, is_unsafe};
const NEXT_LINE: char = '\u{85}';
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum Breaks {
Space,
Newline,
}
impl Breaks {
fn replacement(self) -> char {
match self {
Self::Space => ' ',
Self::Newline => '\n',
}
}
}
pub(crate) fn clean(text: &str, breaks: Breaks) -> String {
let mut cleaned = String::with_capacity(text.len());
let mut characters = text.chars().peekable();
while let Some(character) = characters.next() {
match character {
'\t' => cleaned.push('\t'),
'\r' => {
let _paired = characters.next_if_eq(&'\n');
cleaned.push(breaks.replacement());
}
'\n' | '\u{b}' | '\u{c}' | NEXT_LINE | LINE_SEPARATOR | PARAGRAPH_SEPARATOR => {
cleaned.push(breaks.replacement());
}
other if is_unsafe(other) => {}
other => cleaned.push(other),
}
}
cleaned
}
pub(crate) fn is_clean(text: &str) -> bool {
text.chars().all(|character| !is_unsafe(character))
}
pub(crate) fn span_text(text: &str) -> String {
let cleaned = clean(text, Breaks::Space);
if cleaned.contains('\t') {
expand_tabs(&cleaned, 0, TAB_WIDTH)
} else {
cleaned
}
}
pub(crate) fn expand_tabs(text: &str, column: usize, tab_width: NonZeroU16) -> String {
let tab = usize::from(tab_width.get());
let mut expanded = String::with_capacity(text.len());
let mut column = column;
for (index, chunk) in text.split('\t').enumerate() {
if index > 0 {
let padding = tab - column % tab;
expanded.extend(iter::repeat_n(' ', padding));
column += padding;
}
expanded.push_str(chunk);
column += usize::from(text_width(chunk));
}
expanded
}