Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ Yazi (means "duck") is a terminal file manager written in Rust, based on non-blo
- 🖼️ **Built-in Support for Multiple Image Protocols**: Also integrated with Überzug++, covering almost all terminals.
- 🌟 **Built-in Code Highlighting and Image Decoding**: Combined with the pre-loading mechanism, greatly accelerates image and normal file loading.
- 🔌 **Concurrent Plugin System**: UI plugins (rewriting most of the UI), functional plugins, custom previewer, and custom preloader; Just some pieces of Lua.
- 📡 **Data Distribution Service**: Built on a client-server architecture (no additional server process required), integrated with a Lua-based publish-subscribe model, achieving cross-instance communication and state persistence.
- 🧰 Integration with fd, rg, fzf, zoxide
- 💫 Vim-like input/select/notify component, auto-completion for cd paths
- 💫 Vim-like input/select/which/notify component, auto-completion for cd paths
- 🏷️ Multi-Tab Support, Cross-directory selection, Scrollable Preview (for videos, PDFs, archives, directories, code, etc.)
- 🔄 Bulk Renaming, Visual Mode, File Chooser
- 🎨 Theme System, Custom Layouts, Trash Bin, CSI u
Expand Down
2 changes: 1 addition & 1 deletion cspell.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"version":"0.2","flagWords":[],"language":"en","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"]}
{"version":"0.2","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"]}
2 changes: 1 addition & 1 deletion yazi-adaptor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ image = "0.24.9"
imagesize = "0.12.0"
kamadak-exif = "0.5.5"
ratatui = "0.26.1"
tokio = { version = "1.36.0", features = [ "parking_lot", "io-util", "process" ] }
tokio = { version = "1.36.0", features = [ "full" ] }

