use std::io::Write; use color_eyre::eyre::{ContextCompat, Result}; use console::{Key, Term, style}; use fuzzy_matcher::skim::SkimMatcherV2; use itertools::Itertools; pub fn input_text(term: &mut Term, default: &str, name: &str) -> Result { let mut text = default.to_string(); term.show_cursor()?; write!(term, "{}: {}", name, text)?; loop { term.clear_line()?; write!(term, "{}: {}", name, text)?; let key = match term.read_key() { Ok(key) => key, Err(err) => { term.show_cursor()?; return Err(err.into()); } }; match key { Key::Enter => { writeln!(term)?; term.hide_cursor()?; return Ok(text); } Key::Backspace => { text.pop(); } Key::Char(char) => { text += &char.to_string(); } _ => {} } } } pub fn input_selection(term: &mut Term, data: Vec<(String, T)>, name: &str) -> Result { let mut filter = "".to_string(); write!(term, "{}:", name)?; loop { let matcher = SkimMatcherV2::default(); let matches = data .iter() .filter_map(|(text, data)| { matcher .fuzzy(text, &filter, true) .map(|some| (data, text, some)) }) .sorted_by(|(_, _, fuzzy_a), (_, _, fuzzy_b)| fuzzy_a.0.cmp(&fuzzy_b.0).reverse()) .enumerate() .map(|(index, (path, path_str, fuzzy))| { ( highlight_search(path_str, &fuzzy.1, index != 0), path, path_str, ) }) .collect_vec(); term.clear_line()?; if matches.is_empty() { write!(term, "{}: {}", name, filter)?; } else { write!( term, "{}: {}", name, matches.iter().map(|(path, _, _)| path).join(" ") )?; } let key = match term.read_key() { Ok(key) => key, Err(err) => { term.show_cursor()?; return Err(err.into()); } }; match key { Key::Enter => { let (_, data, text) = matches.first().context(format!("no {} Selected", name))?; term.clear_line()?; writeln!(term, "{}: {}", name, text)?; break Ok((*data).clone()); } Key::Backspace => { filter.pop(); } Key::Char(char) => { filter += &char.to_string(); } _ => {} } } } fn highlight_search(text: &str, highlighted_letters: &[usize], is_dark: bool) -> String { text.chars() .enumerate() .map(|(index, c)| { match (is_dark, highlighted_letters.contains(&index)) { (false, true) => style(c).red().to_string(), (false, false) => c.to_string(), //dark red (true, true) => style(c).color256(8).bold().to_string(), //light gray (true, false) => style(c).color256(8).to_string(), } }) .collect() }