Added tty interactive input.
Signed-off-by: Pavel Kirilin <win10@list.ru>
This commit is contained in:
168
src/tty_stuff.rs
Normal file
168
src/tty_stuff.rs
Normal 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)
|
||||
}
|
Reference in New Issue
Block a user