feat: make mimetype matching more robust (#3082)

This commit is contained in:
三咲雅 misaki masa 2025-08-19 15:17:32 +08:00 committed by GitHub
parent fe9ce240b5
commit ae75fc0f7b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 95 additions and 97 deletions

5
.luarc.json Normal file
View file

@ -0,0 +1,5 @@
{
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
"runtime.version": "Lua 5.4",
"workspace.library": ["~/.config/yazi/plugins/types.yazi/"]
}

View file

@ -1,2 +0,0 @@
# this file caused some issues with the build system
yazi-plugin/preset/plugins/mime.lua

4
Cargo.lock generated
View file

@ -2327,9 +2327,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.142" version = "1.0.143"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",

View file

@ -44,7 +44,7 @@ ratatui = { version = "0.29.0", features = [ "unstable-rendered-line
regex = "1.11.1" regex = "1.11.1"
scopeguard = "1.2.0" scopeguard = "1.2.0"
serde = { version = "1.0.219", features = [ "derive" ] } serde = { version = "1.0.219", features = [ "derive" ] }
serde_json = "1.0.142" serde_json = "1.0.143"
syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] }
tokio = { version = "1.47.1", features = [ "full" ] } tokio = { version = "1.47.1", features = [ "full" ] }
tokio-stream = "0.1.17" tokio-stream = "0.1.17"

View file

@ -103,12 +103,14 @@ module.exports = async ({ github, context, core }) => {
issue_number: id, issue_number: id,
name : LABEL_NAME, name : LABEL_NAME,
}) })
await hideOldComments(id)
} else if (mark && !marked && !await removedLabelManually(id)) { } else if (mark && !marked && !await removedLabelManually(id)) {
await github.rest.issues.addLabels({ await github.rest.issues.addLabels({
...context.repo, ...context.repo,
issue_number: id, issue_number: id,
labels : [LABEL_NAME], labels : [LABEL_NAME],
}) })
await hideOldComments(id)
await github.rest.issues.createComment({ await github.rest.issues.createComment({
...context.repo, ...context.repo,
issue_number: id, issue_number: id,
@ -120,6 +122,37 @@ module.exports = async ({ github, context, core }) => {
} }
} }
async function hideOldComments(id) {
try {
const comments = await github.paginate(github.rest.issues.listComments, {
...context.repo,
issue_number: id,
per_page : 100,
})
for (const c of comments) {
const byBot = c.user?.login?.endsWith("[bot]") || c.user?.type === "Bot"
const contains = c?.body?.includes("or closed after 2 days of inactivity")
if (!byBot || !contains || !c.node_id) continue
try {
await github.graphql(
`mutation($subjectId: ID!, $classifier: ReportedContentClassifiers!) {
minimizeComment(input: {subjectId: $subjectId, classifier: $classifier}) {
minimizedComment { isMinimized }
}
}`,
{ subjectId: c.node_id, classifier: "OUTDATED" },
)
} catch (e) {
core.error(`Error minimizing comment ${c.id}: ${e.message}`)
}
}
} catch (e) {
core.error(`Error listing comments: ${e.message}`)
}
}
async function closeOldIssues() { async function closeOldIssues() {
try { try {
const { data: issues } = await github.rest.issues.listForRepo({ const { data: issues } = await github.rest.issues.listForRepo({

View file

@ -1,3 +1,4 @@
syntax = "Lua54"
indent_width = 2 indent_width = 2
call_parentheses = "NoSingleTable" call_parentheses = "NoSingleTable"
collapse_simple_statement = "FunctionOnly" collapse_simple_statement = "FunctionOnly"

View file

@ -1,6 +1,6 @@
use anyhow::Result; use anyhow::Result;
use yazi_fs::{File, FilesOp}; use yazi_fs::{File, FilesOp};
use yazi_macro::{act, succ}; use yazi_macro::{act, render, succ};
use yazi_parser::mgr::RevealOpt; use yazi_parser::mgr::RevealOpt;
use yazi_shared::event::Data; use yazi_shared::event::Data;
@ -21,7 +21,7 @@ impl Actor for Reveal {
// Try to hover on the child file // Try to hover on the child file
let tab = cx.tab_mut(); let tab = cx.tab_mut();
tab.current.hover(child.as_urn()); render!(tab.current.hover(child.as_urn()));
// If the child is not hovered, which means it doesn't exist, // If the child is not hovered, which means it doesn't exist,
// create a dummy file // create a dummy file

View file

@ -1,44 +1,6 @@
use proc_macro::TokenStream; use proc_macro::TokenStream;
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use syn::{Attribute, Data, DeriveInput, Fields, FnArg, ItemFn, ext::IdentExt, parse_macro_input}; use syn::{Attribute, Data, DeriveInput, Fields, parse_macro_input};
#[proc_macro_attribute]
pub fn command(_: TokenStream, item: TokenStream) -> TokenStream {
let mut f: ItemFn = syn::parse(item).unwrap();
let mut ins = f.sig.inputs.clone();
// Turn `opt: Opt` into `opt: impl Into<Opt>`
ins[1] = {
let FnArg::Typed(opt) = &f.sig.inputs[1] else {
panic!("Cannot find the `opt` argument in the function signature.");
};
let opt_ty = &opt.ty;
syn::parse2(quote! { opt: impl Into<#opt_ty> }).unwrap()
};
// Make the original function private and add a public wrapper
assert!(matches!(f.vis, syn::Visibility::Public(_)));
f.vis = syn::Visibility::Inherited;
// Add `__` prefix to the original function name
let name_ori = f.sig.ident;
f.sig.ident = format_ident!("__{}", name_ori.unraw());
let name_new = &f.sig.ident;
// Collect the rest of the arguments
let rest_args = ins.iter().skip(2).map(|arg| match arg {
FnArg::Receiver(_) => unreachable!(),
FnArg::Typed(t) => &t.pat,
});
quote! {
#[inline]
pub fn #name_ori(#ins) { self.#name_new(opt.into(), #(#rest_args),*); }
#f
}
.into()
}
#[proc_macro_derive(DeserializeOver1)] #[proc_macro_derive(DeserializeOver1)]
pub fn deserialize_over1(input: TokenStream) -> TokenStream { pub fn deserialize_over1(input: TokenStream) -> TokenStream {

View file

@ -109,19 +109,19 @@ keymap = [
{ on = "N", run = "find_arrow --previous", desc = "Previous found" }, { on = "N", run = "find_arrow --previous", desc = "Previous found" },
# Sorting # Sorting
{ on = [ ",", "m" ], run = [ "sort mtime --reverse=no", "linemode mtime" ], desc = "Sort by modified time" }, { on = [ ",", "m" ], run = [ "sort mtime --reverse=no", "linemode mtime" ], desc = "Sort by modified time" },
{ on = [ ",", "M" ], run = [ "sort mtime --reverse", "linemode mtime" ], desc = "Sort by modified time (reverse)" }, { on = [ ",", "M" ], run = [ "sort mtime --reverse=yes", "linemode mtime" ], desc = "Sort by modified time (reverse)" },
{ on = [ ",", "b" ], run = [ "sort btime --reverse=no", "linemode btime" ], desc = "Sort by birth time" }, { on = [ ",", "b" ], run = [ "sort btime --reverse=no", "linemode btime" ], desc = "Sort by birth time" },
{ on = [ ",", "B" ], run = [ "sort btime --reverse", "linemode btime" ], desc = "Sort by birth time (reverse)" }, { on = [ ",", "B" ], run = [ "sort btime --reverse=yes", "linemode btime" ], desc = "Sort by birth time (reverse)" },
{ on = [ ",", "e" ], run = "sort extension --reverse=no", desc = "Sort by extension" }, { on = [ ",", "e" ], run = "sort extension --reverse=no", desc = "Sort by extension" },
{ on = [ ",", "E" ], run = "sort extension --reverse", desc = "Sort by extension (reverse)" }, { on = [ ",", "E" ], run = "sort extension --reverse=yes", desc = "Sort by extension (reverse)" },
{ on = [ ",", "a" ], run = "sort alphabetical --reverse=no", desc = "Sort alphabetically" }, { on = [ ",", "a" ], run = "sort alphabetical --reverse=no", desc = "Sort alphabetically" },
{ on = [ ",", "A" ], run = "sort alphabetical --reverse", desc = "Sort alphabetically (reverse)" }, { on = [ ",", "A" ], run = "sort alphabetical --reverse=yes", desc = "Sort alphabetically (reverse)" },
{ on = [ ",", "n" ], run = "sort natural --reverse=no", desc = "Sort naturally" }, { on = [ ",", "n" ], run = "sort natural --reverse=no", desc = "Sort naturally" },
{ on = [ ",", "N" ], run = "sort natural --reverse", desc = "Sort naturally (reverse)" }, { on = [ ",", "N" ], run = "sort natural --reverse=yes", desc = "Sort naturally (reverse)" },
{ on = [ ",", "s" ], run = [ "sort size --reverse=no", "linemode size" ], desc = "Sort by size" }, { on = [ ",", "s" ], run = [ "sort size --reverse=no", "linemode size" ], desc = "Sort by size" },
{ on = [ ",", "S" ], run = [ "sort size --reverse", "linemode size" ], desc = "Sort by size (reverse)" }, { on = [ ",", "S" ], run = [ "sort size --reverse=yes", "linemode size" ], desc = "Sort by size (reverse)" },
{ on = [ ",", "r" ], run = "sort random --reverse=no", desc = "Sort randomly" }, { on = [ ",", "r" ], run = "sort random --reverse=no", desc = "Sort randomly" },
# Goto # Goto
{ on = [ "g", "h" ], run = "cd ~", desc = "Go home" }, { on = [ "g", "h" ], run = "cd ~", desc = "Go home" },

View file

@ -2,14 +2,14 @@ local M = {}
function M:peek(job) function M:peek(job)
local limit = job.area.h local limit = job.area.h
local files, bound, err = self.list_files({ "-p", tostring(job.file.url) }, job.skip, limit)
local files, bound, code = self.list_files({ "-p", tostring(job.file.url) }, job.skip, limit) if err then
if code ~= 0 then return ya.preview_widget(job, err)
return require("empty").msg( elseif job.skip > 0 and bound < job.skip + limit then
job, return ya.emit("peek", { math.max(0, bound - limit), only_if = job.file.url, upper_bound = true })
code == 2 and "File list in this archive is encrypted" elseif #files == 0 then
or "Failed to start either `7zz` or `7z`. Do you have 7-zip installed?" files = { { path = job.file.url.stem, size = 0, attr = "" } }
)
end end
local left, right = {}, {} local left, right = {}, {}
@ -40,14 +40,10 @@ function M:peek(job)
} }
end end
if job.skip > 0 and bound < job.skip + limit then ya.preview_widget(job, {
ya.emit("peek", { math.max(0, bound - limit), only_if = job.file.url, upper_bound = true }) ui.Text(left):area(job.area),
else ui.Text(right):area(job.area):align(ui.Align.RIGHT),
ya.preview_widget(job, { })
ui.Text(left):area(job.area),
ui.Text(right):area(job.area):align(ui.Align.RIGHT),
})
end
end end
function M:seek(job) require("code"):seek(job) end function M:seek(job) require("code"):seek(job) end
@ -76,26 +72,22 @@ end
---@param limit integer ---@param limit integer
---@return table files ---@return table files
---@return integer bound ---@return integer bound
---@return integer code ---@return Error? err
--- 0: success
--- 1: failed to spawn
--- 2: wrong password
--- 3: partial success
function M.list_files(args, skip, limit) function M.list_files(args, skip, limit)
local child = M.spawn_7z { "l", "-ba", "-slt", "-sccUTF-8", table.unpack(args) } local child = M.spawn_7z { "l", "-ba", "-slt", "-sccUTF-8", table.unpack(args) }
if not child then if not child then
return {}, 0, 1 return {}, 0, Err("Failed to start either `7zz` or `7z`. Do you have 7-zip installed?")
end end
local i, files, code = 0, { { path = "", size = 0, attr = "" } }, 0 local i, files, err = 0, { { path = "", size = 0, attr = "" } }, nil
local key, value = "", "" local key, value, stderr = "", "", {}
repeat repeat
local next, event = child:read_line() local next, event = child:read_line()
if event == 1 and M.is_encrypted(next) then if event == 1 and M.is_encrypted(next) then
code = 2 err = Err("File list in this archive is encrypted")
break break
elseif event == 1 then elseif event == 1 then
code = 3 stderr[#stderr + 1] = next
goto continue goto continue
elseif event ~= 0 then elseif event ~= 0 then
break break
@ -127,7 +119,10 @@ function M.list_files(args, skip, limit)
if files[#files].path == "" then if files[#files].path == "" then
files[#files] = nil files[#files] = nil
end end
return files, i, code if #stderr ~= 0 then
err = Err("7-zip errored out while listing files, stderr: %s", table.concat(stderr, "\n"))
end
return files, i, err
end end
---List metadata of an archive ---List metadata of an archive

View file

@ -1,11 +1,17 @@
local SUPPORTED_TYPES = "application/audio/biosig/chemical/font/image/inode/message/model/rinex/text/vector/video/x-epoc/" -- stylua: ignore
local TYPE_PATS = { "text", "image", "video", "application", "audio", "font", "inode", "message", "model", "vector", "biosig", "chemical", "rinex", "x%-epoc" }
local M = {} local M = {}
local function match_mimetype(s) local function match_mimetype(s)
local type, sub = s:match("^([-a-z]+/)([+-.a-zA-Z0-9]+)%s*$") for _, pat in ipairs(TYPE_PATS) do
if type and sub and SUPPORTED_TYPES:find(type, 1, true) then local typ, sub = s:match(string.format("(%s/)([+-.a-z0-9]+)%%s+$", pat))
return type:gsub("^x%-", "", 1) .. sub:gsub("^x%-", "", 1):gsub("^vnd%.", "", 1) if not sub then
elseif s:find(typ .. sub, 1, true) == 1 then
return typ:gsub("^x%-", "", 1) .. sub:gsub("^x%-", "", 1):gsub("^vnd%.", "", 1)
else
return nil, true
end
end end
end end
@ -32,7 +38,7 @@ function M:fetch(job)
end end
end end
local i, valid, state = 1, nil, {} local i, state, match, ignore = 1, {}, nil, nil
repeat repeat
local line, event = child:read_line_with { timeout = 300 } local line, event = child:read_line_with { timeout = 300 }
if event == 3 then if event == 3 then
@ -42,15 +48,13 @@ function M:fetch(job)
break break
end end
valid = match_mimetype(line) match, ignore = match_mimetype(line)
if valid then if match then
updates[urls[i]], state[i] = valid, true updates[urls[i]], state[i], i = match, true, i + 1
flush(false) flush(false)
else elseif not ignore then
state[i] = false state[i], i = false, i + 1
end end
i = i + 1
::continue:: ::continue::
until i > #urls until i > #urls