aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--helix-term/src/ui/editor.rs97
-rw-r--r--helix-view/src/gutter.rs95
-rw-r--r--helix-view/src/lib.rs1
-rw-r--r--helix-view/src/view.rs24
4 files changed, 117 insertions, 100 deletions
diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs
index 82fb8fbf..83be816f 100644
--- a/helix-term/src/ui/editor.rs
+++ b/helix-term/src/ui/editor.rs
@@ -17,7 +17,6 @@ use helix_core::{
};
use helix_view::{
document::{Mode, SCRATCH_BUFFER_NAME},
- editor::{Config, LineNumber},
graphics::{CursorKind, Modifier, Rect, Style},
info::Info,
input::KeyEvent,
@@ -421,97 +420,12 @@ impl EditorView {
.map(|range| range.cursor_line(text))
.collect();
- use std::fmt::Write;
-
- fn diagnostic<'doc>(
- doc: &'doc Document,
- _view: &View,
- theme: &Theme,
- _config: &Config,
- _is_focused: bool,
- _width: usize,
- ) -> GutterFn<'doc> {
- let warning = theme.get("warning");
- let error = theme.get("error");
- let info = theme.get("info");
- let hint = theme.get("hint");
- let diagnostics = doc.diagnostics();
-
- Box::new(move |line: usize, _selected: bool, out: &mut String| {
- use helix_core::diagnostic::Severity;
- if let Some(diagnostic) = diagnostics.iter().find(|d| d.line == line) {
- write!(out, "●").unwrap();
- return Some(match diagnostic.severity {
- Some(Severity::Error) => error,
- Some(Severity::Warning) | None => warning,
- Some(Severity::Info) => info,
- Some(Severity::Hint) => hint,
- });
- }
- None
- })
- }
-
- fn line_number<'doc>(
- doc: &'doc Document,
- view: &View,
- theme: &Theme,
- config: &Config,
- is_focused: bool,
- width: usize,
- ) -> GutterFn<'doc> {
- let text = doc.text().slice(..);
- let last_line = view.last_line(doc);
- // Whether to draw the line number for the last line of the
- // document or not. We only draw it if it's not an empty line.
- let draw_last = text.line_to_byte(last_line) < text.len_bytes();
-
- let linenr = theme.get("ui.linenr");
- let linenr_select: Style = theme.try_get("ui.linenr.selected").unwrap_or(linenr);
-
- let current_line = doc
- .text()
- .char_to_line(doc.selection(view.id).primary().cursor(text));
-
- let config = config.line_number;
-
- Box::new(move |line: usize, selected: bool, out: &mut String| {
- if line == last_line && !draw_last {
- write!(out, "{:>1$}", '~', width).unwrap();
- Some(linenr)
- } else {
- let line = match config {
- LineNumber::Absolute => line + 1,
- LineNumber::Relative => {
- if current_line == line {
- line + 1
- } else {
- abs_diff(current_line, line)
- }
- }
- };
- let style = if selected && is_focused {
- linenr_select
- } else {
- linenr
- };
- write!(out, "{:>1$}", line, width).unwrap();
- Some(style)
- }
- })
- }
-
- type GutterFn<'doc> = Box<dyn Fn(usize, bool, &mut String) -> Option<Style> + 'doc>;
- type Gutter =
- for<'doc> fn(&'doc Document, &View, &Theme, &Config, bool, usize) -> GutterFn<'doc>;
- let gutters: &[(Gutter, usize)] = &[(diagnostic, 1), (line_number, 5)];
-
let mut offset = 0;
// avoid lots of small allocations by reusing a text buffer for each line
let mut text = String::with_capacity(8);
- for (constructor, width) in gutters {
+ for (constructor, width) in view.gutters() {
let gutter = constructor(doc, view, theme, config, is_focused, *width);
text.reserve(*width); // ensure there's enough space for the gutter
for (i, line) in (view.offset.row..(last_line + 1)).enumerate() {
@@ -1214,12 +1128,3 @@ fn canonicalize_key(key: &mut KeyEvent) {
key.modifiers.remove(KeyModifiers::SHIFT)
}
}
-
-#[inline]
-const fn abs_diff(a: usize, b: usize) -> usize {
- if a > b {
- a - b
- } else {
- b - a
- }
-}
diff --git a/helix-view/src/gutter.rs b/helix-view/src/gutter.rs
new file mode 100644
index 00000000..86773c1d
--- /dev/null
+++ b/helix-view/src/gutter.rs
@@ -0,0 +1,95 @@
+use std::fmt::Write;
+
+use crate::{editor::Config, graphics::Style, Document, Theme, View};
+
+pub type GutterFn<'doc> = Box<dyn Fn(usize, bool, &mut String) -> Option<Style> + 'doc>;
+pub type Gutter =
+ for<'doc> fn(&'doc Document, &View, &Theme, &Config, bool, usize) -> GutterFn<'doc>;
+
+pub fn diagnostic<'doc>(
+ doc: &'doc Document,
+ _view: &View,
+ theme: &Theme,
+ _config: &Config,
+ _is_focused: bool,
+ _width: usize,
+) -> GutterFn<'doc> {
+ let warning = theme.get("warning");
+ let error = theme.get("error");
+ let info = theme.get("info");
+ let hint = theme.get("hint");
+ let diagnostics = doc.diagnostics();
+
+ Box::new(move |line: usize, _selected: bool, out: &mut String| {
+ use helix_core::diagnostic::Severity;
+ if let Some(diagnostic) = diagnostics.iter().find(|d| d.line == line) {
+ write!(out, "●").unwrap();
+ return Some(match diagnostic.severity {
+ Some(Severity::Error) => error,
+ Some(Severity::Warning) | None => warning,
+ Some(Severity::Info) => info,
+ Some(Severity::Hint) => hint,
+ });
+ }
+ None
+ })
+}
+
+pub fn line_number<'doc>(
+ doc: &'doc Document,
+ view: &View,
+ theme: &Theme,
+ config: &Config,
+ is_focused: bool,
+ width: usize,
+) -> GutterFn<'doc> {
+ let text = doc.text().slice(..);
+ let last_line = view.last_line(doc);
+ // Whether to draw the line number for the last line of the
+ // document or not. We only draw it if it's not an empty line.
+ let draw_last = text.line_to_byte(last_line) < text.len_bytes();
+
+ let linenr = theme.get("ui.linenr");
+ let linenr_select: Style = theme.try_get("ui.linenr.selected").unwrap_or(linenr);
+
+ let current_line = doc
+ .text()
+ .char_to_line(doc.selection(view.id).primary().cursor(text));
+
+ let config = config.line_number;
+
+ Box::new(move |line: usize, selected: bool, out: &mut String| {
+ if line == last_line && !draw_last {
+ write!(out, "{:>1$}", '~', width).unwrap();
+ Some(linenr)
+ } else {
+ use crate::editor::LineNumber;
+ let line = match config {
+ LineNumber::Absolute => line + 1,
+ LineNumber::Relative => {
+ if current_line == line {
+ line + 1
+ } else {
+ abs_diff(current_line, line)
+ }
+ }
+ };
+ let style = if selected && is_focused {
+ linenr_select
+ } else {
+ linenr
+ };
+ write!(out, "{:>1$}", line, width).unwrap();
+ Some(style)
+ }
+ })
+}
+
+#[inline(always)]
+const fn abs_diff(a: usize, b: usize) -> usize {
+ if a > b {
+ a - b
+ } else {
+ b - a
+ }
+}
diff --git a/helix-view/src/lib.rs b/helix-view/src/lib.rs
index 4d19ee2e..a56c914d 100644
--- a/helix-view/src/lib.rs
+++ b/helix-view/src/lib.rs
@@ -5,6 +5,7 @@ pub mod clipboard;
pub mod document;
pub mod editor;
pub mod graphics;
+pub mod gutter;
pub mod info;
pub mod input;
pub mod keyboard;
diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs
index a77f1562..217a4940 100644
--- a/helix-view/src/view.rs
+++ b/helix-view/src/view.rs
@@ -1,6 +1,10 @@
use std::borrow::Cow;
-use crate::{graphics::Rect, Document, DocumentId, ViewId};
+use crate::{
+ graphics::Rect,
+ gutter::{self, Gutter},
+ Document, DocumentId, ViewId,
+};
use helix_core::{
graphemes::{grapheme_width, RopeGraphemes},
line_ending::line_end_char_index,
@@ -60,6 +64,8 @@ impl JumpList {
}
}
+const GUTTERS: &[(Gutter, usize)] = &[(gutter::diagnostic, 1), (gutter::line_number, 5)];
+
#[derive(Debug)]
pub struct View {
pub id: ViewId,
@@ -83,10 +89,19 @@ impl View {
}
}
+ pub fn gutters(&self) -> &[(Gutter, usize)] {
+ GUTTERS
+ }
+
pub fn inner_area(&self) -> Rect {
- // TODO: not ideal
- const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
- self.area.clip_left(OFFSET).clip_bottom(1) // -1 for statusline
+ // TODO: cache this
+ let offset = self
+ .gutters()
+ .iter()
+ .map(|(_, width)| *width as u16)
+ .sum::<u16>()
+ + 1; // +1 for some space between gutters and line
+ self.area.clip_left(offset).clip_bottom(1) // -1 for statusline
}
//
@@ -276,6 +291,7 @@ mod tests {
use super::*;
use helix_core::Rope;
const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
+ // const OFFSET: u16 = GUTTERS.iter().map(|(_, width)| *width as u16).sum();
#[test]
fn test_text_pos_at_screen_coords() {