mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-24 12:32:29 +00:00
Merge branch 'repl' into repl-warnings
Signed-off-by: Richard Feldman <oss@rtfeldman.com>
This commit is contained in:
commit
0657c38bb4
5 changed files with 53 additions and 115 deletions
|
@ -23,7 +23,6 @@ rustyline = {git = "https://github.com/roc-lang/rustyline", rev = "e74333c"}
|
|||
rustyline-derive = {git = "https://github.com/roc-lang/rustyline", rev = "e74333c"}
|
||||
target-lexicon = "0.12.2"
|
||||
unicode-segmentation = "1.10.0"
|
||||
termsize = "0.1.6"
|
||||
|
||||
roc_build = {path = "../compiler/build"}
|
||||
roc_builtins = {path = "../compiler/builtins"}
|
||||
|
|
|
@ -22,7 +22,7 @@ pub const WELCOME_MESSAGE: &str = concatcp!(
|
|||
|
||||
// For when nothing is entered in the repl
|
||||
// TODO add link to repl tutorial(does not yet exist).
|
||||
pub const SHORT_INSTRUCTIONS: &str = "Enter an expression, or :help, or :q to quit.\n";
|
||||
pub const SHORT_INSTRUCTIONS: &str = "Enter an expression, or :help, or :q to quit.\n\n";
|
||||
|
||||
pub fn main() -> i32 {
|
||||
use rustyline::error::ReadlineError;
|
||||
|
@ -42,9 +42,10 @@ pub fn main() -> i32 {
|
|||
Ok(line) => {
|
||||
editor.add_history_entry(line.trim());
|
||||
|
||||
let dimensions = editor.dimensions();
|
||||
let repl_helper = editor.helper_mut().expect("Editor helper was not set");
|
||||
|
||||
match repl_helper.step(&line) {
|
||||
match repl_helper.step(&line, dimensions) {
|
||||
Ok(output) => {
|
||||
// If there was no output, don't print a blank line!
|
||||
// (This happens for something like a type annotation.)
|
||||
|
|
|
@ -17,9 +17,8 @@ use rustyline::validate::{self, ValidationContext, ValidationResult, Validator};
|
|||
use rustyline_derive::{Completer, Helper, Hinter};
|
||||
use std::borrow::Cow;
|
||||
use target_lexicon::Triple;
|
||||
use termsize::Size;
|
||||
|
||||
pub const PROMPT: &str = concatcp!("\n", BLUE, "»", END_COL, " ");
|
||||
pub const PROMPT: &str = concatcp!(BLUE, "»", END_COL, " ");
|
||||
pub const CONT_PROMPT: &str = concatcp!(BLUE, "…", END_COL, " ");
|
||||
|
||||
/// The prefix we use for the automatic variable names we assign to each expr,
|
||||
|
@ -92,7 +91,7 @@ impl ReplState {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn step(&mut self, line: &str) -> Result<String, i32> {
|
||||
pub fn step(&mut self, line: &str, dimensions: Option<(usize, usize)>) -> Result<String, i32> {
|
||||
let arena = Bump::new();
|
||||
|
||||
match parse_src(&arena, line) {
|
||||
|
@ -103,7 +102,7 @@ impl ReplState {
|
|||
// 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
|
||||
// in a perpetual Incomplete state due to a syntax error.
|
||||
Ok(self.eval_and_format(line))
|
||||
Ok(self.eval_and_format(line, dimensions))
|
||||
} else {
|
||||
// The previous line wasn't blank, but the line isn't empty either.
|
||||
// This could mean that, for example, you're writing a multiline `when`
|
||||
|
@ -119,7 +118,7 @@ impl ReplState {
|
|||
| ParseOutcome::ValueDef(_)
|
||||
| ParseOutcome::TypeDef(_)
|
||||
| ParseOutcome::SyntaxErr
|
||||
| ParseOutcome::Incomplete => Ok(self.eval_and_format(line)),
|
||||
| ParseOutcome::Incomplete => Ok(self.eval_and_format(line, dimensions)),
|
||||
ParseOutcome::Help => {
|
||||
// TODO add link to repl tutorial(does not yet exist).
|
||||
Ok(TIPS.to_string())
|
||||
|
@ -128,7 +127,7 @@ impl ReplState {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn eval_and_format(&mut self, src: &str) -> String {
|
||||
pub fn eval_and_format(&mut self, src: &str, dimensions: Option<(usize, usize)>) -> String {
|
||||
let arena = Bump::new();
|
||||
let pending_past_def;
|
||||
let mut opt_var_name;
|
||||
|
@ -281,7 +280,7 @@ impl ReplState {
|
|||
self.add_past_def(ident, src);
|
||||
}
|
||||
|
||||
format_output(output, problems, opt_var_name)
|
||||
format_output(output, problems, opt_var_name, dimensions)
|
||||
}
|
||||
|
||||
fn next_auto_ident(&mut self) -> u64 {
|
||||
|
@ -550,6 +549,7 @@ fn format_output(
|
|||
opt_output: Option<ReplOutput>,
|
||||
problems: Problems,
|
||||
opt_var_name: Option<String>,
|
||||
dimensions: Option<(usize, usize)>,
|
||||
) -> String {
|
||||
let mut buf = String::new();
|
||||
|
||||
|
@ -587,10 +587,10 @@ fn format_output(
|
|||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
const VAR_NAME_PREFIX: &str = " # "; // e.g. in " # val1"
|
||||
const VAR_NAME_COLUMN_MAX: u16 = 80; // Right-align the var_name at this column
|
||||
const VAR_NAME_COLUMN_MAX: usize = 32; // Right-align the var_name at this column
|
||||
|
||||
let var_name_column = match termsize::get() {
|
||||
Some(Size { cols, rows: _ }) => cols.min(VAR_NAME_COLUMN_MAX) as usize,
|
||||
let term_width = match dimensions {
|
||||
Some((width, _)) => width.min(VAR_NAME_COLUMN_MAX),
|
||||
None => VAR_NAME_COLUMN_MAX as usize,
|
||||
};
|
||||
|
||||
|
@ -605,11 +605,11 @@ fn format_output(
|
|||
.count();
|
||||
let var_name_len =
|
||||
var_name.graphemes(true).count() + VAR_NAME_PREFIX.graphemes(true).count();
|
||||
let spaces_needed = if last_line_len + var_name_len > var_name_column {
|
||||
let spaces_needed = if last_line_len + var_name_len > term_width {
|
||||
buf.push('\n');
|
||||
var_name_column - var_name_len
|
||||
term_width - var_name_len
|
||||
} else {
|
||||
var_name_column - last_line_len - var_name_len
|
||||
term_width - last_line_len - var_name_len
|
||||
};
|
||||
|
||||
for _ in 0..spaces_needed {
|
||||
|
@ -620,6 +620,7 @@ fn format_output(
|
|||
buf.push_str(VAR_NAME_PREFIX);
|
||||
buf.push_str(&var_name);
|
||||
buf.push_str(END_COL);
|
||||
buf.push('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ fn exhaustiveness_problem() {
|
|||
#[test]
|
||||
fn tips() {
|
||||
assert!(!is_incomplete(""));
|
||||
assert_eq!(ReplState::new().step(""), Ok(TIPS.to_string()));
|
||||
assert_eq!(ReplState::new().step("", None), Ok(TIPS.to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -98,7 +98,7 @@ fn standalone_annotation() {
|
|||
|
||||
incomplete(&mut input);
|
||||
assert!(!is_incomplete(&input));
|
||||
assert_eq!(state.step(&input), Ok(String::new()));
|
||||
assert_eq!(state.step(&input, None), Ok(String::new()));
|
||||
}
|
||||
|
||||
/// validate and step the given input, then check the Result vs the output
|
||||
|
@ -106,7 +106,7 @@ fn standalone_annotation() {
|
|||
fn complete(input: &str, state: &mut ReplState, expected_step_result: Result<(&str, &str), i32>) {
|
||||
assert!(!is_incomplete(input));
|
||||
|
||||
match state.step(input) {
|
||||
match state.step(input, None) {
|
||||
Ok(string) => {
|
||||
let escaped =
|
||||
std::string::String::from_utf8(strip_ansi_escapes::strip(string.trim()).unwrap())
|
||||
|
@ -144,7 +144,7 @@ fn incomplete(input: &mut String) {
|
|||
fn error(input: &str, state: &mut ReplState, expected_step_result: String) {
|
||||
assert!(!is_incomplete(input));
|
||||
|
||||
let escaped = state.step(input).map(|string| {
|
||||
let escaped = state.step(input, None).map(|string| {
|
||||
std::string::String::from_utf8(strip_ansi_escapes::strip(string.trim()).unwrap()).unwrap()
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue