mirror of
https://github.com/gitui-org/gitui
synced 2026-05-23 00:48:35 +00:00
FuzzyFindPopup for general use (#1672)
* replace BranchFindPopup with FuzzyFindPopup * replace FileFindPopup with FuzzyFindPopup
This commit is contained in:
parent
be801a336f
commit
e90e8dc536
9 changed files with 123 additions and 457 deletions
69
src/app.rs
69
src/app.rs
|
|
@ -2,15 +2,16 @@ use crate::{
|
|||
accessors,
|
||||
cmdbar::CommandBar,
|
||||
components::{
|
||||
event_pump, AppOption, BlameFileComponent, BranchFindPopup,
|
||||
event_pump, AppOption, BlameFileComponent,
|
||||
BranchListComponent, CommandBlocking, CommandInfo,
|
||||
CommitComponent, CompareCommitsComponent, Component,
|
||||
ConfirmComponent, CreateBranchComponent, DrawableComponent,
|
||||
ExternalEditorComponent, FetchComponent, FileFindPopup,
|
||||
FileRevlogComponent, HelpComponent, InspectCommitComponent,
|
||||
MsgComponent, OptionsPopupComponent, PullComponent,
|
||||
PushComponent, PushTagsComponent, RenameBranchComponent,
|
||||
ResetPopupComponent, RevisionFilesPopup, StashMsgComponent,
|
||||
ExternalEditorComponent, FetchComponent, FileRevlogComponent,
|
||||
FuzzyFindPopup, FuzzyFinderTarget, HelpComponent,
|
||||
InspectCommitComponent, MsgComponent, OptionsPopupComponent,
|
||||
PullComponent, PushComponent, PushTagsComponent,
|
||||
RenameBranchComponent, ResetPopupComponent,
|
||||
RevisionFilesPopup, StashMsgComponent,
|
||||
SubmodulesListComponent, TagCommitComponent,
|
||||
TagListComponent,
|
||||
},
|
||||
|
|
@ -45,7 +46,7 @@ use ratatui::{
|
|||
};
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
path::Path,
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
|
@ -72,8 +73,7 @@ pub struct App {
|
|||
compare_commits_popup: CompareCommitsComponent,
|
||||
external_editor_popup: ExternalEditorComponent,
|
||||
revision_files_popup: RevisionFilesPopup,
|
||||
find_file_popup: FileFindPopup,
|
||||
branch_find_popup: BranchFindPopup,
|
||||
fuzzy_find_popup: FuzzyFindPopup,
|
||||
push_popup: PushComponent,
|
||||
push_tags_popup: PushTagsComponent,
|
||||
pull_popup: PullComponent,
|
||||
|
|
@ -271,12 +271,7 @@ impl App {
|
|||
theme.clone(),
|
||||
key_config.clone(),
|
||||
),
|
||||
find_file_popup: FileFindPopup::new(
|
||||
&queue,
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
),
|
||||
branch_find_popup: BranchFindPopup::new(
|
||||
fuzzy_find_popup: FuzzyFindPopup::new(
|
||||
&queue,
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
|
|
@ -585,8 +580,7 @@ impl App {
|
|||
accessors!(
|
||||
self,
|
||||
[
|
||||
find_file_popup,
|
||||
branch_find_popup,
|
||||
fuzzy_find_popup,
|
||||
msg,
|
||||
reset,
|
||||
commit,
|
||||
|
|
@ -637,8 +631,7 @@ impl App {
|
|||
create_branch_popup,
|
||||
rename_branch_popup,
|
||||
revision_files_popup,
|
||||
find_file_popup,
|
||||
branch_find_popup,
|
||||
fuzzy_find_popup,
|
||||
push_popup,
|
||||
push_tags_popup,
|
||||
pull_popup,
|
||||
|
|
@ -897,13 +890,8 @@ impl App {
|
|||
InternalEvent::StatusLastFileMoved => {
|
||||
self.status_tab.last_file_moved()?;
|
||||
}
|
||||
InternalEvent::OpenFileFinder(files) => {
|
||||
self.find_file_popup.open(&files)?;
|
||||
flags
|
||||
.insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS);
|
||||
}
|
||||
InternalEvent::OpenBranchFinder(branches) => {
|
||||
self.branch_find_popup.open(branches)?;
|
||||
InternalEvent::OpenFuzzyFinder(contents, target) => {
|
||||
self.fuzzy_find_popup.open(contents, target)?;
|
||||
flags
|
||||
.insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS);
|
||||
}
|
||||
|
|
@ -921,14 +909,25 @@ impl App {
|
|||
|
||||
flags.insert(NeedsUpdate::ALL);
|
||||
}
|
||||
InternalEvent::FileFinderChanged(file) => {
|
||||
self.files_tab.file_finder_update(&file);
|
||||
self.revision_files_popup.file_finder_update(&file);
|
||||
flags
|
||||
.insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS);
|
||||
}
|
||||
InternalEvent::BranchFinderChanged(idx) => {
|
||||
self.select_branch_popup.branch_finder_update(idx)?;
|
||||
InternalEvent::FuzzyFinderChanged(
|
||||
idx,
|
||||
content,
|
||||
target,
|
||||
) => {
|
||||
match target {
|
||||
FuzzyFinderTarget::Branches => self
|
||||
.select_branch_popup
|
||||
.branch_finder_update(idx)?,
|
||||
FuzzyFinderTarget::Files => {
|
||||
self.files_tab.file_finder_update(
|
||||
&PathBuf::from(content.clone()),
|
||||
);
|
||||
self.revision_files_popup.file_finder_update(
|
||||
&PathBuf::from(content),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
flags
|
||||
.insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS);
|
||||
}
|
||||
|
|
@ -1104,7 +1103,7 @@ impl App {
|
|||
|
||||
res.push(CommandInfo::new(
|
||||
strings::commands::find_file(&self.key_config),
|
||||
!self.find_file_popup.is_visible(),
|
||||
!self.fuzzy_find_popup.is_visible(),
|
||||
(!self.any_popup_visible()
|
||||
&& self.files_tab.is_visible())
|
||||
|| self.revision_files_popup.is_visible()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use super::{
|
||||
utils::scroll_vertical::VerticalScroll, visibility_blocking,
|
||||
CommandBlocking, CommandInfo, Component, DrawableComponent,
|
||||
EventState, InspectCommitOpen,
|
||||
EventState, FuzzyFinderTarget, InspectCommitOpen,
|
||||
};
|
||||
use crate::{
|
||||
components::ScrollType,
|
||||
|
|
@ -298,8 +298,10 @@ impl Component for BranchListComponent {
|
|||
.iter()
|
||||
.map(|b| b.name.clone())
|
||||
.collect();
|
||||
self.queue
|
||||
.push(InternalEvent::OpenBranchFinder(branches));
|
||||
self.queue.push(InternalEvent::OpenFuzzyFinder(
|
||||
branches,
|
||||
FuzzyFinderTarget::Branches,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -386,13 +388,8 @@ impl BranchListComponent {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn branch_finder_update(
|
||||
&mut self,
|
||||
idx: Option<usize>,
|
||||
) -> Result<()> {
|
||||
if let Some(idx) = idx {
|
||||
self.set_selection(idx.try_into()?)?;
|
||||
}
|
||||
pub fn branch_finder_update(&mut self, idx: usize) -> Result<()> {
|
||||
self.set_selection(idx.try_into()?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,349 +0,0 @@
|
|||
use super::{
|
||||
visibility_blocking, CommandBlocking, CommandInfo, Component,
|
||||
DrawableComponent, EventState, ScrollType, TextInputComponent,
|
||||
};
|
||||
use crate::{
|
||||
keys::{key_match, SharedKeyConfig},
|
||||
queue::{InternalEvent, Queue},
|
||||
string_utils::trim_length_left,
|
||||
strings,
|
||||
ui::{self, style::SharedTheme},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use asyncgit::sync::TreeFile;
|
||||
use crossterm::event::Event;
|
||||
use fuzzy_matcher::FuzzyMatcher;
|
||||
use ratatui::{
|
||||
backend::Backend,
|
||||
layout::{Constraint, Direction, Layout, Margin, Rect},
|
||||
text::{Line, Span},
|
||||
widgets::{Block, Borders, Clear},
|
||||
Frame,
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub struct FileFindPopup {
|
||||
queue: Queue,
|
||||
visible: bool,
|
||||
find_text: TextInputComponent,
|
||||
query: Option<String>,
|
||||
theme: SharedTheme,
|
||||
files: Vec<TreeFile>,
|
||||
selection: usize,
|
||||
selected_index: Option<usize>,
|
||||
files_filtered: Vec<(usize, Vec<usize>)>,
|
||||
key_config: SharedKeyConfig,
|
||||
}
|
||||
|
||||
impl FileFindPopup {
|
||||
///
|
||||
pub fn new(
|
||||
queue: &Queue,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
) -> Self {
|
||||
let mut find_text = TextInputComponent::new(
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
"",
|
||||
"start typing..",
|
||||
false,
|
||||
);
|
||||
find_text.embed();
|
||||
|
||||
Self {
|
||||
queue: queue.clone(),
|
||||
visible: false,
|
||||
query: None,
|
||||
find_text,
|
||||
theme,
|
||||
files: Vec::new(),
|
||||
files_filtered: Vec::new(),
|
||||
selected_index: None,
|
||||
key_config,
|
||||
selection: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn update_query(&mut self) {
|
||||
if self.find_text.get_text().is_empty() {
|
||||
self.set_query(None);
|
||||
} else if self
|
||||
.query
|
||||
.as_ref()
|
||||
.map_or(true, |q| q != self.find_text.get_text())
|
||||
{
|
||||
self.set_query(Some(
|
||||
self.find_text.get_text().to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn set_query(&mut self, query: Option<String>) {
|
||||
self.query = query;
|
||||
|
||||
self.files_filtered.clear();
|
||||
|
||||
if let Some(q) = &self.query {
|
||||
let matcher =
|
||||
fuzzy_matcher::skim::SkimMatcherV2::default();
|
||||
|
||||
let mut files = self
|
||||
.files
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|a| {
|
||||
a.1.path.to_str().and_then(|path| {
|
||||
matcher.fuzzy_indices(path, q).map(
|
||||
|(score, indices)| (score, a.0, indices),
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect::<Vec<(_, _, _)>>();
|
||||
|
||||
files.sort_by(|(score1, _, _), (score2, _, _)| {
|
||||
score2.cmp(score1)
|
||||
});
|
||||
|
||||
self.files_filtered.extend(
|
||||
files.into_iter().map(|entry| (entry.1, entry.2)),
|
||||
);
|
||||
}
|
||||
|
||||
self.selection = 0;
|
||||
self.refresh_selection();
|
||||
}
|
||||
|
||||
fn refresh_selection(&mut self) {
|
||||
let selection =
|
||||
self.files_filtered.get(self.selection).map(|a| a.0);
|
||||
|
||||
if self.selected_index != selection {
|
||||
self.selected_index = selection;
|
||||
|
||||
let file = self
|
||||
.selected_index
|
||||
.and_then(|index| self.files.get(index))
|
||||
.map(|f| f.path.clone());
|
||||
|
||||
self.queue.push(InternalEvent::FileFinderChanged(file));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open(&mut self, files: &[TreeFile]) -> Result<()> {
|
||||
self.show()?;
|
||||
self.find_text.show()?;
|
||||
self.find_text.set_text(String::new());
|
||||
self.query = None;
|
||||
if self.files != *files {
|
||||
self.files = files.to_owned();
|
||||
}
|
||||
self.update_query();
|
||||
|
||||
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 {
|
||||
fn draw<B: Backend>(
|
||||
&self,
|
||||
f: &mut Frame<B>,
|
||||
area: Rect,
|
||||
) -> Result<()> {
|
||||
if self.is_visible() {
|
||||
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(
|
||||
Block::default()
|
||||
.borders(Borders::all())
|
||||
.style(self.theme.title(true))
|
||||
.title(Span::styled(
|
||||
strings::POPUP_TITLE_FUZZY_FIND,
|
||||
self.theme.title(true),
|
||||
)),
|
||||
area,
|
||||
);
|
||||
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints(
|
||||
[
|
||||
Constraint::Length(1),
|
||||
Constraint::Percentage(100),
|
||||
]
|
||||
.as_ref(),
|
||||
)
|
||||
.split(area.inner(&Margin {
|
||||
horizontal: 1,
|
||||
vertical: 1,
|
||||
}));
|
||||
|
||||
self.find_text.draw(f, chunks[0])?;
|
||||
|
||||
if any_hits {
|
||||
let title =
|
||||
format!("Hits: {}", self.files_filtered.len());
|
||||
|
||||
let height = usize::from(chunks[1].height);
|
||||
let width = usize::from(chunks[1].width);
|
||||
|
||||
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,
|
||||
);
|
||||
Line::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,
|
||||
chunks[1],
|
||||
Block::default()
|
||||
.title(Span::styled(
|
||||
title,
|
||||
self.theme.title(true),
|
||||
))
|
||||
.borders(Borders::TOP),
|
||||
items,
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for FileFindPopup {
|
||||
fn commands(
|
||||
&self,
|
||||
out: &mut Vec<CommandInfo>,
|
||||
force_all: bool,
|
||||
) -> CommandBlocking {
|
||||
if self.is_visible() || force_all {
|
||||
out.push(CommandInfo::new(
|
||||
strings::commands::scroll_popup(&self.key_config),
|
||||
true,
|
||||
true,
|
||||
));
|
||||
|
||||
out.push(CommandInfo::new(
|
||||
strings::commands::close_fuzzy_finder(
|
||||
&self.key_config,
|
||||
),
|
||||
true,
|
||||
true,
|
||||
));
|
||||
}
|
||||
|
||||
visibility_blocking(self)
|
||||
}
|
||||
|
||||
fn event(
|
||||
&mut self,
|
||||
event: &crossterm::event::Event,
|
||||
) -> Result<EventState> {
|
||||
if self.is_visible() {
|
||||
if let Event::Key(key) = event {
|
||||
if key_match(key, self.key_config.keys.exit_popup)
|
||||
|| key_match(key, self.key_config.keys.enter)
|
||||
{
|
||||
self.hide();
|
||||
} else if key_match(
|
||||
key,
|
||||
self.key_config.keys.popup_down,
|
||||
) {
|
||||
self.move_selection(ScrollType::Down);
|
||||
} else if key_match(
|
||||
key,
|
||||
self.key_config.keys.popup_up,
|
||||
) {
|
||||
self.move_selection(ScrollType::Up);
|
||||
}
|
||||
}
|
||||
|
||||
if self.find_text.event(event)?.is_consumed() {
|
||||
self.update_query();
|
||||
}
|
||||
|
||||
return Ok(EventState::Consumed);
|
||||
}
|
||||
|
||||
Ok(EventState::NotConsumed)
|
||||
}
|
||||
|
||||
fn is_visible(&self) -> bool {
|
||||
self.visible
|
||||
}
|
||||
|
||||
fn hide(&mut self) {
|
||||
self.visible = false;
|
||||
}
|
||||
|
||||
fn show(&mut self) -> Result<()> {
|
||||
self.visible = true;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
use super::{
|
||||
visibility_blocking, CommandBlocking, CommandInfo, Component,
|
||||
DrawableComponent, EventState, ScrollType, TextInputComponent,
|
||||
DrawableComponent, EventState, FuzzyFinderTarget, ScrollType,
|
||||
TextInputComponent,
|
||||
};
|
||||
use crate::{
|
||||
keys::{key_match, SharedKeyConfig},
|
||||
|
|
@ -21,20 +22,21 @@ use ratatui::{
|
|||
};
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub struct BranchFindPopup {
|
||||
pub struct FuzzyFindPopup {
|
||||
queue: Queue,
|
||||
visible: bool,
|
||||
find_text: TextInputComponent,
|
||||
query: Option<String>,
|
||||
theme: SharedTheme,
|
||||
branches: Vec<String>,
|
||||
contents: Vec<String>,
|
||||
selection: usize,
|
||||
selected_index: Option<usize>,
|
||||
branches_filtered: Vec<(usize, Vec<usize>)>,
|
||||
filtered: Vec<(usize, Vec<usize>)>,
|
||||
key_config: SharedKeyConfig,
|
||||
target: Option<FuzzyFinderTarget>,
|
||||
}
|
||||
|
||||
impl BranchFindPopup {
|
||||
impl FuzzyFindPopup {
|
||||
///
|
||||
pub fn new(
|
||||
queue: &Queue,
|
||||
|
|
@ -56,11 +58,12 @@ impl BranchFindPopup {
|
|||
query: None,
|
||||
find_text,
|
||||
theme,
|
||||
branches: Vec::new(),
|
||||
branches_filtered: Vec::new(),
|
||||
contents: Vec::new(),
|
||||
filtered: Vec::new(),
|
||||
selected_index: None,
|
||||
key_config,
|
||||
selection: 0,
|
||||
target: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -81,14 +84,14 @@ impl BranchFindPopup {
|
|||
fn set_query(&mut self, query: Option<String>) {
|
||||
self.query = query;
|
||||
|
||||
self.branches_filtered.clear();
|
||||
self.filtered.clear();
|
||||
|
||||
if let Some(q) = &self.query {
|
||||
let matcher =
|
||||
fuzzy_matcher::skim::SkimMatcherV2::default();
|
||||
|
||||
let mut branches = self
|
||||
.branches
|
||||
let mut contents = self
|
||||
.contents
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|a| {
|
||||
|
|
@ -98,12 +101,12 @@ impl BranchFindPopup {
|
|||
})
|
||||
.collect::<Vec<(_, _, _)>>();
|
||||
|
||||
branches.sort_by(|(score1, _, _), (score2, _, _)| {
|
||||
contents.sort_by(|(score1, _, _), (score2, _, _)| {
|
||||
score2.cmp(score1)
|
||||
});
|
||||
|
||||
self.branches_filtered.extend(
|
||||
branches.into_iter().map(|entry| (entry.1, entry.2)),
|
||||
self.filtered.extend(
|
||||
contents.into_iter().map(|entry| (entry.1, entry.2)),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -113,23 +116,37 @@ impl BranchFindPopup {
|
|||
|
||||
fn refresh_selection(&mut self) {
|
||||
let selection =
|
||||
self.branches_filtered.get(self.selection).map(|a| a.0);
|
||||
self.filtered.get(self.selection).map(|a| a.0);
|
||||
|
||||
if self.selected_index != selection {
|
||||
self.selected_index = selection;
|
||||
|
||||
let idx = self.selected_index;
|
||||
self.queue.push(InternalEvent::BranchFinderChanged(idx));
|
||||
if let Some(idx) = self.selected_index {
|
||||
if let Some(target) = self.target {
|
||||
self.queue.push(
|
||||
InternalEvent::FuzzyFinderChanged(
|
||||
idx,
|
||||
self.contents[idx].clone(),
|
||||
target,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open(&mut self, branches: Vec<String>) -> Result<()> {
|
||||
pub fn open(
|
||||
&mut self,
|
||||
contents: Vec<String>,
|
||||
target: FuzzyFinderTarget,
|
||||
) -> Result<()> {
|
||||
self.show()?;
|
||||
self.find_text.show()?;
|
||||
self.find_text.set_text(String::new());
|
||||
self.query = None;
|
||||
if self.branches != branches {
|
||||
self.branches = branches;
|
||||
self.target = Some(target);
|
||||
if self.contents != contents {
|
||||
self.contents = contents;
|
||||
}
|
||||
self.update_query();
|
||||
|
||||
|
|
@ -144,7 +161,7 @@ impl BranchFindPopup {
|
|||
};
|
||||
|
||||
let new_selection = new_selection
|
||||
.clamp(0, self.branches_filtered.len().saturating_sub(1));
|
||||
.clamp(0, self.filtered.len().saturating_sub(1));
|
||||
|
||||
if new_selection != self.selection {
|
||||
self.selection = new_selection;
|
||||
|
|
@ -156,7 +173,7 @@ impl BranchFindPopup {
|
|||
}
|
||||
}
|
||||
|
||||
impl DrawableComponent for BranchFindPopup {
|
||||
impl DrawableComponent for FuzzyFindPopup {
|
||||
fn draw<B: Backend>(
|
||||
&self,
|
||||
f: &mut Frame<B>,
|
||||
|
|
@ -165,7 +182,7 @@ impl DrawableComponent for BranchFindPopup {
|
|||
if self.is_visible() {
|
||||
const MAX_SIZE: (u16, u16) = (50, 20);
|
||||
|
||||
let any_hits = !self.branches_filtered.is_empty();
|
||||
let any_hits = !self.filtered.is_empty();
|
||||
|
||||
let area = ui::centered_rect_absolute(
|
||||
MAX_SIZE.0, MAX_SIZE.1, area,
|
||||
|
|
@ -215,22 +232,18 @@ impl DrawableComponent for BranchFindPopup {
|
|||
self.find_text.draw(f, chunks[0])?;
|
||||
|
||||
if any_hits {
|
||||
let title =
|
||||
format!("Hits: {}", self.branches_filtered.len());
|
||||
let title = format!("Hits: {}", self.filtered.len());
|
||||
|
||||
let height = usize::from(chunks[1].height);
|
||||
let width = usize::from(chunks[1].width);
|
||||
|
||||
let items = self
|
||||
.branches_filtered
|
||||
.iter()
|
||||
.take(height)
|
||||
.map(|(idx, indicies)| {
|
||||
let items = self.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.branches[*idx],
|
||||
&self.contents[*idx],
|
||||
width,
|
||||
);
|
||||
Line::from(
|
||||
|
|
@ -247,7 +260,8 @@ impl DrawableComponent for BranchFindPopup {
|
|||
})
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
ui::draw_list_block(
|
||||
f,
|
||||
|
|
@ -266,7 +280,7 @@ impl DrawableComponent for BranchFindPopup {
|
|||
}
|
||||
}
|
||||
|
||||
impl Component for BranchFindPopup {
|
||||
impl Component for FuzzyFindPopup {
|
||||
fn commands(
|
||||
&self,
|
||||
out: &mut Vec<CommandInfo>,
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
mod blame_file;
|
||||
mod branch_find_popup;
|
||||
mod branchlist;
|
||||
mod changes;
|
||||
mod command;
|
||||
|
|
@ -12,8 +11,8 @@ mod cred;
|
|||
mod diff;
|
||||
mod externaleditor;
|
||||
mod fetch;
|
||||
mod file_find_popup;
|
||||
mod file_revlog;
|
||||
mod fuzzy_find_popup;
|
||||
mod help;
|
||||
mod inspect_commit;
|
||||
mod msg;
|
||||
|
|
@ -37,7 +36,6 @@ mod utils;
|
|||
|
||||
pub use self::status_tree::StatusTreeComponent;
|
||||
pub use blame_file::{BlameFileComponent, BlameFileOpen};
|
||||
pub use branch_find_popup::BranchFindPopup;
|
||||
pub use branchlist::BranchListComponent;
|
||||
pub use changes::ChangesComponent;
|
||||
pub use command::{CommandInfo, CommandText};
|
||||
|
|
@ -49,8 +47,8 @@ pub use create_branch::CreateBranchComponent;
|
|||
pub use diff::DiffComponent;
|
||||
pub use externaleditor::ExternalEditorComponent;
|
||||
pub use fetch::FetchComponent;
|
||||
pub use file_find_popup::FileFindPopup;
|
||||
pub use file_revlog::{FileRevOpen, FileRevlogComponent};
|
||||
pub use fuzzy_find_popup::FuzzyFindPopup;
|
||||
pub use help::HelpComponent;
|
||||
pub use inspect_commit::{InspectCommitComponent, InspectCommitOpen};
|
||||
pub use msg::MsgComponent;
|
||||
|
|
@ -236,6 +234,12 @@ pub enum EventState {
|
|||
NotConsumed,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum FuzzyFinderTarget {
|
||||
Branches,
|
||||
Files,
|
||||
}
|
||||
|
||||
impl EventState {
|
||||
pub fn is_consumed(&self) -> bool {
|
||||
*self == Self::Consumed
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use super::{
|
||||
utils::scroll_vertical::VerticalScroll, BlameFileOpen,
|
||||
CommandBlocking, CommandInfo, Component, DrawableComponent,
|
||||
EventState, FileRevOpen, SyntaxTextComponent,
|
||||
EventState, FileRevOpen, FuzzyFinderTarget, SyntaxTextComponent,
|
||||
};
|
||||
use crate::{
|
||||
keys::{key_match, SharedKeyConfig},
|
||||
|
|
@ -30,11 +30,7 @@ use ratatui::{
|
|||
Frame,
|
||||
};
|
||||
use std::{borrow::Cow, fmt::Write};
|
||||
use std::{
|
||||
collections::BTreeSet,
|
||||
convert::From,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use std::{collections::BTreeSet, convert::From, path::Path};
|
||||
use unicode_truncate::UnicodeTruncateStr;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
|
|
@ -233,17 +229,26 @@ impl RevisionFilesComponent {
|
|||
}
|
||||
|
||||
fn open_finder(&self) {
|
||||
self.queue.push(InternalEvent::OpenFileFinder(
|
||||
self.files.clone().unwrap_or_default(),
|
||||
));
|
||||
if let Some(files) = self.files.clone() {
|
||||
self.queue.push(InternalEvent::OpenFuzzyFinder(
|
||||
files
|
||||
.iter()
|
||||
.map(|a| {
|
||||
a.path
|
||||
.to_str()
|
||||
.unwrap_or_default()
|
||||
.to_string()
|
||||
})
|
||||
.collect(),
|
||||
FuzzyFinderTarget::Files,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
self.selection_changed();
|
||||
}
|
||||
pub fn find_file(&mut self, file: &Path) {
|
||||
self.tree.collapse_but_root();
|
||||
if self.tree.select_file(file) {
|
||||
self.selection_changed();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::path::PathBuf;
|
||||
use std::path::Path;
|
||||
|
||||
use super::{
|
||||
revision_files::RevisionFilesComponent, visibility_blocking,
|
||||
|
|
@ -91,7 +91,7 @@ impl RevisionFilesPopup {
|
|||
self.files.any_work_pending()
|
||||
}
|
||||
|
||||
pub fn file_finder_update(&mut self, file: &Option<PathBuf>) {
|
||||
pub fn file_finder_update(&mut self, file: &Path) {
|
||||
self.files.find_file(file);
|
||||
}
|
||||
|
||||
|
|
|
|||
12
src/queue.rs
12
src/queue.rs
|
|
@ -1,12 +1,12 @@
|
|||
use crate::{
|
||||
components::{
|
||||
AppOption, BlameFileOpen, FileRevOpen, FileTreeOpen,
|
||||
InspectCommitOpen,
|
||||
FuzzyFinderTarget, InspectCommitOpen,
|
||||
},
|
||||
tabs::StashingOptions,
|
||||
};
|
||||
use asyncgit::{
|
||||
sync::{diff::DiffLinePosition, CommitId, TreeFile},
|
||||
sync::{diff::DiffLinePosition, CommitId},
|
||||
PushType,
|
||||
};
|
||||
use bitflags::bitflags;
|
||||
|
|
@ -111,13 +111,9 @@ pub enum InternalEvent {
|
|||
///
|
||||
OptionSwitched(AppOption),
|
||||
///
|
||||
OpenFileFinder(Vec<TreeFile>),
|
||||
OpenFuzzyFinder(Vec<String>, FuzzyFinderTarget),
|
||||
///
|
||||
OpenBranchFinder(Vec<String>),
|
||||
///
|
||||
FileFinderChanged(Option<PathBuf>),
|
||||
///
|
||||
BranchFinderChanged(Option<usize>),
|
||||
FuzzyFinderChanged(usize, String, FuzzyFinderTarget),
|
||||
///
|
||||
FetchRemotes,
|
||||
///
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::path::PathBuf;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::{
|
||||
components::{
|
||||
|
|
@ -75,7 +75,7 @@ impl FilesTab {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn file_finder_update(&mut self, file: &Option<PathBuf>) {
|
||||
pub fn file_finder_update(&mut self, file: &Path) {
|
||||
self.files.find_file(file);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue