cl-frontend: fix strange newline behavior in REPL
This commit is contained in:
parent
67bb3d4ae3
commit
3785045989
@ -213,6 +213,8 @@ pub mod cli {
|
|||||||
const ANSI_RESET: &str = "\x1b[0m";
|
const ANSI_RESET: &str = "\x1b[0m";
|
||||||
const ANSI_OUTPUT: &str = "\x1b[38;5;117m";
|
const ANSI_OUTPUT: &str = "\x1b[38;5;117m";
|
||||||
|
|
||||||
|
const ANSI_CLEAR_LINES: &str = "\x1b[G\x1b[J";
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum CLI {
|
pub enum CLI {
|
||||||
Repl(Repl),
|
Repl(Repl),
|
||||||
@ -358,11 +360,15 @@ pub mod cli {
|
|||||||
/// Prompt functions
|
/// Prompt functions
|
||||||
impl Repl {
|
impl Repl {
|
||||||
pub fn prompt_error(&self, err: &impl Error) {
|
pub fn prompt_error(&self, err: &impl Error) {
|
||||||
println!("{ANSI_RED}{} {err}{ANSI_RESET}", self.prompt_error)
|
let Self { prompt_error: prompt, .. } = self;
|
||||||
|
println!("{ANSI_CLEAR_LINES}{ANSI_RED}{prompt} {err}{ANSI_RESET}",)
|
||||||
}
|
}
|
||||||
|
/// Resets the cursor to the start of the line, clears the terminal,
|
||||||
|
/// and sets the output color
|
||||||
pub fn begin_output(&self) {
|
pub fn begin_output(&self) {
|
||||||
print!("{ANSI_OUTPUT}")
|
print!("{ANSI_CLEAR_LINES}{ANSI_OUTPUT}")
|
||||||
}
|
}
|
||||||
|
pub fn clear_line(&self) {}
|
||||||
}
|
}
|
||||||
/// The actual REPL
|
/// The actual REPL
|
||||||
impl Repl {
|
impl Repl {
|
||||||
@ -372,7 +378,7 @@ pub mod cli {
|
|||||||
}
|
}
|
||||||
/// Runs the main REPL loop
|
/// Runs the main REPL loop
|
||||||
pub fn repl(&mut self) {
|
pub fn repl(&mut self) {
|
||||||
use crate::repline::Repline;
|
use crate::repline::{error::Error, Repline};
|
||||||
let mut rl = Repline::new(
|
let mut rl = Repline::new(
|
||||||
// std::fs::File::open("/dev/stdin").unwrap(),
|
// std::fs::File::open("/dev/stdin").unwrap(),
|
||||||
self.mode.ansi_color(),
|
self.mode.ansi_color(),
|
||||||
@ -380,54 +386,60 @@ pub mod cli {
|
|||||||
self.prompt_again,
|
self.prompt_again,
|
||||||
);
|
);
|
||||||
// self.prompt_begin();
|
// self.prompt_begin();
|
||||||
while let Ok(line) = rl.read() {
|
fn clear_line() {
|
||||||
// Exit the loop
|
print!("\x1b[G\x1b[J");
|
||||||
if line.is_empty() {
|
}
|
||||||
println!();
|
loop {
|
||||||
break;
|
let buf = match rl.read() {
|
||||||
|
Ok(buf) => buf,
|
||||||
|
// Ctrl-C: break if current line is empty
|
||||||
|
Err(Error::CtrlC(buf)) => {
|
||||||
|
if buf.is_empty() || buf.ends_with('\n') {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
self.begin_output();
|
|
||||||
// Process mode-change commands
|
|
||||||
if self.command(&line) {
|
|
||||||
rl.accept();
|
|
||||||
rl.set_color(self.mode.ansi_color());
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Lex the buffer, or reset and output the error
|
// Ctrl-D: reset input, and parse it for errors
|
||||||
let code = Program::new(&line);
|
Err(Error::CtrlD(buf)) => {
|
||||||
match code.lex().into_iter().find(|l| l.is_err()) {
|
if let Err(e) = Program::new(&buf).parse() {
|
||||||
None => (),
|
println!();
|
||||||
Some(Ok(_)) => unreachable!(),
|
clear_line();
|
||||||
Some(Err(error)) => {
|
self.prompt_error(&e);
|
||||||
eprintln!("{error}");
|
}
|
||||||
rl.deny();
|
rl.deny();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
Err(e) => {
|
||||||
|
self.prompt_error(&e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// TODO: only lex the program once
|
|
||||||
// Tokenize mode doesn't require valid parse, so it gets processed first
|
self.begin_output();
|
||||||
|
if self.command(&buf) {
|
||||||
|
rl.deny();
|
||||||
|
rl.set_color(self.mode.ansi_color());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let code = Program::new(&buf);
|
||||||
if self.mode == Mode::Tokenize {
|
if self.mode == Mode::Tokenize {
|
||||||
self.tokenize(&code);
|
self.tokenize(&code);
|
||||||
rl.deny();
|
rl.deny();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Parse code and dispatch to the proper function
|
match code.lex().into_iter().find(|l| l.is_err()) {
|
||||||
match (line.len(), code.parse()) {
|
None => {}
|
||||||
(0, Ok(_)) => {
|
Some(Ok(_)) => unreachable!(),
|
||||||
println!();
|
Some(Err(error)) => {
|
||||||
break;
|
rl.deny();
|
||||||
}
|
eprintln!("{error}");
|
||||||
// If the code is OK, run it and print any errors
|
|
||||||
(_, Ok(mut code)) => {
|
|
||||||
println!();
|
|
||||||
self.dispatch(&mut code);
|
|
||||||
rl.accept();
|
|
||||||
}
|
|
||||||
(_, Err(e)) => {
|
|
||||||
print!("\x1b[100G\x1b[37m//{e}");
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Ok(mut code) = code.parse() {
|
||||||
|
rl.accept();
|
||||||
|
self.dispatch(&mut code);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,11 +454,10 @@ pub mod cli {
|
|||||||
"$tokens" => self.mode = Mode::Tokenize,
|
"$tokens" => self.mode = Mode::Tokenize,
|
||||||
"$type" => self.mode = Mode::Resolve,
|
"$type" => self.mode = Mode::Resolve,
|
||||||
"$run" => self.mode = Mode::Interpret,
|
"$run" => self.mode = Mode::Interpret,
|
||||||
"$mode" => print!("{:?} Mode", self.mode),
|
"$mode" => println!("{:?} Mode", self.mode),
|
||||||
"$help" => self.help(),
|
"$help" => self.help(),
|
||||||
_ => return false,
|
_ => return false,
|
||||||
}
|
}
|
||||||
println!();
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
/// Dispatches calls to repl functions based on the program
|
/// Dispatches calls to repl functions based on the program
|
||||||
|
@ -221,15 +221,14 @@ impl<'a, R: Read> Repline<'a, R> {
|
|||||||
// Ctrl+C: End of Text. Immediately exits.
|
// Ctrl+C: End of Text. Immediately exits.
|
||||||
// Ctrl+D: End of Transmission. Ends the current line.
|
// Ctrl+D: End of Transmission. Ends the current line.
|
||||||
'\x03' => {
|
'\x03' => {
|
||||||
|
self.ed.render(&mut stdout)?;
|
||||||
drop(_make_raw);
|
drop(_make_raw);
|
||||||
return Err(Error::CtrlC(self.ed.to_string()));
|
return Err(Error::CtrlC(self.ed.to_string()));
|
||||||
}
|
}
|
||||||
'\x04' => {
|
'\x04' => {
|
||||||
drop(_make_raw);
|
|
||||||
writeln!(stdout).ignore();
|
|
||||||
self.ed.render(&mut stdout)?; // TODO: this, better
|
self.ed.render(&mut stdout)?; // TODO: this, better
|
||||||
return Ok(self.ed.to_string());
|
drop(_make_raw);
|
||||||
// return Err(Error::CtrlD(self.ed.to_string()));
|
return Err(Error::CtrlD(self.ed.to_string()));
|
||||||
}
|
}
|
||||||
// Tab: extend line by 4 spaces
|
// Tab: extend line by 4 spaces
|
||||||
'\t' => {
|
'\t' => {
|
||||||
@ -333,7 +332,9 @@ impl<'a, R: Read> Repline<'a, R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Clear the line
|
/// Clear the line
|
||||||
pub fn deny(&mut self) {}
|
pub fn deny(&mut self) {
|
||||||
|
self.ed.clear()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Repline<'a, std::io::Stdin> {
|
impl<'a> Repline<'a, std::io::Stdin> {
|
||||||
|
Loading…
Reference in New Issue
Block a user