From 899168e1ce0e17f1572ce36783bd67c0fb963ad1 Mon Sep 17 00:00:00 2001 From: Gleb Davydov <23462908+Mifom@users.noreply.github.com> Date: Fri, 15 Oct 2021 18:23:57 +0300 Subject: [PATCH] Add highlighting for matches in fuzzy finder (#947) closes #893 --- CHANGELOG.md | 1 + src/components/file_find_popup.rs | 59 +++++++++++++++++++------------ src/ui/scrolllist.rs | 27 ++++++++------ src/ui/style.rs | 3 +- 4 files changed, 55 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7f5a4da..75713919 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Added - add `trace-libgit` feature to make git tracing optional [[@dm9pZCAq](https://github.com/dm9pZCAq)] ([#902](https://github.com/extrawurst/gitui/issues/902)) - support merging and rebasing remote branches ([#920](https://github.com/extrawurst/gitui/issues/920)) +- add highlighting matches in fuzzy finder ([#893](https://github.com/extrawurst/gitui/issues/893)) ## [0.18] - 2021-10-11 diff --git a/src/components/file_find_popup.rs b/src/components/file_find_popup.rs index 7d2e0eda..829276b7 100644 --- a/src/components/file_find_popup.rs +++ b/src/components/file_find_popup.rs @@ -17,7 +17,7 @@ use std::borrow::Cow; use tui::{ backend::Backend, layout::{Constraint, Direction, Layout, Margin, Rect}, - text::Span, + text::{Span, Spans}, widgets::{Block, Borders, Clear}, Frame, }; @@ -31,7 +31,7 @@ pub struct FileFindPopup { files: Vec, selection: usize, selected_index: Option, - files_filtered: Vec, + files_filtered: Vec<(usize, Vec)>, key_config: SharedKeyConfig, } @@ -91,8 +91,9 @@ impl FileFindPopup { self.files_filtered.extend( self.files.iter().enumerate().filter_map(|a| { a.1.path.to_str().and_then(|path| { - //TODO: use fuzzy_indices and highlight hits - matcher.fuzzy_match(path, q).map(|_| a.0) + matcher + .fuzzy_indices(path, q) + .map(|(_, indicies)| (a.0, indicies)) }) }), ); @@ -104,7 +105,7 @@ impl FileFindPopup { fn refresh_selection(&mut self) { let selection = - self.files_filtered.get(self.selection).copied(); + self.files_filtered.get(self.selection).map(|a| a.0); if self.selected_index != selection { self.selected_index = selection; @@ -217,24 +218,36 @@ impl DrawableComponent for FileFindPopup { let height = usize::from(chunks[1].height); let width = usize::from(chunks[1].width); - let items = - self.files_filtered.iter().take(height).map( - |idx| { - let selected = self - .selected_index - .map_or(false, |index| index == *idx); - Span::styled( - Cow::from(trim_length_left( - self.files[*idx] - .path - .to_str() - .unwrap_or_default(), - width, - )), - self.theme.text(selected, false), - ) - }, - ); + let items = self + .files_filtered + .iter() + .take(height) + .map(|(idx, indicies)| { + let selected = self + .selected_index + .map_or(false, |index| index == *idx); + let full_text = trim_length_left( + self.files[*idx] + .path + .to_str() + .unwrap_or_default(), + width, + ); + Spans::from( + full_text + .char_indices() + .map(|(c_idx, c)| { + Span::styled( + Cow::from(c.to_string()), + self.theme.text( + selected, + indicies.contains(&c_idx), + ), + ) + }) + .collect::>(), + ) + }); ui::draw_list_block( f, diff --git a/src/ui/scrolllist.rs b/src/ui/scrolllist.rs index adc04bb3..e8a0e527 100644 --- a/src/ui/scrolllist.rs +++ b/src/ui/scrolllist.rs @@ -5,15 +5,16 @@ use tui::{ buffer::Buffer, layout::Rect, style::Style, - text::Span, + text::{Span, Text}, widgets::{Block, Borders, List, ListItem, Widget}, Frame, }; /// -struct ScrollableList<'b, L> +struct ScrollableList<'b, L, S> where - L: Iterator>, + S: Into>, + L: Iterator, { block: Option>, /// Items to be displayed @@ -22,9 +23,10 @@ where style: Style, } -impl<'b, L> ScrollableList<'b, L> +impl<'b, L, S> ScrollableList<'b, L, S> where - L: Iterator>, + S: Into>, + L: Iterator, { fn new(items: L) -> Self { Self { @@ -40,9 +42,10 @@ where } } -impl<'b, L> Widget for ScrollableList<'b, L> +impl<'b, L, S> Widget for ScrollableList<'b, L, S> where - L: Iterator>, + S: Into>, + L: Iterator, { fn render(self, area: Rect, buf: &mut Buffer) { // Render items @@ -55,7 +58,7 @@ where } } -pub fn draw_list<'b, B: Backend, L>( +pub fn draw_list<'b, B: Backend, L, S>( f: &mut Frame, r: Rect, title: &'b str, @@ -63,7 +66,8 @@ pub fn draw_list<'b, B: Backend, L>( selected: bool, theme: &SharedTheme, ) where - L: Iterator>, + S: Into>, + L: Iterator, { let list = ScrollableList::new(items).block( Block::default() @@ -74,13 +78,14 @@ pub fn draw_list<'b, B: Backend, L>( f.render_widget(list, r); } -pub fn draw_list_block<'b, B: Backend, L>( +pub fn draw_list_block<'b, B: Backend, L, S>( f: &mut Frame, r: Rect, block: Block<'b>, items: L, ) where - L: Iterator>, + S: Into>, + L: Iterator, { let list = ScrollableList::new(items).block(block); f.render_widget(list, r); diff --git a/src/ui/style.rs b/src/ui/style.rs index 7892304c..7579c9db 100644 --- a/src/ui/style.rs +++ b/src/ui/style.rs @@ -117,7 +117,8 @@ impl Theme { pub fn text(&self, enabled: bool, selected: bool) -> Style { match (enabled, selected) { - (false, _) => Style::default().fg(self.disabled_fg), + (false, false) => Style::default().fg(self.disabled_fg), + (false, true) => Style::default().bg(self.selection_bg), (true, false) => Style::default(), (true, true) => Style::default() .fg(self.command_fg)