diff options
author | sushi-shi <47691267+sushi-shi@users.noreply.github.com> | 2022-02-23 20:39:46 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-02-23 12:39:46 -0500 |
commit | fe3308c75da696b37c970c12ef9cefe942fa8377 (patch) | |
tree | b4baefda219918cd9603b7dc3e1d2ab222eb28c1 /src | |
parent | 1a78bb897a191ce5e816bf690be52ec03222369f (diff) |
Add incremental search (#140)
Diffstat (limited to 'src')
-rw-r--r-- | src/commands/search_string.rs | 26 | ||||
-rw-r--r-- | src/error/mod.rs | 2 | ||||
-rw-r--r-- | src/key_command/command.rs | 1 | ||||
-rw-r--r-- | src/key_command/constants.rs | 1 | ||||
-rw-r--r-- | src/key_command/impl_appcommand.rs | 1 | ||||
-rw-r--r-- | src/key_command/impl_appexecute.rs | 12 | ||||
-rw-r--r-- | src/key_command/impl_comment.rs | 1 | ||||
-rw-r--r-- | src/key_command/impl_from_str.rs | 2 | ||||
-rw-r--r-- | src/key_command/impl_interactive.rs | 15 | ||||
-rw-r--r-- | src/key_command/mod.rs | 1 | ||||
-rw-r--r-- | src/key_command/traits.rs | 8 | ||||
-rw-r--r-- | src/ui/views/tui_textfield.rs | 8 |
12 files changed, 68 insertions, 10 deletions
diff --git a/src/commands/search_string.rs b/src/commands/search_string.rs index 62d9a7f..70b0795 100644 --- a/src/commands/search_string.rs +++ b/src/commands/search_string.rs @@ -1,5 +1,4 @@ use crate::context::AppContext; -use crate::error::JoshutoResult; use crate::tab::JoshutoTab; use crate::util::search::SearchPattern; @@ -20,6 +19,20 @@ pub fn search_string_fwd(curr_tab: &JoshutoTab, pattern: &str) -> Option<usize> } None } + +pub fn search_string_start(curr_tab: &JoshutoTab, pattern: &str) -> Option<usize> { + let curr_list = curr_tab.curr_list_ref()?; + + let contents_len = curr_list.contents.len(); + for i in 0..contents_len { + let file_name_lower = curr_list.contents[i].file_name().to_lowercase(); + if file_name_lower.contains(pattern) { + return Some(i); + } + } + None +} + pub fn search_string_rev(curr_tab: &JoshutoTab, pattern: &str) -> Option<usize> { let curr_list = curr_tab.curr_list_ref()?; @@ -36,12 +49,17 @@ pub fn search_string_rev(curr_tab: &JoshutoTab, pattern: &str) -> Option<usize> None } -pub fn search_string(context: &mut AppContext, pattern: &str) -> JoshutoResult<()> { +pub fn search_string(context: &mut AppContext, pattern: &str, incremental: bool) { let pattern = pattern.to_lowercase(); - let index = search_string_fwd(context.tab_context_ref().curr_tab_ref(), pattern.as_str()); + let curr_tab = context.tab_context_ref().curr_tab_ref(); + + let index = if incremental { + search_string_start(curr_tab, pattern.as_str()) + } else { + search_string_fwd(curr_tab, pattern.as_str()) + }; if let Some(index) = index { let _ = cursor_move::cursor_move(context, index); } context.set_search_context(SearchPattern::String(pattern)); - Ok(()) } diff --git a/src/error/mod.rs b/src/error/mod.rs index f305df3..8c4ca24 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -4,4 +4,4 @@ mod error_type; pub use self::error_kind::JoshutoErrorKind; pub use self::error_type::JoshutoError; -pub type JoshutoResult<T> = Result<T, JoshutoError>; +pub type JoshutoResult<T = ()> = Result<T, JoshutoError>; diff --git a/src/key_command/command.rs b/src/key_command/command.rs index e20a642..6560ed1 100644 --- a/src/key_command/command.rs +++ b/src/key_command/command.rs @@ -52,6 +52,7 @@ pub enum Command { SearchGlob(String), SearchString(String), + SearchIncremental(String), SearchFzf, SearchNext, SearchPrev, diff --git a/src/key_command/constants.rs b/src/key_command/constants.rs index 101bf15..29d1591 100644 --- a/src/key_command/constants.rs +++ b/src/key_command/constants.rs @@ -53,6 +53,7 @@ cmd_constants![ (CMD_RENAME_FILE_APPEND, "rename_append"), (CMD_RENAME_FILE_PREPEND, "rename_prepend"), (CMD_SEARCH_STRING, "search"), + (CMD_SEARCH_INCREMENTAL, "search_inc"), (CMD_SEARCH_GLOB, "search_glob"), (CMD_SEARCH_FZF, "search_fzf"), (CMD_SEARCH_NEXT, "search_next"), diff --git a/src/key_command/impl_appcommand.rs b/src/key_command/impl_appcommand.rs index c1bc0df..aa15c51 100644 --- a/src/key_command/impl_appcommand.rs +++ b/src/key_command/impl_appcommand.rs @@ -52,6 +52,7 @@ impl AppCommand for Command { Self::RenameFilePrepend => CMD_RENAME_FILE_PREPEND, Self::SearchString(_) => CMD_SEARCH_STRING, + Self::SearchIncremental(_) => CMD_SEARCH_INCREMENTAL, Self::SearchGlob(_) => CMD_SEARCH_GLOB, Self::SearchFzf => CMD_SEARCH_FZF, Self::SearchNext => CMD_SEARCH_NEXT, diff --git a/src/key_command/impl_appexecute.rs b/src/key_command/impl_appexecute.rs index 540d20e..0e6b075 100644 --- a/src/key_command/impl_appexecute.rs +++ b/src/key_command/impl_appexecute.rs @@ -12,7 +12,7 @@ impl AppExecute for Command { context: &mut AppContext, backend: &mut TuiBackend, keymap_t: &AppKeyMapping, - ) -> JoshutoResult<()> { + ) -> JoshutoResult { match &*self { Self::BulkRename => bulk_rename::bulk_rename(context, backend), @@ -70,7 +70,15 @@ impl AppExecute for Command { Self::RenameFilePrepend => rename_file::rename_file_prepend(context, backend, keymap_t), Self::TouchFile(arg) => touch_file::touch_file(context, arg.as_str()), Self::SearchGlob(pattern) => search_glob::search_glob(context, pattern.as_str()), - Self::SearchString(pattern) => search_string::search_string(context, pattern.as_str()), + Self::SearchString(pattern) => Ok(search_string::search_string( + context, + pattern.as_str(), + false, + )), + // We call `interactive_execute` on each key press, so even before Enter is pressed the + // cursor will be one the selected word. And as `interactive_execute` for + // `SearchIncremental` always starts from index 0, this operation will be a no-op + Self::SearchIncremental(_) => Ok(()), Self::SearchFzf => search_fzf::search_fzf(context, backend), Self::SearchNext => search::search_next(context), Self::SearchPrev => search::search_prev(context), diff --git a/src/key_command/impl_comment.rs b/src/key_command/impl_comment.rs index 11265d9..9db7cec 100644 --- a/src/key_command/impl_comment.rs +++ b/src/key_command/impl_comment.rs @@ -68,6 +68,7 @@ impl CommandComment for Command { Self::RenameFilePrepend => "Rename a file", Self::SearchString(_) => "Search", + Self::SearchIncremental(_) => "Search as you type", Self::SearchGlob(_) => "Search with globbing", Self::SearchFzf => "Search via fzf", Self::SearchNext => "Next search entry", diff --git a/src/key_command/impl_from_str.rs b/src/key_command/impl_from_str.rs index aff7996..589e420 100644 --- a/src/key_command/impl_from_str.rs +++ b/src/key_command/impl_from_str.rs @@ -220,6 +220,8 @@ impl std::str::FromStr for Command { )), arg => Ok(Self::SearchString(arg.to_string())), } + } else if command == CMD_SEARCH_INCREMENTAL { + Ok(Self::SearchIncremental(arg.to_string())) } else if command == CMD_SEARCH_GLOB { match arg { "" => Err(JoshutoError::new( diff --git a/src/key_command/impl_interactive.rs b/src/key_command/impl_interactive.rs new file mode 100644 index 0000000..98ad337 --- /dev/null +++ b/src/key_command/impl_interactive.rs @@ -0,0 +1,15 @@ +use crate::commands::*; +use crate::context::AppContext; + +use super::{Command, InteractiveExecute}; + +impl InteractiveExecute for Command { + fn interactive_execute(&self, context: &mut AppContext) { + match self { + Self::SearchIncremental(pattern) => { + search_string::search_string(context, pattern.as_str(), true) + } + _ => (), + } + } +} diff --git a/src/key_command/mod.rs b/src/key_command/mod.rs index 6d7bcfe..f337c0d 100644 --- a/src/key_command/mod.rs +++ b/src/key_command/mod.rs @@ -8,6 +8,7 @@ mod impl_appexecute; mod impl_comment; mod impl_display; mod impl_from_str; +mod impl_interactive; mod impl_numbered; pub use self::command::*; diff --git a/src/key_command/traits.rs b/src/key_command/traits.rs index f988d73..08f6c0d 100644 --- a/src/key_command/traits.rs +++ b/src/key_command/traits.rs @@ -9,7 +9,7 @@ pub trait AppExecute { context: &mut AppContext, backend: &mut TuiBackend, keymap_t: &AppKeyMapping, - ) -> JoshutoResult<()>; + ) -> JoshutoResult; } pub trait NumberedExecute { @@ -19,7 +19,11 @@ pub trait NumberedExecute { context: &mut AppContext, backend: &mut TuiBackend, keymap_t: &AppKeyMapping, - ) -> JoshutoResult<()>; + ) -> JoshutoResult; +} + +pub trait InteractiveExecute { + fn interactive_execute(&self, context: &mut AppContext); } pub trait AppCommand: AppExecute + std::fmt::Display + std::fmt::Debug { diff --git a/src/ui/views/tui_textfield.rs b/src/ui/views/tui_textfield.rs index 153a903..7c481e7 100644 --- a/src/ui/views/tui_textfield.rs +++ b/src/ui/views/tui_textfield.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use rustyline::completion::{Candidate, Completer, FilenameCompleter, Pair}; use rustyline::line_buffer; @@ -8,7 +10,7 @@ use unicode_width::UnicodeWidthStr; use crate::context::AppContext; use crate::event::AppEvent; -use crate::key_command::complete_command; +use crate::key_command::{complete_command, Command, InteractiveExecute}; use crate::ui::views::TuiView; use crate::ui::widgets::{TuiMenu, TuiMultilineText}; use crate::ui::TuiBackend; @@ -270,6 +272,10 @@ impl<'a> TuiTextField<'a> { if line_buffer.insert(c, 1).is_some() { completion_tracker.take(); } + + if let Ok(command) = Command::from_str(line_buffer.as_str()) { + command.interactive_execute(context) + } } _ => {} } |