use crate::sequence::SequenceState;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CopyTarget {
CodePoint,
Grapheme,
WholeText,
}
impl CopyTarget {
pub const ALL: [Self; 3] = [Self::CodePoint, Self::Grapheme, Self::WholeText];
pub const fn label(self) -> &'static str {
match self {
Self::CodePoint => "Code Point",
Self::Grapheme => "Grapheme Cluster",
Self::WholeText => "Whole Text",
}
}
pub fn text(self, sequence: &SequenceState) -> &str {
let analysis = sequence.analysis();
let point = &sequence.code_points()[sequence.selected_index()];
match self {
Self::CodePoint => &analysis.source()[point.byte_range()],
Self::Grapheme => {
&analysis.source()[analysis.graphemes()[point.grapheme_index()].byte_range()]
}
Self::WholeText => analysis.source(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CopyMove {
Previous,
Next,
First,
Last,
}
#[derive(Debug, Clone, Default)]
pub struct CopyDialogState {
selected_index: usize,
}
impl CopyDialogState {
pub const fn selected_index(&self) -> usize {
self.selected_index
}
pub fn selected_target(&self) -> CopyTarget {
CopyTarget::ALL[self.selected_index]
}
pub fn move_selection(&mut self, movement: CopyMove) {
let last = CopyTarget::ALL.len() - 1;
self.selected_index = match movement {
CopyMove::Previous => self.selected_index.saturating_sub(1),
CopyMove::Next => (self.selected_index + 1).min(last),
CopyMove::First => 0,
CopyMove::Last => last,
};
}
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::*;
use crate::sequence::SequenceMove;
#[rstest]
#[case("A\u{0301}")]
#[case("\u{0301}\u{0300}")]
#[case("👩🏽💻")]
#[case("🇯🇵")]
#[case("❤️")]
#[case("\r\n")]
fn copies_the_whole_cluster_from_each_member_even_outside_the_viewport(#[case] cluster: &str) {
let source = format!("\n{cluster}Y");
let mut sequence = SequenceState::new(source.clone());
sequence.resize_viewport(1);
for member in cluster.chars() {
sequence.move_selection(SequenceMove::Next);
assert_eq!(CopyTarget::CodePoint.text(&sequence), member.to_string());
assert_eq!(CopyTarget::Grapheme.text(&sequence), cluster);
assert_eq!(CopyTarget::WholeText.text(&sequence), source);
}
}
#[test]
fn distinguishes_repeated_clusters_and_preserves_all_controls() {
let source = "A\u{0301}\t\r\n\u{1b}\u{202e}A\u{0301}";
let mut sequence = SequenceState::new(source.to_owned());
sequence.move_selection(SequenceMove::Last);
assert_eq!(CopyTarget::CodePoint.text(&sequence), "\u{0301}");
assert_eq!(CopyTarget::Grapheme.text(&sequence), "A\u{0301}");
assert_eq!(CopyTarget::WholeText.text(&sequence), source);
}
}