pub(crate) fn sanitize_terminal_text(input: &str) -> String {
#[derive(Clone, Copy)]
enum State {
Text,
Escape,
Csi,
Osc,
OscEscape,
}
let mut state = State::Text;
let mut out = String::with_capacity(input.len());
for ch in input.chars() {
state = match state {
State::Text if ch == '\u{1b}' => State::Escape,
State::Text => {
if ch == '\n' || ch == '\t' || !ch.is_control() {
out.push(ch);
}
State::Text
}
State::Escape if ch == '[' => State::Csi,
State::Escape if ch == ']' => State::Osc,
State::Escape => State::Text,
State::Csi if ('@'..='~').contains(&ch) => State::Text,
State::Csi => State::Csi,
State::Osc if ch == '\u{7}' => State::Text,
State::Osc if ch == '\u{1b}' => State::OscEscape,
State::Osc => State::Osc,
State::OscEscape if ch == '\\' => State::Text,
State::OscEscape if ch == '\u{1b}' => State::OscEscape,
State::OscEscape => State::Osc,
};
}
out
}
pub(crate) fn bounded_terminal_text(input: &str, limit: usize) -> String {
let sanitized = sanitize_terminal_text(input);
if sanitized.chars().count() <= limit {
return sanitized;
}
let mut preview = sanitized
.chars()
.take(limit.saturating_sub(1))
.collect::<String>();
preview.push('…');
preview
}
pub(crate) fn bounded_terminal_metadata(input: &str, limit: usize) -> String {
let flattened = sanitize_terminal_text(input)
.chars()
.map(|character| match character {
'\n' | '\t' => ' ',
character => character,
})
.collect::<String>();
bounded_terminal_text(&flattened, limit)
}