on master help sign in
Add configuration for min width of line-numbers gutter (#4724)
2b58ff4Doug Kelkhoff
book/src/configuration.md+52
@@ -256,3 +256,55 @@ render = true
256character = "╎" # Some characters that work well: "▏", "┆", "┊", "⸽"
256character = "╎" # Some characters that work well: "▏", "┆", "┊", "⸽"
257skip-levels = 1
257skip-levels = 1
258```
258```
259+
260+### `[editor.gutters]` Section
261+
262+For simplicity, `editor.gutters` accepts an array of gutter types, which will
263+use default settings for all gutter components.
264+
265+```toml
266+[editor]
267+gutters = ["diff", "diagnostics", "line-numbers", "spacer"]
268+```
269+
270+To customize the behavior of gutters, the `[editor.gutters]` section must
271+be used. This section contains top level settings, as well as settings for
272+specific gutter components as sub-sections.
273+
274+| Key | Description | Default |
275+| --- | --- | --- |
276+| `layout` | A vector of gutters to display | `["diagnostics", "spacer", "line-numbers", "spacer", "diff"]` |
277+
278+Example:
279+
280+```toml
281+[editor.gutters]
282+layout = ["diff", "diagnostics", "line-numbers", "spacer"]
283+```
284+
285+#### `[editor.gutters.line-numbers]` Section
286+
287+Options for the line number gutter
288+
289+| Key | Description | Default |
290+| --- | --- | --- |
291+| `min-width` | The minimum number of characters to use | `3` |
292+
293+Example:
294+
295+```toml
296+[editor.gutters.line-numbers]
297+min-width = 1
298+```
299+
300+#### `[editor.gutters.diagnotics]` Section
301+
302+Currently unused
303+
304+#### `[editor.gutters.diff]` Section
305+
306+Currently unused
307+
308+#### `[editor.gutters.spacer]` Section
309+
310+Currently unused
helix-view/src/editor.rs+95−9
@@ -71,6 +71,96 @@ where
71 )
71 )
72}
72}
73
73
74+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
75+#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
76+pub struct GutterConfig {
77+ /// Gutter Layout
78+ pub layout: Vec<GutterType>,
79+ /// Options specific to the "line-numbers" gutter
80+ pub line_numbers: GutterLineNumbersConfig,
81+}
82+
83+impl Default for GutterConfig {
84+ fn default() -> Self {
85+ Self {
86+ layout: vec![
87+ GutterType::Diagnostics,
88+ GutterType::Spacer,
89+ GutterType::LineNumbers,
90+ GutterType::Spacer,
91+ GutterType::Diff,
92+ ],
93+ line_numbers: GutterLineNumbersConfig::default(),
94+ }
95+ }
96+}
97+
98+impl From<Vec<GutterType>> for GutterConfig {
99+ fn from(x: Vec<GutterType>) -> Self {
100+ GutterConfig {
101+ layout: x,
102+ ..Default::default()
103+ }
104+ }
105+}
106+
107+fn deserialize_gutter_seq_or_struct<'de, D>(deserializer: D) -> Result<GutterConfig, D::Error>
108+where
109+ D: Deserializer<'de>,
110+{
111+ struct GutterVisitor;
112+
113+ impl<'de> serde::de::Visitor<'de> for GutterVisitor {
114+ type Value = GutterConfig;
115+
116+ fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
117+ write!(
118+ formatter,
119+ "an array of gutter names or a detailed gutter configuration"
120+ )
121+ }
122+
123+ fn visit_seq<S>(self, mut seq: S) -> Result<Self::Value, S::Error>
124+ where
125+ S: serde::de::SeqAccess<'de>,
126+ {
127+ let mut gutters = Vec::new();
128+ while let Some(gutter) = seq.next_element::<&str>()? {
129+ gutters.push(
130+ gutter
131+ .parse::<GutterType>()
132+ .map_err(serde::de::Error::custom)?,
133+ )
134+ }
135+
136+ Ok(gutters.into())
137+ }
138+
139+ fn visit_map<M>(self, map: M) -> Result<Self::Value, M::Error>
140+ where
141+ M: serde::de::MapAccess<'de>,
142+ {
143+ let deserializer = serde::de::value::MapAccessDeserializer::new(map);
144+ Deserialize::deserialize(deserializer)
145+ }
146+ }
147+
148+ deserializer.deserialize_any(GutterVisitor)
149+}
150+
151+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
152+#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
153+pub struct GutterLineNumbersConfig {
154+ /// Minimum number of characters to use for line number gutter. Defaults to 3.
155+ pub min_width: usize,
156+}
157+
158+impl Default for GutterLineNumbersConfig {
159+ fn default() -> Self {
160+ Self { min_width: 3 }
161+ }
162+}
163+
74#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
164#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
75#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
165#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
76pub struct FilePickerConfig {
166pub struct FilePickerConfig {
@@ -132,8 +222,8 @@ pub struct Config {
132 pub cursorline: bool,
222 pub cursorline: bool,
133 /// Highlight the columns cursors are currently on. Defaults to false.
223 /// Highlight the columns cursors are currently on. Defaults to false.
134 pub cursorcolumn: bool,
224 pub cursorcolumn: bool,
135− /// Gutters. Default ["diagnostics", "line-numbers"]
225+ #[serde(deserialize_with = "deserialize_gutter_seq_or_struct")]
136− pub gutters: Vec<GutterType>,
226+ pub gutters: GutterConfig,
137 /// Middle click paste support. Defaults to true.
227 /// Middle click paste support. Defaults to true.
138 pub middle_click_paste: bool,
228 pub middle_click_paste: bool,
139 /// Automatic insertion of pairs to parentheses, brackets,
229 /// Automatic insertion of pairs to parentheses, brackets,
@@ -606,13 +696,7 @@ impl Default for Config {
606 line_number: LineNumber::Absolute,
696 line_number: LineNumber::Absolute,
607 cursorline: false,
697 cursorline: false,
608 cursorcolumn: false,
698 cursorcolumn: false,
609− gutters: vec![
699+ gutters: GutterConfig::default(),
610− GutterType::Diagnostics,
611− GutterType::Spacer,
612− GutterType::LineNumbers,
613− GutterType::Spacer,
614− GutterType::Diff,
615− ],
616 middle_click_paste: true,
700 middle_click_paste: true,
617 auto_pairs: AutoPairConfig::default(),
701 auto_pairs: AutoPairConfig::default(),
618 auto_completion: true,
702 auto_completion: true,
@@ -844,6 +928,7 @@ impl Editor {
844 let config = self.config();
928 let config = self.config();
845 self.auto_pairs = (&config.auto_pairs).into();
929 self.auto_pairs = (&config.auto_pairs).into();
846 self.reset_idle_timer();
930 self.reset_idle_timer();
931+ self._refresh();
847 }
932 }
848
933
849 pub fn clear_idle_timer(&mut self) {
934 pub fn clear_idle_timer(&mut self) {
@@ -984,6 +1069,7 @@ impl Editor {
984 for (view, _) in self.tree.views_mut() {
1069 for (view, _) in self.tree.views_mut() {
985 let doc = doc_mut!(self, &view.doc);
1070 let doc = doc_mut!(self, &view.doc);
986 view.sync_changes(doc);
1071 view.sync_changes(doc);
1072+ view.gutters = config.gutters.clone();
987 view.ensure_cursor_in_view(doc, config.scrolloff)
1073 view.ensure_cursor_in_view(doc, config.scrolloff)
988 }
1074 }
989 }
1075 }
helix-view/src/gutter.rs+95−10
@@ -35,10 +35,10 @@ impl GutterType {
35 }
35 }
36 }
36 }
37
37
38− pub fn width(self, _view: &View, doc: &Document) -> usize {
38+ pub fn width(self, view: &View, doc: &Document) -> usize {
39 match self {
39 match self {
40 GutterType::Diagnostics => 1,
40 GutterType::Diagnostics => 1,
41− GutterType::LineNumbers => line_numbers_width(_view, doc),
41+ GutterType::LineNumbers => line_numbers_width(view, doc),
42 GutterType::Spacer => 1,
42 GutterType::Spacer => 1,
43 GutterType::Diff => 1,
43 GutterType::Diff => 1,
44 }
44 }
@@ -140,12 +140,13 @@ pub fn line_numbers<'doc>(
140 is_focused: bool,
140 is_focused: bool,
141) -> GutterFn<'doc> {
141) -> GutterFn<'doc> {
142 let text = doc.text().slice(..);
142 let text = doc.text().slice(..);
143− let last_line = view.last_line(doc);
143+ let width = line_numbers_width(view, doc);
144− let width = GutterType::LineNumbers.width(view, doc);
144+
145+ let last_line_in_view = view.last_line(doc);
145
146
146 // Whether to draw the line number for the last line of the
147 // Whether to draw the line number for the last line of the
147 // document or not. We only draw it if it's not an empty line.
148 // document or not. We only draw it if it's not an empty line.
148− let draw_last = text.line_to_byte(last_line) < text.len_bytes();
149+ let draw_last = text.line_to_byte(last_line_in_view) < text.len_bytes();
149
150
150 let linenr = theme.get("ui.linenr");
151 let linenr = theme.get("ui.linenr");
151 let linenr_select = theme.get("ui.linenr.selected");
152 let linenr_select = theme.get("ui.linenr.selected");
@@ -158,7 +159,7 @@ pub fn line_numbers<'doc>(
158 let mode = editor.mode;
159 let mode = editor.mode;
159
160
160 Box::new(move |line: usize, selected: bool, out: &mut String| {
161 Box::new(move |line: usize, selected: bool, out: &mut String| {
161− if line == last_line && !draw_last {
162+ if line == last_line_in_view && !draw_last {
162 write!(out, "{:>1$}", '~', width).unwrap();
163 write!(out, "{:>1$}", '~', width).unwrap();
163 Some(linenr)
164 Some(linenr)
164 } else {
165 } else {
@@ -187,14 +188,19 @@ pub fn line_numbers<'doc>(
187 })
188 })
188}
189}
189
190
191+/// The width of a "line-numbers" gutter
192+///
193+/// The width of the gutter depends on the number of lines in the document,
194+/// whether there is content on the last line (the `~` line), and the
195+/// `editor.gutters.line-numbers.min-width` settings.
190−pub fn line_numbers_width(_view: &View, doc: &Document) -> usize {
196+fn line_numbers_width(view: &View, doc: &Document) -> usize {
191 let text = doc.text();
197 let text = doc.text();
192 let last_line = text.len_lines().saturating_sub(1);
198 let last_line = text.len_lines().saturating_sub(1);
193 let draw_last = text.line_to_byte(last_line) < text.len_bytes();
199 let draw_last = text.line_to_byte(last_line) < text.len_bytes();
194 let last_drawn = if draw_last { last_line + 1 } else { last_line };
200 let last_drawn = if draw_last { last_line + 1 } else { last_line };
195−
196− // set a lower bound to 2-chars to minimize ambiguous relative line numbers
197− std::cmp::max(count_digits(last_drawn), 2)
201+ let digits = count_digits(last_drawn);
202+ let n_min = view.gutters.line_numbers.min_width;
203+ digits.max(n_min)
198}
204}
199
205
200pub fn padding<'doc>(
206pub fn padding<'doc>(
@@ -282,3 +288,82 @@ pub fn diagnostics_or_breakpoints<'doc>(
282 breakpoints(line, selected, out).or_else(|| diagnostics(line, selected, out))
288 breakpoints(line, selected, out).or_else(|| diagnostics(line, selected, out))
283 })
289 })
284}
290}
291+
292+#[cfg(test)]
293+mod tests {
294+ use super::*;
295+ use crate::document::Document;
296+ use crate::editor::{GutterConfig, GutterLineNumbersConfig};
297+ use crate::graphics::Rect;
298+ use crate::DocumentId;
299+ use helix_core::Rope;
300+
301+ #[test]
302+ fn test_default_gutter_widths() {
303+ let mut view = View::new(DocumentId::default(), GutterConfig::default());
304+ view.area = Rect::new(40, 40, 40, 40);
305+
306+ let rope = Rope::from_str("abc\n\tdef");
307+ let doc = Document::from(rope, None);
308+
309+ assert_eq!(view.gutters.layout.len(), 5);
310+ assert_eq!(view.gutters.layout[0].width(&view, &doc), 1);
311+ assert_eq!(view.gutters.layout[1].width(&view, &doc), 1);
312+ assert_eq!(view.gutters.layout[2].width(&view, &doc), 3);
313+ assert_eq!(view.gutters.layout[3].width(&view, &doc), 1);
314+ assert_eq!(view.gutters.layout[4].width(&view, &doc), 1);
315+ }
316+
317+ #[test]
318+ fn test_configured_gutter_widths() {
319+ let gutters = GutterConfig {
320+ layout: vec![GutterType::Diagnostics],
321+ ..Default::default()
322+ };
323+
324+ let mut view = View::new(DocumentId::default(), gutters);
325+ view.area = Rect::new(40, 40, 40, 40);
326+
327+ let rope = Rope::from_str("abc\n\tdef");
328+ let doc = Document::from(rope, None);
329+
330+ assert_eq!(view.gutters.layout.len(), 1);
331+ assert_eq!(view.gutters.layout[0].width(&view, &doc), 1);
332+
333+ let gutters = GutterConfig {
334+ layout: vec![GutterType::Diagnostics, GutterType::LineNumbers],
335+ line_numbers: GutterLineNumbersConfig { min_width: 10 },
336+ };
337+
338+ let mut view = View::new(DocumentId::default(), gutters);
339+ view.area = Rect::new(40, 40, 40, 40);
340+
341+ let rope = Rope::from_str("abc\n\tdef");
342+ let doc = Document::from(rope, None);
343+
344+ assert_eq!(view.gutters.layout.len(), 2);
345+ assert_eq!(view.gutters.layout[0].width(&view, &doc), 1);
346+ assert_eq!(view.gutters.layout[1].width(&view, &doc), 10);
347+ }
348+
349+ #[test]
350+ fn test_line_numbers_gutter_width_resizes() {
351+ let gutters = GutterConfig {
352+ layout: vec![GutterType::Diagnostics, GutterType::LineNumbers],
353+ line_numbers: GutterLineNumbersConfig { min_width: 1 },
354+ };
355+
356+ let mut view = View::new(DocumentId::default(), gutters);
357+ view.area = Rect::new(40, 40, 40, 40);
358+
359+ let rope = Rope::from_str("a\nb");
360+ let doc_short = Document::from(rope, None);
361+
362+ let rope = Rope::from_str("a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\np");
363+ let doc_long = Document::from(rope, None);
364+
365+ assert_eq!(view.gutters.layout.len(), 2);
366+ assert_eq!(view.gutters.layout[1].width(&view, &doc_short), 1);
367+ assert_eq!(view.gutters.layout[1].width(&view, &doc_long), 2);
368+ }
369+}
helix-view/src/tree.rs+9−33
@@ -701,7 +701,7 @@ impl<'a> DoubleEndedIterator for Traverse<'a> {
701#[cfg(test)]
701#[cfg(test)]
702mod test {
702mod test {
703 use super::*;
703 use super::*;
704− use crate::editor::GutterType;
704+ use crate::editor::GutterConfig;
705 use crate::DocumentId;
705 use crate::DocumentId;
706
706
707 #[test]
707 #[test]
@@ -712,34 +712,22 @@ mod test {
712 width: 180,
712 width: 180,
713 height: 80,
713 height: 80,
714 });
714 });
715− let mut view = View::new(
715+ let mut view = View::new(DocumentId::default(), GutterConfig::default());
716− DocumentId::default(),
717− vec![GutterType::Diagnostics, GutterType::LineNumbers],
718− );
719 view.area = Rect::new(0, 0, 180, 80);
716 view.area = Rect::new(0, 0, 180, 80);
720 tree.insert(view);
717 tree.insert(view);
721
718
722 let l0 = tree.focus;
719 let l0 = tree.focus;
723− let view = View::new(
724− DocumentId::default(),
720+ let view = View::new(DocumentId::default(), GutterConfig::default());
725− vec![GutterType::Diagnostics, GutterType::LineNumbers],
726− );
727 tree.split(view, Layout::Vertical);
721 tree.split(view, Layout::Vertical);
728 let r0 = tree.focus;
722 let r0 = tree.focus;
729
723
730 tree.focus = l0;
724 tree.focus = l0;
731− let view = View::new(
732− DocumentId::default(),
725+ let view = View::new(DocumentId::default(), GutterConfig::default());
733− vec![GutterType::Diagnostics, GutterType::LineNumbers],
734− );
735 tree.split(view, Layout::Horizontal);
726 tree.split(view, Layout::Horizontal);
736 let l1 = tree.focus;
727 let l1 = tree.focus;
737
728
738 tree.focus = l0;
729 tree.focus = l0;
739− let view = View::new(
740− DocumentId::default(),
730+ let view = View::new(DocumentId::default(), GutterConfig::default());
741− vec![GutterType::Diagnostics, GutterType::LineNumbers],
742− );
743 tree.split(view, Layout::Vertical);
731 tree.split(view, Layout::Vertical);
744 let l2 = tree.focus;
732 let l2 = tree.focus;
745
733
@@ -781,40 +769,28 @@ mod test {
781 });
769 });
782
770
783 let doc_l0 = DocumentId::default();
771 let doc_l0 = DocumentId::default();
784− let mut view = View::new(
772+ let mut view = View::new(doc_l0, GutterConfig::default());
785− doc_l0,
786− vec![GutterType::Diagnostics, GutterType::LineNumbers],
787− );
788 view.area = Rect::new(0, 0, 180, 80);
773 view.area = Rect::new(0, 0, 180, 80);
789 tree.insert(view);
774 tree.insert(view);
790
775
791 let l0 = tree.focus;
776 let l0 = tree.focus;
792
777
793 let doc_r0 = DocumentId::default();
778 let doc_r0 = DocumentId::default();
794− let view = View::new(
779+ let view = View::new(doc_r0, GutterConfig::default());
795− doc_r0,
796− vec![GutterType::Diagnostics, GutterType::LineNumbers],
797− );
798 tree.split(view, Layout::Vertical);
780 tree.split(view, Layout::Vertical);
799 let r0 = tree.focus;
781 let r0 = tree.focus;
800
782
801 tree.focus = l0;
783 tree.focus = l0;
802
784
803 let doc_l1 = DocumentId::default();
785 let doc_l1 = DocumentId::default();
804− let view = View::new(
786+ let view = View::new(doc_l1, GutterConfig::default());
805− doc_l1,
806− vec![GutterType::Diagnostics, GutterType::LineNumbers],
807− );
808 tree.split(view, Layout::Horizontal);
787 tree.split(view, Layout::Horizontal);
809 let l1 = tree.focus;
788 let l1 = tree.focus;
810
789
811 tree.focus = l0;
790 tree.focus = l0;
812
791
813 let doc_l2 = DocumentId::default();
792 let doc_l2 = DocumentId::default();
814− let view = View::new(
793+ let view = View::new(doc_l2, GutterConfig::default());
815− doc_l2,
816− vec![GutterType::Diagnostics, GutterType::LineNumbers],
817− );
818 tree.split(view, Layout::Vertical);
794 tree.split(view, Layout::Vertical);
819 let l2 = tree.focus;
795 let l2 = tree.focus;
820
796
helix-view/src/view.rs+59−40
@@ -1,4 +1,10 @@
1+use crate::{
2+ align_view,
3+ editor::{GutterConfig, GutterType},
4+ graphics::Rect,
1−use crate::{align_view, editor::GutterType, graphics::Rect, Align, Document, DocumentId, ViewId};
5+ Align, Document, DocumentId, ViewId,
6+};
7+
2use helix_core::{
8use helix_core::{
3 pos_at_visual_coords, visual_coords_at_pos, Position, RopeSlice, Selection, Transaction,
9 pos_at_visual_coords, visual_coords_at_pos, Position, RopeSlice, Selection, Transaction,
4};
10};
@@ -103,8 +109,8 @@ pub struct View {
103 pub last_modified_docs: [Option<DocumentId>; 2],
109 pub last_modified_docs: [Option<DocumentId>; 2],
104 /// used to store previous selections of tree-sitter objects
110 /// used to store previous selections of tree-sitter objects
105 pub object_selections: Vec<Selection>,
111 pub object_selections: Vec<Selection>,
106− /// GutterTypes used to fetch Gutter (constructor) and width for rendering
112+ /// all gutter-related configuration settings, used primarily for gutter rendering
107− gutters: Vec<GutterType>,
113+ pub gutters: GutterConfig,
108 /// A mapping between documents and the last history revision the view was updated at.
114 /// A mapping between documents and the last history revision the view was updated at.
109 /// Changes between documents and views are synced lazily when switching windows. This
115 /// Changes between documents and views are synced lazily when switching windows. This
110 /// mapping keeps track of the last applied history revision so that only new changes
116 /// mapping keeps track of the last applied history revision so that only new changes
@@ -123,7 +129,7 @@ impl fmt::Debug for View {
123}
129}
124
130
125impl View {
131impl View {
126− pub fn new(doc: DocumentId, gutter_types: Vec<crate::editor::GutterType>) -> Self {
132+ pub fn new(doc: DocumentId, gutters: GutterConfig) -> Self {
127 Self {
133 Self {
128 id: ViewId::default(),
134 id: ViewId::default(),
129 doc,
135 doc,
@@ -133,7 +139,7 @@ impl View {
133 docs_access_history: Vec::new(),
139 docs_access_history: Vec::new(),
134 last_modified_docs: [None, None],
140 last_modified_docs: [None, None],
135 object_selections: Vec::new(),
141 object_selections: Vec::new(),
136− gutters: gutter_types,
142+ gutters,
137 doc_revisions: HashMap::new(),
143 doc_revisions: HashMap::new(),
138 }
144 }
139 }
145 }
@@ -154,11 +160,12 @@ impl View {
154 }
160 }
155
161
156 pub fn gutters(&self) -> &[GutterType] {
162 pub fn gutters(&self) -> &[GutterType] {
157− &self.gutters
163+ &self.gutters.layout
158 }
164 }
159
165
160 pub fn gutter_offset(&self, doc: &Document) -> u16 {
166 pub fn gutter_offset(&self, doc: &Document) -> u16 {
161 self.gutters
167 self.gutters
168+ .layout
162 .iter()
169 .iter()
163 .map(|gutter| gutter.width(self, doc) as u16)
170 .map(|gutter| gutter.width(self, doc) as u16)
164 .sum()
171 .sum()
@@ -414,18 +421,19 @@ impl View {
414mod tests {
421mod tests {
415 use super::*;
422 use super::*;
416 use helix_core::Rope;
423 use helix_core::Rope;
417− const OFFSET: u16 = 3; // 1 diagnostic + 2 linenr (< 100 lines)
424+
418− const OFFSET_WITHOUT_LINE_NUMBERS: u16 = 1; // 1 diagnostic
425+ // 1 diagnostic + 1 spacer + 3 linenr (< 1000 lines) + 1 spacer + 1 diff
419− // const OFFSET: u16 = GUTTERS.iter().map(|(_, width)| *width as u16).sum();
426+ const DEFAULT_GUTTER_OFFSET: u16 = 7;
427+
428+ // 1 diagnostics + 1 spacer + 1 gutter
429+ const DEFAULT_GUTTER_OFFSET_ONLY_DIAGNOSTICS: u16 = 3;
430+
420 use crate::document::Document;
431 use crate::document::Document;
421− use crate::editor::GutterType;
432+ use crate::editor::{GutterConfig, GutterLineNumbersConfig, GutterType};
422
433
423 #[test]
434 #[test]
424 fn test_text_pos_at_screen_coords() {
435 fn test_text_pos_at_screen_coords() {
425− let mut view = View::new(
436+ let mut view = View::new(DocumentId::default(), GutterConfig::default());
426− DocumentId::default(),
427− vec![GutterType::Diagnostics, GutterType::LineNumbers],
428− );
429 view.area = Rect::new(40, 40, 40, 40);
437 view.area = Rect::new(40, 40, 40, 40);
430 let rope = Rope::from_str("abc\n\tdef");
438 let rope = Rope::from_str("abc\n\tdef");
431 let doc = Document::from(rope, None);
439 let doc = Document::from(rope, None);
@@ -445,24 +453,24 @@ mod tests {
445 assert_eq!(view.text_pos_at_screen_coords(&doc, 78, 41, 4), None);
453 assert_eq!(view.text_pos_at_screen_coords(&doc, 78, 41, 4), None);
446
454
447 assert_eq!(
455 assert_eq!(
448− view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 3, 4),
456+ view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET + 3, 4),
449 Some(3)
457 Some(3)
450 );
458 );
451
459
452 assert_eq!(view.text_pos_at_screen_coords(&doc, 40, 80, 4), Some(3));
460 assert_eq!(view.text_pos_at_screen_coords(&doc, 40, 80, 4), Some(3));
453
461
454 assert_eq!(
462 assert_eq!(
455− view.text_pos_at_screen_coords(&doc, 41, 40 + OFFSET + 1, 4),
463+ view.text_pos_at_screen_coords(&doc, 41, 40 + DEFAULT_GUTTER_OFFSET + 1, 4),
456 Some(4)
464 Some(4)
457 );
465 );
458
466
459 assert_eq!(
467 assert_eq!(
460− view.text_pos_at_screen_coords(&doc, 41, 40 + OFFSET + 4, 4),
468+ view.text_pos_at_screen_coords(&doc, 41, 40 + DEFAULT_GUTTER_OFFSET + 4, 4),
461 Some(5)
469 Some(5)
462 );
470 );
463
471
464 assert_eq!(
472 assert_eq!(
465− view.text_pos_at_screen_coords(&doc, 41, 40 + OFFSET + 7, 4),
473+ view.text_pos_at_screen_coords(&doc, 41, 40 + DEFAULT_GUTTER_OFFSET + 7, 4),
466 Some(8)
474 Some(8)
467 );
475 );
468
476
@@ -471,19 +479,36 @@ mod tests {
471
479
472 #[test]
480 #[test]
473 fn test_text_pos_at_screen_coords_without_line_numbers_gutter() {
481 fn test_text_pos_at_screen_coords_without_line_numbers_gutter() {
482+ let mut view = View::new(
483+ DocumentId::default(),
484+ GutterConfig {
474− let mut view = View::new(DocumentId::default(), vec![GutterType::Diagnostics]);
485+ layout: vec![GutterType::Diagnostics],
486+ line_numbers: GutterLineNumbersConfig::default(),
487+ },
488+ );
475 view.area = Rect::new(40, 40, 40, 40);
489 view.area = Rect::new(40, 40, 40, 40);
476 let rope = Rope::from_str("abc\n\tdef");
490 let rope = Rope::from_str("abc\n\tdef");
477 let doc = Document::from(rope, None);
491 let doc = Document::from(rope, None);
478 assert_eq!(
492 assert_eq!(
479− view.text_pos_at_screen_coords(&doc, 41, 40 + OFFSET_WITHOUT_LINE_NUMBERS + 1, 4),
493+ view.text_pos_at_screen_coords(
494+ &doc,
495+ 41,
496+ 40 + DEFAULT_GUTTER_OFFSET_ONLY_DIAGNOSTICS + 1,
497+ 4
498+ ),
480 Some(4)
499 Some(4)
481 );
500 );
482 }
501 }
483
502
484 #[test]
503 #[test]
485 fn test_text_pos_at_screen_coords_without_any_gutters() {
504 fn test_text_pos_at_screen_coords_without_any_gutters() {
486− let mut view = View::new(DocumentId::default(), vec![]);
505+ let mut view = View::new(
506+ DocumentId::default(),
507+ GutterConfig {
508+ layout: vec![],
509+ line_numbers: GutterLineNumbersConfig::default(),
510+ },
511+ );
487 view.area = Rect::new(40, 40, 40, 40);
512 view.area = Rect::new(40, 40, 40, 40);
488 let rope = Rope::from_str("abc\n\tdef");
513 let rope = Rope::from_str("abc\n\tdef");
489 let doc = Document::from(rope, None);
514 let doc = Document::from(rope, None);
@@ -492,76 +517,70 @@ mod tests {
492
517
493 #[test]
518 #[test]
494 fn test_text_pos_at_screen_coords_cjk() {
519 fn test_text_pos_at_screen_coords_cjk() {
495− let mut view = View::new(
520+ let mut view = View::new(DocumentId::default(), GutterConfig::default());
496− DocumentId::default(),
497− vec![GutterType::Diagnostics, GutterType::LineNumbers],
498− );
499 view.area = Rect::new(40, 40, 40, 40);
521 view.area = Rect::new(40, 40, 40, 40);
500 let rope = Rope::from_str("Hi! こんにちは皆さん");
522 let rope = Rope::from_str("Hi! こんにちは皆さん");
501 let doc = Document::from(rope, None);
523 let doc = Document::from(rope, None);
502
524
503 assert_eq!(
525 assert_eq!(
504− view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET, 4),
526+ view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET, 4),
505 Some(0)
527 Some(0)
506 );
528 );
507
529
508 assert_eq!(
530 assert_eq!(
509− view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 4, 4),
531+ view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET + 4, 4),
510 Some(4)
532 Some(4)
511 );
533 );
512 assert_eq!(
534 assert_eq!(
513− view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 5, 4),
535+ view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET + 5, 4),
514 Some(4)
536 Some(4)
515 );
537 );
516
538
517 assert_eq!(
539 assert_eq!(
518− view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 6, 4),
540+ view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET + 6, 4),
519 Some(5)
541 Some(5)
520 );
542 );
521
543
522 assert_eq!(
544 assert_eq!(
523− view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 7, 4),
545+ view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET + 7, 4),
524 Some(5)
546 Some(5)
525 );
547 );
526
548
527 assert_eq!(
549 assert_eq!(
528− view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 8, 4),
550+ view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET + 8, 4),
529 Some(6)
551 Some(6)
530 );
552 );
531 }
553 }
532
554
533 #[test]
555 #[test]
534 fn test_text_pos_at_screen_coords_graphemes() {
556 fn test_text_pos_at_screen_coords_graphemes() {
535− let mut view = View::new(
557+ let mut view = View::new(DocumentId::default(), GutterConfig::default());
536− DocumentId::default(),
537− vec![GutterType::Diagnostics, GutterType::LineNumbers],
538− );
539 view.area = Rect::new(40, 40, 40, 40);
558 view.area = Rect::new(40, 40, 40, 40);
540 let rope = Rope::from_str("Hèl̀l̀ò world!");
559 let rope = Rope::from_str("Hèl̀l̀ò world!");
541 let doc = Document::from(rope, None);
560 let doc = Document::from(rope, None);
542
561
543 assert_eq!(
562 assert_eq!(
544− view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET, 4),
563+ view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET, 4),
545 Some(0)
564 Some(0)
546 );
565 );
547
566
548 assert_eq!(
567 assert_eq!(
549− view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 1, 4),
568+ view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET + 1, 4),
550 Some(1)
569 Some(1)
551 );
570 );
552
571
553 assert_eq!(
572 assert_eq!(
554− view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 2, 4),
573+ view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET + 2, 4),
555 Some(3)
574 Some(3)
556 );
575 );
557
576
558 assert_eq!(
577 assert_eq!(
559− view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 3, 4),
578+ view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET + 3, 4),
560 Some(5)
579 Some(5)
561 );
580 );
562
581
563 assert_eq!(
582 assert_eq!(
564− view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 4, 4),
583+ view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET + 4, 4),
565 Some(7)
584 Some(7)
566 );
585 );
567 }
586 }