cl-frontend: fix strange newline behavior in REPL

This commit is contained in:
John 2024-02-28 01:21:50 -06:00
parent 67bb3d4ae3
commit 3785045989
2 changed files with 58 additions and 46 deletions

View File

@ -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

View File

@ -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> {