feat: dedicated mime fetcher, previewer, and spotter for VFS (#3203)

This commit is contained in:
三咲雅 misaki masa 2025-09-25 21:18:52 +08:00 committed by sxyazi
parent 6b09f2b665
commit d905cbf2d7
No known key found for this signature in database
12 changed files with 83 additions and 51 deletions

View file

@ -45,8 +45,9 @@ jobs:
if: matrix.gcc != ''
run: sudo apt update && sudo apt install -yq ${{ matrix.gcc }}
- name: Setup Rust toolchain
run: rustup toolchain install stable --profile minimal --target ${{ matrix.target }}
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Setup sccache
uses: mozilla-actions/sccache-action@v0.0.9
@ -79,8 +80,9 @@ jobs:
steps:
- uses: actions/checkout@v5
- name: Setup Rust toolchain
run: rustup toolchain install stable --profile minimal --target ${{ matrix.target }}
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Setup sccache
uses: mozilla-actions/sccache-action@v0.0.9

View file

@ -16,7 +16,9 @@ pub(super) struct File {
v_cha: Option<Value>,
v_url: Option<Value>,
v_link_to: Option<Value>,
v_name: Option<Value>,
v_name: Option<Value>,
v_cache: Option<Value>,
}
impl Deref for File {
@ -48,7 +50,9 @@ impl File {
v_cha: None,
v_url: None,
v_link_to: None,
v_name: None,
v_cache: None,
})?;
ve.insert(ud.clone());
ud

View file

@ -12,7 +12,9 @@ pub struct File {
v_cha: Option<Value>,
v_url: Option<Value>,
v_link_to: Option<Value>,
v_name: Option<Value>,
v_name: Option<Value>,
v_cache: Option<Value>,
}
impl Deref for File {
@ -27,7 +29,7 @@ impl From<File> for yazi_fs::File {
impl File {
pub fn new(inner: yazi_fs::File) -> Self {
Self { inner, v_cha: None, v_url: None, v_link_to: None, v_name: None }
Self { inner, v_cha: None, v_url: None, v_link_to: None, v_name: None, v_cache: None }
}
}

View file

@ -156,6 +156,9 @@ macro_rules! impl_file_fields {
$crate::cached_field!($fields, name, |lua, me| {
me.name().map(|s| lua.create_string(s.as_encoded_bytes())).transpose()
});
$crate::cached_field!($fields, cache, |_, me| Ok(
yazi_fs::provider::cache(&me.url).map($crate::Url::new)
));
};
}

View file

@ -223,31 +223,23 @@ footer = { fg = "black", bg = "white" }
[filetype]
rules = [
# Images
# Image
{ mime = "image/*", fg = "yellow" },
# Media
{ mime = "{audio,video}/*", fg = "magenta" },
# Archives
# Archive
{ mime = "application/{zip,rar,7z*,tar,gzip,xz,zstd,bzip*,lzma,compress,archive,cpio,arj,xar,ms-cab*}", fg = "red" },
# Documents
# Document
{ mime = "application/{pdf,doc,rtf}", fg = "cyan" },
# Empty files
# { mime = "inode/empty", fg = "red" },
# Special files
# Virtual file system
{ mime = "vfs/*", fg = "gray" },
# Special file
{ url = "*", is = "orphan", bg = "red" },
{ url = "*", is = "exec" , fg = "green" },
# Dummy files
# Dummy file
{ url = "*", is = "dummy", bg = "red" },
{ url = "*/", is = "dummy", bg = "red" },
# Fallback
# { url = "*", fg = "white" },
{ url = "*/", fg = "blue" }
]

View file

@ -223,31 +223,23 @@ footer = { fg = "black", bg = "white" }
[filetype]
rules = [
# Images
# Image
{ mime = "image/*", fg = "yellow" },
# Media
{ mime = "{audio,video}/*", fg = "magenta" },
# Archives
# Archive
{ mime = "application/{zip,rar,7z*,tar,gzip,xz,zstd,bzip*,lzma,compress,archive,cpio,arj,xar,ms-cab*}", fg = "red" },
# Documents
# Document
{ mime = "application/{pdf,doc,rtf}", fg = "cyan" },
# Empty files
# { mime = "inode/empty", fg = "red" },
# Special files
# Virtual file system
{ mime = "vfs/*", fg = "darkgray" },
# Special file
{ url = "*", is = "orphan", bg = "red" },
{ url = "*", is = "exec" , fg = "green" },
# Dummy files
# Dummy file
{ url = "*", is = "dummy", bg = "red" },
{ url = "*/", is = "dummy", bg = "red" },
# Fallback
# { url = "*", fg = "white" },
{ url = "*/", fg = "blue" }
]

View file

@ -105,6 +105,8 @@ spotters = [
{ mime = "image/*", run = "image" },
# Video
{ mime = "video/*", run = "video" },
# Virtual file system
{ mime = "vfs/*", run = "vfs" },
# Fallback
{ url = "*", run = "file" },
]
@ -149,6 +151,8 @@ previewers = [
{ mime = "application/ms-opentype", run = "font" },
# Empty file
{ mime = "inode/empty", run = "empty" },
# Virtual file system
{ mime = "vfs/*", run = "vfs" },
# Fallback
{ url = "*", run = "file" },
]

View file

@ -43,8 +43,9 @@ impl Term {
)?;
let resp = Emulator::read_until_da1();
yazi_term::RestoreCursor::store(&resp);
Mux::tmux_drain()?;
yazi_term::RestoreCursor::store(&resp);
CSI_U.store(resp.contains("\x1b[?0u"), Ordering::Relaxed);
if CSI_U.load(Ordering::Relaxed) {
PushKeyboardEnhancementFlags(
@ -61,7 +62,6 @@ impl Term {
term.hide_cursor()?;
term.clear()?;
term.flush()?;
Mux::tmux_drain()?;
Ok(term)
}

View file

@ -56,10 +56,12 @@ impl FilesOp {
for (o, n) in map {
let Some(o_p) = o.parent() else { continue };
let Some(n_p) = n.url.parent() else { continue };
if o_p != n_p {
if o_p == n_p {
parents.entry_ref(&o_p).or_default().1.insert(o.urn().to_owned(), n);
} else {
parents.entry_ref(&o_p).or_default().0.insert(o.urn().to_owned());
parents.entry_ref(&n_p).or_default().1.insert(n.urn().to_owned(), n);
}
parents.entry_ref(&n_p).or_default().1.insert(n.urn().to_owned(), n);
}
for (p, (o, n)) in parents {
match (o.is_empty(), n.is_empty()) {

View file

@ -3,11 +3,11 @@ local TYPE_PATS = { "text", "image", "video", "application", "audio", "font", "i
local M = {}
local function match_mimetype(s)
local function match_mimetype(line)
for _, pat in ipairs(TYPE_PATS) do
local typ, sub = s:match(string.format("(%s/)([+-.a-z0-9]+)%%s+$", pat))
local typ, sub = line:match(string.format("(%s/)([+-.a-z0-9]+)%%s+$", pat))
if not sub then
elseif s:find(typ .. sub, 1, true) == 1 then
elseif line:find(typ .. sub, 1, true) == 1 then
return typ:gsub("^x%-", "", 1) .. sub:gsub("^x%-", "", 1):gsub("^vnd%.", "", 1)
else
return nil, true
@ -15,14 +15,27 @@ local function match_mimetype(s)
end
end
local function miss_cache(cache, line)
if line:match("^cannot open `.+' %(No such file or directory%)%s+$") then
return true
else
local _, err = fs.cha(Url(cache))
return err and err.code == 2
end
end
function M:fetch(job)
local urls = {}
for _, file in ipairs(job.files) do
urls[#urls + 1] = tostring(file.url)
local paths, origins = {}, {}
for i, file in ipairs(job.files) do
if file.cache then
paths[i], origins[i] = tostring(file.cache), tostring(file.url)
else
paths[i] = tostring(file.url)
end
end
local cmd = os.getenv("YAZI_FILE_ONE") or "file"
local child, err = Command(cmd):arg({ "-bL", "--mime-type", "--" }):arg(urls):stdout(Command.PIPED):spawn()
local child, err = Command(cmd):arg({ "-bL", "--mime-type", "--" }):arg(paths):stdout(Command.PIPED):spawn()
if not child then
return true, Err("Failed to start `%s`, error: %s", cmd, err)
end
@ -50,13 +63,18 @@ function M:fetch(job)
match, ignore = match_mimetype(line)
if match then
updates[urls[i]], state[i], i = match, true, i + 1
updates[origins[i] or paths[i]], state[i], i = match, true, i + 1
flush(false)
elseif not ignore then
elseif ignore then
goto continue
elseif origins[i] and miss_cache(paths[i], line) then
updates[origins[i]], state[i], i = "vfs/todo", true, i + 1
flush(false)
else
state[i], i = false, i + 1
end
::continue::
until i > #urls
until i > #paths
flush(true)
return state

View file

@ -0,0 +1,12 @@
local M = {}
function M:peek(job)
local line = ui.Line("Remote file, download to preview"):reverse()
ya.preview_widget(job, ui.Text(line):area(job.area):wrap(ui.Wrap.YES))
end
function M:seek() end
function M:spot(job) require("file"):spot(job) end
return M

View file

@ -43,6 +43,7 @@ impl Default for Loader {
("pdf".to_owned(), preset!("plugins/pdf").into()),
("session".to_owned(), preset!("plugins/session").into()),
("svg".to_owned(), preset!("plugins/svg").into()),
("vfs".to_owned(), preset!("plugins/vfs").into()),
("video".to_owned(), preset!("plugins/video").into()),
("zoxide".to_owned(), preset!("plugins/zoxide").into()),
]);