Compare commits

...

7 Commits

Author SHA1 Message Date
Xerxes-2
6a64d6aa6b
Merge 1182ce69ec into 987b1d5c49 2024-07-01 13:32:59 -07:00
sxyazi
987b1d5c49
feat: simplify keybindings (#1241) 2024-07-01 23:58:03 +08:00
三咲雅 · Misaki Masa
1a1da216ca
feat: support right-click to open files (#1232) 2024-06-29 11:25:45 +08:00
hankertrix
9961251248
feat: add --hovered option to the rename and remove commands (#1227)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2024-06-28 08:59:23 +08:00
三咲雅 · Misaki Masa
626053da25
perf!: reimplement and significantly speed up archive previewing (#1220) 2024-06-27 00:47:56 +08:00
三咲雅 · Misaki Masa
c64530b35b
fix: magick plugin not working properly (#1213) 2024-06-25 23:49:54 +08:00
三咲雅 · Misaki Masa
9a5b75662a
fix: ueberzug image adapter should respect the user's max_width and max_height settings (#1200) 2024-06-24 18:47:31 +08:00
56 changed files with 667 additions and 508 deletions

22
Cargo.lock generated
View File

@ -1024,6 +1024,15 @@ dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.11"
@ -1242,7 +1251,7 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09697a6cec88e7f58a02c7ab5c18c611c6907c8654613df9cc0192658a4fb859"
dependencies = [
"itertools",
"itertools 0.12.1",
"once_cell",
"proc-macro-error",
"proc-macro2",
@ -1575,19 +1584,20 @@ dependencies = [
[[package]]
name = "ratatui"
version = "0.26.3"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f44c9e68fd46eda15c646fbb85e1040b657a58cdc8c98db1d97a55930d991eef"
checksum = "d16546c5b5962abf8ce6e2881e722b4e0ae3b6f1a08a26ae3573c55853ca68d3"
dependencies = [
"bitflags 2.5.0",
"cassowary",
"compact_str",
"crossterm",
"itertools",
"itertools 0.13.0",
"lru",
"paste",
"stability",
"strum",
"strum_macros",
"unicode-segmentation",
"unicode-truncate",
"unicode-width",
@ -2291,7 +2301,7 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5fbabedabe362c618c714dbefda9927b5afc8e2a8102f47f081089a9019226"
dependencies = [
"itertools",
"itertools 0.12.1",
"unicode-width",
]
@ -2892,8 +2902,6 @@ dependencies = [
"mlua",
"parking_lot",
"ratatui",
"serde",
"serde_json",
"shell-escape",
"shell-words",
"syntect",

View File

@ -23,7 +23,7 @@ futures = "0.3.30"
image = "=0.24.9"
imagesize = "0.13.0"
kamadak-exif = "0.5.5"
ratatui = "0.26.3"
ratatui = "0.27.0"
scopeguard = "1.2.0"
tokio = { version = "1.38.0", features = [ "full" ] }

View File

@ -8,7 +8,7 @@ use tracing::{debug, warn};
use yazi_config::PREVIEW;
use yazi_shared::RoCell;
use crate::{Adapter, Image};
use crate::{Adapter, Dimension};
#[allow(clippy::type_complexity)]
static DEMON: RoCell<Option<UnboundedSender<Option<(PathBuf, Rect)>>>> = RoCell::new();
@ -50,9 +50,16 @@ impl Ueberzug {
let ImageSize { width: w, height: h } =
tokio::task::spawn_blocking(move || imagesize::size(p)).await??;
let area = Image::pixel_area((w as u32, h as u32), max);
tx.send(Some((path.to_owned(), area)))?;
let area = Dimension::ratio()
.map(|(r1, r2)| Rect {
x: max.x,
y: max.y,
width: max.width.min((w.min(PREVIEW.max_width as _) as f64 / r1).ceil() as _),
height: max.height.min((h.min(PREVIEW.max_height as _) as f64 / r2).ceil() as _),
})
.unwrap_or(max);
tx.send(Some((path.to_owned(), area)))?;
Adapter::shown_store(area);
Ok(area)
}

View File

@ -45,7 +45,7 @@ impl CommandPub {
pub(super) fn receiver(&self) -> Result<u64> {
if let Some(receiver) = self.receiver {
Ok(receiver)
} else if let Ok(s) = std::env::var("YAZI_ID") {
} else if let Some(s) = std::env::var("YAZI_PID").ok().filter(|s| !s.is_empty()) {
Ok(s.parse()?)
} else {
bail!("No receiver ID provided, also no YAZI_ID environment variable found.")

View File

@ -18,7 +18,7 @@ bitflags = "2.5.0"
crossterm = "0.27.0"
globset = "0.4.14"
indexmap = "2.2.6"
ratatui = "0.26.3"
ratatui = "0.27.0"
serde = { version = "1.0.203", features = [ "derive" ] }
toml = { version = "0.8.14", features = [ "preserve_order" ] }
validator = { version = "0.18.1", features = [ "derive" ] }

View File

@ -5,84 +5,84 @@
[manager]
keymap = [
{ on = [ "<Esc>" ], run = "escape", desc = "Exit visual mode, clear selected, or cancel search" },
{ on = [ "<C-[>" ], run = "escape", desc = "Exit visual mode, clear selected, or cancel search" },
{ on = [ "q" ], run = "quit", desc = "Exit the process" },
{ on = [ "Q" ], run = "quit --no-cwd-file", desc = "Exit the process without writing cwd-file" },
{ on = [ "<C-c>" ], run = "close", desc = "Close the current tab, or quit if it is last tab" },
{ on = [ "<C-z>" ], run = "suspend", desc = "Suspend the process" },
{ on = "<Esc>", run = "escape", desc = "Exit visual mode, clear selected, or cancel search" },
{ on = "<C-[>", run = "escape", desc = "Exit visual mode, clear selected, or cancel search" },
{ on = "q", run = "quit", desc = "Exit the process" },
{ on = "Q", run = "quit --no-cwd-file", desc = "Exit the process without writing cwd-file" },
{ on = "<C-c>", run = "close", desc = "Close the current tab, or quit if it is last tab" },
{ on = "<C-z>", run = "suspend", desc = "Suspend the process" },
# Navigation
{ on = [ "k" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "j" ], run = "arrow 1", desc = "Move cursor down" },
{ on = "k", run = "arrow -1", desc = "Move cursor up" },
{ on = "j", run = "arrow 1", desc = "Move cursor down" },
{ on = [ "K" ], run = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = [ "J" ], run = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = "K", run = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = "J", run = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = [ "<S-Up>" ], run = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = [ "<S-Down>" ], run = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = "<S-Up>", run = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = "<S-Down>", run = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = [ "<C-u>" ], run = "arrow -50%", desc = "Move cursor up half page" },
{ on = [ "<C-d>" ], run = "arrow 50%", desc = "Move cursor down half page" },
{ on = [ "<C-b>" ], run = "arrow -100%", desc = "Move cursor up one page" },
{ on = [ "<C-f>" ], run = "arrow 100%", desc = "Move cursor down one page" },
{ on = "<C-u>", run = "arrow -50%", desc = "Move cursor up half page" },
{ on = "<C-d>", run = "arrow 50%", desc = "Move cursor down half page" },
{ on = "<C-b>", run = "arrow -100%", desc = "Move cursor up one page" },
{ on = "<C-f>", run = "arrow 100%", desc = "Move cursor down one page" },
{ on = [ "<C-PageUp>" ], run = "arrow -50%", desc = "Move cursor up half page" },
{ on = [ "<C-PageDown>" ], run = "arrow 50%", desc = "Move cursor down half page" },
{ on = [ "<PageUp>" ], run = "arrow -100%", desc = "Move cursor up one page" },
{ on = [ "<PageDown>" ], run = "arrow 100%", desc = "Move cursor down one page" },
{ on = "<C-PageUp>", run = "arrow -50%", desc = "Move cursor up half page" },
{ on = "<C-PageDown>", run = "arrow 50%", desc = "Move cursor down half page" },
{ on = "<PageUp>", run = "arrow -100%", desc = "Move cursor up one page" },
{ on = "<PageDown>", run = "arrow 100%", desc = "Move cursor down one page" },
{ on = [ "h" ], run = "leave", desc = "Go back to the parent directory" },
{ on = [ "l" ], run = "enter", desc = "Enter the child directory" },
{ on = "h", run = "leave", desc = "Go back to the parent directory" },
{ on = "l", run = "enter", desc = "Enter the child directory" },
{ on = [ "H" ], run = "back", desc = "Go back to the previous directory" },
{ on = [ "L" ], run = "forward", desc = "Go forward to the next directory" },
{ on = "H", run = "back", desc = "Go back to the previous directory" },
{ on = "L", run = "forward", desc = "Go forward to the next directory" },
{ on = [ "<A-k>" ], run = "seek -5", desc = "Seek up 5 units in the preview" },
{ on = [ "<A-j>" ], run = "seek 5", desc = "Seek down 5 units in the preview" },
{ on = [ "<A-PageUp>" ], run = "seek -5", desc = "Seek up 5 units in the preview" },
{ on = [ "<A-PageDown>" ], run = "seek 5", desc = "Seek down 5 units in the preview" },
{ on = "<A-k>", run = "seek -5", desc = "Seek up 5 units in the preview" },
{ on = "<A-j>", run = "seek 5", desc = "Seek down 5 units in the preview" },
{ on = "<A-PageUp>", run = "seek -5", desc = "Seek up 5 units in the preview" },
{ on = "<A-PageDown>", run = "seek 5", desc = "Seek down 5 units in the preview" },
{ on = [ "<Up>" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "<Down>" ], run = "arrow 1", desc = "Move cursor down" },
{ on = [ "<Left>" ], run = "leave", desc = "Go back to the parent directory" },
{ on = [ "<Right>" ], run = "enter", desc = "Enter the child directory" },
{ on = "<Up>", run = "arrow -1", desc = "Move cursor up" },
{ on = "<Down>", run = "arrow 1", desc = "Move cursor down" },
{ on = "<Left>", run = "leave", desc = "Go back to the parent directory" },
{ on = "<Right>", run = "enter", desc = "Enter the child directory" },
{ on = [ "g", "g" ], run = "arrow -99999999", desc = "Move cursor to the top" },
{ on = [ "G" ], run = "arrow 99999999", desc = "Move cursor to the bottom" },
{ on = "G", run = "arrow 99999999", desc = "Move cursor to the bottom" },
# Selection
{ on = [ "<Space>" ], run = [ "select --state=none", "arrow 1" ], desc = "Toggle the current selection state" },
{ on = [ "v" ], run = "visual_mode", desc = "Enter visual mode (selection mode)" },
{ on = [ "V" ], run = "visual_mode --unset", desc = "Enter visual mode (unset mode)" },
{ on = [ "<C-a>" ], run = "select_all --state=true", desc = "Select all files" },
{ on = [ "<C-r>" ], run = "select_all --state=none", desc = "Inverse selection of all files" },
{ on = "<Space>", run = [ "select --state=none", "arrow 1" ], desc = "Toggle the current selection state" },
{ on = "v", run = "visual_mode", desc = "Enter visual mode (selection mode)" },
{ on = "V", run = "visual_mode --unset", desc = "Enter visual mode (unset mode)" },
{ on = "<C-a>", run = "select_all --state=true", desc = "Select all files" },
{ on = "<C-r>", run = "select_all --state=none", desc = "Inverse selection of all files" },
# Operation
{ on = [ "o" ], run = "open", desc = "Open the selected files" },
{ on = [ "O" ], run = "open --interactive", desc = "Open the selected files interactively" },
{ on = [ "<Enter>" ], run = "open", desc = "Open the selected files" },
{ on = [ "<S-Enter>" ], run = "open --interactive", desc = "Open the selected files interactively" },
{ on = [ "y" ], run = "yank", desc = "Copy the selected files" },
{ on = [ "Y" ], run = "unyank", desc = "Cancel the yank status of files" },
{ on = [ "x" ], run = "yank --cut", desc = "Cut the selected files" },
{ on = [ "X" ], run = "unyank", desc = "Cancel the yank status of files" },
{ on = [ "p" ], run = "paste", desc = "Paste the files" },
{ on = [ "P" ], run = "paste --force", desc = "Paste the files (overwrite if the destination exists)" },
{ on = [ "-" ], run = "link", desc = "Symlink the absolute path of files" },
{ on = [ "_" ], run = "link --relative", desc = "Symlink the relative path of files" },
{ on = [ "d" ], run = "remove", desc = "Move the files to the trash" },
{ on = [ "D" ], run = "remove --permanently", desc = "Permanently delete the files" },
{ on = [ "a" ], run = "create", desc = "Create a file or directory (ends with / for directories)" },
{ on = [ "r" ], run = "rename --cursor=before_ext", desc = "Rename a file or directory" },
{ on = [ ";" ], run = "shell", desc = "Run a shell command" },
{ on = [ ":" ], run = "shell --block", desc = "Run a shell command (block the UI until the command finishes)" },
{ on = [ "." ], run = "hidden toggle", desc = "Toggle the visibility of hidden files" },
{ on = [ "s" ], run = "search fd", desc = "Search files by name using fd" },
{ on = [ "S" ], run = "search rg", desc = "Search files by content using ripgrep" },
{ on = [ "<C-s>" ], run = "search none", desc = "Cancel the ongoing search" },
{ on = [ "z" ], run = "plugin zoxide", desc = "Jump to a directory using zoxide" },
{ on = [ "Z" ], run = "plugin fzf", desc = "Jump to a directory, or reveal a file using fzf" },
{ on = "o", run = "open", desc = "Open the selected files" },
{ on = "O", run = "open --interactive", desc = "Open the selected files interactively" },
{ on = "<Enter>", run = "open", desc = "Open the selected files" },
{ on = "<S-Enter>", run = "open --interactive", desc = "Open the selected files interactively" },
{ on = "y", run = "yank", desc = "Copy the selected files" },
{ on = "Y", run = "unyank", desc = "Cancel the yank status of files" },
{ on = "x", run = "yank --cut", desc = "Cut the selected files" },
{ on = "X", run = "unyank", desc = "Cancel the yank status of files" },
{ on = "p", run = "paste", desc = "Paste the files" },
{ on = "P", run = "paste --force", desc = "Paste the files (overwrite if the destination exists)" },
{ on = "-", run = "link", desc = "Symlink the absolute path of files" },
{ on = "_", run = "link --relative", desc = "Symlink the relative path of files" },
{ on = "d", run = "remove", desc = "Move the files to the trash" },
{ on = "D", run = "remove --permanently", desc = "Permanently delete the files" },
{ on = "a", run = "create", desc = "Create a file or directory (ends with / for directories)" },
{ on = "r", run = "rename --cursor=before_ext", desc = "Rename a file or directory" },
{ on = ";", run = "shell --interactive", desc = "Run a shell command" },
{ on = ":", run = "shell --block --interactive", desc = "Run a shell command (block the UI until the command finishes)" },
{ on = ".", run = "hidden toggle", desc = "Toggle the visibility of hidden files" },
{ on = "s", run = "search fd", desc = "Search files by name using fd" },
{ on = "S", run = "search rg", desc = "Search files by content using ripgrep" },
{ on = "<C-s>", run = "search none", desc = "Cancel the ongoing search" },
{ on = "z", run = "plugin zoxide", desc = "Jump to a directory using zoxide" },
{ on = "Z", run = "plugin fzf", desc = "Jump to a directory, or reveal a file using fzf" },
# Linemode
{ on = [ "m", "s" ], run = "linemode size", desc = "Set linemode to size" },
@ -97,13 +97,13 @@ keymap = [
{ on = [ "c", "n" ], run = "copy name_without_ext", desc = "Copy the name of the file without the extension" },
# Filter
{ on = [ "f" ], run = "filter --smart", desc = "Filter the files" },
{ on = "f", run = "filter --smart", desc = "Filter the files" },
# Find
{ on = [ "/" ], run = "find --smart", desc = "Find next file" },
{ on = [ "?" ], run = "find --previous --smart", desc = "Find previous file" },
{ on = [ "n" ], run = "find_arrow", desc = "Go to next found file" },
{ on = [ "N" ], run = "find_arrow --previous", desc = "Go to previous found file" },
{ on = "/", run = "find --smart", desc = "Find next file" },
{ on = "?", run = "find --previous --smart", desc = "Find previous file" },
{ on = "n", run = "find_arrow", desc = "Go to next found file" },
{ on = "N", run = "find_arrow --previous", desc = "Go to previous found file" },
# Sorting
{ on = [ ",", "m" ], run = "sort modified --reverse=no", desc = "Sort by modified time" },
@ -120,26 +120,26 @@ keymap = [
{ on = [ ",", "S" ], run = "sort size --reverse", desc = "Sort by size (reverse)" },
# Tabs
{ on = [ "t" ], run = "tab_create --current", desc = "Create a new tab using the current path" },
{ on = "t", run = "tab_create --current", desc = "Create a new tab using the current path" },
{ on = [ "1" ], run = "tab_switch 0", desc = "Switch to the first tab" },
{ on = [ "2" ], run = "tab_switch 1", desc = "Switch to the second tab" },
{ on = [ "3" ], run = "tab_switch 2", desc = "Switch to the third tab" },
{ on = [ "4" ], run = "tab_switch 3", desc = "Switch to the fourth tab" },
{ on = [ "5" ], run = "tab_switch 4", desc = "Switch to the fifth tab" },
{ on = [ "6" ], run = "tab_switch 5", desc = "Switch to the sixth tab" },
{ on = [ "7" ], run = "tab_switch 6", desc = "Switch to the seventh tab" },
{ on = [ "8" ], run = "tab_switch 7", desc = "Switch to the eighth tab" },
{ on = [ "9" ], run = "tab_switch 8", desc = "Switch to the ninth tab" },
{ on = "1", run = "tab_switch 0", desc = "Switch to the first tab" },
{ on = "2", run = "tab_switch 1", desc = "Switch to the second tab" },
{ on = "3", run = "tab_switch 2", desc = "Switch to the third tab" },
{ on = "4", run = "tab_switch 3", desc = "Switch to the fourth tab" },
{ on = "5", run = "tab_switch 4", desc = "Switch to the fifth tab" },
{ on = "6", run = "tab_switch 5", desc = "Switch to the sixth tab" },
{ on = "7", run = "tab_switch 6", desc = "Switch to the seventh tab" },
{ on = "8", run = "tab_switch 7", desc = "Switch to the eighth tab" },
{ on = "9", run = "tab_switch 8", desc = "Switch to the ninth tab" },
{ on = [ "[" ], run = "tab_switch -1 --relative", desc = "Switch to the previous tab" },
{ on = [ "]" ], run = "tab_switch 1 --relative", desc = "Switch to the next tab" },
{ on = "[", run = "tab_switch -1 --relative", desc = "Switch to the previous tab" },
{ on = "]", run = "tab_switch 1 --relative", desc = "Switch to the next tab" },
{ on = [ "{" ], run = "tab_swap -1", desc = "Swap the current tab with the previous tab" },
{ on = [ "}" ], run = "tab_swap 1", desc = "Swap the current tab with the next tab" },
{ on = "{", run = "tab_swap -1", desc = "Swap the current tab with the previous tab" },
{ on = "}", run = "tab_swap 1", desc = "Swap the current tab with the next tab" },
# Tasks
{ on = [ "w" ], run = "tasks_show", desc = "Show the tasks manager" },
{ on = "w", run = "tasks_show", desc = "Show the tasks manager" },
# Goto
{ on = [ "g", "h" ], run = "cd ~", desc = "Go to the home directory" },
@ -148,161 +148,161 @@ keymap = [
{ on = [ "g", "<Space>" ], run = "cd --interactive", desc = "Go to a directory interactively" },
# Help
{ on = [ "~" ], run = "help", desc = "Open help" },
{ on = "~", run = "help", desc = "Open help" },
]
[tasks]
keymap = [
{ on = [ "<Esc>" ], run = "close", desc = "Hide the task manager" },
{ on = [ "<C-[>" ], run = "close", desc = "Hide the task manager" },
{ on = [ "<C-c>" ], run = "close", desc = "Hide the task manager" },
{ on = [ "w" ], run = "close", desc = "Hide the task manager" },
{ on = "<Esc>", run = "close", desc = "Hide the task manager" },
{ on = "<C-[>", run = "close", desc = "Hide the task manager" },
{ on = "<C-c>", run = "close", desc = "Hide the task manager" },
{ on = "w", run = "close", desc = "Hide the task manager" },
{ on = [ "k" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "j" ], run = "arrow 1", desc = "Move cursor down" },
{ on = "k", run = "arrow -1", desc = "Move cursor up" },
{ on = "j", run = "arrow 1", desc = "Move cursor down" },
{ on = [ "<Up>" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "<Down>" ], run = "arrow 1", desc = "Move cursor down" },
{ on = "<Up>", run = "arrow -1", desc = "Move cursor up" },
{ on = "<Down>", run = "arrow 1", desc = "Move cursor down" },
{ on = [ "<Enter>" ], run = "inspect", desc = "Inspect the task" },
{ on = [ "x" ], run = "cancel", desc = "Cancel the task" },
{ on = "<Enter>", run = "inspect", desc = "Inspect the task" },
{ on = "x", run = "cancel", desc = "Cancel the task" },
{ on = [ "~" ], run = "help", desc = "Open help" }
{ on = "~", run = "help", desc = "Open help" }
]
[select]
keymap = [
{ on = [ "<Esc>" ], run = "close", desc = "Cancel selection" },
{ on = [ "<C-[>" ], run = "close", desc = "Cancel selection" },
{ on = [ "<C-c>" ], run = "close", desc = "Cancel selection" },
{ on = [ "<Enter>" ], run = "close --submit", desc = "Submit the selection" },
{ on = "<Esc>", run = "close", desc = "Cancel selection" },
{ on = "<C-[>", run = "close", desc = "Cancel selection" },
{ on = "<C-c>", run = "close", desc = "Cancel selection" },
{ on = "<Enter>", run = "close --submit", desc = "Submit the selection" },
{ on = [ "k" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "j" ], run = "arrow 1", desc = "Move cursor down" },
{ on = "k", run = "arrow -1", desc = "Move cursor up" },
{ on = "j", run = "arrow 1", desc = "Move cursor down" },
{ on = [ "K" ], run = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = [ "J" ], run = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = "K", run = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = "J", run = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = [ "<Up>" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "<Down>" ], run = "arrow 1", desc = "Move cursor down" },
{ on = "<Up>", run = "arrow -1", desc = "Move cursor up" },
{ on = "<Down>", run = "arrow 1", desc = "Move cursor down" },
{ on = [ "<S-Up>" ], run = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = [ "<S-Down>" ], run = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = "<S-Up>", run = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = "<S-Down>", run = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = [ "~" ], run = "help", desc = "Open help" }
{ on = "~", run = "help", desc = "Open help" }
]
[input]
keymap = [
{ on = [ "<C-c>" ], run = "close", desc = "Cancel input" },
{ on = [ "<Enter>" ], run = "close --submit", desc = "Submit the input" },
{ on = [ "<Esc>" ], run = "escape", desc = "Go back the normal mode, or cancel input" },
{ on = [ "<C-[>" ], run = "escape", desc = "Go back the normal mode, or cancel input" },
{ on = "<C-c>", run = "close", desc = "Cancel input" },
{ on = "<Enter>", run = "close --submit", desc = "Submit the input" },
{ on = "<Esc>", run = "escape", desc = "Go back the normal mode, or cancel input" },
{ on = "<C-[>", run = "escape", desc = "Go back the normal mode, or cancel input" },
# Mode
{ on = [ "i" ], run = "insert", desc = "Enter insert mode" },
{ on = [ "a" ], run = "insert --append", desc = "Enter append mode" },
{ on = [ "I" ], run = [ "move -999", "insert" ], desc = "Move to the BOL, and enter insert mode" },
{ on = [ "A" ], run = [ "move 999", "insert --append" ], desc = "Move to the EOL, and enter append mode" },
{ on = [ "v" ], run = "visual", desc = "Enter visual mode" },
{ on = [ "V" ], run = [ "move -999", "visual", "move 999" ], desc = "Enter visual mode and select all" },
{ on = "i", run = "insert", desc = "Enter insert mode" },
{ on = "a", run = "insert --append", desc = "Enter append mode" },
{ on = "I", run = [ "move -999", "insert" ], desc = "Move to the BOL, and enter insert mode" },
{ on = "A", run = [ "move 999", "insert --append" ], desc = "Move to the EOL, and enter append mode" },
{ on = "v", run = "visual", desc = "Enter visual mode" },
{ on = "V", run = [ "move -999", "visual", "move 999" ], desc = "Enter visual mode and select all" },
# Character-wise movement
{ on = [ "h" ], run = "move -1", desc = "Move back a character" },
{ on = [ "l" ], run = "move 1", desc = "Move forward a character" },
{ on = [ "<Left>" ], run = "move -1", desc = "Move back a character" },
{ on = [ "<Right>" ], run = "move 1", desc = "Move forward a character" },
{ on = [ "<C-b>" ], run = "move -1", desc = "Move back a character" },
{ on = [ "<C-f>" ], run = "move 1", desc = "Move forward a character" },
{ on = "h", run = "move -1", desc = "Move back a character" },
{ on = "l", run = "move 1", desc = "Move forward a character" },
{ on = "<Left>", run = "move -1", desc = "Move back a character" },
{ on = "<Right>", run = "move 1", desc = "Move forward a character" },
{ on = "<C-b>", run = "move -1", desc = "Move back a character" },
{ on = "<C-f>", run = "move 1", desc = "Move forward a character" },
# Word-wise movement
{ on = [ "b" ], run = "backward", desc = "Move back to the start of the current or previous word" },
{ on = [ "w" ], run = "forward", desc = "Move forward to the start of the next word" },
{ on = [ "e" ], run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" },
{ on = [ "<A-b>" ], run = "backward", desc = "Move back to the start of the current or previous word" },
{ on = [ "<A-f>" ], run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" },
{ on = "b", run = "backward", desc = "Move back to the start of the current or previous word" },
{ on = "w", run = "forward", desc = "Move forward to the start of the next word" },
{ on = "e", run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" },
{ on = "<A-b>", run = "backward", desc = "Move back to the start of the current or previous word" },
{ on = "<A-f>", run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" },
# Line-wise movement
{ on = [ "0" ], run = "move -999", desc = "Move to the BOL" },
{ on = [ "$" ], run = "move 999", desc = "Move to the EOL" },
{ on = [ "<C-a>" ], run = "move -999", desc = "Move to the BOL" },
{ on = [ "<C-e>" ], run = "move 999", desc = "Move to the EOL" },
{ on = [ "<Home>" ], run = "move -999", desc = "Move to the BOL" },
{ on = [ "<End>" ], run = "move 999", desc = "Move to the EOL" },
{ on = "0", run = "move -999", desc = "Move to the BOL" },
{ on = "$", run = "move 999", desc = "Move to the EOL" },
{ on = "<C-a>", run = "move -999", desc = "Move to the BOL" },
{ on = "<C-e>", run = "move 999", desc = "Move to the EOL" },
{ on = "<Home>", run = "move -999", desc = "Move to the BOL" },
{ on = "<End>", run = "move 999", desc = "Move to the EOL" },
# Delete
{ on = [ "<Backspace>" ], run = "backspace", desc = "Delete the character before the cursor" },
{ on = [ "<Delete>" ], run = "backspace --under", desc = "Delete the character under the cursor" },
{ on = [ "<C-h>" ], run = "backspace", desc = "Delete the character before the cursor" },
{ on = [ "<C-d>" ], run = "backspace --under", desc = "Delete the character under the cursor" },
{ on = "<Backspace>", run = "backspace", desc = "Delete the character before the cursor" },
{ on = "<Delete>", run = "backspace --under", desc = "Delete the character under the cursor" },
{ on = "<C-h>", run = "backspace", desc = "Delete the character before the cursor" },
{ on = "<C-d>", run = "backspace --under", desc = "Delete the character under the cursor" },
# Kill
{ on = [ "<C-u>" ], run = "kill bol", desc = "Kill backwards to the BOL" },
{ on = [ "<C-k>" ], run = "kill eol", desc = "Kill forwards to the EOL" },
{ on = [ "<C-w>" ], run = "kill backward", desc = "Kill backwards to the start of the current word" },
{ on = [ "<A-d>" ], run = "kill forward", desc = "Kill forwards to the end of the current word" },
{ on = "<C-u>", run = "kill bol", desc = "Kill backwards to the BOL" },
{ on = "<C-k>", run = "kill eol", desc = "Kill forwards to the EOL" },
{ on = "<C-w>", run = "kill backward", desc = "Kill backwards to the start of the current word" },
{ on = "<A-d>", run = "kill forward", desc = "Kill forwards to the end of the current word" },
# Cut/Yank/Paste
{ on = [ "d" ], run = "delete --cut", desc = "Cut the selected characters" },
{ on = [ "D" ], run = [ "delete --cut", "move 999" ], desc = "Cut until the EOL" },
{ on = [ "c" ], run = "delete --cut --insert", desc = "Cut the selected characters, and enter insert mode" },
{ on = [ "C" ], run = [ "delete --cut --insert", "move 999" ], desc = "Cut until the EOL, and enter insert mode" },
{ on = [ "x" ], run = [ "delete --cut", "move 1 --in-operating" ], desc = "Cut the current character" },
{ on = [ "y" ], run = "yank", desc = "Copy the selected characters" },
{ on = [ "p" ], run = "paste", desc = "Paste the copied characters after the cursor" },
{ on = [ "P" ], run = "paste --before", desc = "Paste the copied characters before the cursor" },
{ on = "d", run = "delete --cut", desc = "Cut the selected characters" },
{ on = "D", run = [ "delete --cut", "move 999" ], desc = "Cut until the EOL" },
{ on = "c", run = "delete --cut --insert", desc = "Cut the selected characters, and enter insert mode" },
{ on = "C", run = [ "delete --cut --insert", "move 999" ], desc = "Cut until the EOL, and enter insert mode" },
{ on = "x", run = [ "delete --cut", "move 1 --in-operating" ], desc = "Cut the current character" },
{ on = "y", run = "yank", desc = "Copy the selected characters" },
{ on = "p", run = "paste", desc = "Paste the copied characters after the cursor" },
{ on = "P", run = "paste --before", desc = "Paste the copied characters before the cursor" },
# Undo/Redo
{ on = [ "u" ], run = "undo", desc = "Undo the last operation" },
{ on = [ "<C-r>" ], run = "redo", desc = "Redo the last operation" },
{ on = "u", run = "undo", desc = "Undo the last operation" },
{ on = "<C-r>", run = "redo", desc = "Redo the last operation" },
# Help
{ on = [ "~" ], run = "help", desc = "Open help" }
{ on = "~", run = "help", desc = "Open help" }
]
[completion]
keymap = [
{ on = [ "<C-c>" ], run = "close", desc = "Cancel completion" },
{ on = [ "<Tab>" ], run = "close --submit", desc = "Submit the completion" },
{ on = [ "<Enter>" ], run = [ "close --submit", "close_input --submit" ], desc = "Submit the completion and input" },
{ on = "<C-c>", run = "close", desc = "Cancel completion" },
{ on = "<Tab>", run = "close --submit", desc = "Submit the completion" },
{ on = "<Enter>", run = [ "close --submit", "close_input --submit" ], desc = "Submit the completion and input" },
{ on = [ "<A-k>" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "<A-j>" ], run = "arrow 1", desc = "Move cursor down" },
{ on = "<A-k>", run = "arrow -1", desc = "Move cursor up" },
{ on = "<A-j>", run = "arrow 1", desc = "Move cursor down" },
{ on = [ "<Up>" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "<Down>" ], run = "arrow 1", desc = "Move cursor down" },
{ on = "<Up>", run = "arrow -1", desc = "Move cursor up" },
{ on = "<Down>", run = "arrow 1", desc = "Move cursor down" },
{ on = [ "<C-p>" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "<C-n>" ], run = "arrow 1", desc = "Move cursor down" },
{ on = "<C-p>", run = "arrow -1", desc = "Move cursor up" },
{ on = "<C-n>", run = "arrow 1", desc = "Move cursor down" },
{ on = [ "~" ], run = "help", desc = "Open help" }
{ on = "~", run = "help", desc = "Open help" }
]
[help]
keymap = [
{ on = [ "<Esc>" ], run = "escape", desc = "Clear the filter, or hide the help" },
{ on = [ "<C-[>" ], run = "escape", desc = "Clear the filter, or hide the help" },
{ on = [ "q" ], run = "close", desc = "Exit the process" },
{ on = [ "<C-c>" ], run = "close", desc = "Hide the help" },
{ on = "<Esc>", run = "escape", desc = "Clear the filter, or hide the help" },
{ on = "<C-[>", run = "escape", desc = "Clear the filter, or hide the help" },
{ on = "q", run = "close", desc = "Exit the process" },
{ on = "<C-c>", run = "close", desc = "Hide the help" },
# Navigation
{ on = [ "k" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "j" ], run = "arrow 1", desc = "Move cursor down" },
{ on = "k", run = "arrow -1", desc = "Move cursor up" },
{ on = "j", run = "arrow 1", desc = "Move cursor down" },
{ on = [ "K" ], run = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = [ "J" ], run = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = "K", run = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = "J", run = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = [ "<Up>" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "<Down>" ], run = "arrow 1", desc = "Move cursor down" },
{ on = "<Up>", run = "arrow -1", desc = "Move cursor up" },
{ on = "<Down>", run = "arrow 1", desc = "Move cursor down" },
{ on = [ "<S-Up>" ], run = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = [ "<S-Down>" ], run = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = "<S-Up>", run = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = "<S-Down>", run = "arrow 5", desc = "Move cursor down 5 lines" },
# Filtering
{ on = [ "/" ], run = "filter", desc = "Apply a filter for the help items" },
{ on = "/", run = "filter", desc = "Apply a filter for the help items" },
]

View File

@ -764,18 +764,18 @@ exts = [
]
conds = [
# Special files
{ cond = "orphan", text = "" },
{ cond = "link" , text = "" },
{ cond = "block" , text = "" },
{ cond = "char" , text = "" },
{ cond = "fifo" , text = "" },
{ cond = "sock" , text = "" },
{ cond = "sticky", text = "" },
{ if = "orphan", text = "" },
{ if = "link" , text = "" },
{ if = "block" , text = "" },
{ if = "char" , text = "" },
{ if = "fifo" , text = "" },
{ if = "sock" , text = "" },
{ if = "sticky", text = "" },
# Fallback
{ cond = "dir", text = "" },
{ cond = "exec", text = "" },
{ cond = "!dir", text = "" },
{ if = "dir", text = "" },
{ if = "exec", text = "" },
{ if = "!dir", text = "" },
]
# : }}}

View File

@ -83,7 +83,7 @@ suppress_preload = false
fetchers = [
# Mimetype
{ id = "mime", name = "*", cond = "!mime", run = "mime", prio = "high" },
{ id = "mime", name = "*", run = "mime", if = "!mime", prio = "high" },
]
preloaders = [
# Image
@ -113,7 +113,7 @@ previewers = [
{ mime = "application/pdf", run = "pdf" },
# Archive
{ mime = "application/{,g}zip", run = "archive" },
{ mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", run = "archive" },
{ mime = "application/x-{tar,bzip*,7z-compressed,xz,rar,iso9660-image}", run = "archive" },
# Font
{ mime = "font/*", run = "font" },
{ mime = "application/vnd.ms-opentype", run = "font" },

View File

@ -7,8 +7,9 @@ use super::Key;
#[derive(Debug, Default, Deserialize)]
pub struct Control {
#[serde(deserialize_with = "super::deserialize_on")]
pub on: Vec<Key>,
#[serde(deserialize_with = "super::run_deserialize")]
#[serde(deserialize_with = "super::deserialize_run")]
pub run: Vec<Cmd>,
pub desc: Option<String>,
}
@ -24,7 +25,7 @@ impl Control {
#[inline]
pub fn run(&self) -> String {
self.run.iter().map(|e| e.to_string()).collect::<Vec<_>>().join("; ")
self.run.iter().map(|c| c.to_string()).collect::<Vec<_>>().join("; ")
}
#[inline]

View File

@ -0,0 +1,83 @@
use std::{fmt, str::FromStr};
use anyhow::Result;
use serde::{de::{self, Visitor}, Deserializer};
use yazi_shared::event::Cmd;
use crate::keymap::Key;
pub(super) fn deserialize_on<'de, D>(deserializer: D) -> Result<Vec<Key>, D::Error>
where
D: Deserializer<'de>,
{
struct OnVisitor;
impl<'de> Visitor<'de> for OnVisitor {
type Value = Vec<Key>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a `on` string or array of strings within keymap.toml")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let mut cmds = vec![];
while let Some(value) = &seq.next_element::<String>()? {
cmds.push(Key::from_str(value).map_err(de::Error::custom)?);
}
if cmds.is_empty() {
return Err(de::Error::custom("`on` within keymap.toml cannot be empty"));
}
Ok(cmds)
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(vec![Key::from_str(value).map_err(de::Error::custom)?])
}
}
deserializer.deserialize_any(OnVisitor)
}
pub(super) fn deserialize_run<'de, D>(deserializer: D) -> Result<Vec<Cmd>, D::Error>
where
D: Deserializer<'de>,
{
struct RunVisitor;
impl<'de> Visitor<'de> for RunVisitor {
type Value = Vec<Cmd>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a `run` string or array of strings within keymap.toml")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let mut cmds = vec![];
while let Some(value) = &seq.next_element::<String>()? {
cmds.push(Cmd::from_str(value).map_err(de::Error::custom)?);
}
if cmds.is_empty() {
return Err(de::Error::custom("`run` within keymap.toml cannot be empty"));
}
Ok(cmds)
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(vec![Cmd::from_str(value).map_err(de::Error::custom)?])
}
}
deserializer.deserialize_any(RunVisitor)
}

View File

@ -2,10 +2,8 @@ use std::{fmt::{Display, Write}, str::FromStr};
use anyhow::bail;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use serde::Deserialize;
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Hash)]
#[serde(try_from = "String")]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Key {
pub code: KeyCode,
pub shift: bool,
@ -126,12 +124,6 @@ impl FromStr for Key {
}
}
impl TryFrom<String> for Key {
type Error = anyhow::Error;
fn try_from(s: String) -> Result<Self, Self::Error> { Self::from_str(&s) }
}
impl Display for Key {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(c) = self.plain() {

View File

@ -1,12 +1,11 @@
mod control;
mod cow;
mod deserializers;
mod key;
mod keymap;
mod run;
pub use control::*;
pub use cow::*;
use deserializers::*;
pub use key::*;
pub use keymap::*;
#[allow(unused_imports)]
pub use run::*;

View File

@ -1,43 +0,0 @@
use std::{fmt, str::FromStr};
use anyhow::Result;
use serde::{de::{self, Visitor}, Deserializer};
use yazi_shared::event::Cmd;
pub(super) fn run_deserialize<'de, D>(deserializer: D) -> Result<Vec<Cmd>, D::Error>
where
D: Deserializer<'de>,
{
struct RunVisitor;
impl<'de> Visitor<'de> for RunVisitor {
type Value = Vec<Cmd>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a `run` string or array of strings within keymap.toml")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let mut cmds = vec![];
while let Some(value) = &seq.next_element::<String>()? {
cmds.push(Cmd::from_str(value).map_err(de::Error::custom)?);
}
if cmds.is_empty() {
return Err(de::Error::custom("`run` within keymap.toml cannot be empty"));
}
Ok(cmds)
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(vec![Cmd::from_str(value).map_err(de::Error::custom)?])
}
}
deserializer.deserialize_any(RunVisitor)
}

View File

@ -58,5 +58,19 @@ pub fn init() -> anyhow::Result<()> {
SELECT.init(<_>::from_str(yazi_toml)?);
WHICH.init(<_>::from_str(yazi_toml)?);
// TODO: Remove in v0.3.2
for c in &KEYMAP.manager {
for r in &c.run {
if r.name == "shell" && !r.bool("confirm") && !r.bool("interactive") {
eprintln!(
r#"WARNING: In Yazi v0.3, the behavior of the interactive `shell` (i.e., shell templates) must be explicitly specified with `--interactive`.
Please replace e.g. `shell` with `shell --interactive`, `shell "my-template"` with `shell "my-template" --interactive`, in your keymap.toml"#
);
return Ok(());
}
}
}
Ok(())
}

View File

@ -9,7 +9,8 @@ pub struct Fetcher {
pub idx: u8,
pub id: String,
pub cond: Option<Condition>,
#[serde(rename = "if")]
pub if_: Option<Condition>,
pub name: Option<Pattern>,
pub mime: Option<Pattern>,
pub run: Cmd,

View File

@ -25,7 +25,7 @@ impl Plugin {
.fetchers
.iter()
.filter(|&p| {
p.cond.as_ref().and_then(|c| c.eval(f)) != Some(false)
p.if_.as_ref().and_then(|c| c.eval(f)) != Some(false)
&& (p.mime.as_ref().zip(mime).map_or(false, |(p, m)| p.match_mime(m))
|| p.name.as_ref().is_some_and(|p| p.match_path(path, is_dir)))
})

View File

@ -16,11 +16,11 @@ pub struct Icons {
impl Icons {
pub fn matches(&self, file: &File) -> Option<&Icon> {
if let Some((_, i)) = self.globs.iter().find(|(p, _)| p.match_path(&file.url, file.is_dir())) {
if let Some(i) = self.match_by_glob(file) {
return Some(i);
}
if let Some(i) = self.match_name(file) {
if let Some(i) = self.match_by_name(file) {
return Some(i);
}
@ -41,7 +41,12 @@ impl Icons {
}
#[inline]
fn match_name(&self, file: &File) -> Option<&Icon> {
fn match_by_glob(&self, file: &File) -> Option<&Icon> {
self.globs.iter().find(|(p, _)| p.match_path(&file.url, file.is_dir())).map(|(_, i)| i)
}
#[inline]
fn match_by_name(&self, file: &File) -> Option<&Icon> {
let name = file.name()?.to_str()?;
if file.is_dir() {
self.dirs.get(name).or_else(|| self.dirs.get(&name.to_ascii_lowercase()))
@ -110,7 +115,8 @@ impl<'de> Deserialize<'de> for Icons {
}
#[derive(Deserialize)]
pub struct ShadowCond {
cond: Condition,
#[serde(rename = "if")]
if_: Condition,
text: String,
fg_dark: Option<Color>,
#[allow(dead_code)]
@ -136,7 +142,7 @@ impl<'de> Deserialize<'de> for Icons {
.conds
.into_iter()
.map(|v| {
(v.cond, Icon { text: v.text, style: Style { fg: v.fg_dark, ..Default::default() } })
(v.if_, Icon { text: v.text, style: Style { fg: v.fg_dark, ..Default::default() } })
})
.collect();

View File

@ -26,7 +26,7 @@ dirs = "5.0.1"
futures = "0.3.30"
notify = { version = "6.1.1", default-features = false, features = [ "macos_fsevent" ] }
parking_lot = "0.12.3"
ratatui = "0.26.3"
ratatui = "0.27.0"
regex = "1.10.5"
scopeguard = "1.2.0"
serde = "1.0.203"

View File

@ -19,6 +19,10 @@ impl InputSnaps {
}
pub(super) fn tag(&mut self, limit: usize) -> bool {
if self.versions.len() <= self.idx {
return false;
}
// Sync *current* cursor position to the *last* version:
// Save offset/cursor/ect. of the *current* as the last version,
// while keeping the *last* value unchanged.
@ -49,7 +53,7 @@ impl InputSnaps {
}
pub(super) fn redo(&mut self) -> bool {
if self.idx + 1 == self.versions.len() {
if self.idx + 1 >= self.versions.len() {
return false;
}

View File

@ -64,7 +64,7 @@ impl Manager {
done.extend(files.iter().map(|f| (f.url(), String::new())));
if let Err(e) = isolate::fetch("mime", files).await {
error!("fetch `mime` failed in opening: {e}");
error!("Fetch `mime` failed in opening: {e}");
}
ManagerProxy::open_do(OpenDoOpt { hovered, targets: done, interactive: opt.interactive });

View File

@ -7,6 +7,7 @@ use crate::{manager::Manager, tasks::Tasks};
pub struct Opt {
force: bool,
permanently: bool,
hovered: bool,
targets: Vec<Url>,
}
@ -15,6 +16,7 @@ impl From<Cmd> for Opt {
Self {
force: c.bool("force"),
permanently: c.bool("permanently"),
hovered: c.bool("hovered"),
targets: c.take_any("targets").unwrap_or_default(),
}
}
@ -25,9 +27,16 @@ impl Manager {
if !self.active_mut().try_escape_visual() {
return;
}
let Some(hovered) = self.hovered().map(|h| &h.url) else {
return;
};
let mut opt = opt.into() as Opt;
opt.targets = self.selected_or_hovered(false).cloned().collect();
opt.targets = if opt.hovered {
vec![hovered.clone()]
} else {
self.selected_or_hovered(false).cloned().collect()
};
if opt.force {
return self.remove_do(opt, tasks);

View File

@ -10,17 +10,19 @@ use yazi_shared::{event::Cmd, fs::{maybe_exists, ok_or_not_found, paths_to_same_
use crate::manager::Manager;
pub struct Opt {
force: bool,
empty: String,
cursor: String,
hovered: bool,
force: bool,
empty: String,
cursor: String,
}
impl From<Cmd> for Opt {
fn from(mut c: Cmd) -> Self {
Self {
force: c.bool("force"),
empty: c.take_str("empty").unwrap_or_default(),
cursor: c.take_str("cursor").unwrap_or_default(),
hovered: c.bool("hovered"),
force: c.bool("force"),
empty: c.take_str("empty").unwrap_or_default(),
cursor: c.take_str("cursor").unwrap_or_default(),
}
}
}
@ -29,15 +31,16 @@ impl Manager {
pub fn rename(&mut self, opt: impl Into<Opt>) {
if !self.active_mut().try_escape_visual() {
return;
} else if !self.active().selected.is_empty() {
return self.bulk_rename();
}
let Some(hovered) = self.hovered().map(|h| h.url()) else {
return;
};
let opt = opt.into() as Opt;
if !opt.hovered && !self.active().selected.is_empty() {
return self.bulk_rename();
}
let name = Self::empty_url_part(&hovered, &opt.empty);
let cursor = match opt.cursor.as_str() {
"start" => Some(0),

View File

@ -130,7 +130,7 @@ impl Watcher {
continue;
}
if let Err(e) = isolate::fetch("mime", reload).await {
error!("fetch `mime` failed in watcher: {e}");
error!("Fetch `mime` failed in watcher: {e}");
}
}
}

View File

@ -1,25 +1,27 @@
use std::borrow::Cow;
use yazi_config::{open::Opener, popup::InputCfg};
use yazi_proxy::{InputProxy, TasksProxy};
use yazi_proxy::{AppProxy, InputProxy, TasksProxy};
use yazi_shared::event::Cmd;
use crate::tab::Tab;
pub struct Opt {
run: String,
block: bool,
orphan: bool,
confirm: bool,
run: String,
block: bool,
orphan: bool,
confirm: bool,
interactive: bool,
}
impl From<Cmd> for Opt {
fn from(mut c: Cmd) -> Self {
Self {
run: c.take_first_str().unwrap_or_default(),
block: c.bool("block"),
orphan: c.bool("orphan"),
confirm: c.bool("confirm"),
run: c.take_first_str().unwrap_or_default(),
block: c.bool("block"),
orphan: c.bool("orphan"),
confirm: c.bool("confirm"),
interactive: c.bool("interactive"),
}
}
}
@ -31,6 +33,24 @@ impl Tab {
}
let mut opt = opt.into() as Opt;
// TODO: Remove in v0.3.2
if !opt.interactive && !opt.confirm {
AppProxy::notify_error(
"`shell` command",
r#"In Yazi v0.3, the behavior of the interactive `shell` (i.e., shell templates) must be explicitly specified with `--interactive`.
Please replace e.g. `shell` with `shell --interactive`, `shell "my-template"` with `shell "my-template" --interactive`, in your keymap.toml"#,
);
return;
} else if opt.interactive && opt.confirm {
AppProxy::notify_error(
"`shell` command",
"The `shell` command cannot specify both `--confirm` and `--interactive` at the same time.",
);
return;
}
let selected = self.hovered_and_selected(true).cloned().collect();
tokio::spawn(async move {

View File

@ -42,6 +42,9 @@ pub fn init() {
USERS_CACHE.with(Default::default);
// Env
if let Some(s) = std::env::var("YAZI_ID").ok().filter(|s| !s.is_empty()) {
std::env::set_var("YAZI_PID", s);
}
std::env::set_var("YAZI_ID", ID.to_string());
std::env::set_var(
"YAZI_LEVEL",

View File

@ -29,7 +29,7 @@ crossterm = { version = "0.27.0", features = [ "event-stream" ] }
fdlimit = "0.3.0"
futures = "0.3.30"
mlua = { version = "0.9.9", features = [ "lua54" ] }
ratatui = "0.26.3"
ratatui = "0.27.0"
scopeguard = "1.2.0"
syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] }
tokio = { version = "1.38.0", features = [ "full" ] }

View File

@ -2,7 +2,7 @@ use std::ops::Deref;
use mlua::{AnyUserData, IntoLua, Lua, UserDataFields, UserDataMethods};
use yazi_config::THEME;
use yazi_plugin::{bindings::{Cast, Cha, Icon, Range}, elements::Style, url::Url};
use yazi_plugin::{bindings::Range, elements::Style};
use yazi_shared::MIME_DIR;
use super::{CtxRef, SCOPE};
@ -19,6 +19,10 @@ impl Deref for File {
fn deref(&self) -> &Self::Target { &self.folder().files[self.idx] }
}
impl AsRef<yazi_shared::fs::File> for File {
fn as_ref(&self) -> &yazi_shared::fs::File { self }
}
impl File {
#[inline]
pub(super) fn make(
@ -31,16 +35,9 @@ impl File {
pub(super) fn register(lua: &Lua) -> mlua::Result<()> {
lua.register_userdata_type::<Self>(|reg| {
reg.add_field_method_get("idx", |_, me| Ok(me.idx + 1));
reg.add_field_method_get("url", |lua, me| Url::cast(lua, me.url.clone()));
reg.add_field_method_get("cha", |lua, me| Cha::cast(lua, me.cha));
reg.add_field_method_get("link_to", |lua, me| {
me.link_to.as_ref().cloned().map(|u| Url::cast(lua, u)).transpose()
});
yazi_plugin::file::File::register_with(reg);
reg.add_field_method_get("name", |lua, me| {
me.url.file_name().map(|n| lua.create_string(n.as_encoded_bytes())).transpose()
});
reg.add_field_method_get("idx", |_, me| Ok(me.idx + 1));
reg.add_method("size", |_, me, ()| {
Ok(if me.is_dir() { me.folder().files.sizes.get(&me.url).copied() } else { Some(me.len) })
});
@ -57,19 +54,6 @@ impl File {
p.next_back();
Some(lua.create_string(p.as_path().as_os_str().as_encoded_bytes())).transpose()
});
reg.add_method("icon", |lua, me, ()| {
use yazi_shared::theme::IconCache;
match me.icon.get() {
IconCache::Missing => {
let matched = THEME.icons.matches(me);
me.icon.set(matched.map_or(IconCache::Undefined, IconCache::Icon));
matched.map(|i| Icon::cast(lua, i)).transpose()
}
IconCache::Undefined => Ok(None),
IconCache::Icon(cached) => Some(Icon::cast(lua, cached)).transpose(),
}
});
reg.add_method("style", |lua, me, ()| {
let cx = lua.named_registry_value::<CtxRef>("cx")?;
let mime =

View File

@ -29,9 +29,7 @@ futures = "0.3.30"
md-5 = "0.10.6"
mlua = { version = "0.9.9", features = [ "lua54", "serialize", "macros", "async" ] }
parking_lot = "0.12.3"
ratatui = "0.26.3"
serde = "1.0.203"
serde_json = "1.0.117"
ratatui = "0.27.0"
shell-escape = "0.1.5"
shell-words = "1.1.0"
syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] }

View File

@ -44,13 +44,18 @@ function Current:render(area)
end
function Current:click(event, up)
if up or not event.is_left then
if up or event.is_middle then
return
end
local f = Folder:by_kind(Folder.CURRENT)
if event.y <= #f.window and f.hovered then
ya.manager_emit("arrow", { event.y + f.offset - f.hovered.idx })
if event.y > #f.window or not f.hovered then
return
end
ya.manager_emit("arrow", { event.y + f.offset - f.hovered.idx })
if event.is_right then
ya.manager_emit("open", {})
end
end

View File

@ -1,9 +1,64 @@
local M = {}
function M:peek()
local _, bound = ya.preview_archive(self)
if bound then
ya.manager_emit("peek", { bound, only_if = self.file.url, upper_bound = true })
local child
if ya.target_os() == "macos" then
child = self:try_spawn("7zz") or self:try_spawn("7z")
else
child = self:try_spawn("7z") or self:try_spawn("7zz")
end
if not child then
return ya.err("spawn `7z` and `7zz` both commands failed, error code: " .. tostring(self.last_error))
end
local limit = self.area.h
local i, icon, names, sizes = 0, nil, {}, {}
repeat
local next, event = child:read_line()
if event ~= 0 then
break
end
local attr, size, name = next:match("^[-%d]+%s+[:%d]+%s+([.%a]+)%s+(%d+)%s+%d+%s+(.+)[\r\n]+")
if not name then
goto continue
end
i = i + 1
if i <= self.skip then
goto continue
end
icon = File({
url = Url(name),
cha = Cha { kind = attr:sub(1, 1) == "D" and 1 or 0 },
}):icon()
if icon then
names[#names + 1] = ui.Line { ui.Span(" " .. icon.text .. " "):style(icon.style), ui.Span(name) }
else
names[#names + 1] = ui.Line(name)
end
size = tonumber(size)
if size > 0 then
sizes[#sizes + 1] = ui.Line(string.format(" %s ", ya.readable_size(size)))
else
sizes[#sizes + 1] = ui.Line("")
end
::continue::
until i >= self.skip + limit
child:start_kill()
if self.skip > 0 and i < self.skip + limit then
ya.manager_emit("peek", { math.max(0, i - limit), only_if = self.file.url, upper_bound = true })
else
ya.preview_widgets(self, {
ui.Paragraph(self.area, names),
ui.Paragraph(self.area, sizes):align(ui.Paragraph.RIGHT),
})
end
end
@ -18,4 +73,12 @@ function M:seek(units)
end
end
function M:try_spawn(name)
local child, code = Command(name):args({ "l", "-ba", tostring(self.file.url) }):stdout(Command.PIPED):spawn()
if not child then
self.last_error = code
end
return child
end
return M

View File

@ -23,11 +23,11 @@ function M:preload()
local child, code = Command("magick"):args({
"-density",
"200",
tostring(self.file.url),
"-resize",
string.format("%dx%d^", PREVIEW.max_width, PREVIEW.max_height),
"-quality",
tostring(PREVIEW.image_quality),
tostring(self.file.url),
"JPG:" .. tostring(cache),
}):spawn()

View File

@ -1,3 +1,4 @@
os.setlocale("")
package.path = BOOT.plugin_dir .. "/?.yazi/init.lua;" .. package.path
require("dds"):setup()

View File

@ -1,29 +0,0 @@
use mlua::{AnyUserData, Lua, UserDataFields, UserDataRef};
use super::{Cast, Cha};
use crate::url::Url;
pub type FileRef<'lua> = UserDataRef<'lua, yazi_shared::fs::File>;
pub struct File;
impl File {
pub fn register(lua: &Lua) -> mlua::Result<()> {
lua.register_userdata_type::<yazi_shared::fs::File>(|reg| {
reg.add_field_method_get("url", |lua, me| Url::cast(lua, me.url.clone()));
reg.add_field_method_get("cha", |lua, me| Cha::cast(lua, me.cha));
reg.add_field_method_get("link_to", |lua, me| {
me.link_to.as_ref().cloned().map(|u| Url::cast(lua, u)).transpose()
});
// Extension
reg.add_field_method_get("name", |lua, me| {
me.url.file_name().map(|n| lua.create_string(n.as_encoded_bytes())).transpose()
});
})
}
}
impl<T: Into<yazi_shared::fs::File>> Cast<T> for File {
fn cast(lua: &Lua, data: T) -> mlua::Result<AnyUserData> { lua.create_any_userdata(data.into()) }
}

View File

@ -1,8 +1,6 @@
#![allow(clippy::module_inception)]
mod bindings;
mod cha;
mod file;
mod icon;
mod input;
mod mouse;
@ -12,8 +10,6 @@ mod range;
mod window;
pub use bindings::*;
pub use cha::*;
pub use file::*;
pub use icon::*;
pub use input::*;
pub use mouse::*;

View File

@ -1,8 +1,11 @@
use std::time::UNIX_EPOCH;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use mlua::{AnyUserData, Lua, UserDataFields, UserDataMethods};
use mlua::{AnyUserData, ExternalError, Lua, Table, UserDataFields, UserDataMethods, UserDataRef};
use yazi_shared::fs::ChaKind;
use super::Cast;
use crate::bindings::Cast;
pub type UrlRef<'lua> = UserDataRef<'lua, yazi_shared::fs::Cha>;
pub struct Cha;
@ -48,6 +51,39 @@ impl Cha {
Ok(())
}
pub fn install(lua: &Lua) -> mlua::Result<()> {
#[inline]
fn parse_time(f: Option<f64>) -> mlua::Result<Option<SystemTime>> {
Ok(match f {
Some(n) if n >= 0.0 => Some(SystemTime::UNIX_EPOCH + Duration::from_secs_f64(n)),
Some(n) => Err(format!("Invalid timestamp: {n}").into_lua_err())?,
None => None,
})
}
lua.globals().raw_set(
"Cha",
lua.create_function(|lua, t: Table| {
let kind =
ChaKind::from_bits(t.raw_get("kind")?).ok_or_else(|| "Invalid kind".into_lua_err())?;
Self::cast(lua, yazi_shared::fs::Cha {
kind,
len: t.raw_get("len").unwrap_or_default(),
accessed: parse_time(t.raw_get("atime").ok())?,
created: parse_time(t.raw_get("ctime").ok())?,
modified: parse_time(t.raw_get("mtime").ok())?,
#[cfg(unix)]
permissions: t.raw_get("permissions").unwrap_or_default(),
#[cfg(unix)]
uid: t.raw_get("uid").unwrap_or_default(),
#[cfg(unix)]
gid: t.raw_get("gid").unwrap_or_default(),
})
})?,
)
}
}
impl<T: Into<yazi_shared::fs::Cha>> Cast<T> for Cha {

View File

@ -0,0 +1,12 @@
#![allow(clippy::module_inception)]
mod cha;
pub use cha::*;
pub fn pour(lua: &mlua::Lua) -> mlua::Result<()> {
cha::Cha::register(lua)?;
cha::Cha::install(lua)?;
Ok(())
}

View File

@ -1,79 +0,0 @@
use std::path::Path;
use anyhow::anyhow;
use serde::Deserialize;
use serde_json::Value;
use tokio::process::Command;
use yazi_shared::PeekError;
#[derive(Debug)]
pub enum LsarAttr {
Posix(u16),
Windows(u16),
Dos(u8),
}
#[derive(Debug, Deserialize)]
pub struct LsarFile {
#[serde(rename = "XADFileName")]
pub name: String,
#[serde(rename = "XADLastModificationDate")]
pub last_modified: String,
#[serde(rename = "XADFileSize")]
pub size: Option<usize>,
#[serde(rename = "XADCompressedSize")]
pub compressed_size: Option<usize>,
#[serde(rename = "XADCompressionName")]
pub compression_name: Option<String>,
#[serde(skip)]
pub attributes: Option<LsarAttr>,
}
#[allow(clippy::manual_map)]
pub async fn lsar(path: &Path, skip: usize, limit: usize) -> Result<Vec<LsarFile>, PeekError> {
let output = Command::new("lsar").arg("-j").arg(path).kill_on_drop(true).output().await?;
if !output.status.success() {
return Err(String::from_utf8_lossy(&output.stderr).to_string().into());
}
#[derive(Deserialize)]
struct Outer {
#[serde(rename = "lsarContents")]
contents: Vec<Value>,
}
let output = String::from_utf8_lossy(&output.stdout);
let contents = serde_json::from_str::<Outer>(output.trim()).map_err(|e| anyhow!(e))?.contents;
let mut i = 0;
let mut files = Vec::with_capacity(limit);
for content in contents {
i += 1;
if i > skip + limit {
break;
} else if i <= skip {
continue;
}
let attributes = if let Some(p) = content.get("XADPosixPermissions").and_then(|p| p.as_u64()) {
Some(LsarAttr::Posix(p as u16))
} else if let Some(a) = content.get("XADWindowsFileAttributes").and_then(|a| a.as_u64()) {
Some(LsarAttr::Windows(a as u16))
} else if let Some(a) = content.get("XADDOSFileAttributes").and_then(|a| a.as_u64()) {
Some(LsarAttr::Dos(a as u8))
} else {
None
};
let mut file = serde_json::from_value::<LsarFile>(content).map_err(|e| anyhow!(e))?;
file.attributes = attributes;
files.push(file);
}
if skip > 0 && files.len() < limit {
Err(PeekError::Exceed(i.saturating_sub(limit)))
} else {
Ok(files)
}
}

View File

@ -1,9 +1,7 @@
mod fd;
mod highlighter;
mod lsar;
mod rg;
pub use fd::*;
pub use highlighter::*;
pub use lsar::*;
pub use rg::*;

View File

@ -0,0 +1,63 @@
use mlua::{AnyUserData, Lua, Table, UserDataFields, UserDataMethods, UserDataRef, UserDataRegistry};
use yazi_config::THEME;
use crate::{bindings::{Cast, Icon}, cha::Cha, url::Url};
pub type FileRef<'lua> = UserDataRef<'lua, yazi_shared::fs::File>;
pub struct File;
impl File {
#[inline]
pub fn register(lua: &Lua) -> mlua::Result<()> {
lua.register_userdata_type::<yazi_shared::fs::File>(Self::register_with)
}
pub fn register_with<T>(reg: &mut UserDataRegistry<T>)
where
T: AsRef<yazi_shared::fs::File>,
{
reg.add_field_method_get("url", |lua, me| Url::cast(lua, me.as_ref().url.clone()));
reg.add_field_method_get("cha", |lua, me| Cha::cast(lua, me.as_ref().cha));
reg.add_field_method_get("link_to", |lua, me| {
me.as_ref().link_to.clone().map(|u| Url::cast(lua, u)).transpose()
});
// Extension
reg.add_field_method_get("name", |lua, me| {
me.as_ref().url.file_name().map(|n| lua.create_string(n.as_encoded_bytes())).transpose()
});
reg.add_method("icon", |lua, me, ()| {
use yazi_shared::theme::IconCache;
let me = me.as_ref();
match me.icon.get() {
IconCache::Missing => {
let matched = THEME.icons.matches(me);
me.icon.set(matched.map_or(IconCache::Undefined, IconCache::Icon));
matched.map(|i| Icon::cast(lua, i)).transpose()
}
IconCache::Undefined => Ok(None),
IconCache::Icon(cached) => Some(Icon::cast(lua, cached)).transpose(),
}
});
}
pub fn install(lua: &Lua) -> mlua::Result<()> {
lua.globals().raw_set(
"File",
lua.create_function(|lua, t: Table| {
Self::cast(lua, yazi_shared::fs::File {
url: t.raw_get::<_, AnyUserData>("url")?.take()?,
cha: t.raw_get::<_, AnyUserData>("cha")?.take()?,
..Default::default()
})
})?,
)
}
}
impl<T: Into<yazi_shared::fs::File>> Cast<T> for File {
fn cast(lua: &Lua, data: T) -> mlua::Result<AnyUserData> { lua.create_any_userdata(data.into()) }
}

View File

@ -0,0 +1,12 @@
#![allow(clippy::module_inception)]
mod file;
pub use file::*;
pub fn pour(lua: &mlua::Lua) -> mlua::Result<()> {
file::File::register(lua)?;
file::File::install(lua)?;
Ok(())
}

View File

@ -1,7 +1,7 @@
use mlua::{IntoLuaMulti, Lua, Value};
use tokio::fs;
use crate::{bindings::{Cast, Cha}, url::UrlRef};
use crate::{bindings::Cast, cha::Cha, url::UrlRef};
pub fn install(lua: &Lua) -> mlua::Result<()> {
lua.globals().raw_set(

View File

@ -3,7 +3,7 @@ use tokio::runtime::Handle;
use yazi_config::LAYOUT;
use super::slim_lua;
use crate::{bindings::{Cast, File}, elements::Rect, loader::LOADER};
use crate::{bindings::Cast, elements::Rect, file::File, loader::LOADER};
pub async fn fetch(name: &str, files: Vec<yazi_shared::fs::File>) -> mlua::Result<u8> {
LOADER.ensure(name).await.into_lua_err()?;

View File

@ -1,14 +1,15 @@
use mlua::Lua;
use crate::{bindings, elements, runtime::Runtime};
use crate::{elements, runtime::Runtime};
pub fn slim_lua(name: &str) -> mlua::Result<Lua> {
let lua = Lua::new();
lua.set_named_registry_value("rt", Runtime::new(name))?;
// Base
bindings::Cha::register(&lua)?;
bindings::File::register(&lua)?;
crate::bindings::Icon::register(&lua)?;
crate::cha::pour(&lua)?;
crate::file::pour(&lua)?;
crate::url::pour(&lua)?;
crate::fs::install(&lua)?;

View File

@ -6,7 +6,7 @@ use yazi_config::LAYOUT;
use yazi_shared::{emit, event::Cmd, Layer};
use super::slim_lua;
use crate::{bindings::{Cast, File, Window}, elements::Rect, loader::LOADER, Opt, OptCallback, LUA};
use crate::{bindings::{Cast, Window}, elements::Rect, file::File, loader::LOADER, Opt, OptCallback, LUA};
pub fn peek(cmd: &Cmd, file: yazi_shared::fs::File, skip: usize) -> CancellationToken {
let ct = CancellationToken::new();

View File

@ -3,7 +3,7 @@ use tokio::runtime::Handle;
use yazi_config::LAYOUT;
use super::slim_lua;
use crate::{bindings::{Cast, File}, elements::Rect, loader::LOADER};
use crate::{bindings::Cast, elements::Rect, file::File, loader::LOADER};
pub async fn preload(name: &str, file: yazi_shared::fs::File) -> mlua::Result<u8> {
LOADER.ensure(name).await.into_lua_err()?;

View File

@ -2,7 +2,7 @@ use mlua::TableExt;
use yazi_config::LAYOUT;
use yazi_shared::{emit, event::Cmd, Layer};
use crate::{bindings::{Cast, File}, elements::Rect, Opt, OptCallback, LUA};
use crate::{bindings::Cast, elements::Rect, file::File, Opt, OptCallback, LUA};
pub fn seek_sync(cmd: &Cmd, file: yazi_shared::fs::File, units: i16) {
let cb: OptCallback = Box::new(move |_, plugin| {

View File

@ -2,10 +2,12 @@
pub mod bindings;
mod cast;
pub mod cha;
mod clipboard;
mod config;
pub mod elements;
pub mod external;
pub mod file;
pub mod fs;
pub mod isolate;
pub mod loader;

View File

@ -21,13 +21,13 @@ fn stage_1(lua: &'static Lua) -> Result<()> {
// Base
lua.set_named_registry_value("rt", Runtime::default())?;
lua.load(include_str!("../preset/ya.lua")).exec()?;
crate::bindings::Cha::register(lua)?;
crate::bindings::File::register(lua)?;
crate::bindings::Icon::register(lua)?;
crate::bindings::MouseEvent::register(lua)?;
crate::elements::pour(lua)?;
crate::loader::install(lua)?;
crate::pubsub::install(lua)?;
crate::cha::pour(lua)?;
crate::file::pour(lua)?;
crate::url::pour(lua)?;
// Components

View File

@ -3,7 +3,7 @@ use mlua::{Lua, Table};
use yazi_config::PREVIEW;
use super::Utils;
use crate::{bindings::{Cast, FileRef}, url::Url};
use crate::{bindings::Cast, file::FileRef, url::Url};
impl Utils {
pub(super) fn cache(lua: &Lua, ya: &Table) -> mlua::Result<()> {

View File

@ -49,6 +49,14 @@ impl Utils {
})?,
)?;
ya.raw_set(
"input_emit",
lua.create_function(|_, (name, args): (String, Table)| {
emit!(Call(Cmd { name, args: Self::parse_args(args)? }, Layer::Input));
Ok(())
})?,
)?;
Ok(())
}
}

View File

@ -2,7 +2,7 @@ use mlua::{AnyUserData, IntoLuaMulti, Lua, Table, Value};
use yazi_shared::{emit, event::Cmd, Layer, PeekError};
use super::Utils;
use crate::{bindings::{FileRef, Window}, cast_to_renderable, elements::{Paragraph, RectRef, Renderable}, external::{self, Highlighter}};
use crate::{bindings::Window, cast_to_renderable, elements::{Paragraph, RectRef, Renderable}, external::Highlighter, file::FileRef};
pub struct PreviewLock {
pub url: yazi_shared::fs::Url,
@ -49,29 +49,6 @@ impl Utils {
})?,
)?;
ya.raw_set(
"preview_archive",
lua.create_async_function(|lua, t: Table| async move {
let area: RectRef = t.raw_get("area")?;
let mut lock = PreviewLock::try_from(t)?;
let lines: Vec<_> = match external::lsar(&lock.url, lock.skip, area.height as usize).await {
Ok(items) => items.into_iter().map(|f| ratatui::text::Line::from(f.name)).collect(),
Err(PeekError::Exceed(max)) => return (false, max).into_lua_multi(lua),
Err(_) => return (false, Value::Nil).into_lua_multi(lua),
};
lock.data = vec![Box::new(Paragraph {
area: *area,
text: ratatui::text::Text::from(lines),
..Default::default()
})];
emit!(Call(Cmd::new("preview").with_any("lock", lock), Layer::Manager));
(true, Value::Nil).into_lua_multi(lua)
})?,
)?;
ya.raw_set(
"preview_widgets",
lua.create_async_function(|_, (t, widgets): (Table, Vec<AnyUserData>)| async move {

View File

@ -4,23 +4,8 @@ use super::Utils;
impl Utils {
pub(super) fn target(lua: &Lua, ya: &Table) -> mlua::Result<()> {
ya.raw_set(
"target_family",
lua.create_function(|_, ()| {
#[cfg(unix)]
{
Ok("unix")
}
#[cfg(windows)]
{
Ok("windows")
}
#[cfg(target_family = "wasm")]
{
Ok("wasm")
}
})?,
)?;
ya.raw_set("target_os", lua.create_function(|_, ()| Ok(std::env::consts::OS))?)?;
ya.raw_set("target_family", lua.create_function(|_, ()| Ok(std::env::consts::FAMILY))?)?;
Ok(())
}

View File

@ -33,13 +33,24 @@ impl Prework {
let urls: Vec<_> = task.targets.iter().map(|f| f.url()).collect();
let result = isolate::fetch(&task.plugin.name, task.targets).await;
if let Err(e) = result {
self.fail(task.id, format!("Fetch task failed:\n{e}"))?;
self.fail(
task.id,
format!(
"Failed to run fetcher `{}` with:\n{}\n\nError message:\n{e}",
task.plugin.name,
urls.iter().map(ToString::to_string).collect::<Vec<_>>().join("\n")
),
)?;
return Err(e.into());
};
let code = result.unwrap();
if code & 1 == 0 {
error!("Fetch task `{}` returned {code}", task.plugin.name);
error!(
"Returned {code} when running fetcher `{}` with:\n{}",
task.plugin.name,
urls.iter().map(ToString::to_string).collect::<Vec<_>>().join("\n")
);
}
if code >> 1 & 1 != 0 {
let mut loaded = self.loaded.lock();
@ -53,13 +64,16 @@ impl Prework {
let url = task.target.url();
let result = isolate::preload(&task.plugin.name, task.target).await;
if let Err(e) = result {
self.fail(task.id, format!("Preload task failed:\n{e}"))?;
self.fail(
task.id,
format!("Failed to run preloader `{}` with `{url}`:\n{e}", task.plugin.name),
)?;
return Err(e.into());
};
let code = result.unwrap();
if code & 1 == 0 {
error!("Preload task `{}` returned {code}", task.plugin.name);
error!("Returned {code} when running preloader `{}` with `{url}`", task.plugin.name);
}
if code >> 1 & 1 != 0 {
let mut loaded = self.loaded.lock();

View File

@ -17,7 +17,7 @@ dirs = "5.0.1"
futures = "0.3.30"
parking_lot = "0.12.3"
percent-encoding = "2.3.1"
ratatui = "0.26.3"
ratatui = "0.27.0"
regex = "1.10.5"
serde = { version = "1.0.203", features = [ "derive" ] }
shell-words = "1.1.0"

View File

@ -5,16 +5,16 @@ use bitflags::bitflags;
bitflags! {
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ChaKind: u8 {
const DIR = 0b00000001;
const DIR = 0b00000001;
const HIDDEN = 0b00000010;
const LINK = 0b00000100;
const ORPHAN = 0b00001000;
const HIDDEN = 0b00000010;
const LINK = 0b00000100;
const ORPHAN = 0b00001000;
const BLOCK_DEVICE = 0b00010000;
const CHAR_DEVICE = 0b00100000;
const FIFO = 0b01000000;
const SOCKET = 0b10000000;
const BLOCK = 0b00010000;
const CHAR = 0b00100000;
const FIFO = 0b01000000;
const SOCKET = 0b10000000;
}
}
@ -44,10 +44,10 @@ impl From<Metadata> for Cha {
{
use std::os::unix::prelude::FileTypeExt;
if m.file_type().is_block_device() {
ck |= ChaKind::BLOCK_DEVICE;
ck |= ChaKind::BLOCK;
}
if m.file_type().is_char_device() {
ck |= ChaKind::CHAR_DEVICE;
ck |= ChaKind::CHAR;
}
if m.file_type().is_fifo() {
ck |= ChaKind::FIFO;
@ -105,10 +105,10 @@ impl Cha {
pub fn is_orphan(&self) -> bool { self.kind.contains(ChaKind::ORPHAN) }
#[inline]
pub fn is_block(&self) -> bool { self.kind.contains(ChaKind::BLOCK_DEVICE) }
pub fn is_block(&self) -> bool { self.kind.contains(ChaKind::BLOCK) }
#[inline]
pub fn is_char(&self) -> bool { self.kind.contains(ChaKind::CHAR_DEVICE) }
pub fn is_char(&self) -> bool { self.kind.contains(ChaKind::CHAR) }
#[inline]
pub fn is_fifo(&self) -> bool { self.kind.contains(ChaKind::FIFO) }

View File

@ -20,6 +20,11 @@ impl Deref for File {
fn deref(&self) -> &Self::Target { &self.cha }
}
impl AsRef<File> for File {
#[inline]
fn as_ref(&self) -> &File { self }
}
impl File {
#[inline]
pub async fn from(url: Url) -> Result<Self> {