Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modularization of the main struct #134

Merged
merged 21 commits into from
Aug 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use reedline::{DefaultPrompt, Reedline, Signal};

fn main() {
let mut line_editor = Reedline::new();
let mut line_editor = Reedline::create()?;
let prompt = DefaultPrompt::default();

loop {
Expand Down Expand Up @@ -57,7 +57,7 @@ keybindings.add_binding(
vec![EditCommand::BackspaceWord],
);

let mut line_editor = Reedline::new().with_keybindings(keybindings);
let mut line_editor = Reedline::create()?.with_keybindings(keybindings);
```

## Integrate with custom History
Expand All @@ -71,7 +71,7 @@ let history = Box::new(
FileBackedHistory::with_file(5, "history.txt".into())
.expect("Error configuring history with file"),
);
let mut line_editor = Reedline::new()
let mut line_editor = Reedline::create()?
.with_history(history)
.expect("Error configuring reedline with history");
```
Expand All @@ -90,7 +90,7 @@ let commands = vec![
"this is the reedline crate".into(),
];
let mut line_editor =
Reedline::new().with_highlighter(Box::new(DefaultHighlighter::new(commands)));
Reedline::create()?.with_highlighter(Box::new(DefaultHighlighter::new(commands)));
```

## Integrate with custom Tab-Handler
Expand All @@ -108,7 +108,7 @@ let commands = vec![
];
let completer = Box::new(DefaultCompleter::new_with_wordlen(commands.clone(), 2));

let mut line_editor = Reedline::new().with_completion_action_handler(Box::new(
let mut line_editor = Reedline::create()?.with_completion_action_handler(Box::new(
DefaultCompletionActionHandler::default().with_completer(completer),
));
```
Expand All @@ -135,7 +135,7 @@ let commands = vec![
];
let completer = Box::new(DefaultCompleter::new_with_wordlen(commands.clone(), 2));

let mut line_editor = Reedline::new().with_hinter(Box::new(
let mut line_editor = Reedline::create()?.with_hinter(Box::new(
DefaultHinter::default()
.with_completer(completer) // or .with_history()
// .with_inside_line()
Expand All @@ -150,7 +150,7 @@ let mut line_editor = Reedline::new().with_hinter(Box::new(

use reedline::{EditMode, Reedline};

let mut line_editor = Reedline::new().with_edit_mode(
let mut line_editor = Reedline::create()?.with_edit_mode(
EditMode::ViNormal, // or EditMode::Emacs or EditMode::ViInsert
);
```
Expand Down
15 changes: 15 additions & 0 deletions src/edit_mode/base.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use crossterm::event::Event;

use crate::{enums::ReedlineEvent, PromptEditMode};

/// Define the style of parsing for the edit events
/// Available default options:
/// - Emacs
/// - Vi
pub trait EditMode {
/// Translate the given user input event into what the LineEditor understands
fn parse_event(&mut self, event: Event) -> ReedlineEvent;

/// What to display in the prompt indicator
fn edit_mode(&self) -> PromptEditMode;
}
61 changes: 61 additions & 0 deletions src/edit_mode/emacs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};

use crate::{
default_emacs_keybindings,
enums::{EditCommand, ReedlineEvent},
PromptEditMode,
};

use super::{keybindings::Keybindings, EditMode};

/// This parses the incoming Events like a emacs style-editor
pub struct Emacs {
keybindings: Keybindings,
}

impl Default for Emacs {
fn default() -> Self {
Emacs {
keybindings: default_emacs_keybindings(),
}
}
}

impl EditMode for Emacs {
fn parse_event(&mut self, event: Event) -> ReedlineEvent {
match event {
Event::Key(KeyEvent { code, modifiers }) => match (modifiers, code) {
(KeyModifiers::NONE, KeyCode::Tab) => ReedlineEvent::HandleTab,
(KeyModifiers::CONTROL, KeyCode::Char('d')) => ReedlineEvent::CtrlD,
(KeyModifiers::CONTROL, KeyCode::Char('c')) => ReedlineEvent::CtrlC,
(KeyModifiers::CONTROL, KeyCode::Char('l')) => ReedlineEvent::ClearScreen,
(KeyModifiers::NONE, KeyCode::Char(c))
| (KeyModifiers::SHIFT, KeyCode::Char(c)) => {
ReedlineEvent::EditInsert(EditCommand::InsertChar(c))
}
(KeyModifiers::NONE, KeyCode::Enter) => ReedlineEvent::Enter,
_ => {
if let Some(binding) = self.keybindings.find_binding(modifiers, code) {
ReedlineEvent::Edit(binding)
} else {
ReedlineEvent::Edit(vec![])
}
}
},

Event::Mouse(_) => ReedlineEvent::Mouse,
Event::Resize(width, height) => ReedlineEvent::Resize(width, height),
}
}

fn edit_mode(&self) -> PromptEditMode {
PromptEditMode::Emacs
}
}

impl Emacs {
/// Emacs style input parsing constructer if you want to use custom keybindings
pub fn new(keybindings: Keybindings) -> Self {
Emacs { keybindings }
}
}
45 changes: 0 additions & 45 deletions src/keybindings.rs → src/edit_mode/keybindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,51 +55,6 @@ impl Keybindings {
}
}

pub fn default_vi_normal_keybindings() -> Keybindings {
use KeyCode::*;

let mut keybindings = Keybindings::new();

keybindings.add_binding(
KeyModifiers::NONE,
Up,
vec![EditCommand::ViCommandFragment('k')],
);
keybindings.add_binding(
KeyModifiers::NONE,
Down,
vec![EditCommand::ViCommandFragment('j')],
);
keybindings.add_binding(
KeyModifiers::NONE,
Left,
vec![EditCommand::ViCommandFragment('h')],
);
keybindings.add_binding(
KeyModifiers::NONE,
Right,
vec![EditCommand::ViCommandFragment('l')],
);

keybindings
}

pub fn default_vi_insert_keybindings() -> Keybindings {
use KeyCode::*;

let mut keybindings = Keybindings::new();

keybindings.add_binding(KeyModifiers::NONE, Esc, vec![EditCommand::EnterViNormal]);
keybindings.add_binding(KeyModifiers::NONE, Up, vec![EditCommand::PreviousHistory]);
keybindings.add_binding(KeyModifiers::NONE, Down, vec![EditCommand::NextHistory]);
keybindings.add_binding(KeyModifiers::NONE, Left, vec![EditCommand::MoveLeft]);
keybindings.add_binding(KeyModifiers::NONE, Right, vec![EditCommand::MoveRight]);
keybindings.add_binding(KeyModifiers::NONE, Backspace, vec![EditCommand::Backspace]);
keybindings.add_binding(KeyModifiers::NONE, Delete, vec![EditCommand::Delete]);

keybindings
}

/// Returns the current default emacs keybindings
pub fn default_emacs_keybindings() -> Keybindings {
use KeyCode::*;
Expand Down
9 changes: 9 additions & 0 deletions src/edit_mode/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
mod base;
mod emacs;
mod keybindings;
mod vi;

pub use base::EditMode;
pub use emacs::Emacs;
pub use keybindings::{default_emacs_keybindings, Keybindings};
pub use vi::Vi;
127 changes: 127 additions & 0 deletions src/edit_mode/vi/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};

use crate::{
default_emacs_keybindings,
enums::{EditCommand, ReedlineEvent},
PromptEditMode, PromptViMode,
};

use super::{keybindings::Keybindings, EditMode};

#[derive(Debug, PartialEq, Eq)]
enum Mode {
Normal,
Insert,
}

/// This parses incomming input `Event`s like a Vi-Style editor
pub struct Vi {
partial: Option<String>,
keybindings: Keybindings,
mode: Mode,
}

impl Default for Vi {
fn default() -> Self {
Vi {
// FIXME: Setup proper keybinds
keybindings: default_emacs_keybindings(),
partial: None,
mode: Mode::Normal,
}
}
}

impl EditMode for Vi {
fn parse_event(&mut self, event: Event) -> ReedlineEvent {
match event {
Event::Key(KeyEvent { code, modifiers }) => match (modifiers, code) {
(KeyModifiers::NONE, KeyCode::Tab) => ReedlineEvent::HandleTab,
(KeyModifiers::CONTROL, KeyCode::Char('c')) => ReedlineEvent::CtrlC,
(KeyModifiers::NONE, KeyCode::Esc) => {
self.mode = Mode::Normal;
ReedlineEvent::Repaint
}
(KeyModifiers::NONE, KeyCode::Char(c))
| (KeyModifiers::SHIFT, KeyCode::Char(c)) => {
if self.mode == Mode::Normal {
self.parse_vi_fragment(c)
} else {
ReedlineEvent::EditInsert(EditCommand::InsertChar(c))
}
}
(KeyModifiers::NONE, KeyCode::Enter) => ReedlineEvent::Enter,
_ => {
if let Some(binding) = self.keybindings.find_binding(modifiers, code) {
ReedlineEvent::Edit(binding)
} else {
ReedlineEvent::Edit(vec![])
}
}
},

Event::Mouse(_) => ReedlineEvent::Mouse,
Event::Resize(width, height) => ReedlineEvent::Resize(width, height),
}
}

fn edit_mode(&self) -> PromptEditMode {
match self.mode {
Mode::Normal => PromptEditMode::Vi(PromptViMode::Normal),
Mode::Insert => PromptEditMode::Vi(PromptViMode::Insert),
}
}
}

impl Vi {
fn parse_vi_fragment(&mut self, fragment: char) -> ReedlineEvent {
let mut output = vec![];

let partial = self.partial.clone();

match (partial, fragment) {
(None, c) => match c {
'd' => self.partial = Some("d".to_string()),
'p' => {
output.push(EditCommand::PasteCutBuffer);
}
'h' => {
output.push(EditCommand::MoveLeft);
}
'l' => {
output.push(EditCommand::MoveRight);
}
'j' => {
output.push(EditCommand::PreviousHistory);
}
'k' => {
output.push(EditCommand::NextHistory);
}
'i' => {
// NOTE: Ability to handle this with multiple events
// Best to target this once the ViParser is in fully working state
self.mode = Mode::Insert;
return ReedlineEvent::Repaint;
}
_ => {}
},
(Some(partial), c) => {
if partial == "d" {
match c {
'd' => {
output.push(EditCommand::MoveToStart);
output.push(EditCommand::CutToEnd);
}
'w' => {
output.push(EditCommand::CutWordRight);
}
_ => {}
}
}
self.partial = None;
}
};

ReedlineEvent::Edit(output)
}
}
2 changes: 1 addition & 1 deletion src/vi_parser.rs → src/edit_mode/vi/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ fn main() {
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::{assert_eq, assert_ne};
use pretty_assertions::assert_eq;

#[test]
fn test_delete_word() {
Expand Down
Loading