From e650a781681843f6d800421895d8603f556fac28 Mon Sep 17 00:00:00 2001 From: coolkiid Date: Wed, 7 Aug 2024 23:33:45 +0800 Subject: [PATCH] feat: implement opening multi tabs at once 1. CLI can receive more than one paths as entries now. 2. Modify BOOT in yazi-boot/ and Tabs in yazi-core to fit the change above. 3. Add a test for parse_entries(). issue: #1322 --- yazi-boot/src/args.rs | 4 +- yazi-boot/src/boot.rs | 72 ++++++++++++++++---- yazi-core/src/manager/commands/tab_create.rs | 5 +- yazi-core/src/manager/tabs.rs | 14 ++-- 4 files changed, 72 insertions(+), 23 deletions(-) diff --git a/yazi-boot/src/args.rs b/yazi-boot/src/args.rs index 15b81b15c..b50211cd9 100644 --- a/yazi-boot/src/args.rs +++ b/yazi-boot/src/args.rs @@ -6,8 +6,8 @@ use clap::{command, Parser}; #[command(name = "yazi")] pub struct Args { /// Set the current working entry - #[arg(index = 1)] - pub entry: Option, + #[arg(index = 1, value_delimiter = ' ', num_args = 1..)] + pub entries: Vec, /// Write the cwd on exit to this file #[arg(long)] diff --git a/yazi-boot/src/boot.rs b/yazi-boot/src/boot.rs index 928d970fe..b9dc26b80 100644 --- a/yazi-boot/src/boot.rs +++ b/yazi-boot/src/boot.rs @@ -5,8 +5,8 @@ use yazi_shared::{fs::{current_cwd, expand_path}, Xdg}; #[derive(Debug, Default, Serialize)] pub struct Boot { - pub cwd: PathBuf, - pub file: Option, + pub cwds: Vec, + pub files: Vec>, pub local_events: HashSet, pub remote_events: HashSet, @@ -18,25 +18,34 @@ pub struct Boot { } impl Boot { - fn parse_entry(entry: Option<&Path>) -> (PathBuf, Option) { - let entry = match entry { - Some(p) => expand_path(p), - None => return (current_cwd().unwrap(), None), - }; - - let parent = entry.parent(); - if parent.is_none() || entry.is_dir() { - return (entry, None); + fn parse_entries(entries: Vec<&Path>) -> (Vec, Vec>) { + if entries.len() == 0 { + return (vec![current_cwd().unwrap()], vec![None]); } - (parent.unwrap().to_owned(), Some(entry.file_name().unwrap().to_owned())) + let mut cwds = vec![]; + let mut files = vec![]; + for entry in entries { + let _entry = expand_path(entry); + let parent = _entry.parent(); + if parent.is_none() || _entry.is_dir() { + cwds.push(_entry); + files.push(None); + } else { + cwds.push(parent.unwrap().to_owned()); + files.push(Some(_entry.file_name().unwrap().to_owned())); + } + } + + (cwds, files) } } impl From<&crate::Args> for Boot { fn from(args: &crate::Args) -> Self { let config_dir = Xdg::config_dir(); - let (cwd, file) = Self::parse_entry(args.entry.as_deref()); + let entries = args.entries.iter().map(PathBuf::as_path).collect(); + let (cwds, files) = Self::parse_entries(entries); let local_events = args .local_events @@ -50,8 +59,8 @@ impl From<&crate::Args> for Boot { .unwrap_or_default(); Self { - cwd, - file, + cwds, + files, local_events, remote_events, @@ -63,3 +72,36 @@ impl From<&crate::Args> for Boot { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_entries() { + use std::{env::temp_dir, fs}; + + let foo_dir = temp_dir().join(&Path::new("foo")); + let bar_dir = temp_dir().join(&Path::new("bar")); + let poem_path = &foo_dir.join(&Path::new("poem.txt")); + + let _ = fs::create_dir_all(&foo_dir); + let _ = fs::create_dir_all(&bar_dir); + let _ = + fs::OpenOptions::new().create(true).write(true).open(foo_dir.join(&Path::new("poem.txt"))); + + assert_eq!(Boot::parse_entries(vec![]), (vec![current_cwd().unwrap()], vec![None])); + assert_eq!(Boot::parse_entries(vec![&foo_dir]), (vec![foo_dir.clone()], vec![None])); + assert_eq!( + Boot::parse_entries(vec![&poem_path]), + (vec![foo_dir.clone()], vec![Some(OsString::from("poem.txt"))]) + ); + assert_eq!( + Boot::parse_entries(vec![&foo_dir, &bar_dir]), + (vec![foo_dir.clone(), bar_dir.clone()], vec![None, None]) + ); + + let _ = fs::remove_dir_all(&foo_dir); + let _ = fs::remove_dir_all(&bar_dir); + } +} diff --git a/yazi-core/src/manager/commands/tab_create.rs b/yazi-core/src/manager/commands/tab_create.rs index b31814d48..d3216b1c6 100644 --- a/yazi-core/src/manager/commands/tab_create.rs +++ b/yazi-core/src/manager/commands/tab_create.rs @@ -17,7 +17,10 @@ impl From for Opt { Self { url: Default::default(), current: true } } else { Self { - url: c.take_first().and_then(Data::into_url).unwrap_or_else(|| Url::from(&BOOT.cwd)), + url: c + .take_first() + .and_then(Data::into_url) + .unwrap_or_else(|| Url::from(&BOOT.cwds[0])), current: false, } } diff --git a/yazi-core/src/manager/tabs.rs b/yazi-core/src/manager/tabs.rs index 100f6afea..fab714d76 100644 --- a/yazi-core/src/manager/tabs.rs +++ b/yazi-core/src/manager/tabs.rs @@ -13,11 +13,15 @@ pub struct Tabs { impl Tabs { pub fn make() -> Self { - let mut tabs = Self { cursor: 0, items: vec![Tab::default()] }; - if let Some(file) = &BOOT.file { - tabs.items[0].reveal(Url::from(BOOT.cwd.join(file))); - } else { - tabs.items[0].cd(Url::from(&BOOT.cwd)); + let mut tabs = Self { cursor: 0, items: vec![] }; + for (i, file) in BOOT.files.iter().enumerate() { + let mut tab = Tab::default(); + if let Some(f) = file { + tab.reveal(Url::from(BOOT.cwds[i].join(f))); + } else { + tab.cd(Url::from(&BOOT.cwds[i])); + } + tabs.push(tab); } tabs