diff options
author | qkzk <qu3nt1n@gmail.com> | 2023-10-16 17:59:42 +0200 |
---|---|---|
committer | qkzk <qu3nt1n@gmail.com> | 2023-10-16 18:00:19 +0200 |
commit | 087bf81a88d58236b28072c53163d85479a2dfd7 (patch) | |
tree | 6bfb187ec161b98ba5ba4c3bed649d8c4a38b134 /src | |
parent | f2568d1b7e6c932e0a7172d7bc24fb5ab3ddba92 (diff) |
regroup openers when opening multiple files
Diffstat (limited to 'src')
-rw-r--r-- | src/event_exec.rs | 40 | ||||
-rw-r--r-- | src/opener.rs | 46 | ||||
-rw-r--r-- | src/status.rs | 31 |
3 files changed, 79 insertions, 38 deletions
diff --git a/src/event_exec.rs b/src/event_exec.rs index 561a5d8..fc29493 100644 --- a/src/event_exec.rs +++ b/src/event_exec.rs @@ -27,7 +27,7 @@ use crate::mocp::Mocp; use crate::mode::{InputSimple, MarkAction, Mode, Navigate, NeedConfirmation}; use crate::opener::{ execute_and_capture_output, execute_and_capture_output_without_check, execute_in_child, - execute_in_child_without_output_with_path, InternalVariant, + execute_in_child_without_output_with_path, }; use crate::password::{PasswordKind, PasswordUsage}; use crate::preview::ExtensionKind; @@ -344,45 +344,13 @@ impl EventAction { /// their respective opener. /// Directories aren't opened since it will lead nowhere, it would only replace the /// current tab multiple times. It may change in the future. - /// Another strange behavior, it doesn't regroup files by opener : opening multiple - /// text file will create a process per file. - /// This may also change in the future. + /// Only files which use an external opener are supported. pub fn open_file(status: &mut Status) -> Result<()> { if status.flagged.is_empty() { - let filepath = &status - .selected_non_mut() - .selected() - .context("event open file, Empty directory")? - .path - .clone(); - Self::open_filepath(status, filepath)?; + status.open_selected_file() } else { - let content = status.flagged.content().clone(); - for flagged in content.iter() { - if !flagged.is_dir() { - Self::open_filepath(status, flagged)? - } - } + status.open_flagged_files() } - Ok(()) - } - - /// Open a single file with its own opener - fn open_filepath(status: &mut Status, filepath: &path::Path) -> Result<()> { - let opener = status.opener.open_info(filepath); - if let Some(InternalVariant::NotSupported) = opener.internal_variant.as_ref() { - status.mount_iso_drive()?; - } else { - match status.opener.open(filepath) { - Ok(_) => (), - Err(e) => info!( - "Error opening {:?}: {:?}", - status.selected_non_mut().path_content.selected(), - e - ), - } - } - Ok(()) } /// Enter the rename mode. diff --git a/src/opener.rs b/src/opener.rs index a81d5d4..f6d5760 100644 --- a/src/opener.rs +++ b/src/opener.rs @@ -229,7 +229,7 @@ pub enum InternalVariant { /// A way to open one kind of files. /// It's either an internal method or an external program. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct OpenerInfo { /// The external program used to open the file. pub external_program: Option<String>, @@ -321,6 +321,46 @@ impl Opener { } } + /// Open multiple files. + /// Files sharing an opener are opened in a single command ie.: `nvim a.txt b.rs c.py`. + /// Only files opened with an external opener are supported. + pub fn open_multiple(&self, file_paths: &[PathBuf]) -> Result<()> { + let openers = self.regroup_openers(file_paths); + for (open_info, file_paths) in openers.iter() { + let file_paths_str = Self::collect_paths_as_str(file_paths); + let mut args: Vec<&str> = vec![open_info.external_program.as_ref().unwrap()]; + args.extend(&file_paths_str); + self.open_with_args(args, open_info.use_term)?; + } + Ok(()) + } + + /// Create an hashmap of openers -> [files]. + /// Each file in the collection share the same opener. + fn regroup_openers(&self, file_paths: &[PathBuf]) -> HashMap<OpenerInfo, Vec<PathBuf>> { + let mut openers: HashMap<OpenerInfo, Vec<PathBuf>> = HashMap::new(); + for file_path in file_paths.iter() { + let open_info = self.get_opener(extract_extension(file_path)); + if open_info.external_program.is_some() { + openers + .entry(open_info.to_owned()) + .and_modify(|files| files.push((*file_path).to_owned())) + .or_insert(vec![(*file_path).to_owned()]); + } + } + openers + } + + /// Convert a slice of `PathBuf` into their string representation. + /// Files which are directory are skipped. + fn collect_paths_as_str(file_paths: &[PathBuf]) -> Vec<&str> { + file_paths + .iter() + .filter(|fp| !fp.is_dir()) + .filter_map(|fp| fp.to_str()) + .collect() + } + /// Open a file, using the configured method. /// It may fail if the program changed after reading the config file. /// It may also fail if the program can't handle this kind of files. @@ -370,6 +410,10 @@ impl Opener { .to_str() .context("open with: can't parse filepath to str")?; let args = vec![program, strpath]; + self.open_with_args(args, use_term) + } + + fn open_with_args(&self, args: Vec<&str>, use_term: bool) -> Result<std::process::Child> { if use_term { self.open_terminal(args) } else { diff --git a/src/status.rs b/src/status.rs index 5a7ccdd..aa48a50 100644 --- a/src/status.rs +++ b/src/status.rs @@ -28,7 +28,7 @@ use crate::log::write_log_line; use crate::marks::Marks; use crate::mode::{InputSimple, Mode, NeedConfirmation}; use crate::mount_help::MountHelper; -use crate::opener::Opener; +use crate::opener::{InternalVariant, Opener}; use crate::password::{ drop_sudo_privileges, execute_sudo_command_with_password, reset_sudo_faillock, PasswordHolder, PasswordKind, PasswordUsage, @@ -508,6 +508,35 @@ impl Status { self.force_clear = true; } + /// Open a the selected file with its opener + pub fn open_selected_file(&mut self) -> Result<()> { + let filepath = &self + .selected_non_mut() + .selected() + .context("Empty directory")? + .path + .clone(); + let opener = self.opener.open_info(filepath); + if let Some(InternalVariant::NotSupported) = opener.internal_variant.as_ref() { + self.mount_iso_drive()?; + } else { + match self.opener.open(filepath) { + Ok(_) => (), + Err(e) => info!( + "Error opening {:?}: {:?}", + self.selected_non_mut().path_content.selected(), + e + ), + } + } + Ok(()) + } + + /// Open every flagged file with their respective opener. + pub fn open_flagged_files(&mut self) -> Result<()> { + self.opener.open_multiple(self.flagged.content()) + } + /// Mount the currently selected file (which should be an .iso file) to /// `/run/media/$CURRENT_USER/fm_iso` /// Ask a sudo password first if needed. It should always be the case. |