Showing preview only (565K chars total). Download the full file or copy to clipboard to get everything.
Repository: lsd-rs/lsd
Branch: main
Commit: 8bcba5b54a35
Files: 76
Total size: 540.0 KB
Directory structure:
gitextract_ggwc0en1/
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug.yml
│ │ └── bug_report.md
│ ├── PULL_REQUEST_TEMPLATE
│ └── workflows/
│ └── CICD.yml
├── .gitignore
├── .release.toml
├── CODEOWNERS
├── Cargo.toml
├── LICENSE
├── OWNERS
├── README.md
├── build.rs
├── ci/
│ ├── before_deploy.bash
│ ├── before_install.bash
│ └── script.bash
├── doc/
│ ├── colors.md
│ ├── lsd.md
│ └── samples/
│ ├── colors-sample.yaml
│ ├── config-sample.yaml
│ └── icons-sample.yaml
├── lsd.spec
├── rustfmt.toml
├── src/
│ ├── app.rs
│ ├── color.rs
│ ├── config_file.rs
│ ├── core.rs
│ ├── display.rs
│ ├── flags/
│ │ ├── blocks.rs
│ │ ├── color.rs
│ │ ├── date.rs
│ │ ├── dereference.rs
│ │ ├── display.rs
│ │ ├── header.rs
│ │ ├── hyperlink.rs
│ │ ├── icons.rs
│ │ ├── ignore_globs.rs
│ │ ├── indicators.rs
│ │ ├── layout.rs
│ │ ├── literal.rs
│ │ ├── permission.rs
│ │ ├── recursion.rs
│ │ ├── size.rs
│ │ ├── sorting.rs
│ │ ├── symlink_arrow.rs
│ │ ├── symlinks.rs
│ │ ├── total_size.rs
│ │ └── truncate_owner.rs
│ ├── flags.rs
│ ├── git.rs
│ ├── git_theme.rs
│ ├── icon.rs
│ ├── main.rs
│ ├── meta/
│ │ ├── access_control.rs
│ │ ├── date.rs
│ │ ├── filetype.rs
│ │ ├── git_file_status.rs
│ │ ├── indicator.rs
│ │ ├── inode.rs
│ │ ├── links.rs
│ │ ├── locale.rs
│ │ ├── mod.rs
│ │ ├── name.rs
│ │ ├── owner.rs
│ │ ├── permissions.rs
│ │ ├── permissions_or_attributes.rs
│ │ ├── size.rs
│ │ ├── symlink.rs
│ │ ├── windows_attributes.rs
│ │ └── windows_utils.rs
│ ├── sort.rs
│ ├── theme/
│ │ ├── color.rs
│ │ ├── git.rs
│ │ └── icon.rs
│ └── theme.rs
└── tests/
└── integration.rs
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: zwpaper
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: kweizh
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
custom: # Replace with a single custom sponsorship URL
================================================
FILE: .github/ISSUE_TEMPLATE/bug.yml
================================================
name: Bug Report Form
description: Create a report to help us improve, by the new GitHub form
title: "[Bug]: "
labels: ["bug"]
assignees:
- zwpaper
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: checkboxes
id: latest-version
attributes:
label: Version
description: Please make sure you can reproduce in the [latest release](https://github.com/lsd-rs/lsd/releases/latest)
options:
- label: latest
required: true
- type: textarea
id: version
attributes:
label: version
description: "`lsd --version` output"
placeholder: lsd --version
validations:
required: true
- type: dropdown
id: os
attributes:
label: What OS are you seeing the problem on?
multiple: true
options:
- Windows
- Linux
- macOS
- Others
- type: textarea
id: installation
attributes:
label: installation
description: "how do you install lsd?"
placeholder: "how do you install lsd?"
validations:
required: true
- type: textarea
id: term
attributes:
label: term
description: "`echo $TERM` output"
placeholder: echo $TERM
validations:
required: false
- type: textarea
id: ls-colors
attributes:
label: ls-colors
description: "`echo $LS_COLORS` output"
placeholder: echo $LS_COLORS
validations:
required: false
- type: textarea
id: what-happened
attributes:
label: What happened?
description: Tell us what happen?
placeholder: |
If applicable, add the output of the classic ls command (`\ls -la`) in order to show the buggy file/directory.
render: markdown
validations:
required: true
- type: textarea
id: what-expected
attributes:
label: What expected?
description: What did you expect to happen?
placeholder: |
If the application panics run the command with the trace (`RUST_BACKTRACE=1 lsd ...`).
In case of graphical errors, add a screenshot if possible."
render: markdown
validations:
required: true
- type: textarea
id: others
attributes:
label: What else?
description: Is there anything else you want to tell us?
placeholder: "Others"
render: markdown
validations:
required: false
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
- os:
- `lsd --version`:
- `echo $TERM`:
- `echo $LS_COLORS`:
## Expected behavior
If applicable, add the output of the classic ls command (`\ls -la`) in order to show the buggy file/directory.
## Actual behavior
If the application panics run the command with the trace (`RUST_BACKTRACE=1 lsd ...`).
In case of graphical errors, add a screenshot if possible.
================================================
FILE: .github/PULL_REQUEST_TEMPLATE
================================================
<!--- PR Description --->
---
#### TODO
- [ ] Use `cargo fmt`
- [ ] Add necessary tests
- [ ] Update README (if applicable)
- [ ] Update config sample file in `doc/samples` (if applicable)
- [ ] Update icon sample file in `doc/samples` (if applicable)
- [ ] Update color sample file in `doc/samples` (if applicable)
- [ ] Update man page at lsd/doc/lsd.md (if applicable)
================================================
FILE: .github/workflows/CICD.yml
================================================
name: CICD
# spell-checker:ignore CICD CODECOV MSVC MacOS Peltoche SHAs buildable clippy dpkg esac fakeroot gnueabihf halium libssl mkdir musl popd printf pushd rustfmt softprops toolchain
env:
PROJECT_NAME: lsd
PROJECT_DESC: "An ls command with a lot of pretty colors."
PROJECT_AUTH: "Peltoche <peltoche@halium.fr>"
RUST_MIN_SRV: "1.85.0"
on: [push, pull_request]
jobs:
style:
name: Style
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job: [ { os: ubuntu-latest }, { os: macos-latest }, { os: windows-latest } ]
steps:
- uses: actions/checkout@v1
- name: Install `rust` toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_MIN_SRV }}
override: true
profile: minimal # minimal component installation (ie, no documentation)
components: rustfmt, clippy
- name: "`fmt` testing"
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- name: "`clippy` testing"
if: success() || failure() # run regardless of prior step ("`fmt` testing") success/failure
uses: actions-rs/cargo@v1
with:
command: clippy
args: --tests -- -D warnings
- name: "`clap` deprecated checks"
if: success() || failure() # run regardless of prior step ("`fmt` testing") success/failure
uses: actions-rs/cargo@v1
with:
command: check
args: --features clap/deprecated
min_version:
name: MinSRV # Minimum supported rust version
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Install `rust` toolchain (v${{ env.RUST_MIN_SRV }})
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_MIN_SRV }}
profile: minimal # minimal component installation (ie, no documentation)
- name: Test
uses: actions-rs/cargo@v1
with:
command: test
build:
name: Build
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job:
- { os: ubuntu-latest , target: arm-unknown-linux-gnueabihf , use-cross: use-cross }
- { os: ubuntu-latest , target: aarch64-unknown-linux-gnu , use-cross: use-cross }
- { os: ubuntu-latest , target: aarch64-unknown-linux-musl , use-cross: use-cross }
- { os: ubuntu-latest , target: i686-unknown-linux-gnu , use-cross: use-cross }
- { os: ubuntu-latest , target: i686-unknown-linux-musl , use-cross: use-cross }
- { os: ubuntu-latest , target: x86_64-unknown-linux-gnu , use-cross: use-cross }
- { os: ubuntu-latest , target: x86_64-unknown-linux-musl , use-cross: use-cross }
- { os: macos-latest , target: x86_64-apple-darwin }
- { os: macos-latest , target: aarch64-apple-darwin }
- { os: windows-latest , target: i686-pc-windows-gnu }
- { os: windows-latest , target: i686-pc-windows-msvc }
- { os: windows-latest , target: x86_64-pc-windows-gnu }
- { os: windows-latest , target: x86_64-pc-windows-msvc }
outputs:
DEPLOY: ${{ steps.vars.outputs.DEPLOY }}
steps:
- uses: actions/checkout@v1
- name: Install any prerequisites
shell: bash
run: |
case ${{ matrix.job.target }} in
arm-*-linux-*hf) sudo apt-get -y update ; sudo apt-get -y install binutils-arm-linux-gnueabihf ;;
aarch64-*-linux-*) sudo apt-get -y update ; sudo apt-get -y install binutils-aarch64-linux-gnu ;;
esac
- name: Initialize workflow variables
id: vars
shell: bash
run: |
# toolchain
TOOLCHAIN="stable" ## default to "stable" toolchain
# * specify alternate TOOLCHAIN for *-pc-windows-gnu targets; gnu targets on Windows are broken for the standard *-pc-windows-msvc toolchain (refs: <https://github.com/rust-lang/rust/issues/47048>, <https://github.com/rust-lang/rust/issues/53454>, <https://github.com/rust-lang/cargo/issues/6754>)
case ${{ matrix.job.target }} in *-pc-windows-gnu) TOOLCHAIN="stable-${{ matrix.job.target }}" ;; esac;
# * use requested TOOLCHAIN if specified
if [ -n "${{ matrix.job.toolchain }}" ]; then TOOLCHAIN="${{ matrix.job.toolchain }}" ; fi
echo set-output name=TOOLCHAIN::${TOOLCHAIN}
echo ::set-output name=TOOLCHAIN::${TOOLCHAIN}
# staging directory
STAGING='_staging'
echo set-output name=STAGING::${STAGING}
echo ::set-output name=STAGING::${STAGING}
# determine EXE suffix
EXE_suffix="" ; case ${{ matrix.job.target }} in *-pc-windows-*) EXE_suffix=".exe" ;; esac;
echo set-output name=EXE_suffix::${EXE_suffix}
echo ::set-output name=EXE_suffix::${EXE_suffix}
# parse commit reference info
REF_NAME=${GITHUB_REF#refs/*/}
unset REF_BRANCH ; case ${GITHUB_REF} in refs/heads/*) REF_BRANCH=${GITHUB_REF#refs/heads/} ;; esac;
unset REF_TAG ; case ${GITHUB_REF} in refs/tags/*) REF_TAG=${GITHUB_REF#refs/tags/} ;; esac;
REF_SHAS=${GITHUB_SHA:0:8}
echo set-output name=REF_NAME::${REF_NAME}
echo set-output name=REF_BRANCH::${REF_BRANCH}
echo set-output name=REF_TAG::${REF_TAG}
echo set-output name=REF_SHAS::${REF_SHAS}
echo ::set-output name=REF_NAME::${REF_NAME}
echo ::set-output name=REF_BRANCH::${REF_BRANCH}
echo ::set-output name=REF_TAG::${REF_TAG}
echo ::set-output name=REF_SHAS::${REF_SHAS}
# package name
PKG_suffix=".tar.gz" ; case ${{ matrix.job.target }} in *-pc-windows-*) PKG_suffix=".zip" ;; esac;
PKG_BASENAME=${PROJECT_NAME}-${REF_TAG:-$REF_SHAS}-${{ matrix.job.target }}
PKG_NAME=${PKG_BASENAME}${PKG_suffix}
echo set-output name=PKG_suffix::${PKG_suffix}
echo set-output name=PKG_BASENAME::${PKG_BASENAME}
echo set-output name=PKG_NAME::${PKG_NAME}
echo ::set-output name=PKG_suffix::${PKG_suffix}
echo ::set-output name=PKG_BASENAME::${PKG_BASENAME}
echo ::set-output name=PKG_NAME::${PKG_NAME}
# deployable tag? (ie, leading "vM" or "M"; M == version number)
unset DEPLOY ; if [[ $REF_TAG =~ ^[vV]?[0-9].* ]]; then DEPLOY='true' ; fi
echo set-output name=DEPLOY::${DEPLOY:-<empty>/false}
echo ::set-output name=DEPLOY::${DEPLOY}
# DPKG architecture?
unset DPKG_ARCH ; case ${{ matrix.job.target }} in aarch64-*-linux-*) DPKG_ARCH=arm64 ;; i686-*-linux-*) DPKG_ARCH=i686 ;; x86_64-*-linux-*) DPKG_ARCH=amd64 ;; esac;
echo set-output name=DPKG_ARCH::${DPKG_ARCH}
echo ::set-output name=DPKG_ARCH::${DPKG_ARCH}
# DPKG version?
unset DPKG_VERSION ; if [[ $REF_TAG =~ ^[vV]?[0-9].* ]]; then DPKG_VERSION=${REF_TAG/#[vV]/} ; fi
echo set-output name=DPKG_VERSION::${DPKG_VERSION}
echo ::set-output name=DPKG_VERSION::${DPKG_VERSION}
# DPKG base name/conflicts?
DPKG_BASENAME=${PROJECT_NAME}
DPKG_CONFLICTS=${PROJECT_NAME}-musl
case ${{ matrix.job.target }} in *-musl) DPKG_BASENAME=${PROJECT_NAME}-musl ; DPKG_CONFLICTS=${PROJECT_NAME} ;; esac;
echo set-output name=DPKG_BASENAME::${DPKG_BASENAME}
echo set-output name=DPKG_CONFLICTS::${DPKG_CONFLICTS}
echo ::set-output name=DPKG_BASENAME::${DPKG_BASENAME}
echo ::set-output name=DPKG_CONFLICTS::${DPKG_CONFLICTS}
# DPKG name
unset DPKG_NAME;
if [[ -n $DPKG_ARCH && -n $DPKG_VERSION ]]; then DPKG_NAME="${DPKG_BASENAME}_${DPKG_VERSION}_${DPKG_ARCH}.deb" ; fi
echo set-output name=DPKG_NAME::${DPKG_NAME}
echo ::set-output name=DPKG_NAME::${DPKG_NAME}
# target-specific options
# * CARGO_USE_CROSS (truthy)
CARGO_USE_CROSS='true' ; case '${{ matrix.job.use-cross }}' in ''|0|f|false|n|no) unset CARGO_USE_CROSS ;; esac;
echo set-output name=CARGO_USE_CROSS::${CARGO_USE_CROSS:-<empty>/false}
echo ::set-output name=CARGO_USE_CROSS::${CARGO_USE_CROSS}
# * test only binary for arm-type targets
unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-* | aarch64-*-linux-*) CARGO_TEST_OPTIONS="--bin ${PROJECT_NAME}" ;; esac;
echo set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS}
echo ::set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS}
# * strip executable?
STRIP="strip" ; case ${{ matrix.job.target }} in arm-*-linux-*hf) STRIP="arm-linux-gnueabihf-strip" ;; aarch64-*-linux-*) STRIP="aarch64-linux-gnu-strip" ;; *-pc-windows-msvc) STRIP="" ;; esac;
echo set-output name=STRIP::${STRIP}
echo ::set-output name=STRIP::${STRIP}
- name: Create all needed build/work directories
shell: bash
run: |
mkdir -p '${{ steps.vars.outputs.STAGING }}'
mkdir -p '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}'
mkdir -p '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/autocomplete'
mkdir -p '${{ steps.vars.outputs.STAGING }}/dpkg'
- name: Update manpage placeholders
shell: bash
run: |
LSD_VERSION="$(if echo "$GITHUB_REF" | grep -q '^refs/tags'; then echo "${GITHUB_REF#refs/*/}"; else echo; fi)"
sed -i.bk "s|footer: lsd <version>|footer: lsd $LSD_VERSION|" doc/lsd.md
sed -i.bk "s|date: <date>|date: $(date '+%Y-%m-%d')|" doc/lsd.md
rm doc/lsd.md.bk
- name: Setup pandoc
uses: r-lib/actions/setup-pandoc@v2
- name: Generate Manpage
run: pandoc --standalone --to man doc/lsd.md -o lsd.1
- name: Install `rust` toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ steps.vars.outputs.TOOLCHAIN }}
target: ${{ matrix.job.target }}
override: true
profile: minimal # minimal component installation (ie, no documentation)
- name: Build
uses: actions-rs/cargo@v1
with:
use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }}
command: build
args: --release --target=${{ matrix.job.target }} --locked
- name: Test
if: matrix.job.target != 'aarch64-apple-darwin'
uses: actions-rs/cargo@v1
with:
use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }}
command: test
args: --target=${{ matrix.job.target }} ${{ steps.vars.outputs.CARGO_TEST_OPTIONS}}
- name: Archive executable artifacts
uses: actions/upload-artifact@master
with:
name: ${{ env.PROJECT_NAME }}-${{ matrix.job.target }}
path: target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}
- name: Package
shell: bash
run: |
# binary
cp 'target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}' '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/'
# `strip` binary (if needed)
if [ -n "${{ steps.vars.outputs.STRIP }}" ]; then "${{ steps.vars.outputs.STRIP }}" '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}' ; fi
# README and LICENSE
cp README.md '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/'
cp LICENSE '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/'
# manpage
cp lsd.1 '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/'
# autocomplete
cp 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}-'*/'out/${{ env.PROJECT_NAME }}.bash' '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/autocomplete/${{ env.PROJECT_NAME }}.bash-completion'
cp 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}-'*/'out/${{ env.PROJECT_NAME }}.fish' '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/autocomplete/'
cp 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}-'*/'out/_${{ env.PROJECT_NAME }}.ps1' '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/autocomplete/'
cp 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}-'*/'out/_${{ env.PROJECT_NAME }}' '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/autocomplete/'
# base compressed package
pushd '${{ steps.vars.outputs.STAGING }}/' >/dev/null
case ${{ matrix.job.target }} in
*-pc-windows-*) 7z -y a '${{ steps.vars.outputs.PKG_NAME }}' '${{ steps.vars.outputs.PKG_BASENAME }}'/* | tail -2 ;;
*) tar czf '${{ steps.vars.outputs.PKG_NAME }}' '${{ steps.vars.outputs.PKG_BASENAME }}'/* ;;
esac;
popd >/dev/null
# dpkg
if [ -n "${{ steps.vars.outputs.DPKG_NAME }}" ]; then
DPKG_DIR="${{ steps.vars.outputs.STAGING }}/dpkg"
# binary
install -Dm755 'target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}' "${DPKG_DIR}/usr/bin/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}"
if [ -n "${{ steps.vars.outputs.STRIP }}" ]; then "${{ steps.vars.outputs.STRIP }}" "${DPKG_DIR}/usr/bin/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}" ; fi
# README and LICENSE
install -Dm644 README.md "${DPKG_DIR}/usr/share/doc/${{ env.PROJECT_NAME }}/README.md"
install -Dm644 LICENSE "${DPKG_DIR}/usr/share/doc/${{ env.PROJECT_NAME }}/LICENSE"
# (auto-)completions
install -Dm644 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}-'*/'out/${{ env.PROJECT_NAME }}.bash' "${DPKG_DIR}/usr/share/bash-completion/completions/${{ env.PROJECT_NAME }}"
install -Dm644 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}-'*/'out/${{ env.PROJECT_NAME }}.fish' "${DPKG_DIR}/usr/share/fish/vendor_completions.d/${{ env.PROJECT_NAME }}.fish"
install -Dm644 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}-'*/'out/_${{ env.PROJECT_NAME }}' "${DPKG_DIR}/usr/share/zsh/vendor-completions/_${{ env.PROJECT_NAME }}"
# control file
mkdir -p "${DPKG_DIR}/DEBIAN"
printf "Package: ${{ steps.vars.outputs.DPKG_BASENAME }}\nVersion: ${{ steps.vars.outputs.DPKG_VERSION }}\nSection: utils\nPriority: optional\nMaintainer: ${{ env.PROJECT_AUTH }}\nArchitecture: ${{ steps.vars.outputs.DPKG_ARCH }}\nProvides: ${{ env.PROJECT_NAME }}\nConflicts: ${{ steps.vars.outputs.DPKG_CONFLICTS }}\nDescription: ${{ env.PROJECT_DESC }}\n" > "${DPKG_DIR}/DEBIAN/control"
## cat "${DPKG_DIR}/DEBIAN/control"
# build dpkg
fakeroot dpkg-deb --build "${DPKG_DIR}" "${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.DPKG_NAME }}"
# build a deb not using zst
# check https://github.com/lsd-rs/lsd/issues/891
ar x "${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.DPKG_NAME }}"
# Uncompress zstd files an re-compress them using xz
zstd -d < control.tar.zst | xz > control.tar.xz
zstd -d < data.tar.zst | xz > data.tar.xz
# Re-create the Debian package in /tmp/
xz_deb="$(echo ${{ steps.vars.outputs.DPKG_NAME }} | sed 's/.deb/_xz.deb/g')"
ar -m -c -a sdsd ${xz_deb} debian-binary control.tar.xz data.tar.xz
mv ${xz_deb} ${{ steps.vars.outputs.STAGING }}/
# Clean up
rm debian-binary control.tar.xz data.tar.xz control.tar.zst data.tar.zst
fi
- name: Publish
uses: softprops/action-gh-release@v1
if: steps.vars.outputs.DEPLOY
with:
files: |
${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_NAME }}
${{ steps.vars.outputs.STAGING }}/*.deb
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# container_build:
# name: Container Build
# runs-on: ${{ matrix.job.os }}
# strategy:
# fail-fast: false
# matrix:
# job:
# - { os: ubuntu-latest, target: loongarch64-unknown-linux-gnu, platform: loong64 }
# steps:
# - uses: actions/checkout@v1
# - name: Set up QEMU
# uses: docker/setup-qemu-action@v3
# with:
# image: tonistiigi/binfmt:master
# - name: Containerized Build
# # containerized build is slow, but easy to setup and cross compile
# # currently only used for:
# # - loongarch64
# #
# # tests should be done previously
# shell: bash
# run: |
# docker run --platform linux/${{ matrix.job.platform }} \
# -v `pwd`:/src \
# pkgforge/alpine-rust-stable:loongarch64 \
# cargo build --release --target ${{ matrix.job.target }}
#
# # determine EXE suffix
# EXE_suffix="" ; case ${{ matrix.job.target }} in *-pc-windows-*) EXE_suffix=".exe" ;; esac;
# echo "EXE_suffix=${EXE_suffix}" >> $GITHUB_OUTPUT
# - name: Archive executable artifacts
# uses: actions/upload-artifact@master
# with:
# name: ${{ env.PROJECT_NAME }}-${{ matrix.job.target }}
# path: target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}
# - name: Release
# uses: softprops/action-gh-release@v1
# if: startsWith(github.ref, 'refs/tags/')
# with:
# files: |
# target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
winget:
runs-on: ubuntu-latest
needs: build
if: ${{ needs.build.outputs.DEPLOY }}
steps:
- name: Publish to Winget
uses: vedantmgoyal9/winget-releaser@main
with:
identifier: lsd-rs.lsd
installers-regex: 'pc-windows-msvc\.zip$'
token: ${{ secrets.WINGET_TOKEN }}
coverage:
name: Code Coverage
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: true
matrix:
# job: [ { os: ubuntu-latest }, { os: macos-latest }, { os: windows-latest } ]
job: [ { os: ubuntu-latest } ] ## cargo-tarpaulin is currently only available on linux
steps:
- uses: actions/checkout@v1
# - name: Reattach HEAD ## may be needed for accurate code coverage info
# run: git checkout ${{ github.head_ref }}
- name: Initialize workflow variables
id: vars
shell: bash
run: |
# staging directory
STAGING='_staging'
echo set-output name=STAGING::${STAGING}
echo ::set-output name=STAGING::${STAGING}
# check for CODECOV_TOKEN availability (work-around for inaccessible 'secrets' object for 'if'; see <https://github.community/t5/GitHub-Actions/jobs-lt-job-id-gt-if-does-not-work-with-env-secrets/m-p/38549>)
unset HAS_CODECOV_TOKEN
if [ -n $CODECOV_TOKEN ]; then HAS_CODECOV_TOKEN='true' ; fi
echo set-output name=HAS_CODECOV_TOKEN::${HAS_CODECOV_TOKEN}
echo ::set-output name=HAS_CODECOV_TOKEN::${HAS_CODECOV_TOKEN}
env:
CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}"
- name: Create all needed build/work directories
shell: bash
run: |
mkdir -p '${{ steps.vars.outputs.STAGING }}/work'
- name: Install required packages
run: |
sudo apt-get -y install libssl-dev
pushd '${{ steps.vars.outputs.STAGING }}/work' >/dev/null
wget --no-verbose https://github.com/xd009642/tarpaulin/releases/download/0.32.8/cargo-tarpaulin-x86_64-unknown-linux-musl.tar.gz
tar xf cargo-tarpaulin-x86_64-unknown-linux-musl.tar.gz
cp cargo-tarpaulin "$(dirname -- "$(which cargo)")"/
popd >/dev/null
- name: Generate coverage
run: |
cargo tarpaulin --out Xml
- name: Upload coverage results (CodeCov.io)
# CODECOV_TOKEN (aka, "Repository Upload Token" for REPO from CodeCov.io) ## set via REPO/Settings/Secrets
# if: secrets.CODECOV_TOKEN (not supported {yet?}; see <https://github.community/t5/GitHub-Actions/jobs-lt-job-id-gt-if-does-not-work-with-env-secrets/m-p/38549>)
if: steps.vars.outputs.HAS_CODECOV_TOKEN
run: |
# CodeCov.io
cargo tarpaulin --out Xml
bash <(curl -s https://codecov.io/bash)
env:
CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}"
================================================
FILE: .gitignore
================================================
/target
out.md
**/*.rs.bk
================================================
FILE: .release.toml
================================================
sign-commit = true
sign-tag = true
dev-version = false
pre-release-commit-message = "Release {{version}}"
tag-prefix = ""
tag-name = "{{version}}"
pre-release-replacements = [
{file="CHANGELOG.md", search="## \\[Unreleased\\]", replace="## [Unreleased]\n\n## [{{version}}] - {{date}}"},
{file="CHANGELOG.md", search="HEAD", replace="{{version}}"},
{file="CHANGELOG.md", search="\\[Unreleased\\]:", replace="[Unreleased]: https://github.com/Peltoche/lsd/compare/{{version}}...HEAD\n[{{version}}]: "},
{file="README.md", search="lsd_[0-9\\.]+_amd64.deb", replace="lsd_{{version}}_amd64.deb"},
]
================================================
FILE: CODEOWNERS
================================================
* @zwpaper
# Retired
# @meain @Peltoche
================================================
FILE: Cargo.toml
================================================
[package]
authors = ["Peltoche <dev@halium.fr>"]
build = "build.rs"
categories = ["command-line-utilities"]
description = "An ls command with a lot of pretty colors and some other stuff."
keywords = ["ls"]
license = "Apache-2.0"
name = "lsd"
readme = "./README.md"
repository = "https://github.com/lsd-rs/lsd"
version = "1.2.0"
edition = "2024"
rust-version = "1.85"
[[bin]]
name = "lsd"
path = "src/main.rs"
[build-dependencies]
clap = { version = "4.5.*", features = ["derive"] }
clap_complete = "4.5.*"
version_check = "0.9.*"
[dependencies]
crossterm = { version = "0.29.0", features = ["serde"] }
dirs = "6"
libc = "0.2.*"
human-sort = "0.2.2"
# should stick to 0.1, the 0.2 needs some adaptation
# check https://github.com/lsd-rs/lsd/issues/1014
term_grid = "0.1"
terminal_size = "0.4.*"
thiserror = "2.0"
sys-locale = "0.3"
#once_cell = "1.17.1"
once_cell = "1.21"
chrono = { version = "0.4", features = ["unstable-locales"] }
chrono-humanize = "0.2"
# incompatible with v0.1.11
unicode-width = "0.2"
lscolors = "0.20.0"
wild = "2.2"
globset = "0.4.*"
yaml-rust = "0.4.*"
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.9"
url = "2.5.4"
vsort = "0.2"
xdg = "2.5"
[target."cfg(not(all(windows, target_arch = \"x86\", target_env = \"gnu\")))".dependencies]
# if ssl feature is enabled compilation will fail on arm-unknown-linux-gnueabihf and i686-pc-windows-gnu
git2 = { version = "0.20", optional = true, default-features = false }
[target.'cfg(unix)'.dependencies]
users = { version = "0.11.3", package = "uzers" }
xattr = "1"
[target.'cfg(windows)'.dependencies]
windows = { version = "0.61.3", features = ["Win32_Foundation", "Win32_Security_Authorization", "Win32_Storage_FileSystem", "Win32_System_Memory"] }
[dependencies.clap]
features = ["derive", "wrap_help"]
version = "4.5.*"
[dev-dependencies]
assert_cmd = "2"
assert_fs = "1"
predicates = "3"
tempfile = "3"
serial_test = "2.0"
temp-env = "0.3.0"
[features]
default = ["git2"]
sudo = []
no-git = [] # force disabling git even if available by default
[profile.release]
lto = true
codegen-units = 1
strip = true
debug = false
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: OWNERS
================================================
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- zwpaper
================================================
FILE: README.md
================================================
<div align="center">
<sup>Special thanks to:</sup>
<br>
<sup>Maintained with ❤️ + 🤖 by Pochi</sup>
<br>
<br>
<a href="https://app.getpochi.com">
<img alt="Pochi AI Coding Assistant" width="160" src="https://github.com/TabbyML/pochi/blob/main/packages/vscode/assets/icons/logo128.png?raw=true">
</a>
### [Pochi is an AI agent designed for software development.](https://app.getpochi.com)
[It operates within your IDE, using a toolkit of commands to write and refactor code autonomously across your entire project.](https://app.getpochi.com)<br>
</div>
---
> [!IMPORTANT]
> This is the documentation for the development version of lsd.
> Please consult the documentation on [the Tags page](https://github.com/lsd-rs/lsd/tags) if you are looking for the documentation of individual releases.
>
> The current newest release is: [v1.2.0](https://github.com/lsd-rs/lsd/tree/v1.2.0)
# LSD (LSDeluxe)
[](https://raw.githubusercontent.com/lsd-rs/lsd/master/LICENSE)
[](https://crates.io/crates/lsd)
[](https://github.com/lsd-rs/lsd/actions)
[](https://codecov.io/gh/lsd-rs/lsd)
[](https://repology.org/project/lsd/versions)

This project is a rewrite of GNU `ls` with lots of added features like colors, icons, tree-view, more formatting options etc.
The project is heavily inspired by the super [colorls](https://github.com/athityakumar/colorls) project.
## Installation
### Prerequisites
>[!TIP]
> Have a look at the [Nerd Font README](https://github.com/ryanoasis/nerd-fonts/blob/master/readme.md) for help with installing Nerd Fonts
1. In order for icons to work you need to have a patched font like [nerd-font](https://www.nerdfonts.com) or [font-awesome](https://fontawesome.com) installed on your machine and your terminal needs to be configured to use the patched font of your choosing.
2. If you intend to install `lsd` from source you need to have a working Rust toolchain (obviously) on your machine.
### Installing with a package manager
<details>
<summary>Packaging status</summary>
<a href="https://repology.org/project/lsd/versions">
<img src="https://repology.org/badge/vertical-allrepos/lsd.svg?columns=3" alt="Packaging status">
</a>
</details>
Please consult the table below for the installation command associated with your package manager.
| OS/Distro | Command |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------|
| Archlinux | `pacman -S lsd` |
| Fedora | `dnf install lsd` |
| Gentoo | `sudo emerge sys-apps/lsd` |
| macOS | `brew install lsd` or `sudo port install lsd` |
| Nix (non NixOS) | [`nix-env -iA nixpkgs.lsd`](https://search.nixos.org/packages?show=lsd) |
| NixOS | Add [`environment.systemPackages = [ pkgs.lsd ];`](https://search.nixos.org/packages?show=lsd) to your system config |
| FreeBSD | `pkg install lsd` |
| NetBSD or any `pkgsrc` platform | `pkgin install lsd` or `cd /usr/pkgsrc/sysutils/lsd && make install` |
| OpenBSD | `pkg_add lsd` |
| Windows | `scoop install lsd` or `winget install --id lsd-rs.lsd` or `choco install lsd` |
| Android (via Termux) | `pkg install lsd` |
| Debian sid and bookworm | `apt install lsd` |
| Ubuntu 23.04 (Lunar Lobster) | `apt install lsd` |
| Earlier Ubuntu/Debian versions | **snap discontinued**, use the method described [here](#installing-binaries-directly) instead |
| Solus | `eopkg it lsd` |
| Void Linux | `sudo xbps-install lsd` |
| openSUSE | `sudo zypper install lsd` |
### Installing from source
With [Rust's package manager cargo](https://doc.rust-lang.org/stable/cargo/), you can install lsd via:
```sh
cargo install lsd
```
And if you want to install the latest `main` branch commit you can do so via:
```sh
cargo install --git https://github.com/lsd-rs/lsd.git --branch main
```
### Installing binaries directly
The [release page](https://github.com/lsd-rs/lsd/releases) includes precompiled binaries for Linux, macOS, and Windows for every release. You can also get the latest binary of the `main` branch from the [GitHub action build artifacts](https://github.com/lsd-rs/lsd/actions?query=branch%3Amain+is%3Asuccess+event%3Apush) (choose the top action and then scroll down to the artifacts section).
#### Configuring your shell to use lsd instead of ls (optional)
In order to use lsd instead of entering the `ls` command, you need to create an alias for ls in to your shell configuration file (`~/.bashrc`, `~/.zshrc`, etc...). The simplest variant of such an alias is:
```sh
alias ls='lsd'
```
The alias above will replace a stock ls command with an lsd command without additional parameters.
Some examples of other useful aliases are:
```sh
alias l='lsd -l'
alias la='lsd -a'
alias lla='lsd -la'
alias lt='lsd --tree'
```
## Customizing lsd (configuration and theming)
> [!TIP]
> In order to make the customization process easier for you we’ve supplied sample files. These files contain the entries for all the defaults that `lsd` comes with after installation. You can find the sample files in the [documentation folder](./doc).
>
> We've also supplied a [color reference](./doc/colors.md) where we’ve documented the default colors `lsd` uses in its output. You can also preview there.
In order to tailor `lsd` to your specific needs you can create any of the following three files and make adjustments as you see fit.
1. `config.yaml` → [config sample file here](./doc/samples/config-sample.yaml)
2. `colors.yaml` → [colors sample file here](./doc/samples/colors-sample.yaml)
3. `icons.yaml` → [icons sample file here](./doc/samples/icons-sample.yaml)
Note that it is _not_ required to have all three of the files present in order for your configuration to be applied. For example, if you [only want to customize the icons](#customization-example) then only [`icons.yaml`](./doc/icons-sample.yaml) needs to be present in the [configuration directory](#config-file-locations); [`config.yaml`](./doc/config-sample.yaml), and [`colors.yaml`](./doc/colors-sample.yaml) do not have to be present in order for your icon modifications to be applied.
### Config file locations
> [!TIP]
> You can also instruct `lsd` to look for configuration files in a custom location of your choosing by using the following command: `lsd --config-file [YOUR_CUSTOM_PATH]`. This is particularly useful when testing a configuration changes before committing to them.
#### Unix (Linux, Mac, etc...)
On non-Windows systems `lsd` follows the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html), thus `lsd` will look for configuration files any of the following locations:
- `$HOME/.config/lsd`
- `$XDG_CONFIG_HOME/lsd`
On most systems these variables are mapped to the same location, which is usually `~/.config/lsd/`. If `lsd` does not detect the location, or if the location exists but does not contain any of the three configuration files, the default configuration will be used instead.
#### Windows
On Windows systems `lsd` will look for configuration files in the following locations, **in order**:
1. `%USERPROFILE%\.config\lsd`
2. `%APPDATA%\lsd`
These locations are usually something like `C:\Users\username\AppData\Roaming\lsd\`, and `C:\Users\username\.config\lsd\` respectively.
### Quick customization example
For this example let's assume you're already content `lsd`, but there are a few of the default icons that really bug you and you want to change them to something that suits your needs better. All you have to do is create an `icons.yaml` file in the [configuration directory](#config-file-locations) and configure your custom icon there. Here’s how.
There are 3 kinds of icon overrides available in `lsd`:
- `name`
- `filetype`
- `extension`
Both nerd font glyphs and Unicode emojis can be used for icons. The final set of icons that `lsd` will use is a combination of the [default icons](./src/theme/icon.rs) with the custom icons you’ve set in the `icons.yaml` file.
> [!NOTE]
> Aside from the [icon sample file](./doc/icons-sample.yaml), you can also find the default icon set in the source code [here](src/theme/icon.rs).
A short example for each type of the icon overrides is shown below.
```yaml
name:
.trash:
.cargo:
.emacs.d:
a.out:
extension:
go:
hs:
rs: 🦀
filetype:
dir: 📂
file: 📄
pipe: 📩
socket:
executable:
symlink-dir:
symlink-file:
device-char:
device-block:
special:
```
## F.A.Q and troubleshooting
### How can I enable nerd fonts using xresources?
To enable nerd fonts for your terminal, URxvt for example, in `.Xresources` take a look at the example below:
```sh
URxvt*font: xft:Hack Nerd Font:style=Regular:size=11
```
### Why am I seeing `Uses unknown compression for member ‘control.tar.zst'` when using deb?
Zst compression is only supported from `Debian 12`, `Ubuntu 21.10`, and upward. Starting from `lsd v1.1.0` please use the `_xz.deb` release instead. See [this issue](https://github.com/lsd-rs/lsd/issues/891) for additional details and manual fixes.
### How can I set custom color schemes for Windows?
In order to display a custom color scheme `lsd` reads a system environment variable called `LS_COLORS`. If your custom color scheme is not working `LS_COLORS` is most likely missing. Please look at [the marked solution in this post](https://github.com/orgs/lsd-rs/discussions/958#discussioncomment-7659375), which contains instructions on how to set a custom color scheme on Windows for guidance.
### Why are icons not showing up
> [!IMPORTANT]
> Always check if the font you are using is correctly set up! Run the following snippet in your terminal emulator and verify that the output [prints a folder icon](https://github.com/lsd-rs/lsd/issues/510#issuecomment-860000306). If it prints a box, or question mark, or something else, then you might have some issues in how you set up the font or how your terminal emulator renders the font.
>
> ```sh
> echo $'\uf115'
> ```
For `lsd` to be able to display icons the font has to include special font glyphs. If icons are not being displayed it could be the case that your current font does not include such glyphs. Thankfully, you can patch most fonts using [NerdFont](https://www.nerdfonts.com/) and add these icons to your current font.
Alternatively, you can also download an already patched version of your favorite font from the [NerdFont font download page](https://www.nerdfonts.com/font-downloads).
Here is a guide on how to set up fonts on [macOS](https://github.com/lsd-rs/lsd/issues/199#issuecomment-494218334), and on [Android](https://github.com/lsd-rs/lsd/issues/423).
### Why are Icons missing or not rendering correctly using PuTTY/KiTTY on Windows?
First of all, make sure a patched font is available on your local machine and that PuTTY/KiTTY is configured to use that font. If you are not certain what this entails please read the [Prerequisites](#prerequisites).
Please note that there are problems for PuTTY/KiTTY displaying 2 character wide icons which may be the case for the font you configured. To ensure only 1 character wide icons are used by your font, please select a font like [Hack Regular Nerd Font Complete Mono Windows Compatible](https://github.com/ryanoasis/nerd-fonts/blob/master/patched-fonts/Hack/Regular/complete/Hack%20Regular%20Nerd%20Font%20Complete%20Mono%20Windows%20Compatible.ttf) (see [this issue](https://github.com/lsd-rs/lsd/issues/331) for further details).
### Why is the first character of a folder/file getting trimmed?
> [!NOTE]
> **Workaround for [Konsole](https://apps.kde.org/konsole/):** edit the config file (or create it if it doesn't already exist) and paste the following configuration directive into it.
>
> ```yaml
> # CAREFUL: use copy-paste because this block contains invisible Unicode characters!
> icons:
> separator: " ㅤ"
> ```
This is a known issue in a few terminal emulators. Try using a different terminal emulator like [Alacritty](https://github.com/alacritty/alacritty) or [Kitty](https://github.com/kovidgoyal/kitty) and see if these suit your needs.
You might also want to check if your font is responsible for causing this. To verify this, try running lsd with icons disabled and if the first character is still being trimmed, you’ve discovered a bug in `lsd`. Until the bug is fixed you can use the following command as workaround:
```sh
lsd --icon never --ignore-config
```
### Why are there weird (UTF-8) characters in the output?
`lsd` will always attempt to display the UTF-8 characters in file name, but a `U+FFFD REPLACEMENT CHARACTER`(�) is used to represent the invalid UTF-8 characters. If you are seeing this in your `lsd` output your filename contains an invalid UTF-8 character.
### Why are the icons are showing up strangely?
Nerd Fonts is moving the code points of the Material Design Icons in version 3.0, so starting from #830 `lsd` is using an updated the icon set. If your icons look weird, use fonts that have been patched using [Nerd Fonts v2.3.0](https://github.com/ryanoasis/nerd-fonts/releases/tag/v2.3.3) or later.
## Contributors
Everyone can contribute to this project, improving the code or adding functions. If anyone wants something to be added we will try to do it.
As this is being updated regularly, don't forget to rebase your fork before creating a pull-request.
## Credits
Special thanks to:
- [meain](https://github.com/meain) for all his contributions and reviews
- [danieldulaney](https://github.com/danieldulaney) for the Windows integration
- [sharkdp](https://github.com/sharkdp) and his superb [fd](https://github.com/sharkdp/fd) from which I have stolen a lot of CI stuff.
- [athityakumar](https://github.com/athityakumar) for the project [colorls](https://github.com/athityakumar/colorls)
- [All the other contributors](https://github.com/lsd-rs/lsd/graphs/contributors)
================================================
FILE: build.rs
================================================
// Copyright (c) 2017 fd developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
use clap::CommandFactory;
use clap_complete::generate_to;
use clap_complete::shells::*;
use std::fs;
use std::process::exit;
include!("src/app.rs");
fn main() {
let outdir = std::env::var_os("SHELL_COMPLETIONS_DIR")
.or_else(|| std::env::var_os("OUT_DIR"))
.unwrap_or_else(|| exit(0));
fs::create_dir_all(&outdir).unwrap();
let mut app = Cli::command();
let bin_name = "lsd";
generate_to(Bash, &mut app, bin_name, &outdir).expect("Failed to generate Bash completions");
generate_to(Fish, &mut app, bin_name, &outdir).expect("Failed to generate Fish completions");
generate_to(Zsh, &mut app, bin_name, &outdir).expect("Failed to generate Zsh completions");
generate_to(PowerShell, &mut app, bin_name, &outdir)
.expect("Failed to generate PowerShell completions");
// Disable git feature for these target where git2 is not well supported
if !std::env::var("CARGO_FEATURE_GIT2")
.map(|flag| flag == "1")
.unwrap_or(false)
|| std::env::var("TARGET")
.map(|target| target == "i686-pc-windows-gnu")
.unwrap_or(false)
{
println!(r#"cargo:rustc-cfg=feature="no-git""#);
}
}
================================================
FILE: ci/before_deploy.bash
================================================
#!/usr/bin/env bash
# Building and packaging for release
set -ex
build() {
cargo build --target "$TARGET" --features="$FEATURES" --release --verbose
}
pack() {
local tempdir
local out_dir
local package_name
local gcc_prefix
tempdir=$(mktemp -d 2>/dev/null || mktemp -d -t tmp)
out_dir=$(pwd)
package_name="$PROJECT_NAME-$TRAVIS_TAG-$TARGET"
if [[ $TARGET == arm-unknown-linux-* ]]; then
gcc_prefix="arm-linux-gnueabihf-"
else
gcc_prefix=""
fi
# create a "staging" directory
mkdir "$tempdir/$package_name"
mkdir "$tempdir/$package_name/autocomplete"
# copying the main binary
cp "target/$TARGET/release/$PROJECT_NAME" "$tempdir/$package_name/"
"${gcc_prefix}"strip "$tempdir/$package_name/$PROJECT_NAME"
# manpage, readme and license
cp README.md "$tempdir/$package_name"
cp LICENSE "$tempdir/$package_name"
# various autocomplete
cp target/"$TARGET"/release/build/"$PROJECT_NAME"-*/out/"$PROJECT_NAME".bash "$tempdir/$package_name/autocomplete/${PROJECT_NAME}.bash-completion"
cp target/"$TARGET"/release/build/"$PROJECT_NAME"-*/out/"$PROJECT_NAME".fish "$tempdir/$package_name/autocomplete"
cp target/"$TARGET"/release/build/"$PROJECT_NAME"-*/out/_"$PROJECT_NAME" "$tempdir/$package_name/autocomplete"
# archiving
pushd "$tempdir"
tar czf "$out_dir/$package_name.tar.gz" "$package_name"/*
popd
rm -r "$tempdir"
}
make_deb() {
local tempdir
local architecture
local version
local dpkgname
local conflictname
case $TARGET in
x86_64*)
architecture=amd64
;;
i686*)
architecture=i386
;;
*)
echo "make_deb: skipping target '${TARGET}'" >&2
return 0
;;
esac
version=${TRAVIS_TAG#v}
if [[ $TARGET = *musl* ]]; then
dpkgname=$PROJECT_NAME-musl
conflictname=$PROJECT_NAME
else
dpkgname=$PROJECT_NAME
conflictname=$PROJECT_NAME-musl
fi
tempdir=$(mktemp -d 2>/dev/null || mktemp -d -t tmp)
# copy the main binary
install -Dm755 "target/$TARGET/release/$PROJECT_NAME" "$tempdir/usr/bin/$PROJECT_NAME"
strip "$tempdir/usr/bin/$PROJECT_NAME"
# readme and license
install -Dm644 README.md "$tempdir/usr/share/doc/$PROJECT_NAME/README.md"
install -Dm644 LICENSE "$tempdir/usr/share/doc/$PROJECT_NAME/LICENSE"
# completions
install -Dm644 target/$TARGET/release/build/$PROJECT_NAME-*/out/$PROJECT_NAME.bash "$tempdir/usr/share/bash-completion/completions/${PROJECT_NAME}"
install -Dm644 target/$TARGET/release/build/$PROJECT_NAME-*/out/$PROJECT_NAME.fish "$tempdir/usr/share/fish/completions/$PROJECT_NAME.fish"
install -Dm644 target/$TARGET/release/build/$PROJECT_NAME-*/out/_$PROJECT_NAME "$tempdir/usr/share/zsh/vendor-completions/_$PROJECT_NAME"
# Control file
mkdir "$tempdir/DEBIAN"
cat > "$tempdir/DEBIAN/control" <<EOF
Package: $dpkgname
Version: $version
Section: utils
Priority: optional
Maintainer: Peltoche <peltoche@halium.fr>
Architecture: $architecture
Provides: $PROJECT_NAME
Conflicts: $conflictname
Description: A ls command with a lot of pretty colors.
EOF
fakeroot dpkg-deb --build "$tempdir" "${dpkgname}_${version}_${architecture}.deb"
}
main() {
build
pack
if [[ $TARGET = *linux* ]]; then
make_deb
fi
}
main
================================================
FILE: ci/before_install.bash
================================================
#!/usr/bin/env bash
set -ex
if [ "$TRAVIS_OS_NAME" != linux ]; then
exit 0
fi
sudo apt-get update
# needed to build deb packages
sudo apt-get install -y fakeroot
# needed for i686 linux gnu target
if [[ $TARGET == i686-unknown-linux-gnu ]]; then
sudo apt-get install -y gcc-multilib
fi
# needed for cross-compiling for arm
if [[ $TARGET == arm-unknown-linux-* ]]; then
sudo apt-get install -y \
gcc-4.8-arm-linux-gnueabihf \
binutils-arm-linux-gnueabihf \
libc6-armhf-cross \
libc6-dev-armhf-cross
fi
================================================
FILE: ci/script.bash
================================================
#!/usr/bin/env bash
set -ex
# Incorporate TARGET env var to the build and test process
cargo build --target "$TARGET" --verbose
# We cannot run arm executables on linux
if [[ $TARGET != arm-unknown-linux-* ]]; then
cargo test --target "$TARGET" --verbose
fi
================================================
FILE: doc/colors.md
================================================
# Color reference
> [!TIP]
> Checkout [trapd00r/LS_COLORS](https://github.com/trapd00r/LS_COLORS) and [sharkdp/vivid](https://github.com/sharkdp/vivid) for help in theming using `LS_COLORS`.
You can customize filetype colors using `LS_COLORS` and other colors using the theme.
## Default colors
### Users and groups
| Color | Hex code | RGB array | Used for |
| :------------------------------------------------------------------- | :-------- | :---------------- | :------- |
| <img valign='middle' alt='very pale yellow' src='./img/ffffd7.svg'/> | `#ffffd7` | `[255, 255, 215]` | User |
| <img valign='middle' alt='grayish yellow' src='./img/d7d7af.svg'/> | `#d7d7af` | `[215, 215, 175]` | Group |
### Permissions
| Color | Hex code | RGB array | Used for |
| :-------------------------------------------------------------------------------- | :-------- | :---------------- | :-------------------------------- |
| <img valign='middle' alt='strong lime green' src='./img/00d700.svg'/> | `#00d700` | `[0, 215, 0]` | Read permission |
| <img valign='middle' alt='very light green' src='./img/d7ff87.svg'/> | `#d7ff87` | `[215, 255, 135]` | Write permission |
| <img valign='middle' alt='dark red' src='./img/af0000.svg'/> | `#af0000` | `[175, 0, 1]` | Execute permission |
| <img valign='middle' alt='pure (or mostly pure) magenta' src='./img/ff00ff.svg'/> | `#ff00ff` | `[255, 0, 255]` | Execute permission with stickybit |
| <img valign='middle' alt='moderate pink' src='./img/d75f87.svg'/> | `#d75f87` | `[215, 95, 135]` | No Access |
### File Types
> [!Note]
> These change based on your configured terminal color scheme
| Color | Hex code | RGB array | Used for |
| :------------------------------------------------------------------------------ | :-------- | :---------------- | :-------------------------------------- |
| <img valign='middle' alt='pure (or mostly pure) blue.' src='./img/0087ff.svg'/> | `#0087ff` | `[0, 135, 255]` | Directory |
| <img valign='middle' alt='strong lime green' src='./img/00d700.svg'/> | `#00d700` | `[0, 215, 0]` | Executable file |
| <img valign='middle' alt='white' src='./img/ffffff.svg'/> | `#ffffff` | `[255, 255, 255]` | Non-executable file |
| <img valign='middle' alt='dark red' src='./img/af0000.svg'/> | `#af0000` | `[175, 0, 1]` | Broken symlink |
| <img valign='middle' alt='strong cyan' src='./img/00d7d7.svg'/> | `#00d7d7` | `[0, 215, 215]` | Pipe/Symlink/Blockdevice/Socket/Special |
| <img valign='middle' alt='strong orange' src='./img/d78700.svg'/> | `#d78700` | `[215, 135, 0]` | CharDevice |
### Dates
| Color | Hex code | RGB array | Used for |
| :--------------------------------------------------------------------------- | :-------- | :-------------- | :------------------- |
| <img valign='middle' alt='strong lime green' src='./img/00d700.svg'/> | `#00d700` | `[0, 215, 0]`. | Within the last hour |
| <img valign='middle' alt='strong cyan - lime green' src='./img/00d787.svg'/> | `#00d787` | `[215, 135, 1]` | Within the last day |
| <img valign='middle' alt='dark cyan' src='./img/00af87.svg'/> | `#00af87` | `[175, 135, 1]` | Older |
### File Sizes
| Color | Hex code | RGB array | Used for |
| :-------------------------------------------------------------------- | :-------- | :---------------- | :---------- |
| <img valign='middle' alt='pale yellow' src='./img/ffffaf.svg'/> | `#ffffaf` | `[255, 255, 175]` | Small file |
| <img valign='middle' alt='very light orange' src='./img/ffaf87.svg'/> | `#ffaf87` | `[215, 135, 1]` | Medium file |
| <img valign='middle' alt='strong orange' src='./img/d78700.svg'/> | `#d78700` | `[215, 135, 0]` | Large file |
| <img valign='middle' alt='white' src='./img/ffffff.svg'/> | `#ffffff` | `[255, 255, 255]` | Non file |
================================================
FILE: doc/lsd.md
================================================
---
title: lsd
section: 1
header: User Manual
footer: lsd <version>
date: <date>
---
# NAME
lsd - LSDeluxe
# SYNOPSIS
`lsd [FLAGS] [OPTIONS] [--] [FILE]...`
# DESCRIPTION
lsd is a ls command with a lot of pretty colours and some other stuff to enrich and enhance the directory listing experience.
# OPTIONS
`-a`, `--all`
: Do not ignore entries starting with **.**
`-A`, `--almost-all`
: Do not list implied **.** and **..**
`--classic`
: Enable classic mode (no colours or icons)
`-L`, `--dereference`
: When showing file information for a symbolic link, show information for the file the link references rather than for the link itself
`-d`, `--directory-only`
: Display directories themselves, and not their contents (recursively when used with --tree)
`-X`, `--extensionsort`
: Sort by file extension
`--git`
: Display git status. Directory git status is a reduction of included file statuses (recursively).
`--help`
: Prints help information
`-h`, `--human-readable`
: For ls compatibility purposes ONLY, currently set by default
`--ignore-config`
: Ignore the configuration file
`--config-file <path>`
: Provide the config file from a custom location
`-F`, `--classify`
: Append indicator (one of \*/=>@|) at the end of the file names
`-i`, `--inode`
: Display the index number of each file
`-l`, `--long`
: Display extended file metadata as a table
`--no-symlink`
: Do not display symlink target
`-1`, `--oneline`
: Display one entry per line
`-R`, `--recursive`
: Recurse into directories
`-r`, `--reverse`
: Reverse the order of the sort
`-S`, `--sizesort`
: Sort by size
`-t`, `--timesort`
: Sort by time modified
`--total-size`
: Display the total size of directories
`--tree`
: Recurse into directories and present the result as a tree
`-V`, `--version`
: Prints version information
`-v`, `--versionsort`
: Natural sort of (version) numbers within text
`--blocks <blocks>...`
: Specify the blocks that will be displayed and in what order [possible values: permission, user, group, size, date, name, inode, git]
`--color <color>...`
: When to use terminal colours [default: auto] [possible values: always, auto, never]
`--date <date>...`
: How to display date [possible values: date, locale, relative, +date-time-format] [default: date]
`--depth <num>...`
: Stop recursing into directories after reaching specified depth
`--group-dirs <group-dirs>...`
: Sort the directories then the files [default: none] [possible values: none, first, last]
`--group-directories-first`
: Groups the directories at the top before the files. Same as `--group-dirs=first`
`--hyperlink <hyperlink>...`
: Attach hyperlink to filenames [default: never] [possible values: always, auto, never]
`--icon <icon>...`
: When to print the icons [default: auto] [possible values: always, auto, never]
`--icon-theme <icon-theme>...`
: Whether to use fancy or unicode icons [default: fancy] [possible values: fancy, unicode]
`-I, --ignore-glob <pattern>...`
: Do not display files/directories with names matching the glob pattern(s). More than one can be specified by repeating the argument [default: ]
`--permission <permission>...`
: How to display permissions [default: rwx for linux, attributes for windows] [possible values: rwx, octal, attributes, disable]
`--size <size>...`
: How to display size [default: default] [possible values: default, short, bytes]
`--sort <WORD>...`
: Sort by WORD instead of name [possible values: size, time, version, extension, git]
`-U`, `--no-sort`
: Do not sort. List entries in directory order
`-Z` `--context`
: Display SELinux or SMACK security context
`--header`
: Display block headers
`-N --literal`
: Print entry names without quoting
`--truncate-owner-after`
: Truncate the user and group names if they exceed a certain number of characters
`--truncate-owner-marker`
: Truncation marker appended to a truncated user or group name
# ARGS
`<FILE>...`
: A file or directory to list [default: .]
# EXAMPLES
`lsd`
: Display listing for current directory
`lsd /etc`
: Display listing of /etc
`lsd -la`
: Display listing of current directory, including files starting with `.` and the current directory's entry.
# ENVIRONMENT
`LS_COLORS`
: Used to determine color for displaying filenames. See **dir_colors**.
`XDG_CONFIG_HOME`
: Used to locate optional config file. If `XDG_CONFIG_HOME` is set, use `$XDG_CONFIG_HOME/lsd/config.yaml` else `$HOME/.config/lsd/config.yaml`.
`SHELL_COMPLETIONS_DIR` or `OUT_DIR`
: Used to specify the directory for generating a shell completions file. If neither are set, no completions file will be generated. The directory will be created if it does not exist.
================================================
FILE: doc/samples/colors-sample.yaml
================================================
---
user: 230 # Default = 230
group: 187 # Default = 187
permission:
read: dark_green # Default = dark_green
write: dark_yellow # Default = dark_yellow
exec: dark_red # Default = dark_red
exec-sticky: 5 # Default = 5
no-access: 245 # Default = 245
octal: 6 # Default = 6
acl: dark_cyan # Default = dark_cyan
context: cyan # Default = cyan
date:
hour-old: 40 # Default = 40
day-old: 42 # Default = 42
older: 36 # Default = 36
size:
none: 245 # Default = 245
small: 229 # Default = 229
medium: 216 # Default = 216
large: 172 # Default = 172
inode:
valid: 13 # Default = 13
invalid: 245 # Default = 245
links:
valid: 13 # Default = 13
invalid: 245 # Default = 245
tree-edge: 245 # Default = 245
git-status:
default: 245 # Default = 245
unmodified: 245 # Default = 245
ignored: 245 # Default = 245
new-in-index: dark_green # Default = dark_green
new-in-workdir: dark_green # Default = dark_green
typechange: dark_yellow # Default = dark_yellow
deleted: dark_red # Default = dark_red
renamed: dark_green # Default = dark_green
modified: dark_yellow # Default = dark_yellow
conflicted: dark_red # Default = dark_red
================================================
FILE: doc/samples/config-sample.yaml
================================================
---
# == Classic ==
# This is a shorthand to override some of the options to be backwards compatible
# with `ls`. It affects the "color"->"when", "sorting"->"dir-grouping", "date"
# and "icons"->"when" options.
# Possible values: false, true
classic: false
# == Blocks ==
# This specifies the columns and their order when using the long and the tree
# layout.
# Possible values: permission, user, group, context, size, date, name, inode, links, git
blocks:
- permission
- user
- group
- size
- date
- name
# == Color ==
# This has various color options. (Will be expanded in the future.)
color:
# When to colorize the output.
# When "classic" is set, this is set to "never".
# Possible values: never, auto, always
when: auto
# How to colorize the output.
# When "classic" is set, this is set to "no-color".
# Possible values: default, custom
# When "custom" is set, lsd will look in the config directory for `colors.yaml`.
theme: default
# == Date ==
# This specifies the date format for the date column. The freeform format
# accepts a strftime like string.
# When "classic" is set, this is set to "date".
# Possible values: date, locale, relative, '+<date_format>'
# `date_format` will be a `strftime` formatted value. e.g. `date: '+%d %b %y %X'` will give you a date like this: 17 Jun 21 20:14:55
date: date
# == Dereference ==
# Whether to dereference symbolic links.
# Possible values: false, true
dereference: false
# == Display ==
# What items to display. Do not specify this for the default behavior.
# Possible values: all, almost-all, directory-only
# display: all
# == Icons ==
icons:
# When to use icons.
# When "classic" is set, this is set to "never".
# Possible values: always, auto, never
when: auto
# Which icon theme to use.
# Possible values: fancy, unicode
theme: fancy
# Separator between icon and the name
# Default to 1 space
separator: " "
# == Ignore Globs ==
# A list of globs to ignore when listing.
# ignore-globs:
# - .git
# == Indicators ==
# Whether to add indicator characters to certain listed files.
# Possible values: false, true
indicators: false
# == Layout ==
# Which layout to use. "oneline" might be a bit confusing here and should be
# called "one-per-line". It might be changed in the future.
# Possible values: grid, tree, oneline
layout: grid
# == Recursion ==
recursion:
# Whether to enable recursion.
# Possible values: false, true
enabled: false
# How deep the recursion should go. This has to be a positive integer. Leave
# it unspecified for (virtually) infinite.
# depth: 3
# == Size ==
# Specifies the format of the size column.
# Possible values: default, short, bytes
size: default
# == Permission ==
# Specify the format of the permission column
# Possible value: rwx, octal, attributes (windows only), disable
# permission: rwx
# == Sorting ==
sorting:
# Specify what to sort by.
# Possible values: extension, name, time, size, version
column: name
# Whether to reverse the sorting.
# Possible values: false, true
reverse: false
# Whether to group directories together and where.
# When "classic" is set, this is set to "none".
# Possible values: first, last, none
dir-grouping: none
# == No Symlink ==
# Whether to omit showing symlink targets
# Possible values: false, true
no-symlink: false
# == Total size ==
# Whether to display the total size of directories.
# Possible values: false, true
total-size: false
# == Hyperlink ==
# Attach hyperlink to filenames
# Possible values: always, auto, never
hyperlink: never
# == Symlink arrow ==
# Specifies how the symlink arrow display, chars in both ascii and utf8
symlink-arrow: ⇒
# == Header ==
# Whether to display block headers.
# Possible values: false, true
header: false
# == Literal ==
# Whether to show quotes on filenames.
# Possible values: false, true
literal: false
# == Truncate owner ==
# How to truncate the username and group names for a file if they exceed a certain
# number of characters.
truncate-owner:
# Number of characters to keep. By default, no truncation is done (empty value).
after:
# String to be appended to a name if truncated.
marker: ""
================================================
FILE: doc/samples/icons-sample.yaml
================================================
---
# See https://www.nerdfonts.com/cheat-sheet for additional icons
filetype:
dir: # Default =
file: # Default =
pipe: # Default =
socket: # Default =
executable: # Default =
symlink-dir: # Default =
symlink-file: # Default =
device-char: # Default =
device-block: # Default =
special: # Default =
name:
a: # Default =
api: # Default =
.asoundrc: # Default =
.atom: # Default =
.ash: # Default =
.ash_history: # Default =
authorized_keys: # Default =
assets: # Default =
.android: # Default =
.audacity: # Default =
backups: # Default =
.bash_history: # Default =
.bash_logout: # Default =
.bash_profile: # Default =
.bashrc: # Default =
bin: # Default =
.bpython_history: # Default =
build: # Default =
bspwmrc: # Default =
build: # Default =
.cache: # Default =
cache: # Default =
cargo: # Default =
cargo: # Default =
.cargo: # Default =
.ccls: # Default =
changelog: # Default =
.clang: # Default =
composer: # Default =
composer: # Default =
conf: # Default =
config: # Default =
config: # Default =
config: # Default =
.config: # Default =
config: # Default =
configure: # Default =
containerfile: # Default =
content: # Default =
contributing: # Default =
copyright: # Default =
cron: # Default =
cron: # Default =
cron: # Default =
cron: # Default =
cron: # Default =
crontab: # Default =
cron: # Default =
crypttab: # Default =
.cshrc: # Default =
csh: # Default =
csh: # Default =
csh: # Default =
css: # Default =
custom: # Default =
.dbus: # Default =
desktop: # Default =
docker: # Default =
dockerfile: # Default =
doc: # Default =
dist: # Default =
documents: # Default =
.doom: # Default =
downloads: # Default =
.ds_store: # Default =
.editorconfig: # Default =
.electron: # Default =
.emacs: # Default =
.env: # Default =
environment: # Default =
.eslintrc: # Default =
.eslintrc: # Default =
.eslintrc: # Default =
etc: # Default =
favicon: # Default =
favicons: # Default =
.fennelrc: # Default =
fstab: # Default =
.fastboot: # Default =
.gitattributes: # Default =
# .gitconfig: # Default =
.gitcredentials: # Default =
.github: # Default =
gitignore_global: # Default =
.gitignore: # Default =
.gitlab: # Default =
.gitmodules: # Default =
.git: # Default =
.gnupg: # Default =
go: # Default =
go: # Default =
go: # Default =
gradle: # Default =
gradle: # Default =
gradlew: # Default =
gradlew: # Default =
group: # Default =
gruntfile: # Default =
gruntfile: # Default =
gruntfile: # Default =
gshadow: # Default =
gulpfile: # Default =
gulpfile: # Default =
gulpfile: # Default =
heroku: # Default =
hidden: # Default =
home: # Default =
hostname: # Default =
hosts: # Default =
.htaccess: # Default =
htoprc: # Default =
.htpasswd: # Default =
.icons: # Default =
icons: # Default =
id_dsa: # Default =
id_ecdsa: # Default =
id_rsa: # Default =
.idlerc: # Default =
img: # Default =
include: # Default =
init: # Default =
.inputrc: # Default =
inputrc: # Default =
.java: # Default =
jenkinsfile: # Default =
js: # Default =
jule: # Default =
.jupyter: # Default =
kbuild: # Default =
kconfig: # Default =
kdeglobals: # Default =
kdenliverc: # Default =
known_hosts: # Default =
.kshrc: # Default =
libexec: # Default =
lib32: # Default =
lib64: # Default =
lib: # Default =
license: # Default =
licenses: # Default =
license: # Default =
license: # Default =
localized: # Default =
lsb: # Default =
.lynxrc: # Default =
.mailcap: # Default =
mail: # Default =
magic: # Default =
maintainers: # Default =
makefile: # Default =
makefile: # Default =
manifest: # Default =
md5sum: # Default =
meson: # Default =
metadata: # Default =
metadata: # Default =
media: # Default =
.mime: # Default =
mime: # Default =
module: # Default =
.mozilla: # Default =
music: # Default =
muttrc: # Default =
.muttrc: # Default =
.mutt: # Default =
.mypy_cache: # Default =
neomuttrc: # Default =
.neomuttrc: # Default =
netlify: # Default =
.nix: # Default =
.nix: # Default =
.node: # Default =
node_modules: # Default =
.node_repl_history: # Default =
npmignore: # Default =
.npm: # Default =
nvim: # Default =
obj: # Default =
os: # Default =
package: # Default =
package: # Default =
packages: # Default =
pam: # Default =
passwd: # Default =
pictures: # Default =
pkgbuild: # Default =
.pki: # Default =
portage: # Default =
profile: # Default =
.profile: # Default =
public: # Default =
__pycache__: # Default =
pyproject: # Default =
.python_history: # Default =
.pypirc: # Default =
rc: # Default =
readme: # Default =
.release: # Default =
requirements: # Default =
robots: # Default =
root: # Default =
rubydoc: # Default =
runtime: # Default =
.rustup: # Default =
rustfmt: # Default =
.rvm: # Default =
sass: # Default =
sbin: # Default =
scripts: # Default =
scss: # Default =
sha256sum: # Default =
shadow: # Default =
share: # Default =
.shellcheckrc: # Default =
shells: # Default =
.spacemacs: # Default =
.sqlite_history: # Default =
src: # Default =
source: # Default =
.ssh: # Default =
static: # Default =
std: # Default =
styles: # Default =
subgid: # Default =
subuid: # Default =
sudoers: # Default =
sxhkdrc: # Default =
template: # Default =
tests: # Default =
test: # Default =
tigrc: # Default =
timezone: # Default =
tox: # Default =
.trash: # Default =
ts: # Default =
.tox: # Default =
unlicense: # Default =
url: # Default =
user: # Default =
vagrantfile: # Default =
vendor: # Default =
venv: # Default =
videos: # Default =
.viminfo: # Default =
.vimrc: # Default =
vimrc: # Default =
.vim: # Default =
vim: # Default =
.vscode: # Default =
webpack: # Default =
.wgetrc: # Default =
wgetrc: # Default =
.xauthority: # Default =
.Xauthority: # Default =
xbps: # Default =
xbps: # Default =
.xinitrc: # Default =
.xmodmap: # Default =
.Xmodmap: # Default =
xmonad: # Default =
xorg: # Default =
.xprofile: # Default =
.Xprofile: # Default =
.xresources: # Default =
.yarnrc: # Default =
yarn: # Default =
zathurarc: # Default =
.zcompdump: # Default =
.zlogin: # Default =
.zlogout: # Default =
.zprofile: # Default =
.zsh_history: # Default =
.zshrc: # Default =
extension:
1: # Default =
2: # Default =
3: # Default =
4: # Default =
5: # Default =
6: # Default =
7: # Default =
7z: # Default =
8: # Default =
890: # Default =
a: # Default =
ai: # Default =
ape: # Default =
apk: # Default =
apng: # Default =
ar: # Default =
asc: # Default =
asm: # Default =
asp: # Default =
avi: # Default =
avif: # Default =
avro: # Default =
awk: # Default =
bak: # Default =
bash_history: # Default =
bash_profile: # Default =
bashrc: # Default =
bash: # Default =
bat: # Default =
bin: # Default =
bio: # Default =
blend: # Default =
blend1: # Default =
bmp: # Default =
bz2: # Default =
cc: # Default =
cfg: # Default =
cip: # Default =
cjs: # Default =
class: # Default =
cljs: # Default =
clj: # Default =
cls: # Default =
cl: # Default =
cmd: # Default =
coffee: # Default =
conf: # Default =
cpp: # Default =
cp: # Default =
cshtml: # Default =
csh: # Default =
csproj: # Default =
css: # Default =
cs: # Default =
csv: # Default =
csx: # Default =
cts: # Default =
c: # Default =
c: # Default =
cue: # Default =
cxx: # Default =
cypher: # Default =
dart: # Default =
dat: # Default =
db: # Default =
deb: # Default =
desktop: # Default =
diff: # Default =
dll: # Default =
dockerfile: # Default =
doc: # Default =
docx: # Default =
download: # Default =
ds_store: # Default =
dump: # Default =
ebook: # Default =
ebuild: # Default =
eclass: # Default =
editorconfig: # Default =
egg: # Default =
ejs: # Default =
elc: # Default =
elf: # Default =
elm: # Default =
el: # Default =
env: # Default =
eot: # Default =
epub: # Default =
erb: # Default =
erl: # Default =
exe: # Default =
exs: # Default =
ex: # Default =
fish: # Default =
flac: # Default =
flv: # Default =
fnl: # Default =
font: # Default =
fpl: # Default =
fsi: # Default =
fs: # Default =
fsx: # Default =
gdoc: # Default =
gemfile: # Default =
gemspec: # Default =
gform: # Default =
gif: # Default =
git: # Default =
go: # Default =
gpg: # Default =
gradle: # Default =
gsheet: # Default =
gslides: # Default =
guardfile: # Default =
gv: # Default =
gz: # Default =
hbs: # Default =
heic: # Default =
heif: # Default =
heix: # Default =
hh: # Default =
hpp: # Default =
hs: # Default =
html: # Default =
htm: # Default =
h: # Default =
hxx: # Default =
ico: # Default =
image: # Default =
img: # Default =
iml: # Default =
info: # Default =
in: # Default =
ini: # Default =
ipynb: # Default =
iso: # Default =
j2: # Default =
jar: # Default =
java: # Default =
jinja: # Default =
jl: # Default =
jpeg: # Default =
jpg: # Default =
jsonc: # Default =
json: # Default =
js: # Default =
jsx: # Default =
jule: # Default =
key: # Default =
ksh: # Default =
kt: # Default =
kts: # Default =
kusto: # Default =
ldb: # Default =
ld: # Default =
less: # Default =
lhs: # Default =
license: # Default =
lisp: # Default =
list: # Default =
localized: # Default =
lock: # Default =
log: # Default =
lss: # Default =
lua: # Default =
lz: # Default =
mgc: # Default =
m3u8: # Default =
m3u: # Default =
m4: # Default =
m4a: # Default =
m4v: # Default =
magnet: # Default =
make: # Default =
makefile: # Default =
malloy: # Default =
man: # Default =
markdown: # Default =
md: # Default =
mjs: # Default =
mkd: # Default =
mk: # Default =
mkv: # Default =
ml: # Default =
mli: # Default =
mll: # Default =
mly: # Default =
mobi: # Default =
mov: # Default =
mp3: # Default =
mp4: # Default =
msi: # Default =
mts: # Default =
mustache: # Default =
nim: # Default =
nimble: # Default =
nix: # Default =
npmignore: # Default =
odp: # Default =
ods: # Default =
odt: # Default =
ogg: # Default =
ogv: # Default =
old: # Default =
opus: # Default =
orig: # Default =
org: # Default =
otf: # Default =
o: # Default =
part: # Default =
patch: # Default =
pdb: # Default =
pdf: # Default =
pem: # Default =
phar: # Default =
php: # Default =
pkg: # Default =
pl: # Default =
plist: # Default =
pls: # Default =
plx: # Default =
pm: # Default =
png: # Default =
pod: # Default =
pp: # Default =
ppt: # Default =
pptx: # Default =
procfile: # Default =
properties: # Default =
prql: # Default =
ps1: # Default =
psd: # Default =
pub: # Default =
pug: # Default =
sbv: # Default =
scc: # Default =
slt: # Default =
smi: # Default =
pxm: # Default =
pyc: # Default =
py: # Default =
rakefile: # Default =
rar: # Default =
razor: # Default =
rb: # Default =
rdata: # Default =
rdb: # Default =
rdoc: # Default =
rds: # Default =
readme: # Default =
rlib: # Default =
rl: # Default =
rmd: # Default =
rmeta: # Default =
rpm: # Default =
rproj: # Default =
rq: # Default =
rspec_parallel: # Default =
rspec_status: # Default =
rspec: # Default =
rss: # Default =
rs: # Default =
rtf: # Default =
rubydoc: # Default =
r: # Default =
ru: # Default =
sass: # Default =
scala: # Default =
scpt: # Default =
scss: # Default =
shell: # Default =
sh: # Default =
sig: # Default =
slim: # Default =
sln: # Default =
so: # Default =
sqlite3: # Default =
sql: # Default =
srt: # Default =
styl: # Default =
stylus: # Default =
sublime: # Default =
sublime: # Default =
sublime: # Default =
sublime: # Default =
sub: # Default =
s: # Default =
svg: # Default =
svelte: # Default =
swift: # Default =
swp: # Default =
sym: # Default =
tar: # Default =
taz: # Default =
tbz: # Default =
tbz2: # Default =
tex: # Default =
tgz: # Default =
tiff: # Default =
timestamp: # Default =
toml: # Default =
torrent: # Default =
trash: # Default =
ts: # Default =
tsx: # Default =
ttc: # Default =
ttf: # Default =
t: # Default =
twig: # Default =
txt: # Default =
unity: # Default =
unity32: # Default =
video: # Default =
vim: # Default =
vlc: # Default =
vtt: # Default =
vue: # Default =
wav: # Default =
webm: # Default =
webp: # Default =
whl: # Default =
windows: # Default =
wma: # Default =
wmv: # Default =
woff2: # Default =
woff: # Default =
wpl: # Default =
xbps: # Default =
xcf: # Default =
xls: # Default =
xlsx: # Default =
xml: # Default =
xul: # Default =
xz: # Default =
yaml: # Default =
yml: # Default =
zip: # Default =
zig: # Default =
zon: # Default =
zshrc: # Default =
zsh: # Default =
zsh: # Default =
zst: # Default =
================================================
FILE: lsd.spec
================================================
Name: lsd
Version: 1.2.0
Release: 1%{?dist}
Summary: The next gen ls command
License: MIT
URL: https://github.com/lsd-rs/lsd
Source0: https://github.com/lsd-rs/lsd/archive/refs/tags/v%{version}.tar.gz
BuildRequires: rust
BuildRequires: cargo
%description
This project is a rewrite of GNU ls with lots of added features like colors, icons, tree-view, more formatting options etc. The project is heavily inspired by the super colorls project.
%global debug_package %{nil}
%prep
%setup -q
%build
cargo build --release
%install
%global _build_id_links none
mkdir -p %{buildroot}/%{_bindir}
# upx "target/release/lsd"
install -m 755 target/release/%{name} %{buildroot}/%{_bindir}/%{name}
%files
%defattr(-,root,root,-)
%{_bindir}/%{name}
================================================
FILE: rustfmt.toml
================================================
# use empty config file to ensure that `rustfmt` will always use
# the default configuration when formatting code
# even if there is a `rustfmt.toml` file in parent directories
================================================
FILE: src/app.rs
================================================
use std::path::PathBuf;
use clap::{ArgAction, Parser, ValueHint};
#[derive(Debug, Parser)]
#[command(about, version, args_override_self = true, disable_help_flag = true)]
pub struct Cli {
#[arg(value_name = "FILE", default_value = ".", value_hint = ValueHint::AnyPath)]
pub inputs: Vec<PathBuf>,
/// Do not ignore entries starting with . .
#[arg(short, long, overrides_with = "almost_all")]
pub all: bool,
/// Do not list implied . and ..
#[arg(short = 'A', long)]
pub almost_all: bool,
/// When to use terminal colours [default: auto]
#[arg(long, value_name = "MODE", value_parser = ["always", "auto", "never"])]
pub color: Option<String>,
/// When to print the icons [default: auto]
#[arg(long, value_name = "MODE", value_parser = ["always", "auto", "never"])]
pub icon: Option<String>,
/// Whether to use fancy or unicode icons [default: fancy]
#[arg(long, value_name = "THEME", value_parser = ["fancy", "unicode"])]
pub icon_theme: Option<String>,
/// Append indicator (one of */=>@|) at the end of the file names
#[arg(short = 'F', long = "classify")]
pub indicators: bool,
/// Display extended file metadata as a table
#[arg(short, long)]
pub long: bool,
/// Ignore the configuration file
#[arg(long)]
pub ignore_config: bool,
/// Provide a custom lsd configuration file
#[arg(long, value_name = "PATH")]
pub config_file: Option<PathBuf>,
/// Generate default configuration file and print to stdout
#[arg(long)]
pub generate_config: bool,
/// Display one entry per line
#[arg(short = '1', long)]
pub oneline: bool,
/// Recurse into directories
#[arg(short = 'R', long, conflicts_with = "tree")]
pub recursive: bool,
/// For ls compatibility purposes ONLY, currently set by default
#[arg(short, long)]
human_readable: bool,
/// Recurse into directories and present the result as a tree
#[arg(long)]
pub tree: bool,
/// Stop recursing into directories after reaching specified depth
#[arg(long, value_name = "NUM")]
pub depth: Option<usize>,
/// Display directories themselves, and not their contents (recursively when used with --tree)
#[arg(short, long, conflicts_with = "recursive")]
pub directory_only: bool,
/// How to display permissions [default: rwx for linux, attributes for windows]
#[arg(long, value_name = "MODE", value_parser = ["rwx", "octal", "attributes", "disable"])]
pub permission: Option<String>,
/// How to display size [default: default]
#[arg(long, value_name = "MODE", value_parser = ["default", "short", "bytes"])]
pub size: Option<String>,
/// Display the total size of directories
#[arg(long)]
pub total_size: bool,
/// How to display date [default: date] [possible values: date, locale, relative, +date-time-format]
#[arg(long, value_parser = validate_date_argument)]
pub date: Option<String>,
/// Sort by time modified
#[arg(short = 't', long)]
pub timesort: bool,
/// Sort by size
#[arg(short = 'S', long)]
pub sizesort: bool,
/// Sort by file extension
#[arg(short = 'X', long)]
pub extensionsort: bool,
/// Sort by git status
#[arg(short = 'G', long)]
pub gitsort: bool,
/// Natural sort of (version) numbers within text
#[arg(short = 'v', long)]
pub versionsort: bool,
/// Sort by TYPE instead of name
#[arg(
long,
value_name = "TYPE",
value_parser = ["size", "time", "version", "extension", "git", "none"],
overrides_with_all = ["timesort", "sizesort", "extensionsort", "versionsort", "gitsort", "no_sort"]
)]
pub sort: Option<String>,
/// Do not sort. List entries in directory order
#[arg(short = 'U', long, overrides_with_all = ["timesort", "sizesort", "extensionsort", "versionsort", "gitsort", "sort"])]
pub no_sort: bool,
/// Reverse the order of the sort
#[arg(short, long)]
pub reverse: bool,
/// Sort the directories then the files
#[arg(long, value_name = "MODE", value_parser = ["none", "first", "last"])]
pub group_dirs: Option<String>,
/// Groups the directories at the top before the files. Same as --group-dirs=first
#[arg(long)]
pub group_directories_first: bool,
/// Specify the blocks that will be displayed and in what order
#[arg(
long,
value_delimiter = ',',
value_parser = ["permission", "user", "group", "context", "size", "date", "name", "inode", "links", "git"],
)]
pub blocks: Vec<String>,
/// Enable classic mode (display output similar to ls)
#[arg(long)]
pub classic: bool,
/// Do not display symlink target
#[arg(long)]
pub no_symlink: bool,
/// Do not display files/directories with names matching the glob pattern(s).
/// More than one can be specified by repeating the argument
#[arg(short = 'I', long, value_name = "PATTERN")]
pub ignore_glob: Vec<String>,
/// Display the index number of each file
#[arg(short, long)]
pub inode: bool,
/// Show git status on file and directory
/// Only when used with --long option
#[arg(short, long)]
pub git: bool,
/// When showing file information for a symbolic link,
/// show information for the file the link references rather than for the link itself
#[arg(short = 'L', long)]
pub dereference: bool,
/// Print security context (label) of each file
#[arg(short = 'Z', long)]
pub context: bool,
/// Attach hyperlink to filenames [default: never]
#[arg(long, value_name = "MODE", value_parser = ["always", "auto", "never"])]
pub hyperlink: Option<String>,
/// Display block headers
#[arg(long)]
pub header: bool,
/// Truncate the user and group names if they exceed a certain number of characters
#[arg(long, value_name = "NUM")]
pub truncate_owner_after: Option<usize>,
/// Truncation marker appended to a truncated user or group name
#[arg(long, value_name = "STR")]
pub truncate_owner_marker: Option<String>,
/// Includes files with the windows system protection flag set.
/// This is the same as --all on other platforms
#[arg(long, hide = !cfg!(windows))]
pub system_protected: bool,
/// Print entry names without quoting
#[arg(short = 'N', long)]
pub literal: bool,
/// Print help information
#[arg(long, action = ArgAction::Help)]
help: (),
}
fn validate_date_argument(arg: &str) -> Result<String, String> {
if arg.starts_with('+') {
validate_time_format(arg)
} else if arg == "date" || arg == "relative" || arg == "locale" {
Result::Ok(arg.to_owned())
} else {
Result::Err("possible values: date, locale, relative, +date-time-format".to_owned())
}
}
pub fn validate_time_format(formatter: &str) -> Result<String, String> {
let mut chars = formatter.chars();
loop {
match chars.next() {
Some('%') => match chars.next() {
Some('.') => match chars.next() {
Some('f') => (),
Some(n @ ('3' | '6' | '9')) => match chars.next() {
Some('f') => (),
Some(c) => return Err(format!("invalid format specifier: %.{n}{c}")),
None => return Err("missing format specifier".to_owned()),
},
Some(c) => return Err(format!("invalid format specifier: %.{c}")),
None => return Err("missing format specifier".to_owned()),
},
Some(n @ (':' | '#')) => match chars.next() {
Some('z') => (),
Some(c) => return Err(format!("invalid format specifier: %{n}{c}")),
None => return Err("missing format specifier".to_owned()),
},
Some(n @ ('-' | '_' | '0')) => match chars.next() {
Some(
'C' | 'd' | 'e' | 'f' | 'G' | 'g' | 'H' | 'I' | 'j' | 'k' | 'l' | 'M' | 'm'
| 'S' | 's' | 'U' | 'u' | 'V' | 'W' | 'w' | 'Y' | 'y',
) => (),
Some(c) => return Err(format!("invalid format specifier: %{n}{c}")),
None => return Err("missing format specifier".to_owned()),
},
Some(
'A' | 'a' | 'B' | 'b' | 'C' | 'c' | 'D' | 'd' | 'e' | 'F' | 'f' | 'G' | 'g'
| 'H' | 'h' | 'I' | 'j' | 'k' | 'l' | 'M' | 'm' | 'n' | 'P' | 'p' | 'R' | 'r'
| 'S' | 's' | 'T' | 't' | 'U' | 'u' | 'V' | 'v' | 'W' | 'w' | 'X' | 'x' | 'Y'
| 'y' | 'Z' | 'z' | '+' | '%',
) => (),
Some(n @ ('3' | '6' | '9')) => match chars.next() {
Some('f') => (),
Some(c) => return Err(format!("invalid format specifier: %{n}{c}")),
None => return Err("missing format specifier".to_owned()),
},
Some(c) => return Err(format!("invalid format specifier: %{c}")),
None => return Err("missing format specifier".to_owned()),
},
None => break,
_ => continue,
}
}
Ok(formatter.to_owned())
}
// Wrapper for value_parser to simply remove non supported option (mainly git flag)
// required since value_parser requires impl Into<ValueParser> that Vec do not support
// should be located here, since this file is included by build.rs
struct LabelFilter<Filter: Fn(&'static str) -> bool, const C: usize>([&'static str; C], Filter);
impl<Filter: Fn(&'static str) -> bool, const C: usize> From<LabelFilter<Filter, C>>
for clap::builder::ValueParser
{
fn from(label_filter: LabelFilter<Filter, C>) -> Self {
let filter = label_filter.1;
let values = label_filter.0.into_iter().filter(|x| filter(x));
let inner = clap::builder::PossibleValuesParser::from(values);
Self::from(inner)
}
}
================================================
FILE: src/color.rs
================================================
use crossterm::style::Color;
use crossterm::style::{Attribute, ContentStyle, StyledContent, Stylize};
use lscolors::{Indicator, LsColors};
use std::path::Path;
pub use crate::flags::color::ThemeOption;
use crate::git::GitStatus;
use crate::print_output;
use crate::theme::{Theme, color::ColorTheme};
#[allow(dead_code)]
#[derive(Hash, Debug, Eq, PartialEq, Clone)]
pub enum Elem {
/// Node type
File {
exec: bool,
uid: bool,
},
SymLink,
BrokenSymLink,
MissingSymLinkTarget,
Dir {
uid: bool,
},
Pipe,
BlockDevice,
CharDevice,
Socket,
Special,
/// Permission
Read,
Write,
Exec,
ExecSticky,
NoAccess,
Octal,
Acl,
Context,
/// Attributes
Archive,
AttributeRead,
Hidden,
System,
/// Last Time Modified
DayOld,
HourOld,
Older,
/// User / Group Name
User,
Group,
/// File Size
NonFile,
FileLarge,
FileMedium,
FileSmall,
/// INode
INode {
valid: bool,
},
Links {
valid: bool,
},
TreeEdge,
GitStatus {
status: GitStatus,
},
}
impl Elem {
fn has_suid(&self) -> bool {
matches!(self, Elem::Dir { uid: true } | Elem::File { uid: true, .. })
}
pub fn get_color(&self, theme: &ColorTheme) -> Color {
match self {
Elem::File {
exec: true,
uid: true,
} => theme.file_type.file.exec_uid,
Elem::File {
exec: false,
uid: true,
} => theme.file_type.file.uid_no_exec,
Elem::File {
exec: true,
uid: false,
} => theme.file_type.file.exec_no_uid,
Elem::File {
exec: false,
uid: false,
} => theme.file_type.file.no_exec_no_uid,
Elem::SymLink => theme.file_type.symlink.default,
Elem::BrokenSymLink => theme.file_type.symlink.broken,
Elem::MissingSymLinkTarget => theme.file_type.symlink.missing_target,
Elem::Dir { uid: true } => theme.file_type.dir.uid,
Elem::Dir { uid: false } => theme.file_type.dir.no_uid,
Elem::Pipe => theme.file_type.pipe,
Elem::BlockDevice => theme.file_type.block_device,
Elem::CharDevice => theme.file_type.char_device,
Elem::Socket => theme.file_type.socket,
Elem::Special => theme.file_type.special,
Elem::Read => theme.permission.read,
Elem::Write => theme.permission.write,
Elem::Exec => theme.permission.exec,
Elem::ExecSticky => theme.permission.exec_sticky,
Elem::NoAccess => theme.permission.no_access,
Elem::Octal => theme.permission.octal,
Elem::Acl => theme.permission.acl,
Elem::Context => theme.permission.context,
Elem::Archive => theme.attributes.archive,
Elem::AttributeRead => theme.attributes.read,
Elem::Hidden => theme.attributes.hidden,
Elem::System => theme.attributes.system,
Elem::DayOld => theme.date.day_old,
Elem::HourOld => theme.date.hour_old,
Elem::Older => theme.date.older,
Elem::User => theme.user,
Elem::Group => theme.group,
Elem::NonFile => theme.size.none,
Elem::FileLarge => theme.size.large,
Elem::FileMedium => theme.size.medium,
Elem::FileSmall => theme.size.small,
Elem::INode { valid: true } => theme.inode.valid,
Elem::INode { valid: false } => theme.inode.invalid,
Elem::TreeEdge => theme.tree_edge,
Elem::Links { valid: false } => theme.links.invalid,
Elem::Links { valid: true } => theme.links.valid,
Elem::GitStatus {
status: GitStatus::Default,
} => theme.git_status.default,
Elem::GitStatus {
status: GitStatus::Unmodified,
} => theme.git_status.unmodified,
Elem::GitStatus {
status: GitStatus::Ignored,
} => theme.git_status.ignored,
Elem::GitStatus {
status: GitStatus::NewInIndex,
} => theme.git_status.new_in_index,
Elem::GitStatus {
status: GitStatus::NewInWorkdir,
} => theme.git_status.new_in_workdir,
Elem::GitStatus {
status: GitStatus::Typechange,
} => theme.git_status.typechange,
Elem::GitStatus {
status: GitStatus::Deleted,
} => theme.git_status.deleted,
Elem::GitStatus {
status: GitStatus::Renamed,
} => theme.git_status.renamed,
Elem::GitStatus {
status: GitStatus::Modified,
} => theme.git_status.modified,
Elem::GitStatus {
status: GitStatus::Conflicted,
} => theme.git_status.conflicted,
}
}
}
pub type ColoredString = StyledContent<String>;
pub struct Colors {
theme: Option<ColorTheme>,
lscolors: Option<LsColors>,
}
impl Colors {
pub fn new(t: ThemeOption) -> Self {
let theme = match t {
ThemeOption::NoColor => None,
ThemeOption::Default | ThemeOption::NoLscolors => Some(Theme::default().color),
ThemeOption::Custom => Some(
Theme::from_path::<ColorTheme>(Path::new("colors").to_str().unwrap())
.unwrap_or_default(),
),
ThemeOption::CustomLegacy(ref file) => {
print_output!(
"Warning: the 'themes' directory is deprecated, use 'colors.yaml' instead.\n\n"
);
// TODO: drop the `themes` dir prefix, adding it here only for backwards compatibility
Some(
Theme::from_path::<ColorTheme>(
Path::new("themes").join(file).to_str().unwrap_or(file),
)
.unwrap_or_default(),
)
}
};
let lscolors = match t {
ThemeOption::Default | ThemeOption::Custom | ThemeOption::CustomLegacy(_) => {
Some(LsColors::from_env().unwrap_or_default())
}
_ => None,
};
Self { theme, lscolors }
}
pub fn colorize<S: Into<String>>(&self, input: S, elem: &Elem) -> ColoredString {
self.style(elem).apply(input.into())
}
pub fn colorize_using_path(&self, input: String, path: &Path, elem: &Elem) -> ColoredString {
let style_from_path = self.style_from_path(path);
match style_from_path {
Some(style_from_path) => style_from_path.apply(input),
None => self.colorize(input, elem),
}
}
pub fn default_style() -> ContentStyle {
ContentStyle::default()
}
fn style_from_path(&self, path: &Path) -> Option<ContentStyle> {
match &self.lscolors {
Some(lscolors) => lscolors.style_for_path(path).map(to_content_style),
None => None,
}
}
fn style(&self, elem: &Elem) -> ContentStyle {
match &self.lscolors {
Some(lscolors) => match self.get_indicator_from_elem(elem) {
Some(style) => {
let style = lscolors.style_for_indicator(style);
style.map(to_content_style).unwrap_or_default()
}
None => self.style_default(elem),
},
None => self.style_default(elem),
}
}
fn style_default(&self, elem: &Elem) -> ContentStyle {
if let Some(t) = &self.theme {
let style_fg = ContentStyle::default().with(elem.get_color(t));
if elem.has_suid() {
style_fg.on(Color::AnsiValue(124)) // Red3
} else {
style_fg
}
} else {
ContentStyle::default()
}
}
fn get_indicator_from_elem(&self, elem: &Elem) -> Option<Indicator> {
let indicator_string = match elem {
Elem::File { exec, uid } => match (exec, uid) {
(_, true) => None,
(true, false) => Some("ex"),
(false, false) => Some("fi"),
},
Elem::Dir { uid } => {
if *uid {
None
} else {
Some("di")
}
}
Elem::SymLink => Some("ln"),
Elem::Pipe => Some("pi"),
Elem::Socket => Some("so"),
Elem::BlockDevice => Some("bd"),
Elem::CharDevice => Some("cd"),
Elem::BrokenSymLink => Some("or"),
Elem::MissingSymLinkTarget => Some("mi"),
_ => None,
};
match indicator_string {
Some(ids) => Indicator::from(ids),
None => None,
}
}
}
fn to_content_style(ls: &lscolors::Style) -> ContentStyle {
let to_crossterm_color = |c: &lscolors::Color| match c {
lscolors::style::Color::RGB(r, g, b) => Color::Rgb {
r: *r,
g: *g,
b: *b,
},
lscolors::style::Color::Fixed(n) => Color::AnsiValue(*n),
lscolors::style::Color::Black => Color::Black,
lscolors::style::Color::Red => Color::DarkRed,
lscolors::style::Color::Green => Color::DarkGreen,
lscolors::style::Color::Yellow => Color::DarkYellow,
lscolors::style::Color::Blue => Color::DarkBlue,
lscolors::style::Color::Magenta => Color::DarkMagenta,
lscolors::style::Color::Cyan => Color::DarkCyan,
lscolors::style::Color::White => Color::Grey,
lscolors::style::Color::BrightBlack => Color::DarkGrey,
lscolors::style::Color::BrightRed => Color::Red,
lscolors::style::Color::BrightGreen => Color::Green,
lscolors::style::Color::BrightYellow => Color::Yellow,
lscolors::style::Color::BrightBlue => Color::Blue,
lscolors::style::Color::BrightMagenta => Color::Magenta,
lscolors::style::Color::BrightCyan => Color::Cyan,
lscolors::style::Color::BrightWhite => Color::White,
};
let mut style = ContentStyle {
foreground_color: ls.foreground.as_ref().map(to_crossterm_color),
background_color: ls.background.as_ref().map(to_crossterm_color),
..ContentStyle::default()
};
if ls.font_style.bold {
style.attributes.set(Attribute::Bold);
}
if ls.font_style.dimmed {
style.attributes.set(Attribute::Dim);
}
if ls.font_style.italic {
style.attributes.set(Attribute::Italic);
}
if ls.font_style.underline {
style.attributes.set(Attribute::Underlined);
}
if ls.font_style.rapid_blink {
style.attributes.set(Attribute::RapidBlink);
}
if ls.font_style.slow_blink {
style.attributes.set(Attribute::SlowBlink);
}
if ls.font_style.reverse {
style.attributes.set(Attribute::Reverse);
}
if ls.font_style.hidden {
style.attributes.set(Attribute::Hidden);
}
if ls.font_style.strikethrough {
style.attributes.set(Attribute::CrossedOut);
}
style
}
#[cfg(test)]
mod tests {
use super::Colors;
use crate::color::ThemeOption;
use crate::theme::color::ColorTheme;
#[test]
fn test_color_new_no_color_theme() {
assert!(Colors::new(ThemeOption::NoColor).theme.is_none());
}
#[test]
fn test_color_new_custom_theme() {
assert_eq!(
Colors::new(ThemeOption::Custom).theme,
Some(ColorTheme::default_dark()),
);
}
#[test]
fn test_color_new_custom_no_file_theme() {
assert_eq!(
Colors::new(ThemeOption::Custom).theme,
Some(ColorTheme::default_dark()),
);
}
#[test]
fn test_color_new_bad_legacy_custom_theme() {
assert_eq!(
Colors::new(ThemeOption::CustomLegacy("not-existed".to_string())).theme,
Some(ColorTheme::default_dark()),
);
}
}
#[cfg(test)]
mod elem {
use super::Elem;
use crate::theme::{color, color::ColorTheme};
use crossterm::style::Color;
#[cfg(test)]
fn test_theme() -> ColorTheme {
ColorTheme {
user: Color::AnsiValue(230), // Cornsilk1
group: Color::AnsiValue(187), // LightYellow3
permission: color::Permission {
read: Color::Green,
write: Color::Yellow,
exec: Color::Red,
exec_sticky: Color::Magenta,
no_access: Color::AnsiValue(245), // Grey
octal: Color::AnsiValue(6),
acl: Color::DarkCyan,
context: Color::Cyan,
},
attributes: color::Attributes {
read: Color::Green,
archive: Color::Yellow,
hidden: Color::Red,
system: Color::Magenta,
},
file_type: color::FileType {
file: color::File {
exec_uid: Color::AnsiValue(40), // Green3
uid_no_exec: Color::AnsiValue(184), // Yellow3
exec_no_uid: Color::AnsiValue(40), // Green3
no_exec_no_uid: Color::AnsiValue(184), // Yellow3
},
dir: color::Dir {
uid: Color::AnsiValue(33), // DodgerBlue1
no_uid: Color::AnsiValue(33), // DodgerBlue1
},
pipe: Color::AnsiValue(44), // DarkTurquoise
symlink: color::Symlink {
default: Color::AnsiValue(44), // DarkTurquoise
broken: Color::AnsiValue(124), // Red3
missing_target: Color::AnsiValue(124), // Red3
},
block_device: Color::AnsiValue(44), // DarkTurquoise
char_device: Color::AnsiValue(172), // Orange3
socket: Color::AnsiValue(44), // DarkTurquoise
special: Color::AnsiValue(44), // DarkTurquoise
},
date: color::Date {
hour_old: Color::AnsiValue(40), // Green3
day_old: Color::AnsiValue(42), // SpringGreen2
older: Color::AnsiValue(36), // DarkCyan
},
size: color::Size {
none: Color::AnsiValue(245), // Grey
small: Color::AnsiValue(229), // Wheat1
medium: Color::AnsiValue(216), // LightSalmon1
large: Color::AnsiValue(172), // Orange3
},
inode: color::INode {
valid: Color::AnsiValue(13), // Pink
invalid: Color::AnsiValue(245), // Grey
},
links: color::Links {
valid: Color::AnsiValue(13), // Pink
invalid: Color::AnsiValue(245), // Grey
},
tree_edge: Color::AnsiValue(245), // Grey
git_status: Default::default(),
}
}
#[test]
fn test_default_theme_color() {
assert_eq!(
Elem::File {
exec: true,
uid: true
}
.get_color(&test_theme()),
Color::AnsiValue(40),
);
assert_eq!(
Elem::File {
exec: false,
uid: true
}
.get_color(&test_theme()),
Color::AnsiValue(184),
);
assert_eq!(
Elem::File {
exec: true,
uid: false
}
.get_color(&test_theme()),
Color::AnsiValue(40),
);
assert_eq!(
Elem::File {
exec: false,
uid: false
}
.get_color(&test_theme()),
Color::AnsiValue(184),
);
}
}
================================================
FILE: src/config_file.rs
================================================
//! This module provides methods to handle the program's config files and
//! operations related to this.
use crate::flags::HyperlinkOption;
use crate::flags::display::Display;
use crate::flags::icons::{IconOption, IconTheme};
use crate::flags::layout::Layout;
use crate::flags::permission::PermissionFlag;
use crate::flags::size::SizeFlag;
use crate::flags::sorting::{DirGrouping, SortColumn};
use crate::flags::{ColorOption, ThemeOption};
use crate::print_error;
use std::path::{Path, PathBuf};
use serde::Deserialize;
use std::fs;
use std::io;
/// A struct to hold an optional configuration items, and provides methods
/// around error handling in a config file.
#[derive(Eq, PartialEq, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[serde(deny_unknown_fields)]
pub struct Config {
pub classic: Option<bool>,
pub blocks: Option<Vec<String>>,
pub color: Option<Color>,
pub date: Option<String>,
pub dereference: Option<bool>,
pub display: Option<Display>,
pub icons: Option<Icons>,
pub ignore_globs: Option<Vec<String>>,
pub indicators: Option<bool>,
pub layout: Option<Layout>,
pub recursion: Option<Recursion>,
pub size: Option<SizeFlag>,
pub permission: Option<PermissionFlag>,
pub sorting: Option<Sorting>,
pub no_symlink: Option<bool>,
pub total_size: Option<bool>,
pub symlink_arrow: Option<String>,
pub hyperlink: Option<HyperlinkOption>,
pub header: Option<bool>,
pub literal: Option<bool>,
pub truncate_owner: Option<TruncateOwner>,
}
#[derive(Eq, PartialEq, Debug, Deserialize)]
pub struct Color {
pub when: Option<ColorOption>,
pub theme: Option<ThemeOption>,
}
#[derive(Eq, PartialEq, Debug, Deserialize)]
pub struct Icons {
pub when: Option<IconOption>,
pub theme: Option<IconTheme>,
pub separator: Option<String>,
}
#[derive(Eq, PartialEq, Debug, Deserialize)]
pub struct Recursion {
pub enabled: Option<bool>,
pub depth: Option<usize>,
}
#[derive(Eq, PartialEq, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Sorting {
pub column: Option<SortColumn>,
pub reverse: Option<bool>,
pub dir_grouping: Option<DirGrouping>,
}
#[derive(Eq, PartialEq, Debug, Deserialize)]
pub struct TruncateOwner {
pub after: Option<usize>,
pub marker: Option<String>,
}
/// This expand the `~` in path to HOME dir
/// returns the origin one if no `~` found;
/// returns None if error happened when getting home dir
///
/// Implementing this to reuse the `dirs` dependency, avoid adding new one
pub fn expand_home<P: AsRef<Path>>(path: P) -> Option<PathBuf> {
let p = path.as_ref();
if !p.starts_with("~") {
return Some(p.to_path_buf());
}
if p == Path::new("~") {
return dirs::home_dir();
}
dirs::home_dir().map(|mut h| {
if h == Path::new("/") {
// Corner case: `h` root directory;
// don't prepend extra `/`, just drop the tilde.
p.strip_prefix("~").unwrap().to_path_buf()
} else {
h.push(p.strip_prefix("~/").unwrap());
h
}
})
}
impl Config {
/// This constructs a Config struct with all None
pub fn with_none() -> Self {
Self {
classic: None,
blocks: None,
color: None,
date: None,
dereference: None,
display: None,
icons: None,
ignore_globs: None,
indicators: None,
layout: None,
recursion: None,
size: None,
permission: None,
sorting: None,
no_symlink: None,
total_size: None,
symlink_arrow: None,
hyperlink: None,
header: None,
literal: None,
truncate_owner: None,
}
}
/// This constructs a Config struct with a passed file path.
pub fn from_file<P: AsRef<Path>>(file: P) -> Option<Self> {
let file = file.as_ref();
match fs::read(file) {
Ok(f) => match Self::from_yaml(&String::from_utf8_lossy(&f)) {
Ok(c) => Some(c),
Err(e) => {
print_error!(
"Configuration file {} format error, {}.",
file.to_string_lossy(),
e
);
None
}
},
Err(e) => {
if e.kind() != io::ErrorKind::NotFound {
print_error!(
"Can not open config file {}: {}.",
file.to_string_lossy(),
e
);
}
None
}
}
}
/// This constructs a Config struct with a passed [Yaml] str.
/// If error happened, return the [serde_yaml::Error].
fn from_yaml(yaml: &str) -> Result<Self, serde_yaml::Error> {
serde_yaml::from_str::<Self>(yaml)
}
/// Config paths for non-Windows platforms will be read from
/// `$XDG_CONFIG_HOME/lsd` or `$HOME/.config/lsd`
/// (usually, those are the same) in that order.
/// The default paths for Windows will be read from
/// `%APPDATA%\lsd` or `%USERPROFILE%\.config\lsd` in that order.
/// This will apply both to the config file and the theme file.
pub fn config_paths() -> impl Iterator<Item = PathBuf> {
#[cfg(not(windows))]
use xdg::BaseDirectories;
[
dirs::home_dir().map(|h| h.join(".config")),
dirs::config_dir(),
#[cfg(not(windows))]
BaseDirectories::with_prefix("")
.ok()
.map(|p| p.get_config_home()),
]
.iter()
.filter_map(|p| p.as_ref().map(|p| p.join("lsd")))
.collect::<Vec<_>>()
.into_iter()
}
}
impl Default for Config {
/// Try to find either config.yaml or config.yml in the config directories
/// and use the first one that is found. If none are found, or the parsing fails,
/// use the default from DEFAULT_CONFIG.
fn default() -> Self {
Config::config_paths()
.find_map(|p| {
let yaml = p.join("config.yaml");
let yml = p.join("config.yml");
if yaml.is_file() {
Config::from_file(yaml)
} else if yml.is_file() {
Config::from_file(yml)
} else {
None
}
})
.or(Self::from_yaml(DEFAULT_CONFIG).ok())
.expect("Failed to read both config file and default config")
}
}
pub const DEFAULT_CONFIG: &str = r#"---
# == Classic ==
# This is a shorthand to override some of the options to be backwards compatible
# with `ls`. It affects the "color"->"when", "sorting"->"dir-grouping", "date"
# and "icons"->"when" options.
# Possible values: false, true
classic: false
# == Blocks ==
# This specifies the columns and their order when using the long and the tree
# layout.
# Possible values: permission, user, group, context, size, date, name, inode, links, git
blocks:
- permission
- user
- group
- size
- date
- name
# == Color ==
# This has various color options. (Will be expanded in the future.)
color:
# When to colorize the output.
# When "classic" is set, this is set to "never".
# Possible values: never, auto, always
when: auto
# How to colorize the output.
# When "classic" is set, this is set to "no-color".
# Possible values: default, custom
# When "custom" is set, lsd will look in the config directory for `colors.yaml`.
theme: default
# == Date ==
# This specifies the date format for the date column. The freeform format
# accepts a strftime like string.
# When "classic" is set, this is set to "date".
# Possible values: date, locale, relative, '+<date_format>'
# `date_format` will be a `strftime` formatted value. e.g. `date: '+%d %b %y %X'` will give you a date like this: 17 Jun 21 20:14:55
date: date
# == Dereference ==
# Whether to dereference symbolic links.
# Possible values: false, true
dereference: false
# == Display ==
# What items to display. Do not specify this for the default behavior.
# Possible values: all, almost-all, directory-only
# display: all
# == Icons ==
icons:
# When to use icons.
# When "classic" is set, this is set to "never".
# Possible values: always, auto, never
when: auto
# Which icon theme to use.
# Possible values: fancy, unicode
theme: fancy
# Separator between icon and the name
# Default to 1 space
separator: " "
# == Ignore Globs ==
# A list of globs to ignore when listing.
# ignore-globs:
# - .git
# == Indicators ==
# Whether to add indicator characters to certain listed files.
# Possible values: false, true
indicators: false
# == Layout ==
# Which layout to use. "oneline" might be a bit confusing here and should be
# called "one-per-line". It might be changed in the future.
# Possible values: grid, tree, oneline
layout: grid
# == Recursion ==
recursion:
# Whether to enable recursion.
# Possible values: false, true
enabled: false
# How deep the recursion should go. This has to be a positive integer. Leave
# it unspecified for (virtually) infinite.
# depth: 3
# == Size ==
# Specifies the format of the size column.
# Possible values: default, short, bytes
size: default
# == Permission ==
# Specify the format of the permission column
# Possible value: rwx, octal, attributes (windows only), disable
# permission: rwx
# == Sorting ==
sorting:
# Specify what to sort by.
# Possible values: extension, name, time, size, version
column: name
# Whether to reverse the sorting.
# Possible values: false, true
reverse: false
# Whether to group directories together and where.
# When "classic" is set, this is set to "none".
# Possible values: first, last, none
dir-grouping: none
# == No Symlink ==
# Whether to omit showing symlink targets
# Possible values: false, true
no-symlink: false
# == Total size ==
# Whether to display the total size of directories.
# Possible values: false, true
total-size: false
# == Hyperlink ==
# Attach hyperlink to filenames
# Possible values: always, auto, never
hyperlink: never
# == Symlink arrow ==
# Specifies how the symlink arrow display, chars in both ascii and utf8
symlink-arrow: ⇒
# == Header ==
# Whether to display block headers.
# Possible values: false, true
header: false
# == Literal ==
# Whether to show quotes on filenames.
# Possible values: false, true
literal: false
# == Truncate owner ==
# How to truncate the username and group names for a file if they exceed a certain
# number of characters.
truncate-owner:
# Number of characters to keep. By default, no truncation is done (empty value).
after:
# String to be appended to a name if truncated.
marker: ""
"#;
#[cfg(test)]
impl Config {
pub fn builtin() -> Self {
Self::from_yaml(DEFAULT_CONFIG).unwrap()
}
}
#[cfg(test)]
mod tests {
use super::Config;
use crate::config_file;
use crate::flags::HyperlinkOption;
use crate::flags::color::{ColorOption, ThemeOption};
use crate::flags::icons::{IconOption, IconTheme};
use crate::flags::layout::Layout;
use crate::flags::size::SizeFlag;
use crate::flags::sorting::{DirGrouping, SortColumn};
#[test]
fn test_read_default() {
let c = Config::from_yaml(config_file::DEFAULT_CONFIG).unwrap();
assert_eq!(
Config {
classic: Some(false),
blocks: Some(vec![
"permission".into(),
"user".into(),
"group".into(),
"size".into(),
"date".into(),
"name".into(),
]),
color: Some(config_file::Color {
when: Some(ColorOption::Auto),
theme: Some(ThemeOption::Default)
}),
date: Some("date".to_string()),
dereference: Some(false),
display: None,
icons: Some(config_file::Icons {
when: Some(IconOption::Auto),
theme: Some(IconTheme::Fancy),
separator: Some(" ".to_string()),
}),
ignore_globs: None,
indicators: Some(false),
layout: Some(Layout::Grid),
recursion: Some(config_file::Recursion {
enabled: Some(false),
depth: None,
}),
size: Some(SizeFlag::Default),
permission: None,
sorting: Some(config_file::Sorting {
column: Some(SortColumn::Name),
reverse: Some(false),
dir_grouping: Some(DirGrouping::None),
}),
no_symlink: Some(false),
total_size: Some(false),
symlink_arrow: Some("⇒".into()),
hyperlink: Some(HyperlinkOption::Never),
header: Some(false),
literal: Some(false),
truncate_owner: Some(config_file::TruncateOwner {
after: None,
marker: Some("".to_string()),
}),
},
c
);
}
#[test]
fn test_read_config_ok() {
let c = Config::from_yaml("classic: true").unwrap();
assert!(c.classic.unwrap())
}
#[test]
fn test_read_config_bad_bool() {
let c = Config::from_yaml("classic: notbool");
assert!(c.is_err())
}
#[test]
fn test_read_config_file_not_found() {
let c = Config::from_file("not-existed");
assert!(c.is_none())
}
#[test]
fn test_read_bad_display() {
assert!(Config::from_yaml("display: bad").is_err())
}
}
================================================
FILE: src/core.rs
================================================
use crate::color::Colors;
use crate::display;
use crate::flags::{
ColorOption, Display, Flags, HyperlinkOption, Layout, Literal, SortOrder, ThemeOption,
};
use crate::git::GitCache;
use crate::icon::Icons;
use crate::meta::Meta;
use crate::{ExitCode, print_error, print_output, sort};
use std::path::PathBuf;
#[cfg(not(target_os = "windows"))]
use std::io;
#[cfg(not(target_os = "windows"))]
use std::os::unix::io::AsRawFd;
use crate::flags::blocks::Block;
use crate::git_theme::GitTheme;
#[cfg(target_os = "windows")]
use terminal_size::terminal_size;
pub struct Core {
flags: Flags,
icons: Icons,
colors: Colors,
git_theme: GitTheme,
sorters: Vec<(SortOrder, sort::SortFn)>,
}
impl Core {
pub fn new(mut flags: Flags) -> Self {
// Check through libc if stdout is a tty. Unix specific so not on windows.
// Determine color output availability (and initialize color output (for Windows 10))
#[cfg(not(target_os = "windows"))]
let tty_available = unsafe { libc::isatty(io::stdout().as_raw_fd()) == 1 };
#[cfg(not(target_os = "windows"))]
let console_color_ok = true;
#[cfg(target_os = "windows")]
let tty_available = terminal_size().is_some(); // terminal_size allows us to know if the stdout is a tty or not.
#[cfg(target_os = "windows")]
let console_color_ok = crossterm::ansi_support::supports_ansi();
let color_theme = match (tty_available && console_color_ok, flags.color.when) {
(_, ColorOption::Never) | (false, ColorOption::Auto) => ThemeOption::NoColor,
_ => flags.color.theme.clone(),
};
// Ensure that --color=... option is respected by crossterm.
// ref https://github.com/lsd-rs/lsd/issues/1159
crossterm::style::force_color_output(color_theme != ThemeOption::NoColor);
let icon_when = flags.icons.when;
let icon_theme = flags.icons.theme.clone();
// TODO: Rework this so that flags passed downstream does not
// have Auto option for any (icon, color, hyperlink).
if matches!(flags.hyperlink, HyperlinkOption::Auto) {
flags.hyperlink = if tty_available {
HyperlinkOption::Always
} else {
HyperlinkOption::Never
}
}
let icon_separator = flags.icons.separator.0.clone();
// The output is not a tty, this means the command is piped. e.g.
//
// lsd -l | less
//
// Most of the programs does not handle correctly the ansi colors
// or require a raw output (like the `wc` command).
if !tty_available {
// we should not overwrite the tree layout
if flags.layout != Layout::Tree {
flags.layout = Layout::OneLine;
}
flags.literal = Literal(true);
};
let sorters = sort::assemble_sorters(&flags);
Self {
flags,
colors: Colors::new(color_theme),
icons: Icons::new(tty_available, icon_when, icon_theme, icon_separator),
git_theme: GitTheme::new(),
sorters,
}
}
pub fn run(self, paths: Vec<PathBuf>) -> ExitCode {
let (mut meta_list, exit_code) = self.fetch(paths);
self.sort(&mut meta_list);
self.display(&meta_list);
exit_code
}
fn fetch(&self, paths: Vec<PathBuf>) -> (Vec<Meta>, ExitCode) {
let mut exit_code = ExitCode::OK;
let mut meta_list = Vec::with_capacity(paths.len());
let depth = match self.flags.layout {
Layout::Tree => self.flags.recursion.depth,
_ if self.flags.recursion.enabled => self.flags.recursion.depth,
_ => 1,
};
#[cfg(target_os = "windows")]
use crate::config_file;
#[cfg(target_os = "windows")]
let paths: Vec<PathBuf> = paths
.into_iter()
.filter_map(config_file::expand_home)
.collect();
for path in paths {
let mut meta =
match Meta::from_path(&path, self.flags.dereference.0, self.flags.permission) {
Ok(meta) => meta,
Err(err) => {
print_error!("{}: {}.", path.display(), err);
exit_code.set_if_greater(ExitCode::MajorIssue);
continue;
}
};
let cache = if self.flags.blocks.0.contains(&Block::GitStatus) {
Some(GitCache::new(&path))
} else {
None
};
let recurse =
self.flags.layout == Layout::Tree || self.flags.display != Display::DirectoryOnly;
if recurse {
match meta.recurse_into(depth, &self.flags, cache.as_ref()) {
Ok((content, path_exit_code)) => {
meta.content = content;
meta.git_status = cache.and_then(|cache| cache.get(&meta.path, true));
meta_list.push(meta);
exit_code.set_if_greater(path_exit_code);
}
Err(err) => {
print_error!("lsd: {}: {}\n", path.display(), err);
exit_code.set_if_greater(ExitCode::MinorIssue);
continue;
}
};
} else {
meta.git_status = cache.and_then(|cache| cache.get(&meta.path, true));
meta_list.push(meta);
};
}
// Only calculate the total size of a directory if it will be displayed
if self.flags.total_size.0 && self.flags.blocks.displays_size() {
for meta in &mut meta_list.iter_mut() {
meta.calculate_total_size();
}
}
(meta_list, exit_code)
}
fn sort(&self, metas: &mut Vec<Meta>) {
metas.sort_unstable_by(|a, b| sort::by_meta(&self.sorters, a, b));
for meta in metas {
if let Some(ref mut content) = meta.content {
self.sort(content);
}
}
}
fn display(&self, metas: &[Meta]) {
let output = if self.flags.layout == Layout::Tree {
display::tree(
metas,
&self.flags,
&self.colors,
&self.icons,
&self.git_theme,
)
} else {
display::grid(
metas,
&self.flags,
&self.colors,
&self.icons,
&self.git_theme,
)
};
print_output!("{}", output);
}
}
================================================
FILE: src/display.rs
================================================
use crate::color::{Colors, Elem};
use crate::flags::blocks::Block;
use crate::flags::{Display, Flags, HyperlinkOption, Layout};
use crate::git_theme::GitTheme;
use crate::icon::Icons;
use crate::meta::name::DisplayOption;
use crate::meta::{FileType, Meta, OwnerCache};
use std::collections::HashMap;
use term_grid::{Cell, Direction, Filling, Grid, GridOptions};
use terminal_size::terminal_size;
use unicode_width::UnicodeWidthStr;
const EDGE: &str = "\u{251c}\u{2500}\u{2500}"; // "├──"
const LINE: &str = "\u{2502} "; // "│ "
const CORNER: &str = "\u{2514}\u{2500}\u{2500}"; // "└──"
const BLANK: &str = " ";
pub fn grid(
metas: &[Meta],
flags: &Flags,
colors: &Colors,
icons: &Icons,
git_theme: &GitTheme,
) -> String {
let term_width = terminal_size().map(|(w, _)| w.0 as usize);
let owner_cache = OwnerCache::default();
inner_display_grid(
&DisplayOption::None,
metas,
&owner_cache,
flags,
colors,
icons,
git_theme,
0,
term_width,
)
}
pub fn tree(
metas: &[Meta],
flags: &Flags,
colors: &Colors,
icons: &Icons,
git_theme: &GitTheme,
) -> String {
let mut grid = Grid::new(GridOptions {
filling: Filling::Spaces(1),
direction: Direction::LeftToRight,
});
let padding_rules = get_padding_rules(metas, flags);
let mut index = 0;
for (i, block) in flags.blocks.0.iter().enumerate() {
if block == &Block::Name {
index = i;
break;
}
}
let owner_cache = OwnerCache::default();
for cell in inner_display_tree(
metas,
&owner_cache,
flags,
colors,
icons,
git_theme,
(0, ""),
&padding_rules,
index,
) {
grid.add(cell);
}
grid.fit_into_columns(flags.blocks.0.len()).to_string()
}
#[allow(clippy::too_many_arguments)] // should wrap flags, colors, icons, git_theme into one struct
fn inner_display_grid(
display_option: &DisplayOption,
metas: &[Meta],
owner_cache: &OwnerCache,
flags: &Flags,
colors: &Colors,
icons: &Icons,
git_theme: &GitTheme,
depth: usize,
term_width: Option<usize>,
) -> String {
let mut output = String::new();
let mut cells = Vec::new();
let padding_rules = get_padding_rules(metas, flags);
let mut grid = match flags.layout {
Layout::OneLine => Grid::new(GridOptions {
filling: Filling::Spaces(1),
direction: Direction::LeftToRight,
}),
_ => Grid::new(GridOptions {
filling: Filling::Spaces(2),
direction: Direction::TopToBottom,
}),
};
// The first iteration (depth == 0) corresponds to the inputs given by the
// user. We defer displaying directories given by the user unless we've been
// asked to display the directory itself (rather than its contents).
let skip_dirs = (depth == 0) && (flags.display != Display::DirectoryOnly);
// print the files first.
for meta in metas {
// Maybe skip showing the directory meta now; show its contents later.
if skip_dirs
&& (matches!(meta.file_type, FileType::Directory { .. })
|| (matches!(meta.file_type, FileType::SymLink { is_dir: true }))
&& flags.blocks.0.len() == 1)
{
continue;
}
let blocks = get_output(
meta,
owner_cache,
colors,
icons,
git_theme,
flags,
display_option,
&padding_rules,
(0, ""),
);
for block in blocks {
cells.push(Cell {
width: get_visible_width(&block, flags.hyperlink == HyperlinkOption::Always),
contents: block,
});
}
}
// Print block headers
if flags.header.0 && flags.layout == Layout::OneLine && !cells.is_empty() {
add_header(flags, &cells, &mut grid);
}
for cell in cells {
grid.add(cell);
}
if flags.layout == Layout::Grid {
if let Some(tw) = term_width {
if let Some(gridded_output) = grid.fit_into_width(tw) {
output += &gridded_output.to_string();
} else {
//does not fit into grid, usually because (some) filename(s)
//are longer or almost as long as term_width
//print line by line instead!
output += &grid.fit_into_columns(1).to_string();
}
} else {
output += &grid.fit_into_columns(1).to_string();
}
} else {
output += &grid.fit_into_columns(flags.blocks.0.len()).to_string();
}
let should_display_folder_path = should_display_folder_path(depth, metas);
// print the folder content
for meta in metas {
if let Some(content) = &meta.content {
if should_display_folder_path {
output += &display_folder_path(meta);
}
let display_option = DisplayOption::Relative {
base_path: &meta.path,
};
output += &inner_display_grid(
&display_option,
content,
owner_cache,
flags,
colors,
icons,
git_theme,
depth + 1,
term_width,
);
}
}
output
}
fn add_header(flags: &Flags, cells: &[Cell], grid: &mut Grid) {
let num_columns: usize = flags.blocks.0.len();
let mut widths = flags
.blocks
.0
.iter()
.map(|b| get_visible_width(b.get_header(), flags.hyperlink == HyperlinkOption::Always))
.collect::<Vec<usize>>();
// find max widths of each column
for (index, cell) in cells.iter().enumerate() {
let index = index % num_columns;
widths[index] = std::cmp::max(widths[index], cell.width);
}
for (idx, block) in flags.blocks.0.iter().enumerate() {
// center and underline header
let underlined_header = crossterm::style::Stylize::attribute(
format!("{: ^1$}", block.get_header(), widths[idx]),
crossterm::style::Attribute::Underlined,
)
.to_string();
grid.add(Cell {
width: widths[idx],
contents: underlined_header,
});
}
}
#[allow(clippy::too_many_arguments)]
fn inner_display_tree(
metas: &[Meta],
owner_cache: &OwnerCache,
flags: &Flags,
colors: &Colors,
icons: &Icons,
git_theme: &GitTheme,
tree_depth_prefix: (usize, &str),
padding_rules: &HashMap<Block, usize>,
tree_index: usize,
) -> Vec<Cell> {
let mut cells = Vec::new();
let last_idx = metas.len();
for (idx, meta) in metas.iter().enumerate() {
let current_prefix = if tree_depth_prefix.0 > 0 {
if idx + 1 != last_idx {
// is last folder elem
format!("{}{} ", tree_depth_prefix.1, EDGE)
} else {
format!("{}{} ", tree_depth_prefix.1, CORNER)
}
} else {
tree_depth_prefix.1.to_string()
};
for block in get_output(
meta,
owner_cache,
colors,
icons,
git_theme,
flags,
&DisplayOption::FileName,
padding_rules,
(tree_index, ¤t_prefix),
) {
cells.push(Cell {
width: get_visible_width(&block, flags.hyperlink == HyperlinkOption::Always),
contents: block,
});
}
if let Some(content) = &meta.content {
let new_prefix = if tree_depth_prefix.0 > 0 {
if idx + 1 != last_idx {
// is last folder elem
format!("{}{} ", tree_depth_prefix.1, LINE)
} else {
format!("{}{} ", tree_depth_prefix.1, BLANK)
}
} else {
tree_depth_prefix.1.to_string()
};
cells.extend(inner_display_tree(
content,
owner_cache,
flags,
colors,
icons,
git_theme,
(tree_depth_prefix.0 + 1, &new_prefix),
padding_rules,
tree_index,
));
}
}
cells
}
fn should_display_folder_path(depth: usize, metas: &[Meta]) -> bool {
if depth > 0 {
true
} else {
let folder_number = metas
.iter()
.filter(|x| {
matches!(x.file_type, FileType::Directory { .. })
|| (matches!(x.file_type, FileType::SymLink { is_dir: true }))
})
.count();
folder_number > 1 || folder_number < metas.len()
}
}
fn display_folder_path(meta: &Meta) -> String {
format!("\n{}:\n", meta.path.to_string_lossy())
}
#[allow(clippy::too_many_arguments)]
fn get_output(
meta: &Meta,
owner_cache: &OwnerCache,
colors: &Colors,
icons: &Icons,
git_theme: &GitTheme,
flags: &Flags,
display_option: &DisplayOption,
padding_rules: &HashMap<Block, usize>,
tree: (usize, &str),
) -> Vec<String> {
let mut strings: Vec<String> = Vec::new();
let colorize_missing = |string: &str| colors.colorize(string, &Elem::NoAccess);
for (i, block) in flags.blocks.0.iter().enumerate() {
let mut block_vec = if Layout::Tree == flags.layout && tree.0 == i {
vec![colors.colorize(tree.1, &Elem::TreeEdge)]
} else {
Vec::new()
};
match block {
Block::INode => block_vec.push(match &meta.inode {
Some(inode) => inode.render(colors),
None => colorize_missing("?"),
}),
Block::Links => block_vec.push(match &meta.links {
Some(links) => links.render(colors),
None => colorize_missing("?"),
}),
Block::Permission => {
block_vec.extend([
meta.file_type.render(colors),
match &meta.permissions_or_attributes {
Some(permissions_or_attributes) => {
permissions_or_attributes.render(colors, flags)
}
None => colorize_missing("?????????"),
},
match &meta.access_control {
Some(access_control) => access_control.render_method(colors),
None => colorize_missing(""),
},
]);
}
Block::User => block_vec.push(match &meta.owner {
Some(owner) => owner.render_user(colors, owner_cache, flags),
None => colorize_missing("?"),
}),
Block::Group => block_vec.push(match &meta.owner {
Some(owner) => owner.render_group(colors, owner_cache, flags),
None => colorize_missing("?"),
}),
Block::Context => block_vec.push(match &meta.access_control {
Some(access_control) => access_control.render_context(colors),
None => colorize_missing("?"),
}),
Block::Size => {
let pad = if Layout::Tree == flags.layout && 0 == tree.0 && 0 == i {
None
} else {
Some(padding_rules[&Block::SizeValue])
};
block_vec.push(match &meta.size {
Some(size) => size.render(colors, flags, pad),
None => colorize_missing("?"),
})
}
Block::SizeValue => block_vec.push(match &meta.size {
Some(size) => size.render_value(colors, flags),
None => colorize_missing("?"),
}),
Block::Date => block_vec.push(match &meta.date {
Some(date) => date.render(colors, flags),
None => colorize_missing("?"),
}),
Block::Name => {
block_vec.extend([
meta.name.render(
colors,
icons,
display_option,
flags.hyperlink,
flags.literal.0,
),
meta.indicator.render(flags),
]);
if !(flags.no_symlink.0 || flags.dereference.0 || flags.layout == Layout::Grid) {
block_vec.push(meta.symlink.render(colors, flags))
}
}
Block::GitStatus => {
if let Some(_s) = &meta.git_status {
block_vec.push(_s.render(colors, git_theme));
}
}
};
strings.push(
block_vec
.into_iter()
.map(|s| s.to_string())
.collect::<Vec<String>>()
.join(""),
);
}
strings
}
fn get_visible_width(input: &str, hyperlink: bool) -> usize {
let mut nb_invisible_char = 0;
// If the input has color, do not compute the length contributed by the color to the actual length
for (idx, _) in input.match_indices("\u{1b}[") {
let (_, s) = input.split_at(idx);
let m_pos = s.find('m');
if let Some(len) = m_pos {
// len points to the 'm' character, we must include 'm' to invisible characters
nb_invisible_char += len + 1;
}
}
if hyperlink {
for (idx, _) in input.match_indices("\x1B]8;;") {
let (_, s) = input.split_at(idx);
let m_pos = s.find("\x1B\x5C");
if let Some(len) = m_pos {
// len points to the '\x1B' character, we must include both '\x1B' and '\x5C' to invisible characters
nb_invisible_char += len + 2
}
}
}
// `UnicodeWidthStr::width` counts all unicode characters including escape '\u{1b}' and hyperlink '\x1B'
UnicodeWidthStr::width(input) - nb_invisible_char
}
fn detect_size_lengths(metas: &[Meta], flags: &Flags) -> usize {
let mut max_value_length: usize = 0;
for meta in metas {
let value_len = match &meta.size {
Some(size) => size.value_string(flags).len(),
None => 0,
};
if value_len > max_value_length {
max_value_length = value_len;
}
if Layout::Tree == flags.layout {
if let Some(subs) = &meta.content {
let sub_length = detect_size_lengths(subs, flags);
if sub_length > max_value_length {
max_value_length = sub_length;
}
}
}
}
max_value_length
}
fn get_padding_rules(metas: &[Meta], flags: &Flags) -> HashMap<Block, usize> {
let mut padding_rules: HashMap<Block, usize> = HashMap::new();
if flags.blocks.0.contains(&Block::Size) {
let size_val = detect_size_lengths(metas, flags);
padding_rules.insert(Block::SizeValue, size_val);
}
padding_rules
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Config;
use crate::app::Cli;
use crate::color;
use crate::color::Colors;
use crate::flags::{HyperlinkOption, IconOption, IconTheme as FlagTheme, PermissionFlag};
use crate::icon::Icons;
use crate::meta::{FileType, Name};
use crate::{flags, sort};
use assert_fs::prelude::*;
use clap::Parser;
use std::path::Path;
use tempfile::tempdir;
#[test]
fn test_display_get_visible_width_without_icons() {
for (s, l) in [
("Hello,world!", 22),
("ASCII1234-_", 11),
("制作样本。", 10),
("日本語", 6),
("샘플은 무료로 드리겠습니다", 28),
("👩🐩", 4),
("🔬", 2),
] {
let path = Path::new(s);
let name = Name::new(
path,
FileType::File {
exec: false,
uid: false,
},
);
let output = name
.render(
&Colors::new(color::ThemeOption::NoColor),
&Icons::new(false, IconOption::Never, FlagTheme::Fancy, " ".to_string()),
&DisplayOption::FileName,
HyperlinkOption::Never,
false,
)
.to_string();
assert_eq!(get_visible_width(&output, false), l);
}
}
#[test]
fn test_display_get_visible_width_with_icons() {
for (s, l) in [
// Add 3 characters for the icons.
("Hello,world!", 24),
("ASCII1234-_", 13),
("File with space", 19),
("制作样本。", 12),
("日本語", 8),
("샘플은 무료로 드리겠습니다", 30),
("👩🐩", 6),
("🔬", 4),
] {
let path = Path::new(s);
let name = Name::new(
path,
FileType::File {
exec: false,
uid: false,
},
);
let output = name
.render(
&Colors::new(color::ThemeOption::NoColor),
&Icons::new(false, IconOption::Always, FlagTheme::Fancy, " ".to_string()),
&DisplayOption::FileName,
HyperlinkOption::Never,
false,
)
.to_string();
assert_eq!(get_visible_width(&output, false), l);
}
}
#[test]
fn test_display_get_visible_width_with_colors() {
// crossterm implicitly colors if NO_COLOR is set.
crossterm::style::force_color_output(true);
for (s, l) in [
("Hello,world!", 22),
("ASCII1234-_", 11),
("File with space", 17),
("制作样本。", 10),
("日本語", 6),
("샘플은 무료로 드리겠습니다", 28),
("👩🐩", 4),
("🔬", 2),
] {
let path = Path::new(s);
let name = Name::new(
path,
FileType::File {
exec: false,
uid: false,
},
);
let output = name
.render(
&Colors::new(color::ThemeOption::NoLscolors),
&Icons::new(false, IconOption::Never, FlagTheme::Fancy, " ".to_string()),
&DisplayOption::FileName,
HyperlinkOption::Never,
false,
)
.to_string();
// check if the color is present.
assert!(
output.starts_with("\u{1b}[38;5;"),
"{output:?} should start with color"
);
assert!(output.ends_with("[39m"), "reset foreground color");
assert_eq!(get_visible_width(&output, false), l, "visible match");
}
}
#[test]
fn test_display_get_visible_width_without_colors() {
for (s, l) in [
("Hello,world!", 22),
("ASCII1234-_", 11),
("File with space", 17),
("制作样本。", 10),
("日本語", 6),
("샘플은 무료로 드리겠습니다", 28),
("👩🐩", 4),
("🔬", 2),
] {
let path = Path::new(s);
let name = Name::new(
path,
FileType::File {
exec: false,
uid: false,
},
);
let output = name
.render(
&Colors::new(color::ThemeOption::NoColor),
&Icons::new(false, IconOption::Never, FlagTheme::Fancy, " ".to_string()),
&DisplayOption::FileName,
HyperlinkOption::Never,
false,
)
.to_string();
// check if the color is present.
assert!(!output.starts_with("\u{1b}[38;5;"));
assert!(!output.ends_with("[0m"));
assert_eq!(get_visible_width(&output, false), l);
}
}
#[test]
fn test_display_get_visible_width_hypelink_simple() {
for (s, l) in [
("Hello,world!", 22),
("ASCII1234-_", 11),
("File with space", 15),
("制作样本。", 10),
("日本語", 6),
("샘플은 무료로 드리겠습니다", 26),
("👩🐩", 4),
("🔬", 2),
] {
// rending name require actual file, so we are mocking that
let output = format!("\x1B]8;;{}\x1B\x5C{}\x1B]8;;\x1B\x5C", "url://fake-url", s);
assert_eq!(get_visible_width(&output, true), l);
}
}
fn sort(metas: &mut Vec<Meta>, sorters: &Vec<(flags::SortOrder, sort::SortFn)>) {
metas.sort_unstable_by(|a, b| sort::by_meta(sorters, a, b));
for meta in metas {
if let Some(ref mut content) = meta.content {
sort(content, sorters);
}
}
}
#[test]
fn test_display_tree_with_all() {
let argv = ["lsd", "--tree", "--all"];
let cli = Cli::try_parse_from(argv).unwrap();
let flags = Flags::configure_from(&cli, &Config::with_none()).unwrap();
let dir = assert_fs::TempDir::new().unwrap();
dir.child("one.d").create_dir_all().unwrap();
dir.child("one.d/two").touch().unwrap();
dir.child("one.d/.hidden").touch().unwrap();
let mut metas = Meta::from_path(Path::new(dir.path()), false, PermissionFlag::Rwx)
.unwrap()
.recurse_into(42, &flags, None)
.unwrap()
.0
.unwrap();
sort(&mut metas, &sort::assemble_sorters(&flags));
let output = tree(
&metas,
&flags,
&Colors::new(color::ThemeOption::NoColor),
&Icons::new(false, IconOption::Never, FlagTheme::Fancy, " ".to_string()),
&GitTheme::new(),
);
assert_eq!("one.d\n├── .hidden\n└── two\n", output);
}
/// Different level of folder may form a different width
/// we must make sure it is aligned in all level
///
/// dir has a bytes size
/// empty file has an empty size
/// `---blocks size,name` can help us for this case
#[test]
fn test_tree_align_subfolder() {
let argv = ["lsd", "--tree", "--blocks", "size,name"];
let cli = Cli::try_parse_from(argv).unwrap();
let flags = Flags::configure_from(&cli, &Config::with_none()).unwrap();
let dir = assert_fs::TempDir::new().unwrap();
dir.child("dir").create_dir_all().unwrap();
dir.child("dir/file").touch().unwrap();
let metas = Meta::from_path(Path::new(dir.path()), false, PermissionFlag::Rwx)
.unwrap()
.recurse_into(42, &flags, None)
.unwrap()
.0
.unwrap();
let output = tree(
&metas,
&flags,
&Colors::new(color::ThemeOption::NoColor),
&Icons::new(false, IconOption::Never, FlagTheme::Fancy, " ".to_string()),
&GitTheme::new(),
);
let length_before_b = |i| -> usize {
output
.lines()
.nth(i)
.unwrap()
.split(['K', 'B'])
.next()
.unwrap()
.len()
};
assert_eq!(length_before_b(0), length_before_b(1));
assert_eq!(
output.lines().next().unwrap().find('d'),
output.lines().nth(1).unwrap().find('└')
);
}
#[test]
#[cfg(unix)]
fn test_tree_size_first_without_name() {
let argv = ["lsd", "--tree", "--blocks", "size,permission"];
let cli = Cli::try_parse_from(argv).unwrap();
let flags = Flags::configure_from(&cli, &Config::with_none()).unwrap();
let dir = assert_fs::TempDir::new().unwrap();
dir.child("dir").create_dir_all().unwrap();
dir.child("dir/file").touch().unwrap();
let metas = Meta::from_path(Path::new(dir.path()), false, PermissionFlag::Rwx)
.unwrap()
.recurse_into(42, &flags, None)
.unwrap()
.0
.unwrap();
let output = tree(
&metas,
&flags,
&Colors::new(color::ThemeOption::NoColor),
&Icons::new(false, IconOption::Never, FlagTheme::Fancy, " ".to_string()),
&GitTheme::new(),
);
assert_eq!(output.lines().nth(1).unwrap().chars().next().unwrap(), '└');
assert_eq!(
output
.lines()
.next()
.unwrap()
.chars()
.position(|x| x == 'd'),
output
.lines()
.nth(1)
.unwrap()
.chars()
.position(|x| x == '.'),
);
}
#[test]
fn test_tree_edge_before_name() {
let argv = ["lsd", "--tree", "--long"];
let cli = Cli::try_parse_from(argv).unwrap();
let flags = Flags::configure_from(&cli, &Config::with_none()).unwrap();
let dir = assert_fs::TempDir::new().unwrap();
dir.child("one.d").create_dir_all().unwrap();
dir.child("one.d/two").touch().unwrap();
let metas = Meta::from_path(Path::new(dir.path()), false, PermissionFlag::Rwx)
.unwrap()
.recurse_into(42, &flags, None)
.unwrap()
.0
.unwrap();
let output = tree(
&metas,
&flags,
&Colors::new(color::ThemeOption::NoColor),
&Icons::new(false, IconOption::Never, FlagTheme::Fancy, " ".to_string()),
&GitTheme::new(),
);
assert!(output.ends_with("└── two\n"));
}
#[test]
fn test_grid_all_block_headers() {
let argv = [
"lsd",
"--header",
"--blocks",
"permission,user,group,size,date,name,inode,links",
];
let cli = Cli::try_parse_from(argv).unwrap();
let flags = Flags::configure_from(&cli, &Config::with_none()).unwrap();
let dir = assert_fs::TempDir::new().unwrap();
dir.child("testdir").create_dir_all().unwrap();
dir.child("test").touch().unwrap();
let metas = Meta::from_path(Path::new(dir.path()), false, PermissionFlag::Rwx)
.unwrap()
.recurse_into(1, &flags, None)
.unwrap()
.0
.unwrap();
let output = grid(
&metas,
&flags,
&Colors::new(color::ThemeOption::NoColor),
&Icons::new(false, IconOption::Never, FlagTheme::Fancy, " ".to_string()),
&GitTheme::new(),
);
dir.close().unwrap();
assert!(output.contains("Permissions"));
assert!(output.contains("User"));
assert!(output.contains("Group"));
assert!(output.contains("Size"));
assert!(output.contains("Date Modified"));
assert!(output.contains("Name"));
assert!(output.contains("INode"));
assert!(output.contains("Links"));
}
#[test]
fn test_grid_no_header_with_empty_meta() {
let argv = ["lsd", "--header", "-l"];
let cli = Cli::try_parse_from(argv).unwrap();
let flags = Flags::configure_from(&cli, &Config::with_none()).unwrap();
let dir = assert_fs::TempDir::new().unwrap();
dir.child("testdir").create_dir_all().unwrap();
let metas = Meta::from_path(Path::new(dir.path()), false, PermissionFlag::Rwx)
.unwrap()
.recurse_into(1, &flags, None)
.unwrap()
.0
.unwrap();
let output = grid(
&metas,
&flags,
&Colors::new(color::ThemeOption::NoColor),
&Icons::new(false, IconOption::Never, FlagTheme::Fancy, " ".to_string()),
&GitTheme::new(),
);
dir.close().unwrap();
assert!(!output.contains("Permissions"));
assert!(!output.contains("User"));
assert!(!output.contains("Group"));
assert!(!output.contains("Size"));
assert!(!output.contains("Date Modified"));
assert!(!output.contains("Name"));
}
#[test]
fn test_folder_path() {
let tmp_dir = tempdir().expect("failed to create temp dir");
let file_path = tmp_dir.path().join("file");
std::fs::File::create(&file_path).expect("failed to create the file");
let file = Meta::from_path(&file_path, false, PermissionFlag::Rwx).unwrap();
let dir_path = tmp_dir.path().join("dir");
std::fs::create_dir(&dir_path).expect("failed to create the dir");
let dir = Meta::from_path(&dir_path, false, PermissionFlag::Rwx).unwrap();
assert_eq!(
display_folder_path(&dir),
format!(
"\n{}{}dir:\n",
tmp_dir.path().to_string_lossy(),
std::path::MAIN_SEPARATOR
)
);
const YES: bool = true;
const NO: bool = false;
assert_eq!(
should_display_folder_path(0, &[file.clone()]),
YES // doesn't matter since there is no folder
);
assert_eq!(should_display_folder_path(0, &[dir.clone()]), NO);
assert_eq!(
should_display_folder_path(0, &[file.clone(), dir.clone()]),
YES
);
assert_eq!(
should_display_folder_path(0, &[dir.clone(), dir.clone()]),
YES
);
assert_eq!(
should_display_folder_path(0, &[file.clone(), file.clone()]),
YES // doesn't matter since there is no folder
);
drop(dir); // to avoid clippy complains about previous .clone()
drop(file);
}
#[cfg(unix)]
#[test]
fn test_folder_path_with_links() {
let tmp_dir = tempdir().expect("failed to create temp dir");
let file_path = tmp_dir.path().join("file");
std::fs::File::create(&file_path).expect("failed to create the file");
let file = Meta::from_path(&file_path, false, PermissionFlag::Rwx).unwrap();
let dir_path = tmp_dir.path().join("dir");
std::fs::create_dir(&dir_path).expect("failed to create the dir");
let dir = Meta::from_path(&dir_path, false, PermissionFlag::Rwx).unwrap();
let link_path = tmp_dir.path().join("link");
std::os::unix::fs::symlink("dir", &link_path).unwrap();
let link = Meta::from_path(&link_path, false, PermissionFlag::Rwx).unwrap();
const YES: bool = true;
const NO: bool = false;
assert_eq!(should_display_folder_path(0, &[link.clone()]), NO);
assert_eq!(
should_display_folder_path(0, &[file.clone(), link.clone()]),
YES
);
assert_eq!(
should_display_folder_path(0, &[dir.clone(), link.clone()]),
YES
);
drop(dir); // to avoid clippy complains about previous .clone()
drop(file);
drop(link);
}
}
================================================
FILE: src/flags/blocks.rs
================================================
//! This module defines the [Blocks] struct. To set it up from [Cli], a [Config] and its
//! [Default] value, use its [configure_from](Blocks::configure_from) method.
use super::Configurable;
use crate::app::Cli;
use crate::config_file::Config;
use crate::print_error;
use std::convert::TryFrom;
/// A struct to hold a [Vec] of [Block]s and to provide methods to create it.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Blocks(pub Vec<Block>);
impl Blocks {
/// This returns a Blocks struct for the long format.
///
/// It contains the [Block]s [Permission](Block::Permission), [User](Block::User),
/// [Group](Block::Group), [Size](Block::Size), [Date](Block::Date) and [Name](Block::Name).
fn long() -> Self {
Self(vec![
Block::Permission,
Block::User,
Block::Group,
Block::Size,
Block::Date,
Block::Name,
])
}
/// Checks whether `self` already contains a [Block] of variant [INode](Block::INode).
fn contains_inode(&self) -> bool {
self.0.contains(&Block::INode)
}
/// Prepends a [Block] of variant [INode](Block::INode) to `self`.
fn prepend_inode(&mut self) {
self.0.insert(0, Block::INode);
}
/// Prepends a [Block] of variant [INode](Block::INode), if `self` does not already contain a
/// Block of that variant.
fn optional_prepend_inode(&mut self) {
if !self.contains_inode() {
self.prepend_inode()
}
}
pub fn displays_size(&self) -> bool {
self.0.contains(&Block::Size)
}
/// Inserts a [Block] of variant [INode](Block::Context), if `self` does not already contain a
/// [Block] of that variant. The positioning will be best-effort approximation of coreutils
/// ls position for a security context
fn optional_insert_context(&mut self) {
if self.0.contains(&Block::Context) {
return;
}
let mut pos = self.0.iter().position(|elem| *elem == Block::Group);
if pos.is_none() {
pos = self.0.iter().position(|elem| *elem == Block::User);
}
match pos {
Some(pos) => self.0.insert(pos + 1, Block::Context),
None => self.0.insert(0, Block::Context),
}
}
/// Checks whether `self` already contains a [Block] of variant [GitStatus](Block::GitStatus).
fn contains_git_status(&self) -> bool {
self.0.contains(&Block::GitStatus)
}
/// Put a [Block] of variant [GitStatus](Block::GitStatus) on the left of [GitStatus](Block::Name) to `self`.
fn add_git_status(&mut self) {
if let Some(position) = self.0.iter().position(|&b| b == Block::Name) {
self.0.insert(position, Block::GitStatus);
} else {
self.0.push(Block::GitStatus);
}
}
/// Prepends a [Block] of variant [GitStatus](Block::GitStatus), if `self` does not already contain a
/// Block of that variant.
fn optional_add_git_status(&mut self) {
if !self.contains_git_status() {
self.add_git_status()
}
}
}
impl Configurable<Self> for Blocks {
/// Returns a value from either [Cli], a [Config] or a default value.
/// Unless the "long" argument is passed, this returns [Default::default]. Otherwise the first
/// value, that is not [None], is used. The order of precedence for the value used is:
/// - [from_cli](Blocks::from_cli)
/// - [from_config](Blocks::from_config)
/// - [long](Blocks::long)
///
/// No matter if the "long" argument was passed, if the "inode" argument is passed and the
/// `Blocks` does not contain a [Block] of variant [INode](Block::INode) yet, one is prepended
/// to the returned value.
fn configure_from(cli: &Cli, config: &Config) -> Self {
let mut blocks = if cli.long {
Self::long()
} else {
Default::default()
};
if cli.long {
if let Some(value) = Self::from_config(config) {
blocks = value;
}
}
if let Some(value) = Self::from_cli(cli) {
blocks = value;
}
if cli.context {
blocks.optional_insert_context();
}
if cli.inode {
blocks.optional_prepend_inode();
}
if !cfg!(feature = "no-git") && cli.git && cli.long {
blocks.optional_add_git_status();
}
blocks
}
/// Get a potential `Blocks` struct from [Cli].
///
/// If the "blocks" argument is passed, then this returns a `Blocks` containing the parameter
/// values in a [Some]. Otherwise this returns [None].
fn from_cli(cli: &Cli) -> Option<Self> {
if cli.blocks.is_empty() {
return None;
}
let blocks = cli
.blocks
.iter()
.map(|b| Block::try_from(b.as_str()).unwrap())
.collect();
Some(Self(blocks))
}
/// Get a potential `Blocks` struct from a [Config].
///
/// If the [Config] contains an array of blocks values,
/// its [String] values is returned as `Blocks` in a [Some].
/// Otherwise it returns [None].
fn from_config(config: &Config) -> Option<Self> {
if let Some(c) = &config.blocks {
let mut blocks: Vec<Block> = vec![];
for b in c.iter() {
match Block::try_from(b.as_str()) {
Ok(block) => blocks.push(block),
Err(err) => print_error!("{}.", err),
}
}
if blocks.is_empty() {
None
} else {
Some(Self(blocks))
}
} else {
None
}
}
}
/// The default value for `Blocks` contains a [Vec] of [Name](Block::Name).
impl Default for Blocks {
fn default() -> Self {
Self(vec![Block::Name])
}
}
/// A block of data to show.
#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Block {
Permission,
User,
Group,
Context,
Size,
SizeValue,
Date,
Name,
INode,
Links,
GitStatus,
}
impl Block {
pub fn get_header(&self) -> &'static str {
match self {
Block::INode => "INode",
Block::Links => "Links",
Block::Permission => "Permissions",
Block::User => "User",
Block::Group => "Group",
Block::Context => "Context",
Block::Size => "Size",
Block::SizeValue => "SizeValue",
Block::Date => "Date Modified",
Block::Name => "Name",
Block::GitStatus => "Git",
}
}
}
impl TryFrom<&str> for Block {
type Error = String;
fn try_from(string: &str) -> Result<Self, Self::Error> {
match string {
"permission" => Ok(Self::Permission),
"user" => Ok(Self::User),
"group" => Ok(Self::Group),
"context" => Ok(Self::Context),
"size" => Ok(Self::Size),
"size_value" => Ok(Self::SizeValue),
"date" => Ok(Self::Date),
"name" => Ok(Self::Name),
"inode" => Ok(Self::INode),
"links" => Ok(Self::Links),
"git" => Ok(Self::GitStatus),
_ => Err(format!("Not a valid block name: {string}")),
}
}
}
#[cfg(test)]
mod test_blocks {
use clap::Parser;
use super::Block;
use super::Blocks;
use crate::app::Cli;
use crate::config_file::Config;
use crate::flags::Configurable;
#[test]
fn test_configure_from_without_long() {
let argv = ["lsd"];
let target = Blocks::default();
let cli = Cli::try_parse_from(argv).unwrap();
let result = Blocks::configure_from(&cli, &Config::with_none());
assert_eq!(result, target);
}
#[test]
fn test_configure_from_with_long() {
let argv = ["lsd", "--long"];
let target = Blocks::long();
let cli = Cli::try_parse_from(argv).unwrap();
let result = Blocks::configure_from(&cli, &Config::with_none());
assert_eq!(result, target);
}
#[test]
fn test_configure_from_with_blocks_and_without_long() {
let argv = ["lsd", "--blocks", "permission"];
let target = Blocks(vec![Block::Permission]);
let cli = Cli::try_parse_from(argv).unwrap();
let result = Blocks::configure_from(&cli, &Config::with_none());
assert_eq!(result, target);
}
#[test]
fn test_configure_from_with_blocks_and_long() {
let argv = ["lsd", "--long", "--blocks", "permission"];
let target = Blocks(vec![Block::Permission]);
let cli = Cli::try_parse_from(argv).unwrap();
let result = Blocks::configure_from(&cli, &Config::with_none());
assert_eq!(result, target);
}
#[test]
fn test_configure_from_with_inode() {
let argv = ["lsd", "--inode"];
let target = Blocks(vec![Block::INode, Block::Name]);
let cli = Cli::try_parse_from(argv).unwrap();
let result = Blocks::configure_from(&cli, &Config::with_none());
assert_eq!(result, target);
}
#[test]
fn test_configure_from_prepend_inode_without_long() {
let argv = ["lsd", "--blocks", "permission", "--inode"];
let target = Blocks(vec![Block::INode, Block::Permission]);
let cli = Cli::try_parse_from(argv).unwrap();
let result = Blocks::configure_from(&cli, &Config::with_none());
assert_eq!(result, target);
}
#[test]
fn test_configure_from_prepend_inode_with_long() {
let argv = ["lsd", "--long", "--blocks", "permission", "--inode"];
let target = Blocks(vec![Block::INode, Block::Permission]);
let cli = Cli::try_parse_from(argv).unwrap();
let result = Blocks::configure_from(&cli, &Config::with_none());
assert_eq!(result, target);
}
#[test]
fn test_configure_from_ignore_prepend_inode_without_long() {
let argv = ["lsd", "--blocks", "permission,inode", "--inode"];
let target = Blocks(vec![Block::Permission, Block::INode]);
let cli = Cli::try_parse_from(argv).unwrap();
let result = Blocks::configure_from(&cli, &Config::with_none());
assert_eq!(result, target);
}
#[test]
fn test_configure_from_ignore_prepend_inode_with_long() {
let argv = ["lsd", "--long", "--blocks", "permission,inode", "--inode"];
let target = Blocks(vec![Block::Permission, Block::INode]);
let cli = Cli::try_parse_from(argv).unwrap();
let result = Blocks::configure_from(&cli, &Config::with_none());
assert_eq!(result, target);
}
#[test]
fn test_from_cli_none() {
let argv = ["lsd"];
let cli = Cli::try_parse_from(argv).unwrap();
assert!(Blocks::from_cli(&cli).is_none());
}
#[test]
fn test_from_cli_one() {
let argv = ["lsd", "--blocks", "permission"];
let cli = Cli::try_parse_from(argv).unwrap();
let test_blocks = Blocks(vec![Block::Permission]);
assert_eq!(Blocks::from_cli(&cli), Some(test_blocks));
}
#[test]
fn test_from_cli_multi_occurences() {
let argv = ["lsd", "--blocks", "permission", "--blocks", "name"];
let cli = Cli::try_parse_from(argv).unwrap();
let test_blocks = Blocks(vec![Block::Permission, Block::Name]);
assert_eq!(Blocks::from_cli(&cli), Some(test_blocks));
}
#[test]
fn test_from_cli_multi_values() {
let argv = ["lsd", "--blocks", "permission,name"];
let cli = Cli::try_parse_from(argv).unwrap();
let test_blocks = Blocks(vec![Block::Permission, Block::Name]);
assert_eq!(Blocks::from_cli(&cli), Some(test_blocks));
}
#[test]
fn test_from_cli_reversed_default() {
let argv = ["lsd", "--blocks", "name,date,size,group,user,permission"];
let cli = Cli::try_parse_from(argv).unwrap();
let test_blocks = Blocks(vec![
Block::Name,
Block::Date,
Block::Size,
Block::Group,
Block::User,
Block::Permission,
]);
assert_eq!(Blocks::from_cli(&cli), Some(test_blocks));
}
#[test]
fn test_from_cli_every_second_one() {
let argv = ["lsd", "--blocks", "permission,group,date"];
let cli = Cli::try_parse_from(argv).unwrap();
let test_blocks = Blocks(vec![Block::Permission, Block::Group, Block::Date]);
assert_eq!(Blocks::from_cli(&cli), Some(test_blocks));
}
#[cfg(not(feature = "no-git"))]
#[test]
fn test_from_cli_implicit_add_git_block() {
let argv = vec![
"lsd",
"--blocks",
"permission,name,group,date",
"--git",
"--long",
];
let cli = Cli::try_parse_from(argv).unwrap();
let test_blocks = Blocks(vec![
Block::Permission,
Block::GitStatus,
Block::Name,
Block::Group,
Block::Date,
]);
assert_eq!(
Blocks::configure_from(&cli, &Config::with_none()),
test_blocks
);
}
#[test]
fn test_from_config_none() {
assert_eq!(None, Blocks::from_config(&Config::with_none()));
}
#[test]
fn test_from_config_one() {
let mut c = Config::with_none();
c.blocks = Some(vec!["permission".into()]);
let blocks = Blocks(vec![Block::Permission]);
assert_eq!(Some(blocks), Blocks::from_config(&c));
}
#[t
gitextract_ggwc0en1/
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug.yml
│ │ └── bug_report.md
│ ├── PULL_REQUEST_TEMPLATE
│ └── workflows/
│ └── CICD.yml
├── .gitignore
├── .release.toml
├── CODEOWNERS
├── Cargo.toml
├── LICENSE
├── OWNERS
├── README.md
├── build.rs
├── ci/
│ ├── before_deploy.bash
│ ├── before_install.bash
│ └── script.bash
├── doc/
│ ├── colors.md
│ ├── lsd.md
│ └── samples/
│ ├── colors-sample.yaml
│ ├── config-sample.yaml
│ └── icons-sample.yaml
├── lsd.spec
├── rustfmt.toml
├── src/
│ ├── app.rs
│ ├── color.rs
│ ├── config_file.rs
│ ├── core.rs
│ ├── display.rs
│ ├── flags/
│ │ ├── blocks.rs
│ │ ├── color.rs
│ │ ├── date.rs
│ │ ├── dereference.rs
│ │ ├── display.rs
│ │ ├── header.rs
│ │ ├── hyperlink.rs
│ │ ├── icons.rs
│ │ ├── ignore_globs.rs
│ │ ├── indicators.rs
│ │ ├── layout.rs
│ │ ├── literal.rs
│ │ ├── permission.rs
│ │ ├── recursion.rs
│ │ ├── size.rs
│ │ ├── sorting.rs
│ │ ├── symlink_arrow.rs
│ │ ├── symlinks.rs
│ │ ├── total_size.rs
│ │ └── truncate_owner.rs
│ ├── flags.rs
│ ├── git.rs
│ ├── git_theme.rs
│ ├── icon.rs
│ ├── main.rs
│ ├── meta/
│ │ ├── access_control.rs
│ │ ├── date.rs
│ │ ├── filetype.rs
│ │ ├── git_file_status.rs
│ │ ├── indicator.rs
│ │ ├── inode.rs
│ │ ├── links.rs
│ │ ├── locale.rs
│ │ ├── mod.rs
│ │ ├── name.rs
│ │ ├── owner.rs
│ │ ├── permissions.rs
│ │ ├── permissions_or_attributes.rs
│ │ ├── size.rs
│ │ ├── symlink.rs
│ │ ├── windows_attributes.rs
│ │ └── windows_utils.rs
│ ├── sort.rs
│ ├── theme/
│ │ ├── color.rs
│ │ ├── git.rs
│ │ └── icon.rs
│ └── theme.rs
└── tests/
└── integration.rs
SYMBOL INDEX (829 symbols across 54 files)
FILE: build.rs
function main (line 17) | fn main() {
FILE: src/app.rs
type Cli (line 7) | pub struct Cli {
function validate_date_argument (line 205) | fn validate_date_argument(arg: &str) -> Result<String, String> {
function validate_time_format (line 215) | pub fn validate_time_format(formatter: &str) -> Result<String, String> {
type LabelFilter (line 267) | struct LabelFilter<Filter: Fn(&'static str) -> bool, const C: usize>([&'...
function from (line 272) | fn from(label_filter: LabelFilter<Filter, C>) -> Self {
FILE: src/color.rs
type Elem (line 13) | pub enum Elem {
method has_suid (line 79) | fn has_suid(&self) -> bool {
method get_color (line 83) | pub fn get_color(&self, theme: &ColorTheme) -> Color {
type ColoredString (line 176) | pub type ColoredString = StyledContent<String>;
type Colors (line 178) | pub struct Colors {
method new (line 184) | pub fn new(t: ThemeOption) -> Self {
method colorize (line 215) | pub fn colorize<S: Into<String>>(&self, input: S, elem: &Elem) -> Colo...
method colorize_using_path (line 219) | pub fn colorize_using_path(&self, input: String, path: &Path, elem: &E...
method default_style (line 227) | pub fn default_style() -> ContentStyle {
method style_from_path (line 231) | fn style_from_path(&self, path: &Path) -> Option<ContentStyle> {
method style (line 238) | fn style(&self, elem: &Elem) -> ContentStyle {
method style_default (line 251) | fn style_default(&self, elem: &Elem) -> ContentStyle {
method get_indicator_from_elem (line 264) | fn get_indicator_from_elem(&self, elem: &Elem) -> Option<Indicator> {
function to_content_style (line 295) | fn to_content_style(ls: &lscolors::Style) -> ContentStyle {
function test_color_new_no_color_theme (line 363) | fn test_color_new_no_color_theme() {
function test_color_new_custom_theme (line 368) | fn test_color_new_custom_theme() {
function test_color_new_custom_no_file_theme (line 376) | fn test_color_new_custom_no_file_theme() {
function test_color_new_bad_legacy_custom_theme (line 384) | fn test_color_new_bad_legacy_custom_theme() {
function test_theme (line 399) | fn test_theme() -> ColorTheme {
function test_default_theme_color (line 466) | fn test_default_theme_color() {
FILE: src/config_file.rs
type Config (line 25) | pub struct Config {
method with_none (line 109) | pub fn with_none() -> Self {
method from_file (line 136) | pub fn from_file<P: AsRef<Path>>(file: P) -> Option<Self> {
method from_yaml (line 165) | fn from_yaml(yaml: &str) -> Result<Self, serde_yaml::Error> {
method config_paths (line 175) | pub fn config_paths() -> impl Iterator<Item = PathBuf> {
method builtin (line 369) | pub fn builtin() -> Self {
type Color (line 50) | pub struct Color {
type Icons (line 56) | pub struct Icons {
type Recursion (line 63) | pub struct Recursion {
type Sorting (line 70) | pub struct Sorting {
type TruncateOwner (line 77) | pub struct TruncateOwner {
function expand_home (line 87) | pub fn expand_home<P: AsRef<Path>>(path: P) -> Option<PathBuf> {
method default (line 198) | fn default() -> Self {
constant DEFAULT_CONFIG (line 216) | pub const DEFAULT_CONFIG: &str = r#"---
function test_read_default (line 386) | fn test_read_default() {
function test_read_config_ok (line 441) | fn test_read_config_ok() {
function test_read_config_bad_bool (line 447) | fn test_read_config_bad_bool() {
function test_read_config_file_not_found (line 453) | fn test_read_config_file_not_found() {
function test_read_bad_display (line 459) | fn test_read_bad_display() {
FILE: src/core.rs
type Core (line 23) | pub struct Core {
method new (line 32) | pub fn new(mut flags: Flags) -> Self {
method run (line 97) | pub fn run(self, paths: Vec<PathBuf>) -> ExitCode {
method fetch (line 105) | fn fetch(&self, paths: Vec<PathBuf>) -> (Vec<Meta>, ExitCode) {
method sort (line 170) | fn sort(&self, metas: &mut Vec<Meta>) {
method display (line 180) | fn display(&self, metas: &[Meta]) {
FILE: src/display.rs
constant EDGE (line 13) | const EDGE: &str = "\u{251c}\u{2500}\u{2500}";
constant LINE (line 14) | const LINE: &str = "\u{2502} ";
constant CORNER (line 15) | const CORNER: &str = "\u{2514}\u{2500}\u{2500}";
constant BLANK (line 16) | const BLANK: &str = " ";
function grid (line 18) | pub fn grid(
function tree (line 41) | pub fn tree(
function inner_display_grid (line 82) | fn inner_display_grid(
function add_header (line 200) | fn add_header(flags: &Flags, cells: &[Cell], grid: &mut Grid) {
function inner_display_tree (line 232) | fn inner_display_tree(
function should_display_folder_path (line 304) | fn should_display_folder_path(depth: usize, metas: &[Meta]) -> bool {
function display_folder_path (line 320) | fn display_folder_path(meta: &Meta) -> String {
function get_output (line 325) | fn get_output(
function get_visible_width (line 433) | fn get_visible_width(input: &str, hyperlink: bool) -> usize {
function detect_size_lengths (line 463) | fn detect_size_lengths(metas: &[Meta], flags: &Flags) -> usize {
function get_padding_rules (line 489) | fn get_padding_rules(metas: &[Meta], flags: &Flags) -> HashMap<Block, us...
function test_display_get_visible_width_without_icons (line 518) | fn test_display_get_visible_width_without_icons() {
function test_display_get_visible_width_with_icons (line 551) | fn test_display_get_visible_width_with_icons() {
function test_display_get_visible_width_with_colors (line 586) | fn test_display_get_visible_width_with_colors() {
function test_display_get_visible_width_without_colors (line 630) | fn test_display_get_visible_width_without_colors() {
function test_display_get_visible_width_hypelink_simple (line 668) | fn test_display_get_visible_width_hypelink_simple() {
function sort (line 685) | fn sort(metas: &mut Vec<Meta>, sorters: &Vec<(flags::SortOrder, sort::So...
function test_display_tree_with_all (line 696) | fn test_display_tree_with_all() {
function test_tree_align_subfolder (line 730) | fn test_tree_align_subfolder() {
function test_tree_size_first_without_name (line 771) | fn test_tree_size_first_without_name() {
function test_tree_edge_before_name (line 811) | fn test_tree_edge_before_name() {
function test_grid_all_block_headers (line 837) | fn test_grid_all_block_headers() {
function test_grid_no_header_with_empty_meta (line 877) | fn test_grid_no_header_with_empty_meta() {
function test_folder_path (line 909) | fn test_folder_path() {
function test_folder_path_with_links (line 956) | fn test_folder_path_with_links() {
FILE: src/flags.rs
type Flags (line 59) | pub struct Flags {
method configure_from (line 89) | pub fn configure_from(cli: &Cli, config: &Config) -> Result<Self, Erro...
type Configurable (line 117) | pub trait Configurable<T>
method configure_from (line 132) | fn configure_from(cli: &Cli, config: &Config) -> T {
method from_cli (line 149) | fn from_cli(cli: &Cli) -> Option<T>;
method from_config (line 153) | fn from_config(config: &Config) -> Option<T>;
method from_environment (line 156) | fn from_environment() -> Option<T> {
FILE: src/flags/blocks.rs
type Blocks (line 13) | pub struct Blocks(pub Vec<Block>);
method long (line 20) | fn long() -> Self {
method contains_inode (line 32) | fn contains_inode(&self) -> bool {
method prepend_inode (line 37) | fn prepend_inode(&mut self) {
method optional_prepend_inode (line 43) | fn optional_prepend_inode(&mut self) {
method displays_size (line 49) | pub fn displays_size(&self) -> bool {
method optional_insert_context (line 56) | fn optional_insert_context(&mut self) {
method contains_git_status (line 71) | fn contains_git_status(&self) -> bool {
method add_git_status (line 76) | fn add_git_status(&mut self) {
method optional_add_git_status (line 86) | fn optional_add_git_status(&mut self) {
method configure_from (line 104) | fn configure_from(cli: &Cli, config: &Config) -> Self {
method from_cli (line 139) | fn from_cli(cli: &Cli) -> Option<Self> {
method from_config (line 157) | fn from_config(config: &Config) -> Option<Self> {
method default (line 179) | fn default() -> Self {
type Block (line 186) | pub enum Block {
method get_header (line 201) | pub fn get_header(&self) -> &'static str {
type Error (line 219) | type Error = String;
method try_from (line 221) | fn try_from(string: &str) -> Result<Self, Self::Error> {
function test_configure_from_without_long (line 251) | fn test_configure_from_without_long() {
function test_configure_from_with_long (line 262) | fn test_configure_from_with_long() {
function test_configure_from_with_blocks_and_without_long (line 273) | fn test_configure_from_with_blocks_and_without_long() {
function test_configure_from_with_blocks_and_long (line 284) | fn test_configure_from_with_blocks_and_long() {
function test_configure_from_with_inode (line 295) | fn test_configure_from_with_inode() {
function test_configure_from_prepend_inode_without_long (line 306) | fn test_configure_from_prepend_inode_without_long() {
function test_configure_from_prepend_inode_with_long (line 317) | fn test_configure_from_prepend_inode_with_long() {
function test_configure_from_ignore_prepend_inode_without_long (line 328) | fn test_configure_from_ignore_prepend_inode_without_long() {
function test_configure_from_ignore_prepend_inode_with_long (line 339) | fn test_configure_from_ignore_prepend_inode_with_long() {
function test_from_cli_none (line 350) | fn test_from_cli_none() {
function test_from_cli_one (line 357) | fn test_from_cli_one() {
function test_from_cli_multi_occurences (line 365) | fn test_from_cli_multi_occurences() {
function test_from_cli_multi_values (line 373) | fn test_from_cli_multi_values() {
function test_from_cli_reversed_default (line 381) | fn test_from_cli_reversed_default() {
function test_from_cli_every_second_one (line 396) | fn test_from_cli_every_second_one() {
function test_from_cli_implicit_add_git_block (line 405) | fn test_from_cli_implicit_add_git_block() {
function test_from_config_none (line 428) | fn test_from_config_none() {
function test_from_config_one (line 433) | fn test_from_config_one() {
function test_from_config_reversed_default (line 442) | fn test_from_config_reversed_default() {
function test_from_config_every_second_one (line 465) | fn test_from_config_every_second_one() {
function test_from_config_invalid_is_ignored (line 473) | fn test_from_config_invalid_is_ignored() {
function test_context_not_present_on_cli (line 481) | fn test_context_not_present_on_cli() {
function test_context_present_if_context_on (line 490) | fn test_context_present_if_context_on() {
function test_only_one_context_no_other_blocks_affected (line 499) | fn test_only_one_context_no_other_blocks_affected() {
function test_err (line 528) | fn test_err() {
function test_permission (line 536) | fn test_permission() {
function test_user (line 541) | fn test_user() {
function test_group (line 546) | fn test_group() {
function test_size (line 551) | fn test_size() {
function test_size_value (line 556) | fn test_size_value() {
function test_date (line 561) | fn test_date() {
function test_name (line 566) | fn test_name() {
function test_inode (line 571) | fn test_inode() {
function test_links (line 576) | fn test_links() {
function test_context (line 581) | fn test_context() {
function test_block_headers (line 586) | fn test_block_headers() {
function test_git_status (line 601) | fn test_git_status() {
FILE: src/flags/color.rs
type Color (line 16) | pub struct Color {
method configure_from (line 26) | pub fn configure_from(cli: &Cli, config: &Config) -> Self {
type ThemeOption (line 37) | pub enum ThemeOption {
method from_config (line 48) | fn from_config(config: &Config) -> ThemeOption {
method deserialize (line 62) | fn deserialize<D>(deserializer: D) -> Result<ThemeOption, D::Error>
type ColorOption (line 94) | pub enum ColorOption {
method from_arg_str (line 102) | fn from_arg_str(value: &str) -> Self {
method from_cli (line 119) | fn from_cli(cli: &Cli) -> Option<Self> {
method from_config (line 132) | fn from_config(config: &Config) -> Option<Self> {
method from_environment (line 140) | fn from_environment() -> Option<Self> {
function test_from_cli_none (line 160) | fn test_from_cli_none() {
function test_from_cli_always (line 167) | fn test_from_cli_always() {
function test_from_cli_auto (line 174) | fn test_from_cli_auto() {
function test_from_cli_never (line 181) | fn test_from_cli_never() {
function test_from_env_no_color (line 188) | fn test_from_env_no_color() {
function test_from_cli_classic_mode (line 195) | fn test_from_cli_classic_mode() {
function test_from_cli_color_multiple (line 202) | fn test_from_cli_color_multiple() {
function test_from_config_none (line 209) | fn test_from_config_none() {
function test_from_config_always (line 214) | fn test_from_config_always() {
function test_from_config_auto (line 225) | fn test_from_config_auto() {
function test_from_config_never (line 235) | fn test_from_config_never() {
function test_from_config_classic_mode (line 245) | fn test_from_config_classic_mode() {
function test_from_config_none_default (line 262) | fn test_from_config_none_default() {
function test_from_config_default (line 270) | fn test_from_config_default() {
function test_from_config_no_color (line 281) | fn test_from_config_no_color() {
function test_from_config_no_lscolor (line 291) | fn test_from_config_no_lscolor() {
function test_from_config_bad_file_flag (line 301) | fn test_from_config_bad_file_flag() {
function test_from_config_classic_mode (line 314) | fn test_from_config_classic_mode() {
FILE: src/flags/date.rs
type DateFlag (line 12) | pub enum DateFlag {
method from_format_string (line 23) | fn from_format_string(value: &str) -> Option<Self> {
method from_str (line 33) | fn from_str<S: AsRef<str>>(value: S) -> Option<Self> {
method from_cli (line 54) | fn from_cli(cli: &Cli) -> Option<Self> {
method from_config (line 68) | fn from_config(config: &Config) -> Option<Self> {
method from_environment (line 77) | fn from_environment() -> Option<Self> {
function test_from_cli_none (line 107) | fn test_from_cli_none() {
function test_from_cli_date (line 114) | fn test_from_cli_date() {
function test_from_cli_locale (line 121) | fn test_from_cli_locale() {
function test_from_cli_relative (line 128) | fn test_from_cli_relative() {
function test_from_cli_format (line 135) | fn test_from_cli_format() {
function test_from_cli_format_invalid (line 146) | fn test_from_cli_format_invalid() {
function test_from_cli_classic_mode (line 153) | fn test_from_cli_classic_mode() {
function test_from_cli_date_multi (line 160) | fn test_from_cli_date_multi() {
function test_from_config_none (line 167) | fn test_from_config_none() {
function test_from_config_date (line 172) | fn test_from_config_date() {
function test_from_config_relative (line 180) | fn test_from_config_relative() {
function test_from_config_format (line 187) | fn test_from_config_format() {
function test_from_config_format_invalid (line 197) | fn test_from_config_format_invalid() {
function test_from_config_classic_mode (line 204) | fn test_from_config_classic_mode() {
function test_from_environment_none (line 213) | fn test_from_environment_none() {
function test_from_environment_full_iso (line 221) | fn test_from_environment_full_iso() {
function test_from_environment_long_iso (line 232) | fn test_from_environment_long_iso() {
function test_from_environment_iso (line 243) | fn test_from_environment_iso() {
function test_from_environment_format (line 251) | fn test_from_environment_format() {
function test_parsing_order_arg (line 262) | fn test_parsing_order_arg() {
function test_parsing_order_env (line 277) | fn test_parsing_order_env() {
function test_parsing_order_config (line 292) | fn test_parsing_order_config() {
FILE: src/flags/dereference.rs
type Dereference (line 11) | pub struct Dereference(pub bool);
method from_cli (line 18) | fn from_cli(cli: &Cli) -> Option<Self> {
method from_config (line 30) | fn from_config(config: &Config) -> Option<Self> {
function test_from_cli_none (line 46) | fn test_from_cli_none() {
function test_from_cli_true (line 53) | fn test_from_cli_true() {
function test_from_config_none (line 60) | fn test_from_config_none() {
function test_from_config_true (line 65) | fn test_from_config_true() {
function test_from_config_false (line 72) | fn test_from_config_false() {
FILE: src/flags/display.rs
type Display (line 14) | pub enum Display {
method from_cli (line 30) | fn from_cli(cli: &Cli) -> Option<Self> {
method from_config (line 54) | fn from_config(config: &Config) -> Option<Self> {
function test_from_cli_none (line 70) | fn test_from_cli_none() {
function test_from_cli_system_protected (line 77) | fn test_from_cli_system_protected() {
function test_from_cli_all (line 88) | fn test_from_cli_all() {
function test_from_cli_almost_all (line 95) | fn test_from_cli_almost_all() {
function test_from_cli_directory_only (line 102) | fn test_from_cli_directory_only() {
function test_from_config_none (line 109) | fn test_from_config_none() {
function test_from_config_all (line 114) | fn test_from_config_all() {
function test_from_config_almost_all (line 121) | fn test_from_config_almost_all() {
function test_from_config_directory_only (line 128) | fn test_from_config_directory_only() {
function test_from_config_visible_only (line 135) | fn test_from_config_visible_only() {
FILE: src/flags/header.rs
type Header (line 11) | pub struct Header(pub bool);
method from_cli (line 18) | fn from_cli(cli: &Cli) -> Option<Self> {
method from_config (line 27) | fn from_config(config: &Config) -> Option<Self> {
function test_from_cli_none (line 43) | fn test_from_cli_none() {
function test_from_cli_true (line 50) | fn test_from_cli_true() {
function test_from_config_none (line 57) | fn test_from_config_none() {
function test_from_config_true (line 62) | fn test_from_config_true() {
function test_from_config_false (line 69) | fn test_from_config_false() {
FILE: src/flags/hyperlink.rs
type HyperlinkOption (line 14) | pub enum HyperlinkOption {
method from_arg_str (line 22) | fn from_arg_str(value: &str) -> Self {
method from_cli (line 39) | fn from_cli(cli: &Cli) -> Option<Self> {
method from_config (line 53) | fn from_config(config: &Config) -> Option<Self> {
function test_from_cli_none (line 73) | fn test_from_cli_none() {
function test_from_cli_always (line 80) | fn test_from_cli_always() {
function test_from_cli_auto (line 90) | fn test_from_cli_auto() {
function test_from_cli_never (line 97) | fn test_from_cli_never() {
function test_from_cli_classic_mode (line 107) | fn test_from_cli_classic_mode() {
function test_from_cli_hyperlink_when_multi (line 117) | fn test_from_cli_hyperlink_when_multi() {
function test_from_config_none (line 127) | fn test_from_config_none() {
function test_from_config_always (line 132) | fn test_from_config_always() {
function test_from_config_auto (line 142) | fn test_from_config_auto() {
function test_from_config_never (line 152) | fn test_from_config_never() {
function test_from_config_classic_mode (line 162) | fn test_from_config_classic_mode() {
FILE: src/flags/icons.rs
type Icons (line 13) | pub struct Icons {
method configure_from (line 27) | pub fn configure_from(cli: &Cli, config: &Config) -> Self {
type IconOption (line 42) | pub enum IconOption {
method from_arg_str (line 50) | fn from_arg_str(value: &str) -> Self {
method from_cli (line 67) | fn from_cli(cli: &Cli) -> Option<Self> {
method from_config (line 81) | fn from_config(config: &Config) -> Option<Self> {
type IconTheme (line 93) | pub enum IconTheme {
method from_arg_str (line 100) | fn from_arg_str(value: &str) -> Self {
method from_cli (line 115) | fn from_cli(cli: &Cli) -> Option<Self> {
method from_config (line 124) | fn from_config(config: &Config) -> Option<Self> {
type IconSeparator (line 131) | pub struct IconSeparator(pub String);
method from_cli (line 138) | fn from_cli(_cli: &Cli) -> Option<Self> {
method from_config (line 146) | fn from_config(config: &Config) -> Option<Self> {
method default (line 158) | fn default() -> Self {
function test_from_cli_none (line 174) | fn test_from_cli_none() {
function test_from_cli_always (line 181) | fn test_from_cli_always() {
function test_from_cli_auto (line 188) | fn test_from_cli_auto() {
function test_from_cli_never (line 195) | fn test_from_cli_never() {
function test_from_cli_classic_mode (line 202) | fn test_from_cli_classic_mode() {
function test_from_cli_icon_when_multi (line 209) | fn test_from_cli_icon_when_multi() {
function test_from_config_none (line 216) | fn test_from_config_none() {
function test_from_config_always (line 221) | fn test_from_config_always() {
function test_from_config_auto (line 232) | fn test_from_config_auto() {
function test_from_config_never (line 243) | fn test_from_config_never() {
function test_from_config_classic_mode (line 254) | fn test_from_config_classic_mode() {
function test_from_cli_none (line 277) | fn test_from_cli_none() {
function test_from_cli_fancy (line 284) | fn test_from_cli_fancy() {
function test_from_cli_unicode (line 291) | fn test_from_cli_unicode() {
function test_from_cli_icon_multi (line 298) | fn test_from_cli_icon_multi() {
function test_from_config_none (line 305) | fn test_from_config_none() {
function test_from_config_fancy (line 310) | fn test_from_config_fancy() {
function test_from_config_unicode (line 321) | fn test_from_config_unicode() {
function test_from_config_default (line 340) | fn test_from_config_default() {
function test_from_config_custom (line 352) | fn test_from_config_custom() {
FILE: src/flags/ignore_globs.rs
type IgnoreGlobs (line 13) | pub struct IgnoreGlobs(pub GlobSet);
method configure_from (line 25) | pub fn configure_from(cli: &Cli, config: &Config) -> Result<Self, Erro...
method from_cli (line 42) | fn from_cli(cli: &Cli) -> Option<Result<Self, Error>> {
method from_config (line 68) | fn from_config(config: &Config) -> Option<Result<Self, Error>> {
method create_glob (line 87) | fn create_glob(pattern: &str) -> Result<Glob, Error> {
method create_glob_set (line 94) | fn create_glob_set(builder: &GlobSetBuilder) -> Result<GlobSet, Error> {
method default (line 103) | fn default() -> Self {
function test_configuration_from_none (line 124) | fn test_configuration_from_none() {
function test_configuration_from_args (line 134) | fn test_configuration_from_args() {
function test_configuration_from_config (line 144) | fn test_configuration_from_config() {
function test_from_cli_none (line 153) | fn test_from_cli_none() {
function test_from_config_none (line 160) | fn test_from_config_none() {
FILE: src/flags/indicators.rs
type Indicators (line 11) | pub struct Indicators(pub bool);
method from_cli (line 18) | fn from_cli(cli: &Cli) -> Option<Self> {
method from_config (line 31) | fn from_config(config: &Config) -> Option<Self> {
function test_from_cli_none (line 47) | fn test_from_cli_none() {
function test_from_cli_true (line 54) | fn test_from_cli_true() {
function test_from_config_none (line 61) | fn test_from_config_none() {
function test_from_config_true (line 66) | fn test_from_config_true() {
function test_from_config_false (line 73) | fn test_from_config_false() {
FILE: src/flags/layout.rs
type Layout (line 14) | pub enum Layout {
method from_cli (line 28) | fn from_cli(cli: &Cli) -> Option<Self> {
method from_config (line 45) | fn from_config(config: &Config) -> Option<Self> {
function test_from_cli_none (line 61) | fn test_from_cli_none() {
function test_from_cli_tree (line 68) | fn test_from_cli_tree() {
function test_from_cli_oneline (line 75) | fn test_from_cli_oneline() {
function test_from_cli_oneline_through_long (line 82) | fn test_from_cli_oneline_through_long() {
function test_from_cli_oneline_through_blocks (line 89) | fn test_from_cli_oneline_through_blocks() {
function test_from_config_none (line 96) | fn test_from_config_none() {
function test_from_config_tree (line 101) | fn test_from_config_tree() {
function test_from_config_oneline (line 108) | fn test_from_config_oneline() {
function test_from_config_grid (line 115) | fn test_from_config_grid() {
FILE: src/flags/literal.rs
type Literal (line 11) | pub struct Literal(pub bool);
method from_cli (line 18) | fn from_cli(cli: &Cli) -> Option<Self> {
method from_config (line 27) | fn from_config(config: &Config) -> Option<Self> {
function test_from_cli_none (line 43) | fn test_from_cli_none() {
function test_from_cli_literal (line 50) | fn test_from_cli_literal() {
function test_from_config_none (line 57) | fn test_from_config_none() {
function test_from_config_true (line 62) | fn test_from_config_true() {
function test_from_config_false (line 69) | fn test_from_config_false() {
FILE: src/flags/permission.rs
type PermissionFlag (line 14) | pub enum PermissionFlag {
method from_arg_str (line 28) | fn from_arg_str(value: &str) -> Self {
method from_cli (line 47) | fn from_cli(cli: &Cli) -> Option<Self> {
method from_config (line 61) | fn from_config(config: &Config) -> Option<Self> {
function test_default (line 81) | fn test_default() {
function test_from_cli_none (line 91) | fn test_from_cli_none() {
function test_from_cli_default (line 98) | fn test_from_cli_default() {
function test_from_cli_short (line 105) | fn test_from_cli_short() {
function test_from_cli_attributes (line 112) | fn test_from_cli_attributes() {
function test_from_cli_permissions_disable (line 122) | fn test_from_cli_permissions_disable() {
function test_from_cli_unknown (line 133) | fn test_from_cli_unknown() {
function test_from_cli_permissions_multi (line 138) | fn test_from_cli_permissions_multi() {
function test_from_cli_permissions_classic (line 145) | fn test_from_cli_permissions_classic() {
function test_from_config_none (line 152) | fn test_from_config_none() {
function test_from_config_rwx (line 157) | fn test_from_config_rwx() {
function test_from_config_octal (line 164) | fn test_from_config_octal() {
function test_from_config_classic_mode (line 171) | fn test_from_config_classic_mode() {
FILE: src/flags/recursion.rs
type Recursion (line 9) | pub struct Recursion {
method configure_from (line 25) | pub fn configure_from(cli: &Cli, config: &Config) -> Self {
method enabled_from (line 36) | fn enabled_from(cli: &Cli, config: &Config) -> bool {
method enabled_from_cli (line 53) | fn enabled_from_cli(cli: &Cli) -> Option<bool> {
method depth_from (line 66) | fn depth_from(cli: &Cli, config: &Config) -> usize {
method default (line 85) | fn default() -> Self {
function test_enabled_from_cli_empty (line 104) | fn test_enabled_from_cli_empty() {
function test_enabled_from_cli_true (line 111) | fn test_enabled_from_cli_true() {
function test_enabled_from_empty_matches_and_config (line 118) | fn test_enabled_from_empty_matches_and_config() {
function test_enabled_from_matches_empty_and_config_true (line 127) | fn test_enabled_from_matches_empty_and_config_true() {
function test_enabled_from_matches_empty_and_config_false (line 141) | fn test_enabled_from_matches_empty_and_config_false() {
function test_depth_from_cli_empty (line 158) | fn test_depth_from_cli_empty() {
function test_depth_from_cli_integer (line 165) | fn test_depth_from_cli_integer() {
function test_depth_from_cli_depth_multi (line 172) | fn test_depth_from_cli_depth_multi() {
function test_depth_from_cli_neg_int (line 179) | fn test_depth_from_cli_neg_int() {
function test_depth_from_cli_non_int (line 186) | fn test_depth_from_cli_non_int() {
function test_depth_from_config_none_max (line 193) | fn test_depth_from_config_none_max() {
function test_depth_from_config_pos_integer (line 203) | fn test_depth_from_config_pos_integer() {
FILE: src/flags/size.rs
type SizeFlag (line 14) | pub enum SizeFlag {
method from_arg_str (line 25) | fn from_arg_str(value: &str) -> Self {
method from_cli (line 42) | fn from_cli(cli: &Cli) -> Option<Self> {
method from_config (line 55) | fn from_config(config: &Config) -> Option<Self> {
function test_default (line 75) | fn test_default() {
function test_from_cli_none (line 80) | fn test_from_cli_none() {
function test_from_cli_default (line 87) | fn test_from_cli_default() {
function test_from_cli_short (line 94) | fn test_from_cli_short() {
function test_from_cli_bytes (line 101) | fn test_from_cli_bytes() {
function test_from_cli_unknown (line 109) | fn test_from_cli_unknown() {
function test_from_cli_size_multi (line 114) | fn test_from_cli_size_multi() {
function test_from_cli_size_classic (line 121) | fn test_from_cli_size_classic() {
function test_from_config_none (line 128) | fn test_from_config_none() {
function test_from_config_default (line 133) | fn test_from_config_default() {
function test_from_config_short (line 140) | fn test_from_config_short() {
function test_from_config_bytes (line 147) | fn test_from_config_bytes() {
function test_from_config_classic_mode (line 154) | fn test_from_config_classic_mode() {
FILE: src/flags/sorting.rs
type Sorting (line 13) | pub struct Sorting {
method configure_from (line 24) | pub fn configure_from(cli: &Cli, config: &Config) -> Self {
type SortColumn (line 39) | pub enum SortColumn {
method from_cli (line 55) | fn from_cli(cli: &Cli) -> Option<Self> {
method from_config (line 80) | fn from_config(config: &Config) -> Option<Self> {
type SortOrder (line 87) | pub enum SortOrder {
method from_cli (line 98) | fn from_cli(cli: &Cli) -> Option<Self> {
method from_config (line 112) | fn from_config(config: &Config) -> Option<Self> {
type DirGrouping (line 124) | pub enum DirGrouping {
method from_arg_str (line 132) | fn from_arg_str(value: &str) -> Self {
method from_cli (line 148) | fn from_cli(cli: &Cli) -> Option<Self> {
method from_config (line 171) | fn from_config(config: &Config) -> Option<Self> {
function test_from_cli_none (line 191) | fn test_from_cli_none() {
function test_from_cli_extension (line 198) | fn test_from_cli_extension() {
function test_from_cli_time (line 205) | fn test_from_cli_time() {
function test_from_cli_size (line 212) | fn test_from_cli_size() {
function test_from_cli_git (line 219) | fn test_from_cli_git() {
function test_from_cli_version (line 226) | fn test_from_cli_version() {
function test_from_cli_no_sort (line 233) | fn test_from_cli_no_sort() {
function test_from_cli_sort (line 240) | fn test_from_cli_sort() {
function test_from_arg_cli_sort_git (line 264) | fn test_from_arg_cli_sort_git() {
function test_multi_sort (line 271) | fn test_multi_sort() {
function test_multi_sort_use_last (line 278) | fn test_multi_sort_use_last() {
function test_from_config_empty (line 285) | fn test_from_config_empty() {
function test_from_config_empty_column (line 290) | fn test_from_config_empty_column() {
function test_from_config_extension (line 302) | fn test_from_config_extension() {
function test_from_config_name (line 313) | fn test_from_config_name() {
function test_from_config_time (line 324) | fn test_from_config_time() {
function test_from_config_size (line 335) | fn test_from_config_size() {
function test_from_config_version (line 346) | fn test_from_config_version() {
function test_from_config_git_status (line 357) | fn test_from_config_git_status() {
function test_from_cli_none (line 379) | fn test_from_cli_none() {
function test_from_cli_reverse (line 386) | fn test_from_cli_reverse() {
function test_from_config_empty (line 393) | fn test_from_config_empty() {
function test_from_config_default_config (line 398) | fn test_from_config_default_config() {
function test_from_config_empty_reverse (line 406) | fn test_from_config_empty_reverse() {
function test_from_config_reverse_true (line 417) | fn test_from_config_reverse_true() {
function test_from_config_reverse_false (line 428) | fn test_from_config_reverse_false() {
function test_from_str_bad_value (line 451) | fn test_from_str_bad_value() {
function test_from_cli_none (line 456) | fn test_from_cli_none() {
function test_from_cli_first (line 463) | fn test_from_cli_first() {
function test_from_cli_last (line 470) | fn test_from_cli_last() {
function test_from_cli_explicit_none (line 477) | fn test_from_cli_explicit_none() {
function test_from_cli_classic_mode (line 484) | fn test_from_cli_classic_mode() {
function test_from_cli_group_dirs_multi (line 491) | fn test_from_cli_group_dirs_multi() {
function test_from_cli_group_directories_first (line 498) | fn test_from_cli_group_directories_first() {
function test_from_config_empty (line 505) | fn test_from_config_empty() {
function test_from_config_first (line 510) | fn test_from_config_first() {
function test_from_config_last (line 521) | fn test_from_config_last() {
function test_from_config_explicit_empty (line 532) | fn test_from_config_explicit_empty() {
function test_from_config_classic_mode (line 543) | fn test_from_config_classic_mode() {
FILE: src/flags/symlink_arrow.rs
type SymlinkArrow (line 8) | pub struct SymlinkArrow(String);
method from_cli (line 14) | fn from_cli(_: &Cli) -> Option<Self> {
method from_config (line 22) | fn from_config(config: &Config) -> Option<Self> {
method fmt (line 40) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
method default (line 32) | fn default() -> Self {
function test_symlink_arrow_from_config_utf8 (line 55) | fn test_symlink_arrow_from_config_utf8() {
function test_symlink_arrow_from_args_none (line 65) | fn test_symlink_arrow_from_args_none() {
function test_symlink_arrow_default (line 72) | fn test_symlink_arrow_default() {
function test_symlink_display (line 80) | fn test_symlink_display() {
FILE: src/flags/symlinks.rs
type NoSymlink (line 11) | pub struct NoSymlink(pub bool);
method from_cli (line 18) | fn from_cli(cli: &Cli) -> Option<Self> {
method from_config (line 31) | fn from_config(config: &Config) -> Option<Self> {
function test_from_cli_none (line 47) | fn test_from_cli_none() {
function test_from_cli_true (line 54) | fn test_from_cli_true() {
function test_from_config_none (line 61) | fn test_from_config_none() {
function test_from_config_true (line 66) | fn test_from_config_true() {
function test_from_config_false (line 73) | fn test_from_config_false() {
FILE: src/flags/total_size.rs
type TotalSize (line 11) | pub struct TotalSize(pub bool);
method from_cli (line 18) | fn from_cli(cli: &Cli) -> Option<Self> {
method from_config (line 31) | fn from_config(config: &Config) -> Option<Self> {
function test_from_cli_none (line 47) | fn test_from_cli_none() {
function test_from_cli_true (line 54) | fn test_from_cli_true() {
function test_from_config_none (line 61) | fn test_from_config_none() {
function test_from_config_true (line 66) | fn test_from_config_true() {
function test_from_config_false (line 73) | fn test_from_config_false() {
FILE: src/flags/truncate_owner.rs
type TruncateOwner (line 11) | pub struct TruncateOwner {
method from_cli (line 21) | fn from_cli(cli: &Cli) -> Option<Self> {
method from_config (line 33) | fn from_config(config: &Config) -> Option<Self> {
function test_from_cli_none (line 52) | fn test_from_cli_none() {
function test_from_cli_after_some (line 59) | fn test_from_cli_after_some() {
function test_from_cli_marker_some (line 72) | fn test_from_cli_marker_some() {
function test_from_config_none (line 85) | fn test_from_config_none() {
function test_from_config_all_fields_none (line 90) | fn test_from_config_all_fields_none() {
function test_from_config_all_fields_some (line 106) | fn test_from_config_all_fields_some() {
FILE: src/git.rs
type GitStatus (line 6) | pub enum GitStatus {
type GitCache (line 30) | pub struct GitCache {
method new (line 37) | pub fn new(_: &Path) -> Self {
method get (line 41) | pub fn get(&self, _filepath: &PathBuf, _is_directory: bool) -> Option<...
method new (line 48) | pub fn new(path: &Path) -> GitCache {
method empty (line 88) | pub fn empty() -> Self {
method get (line 94) | pub fn get(&self, filepath: &PathBuf, is_directory: bool) -> Option<Gi...
method inner_get (line 106) | fn inner_get(&self, filepath: &PathBuf, is_directory: bool) -> GitFile...
function compare_git_status (line 140) | fn compare_git_status() {
function repo_init (line 153) | fn repo_init() -> (TempDir, Repository) {
function commit (line 171) | fn commit(repo: &Repository, index: &mut Index, msg: &str) -> (Oid, Oid) {
function check_cache (line 181) | fn check_cache(root: &Path, statuses: &HashMap<&PathBuf, GitFileStatus>,...
function test_git_workflow (line 198) | fn test_git_workflow() {
FILE: src/git_theme.rs
type GitTheme (line 4) | pub struct GitTheme {
method new (line 9) | pub fn new() -> GitTheme {
method get_symbol (line 16) | pub fn get_symbol(&self, status: &GitStatus) -> String {
FILE: src/icon.rs
type Icons (line 5) | pub struct Icons {
method new (line 15) | pub fn new(tty: bool, when: IconOption, theme: FlagTheme, icon_separat...
method get (line 34) | pub fn get(&self, name: &Name) -> String {
function get_no_icon_never_tty (line 84) | fn get_no_icon_never_tty() {
function get_no_icon_never_not_tty (line 96) | fn get_no_icon_never_not_tty() {
function get_no_icon_auto (line 109) | fn get_no_icon_auto() {
function get_icon_auto_tty (line 121) | fn get_icon_auto_tty() {
function get_icon_always_tty_default_file (line 134) | fn get_icon_always_tty_default_file() {
function get_icon_always_not_tty_default_file (line 147) | fn get_icon_always_not_tty_default_file() {
function get_icon_default_file_icon_unicode (line 160) | fn get_icon_default_file_icon_unicode() {
function get_icon_default_directory (line 178) | fn get_icon_default_directory() {
function get_icon_default_directory_unicode (line 190) | fn get_icon_default_directory_unicode() {
function get_icon_by_name (line 207) | fn get_icon_by_name() {
function get_icon_by_extension (line 223) | fn get_icon_by_extension() {
FILE: src/main.rs
type ExitCode (line 50) | pub enum ExitCode {
method set_if_greater (line 56) | pub fn set_if_greater(&mut self, code: ExitCode) {
function main (line 110) | fn main() {
function generate_default_config (line 133) | fn generate_default_config() {
FILE: src/meta/access_control.rs
type AccessControl (line 5) | pub struct AccessControl {
method for_path (line 13) | pub fn for_path(_: &Path) -> Self {
method for_path (line 18) | pub fn for_path(path: &Path) -> Self {
method from_data (line 33) | fn from_data(has_acl: bool, selinux_context: &[u8], smack_context: &[u...
method render_method (line 43) | pub fn render_method(&self, colors: &Colors) -> ColoredString {
method render_context (line 53) | pub fn render_context(&self, colors: &Colors) -> ColoredString {
type Method (line 69) | enum Method {
method name (line 77) | fn name(&self) -> &'static str {
function test_acl_only_indicator (line 93) | fn test_acl_only_indicator() {
function test_smack_only_indicator (line 104) | fn test_smack_only_indicator() {
function test_acl_and_selinux_indicator (line 114) | fn test_acl_and_selinux_indicator() {
function test_selinux_context (line 124) | fn test_selinux_context() {
function test_selinux_and_smack_context (line 134) | fn test_selinux_and_smack_context() {
function test_no_context (line 144) | fn test_no_context() {
FILE: src/meta/date.rs
type Date (line 11) | pub enum Date {
method from (line 18) | fn from(systime: SystemTime) -> Self {
method from (line 27) | fn from(meta: &Metadata) -> Self {
method render (line 35) | pub fn render(&self, colors: &Colors, flags: &Flags) -> ColoredString {
method date_string (line 46) | fn date_string(&self, flags: &Flags) -> String {
function cross_platform_touch (line 86) | fn cross_platform_touch(path: &Path, date: &DateTime<Local>) -> io::Resu...
function cross_platform_touch (line 95) | fn cross_platform_touch(path: &Path, date: &DateTime<Local>) -> io::Resu...
function test_an_hour_old_file_color (line 120) | fn test_an_hour_old_file_color() {
function test_a_day_old_file_color (line 148) | fn test_a_day_old_file_color() {
function test_a_several_days_old_file_color (line 176) | fn test_a_several_days_old_file_color() {
function test_with_relative_date (line 204) | fn test_with_relative_date() {
function test_with_relative_date_now (line 233) | fn test_with_relative_date_now() {
function test_iso_format_now (line 260) | fn test_iso_format_now() {
function test_iso_format_year_old (line 290) | fn test_iso_format_year_old() {
function test_locale_format_now (line 321) | fn test_locale_format_now() {
function test_bad_date (line 352) | fn test_bad_date() {
FILE: src/meta/filetype.rs
type FileType (line 6) | pub enum FileType {
constant EXECUTABLE_EXTENSIONS (line 19) | const EXECUTABLE_EXTENSIONS: &'static [&'static str] = &["exe", "msi",...
method new (line 22) | pub fn new(
method new (line 59) | pub fn new(meta: &Metadata, symlink_meta: Option<&Metadata>, path: &st...
method is_dirlike (line 85) | pub fn is_dirlike(self) -> bool {
method render (line 94) | pub fn render(self, colors: &Colors) -> ColoredString {
function test_file_type (line 130) | fn test_file_type() {
function test_dir_type (line 148) | fn test_dir_type() {
function test_symlink_type_file (line 175) | fn test_symlink_type_file() {
function test_symlink_type_dir (line 200) | fn test_symlink_type_dir() {
function test_pipe_type (line 225) | fn test_pipe_type() {
function test_char_device_type (line 249) | fn test_char_device_type() {
function test_socket_type (line 277) | fn test_socket_type() {
function test_file_executable (line 296) | fn test_file_executable() {
function test_file_not_executable (line 316) | fn test_file_not_executable() {
FILE: src/meta/git_file_status.rs
type GitFileStatus (line 6) | pub struct GitFileStatus {
method new (line 22) | pub fn new(status: git2::Status) -> Self {
method render (line 46) | pub fn render(&self, colors: &Colors, git_theme: &GitTheme) -> Colored...
method default (line 12) | fn default() -> Self {
FILE: src/meta/indicator.rs
type Indicator (line 6) | pub struct Indicator(&'static str);
method from (line 9) | fn from(file_type: FileType) -> Self {
method render (line 24) | pub fn render(&self, flags: &Flags) -> ColoredString {
function test_directory_indicator (line 40) | fn test_directory_indicator() {
function test_executable_file_indicator (line 52) | fn test_executable_file_indicator() {
function test_socket_indicator (line 67) | fn test_socket_indicator() {
function test_symlink_indicator (line 79) | fn test_symlink_indicator() {
function test_not_represented_indicator (line 93) | fn test_not_represented_indicator() {
FILE: src/meta/inode.rs
type INode (line 5) | pub struct INode {
method from (line 11) | fn from(meta: &Metadata) -> Self {
method from (line 20) | fn from(_: &Metadata) -> Self {
method render (line 26) | pub fn render(&self, colors: &Colors) -> ColoredString {
function cross_platform_touch (line 43) | fn cross_platform_touch(path: &Path) -> io::Result<ExitStatus> {
function test_inode_no_zero (line 48) | fn test_inode_no_zero() {
FILE: src/meta/links.rs
type Links (line 5) | pub struct Links {
method from (line 11) | fn from(meta: &Metadata) -> Self {
method from (line 20) | fn from(_: &Metadata) -> Self {
method render (line 26) | pub fn render(&self, colors: &Colors) -> ColoredString {
function cross_platform_touch (line 43) | fn cross_platform_touch(path: &Path) -> io::Result<ExitStatus> {
function test_hardlinks_no_zero (line 48) | fn test_hardlinks_no_zero() {
FILE: src/meta/locale.rs
function locale_str (line 5) | fn locale_str() -> String {
function current_locale (line 10) | pub fn current_locale() -> Locale {
FILE: src/meta/mod.rs
type Meta (line 45) | pub struct Meta {
method recurse_into (line 63) | pub fn recurse_into(
method calculate_total_size (line 192) | pub fn calculate_total_size(&mut self) {
method calculate_total_file_size (line 223) | fn calculate_total_file_size(path: &Path) -> u64 {
method from_path (line 261) | pub fn from_path(
function test_from_path_path (line 383) | fn test_from_path_path() {
function test_from_path_disable_permission (line 390) | fn test_from_path_disable_permission() {
function test_from_path (line 398) | fn test_from_path() {
function test_calculate_total_file_size_empty (line 444) | fn test_calculate_total_file_size_empty() {
function test_calculate_total_file_size_file_100b (line 453) | fn test_calculate_total_file_size_file_100b() {
FILE: src/meta/name.rs
type DisplayOption (line 12) | pub enum DisplayOption<'a> {
type Name (line 19) | pub struct Name {
method new (line 27) | pub fn new(path: &Path, file_type: FileType) -> Self {
method file_name (line 45) | pub fn file_name(&self) -> &str {
method relative_path (line 52) | fn relative_path<T: AsRef<Path> + Clone>(&self, base_path: T) -> PathB...
method escape (line 81) | fn escape(&self, string: &str, literal: bool) -> String {
method hyperlink (line 114) | fn hyperlink(&self, name: String, hyperlink: HyperlinkOption) -> String {
method render (line 143) | pub fn render(
method extension (line 191) | pub fn extension(&self) -> Option<&str> {
method file_type (line 195) | pub fn file_type(&self) -> FileType {
method cmp (line 201) | fn cmp(&self, other: &Self) -> Ordering {
method partial_cmp (line 207) | fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
method eq (line 213) | fn eq(&self, other: &Self) -> bool {
function test_print_file_name (line 243) | fn test_print_file_name() {
function test_print_dir_name (line 269) | fn test_print_dir_name() {
function test_print_symlink_name_file (line 294) | fn test_print_symlink_name_file() {
function test_print_symlink_name_dir (line 328) | fn test_print_symlink_name_dir() {
function test_print_other_type_name (line 362) | fn test_print_other_type_name() {
function test_print_without_icon_or_color (line 393) | fn test_print_without_icon_or_color() {
function test_print_hyperlink (line 419) | fn test_print_hyperlink() {
function test_extensions_with_valid_file (line 452) | fn test_extensions_with_valid_file() {
function test_extensions_with_file_without_extension (line 467) | fn test_extensions_with_file_without_extension() {
function test_order_impl_is_case_insensitive (line 482) | fn test_order_impl_is_case_insensitive() {
function test_partial_order_impl (line 505) | fn test_partial_order_impl() {
function test_partial_order_impl_is_case_insensitive (line 528) | fn test_partial_order_impl_is_case_insensitive() {
function test_partial_eq_impl (line 551) | fn test_partial_eq_impl() {
function test_partial_eq_impl_is_case_insensitive (line 574) | fn test_partial_eq_impl_is_case_insensitive() {
function test_parent_relative_path (line 597) | fn test_parent_relative_path() {
function test_current_relative_path (line 614) | fn test_current_relative_path() {
function test_grand_parent_relative_path (line 628) | fn test_grand_parent_relative_path() {
function test_special_chars_in_filename (line 646) | fn test_special_chars_in_filename() {
FILE: src/meta/owner.rs
type Cache (line 9) | pub struct Cache {
type Owner (line 18) | pub struct Owner {
method new (line 32) | pub fn new(user: String, group: String) -> Self {
method from (line 39) | fn from(meta: &Metadata) -> Self {
method render_user (line 68) | pub fn render_user(&self, colors: &Colors, cache: &Cache, flags: &Flag...
method render_group (line 89) | pub fn render_group(&self, colors: &Colors, cache: &Cache, flags: &Fla...
type Owner (line 25) | pub struct Owner {
method new (line 32) | pub fn new(user: String, group: String) -> Self {
method from (line 39) | fn from(meta: &Metadata) -> Self {
method render_user (line 68) | pub fn render_user(&self, colors: &Colors, cache: &Cache, flags: &Flag...
method render_group (line 89) | pub fn render_group(&self, colors: &Colors, cache: &Cache, flags: &Fla...
function truncate (line 49) | fn truncate(input: &str, after: Option<usize>, marker: Option<String>) -...
function test_none (line 114) | fn test_none() {
function test_unchanged_without_marker (line 119) | fn test_unchanged_without_marker() {
function test_unchanged_with_marker (line 124) | fn test_unchanged_with_marker() {
function test_truncated_without_marker (line 129) | fn test_truncated_without_marker() {
function test_truncated_with_marker (line 134) | fn test_truncated_with_marker() {
FILE: src/meta/permissions.rs
type Permissions (line 6) | pub struct Permissions {
method from (line 26) | fn from(meta: &Metadata) -> Self {
method from (line 52) | fn from(_: &Metadata) -> Self {
method bits_to_octal (line 58) | fn bits_to_octal(r: bool, w: bool, x: bool) -> u8 {
method render (line 62) | pub fn render(&self, colors: &Colors, flags: &Flags) -> ColoredString {
method is_executable (line 134) | pub fn is_executable(&self) -> bool {
type Mode (line 143) | pub type Mode = u32;
constant USER_READ (line 147) | pub const USER_READ: Mode = libc::S_IRUSR as Mode;
constant USER_WRITE (line 148) | pub const USER_WRITE: Mode = libc::S_IWUSR as Mode;
constant USER_EXECUTE (line 149) | pub const USER_EXECUTE: Mode = libc::S_IXUSR as Mode;
constant GROUP_READ (line 151) | pub const GROUP_READ: Mode = libc::S_IRGRP as Mode;
constant GROUP_WRITE (line 152) | pub const GROUP_WRITE: Mode = libc::S_IWGRP as Mode;
constant GROUP_EXECUTE (line 153) | pub const GROUP_EXECUTE: Mode = libc::S_IXGRP as Mode;
constant OTHER_READ (line 155) | pub const OTHER_READ: Mode = libc::S_IROTH as Mode;
constant OTHER_WRITE (line 156) | pub const OTHER_WRITE: Mode = libc::S_IWOTH as Mode;
constant OTHER_EXECUTE (line 157) | pub const OTHER_EXECUTE: Mode = libc::S_IXOTH as Mode;
constant STICKY (line 159) | pub const STICKY: Mode = libc::S_ISVTX as Mode;
constant SETGID (line 160) | pub const SETGID: Mode = libc::S_ISGID as Mode;
constant SETUID (line 161) | pub const SETUID: Mode = libc::S_ISUID as Mode;
function permission_rwx (line 176) | fn permission_rwx() {
function permission_rwx2 (line 196) | fn permission_rwx2() {
function permission_rwx_sticky (line 216) | fn permission_rwx_sticky() {
function permission_octal (line 238) | fn permission_octal() {
function permission_octal2 (line 259) | fn permission_octal2() {
function permission_octal_sticky (line 280) | fn permission_octal_sticky() {
function permission_disable (line 302) | fn permission_disable() {
FILE: src/meta/permissions_or_attributes.rs
type PermissionsOrAttributes (line 11) | pub enum PermissionsOrAttributes {
method render (line 18) | pub fn render(&self, colors: &Colors, flags: &Flags) -> ColoredString {
FILE: src/meta/size.rs
constant KB (line 5) | const KB: u64 = 1024;
constant MB (line 6) | const MB: u64 = 1024_u64.pow(2);
constant GB (line 7) | const GB: u64 = 1024_u64.pow(3);
constant TB (line 8) | const TB: u64 = 1024_u64.pow(4);
type Unit (line 11) | pub enum Unit {
type Size (line 20) | pub struct Size {
method from (line 25) | fn from(meta: &Metadata) -> Self {
method new (line 31) | pub fn new(bytes: u64) -> Self {
method get_bytes (line 35) | pub fn get_bytes(&self) -> u64 {
method format_size (line 39) | fn format_size(&self, number: f64) -> String {
method get_unit (line 43) | fn get_unit(&self, flags: &Flags) -> Unit {
method render (line 57) | pub fn render(
method paint (line 89) | fn paint(&self, colors: &Colors, content: String) -> ColoredString {
method render_value (line 103) | pub fn render_value(&self, colors: &Colors, flags: &Flags) -> ColoredS...
method value_string (line 109) | pub fn value_string(&self, flags: &Flags) -> String {
method render_unit (line 121) | pub fn render_unit(&self, colors: &Colors, flags: &Flags) -> ColoredSt...
method unit_string (line 127) | pub fn unit_string(&self, flags: &Flags) -> String {
function render_byte (line 157) | fn render_byte() {
function render_10_minus_kilobyte (line 171) | fn render_10_minus_kilobyte() {
function render_kilobyte (line 182) | fn render_kilobyte() {
function render_100_plus_kilobyte (line 193) | fn render_100_plus_kilobyte() {
function render_10_minus_megabyte (line 204) | fn render_10_minus_megabyte() {
function render_megabyte (line 215) | fn render_megabyte() {
function render_100_plus_megabyte (line 226) | fn render_100_plus_megabyte() {
function render_10_minus_gigabyte (line 237) | fn render_10_minus_gigabyte() {
function render_gigabyte (line 248) | fn render_gigabyte() {
function render_100_plus_gigabyte (line 259) | fn render_100_plus_gigabyte() {
function render_10_minus_terabyte (line 270) | fn render_10_minus_terabyte() {
function render_terabyte (line 281) | fn render_terabyte() {
function render_100_plus_terabyte (line 292) | fn render_100_plus_terabyte() {
function render_with_a_fraction (line 303) | fn render_with_a_fraction() {
function render_with_a_truncated_fraction (line 312) | fn render_with_a_truncated_fraction() {
function render_short_nospaces (line 321) | fn render_short_nospaces() {
FILE: src/meta/symlink.rs
type SymLink (line 7) | pub struct SymLink {
method from (line 13) | fn from(path: &Path) -> Self {
method symlink_string (line 46) | pub fn symlink_string(&self) -> Option<String> {
method render (line 50) | pub fn render(&self, colors: &Colors, flag: &Flags) -> ColoredString {
function test_symlink_render_default_valid_target_nocolor (line 87) | fn test_symlink_render_default_valid_target_nocolor() {
function test_symlink_render_default_invalid_target_nocolor (line 105) | fn test_symlink_render_default_invalid_target_nocolor() {
function test_symlink_render_default_invalid_target_withcolor (line 123) | fn test_symlink_render_default_invalid_target_withcolor() {
FILE: src/meta/windows_attributes.rs
type WindowsAttributes (line 9) | pub struct WindowsAttributes {
method render (line 35) | pub fn render(&self, colors: &Colors, _flags: &Flags) -> ColoredString {
function get_attributes (line 16) | pub fn get_attributes(metadata: &std::fs::Metadata) -> WindowsAttributes {
function archived_file (line 78) | pub fn archived_file() {
function readonly_file (line 84) | pub fn readonly_file() {
function hidden_file (line 90) | pub fn hidden_file() {
function system_file (line 96) | pub fn system_file() {
function create_and_process_file_with_attributes (line 101) | fn create_and_process_file_with_attributes(name: &str, attrs: &str) -> S...
FILE: src/meta/windows_utils.rs
constant BUF_SIZE (line 12) | const BUF_SIZE: u32 = 256;
function get_file_data (line 14) | pub fn get_file_data(path: &Path) -> Result<(Owner, Permissions), io::Er...
function get_acl_access_mask (line 199) | unsafe fn get_acl_access_mask(
function trustee_from_sid (line 227) | unsafe fn trustee_from_sid<P: Into<PSID>>(sid_ptr: P) -> TRUSTEE_W {
function lookup_account_sid (line 243) | unsafe fn lookup_account_sid(sid: PSID) -> Result<(Vec<u16>, Vec<u16>), ...
function os_from_buf (line 293) | fn os_from_buf(buf: &[u16]) -> OsString {
function buf_from_os (line 305) | fn buf_from_os(os: &OsStr) -> Vec<u16> {
function has_path_attribute (line 316) | fn has_path_attribute(
function is_path_hidden (line 333) | pub fn is_path_hidden(path: &Path) -> bool {
function is_path_system (line 344) | pub fn is_path_system(path: &Path) -> bool {
function basic_wtf16_behavior (line 356) | fn basic_wtf16_behavior() {
function every_wtf16_codepair_roundtrip (line 375) | fn every_wtf16_codepair_roundtrip() {
FILE: src/sort.rs
type SortFn (line 6) | pub type SortFn = fn(&Meta, &Meta) -> Ordering;
function assemble_sorters (line 8) | pub fn assemble_sorters(flags: &Flags) -> Vec<(SortOrder, SortFn)> {
function by_meta (line 32) | pub fn by_meta(sorters: &[(SortOrder, SortFn)], a: &Meta, b: &Meta) -> O...
function with_dirs_first (line 47) | fn with_dirs_first(a: &Meta, b: &Meta) -> Ordering {
function by_size (line 51) | fn by_size(a: &Meta, b: &Meta) -> Ordering {
function by_name (line 60) | fn by_name(a: &Meta, b: &Meta) -> Ordering {
function by_date (line 64) | fn by_date(a: &Meta, b: &Meta) -> Ordering {
function by_version (line 68) | fn by_version(a: &Meta, b: &Meta) -> Ordering {
function by_extension (line 72) | fn by_extension(a: &Meta, b: &Meta) -> Ordering {
function by_git_status (line 76) | fn by_git_status(a: &Meta, b: &Meta) -> Ordering {
function test_sort_assemble_sorters_by_name_with_dirs_first (line 90) | fn test_sort_assemble_sorters_by_name_with_dirs_first() {
function test_sort_assemble_sorters_by_name_with_files_first (line 120) | fn test_sort_assemble_sorters_by_name_with_files_first() {
function test_sort_assemble_sorters_by_name_unordered (line 148) | fn test_sort_assemble_sorters_by_name_unordered() {
function test_sort_assemble_sorters_by_name_unordered_2 (line 178) | fn test_sort_assemble_sorters_by_name_unordered_2() {
function test_sort_assemble_sorters_by_time (line 208) | fn test_sort_assemble_sorters_by_time() {
function test_sort_assemble_sorters_by_extension (line 258) | fn test_sort_assemble_sorters_by_extension() {
function test_sort_assemble_sorters_by_version (line 300) | fn test_sort_assemble_sorters_by_version() {
function test_sort_assemble_sorters_no_sort (line 329) | fn test_sort_assemble_sorters_no_sort() {
function test_sort_by_size (line 375) | fn test_sort_by_size() {
FILE: src/theme.rs
type Theme (line 22) | pub struct Theme {
method from_path (line 42) | pub fn from_path<D>(file: &str) -> Result<D, Error>
method with_yaml (line 86) | fn with_yaml<D>(yaml: &str) -> Result<D, serde_yaml::Error>
type Error (line 29) | pub enum Error {
FILE: src/theme/color.rs
function deserialize_color (line 8) | fn deserialize_color<'de, D>(deserializer: D) -> Result<Color, D::Error>
type ColorTheme (line 86) | pub struct ColorTheme {
method default_dark (line 405) | pub fn default_dark() -> Self {
type Permission (line 109) | pub struct Permission {
type Attributes (line 132) | pub struct Attributes {
type FileType (line 147) | pub struct FileType {
type File (line 167) | pub struct File {
type Dir (line 182) | pub struct Dir {
type Symlink (line 193) | pub struct Symlink {
type Date (line 206) | pub struct Date {
type Size (line 219) | pub struct Size {
type INode (line 234) | pub struct INode {
type Links (line 245) | pub struct Links {
type GitStatus (line 256) | pub struct GitStatus {
method default (line 280) | fn default() -> Self {
method default (line 294) | fn default() -> Self {
method default (line 304) | fn default() -> Self {
method default (line 318) | fn default() -> Self {
method default (line 328) | fn default() -> Self {
method default (line 336) | fn default() -> Self {
method default (line 345) | fn default() -> Self {
method default (line 354) | fn default() -> Self {
method default (line 364) | fn default() -> Self {
method default (line 372) | fn default() -> Self {
method default (line 381) | fn default() -> Self {
method default (line 398) | fn default() -> Self {
function default_yaml (line 427) | fn default_yaml() -> &'static str {
function test_default_theme (line 457) | fn test_default_theme() {
function test_default_theme_file (line 465) | fn test_default_theme_file() {
function test_empty_theme_return_default (line 480) | fn test_empty_theme_return_default() {
function test_first_level_theme_return_default_but_changed (line 489) | fn test_first_level_theme_return_default_but_changed() {
function test_hexadecimal_colors (line 500) | fn test_hexadecimal_colors() {
function test_second_level_theme_return_default_but_changed (line 515) | fn test_second_level_theme_return_default_but_changed() {
FILE: src/theme/git.rs
type GitThemeSymbols (line 7) | pub struct GitThemeSymbols {
method default (line 21) | fn default() -> GitThemeSymbols {
FILE: src/theme/icon.rs
type ByFilename (line 4) | enum ByFilename {
function deserialize_by_filename (line 9) | fn deserialize_by_filename<'de, D>(
function deserialize_by_name (line 24) | fn deserialize_by_name<'de, D>(deserializer: D) -> Result<HashMap<String...
function deserialize_by_extension (line 31) | fn deserialize_by_extension<'de, D>(deserializer: D) -> Result<HashMap<S...
type IconTheme (line 42) | pub struct IconTheme {
method unicode (line 112) | pub fn unicode() -> Self {
method get_default_icons_by_name (line 121) | pub fn get_default_icons_by_name() -> HashMap<String, String> {
method get_default_icons_by_extension (line 404) | pub fn get_default_icons_by_extension() -> HashMap<String, String> {
type ByType (line 54) | pub struct ByType {
method unicode (line 95) | pub fn unicode() -> Self {
method default (line 68) | fn default() -> Self {
method default (line 78) | fn default() -> ByType {
function partial_default_yaml (line 762) | fn partial_default_yaml() -> &'static str {
function check_partial_yaml (line 787) | fn check_partial_yaml(def: &IconTheme, yaml: &IconTheme) {
function test_default_theme (line 792) | fn test_default_theme() {
function test_tmp_partial_default_theme_file (line 799) | fn test_tmp_partial_default_theme_file() {
function test_empty_theme_return_default (line 812) | fn test_empty_theme_return_default() {
function test_partial_theme_return_default (line 821) | fn test_partial_theme_return_default() {
function test_serde_dir_from_yaml (line 830) | fn test_serde_dir_from_yaml() {
function test_custom_icon_by_name (line 838) | fn test_custom_icon_by_name() {
function test_default_icon_by_name_with_custom_entry (line 846) | fn test_default_icon_by_name_with_custom_entry() {
function test_custom_icon_by_extension (line 854) | fn test_custom_icon_by_extension() {
function test_default_icon_by_extension_with_custom_entry (line 862) | fn test_default_icon_by_extension_with_custom_entry() {
FILE: tests/integration.rs
function test_runs_okay (line 15) | fn test_runs_okay() {
function test_list_empty_directory (line 20) | fn test_list_empty_directory() {
function test_list_almost_all_empty_directory (line 29) | fn test_list_almost_all_empty_directory() {
function test_list_all_empty_directory (line 47) | fn test_list_all_empty_directory() {
function test_list_populated_directory (line 65) | fn test_list_populated_directory() {
function test_list_almost_all_populated_directory (line 77) | fn test_list_almost_all_populated_directory() {
function test_list_all_populated_directory (line 90) | fn test_list_all_populated_directory() {
function test_almost_sort_with_folder (line 103) | fn test_almost_sort_with_folder() {
function test_list_inode_populated_directory (line 118) | fn test_list_inode_populated_directory() {
function test_list_block_inode_populated_directory_without_long (line 143) | fn test_list_block_inode_populated_directory_without_long() {
function test_list_block_inode_populated_directory_with_long (line 163) | fn test_list_block_inode_populated_directory_with_long() {
function test_list_inode_with_long_ok (line 184) | fn test_list_inode_with_long_ok() {
function test_list_broken_link_ok (line 197) | fn test_list_broken_link_ok() {
function test_nosymlink_on_non_long (line 221) | fn test_nosymlink_on_non_long() {
function test_symlink_on_long (line 239) | fn test_symlink_on_long() {
function test_dereference_link_right_type_and_no_link (line 256) | fn test_dereference_link_right_type_and_no_link() {
function test_dereference_link_broken_link (line 285) | fn test_dereference_link_broken_link() {
function test_dereference_link_broken_link_output (line 308) | fn test_dereference_link_broken_link_output() {
function test_show_folder_content_of_symlink (line 343) | fn test_show_folder_content_of_symlink() {
function test_no_show_folder_content_of_symlink_for_long (line 361) | fn test_no_show_folder_content_of_symlink_for_long() {
function test_show_folder_content_of_symlink_for_long_tail_slash (line 380) | fn test_show_folder_content_of_symlink_for_long_tail_slash() {
function test_show_folder_of_symlink_for_long_multi (line 397) | fn test_show_folder_of_symlink_for_long_multi() {
function test_version_sort (line 415) | fn test_version_sort() {
function test_version_sort_overwrite_by_timesort (line 437) | fn test_version_sort_overwrite_by_timesort() {
function test_version_sort_overwrite_by_sizesort (line 451) | fn test_version_sort_overwrite_by_sizesort() {
function bad_utf8 (line 469) | fn bad_utf8(tmp: &std::path::Path, pre: &str, suf: &str) -> String {
function test_bad_utf_8_extension (line 480) | fn test_bad_utf_8_extension() {
function test_bad_utf_8_name (line 495) | fn test_bad_utf_8_name() {
function test_tree (line 509) | fn test_tree() {
function test_tree_all_not_show_self (line 524) | fn test_tree_all_not_show_self() {
function test_tree_show_edge_before_name (line 544) | fn test_tree_show_edge_before_name() {
function test_tree_d (line 559) | fn test_tree_d() {
function test_tree_no_dereference (line 579) | fn test_tree_no_dereference() {
function test_tree_dereference (line 598) | fn test_tree_dereference() {
function cmd (line 619) | fn cmd() -> Command {
function tempdir (line 623) | fn tempdir() -> assert_fs::TempDir {
function test_lower_case_name_icon_match (line 629) | fn test_lower_case_name_icon_match() {
function test_upper_case_name_icon_match (line 645) | fn test_upper_case_name_icon_match() {
function test_lower_case_ext_icon_match (line 661) | fn test_lower_case_ext_icon_match() {
function test_upper_case_ext_icon_match (line 677) | fn test_upper_case_ext_icon_match() {
function test_truncate_owner (line 693) | fn test_truncate_owner() {
function test_custom_config_file_parsing (line 711) | fn test_custom_config_file_parsing() {
function test_cannot_access_file_exit_status (line 727) | fn test_cannot_access_file_exit_status() {
function test_cannot_access_subdir_exit_status (line 745) | fn test_cannot_access_subdir_exit_status() {
function test_date_custom_format_supports_nanos_with_length (line 766) | fn test_date_custom_format_supports_nanos_with_length() {
function test_date_custom_format_supports_padding (line 786) | fn test_date_custom_format_supports_padding() {
function test_all_directory (line 806) | fn test_all_directory() {
function test_multiple_files (line 821) | fn test_multiple_files() {
Condensed preview — 76 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (582K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 426,
"preview": "# These are supported funding model platforms\n\ngithub: zwpaper\npatreon: # Replace with a single Patreon username\nopen_co"
},
{
"path": ".github/ISSUE_TEMPLATE/bug.yml",
"chars": 2443,
"preview": "name: Bug Report Form\ndescription: Create a report to help us improve, by the new GitHub form\ntitle: \"[Bug]: \"\nlabels: ["
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 470,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n- os: \n- `lsd -"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE",
"chars": 375,
"preview": "\n<!--- PR Description --->\n\n---\n#### TODO\n\n- [ ] Use `cargo fmt`\n- [ ] Add necessary tests\n- [ ] Update README (if appli"
},
{
"path": ".github/workflows/CICD.yml",
"chars": 20540,
"preview": "name: CICD\n\n# spell-checker:ignore CICD CODECOV MSVC MacOS Peltoche SHAs buildable clippy dpkg esac fakeroot gnueabihf h"
},
{
"path": ".gitignore",
"chars": 26,
"preview": "/target\nout.md\n**/*.rs.bk\n"
},
{
"path": ".release.toml",
"chars": 601,
"preview": "sign-commit = true\nsign-tag = true\ndev-version = false\npre-release-commit-message = \"Release {{version}}\"\ntag-prefix = \""
},
{
"path": "CODEOWNERS",
"chars": 42,
"preview": "* @zwpaper\n\n# Retired\n# @meain @Peltoche\n"
},
{
"path": "Cargo.toml",
"chars": 2122,
"preview": "[package]\nauthors = [\"Peltoche <dev@halium.fr>\"]\nbuild = \"build.rs\"\ncategories = [\"command-line-utilities\"]\ndescription "
},
{
"path": "LICENSE",
"chars": 11359,
"preview": "\n\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "OWNERS",
"chars": 72,
"preview": "# See the OWNERS docs at https://go.k8s.io/owners\n\napprovers:\n- zwpaper\n"
},
{
"path": "README.md",
"chars": 16061,
"preview": "<div align=\"center\">\n <sup>Special thanks to:</sup>\n <br>\n <sup>Maintained with ❤️ + 🤖 by Pochi</sup>\n <br>\n <"
},
{
"path": "build.rs",
"chars": 1575,
"preview": "// Copyright (c) 2017 fd developers\n// Licensed under the Apache License, Version 2.0\n// <LICENSE-APACHE or http://www.a"
},
{
"path": "ci/before_deploy.bash",
"chars": 3426,
"preview": "#!/usr/bin/env bash\n# Building and packaging for release\n\nset -ex\n\nbuild() {\n cargo build --target \"$TARGET\" --featur"
},
{
"path": "ci/before_install.bash",
"chars": 551,
"preview": "#!/usr/bin/env bash\n\nset -ex\n\nif [ \"$TRAVIS_OS_NAME\" != linux ]; then\n exit 0\nfi\n\nsudo apt-get update\n\n# needed to bu"
},
{
"path": "ci/script.bash",
"chars": 265,
"preview": "#!/usr/bin/env bash\n\nset -ex\n\n# Incorporate TARGET env var to the build and test process\ncargo build --target \"$TARGET\" "
},
{
"path": "doc/colors.md",
"chars": 4648,
"preview": "# Color reference\n> [!TIP]\n> Checkout [trapd00r/LS_COLORS](https://github.com/trapd00r/LS_COLORS) and [sharkdp/vivid](ht"
},
{
"path": "doc/lsd.md",
"chars": 4680,
"preview": "---\ntitle: lsd\nsection: 1\nheader: User Manual\nfooter: lsd <version>\ndate: <date>\n---\n\n# NAME\n\nlsd - LSDeluxe\n\n# SYNOPSIS"
},
{
"path": "doc/samples/colors-sample.yaml",
"chars": 1655,
"preview": "---\nuser: 230 # Default = 230\ngroup: 187 # Default = 187\npermission:\n read: "
},
{
"path": "doc/samples/config-sample.yaml",
"chars": 4188,
"preview": "---\n# == Classic ==\n# This is a shorthand to override some of the options to be backwards compatible\n# with `ls`. It aff"
},
{
"path": "doc/samples/icons-sample.yaml",
"chars": 27670,
"preview": "---\n# See https://www.nerdfonts.com/cheat-sheet for additional icons\nfiletype:\n dir: # Default ="
},
{
"path": "lsd.spec",
"chars": 797,
"preview": "Name: lsd\nVersion: 1.2.0\nRelease: 1%{?dist}\nSummary: The next gen ls command\n\nLicense: "
},
{
"path": "rustfmt.toml",
"chars": 178,
"preview": "# use empty config file to ensure that `rustfmt` will always use \n# the default configuration when formatting code\n# eve"
},
{
"path": "src/app.rs",
"chars": 10117,
"preview": "use std::path::PathBuf;\n\nuse clap::{ArgAction, Parser, ValueHint};\n\n#[derive(Debug, Parser)]\n#[command(about, version, a"
},
{
"path": "src/color.rs",
"chars": 16156,
"preview": "use crossterm::style::Color;\nuse crossterm::style::{Attribute, ContentStyle, StyledContent, Stylize};\nuse lscolors::{Ind"
},
{
"path": "src/config_file.rs",
"chars": 14000,
"preview": "//! This module provides methods to handle the program's config files and\n//! operations related to this.\nuse crate::fla"
},
{
"path": "src/core.rs",
"chars": 6770,
"preview": "use crate::color::Colors;\nuse crate::display;\nuse crate::flags::{\n ColorOption, Display, Flags, HyperlinkOption, Layo"
},
{
"path": "src/display.rs",
"chars": 31508,
"preview": "use crate::color::{Colors, Elem};\nuse crate::flags::blocks::Block;\nuse crate::flags::{Display, Flags, HyperlinkOption, L"
},
{
"path": "src/flags/blocks.rs",
"chars": 18367,
"preview": "//! This module defines the [Blocks] struct. To set it up from [Cli], a [Config] and its\n//! [Default] value, use its [c"
},
{
"path": "src/flags/color.rs",
"chars": 9484,
"preview": "//! This module defines the [Color]. To set it up from [Cli], a [Config] and its [Default]\n//! value, use its [configure"
},
{
"path": "src/flags/date.rs",
"chars": 9266,
"preview": "//! This module defines the [DateFlag]. To set it up from [Cli], a [Config] and its\n//! [Default] value, use its [config"
},
{
"path": "src/flags/dereference.rs",
"chars": 2261,
"preview": "//! This module defines the [Dereference] flag. To set it up from [Cli], a [Config] and its\n//! [Default] value, use the"
},
{
"path": "src/flags/display.rs",
"chars": 4165,
"preview": "//! This module defines the [Display] flag. To set it up from [Cli], a [Config] and its\n//! [Default] value, use its [co"
},
{
"path": "src/flags/header.rs",
"chars": 2102,
"preview": "//! This module defines the [Header] flag. To set it up from [Cli], a [Config] and its\n//! [Default] value, use the [con"
},
{
"path": "src/flags/hyperlink.rs",
"chars": 5049,
"preview": "//! This module defines the [HyperlinkOption]. To set it up from [Cli], a [Config] and its\n//! [Default] value, use its "
},
{
"path": "src/flags/icons.rs",
"chars": 10861,
"preview": "//! This module defines the [IconOption]. To set it up from [Cli], a [Config] and its\n//! [Default] value, use its [conf"
},
{
"path": "src/flags/ignore_globs.rs",
"chars": 5465,
"preview": "//! This module defines the [IgnoreGlobs]. To set it up from [Cli], a [Config] and its\n//! [Default] value, use the [con"
},
{
"path": "src/flags/indicators.rs",
"chars": 2266,
"preview": "//! This module defines the [Indicators] flag. To set it up from [Cli], a [Config] and its\n//! [Default] value, use the "
},
{
"path": "src/flags/layout.rs",
"chars": 3572,
"preview": "//! This module defines the [Layout] flag. To set it up from [Cli], a [Config] and its\n//! [Default] value, use its [con"
},
{
"path": "src/flags/literal.rs",
"chars": 2150,
"preview": "//! This module defines the [Literal]. To set it up from [Cli], a [Config] and its\n//! [Default] value, use its [configu"
},
{
"path": "src/flags/permission.rs",
"chars": 5574,
"preview": "//! This module defines the [PermissionFlag]. To set it up from [Cli], a [Config] and its\n//! [Default] value, use its ["
},
{
"path": "src/flags/recursion.rs",
"chars": 6517,
"preview": "//! This module defines the [Recursion] options. To set it up from [Cli], a [Config] and its\n//! [Default] value, use th"
},
{
"path": "src/flags/size.rs",
"chars": 4726,
"preview": "//! This module defines the [SizeFlag]. To set it up from [Cli], a [Config] and its\n//! [Default] value, use its [config"
},
{
"path": "src/flags/sorting.rs",
"chars": 16718,
"preview": "//! This module defines the [Sorting] options. To set it up from [Cli], a [Config]\n//! and its [Default] value, use the "
},
{
"path": "src/flags/symlink_arrow.rs",
"chars": 2149,
"preview": "use super::Configurable;\n\nuse crate::app::Cli;\nuse crate::config_file::Config;\n\n/// The flag showing how to display symb"
},
{
"path": "src/flags/symlinks.rs",
"chars": 2218,
"preview": "//! This module defines the [NoSymlink] flag. To set it up from [Cli], a [Config] and its\n//! [Default] value, use the ["
},
{
"path": "src/flags/total_size.rs",
"chars": 2232,
"preview": "//! This module defines the [TotalSize] flag. To set it up from [Cli], a [Config] and its\n//! [Default] value, use the ["
},
{
"path": "src/flags/truncate_owner.rs",
"chars": 3458,
"preview": "//! This module defines the [TruncateOwner] flag. To set it up from [Cli], a [Config] and its\n//! [Default] value, use t"
},
{
"path": "src/flags.rs",
"chars": 5147,
"preview": "pub mod blocks;\npub mod color;\npub mod date;\npub mod dereference;\npub mod display;\npub mod header;\npub mod hyperlink;\npu"
},
{
"path": "src/git.rs",
"chars": 15425,
"preview": "use crate::meta::git_file_status::GitFileStatus;\nuse std::path::{Path, PathBuf};\n\n#[allow(dead_code)]\n#[derive(Copy, Clo"
},
{
"path": "src/git_theme.rs",
"chars": 1043,
"preview": "use crate::git::GitStatus;\nuse crate::theme::git::GitThemeSymbols;\n\npub struct GitTheme {\n symbols: GitThemeSymbols,\n"
},
{
"path": "src/icon.rs",
"chars": 9110,
"preview": "use crate::flags::{IconOption, IconTheme as FlagTheme};\nuse crate::meta::{FileType, Name};\nuse crate::theme::{Theme, ico"
},
{
"path": "src/main.rs",
"chars": 3347,
"preview": "#![allow(\n clippy::cast_precision_loss,\n clippy::cast_sign_loss,\n clippy::match_same_arms,\n clippy::cast_pos"
},
{
"path": "src/meta/access_control.rs",
"chars": 4315,
"preview": "use crate::color::{ColoredString, Colors, Elem};\nuse std::path::Path;\n\n#[derive(Clone, Debug)]\npub struct AccessControl "
},
{
"path": "src/meta/date.rs",
"chars": 11441,
"preview": "use super::locale::current_locale;\nuse crate::color::{ColoredString, Colors, Elem};\nuse crate::flags::{DateFlag, Flags};"
},
{
"path": "src/meta/filetype.rs",
"chars": 11068,
"preview": "use crate::color::{ColoredString, Colors, Elem};\nuse std::fs::Metadata;\n\n#[derive(Debug, PartialEq, Eq, Copy, Clone)]\n#["
},
{
"path": "src/meta/git_file_status.rs",
"chars": 2517,
"preview": "use crate::color::{self, ColoredString, Colors};\nuse crate::git::GitStatus;\nuse crate::git_theme::GitTheme;\n\n#[derive(Co"
},
{
"path": "src/meta/indicator.rs",
"chars": 2833,
"preview": "use crate::color::{ColoredString, Colors};\nuse crate::flags::Flags;\nuse crate::meta::FileType;\n\n#[derive(Clone, Debug)]\n"
},
{
"path": "src/meta/inode.rs",
"chars": 1487,
"preview": "use crate::color::{ColoredString, Colors, Elem};\nuse std::fs::Metadata;\n\n#[derive(Debug, PartialEq, Eq, Copy, Clone)]\npu"
},
{
"path": "src/meta/links.rs",
"chars": 1493,
"preview": "use crate::color::{ColoredString, Colors, Elem};\nuse std::fs::Metadata;\n\n#[derive(Debug, PartialEq, Eq, Copy, Clone)]\npu"
},
{
"path": "src/meta/locale.rs",
"chars": 413,
"preview": "use chrono::Locale;\nuse once_cell::sync::OnceCell;\nuse sys_locale::get_locale;\n\nfn locale_str() -> String {\n get_loca"
},
{
"path": "src/meta/mod.rs",
"chars": 15556,
"preview": "mod access_control;\nmod date;\nmod filetype;\npub mod git_file_status;\nmod indicator;\nmod inode;\nmod links;\nmod locale;\npu"
},
{
"path": "src/meta/name.rs",
"chars": 23225,
"preview": "use crate::color::{ColoredString, Colors, Elem};\nuse crate::flags::HyperlinkOption;\nuse crate::icon::Icons;\nuse crate::m"
},
{
"path": "src/meta/owner.rs",
"chars": 3323,
"preview": "use crate::Flags;\nuse crate::color::{ColoredString, Colors, Elem};\n#[cfg(unix)]\nuse std::fs::Metadata;\n#[cfg(unix)]\nuse "
},
{
"path": "src/meta/permissions.rs",
"chars": 11666,
"preview": "use crate::color::{ColoredString, Colors, Elem};\nuse crate::flags::{Flags, PermissionFlag};\nuse std::fs::Metadata;\n\n#[de"
},
{
"path": "src/meta/permissions_or_attributes.rs",
"chars": 725,
"preview": "#[cfg(windows)]\nuse super::windows_attributes::WindowsAttributes;\nuse crate::{\n color::{ColoredString, Colors},\n f"
},
{
"path": "src/meta/size.rs",
"chars": 9958,
"preview": "use crate::color::{ColoredString, Colors, Elem};\nuse crate::flags::{Flags, SizeFlag};\nuse std::fs::Metadata;\n\nconst KB: "
},
{
"path": "src/meta/symlink.rs",
"chars": 4117,
"preview": "use crate::color::{ColoredString, Colors, Elem};\nuse crate::flags::Flags;\nuse std::fs::read_link;\nuse std::path::Path;\n\n"
},
{
"path": "src/meta/windows_attributes.rs",
"chars": 3781,
"preview": "use crate::{\n color::{ColoredString, Colors, Elem},\n flags::Flags,\n};\n\nuse std::os::windows::fs::MetadataExt;\n\n#[d"
},
{
"path": "src/meta/windows_utils.rs",
"chars": 13681,
"preview": "use std::ffi::{OsStr, OsString};\nuse std::io;\nuse std::mem::MaybeUninit;\nuse std::os::windows::ffi::{OsStrExt, OsStringE"
},
{
"path": "src/sort.rs",
"chars": 15634,
"preview": "use crate::flags::{DirGrouping, Flags, SortColumn, SortOrder};\nuse crate::meta::Meta;\nuse std::cmp::Ordering;\nuse vsort:"
},
{
"path": "src/theme/color.rs",
"chars": 15768,
"preview": "//! This module provides methods to create theme from files and operations related to\n//! this.\nuse crossterm::style::Co"
},
{
"path": "src/theme/git.rs",
"chars": 927,
"preview": "use serde::Deserialize;\n\n#[derive(Debug, Deserialize, PartialEq, Eq)]\n#[serde(rename_all = \"kebab-case\")]\n#[serde(deny_u"
},
{
"path": "src/theme/icon.rs",
"chars": 40295,
"preview": "use serde::Deserialize;\nuse std::collections::HashMap;\n\nenum ByFilename {\n Name,\n Extension,\n}\n\nfn deserialize_by_"
},
{
"path": "src/theme.rs",
"chars": 2614,
"preview": "pub mod color;\npub mod git;\npub mod icon;\n\nuse std::path::Path;\nuse std::{fs, io};\n\nuse serde::{Deserialize, de::Deseria"
},
{
"path": "tests/integration.rs",
"chars": 20732,
"preview": "extern crate assert_cmd;\nextern crate predicates;\n\nuse assert_cmd::prelude::*;\nuse assert_fs::prelude::*;\nuse predicates"
}
]
About this extraction
This page contains the full source code of the lsd-rs/lsd GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 76 files (540.0 KB), approximately 142.7k tokens, and a symbol index with 829 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.