feat: show error when image preview fails (#2706)

This commit is contained in:
三咲雅 · Misaki Masa 2025-05-01 22:58:24 +08:00 committed by GitHub
parent 8ed569b729
commit d6a01a63ef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 79 additions and 72 deletions

View file

@ -35,7 +35,7 @@ function M:peek(job)
if job.skip > 0 and bound < job.skip + limit then
ya.emit("peek", { math.max(0, bound - limit), only_if = job.file.url, upper_bound = true })
else
ya.preview_widgets(job, {
ya.preview_widget(job, {
ui.Text(paths):area(job.area),
ui.Text(sizes):area(job.area):align(ui.Text.RIGHT),
})

View file

@ -1,10 +1,6 @@
local M = {}
function M.msg(job, s)
ya.preview_widgets(job, {
ui.Text(ui.Line(s):reverse()):area(job.area):wrap(ui.Text.WRAP),
})
end
function M.msg(job, s) ya.preview_widget(job, ui.Text(ui.Line(s):reverse()):area(job.area):wrap(ui.Text.WRAP)) end
function M:peek(job)
local path = tostring(job.file.url)
@ -43,7 +39,7 @@ function M:peek(job)
if job.skip > 0 and i < job.skip + limit then
ya.emit("peek", { math.max(0, i - limit), only_if = job.file.url, upper_bound = true })
else
ya.preview_widgets(job, { ui.Text(lines):area(job.area) })
ya.preview_widget(job, ui.Text(lines):area(job.area))
end
end

View file

@ -11,7 +11,7 @@ function M:peek(job)
text = ui.Text(string.format("Failed to start `%s`, error: %s", cmd, err))
end
ya.preview_widgets(job, { text:area(job.area):wrap(ui.Text.WRAP) })
ya.preview_widget(job, text:area(job.area):wrap(ui.Text.WRAP))
end
function M:seek() end

View file

@ -16,9 +16,7 @@ function M:peek(job)
if #folder.files == 0 then
local done, err = folder.stage()
local s = not done and "Loading..." or not err and "No items" or string.format("Error: %s", err)
return ya.preview_widgets(job, {
ui.Text(s):area(job.area):align(ui.Text.CENTER),
})
return ya.preview_widget(job, ui.Text(s):area(job.area):align(ui.Text.CENTER))
end
local entities = {}
@ -26,7 +24,7 @@ function M:peek(job)
entities[#entities + 1] = Entity:new(f):redraw()
end
ya.preview_widgets(job, {
ya.preview_widget(job, {
ui.List(entities):area(job.area),
table.unpack(Marker:new(job.area, folder):redraw()),
})

View file

@ -14,8 +14,9 @@ function M:peek(job)
end
ya.sleep(math.max(0, rt.preview.image_delay / 1000 + start - os.clock()))
ya.image_show(cache, job.area)
ya.preview_widgets(job, {})
local _, err = ya.image_show(cache, job.area)
ya.preview_widget(job, err and ui.Text(tostring(err)):wrap(ui.Text.WRAP))
end
function M:seek() end

View file

@ -7,8 +7,9 @@ function M:peek(job)
end
ya.sleep(math.max(0, rt.preview.image_delay / 1000 + start - os.clock()))
ya.image_show(url, job.area)
ya.preview_widgets(job, {})
local _, err = ya.image_show(url, job.area)
ya.preview_widget(job, err and ui.Text(tostring(err)):area(job.area):wrap(ui.Text.WRAP))
end
function M:seek() end

View file

@ -32,9 +32,10 @@ function M:peek(job)
ya.emit("peek", { math.max(0, i - limit), only_if = job.file.url, upper_bound = true })
else
lines = lines:gsub("\t", string.rep(" ", rt.preview.tab_size))
ya.preview_widgets(job, {
ui.Text.parse(lines):area(job.area):wrap(rt.preview.wrap == "yes" and ui.Text.WRAP or ui.Text.WRAP_NO),
})
ya.preview_widget(
job,
ui.Text.parse(lines):area(job.area):wrap(rt.preview.wrap == "yes" and ui.Text.WRAP or ui.Text.WRAP_NO)
)
end
end

View file

@ -12,8 +12,9 @@ function M:peek(job)
end
ya.sleep(math.max(0, rt.preview.image_delay / 1000 + start - os.clock()))
ya.image_show(cache, job.area)
ya.preview_widgets(job, {})
local _, err = ya.image_show(cache, job.area)
ya.preview_widget(job, err and ui.Text(tostring(err)):wrap(ui.Text.WRAP))
end
function M:seek() end

View file

@ -12,8 +12,9 @@ function M:peek(job)
end
ya.sleep(math.max(0, rt.preview.image_delay / 1000 + start - os.clock()))
ya.image_show(cache, job.area)
ya.preview_widgets(job, {})
local _, err = ya.image_show(cache, job.area)
ya.preview_widget(job, err and ui.Text(tostring(err)):wrap(ui.Text.WRAP))
end
function M:seek(job)

View file

@ -12,8 +12,9 @@ function M:peek(job)
end
ya.sleep(math.max(0, rt.preview.image_delay / 1000 + start - os.clock()))
ya.image_show(cache, job.area)
ya.preview_widgets(job, {})
local _, err = ya.image_show(cache, job.area)
ya.preview_widget(job, err and ui.Text(tostring(err)):wrap(ui.Text.WRAP))
end
function M:seek() end

View file

@ -12,8 +12,9 @@ function M:peek(job)
end
ya.sleep(math.max(0, rt.preview.image_delay / 1000 + start - os.clock()))
ya.image_show(cache, job.area)
ya.preview_widgets(job, {})
local _, err = ya.image_show(cache, job.area)
ya.preview_widget(job, err and ui.Text(tostring(err)):wrap(ui.Text.WRAP))
end
function M:seek(job)

View file

@ -7,7 +7,7 @@ impl UserData for SizeCalculator {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_async_method_mut("recv", |lua, mut me, ()| async move {
match me.0.next().await {
Ok(value) => (value, Value::Nil).into_lua_multi(&lua),
Ok(value) => value.into_lua_multi(&lua),
Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua),
}
});

View file

@ -8,7 +8,7 @@ impl UserData for MpscTx {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_async_method("send", |lua, me, value: Value| async move {
match me.0.send(value).await {
Ok(()) => (true, Value::Nil).into_lua_multi(&lua),
Ok(()) => true.into_lua_multi(&lua),
Err(e) => (false, Error::Custom(e.to_string())).into_lua_multi(&lua),
}
});
@ -32,7 +32,7 @@ pub struct MpscUnboundedRx(pub tokio::sync::mpsc::UnboundedReceiver<Value>);
impl UserData for MpscUnboundedTx {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("send", |lua, me, value: Value| match me.0.send(value) {
Ok(()) => (true, Value::Nil).into_lua_multi(lua),
Ok(()) => true.into_lua_multi(lua),
Err(e) => (false, Error::Custom(e.to_string())).into_lua_multi(lua),
});
}
@ -59,7 +59,7 @@ impl UserData for OneshotTx {
return Err("Oneshot sender already used".into_lua_err());
};
match tx.send(value) {
Ok(()) => (true, Value::Nil).into_lua_multi(lua),
Ok(()) => true.into_lua_multi(lua),
Err(_) => (false, Error::Custom("Oneshot receiver closed".to_string())).into_lua_multi(lua),
}
});
@ -73,7 +73,7 @@ impl UserData for OneshotRx {
return Err("Oneshot receiver already used".into_lua_err());
};
match rx.await {
Ok(value) => (value, Value::Nil).into_lua_multi(&lua),
Ok(value) => value.into_lua_multi(&lua),
Err(e) => (Value::Nil, Error::Custom(e.to_string())).into_lua_multi(&lua),
}
});

View file

@ -1,7 +1,7 @@
use std::mem;
use ansi_to_tui::IntoText;
use mlua::{ExternalError, ExternalResult, IntoLua, Lua, MetaMethod, Table, UserData, Value};
use mlua::{ExternalError, ExternalResult, IntoLua, Lua, MetaMethod, ObjectLike, Table, UserData, Value};
use ratatui::widgets::Widget;
use super::{Area, Line, Span};

View file

@ -37,7 +37,7 @@ fn op(lua: &Lua) -> mlua::Result<Function> {
fn cwd(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|lua, ()| match std::env::current_dir() {
Ok(p) => (Url::new(p), Value::Nil).into_lua_multi(lua),
Ok(p) => Url::new(p).into_lua_multi(lua),
Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(lua),
})
}
@ -51,7 +51,7 @@ fn cha(lua: &Lua) -> mlua::Result<Function> {
};
match meta {
Ok(m) => (Cha::from(m), Value::Nil).into_lua_multi(&lua),
Ok(m) => Cha::from(m).into_lua_multi(&lua),
Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua),
}
})
@ -60,7 +60,7 @@ fn cha(lua: &Lua) -> mlua::Result<Function> {
fn write(lua: &Lua) -> mlua::Result<Function> {
lua.create_async_function(|lua, (url, data): (UrlRef, mlua::String)| async move {
match fs::write(&*url, data.as_bytes()).await {
Ok(()) => (true, Value::Nil).into_lua_multi(&lua),
Ok(()) => true.into_lua_multi(&lua),
Err(e) => (false, Error::Io(e)).into_lua_multi(&lua),
}
})
@ -75,7 +75,7 @@ fn create(lua: &Lua) -> mlua::Result<Function> {
};
match result {
Ok(()) => (true, Value::Nil).into_lua_multi(&lua),
Ok(()) => true.into_lua_multi(&lua),
Err(e) => (false, Error::Io(e)).into_lua_multi(&lua),
}
})
@ -92,7 +92,7 @@ fn remove(lua: &Lua) -> mlua::Result<Function> {
};
match result {
Ok(()) => (true, Value::Nil).into_lua_multi(&lua),
Ok(()) => true.into_lua_multi(&lua),
Err(e) => (false, Error::Io(e)).into_lua_multi(&lua),
}
})
@ -150,7 +150,7 @@ fn read_dir(lua: &Lua) -> mlua::Result<Function> {
tbl.raw_push(f)?;
}
(tbl, Value::Nil).into_lua_multi(&lua)
tbl.into_lua_multi(&lua)
})
}
@ -177,7 +177,7 @@ fn expand_url(lua: &Lua) -> mlua::Result<Function> {
fn unique_name(lua: &Lua) -> mlua::Result<Function> {
lua.create_async_function(|lua, url: UrlRef| async move {
match yazi_fs::unique_name(url.clone(), async { false }).await {
Ok(u) => (Url::new(u), Value::Nil).into_lua_multi(&lua),
Ok(u) => Url::new(u).into_lua_multi(&lua),
Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua),
}
})

View file

@ -139,7 +139,7 @@ impl UserData for Child {
return Err("stdin is not piped".into_lua_err());
};
match stdin.write_all(&src.as_bytes()).await {
Ok(()) => (true, Value::Nil).into_lua_multi(&lua),
Ok(()) => true.into_lua_multi(&lua),
Err(e) => (false, Error::Io(e)).into_lua_multi(&lua),
}
});
@ -148,32 +148,32 @@ impl UserData for Child {
return Err("stdin is not piped".into_lua_err());
};
match stdin.flush().await {
Ok(()) => (true, Value::Nil).into_lua_multi(&lua),
Ok(()) => true.into_lua_multi(&lua),
Err(e) => (false, Error::Io(e)).into_lua_multi(&lua),
}
});
methods.add_async_method_mut("wait", |lua, mut me, ()| async move {
match me.wait().await {
Ok(status) => (Status::new(status), Value::Nil).into_lua_multi(&lua),
Ok(status) => Status::new(status).into_lua_multi(&lua),
Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua),
}
});
methods.add_async_function("wait_with_output", |lua, ud: AnyUserData| async move {
match ud.take::<Self>()?.wait_with_output().await {
Ok(output) => (Output::new(output), Value::Nil).into_lua_multi(&lua),
Ok(output) => Output::new(output).into_lua_multi(&lua),
Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua),
}
});
methods.add_async_method_mut("try_wait", |lua, mut me, ()| async move {
match me.inner.try_wait() {
Ok(Some(status)) => (Status::new(status), Value::Nil).into_lua_multi(&lua),
Ok(None) => (Value::Nil, Value::Nil).into_lua_multi(&lua),
Ok(Some(status)) => Status::new(status).into_lua_multi(&lua),
Ok(None) => Value::Nil.into_lua_multi(&lua),
Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua),
}
});
methods.add_method_mut("start_kill", |lua, me, ()| match me.inner.start_kill() {
Ok(_) => (true, Value::Nil).into_lua_multi(lua),
Ok(_) => true.into_lua_multi(lua),
Err(e) => (false, Error::Io(e)).into_lua_multi(lua),
});

