diff options
author | qkzk <qu3nt1n@gmail.com> | 2023-12-03 18:22:46 +0100 |
---|---|---|
committer | qkzk <qu3nt1n@gmail.com> | 2023-12-03 18:22:46 +0100 |
commit | d4b5e46118154f80daf178d07f873b6a43ed01a1 (patch) | |
tree | 37ede46c7fe745c6cf32ca8a56686945e44ae1d9 | |
parent | 1d0c6555437a2a1eb90902520616d4ee3f75b780 (diff) |
WIP merge specific commands into cli info
-rw-r--r-- | development.md | 6 | ||||
-rw-r--r-- | src/common/constant_strings_paths.rs | 13 | ||||
-rw-r--r-- | src/io/display.rs | 15 | ||||
-rw-r--r-- | src/modes/edit/cli_menu.rs | 106 | ||||
-rw-r--r-- | src/modes/edit/leave_mode.rs | 2 |
5 files changed, 103 insertions, 39 deletions
diff --git a/development.md b/development.md index 4dd7dd9..053d44f 100644 --- a/development.md +++ b/development.md @@ -675,9 +675,13 @@ New view: Tree ! Toggle with 't', fold with 'z'. Navigate normally. - [x] use rc instead of owned types in fileinfo - [x] renamed path_content to Directory - [x] renamed Display::Normal to Display::Directory -- [ ] WIP: event_dispatch refactor +- [x] event_dispatch refactor - [ ] refactor file opening. It's a mess - WIP - [ ] opening a text file should try to open in current neovim (???) +- [ ] merge specific commands & cli_info + - [x] merge + - [ ] remove specific commands + - [ ] ensure diff only use first 2 files (???) ## TODO diff --git a/src/common/constant_strings_paths.rs b/src/common/constant_strings_paths.rs index 945cc0f..2091583 100644 --- a/src/common/constant_strings_paths.rs +++ b/src/common/constant_strings_paths.rs @@ -168,8 +168,17 @@ pub const RENAME_LINES: [&str; 3] = [ ]; pub const TRASH_CONFIRM_LINE: &str = "Enter: restore the selected file -- x: delete permanently"; /// Executable commands whose output is a text to be displayed in terminal -pub const CLI_INFO_COMMANDS: [&str; 4] = - ["duf", "inxi -S -I -D -i -J -M --color", "neofetch", "lsusb"]; +pub const CLI_INFO: [(&str, &str); 9] = [ + ("drag and drop", "dragon-drop %s"), + ("set wallpaper", "nitrogen --set-zoom-fill --save %s"), + ("man page ", "man %s"), + ("space used ", "duf"), + ("system info ", "inxi -S -I -D -i -J -M --color"), + ("system info ", "neofetch"), + ("list usb ", "lsusb"), + ("media tags ", "mediainfo %s"), + ("diff 2 files ", "diff %f"), +]; /// Wallpaper executable pub const NITROGEN: &str = "nitrogen"; /// Mediainfo (used to preview media files) executable diff --git a/src/io/display.rs b/src/io/display.rs index be3fd2f..c07a4d7 100644 --- a/src/io/display.rs +++ b/src/io/display.rs @@ -912,9 +912,20 @@ impl<'a> WinSecondary<'a> { canvas.print_with_attr(2, 1, "pick a command", Self::ATTR_YELLOW)?; let content = &self.status.menu.cli_applications.content; - for (row, command, attr) in enumerated_colored_iter!(content) { + for (row, cli_command, attr) in enumerated_colored_iter!(content) { let attr = self.status.menu.cli_applications.attr(row, attr); - Self::draw_content_line(canvas, row + 2, command, attr)?; + let col = canvas.print_with_attr( + row + 2 + ContentWindow::WINDOW_MARGIN_TOP, + 4, + cli_command.desc, + attr, + )?; + canvas.print_with_attr( + row + 2 + ContentWindow::WINDOW_MARGIN_TOP, + 8 + col, + cli_command.executable, + attr, + )?; } Ok(()) } diff --git a/src/modes/edit/cli_menu.rs b/src/modes/edit/cli_menu.rs index dee7f57..435ecdc 100644 --- a/src/modes/edit/cli_menu.rs +++ b/src/modes/edit/cli_menu.rs @@ -1,11 +1,75 @@ use anyhow::Result; +use crate::app::Status; use crate::common::is_program_in_path; -use crate::common::CLI_INFO_COMMANDS; +use crate::common::CLI_INFO; use crate::impl_selectable_content; use crate::io::execute_with_ansi_colors; use crate::log_info; use crate::log_line; +use crate::modes::ShellCommandParser; + +/// A command line command. +/// It's constructed from a line in [`crate::common::CLI_INFO`]. +/// Each command has a short description, a name (first word of second element) +/// and a list of parsable parameters. +/// See [`crate::modes::ShellCommandParser`] for a description of accetable tokens. +/// +/// Only commands which are in `$PATH` at runtime are built from `Self::new(...)`, +/// Commands which aren't accessible return `None` +/// +/// Those commands should output a string (therefore be command line). +/// No interaction with the user is possible. +#[derive(Clone)] +pub struct CliCommand { + /// The executable itself like `ls` + pub executable: &'static str, + /// The full command with parsable arguments like %s + parsable_command: &'static str, + /// A single line description of the command + pub desc: &'static str, +} + +impl CliCommand { + fn new(desc_command: (&'static str, &'static str)) -> Option<Self> { + let desc = desc_command.0; + let parsable_command = desc_command.1; + let args = parsable_command.split(' ').collect::<Vec<_>>(); + let Some(executable) = args.get(0) else { + return None; + }; + if is_program_in_path(*executable) { + Some(Self { + executable, + parsable_command, + desc, + }) + } else { + None + } + } + + /// Run its parsable command and capture its output. + /// Some environement variables are first set to ensure the colored output. + /// Long running commands may freeze the display. + fn execute(&self, status: &Status) -> Result<String> { + let args = ShellCommandParser::new(self.parsable_command).compute(status)?; + log_info!("execute. {args:?}"); + log_line!("Executed {args:?}"); + + let params: Vec<&str> = args.iter().map(|s| s.as_str()).collect(); + let command_output = execute_with_ansi_colors(¶ms)?; + let text_output = String::from_utf8(command_output.stdout)?; + if command_output.status.success() { + log_info!( + "Command {a} exited with error code {e}", + a = args[0], + e = command_output.status + ); + }; + Ok(text_output) + } +} /// Holds the command line commands we can run and display /// without leaving FM. @@ -13,27 +77,19 @@ use crate::log_line; /// file tree or setup. #[derive(Clone)] pub struct CliApplications { - pub content: Vec<&'static str>, - commands: Vec<Vec<&'static str>>, + pub content: Vec<CliCommand>, index: usize, } impl Default for CliApplications { fn default() -> Self { let index = 0; - let commands: Vec<Vec<&str>> = CLI_INFO_COMMANDS + let content = CLI_INFO .iter() - .map(|command| command.split(' ').collect::<Vec<_>>()) - .filter(|args| is_program_in_path(args[0])) + .map(|line| CliCommand::new(*line)) + .map(|opt_command| opt_command.unwrap()) .collect(); - - let content: Vec<&str> = commands.iter().map(|args| args[0]).collect(); - - Self { - content, - index, - commands, - } + Self { content, index } } } @@ -41,25 +97,9 @@ impl CliApplications { /// Run the selected command and capture its output. /// Some environement variables are first set to ensure the colored output. /// Long running commands may freeze the display. - pub fn execute(&self) -> Result<String> { - let args = self.commands[self.index].clone(); - log_info!("execute. {args:?}"); - log_line!("Executed {args:?}"); - let command_output = execute_with_ansi_colors(&args)?; - let text_output = { - if command_output.status.success() { - String::from_utf8(command_output.stdout)? - } else { - format!( - "Command {a} exited with error code {e}", - a = args[0], - e = command_output.status - ) - } - }; - Ok(text_output) + pub fn execute(&self, status: &Status) -> Result<String> { + self.content[self.index].execute(status) } } -type StaticStr = &'static str; -impl_selectable_content!(StaticStr, CliApplications); +impl_selectable_content!(CliCommand, CliApplications); diff --git a/src/modes/edit/leave_mode.rs b/src/modes/edit/leave_mode.rs index a97e4bf..0321453 100644 --- a/src/modes/edit/leave_mode.rs +++ b/src/modes/edit/leave_mode.rs @@ -145,7 +145,7 @@ impl LeaveMode { } pub fn cli_info(status: &mut Status) -> Result<()> { - let output = status.menu.cli_applications.execute()?; + let output = status.menu.cli_applications.execute(status)?; log_info!("output\n{output}"); status.current_tab_mut().reset_edit_mode(); status.current_tab_mut().set_display_mode(Display::Preview); |