-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce CommandPrompt to manage interactions with the command prompt
This is a state machine, tracking inputs and maintaining properties of the prompt. It doesn't yet handle input parsing but the scaffolding for passing commands back to the main loop are set up. The implementation of CommandPrompt may seem a little strange, I think. It has a few interesting features: * it consumes itself on each transition - meaning you can't assign the result of a transition to a variable and then accidentally trigger another state update on the original * it emits events based on a pull model, assigning the results of the last input to it's `last_event` field. I implemented it this way to avoid the complexity of working with callbacks. On second thoughts maybe it'd make it harder to forget about the event by having `CommandPrompt::step` emit a tuple `(CommandPrompt, Event)`. I'm probably going to pull in `nom`[1] for writing the command parser. This may be overkill but I want excuses to use it ¯\_(ツ)_/¯ [1]: https://github.com/Geal/nom
- Loading branch information
1 parent
ad85462
commit 4a1165f
Showing
3 changed files
with
162 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
use termion::event::Key; | ||
|
||
pub enum CommandMachineEvent { | ||
Reset, | ||
Update, | ||
Execute(Command), | ||
} | ||
|
||
#[derive(Debug)] | ||
pub enum Command { | ||
SetWidth(usize), | ||
} | ||
|
||
pub struct CommandPrompt { | ||
pub text: String, | ||
pub cursor: usize, | ||
pub last_event: CommandMachineEvent, | ||
} | ||
|
||
impl CommandPrompt { | ||
pub fn new() -> Self { | ||
Self { | ||
cursor: 0, | ||
text: String::new(), | ||
last_event: CommandMachineEvent::Reset, | ||
} | ||
} | ||
|
||
pub fn step(mut self, key: Key) -> Self { | ||
match key { | ||
Key::Char('\n') => { | ||
self.text.clear(); | ||
Self { | ||
cursor: 0, | ||
text: self.text, | ||
last_event: CommandMachineEvent::Reset, | ||
} | ||
} | ||
Key::Ctrl('c') => { | ||
self.text.clear(); | ||
Self { | ||
cursor: 0, | ||
text: self.text, | ||
last_event: CommandMachineEvent::Reset, | ||
} | ||
} | ||
Key::Char(x) => { | ||
self.text.push(x); | ||
Self { | ||
cursor: self.cursor + 1, | ||
text: self.text, | ||
last_event: CommandMachineEvent::Update, | ||
} | ||
} | ||
Key::Backspace => if self.cursor > 0 { | ||
self.text.remove(self.cursor - 1); | ||
Self { | ||
cursor: self.cursor - 1, | ||
text: self.text, | ||
last_event: CommandMachineEvent::Update, | ||
} | ||
} else { | ||
Self { | ||
cursor: self.cursor, | ||
text: self.text, | ||
last_event: CommandMachineEvent::Update, | ||
} | ||
}, | ||
_ => panic!("oh no"), | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::CommandPrompt; | ||
use termion::event::Key; | ||
|
||
#[test] | ||
fn it_builds_text_based_on_input() { | ||
let command = CommandPrompt::new(); | ||
let command = command.step(Key::Char('h')); | ||
let command = command.step(Key::Char('e')); | ||
let command = command.step(Key::Char('l')); | ||
let command = command.step(Key::Char('l')); | ||
let command = command.step(Key::Char('o')); | ||
|
||
assert_eq!(command.text, "hello"); | ||
assert_eq!(command.cursor, 5); | ||
} | ||
|
||
#[test] | ||
fn it_keeps_track_of_cursor_position() { | ||
let command = CommandPrompt::new(); | ||
let command = command.step(Key::Char('h')); | ||
assert_eq!(command.cursor, 1); | ||
let command = command.step(Key::Char('i')); | ||
assert_eq!(command.cursor, 2); | ||
} | ||
|
||
#[test] | ||
fn it_deletes_text_when_backspacing() { | ||
let command = CommandPrompt::new(); | ||
let command = command.step(Key::Char('h')); | ||
let command = command.step(Key::Char('i')); | ||
|
||
let command = command.step(Key::Backspace); | ||
assert_eq!(command.cursor, 1); | ||
assert_eq!(command.text, "h"); | ||
|
||
let command = command.step(Key::Backspace); | ||
assert_eq!(command.cursor, 0); | ||
assert_eq!(command.text, ""); | ||
} | ||
|
||
#[test] | ||
fn it_does_nothing_when_backspacing_past_the_start_of_text() { | ||
let command = CommandPrompt::new(); | ||
let command = command.step(Key::Backspace); | ||
assert_eq!(command.cursor, 0); | ||
assert_eq!(command.text, ""); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters