diff --git a/src/builtin.rs b/src/builtin.rs index 9cf6171..8b5437e 100644 --- a/src/builtin.rs +++ b/src/builtin.rs @@ -1,7 +1,16 @@ +/// changes directory mod cd; + +/// prints stuff mod echo; + +/// prints environment variables mod printenv; + +/// the butt of a file mod tail; + +/// locates a binary on our PATH mod which; use crate::{ @@ -10,6 +19,7 @@ use crate::{ }; use std::collections::HashMap; +/// All of the builtin commands that exist #[derive(Clone, Copy)] pub enum Builtin { Changedir, @@ -38,6 +48,7 @@ impl Call for Builtin { } } +/// A mapping of all of the builtin command values to their names pub fn all() -> HashMap<&'static str, Builtin> { use Builtin::*; HashMap::from([ diff --git a/src/edit.rs b/src/edit.rs index 1bff22a..d4056c3 100644 --- a/src/edit.rs +++ b/src/edit.rs @@ -5,11 +5,12 @@ use crate::{ prompt::Prompt, }; +use anyhow::Result; use std::{cmp::min, io::Write}; /// The text buffer of a text editor. pub struct Buffer { - /// the current contents of the buffer. This is the least efficient way possible to do this. + /// the current text of the edit buffer chars: Vec, /// the position of our edit cursor within [Self::chars] @@ -17,6 +18,7 @@ pub struct Buffer { } impl Buffer { + /// an empty buffer pub fn new() -> Self { Self { chars: Vec::new(), @@ -31,18 +33,21 @@ impl Buffer { self.show_debug(); } - pub fn pop(&mut self) -> String { + /// takes the text out of the editor, leaving an empty buffer in its place + fn pop(&mut self) -> String { let s: String = self.chars.iter().collect(); self.clear(); self.show_debug(); s } - pub fn show(&self) -> String { + fn show(&self) -> String { self.chars.iter().collect() } - pub fn back(&mut self, n: usize) -> bool { + /// moves the edit cursor back by a fixed number of characters without modifying the contents + /// of the buffer + fn back(&mut self, n: usize) -> bool { if self.cursor > 0 { if n > self.cursor { self.cursor = n; @@ -56,7 +61,8 @@ impl Buffer { } } - pub fn backspace(&mut self) -> bool { + /// drops a character from the text buffer at the cursor's location + fn backspace(&mut self) -> bool { if self.chars.len() > 0 { if self.cursor > 0 { self.cursor -= 1; @@ -70,7 +76,7 @@ impl Buffer { } /// removes the elements left of the cursor, returning the count of removed elements - pub fn clear_left(&mut self) -> usize { + fn clear_left(&mut self) -> usize { if self.cursor > 0 { let n = self.chars.drain(..self.cursor).count(); self.cursor = 0; @@ -83,7 +89,7 @@ impl Buffer { /// moves the edit cursor to the beginning of the buffer. The returned value is the index of /// the edit cursor before the move. - pub fn seek_left(&mut self) -> usize { + fn seek_left(&mut self) -> usize { let n = self.cursor; self.cursor = 0; self.show_debug(); @@ -92,7 +98,7 @@ impl Buffer { /// moves the edit cursor to the end of the buffer. The returned value is the index of th eedit /// cursor before the move. - pub fn seek_right(&mut self) -> usize { + fn seek_right(&mut self) -> usize { let n = self.chars.len() - self.cursor; self.cursor = self.chars.len(); self.show_debug(); @@ -100,7 +106,7 @@ impl Buffer { } /// moves the edit cursor forward by n positions - pub fn forward(&mut self, n: usize) -> bool { + fn forward(&mut self, n: usize) -> bool { if self.cursor < self.chars.len() { self.cursor = min(self.chars.len(), self.cursor + n); self.show_debug(); @@ -110,13 +116,15 @@ impl Buffer { } } - pub fn insert(&mut self, c: char) { + /// inserts a character into the edit buffer at the cursor's current location + fn insert(&mut self, c: char) { self.chars.insert(self.cursor, c); self.cursor += 1; self.show_debug(); } - pub fn tail(&self) -> String { + /// the portion of the text buffer from the edit cursor to the end of the buffer + fn tail(&self) -> String { let mut start = self.cursor; if start > 0 { start -= 1; @@ -126,11 +134,13 @@ impl Buffer { chars.iter().collect() } - pub fn pos(&self) -> usize { + /// the current position of the edit cursor + fn pos(&self) -> usize { self.cursor } - pub fn len(&self) -> usize { + /// the length of the text buffer + fn len(&self) -> usize { self.chars.len() } @@ -157,16 +167,22 @@ impl Buffer { } } +/// Describes how we handle an action from the editor enum Status { + /// Editing is still in progress Ongoing, + + /// A command has been entered in the editor and should be sent to the interpreter Submit(String), + + /// The edit session has ended and the shell should shut down Done, } /// A primitive terminal-based text editor pub struct Editor { /// the incoming terminal events - pub(crate) input: input::Reader, + input: input::Reader, /// the in-memory representation of the command that we're currently editing buffer: Buffer, @@ -174,11 +190,12 @@ pub struct Editor { /// our outgoing terminal events, which is used as the display portion of the editor display: output::Writer, + /// this thing draws our prompt prompt: Prompt, } impl Editor { - pub fn new() -> anyhow::Result { + pub fn new() -> Result { Ok(Self { input: input::Reader::new()?, buffer: Buffer::new(), @@ -187,7 +204,9 @@ impl Editor { }) } - pub fn read_command(&mut self) -> anyhow::Result> { + /// reads one command from the editor. This function blocks until the user has entered an + /// entire command. + pub fn read_command(&mut self) -> Result> { use input::Event::*; loop { @@ -225,7 +244,7 @@ impl Editor { } } - fn handle_key_press(&mut self, event: crate::key::Event) -> anyhow::Result { + fn handle_key_press(&mut self, event: crate::key::Event) -> Result { use crate::key::codes::*; match event { _ if event.code == ENTER || event.char == '\r' => return self.submit(), @@ -241,7 +260,7 @@ impl Editor { Ok(Status::Ongoing) } - fn handle_escape(&mut self, esc: Escape) -> anyhow::Result { + fn handle_escape(&mut self, esc: Escape) -> Result { use Escape::*; match esc { Left => self.back(1)?, @@ -253,7 +272,7 @@ impl Editor { Ok(Status::Ongoing) } - fn handle_control(&mut self, cc: ControlCharacter) -> anyhow::Result { + fn handle_control(&mut self, cc: ControlCharacter) -> Result { use ControlCharacter::*; match cc { StartOfHeading => self.seek_left()?, @@ -267,12 +286,12 @@ impl Editor { Ok(Status::Ongoing) } - fn insert_dot(&mut self) -> anyhow::Result<()> { + fn insert_dot(&mut self) -> Result<()> { self.insert('\u{2022}')?; Ok(()) } - fn submit(&mut self) -> anyhow::Result { + fn submit(&mut self) -> Result { self.display.newline()?; let text = self.buffer.pop(); info!("◇ {}", text); @@ -284,7 +303,7 @@ impl Editor { fn focus_end(&mut self) {} /// moves the edit cursor one character to the left - pub fn back(&mut self, n: usize) -> anyhow::Result<()> { + pub fn back(&mut self, n: usize) -> Result<()> { debug!("⛬ ←"); if self.buffer.back(n) { self.display.back(n)?; @@ -293,7 +312,7 @@ impl Editor { } /// moves the edit cursor one character to the right - pub fn forward(&mut self, n: usize) -> anyhow::Result<()> { + pub fn forward(&mut self, n: usize) -> Result<()> { debug!("⛬ →"); if self.buffer.forward(n) { self.display.forward(n)?; @@ -302,7 +321,7 @@ impl Editor { } /// moves the cursor position to the end of the line - pub fn seek_right(&mut self) -> anyhow::Result<()> { + pub fn seek_right(&mut self) -> Result<()> { info!("»"); let n = self.buffer.seek_right(); if n > 0 { @@ -313,7 +332,7 @@ impl Editor { } /// moves the cursor position to the beginning of the line - pub fn seek_left(&mut self) -> anyhow::Result<()> { + pub fn seek_left(&mut self) -> Result<()> { info!("«"); let n = self.buffer.seek_left(); if n > 0 { @@ -324,7 +343,7 @@ impl Editor { } /// clears the line from the current cursor position to the beginning of the line - pub fn clear_left(&mut self) -> anyhow::Result<()> { + pub fn clear_left(&mut self) -> Result<()> { info!("» clear left"); let n = self.buffer.clear_left(); if n > 0 { @@ -342,7 +361,7 @@ impl Editor { /// clears the scrollback buffer, moving the current edit line to the top of the screen, but /// leaving the edit cursor in place - pub fn clear_screen(&mut self) -> anyhow::Result<()> { + pub fn clear_screen(&mut self) -> Result<()> { info!("» clear"); self.display.clear()?; self.prompt.print(&mut self.display)?; @@ -352,14 +371,14 @@ impl Editor { Ok(()) } - pub fn show_prompt(&mut self) -> anyhow::Result<()> { + pub fn show_prompt(&mut self) -> Result<()> { self.reset()?; self.prompt.print(&mut self.display)?; Ok(()) } /// inserts a character at edit cursor's current position - pub fn insert(&mut self, c: char) -> anyhow::Result<()> { + pub fn insert(&mut self, c: char) -> Result<()> { self.buffer.insert(c); let tail = self.buffer.tail(); @@ -375,7 +394,7 @@ impl Editor { Ok(()) } - pub fn backspace(&mut self) -> anyhow::Result<()> { + pub fn backspace(&mut self) -> Result<()> { if !self.buffer.backspace() { return Ok(()); } @@ -397,7 +416,7 @@ impl Editor { Ok(()) } - pub fn reset(&mut self) -> anyhow::Result<()> { + pub fn reset(&mut self) -> Result<()> { self.buffer.clear(); self.display.reset()?; Ok(()) diff --git a/src/input.rs b/src/input.rs index c15510a..ae8efb9 100644 --- a/src/input.rs +++ b/src/input.rs @@ -329,12 +329,14 @@ impl From for Event { } } +/// A reference to a target node in a prefix tree, used for manipulating the prefix tree #[derive(Debug)] pub struct EscapeCursor { target: Rc, root: Rc, } +/// A node in a prefix tree. This prefix tree is used to parse strings into escape sequences #[derive(Debug)] pub enum EscapeNode { Root { @@ -436,10 +438,12 @@ impl EscapeCursor { } } + /// adds a terminal node into the tree as a child of the current node fn add_terminal(&mut self, c: char, v: Escape) { self.target.add_child_terminal(c, v); } + /// resets the cursor back to the top of the tree fn reset(&mut self) { self.target = Rc::clone(&self.root); } @@ -448,6 +452,7 @@ impl EscapeCursor { Rc::ptr_eq(&self.target, &self.root) } + /// inserts a sequence -> Escape mapping into the prefix tree fn insert(&mut self, sequence: &str, v: Escape) { self.reset(); let mut chars = sequence.chars().peekable(); @@ -462,8 +467,10 @@ impl EscapeCursor { } } +/// generates a prefix tree used for parsing escape sequences macro_rules! escapes { ($($sequence:literal $variant:tt)*) => { + /// a parsed escape sequence #[derive(Debug, Clone, Copy)] pub enum Escape { Empty, @@ -472,7 +479,10 @@ macro_rules! escapes { )* } - pub fn build_prefix_tree() -> EscapeCursor { + /// assembles our prefix tree. Generally only needs to be called once. I think this could + /// actually be done in a proc macro that builds the prefix tree at compile time instead of + /// at run time. + fn build_prefix_tree() -> EscapeCursor { let mut tree = EscapeNode::new(); $( let v = Escape::$variant; @@ -521,7 +531,8 @@ escapes! { "[53;5u" CtrlShift5 } -/// a control character +/// a control character. Control characters are single individual ascii characters that represent +/// out of band signalling. #[derive(Debug)] pub enum ControlCharacter { Null, diff --git a/src/key.rs b/src/key.rs index 2261900..7920ea3 100644 --- a/src/key.rs +++ b/src/key.rs @@ -1,15 +1,18 @@ use std::fmt; use windows::Win32::System::Console; +/// an abstract keycode #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct Code { - /// The integer value of the keycode + /// The integer value of the keycode, as defined by the Windows api. These associate to windows + /// virtual key codes. pub val: u16, /// The unicode symbol that represents the keycode, if any pub sym: Option, } +/// An individual keypress event #[derive(Debug)] pub struct Event { /// True for key press events, false for key release events @@ -31,6 +34,7 @@ pub struct Event { /// relationship to the user's chosen layout. pub scancode: u16, + /// the untranslated windows virtual keycode pub keycode: u16, /// The unicode character of the Event. Note this these correspond to virtual terminal @@ -107,6 +111,7 @@ impl fmt::Display for Event { macro_rules! codes { ($($val:literal $name:ident $sym:literal)*) => { + /// stores our key code constants pub mod codes { use super::Code; $( diff --git a/src/lex.rs b/src/lex.rs index 820b524..34877fc 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -60,6 +60,8 @@ impl From> for Lexeme { } } +/// The smallest lexical unit of our language. A token represents a single element of program +/// source text, such as an identifier or a piece of punctuation. #[allow(dead_code)] #[derive(Debug, PartialEq, Clone)] pub enum Token { @@ -103,8 +105,9 @@ impl Token { } } -/// Tokenizer splits some input [Glyphs] into [Token] values -pub struct Tokenizer<'text> { +/// Tokenizer splits some input [Glyphs] into [Token] values. The Tokenize has no lookahead or +/// buffering. +struct Tokenizer<'text> { source: Glyphs<'text>, } @@ -199,6 +202,7 @@ impl<'text> Iterator for Tokenizer<'text> { } } +/// A Lexer is responsible for converting a piece of source text into a sequence of tokens. pub struct Lexer<'text> { source: Tokenizer<'text>, lookahead: VecDeque, @@ -223,7 +227,7 @@ impl<'text> Lexer<'text> { Ok(true) } - pub fn peek_at(&mut self, idx: usize) -> Result, LexError> { + fn peek_at(&mut self, idx: usize) -> Result, LexError> { self.fill_lookahead(idx + 1)?; Ok(self.lookahead.get(idx)) } diff --git a/src/main.rs b/src/main.rs index 320bcee..e7a5c75 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,10 @@ mod lex; /// a real primitive line editor mod edit; +/// logging configuration mod log; + +/// stdout control, output more generally mod output; /// turns our tokens into parse trees @@ -43,28 +46,5 @@ fn main() -> anyhow::Result<()> { #[cfg(debug_assertions)] session.enable_logging("~/clyde.log"); - session.run()?; - Ok(()) - - /* - loop { - match session.input.next()? { - input::Event::Key(event) => { - // CTRL-J to draw a cool little dot - if event.ctrl && event.code == key::J { - debug!("⎈ j: dot"); - // red bullet - session - .stdout - .write(String::from("\x1b[31m\u{2022}\x1b[0m").as_bytes())?; - continue; - } - - warn!("‽ {}", event); - } - input::Event::Up => debug!("⛬ ↑"), - input::Event::Down => debug!("⛬ ↓"), - } - } - */ + session.run() } diff --git a/src/parse.rs b/src/parse.rs index e214b6a..b6db748 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -72,6 +72,7 @@ impl PartialEq for Node { } } +/// Iterates through the children of a cursor node, producing new cursor positions. pub struct ChildIter { target: Rc, root: Rc, @@ -179,6 +180,7 @@ impl Cursor { } } +/// The Parser is responsible for turning a sequence of Tokens into a Parse Tree. pub struct Parser<'text> { input: Lexer<'text>, output: Cursor, diff --git a/src/prompt.rs b/src/prompt.rs index 2d62f4d..b12a3a7 100644 --- a/src/prompt.rs +++ b/src/prompt.rs @@ -1,7 +1,8 @@ use crate::output; -use std::io::Write; use anyhow::Result; +use std::io::Write; +/// draws the prompt that appears before a user enters a command pub struct Prompt { s: String, } @@ -21,12 +22,13 @@ impl Prompt { pub fn print(&self, output: &mut output::Writer) -> Result<()> { match std::env::current_dir() { Ok(d) => { - let text = d.to_str().unwrap().to_owned() + &self.s; - output.write(text.as_bytes())?; + _ = write!(output, "{cwd}{arrow}", cwd = d.display(), arrow = self.s); + // let text = d.to_str().unwrap().to_owned() + &self.s; + // output.write(text.as_bytes())?; } Err(_) => { - output.write(self.s.as_bytes())?; - }, + _ = write!(output, "{}", self.s); + } } Ok(()) } diff --git a/src/syntax.rs b/src/syntax.rs index d35c034..791ea33 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -106,6 +106,8 @@ impl Eval for Command { } } +/// the TreeBuilder is responsible for converting a parse tree into an abstract syntax tree. This +/// process leaves us with a tree that can be executed directly struct TreeBuilder { visited: HashSet, }