Skip to content

Commit b48f44d

Browse files
committed
Extract gutters into helix-view
1 parent 646c94d commit b48f44d

File tree

4 files changed

+117
-100
lines changed

4 files changed

+117
-100
lines changed

helix-term/src/ui/editor.rs

Lines changed: 1 addition & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ use helix_core::{
1717
};
1818
use helix_view::{
1919
document::{Mode, SCRATCH_BUFFER_NAME},
20-
editor::{Config, LineNumber},
2120
graphics::{CursorKind, Modifier, Rect, Style},
2221
info::Info,
2322
input::KeyEvent,
@@ -421,93 +420,8 @@ impl EditorView {
421420
.map(|range| range.cursor_line(text))
422421
.collect();
423422

424-
use std::fmt::Write;
425-
426-
fn diagnostic<'doc>(
427-
doc: &'doc Document,
428-
_view: &View,
429-
theme: &Theme,
430-
_config: &Config,
431-
_is_focused: bool,
432-
_width: usize,
433-
) -> GutterFn<'doc> {
434-
let warning = theme.get("warning");
435-
let error = theme.get("error");
436-
let info = theme.get("info");
437-
let hint = theme.get("hint");
438-
let diagnostics = doc.diagnostics();
439-
440-
Box::new(move |line: usize, _selected: bool, out: &mut String| {
441-
use helix_core::diagnostic::Severity;
442-
if let Some(diagnostic) = diagnostics.iter().find(|d| d.line == line) {
443-
write!(out, "●").unwrap();
444-
return Some(match diagnostic.severity {
445-
Some(Severity::Error) => error,
446-
Some(Severity::Warning) | None => warning,
447-
Some(Severity::Info) => info,
448-
Some(Severity::Hint) => hint,
449-
});
450-
}
451-
None
452-
})
453-
}
454-
455-
fn line_number<'doc>(
456-
doc: &'doc Document,
457-
view: &View,
458-
theme: &Theme,
459-
config: &Config,
460-
is_focused: bool,
461-
width: usize,
462-
) -> GutterFn<'doc> {
463-
let text = doc.text().slice(..);
464-
let last_line = view.last_line(doc);
465-
// Whether to draw the line number for the last line of the
466-
// document or not. We only draw it if it's not an empty line.
467-
let draw_last = text.line_to_byte(last_line) < text.len_bytes();
468-
469-
let linenr = theme.get("ui.linenr");
470-
let linenr_select: Style = theme.try_get("ui.linenr.selected").unwrap_or(linenr);
471-
472-
let current_line = doc
473-
.text()
474-
.char_to_line(doc.selection(view.id).primary().cursor(text));
475-
476-
let config = config.line_number;
477-
478-
Box::new(move |line: usize, selected: bool, out: &mut String| {
479-
if line == last_line && !draw_last {
480-
write!(out, "{:>1$}", '~', width).unwrap();
481-
Some(linenr)
482-
} else {
483-
let line = match config {
484-
LineNumber::Absolute => line + 1,
485-
LineNumber::Relative => {
486-
if current_line == line {
487-
line + 1
488-
} else {
489-
abs_diff(current_line, line)
490-
}
491-
}
492-
};
493-
let style = if selected && is_focused {
494-
linenr_select
495-
} else {
496-
linenr
497-
};
498-
write!(out, "{:>1$}", line, width).unwrap();
499-
Some(style)
500-
}
501-
})
502-
}
503-
504-
type GutterFn<'doc> = Box<dyn Fn(usize, bool, &mut String) -> Option<Style> + 'doc>;
505-
type Gutter =
506-
for<'doc> fn(&'doc Document, &View, &Theme, &Config, bool, usize) -> GutterFn<'doc>;
507-
let gutters: &[(Gutter, usize)] = &[(diagnostic, 1), (line_number, 5)];
508-
509423
let mut offset = 0;
510-
for (constructor, width) in gutters {
424+
for (constructor, width) in view.gutters() {
511425
let gutter = constructor(doc, view, theme, config, is_focused, *width);
512426
for (i, line) in (view.offset.row..(last_line + 1)).enumerate() {
513427
let selected = cursors.contains(&line);
@@ -1211,12 +1125,3 @@ fn canonicalize_key(key: &mut KeyEvent) {
12111125
key.modifiers.remove(KeyModifiers::SHIFT)
12121126
}
12131127
}
1214-
1215-
#[inline]
1216-
const fn abs_diff(a: usize, b: usize) -> usize {
1217-
if a > b {
1218-
a - b
1219-
} else {
1220-
b - a
1221-
}
1222-
}

helix-view/src/gutter.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use std::fmt::Write;
2+
3+
use crate::{editor::Config, graphics::Style, Document, Theme, View};
4+
5+
pub type GutterFn<'doc> = Box<dyn Fn(usize, bool, &mut String) -> Option<Style> + 'doc>;
6+
pub type Gutter =
7+
for<'doc> fn(&'doc Document, &View, &Theme, &Config, bool, usize) -> GutterFn<'doc>;
8+
9+
pub fn diagnostic<'doc>(
10+
doc: &'doc Document,
11+
_view: &View,
12+
theme: &Theme,
13+
_config: &Config,
14+
_is_focused: bool,
15+
_width: usize,
16+
) -> GutterFn<'doc> {
17+
let warning = theme.get("warning");
18+
let error = theme.get("error");
19+
let info = theme.get("info");
20+
let hint = theme.get("hint");
21+
let diagnostics = doc.diagnostics();
22+
23+
Box::new(move |line: usize, _selected: bool, out: &mut String| {
24+
use helix_core::diagnostic::Severity;
25+
if let Some(diagnostic) = diagnostics.iter().find(|d| d.line == line) {
26+
write!(out, "●").unwrap();
27+
return Some(match diagnostic.severity {
28+
Some(Severity::Error) => error,
29+
Some(Severity::Warning) | None => warning,
30+
Some(Severity::Info) => info,
31+
Some(Severity::Hint) => hint,
32+
});
33+
}
34+
None
35+
})
36+
}
37+
38+
pub fn line_number<'doc>(
39+
doc: &'doc Document,
40+
view: &View,
41+
theme: &Theme,
42+
config: &Config,
43+
is_focused: bool,
44+
width: usize,
45+
) -> GutterFn<'doc> {
46+
let text = doc.text().slice(..);
47+
let last_line = view.last_line(doc);
48+
// Whether to draw the line number for the last line of the
49+
// document or not. We only draw it if it's not an empty line.
50+
let draw_last = text.line_to_byte(last_line) < text.len_bytes();
51+
52+
let linenr = theme.get("ui.linenr");
53+
let linenr_select: Style = theme.try_get("ui.linenr.selected").unwrap_or(linenr);
54+
55+
let current_line = doc
56+
.text()
57+
.char_to_line(doc.selection(view.id).primary().cursor(text));
58+
59+
let config = config.line_number;
60+
61+
Box::new(move |line: usize, selected: bool, out: &mut String| {
62+
if line == last_line && !draw_last {
63+
write!(out, "{:>1$}", '~', width).unwrap();
64+
Some(linenr)
65+
} else {
66+
use crate::editor::LineNumber;
67+
let line = match config {
68+
LineNumber::Absolute => line + 1,
69+
LineNumber::Relative => {
70+
if current_line == line {
71+
line + 1
72+
} else {
73+
abs_diff(current_line, line)
74+
}
75+
}
76+
};
77+
let style = if selected && is_focused {
78+
linenr_select
79+
} else {
80+
linenr
81+
};
82+
write!(out, "{:>1$}", line, width).unwrap();
83+
Some(style)
84+
}
85+
})
86+
}
87+
88+
#[inline(always)]
89+
const fn abs_diff(a: usize, b: usize) -> usize {
90+
if a > b {
91+
a - b
92+
} else {
93+
b - a
94+
}
95+
}

