diff --git a/.github/workflows/draft.yml b/.github/workflows/draft.yml index 1b74fe10..faf4eea7 100644 --- a/.github/workflows/draft.yml +++ b/.github/workflows/draft.yml @@ -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 diff --git a/yazi-actor/src/lives/file.rs b/yazi-actor/src/lives/file.rs index 4ce1858e..ab731a0d 100644 --- a/yazi-actor/src/lives/file.rs +++ b/yazi-actor/src/lives/file.rs @@ -16,7 +16,9 @@ pub(super) struct File { v_cha: Option, v_url: Option, v_link_to: Option, - v_name: Option, + + v_name: Option, + v_cache: Option, } 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 diff --git a/yazi-binding/src/file.rs b/yazi-binding/src/file.rs index b692ef31..9ef1b7a8 100644 --- a/yazi-binding/src/file.rs +++ b/yazi-binding/src/file.rs @@ -12,7 +12,9 @@ pub struct File { v_cha: Option, v_url: Option, v_link_to: Option, - v_name: Option, + + v_name: Option, + v_cache: Option, } impl Deref for File { @@ -27,7 +29,7 @@ impl From 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 } } } diff --git a/yazi-binding/src/macros.rs b/yazi-binding/src/macros.rs index 509462cc..84f43711 100644 --- a/yazi-binding/src/macros.rs +++ b/yazi-binding/src/macros.rs @@ -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) + )); }; } diff --git a/yazi-config/preset/theme-dark.toml b/yazi-config/preset/theme-dark.toml index e33e8ddd..ffff1428 100644 --- a/yazi-config/preset/theme-dark.toml +++ b/yazi-config/preset/theme-dark.toml @@ -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" } ] diff --git a/yazi-config/preset/theme-light.toml b/yazi-config/preset/theme-light.toml index d278acf3..0ea9e200 100644 --- a/yazi-config/preset/theme-light.toml +++ b/yazi-config/preset/theme-light.toml @@ -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" } ] diff --git a/yazi-config/preset/yazi-default.toml b/yazi-config/preset/yazi-default.toml index 25add0a8..fe16a463 100644 --- a/yazi-config/preset/yazi-default.toml +++ b/yazi-config/preset/yazi-default.toml @@ -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" }, ] diff --git a/yazi-fm/src/term.rs b/yazi-fm/src/term.rs index f77da951..bed599d0 100644 --- a/yazi-fm/src/term.rs +++ b/yazi-fm/src/term.rs @@ -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) } diff --git a/yazi-fs/src/op.rs b/yazi-fs/src/op.rs index 018eeea3..079f6de2 100644 --- a/yazi-fs/src/op.rs +++ b/yazi-fs/src/op.rs @@ -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()) { diff --git a/yazi-plugin/preset/plugins/mime.lua b/yazi-plugin/preset/plugins/mime.lua index 648fade6..d2a3f960 100644 --- a/yazi-plugin/preset/plugins/mime.lua +++ b/yazi-plugin/preset/plugins/mime.lua @@ -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 diff --git a/yazi-plugin/preset/plugins/vfs.lua b/yazi-plugin/preset/plugins/vfs.lua new file mode 100644 index 00000000..f1e55bb2 --- /dev/null +++ b/yazi-plugin/preset/plugins/vfs.lua @@ -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 diff --git a/yazi-plugin/src/loader/loader.rs b/yazi-plugin/src/loader/loader.rs index 8fe1390e..9d5946e2 100644 --- a/yazi-plugin/src/loader/loader.rs +++ b/yazi-plugin/src/loader/loader.rs @@ -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()), ]);