Skip to content

Commit 899168e

Browse files
authored
Add highlighting for matches in fuzzy finder (#947)
closes #893
1 parent 153c79a commit 899168e

File tree

4 files changed

+55
-35
lines changed

4 files changed

+55
-35
lines changed

Diff for: CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
## Added
1111
- add `trace-libgit` feature to make git tracing optional [[@dm9pZCAq](https://github.com/dm9pZCAq)] ([#902](https://github.com/extrawurst/gitui/issues/902))
1212
- support merging and rebasing remote branches ([#920](https://github.com/extrawurst/gitui/issues/920))
13+
- add highlighting matches in fuzzy finder ([#893](https://github.com/extrawurst/gitui/issues/893))
1314

1415
## [0.18] - 2021-10-11
1516

Diff for: src/components/file_find_popup.rs

+36-23
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use std::borrow::Cow;
1717
use tui::{
1818
backend::Backend,
1919
layout::{Constraint, Direction, Layout, Margin, Rect},
20-
text::Span,
20+
text::{Span, Spans},
2121
widgets::{Block, Borders, Clear},
2222
Frame,
2323
};
@@ -31,7 +31,7 @@ pub struct FileFindPopup {
3131
files: Vec<TreeFile>,
3232
selection: usize,
3333
selected_index: Option<usize>,
34-
files_filtered: Vec<usize>,
34+
files_filtered: Vec<(usize, Vec<usize>)>,
3535
key_config: SharedKeyConfig,
3636
}
3737

@@ -91,8 +91,9 @@ impl FileFindPopup {
9191
self.files_filtered.extend(
9292
self.files.iter().enumerate().filter_map(|a| {
9393
a.1.path.to_str().and_then(|path| {
94-
//TODO: use fuzzy_indices and highlight hits
95-
matcher.fuzzy_match(path, q).map(|_| a.0)
94+
matcher
95+
.fuzzy_indices(path, q)
96+
.map(|(_, indicies)| (a.0, indicies))
9697
})
9798
}),
9899
);
@@ -104,7 +105,7 @@ impl FileFindPopup {
104105

105106
fn refresh_selection(&mut self) {
106107
let selection =
107-
self.files_filtered.get(self.selection).copied();
108+
self.files_filtered.get(self.selection).map(|a| a.0);
108109

109110
if self.selected_index != selection {
110111
self.selected_index = selection;
@@ -217,24 +218,36 @@ impl DrawableComponent for FileFindPopup {
217218
let height = usize::from(chunks[1].height);
218219
let width = usize::from(chunks[1].width);
219220

220-
let items =
221-
self.files_filtered.iter().take(height).map(
222-
|idx| {
223-
let selected = self
224-
.selected_index
225-
.map_or(false, |index| index == *idx);
226-
Span::styled(
227-
Cow::from(trim_length_left(
228-
self.files[*idx]
229-
.path
230-
.to_str()
231-
.unwrap_or_default(),
232-
width,
233-
)),
234-
self.theme.text(selected, false),
235-
)
236-
},
237-
);
221+
let items = self
222+
.files_filtered
223+
.iter()
224+
.take(height)
225+
.map(|(idx, indicies)| {
226+
let selected = self
227+
.selected_index
228+
.map_or(false, |index| index == *idx);
229+
let full_text = trim_length_left(
230+
self.files[*idx]
231+
.path
232+
.to_str()
233+
.unwrap_or_default(),
234+
width,
235+
);
236+
Spans::from(
237+
full_text
238+
.char_indices()
239+
.map(|(c_idx, c)| {
240+
Span::styled(
241+
Cow::from(c.to_string()),
242+
self.theme.text(
243+
selected,
244+
indicies.contains(&c_idx),
245+
),
246+
)
247+
})
248+
.collect::<Vec<_>>(),
249+
)
250+
});
238251

239252
ui::draw_list_block(
240253
f,

Diff for: src/ui/scrolllist.rs

+16-11
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@ use tui::{
55
buffer::Buffer,
66
layout::Rect,
77
style::Style,
8-
text::Span,
8+
text::{Span, Text},
99
widgets::{Block, Borders, List, ListItem, Widget},
1010
Frame,
1111
};
1212

1313
///
14-
struct ScrollableList<'b, L>
14+
struct ScrollableList<'b, L, S>
1515
where
16-
L: Iterator<Item = Span<'b>>,
16+
S: Into<Text<'b>>,
17+
L: Iterator<Item = S>,
1718
{
1819
block: Option<Block<'b>>,
1920
/// Items to be displayed
@@ -22,9 +23,10 @@ where
2223
style: Style,
2324
}
2425

25-
impl<'b, L> ScrollableList<'b, L>
26+
impl<'b, L, S> ScrollableList<'b, L, S>
2627
where
27-
L: Iterator<Item = Span<'b>>,
28+
S: Into<Text<'b>>,
29+
L: Iterator<Item = S>,
2830
{
2931
fn new(items: L) -> Self {
3032
Self {
@@ -40,9 +42,10 @@ where
4042
}
4143
}
4244

43-
impl<'b, L> Widget for ScrollableList<'b, L>
45+
impl<'b, L, S> Widget for ScrollableList<'b, L, S>
4446
where
45-
L: Iterator<Item = Span<'b>>,
47+
S: Into<Text<'b>>,
48+
L: Iterator<Item = S>,
4649
{
4750
fn render(self, area: Rect, buf: &mut Buffer) {
4851
// Render items
@@ -55,15 +58,16 @@ where
5558
}
5659
}
5760

58-
pub fn draw_list<'b, B: Backend, L>(
61+
pub fn draw_list<'b, B: Backend, L, S>(
5962
f: &mut Frame<B>,
6063
r: Rect,
6164
title: &'b str,
6265
items: L,
6366
selected: bool,
6467
theme: &SharedTheme,
6568
) where
66-
L: Iterator<Item = Span<'b>>,
69+
S: Into<Text<'b>>,
70+
L: Iterator<Item = S>,
6771
{
6872
let list = ScrollableList::new(items).block(
6973
Block::default()
@@ -74,13 +78,14 @@ pub fn draw_list<'b, B: Backend, L>(
7478
f.render_widget(list, r);
7579
}
7680

77-
pub fn draw_list_block<'b, B: Backend, L>(
81+
pub fn draw_list_block<'b, B: Backend, L, S>(
7882
f: &mut Frame<B>,
7983
r: Rect,
8084
block: Block<'b>,
8185
items: L,
8286
) where
83-
L: Iterator<Item = Span<'b>>,
87+
S: Into<Text<'b>>,
88+
L: Iterator<Item = S>,
8489
{
8590
let list = ScrollableList::new(items).block(block);
8691
f.render_widget(list, r);

Diff for: src/ui/style.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@ impl Theme {
117117

118118
pub fn text(&self, enabled: bool, selected: bool) -> Style {
119119
match (enabled, selected) {
120-
(false, _) => Style::default().fg(self.disabled_fg),
120+
(false, false) => Style::default().fg(self.disabled_fg),
121+
(false, true) => Style::default().bg(self.selection_bg),
121122
(true, false) => Style::default(),
122123
(true, true) => Style::default()
123124
.fg(self.command_fg)

0 commit comments

Comments
 (0)