on master help sign in
Horizontal scrolling! It only took a year to get around to it.
978f511Blaž Hrastnik
helix-term/src/ui/editor.rs+9−7
@@ -189,18 +189,20 @@ impl EditorView {
189 } else if grapheme == "\t" {
189 } else if grapheme == "\t" {
190 visual_x += (tab_width as u16);
190 visual_x += (tab_width as u16);
191 } else {
191 } else {
192+ let out_of_bounds = visual_x < view.first_col as u16
192− if visual_x >= viewport.width {
193+ || visual_x >= viewport.width + view.first_col as u16;
193− // if we're offscreen just keep going until we hit a new line
194− // TODO: will need tweaking when we also take into account
195− // horizontal scrolling
196− continue;
197− }
198
194
199 // Cow will prevent allocations if span contained in a single slice
195 // Cow will prevent allocations if span contained in a single slice
200 // which should really be the majority case
196 // which should really be the majority case
201 let grapheme = Cow::from(grapheme);
197 let grapheme = Cow::from(grapheme);
202 let width = grapheme_width(&grapheme) as u16;
198 let width = grapheme_width(&grapheme) as u16;
203
199
200+ if out_of_bounds {
201+ // if we're offscreen just keep going until we hit a new line
202+ visual_x += width;
203+ continue;
204+ }
205+
204 // ugh,interleave highlight spans with diagnostic spans
206 // ugh,interleave highlight spans with diagnostic spans
205 let is_diagnostic = doc.diagnostics.iter().any(|diagnostic| {
207 let is_diagnostic = doc.diagnostics.iter().any(|diagnostic| {
206 diagnostic.range.start <= char_index
208 diagnostic.range.start <= char_index
@@ -214,7 +216,7 @@ impl EditorView {
214 };
216 };
215
217
216 surface.set_string(
218 surface.set_string(
217− viewport.x + visual_x,
219+ viewport.x + visual_x - view.first_col as u16,
218 viewport.y + line,
220 viewport.y + line,
219 grapheme,
221 grapheme,
220 style,
222 style,
helix-view/src/view.rs+21−5
@@ -4,6 +4,7 @@ use std::borrow::Cow;
4
4
5use crate::{Document, DocumentId, ViewId};
5use crate::{Document, DocumentId, ViewId};
6use helix_core::{
6use helix_core::{
7+ coords_at_pos,
7 graphemes::{grapheme_width, RopeGraphemes},
8 graphemes::{grapheme_width, RopeGraphemes},
8 Position, RopeSlice, Selection,
9 Position, RopeSlice, Selection,
9};
10};
@@ -56,6 +57,7 @@ pub struct View {
56 pub id: ViewId,
57 pub id: ViewId,
57 pub doc: DocumentId,
58 pub doc: DocumentId,
58 pub first_line: usize,
59 pub first_line: usize,
60+ pub first_col: usize,
59 pub area: Rect,
61 pub area: Rect,
60 pub jumps: JumpList,
62 pub jumps: JumpList,
61}
63}
@@ -66,6 +68,7 @@ impl View {
66 id: ViewId::default(),
68 id: ViewId::default(),
67 doc,
69 doc,
68 first_line: 0,
70 first_line: 0,
71+ first_col: 0,
69 area: Rect::default(), // will get calculated upon inserting into tree
72 area: Rect::default(), // will get calculated upon inserting into tree
70 jumps: JumpList::new((doc, Selection::point(0))), // TODO: use actual sel
73 jumps: JumpList::new((doc, Selection::point(0))), // TODO: use actual sel
71 };
74 };
@@ -75,18 +78,30 @@ impl View {
75
78
76 pub fn ensure_cursor_in_view(&mut self, doc: &Document) {
79 pub fn ensure_cursor_in_view(&mut self, doc: &Document) {
77 let cursor = doc.selection(self.id).cursor();
80 let cursor = doc.selection(self.id).cursor();
78− let line = doc.text().char_to_line(cursor);
81+ let pos = coords_at_pos(doc.text().slice(..), cursor);
82+ let line = pos.row;
83+ let col = pos.col;
79− let document_end = self.first_line + (self.area.height as usize).saturating_sub(2);
84+ let last_line = self.first_line + (self.area.height as usize).saturating_sub(2);
80
85
81− // TODO: side scroll
86+ // TODO: not ideal
87+ const OFFSET: usize = 7; // 1 diagnostic + 5 linenr + 1 gutter
88+ let last_col = self.first_col + (self.area.width as usize - OFFSET);
82
89
83− if line > document_end.saturating_sub(PADDING) {
90+ if line > last_line.saturating_sub(PADDING) {
84 // scroll down
91 // scroll down
85− self.first_line += line - (document_end.saturating_sub(PADDING));
92+ self.first_line += line - (last_line.saturating_sub(PADDING));
86 } else if line < self.first_line + PADDING {
93 } else if line < self.first_line + PADDING {
87 // scroll up
94 // scroll up
88 self.first_line = line.saturating_sub(PADDING);
95 self.first_line = line.saturating_sub(PADDING);
89 }
96 }
97+
98+ if col > last_col.saturating_sub(PADDING) {
99+ // scroll right
100+ self.first_col += col - (last_col.saturating_sub(PADDING));
101+ } else if col < self.first_col + PADDING {
102+ // scroll left
103+ self.first_col = col.saturating_sub(PADDING);
104+ }
90 }
105 }
91
106
92 /// Calculates the last visible line on screen
107 /// Calculates the last visible line on screen
@@ -130,6 +145,7 @@ impl View {
130 }
145 }
131
146
132 let row = line - self.first_line as usize;
147 let row = line - self.first_line as usize;
148+ let col = col - self.first_col as usize;
133
149
134 Some(Position::new(row, col))
150 Some(Position::new(row, col))
135 }
151 }