feat: deduplicate empty directories in preset archive previewer (#3676)

This commit is contained in:
三咲雅 misaki masa 2026-02-11 11:07:23 +08:00 committed by GitHub
parent 30ec603441
commit a8a4b684a1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 77 additions and 30 deletions

View file

@ -237,7 +237,7 @@ end
---@return table files ---@return table files
---@return Error? err ---@return Error? err
function M.parse_7z_slt(child, skip, limit) function M.parse_7z_slt(child, skip, limit)
local files, parents, err = { M.make_file() }, {}, nil local files, tops, parents, err = { M.make_file() }, {}, {}, nil
local key, value, empty, stderr = "", "", Path.os(""), {} local key, value, empty, stderr = "", "", Path.os(""), {}
repeat repeat
local next, event = child:read_line() local next, event = child:read_line()
@ -253,7 +253,8 @@ function M.parse_7z_slt(child, skip, limit)
if next == "\n" or next == "\r\n" then if next == "\n" or next == "\r\n" then
if files[#files].path ~= empty then if files[#files].path ~= empty then
M.treelize(files, parents) M.treelize(files, tops, parents)
M.pop_dup_dir(files, parents, false)
files[#files + 1] = M.make_file() files[#files + 1] = M.make_file()
end end
goto continue goto continue
@ -273,12 +274,17 @@ function M.parse_7z_slt(child, skip, limit)
end end
::continue:: ::continue::
until #files > skip + limit until #files - 1 > skip + limit
if files[#files].path == empty then if files[#files].path == empty then
files[#files] = nil files[#files] = nil
else else
M.treelize(files, parents) M.treelize(files, tops, parents)
end
M.pop_dup_dir(files, parents, #files <= skip + limit)
if #files > skip + limit then
files[#files] = nil
end end
if #stderr ~= 0 then if #stderr ~= 0 then
@ -289,29 +295,50 @@ end
---Convert a flat list of files into a tree structure ---Convert a flat list of files into a tree structure
---@param files table ---@param files table
---@param parents Path[] ---@param tops Path[]
function M.treelize(files, parents) ---@param parents table<string, boolean>
function M.treelize(files, tops, parents)
local f = table.remove(files) local f = table.remove(files)
while #parents > 0 and not f.path:starts_with(parents[#parents]) do while #tops > 0 and not f.path:starts_with(tops[#tops]) do
parents[#parents] = nil tops[#tops] = nil
end end
local buf, it = {}, f.path.parent local buf, it = {}, f.path.parent
while it and it ~= parents[#parents] do while it and it ~= tops[#tops] do
buf[#buf + 1], it = it, it.parent buf[#buf + 1], it = it, it.parent
end end
for i = #buf, 1, -1 do for i = #buf, 1, -1 do
files[#files + 1] = M.make_file { path = buf[i], depth = #parents, is_dir = true } files[#files + 1] = M.make_file { path = buf[i], depth = #tops, is_dir = true }
parents[#parents + 1] = buf[i] tops[#tops + 1] = buf[i]
M.pop_dup_dir(files, parents, false)
end end
f.depth = #parents f.depth = #tops
f.is_dir = f.folder == "+" or f.attr:sub(1, 1) == "D" f.is_dir = f.folder == "+" or f.attr:sub(1, 1) == "D"
if not f.is_dir then if not f.is_dir then
files[#files + 1] = f files[#files + 1] = f
elseif f.path ~= parents[#parents] then elseif f.path ~= tops[#tops] then
files[#files + 1], parents[#parents + 1] = f, f.path files[#files + 1], tops[#tops + 1] = f, f.path
end
end
---@param files table
---@param parents table<string, boolean>
---@param eof boolean
function M.pop_dup_dir(files, parents, eof)
local n, i = #files, eof and #files or #files - 1
if not files[i] or not files[i].is_dir then
return
end
local p = tostring(files[i].path)
if not parents[p] then
parents[p] = true
elseif eof then
files[n] = nil
elseif not files[n].path:starts_with(files[i].path) then
files[i], files[n] = files[n], nil
end end
end end

View file

@ -1,20 +1,48 @@
use mlua::{Function, Lua, MultiValue}; use std::{any::TypeId, fmt::Write};
use mlua::{Function, Lua, MultiValue, Value};
use tracing::{debug, error}; use tracing::{debug, error};
use super::Utils; use super::Utils;
impl Utils { impl Utils {
pub(super) fn dbg(lua: &Lua) -> mlua::Result<Function> { pub(super) fn dbg(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|_, values: MultiValue| { lua.create_function(|_, values: MultiValue| Ok(debug!("{}", Self::format_all(values)?)))
let s = values.into_iter().map(|v| format!("{v:#?}")).collect::<Vec<_>>().join(" ");
Ok(debug!("{s}"))
})
} }
pub(super) fn err(lua: &Lua) -> mlua::Result<Function> { pub(super) fn err(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|_, values: MultiValue| { lua.create_function(|_, values: MultiValue| Ok(error!("{}", Self::format_all(values)?)))
let s = values.into_iter().map(|v| format!("{v:#?}")).collect::<Vec<_>>().join(" "); }
Ok(error!("{s}"))
fn format_all(values: MultiValue) -> anyhow::Result<String> {
let mut s = String::new();
for value in values {
if !s.is_empty() {
s.push(' ');
}
Utils::format_one(&mut s, value)?;
}
Ok(s)
}
fn format_one(buf: &mut String, value: Value) -> anyhow::Result<()> {
let Value::UserData(ud) = &value else {
return Ok(write!(buf, "{value:#?}")?);
};
let id = ud.type_id();
let ptr = ud.to_pointer();
Ok(match id {
Some(t) if t == TypeId::of::<yazi_binding::Url>() => {
write!(buf, "Url({ptr:?}): {:?}", **ud.borrow::<yazi_binding::Url>()?)?
}
Some(t) if t == TypeId::of::<yazi_binding::Path>() => {
write!(buf, "Path({ptr:?}): {:?}", **ud.borrow::<yazi_binding::Path>()?)?
}
Some(t) if t == TypeId::of::<yazi_binding::Id>() => {
write!(buf, "Id({ptr:?}): {}", **ud.borrow::<yazi_binding::Id>()?)?
}
_ => write!(buf, "{value:#?}")?,
}) })
} }
} }

View file

@ -164,14 +164,6 @@ where
} }
} }
#[inline]
pub fn to_inner(&self) -> P
where
P: Clone,
{
self.inner.clone()
}
#[inline] #[inline]
pub fn into_inner(self) -> P { self.inner } pub fn into_inner(self) -> P { self.inner }