feat: command mode command implementations

parts of #3
This commit is contained in:
Jeremy Wall 2024-11-21 15:45:22 -05:00
parent d7301246d8
commit e91c149619
5 changed files with 163 additions and 0 deletions

6
Cargo.lock generated
View File

@ -1399,6 +1399,7 @@ dependencies = [
"futures",
"ironcalc",
"ratatui",
"slice-cursor",
"thiserror",
"tui-prompts",
"tui-textarea",
@ -1455,6 +1456,11 @@ dependencies = [
"autocfg",
]
[[package]]
name = "slice-cursor"
version = "0.1.0"
source = "git+https://dev.zaphar.net/zaphar/slice-cursor-rs.git#562a78eb3f06ac2a9729af7aa211a070f8ed9c39"
[[package]]
name = "smallvec"
version = "1.13.2"

View File

@ -16,3 +16,4 @@ ratatui = "0.29.0"
thiserror = "1.0.65"
tui-textarea = "0.7.0"
tui-prompts = "0.5.0"
slice-cursor = { git = "https://dev.zaphar.net/zaphar/slice-cursor-rs.git" }

99
src/ui/cmd.rs Normal file
View File

@ -0,0 +1,99 @@
//! Command mode command parsers.
use slice_cursor::{Cursor, Seekable, Span, SpanRange, StrCursor};
#[derive(Debug, PartialEq, Eq)]
pub enum Cmd<'a> {
Write(Option<&'a str>),
InsertRow(usize),
InsertColumns(usize),
Edit(&'a str),
Help(Option<&'a str>),
}
pub fn parse<'cmd, 'i: 'cmd>(input: &'i str) -> Result<Option<Cmd<'cmd>>, &'static str> {
let cursor = StrCursor::new(input);
// try consume write command.
if let Some(cmd) = try_consume_write(cursor.clone()) {
return Ok(Some(cmd));
}
// try consume insert-row command.
if let Some(cmd) = try_consume_insert_row(cursor.clone())? {
return Ok(Some(cmd));
}
// try consume insert-col command.
if let Some(cmd) = try_consume_insert_column(cursor.clone()) {
return Ok(Some(cmd));
}
// try consume edit command.
if let Some(cmd) = try_consume_edit(cursor.clone()) {
return Ok(Some(cmd));
}
// try consume help command.
if let Some(cmd) = try_consume_help(cursor.clone()) {
return Ok(Some(cmd));
}
Ok(None)
}
const WRITE: &'static str = "write";
pub fn try_consume_write<'cmd, 'i: 'cmd>(mut input: StrCursor<'i>) -> Option<Cmd<'cmd>> {
let prefix_len = WRITE.len();
let full_length = dbg!(input.span(..).len());
let arg = if full_length >= prefix_len && input.span(..prefix_len) == WRITE {
input.seek(prefix_len);
// Should we check for whitespace?
input.span(prefix_len..)
} else if full_length >= 2 && input.span(..2) == "w " {
input.span(2..)
// Should we check for whitespace?
} else {
return None;
}
.trim();
return Some(Cmd::Write(if arg.is_empty() { None } else { Some(arg) }));
}
const IR: &'static str = "ir";
const INSERT_ROW: &'static str = "insert-row";
pub fn try_consume_insert_row<'cmd, 'i: 'cmd>(
mut input: StrCursor<'i>,
) -> Result<Option<Cmd<'cmd>>, &'static str> {
let prefix_len = INSERT_ROW.len();
let second_prefix_len = IR.len();
let full_length = input.span(..).len();
let arg =
if full_length >= prefix_len && input.span(..prefix_len) == INSERT_ROW {
input.seek(prefix_len);
// Should we check for whitespace?
input.span(prefix_len..)
} else if full_length >= second_prefix_len && input.span(..second_prefix_len) == IR {
input.span(second_prefix_len..)
// Should we check for whitespace?
} else {
return Ok(None);
}
.trim();
return Ok(Some(Cmd::InsertRow(if arg.is_empty() {
1
} else {
if let Ok(count) = arg.parse() {
count
} else {
return Err("You must pass in a non negative number for the row count");
}
})));
}
pub fn try_consume_insert_column<'cmd, 'i: 'cmd>(mut input: StrCursor<'i>) -> Option<Cmd<'cmd>> {
todo!("insert-column not yet implemented")
}
pub fn try_consume_edit<'cmd, 'i: 'cmd>(mut input: StrCursor<'i>) -> Option<Cmd<'cmd>> {
todo!("edit not yet implemented")
}
pub fn try_consume_help<'cmd, 'i: 'cmd>(mut input: StrCursor<'i>) -> Option<Cmd<'cmd>> {
todo!("help not yet implemented")
}

View File

@ -12,6 +12,10 @@ use ratatui::{
use tui_prompts::{State, Status, TextPrompt, TextState};
use tui_textarea::{CursorMove, TextArea};
mod cmd;
#[cfg(test)]
mod test;
#[derive(Default, Debug, PartialEq)]
pub enum Modality {
#[default]
@ -197,6 +201,14 @@ impl<'ws> Workspace<'ws> {
if cmd.is_empty() {
return Ok(true);
}
match cmd.as_str() {
"w" | "write" => {
self.save_file()?;
},
_ => {
// noop?
}
}
Ok(false)
}

45
src/ui/test.rs Normal file
View File

@ -0,0 +1,45 @@
use super::cmd::{parse, Cmd};
#[test]
fn test_write_cmd() {
let input = "write foo.xlsx";
let result = parse(input);
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.is_some());
let cmd = output.unwrap();
assert_eq!(cmd, Cmd::Write(Some("foo.xlsx")));
}
#[test]
fn test_short_write_cmd() {
let input = "w foo.xlsx";
let result = parse(input);
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.is_some());
let cmd = output.unwrap();
assert_eq!(cmd, Cmd::Write(Some("foo.xlsx")));
}
#[test]
fn test_insert_row_cmd() {
let input = "insert-row 1";
let result = parse(input);
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.is_some());
let cmd = output.unwrap();
assert_eq!(cmd, Cmd::InsertRow(1));
}
#[test]
fn test_insert_row_cmd_short() {
let input = "ir 1";
let result = parse(input);
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.is_some());
let cmd = output.unwrap();
assert_eq!(cmd, Cmd::InsertRow(1));
}