helix-view/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pub mod clipboard;
55
pub mod document;
66
pub mod editor;
77
pub mod graphics;
8+
pub mod gutter;
89
pub mod info;
910
pub mod input;
1011
pub mod keyboard;

helix-view/src/view.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
use std::borrow::Cow;
22

3-
use crate::{graphics::Rect, Document, DocumentId, ViewId};
3+
use crate::{
4+
graphics::Rect,
5+
gutter::{self, Gutter},
6+
Document, DocumentId, ViewId,
7+
};
48
use helix_core::{
59
graphemes::{grapheme_width, RopeGraphemes},
610
line_ending::line_end_char_index,
@@ -60,6 +64,8 @@ impl JumpList {
6064
}
6165
}
6266

67+
const GUTTERS: &[(Gutter, usize)] = &[(gutter::diagnostic, 1), (gutter::line_number, 5)];
68+
6369
#[derive(Debug)]
6470
pub struct View {
6571
pub id: ViewId,
@@ -83,10 +89,19 @@ impl View {
8389
}
8490
}
8591

92+
pub fn gutters(&self) -> &[(Gutter, usize)] {
93+
GUTTERS
94+
}
95+
8696
pub fn inner_area(&self) -> Rect {
87-
// TODO: not ideal
88-
const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
89-
self.area.clip_left(OFFSET).clip_bottom(1) // -1 for statusline
97+
// TODO: cache this
98+
let offset = self
99+
.gutters()
100+
.iter()
101+
.map(|(_, width)| *width as u16)
102+
.sum::<u16>()
103+
+ 1; // +1 for some space between gutters and line
104+
self.area.clip_left(offset).clip_bottom(1) // -1 for statusline
90105
}
91106

92107
//
@@ -276,6 +291,7 @@ mod tests {
276291
use super::*;
277292
use helix_core::Rope;
278293
const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
294+
// const OFFSET: u16 = GUTTERS.iter().map(|(_, width)| *width as u16).sum();
279295

280296
#[test]
281297
fn test_text_pos_at_screen_coords() {

0 commit comments

Comments
 (0)