Reworked prompt

This commit is contained in:
2023-03-23 22:10:53 -07:00
parent a23cea274f
commit 0eec282cc2
4 changed files with 131 additions and 135 deletions

View File

@ -1,110 +1,103 @@
use std::io;
use std::io::Write;
//use std::io::Read;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use termcolor::{
Color,
ColorChoice,
ColorSpec,
StandardStream,
WriteColor
};
use std::io::{Write, stdout, stdin};
use termion::event::Key;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
use termion::raw::RawTerminal;
use termion::{color, style};
mod parser;
use crate::parser::Token;
//use crate::parser::Token;
//use crate::parser::ParserError;
use crate::parser::LineLocation;
const PROMPT_PREFIX: &str = "==> ";
/// Show a prompt and save trimmed input to `input`.
///
/// # Arguments:
///
/// * `stdout`: Where we should write the prompt
/// * `input`: Where we should save user input
///
/// # Example usage:
/// ```
/// let mut input = String::new();
/// prompt(&mut stdout, &mut input)?;
/// ```
fn prompt(
stdout: &mut StandardStream,
input: &mut String
) -> Result<(), std::io::Error> {
fn draw_line(stdout: &mut RawTerminal<std::io::Stdout>, s: &String) -> Result<(), std::io::Error> {
write!(
stdout,
"\r{}{}==>{}{} {s} {}",
style::Bold,
color::Fg(color::Blue),
color::Fg(color::Reset),
style::Reset,
// Print colored prompt prefix
stdout.set_color(ColorSpec::new().set_fg(Some(Color::Blue)))?;
write!(*stdout, "{PROMPT_PREFIX}")?;
stdout.reset()?; // reset colors
stdout.flush()?; // flush, we didn't print a full line yet.
// Our string can change by at most one character each iteration.
// Clearing is done by inserting an extra space, then moving the cursor left.
termion::cursor::Left(1)
)?;
stdout.flush()?;
// Ask for input
io::stdin().read_line(input)?;
// If this input doesn't end with a newline,
// the user terminated this prompt with ctrl-d.
// Add a newline to keep spacing consistent,
// and clear the input.
if match input.chars().last() {
Some(val) => val != '\n',
None => true
} {
write!(*stdout, "\n")?;
input.clear();
} else {
(*input) = input.trim().to_string();
}
Ok(())
return Ok(());
}
fn main() -> Result<(), std::io::Error> {
let mut stdout = stdout().into_raw_mode().unwrap();
let mut stdout = StandardStream::stdout(ColorChoice::Always);
//let size = termion::terminal_size().unwrap();
//write!(stdout, "{:?}", size).unwrap();
let term = Arc::new(AtomicBool::new(false));
signal_hook::flag::register(signal_hook::consts::SIGINT, Arc::clone(&term))?;
while !term.load(Ordering::Relaxed) {
let mut input = String::with_capacity(64);
prompt(&mut stdout, &mut input).expect("Could not show prompt");
let input = input;
let mut s: String = String::with_capacity(64);
// Ignore empty input
if input == "" {
stdout.flush()?;
continue;
'outer: loop {
s.clear();
draw_line(&mut stdout, &s)?;
let stdin = stdin();
for c in stdin.keys() {
if let Key::Char(q) = c.as_ref().unwrap() {
match q {
'\n' => {
let s = s.trim().to_string();
if s == "" { write!(stdout, "\r\n")?; break; }
RawTerminal::suspend_raw_mode(&stdout)?;
write!(stdout, "\n")?;
let g = parser::parse(&s);
RawTerminal::activate_raw_mode(&stdout)?;
match g {
Ok(g) => {
RawTerminal::suspend_raw_mode(&stdout)?;
writeln!(stdout, "Tokenized: {g:#?}")?;
RawTerminal::activate_raw_mode(&stdout)?;
},
Err((l, e)) => {
let LineLocation{pos, len} = l;
write!(
stdout,
"{}{}{} {e:?}{}\r\n",
color::Fg(color::Red),
" ".repeat(pos + 4),
"^".repeat(len),
color::Fg(color::Reset),
)?;
}
};
break;
},
'/' => { s.push('÷'); },
'*' => { s.push('×'); },
_ => { s.push(*q); }
};
} else {
match c.unwrap() {
Key::Backspace => { s.pop(); },
Key::Delete => { s.pop(); },
Key::Left => {},
Key::Right => {},
Key::Up => {},
Key::Down => {},
Key::Ctrl('d') |
Key::Ctrl('c') => { break 'outer; },
_ => {}
};
};
draw_line(&mut stdout, &s)?;
}
// Parse input.
// Fail if we encounter invalid characters.
let g: Token = match parser::parse(&input) {
Ok(g) => g,
Err((l, e)) => {
let LineLocation{pos, len} = l;
let s = " ";
let m = "^";
println!("{}{} {:?}", s.repeat(pos + 4), m.repeat(len), e);
stdout.flush()?;
continue;
}
};
stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?;
write!(stdout, "\n => ")?;
stdout.reset()?;
write!(stdout, "Got {input}\n\n\n")?;
writeln!(stdout, "Tokenized: {g:#?}")?;
}
writeln!(stdout, "Exiting.")?;
Ok(())
write!(stdout, "\r\n")?;
return Ok(());
}

View File

@ -211,15 +211,18 @@ pub fn tokenize(input: &String) -> Result<Token, (LineLocation, ParserError)> {
// Operator
// Always one character
'+' | '*' | '/' | '^' | '%' => {
'*'|'×'|
'/'|'÷'|
'+'|'%'|'^' => {
push_token(g_now, i, t)?;
t = Some(Token::PreOperator(
LineLocation{pos: i, len: 1},
match c {
'^' => Operators::Power,
'%' => Operators::Modulo,
'*' => Operators::Multiply,
'/' => Operators::Divide,
'*'|'×' => Operators::Multiply,
'/'|'÷' => Operators::Divide,
'+' => Operators::Add,
_ => panic!()
}