Add highlighting for matches in fuzzy finder (#947)

closes #893
This commit is contained in:
Gleb Davydov 2021-10-15 18:23:57 +03:00 committed by GitHub
parent 153c79a828
commit 899168e1ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 55 additions and 35 deletions

View file

@ -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

View file

@ -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<TreeFile>,
selection: usize,
selected_index: Option<usize>,
files_filtered: Vec<usize>,
files_filtered: Vec<(usize, Vec<usize>)>,
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::<Vec<_>>(),
)
});
ui::draw_list_block(
f,

View file

@ -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<Item = Span<'b>>,
S: Into<Text<'b>>,
L: Iterator<Item = S>,
{
block: Option<Block<'b>>,
/// 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<Item = Span<'b>>,
S: Into<Text<'b>>,
L: Iterator<Item = S>,
{
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<Item = Span<'b>>,
S: Into<Text<'b>>,
L: Iterator<Item = S>,
{
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<B>,
r: Rect,
title: &'b str,
@ -63,7 +66,8 @@ pub fn draw_list<'b, B: Backend, L>(
selected: bool,
theme: &SharedTheme,
) where
L: Iterator<Item = Span<'b>>,
S: Into<Text<'b>>,
L: Iterator<Item = S>,
{
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<B>,
r: Rect,
block: Block<'b>,
items: L,
) where
L: Iterator<Item = Span<'b>>,
S: Into<Text<'b>>,
L: Iterator<Item = S>,
{
let list = ScrollableList::new(items).block(block);
f.render_widget(list, r);

View file

@ -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)