yazi_widgets/input/
input.rs

1use std::{borrow::Cow, ops::Range};
2
3use crossterm::cursor::SetCursorStyle;
4use yazi_config::YAZI;
5
6use super::{InputSnap, InputSnaps, mode::InputMode, op::InputOp};
7use crate::CLIPBOARD;
8
9pub type InputCallback = Box<dyn Fn(&str, &str)>;
10
11#[derive(Default)]
12pub struct Input {
13	pub snaps:    InputSnaps,
14	pub limit:    usize,
15	pub obscure:  bool,
16	pub callback: Option<InputCallback>,
17}
18
19impl Input {
20	pub fn new(value: String, limit: usize, obscure: bool, callback: InputCallback) -> Self {
21		Self { snaps: InputSnaps::new(value, obscure, limit), limit, obscure, callback: Some(callback) }
22	}
23
24	pub(super) fn handle_op(&mut self, cursor: usize, include: bool) -> bool {
25		let old = self.snap().clone();
26		let snap = self.snap_mut();
27
28		match snap.op {
29			InputOp::None | InputOp::Select(_) => {
30				snap.cursor = cursor;
31			}
32			InputOp::Delete(cut, insert, _) => {
33				let range = snap.op.range(cursor, include).unwrap();
34				let Range { start, end } = snap.idx(range.start)..snap.idx(range.end);
35
36				let drain = snap.value.drain(start.unwrap()..end.unwrap()).collect::<String>();
37				if cut {
38					futures::executor::block_on(CLIPBOARD.set(&drain));
39				}
40
41				snap.op = InputOp::None;
42				snap.mode = if insert { InputMode::Insert } else { InputMode::Normal };
43				snap.cursor = range.start;
44			}
45			InputOp::Yank(_) => {
46				let range = snap.op.range(cursor, include).unwrap();
47				let Range { start, end } = snap.idx(range.start)..snap.idx(range.end);
48				let yanked = &snap.value[start.unwrap()..end.unwrap()];
49
50				snap.op = InputOp::None;
51				futures::executor::block_on(CLIPBOARD.set(yanked));
52			}
53		};
54
55		snap.cursor = snap.count().saturating_sub(snap.mode.delta()).min(snap.cursor);
56		if snap == &old {
57			return false;
58		}
59		if !matches!(old.op, InputOp::None | InputOp::Select(_)) {
60			self.snaps.tag(self.limit).then(|| self.flush_value());
61		}
62		true
63	}
64
65	pub(super) fn flush_value(&mut self) {
66		if let Some(cb) = &self.callback {
67			let (before, after) = self.partition();
68			cb(before, after);
69		}
70	}
71}
72
73impl Input {
74	#[inline]
75	pub fn value(&self) -> &str { &self.snap().value }
76
77	#[inline]
78	pub fn display(&self) -> Cow<'_, str> {
79		if self.obscure {
80			"•".repeat(self.snap().window(self.limit).len()).into()
81		} else {
82			self.snap().slice(self.snap().window(self.limit)).into()
83		}
84	}
85
86	#[inline]
87	pub fn mode(&self) -> InputMode { self.snap().mode }
88
89	#[inline]
90	pub fn cursor(&self) -> u16 { self.snap().width(self.snap().offset..self.snap().cursor) }
91
92	pub fn cursor_shape(&self) -> SetCursorStyle {
93		use InputMode as M;
94
95		match self.mode() {
96			M::Normal if YAZI.input.cursor_blink => SetCursorStyle::BlinkingBlock,
97			M::Normal if !YAZI.input.cursor_blink => SetCursorStyle::SteadyBlock,
98			M::Insert if YAZI.input.cursor_blink => SetCursorStyle::BlinkingBar,
99			M::Insert if !YAZI.input.cursor_blink => SetCursorStyle::SteadyBar,
100			M::Replace if YAZI.input.cursor_blink => SetCursorStyle::BlinkingUnderScore,
101			M::Replace if !YAZI.input.cursor_blink => SetCursorStyle::SteadyUnderScore,
102			M::Normal | M::Insert | M::Replace => unreachable!(),
103		}
104	}
105
106	pub fn selected(&self) -> Option<Range<u16>> {
107		let snap = self.snap();
108		let start = snap.op.start()?;
109
110		let (start, end) =
111			if start < snap.cursor { (start, snap.cursor) } else { (snap.cursor + 1, start + 1) };
112
113		let win = snap.window(self.limit);
114		let Range { start, end } = start.max(win.start)..end.min(win.end);
115
116		let s = snap.width(snap.offset..start);
117		Some(s..s + snap.width(start..end))
118	}
119
120	#[inline]
121	pub fn partition(&self) -> (&str, &str) {
122		let snap = self.snap();
123		let idx = snap.idx(snap.cursor).unwrap();
124		(&snap.value[..idx], &snap.value[idx..])
125	}
126
127	#[inline]
128	pub fn snap(&self) -> &InputSnap { self.snaps.current() }
129
130	#[inline]
131	pub fn snap_mut(&mut self) -> &mut InputSnap { self.snaps.current_mut() }
132}