Added simple history

pull/2/head
Mark 2023-03-24 19:42:23 -07:00
parent 09ad88c5d7
commit 5fc7a7922e
Signed by: Mark
GPG Key ID: AD62BB059C2AAEE4
2 changed files with 144 additions and 19 deletions

View File

@ -1,4 +1,5 @@
use std::io::{Write, stdout, stdin}; use std::io::{Write, stdout, stdin};
use termion::event::Key; use termion::event::Key;
use termion::input::TermRead; use termion::input::TermRead;
use termion::raw::IntoRawMode; use termion::raw::IntoRawMode;
@ -6,24 +7,38 @@ use termion::raw::RawTerminal;
use termion::{color, style}; use termion::{color, style};
mod parser; mod parser;
mod promptbuffer;
use crate::promptbuffer::PromptBuffer;
use crate::parser::Token; use crate::parser::Token;
//use crate::parser::ParserError; //use crate::parser::ParserError;
use crate::parser::LineLocation; use crate::parser::LineLocation;
use crate::parser::Eval; use crate::parser::Eval;
fn draw_line(stdout: &mut RawTerminal<std::io::Stdout>, s: &String) -> Result<(), std::io::Error> { fn draw_line(
stdout: &mut RawTerminal<std::io::Stdout>,
s: &String,
clear_len: usize
) -> Result<(), std::io::Error> {
write!( write!(
stdout, "\r{}{}==>{}{} {s} {}", stdout, "\r{}{}==>{}{} {}",
style::Bold, style::Bold,
color::Fg(color::Blue), color::Fg(color::Blue),
color::Fg(color::Reset), color::Fg(color::Reset),
style::Reset, style::Reset,
s
// 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)
)?; )?;
// If this string is shorter, clear the remaining old one.
if clear_len != 0 {
write!(
stdout, "{}{}",
" ".repeat(clear_len as usize),
termion::cursor::Left(clear_len as u16)
)?;
}
stdout.flush()?; stdout.flush()?;
return Ok(()); return Ok(());
@ -35,19 +50,26 @@ fn main() -> Result<(), std::io::Error> {
//let size = termion::terminal_size().unwrap(); //let size = termion::terminal_size().unwrap();
//write!(stdout, "{:?}", size).unwrap(); //write!(stdout, "{:?}", size).unwrap();
let mut s: String = String::with_capacity(64); let mut pb: PromptBuffer = PromptBuffer::new(64);
let mut last_len: usize = 0;
'outer: loop { 'outer: loop {
s.clear(); draw_line(
draw_line(&mut stdout, &s)?; &mut stdout,
pb.get_contents(),
if pb.get_contents().len() >= last_len
{ 0 } else {last_len - pb.get_contents().len()}
)?;
last_len = pb.get_contents().len();
let stdin = stdin(); let stdin = stdin();
for c in stdin.keys() { for c in stdin.keys() {
if let Key::Char(q) = c.as_ref().unwrap() { if let Key::Char(q) = c.as_ref().unwrap() {
match q { match q {
'\n' => { '\n' => {
let s = s.trim().to_string(); let s = pb.enter();
if s == "" { write!(stdout, "\r\n")?; break; } if s == "" { write!(stdout, "\r\n")?; break; }
RawTerminal::suspend_raw_mode(&stdout)?; RawTerminal::suspend_raw_mode(&stdout)?;
@ -82,25 +104,32 @@ fn main() -> Result<(), std::io::Error> {
break; break;
}, },
'/' => { s.push('÷'); }, '/' => { pb.add_char('÷'); },
'*' => { s.push('×'); }, '*' => { pb.add_char('×'); },
_ => { s.push(*q); } _ => { pb.add_char(*q); }
}; };
} else { } else {
match c.unwrap() { match c.unwrap() {
Key::Backspace => { s.pop(); }, Key::Backspace => { pb.backspace(); },
Key::Delete => { s.pop(); }, Key::Delete => { pb.delete(); },
Key::Left => {}, Key::Left => {},
Key::Right => {}, Key::Right => {},
Key::Up => {}, Key::Up => { pb.hist_up(); },
Key::Down => {}, Key::Down => { pb.hist_down(); },
Key::Ctrl('d') | Key::Ctrl('d') |
Key::Ctrl('c') => { break 'outer; }, Key::Ctrl('c') => { break 'outer; },
_ => {} _ => {}
}; };
}; };
draw_line(&mut stdout, &s)?;
draw_line(
&mut stdout,
pb.get_contents(),
if pb.get_contents().len() >= last_len
{ 0 } else {last_len - pb.get_contents().len()}
)?;
last_len = pb.get_contents().len();
} }
} }

96
src/promptbuffer.rs Normal file
View File

@ -0,0 +1,96 @@
use std::collections::VecDeque;
#[derive(Debug)]
pub struct PromptBuffer {
// History
hist: VecDeque<String>,
hist_maxlen: usize,
// Counts from back of hist.
// 0 means "not on history",
// 1 means "on last item of history"
hist_cursor: usize,
buffer: String,
buffer_changed: bool,
//cursor: usize // Counts from back of buffer
}
impl PromptBuffer {
pub fn new(maxlen: usize) -> PromptBuffer {
return PromptBuffer {
hist: VecDeque::with_capacity(maxlen/2),
hist_maxlen: maxlen,
hist_cursor: 0,
buffer: String::with_capacity(64),
buffer_changed: false
//cursor: 0,
};
}
// Prompt methods
pub fn get_contents(&self) -> &String {&self.buffer}
pub fn enter(&mut self) -> String{
let s = String::from(self.buffer.trim());
self.buffer.clear();
self.hist_cursor = 0;
self.buffer_changed = false;
if s != "" { self.hist.push_back(s.clone()); }
return s;
}
// Buffer manipulation
pub fn add_char(&mut self, c: char) {
self.buffer.push(c);
self.buffer_changed = true;
}
pub fn backspace(&mut self) {
if self.buffer.len() != 0 {
self.buffer_changed = true;
self.buffer.pop();
}
}
pub fn delete(&mut self) {
self.backspace();
}
// History manipulation
pub fn hist_up(&mut self) {
if self.buffer_changed && self.buffer.len() != 0 { return; }
if self.hist_cursor < self.hist.len() {
if self.buffer.len() != 0 || !self.buffer_changed {
self.hist_cursor += 1;
}
self.buffer_changed = false;
if self.hist_cursor == 0 {
self.buffer.clear();
} else {
self.buffer = self.hist[self.hist.len() - self.hist_cursor].clone();
}
}
}
pub fn hist_down(&mut self) {
if self.buffer_changed && self.buffer.len() != 0 { return; }
if self.hist_cursor > 0 {
self.hist_cursor -= 1;
self.buffer_changed = false;
if self.hist_cursor == 0 {
self.buffer.clear();
} else {
self.buffer = self.hist[self.hist.len() - self.hist_cursor].clone();
}
} else {
self.buffer.clear();
}
}
}