diff --git a/src/cli.rs b/src/cli.rs index d7d873bd15..57afc34e6b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -9,7 +9,6 @@ use crate::context::Context; use crate::data::config; use crate::data::Value; pub(crate) use crate::errors::ShellError; -use crate::fuzzysearch::{interactive_fuzzy_search, SelectionResult}; #[cfg(not(feature = "starship-prompt"))] use crate::git::current_branch; use crate::parser::registry::Signature; @@ -388,14 +387,6 @@ pub async fn cli() -> Result<(), Box> { rl.set_edit_mode(edit_mode); - // Register Ctrl-r for history fuzzy search - // rustyline doesn't support custom commands, so we override Ctrl-D (EOF) - // https://github.com/nushell/nushell/issues/689 - #[cfg(all(not(windows), feature = "crossterm"))] - rl.bind_sequence(rustyline::KeyPress::Ctrl('R'), rustyline::Cmd::EndOfFile); - // Redefine Ctrl-D to same command as Ctrl-C - rl.bind_sequence(rustyline::KeyPress::Ctrl('D'), rustyline::Cmd::Interrupt); - let colored_prompt = { #[cfg(feature = "starship-prompt")] { @@ -428,27 +419,7 @@ pub async fn cli() -> Result<(), Box> { let mut readline = Err(ReadlineError::Eof); while let Some(ref cmd) = initial_command { readline = rl.readline_with_initial(&prompt, (&cmd, "")); - if let Err(ReadlineError::Eof) = &readline { - // Fuzzy search in history - let lines = rl.history().iter().rev().map(|s| s.as_str()).collect(); - let selection = interactive_fuzzy_search(&lines, 5); // Clears last line with prompt - match selection { - SelectionResult::Selected(line) => { - println!("{}{}", &prompt, &line); // TODO: colorize prompt - readline = Ok(line.clone()); - initial_command = None; - } - SelectionResult::Edit(line) => { - initial_command = Some(line); - } - SelectionResult::NoSelection => { - readline = Ok("".to_string()); - initial_command = None; - } - } - } else { - initial_command = None; - } + initial_command = None; } match process_line(readline, &mut context).await { diff --git a/src/fuzzysearch.rs b/src/fuzzysearch.rs deleted file mode 100644 index c7d58ed632..0000000000 --- a/src/fuzzysearch.rs +++ /dev/null @@ -1,187 +0,0 @@ -use ansi_term::{ANSIString, ANSIStrings, Colour, Style}; -#[cfg(feature = "crossterm")] -use crossterm::{cursor, terminal, ClearType, InputEvent, KeyEvent, RawScreen}; -use std::io::Write; -use sublime_fuzzy::best_match; - -pub enum SelectionResult { - Selected(String), - Edit(String), - NoSelection, -} - -pub fn interactive_fuzzy_search(lines: &Vec<&str>, max_results: usize) -> SelectionResult { - #[derive(PartialEq)] - enum State { - Selecting, - Quit, - Selected(String), - Edit(String), - } - let mut state = State::Selecting; - #[cfg(feature = "crossterm")] - { - if let Ok(_raw) = RawScreen::into_raw_mode() { - // User input for search - let mut searchinput = String::new(); - let mut selected = 0; - - let mut cursor = cursor(); - let _ = cursor.hide(); - let input = crossterm::input(); - let mut sync_stdin = input.read_sync(); - - while state == State::Selecting { - let mut selected_lines = fuzzy_search(&searchinput, &lines, max_results); - let num_lines = selected_lines.len(); - paint_selection_list(&selected_lines, selected); - if let Some(ev) = sync_stdin.next() { - match ev { - InputEvent::Keyboard(k) => match k { - KeyEvent::Esc | KeyEvent::Ctrl('c') => { - state = State::Quit; - } - KeyEvent::Up => { - if selected > 0 { - selected -= 1; - } - } - KeyEvent::Down => { - if selected + 1 < selected_lines.len() { - selected += 1; - } - } - KeyEvent::Char('\n') => { - state = if selected_lines.len() > 0 { - State::Selected(selected_lines.remove(selected).text) - } else { - State::Edit("".to_string()) - }; - } - KeyEvent::Char('\t') | KeyEvent::Right => { - state = if selected_lines.len() > 0 { - State::Edit(selected_lines.remove(selected).text) - } else { - State::Edit("".to_string()) - }; - } - KeyEvent::Char(ch) => { - searchinput.push(ch); - selected = 0; - } - KeyEvent::Backspace => { - searchinput.pop(); - selected = 0; - } - _ => {} - }, - _ => {} - } - } - if num_lines > 0 { - cursor.move_up(num_lines as u16); - } - } - let (_x, y) = cursor.pos(); - let _ = cursor.goto(0, y - 1); - let _ = cursor.show(); - let _ = RawScreen::disable_raw_mode(); - } - terminal().clear(ClearType::FromCursorDown).unwrap(); - } - match state { - State::Selected(line) => SelectionResult::Selected(line), - State::Edit(line) => SelectionResult::Edit(line), - _ => SelectionResult::NoSelection, - } -} - -pub struct Match { - text: String, - char_matches: Vec<(usize, usize)>, -} - -pub fn fuzzy_search(searchstr: &str, lines: &Vec<&str>, max_results: usize) -> Vec { - if searchstr.is_empty() { - return lines - .iter() - .take(max_results) - .map(|line| Match { - text: line.to_string(), - char_matches: Vec::new(), - }) - .collect(); - } - - let mut matches = lines - .iter() - .enumerate() - .map(|(idx, line)| (idx, best_match(&searchstr, line))) - .filter(|(_i, m)| m.is_some()) - .map(|(i, m)| (i, m.unwrap())) - .collect::>(); - matches.sort_by(|a, b| b.1.score().cmp(&a.1.score())); - - let results: Vec = matches - .iter() - .take(max_results) - .map(|(i, m)| Match { - text: lines[*i].to_string(), - char_matches: m.continuous_matches(), - }) - .collect(); - results -} - -#[cfg(feature = "crossterm")] -fn highlight(textmatch: &Match, normal: Style, highlighted: Style) -> Vec { - let text = &textmatch.text; - let mut ansi_strings = vec![]; - let mut idx = 0; - for (match_idx, len) in &textmatch.char_matches { - ansi_strings.push(normal.paint(&text[idx..*match_idx])); - idx = match_idx + len; - ansi_strings.push(highlighted.paint(&text[*match_idx..idx])); - } - if idx < text.len() { - ansi_strings.push(normal.paint(&text[idx..text.len()])); - } - ansi_strings -} - -#[cfg(feature = "crossterm")] -fn paint_selection_list(lines: &Vec, selected: usize) { - let terminal = terminal(); - let size = terminal.terminal_size(); - let width = size.0 as usize; - let cursor = cursor(); - let (_x, y) = cursor.pos(); - for (i, line) in lines.iter().enumerate() { - let _ = cursor.goto(0, y + (i as u16)); - let (style, highlighted) = if selected == i { - (Colour::White.normal(), Colour::Cyan.normal()) - } else { - (Colour::White.dimmed(), Colour::Cyan.normal()) - }; - let mut ansi_strings = highlight(line, style, highlighted); - for _ in line.text.len()..width { - ansi_strings.push(style.paint(' '.to_string())); - } - println!("{}", ANSIStrings(&ansi_strings)); - } - let _ = cursor.goto(0, y + (lines.len() as u16)); - print!( - "{}", - Colour::Blue.paint("[ESC to quit, Enter to execute, Tab to edit]") - ); - - let _ = std::io::stdout().flush(); - // Clear additional lines from previous selection - terminal.clear(ClearType::FromCursorDown).unwrap(); -} - -#[test] -fn fuzzy_match() { - let matches = fuzzy_search("cb", &vec!["abc", "cargo build"], 1); - assert_eq!(matches[0].text, "cargo build"); -} diff --git a/src/lib.rs b/src/lib.rs index d6dcee0f22..f0b8102203 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,6 @@ mod env; mod errors; mod evaluate; mod format; -mod fuzzysearch; mod git; mod parser; mod plugin;