Skip to content

Conversation

@itsvyle
Copy link
Contributor

@itsvyle itsvyle commented Jan 7, 2026

Which issue does this PR resolve?

Follow-up on #3518

Rationale of this PR

Two changes:

  • Moved the display of the list to the preview into a separate function. This is so that other previewers may call into the public API of the archive plugin to display their own file lists in a beautiful list with icons and colors
  • Added a "tree" style display for folders in archives:
Before After
image image
image image
image image

@sxyazi
Copy link
Owner

sxyazi commented Jan 7, 2026

This looks fantastic! There's a problem though - when I seek down the preview (pressing the J key) there are some layout shifts that flicker between the list and the tree mode.

@itsvyle
Copy link
Contributor Author

itsvyle commented Jan 7, 2026

there are some layout shifts that flicker between the list and the tree mode

These should have been fixed by my latest change.

However, I noticed a new issue: some archive files don't actually contain the directories as "files" in their contents. As a result, my current way of detecting directories isn't working; example:

Original archive Decompressed then recompressed with 7z
image image
image image

I'm wondering if auto-detecting a directory structure inside of the list is a good idea. Realistically, it's only at most processing on a terminal height's amount of files (50-ish?), but it could imply making some assumptions about the environment which can be pretty unknown.

@sxyazi
Copy link
Owner

sxyazi commented Jan 8, 2026

Yeah 7-Zip sometimes doesn't report directory status or even any attributes for a file. Do you think detect directories solely from the path? For example, for the path a/b/c, if c appears then that proves a/b exists and is a directory.

@itsvyle itsvyle closed this Jan 8, 2026
@itsvyle itsvyle force-pushed the archive-preview-tree branch from a7fb778 to ebedab3 Compare January 8, 2026 15:17
@itsvyle itsvyle reopened this Jan 8, 2026
@itsvyle
Copy link
Contributor Author

itsvyle commented Jan 8, 2026

I have added auto detection for directories.

Sadly, this only works if the files are listed in the correct lexical order, which is the order by default on a created zip, but isn't actually the order that 7z lists the files in - 7z just lists the files in an order decided by the compressor of the file.

This can lead to bad situations like this one (seems to only happen on zip files, although I'm not 100% sure it can't happen on tarballs)

image

And with the current architecture, I don't think there is much that can be done - because if we sort the files we have listed (which are limitted to the height of the terminal), scrolling through the archive will be very weird. We would have to get all the files in the archive and then sort them, but then that can add a lot of overhead.

I haven't really looked into a way to recreate an example zip file which has problematic order.

I however personally believe that the current behavior, where the archive is required to have been created with files in lexical order (which has been the default on 7z for years), is good enough, as it should regard most of the archive files out there.

@sxyazi sxyazi force-pushed the archive-preview-tree branch from c16d790 to 646b008 Compare January 9, 2026 11:59
@sxyazi sxyazi changed the title refactor: display a tree for archive preview feat: tree view for the preset archive previewer Jan 9, 2026
@sxyazi
Copy link
Owner

sxyazi commented Jan 9, 2026

Hi, I did some refactoring to simplify the code, could you have a look to see if there are any errors?

During testing I found an issue: if an archive has a deep directory structure, scroll down to the end can cause some files to be lost, here's the steps to repruduce it:

  1. Download test.zip
  2. Keep pressing J until you reach the bottom
  3. The file bbbbbbbbbb (ten b) is missing, and only bbbbbbbb (eight b) is shown

UcUsBF0Q

Do you have any idea how to fix this?

@itsvyle
Copy link
Contributor Author

itsvyle commented Jan 9, 2026

We created a bunch of rows for directories - these aren't listed inside the 7z l output, but we scroll through that list, rather than the one with the directories. We would need to find a way to adjust "job.skip" for this shift; "job.skip" for the next page should be equal to "bound", not the number of lines which were outputted.

I'm not sure how job.skip is defined though, or if we can modify it...

@sxyazi
Copy link
Owner

sxyazi commented Jan 9, 2026

skip is changed inside the M:seek() method - basically it just increases by unit*screen_height/10 of units each time:

local step = math.floor(job.units * job.area.h / 10)
step = step == 0 and ya.clamp(-1, job.units, 1) or step
ya.emit("peek", {
math.max(0, cx.active.preview.skip + step),
only_if = job.file.url,
})

But we can't get 7-Zip's output inside the seek method because it's sync and not suitable for running time-consuming tasks.

@sxyazi
Copy link
Owner

sxyazi commented Jan 11, 2026

OK I think I've found a way to fix it in dce7115, there are no blockers now

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds tree view functionality to the preset archive previewer, displaying directories in archives with visual hierarchy using tree characters.

