From ae75fc0f7b8999aa48e0b3686941ddc0c4fa065a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Tue, 19 Aug 2025 15:17:32 +0800 Subject: [PATCH] feat: make mimetype matching more robust (#3082) --- .luarc.json | 5 +++ .styluaignore | 2 -- Cargo.lock | 4 +-- Cargo.toml | 2 +- scripts/validate-form/main.js | 33 ++++++++++++++++++ stylua.toml | 1 + yazi-actor/src/mgr/reveal.rs | 4 +-- yazi-codegen/src/lib.rs | 40 +--------------------- yazi-config/preset/keymap-default.toml | 26 +++++++------- yazi-plugin/preset/plugins/archive.lua | 47 ++++++++++++-------------- yazi-plugin/preset/plugins/mime.lua | 28 ++++++++------- 11 files changed, 95 insertions(+), 97 deletions(-) create mode 100644 .luarc.json delete mode 100644 .styluaignore diff --git a/.luarc.json b/.luarc.json new file mode 100644 index 00000000..48273829 --- /dev/null +++ b/.luarc.json @@ -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/"] +} diff --git a/.styluaignore b/.styluaignore deleted file mode 100644 index d8e2f141..00000000 --- a/.styluaignore +++ /dev/null @@ -1,2 +0,0 @@ -# this file caused some issues with the build system -yazi-plugin/preset/plugins/mime.lua diff --git a/Cargo.lock b/Cargo.lock index 45866155..80a7d4e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2327,9 +2327,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.142" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ "itoa", "memchr", diff --git a/Cargo.toml b/Cargo.toml index 76fe1eb7..f0e12f35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,7 @@ ratatui = { version = "0.29.0", features = [ "unstable-rendered-line regex = "1.11.1" scopeguard = "1.2.0" 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" ] } tokio = { version = "1.47.1", features = [ "full" ] } tokio-stream = "0.1.17" diff --git a/scripts/validate-form/main.js b/scripts/validate-form/main.js index fe10c66b..0f616a2e 100644 --- a/scripts/validate-form/main.js +++ b/scripts/validate-form/main.js @@ -103,12 +103,14 @@ module.exports = async ({ github, context, core }) => { issue_number: id, name : LABEL_NAME, }) + await hideOldComments(id) } else if (mark && !marked && !await removedLabelManually(id)) { await github.rest.issues.addLabels({ ...context.repo, issue_number: id, labels : [LABEL_NAME], }) + await hideOldComments(id) await github.rest.issues.createComment({ ...context.repo, 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() { try { const { data: issues } = await github.rest.issues.listForRepo({ diff --git a/stylua.toml b/stylua.toml index ffaa2031..fc19abf7 100644 --- a/stylua.toml +++ b/stylua.toml @@ -1,3 +1,4 @@ +syntax = "Lua54" indent_width = 2 call_parentheses = "NoSingleTable" collapse_simple_statement = "FunctionOnly" diff --git a/yazi-actor/src/mgr/reveal.rs b/yazi-actor/src/mgr/reveal.rs index 820e7506..e525f653 100644 --- a/yazi-actor/src/mgr/reveal.rs +++ b/yazi-actor/src/mgr/reveal.rs @@ -1,6 +1,6 @@ use anyhow::Result; use yazi_fs::{File, FilesOp}; -use yazi_macro::{act, succ}; +use yazi_macro::{act, render, succ}; use yazi_parser::mgr::RevealOpt; use yazi_shared::event::Data; @@ -21,7 +21,7 @@ impl Actor for Reveal { // Try to hover on the child file 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, // create a dummy file diff --git a/yazi-codegen/src/lib.rs b/yazi-codegen/src/lib.rs index bd93d1dd..02bcf65c 100644 --- a/yazi-codegen/src/lib.rs +++ b/yazi-codegen/src/lib.rs @@ -1,44 +1,6 @@ use proc_macro::TokenStream; use quote::{format_ident, quote}; -use syn::{Attribute, Data, DeriveInput, Fields, FnArg, ItemFn, ext::IdentExt, 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` - 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() -} +use syn::{Attribute, Data, DeriveInput, Fields, parse_macro_input}; #[proc_macro_derive(DeserializeOver1)] pub fn deserialize_over1(input: TokenStream) -> TokenStream { diff --git a/yazi-config/preset/keymap-default.toml b/yazi-config/preset/keymap-default.toml index c02666de..dee02f00 100644 --- a/yazi-config/preset/keymap-default.toml +++ b/yazi-config/preset/keymap-default.toml @@ -109,19 +109,19 @@ keymap = [ { on = "N", run = "find_arrow --previous", desc = "Previous found" }, # Sorting - { 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 = [ ",", "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 = [ ",", "e" ], run = "sort extension --reverse=no", desc = "Sort by extension" }, - { on = [ ",", "E" ], run = "sort extension --reverse", desc = "Sort by extension (reverse)" }, - { on = [ ",", "a" ], run = "sort alphabetical --reverse=no", desc = "Sort alphabetically" }, - { on = [ ",", "A" ], run = "sort alphabetical --reverse", desc = "Sort alphabetically (reverse)" }, - { on = [ ",", "n" ], run = "sort natural --reverse=no", desc = "Sort naturally" }, - { on = [ ",", "N" ], run = "sort natural --reverse", desc = "Sort naturally (reverse)" }, - { 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 = [ ",", "r" ], run = "sort random --reverse=no", desc = "Sort randomly" }, + { on = [ ",", "m" ], run = [ "sort mtime --reverse=no", "linemode mtime" ], desc = "Sort by modified time" }, + { 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=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=yes", desc = "Sort by extension (reverse)" }, + { on = [ ",", "a" ], run = "sort alphabetical --reverse=no", desc = "Sort alphabetically" }, + { 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=yes", desc = "Sort naturally (reverse)" }, + { on = [ ",", "s" ], run = [ "sort size --reverse=no", "linemode size" ], desc = "Sort by size" }, + { on = [ ",", "S" ], run = [ "sort size --reverse=yes", "linemode size" ], desc = "Sort by size (reverse)" }, + { on = [ ",", "r" ], run = "sort random --reverse=no", desc = "Sort randomly" }, # Goto { on = [ "g", "h" ], run = "cd ~", desc = "Go home" }, diff --git a/yazi-plugin/preset/plugins/archive.lua b/yazi-plugin/preset/plugins/archive.lua index 231d41ff..868c2d9e 100644 --- a/yazi-plugin/preset/plugins/archive.lua +++ b/yazi-plugin/preset/plugins/archive.lua @@ -2,14 +2,14 @@ local M = {} function M:peek(job) 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 code ~= 0 then - return require("empty").msg( - job, - code == 2 and "File list in this archive is encrypted" - or "Failed to start either `7zz` or `7z`. Do you have 7-zip installed?" - ) + if err then + return ya.preview_widget(job, err) + elseif job.skip > 0 and bound < job.skip + limit then + return ya.emit("peek", { math.max(0, bound - limit), only_if = job.file.url, upper_bound = true }) + elseif #files == 0 then + files = { { path = job.file.url.stem, size = 0, attr = "" } } end local left, right = {}, {} @@ -40,14 +40,10 @@ function M:peek(job) } end - if job.skip > 0 and bound < job.skip + limit then - ya.emit("peek", { math.max(0, bound - limit), only_if = job.file.url, upper_bound = true }) - else - ya.preview_widget(job, { - ui.Text(left):area(job.area), - ui.Text(right):area(job.area):align(ui.Align.RIGHT), - }) - end + ya.preview_widget(job, { + ui.Text(left):area(job.area), + ui.Text(right):area(job.area):align(ui.Align.RIGHT), + }) end function M:seek(job) require("code"):seek(job) end @@ -76,26 +72,22 @@ end ---@param limit integer ---@return table files ---@return integer bound ----@return integer code ---- 0: success ---- 1: failed to spawn ---- 2: wrong password ---- 3: partial success +---@return Error? err function M.list_files(args, skip, limit) local child = M.spawn_7z { "l", "-ba", "-slt", "-sccUTF-8", table.unpack(args) } if not child then - return {}, 0, 1 + return {}, 0, Err("Failed to start either `7zz` or `7z`. Do you have 7-zip installed?") end - local i, files, code = 0, { { path = "", size = 0, attr = "" } }, 0 - local key, value = "", "" + local i, files, err = 0, { { path = "", size = 0, attr = "" } }, nil + local key, value, stderr = "", "", {} repeat local next, event = child:read_line() if event == 1 and M.is_encrypted(next) then - code = 2 + err = Err("File list in this archive is encrypted") break elseif event == 1 then - code = 3 + stderr[#stderr + 1] = next goto continue elseif event ~= 0 then break @@ -127,7 +119,10 @@ function M.list_files(args, skip, limit) if files[#files].path == "" then files[#files] = nil 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 ---List metadata of an archive diff --git a/yazi-plugin/preset/plugins/mime.lua b/yazi-plugin/preset/plugins/mime.lua index 1ba263a9..648fade6 100644 --- a/yazi-plugin/preset/plugins/mime.lua +++ b/yazi-plugin/preset/plugins/mime.lua @@ -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 function match_mimetype(s) - local type, sub = s:match("^([-a-z]+/)([+-.a-zA-Z0-9]+)%s*$") - if type and sub and SUPPORTED_TYPES:find(type, 1, true) then - return type:gsub("^x%-", "", 1) .. sub:gsub("^x%-", "", 1):gsub("^vnd%.", "", 1) + for _, pat in ipairs(TYPE_PATS) do + local typ, sub = s:match(string.format("(%s/)([+-.a-z0-9]+)%%s+$", pat)) + 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 @@ -32,7 +38,7 @@ function M:fetch(job) end end - local i, valid, state = 1, nil, {} + local i, state, match, ignore = 1, {}, nil, nil repeat local line, event = child:read_line_with { timeout = 300 } if event == 3 then @@ -42,15 +48,13 @@ function M:fetch(job) break end - valid = match_mimetype(line) - if valid then - updates[urls[i]], state[i] = valid, true + match, ignore = match_mimetype(line) + if match then + updates[urls[i]], state[i], i = match, true, i + 1 flush(false) - else - state[i] = false + elseif not ignore then + state[i], i = false, i + 1 end - - i = i + 1 ::continue:: until i > #urls