# Logging
tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] }
2 changes: 1 addition & 1 deletion yazi-adaptor/src/kitty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ impl Kitty {
for y in 0..rect.height {
write!(buf, "\x1b[{};{}H\x1b[38;5;1m", rect.y + y + 1, rect.x + 1)?;
for x in 0..rect.width {
write!(buf, "{}", '\u{10EEEE}')?;
write!(buf, "\u{10EEEE}")?;
write!(buf, "{}", *DIACRITICS.get(y as usize).unwrap_or(&DIACRITICS[0]))?;
write!(buf, "{}", *DIACRITICS.get(x as usize).unwrap_or(&DIACRITICS[0]))?;
}
Expand Down
9 changes: 8 additions & 1 deletion yazi-boot/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,21 @@ pub struct Args {
/// Write the cwd on exit to this file
#[arg(long)]
pub cwd_file: Option<PathBuf>,
/// Write the selected files on open emitted by the chooser mode
/// Write the selected files to this file on open fired
#[arg(long)]
pub chooser_file: Option<PathBuf>,

/// Clear the cache directory
#[arg(long, action)]
pub clear_cache: bool,

/// Report the specified local events to stdout
#[arg(long, action)]
pub local_events: Option<String>,
/// Report the specified remote events to stdout
#[arg(long, action)]
pub remote_events: Option<String>,

/// Print debug information
#[arg(long, action)]
pub debug: bool,
Expand Down
23 changes: 21 additions & 2 deletions yazi-boot/src/boot.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{env, ffi::OsString, fmt::Write, path::{Path, PathBuf}, process};
use std::{collections::HashSet, env, ffi::OsString, fmt::Write, path::{Path, PathBuf}, process};

use clap::Parser;
use serde::Serialize;
Expand All @@ -13,9 +13,13 @@ pub struct Boot {
pub cwd: PathBuf,
pub file: Option<OsString>,

pub local_events: HashSet<String>,
pub remote_events: HashSet<String>,

pub config_dir: PathBuf,
pub flavor_dir: PathBuf,
pub plugin_dir: PathBuf,
pub state_dir: PathBuf,
}

impl Boot {
Expand Down Expand Up @@ -84,7 +88,7 @@ impl Boot {
writeln!(
s,
" Version: {:?}",
Command::new(env::var_os("YAZI_FILE_TWO").unwrap_or("file".into())).arg("--version").output()
Command::new(env::var_os("YAZI_FILE_ONE").unwrap_or("file".into())).arg("--version").output()
)?;

writeln!(s, "\nText Opener")?;
Expand Down Expand Up @@ -129,13 +133,28 @@ impl Default for Boot {
let config_dir = Xdg::config_dir();
let (cwd, file) = Self::parse_entry(ARGS.entry.as_deref());

let local_events = ARGS
.local_events
.as_ref()
.map(|s| s.split(',').map(|s| s.to_owned()).collect())
.unwrap_or_default();
let remote_events = ARGS
.remote_events
.as_ref()
.map(|s| s.split(',').map(|s| s.to_owned()).collect())
.unwrap_or_default();

let boot = Self {
cwd,
file,

local_events,
remote_events,

flavor_dir: config_dir.join("flavors"),
plugin_dir: config_dir.join("plugins"),
config_dir,
state_dir: Xdg::state_dir(),
};

std::fs::create_dir_all(&boot.flavor_dir).expect("Failed to create flavor directory");
Expand Down
4 changes: 2 additions & 2 deletions yazi-config/src/plugin/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ impl Plugin {
.iter()
.filter(|&rule| {
rule.cond.as_ref().and_then(|c| c.eval(f)) != Some(false)
&& (rule.name.as_ref().is_some_and(|n| n.match_path(path, is_folder))
|| rule.mime.as_ref().zip(mime).map_or(false, |(m, s)| m.matches(s)))
&& (rule.mime.as_ref().zip(mime).map_or(false, |(m, s)| m.matches(s))
|| rule.name.as_ref().is_some_and(|n| n.match_path(path, is_folder)))
})
.collect()
}
Expand Down
1 change: 1 addition & 0 deletions yazi-config/src/plugin/rule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use yazi_shared::{event::Cmd, Condition};

use crate::{Pattern, Priority, DEPRECATED_EXEC};

#[derive(Debug)]
pub struct PluginRule {
pub id: u8,
pub cond: Option<Condition>,
Expand Down
3 changes: 2 additions & 1 deletion yazi-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ repository = "https://github.com/sxyazi/yazi"
yazi-adaptor = { path = "../yazi-adaptor", version = "0.2.4" }
yazi-boot = { path = "../yazi-boot", version = "0.2.4" }
yazi-config = { path = "../yazi-config", version = "0.2.4" }
yazi-dds = { path = "../yazi-dds", version = "0.2.4" }
yazi-plugin = { path = "../yazi-plugin", version = "0.2.4" }
yazi-proxy = { path = "../yazi-proxy", version = "0.2.4" }
yazi-scheduler = { path = "../yazi-scheduler", version = "0.2.4" }
Expand All @@ -29,7 +30,7 @@ parking_lot = "0.12.1"
ratatui = "0.26.1"
regex = "1.10.3"
serde = "1.0.197"
tokio = { version = "1.36.0", features = [ "parking_lot", "macros", "rt-multi-thread", "sync", "time", "fs", "process", "io-std", "io-util" ] }
tokio = { version = "1.36.0", features = [ "full" ] }
tokio-stream = "0.1.15"
tokio-util = "0.7.10"
unicode-width = "0.1.11"
Expand Down
2 changes: 1 addition & 1 deletion yazi-core/src/clipboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl Clipboard {
use tokio::{io::AsyncWriteExt, process::Command};
use yazi_shared::in_ssh_connection;

*self.content.lock() = s.as_ref().to_owned();
s.as_ref().clone_into(&mut self.content.lock());
if in_ssh_connection() {
execute!(BufWriter::new(stderr()), osc52::SetClipboard::new(s.as_ref())).ok();
}
Expand Down
1 change: 0 additions & 1 deletion yazi-core/src/input/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ impl Input {
true
}

#[inline]
pub(super) fn flush_value(&mut self) {
self.ticket = self.ticket.wrapping_add(1);

Expand Down
4 changes: 2 additions & 2 deletions yazi-core/src/manager/commands/bulk_rename.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ impl Manager {
return Ok(());
}

let _permit = WATCHER.acquire().await.unwrap();
let permit = WATCHER.acquire().await.unwrap();
let (mut failed, mut succeeded) = (Vec::new(), HashMap::with_capacity(todo.len()));
for (o, n) in todo {
let (old, new) = (root.join(&o), root.join(&n));
Expand All @@ -96,7 +96,7 @@ impl Manager {
if !succeeded.is_empty() {
FilesOp::Upserting(cwd, succeeded).emit();
}
drop(_permit);
drop(permit);

if !failed.is_empty() {
Self::output_failed(failed).await?;
Expand Down
2 changes: 1 addition & 1 deletion yazi-core/src/manager/commands/close.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{manager::Manager, tasks::Tasks};
impl Manager {
pub fn close(&mut self, _: Cmd, tasks: &Tasks) {
if self.tabs.len() > 1 {
return self.tabs.close(self.tabs.idx);
return self.tabs.close(self.tabs.cursor);
}
self.quit((), tasks);
}
Expand Down
10 changes: 7 additions & 3 deletions yazi-core/src/manager/commands/rename.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::collections::HashMap;
use anyhow::Result;
use tokio::fs;
use yazi_config::popup::InputCfg;
use yazi_dds::Pubsub;
use yazi_proxy::{InputProxy, ManagerProxy, WATCHER};
use yazi_shared::{event::Cmd, fs::{accessible, File, FilesOp, Url}};

Expand Down Expand Up @@ -49,6 +50,7 @@ impl Manager {
_ => None,
};

let tab = self.tabs.cursor;
tokio::spawn(async move {
let mut result = InputProxy::show(InputCfg::rename().with_value(name).with_cursor(cursor));
let Some(Ok(name)) = result.recv().await else {
Expand All @@ -57,20 +59,20 @@ impl Manager {

let new = hovered.parent().unwrap().join(name);
if opt.force || !accessible(&new).await {
Self::rename_do(hovered, Url::from(new)).await.ok();
Self::rename_do(tab, hovered, Url::from(new)).await.ok();
return;
}

let mut result = InputProxy::show(InputCfg::overwrite());
if let Some(Ok(choice)) = result.recv().await {
if choice == "y" || choice == "Y" {
Self::rename_do(hovered, Url::from(new)).await.ok();
Self::rename_do(tab, hovered, Url::from(new)).await.ok();
}
};
});
}

async fn rename_do(old: Url, new: Url) -> Result<()> {
async fn rename_do(tab: usize, old: Url, new: Url) -> Result<()> {
let _permit = WATCHER.acquire().await.unwrap();

fs::rename(&old, &new).await?;
Expand All @@ -79,6 +81,8 @@ impl Manager {
}

let file = File::from(new.clone()).await?;
Pubsub::pub_from_rename(tab, &old, &new);

FilesOp::Deleting(file.parent().unwrap(), vec![new.clone()]).emit();
FilesOp::Upserting(file.parent().unwrap(), HashMap::from_iter([(old, file)])).emit();
Ok(ManagerProxy::hover(Some(new)))
Expand Down
3 changes: 1 addition & 2 deletions yazi-core/src/manager/commands/suspend.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use yazi_proxy::AppProxy;
use yazi_shared::event::Cmd;

use crate::manager::Manager;
Expand All @@ -7,7 +6,7 @@ impl Manager {
pub fn suspend(&mut self, _: Cmd) {
#[cfg(unix)]
tokio::spawn(async move {
AppProxy::stop().await;
yazi_proxy::AppProxy::stop().await;
unsafe { libc::raise(libc::SIGTSTP) };
});
}
Expand Down
3 changes: 2 additions & 1 deletion yazi-core/src/manager/commands/tab_close.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ impl Tabs {
}

self.items.remove(opt.idx).shutdown();
if opt.idx <= self.idx {
if opt.idx <= self.cursor {
self.set_idx(self.absolute(1));
}

self.reorder();
render!();
}
}
5 changes: 3 additions & 2 deletions yazi-core/src/manager/commands/tab_create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ impl Tabs {
tab.conf = self.active().conf.clone();
tab.apply_files_attrs();

self.items.insert(self.idx + 1, tab);
self.set_idx(self.idx + 1);
self.items.insert(self.cursor + 1, tab);
self.set_idx(self.cursor + 1);
self.reorder();
render!();
}
}
5 changes: 3 additions & 2 deletions yazi-core/src/manager/commands/tab_swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ impl From<Cmd> for Opt {
impl Tabs {
pub fn swap(&mut self, opt: impl Into<Opt>) {
let idx = self.absolute(opt.into().step);
if idx == self.idx {
if idx == self.cursor {
return;
}

self.items.swap(self.idx, idx);
self.items.swap(self.cursor, idx);
self.set_idx(idx);
self.reorder();
render!();
}
}
4 changes: 2 additions & 2 deletions yazi-core/src/manager/commands/tab_switch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ impl Tabs {
pub fn switch(&mut self, opt: impl Into<Opt>) {
let opt = opt.into() as Opt;
let idx = if opt.relative {
(self.idx as isize + opt.step).rem_euclid(self.items.len() as isize) as usize
(self.cursor as isize + opt.step).rem_euclid(self.items.len() as isize) as usize
} else {
opt.step as usize
};

if idx == self.idx || idx >= self.items.len() {
if idx == self.cursor || idx >= self.items.len() {
return;
}

Expand Down
2 changes: 1 addition & 1 deletion yazi-core/src/manager/commands/update_files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl Manager {
}

for op in ops {
let idx = self.tabs.idx;
let idx = self.tabs.cursor;
self.yanked.apply_op(&op);

for (_, tab) in self.tabs.iter_mut().enumerate().filter(|(i, _)| *i != idx) {
Expand Down
2 changes: 1 addition & 1 deletion yazi-core/src/manager/commands/update_mimetype.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::HashMap;

use yazi_plugin::ValueSendable;
use yazi_dds::ValueSendable;
use yazi_shared::{event::Cmd, fs::Url, render};

use crate::{manager::Manager, tasks::Tasks};
Expand Down
Loading