-
Notifications
You must be signed in to change notification settings - Fork 700
feat: tree view for the preset archive previewer #3525
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4ca15cc
6f442b7
1d0c9a0
646b008
8f0cb6a
b06380e
d156508
dce7115
d3abfff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| {"flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","Konsole","Überzug","pkgs","pdftoppm","poppler","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone","DECRQSS","DECSCUSR","libvterm","Uninit","lockin","rposition","resvg","foldhash","tilded","futs","chdir","hashbrown","JEMALLOC","RUSTFLAGS","RDONLY","GETPATH","fcntl","casefold","inodes","Splatable","casefied","thiserror","memchr","memmem","russh","deadpool","keepalive","nodelay","publickey","deadpool","initing"],"version":"0.2","language":"en"} | ||
| {"language":"en","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","Konsole","Überzug","pkgs","pdftoppm","poppler","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone","DECRQSS","DECSCUSR","libvterm","Uninit","lockin","rposition","resvg","foldhash","tilded","futs","chdir","hashbrown","JEMALLOC","RUSTFLAGS","RDONLY","GETPATH","fcntl","casefold","inodes","Splatable","casefied","thiserror","memchr","memmem","russh","deadpool","keepalive","nodelay","publickey","deadpool","initing","treelize"],"version":"0.2"} |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,26 +2,27 @@ local M = {} | |||||||||||||
|
|
||||||||||||||
| function M:peek(job) | ||||||||||||||
| local limit = job.area.h | ||||||||||||||
| local files, bound, err = self.list_archive({ "-p", tostring(job.file.path) }, job.skip, limit) | ||||||||||||||
| local files, err = self.list_archive({ "-p", tostring(job.file.path) }, job.skip, limit) | ||||||||||||||
|
|
||||||||||||||
| local first = (#files == 1 and files[1]) or (#files == 0 and M.list_if_only_one(job.file.path)) | ||||||||||||||
| if first and M.should_decompress_tar(first) then | ||||||||||||||
| files, bound, err = self.list_compressed_tar({ "-p", tostring(job.file.path) }, job.skip, limit) | ||||||||||||||
| files, err = self.list_compressed_tar({ "-p", tostring(job.file.path) }, job.skip, limit) | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| 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 job.skip > 0 and #files < job.skip + limit then | ||||||||||||||
| return ya.emit("peek", { math.max(0, #files - limit), only_if = job.file.url, upper_bound = true }) | ||||||||||||||
| elseif #files == 0 then | ||||||||||||||
| files = { { path = job.file.url.stem, size = 0, packed_size = 0, attr = "" } } | ||||||||||||||
| files = { M.make_file { path = job.file.url.stem } } | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| local left, right = {}, {} | ||||||||||||||
| for _, f in ipairs(files) do | ||||||||||||||
| for i = job.skip + 1, #files do | ||||||||||||||
| local f = files[i] | ||||||||||||||
| local icon = File({ | ||||||||||||||
| url = Url(f.path), | ||||||||||||||
| cha = Cha { mode = tonumber(f.attr:sub(1, 1) == "D" and "40700" or "100644", 8) }, | ||||||||||||||
| cha = Cha { mode = tonumber(f.is_dir and "40700" or "100644", 8) }, | ||||||||||||||
| }):icon() | ||||||||||||||
|
|
||||||||||||||
| if f.size > 0 then | ||||||||||||||
|
|
@@ -37,8 +38,9 @@ function M:peek(job) | |||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| left[#left] = ui.Line { | ||||||||||||||
| string.rep(" │", f.depth), | ||||||||||||||
| left[#left], | ||||||||||||||
| ui.truncate(f.path, { | ||||||||||||||
| ui.truncate(f.path.name, { | ||||||||||||||
| rtl = true, | ||||||||||||||
| max = math.max(0, job.area.w - ui.width(left[#left]) - ui.width(right[#right])), | ||||||||||||||
| }), | ||||||||||||||
|
|
@@ -104,49 +106,49 @@ end | |||||||||||||
| ---@param skip integer | ||||||||||||||
| ---@param limit integer | ||||||||||||||
| ---@return table files | ||||||||||||||
| ---@return integer bound | ||||||||||||||
| ---@return Error? err | ||||||||||||||
| function M.list_archive(args, skip, limit) | ||||||||||||||
| local child = M.spawn_7z { "l", "-ba", "-slt", "-sccUTF-8", table.unpack(args) } | ||||||||||||||
| if not child then | ||||||||||||||
| return {}, 0, Err("Failed to start either `7zz` or `7z`. Do you have 7-zip installed?") | ||||||||||||||
| return {}, Err("Failed to start either `7zz` or `7z`. Do you have 7-zip installed?") | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| local files, bound, err = M.parse_7z_slt(child, skip, limit) | ||||||||||||||
| local files, err = M.parse_7z_slt(child, skip, limit) | ||||||||||||||
| child:start_kill() | ||||||||||||||
|
|
||||||||||||||
| return files, bound, err | ||||||||||||||
| return files, err | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| ---List files in a compressed tarball | ||||||||||||||
| ---@param args table | ||||||||||||||
| ---@param skip integer | ||||||||||||||
| ---@param limit integer | ||||||||||||||
| ---@return table files | ||||||||||||||
| ---@return integer bound | ||||||||||||||
| ---@return Error? err | ||||||||||||||
| function M.list_compressed_tar(args, skip, limit) | ||||||||||||||
| local src, dst = M.spawn_7z_piped( | ||||||||||||||
| { "x", "-so", table.unpack(args) }, | ||||||||||||||
| { "l", "-ba", "-slt", "-ttar", "-sccUTF-8", "-si" } | ||||||||||||||
| ) | ||||||||||||||
| if not dst then | ||||||||||||||
| return {}, 0, Err("Failed to start either `7zz` or `7z`. Do you have 7-zip installed?") | ||||||||||||||
| return {}, Err("Failed to start either `7zz` or `7z`. Do you have 7-zip installed?") | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| local files, bound, err = M.parse_7z_slt(dst, skip, limit) | ||||||||||||||
| local files, err = M.parse_7z_slt(dst, skip, limit) | ||||||||||||||
| src:start_kill() | ||||||||||||||
| dst:start_kill() | ||||||||||||||
|
|
||||||||||||||
| return files, bound, err | ||||||||||||||
| return files, err | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| ---@param path Path | ||||||||||||||
| ---@return table? | ||||||||||||||
| function M.list_if_only_one(path) | ||||||||||||||
| -- For certain compressed tarballs (e.g. .tar.xz), | ||||||||||||||
| -- 7-zip doesn't print a .tar file if -slt is specified, so we are not doing that here | ||||||||||||||
| local child = M.spawn_7z { "l", "-ba", "-sccUTF-8", "-p", tostring(path) } | ||||||||||||||
| if not child then | ||||||||||||||
| return false | ||||||||||||||
| return | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| local files = {} | ||||||||||||||
|
|
@@ -155,7 +157,7 @@ function M.list_if_only_one(path) | |||||||||||||
| if event == 0 then | ||||||||||||||
| local attr, size, packed_size, path = next:sub(20):match("([^ ]+) +(%d+) +(%d+) +([^\r\n]+)") | ||||||||||||||
| if path then | ||||||||||||||
| files[#files + 1] = { path = path, size = tonumber(size), packed_size = tonumber(packed_size), attr = attr } | ||||||||||||||
| files[#files + 1] = M.make_file { path = path, size = size, packed_size = packed_size, attr = attr } | ||||||||||||||
| end | ||||||||||||||
| elseif event ~= 1 then | ||||||||||||||
| break | ||||||||||||||
|
|
@@ -170,7 +172,7 @@ end | |||||||||||||
|
|
||||||||||||||
| ---List metadata of an archive | ||||||||||||||
| ---@param args table | ||||||||||||||
| ---@return string|nil type | ||||||||||||||
| ---@return string? type | ||||||||||||||
| ---@return integer code | ||||||||||||||
| --- 0: success | ||||||||||||||
| --- 1: failed to spawn | ||||||||||||||
|
|
@@ -213,8 +215,18 @@ function M.is_encrypted(s) return s:find(" Wrong password", 1, true) end | |||||||||||||
|
|
||||||||||||||
| function M.is_tar(path) return M.list_meta { "-p", tostring(path) } == "tar" end | ||||||||||||||
|
|
||||||||||||||
| function M.make_file(t) | ||||||||||||||
| t = t or {} | ||||||||||||||
| t.path = type(t.path or "") == "string" and Path.os(t.path or "") or t.path | ||||||||||||||
| t.size = tonumber(t.size) or 0 | ||||||||||||||
| t.packed_size = tonumber(t.packed_size) or 0 | ||||||||||||||
| t.attr = t.attr or "" | ||||||||||||||
| t.folder = t.folder or "" | ||||||||||||||
| return t | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| function M.should_decompress_tar(file) | ||||||||||||||
| return file.packed_size <= 1024 * 1024 * 1024 and file.path:lower():find(".+%.tar$") ~= nil | ||||||||||||||
| return file.packed_size <= 1024 * 1024 * 1024 and (file.path.ext or ""):lower() == "tar" | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| -- Parse the output of a "7z l -slt" command. | ||||||||||||||
|
|
@@ -223,11 +235,10 @@ end | |||||||||||||
| ---@param skip integer | ||||||||||||||
| ---@param limit integer | ||||||||||||||
| ---@return table files | ||||||||||||||
| ---@return integer bound | ||||||||||||||
| ---@return Error? err | ||||||||||||||
| function M.parse_7z_slt(child, skip, limit) | ||||||||||||||
| local i, files, err = 0, { { path = "", size = 0, packed_size = 0, attr = "" } }, nil | ||||||||||||||
| local key, value, stderr = "", "", {} | ||||||||||||||
| local files, parents, err = { M.make_file() }, {}, nil | ||||||||||||||
| local key, value, empty, stderr = "", "", Path.os(""), {} | ||||||||||||||
| repeat | ||||||||||||||
| local next, event = child:read_line() | ||||||||||||||
| if event == 1 and M.is_encrypted(next) then | ||||||||||||||
|
|
@@ -241,36 +252,66 @@ function M.parse_7z_slt(child, skip, limit) | |||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| if next == "\n" or next == "\r\n" then | ||||||||||||||
| i = i + 1 | ||||||||||||||
| if files[#files].path ~= "" then | ||||||||||||||
| files[#files + 1] = { path = "", size = 0, packed_size = 0, attr = "" } | ||||||||||||||
| if files[#files].path ~= empty then | ||||||||||||||
| M.treelize(files, parents) | ||||||||||||||
| files[#files + 1] = M.make_file() | ||||||||||||||
| end | ||||||||||||||
| goto continue | ||||||||||||||
| elseif i < skip then | ||||||||||||||
| goto continue | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| key, value = next:match("^(%u[%a ]+) = (.-)[\r\n]+") | ||||||||||||||
| if key == "Path" then | ||||||||||||||
| files[#files].path = value | ||||||||||||||
| files[#files].path = Path.os(value) | ||||||||||||||
| elseif key == "Size" then | ||||||||||||||
| files[#files].size = tonumber(value) or 0 | ||||||||||||||
| elseif key == "Packed Size" then | ||||||||||||||
| files[#files].packed_size = tonumber(value) or 0 | ||||||||||||||
| elseif key == "Attributes" then | ||||||||||||||
| files[#files].attr = value | ||||||||||||||
| elseif key == "Folder" then | ||||||||||||||
| files[#files].folder = value | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| ::continue:: | ||||||||||||||
| until i >= skip + limit | ||||||||||||||
| until #files > skip + limit | ||||||||||||||
|
|
||||||||||||||
|
||||||||||||||
| -- Ensure the last file is treelized if it represents a real entry | |
| if files[#files].path ~= empty then | |
| M.treelize(files, parents) | |
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic has been inverted from
is_none_ortois_some_and, which changes the behavior. Previously, this returnedtrueif there was no matching partition OR if the partition requires heuristic polling. Now it returnstrueonly if there IS a matching partition AND it requires heuristic polling. This means directories on unmounted/unknown filesystems will no longer use heuristic polling, which could be a breaking behavioral change. Please verify this is the intended behavior.