diff options
author | Jiayi Zhao <jeff.no.zhao@gmail.com> | 2020-05-13 09:31:56 -0400 |
---|---|---|
committer | Jiayi Zhao <jeff.no.zhao@gmail.com> | 2020-05-13 09:31:56 -0400 |
commit | 95bc120687723a8c9e525b7448c5fd71235a329e (patch) | |
tree | 84a1e9fe847b366fa1beff8afaa7cba8865bac1c /src/commands | |
parent | ddb0669bbf0b5bb9e039094e58e69a5a81f13494 (diff) |
rework command parsing and add shell command
- This change will break compatibility with previous keymap.toml configs
- 'shell' command allows users to run a shell command inside joshuto
Diffstat (limited to 'src/commands')
-rw-r--r-- | src/commands/command_line.rs | 13 | ||||
-rw-r--r-- | src/commands/mod.rs | 210 | ||||
-rw-r--r-- | src/commands/new_directory.rs | 10 | ||||
-rw-r--r-- | src/commands/shell.rs | 44 | ||||
-rw-r--r-- | src/commands/sort.rs | 33 |
5 files changed, 153 insertions, 157 deletions
diff --git a/src/commands/command_line.rs b/src/commands/command_line.rs index 0714ed2..577d988 100644 --- a/src/commands/command_line.rs +++ b/src/commands/command_line.rs @@ -31,17 +31,8 @@ impl CommandLine { if let Some(s) = user_input { let trimmed = s.trim_start(); - match trimmed.find(' ') { - Some(ind) => { - let (cmd, xs) = trimmed.split_at(ind); - let xs = xs.trim_start(); - let args: Vec<String> = vec![String::from(xs)]; - let command = commands::from_args(cmd.to_string(), args)?; - command.execute(context, backend) - } - None => commands::from_args(String::from(trimmed), Vec::new())? - .execute(context, backend), - } + let command = commands::parse_command(trimmed)?; + command.execute(context, backend) } else { Ok(()) } diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 953da26..f276d8d 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -13,6 +13,7 @@ mod rename_file; mod search; mod selection; mod set_mode; +mod shell; mod show_hidden; mod sort; mod tab_operations; @@ -37,8 +38,9 @@ pub use self::rename_file::{RenameFile, RenameFileAppend, RenameFilePrepend}; pub use self::search::{Search, SearchNext, SearchPrev}; pub use self::selection::SelectFiles; pub use self::set_mode::SetMode; +pub use self::shell::ShellCommand; pub use self::show_hidden::ToggleHiddenFiles; -pub use self::sort::Sort; +pub use self::sort::{Sort,SortReverse}; pub use self::tab_operations::{CloseTab, NewTab}; pub use self::tab_switch::TabSwitch; @@ -74,87 +76,61 @@ pub trait JoshutoRunnable { pub trait JoshutoCommand: JoshutoRunnable + std::fmt::Display + std::fmt::Debug {} -pub fn from_args(command: String, args: Vec<String>) -> JoshutoResult<Box<dyn JoshutoCommand>> { - match command.as_str() { +pub fn parse_command(s: &str) -> JoshutoResult<Box<dyn JoshutoCommand>> { + let (command, arg) = match s.find(' ') { + Some(i) => (&s[..i], s[i+1..].trim_start()), + None => (s, ""), + }; + + match command { "bulk_rename" => Ok(Box::new(self::BulkRename::new())), - "cd" => match args.len() { - 0 => match HOME_DIR.as_ref() { + "cd" => match arg { + "" => match HOME_DIR.as_ref() { Some(s) => Ok(Box::new(self::ChangeDirectory::new(s.clone()))), None => Err(JoshutoError::new( JoshutoErrorKind::EnvVarNotPresent, format!("{}: Cannot find home directory", command), )), }, - 1 => match args[0].as_str() { - ".." => Ok(Box::new(self::ParentDirectory::new())), - arg => Ok(Box::new(self::ChangeDirectory::new(PathBuf::from(arg)))), - }, - i => Err(JoshutoError::new( - JoshutoErrorKind::IOInvalidData, - format!("{}: Expected 1 argument, got {}", command, i), - )), - }, + ".." => Ok(Box::new(self::ParentDirectory::new())), + arg => Ok(Box::new(self::ChangeDirectory::new(PathBuf::from(arg)))), + } "close_tab" => Ok(Box::new(self::CloseTab::new())), "copy_files" => Ok(Box::new(self::CopyFiles::new())), - "console" => match args.len() { - 0 => Ok(Box::new(self::CommandLine::new( - "".to_string(), - "".to_string(), - ))), - 1 => Ok(Box::new(self::CommandLine::new( - args[0].clone(), - "".to_string(), - ))), - i => Err(JoshutoError::new( - JoshutoErrorKind::IOInvalidData, - format!("{}: Expected 0 or 2 arguments, got {}", command, i), - )), - }, - "cursor_move_down" => match args.len() { - 0 => Ok(Box::new(self::CursorMoveDown::new(1))), - 1 => match args[0].parse::<usize>() { + "console" => Ok(Box::new(self::CommandLine::new(arg.to_owned(), "".to_owned()))), + "cursor_move_home" => Ok(Box::new(self::CursorMoveHome::new())), + "cursor_move_end" => Ok(Box::new(self::CursorMoveEnd::new())), + "cursor_move_page_up" => Ok(Box::new(self::CursorMovePageUp::new())), + "cursor_move_page_down" => Ok(Box::new(self::CursorMovePageDown::new())), + "cursor_move_down" => match arg { + "" => Ok(Box::new(self::CursorMoveDown::new(1))), + arg => match arg.parse::<usize>() { Ok(s) => Ok(Box::new(self::CursorMoveDown::new(s))), Err(e) => Err(JoshutoError::new( JoshutoErrorKind::ParseError, e.to_string(), )), }, - i => Err(JoshutoError::new( - JoshutoErrorKind::IOInvalidData, - format!("{}: Expected 0 or 1 arguments, got {}", command, i), - )), - }, - "cursor_move_up" => match args.len() { - 0 => Ok(Box::new(self::CursorMoveUp::new(1))), - 1 => match args[0].parse::<usize>() { + } + "cursor_move_up" => match arg { + "" => Ok(Box::new(self::CursorMoveUp::new(1))), + arg => match arg.parse::<usize>() { Ok(s) => Ok(Box::new(self::CursorMoveUp::new(s))), Err(e) => Err(JoshutoError::new( JoshutoErrorKind::ParseError, - format!("{}: {}", command, e.to_string()), + e.to_string(), )), }, - i => Err(JoshutoError::new( - JoshutoErrorKind::IOInvalidData, - format!("{}: Expected 0 or 1 arguments, got {}", command, i), - )), - }, - "cursor_move_home" => Ok(Box::new(self::CursorMoveHome::new())), - "cursor_move_end" => Ok(Box::new(self::CursorMoveEnd::new())), - "cursor_move_page_up" => Ok(Box::new(self::CursorMovePageUp::new())), - "cursor_move_page_down" => Ok(Box::new(self::CursorMovePageDown::new())), + } "cut_files" => Ok(Box::new(self::CutFiles::new())), "delete_files" => Ok(Box::new(self::DeleteFiles::new())), "force_quit" => Ok(Box::new(self::ForceQuit::new())), - "mkdir" => { - if args.is_empty() { - Err(JoshutoError::new( - JoshutoErrorKind::IOInvalidData, - format!("{}: missing additional parameter", command), - )) - } else { - let paths: Vec<PathBuf> = args.iter().map(PathBuf::from).collect(); - Ok(Box::new(self::NewDirectory::new(paths))) - } + "mkdir" => match arg { + "" => Err(JoshutoError::new( + JoshutoErrorKind::IOInvalidData, + format!("{}: missing additional parameter", command), + )), + arg => Ok(Box::new(self::NewDirectory::new(PathBuf::from(arg)))), } "new_tab" => Ok(Box::new(self::NewTab::new())), @@ -162,8 +138,8 @@ pub fn from_args(command: String, args: Vec<String>) -> JoshutoResult<Box<dyn Jo "open_file_with" => Ok(Box::new(self::OpenFileWith::new())), "paste_files" => { let mut options = Options::default(); - for arg in args { - match arg.as_str() { + for arg in arg.split_whitespace() { + match arg { "--overwrite" => options.overwrite = true, "--skip_exist" => options.skip_exist = true, _ => { @@ -178,32 +154,32 @@ pub fn from_args(command: String, args: Vec<String>) -> JoshutoResult<Box<dyn Jo } "quit" => Ok(Box::new(self::Quit::new())), "reload_dir_list" => Ok(Box::new(self::ReloadDirList::new())), - "rename" => match args.len() { - 1 => { - let path: PathBuf = PathBuf::from(args[0].as_str()); - Ok(Box::new(self::RenameFile::new(path))) - } - i => Err(JoshutoError::new( + "rename" => match arg { + "" => Err(JoshutoError::new( JoshutoErrorKind::IOInvalidData, - format!("rename_file: Expected 1, got {}", i), + format!("rename_file: Expected 1, got 0"), )), + arg => { + let path: PathBuf = PathBuf::from(arg); + Ok(Box::new(self::RenameFile::new(path))) + } }, "rename_append" => Ok(Box::new(self::RenameFileAppend::new())), "rename_prepend" => Ok(Box::new(self::RenameFilePrepend::new())), - "search" => match args.len() { - 1 => Ok(Box::new(self::Search::new(args[0].as_str()))), - i => Err(JoshutoError::new( + "search" => match arg { + "" => Err(JoshutoError::new( JoshutoErrorKind::IOInvalidData, - format!("{}: Expected 1, got {}", command, i), + format!("{}: Expected 1, got 0", command), )), + arg => Ok(Box::new(self::Search::new(arg))), }, "search_next" => Ok(Box::new(self::SearchNext::new())), "search_prev" => Ok(Box::new(self::SearchPrev::new())), "select_files" => { let mut toggle = false; let mut all = false; - for arg in args { - match arg.as_str() { + for arg in arg.split_whitespace() { + match arg { "--toggle" => toggle = true, "--all" => all = true, _ => { @@ -217,38 +193,28 @@ pub fn from_args(command: String, args: Vec<String>) -> JoshutoResult<Box<dyn Jo Ok(Box::new(self::SelectFiles::new(toggle, all))) } "set_mode" => Ok(Box::new(self::SetMode::new())), - "sort" => { - if args.len() == 1 { - match args[0].as_str() { - "lexical" => Ok(Box::new(self::Sort::new(SortType::Lexical))), - "mtime" => Ok(Box::new(self::Sort::new(SortType::Mtime))), - "natural" => Ok(Box::new(self::Sort::new(SortType::Natural))), - a => Err(JoshutoError::new( - JoshutoErrorKind::IOInvalidData, - format!("sort: Unknown option {}", a), - )), - } - } else { - Err(JoshutoError::new( + "shell" => Ok(Box::new(self::ShellCommand::new(arg.to_owned()))), + "sort" => match arg { + "reverse" => Ok(Box::new(self::SortReverse::new())), + arg => match SortType::parse(arg) { + Some(s) => Ok(Box::new(self::Sort::new(s))), + None => Err(JoshutoError::new( JoshutoErrorKind::IOInvalidData, - format!("sort: Expected 1, got {}", args.len()), - )) - } - } - "tab_switch" => { - if args.len() == 1 { - match args[0].parse::<i32>() { - Ok(s) => Ok(Box::new(self::TabSwitch::new(s))), - Err(e) => Err(JoshutoError::new( - JoshutoErrorKind::IOInvalidData, - format!("{}: {}", command, e.to_string()), - )), - } - } else { - Err(JoshutoError::new( + format!("sort: Unknown option {}", arg), + )), + }, + }, + "tab_switch" => match arg { + "" => Err(JoshutoError::new( + JoshutoErrorKind::IOInvalidData, + format!("{}: {}", command, "No option provided"), + )), + arg => match arg.parse::<i32>() { + Ok(s) => Ok(Box::new(self::TabSwitch::new(s))), + Err(e) => Err(JoshutoError::new( JoshutoErrorKind::IOInvalidData, - format!("{}: {}", command, "No option provided"), - )) + format!("{}: {}", command, e.to_string()), + )), } } "toggle_hidden" => Ok(Box::new(self::ToggleHiddenFiles::new())), @@ -258,39 +224,3 @@ pub fn from_args(command: String, args: Vec<String>) -> JoshutoResult<Box<dyn Jo )), } } - -/* -pub fn split_shell_style(line: &str) -> Vec<&str> { - let mut args: Vec<&str> = Vec::new(); - let mut char_ind = line.char_indices(); - - while let Some((i, ch)) = char_ind.next() { - if ch.is_whitespace() { - continue; - } - if ch == '\'' { - while let Some((j, ch)) = char_ind.next() { - if ch == '\'' { - args.push(&line[i + 1..j]); - break; - } - } - } else if ch == '"' { - while let Some((j, ch)) = char_ind.next() { - if ch == '"' { - args.push(&line[i + 1..j]); - break; - } - } - } else { - while let Some((j, ch)) = char_ind.next() { - if ch.is_whitespace() { - args.push(&line[i..j]); - break; - } - } - } - } - args -} -*/ diff --git a/src/commands/new_directory.rs b/src/commands/new_directory.rs index 5152374..f1ce5e4 100644 --- a/src/commands/new_directory.rs +++ b/src/commands/new_directory.rs @@ -9,12 +9,12 @@ use crate::util::load_child::LoadChild; #[derive(Clone, Debug)] pub struct NewDirectory { - paths: Vec<path::PathBuf>, + path: path::PathBuf, } impl NewDirectory { - pub fn new(paths: Vec<path::PathBuf>) -> Self { - NewDirectory { paths } + pub fn new(path: path::PathBuf) -> Self { + NewDirectory { path } } pub const fn command() -> &'static str { "mkdir" @@ -31,9 +31,7 @@ impl std::fmt::Display for NewDirectory { impl JoshutoRunnable for NewDirectory { fn execute(&self, context: &mut JoshutoContext, _: &mut TuiBackend) -> JoshutoResult<()> { - for path in &self.paths { - std::fs::create_dir_all(path)?; - } + std::fs::create_dir_all(&self.path)?; let options = &context.config_t.sort_option; let curr_path = context.tabs[context.curr_tab_index].curr_path.clone(); diff --git a/src/commands/shell.rs b/src/commands/shell.rs new file mode 100644 index 0000000..19e901d --- /dev/null +++ b/src/commands/shell.rs @@ -0,0 +1,44 @@ +use std::process; + +use crate::commands::{self, JoshutoCommand, JoshutoRunnable}; +use crate::context::JoshutoContext; +use crate::error::JoshutoResult; +use crate::ui::widgets::TuiTextField; +use crate::ui::TuiBackend; + +#[derive(Clone, Debug)] +pub struct ShellCommand { + pub command: String, +} + +impl ShellCommand { + pub fn new(command: String) -> Self { + Self { command } + } + pub const fn command() -> &'static str { + "console" + } + + pub fn shell_command(command: &str) -> std::io::Result<()> { + let mut command = process::Command::new("sh").arg("-c").arg(command).spawn()?; + Ok(()) + } +} + +impl JoshutoCommand for ShellCommand {} + +impl std::fmt::Display for ShellCommand { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}: sh -c '{}'", Self::command(), self.command) + } +} + +impl JoshutoRunnable for ShellCommand { + fn execute(&self, context: &mut JoshutoContext, backend: &mut TuiBackend) -> JoshutoResult<()> { + backend.terminal_drop(); + let res = Self::shell_command(self.command.as_str()); + backend.terminal_restore()?; + res?; + Ok(()) + } +} diff --git a/src/commands/sort.rs b/src/commands/sort.rs index 7209cb4..93882f6 100644 --- a/src/commands/sort.rs +++ b/src/commands/sort.rs @@ -40,3 +40,36 @@ impl JoshutoRunnable for Sort { Ok(()) } } + +#[derive(Clone, Debug)] +pub struct SortReverse; + +impl SortReverse { + pub const fn new() -> Self { + Self {} + } + pub const fn command() -> &'static str { + "sort" + } +} + +impl JoshutoCommand for SortReverse {} + +impl std::fmt::Display for SortReverse { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{} reverse", Self::command()) + } +} + +impl JoshutoRunnable for SortReverse { + fn execute(&self, context: &mut JoshutoContext, _: &mut TuiBackend) -> JoshutoResult<()> { + context.config_t.sort_option.reverse = !context.config_t.sort_option.reverse; + for tab in context.tabs.iter_mut() { + tab.history.depreciate_all_entries(); + } + ReloadDirList::soft_reload(context.curr_tab_index, context)?; + LoadChild::load_child(context)?; + Ok(()) + } +} + |