use std::str::FromStr;
use anyhow::{bail, Context, Result};
use crate::app::Status;
use crate::common::{path_to_string, rename_fullpath, string_to_path};
use crate::config::Bindings;
use crate::event::{ActionMap, EventAction, FmEvents};
use crate::modes::{
Content, InputCompleted, InputSimple, Leave, MarkAction, Menu, MountAction, Navigate,
NodeCreation, PasswordUsage, PickerCaller, TerminalApplications,
};
use crate::{log_info, log_line};
pub struct LeaveMenu;
impl LeaveMenu {
pub fn leave_menu(status: &mut Status, binds: &Bindings) -> Result<()> {
status
.menu
.input_history
.update(status.current_tab().menu_mode, &status.menu.input.string())?;
let must_refresh = status.current_tab().menu_mode.must_refresh();
let must_reset_mode = status.current_tab().menu_mode.must_reset_mode();
match status.current_tab().menu_mode {
Menu::Nothing => Ok(()),
Menu::InputSimple(InputSimple::Rename) => LeaveMenu::rename(status),
Menu::InputSimple(InputSimple::Newfile) => LeaveMenu::new_file(status),
Menu::InputSimple(InputSimple::Newdir) => LeaveMenu::new_dir(status),
Menu::InputSimple(InputSimple::Chmod) => LeaveMenu::chmod(status),
Menu::InputSimple(InputSimple::RegexMatch) => LeaveMenu::regex_match(status),
Menu::InputSimple(InputSimple::SetNvimAddr) => LeaveMenu::set_nvim_addr(status),
Menu::InputSimple(InputSimple::ShellCommand) => LeaveMenu::shell_command(status),
Menu::InputSimple(InputSimple::Sort) => LeaveMenu::sort(status),
Menu::InputSimple(InputSimple::Filter) => LeaveMenu::filter(status),
Menu::InputSimple(InputSimple::Password(action, usage)) => {
LeaveMenu::password(status, action, usage)
}
Menu::InputSimple(InputSimple::CloudNewdir) => {
LeaveMenu::cloud_newdir(status)?;
return Ok(());
}
Menu::InputSimple(InputSimple::Remote) => LeaveMenu::remote(status),
Menu::Navigate(Navigate::History) => LeaveMenu::history(status),
Menu::Navigate(Navigate::Shortcut) => LeaveMenu::shortcut(status),
Menu::Navigate(Navigate::Trash) => LeaveMenu::trash(status),
Menu::Navigate(Navigate::TuiApplication) => LeaveMenu::tui_application(status),
Menu::Navigate(Navigate::CliApplication) => LeaveMenu::cli_info(status),
Menu::Navigate(Navigate::Cloud) => {
LeaveMenu::cloud_enter(status)?;
return Ok(());
}
Menu::Navigate(Navigate::Marks(MarkAction::New)) => LeaveMenu::marks_update(status),
Menu::Navigate(Navigate::Marks(MarkAction::Jump)) => LeaveMenu::marks_jump(status),
Menu::Navigate(Navigate::TempMarks(MarkAction::New)) => {
LeaveMenu::tempmark_update(status)
}
Menu::Navigate(Navigate::TempMarks(MarkAction::Jump)) => LeaveMenu::tempmark_jp(status),
Menu::Navigate(Navigate::Compress) => LeaveMenu::compress(status),
Menu::Navigate(Navigate::Mount) => LeaveMenu::go_to_mount(status),
Menu::Navigate(Navigate::Context) => LeaveMenu::context(status, binds),
Menu::Navigate(Navigate::Picker) => {
LeaveMenu::picker(status)?;
return Ok(());
}
Menu::Navigate(Navigate::Flagged) => LeaveMenu::flagged(status),
Menu::InputCompleted(InputCompleted::Exec) => {
LeaveMenu::exec(status)?;
return Ok(());
}
Menu::InputCompleted(InputCompleted::Search) => Ok(()),
Menu::InputCompleted(InputCompleted::Cd) => LeaveMenu::cd(status),
Menu::InputCompleted(InputCompleted::Action) => LeaveMenu::action(status),
Menu::NeedConfirmation(_) => Ok(()),
}?;
status.menu.input.reset();
if must_reset_mode {
status.reset_menu_mode()?;
}
if must_refresh {
status.refresh_status()?;
}
Ok(())
}
pub fn trash(status: &mut Status) -> Result<()> {
if status.focus.is_file() {
return Ok(());
}
let _ = status.menu.trash.restore();
status.reset_menu_mode()?;
status.current_tab_mut().refresh_view()?;
status.update_second_pane_for_preview()
}
fn marks_jump(status: &mut Status) -> Result<()> {
if let Some((_, path)) = &status.menu.marks.selected() {
let len = status.current_tab().directory.content.len();
status.tabs[status.index].cd(path)?;
status.current_tab_mut().window.reset(len);
status.menu.input.reset();
}
status.update_second_pane_for_preview()
}
fn marks_update(status: &mut Status) -> Result<()> {
if let Some((ch, _)) = status.menu.marks.selected() {
let len = status.current_tab().directory.content.len();
let new_path = &status.tabs[status.index].directory.path;
log_line!("Saved mark {ch} -> {p}", p = new_path.display());
status.menu.marks.new_mark(*ch, new_path)?;
status.current_tab_mut().window.reset(len);
status.menu.input.reset();
}
Ok(())
}
fn tempmark_update(status: &mut Status) -> Result<()> {
let index = status.menu.temp_marks.index;
let len = status.current_tab().directory.content.len();
let new_path = &status.tabs[status.index].directory_of_selected()?;
status
.menu
.temp_marks
.set_mark(index as _, new_path.to_path_buf());
log_line!("Saved temp mark {index} -> {p}", p = new_path.display());
status.current_tab_mut().window.reset(len);
status.menu.input.reset();
Ok(())
}
fn tempmark_jp(status: &mut Status) -> Result<()> {
let Some(opt_path) = &status.menu.temp_marks.selected() else {
log_info!("no selected temp mark");
return Ok(());
};
let Some(path) = opt_path else {
return Ok(());
};
let len = status.current_tab().directory.content.len();
status.tabs[status.index].cd(path)?;
status.current_tab_mut().window.reset(len);
status.menu.input.reset();
status.update_second_pane_for_preview()
}
fn tui_application(status: &mut Status) -> Result<()> {
status.internal_settings.disable_display();
status.menu.tui_applications.execute(status)?;
status.internal_settings.enable_display();
Ok(())
}
fn cli_info(status: &mut Status) -> Result<()> {
let (output, command) = status.menu.cli_applications.execute(status)?;
log_info!("cli info: command {command}, output\n{output}");
status.preview_command_output(output, command);
Ok(())
}
fn cloud_enter(status: &mut Status) -> Result<()> {
status.cloud_enter_file_or_dir()
}
fn cloud_newdir(status: &mut Status) -> Result<()> {
status.cloud_create_newdir(status.menu.input.string())?;
status.reset_menu_mode()?;
status.cloud_open()
}
fn chmod(status: &mut Status) -> Result<()> {
status.chmod()
}
fn set_nvim_addr(status: &mut Status) -> Result<()> {
status.internal_settings.nvim_server = status.menu.input.string();
status.reset_menu_mode()?;
Ok(())
}
fn regex_match(status: &mut Status) -> Result<()> {
status.flag_from_regex()?;
status.menu.input.reset();
Ok(())
}
fn shell_command(status: &mut Status) -> Result<()> {
status.execute_shell_command_from_input()?;
Ok(())
}
fn rename(status: &mut Status) -> Result<()> {
if status.menu.input.is_empty() {
log_line!("Can't rename: new name is empty");
log_info!("Can't rename: new name is empty");
return Ok(());
}
let new_path = status.menu.input.string();
let old_path = status.current_tab().current_file()?.path;
match rename_fullpath(&old_path, &new_path) {
Ok(()) => {
status.rename_marks(&old_path, &new_path)?;
status.current_tab_mut().refresh_view()?;
status.current_tab_mut().cd_to_file(&new_path)?;
}
Err(error) => {
log_info!(
"Error renaming {old_path} to {new_path}. Error: {error}",
old_path = old_path.display()
);
log_line!(
"Error renaming {old_path} to {new_path}. Error: {error}",
old_path = old_path.display()
);
}
}
Ok(())
}
fn new_file(status: &mut Status) -> Result<()> {
match NodeCreation::Newfile.create(status) {
Ok(path) => {
status.current_tab_mut().go_to_file(&path);
status.menu.flagged.push(path);
status.refresh_tabs()?;
}
Err(error) => log_info!("Error creating file. Error: {error}",),
}
Ok(())
}
fn new_dir(status: &mut Status) -> Result<()> {
match NodeCreation::Newdir.create(status) {
Ok(path) => {
status.refresh_tabs()?;
status.current_tab_mut().go_to_file(&path);
status.menu.flagged.push(path);
}
Err(error) => log_info!("Error creating directory. Error: {error}",),
}
Ok(())
}
fn exec(status: &mut Status) -> Result<()> {
if status.current_tab().directory.content.is_empty() {
bail!("exec: empty directory")
}
let exec_command = status.menu.input.string();
if status.execute_shell_command(
exec_command,
Some(status.menu.flagged.as_strings()),
false,
)? {
status.menu.completion.reset();
status.menu.input.reset();
}
Ok(())
}
fn cd(status: &mut Status) -> Result<()> {
if status.menu.completion.is_empty() {
return Ok(());
}
let completed = status.menu.completion.current_proposition();
let path = string_to_path(completed)?;
status.thumbnail_queue_clear();
status.menu.input.reset();
status.current_tab_mut().cd_to_file(&path)?;
let len = status.current_tab().directory.content.len();
status.current_tab_mut().window.reset(len);
status.update_second_pane_for_preview()
}
fn shortcut(status: &mut Status) -> Result<()> {
let path = status
.menu
.shortcut
.selected()
.context("exec shortcut: empty shortcuts")?;
status.tabs[status.index].cd(path)?;
status.current_tab_mut().refresh_view()?;
status.update_second_pane_for_preview()
}
fn sort(status: &mut Status) -> Result<()> {
status.current_tab_mut().set_sortkind_per_mode();
status.update_second_pane_for_preview()?;
status.focus = status.focus.to_parent();
Ok(())
}
fn history(status: &mut Status) -> Result<()> {
status.current_tab_mut().history_cd_to_last()?;
status.update_second_pane_for_preview()
}
fn password(
status: &mut Status,
action: Option<MountAction>,
usage: PasswordUsage,
) -> Result<()> {
status.execute_password_command(action, usage)
}
fn compress(status: &mut Status) -> Result<()> {
status.compress()
}
fn context(status: &mut Status, binds: &Bindings) -> Result<()> {
let command = status.menu.context.matcher().to_owned();
EventAction::reset_mode(status)?;
command.matcher(status, binds)
}
fn action(status: &mut Status) -> Result<()> {
let action_str = status.menu.completion.current_proposition();
let Ok(action) = ActionMap::from_str(action_str) else {
return Ok(());
};
status.reset_menu_mode()?;
status.focus = status.focus.to_parent();
status.fm_sender.send(FmEvents::Action(action))?;
Ok(())
}
fn filter(status: &mut Status) -> Result<()> {
status.filter()?;
status.menu.input.reset();
Ok(())
}
fn remote(status: &mut Status) -> Result<()> {
let current_path = &path_to_string(&status.current_tab().directory_of_selected()?);
status.menu.mount_remote(current_path);
Ok(())
}
fn go_to_mount(status: &mut Status) -> Result<()> {
match status.current_tab().menu_mode {
Menu::Navigate(Navigate::Mount) => status.go_to_normal_drive(),
_ => Ok(()),
}
}
fn picker(status: &mut Status) -> Result<()> {
let Some(caller) = &status.menu.picker.caller else {
return Ok(());
};
match caller {
PickerCaller::Cloud => status.cloud_load_config(),
PickerCaller::Menu(menu) => EventAction::reenter_menu_from_picker(status, *menu),
PickerCaller::Unknown => Ok(()),
}
}
fn flagged(status: &mut Status) -> Result<()> {
status.jump_flagged()
}
}