renaming Shell to Session

main
Jordan Orelli 8 months ago
parent 68909d22a8
commit f39a22d408

@ -1,7 +1,7 @@
use crate::log::*; use crate::log::*;
use std::cmp::min; use std::cmp::min;
pub struct Line { pub struct Editor {
/// the current contents of the line /// the current contents of the line
chars: Vec<char>, chars: Vec<char>,
@ -9,7 +9,7 @@ pub struct Line {
cursor: usize, cursor: usize,
} }
impl Line { impl Editor {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
chars: Vec::new(), chars: Vec::new(),

@ -1,8 +1,8 @@
use crate::{error::Error, key, log::*}; use crate::{error::Error, key, log::*};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use macros::escapes;
use windows::Win32::Foundation::HANDLE; use windows::Win32::Foundation::HANDLE;
use windows::Win32::System::Console; use windows::Win32::System::Console;
use macros::escapes;
#[allow(dead_code)] #[allow(dead_code)]
fn log_input_mode(mode: Console::CONSOLE_MODE) { fn log_input_mode(mode: Console::CONSOLE_MODE) {
@ -214,49 +214,79 @@ impl Reader {
';' => match self.next_escape_char()? { ';' => match self.next_escape_char()? {
'5' => match self.next_escape_char()? { '5' => match self.next_escape_char()? {
'u' => Ok(Event::Drop(String::from("13;5u"))), 'u' => Ok(Event::Drop(String::from("13;5u"))),
e => Err(Error::input_error(format!("[13;5 unexpected escape char: {}", e)).into()), e => Err(Error::input_error(format!(
} "[13;5 unexpected escape char: {}",
e => Err(Error::input_error(format!("[13; unexpected escape char: {}", e)).into()), e
} ))
e => Err(Error::input_error(format!("[13 unexpected escape char: {}", e)).into()), .into()),
} },
e => Err(Error::input_error(format!(
"[13; unexpected escape char: {}",
e
))
.into()),
},
e => Err(
Error::input_error(format!("[13 unexpected escape char: {}", e)).into(),
),
},
'5' => match self.next_escape_char()? { '5' => match self.next_escape_char()? {
'~' => Ok(Event::Drop(String::from("[15~ - F5"))), '~' => Ok(Event::Drop(String::from("[15~ - F5"))),
e => Err(Error::input_error(format!("[15 unexpected escape char: {}", e)).into()), e => Err(
} Error::input_error(format!("[15 unexpected escape char: {}", e)).into(),
),
},
'7' => match self.next_escape_char()? { '7' => match self.next_escape_char()? {
'~' => Ok(Event::Drop(String::from("[17~ - F6"))), '~' => Ok(Event::Drop(String::from("[17~ - F6"))),
e => Err(Error::input_error(format!("[17 unexpected escape char: {}", e)).into()), e => Err(
} Error::input_error(format!("[17 unexpected escape char: {}", e)).into(),
),
},
'8' => match self.next_escape_char()? { '8' => match self.next_escape_char()? {
'~' => Ok(Event::Drop(String::from("[18~ - F7"))), '~' => Ok(Event::Drop(String::from("[18~ - F7"))),
e => Err(Error::input_error(format!("[18 unexpected escape char: {}", e)).into()), e => Err(
} Error::input_error(format!("[18 unexpected escape char: {}", e)).into(),
),
},
'9' => match self.next_escape_char()? { '9' => match self.next_escape_char()? {
'~' => Ok(Event::Drop(String::from("[19~ - F8"))), '~' => Ok(Event::Drop(String::from("[19~ - F8"))),
e => Err(Error::input_error(format!("[19 unexpected escape char: {}", e)).into()), e => Err(
Error::input_error(format!("[19 unexpected escape char: {}", e)).into(),
),
},
e => {
Err(Error::input_error(format!("[1 unexpected escape char: {}", e)).into())
} }
e => Err(Error::input_error(format!("[1 unexpected escape char: {}", e)).into()),
}, },
'2' => match self.next_escape_char()? { '2' => match self.next_escape_char()? {
'0' => match self.next_escape_char()? { '0' => match self.next_escape_char()? {
'~' => Ok(Event::Drop(String::from("[20~ - F9"))), '~' => Ok(Event::Drop(String::from("[20~ - F9"))),
e => Err(Error::input_error(format!("[20 unexpected escape char: {}", e)).into()), e => Err(
} Error::input_error(format!("[20 unexpected escape char: {}", e)).into(),
),
},
'1' => match self.next_escape_char()? { '1' => match self.next_escape_char()? {
'~' => Ok(Event::Drop(String::from("[21~ - F10"))), '~' => Ok(Event::Drop(String::from("[21~ - F10"))),
e => Err(Error::input_error(format!("[20 unexpected escape char: {}", e)).into()), e => Err(
} Error::input_error(format!("[20 unexpected escape char: {}", e)).into(),
),
},
'3' => match self.next_escape_char()? { '3' => match self.next_escape_char()? {
'~' => Ok(Event::Drop(String::from("[23~ - F11"))), '~' => Ok(Event::Drop(String::from("[23~ - F11"))),
e => Err(Error::input_error(format!("[23 unexpected escape char: {}", e)).into()), e => Err(
} Error::input_error(format!("[23 unexpected escape char: {}", e)).into(),
),
},
'4' => match self.next_escape_char()? { '4' => match self.next_escape_char()? {
'~' => Ok(Event::Drop(String::from("[24~ - F12"))), '~' => Ok(Event::Drop(String::from("[24~ - F12"))),
e => Err(Error::input_error(format!("[24 unexpected escape char: {}", e)).into()), e => Err(
Error::input_error(format!("[24 unexpected escape char: {}", e)).into(),
),
},
e => {
Err(Error::input_error(format!("[2 unexpected escape char: {}", e)).into())
} }
e => Err(Error::input_error(format!("[2 unexpected escape char: {}", e)).into()), },
}
e => Err(Error::input_error(format!("[ unexpected escape char: {}", e)).into()), e => Err(Error::input_error(format!("[ unexpected escape char: {}", e)).into()),
}, },
'O' => match self.next_escape_char()? { 'O' => match self.next_escape_char()? {
@ -265,27 +295,11 @@ impl Reader {
'R' => Ok(Event::Drop(String::from("OR - F3"))), 'R' => Ok(Event::Drop(String::from("OR - F3"))),
'S' => Ok(Event::Drop(String::from("OS - F4"))), 'S' => Ok(Event::Drop(String::from("OS - F4"))),
e => Err(Error::input_error(format!("O unexpected escape char: {}", e)).into()), e => Err(Error::input_error(format!("O unexpected escape char: {}", e)).into()),
} },
e => Err(Error::input_error(format!("unexpected escape char: {}", e)).into()), e => Err(Error::input_error(format!("unexpected escape char: {}", e)).into()),
} }
} }
// fn take_bracket(&mut self) -> Result<()> {
// let rec = self.next_rec()?;
// if rec.EventType as u32 != Console::KEY_EVENT {
// Err(Error::input_error("failed to read escape sequence: not a key event").into())
// } else {
// unsafe {
// let event = rec.Event.KeyEvent;
// if event.wVirtualKeyCode == 0 && event.uChar.UnicodeChar == 91 {
// Ok(())
// } else {
// Err(Error::input_error("failed to read escape sequence: not a [").into())
// }
// }
// }
// }
fn next_escape_char(&mut self) -> Result<char> { fn next_escape_char(&mut self) -> Result<char> {
let rec = self.next_rec()?; let rec = self.next_rec()?;
if rec.EventType as u32 != Console::KEY_EVENT { if rec.EventType as u32 != Console::KEY_EVENT {
@ -416,5 +430,3 @@ escapes! {
"[24~" F12 "[24~" F12
"[53;5u" Ctrl_Shift_5 "[53;5u" Ctrl_Shift_5
} }
/*
*/

@ -1,7 +1,7 @@
use crate::{ use crate::{
edit::Editor,
ext::{Command, Echo, Printenv, Tail, Which}, ext::{Command, Echo, Printenv, Tail, Which},
input, input,
line::Line,
log::*, log::*,
output, syntax, output, syntax,
}; };
@ -16,26 +16,26 @@ use std::{
use anyhow::Result; use anyhow::Result;
use dirs; use dirs;
pub struct Shell { pub struct Session {
pub input: input::Reader, pub input: input::Reader,
pub output: output::Writer, pub output: output::Writer,
pub line: Line, pub editor: Editor,
pub state: syntax::State, pub state: syntax::State,
} }
impl Shell { impl Session {
pub fn new() -> Result<Self> { pub fn new() -> Result<Self> {
Ok(Self { Ok(Self {
input: input::Reader::new()?, input: input::Reader::new()?,
output: output::Writer::stdout()?, output: output::Writer::stdout()?,
line: Line::new(), editor: Editor::new(),
state: syntax::State::new(), state: syntax::State::new(),
}) })
} }
pub fn back(&mut self, n: usize) -> Result<()> { pub fn back(&mut self, n: usize) -> Result<()> {
debug!("⛬ ←"); debug!("⛬ ←");
if self.line.back(n) { if self.editor.back(n) {
self.output.back(n)?; self.output.back(n)?;
} }
Ok(()) Ok(())
@ -43,7 +43,7 @@ impl Shell {
pub fn forward(&mut self, n: usize) -> Result<()> { pub fn forward(&mut self, n: usize) -> Result<()> {
debug!("⛬ →"); debug!("⛬ →");
if self.line.forward(n) { if self.editor.forward(n) {
self.output.forward(n)?; self.output.forward(n)?;
} }
Ok(()) Ok(())
@ -57,7 +57,7 @@ impl Shell {
pub fn seek_right(&mut self) -> Result<()> { pub fn seek_right(&mut self) -> Result<()> {
info!("»"); info!("»");
let n = self.line.seek_right(); let n = self.editor.seek_right();
if n > 0 { if n > 0 {
// move right by the distance seeked // move right by the distance seeked
self.output.forward(n)?; self.output.forward(n)?;
@ -67,7 +67,7 @@ impl Shell {
pub fn seek_left(&mut self) -> Result<()> { pub fn seek_left(&mut self) -> Result<()> {
info!("«"); info!("«");
let n = self.line.seek_left(); let n = self.editor.seek_left();
if n > 0 { if n > 0 {
// move left by the distance seeked // move left by the distance seeked
self.output.back(n)?; self.output.back(n)?;

@ -11,6 +11,9 @@ mod ext;
/// handles input from terminals /// handles input from terminals
mod input; mod input;
/// defines the interactive mode
mod interactive;
/// key presses, independent from input sources /// key presses, independent from input sources
mod key; mod key;
@ -18,7 +21,7 @@ mod key;
mod lex; mod lex;
/// a real primitive line editor /// a real primitive line editor
mod line; mod edit;
mod log; mod log;
mod output; mod output;
@ -27,7 +30,6 @@ mod output;
mod parse; mod parse;
mod prompt; mod prompt;
mod shell;
/// syntax and semantic analysis /// syntax and semantic analysis
mod syntax; mod syntax;
@ -35,25 +37,22 @@ mod syntax;
/// topoglyph is a real word, i promise /// topoglyph is a real word, i promise
mod topo; mod topo;
use crate::log::*; use crate::{interactive::Session, log::*, prompt::Prompt, syntax::Eval};
use prompt::Prompt;
use shell::Shell;
use syntax::Eval;
use std::io::Write; use std::io::Write;
use anyhow::Result; use anyhow::Result;
fn main() -> Result<()> { fn main() -> Result<()> {
let mut shell = Shell::new()?; let mut session = Session::new()?;
shell.enable_logging("~/clyde.log"); session.enable_logging("~/clyde.log");
let prompt = Prompt::new(); let prompt = Prompt::new();
prompt.print(&mut shell.output)?; prompt.print(&mut session.output)?;
info!("» shell session start --------"); info!("» shell session start --------");
loop { loop {
match shell.input.next()? { match session.input.next()? {
input::Event::Key(event) => { input::Event::Key(event) => {
if event.down { if event.down {
if event.code.val == 0 { if event.code.val == 0 {
@ -66,18 +65,18 @@ fn main() -> Result<()> {
info!(" {}", event); info!(" {}", event);
if event.code == key::LEFT { if event.code == key::LEFT {
shell.back(1)?; session.back(1)?;
continue; continue;
} }
if event.code == key::RIGHT { if event.code == key::RIGHT {
shell.forward(1)?; session.forward(1)?;
continue; continue;
} }
if event.code == key::ENTER { if event.code == key::ENTER {
shell.output.newline()?; session.output.newline()?;
let s = shell.line.pop(); let s = session.editor.pop();
info!("◇ {}", s); info!("◇ {}", s);
if let Ok(tokens) = lex::lex(&s) { if let Ok(tokens) = lex::lex(&s) {
for t in tokens { for t in tokens {
@ -90,19 +89,19 @@ fn main() -> Result<()> {
let mut state = syntax::State::new(); let mut state = syntax::State::new();
if let Err(e) = tree.eval(&mut state) { if let Err(e) = tree.eval(&mut state) {
error!("{e:?}"); error!("{e:?}");
shell.render_error(e); _ = session.render_error(e);
} }
} }
Err(e) => { Err(e) => {
error!("{e:?}"); error!("{e:?}");
shell.render_error(e); _ = session.render_error(e);
} }
} }
// shell.exec(tree.into())?; // shell.exec(tree.into())?;
// Some commands don't leave the terminal in a clean state, so we use reset // Some commands don't leave the terminal in a clean state, so we use reset
// to ensure that our input and output modes are what we expect them to be. // to ensure that our input and output modes are what we expect them to be.
shell.reset()?; session.reset()?;
prompt.print(&mut shell.output)?; prompt.print(&mut session.output)?;
continue; continue;
} }
@ -111,22 +110,22 @@ fn main() -> Result<()> {
} }
if event.code == key::BACKSPACE { if event.code == key::BACKSPACE {
if shell.line.backspace() { if session.editor.backspace() {
// move cursor back two spaces // move cursor back two spaces
shell.output.back(2)?; session.output.back(2)?;
let tail = format!("{} ", shell.line.tail()); let tail = format!("{} ", session.editor.tail());
let n = tail.chars().count(); let n = tail.chars().count();
shell.output.write(tail.as_bytes())?; session.output.write(tail.as_bytes())?;
// after writing out the tail, rewind by the number of characters in // after writing out the tail, rewind by the number of characters in
// the tail // the tail
if n > 1 { if n > 1 {
// let text = format!("\x1b[{}D", n - 1); // let text = format!("\x1b[{}D", n - 1);
shell.output.back(n - 1)?; session.output.back(n - 1)?;
// output.write(text.as_bytes())?; // output.write(text.as_bytes())?;
} else { } else {
// honestly I can't remember how I figured this out // honestly I can't remember how I figured this out
shell.output.write(b" \x1b[1D")?; session.output.write(b" \x1b[1D")?;
} }
} }
continue; continue;
@ -135,7 +134,7 @@ fn main() -> Result<()> {
// CTRL-D to exit // CTRL-D to exit
if event.ctrl && event.code == key::D { if event.ctrl && event.code == key::D {
info!("» exit"); info!("» exit");
shell.output.close()?; session.output.close()?;
return Ok(()); return Ok(());
} }
@ -143,7 +142,7 @@ fn main() -> Result<()> {
if event.ctrl && event.code == key::J { if event.ctrl && event.code == key::J {
debug!("⎈ j: dot"); debug!("⎈ j: dot");
// red bullet // red bullet
shell session
.output .output
.write(String::from("\x1b[31m\u{2022}\x1b[0m").as_bytes())?; .write(String::from("\x1b[31m\u{2022}\x1b[0m").as_bytes())?;
continue; continue;
@ -152,69 +151,71 @@ fn main() -> Result<()> {
// CTRL-L to clear the screen // CTRL-L to clear the screen
if event.ctrl && event.code == key::L { if event.ctrl && event.code == key::L {
info!("» clear"); info!("» clear");
shell.output.clear()?; session.output.clear()?;
prompt.print(&mut shell.output)?; prompt.print(&mut session.output)?;
shell.output.write(shell.line.show().as_bytes())?; session.output.write(session.editor.show().as_bytes())?;
shell.output.back(shell.line.len() - shell.line.pos())?; session
shell.reset()?; .output
.back(session.editor.len() - session.editor.pos())?;
session.reset()?;
continue; continue;
} }
// CTRL-U to erase to the beginning of the line // CTRL-U to erase to the beginning of the line
if event.ctrl && event.code == key::U { if event.ctrl && event.code == key::U {
info!("» clear left"); info!("» clear left");
let n = shell.line.clear_left(); let n = session.editor.clear_left();
if n > 0 { if n > 0 {
// move left by the number of elements removed // move left by the number of elements removed
shell.output.back(n)?; session.output.back(n)?;
// draw the elements remaining, followed by a space for each removed // draw the elements remaining, followed by a space for each removed
// element // element
let kept = shell.line.show(); let kept = session.editor.show();
let text = format!("{}{:width$}", kept, "", width = n); let text = format!("{}{:width$}", kept, "", width = n);
shell.output.write(text.as_bytes())?; session.output.write(text.as_bytes())?;
shell.output.back(n + kept.chars().count())?; session.output.back(n + kept.chars().count())?;
} }
continue; continue;
} }
// CTRL-A to move to the beginning of the line // CTRL-A to move to the beginning of the line
if event.ctrl && event.code == key::A { if event.ctrl && event.code == key::A {
shell.seek_left()?; session.seek_left()?;
continue; continue;
} }
// CTRL-E to move to the end of the line // CTRL-E to move to the end of the line
if event.ctrl && event.code == key::E { if event.ctrl && event.code == key::E {
shell.seek_right()?; session.seek_right()?;
continue; continue;
} }
// TODO: something better here, this is crappy. I should be checking characters // TODO: something better here, this is crappy. I should be checking characters
// based on their unicode categories, not this garbo // based on their unicode categories, not this garbo
if !event.char.is_control() { if !event.char.is_control() {
shell.line.insert(event.char); session.editor.insert(event.char);
let tail = shell.line.tail(); let tail = session.editor.tail();
let n = tail.chars().count(); let n = tail.chars().count();
// write everything from the current line cursor out to the output buffer. // write everything from the current line cursor out to the output buffer.
shell.output.write(tail.as_bytes())?; session.output.write(tail.as_bytes())?;
if n > 1 { if n > 1 {
// if we wrote more than one character, because we weren't at the end, we // if we wrote more than one character, because we weren't at the end, we
// need to rewind the terminal cursor to where it was. // need to rewind the terminal cursor to where it was.
shell.output.back(n - 1)?; session.output.back(n - 1)?;
} }
continue; continue;
} }
warn!("‽ {}", event); warn!("‽ {}", event);
} }
input::Event::Left => shell.back(1)?, input::Event::Left => session.back(1)?,
input::Event::Right => shell.forward(1)?, input::Event::Right => session.forward(1)?,
input::Event::Up => debug!("⛬ ↑"), input::Event::Up => debug!("⛬ ↑"),
input::Event::Down => debug!("⛬ ↓"), input::Event::Down => debug!("⛬ ↓"),
input::Event::Home => shell.seek_left()?, input::Event::Home => session.seek_left()?,
input::Event::End => shell.seek_right()?, input::Event::End => session.seek_right()?,
input::Event::Focus(true) => {} input::Event::Focus(true) => {}
input::Event::Focus(false) => {} input::Event::Focus(false) => {}
input::Event::Menu(_command_id) => {} input::Event::Menu(_command_id) => {}

Loading…
Cancel
Save