diff options
-rw-r--r-- | development.md | 3 | ||||
-rw-r--r-- | readme.md | 10 | ||||
-rw-r--r-- | src/args.rs | 18 | ||||
-rw-r--r-- | src/shell_menu.rs | 17 | ||||
-rw-r--r-- | src/status.rs | 70 | ||||
-rw-r--r-- | src/tab.rs | 26 |
6 files changed, 94 insertions, 50 deletions
diff --git a/development.md b/development.md index 06033fd..8ef539a 100644 --- a/development.md +++ b/development.md @@ -577,6 +577,9 @@ New view: Tree ! Toggle with 't', fold with 'z'. Navigate normally. - [x] refresh every 10 seconds. If no file in current dir has changed, nothing happens. - [x] scroll in preview second screen - [x] FIX sending `Event::User(())` events from refresher hangs skim. Use `Event(Key::AltPageUp)` which is now reserved. +- [x] allow file selection from args : -f filename selects a file +- [x] more args : dual pane, preview second, display full, show hidden +- [ ] document filepicking (from my config etc.). - [ ] avoid multiple refreshs if we edit files ourself ## TODO @@ -11,16 +11,16 @@ Written in rust. [docrs]: https://docs.rs/fm-tui/0.1.0 ``` -FM : dired like file manager - +A TUI file manager inspired by dired and ranger Usage: fm [OPTIONS] Options: -p, --path <PATH> Starting path [default: .] -s, --server <SERVER> Nvim server [default: ] - -h, --help Print help information - -V, --version Print version information + -f, --file <FILE> Selected file [default: .] + -h, --help Print help + -V, --version Print version ``` ## Platform @@ -100,7 +100,7 @@ Many ways to jump somewhere : - Toggle the tree view with t. Fold selected folder with z. Unfold every folder with Z, fold every folder with Alt+z. - Enter preview mode with Alt+P. Every file is previewed in the second pane. - Filter the view (by extension, name, directory only, all files) with F -- Find files with / (with completion: Tab, enter to search), +- Find files with / (with completion: Tab, enter to search), - flag files matching a regex with w ### Fuzzy finder diff --git a/src/args.rs b/src/args.rs index 4034d44..f49a697 100644 --- a/src/args.rs +++ b/src/args.rs @@ -4,11 +4,27 @@ use clap::Parser; #[clap(author, version, about)] /// FM : dired like file manager{n} pub struct Args { - /// Starting path + /// Starting path. directory or file #[arg(short, long, default_value_t = String::from("."))] pub path: String, /// Nvim server #[arg(short, long, default_value_t = String::from(""))] pub server: String, + + /// Dual pane ? default to true + #[arg(short, long, default_value_t = false)] + pub dual: bool, + + /// Display file metadata ? default to true + #[arg(short, long, default_value_t = false)] + pub metadata: bool, + + /// Use second pane as preview ? default to false + #[arg(short = 'P', long, default_value_t = false)] + pub preview: bool, + + /// Display all files (hidden) + #[arg(short, long, default_value_t = false)] + pub all: bool, } diff --git a/src/shell_menu.rs b/src/shell_menu.rs index fb54abd..05fed46 100644 --- a/src/shell_menu.rs +++ b/src/shell_menu.rs @@ -21,6 +21,15 @@ impl Default for ShellMenu { } impl ShellMenu { + pub fn new(path: &str) -> Result<Self> { + let mut shell_menu = Self::default(); + let file = + std::fs::File::open(std::path::Path::new(&shellexpand::tilde(path).to_string()))?; + let yaml = serde_yaml::from_reader(file)?; + shell_menu.update_from_file(&yaml)?; + Ok(shell_menu) + } + fn update_from_file(&mut self, yaml: &serde_yaml::mapping::Mapping) -> Result<()> { for (key, mapping) in yaml.into_iter() { let Some(command) = key.as_str() else { @@ -86,11 +95,3 @@ impl ShellMenu { type SBool = (String, bool); impl_selectable_content!(SBool, ShellMenu); - -pub fn load_shell_menu(path: &str) -> Result<ShellMenu> { - let mut shell_menu = ShellMenu::default(); - let file = std::fs::File::open(std::path::Path::new(&shellexpand::tilde(path).to_string()))?; - let yaml = serde_yaml::from_reader(file)?; - shell_menu.update_from_file(&yaml)?; - Ok(shell_menu) -} diff --git a/src/status.rs b/src/status.rs index d2cf265..4ae9f5a 100644 --- a/src/status.rs +++ b/src/status.rs @@ -35,7 +35,7 @@ use crate::password::{ }; use crate::preview::{Directory, Preview}; use crate::selectable_content::SelectableContent; -use crate::shell_menu::{load_shell_menu, ShellMenu}; +use crate::shell_menu::ShellMenu; use crate::shell_parser::ShellCommandParser; use crate::skim::Skimer; use crate::tab::Tab; @@ -109,50 +109,52 @@ impl Status { settings: &Settings, ) -> Result<Self> { let args = Args::parse(); - let Ok(shell_menu) = load_shell_menu(TUIS_PATH) else { - eprintln!("Couldn't load the TUIs config file at {TUIS_PATH}. See https://raw.githubusercontent.com/qkzk/fm/master/config_files/fm/tuis.yaml for an example"); - info!("Couldn't read tuis file at {TUIS_PATH}. Exiting"); - std::process::exit(1); + let preview_second = args.preview; + let start_folder = std::fs::canonicalize(std::path::PathBuf::from(&args.path))?; + let nvim_server = args.server.clone(); + let display_full = args.metadata || settings.full; + let dual_pane = (args.dual || settings.dual) && Self::display_wide_enough(&term)?; + + let Ok(shell_menu) = ShellMenu::new(TUIS_PATH) else { + Self::quit() }; let cli_info = CliInfo::default(); - let sys = System::new_with_specifics(RefreshKind::new().with_disks()); - let nvim_server = args.server.clone(); let encrypted_devices = CryptoDeviceOpener::default(); let trash = Trash::new()?; let compression = Compresser::default(); let force_clear = false; let bulk = Bulk::default(); - let start_folder = std::fs::canonicalize(std::path::PathBuf::from(&args.path))?; - let dual_pane = settings.dual && term.term_size()?.0 >= MIN_WIDTH_FOR_DUAL_PANE; + let iso_device = None; + let password_holder = PasswordHolder::default(); + let sudo_command = None; + let flagged = Flagged::default(); + let marks = Marks::read_from_config_file(); + let skimer = Skimer::new(term.clone()); + let index = 0; + // unsafe because of UsersCache::with_all_users let users_cache = unsafe { UsersCache::with_all_users() }; - let mut right_tab = Tab::new(args.clone(), height, users_cache)?; - right_tab - .shortcut - .extend_with_mount_points(&Self::disks_mounts(sys.disks())); - // unsafe because of UsersCache::with_all_users let users_cache2 = unsafe { UsersCache::with_all_users() }; - let mut left_tab = Tab::new(args, height, users_cache2)?; - left_tab - .shortcut - .extend_with_mount_points(&Self::disks_mounts(sys.disks())); - let iso_mounter = None; - let password_holder = PasswordHolder::default(); - let sudo_command = None; + let mount_points = Self::disks_mounts(sys.disks()); + + let tabs = [ + Tab::new(&args, height, users_cache, &mount_points)?, + Tab::new(&args, height, users_cache2, &mount_points)?, + ]; Ok(Self { - tabs: [left_tab, right_tab], - index: 0, - flagged: Flagged::default(), - marks: Marks::read_from_config_file(), - skimer: Skimer::new(term.clone()), + tabs, + index, + flagged, + marks, + skimer, term, dual_pane, - preview_second: false, + preview_second, system_info: sys, - display_full: settings.full, + display_full, opener, help, trash, @@ -162,7 +164,7 @@ impl Status { force_clear, bulk, shell_menu, - iso_device: iso_mounter, + iso_device, cli_info, start_folder, password_holder, @@ -170,6 +172,16 @@ impl Status { }) } + fn display_wide_enough(term: &Arc<Term>) -> Result<bool> { + Ok(term.term_size()?.0 >= MIN_WIDTH_FOR_DUAL_PANE) + } + + fn quit() -> ! { + eprintln!("Couldn't load the TUIs config file at {TUIS_PATH}. See https://raw.githubusercontent.com/qkzk/fm/master/config_files/fm/tuis.yaml for an example"); + info!("Couldn't read tuis file at {TUIS_PATH}. Exiting"); + std::process::exit(1); + } + /// Select the other tab if two are displayed. Does nother otherwise. pub fn next(&mut self) { if !self.dual_pane { @@ -58,24 +58,36 @@ pub struct Tab { impl Tab { /// Creates a new tab from args and height. - pub fn new(args: Args, height: usize, users_cache: UsersCache) -> Result<Self> { + pub fn new( + args: &Args, + height: usize, + users_cache: UsersCache, + mount_points: &[&path::Path], + ) -> Result<Self> { let path = std::fs::canonicalize(path::Path::new(&args.path))?; - let directory = Directory::empty(&path, &users_cache)?; + let start_dir = if path.is_dir() { + &path + } else { + path.parent().context("")? + }; + let directory = Directory::empty(start_dir, &users_cache)?; let filter = FilterKind::All; - let show_hidden = false; - let path_content = PathContent::new(&path, users_cache, &filter, show_hidden)?; - let show_hidden = false; + let show_hidden = args.all; + let mut path_content = PathContent::new(start_dir, users_cache, &filter, show_hidden)?; let mode = Mode::Normal; let previous_mode = Mode::Normal; - let window = ContentWindow::new(path_content.content.len(), height); + let mut window = ContentWindow::new(path_content.content.len(), height); let input = Input::default(); let completion = Completion::default(); let must_quit = false; let preview = Preview::Empty; let mut history = History::default(); history.push(&path); - let shortcut = Shortcut::new(&path); + let mut shortcut = Shortcut::new(&path); + shortcut.extend_with_mount_points(mount_points); let searched = None; + let index = path_content.select_file(&path); + window.scroll_to(index); Ok(Self { mode, previous_mode, |