pub const HIDDEN: &[(char, char)] = &[
('\u{00AD}', '\u{00AD}'),
('\u{0600}', '\u{0605}'),
('\u{061C}', '\u{061C}'),
('\u{06DD}', '\u{06DD}'),
('\u{070F}', '\u{070F}'),
('\u{0890}', '\u{0891}'),
('\u{08E2}', '\u{08E2}'),
('\u{180E}', '\u{180E}'),
('\u{200B}', '\u{200F}'),
('\u{202A}', '\u{202E}'),
('\u{2060}', '\u{2064}'),
('\u{2066}', '\u{206F}'),
('\u{FEFF}', '\u{FEFF}'),
('\u{FFF9}', '\u{FFFB}'),
('\u{110BD}', '\u{110BD}'),
('\u{110CD}', '\u{110CD}'),
('\u{13430}', '\u{1343F}'),
('\u{1BCA0}', '\u{1BCA3}'),
('\u{1D173}', '\u{1D17A}'),
('\u{E0000}', '\u{E007F}'),
('\u{180B}', '\u{180D}'),
('\u{180F}', '\u{180F}'),
('\u{FE00}', '\u{FE0F}'),
('\u{E0100}', '\u{E01EF}'),
('\u{115F}', '\u{1160}'),
('\u{3164}', '\u{3164}'),
('\u{FFA0}', '\u{FFA0}'),
('\u{034F}', '\u{034F}'),
('\u{17B4}', '\u{17B5}'),
('\u{2800}', '\u{2800}'),
('\u{FFF0}', '\u{FFF8}'),
];
pub fn is_hidden(ch: char) -> bool {
HIDDEN.iter().any(|&(low, high)| (low..=high).contains(&ch))
}
pub fn is_hidden_on_screen(ch: char) -> bool {
ch != '\u{200D}' && is_hidden(ch)
}
pub fn breaks_line(ch: char) -> bool {
ch.is_control() || matches!(ch, '\u{2028}' | '\u{2029}')
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ranges_hold_and_only_the_joiner_differs_on_screen() {
for &(low, high) in HIDDEN {
assert!(low <= high, "U+{:04X}", u32::from(low));
for ch in [low, high] {
assert!(is_hidden(ch), "U+{:04X}", u32::from(ch));
assert_eq!(is_hidden_on_screen(ch), ch != '\u{200D}');
}
}
assert!(is_hidden('\u{200D}'));
assert!(!is_hidden_on_screen('\u{200D}'));
for ch in ['a', ' ', '\u{1F468}', '\u{00AC}', '\u{00AE}', '\u{FFFC}'] {
assert!(!is_hidden(ch), "U+{:04X}", u32::from(ch));
}
}
#[test]
fn line_breaks() {
for ch in [
'\n', '\r', '\t', '\u{1b}', '\u{7f}', '\u{85}', '\u{9f}', '\u{2028}', '\u{2029}',
] {
assert!(breaks_line(ch), "U+{:04X}", u32::from(ch));
}
for ch in ['a', ' ', '\u{a0}', '\u{202E}', '\u{200B}'] {
assert!(!breaks_line(ch), "U+{:04X}", u32::from(ch));
}
}
}