More fuzzy finder (#892)

* allow selecting entries in fuzzy finder
* fix fuzzy finder also in files popup
* changelog
This commit is contained in:
Stephan Dilly 2021-09-04 23:56:34 +02:00 committed by GitHub
parent 26a9aaacf5
commit 6d6f60349a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 118 additions and 56 deletions

View file

@ -7,11 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
**fuzzy find files**
![fuzzy-find](assets/fuzzy-find.gif)
**emojified commit message**
![emojified-commit-message](assets/emojified-commit-message.png)
## Added
- fuzzy find files ([#891](https://github.com/extrawurst/gitui/issues/891))
- visualize progress during async syntax highlighting ([#889](https://github.com/extrawurst/gitui/issues/889))
- added support for markdown emoji's in commits [[@andrewpollack](https://github.com/andrewpollack)] ([#768](https://github.com/extrawurst/gitui/issues/768))
- added scrollbar to revlog [[@ashvin021](https://github.com/ashvin021)] ([#868](https://github.com/extrawurst/gitui/issues/868))

BIN
assets/fuzzy-find.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 KiB

View file

@ -438,6 +438,7 @@ impl App {
accessors!(
self,
[
find_file_popup,
msg,
reset,
commit,
@ -454,7 +455,6 @@ impl App {
rename_branch_popup,
select_branch_popup,
revision_files_popup,
find_file_popup,
tags_popup,
options_popup,
help,
@ -726,7 +726,8 @@ impl App {
.insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS);
}
InternalEvent::FileFinderChanged(file) => {
self.files_tab.file_finder_update(file);
self.files_tab.file_finder_update(&file);
self.revision_files_popup.file_finder_update(&file);
flags
.insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS);
}

View file

@ -1,6 +1,6 @@
use super::{
visibility_blocking, CommandBlocking, CommandInfo, Component,
DrawableComponent, EventState, TextInputComponent,
DrawableComponent, EventState, ScrollType, TextInputComponent,
};
use crate::{
keys::SharedKeyConfig,
@ -29,7 +29,8 @@ pub struct FileFindPopup {
query: Option<String>,
theme: SharedTheme,
files: Vec<TreeFile>,
selection: Option<usize>,
selection: usize,
selected_index: Option<usize>,
files_filtered: Vec<usize>,
key_config: SharedKeyConfig,
}
@ -58,8 +59,9 @@ impl FileFindPopup {
theme,
files: Vec::new(),
files_filtered: Vec::new(),
selected_index: None,
key_config,
selection: None,
selection: 0,
}
}
@ -94,22 +96,21 @@ impl FileFindPopup {
})
}),
);
self.refresh_selection();
} else {
self.files_filtered
.extend(self.files.iter().enumerate().map(|a| a.0));
}
self.selection = 0;
self.refresh_selection();
}
fn refresh_selection(&mut self) {
let selection = self.files_filtered.first().copied();
let selection =
self.files_filtered.get(self.selection).copied();
if self.selection != selection {
self.selection = selection;
if self.selected_index != selection {
self.selected_index = selection;
let file = self
.selection
.selected_index
.and_then(|index| self.files.get(index))
.map(|f| f.path.clone());
@ -129,6 +130,25 @@ impl FileFindPopup {
Ok(())
}
fn move_selection(&mut self, move_type: ScrollType) -> bool {
let new_selection = match move_type {
ScrollType::Up => self.selection.saturating_sub(1),
ScrollType::Down => self.selection.saturating_add(1),
_ => self.selection,
};
let new_selection = new_selection
.clamp(0, self.files_filtered.len().saturating_sub(1));
if new_selection != self.selection {
self.selection = new_selection;
self.refresh_selection();
return true;
}
false
}
}
impl DrawableComponent for FileFindPopup {
@ -138,9 +158,28 @@ impl DrawableComponent for FileFindPopup {
area: Rect,
) -> Result<()> {
if self.is_visible() {
const SIZE: (u16, u16) = (50, 25);
let area =
ui::centered_rect_absolute(SIZE.0, SIZE.1, area);
const MAX_SIZE: (u16, u16) = (50, 20);
let any_hits = !self.files_filtered.is_empty();
let area = ui::centered_rect_absolute(
MAX_SIZE.0, MAX_SIZE.1, area,
);
let area = if any_hits {
area
} else {
Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Length(3),
Constraint::Percentage(100),
]
.as_ref(),
)
.split(area)[0]
};
f.render_widget(Clear, area);
f.render_widget(
@ -155,7 +194,7 @@ impl DrawableComponent for FileFindPopup {
area,
);
let area = Layout::default()
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
@ -169,45 +208,46 @@ impl DrawableComponent for FileFindPopup {
vertical: 1,
}));
self.find_text.draw(f, area[0])?;
self.find_text.draw(f, chunks[0])?;
let height = usize::from(area[1].height);
let width = usize::from(area[1].width);
if any_hits {
let title =
format!("Hits: {}", self.files_filtered.len());
let items =
self.files_filtered.iter().take(height).map(|idx| {
let selected = self
.selection
.map_or(false, |selection| selection == *idx);
Span::styled(
Cow::from(trim_length_left(
self.files[*idx]
.path
.to_str()
.unwrap_or_default(),
width,
)),
self.theme.text(selected, false),
)
});
let height = usize::from(chunks[1].height);
let width = usize::from(chunks[1].width);
let title = format!(
"Hits: {}/{}",
height.min(self.files_filtered.len()),
self.files_filtered.len()
);
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),
)
},
);
ui::draw_list_block(
f,
area[1],
Block::default()
.title(Span::styled(
title,
self.theme.title(true),
))
.borders(Borders::TOP),
items,
);
ui::draw_list_block(
f,
chunks[1],
Block::default()
.title(Span::styled(
title,
self.theme.title(true),
))
.borders(Borders::TOP),
items,
);
}
}
Ok(())
}
@ -228,6 +268,12 @@ impl Component for FileFindPopup {
)
.order(1),
);
out.push(CommandInfo::new(
strings::commands::scroll(&self.key_config),
true,
true,
));
}
visibility_blocking(self)
@ -243,6 +289,10 @@ impl Component for FileFindPopup {
|| *key == self.key_config.enter
{
self.hide();
} else if *key == self.key_config.move_down {
self.move_selection(ScrollType::Down);
} else if *key == self.key_config.move_up {
self.move_selection(ScrollType::Up);
}
}

View file

@ -146,10 +146,10 @@ impl RevisionFilesComponent {
.push(InternalEvent::OpenFileFinder(self.files.clone()));
}
pub fn find_file(&mut self, file: Option<PathBuf>) {
pub fn find_file(&mut self, file: &Option<PathBuf>) {
if let Some(file) = file {
self.tree.collapse_but_root();
if self.tree.select_file(&file) {
if self.tree.select_file(file) {
self.selection_changed();
}
}

View file

@ -1,3 +1,5 @@
use std::path::PathBuf;
use super::{
revision_files::RevisionFilesComponent, visibility_blocking,
CommandBlocking, CommandInfo, Component, DrawableComponent,
@ -59,6 +61,10 @@ impl RevisionFilesPopup {
pub fn any_work_pending(&self) -> bool {
self.files.any_work_pending()
}
pub fn file_finder_update(&mut self, file: &Option<PathBuf>) {
self.files.find_file(file);
}
}
impl DrawableComponent for RevisionFilesPopup {

View file

@ -71,7 +71,7 @@ impl FilesTab {
}
}
pub fn file_finder_update(&mut self, file: Option<PathBuf>) {
pub fn file_finder_update(&mut self, file: &Option<PathBuf>) {
self.files.find_file(file);
}
}