mirror of
https://github.com/sharkdp/bat
synced 2026-08-04 19:01:44 +00:00
Compare commits
286 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 410e284322 | |||
| 7d9884d6d2 | |||
| 652489251b | |||
| 89f3d2d31a | |||
| 3ee9ea9a8d | |||
| b511b928f4 | |||
| 3e789f5241 | |||
| a500fb236a | |||
| 515b9d62db | |||
| 62907b1da5 | |||
| eef7107407 | |||
| 87e043b91c | |||
| b8d462ba87 | |||
| ca3ef28d56 | |||
| 0b4c886efc | |||
| 780fdeb888 | |||
| 7a9c6e0de5 | |||
| a1e7c0ab4a | |||
| 1e36873704 | |||
| e86797fbf4 | |||
| e60875ac12 | |||
| 66eece4e2c | |||
| a19593b383 | |||
| 7a6f442c86 | |||
| 3767f15c2a | |||
| 1932d1da39 | |||
| b0e5590864 | |||
| f2daa1eb6e | |||
| 1f540752ef | |||
| fc94a0ec49 | |||
| 169dc7c45b | |||
| 5e140558b1 | |||
| 99c8e15c27 | |||
| 2a3ed948ec | |||
| 618d7340bb | |||
| 4a38eab3ea | |||
| 9fccdbc484 | |||
| c6e661d80b | |||
| 031ba0953a | |||
| 2a29802dd5 | |||
| 31e30a2e97 | |||
| ffed52f6eb | |||
| a4e853c4fa | |||
| f11374535d | |||
| de209f8a23 | |||
| 22c38b6fcb | |||
| 63d5796f8f | |||
| a1917275cd | |||
| 884765bdeb | |||
| 56fe0fa226 | |||
| d9adfe90fc | |||
| b1f04995a6 | |||
| ea6d2ac911 | |||
| ac97356930 | |||
| 23abbdcea9 | |||
| 927873392b | |||
| 91965d28a6 | |||
| 88ff40f93e | |||
| 0dcdcb678a | |||
| f49641a6c3 | |||
| fc4f12b390 | |||
| 9cce9e04d2 | |||
| 844bfded50 | |||
| ab80bd9717 | |||
| a1a10c777f | |||
| cc5f782d28 | |||
| 1424f9d6bf | |||
| fd67095cff | |||
| 908b1f22d1 | |||
| 17a70d9b8c | |||
| 49d77a469a | |||
| fa35495888 | |||
| 8a858b19dc | |||
| 397252d2cc | |||
| 5e59957349 | |||
| 90f8e00e0c | |||
| 673929c189 | |||
| e36bb8cc66 | |||
| 7d7e202bd5 | |||
| 74de075748 | |||
| 594069581d | |||
| 62b0388236 | |||
| 16d041492c | |||
| 22ee03ff00 | |||
| 790bed3a2d | |||
| bc84854d4b | |||
| 319811df01 | |||
| 2c1a8caadd | |||
| a240aa4afd | |||
| 00e38cd056 | |||
| 335eff51f3 | |||
| d04b960c05 | |||
| fbc05da785 | |||
| a71d16fa1b | |||
| 5bd08845a3 | |||
| 3ebfbb7ac5 | |||
| 2b47f3c5eb | |||
| 9f8ac934ab | |||
| ab393a0281 | |||
| 990b2fc06f | |||
| 0c883d6f28 | |||
| 167dda63a8 | |||
| cb8b637574 | |||
| 391e7ff072 | |||
| 2b517199a2 | |||
| a0f326aa22 | |||
| a4a7692134 | |||
| 3df9d581b7 | |||
| eb724f2dc4 | |||
| c491488140 | |||
| 3f12b1c1ab | |||
| 2c682ff6a6 | |||
| 36b37e1aa5 | |||
| d19c80a7c1 | |||
| eff57943c9 | |||
| 5a4a7de933 | |||
| ba97230b98 | |||
| 8664cc9df9 | |||
| 3b34b47d55 | |||
| 9a5519057c | |||
| 93411a96e4 | |||
| bd461afd75 | |||
| 55306c15a6 | |||
| afd804ebea | |||
| 3d0a7ed0a0 | |||
| 8f6f5dfbd2 | |||
| 43ab174d42 | |||
| b396ab865f | |||
| 8a82847121 | |||
| c3c19c8018 | |||
| 0d2078b32b | |||
| cddfad83e4 | |||
| 2e355ffa4e | |||
| fa66d8e3ef | |||
| cb83b8fb9a | |||
| adfdcf4c6d | |||
| 593cf76811 | |||
| 3aaec8cb3b | |||
| 2030ffa664 | |||
| edb8342eab | |||
| 6a7936a26f | |||
| 26118afde0 | |||
| 783acbc83d | |||
| 6f0a61cef9 | |||
| 53a10e0b0a | |||
| 626154317f | |||
| 885ae1dc81 | |||
| f6f1f18e9c | |||
| 69a2ffbdac | |||
| 6b8decf2a4 | |||
| cd06fe46e0 | |||
| 099d716455 | |||
| 298e1f5321 | |||
| 3cb55e85d0 | |||
| 981f25989a | |||
| 57bf8ff7da | |||
| 31f982e736 | |||
| 0a97a1edd7 | |||
| 6c75acfffc | |||
| 4e84e838a3 | |||
| d48bf240a7 | |||
| 7dc180eefb | |||
| 3276e9469a | |||
| 60902c796f | |||
| c12cdcace3 | |||
| 823fd9f5c6 | |||
| 88d6fd0248 | |||
| 32047346e3 | |||
| 56678dc771 | |||
| b34a1e0cc2 | |||
| 41f55bf902 | |||
| c8514543af | |||
| ad608014d9 | |||
| 782ce71247 | |||
| 8c7fccc8a5 | |||
| fca5502f21 | |||
| 9bf344f760 | |||
| 4db185834b | |||
| 4549ab3f22 | |||
| 629a8968fc | |||
| de414ed631 | |||
| 59c5896902 | |||
| b22fc5db6b | |||
| f97f9ebf03 | |||
| 64a4b204a2 | |||
| 5fb8a25c30 | |||
| 80ef6832d3 | |||
| 51bdaa5f88 | |||
| eb2a8e29c7 | |||
| b7dd88eafd | |||
| 79fe5fea63 | |||
| d15e399fce | |||
| 4d188e45fe | |||
| abce6ac0d1 | |||
| 566595f0eb | |||
| 1796324531 | |||
| 2371077352 | |||
| 7e7ab8a860 | |||
| c054541776 | |||
| d6ffe3a9be | |||
| 4a1ceb3ce0 | |||
| 45a6ee95d5 | |||
| 3eeaf29675 | |||
| 58d1fa83e9 | |||
| 9d4ca3bc22 | |||
| 9b23b05841 | |||
| f3e8ffc276 | |||
| 9b73911f2c | |||
| 25bfa9fa81 | |||
| 979ba22628 | |||
| 19ab724a73 | |||
| def49e19a7 | |||
| 1fb269a0fb | |||
| 0e469634a3 | |||
| 6dfe471686 | |||
| 2c7fc5f926 | |||
| 579d371378 | |||
| c74ca27075 | |||
| e8f7b12bb1 | |||
| e7006b53e3 | |||
| a6e0057c12 | |||
| 1ffed0c039 | |||
| 602df893de | |||
| 3755f788ca | |||
| abc7261488 | |||
| f9c33971d9 | |||
| 7d37325c19 | |||
| d17481575a | |||
| 1ee1d8fdff | |||
| af8f0626ac | |||
| c88353b386 | |||
| 961f367ca8 | |||
| 8bf3be5e61 | |||
| f8b6a009cb | |||
| efa9f0e63d | |||
| a4212e99b0 | |||
| 6884a8e7e0 | |||
| a1b1feb247 | |||
| 70ec3fc24e | |||
| 1e4a4b765f | |||
| cfe49779fa | |||
| c51cff8961 | |||
| 1abecd2c93 | |||
| 3217a7e667 | |||
| 015cafdbdc | |||
| f5a934c213 | |||
| 24813db49f | |||
| ce856dbba7 | |||
| 8b09724420 | |||
| 2af7be1b65 | |||
| dac98da526 | |||
| 9456f7d8cd | |||
| 3ecbd18e56 | |||
| 61fbc3fc0c | |||
| 5bcbd91943 | |||
| cfe32fa219 | |||
| f0f4af864a | |||
| 8bace0e808 | |||
| 2f5cba7f8f | |||
| 528aa4fedd | |||
| 490e1580ad | |||
| cbfbfc8ff3 | |||
| b6edf7ab0b | |||
| 65e6b4de69 | |||
| 2ca62e25f1 | |||
| 1b2a814478 | |||
| e33967aa39 | |||
| a1adefce36 | |||
| 6e87e02e96 | |||
| ab9cc674d5 | |||
| 8e0e13a899 | |||
| 5371426588 | |||
| 1852dea96a | |||
| a8140f91cc | |||
| 2fac9b2740 | |||
| 6a99915afe | |||
| 4e13c3eb5b | |||
| 7e55608975 | |||
| 4bd5cbca8e | |||
| 7c3fa7e1ce | |||
| 3188a147d8 | |||
| 926fbc4b13 | |||
| 006d77fa39 | |||
| 52a792d46f | |||
| 20db989fb1 | |||
| 2c63d3c792 |
+24
-26
@@ -35,7 +35,7 @@ jobs:
|
||||
name: Extract crate metadata
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- name: Extract crate information
|
||||
id: crate_metadata
|
||||
run: |
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt,clippy
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- run: cargo fmt -- --check
|
||||
- run: cargo clippy --locked --all-targets --all-features -- -D warnings
|
||||
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
needs: crate_metadata
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
- name: Install rust toolchain (v${{ needs.crate_metadata.outputs.msrv }})
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
@@ -80,7 +80,7 @@ jobs:
|
||||
name: License checks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: true # we especially want to perform license checks on submodules
|
||||
- run: tests/scripts/license-checks.sh
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Git checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: true # we need all syntax and theme submodules
|
||||
- name: Install Rust toolchain
|
||||
@@ -119,7 +119,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Git checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
- name: Prepare environment variables
|
||||
run: |
|
||||
echo "BAT_SYSTEM_CONFIG_PREFIX=$GITHUB_WORKSPACE/tests/examples/system_config" >> $GITHUB_ENV
|
||||
@@ -135,7 +135,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Git checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
- name: Check documentation
|
||||
@@ -150,7 +150,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: cargo install cargo-audit --locked
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- run: cargo audit
|
||||
|
||||
build:
|
||||
@@ -161,24 +161,24 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- { target: aarch64-unknown-linux-musl , os: ubuntu-latest, dpkg_arch: arm64, use-cross: true }
|
||||
- { target: aarch64-unknown-linux-gnu , os: ubuntu-latest, dpkg_arch: arm64, use-cross: true }
|
||||
- { target: arm-unknown-linux-gnueabihf , os: ubuntu-latest, dpkg_arch: armhf, use-cross: true }
|
||||
- { target: arm-unknown-linux-musleabihf, os: ubuntu-latest, dpkg_arch: musl-linux-armhf, use-cross: true }
|
||||
- { target: i686-pc-windows-msvc , os: windows-2025, }
|
||||
- { target: i686-unknown-linux-gnu , os: ubuntu-latest, dpkg_arch: i686, use-cross: true }
|
||||
- { target: i686-unknown-linux-musl , os: ubuntu-latest, dpkg_arch: musl-linux-i686, use-cross: true }
|
||||
- { target: x86_64-apple-darwin , os: macos-13, }
|
||||
- { target: aarch64-apple-darwin , os: macos-14, }
|
||||
- { target: x86_64-pc-windows-msvc , os: windows-2025, }
|
||||
- { target: aarch64-pc-windows-msvc , os: windows-11-arm, }
|
||||
- { target: x86_64-unknown-linux-gnu , os: ubuntu-latest, dpkg_arch: amd64, use-cross: true }
|
||||
- { target: x86_64-unknown-linux-musl , os: ubuntu-latest, dpkg_arch: musl-linux-amd64, use-cross: true }
|
||||
- { target: aarch64-unknown-linux-musl , os: ubuntu-latest , dpkg_arch: musl-linux-arm64, use-cross: true }
|
||||
- { target: aarch64-unknown-linux-gnu , os: ubuntu-latest , dpkg_arch: arm64, use-cross: true }
|
||||
- { target: arm-unknown-linux-gnueabihf , os: ubuntu-latest , dpkg_arch: armhf, use-cross: true }
|
||||
- { target: arm-unknown-linux-musleabihf, os: ubuntu-latest , dpkg_arch: musl-linux-armhf, use-cross: true }
|
||||
- { target: i686-pc-windows-msvc , os: windows-2025 , }
|
||||
- { target: i686-unknown-linux-gnu , os: ubuntu-latest , dpkg_arch: i386, use-cross: true }
|
||||
- { target: i686-unknown-linux-musl , os: ubuntu-latest , dpkg_arch: musl-linux-i686, use-cross: true }
|
||||
- { target: x86_64-apple-darwin , os: macos-15-intel, }
|
||||
- { target: aarch64-apple-darwin , os: macos-latest , }
|
||||
- { target: x86_64-pc-windows-msvc , os: windows-2025 , }
|
||||
- { target: aarch64-pc-windows-msvc , os: windows-11-arm, }
|
||||
- { target: x86_64-unknown-linux-gnu , os: ubuntu-latest , dpkg_arch: amd64, use-cross: true }
|
||||
- { target: x86_64-unknown-linux-musl , os: ubuntu-latest , dpkg_arch: musl-linux-amd64, use-cross: true }
|
||||
env:
|
||||
BUILD_CMD: cargo
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install prerequisites
|
||||
shell: bash
|
||||
@@ -195,9 +195,7 @@ jobs:
|
||||
|
||||
- name: Install cross
|
||||
if: matrix.job.use-cross
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cross
|
||||
run: cargo install cross --git https://github.com/cross-rs/cross --rev 588b3c99db52b5a9c5906fab96cfadcf1bde7863
|
||||
|
||||
- name: Overwrite build command env variable
|
||||
if: matrix.job.use-cross
|
||||
@@ -335,7 +333,7 @@ jobs:
|
||||
|
||||
DPKG_BASENAME=${{ needs.crate_metadata.outputs.name }}
|
||||
DPKG_CONFLICTS=${{ needs.crate_metadata.outputs.name }}-musl
|
||||
case ${{ matrix.job.target }} in *-musl) DPKG_BASENAME=${{ needs.crate_metadata.outputs.name }}-musl ; DPKG_CONFLICTS=${{ needs.crate_metadata.outputs.name }} ;; esac;
|
||||
case ${{ matrix.job.target }} in *-musl*) DPKG_BASENAME=${{ needs.crate_metadata.outputs.name }}-musl ; DPKG_CONFLICTS=${{ needs.crate_metadata.outputs.name }} ;; esac;
|
||||
DPKG_VERSION=${{ needs.crate_metadata.outputs.version }}
|
||||
DPKG_ARCH="${{ matrix.job.dpkg_arch }}"
|
||||
DPKG_NAME="${DPKG_BASENAME}_${DPKG_VERSION}_${DPKG_ARCH}.deb"
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
PR_BASE: ${{ github.base_ref }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- name: Fetch PR base
|
||||
run: git fetch --no-tags --prune --depth=1 origin
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
.direnv/
|
||||
/target/
|
||||
**/*.rs.bk
|
||||
|
||||
# Editors
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
# Generated files
|
||||
/assets/completions/_bat.ps1
|
||||
/assets/completions/bat.bash
|
||||
@@ -8,3 +13,5 @@
|
||||
/assets/completions/bat.zsh
|
||||
/assets/manual/bat.1
|
||||
/assets/metadata.yaml
|
||||
|
||||
|
||||
|
||||
+6
-6
@@ -130,7 +130,7 @@
|
||||
url = https://github.com/djuretic/SublimeStrace
|
||||
[submodule "assets/syntaxes/Jinja2"]
|
||||
path = assets/syntaxes/02_Extra/Jinja2
|
||||
url = https://github.com/Martin819/sublime-jinja2
|
||||
url = https://github.com/ltrzesniewski/sublime-jinja2.git
|
||||
[submodule "assets/syntaxes/SLS"]
|
||||
path = assets/syntaxes/02_Extra/SLS
|
||||
url = https://github.com/saltstack/sublime-text
|
||||
@@ -196,16 +196,13 @@
|
||||
url = https://github.com/leanprover/vscode-lean4.git
|
||||
[submodule "assets/syntaxes/02_Extra/Zig"]
|
||||
path = assets/syntaxes/02_Extra/Zig
|
||||
url = https://github.com/ziglang/sublime-zig-language.git
|
||||
url = https://codeberg.org/ziglang/sublime-zig-language.git
|
||||
[submodule "assets/syntaxes/02_Extra/gnuplot"]
|
||||
path = assets/syntaxes/02_Extra/gnuplot
|
||||
url = https://github.com/hesstobi/sublime_gnuplot
|
||||
[submodule "assets/syntaxes/02_Extra/SystemVerilog"]
|
||||
path = assets/syntaxes/02_Extra/SystemVerilog
|
||||
url = https://github.com/TheClams/SystemVerilog.git
|
||||
[submodule "assets/themes/visual-studio-dark-plus"]
|
||||
path = assets/themes/visual-studio-dark-plus
|
||||
url = https://github.com/vidann1/visual-studio-dark-plus.git
|
||||
[submodule "assets/syntaxes/02_Extra/SublimeEthereum"]
|
||||
path = assets/syntaxes/02_Extra/SublimeEthereum
|
||||
url = https://github.com/davidhq/SublimeEthereum.git
|
||||
@@ -243,7 +240,7 @@
|
||||
url = https://github.com/dertuxmalwieder/SublimeTodoTxt
|
||||
[submodule "assets/syntaxes/02_Extra/Ada"]
|
||||
path = assets/syntaxes/02_Extra/Ada
|
||||
url = https://github.com/wiremoons/ada-sublime-syntax
|
||||
url = https://github.com/AldanTanneo/ada-sublime-syntax
|
||||
|
||||
[submodule "assets/syntaxes/02_Extra/Crontab"]
|
||||
path = assets/syntaxes/02_Extra/Crontab
|
||||
@@ -281,3 +278,6 @@
|
||||
[submodule "assets/syntaxes/02_Extra/Gomod"]
|
||||
path = assets/syntaxes/02_Extra/Gomod
|
||||
url = https://github.com/mitranim/sublime-gomod
|
||||
[submodule "assets/syntaxes/02_Extra/COBOL"]
|
||||
path = assets/syntaxes/02_Extra/COBOL
|
||||
url = https://github.com/adukhan99/sublime_cobol.git
|
||||
|
||||
+88
-1
@@ -1,3 +1,90 @@
|
||||
# unreleased
|
||||
|
||||
|
||||
## Other
|
||||
|
||||
- Add instructions for removing fish help abbreviations to README, see #3655 (@claw-explorer). Closes #3536
|
||||
|
||||
## Features
|
||||
|
||||
- Preserve `--diff` change markers and snip separators when `--plain` is set. Closes #3630, see #3643 (@mvanhorn)
|
||||
- Added support for `hidden_file_extensions` from `.sublime-syntax` files, see #3613 (@Matei02355)
|
||||
- Add word wrapping mode via `--wrap=word`, see #3597 (@veeceey)
|
||||
- Implement `--unbuffered` mode for streaming input, allowing partial lines to display immediately (e.g. `tail -f | bat -u`). Closes #3555, see #3583 (@mainnebula)
|
||||
- Added an initial `flake.nix` for a ready made development environment; see #3578 (@vorburger)
|
||||
- Add `--quiet-empty` (`-E`) flag to suppress output when input is empty. Closes #1936, see #3563 (@NORMAL-EX)
|
||||
- Improve native man pages and command help syntax highlighting by stripping overstriking, see #3517 (@akirk)
|
||||
- Add `--fallback-syntax`/`--fallback-language` to apply syntax highlighting only when auto-detection fails, see #1341 (@Xavrir)
|
||||
- Map `BUILD` case sensitively to Python (Starlark) for Bazel, see #3576 (@vorburger)
|
||||
|
||||
## Bugfixes
|
||||
- Fix i686 `.deb` package using incorrect architecture name (`i686` instead of `i386`), preventing installation on Debian. Closes #3611, see #3650 (@Sim-hu)
|
||||
- Fix inconsistent `.deb` MUSL package names (aarch64-musl used `arm64` instead of `musl-linux-arm64`, and `musleabihf` target missed `bat-musl` prefix). Closes #3482, see #3642 (@mvanhorn)
|
||||
- Fix incorrect text width computation when using `--binary=as-text` with non-printable characters in caret notation, see #3640 and #3631 (@eyupcanakman)
|
||||
- Fix `BAT_CONFIG_DIR` pointing at system config directory causing duplicate flag errors. Closes #3589, see #3620 (@Xavrir)
|
||||
- Fix syntax highlighting for symlinked files when the symlink name has no extension but the target does. Closes #1001, see #3621 (@Xavrir)
|
||||
- Report error when pager is missing instead of silently falling back, see #3588 (@IMaloney)
|
||||
- Fix `--wrap=never` and `-S` flags being ignored when piping to pager, see #3592 (@IMaloney)
|
||||
- Fix crash with BusyBox `less` on Windows, see #3527 (@Anchal-T)
|
||||
- Fix `bat cache --help` failing with 'unexpected argument' error, see #3580 and #3560 (@NORMAL-EX)
|
||||
- `--help` now correctly honors `--pager=builtin`. See #3516 (@keith-hall)
|
||||
- `--help` now correctly honors custom themes. See #3524 (@keith-hall)
|
||||
- Fixed test compatibility with future Cargo build directory changes, see #3550 (@nmacl)
|
||||
- Fixed bug caused by using `--plain` and `--terminal-width=N` flags simultaneously, see #3529 (@H4k1l)
|
||||
- Fixed syntax tests path, see #3610 (@foxfromworld)
|
||||
|
||||
## Other
|
||||
- Use git version of cross. See #3533 (@OctopusET)
|
||||
- Bump MSRV to 1.88, update `time` crate to 0.3.47 to fix RUSTSEC-2026-0009, see #3581 (@NORMAL-EX)
|
||||
- Allow home and end keys to be used with builtin pager, see #3651 (@keith-hall)
|
||||
- Builtin syntax mapping: cleanup matcher glob parsing logic #3652 (@cyqsimon)
|
||||
|
||||
## Syntaxes
|
||||
|
||||
- Change the URL of Zig submodule from GitHub to Codeberg, see #3519 (@sorairolake)
|
||||
- Don't color strings inside CSV files, to make it easier to tell which column they belong to, see #3521 (@keith-hall)
|
||||
- Add syntax highlighting support for COBOL, see #3584 (@adukhan99)
|
||||
- Fixed manpage syntax so that ANSI escape codes don't get incorrectly highlighted and thus broken, see #3586 (@BlueElectivire)
|
||||
- Map several Google Cloud CLI config files to their appropriate syntax #3635 (@victor-gp)
|
||||
- Map all ignore dotfiles to Git Ignore syntax #3636 (@victor-gp)
|
||||
|
||||
## Themes
|
||||
|
||||
- Remove the Visual Studio Dark+ theme, see #3552 (@CosmicHorrorDev)
|
||||
|
||||
## `bat` as a library
|
||||
|
||||
# v0.26.1
|
||||
|
||||
## Features
|
||||
|
||||
- Add paging to '-h' and '--help' see PR #3478 (@MuntasirSZN)
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Fix hang when using `--list-themes` with an explicit pager, see #3457 (@abhinavcool42)
|
||||
- Fix negative values of N not being parsed in <N:M> line ranges without `=` flag value separator, see #3442 (@lmmx)
|
||||
- Fix broken Docker syntax preventing use of custom assets, see #3476 (@keith-hall)
|
||||
- Fix decorations being applied unexpectedly when piping. Now only line numbers explicitly required on the command line should be applied in auto decorations mode for `cat` compatibility. See #3496 (@keith-hall)
|
||||
- Fix diagnostics attempting to find the version of an executable named builtin when builtin pager is used. See #3498 (@keith-hall)
|
||||
- `--help` now correctly reads the config file for theme information etc. See #3507 (@keith-hall)
|
||||
|
||||
## Other
|
||||
- Improve README documentation on pager options passed to less, see #3443 (@injust)
|
||||
- Make PowerShell completions compatible with PowerShell v5.1, see #3495 (@keith-hall)
|
||||
- Use more robust approach to escaping in Bash completions, see #3448 (@akinomyoga)
|
||||
|
||||
## Syntaxes
|
||||
|
||||
- Update quadlet syntax mapping to include *.{build,pod} files #3484 (@cyqsimon)
|
||||
- Fix inconsistencies in Ada syntax, see #3481 (@AldanTanneo)
|
||||
- Add syntax mapping for podman's `artifact` quadlet files, see #3497 (@xduugu)
|
||||
- Highlight Korn Shell scripts (i.e. with a shebang of ...`ksh`) using Bash syntax, see #3509 (@keith-hall)
|
||||
|
||||
## Themes
|
||||
|
||||
## `bat` as a library
|
||||
|
||||
# v0.26.0
|
||||
|
||||
## Features
|
||||
@@ -29,7 +116,7 @@
|
||||
- Update base16 README links to community driven base16 work #2871 (@JamyGolden)
|
||||
- Work around build failures when building `bat` from vendored sources #3179 (@dtolnay)
|
||||
- CICD: Stop building for x86_64-pc-windows-gnu which fails #3261 (Enselic)
|
||||
- CICD: CICD: replace windows-2019 runners with windows-2025 #3339 (@cyqsimon)
|
||||
- CICD: replace windows-2019 runners with windows-2025 #3339 (@cyqsimon)
|
||||
- Build script: replace string-based codegen with quote-based codegen #3340 (@cyqsimon)
|
||||
- Improve code coverage of `--list-languages` parameter #2942 (@sblondon)
|
||||
- Only start offload worker thread when there's more than 1 core #2956 (@cyqsimon)
|
||||
|
||||
@@ -68,6 +68,8 @@ users, please read the corresponding
|
||||
[documentation](https://github.com/sharkdp/bat/blob/master/doc/assets.md)
|
||||
first.
|
||||
|
||||
To map a file name pattern to an existing syntax, read [the documentation here](https://github.com/sharkdp/bat/blob/master/src/syntax_mapping/builtins/README.md).
|
||||
|
||||
Note: We are currently not accepting new default themes.
|
||||
|
||||
|
||||
@@ -91,3 +93,29 @@ To learn how to write regression tests for theme and syntax changes, read the
|
||||
[Syntax
|
||||
tests](https://github.com/sharkdp/bat/blob/master/doc/assets.md#syntax-tests)
|
||||
section in `assets.md`.
|
||||
|
||||
### Ensuring bat is available for Syntax tests
|
||||
|
||||
The syntax test script (`tests/syntax-tests/update.sh`) calls `bat` from your PATH and regenerates the highlighted output files under
|
||||
`tests/syntax-tests/highlighted/`. These files are used to verify that syntax highlighting works as expected.
|
||||
|
||||
- If you only built the binaries with:
|
||||
```bash
|
||||
cargo build --bins
|
||||
```
|
||||
|
||||
you need to add the debug build to your PATH from the bat project root before running the tests.
|
||||
See also step 5 in [Syntax
|
||||
tests](https://github.com/sharkdp/bat/blob/master/doc/assets.md#syntax-tests) for related instructions.
|
||||
```bash
|
||||
export PATH="$PATH:$(pwd)/target/debug"
|
||||
```
|
||||
Otherwise, you will see:
|
||||
```bash
|
||||
Error: Could not execute 'bat'. Please make sure that the executable is available on the PATH.
|
||||
```
|
||||
- If you installed bat with:
|
||||
```bash
|
||||
cargo install --path . --locked
|
||||
```
|
||||
then bat will be available in ~/.cargo/bin (usually already in PATH), and the tests will run without issues.
|
||||
Generated
+149
-206
@@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
@@ -83,13 +83,12 @@ checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
|
||||
|
||||
[[package]]
|
||||
name = "assert_cmd"
|
||||
version = "2.0.16"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d"
|
||||
checksum = "bcbb6924530aa9e0432442af08bbcafdad182db80d2e560da42a6d442535bf85"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"bstr",
|
||||
"doc-comment",
|
||||
"libc",
|
||||
"predicates",
|
||||
"predicates-core",
|
||||
@@ -111,7 +110,7 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bat"
|
||||
version = "0.26.0"
|
||||
version = "0.26.1"
|
||||
dependencies = [
|
||||
"ansi_colours",
|
||||
"anyhow",
|
||||
@@ -131,7 +130,6 @@ dependencies = [
|
||||
"git2",
|
||||
"globset",
|
||||
"grep-cli",
|
||||
"home",
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"minus",
|
||||
@@ -225,9 +223,9 @@ checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
|
||||
|
||||
[[package]]
|
||||
name = "bytesize"
|
||||
version = "1.3.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc"
|
||||
checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
@@ -254,18 +252,18 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.46"
|
||||
version = "4.5.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57"
|
||||
checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.46"
|
||||
version = "4.5.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41"
|
||||
checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -276,9 +274,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.4"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
|
||||
|
||||
[[package]]
|
||||
name = "clircle"
|
||||
@@ -298,15 +296,15 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.16.0"
|
||||
version = "0.16.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e09ced7ebbccb63b4c65413d821f2e00ce54c5ca4514ddc6b3c892fdbcbc69d"
|
||||
checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"unicode-width",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -394,9 +392,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.10"
|
||||
version = "0.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
|
||||
checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
@@ -404,9 +402,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.10"
|
||||
version = "0.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
|
||||
checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
@@ -418,9 +416,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.10"
|
||||
version = "0.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||
checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
@@ -442,9 +440,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.11"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
|
||||
checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
@@ -472,12 +470,6 @@ version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d"
|
||||
|
||||
[[package]]
|
||||
name = "doc-comment"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
@@ -517,20 +509,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "etcetera"
|
||||
version = "0.10.0"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26c7b13d0780cb82722fd59f6f57f925e143427e4a75313a6c77243bf5326ae6"
|
||||
checksum = "de48cc4d1c1d97a20fd819def54b890cadde72ed3ad0c614822a0a433361be96"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"home",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "execute"
|
||||
version = "0.2.13"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a82608ee96ce76aeab659e9b8d3c2b787bffd223199af88c674923d861ada10"
|
||||
checksum = "0be3cc61fe54b4cae4463cdbda0401978ffe19d4dcc7a5201a312cddf64726dd"
|
||||
dependencies = [
|
||||
"execute-command-macro",
|
||||
"execute-command-tokens",
|
||||
@@ -667,9 +658,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.20.0"
|
||||
version = "0.20.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fda788993cc341f69012feba8bf45c0ba4f3291fcc08e214b4d5a7332d88aff"
|
||||
checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
@@ -686,9 +677,9 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
||||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
version = "0.4.16"
|
||||
version = "0.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5"
|
||||
checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"bstr",
|
||||
@@ -699,9 +690,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "grep-cli"
|
||||
version = "0.1.11"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47f1288f0e06f279f84926fa4c17e3fcd2a22b357927a82f2777f7be26e4cec0"
|
||||
checksum = "cf32d263c5d5cc2a23ce587097f5ddafdb188492ba2e6fb638eaccdc22453631"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"globset",
|
||||
@@ -719,18 +710,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
@@ -879,13 +861,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.8.0"
|
||||
version = "2.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
|
||||
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.15.2",
|
||||
"hashbrown 0.16.1",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -926,15 +909,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.175"
|
||||
version = "0.2.182"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
|
||||
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
version = "0.18.0+1.9.0"
|
||||
version = "0.18.3+1.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1a117465e7e1597e8febea8bb0c410f1c7fb93b1e1cddf34363f8390367ffec"
|
||||
checksum = "c9b3acc4b91781bb0b3386669d325163746af5f6e4f73e6d2d630e09a35f3487"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -966,6 +949,12 @@ version = "0.4.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.7.4"
|
||||
@@ -1032,20 +1021,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.3"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
|
||||
checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.30.1"
|
||||
version = "0.31.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
|
||||
checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
@@ -1070,18 +1059,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.50.1"
|
||||
version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
@@ -1218,9 +1207,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.35"
|
||||
version = "0.2.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a"
|
||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
@@ -1228,9 +1217,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -1284,9 +1273,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
version = "1.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -1296,9 +1285,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -1329,10 +1318,23 @@ dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"linux-raw-sys 0.4.15",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.11.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
@@ -1362,18 +1364,28 @@ checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.217"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.217"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1394,29 +1406,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.0.0"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83"
|
||||
checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "3.12.0"
|
||||
version = "3.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa"
|
||||
checksum = "381b283ce7bc6b476d903296fb59d0d36633652b633b27f64db4fb46dcbfc3b9"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_core",
|
||||
"serde_with_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "3.12.0"
|
||||
version = "3.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e"
|
||||
checksum = "a6d4e30573c8cb306ed6ab1dca8423eec9a463ea0e155f45399455e0368b27e0"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
@@ -1468,9 +1479,9 @@ checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
|
||||
|
||||
[[package]]
|
||||
name = "shell-words"
|
||||
version = "1.1.0"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
|
||||
checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
@@ -1534,9 +1545,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.104"
|
||||
version = "2.0.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1592,16 +1603,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.16.0"
|
||||
version = "3.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91"
|
||||
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
"rustix 1.1.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1615,28 +1625,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "terminal-colorsaurus"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f7226dad4b1817567c1e2f5d453897ef36abe79def7783af3fa241a694e30b3"
|
||||
checksum = "8909f33134da34b43f69145e748790de650a6abd84faf1f82e773444dd293ec8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memchr",
|
||||
"mio 1.0.3",
|
||||
"mio 1.1.0",
|
||||
"terminal-trx",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
"xterm-color",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal-trx"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "975b4233aefa1b02456d5e53b22c61653c743e308c51cf4181191d8ce41753ab"
|
||||
checksum = "662a3cd5ca570df622e848ef18b50c151e65c9835257465417242243b0bce783"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1645,7 +1655,7 @@ version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9"
|
||||
dependencies = [
|
||||
"rustix",
|
||||
"rustix 0.38.43",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -1706,30 +1716,30 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.37"
|
||||
version = "0.3.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
|
||||
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"num-conv",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.2"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.19"
|
||||
version = "0.2.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
|
||||
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
@@ -1747,12 +1757,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.9.1"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0207d6ed1852c2a124c1fbec61621acb8330d2bf969a5d0643131e9affd985a5"
|
||||
checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_parser",
|
||||
@@ -1762,27 +1772,27 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.7.0"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3"
|
||||
checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.0.0"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5c1c469eda89749d2230d8156a5969a69ffe0d6d01200581cdc6110674d293e"
|
||||
checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.0.0"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b679217f2848de74cabd3e8fc5e6d66f40b7da40f8e1954d92054d9010690fd5"
|
||||
checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
@@ -2011,6 +2021,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.2"
|
||||
@@ -2029,15 +2045,6 @@ dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
@@ -2049,11 +2056,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.2",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2080,29 +2087,13 @@ dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.53.0",
|
||||
"windows_aarch64_msvc 0.53.0",
|
||||
"windows_i686_gnu 0.53.0",
|
||||
"windows_i686_gnullvm 0.53.0",
|
||||
"windows_i686_msvc 0.53.0",
|
||||
"windows_x86_64_gnu 0.53.0",
|
||||
"windows_x86_64_gnullvm 0.53.0",
|
||||
"windows_x86_64_msvc 0.53.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
@@ -2115,12 +2106,6 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
@@ -2133,12 +2118,6 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
@@ -2151,24 +2130,12 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
@@ -2181,12 +2148,6 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
@@ -2199,12 +2160,6 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
@@ -2217,12 +2172,6 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
@@ -2235,17 +2184,11 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.11"
|
||||
version = "0.7.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd"
|
||||
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
|
||||
+21
-22
@@ -6,12 +6,12 @@ homepage = "https://github.com/sharkdp/bat"
|
||||
license = "MIT OR Apache-2.0"
|
||||
name = "bat"
|
||||
repository = "https://github.com/sharkdp/bat"
|
||||
version = "0.26.0"
|
||||
version = "0.26.1"
|
||||
exclude = ["assets/syntaxes/*", "assets/themes/*"]
|
||||
build = "build/main.rs"
|
||||
edition = '2021'
|
||||
# You are free to bump MSRV as soon as a reason for bumping emerges.
|
||||
rust-version = "1.79"
|
||||
rust-version = "1.88"
|
||||
|
||||
[features]
|
||||
default = ["application", "git"]
|
||||
@@ -42,16 +42,16 @@ regex-onig = ["syntect/regex-onig"] # Use the "oniguruma" regex engine
|
||||
regex-fancy = ["syntect/regex-fancy"] # Use the rust-only "fancy-regex" engine
|
||||
|
||||
[dependencies]
|
||||
nu-ansi-term = "0.50.0"
|
||||
nu-ansi-term = "0.50.3"
|
||||
ansi_colours = "^1.2"
|
||||
bincode = "1.0"
|
||||
console = "0.16.0"
|
||||
console = "0.16.2"
|
||||
flate2 = "1.1"
|
||||
once_cell = "1.20"
|
||||
thiserror = "2.0"
|
||||
wild = { version = "2.2", optional = true }
|
||||
content_inspector = "0.2.4"
|
||||
shell-words = { version = "1.1.0", optional = true }
|
||||
shell-words = { version = "1.1.1", optional = true }
|
||||
minus = { version = "5.6", optional = true, features = [
|
||||
"dynamic_output",
|
||||
"search",
|
||||
@@ -65,13 +65,13 @@ semver = "1.0"
|
||||
path_abs = { version = "0.5", default-features = false }
|
||||
clircle = { version = "0.6.1", default-features = false }
|
||||
bugreport = { version = "0.5.0", optional = true }
|
||||
etcetera = { version = "0.10.0", optional = true }
|
||||
grep-cli = { version = "0.1.11", optional = true }
|
||||
regex = { version = "1.10.6", optional = true }
|
||||
etcetera = { version = "0.11.0", optional = true }
|
||||
grep-cli = { version = "0.1.12", optional = true }
|
||||
regex = { version = "1.12.2", optional = true }
|
||||
walkdir = { version = "2.5", optional = true }
|
||||
bytesize = { version = "1.3.0" }
|
||||
bytesize = { version = "2.3.1" }
|
||||
encoding_rs = "0.8.35"
|
||||
execute = { version = "0.2.13", optional = true }
|
||||
execute = { version = "0.2.15", optional = true }
|
||||
terminal-colorsaurus = "1.0"
|
||||
unicode-segmentation = "1.12.0"
|
||||
itertools = "0.14.0"
|
||||
@@ -87,44 +87,43 @@ default-features = false
|
||||
features = ["parsing"]
|
||||
|
||||
[dependencies.clap]
|
||||
version = "4.5.46"
|
||||
version = "4.5.60"
|
||||
optional = true
|
||||
features = ["wrap_help", "cargo"]
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
home = "0.5.9"
|
||||
plist = "1.7.0"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = "2.0.12"
|
||||
assert_cmd = "2.0.16"
|
||||
expect-test = "1.5.0"
|
||||
serial_test = { version = "2.0.0", default-features = false }
|
||||
predicates = "3.1.3"
|
||||
wait-timeout = "0.2.1"
|
||||
tempfile = "3.16.0"
|
||||
tempfile = "3.23.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
[target.'cfg(unix)'.dev-dependencies]
|
||||
nix = { version = "0.30", default-features = false, features = ["term"] }
|
||||
nix = { version = "0.31", default-features = false, features = ["term"] }
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1.0.97"
|
||||
indexmap = { version = "2.8.0", features = ["serde"] }
|
||||
indexmap = { version = "2.13.0", features = ["serde"] }
|
||||
itertools = "0.14.0"
|
||||
once_cell = "1.20"
|
||||
prettyplease = "0.2.35"
|
||||
proc-macro2 = "1.0.95"
|
||||
prettyplease = "0.2.37"
|
||||
proc-macro2 = "1.0.106"
|
||||
quote = "1.0.40"
|
||||
regex = "1.10.6"
|
||||
regex = "1.12.2"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_with = { version = "3.12.0", default-features = false, features = ["macros"] }
|
||||
serde_with = { version = "3.17.0", default-features = false, features = ["macros"] }
|
||||
syn = { version = "2.0.104", features = ["full"] }
|
||||
toml = { version = "0.9.1", features = ["preserve_order"] }
|
||||
toml = { version = "0.9.8", features = ["preserve_order"] }
|
||||
walkdir = "2.5"
|
||||
|
||||
[build-dependencies.clap]
|
||||
version = "4.5.46"
|
||||
version = "4.5.60"
|
||||
optional = true
|
||||
features = ["wrap_help", "cargo"]
|
||||
|
||||
|
||||
@@ -31,12 +31,6 @@ A special *thank you* goes to our biggest <a href="doc/sponsors.md">sponsors</a>
|
||||
<br>
|
||||
<sub>Available on MacOS, Linux, Windows</sub>
|
||||
</a>
|
||||
</p><p>
|
||||
<a href="https://graphite.dev/?utm_source=github&utm_medium=repo&utm_campaign=bat">
|
||||
<img src="doc/sponsors/graphite-logo.jpeg" width="200" alt="Graphite">
|
||||
<br>
|
||||
<strong>Graphite is the AI developer productivity platform helping<br>teams on GitHub ship higher quality software, faster</strong>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
### Syntax highlighting
|
||||
@@ -49,7 +43,7 @@ languages:
|
||||
### Git integration
|
||||
|
||||
`bat` communicates with `git` to show modifications with respect to the index
|
||||
(see left side bar):
|
||||
(see left sidebar):
|
||||
|
||||

|
||||
|
||||
@@ -201,18 +195,13 @@ bat main.cpp | xclip
|
||||
`MANPAGER` environment variable:
|
||||
|
||||
```bash
|
||||
export MANPAGER="sh -c 'awk '\''{ gsub(/\x1B\[[0-9;]*m/, \"\", \$0); gsub(/.\x08/, \"\", \$0); print }'\'' | bat -p -lman'"
|
||||
export MANPAGER="bat -plman"
|
||||
man 2 select
|
||||
```
|
||||
(replace `bat` with `batcat` if you are on Debian or Ubuntu)
|
||||
(on some older Debian or Ubuntu releases, the executable is named `batcat` instead of `bat`)
|
||||
|
||||
If you prefer to have this bundled in a new command, you can also use [`batman`](https://github.com/eth-p/bat-extras/blob/master/doc/batman.md).
|
||||
|
||||
> [!WARNING]
|
||||
> This will [not work](https://github.com/sharkdp/bat/issues/1145) out of the box with Mandoc's `man` implementation.
|
||||
>
|
||||
> Please either use `batman`, or convert the shell script to a [shebang executable](https://en.wikipedia.org/wiki/Shebang_(Unix)) and point `MANPAGER` to that.
|
||||
|
||||
Note that the [Manpage syntax](assets/syntaxes/02_Extra/Manpage.sublime-syntax) is developed in this repository and still needs some work.
|
||||
|
||||
#### `prettier` / `shfmt` / `rustfmt`
|
||||
@@ -257,6 +246,14 @@ abbr -a --position anywhere -- -h '-h | bat -plhelp'
|
||||
|
||||
This way, you can keep on using `cp --help`, but get colorized help pages.
|
||||
|
||||
> [!TIP]
|
||||
> To remove these abbreviations later, run:
|
||||
> ```fish
|
||||
> abbr -e -- --help
|
||||
> abbr -e -- -h
|
||||
> ```
|
||||
> The `--` before the abbreviation name is required because `--help` and `-h` start with dashes, which would otherwise be interpreted as flags to `abbr` itself.
|
||||
|
||||
Be aware that in some cases, `-h` may not be a shorthand of `--help` (for example with `ls`). In cases where you need to use `-h`
|
||||
as a command argument you can prepend `\` to the argument (eg. `ls \-h`) to escape the aliasing defined above.
|
||||
|
||||
@@ -287,8 +284,8 @@ If your Ubuntu/Debian installation is new enough you can simply run:
|
||||
sudo apt install bat
|
||||
```
|
||||
|
||||
**Important**: If you install `bat` this way, please note that the executable may be installed as `batcat` instead of `bat` (due to [a name
|
||||
clash with another package](https://github.com/sharkdp/bat/issues/982)). You can set up a `bat -> batcat` symlink or alias to prevent any issues that may come up because of this and to be consistent with other distributions:
|
||||
**Important**: On some older Ubuntu/Debian releases, the executable is installed as `batcat` instead of `bat` (due to [a name
|
||||
clash with another package](https://github.com/sharkdp/bat/issues/982)). On newer releases, the executable is available as `bat`. If `bat --version` does not work after installation, try `batcat --version` instead. You can set up a `bat -> batcat` symlink or alias to prevent any issues that may come up because of this and to be consistent with other distributions:
|
||||
``` bash
|
||||
mkdir -p ~/.local/bin
|
||||
ln -s /usr/bin/batcat ~/.local/bin/bat
|
||||
@@ -540,6 +537,12 @@ variable to make these changes permanent or use `bat`'s
|
||||
> Or, if you want to override the styles completely, you use `--style=numbers` to
|
||||
> only show the line numbers.
|
||||
|
||||
### Decorations
|
||||
|
||||
By default, `bat` only shows decorations (such as line numbers, file headers, grid borders, etc.) when outputting to an interactive terminal. You can control this behavior with the `--decorations` option. Use `--decorations=always` to show decorations even when piping output to another command, or `--decorations=never` to disable them entirely. Possible values are `auto` (default), `never`, and `always`.
|
||||
|
||||
There is also the `--force-colorization` option, which is an alias for `--decorations=always --color=always`. This is useful if you want to keep colorization and decorations when piping `bat`'s output to another program.
|
||||
|
||||
### Adding new syntaxes / language definitions
|
||||
|
||||
Should you find that a particular syntax is not available within `bat`, you can follow these
|
||||
@@ -586,7 +589,8 @@ syntax:
|
||||
|
||||
This works very similar to how we add new syntax definitions.
|
||||
> [!NOTE]
|
||||
> Themes are stored in [`.tmTheme` files](https://www.sublimetext.com/docs/color_schemes_tmtheme.html).
|
||||
> Custom themes must be stored in [`.tmTheme` files](https://www.sublimetext.com/docs/color_schemes_tmtheme.html).
|
||||
> Newer `.sublime-color-scheme` files are currently not supported.
|
||||
|
||||
First, create a folder with the new syntax highlighting themes:
|
||||
```bash
|
||||
@@ -670,22 +674,22 @@ to improve the experience. Specifically, `-R`/`--RAW-CONTROL-CHARS`, `-F`/`--qui
|
||||
> - The `--paging=always` argument is used.
|
||||
> - The `BAT_PAGING` environment is set to `always`.
|
||||
|
||||
The `-R` option is needed to interpret ANSI colors correctly.
|
||||
The `-R`/`--RAW-CONTROL-CHARS` option is needed to interpret ANSI colors correctly.
|
||||
|
||||
The `-F` option instructs `less` to exit immediately if the output size is smaller than
|
||||
The `-F`/`--quit-if-one-screen` option instructs `less` to exit immediately if the output size is smaller than
|
||||
the vertical size of the terminal. This is convenient for small files because you do not
|
||||
have to press `q` to quit the pager.
|
||||
|
||||
The `-K` option instructs `less` to exit immediately when an interrupt signal is received.
|
||||
The `-K`/`--quit-on-intr` option instructs `less` to exit immediately when an interrupt signal is received.
|
||||
This is useful to ensure that `less` quits together with `bat` on SIGINT.
|
||||
|
||||
The `-X` option is needed to fix a bug with the `--quit-if-one-screen` feature in versions
|
||||
of `less` older than version 530. Unfortunately, it also breaks mouse-wheel support in `less`.
|
||||
The `-X`/`--no-init` option is added to versions of `less` older than version 530 (older than 558 on Windows) to
|
||||
fix a bug with the `-F`/`--quit-if-one-screen` feature. Unfortunately, it also breaks mouse-wheel support in `less`.
|
||||
If you want to enable mouse-wheel scrolling on older versions of `less` and do not mind losing
|
||||
the quit-if-one-screen feature, you can set the pager (via `--pager` or `BAT_PAGER`) to `less -R`.
|
||||
For `less` 530 or newer, it should work out of the box.
|
||||
|
||||
The `-S` option is added when `bat`'s `-S`/`--chop-long-lines` option is used. This tells `less`
|
||||
The `-S`/`--chop-long-lines` option is added when `bat`'s `-S`/`--chop-long-lines` option is used. This tells `less`
|
||||
to truncate any lines larger than the terminal width.
|
||||
|
||||
### Indentation
|
||||
|
||||
Vendored
BIN
Binary file not shown.
Vendored
+8
-4
@@ -9,7 +9,7 @@ Register-ArgumentCompleter -Native -CommandName '{{PROJECT_EXECUTABLE}}' -Script
|
||||
$ArrayCompletion = @('bash', 'fish', 'zsh', 'ps1')
|
||||
$ArrayWhen = @('auto', 'never', 'always')
|
||||
$ArrayYesNo = @('never', 'always')
|
||||
$ArrayWrap = @('always', 'never', 'character')
|
||||
$ArrayWrap = @('always', 'never', 'character', 'word')
|
||||
$ArrayBinary = @('no-printing', 'as-text')
|
||||
$ArrayPrint = @('unicode', 'caret')
|
||||
|
||||
@@ -49,7 +49,10 @@ Register-ArgumentCompleter -Native -CommandName '{{PROJECT_EXECUTABLE}}' -Script
|
||||
}
|
||||
'*;--language' {
|
||||
Get-MyLanguages |
|
||||
ForEach-Object {[CompletionResult]::new(($_.MyParameter -replace "^(.*)$", '''$1'''), $_.MyParameter, [CompletionResultType]::ParameterName, $_.MyDescription ?? '_no value_')}
|
||||
ForEach-Object {
|
||||
$desc = if ($null -eq $_.MyDescription) { '_no value_' } else { $_.MyDescription }
|
||||
[CompletionResult]::new(($_.MyParameter -replace "^(.*)$", '''$1'''), $_.MyParameter, [CompletionResultType]::ParameterName, $desc)
|
||||
}
|
||||
break
|
||||
}
|
||||
'*;--theme' {
|
||||
@@ -132,7 +135,7 @@ Register-ArgumentCompleter -Native -CommandName '{{PROJECT_EXECUTABLE}}' -Script
|
||||
[CompletionResult]::new('--file-name' , 'file-name' , [CompletionResultType]::ParameterName, 'Specify the name to display for a file.')
|
||||
[CompletionResult]::new('--diff-context' , 'diff-context' , [CompletionResultType]::ParameterName, 'diff-context')
|
||||
[CompletionResult]::new('--tabs' , 'tabs' , [CompletionResultType]::ParameterName, 'Set the tab width to T spaces.')
|
||||
[CompletionResult]::new('--wrap' , 'wrap' , [CompletionResultType]::ParameterName, 'Specify the text-wrapping mode (*auto*, character).')
|
||||
[CompletionResult]::new('--wrap' , 'wrap' , [CompletionResultType]::ParameterName, 'Specify the text-wrapping mode (*auto*, never, character, word).')
|
||||
[CompletionResult]::new('--terminal-width' , 'terminal-width' , [CompletionResultType]::ParameterName, 'Explicitly set the width of the terminal instead of determining it automatically. If prefixed with ''+'' or ''-'', the value will be treated as an offset to the actual terminal width. See also: ''--wrap''.')
|
||||
[CompletionResult]::new('--color' , 'color' , [CompletionResultType]::ParameterName, 'When to use colors (*auto*, never, always).')
|
||||
[CompletionResult]::new('--italic-text' , 'italic-text' , [CompletionResultType]::ParameterName, 'Use italics in output (always, *never*)')
|
||||
@@ -170,7 +173,7 @@ Register-ArgumentCompleter -Native -CommandName '{{PROJECT_EXECUTABLE}}' -Script
|
||||
# [CompletionResult]::new('-L' , 'L' , [CompletionResultType]::ParameterName, 'Display all supported languages.')
|
||||
[CompletionResult]::new('--list-languages' , 'list-languages' , [CompletionResultType]::ParameterName, 'Display all supported languages.')
|
||||
# [CompletionResult]::new('-u' , 'u' , [CompletionResultType]::ParameterName, 'u')
|
||||
[CompletionResult]::new('--unbuffered' , 'unbuffered' , [CompletionResultType]::ParameterName, 'unbuffered')
|
||||
[CompletionResult]::new('--unbuffered' , 'unbuffered' , [CompletionResultType]::ParameterName, 'Enable unbuffered input reading for streaming use cases')
|
||||
[CompletionResult]::new('--completion' , 'completion' , [CompletionResultType]::ParameterName, 'Show shell completion for a certain shell. [possible values: bash, fish, zsh, ps1]')
|
||||
[CompletionResult]::new('--no-config' , 'no-config' , [CompletionResultType]::ParameterName, 'Do not use the configuration file')
|
||||
[CompletionResult]::new('--no-custom-assets' , 'no-custom-assets' , [CompletionResultType]::ParameterName, 'Do not load custom assets')
|
||||
@@ -183,6 +186,7 @@ Register-ArgumentCompleter -Native -CommandName '{{PROJECT_EXECUTABLE}}' -Script
|
||||
[CompletionResult]::new('--acknowledgements' , 'acknowledgements' , [CompletionResultType]::ParameterName, 'Show acknowledgements.')
|
||||
[CompletionResult]::new('--set-terminal-title' , 'set-terminal-title' , [CompletionResultType]::ParameterName, 'Sets terminal title to filenames when using a pager.')
|
||||
[CompletionResult]::new('--diagnostic' , 'diagnostic' , [CompletionResultType]::ParameterName, 'Show diagnostic information for bug reports.')
|
||||
[CompletionResult]::new('--quiet-empty' , 'quiet-empty' , [CompletionResultType]::ParameterName, 'Do not produce any output when the input is empty.')
|
||||
# [CompletionResult]::new('-h' , 'h' , [CompletionResultType]::ParameterName, 'Print this help message.')
|
||||
[CompletionResult]::new('--help' , 'help' , [CompletionResultType]::ParameterName, 'Print this help message.')
|
||||
# [CompletionResult]::new('-V' , 'V' , [CompletionResultType]::ParameterName, 'Show version information.')
|
||||
|
||||
Vendored
+34
-14
@@ -14,18 +14,36 @@ __bat_escape_completions()
|
||||
{
|
||||
# Do not escape if completing a quoted value.
|
||||
[[ $cur == [\"\']* ]] && return 0
|
||||
# printf -v to an array index is available in bash >= 4.1.
|
||||
# Use it if available, as -o filenames is semantically incorrect if
|
||||
# we are not actually completing filenames, and it has side effects
|
||||
# (e.g. adds trailing slash to candidates matching present dirs).
|
||||
if ((
|
||||
BASH_VERSINFO[0] > 5 || \
|
||||
BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 3
|
||||
)); then
|
||||
# bash >= 5.3 has "compopt -o fullquote", which exactly does
|
||||
# what this function tries to do.
|
||||
compopt -o fullquote
|
||||
elif ((
|
||||
BASH_VERSINFO[0] > 4 || \
|
||||
BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] > 0
|
||||
)); then
|
||||
# printf -v to an array index is available in bash >= 4.1.
|
||||
# Use it if available, as -o filenames is semantically
|
||||
# incorrect if we are not actually completing filenames, and it
|
||||
# has side effects (e.g. adds trailing slash to candidates
|
||||
# matching present dirs).
|
||||
local i
|
||||
for i in ${!COMPREPLY[*]}; do
|
||||
printf -v "COMPREPLY[i]" %q "${COMPREPLY[i]}"
|
||||
done
|
||||
|
||||
# We can use "compopt -o noquote" available in bash >= 4.3 to
|
||||
# prevent further quoting by the shell, which would be
|
||||
# unexpectedly applied when a quoted result matches a filename.
|
||||
if ((
|
||||
BASH_VERSINFO[0] > 4 || \
|
||||
BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 3
|
||||
)); then
|
||||
compopt -o noquote
|
||||
fi
|
||||
else
|
||||
compopt -o filenames
|
||||
fi
|
||||
@@ -66,7 +84,7 @@ _bat() {
|
||||
printf "%s\n" "$lang"
|
||||
done
|
||||
)" -- "$cur"))
|
||||
__bat_escape_completions
|
||||
__bat_escape_completions
|
||||
return 0
|
||||
;;
|
||||
-H | --highlight-line | \
|
||||
@@ -82,6 +100,7 @@ _bat() {
|
||||
--lessopen | \
|
||||
--no-paging | \
|
||||
--diagnostic | \
|
||||
--quiet-empty | \
|
||||
--acknowledgements | \
|
||||
-h | --help | \
|
||||
-V | --version | \
|
||||
@@ -98,7 +117,7 @@ _bat() {
|
||||
return 0
|
||||
;;
|
||||
--wrap)
|
||||
COMPREPLY=($(compgen -W "auto never character" -- "$cur"))
|
||||
COMPREPLY=($(compgen -W "auto never character word" -- "$cur"))
|
||||
return 0
|
||||
;;
|
||||
--binary)
|
||||
@@ -130,16 +149,16 @@ _bat() {
|
||||
return 0
|
||||
;;
|
||||
--theme)
|
||||
local IFS=$'\n'
|
||||
COMPREPLY=($(compgen -W "auto${IFS}auto:always${IFS}auto:system${IFS}dark${IFS}light${IFS}$("$1" --list-themes)" -- "$cur"))
|
||||
__bat_escape_completions
|
||||
return 0
|
||||
;;
|
||||
local IFS=$'\n'
|
||||
COMPREPLY=($(compgen -W "auto${IFS}auto:always${IFS}auto:system${IFS}dark${IFS}light${IFS}$("$1" --list-themes)" -- "$cur"))
|
||||
__bat_escape_completions
|
||||
return 0
|
||||
;;
|
||||
--theme-dark | \
|
||||
--theme-light)
|
||||
local IFS=$'\n'
|
||||
COMPREPLY=($(compgen -W "$("$1" --list-themes)" -- "$cur"))
|
||||
__bat_escape_completions
|
||||
__bat_escape_completions
|
||||
return 0
|
||||
;;
|
||||
--style)
|
||||
@@ -158,7 +177,7 @@ _bat() {
|
||||
numbers
|
||||
snip
|
||||
)
|
||||
# shellcheck disable=SC2016
|
||||
# shellcheck disable=SC2016
|
||||
if declare -F _comp_delimited >/dev/null 2>&1; then
|
||||
# bash-completion > 2.11
|
||||
_comp_delimited , -W '"${styles[@]}"'
|
||||
@@ -171,8 +190,8 @@ _bat() {
|
||||
$split && return 0
|
||||
|
||||
if [[ $cur == -* ]]; then
|
||||
# --unbuffered excluded intentionally (no-op)
|
||||
COMPREPLY=($(compgen -W "
|
||||
--unbuffered
|
||||
--show-all
|
||||
--nonprintable-notation
|
||||
--binary
|
||||
@@ -209,6 +228,7 @@ _bat() {
|
||||
--lessopen
|
||||
--completion
|
||||
--diagnostic
|
||||
--quiet-empty
|
||||
--acknowledgements
|
||||
--set-terminal-title
|
||||
--help
|
||||
|
||||
Vendored
+4
-2
@@ -61,7 +61,7 @@ function __bat_no_excl_args
|
||||
-s V -l version \
|
||||
-l acknowledgements \
|
||||
-l config-dir -l config-file \
|
||||
-l diagnostic \
|
||||
-l diagnostic -l quiet-empty \
|
||||
-l list-languages -l list-themes
|
||||
end
|
||||
|
||||
@@ -118,6 +118,7 @@ set -l wrap_opts '
|
||||
auto\tdefault
|
||||
never\t
|
||||
character\t
|
||||
word\t
|
||||
'
|
||||
|
||||
# While --tabs theoretically takes any number, most people should be OK with these.
|
||||
@@ -159,6 +160,7 @@ complete -c $bat -l config-file -f -d "Display location of configuration file" -
|
||||
complete -c $bat -l decorations -x -a "$decorations_opts" -d "When to use --style decorations" -n __bat_no_excl_args
|
||||
|
||||
complete -c $bat -l diagnostic -d "Print diagnostic info for bug reports" -n __fish_is_first_arg
|
||||
complete -c $bat -l quiet-empty -d "Do not produce any output when the input is empty" -n __fish_is_first_arg
|
||||
|
||||
complete -c $bat -s d -l diff -d "Only show lines with Git changes" -n __bat_no_excl_args
|
||||
|
||||
@@ -238,7 +240,7 @@ complete -c $bat -l theme-dark -x -a "(command $bat --list-themes | command cat)
|
||||
|
||||
complete -c $bat -l theme-light -x -a "(command $bat --list-themes | command cat)" -d "Set the syntax highlighting theme for light backgrounds" -n __bat_no_excl_args
|
||||
|
||||
complete -c $bat -s u -l unbuffered -d "This option exists for POSIX-compliance reasons" -n __bat_no_excl_args
|
||||
complete -c $bat -s u -l unbuffered -d "Enable unbuffered input reading for streaming use cases" -n __bat_no_excl_args
|
||||
|
||||
complete -c $bat -s V -l version -f -d "Show version information" -n __fish_is_first_arg
|
||||
|
||||
|
||||
Vendored
+3
-2
@@ -34,7 +34,7 @@ _{{PROJECT_EXECUTABLE}}_main() {
|
||||
'(-d --diff)'--diff'[only show lines that have been added/removed/modified]'
|
||||
--diff-context='[specify lines of context around added/removed/modified lines when using `--diff`]:lines'
|
||||
--tabs='[set the tab width]:tab width [4]'
|
||||
--wrap='[specify the text-wrapping mode]:mode [auto]:(auto never character)'
|
||||
--wrap='[specify the text-wrapping mode]:mode [auto]:(auto never character word)'
|
||||
'!(--wrap)'{-S,--chop-long-lines}
|
||||
--terminal-width='[explicitly set the width of the terminal instead of determining it automatically]:width'
|
||||
'(-n --number --diff --diff-context)'{-n,--number}'[show line numbers]'
|
||||
@@ -58,10 +58,11 @@ _{{PROJECT_EXECUTABLE}}_main() {
|
||||
default auto full plain changes header header-filename header-filesize grid rule numbers snip'
|
||||
\*{-r+,--line-range=}'[only print the specified line range]:start\:end'
|
||||
'(* -)'{-L,--list-languages}'[display all supported languages]'
|
||||
'(-u --unbuffered)'--unbuffered'[this option exists for POSIX-compliance reasons]'
|
||||
'(-u --unbuffered)'--unbuffered'[enable unbuffered input reading for streaming use cases]'
|
||||
--completion='[show shell completion for a certain shell]:shell:(bash fish zsh ps1)'
|
||||
--set-terminal-title'[sets terminal title to filenames when using a pager]'
|
||||
--diagnostic'[show diagnostic information for bug reports]'
|
||||
--quiet-empty'[do not produce any output when the input is empty]'
|
||||
-P'[disable paging]'
|
||||
"--no-config[don't use the configuration file]"
|
||||
"--no-custom-assets[don't load custom assets]"
|
||||
|
||||
Vendored
+12
-4
@@ -102,8 +102,10 @@ Set the tab width to T spaces. Use a width of 0 to pass tabs through directly
|
||||
.HP
|
||||
\fB\-\-wrap\fR <mode>
|
||||
.IP
|
||||
Specify the text\-wrapping mode (*auto*, never, character). The '\-\-terminal\-width' option
|
||||
can be used in addition to control the output width.
|
||||
Specify the text\-wrapping mode (*auto*, never, character, word). The '\-\-terminal\-width' option
|
||||
can be used in addition to control the output width. In \fBword\fR mode, lines are broken at
|
||||
whitespace boundaries. If a single word exceeds the terminal width, it falls back to
|
||||
character wrapping.
|
||||
.HP
|
||||
\fB\-S\fR, \fB\-\-chop\-long\-lines\fR
|
||||
.IP
|
||||
@@ -263,8 +265,10 @@ Display a list of supported languages for syntax highlighting.
|
||||
.HP
|
||||
\fB\-u\fR, \fB\-\-unbuffered\fR
|
||||
.IP
|
||||
This option exists for POSIX\-compliance reasons ('u' is for 'unbuffered'). The output is
|
||||
always unbuffered \- this option is simply ignored.
|
||||
Enable unbuffered input reading. When this flag is set, bat will display data as soon as it
|
||||
is available, without waiting for a complete line. This is useful for streaming use cases like
|
||||
\&'tail \-f logfile | bat \-u \-\-paging=never'. Note that line numbers are automatically disabled
|
||||
in unbuffered mode, and syntax highlighting may be imperfect on partial lines.
|
||||
.HP
|
||||
\fB\-\-no\-custom\-assets\fR
|
||||
.IP
|
||||
@@ -282,6 +286,10 @@ Show bat's cache directory.
|
||||
.IP
|
||||
Show diagnostic information for bug reports.
|
||||
.HP
|
||||
\fB\-\-quiet\-empty\fR
|
||||
.IP
|
||||
Do not produce any output when the input is empty (e.g. an empty file or empty stdin). This is useful in scripts where silent behavior is preferred for empty input.
|
||||
.HP
|
||||
\fB\-\-acknowledgements\fR
|
||||
.IP
|
||||
Show acknowledgements.
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ index e973e319..07c170a7 100644
|
||||
first_line_match: |
|
||||
(?x)
|
||||
- ^\#! .* \b(bash|zsh|sh|tcsh|ash)\b
|
||||
+ ^\#! .* \b(bash|zsh|sh|tcsh|ash|dash)\b
|
||||
+ ^\#! .* \b(bash|zsh|sh|tcsh|ash|dash|ksh)\b
|
||||
| ^\# \s* -\*- [^*]* mode: \s* shell-script [^*]* -\*-
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
Vendored
BIN
Binary file not shown.
Vendored
+1
-1
Submodule assets/syntaxes/02_Extra/Ada updated: e2b8fd5175...3f17c2363e
+1
Submodule assets/syntaxes/02_Extra/COBOL added at 27825ee022
+1
-1
@@ -79,7 +79,7 @@ contexts:
|
||||
|
||||
double_quoted_string:
|
||||
- meta_include_prototype: false
|
||||
- meta_scope: string.quoted.double.csv
|
||||
- meta_scope: meta.string.quoted.double.csv
|
||||
- match: '""'
|
||||
scope: constant.character.escape.csv
|
||||
- match: '"'
|
||||
|
||||
Vendored
+1
-1
Submodule assets/syntaxes/02_Extra/Docker updated: c001fb2805...0f6b7bc87a
Vendored
+1
-1
Submodule assets/syntaxes/02_Extra/Idris2 updated: 2874f206f5...bbfe50e023
Vendored
+1
-1
Submodule assets/syntaxes/02_Extra/Jinja2 updated: 45355633d1...e572427f6a
+2
-1
@@ -9,6 +9,7 @@ scope: source.man
|
||||
variables:
|
||||
section_heading: '^(?!#)\S.*$'
|
||||
command_line_option: '(--?[A-Za-z0-9][_A-Za-z0-9-]*)'
|
||||
ansi_escape_sequence: '\e\[[\?=]?(?:\d+;?)*[A-Za-z]'
|
||||
|
||||
contexts:
|
||||
prototype:
|
||||
@@ -69,7 +70,7 @@ contexts:
|
||||
escape: '(?={{section_heading}})'
|
||||
|
||||
function-call:
|
||||
- match: '\b([A-Za-z0-9_\-]+\.)?([A-Za-z0-9_\-]+)(\()([^)]*)(\))'
|
||||
- match: '(?<!\e\[)(?:\b|\s)(?:{{ansi_escape_sequence}})?([A-Za-z0-9_\-]+\.)?([A-Za-z0-9_\-]+)(?:{{ansi_escape_sequence}})?(\()([^)]*)(\))'
|
||||
captures:
|
||||
1: entity.name.function.man
|
||||
2: entity.name.function.man
|
||||
|
||||
+1
-1
Submodule assets/syntaxes/02_Extra/SystemVerilog updated: 7eca705e87...b340f1c7f6
Vendored
+1
-1
Submodule assets/syntaxes/02_Extra/Zig updated: 8a4a3fe4a0...c16d871ccc
Vendored
+1
-1
Submodule assets/syntaxes/02_Extra/cmd-help updated: c71ba410bd...2757ac1681
+1
-1
Submodule assets/syntaxes/02_Extra/typst-syntax-highlight updated: 4e2e68b0a1...363f0e767c
-1
Submodule assets/themes/visual-studio-dark-plus deleted from 01ee1e8e0d
+118
-53
@@ -47,12 +47,49 @@ impl ToTokens for MappingTarget {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, DeserializeFromStr)]
|
||||
/// Helper type for deserializing a `Matcher` from either a plain string or a
|
||||
/// `{ glob = "...", case_sensitive = true }` struct.
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum RawMatcher {
|
||||
Simple(String),
|
||||
Full {
|
||||
glob: String,
|
||||
case_sensitive: Option<bool>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Whether a glob pattern should be matched case-sensitively or case-insensitively.
|
||||
///
|
||||
/// Mirrors the runtime `Case` type in `src/syntax_mapping.rs`.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
enum Case {
|
||||
Sensitive,
|
||||
Insensitive,
|
||||
}
|
||||
impl ToTokens for Case {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let t = match self {
|
||||
Self::Sensitive => quote! { Case::Sensitive },
|
||||
Self::Insensitive => quote! { Case::Insensitive },
|
||||
};
|
||||
tokens.append_all(t);
|
||||
}
|
||||
}
|
||||
|
||||
/// A single matcher.
|
||||
///
|
||||
/// Codegen converts this into a `Lazy<Option<GlobMatcher>>`.
|
||||
struct Matcher(Vec<MatcherSegment>);
|
||||
/// Parse a matcher.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize)]
|
||||
#[serde(try_from = "RawMatcher")]
|
||||
struct Matcher {
|
||||
segments: Vec<MatcherSegment>,
|
||||
/// Whether the glob pattern should be matched case-sensitively.
|
||||
///
|
||||
/// Defaults to `Case::Insensitive` for backwards compatibility.
|
||||
case: Case,
|
||||
}
|
||||
/// Parse the glob pattern of a matcher.
|
||||
///
|
||||
/// Note that this implementation is rather strict: it will greedily interpret
|
||||
/// every valid environment variable replacement as such, then immediately
|
||||
@@ -66,68 +103,95 @@ struct Matcher(Vec<MatcherSegment>);
|
||||
///
|
||||
/// Revision history:
|
||||
/// - 2024-02-20: allow `{` and `}` (glob brace expansion)
|
||||
impl FromStr for Matcher {
|
||||
type Err = anyhow::Error;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
use MatcherSegment as Seg;
|
||||
static VAR_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\$\{([\w\d_]+)\}").unwrap());
|
||||
fn parse_glob(s: &str) -> Result<Vec<MatcherSegment>, anyhow::Error> {
|
||||
use MatcherSegment as Seg;
|
||||
static VAR_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\$\{([\w\d_]+)\}").unwrap());
|
||||
|
||||
let mut segments = vec![];
|
||||
let mut text_start = 0;
|
||||
for capture in VAR_REGEX.captures_iter(s) {
|
||||
let match_0 = capture.get(0).unwrap();
|
||||
let mut segments = vec![];
|
||||
let mut text_start = 0;
|
||||
for capture in VAR_REGEX.captures_iter(s) {
|
||||
let match_0 = capture.get(0).unwrap();
|
||||
|
||||
// text before this var
|
||||
let text_end = match_0.start();
|
||||
segments.push(Seg::Text(s[text_start..text_end].into()));
|
||||
text_start = match_0.end();
|
||||
// text before this var
|
||||
let text_end = match_0.start();
|
||||
segments.push(Seg::Text(s[text_start..text_end].into()));
|
||||
text_start = match_0.end();
|
||||
|
||||
// this var
|
||||
segments.push(Seg::Env(capture.get(1).unwrap().as_str().into()));
|
||||
// this var
|
||||
segments.push(Seg::Env(capture.get(1).unwrap().as_str().into()));
|
||||
}
|
||||
// possible trailing text
|
||||
segments.push(Seg::Text(s[text_start..].into()));
|
||||
|
||||
// cleanup empty text segments
|
||||
let non_empty_segments = segments
|
||||
.into_iter()
|
||||
.filter(|seg| seg.text().map(|t| !t.is_empty()).unwrap_or(true))
|
||||
.collect_vec();
|
||||
|
||||
// sanity check
|
||||
if non_empty_segments
|
||||
.windows(2)
|
||||
.any(|segs| segs[0].is_text() && segs[1].is_text())
|
||||
{
|
||||
unreachable!("Parsed into consecutive text segments: {non_empty_segments:?}");
|
||||
}
|
||||
|
||||
// guard empty case
|
||||
if non_empty_segments.is_empty() {
|
||||
bail!(r#"Parsed an empty matcher: "{s}""#);
|
||||
}
|
||||
|
||||
// guard variable syntax leftover fragments
|
||||
if non_empty_segments
|
||||
.iter()
|
||||
.filter_map(Seg::text)
|
||||
.any(|t| t.contains('$'))
|
||||
{
|
||||
bail!(r#"Invalid matcher: "{s}""#);
|
||||
}
|
||||
|
||||
Ok(non_empty_segments)
|
||||
}
|
||||
impl TryFrom<RawMatcher> for Matcher {
|
||||
type Error = anyhow::Error;
|
||||
fn try_from(raw: RawMatcher) -> Result<Self, Self::Error> {
|
||||
const DEFAULT_CASE: Case = Case::Insensitive;
|
||||
match &raw {
|
||||
RawMatcher::Simple(s) => {
|
||||
let segments = parse_glob(s)?;
|
||||
Ok(Self {
|
||||
segments,
|
||||
case: DEFAULT_CASE,
|
||||
})
|
||||
}
|
||||
RawMatcher::Full {
|
||||
glob,
|
||||
case_sensitive,
|
||||
} => {
|
||||
let segments = parse_glob(glob)?;
|
||||
let case = match case_sensitive {
|
||||
None => DEFAULT_CASE,
|
||||
Some(false) => Case::Insensitive,
|
||||
Some(true) => Case::Sensitive,
|
||||
};
|
||||
Ok(Self { segments, case })
|
||||
}
|
||||
}
|
||||
// possible trailing text
|
||||
segments.push(Seg::Text(s[text_start..].into()));
|
||||
|
||||
// cleanup empty text segments
|
||||
let non_empty_segments = segments
|
||||
.into_iter()
|
||||
.filter(|seg| seg.text().map(|t| !t.is_empty()).unwrap_or(true))
|
||||
.collect_vec();
|
||||
|
||||
// sanity check
|
||||
if non_empty_segments
|
||||
.windows(2)
|
||||
.any(|segs| segs[0].is_text() && segs[1].is_text())
|
||||
{
|
||||
unreachable!("Parsed into consecutive text segments: {non_empty_segments:?}");
|
||||
}
|
||||
|
||||
// guard empty case
|
||||
if non_empty_segments.is_empty() {
|
||||
bail!(r#"Parsed an empty matcher: "{s}""#);
|
||||
}
|
||||
|
||||
// guard variable syntax leftover fragments
|
||||
if non_empty_segments
|
||||
.iter()
|
||||
.filter_map(Seg::text)
|
||||
.any(|t| t.contains('$'))
|
||||
{
|
||||
bail!(r#"Invalid matcher: "{s}""#);
|
||||
}
|
||||
|
||||
Ok(Self(non_empty_segments))
|
||||
}
|
||||
}
|
||||
impl ToTokens for Matcher {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let t = match self.0.as_slice() {
|
||||
let case = &self.case;
|
||||
let t = match self.segments.as_slice() {
|
||||
[] => unreachable!("0-length matcher should never be created"),
|
||||
[MatcherSegment::Text(text)] => {
|
||||
quote! { Lazy::new(|| Some(build_matcher_fixed(#text))) }
|
||||
quote! { Lazy::new(|| Some(build_matcher_fixed(#text, #case))) }
|
||||
}
|
||||
// parser logic ensures that this case can only happen when there are dynamic segments
|
||||
segs @ [_, ..] => quote! { Lazy::new(|| build_matcher_dynamic(&[ #(#segs),* ])) },
|
||||
segs @ [_, ..] => {
|
||||
quote! { Lazy::new(|| build_matcher_dynamic(&[ #(#segs),* ], #case)) }
|
||||
}
|
||||
};
|
||||
tokens.append_all(t);
|
||||
}
|
||||
@@ -175,6 +239,7 @@ impl MatcherSegment {
|
||||
/// A struct that models a single .toml file in /src/syntax_mapping/builtins/.
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
struct MappingDefModel {
|
||||
#[serde(default)]
|
||||
mappings: IndexMap<MappingTarget, Vec<Matcher>>,
|
||||
}
|
||||
impl MappingDefModel {
|
||||
|
||||
+3
-3
@@ -6,12 +6,12 @@ if you are not looking for a program like `bat`, this comparison might not be fo
|
||||
|
||||
| | bat | [pygments](http://pygments.org/) | [highlight](http://www.andre-simon.de/doku/highlight/highlight.php) | [ccat](https://github.com/jingweno/ccat) | [source-highlight](https://www.gnu.org/software/src-highlite/) | [hicat](https://github.com/rstacruz/hicat) | [coderay](https://github.com/rubychan/coderay) | [rouge](https://github.com/jneen/rouge) | [clp](https://github.com/jpe90/clp) |
|
||||
|----------------------------------------------|---------------------------------------------------------------------|----------------------------------|---------------------------------------------------------------------|------------------------------------------|----------------------------------------------------------------|-----------------------------------------------------|-----------------------------------------------------|-----------------------------------------------------|-----------------------------------------------------|
|
||||
| Drop-in `cat` replacement | :heavy_check_mark: [*](https://github.com/sharkdp/bat/issues/134) | :x: | :x: | (:heavy_check_mark:) | :x: | :x: [*](https://github.com/rstacruz/hicat/issues/6) | :x: | :x: | :x: |
|
||||
| Drop-in `cat` replacement | :heavy_check_mark: [*](https://github.com/sharkdp/bat/issues/134) | :x: | :x: | (✔️) | :x: | :x: [*](https://github.com/rstacruz/hicat/issues/6) | :x: | :x: | :x: |
|
||||
| Git integration | :heavy_check_mark: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: |
|
||||
| Automatic paging | :heavy_check_mark: | :x: | :x: | :x: | :x: | :heavy_check_mark: | :x: | :x: | :x: |
|
||||
| Languages (circa) | 150 | 300 | 200 | 7 | 80 | 130 | 30 | 130 | 150 |
|
||||
| Extensible (languages, themes) | :heavy_check_mark: | (:heavy_check_mark:) | (:heavy_check_mark:) | :x: | (:heavy_check_mark:) | :x: | :x: | :x: | :heavy_check_mark: |
|
||||
| Advanced highlighting (e.g. nested syntaxes) | :heavy_check_mark: | :heavy_check_mark: | (:heavy_check_mark:) ? | :x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Extensible (languages, themes) | :heavy_check_mark: | (✔️) | (✔️) | :x: | (✔️) | :x: | :x: | :x: | :heavy_check_mark: |
|
||||
| Advanced highlighting (e.g. nested syntaxes) | :heavy_check_mark: | :heavy_check_mark: | (✔️) ? | :x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Execution time [ms] (`jquery-3.3.1.js`) | 422 | 455 | 299 | 39 | 208 | 287 | 128 | 740 | 22 |
|
||||
| Execution time [ms] (`miniz.c`) | 27 | 169 | 19 | 4 | 36 | 131 | 58 | 231 | 4 |
|
||||
| Execution time [ms] (957 kB XML file) | 215 | 296 | 236 | 165 | 83 | 412 | 135 | 386 | 127 |
|
||||
|
||||
+19
-4
@@ -37,6 +37,13 @@ Options:
|
||||
name (like 'C++' or 'LaTeX') or possible file extension (like 'cpp', 'hpp' or 'md'). Use
|
||||
'--list-languages' to show all supported language names and file extensions.
|
||||
|
||||
--fallback-syntax <fallback-syntax>
|
||||
Set a fallback language for syntax highlighting when auto-detection fails. Unlike
|
||||
'--language', this is only used when no syntax could be detected from filename, custom
|
||||
syntax mappings, or first-line detection.
|
||||
|
||||
[aliases: --fallback-language]
|
||||
|
||||
-H, --highlight-line <N:M>
|
||||
Highlight the specified line ranges with a different background color For example:
|
||||
'--highlight-line 40' highlights line 40
|
||||
@@ -61,8 +68,8 @@ Options:
|
||||
Set the tab width to T spaces. Use a width of 0 to pass tabs through directly
|
||||
|
||||
--wrap <mode>
|
||||
Specify the text-wrapping mode (*auto*, never, character). The '--terminal-width' option
|
||||
can be used in addition to control the output width.
|
||||
Specify the text-wrapping mode (*auto*, never, character, word). The '--terminal-width'
|
||||
option can be used in addition to control the output width.
|
||||
|
||||
-S, --chop-long-lines
|
||||
Truncate all lines longer than screen width. Alias for '--wrap=never'.
|
||||
@@ -194,6 +201,7 @@ Options:
|
||||
'--line-range :40' prints lines 1 to 40
|
||||
'--line-range 40:' prints lines 40 to the end of the file
|
||||
'--line-range 40' only prints line 40
|
||||
'--line-range -10:' prints the last 10 lines
|
||||
'--line-range 30:+10' prints lines 30 to 40
|
||||
'--line-range 35::5' prints lines 30 to 40 (line 35 with 5 lines of context)
|
||||
'--line-range 30:40:2' prints lines 28 to 42 (range 30-40 with 2 lines of context)
|
||||
@@ -202,8 +210,11 @@ Options:
|
||||
Display a list of supported languages for syntax highlighting.
|
||||
|
||||
-u, --unbuffered
|
||||
This option exists for POSIX-compliance reasons ('u' is for 'unbuffered'). The output is
|
||||
always unbuffered - this option is simply ignored.
|
||||
Enable unbuffered input reading. When this flag is set, bat will display data as soon as
|
||||
it is available, without waiting for a complete line. This is useful for streaming use
|
||||
cases like 'tail -f logfile | bat -u --paging=never'. Note that line numbers are
|
||||
automatically disabled in unbuffered mode, and syntax highlighting may be imperfect on
|
||||
partial lines.
|
||||
|
||||
--completion <SHELL>
|
||||
Show shell completion for a certain shell. [possible values: bash, fish, zsh, ps1]
|
||||
@@ -211,6 +222,10 @@ Options:
|
||||
--diagnostic
|
||||
Show diagnostic information for bug reports.
|
||||
|
||||
-E, --quiet-empty
|
||||
When this flag is set, bat will produce no output at all when the input is empty. This is
|
||||
useful when piping commands that may produce empty output, like 'git diff'.
|
||||
|
||||
--acknowledgements
|
||||
Show acknowledgements.
|
||||
|
||||
|
||||
+7
-1
@@ -17,6 +17,8 @@ Options:
|
||||
Show plain style (alias for '--style=plain').
|
||||
-l, --language <language>
|
||||
Set the language for syntax highlighting.
|
||||
--fallback-syntax <fallback-syntax>
|
||||
Set a fallback language for undetected syntaxes. [aliases: --fallback-language]
|
||||
-H, --highlight-line <N:M>
|
||||
Highlight lines N through M.
|
||||
--file-name <name>
|
||||
@@ -26,7 +28,7 @@ Options:
|
||||
--tabs <T>
|
||||
Set the tab width to T spaces.
|
||||
--wrap <mode>
|
||||
Specify the text-wrapping mode (*auto*, never, character).
|
||||
Specify the text-wrapping mode (*auto*, never, character, word).
|
||||
-S, --chop-long-lines
|
||||
Truncate all lines longer than screen width. Alias for '--wrap=never'.
|
||||
-n, --number
|
||||
@@ -58,8 +60,12 @@ Options:
|
||||
Only print the lines from N to M.
|
||||
-L, --list-languages
|
||||
Display all supported languages.
|
||||
-u, --unbuffered
|
||||
Enable unbuffered input reading for streaming use cases.
|
||||
--completion <SHELL>
|
||||
Show shell completion for a certain shell. [possible values: bash, fish, zsh, ps1]
|
||||
-E, --quiet-empty
|
||||
Produce no output when the input is empty.
|
||||
-h, --help
|
||||
Print help (see more with '--help')
|
||||
-V, --version
|
||||
|
||||
Generated
+27
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1769789167,
|
||||
"narHash": "sha256-kKB3bqYJU5nzYeIROI82Ef9VtTbu4uA3YydSk/Bioa8=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "62c8382960464ceb98ea593cb8321a2cf8f9e3e5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
description = "bat";
|
||||
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
|
||||
outputs =
|
||||
{ self, ... }@inputs:
|
||||
let
|
||||
supportedSystems = [
|
||||
"x86_64-linux" # 64-bit Intel/AMD Linux
|
||||
"aarch64-linux" # 64-bit ARM Linux
|
||||
"aarch64-darwin" # 64-bit ARM macOS
|
||||
"x86_64-darwin" # 64-bit Intel macOS
|
||||
];
|
||||
|
||||
forEachSupportedSystem =
|
||||
f:
|
||||
inputs.nixpkgs.lib.genAttrs supportedSystems (
|
||||
system:
|
||||
f {
|
||||
inherit system;
|
||||
pkgs = import inputs.nixpkgs {
|
||||
inherit system;
|
||||
};
|
||||
}
|
||||
);
|
||||
in
|
||||
{
|
||||
devShells = forEachSupportedSystem (
|
||||
{ pkgs, system }:
|
||||
{
|
||||
default = pkgs.mkShellNoCC {
|
||||
packages = with pkgs; [
|
||||
cargo
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
+143
-15
@@ -210,6 +210,7 @@ impl HighlightingAssets {
|
||||
pub(crate) fn get_syntax(
|
||||
&self,
|
||||
language: Option<&str>,
|
||||
fallback_syntax: Option<&str>,
|
||||
input: &mut OpenedInput,
|
||||
mapping: &SyntaxMapping,
|
||||
) -> Result<SyntaxReferenceInSet<'_>> {
|
||||
@@ -222,21 +223,50 @@ impl HighlightingAssets {
|
||||
}
|
||||
|
||||
let path = input.path();
|
||||
let path_syntax = if let Some(path) = path {
|
||||
self.get_syntax_for_path(
|
||||
PathAbs::new(path).map_or_else(|_| path.to_owned(), |p| p.as_path().to_path_buf()),
|
||||
mapping,
|
||||
)
|
||||
let absolute_path = path.and_then(|p| {
|
||||
PathAbs::new(p)
|
||||
.ok()
|
||||
.map(|abs| abs.as_path().to_path_buf())
|
||||
.or_else(|| Some(p.to_owned()))
|
||||
});
|
||||
|
||||
let path_syntax = if let Some(ref path) = absolute_path {
|
||||
self.get_syntax_for_path(path, mapping).or_else(|e| {
|
||||
// If syntax detection failed on the given path, retry with the
|
||||
// canonicalized path (which resolves symlinks). This handles
|
||||
// cases like `Aliases/0install -> ../Formula/zero-install.rb`
|
||||
// where the symlink name has no extension but the target does.
|
||||
// See #1001.
|
||||
if matches!(e, Error::UndetectedSyntax(_)) {
|
||||
if let Ok(resolved) = fs::canonicalize(path) {
|
||||
if resolved != *path {
|
||||
return match self.get_syntax_for_path(&resolved, mapping) {
|
||||
Ok(syntax) => Ok(syntax),
|
||||
Err(Error::UndetectedSyntax(_)) => Err(e),
|
||||
Err(err) => Err(err),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e)
|
||||
})
|
||||
} else {
|
||||
Err(Error::UndetectedSyntax("[unknown]".into()))
|
||||
};
|
||||
|
||||
// If a path wasn't provided, or if path based syntax detection
|
||||
// above failed, we fall back to first-line syntax detection.
|
||||
match path_syntax {
|
||||
// If a path wasn't provided, or if path based syntax detection
|
||||
// above failed, we fall back to first-line syntax detection.
|
||||
Err(Error::UndetectedSyntax(path)) => self
|
||||
.get_first_line_syntax(&mut input.reader)?
|
||||
.ok_or(Error::UndetectedSyntax(path)),
|
||||
Err(Error::UndetectedSyntax(path)) => {
|
||||
if let Some(syntax_in_set) = self.get_first_line_syntax(&mut input.reader)? {
|
||||
Ok(syntax_in_set)
|
||||
} else if let Some(language) = fallback_syntax {
|
||||
self.find_syntax_by_token(language)?
|
||||
.ok_or_else(|| Error::UnknownSyntax(language.to_owned()))
|
||||
} else {
|
||||
Err(Error::UndetectedSyntax(path))
|
||||
}
|
||||
}
|
||||
_ => path_syntax,
|
||||
}
|
||||
}
|
||||
@@ -262,6 +292,24 @@ impl HighlightingAssets {
|
||||
.map(|syntax| SyntaxReferenceInSet { syntax, syntax_set }))
|
||||
}
|
||||
|
||||
fn find_syntax_by_hidden_file_name(
|
||||
&self,
|
||||
file_name: &OsStr,
|
||||
) -> Result<Option<SyntaxReferenceInSet<'_>>> {
|
||||
let Some(hidden_file_extension) = file_name
|
||||
.to_str()
|
||||
.and_then(|name| name.strip_prefix('.'))
|
||||
.filter(|name| !name.is_empty())
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// syntect stores `hidden_file_extensions` in the same extension list as
|
||||
// regular file extensions, but dotfiles must be queried without the
|
||||
// leading period.
|
||||
self.find_syntax_by_extension(Some(OsStr::new(hidden_file_extension)))
|
||||
}
|
||||
|
||||
fn find_syntax_by_token(&self, token: &str) -> Result<Option<SyntaxReferenceInSet<'_>>> {
|
||||
let syntax_set = self.get_syntax_set()?;
|
||||
Ok(syntax_set
|
||||
@@ -275,6 +323,9 @@ impl HighlightingAssets {
|
||||
ignored_suffixes: &IgnoredSuffixes,
|
||||
) -> Result<Option<SyntaxReferenceInSet<'_>>> {
|
||||
let mut syntax = self.find_syntax_by_extension(Some(file_name))?;
|
||||
if syntax.is_none() {
|
||||
syntax = self.find_syntax_by_hidden_file_name(file_name)?;
|
||||
}
|
||||
if syntax.is_none() {
|
||||
syntax =
|
||||
ignored_suffixes.try_with_stripped_suffix(file_name, |stripped_file_name| {
|
||||
@@ -395,11 +446,12 @@ mod tests {
|
||||
fn get_syntax_name(
|
||||
&self,
|
||||
language: Option<&str>,
|
||||
fallback_syntax: Option<&str>,
|
||||
input: &mut OpenedInput,
|
||||
mapping: &SyntaxMapping,
|
||||
) -> String {
|
||||
self.assets
|
||||
.get_syntax(language, input, mapping)
|
||||
.get_syntax(language, fallback_syntax, input, mapping)
|
||||
.map(|syntax_in_set| syntax_in_set.syntax.name.clone())
|
||||
.unwrap_or_else(|_| "!no syntax!".to_owned())
|
||||
}
|
||||
@@ -419,7 +471,7 @@ mod tests {
|
||||
let dummy_stdin: &[u8] = &[];
|
||||
let mut opened_input = input.open(dummy_stdin, None).unwrap();
|
||||
|
||||
self.get_syntax_name(None, &mut opened_input, &self.syntax_mapping)
|
||||
self.get_syntax_name(None, None, &mut opened_input, &self.syntax_mapping)
|
||||
}
|
||||
|
||||
fn syntax_for_file_with_content_os(&self, file_name: &OsStr, first_line: &str) -> String {
|
||||
@@ -429,7 +481,7 @@ mod tests {
|
||||
let dummy_stdin: &[u8] = &[];
|
||||
let mut opened_input = input.open(dummy_stdin, None).unwrap();
|
||||
|
||||
self.get_syntax_name(None, &mut opened_input, &self.syntax_mapping)
|
||||
self.get_syntax_name(None, None, &mut opened_input, &self.syntax_mapping)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -449,7 +501,7 @@ mod tests {
|
||||
let input = Input::stdin().with_name(Some(file_name));
|
||||
let mut opened_input = input.open(content, None).unwrap();
|
||||
|
||||
self.get_syntax_name(None, &mut opened_input, &self.syntax_mapping)
|
||||
self.get_syntax_name(None, None, &mut opened_input, &self.syntax_mapping)
|
||||
}
|
||||
|
||||
fn syntax_is_same_for_inputkinds(&self, file_name: &str, content: &str) -> bool {
|
||||
@@ -661,6 +713,55 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "build-assets")]
|
||||
#[test]
|
||||
fn syntax_detection_hidden_file_extensions() {
|
||||
let source_dir = TempDir::new().expect("creation of temporary source directory");
|
||||
let cache_dir = TempDir::new().expect("creation of temporary cache directory");
|
||||
let syntax_dir = source_dir.path().join("syntaxes");
|
||||
|
||||
std::fs::create_dir_all(&syntax_dir).expect("creation of syntax directory succeeds");
|
||||
std::fs::write(
|
||||
syntax_dir.join("HiddenFileExtension.sublime-syntax"),
|
||||
r#"%YAML 1.2
|
||||
---
|
||||
name: Hidden File Extension
|
||||
hidden_file_extensions:
|
||||
- testrc
|
||||
scope: source.hiddenfileextension
|
||||
|
||||
contexts:
|
||||
main:
|
||||
- match: .
|
||||
scope: source.hiddenfileextension
|
||||
"#,
|
||||
)
|
||||
.expect("custom syntax can be written");
|
||||
|
||||
build(
|
||||
source_dir.path(),
|
||||
false,
|
||||
false,
|
||||
cache_dir.path(),
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
)
|
||||
.expect("custom assets can be built");
|
||||
|
||||
let test = SyntaxDetectionTest {
|
||||
assets: HighlightingAssets::from_cache(cache_dir.path())
|
||||
.expect("custom syntax cache can be loaded"),
|
||||
syntax_mapping: SyntaxMapping::new(),
|
||||
temp_dir: TempDir::new().expect("creation of temporary directory"),
|
||||
};
|
||||
|
||||
assert_eq!(test.syntax_for_file(".testrc"), "Hidden File Extension");
|
||||
assert_eq!(
|
||||
test.syntax_for_stdin_with_content(".testrc", b""),
|
||||
"Hidden File Extension"
|
||||
);
|
||||
assert!(test.syntax_is_same_for_inputkinds(".testrc", ""));
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn syntax_detection_for_symlinked_file() {
|
||||
@@ -682,8 +783,35 @@ mod tests {
|
||||
let mut opened_input = input.open(dummy_stdin, None).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
test.get_syntax_name(None, &mut opened_input, &test.syntax_mapping),
|
||||
test.get_syntax_name(None, None, &mut opened_input, &test.syntax_mapping),
|
||||
"SSH Config"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn syntax_detection_for_symlinked_file_by_target_extension() {
|
||||
use std::os::unix::fs::symlink;
|
||||
|
||||
let test = SyntaxDetectionTest::new();
|
||||
|
||||
let formula_dir = test.temp_dir.path().join("Formula");
|
||||
std::fs::create_dir(&formula_dir).unwrap();
|
||||
let target = formula_dir.join("zero-install.rb");
|
||||
File::create(&target).unwrap();
|
||||
|
||||
let aliases_dir = test.temp_dir.path().join("Aliases");
|
||||
std::fs::create_dir(&aliases_dir).unwrap();
|
||||
let link = aliases_dir.join("0install");
|
||||
symlink(&target, &link).unwrap();
|
||||
|
||||
let input = Input::ordinary_file(&link);
|
||||
let dummy_stdin: &[u8] = &[];
|
||||
let mut opened_input = input.open(dummy_stdin, None).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
test.get_syntax_name(None, None, &mut opened_input, &test.syntax_mapping),
|
||||
"Ruby"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+271
-60
@@ -38,9 +38,23 @@ pub fn env_no_color() -> bool {
|
||||
env::var_os("NO_COLOR").is_some_and(|x| !x.is_empty())
|
||||
}
|
||||
|
||||
enum HelpType {
|
||||
Short,
|
||||
Long,
|
||||
}
|
||||
|
||||
pub struct App {
|
||||
pub matches: ArgMatches,
|
||||
interactive_output: bool,
|
||||
/// True if -n / --number was passed on the command line
|
||||
/// (not from config file or environment variables).
|
||||
/// This is used to honor the flag when piping output, similar to `cat -n`.
|
||||
number_from_cli: bool,
|
||||
/// True if --wrap=character was passed on the command line
|
||||
/// (not from config file or environment variables).
|
||||
/// When piping output (non-interactive), --wrap=character is ignored unless
|
||||
/// it was explicitly provided on the command line.
|
||||
wrap_character_from_cli: bool,
|
||||
}
|
||||
|
||||
impl App {
|
||||
@@ -50,65 +64,230 @@ impl App {
|
||||
|
||||
let interactive_output = std::io::stdout().is_terminal();
|
||||
|
||||
// Check if the -n / --number option was passed on the command line
|
||||
// (before merging with config file and environment variables).
|
||||
// This is needed to honor the -n flag when piping output, similar to `cat -n`.
|
||||
// We need to handle both standalone (-n, --number) and combined short flags (-pn, -An, etc.)
|
||||
// Note: We only check if -n appears and is not overridden by -p in the same combined flag.
|
||||
// For combined flags like -np, -p comes after -n and overrides it, so we don't count it.
|
||||
// For combined flags like -pn, -n comes after -p and takes effect.
|
||||
let number_from_cli = wild::args_os().any(|arg| {
|
||||
let arg_str = arg.to_string_lossy();
|
||||
if arg_str == "-n" || arg_str == "--number" {
|
||||
return true;
|
||||
}
|
||||
// Handle combined short flags
|
||||
// Only count -n if it's the LAST flag in the combined form (so -p doesn't override it)
|
||||
// or if -p is not present in the combined form
|
||||
if arg_str.starts_with('-') && !arg_str.starts_with("--") && arg_str.len() > 2 {
|
||||
let chars: Vec<char> = arg_str.chars().skip(1).collect();
|
||||
let n_pos = chars.iter().position(|&c| c == 'n');
|
||||
let p_pos = chars.iter().position(|&c| c == 'p');
|
||||
// -n is in the combined flag and either:
|
||||
// - -p is not present, OR
|
||||
// - -n comes after -p (so -n takes effect)
|
||||
if let Some(n) = n_pos {
|
||||
if p_pos.is_none() || n > p_pos.unwrap() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
});
|
||||
|
||||
// Check if --wrap=character was passed on the command line
|
||||
// (before merging with config file and environment variables).
|
||||
// This is needed to honor --wrap=character when piping output, while
|
||||
// ignoring it when it comes only from the config file or BAT_OPTS.
|
||||
let cli_args_vec: Vec<_> = wild::args_os().collect();
|
||||
let wrap_character_from_cli = cli_args_vec
|
||||
.iter()
|
||||
.any(|arg| arg.to_string_lossy() == "--wrap=character")
|
||||
|| cli_args_vec.windows(2).any(|pair| {
|
||||
pair[0].to_string_lossy() == "--wrap" && pair[1].to_string_lossy() == "character"
|
||||
});
|
||||
|
||||
let matches = Self::matches(interactive_output)?;
|
||||
|
||||
if matches.get_flag("help") {
|
||||
let help_type = if wild::args_os().any(|arg| arg == "--help") {
|
||||
HelpType::Long
|
||||
} else {
|
||||
HelpType::Short
|
||||
};
|
||||
|
||||
let use_pager = match matches.get_one::<String>("paging").map(|s| s.as_str()) {
|
||||
Some("never") => false,
|
||||
_ => !matches.get_flag("no-paging"),
|
||||
};
|
||||
|
||||
let use_color = match matches.get_one::<String>("color").map(|s| s.as_str()) {
|
||||
Some("always") => true,
|
||||
Some("never") => false,
|
||||
_ => interactive_output, // auto: use color if interactive
|
||||
};
|
||||
|
||||
let pager = matches.get_one::<String>("pager").map(|s| s.as_str());
|
||||
let theme_options = Self::theme_options_from_matches(&matches);
|
||||
let use_custom_assets = !matches.get_flag("no-custom-assets");
|
||||
|
||||
Self::display_help(
|
||||
interactive_output,
|
||||
help_type,
|
||||
use_pager,
|
||||
use_color,
|
||||
pager,
|
||||
theme_options,
|
||||
use_custom_assets,
|
||||
)?;
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
Ok(App {
|
||||
matches: Self::matches(interactive_output)?,
|
||||
matches,
|
||||
interactive_output,
|
||||
number_from_cli,
|
||||
wrap_character_from_cli,
|
||||
})
|
||||
}
|
||||
|
||||
fn display_help(
|
||||
interactive_output: bool,
|
||||
help_type: HelpType,
|
||||
use_pager: bool,
|
||||
use_color: bool,
|
||||
pager: Option<&str>,
|
||||
theme_options: ThemeOptions,
|
||||
use_custom_assets: bool,
|
||||
) -> Result<()> {
|
||||
use crate::assets::assets_from_cache_or_binary;
|
||||
use crate::directories::PROJECT_DIRS;
|
||||
use bat::{
|
||||
config::Config,
|
||||
controller::Controller,
|
||||
input::Input,
|
||||
style::{StyleComponent, StyleComponents},
|
||||
theme::theme,
|
||||
PagingMode,
|
||||
};
|
||||
|
||||
let mut cmd = clap_app::build_app(interactive_output);
|
||||
let help_text = match help_type {
|
||||
HelpType::Short => cmd.render_help().to_string(),
|
||||
HelpType::Long => cmd.render_long_help().to_string(),
|
||||
};
|
||||
|
||||
let inputs: Vec<Input> = vec![Input::from_reader(Box::new(help_text.as_bytes()))];
|
||||
|
||||
let paging_mode = if use_pager {
|
||||
PagingMode::QuitIfOneScreen
|
||||
} else {
|
||||
PagingMode::Never
|
||||
};
|
||||
|
||||
let help_config = Config {
|
||||
style_components: StyleComponents::new(StyleComponent::Plain.components(false)),
|
||||
paging_mode,
|
||||
pager,
|
||||
colored_output: use_color,
|
||||
true_color: use_color,
|
||||
language: if use_color { Some("help") } else { None },
|
||||
theme: theme(theme_options).to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let cache_dir = PROJECT_DIRS.cache_dir();
|
||||
let assets = assets_from_cache_or_binary(use_custom_assets, cache_dir)?;
|
||||
Controller::new(&help_config, &assets)
|
||||
.run(inputs, None)
|
||||
.ok();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Build argument list with env vars and CLI args (without config file)
|
||||
fn build_args_without_config() -> Vec<std::ffi::OsString> {
|
||||
let mut cli_args = wild::args_os();
|
||||
let mut args = get_args_from_env_vars();
|
||||
|
||||
// Put the zero-th CLI argument (program name) first
|
||||
args.insert(0, cli_args.next().unwrap());
|
||||
|
||||
// .. and the rest at the end
|
||||
cli_args.for_each(|a| args.push(a));
|
||||
|
||||
args
|
||||
}
|
||||
|
||||
fn matches(interactive_output: bool) -> Result<ArgMatches> {
|
||||
// Check if we should skip config file processing for special arguments
|
||||
// that don't require full application setup (help, version, diagnostic)
|
||||
// that don't require full application setup (version, diagnostic)
|
||||
let should_skip_config = wild::args_os().any(|arg| {
|
||||
matches!(
|
||||
arg.to_str(),
|
||||
Some("-h" | "--help" | "-V" | "--version" | "--diagnostic" | "--diagnostics")
|
||||
Some("-V" | "--version" | "--diagnostic" | "--diagnostics")
|
||||
)
|
||||
});
|
||||
|
||||
let args = if wild::args_os().nth(1) == Some("cache".into()) {
|
||||
// Check if help was requested - help should read the config file but be
|
||||
// forgiving of invalid arguments (so configured theme etc. can be used)
|
||||
let help_requested =
|
||||
wild::args_os().any(|arg| matches!(arg.to_str(), Some("-h" | "--help")));
|
||||
|
||||
if wild::args_os().nth(1) == Some("cache".into()) {
|
||||
// Skip the config file and env vars
|
||||
let args = wild::args_os().collect::<Vec<_>>();
|
||||
return Ok(clap_app::build_app(interactive_output).get_matches_from(args));
|
||||
}
|
||||
|
||||
wild::args_os().collect::<Vec<_>>()
|
||||
} else if wild::args_os().any(|arg| arg == "--no-config") || should_skip_config {
|
||||
if wild::args_os().any(|arg| arg == "--no-config") || should_skip_config {
|
||||
// Skip the arguments in bats config file when --no-config is present
|
||||
// or when user requests help, version, or diagnostic information
|
||||
// or when user requests version or diagnostic information
|
||||
let args = Self::build_args_without_config();
|
||||
return Ok(clap_app::build_app(interactive_output).get_matches_from(args));
|
||||
}
|
||||
|
||||
let mut cli_args = wild::args_os();
|
||||
let mut args = if should_skip_config {
|
||||
// For special commands, don't even try to load env vars that might fail
|
||||
vec![]
|
||||
} else {
|
||||
get_args_from_env_vars()
|
||||
};
|
||||
// Build arguments with config file
|
||||
let mut cli_args = wild::args_os();
|
||||
|
||||
// Put the zero-th CLI argument (program name) first
|
||||
args.insert(0, cli_args.next().unwrap());
|
||||
|
||||
// .. and the rest at the end
|
||||
cli_args.for_each(|a| args.push(a));
|
||||
|
||||
args
|
||||
} else {
|
||||
let mut cli_args = wild::args_os();
|
||||
|
||||
// Read arguments from bats config file
|
||||
let mut args = get_args_from_env_opts_var()
|
||||
.unwrap_or_else(get_args_from_config_file)
|
||||
.map_err(|_| "Could not parse configuration file")?;
|
||||
|
||||
// Selected env vars supersede config vars
|
||||
args.extend(get_args_from_env_vars());
|
||||
|
||||
// Put the zero-th CLI argument (program name) first
|
||||
args.insert(0, cli_args.next().unwrap());
|
||||
|
||||
// .. and the rest at the end
|
||||
cli_args.for_each(|a| args.push(a));
|
||||
args
|
||||
// Read arguments from bats config file
|
||||
let config_args = match get_args_from_env_opts_var() {
|
||||
Some(result) => result,
|
||||
None => get_args_from_config_file(),
|
||||
};
|
||||
|
||||
Ok(clap_app::build_app(interactive_output).get_matches_from(args))
|
||||
// For help, ignore config file parse errors (use empty config instead)
|
||||
// For non-help, propagate the error
|
||||
let mut args = if help_requested {
|
||||
config_args.unwrap_or_default()
|
||||
} else {
|
||||
config_args.map_err(|_| "Could not parse configuration file")?
|
||||
};
|
||||
|
||||
// Selected env vars supersede config vars
|
||||
args.extend(get_args_from_env_vars());
|
||||
|
||||
// Put the zero-th CLI argument (program name) first
|
||||
args.insert(0, cli_args.next().unwrap());
|
||||
|
||||
// .. and the rest at the end
|
||||
cli_args.for_each(|a| args.push(a));
|
||||
|
||||
// For help, try parsing with config, and if clap fails (e.g., invalid
|
||||
// argument in config), fall back to parsing without config file args
|
||||
if help_requested {
|
||||
let app = clap_app::build_app(interactive_output);
|
||||
match app.try_get_matches_from(args) {
|
||||
Ok(matches) => Ok(matches),
|
||||
Err(_) => {
|
||||
// Config has invalid arguments, fall back to just env vars + CLI args
|
||||
let fallback_args = Self::build_args_without_config();
|
||||
Ok(clap_app::build_app(interactive_output).get_matches_from(fallback_args))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Ok(clap_app::build_app(interactive_output).get_matches_from(args))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn config(&self, inputs: &[Input]) -> Result<Config<'_>> {
|
||||
@@ -223,6 +402,10 @@ impl App {
|
||||
None
|
||||
}
|
||||
}),
|
||||
fallback_syntax: self
|
||||
.matches
|
||||
.get_one::<String>("fallback-syntax")
|
||||
.map(|s| s.as_str()),
|
||||
show_nonprintable: self.matches.get_flag("show-all"),
|
||||
nonprintable_notation: match self
|
||||
.matches
|
||||
@@ -238,27 +421,38 @@ impl App {
|
||||
Some("no-printing") => BinaryBehavior::NoPrinting,
|
||||
_ => unreachable!("other values for --binary are not allowed"),
|
||||
},
|
||||
wrapping_mode: if self.interactive_output || maybe_term_width.is_some() {
|
||||
if !self.matches.get_flag("chop-long-lines") {
|
||||
wrapping_mode: {
|
||||
if self.matches.get_flag("chop-long-lines") {
|
||||
WrappingMode::NoWrapping(true)
|
||||
} else {
|
||||
match self.matches.get_one::<String>("wrap").map(|s| s.as_str()) {
|
||||
Some("character") => WrappingMode::Character,
|
||||
Some("never") => WrappingMode::NoWrapping(true),
|
||||
Some("auto") | None => {
|
||||
if style_components.plain() {
|
||||
Some("character") => {
|
||||
if !self.interactive_output && !self.wrap_character_from_cli {
|
||||
// When piping output (non-interactive), ignore --wrap=character
|
||||
// unless it was explicitly provided on the command line.
|
||||
WrappingMode::NoWrapping(false)
|
||||
} else {
|
||||
WrappingMode::Character
|
||||
}
|
||||
}
|
||||
Some("word") => WrappingMode::Word,
|
||||
Some("never") => WrappingMode::NoWrapping(true),
|
||||
Some("auto") | None => {
|
||||
if self.interactive_output || maybe_term_width.is_some() {
|
||||
if style_components.plain() && maybe_term_width.is_none() {
|
||||
WrappingMode::NoWrapping(false)
|
||||
} else {
|
||||
WrappingMode::Character
|
||||
}
|
||||
} else {
|
||||
// We don't have the tty width when piping to another program.
|
||||
// There's no point in wrapping when this is the case.
|
||||
WrappingMode::NoWrapping(false)
|
||||
}
|
||||
}
|
||||
_ => unreachable!("other values for --wrap are not allowed"),
|
||||
}
|
||||
} else {
|
||||
WrappingMode::NoWrapping(true)
|
||||
}
|
||||
} else {
|
||||
// We don't have the tty width when piping to another program.
|
||||
// There's no point in wrapping when this is the case.
|
||||
WrappingMode::NoWrapping(false)
|
||||
},
|
||||
colored_output: self.matches.get_flag("force-colorization")
|
||||
|| match self.matches.get_one::<String>("color").map(|s| s.as_str()) {
|
||||
@@ -277,8 +471,7 @@ impl App {
|
||||
.map(|s| s.as_str())
|
||||
== Some("always")
|
||||
|| self.matches.get_flag("force-colorization")
|
||||
|| self.matches.get_flag("number")
|
||||
|| self.matches.contains_id("style") && !style_components.plain()),
|
||||
|| self.number_from_cli),
|
||||
tab_width: self
|
||||
.matches
|
||||
.get_one::<String>("tabs")
|
||||
@@ -301,6 +494,8 @@ impl App {
|
||||
Some("auto") => StripAnsiMode::Auto,
|
||||
_ => unreachable!("other values for --strip-ansi are not allowed"),
|
||||
},
|
||||
quiet_empty: self.matches.get_flag("quiet-empty"),
|
||||
unbuffered: self.matches.get_flag("unbuffered"),
|
||||
theme: theme(self.theme_options()).to_string(),
|
||||
visible_lines: match self.matches.try_contains_id("diff").unwrap_or_default()
|
||||
&& self.matches.get_flag("diff")
|
||||
@@ -421,7 +616,17 @@ impl App {
|
||||
|
||||
// Plain if `--plain` is specified at least once.
|
||||
if self.matches.get_count("plain") > 0 {
|
||||
return Some(StyleComponents(HashSet::from([StyleComponent::Plain])));
|
||||
let mut components = HashSet::from([StyleComponent::Plain]);
|
||||
// When --diff is active, preserve change markers and snip separators
|
||||
// so that diff output remains visually useful.
|
||||
if self.matches.try_contains_id("diff").unwrap_or_default()
|
||||
&& self.matches.get_flag("diff")
|
||||
{
|
||||
#[cfg(feature = "git")]
|
||||
components.insert(StyleComponent::Changes);
|
||||
components.insert(StyleComponent::Snip);
|
||||
}
|
||||
return Some(StyleComponents(components));
|
||||
}
|
||||
|
||||
// Default behavior.
|
||||
@@ -458,21 +663,27 @@ impl App {
|
||||
bat_warning!("Style 'rule' is a subset of style 'grid', 'rule' will not be visible.");
|
||||
}
|
||||
|
||||
// Auto-disable line numbers in unbuffered mode to avoid confusion with partial lines
|
||||
if self.matches.get_flag("unbuffered") {
|
||||
styled_components.0.remove(&StyleComponent::LineNumbers);
|
||||
}
|
||||
|
||||
Ok(styled_components)
|
||||
}
|
||||
|
||||
fn theme_options(&self) -> ThemeOptions {
|
||||
let theme = self
|
||||
.matches
|
||||
Self::theme_options_from_matches(&self.matches)
|
||||
}
|
||||
|
||||
fn theme_options_from_matches(matches: &ArgMatches) -> ThemeOptions {
|
||||
let theme = matches
|
||||
.get_one::<String>("theme")
|
||||
.map(|t| ThemePreference::from_str(t).unwrap())
|
||||
.unwrap_or_default();
|
||||
let theme_dark = self
|
||||
.matches
|
||||
let theme_dark = matches
|
||||
.get_one::<String>("theme-dark")
|
||||
.map(|t| ThemeName::from_str(t).unwrap());
|
||||
let theme_light = self
|
||||
.matches
|
||||
let theme_light = matches
|
||||
.get_one::<String>("theme-light")
|
||||
.map(|t| ThemeName::from_str(t).unwrap());
|
||||
ThemeOptions {
|
||||
|
||||
+63
-15
@@ -1,7 +1,5 @@
|
||||
use bat::style::StyleComponentList;
|
||||
use clap::{
|
||||
crate_name, crate_version, value_parser, Arg, ArgAction, ArgGroup, ColorChoice, Command,
|
||||
};
|
||||
use clap::{crate_name, crate_version, value_parser, Arg, ArgAction, ColorChoice, Command};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -34,6 +32,8 @@ pub fn build_app(interactive_output: bool) -> Command {
|
||||
.args_conflicts_with_subcommands(true)
|
||||
.allow_external_subcommands(true)
|
||||
.disable_help_subcommand(true)
|
||||
.disable_help_flag(true)
|
||||
.disable_version_flag(true)
|
||||
.max_term_width(100)
|
||||
.about("A cat(1) clone with wings.")
|
||||
.long_about("A cat(1) clone with syntax highlighting and Git integration.")
|
||||
@@ -120,6 +120,17 @@ pub fn build_app(interactive_output: bool) -> Command {
|
||||
language names and file extensions.",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("fallback-syntax")
|
||||
.long("fallback-syntax")
|
||||
.visible_alias("fallback-language")
|
||||
.help("Set a fallback language for undetected syntaxes.")
|
||||
.long_help(
|
||||
"Set a fallback language for syntax highlighting when auto-detection fails. \
|
||||
Unlike '--language', this is only used when no syntax could be detected from \
|
||||
filename, custom syntax mappings, or first-line detection.",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("highlight-line")
|
||||
.long("highlight-line")
|
||||
@@ -211,11 +222,11 @@ pub fn build_app(interactive_output: bool) -> Command {
|
||||
.long("wrap")
|
||||
.overrides_with("wrap")
|
||||
.value_name("mode")
|
||||
.value_parser(["auto", "never", "character"])
|
||||
.value_parser(["auto", "never", "character", "word"])
|
||||
.default_value("auto")
|
||||
.hide_default_value(true)
|
||||
.help("Specify the text-wrapping mode (*auto*, never, character).")
|
||||
.long_help("Specify the text-wrapping mode (*auto*, never, character). \
|
||||
.help("Specify the text-wrapping mode (*auto*, never, character, word).")
|
||||
.long_help("Specify the text-wrapping mode (*auto*, never, character, word). \
|
||||
The '--terminal-width' option can be used in addition to \
|
||||
control the output width."),
|
||||
)
|
||||
@@ -519,6 +530,7 @@ pub fn build_app(interactive_output: bool) -> Command {
|
||||
.short('r')
|
||||
.action(ArgAction::Append)
|
||||
.value_name("N:M")
|
||||
.allow_hyphen_values(true)
|
||||
.help("Only print the lines from N to M.")
|
||||
.long_help(
|
||||
"Only print the specified range of lines for each file. \
|
||||
@@ -527,6 +539,7 @@ pub fn build_app(interactive_output: bool) -> Command {
|
||||
'--line-range :40' prints lines 1 to 40\n \
|
||||
'--line-range 40:' prints lines 40 to the end of the file\n \
|
||||
'--line-range 40' only prints line 40\n \
|
||||
'--line-range -10:' prints the last 10 lines\n \
|
||||
'--line-range 30:+10' prints lines 30 to 40\n \
|
||||
'--line-range 35::5' prints lines 30 to 40 (line 35 with 5 lines of context)\n \
|
||||
'--line-range 30:40:2' prints lines 28 to 42 (range 30-40 with 2 lines of context)",
|
||||
@@ -546,11 +559,14 @@ pub fn build_app(interactive_output: bool) -> Command {
|
||||
.short('u')
|
||||
.long("unbuffered")
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide_short_help(true)
|
||||
.help("Enable unbuffered input reading for streaming use cases.")
|
||||
.long_help(
|
||||
"This option exists for POSIX-compliance reasons ('u' is for \
|
||||
'unbuffered'). The output is always unbuffered - this option \
|
||||
is simply ignored.",
|
||||
"Enable unbuffered input reading. When this flag is set, bat will \
|
||||
display data as soon as it is available, without waiting for a \
|
||||
complete line. This is useful for streaming use cases like \
|
||||
'tail -f logfile | bat -u --paging=never'. Note that line numbers \
|
||||
are automatically disabled in unbuffered mode, and syntax \
|
||||
highlighting may be imperfect on partial lines.",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
@@ -639,6 +655,18 @@ pub fn build_app(interactive_output: bool) -> Command {
|
||||
.hide_short_help(true)
|
||||
.help("Show diagnostic information for bug reports."),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("quiet-empty")
|
||||
.long("quiet-empty")
|
||||
.short('E')
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Produce no output when the input is empty.")
|
||||
.long_help(
|
||||
"When this flag is set, bat will produce no output at all when \
|
||||
the input is empty. This is useful when piping commands that may \
|
||||
produce empty output, like 'git diff'.",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("acknowledgements")
|
||||
.long("acknowledgements")
|
||||
@@ -652,6 +680,21 @@ pub fn build_app(interactive_output: bool) -> Command {
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide_short_help(true)
|
||||
.help("Sets terminal title to filenames when using a pager."),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("help")
|
||||
.short('h')
|
||||
.long("help")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Print help (see more with '--help')")
|
||||
.long_help("Print help (see a summary with '-h')"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("version")
|
||||
.long("version")
|
||||
.short('V')
|
||||
.action(ArgAction::Version)
|
||||
.help("Print version"),
|
||||
);
|
||||
|
||||
// Check if the current directory contains a file name cache. Otherwise,
|
||||
@@ -663,11 +706,20 @@ pub fn build_app(interactive_output: bool) -> Command {
|
||||
Command::new("cache")
|
||||
.hide(true)
|
||||
.about("Modify the syntax-definition and theme cache")
|
||||
.arg_required_else_help(true)
|
||||
.arg(
|
||||
Arg::new("help")
|
||||
.short('h')
|
||||
.long("help")
|
||||
.action(ArgAction::Help)
|
||||
.help("Print help"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("build")
|
||||
.long("build")
|
||||
.short('b')
|
||||
.action(ArgAction::SetTrue)
|
||||
.conflicts_with("clear")
|
||||
.help("Initialize (or update) the syntax/theme cache.")
|
||||
.long_help(
|
||||
"Initialize (or update) the syntax/theme cache by loading from \
|
||||
@@ -679,13 +731,9 @@ pub fn build_app(interactive_output: bool) -> Command {
|
||||
.long("clear")
|
||||
.short('c')
|
||||
.action(ArgAction::SetTrue)
|
||||
.conflicts_with("build")
|
||||
.help("Remove the cached syntax definitions and themes."),
|
||||
)
|
||||
.group(
|
||||
ArgGroup::new("cache-actions")
|
||||
.args(["build", "clear"])
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("source")
|
||||
.long("source")
|
||||
|
||||
+55
-4
@@ -2,7 +2,7 @@ use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::io::{self, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::directories::PROJECT_DIRS;
|
||||
|
||||
@@ -104,18 +104,32 @@ pub fn generate_config_file() -> bat::error::Result<()> {
|
||||
pub fn get_args_from_config_file() -> Result<Vec<OsString>, shell_words::ParseError> {
|
||||
let mut config = String::new();
|
||||
|
||||
if let Ok(c) = fs::read_to_string(system_config_file()) {
|
||||
let system_config = system_config_file();
|
||||
let user_config = config_file();
|
||||
|
||||
if let Ok(c) = fs::read_to_string(&system_config) {
|
||||
config.push_str(&c);
|
||||
config.push('\n');
|
||||
}
|
||||
|
||||
if let Ok(c) = fs::read_to_string(config_file()) {
|
||||
config.push_str(&c);
|
||||
// Skip the user config if it resolves to the same file as the system config,
|
||||
// which can happen when BAT_CONFIG_DIR is set to e.g. "/etc/bat". See #3589.
|
||||
if !same_file(&system_config, &user_config) {
|
||||
if let Ok(c) = fs::read_to_string(&user_config) {
|
||||
config.push_str(&c);
|
||||
}
|
||||
}
|
||||
|
||||
get_args_from_str(&config)
|
||||
}
|
||||
|
||||
fn same_file(a: &Path, b: &Path) -> bool {
|
||||
match (fs::canonicalize(a), fs::canonicalize(b)) {
|
||||
(Ok(a), Ok(b)) => a == b,
|
||||
_ => a == b,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_args_from_env_opts_var() -> Option<Result<Vec<OsString>, shell_words::ParseError>> {
|
||||
env::var("BAT_OPTS").ok().map(|s| get_args_from_str(&s))
|
||||
}
|
||||
@@ -214,3 +228,40 @@ fn comments() {
|
||||
get_args_from_str(config).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_file_identical_paths() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let file = dir.path().join("config");
|
||||
fs::write(&file, "").unwrap();
|
||||
assert!(same_file(&file, &file));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_file_different_paths() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let a = dir.path().join("a");
|
||||
let b = dir.path().join("b");
|
||||
fs::write(&a, "").unwrap();
|
||||
fs::write(&b, "").unwrap();
|
||||
assert!(!same_file(&a, &b));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_file_nonexistent() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let a = dir.path().join("a");
|
||||
let b = dir.path().join("b");
|
||||
assert!(!same_file(&a, &b));
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn same_file_via_symlink() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let original = dir.path().join("config");
|
||||
let link = dir.path().join("link");
|
||||
fs::write(&original, "").unwrap();
|
||||
std::os::unix::fs::symlink(&original, &link).unwrap();
|
||||
assert!(same_file(&original, &link));
|
||||
}
|
||||
|
||||
+19
-16
@@ -16,7 +16,7 @@ use std::io::{BufReader, Write};
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
|
||||
use bat::output::OutputType;
|
||||
use bat::output::{OutputHandle, OutputType};
|
||||
use bat::theme::DetectColorScheme;
|
||||
use nu_ansi_term::Color::Green;
|
||||
use nu_ansi_term::Style;
|
||||
@@ -206,11 +206,10 @@ pub fn list_themes(
|
||||
config.language = Some("Rust");
|
||||
config.style_components = StyleComponents(style);
|
||||
|
||||
let mut output_type =
|
||||
OutputType::from_mode(config.paging_mode, config.wrapping_mode, config.pager)?;
|
||||
let mut writer = output_type.handle()?;
|
||||
|
||||
let default_theme_name = default_theme(color_scheme(detect_color_scheme).unwrap_or_default());
|
||||
let mut buf = String::new();
|
||||
let mut handle = OutputHandle::FmtWrite(&mut buf);
|
||||
|
||||
for theme in assets.themes() {
|
||||
let default_theme_info = if default_theme_name == theme {
|
||||
" (default)"
|
||||
@@ -221,35 +220,39 @@ pub fn list_themes(
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
if config.colored_output {
|
||||
writeln!(
|
||||
writer,
|
||||
"Theme: {}{default_theme_info}\n",
|
||||
handle.write_fmt(format_args!(
|
||||
"{}{default_theme_info}\n\n",
|
||||
Style::new().bold().paint(theme.to_string()),
|
||||
)?;
|
||||
))?;
|
||||
config.theme = theme.to_string();
|
||||
Controller::new(&config, &assets)
|
||||
.run(vec![theme_preview_file()], Some(&mut writer))
|
||||
.run(vec![theme_preview_file()], Some(&mut handle))
|
||||
.ok();
|
||||
writeln!(writer)?;
|
||||
handle.write_fmt(format_args!("\n"))?;
|
||||
} else if config.loop_through {
|
||||
writeln!(writer, "{theme}")?;
|
||||
handle.write_fmt(format_args!("{theme}\n"))?;
|
||||
} else {
|
||||
writeln!(writer, "{theme}{default_theme_info}")?;
|
||||
handle.write_fmt(format_args!("{theme}{default_theme_info}\n"))?;
|
||||
}
|
||||
}
|
||||
|
||||
if config.colored_output {
|
||||
writeln!(
|
||||
writer,
|
||||
handle.write_fmt(format_args!(
|
||||
"Further themes can be installed to '{}', \
|
||||
and are added to the cache with `bat cache --build`. \
|
||||
For more information, see:\n\n \
|
||||
https://github.com/sharkdp/bat#adding-new-themes",
|
||||
config_dir.join("themes").to_string_lossy()
|
||||
)?;
|
||||
))?;
|
||||
}
|
||||
|
||||
let mut output_type =
|
||||
OutputType::from_mode(config.paging_mode, config.wrapping_mode, config.pager)?;
|
||||
let mut writer = output_type.handle()?;
|
||||
writer.write_fmt(format_args!("{buf}"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
+95
-3
@@ -38,6 +38,9 @@ pub struct Config<'a> {
|
||||
/// The explicitly configured language, if any
|
||||
pub language: Option<&'a str>,
|
||||
|
||||
/// The fallback syntax used when auto-detection fails
|
||||
pub fallback_syntax: Option<&'a str>,
|
||||
|
||||
/// Whether or not to show/replace non-printable characters like space, tab and newline.
|
||||
pub show_nonprintable: bool,
|
||||
|
||||
@@ -99,14 +102,20 @@ pub struct Config<'a> {
|
||||
#[cfg(feature = "lessopen")]
|
||||
pub use_lessopen: bool,
|
||||
|
||||
// Weather or not to set terminal title when using a pager
|
||||
// Whether or not to set terminal title when using a pager
|
||||
pub set_terminal_title: bool,
|
||||
|
||||
/// The maximum number of consecutive empty lines to display
|
||||
pub squeeze_lines: Option<usize>,
|
||||
|
||||
// Weather or not to set terminal title when using a pager
|
||||
// Whether or not to strip ANSI escape codes from the input
|
||||
pub strip_ansi: StripAnsiMode,
|
||||
|
||||
/// Whether or not to produce no output when input is empty
|
||||
pub quiet_empty: bool,
|
||||
|
||||
/// Whether or not to use unbuffered input reading for streaming use cases
|
||||
pub unbuffered: bool,
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "minimal-application", feature = "paging"))]
|
||||
@@ -114,7 +123,13 @@ pub fn get_pager_executable(config_pager: Option<&str>) -> Option<String> {
|
||||
crate::pager::get_pager(config_pager)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|pager| pager.bin)
|
||||
.and_then(|pager| {
|
||||
if pager.kind != crate::pager::PagerKind::Builtin {
|
||||
Some(pager.bin)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -141,3 +156,80 @@ fn default_config_should_highlight_no_lines() {
|
||||
RangeCheckResult::InRange
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "minimal-application", feature = "paging"))]
|
||||
#[test]
|
||||
fn get_pager_executable_with_config_pager_less() {
|
||||
let result = get_pager_executable(Some("less"));
|
||||
assert_eq!(result, Some("less".to_string()));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "minimal-application", feature = "paging"))]
|
||||
#[test]
|
||||
fn get_pager_executable_with_config_pager_builtin() {
|
||||
let result = get_pager_executable(Some("builtin"));
|
||||
assert_eq!(result, None);
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "minimal-application", feature = "paging"))]
|
||||
#[test]
|
||||
fn get_pager_executable_with_config_pager_more() {
|
||||
let result = get_pager_executable(Some("more"));
|
||||
assert_eq!(result, Some("more".to_string()));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "minimal-application", feature = "paging"))]
|
||||
#[test]
|
||||
fn get_pager_executable_with_bat_pager() {
|
||||
std::env::set_var("BAT_PAGER", "most");
|
||||
let result = get_pager_executable(None);
|
||||
assert_eq!(result, Some("most".to_string()));
|
||||
std::env::remove_var("BAT_PAGER");
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "minimal-application", feature = "paging"))]
|
||||
#[test]
|
||||
fn get_pager_executable_with_pager_more_switches_to_less() {
|
||||
std::env::set_var("PAGER", "more");
|
||||
let result = get_pager_executable(None);
|
||||
assert_eq!(result, Some("less".to_string()));
|
||||
std::env::remove_var("PAGER");
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "minimal-application", feature = "paging"))]
|
||||
#[test]
|
||||
fn get_pager_executable_default() {
|
||||
// Ensure no env vars
|
||||
std::env::remove_var("BAT_PAGER");
|
||||
std::env::remove_var("PAGER");
|
||||
let result = get_pager_executable(None);
|
||||
assert_eq!(result, Some("less".to_string()));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "minimal-application", feature = "paging"))]
|
||||
#[test]
|
||||
fn get_pager_executable_name_ignoring_arguments() {
|
||||
let result = get_pager_executable(Some("foo --bar"));
|
||||
assert_eq!(result, Some("foo".to_string()));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "minimal-application", feature = "paging"))]
|
||||
#[test]
|
||||
fn get_pager_executable_name_ignoring_path() {
|
||||
let result = get_pager_executable(Some("/bin/foo test"));
|
||||
assert_eq!(result, Some("/bin/foo".to_string()));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "minimal-application", feature = "paging"))]
|
||||
#[test]
|
||||
fn get_pager_executable_invalid_command() {
|
||||
let result = get_pager_executable(Some("invalid ' command"));
|
||||
assert_eq!(result, None);
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "minimal-application", feature = "paging"))]
|
||||
#[test]
|
||||
fn get_pager_executable_empty_config() {
|
||||
let result = get_pager_executable(Some(""));
|
||||
assert_eq!(result, None);
|
||||
}
|
||||
|
||||
+26
-10
@@ -50,10 +50,12 @@ impl Controller<'_> {
|
||||
output_handle: Option<&mut OutputHandle<'_>>,
|
||||
mut handle_error: impl FnMut(&Error, &mut dyn Write),
|
||||
) -> Result<bool> {
|
||||
let mut output_type;
|
||||
// only create our own OutputType if no output handle was provided.
|
||||
#[allow(unused_mut)]
|
||||
let mut output_type_opt: Option<OutputType> = None;
|
||||
|
||||
#[cfg(feature = "paging")]
|
||||
{
|
||||
if output_handle.is_none() {
|
||||
use crate::input::InputKind;
|
||||
use std::path::Path;
|
||||
|
||||
@@ -74,25 +76,35 @@ impl Controller<'_> {
|
||||
|
||||
let wrapping_mode = self.config.wrapping_mode;
|
||||
|
||||
output_type = OutputType::from_mode(paging_mode, wrapping_mode, self.config.pager)?;
|
||||
output_type_opt = Some(OutputType::from_mode(
|
||||
paging_mode,
|
||||
wrapping_mode,
|
||||
self.config.pager,
|
||||
)?);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "paging"))]
|
||||
{
|
||||
output_type = OutputType::stdout();
|
||||
if output_handle.is_none() {
|
||||
output_type_opt = Some(OutputType::stdout());
|
||||
}
|
||||
|
||||
let attached_to_pager = output_type.is_pager();
|
||||
let attached_to_pager = match (&output_handle, &output_type_opt) {
|
||||
(Some(_), _) => true,
|
||||
(None, Some(ot)) => ot.is_pager(),
|
||||
(None, None) => false,
|
||||
};
|
||||
|
||||
let stdout_identifier = if cfg!(windows) || attached_to_pager {
|
||||
None
|
||||
} else {
|
||||
clircle::Identifier::stdout()
|
||||
};
|
||||
|
||||
let mut writer = match output_handle {
|
||||
Some(OutputHandle::FmtWrite(w)) => OutputHandle::FmtWrite(w),
|
||||
Some(OutputHandle::IoWrite(w)) => OutputHandle::IoWrite(w),
|
||||
None => output_type.handle()?,
|
||||
let mut writer = match (output_handle, &mut output_type_opt) {
|
||||
(Some(OutputHandle::FmtWrite(w)), _) => OutputHandle::FmtWrite(w),
|
||||
(Some(OutputHandle::IoWrite(w)), _) => OutputHandle::IoWrite(w),
|
||||
(None, Some(ot)) => ot.handle()?,
|
||||
(None, None) => unreachable!("No output handle and no output type available"),
|
||||
};
|
||||
let mut no_errors: bool = true;
|
||||
let stderr = io::stderr();
|
||||
@@ -146,6 +158,7 @@ impl Controller<'_> {
|
||||
#[cfg(not(feature = "lessopen"))]
|
||||
input.open(stdin, stdout_identifier)?
|
||||
};
|
||||
opened_input.reader.unbuffered = self.config.unbuffered;
|
||||
#[cfg(feature = "git")]
|
||||
let line_changes = if self.config.visible_lines.diff_mode()
|
||||
|| (!self.config.loop_through && self.config.style_components.changes())
|
||||
@@ -315,6 +328,9 @@ impl Controller<'_> {
|
||||
}
|
||||
|
||||
printer.print_line(false, writer, line_nr, &line, max_buffered_line_number)?;
|
||||
if self.config.unbuffered {
|
||||
writer.flush()?;
|
||||
}
|
||||
}
|
||||
RangeCheckResult::AfterLastRange => {
|
||||
break;
|
||||
|
||||
+105
@@ -253,6 +253,7 @@ pub(crate) struct InputReader<'a> {
|
||||
inner: Box<dyn BufRead + 'a>,
|
||||
pub(crate) first_line: Vec<u8>,
|
||||
pub(crate) content_type: Option<ContentType>,
|
||||
pub(crate) unbuffered: bool,
|
||||
}
|
||||
|
||||
impl<'a> InputReader<'a> {
|
||||
@@ -276,6 +277,7 @@ impl<'a> InputReader<'a> {
|
||||
inner: Box::new(reader),
|
||||
first_line,
|
||||
content_type,
|
||||
unbuffered: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,9 +294,29 @@ impl<'a> InputReader<'a> {
|
||||
return read_utf16_line(&mut self.inner, buf, 0x0A, 0x00);
|
||||
}
|
||||
|
||||
if self.unbuffered {
|
||||
return self.read_line_unbuffered(buf);
|
||||
}
|
||||
|
||||
let res = self.inner.read_until(b'\n', buf).map(|size| size > 0)?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn read_line_unbuffered(&mut self, buf: &mut Vec<u8>) -> io::Result<bool> {
|
||||
let available = self.inner.fill_buf()?;
|
||||
if available.is_empty() {
|
||||
return Ok(!buf.is_empty());
|
||||
}
|
||||
if let Some(pos) = available.iter().position(|&b| b == b'\n') {
|
||||
buf.extend_from_slice(&available[..=pos]);
|
||||
self.inner.consume(pos + 1);
|
||||
} else {
|
||||
let len = available.len();
|
||||
buf.extend_from_slice(available);
|
||||
self.inner.consume(len);
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
fn read_utf16_line<R: BufRead>(
|
||||
@@ -381,6 +403,89 @@ fn utf16le() {
|
||||
assert!(buffer.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unbuffered_returns_partial_data() {
|
||||
use std::io::Cursor;
|
||||
|
||||
let content = b"first line\npartial";
|
||||
let mut reader = InputReader::new(Cursor::new(&content[..]));
|
||||
reader.unbuffered = true;
|
||||
|
||||
// First call returns first_line (buffered during new())
|
||||
let mut buffer = vec![];
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert!(res.unwrap());
|
||||
assert_eq!(b"first line\n", &buffer[..]);
|
||||
|
||||
// Subsequent calls use unbuffered reading
|
||||
buffer.clear();
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert!(res.unwrap());
|
||||
assert_eq!(b"partial", &buffer[..]);
|
||||
|
||||
// EOF
|
||||
buffer.clear();
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert!(!res.unwrap());
|
||||
assert!(buffer.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unbuffered_returns_complete_lines() {
|
||||
use std::io::Cursor;
|
||||
|
||||
let content = b"line1\nline2\n";
|
||||
let mut reader = InputReader::new(Cursor::new(&content[..]));
|
||||
reader.unbuffered = true;
|
||||
|
||||
// First call returns first_line
|
||||
let mut buffer = vec![];
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert!(res.unwrap());
|
||||
assert_eq!(b"line1\n", &buffer[..]);
|
||||
|
||||
// Second call returns line2 (complete line with newline)
|
||||
buffer.clear();
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert!(res.unwrap());
|
||||
assert_eq!(b"line2\n", &buffer[..]);
|
||||
|
||||
// EOF
|
||||
buffer.clear();
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert!(!res.unwrap());
|
||||
assert!(buffer.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unbuffered_eof_handling() {
|
||||
use std::io::Cursor;
|
||||
|
||||
let content = b"only line\n";
|
||||
let mut reader = InputReader::new(Cursor::new(&content[..]));
|
||||
reader.unbuffered = true;
|
||||
|
||||
// First call returns first_line
|
||||
let mut buffer = vec![];
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert!(res.unwrap());
|
||||
assert_eq!(b"only line\n", &buffer[..]);
|
||||
|
||||
// EOF - empty buffer returns false
|
||||
buffer.clear();
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert!(!res.unwrap());
|
||||
assert!(buffer.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn utf16le_issue3367() {
|
||||
let content = b"\xFF\xFE\x0A\x4E\x00\x4E\x0A\x4F\x00\x52\x0A\x00\
|
||||
|
||||
+49
-13
@@ -23,6 +23,18 @@ pub struct BuiltinPager {
|
||||
impl BuiltinPager {
|
||||
fn new() -> Self {
|
||||
let pager = minus::Pager::new();
|
||||
|
||||
let mut input_register = minus::input::HashedEventRegister::default();
|
||||
input_register.add_key_events(&["home"], |_, _| {
|
||||
minus::input::InputEvent::UpdateUpperMark(0)
|
||||
});
|
||||
input_register.add_key_events(&["end"], |_, _| {
|
||||
minus::input::InputEvent::UpdateUpperMark(usize::MAX)
|
||||
});
|
||||
pager
|
||||
.set_input_classifier(Box::new(input_register))
|
||||
.expect("failed to set input classifier on newly created pager");
|
||||
|
||||
let handle = {
|
||||
let pager = pager.clone();
|
||||
Some(spawn(move || {
|
||||
@@ -105,6 +117,10 @@ impl OutputType {
|
||||
let resolved_path = match grep_cli::resolve_binary(&pager.bin) {
|
||||
Ok(path) => path,
|
||||
Err(_) => {
|
||||
crate::bat_warning!(
|
||||
"Pager '{}' not found, outputting to stdout instead",
|
||||
pager.bin
|
||||
);
|
||||
return Ok(OutputType::stdout());
|
||||
}
|
||||
};
|
||||
@@ -131,8 +147,13 @@ impl OutputType {
|
||||
p.arg("-S"); // Short version of --chop-long-lines for compatibility
|
||||
}
|
||||
|
||||
let less_version = retrieve_less_version(&pager.bin);
|
||||
|
||||
// Ensures that 'less' quits together with 'bat'
|
||||
p.arg("-K"); // Short version of '--quit-on-intr'
|
||||
// The BusyBox version of less does not support -K
|
||||
if less_version != Some(LessVersion::BusyBox) {
|
||||
p.arg("-K"); // Short version of '--quit-on-intr'
|
||||
}
|
||||
|
||||
// Passing '--no-init' fixes a bug with '--quit-if-one-screen' in older
|
||||
// versions of 'less'. Unfortunately, it also breaks mouse-wheel support.
|
||||
@@ -141,16 +162,18 @@ impl OutputType {
|
||||
//
|
||||
// For newer versions (530 or 558 on Windows), we omit '--no-init' as it
|
||||
// is not needed anymore.
|
||||
match retrieve_less_version(&pager.bin) {
|
||||
None => {
|
||||
p.arg("--no-init");
|
||||
if single_screen_action == SingleScreenAction::Quit {
|
||||
match less_version {
|
||||
None => {
|
||||
p.arg("--no-init");
|
||||
}
|
||||
Some(LessVersion::Less(version))
|
||||
if (version < 530 || (cfg!(windows) && version < 558)) =>
|
||||
{
|
||||
p.arg("--no-init");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Some(LessVersion::Less(version))
|
||||
if (version < 530 || (cfg!(windows) && version < 558)) =>
|
||||
{
|
||||
p.arg("--no-init");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
p.args(args);
|
||||
@@ -167,7 +190,13 @@ impl OutputType {
|
||||
Ok(p.stdin(Stdio::piped())
|
||||
.spawn()
|
||||
.map(OutputType::Pager)
|
||||
.unwrap_or_else(|_| OutputType::stdout()))
|
||||
.unwrap_or_else(|_| {
|
||||
crate::bat_warning!(
|
||||
"Pager '{}' not found, outputting to stdout instead",
|
||||
&pager.bin
|
||||
);
|
||||
OutputType::stdout()
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn stdout() -> Self {
|
||||
@@ -208,8 +237,8 @@ impl Drop for OutputType {
|
||||
let _ = command.wait();
|
||||
}
|
||||
OutputType::BuiltinPager(ref mut pager) => {
|
||||
if pager.handle.is_some() {
|
||||
let _ = pager.handle.take().unwrap().join().unwrap();
|
||||
if let Some(handle) = pager.handle.take() {
|
||||
let _ = handle.join();
|
||||
}
|
||||
}
|
||||
OutputType::Stdout(_) => (),
|
||||
@@ -229,4 +258,11 @@ impl OutputHandle<'_> {
|
||||
Self::FmtWrite(handle) => handle.write_fmt(args).map_err(Into::into),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flush(&mut self) -> Result<()> {
|
||||
match self {
|
||||
Self::IoWrite(handle) => handle.flush().map_err(Into::into),
|
||||
Self::FmtWrite(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,35 @@ pub fn strip_ansi(line: &str) -> String {
|
||||
buffer
|
||||
}
|
||||
|
||||
/// Strips overstrike sequences (backspace formatting) from input.
|
||||
///
|
||||
/// Overstrike formatting is used by man pages and some help output:
|
||||
/// - Bold: `X\x08X` (character, backspace, same character)
|
||||
/// - Underline: `_\x08X` (underscore, backspace, character)
|
||||
///
|
||||
/// This function removes these sequences, keeping only the visible character.
|
||||
/// `first_backspace` is the position of the first backspace in the line.
|
||||
pub fn strip_overstrike(line: &str, first_backspace: usize) -> String {
|
||||
let mut output = String::with_capacity(line.len());
|
||||
output.push_str(&line[..first_backspace]);
|
||||
output.pop();
|
||||
|
||||
let mut remaining = &line[first_backspace + 1..];
|
||||
|
||||
loop {
|
||||
if let Some(pos) = remaining.find('\x08') {
|
||||
output.push_str(&remaining[..pos]);
|
||||
output.pop();
|
||||
remaining = &remaining[pos + 1..];
|
||||
} else {
|
||||
output.push_str(remaining);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy, Default)]
|
||||
pub enum StripAnsiMode {
|
||||
#[default]
|
||||
@@ -211,3 +240,24 @@ fn test_strip_ansi() {
|
||||
"multiple sequences"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_strip_overstrike() {
|
||||
// Bold: X\x08X (same char repeated)
|
||||
assert_eq!(strip_overstrike("H\x08Hello", 1), "Hello");
|
||||
|
||||
// Underline: _\x08X (underscore before char)
|
||||
assert_eq!(strip_overstrike("_\x08Hello", 1), "Hello");
|
||||
|
||||
// Multiple overstrike sequences
|
||||
assert_eq!(strip_overstrike("B\x08Bo\x08ol\x08ld\x08d", 1), "Bold");
|
||||
|
||||
// Backspace at start of line (nothing to pop)
|
||||
assert_eq!(strip_overstrike("\x08Hello", 0), "Hello");
|
||||
|
||||
// Multiple consecutive backspaces
|
||||
assert_eq!(strip_overstrike("ABC\x08\x08\x08XYZ", 3), "XYZ");
|
||||
|
||||
// Unicode with overstrike
|
||||
assert_eq!(strip_overstrike("ä\x08äöü", 2), "äöü");
|
||||
}
|
||||
|
||||
+91
-9
@@ -29,8 +29,7 @@ use crate::error::*;
|
||||
use crate::input::OpenedInput;
|
||||
use crate::line_range::{MaxBufferedLineNumber, RangeCheckResult};
|
||||
use crate::output::OutputHandle;
|
||||
use crate::preprocessor::strip_ansi;
|
||||
use crate::preprocessor::{expand_tabs, replace_nonprintable};
|
||||
use crate::preprocessor::{expand_tabs, replace_nonprintable, strip_ansi, strip_overstrike};
|
||||
use crate::style::StyleComponent;
|
||||
use crate::terminal::{as_terminal_escaped, to_ansi_color};
|
||||
use crate::vscreen::{AnsiStyle, EscapeSequence, EscapeSequenceIterator};
|
||||
@@ -38,6 +37,16 @@ use crate::wrapping::WrappingMode;
|
||||
use crate::BinaryBehavior;
|
||||
use crate::StripAnsiMode;
|
||||
|
||||
// Return the displayed width of a character.
|
||||
//
|
||||
// Control characters (0x00..=0x1F and 0x7F) are rendered by the terminal
|
||||
// in caret notation (e.g. ^@, ^A, ..., ^?), which occupies two columns.
|
||||
// UnicodeWidthChar::width() returns None for these, so we map them to 2
|
||||
// here instead of the previous default of 0.
|
||||
fn char_width(c: char) -> usize {
|
||||
c.width().unwrap_or(if c.is_control() { 2 } else { 0 })
|
||||
}
|
||||
|
||||
const ANSI_UNDERLINE_ENABLE: EscapeSequence = EscapeSequence::CSI {
|
||||
raw_sequence: "\x1B[4m",
|
||||
parameters: "4",
|
||||
@@ -199,6 +208,7 @@ pub(crate) struct InteractivePrinter<'a> {
|
||||
background_color_highlight: Option<Color>,
|
||||
consecutive_empty_lines: usize,
|
||||
strip_ansi: bool,
|
||||
strip_overstrike: bool,
|
||||
}
|
||||
|
||||
impl<'a> InteractivePrinter<'a> {
|
||||
@@ -263,17 +273,27 @@ impl<'a> InteractivePrinter<'a> {
|
||||
|| matches!(config.binary, BinaryBehavior::AsText))
|
||||
&& (config.colored_output || config.strip_ansi == StripAnsiMode::Auto);
|
||||
|
||||
let (is_plain_text, highlighter_from_set) = if needs_to_match_syntax {
|
||||
let (is_plain_text, strip_overstrike, highlighter_from_set) = if needs_to_match_syntax {
|
||||
// Determine the type of syntax for highlighting
|
||||
const PLAIN_TEXT_SYNTAX: &str = "Plain Text";
|
||||
match assets.get_syntax(config.language, input, &config.syntax_mapping) {
|
||||
const MANPAGE_SYNTAX: &str = "Manpage";
|
||||
const COMMAND_HELP_SYNTAX: &str = "Command Help";
|
||||
match assets.get_syntax(
|
||||
config.language,
|
||||
config.fallback_syntax,
|
||||
input,
|
||||
&config.syntax_mapping,
|
||||
) {
|
||||
Ok(syntax_in_set) => (
|
||||
syntax_in_set.syntax.name == PLAIN_TEXT_SYNTAX,
|
||||
syntax_in_set.syntax.name == MANPAGE_SYNTAX
|
||||
|| syntax_in_set.syntax.name == COMMAND_HELP_SYNTAX,
|
||||
Some(HighlighterFromSet::new(syntax_in_set, theme)),
|
||||
),
|
||||
|
||||
Err(Error::UndetectedSyntax(_)) => (
|
||||
true,
|
||||
false,
|
||||
Some(
|
||||
assets
|
||||
.find_syntax_by_name(PLAIN_TEXT_SYNTAX)?
|
||||
@@ -285,7 +305,7 @@ impl<'a> InteractivePrinter<'a> {
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
} else {
|
||||
(false, None)
|
||||
(false, false, None)
|
||||
};
|
||||
|
||||
// Determine when to strip ANSI sequences
|
||||
@@ -310,6 +330,7 @@ impl<'a> InteractivePrinter<'a> {
|
||||
background_color_highlight,
|
||||
consecutive_empty_lines: 0,
|
||||
strip_ansi,
|
||||
strip_overstrike,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -448,6 +469,11 @@ impl Printer for InteractivePrinter<'_> {
|
||||
input: &OpenedInput,
|
||||
add_header_padding: bool,
|
||||
) -> Result<()> {
|
||||
// If input is empty and quiet_empty is enabled, skip all output
|
||||
if self.content_type.is_none() && self.config.quiet_empty {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if add_header_padding && self.config.style_components.rule() {
|
||||
self.print_horizontal_line_term(handle, self.colors.rule)?;
|
||||
}
|
||||
@@ -550,6 +576,11 @@ impl Printer for InteractivePrinter<'_> {
|
||||
}
|
||||
|
||||
fn print_footer(&mut self, handle: &mut OutputHandle, _input: &OpenedInput) -> Result<()> {
|
||||
// If input is empty and quiet_empty is enabled, skip footer
|
||||
if self.content_type.is_none() && self.config.quiet_empty {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if self.config.style_components.grid()
|
||||
&& (self.content_type.is_some_and(|c| c.is_text())
|
||||
|| self.config.show_nonprintable
|
||||
@@ -622,6 +653,12 @@ impl Printer for InteractivePrinter<'_> {
|
||||
}
|
||||
};
|
||||
|
||||
if self.strip_overstrike {
|
||||
if let Some(pos) = line.find('\x08') {
|
||||
line = strip_overstrike(&line, pos).into();
|
||||
}
|
||||
}
|
||||
|
||||
// If ANSI escape sequences are supposed to be stripped, do it before syntax highlighting.
|
||||
if self.strip_ansi {
|
||||
line = strip_ansi(&line).into()
|
||||
@@ -759,11 +796,21 @@ impl Printer for InteractivePrinter<'_> {
|
||||
// Displayed width of line_buf
|
||||
let mut current_width = 0;
|
||||
|
||||
let word_wrap = matches!(self.config.wrapping_mode, WrappingMode::Word);
|
||||
|
||||
// For word wrapping, track last whitespace position.
|
||||
let mut last_ws_idx: Option<usize> = None;
|
||||
|
||||
for c in text.chars() {
|
||||
// calculate the displayed width for next character
|
||||
let cw = c.width().unwrap_or(0);
|
||||
let cw = char_width(c);
|
||||
current_width += cw;
|
||||
|
||||
// Track whitespace positions for word wrapping.
|
||||
if word_wrap && c.is_whitespace() {
|
||||
last_ws_idx = Some(line_buf.len());
|
||||
}
|
||||
|
||||
// if next character cannot be printed on this line,
|
||||
// flush the buffer.
|
||||
if current_width > max_width {
|
||||
@@ -785,13 +832,37 @@ impl Printer for InteractivePrinter<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the break point and remainder
|
||||
// for word wrapping.
|
||||
let (emit_end, rest_start) = if word_wrap {
|
||||
if let Some(ws_idx) = last_ws_idx {
|
||||
// Skip the whitespace character itself
|
||||
// and carry the rest to the next line.
|
||||
let rs = ws_idx
|
||||
+ line_buf[ws_idx..]
|
||||
.chars()
|
||||
.next()
|
||||
.map(|ch| ch.len_utf8())
|
||||
.unwrap_or(0);
|
||||
(ws_idx, Some(rs))
|
||||
} else {
|
||||
(line_buf.len(), None)
|
||||
}
|
||||
} else {
|
||||
(line_buf.len(), None)
|
||||
};
|
||||
|
||||
// It wraps.
|
||||
write!(
|
||||
handle,
|
||||
"{}{}\n{}",
|
||||
as_terminal_escaped(
|
||||
style,
|
||||
&format!("{}{line_buf}", self.ansi_style),
|
||||
&format!(
|
||||
"{}{}",
|
||||
self.ansi_style,
|
||||
&line_buf[..emit_end]
|
||||
),
|
||||
self.config.true_color,
|
||||
self.config.colored_output,
|
||||
self.config.use_italic_text,
|
||||
@@ -804,8 +875,19 @@ impl Printer for InteractivePrinter<'_> {
|
||||
cursor = 0;
|
||||
max_width = cursor_max;
|
||||
|
||||
line_buf.clear();
|
||||
current_width = cw;
|
||||
if let Some(rs) = rest_start {
|
||||
// Word wrap: carry remainder to next line.
|
||||
let remainder = line_buf[rs..].to_string();
|
||||
let rem_width: usize =
|
||||
remainder.chars().map(char_width).sum();
|
||||
line_buf.clear();
|
||||
line_buf.push_str(&remainder);
|
||||
current_width = rem_width + cw;
|
||||
} else {
|
||||
line_buf.clear();
|
||||
current_width = cw;
|
||||
}
|
||||
last_ws_idx = None;
|
||||
}
|
||||
|
||||
line_buf.push(c);
|
||||
|
||||
+54
-3
@@ -17,9 +17,16 @@ use ignored_suffixes::IgnoredSuffixes;
|
||||
mod builtin;
|
||||
pub mod ignored_suffixes;
|
||||
|
||||
fn make_glob_matcher(from: &str) -> Result<GlobMatcher> {
|
||||
/// Whether a glob pattern should be matched case-sensitively or case-insensitively.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum Case {
|
||||
Sensitive,
|
||||
Insensitive,
|
||||
}
|
||||
|
||||
fn make_glob_matcher(from: &str, case: Case) -> Result<GlobMatcher> {
|
||||
let matcher = GlobBuilder::new(from)
|
||||
.case_insensitive(true)
|
||||
.case_insensitive(matches!(case, Case::Insensitive))
|
||||
.literal_separator(true)
|
||||
.build()?
|
||||
.compile_matcher();
|
||||
@@ -97,7 +104,14 @@ impl<'a> SyntaxMapping<'a> {
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, from: &str, to: MappingTarget<'a>) -> Result<()> {
|
||||
let matcher = make_glob_matcher(from)?;
|
||||
let matcher = make_glob_matcher(from, Case::Insensitive)?;
|
||||
self.custom_mappings.push((matcher, to));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Like [`Self::insert`], but the glob pattern is matched case-sensitively.
|
||||
pub fn insert_case_sensitive(&mut self, from: &str, to: MappingTarget<'a>) -> Result<()> {
|
||||
let matcher = make_glob_matcher(from, Case::Sensitive)?;
|
||||
self.custom_mappings.push((matcher, to));
|
||||
Ok(())
|
||||
}
|
||||
@@ -261,4 +275,41 @@ mod tests {
|
||||
Some(MappingTarget::MapTo("alpha"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn case_sensitive_custom_mappings_work() {
|
||||
let mut map = SyntaxMapping::new();
|
||||
map.insert_case_sensitive("MY_SPECIAL_FILE", MappingTarget::MapTo("Python"))
|
||||
.ok();
|
||||
|
||||
// Exact case matches
|
||||
assert_eq!(
|
||||
map.get_syntax_for("/path/to/MY_SPECIAL_FILE"),
|
||||
Some(MappingTarget::MapTo("Python"))
|
||||
);
|
||||
// Different case should NOT match the case-sensitive rule
|
||||
assert_eq!(map.get_syntax_for("/path/to/my_special_file"), None);
|
||||
assert_eq!(map.get_syntax_for("/path/to/My_Special_File"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builtin_mappings_build_is_case_sensitive() {
|
||||
let map = SyntaxMapping::new();
|
||||
|
||||
// "BUILD" (uppercase) should map to Python via case-sensitive builtin
|
||||
assert_eq!(
|
||||
map.get_syntax_for("/path/to/BUILD"),
|
||||
Some(MappingTarget::MapTo("Python"))
|
||||
);
|
||||
// "build" (lowercase) should still map to MapToUnknown
|
||||
assert_eq!(
|
||||
map.get_syntax_for("/path/to/build"),
|
||||
Some(MappingTarget::MapToUnknown)
|
||||
);
|
||||
// Mixed case should NOT match the Python rule
|
||||
assert_eq!(
|
||||
map.get_syntax_for("/path/to/Build"),
|
||||
Some(MappingTarget::MapToUnknown)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::env;
|
||||
use globset::GlobMatcher;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::syntax_mapping::{make_glob_matcher, MappingTarget};
|
||||
use crate::syntax_mapping::{make_glob_matcher, Case, MappingTarget};
|
||||
|
||||
// Static syntax mappings generated from /src/syntax_mapping/builtins/ by the
|
||||
// build script (/build/syntax_mapping.rs).
|
||||
@@ -53,8 +53,8 @@ include!(concat!(
|
||||
/// A failure to compile is a fatal error.
|
||||
///
|
||||
/// Used internally by `Lazy<Option<GlobMatcher>>`'s lazy evaluation closure.
|
||||
fn build_matcher_fixed(from: &str) -> GlobMatcher {
|
||||
make_glob_matcher(from).expect("A builtin fixed glob matcher failed to compile")
|
||||
fn build_matcher_fixed(from: &str, case: Case) -> GlobMatcher {
|
||||
make_glob_matcher(from, case).expect("A builtin fixed glob matcher failed to compile")
|
||||
}
|
||||
|
||||
/// Join a list of matcher segments to create a glob string, replacing all
|
||||
@@ -64,7 +64,7 @@ fn build_matcher_fixed(from: &str) -> GlobMatcher {
|
||||
/// to compile.
|
||||
///
|
||||
/// Used internally by `Lazy<Option<GlobMatcher>>`'s lazy evaluation closure.
|
||||
fn build_matcher_dynamic(segs: &[MatcherSegment]) -> Option<GlobMatcher> {
|
||||
fn build_matcher_dynamic(segs: &[MatcherSegment], case: Case) -> Option<GlobMatcher> {
|
||||
// join segments
|
||||
let mut buf = String::new();
|
||||
for seg in segs {
|
||||
@@ -77,7 +77,7 @@ fn build_matcher_dynamic(segs: &[MatcherSegment]) -> Option<GlobMatcher> {
|
||||
}
|
||||
}
|
||||
// compile glob matcher
|
||||
let matcher = make_glob_matcher(&buf).ok()?;
|
||||
let matcher = make_glob_matcher(&buf, case).ok()?;
|
||||
Some(matcher)
|
||||
}
|
||||
|
||||
|
||||
@@ -20,12 +20,10 @@ syntax mappings defined by all TOML files, and embed them into the binary.
|
||||
|
||||
## File syntax
|
||||
|
||||
Each TOML file should contain a single section named `mappings`, with each of
|
||||
its keys being a language identifier (first column of `bat -L`; also referred to
|
||||
as "target").
|
||||
Each TOML file should contain a single section named `mappings`, with each of its keys being a language
|
||||
identifier (first column of `bat -L`; also referred to as "target").
|
||||
|
||||
The value of each key should be an array of strings, with each item being a glob
|
||||
matcher. We will call each of these items a "rule".
|
||||
The value of each key should be an array of "rules". The rules are expected to be objects with a `glob` string and a `case_sensitive` boolean. For simplification, a rule can be just a glob string, which is shorthand for the default case insensitive mode.
|
||||
|
||||
For example, if `foo-application` uses both TOML and YAML configuration files,
|
||||
we could write something like this:
|
||||
@@ -98,6 +96,15 @@ like this:
|
||||
]
|
||||
```
|
||||
|
||||
### Case sensitivity
|
||||
|
||||
By default, all glob patterns are matched case-insensitively. To match a pattern case-sensitively, use the object form of the rule with the `case_sensitive` option:
|
||||
|
||||
```toml
|
||||
[mappings]
|
||||
"Python" = [{ glob = "BUILD", case_sensitive = true }]
|
||||
```
|
||||
|
||||
## Ordering
|
||||
|
||||
At compile time, all TOML files applicable to the target are processed in
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
[mappings]
|
||||
"Python" = [{ glob = "BUILD", case_sensitive = true }]
|
||||
@@ -0,0 +1,3 @@
|
||||
[mappings]
|
||||
"INI" = [".boto", "**/gcloud/configurations/config_*"]
|
||||
"Git Ignore" = [".gcloudignore"]
|
||||
@@ -0,0 +1,2 @@
|
||||
[mappings]
|
||||
"Git Ignore" = [".?*ignore"]
|
||||
@@ -1,5 +1,5 @@
|
||||
# see `man quadlet`
|
||||
[mappings]
|
||||
"INI" = [
|
||||
"**/containers/systemd/**/*.{container,volume,network,kube,image}",
|
||||
"**/containers/systemd/**/*.{container,volume,network,kube,image,build,pod,artifact}",
|
||||
]
|
||||
|
||||
+1
-1
@@ -288,7 +288,7 @@ fn color_scheme_from_system() -> Option<ColorScheme> {
|
||||
const PREFERENCES_FILE: &str = "Library/Preferences/.GlobalPreferences.plist";
|
||||
const STYLE_KEY: &str = "AppleInterfaceStyle";
|
||||
|
||||
let preferences_file = home::home_dir()
|
||||
let preferences_file = std::env::home_dir()
|
||||
.map(|home| home.join(PREFERENCES_FILE))
|
||||
.expect("Could not get home directory");
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum WrappingMode {
|
||||
Character,
|
||||
Word,
|
||||
// The bool specifies whether wrapping has been explicitly disabled by the user via --wrap=never
|
||||
NoWrapping(bool),
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ fn all_themes_are_present() {
|
||||
"Solarized (light)",
|
||||
"Sublime Snazzy",
|
||||
"TwoDark",
|
||||
"Visual Studio Dark+",
|
||||
"ansi",
|
||||
"base16",
|
||||
"base16-256",
|
||||
|
||||
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
GIT-COMMIT(1) Git Manual GIT-COMMIT(1)
|
||||
|
||||
NNAAMMEE
|
||||
git-commit - Record changes to the repository
|
||||
|
||||
SSYYNNOOPPSSIISS
|
||||
ggiitt ccoommmmiitt [--aa | ----iinntteerraaccttiivvee | ----ppaattcchh] [--ss] [--vv] [--uu[_<_m_o_d_e_>]] [----aammeenndd]
|
||||
[----ddrryy--rruunn] [(--cc | --CC | ----ssqquuaasshh) _<_c_o_m_m_i_t_> | ----ffiixxuupp [(aammeenndd|rreewwoorrdd)::]_<_c_o_m_m_i_t_>]
|
||||
[--FF _<_f_i_l_e_> | --mm _<_m_s_g_>] [----rreesseett--aauutthhoorr] [----aallllooww--eemmppttyy]
|
||||
[----aallllooww--eemmppttyy--mmeessssaaggee] [----nnoo--vveerriiffyy] [--ee] [----aauutthhoorr==_<_a_u_t_h_o_r_>]
|
||||
[----ddaattee==_<_d_a_t_e_>] [----cclleeaannuupp==_<_m_o_d_e_>] [----[nnoo--]ssttaattuuss]
|
||||
[--ii | --oo] [----ppaatthhssppeecc--ffrroomm--ffiillee==_<_f_i_l_e_> [----ppaatthhssppeecc--ffiillee--nnuull]]
|
||||
[(----ttrraaiilleerr _<_t_o_k_e_n_>[(==|::)_<_v_a_l_u_e_>])...] [--SS[_<_k_e_y_i_d_>]]
|
||||
[----] [_<_p_a_t_h_s_p_e_c_>...]
|
||||
|
||||
DDEESSCCRRIIPPTTIIOONN
|
||||
Create a new commit containing the current contents of the index and
|
||||
the given log message describing the changes. The new commit is a
|
||||
direct child of HEAD, usually the tip of the current branch, and the
|
||||
branch is updated to point to it (unless no branch is associated with
|
||||
the working tree, in which case HHEEAADD is "detached" as described in ggiitt--
|
||||
cchheecckkoouutt(1)).
|
||||
|
||||
The content to be committed can be specified in several ways:
|
||||
|
||||
1. by using ggiitt--aadddd(1) to incrementally "add" changes to the index
|
||||
before using the ccoommmmiitt command (Note: even modified files must be
|
||||
"added");
|
||||
|
||||
2. by using ggiitt--rrmm(1) to remove files from the working tree and the
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
BBold tteexxtt and _u_n_d_e_r_l_i_n_e
|
||||
BIN
Binary file not shown.
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
The quick brown fox jumps over the lazy dog and then runs away
|
||||
superlongwordthatdefinitelyexceedstheterminalwidthandshouldfallbacktocharacterwrapping
|
||||
short words here
|
||||
+807
-37
@@ -166,6 +166,53 @@ fn line_numbers() {
|
||||
.stdout(" 1 line 1\n 2 line 2\n 3 line 3\n 4 line 4\n 5 line 5\n 6 line 6\n 7 line 7\n 8 line 8\n 9 line 9\n 10 line 10\n");
|
||||
}
|
||||
|
||||
// Test that -n on command line shows line numbers even when piping (similar to `cat -n`)
|
||||
#[test]
|
||||
fn line_numbers_from_cli_in_loop_through_mode() {
|
||||
bat()
|
||||
.arg("multiline.txt")
|
||||
.arg("-n")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(" 1 line 1\n 2 line 2\n 3 line 3\n 4 line 4\n 5 line 5\n 6 line 6\n 7 line 7\n 8 line 8\n 9 line 9\n 10 line 10\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn style_from_env_var_ignored_and_line_numbers_from_cli_in_loop_through_mode() {
|
||||
bat()
|
||||
.env("BAT_STYLE", "full")
|
||||
.arg("multiline.txt")
|
||||
.arg("-n")
|
||||
.arg("--decorations=auto")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(" 1 line 1\n 2 line 2\n 3 line 3\n 4 line 4\n 5 line 5\n 6 line 6\n 7 line 7\n 8 line 8\n 9 line 9\n 10 line 10\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn numbers_ignored_from_cli_when_followed_by_plain_in_loop_through_mode() {
|
||||
bat()
|
||||
.arg("multiline.txt")
|
||||
.arg("-np")
|
||||
.arg("--decorations=auto")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(
|
||||
"line 1\nline 2\nline 3\nline 4\nline 5\nline 6\nline 7\nline 8\nline 9\nline 10\n",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn numbers_honored_from_cli_when_preceeded_by_plain_in_loop_through_mode() {
|
||||
bat()
|
||||
.arg("multiline.txt")
|
||||
.arg("-pn")
|
||||
.arg("--decorations=auto")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(" 1 line 1\n 2 line 2\n 3 line 3\n 4 line 4\n 5 line 5\n 6 line 6\n 7 line 7\n 8 line 8\n 9 line 9\n 10 line 10\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn line_range_2_3() {
|
||||
bat()
|
||||
@@ -207,7 +254,7 @@ fn line_range_from_back_last_two() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn line_range_from_back_last_two_single_line() {
|
||||
fn line_range_from_back_last_two_single_line_eq_sep() {
|
||||
bat()
|
||||
.arg("single-line.txt")
|
||||
.arg("--line-range=-2:")
|
||||
@@ -216,6 +263,17 @@ fn line_range_from_back_last_two_single_line() {
|
||||
.stdout("Single Line");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn line_range_from_back_last_two_single_line_no_sep() {
|
||||
bat()
|
||||
.arg("single-line.txt")
|
||||
.arg("--line-range")
|
||||
.arg("-2:")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("Single Line");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn line_range_first_two() {
|
||||
bat()
|
||||
@@ -404,26 +462,18 @@ fn piped_output_with_line_numbers_style_flag() {
|
||||
.write_stdin("hello\nworld\n")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(" 1 hello\n 2 world\n");
|
||||
.stdout("hello\nworld\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn piped_output_with_line_numbers_with_header_grid_style_flag() {
|
||||
// style ignored because non-interactive
|
||||
bat()
|
||||
.arg("--style=header,grid,numbers")
|
||||
.write_stdin("hello\nworld\n")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(
|
||||
"─────┬──────────────────────────────────────────────────────────────────────────
|
||||
│ STDIN
|
||||
─────┼──────────────────────────────────────────────────────────────────────────
|
||||
1 │ hello
|
||||
2 │ world
|
||||
─────┴──────────────────────────────────────────────────────────────────────────
|
||||
",
|
||||
);
|
||||
.stdout("hello\nworld\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -441,6 +491,7 @@ fn piped_output_with_auto_style() {
|
||||
fn piped_output_with_default_style_flag() {
|
||||
bat()
|
||||
.arg("--style=default")
|
||||
.arg("--decorations=always")
|
||||
.write_stdin("hello\nworld\n")
|
||||
.assert()
|
||||
.success()
|
||||
@@ -594,6 +645,44 @@ fn test_help(arg: &str, expect_file: &str) {
|
||||
.assert_eq(&String::from_utf8_lossy(&assert.get_output().stdout));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn short_help_with_highlighting() {
|
||||
bat()
|
||||
.arg("-h")
|
||||
.arg("--paging=never")
|
||||
.arg("--color=always")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("\x1B["))
|
||||
.stdout(predicate::str::contains("Usage:"))
|
||||
.stdout(predicate::str::contains("Options:"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn long_help_with_highlighting() {
|
||||
bat()
|
||||
.arg("--help")
|
||||
.arg("--paging=never")
|
||||
.arg("--color=always")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("\x1B["))
|
||||
.stdout(predicate::str::contains("Usage:"))
|
||||
.stdout(predicate::str::contains("Options:"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn help_with_color_never() {
|
||||
bat()
|
||||
.arg("--help")
|
||||
.arg("--color=never")
|
||||
.arg("--paging=never")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("\x1B[").not())
|
||||
.stdout(predicate::str::contains("Usage:"));
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn setup_temp_file(content: &[u8]) -> io::Result<(PathBuf, tempfile::TempDir)> {
|
||||
let dir = tempfile::tempdir().expect("Couldn't create tempdir");
|
||||
@@ -1268,27 +1357,33 @@ fn disable_pager_if_pp_flag_comes_after_paging() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn enable_pager_if_disable_paging_flag_comes_before_paging() {
|
||||
bat()
|
||||
.env("PAGER", "echo pager-output")
|
||||
.arg("-P")
|
||||
.arg("--paging=always")
|
||||
.arg("test.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::eq("pager-output\n").normalize());
|
||||
mocked_pagers::with_mocked_versions_of_more_and_most_in_path(|| {
|
||||
bat()
|
||||
.env("PAGER", mocked_pagers::from("echo pager-output"))
|
||||
.arg("-P")
|
||||
.arg("--paging=always")
|
||||
.arg("test.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("pager-output\n").normalize());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn enable_pager_if_pp_flag_comes_before_paging() {
|
||||
bat()
|
||||
.env("PAGER", "echo pager-output")
|
||||
.arg("-pp")
|
||||
.arg("--paging=always")
|
||||
.arg("test.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::eq("pager-output\n").normalize());
|
||||
mocked_pagers::with_mocked_versions_of_more_and_most_in_path(|| {
|
||||
bat()
|
||||
.env("PAGER", mocked_pagers::from("echo pager-output"))
|
||||
.arg("-pp")
|
||||
.arg("--paging=always")
|
||||
.arg("test.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("pager-output\n").normalize());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1305,15 +1400,18 @@ fn paging_does_not_override_simple_plain() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn simple_plain_does_not_override_paging() {
|
||||
bat()
|
||||
.env("PAGER", "echo pager-output")
|
||||
.arg("--paging=always")
|
||||
.arg("--plain")
|
||||
.arg("test.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::eq("pager-output\n"));
|
||||
mocked_pagers::with_mocked_versions_of_more_and_most_in_path(|| {
|
||||
bat()
|
||||
.env("PAGER", mocked_pagers::from("echo pager-output"))
|
||||
.arg("--paging=always")
|
||||
.arg("--plain")
|
||||
.arg("test.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("pager-output\n").normalize());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1327,6 +1425,21 @@ fn pager_failed_to_parse() {
|
||||
.stderr(predicate::str::contains("Could not parse pager command"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn pager_missing_warning() {
|
||||
bat()
|
||||
.env("BAT_PAGER", "nonexistent-pager-xyz-missing")
|
||||
.arg("--paging=always")
|
||||
.arg("test.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stderr(predicate::str::contains("[bat warning]"))
|
||||
.stderr(predicate::str::contains("not found"))
|
||||
.stderr(predicate::str::contains("nonexistent-pager-xyz-missing"))
|
||||
.stdout(predicate::str::contains("hello world\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn env_var_bat_paging() {
|
||||
@@ -1344,6 +1457,7 @@ fn env_var_bat_paging() {
|
||||
#[test]
|
||||
fn basic_set_terminal_title() {
|
||||
bat()
|
||||
.env("BAT_PAGER", "cat")
|
||||
.arg("--paging=always")
|
||||
.arg("--set-terminal-title")
|
||||
.arg("test.txt")
|
||||
@@ -1390,6 +1504,28 @@ fn help_works_with_invalid_config() {
|
||||
.stdout(predicate::str::contains("A cat(1) clone with wings"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn help_uses_valid_config() {
|
||||
let tmp_dir = tempdir().expect("can create temporary directory");
|
||||
let tmp_config_path = tmp_dir.path().join("valid-config.conf");
|
||||
|
||||
// Write a valid config file with an invalid theme (so we can see a warning)
|
||||
std::fs::write(&tmp_config_path, "--theme=NonExistentThemeName123")
|
||||
.expect("can write config file");
|
||||
|
||||
// --help should read the config file and try to use the theme
|
||||
// (we'll see a warning about unknown theme. This is the easiest way to prove the theme is read from config.)
|
||||
bat_with_config()
|
||||
.env("BAT_CONFIG_PATH", tmp_config_path.to_str().unwrap())
|
||||
.arg("--help")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains(
|
||||
"A cat(1) clone with syntax highlighting",
|
||||
))
|
||||
.stderr(predicate::str::contains("Unknown theme"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_works_with_invalid_config() {
|
||||
let tmp_dir = tempdir().expect("can create temporary directory");
|
||||
@@ -1550,7 +1686,7 @@ Clearing metadata file ... okay",
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn cache_build() {
|
||||
fn cache_build_blank() {
|
||||
let src_dir = "cache_source";
|
||||
let tmp_dir = tempdir().expect("can create temporary directory");
|
||||
let tmp_themes_path = tmp_dir.path().join("themes.bin");
|
||||
@@ -1593,6 +1729,50 @@ Writing metadata to folder .* ... okay",
|
||||
assert!(tmp_metadata_path.exists());
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn cache_build() {
|
||||
let src_dir = "cache_source";
|
||||
let tmp_dir = tempdir().expect("can create temporary directory");
|
||||
let tmp_themes_path = tmp_dir.path().join("themes.bin");
|
||||
let tmp_syntaxes_path = tmp_dir.path().join("syntaxes.bin");
|
||||
let tmp_acknowledgements_path = tmp_dir.path().join("acknowledgements.bin");
|
||||
let tmp_metadata_path = tmp_dir.path().join("metadata.yaml");
|
||||
|
||||
// Build the cache
|
||||
// Include the BAT_CONFIG_PATH and BAT_THEME environment variables to ensure that
|
||||
// options loaded from a config or the environment are not inserted
|
||||
// before the cache subcommand, which would break it.
|
||||
bat_with_config()
|
||||
.current_dir(Path::new(EXAMPLES_DIR).join(src_dir))
|
||||
.env("BAT_CONFIG_PATH", "bat.conf")
|
||||
.env("BAT_THEME", "1337")
|
||||
.arg("cache")
|
||||
.arg("--build")
|
||||
// removed --blank
|
||||
.arg("--source")
|
||||
.arg(".")
|
||||
.arg("--target")
|
||||
.arg(tmp_dir.path().to_str().unwrap())
|
||||
.arg("--acknowledgements")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(
|
||||
predicate::str::is_match(
|
||||
"Writing theme set to .*/themes.bin ... okay\nWriting syntax set to .*/syntaxes.bin ... okay\nWriting acknowledgements to .*/acknowledgements.bin ... okay\nWriting metadata to folder .* ... okay",
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.stdout(predicate::str::contains("Some referenced contexts could not be found!").not())
|
||||
;
|
||||
|
||||
// Now we expect the files to exist. If they exist, we assume contents are correct
|
||||
assert!(tmp_themes_path.exists());
|
||||
assert!(tmp_syntaxes_path.exists());
|
||||
assert!(tmp_acknowledgements_path.exists());
|
||||
assert!(tmp_metadata_path.exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn utf16() {
|
||||
// The output will be converted to UTF-8 with the leading UTF-16
|
||||
@@ -2290,6 +2470,121 @@ fn no_first_line_fallback_when_mapping_to_invalid_syntax() {
|
||||
.stderr(predicate::str::contains("unknown syntax: 'InvalidSyntax'"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fallback_syntax_is_used_when_no_syntax_is_detected() {
|
||||
let content = "# comment\nfoo=bar\n";
|
||||
|
||||
let fallback_output = bat()
|
||||
.arg("--color=always")
|
||||
.arg("--style=plain")
|
||||
.arg("--file-name=unknown.fallbacksyntax")
|
||||
.arg("--fallback-syntax=bash")
|
||||
.write_stdin(content)
|
||||
.assert()
|
||||
.success()
|
||||
.get_output()
|
||||
.stdout
|
||||
.clone();
|
||||
|
||||
let explicit_output = bat()
|
||||
.arg("--color=always")
|
||||
.arg("--style=plain")
|
||||
.arg("--language=bash")
|
||||
.arg("--file-name=unknown.fallbacksyntax")
|
||||
.write_stdin(content)
|
||||
.assert()
|
||||
.success()
|
||||
.get_output()
|
||||
.stdout
|
||||
.clone();
|
||||
|
||||
assert_eq!(
|
||||
from_utf8(&fallback_output).expect("output is valid utf-8"),
|
||||
from_utf8(&explicit_output).expect("output is valid utf-8")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fallback_syntax_does_not_override_detected_syntax() {
|
||||
let content = "fn main() { println!(\"hello\"); }\n";
|
||||
|
||||
let with_fallback = bat()
|
||||
.arg("--color=always")
|
||||
.arg("--style=plain")
|
||||
.arg("--file-name=test.rs")
|
||||
.arg("--fallback-syntax=json")
|
||||
.write_stdin(content)
|
||||
.assert()
|
||||
.success()
|
||||
.get_output()
|
||||
.stdout
|
||||
.clone();
|
||||
|
||||
let without_fallback = bat()
|
||||
.arg("--color=always")
|
||||
.arg("--style=plain")
|
||||
.arg("--file-name=test.rs")
|
||||
.write_stdin(content)
|
||||
.assert()
|
||||
.success()
|
||||
.get_output()
|
||||
.stdout
|
||||
.clone();
|
||||
|
||||
assert_eq!(
|
||||
from_utf8(&with_fallback).expect("output is valid utf-8"),
|
||||
from_utf8(&without_fallback).expect("output is valid utf-8")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fallback_syntax_does_not_override_explicit_language() {
|
||||
let content = "{\"a\": 1}\n";
|
||||
|
||||
let with_fallback = bat()
|
||||
.arg("--color=always")
|
||||
.arg("--style=plain")
|
||||
.arg("--language=json")
|
||||
.arg("--fallback-syntax=rust")
|
||||
.arg("--file-name=unknown.fallbacksyntax")
|
||||
.write_stdin(content)
|
||||
.assert()
|
||||
.success()
|
||||
.get_output()
|
||||
.stdout
|
||||
.clone();
|
||||
|
||||
let without_fallback = bat()
|
||||
.arg("--color=always")
|
||||
.arg("--style=plain")
|
||||
.arg("--language=json")
|
||||
.arg("--file-name=unknown.fallbacksyntax")
|
||||
.write_stdin(content)
|
||||
.assert()
|
||||
.success()
|
||||
.get_output()
|
||||
.stdout
|
||||
.clone();
|
||||
|
||||
assert_eq!(
|
||||
from_utf8(&with_fallback).expect("output is valid utf-8"),
|
||||
from_utf8(&without_fallback).expect("output is valid utf-8")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_fallback_syntax_returns_error() {
|
||||
bat()
|
||||
.arg("--color=always")
|
||||
.arg("--style=plain")
|
||||
.arg("--file-name=unknown.fallbacksyntax")
|
||||
.arg("--fallback-syntax=InvalidSyntax")
|
||||
.write_stdin("foo\n")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains("unknown syntax: 'InvalidSyntax'"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_all_mode() {
|
||||
bat()
|
||||
@@ -2386,6 +2681,92 @@ fn binary_as_text() {
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binary_as_text_control_char_width() {
|
||||
// Control characters are displayed as caret notation (e.g. ^@) by the
|
||||
// terminal, occupying 2 columns each. With 20 NUL bytes (40 columns) +
|
||||
// "END" (3 columns) = 43 columns, wrapping at terminal width 40 must
|
||||
// produce 2 lines, not 1. See #3631.
|
||||
bat()
|
||||
.arg("--binary=as-text")
|
||||
.arg("--wrap=character")
|
||||
.arg("--terminal-width=40")
|
||||
.arg("--decorations=always")
|
||||
.arg("--style=plain")
|
||||
.arg("--color=never")
|
||||
.arg("regression_tests/issue_3631.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::function(|s: &str| s.lines().count() == 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_strip_overstrike_for_plain_text() {
|
||||
// Overstrike is preserved for plain text files (no syntax highlighting)
|
||||
bat()
|
||||
.arg("--color=never")
|
||||
.arg("--decorations=never")
|
||||
.arg("overstrike.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("B\x08Bold t\x08te\x08ex\x08xt\x08t and _\x08u_\x08n_\x08d_\x08e_\x08r_\x08l_\x08i_\x08n_\x08e\n")
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strip_overstrike_with_syntax_highlighting() {
|
||||
// Overstrike is stripped for certain syntax highlighting like command help.
|
||||
bat()
|
||||
.arg("--force-colorization")
|
||||
.arg("--language=help")
|
||||
.arg("overstrike.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("Bold text and underline"))
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strip_overstrike_for_manpage_syntax() {
|
||||
// Overstrike is stripped for .man files (Manpage syntax)
|
||||
bat()
|
||||
.arg("--force-colorization")
|
||||
.arg("git-commit.man")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("NAME"))
|
||||
.stdout(predicate::str::contains("git-commit - Record changes"))
|
||||
.stdout(predicate::str::is_match(r"\x1b\[38;[0-9;]+m--interactive\x1b\[").unwrap())
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_strip_overstrike_for_other_syntax() {
|
||||
// Overstrike is NOT stripped for other syntaxes (e.g., Rust)
|
||||
bat()
|
||||
.arg("--force-colorization")
|
||||
.arg("--language=rust")
|
||||
.arg("overstrike.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("\x08"))
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_all_shows_backspace_with_caret_notation() {
|
||||
// --show-all should display backspace characters (not strip them)
|
||||
bat()
|
||||
.arg("--show-all")
|
||||
.arg("--nonprintable-notation=caret")
|
||||
.arg("--decorations=never")
|
||||
.arg("overstrike.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("^H"))
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_paging_arg() {
|
||||
bat()
|
||||
@@ -2653,6 +3034,91 @@ fn no_wrapping_with_chop_long_lines() {
|
||||
wrapping_test("--chop-long-lines", false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn line_wrapping_when_set_to_character() {
|
||||
wrapping_test("--wrap=character", true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_line_wrapping_when_character_wrap_from_config() {
|
||||
// --wrap=character in a config file should be ignored when output is not interactive
|
||||
// (e.g. when piping), since wrapping is only useful with a pager.
|
||||
let tmp_dir = tempdir().expect("can create temporary directory");
|
||||
let tmp_config_path = tmp_dir.path().join("wrap-character.conf");
|
||||
std::fs::write(&tmp_config_path, "--wrap=character").expect("can write config file");
|
||||
|
||||
let expected = "abcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyz\n";
|
||||
|
||||
bat_with_config()
|
||||
.env("BAT_CONFIG_PATH", tmp_config_path.to_str().unwrap())
|
||||
.arg("--style=rule")
|
||||
.arg("--color=never")
|
||||
.arg("--decorations=always")
|
||||
.arg("--terminal-width=80")
|
||||
.arg("long-single-line.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(expected)
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_line_wrapping_when_character_wrap_from_bat_opts() {
|
||||
// --wrap=character in BAT_OPTS should be ignored when output is not interactive,
|
||||
// since it was not explicitly provided on the command line.
|
||||
let expected = "abcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyz\n";
|
||||
|
||||
bat_with_config()
|
||||
.env("BAT_OPTS", "--wrap=character")
|
||||
.arg("--style=rule")
|
||||
.arg("--color=never")
|
||||
.arg("--decorations=always")
|
||||
.arg("--terminal-width=80")
|
||||
.arg("long-single-line.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(expected)
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn wrap_never_flag_respected_with_paging_always() {
|
||||
mocked_pagers::with_mocked_versions_of_more_and_most_in_path(|| {
|
||||
bat()
|
||||
.arg("--pager=cat")
|
||||
.arg("--paging=always")
|
||||
.arg("--wrap=never")
|
||||
.arg("--color=never")
|
||||
.arg("--decorations=never")
|
||||
.arg("--style=plain")
|
||||
.write_stdin("abcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyz\n")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("abcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyz").normalize())
|
||||
.stderr("");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn s_flag_respected_with_paging_always() {
|
||||
mocked_pagers::with_mocked_versions_of_more_and_most_in_path(|| {
|
||||
bat()
|
||||
.arg("--pager=cat")
|
||||
.arg("--paging=always")
|
||||
.arg("-S")
|
||||
.arg("--color=never")
|
||||
.arg("--decorations=never")
|
||||
.arg("--style=plain")
|
||||
.write_stdin("abcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyz\n")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("abcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyz").normalize())
|
||||
.stderr("");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn theme_arg_overrides_env() {
|
||||
bat()
|
||||
@@ -3409,3 +3875,307 @@ fn style_components_will_merge_with_env_var() {
|
||||
.stdout(" STDIN\n 1 test\n")
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
// Test for https://github.com/sharkdp/bat/issues/3526
|
||||
#[test]
|
||||
fn plain_with_sized_terminal_width() {
|
||||
bat()
|
||||
.arg("--plain")
|
||||
.arg("--terminal-width=6")
|
||||
.arg("--decorations=always")
|
||||
.arg("test.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("hello \nworld\n")
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quiet_empty_suppresses_output_on_empty_stdin() {
|
||||
bat()
|
||||
.arg("--quiet-empty")
|
||||
.write_stdin("")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quiet_empty_does_not_affect_non_empty_input() {
|
||||
bat()
|
||||
.arg("--quiet-empty")
|
||||
.write_stdin("hello\n")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("hello\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quiet_empty_suppresses_output_on_empty_file() {
|
||||
bat()
|
||||
.arg("--quiet-empty")
|
||||
.arg("empty.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_help_shows_help_message() {
|
||||
// Test that `bat cache --help` works (fixes #3560)
|
||||
// Run in cache_source directory which doesn't have a file named "cache"
|
||||
bat_with_config()
|
||||
.current_dir(Path::new(EXAMPLES_DIR).join("cache_source"))
|
||||
.arg("cache")
|
||||
.arg("--help")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains(
|
||||
"Modify the syntax-definition and theme cache",
|
||||
))
|
||||
.stdout(predicate::str::contains("--build"))
|
||||
.stdout(predicate::str::contains("--clear"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unbuffered_flag_is_accepted() {
|
||||
bat()
|
||||
.arg("--unbuffered")
|
||||
.arg("test.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("hello world\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unbuffered_mode_disables_line_numbers() {
|
||||
// When --unbuffered is used, line numbers should be auto-disabled even if requested
|
||||
bat()
|
||||
.arg("--unbuffered")
|
||||
.arg("--style=numbers")
|
||||
.arg("--decorations=always")
|
||||
.arg("--color=never")
|
||||
.arg("test.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::starts_with(" 1").not());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unbuffered_mode_plain_output() {
|
||||
bat()
|
||||
.arg("--unbuffered")
|
||||
.arg("--color=never")
|
||||
.arg("--decorations=never")
|
||||
.arg("test.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("hello world\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn word_wrap_breaks_at_word_boundaries() {
|
||||
bat()
|
||||
.arg("word-wrap.txt")
|
||||
.arg("--wrap=word")
|
||||
.arg("--terminal-width=40")
|
||||
.arg("--style=plain")
|
||||
.arg("--decorations=always")
|
||||
.arg("--color=never")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(
|
||||
"\
|
||||
The quick brown fox jumps over the lazy
|
||||
dog and then runs away
|
||||
superlongwordthatdefinitelyexceedstheter
|
||||
minalwidthandshouldfallbacktocharacterwr
|
||||
apping
|
||||
short words here
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn word_wrap_with_line_numbers() {
|
||||
bat()
|
||||
.arg("word-wrap.txt")
|
||||
.arg("--wrap=word")
|
||||
.arg("--terminal-width=40")
|
||||
.arg("--style=numbers")
|
||||
.arg("--decorations=always")
|
||||
.arg("--color=never")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(
|
||||
" 1 The quick brown fox jumps over the
|
||||
lazy dog and then runs away
|
||||
2 superlongwordthatdefinitelyexceedst
|
||||
heterminalwidthandshouldfallbacktoc
|
||||
haracterwrapping
|
||||
3 short words here
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn word_wrap_short_line_no_wrap() {
|
||||
bat()
|
||||
.arg("--wrap=word")
|
||||
.arg("--terminal-width=80")
|
||||
.arg("--style=plain")
|
||||
.arg("--decorations=always")
|
||||
.arg("--color=never")
|
||||
.arg("single-line.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("Single Line\n");
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg(feature = "git")]
|
||||
fn setup_diff_test_repo() -> tempfile::TempDir {
|
||||
use std::process::Command;
|
||||
|
||||
let dir = tempfile::tempdir().expect("can create temporary directory");
|
||||
let repo = dir.path();
|
||||
|
||||
// Initialize a git repo and commit a file
|
||||
Command::new("git")
|
||||
.args(["init"])
|
||||
.current_dir(repo)
|
||||
.output()
|
||||
.expect("git init");
|
||||
|
||||
Command::new("git")
|
||||
.args(["config", "user.email", "test@test.com"])
|
||||
.current_dir(repo)
|
||||
.output()
|
||||
.expect("git config email");
|
||||
|
||||
Command::new("git")
|
||||
.args(["config", "user.name", "Test"])
|
||||
.current_dir(repo)
|
||||
.output()
|
||||
.expect("git config name");
|
||||
|
||||
std::fs::write(repo.join("test.txt"), "line 1\nline 2\nline 3\n").expect("can write test file");
|
||||
|
||||
Command::new("git")
|
||||
.args(["add", "test.txt"])
|
||||
.current_dir(repo)
|
||||
.output()
|
||||
.expect("git add");
|
||||
|
||||
Command::new("git")
|
||||
.args(["commit", "-m", "initial"])
|
||||
.current_dir(repo)
|
||||
.output()
|
||||
.expect("git commit");
|
||||
|
||||
// Modify the file so --diff has something to show
|
||||
std::fs::write(
|
||||
repo.join("test.txt"),
|
||||
"line 1\nline 2 modified\nline 3\nline 4 added\n",
|
||||
)
|
||||
.expect("can write modified test file");
|
||||
|
||||
dir
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg(feature = "git")]
|
||||
#[test]
|
||||
fn diff_plain_preserves_change_markers() {
|
||||
let repo = setup_diff_test_repo();
|
||||
|
||||
// With --diff --plain, output should contain the change marker column
|
||||
// but not other decorations like line numbers or grid
|
||||
let output = bat()
|
||||
.current_dir(repo.path())
|
||||
.arg("--diff")
|
||||
.arg("--plain")
|
||||
.arg("--color=never")
|
||||
.arg("--decorations=always")
|
||||
.arg("test.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.get_output()
|
||||
.stdout
|
||||
.clone();
|
||||
|
||||
let stdout = std::str::from_utf8(&output).expect("valid utf-8");
|
||||
|
||||
// The output should contain the modified and added lines
|
||||
assert!(
|
||||
stdout.contains("line 2 modified"),
|
||||
"diff plain output should contain modified line, got: {stdout}"
|
||||
);
|
||||
assert!(
|
||||
stdout.contains("line 4 added"),
|
||||
"diff plain output should contain added line, got: {stdout}"
|
||||
);
|
||||
|
||||
// Should NOT contain line numbers (a decoration that --plain disables)
|
||||
assert!(
|
||||
!stdout.contains(" 1"),
|
||||
"diff plain output should not contain line numbers, got: {stdout}"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg(feature = "git")]
|
||||
#[test]
|
||||
fn diff_plain_does_not_show_grid_or_header() {
|
||||
let repo = setup_diff_test_repo();
|
||||
|
||||
let output = bat()
|
||||
.current_dir(repo.path())
|
||||
.arg("--diff")
|
||||
.arg("--plain")
|
||||
.arg("--color=never")
|
||||
.arg("--decorations=always")
|
||||
.arg("--terminal-width=80")
|
||||
.arg("test.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.get_output()
|
||||
.stdout
|
||||
.clone();
|
||||
|
||||
let stdout = std::str::from_utf8(&output).expect("valid utf-8");
|
||||
|
||||
// Grid lines use box-drawing characters
|
||||
assert!(
|
||||
!stdout.contains('─'),
|
||||
"diff plain output should not contain grid lines, got: {stdout}"
|
||||
);
|
||||
assert!(
|
||||
!stdout.contains('│'),
|
||||
"diff plain output should not contain grid separators, got: {stdout}"
|
||||
);
|
||||
|
||||
// Header shows "File: <filename>"
|
||||
assert!(
|
||||
!stdout.contains("File:"),
|
||||
"diff plain output should not contain file header, got: {stdout}"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg(feature = "git")]
|
||||
#[test]
|
||||
fn plain_without_diff_still_works() {
|
||||
let repo = setup_diff_test_repo();
|
||||
|
||||
// --plain without --diff should output file content with no decorations at all
|
||||
bat()
|
||||
.current_dir(repo.path())
|
||||
.arg("--plain")
|
||||
.arg("--color=never")
|
||||
.arg("--decorations=always")
|
||||
.arg("test.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("line 1\nline 2 modified\nline 3\nline 4 added\n");
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
[38;2;230;219;116mwith Chests.Ring_Buffers;[0m
|
||||
[38;2;230;219;116mwith USB.Device.HID.Keyboard;[0m
|
||||
[38;2;249;38;114mwith[0m[38;2;248;248;242m [0m[38;2;166;226;46mChests.Ring_Buffers[0m[38;2;248;248;242m;[0m
|
||||
[38;2;249;38;114mwith[0m[38;2;248;248;242m [0m[38;2;166;226;46mUSB.Device.HID.Keyboard[0m[38;2;248;248;242m;[0m
|
||||
|
||||
[38;2;249;38;114mpackage[0m[38;2;248;248;242m [0m[38;2;249;38;114mbody[0m[38;2;248;248;242m [0m[38;2;166;226;46mClick[0m[38;2;248;248;242m [0m[38;2;249;38;114mis[0m
|
||||
[38;2;248;248;242m [0m[38;2;117;113;94m--[0m[38;2;117;113;94m--------------[0m
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
[38;2;230;219;116mwith HAL.GPIO;[0m
|
||||
[38;2;230;219;116mwith USB.Device.HID.Keyboard;[0m
|
||||
[38;2;249;38;114mwith[0m[38;2;248;248;242m [0m[38;2;166;226;46mHAL.GPIO[0m[38;2;248;248;242m;[0m
|
||||
[38;2;249;38;114mwith[0m[38;2;248;248;242m [0m[38;2;166;226;46mUSB.Device.HID.Keyboard[0m[38;2;248;248;242m;[0m
|
||||
|
||||
[38;2;249;38;114mgeneric[0m
|
||||
[38;2;249;38;114m [0m[38;2;166;226;46mNb_Bounce[0m[38;2;249;38;114m :[0m[38;2;248;248;242m Natural;[0m
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
[3;38;2;124;120;101m#[0m[3;38;2;124;120;101m!/bin/ksh[0m
|
||||
[3;38;2;124;120;101m#[0m
|
||||
[3;38;2;124;120;101m#[0m[3;38;2;124;120;101m https://www.qnx.com/developers/docs/6.4.0/neutrino/user_guide/scripts.html[0m
|
||||
[3;38;2;124;120;101m#[0m[3;38;2;124;120;101m tfind:[0m
|
||||
[3;38;2;124;120;101m#[0m[3;38;2;124;120;101m script to look for strings in various files and dump to less[0m
|
||||
|
||||
[38;2;249;38;114mcase[0m[38;2;255;255;255m [0m[38;2;255;255;255m$[0m[38;2;255;255;255m#[0m[38;2;255;255;255m [0m[38;2;249;38;114min[0m
|
||||
[38;2;255;255;255m1[0m[38;2;249;38;114m)[0m
|
||||
[38;2;255;255;255m [0m[38;2;255;255;255mfind[0m[38;2;255;255;255m .[0m[3;38;2;253;151;31m -[0m[3;38;2;253;151;31mname[0m[38;2;255;255;255m [0m[38;2;255;255;255m'[0m[38;2;230;219;116m*.[ch][0m[38;2;255;255;255m'[0m[38;2;255;255;255m [0m[38;2;249;38;114m|[0m[38;2;255;255;255m [0m[38;2;255;255;255mxargs[0m[38;2;255;255;255m grep [0m[38;2;255;255;255m$[0m[38;2;255;255;255m1[0m[38;2;255;255;255m [0m[38;2;249;38;114m|[0m[38;2;255;255;255m [0m[38;2;255;255;255mless[0m
|
||||
[38;2;255;255;255m [0m[38;2;102;217;239mexit[0m[38;2;255;255;255m 0 [0m[3;38;2;124;120;101m#[0m[3;38;2;124;120;101m good status[0m
|
||||
[38;2;249;38;114mesac[0m
|
||||
|
||||
[38;2;102;217;239mecho[0m[38;2;255;255;255m [0m[38;2;255;255;255m"[0m[38;2;230;219;116mUse tfind stuff_to_find [0m[38;2;255;255;255m"[0m
|
||||
[38;2;102;217;239mecho[0m[38;2;255;255;255m [0m[38;2;255;255;255m"[0m[38;2;230;219;116m where : stuff_to_find = search string [0m[38;2;255;255;255m"[0m
|
||||
[38;2;102;217;239mecho[0m[38;2;255;255;255m [0m[38;2;255;255;255m"[0m[38;2;230;219;116m [0m[38;2;255;255;255m"[0m
|
||||
[38;2;102;217;239mecho[0m[38;2;255;255;255m [0m[38;2;255;255;255m"[0m[38;2;230;219;116me.g. tfind console_state looks through all files in [0m[38;2;255;255;255m"[0m[38;2;255;255;255m [0m
|
||||
[38;2;102;217;239mecho[0m[38;2;255;255;255m [0m[38;2;255;255;255m"[0m[38;2;230;219;116m the current directory and below and displays all [0m[38;2;255;255;255m"[0m
|
||||
[38;2;102;217;239mecho[0m[38;2;255;255;255m [0m[38;2;255;255;255m"[0m[38;2;230;219;116m instances of console_state.[0m[38;2;255;255;255m"[0m
|
||||
[38;2;102;217;239mexit[0m[38;2;255;255;255m 1 [0m[3;38;2;124;120;101m#[0m[3;38;2;124;120;101m bad status[0m
|
||||
@@ -0,0 +1,61 @@
|
||||
[38;2;249;38;114mIDENTIFICATION DIVISION[0m[38;2;248;248;242m.[0m
|
||||
[38;2;249;38;114mPROGRAM-ID[0m[38;2;248;248;242m. [0m[38;2;248;248;242mPAYROLL-CALC[0m[38;2;248;248;242m.[0m
|
||||
|
||||
[38;2;249;38;114mDATA DIVISION[0m[38;2;248;248;242m.[0m
|
||||
[38;2;249;38;114mWORKING-STORAGE SECTION[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m01[0m[38;2;248;248;242m [0m[38;2;248;248;242mEMPLOYEE-DETAILS[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m05[0m[38;2;248;248;242m [0m[38;2;248;248;242mEMPLOYEE-NAME[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mPIC X(30).[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m05[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURS-WORKED[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mPIC 99V9[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m05[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURLY-RATE[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mPIC 99V99[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m01[0m[38;2;248;248;242m [0m[38;2;248;248;242mPAY-CALCULATIONS[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m05[0m[38;2;248;248;242m [0m[38;2;248;248;242mGROSS-PAY[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mPIC 9(5)V99.[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m05[0m[38;2;248;248;242m [0m[38;2;248;248;242mOVERTIME-HOURS[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mPIC 99V9[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m05[0m[38;2;248;248;242m [0m[38;2;248;248;242mOVERTIME-PAY[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mPIC 9(5)V99.[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m05[0m[38;2;248;248;242m [0m[38;2;248;248;242mTAX-RATE[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mPIC V99[0m[38;2;248;248;242m [0m[38;2;249;38;114mVALUE[0m[38;2;248;248;242m [0m[38;2;190;132;255m0.10[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m05[0m[38;2;248;248;242m [0m[38;2;248;248;242mTAX-AMOUNT[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mPIC 9(5)V99.[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m05[0m[38;2;248;248;242m [0m[38;2;248;248;242mNET-PAY[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mPIC 9(5)V99.[0m
|
||||
|
||||
[38;2;249;38;114mPROCEDURE DIVISION[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242mMAIN-LOGIC[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116m--- COBOL Payroll Calculator ---[0m[38;2;230;219;116m"[0m[38;2;248;248;242m.[0m
|
||||
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mEnter Employee Name: [0m[38;2;230;219;116m"[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mACCEPT[0m[38;2;248;248;242m [0m[38;2;248;248;242mEMPLOYEE-NAME[0m[38;2;248;248;242m.[0m
|
||||
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mEnter Hours Worked (e.g., 40.5): [0m[38;2;230;219;116m"[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mACCEPT[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURS-WORKED[0m[38;2;248;248;242m.[0m
|
||||
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mEnter Hourly Rate (e.g., 15.75): [0m[38;2;230;219;116m"[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mACCEPT[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURLY-RATE[0m[38;2;248;248;242m.[0m
|
||||
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mPERFORM[0m[38;2;248;248;242m [0m[38;2;248;248;242mCALCULATE-GROSS-PAY[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mPERFORM[0m[38;2;248;248;242m [0m[38;2;248;248;242mCALCULATE-TAX[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mPERFORM[0m[38;2;248;248;242m [0m[38;2;248;248;242mCALCULATE-NET-PAY[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mPERFORM[0m[38;2;248;248;242m [0m[38;2;248;248;242mDISPLAY-RESULTS[0m[38;2;248;248;242m.[0m
|
||||
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mSTOP RUN[0m[38;2;248;248;242m.[0m
|
||||
|
||||
[38;2;248;248;242mCALCULATE-GROSS-PAY[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mIF[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURS-WORKED[0m[38;2;248;248;242m [0m[38;2;249;38;114m>[0m[38;2;248;248;242m [0m[38;2;190;132;255m40[0m[38;2;248;248;242m [0m[38;2;249;38;114mTHEN[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mCOMPUTE[0m[38;2;248;248;242m [0m[38;2;248;248;242mOVERTIME-HOURS[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURS-WORKED[0m[38;2;248;248;242m [0m[38;2;249;38;114m-[0m[38;2;248;248;242m [0m[38;2;190;132;255m40[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mCOMPUTE[0m[38;2;248;248;242m [0m[38;2;248;248;242mOVERTIME-PAY[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;248;248;242mOVERTIME-HOURS[0m[38;2;248;248;242m [0m[38;2;249;38;114m*[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURLY-RATE[0m[38;2;248;248;242m [0m[38;2;249;38;114m*[0m[38;2;248;248;242m [0m[38;2;190;132;255m1.5[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mCOMPUTE[0m[38;2;248;248;242m [0m[38;2;248;248;242mGROSS-PAY[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m ([0m[38;2;190;132;255m40[0m[38;2;248;248;242m [0m[38;2;249;38;114m*[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURLY-RATE[0m[38;2;248;248;242m) [0m[38;2;249;38;114m+[0m[38;2;248;248;242m [0m[38;2;248;248;242mOVERTIME-PAY[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mELSE[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mCOMPUTE[0m[38;2;248;248;242m [0m[38;2;248;248;242mGROSS-PAY[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURS-WORKED[0m[38;2;248;248;242m [0m[38;2;249;38;114m*[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURLY-RATE[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mEND-IF[0m[38;2;248;248;242m.[0m
|
||||
|
||||
[38;2;248;248;242mCALCULATE-TAX[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mCOMPUTE[0m[38;2;248;248;242m [0m[38;2;248;248;242mTAX-AMOUNT[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;248;248;242mGROSS-PAY[0m[38;2;248;248;242m [0m[38;2;249;38;114m*[0m[38;2;248;248;242m [0m[38;2;248;248;242mTAX-RATE[0m[38;2;248;248;242m.[0m
|
||||
|
||||
[38;2;248;248;242mCALCULATE-NET-PAY[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mCOMPUTE[0m[38;2;248;248;242m [0m[38;2;248;248;242mNET-PAY[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;248;248;242mGROSS-PAY[0m[38;2;248;248;242m [0m[38;2;249;38;114m-[0m[38;2;248;248;242m [0m[38;2;248;248;242mTAX-AMOUNT[0m[38;2;248;248;242m.[0m
|
||||
|
||||
[38;2;248;248;242mDISPLAY-RESULTS[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116m----------------------------------[0m[38;2;230;219;116m"[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mEmployee Name: [0m[38;2;230;219;116m"[0m[38;2;248;248;242m [0m[38;2;248;248;242mEMPLOYEE-NAME[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mHours Worked: [0m[38;2;230;219;116m"[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURS-WORKED[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mHourly Rate: [0m[38;2;230;219;116m"[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURLY-RATE[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mGross Pay: [0m[38;2;230;219;116m"[0m[38;2;248;248;242m [0m[38;2;248;248;242mGROSS-PAY[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mTax (10%): [0m[38;2;230;219;116m"[0m[38;2;248;248;242m [0m[38;2;248;248;242mTAX-AMOUNT[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mNet Pay: [0m[38;2;230;219;116m"[0m[38;2;248;248;242m [0m[38;2;248;248;242mNET-PAY[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116m----------------------------------[0m[38;2;230;219;116m"[0m[38;2;248;248;242m.[0m
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
[38;2;249;38;114mIDENTIFICATION DIVISION[0m[38;2;248;248;242m.[0m
|
||||
[38;2;249;38;114mPROGRAM-ID[0m[38;2;248;248;242m. [0m[38;2;248;248;242mPAYROLL-CALC[0m[38;2;248;248;242m.[0m
|
||||
|
||||
[38;2;249;38;114mDATA DIVISION[0m[38;2;248;248;242m.[0m
|
||||
[38;2;249;38;114mWORKING-STORAGE SECTION[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m01[0m[38;2;248;248;242m [0m[38;2;248;248;242mEMPLOYEE-DETAILS[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m05[0m[38;2;248;248;242m [0m[38;2;248;248;242mEMPLOYEE-NAME[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mPIC X(30).[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m05[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURS-WORKED[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mPIC 99V9[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m05[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURLY-RATE[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mPIC 99V99[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m01[0m[38;2;248;248;242m [0m[38;2;248;248;242mPAY-CALCULATIONS[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m05[0m[38;2;248;248;242m [0m[38;2;248;248;242mGROSS-PAY[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mPIC 9(5)V99.[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m05[0m[38;2;248;248;242m [0m[38;2;248;248;242mOVERTIME-HOURS[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mPIC 99V9[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m05[0m[38;2;248;248;242m [0m[38;2;248;248;242mOVERTIME-PAY[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mPIC 9(5)V99.[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m05[0m[38;2;248;248;242m [0m[38;2;248;248;242mTAX-RATE[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mPIC V99[0m[38;2;248;248;242m [0m[38;2;249;38;114mVALUE[0m[38;2;248;248;242m [0m[38;2;190;132;255m0.10[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m05[0m[38;2;248;248;242m [0m[38;2;248;248;242mTAX-AMOUNT[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mPIC 9(5)V99.[0m
|
||||
[38;2;248;248;242m [0m[38;2;190;132;255m05[0m[38;2;248;248;242m [0m[38;2;248;248;242mNET-PAY[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mPIC 9(5)V99.[0m
|
||||
|
||||
[38;2;249;38;114mPROCEDURE DIVISION[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242mMAIN-LOGIC[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116m--- COBOL Payroll Calculator ---[0m[38;2;230;219;116m"[0m[38;2;248;248;242m.[0m
|
||||
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mEnter Employee Name: [0m[38;2;230;219;116m"[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mACCEPT[0m[38;2;248;248;242m [0m[38;2;248;248;242mEMPLOYEE-NAME[0m[38;2;248;248;242m.[0m
|
||||
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mEnter Hours Worked (e.g., 40.5): [0m[38;2;230;219;116m"[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mACCEPT[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURS-WORKED[0m[38;2;248;248;242m.[0m
|
||||
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mEnter Hourly Rate (e.g., 15.75): [0m[38;2;230;219;116m"[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mACCEPT[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURLY-RATE[0m[38;2;248;248;242m.[0m
|
||||
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mPERFORM[0m[38;2;248;248;242m [0m[38;2;248;248;242mCALCULATE-GROSS-PAY[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mPERFORM[0m[38;2;248;248;242m [0m[38;2;248;248;242mCALCULATE-TAX[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mPERFORM[0m[38;2;248;248;242m [0m[38;2;248;248;242mCALCULATE-NET-PAY[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mPERFORM[0m[38;2;248;248;242m [0m[38;2;248;248;242mDISPLAY-RESULTS[0m[38;2;248;248;242m.[0m
|
||||
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mSTOP RUN[0m[38;2;248;248;242m.[0m
|
||||
|
||||
[38;2;248;248;242mCALCULATE-GROSS-PAY[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mIF[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURS-WORKED[0m[38;2;248;248;242m [0m[38;2;249;38;114m>[0m[38;2;248;248;242m [0m[38;2;190;132;255m40[0m[38;2;248;248;242m [0m[38;2;249;38;114mTHEN[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mCOMPUTE[0m[38;2;248;248;242m [0m[38;2;248;248;242mOVERTIME-HOURS[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURS-WORKED[0m[38;2;248;248;242m [0m[38;2;249;38;114m-[0m[38;2;248;248;242m [0m[38;2;190;132;255m40[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mCOMPUTE[0m[38;2;248;248;242m [0m[38;2;248;248;242mOVERTIME-PAY[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;248;248;242mOVERTIME-HOURS[0m[38;2;248;248;242m [0m[38;2;249;38;114m*[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURLY-RATE[0m[38;2;248;248;242m [0m[38;2;249;38;114m*[0m[38;2;248;248;242m [0m[38;2;190;132;255m1.5[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mCOMPUTE[0m[38;2;248;248;242m [0m[38;2;248;248;242mGROSS-PAY[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m ([0m[38;2;190;132;255m40[0m[38;2;248;248;242m [0m[38;2;249;38;114m*[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURLY-RATE[0m[38;2;248;248;242m) [0m[38;2;249;38;114m+[0m[38;2;248;248;242m [0m[38;2;248;248;242mOVERTIME-PAY[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mELSE[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mCOMPUTE[0m[38;2;248;248;242m [0m[38;2;248;248;242mGROSS-PAY[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURS-WORKED[0m[38;2;248;248;242m [0m[38;2;249;38;114m*[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURLY-RATE[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mEND-IF[0m[38;2;248;248;242m.[0m
|
||||
|
||||
[38;2;248;248;242mCALCULATE-TAX[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mCOMPUTE[0m[38;2;248;248;242m [0m[38;2;248;248;242mTAX-AMOUNT[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;248;248;242mGROSS-PAY[0m[38;2;248;248;242m [0m[38;2;249;38;114m*[0m[38;2;248;248;242m [0m[38;2;248;248;242mTAX-RATE[0m[38;2;248;248;242m.[0m
|
||||
|
||||
[38;2;248;248;242mCALCULATE-NET-PAY[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mCOMPUTE[0m[38;2;248;248;242m [0m[38;2;248;248;242mNET-PAY[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;248;248;242mGROSS-PAY[0m[38;2;248;248;242m [0m[38;2;249;38;114m-[0m[38;2;248;248;242m [0m[38;2;248;248;242mTAX-AMOUNT[0m[38;2;248;248;242m.[0m
|
||||
|
||||
[38;2;248;248;242mDISPLAY-RESULTS[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116m----------------------------------[0m[38;2;230;219;116m"[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mEmployee Name: [0m[38;2;230;219;116m"[0m[38;2;248;248;242m [0m[38;2;248;248;242mEMPLOYEE-NAME[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mHours Worked: [0m[38;2;230;219;116m"[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURS-WORKED[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mHourly Rate: [0m[38;2;230;219;116m"[0m[38;2;248;248;242m [0m[38;2;248;248;242mHOURLY-RATE[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mGross Pay: [0m[38;2;230;219;116m"[0m[38;2;248;248;242m [0m[38;2;248;248;242mGROSS-PAY[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mTax (10%): [0m[38;2;230;219;116m"[0m[38;2;248;248;242m [0m[38;2;248;248;242mTAX-AMOUNT[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mNet Pay: [0m[38;2;230;219;116m"[0m[38;2;248;248;242m [0m[38;2;248;248;242mNET-PAY[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mDISPLAY[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116m----------------------------------[0m[38;2;230;219;116m"[0m[38;2;248;248;242m.[0m
|
||||
+5
-5
@@ -1,7 +1,7 @@
|
||||
[3;38;2;253;151;31mfirst[0m[38;2;253;151;31m,[0m[38;2;102;217;239mlast[0m[38;2;253;151;31m,[0m[38;2;190;132;255maddress[0m[38;2;253;151;31m,[0m[38;2;249;38;114mcity[0m[38;2;253;151;31m,[0m[38;2;230;219;116mzip[0m
|
||||
[3;38;2;253;151;31mJohn[0m[38;2;253;151;31m,[0m[38;2;102;217;239mDoe[0m[38;2;253;151;31m,[0m[38;2;190;132;255m120 any st.[0m[38;2;253;151;31m,[0m[38;2;230;219;116m"[0m[38;2;230;219;116mAnytown, WW[0m[38;2;230;219;116m"[0m[38;2;253;151;31m,[0m[38;2;230;219;116m08123[0m
|
||||
[3;38;2;253;151;31mJohn[0m[38;2;253;151;31m,[0m[38;2;102;217;239mDoe[0m[38;2;253;151;31m,[0m[38;2;190;132;255m120 any st.[0m[38;2;253;151;31m,[0m[38;2;249;38;114m"[0m[38;2;249;38;114mAnytown, WW[0m[38;2;249;38;114m"[0m[38;2;253;151;31m,[0m[38;2;230;219;116m08123[0m
|
||||
[3;38;2;253;151;31ma[0m[38;2;253;151;31m,[0m[38;2;102;217;239mb[0m
|
||||
[3;38;2;253;151;31m1[0m[38;2;253;151;31m,[0m[38;2;230;219;116m"[0m[38;2;230;219;116mha [0m
|
||||
[38;2;190;132;255m""[0m[38;2;230;219;116mha[0m[38;2;190;132;255m""[0m[38;2;230;219;116m [0m
|
||||
[38;2;230;219;116mha[0m[38;2;230;219;116m"[0m[38;2;253;151;31m,[0m[38;2;190;132;255m120 any st.[0m[38;2;253;151;31m,[0m[38;2;230;219;116m"[0m[38;2;230;219;116mAnytown, WW[0m[38;2;230;219;116m"[0m[38;2;253;151;31m,[0m[38;2;230;219;116m08123[0m
|
||||
[3;38;2;253;151;31m3[0m[38;2;253;151;31m,[0m[38;2;102;217;239m4[0m[38;2;253;151;31m,[0m[38;2;190;132;255m120 any st.[0m[38;2;253;151;31m,[0m[38;2;230;219;116m"[0m[38;2;230;219;116mAnytown, WW[0m[38;2;230;219;116m"[0m[38;2;253;151;31m,[0m[38;2;230;219;116m08123[0m
|
||||
[3;38;2;253;151;31m1[0m[38;2;253;151;31m,[0m[38;2;102;217;239m"[0m[38;2;102;217;239mha [0m
|
||||
[38;2;190;132;255m""[0m[38;2;102;217;239mha[0m[38;2;190;132;255m""[0m[38;2;102;217;239m [0m
|
||||
[38;2;102;217;239mha[0m[38;2;102;217;239m"[0m[38;2;253;151;31m,[0m[38;2;190;132;255m120 any st.[0m[38;2;253;151;31m,[0m[38;2;249;38;114m"[0m[38;2;249;38;114mAnytown, WW[0m[38;2;249;38;114m"[0m[38;2;253;151;31m,[0m[38;2;230;219;116m08123[0m
|
||||
[3;38;2;253;151;31m3[0m[38;2;253;151;31m,[0m[38;2;102;217;239m4[0m[38;2;253;151;31m,[0m[38;2;190;132;255m120 any st.[0m[38;2;253;151;31m,[0m[38;2;249;38;114m"[0m[38;2;249;38;114mAnytown, WW[0m[38;2;249;38;114m"[0m[38;2;253;151;31m,[0m[38;2;230;219;116m08123[0m
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 177.
|
+1
-1
@@ -1,3 +1,3 @@
|
||||
[3;38;2;253;151;31mfoo[0m[38;2;253;151;31m [0m[38;2;102;217;239mbar[0m[38;2;253;151;31m [0m[38;2;190;132;255mbaz|;,[0m[38;2;253;151;31m [0m[38;2;249;38;114mtest[0m[38;2;253;151;31m [0m[38;2;230;219;116mhello world[0m[38;2;253;151;31m [0m[3;38;2;253;151;31mtsv[0m
|
||||
[3;38;2;253;151;31m1,2[0m[38;2;253;151;31m [0m[38;2;102;217;239m1,7[0m[38;2;253;151;31m [0m[38;2;190;132;255m2,7[0m[38;2;253;151;31m [0m[38;2;249;38;114ma b c[0m[38;2;253;151;31m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mhello again[0m[38;2;230;219;116m"[0m[38;2;253;151;31m [0m[3;38;2;253;151;31mtsv[0m
|
||||
[3;38;2;230;219;116m"[0m[3;38;2;230;219;116m;|,[0m[3;38;2;230;219;116m"[0m[38;2;253;151;31m [0m[38;2;102;217;239m;|,[0m[38;2;253;151;31m [0m[38;2;190;132;255mbaz[0m[38;2;253;151;31m [0m[38;2;249;38;114mtest[0m[38;2;253;151;31m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mhello world[0m[38;2;230;219;116m"[0m[38;2;253;151;31m [0m[3;38;2;253;151;31mtsv[0m
|
||||
[3;38;2;253;151;31m"[0m[3;38;2;253;151;31m;|,[0m[3;38;2;253;151;31m"[0m[38;2;253;151;31m [0m[38;2;102;217;239m;|,[0m[38;2;253;151;31m [0m[38;2;190;132;255mbaz[0m[38;2;253;151;31m [0m[38;2;249;38;114mtest[0m[38;2;253;151;31m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mhello world[0m[38;2;230;219;116m"[0m[38;2;253;151;31m [0m[3;38;2;253;151;31mtsv[0m
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 218.
|
@@ -0,0 +1,399 @@
|
||||
[38;2;190;132;255mUWSM[0m[38;2;249;38;114m([0m[38;2;230;219;116m1[0m[38;2;249;38;114m)[0m[38;2;248;248;242m [0m[38;2;253;151;31mGeneral[0m[38;2;253;151;31m [0m[38;2;253;151;31mCommands[0m[38;2;253;151;31m [0m[38;2;253;151;31mManual[0m[38;2;248;248;242m [0m[38;2;190;132;255mUWSM[0m[38;2;249;38;114m([0m[38;2;230;219;116m1[0m[38;2;249;38;114m)[0m
|
||||
|
||||
[38;2;253;151;31mNAME[0m
|
||||
[38;2;248;248;242m UWSM - Universal Wayland Session Manager.[0m
|
||||
|
||||
[38;2;253;151;31mSYNOPSIS[0m
|
||||
[38;2;248;248;242m uwsm [0m[38;2;248;248;242m[[0m[38;2;166;226;46m-h[0m[38;2;249;38;114m|[0m[38;2;166;226;46m-v[0m[38;2;248;248;242m][0m[38;2;248;248;242m {subcommand} [0m[38;2;248;248;242m[[0m[38;2;248;248;242moptions ...[0m[38;2;248;248;242m][0m
|
||||
|
||||
[38;2;253;151;31mDESCRIPTION[0m
|
||||
[38;2;248;248;242m Launches arbitrary wayland compositor via a set of systemd user units to provide graphical user[0m
|
||||
[38;2;248;248;242m session with environment management, XDG autostart support, clean shutdown. Provides helpers[0m
|
||||
[38;2;248;248;242m for launching applications as scopes or services.[0m
|
||||
|
||||
[38;2;253;151;31mSUBCOMMANDS[0m
|
||||
[38;2;248;248;242m select Select default compositor Entry.[0m
|
||||
[38;2;248;248;242m start Start compositor and graphical session.[0m
|
||||
[38;2;248;248;242m finalize Send compositor-set variables and unit startup notification to systemd user manager.[0m
|
||||
[38;2;248;248;242m stop Stop graphical session and compositor.[0m
|
||||
[38;2;248;248;242m app Application unit launcher (with Desktop Entry support).[0m
|
||||
[38;2;248;248;242m check Perform state checks (for scripting and info).[0m
|
||||
[38;2;248;248;242m aux Technical functions for use inside units.[0m
|
||||
|
||||
[38;2;248;248;242m See corresponding SUBCOMMANDS subsections below for further info.[0m
|
||||
|
||||
[38;2;248;248;242m Help for each subcommand is accessible by running "uwsm {subcommand} [0m[38;2;166;226;46m-h[0m[38;2;248;248;242m".[0m
|
||||
|
||||
[38;2;253;151;31mCONFIGURATION[0m
|
||||
[38;2;248;248;242m Files[0m
|
||||
[38;2;248;248;242m In XDG config hierarchy:[0m
|
||||
[38;2;248;248;242m uwsm/env[0m
|
||||
[38;2;248;248;242m uwsm/env.d/*[0m
|
||||
[38;2;248;248;242m uwsm/env-${compositor}[0m
|
||||
[38;2;248;248;242m uwsm/env-${compositor}.d/* Environment (shell) to be sourced for the graphical session.[0m
|
||||
[38;2;248;248;242m Sourced from directories of increasing priority, in each directory[0m
|
||||
[38;2;248;248;242m common file is sourced first, then suffixed files in the order of[0m
|
||||
[38;2;248;248;242m items listed in XDG_CURRENT_SESSION var (lowercased).[0m
|
||||
[38;2;248;248;242m uwsm/default-id Stores Desktop Entry ID of default compositor.[0m
|
||||
|
||||
[38;2;248;248;242m Fallback is also extended into the system part of XDG data hierarchy, this can be used for dis‐[0m
|
||||
[38;2;248;248;242m tro level defaults.[0m
|
||||
|
||||
[38;2;248;248;242m Environment vars[0m
|
||||
[38;2;248;248;242m UWSM_UNIT_RUNG (run|home)[0m
|
||||
[38;2;248;248;242m Which rung of systemd/user/ hierarchy to manage generated unit[0m
|
||||
[38;2;248;248;242m and drop-in files in: [0m[38;2;255;255;255m$[0m[38;2;190;132;255mXDG_RUNTIME_DIR[0m[38;2;248;248;242m or [0m[38;2;255;255;255m$[0m[38;2;190;132;255mXDG_CONFIG_HOME[0m[38;2;248;248;242m.[0m
|
||||
[38;2;248;248;242m UWSM_TWEAKS (boolean value)[0m
|
||||
[38;2;248;248;242m Set to False to remove and not generate tweak drop-ins for[0m
|
||||
[38;2;248;248;242m other software.[0m
|
||||
[38;2;248;248;242m UWSM_FINALIZE_VARNAMES (whitespace-separated names of env vars)[0m
|
||||
[38;2;248;248;242m Additional variables for "uwsm finalize".[0m
|
||||
[38;2;248;248;242m UWSM_WAIT_VARNAMES (whitespace-separated names of env vars)[0m
|
||||
[38;2;248;248;242m Variables to wait for in activation environment before proceed‐[0m
|
||||
[38;2;248;248;242m ing to graphical session (in addition to WAYLAND_DISPLAY).[0m
|
||||
[38;2;248;248;242m UWSM_WAIT_VARNAMES_TIMEOUT (int value)[0m
|
||||
[38;2;248;248;242m Seconds to wait for variables to appear in activation environ‐[0m
|
||||
[38;2;248;248;242m ment. Essentially, startup timeout (default: 10).[0m
|
||||
[38;2;248;248;242m UWSM_WAIT_VARNAMES_SETTLETIME (float value)[0m
|
||||
[38;2;248;248;242m Seconds to pause after all expected vars found in activation[0m
|
||||
[38;2;248;248;242m environment (default: 0.2).[0m
|
||||
[38;2;248;248;242m UWSM_APP_UNIT_TYPE (scope|service)[0m
|
||||
[38;2;248;248;242m Default unit type for launching apps (default: scope).[0m
|
||||
[38;2;248;248;242m UWSM_SILENT_START (int or boolean value)[0m
|
||||
[38;2;248;248;242m True or 1 to inhibit stdout messages from "uwsm start". 2 to[0m
|
||||
[38;2;248;248;242m also inhibit warnings.[0m
|
||||
[38;2;248;248;242m DEBUG (int or boolean value)[0m
|
||||
[38;2;248;248;242m True or positive number to dump debug info to stderr.[0m
|
||||
|
||||
[38;2;253;151;31mOPERATION OVERVIEW[0m
|
||||
[38;2;248;248;242m Login Sequence Integration[0m
|
||||
[38;2;248;248;242m uwsm can be launched by using conditional exec in shell profile to replace login shell (see[0m
|
||||
[38;2;248;248;242m Shell Profile Integration section).[0m
|
||||
|
||||
[38;2;248;248;242m Alternatively "uwsm start ..." command can be put into wayland session's Desktop Entry to be[0m
|
||||
[38;2;248;248;242m launched by a display manager (see Use Inside Desktop Entry section).[0m
|
||||
|
||||
[38;2;248;248;242m Compositor Selection[0m
|
||||
[38;2;248;248;242m uwsm can run arbitrary compositor command line or a Desktop Entry by ID (specifying Action ID[0m
|
||||
[38;2;248;248;242m is also supported).[0m
|
||||
|
||||
[38;2;248;248;242m Desktop Entry can also be selected via a whiptail menu (see select subcommand section).[0m
|
||||
|
||||
[38;2;248;248;242m Startup[0m
|
||||
[38;2;248;248;242m See start subcommand section for command syntax.[0m
|
||||
|
||||
[38;2;248;248;242m UWSM uses a set of units bound to standard user session targets:[0m
|
||||
|
||||
[38;2;248;248;242m • wayland-session-pre@.target (bound to graphical-session-pre.target)[0m
|
||||
[38;2;248;248;242m • wayland-wm-env@.service (environment preloader service)[0m
|
||||
[38;2;248;248;242m • wayland-session@.target (bound to graphical-session.target)[0m
|
||||
[38;2;248;248;242m • wayland-wm@.service (service for the selected compositor)[0m
|
||||
[38;2;248;248;242m • wayland-session-xdg-autostart@.target (bound to xdg-desktop-autostart.target)[0m
|
||||
[38;2;248;248;242m • wayland-session-envelope@.target (lives through entire lifecycle)[0m
|
||||
[38;2;248;248;242m • wayland-session-shutdown.target (conflicts with targets above for shutdown)[0m
|
||||
[38;2;248;248;242m • wayland-session-bindpid@.service (PID-tracking session killswitch)[0m
|
||||
[38;2;248;248;242m • wayland-session-waitenv.service (delays graphical session until vars appear)[0m
|
||||
|
||||
[38;2;248;248;242m Compositor ID (Desktop Entry ID or executable name) becomes the specifier for all templated[0m
|
||||
[38;2;248;248;242m units.[0m
|
||||
|
||||
[38;2;248;248;242m At the stage of graphical-session-pre.target, the environment saved from "uwsm start" context[0m
|
||||
[38;2;248;248;242m is loaded (or POSIX shell profile is sourced), uwsm environment files are sourced. The delta is[0m
|
||||
[38;2;248;248;242m exported to the systemd and D-Bus activation environments by the environment preloader service[0m
|
||||
[38;2;248;248;242m and is marked for cleanup at shutdown stage. Preloader shell context for convenience has[0m
|
||||
[38;2;248;248;242m IN_UWSM_ENV_PRELOADER var set to true.[0m
|
||||
|
||||
[38;2;248;248;242m At the stage of graphical-session.target (before it) the main compositor unit wayland-[0m
|
||||
[38;2;248;248;242m wm@${ID}.service and wayland-session-waitenv.service are started.[0m
|
||||
|
||||
[38;2;248;248;242m Compositor should at least put WAYLAND_DISPLAY variable to systemd activation environment. This[0m
|
||||
[38;2;248;248;242m will trigger uwsm's automatic finalization logic. Without WAYLAND_DISPLAY in activation envi‐[0m
|
||||
[38;2;248;248;242m ronment startup will timeout in 10 seconds.[0m
|
||||
|
||||
[38;2;248;248;242m Manual finalization is possible by running "uwsm finalize" (see finalize subcommand section),[0m
|
||||
[38;2;248;248;242m also in combination with tweaking UWSM_WAIT_VARNAMES and UWSM_WAIT_VARNAMES_SETTLETIME vars[0m
|
||||
[38;2;248;248;242m (see Environment vars section).[0m
|
||||
|
||||
[38;2;248;248;242m Successful activation of compositor unit and existence of WAYLAND_DISPLAY in activation envi‐[0m
|
||||
[38;2;248;248;242m ronment will allow graphical-session.target to be declared reached.[0m
|
||||
|
||||
[38;2;248;248;242m Finally, xdg-desktop-autostart.target is activated.[0m
|
||||
|
||||
[38;2;248;248;242m Inside session[0m
|
||||
[38;2;248;248;242m It is highly recommended to configure the compositor or app launcher to launch apps as scopes[0m
|
||||
[38;2;248;248;242m or services in special user session slices (app.slice, background.slice, session.slice). uwsm[0m
|
||||
[38;2;248;248;242m provides custom nested slices for apps to live in and be terminated on session end:[0m
|
||||
[38;2;248;248;242m • app-graphical.slice[0m
|
||||
[38;2;248;248;242m • background-graphical.slice[0m
|
||||
[38;2;248;248;242m • session-graphical.slice[0m
|
||||
|
||||
[38;2;248;248;242m A helper app subcommand is provided to handle all the systemd-run invocations for you (see app[0m
|
||||
[38;2;248;248;242m subcommand section).[0m
|
||||
|
||||
[38;2;248;248;242m The compositor is launched in session.slice by default (as recommended by [0m[38;2;166;226;46msystemd.[0m[38;2;166;226;46mspecial[0m[38;2;249;38;114m([0m[38;2;190;132;255m7[0m[38;2;249;38;114m)[0m[38;2;248;248;242m).[0m
|
||||
|
||||
[38;2;248;248;242m Shutdown[0m
|
||||
[38;2;248;248;242m Can be initiated by either:[0m
|
||||
[38;2;248;248;242m • running uwsm stop[0m
|
||||
[38;2;248;248;242m • stopping wayland-wm@*.service or wayland-session-envelope@*.target[0m
|
||||
[38;2;248;248;242m • starting wayland-session-shutdown.target[0m
|
||||
|
||||
[38;2;248;248;242m Systemd stops all user units in reverse, as it usually does. During deactivation of graphical-[0m
|
||||
[38;2;248;248;242m session-pre.target, the environment preloader service cleans activation environments by unset‐[0m
|
||||
[38;2;248;248;242m ting all variables that were marked for removal during startup and finalization stages.[0m
|
||||
|
||||
[38;2;248;248;242m Do not use compositor's native exit mechanism or kill its process directly.[0m
|
||||
|
||||
[38;2;253;151;31mSUBCOMMANDS[0m
|
||||
[38;2;248;248;242m select[0m
|
||||
[38;2;248;248;242m Selects default wayland session compositor Desktop Entry.[0m
|
||||
|
||||
[38;2;248;248;242m uwsm select[0m
|
||||
|
||||
[38;2;248;248;242m Invokes a whiptail menu to select default session among Desktop Entries in wayland-sessions XDG[0m
|
||||
[38;2;248;248;242m data hierarchy. Writes to ${XDG_CONFIG_HOME}/uwsm/default-id. Nothing else is done. Returns 1[0m
|
||||
[38;2;248;248;242m if selection is cancelled. Can be used for scripting launch condition in shell profile.[0m
|
||||
|
||||
[38;2;248;248;242m check[0m
|
||||
[38;2;248;248;242m Performs tests, returns 0 on success, 1 on failure.[0m
|
||||
|
||||
[38;2;248;248;242m is-active:[0m
|
||||
|
||||
[38;2;248;248;242m uwsm check is-active [[0m[38;2;166;226;46m-h[0m[38;2;248;248;242m] [[0m[38;2;166;226;46m-v[0m[38;2;248;248;242m] [compositor][0m
|
||||
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-v[0m[38;2;248;248;242m show additional info[0m
|
||||
[38;2;248;248;242m compositor check for specific compositor[0m
|
||||
|
||||
[38;2;248;248;242m Checks if unit of specific compositor or graphical-session*.target in general is in active or[0m
|
||||
[38;2;248;248;242m activating state.[0m
|
||||
|
||||
[38;2;248;248;242m may-start:[0m
|
||||
|
||||
[38;2;248;248;242m uwsm check may-start [[0m[38;2;166;226;46m-h[0m[38;2;248;248;242m] [[0m[38;2;166;226;46m-g[0m[38;2;248;248;242m [S]] [[0m[38;2;166;226;46m-v[0m[38;2;248;248;242m|[0m[38;2;166;226;46m-q[0m[38;2;248;248;242m] [N ...][0m
|
||||
|
||||
[38;2;248;248;242m N ... allowed VT numbers (default: 1)[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-g[0m[38;2;248;248;242m S wait S seconds for graphical.target in queue (default: 60; 0 or less disables[0m
|
||||
[38;2;248;248;242m check).[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-i[0m[38;2;248;248;242m do not check for login shell[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-r[0m[38;2;248;248;242m do not check for local session (allow remote session)[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-v[0m[38;2;248;248;242m show all failed tests[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-q[0m[38;2;248;248;242m be quiet[0m
|
||||
|
||||
[38;2;248;248;242m Checks whether it is OK to launch a wayland session via the following conditions:[0m
|
||||
[38;2;248;248;242m • DBUS_SESSION_BUS_ADDRESS is set[0m
|
||||
[38;2;248;248;242m • Running from login shell[0m
|
||||
[38;2;248;248;242m • System is at graphical.target[0m
|
||||
[38;2;248;248;242m • User graphical-session*.target units are not yet active[0m
|
||||
[38;2;248;248;242m • Foreground VT is among allowed (default: 1)[0m
|
||||
[38;2;248;248;242m • Login session's VT is matching[0m
|
||||
|
||||
[38;2;248;248;242m start[0m
|
||||
[38;2;248;248;242m Generates units for given compositor command line or Desktop Entry and starts them.[0m
|
||||
|
||||
[38;2;248;248;242m uwsm start [[0m[38;2;166;226;46m-h[0m[38;2;248;248;242m] [[0m[38;2;166;226;46m-D[0m[38;2;248;248;242m name[:name...]] [[0m[38;2;166;226;46m-a[0m[38;2;248;248;242m|[0m[38;2;166;226;46m-e[0m[38;2;248;248;242m] [[0m[38;2;166;226;46m-N[0m[38;2;248;248;242m Name] [[0m[38;2;166;226;46m-C[0m[38;2;248;248;242m Comment] [[0m[38;2;166;226;46m-U[0m[38;2;248;248;242m {run|home}] [[0m[38;2;166;226;46m-t[0m[38;2;248;248;242m][0m
|
||||
[38;2;248;248;242m [[0m[38;2;166;226;46m-o[0m[38;2;248;248;242m] [[0m[38;2;166;226;46m-n[0m[38;2;248;248;242m] -- compositor [args ...][0m
|
||||
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-F[0m[38;2;248;248;242m Hardcode mode, always write command line to unit drop-ins and use full[0m
|
||||
[38;2;248;248;242m paths.[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-D[0m[38;2;248;248;242m name[:name...] Names to fill XDG_CURRENT_DESKTOP with (:[0m[38;2;166;226;46m-separated[0m[38;2;248;248;242m). Existing var con‐[0m
|
||||
[38;2;248;248;242m tent is a starting point if no active session is running.[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-a[0m[38;2;248;248;242m Append desktop names set by [0m[38;2;166;226;46m-D[0m[38;2;248;248;242m to other sources (default).[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-e[0m[38;2;248;248;242m Use desktop names set by [0m[38;2;166;226;46m-D[0m[38;2;248;248;242m exclusively, discard other sources.[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-N[0m[38;2;248;248;242m Name Fancy name for compositor (filled from Desktop Entry by default).[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-C[0m[38;2;248;248;242m Comment Fancy description for compositor (filled from Desktop Entry by de‐[0m
|
||||
[38;2;248;248;242m fault).[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-U[0m[38;2;248;248;242m {run|home} Select rung for generated unit files: run: [0m[38;2;255;255;255m$[0m[38;2;190;132;255mXDG_RUNTIME_DIR[0m[38;2;248;248;242m/sys‐[0m
|
||||
[38;2;248;248;242m temd/user (default), or home: [0m[38;2;255;255;255m$[0m[38;2;190;132;255mXDG_CONFIG_HOME[0m[38;2;248;248;242m/systemd/user. Permanent[0m
|
||||
[38;2;248;248;242m destination will save some time by removing need for reloading systemd.[0m
|
||||
[38;2;248;248;242m Managed files from other rung will be removed. Can be preset with[0m
|
||||
[38;2;248;248;242m UWSM_UNIT_RUNG environment var.[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-t[0m[38;2;248;248;242m Do not generate (and remove) tweak unit files. Can be preset with[0m
|
||||
[38;2;248;248;242m UWSM_TWEAKS=false environment var.[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-T[0m[38;2;248;248;242m Generate tweak unit files for other software. This is default behavior.[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-g[0m[38;2;248;248;242m S Wait for S seconds for system graphical.target in queue and warn if[0m
|
||||
[38;2;248;248;242m timed out or not in queue (default: 60, negative to disable).[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-G[0m[38;2;248;248;242m S Wait for S seconds for system graphical.target in queue and abort if[0m
|
||||
[38;2;248;248;242m timed out or not in queue (overrides [0m[38;2;166;226;46m-g[0m[38;2;248;248;242m, default: [0m[38;2;166;226;46m-1[0m[38;2;248;248;242m, (disabled)).[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-o[0m[38;2;248;248;242m Only generate units, but do not start.[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-n[0m[38;2;248;248;242m Dry run, do not write or start anything.[0m
|
||||
|
||||
[38;2;248;248;242m The first argument of the compositor command line acts as an ID and should be either one of:[0m
|
||||
[38;2;248;248;242m • Executable name[0m
|
||||
[38;2;248;248;242m • Desktop Entry ID (optionally with ":"[0m[38;2;166;226;46m-delimited[0m[38;2;248;248;242m action ID)[0m
|
||||
[38;2;248;248;242m • Special value:[0m
|
||||
[38;2;248;248;242m • select - invoke menu to select compositor.[0m
|
||||
[38;2;248;248;242m • default - run previously selected compositor (or select if no selection was saved).[0m
|
||||
|
||||
[38;2;248;248;242m If given as path, hardcode mode will be used implicitly.[0m
|
||||
|
||||
[38;2;248;248;242m Always use "--" to disambiguate dashed arguments intended for compositor itself.[0m
|
||||
|
||||
[38;2;248;248;242m After units are (re)generated, wayland-session-bindpid@${PID}.service is started, to track the[0m
|
||||
[38;2;248;248;242m PID of invoking uwsm, then uwsm process replaces itself with systemctl execution that starts[0m
|
||||
[38;2;248;248;242m wayland-wm@${ID}.service and waits for it to finish.[0m
|
||||
|
||||
[38;2;248;248;242m In order to complete the startup sequence, the compositor has to put WAYLAND_DISPLAY into the[0m
|
||||
[38;2;248;248;242m systemd activation environment. This can be done explicitly by making compositor run "uwsm fi‐[0m
|
||||
[38;2;248;248;242m nalize" command (see the next subsection).[0m
|
||||
|
||||
[38;2;248;248;242m finalize[0m
|
||||
[38;2;248;248;242m For running by a compositor on startup.[0m
|
||||
|
||||
[38;2;248;248;242m uwsm finalize [[0m[38;2;166;226;46m-h[0m[38;2;248;248;242m] [VAR_NAME ...][0m
|
||||
|
||||
[38;2;248;248;242m Exports WAYLAND_DISPLAY, DISPLAY and any defined vars mentioned by names in arguments or in[0m
|
||||
[38;2;248;248;242m UWSM_FINALIZE_VARNAMES variable (whitespace-separated). Then sends startup notification for the[0m
|
||||
[38;2;248;248;242m unit to systemd user manager.[0m
|
||||
|
||||
[38;2;248;248;242m This is required if compositor itself does not put WAYLAND_DISPLAY to systemd activation envi‐[0m
|
||||
[38;2;248;248;242m ronment, otherwise wayland-session@.service unit or a dedicated wayland-session-waitenv.service[0m
|
||||
[38;2;248;248;242m unit will terminate due to startup timeout.[0m
|
||||
|
||||
[38;2;248;248;242m UWSM_FINALIZE_VARNAMES variable can be prefilled by plugins.[0m
|
||||
|
||||
[38;2;248;248;242m Direct assignment as VAR_NAME=value is also possible, but recommended only for creating flags[0m
|
||||
[38;2;248;248;242m for UWSM_WAIT_VARNAMES mechanism.[0m
|
||||
|
||||
[38;2;248;248;242m stop[0m
|
||||
[38;2;248;248;242m Stops compositor and optionally removes generated units.[0m
|
||||
|
||||
[38;2;248;248;242m uwsm stop [[0m[38;2;166;226;46m-h[0m[38;2;248;248;242m] [[0m[38;2;166;226;46m-r[0m[38;2;248;248;242m [compositor] [[0m[38;2;166;226;46m-U[0m[38;2;248;248;242m {run|home}] [[0m[38;2;166;226;46m-n[0m[38;2;248;248;242m][0m
|
||||
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-r[0m[38;2;248;248;242m [compositor] Also remove units (all or only compositor-specific).[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-U[0m[38;2;248;248;242m {run|home} Select rung for generated unit files: run: [0m[38;2;255;255;255m$[0m[38;2;190;132;255mXDG_RUNTIME_DIR[0m[38;2;248;248;242m/systemd/user[0m
|
||||
[38;2;248;248;242m (default), or home: [0m[38;2;255;255;255m$[0m[38;2;190;132;255mXDG_CONFIG_HOME[0m[38;2;248;248;242m/systemd/user. Permanent destination[0m
|
||||
[38;2;248;248;242m will save some time by removing need for reloading systemd. Managed files[0m
|
||||
[38;2;248;248;242m from other rung will be removed. Can be preset with UWSM_UNIT_RUNG envi‐[0m
|
||||
[38;2;248;248;242m ronment var.[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-n[0m[38;2;248;248;242m Dry run, do not stop or remove anything.[0m
|
||||
|
||||
[38;2;248;248;242m app[0m
|
||||
[38;2;248;248;242m Application-to-unit launcher with Desktop Entry support.[0m
|
||||
|
||||
[38;2;248;248;242m uwsm app [[0m[38;2;166;226;46m-h[0m[38;2;248;248;242m] [[0m[38;2;166;226;46m-s[0m[38;2;248;248;242m {a,b,s,custom.slice}] [[0m[38;2;166;226;46m-t[0m[38;2;248;248;242m {scope,service}] [[0m[38;2;166;226;46m-a[0m[38;2;248;248;242m app_name] [[0m[38;2;166;226;46m-u[0m[38;2;248;248;242m unit_name][0m
|
||||
[38;2;248;248;242m [[0m[38;2;166;226;46m-d[0m[38;2;248;248;242m unit_description] [[0m[38;2;166;226;46m-S[0m[38;2;248;248;242m ] [[0m[38;2;166;226;46m-T[0m[38;2;248;248;242m] -- application [args ...][0m
|
||||
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-s[0m[38;2;248;248;242m {a,b,s,custom.slice} Slice selector (default: a):[0m
|
||||
[38;2;248;248;242m a - app-graphical.slice[0m
|
||||
[38;2;248;248;242m b - background-graphical.slice[0m
|
||||
[38;2;248;248;242m s - session-graphical.slice[0m
|
||||
[38;2;248;248;242m any slice by full name[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-t[0m[38;2;248;248;242m {scope,service} Type of unit to launch (default: scope, can be preset by[0m
|
||||
[38;2;248;248;242m UWSM_APP_UNIT_TYPE env var).[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-a[0m[38;2;248;248;242m app_name Override app name (a substring in unit name).[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-u[0m[38;2;248;248;242m unit_name Override the whole autogenerated unit name.[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-d[0m[38;2;248;248;242m unit_description Unit Description.[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-p[0m[38;2;248;248;242m Property=value Set additional unit property (option is repeatable).[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-S[0m[38;2;248;248;242m {out,err,both} Silence stdout, stderr, or both.[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46m-T[0m[38;2;248;248;242m Launch app in a terminal. Allows command to be empty to just[0m
|
||||
[38;2;248;248;242m launch a terminal.[0m
|
||||
|
||||
[38;2;248;248;242m Application can be provided as a command with optional arguments, or a Desktop Entry ID, op‐[0m
|
||||
[38;2;248;248;242m tionally suffixed with ":"[0m[38;2;166;226;46m-delimited[0m[38;2;248;248;242m Action ID. If Desktop Entry is being launched, arguments[0m
|
||||
[38;2;248;248;242m should be compatible with it.[0m
|
||||
|
||||
[38;2;248;248;242m Always use "--" to disambiguate dashed arguments intended for application itself.[0m
|
||||
|
||||
[38;2;248;248;242m aux[0m
|
||||
[38;2;248;248;242m For use in systemd user services. Can only be called by systemd user manager.[0m
|
||||
|
||||
[38;2;248;248;242m prepare-env Prepares environment (for use in ExecStart in wayland-wm-env@.service bound to[0m
|
||||
[38;2;248;248;242m wayland-session-pre@.target).[0m
|
||||
[38;2;248;248;242m cleanup-env Cleans up environment (for use ExecStop in in wayland-wm-env@.service bound to[0m
|
||||
[38;2;248;248;242m wayland-session-pre@.target).[0m
|
||||
[38;2;248;248;242m exec Executes a command with arguments or a desktop entry (for use in Exec in wayland-[0m
|
||||
[38;2;248;248;242m wm@.service bound to wayland-session@.target).[0m
|
||||
[38;2;248;248;242m app-daemon Daemon for faster app argument generation, used by uwsm-app client.[0m
|
||||
|
||||
[38;2;253;151;31mAPP DAEMON[0m
|
||||
[38;2;248;248;242m Provided as wayland-wm-app-daemon.service to be started on-demand.[0m
|
||||
|
||||
[38;2;248;248;242m Daemon receives app arguments from ${XDG_RUNTIME_DIR}/uwsm-app-daemon-in pipe. Resulting argu‐[0m
|
||||
[38;2;248;248;242m ments are formatted as shell code and written to ${XDG_RUNTIME_DIR}/uwsm-app-daemon-out pipe.[0m
|
||||
|
||||
[38;2;248;248;242m Arguments are expected to be \0-delimited, leading \0 are stripped. One command is received per[0m
|
||||
[38;2;248;248;242m write+close.[0m
|
||||
|
||||
[38;2;248;248;242m The first argument determines the behavior:[0m
|
||||
|
||||
[38;2;248;248;242m • app the rest is processed the same as in "uwsm app"[0m
|
||||
[38;2;248;248;242m • ping just "pong" is returnedn[0m
|
||||
[38;2;248;248;242m • stop daemon is stoppedn[0m
|
||||
|
||||
[38;2;248;248;242m Single commands are prepended with exec, iterated commands are assembled with trailing & each,[0m
|
||||
[38;2;248;248;242m followed by wait.[0m
|
||||
|
||||
[38;2;248;248;242m The purpose of all this is to skip all the expensive Python startup and import routines that[0m
|
||||
[38;2;248;248;242m slow things down every time "uwsm app" is called. Instead the daemon does it once and then lis‐[0m
|
||||
[38;2;248;248;242m tens for requests, while a simple shell script may dump arguments to one pipe and run the code[0m
|
||||
[38;2;248;248;242m received from another via eval, which is much faster.[0m
|
||||
|
||||
[38;2;248;248;242m The simplest script is:[0m
|
||||
|
||||
[38;2;248;248;242m #!/bin/sh[0m
|
||||
[38;2;248;248;242m printf '0%s' app "$@" > "${XDG_RUNTIME_DIR}/uwsm-app-daemon-in"[0m
|
||||
[38;2;248;248;242m IFS='' read [0m[38;2;166;226;46m-r[0m[38;2;248;248;242m cmd < "${XDG_RUNTIME_DIR}/uwsm-app-daemon-out"[0m
|
||||
[38;2;248;248;242m eval "[0m[38;2;255;255;255m$[0m[38;2;190;132;255mcmd[0m[38;2;248;248;242m"[0m
|
||||
|
||||
[38;2;248;248;242m Provided uwsm-app client script is a bit smarter: it can start the daemon, applies timeouts,[0m
|
||||
[38;2;248;248;242m and supports newlines in returned args.[0m
|
||||
|
||||
[38;2;253;151;31mSHELL PROFILE INTEGRATION[0m
|
||||
[38;2;248;248;242m To launch uwsm automatically on login, add one of constructs below (or similar) to shell pro‐[0m
|
||||
[38;2;248;248;242m file.[0m
|
||||
|
||||
[38;2;248;248;242m This asks to select a compositor (or refuse and continue with login shell) when logged in on VT[0m
|
||||
[38;2;248;248;242m 1:[0m
|
||||
|
||||
[38;2;248;248;242m if uwsm check may-start && uwsm select; then[0m
|
||||
[38;2;248;248;242m exec systemd-cat [0m[38;2;166;226;46m-t[0m[38;2;248;248;242m uwsm_start uwsm start default[0m
|
||||
[38;2;248;248;242m fi[0m
|
||||
|
||||
[38;2;248;248;242m This just starts a specific compositor depending on foreground VT:[0m
|
||||
|
||||
[38;2;248;248;242m if uwsm check may-start 1; then[0m
|
||||
[38;2;248;248;242m exec systemd-cat [0m[38;2;166;226;46m-t[0m[38;2;248;248;242m uwsm_start uwsm start sway.desktop[0m
|
||||
[38;2;248;248;242m elif uwsm check may-start 2; then[0m
|
||||
[38;2;248;248;242m exec systemd-cat [0m[38;2;166;226;46m-t[0m[38;2;248;248;242m uwsm_start uwsm start labwc.desktop[0m
|
||||
[38;2;248;248;242m fi[0m
|
||||
|
||||
[38;2;248;248;242m Using "uwsm check may-start" as a condition is essential, not only to prevent accidental[0m
|
||||
[38;2;248;248;242m startup attempts where they are not expected, but also since startup may involve sourcing shell[0m
|
||||
[38;2;248;248;242m profile, which might lead to nasty loops.[0m
|
||||
|
||||
[38;2;248;248;242m See check subcommand section for info on may-start checker.[0m
|
||||
|
||||
[38;2;248;248;242m exec allows uwsm to replace login shell in order to properly bind to user session and handle[0m
|
||||
[38;2;248;248;242m session termination.[0m
|
||||
|
||||
[38;2;248;248;242m "systemd-cat [0m[38;2;166;226;46m-t[0m[38;2;248;248;242m uwsm_start" (optional) executes the command given to it (uwsm) with its stdout[0m
|
||||
[38;2;248;248;242m and stderr connected to the systemd journal, tagged with identifier "uwsm_start". See systemd-[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46mcat[0m[38;2;249;38;114m([0m[38;2;190;132;255m1[0m[38;2;249;38;114m)[0m[38;2;248;248;242m for more options.[0m
|
||||
|
||||
[38;2;253;151;31mUSE INSIDE DESKTOP ENTRY[0m
|
||||
[38;2;248;248;242m To launch uwsm from a display/login manager, "uwsm start" can be used inside Desktop Entries.[0m
|
||||
[38;2;248;248;242m Example /usr/local/share/wayland-sessions/my-compositor.desktop:[0m
|
||||
|
||||
[38;2;248;248;242m [Desktop Entry][0m
|
||||
[38;2;248;248;242m Name=My compositor (with UWSM)[0m
|
||||
[38;2;248;248;242m Comment=My cool compositor[0m
|
||||
[38;2;248;248;242m Exec=uwsm start [0m[38;2;166;226;46m-N[0m[38;2;248;248;242m "My compositor" [0m[38;2;166;226;46m-D[0m[38;2;248;248;242m mycompositor [0m[38;2;166;226;46m-C[0m[38;2;248;248;242m "My cool compositor" mywm[0m
|
||||
[38;2;248;248;242m DesktopNames=mycompositor[0m
|
||||
[38;2;248;248;242m Type=Application[0m
|
||||
|
||||
[38;2;248;248;242m Things to keep in mind:[0m
|
||||
|
||||
[38;2;248;248;242m • For consistency, command line arguments should mirror the keys of the entry[0m
|
||||
[38;2;248;248;242m • Command in Exec= should start with "uwsm start"[0m
|
||||
[38;2;248;248;242m • It should not point to itself (as a combination of Desktop Entry ID and Action ID)[0m
|
||||
[38;2;248;248;242m • It should not point to a Desktop Entry ID and Action ID that also uses ‘uwsm‘[0m
|
||||
|
||||
[38;2;248;248;242m Potentially such entries may be found and used by uwsm itself, i.e. in shell profile integra‐[0m
|
||||
[38;2;248;248;242m tion situation, or when launched manually. Following the principles above ensures uwsm will[0m
|
||||
[38;2;248;248;242m properly recognize itself and parse requested arguments inside the entry without any side ef‐[0m
|
||||
[38;2;248;248;242m fects.[0m
|
||||
|
||||
[38;2;253;151;31mSEE ALSO[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46muwsm-plugins[0m[38;2;249;38;114m([0m[38;2;190;132;255m3[0m[38;2;249;38;114m)[0m[38;2;248;248;242m, [0m[38;2;166;226;46msystemd-run[0m[38;2;249;38;114m([0m[38;2;190;132;255m1[0m[38;2;249;38;114m)[0m[38;2;248;248;242m, [0m[38;2;166;226;46msystemd-cat[0m[38;2;249;38;114m([0m[38;2;190;132;255m1[0m[38;2;249;38;114m)[0m[38;2;248;248;242m, [0m[38;2;166;226;46msystemd.[0m[38;2;166;226;46mspecial[0m[38;2;249;38;114m([0m[38;2;190;132;255m7[0m[38;2;249;38;114m)[0m
|
||||
|
||||
[38;2;248;248;242m 2026-02-14 [0m[38;2;166;226;46mUWSM[0m[38;2;249;38;114m([0m[38;2;190;132;255m1[0m[38;2;249;38;114m)[0m
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/bin/ksh
|
||||
#
|
||||
# https://www.qnx.com/developers/docs/6.4.0/neutrino/user_guide/scripts.html
|
||||
# tfind:
|
||||
# script to look for strings in various files and dump to less
|
||||
|
||||
case $# in
|
||||
1)
|
||||
find . -name '*.[ch]' | xargs grep $1 | less
|
||||
exit 0 # good status
|
||||
esac
|
||||
|
||||
echo "Use tfind stuff_to_find "
|
||||
echo " where : stuff_to_find = search string "
|
||||
echo " "
|
||||
echo "e.g. tfind console_state looks through all files in "
|
||||
echo " the current directory and below and displays all "
|
||||
echo " instances of console_state."
|
||||
exit 1 # bad status
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
IDENTIFICATION DIVISION.
|
||||
PROGRAM-ID. PAYROLL-CALC.
|
||||
|
||||
DATA DIVISION.
|
||||
WORKING-STORAGE SECTION.
|
||||
01 EMPLOYEE-DETAILS.
|
||||
05 EMPLOYEE-NAME PIC X(30).
|
||||
05 HOURS-WORKED PIC 99V9.
|
||||
05 HOURLY-RATE PIC 99V99.
|
||||
01 PAY-CALCULATIONS.
|
||||
05 GROSS-PAY PIC 9(5)V99.
|
||||
05 OVERTIME-HOURS PIC 99V9.
|
||||
05 OVERTIME-PAY PIC 9(5)V99.
|
||||
05 TAX-RATE PIC V99 VALUE 0.10.
|
||||
05 TAX-AMOUNT PIC 9(5)V99.
|
||||
05 NET-PAY PIC 9(5)V99.
|
||||
|
||||
PROCEDURE DIVISION.
|
||||
MAIN-LOGIC.
|
||||
DISPLAY "--- COBOL Payroll Calculator ---".
|
||||
|
||||
DISPLAY "Enter Employee Name: ".
|
||||
ACCEPT EMPLOYEE-NAME.
|
||||
|
||||
DISPLAY "Enter Hours Worked (e.g., 40.5): ".
|
||||
ACCEPT HOURS-WORKED.
|
||||
|
||||
DISPLAY "Enter Hourly Rate (e.g., 15.75): ".
|
||||
ACCEPT HOURLY-RATE.
|
||||
|
||||
PERFORM CALCULATE-GROSS-PAY.
|
||||
PERFORM CALCULATE-TAX.
|
||||
PERFORM CALCULATE-NET-PAY.
|
||||
PERFORM DISPLAY-RESULTS.
|
||||
|
||||
STOP RUN.
|
||||
|
||||
CALCULATE-GROSS-PAY.
|
||||
IF HOURS-WORKED > 40 THEN
|
||||
COMPUTE OVERTIME-HOURS = HOURS-WORKED - 40
|
||||
COMPUTE OVERTIME-PAY = OVERTIME-HOURS * HOURLY-RATE * 1.5
|
||||
COMPUTE GROSS-PAY = (40 * HOURLY-RATE) + OVERTIME-PAY
|
||||
ELSE
|
||||
COMPUTE GROSS-PAY = HOURS-WORKED * HOURLY-RATE
|
||||
END-IF.
|
||||
|
||||
CALCULATE-TAX.
|
||||
COMPUTE TAX-AMOUNT = GROSS-PAY * TAX-RATE.
|
||||
|
||||
CALCULATE-NET-PAY.
|
||||
COMPUTE NET-PAY = GROSS-PAY - TAX-AMOUNT.
|
||||
|
||||
DISPLAY-RESULTS.
|
||||
DISPLAY "----------------------------------".
|
||||
DISPLAY "Employee Name: " EMPLOYEE-NAME.
|
||||
DISPLAY "Hours Worked: " HOURS-WORKED.
|
||||
DISPLAY "Hourly Rate: " HOURLY-RATE.
|
||||
DISPLAY "Gross Pay: " GROSS-PAY.
|
||||
DISPLAY "Tax (10%): " TAX-AMOUNT.
|
||||
DISPLAY "Net Pay: " NET-PAY.
|
||||
DISPLAY "----------------------------------".
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
IDENTIFICATION DIVISION.
|
||||
PROGRAM-ID. PAYROLL-CALC.
|
||||
|
||||
DATA DIVISION.
|
||||
WORKING-STORAGE SECTION.
|
||||
01 EMPLOYEE-DETAILS.
|
||||
05 EMPLOYEE-NAME PIC X(30).
|
||||
05 HOURS-WORKED PIC 99V9.
|
||||
05 HOURLY-RATE PIC 99V99.
|
||||
01 PAY-CALCULATIONS.
|
||||
05 GROSS-PAY PIC 9(5)V99.
|
||||
05 OVERTIME-HOURS PIC 99V9.
|
||||
05 OVERTIME-PAY PIC 9(5)V99.
|
||||
05 TAX-RATE PIC V99 VALUE 0.10.
|
||||
05 TAX-AMOUNT PIC 9(5)V99.
|
||||
05 NET-PAY PIC 9(5)V99.
|
||||
|
||||
PROCEDURE DIVISION.
|
||||
MAIN-LOGIC.
|
||||
DISPLAY "--- COBOL Payroll Calculator ---".
|
||||
|
||||
DISPLAY "Enter Employee Name: ".
|
||||
ACCEPT EMPLOYEE-NAME.
|
||||
|
||||
DISPLAY "Enter Hours Worked (e.g., 40.5): ".
|
||||
ACCEPT HOURS-WORKED.
|
||||
|
||||
DISPLAY "Enter Hourly Rate (e.g., 15.75): ".
|
||||
ACCEPT HOURLY-RATE.
|
||||
|
||||
PERFORM CALCULATE-GROSS-PAY.
|
||||
PERFORM CALCULATE-TAX.
|
||||
PERFORM CALCULATE-NET-PAY.
|
||||
PERFORM DISPLAY-RESULTS.
|
||||
|
||||
STOP RUN.
|
||||
|
||||
CALCULATE-GROSS-PAY.
|
||||
IF HOURS-WORKED > 40 THEN
|
||||
COMPUTE OVERTIME-HOURS = HOURS-WORKED - 40
|
||||
COMPUTE OVERTIME-PAY = OVERTIME-HOURS * HOURLY-RATE * 1.5
|
||||
COMPUTE GROSS-PAY = (40 * HOURLY-RATE) + OVERTIME-PAY
|
||||
ELSE
|
||||
COMPUTE GROSS-PAY = HOURS-WORKED * HOURLY-RATE
|
||||
END-IF.
|
||||
|
||||
CALCULATE-TAX.
|
||||
COMPUTE TAX-AMOUNT = GROSS-PAY * TAX-RATE.
|
||||
|
||||
CALCULATE-NET-PAY.
|
||||
COMPUTE NET-PAY = GROSS-PAY - TAX-AMOUNT.
|
||||
|
||||
DISPLAY-RESULTS.
|
||||
DISPLAY "----------------------------------".
|
||||
DISPLAY "Employee Name: " EMPLOYEE-NAME.
|
||||
DISPLAY "Hours Worked: " HOURS-WORKED.
|
||||
DISPLAY "Hourly Rate: " HOURLY-RATE.
|
||||
DISPLAY "Gross Pay: " GROSS-PAY.
|
||||
DISPLAY "Tax (10%): " TAX-AMOUNT.
|
||||
DISPLAY "Net Pay: " NET-PAY.
|
||||
DISPLAY "----------------------------------".
|
||||
+399
@@ -0,0 +1,399 @@
|
||||
UWSM(1) General Commands Manual UWSM(1)
|
||||
|
||||
NAME
|
||||
UWSM - Universal Wayland Session Manager.
|
||||
|
||||
SYNOPSIS
|
||||
uwsm [-h|-v] {subcommand} [options ...]
|
||||
|
||||
DESCRIPTION
|
||||
Launches arbitrary wayland compositor via a set of systemd user units to provide graphical user
|
||||
session with environment management, XDG autostart support, clean shutdown. Provides helpers
|
||||
for launching applications as scopes or services.
|
||||
|
||||
SUBCOMMANDS
|
||||
select Select default compositor Entry.
|
||||
start Start compositor and graphical session.
|
||||
finalize Send compositor-set variables and unit startup notification to systemd user manager.
|
||||
stop Stop graphical session and compositor.
|
||||
app Application unit launcher (with Desktop Entry support).
|
||||
check Perform state checks (for scripting and info).
|
||||
aux Technical functions for use inside units.
|
||||
|
||||
See corresponding SUBCOMMANDS subsections below for further info.
|
||||
|
||||
Help for each subcommand is accessible by running "uwsm {subcommand} -h".
|
||||
|
||||
CONFIGURATION
|
||||
Files
|
||||
In XDG config hierarchy:
|
||||
uwsm/env
|
||||
uwsm/env.d/*
|
||||
uwsm/env-${compositor}
|
||||
uwsm/env-${compositor}.d/* Environment (shell) to be sourced for the graphical session.
|
||||
Sourced from directories of increasing priority, in each directory
|
||||
common file is sourced first, then suffixed files in the order of
|
||||
items listed in XDG_CURRENT_SESSION var (lowercased).
|
||||
uwsm/default-id Stores Desktop Entry ID of default compositor.
|
||||
|
||||
Fallback is also extended into the system part of XDG data hierarchy, this can be used for dis‐
|
||||
tro level defaults.
|
||||
|
||||
Environment vars
|
||||
UWSM_UNIT_RUNG (run|home)
|
||||
Which rung of systemd/user/ hierarchy to manage generated unit
|
||||
and drop-in files in: $XDG_RUNTIME_DIR or $XDG_CONFIG_HOME.
|
||||
UWSM_TWEAKS (boolean value)
|
||||
Set to False to remove and not generate tweak drop-ins for
|
||||
other software.
|
||||
UWSM_FINALIZE_VARNAMES (whitespace-separated names of env vars)
|
||||
Additional variables for "uwsm finalize".
|
||||
UWSM_WAIT_VARNAMES (whitespace-separated names of env vars)
|
||||
Variables to wait for in activation environment before proceed‐
|
||||
ing to graphical session (in addition to WAYLAND_DISPLAY).
|
||||
UWSM_WAIT_VARNAMES_TIMEOUT (int value)
|
||||
Seconds to wait for variables to appear in activation environ‐
|
||||
ment. Essentially, startup timeout (default: 10).
|
||||
UWSM_WAIT_VARNAMES_SETTLETIME (float value)
|
||||
Seconds to pause after all expected vars found in activation
|
||||
environment (default: 0.2).
|
||||
UWSM_APP_UNIT_TYPE (scope|service)
|
||||
Default unit type for launching apps (default: scope).
|
||||
UWSM_SILENT_START (int or boolean value)
|
||||
True or 1 to inhibit stdout messages from "uwsm start". 2 to
|
||||
also inhibit warnings.
|
||||
DEBUG (int or boolean value)
|
||||
True or positive number to dump debug info to stderr.
|
||||
|
||||
OPERATION OVERVIEW
|
||||
Login Sequence Integration
|
||||
uwsm can be launched by using conditional exec in shell profile to replace login shell (see
|
||||
Shell Profile Integration section).
|
||||
|
||||
Alternatively "uwsm start ..." command can be put into wayland session's Desktop Entry to be
|
||||
launched by a display manager (see Use Inside Desktop Entry section).
|
||||
|
||||
Compositor Selection
|
||||
uwsm can run arbitrary compositor command line or a Desktop Entry by ID (specifying Action ID
|
||||
is also supported).
|
||||
|
||||
Desktop Entry can also be selected via a whiptail menu (see select subcommand section).
|
||||
|
||||
Startup
|
||||
See start subcommand section for command syntax.
|
||||
|
||||
UWSM uses a set of units bound to standard user session targets:
|
||||
|
||||
• wayland-session-pre@.target (bound to graphical-session-pre.target)
|
||||
• wayland-wm-env@.service (environment preloader service)
|
||||
• wayland-session@.target (bound to graphical-session.target)
|
||||
• wayland-wm@.service (service for the selected compositor)
|
||||
• wayland-session-xdg-autostart@.target (bound to xdg-desktop-autostart.target)
|
||||
• wayland-session-envelope@.target (lives through entire lifecycle)
|
||||
• wayland-session-shutdown.target (conflicts with targets above for shutdown)
|
||||
• wayland-session-bindpid@.service (PID-tracking session killswitch)
|
||||
• wayland-session-waitenv.service (delays graphical session until vars appear)
|
||||
|
||||
Compositor ID (Desktop Entry ID or executable name) becomes the specifier for all templated
|
||||
units.
|
||||
|
||||
At the stage of graphical-session-pre.target, the environment saved from "uwsm start" context
|
||||
is loaded (or POSIX shell profile is sourced), uwsm environment files are sourced. The delta is
|
||||
exported to the systemd and D-Bus activation environments by the environment preloader service
|
||||
and is marked for cleanup at shutdown stage. Preloader shell context for convenience has
|
||||
IN_UWSM_ENV_PRELOADER var set to true.
|
||||
|
||||
At the stage of graphical-session.target (before it) the main compositor unit wayland-
|
||||
wm@${ID}.service and wayland-session-waitenv.service are started.
|
||||
|
||||
Compositor should at least put WAYLAND_DISPLAY variable to systemd activation environment. This
|
||||
will trigger uwsm's automatic finalization logic. Without WAYLAND_DISPLAY in activation envi‐
|
||||
ronment startup will timeout in 10 seconds.
|
||||
|
||||
Manual finalization is possible by running "uwsm finalize" (see finalize subcommand section),
|
||||
also in combination with tweaking UWSM_WAIT_VARNAMES and UWSM_WAIT_VARNAMES_SETTLETIME vars
|
||||
(see Environment vars section).
|
||||
|
||||
Successful activation of compositor unit and existence of WAYLAND_DISPLAY in activation envi‐
|
||||
ronment will allow graphical-session.target to be declared reached.
|
||||
|
||||
Finally, xdg-desktop-autostart.target is activated.
|
||||
|
||||
Inside session
|
||||
It is highly recommended to configure the compositor or app launcher to launch apps as scopes
|
||||
or services in special user session slices (app.slice, background.slice, session.slice). uwsm
|
||||
provides custom nested slices for apps to live in and be terminated on session end:
|
||||
• app-graphical.slice
|
||||
• background-graphical.slice
|
||||
• session-graphical.slice
|
||||
|
||||
A helper app subcommand is provided to handle all the systemd-run invocations for you (see app
|
||||
subcommand section).
|
||||
|
||||
The compositor is launched in session.slice by default (as recommended by systemd.special(7)).
|
||||
|
||||
Shutdown
|
||||
Can be initiated by either:
|
||||
• running uwsm stop
|
||||
• stopping wayland-wm@*.service or wayland-session-envelope@*.target
|
||||
• starting wayland-session-shutdown.target
|
||||
|
||||
Systemd stops all user units in reverse, as it usually does. During deactivation of graphical-
|
||||
session-pre.target, the environment preloader service cleans activation environments by unset‐
|
||||
ting all variables that were marked for removal during startup and finalization stages.
|
||||
|
||||
Do not use compositor's native exit mechanism or kill its process directly.
|
||||
|
||||
SUBCOMMANDS
|
||||
select
|
||||
Selects default wayland session compositor Desktop Entry.
|
||||
|
||||
uwsm select
|
||||
|
||||
Invokes a whiptail menu to select default session among Desktop Entries in wayland-sessions XDG
|
||||
data hierarchy. Writes to ${XDG_CONFIG_HOME}/uwsm/default-id. Nothing else is done. Returns 1
|
||||
if selection is cancelled. Can be used for scripting launch condition in shell profile.
|
||||
|
||||
check
|
||||
Performs tests, returns 0 on success, 1 on failure.
|
||||
|
||||
is-active:
|
||||
|
||||
uwsm check is-active [-h] [-v] [compositor]
|
||||
|
||||
-v show additional info
|
||||
compositor check for specific compositor
|
||||
|
||||
Checks if unit of specific compositor or graphical-session*.target in general is in active or
|
||||
activating state.
|
||||
|
||||
may-start:
|
||||
|
||||
uwsm check may-start [-h] [-g [S]] [-v|-q] [N ...]
|
||||
|
||||
N ... allowed VT numbers (default: 1)
|
||||
-g S wait S seconds for graphical.target in queue (default: 60; 0 or less disables
|
||||
check).
|
||||
-i do not check for login shell
|
||||
-r do not check for local session (allow remote session)
|
||||
-v show all failed tests
|
||||
-q be quiet
|
||||
|
||||
Checks whether it is OK to launch a wayland session via the following conditions:
|
||||
• DBUS_SESSION_BUS_ADDRESS is set
|
||||
• Running from login shell
|
||||
• System is at graphical.target
|
||||
• User graphical-session*.target units are not yet active
|
||||
• Foreground VT is among allowed (default: 1)
|
||||
• Login session's VT is matching
|
||||
|
||||
start
|
||||
Generates units for given compositor command line or Desktop Entry and starts them.
|
||||
|
||||
uwsm start [-h] [-D name[:name...]] [-a|-e] [-N Name] [-C Comment] [-U {run|home}] [-t]
|
||||
[-o] [-n] -- compositor [args ...]
|
||||
|
||||
-F Hardcode mode, always write command line to unit drop-ins and use full
|
||||
paths.
|
||||
-D name[:name...] Names to fill XDG_CURRENT_DESKTOP with (:-separated). Existing var con‐
|
||||
tent is a starting point if no active session is running.
|
||||
-a Append desktop names set by -D to other sources (default).
|
||||
-e Use desktop names set by -D exclusively, discard other sources.
|
||||
-N Name Fancy name for compositor (filled from Desktop Entry by default).
|
||||
-C Comment Fancy description for compositor (filled from Desktop Entry by de‐
|
||||
fault).
|
||||
-U {run|home} Select rung for generated unit files: run: $XDG_RUNTIME_DIR/sys‐
|
||||
temd/user (default), or home: $XDG_CONFIG_HOME/systemd/user. Permanent
|
||||
destination will save some time by removing need for reloading systemd.
|
||||
Managed files from other rung will be removed. Can be preset with
|
||||
UWSM_UNIT_RUNG environment var.
|
||||
-t Do not generate (and remove) tweak unit files. Can be preset with
|
||||
UWSM_TWEAKS=false environment var.
|
||||
-T Generate tweak unit files for other software. This is default behavior.
|
||||
-g S Wait for S seconds for system graphical.target in queue and warn if
|
||||
timed out or not in queue (default: 60, negative to disable).
|
||||
-G S Wait for S seconds for system graphical.target in queue and abort if
|
||||
timed out or not in queue (overrides -g, default: -1, (disabled)).
|
||||
-o Only generate units, but do not start.
|
||||
-n Dry run, do not write or start anything.
|
||||
|
||||
The first argument of the compositor command line acts as an ID and should be either one of:
|
||||
• Executable name
|
||||
• Desktop Entry ID (optionally with ":"-delimited action ID)
|
||||
• Special value:
|
||||
• select - invoke menu to select compositor.
|
||||
• default - run previously selected compositor (or select if no selection was saved).
|
||||
|
||||
If given as path, hardcode mode will be used implicitly.
|
||||
|
||||
Always use "--" to disambiguate dashed arguments intended for compositor itself.
|
||||
|
||||
After units are (re)generated, wayland-session-bindpid@${PID}.service is started, to track the
|
||||
PID of invoking uwsm, then uwsm process replaces itself with systemctl execution that starts
|
||||
wayland-wm@${ID}.service and waits for it to finish.
|
||||
|
||||
In order to complete the startup sequence, the compositor has to put WAYLAND_DISPLAY into the
|
||||
systemd activation environment. This can be done explicitly by making compositor run "uwsm fi‐
|
||||
nalize" command (see the next subsection).
|
||||
|
||||
finalize
|
||||
For running by a compositor on startup.
|
||||
|
||||
uwsm finalize [-h] [VAR_NAME ...]
|
||||
|
||||
Exports WAYLAND_DISPLAY, DISPLAY and any defined vars mentioned by names in arguments or in
|
||||
UWSM_FINALIZE_VARNAMES variable (whitespace-separated). Then sends startup notification for the
|
||||
unit to systemd user manager.
|
||||
|
||||
This is required if compositor itself does not put WAYLAND_DISPLAY to systemd activation envi‐
|
||||
ronment, otherwise wayland-session@.service unit or a dedicated wayland-session-waitenv.service
|
||||
unit will terminate due to startup timeout.
|
||||
|
||||
UWSM_FINALIZE_VARNAMES variable can be prefilled by plugins.
|
||||
|
||||
Direct assignment as VAR_NAME=value is also possible, but recommended only for creating flags
|
||||
for UWSM_WAIT_VARNAMES mechanism.
|
||||
|
||||
stop
|
||||
Stops compositor and optionally removes generated units.
|
||||
|
||||
uwsm stop [-h] [-r [compositor] [-U {run|home}] [-n]
|
||||
|
||||
-r [compositor] Also remove units (all or only compositor-specific).
|
||||
-U {run|home} Select rung for generated unit files: run: $XDG_RUNTIME_DIR/systemd/user
|
||||
(default), or home: $XDG_CONFIG_HOME/systemd/user. Permanent destination
|
||||
will save some time by removing need for reloading systemd. Managed files
|
||||
from other rung will be removed. Can be preset with UWSM_UNIT_RUNG envi‐
|
||||
ronment var.
|
||||
-n Dry run, do not stop or remove anything.
|
||||
|
||||
app
|
||||
Application-to-unit launcher with Desktop Entry support.
|
||||
|
||||
uwsm app [-h] [-s {a,b,s,custom.slice}] [-t {scope,service}] [-a app_name] [-u unit_name]
|
||||
[-d unit_description] [-S ] [-T] -- application [args ...]
|
||||
|
||||
-s {a,b,s,custom.slice} Slice selector (default: a):
|
||||
a - app-graphical.slice
|
||||
b - background-graphical.slice
|
||||
s - session-graphical.slice
|
||||
any slice by full name
|
||||
-t {scope,service} Type of unit to launch (default: scope, can be preset by
|
||||
UWSM_APP_UNIT_TYPE env var).
|
||||
-a app_name Override app name (a substring in unit name).
|
||||
-u unit_name Override the whole autogenerated unit name.
|
||||
-d unit_description Unit Description.
|
||||
-p Property=value Set additional unit property (option is repeatable).
|
||||
-S {out,err,both} Silence stdout, stderr, or both.
|
||||
-T Launch app in a terminal. Allows command to be empty to just
|
||||
launch a terminal.
|
||||
|
||||
Application can be provided as a command with optional arguments, or a Desktop Entry ID, op‐
|
||||
tionally suffixed with ":"-delimited Action ID. If Desktop Entry is being launched, arguments
|
||||
should be compatible with it.
|
||||
|
||||
Always use "--" to disambiguate dashed arguments intended for application itself.
|
||||
|
||||
aux
|
||||
For use in systemd user services. Can only be called by systemd user manager.
|
||||
|
||||
prepare-env Prepares environment (for use in ExecStart in wayland-wm-env@.service bound to
|
||||
wayland-session-pre@.target).
|
||||
cleanup-env Cleans up environment (for use ExecStop in in wayland-wm-env@.service bound to
|
||||
wayland-session-pre@.target).
|
||||
exec Executes a command with arguments or a desktop entry (for use in Exec in wayland-
|
||||
wm@.service bound to wayland-session@.target).
|
||||
app-daemon Daemon for faster app argument generation, used by uwsm-app client.
|
||||
|
||||
APP DAEMON
|
||||
Provided as wayland-wm-app-daemon.service to be started on-demand.
|
||||
|
||||
Daemon receives app arguments from ${XDG_RUNTIME_DIR}/uwsm-app-daemon-in pipe. Resulting argu‐
|
||||
ments are formatted as shell code and written to ${XDG_RUNTIME_DIR}/uwsm-app-daemon-out pipe.
|
||||
|
||||
Arguments are expected to be \0-delimited, leading \0 are stripped. One command is received per
|
||||
write+close.
|
||||
|
||||
The first argument determines the behavior:
|
||||
|
||||
• app the rest is processed the same as in "uwsm app"
|
||||
• ping just "pong" is returnedn
|
||||
• stop daemon is stoppedn
|
||||
|
||||
Single commands are prepended with exec, iterated commands are assembled with trailing & each,
|
||||
followed by wait.
|
||||
|
||||
The purpose of all this is to skip all the expensive Python startup and import routines that
|
||||
slow things down every time "uwsm app" is called. Instead the daemon does it once and then lis‐
|
||||
tens for requests, while a simple shell script may dump arguments to one pipe and run the code
|
||||
received from another via eval, which is much faster.
|
||||
|
||||
The simplest script is:
|
||||
|
||||
#!/bin/sh
|
||||
printf '0%s' app "$@" > "${XDG_RUNTIME_DIR}/uwsm-app-daemon-in"
|
||||
IFS='' read -r cmd < "${XDG_RUNTIME_DIR}/uwsm-app-daemon-out"
|
||||
eval "$cmd"
|
||||
|
||||
Provided uwsm-app client script is a bit smarter: it can start the daemon, applies timeouts,
|
||||
and supports newlines in returned args.
|
||||
|
||||
SHELL PROFILE INTEGRATION
|
||||
To launch uwsm automatically on login, add one of constructs below (or similar) to shell pro‐
|
||||
file.
|
||||
|
||||
This asks to select a compositor (or refuse and continue with login shell) when logged in on VT
|
||||
1:
|
||||
|
||||
if uwsm check may-start && uwsm select; then
|
||||
exec systemd-cat -t uwsm_start uwsm start default
|
||||
fi
|
||||
|
||||
This just starts a specific compositor depending on foreground VT:
|
||||
|
||||
if uwsm check may-start 1; then
|
||||
exec systemd-cat -t uwsm_start uwsm start sway.desktop
|
||||
elif uwsm check may-start 2; then
|
||||
exec systemd-cat -t uwsm_start uwsm start labwc.desktop
|
||||
fi
|
||||
|
||||
Using "uwsm check may-start" as a condition is essential, not only to prevent accidental
|
||||
startup attempts where they are not expected, but also since startup may involve sourcing shell
|
||||
profile, which might lead to nasty loops.
|
||||
|
||||
See check subcommand section for info on may-start checker.
|
||||
|
||||
exec allows uwsm to replace login shell in order to properly bind to user session and handle
|
||||
session termination.
|
||||
|
||||
"systemd-cat -t uwsm_start" (optional) executes the command given to it (uwsm) with its stdout
|
||||
and stderr connected to the systemd journal, tagged with identifier "uwsm_start". See systemd-
|
||||
cat(1) for more options.
|
||||
|
||||
USE INSIDE DESKTOP ENTRY
|
||||
To launch uwsm from a display/login manager, "uwsm start" can be used inside Desktop Entries.
|
||||
Example /usr/local/share/wayland-sessions/my-compositor.desktop:
|
||||
|
||||
[Desktop Entry]
|
||||
Name=My compositor (with UWSM)
|
||||
Comment=My cool compositor
|
||||
Exec=uwsm start -N "My compositor" -D mycompositor -C "My cool compositor" mywm
|
||||
DesktopNames=mycompositor
|
||||
Type=Application
|
||||
|
||||
Things to keep in mind:
|
||||
|
||||
• For consistency, command line arguments should mirror the keys of the entry
|
||||
• Command in Exec= should start with "uwsm start"
|
||||
• It should not point to itself (as a combination of Desktop Entry ID and Action ID)
|
||||
• It should not point to a Desktop Entry ID and Action ID that also uses ‘uwsm‘
|
||||
|
||||
Potentially such entries may be found and used by uwsm itself, i.e. in shell profile integra‐
|
||||
tion situation, or when launched manually. Following the principles above ensures uwsm will
|
||||
properly recognize itself and parse requested arguments inside the entry without any side ef‐
|
||||
fects.
|
||||
|
||||
SEE ALSO
|
||||
uwsm-plugins(3), systemd-run(1), systemd-cat(1), systemd.special(7)
|
||||
|
||||
2026-02-14 UWSM(1)
|
||||
+1
-10
@@ -53,16 +53,7 @@ impl Default for BatTester {
|
||||
fn default() -> Self {
|
||||
let temp_dir = create_sample_directory().expect("sample directory");
|
||||
|
||||
let root = env::current_exe()
|
||||
.expect("tests executable")
|
||||
.parent()
|
||||
.expect("tests executable directory")
|
||||
.parent()
|
||||
.expect("bat executable directory")
|
||||
.to_path_buf();
|
||||
|
||||
let exe_name = if cfg!(windows) { "bat.exe" } else { "bat" };
|
||||
let exe = root.join(exe_name);
|
||||
let exe = assert_cmd::cargo::cargo_bin!("bat").to_path_buf();
|
||||
|
||||
BatTester { temp_dir, exe }
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use assert_cmd::cargo::CommandCargoExt;
|
||||
use std::process::Command;
|
||||
|
||||
pub fn bat_raw_command_with_config() -> Command {
|
||||
let mut cmd = Command::cargo_bin("bat").unwrap();
|
||||
let mut cmd = Command::new(assert_cmd::cargo::cargo_bin!("bat"));
|
||||
cmd.current_dir("tests/examples");
|
||||
cmd.env_remove("BAT_CACHE_PATH");
|
||||
cmd.env_remove("BAT_CONFIG_DIR");
|
||||
|
||||
Reference in New Issue
Block a user