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
4 changes: 2 additions & 2 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ clap = { version = "4.5.32", features = [ "derive" ] }
core-foundation-sys = "0.8.7"
crossterm = { version = "0.28.1", features = [ "event-stream" ] }
dirs = "6.0.0"
foldhash = "0.1.4"
foldhash = "0.1.5"
futures = "0.3.31"
globset = "0.4.16"
indexmap = { version = "2.8.0", features = [ "serde" ] }
Expand Down
6 changes: 3 additions & 3 deletions yazi-config/preset/keymap-default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ keymap = [
{ on = "<PageDown>", run = "arrow 100%", desc = "Move cursor down one page" },

{ on = [ "g", "g" ], run = "arrow top", desc = "Move cursor to the top" },
{ on = "G", run = "arrow bot", desc = "Move cursor to the bottom" },
{ on = "G", run = "arrow bot", desc = "Move cursor to the bottom" },

# Navigation
{ on = "h", run = "leave", desc = "Go back to the parent directory" },
{ on = "h", run = "leave", desc = "Back to the parent directory" },
{ on = "l", run = "enter", desc = "Enter the child directory" },

{ on = "<Left>", run = "leave", desc = "Go back to the parent directory" },
{ on = "<Left>", run = "leave", desc = "Back to the parent directory" },
{ on = "<Right>", run = "enter", desc = "Enter the child directory" },

{ on = "H", run = "back", desc = "Back to previous directory" },
Expand Down
2 changes: 1 addition & 1 deletion yazi-config/preset/yazi-default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ preloaders = [
{ mime = "application/ms-opentype", run = "font" },
]
previewers = [
{ name = "*/", run = "folder", sync = true },
{ name = "*/", run = "folder" },
# Code
{ mime = "text/*", run = "code" },
{ mime = "application/{mbox,javascript,wine-extension-ini}", run = "code" },
Expand Down
2 changes: 0 additions & 2 deletions yazi-config/src/plugin/previewer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ pub struct Previewer {
pub name: Option<Pattern>,
pub mime: Option<Pattern>,
pub run: Cmd,
#[serde(default)]
pub sync: bool,
}

impl Previewer {
Expand Down
6 changes: 1 addition & 5 deletions yazi-core/src/tab/preview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,7 @@ impl Preview {
};

self.abort();
if previewer.sync {
isolate::peek_sync(&previewer.run, file, mime, self.skip);
} else {
self.previewer_ct = Some(isolate::peek(&previewer.run, file, mime, self.skip));
}
self.previewer_ct = isolate::peek(&previewer.run, file, mime, self.skip);
}

pub fn go_folder(&mut self, file: File, dir: Option<Cha>, force: bool) {
Expand Down
2 changes: 1 addition & 1 deletion yazi-fm/src/app/commands/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl App {
}

tokio::spawn(async move {
match LOADER.ensure(&opt.id).await {
match LOADER.ensure(&opt.id, |_| ()).await {
Ok(()) => AppProxy::plugin_do(opt),
Err(e) => AppProxy::notify_error("Plugin load failed", e),
}
Expand Down
8 changes: 4 additions & 4 deletions yazi-fm/src/term.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ impl Term {
execute!(
TTY.writer(),
screen::SetScreen(true),
Print(Mux::csi("\x1bP$q q\x1b\\")), // Request cursor shape (DECRQSS query for DECSCUSR)
Print(Mux::csi("\x1b[?12$p")), // Request cursor blink status (DECSET)
Print("\x1b[?u"), // Request keyboard enhancement flags (CSI u)
Print(Mux::csi("\x1b[0c")), // Request device attributes
Print("\x1bP$q q\x1b\\"), // Request cursor shape (DECRQSS query for DECSCUSR)
Print(Mux::csi("\x1b[?12$p")), // Request cursor blink status (DECSET)
Print("\x1b[?u"), // Request keyboard enhancement flags (CSI u)
Print(Mux::csi("\x1b[0c")), // Request device attributes
screen::SetScreen(false),
EnableBracketedPaste,
mouse::SetMouse(true),
Expand Down
2 changes: 2 additions & 0 deletions yazi-plugin/preset/plugins/folder.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
--- @sync peek

local M = {}

function M:peek(job)
Expand Down
15 changes: 5 additions & 10 deletions yazi-plugin/src/config/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,11 @@ impl<'a> Runtime<'a> {
}

fn args(lua: &Lua) -> mlua::Result<Value> {
Composer::make(lua, 5, |lua, key| {
match key {
b"entries" => {
lua.create_sequence_from(ARGS.entries.iter().map(Url::from))?.into_lua(lua)?
}
b"cwd_file" => ARGS.cwd_file.as_ref().map(Url::from).into_lua(lua)?,
b"chooser_file" => ARGS.chooser_file.as_ref().map(Url::from).into_lua(lua)?,
_ => return Ok(Value::Nil),
}
.into_lua(lua)
Composer::make(lua, 5, |lua, key| match key {
b"entries" => lua.create_sequence_from(ARGS.entries.iter().map(Url::from))?.into_lua(lua),
b"cwd_file" => ARGS.cwd_file.as_ref().map(Url::from).into_lua(lua),
b"chooser_file" => ARGS.chooser_file.as_ref().map(Url::from).into_lua(lua),
_ => Ok(Value::Nil),
})
}

Expand Down
6 changes: 3 additions & 3 deletions yazi-plugin/src/isolate/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ use super::slim_lua;
use crate::loader::LOADER;

pub async fn entry(opt: PluginOpt) -> mlua::Result<()> {
LOADER.ensure(&opt.id).await.into_lua_err()?;
LOADER.ensure(&opt.id, |_| ()).await.into_lua_err()?;

tokio::task::spawn_blocking(move || {
let lua = slim_lua(&opt.id)?;
let plugin: Table = if let Some(b) = LOADER.read().get(opt.id.as_ref()) {
lua.load(b.as_bytes()).set_name(opt.id.as_ref()).call(())?
let plugin: Table = if let Some(c) = LOADER.read().get(opt.id.as_ref()) {
lua.load(c.as_bytes()).set_name(opt.id.as_ref()).call(())?
} else {
return Err("unloaded plugin".into_lua_err());
};
Expand Down
6 changes: 3 additions & 3 deletions yazi-plugin/src/isolate/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ pub async fn fetch(
if files.is_empty() {
return Ok((FetchState::Bool(true), None));
}
LOADER.ensure(&cmd.name).await.into_lua_err()?;
LOADER.ensure(&cmd.name, |_| ()).await.into_lua_err()?;

tokio::task::spawn_blocking(move || {
let lua = slim_lua(&cmd.name)?;
let plugin: Table = if let Some(b) = LOADER.read().get(&cmd.name) {
lua.load(b.as_bytes()).set_name(&cmd.name).call(())?
let plugin: Table = if let Some(c) = LOADER.read().get(&cmd.name) {
lua.load(c.as_bytes()).set_name(&cmd.name).call(())?
} else {
return Err("unloaded plugin".into_lua_err());
};
Expand Down
85 changes: 57 additions & 28 deletions yazi-plugin/src/isolate/peek.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::borrow::Cow;

use mlua::{ExternalError, ExternalResult, HookTriggers, IntoLua, ObjectLike, Table, VmState};
use mlua::{ExternalError, HookTriggers, IntoLua, ObjectLike, Table, VmState};
use tokio::{runtime::Handle, select};
use tokio_util::sync::CancellationToken;
use tracing::error;
Expand All @@ -17,28 +17,75 @@ pub fn peek(
file: yazi_fs::File,
mime: Cow<'static, str>,
skip: usize,
) -> CancellationToken {
) -> Option<CancellationToken> {
let ct = CancellationToken::new();
let (ct1, ct2) = (ct.clone(), ct.clone());
if let Some(c) = LOADER.read().get(&cmd.name) {
if c.sync_peek {
peek_sync(cmd, file, mime, skip);
} else {
peek_async(cmd, file, mime, skip, ct.clone());
}
return Some(ct).filter(|_| !c.sync_peek);
}

let ct_ = ct.clone();
tokio::spawn(async move {
select! {
_ = ct_.cancelled() => {},
Ok(b) = LOADER.ensure(&cmd.name, |c| c.sync_peek) => {
if b {
peek_sync(cmd, file, mime, skip);
} else {
peek_async(cmd, file, mime, skip, ct_);
}
},
else => {}
}
});

Some(ct)
}

fn peek_sync(cmd: &'static Cmd, file: yazi_fs::File, mime: Cow<'static, str>, skip: usize) {
let cb: PluginCallback = Box::new(move |lua, plugin| {
let job = lua.create_table_from([
("area", Rect::from(LAYOUT.get().preview).into_lua(lua)?),
("args", Sendable::args_to_table_ref(lua, &cmd.args)?.into_lua(lua)?),
("file", File(file).into_lua(lua)?),
("mime", mime.into_lua(lua)?),
("skip", skip.into_lua(lua)?),
])?;

plugin.call_method("peek", job)
});

AppProxy::plugin(PluginOpt::new_callback(&cmd.name, cb));
}

fn peek_async(
cmd: &'static Cmd,
file: yazi_fs::File,
mime: Cow<'static, str>,
skip: usize,
ct: CancellationToken,
) {
let ct_ = ct.clone();
tokio::task::spawn_blocking(move || {
let future = async {
LOADER.ensure(&cmd.name).await.into_lua_err()?;

let lua = slim_lua(&cmd.name)?;
lua.set_hook(
HookTriggers::new().on_calls().on_returns().every_nth_instruction(2000),
move |_, dbg| {
if ct1.is_cancelled() && dbg.source().what != "C" {
if ct.is_cancelled() && dbg.source().what != "C" {
Err("Peek task cancelled".into_lua_err())
} else {
Ok(VmState::Continue)
}
},
);

let plugin: Table = if let Some(b) = LOADER.read().get(&cmd.name) {
lua.load(b.as_bytes()).set_name(&cmd.name).call(())?
let plugin: Table = if let Some(c) = LOADER.read().get(&cmd.name) {
lua.load(c.as_bytes()).set_name(&cmd.name).call(())?
} else {
return Err("unloaded plugin".into_lua_err());
};
Expand All @@ -51,12 +98,12 @@ pub fn peek(
("skip", skip.into_lua(&lua)?),
])?;

if ct2.is_cancelled() { Ok(()) } else { plugin.call_async_method("peek", job).await }
if ct_.is_cancelled() { Ok(()) } else { plugin.call_async_method("peek", job).await }
};

let result = Handle::current().block_on(async {
select! {
_ = ct2.cancelled() => Ok(()),
_ = ct_.cancelled() => Ok(()),
r = future => r,
}
});
Expand All @@ -67,22 +114,4 @@ pub fn peek(
}
}
});

ct
}

pub fn peek_sync(cmd: &'static Cmd, file: yazi_fs::File, mime: Cow<'static, str>, skip: usize) {
let cb: PluginCallback = Box::new(move |lua, plugin| {
let job = lua.create_table_from([
("area", Rect::from(LAYOUT.get().preview).into_lua(lua)?),
("args", Sendable::args_to_table_ref(lua, &cmd.args)?.into_lua(lua)?),
("file", File(file).into_lua(lua)?),
("mime", mime.into_lua(lua)?),
("skip", skip.into_lua(lua)?),
])?;

plugin.call_method("peek", job)
});

AppProxy::plugin(PluginOpt::new_callback(&cmd.name, cb));
}
2 changes: 1 addition & 1 deletion yazi-plugin/src/isolate/preload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub async fn preload(
cmd: &'static Cmd,
file: yazi_fs::File,
) -> mlua::Result<(bool, Option<Error>)> {
LOADER.ensure(&cmd.name).await.into_lua_err()?;
LOADER.ensure(&cmd.name, |_| ()).await.into_lua_err()?;

tokio::task::spawn_blocking(move || {
let lua = slim_lua(&cmd.name)?;
Expand Down
2 changes: 1 addition & 1 deletion yazi-plugin/src/isolate/spot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub fn spot(

tokio::task::spawn_blocking(move || {
let future = async {
LOADER.ensure(&cmd.name).await.into_lua_err()?;
LOADER.ensure(&cmd.name, |_| ()).await.into_lua_err()?;

let lua = slim_lua(&cmd.name)?;
lua.set_hook(
Expand Down
5 changes: 4 additions & 1 deletion yazi-plugin/src/loader/chunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use yazi_shared::natsort;
pub struct Chunk {
pub bytes: Cow<'static, [u8]>,
pub since: String,
pub sync_peek: bool,
pub sync_entry: bool,
}

Expand All @@ -29,6 +30,7 @@ impl Chunk {

let Some(i) = rest.iter().position(|&b| b == b' ' || b == b'\t') else { break };
match (rest[..i].trim_ascii(), rest[i..].trim_ascii()) {
(b"@sync", b"peek") => self.sync_peek = true,
(b"@sync", b"entry") => self.sync_entry = true,

(b"@since", b"") => continue,
Expand All @@ -44,7 +46,8 @@ impl Chunk {

impl From<Cow<'static, [u8]>> for Chunk {
fn from(b: Cow<'static, [u8]>) -> Self {
let mut chunk = Self { bytes: b, since: String::new(), sync_entry: false };
let mut chunk =
Self { bytes: b, since: String::new(), sync_entry: false, sync_peek: false };
chunk.analyze();
chunk
}
Expand Down
50 changes: 13 additions & 37 deletions yazi-plugin/src/loader/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,47 +50,23 @@ impl Default for Loader {
}

impl Loader {
pub async fn ensure(&self, name: &str) -> Result<()> {
if let Some(chunk) = self.cache.read().get(name) {
return Self::compatible_or_error(name, chunk);
}

// TODO: remove this
fn warn(name: &str) {
static WARNED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
if !WARNED.swap(true, std::sync::atomic::Ordering::Relaxed) {
yazi_proxy::AppProxy::notify(yazi_proxy::options::NotifyOpt {
title: "Deprecated entry file".to_owned(),
content: format!(
"The plugin's entry file `init.lua` has been deprecated in favor of the new `main.lua` (user's own `init.lua` remains unchanged).

Please run `ya pack -m` to automatically migrate all plugins, or manually rename your `{name}.yazi/init.lua` to `{name}.yazi/main.lua`."
),
level: yazi_proxy::options::NotifyLevel::Warn,
timeout: std::time::Duration::from_secs(25),
});
}
pub async fn ensure<F, T>(&self, name: &str, f: F) -> Result<T>
where
F: FnOnce(&Chunk) -> T,
{
if let Some(c) = self.cache.read().get(name) {
return Self::compatible_or_error(name, c).map(|_| f(c));
}

let p = BOOT.plugin_dir.join(format!("{name}.yazi/main.lua"));
let chunk = match fs::read(&p).await {
Ok(b) => b,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
let p = BOOT.plugin_dir.join(format!("{name}.yazi/init.lua"));
match fs::read(&p).await {
Ok(b) => {
warn(name);
b
}
Err(e) => Err(e).with_context(|| format!("Failed to load plugin from {p:?}"))?,
}
}
Err(e) => Err(e).with_context(|| format!("Failed to load plugin from {p:?}"))?,
};
let chunk =
fs::read(&p).await.with_context(|| format!("Failed to load plugin from {p:?}"))?.into();

let result = Self::compatible_or_error(name, &chunk);
let inspect = f(&chunk);

let mut cache = self.cache.write();
cache.insert(name.to_owned(), chunk.into());
Self::compatible_or_error(name, cache.get(name).unwrap())
self.cache.write().insert(name.to_owned(), chunk);
result.map(|_| inspect)
}

pub fn load(&self, lua: &Lua, id: &str) -> mlua::Result<Table> {
Expand Down
Loading
Loading