Changes:

  • Refactored archive listing to build a tree structure from flat file lists
  • Added treelize function to compute parent directories and depth for tree visualization
  • Fixed a logic bug in filesystem partition heuristic detection (inverted from is_none_or to is_some_and)

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
yazi-plugin/preset/plugins/archive.lua Main changes: added tree view rendering with depth indicators, refactored parsing to remove bound return value, added treelize and make_file helper functions
yazi-fs/src/mounts/partitions.rs Changed heuristic detection logic from is_none_or to is_some_and
yazi-fs/src/mounts/partition.rs Minor comment update removing redundant text
Cargo.toml Updated ansi-to-tui to 8.0.1 and toml to 0.9.11
Cargo.lock Lockfile updates for dependency changes
cspell.json Added "treelize" to dictionary
CHANGELOG.md Added entry for tree view feature

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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?")
Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return statement includes an extra value 0 that doesn't match the updated function signature. This function now returns only files and err, but this error path returns three values: {}, 0, and an Error. The 0 should be removed to match the new signature.

Copilot uses AI. Check for mistakes.
{ "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?")
Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return statement includes an extra value 0 that doesn't match the updated function signature. This function now returns only files and err, but this error path returns three values: {}, 0, and an Error. The 0 should be removed to match the new signature.

Copilot uses AI. Check for mistakes.
#[cfg(any(target_os = "linux", target_os = "macos"))]
{
self.by_dev(_cha.dev).is_none_or(|p| p.heuristic())
self.by_dev(_cha.dev).is_some_and(|p| p.heuristic())
Copy link

Copilot AI Jan 11, 2026

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_or to is_some_and, which changes the behavior. Previously, this returned true if there was no matching partition OR if the partition requires heuristic polling. Now it returns true only 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.

Suggested change
self.by_dev(_cha.dev).is_some_and(|p| p.heuristic())
self.by_dev(_cha.dev).is_none_or(|p| p.heuristic())

Copilot uses AI. Check for mistakes.
::continue::
until i >= skip + limit
until #files > skip + limit

Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the last file in the stream doesn't end with a blank line, it won't go through the treelize function (which is only called at line 254 when a blank line is encountered). This means the last file may not have the depth and is_dir fields set, which will cause issues when rendering (line 41 uses f.depth, line 25 uses f.is_dir). Consider calling treelize for the last file if it has a non-empty path before checking at line 276.

Suggested change
-- Ensure the last file is treelized if it represents a real entry
if files[#files].path ~= empty then
M.treelize(files, parents)
end

Copilot uses AI. Check for mistakes.
Copy link
Owner

@sxyazi sxyazi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you

@sxyazi sxyazi merged commit 233d721 into sxyazi:main Jan 11, 2026
6 checks passed
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Jan 23, 2026
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [sxyazi/yazi](https://github.com/sxyazi/yazi) | patch | `v26.1.4` → `v26.1.22` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>sxyazi/yazi (sxyazi/yazi)</summary>

### [`v26.1.22`](https://github.com/sxyazi/yazi/blob/HEAD/CHANGELOG.md#v26122)

[Compare Source](sxyazi/yazi@v26.1.4...v26.1.22)

##### Added

- Tree view for the preset archive previewer (\[[#&#8203;3525](sxyazi/yazi#3525)])
- Support compressed tarballs (`.tar.gz`, `.tar.bz2`, etc.) in the preset archive previewer (\[[#&#8203;3518](sxyazi/yazi#3518)])
- Check and refresh the file list when the terminal gains focus (\[[#&#8203;3561](sxyazi/yazi#3561)])
- Experimental module-level async support (\[[#&#8203;3594](sxyazi/yazi#3594)])
- Disable ANSI escape sequences in `ya pkg` when stdout is not a TTY (\[[#&#8203;3566](sxyazi/yazi#3566)])
- New `Path.os()` API creates an OS-native `Path` (\[[#&#8203;3541](sxyazi/yazi#3541)])

##### Fixed

- Smart-case in interactive `cd` broken due to a typo (\[[#&#8203;3540](sxyazi/yazi#3540)])
- Fix shell formatting for non-spread opener rules (\[[#&#8203;3532](sxyazi/yazi#3532)])
- `sort extension` excludes directories since only files have extensions (\[[#&#8203;3582](sxyazi/yazi#3582)])
- Account for URL covariance in `Url:join()` (\[[#&#8203;3514](sxyazi/yazi#3514)])

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever MR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi44OC4yIiwidXBkYXRlZEluVmVyIjoiNDIuODguMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiUmVub3ZhdGUgQm90IiwiYXV0b21hdGlvbjpib3QtYXV0aG9yZWQiLCJkZXBlbmRlbmN5LXR5cGU6OnBhdGNoIl19-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants