Merge pull request #716 from rtfeldman/repl_arrows

Using arrows in REPL moves cursor
This commit is contained in:
Richard Feldman 2020-11-21 14:59:46 -05:00 committed by GitHub
commit 0ef6a2676d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 256 additions and 113 deletions

138
Cargo.lock generated
View File

@ -142,7 +142,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f813291114c186a042350e787af10c26534601062603d888be110f59f85ef8fa"
dependencies = [
"addr2line",
"cfg-if",
"cfg-if 0.1.10",
"libc",
"miniz_oxide",
"object",
@ -256,7 +256,7 @@ checksum = "7aa2097be53a00de9e8fc349fea6d76221f398f5c4fa550d420669906962d160"
dependencies = [
"mio",
"mio-extras",
"nix",
"nix 0.14.1",
]
[[package]]
@ -283,6 +283,12 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "2.33.3"
@ -468,7 +474,7 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"core-foundation-sys 0.7.0",
"core-graphics",
"libc",
@ -517,7 +523,7 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-epoch",
@ -553,7 +559,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
dependencies = [
"autocfg 1.0.1",
"cfg-if",
"cfg-if 0.1.10",
"crossbeam-utils",
"lazy_static",
"maybe-uninit",
@ -567,7 +573,7 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"crossbeam-utils",
"maybe-uninit",
]
@ -579,7 +585,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"autocfg 1.0.1",
"cfg-if",
"cfg-if 0.1.10",
"lazy_static",
]
@ -642,6 +648,27 @@ dependencies = [
"generic-array",
]
[[package]]
name = "dirs-next"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf36e65a80337bea855cd4ef9b8401ffce06a7baedf2e85ec467b1ac3f6e82b6"
dependencies = [
"cfg-if 1.0.0",
"dirs-sys-next",
]
[[package]]
name = "dirs-sys-next"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99de365f605554ae33f115102a02057d4fc18b01f3284d6870be0938743cfe7d"
dependencies = [
"libc",
"redox_users",
"winapi 0.3.9",
]
[[package]]
name = "dispatch"
version = "0.2.0"
@ -902,7 +929,7 @@ version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"libc",
"wasi",
]
@ -1342,7 +1369,7 @@ version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2443d8f0478b16759158b2f66d525991a05491138bc05814ef52a250148ef4f9"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"winapi 0.3.9",
]
@ -1398,7 +1425,7 @@ version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
]
[[package]]
@ -1424,9 +1451,9 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "memchr"
version = "2.3.3"
version = "2.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
[[package]]
name = "memmap"
@ -1477,7 +1504,7 @@ version = "0.6.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"fuchsia-zircon",
"fuchsia-zircon-sys",
"iovec",
@ -1598,7 +1625,7 @@ version = "0.2.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"libc",
"winapi 0.3.9",
]
@ -1611,11 +1638,23 @@ checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce"
dependencies = [
"bitflags",
"cc",
"cfg-if",
"cfg-if 0.1.10",
"libc",
"void",
]
[[package]]
name = "nix"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055"
dependencies = [
"bitflags",
"cc",
"cfg-if 0.1.10",
"libc",
]
[[package]]
name = "num-traits"
version = "0.2.12"
@ -1745,7 +1784,7 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"cloudabi 0.0.3",
"libc",
"redox_syscall",
@ -1760,7 +1799,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b"
dependencies = [
"backtrace",
"cfg-if",
"cfg-if 0.1.10",
"cloudabi 0.1.0",
"instant",
"libc",
@ -2236,6 +2275,16 @@ version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]]
name = "redox_users"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
dependencies = [
"getrandom",
"redox_syscall",
]
[[package]]
name = "regex"
version = "1.3.9"
@ -2382,6 +2431,8 @@ dependencies = [
"roc_types",
"roc_unify",
"roc_uniq",
"rustyline",
"rustyline-derive",
"serde",
"serde-xml-rs",
"serial_test",
@ -2789,6 +2840,35 @@ dependencies = [
"stb_truetype",
]
[[package]]
name = "rustyline"
version = "6.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0d5e7b0219a3eadd5439498525d4765c59b7c993ef0c12244865cd2d988413"
dependencies = [
"cfg-if 0.1.10",
"dirs-next",
"libc",
"log",
"memchr",
"nix 0.18.0",
"scopeguard",
"unicode-segmentation",
"unicode-width",
"utf8parse 0.2.0",
"winapi 0.3.9",
]
[[package]]
name = "rustyline-derive"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54a50e29610a5be68d4a586a5cce3bfb572ed2c2a74227e4168444b7bf4e5235"
dependencies = [
"quote 1.0.7",
"syn 1.0.40",
]
[[package]]
name = "ryu"
version = "1.0.5"
@ -2975,7 +3055,7 @@ dependencies = [
"dlib",
"lazy_static",
"memmap",
"nix",
"nix 0.14.1",
"wayland-client",
"wayland-protocols",
]
@ -2986,7 +3066,7 @@ version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"libc",
"redox_syscall",
"winapi 0.3.9",
@ -3103,7 +3183,7 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"libc",
"rand 0.7.3",
"redox_syscall",
@ -3220,7 +3300,7 @@ version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0987850db3733619253fe60e17cb59b82d37c7e6c0236bb81e4d6b87c879f27"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"pin-project-lite",
"tracing-core",
]
@ -3312,6 +3392,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d"
[[package]]
name = "utf8parse"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
[[package]]
name = "vec_map"
version = "0.8.2"
@ -3362,7 +3448,7 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f42f536e22f7fcbb407639765c8fd78707a33109301f834a594758bedd6e8cf"
dependencies = [
"utf8parse",
"utf8parse 0.1.1",
]
[[package]]
@ -3388,7 +3474,7 @@ version = "0.2.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"wasm-bindgen-macro",
]
@ -3413,7 +3499,7 @@ version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7866cab0aa01de1edf8b5d7936938a7e397ee50ce24119aef3e1eaa3b6171da"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"js-sys",
"wasm-bindgen",
"web-sys",
@ -3459,7 +3545,7 @@ dependencies = [
"downcast-rs",
"libc",
"mio",
"nix",
"nix 0.14.1",
"wayland-commons",
"wayland-scanner",
"wayland-sys",
@ -3471,7 +3557,7 @@ version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb66b0d1a27c39bbce712b6372131c6e25149f03ffb0cd017cf8f7de8d66dbdb"
dependencies = [
"nix",
"nix 0.14.1",
"wayland-sys",
]

View File

@ -53,6 +53,8 @@ roc_editor = { path = "../editor" }
# TODO switch to clap 3.0.0 once it's out. Tried adding clap = "~3.0.0-beta.1" and cargo wouldn't accept it
clap = { git = "https://github.com/rtfeldman/clap", branch = "master" }
const_format = "0.2.8"
rustyline = "6.3.0"
rustyline-derive = "0.3.1"
im = "14" # im and im-rc should always have the same version!
im-rc = "14" # im and im-rc should always have the same version!
bumpalo = { version = "3.2", features = ["collections"] }

View File

@ -2,7 +2,11 @@ use const_format::concatcp;
use gen::{gen_and_eval, ReplOutput};
use roc_gen::llvm::build::OptLevel;
use roc_parse::parser::{Fail, FailReason};
use std::io::{self, Write};
use rustyline::error::ReadlineError;
use rustyline::validate::{self, ValidationContext, ValidationResult, Validator};
use rustyline::Editor;
use rustyline_derive::{Completer, Helper, Highlighter, Hinter};
use std::io::{self};
use target_lexicon::Triple;
const BLUE: &str = "\u{001b}[36m";
@ -22,107 +26,158 @@ pub const WELCOME_MESSAGE: &str = concatcp!(
);
pub const INSTRUCTIONS: &str = "Enter an expression, or :help, or :exit/:q.\n";
pub const PROMPT: &str = concatcp!("\n", BLUE, "»", END_COL, " ");
const ELLIPSIS: &str = concatcp!(BLUE, "", END_COL, " ");
mod eval;
mod gen;
pub fn main() -> io::Result<()> {
use std::io::BufRead;
#[derive(Completer, Helper, Hinter, Highlighter)]
struct ReplHelper {
validator: InputValidator,
pending_src: String,
}
impl ReplHelper {
pub(crate) fn new() -> ReplHelper {
ReplHelper {
validator: InputValidator::new(),
pending_src: String::new(),
}
}
}
impl Validator for ReplHelper {
fn validate(
&self,
ctx: &mut validate::ValidationContext,
) -> rustyline::Result<validate::ValidationResult> {
self.validator.validate(ctx)
}
fn validate_while_typing(&self) -> bool {
self.validator.validate_while_typing()
}
}
struct InputValidator {}
impl InputValidator {
pub(crate) fn new() -> InputValidator {
InputValidator {}
}
}
impl Validator for InputValidator {
fn validate(&self, ctx: &mut ValidationContext) -> rustyline::Result<ValidationResult> {
if ctx.input().is_empty() {
Ok(ValidationResult::Incomplete)
} else {
Ok(ValidationResult::Valid(None))
}
}
}
pub fn main() -> io::Result<()> {
// To debug rustyline:
// <UNCOMMENT> env_logger::init();
// <RUN WITH:> RUST_LOG=rustyline=debug cargo run repl 2> debug.log
print!("{}{}", WELCOME_MESSAGE, INSTRUCTIONS);
// Loop
let mut pending_src = String::new();
let mut prev_line_blank = false;
let mut editor = Editor::<ReplHelper>::new();
let repl_helper = ReplHelper::new();
editor.set_helper(Some(repl_helper));
loop {
if pending_src.is_empty() {
print!("{}", PROMPT);
} else {
print!("{}", ELLIPSIS);
}
let readline = editor.readline(PROMPT);
let pending_src = &mut editor
.helper_mut()
.expect("Editor helper was not set")
.pending_src;
io::stdout().flush().unwrap();
let stdin = io::stdin();
let line = stdin
.lock()
.lines()
.next()
.expect("there was no next line")
.expect("the line could not be read");
let line = line.trim();
match line.to_lowercase().as_str() {
":help" => {
println!("Use :exit or :q to exit.");
}
"" => {
if pending_src.is_empty() {
print!("\n{}", INSTRUCTIONS);
} else if prev_line_blank {
// After two blank lines in a row, give up and try parsing it
// even though it's going to fail. This way you don't get stuck.
match eval_and_format(pending_src.as_str()) {
Ok(output) => {
println!("{}", output);
}
Err(fail) => {
report_parse_error(fail);
}
}
pending_src.clear();
} else {
pending_src.push('\n');
prev_line_blank = true;
continue; // Skip the part where we reset prev_line_blank to false
}
}
":exit" => {
break;
}
":q" => {
break;
}
_ => {
let result = if pending_src.is_empty() {
eval_and_format(line)
} else {
pending_src.push('\n');
pending_src.push_str(line);
eval_and_format(pending_src.as_str())
};
match result {
Ok(output) => {
println!("{}", output);
pending_src.clear();
}
Err(Fail {
reason: FailReason::Eof(_),
..
}) => {
// If we hit an eof, and we're allowed to keep going,
// append the str to the src we're building up and continue.
// (We only need to append it here if it was empty before;
// otherwise, we already appended it before calling eval_and_format.)
match readline {
Ok(line) => {
//TODO rl.add_history_entry(line.as_str());
let trim_line = line.trim();
match trim_line.to_lowercase().as_str() {
"" => {
if pending_src.is_empty() {
pending_src.push_str(line);
print!("\n{}", INSTRUCTIONS);
} else if prev_line_blank {
// After two blank lines in a row, give up and try parsing it
// even though it's going to fail. This way you don't get stuck.
match eval_and_format(pending_src.as_str()) {
Ok(output) => {
println!("{}", output);
}
Err(fail) => {
report_parse_error(fail);
}
}
pending_src.clear();
} else {
pending_src.push('\n');
prev_line_blank = true;
continue; // Skip the part where we reset prev_line_blank to false
}
}
Err(fail) => {
report_parse_error(fail);
pending_src.clear();
":help" => {
println!("Use :exit or :q to exit.");
}
":exit" => {
break;
}
":q" => {
break;
}
_ => {
let result = if pending_src.is_empty() {
eval_and_format(trim_line)
} else {
pending_src.push('\n');
pending_src.push_str(trim_line);
eval_and_format(pending_src.as_str())
};
match result {
Ok(output) => {
println!("{}", output);
pending_src.clear();
}
Err(Fail {
reason: FailReason::Eof(_),
..
}) => {}
Err(fail) => {
report_parse_error(fail);
pending_src.clear();
}
}
}
}
}
Err(ReadlineError::Interrupted) => {
println!("CTRL-C");
break;
}
Err(ReadlineError::Eof) => {
// If we hit an eof, and we're allowed to keep going,
// append the str to the src we're building up and continue.
// (We only need to append it here if it was empty before;
// otherwise, we already appended it before calling eval_and_format.)
if pending_src.is_empty() {
pending_src.push_str("");
}
break;
}
Err(err) => {
println!("Error: {:?}", err);
break;
}
}
prev_line_blank = false;

View File

@ -5,7 +5,7 @@ extern crate roc_load;
extern crate roc_module;
extern crate tempfile;
use roc_cli::repl::{INSTRUCTIONS, PROMPT, WELCOME_MESSAGE};
use roc_cli::repl::{INSTRUCTIONS, WELCOME_MESSAGE};
use serde::Deserialize;
use serde_xml_rs::from_str;
use std::env;
@ -278,7 +278,7 @@ pub fn repl_eval(input: &str) -> Out {
// Remove the initial instructions from the output.
let expected_instructions = format!("{}{}{}", WELCOME_MESSAGE, INSTRUCTIONS, PROMPT);
let expected_instructions = format!("{}{}", WELCOME_MESSAGE, INSTRUCTIONS);
let stdout = String::from_utf8(output.stdout).unwrap();
assert!(
@ -300,7 +300,7 @@ pub fn repl_eval(input: &str) -> Out {
panic!("repl exited unexpectedly before finishing evaluation. Exit status was {:?} and stderr was {:?}", output.status, String::from_utf8(output.stderr).unwrap());
}
} else {
let expected_after_answer = format!("\n{}", PROMPT);
let expected_after_answer = format!("\n");
assert!(
answer.ends_with(&expected_after_answer),