Added tty interactive input.

Signed-off-by: Pavel Kirilin <win10@list.ru>
This commit is contained in:
2020-04-01 04:56:02 +04:00
parent 822fd0d43b
commit 02344c86f9
8 changed files with 254 additions and 70 deletions

168
src/tty_stuff.rs Normal file
View File

@ -0,0 +1,168 @@
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<String> {
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<String> {
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<String> {
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<usize> {
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<Stdout>,
prompt: &str,
current_value: String,
after_key_press: fn(&mut RawTerminal<Stdout>, String) -> AppResult<()>,
) -> AppResult<String> {
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)
}