mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-23 07:41:03 +00:00
104 lines
2.3 KiB
Rust
104 lines
2.3 KiB
Rust
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<usize> {
|
|
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<usize>) -> &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<usize>) -> u16 {
|
|
if self.obscure { range.len() as u16 } else { self.slice(range).width() as u16 }
|
|
}
|
|
|
|
pub(super) fn window(&self, limit: usize) -> Range<usize> {
|
|
Self::find_window(
|
|
self.value.chars().map(|c| if self.obscure { '•' } else { c }),
|
|
self.offset,
|
|
limit,
|
|
)
|
|
}
|
|
|
|
pub(super) fn find_window<T>(it: T, offset: usize, limit: usize) -> Range<usize>
|
|
where
|
|
T: Iterator<Item = char>,
|
|
{
|
|
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)
|
|
}
|
|
}
|