use std::io::{self, Write};
use std::time::Instant;
use super::cmd::run_cmd_line;
use tty::{Key, RawTTy};
pub mod colors;
pub mod history;
pub mod tty;
mod prompt;
pub fn run() {
let repl = Repl::new();
repl.main_loop();
tty::reset_terminal_mode();
}
impl Repl {
fn main_loop(mut self) {
let prompt = prompt::Prompt::new();
let mut stdout = io::stdout().lock();
let mut elapsed: u64 = 0;
loop {
prompt.show(&self.state.pwd, elapsed);
loop {
match self.tty.read_one_key() {
Key::Enter => {
let start = Instant::now();
self.run_cmd();
elapsed = start.elapsed().as_secs();
break;
}
Key::Tab => self.handle_completion(),
Key::Up => {
let line = self.state.hist.get_previous();
self.display_line(line);
}
Key::Down => {
let line = self.state.hist.get_next();
self.display_line(line);
}
Key::Right => self.move_right(),
Key::Left => self.move_left(),
Key::Ctrl_C => return,
Key::Ctrl_Z => return,
Key::Backspace => {
if self.position_on_line > 0 {
self.delete_one_char()
}
}
Key::Char(c) => {
self.add_one_char(c);
}
k => unimplemented!("{:?} not handled", k),
}
stdout.flush().expect("unable to flush stdout");
}
}
}
fn run_cmd(&mut self) {
eprintln!("");
if self.buffer.len() > 0 {
let line = &self.buffer.to_owned();
let line = std::str::from_utf8(line).unwrap();
if let Err(err) = self._run_cmd(line) {
eprintln!("io error: {}", err);
} else {
self.state.hist.add(line.to_owned());
}
}
self.buffer.clear();
self.position_on_line = 0;
}
fn _run_cmd(&mut self, line: &str) -> Result<(), std::io::Error> {
let args: Vec<&str> = line.split(' ').filter(|s| !s.is_empty()).collect();
if let Some(func) = self.state.builtins.get(args[0]) {
func(args, &mut self.state)?;
} else {
let output = run_cmd_line(&line)?;
let stdout = std::str::from_utf8(&output.stdout).unwrap();
let stderr = std::str::from_utf8(&output.stderr).unwrap();
print!("{}{}", stderr, stdout);
}
Ok(())
}
fn display_line(&mut self, line: String) {
self.clear_line();
for c in line.bytes() {
self.add_one_char(c);
}
}
fn clear_line(&mut self) {
loop {
if self.position_on_line < 1 {
break;
}
self.delete_one_char();
}
}
fn handle_completion(&mut self) {
eprintln!("todo! handle autocompletion")
}
fn delete_one_char(&mut self) {
print!("\x1b[1D \x1b[1D");
self.position_on_line -= 1;
self.buffer.pop();
}
fn add_one_char(&mut self, c: u8) {
self.buffer.push(c);
self.position_on_line += 1;
print!("{}", c as char);
}
fn new() -> Self {
let position_on_line = 0;
let buffer: Vec<u8> = Vec::new();
let state = super::ShellState::new();
let raw_tty = tty::RawTTy::new();
Self {
state,
position_on_line,
tty: raw_tty,
buffer,
}
}
fn move_left(&mut self) {
if self.position_on_line > 0 {
self.position_on_line -= 1;
eprint!("\x1B[1D");
}
}
fn move_right(&mut self) {
if self.position_on_line + 1 < self.buffer.len() {
eprint!("\x1B[1C");
self.position_on_line += 1;
}
}
}
struct Repl {
state: super::ShellState,
position_on_line: usize,
tty: RawTTy<'static>,
buffer: Vec<u8>, }