on master help sign in
Dynamically resize line number gutter width (#3469)

* dynamically resize line number gutter width * removing digits lower-bound, permitting spacer * removing max line num char limit; adding notes; qualified successors; notes * updating tests to use new line number width when testing views * linenr width based on document line count * using min width of 2 so line numbers relative is useful * lint rolling; removing unnecessary type parameter lifetime * merge change resolution * reformat code * rename row_styler to style; add int_log resource * adding spacer to gutters default; updating book config entry * adding view.inner_height(), swap for loop for iterator * reverting change of current! to view! now that doc is not needed

commit7ed9e9cf2567ee5e23cd8694ffccb4b38602c02a authorDoug Kelkhoff <18220321+dgkf@users.noreply.github.com> dateTue 8 Nov 2022, 07:19 (4 years ago) parentc94feed
8 files changed+143−109
book/src/configuration.md+1−1 helix-term/src/commands.rs+8−8 helix-term/src/ui/editor.rs+9−8 helix-view/src/editor.rs+7−2 helix-view/src/gutter.rs+50−7 helix-view/src/lib.rs+1−1 helix-view/src/tree.rs+2−2 helix-view/src/view.rs+65−80
book/src/configuration.md+1−1
@@ -46,7 +46,7 @@ on unix operating systems.
4646| `line-number` | Line number display: `absolute` simply shows each line's number, while `relative` shows the distance from the current line. When unfocused or in insert mode, `relative` will still show absolute line numbers. | `absolute` |
4747| `cursorline` | Highlight all lines with a cursor. | `false` |
4848| `cursorcolumn` | Highlight all columns with a cursor. | `false` |
49−| `gutters` | Gutters to display: Available are `diagnostics` and `line-numbers` and `spacer`, note that `diagnostics` also includes other features like breakpoints, 1-width padding will be inserted if gutters is non-empty | `["diagnostics", "line-numbers"]` |
49+| `gutters` | Gutters to display: Available are `diagnostics` and `line-numbers` and `spacer`, note that `diagnostics` also includes other features like breakpoints, 1-width padding will be inserted if gutters is non-empty | `["diagnostics", "spacer", "line-numbers"]` |
5050| `auto-completion` | Enable automatic pop up of auto-completion. | `true` |
5151| `auto-format` | Enable automatic formatting on save. | `true` |
5252| `auto-save` | Enable automatic saving on focus moving away from Helix. Requires [focus event support](https://github.com/helix-editor/helix/wiki/Terminal-Support) from your terminal. | `false` |
helix-term/src/commands.rs+8−8
@@ -870,7 +870,7 @@ fn goto_window(cx: &mut Context, align: Align) {
870870 let config = cx.editor.config();
871871 let (view, doc) = current!(cx.editor);
872872
873− let height = view.inner_area().height as usize;
873+ let height = view.inner_height();
874874
875875 // respect user given count if any
876876 // - 1 so we have at least one gap in the middle.
@@ -1372,9 +1372,9 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
13721372 return;
13731373 }
13741374
1375− let height = view.inner_area().height;
1375+ let height = view.inner_height();
13761376
1377− let scrolloff = config.scrolloff.min(height as usize / 2);
1377+ let scrolloff = config.scrolloff.min(height / 2);
13781378
13791379 view.offset.row = match direction {
13801380 Forward => view.offset.row + offset,
@@ -1412,25 +1412,25 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
14121412
14131413fn page_up(cx: &mut Context) {
14141414 let view = view!(cx.editor);
1415− let offset = view.inner_area().height as usize;
1415+ let offset = view.inner_height();
14161416 scroll(cx, offset, Direction::Backward);
14171417}
14181418
14191419fn page_down(cx: &mut Context) {
14201420 let view = view!(cx.editor);
1421− let offset = view.inner_area().height as usize;
1421+ let offset = view.inner_height();
14221422 scroll(cx, offset, Direction::Forward);
14231423}
14241424
14251425fn half_page_up(cx: &mut Context) {
14261426 let view = view!(cx.editor);
1427− let offset = view.inner_area().height as usize / 2;
1427+ let offset = view.inner_height() / 2;
14281428 scroll(cx, offset, Direction::Backward);
14291429}
14301430
14311431fn half_page_down(cx: &mut Context) {
14321432 let view = view!(cx.editor);
1433− let offset = view.inner_area().height as usize / 2;
1433+ let offset = view.inner_height() / 2;
14341434 scroll(cx, offset, Direction::Forward);
14351435}
14361436
@@ -4342,7 +4342,7 @@ fn align_view_middle(cx: &mut Context) {
43424342
43434343 view.offset.col = pos
43444344 .col
4345− .saturating_sub((view.inner_area().width as usize) / 2);
4345+ .saturating_sub((view.inner_area(doc).width as usize) / 2);
43464346}
43474347
43484348fn scroll_up(cx: &mut Context) {
helix-term/src/ui/editor.rs+9−8
@@ -79,7 +79,7 @@ impl EditorView {
7979 surface: &mut Surface,
8080 is_focused: bool,
8181 ) {
82− let inner = view.inner_area();
82+ let inner = view.inner_area(doc);
8383 let area = view.area;
8484 let theme = &editor.theme;
8585
@@ -736,9 +736,10 @@ impl EditorView {
736736 // avoid lots of small allocations by reusing a text buffer for each line
737737 let mut text = String::with_capacity(8);
738738
739− for (constructor, width) in view.gutters() {
740− let gutter = constructor(editor, doc, view, theme, is_focused, *width);
741− text.reserve(*width); // ensure there's enough space for the gutter
739+ for gutter_type in view.gutters() {
740+ let gutter = gutter_type.style(editor, doc, view, theme, is_focused);
741+ let width = gutter_type.width(view, doc);
742+ text.reserve(width); // ensure there's enough space for the gutter
742743 for (i, line) in (view.offset.row..(last_line + 1)).enumerate() {
743744 let selected = cursors.contains(&line);
744745 let x = viewport.x + offset;
@@ -751,13 +752,13 @@ impl EditorView {
751752 };
752753
753754 if let Some(style) = gutter(line, selected, &mut text) {
754− surface.set_stringn(x, y, &text, *width, gutter_style.patch(style));
755+ surface.set_stringn(x, y, &text, width, gutter_style.patch(style));
755756 } else {
756757 surface.set_style(
757758 Rect {
758759 x,
759760 y,
760− width: *width as u16,
761+ width: width as u16,
761762 height: 1,
762763 },
763764 gutter_style,
@@ -766,7 +767,7 @@ impl EditorView {
766767 text.clear();
767768 }
768769
769− offset += *width as u16;
770+ offset += width as u16;
770771 }
771772 }
772773
@@ -882,7 +883,7 @@ impl EditorView {
882883 .or_else(|| theme.try_get_exact("ui.cursorcolumn"))
883884 .unwrap_or_else(|| theme.get("ui.cursorline.secondary"));
884885
885− let inner_area = view.inner_area();
886+ let inner_area = view.inner_area(doc);
886887 let offset = view.offset.col;
887888
888889 let selection = doc.selection(view.id);
helix-view/src/editor.rs+7−2
@@ -456,6 +456,7 @@ impl std::str::FromStr for GutterType {
456456 fn from_str(s: &str) -> Result<Self, Self::Err> {
457457 match s.to_lowercase().as_str() {
458458 "diagnostics" => Ok(Self::Diagnostics),
459+ "spacer" => Ok(Self::Spacer),
459460 "line-numbers" => Ok(Self::LineNumbers),
460461 _ => anyhow::bail!("Gutter type can only be `diagnostics` or `line-numbers`."),
461462 }
@@ -589,7 +590,11 @@ impl Default for Config {
589590 line_number: LineNumber::Absolute,
590591 cursorline: false,
591592 cursorcolumn: false,
592− gutters: vec![GutterType::Diagnostics, GutterType::LineNumbers],
593+ gutters: vec![
594+ GutterType::Diagnostics,
595+ GutterType::Spacer,
596+ GutterType::LineNumbers,
597+ ],
593598 middle_click_paste: true,
594599 auto_pairs: AutoPairConfig::default(),
595600 auto_completion: true,
@@ -1308,7 +1313,7 @@ impl Editor {
13081313 .primary()
13091314 .cursor(doc.text().slice(..));
13101315 if let Some(mut pos) = view.screen_coords_at_pos(doc, doc.text().slice(..), cursor) {
1311− let inner = view.inner_area();
1316+ let inner = view.inner_area(doc);
13121317 pos.col += inner.x as usize;
13131318 pos.row += inner.y as usize;
13141319 let cursorkind = config.cursor_shape.from_mode(self.mode);
helix-view/src/gutter.rs+50−7
@@ -1,21 +1,54 @@
11use std::fmt::Write;
22
33use crate::{
4+ editor::GutterType,
45 graphics::{Color, Style, UnderlineStyle},
56 Document, Editor, Theme, View,
67};
78
9+fn count_digits(n: usize) -> usize {
10+ // NOTE: if int_log gets standardized in stdlib, can use checked_log10
11+ // (https://github.com/rust-lang/rust/issues/70887#issue)
12+ std::iter::successors(Some(n), |&n| (n >= 10).then(|| n / 10)).count()
13+}
14+
815pub type GutterFn<'doc> = Box<dyn Fn(usize, bool, &mut String) -> Option<Style> + 'doc>;
916pub type Gutter =
1017 for<'doc> fn(&'doc Editor, &'doc Document, &View, &Theme, bool, usize) -> GutterFn<'doc>;
1118
19+impl GutterType {
20+ pub fn style<'doc>(
21+ self,
22+ editor: &'doc Editor,
23+ doc: &'doc Document,
24+ view: &View,
25+ theme: &Theme,
26+ is_focused: bool,
27+ ) -> GutterFn<'doc> {
28+ match self {
29+ GutterType::Diagnostics => {
30+ diagnostics_or_breakpoints(editor, doc, view, theme, is_focused)
31+ }
32+ GutterType::LineNumbers => line_numbers(editor, doc, view, theme, is_focused),
33+ GutterType::Spacer => padding(editor, doc, view, theme, is_focused),
34+ }
35+ }
36+
37+ pub fn width(self, _view: &View, doc: &Document) -> usize {
38+ match self {
39+ GutterType::Diagnostics => 1,
40+ GutterType::LineNumbers => line_numbers_width(_view, doc),
41+ GutterType::Spacer => 1,
42+ }
43+ }
44+}
45+
1246pub fn diagnostic<'doc>(
1347 _editor: &'doc Editor,
1448 doc: &'doc Document,
1549 _view: &View,
1650 theme: &Theme,
1751 _is_focused: bool,
18− _width: usize,
1952) -> GutterFn<'doc> {
2053 let warning = theme.get("warning");
2154 let error = theme.get("error");
@@ -56,10 +89,11 @@ pub fn line_numbers<'doc>(
5689 view: &View,
5790 theme: &Theme,
5891 is_focused: bool,
59− width: usize,
6092) -> GutterFn<'doc> {
6193 let text = doc.text().slice(..);
6294 let last_line = view.last_line(doc);
95+ let width = GutterType::LineNumbers.width(view, doc);
96+
6397 // Whether to draw the line number for the last line of the
6498 // document or not. We only draw it if it's not an empty line.
6599 let draw_last = text.line_to_byte(last_line) < text.len_bytes();
@@ -91,24 +125,35 @@ pub fn line_numbers<'doc>(
91125 } else {
92126 line + 1
93127 };
128+
94129 let style = if selected && is_focused {
95130 linenr_select
96131 } else {
97132 linenr
98133 };
134+
99135 write!(out, "{:>1$}", display_num, width).unwrap();
100136 Some(style)
101137 }
102138 })
103139}
104140
141+pub fn line_numbers_width(_view: &View, doc: &Document) -> usize {
142+ let text = doc.text();
143+ let last_line = text.len_lines().saturating_sub(1);
144+ let draw_last = text.line_to_byte(last_line) < text.len_bytes();
145+ let last_drawn = if draw_last { last_line + 1 } else { last_line };
146+
147+ // set a lower bound to 2-chars to minimize ambiguous relative line numbers
148+ std::cmp::max(count_digits(last_drawn), 2)
149+}
150+
105151pub fn padding<'doc>(
106152 _editor: &'doc Editor,
107153 _doc: &'doc Document,
108154 _view: &View,
109155 _theme: &Theme,
110156 _is_focused: bool,
111− _width: usize,
112157) -> GutterFn<'doc> {
113158 Box::new(|_line: usize, _selected: bool, _out: &mut String| None)
114159}
@@ -128,7 +173,6 @@ pub fn breakpoints<'doc>(
128173 _view: &View,
129174 theme: &Theme,
130175 _is_focused: bool,
131− _width: usize,
132176) -> GutterFn<'doc> {
133177 let warning = theme.get("warning");
134178 let error = theme.get("error");
@@ -181,10 +225,9 @@ pub fn diagnostics_or_breakpoints<'doc>(
181225 view: &View,
182226 theme: &Theme,
183227 is_focused: bool,
184− width: usize,
185228) -> GutterFn<'doc> {
186− let diagnostics = diagnostic(editor, doc, view, theme, is_focused, width);
187− let breakpoints = breakpoints(editor, doc, view, theme, is_focused, width);
229+ let diagnostics = diagnostic(editor, doc, view, theme, is_focused);
230+ let breakpoints = breakpoints(editor, doc, view, theme, is_focused);
188231
189232 Box::new(move |line, selected, out| {
190233 breakpoints(line, selected, out).or_else(|| diagnostics(line, selected, out))
helix-view/src/lib.rs+1−1
@@ -55,7 +55,7 @@ pub fn align_view(doc: &Document, view: &mut View, align: Align) {
5555 .cursor(doc.text().slice(..));
5656 let line = doc.text().char_to_line(pos);
5757
58− let last_line_height = view.inner_area().height.saturating_sub(1) as usize;
58+ let last_line_height = view.inner_height().saturating_sub(1);
5959
6060 let relative = match align {
6161 Align::Center => last_line_height / 2,
helix-view/src/tree.rs+2−2
@@ -499,7 +499,7 @@ impl Tree {
499499 // in a vertical container (and already correct based on previous search)
500500 child_id = *container.children.iter().min_by_key(|id| {
501501 let x = match &self.nodes[**id].content {
502− Content::View(view) => view.inner_area().left(),
502+ Content::View(view) => view.area.left(),
503503 Content::Container(container) => container.area.left(),
504504 };
505505 (current_x as i16 - x as i16).abs()
@@ -510,7 +510,7 @@ impl Tree {
510510 // in a horizontal container (and already correct based on previous search)
511511 child_id = *container.children.iter().min_by_key(|id| {
512512 let y = match &self.nodes[**id].content {
513− Content::View(view) => view.inner_area().top(),
513+ Content::View(view) => view.area.top(),
514514 Content::Container(container) => container.area.top(),
515515 };
516516 (current_y as i16 - y as i16).abs()
helix-view/src/view.rs+65−80
@@ -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 }