diff options
author | Stephan Dilly <dilly.stephan@gmail.com> | 2021-09-04 10:50:03 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-04 10:50:03 +0200 |
commit | fb2b990072625eaecc23beb76bcd197c6536e80a (patch) | |
tree | 7ce2c42dccc7a4def7d983b51fd28a54719e330b | |
parent | 3b5d43ecb28d4846e4f5d16b3fa68ab63fd776c9 (diff) |
find files via fuzzy finder (#890)
-rw-r--r-- | Cargo.lock | 19 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | filetreelist/src/filetree.rs | 19 | ||||
-rw-r--r-- | filetreelist/src/filetreeitems.rs | 232 | ||||
-rw-r--r-- | filetreelist/src/item.rs | 13 | ||||
-rw-r--r-- | src/app.rs | 20 | ||||
-rw-r--r-- | src/components/diff.rs | 5 | ||||
-rw-r--r-- | src/components/file_find.rs | 271 | ||||
-rw-r--r-- | src/components/mod.rs | 21 | ||||
-rw-r--r-- | src/components/reset.rs | 2 | ||||
-rw-r--r-- | src/components/revision_files.rs | 32 | ||||
-rw-r--r-- | src/components/syntax_text.rs | 5 | ||||
-rw-r--r-- | src/components/textinput.rs | 33 | ||||
-rw-r--r-- | src/keys.rs | 2 | ||||
-rw-r--r-- | src/main.rs | 1 | ||||
-rw-r--r-- | src/queue.rs | 12 | ||||
-rw-r--r-- | src/string_utils.rs | 35 | ||||
-rw-r--r-- | src/tabs/files.rs | 6 | ||||
-rw-r--r-- | vim_style_key_config.ron | 1 |
19 files changed, 694 insertions, 36 deletions
@@ -411,6 +411,15 @@ dependencies = [ ] [[package]] +name = "fuzzy-matcher" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94" +dependencies = [ + "thread_local", +] + +[[package]] name = "getrandom" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -491,6 +500,7 @@ dependencies = [ "dirs-next", "easy-cast", "filetreelist", + "fuzzy-matcher", "gh-emoji", "itertools", "lazy_static", @@ -1423,6 +1433,15 @@ dependencies = [ ] [[package]] +name = "thread_local" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" +dependencies = [ + "once_cell", +] + +[[package]] name = "time" version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -48,6 +48,7 @@ bugreport = "0.4" lazy_static = "1.4" syntect = { version = "4.5", default-features = false, features = ["metadata", "default-fancy"]} gh-emoji = "1.0.6" +fuzzy-matcher = "0.3" [target.'cfg(all(target_family="unix",not(target_os="macos")))'.dependencies] which = "4.1" diff --git a/filetreelist/src/filetree.rs b/filetreelist/src/filetree.rs index 17db731e..bdde9cf0 100644 --- a/filetreelist/src/filetree.rs +++ b/filetreelist/src/filetree.rs @@ -142,6 +142,25 @@ impl FileTree { }) } + pub fn select_file(&mut self, path: &Path) -> bool { + let new_selection = self + .items + .tree_items + .iter() + .position(|item| item.info().full_path() == path); + + if new_selection == self.selection { + return false; + } + + self.selection = new_selection; + if let Some(selection) = self.selection { + self.items.show_element(selection); + } + self.visual_selection = self.calc_visual_selection(); + true + } + fn visual_index_to_absolute( &self, visual_index: usize, diff --git a/filetreelist/src/filetreeitems.rs b/filetreelist/src/filetreeitems.rs index b545f841..a3857870 100644 --- a/filetreelist/src/filetreeitems.rs +++ b/filetreelist/src/filetreeitems.rs @@ -148,7 +148,7 @@ impl FileTreeItems { if item_path.starts_with(&path) { item.hide(); } else { - return; + break; } } } @@ -188,6 +188,90 @@ impl FileTreeItems { } } + /// makes sure `index` is visible. + /// this expands all parents and shows all siblings + pub fn show_element(&mut self, index: usize) -> Option<usize> { + Some( + self.show_element_upward(index)? + + self.show_element_downward(index)?, + ) + } + + fn show_element_upward(&mut self, index: usize) -> Option<usize> { + let mut shown = 0_usize; + + let item = self.tree_items.get(index)?; + let mut current_folder: (PathBuf, u8) = ( + item.info().full_path().parent()?.to_path_buf(), + item.info().indent(), + ); + + let item_count = self.tree_items.len(); + for item in self + .tree_items + .iter_mut() + .rev() + .skip(item_count - index - 1) + { + if item.info().indent() == current_folder.1 { + item.show(); + shown += 1; + } else if item.info().indent() == current_folder.1 - 1 { + // this must be our parent + + item.expand_path(); + + if item.info().is_visible() { + // early out if parent already visible + break; + } + + item.show(); + shown += 1; + + current_folder = ( + item.info().full_path().parent()?.to_path_buf(), + item.info().indent(), + ); + } + } + + Some(shown) + } + + fn show_element_downward( + &mut self, + index: usize, + ) -> Option<usize> { + let mut shown = 0_usize; + + let item = self.tree_items.get(index)?; + let mut current_folder: (PathBuf, u8) = ( + item.info().full_path().parent()?.to_path_buf(), + item.info().indent(), + ); + + for item in self.tree_items.iter_mut().skip(index + 1) { + if item.info().indent() == current_folder.1 { + item.show(); + shown += 1; + } + if item.info().indent() == current_folder.1 - 1 { + // this must be our parent + + item.show(); + shown += 1; + + current_folder = ( + item.info().full_path().parent()?.to_path_buf(), + item.info().indent(), + ); + } + } + + Some(shown) + } + fn update_visibility( &mut self, prefix: &Option<PathBuf>, @@ -687,6 +771,152 @@ mod tests { ] ); } + + #[test] + fn test_show_element() { + let items = vec![ + Path::new("a/b/c"), // + Path::new("a/b2/d"), // + Path::new("a/b2/e"), // + ]; + + //0 a/ + //1 b/ + //2 c + //3 b2/ + //4 d + //5 e + + let mut tree = + FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); + + tree.collapse(0, true); + + let res = tree.show_element(5).unwrap(); + assert_eq!(res, 4); + assert!(tree.tree_items[3].kind().is_path()); + assert!(!tree.tree_items[3].kind().is_path_collapsed()); + + assert_eq!( + get_visibles(&tree), + vec![ + true, // + true, // + false, // + true, // + true, // + true, + ] + ); + } + + #[test] + fn test_show_element_later_elements() { + let items = vec![ + Path::new("a/b"), // + Path::new("a/c"), // + ]; + + //0 a/ + //1 b + //2 c + + let mut tree = + FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); + + tree.collapse(0, true); + + assert_eq!( + get_visibles(&tree), + vec![ + true, // + false, // + false, // + ] + ); + + let res = tree.show_element(1).unwrap(); + assert_eq!(res, 2); + + assert_eq!( + get_visibles(&tree), + vec![ + true, // + true, // + true, // + ] + ); + } + + #[test] + fn test_show_element_downward_parent() { + let items = vec![ + Path::new("a/b/c"), // + Path::new("a/d"), // + Path::new("a/e"), // + ]; + + //0 a/ + //1 b/ + //2 c + //3 d + //4 e + + let mut tree = + FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); + + tree.collapse(0, true); + + let res = tree.show_element(2).unwrap(); + assert_eq!(res, 4); + + assert_eq!( + get_visibles(&tree), + vec![ + true, // + true, // + true, // + true, // + true, // + ] + ); + } + + #[test] + fn test_show_element_expand_visible_parent() { + let items = vec![ + Path::new("a/b"), // + ]; + + //0 a/ + //1 b + + let mut tree = + FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); + + tree.collapse(0, true); + + assert_eq!( + get_visibles(&tree), + vec![ + true, // + false, // + ] + ); + + let res = tree.show_element(1).unwrap(); + assert_eq!(res, 1); + assert!(tree.tree_items[0].kind().is_path()); + assert!(!tree.tree_items[0].kind().is_path_collapsed()); + + assert_eq!( + get_visibles(&tree), + vec![ + true, // + true, // + ] + ); + } } #[cfg(test)] diff --git a/filetreelist/src/item.rs b/filetreelist/src/item.rs index f343e810..68f91d95 100644 --- a/filetreelist/src/item.rs +++ b/filetreelist/src/item.rs @@ -164,13 +164,17 @@ impl FileTreeItem { &self.kind } - /// + /// # Panics + /// panics if self is not a path pub fn collapse_path(&mut self) { + assert!(self.kind.is_path()); self.kind = FileTreeItemKind::Path(PathCollapsed(true)); } - /// + /// # Panics + /// panics if self is not a path pub fn expand_path(&mut self) { + assert!(self.kind.is_path()); self.kind = FileTreeItemKind::Path(PathCollapsed(false)); } @@ -178,6 +182,11 @@ impl FileTreeItem { pub fn hide(&mut self) { self.info.visible = false; } + + /// + pub fn show(&mut self) { + self.info.visible = true; + } } impl Eq for FileTreeItem {} @@ -6,7 +6,7 @@ use crate::{ BranchListComponent, CommandBlocking, CommandInfo, CommitComponent, CompareCommitsComponent, Component, ConfirmComponent, CreateBranchComponent, DrawableComponent, - ExternalEditorComponent, HelpComponent, + ExternalEditorComponent, FileFindComponent, HelpComponent, InspectCommitComponent, MsgComponent, OptionsPopupComponent, PullComponent, PushComponent, PushTagsComponent, RenameBranchComponent, RevisionFilesPopup, SharedOptions, @@ -51,6 +51,7 @@ pub struct App { compare_commits_popup: CompareCommitsComponent, external_editor_popup: ExternalEditorComponent, revision_files_popup: RevisionFilesPopup, + find_file_popup: FileFindComponent, push_popup: PushComponent, push_tags_popup: PushTagsComponent, pull_popup: PullComponent, @@ -189,6 +190,11 @@ impl App { key_config.clone(), options.clone(), ), + find_file_popup: FileFindComponent::new( + &queue, + theme.clone(), + key_config.clone(), + ), do_quit: false, cmdbar: RefCell::new(CommandBar::new( theme.clone(), @@ -448,6 +454,7 @@ impl App { rename_branch_popup, select_branch_popup, revision_files_popup, + find_file_popup, tags_popup, options_popup, help, @@ -475,6 +482,7 @@ impl App { create_branch_popup, rename_branch_popup, revision_files_popup, + find_file_popup, push_popup, push_tags_popup, pull_popup, @@ -693,6 +701,11 @@ impl App { flags .insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS); } + InternalEvent::OpenFileFinder(files) => { + self.find_file_popup.open(&files)?; + flags + .insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS); + } InternalEvent::OptionSwitched(o) => { match o { AppOption::StatusShowUntracked => { @@ -712,6 +725,11 @@ impl App { flags .insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS); } + InternalEvent::FileFinderChanged(file) => { + self.files_tab.file_finder_update(file); + flags + .insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS); + } }; Ok(flags) diff --git a/src/components/diff.rs b/src/components/diff.rs index c4496400..598c96ce 100644 --- a/src/components/diff.rs +++ b/src/components/diff.rs @@ -3,11 +3,10 @@ use super::{ Direction, DrawableComponent, ScrollType, }; use crate::{ - components::{ - tabs_to_spaces, CommandInfo, Component, EventState, - }, + components::{CommandInfo, Component, EventState}, keys::SharedKeyConfig, queue::{Action, InternalEvent, NeedsUpdate, Queue, ResetItem}, + string_utils::tabs_to_spaces, strings, try_or_popup, ui::style::SharedTheme, }; diff --git a/src/components/file_find.rs b/src/components/file_find.rs new file mode 100644 index 00000000..e302783b --- /dev/null +++ b/src/components/file_find.rs @@ -0,0 +1,271 @@ +use super::{ + visibility_blocking, CommandBlocking, CommandInfo, Component, + DrawableComponent, EventState, TextInputComponent, +}; +use crate::{ + keys::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 std::borrow::Cow; +use tui::{ + backend::Backend, + layout::{Constraint, Direction, Layout, Margin, Rect}, + text::Span, + widgets::{Block, Borders, Clear}, + Frame, +}; + +pub struct FileFindComponent { + queue: Queue, + visible: bool, + find_text: TextInputComponent, + query: Option<String>, + theme: SharedTheme, + files: Vec<TreeFile>, + selection: Option<usize>, + files_filtered: Vec<usize>, + key_config: SharedKeyConfig, +} + +impl FileFindComponent { + /// + 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(), + key_config, + selection: None, + } + } + + 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(); + + 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) + }) + }), + ); + + self.refresh_selection(); + } else { + self.files_filtered + .extend(self.files.iter().enumerate().map(|a| a.0)); + } + } + + fn refresh_selection(&mut self) { + let selection = self.files_filtered.first().copied(); + + if self.selection != selection { + self.selection = selection; + + let file = self + .selection + .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(()) + } +} + +impl DrawableComponent for FileFindComponent { + fn draw<B: Backend>( + &self, + f: &mut Frame<B>, + area: Rect, + ) -> Result<()> { + if self.is_visible() { + const SIZE: (u16, u16) = (50, 25); + let area = + ui::centered_rect_absolute(SIZE.0, SIZE.1, area); + + f.render_widget(Clear, area); + f.render_widget( + Block::default() + .borders(Borders::all()) + .style(self.theme.title(true)) + .title(Span::styled( + //TODO: strings + "Fuzzy find", + self.theme.title(true), + )), + area, + ); + + let area = 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, area[0])?; + + let height = usize::from(area[1].height); + let width = usize::from(area[1].width); + + 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 title = format!( + "Hits: {}/{}", + height.min(self.files_filtered.len()), + self.files_filtered.len() + ); + + ui::draw_list_block( + f, + area[1], + Block::default() + .title(Span::styled( + title, + self.theme.title(true), + )) + .borders(Borders::TOP), + items, + ); + } + Ok(()) + } +} + +impl Component for FileFindComponent { + fn commands( + &self, + out: &mut Vec<CommandInfo>, + force_all: bool, + ) -> CommandBlocking { + if self.is_visible() || force_all { + out.push( + CommandInfo::new( + strings::commands::close_popup(&self.key_config), + true, + true, + ) + .order(1), + ); + } + + 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 == self.key_config.exit_popup + || *key == self.key_config.enter + { + self.hide(); + } + } + + 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(()) + } +} diff --git a/src/components/mod.rs b/src/components/mod.rs index 523c3f89..064f7edf 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -10,6 +10,7 @@ mod create_branch; mod cred; mod diff; mod externaleditor; +mod file_find; mod filetree; mod help; mod inspect_commit; @@ -41,6 +42,7 @@ pub use compare_commits::CompareCommitsComponent; pub use create_branch::CreateBranchComponent; pub use diff::DiffComponent; pub use externaleditor::ExternalEditorComponent; +pub use file_find::FileFindComponent; pub use help::HelpComponent; pub use inspect_commit::InspectCommitComponent; pub use msg::MsgComponent; @@ -297,27 +299,24 @@ fn popup_paragraph<'a, T>( content: T, theme: &Theme, focused: bool, + block: bool, ) -> Paragraph<'a> where T: Into<Text<'a>>, { - Paragraph::new(content.into()) - .block( + let paragraph = Paragraph::new(content.into()) + .alignment(Alignment::Left) + .wrap(Wrap { trim: true }); + + if block { + paragraph.block( Block::default() .title(Span::styled(title, theme.title(focused))) .borders(Borders::ALL) .border_type(BorderType::Thick) .border_style(theme.block(focused)), ) - .alignment(Alignment::Left) - .wrap(Wrap { trim: true }) -} - -//TODO: allow customize tabsize -pub fn tabs_to_spaces(input: String) -> String { - if input.contains('\t') { - input.replace("\t", " ") } else { - input + paragraph } } diff --git a/src/components/reset.rs b/src/components/reset.rs index 02bef26e..0ff0a704 100644 --- a/src/components/reset.rs +++ b/src/components/reset.rs @@ -41,7 +41,7 @@ impl DrawableComponent for ConfirmComponent { let area = ui::centered_rect(50, 20, f.size()); f.render_widget(Clear, area); f.render_widget( - popup_paragraph(&title, txt, &self.theme, true), + popup_paragraph(&title, txt, &self.theme, true, true), area, ); } diff --git a/src/components/revision_files.rs b/src/components/revision_files.rs index 4a36bbd0..0abcf457 100644 --- a/src/components/revision_files.rs +++ b/src/components/revision_files.rs @@ -18,7 +18,11 @@ use asyncgit::{ use crossbeam_channel::Sender; use crossterm::event::Event; use filetreelist::{FileTree, FileTreeItem}; -use std::{collections::BTreeSet, convert::From, path::Path}; +use std::{ + collections::BTreeSet, + convert::From, + path::{Path, PathBuf}, +}; use tui::{ backend::Backend, layout::{Constraint, Direction, Layout, Rect}, @@ -137,6 +141,20 @@ impl RevisionFilesComponent { }) } + fn open_finder(&self) { + self.queue + .push(InternalEvent::OpenFileFinder(self.files.clone())); + } + + 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(); + } + } + } + fn selection_changed(&mut self) { //TODO: retrieve TreeFile from tree datastructure if let Some(file) = self @@ -144,6 +162,7 @@ impl RevisionFilesComponent { .selected_file() .map(|file| file.full_path_str().to_string()) { + log::info!("selected: {:?}", file); let path = Path::new(&file); if let Some(item) = self.files.iter().find(|f| f.path == path) @@ -188,7 +207,7 @@ impl RevisionFilesComponent { "Files at [{}]", self.revision .map(|c| c.get_short_string()) - .unwrap_or_default() + .unwrap_or_default(), ); ui::draw_list_block( f, @@ -241,7 +260,9 @@ impl Component for RevisionFilesComponent { out: &mut Vec<CommandInfo>, force_all: bool, ) -> CommandBlocking { - if matches!(self.focus, Focus::Tree) || force_all { + let is_tree_focused = matches!(self.focus, Focus::Tree); + + if is_tree_focused || force_all { out.push( CommandInfo::new( strings::commands::blame_file(&self.key_config), @@ -288,6 +309,11 @@ impl Component for RevisionFilesComponent { self.focus(false); return Ok(EventState::Consumed); } + } else if key == self.key_config.file_find { + if is_tree_focused { + self.open_finder(); + return Ok(EventState::Consumed); + } } else if !is_tree_focused { return self.current_file.event(event); } diff --git a/src/components/syntax_text.rs b/src/components/syntax_text.rs index cfef3199..9df85a49 100644 --- a/src/components/syntax_text.rs +++ b/src/components/syntax_text.rs @@ -1,9 +1,10 @@ use super::{ - tabs_to_spaces, CommandBlocking, CommandInfo, Component, - DrawableComponent, EventState, + CommandBlocking, CommandInfo, Component, DrawableComponent, + EventState, }; use crate::{ keys::SharedKeyConfig, + string_utils::tabs_to_spaces, strings, ui::{ self, common_nav, style::SharedTheme, AsyncSyntaxJob, diff --git a/src/components/textinput.rs b/src/components/textinput.rs index a53dd258..6086753d 100644 --- a/src/components/textinput.rs +++ b/src/components/textinput.rs @@ -41,6 +41,7 @@ pub struct TextInputComponent { cursor_position: usize, input_type: InputType, current_area: Cell<Rect>, + embed: bool, } impl TextInputComponent { @@ -63,6 +64,7 @@ impl TextInputComponent { cursor_position: 0, input_type: InputType::Multiline, current_area: Cell::new(Rect::default()), + embed: false, } } @@ -90,6 +92,11 @@ impl TextInputComponent { self.current_area.get() } + /// embed into parent draw area + pub fn embed(&mut self) { + self.embed = true; + } + /// Move the cursor right one char. fn incr_cursor(&mut self) { if let Some(pos) = self.next_char_position() { @@ -267,7 +274,7 @@ impl DrawableComponent for TextInputComponent { fn draw<B: Backend>( &self, f: &mut Frame<B>, - _rect: Rect, + rect: Rect, ) -> Result<()> { if self.visible { let txt = if self.msg.is_empty() { @@ -279,16 +286,21 @@ impl DrawableComponent for TextInputComponent { self.get_draw_text() }; - let area = match self.input_type { - InputType::Multiline => { - let area = ui::centered_rect(60, 20, f.size()); - ui::rect_inside( - Size::new(10, 3), - f.size().into(), - area, - ) + let area = if self.embed { + rect + } else { + match self.input_type { + InputType::Multiline => { + let area = + ui::centered_rect(60, 20, f.size()); + ui::rect_inside( + Size::new(10, 3), + f.size().into(), + area, + ) + } + _ => ui::centered_rect_absolute(32, 3, f.size()), } - _ => ui::centered_rect_absolute(32, 3, f.size()), }; f.render_widget(Clear, area); @@ -298,6 +310,7 @@ impl DrawableComponent for TextInputComponent { txt, &self.theme, true, + !self.embed, ), area, ); diff --git a/src/keys.rs b/src/keys.rs index 201610cf..dd6c2cff 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -83,6 +83,7 @@ pub struct KeyConfig { pub select_tag: KeyEvent, pub push: KeyEvent, pub open_file_tree: KeyEvent, + pub file_find: KeyEvent, |