Allow changing search_mode during interactive search (#586)
* Make search_mode a part of SearchState * Allow changing search mode using ctrl+s * Tweak state reset for switched_search_mode * Improve search_mode display in interactive mode * Incorporate review suggestion * Tweak language * Fix Clippy and format
This commit is contained in:
parent
bc06d5f36f
commit
378be6b790
2 changed files with 57 additions and 14 deletions
|
@ -34,6 +34,32 @@ pub enum SearchMode {
|
||||||
Skim,
|
Skim,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SearchMode {
|
||||||
|
pub fn as_str(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
SearchMode::Prefix => "PREFIX",
|
||||||
|
SearchMode::FullText => "FULLTXT",
|
||||||
|
SearchMode::Fuzzy => "FUZZY",
|
||||||
|
SearchMode::Skim => "SKIM",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn next(&self, settings: &Settings) -> Self {
|
||||||
|
match self {
|
||||||
|
SearchMode::Prefix => SearchMode::FullText,
|
||||||
|
SearchMode::FullText => {
|
||||||
|
// if the user is using skim, we go to skim, otherwise fuzzy.
|
||||||
|
if settings.search_mode == SearchMode::Skim {
|
||||||
|
SearchMode::Skim
|
||||||
|
} else {
|
||||||
|
SearchMode::Fuzzy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SearchMode::Fuzzy => SearchMode::Prefix,
|
||||||
|
SearchMode::Skim => SearchMode::Prefix,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Copy, PartialEq, Eq, ValueEnum)]
|
#[derive(Clone, Debug, Deserialize, Copy, PartialEq, Eq, ValueEnum)]
|
||||||
pub enum FilterMode {
|
pub enum FilterMode {
|
||||||
#[serde(rename = "global")]
|
#[serde(rename = "global")]
|
||||||
|
|
|
@ -54,15 +54,16 @@ struct State {
|
||||||
pub struct SearchState {
|
pub struct SearchState {
|
||||||
pub input: Cursor,
|
pub input: Cursor,
|
||||||
pub filter_mode: FilterMode,
|
pub filter_mode: FilterMode,
|
||||||
|
pub search_mode: SearchMode,
|
||||||
|
/// Store if the user has _just_ changed the search mode.
|
||||||
|
/// If so, we change the UI to show the search mode instead
|
||||||
|
/// of the filter mode until user starts typing again.
|
||||||
|
switched_search_mode: bool,
|
||||||
pub context: Context,
|
pub context: Context,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
async fn query_results(
|
async fn query_results(&mut self, db: &mut impl Database) -> Result<Vec<Arc<HistoryWrapper>>> {
|
||||||
&mut self,
|
|
||||||
search_mode: SearchMode,
|
|
||||||
db: &mut impl Database,
|
|
||||||
) -> Result<Vec<Arc<HistoryWrapper>>> {
|
|
||||||
let i = self.search.input.as_str();
|
let i = self.search.input.as_str();
|
||||||
let results = if i.is_empty() {
|
let results = if i.is_empty() {
|
||||||
db.list(
|
db.list(
|
||||||
|
@ -76,7 +77,7 @@ impl State {
|
||||||
.map(|history| HistoryWrapper { history, count: 1 })
|
.map(|history| HistoryWrapper { history, count: 1 })
|
||||||
.map(Arc::new)
|
.map(Arc::new)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
} else if search_mode == SearchMode::Skim {
|
} else if self.search.search_mode == SearchMode::Skim {
|
||||||
if self.all_history.is_empty() {
|
if self.all_history.is_empty() {
|
||||||
self.all_history = db
|
self.all_history = db
|
||||||
.all_with_count()
|
.all_with_count()
|
||||||
|
@ -91,7 +92,7 @@ impl State {
|
||||||
super::skim_impl::fuzzy_search(&self.search, &self.all_history).await
|
super::skim_impl::fuzzy_search(&self.search, &self.all_history).await
|
||||||
} else {
|
} else {
|
||||||
db.search(
|
db.search(
|
||||||
search_mode,
|
self.search.search_mode,
|
||||||
self.search.filter_mode,
|
self.search.filter_mode,
|
||||||
&self.search.context,
|
&self.search.context,
|
||||||
i,
|
i,
|
||||||
|
@ -154,6 +155,8 @@ impl State {
|
||||||
|
|
||||||
let ctrl = input.modifiers.contains(KeyModifiers::CONTROL);
|
let ctrl = input.modifiers.contains(KeyModifiers::CONTROL);
|
||||||
let alt = input.modifiers.contains(KeyModifiers::ALT);
|
let alt = input.modifiers.contains(KeyModifiers::ALT);
|
||||||
|
// reset the state, will be set to true later if user really did change it
|
||||||
|
self.search.switched_search_mode = false;
|
||||||
match input.code {
|
match input.code {
|
||||||
KeyCode::Char('c' | 'd' | 'g') if ctrl => return Some(RETURN_ORIGINAL),
|
KeyCode::Char('c' | 'd' | 'g') if ctrl => return Some(RETURN_ORIGINAL),
|
||||||
KeyCode::Esc => {
|
KeyCode::Esc => {
|
||||||
|
@ -226,6 +229,10 @@ impl State {
|
||||||
let i = (i + 1) % FILTER_MODES.len();
|
let i = (i + 1) % FILTER_MODES.len();
|
||||||
self.search.filter_mode = FILTER_MODES[i];
|
self.search.filter_mode = FILTER_MODES[i];
|
||||||
}
|
}
|
||||||
|
KeyCode::Char('s') if ctrl => {
|
||||||
|
self.search.switched_search_mode = true;
|
||||||
|
self.search.search_mode = self.search.search_mode.next(settings);
|
||||||
|
}
|
||||||
KeyCode::Down if self.results_state.selected() == 0 => return Some(RETURN_ORIGINAL),
|
KeyCode::Down if self.results_state.selected() == 0 => return Some(RETURN_ORIGINAL),
|
||||||
KeyCode::Down => {
|
KeyCode::Down => {
|
||||||
let i = self.results_state.selected().saturating_sub(1);
|
let i = self.results_state.selected().saturating_sub(1);
|
||||||
|
@ -395,11 +402,17 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_input(&mut self, compact: bool, chunk_width: usize) -> Paragraph {
|
fn build_input(&mut self, compact: bool, chunk_width: usize) -> Paragraph {
|
||||||
let input = format!(
|
/// Max width of the UI box showing current mode
|
||||||
"[{:^14}] {}",
|
const MAX_WIDTH: usize = 14;
|
||||||
self.search.filter_mode.as_str(),
|
let (pref, mode) = if self.search.switched_search_mode {
|
||||||
self.search.input.as_str(),
|
(" SRCH:", self.search.search_mode.as_str())
|
||||||
);
|
} else {
|
||||||
|
("", self.search.filter_mode.as_str())
|
||||||
|
};
|
||||||
|
let mode_width = MAX_WIDTH - pref.len();
|
||||||
|
// sanity check to ensure we don't exceed the layout limits
|
||||||
|
debug_assert!(mode_width >= mode.len(), "mode name '{mode}' is too long!");
|
||||||
|
let input = format!("[{pref}{mode:^mode_width$}] {}", self.search.input.as_str(),);
|
||||||
let input = if compact {
|
let input = if compact {
|
||||||
Paragraph::new(input)
|
Paragraph::new(input)
|
||||||
} else {
|
} else {
|
||||||
|
@ -543,11 +556,13 @@ pub async fn history(
|
||||||
} else {
|
} else {
|
||||||
settings.filter_mode
|
settings.filter_mode
|
||||||
},
|
},
|
||||||
|
search_mode: settings.search_mode,
|
||||||
|
switched_search_mode: false,
|
||||||
},
|
},
|
||||||
all_history: Vec::new(),
|
all_history: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut results = app.query_results(settings.search_mode, db).await?;
|
let mut results = app.query_results(db).await?;
|
||||||
|
|
||||||
let index = 'render: loop {
|
let index = 'render: loop {
|
||||||
let compact = match settings.style {
|
let compact = match settings.style {
|
||||||
|
@ -561,6 +576,7 @@ pub async fn history(
|
||||||
|
|
||||||
let initial_input = app.search.input.as_str().to_owned();
|
let initial_input = app.search.input.as_str().to_owned();
|
||||||
let initial_filter_mode = app.search.filter_mode;
|
let initial_filter_mode = app.search.filter_mode;
|
||||||
|
let initial_search_mode = app.search.search_mode;
|
||||||
|
|
||||||
let event_ready = tokio::task::spawn_blocking(|| event::poll(Duration::from_millis(250)));
|
let event_ready = tokio::task::spawn_blocking(|| event::poll(Duration::from_millis(250)));
|
||||||
|
|
||||||
|
@ -584,8 +600,9 @@ pub async fn history(
|
||||||
|
|
||||||
if initial_input != app.search.input.as_str()
|
if initial_input != app.search.input.as_str()
|
||||||
|| initial_filter_mode != app.search.filter_mode
|
|| initial_filter_mode != app.search.filter_mode
|
||||||
|
|| initial_search_mode != app.search.search_mode
|
||||||
{
|
{
|
||||||
results = app.query_results(settings.search_mode, db).await?;
|
results = app.query_results(db).await?;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if index < results.len() {
|
if index < results.len() {
|
||||||
|
|
Loading…
Reference in a new issue