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

Chained cmds #126

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
163 changes: 163 additions & 0 deletions src/iota/chained_command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use std::str;
use textobject::{Offset, Kind, Anchor};
use command::Command;

// Current chainable commands
pub enum ChainableCmds {
Quit,
Write,
}
// Holds our chained command structure
pub struct ChainedCmdBuilder {
quit: bool,
save: bool,
line_j: Option<usize>,
}
// Parses our input
pub struct ChainedCmdParser<'a> {
buffer: &'a [u8],
len: usize,
pos: usize,
is_arg: bool,
}

// A safe version of memcmp. May not be necessary, but there
// was some performance issues with built in solution in the
// past
fn memeq<'a, T: PartialEq>(a: &'a [T], b: &'a [T]) -> bool {
if a.len() != b.len() {
return false;
}

for i in 0..a.len() {
if a[i] != b[i] {
return false;
}
}

true
}

impl ChainedCmdBuilder {
pub fn new() -> ChainedCmdBuilder {
ChainedCmdBuilder {
quit: false,
save: false,
line_j: None,
}
}
// Parses a chainable command
pub fn parse(&mut self, cmd: &str) -> Vec<Command> {
let mut parser = ChainedCmdParser::new(cmd);

while parser.pos <= parser.len {
match parser.buffer[parser.pos] {
b'q' => {
// our command is quit
self.quit = true;
if parser.pos < parser.len {
parser.cmd(ChainableCmds::Quit);
}
}
b'w' => {
// Our command is write
self.save = true;
if parser.pos < parser.len {
parser.cmd(ChainableCmds::Write);
}
}
b'1' | b'2' | b'3' | b'4' | b'5' | b'6' | b'7' | b'8' | b'9' | b'0' => {
let line_j: usize = parser.arg().parse().ok().expect("Need usize");
self.line_j = Some(line_j); // This should always be set
}
b' ' => {
// Arg is coming, this will be useful in the future
parser.is_arg = true;
}
_ => {
continue;
}
}

parser.is_arg = false;
parser.pos += 1;
}

self.build_commands()
}

// Builds our final vec of commands
fn build_commands(&mut self) -> Vec<Command> {
let mut res = vec![];

if self.line_j.is_some() {
res.push(Command::movement(Offset::Absolute(self.line_j.unwrap()),
Kind::Line(Anchor::Start)));
}
if self.save {
res.push(Command::save_buffer());
}
if self.quit {
res.push(Command::exit_editor());
}
if res.is_empty() {
res.push(Command::noop());
}

return res;
}
}

impl<'a> ChainedCmdParser<'a> {
pub fn new(cmd: &'a str) -> ChainedCmdParser<'a> {
ChainedCmdParser {
buffer: cmd.as_bytes(),
len: cmd.len() - 1,
pos: 0,
is_arg: false,
}
}

// This is a little hokey, but basically checks if
// our command is long and moves our position
fn cmd(&mut self, cmd: ChainableCmds) {
match cmd {
ChainableCmds::Quit => {
let l_cmd = "quit".as_bytes();
match self.len >= l_cmd.len() {
true => {
match memeq(&self.buffer[self.pos..l_cmd.len()], &l_cmd) {
true => self.pos += l_cmd.len() - 1,
false => return,
}
}
false => return,
}
}
ChainableCmds::Write => {
let l_cmd = "write".as_bytes();
match self.len >= l_cmd.len() {
true => {
match memeq(&self.buffer[self.pos..l_cmd.len()], &l_cmd) {
true => self.pos += l_cmd.len() - 1,
false => return,
}
}
false => return,
}
}
}
}

// Parses an arg from a chainable command
fn arg(&mut self) -> &str {
let start = self.pos;
while self.buffer[self.pos] != b' ' {
if self.pos + 1 > self.len {
break;
}
self.pos += 1;
}
str::from_utf8(&self.buffer[start..self.pos + 1]).unwrap_or("")
}
}
56 changes: 32 additions & 24 deletions src/iota/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use overlay::{Overlay, OverlayEvent};
use buffer::Buffer;
use command::Command;
use command::{Action, BuilderEvent, Operation, Instruction};

use chained_command::ChainedCmdBuilder;

/// The main Editor structure
///
Expand Down Expand Up @@ -72,20 +72,27 @@ impl<'e, T: Frontend> Editor<'e, T> {
};

let mut remove_overlay = false;
let command = match self.view.overlay {
Overlay::None => self.mode.handle_key_event(key),
let mut command = BuilderEvent::Incomplete;

match self.view.overlay {
Overlay::None => { command = self.mode.handle_key_event(key) },
_ => {
let event = self.view.overlay.handle_key_event(key);
match event {
OverlayEvent::Finished(response) => {
remove_overlay = true;
self.handle_overlay_response(response)
let commands = self.handle_overlay_response(response);
for cmd in commands {
if let BuilderEvent::Complete(c) = cmd {
self.handle_command(c);
}
}
}

_ => { BuilderEvent::Incomplete }
_ => { command = BuilderEvent::Incomplete }
}
}
};
}

if remove_overlay {
self.view.overlay = Overlay::None;
Expand All @@ -102,34 +109,33 @@ impl<'e, T: Frontend> Editor<'e, T> {
/// In most cases, we will just want to convert the response directly to
/// a Command, however in some cases we will want to perform other actions
/// first, such as in the case of Overlay::SavePrompt.
fn handle_overlay_response(&mut self, response: Option<String>) -> BuilderEvent {
// FIXME: This entire method neext to be updated
fn handle_overlay_response(&mut self, response: Option<String>) -> Vec<BuilderEvent> {
let mut commands = vec![];

match response {
Some(data) => {
match self.view.overlay {

// FIXME: this is just a temporary fix
Overlay::Prompt { ref data, .. } => {
match &**data {
// FIXME: need to find a better system for these commands
// They should be chainable
// ie: wq - save & quit
// They should also take arguments
// ie w file.txt - write buffer to file.txt
"q" | "quit" => BuilderEvent::Complete(Command::exit_editor()),
"w" | "write" => BuilderEvent::Complete(Command::save_buffer()),

_ => BuilderEvent::Incomplete
let mut builder = ChainedCmdBuilder::new();
let cmds: Vec<Command> = builder.parse(&**data);

if cmds.len() < 1 {
commands.push(BuilderEvent::Incomplete);
} else {
for cmd in cmds {
commands.push(BuilderEvent::Complete(cmd));
}
}
}

Overlay::SavePrompt { .. } => {
if data.is_empty() {
BuilderEvent::Invalid
commands.push(BuilderEvent::Invalid);
} else {
let path = PathBuf::from(&*data);
self.view.buffer.lock().unwrap().file_path = Some(path);
BuilderEvent::Complete(Command::save_buffer())
commands.push(BuilderEvent::Complete(Command::save_buffer()));
}
}

Expand All @@ -139,14 +145,16 @@ impl<'e, T: Frontend> Editor<'e, T> {
self.buffers.push(buffer.clone());
self.view.set_buffer(buffer.clone());
self.view.clear(&mut self.frontend);
BuilderEvent::Complete(Command::noop())
commands.push(BuilderEvent::Complete(Command::noop()));
}

_ => BuilderEvent::Incomplete,
_ => commands.push(BuilderEvent::Incomplete),
}
}
None => BuilderEvent::Incomplete
None => commands.push(BuilderEvent::Incomplete)
}

commands
}

/// Handle resize events
Expand Down
1 change: 1 addition & 0 deletions src/iota/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ mod overlay;
mod command;
mod textobject;
mod iterators;
mod chained_command;

#[cfg(feature="syntax-highlighting")]
mod syntax;