From 3e84a202a87038304374693b7b668f5bb9584022 Mon Sep 17 00:00:00 2001 From: GNUSheep Date: Mon, 19 Aug 2024 14:57:49 +0200 Subject: [PATCH 1/7] Redone helix-editor#4189 on top of master, now everyting works --- helix-term/src/ui/picker.rs | 137 +++++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 1 deletion(-) diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 82fe9689164d..919ddc2260dc 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -273,6 +273,10 @@ pub struct Picker { /// An event handler for syntax highlighting the currently previewed file. preview_highlight_handler: Sender>, dynamic_query_handler: Option>, + + preview_scroll_offset: (Direction, usize), + preview_height: u16, + cursor_picker: u32, } impl Picker { @@ -394,6 +398,9 @@ impl Picker { file_fn: None, preview_highlight_handler: PreviewHighlightHandler::::default().spawn(), dynamic_query_handler: None, + preview_scroll_offset: (Direction::Forward, 0), + preview_height: 0, + cursor_picker: 0, } } @@ -445,6 +452,44 @@ impl Picker { self } + /// Moves the picker file preview by a number of lines, either down (`Forward`) or up (`Backward`) + fn move_preview_by(&mut self, amount: usize, move_direction: Direction) { + let (current_scroll_direction, current_scroll_offset) = self.preview_scroll_offset; + + match move_direction { + Direction::Backward => match current_scroll_direction { + Direction::Backward => { + self.preview_scroll_offset.1 = current_scroll_offset.saturating_add(amount); + } + Direction::Forward => { + if let Some(change) = current_scroll_offset.checked_sub(amount) { + self.preview_scroll_offset.1 = change; + } else { + self.preview_scroll_offset = ( + Direction::Backward, + amount.saturating_sub(current_scroll_offset), + ); + } + } + }, + Direction::Forward => match current_scroll_direction { + Direction::Backward => { + if let Some(change) = current_scroll_offset.checked_sub(amount) { + self.preview_scroll_offset.1 = change; + } else { + self.preview_scroll_offset = ( + Direction::Forward, + amount.saturating_sub(current_scroll_offset), + ); + } + } + Direction::Forward => { + self.preview_scroll_offset.1 = current_scroll_offset.saturating_add(amount); + } + }, + }; + } + /// Move the cursor by a number of lines, either down (`Forward`) or up (`Backward`) pub fn move_by(&mut self, amount: u32, direction: Direction) { let len = self.matcher.snapshot().matched_item_count(); @@ -849,6 +894,15 @@ impl Picker { let inner = inner.inner(margin); BLOCK.render(area, surface); + let mut preview_scroll_offset = self.preview_scroll_offset; + + // Reset preview scroll if cursor moved + let cursor_position = self.cursor_picker; + if self.cursor != cursor_position { + preview_scroll_offset = (Direction::Forward, 0); + self.cursor_picker = self.cursor; + } + if let Some((preview, range)) = self.get_preview(cx.editor) { let doc = match preview.document() { Some(doc) @@ -866,7 +920,8 @@ impl Picker { return; } }; - + let doc_height = doc.text().len_lines(); + let mut offset = ViewPosition::default(); if let Some((start_line, end_line)) = range { let height = end_line - start_line; @@ -894,6 +949,21 @@ impl Picker { } } + // limit scroll offset between [-offset.anchor, doc.text().len_lines() - offset.anchor - preview height] + preview_scroll_offset.1 = match preview_scroll_offset.0 { + Direction::Backward => preview_scroll_offset.1.min(offset.anchor), + Direction::Forward => preview_scroll_offset.1.min( + doc_height + .saturating_sub(offset.anchor) + .saturating_sub(inner.height as usize), + ), + }; + + offset.vertical_offset = match preview_scroll_offset.0 { + Direction::Backward => offset.anchor.saturating_sub(preview_scroll_offset.1), + Direction::Forward => offset.anchor.saturating_add(preview_scroll_offset.1), + }; + let syntax_highlights = EditorView::doc_syntax_highlights( doc, offset.anchor, @@ -943,6 +1013,39 @@ impl Picker { &cx.editor.theme, decorations, ); + + self.preview_scroll_offset = preview_scroll_offset; + + let win_height = inner.height as usize; + let len = doc_height; + let fits = len <= win_height; + let scroll = offset.vertical_offset; + let scroll_style = cx.editor.theme.get("ui.menu.scroll"); + + const fn div_ceil(a: usize, b: usize) -> usize { + (a + b - 1) / b + } + + if !fits { + let scroll_height = div_ceil(win_height.pow(2), len).min(win_height); + let scroll_line = (win_height - scroll_height) * scroll + / std::cmp::max(1, len.saturating_sub(win_height)); + + let mut cell; + for i in 0..win_height { + cell = &mut surface[(inner.right() - 1, inner.top() + i as u16)]; + + cell.set_symbol("▐"); // right half block + + if scroll_line <= i && i < scroll_line + scroll_height { + // Draw scroll thumb + cell.set_fg(scroll_style.fg.unwrap_or(helix_view::theme::Color::Reset)); + } else { + // Draw scroll track + cell.set_fg(scroll_style.bg.unwrap_or(helix_view::theme::Color::Reset)); + } + } + } } } } @@ -1074,6 +1177,36 @@ impl Component for Picker { self.toggle_preview(); } + alt!('k') | shift!(Up) if self.show_preview => { + self.move_preview_by( + ctx.editor.config().scroll_lines.unsigned_abs(), + Direction::Backward, + ); + } + alt!('j') | shift!(Down) if self.show_preview => { + self.move_preview_by( + ctx.editor.config().scroll_lines.unsigned_abs(), + Direction::Forward, + ); + } + alt!('u') if self.show_preview => { + self.move_preview_by( + self.preview_height.saturating_div(2) as usize, + Direction::Backward, + ); + } + alt!('d') if self.show_preview => { + self.move_preview_by( + self.preview_height.saturating_div(2) as usize, + Direction::Forward, + ); + } + alt!('b') if self.show_preview => { + self.move_preview_by(self.preview_height as usize, Direction::Backward); + } + alt!('f') if self.show_preview => { + self.move_preview_by(self.preview_height as usize, Direction::Forward); + } _ => { self.prompt_handle_event(event, ctx); } @@ -1095,6 +1228,8 @@ impl Component for Picker Option<(u16, u16)> { self.completion_height = height.saturating_sub(4 + self.header_height()); + self.preview_height = height.saturating_sub(2); + Some((width, height)) } From a69ea1dd1084d5455a4e1dbec17a949eac666d45 Mon Sep 17 00:00:00 2001 From: GNUSheep Date: Mon, 19 Aug 2024 14:59:27 +0200 Subject: [PATCH 2/7] cargo fmt, adding co-author, code was based on his work Co-authored-by: Manos Mertzianis --- helix-term/src/ui/picker.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 919ddc2260dc..cbb098113498 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -489,7 +489,7 @@ impl Picker { }, }; } - + /// Move the cursor by a number of lines, either down (`Forward`) or up (`Backward`) pub fn move_by(&mut self, amount: u32, direction: Direction) { let len = self.matcher.snapshot().matched_item_count(); @@ -902,7 +902,7 @@ impl Picker { preview_scroll_offset = (Direction::Forward, 0); self.cursor_picker = self.cursor; } - + if let Some((preview, range)) = self.get_preview(cx.editor) { let doc = match preview.document() { Some(doc) @@ -920,8 +920,8 @@ impl Picker { return; } }; - let doc_height = doc.text().len_lines(); - + let doc_height = doc.text().len_lines(); + let mut offset = ViewPosition::default(); if let Some((start_line, end_line)) = range { let height = end_line - start_line; @@ -958,12 +958,12 @@ impl Picker { .saturating_sub(inner.height as usize), ), }; - + offset.vertical_offset = match preview_scroll_offset.0 { Direction::Backward => offset.anchor.saturating_sub(preview_scroll_offset.1), Direction::Forward => offset.anchor.saturating_add(preview_scroll_offset.1), - }; - + }; + let syntax_highlights = EditorView::doc_syntax_highlights( doc, offset.anchor, @@ -1015,7 +1015,7 @@ impl Picker { ); self.preview_scroll_offset = preview_scroll_offset; - + let win_height = inner.height as usize; let len = doc_height; let fits = len <= win_height; @@ -1045,7 +1045,7 @@ impl Picker { cell.set_fg(scroll_style.bg.unwrap_or(helix_view::theme::Color::Reset)); } } - } + } } } } @@ -1179,8 +1179,8 @@ impl Component for Picker { self.move_preview_by( - ctx.editor.config().scroll_lines.unsigned_abs(), - Direction::Backward, + ctx.editor.config().scroll_lines.unsigned_abs(), + Direction::Backward, ); } alt!('j') | shift!(Down) if self.show_preview => { @@ -1206,7 +1206,7 @@ impl Component for Picker { self.move_preview_by(self.preview_height as usize, Direction::Forward); - } + } _ => { self.prompt_handle_event(event, ctx); } @@ -1228,8 +1228,8 @@ impl Component for Picker Option<(u16, u16)> { self.completion_height = height.saturating_sub(4 + self.header_height()); - self.preview_height = height.saturating_sub(2); - + self.preview_height = height.saturating_sub(2); + Some((width, height)) } From e30a9a059c2c226b6376ba523aff05c9ec581b23 Mon Sep 17 00:00:00 2001 From: GNUSheep Date: Mon, 19 Aug 2024 16:20:41 +0200 Subject: [PATCH 3/7] Fix syntax highlighting not updating --- helix-term/src/ui/picker.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index cbb098113498..b964f76e63d8 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -963,10 +963,9 @@ impl Picker { Direction::Backward => offset.anchor.saturating_sub(preview_scroll_offset.1), Direction::Forward => offset.anchor.saturating_add(preview_scroll_offset.1), }; - let syntax_highlights = EditorView::doc_syntax_highlights( doc, - offset.anchor, + doc.text().slice(..).line_to_char(offset.vertical_offset), area.height, &cx.editor.theme, ); From c8e7ed840ad57b939e1987f42f3355fcb42a86e1 Mon Sep 17 00:00:00 2001 From: GNUSheep Date: Mon, 19 Aug 2024 16:41:28 +0200 Subject: [PATCH 4/7] Add missing keybindings --- helix-term/src/ui/picker.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index b964f76e63d8..374d3f018195 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -1200,10 +1200,10 @@ impl Component for Picker { + alt!('b') | alt!(PageUp) if self.show_preview => { self.move_preview_by(self.preview_height as usize, Direction::Backward); } - alt!('f') if self.show_preview => { + alt!('f') | alt!(PageDown) if self.show_preview => { self.move_preview_by(self.preview_height as usize, Direction::Forward); } _ => { From bf1e00c0135c38544e5e745a9559ca8eed76454e Mon Sep 17 00:00:00 2001 From: GNUSheep Date: Fri, 20 Sep 2024 19:57:52 +0200 Subject: [PATCH 5/7] Fix bug: bad rendering doc syntax diagnostic --- helix-term/src/ui/picker.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 374d3f018195..b5cba13bd932 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -970,8 +970,11 @@ impl Picker { &cx.editor.theme, ); - let mut overlay_highlights = - EditorView::empty_highlight_iter(doc, offset.anchor, area.height); + let mut overlay_highlights = EditorView::empty_highlight_iter( + doc, + doc.text().slice(..).line_to_char(offset.vertical_offset), + area.height, + ); for spans in EditorView::doc_diagnostics_highlights(doc, &cx.editor.theme) { if spans.is_empty() { continue; From 3935b1bdac78073de86bf7724bf6fd660574a122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20S=C5=82omka?= Date: Mon, 30 Dec 2024 19:03:24 +0100 Subject: [PATCH 6/7] Change default preview scroll bindings --- helix-term/src/ui/picker.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 0e5a73cb5cb6..68d04863b6a2 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -1104,10 +1104,10 @@ impl Component for Picker { self.move_by(1, Direction::Forward); } - key!(PageDown) | ctrl!('d') => { + key!(PageDown) | ctrl!('d') if !self.show_preview => { self.page_down(); } - key!(PageUp) | ctrl!('u') => { + key!(PageUp) | ctrl!('u') if !self.show_preview => { self.page_up(); } key!(Home) => { @@ -1188,10 +1188,10 @@ impl Component for Picker { + key!(PageUp) | alt!('b') if self.show_preview => { self.move_preview_by(self.preview_height as usize, Direction::Backward); } - alt!('f') | alt!(PageDown) if self.show_preview => { + key!(PageDown) | alt!('f') if self.show_preview => { self.move_preview_by(self.preview_height as usize, Direction::Forward); } _ => { From 40d95472f0b3e2bed3233b0df2653984c2cdeb6c Mon Sep 17 00:00:00 2001 From: GNUSheep Date: Wed, 15 Jan 2025 13:35:05 +0100 Subject: [PATCH 7/7] Using offset.anchor instead of offset.vertical_offset to fix panics. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Aleksander Słomka --- helix-term/src/ui/picker.rs | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 68d04863b6a2..11ce77e9d8ef 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -934,32 +934,37 @@ impl Picker { } } - // limit scroll offset between [-offset.anchor, doc.text().len_lines() - offset.anchor - preview height] + let mut current_line = doc.text().slice(..).char_to_line(offset.anchor); + preview_scroll_offset.1 = match preview_scroll_offset.0 { - Direction::Backward => preview_scroll_offset.1.min(offset.anchor), + Direction::Backward => preview_scroll_offset.1.min(current_line), Direction::Forward => preview_scroll_offset.1.min( doc_height - .saturating_sub(offset.anchor) + .saturating_sub(current_line) .saturating_sub(inner.height as usize), ), }; - offset.vertical_offset = match preview_scroll_offset.0 { - Direction::Backward => offset.anchor.saturating_sub(preview_scroll_offset.1), - Direction::Forward => offset.anchor.saturating_add(preview_scroll_offset.1), + offset.anchor = match preview_scroll_offset.0 { + Direction::Backward => doc + .text() + .slice(..) + .line_to_char(current_line.saturating_sub(preview_scroll_offset.1)), + Direction::Forward => doc + .text() + .slice(..) + .line_to_char(current_line.saturating_add(preview_scroll_offset.1)), }; + let syntax_highlights = EditorView::doc_syntax_highlights( doc, - doc.text().slice(..).line_to_char(offset.vertical_offset), + offset.anchor, area.height, &cx.editor.theme, ); - let mut overlay_highlights = EditorView::empty_highlight_iter( - doc, - doc.text().slice(..).line_to_char(offset.vertical_offset), - area.height, - ); + let mut overlay_highlights = + EditorView::empty_highlight_iter(doc, offset.anchor, area.height); for spans in EditorView::doc_diagnostics_highlights(doc, &cx.editor.theme) { if spans.is_empty() { continue; @@ -988,6 +993,8 @@ impl Picker { decorations.add_decoration(draw_highlight); } + current_line = doc.text().slice(..).char_to_line(offset.anchor); + render_document( surface, inner, @@ -1006,7 +1013,7 @@ impl Picker { let win_height = inner.height as usize; let len = doc_height; let fits = len <= win_height; - let scroll = offset.vertical_offset; + let scroll = current_line; let scroll_style = cx.editor.theme.get("ui.menu.scroll"); const fn div_ceil(a: usize, b: usize) -> usize {