use std::ops::Range; use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; use super::{InputMode, InputOp}; #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct InputSnap { pub value: String, pub op: InputOp, pub mode: InputMode, pub obscure: bool, pub offset: usize, pub cursor: usize, } impl InputSnap { pub(super) fn new(value: String, obscure: bool) -> Self { let mut snap = Self { value, op: Default::default(), mode: Default::default(), obscure, offset: 0, cursor: usize::MAX, }; snap.resize(0); snap } pub(super) fn resize(&mut self, limit: usize) { let count = self.count(); let limit = if self.obscure { count.min(limit) } else { Self::find_window(self.value.chars().rev(), 0, limit).end }; self.cursor = self.cursor.min(self.count().saturating_sub(self.mode.delta())); self.offset = if self.cursor < (self.offset + limit).min(count) { count.saturating_sub(limit).min(self.offset) } else { count.saturating_sub(limit).min(self.cursor.saturating_sub(limit) + 1) }; } } impl InputSnap { pub(super) fn len(&self) -> usize { self.value.len() } pub(super) fn count(&self) -> usize { self.value.chars().count() } pub(super) fn idx(&self, n: usize) -> Option { self .value .char_indices() .nth(n) .map(|(i, _)| i) .or_else(|| if n == self.count() { Some(self.len()) } else { None }) } pub(super) fn slice(&self, range: Range) -> &str { let (s, e) = (self.idx(range.start), self.idx(range.end)); &self.value[s.unwrap()..e.unwrap()] } pub(super) fn width(&self, range: Range) -> u16 { if self.obscure { range.len() as u16 } else { self.slice(range).width() as u16 } } pub(super) fn window(&self, limit: usize) -> Range { Self::find_window( self.value.chars().map(|c| if self.obscure { '•' } else { c }), self.offset, limit, ) } pub(super) fn find_window(it: T, offset: usize, limit: usize) -> Range where T: Iterator, { let mut width = 0; let mut range = None; for (i, c) in it.enumerate().skip(offset) { width += c.width().unwrap_or(0); if width > limit { break; } match range { None => range = Some(i..i + 1), Some(ref mut r) => r.end = i + 1, } } range.unwrap_or(0..0) } }