Repository: sxyazi/yazi (language: rust)

Issue:
Start with multiple tabs with different paths
### Please describe the problem you're trying to solve

yazi only works with 1 path.
i dont see any options in the help pages that allows to open multiple tabs on startup.

eg.
`yazi ~/Downloads`

### Would you be willing to contribute this feature?

- [ ] Yes, I'll give it a shot

### Describe the solution you'd like

add option to start yazi with multiple tabs with different paths on startup

`yazi ~/Download ~/Documents ~/Public ~/Music`

### Additional context

_No response_


Discussion / hints:
Sounds good to me, anyone wants to give it a go? 

We need to change the `entry`

https://github.com/sxyazi/yazi/blob/db9332133886f2a2335d56194cae5ac14e3bf0a0/yazi-boot/src/args.rs#L10

to

```rust
pub entries: Vec<PathBuf>
```

and here is the code to parse it:

https://github.com/sxyazi/yazi/blob/db9332133886f2a2335d56194cae5ac14e3bf0a0/yazi-boot/src/boot.rs#L21-L33

Fix patch:
diff --git a/cspell.json b/cspell.json
index ee4662c75..f858aecd1 100644
--- a/cspell.json
+++ b/cspell.json
@@ -1,1 +1,1 @@
-{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek"],"version":"0.2","language":"en","flagWords":[]}
\ No newline at end of file
+{"language":"en","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds"],"version":"0.2"}
\ No newline at end of file
diff --git a/yazi-boot/src/args.rs b/yazi-boot/src/args.rs
index 15b81b15c..f2b4105a2 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<PathBuf>,
+	#[arg(index = 1, num_args = 1..=9)]
+	pub entries: Vec<PathBuf>,
 
 	/// 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..9bbb142c1 100644
--- a/yazi-boot/src/boot.rs
+++ b/yazi-boot/src/boot.rs
@@ -1,12 +1,12 @@
-use std::{collections::HashSet, ffi::OsString, path::{Path, PathBuf}};
+use std::{collections::HashSet, ffi::OsString, path::PathBuf};
 
 use serde::Serialize;
 use yazi_shared::{fs::{current_cwd, expand_path}, Xdg};
 
 #[derive(Debug, Default, Serialize)]
 pub struct Boot {
-	pub cwd:  PathBuf,
-	pub file: Option<OsString>,
+	pub cwds:  Vec<PathBuf>,
+	pub files: Vec<OsString>,
 
 	pub local_events:  HashSet<String>,
 	pub remote_events: HashSet<String>,
@@ -18,25 +18,31 @@ pub struct Boot {
 }
 
 impl Boot {
-	fn parse_entry(entry: Option<&Path>) -> (PathBuf, Option<OsString>) {
-		let entry = match entry {
-			Some(p) => expand_path(p),
-			None => return (current_cwd().unwrap(), None),
-		};
+	fn parse_entries(entries: &[PathBuf]) -> (Vec<PathBuf>, Vec<OsString>) {
+		if entries.is_empty() {
+			return (vec![current_cwd().unwrap()], vec![OsString::new()]);
+		}
 
-		let parent = entry.parent();
-		if parent.is_none() || entry.is_dir() {
-			return (entry, None);
+		let mut cwds = Vec::with_capacity(entries.len());
+		let mut files = Vec::with_capacity(entries.len());
+		for entry in entries.iter().map(expand_path) {
+			if let Some(p) = entry.parent().filter(|_| !entry.is_dir()) {
+				cwds.push(p.to_owned());
+				files.push(entry.file_name().unwrap().to_owned());
+			} else {
+				cwds.push(entry);
+				files.push(OsString::new());
+			}
 		}
 
-		(parent.unwrap().to_owned(), 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 (cwds, files) = Self::parse_entries(&args.entries);
 
 		let local_events = args
 			.local_events
@@ -50,8 +56,8 @@ impl From<&crate::Args> for Boot {
 			.unwrap_or_default();
 
 		Self {
-			cwd,
-			file,
+			cwds,
+			files,
 
 			local_events,
 			remote_events,
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<Cmd> 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 91cf11ead..a005e5719 100644
--- a/yazi-core/src/manager/tabs.rs
+++ b/yazi-core/src/manager/tabs.rs
@@ -13,13 +13,18 @@ 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: (0..BOOT.cwds.len()).map(|_| Tab::default()).collect() };
+		tabs.reorder();
+
+		for (i, tab) in tabs.iter_mut().enumerate() {
+			let file = &BOOT.files[i];
+			if file.is_empty() {
+				tab.cd(Url::from(&BOOT.cwds[i]));
+			} else {
+				tab.reveal(Url::from(BOOT.cwds[i].join(file)));
+			}
 		}
-
 		tabs
 	}
 


Test patch: