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') 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 05c6cb1d0b576547c14b204e0df543650c93892f Mon Sep 17 00:00:00 2001 From: Skyler Hawthorne Date: Sat, 20 Nov 2021 09:17:38 -0500 Subject: Solarized theme: fix popup colors, adjust menu (#1124) * fix popup colors, adjust menu * fix hardcoded horizontal rule color--- helix-term/src/ui/markdown.rs | 6 ++++-- runtime/themes/solarized_dark.toml | 12 ++++++------ runtime/themes/solarized_light.toml | 12 ++++++------ 3 files changed, 16 insertions(+), 14 deletions(-) (limited to 'helix-term/src/ui') diff --git a/helix-term/src/ui/markdown.rs b/helix-term/src/ui/markdown.rs index 61630d55..649703b5 100644 --- a/helix-term/src/ui/markdown.rs +++ b/helix-term/src/ui/markdown.rs @@ -55,7 +55,7 @@ fn parse<'a>( fn to_span(text: pulldown_cmark::CowStr) -> Span { use std::ops::Deref; Span::raw::>(match text { - CowStr::Borrowed(s) => s.to_string().into(), // could retain borrow + CowStr::Borrowed(s) => s.into(), CowStr::Boxed(s) => s.to_string().into(), CowStr::Inlined(s) => s.deref().to_owned().into(), }) @@ -179,7 +179,9 @@ fn parse<'a>( spans.push(Span::raw(" ")); } Event::Rule => { - lines.push(Spans::from("---")); + let mut span = Span::raw("---"); + span.style = code_style; + lines.push(Spans::from(span)); lines.push(Spans::default()); } // TaskListMarker(bool) true if checked diff --git a/runtime/themes/solarized_dark.toml b/runtime/themes/solarized_dark.toml index afcafd54..984c86ee 100644 --- a/runtime/themes/solarized_dark.toml +++ b/runtime/themes/solarized_dark.toml @@ -28,18 +28,18 @@ # 行号栏 "ui.linenr" = { fg = "base0", bg = "base02" } # 当前行号栏 -"ui.linenr.selected" = { fg = "red", modifiers = ["bold"] } +"ui.linenr.selected" = { fg = "blue", modifiers = ["bold"] } # 状态栏 -"ui.statusline" = { fg = "base02", bg = "base1" } +"ui.statusline" = { fg = "base03", bg = "base0" } # 非活动状态栏 -"ui.statusline.inactive" = { fg = "base02", bg = "base00" } +"ui.statusline.inactive" = { fg = "base1", bg = "base01" } # 补全窗口, preview窗口 -"ui.popup" = { bg = "base1" } +"ui.popup" = { bg = "base02" } # 影响 补全选中 cmd弹出信息选中 -"ui.menu.selected" = { fg = "base02", bg = "violet"} -"ui.menu" = { fg = "base02" } +"ui.menu.selected" = { fg = "base02", bg = "base2"} +"ui.menu" = { fg = "base1" } # ?? "ui.window" = { fg = "base3" } # 命令行 补全的帮助信息 diff --git a/runtime/themes/solarized_light.toml b/runtime/themes/solarized_light.toml index aec5bf48..0ab1b962 100644 --- a/runtime/themes/solarized_light.toml +++ b/runtime/themes/solarized_light.toml @@ -28,18 +28,18 @@ # 行号栏 "ui.linenr" = { fg = "base0", bg = "base02" } # 当前行号栏 -"ui.linenr.selected" = { fg = "red", modifiers = ["bold"] } +"ui.linenr.selected" = { fg = "blue", modifiers = ["bold"] } # 状态栏 -"ui.statusline" = { fg = "base02", bg = "base1" } +"ui.statusline" = { fg = "base03", bg = "base0" } # 非活动状态栏 -"ui.statusline.inactive" = { fg = "base02", bg = "base00" } +"ui.statusline.inactive" = { fg = "base1", bg = "base01" } # 补全窗口, preview窗口 -"ui.popup" = { bg = "base1" } +"ui.popup" = { bg = "base02" } # 影响 补全选中 cmd弹出信息选中 -"ui.menu.selected" = { fg = "base02", bg = "violet"} -"ui.menu" = { fg = "base02" } +"ui.menu.selected" = { fg = "base02", bg = "base2"} +"ui.menu" = { fg = "base1" } # ?? "ui.window" = { fg = "base3" } # 命令行 补全的帮助信息 -- cgit v1.2.3-70-g09d2 From 6a4d9693ba12feed5b6d6b1b34a4ff56cb9f9fd7 Mon Sep 17 00:00:00 2001 From: Dan Nases Sha Date: Sat, 20 Nov 2021 14:23:36 +0000 Subject: File picker config (#988) * squashed WIP commits * hide_gitignore working with config * pass reference to new config parameter of file_picker() * update config option name to match name on walk builder * add comments to config and documentation of option to book * add git_ignore option to WalkBuilder within prompt in commands.rs * WIP: add FilePickerConfig struct * WIP: cleanup * WIP: add more options including max_depth * WIP: changed defaults to match ignore crate defaults * WIP: change WalkBuilder in global_search() to use config options * WIP: removed follow_links, changed max_depth to follow config setting * WIP: update book with file-picker inline table notation * update documentation for file-picker config in book * adjusted to [editor.file-picker] in book configuration.md * adjust comments in editor.rs to be doc comments, cleanup * adjust comments * adjust book--- book/src/configuration.md | 12 +++++++ helix-term/src/application.rs | 2 +- helix-term/src/commands.rs | 79 +++++++++++++++++++++++++------------------ helix-term/src/ui/mod.rs | 11 +++++- helix-view/src/editor.rs | 42 +++++++++++++++++++++++ 5 files changed, 112 insertions(+), 34 deletions(-) (limited to 'helix-term/src/ui') diff --git a/book/src/configuration.md b/book/src/configuration.md index be25441f..2ed48d51 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -24,6 +24,18 @@ To override global configuration parameters, create a `config.toml` file located | `completion-trigger-len` | The min-length of word under cursor to trigger autocompletion | `2` | | `auto-info` | Whether to display infoboxes | `true` | +`[editor.filepicker]` section of the config. Sets options for file picker and global search. All but the last key listed in the default file-picker configuration below are IgnoreOptions: whether hidden files and files listed within ignore files are ignored by (not visible in) the helix file picker and global search. There is also one other key, `max-depth` available, which is not defined by default. + +| Key | Description | Default | +|--|--|---------| +|`hidden` | Enables ignoring hidden files. | true +|`parents` | Enables reading ignore files from parent directories. | true +|`ignore` | Enables reading `.ignore` files. | true +|`git-ignore` | Enables reading `.gitignore` files. | true +|`git-global` | Enables reading global .gitignore, whose path is specified in git's config: `core.excludefile` option. | true +|`git-exclude` | Enables reading `.git/info/exclude` files. | true +|`max-depth` | Set with an integer value for maximum depth to recurse. | Defaults to `None`. + ## LSP To display all language server messages in the status line add the following to your `config.toml`: diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 78b93cd9..a795a56e 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -120,7 +120,7 @@ impl Application { if first.is_dir() { std::env::set_current_dir(&first)?; editor.new_file(Action::VerticalSplit); - compositor.push(Box::new(ui::file_picker(".".into()))); + compositor.push(Box::new(ui::file_picker(".".into(), &config.editor))); } else { let nr_of_files = args.files.len(); editor.open(first.to_path_buf(), Action::VerticalSplit)?; diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index e70773eb..fde505fd 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -1440,6 +1440,7 @@ fn global_search(cx: &mut Context) { let (all_matches_sx, all_matches_rx) = tokio::sync::mpsc::unbounded_channel::<(usize, PathBuf)>(); let smart_case = cx.editor.config.smart_case; + let file_picker_config = cx.editor.config.file_picker.clone(); let completions = search_completions(cx, None); let prompt = ui::regex_prompt( @@ -1468,41 +1469,55 @@ fn global_search(cx: &mut Context) { let search_root = std::env::current_dir() .expect("Global search error: Failed to get current dir"); - WalkBuilder::new(search_root).build_parallel().run(|| { - let mut searcher_cl = searcher.clone(); - let matcher_cl = matcher.clone(); - let all_matches_sx_cl = all_matches_sx.clone(); - Box::new(move |dent: Result| -> WalkState { - let dent = match dent { - Ok(dent) => dent, - Err(_) => return WalkState::Continue, - }; - - match dent.file_type() { - Some(fi) => { - if !fi.is_file() { - return WalkState::Continue; + WalkBuilder::new(search_root) + .hidden(file_picker_config.hidden) + .parents(file_picker_config.parents) + .ignore(file_picker_config.ignore) + .git_ignore(file_picker_config.git_ignore) + .git_global(file_picker_config.git_global) + .git_exclude(file_picker_config.git_exclude) + .max_depth(file_picker_config.max_depth) + .build_parallel() + .run(|| { + let mut searcher_cl = searcher.clone(); + let matcher_cl = matcher.clone(); + let all_matches_sx_cl = all_matches_sx.clone(); + Box::new(move |dent: Result| -> WalkState { + let dent = match dent { + Ok(dent) => dent, + Err(_) => return WalkState::Continue, + }; + + match dent.file_type() { + Some(fi) => { + if !fi.is_file() { + return WalkState::Continue; + } } + None => return WalkState::Continue, } - None => return WalkState::Continue, - } - let result_sink = sinks::UTF8(|line_num, _| { - match all_matches_sx_cl - .send((line_num as usize - 1, dent.path().to_path_buf())) - { - Ok(_) => Ok(true), - Err(_) => Ok(false), + let result_sink = sinks::UTF8(|line_num, _| { + match all_matches_sx_cl + .send((line_num as usize - 1, dent.path().to_path_buf())) + { + Ok(_) => Ok(true), + Err(_) => Ok(false), + } + }); + let result = + searcher_cl.search_path(&matcher_cl, dent.path(), result_sink); + + if let Err(err) = result { + log::error!( + "Global search error: {}, {}", + dent.path().display(), + err + ); } - }); - let result = searcher_cl.search_path(&matcher_cl, dent.path(), result_sink); - - if let Err(err) = result { - log::error!("Global search error: {}, {}", dent.path().display(), err); - } - WalkState::Continue - }) - }); + WalkState::Continue + }) + }); } else { // Otherwise do nothing // log::warn!("Global Search Invalid Pattern") @@ -2742,7 +2757,7 @@ fn command_mode(cx: &mut Context) { fn file_picker(cx: &mut Context) { let root = find_root(None).unwrap_or_else(|| PathBuf::from("./")); - let picker = ui::file_picker(root); + let picker = ui::file_picker(root, &cx.editor.config); cx.push_layer(Box::new(picker)); } diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 62da0dce..cdf42311 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -93,13 +93,22 @@ pub fn regex_prompt( ) } -pub fn file_picker(root: PathBuf) -> FilePicker { +pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePicker { use ignore::{types::TypesBuilder, WalkBuilder}; use std::time; // We want to exclude files that the editor can't handle yet let mut type_builder = TypesBuilder::new(); let mut walk_builder = WalkBuilder::new(&root); + walk_builder + .hidden(config.file_picker.hidden) + .parents(config.file_picker.parents) + .ignore(config.file_picker.ignore) + .git_ignore(config.file_picker.git_ignore) + .git_global(config.file_picker.git_global) + .git_exclude(config.file_picker.git_exclude) + .max_depth(config.file_picker.max_depth); + let walk_builder = match type_builder.add( "compressed", "*.{zip,gz,bz2,zst,lzo,sz,tgz,tbz2,lz,lz4,lzma,lzo,z,Z,xz,7z,rar,cab}", diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 364865d9..1ce33760 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -35,6 +35,46 @@ where Ok(Duration::from_millis(millis)) } +#[derive(Debug, Clone, PartialEq, Deserialize)] +#[serde(rename_all = "kebab-case", default, deny_unknown_fields)] +pub struct FilePickerConfig { + /// IgnoreOptions + /// Enables ignoring hidden files. + /// Whether to hide hidden files in file picker and global search results. Defaults to true. + pub hidden: bool, + /// Enables reading ignore files from parent directories. Defaults to true. + pub parents: bool, + /// Enables reading `.ignore` files. + /// Whether to hide files listed in .ignore in file picker and global search results. Defaults to true. + pub ignore: bool, + /// Enables reading `.gitignore` files. + /// Whether to hide files listed in .gitignore in file picker and global search results. Defaults to true. + pub git_ignore: bool, + /// Enables reading global .gitignore, whose path is specified in git's config: `core.excludefile` option. + /// Whether to hide files listed in global .gitignore in file picker and global search results. Defaults to true. + pub git_global: bool, + /// Enables reading `.git/info/exclude` files. + /// Whether to hide files listed in .git/info/exclude in file picker and global search results. Defaults to true. + pub git_exclude: bool, + /// WalkBuilder options + /// Maximum Depth to recurse directories in file picker and global search. Defaults to `None`. + pub max_depth: Option, +} + +impl Default for FilePickerConfig { + fn default() -> Self { + Self { + hidden: true, + parents: true, + ignore: true, + git_ignore: true, + git_global: true, + git_exclude: true, + max_depth: None, + } + } +} + #[derive(Debug, Clone, PartialEq, Deserialize)] #[serde(rename_all = "kebab-case", default, deny_unknown_fields)] pub struct Config { @@ -62,6 +102,7 @@ pub struct Config { pub completion_trigger_len: u8, /// Whether to display infoboxes. Defaults to true. pub auto_info: bool, + pub file_picker: FilePickerConfig, } #[derive(Debug, Clone, PartialEq, Eq, Deserialize)] @@ -93,6 +134,7 @@ impl Default for Config { idle_timeout: Duration::from_millis(400), completion_trigger_len: 2, auto_info: true, + file_picker: FilePickerConfig::default(), } } } -- 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') 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 c71c9f69e21a80c5c9c744bddbde7d5041da99a5 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Thu, 7 Oct 2021 10:26:28 +0900 Subject: TODO --- helix-term/src/ui/markdown.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'helix-term/src/ui') diff --git a/helix-term/src/ui/markdown.rs b/helix-term/src/ui/markdown.rs index 649703b5..ca8303dd 100644 --- a/helix-term/src/ui/markdown.rs +++ b/helix-term/src/ui/markdown.rs @@ -228,6 +228,7 @@ impl Component for Markdown { return None; } let contents = parse(&self.contents, None, &self.config_loader); + // TODO: account for tab width let max_text_width = (viewport.0 - padding).min(120); let mut text_width = 0; let mut height = padding; -- 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') 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') 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