use crate::result::AppResult; use crate::config::get_matched_files; use termion::input::TermRead; use std::io::{Write, stdout, stdin, Stdout}; use termion::event::Key; use termion::raw::{IntoRawMode, RawTerminal}; use term_grid::{Grid, GridOptions, Filling, Direction, Cell}; use std::process::exit; use std::str::FromStr; pub fn get_matched_files_grid(pattern: String) -> AppResult { let mut grid = Grid::new(GridOptions { direction: Direction::LeftToRight, filling: Filling::Spaces(2), }); let filenames = get_matched_files(pattern).unwrap_or_else(|_| Vec::new()); for filename in filenames { grid.add(Cell::from(filename)) } Ok(format!("{}", grid.fit_into_columns(6))) } pub fn choose_pattern(current_pattern: String) -> AppResult { let mut stdout = stdout().into_raw_mode()?; let res = read_tty_line( &mut stdout, "How can we recognize files? Enter filename regex.", current_pattern, |stdout, pattern| { if !pattern.is_empty() { write!(stdout, "{}------Matched files------", termion::cursor::Goto(1, 3))?; let grid = get_matched_files_grid(pattern)?; if grid.is_empty() { write!(stdout, "{}No matches found", termion::cursor::Goto(1, 4) )?; } else { write!(stdout, "{}{}", termion::cursor::Goto(1, 4), grid )?; } } Ok(()) }, ); stdout.suspend_raw_mode()?; res } pub fn choose_command(current_command: String) -> AppResult { let mut stdout = stdout().into_raw_mode()?; let res = read_tty_line( &mut stdout, "Command to execute files.", current_command, |_, _| { Ok(()) }, ); stdout.suspend_raw_mode()?; res } pub fn choose_episode(current_episode: usize) -> AppResult { let mut stdout = stdout().into_raw_mode()?; let res = read_tty_line( &mut stdout, "Choose episode.", format!("{}", current_episode), |_, _| { Ok(()) }, ).map(|s| { usize::from_str(s.as_str()).unwrap_or_else(|_| current_episode) }); stdout.suspend_raw_mode()?; res } pub fn read_tty_line( stdout: &mut RawTerminal, prompt: &str, current_value: String, after_key_press: fn(&mut RawTerminal, String) -> AppResult<()>, ) -> AppResult { let stdin = stdin(); // Get the standard output stream and go to raw mode. write!(stdout, "{}{}{}{}", termion::clear::All, termion::cursor::Goto(1, 1), prompt, termion::cursor::Goto(1, 2) )?; // Flush stdout (i.e. make the output appear). stdout.flush()?; let mut buffer = current_value; let mut current_pos = buffer.len() + 1; if !buffer.is_empty() { write!(stdout, "{}{}{}", termion::cursor::Goto(1, 2), termion::clear::AfterCursor, buffer)?; after_key_press(stdout, buffer.clone())?; write!(stdout, "{}", termion::cursor::Goto(current_pos as u16, 2) )?; stdout.flush()?; } for c in stdin.keys() { match c? { // Exit if \n. Key::Char('\n') => { break; } // Update pattern Key::Char(c) => { buffer.insert(current_pos - 1, c.clone()); current_pos += 1; println!("{:#?}", c); } Key::Backspace => { if let Some(pos) = current_pos.checked_sub(2) { current_pos = pos + 1; buffer.remove(pos); } } Key::Delete => { if current_pos <= buffer.len() { buffer.remove(current_pos - 1); } } Key::Right => { if current_pos <= buffer.len() { current_pos += 1; } } Key::Left => { if let Some(pos) = current_pos.checked_sub(1) { current_pos = pos; } } Key::Ctrl('c') => { write!(stdout, "{}", termion::cursor::Show)?; stdout.suspend_raw_mode()?; exit(0); } Key::Ctrl('u') => { buffer.clear(); current_pos = 1; } _ => {} } // Clear the current line. write!(stdout, "{}{}{}", termion::cursor::Goto(1, 2), termion::clear::AfterCursor, buffer)?; // Print matched files after_key_press(stdout, buffer.clone())?; write!(stdout, "{}", termion::cursor::Goto(current_pos as u16, 2) )?; stdout.flush()?; } write!(stdout, "{}{}", termion::clear::All, termion::cursor::Goto(1, 1))?; stdout.flush()?; // Show the cursor again before we exit. Ok(buffer) }