strop_engine/editor/
view.rs1use super::{Editor, Mode};
10
11impl Editor {
12 pub(crate) fn scroll_half_page(&mut self, down: bool) {
15 self.scroll_by(self.view_rows().max(2) / 2, down);
16 }
17
18 pub(crate) fn scroll_full_page(&mut self, down: bool) {
20 self.scroll_by(self.view_rows().saturating_sub(1).max(1), down);
21 }
22
23 pub(crate) fn scroll_by(&mut self, rows: usize, down: bool) {
24 let line = self.buf().line_of(self.head());
25 let last = self.buf().last_content_line();
26 let line = if down {
27 (line + rows).min(last)
28 } else {
29 line.saturating_sub(rows)
30 };
31 let col = self.buf().col_of(self.head());
32 let start = self.buf().line_start(line);
33 let end = self.buf().line_end(line);
34 self.set_head((start + col).min(end));
35 self.clamp_cursor();
36 self.scroll_to_cursor(self.view_rows());
37 }
38
39 pub(crate) fn view_place(&mut self, place: char) {
41 let line = self.buf().line_of(self.head());
42 let rows = self.view_rows();
43 let last = self.buf().last_content_line();
44 let top = match place {
45 't' => line,
46 'b' => (line + 1).saturating_sub(rows),
47 _ => (line + 1).saturating_sub(rows.max(2) / 2 + 1),
48 };
49 let max_top = (last + 1).saturating_sub(rows);
51 self.view_mut().view_top = top.min(max_top);
52 }
53
54 pub(crate) fn jump_visible(&mut self, which: char, count: usize) {
57 let rows = self.view_rows();
58 let last = self.buf().last_content_line();
59 let line = match which {
60 'H' => self.view_top() + count.saturating_sub(1),
61 'L' => {
62 (self.view_top() + rows.saturating_sub(1)).saturating_sub(count.saturating_sub(1))
63 }
64 _ => self.view_top() + rows.max(2) / 2,
65 }
66 .min(last);
67 let s = self.buf().line_start(line);
68 let e = self.buf().line_end(line);
69 let mut p = s;
71 while p < e
72 && self
73 .buf()
74 .byte_at(p)
75 .is_some_and(|b| b == b' ' || b == b'\t')
76 {
77 p += 1;
78 }
79 self.set_head(p.min(e));
80 self.clamp_cursor();
81 }
82
83 pub(crate) fn scroll_counted(&mut self, which: char, n: usize) {
85 let counted = n > 1;
86 match (which, counted) {
87 ('d', true) => self.scroll_by(n, true),
88 ('u', true) => self.scroll_by(n, false),
89 ('d', false) => self.scroll_half_page(true),
90 ('u', false) => self.scroll_half_page(false),
91 ('f', _) => self.scroll_full_page(true),
92 _ => self.scroll_full_page(false),
93 }
94 }
95
96 pub(crate) fn write_quit(&mut self) {
99 if self.buf().dirty {
100 self.request_save(None, false, true);
101 return;
102 }
103 self.close_pane_or_buffer(false);
104 }
105
106 pub(crate) fn alternate_buffer(&mut self) {
108 let cur = self.current();
109 let Some(&alt) = self.mru.iter().find(|&&id| id != cur) else {
110 self.message = "no alternate buffer".into();
111 return;
112 };
113 self.switch_to(alt);
114 self.clamp_cursor();
115 }
116
117 pub(crate) fn reselect_visual(&mut self) {
119 let Some((anchor, head)) = self.last_visual else {
120 self.message = "no previous visual selection".into();
121 return;
122 };
123 let max = self.buf().len_bytes();
124 let (a, h) = (anchor.min(max), head.min(max));
125 let a = self.buf().clamp_boundary(a);
126 let h = self.buf().clamp_boundary(h);
127 self.sels_mut().stretch_primary(a, h);
128 self.mode = Mode::Visual;
129 }
130
131 pub(crate) fn insert_at_last(&mut self) {
133 let Some(pos) = self.last_insert_pos else {
134 self.message = "no previous insert".into();
135 return;
136 };
137 let pos = self.buf().clamp_boundary(pos.min(self.buf().len_bytes()));
138 self.set_head(pos);
139 self.mode = Mode::Insert;
140 }
141
142 pub(crate) fn change_jump(&mut self, back: bool) {
146 let positions = self.change_positions();
147 if positions.is_empty() {
148 self.message = "change list is empty".into();
149 return;
150 }
151 let depth = self.buf().history().depth();
155 let idx = match self.change_idx.filter(|(_, d)| *d == depth) {
156 None => {
157 if back {
158 0
159 } else {
160 self.message = "at the newest change".into();
161 return;
162 }
163 }
164 Some((i, _)) => {
165 if back {
166 (i + 1).min(positions.len() - 1)
167 } else {
168 i.saturating_sub(1)
169 }
170 }
171 };
172 self.change_idx = Some((idx, depth));
173 let pos = positions[idx];
174 let pos = self.buf().clamp_boundary(pos.min(self.buf().len_bytes()));
175 self.set_head(pos);
176 self.clamp_cursor();
177 self.scroll_to_cursor(self.view_rows());
178 }
179
180 fn change_positions(&self) -> Vec<usize> {
182 self.buf().history().change_positions()
183 }
184}