[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().
This commit is contained in:
parent
8e2d795778
commit
c19337c8bf
4 changed files with 62 additions and 34 deletions
|
@ -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"
|
||||
|
|
|
@ -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<Reader<DefaultTerminal>>,
|
||||
interface: Option<Interface<DefaultTerminal>>,
|
||||
in_process_cmd: Option<Command>,
|
||||
}
|
||||
|
||||
|
@ -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<Interface<DefaultTerminal>>) -> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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<Repl, String> {
|
||||
pub fn new(tty: bool) -> Result<Repl, String> {
|
||||
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<Vec<Command>>) {
|
||||
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;
|
||||
|
|
Loading…
Reference in a new issue