View file

@ -176,18 +176,18 @@ impl UserData for Command {
Ok(ud)
});
methods.add_method_mut("spawn", |lua, me, ()| match me.spawn() {
Ok(child) => (child, Value::Nil).into_lua_multi(lua),
Ok(child) => child.into_lua_multi(lua),
Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(lua),
});
methods.add_async_method_mut("output", |lua, mut me, ()| async move {
match me.output().await {
Ok(output) => (Output::new(output), Value::Nil).into_lua_multi(&lua),
Ok(output) => Output::new(output).into_lua_multi(&lua),
Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua),
}
});
methods.add_async_method_mut("status", |lua, mut me, ()| async move {
match me.status().await {
Ok(status) => (Status::new(status), Value::Nil).into_lua_multi(&lua),
Ok(status) => Status::new(status).into_lua_multi(&lua),
Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua),
}
});

View file

@ -1,6 +1,6 @@
use mlua::{Function, IntoLua, Lua, Value};
use mlua::{Function, IntoLuaMulti, Lua, Value};
use yazi_adapter::{ADAPTOR, Image};
use yazi_binding::UrlRef;
use yazi_binding::{Error, UrlRef};
use super::Utils;
use crate::{bindings::ImageInfo, elements::Rect};
@ -8,20 +8,18 @@ use crate::{bindings::ImageInfo, elements::Rect};
impl Utils {
pub(super) fn image_info(lua: &Lua) -> mlua::Result<Function> {
lua.create_async_function(|lua, url: UrlRef| async move {
if let Ok(info) = yazi_adapter::ImageInfo::new(&url).await {
ImageInfo::from(info).into_lua(&lua)
} else {
Value::Nil.into_lua(&lua)
match yazi_adapter::ImageInfo::new(&url).await {
Ok(info) => ImageInfo::from(info).into_lua_multi(&lua),
Err(e) => (Value::Nil, Error::Custom(e.to_string())).into_lua_multi(&lua),
}
})
}
pub(super) fn image_show(lua: &Lua) -> mlua::Result<Function> {
lua.create_async_function(|lua, (url, rect): (UrlRef, Rect)| async move {
if let Ok(area) = ADAPTOR.get().image_show(&url, *rect).await {
Rect::from(area).into_lua(&lua)
} else {
Value::Nil.into_lua(&lua)
match ADAPTOR.get().image_show(&url, *rect).await {
Ok(area) => Rect::from(area).into_lua_multi(&lua),
Err(e) => (Value::Nil, Error::Custom(e.to_string())).into_lua_multi(&lua),
}
})
}

View file

@ -8,7 +8,7 @@ impl Utils {
pub(super) fn json_encode(lua: &Lua) -> mlua::Result<Function> {
lua.create_async_function(|lua, value: Value| async move {
match serde_json::to_string(&value) {
Ok(s) => (s, Value::Nil).into_lua_multi(&lua),
Ok(s) => s.into_lua_multi(&lua),
Err(e) => (Value::Nil, Error::Serde(e)).into_lua_multi(&lua),
}
})
@ -17,7 +17,7 @@ impl Utils {
pub(super) fn json_decode(lua: &Lua) -> mlua::Result<Function> {
lua.create_async_function(|lua, s: mlua::String| async move {
match serde_json::from_slice::<serde_json::Value>(&s.as_bytes()) {
Ok(v) => (lua.to_value_with(&v, OPTS)?, Value::Nil).into_lua_multi(&lua),
Ok(v) => lua.to_value_with(&v, OPTS)?.into_lua_multi(&lua),
Err(e) => (Value::Nil, Error::Serde(e)).into_lua_multi(&lua),
}
})

View file

@ -59,10 +59,9 @@ impl Utils {
if debounce < 0.0 {
Err("negative debounce duration".into_lua_err())
} else if debounce == 0.0 {
(InputRx::new(rx), Value::Nil).into_lua_multi(&lua)
InputRx::new(rx).into_lua_multi(&lua)
} else {
(InputRx::new(Debounce::new(rx, Duration::from_secs_f64(debounce))), Value::Nil)
.into_lua_multi(&lua)
InputRx::new(Debounce::new(rx, Duration::from_secs_f64(debounce))).into_lua_multi(&lua)
}
})
}

View file

@ -1,4 +1,4 @@
use mlua::{AnyUserData, Function, IntoLuaMulti, Lua, Table, Value};
use mlua::{AnyUserData, ExternalError, Function, IntoLuaMulti, Lua, Table, Value};
use yazi_config::{YAZI, preview::PreviewWrap};
use yazi_macro::emit;
use yazi_shared::{errors::PeekError, event::Cmd};
@ -44,7 +44,7 @@ impl Utils {
Ok(text) => text,
Err(e @ PeekError::Exceed(max)) => return (e.to_string(), max).into_lua_multi(&lua),
Err(e @ PeekError::Unexpected(_)) => {
return (e.to_string(), Value::Nil).into_lua_multi(&lua);
return e.to_string().into_lua_multi(&lua);
}
};
@ -56,14 +56,22 @@ impl Utils {
})];
emit!(Call(Cmd::new("mgr:update_peeked").with_any("lock", lock)));
(Value::Nil, Value::Nil).into_lua_multi(&lua)
().into_lua_multi(&lua)
})
}
pub(super) fn preview_widgets(lua: &Lua) -> mlua::Result<Function> {
lua.create_async_function(|_, (t, widgets): (Table, Vec<AnyUserData>)| async move {
pub(super) fn preview_widget(lua: &Lua) -> mlua::Result<Function> {
lua.create_async_function(|_, (t, value): (Table, Value)| async move {
let mut lock = PreviewLock::try_from(t)?;
lock.data = widgets.into_iter().map(Renderable::try_from).collect::<mlua::Result<_>>()?;
lock.data = match value {
Value::Nil => vec![],
Value::Table(tbl) => tbl
.sequence_values::<AnyUserData>()
.map(|ud| ud.and_then(Renderable::try_from))
.collect::<mlua::Result<_>>()?,
Value::UserData(ud) => vec![Renderable::try_from(ud)?],
_ => Err("preview widget must be a renderable element or a table of them".into_lua_err())?,
};
emit!(Call(Cmd::new("mgr:update_peeked").with_any("lock", lock)));
Ok(())

View file

@ -44,7 +44,8 @@ pub fn compose(lua: &Lua, isolate: bool) -> mlua::Result<Value> {
// Preview
b"preview_code" => Utils::preview_code(lua)?,
b"preview_widgets" => Utils::preview_widgets(lua)?,
b"preview_widget" => Utils::preview_widget(lua)?,
b"preview_widgets" => Utils::preview_widget(lua)?, // TODO: remove this in the future
// Process
b"proc_info" => Utils::proc_info(lua)?,