refactor: move Lua bindings down into each subcrates (#4049)

This commit is contained in:
三咲雅 misaki masa 2026-06-18 15:06:31 +08:00 committed by GitHub
parent f1e93d7f52
commit 446a5721bf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
439 changed files with 3465 additions and 4058 deletions

420
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -50,6 +50,7 @@ foldhash = "0.2.0"
futures = "0.3.32"
globset = "0.4.18"
hashbrown = { version = "0.17.1", features = [ "serde" ] }
image = { version = "0.25.10", default-features = false, features = [ "avif", "bmp", "dds", "exr", "ff", "gif", "hdr", "ico", "jpeg", "png", "pnm", "qoi", "tga", "tiff", "webp" ] }
indexmap = { version = "2.14.0", features = [ "serde" ] }
inventory = "0.3.24"
libc = "0.2.186"

View file

@ -18,8 +18,6 @@ impl Actor for Dnd {
const NAME: &str = "dnd";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
let event = yazi_binding::DndEvent::from(form.event);
let Some(size) = cx.term.as_ref().and_then(|t| t.size().ok()) else { succ!() };
let area = yazi_binding::elements::Rect::from(size);
@ -27,10 +25,10 @@ impl Actor for Dnd {
runtime_scope!(LUA, "root", {
let root = LUA.globals().raw_get::<Table>("Root")?.call_method::<Table>("new", area)?;
if event.is_drag() {
root.call_method::<()>("drag", event)?;
if form.event.is_drag() {
root.call_method::<()>("drag", form.event)?;
} else {
root.call_method::<()>("drop", event)?;
root.call_method::<()>("drop", form.event)?;
}
Ok(())

View file

@ -1,9 +1,9 @@
use anyhow::Result;
use yazi_binding::{Sendable, runtime_scope};
use yazi_binding::runtime_scope;
use yazi_macro::succ;
use yazi_parser::app::LuaForm;
use yazi_plugin::LUA;
use yazi_shared::data::Data;
use yazi_shared::data::{Data, Sendable};
use crate::{Actor, Ctx, lives::Lives};

View file

@ -19,8 +19,6 @@ impl Actor for Mouse {
const NAME: &str = "mouse";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
let event = yazi_binding::MouseEvent::from(form.event);
let Some(size) = cx.term.as_ref().and_then(|t| t.size().ok()) else { succ!() };
let area = yazi_binding::elements::Rect::from(size);
@ -28,18 +26,18 @@ impl Actor for Mouse {
runtime_scope!(LUA, "root", {
let root = LUA.globals().raw_get::<Table>("Root")?.call_method::<Table>("new", area)?;
match event.kind {
MouseEventKind::Down(_) => root.call_method("click", (event, false))?,
MouseEventKind::Up(_) => root.call_method("click", (event, true))?,
match form.event.kind {
MouseEventKind::Down(_) => root.call_method("click", (form.event, false))?,
MouseEventKind::Up(_) => root.call_method("click", (form.event, true))?,
MouseEventKind::ScrollDown => root.call_method("scroll", (event, 1))?,
MouseEventKind::ScrollUp => root.call_method("scroll", (event, -1))?,
MouseEventKind::ScrollDown => root.call_method("scroll", (form.event, 1))?,
MouseEventKind::ScrollUp => root.call_method("scroll", (form.event, -1))?,
MouseEventKind::ScrollRight => root.call_method("touch", (event, 1))?,
MouseEventKind::ScrollLeft => root.call_method("touch", (event, -1))?,
MouseEventKind::ScrollRight => root.call_method("touch", (form.event, 1))?,
MouseEventKind::ScrollLeft => root.call_method("touch", (form.event, -1))?,
MouseEventKind::Moved => root.call_method("move", event)?,
MouseEventKind::Drag(_) => root.call_method("drag", event)?,
MouseEventKind::Moved => root.call_method("move", form.event)?,
MouseEventKind::Drag(_) => root.call_method("drag", form.event)?,
}
Ok(())

View file

@ -3,8 +3,7 @@ use yazi_actor::Ctx;
use yazi_macro::{succ, writef};
use yazi_parser::{app::TitleForm, spark::SparkKind};
use yazi_shared::{Source, data::Data};
use yazi_term::sequence::SetTitle;
use yazi_tty::TTY;
use yazi_tty::{TTY, sequence::SetTitle};
use yazi_tui::RatermState;
use crate::Actor;

View file

@ -2,8 +2,8 @@ use std::ops::{Deref, DerefMut};
use anyhow::{Result, anyhow};
use yazi_core::{Core, mgr::Tabs, tab::{Folder, Tab}};
use yazi_fs::File;
use yazi_shared::{Id, Source, event::Action, url::UrlBuf};
use yazi_fs::file::File;
use yazi_shared::{Source, event::Action, id::Id, url::UrlBuf};
use yazi_tui::Raterm;
pub struct Ctx<'a> {

View file

@ -1,28 +1,31 @@
use anyhow::Result;
use ratatui::widgets::Padding;
use yazi_config::{THEME, YAZI};
use yazi_macro::{act, render, succ};
use yazi_parser::input::ShowForm;
use yazi_shared::data::Data;
use yazi_widgets::input::InputOpt;
use crate::{Actor, Ctx};
pub struct Show;
impl Actor for Show {
type Form = InputOpt;
type Form = ShowForm;
const NAME: &str = "show";
fn act(cx: &mut Ctx, mut form: Self::Form) -> Result<Data> {
fn act(cx: &mut Ctx, Self::Form { mut opt }: Self::Form) -> Result<Data> {
act!(input:close, cx)?;
let input = &mut cx.input;
input.main_visible = true;
input.main_title = form.cfg.title.clone();
input.main_position = form.cfg.position;
input.main_title = opt.title.clone();
input.main_position = opt.position;
form.cfg.position = form.cfg.position.padding(Padding::uniform(1));
input.main = yazi_widgets::input::Input::new(form)?;
opt.styles = (&THEME.input).into();
opt.blinking = YAZI.input.cursor_blink;
opt.position = opt.position.padding(Padding::uniform(1));
input.main = yazi_widgets::input::Input::new(opt)?;
succ!(render!());
}

View file

@ -63,9 +63,7 @@ impl UserData for Core {
b"yanked" => reuse!(yanked, super::Yanked::make(&me.mgr.yanked)),
b"input" => reuse!(input, super::Input::make(&me.input)),
b"which" => reuse!(which, super::Which::make(&me.which)),
b"layer" => {
reuse!(layer, Ok::<_, mlua::Error>(yazi_binding::Layer::from(me.layer())))
}
b"layer" => reuse!(layer, Ok::<_, mlua::Error>(me.layer())),
_ => Value::Nil,
})
});

View file

@ -1,7 +1,7 @@
use std::{ops::Deref, ptr};
use mlua::{AnyUserData, IntoLua, UserData, UserDataFields, UserDataMethods};
use yazi_binding::{Range, Style};
use yazi_binding::{Range, style::Style};
use yazi_config::THEME;
use yazi_shared::{path::AsPath, url::UrlLike};
@ -15,13 +15,13 @@ pub(super) struct File {
}
impl Deref for File {
type Target = yazi_fs::File;
type Target = yazi_fs::file::File;
fn deref(&self) -> &Self::Target { &self.folder.files[self.idx] }
}
impl AsRef<yazi_fs::File> for File {
fn as_ref(&self) -> &yazi_fs::File { self }
impl AsRef<yazi_fs::file::File> for File {
fn as_ref(&self) -> &yazi_fs::file::File { self }
}
impl File {
@ -49,7 +49,7 @@ impl File {
impl UserData for File {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
yazi_binding::impl_file_fields!(fields);
fields.add_cached_field("bare", |_, me| Ok(yazi_binding::File::new(&**me)));
fields.add_cached_field("bare", |_, me| Ok(yazi_fs::file::File::from(&**me)));
fields.add_field_method_get("idx", |_, me| Ok(me.idx + 1));
fields.add_field_method_get("is_hovered", |_, me| Ok(me.is_hovered()));
@ -63,9 +63,8 @@ impl UserData for File {
yazi_binding::impl_file_methods!(methods);
methods.add_method("icon", |_, me, ()| {
use yazi_binding::Icon;
// TODO: use a cache
Ok(yazi_config::THEME.icon.matches(me, me.is_hovered()).map(Icon::from))
Ok(yazi_config::THEME.icon.matches(me, me.is_hovered()))
});
methods.add_method("size", |_, me, ()| {
Ok(if me.is_dir() { me.folder.files.sizes.get(&me.urn()).copied() } else { Some(me.len) })

View file

@ -1,7 +1,6 @@
use std::ops::{Deref, Range};
use mlua::{AnyUserData, UserData, UserDataFields};
use yazi_binding::{FolderStage, Url};
use yazi_config::LAYOUT;
use yazi_shim::mlua::UserDataFieldsExt;
@ -39,9 +38,9 @@ impl Folder {
impl UserData for Folder {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_cached_field("cwd", |_, me| Ok(Url::new(&me.url)));
fields.add_cached_field("cwd", |_, me| Ok(me.url.clone()));
fields.add_static_field("files", |_, me| Files::make(0..me.files.len(), me, &me.tab));
fields.add_cached_field("stage", |_, me| Ok(FolderStage::new(me.stage.clone())));
fields.add_cached_field("stage", |_, me| Ok(me.stage.clone()));
fields.add_static_field("window", |_, me| Files::make(me.window.clone(), me, &me.tab));
fields.add_field_method_get("offset", |_, me| Ok(me.offset));

View file

@ -10,8 +10,9 @@ use super::{Core, PtrCell};
use crate::lives::MutCell;
pub(super) static TO_DESTROY: MutCell<Vec<AnyUserData>> = MutCell::new(Vec::new());
pub(super) static FILE_CACHE: MutCell<MaybeUninit<HashMap<PtrCell<yazi_fs::File>, AnyUserData>>> =
MutCell::new(MaybeUninit::uninit());
pub(super) static FILE_CACHE: MutCell<
MaybeUninit<HashMap<PtrCell<yazi_fs::file::File>, AnyUserData>>,
> = MutCell::new(MaybeUninit::uninit());
pub struct Lives;

View file

@ -1,4 +1,5 @@
use mlua::AnyUserData;
use yazi_shared::url::UrlBuf;
use super::Lives;
use crate::lives::PtrCell;
@ -11,7 +12,7 @@ impl Selected {
let inner = PtrCell::from(inner);
Lives::scoped_userdata(yazi_binding::Iter::new(
inner.as_static().values().map(yazi_binding::Url::new),
inner.as_static().values().map(UrlBuf::from),
Some(inner.len()),
))
}

View file

@ -1,7 +1,7 @@
use std::{borrow::Cow, ops::Deref};
use mlua::{AnyUserData, UserData, UserDataFields, UserDataMethods};
use yazi_binding::{Id, UrlRef};
use yazi_shared::url::UrlRef;
use yazi_shim::mlua::UserDataFieldsExt;
use super::{Finder, Folder, Lives, Mode, Preference, Preview, PtrCell, Selected};
@ -24,7 +24,7 @@ impl Tab {
impl UserData for Tab {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("id", |_, me| Ok(Id(me.id)));
fields.add_field_method_get("id", |_, me| Ok(me.id));
fields.add_cached_field("name", |lua, me| match me.name() {
Cow::Borrowed(s) => lua.create_string(s),
Cow::Owned(s) => lua.create_external_string(s),

View file

@ -1,9 +1,8 @@
use std::ops::Deref;
use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields};
use yazi_binding::SER_OPT;
use yazi_scheduler::Progress;
use yazi_shim::mlua::UserDataFieldsExt;
use yazi_shim::mlua::{SER_OPT, UserDataFieldsExt};
use super::{Lives, PtrCell};

View file

@ -1,8 +1,7 @@
use std::ops::Deref;
use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields};
use yazi_binding::SER_OPT;
use yazi_shim::mlua::UserDataFieldsExt;
use yazi_shim::mlua::{SER_OPT, UserDataFieldsExt};
use super::{Lives, PtrCell};
use crate::lives::{Behavior, TaskSnap};

View file

@ -26,7 +26,7 @@ impl UserData for Which {
fields.add_cached_field("tx", |_, me| Ok(me.tx.clone().map(yazi_binding::MpscUnboundedTx)));
fields.add_field_method_get("times", |_, me| Ok(me.inner.times));
fields.add_cached_field("cands", |lua, me| {
lua.create_sequence_from(me.inner.cands.iter().map(yazi_binding::keymap::Chord::from))
lua.create_sequence_from(me.inner.cands.iter().cloned())
});
fields.add_field_method_get("active", |_, me| Ok(me.inner.active));

View file

@ -1,7 +1,9 @@
use std::ops::Deref;
use mlua::{AnyUserData, MetaMethod, MultiValue, ObjectLike, UserData, UserDataFields, UserDataMethods};
use yazi_binding::{Iter, get_metatable};
use yazi_binding::Iter;
use yazi_shared::url::UrlBuf;
use yazi_shim::mlua::get_metatable;
use super::{Lives, PtrCell};
@ -23,7 +25,7 @@ impl Yanked {
Lives::scoped_userdata(Self {
inner,
iter: Lives::scoped_userdata(Iter::new(
inner.as_static().iter().map(yazi_binding::Url::new),
inner.as_static().iter().map(UrlBuf::from),
Some(inner.len()),
))?,
})

View file

@ -1,18 +1,18 @@
use std::{fmt::{self, Display}, io::{self, Read, Write}, path::{MAIN_SEPARATOR, Path}, sync::Arc};
use std::{fmt::{self, Display}, io::{self, Read, Write}, path::{MAIN_SEPARATOR, Path}};
use anyhow::{Result, anyhow};
use scopeguard::defer;
use yazi_binding::Permit;
use yazi_config::{YAZI, opener::OpenerRule};
use yazi_fs::{File, FilesOp, Splatter, provider::{Provider, local::Local}};
use yazi_config::{YAZI, opener::OpenerRuleArc};
use yazi_fs::{FilesOp, Splatter, file::File, provider::{Provider, local::Local}};
use yazi_macro::{succ, writef};
use yazi_parser::VoidForm;
use yazi_proxy::TasksProxy;
use yazi_scheduler::{AppProxy, NotifyProxy};
use yazi_shared::{data::Data, strand::Strand, url::{AsUrl, UrlBuf, UrlCow, UrlLike}};
use yazi_shim::path::CROSS_SEPARATOR;
use yazi_term::{YIELD_TO_SUBPROCESS, sequence::EraseScreen};
use yazi_tty::TTY;
use yazi_term::YIELD_TO_SUBPROCESS;
use yazi_tty::{TTY, sequence::EraseScreen};
use yazi_vfs::{VfsFile, provider};
use yazi_watcher::WATCHER;
@ -106,7 +106,7 @@ impl BulkCreate {
Ok(())
}
fn opener() -> Option<Arc<OpenerRule>> {
fn opener() -> Option<OpenerRuleArc> {
YAZI
.open
.match_dummy(Path::new("bulk-create.txt"), "text/plain")

View file

@ -1,20 +1,20 @@
use std::{hash::Hash, io::{Read, Write}, ops::Deref, path::Path, sync::Arc};
use std::{hash::Hash, io::{Read, Write}, ops::Deref, path::Path};
use anyhow::{Result, anyhow};
use hashbrown::HashMap;
use scopeguard::defer;
use tokio::io::AsyncWriteExt;
use yazi_binding::Permit;
use yazi_config::{YAZI, opener::OpenerRule};
use yazi_config::{YAZI, opener::OpenerRuleArc};
use yazi_dds::Pubsub;
use yazi_fs::{File, FilesOp, Splatter, max_common_root, path::skip_url, provider::{FileBuilder, Provider, local::{Gate, Local}}};
use yazi_fs::{FilesOp, Splatter, file::File, max_common_root, path::skip_url, provider::{FileBuilder, Provider, local::{Gate, Local}}};
use yazi_macro::{err, succ, writef};
use yazi_parser::VoidForm;
use yazi_proxy::TasksProxy;
use yazi_scheduler::{AppProxy, NotifyProxy};
use yazi_shared::{data::Data, path::PathDyn, strand::{AsStrand, AsStrandJoin, Strand, StrandBuf, StrandLike}, url::{AsUrl, UrlBuf, UrlCow, UrlLike}};
use yazi_term::{YIELD_TO_SUBPROCESS, sequence::EraseScreen};
use yazi_tty::TTY;
use yazi_term::YIELD_TO_SUBPROCESS;
use yazi_tty::{TTY, sequence::EraseScreen};
use yazi_vfs::{VfsFile, maybe_exists, provider};
use yazi_watcher::WATCHER;
@ -152,7 +152,7 @@ impl BulkRename {
Ok(())
}
fn opener() -> Option<Arc<OpenerRule>> {
fn opener() -> Option<OpenerRuleArc> {
YAZI
.open
.match_dummy(Path::new("bulk-rename.txt"), "text/plain")

View file

@ -3,10 +3,10 @@ use std::{mem, time::Duration};
use anyhow::Result;
use tokio::pin;
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
use yazi_config::popup::InputCfg;
use yazi_config::YAZI;
use yazi_core::mgr::CdSource;
use yazi_dds::Pubsub;
use yazi_fs::{File, FilesOp, path::{clean_url, expand_url}};
use yazi_fs::{FilesOp, file::File, path::{clean_url, expand_url}};
use yazi_macro::{act, err, input, render, succ};
use yazi_parser::mgr::CdForm;
use yazi_proxy::{CmpProxy, MgrProxy};
@ -63,7 +63,7 @@ impl Actor for Cd {
impl Cd {
fn cd_interactive(cx: &mut Ctx) -> Result<Data> {
let input = input!(cx, InputCfg::cd(cx.cwd().as_url()))?;
let input = input!(cx, YAZI.input.cd(cx.cwd().as_url()))?;
tokio::spawn(async move {
let rx = Debounce::new(UnboundedReceiverStream::new(input), Duration::from_millis(50));

View file

@ -1,6 +1,6 @@
use anyhow::{Result, bail};
use yazi_config::popup::{ConfirmCfg, InputCfg};
use yazi_fs::{File, FilesOp};
use yazi_config::{YAZI, popup::ConfirmCfg};
use yazi_fs::{FilesOp, file::File};
use yazi_macro::{input, ok_or_not_found, succ};
use yazi_parser::mgr::CreateForm;
use yazi_proxy::{ConfirmProxy, MgrProxy};
@ -20,7 +20,7 @@ impl Actor for Create {
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
let cwd = cx.cwd().to_owned();
let mut input = input!(cx, InputCfg::create(form.dir))?;
let mut input = input!(cx, YAZI.input.create(form.dir))?;
tokio::spawn(async move {
let Some(InputEvent::Submit(name)) = input.recv().await else { return };

View file

@ -4,7 +4,7 @@ use anyhow::Result;
use futures::{StreamExt, stream::FuturesUnordered};
use hashbrown::HashSet;
use yazi_core::mgr::OpenOpt;
use yazi_fs::{File, FsScheme, provider::{Provider, local::Local}};
use yazi_fs::{FsScheme, file::File, provider::{Provider, local::Local}};
use yazi_macro::succ;
use yazi_parser::mgr::DownloadForm;
use yazi_proxy::MgrProxy;

View file

@ -3,7 +3,7 @@ use std::time::Duration;
use anyhow::Result;
use tokio::pin;
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
use yazi_config::popup::InputCfg;
use yazi_config::YAZI;
use yazi_core::mgr::FilterOpt;
use yazi_macro::{input, succ};
use yazi_parser::mgr::FilterForm;
@ -21,7 +21,7 @@ impl Actor for Filter {
const NAME: &str = "filter";
fn act(cx: &mut Ctx, Self::Form { opt }: Self::Form) -> Result<Data> {
let input = input!(cx, InputCfg::filter())?;
let input = input!(cx, YAZI.input.filter())?;
tokio::spawn(async move {
let rx = Debounce::new(UnboundedReceiverStream::new(input), Duration::from_millis(50));

View file

@ -3,7 +3,7 @@ use std::time::Duration;
use anyhow::Result;
use tokio::pin;
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
use yazi_config::popup::InputCfg;
use yazi_config::YAZI;
use yazi_core::mgr::FindDoOpt;
use yazi_macro::{input, succ};
use yazi_parser::mgr::FindForm;
@ -21,7 +21,7 @@ impl Actor for Find {
const NAME: &str = "find";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
let input = input!(cx, InputCfg::find(form.prev))?;
let input = input!(cx, YAZI.input.find(form.prev))?;
tokio::spawn(async move {
let rx = Debounce::new(UnboundedReceiverStream::new(input), Duration::from_millis(50));

View file

@ -3,7 +3,7 @@ use futures::StreamExt;
use hashbrown::HashSet;
use yazi_boot::ARGS;
use yazi_core::mgr::OpenDoOpt;
use yazi_fs::File;
use yazi_fs::file::File;
use yazi_macro::{act, succ};
use yazi_parser::mgr::OpenForm;
use yazi_proxy::MgrProxy;

View file

@ -1,7 +1,8 @@
use anyhow::Result;
use hashbrown::HashMap;
use indexmap::IndexSet;
use yazi_config::{YAZI, popup::PickCfg};
use yazi_config::YAZI;
use yazi_fs::file::File;
use yazi_macro::succ;
use yazi_parser::mgr::OpenDoForm;
use yazi_proxy::{PickProxy, TasksProxy};
@ -40,7 +41,7 @@ impl Actor for OpenDo {
succ!();
}
let pick = PickProxy::show(PickCfg::open(openers.iter().map(|o| o.desc()).collect()));
let pick = PickProxy::show(YAZI.pick.open(openers.iter().map(|o| o.desc()).collect()));
let urls: Vec<_> = [UrlCow::default()]
.into_iter()
.chain(targets.into_iter().map(|(file, _)| file.url.into()))
@ -63,7 +64,7 @@ impl Actor for OpenDo {
impl OpenDo {
// TODO: remove
fn match_and_open(cx: &Ctx, cwd: UrlBuf, targets: Vec<(yazi_fs::File, &str)>) {
fn match_and_open(cx: &Ctx, cwd: UrlBuf, targets: Vec<(File, &str)>) {
let mut openers = HashMap::new();
for (file, mime) in targets {
if let Some(open) = YAZI.open.matches(&file, mime)

View file

@ -1,11 +1,11 @@
use anyhow::Result;
use yazi_config::popup::{ConfirmCfg, InputCfg};
use yazi_config::{YAZI, popup::ConfirmCfg};
use yazi_dds::Pubsub;
use yazi_fs::{File, FilesOp};
use yazi_fs::{FilesOp, file::File};
use yazi_macro::{act, err, input, ok_or_not_found, succ};
use yazi_parser::mgr::RenameForm;
use yazi_proxy::{ConfirmProxy, MgrProxy};
use yazi_shared::{Id, data::Data, url::{UrlBuf, UrlLike}};
use yazi_shared::{data::Data, id::Id, url::{UrlBuf, UrlLike}};
use yazi_vfs::{VfsFile, maybe_exists, provider};
use yazi_watcher::WATCHER;
use yazi_widgets::input::InputEvent;
@ -42,7 +42,7 @@ impl Actor for Rename {
};
let (tab, old) = (cx.tab().id, hovered.url_owned());
let mut input = input!(cx, InputCfg::rename().with_value(name).with_cursor(cursor))?;
let mut input = input!(cx, YAZI.input.rename().with_value(name).with_cursor(cursor))?;
tokio::spawn(async move {
let Some(InputEvent::Submit(name)) = input.recv().await else { return };

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use yazi_fs::{File, FilesOp};
use yazi_fs::{FilesOp, file::File};
use yazi_macro::{act, render, succ};
use yazi_parser::mgr::RevealForm;
use yazi_shared::{data::Data, url::UrlLike};

View file

@ -3,7 +3,7 @@ use std::{borrow::Cow, time::Duration};
use anyhow::Result;
use tokio::pin;
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
use yazi_config::popup::InputCfg;
use yazi_config::YAZI;
use yazi_core::mgr::{CdSource, SearchVia};
use yazi_fs::{FilesOp, cha::Cha};
use yazi_macro::{act, input, succ};
@ -28,7 +28,7 @@ impl Actor for Search {
handle.abort();
}
let mut input = input!(cx, InputCfg::search(opt.via.into()).with_value(&*opt.subject))?;
let mut input = input!(cx, YAZI.input.search(opt.via.into()).with_value(&*opt.subject))?;
tokio::spawn(async move {
if let Some(InputEvent::Submit(subject)) = input.recv().await {

View file

@ -1,7 +1,7 @@
use std::borrow::Cow;
use anyhow::Result;
use yazi_config::popup::InputCfg;
use yazi_config::YAZI;
use yazi_macro::{act, input, succ};
use yazi_parser::mgr::ShellForm;
use yazi_proxy::TasksProxy;
@ -25,7 +25,10 @@ impl Actor for Shell {
let selected: Vec<_> = cx.tab().hovered_and_selected().cloned().map(Into::into).collect();
let input = if form.interactive {
Some(input!(cx, InputCfg::shell(form.block).with_value(&*form.run).with_cursor(form.cursor))?)
Some(input!(
cx,
YAZI.input.shell(form.block).with_value(&*form.run).with_cursor(form.cursor)
)?)
} else {
None
};

View file

@ -1,7 +1,7 @@
use std::borrow::Cow;
use anyhow::Result;
use yazi_config::popup::InputCfg;
use yazi_config::YAZI;
use yazi_macro::{act, input, render, succ};
use yazi_parser::mgr::TabRenameForm;
use yazi_proxy::MgrProxy;
@ -29,7 +29,7 @@ impl Actor for TabRename {
let mut input = input!(
cx,
InputCfg::tab_rename().with_value(form.name.unwrap_or(Cow::Borrowed(&pref.name)))
YAZI.input.tab_rename().with_value(form.name.unwrap_or(Cow::Borrowed(&pref.name)))
)?;
tokio::spawn(async move {

View file

@ -8,8 +8,8 @@ use yazi_macro::{succ, writef};
use yazi_parser::VoidForm;
use yazi_scheduler::AppProxy;
use yazi_shared::data::Data;
use yazi_term::{TERM, YIELD_TO_SUBPROCESS, sequence::EraseScreen};
use yazi_tty::TTY;
use yazi_term::{TERM, YIELD_TO_SUBPROCESS};
use yazi_tty::{TTY, sequence::EraseScreen};
use crate::{Actor, Ctx};

View file

@ -19,14 +19,15 @@ yazi-fs = { path = "../yazi-fs", version = "26.5.6" }
yazi-macro = { path = "../yazi-macro", version = "26.5.6" }
yazi-shared = { path = "../yazi-shared", version = "26.5.6" }
yazi-shim = { path = "../yazi-shim", version = "26.5.6" }
yazi-term = { path = "../yazi-term", version = "26.5.6" }
yazi-tty = { path = "../yazi-tty", version = "26.5.9" }
yazi-widgets = { path = "../yazi-widgets", version = "26.5.6" }
# External dependencies
ansi-to-tui = { workspace = true }
anyhow = { workspace = true }
base64 = { workspace = true }
image = { version = "0.25.10", default-features = false, features = [ "avif", "bmp", "dds", "exr", "ff", "gif", "hdr", "ico", "jpeg", "png", "pnm", "qoi", "tga", "tiff", "webp" ] }
image = { workspace = true }
inventory = { workspace = true }
moxcms = "0.8.1"
palette = { version = "0.7.6", default-features = false }
quantette = { version = "0.6.0", default-features = false }

View file

@ -1,105 +1,56 @@
use std::{env, path::PathBuf};
use std::{fmt::{self, Debug}, ops::Deref};
use anyhow::Result;
use ratatui::layout::Rect;
use strum::{Display, IntoStaticStr};
use tracing::warn;
use yazi_emulator::{Emulator, TMUX};
use yazi_shared::env_exists;
use yazi_emulator::EMULATOR;
use yazi_shim::cell::SyncCell;
use yazi_widgets::clear::ClearInventory;
use crate::{Adapters, SHOWN, drivers};
use crate::{ADAPTOR, drivers::{Driver, Drivers}};
#[derive(Clone, Copy, Debug, Display, Eq, IntoStaticStr, PartialEq)]
#[strum(serialize_all = "kebab-case")]
pub enum Adapter {
Kgp,
KgpOld,
Iip,
Sixel,
pub struct Adapter {
driver: Driver,
shown: SyncCell<Option<Rect>>,
pub collision: SyncCell<bool>,
}
// Supported by Überzug++
X11,
Wayland,
Chafa,
impl Deref for Adapter {
type Target = Driver;
fn deref(&self) -> &Self::Target { &self.driver }
}
impl Debug for Adapter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.driver.fmt(f) }
}
impl Adapter {
pub async fn image_show<P>(self, path: P, max: Rect) -> Result<Rect>
where
P: Into<PathBuf>,
{
if max.is_empty() {
return Ok(Rect::default());
}
let path = path.into();
match self {
Self::Kgp => drivers::Kgp::image_show(path, max).await,
Self::KgpOld => drivers::KgpOld::image_show(path, max).await,
Self::Iip => drivers::Iip::image_show(path, max).await,
Self::Sixel => drivers::Sixel::image_show(path, max).await,
Self::X11 | Self::Wayland => drivers::Ueberzug::image_show(path, max).await,
Self::Chafa => drivers::Chafa::image_show(path, max).await,
pub(super) fn new() -> Self {
Self {
driver: Drivers::matches(&EMULATOR),
shown: SyncCell::new(None),
collision: SyncCell::new(false),
}
}
pub fn image_hide(self) -> Result<()> {
if let Some(area) = SHOWN.replace(None) { self.image_erase(area) } else { Ok(()) }
pub fn image_hide(&self) -> Result<()> {
if let Some(area) = self.shown.replace(None) { self.driver.image_erase(area) } else { Ok(()) }
}
pub fn image_erase(self, area: Rect) -> Result<()> {
match self {
Self::Kgp => drivers::Kgp::image_erase(area),
Self::KgpOld => drivers::KgpOld::image_erase(area),
Self::Iip => drivers::Iip::image_erase(area),
Self::Sixel => drivers::Sixel::image_erase(area),
Self::X11 | Self::Wayland => drivers::Ueberzug::image_erase(area),
Self::Chafa => drivers::Chafa::image_erase(area),
}
}
#[inline]
pub fn shown_load(self) -> Option<Rect> { SHOWN.get() }
#[inline]
pub(super) fn shown_store(area: Rect) { SHOWN.set(Some(area)); }
pub(super) fn start(self) { drivers::Ueberzug::start(self); }
#[inline]
pub(super) fn needs_ueberzug(self) -> bool {
!matches!(self, Self::Kgp | Self::KgpOld | Self::Iip | Self::Sixel)
}
pub(super) fn shown_store(&self, area: Rect) { self.shown.set(Some(area)); }
}
impl Adapter {
pub fn matches(emulator: &Emulator) -> Self {
let mut adapters: Adapters = emulator.into();
if env_exists("ZELLIJ_SESSION_NAME") {
adapters.retain(|p| *p == Self::Sixel);
} else if TMUX.get() {
adapters.retain(|p| *p != Self::KgpOld);
}
if let Some(p) = adapters.first() {
return *p;
}
inventory::submit! {
ClearInventory {
clear: |area| {
let overlap = area.intersection(ADAPTOR.shown.get()?);
if overlap.area() == 0 {
return None;
}
let supported_compositor = drivers::Ueberzug::supported_compositor();
match env::var("XDG_SESSION_TYPE").unwrap_or_default().as_str() {
"x11" => return Self::X11,
"wayland" if supported_compositor => return Self::Wayland,
"wayland" if !supported_compositor => return Self::Chafa,
_ => warn!("[Adapter] Could not identify XDG_SESSION_TYPE"),
}
if env_exists("WAYLAND_DISPLAY") {
return if supported_compositor { Self::Wayland } else { Self::Chafa };
}
match env::var("DISPLAY").unwrap_or_default().as_str() {
s if !s.is_empty() && !s.contains("/org.xquartz") => return Self::X11,
_ => {}
}
warn!("[Adapter] Falling back to chafa");
Self::Chafa
ADAPTOR.driver.image_erase(overlap).ok();
ADAPTOR.collision.set(true);
Some(overlap)
},
}
}

View file

@ -1,62 +0,0 @@
use std::ops::{Deref, DerefMut};
use crate::Adapter;
pub(super) struct Adapters(Vec<Adapter>);
impl Deref for Adapters {
type Target = Vec<Adapter>;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl DerefMut for Adapters {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}
impl From<&yazi_emulator::Emulator> for Adapters {
fn from(value: &yazi_emulator::Emulator) -> Self { value.kind.either_into() }
}
impl From<yazi_emulator::Brand> for Adapters {
fn from(value: yazi_emulator::Brand) -> Self {
use yazi_emulator::Brand as B;
use crate::Adapter as A;
Self(match value {
B::Kitty => vec![A::Kgp],
B::Konsole => vec![A::KgpOld],
B::Iterm2 => vec![A::Iip, A::Sixel],
B::WezTerm => vec![A::Iip, A::Sixel],
B::Foot => vec![A::Sixel],
B::Ghostty => vec![A::Kgp],
B::Microsoft => vec![A::Sixel],
B::Warp => vec![A::Iip, A::KgpOld],
B::Rio => vec![A::Kgp],
B::BlackBox => vec![A::Sixel],
B::VSCode => vec![A::Iip, A::Sixel],
B::Tabby => vec![A::Iip, A::Sixel],
B::Hyper => vec![A::Iip, A::Sixel],
B::Mintty => vec![A::Iip],
B::Tmux => vec![],
B::VTerm => vec![],
B::Apple => vec![],
B::Urxvt => vec![],
B::Bobcat => vec![A::Iip, A::Sixel],
})
}
}
impl From<yazi_emulator::Unknown> for Adapters {
fn from(value: yazi_emulator::Unknown) -> Self {
use Adapter as A;
Self(match (value.kgp, value.sixel) {
(true, true) => vec![A::Sixel, A::KgpOld],
(true, false) => vec![A::KgpOld],
(false, true) => vec![A::Sixel],
(false, false) => vec![],
})
}
}

View file

@ -6,14 +6,14 @@ use ratatui::layout::Rect;
use tokio::process::Command;
use yazi_config::THEME;
use yazi_emulator::Emulator;
use yazi_term::sequence::{MoveTo, ResetAttrs, SetBg};
use yazi_tty::sequence::{MoveTo, ResetAttrs, SetBg};
use crate::Adapter;
use crate::ADAPTOR;
pub(crate) struct Chafa;
pub(super) struct Chafa;
impl Chafa {
pub(crate) async fn image_show(path: PathBuf, max: Rect) -> Result<Rect> {
pub(super) async fn image_show(path: PathBuf, max: Rect) -> Result<Rect> {
let child = Command::new("chafa")
.args([
"-f",
@ -58,8 +58,8 @@ impl Chafa {
height: lines.len() as u16,
};
Adapter::Chafa.image_hide()?;
Adapter::shown_store(area);
ADAPTOR.image_hide()?;
ADAPTOR.shown_store(area);
Emulator::move_lock((max.x, max.y), |w| {
for (i, line) in lines.into_iter().enumerate() {
w.write_all(line)?;
@ -69,7 +69,7 @@ impl Chafa {
})
}
pub(crate) fn image_erase(area: Rect) -> Result<()> {
pub(super) fn image_erase(area: Rect) -> Result<()> {
let s = " ".repeat(area.width as usize);
Emulator::move_lock((0, 0), |w| {
if let Some(c) = THEME.app.overall.get().bg {

View file

@ -0,0 +1,59 @@
use std::path::PathBuf;
use anyhow::Result;
use ratatui::layout::Rect;
use strum::{Display, IntoStaticStr};
use crate::drivers::{Chafa, Iip, Kgp, KgpOld, Sixel, Ueberzug};
#[derive(Clone, Copy, Debug, Display, Eq, IntoStaticStr, PartialEq)]
#[strum(serialize_all = "kebab-case")]
pub enum Driver {
Kgp,
KgpOld,
Iip,
Sixel,
// Supported by Überzug++
X11,
Wayland,
Chafa,
}
impl Driver {
pub async fn image_show<P>(self, path: P, max: Rect) -> Result<Rect>
where
P: Into<PathBuf>,
{
if max.is_empty() {
return Ok(Rect::default());
}
let path = path.into();
match self {
Self::Kgp => Kgp::image_show(path, max).await,
Self::KgpOld => KgpOld::image_show(path, max).await,
Self::Iip => Iip::image_show(path, max).await,
Self::Sixel => Sixel::image_show(path, max).await,
Self::X11 | Self::Wayland => Ueberzug::image_show(path, max).await,
Self::Chafa => Chafa::image_show(path, max).await,
}
}
pub fn image_erase(self, area: Rect) -> Result<()> {
match self {
Self::Kgp => Kgp::image_erase(area),
Self::KgpOld => KgpOld::image_erase(area),
Self::Iip => Iip::image_erase(area),
Self::Sixel => Sixel::image_erase(area),
Self::X11 | Self::Wayland => Ueberzug::image_erase(area),
Self::Chafa => Chafa::image_erase(area),
}
}
pub(crate) fn start(self) { Ueberzug::start(self); }
pub(crate) fn needs_ueberzug(self) -> bool {
!matches!(self, Self::Kgp | Self::KgpOld | Self::Iip | Self::Sixel)
}
}

View file

@ -0,0 +1,94 @@
use std::{env, ops::{Deref, DerefMut}};
use tracing::warn;
use yazi_emulator::{Emulator, TMUX};
use yazi_shared::env_exists;
use crate::drivers::{Driver as D, Ueberzug};
pub(crate) struct Drivers(Vec<D>);
impl Deref for Drivers {
type Target = Vec<D>;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl DerefMut for Drivers {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}
impl From<&yazi_emulator::Emulator> for Drivers {
fn from(value: &yazi_emulator::Emulator) -> Self { value.kind.either_into() }
}
impl From<yazi_emulator::Brand> for Drivers {
fn from(value: yazi_emulator::Brand) -> Self {
use yazi_emulator::Brand as B;
Self(match value {
B::Kitty => vec![D::Kgp],
B::Konsole => vec![D::KgpOld],
B::Iterm2 => vec![D::Iip, D::Sixel],
B::WezTerm => vec![D::Iip, D::Sixel],
B::Foot => vec![D::Sixel],
B::Ghostty => vec![D::Kgp],
B::Microsoft => vec![D::Sixel],
B::Warp => vec![D::Iip, D::KgpOld],
B::Rio => vec![D::Kgp],
B::BlackBox => vec![D::Sixel],
B::VSCode => vec![D::Iip, D::Sixel],
B::Tabby => vec![D::Iip, D::Sixel],
B::Hyper => vec![D::Iip, D::Sixel],
B::Mintty => vec![D::Iip],
B::Tmux => vec![],
B::VTerm => vec![],
B::Apple => vec![],
B::Urxvt => vec![],
B::Bobcat => vec![D::Iip, D::Sixel],
})
}
}
impl From<yazi_emulator::Unknown> for Drivers {
fn from(value: yazi_emulator::Unknown) -> Self {
Self(match (value.kgp, value.sixel) {
(true, true) => vec![D::Sixel, D::KgpOld],
(true, false) => vec![D::KgpOld],
(false, true) => vec![D::Sixel],
(false, false) => vec![],
})
}
}
impl Drivers {
pub fn matches(emulator: &Emulator) -> D {
let mut adapters: Self = emulator.into();
if env_exists("ZELLIJ_SESSION_NAME") {
adapters.retain(|p| *p == D::Sixel);
} else if TMUX.get() {
adapters.retain(|p| *p != D::KgpOld);
}
if let Some(p) = adapters.first() {
return *p;
}
let supported_compositor = Ueberzug::supported_compositor();
match env::var("XDG_SESSION_TYPE").unwrap_or_default().as_str() {
"x11" => return D::X11,
"wayland" if supported_compositor => return D::Wayland,
"wayland" if !supported_compositor => return D::Chafa,
_ => warn!("[Adapter] Could not identify XDG_SESSION_TYPE"),
}
if env_exists("WAYLAND_DISPLAY") {
return if supported_compositor { D::Wayland } else { D::Chafa };
}
match env::var("DISPLAY").unwrap_or_default().as_str() {
s if !s.is_empty() && !s.contains("/org.xquartz") => return D::X11,
_ => {}
}
warn!("[Adapter] Falling back to chafa");
D::Chafa
}
}

View file

@ -6,27 +6,27 @@ use image::{DynamicImage, ExtendedColorType, ImageEncoder, codecs::{jpeg::JpegEn
use ratatui::layout::Rect;
use yazi_config::{THEME, YAZI};
use yazi_emulator::{CLOSE, Emulator, START};
use yazi_term::sequence::{MoveTo, ResetAttrs, SetBg};
use yazi_tty::sequence::{MoveTo, ResetAttrs, SetBg};
use crate::{Image, adapter::Adapter};
use crate::{ADAPTOR, Image};
pub(crate) struct Iip;
pub(super) struct Iip;
impl Iip {
pub(crate) async fn image_show(path: PathBuf, max: Rect) -> Result<Rect> {
pub(super) async fn image_show(path: PathBuf, max: Rect) -> Result<Rect> {
let img = Image::downscale(path, max).await?;
let area = Image::pixel_area((img.width(), img.height()), max);
let b = Self::encode(img).await?;
Adapter::Iip.image_hide()?;
Adapter::shown_store(area);
ADAPTOR.image_hide()?;
ADAPTOR.shown_store(area);
Emulator::move_lock((max.x, max.y), |w| {
w.write_all(&b)?;
Ok(area)
})
}
pub(crate) fn image_erase(area: Rect) -> Result<()> {
pub(super) fn image_erase(area: Rect) -> Result<()> {
let s = " ".repeat(area.width as usize);
Emulator::move_lock((0, 0), |w| {
if let Some(c) = THEME.app.overall.get().bg {

View file

@ -8,9 +8,9 @@ use ratatui::{layout::Rect, style::Color};
use yazi_config::THEME;
use yazi_emulator::{CLOSE, ESCAPE, Emulator, START};
use yazi_shim::cell::SyncCell;
use yazi_term::sequence::{MoveTo, ResetAttrs, SetBg, SetFg};
use yazi_tty::sequence::{MoveTo, ResetAttrs, SetBg, SetFg};
use crate::{adapter::Adapter, image::Image};
use crate::{ADAPTOR, image::Image};
static DIACRITICS: [char; 297] = [
'\u{0305}',
@ -312,18 +312,18 @@ static DIACRITICS: [char; 297] = [
'\u{1D244}',
];
pub(crate) struct Kgp;
pub(super) struct Kgp;
impl Kgp {
pub(crate) async fn image_show(path: PathBuf, max: Rect) -> Result<Rect> {
pub(super) async fn image_show(path: PathBuf, max: Rect) -> Result<Rect> {
let img = Image::downscale(path, max).await?;
let area = Image::pixel_area((img.width(), img.height()), max);
let b1 = Self::encode(img).await?;
let b2 = Self::place(&area)?;
Adapter::Kgp.image_hide()?;
Adapter::shown_store(area);
ADAPTOR.image_hide()?;
ADAPTOR.shown_store(area);
Emulator::move_lock((area.x, area.y), |w| {
w.write_all(&b1)?;
w.write_all(&b2)?;
@ -331,7 +331,7 @@ impl Kgp {
})
}
pub(crate) fn image_erase(area: Rect) -> Result<()> {
pub(super) fn image_erase(area: Rect) -> Result<()> {
let s = " ".repeat(area.width as usize);
Emulator::move_lock((0, 0), |w| {
if let Some(c) = THEME.app.overall.get().bg {

View file

@ -8,25 +8,25 @@ use ratatui::layout::Rect;
use yazi_emulator::{CLOSE, ESCAPE, Emulator, START};
use yazi_tty::TTY;
use crate::{Image, adapter::Adapter};
use crate::{ADAPTOR, Image};
pub(crate) struct KgpOld;
pub(super) struct KgpOld;
impl KgpOld {
pub(crate) async fn image_show(path: PathBuf, max: Rect) -> Result<Rect> {
pub(super) async fn image_show(path: PathBuf, max: Rect) -> Result<Rect> {
let img = Image::downscale(path, max).await?;
let area = Image::pixel_area((img.width(), img.height()), max);
let b = Self::encode(img).await?;
Adapter::KgpOld.image_hide()?;
Adapter::shown_store(area);
ADAPTOR.image_hide()?;
ADAPTOR.shown_store(area);
Emulator::move_lock((area.x, area.y), |w| {
w.write_all(&b)?;
Ok(area)
})
}
pub(crate) fn image_erase(_: Rect) -> Result<()> {
pub(super) fn image_erase(_: Rect) -> Result<()> {
let mut w = TTY.lockout();
write!(w, "{START}_Gq=2,a=d,d=A{ESCAPE}\\{CLOSE}")?;
w.flush()?;

View file

@ -1 +1 @@
yazi_macro::mod_flat!(chafa iip kgp kgp_old sixel ueberzug);
yazi_macro::mod_flat!(chafa driver drivers iip kgp kgp_old sixel ueberzug);

View file

@ -7,11 +7,11 @@ use quantette::{PaletteSize, color_map::IndexedColorMap, wu::{BinnerU8x3, WuU8x3
use ratatui::layout::Rect;
use yazi_config::THEME;
use yazi_emulator::{CLOSE, ESCAPE, Emulator, START};
use yazi_term::sequence::{MoveTo, ResetAttrs, SetBg};
use yazi_tty::sequence::{MoveTo, ResetAttrs, SetBg};
use crate::{Image, adapter::Adapter};
use crate::{ADAPTOR, Image};
pub(crate) struct Sixel;
pub(super) struct Sixel;
struct QuantizeOutput<T> {
indices: Vec<u8>,
@ -19,20 +19,20 @@ struct QuantizeOutput<T> {
}
impl Sixel {
pub(crate) async fn image_show(path: PathBuf, max: Rect) -> Result<Rect> {
pub(super) async fn image_show(path: PathBuf, max: Rect) -> Result<Rect> {
let img = Image::downscale(path, max).await?;
let area = Image::pixel_area((img.width(), img.height()), max);
let b = Self::encode(img).await?;
Adapter::Sixel.image_hide()?;
Adapter::shown_store(area);
ADAPTOR.image_hide()?;
ADAPTOR.shown_store(area);
Emulator::move_lock((area.x, area.y), |w| {
w.write_all(&b)?;
Ok(area)
})
}
pub(crate) fn image_erase(area: Rect) -> Result<()> {
pub(super) fn image_erase(area: Rect) -> Result<()> {
let s = " ".repeat(area.width as usize);
Emulator::move_lock((0, 0), |w| {
if let Some(c) = THEME.app.overall.get().bg {

View file

@ -10,21 +10,21 @@ use yazi_emulator::Dimension;
use yazi_shared::{LOG_LEVEL, env_exists};
use yazi_shim::{cell::RoCell, strum::IntoStr};
use crate::Adapter;
use crate::{ADAPTOR, drivers::Driver};
type Cmd = Option<(PathBuf, Rect)>;
static DEMON: RoCell<Option<UnboundedSender<Cmd>>> = RoCell::new();
pub(crate) struct Ueberzug;
pub(super) struct Ueberzug;
impl Ueberzug {
pub(crate) fn start(adapter: Adapter) {
if !adapter.needs_ueberzug() {
pub(super) fn start(driver: Driver) {
if !driver.needs_ueberzug() {
return DEMON.init(None);
}
let mut child = Self::create_demon(adapter).ok();
let mut child = Self::create_demon(driver).ok();
let (tx, mut rx) = mpsc::unbounded_channel();
tokio::spawn(async move {
@ -34,17 +34,17 @@ impl Ueberzug {
child = None;
}
if child.is_none() {
child = Self::create_demon(adapter).ok();
child = Self::create_demon(driver).ok();
}
if let Some(c) = &mut child {
Self::send_command(adapter, c, cmd).await.ok();
Self::send_command(driver, c, cmd).await.ok();
}
}
});
DEMON.init(Some(tx))
}
pub(crate) async fn image_show(path: PathBuf, max: Rect) -> Result<Rect> {
pub(super) async fn image_show(path: PathBuf, max: Rect) -> Result<Rect> {
let Some(tx) = &*DEMON else {
bail!("uninitialized ueberzugpp");
};
@ -64,11 +64,11 @@ impl Ueberzug {
.unwrap_or(max);
tx.send(Some((path, area)))?;
Adapter::shown_store(area);
ADAPTOR.shown_store(area);
Ok(area)
}
pub(crate) fn image_erase(_: Rect) -> Result<()> {
pub(super) fn image_erase(_: Rect) -> Result<()> {
if let Some(tx) = &*DEMON {
Ok(tx.send(None)?)
} else {
@ -79,16 +79,16 @@ impl Ueberzug {
// Currently Überzug++'s Wayland output only supports Niri, Sway, Hyprland and
// Wayfire as it requires information from specific compositor socket directly.
// These environment variables are from ueberzugpp src/canvas/wayland/config.cpp
pub(crate) fn supported_compositor() -> bool {
pub(super) fn supported_compositor() -> bool {
env_exists("NIRI_SOCKET")
|| env_exists("SWAYSOCK")
|| env_exists("HYPRLAND_INSTANCE_SIGNATURE")
|| env_exists("WAYFIRE_SOCKET")
}
fn create_demon(adapter: Adapter) -> Result<Child> {
fn create_demon(driver: Driver) -> Result<Child> {
let result = Command::new("ueberzugpp")
.args(["layer", "-so", adapter.into_str()])
.args(["layer", "-so", driver.into_str()])
.env("SPDLOG_LEVEL", if LOG_LEVEL.get().is_none() { "" } else { "debug" })
.kill_on_drop(true)
.stdin(Stdio::piped())
@ -113,7 +113,7 @@ impl Ueberzug {
rect
}
async fn send_command(adapter: Adapter, child: &mut Child, cmd: Cmd) -> Result<()> {
async fn send_command(driver: Driver, child: &mut Child, cmd: Cmd) -> Result<()> {
let s = if let Some((path, rect)) = cmd {
debug!("ueberzugpp rect before adjustment: {:?}", rect);
let rect = Self::adjust_rect(rect);
@ -132,7 +132,7 @@ impl Ueberzug {
format!(r#"{{"action":"remove","identifier":"yazi"}}{}"#, '\n')
};
debug!("`ueberzugpp layer -so {adapter}` command: {s}");
debug!("`ueberzugpp layer -so {driver}` command: {s}");
child.stdin.as_mut().unwrap().write_all(s.as_bytes()).await?;
Ok(())

View file

@ -3,6 +3,7 @@ use image::{ColorType, DynamicImage, GrayAlphaImage, GrayImage, ImageDecoder, Rg
use moxcms::{CicpColorPrimaries, ColorProfile, DataColorSpace, Layout, TransferCharacteristics, TransformOptions};
pub(super) struct Icc;
impl Icc {
pub(super) fn transform(mut decoder: impl ImageDecoder) -> anyhow::Result<DynamicImage> {
if let Some(layout) = Self::color_type_to_layout(decoder.color_type())

View file

@ -1,43 +0,0 @@
use std::path::PathBuf;
use image::{ImageDecoder, ImageError};
pub type ImageFormat = image::ImageFormat;
pub type ImageColor = image::ColorType;
pub type ImageOrientation = image::metadata::Orientation;
#[derive(Clone, Copy)]
pub struct ImageInfo {
pub format: ImageFormat,
pub width: u32,
pub height: u32,
pub color: ImageColor,
pub orientation: Option<ImageOrientation>,
}
impl ImageInfo {
pub async fn new(path: PathBuf) -> image::ImageResult<Self> {
tokio::task::spawn_blocking(move || {
let reader = image::ImageReader::open(path)?.with_guessed_format()?;
let Some(format) = reader.format() else {
return Err(ImageError::IoError(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"unknown image format",
)));
};
let mut decoder = reader.into_decoder()?;
let (width, height) = decoder.dimensions();
Ok(Self {
format,
width,
height,
color: decoder.color_type(),
orientation: decoder.orientation().ok(),
})
})
.await
.map_err(|e| ImageError::IoError(e.into()))?
}
}

View file

@ -1,15 +1,12 @@
yazi_macro::mod_pub!(drivers);
yazi_macro::mod_flat!(adapter adapters icc image info);
yazi_macro::mod_flat!(adapter icc image);
use yazi_emulator::{Brand, CLOSE, EMULATOR, ESCAPE, Emulator, Mux, START, TMUX};
use yazi_shared::in_wsl;
use yazi_shim::cell::SyncCell;
use yazi_shim::cell::{RoCell, SyncCell};
pub static ADAPTOR: SyncCell<Adapter> = SyncCell::new(Adapter::Chafa);
// Image state
static SHOWN: SyncCell<Option<ratatui::layout::Rect>> = SyncCell::new(None);
pub static ADAPTOR: RoCell<Adapter> = RoCell::new();
// WSL support
pub static WSL: SyncCell<bool> = SyncCell::new(false);
@ -34,7 +31,7 @@ pub fn init() -> anyhow::Result<()> {
EMULATOR.init(emulator);
yazi_config::init_flavor(EMULATOR.light)?;
ADAPTOR.set(Adapter::matches(&EMULATOR));
ADAPTOR.get().start();
ADAPTOR.init(Adapter::new());
ADAPTOR.start();
Ok(())
}

View file

@ -17,33 +17,24 @@ default = [ "vendored-lua" ]
vendored-lua = [ "mlua/vendored" ]
[dependencies]
yazi-adapter = { path = "../yazi-adapter", version = "26.5.6" }
yazi-codegen = { path = "../yazi-codegen", version = "26.5.6" }
yazi-config = { path = "../yazi-config", version = "26.5.6" }
yazi-fs = { path = "../yazi-fs", version = "26.5.6" }
yazi-macro = { path = "../yazi-macro", version = "26.5.6" }
yazi-shared = { path = "../yazi-shared", version = "26.5.6" }
yazi-shim = { path = "../yazi-shim", version = "26.5.6" }
yazi-term = { path = "../yazi-term", version = "26.5.6" }
yazi-tty = { path = "../yazi-tty", version = "26.5.9" }
yazi-vfs = { path = "../yazi-vfs", version = "26.5.6" }
yazi-widgets = { path = "../yazi-widgets", version = "26.5.6" }
# External dependencies
ansi-to-tui = { workspace = true }
anyhow = { workspace = true }
futures = { workspace = true }
hashbrown = { workspace = true }
image = { workspace = true }
inventory = { workspace = true }
mlua = { workspace = true }
ordered-float = { workspace = true }
parking_lot = { workspace = true }
paste = { workspace = true }
ratatui = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
strum = { workspace = true }
tokio = { workspace = true }
tokio-stream = { workspace = true }
tracing = { workspace = true }
unicode-width = { workspace = true }

View file

@ -1,42 +0,0 @@
use mlua::{AnyUserData, IntoLuaMulti, UserData, UserDataMethods, Value};
use yazi_fs::provider::FileBuilder;
use crate::{Error, Fd, UrlRef};
#[derive(Default)]
pub struct Access(yazi_vfs::provider::Gate);
impl UserData for Access {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_function("append", |_, (ud, append): (AnyUserData, bool)| {
ud.borrow_mut::<Self>()?.0.append(append);
Ok(ud)
});
methods.add_function("create", |_, (ud, create): (AnyUserData, bool)| {
ud.borrow_mut::<Self>()?.0.create(create);
Ok(ud)
});
methods.add_function("create_new", |_, (ud, create_new): (AnyUserData, bool)| {
ud.borrow_mut::<Self>()?.0.create_new(create_new);
Ok(ud)
});
methods.add_async_method("open", |lua, me, url: UrlRef| async move {
match me.0.open(&*url).await {
Ok(fd) => Fd(fd).into_lua_multi(&lua),
Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua),
}
});
methods.add_function("read", |_, (ud, read): (AnyUserData, bool)| {
ud.borrow_mut::<Self>()?.0.read(read);
Ok(ud)
});
methods.add_function("truncate", |_, (ud, truncate): (AnyUserData, bool)| {
ud.borrow_mut::<Self>()?.0.truncate(truncate);
Ok(ud)
});
methods.add_function("write", |_, (ud, write): (AnyUserData, bool)| {
ud.borrow_mut::<Self>()?.0.write(write);
Ok(ud)
});
}
}

View file

@ -5,10 +5,9 @@ pub type ComposerGet = fn(&Lua, &[u8]) -> mlua::Result<Value>;
pub type ComposerSet = fn(&Lua, &[u8], Value) -> mlua::Result<Value>;
pub struct Composer<G, S> {
get: G,
set: S,
parent: Option<(G, S)>,
cache: HashMap<Vec<u8>, Value>,
get: G,
set: S,
cache: HashMap<Vec<u8>, Value>,
}
impl<G, S> Composer<G, S>
@ -16,13 +15,7 @@ where
G: Fn(&Lua, &[u8]) -> mlua::Result<Value> + 'static,
S: Fn(&Lua, &[u8], Value) -> mlua::Result<Value> + 'static,
{
#[inline]
pub fn new(get: G, set: S) -> Self { Self { get, set, parent: None, cache: Default::default() } }
#[inline]
pub fn with_parent(get: G, set: S, p_get: G, p_set: S) -> Self {
Self { get, set, parent: Some((p_get, p_set)), cache: Default::default() }
}
pub fn new(get: G, set: S) -> Self { Self { get, set, cache: Default::default() } }
}
impl<G, S> UserData for Composer<G, S>
@ -37,14 +30,9 @@ where
return Ok(v.clone());
}
let mut value = (me.get)(lua, &key)?;
if value.is_nil()
&& let Some((p_get, _)) = &me.parent
{
value = p_get(lua, &key)?;
}
let value = (me.get)(lua, &key)?;
me.cache.insert(key.to_owned(), value.clone());
Ok(value)
});
@ -56,11 +44,6 @@ where
if value.is_nil() {
me.cache.remove(key.as_ref());
} else if let Some((_, p_set)) = &me.parent {
match p_set(lua, key.as_ref(), value)? {
Value::Nil => me.cache.remove(key.as_ref()),
v => me.cache.insert(key.to_owned(), v),
};
} else {
me.cache.insert(key.to_owned(), value);
}

View file

@ -1,74 +0,0 @@
use std::{ops::Deref, sync::Arc};
use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value};
use yazi_config::YAZI;
use yazi_shim::mlua::UserDataFieldsExt;
use crate::{FileRef, Id, Iter};
#[derive(Clone)]
pub struct Fetcher {
inner: Arc<yazi_config::plugin::Fetcher>,
}
impl Deref for Fetcher {
type Target = yazi_config::plugin::Fetcher;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl Fetcher {
pub fn new(inner: impl Into<Arc<yazi_config::plugin::Fetcher>>) -> Self {
Self { inner: inner.into() }
}
}
impl UserData for Fetcher {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("id", |_, me| Ok(Id(me.id)));
fields.add_cached_field("name", |lua, me| lua.create_string(&*me.name));
}
}
// --- Matcher
pub struct FetcherMatcher(yazi_config::plugin::FetcherMatcher<'static>);
impl FetcherMatcher {
pub fn new(inner: impl Into<yazi_config::plugin::FetcherMatcher<'static>>) -> Self {
Self(inner.into())
}
}
impl TryFrom<Table> for FetcherMatcher {
type Error = mlua::Error;
fn try_from(value: Table) -> Result<Self, Self::Error> {
let id: Id = value.raw_get("id").unwrap_or_default();
let file: Option<FileRef> = value.raw_get("file")?;
let mime: Option<String> = value.raw_get("mime")?;
Ok(Self(yazi_config::plugin::FetcherMatcher {
fetchers: YAZI.plugin.fetchers.load_full(),
id: id.0,
file: file.map(|f| f.inner.clone().into()),
mime: mime.map(Into::into),
..Default::default()
}))
}
}
impl FromLua for FetcherMatcher {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
match value {
Value::Table(t) => t.try_into(),
_ => Err("expected a table of FetcherMatcher".into_lua_err()),
}
}
}
impl IntoLua for FetcherMatcher {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
Iter::new(self.0.map(Fetcher::new), None).into_lua(lua)
}
}

View file

@ -1,17 +0,0 @@
use mlua::{IntoLua, MetaMethod, UserData, UserDataMethods};
use yazi_config::YAZI;
use crate::config::FetcherMatcher;
pub struct Fetchers;
impl UserData for Fetchers {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("match", |lua, _, matcher: Option<FetcherMatcher>| match matcher {
Some(matcher) => matcher.into_lua(lua),
None => FetcherMatcher::new(&YAZI.plugin.fetchers).into_lua(lua),
});
methods.add_meta_method(MetaMethod::Len, |_, _, ()| Ok(YAZI.plugin.fetchers.load().len()));
}
}

View file

@ -1 +0,0 @@
yazi_macro::mod_flat!(fetcher fetchers open_rule open_rules opener opener_rule opener_rules preloader preloaders previewer previewers spotter spotters);

View file

@ -1,84 +0,0 @@
use std::{ops::Deref, sync::Arc};
use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value};
use yazi_config::YAZI;
use yazi_shim::mlua::UserDataFieldsExt;
use crate::{FileRef, Id, Iter};
pub struct OpenRule {
inner: Arc<yazi_config::open::OpenRule>,
}
impl Deref for OpenRule {
type Target = Arc<yazi_config::open::OpenRule>;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl From<OpenRule> for Arc<yazi_config::open::OpenRule> {
fn from(value: OpenRule) -> Self { value.inner }
}
impl OpenRule {
pub fn new(inner: impl Into<Arc<yazi_config::open::OpenRule>>) -> Self {
Self { inner: inner.into() }
}
}
impl FromLua for OpenRule {
fn from_lua(value: Value, lua: &Lua) -> mlua::Result<Self> {
Ok(Self::new(lua.from_value::<yazi_config::open::OpenRule>(value)?))
}
}
impl UserData for OpenRule {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("id", |_, me| Ok(Id(me.id)));
fields.add_cached_field("use", |lua, me| {
lua.create_sequence_from(me.r#use.iter().map(|s| s.as_str()))
});
}
}
// --- Matcher
pub struct OpenRuleMatcher(pub(super) yazi_config::open::OpenRuleMatcher<'static>);
impl OpenRuleMatcher {
pub fn new(inner: impl Into<yazi_config::open::OpenRuleMatcher<'static>>) -> Self {
Self(inner.into())
}
}
impl TryFrom<Table> for OpenRuleMatcher {
type Error = mlua::Error;
fn try_from(value: Table) -> Result<Self, Self::Error> {
let id: Id = value.raw_get("id").unwrap_or_default();
let file: Option<FileRef> = value.raw_get("file")?;
let mime: Option<String> = value.raw_get("mime")?;
Ok(Self(yazi_config::open::OpenRuleMatcher {
rules: YAZI.open.load_full(),
id: id.0,
file: file.map(|f| f.inner.clone().into()),
mime: mime.map(Into::into),
..Default::default()
}))
}
}
impl FromLua for OpenRuleMatcher {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
match value {
Value::Table(t) => t.try_into(),
_ => Err("expected a table of OpenRuleMatcher".into_lua_err()),
}
}
}
impl IntoLua for OpenRuleMatcher {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
Iter::new(self.0.map(OpenRule::new), None).into_lua(lua)
}
}

View file

@ -1,39 +0,0 @@
use mlua::{ExternalError, ExternalResult, IntoLua, MetaMethod, Table, UserData, UserDataMethods};
use yazi_config::YAZI;
use yazi_shim::mlua::DeserializeOverLua;
use crate::config::{OpenRule, OpenRuleMatcher};
pub struct OpenRules;
impl UserData for OpenRules {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("match", |lua, _, matcher: Option<OpenRuleMatcher>| match matcher {
Some(matcher) => matcher.into_lua(lua),
None => OpenRuleMatcher::new(&*YAZI.open).into_lua(lua),
});
methods.add_method("insert", |_, _, (index, rule): (isize, OpenRule)| {
let index = match index {
1.. => index - 1,
0 => return Err("index must be 1-based or negative".into_lua_err()),
_ => index,
};
YAZI.open.insert(index, rule.clone()).into_lua_err()?;
Ok(rule)
});
methods.add_method("remove", |_, _, matcher: OpenRuleMatcher| {
YAZI.open.remove(matcher.0);
Ok(())
});
methods.add_method("update", |_, _, (matcher, table): (OpenRuleMatcher, Table)| {
YAZI.open.update(matcher.0, |rule| rule.deserialize_over_lua(&table))?;
Ok(())
});
methods.add_meta_method(MetaMethod::Len, |_, _, ()| Ok(YAZI.open.load().len()));
}
}

View file

@ -1,45 +0,0 @@
use mlua::{ExternalError, FromLua, IntoLua, IntoLuaMulti, MetaMethod, UserData, UserDataMethods, Value};
use yazi_config::{YAZI, opener::OpenerRulesMatcher};
use crate::config::OpenerRules;
pub struct Opener;
impl UserData for Opener {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method(MetaMethod::Index, |lua, _, key: mlua::String| {
let key = key.to_str()?;
match YAZI.opener.load().get(&*key) {
Some(rules) => OpenerRules::new(rules.clone()).into_lua(lua),
None => Ok(Value::Nil),
}
});
methods.add_meta_method(MetaMethod::NewIndex, |lua, _, (key, value): (mlua::String, Value)| {
let key = key.to_str()?;
match value {
t @ Value::Table(_) => {
YAZI.opener.insert(&key, &*OpenerRules::from_lua(t, lua)?);
}
Value::Nil => {
YAZI.opener.remove(&key);
}
_ => return Err("expected a table or nil".into_lua_err()),
}
Ok(())
});
methods.add_meta_method(MetaMethod::Pairs, |lua, _, ()| {
let mut matcher = OpenerRulesMatcher::from(&YAZI.opener);
let iter = lua.create_function_mut(move |lua, ()| {
if let Some((name, rules)) = matcher.next() {
(name, OpenerRules::new(rules)).into_lua_multi(lua)
} else {
().into_lua_multi(lua)
}
})?;
Ok(iter)
});
}
}

View file

@ -1,79 +0,0 @@
use std::{ops::Deref, sync::Arc};
use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value};
use yazi_shim::mlua::UserDataFieldsExt;
use crate::{Id, Iter};
pub struct OpenerRule {
inner: Arc<yazi_config::opener::OpenerRule>,
}
impl Deref for OpenerRule {
type Target = Arc<yazi_config::opener::OpenerRule>;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl From<OpenerRule> for Arc<yazi_config::opener::OpenerRule> {
fn from(value: OpenerRule) -> Self { value.inner }
}
impl OpenerRule {
pub fn new(inner: impl Into<Arc<yazi_config::opener::OpenerRule>>) -> Self {
Self { inner: inner.into() }
}
}
impl FromLua for OpenerRule {
fn from_lua(value: Value, lua: &Lua) -> mlua::Result<Self> {
let mut inner: yazi_config::opener::OpenerRule = lua.from_value(value)?;
inner.fill();
Ok(Self::new(inner))
}
}
impl UserData for OpenerRule {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("id", |_, me| Ok(Id(me.id)));
fields.add_cached_field("run", |lua, me| lua.create_string(&*me.run));
fields.add_field_method_get("block", |_, me| Ok(me.block));
fields.add_field_method_get("orphan", |_, me| Ok(me.orphan));
fields.add_cached_field("desc", |lua, me| lua.create_string(&*me.desc));
}
}
// --- Matcher
pub struct OpenerRuleMatcher(pub(super) yazi_config::opener::OpenerRuleMatcher);
impl OpenerRuleMatcher {
pub fn new(inner: impl Into<yazi_config::opener::OpenerRuleMatcher>) -> Self {
Self(inner.into())
}
}
impl TryFrom<Table> for OpenerRuleMatcher {
type Error = mlua::Error;
fn try_from(t: Table) -> Result<Self, Self::Error> {
let id: Id = t.raw_get("id").unwrap_or_default();
Ok(Self(yazi_config::opener::OpenerRuleMatcher { id: id.0, ..Default::default() }))
}
}
impl FromLua for OpenerRuleMatcher {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
match value {
Value::Table(t) => t.try_into(),
_ => Err("expected a table of OpenerRuleMatcher".into_lua_err()),
}
}
}
impl IntoLua for OpenerRuleMatcher {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
Iter::new(self.0.map(OpenerRule::new), None).into_lua(lua)
}
}

View file

@ -1,57 +0,0 @@
use std::{ops::Deref, sync::Arc};
use mlua::{ExternalError, ExternalResult, FromLua, IntoLua, Lua, LuaSerdeExt, MetaMethod, UserData, UserDataMethods, Value};
use yazi_shim::toml::DeserializeOverHook;
use crate::config::{OpenerRule, OpenerRuleMatcher};
pub struct OpenerRules {
inner: Arc<yazi_config::opener::OpenerRules>,
}
impl Deref for OpenerRules {
type Target = Arc<yazi_config::opener::OpenerRules>;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl OpenerRules {
pub fn new(inner: impl Into<Arc<yazi_config::opener::OpenerRules>>) -> Self {
Self { inner: inner.into() }
}
}
impl FromLua for OpenerRules {
fn from_lua(value: Value, lua: &Lua) -> mlua::Result<Self> {
let inner: yazi_config::opener::OpenerRules = lua.from_value(value)?;
Ok(Self::new(inner.deserialize_over_hook().into_lua_err()?))
}
}
impl UserData for OpenerRules {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("match", |lua, me, matcher: Option<OpenerRuleMatcher>| match matcher {
Some(matcher) => matcher.into_lua(lua),
None => OpenerRuleMatcher::new(&*me.inner).into_lua(lua),
});
methods.add_method("insert", |_, me, (index, rule): (isize, OpenerRule)| {
let index = match index {
1.. => index - 1,
0 => return Err("index must be 1-based or negative".into_lua_err()),
_ => index,
};
me.inner.insert(index, rule.clone()).into_lua_err()?;
Ok(rule)
});
methods.add_method("remove", |_, me, matcher: OpenerRuleMatcher| {
me.inner.remove(matcher.0);
Ok(())
});
methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.inner.load().len()));
}
}

View file

@ -1,74 +0,0 @@
use std::{ops::Deref, sync::Arc};
use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value};
use yazi_config::YAZI;
use yazi_shim::mlua::UserDataFieldsExt;
use crate::{FileRef, Id, Iter};
#[derive(Clone)]
pub struct Preloader {
inner: Arc<yazi_config::plugin::Preloader>,
}
impl Deref for Preloader {
type Target = yazi_config::plugin::Preloader;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl Preloader {
pub fn new(inner: impl Into<Arc<yazi_config::plugin::Preloader>>) -> Self {
Self { inner: inner.into() }
}
}
impl UserData for Preloader {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("id", |_, me| Ok(Id(me.id)));
fields.add_cached_field("name", |lua, me| lua.create_string(&*me.name));
}
}
// --- Matcher
pub struct PreloaderMatcher(pub(super) yazi_config::plugin::PreloaderMatcher<'static>);
impl PreloaderMatcher {
pub fn new(inner: impl Into<yazi_config::plugin::PreloaderMatcher<'static>>) -> Self {
Self(inner.into())
}
}
impl TryFrom<Table> for PreloaderMatcher {
type Error = mlua::Error;
fn try_from(value: Table) -> Result<Self, Self::Error> {
let id: Id = value.raw_get("id").unwrap_or_default();
let file: Option<FileRef> = value.raw_get("file")?;
let mime: Option<String> = value.raw_get("mime")?;
Ok(Self(yazi_config::plugin::PreloaderMatcher {
preloaders: YAZI.plugin.preloaders.load_full(),
id: id.0,
file: file.map(|f| f.inner.clone().into()),
mime: mime.map(Into::into),
..Default::default()
}))
}
}
impl FromLua for PreloaderMatcher {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
match value {
Value::Table(t) => t.try_into(),
_ => Err("expected a table of PreloaderMatcher".into_lua_err()),
}
}
}
impl IntoLua for PreloaderMatcher {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
Iter::new(self.0.map(Preloader::new), None).into_lua(lua)
}
}

View file

@ -1,17 +0,0 @@
use mlua::{IntoLua, MetaMethod, UserData, UserDataMethods};
use yazi_config::YAZI;
use crate::config::PreloaderMatcher;
pub struct Preloaders;
impl UserData for Preloaders {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("match", |lua, _, matcher: Option<PreloaderMatcher>| match matcher {
Some(matcher) => matcher.into_lua(lua),
None => PreloaderMatcher::new(&YAZI.plugin.preloaders).into_lua(lua),
});
methods.add_meta_method(MetaMethod::Len, |_, _, ()| Ok(YAZI.plugin.preloaders.load().len()));
}
}

View file

@ -1,84 +0,0 @@
use std::{ops::Deref, sync::Arc};
use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value};
use yazi_config::YAZI;
use yazi_shim::mlua::UserDataFieldsExt;
use crate::{FileRef, Id, Iter};
#[derive(Clone)]
pub struct Previewer {
inner: Arc<yazi_config::plugin::Previewer>,
}
impl Deref for Previewer {
type Target = yazi_config::plugin::Previewer;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl From<Previewer> for Arc<yazi_config::plugin::Previewer> {
fn from(value: Previewer) -> Self { value.inner }
}
impl Previewer {
pub fn new(inner: impl Into<Arc<yazi_config::plugin::Previewer>>) -> Self {
Self { inner: inner.into() }
}
}
impl FromLua for Previewer {
fn from_lua(value: Value, lua: &Lua) -> mlua::Result<Self> {
Ok(Self::new(lua.from_value::<yazi_config::plugin::Previewer>(value)?))
}
}
impl UserData for Previewer {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("id", |_, me| Ok(Id(me.id)));
fields.add_cached_field("name", |lua, me| lua.create_string(&*me.name));
}
}
// --- Matcher
pub struct PreviewerMatcher(pub(super) yazi_config::plugin::PreviewerMatcher<'static>);
impl PreviewerMatcher {
pub fn new(inner: impl Into<yazi_config::plugin::PreviewerMatcher<'static>>) -> Self {
Self(inner.into())
}
}
impl TryFrom<Table> for PreviewerMatcher {
type Error = mlua::Error;
fn try_from(value: Table) -> Result<Self, Self::Error> {
let id: Id = value.raw_get("id").unwrap_or_default();
let file: Option<FileRef> = value.raw_get("file")?;
let mime: Option<String> = value.raw_get("mime")?;
Ok(Self(yazi_config::plugin::PreviewerMatcher {
previewers: YAZI.plugin.previewers.load_full(),
id: id.0,
file: file.map(|f| f.inner.clone().into()),
mime: mime.map(Into::into),
..Default::default()
}))
}
}
impl FromLua for PreviewerMatcher {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
match value {
Value::Table(t) => t.try_into(),
_ => Err("expected a table of PreviewerMatcher".into_lua_err()),
}
}
}
impl IntoLua for PreviewerMatcher {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
Iter::new(self.0.map(Previewer::new), None).into_lua(lua)
}
}

View file

@ -1,33 +0,0 @@
use mlua::{ExternalError, ExternalResult, IntoLua, MetaMethod, UserData, UserDataMethods};
use yazi_config::YAZI;
use crate::config::{Previewer, PreviewerMatcher};
pub struct Previewers;
impl UserData for Previewers {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("match", |lua, _, matcher: Option<PreviewerMatcher>| match matcher {
Some(matcher) => matcher.into_lua(lua),
None => PreviewerMatcher::new(&YAZI.plugin.previewers).into_lua(lua),
});
methods.add_method("insert", |_, _, (index, previewer): (isize, Previewer)| {
let index = match index {
1.. => index - 1,
0 => return Err("index must be 1-based or negative".into_lua_err()),
_ => index,
};
YAZI.plugin.previewers.insert(index, previewer.clone().into()).into_lua_err()?;
Ok(previewer)
});
methods.add_method("remove", |_, _, matcher: PreviewerMatcher| {
YAZI.plugin.previewers.remove(matcher.0);
Ok(())
});
methods.add_meta_method(MetaMethod::Len, |_, _, ()| Ok(YAZI.plugin.previewers.load().len()));
}
}

View file

@ -1,74 +0,0 @@
use std::{ops::Deref, sync::Arc};
use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value};
use yazi_config::YAZI;
use yazi_shim::mlua::UserDataFieldsExt;
use crate::{FileRef, Id, Iter};
#[derive(Clone)]
pub struct Spotter {
inner: Arc<yazi_config::plugin::Spotter>,
}
impl Deref for Spotter {
type Target = yazi_config::plugin::Spotter;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl Spotter {
pub fn new(inner: impl Into<Arc<yazi_config::plugin::Spotter>>) -> Self {
Self { inner: inner.into() }
}
}
impl UserData for Spotter {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("id", |_, me| Ok(Id(me.id)));
fields.add_cached_field("name", |lua, me| lua.create_string(&*me.name));
}
}
// --- Matcher
pub struct SpotterMatcher(pub(super) yazi_config::plugin::SpotterMatcher<'static>);
impl SpotterMatcher {
pub fn new(inner: impl Into<yazi_config::plugin::SpotterMatcher<'static>>) -> Self {
Self(inner.into())
}
}
impl TryFrom<Table> for SpotterMatcher {
type Error = mlua::Error;
fn try_from(value: Table) -> Result<Self, Self::Error> {
let id: Id = value.raw_get("id").unwrap_or_default();
let file: Option<FileRef> = value.raw_get("file")?;
let mime: Option<String> = value.raw_get("mime")?;
Ok(Self(yazi_config::plugin::SpotterMatcher {
spotters: YAZI.plugin.spotters.load_full(),
id: id.0,
file: file.map(|f| f.inner.clone().into()),
mime: mime.map(Into::into),
..Default::default()
}))
}
}
impl FromLua for SpotterMatcher {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
match value {
Value::Table(t) => t.try_into(),
_ => Err("expected a table of SpotterMatcher".into_lua_err()),
}
}
}
impl IntoLua for SpotterMatcher {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
Iter::new(self.0.map(Spotter::new), None).into_lua(lua)
}
}

View file

@ -1,17 +0,0 @@
use mlua::{IntoLua, MetaMethod, UserData, UserDataMethods};
use yazi_config::YAZI;
use crate::config::SpotterMatcher;
pub struct Spotters;
impl UserData for Spotters {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("match", |lua, _, matcher: Option<SpotterMatcher>| match matcher {
Some(matcher) => matcher.into_lua(lua),
None => SpotterMatcher::new(&YAZI.plugin.spotters).into_lua(lua),
});
methods.add_meta_method(MetaMethod::Len, |_, _, ()| Ok(YAZI.plugin.spotters.load().len()));
}
}

View file

@ -1,48 +0,0 @@
use std::{mem, ops::Deref};
use mlua::{IntoLua, UserData, UserDataFields, Value};
use yazi_shim::{mlua::UserDataFieldsExt, strum::IntoStr};
use yazi_term::event::{DndDropArrive, DndEvent as Inner};
pub struct DndEvent {
inner: Inner,
}
impl Deref for DndEvent {
type Target = Inner;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl From<Inner> for DndEvent {
fn from(inner: Inner) -> Self { Self { inner } }
}
impl UserData for DndEvent {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("type", |_, me| Ok(me.inner.r#type()));
fields.add_field_method_get("x", |_, me| Ok(me.inner.x()));
fields.add_field_method_get("y", |_, me| Ok(me.inner.y()));
fields.add_field_method_get("idx", |_, me| Ok(me.inner.idx()));
fields.add_field_method_get("op", |_, me| Ok(me.inner.op().map(IntoStr::into_str)));
fields.add_cached_field("mimes", |lua, me| {
if let Some(mimes) = me.inner.mimes() {
lua.create_sequence_from(mimes.iter())?.into_lua(lua)
} else {
Ok(Value::Nil)
}
});
fields.add_cached_field_mut("data", |lua, me| match &mut me.inner {
Inner::DropArrive(DndDropArrive { data, .. }) => {
lua.create_external_string(mem::take(data))?.into_lua(lua)
}
_ => Ok(Value::Nil),
});
}
}

View file

@ -3,6 +3,7 @@ use std::fmt::Debug;
use mlua::{AnyUserData, ExternalError, FromLua, IntoLua, Lua, Value};
use super::{Pos, Rect};
use crate::position::Position;
const EXPECTED: &str = "expected a Pos or Rect";
@ -45,7 +46,7 @@ impl Area {
pub fn transform(
self,
f: impl FnOnce(yazi_config::popup::Position) -> ratatui::layout::Rect,
f: impl FnOnce(Position) -> ratatui::layout::Rect,
) -> ratatui::layout::Rect {
match self {
Self::Rect(rect) => *rect,
@ -62,8 +63,8 @@ impl From<ratatui::layout::Rect> for Area {
fn from(rect: ratatui::layout::Rect) -> Self { Self::Rect(rect.into()) }
}
impl From<yazi_config::popup::Position> for Area {
fn from(pos: yazi_config::popup::Position) -> Self { Self::Pos(pos.into()) }
impl From<Position> for Area {
fn from(value: Position) -> Self { Self::Pos(value.into()) }
}
impl TryFrom<&AnyUserData> for Area {

View file

@ -36,15 +36,6 @@ impl Spatial for Bar {
fn set_area(&mut self, area: Area) { self.area = area; }
}
impl Widget for Bar {
fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer)
where
Self: Sized,
{
(&self).render(rect, buf);
}
}
impl Widget for &Bar {
fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer)
where

View file

@ -1,38 +0,0 @@
use mlua::{IntoLua, Lua, Value};
use crate::{Composer, ComposerGet, ComposerSet};
pub fn compose(p_get: ComposerGet, p_set: ComposerSet) -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
match key {
b"Align" => super::Align::compose(lua)?,
b"Bar" => super::Bar::compose(lua)?,
b"Border" => super::Border::compose(lua)?,
b"Clear" => super::Clear::compose(lua)?,
b"Color" => super::Color::compose(lua)?,
b"Constraint" => super::Constraint::compose(lua)?,
b"Edge" => super::Edge::compose(lua)?,
b"Fill" => super::Fill::compose(lua)?,
b"Gauge" => super::Gauge::compose(lua)?,
b"Input" => super::Input::compose(lua)?,
b"Layout" => super::Layout::compose(lua)?,
b"Line" => super::Line::compose(lua)?,
b"List" => super::List::compose(lua)?,
b"Pad" => super::Pad::compose(lua)?,
b"Pos" => super::Pos::compose(lua)?,
b"Rect" => super::Rect::compose(lua)?,
b"Row" => super::Row::compose(lua)?,
b"Span" => super::Span::compose(lua)?,
b"Style" => crate::Style::compose(lua)?,
b"Table" => super::Table::compose(lua)?,
b"Text" => super::Text::compose(lua)?,
b"Wrap" => super::Wrap::compose(lua)?,
_ => return Ok(Value::Nil),
}
.into_lua(lua)
}
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::with_parent(get, set, p_get, p_set)
}

View file

@ -35,15 +35,6 @@ impl Spatial for Fill {
fn set_area(&mut self, area: Area) { self.area = area; }
}
impl Widget for Fill {
fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer)
where
Self: Sized,
{
(&self).render(rect, buf);
}
}
impl Widget for &Fill {
fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer)
where

View file

@ -2,7 +2,7 @@ use mlua::{AnyUserData, ExternalError, IntoLua, Lua, MetaMethod, Table, UserData
use ratatui::widgets::Widget;
use super::{Area, Span};
use crate::{Style, elements::Spatial};
use crate::{elements::Spatial, style::Style};
#[derive(Clone, Debug, Default)]
pub struct Gauge {

View file

@ -1,117 +0,0 @@
use std::sync::Arc;
use mlua::{AnyUserData, ExternalError, FromLua, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value};
use parking_lot::Mutex;
use ratatui::widgets::Widget;
use super::Area;
use crate::{elements::Spatial, impl_area_method};
const EXPECTED: &str = "expected a table or a Input";
#[derive(Clone, Debug, Default)]
pub struct Input {
pub focus: bool,
inner: Arc<Mutex<yazi_widgets::input::Input>>,
}
impl Input {
pub fn compose(lua: &Lua) -> mlua::Result<Value> {
let new = lua.create_function(|_, (_, input): (Table, Self)| Ok(input))?;
let input = lua.create_table()?;
input.set_metatable(Some(lua.create_table_from([(MetaMethod::Call.name(), new)])?))?;
input.into_lua(lua)
}
}
impl From<&Input> for Arc<Mutex<yazi_widgets::input::Input>> {
fn from(input: &Input) -> Self { input.inner.clone() }
}
impl TryFrom<&AnyUserData> for Input {
type Error = mlua::Error;
fn try_from(value: &AnyUserData) -> Result<Self, Self::Error> {
Ok(value.borrow::<Self>()?.clone())
}
}
impl TryFrom<Table> for Input {
type Error = mlua::Error;
fn try_from(tb: Table) -> Result<Self, Self::Error> {
let input = yazi_widgets::input::Input::new(yazi_widgets::input::InputOpt {
cfg: yazi_config::popup::InputCfg {
title: String::new(),
value: tb.raw_get("value")?,
cursor: None,
obscure: false,
position: Default::default(),
realtime: false,
completion: false,
},
cb: None,
})?;
Ok(Self { inner: Arc::new(Mutex::new(input)), ..Default::default() })
}
}
impl Spatial for Input {
fn area(&self) -> Area { self.inner.lock().pos.into() }
fn set_area(&mut self, area: Area) {
self.inner.lock().repos(match area {
Area::Rect(rect) => yazi_config::popup::Position {
origin: yazi_config::popup::Origin::TopLeft,
offset: yazi_config::popup::Offset {
x: rect.x as i16,
y: rect.y as i16,
width: rect.width,
height: rect.height,
},
},
Area::Pos(pos) => pos.into(),
});
}
}
impl Widget for Input {
fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer)
where
Self: Sized,
{
(&self).render(rect, buf);
}
}
impl Widget for &Input {
fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer)
where
Self: Sized,
{
self.inner.lock().render(rect, buf)
}
}
impl FromLua for Input {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
match value {
Value::Table(tb) => Self::try_from(tb),
_ => Err(EXPECTED.into_lua_err()),
}
}
}
impl UserData for Input {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
impl_area_method!(methods);
methods.add_function("focus", |lua, (ud, focus): (AnyUserData, bool)| {
ud.borrow_mut::<Self>()?.focus = focus;
ud.into_lua(lua)
});
}
}

View file

@ -106,15 +106,6 @@ impl Spatial for Line {
fn set_area(&mut self, area: Area) { self.area = area; }
}
impl Widget for Line {
fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer)
where
Self: Sized,
{
(&self).render(rect, buf);
}
}
impl Widget for &Line {
fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer)
where

View file

@ -37,15 +37,6 @@ impl Spatial for List {
fn set_area(&mut self, area: Area) { self.area = area; }
}
impl Widget for List {
fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer)
where
Self: Sized,
{
(&self).render(rect, buf);
}
}
impl Widget for &List {
fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer)
where

View file

@ -1 +1 @@
yazi_macro::mod_flat!(align area bar border cell clear color constraint edge elements fill gauge input layout line list pad pos rect renderable renderables row span spatial table text wrap);
yazi_macro::mod_flat!(align area bar border cell color constraint edge fill gauge layout line list pad pos rect row span spatial table text wrap);

View file

@ -4,29 +4,28 @@ use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, IntoLua, Lua, Me
use yazi_shim::strum::IntoStr;
use super::Pad;
use crate::position::{Offset, Origin, Position};
const EXPECTED: &str = "expected a Pos";
#[derive(Clone, Copy, Default)]
pub struct Pos {
inner: yazi_config::popup::Position,
inner: Position,
pub(super) pad: Pad,
}
impl Deref for Pos {
type Target = yazi_config::popup::Position;
type Target = Position;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl From<yazi_config::popup::Position> for Pos {
fn from(value: yazi_config::popup::Position) -> Self {
Self { inner: value, ..Default::default() }
}
impl From<Position> for Pos {
fn from(value: Position) -> Self { Self { inner: value, ..Default::default() } }
}
impl From<Pos> for yazi_config::popup::Position {
impl From<Pos> for Position {
fn from(value: Pos) -> Self { value.inner }
}
@ -42,8 +41,6 @@ impl TryFrom<Table> for Pos {
type Error = mlua::Error;
fn try_from(t: Table) -> Result<Self, Self::Error> {
use yazi_config::popup::{Offset, Origin, Position};
Ok(Self::from(Position {
origin: Origin::from_str(&t.raw_get::<mlua::String>(1)?.to_str()?).into_lua_err()?,
offset: Offset {

View file

@ -2,7 +2,7 @@ use mlua::{AnyUserData, IntoLua, Lua, MetaMethod, UserData, UserDataMethods, Val
use ratatui::widgets::{StatefulWidget, Widget};
use super::{Area, Row};
use crate::{Style, elements::{Constraint, Spatial}};
use crate::{elements::{Constraint, Spatial}, style::Style};
// --- Table
#[derive(Clone, Debug, Default)]

View file

@ -3,7 +3,7 @@ use std::{any::TypeId, mem};
use ansi_to_tui::IntoText;
use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value};
use ratatui::widgets::Widget;
use yazi_shared::SStr;
use yazi_shim::SStr;
use super::{Area, Line, Span, Wrap};
use crate::{Error, elements::{Align, Spatial}};

View file

@ -1,10 +1,9 @@
use std::ops::Deref;
use mlua::{FromLua, IntoLua, Lua, Value};
use yazi_config::preview::PreviewWrap;
#[derive(Clone, Copy, Debug, Default)]
pub struct Wrap(pub(super) Option<ratatui::widgets::Wrap>);
pub struct Wrap(pub Option<ratatui::widgets::Wrap>);
impl Deref for Wrap {
type Target = Option<ratatui::widgets::Wrap>;
@ -26,15 +25,6 @@ impl From<ratatui::widgets::Wrap> for Wrap {
fn from(value: ratatui::widgets::Wrap) -> Self { Self(Some(value)) }
}
impl From<PreviewWrap> for Wrap {
fn from(value: PreviewWrap) -> Self {
Self(match value {
PreviewWrap::No => None,
PreviewWrap::Yes => Some(ratatui::widgets::Wrap { trim: false }),
})
}
}
impl FromLua for Wrap {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
let Value::Integer(n) = value else {

View file

@ -2,12 +2,12 @@ use std::{borrow::Cow, fmt::Display};
use mlua::{ExternalError, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods, Value};
use yazi_codegen::FromLuaOwned;
use yazi_shared::SStr;
use yazi_shim::SStr;
#[derive(FromLuaOwned)]
pub enum Error {
Io(std::io::Error),
Fs(yazi_fs::error::Error),
Fs(yazi_shim::fs::Error),
Serde(serde_json::Error),
Custom(SStr),
}
@ -18,7 +18,7 @@ impl Error {
let fs = lua.create_function(|_, value: Value| {
Ok(Self::Fs(match value {
Value::Table(t) => yazi_fs::error::Error::custom(
Value::Table(t) => yazi_shim::fs::Error::custom(
&t.raw_get::<mlua::String>("kind")?.to_str()?,
t.raw_get("code")?,
&t.raw_get::<mlua::String>("message")?.to_str()?,
@ -64,7 +64,7 @@ impl UserData for Error {
});
fields.add_field_method_get("kind", |_, me| {
Ok(match me {
Self::Io(e) => Some(yazi_fs::error::Error::from(e.kind()).kind_str()),
Self::Io(e) => Some(yazi_shim::fs::Error::from(e.kind()).kind_str()),
Self::Fs(e) => Some(e.kind_str()),
_ => None,
})

View file

@ -1,52 +0,0 @@
use std::ops::{Deref, DerefMut};
use mlua::{FromLua, Lua, UserData, UserDataFields, Value};
use yazi_shim::mlua::UserDataFieldsExt;
use crate::event::Cmd;
pub struct Action {
inner: yazi_shared::event::Action,
}
impl Deref for Action {
type Target = yazi_shared::event::Action;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl DerefMut for Action {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
}
impl From<Action> for yazi_shared::event::Action {
fn from(value: Action) -> Self { value.inner }
}
impl From<Action> for yazi_shared::event::Actions {
fn from(value: Action) -> Self { Self::from(value.inner) }
}
impl Action {
pub fn new(inner: impl Into<yazi_shared::event::Action>) -> Self { Self { inner: inner.into() } }
}
impl FromLua for Action {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
Ok(match value {
Value::String(s) => Self { inner: s.to_str()?.parse()? },
Value::UserData(ud) => ud.take()?,
_ => Err(mlua::Error::FromLuaConversionError {
from: value.type_name(),
to: "Action".to_owned(),
message: Some("expected a string or an Action".to_string()),
})?,
})
}
}
impl UserData for Action {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_cached_field("cmd", |_, me| Ok(Cmd::new(me.cmd.clone())));
}
}

View file

@ -1,50 +0,0 @@
use std::ops::{Deref, DerefMut};
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
use crate::event::Action;
#[derive(Clone)]
pub struct Actions {
inner: yazi_shared::event::Actions,
}
impl Deref for Actions {
type Target = yazi_shared::event::Actions;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl DerefMut for Actions {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
}
impl From<Actions> for yazi_shared::event::Actions {
fn from(value: Actions) -> Self { value.inner }
}
impl Actions {
pub fn new(inner: yazi_shared::event::Actions) -> Self { Self { inner } }
}
impl FromLua for Actions {
fn from_lua(value: Value, lua: &Lua) -> mlua::Result<Self> {
let inner = match value {
Value::Table(t) => t
.sequence_values::<Action>()
.map(|a| a.map(Into::into))
.collect::<mlua::Result<Vec<_>>>()?
.into(),
v @ Value::String(_) => Action::from_lua(v, lua)?.into(),
_ => Err("expected a string or a table of actions".into_lua_err())?,
};
Ok(Self { inner })
}
}
impl IntoLua for Actions {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
lua.create_sequence_from(self.inner.into_iter().map(Action::new))?.into_lua(lua)
}
}

View file

@ -1,34 +0,0 @@
use std::{mem, ops::{Deref, DerefMut}};
use mlua::{UserData, UserDataFields};
use yazi_shim::mlua::UserDataFieldsExt;
use crate::Sendable;
pub struct Cmd {
inner: yazi_shared::event::Cmd,
}
impl Deref for Cmd {
type Target = yazi_shared::event::Cmd;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl DerefMut for Cmd {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
}
impl Cmd {
pub fn new(inner: impl Into<yazi_shared::event::Cmd>) -> Self { Self { inner: inner.into() } }
}
impl UserData for Cmd {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_cached_field_mut("name", |_, me| Ok(mem::take(&mut me.name)));
fields.add_cached_field_mut("args", |lua, me| {
Sendable::args_to_table(lua, mem::take(&mut me.args))
});
}
}

View file

@ -1 +0,0 @@
yazi_macro::mod_flat!(action actions cmd);

View file

@ -1,78 +0,0 @@
use std::ops::Deref;
use mlua::{AnyUserData, ExternalError, FromLua, Lua, ObjectLike, Table, UserData, UserDataFields, UserDataMethods, UserDataRef, Value};
use crate::{Cha, Url, impl_file_fields, impl_file_methods};
pub type FileRef = UserDataRef<File>;
const EXPECTED: &str = "expected a table, File, or fs::File";
#[derive(Clone)]
pub struct File {
pub(crate) inner: yazi_fs::File,
}
impl Deref for File {
type Target = yazi_fs::File;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl From<File> for yazi_fs::File {
fn from(value: File) -> Self { value.inner }
}
impl File {
pub fn new(inner: impl Into<yazi_fs::File>) -> Self { Self { inner: inner.into() } }
pub fn install(lua: &Lua) -> mlua::Result<()> {
lua.globals().raw_set("File", lua.create_function(|_, file: Self| Ok(file))?)
}
}
impl TryFrom<Table> for File {
type Error = mlua::Error;
fn try_from(value: Table) -> Result<Self, Self::Error> {
Ok(Self::new(yazi_fs::File {
url: value.raw_get::<Url>("url")?.into(),
cha: *value.raw_get::<Cha>("cha")?,
..Default::default()
}))
}
}
impl TryFrom<AnyUserData> for File {
type Error = mlua::Error;
fn try_from(value: AnyUserData) -> Result<Self, Self::Error> {
Ok(if let Ok(me) = value.take::<Self>() { me } else { value.get("bare")? })
}
}
impl FromLua for File {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
match value {
Value::Table(tbl) => Self::try_from(tbl),
Value::UserData(ud) => Self::try_from(ud),
_ => Err(EXPECTED.into_lua_err())?,
}
}
}
impl UserData for File {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
impl_file_fields!(fields);
}
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
impl_file_methods!(methods);
methods.add_method("icon", |_, me, ()| {
use crate::Icon;
// TODO: use a cache
Ok(yazi_config::THEME.icon.matches(me, false).map(Icon::from))
});
}
}

View file

@ -1,30 +0,0 @@
use std::ops::Deref;
use mlua::{IntoLua, Lua, Value};
use crate::Style;
pub struct Icon {
inner: yazi_config::Icon,
}
impl Deref for Icon {
type Target = yazi_config::Icon;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl From<yazi_config::Icon> for Icon {
fn from(inner: yazi_config::Icon) -> Self { Self { inner } }
}
impl IntoLua for Icon {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
lua
.create_table_from([
("text", self.inner.text.into_lua(lua)?),
("style", Style::from(self.inner.style).into_lua(lua)?),
])?
.into_lua(lua)
}
}

View file

@ -1,17 +1,43 @@
use std::ops::Deref;
use std::path::PathBuf;
use image::{ImageDecoder, ImageError};
use mlua::{MetaMethod, UserData, UserDataFields, UserDataMethods};
pub struct ImageInfo(yazi_adapter::ImageInfo);
impl Deref for ImageInfo {
type Target = yazi_adapter::ImageInfo;
fn deref(&self) -> &Self::Target { &self.0 }
// --- ImageInfo
#[derive(Clone, Copy)]
pub struct ImageInfo {
pub format: image::ImageFormat,
pub width: u32,
pub height: u32,
pub color: image::ColorType,
pub orientation: Option<image::metadata::Orientation>,
}
impl From<yazi_adapter::ImageInfo> for ImageInfo {
fn from(value: yazi_adapter::ImageInfo) -> Self { Self(value) }
impl ImageInfo {
pub async fn new(path: PathBuf) -> image::ImageResult<Self> {
tokio::task::spawn_blocking(move || {
let reader = image::ImageReader::open(path)?.with_guessed_format()?;
let Some(format) = reader.format() else {
return Err(ImageError::IoError(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"unknown image format",
)));
};
let mut decoder = reader.into_decoder()?;
let (width, height) = decoder.dimensions();
Ok(Self {
format,
width,
height,
color: decoder.color_type(),
orientation: decoder.orientation().ok(),
})
})
.await
.map_err(|e| ImageError::IoError(e.into()))?
}
}
impl UserData for ImageInfo {
@ -25,12 +51,12 @@ impl UserData for ImageInfo {
}
// --- ImageFormat
struct ImageFormat(yazi_adapter::ImageFormat);
struct ImageFormat(image::ImageFormat);
impl UserData for ImageFormat {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method(MetaMethod::ToString, |_, me, ()| {
use yazi_adapter::ImageFormat as F;
use image::ImageFormat as F;
Ok(match me.0 {
F::Png => "PNG",
@ -55,12 +81,12 @@ impl UserData for ImageFormat {
}
// --- ImageColor
struct ImageColor(yazi_adapter::ImageColor);
struct ImageColor(image::ColorType);
impl UserData for ImageColor {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method(MetaMethod::ToString, |_, me, ()| {
use yazi_adapter::ImageColor as C;
use image::ColorType as C;
Ok(match me.0 {
C::L8 => "L8",

View file

@ -1,108 +0,0 @@
use std::ops::Deref;
use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value};
use serde::Deserialize;
use yazi_codegen::FromLuaOwned;
use yazi_shim::mlua::UserDataFieldsExt;
use crate::{Id, Iter, event::Actions, keymap::Key};
#[derive(FromLuaOwned)]
pub struct Chord {
inner: yazi_config::keymap::ChordArc,
}
impl Deref for Chord {
type Target = yazi_config::keymap::ChordArc;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl From<&yazi_config::keymap::ChordArc> for Chord {
fn from(value: &yazi_config::keymap::ChordArc) -> Self { Self { inner: value.clone() } }
}
impl From<Chord> for yazi_config::keymap::ChordArc {
fn from(value: Chord) -> Self { value.inner }
}
impl Chord {
pub fn new(inner: impl Into<yazi_config::keymap::ChordArc>) -> Self {
Self { inner: inner.into() }
}
}
impl TryFrom<(Value, yazi_shared::Layer)> for Chord {
type Error = mlua::Error;
fn try_from((value, layer): (Value, yazi_shared::Layer)) -> Result<Self, Self::Error> {
use yazi_config::keymap::Chord as C;
use yazi_shared::Layer as L;
let de = mlua::serde::Deserializer::new(value);
let inner = match layer {
L::Null => C::<{ L::Null as u8 }>::deserialize(de)?.into(),
L::App => C::<{ L::App as u8 }>::deserialize(de)?.into(),
L::Mgr => C::<{ L::Mgr as u8 }>::deserialize(de)?.into(),
L::Tasks => C::<{ L::Tasks as u8 }>::deserialize(de)?.into(),
L::Spot => C::<{ L::Spot as u8 }>::deserialize(de)?.into(),
L::Pick => C::<{ L::Pick as u8 }>::deserialize(de)?.into(),
L::Input => C::<{ L::Input as u8 }>::deserialize(de)?.into(),
L::Confirm => C::<{ L::Confirm as u8 }>::deserialize(de)?.into(),
L::Help => C::<{ L::Help as u8 }>::deserialize(de)?.into(),
L::Cmp => C::<{ L::Cmp as u8 }>::deserialize(de)?.into(),
L::Which => C::<{ L::Which as u8 }>::deserialize(de)?.into(),
L::Notify => C::<{ L::Notify as u8 }>::deserialize(de)?.into(),
};
Ok(Self { inner })
}
}
impl UserData for Chord {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("id", |_, me| Ok(Id(me.inner.id)));
fields
.add_cached_field("on", |lua, me| lua.create_sequence_from(me.on.iter().copied().map(Key)));
fields.add_cached_field("run", |_, me| Ok(Actions::new(me.run.clone())));
fields.add_cached_field("desc", |lua, me| lua.create_string(&me.desc));
}
}
// --- Matcher
pub struct ChordMatcher(pub(super) yazi_config::keymap::ChordMatcher);
impl TryFrom<Table> for ChordMatcher {
type Error = mlua::Error;
fn try_from(value: Table) -> Result<Self, Self::Error> {
let id: Id = value.raw_get("id").unwrap_or_default();
Ok(Self(yazi_config::keymap::ChordMatcher { id: id.0, ..Default::default() }))
}
}
impl FromLua for ChordMatcher {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
match value {
Value::Table(t) => t.try_into(),
_ => Err("expected a table of ChordMatcher".into_lua_err()),
}
}
}
// --- ChordIter
pub struct ChordIter(pub(super) yazi_config::keymap::ChordIter);
impl ChordIter {
pub fn new(inner: impl Into<yazi_config::keymap::ChordIter>) -> Self { Self(inner.into()) }
}
impl IntoLua for ChordIter {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
Iter::new(self.0.map(Chord::new), None).into_lua(lua)
}
}

View file

@ -1,69 +0,0 @@
use std::ops::Deref;
use mlua::{ExternalError, ExternalResult, MetaMethod, Table, UserData, UserDataMethods, Value};
use yazi_shim::mlua::DeserializeOverLua;
use super::{Chord, ChordMatcher};
use crate::{event::Actions, keymap::ChordIter};
pub struct Chords {
pub(super) inner: &'static yazi_config::keymap::Chords,
pub(super) layer: yazi_shared::Layer,
}
impl Deref for Chords {
type Target = yazi_config::keymap::Chords;
fn deref(&self) -> &Self::Target { self.inner }
}
impl UserData for Chords {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("match", |_, me, matcher: Option<ChordMatcher>| {
Ok(match matcher {
Some(matcher) => ChordIter::new(yazi_config::keymap::ChordIter {
chords: me.as_erased(),
matcher: matcher.0,
..Default::default()
}),
None => ChordIter::new(&*me.inner),
})
});
methods.add_method("insert", |_, me, (index, value): (isize, Value)| {
let index = match index {
1.. => index - 1,
0 => return Err("index must be 1-based or negative".into_lua_err()),
_ => index,
};
let chord = Chord::try_from((value, me.layer))?;
me.insert(index, chord.clone()).into_lua_err()?;
Ok(chord)
});
methods.add_method("remove", |_, me, matcher: ChordMatcher| {
me.remove(matcher.0);
Ok(())
});
methods.add_method("update", |_, me, (matcher, table): (ChordMatcher, Table)| {
let mut run: Option<Actions> = table.raw_get("run")?;
if let Some(run) = &mut run {
table.raw_remove("run")?;
run.set(me.layer, yazi_shared::Source::Key);
}
me.update(matcher.0, |mut chord| {
chord = chord.deserialize_over_lua(&table)?;
if let Some(run) = &run {
chord.run = run.clone().into();
}
Ok(chord)
})
});
methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.load().len()));
}
}

View file

@ -1,18 +0,0 @@
use std::ops::Deref;
use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value};
use crate::SER_OPT;
#[derive(Clone, Copy, Default, FromLua)]
pub struct Key(pub yazi_config::keymap::Key);
impl Deref for Key {
type Target = yazi_config::keymap::Key;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl IntoLua for Key {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { lua.to_value_with(&self.0, SER_OPT) }
}

View file

@ -1 +0,0 @@
yazi_macro::mod_flat!(chord chords key section);

View file

@ -1,49 +0,0 @@
use std::ops::Deref;
use anyhow::bail;
use mlua::{UserData, UserDataFields};
use yazi_config::KEYMAP;
use yazi_shim::mlua::UserDataFieldsExt;
use crate::keymap::Chords;
pub struct KeymapSection {
inner: &'static yazi_config::keymap::KeymapSection,
layer: yazi_shared::Layer,
}
impl Deref for KeymapSection {
type Target = yazi_config::keymap::KeymapSection;
fn deref(&self) -> &Self::Target { self.inner }
}
impl TryFrom<yazi_shared::Layer> for KeymapSection {
type Error = anyhow::Error;
fn try_from(layer: yazi_shared::Layer) -> Result<Self, Self::Error> {
use yazi_shared::Layer as L;
let inner = match layer {
L::Null | L::App => bail!("invalid layer"),
L::Mgr => KEYMAP.mgr.as_erased(),
L::Tasks => KEYMAP.tasks.as_erased(),
L::Spot => KEYMAP.spot.as_erased(),
L::Pick => KEYMAP.pick.as_erased(),
L::Input => KEYMAP.input.as_erased(),
L::Confirm => KEYMAP.confirm.as_erased(),
L::Help => KEYMAP.help.as_erased(),
L::Cmp => KEYMAP.cmp.as_erased(),
L::Which => bail!("invalid layer"),
L::Notify => bail!("invalid layer"),
};
Ok(Self { inner, layer })
}
}
impl UserData for KeymapSection {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_cached_field("rules", |_, me| Ok(Chords { inner: me.inner, layer: me.layer }));
}
}

View file

@ -1,15 +0,0 @@
use mlua::{MetaMethod, UserData, UserDataMethods};
use yazi_shim::strum::IntoStr;
#[derive(Clone, Copy)]
pub struct Layer(yazi_shared::Layer);
impl From<yazi_shared::Layer> for Layer {
fn from(event: yazi_shared::Layer) -> Self { Self(event) }
}
impl UserData for Layer {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method(MetaMethod::ToString, |_, me, ()| Ok(me.0.into_str()));
}
}

View file

@ -1,5 +1,5 @@
mod macros;
yazi_macro::mod_pub!(config elements event keymap process theme);
yazi_macro::mod_pub!(elements position process style);
yazi_macro::mod_flat!(access calculator cha chan composer dnd error fd file handle icon id image input iter layer mouse path permit range runtime scheme selector sendable stage style tty url utils);
yazi_macro::mod_flat!(chan composer error handle image iter permit range runtime);

View file

@ -50,8 +50,7 @@ macro_rules! impl_area_method {
"area",
|lua, (ud, area): (mlua::AnyUserData, Option<mlua::AnyUserData>)| {
use mlua::IntoLua;
use crate::elements::Spatial;
use $crate::elements::Spatial;
if let Some(v) = area {
ud.borrow_mut::<Self>()?.set_area((&v).try_into()?);
@ -67,7 +66,7 @@ macro_rules! impl_area_method {
#[macro_export]
macro_rules! impl_style_method {
($methods:ident, $($field:tt).+) => {
$methods.add_function("style", |_, (ud, style): (mlua::AnyUserData, $crate::Style)| {
$methods.add_function("style", |_, (ud, style): (mlua::AnyUserData, $crate::style::Style)| {
ud.borrow_mut::<Self>()?.$($field).+ = style.0;
Ok(ud)
});
@ -208,21 +207,22 @@ macro_rules! impl_file_fields {
($fields:ident) => {
use yazi_shim::mlua::UserDataFieldsExt;
$fields.add_cached_field("cha", |_, me| Ok($crate::Cha(me.cha)));
$fields.add_cached_field("url", |_, me| Ok($crate::Url::new(me.url_owned())));
$fields.add_cached_field("link_to", |_, me| Ok(me.link_to.as_ref().map($crate::Path::new)));
$fields.add_cached_field("cha", |_, me| Ok(me.cha));
$fields.add_cached_field("url", |_, me| Ok(me.url_owned()));
$fields.add_cached_field("link_to", |_, me| Ok(me.link_to.clone()));
$fields.add_cached_field("name", |lua, me| {
me.name().map(|s| lua.create_string(s.encoded_bytes())).transpose()
});
$fields.add_cached_field("path", |_, me| {
use yazi_fs::FsUrl;
use yazi_shared::url::AsUrl;
Ok($crate::Path::new(me.url.as_url().unified_path()))
use yazi_shared::{path::PathBufDyn, url::AsUrl};
Ok(PathBufDyn::from(me.url.as_url().unified_path()))
});
$fields.add_cached_field("cache", |_, me| {
use yazi_fs::FsUrl;
Ok(me.url.cache().map($crate::Path::new))
use yazi_shared::path::PathBufDyn;
Ok(me.url.cache().map(PathBufDyn::from))
});
};
}

View file

@ -1,37 +0,0 @@
use std::ops::Deref;
use mlua::{UserData, UserDataFields};
use yazi_term::event::{MouseButton, MouseEventKind};
#[derive(Clone, Copy)]
pub struct MouseEvent(yazi_term::event::MouseEvent);
impl Deref for MouseEvent {
type Target = yazi_term::event::MouseEvent;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl From<yazi_term::event::MouseEvent> for MouseEvent {
fn from(event: yazi_term::event::MouseEvent) -> Self { Self(event) }
}
impl UserData for MouseEvent {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field("type", "legacy");
fields.add_field_method_get("x", |_, me| Ok(me.column));
fields.add_field_method_get("y", |_, me| Ok(me.row));
fields.add_field_method_get("is_left", |_, me| {
use MouseEventKind as K;
Ok(matches!(me.kind, K::Down(b) | K::Up(b) | K::Drag(b) if b == MouseButton::Left))
});
fields.add_field_method_get("is_right", |_, me| {
use MouseEventKind as K;
Ok(matches!(me.kind, K::Down(b) | K::Up(b) | K::Drag(b) if b == MouseButton::Right))
});
fields.add_field_method_get("is_middle", |_, me| {
use MouseEventKind as K;
Ok(matches!(me.kind, K::Down(b) | K::Up(b) | K::Drag(b) if b == MouseButton::Middle))
});
}
}

View file

@ -0,0 +1 @@
yazi_macro::mod_flat!(offset origin position);

View file

@ -1,7 +1,6 @@
use std::ops::{Deref, DerefMut};
use ratatui::{layout::Rect, widgets::Padding};
use yazi_term::Dimension;
use super::{Offset, Origin};
@ -24,7 +23,7 @@ impl DerefMut for Position {
impl Position {
pub const fn new(origin: Origin, offset: Offset) -> Self { Self { origin, offset } }
pub fn rect(&self, Dimension { cols, rows, .. }: Dimension) -> Rect {
pub fn rect(&self, (cols, rows): (u16, u16)) -> Rect {
use Origin::*;
let Offset { x, y, width, height } = self.offset;
@ -54,7 +53,7 @@ impl Position {
}
}
pub fn sticky(Dimension { cols, rows, .. }: Dimension, base: Rect, offset: Offset) -> Rect {
pub fn sticky((cols, rows): (u16, u16), base: Rect, offset: Offset) -> Rect {
let Offset { x, y, width, height } = offset;
let above =

Some files were not shown because too many files have changed in this diff Show more