1use crate::{buffer::Buffer, key::Arrow};
2use std::cmp::min;
3
4#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
5pub struct Cur {
6 pub idx: usize,
7}
8
9impl Cur {
10 pub fn new(idx: usize) -> Self {
11 Self { idx }
12 }
13
14 pub fn buffer_start() -> Self {
15 Cur { idx: 0 }
16 }
17
18 pub fn buffer_end(b: &Buffer) -> Self {
19 Cur {
20 idx: b.txt.len_chars(),
21 }
22 }
23
24 pub fn as_string_addr(&self, b: &Buffer) -> String {
25 let (y, x) = self.as_yx(b);
26 format!("{}:{}", y + 1, x + 1)
27 }
28
29 pub(crate) fn as_yx(&self, b: &Buffer) -> (usize, usize) {
30 let y = b.txt.char_to_line(self.idx);
31 let x = self.idx - b.txt.line_to_char(y);
32
33 (y, x)
34 }
35
36 pub(crate) fn from_yx(y_idx: usize, x_idx: usize, b: &Buffer) -> Self {
37 let line_start = b.txt.line_to_char(y_idx);
38 let mut x_max = b.txt.line_len_chars(y_idx);
39 if y_idx < b.len_lines() - 1 {
40 x_max -= 1;
41 }
42
43 Self {
44 idx: line_start + min(x_idx, x_max),
45 }
46 }
47
48 pub(super) fn arr_w_count(&self, arr: Arrow, count: usize, b: &Buffer) -> Self {
49 let mut cur = *self;
50
51 for _ in 0..count {
52 cur = cur.arr(arr, b);
53 }
54
55 cur.clamp_idx(b.txt.len_chars());
56 cur
57 }
58
59 pub(super) fn arr(&self, arr: Arrow, b: &Buffer) -> Self {
60 let mut cur = *self;
61
62 match arr {
63 Arrow::Up => {
64 let (mut y, _) = self.as_yx(b);
65 if y != 0 {
66 y -= 1;
67 cur.idx = b.txt.line_to_char(y) + b.x_from_rx(y);
68 }
69 }
70 Arrow::Down => {
71 let (mut y, _) = self.as_yx(b);
72 if !b.is_empty() && y < b.len_lines() - 1 {
73 y += 1;
74 cur.idx = b.txt.line_to_char(y) + b.x_from_rx(y);
75 }
76 }
77 Arrow::Left => cur.idx = cur.idx.saturating_sub(1),
78 Arrow::Right => cur.idx = min(cur.idx + 1, b.txt.len_chars()),
79 }
80
81 cur
82 }
83
84 pub(crate) fn clamp_idx(&mut self, max_idx: usize) {
85 self.idx = min(self.idx, max_idx);
86 }
87
88 #[must_use]
89 pub(super) fn move_to_line_start(mut self, b: &Buffer) -> Self {
90 let y = b.txt.char_to_line(self.idx);
91 self.idx = b.txt.line_to_char(y);
92 self
93 }
94
95 #[must_use]
96 pub(super) fn move_to_line_end(mut self, b: &Buffer) -> Self {
97 let y = b.txt.char_to_line(self.idx);
98 self.idx = b.txt.line_to_char(y) + b.txt.line_len_chars(y);
99
100 if y < b.len_lines().saturating_sub(1) {
103 self.idx = self.idx.saturating_sub(1);
104 }
105
106 self
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113 use crate::buffer::tests::buffer_from_lines;
114
115 #[test]
116 fn from_yx_focuses_eol() {
117 let lines = &["a", "ab", "abc", "abcd"];
118 let b = buffer_from_lines(lines);
119
120 for (n, s) in lines.iter().enumerate() {
121 let c = Cur::from_yx(n, 100, &b);
122
123 assert_eq!(c.as_yx(&b), (n, s.len()), "line='{s}'");
127 }
128 }
129
130 #[test]
131 fn arr_right_at_eof_focuses_eof() {
132 let lines = &["a", "ab", "abc", "abcd"];
133 let b = buffer_from_lines(lines);
134 let c = Cur::buffer_end(&b);
135 assert_eq!(c.as_yx(&b), (3, 4));
136
137 let final_c = c.arr(Arrow::Right, &b);
138 assert_eq!(final_c.as_yx(&b), (3, 4));
139 }
140
141 #[test]
142 fn arr_right_at_last_char_focuses_eof() {
143 let lines = &["a", "ab", "abc", "abcd"];
144 let b = buffer_from_lines(lines);
145 let mut c = Cur::buffer_end(&b);
146 c.idx -= 1;
147
148 assert_eq!(c.as_yx(&b), (3, 3));
149
150 let mut final_c = c.arr(Arrow::Right, &b);
151 final_c.clamp_idx(b.txt.len_chars());
152 assert_eq!(final_c.as_yx(&b), (3, 4));
153 }
154}