@@ -1,8 +1,4 @@
1−use crate::{
2− graphics::Rect,
3− gutter::{self, Gutter},
4− Document, DocumentId, ViewId,
5−};
1+use crate::{editor::GutterType, graphics::Rect, Document, DocumentId, ViewId};
62use helix_core::{
73 pos_at_visual_coords, visual_coords_at_pos, Position, RopeSlice, Selection, Transaction,
84};
@@ -98,11 +94,8 @@ pub struct View {
9894 pub last_modified_docs: [Option<DocumentId>; 2],
9995 /// used to store previous selections of tree-sitter objects
10096 pub object_selections: Vec<Selection>,
101− /// Gutter (constructor) and width of gutter, used to calculate
102− /// `gutter_offset`
103− gutters: Vec<(Gutter, usize)>,
104− /// cached total width of gutter
105− gutter_offset: u16,
97+ /// GutterTypes used to fetch Gutter (constructor) and width for rendering
98+ gutters: Vec<GutterType>,
10699}
107100
108101impl fmt::Debug for View {
@@ -117,28 +110,6 @@ impl fmt::Debug for View {
117110
118111impl View {
119112 pub fn new(doc: DocumentId, gutter_types: Vec<crate::editor::GutterType>) -> Self {
120− let mut gutters: Vec<(Gutter, usize)> = vec![];
121− let mut gutter_offset = 0;
122− use crate::editor::GutterType;
123− for gutter_type in &gutter_types {
124− let width = match gutter_type {
125− GutterType::Diagnostics => 1,
126− GutterType::LineNumbers => 5,
127− GutterType::Spacer => 1,
128− };
129− gutter_offset += width;
130− gutters.push((
131− match gutter_type {
132− GutterType::Diagnostics => gutter::diagnostics_or_breakpoints,
133− GutterType::LineNumbers => gutter::line_numbers,
134− GutterType::Spacer => gutter::padding,
135− },
136− width as usize,
137− ));
138− }
139− if !gutter_types.is_empty() {
140− gutter_offset += 1;
141− }
142113 Self {
143114 id: ViewId::default(),
144115 doc,
@@ -148,8 +119,7 @@ impl View {
148119 docs_access_history: Vec::new(),
149120 last_modified_docs: [None, None],
150121 object_selections: Vec::new(),
151− gutters,
152− gutter_offset,
122+ gutters: gutter_types,
153123 }
154124 }
155125
@@ -160,15 +130,32 @@ impl View {
160130 self.docs_access_history.push(id);
161131 }
162132
163− pub fn inner_area(&self) -> Rect {
164− // TODO add abilty to not use cached offset for runtime configurable gutter
165− self.area.clip_left(self.gutter_offset).clip_bottom(1) // -1 for statusline
133+ pub fn inner_area(&self, doc: &Document) -> Rect {
134+ self.area.clip_left(self.gutter_offset(doc)).clip_bottom(1) // -1 for statusline
135+ }
136+
137+ pub fn inner_height(&self) -> usize {
138+ self.area.clip_bottom(1).height.into() // -1 for statusline
166139 }
167140
168− pub fn gutters(&self) -> &[(Gutter, usize)] {
141+ pub fn gutters(&self) -> &[GutterType] {
169142 &self.gutters
170143 }
171144
145+ pub fn gutter_offset(&self, doc: &Document) -> u16 {
146+ let mut offset = self
147+ .gutters
148+ .iter()
149+ .map(|gutter| gutter.width(self, doc) as u16)
150+ .sum();
151+
152+ if offset > 0 {
153+ offset += 1
154+ }
155+
156+ offset
157+ }
158+
172159 //
173160 pub fn offset_coords_to_in_view(
174161 &self,
@@ -183,7 +170,7 @@ impl View {
183170 let Position { col, row: line } =
184171 visual_coords_at_pos(doc.text().slice(..), cursor, doc.tab_width());
185172
186− let inner_area = self.inner_area();
173+ let inner_area = self.inner_area(doc);
187174 let last_line = (self.offset.row + inner_area.height as usize).saturating_sub(1);
188175
189176 // - 1 so we have at least one gap in the middle.
@@ -233,10 +220,9 @@ impl View {
233220 /// Calculates the last visible line on screen
234221 #[inline]
235222 pub fn last_line(&self, doc: &Document) -> usize {
236− let height = self.inner_area().height;
237223 std::cmp::min(
238224 // Saturating subs to make it inclusive zero indexing.
239− (self.offset.row + height as usize).saturating_sub(1),
225+ (self.offset.row + self.inner_height()).saturating_sub(1),
240226 doc.text().len_lines().saturating_sub(1),
241227 )
242228 }
@@ -270,12 +256,13 @@ impl View {
270256
271257 pub fn text_pos_at_screen_coords(
272258 &self,
273− text: &RopeSlice,
259+ doc: &Document,
274260 row: u16,
275261 column: u16,
276262 tab_width: usize,
277263 ) -> Option<usize> {
278− let inner = self.inner_area();
264+ let text = doc.text().slice(..);
265+ let inner = self.inner_area(doc);
279266 // 1 for status
280267 if row < inner.top() || row >= inner.bottom() {
281268 return None;
@@ -293,7 +280,7 @@ impl View {
293280 let text_col = (column - inner.x) as usize + self.offset.col;
294281
295282 Some(pos_at_visual_coords(
296− *text,
283+ text,
297284 Position {
298285 row: text_row,
299286 col: text_col,
@@ -305,7 +292,7 @@ impl View {
305292 /// Translates a screen position to position in the text document.
306293 /// Returns a usize typed position in bounds of the text if found in this view, None if out of view.
307294 pub fn pos_at_screen_coords(&self, doc: &Document, row: u16, column: u16) -> Option<usize> {
308− self.text_pos_at_screen_coords(&doc.text().slice(..), row, column, doc.tab_width())
295+ self.text_pos_at_screen_coords(doc, row, column, doc.tab_width())
309296 }
310297
311298 /// Translates screen coordinates into coordinates on the gutter of the view.
@@ -366,9 +353,10 @@ impl View {
366353mod tests {
367354 use super::*;
368355 use helix_core::Rope;
369− const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
356+ const OFFSET: u16 = 4; // 1 diagnostic + 2 linenr (< 100 lines) + 1 gutter
370357 const OFFSET_WITHOUT_LINE_NUMBERS: u16 = 2; // 1 diagnostic + 1 gutter
371358 // const OFFSET: u16 = GUTTERS.iter().map(|(_, width)| *width as u16).sum();
359+ use crate::document::Document;
372360 use crate::editor::GutterType;
373361
374362 #[test]
@@ -379,45 +367,45 @@ mod tests {
379367 );
380368 view.area = Rect::new(40, 40, 40, 40);
381369 let rope = Rope::from_str("abc\n\tdef");
382− let text = rope.slice(..);
370+ let doc = Document::from(rope, None);
383371
384− assert_eq!(view.text_pos_at_screen_coords(&text, 40, 2, 4), None);
372+ assert_eq!(view.text_pos_at_screen_coords(&doc, 40, 2, 4), None);
385373
386− assert_eq!(view.text_pos_at_screen_coords(&text, 40, 41, 4), None);
374+ assert_eq!(view.text_pos_at_screen_coords(&doc, 40, 41, 4), None);
387375
388− assert_eq!(view.text_pos_at_screen_coords(&text, 0, 2, 4), None);
376+ assert_eq!(view.text_pos_at_screen_coords(&doc, 0, 2, 4), None);
389377
390− assert_eq!(view.text_pos_at_screen_coords(&text, 0, 49, 4), None);
378+ assert_eq!(view.text_pos_at_screen_coords(&doc, 0, 49, 4), None);
391379
392− assert_eq!(view.text_pos_at_screen_coords(&text, 0, 41, 4), None);
380+ assert_eq!(view.text_pos_at_screen_coords(&doc, 0, 41, 4), None);
393381
394− assert_eq!(view.text_pos_at_screen_coords(&text, 40, 81, 4), None);
382+ assert_eq!(view.text_pos_at_screen_coords(&doc, 40, 81, 4), None);
395383
396− assert_eq!(view.text_pos_at_screen_coords(&text, 78, 41, 4), None);
384+ assert_eq!(view.text_pos_at_screen_coords(&doc, 78, 41, 4), None);
397385
398386 assert_eq!(
399− view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET + 3, 4),
387+ view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 3, 4),
400388 Some(3)
401389 );
402390
403− assert_eq!(view.text_pos_at_screen_coords(&text, 40, 80, 4), Some(3));
391+ assert_eq!(view.text_pos_at_screen_coords(&doc, 40, 80, 4), Some(3));
404392
405393 assert_eq!(
406− view.text_pos_at_screen_coords(&text, 41, 40 + OFFSET + 1, 4),
394+ view.text_pos_at_screen_coords(&doc, 41, 40 + OFFSET + 1, 4),
407395 Some(4)
408396 );
409397
410398 assert_eq!(
411− view.text_pos_at_screen_coords(&text, 41, 40 + OFFSET + 4, 4),
399+ view.text_pos_at_screen_coords(&doc, 41, 40 + OFFSET + 4, 4),
412400 Some(5)
413401 );
414402
415403 assert_eq!(
416− view.text_pos_at_screen_coords(&text, 41, 40 + OFFSET + 7, 4),
404+ view.text_pos_at_screen_coords(&doc, 41, 40 + OFFSET + 7, 4),
417405 Some(8)
418406 );
419407
420− assert_eq!(view.text_pos_at_screen_coords(&text, 41, 80, 4), Some(8));
408+ assert_eq!(view.text_pos_at_screen_coords(&doc, 41, 80, 4), Some(8));
421409 }
422410
423411 #[test]
@@ -425,9 +413,9 @@ mod tests {
425413 let mut view = View::new(DocumentId::default(), vec![GutterType::Diagnostics]);
426414 view.area = Rect::new(40, 40, 40, 40);
427415 let rope = Rope::from_str("abc\n\tdef");
428− let text = rope.slice(..);
416+ let doc = Document::from(rope, None);
429417 assert_eq!(
430− view.text_pos_at_screen_coords(&text, 41, 40 + OFFSET_WITHOUT_LINE_NUMBERS + 1, 4),
418+ view.text_pos_at_screen_coords(&doc, 41, 40 + OFFSET_WITHOUT_LINE_NUMBERS + 1, 4),
431419 Some(4)
432420 );
433421 }
@@ -437,11 +425,8 @@ mod tests {
437425 let mut view = View::new(DocumentId::default(), vec![]);
438426 view.area = Rect::new(40, 40, 40, 40);
439427 let rope = Rope::from_str("abc\n\tdef");
440− let text = rope.slice(..);
441− assert_eq!(
442− view.text_pos_at_screen_coords(&text, 41, 40 + 1, 4),
443− Some(4)
444− );
428+ let doc = Document::from(rope, None);
429+ assert_eq!(view.text_pos_at_screen_coords(&doc, 41, 40 + 1, 4), Some(4));
445430 }
446431
447432 #[test]
@@ -452,34 +437,34 @@ mod tests {
452437 );
453438 view.area = Rect::new(40, 40, 40, 40);
454439 let rope = Rope::from_str("Hi! こんにちは皆さん");
455− let text = rope.slice(..);
440+ let doc = Document::from(rope, None);
456441
457442 assert_eq!(
458− view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET, 4),
443+ view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET, 4),
459444 Some(0)
460445 );
461446
462447 assert_eq!(
463− view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET + 4, 4),
448+ view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 4, 4),
464449 Some(4)
465450 );
466451 assert_eq!(
467− view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET + 5, 4),
452+ view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 5, 4),
468453 Some(4)
469454 );
470455
471456 assert_eq!(
472− view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET + 6, 4),
457+ view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 6, 4),
473458 Some(5)
474459 );
475460
476461 assert_eq!(
477− view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET + 7, 4),
462+ view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 7, 4),
478463 Some(5)
479464 );
480465
481466 assert_eq!(
482− view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET + 8, 4),
467+ view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 8, 4),
483468 Some(6)
484469 );
485470 }
@@ -492,30 +477,30 @@ mod tests {
492477 );
493478 view.area = Rect::new(40, 40, 40, 40);
494479 let rope = Rope::from_str("Hèl̀l̀ò world!");
495− let text = rope.slice(..);
480+ let doc = Document::from(rope, None);
496481
497482 assert_eq!(
498− view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET, 4),
483+ view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET, 4),
499484 Some(0)
500485 );
501486
502487 assert_eq!(
503− view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET + 1, 4),
488+ view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 1, 4),
504489 Some(1)
505490 );
506491
507492 assert_eq!(
508− view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET + 2, 4),
493+ view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 2, 4),
509494 Some(3)
510495 );
511496
512497 assert_eq!(
513− view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET + 3, 4),
498+ view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 3, 4),
514499 Some(5)
515500 );
516501
517502 assert_eq!(
518− view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET + 4, 4),
503+ view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 4, 4),
519504 Some(7)
520505 );
521506 }