From 1d80c446759f576bfdba4346839101cf0f7e410b Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 04:20:40 +0000 Subject: [PATCH 01/12] ci(release): take the release notes from CHANGELOG.md The notes came from git-cliff over the commit titles since the last tag. Once a pull request is squashed, that is one line for all of it: the 45 fixes in #149 would have reached the v0.28.5 notes as "Audit the suite against independent oracles". CHANGELOG.md already holds the section written for readers, so publish that. A new job takes the section for the version in Cargo.toml and fails when there is none, or, on a tag, when the tag is not that version. It runs in the dry run too, so a missing section or a forgotten bump shows up before tagging rather than after the binaries are built. The publish job no longer compiles git-cliff. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01K1zQZxjBx5aUYbEcuEkXjk --- .github/workflows/release.yml | 45 +++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 83df45ad..d3fcb2d3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,9 @@ # not currently have, and it runs containers on the maintainer's machine. This # does neither. # -# The release is published as soon as every binary is built. +# The release is published as soon as every binary is built. Its notes are +# the version's section of CHANGELOG.md, the one written by hand: the commit +# titles git-cliff would list say little once a pull request is squashed. # # Run any other way, by hand or by a pull request that changes this file, it # is a dry run: everything is built and packaged, under a stand-in version, @@ -100,6 +102,37 @@ jobs: name: binary-${{ matrix.target }} path: dist/* if-no-files-found: error + notes: + name: Release notes + runs-on: ubuntu-latest + outputs: + notes: ${{ steps.notes.outputs.notes }} + steps: + - name: Checkout repository + uses: actions/checkout@v7 + - name: Take the version's section of CHANGELOG.md + id: notes + run: | + # The first `version =` in Cargo.toml is the package's. + version=$(sed -n 's/^version = "\(.*\)"$/\1/p' Cargo.toml | head -n 1) + if [ "${GITHUB_EVENT_NAME}" = push ] && [ "${VERSION}" != "v${version}" ]; then + echo "::error file=Cargo.toml::The tag is ${VERSION}, but Cargo.toml says ${version}." + exit 1 + fi + awk -v head="## [${version}]" ' + index($0, "## [") == 1 { if (on) exit; on = index($0, head) == 1; next } + on + ' CHANGELOG.md > NOTES.md + if ! grep -q '[^[:space:]]' NOTES.md; then + echo "::error file=CHANGELOG.md::CHANGELOG.md has no section for ${version}." + exit 1 + fi + cat NOTES.md + { + echo "notes<> "$GITHUB_OUTPUT" docs: name: Man pages and completions runs-on: ubuntu-latest @@ -127,7 +160,7 @@ jobs: if-no-files-found: error publish: name: Publish the release - needs: [binaries, docs] + needs: [binaries, docs, notes] # Only a pushed tag publishes; run by hand, even on a tag, it is a dry run. if: github.event_name == 'push' && github.ref_type == 'tag' runs-on: ubuntu-latest @@ -136,8 +169,6 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v7 - with: - fetch-depth: 0 - name: Collect the artifacts uses: actions/download-artifact@v4 with: @@ -148,10 +179,10 @@ jobs: find artifacts -type f -exec mv {} dist/ \; ls -l dist - name: Write the release notes + env: + NOTES: ${{ needs.notes.outputs.notes }} run: | - version="${GITHUB_REF_NAME}" - cargo install git-cliff --locked --version 2.13.1 - git cliff -c .config/cliff.toml -t "${version}" --current > NOTES.md + printf '%s\n' "${NOTES}" > NOTES.md { echo echo "## Checksums" From f57c3bf22f345b2f9362c04523e946008f523578 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 04:29:04 +0000 Subject: [PATCH 02/12] ci(release): bump the Homebrew formula in a job of its own The bump ran as the last step of the job that creates the GitHub release. When the tap token could not write, as on v0.28.4, that job went red although the release was out, and anything made to wait on the release would have been skipped with it. In its own job the failure stays where it belongs. The action talks to the GitHub API only, so the job needs no checkout. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01K1zQZxjBx5aUYbEcuEkXjk --- .github/workflows/release.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d3fcb2d3..15721afe 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -199,9 +199,17 @@ jobs: --title "lez ${GITHUB_REF_NAME}" \ --notes-file NOTES.md \ dist/* + homebrew: + name: Update the Homebrew formula + needs: publish + # Its own job, so a tap token that cannot write fails here alone rather + # than marking the release, or anything waiting on it, failed. + if: "!contains(github.ref_name, '-')" + runs-on: ubuntu-latest + steps: - name: Update Homebrew formula uses: mislav/bump-homebrew-formula-action@f865c8b0dbebd6e263cc06782a0e974c61cf12d2 # v4.2 - if: "!contains(github.ref_name, '-') && env.COMMITTER_TOKEN != ''" + if: env.COMMITTER_TOKEN != '' with: formula-name: lez homebrew-tap: fxrdhan/homebrew-tap From 3a31fdffaa96056c6e8721626103c3cae26aa81c Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 04:29:27 +0000 Subject: [PATCH 03/12] ci(release): publish the crate to crates.io Every release so far reached crates.io by a `cargo publish` run by hand some minutes after the tag, a step nothing in the repository recorded. Publish it from the release workflow instead, once the GitHub release is out: `cargo binstall` takes the version from crates.io and the binaries from that release, so this order never offers a version without them. It authenticates by Trusted Publishing, which trades the workflow's OIDC identity for a token that lasts 30 minutes, so no registry token is stored to leak or expire. The crate's crates.io settings must name this repository and release.yml as a trusted publisher. A dry run runs `cargo publish --dry-run`, which packages and builds the crate. Once a version is out it only warns that the version exists, so later dry runs stay green. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01K1zQZxjBx5aUYbEcuEkXjk --- .github/workflows/release.yml | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 15721afe..f771fcd6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,7 +1,8 @@ # SPDX-FileCopyrightText: 2026 fxrdhan # SPDX-License-Identifier: EUPL-1.2 # -# Publishes a release when a v* tag is pushed. +# Publishes a release when a v* tag is pushed: the GitHub release with its +# binaries, then the crate on crates.io and the Homebrew formula. # # The binaries are built on GitHub runners, one target per runner, natively. # The justfile's `cross` recipe builds seven targets through Docker instead; @@ -218,3 +219,35 @@ jobs: commit-message: "bump lez to ${{ github.ref_name }}" env: COMMITTER_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + crates-io: + name: Publish to crates.io + # After the GitHub release, because `cargo binstall` takes the version + # from crates.io and the binaries from that release. A dry run, which + # releases nothing, packages and builds the crate without uploading it. + needs: publish + if: ${{ !cancelled() && (needs.publish.result == 'success' || github.event_name != 'push') }} + runs-on: ubuntu-latest + permissions: + contents: read + # Trusted Publishing: crates.io trusts this workflow, so no token is + # stored. Configured under the crate's Settings > Trusted Publishing. + id-token: write + steps: + - name: Checkout repository + uses: actions/checkout@v7 + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@7e38f4b43b4db5c8dd498af069a4f6196df1d067 # master + with: + toolchain: stable + - name: Check the package + if: github.event_name != 'push' + run: cargo publish --dry-run --locked + - name: Authenticate with crates.io + id: auth + if: github.event_name == 'push' + uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5 + - name: Publish + if: github.event_name == 'push' + env: + CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} + run: cargo publish --locked From b0b4e9374284346022ad5394cbdbcceef0e33136 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 04:34:06 +0000 Subject: [PATCH 04/12] docs(theme): make the theme schema accept the documented examples The README's schema example and docs/theme.yml both failed the schema they point editors at: - `filekinds.symlink` took `oneOf` a link style or a plain style, and a plain style such as `{ foreground: Cyan }` is both, so every theme that styled symlinks was flagged. It is `anyOf` now, which still rejects a key neither knows. - The examples wrote `bold` and `underline`. lez reads those as aliases of `is_bold` and `is_underline`, but the schema and the man pages name only the `is_` forms, so the examples now use those. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01K1zQZxjBx5aUYbEcuEkXjk --- README.md | 2 +- docs/theme-schema.json | 2 +- docs/theme.yml | 2 +- tests/icons_theme/schema.rs | 6 +++++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8a27e1da..a99441b8 100644 --- a/README.md +++ b/README.md @@ -414,7 +414,7 @@ You can enable autocomplete and schema validation in VSCode, Neovim, or Zed by r # yaml-language-server: $schema=https://raw.githubusercontent.com/fxrdhan/lez/main/docs/theme-schema.json filekinds: - directory: { foreground: Blue, bold: true } + directory: { foreground: Blue, is_bold: true } symlink: { foreground: Cyan } ``` diff --git a/docs/theme-schema.json b/docs/theme-schema.json index b5394704..59a53fea 100644 --- a/docs/theme-schema.json +++ b/docs/theme-schema.json @@ -222,7 +222,7 @@ }, "symlink": { "description": "A style, or the literal \"target\" (optionally with ANSI codes) to borrow the pointed-to file's colour", - "oneOf": [ + "anyOf": [ { "$ref": "#/$defs/symlink_style" }, diff --git a/docs/theme.yml b/docs/theme.yml index 90642225..c9ad849d 100644 --- a/docs/theme.yml +++ b/docs/theme.yml @@ -12,7 +12,7 @@ filekinds: # Colour for Btrfs subvolume directories (Linux) btrfs_subvol: foreground: Blue - underline: true + is_underline: true executable: foreground: Green perms: diff --git a/tests/icons_theme/schema.rs b/tests/icons_theme/schema.rs index 0d88e236..4414b314 100644 --- a/tests/icons_theme/schema.rs +++ b/tests/icons_theme/schema.rs @@ -84,11 +84,15 @@ fn the_schema_is_draft_7_with_shared_definitions() { } /// A link's style may be a style, `target`, or `target` with ANSI codes. +/// `anyOf`, not `oneOf`: a plain style such as `{ foreground: Cyan }` is both +/// a link style and a style, and `oneOf` rejects a value two branches accept. #[test] fn filekinds_symlink_allows_the_target_keyword() { let schema = schema(); + let symlink = &schema["properties"]["filekinds"]["properties"]["symlink"]; + assert_eq!(symlink.get("oneOf"), None); assert_eq!( - schema["properties"]["filekinds"]["properties"]["symlink"]["oneOf"], + symlink["anyOf"], json!([ {"$ref": "#/$defs/symlink_style"}, {"$ref": "#/$defs/style"}, From defafd5c9111176fa2a387692a8c1cc5fab9e77c Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 04:35:21 +0000 Subject: [PATCH 05/12] fix(options): say -O lists file flags on Linux too The help text and the completions read "Mac, BSD, and Windows only", from before Linux inode flags (`FS_IOC_GETFLAGS`, as `lsattr` shows them) were supported. The README and lez(1) already say Linux. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01K1zQZxjBx5aUYbEcuEkXjk --- completions/fish/eza.fish | 2 +- completions/fish/lez.fish | 2 +- completions/nush/eza.nu | 2 +- completions/nush/lez.nu | 2 +- completions/pwsh/_eza.ps1 | 4 ++-- completions/pwsh/_lez.ps1 | 4 ++-- completions/zsh/_eza | 2 +- completions/zsh/_lez | 2 +- src/options/parser.rs | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/completions/fish/eza.fish b/completions/fish/eza.fish index de2ac5a4..0b6d8947 100644 --- a/completions/fish/eza.fish +++ b/completions/fish/eza.fish @@ -225,5 +225,5 @@ complete -c eza -l git-repos -d "List each git-repos status and branch name" complete -c eza -l git-repos-no-status -d "List each git-repos branch name (much faster)" complete -c eza -s '@' -l extended -d "List each file's extended attributes and sizes" complete -c eza -s Z -l context -d "List each file's security context" -complete -c eza -s O -l flags -d "List file flags (Mac, BSD, and Windows only)" +complete -c eza -s O -l flags -d "List file flags (Linux, macOS, BSD, and Windows)" complete -c eza -s e -l tags -d "List each file's color tags stored in extended attributes" diff --git a/completions/fish/lez.fish b/completions/fish/lez.fish index b996427c..95c51051 100644 --- a/completions/fish/lez.fish +++ b/completions/fish/lez.fish @@ -225,5 +225,5 @@ complete -c lez -l git-repos -d "List each git-repos status and branch name" complete -c lez -l git-repos-no-status -d "List each git-repos branch name (much faster)" complete -c lez -s '@' -l extended -d "List each file's extended attributes and sizes" complete -c lez -s Z -l context -d "List each file's security context" -complete -c lez -s O -l flags -d "List file flags (Mac, BSD, and Windows only)" +complete -c lez -s O -l flags -d "List file flags (Linux, macOS, BSD, and Windows)" complete -c lez -s e -l tags -d "List each file's color tags stored in extended attributes" diff --git a/completions/nush/eza.nu b/completions/nush/eza.nu index 9f7c9102..baf3275a 100644 --- a/completions/nush/eza.nu +++ b/completions/nush/eza.nu @@ -90,7 +90,7 @@ export extern "eza" [ --git-repos-no-status # List each git-repos branch name (much faster) --extended(-@) # List each file's extended attributes and sizes --context(-Z) # List each file's security context - --flags(-O) # List file flags (Mac, BSD, and Windows only) + --flags(-O) # List file flags (Linux, macOS, BSD, and Windows) --tags(-e) # List each file's color tags stored in extended attributes --smart-group # Only show group if it has a different name from owner --stdin # When piping to eza. Read file paths from stdin diff --git a/completions/nush/lez.nu b/completions/nush/lez.nu index 9a48d7d1..c0fc57ab 100644 --- a/completions/nush/lez.nu +++ b/completions/nush/lez.nu @@ -90,7 +90,7 @@ export extern "lez" [ --git-repos-no-status # List each git-repos branch name (much faster) --extended(-@) # List each file's extended attributes and sizes --context(-Z) # List each file's security context - --flags(-O) # List file flags (Mac, BSD, and Windows only) + --flags(-O) # List file flags (Linux, macOS, BSD, and Windows) --tags(-e) # List each file's color tags stored in extended attributes --smart-group # Only show group if it has a different name from owner --stdin # When piping to lez. Read file paths from stdin diff --git a/completions/pwsh/_eza.ps1 b/completions/pwsh/_eza.ps1 index f398b9a1..24be1799 100644 --- a/completions/pwsh/_eza.ps1 +++ b/completions/pwsh/_eza.ps1 @@ -104,8 +104,8 @@ Register-ArgumentCompleter -Native -CommandName 'eza' -ScriptBlock { # [CompletionResult]::new('--mounts' ,'mounts' , [CompletionResultType]::ParameterName, 'show mount details (Linux and Mac only)') # [CompletionResult]::new('-n' ,'numeric' , [CompletionResultType]::ParameterName, 'list numeric user and group IDs') [CompletionResult]::new('--numeric' ,'numeric' , [CompletionResultType]::ParameterName, 'list numeric user and group IDs') - # [CompletionResult]::new('-O' ,'flags' , [CompletionResultType]::ParameterName, 'list file flags (Mac, BSD, and Windows only)') - [CompletionResult]::new('--flags' ,'flags' , [CompletionResultType]::ParameterName, 'list file flags (Mac, BSD, and Windows only)') + # [CompletionResult]::new('-O' ,'flags' , [CompletionResultType]::ParameterName, 'list file flags (Linux, macOS, BSD, and Windows)') + [CompletionResult]::new('--flags' ,'flags' , [CompletionResultType]::ParameterName, 'list file flags (Linux, macOS, BSD, and Windows)') # [CompletionResult]::new('-S' ,'blocksize' , [CompletionResultType]::ParameterName, 'list the allocated size of each file, in bytes') [CompletionResult]::new('--blocksize' ,'blocksize' , [CompletionResultType]::ParameterName, 'list the allocated size of each file, in bytes') [CompletionResult]::new('--blocks' ,'blocks' , [CompletionResultType]::ParameterName, 'list the allocated size of each file, in blocks') diff --git a/completions/pwsh/_lez.ps1 b/completions/pwsh/_lez.ps1 index 4ca6fb28..0cd58158 100644 --- a/completions/pwsh/_lez.ps1 +++ b/completions/pwsh/_lez.ps1 @@ -104,8 +104,8 @@ Register-ArgumentCompleter -Native -CommandName 'lez' -ScriptBlock { # [CompletionResult]::new('--mounts' ,'mounts' , [CompletionResultType]::ParameterName, 'show mount details (Linux and Mac only)') # [CompletionResult]::new('-n' ,'numeric' , [CompletionResultType]::ParameterName, 'list numeric user and group IDs') [CompletionResult]::new('--numeric' ,'numeric' , [CompletionResultType]::ParameterName, 'list numeric user and group IDs') - # [CompletionResult]::new('-O' ,'flags' , [CompletionResultType]::ParameterName, 'list file flags (Mac, BSD, and Windows only)') - [CompletionResult]::new('--flags' ,'flags' , [CompletionResultType]::ParameterName, 'list file flags (Mac, BSD, and Windows only)') + # [CompletionResult]::new('-O' ,'flags' , [CompletionResultType]::ParameterName, 'list file flags (Linux, macOS, BSD, and Windows)') + [CompletionResult]::new('--flags' ,'flags' , [CompletionResultType]::ParameterName, 'list file flags (Linux, macOS, BSD, and Windows)') # [CompletionResult]::new('-S' ,'blocksize' , [CompletionResultType]::ParameterName, 'list the allocated size of each file, in bytes') [CompletionResult]::new('--blocksize' ,'blocksize' , [CompletionResultType]::ParameterName, 'list the allocated size of each file, in bytes') [CompletionResult]::new('--blocks' ,'blocks' , [CompletionResultType]::ParameterName, 'list the allocated size of each file, in blocks') diff --git a/completions/zsh/_eza b/completions/zsh/_eza index 0212c515..77812013 100644 --- a/completions/zsh/_eza +++ b/completions/zsh/_eza @@ -100,7 +100,7 @@ __eza() { \*{-@,--extended}"[List each file's extended attributes and sizes]" \ \*--no-extended"[Don't show the '@' marker that a file has extended attributes]" \ \*{-Z,--context}"[List each file's security context]" \ - \*{-O,--flags}"[List file flags (Mac, BSD, and Windows only)]" \ + \*{-O,--flags}"[List file flags (Linux, macOS, BSD, and Windows)]" \ \*{-e,--tags}"[List each file's color tags stored in extended attributes]" \ \*{-M,--mounts}"[Show mount details (long mode only)]" \ '*:filename:_files' \ diff --git a/completions/zsh/_lez b/completions/zsh/_lez index e589f7d1..5274cb4e 100644 --- a/completions/zsh/_lez +++ b/completions/zsh/_lez @@ -100,7 +100,7 @@ __lez() { \*{-@,--extended}"[List each file's extended attributes and sizes]" \ \*--no-extended"[Don't show the '@' marker that a file has extended attributes]" \ \*{-Z,--context}"[List each file's security context]" \ - \*{-O,--flags}"[List file flags (Mac, BSD, and Windows only)]" \ + \*{-O,--flags}"[List file flags (Linux, macOS, BSD, and Windows)]" \ \*{-e,--tags}"[List each file's color tags stored in extended attributes]" \ \*{-M,--mounts}"[Show mount details (long mode only)]" \ '*:filename:_files' \ diff --git a/src/options/parser.rs b/src/options/parser.rs index 2705dcd8..c49b384c 100644 --- a/src/options/parser.rs +++ b/src/options/parser.rs @@ -236,7 +236,7 @@ pub fn get_command() -> clap::Command { .help(format!("how to format timestamps {FORMAT_STYLE_FIELDS_HELP}")) .value_parser(TimeFormatParser) .hide_possible_values(false)) - .arg(arg!(-O --flags "list file flags (Mac, BSD, and Windows only)").id("file-flags")) + .arg(arg!(-O --flags "list file flags (Linux, macOS, BSD, and Windows)").id("file-flags")) .arg(arg!(-Z --context "list each file's security context").id("security-context")) .arg(arg!(--git "list each file's Git status, if tracked or ignored") .overrides_with("no-git")) From ff00d755a0b4461f212acb23c6557ea864e80a8c Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 04:35:53 +0000 Subject: [PATCH 06/12] docs(readme): correct what the binary does not bear out Checked against the binary, the README claimed: - `--spacing` acts in the grid views and takes 0 to 255. It sets the long view's spacing too, where the default is 1, and is capped at 1000. - `recent` is an alias of `relative-recent`. lez refuses it; what it does take, and nothing documented, is `relative-recent:DAYS`. - `CLICOLOR` and `CLICOLOR_FORCE` are honoured, and `LEZ_OVERRIDE_AUTO_COLOR` exists. lez reads none of them; only `NO_COLOR`, and only when it is not empty. - "Syntax highlighting". lez colours file names by type; it highlights no syntax. It also had two "Installation" sections, the second only a pointer to INSTALL.md, now a line in the first; and its feature matrix listed "Custom Column Order (`--blocks`)" as missing although lez has a `--blocks`, a different one, so the row now names lsd's. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01K1zQZxjBx5aUYbEcuEkXjk --- README.md | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index a99441b8..4c7ec0f6 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ SPDX-License-Identifier: EUPL-1.2 **`lez`** is a fast, modern file-listing command-line tool with smart defaults, enhanced file icons, Git integration, and continuous performance improvements. - **Fast & Lightweight:** Written in modern Rust (2024 Edition) with multithreaded directory scanning via Rayon. -- **Rich Visuals:** Syntax highlighting, colored CLI help output, Nerd Font icons, and automatic luminance color scaling. +- **Rich Visuals:** Colors by file type, colored CLI help output, Nerd Font icons, and automatic luminance color scaling. - **Git Integration:** View file and repo status (`M`odified, `U`ntracked, `I`gnored, etc.) directly in the file listing. - **Built-in Tree View:** Hierarchical directory tree out of the box (`lez --tree`). - **Structured Data Export:** Full metadata serialization via `--json` in complete parity with the long view. @@ -85,7 +85,7 @@ hyperfine --warmup 3 'lez --tree ~/.cargo/registry' 'eza --tree ~/.cargo/registr | **Nerd Font Icons & Color Themes** | ✅ | ✅ | ✅ | | **Directory Tree View** (`--tree`) | ✅ | ✅ | ✅ | | **Hyperlink Support** (`--hyperlink` OSC 8) | ✅ | ✅ | ✅ | -| **Custom Column Order** (`--blocks`) | ❌ | ❌ | ✅ | +| **Custom Column Order** (`lsd --blocks`) | ❌ | ❌ | ✅ | | **Classic GNU `ls` Mode** (`--classic`) | ❌ | ❌ | ✅ | | **Unicode Emoji Fallback** (`--icon-theme unicode`) | ❌ | ❌ | ✅ | | **Multithreaded Traversal** (Rayon Engine) | ✅ | ⚠️ Limited | ❌ | @@ -108,6 +108,8 @@ hyperfine --warmup 3 'lez --tree ~/.cargo/registry' 'eza --tree ~/.cargo/registr

