From a3a3b0b517d0e690f3efc66b17ac7b9f769dba9d Mon Sep 17 00:00:00 2001 From: Martin Junghanns Date: Sat, 20 Nov 2021 06:17:25 -0800 Subject: Jump to end char of surrounding pair from any cursor pos (#1121) * Jump to end char of surrounding pair from any cursor pos * Separate bracket matching into exact and fuzzy search * Add constants for bracket chars * Abort early if char under cursor is not a bracket * Simplify bracket char validation * Refactor node search and unify find methods * Remove bracket constants--- helix-term/src/ui/editor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'helix-term/src/ui/editor.rs') diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 03cd0474..27d33d22 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -377,7 +377,7 @@ impl EditorView { use helix_core::match_brackets; let pos = doc.selection(view.id).primary().cursor(text); - let pos = match_brackets::find(syntax, doc.text(), pos) + let pos = match_brackets::find_matching_bracket(syntax, doc.text(), pos) .and_then(|pos| view.screen_coords_at_pos(doc, text, pos)); if let Some(pos) = pos { -- cgit v1.2.3-70-g09d2 From 30171416cb5b801086da69566a82462fca16ea14 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Fri, 24 Sep 2021 10:29:41 +0900 Subject: Gutter functions --- helix-core/src/diagnostic.rs | 4 +- helix-term/src/ui/editor.rs | 150 ++++++++++++++++++++++++++----------------- helix-view/src/editor.rs | 2 +- 3 files changed, 94 insertions(+), 62 deletions(-) (limited to 'helix-term/src/ui/editor.rs') diff --git a/helix-core/src/diagnostic.rs b/helix-core/src/diagnostic.rs index ad1ba16a..4fcf51c9 100644 --- a/helix-core/src/diagnostic.rs +++ b/helix-core/src/diagnostic.rs @@ -1,7 +1,7 @@ //! LSP diagnostic utility types. /// Describes the severity level of a [`Diagnostic`]. -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum Severity { Error, Warning, @@ -17,7 +17,7 @@ pub struct Range { } /// Corresponds to [`lsp_types::Diagnostic`](https://docs.rs/lsp-types/0.91.0/lsp_types/struct.Diagnostic.html) -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Diagnostic { pub range: Range, pub line: usize, diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 27d33d22..8c4ea9cc 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -17,7 +17,7 @@ use helix_core::{ }; use helix_view::{ document::{Mode, SCRATCH_BUFFER_NAME}, - editor::LineNumber, + editor::{Config, LineNumber}, graphics::{CursorKind, Modifier, Rect, Style}, info::Info, input::KeyEvent, @@ -412,22 +412,6 @@ impl EditorView { let text = doc.text().slice(..); let last_line = view.last_line(doc); - let linenr = theme.get("ui.linenr"); - let linenr_select: Style = theme.try_get("ui.linenr.selected").unwrap_or(linenr); - - let warning = theme.get("warning"); - let error = theme.get("error"); - let info = theme.get("info"); - let hint = theme.get("hint"); - - // 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 current_line = doc - .text() - .char_to_line(doc.selection(view.id).primary().cursor(text)); - // it's used inside an iterator so the collect isn't needless: // https://github.com/rust-lang/rust-clippy/issues/6164 #[allow(clippy::needless_collect)] @@ -437,51 +421,99 @@ impl EditorView { .map(|range| range.cursor_line(text)) .collect(); - for (i, line) in (view.offset.row..(last_line + 1)).enumerate() { - use helix_core::diagnostic::Severity; - if let Some(diagnostic) = doc.diagnostics().iter().find(|d| d.line == line) { - surface.set_stringn( - viewport.x, - viewport.y + i as u16, - "●", - 1, - match diagnostic.severity { - Some(Severity::Error) => error, - Some(Severity::Warning) | None => warning, - Some(Severity::Info) => info, - Some(Severity::Hint) => hint, - }, - ); - } + fn diagnostic( + doc: &Document, + _view: &View, + theme: &Theme, + _config: &Config, + _is_focused: bool, + _width: usize, + ) -> GutterFn { + let warning = theme.get("warning"); + let error = theme.get("error"); + let info = theme.get("info"); + let hint = theme.get("hint"); + let diagnostics = doc.diagnostics().to_vec(); // TODO + + Box::new(move |line: usize, _selected: bool| { + use helix_core::diagnostic::Severity; + if let Some(diagnostic) = diagnostics.iter().find(|d| d.line == line) { + return Some(( + "●".to_string(), + match diagnostic.severity { + Some(Severity::Error) => error, + Some(Severity::Warning) | None => warning, + Some(Severity::Info) => info, + Some(Severity::Hint) => hint, + }, + )); + } + None + }) + } - let selected = cursors.contains(&line); + fn line_number( + doc: &Document, + view: &View, + theme: &Theme, + config: &Config, + is_focused: bool, + width: usize, + ) -> GutterFn { + 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 text = if line == last_line && !draw_last { - " ~".into() - } else { - let line = match config.line_number { - LineNumber::Absolute => line + 1, - LineNumber::Relative => { - if current_line == line { - line + 1 - } else { - abs_diff(current_line, line) - } - } - }; - format!("{:>5}", line) - }; - surface.set_stringn( - viewport.x + 1, - viewport.y + i as u16, - text, - 5, - if selected && is_focused { - linenr_select + 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| { + if line == last_line && !draw_last { + Some((format!("{:>1$}", '~', width), linenr)) } else { - linenr - }, - ); + 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 + }; + Some((format!("{:>1$}", line, width), style)) + } + }) + } + + type GutterFn = Box Option<(String, Style)>>; + type Gutter = fn(&Document, &View, &Theme, &Config, bool, usize) -> GutterFn; + let gutters: &[(Gutter, usize)] = &[(diagnostic, 1), (line_number, 5)]; + + let mut offset = 0; + for (constructor, width) in gutters { + let gutter = constructor(doc, view, theme, config, is_focused, *width); + for (i, line) in (view.offset.row..(last_line + 1)).enumerate() { + let selected = cursors.contains(&line); + + if let Some((text, style)) = gutter(line, selected) { + surface.set_stringn(viewport.x + offset, viewport.y + i as u16, text, 5, style); + } + } + offset += *width as u16; } } diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 77cea783..d5913a51 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -106,7 +106,7 @@ pub struct Config { pub file_picker: FilePickerConfig, } -#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)] #[serde(rename_all = "kebab-case")] pub enum LineNumber { /// Show absolute line number -- cgit v1.2.3-70-g09d2 From ba45db84d4b49913836a949472366a30a620e67b Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Mon, 22 Nov 2021 16:45:52 +0900 Subject: Tie the GutterFn lifetime to the doc so we can avoid cloning data --- helix-term/src/ui/editor.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'helix-term/src/ui/editor.rs') diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 8c4ea9cc..2cc212ea 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -421,19 +421,19 @@ impl EditorView { .map(|range| range.cursor_line(text)) .collect(); - fn diagnostic( - doc: &Document, + fn diagnostic<'doc>( + doc: &'doc Document, _view: &View, theme: &Theme, _config: &Config, _is_focused: bool, _width: usize, - ) -> GutterFn { + ) -> 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().to_vec(); // TODO + let diagnostics = doc.diagnostics(); Box::new(move |line: usize, _selected: bool| { use helix_core::diagnostic::Severity; @@ -452,14 +452,14 @@ impl EditorView { }) } - fn line_number( - doc: &Document, + fn line_number<'doc>( + doc: &'doc Document, view: &View, theme: &Theme, config: &Config, is_focused: bool, width: usize, - ) -> GutterFn { + ) -> 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 @@ -499,8 +499,9 @@ impl EditorView { }) } - type GutterFn = Box Option<(String, Style)>>; - type Gutter = fn(&Document, &View, &Theme, &Config, bool, usize) -> GutterFn; + type GutterFn<'doc> = Box Option<(String, 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; -- cgit v1.2.3-70-g09d2 From 27c1a84f053d1282ed09d64ec737a46f55685d85 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Mon, 22 Nov 2021 17:02:46 +0900 Subject: Reuse a text buffer for each gutter line --- helix-term/src/ui/editor.rs | 46 +++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) (limited to 'helix-term/src/ui/editor.rs') diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 2cc212ea..82fb8fbf 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -421,6 +421,8 @@ impl EditorView { .map(|range| range.cursor_line(text)) .collect(); + use std::fmt::Write; + fn diagnostic<'doc>( doc: &'doc Document, _view: &View, @@ -435,18 +437,16 @@ impl EditorView { let hint = theme.get("hint"); let diagnostics = doc.diagnostics(); - Box::new(move |line: usize, _selected: bool| { + 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) { - return Some(( - "●".to_string(), - match diagnostic.severity { - Some(Severity::Error) => error, - Some(Severity::Warning) | None => warning, - Some(Severity::Info) => info, - Some(Severity::Hint) => hint, - }, - )); + 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 }) @@ -475,9 +475,10 @@ impl EditorView { let config = config.line_number; - Box::new(move |line: usize, selected: bool| { + Box::new(move |line: usize, selected: bool, out: &mut String| { if line == last_line && !draw_last { - Some((format!("{:>1$}", '~', width), linenr)) + write!(out, "{:>1$}", '~', width).unwrap(); + Some(linenr) } else { let line = match config { LineNumber::Absolute => line + 1, @@ -494,25 +495,38 @@ impl EditorView { } else { linenr }; - Some((format!("{:>1$}", line, width), style)) + write!(out, "{:>1$}", line, width).unwrap(); + Some(style) } }) } - type GutterFn<'doc> = Box Option<(String, Style)> + 'doc>; + type GutterFn<'doc> = Box Option