From b6709ebadff904ddefec07625d198c8fa33a4555 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Tue, 9 Jul 2019 01:01:55 -0400 Subject: split into a repl and a tui the repl uses readline and is line-based, the tui will eventually be a full alternate screen app --- src/bin/repl.rs | 5 +++ src/bin/tui.rs | 5 +++ src/lib.rs | 22 ++++++++++++ src/main.rs | 24 -------------- src/repl.rs | 101 +++++++++++++++++++++++++------------------------------- src/tui.rs | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 169 insertions(+), 80 deletions(-) create mode 100644 src/bin/repl.rs create mode 100644 src/bin/tui.rs create mode 100644 src/lib.rs delete mode 100644 src/main.rs create mode 100644 src/tui.rs diff --git a/src/bin/repl.rs b/src/bin/repl.rs new file mode 100644 index 0000000..ed483ec --- /dev/null +++ b/src/bin/repl.rs @@ -0,0 +1,5 @@ +extern crate nbsh; + +fn main() { + nbsh::repl::repl(); +} diff --git a/src/bin/tui.rs b/src/bin/tui.rs new file mode 100644 index 0000000..68be8a8 --- /dev/null +++ b/src/bin/tui.rs @@ -0,0 +1,5 @@ +extern crate nbsh; + +fn main() { + nbsh::tui::tui(); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..ebe3785 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,22 @@ +#![warn(clippy::pedantic)] +#![warn(clippy::nursery)] +// clippy::cargo seems to be broken with rls currently +// #![warn(clippy::cargo)] +#![allow(clippy::collapsible_if)] +#![allow(clippy::if_not_else)] +// match_same_arms is buggy, doesn't notice differences due to match arm order +#![allow(clippy::match_same_arms)] +#![allow(clippy::module_name_repetitions)] +#![allow(clippy::multiple_crate_versions)] +#![allow(clippy::single_match)] +#![allow(clippy::write_with_newline)] + +mod builtins; +mod eval; +mod parser; +mod process; +mod readline; +mod state; + +pub mod repl; +pub mod tui; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index cd3dc54..0000000 --- a/src/main.rs +++ /dev/null @@ -1,24 +0,0 @@ -#![warn(clippy::pedantic)] -#![warn(clippy::nursery)] -// clippy::cargo seems to be broken with rls currently -// #![warn(clippy::cargo)] -#![allow(clippy::collapsible_if)] -#![allow(clippy::if_not_else)] -// match_same_arms is buggy, doesn't notice differences due to match arm order -#![allow(clippy::match_same_arms)] -#![allow(clippy::module_name_repetitions)] -#![allow(clippy::multiple_crate_versions)] -#![allow(clippy::single_match)] -#![allow(clippy::write_with_newline)] - -mod builtins; -mod eval; -mod parser; -mod process; -mod readline; -mod repl; -mod state; - -fn main() { - repl::repl(); -} diff --git a/src/repl.rs b/src/repl.rs index 874a22e..aa80132 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,5 +1,6 @@ use futures::future::{Future as _, IntoFuture as _}; use futures::stream::Stream as _; +use snafu::ResultExt as _; use std::io::Write as _; #[derive(Debug, snafu::Snafu)] @@ -11,50 +12,41 @@ pub enum Error { Eval { source: crate::eval::Error }, #[snafu(display("error during print: {}", source))] - Print { source: crate::state::Error }, + Print { source: std::io::Error }, } -pub fn repl() { - tokio::run(futures::lazy(|| { - let (w, r) = futures::sync::mpsc::channel(0); - - tokio::spawn(crate::state::State::new(r).map_err(|e| { - error(&Error::Print { source: e }); - })); +pub type Result = std::result::Result; - futures::future::loop_fn(0, move |idx| { - let w = w.clone(); - read() - .and_then(move |line| { - let w = w.clone(); - eval(&line).for_each(move |event| { - let w = w.clone(); - print(w, idx, &event) - }) - }) - .then(move |res| match res { - // successful run or empty input means prompt again - Ok(_) - | Err(Error::Eval { - source: - crate::eval::Error::Parser { - source: crate::parser::Error::CommandRequired, - .. - }, - }) => Ok(futures::future::Loop::Continue(idx + 1)), - // eof means we're done - Err(Error::Read { - source: crate::readline::Error::EOF, - }) => Ok(futures::future::Loop::Break(())), - // any other errors should be displayed, then we - // prompt again - Err(e) => { - error(&e); - Ok(futures::future::Loop::Continue(idx + 1)) - } - }) - }) - })); +pub fn repl() { + tokio::run(futures::future::loop_fn((), |_| { + read() + .and_then(|line| eval(&line).for_each(|event| print(&event))) + .then(|res| match res { + // successful run or empty input means prompt again + Ok(_) + | Err(Error::Eval { + source: + crate::eval::Error::Parser { + source: crate::parser::Error::CommandRequired, + .. + }, + }) => Ok(futures::future::Loop::Continue(())), + // eof means we're done + Err(Error::Read { + source: crate::readline::Error::EOF, + }) => Ok(futures::future::Loop::Break(())), + // any other errors should be displayed, then we + // prompt again + Err(e) => { + let stderr = std::io::stderr(); + let mut stderr = stderr.lock(); + // panics seem fine for errors during error handling + write!(stderr, "{}\r\n", e).unwrap(); + stderr.flush().unwrap(); + Ok(futures::future::Loop::Continue(())) + } + }) + })) } fn read() -> impl futures::future::Future { @@ -74,19 +66,16 @@ fn eval( .map_err(|e| Error::Eval { source: e }) } -fn print( - w: futures::sync::mpsc::Sender, - idx: usize, - event: &crate::eval::CommandEvent, -) -> impl futures::future::Future { - crate::state::update(w, idx, event) - .map_err(|e| Error::Print { source: e }) -} - -fn error(e: &Error) { - let stderr = std::io::stderr(); - let mut stderr = stderr.lock(); - // panics seem fine for errors during error handling - write!(stderr, "{}\r\n", e).unwrap(); - stderr.flush().unwrap(); +fn print(event: &crate::eval::CommandEvent) -> Result<()> { + match event { + crate::eval::CommandEvent::CommandStart(_, _) => {} + crate::eval::CommandEvent::Output(out) => { + let stdout = std::io::stdout(); + let mut stdout = stdout.lock(); + stdout.write(out).context(Print)?; + stdout.flush().context(Print)?; + } + crate::eval::CommandEvent::CommandExit(_) => {} + } + Ok(()) } diff --git a/src/tui.rs b/src/tui.rs new file mode 100644 index 0000000..8d84a7f --- /dev/null +++ b/src/tui.rs @@ -0,0 +1,92 @@ +use futures::future::{Future as _, IntoFuture as _}; +use futures::stream::Stream as _; +use std::io::Write as _; + +#[derive(Debug, snafu::Snafu)] +pub enum Error { + #[snafu(display("error during read: {}", source))] + Read { source: crate::readline::Error }, + + #[snafu(display("error during eval: {}", source))] + Eval { source: crate::eval::Error }, + + #[snafu(display("error during print: {}", source))] + Print { source: crate::state::Error }, +} + +pub fn tui() { + tokio::run(futures::lazy(|| { + let (w, r) = futures::sync::mpsc::channel(0); + + tokio::spawn(crate::state::State::new(r).map_err(|e| { + error(&Error::Print { source: e }); + })); + + futures::future::loop_fn(0, move |idx| { + let w = w.clone(); + read() + .and_then(move |line| { + let w = w.clone(); + eval(&line).for_each(move |event| { + let w = w.clone(); + print(w, idx, &event) + }) + }) + .then(move |res| match res { + // successful run or empty input means prompt again + Ok(_) + | Err(Error::Eval { + source: + crate::eval::Error::Parser { + source: crate::parser::Error::CommandRequired, + .. + }, + }) => Ok(futures::future::Loop::Continue(idx + 1)), + // eof means we're done + Err(Error::Read { + source: crate::readline::Error::EOF, + }) => Ok(futures::future::Loop::Break(())), + // any other errors should be displayed, then we + // prompt again + Err(e) => { + error(&e); + Ok(futures::future::Loop::Continue(idx + 1)) + } + }) + }) + })); +} + +fn read() -> impl futures::future::Future { + crate::readline::readline("$ ", true) + .into_future() + .flatten() + .map_err(|e| Error::Read { source: e }) +} + +fn eval( + line: &str, +) -> impl futures::stream::Stream +{ + crate::eval::eval(line) + .into_future() + .flatten_stream() + .map_err(|e| Error::Eval { source: e }) +} + +fn print( + w: futures::sync::mpsc::Sender, + idx: usize, + event: &crate::eval::CommandEvent, +) -> impl futures::future::Future { + crate::state::update(w, idx, event) + .map_err(|e| Error::Print { source: e }) +} + +fn error(e: &Error) { + let stderr = std::io::stderr(); + let mut stderr = stderr.lock(); + // panics seem fine for errors during error handling + write!(stderr, "{}\r\n", e).unwrap(); + stderr.flush().unwrap(); +} -- cgit v1.2.3-54-g00ecf