From c19337c8bfa6bbc8fb791642fb425ea3c8460b42 Mon Sep 17 00:00:00 2001 From: Nick Alexander Date: Thu, 21 Jun 2018 16:26:50 -0700 Subject: [PATCH] [cli] Part 1: Bump linefeed; use linefeed::Interface; add "--no-tty" argument. I don't really understand why we were using `linefeed::Reader` directly, but reading is not the full set of linefeed features we want to access. I think the `linefeed::Interface` should be owned by the `Repl`, not the `InputReader`, but it's a little awkward to share access with that configuration, so I'm not going to lift the ownership until I have a reason to. I think the "--no-tty" argument might be useful for running inside Emacs. Along the way, I made read_stdin() strip the trailing newline, which agrees with InputReader::read_line(). --- tools/cli/Cargo.toml | 2 +- tools/cli/src/mentat_cli/input.rs | 55 +++++++++++++++++++------------ tools/cli/src/mentat_cli/lib.rs | 15 +++++---- tools/cli/src/mentat_cli/repl.rs | 24 ++++++++++---- 4 files changed, 62 insertions(+), 34 deletions(-) diff --git a/tools/cli/Cargo.toml b/tools/cli/Cargo.toml index 6a6eb088..dc377e13 100644 --- a/tools/cli/Cargo.toml +++ b/tools/cli/Cargo.toml @@ -24,7 +24,7 @@ failure = "0.1.1" failure_derive = "0.1.1" getopts = "0.2" lazy_static = "0.2" -linefeed = "0.4" +linefeed = "0.5" log = "0.4" tabwriter = "1" tempfile = "1.1" diff --git a/tools/cli/src/mentat_cli/input.rs b/tools/cli/src/mentat_cli/input.rs index 5956ef91..c190aaab 100644 --- a/tools/cli/src/mentat_cli/input.rs +++ b/tools/cli/src/mentat_cli/input.rs @@ -8,11 +8,15 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. -use std::io::stdin; +use std::io::{ + stdin, + stdout, + Write, +}; use linefeed::{ DefaultTerminal, - Reader, + Interface, ReadResult, Signal, }; @@ -52,7 +56,7 @@ pub enum InputResult { /// Reads input from `stdin` pub struct InputReader { buffer: String, - reader: Option>, + interface: Option>, in_process_cmd: Option, } @@ -71,27 +75,24 @@ enum UserAction { impl InputReader { /// Constructs a new `InputReader` reading from `stdin`. - pub fn new() -> InputReader { - let r = match Reader::new("mentat") { - Ok(mut r) => { - // Handle SIGINT (Ctrl-C) - r.set_report_signal(Signal::Interrupt, true); - r.set_word_break_chars(" \t\n!\"#$%&'(){}*+,-./:;<=>?@[\\]^`"); - Some(r) - }, - Err(_) => None, - }; + pub fn new(interface: Option>) -> InputReader { + if let Some(ref interface) = interface { + let mut r = interface.lock_reader(); + // Handle SIGINT (Ctrl-C) + r.set_report_signal(Signal::Interrupt, true); + r.set_word_break_chars(" \t\n!\"#$%&'(){}*+,-./:;<=>?@[\\]^`"); + } InputReader{ buffer: String::new(), - reader: r, + interface, in_process_cmd: None, } } /// Returns whether the `InputReader` is reading from a TTY. pub fn is_tty(&self) -> bool { - self.reader.is_some() + self.interface.is_some() } /// Reads a single command, item, or statement from `stdin`. @@ -179,7 +180,7 @@ impl InputReader { } fn read_line(&mut self, prompt: &str) -> UserAction { - match self.reader { + match self.interface { Some(ref mut r) => { r.set_prompt(prompt); r.read_line().ok().map_or(UserAction::Quit, |line| @@ -191,7 +192,13 @@ impl InputReader { }) }, - None => self.read_stdin() + None => { + print!("{}", prompt); + if stdout().flush().is_err() { + return UserAction::Quit; + } + self.read_stdin() + }, } } @@ -200,13 +207,19 @@ impl InputReader { match stdin().read_line(&mut s) { Ok(0) | Err(_) => UserAction::Quit, - Ok(_) => UserAction::TextInput(s) + Ok(_) => { + if s.ends_with("\n") { + let len = s.len() - 1; + s.truncate(len); + } + UserAction::TextInput(s) + }, } } - fn add_history(&mut self, line: String) { - if let Some(ref mut r) = self.reader { - r.add_history(line); + fn add_history(&self, line: String) { + if let Some(ref interface) = self.interface { + interface.add_history(line); } } } diff --git a/tools/cli/src/mentat_cli/lib.rs b/tools/cli/src/mentat_cli/lib.rs index bb637e19..b8df8caf 100644 --- a/tools/cli/src/mentat_cli/lib.rs +++ b/tools/cli/src/mentat_cli/lib.rs @@ -64,6 +64,7 @@ pub fn run() -> i32 { opts.optmulti("t", "transact", "Execute a transact on startup. Transacts are executed before queries.", "TRANSACT"); opts.optmulti("i", "import", "Execute an import on startup. Imports are executed before queries.", "PATH"); opts.optflag("v", "version", "Print version and exit"); + opts.optflag("", "no-tty", "Don't try to use a TTY for readline-like input processing"); let matches = match opts.parse(&args[1..]) { Ok(m) => m, @@ -121,13 +122,15 @@ pub fn run() -> i32 { } }).collect(); - let repl = repl::Repl::new(); - if repl.is_ok() { - repl.unwrap().run(Some(cmds)); + let mut repl = match repl::Repl::new(!matches.opt_present("no-tty")) { + Ok(repl) => repl, + Err(e) => { + println!("{}", e); + return 1 + } + }; - } else { - println!("{}", repl.err().unwrap()); - } + repl.run(Some(cmds)); 0 } diff --git a/tools/cli/src/mentat_cli/repl.rs b/tools/cli/src/mentat_cli/repl.rs index b4f66197..d68b3d7a 100644 --- a/tools/cli/src/mentat_cli/repl.rs +++ b/tools/cli/src/mentat_cli/repl.rs @@ -16,6 +16,10 @@ use failure::{ Error, }; +use linefeed::{ + Interface, +}; + use tabwriter::TabWriter; use termion::{ @@ -185,6 +189,7 @@ fn format_time(duration: Duration) { /// Executes input and maintains state of persistent items. pub struct Repl { + input_reader: InputReader, path: String, store: Store, timer_on: bool, @@ -200,19 +205,26 @@ impl Repl { } /// Constructs a new `Repl`. - pub fn new() -> Result { + pub fn new(tty: bool) -> Result { + let interface = if tty { + Some(Interface::new("mentat").map_err(|_| "failed to create tty interface; try --no-tty")?) + } else { + None + }; + + let input_reader = InputReader::new(interface); + let store = Store::open("").map_err(|e| e.to_string())?; Ok(Repl { + input_reader, path: "".to_string(), - store: store, + store, timer_on: false, }) } /// Runs the REPL interactively. pub fn run(&mut self, startup_commands: Option>) { - let mut input = InputReader::new(); - if let Some(cmds) = startup_commands { for command in cmds.iter() { println!("{}", command.output()); @@ -221,7 +233,7 @@ impl Repl { } loop { - let res = input.read_input(); + let res = self.input_reader.read_input(); match res { Ok(MetaCommand(cmd)) => { @@ -231,7 +243,7 @@ impl Repl { Ok(Empty) | Ok(More) => (), Ok(Eof) => { - if input.is_tty() { + if self.input_reader.is_tty() { println!(); } break;