on master help sign in
commands: Improve scroll functions.

Followed kakoune's implementation, it's no longer janky and can scroll all the way in either direction.

commita5c4314940e2ef875026f52ffb6f285725d3fb02 authorBlaž Hrastnik <blaz@mxxn.io> dateThu 4 Mar 2021, 16:13 (6 years ago) parent565fb94
2 files changed+40−51
helix-term/src/commands.rs+40−40 helix-view/src/view.rs−11
helix-term/src/commands.rs+40−40
@@ -4,8 +4,8 @@ use helix_core::{
44 object,
55 regex::{self, Regex},
66 register, selection,
7− state::{Direction, Granularity, State},
8− Change, ChangeSet, Range, Selection, Tendril, Transaction,
7+ state::{coords_at_pos, pos_at_coords, Direction, Granularity, State},
8+ Change, ChangeSet, Position, Range, Selection, Tendril, Transaction,
99};
1010
1111use once_cell::sync::Lazy;
@@ -222,62 +222,60 @@ pub fn extend_next_word_end(cx: &mut Context) {
222222 doc.set_selection(selection);
223223}
224224
225−pub fn page_up(cx: &mut Context) {
226− let view = cx.view();
227− if view.first_line < PADDING {
225+fn scroll(view: &mut View, offset: usize, direction: Direction) {
226+ use Direction::*;
227+ let text = view.doc.text().slice(..);
228+
229+ let last_line = view.last_line();
230+ let cursor = coords_at_pos(text, view.doc.selection().cursor());
231+ let doc_last_line = text.len_lines() - 1;
232+
233+ if direction == Backward && view.first_line == 0
234+ || direction == Forward && last_line == doc_last_line
235+ {
228236 return;
229237 }
230238
231− view.first_line = view.first_line.saturating_sub(view.area.height as usize);
239+ let scrolloff = PADDING; // min(user pref, half win width/height)
240+
241+ // cursor visual offset
242+ let cursor_off = cursor.row - view.first_line;
232243
233− if !view.check_cursor_in_view() {
234− let text = view.doc.text();
235− let pos = text.line_to_char(view.last_line().saturating_sub(PADDING));
236− view.doc.set_selection(Selection::point(pos));
244+ view.first_line = match direction {
245+ Forward => view.first_line + offset,
246+ Backward => view.first_line.saturating_sub(offset),
237247 }
248+ .min(doc_last_line);
249+
250+ // clamp into viewport
251+ let line = (view.first_line + cursor_off).clamp(
252+ view.first_line + scrolloff,
253+ view.first_line + view.last_line().saturating_sub(scrolloff),
254+ );
255+
256+ let pos = pos_at_coords(text, Position::new(line, cursor.col)); // this func will properly truncate to line end
257+ view.doc.set_selection(Selection::point(pos));
238258}
239259
240−pub fn page_down(cx: &mut Context) {
260+pub fn page_up(cx: &mut Context) {
241261 let view = cx.view();
242− view.first_line += view.area.height as usize + PADDING;
262+ scroll(view, view.area.height as usize, Direction::Backward);
263+}
243264
244− if view.first_line < view.doc.text().len_lines() {
245− let text = view.doc.text();
246− let pos = text.line_to_char(view.first_line as usize);
247− view.doc.set_selection(Selection::point(pos));
248− }
265+pub fn page_down(cx: &mut Context) {
266+ let view = cx.view();
267+ scroll(view, view.area.height as usize, Direction::Forward);
249268}
250269
251270pub fn half_page_up(cx: &mut Context) {
252271 let view = cx.view();
253− if view.first_line < PADDING {
254− return;
255− }
256−
257− view.first_line = view
258− .first_line
259− .saturating_sub(view.area.height as usize / 2);
260−
261− if !view.check_cursor_in_view() {
262− let text = &view.doc.text();
263− let pos = text.line_to_char(view.last_line() - PADDING);
264− view.doc.set_selection(Selection::point(pos));
265− }
272+ scroll(view, view.area.height as usize / 2, Direction::Backward);
266273}
267274
268275pub fn half_page_down(cx: &mut Context) {
269276 let view = cx.view();
270− let lines = view.doc.text().len_lines();
271− if view.first_line < lines.saturating_sub(view.area.height as usize) {
272− view.first_line += view.area.height as usize / 2;
273− }
274− if !view.check_cursor_in_view() {
275− let text = view.doc.text();
276− let pos = text.line_to_char(view.first_line as usize);
277− view.doc.set_selection(Selection::point(pos));
278− }
277+ scroll(view, view.area.height as usize / 2, Direction::Forward);
279278}
280−// avoid select by default by having a visual mode switch that makes movements into selects
281279
282280pub fn extend_char_left(cx: &mut Context) {
283281 let count = cx.count;
@@ -439,8 +437,10 @@ pub fn search_selection(cx: &mut Context) {
439437pub fn select_line(cx: &mut Context) {
440438 let count = cx.count;
441439 let doc = cx.doc();
440+
442441 let pos = doc.selection().primary();
443442 let text = doc.text();
443+
444444 let line = text.char_to_line(pos.head);
445445 let start = text.line_to_char(line);
446446 let end = text.line_to_char(line + count).saturating_sub(1);
helix-view/src/view.rs−11
@@ -35,17 +35,6 @@ impl View {
3535 Ok(view)
3636 }
3737
38− pub fn check_cursor_in_view(&self) -> bool {
39− let cursor = self.doc.selection().cursor();
40− let line = self.doc.text().char_to_line(cursor);
41− let document_end = self.first_line + self.area.height.saturating_sub(1) as usize;
42−
43− if (line > document_end.saturating_sub(PADDING)) || (line < self.first_line + PADDING) {
44− return false;
45− }
46− true
47− }
48−
4938 pub fn ensure_cursor_in_view(&mut self) {
5039 let cursor = self.doc.state.selection().cursor();
5140 let line = self.doc.text().char_to_line(cursor);