Installation

+`lez` is available for macOS, Linux, and Windows. [INSTALL.md](INSTALL.md) covers every channel, plus shell completions and the man pages. + ### Homebrew (macOS & Linux) Install from the official [fxrdhan tap](https://github.com/fxrdhan/homebrew-tap): @@ -185,12 +187,6 @@ nix run github:fxrdhan/lez --- -# Installation - -`lez` is available for macOS, Linux, and Windows. Detailed platform-specific installation instructions can be found in [INSTALL.md](INSTALL.md). - ---- -

Command-line options

@@ -216,7 +212,7 @@ nix run github:fxrdhan/lez - **--colo[u]r-scale=(fields)**: highlight levels of `fields` distinctly (all, age, size) - **--color-scale-mode=(mode)**: use gradient or fixed colors in `--color-scale` (`fixed` or `gradient`) - **--icons[=(when)]**: when to display icons (always, auto, never; requires '=' if value provided) -- **--spacing=(spaces)**: number of spaces between columns in grid views (default: 2, range: 0..=255) +- **--spacing=(spaces)**: number of spaces between columns (default: 2 in the grid views, 1 in the long view; at most 1000) - **--no-symlink-targets**: do not show symlink targets (the `-> ...`) - **--quotes=(when)**: when to quote file names (always, auto, never; requires '=' if value provided) - **--summary**: display total summary statistics of entries (directories, files, symlinks, and total) @@ -314,7 +310,7 @@ Some of the options accept parameters: - Valid **--colo\[u\]r** options are **always**, **automatic** (or **auto** for short), and **never**. - Valid sort fields are **accessed**, **changed**, **created**, **extension**, **Extension**, **inode**, **lexicographic**, **Lexicographic**, **modified**, **name**, **Name**, **path**, **Path**, **size**, **block**, **type**, and **none**. Fields starting with a capital letter sort uppercase before lowercase. The modified field has the aliases **date**, **time**, **mod**, **old**, and **oldest**, while its reverse has the aliases **age**, **new**, and **newest**. The **block** field has the aliases **blocks** and **blocksize**. The **lexicographic** field has the aliases **lex** and **lg**, and compares names code point by code point — no natural ordering of digit runs, no locale collation — so **Lexicographic** matches `ls` under the C locale. - Valid time fields are **modified**, **changed**, **accessed**, and **created**. -- Valid time styles are **default**, **iso**, **long-iso**, **full-iso**, **relative**, and **relative-recent** (or **recent**). +- Valid time styles are **default**, **iso**, **long-iso**, **full-iso**, **relative**, **relative-recent** (relative times for the last 7 days, the default style before that; **relative-recent:DAYS** sets the window), and a custom **+FORMAT**. See the `man` pages for further documentation of usage. They are available: - online [in the repo](https://github.com/fxrdhan/lez/tree/main/man) @@ -390,9 +386,8 @@ An annotated sample configuration is provided in [`docs/config.example.toml`](do | `LEZ_NO_EMPTY_DIR_ICON` / `EZA_NO_EMPTY_DIR_ICON` | Set to anything to give every directory the same icon. Distinguishing an empty one costs a filesystem round trip per directory, which is slow on FUSE and network mounts. | | `LEZ_STDIN_SEPARATOR` / `EZA_STDIN_SEPARATOR` | Delimiter for paths read from standard input with `--stdin`; ignored by `--stdin0` (default: newline `\n`). Supports escape sequences (e.g. `\0`, `\n`, `\t`, `\x00`) and `null`/`nul`. | | `LEZ_SIZE_DIGITS` / `EZA_SIZE_DIGITS` | Default number of digits (1..=8) to display for formatted file sizes, the decimal point counting as one (default: `3`). | -| `LEZ_OVERRIDE_AUTO_COLOR` | Force automatic color detection behavior. | | `TIME_STYLE` | Default timestamp format style (`default`, `iso`, `long-iso`, `full-iso`, `relative`, `relative-recent`, or `+`), also read in GNU `ls`'s `locale` and `posix-