yazi_core/input/commands/
move_.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use std::{num::ParseIntError, str::FromStr};

use unicode_width::UnicodeWidthStr;
use yazi_macro::render;
use yazi_shared::event::{CmdCow, Data};

use crate::input::{Input, op::InputOp, snap::InputSnap};

struct Opt {
	step:         OptStep,
	in_operating: bool,
}

impl From<CmdCow> for Opt {
	fn from(c: CmdCow) -> Self {
		Self {
			step:         c.first().and_then(|d| d.try_into().ok()).unwrap_or_default(),
			in_operating: c.bool("in-operating"),
		}
	}
}
impl From<isize> for Opt {
	fn from(step: isize) -> Self { Self { step: step.into(), in_operating: false } }
}

impl Input {
	#[yazi_codegen::command]
	pub fn move_(&mut self, opt: Opt) {
		let snap = self.snap();
		if opt.in_operating && snap.op == InputOp::None {
			return;
		}

		render!(self.handle_op(opt.step.cursor(snap), false));

		let (limit, snap) = (self.limit(), self.snap_mut());
		if snap.offset > snap.cursor {
			snap.offset = snap.cursor;
		} else if snap.value.is_empty() {
			snap.offset = 0;
		} else {
			let delta = snap.mode.delta();
			let s = snap.slice(snap.offset..snap.cursor + delta);
			if s.width() >= limit {
				let s = s.chars().rev().collect::<String>();
				snap.offset = snap.cursor - InputSnap::find_window(&s, 0, limit).end.saturating_sub(delta);
			}
		}
	}
}

// --- Step
enum OptStep {
	Offset(isize),
	Bol,
	Eol,
	FirstChar,
}

impl OptStep {
	fn cursor(self, snap: &InputSnap) -> usize {
		match self {
			Self::Offset(n) if n <= 0 => snap.cursor.saturating_add_signed(n),
			Self::Offset(n) => snap.count().min(snap.cursor + n as usize),
			Self::Bol => 0,
			Self::Eol => snap.count(),
			Self::FirstChar => {
				snap.value.chars().enumerate().find(|(_, c)| !c.is_whitespace()).map_or(0, |(i, _)| i)
			}
		}
	}
}

impl Default for OptStep {
	fn default() -> Self { 0.into() }
}

impl FromStr for OptStep {
	type Err = ParseIntError;

	fn from_str(s: &str) -> Result<Self, Self::Err> {
		Ok(match s {
			"bol" => Self::Bol,
			"eol" => Self::Eol,
			"first-char" => Self::FirstChar,
			s => Self::Offset(s.parse()?),
		})
	}
}

impl From<isize> for OptStep {
	fn from(value: isize) -> Self { Self::Offset(value) }
}

impl TryFrom<&Data> for OptStep {
	type Error = ();

	fn try_from(value: &Data) -> Result<Self, Self::Error> {
		match value {
			Data::String(s) => s.parse().map_err(|_| ()),
			Data::Integer(i) => Ok(Self::from(*i as isize)),
			_ => Err(()),
		}
	}
}