mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
perf: chunk loading for mime-types (#467)
This commit is contained in:
parent
ccb98ac21f
commit
5fb0fceacb
31 changed files with 219 additions and 115 deletions
|
|
@ -112,7 +112,7 @@ impl Adaptor {
|
||||||
return Self::X11;
|
return Self::X11;
|
||||||
}
|
}
|
||||||
if std::fs::symlink_metadata("/proc/sys/fs/binfmt_misc/WSLInterop").is_ok() {
|
if std::fs::symlink_metadata("/proc/sys/fs/binfmt_misc/WSLInterop").is_ok() {
|
||||||
return Self::Kitty;
|
return Self::KittyOld;
|
||||||
}
|
}
|
||||||
|
|
||||||
warn!("[Adaptor] Falling back to chafa");
|
warn!("[Adaptor] Falling back to chafa");
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ where
|
||||||
where
|
where
|
||||||
A: de::SeqAccess<'de>,
|
A: de::SeqAccess<'de>,
|
||||||
{
|
{
|
||||||
let mut execs = Vec::new();
|
let mut execs = vec![];
|
||||||
while let Some(value) = &seq.next_element::<String>()? {
|
while let Some(value) = &seq.next_element::<String>()? {
|
||||||
execs.push(parse(value).map_err(de::Error::custom)?);
|
execs.push(parse(value).map_err(de::Error::custom)?);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ impl OpenRule {
|
||||||
where
|
where
|
||||||
A: de::SeqAccess<'de>,
|
A: de::SeqAccess<'de>,
|
||||||
{
|
{
|
||||||
let mut uses = Vec::new();
|
let mut uses = vec![];
|
||||||
while let Some(use_) = seq.next_element::<String>()? {
|
while let Some(use_) = seq.next_element::<String>()? {
|
||||||
uses.push(use_);
|
uses.push(use_);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,13 +24,16 @@ impl Pattern {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn match_path(&self, path: impl AsRef<Path>, is_folder: bool) -> bool {
|
pub fn match_path(&self, path: impl AsRef<Path>, is_folder: bool) -> bool {
|
||||||
|
if is_folder != self.is_folder {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let s = if self.full_path {
|
self.matches(if self.full_path {
|
||||||
path.to_str()
|
path.to_string_lossy()
|
||||||
} else {
|
} else {
|
||||||
path.file_name().and_then(|n| n.to_str()).or_else(|| path.to_str())
|
path.file_name().map_or_else(|| path.to_string_lossy(), |n| n.to_string_lossy())
|
||||||
};
|
})
|
||||||
is_folder == self.is_folder && s.is_some_and(|s| self.matches(s))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ impl Icon {
|
||||||
where
|
where
|
||||||
A: de::MapAccess<'de>,
|
A: de::MapAccess<'de>,
|
||||||
{
|
{
|
||||||
let mut icons = Vec::new();
|
let mut icons = vec![];
|
||||||
while let Some((key, value)) = &map.next_entry::<String, String>()? {
|
while let Some((key, value)) = &map.next_entry::<String, String>()? {
|
||||||
icons.push(Icon {
|
icons.push(Icon {
|
||||||
name: Pattern::try_from(key.clone())
|
name: Pattern::try_from(key.clone())
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ impl Completion {
|
||||||
let ticket = self.ticket;
|
let ticket = self.ticket;
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut dir = fs::read_dir(&parent).await?;
|
let mut dir = fs::read_dir(&parent).await?;
|
||||||
let mut cache = Vec::new();
|
let mut cache = vec![];
|
||||||
while let Ok(Some(f)) = dir.next_entry().await {
|
while let Ok(Some(f)) = dir.next_entry().await {
|
||||||
let Ok(meta) = f.metadata().await else {
|
let Ok(meta) = f.metadata().await else {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -343,7 +343,7 @@ impl Files {
|
||||||
// --- Selected
|
// --- Selected
|
||||||
pub fn selected(&self, pending: &BTreeSet<usize>, unset: bool) -> Vec<&File> {
|
pub fn selected(&self, pending: &BTreeSet<usize>, unset: bool) -> Vec<&File> {
|
||||||
if self.selected.is_empty() && (unset || pending.is_empty()) {
|
if self.selected.is_empty() && (unset || pending.is_empty()) {
|
||||||
return Vec::new();
|
return vec![];
|
||||||
}
|
}
|
||||||
|
|
||||||
let selected: BTreeSet<_> = self.selected.iter().collect();
|
let selected: BTreeSet<_> = self.selected.iter().collect();
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ impl Manager {
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
done.extend(todo.iter().map(|f| (f.url(), None)));
|
done.extend(todo.iter().map(|f| (f.url(), None)));
|
||||||
if let Err(e) = isolate::preload("mime.lua".to_string(), todo, true).await {
|
if let Err(e) = isolate::preload("mime.lua", todo, true).await {
|
||||||
error!("preload in watcher failed: {e}");
|
error!("preload in watcher failed: {e}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ impl Manager {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut failed = Vec::new();
|
let mut failed = vec![];
|
||||||
for (o, n) in todo {
|
for (o, n) in todo {
|
||||||
if fs::symlink_metadata(&n).await.is_ok() {
|
if fs::symlink_metadata(&n).await.is_ok() {
|
||||||
failed.push((o, n, anyhow!("Destination already exists")));
|
failed.push((o, n, anyhow!("Destination already exists")));
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ impl Manager {
|
||||||
|
|
||||||
fn handle_ioerr(&mut self, op: FilesOp) -> bool {
|
fn handle_ioerr(&mut self, op: FilesOp) -> bool {
|
||||||
let url = op.url();
|
let url = op.url();
|
||||||
let op = FilesOp::Full(url.clone(), Vec::new());
|
let op = FilesOp::Full(url.clone(), vec![]);
|
||||||
|
|
||||||
if url == self.cwd() {
|
if url == self.cwd() {
|
||||||
self.current_mut().update(op);
|
self.current_mut().update(op);
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,7 @@ impl Watcher {
|
||||||
if reload.is_empty() {
|
if reload.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Err(e) = isolate::preload("mime.lua".to_string(), reload, true).await {
|
if let Err(e) = isolate::preload("mime.lua", reload, true).await {
|
||||||
error!("preload in watcher failed: {e}");
|
error!("preload in watcher failed: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ impl App {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut patches = Vec::new();
|
let mut patches = vec![];
|
||||||
for x in frame.area.left()..frame.area.right() {
|
for x in frame.area.left()..frame.area.right() {
|
||||||
for y in frame.area.top()..frame.area.bottom() {
|
for y in frame.area.top()..frame.area.bottom() {
|
||||||
let cell = frame.buffer.get(x, y);
|
let cell = frame.buffer.get(x, y);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
function M:peek()
|
function M:peek()
|
||||||
local limit = self.area.h
|
|
||||||
local child = Command("jq")
|
local child = Command("jq")
|
||||||
:args({
|
:args({
|
||||||
"-C",
|
"-C",
|
||||||
|
|
@ -13,10 +12,17 @@ function M:peek()
|
||||||
:stderr(Command.PIPED)
|
:stderr(Command.PIPED)
|
||||||
:spawn()
|
:spawn()
|
||||||
|
|
||||||
|
if not child then
|
||||||
|
return self:fallback_to_builtin()
|
||||||
|
end
|
||||||
|
|
||||||
|
local limit = self.area.h
|
||||||
local i, lines = 0, ""
|
local i, lines = 0, ""
|
||||||
repeat
|
repeat
|
||||||
local code, next = child:read_line()
|
local next, event = child:read_line()
|
||||||
if code ~= 0 then
|
if event == 1 then
|
||||||
|
return self:fallback_to_builtin()
|
||||||
|
elseif event ~= 0 then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -46,4 +52,11 @@ function M:seek(units)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function M:fallback_to_builtin()
|
||||||
|
local _, bound = ya.preview_code(self)
|
||||||
|
if bound then
|
||||||
|
ya.manager_emit("peek", { tostring(bound), only_if = tostring(self.file.url), upper_bound = "" })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,57 @@
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
function M:preload()
|
function M:preload()
|
||||||
local command = Command("file"):arg("--mime-type"):stdout(Command.PIPED):stderr(Command.PIPED)
|
|
||||||
if ya.target_family() == "windows" then
|
|
||||||
command:arg("-b")
|
|
||||||
else
|
|
||||||
command:arg("-bL")
|
|
||||||
end
|
|
||||||
|
|
||||||
local urls = {}
|
local urls = {}
|
||||||
for _, file in ipairs(self.files) do
|
for _, file in ipairs(self.files) do
|
||||||
urls[#urls + 1] = tostring(file.url)
|
urls[#urls + 1] = tostring(file.url)
|
||||||
end
|
end
|
||||||
|
|
||||||
local i, mimes = 1, {}
|
local args
|
||||||
local output = command:args(urls):output()
|
if ya.target_family() == "windows" then
|
||||||
for line in output.stdout:gmatch("[^\r\n]+") do
|
args = { "-b", "--mime-type" }
|
||||||
if i > #urls then
|
else
|
||||||
break
|
args = { "-b", "-L", "--mime-type" }
|
||||||
end
|
|
||||||
if ya.mime_valid(line) then
|
|
||||||
mimes[urls[i]] = line
|
|
||||||
end
|
|
||||||
i = i + 1
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if #mimes then
|
local child, code = Command("file"):args(args):args(urls):stdout(Command.PIPED):spawn()
|
||||||
ya.manager_emit("update_mimetype", {}, mimes)
|
if not child then
|
||||||
return 3
|
ya.err("spawn `file` command returns " .. tostring(code))
|
||||||
|
return 0
|
||||||
end
|
end
|
||||||
return 2
|
|
||||||
|
local mimes, last = {}, ya.time()
|
||||||
|
local flush = function(force)
|
||||||
|
if not force and ya.time() - last < 0.1 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if next(mimes) then
|
||||||
|
ya.manager_emit("update_mimetype", {}, mimes)
|
||||||
|
mimes, last = {}, ya.time()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local i, j = 1, 0
|
||||||
|
repeat
|
||||||
|
local next, event = child:read_line_with { timeout = 100 }
|
||||||
|
if event == 3 then
|
||||||
|
flush(true)
|
||||||
|
goto continue
|
||||||
|
elseif event ~= 0 then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
next = next:gsub("[\r\n]+$", "")
|
||||||
|
if ya.mime_valid(next) then
|
||||||
|
j, mimes[urls[i]] = j + 1, next
|
||||||
|
flush(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
i = i + 1
|
||||||
|
::continue::
|
||||||
|
until i > #urls
|
||||||
|
|
||||||
|
flush(true)
|
||||||
|
return j == #urls and 3 or 2
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,9 @@ function M:preload()
|
||||||
:stderr(Command.PIPED)
|
:stderr(Command.PIPED)
|
||||||
:output()
|
:output()
|
||||||
|
|
||||||
if not output.status:success() then
|
if not output then
|
||||||
|
return 0
|
||||||
|
elseif not output.status:success() then
|
||||||
local pages = tonumber(output.stderr:match("the last page %((%d+)%)")) or 0
|
local pages = tonumber(output.stderr:match("the last page %((%d+)%)")) or 0
|
||||||
if self.skip > 0 and pages > 0 then
|
if self.skip > 0 and pages > 0 then
|
||||||
ya.manager_emit("peek", { tostring(math.max(0, pages - 1)), only_if = tostring(self.file.url), upper_bound = "" })
|
ya.manager_emit("peek", { tostring(math.max(0, pages - 1)), only_if = tostring(self.file.url), upper_bound = "" })
|
||||||
|
|
|
||||||
|
|
@ -31,25 +31,27 @@ function M:preload()
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
local status = Command("ffmpegthumbnailer")
|
local child = Command("ffmpegthumbnailer"):args({
|
||||||
:args({
|
"-q",
|
||||||
"-q",
|
"6",
|
||||||
"6",
|
"-c",
|
||||||
"-c",
|
"jpeg",
|
||||||
"jpeg",
|
"-i",
|
||||||
"-i",
|
tostring(self.file.url),
|
||||||
tostring(self.file.url),
|
"-o",
|
||||||
"-o",
|
tostring(cache),
|
||||||
tostring(cache),
|
"-t",
|
||||||
"-t",
|
tostring(percentage),
|
||||||
tostring(percentage),
|
"-s",
|
||||||
"-s",
|
tostring(PREVIEW.max_width),
|
||||||
tostring(PREVIEW.max_width),
|
}):spawn()
|
||||||
})
|
|
||||||
:spawn()
|
|
||||||
:wait()
|
|
||||||
|
|
||||||
return status:success() and 1 or 2
|
if not child then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local status = child:wait()
|
||||||
|
return status and status:success() and 1 or 2
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
||||||
|
|
@ -32,13 +32,12 @@ impl<'a> TryFrom<Table<'a>> for Line {
|
||||||
|
|
||||||
impl Line {
|
impl Line {
|
||||||
pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> {
|
pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> {
|
||||||
#[inline]
|
let new = lua.create_function(|_, (_, value): (Table, Value)| {
|
||||||
fn new(value: Value) -> mlua::Result<Line> {
|
|
||||||
if let Value::Table(tbl) = value {
|
if let Value::Table(tbl) = value {
|
||||||
return Line::try_from(tbl);
|
return Line::try_from(tbl);
|
||||||
}
|
}
|
||||||
Err("expected a table of Spans or Lines".into_lua_err())
|
Err("expected a table of Spans or Lines".into_lua_err())
|
||||||
}
|
})?;
|
||||||
|
|
||||||
let line = lua.create_table_from([
|
let line = lua.create_table_from([
|
||||||
// Alignment
|
// Alignment
|
||||||
|
|
@ -47,10 +46,7 @@ impl Line {
|
||||||
("RIGHT", RIGHT.into_lua(lua)?),
|
("RIGHT", RIGHT.into_lua(lua)?),
|
||||||
])?;
|
])?;
|
||||||
|
|
||||||
line.set_metatable(Some(lua.create_table_from([(
|
line.set_metatable(Some(lua.create_table_from([("__call", new)])?));
|
||||||
"__call",
|
|
||||||
lua.create_function(|_, (_, value): (Table, Value)| new(value))?,
|
|
||||||
)])?));
|
|
||||||
|
|
||||||
ui.set("Line", line)
|
ui.set("Line", line)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,25 +9,21 @@ pub struct Rect;
|
||||||
|
|
||||||
impl Rect {
|
impl Rect {
|
||||||
pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> {
|
pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> {
|
||||||
#[inline]
|
let new = lua.create_function(|lua, (_, args): (Table, Table)| {
|
||||||
fn new<'a>(lua: &'a Lua, args: Table) -> mlua::Result<AnyUserData<'a>> {
|
|
||||||
Rect::cast(lua, ratatui::layout::Rect {
|
Rect::cast(lua, ratatui::layout::Rect {
|
||||||
x: args.get("x")?,
|
x: args.get("x")?,
|
||||||
y: args.get("y")?,
|
y: args.get("y")?,
|
||||||
width: args.get("w")?,
|
width: args.get("w")?,
|
||||||
height: args.get("h")?,
|
height: args.get("h")?,
|
||||||
})
|
})
|
||||||
}
|
})?;
|
||||||
|
|
||||||
let rect = lua.create_table_from([(
|
let rect = lua.create_table_from([(
|
||||||
"default",
|
"default",
|
||||||
Rect::cast(lua, ratatui::layout::Rect::default())?.into_lua(lua)?,
|
Rect::cast(lua, ratatui::layout::Rect::default())?.into_lua(lua)?,
|
||||||
)])?;
|
)])?;
|
||||||
|
|
||||||
rect.set_metatable(Some(lua.create_table_from([(
|
rect.set_metatable(Some(lua.create_table_from([("__call", new)])?));
|
||||||
"__call",
|
|
||||||
lua.create_function(|lua, (_, args): (Table, Table)| new(lua, args))?,
|
|
||||||
)])?));
|
|
||||||
|
|
||||||
ui.set("Rect", rect)
|
ui.set("Rect", rect)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,12 @@ pub struct Style(pub(super) ratatui::style::Style);
|
||||||
|
|
||||||
impl Style {
|
impl Style {
|
||||||
pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> {
|
pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> {
|
||||||
ui.set("Style", lua.create_function(|_, ()| Ok(Self::default()))?)
|
let new = lua.create_function(|_, ()| Ok(Self::default()))?;
|
||||||
|
|
||||||
|
let style = lua.create_table()?;
|
||||||
|
style.set_metatable(Some(lua.create_table_from([("__call", new)])?));
|
||||||
|
|
||||||
|
ui.set("Style", style)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
2
yazi-plugin/src/external/shell.rs
vendored
2
yazi-plugin/src/external/shell.rs
vendored
|
|
@ -73,7 +73,7 @@ mod parser {
|
||||||
|
|
||||||
pub(super) fn parse(cmd: &str, args: &[&str]) -> Vec<String> {
|
pub(super) fn parse(cmd: &str, args: &[&str]) -> Vec<String> {
|
||||||
let mut it = cmd.chars().peekable();
|
let mut it = cmd.chars().peekable();
|
||||||
let mut expanded = Vec::new();
|
let mut expanded = vec![];
|
||||||
|
|
||||||
while let Some(c) = it.next() {
|
while let Some(c) = it.next() {
|
||||||
if c.is_whitespace() {
|
if c.is_whitespace() {
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,13 @@ use super::slim_lua;
|
||||||
use crate::{bindings::{Cast, File}, elements::Rect, LOADED};
|
use crate::{bindings::{Cast, File}, elements::Rect, LOADED};
|
||||||
|
|
||||||
pub async fn preload(
|
pub async fn preload(
|
||||||
name: String,
|
name: &str,
|
||||||
files: Vec<yazi_shared::fs::File>,
|
files: Vec<yazi_shared::fs::File>,
|
||||||
multi: bool,
|
multi: bool,
|
||||||
) -> mlua::Result<u8> {
|
) -> mlua::Result<u8> {
|
||||||
LOADED.ensure(&name).await.into_lua_err()?;
|
LOADED.ensure(name).await.into_lua_err()?;
|
||||||
|
|
||||||
|
let name = name.to_owned();
|
||||||
tokio::task::spawn_blocking(move || {
|
tokio::task::spawn_blocking(move || {
|
||||||
let lua = slim_lua()?;
|
let lua = slim_lua()?;
|
||||||
let plugin: Table = if let Some(b) = LOADED.read().get(&name) {
|
let plugin: Table = if let Some(b) = LOADED.read().get(&name) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
use mlua::{prelude::LuaUserDataMethods, UserData};
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use mlua::{prelude::LuaUserDataMethods, IntoLua, Table, UserData, Value};
|
||||||
use tokio::{io::{AsyncBufReadExt, AsyncReadExt, BufReader}, process::{ChildStderr, ChildStdin, ChildStdout}, select};
|
use tokio::{io::{AsyncBufReadExt, AsyncReadExt, BufReader}, process::{ChildStderr, ChildStdin, ChildStdout}, select};
|
||||||
|
|
||||||
use super::Status;
|
use super::Status;
|
||||||
|
|
@ -21,27 +23,8 @@ impl Child {
|
||||||
|
|
||||||
impl UserData for Child {
|
impl UserData for Child {
|
||||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
methods.add_async_method_mut("read", |_, me, ()| async move {
|
#[inline]
|
||||||
async fn read(t: Option<impl AsyncBufReadExt + Unpin>) -> Option<Vec<u8>> {
|
async fn read_line(me: &mut Child) -> (String, u8) {
|
||||||
let Some(mut r) = t else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut buf = vec![0; 4096];
|
|
||||||
match r.read(&mut buf).await {
|
|
||||||
Ok(0) | Err(_) => return None,
|
|
||||||
Ok(n) => buf.truncate(n),
|
|
||||||
}
|
|
||||||
Some(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(select! {
|
|
||||||
Some(r) = read(me.stdout.as_mut()) => (0u8, r),
|
|
||||||
Some(r) = read(me.stderr.as_mut()) => (1u8, r),
|
|
||||||
else => (2u8, Vec::new())
|
|
||||||
})
|
|
||||||
});
|
|
||||||
methods.add_async_method_mut("read_line", |_, me, ()| async move {
|
|
||||||
async fn read(t: Option<impl AsyncBufReadExt + Unpin>) -> Option<String> {
|
async fn read(t: Option<impl AsyncBufReadExt + Unpin>) -> Option<String> {
|
||||||
let Some(mut r) = t else {
|
let Some(mut r) = t else {
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -54,15 +37,52 @@ impl UserData for Child {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select! {
|
||||||
|
Some(r) = read(me.stdout.as_mut()) => (r, 0u8),
|
||||||
|
Some(r) = read(me.stderr.as_mut()) => (r, 1u8),
|
||||||
|
else => (String::new(), 2u8),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
methods.add_async_method_mut("read", |_, me, len: usize| async move {
|
||||||
|
async fn read(t: Option<impl AsyncBufReadExt + Unpin>, len: usize) -> Option<Vec<u8>> {
|
||||||
|
let Some(mut r) = t else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut buf = vec![0; len];
|
||||||
|
match r.read(&mut buf).await {
|
||||||
|
Ok(0) | Err(_) => return None,
|
||||||
|
Ok(n) => buf.truncate(n),
|
||||||
|
}
|
||||||
|
Some(buf)
|
||||||
|
}
|
||||||
|
|
||||||
Ok(select! {
|
Ok(select! {
|
||||||
Some(r) = read(me.stdout.as_mut()) => (0u8, r),
|
Some(r) = read(me.stdout.as_mut(), len) => (r, 0u8),
|
||||||
Some(r) = read(me.stderr.as_mut()) => (1u8, r),
|
Some(r) = read(me.stderr.as_mut(), len) => (r, 1u8),
|
||||||
else => (2u8, String::new()),
|
else => (vec![], 2u8)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
methods.add_async_method_mut("wait", |_, me, ()| async move {
|
methods.add_async_method_mut("read_line", |_, me, ()| async move { Ok(read_line(me).await) });
|
||||||
Ok(Status::new(me.inner.wait().await?))
|
methods.add_async_method_mut("read_line_with", |_, me, options: Table| async move {
|
||||||
|
let timeout: u64 = options.get("timeout")?;
|
||||||
|
match tokio::time::timeout(Duration::from_millis(timeout), read_line(me)).await {
|
||||||
|
Ok(value) => Ok(value),
|
||||||
|
Err(_) => Ok((String::new(), 3u8)),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
methods.add_async_method_mut("wait", |lua, me, ()| async move {
|
||||||
|
Ok(match me.inner.wait().await {
|
||||||
|
Ok(status) => (Status::new(status).into_lua(lua)?, Value::Nil),
|
||||||
|
Err(e) => (Value::Nil, e.raw_os_error().into_lua(lua)?),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
methods.add_method_mut("start_kill", |lua, me, ()| {
|
||||||
|
Ok(match me.inner.start_kill() {
|
||||||
|
Ok(_) => (true, Value::Nil),
|
||||||
|
Err(e) => (false, e.raw_os_error().into_lua(lua)?),
|
||||||
|
})
|
||||||
});
|
});
|
||||||
methods.add_method_mut("start_kill", |_, me, ()| Ok(me.inner.start_kill().is_ok()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
|
|
||||||
use mlua::{prelude::LuaUserDataMethods, AnyUserData, IntoLua, Lua, Table, UserData};
|
use mlua::{prelude::LuaUserDataMethods, AnyUserData, IntoLua, Lua, Table, UserData, Value};
|
||||||
|
|
||||||
use super::{output::Output, Child};
|
use super::{output::Output, Child};
|
||||||
|
|
||||||
|
|
@ -72,9 +72,17 @@ impl UserData for Command {
|
||||||
});
|
});
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_method_mut("spawn", |_, me, ()| Ok(Child::new(me.inner.spawn()?)));
|
methods.add_method_mut("spawn", |lua, me, ()| {
|
||||||
methods.add_async_method_mut("output", |_, me, ()| async move {
|
Ok(match me.inner.spawn() {
|
||||||
Ok(Output::new(me.inner.output().await?))
|
Ok(child) => (Child::new(child).into_lua(lua)?, Value::Nil),
|
||||||
|
Err(e) => (Value::Nil, e.raw_os_error().into_lua(lua)?),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
methods.add_async_method_mut("output", |lua, me, ()| async move {
|
||||||
|
Ok(match me.inner.output().await {
|
||||||
|
Ok(output) => (Output::new(output).into_lua(lua)?, Value::Nil),
|
||||||
|
Err(e) => (Value::Nil, e.raw_os_error().into_lua(lua)?),
|
||||||
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use crate::ValueSendable;
|
||||||
|
|
||||||
impl Utils {
|
impl Utils {
|
||||||
fn parse_args(t: Table) -> mlua::Result<(Vec<String>, BTreeMap<String, String>)> {
|
fn parse_args(t: Table) -> mlua::Result<(Vec<String>, BTreeMap<String, String>)> {
|
||||||
let mut args = Vec::new();
|
let mut args = vec![];
|
||||||
let mut named = BTreeMap::new();
|
let mut named = BTreeMap::new();
|
||||||
for result in t.pairs::<Value, Value>() {
|
for result in t.pairs::<Value, Value>() {
|
||||||
let (k, Value::String(v)) = result? else {
|
let (k, Value::String(v)) = result? else {
|
||||||
|
|
|
||||||
14
yazi-plugin/src/utils/log.rs
Normal file
14
yazi-plugin/src/utils/log.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
use mlua::{Lua, Table};
|
||||||
|
use tracing::{debug, error};
|
||||||
|
|
||||||
|
use super::Utils;
|
||||||
|
|
||||||
|
impl Utils {
|
||||||
|
pub(super) fn log(lua: &Lua, ya: &Table) -> mlua::Result<()> {
|
||||||
|
ya.set("dbg", lua.create_async_function(|_, s: String| async move { Ok(debug!("{s}")) })?)?;
|
||||||
|
|
||||||
|
ya.set("err", lua.create_async_function(|_, s: String| async move { Ok(error!("{s}")) })?)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,10 +3,12 @@
|
||||||
mod cache;
|
mod cache;
|
||||||
mod call;
|
mod call;
|
||||||
mod image;
|
mod image;
|
||||||
|
mod log;
|
||||||
mod plugin;
|
mod plugin;
|
||||||
mod preview;
|
mod preview;
|
||||||
mod target;
|
mod target;
|
||||||
mod text;
|
mod text;
|
||||||
|
mod time;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
pub use preview::*;
|
pub use preview::*;
|
||||||
|
|
|
||||||
18
yazi-plugin/src/utils/time.rs
Normal file
18
yazi-plugin/src/utils/time.rs
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
use mlua::{Lua, Table};
|
||||||
|
|
||||||
|
use super::Utils;
|
||||||
|
|
||||||
|
impl Utils {
|
||||||
|
pub(super) fn time(lua: &Lua, ya: &Table) -> mlua::Result<()> {
|
||||||
|
ya.set(
|
||||||
|
"time",
|
||||||
|
lua.create_function(|_, ()| {
|
||||||
|
Ok(SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok())
|
||||||
|
})?,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,9 +8,11 @@ pub fn install(lua: &Lua) -> mlua::Result<()> {
|
||||||
Utils::cache(lua, &ya)?;
|
Utils::cache(lua, &ya)?;
|
||||||
Utils::call(lua, &ya)?;
|
Utils::call(lua, &ya)?;
|
||||||
Utils::image(lua, &ya)?;
|
Utils::image(lua, &ya)?;
|
||||||
|
Utils::log(lua, &ya)?;
|
||||||
Utils::plugin(lua, &ya)?;
|
Utils::plugin(lua, &ya)?;
|
||||||
Utils::preview(lua, &ya)?;
|
Utils::preview(lua, &ya)?;
|
||||||
Utils::target(lua, &ya)?;
|
Utils::target(lua, &ya)?;
|
||||||
|
Utils::time(lua, &ya)?;
|
||||||
Utils::text(lua, &ya)?;
|
Utils::text(lua, &ya)?;
|
||||||
|
|
||||||
lua.globals().set("ya", ya)
|
lua.globals().set("ya", ya)
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ impl Preload {
|
||||||
match op {
|
match op {
|
||||||
PreloadOp::Rule(task) => {
|
PreloadOp::Rule(task) => {
|
||||||
let urls: Vec<_> = task.targets.iter().map(|f| f.url()).collect();
|
let urls: Vec<_> = task.targets.iter().map(|f| f.url()).collect();
|
||||||
let result = isolate::preload(task.plugin.cmd, task.targets, task.plugin.multi).await;
|
let result = isolate::preload(&task.plugin.cmd, task.targets, task.plugin.multi).await;
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
self.fail(task.id, format!("Preload task failed:\n{e}"))?;
|
self.fail(task.id, format!("Preload task failed:\n{e}"))?;
|
||||||
return Err(e.into());
|
return Err(e.into());
|
||||||
|
|
@ -39,7 +39,7 @@ impl Preload {
|
||||||
|
|
||||||
let code = result.unwrap();
|
let code = result.unwrap();
|
||||||
if code & 1 == 0 {
|
if code & 1 == 0 {
|
||||||
error!("Preload task returned {code}");
|
error!("Preload task `{}` returned {code}", task.plugin.cmd);
|
||||||
}
|
}
|
||||||
if code >> 1 & 1 != 0 {
|
if code >> 1 & 1 != 0 {
|
||||||
let mut loaded = self.rule_loaded.write();
|
let mut loaded = self.rule_loaded.write();
|
||||||
|
|
|
||||||
|
|
@ -73,8 +73,8 @@ impl<'de> Deserialize<'de> for Condition {
|
||||||
|
|
||||||
impl Condition {
|
impl Condition {
|
||||||
fn build(expr: &str) -> Self {
|
fn build(expr: &str) -> Self {
|
||||||
let mut stack: Vec<ConditionOp> = Vec::new();
|
let mut stack: Vec<ConditionOp> = vec![];
|
||||||
let mut output: Vec<ConditionOp> = Vec::new();
|
let mut output: Vec<ConditionOp> = vec![];
|
||||||
|
|
||||||
let mut chars = expr.chars().peekable();
|
let mut chars = expr.chars().peekable();
|
||||||
while let Some(token) = chars.next() {
|
while let Some(token) = chars.next() {
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ impl FilesOp {
|
||||||
|
|
||||||
pub fn prepare(url: &Url) -> u64 {
|
pub fn prepare(url: &Url) -> u64 {
|
||||||
let ticket = FILES_TICKET.fetch_add(1, Ordering::Relaxed);
|
let ticket = FILES_TICKET.fetch_add(1, Ordering::Relaxed);
|
||||||
Self::Part(url.clone(), Vec::new(), ticket).emit();
|
Self::Part(url.clone(), vec![], ticket).emit();
|
||||||
ticket
|
ticket
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue