From 99e7d3895a977451f63d9123a83d7bdfc3be4126 Mon Sep 17 00:00:00 2001 From: Patrick Auernig Date: Thu, 28 Nov 2024 00:33:38 +0100 Subject: [PATCH] feat(tui): Implement fuzzy searching --- src/tui.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/src/tui.rs b/src/tui.rs index 1077a7d..77baeca 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -7,6 +7,7 @@ use ratatui::layout::{Constraint, Layout, Rect}; use ratatui::style::{Color, Style}; use ratatui::widgets::{Block, Borders, Cell, Paragraph, Row, Table, TableState}; use ratatui::Frame; +use tracing::trace; use tui_input::{Input, InputRequest}; 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<()> { clear_selected_project_file()?; @@ -115,7 +117,6 @@ fn handle_key_event(state: &mut State, tx: &mut mpsc::Sender, event: Ke state.search.handle(InputRequest::DeletePrevChar); Message::SearchUpdate } - (_, _, KeyCode::Enter) => Message::Confirm, _ => Message::Noop, @@ -224,11 +225,13 @@ fn draw_list(state: &mut State, frame: &mut Frame, area: Rect) { let search_value = state.search.value(); - if path_str.contains(search_value) { - Some(path_str.to_string()) - } else { - None + if search_value.is_empty() { + return Some(path_str.to_string()); } + + let indices = fuzzy_search(search_value, path_str)?; + trace!(?search_value, ?path_str, ?indices); + Some(path_str.to_string()) }) .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); } + + +fn fuzzy_search(search: &str, text: &str) -> Option> { + 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); + } +}