feat(tui): Implement fuzzy searching

This commit is contained in:
Patrick Auernig 2024-11-28 00:33:38 +01:00
parent 38fe976261
commit 99e7d3895a

View File

@ -7,6 +7,7 @@ use ratatui::layout::{Constraint, Layout, Rect};
use ratatui::style::{Color, Style}; use ratatui::style::{Color, Style};
use ratatui::widgets::{Block, Borders, Cell, Paragraph, Row, Table, TableState}; use ratatui::widgets::{Block, Borders, Cell, Paragraph, Row, Table, TableState};
use ratatui::Frame; use ratatui::Frame;
use tracing::trace;
use tui_input::{Input, InputRequest}; use tui_input::{Input, InputRequest};
use crate::state::{clear_selected_project_file, write_selected_project_file}; use crate::state::{clear_selected_project_file, write_selected_project_file};
@ -61,6 +62,7 @@ impl State {
} }
#[tracing::instrument(skip_all)]
pub fn run(projects: Projects) -> Result<()> { pub fn run(projects: Projects) -> Result<()> {
clear_selected_project_file()?; clear_selected_project_file()?;
@ -115,7 +117,6 @@ fn handle_key_event(state: &mut State, tx: &mut mpsc::Sender<Message>, event: Ke
state.search.handle(InputRequest::DeletePrevChar); state.search.handle(InputRequest::DeletePrevChar);
Message::SearchUpdate Message::SearchUpdate
} }
(_, _, KeyCode::Enter) => Message::Confirm, (_, _, KeyCode::Enter) => Message::Confirm,
_ => Message::Noop, _ => Message::Noop,
@ -224,11 +225,13 @@ fn draw_list(state: &mut State, frame: &mut Frame, area: Rect) {
let search_value = state.search.value(); let search_value = state.search.value();
if path_str.contains(search_value) { if search_value.is_empty() {
Some(path_str.to_string()) return Some(path_str.to_string());
} else {
None
} }
let indices = fuzzy_search(search_value, path_str)?;
trace!(?search_value, ?path_str, ?indices);
Some(path_str.to_string())
}) })
.collect(); .collect();
@ -245,3 +248,49 @@ fn draw_list(state: &mut State, frame: &mut Frame, area: Rect) {
frame.render_stateful_widget(table, area, &mut state.project_table); frame.render_stateful_widget(table, area, &mut state.project_table);
} }
fn fuzzy_search(search: &str, text: &str) -> Option<Vec<usize>> {
let mut found_indices = Vec::new();
let mut start_idx = 0;
for ch in search.chars() {
let remaining = &text[start_idx..];
let mut found = false;
for (byte_idx, txt_ch) in remaining.char_indices() {
if ch == txt_ch {
found_indices.push(start_idx + byte_idx);
start_idx += byte_idx + txt_ch.len_utf8();
found = true;
break;
}
}
if !found {
return None;
}
}
if search.is_empty() {
None
} else {
Some(found_indices)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn fuzzy_test() {
assert_eq!(fuzzy_search("st", "st"), Some(vec![0, 1]));
assert_eq!(fuzzy_search("st", "some text"), Some(vec![0, 5]));
assert_eq!(fuzzy_search("st", "nothing"), None);
assert_eq!(fuzzy_search("st", ""), None);
assert_eq!(fuzzy_search("", "nonempty"), None);
}
}