diff options
Diffstat (limited to 'src/event_exec.rs')
-rw-r--r-- | src/event_exec.rs | 720 |
1 files changed, 371 insertions, 349 deletions
diff --git a/src/event_exec.rs b/src/event_exec.rs index 34763d8..47609ed 100644 --- a/src/event_exec.rs +++ b/src/event_exec.rs @@ -4,6 +4,7 @@ use std::fs; use std::path; use std::str::FromStr; +use anyhow::{anyhow, Context, Result}; use copypasta::{ClipboardContext, ClipboardProvider}; use log::info; use sysinfo::SystemExt; @@ -11,28 +12,25 @@ use sysinfo::SystemExt; use crate::action_map::ActionMap; use crate::completion::InputCompleted; use crate::config::Colors; -use crate::constant_strings_paths::CONFIG_PATH; -use crate::constant_strings_paths::DEFAULT_DRAGNDROP; -use crate::constant_strings_paths::NVIM_RPC_SENDER; +use crate::constant_strings_paths::{CONFIG_PATH, DEFAULT_DRAGNDROP}; use crate::content_window::RESERVED_ROWS; use crate::copy_move::CopyMove; -use crate::cryptsetup::EncryptedAction; -use crate::cryptsetup::PasswordKind; +use crate::cryptsetup::BlockDeviceAction; use crate::fileinfo::FileKind; use crate::filter::FilterKind; -use crate::fm_error::{FmError, FmResult}; +use crate::iso::IsoMounter; use crate::log::read_log; use crate::mocp::Mocp; -use crate::mode::Navigate; -use crate::mode::{InputSimple, MarkAction, Mode, NeedConfirmation}; -use crate::opener::execute_in_child; -use crate::opener::execute_in_child_without_output_with_path; +use crate::mode::{InputSimple, MarkAction, Mode, Navigate, NeedConfirmation}; +use crate::mount_help::MountHelper; +use crate::nvim::nvim; +use crate::opener::{execute_in_child, execute_in_child_without_output_with_path, InternalVariant}; +use crate::password::{PasswordKind, PasswordUsage}; use crate::preview::Preview; use crate::selectable_content::SelectableContent; use crate::status::Status; use crate::tab::Tab; -use crate::utils::disk_used_by_path; -use crate::utils::is_program_in_path; +use crate::utils::{current_username, disk_used_by_path, filename_from_path}; /// Every kind of mutation of the application is defined here. /// It mutates `Status` or its children `Tab`. @@ -40,7 +38,7 @@ pub struct EventExec {} impl EventExec { /// Reset the selected tab view to the default. - pub fn refresh_status(status: &mut Status, colors: &Colors) -> FmResult<()> { + pub fn refresh_status(status: &mut Status, colors: &Colors) -> Result<()> { status.force_clear(); status.refresh_users()?; status.selected().refresh_view()?; @@ -54,12 +52,7 @@ impl EventExec { /// isn't sufficiant to display enough information. /// We also need to know the new height of the terminal to start scrolling /// up or down. - pub fn resize( - status: &mut Status, - width: usize, - height: usize, - colors: &Colors, - ) -> FmResult<()> { + pub fn resize(status: &mut Status, width: usize, height: usize, colors: &Colors) -> Result<()> { status.set_dual_pane_if_wide_enough(width)?; status.selected().set_height(height); Self::refresh_status(status, colors)?; @@ -67,13 +60,13 @@ impl EventExec { } /// Remove every flag on files in this directory and others. - pub fn event_clear_flags(status: &mut Status) -> FmResult<()> { + pub fn event_clear_flags(status: &mut Status) -> Result<()> { status.flagged.clear(); Ok(()) } /// Flag all files in the current directory. - pub fn event_flag_all(status: &mut Status) -> FmResult<()> { + pub fn event_flag_all(status: &mut Status) -> Result<()> { status.tabs[status.index] .path_content .content @@ -86,7 +79,7 @@ impl EventExec { /// Reverse every flag in _current_ directory. Flagged files in other /// directory aren't affected. - pub fn event_reverse_flags(status: &mut Status) -> FmResult<()> { + pub fn event_reverse_flags(status: &mut Status) -> Result<()> { status.tabs[status.index] .path_content .content @@ -96,7 +89,7 @@ impl EventExec { } /// Toggle a single flag and move down one row. - pub fn event_toggle_flag(status: &mut Status) -> FmResult<()> { + pub fn event_toggle_flag(status: &mut Status) -> Result<()> { let tab = status.selected_non_mut(); match tab.mode { @@ -115,18 +108,8 @@ impl EventExec { Ok(()) } - /// Move to the next file in the jump list. - pub fn event_jumplist_next(status: &mut Status) { - status.flagged.next() - } - - /// Move to the previous file in the jump list. - pub fn event_jumplist_prev(status: &mut Status) { - status.flagged.prev() - } - /// Change to CHMOD mode allowing to edit permissions of a file. - pub fn event_chmod(status: &mut Status) -> FmResult<()> { + pub fn event_chmod(status: &mut Status) -> Result<()> { if status.selected().path_content.is_empty() { return Ok(()); } @@ -143,7 +126,7 @@ impl EventExec { /// Enter JUMP mode, allowing to jump to any flagged file. /// Does nothing if no file is flagged. - pub fn event_jump(status: &mut Status) -> FmResult<()> { + pub fn event_jump(status: &mut Status) -> Result<()> { if !status.flagged.is_empty() { status.flagged.index = 0; status.selected().set_mode(Mode::Navigate(Navigate::Jump)) @@ -152,13 +135,13 @@ impl EventExec { } /// Enter Marks new mode, allowing to bind a char to a path. - pub fn event_marks_new(tab: &mut Tab) -> FmResult<()> { + pub fn event_marks_new(tab: &mut Tab) -> Result<()> { tab.set_mode(Mode::Navigate(Navigate::Marks(MarkAction::New))); Ok(()) } /// Enter Marks jump mode, allowing to jump to a marked file. - pub fn event_marks_jump(status: &mut Status) -> FmResult<()> { + pub fn event_marks_jump(status: &mut Status) -> Result<()> { if status.marks.is_empty() { return Ok(()); } @@ -169,7 +152,7 @@ impl EventExec { } /// Jump to the current mark. - pub fn exec_marks_jump_selected(status: &mut Status) -> FmResult<()> { + pub fn exec_marks_jump(status: &mut Status) -> Result<()> { let marks = status.marks.clone(); let tab = status.selected(); if let Some((_, path)) = marks.selected() { @@ -184,7 +167,7 @@ impl EventExec { /// Update the selected mark with the current path. /// Doesn't change its char. /// If it doesn't fail, a new pair will be set with (oldchar, new path). - pub fn exec_marks_update_selected(status: &mut Status) -> FmResult<()> { + pub fn exec_marks_update(status: &mut Status) -> Result<()> { let marks = status.marks.clone(); let len = status.selected_non_mut().path_content.content.len(); if let Some((ch, _)) = marks.selected() { @@ -198,7 +181,7 @@ impl EventExec { } /// Execute a new mark, saving it to a config file for futher use. - pub fn exec_marks_new(status: &mut Status, c: char, colors: &Colors) -> FmResult<()> { + pub fn exec_marks_new(status: &mut Status, c: char, colors: &Colors) -> Result<()> { let path = status.selected().path_content.path.clone(); status.marks.new_mark(c, path)?; Self::event_normal(status.selected())?; @@ -208,9 +191,10 @@ impl EventExec { /// Execute a jump to a mark, moving to a valid path. /// If the saved path is invalid, it does nothing but reset the view. - pub fn exec_marks_jump(status: &mut Status, c: char, colors: &Colors) -> FmResult<()> { + pub fn exec_marks_jump_char(status: &mut Status, c: char, colors: &Colors) -> Result<()> { if let Some(path) = status.marks.get(c) { - status.selected().set_pathcontent(&path)? + status.selected().set_pathcontent(&path)?; + status.selected().history.push(&path); } Self::event_normal(status.selected())?; status.selected().reset_mode(); @@ -218,12 +202,12 @@ impl EventExec { } /// Creates a symlink of every flagged file to the current directory. - pub fn event_symlink(status: &mut Status) -> FmResult<()> { + pub fn event_symlink(status: &mut Status) -> Result<()> { for original_file in status.flagged.content.iter() { let filename = original_file .as_path() .file_name() - .ok_or_else(|| FmError::custom("event symlink", "File not found"))?; + .context("event symlink: File not found")?; let link = status .selected_non_mut() .directory_of_selected()? @@ -237,51 +221,43 @@ impl EventExec { /// Enter bulkrename mode, opening a random temp file where the user /// can edit the selected filenames. /// Once the temp file is saved, those file names are changed. - pub fn event_bulk(status: &mut Status) -> FmResult<()> { + pub fn event_bulk(status: &mut Status) -> Result<()> { status.selected().set_mode(Mode::Navigate(Navigate::Bulk)); Ok(()) } - pub fn event_bulk_prev(status: &mut Status) { - status.bulk.prev() - } - - pub fn event_bulk_next(status: &mut Status) { - status.bulk.next() - } - - pub fn event_shell_menu_prev(status: &mut Status) { - info!("shell menu prev"); - status.shell_menu.prev() - } - - pub fn event_shell_menu_next(status: &mut Status) { - info!("shell menu next"); - status.shell_menu.next() - } - - pub fn exec_bulk(status: &mut Status) -> FmResult<()> { + pub fn exec_bulk(status: &mut Status) -> Result<()> { status.bulk.execute_bulk(status) } - pub fn exec_shellmenu(status: &mut Status) -> FmResult<()> { + pub fn exec_shellmenu(status: &mut Status) -> Result<()> { status.shell_menu.execute(status) } + pub fn exec_cli_info(status: &mut Status) -> Result<()> { + let output = status.cli_info.execute()?; + info!("output\n{output}"); + status.selected().set_mode(Mode::Preview); + let preview = Preview::cli_info(&output); + status.selected().window.reset(preview.len()); + status.selected().preview = preview; + Ok(()) + } + /// Copy the flagged file to current directory. /// A progress bar is displayed and a notification is sent once it's done. - pub fn exec_copy_paste(status: &mut Status) -> FmResult<()> { + pub fn exec_copy_paste(status: &mut Status) -> Result<()> { status.cut_or_copy_flagged_files(CopyMove::Copy) } /// Move the flagged file to current directory. /// A progress bar is displayed and a notification is sent once it's done. - pub fn exec_cut_paste(status: &mut Status) -> FmResult<()> { + pub fn exec_cut_paste(status: &mut Status) -> Result<()> { status.cut_or_copy_flagged_files(CopyMove::Move) } /// Recursively delete all flagged files. - pub fn exec_delete_files(status: &mut Status, colors: &Colors) -> FmResult<()> { + pub fn exec_delete_files(status: &mut Status, colors: &Colors) -> Result<()> { for pathbuf in status.flagged.content.iter() { if pathbuf.is_dir() { std::fs::remove_dir_all(pathbuf)?; @@ -299,7 +275,7 @@ impl EventExec { /// the file. /// Nothing is done if the user typed nothing or an invalid permission like /// 955. - pub fn exec_chmod(status: &mut Status) -> FmResult<()> { + pub fn exec_chmod(status: &mut Status) -> Result<()> { if status.selected().input.is_empty() { return Ok(()); } @@ -315,7 +291,7 @@ impl EventExec { status.reset_tabs_view() } - pub fn exec_set_neovim_address(status: &mut Status) -> FmResult<()> { + pub fn exec_set_nvim_addr(status: &mut Status) -> Result<()> { status.nvim_server = status.selected_non_mut().input.string(); status.selected().reset_mode(); Ok(()) @@ -324,22 +300,16 @@ impl EventExec { /// Execute a jump to the selected flagged file. /// If the user selected a directory, we jump inside it. /// Otherwise, we jump to the parent and select the file. - pub fn exec_jump(status: &mut Status) -> FmResult<()> { + pub fn exec_jump(status: &mut Status) -> Result<()> { let Some(jump_target) = status.flagged.selected() else { return Ok(()) }; let jump_target = jump_target.to_owned(); let target_dir = match jump_target.parent() { Some(parent) => parent, None => &jump_target, }; - let tab = status.selected(); - tab.input.clear(); - tab.history.push(target_dir); - tab.path_content - .change_directory(target_dir, &tab.filter, tab.show_hidden)?; - let index = tab.find_jump_index(&jump_target).unwrap_or_default(); - tab.path_content.select_index(index); - tab.set_window(); - tab.scroll_to(index); + status.selected().set_pathcontent(target_dir)?; + let index = status.selected().path_content.select_file(&jump_target); + status.selected().scroll_to(index); Ok(()) } @@ -349,9 +319,9 @@ impl EventExec { status: &mut Status, confirmed_action: NeedConfirmation, colors: &Colors, - ) -> FmResult<()> { + ) -> Result<()> { Self::_exec_confirmed_action(status, confirmed_action, colors)?; - status.selected().set_mode(Mode::Normal); + status.selected().reset_mode(); Ok(()) } @@ -359,7 +329,7 @@ impl EventExec { status: &mut Status, confirmed_action: NeedConfirmation, colors: &Colors, - ) -> FmResult<()> { + ) -> Result<()> { match confirmed_action { NeedConfirmation::Delete => Self::exec_delete_files(status, colors), NeedConfirmation::Move => Self::exec_cut_paste(status), @@ -377,13 +347,13 @@ impl EventExec { /// Leave current mode to normal mode. /// Reset the inputs and completion, reset the window, exit the preview. - pub fn event_reset_mode(tab: &mut Tab) -> FmResult<()> { + pub fn event_reset_mode(tab: &mut Tab) -> Result<()> { tab.reset_mode(); tab.refresh_view() } /// Reset the inputs and completion, reset the window, exit the preview. - pub fn event_normal(tab: &mut Tab) -> FmResult<()> { + pub fn event_normal(tab: &mut Tab) -> Result<()> { tab.refresh_view() } @@ -499,7 +469,7 @@ impl EventExec { } /// Select the left or right tab depending on where the user clicked. - pub fn event_select_pane(status: &mut Status, col: u16) -> FmResult<()> { + pub fn event_select_pane(status: &mut Status, col: u16) -> Result<()> { let (width, _) = status.term_size()?; if (col as usize) < width / 2 { status.select_tab(0)?; @@ -510,7 +480,7 @@ impl EventExec { } /// Select a given row, if there's something in it. - pub fn event_select_row(status: &mut Status, row: u16, colors: &Colors) -> FmResult<()> { + pub fn event_select_row(status: &mut Status, row: u16, colors: &Colors) -> Result<()> { let tab = status.selected(); match tab.mode { Mode::Normal => { @@ -531,44 +501,10 @@ impl EventExec { Ok(()) } - /// Select the next shortcut. - pub fn event_shortcut_next(tab: &mut Tab) { - tab.shortcut.next() - } - - /// Select the previous shortcut. - pub fn event_shortcut_prev(tab: &mut Tab) { - tab.shortcut.prev() - } - - /// Select the next shortcut. - pub fn event_marks_next(status: &mut Status) { - status.marks.next() - } - - /// Select the previous shortcut. - pub fn event_marks_prev(status: &mut Status) { - status.marks.prev() - } - - /// Select the next element in history of visited files. - /// Watchout! Since the history is displayed in reverse order, - /// we call the "prev" method of the `History` instance instead. - pub fn event_history_next(tab: &mut Tab) { - tab.history.next() - } - - /// Select the previous element in history of visited files. - /// Watchout! Since the history is displayed in reverse order, - /// we call the "next" method of the `History` instance instead. - pub fn event_history_prev(tab: &mut Tab) { - tab.history.prev() - } - /// Move to parent directory if there's one. /// Does /// Add the starting directory to history. - pub fn event_move_to_parent(tab: &mut Tab) -> FmResult<()> { + pub fn event_move_to_parent(tab: &mut Tab) -> Result<()> { tab.move_to_parent() } @@ -578,7 +514,7 @@ impl EventExec { } /// Open the file with configured opener or enter the directory. - pub fn exec_file(status: &mut Status) -> FmResult<()> { + pub fn exec_file(status: &mut Status) -> Result<()> { let tab = status.selected(); if tab.path_content.is_empty() { return Ok(()); @@ -606,7 +542,7 @@ impl EventExec { } /// Add a char to input string, look for a possible completion. - pub fn event_text_insert_and_complete(tab: &mut Tab, c: char) -> FmResult<()> { + pub fn event_text_insert_and_complete(tab: &mut Tab, c: char) -> Result<()> { Self::event_text_insertion(tab, c); tab.fill_completion() } @@ -615,7 +551,7 @@ impl EventExec { /// A confirmation is asked before copying all flagged files to /// the current directory. /// Does nothing if no file is flagged. - pub fn event_copy_paste(status: &mut Status) -> FmResult<()> { + pub fn event_copy_paste(status: &mut Status) -> Result<()> { if status.flagged.is_empty() { return Ok(()); } @@ -629,7 +565,7 @@ impl EventExec { /// A confirmation is asked before moving all flagged files to /// the current directory. /// Does nothing if no file is flagged. - pub fn event_cut_paste(status: &mut Status) -> FmResult<()> { + pub fn event_cut_paste(status: &mut Status) -> Result<()> { if status.flagged.is_empty() { return Ok(()); } @@ -640,20 +576,20 @@ impl EventExec { } /// Enter the new dir mode. - pub fn event_new_dir(tab: &mut Tab) -> FmResult<()> { + pub fn event_new_dir(tab: &mut Tab) -> Result<()> { tab.set_mode(Mode::InputSimple(InputSimple::Newdir)); Ok(()) } /// Enter the new file mode. - pub fn event_new_file(tab: &mut Tab) -> FmResult<()> { + pub fn event_new_file(tab: &mut Tab) -> Result<()> { tab.set_mode(Mode::InputSimple(InputSimple::Newfile)); Ok(()) } /// Enter the execute mode. Most commands must be executed to allow for /// a confirmation. - pub fn event_exec(tab: &mut Tab) -> FmResult<()> { + pub fn event_exec(tab: &mut Tab) -> Result<()> { tab.set_mode(Mode::InputCompleted(InputCompleted::Exec)); Ok(()) } @@ -662,7 +598,7 @@ impl EventExec { /// Every file can be previewed. See the `crate::enum::Preview` for /// more details on previewinga file. /// Does nothing if the directory is empty. - pub fn event_preview(status: &mut Status, colors: &Colors) -> FmResult<()> { + pub fn event_preview(status: &mut Status, colors: &Colors) -> Result<()> { if status.selected_non_mut().path_content.is_empty() { return Ok(()); } @@ -691,7 +627,7 @@ impl EventExec { /// Enter the delete mode. /// A confirmation is then asked before deleting all the flagged files. /// Does nothing is no file is flagged. - pub fn event_delete_file(status: &mut Status) -> FmResult<()> { + pub fn event_delete_file(status: &mut Status) -> Result<()> { if status.flagged.is_empty() { return Ok(()); } @@ -703,7 +639,7 @@ impl EventExec { /// Display the help which can be navigated and displays the configrable /// binds. - pub fn event_help(status: &mut Status) -> FmResult<()> { + pub fn event_help(status: &mut Status) -> Result<()> { let help = status.help.clone(); let tab = status.selected(); tab.set_mode(Mode::Preview); @@ -713,7 +649,7 @@ impl EventExec { } /// Display the last actions impacting the file tree - pub fn event_log(tab: &mut Tab) -> FmResult<()> { + pub fn event_log(tab: &mut Tab) -> Result<()> { let log = read_log()?; tab.set_mode(Mode::Preview); tab.preview = Preview::log(log); @@ -724,7 +660,7 @@ impl EventExec { /// Enter the search mode. /// Matching items are displayed as you type them. - pub fn event_search(tab: &mut Tab) -> FmResult<()> { + pub fn event_search(tab: &mut Tab) -> Result<()> { tab.searched = None; tab.set_mode(Mode::InputCompleted(InputCompleted::Search)); Ok(()) @@ -732,7 +668,7 @@ impl EventExec { /// Enter the regex mode. /// Every file matching the typed regex will be flagged. - pub fn event_regex_match(tab: &mut Tab) -> FmResult<()> { + pub fn event_regex_match(tab: &mut Tab) -> Result<()> { match tab.mode { Mode::Tree => (), _ => tab.set_mode(Mode::InputSimple(InputSimple::RegexMatch)), @@ -741,14 +677,14 @@ impl EventExec { } /// Enter the sort mode, allowing the user to select a sort method. - pub fn event_sort(tab: &mut Tab) -> FmResult<()> { + pub fn event_sort(tab: &mut Tab) -> Result<()> { tab.set_mode(Mode::InputSimple(InputSimple::Sort)); Ok(()) } /// Once a quit event is received, we change a flag and break the main loop. /// It's usefull to reset the cursor before leaving the application. - pub fn event_quit(tab: &mut Tab) -> FmResult<()> { + pub fn event_quit(tab: &mut Tab) -> Result<()> { if let Mode::Tree = tab.mode { Self::event_normal(tab)?; tab.set_mode(Mode::Normal) @@ -773,7 +709,7 @@ impl EventExec { /// by extension. /// The first letter is used to identify the method. /// If the user types an uppercase char, the sort is reverse. - pub fn event_leave_sort(status: &mut Status, c: char, colors: &Colors) -> FmResult<()> { + pub fn event_leave_sort(status: &mut Status, c: char, colors: &Colors) -> Result<()> { if status.selected_non_mut().path_content.content.is_empty() { return Ok(()); } @@ -804,7 +740,7 @@ impl EventExec { } /// Toggle the display of hidden files. - pub fn event_toggle_hidden(status: &mut Status, colors: &Colors) -> FmResult<()> { + pub fn event_toggle_hidden(status: &mut Status, colors: &Colors) -> Result<()> { let tab = status.selected(); tab.show_hidden = !tab.show_hidden; tab.path_content.reset_files(&tab.filter, tab.show_hidden)?; @@ -816,20 +752,25 @@ impl EventExec { } /// Open a file with custom opener. - pub fn event_open_file(status: &mut Status) -> FmResult<()> { - match status.opener.open( - &status - .selected_non_mut() - .selected() - .ok_or_else(|| FmError::custom("event open file", "Empty directory"))? - .path, - ) { - Ok(_) => (), - Err(e) => info!( - "Error opening {:?}: {:?}", - status.selected_non_mut().path_content.selected(), - e - ), + pub fn event_open_file(status: &mut Status) -> Result<()> { + let filepath = &status + .selected_non_mut() + .selected() + .context("event open file, Empty directory")? + .path + .clone(); + let opener = status.opener.open_info(filepath); + if let Some(InternalVariant::NotSupported) = opener.internal_variant.as_ref() { + Self::event_mount_iso_drive(status)?; + } else { + match status.opener.open(filepath) { + Ok(_) => (), + Err(e) => info!( + "Error opening {:?}: {:?}", + status.selected_non_mut().path_content.selected(), + e + ), + } } Ok(()) } @@ -838,15 +779,26 @@ impl EventExec { /// Keep a track of the current mode to ensure we rename the correct file. /// When we enter rename from a "tree" mode, we'll need to rename the selected file in the tree, /// not the selected file in the pathcontent. - pub fn event_rename(tab: &mut Tab) -> FmResult<()> { + pub fn event_rename(tab: &mut Tab) -> Result<()> { if tab.selected().is_some() { + let old_name = match tab.mode { + Mode::Tree => tab.directory.tree.current_node.filename(), + _ => filename_from_path( + &tab.path_content + .selected() + .context("Event rename: no file in current directory")? + .path, + )? + .to_owned(), + }; + tab.input.replace(&old_name); tab.set_mode(Mode::InputSimple(InputSimple::Rename)); } Ok(()) } /// Enter the goto mode where an user can type a path to jump to. - pub fn event_goto(tab: &mut Tab) -> FmResult<()> { + pub fn event_goto(tab: &mut Tab) -> Result<()> { tab.set_mode(Mode::InputCompleted(InputCompleted::Goto)); tab.completion.reset(); Ok(()) @@ -855,22 +807,31 @@ impl EventExec { /// Open a new terminal in current directory. /// The shell is a fork of current process and will exit if the application /// is terminated first. - pub fn event_shell(status: &mut Status) -> FmResult<()> { + pub fn event_shell(status: &mut Status) -> Result<()> { let tab = status.selected_non_mut(); let path = tab.directory_of_selected()?; execute_in_child_without_output_with_path(&status.opener.terminal, path, None)?; Ok(()) } - /// Enter the history mode, allowing to navigate to previously visited - /// directory. - pub fn event_shell_menu(tab: &mut Tab) -> FmResult<()> { + /// Enter the shell menu mode. You can pick a TUI application to be run + pub fn event_shell_menu(tab: &mut Tab) -> Result<()> { tab.set_mode(Mode::Navigate(Navigate::ShellMenu)); Ok(()) } + + /// Enter the cli info mode. You can pick a Text application to be + /// displayed/ + pub fn event_cli_info(status: &mut Status) -> Result<()> { + status + .selected() + .set_mode(Mode::Navigate(Navigate::CliInfo)); + Ok(()) + } + /// Enter the history mode, allowing to navigate to previously visited /// directory. - pub fn event_history(tab: &mut Tab) -> FmResult<()> { + pub fn event_history(tab: &mut Tab) -> Result<()> { tab.set_mode(Mode::Navigate(Navigate::History)); Ok(()) } @@ -878,7 +839,7 @@ impl EventExec { /// Enter the shortcut mode, allowing to visit predefined shortcuts. /// Basic folders (/, /dev... $HOME) and mount points (even impossible to /// visit ones) are proposed. - pub fn event_shortcut(tab: &mut Tab) -> FmResult<()> { + pub fn event_shortcut(tab: &mut Tab) -> Result<()> { std::env::set_current_dir(tab.current_path())?; tab.shortcut.update_git_root(); tab.set_mode(Mode::Navigate(Navigate::Shortcut)); @@ -886,7 +847,7 @@ impl EventExec { } /// A right click opens a file or a directory. - pub fn event_right_click(status: &mut Status, colors: &Colors) -> FmResult<()> { + pub fn event_right_click(status: &mut Status, colors: &Colors) -> Result<()> { match status.selected().mode { Mode::Normal => Self::exec_file(status), Mode::Tree => Self::exec_tree(status, colors), @@ -903,10 +864,7 @@ impl EventExec { /// If no RPC server were provided at launch time - which may happen for /// reasons unknow to me - it does nothing. /// It requires the "nvim-send" application to be in $PATH. - pub fn event_nvim_filepicker(status: &mut Status) -> FmResult<()> { - if !is_program_in_path(NVIM_RPC_SENDER) { - return Ok(()); - }; + pub fn event_nvim_filepicker(status: &mut Status) -> Result<()> { Self::read_nvim_listen_address_if_needed(status); if status.nvim_server.is_empty() { return Ok(()); @@ -920,51 +878,44 @@ impl EventExec { Ok(()) } - pub fn event_set_nvim_server(status: &mut Status) -> FmResult<()> { + pub fn event_set_nvim_server(status: &mut Status) -> Result<()> { status .selected() - .set_mode(Mode::InputSimple(InputSimple::SetNvimAddress)); + .set_mode(Mode::InputSimple(InputSimple::SetNvimAddr)); Ok(()) } fn open_in_current_neovim(path_str: &str, nvim_server: &str) { - let _ = execute_in_child( - NVIM_RPC_SENDER, - &vec![ - "--remote-send", - &format!("<esc>:e {path_str}<cr><esc>:set number<cr><esc>:close<cr>"), - "--servername", - nvim_server, - ], - ); + let command = &format!("<esc>:e {path_str}<cr><esc>:set number<cr><esc>:close<cr>"); + let _ = nvim(nvim_server, command); } /// Copy the selected filename to the clipboard. Only the filename. - pub fn event_filename_to_clipboard(tab: &Tab) -> FmResult<()> { + pub fn event_filename_to_clipboard(tab: &Tab) -> Result<()> { Self::set_clipboard( tab.selected() - .ok_or_else(|| FmError::custom("event_filename_to_clipboard", "no selected file"))? + .context("event_filename_to_clipboard: no selected file")? .filename .clone(), ) } /// Copy the selected filepath to the clipboard. The absolute path. - pub fn event_filepath_to_clipboard(tab: &Tab) -> FmResult<()> { + pub fn event_filepath_to_clipboard(tab: &Tab) -> Result<()> { Self::set_clipboard( tab.selected() - .ok_or_else(|| FmError::custom("event_filepath_to_clipboard", "no selected file"))? + .context("event_filepath_to_clipboard: no selected file")? .path .to_str() - .ok_or_else(|| FmError::custom("event_filepath_to_clipboard", "no selected file"))? + .context("event_filepath_to_clipboard: no selected file")? .to_owned(), ) } - fn set_clipboard(content: String) -> FmResult<()> { + fn set_clipboard(content: String) -> Result<()> { info!("copied to clipboard: {}", content); - let mut ctx = ClipboardContext::new()?; - ctx.set_contents(content)?; + let Ok(mut ctx) = ClipboardContext::new() else { return Ok(()); }; + let Ok(_) = ctx.set_contents(content) else { return Ok(()); }; // For some reason, it's not writen if you don't read it back... let _ = ctx.get_contents(); Ok(()) @@ -972,13 +923,13 @@ impl EventExec { /// Enter the filter mode, where you can filter. /// See `crate::filter::Filter` for more details. - pub fn event_filter(tab: &mut Tab) -> FmResult<()> { + pub fn event_filter(tab: &mut Tab) -> Result<()> { tab.set_mode(Mode::InputSimple(InputSimple::Filter)); Ok(()) } /// Move back in history to the last visited directory. - pub fn event_back(status: &mut Status, colors: &Colors) -> FmResult<()> { + pub fn event_back(status: &mut Status, colors: &Colors) -> Result<()> { if status.selected_non_mut().history.content.len() <= 1 { return Ok(()); } @@ -995,7 +946,7 @@ impl EventExec { } /// Move to $HOME aka ~. - pub fn event_home(tab: &mut Tab) -> FmResult<()> { + pub fn event_home(tab: &mut Tab) -> Result<()> { let home_cow = shellexpand::tilde("~"); let home: &str = home_cow.borrow(); let path = std::fs::canonicalize(home)?; @@ -1016,13 +967,13 @@ impl EventExec { /// It uses the `fs::rename` function and has the same limitations. /// We only try to rename in the same directory, so it shouldn't be a problem. /// Filename is sanitized before processing. - pub fn exec_rename(tab: &mut Tab) -> FmResult<()> { + pub fn exec_rename(tab: &mut Tab) -> Result<()> { let fileinfo = match tab.previous_mode { Mode::Tree => &tab.directory.tree.current_node.fileinfo, _ => tab .path_content .selected() - .ok_or_else(|| FmError::custom("rename", "couldnt parse selected"))?, + .context("rename: couldnt parse selected")?, }; let original_path = &fileinfo.path; @@ -1047,7 +998,7 @@ impl EventExec { /// Creates a new file with input string as name. /// Nothing is done if the file already exists. /// Filename is sanitized before processing. - pub fn exec_newfile(tab: &mut Tab) -> FmResult<()> { + pub fn exec_newfile(tab: &mut Tab) -> Result<()> { let path = tab .path_content .path @@ -1064,7 +1015,7 @@ impl EventExec { /// We use `fs::create_dir` internally so it will fail if the input string /// ie. the user can create `newdir` or `newdir/newfolder`. /// Directory name is sanitized before processing. - pub fn exec_newdir(tab: &mut Tab) -> FmResult<()> { + pub fn exec_newdir(tab: &mut Tab) -> Result<()> { let path = tab .path_content .path @@ -1080,9 +1031,9 @@ impl EventExec { /// from the input string. It will fail silently if the executable can't /// be found. /// Optional parameters can be passed normally. ie. `"ls -lah"` - pub fn exec_exec(tab: &mut Tab) -> FmResult<()> { + pub fn exec_exec(tab: &mut Tab) -> Result<()> { if tab.path_content.content.is_empty() { - return Err(FmError::custom("exec exec", "empty directory")); + return Err(anyhow!("exec exec: empty directory")); } let exec_command = tab.input.string(); let mut args: Vec<&str> = exec_command.split(' ').collect(); @@ -1090,16 +1041,12 @@ impl EventExec { if std::path::Path::new(command).exists() { let path = &tab |