1
0
mirror of https://github.com/sharkdp/bat synced 2026-08-03 18:51:44 +00:00

Compare commits

...

352 Commits

Author SHA1 Message Date
dependabot[bot] b739f7d1e8 build(deps): bump softprops/action-gh-release from 2 to 3
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2 to 3.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v2...v3)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: '3'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-01 02:12:24 +00:00
Keith Hall 7fcab0616a Merge pull request #3700 from optimistiCli/list-themes-term-probe-fix
Fixed hardcoded terminal probing when `--list-themes` is called
2026-04-30 18:09:32 +03:00
Keith Hall e157ac911b Merge branch 'master' into list-themes-term-probe-fix 2026-04-30 17:58:24 +03:00
Keith Hall b29e1d55c4 Merge pull request #3709 from mvanhorn/osc/3228-readme-style-list
docs(README): list available --style components in Output style section
2026-04-30 14:15:25 +03:00
Keith Hall 12ee94f530 Merge branch 'master' into list-themes-term-probe-fix 2026-04-30 14:13:47 +03:00
Matt Van Horn cbe7919451 docs(README): list available --style components in Output style section
The Output style section explained how to combine values into --style
without listing what those values are, so the recommended path was
either to read the man page or grep the source. Adds two tables: the
four pre-defined styles (default, full, auto, plain) and the eight
individual components (changes, header, header-filename, header-filesize,
grid, rule, numbers, snip), copying the descriptions from `bat --help`
verbatim. Also calls out the default-enabled set explicitly so the
"by default..." sentence in the existing tip block has a referent.

Closes #3228
2026-04-30 01:54:42 -07:00
David Peter 10b4f07a87 Remove sponsors 2026-04-30 10:18:58 +02:00
David Peter ebf469b99a Update README 2026-04-30 10:16:33 +02:00
Keith Hall 8fbc96a5f2 Merge pull request #3654 from cuiweixie/fix/lessclose-nonzero-warning
fix: correct $LESSCLOSE exit-status warning
2026-04-30 08:19:42 +03:00
Keith Hall 54bfc33248 Merge branch 'master' into fix/lessclose-nonzero-warning 2026-04-30 08:10:40 +03:00
Keith Hall 91aac95db2 Merge pull request #3691 from curious-rabbit/master
sanitize filenames
2026-04-28 23:24:00 +03:00
Keith Hall 5977245a23 Merge branch 'master' into fix/lessclose-nonzero-warning 2026-04-28 19:24:00 +03:00
Keith Hall 3f98e6f10f Merge branch 'master' into master 2026-04-28 19:22:51 +03:00
auto-merge-dependabot-prs[bot] 78963986e0 Merge pull request #3666 from sharkdp/dependabot/submodules/assets/syntaxes/02_Extra/Idris2-4d8eb35
build(deps): bump assets/syntaxes/02_Extra/Idris2 from `bbfe50e` to `4d8eb35`
2026-04-27 19:41:31 +00:00
dependabot[bot] de64d3a0eb build(deps): bump assets/syntaxes/02_Extra/Idris2
Bumps [assets/syntaxes/02_Extra/Idris2](https://github.com/buzden/sublime-syntax-idris2) from `bbfe50e` to `4d8eb35`.
- [Commits](https://github.com/buzden/sublime-syntax-idris2/compare/bbfe50e023e0edc74f5e0c003eb946528d49279f...4d8eb35a38254d422030e77b4933530008dd3c6e)

---
updated-dependencies:
- dependency-name: assets/syntaxes/02_Extra/Idris2
  dependency-version: 4d8eb35a38254d422030e77b4933530008dd3c6e
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-27 19:28:44 +00:00
Keith Hall 78cb14af05 Merge pull request #3699 from guille/master
Improve Kotlin syntax
2026-04-27 22:26:10 +03:00
Keith Hall 8c8ed90d6d Merge branch 'master' into master 2026-04-27 22:20:33 +03:00
guille 492c387ce7 CHANGELOG: reference PR not issue 2026-04-27 22:16:09 +03:00
guille bceb260e91 Improve Kotlin syntax 2026-04-27 22:16:09 +03:00
Keith Hall 8cadefb5cb Merge pull request #3706 from lawrence3699/fix/initial-read-errors
Propagate initial input read errors
2026-04-27 21:46:10 +03:00
lawrence3699 64567c4819 Propagate initial input read errors 2026-04-28 01:11:48 +10:00
Keith Hall de2f21562a Merge pull request #3704 from sharkdp/fix/zsh-completion-language-word-split
Fix/zsh completion language word split
2026-04-27 06:07:37 +03:00
Yoshi Tacke 5722311b2e docs(changelog): add bugfix entry for zsh completion fix 2026-04-27 05:55:48 +03:00
YoshKoz f39d63b85d fix(zsh): use newline-splitting for language completions to fix word-splitting on names with spaces
Language names like "HTML (Jinja2)" and "Apache Conf" contain spaces, which
caused $() command substitution to word-split them into garbage tokens. Use
${(f)"$(...)"}  (split on newlines only) to match the pattern already used
for theme completions on line 100.

Fixes #2897
2026-04-27 05:55:48 +03:00
Ish West 4144059e7a Fixed CHANGELOG.md entry format 2026-04-25 21:30:03 +02:00
Ish West 044d445adc Updated CHANGELOG.md 2026-04-25 21:17:02 +02:00
Ish West c3df0e6a88 Fixed a bug with hardcoded terminal probing when --list-themes is called 2026-04-25 17:37:36 +02:00
curious-rabbit 71c894e843 santize filenames 2026-04-20 14:25:49 +02:00
Keith Hall 93c52a4f88 Merge pull request #3692 from barry3406/fix/remove-crt-windows
windows: statically link the CRT to remove vcruntime dependency
2026-04-20 05:09:54 +03:00
Barry ea7fafca1e windows: statically link the CRT to remove vcruntime dependency
Add a .cargo/config.toml that sets target-feature=+crt-static for the
i686, x86_64, and aarch64 MSVC targets so bat.exe no longer depends on
the vcruntime DLL. Mirrors the fix applied to fd in sharkdp/fd#1891.

Closes #3634
2026-04-19 14:19:00 -07:00
Keith Hall e782ec125c Merge pull request #3647 from mvanhorn/osc/3513-tcl-shebang
feat: add tclsh, wish, and expect shebang detection for Tcl syntax
2026-04-19 05:52:52 +03:00
Matt Van Horn 0ecdeb28d4 test(Tcl): regenerate shebang golden files to match patched syntax
The Tcl.sublime-syntax.patch adds first_line_match so bat recognizes
tclsh/wish/expect shebang files as Tcl. With the patch applied, the
leading '#!/...' and '# comment' lines get tokenized as Tcl comments
and rendered in Monokai-Extended's comment color instead of default
foreground.

The goldens were generated against unpatched bat in the original
commit, so CI's 'Run tests with updated syntaxes and themes' job (which
runs assets/create.sh before the regression test) disagreed. Regenerate
against the patched build so the regression test passes.
2026-04-18 09:42:26 -07:00
Matt Van Horn 77ea750e66 test(Tcl): add shebang source files for regression test
CI reported FileNotFoundError for Tcl/{tclsh,expect,wish}_shebang in
tests/syntax-tests/source/Tcl/. The highlighted files existed but the
source files had been omitted, so create_highlighted_versions.py had
nothing to read from.

Source files match the shebang regression test inputs at
tests/examples/regression_tests/issue_3647_*.
2026-04-18 06:58:45 -07:00
Matt Van Horn cafad6b036 test: add highlighted outputs for Tcl shebang regression tests
Generate highlighted test outputs for tclsh, wish, and expect
shebang detection files to prevent regressions.
2026-04-18 06:39:51 -07:00
Matt Van Horn e070d105b5 test: add shebang regression tests and move changelog to Syntaxes section
Add extensionless regression test files for tclsh, wish, and expect
shebangs so syntect bumps don't silently break first-line detection.
Move changelog entry from Features to Syntaxes per reviewer suggestion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 06:38:44 -07:00
Matt Van Horn 1f89178fce fix: reference PR number in changelog entry 2026-04-18 06:38:27 -07:00
Matt Van Horn 52763e0205 feat: add tclsh, wish, and expect shebang detection for Tcl syntax
Add first_line_match to the Tcl syntax definition via a patch file,
enabling automatic Tcl highlighting for scripts with tclsh, wish,
or expect shebangs.

Fixes #3513
2026-04-18 06:38:18 -07:00
Keith Hall f822bd05ce Merge pull request #3689 from janlarres/python-uv
[Python] Support uv as script runner in shebang
2026-04-13 20:28:02 +03:00
Jan Larres e89c515e9a [Python] Support uv as script runner in shebang 2026-04-13 17:56:05 +12:00
Keith Hall a36b02d90f Merge pull request #3688 from orbisai0security/fix-fix-shell-injection-subprocess-call
fix: sanitize subprocess call in generate_snapshots.py
2026-04-12 16:04:47 +03:00
Kira Security Bot 6876a782da Apply code changes: @orbisai0security can you address code review comm... 2026-04-12 12:37:43 +00:00
orbisai0security db647c9813 fix: V-001 security vulnerability
Automated security fix generated by Orbis Security AI
2026-04-12 11:43:12 +00:00
Keith Hall 33045c3558 Merge pull request #3687 from officialasishkumar/support-bat-width-env
Support BAT_WIDTH for terminal width
2026-04-12 04:59:03 +03:00
Asish Kumar 01174b31f5 Update changelog entry for #3687
Signed-off-by: Asish Kumar <officialasishkumar@gmail.com>
2026-04-12 01:13:08 +05:30
Asish Kumar a9137fab05 Support BAT_WIDTH as an alias for --terminal-width
Signed-off-by: Asish Kumar <officialasishkumar@gmail.com>
2026-04-12 01:12:55 +05:30
Keith Hall a995764d23 Merge pull request #3686 from officialasishkumar/fix-zip-binary-detection
Detect ZIP archives as binary content
2026-04-10 20:30:27 +03:00
Asish Kumar b3aec318cf Update changelog entry for #3686 2026-04-10 14:37:00 +00:00
Asish Kumar 2459aa9404 Detect ZIP archives as binary content 2026-04-10 14:35:33 +00:00
Keith Hall 111aa2e10e Merge pull request #3682 from ltrzesniewski/slnx
Add .NET `slnx` extension
2026-04-08 05:20:55 +03:00
Lucas Trzesniewski c64b6761fe Add .NET slnx extension
This is the new XML-based format for solution files.
2026-04-07 23:25:40 +02:00
Keith Hall 11efacbe64 Merge pull request #3661 from sharkdp/diagnostic_themes
Update --diagnostic output with BAT_THEME_LIGHT/DARK env vars and detected theme type
2026-04-02 21:44:28 +03:00
Keith Hall 204ca18d71 Merge branch 'master' into diagnostic_themes 2026-04-02 21:36:24 +03:00
auto-merge-dependabot-prs[bot] a158f12106 Merge pull request #3669 from sharkdp/dependabot/cargo/quote-1.0.45
build(deps): bump quote from 1.0.40 to 1.0.45
2026-04-01 04:07:40 +00:00
dependabot[bot] 956f0aeb08 build(deps): bump quote from 1.0.40 to 1.0.45
Bumps [quote](https://github.com/dtolnay/quote) from 1.0.40 to 1.0.45.
- [Release notes](https://github.com/dtolnay/quote/releases)
- [Commits](https://github.com/dtolnay/quote/compare/1.0.40...1.0.45)

---
updated-dependencies:
- dependency-name: quote
  dependency-version: 1.0.45
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-01 03:54:15 +00:00
auto-merge-dependabot-prs[bot] 1a6073d2e8 Merge pull request #3668 from sharkdp/dependabot/cargo/tempfile-3.27.0
build(deps): bump tempfile from 3.23.0 to 3.27.0
2026-04-01 03:51:21 +00:00
dependabot[bot] 57e99e9bcd build(deps): bump tempfile from 3.23.0 to 3.27.0
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.23.0 to 3.27.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.23.0...v3.27.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-version: 3.27.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-01 03:38:08 +00:00
auto-merge-dependabot-prs[bot] 68eaa96e06 Merge pull request #3664 from sharkdp/dependabot/cargo/toml-1.1.1spec-1.1.0
build(deps): bump toml from 0.9.8 to 1.1.1+spec-1.1.0
2026-04-01 03:34:35 +00:00
dependabot[bot] de9ca4ef7b build(deps): bump toml from 0.9.8 to 1.1.1+spec-1.1.0
Bumps [toml](https://github.com/toml-rs/toml) from 0.9.8 to 1.1.1+spec-1.1.0.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.9.8...toml-v1.1.1)

---
updated-dependencies:
- dependency-name: toml
  dependency-version: 1.1.1+spec-1.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-01 03:13:41 +00:00
auto-merge-dependabot-prs[bot] 7bb905436b Merge pull request #3667 from sharkdp/dependabot/cargo/unicode-width-0.2.2
build(deps): bump unicode-width from 0.2.1 to 0.2.2
2026-04-01 03:11:34 +00:00
dependabot[bot] f820ad1050 build(deps): bump unicode-width from 0.2.1 to 0.2.2
Bumps [unicode-width](https://github.com/unicode-rs/unicode-width) from 0.2.1 to 0.2.2.
- [Commits](https://github.com/unicode-rs/unicode-width/compare/v0.2.1...v0.2.2)

---
updated-dependencies:
- dependency-name: unicode-width
  dependency-version: 0.2.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-01 02:41:02 +00:00
auto-merge-dependabot-prs[bot] 259e1266a8 Merge pull request #3665 from sharkdp/dependabot/cargo/unicode-segmentation-1.13.2
build(deps): bump unicode-segmentation from 1.12.0 to 1.13.2
2026-04-01 02:37:51 +00:00
dependabot[bot] 31b0a35719 build(deps): bump unicode-segmentation from 1.12.0 to 1.13.2
Bumps [unicode-segmentation](https://github.com/unicode-rs/unicode-segmentation) from 1.12.0 to 1.13.2.
- [Commits](https://github.com/unicode-rs/unicode-segmentation/compare/v1.12.0...v1.13.2)

---
updated-dependencies:
- dependency-name: unicode-segmentation
  dependency-version: 1.13.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-01 02:03:59 +00:00
Keith Hall 3e4169b8c3 cargo fmt 2026-03-31 22:35:21 +03:00
Keith Hall ba223289ce Update --diagnostic output with detected terminal color scheme type 2026-03-31 22:23:10 +03:00
Keith Hall abab6533e4 Update --diagnostic output with BAT_THEME_LIGHT/DARK env vars 2026-03-31 22:22:31 +03:00
Keith Hall 7d9884d6d2 Merge pull request #3655 from claw-explorer/docs/fish-abbr-removal
docs: add instructions for removing fish help abbreviations
2026-03-28 22:44:07 +02:00
Claw Explorer 652489251b docs: move removal instructions into a TIP callout
Address review feedback from @keith-hall: wrap the abbreviation
removal instructions in a GitHub callout (> [!TIP]) to visually
separate them from the main setup flow. Also reorder so the
'This way, you can keep on using...' sentence follows directly
after the abbreviation creation, improving readability.
2026-03-28 16:34:28 -04:00
Claw Explorer 89f3d2d31a docs: add instructions for removing fish help abbreviations
Add documentation showing how to erase the fish abbreviations for
--help and -h, since the dash-prefixed names make removal non-obvious.
The key is using -- before the abbreviation name to prevent fish from
interpreting it as a flag.

Closes #3536
2026-03-26 12:21:56 -04:00
Weixie Cui 5c3b8040db fix: add changelog entry for LESSCLOSE warning fix 2026-03-26 11:11:19 +08:00
Weixie Cui 7ff1fb3278 fix: warn when $LESSCLOSE fails, not when it succeeds
The Drop cleanup for Preprocessed inverted the exit-status check, so bat
emitted a warning when LESSCLOSE exited successfully and stayed silent on
failure. Align the condition with the intended behavior.
2026-03-26 11:10:58 +08:00
Keith Hall 3ee9ea9a8d Merge pull request #3652 from cyqsimon/syntax-mapping-glob-parse-cleanup
Builtin syntax mapping: cleanup matcher glob parsing logic
2026-03-25 22:01:52 +02:00
cyqsimon b511b928f4 Write changelog 2026-03-25 15:58:58 +08:00
cyqsimon 3e789f5241 Imprv: cleanup matcher glob parsing logic
- Data flow is now strictly linear from `RawMatcher` to `Matcher`
  - I've also hoisted `RawMatcher` in front of `Matcher` to signal this
- Re-type `RawMatcher.case_sensitive` from `bool` to `Option<bool>`
  - This moves all parser logic away from `RawMatcher`, making it a more faithful representation of the data
- Favour default consts in `Matcher::try_from<RawMatcher>` to `Default` impl on `Case`
  - Because the default choice of casing is a design decision of the logic, not an intrinsic property of the type
2026-03-25 15:58:45 +08:00
Keith Hall a500fb236a Merge pull request #3650 from Sim-hu/fix/i686-deb-architecture
fix: use correct Debian architecture name for i686 .deb
2026-03-24 22:37:48 +02:00
Keith Hall 515b9d62db Merge branch 'master' into fix/i686-deb-architecture 2026-03-24 22:23:29 +02:00
Keith Hall 62907b1da5 Merge pull request #3651 from sharkdp/minus_home_end_keys
Add Home/End key bindings to builtin minus pager
2026-03-24 22:22:53 +02:00
Keith Hall eef7107407 update changelog 2026-03-24 21:38:07 +02:00
copilot-swe-agent[bot] 87e043b91c Add Home/End key bindings to builtin minus pager
Co-authored-by: keith-hall <11882719+keith-hall@users.noreply.github.com>
2026-03-24 21:33:43 +02:00
Sim-hu b8d462ba87 fix: add PR number and author to changelog entry 2026-03-24 19:24:36 +09:00
Sim-hu ca3ef28d56 fix: use correct Debian architecture name for i686 .deb package
The i686 .deb package declared its architecture as "i686", which
Debian does not recognize. Debian uses "i386" for 32-bit x86,
matching the convention already used by the other targets (arm64,
armhf, amd64).

Closes #3611
2026-03-24 19:20:18 +09:00
Sungjoon Moon 0b4c886efc ci: Use git version of cross for better target support 2026-03-24 06:09:32 +01:00
Keith Hall 780fdeb888 Merge pull request #3576 from vorburger/patch-2
feat: Map BUILD to Python (Starlark) for Bazel (fixes #3575)
2026-03-22 20:14:05 +02:00
Keith Hall 7a9c6e0de5 Merge branch 'master' into patch-2 2026-03-22 17:47:24 +02:00
Keith Hall a1e7c0ab4a Merge pull request #3643 from mvanhorn/feat/diff-plain-compatibility
feat: preserve change markers when combining --diff with --plain
2026-03-21 16:30:43 +02:00
Keith Hall 1e36873704 Merge branch 'master' into feat/diff-plain-compatibility 2026-03-21 16:21:56 +02:00
Matt Van Horn e86797fbf4 style: auto-format integration tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 15:46:54 -07:00
Matt Van Horn e60875ac12 fix: gate Changes component on git feature flag
When bat is built without the `git` feature, `StyleComponent::Changes`
is not available. Add `#[cfg(feature = "git")]` guard so `--diff --plain`
works in both configurations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 15:27:10 -07:00
Keith Hall 66eece4e2c Merge branch 'master' into patch-2 2026-03-20 23:57:19 +02:00
Keith Hall a19593b383 improvements from PR review
1. Added `Deserialize` derive and `#[serde(try_from = "RawMatcher")]` to the `Matcher` struct. With `try_from`, serde generates a `Deserialize` impl that first deserializes into `RawMatcher`, then calls `TryFrom<RawMatcher> for Matcher`. It never attempts to deserialize `Matcher`'s fields directly, so `Case` and `MatcherSegment` don't need `Deserialize` impls.
2. Replaced the manual `impl<'de> serde::Deserialize<'de> for Matcher` with a standard `impl TryFrom<RawMatcher> for Matcher`. The logic is identical - the conversion is fallible because `Matcher::from_str` returns `Result<_, anyhow::Error>`, so `try_from` (not `from`) is the correct choice, avoiding any panics.

The net effect: same behavior, same error handling, but using the idiomatic serde pattern instead of a manual deserializer impl. The `RawMatcher` intermediate type and its `#[serde(untagged)]` derive remain unchanged.
2026-03-20 23:53:30 +02:00
Keith Hall 7a6f442c86 improvements from PR review
- prefer to use `Default::default` because it's semantically clearer why we've chosen to use a particular value
2026-03-20 23:35:27 +02:00
Keith Hall 3767f15c2a improvements from PR review
fix documentation about syntax mappings
2026-03-20 23:34:38 +02:00
Keith Hall 1932d1da39 Merge pull request #3642 from mvanhorn/fix/deb-musl-package-names
fix: consistent .deb MUSL package names
2026-03-20 22:57:41 +02:00
Keith Hall b0e5590864 Merge branch 'master' into fix/deb-musl-package-names 2026-03-20 22:42:24 +02:00
Keith Hall f2daa1eb6e Merge pull request #3640 from eyupcanakman/fix/binary-as-text-width-3631
Fix line wrapping for files with control characters
2026-03-20 22:40:28 +02:00
Eyüp Can Akman 1f540752ef fix: reference PR number in CHANGELOG entry 2026-03-20 22:28:39 +02:00
Eyüp Can Akman fc94a0ec49 fix: account for caret notation width in text wrapping
Control characters displayed in caret notation (e.g. ^@ for NUL)
occupy 2 terminal columns, but the width calculation treated them
as 0-width. Add a char_width() helper that returns 2 for control
characters, fixing incorrect line wrapping with --binary=as-text.

Fixes #3631
2026-03-20 22:28:39 +02:00
Matt Van Horn 169dc7c45b test: add integration tests for --diff combined with --plain 2026-03-20 10:33:33 -07:00
Matt Van Horn 5e140558b1 fix: update changelog entry with PR number for CI check 2026-03-19 21:38:03 -07:00
Matt Van Horn 99c8e15c27 fix: update changelog entry with PR number for CI check 2026-03-19 21:37:45 -07:00
Matt Van Horn 2a3ed948ec feat: preserve change markers when combining --diff with --plain
When --plain is set (via CLI or config file), --diff output loses all
visual markers, making it indistinguishable from plain text. The diff
line filtering still works, but without Changes markers or Snip
separators the output is not useful.

Automatically include Changes and Snip style components when --diff is
active alongside --plain. This preserves the --plain intent (no grid,
no header, no line numbers) while keeping diff output readable.

Closes #3630
2026-03-19 21:35:48 -07:00
Matt Van Horn 618d7340bb fix: consistent .deb MUSL package names
Fix two inconsistencies in MUSL .deb package naming:

1. Change dpkg_arch for aarch64-unknown-linux-musl from `arm64` to
   `musl-linux-arm64` to match the convention used by other MUSL targets.

2. Change the DPKG_BASENAME case pattern from `*-musl)` to `*-musl*)`
   so it also matches `musleabihf`, giving the arm target the `bat-musl`
   prefix like all other MUSL packages.

After this fix, all MUSL .deb packages follow the consistent pattern:
`bat-musl_VERSION_musl-linux-ARCH.deb`

Closes #3482
2026-03-19 21:34:12 -07:00
Keith Hall 4a38eab3ea Merge pull request #3628 from XploitMonk0x01/codex/clarify-bat-vs-batcat
docs: clarify Ubuntu and Debian executable name
2026-03-19 23:00:59 +02:00
XploitMonk0x01 9fccdbc484 docs: clarify Ubuntu and Debian executable name 2026-03-19 22:50:23 +02:00
Keith Hall c6e661d80b cargo fmt 2026-03-18 22:25:07 +02:00
Keith Hall 031ba0953a Merge branch 'master' into patch-2 2026-03-18 22:17:18 +02:00
copilot-swe-agent[bot] 2a29802dd5 Refactor: string-or-struct matcher syntax, Case enum, remove case_sensitive_mappings table
Co-authored-by: keith-hall <11882719+keith-hall@users.noreply.github.com>
2026-03-18 22:16:50 +02:00
Keith Hall 31e30a2e97 Merge pull request #3636 from victor-gp/add-mapping-for-ignore-files
Add mapping for ignore files
2026-03-18 22:08:02 +02:00
Víctor González Prieto ffed52f6eb Document how to contribute syntax mappings 2026-03-18 21:58:23 +02:00
Víctor González Prieto a4e853c4fa Map ignore files to Git Ignore syntax
This would catch any dotfiles that end in "ignore", but not `.ignore`

Set as a lower priority rule (90 vs the usual 50) because it's a blanket
assignation. If there are conflict with other, more specific rules,
those will take precedence.
2026-03-18 21:58:23 +02:00
Keith Hall f11374535d Merge pull request #3629 from Rohan5commit/docs/fix-readme-wording
docs: fix README sidebar wording
2026-03-18 20:37:04 +02:00
rohan436 de209f8a23 docs: fix README sidebar wording 2026-03-18 20:15:53 +02:00
Keith Hall 22c38b6fcb Merge remote-tracking branch 'origin/master' into patch-2 2026-03-18 05:19:21 +02:00
Keith Hall 63d5796f8f Merge pull request #3635 from victor-gp/add-mappings-for-gcloud-cli
Add syntax mappings for GCloud CLI config files
2026-03-17 22:45:12 +02:00
Víctor González Prieto a1917275cd Add syntax mappings for GCloud CLI config files
> .boto => INI

I was uninstalling gcloud-cli and the docs said to review the contents
of ~/.boto [^1].

`bat` doesn't recognize it but it's just an INI-like file to configure
gsutil [^2].

I took the chance to add a few more mappings for other GCloud config
files without extension...

> .gcloudignore => Git Ignore

This one's self-explanatory, but it's documented in [^3].

Makes me wonder if we could just do a blanket `Git Ignore = ".?*ignore"`
for every kind of ignore file. For instance, `bat` doesn't catch
.dockerignore either.

> **/gcloud/configurations/config_* => INI

These are configuration files for each different profile you have
configured for the gcloud-cli [^4].

They're named like "config_default" or "config_<name>", without a
file extension, but they are also INI syntax [^5][^6].

[^1]: https://docs.cloud.google.com/sdk/docs/uninstall-cloud-sdk
[^2]: https://docs.cloud.google.com/storage/docs/boto-gsutil
[^3]: https://docs.cloud.google.com/sdk/gcloud/reference/topic/gcloudignore
[^4]: https://docs.cloud.google.com/sdk/docs/configurations
[^5]: https://stackoverflow.com/questions/74290882/return-active-gcp-project-name-programatically-for-both-local-dev-and-deployed-c/7^4292151#74292151
[^6]: https://stackoverflow.com/questions/49212350/where-does-gcloud-store-its-defaults/49256835#49256835
2026-03-17 20:51:23 +01:00
Keith Hall 884765bdeb Merge remote-tracking branch 'origin/master' into patch-2 2026-03-14 09:21:19 +02:00
Keith Hall 56fe0fa226 Add case-sensitive glob support to syntax mapping
to allow us to map `BUILD` case sensitively to Python for Skylark
2026-03-14 09:18:56 +02:00
Keith Hall d9adfe90fc Merge pull request #3627 from XploitMonk0x01/codex/clarify-theme-format
docs: clarify supported custom theme format
2026-03-14 09:08:30 +02:00
XploitMonk0x01 b1f04995a6 docs: clarify supported custom theme format 2026-03-14 11:44:38 +05:30
auto-merge-dependabot-prs[bot] ea6d2ac911 Merge pull request #3605 from sharkdp/dependabot/submodules/assets/syntaxes/02_Extra/cmd-help-db0a616
build(deps): bump assets/syntaxes/02_Extra/cmd-help from `273cb98` to `db0a616`
2026-03-11 17:06:16 +00:00
dependabot[bot] ac97356930 build(deps): bump assets/syntaxes/02_Extra/cmd-help
Bumps [assets/syntaxes/02_Extra/cmd-help](https://github.com/victor-gp/cmd-help-sublime-syntax) from `273cb98` to `db0a616`.
- [Commits](https://github.com/victor-gp/cmd-help-sublime-syntax/compare/273cb988177e96f4187e06008b13fa72ad22ae4d...db0a616cfa367a4f52fd81d1a46c8ebea11e867d)

---
updated-dependencies:
- dependency-name: assets/syntaxes/02_Extra/cmd-help
  dependency-version: db0a616cfa367a4f52fd81d1a46c8ebea11e867d
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-11 16:54:46 +00:00
Keith Hall 23abbdcea9 Merge pull request #3584 from adukhan99/add-cobol-syntax
Add COBOL syntax highlighting
2026-03-11 18:51:14 +02:00
Alex Dukhan 927873392b Drop .ss extension from COBOL 2026-03-11 15:06:47 +00:00
Alex Dukhan 91965d28a6 Add COBOL sans CPY ext 2026-03-11 14:36:20 +00:00
Alex Dukhan 88ff40f93e Merge branch 'master' into add-cobol-syntax 2026-03-11 11:26:01 +00:00
Keith Hall 0dcdcb678a Merge pull request #3621 from Xavrir/fix-symlink-syntax-detection
Fix syntax highlighting for symlinked files by resolving target path
2026-03-08 20:01:25 +02:00
Rizky Mirzaviandy Priambodo f49641a6c3 Fix syntax highlighting for symlinked files by resolving target path
When a symlink name has no recognizable extension (e.g. Aliases/0install),
syntax detection fails because the symlink path doesn't match any syntax.
The target file may have a recognizable extension (e.g. Formula/zero-install.rb)
but was never consulted.

When path-based syntax detection fails with UndetectedSyntax, canonicalize
the path to resolve symlinks and retry detection with the target path.
This preserves existing behavior where the symlink path itself matches
(e.g. .ssh/config), since the original path is tried first.

Closes #1001
2026-03-09 00:01:09 +07:00
Keith Hall fc4f12b390 Merge pull request #3620 from Xavrir/fix-bat-config-dir-duplicate
Fix BAT_CONFIG_DIR pointing at system config directory causing duplicate flag errors
2026-03-08 08:23:14 +02:00
Rizky Mirzaviandy Priambodo 9cce9e04d2 Fix BAT_CONFIG_DIR pointing at system config dir causing duplicate flag errors
When BAT_CONFIG_DIR is set to the system config directory (e.g. /etc/bat),
both system_config_file() and config_file() resolve to /etc/bat/config.
The config file was read twice, causing clap errors for non-repeatable flags
like --italic-text.

Skip the user config read when it resolves to the same file as the system
config, using canonicalization to also handle symlinks.

Closes #3589
2026-03-08 12:20:27 +07:00
Rizky Mirzaviandy Priambodo 844bfded50 Add --fallback-syntax for undetected files (#3617)
* feat(cli): add fallback syntax option

Expose a new fallback syntax CLI option so users can opt into syntax highlighting only when auto-detection fails.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* feat(syntax): apply fallback only after detection fails

Use the fallback syntax only when path and first-line detection fail, preserving existing behavior for detected files and explicit language selection.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* test(cli): cover fallback syntax behavior

Add integration coverage for fallback syntax usage, precedence with --language, and no-op behavior when syntax is already detected; update help snapshots for the new option.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* docs(changelog): document fallback syntax option

Record the new fallback syntax feature in the unreleased changelog section.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

---------

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-08 05:18:29 +02:00
Matei6942 ab80bd9717 feat(syntax): add support for hidden_file_extensions (#3613)
* feat(syntax): add support for hidden_file_extensions
2026-03-06 20:14:20 +02:00
foxfromworld a1a10c777f Docs: clarify PATH requirement and explain highlighted outputs (Fixes… (#3610)
* Docs: clarify PATH requirement and explain highlighted outputs (Fixes #2376)

* Update CHANGELOG for PR #3610

* Apply suggestion from @keith-hall

Applied reviewer’s suggestion to improve PATH export: safer with quotes and more convenient with $(pwd).

Co-authored-by: Keith Hall <keith-hall@users.noreply.github.com>

* Docs: clarify PATH setup in CONTRIBUTING.md, remove that section from README.md

* Docs: add cross reference to syntax tests step 5 in doc/assets.md

---------

Co-authored-by: Keith Hall <keith-hall@users.noreply.github.com>
2026-03-05 05:55:05 +00:00
Varun Chawla cc5f782d28 Add word wrapping mode (#3597)
* feat: add word wrapping mode for --wrap flag

* Run `cargo fmt` and add CHANGELOG entry

* Add word wrap tests, update manpage and shell completions

- Add integration tests for word wrapping: basic word boundary breaking,
  fallback to character wrapping for long words, line numbers, and
  short lines that fit without wrapping
- Update manpage to document the new 'word' wrapping mode
- Update bash, fish, zsh, and PowerShell completions with 'word' option
- Avoid unnecessary clone of `line_buf` when word wrap is disabled

* make clippy and cargo fmt happy

---------

Co-authored-by: Keith Hall <keith-hall@users.noreply.github.com>
2026-03-03 05:18:49 +02:00
Keith Hall 1424f9d6bf Merge pull request #3609 from Nicolas0315/fix/builtin-pager-drop-panic
Fix panic in BuiltinPager Drop when pager thread fails (#3449)
2026-03-02 04:57:37 +02:00
Nicolas fd67095cff Fix panic in BuiltinPager drop when pager thread panics
The Drop impl for OutputType::BuiltinPager calls
handle.take().unwrap().join().unwrap() which panics if the
pager thread itself panicked. In Drop, this causes a double
panic (abort).

Replace with if-let and discard the join result, matching the
pattern used for OutputType::Pager which also uses let _ =.

Reported in #3449 where BAT_PAGER=builtin with --help
causes the pager thread to fail.

Fixes #3449
2026-03-02 04:12:50 +09:00
auto-merge-dependabot-prs[bot] 908b1f22d1 Merge pull request #3608 from sharkdp/dependabot/cargo/bytesize-2.3.1
build(deps): bump bytesize from 1.3.0 to 2.3.1
2026-03-01 04:47:12 +00:00
dependabot[bot] 17a70d9b8c build(deps): bump bytesize from 1.3.0 to 2.3.1
Bumps [bytesize](https://github.com/bytesize-rs/bytesize) from 1.3.0 to 2.3.1.
- [Release notes](https://github.com/bytesize-rs/bytesize/releases)
- [Changelog](https://github.com/bytesize-rs/bytesize/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bytesize-rs/bytesize/compare/v1.3.0...bytesize-v2.3.1)

---
updated-dependencies:
- dependency-name: bytesize
  dependency-version: 2.3.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-01 04:36:13 +00:00
auto-merge-dependabot-prs[bot] 49d77a469a Merge pull request #3607 from sharkdp/dependabot/cargo/clap-4.5.60
build(deps): bump clap from 4.5.56 to 4.5.60
2026-03-01 04:33:43 +00:00
dependabot[bot] fa35495888 build(deps): bump clap from 4.5.56 to 4.5.60
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.56 to 4.5.60.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.56...clap_complete-v4.5.60)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.60
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-01 04:18:28 +00:00
auto-merge-dependabot-prs[bot] 8a858b19dc Merge pull request #3606 from sharkdp/dependabot/cargo/git2-0.20.4
build(deps): bump git2 from 0.20.3 to 0.20.4
2026-03-01 04:16:23 +00:00
dependabot[bot] 397252d2cc build(deps): bump git2 from 0.20.3 to 0.20.4
Bumps [git2](https://github.com/rust-lang/git2-rs) from 0.20.3 to 0.20.4.
- [Changelog](https://github.com/rust-lang/git2-rs/blob/git2-0.20.4/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/git2-rs/compare/git2-0.20.3...git2-0.20.4)

---
updated-dependencies:
- dependency-name: git2
  dependency-version: 0.20.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-01 04:00:55 +00:00
auto-merge-dependabot-prs[bot] 5e59957349 Merge pull request #3603 from sharkdp/dependabot/cargo/nix-0.31.2
build(deps): bump nix from 0.30.1 to 0.31.2
2026-03-01 03:58:09 +00:00
dependabot[bot] 90f8e00e0c build(deps): bump nix from 0.30.1 to 0.31.2
Bumps [nix](https://github.com/nix-rust/nix) from 0.30.1 to 0.31.2.
- [Changelog](https://github.com/nix-rust/nix/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nix-rust/nix/compare/v0.30.1...v0.31.2)

---
updated-dependencies:
- dependency-name: nix
  dependency-version: 0.31.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-01 03:35:51 +00:00
auto-merge-dependabot-prs[bot] 673929c189 Merge pull request #3604 from sharkdp/dependabot/cargo/serde_with-3.17.0
build(deps): bump serde_with from 3.16.1 to 3.17.0
2026-03-01 03:32:28 +00:00
dependabot[bot] e36bb8cc66 build(deps): bump serde_with from 3.16.1 to 3.17.0
Bumps [serde_with](https://github.com/jonasbb/serde_with) from 3.16.1 to 3.17.0.
- [Release notes](https://github.com/jonasbb/serde_with/releases)
- [Commits](https://github.com/jonasbb/serde_with/compare/v3.16.1...v3.17.0)

---
updated-dependencies:
- dependency-name: serde_with
  dependency-version: 3.17.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-01 03:04:00 +00:00
Keith Hall 7d7e202bd5 Merge pull request #3588 from IMaloney/fix/warn-missing-pager
warn when pager is missing instead of silently falling back
2026-02-20 20:46:38 +02:00
Keith Hall 74de075748 Merge branch 'master' into fix/warn-missing-pager 2026-02-20 20:37:07 +02:00
Keith Hall 594069581d Merge pull request #3592 from IMaloney/fix/wrap-never-clean
respect --wrap=never flag when piping to pager
2026-02-20 20:26:23 +02:00
Ian Maloney 62b0388236 remove code comments 2026-02-20 06:12:14 +00:00
Ian Maloney 16d041492c fix pr number in changelog 2026-02-20 06:07:23 +00:00
Ian Maloney 22ee03ff00 add changelog entry for wrap-never bug fix 2026-02-20 06:04:29 +00:00
Ian Maloney 790bed3a2d fix: respect --wrap=never flag when paging is enabled
When output is piped to a pager, the wrapping_mode logic was not checking the explicit --wrap=never flag and would always default to NoWrapping(false). This meant the -S flag was not passed to less, causing lines to wrap despite the user's explicit request.

The fix prioritizes explicit CLI flags (--wrap=never, --chop-long-lines) over the interactive_output-based logic, ensuring they are always respected.

Fixes #3587
2026-02-20 06:04:14 +00:00
Ian Maloney bc84854d4b use cat as test pager instead of builtin (builtin is interactive, doesnt output to stdout) 2026-02-20 00:19:47 +00:00
Ian Maloney 319811df01 fix test: use builtin pager instead of less, revert to warn for all missing pagers 2026-02-20 00:09:02 +00:00
Ian Maloney 2c1a8caadd simplify: just never warn for less (universal default) 2026-02-19 22:49:02 +00:00
Ian Maloney a240aa4afd never warn for missing 'less' pager (common default) 2026-02-19 22:21:17 +00:00
Ian Maloney 00e38cd056 only warn for explicitly configured pagers, not defaults 2026-02-19 22:01:58 +00:00
Ian Maloney 335eff51f3 fix type error, pager.bin is already a string 2026-02-19 21:44:47 +00:00
Ian Maloney d04b960c05 warn when pager is missing instead of silently falling back
when a configured pager (via BAT_PAGER, PAGER, or --pager) is not found,
bat now shows a warning message before falling back to stdout. this helps
users understand why their pager isn't running and makes it obvious when
there's a typo or PATH issue.

fixes issue #2904
2026-02-19 21:29:59 +00:00
Keith Hall fbc05da785 Merge pull request #3586 from BlueElectivire/master
Fixed manpage syntax to remove ANSI artifacts
2026-02-19 18:56:14 +02:00
Guy Barzilai a71d16fa1b Fixed manpage syntax-test format 2026-02-19 18:11:36 +02:00
Guy Barzilai 5bd08845a3 Fixed manpage regex regression and add syntax test 2026-02-18 23:59:38 +02:00
Guy Barzilai 3ebfbb7ac5 Fixed manpage syntax to remove ANSI artifacts 2026-02-17 21:54:58 +02:00
Alex Dukhan 2b47f3c5eb Updated CHANGELOG.md 2026-02-12 22:10:08 +00:00
Alex Dukhan 9f8ac934ab Updated CHANGELOG.md 2026-02-12 21:43:34 +00:00
Alex Dukhan ab393a0281 Add COBOL syntax highlighting 2026-02-12 21:06:03 +00:00
Keith Hall 990b2fc06f Merge pull request #3583 from mainnebula/steward/sharkdp-bat-3555-2de8ac3c
feat: implement --unbuffered mode for streaming input
2026-02-12 08:40:44 +02:00
mainnebula 0c883d6f28 docs: add changelog entry for --unbuffered mode (#3583) 2026-02-11 23:45:20 -05:00
mainnebula 167dda63a8 feat: implement --unbuffered mode for streaming input (#3555)
Repurpose the existing --unbuffered/-u flag (previously a POSIX no-op)
to enable unbuffered input reading using fill_buf()/consume() instead
of read_until(b'\n'). This allows partial lines to display immediately
when piping streaming input like `tail -f` into bat.

- Add unbuffered field to Config and InputReader
- Add read_line_unbuffered() using BufRead::fill_buf()/consume()
- Add flush() to OutputHandle, called after each line in unbuffered mode
- Auto-disable line numbers in unbuffered mode to avoid partial line confusion
- Update help text, man page, and shell completions
- Add unit tests and integration tests
2026-02-11 23:27:08 -05:00
dddffgg cb8b637574 fix(cache): allow --help flag for cache subcommand (#3580)
* fix(cache): allow --help flag for cache subcommand

* docs: add changelog entry for cache --help fix
2026-02-09 05:06:40 +02:00
Keith Hall 391e7ff072 Merge pull request #3581 from NORMAL-EX/chore/update-time-crate
chore(deps): update time crate to 0.3.47 (RUSTSEC-2026-0009)
2026-02-07 13:37:17 +02:00
dddffgg 2b517199a2 docs: add changelog entry for MSRV bump and time crate update 2026-02-07 19:23:57 +08:00
dddffgg a0f326aa22 chore: bump MSRV to 1.88 2026-02-07 19:23:47 +08:00
dddffgg a4a7692134 chore(deps): update time crate to 0.3.47 (RUSTSEC-2026-0009) 2026-02-06 21:32:02 +08:00
Keith Hall 3df9d581b7 Merge pull request #3527 from Anchal-T/master
Fix bat crash with BusyBox less on Windows
2026-02-03 22:40:04 +02:00
Keith Hall eb724f2dc4 Merge branch 'master' into master 2026-02-03 22:27:30 +02:00
Keith Hall c491488140 Merge pull request #3578 from vorburger/nix
Add initial flake.nix; just for develop, for now (fixes#3577)
2026-02-03 22:25:49 +02:00
Michael Vorburger 3f12b1c1ab docs: Use PR not Issue ID in CHANGELOG.md 2026-02-01 14:20:40 +01:00
Michael Vorburger 2c682ff6a6 docs: Document flake.nix in CHANGELOG.md (see #3577) 2026-02-01 14:13:03 +01:00
Michael Vorburger 36b37e1aa5 Add x86_64-darwin (Intel macOS) to flake.nix 2026-02-01 14:11:05 +01:00
Michael Vorburger d19c80a7c1 Remove allowUnfree = true from flake.nix
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-01 14:09:18 +01:00
Michael Vorburger eff57943c9 Add initial flake.nix; just for develop, for now (fixes#3577) 2026-02-01 14:06:05 +01:00
Michael Vorburger 5a4a7de933 docs: Document mapping BUILD to Python (Starlark) for Bazel in CHANGELOG.md (see #3576) 2026-02-01 13:40:51 +01:00
Michael Vorburger ba97230b98 feat: Map BUILD to Python (Starlark) for Bazel (fixes #3575)
See #3575.
2026-02-01 13:34:33 +01:00
Keith Hall 8664cc9df9 Merge pull request #3563 from NORMAL-EX/feat/quiet-empty
feat: add --quiet-empty flag to suppress output on empty input
2026-02-01 11:15:45 +02:00
dddffgg 3b34b47d55 docs: add --quiet-empty to PowerShell completion 2026-02-01 16:21:38 +08:00
dddffgg 9a5519057c docs: add --quiet-empty to fish completion 2026-02-01 16:21:22 +08:00
dddffgg 93411a96e4 docs: add --quiet-empty to zsh completion 2026-02-01 16:21:05 +08:00
dddffgg bd461afd75 docs: add --quiet-empty to bash completion 2026-02-01 16:20:49 +08:00
dddffgg 55306c15a6 docs: add --quiet-empty to man page 2026-02-01 16:20:27 +08:00
dddffgg afd804ebea Merge branch 'master' into feat/quiet-empty 2026-02-01 14:50:07 +08:00
auto-merge-dependabot-prs[bot] 3d0a7ed0a0 Merge pull request #3568 from sharkdp/dependabot/cargo/indexmap-2.13.0
build(deps): bump indexmap from 2.12.1 to 2.13.0
2026-02-01 05:30:27 +00:00
dependabot[bot] 8f6f5dfbd2 build(deps): bump indexmap from 2.12.1 to 2.13.0
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.12.1 to 2.13.0.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/main/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.12.1...2.13.0)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-version: 2.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-01 05:19:49 +00:00
auto-merge-dependabot-prs[bot] 43ab174d42 Merge pull request #3567 from sharkdp/dependabot/cargo/proc-macro2-1.0.106
build(deps): bump proc-macro2 from 1.0.103 to 1.0.106
2026-02-01 05:16:54 +00:00
dependabot[bot] b396ab865f build(deps): bump proc-macro2 from 1.0.103 to 1.0.106
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.103 to 1.0.106.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.103...1.0.106)

---
updated-dependencies:
- dependency-name: proc-macro2
  dependency-version: 1.0.106
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-01 04:54:36 +00:00
auto-merge-dependabot-prs[bot] 8a82847121 Merge pull request #3571 from sharkdp/dependabot/cargo/serde_with-3.16.1
build(deps): bump serde_with from 3.15.1 to 3.16.1
2026-02-01 04:48:09 +00:00
dependabot[bot] c3c19c8018 build(deps): bump serde_with from 3.15.1 to 3.16.1
Bumps [serde_with](https://github.com/jonasbb/serde_with) from 3.15.1 to 3.16.1.
- [Release notes](https://github.com/jonasbb/serde_with/releases)
- [Commits](https://github.com/jonasbb/serde_with/compare/v3.15.1...v3.16.1)

---
updated-dependencies:
- dependency-name: serde_with
  dependency-version: 3.16.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-01 04:19:31 +00:00
auto-merge-dependabot-prs[bot] 0d2078b32b Merge pull request #3569 from sharkdp/dependabot/cargo/clap-4.5.56
build(deps): bump clap from 4.5.46 to 4.5.56
2026-02-01 04:15:25 +00:00
dependabot[bot] cddfad83e4 build(deps): bump clap from 4.5.46 to 4.5.56
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.46 to 4.5.56.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.46...clap_complete-v4.5.56)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.56
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-01 03:49:22 +00:00
dddffgg 2e355ffa4e Merge branch 'master' into feat/quiet-empty 2026-02-01 11:48:43 +08:00
dddffgg fa66d8e3ef fix: correct position of --quiet-empty in long-help.txt 2026-02-01 11:48:10 +08:00
dddffgg cb83b8fb9a fix: correct position of --quiet-empty in short-help.txt 2026-02-01 11:47:24 +08:00
auto-merge-dependabot-prs[bot] adfdcf4c6d Merge pull request #3564 from sharkdp/dependabot/cargo/git2-0.20.3
build(deps): bump git2 from 0.20.2 to 0.20.3
2026-02-01 03:39:56 +00:00
dependabot[bot] 593cf76811 build(deps): bump git2 from 0.20.2 to 0.20.3
Bumps [git2](https://github.com/rust-lang/git2-rs) from 0.20.2 to 0.20.3.
- [Changelog](https://github.com/rust-lang/git2-rs/blob/git2-0.20.3/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/git2-rs/compare/git2-0.20.2...git2-0.20.3)

---
updated-dependencies:
- dependency-name: git2
  dependency-version: 0.20.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-01 03:03:36 +00:00
dddffgg 3aaec8cb3b docs: update long-help.txt with --quiet-empty flag 2026-01-31 17:02:24 +08:00
dddffgg 2030ffa664 docs: update short-help.txt with --quiet-empty flag 2026-01-31 17:02:12 +08:00
dddffgg edb8342eab docs: fix changelog entry to reference PR number 2026-01-31 16:48:03 +08:00
dddffgg 6a7936a26f test: add integration tests for --quiet-empty flag 2026-01-31 16:45:46 +08:00
dddffgg 26118afde0 docs: add changelog entry for --quiet-empty flag 2026-01-31 16:44:39 +08:00
dddffgg 783acbc83d feat: implement quiet_empty behavior in printer 2026-01-31 16:42:28 +08:00
dddffgg 6f0a61cef9 feat: wire up quiet_empty config in app 2026-01-31 16:41:46 +08:00
dddffgg 53a10e0b0a feat: add --quiet-empty CLI flag 2026-01-31 16:41:13 +08:00
dddffgg 626154317f feat: add quiet_empty config option 2026-01-31 16:40:59 +08:00
Keith Hall 885ae1dc81 Merge pull request #3561 from cerdelen/small_typo_fix
Small typo fixes
2026-01-28 22:03:09 +02:00
Cedric Erdelen f6f1f18e9c remove trailing whitespace 2026-01-28 19:59:10 +01:00
cerdelen 69a2ffbdac Update src/config.rs
Co-authored-by: Keith Hall <keith-hall@users.noreply.github.com>
2026-01-28 19:48:02 +01:00
Cedric Erdelen 6b8decf2a4 Small typo fixes 2026-01-28 14:12:32 +01:00
Keith Hall cd06fe46e0 Merge pull request #3552 from CosmicHorrorDev/remove-vs-dark+
Remove Visual Studio Dark+ theme
2026-01-15 22:13:41 +02:00
Cosmic Horror 099d716455 Remove Visual Studio Dark+ theme 2026-01-14 21:13:44 -07:00
auto-merge-dependabot-prs[bot] 298e1f5321 Merge pull request #3506 from sharkdp/dependabot/github_actions/actions/checkout-6
build(deps): bump actions/checkout from 5 to 6
2026-01-12 05:14:43 +00:00
dependabot[bot] 3cb55e85d0 build(deps): bump actions/checkout from 5 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-12 06:04:14 +01:00
Keith Hall 981f25989a Merge pull request #3550 from nmacl/master
Use cargo_bin! macro to fix tests for future Cargo versions
2026-01-10 13:53:09 +02:00
Your Name 57bf8ff7da Update changelog with PR number 2026-01-08 09:45:41 -05:00
Your Name 31f982e736 Fixed formatting 2026-01-08 09:42:42 -05:00
Your Name 0a97a1edd7 Add changelog entry 2026-01-08 09:35:01 -05:00
Your Name 6c75acfffc Replace deprecated cargo_bin with cargo_bin! macro
The old Command::cargo_bin() is deprecated and will break when Cargo
changes build directory layout. Updated to use cargo_bin!() macro instead.

Bumped assert_cmd to 2.0.16 to get the new macro.

Fixes #3528
2026-01-08 09:28:11 -05:00
auto-merge-dependabot-prs[bot] 4e84e838a3 Merge pull request #3544 from sharkdp/dependabot/cargo/shell-words-1.1.1
build(deps): bump shell-words from 1.1.0 to 1.1.1
2026-01-01 04:33:25 +00:00
dependabot[bot] d48bf240a7 build(deps): bump shell-words from 1.1.0 to 1.1.1
Bumps [shell-words](https://github.com/tmiasko/shell-words) from 1.1.0 to 1.1.1.
- [Commits](https://github.com/tmiasko/shell-words/compare/v1.1.0...v1.1.1)

---
updated-dependencies:
- dependency-name: shell-words
  dependency-version: 1.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-01 04:20:47 +00:00
auto-merge-dependabot-prs[bot] 7dc180eefb Merge pull request #3542 from sharkdp/dependabot/cargo/console-0.16.2
build(deps): bump console from 0.16.1 to 0.16.2
2026-01-01 04:18:01 +00:00
dependabot[bot] 3276e9469a build(deps): bump console from 0.16.1 to 0.16.2
Bumps [console](https://github.com/console-rs/console) from 0.16.1 to 0.16.2.
- [Release notes](https://github.com/console-rs/console/releases)
- [Changelog](https://github.com/console-rs/console/blob/main/CHANGELOG.md)
- [Commits](https://github.com/console-rs/console/compare/0.16.1...0.16.2)

---
updated-dependencies:
- dependency-name: console
  dependency-version: 0.16.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-01 04:03:19 +00:00
auto-merge-dependabot-prs[bot] 60902c796f Merge pull request #3541 from sharkdp/dependabot/cargo/nu-ansi-term-0.50.3
build(deps): bump nu-ansi-term from 0.50.1 to 0.50.3
2026-01-01 04:00:33 +00:00
dependabot[bot] c12cdcace3 build(deps): bump nu-ansi-term from 0.50.1 to 0.50.3
Bumps [nu-ansi-term](https://github.com/nushell/nu-ansi-term) from 0.50.1 to 0.50.3.
- [Release notes](https://github.com/nushell/nu-ansi-term/releases)
- [Changelog](https://github.com/nushell/nu-ansi-term/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nushell/nu-ansi-term/compare/v0.50.1...v0.50.3)

---
updated-dependencies:
- dependency-name: nu-ansi-term
  dependency-version: 0.50.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-01 03:19:08 +00:00
auto-merge-dependabot-prs[bot] 823fd9f5c6 Merge pull request #3539 from sharkdp/dependabot/cargo/tempfile-3.23.0
build(deps): bump tempfile from 3.16.0 to 3.23.0
2026-01-01 03:15:20 +00:00
dependabot[bot] 88d6fd0248 build(deps): bump tempfile from 3.16.0 to 3.23.0
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.16.0 to 3.23.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.16.0...v3.23.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-version: 3.23.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-01 03:02:10 +00:00
Keith Hall 32047346e3 Merge pull request #3534 from ltrzesniewski/windows-clone
Make the repository clone correctly under Windows
2025-12-28 08:02:56 +02:00
Lucas Trzesniewski 56678dc771 Change the Jinja2 fork URL 2025-12-28 07:53:27 +02:00
Lucas Trzesniewski b34a1e0cc2 Make the repository clone correctly under Windows 2025-12-28 07:53:27 +02:00
Keith Hall 41f55bf902 Merge pull request #3535 from ltrzesniewski/windows-build
Fix integration tests on Windows
2025-12-28 07:46:13 +02:00
Lucas Trzesniewski c8514543af Fix integration tests on Windows 2025-12-27 21:17:40 +01:00
Hakil ad608014d9 fix issue #3526 (#3529) 2025-12-23 19:08:38 +02:00
Anchal 782ce71247 Update issue references in CHANGELOG.md 2025-12-16 22:40:35 +05:30
Anchal 8c7fccc8a5 Update changelog for feature #3527 2025-12-16 22:33:44 +05:30
ANCHAL fca5502f21 Fix bat crash with BusyBox less on Windows
- Retrieve less version earlier in src/output.rs.
- Skip -K argument if less is detected as BusyBox version.
- Reuses the version check for the existing --no-init logic.
- Fixes #3518.
2025-12-16 22:25:27 +05:30
Keith Hall 9bf344f760 Merge pull request #3517 from akirk/strip-overstriking
Improve native man pages and command help syntax highlighting by stripping overstriking
2025-12-11 08:36:18 +02:00
Alex Kirk 4db185834b Update changelog 2025-12-11 06:12:44 +01:00
Alex Kirk 4549ab3f22 Move the strip_overstrike check 2025-12-11 06:06:07 +01:00
Alex Kirk 629a8968fc Remove unnecessary code change 2025-12-11 05:54:30 +01:00
Alex Kirk de414ed631 Limit overstrike stripping to man pages and help 2025-12-11 05:49:46 +01:00
Alex Kirk 59c5896902 Simplify MANPAGER guide 2025-12-11 05:49:46 +01:00
Alex Kirk b22fc5db6b Add show-all integration test 2025-12-11 05:49:46 +01:00
Alex Kirk f97f9ebf03 Remove double backspace scan 2025-12-11 05:49:46 +01:00
Alex Kirk 64a4b204a2 Only strip overstrike when a syntax highlighting theme is used 2025-12-11 05:49:46 +01:00
Alex Kirk 5fb8a25c30 Performance optimization 2025-12-11 05:49:46 +01:00
Alex Kirk 80ef6832d3 Add a changelog entry 2025-12-11 05:49:46 +01:00
Alex Kirk 51bdaa5f88 Strip overstriking to better support man pages 2025-12-11 05:49:46 +01:00
Keith Hall eb2a8e29c7 Merge pull request #3524 from sharkdp/help_custom_assets
Support custom assets (themes) when displaying help output
2025-12-10 22:39:51 +02:00
Keith Hall b7dd88eafd Support custom assets (themes) when displaying help output 2025-12-10 22:30:01 +02:00
Keith Hall 79fe5fea63 Merge pull request #3521 from sharkdp/rainbow_csv_strings
[CSV] don't apply string highlighting by default
2025-12-08 20:08:04 +02:00
Keith Hall d15e399fce [CSV] don't apply string highlighting by default 2025-12-07 21:55:05 +02:00
Keith Hall 4d188e45fe Merge pull request #3519 from sorairolake/change-zig-url
chore: Change URL of Zig submodule from GitHub to Codeberg
2025-12-06 13:18:58 +02:00
Shun Sakai abce6ac0d1 chore: Change URL of Zig submodule 2025-12-06 12:00:48 +09:00
Keith Hall 566595f0eb Merge pull request #3516 from sharkdp/fix_help_builtin_pager
Fix --help --pager=builtin
2025-12-05 22:22:22 +02:00
Keith Hall 1796324531 Fix --help --pager=builtin
Previously it still used less
2025-12-04 21:53:45 +02:00
David Peter 2371077352 Update sponsors section (#3514)
Removed Graphite sponsorship information from README.
2025-12-04 09:29:18 +01:00
Keith Hall 7e7ab8a860 Merge pull request #3511 from sharkdp/prepare_changelog
add empty changelog sections ready for future changes
2025-12-03 22:48:16 +02:00
Keith Hall c054541776 add empty changelog sections ready for future changes 2025-12-03 22:37:26 +02:00
auto-merge-dependabot-prs[bot] d6ffe3a9be Merge pull request #3503 from sharkdp/dependabot/cargo/execute-0.2.15
build(deps): bump execute from 0.2.13 to 0.2.15
2025-12-02 22:34:27 +00:00
dependabot[bot] 4a1ceb3ce0 build(deps): bump execute from 0.2.13 to 0.2.15
Bumps [execute](https://github.com/magiclen/execute) from 0.2.13 to 0.2.15.
- [Commits](https://github.com/magiclen/execute/compare/v0.2.13...v0.2.15)

---
updated-dependencies:
- dependency-name: execute
  dependency-version: 0.2.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-02 22:24:11 +00:00
auto-merge-dependabot-prs[bot] 45a6ee95d5 Merge pull request #3505 from sharkdp/dependabot/cargo/toml-0.9.8
build(deps): bump toml from 0.9.1 to 0.9.8
2025-12-02 22:21:11 +00:00
dependabot[bot] 3eeaf29675 build(deps): bump toml from 0.9.1 to 0.9.8
Bumps [toml](https://github.com/toml-rs/toml) from 0.9.1 to 0.9.8.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.9.1...toml-v0.9.8)

---
updated-dependencies:
- dependency-name: toml
  dependency-version: 0.9.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-02 22:06:24 +00:00
auto-merge-dependabot-prs[bot] 58d1fa83e9 Merge pull request #3504 from sharkdp/dependabot/cargo/proc-macro2-1.0.103
build(deps): bump proc-macro2 from 1.0.95 to 1.0.103
2025-12-02 22:04:00 +00:00
dependabot[bot] 9d4ca3bc22 build(deps): bump proc-macro2 from 1.0.95 to 1.0.103
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.95 to 1.0.103.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.95...1.0.103)

---
updated-dependencies:
- dependency-name: proc-macro2
  dependency-version: 1.0.103
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-02 21:48:06 +00:00
auto-merge-dependabot-prs[bot] 9b23b05841 Merge pull request #3502 from sharkdp/dependabot/cargo/console-0.16.1
build(deps): bump console from 0.16.0 to 0.16.1
2025-12-02 21:45:41 +00:00
dependabot[bot] f3e8ffc276 build(deps): bump console from 0.16.0 to 0.16.1
Bumps [console](https://github.com/console-rs/console) from 0.16.0 to 0.16.1.
- [Release notes](https://github.com/console-rs/console/releases)
- [Changelog](https://github.com/console-rs/console/blob/main/CHANGELOG.md)
- [Commits](https://github.com/console-rs/console/compare/0.16.0...0.16.1)

---
updated-dependencies:
- dependency-name: console
  dependency-version: 0.16.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-02 21:21:35 +00:00
auto-merge-dependabot-prs[bot] 9b73911f2c Merge pull request #3501 from sharkdp/dependabot/cargo/grep-cli-0.1.12
build(deps): bump grep-cli from 0.1.11 to 0.1.12
2025-12-02 21:19:00 +00:00
dependabot[bot] 25bfa9fa81 build(deps): bump grep-cli from 0.1.11 to 0.1.12
Bumps [grep-cli](https://github.com/BurntSushi/ripgrep) from 0.1.11 to 0.1.12.
- [Release notes](https://github.com/BurntSushi/ripgrep/releases)
- [Changelog](https://github.com/BurntSushi/ripgrep/blob/master/CHANGELOG.md)
- [Commits](https://github.com/BurntSushi/ripgrep/compare/grep-cli-0.1.11...grep-cli-0.1.12)

---
updated-dependencies:
- dependency-name: grep-cli
  dependency-version: 0.1.12
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-02 20:47:04 +00:00
Keith Hall 979ba22628 Merge pull request #3510 from sharkdp/prepare_release
Prepare for next release
2025-12-02 22:44:14 +02:00
Keith Hall 19ab724a73 Prepare for next release 2025-12-02 22:33:02 +02:00
Keith Hall def49e19a7 Merge pull request #3509 from sharkdp/ksh
Add syntax detection for files with ksh shebang lines
2025-12-02 22:15:48 +02:00
Keith Hall 1fb269a0fb Add syntax detection for files with ksh shebang lines 2025-12-02 22:06:21 +02:00
Keith Hall 0e469634a3 return None for get_pager_executable when builtin pager is used (#3498) 2025-12-01 20:02:55 +00:00
Keith Hall 6dfe471686 Merge pull request #3507 from sharkdp/help_read_theme_from_config
Ensure --help respects valid config file
2025-12-01 21:36:58 +02:00
Keith Hall 2c7fc5f926 update changelog 2025-12-01 06:51:04 +02:00
Keith Hall 579d371378 Ensure --help respects valid config file
While still ignoring ignoring invalid arguments in the config
2025-12-01 05:30:05 +02:00
auto-merge-dependabot-prs[bot] c74ca27075 Merge pull request #3500 from sharkdp/dependabot/submodules/assets/syntaxes/02_Extra/Idris2-bbfe50e
build(deps): bump assets/syntaxes/02_Extra/Idris2 from `2874f20` to `bbfe50e`
2025-12-01 03:21:57 +00:00
dependabot[bot] e8f7b12bb1 build(deps): bump assets/syntaxes/02_Extra/Idris2
Bumps [assets/syntaxes/02_Extra/Idris2](https://github.com/buzden/sublime-syntax-idris2) from `2874f20` to `bbfe50e`.
- [Commits](https://github.com/buzden/sublime-syntax-idris2/compare/2874f206f56c588a29fd76b88043129471d1454a...bbfe50e023e0edc74f5e0c003eb946528d49279f)

---
updated-dependencies:
- dependency-name: assets/syntaxes/02_Extra/Idris2
  dependency-version: bbfe50e023e0edc74f5e0c003eb946528d49279f
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-01 03:06:08 +00:00
Keith Hall e7006b53e3 Merge pull request #3497 from xduugu/quadlet
Add syntax mapping for podman's `artifact` quadlet files
2025-11-30 14:26:53 +02:00
Cedric Staniewski a6e0057c12 Add syntax mapping for podman's artifact quadlet files 2025-11-30 14:18:12 +02:00
Keith Hall 1ffed0c039 Merge pull request #3496 from sharkdp/decorations_auto
Restore expected behavior when piping output with styles configured and auto decorations
2025-11-30 05:14:01 +02:00
Keith Hall 602df893de Add additional test cases for -n in loop through mode 2025-11-30 05:02:13 +02:00
Keith Hall 3755f788ca Improve -n detection to handle combined flags correctly
Updated the logic to correctly handle combined short flags like -pn and -np.
The -n flag is only honored when it's either:
1. A standalone flag (-n or --number)
2. The last flag in a combined form that includes -p (e.g., -pn), or
3. In a combined form without -p (e.g., -An)

This ensures that -np (where -p overrides -n) correctly produces plain output,
while -pn (where -n overrides -p) produces line numbers.
2025-11-30 05:02:13 +02:00
Keith Hall abc7261488 Fix -n flag to show line numbers in loop-through mode
When the -n/--number flag is passed on the command line, bat now shows
line numbers even when piping output to another process (loop-through
mode), similar to how `cat -n` behaves.

This change detects if -n or --number was passed on the CLI (before
merging with config file and environment variables) and disables
loop-through mode in that case, allowing the InteractivePrinter to
add line numbers.

The existing behavior is preserved:
- Styles from config/env are still ignored when piping (unless --decorations=always is set)
- Only the -n flag from CLI enables line numbers in piped mode
- -p and --style options from CLI do not disable loop-through mode
2025-11-30 05:02:13 +02:00
Keith Hall f9c33971d9 Update readme to mention decorations 2025-11-30 05:02:13 +02:00
Keith Hall 7d37325c19 Fix to not show style decorations in auto mode when piping 2025-11-30 05:02:13 +02:00
Keith Hall d17481575a Merge pull request #3495 from sharkdp/powershell_completions
Update PowerShell completions for compatibility with PowerShell v5.1
2025-11-28 22:49:44 +02:00
Keith Hall 1ee1d8fdff update changelog 2025-11-27 21:57:47 +02:00
Keith Hall af8f0626ac Update PowerShell completions for compatibility with PowerShell v5.1
Modified the PowerShell completion script to replace the null-coalescing operator (`??` - a v7 feature) with a conditional statement compatible with PowerShell v5.1. The script now uses an if-else check for null descriptions in the language completion section.
2025-11-27 21:50:17 +02:00
Keith Hall c88353b386 Merge pull request #3476 from sharkdp/docker_syntax_fix
Revert "Bump assets/syntaxes/02_Extra/Docker from `0f6b7bc` to `c001fb2` (#3024)"
2025-11-26 22:19:06 +02:00
Keith Hall 961f367ca8 use newer MacOS runner images
otherwise CI fails due to the brownout https://github.com/actions/runner-images/issues/13046
2025-11-26 22:07:06 +02:00
Keith Hall 8bf3be5e61 Revert "Patch Dockerfile to replace reference to non-existent context"
This reverts commit a4212e99b0.

No longer needed because we reverted the Docker submodule.
2025-11-25 22:43:58 +02:00
Keith Hall f8b6a009cb commit syntax dump to get integration tests to pass 2025-11-24 22:30:34 +02:00
Keith Hall efa9f0e63d add integration test to prove that building the cache finds all contexts 2025-11-24 22:20:44 +02:00
Keith Hall a4212e99b0 Patch Dockerfile to replace reference to non-existent context 2025-11-24 22:05:31 +02:00
Keith Hall 6884a8e7e0 Update changelog 2025-11-24 21:56:05 +02:00
Keith Hall a1b1feb247 Revert "Bump assets/syntaxes/02_Extra/Docker from 0f6b7bc to c001fb2 (#3024)"
This reverts commit 3838766dd4.
2025-11-24 21:56:05 +02:00
Muntasir Mahmud 70ec3fc24e feat: add paging to '-h' and '--help' (#3478)
* feat: add paging to '-h' and '--help'

Fixes #1587
2025-11-23 20:10:56 +00:00
Keith Hall 1e4a4b765f Merge pull request #3457 from abhinavcool42/fix/list-themes-pager-hang
fix: `--list-themes` pagination
2025-11-15 15:49:40 +02:00
Keith Hall cfe49779fa Merge branch 'master' into fix/list-themes-pager-hang 2025-11-15 15:41:07 +02:00
Keith Hall c51cff8961 Merge pull request #3481 from AldanTanneo/master
update Ada syntax gitmodule
2025-11-14 19:46:22 +02:00
AldanTanneo 1abecd2c93 update Ada syntax gitmodule 2025-11-14 11:41:08 +01:00
Keith Hall 3217a7e667 Merge pull request #3484 from cyqsimon/quadlet-update
Update quadlet syntax mapping to include `*.{build,pod}` files
2025-11-13 19:15:28 +02:00
cyqsimon 015cafdbdc Write changelog 2025-11-13 13:50:53 +08:00
cyqsimon f5a934c213 Update quadlet syntax mapping to include *.{build,pod} files 2025-11-13 13:44:24 +08:00
Abhinav Diwakar 24813db49f Merge branch 'master' into fix/list-themes-pager-hang 2025-11-06 23:26:06 +05:30
Benjamin A. Beasley ce856dbba7 Update etcetera to 0.11; no longer depend on home; MSRV 1.87
Release 0.11 of `etcetera` requires MSRV 1.87, in which
`std::env::home_dir` is no longer deprecated,
https://github.com/rust-lang/rust/pull/137327.

Update to that MSRV and to `etcetera`, and drop the dependency on the
`home` crate just as `etcetera` 0.11 did.
2025-11-03 19:06:33 +01:00
abhinavcool42 8b09724420 change imports 2025-11-02 11:55:30 +05:30
abhinavcool42 2af7be1b65 fix prefix 2025-11-02 11:51:00 +05:30
Abhinav Diwakar dac98da526 Merge branch 'master' into fix/list-themes-pager-hang 2025-11-02 11:35:27 +05:30
abhinavcool42 9456f7d8cd list-themes buffer added 2025-11-02 11:31:07 +05:30
auto-merge-dependabot-prs[bot] 3ecbd18e56 Merge pull request #3469 from sharkdp/dependabot/submodules/assets/syntaxes/02_Extra/typst-syntax-highlight-363f0e7
build(deps): bump assets/syntaxes/02_Extra/typst-syntax-highlight from `4e2e68b` to `363f0e7`
2025-11-01 10:41:09 +00:00
dependabot[bot] 61fbc3fc0c build(deps): bump assets/syntaxes/02_Extra/typst-syntax-highlight
Bumps [assets/syntaxes/02_Extra/typst-syntax-highlight](https://github.com/hyrious/typst-syntax-highlight) from `4e2e68b` to `363f0e7`.
- [Release notes](https://github.com/hyrious/typst-syntax-highlight/releases)
- [Commits](https://github.com/hyrious/typst-syntax-highlight/compare/4e2e68b0a13555720a5ff0c4b32db98ddf490ed1...363f0e767c938c615a14912c302db7936f025fc2)

---
updated-dependencies:
- dependency-name: assets/syntaxes/02_Extra/typst-syntax-highlight
  dependency-version: 363f0e767c938c615a14912c302db7936f025fc2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-01 10:24:37 +00:00
auto-merge-dependabot-prs[bot] 5bcbd91943 Merge pull request #3472 from sharkdp/dependabot/submodules/assets/syntaxes/02_Extra/Zig-c16d871
build(deps): bump assets/syntaxes/02_Extra/Zig from `8a4a3fe` to `c16d871`
2025-11-01 10:21:28 +00:00
dependabot[bot] cfe32fa219 build(deps): bump assets/syntaxes/02_Extra/Zig
Bumps [assets/syntaxes/02_Extra/Zig](https://github.com/ziglang/sublime-zig-language) from `8a4a3fe` to `c16d871`.
- [Release notes](https://github.com/ziglang/sublime-zig-language/releases)
- [Commits](https://github.com/ziglang/sublime-zig-language/compare/8a4a3fe4a051f85c4752b82f586d395cab843c06...c16d871ccccb3749f5a4b824f3fd44b143114565)

---
updated-dependencies:
- dependency-name: assets/syntaxes/02_Extra/Zig
  dependency-version: c16d871ccccb3749f5a4b824f3fd44b143114565
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-01 10:05:25 +00:00
auto-merge-dependabot-prs[bot] f0f4af864a Merge pull request #3470 from sharkdp/dependabot/submodules/assets/syntaxes/02_Extra/cmd-help-273cb98
build(deps): bump assets/syntaxes/02_Extra/cmd-help from `c71ba41` to `273cb98`
2025-11-01 10:01:27 +00:00
dependabot[bot] 8bace0e808 build(deps): bump assets/syntaxes/02_Extra/cmd-help
Bumps [assets/syntaxes/02_Extra/cmd-help](https://github.com/victor-gp/cmd-help-sublime-syntax) from `c71ba41` to `273cb98`.
- [Commits](https://github.com/victor-gp/cmd-help-sublime-syntax/compare/c71ba410bdfcc8f627df3219f14e3f2be4fe68ba...273cb988177e96f4187e06008b13fa72ad22ae4d)

---
updated-dependencies:
- dependency-name: assets/syntaxes/02_Extra/cmd-help
  dependency-version: 273cb988177e96f4187e06008b13fa72ad22ae4d
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-01 09:40:14 +00:00
auto-merge-dependabot-prs[bot] 2f5cba7f8f Merge pull request #3473 from sharkdp/dependabot/submodules/assets/syntaxes/02_Extra/SystemVerilog-b340f1c
build(deps): bump assets/syntaxes/02_Extra/SystemVerilog from `7eca705` to `b340f1c`
2025-11-01 09:37:25 +00:00
dependabot[bot] 528aa4fedd build(deps): bump assets/syntaxes/02_Extra/SystemVerilog
Bumps [assets/syntaxes/02_Extra/SystemVerilog](https://github.com/TheClams/SystemVerilog) from `7eca705` to `b340f1c`.
- [Commits](https://github.com/TheClams/SystemVerilog/compare/7eca705e87f87b94478fe222fc91d54d488cc8e3...b340f1c7f6a38d4da9f77be5abd6d455e5da7641)

---
updated-dependencies:
- dependency-name: assets/syntaxes/02_Extra/SystemVerilog
  dependency-version: b340f1c7f6a38d4da9f77be5abd6d455e5da7641
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-01 09:27:44 +00:00
auto-merge-dependabot-prs[bot] 490e1580ad Merge pull request #3471 from sharkdp/dependabot/cargo/serde_with-3.15.1
build(deps): bump serde_with from 3.12.0 to 3.15.1
2025-11-01 05:15:10 +00:00
dependabot[bot] cbfbfc8ff3 build(deps): bump serde_with from 3.12.0 to 3.15.1
Bumps [serde_with](https://github.com/jonasbb/serde_with) from 3.12.0 to 3.15.1.
- [Release notes](https://github.com/jonasbb/serde_with/releases)
- [Commits](https://github.com/jonasbb/serde_with/compare/v3.12.0...v3.15.1)

---
updated-dependencies:
- dependency-name: serde_with
  dependency-version: 3.15.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-01 05:03:16 +00:00
auto-merge-dependabot-prs[bot] b6edf7ab0b Merge pull request #3468 from sharkdp/dependabot/cargo/prettyplease-0.2.37
build(deps): bump prettyplease from 0.2.35 to 0.2.37
2025-11-01 05:00:21 +00:00
dependabot[bot] 65e6b4de69 build(deps): bump prettyplease from 0.2.35 to 0.2.37
Bumps [prettyplease](https://github.com/dtolnay/prettyplease) from 0.2.35 to 0.2.37.
- [Release notes](https://github.com/dtolnay/prettyplease/releases)
- [Commits](https://github.com/dtolnay/prettyplease/compare/0.2.35...0.2.37)

---
updated-dependencies:
- dependency-name: prettyplease
  dependency-version: 0.2.37
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-01 04:42:58 +00:00
auto-merge-dependabot-prs[bot] 2ca62e25f1 Merge pull request #3474 from sharkdp/dependabot/cargo/git2-0.20.2
build(deps): bump git2 from 0.20.0 to 0.20.2
2025-11-01 04:39:53 +00:00
dependabot[bot] 1b2a814478 build(deps): bump git2 from 0.20.0 to 0.20.2
Bumps [git2](https://github.com/rust-lang/git2-rs) from 0.20.0 to 0.20.2.
- [Changelog](https://github.com/rust-lang/git2-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/git2-rs/compare/git2-0.20.0...git2-0.20.2)

---
updated-dependencies:
- dependency-name: git2
  dependency-version: 0.20.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-01 04:22:33 +00:00
auto-merge-dependabot-prs[bot] e33967aa39 Merge pull request #3467 from sharkdp/dependabot/cargo/regex-1.12.2
build(deps): bump regex from 1.11.1 to 1.12.2
2025-11-01 04:17:04 +00:00
dependabot[bot] a1adefce36 build(deps): bump regex from 1.11.1 to 1.12.2
Bumps [regex](https://github.com/rust-lang/regex) from 1.11.1 to 1.12.2.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.11.1...1.12.2)

---
updated-dependencies:
- dependency-name: regex
  dependency-version: 1.12.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-01 03:40:47 +00:00
auto-merge-dependabot-prs[bot] 6e87e02e96 Merge pull request #3465 from sharkdp/dependabot/cargo/terminal-colorsaurus-1.0.1
build(deps): bump terminal-colorsaurus from 1.0.0 to 1.0.1
2025-11-01 03:37:12 +00:00
dependabot[bot] ab9cc674d5 build(deps): bump terminal-colorsaurus from 1.0.0 to 1.0.1
Bumps [terminal-colorsaurus](https://github.com/bash/terminal-colorsaurus) from 1.0.0 to 1.0.1.
- [Release notes](https://github.com/bash/terminal-colorsaurus/releases)
- [Changelog](https://github.com/bash/terminal-colorsaurus/blob/main/changelog.md)
- [Commits](https://github.com/bash/terminal-colorsaurus/compare/1.0.0...1.0.1)

---
updated-dependencies:
- dependency-name: terminal-colorsaurus
  dependency-version: 1.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-01 03:02:09 +00:00
Abhinav Diwakar 8e0e13a899 Merge branch 'master' into fix/list-themes-pager-hang 2025-11-01 00:59:59 +05:30
Jack Haden-Enneking 5371426588 alternatives.md: fix emoji & formatting
Could replace all emoji shortcodes (`✔️` & ``) in source with actual emoji. Erred on the side of least change.
2025-10-31 19:32:29 +01:00
abhinavcool42 1852dea96a Fix --list-themes pager init so less stays interactive 2025-10-30 18:42:35 +05:30
abhinavcool42 a8140f91cc changelog 2025-10-28 17:25:43 +05:30
abhinavcool42 2fac9b2740 added changelog 2025-10-28 17:07:12 +05:30
abhinavcool42 6a99915afe fix --list-themes hang 2025-10-28 16:10:32 +05:30
Justin Su 4e13c3eb5b Update CHANGELOG.md
Also fix a typo from the previous release
2025-10-20 19:23:26 +02:00
Justin Su 7e55608975 docs: Clarify that -X/--no-init is not added on modern versions of less 2025-10-20 19:23:26 +02:00
Justin Su 4bd5cbca8e docs: Mention less options by short+long names 2025-10-20 19:23:26 +02:00
Keith Hall 7c3fa7e1ce Merge pull request #3448 from akinomyoga/bash-fullquote
Use `-o fullquote` and `-o noquote` for more robust escaping in Bash completions
2025-10-20 19:55:56 +03:00
Koichi Murase 3188a147d8 Use "-o fullquote" and "-o noquote" to escape Bash completions 2025-10-20 19:38:54 +09:00
Koichi Murase 926fbc4b13 Fix indentation of the Bash completion file 2025-10-20 17:59:48 +09:00
Keith Hall 006d77fa39 Merge pull request #3442 from lmmx/patch-1
fix: allow hyphen values to `-r`/`--line-range`
2025-10-19 23:44:25 +03:00
Louis Maddox 52a792d46f fix: allow hyphen values to -r/--line-range
via #2944
2025-10-19 21:24:59 +01:00
Keith Hall 20db989fb1 Merge pull request #3441 from sharkdp/prepare_changelog
Prepare changelog for future changes
2025-10-19 21:40:50 +03:00
Keith Hall 2c63d3c792 Prepare changelog for future changes 2025-10-19 21:24:54 +03:00
101 changed files with 4066 additions and 1144 deletions
+11
View File
@@ -0,0 +1,11 @@
# On Windows MSVC, statically link the C runtime so that the resulting EXE does
# not depend on the vcruntime DLL.
#
# See: https://github.com/sharkdp/bat/issues/3634
[target.x86_64-pc-windows-msvc]
rustflags = ["-C", "target-feature=+crt-static"]
[target.i686-pc-windows-msvc]
rustflags = ["-C", "target-feature=+crt-static"]
[target.aarch64-pc-windows-msvc]
rustflags = ["-C", "target-feature=+crt-static"]
+1
View File
@@ -0,0 +1 @@
use flake
+25 -27
View File
@@ -35,7 +35,7 @@ jobs:
name: Extract crate metadata name: Extract crate metadata
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Extract crate information - name: Extract crate information
id: crate_metadata id: crate_metadata
run: | run: |
@@ -58,7 +58,7 @@ jobs:
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
with: with:
components: rustfmt,clippy components: rustfmt,clippy
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- run: cargo fmt -- --check - run: cargo fmt -- --check
- run: cargo clippy --locked --all-targets --all-features -- -D warnings - run: cargo clippy --locked --all-targets --all-features -- -D warnings
@@ -68,7 +68,7 @@ jobs:
needs: crate_metadata needs: crate_metadata
steps: steps:
- name: Checkout source code - name: Checkout source code
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Install rust toolchain (v${{ needs.crate_metadata.outputs.msrv }}) - name: Install rust toolchain (v${{ needs.crate_metadata.outputs.msrv }})
uses: dtolnay/rust-toolchain@master uses: dtolnay/rust-toolchain@master
with: with:
@@ -80,7 +80,7 @@ jobs:
name: License checks name: License checks
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
with: with:
submodules: true # we especially want to perform license checks on submodules submodules: true # we especially want to perform license checks on submodules
- run: tests/scripts/license-checks.sh - run: tests/scripts/license-checks.sh
@@ -90,7 +90,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Git checkout - name: Git checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
submodules: true # we need all syntax and theme submodules submodules: true # we need all syntax and theme submodules
- name: Install Rust toolchain - name: Install Rust toolchain
@@ -119,7 +119,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Git checkout - name: Git checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Prepare environment variables - name: Prepare environment variables
run: | run: |
echo "BAT_SYSTEM_CONFIG_PREFIX=$GITHUB_WORKSPACE/tests/examples/system_config" >> $GITHUB_ENV echo "BAT_SYSTEM_CONFIG_PREFIX=$GITHUB_WORKSPACE/tests/examples/system_config" >> $GITHUB_ENV
@@ -135,7 +135,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Git checkout - name: Git checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Install Rust toolchain - name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
- name: Check documentation - name: Check documentation
@@ -150,7 +150,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- run: cargo install cargo-audit --locked - run: cargo install cargo-audit --locked
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- run: cargo audit - run: cargo audit
build: build:
@@ -161,24 +161,24 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
job: job:
- { target: aarch64-unknown-linux-musl , os: ubuntu-latest, dpkg_arch: arm64, 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: 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-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: 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-pc-windows-msvc , os: windows-2025 , }
- { target: i686-unknown-linux-gnu , os: ubuntu-latest, dpkg_arch: i686, use-cross: true } - { 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: i686-unknown-linux-musl , os: ubuntu-latest , dpkg_arch: musl-linux-i686, use-cross: true }
- { target: x86_64-apple-darwin , os: macos-13, } - { target: x86_64-apple-darwin , os: macos-15-intel, }
- { target: aarch64-apple-darwin , os: macos-14, } - { target: aarch64-apple-darwin , os: macos-latest , }
- { target: x86_64-pc-windows-msvc , os: windows-2025, } - { target: x86_64-pc-windows-msvc , os: windows-2025 , }
- { target: aarch64-pc-windows-msvc , os: windows-11-arm, } - { 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-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: x86_64-unknown-linux-musl , os: ubuntu-latest , dpkg_arch: musl-linux-amd64, use-cross: true }
env: env:
BUILD_CMD: cargo BUILD_CMD: cargo
steps: steps:
- name: Checkout source code - name: Checkout source code
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Install prerequisites - name: Install prerequisites
shell: bash shell: bash
@@ -195,9 +195,7 @@ jobs:
- name: Install cross - name: Install cross
if: matrix.job.use-cross if: matrix.job.use-cross
uses: taiki-e/install-action@v2 run: cargo install cross --git https://github.com/cross-rs/cross --rev 588b3c99db52b5a9c5906fab96cfadcf1bde7863
with:
tool: cross
- name: Overwrite build command env variable - name: Overwrite build command env variable
if: matrix.job.use-cross if: matrix.job.use-cross
@@ -335,7 +333,7 @@ jobs:
DPKG_BASENAME=${{ needs.crate_metadata.outputs.name }} DPKG_BASENAME=${{ needs.crate_metadata.outputs.name }}
DPKG_CONFLICTS=${{ needs.crate_metadata.outputs.name }}-musl 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_VERSION=${{ needs.crate_metadata.outputs.version }}
DPKG_ARCH="${{ matrix.job.dpkg_arch }}" DPKG_ARCH="${{ matrix.job.dpkg_arch }}"
DPKG_NAME="${DPKG_BASENAME}_${DPKG_VERSION}_${DPKG_ARCH}.deb" DPKG_NAME="${DPKG_BASENAME}_${DPKG_VERSION}_${DPKG_ARCH}.deb"
@@ -444,7 +442,7 @@ jobs:
echo "IS_RELEASE=${IS_RELEASE}" >> $GITHUB_OUTPUT echo "IS_RELEASE=${IS_RELEASE}" >> $GITHUB_OUTPUT
- name: Publish archives and packages - name: Publish archives and packages
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v3
if: steps.is-release.outputs.IS_RELEASE if: steps.is-release.outputs.IS_RELEASE
with: with:
files: | files: |
@@ -13,7 +13,7 @@ jobs:
PR_NUMBER: ${{ github.event.number }} PR_NUMBER: ${{ github.event.number }}
PR_BASE: ${{ github.base_ref }} PR_BASE: ${{ github.base_ref }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Fetch PR base - name: Fetch PR base
run: git fetch --no-tags --prune --depth=1 origin run: git fetch --no-tags --prune --depth=1 origin
+7
View File
@@ -1,6 +1,11 @@
.direnv/
/target/ /target/
**/*.rs.bk **/*.rs.bk
# Editors
.idea/
.vscode/
# Generated files # Generated files
/assets/completions/_bat.ps1 /assets/completions/_bat.ps1
/assets/completions/bat.bash /assets/completions/bat.bash
@@ -8,3 +13,5 @@
/assets/completions/bat.zsh /assets/completions/bat.zsh
/assets/manual/bat.1 /assets/manual/bat.1
/assets/metadata.yaml /assets/metadata.yaml
+9 -9
View File
@@ -49,9 +49,6 @@
[submodule "assets/themes/zenburn"] [submodule "assets/themes/zenburn"]
path = assets/themes/zenburn path = assets/themes/zenburn
url = https://github.com/colinta/zenburn.git url = https://github.com/colinta/zenburn.git
[submodule "assets/syntaxes/Kotlin"]
path = assets/syntaxes/02_Extra/Kotlin
url = https://github.com/vkostyukov/kotlin-sublime-package
[submodule "assets/syntaxes/Elm"] [submodule "assets/syntaxes/Elm"]
path = assets/syntaxes/02_Extra/Elm path = assets/syntaxes/02_Extra/Elm
url = https://github.com/elm-community/SublimeElmLanguageSupport url = https://github.com/elm-community/SublimeElmLanguageSupport
@@ -130,7 +127,7 @@
url = https://github.com/djuretic/SublimeStrace url = https://github.com/djuretic/SublimeStrace
[submodule "assets/syntaxes/Jinja2"] [submodule "assets/syntaxes/Jinja2"]
path = assets/syntaxes/02_Extra/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"] [submodule "assets/syntaxes/SLS"]
path = assets/syntaxes/02_Extra/SLS path = assets/syntaxes/02_Extra/SLS
url = https://github.com/saltstack/sublime-text url = https://github.com/saltstack/sublime-text
@@ -196,16 +193,13 @@
url = https://github.com/leanprover/vscode-lean4.git url = https://github.com/leanprover/vscode-lean4.git
[submodule "assets/syntaxes/02_Extra/Zig"] [submodule "assets/syntaxes/02_Extra/Zig"]
path = 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"] [submodule "assets/syntaxes/02_Extra/gnuplot"]
path = assets/syntaxes/02_Extra/gnuplot path = assets/syntaxes/02_Extra/gnuplot
url = https://github.com/hesstobi/sublime_gnuplot url = https://github.com/hesstobi/sublime_gnuplot
[submodule "assets/syntaxes/02_Extra/SystemVerilog"] [submodule "assets/syntaxes/02_Extra/SystemVerilog"]
path = assets/syntaxes/02_Extra/SystemVerilog path = assets/syntaxes/02_Extra/SystemVerilog
url = https://github.com/TheClams/SystemVerilog.git 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"] [submodule "assets/syntaxes/02_Extra/SublimeEthereum"]
path = assets/syntaxes/02_Extra/SublimeEthereum path = assets/syntaxes/02_Extra/SublimeEthereum
url = https://github.com/davidhq/SublimeEthereum.git url = https://github.com/davidhq/SublimeEthereum.git
@@ -243,7 +237,7 @@
url = https://github.com/dertuxmalwieder/SublimeTodoTxt url = https://github.com/dertuxmalwieder/SublimeTodoTxt
[submodule "assets/syntaxes/02_Extra/Ada"] [submodule "assets/syntaxes/02_Extra/Ada"]
path = 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"] [submodule "assets/syntaxes/02_Extra/Crontab"]
path = assets/syntaxes/02_Extra/Crontab path = assets/syntaxes/02_Extra/Crontab
@@ -281,3 +275,9 @@
[submodule "assets/syntaxes/02_Extra/Gomod"] [submodule "assets/syntaxes/02_Extra/Gomod"]
path = assets/syntaxes/02_Extra/Gomod path = assets/syntaxes/02_Extra/Gomod
url = https://github.com/mitranim/sublime-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
[submodule "assets/syntaxes/02_Extra/Kotlin"]
path = assets/syntaxes/02_Extra/Kotlin
url = https://github.com/guille/sublime-kotlin
+100 -1
View File
@@ -1,3 +1,102 @@
# unreleased
## Other
- Add instructions for removing fish help abbreviations to README, see #3655 (@claw-explorer). Closes #3536
- Add .NET slnx extension, see #3682 (@ltrzesniewski)
## 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)
- Support configuring `--terminal-width` via `BAT_WIDTH`, see #3679 (@officialasishkumar)
- 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)
- Syntax highlighting for Python files using uv as script runner in shebang #3689 (@janlarres)
## Bugfixes
- Fix `--list-themes` unconditionally probing the terminal via OSC 10/11 even when `--theme` was set to an explicit value, see #3700 (regression introduced in bc42149a). (@optimistiCli)
- Fix inverted `$LESSCLOSE` warning so bat warns on nonzero exit, not on success. See #3654 (@cuiweixie)
- Sanitize control characters in filenames before displaying them in the file header, error messages, and the terminal title, preventing ANSI escape injection via crafted filenames. Closes #3054, see #3691 (@curious-rabbit)
- Report initial input read errors instead of treating them as empty input. Closes #3002, see #3706 (@lawrence3699)
- Treat ZIP archives as binary content based on their magic header, see #3686 (@officialasishkumar)
- 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)
- Fix zsh tab completion word-splitting language names containing spaces (e.g. `HTML (Jinja2)`, `Apache Conf`), see #3693 (@YoshKoz)
## 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)
- Statically link the CRT for MSVC builds via Cargo config to avoid runtime DLL dependencies. Closes #3634, see #3692 (@barry3406)
## Syntaxes
- Add shebang-based detection for Tcl (`tclsh`, `wish`) and Expect (`expect`) scripts, see #3647 (@mvanhorn)
- 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)
- Improved Kotlin syntax, see #3699 (@guille)
## 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 # v0.26.0
## Features ## Features
@@ -29,7 +128,7 @@
- Update base16 README links to community driven base16 work #2871 (@JamyGolden) - Update base16 README links to community driven base16 work #2871 (@JamyGolden)
- Work around build failures when building `bat` from vendored sources #3179 (@dtolnay) - 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: 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) - Build script: replace string-based codegen with quote-based codegen #3340 (@cyqsimon)
- Improve code coverage of `--list-languages` parameter #2942 (@sblondon) - Improve code coverage of `--list-languages` parameter #2942 (@sblondon)
- Only start offload worker thread when there's more than 1 core #2956 (@cyqsimon) - Only start offload worker thread when there's more than 1 core #2956 (@cyqsimon)
+28
View File
@@ -68,6 +68,8 @@ users, please read the corresponding
[documentation](https://github.com/sharkdp/bat/blob/master/doc/assets.md) [documentation](https://github.com/sharkdp/bat/blob/master/doc/assets.md)
first. 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. 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 [Syntax
tests](https://github.com/sharkdp/bat/blob/master/doc/assets.md#syntax-tests) tests](https://github.com/sharkdp/bat/blob/master/doc/assets.md#syntax-tests)
section in `assets.md`. 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
+155 -212
View File
@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 4
[[package]] [[package]]
name = "adler2" name = "adler2"
@@ -83,13 +83,12 @@ checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
[[package]] [[package]]
name = "assert_cmd" name = "assert_cmd"
version = "2.0.16" version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" checksum = "bcbb6924530aa9e0432442af08bbcafdad182db80d2e560da42a6d442535bf85"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"bstr", "bstr",
"doc-comment",
"libc", "libc",
"predicates", "predicates",
"predicates-core", "predicates-core",
@@ -111,7 +110,7 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]] [[package]]
name = "bat" name = "bat"
version = "0.26.0" version = "0.26.1"
dependencies = [ dependencies = [
"ansi_colours", "ansi_colours",
"anyhow", "anyhow",
@@ -131,7 +130,6 @@ dependencies = [
"git2", "git2",
"globset", "globset",
"grep-cli", "grep-cli",
"home",
"indexmap", "indexmap",
"itertools", "itertools",
"minus", "minus",
@@ -225,9 +223,9 @@ checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
[[package]] [[package]]
name = "bytesize" name = "bytesize"
version = "1.3.0" version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3"
[[package]] [[package]]
name = "cc" name = "cc"
@@ -254,18 +252,18 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.46" version = "4.5.60"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57" checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
] ]
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.46" version = "4.5.60"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41" checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@@ -276,9 +274,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.7.4" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
[[package]] [[package]]
name = "clircle" name = "clircle"
@@ -298,15 +296,15 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]] [[package]]
name = "console" name = "console"
version = "0.16.0" version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e09ced7ebbccb63b4c65413d821f2e00ce54c5ca4514ddc6b3c892fdbcbc69d" checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4"
dependencies = [ dependencies = [
"encode_unicode", "encode_unicode",
"libc", "libc",
"once_cell", "once_cell",
"unicode-width", "unicode-width",
"windows-sys 0.60.2", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
@@ -394,9 +392,9 @@ dependencies = [
[[package]] [[package]]
name = "darling" name = "darling"
version = "0.20.10" version = "0.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"darling_macro", "darling_macro",
@@ -404,9 +402,9 @@ dependencies = [
[[package]] [[package]]
name = "darling_core" name = "darling_core"
version = "0.20.10" version = "0.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4"
dependencies = [ dependencies = [
"fnv", "fnv",
"ident_case", "ident_case",
@@ -418,9 +416,9 @@ dependencies = [
[[package]] [[package]]
name = "darling_macro" name = "darling_macro"
version = "0.20.10" version = "0.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
@@ -442,9 +440,9 @@ dependencies = [
[[package]] [[package]]
name = "deranged" name = "deranged"
version = "0.3.11" version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587"
dependencies = [ dependencies = [
"powerfmt", "powerfmt",
] ]
@@ -472,12 +470,6 @@ version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d"
[[package]]
name = "doc-comment"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]] [[package]]
name = "either" name = "either"
version = "1.13.0" version = "1.13.0"
@@ -517,20 +509,19 @@ dependencies = [
[[package]] [[package]]
name = "etcetera" name = "etcetera"
version = "0.10.0" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26c7b13d0780cb82722fd59f6f57f925e143427e4a75313a6c77243bf5326ae6" checksum = "de48cc4d1c1d97a20fd819def54b890cadde72ed3ad0c614822a0a433361be96"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"home", "windows-sys 0.61.2",
"windows-sys 0.59.0",
] ]
[[package]] [[package]]
name = "execute" name = "execute"
version = "0.2.13" version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a82608ee96ce76aeab659e9b8d3c2b787bffd223199af88c674923d861ada10" checksum = "0be3cc61fe54b4cae4463cdbda0401978ffe19d4dcc7a5201a312cddf64726dd"
dependencies = [ dependencies = [
"execute-command-macro", "execute-command-macro",
"execute-command-tokens", "execute-command-tokens",
@@ -667,9 +658,9 @@ dependencies = [
[[package]] [[package]]
name = "git2" name = "git2"
version = "0.20.0" version = "0.20.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fda788993cc341f69012feba8bf45c0ba4f3291fcc08e214b4d5a7332d88aff" checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"libc", "libc",
@@ -686,9 +677,9 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
[[package]] [[package]]
name = "globset" name = "globset"
version = "0.4.16" version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"bstr", "bstr",
@@ -699,9 +690,9 @@ dependencies = [
[[package]] [[package]]
name = "grep-cli" name = "grep-cli"
version = "0.1.11" version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47f1288f0e06f279f84926fa4c17e3fcd2a22b357927a82f2777f7be26e4cec0" checksum = "cf32d263c5d5cc2a23ce587097f5ddafdb188492ba2e6fb638eaccdc22453631"
dependencies = [ dependencies = [
"bstr", "bstr",
"globset", "globset",
@@ -719,18 +710,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.15.2" version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[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",
]
[[package]] [[package]]
name = "icu_collections" name = "icu_collections"
@@ -879,13 +861,14 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.8.0" version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.15.2", "hashbrown 0.16.1",
"serde", "serde",
"serde_core",
] ]
[[package]] [[package]]
@@ -926,15 +909,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.175" version = "0.2.182"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
[[package]] [[package]]
name = "libgit2-sys" 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" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1a117465e7e1597e8febea8bb0c410f1c7fb93b1e1cddf34363f8390367ffec" checksum = "c9b3acc4b91781bb0b3386669d325163746af5f6e4f73e6d2d630e09a35f3487"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@@ -966,6 +949,12 @@ version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "linux-raw-sys"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]] [[package]]
name = "litemap" name = "litemap"
version = "0.7.4" version = "0.7.4"
@@ -1032,20 +1021,20 @@ dependencies = [
[[package]] [[package]]
name = "mio" name = "mio"
version = "1.0.3" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873"
dependencies = [ dependencies = [
"libc", "libc",
"wasi 0.11.0+wasi-snapshot-preview1", "wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.52.0", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
name = "nix" name = "nix"
version = "0.30.1" version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"cfg-if", "cfg-if",
@@ -1070,18 +1059,18 @@ dependencies = [
[[package]] [[package]]
name = "nu-ansi-term" name = "nu-ansi-term"
version = "0.50.1" version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
name = "num-conv" name = "num-conv"
version = "0.1.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
[[package]] [[package]]
name = "num-traits" name = "num-traits"
@@ -1218,9 +1207,9 @@ dependencies = [
[[package]] [[package]]
name = "prettyplease" name = "prettyplease"
version = "0.2.35" version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"syn", "syn",
@@ -1228,9 +1217,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.95" version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@@ -1246,9 +1235,9 @@ dependencies = [
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.40" version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@@ -1284,9 +1273,9 @@ dependencies = [
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.11.1" version = "1.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@@ -1296,9 +1285,9 @@ dependencies = [
[[package]] [[package]]
name = "regex-automata" name = "regex-automata"
version = "0.4.9" version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@@ -1329,10 +1318,23 @@ dependencies = [
"bitflags", "bitflags",
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys 0.4.15",
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
[[package]]
name = "rustix"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys 0.12.1",
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.18" version = "1.0.18"
@@ -1362,18 +1364,28 @@ checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.217" version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index" 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 = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.217" version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1394,29 +1406,28 @@ dependencies = [
[[package]] [[package]]
name = "serde_spanned" name = "serde_spanned"
version = "1.0.0" version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
dependencies = [ dependencies = [
"serde", "serde_core",
] ]
[[package]] [[package]]
name = "serde_with" name = "serde_with"
version = "3.12.0" version = "3.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" checksum = "381b283ce7bc6b476d903296fb59d0d36633652b633b27f64db4fb46dcbfc3b9"
dependencies = [ dependencies = [
"serde", "serde_core",
"serde_derive",
"serde_with_macros", "serde_with_macros",
] ]
[[package]] [[package]]
name = "serde_with_macros" name = "serde_with_macros"
version = "3.12.0" version = "3.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" checksum = "a6d4e30573c8cb306ed6ab1dca8423eec9a463ea0e155f45399455e0368b27e0"
dependencies = [ dependencies = [
"darling", "darling",
"proc-macro2", "proc-macro2",
@@ -1468,9 +1479,9 @@ checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
[[package]] [[package]]
name = "shell-words" name = "shell-words"
version = "1.1.0" version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
[[package]] [[package]]
name = "shlex" name = "shlex"
@@ -1534,9 +1545,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.104" version = "2.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1592,16 +1603,15 @@ dependencies = [
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.16.0" version = "3.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [ dependencies = [
"cfg-if",
"fastrand", "fastrand",
"getrandom", "getrandom",
"once_cell", "once_cell",
"rustix", "rustix 1.1.4",
"windows-sys 0.59.0", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
@@ -1615,28 +1625,28 @@ dependencies = [
[[package]] [[package]]
name = "terminal-colorsaurus" name = "terminal-colorsaurus"
version = "1.0.0" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f7226dad4b1817567c1e2f5d453897ef36abe79def7783af3fa241a694e30b3" checksum = "8909f33134da34b43f69145e748790de650a6abd84faf1f82e773444dd293ec8"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"memchr", "memchr",
"mio 1.0.3", "mio 1.1.0",
"terminal-trx", "terminal-trx",
"windows-sys 0.59.0", "windows-sys 0.61.2",
"xterm-color", "xterm-color",
] ]
[[package]] [[package]]
name = "terminal-trx" name = "terminal-trx"
version = "0.2.4" version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "975b4233aefa1b02456d5e53b22c61653c743e308c51cf4181191d8ce41753ab" checksum = "662a3cd5ca570df622e848ef18b50c151e65c9835257465417242243b0bce783"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"windows-sys 0.59.0", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
@@ -1645,7 +1655,7 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9"
dependencies = [ dependencies = [
"rustix", "rustix 0.38.43",
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
@@ -1706,30 +1716,30 @@ dependencies = [
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.37" version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
dependencies = [ dependencies = [
"deranged", "deranged",
"itoa", "itoa",
"num-conv", "num-conv",
"powerfmt", "powerfmt",
"serde", "serde_core",
"time-core", "time-core",
"time-macros", "time-macros",
] ]
[[package]] [[package]]
name = "time-core" name = "time-core"
version = "0.1.2" version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]] [[package]]
name = "time-macros" name = "time-macros"
version = "0.2.19" version = "0.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
dependencies = [ dependencies = [
"num-conv", "num-conv",
"time-core", "time-core",
@@ -1747,12 +1757,12 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.9.1" version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0207d6ed1852c2a124c1fbec61621acb8330d2bf969a5d0643131e9affd985a5" checksum = "994b95d9e7bae62b34bab0e2a4510b801fa466066a6a8b2b57361fa1eba068ee"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde", "serde_core",
"serde_spanned", "serde_spanned",
"toml_datetime", "toml_datetime",
"toml_parser", "toml_parser",
@@ -1762,27 +1772,27 @@ dependencies = [
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.7.0" version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
dependencies = [ dependencies = [
"serde", "serde_core",
] ]
[[package]] [[package]]
name = "toml_parser" name = "toml_parser"
version = "1.0.0" version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5c1c469eda89749d2230d8156a5969a69ffe0d6d01200581cdc6110674d293e" checksum = "39ca317ebc49f06bd748bfba29533eac9485569dc9bf80b849024b025e814fb9"
dependencies = [ dependencies = [
"winnow", "winnow",
] ]
[[package]] [[package]]
name = "toml_writer" name = "toml_writer"
version = "1.0.0" version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b679217f2848de74cabd3e8fc5e6d66f40b7da40f8e1954d92054d9010690fd5" checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
[[package]] [[package]]
name = "typenum" name = "typenum"
@@ -1798,15 +1808,15 @@ checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
version = "1.12.0" version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.2.1" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
[[package]] [[package]]
name = "unsafe-libyaml" name = "unsafe-libyaml"
@@ -2011,6 +2021,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]] [[package]]
name = "windows-result" name = "windows-result"
version = "0.1.2" version = "0.1.2"
@@ -2029,15 +2045,6 @@ dependencies = [
"windows-targets 0.48.5", "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]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.59.0" version = "0.59.0"
@@ -2049,11 +2056,11 @@ dependencies = [
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.60.2" version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [ dependencies = [
"windows-targets 0.53.2", "windows-link",
] ]
[[package]] [[package]]
@@ -2080,29 +2087,13 @@ dependencies = [
"windows_aarch64_gnullvm 0.52.6", "windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6", "windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 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_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6", "windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6", "windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 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]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.48.5" version = "0.48.5"
@@ -2115,12 +2106,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.48.5" version = "0.48.5"
@@ -2133,12 +2118,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.48.5" version = "0.48.5"
@@ -2151,24 +2130,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
[[package]] [[package]]
name = "windows_i686_gnullvm" name = "windows_i686_gnullvm"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.48.5" version = "0.48.5"
@@ -2181,12 +2148,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.48.5" version = "0.48.5"
@@ -2199,12 +2160,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 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]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.48.5" version = "0.48.5"
@@ -2217,12 +2172,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 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]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.48.5" version = "0.48.5"
@@ -2235,17 +2184,11 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 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]] [[package]]
name = "winnow" name = "winnow"
version = "0.7.11" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5"
[[package]] [[package]]
name = "wit-bindgen-rt" name = "wit-bindgen-rt"
+24 -25
View File
@@ -6,12 +6,12 @@ homepage = "https://github.com/sharkdp/bat"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
name = "bat" name = "bat"
repository = "https://github.com/sharkdp/bat" repository = "https://github.com/sharkdp/bat"
version = "0.26.0" version = "0.26.1"
exclude = ["assets/syntaxes/*", "assets/themes/*"] exclude = ["assets/syntaxes/*", "assets/themes/*"]
build = "build/main.rs" build = "build/main.rs"
edition = '2021' edition = '2021'
# You are free to bump MSRV as soon as a reason for bumping emerges. # You are free to bump MSRV as soon as a reason for bumping emerges.
rust-version = "1.79" rust-version = "1.88"
[features] [features]
default = ["application", "git"] default = ["application", "git"]
@@ -42,21 +42,21 @@ regex-onig = ["syntect/regex-onig"] # Use the "oniguruma" regex engine
regex-fancy = ["syntect/regex-fancy"] # Use the rust-only "fancy-regex" engine regex-fancy = ["syntect/regex-fancy"] # Use the rust-only "fancy-regex" engine
[dependencies] [dependencies]
nu-ansi-term = "0.50.0" nu-ansi-term = "0.50.3"
ansi_colours = "^1.2" ansi_colours = "^1.2"
bincode = "1.0" bincode = "1.0"
console = "0.16.0" console = "0.16.2"
flate2 = "1.1" flate2 = "1.1"
once_cell = "1.20" once_cell = "1.20"
thiserror = "2.0" thiserror = "2.0"
wild = { version = "2.2", optional = true } wild = { version = "2.2", optional = true }
content_inspector = "0.2.4" 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 = [ minus = { version = "5.6", optional = true, features = [
"dynamic_output", "dynamic_output",
"search", "search",
] } ] }
unicode-width = "0.2.1" unicode-width = "0.2.2"
globset = "0.4" globset = "0.4"
serde = "1.0" serde = "1.0"
serde_derive = "1.0" serde_derive = "1.0"
@@ -65,15 +65,15 @@ semver = "1.0"
path_abs = { version = "0.5", default-features = false } path_abs = { version = "0.5", default-features = false }
clircle = { version = "0.6.1", default-features = false } clircle = { version = "0.6.1", default-features = false }
bugreport = { version = "0.5.0", optional = true } bugreport = { version = "0.5.0", optional = true }
etcetera = { version = "0.10.0", optional = true } etcetera = { version = "0.11.0", optional = true }
grep-cli = { version = "0.1.11", optional = true } grep-cli = { version = "0.1.12", optional = true }
regex = { version = "1.10.6", optional = true } regex = { version = "1.12.2", optional = true }
walkdir = { version = "2.5", optional = true } walkdir = { version = "2.5", optional = true }
bytesize = { version = "1.3.0" } bytesize = { version = "2.3.1" }
encoding_rs = "0.8.35" encoding_rs = "0.8.35"
execute = { version = "0.2.13", optional = true } execute = { version = "0.2.15", optional = true }
terminal-colorsaurus = "1.0" terminal-colorsaurus = "1.0"
unicode-segmentation = "1.12.0" unicode-segmentation = "1.13.2"
itertools = "0.14.0" itertools = "0.14.0"
[dependencies.git2] [dependencies.git2]
@@ -87,44 +87,43 @@ default-features = false
features = ["parsing"] features = ["parsing"]
[dependencies.clap] [dependencies.clap]
version = "4.5.46" version = "4.5.60"
optional = true optional = true
features = ["wrap_help", "cargo"] features = ["wrap_help", "cargo"]
[target.'cfg(target_os = "macos")'.dependencies] [target.'cfg(target_os = "macos")'.dependencies]
home = "0.5.9"
plist = "1.7.0" plist = "1.7.0"
[dev-dependencies] [dev-dependencies]
assert_cmd = "2.0.12" assert_cmd = "2.0.16"
expect-test = "1.5.0" expect-test = "1.5.0"
serial_test = { version = "2.0.0", default-features = false } serial_test = { version = "2.0.0", default-features = false }
predicates = "3.1.3" predicates = "3.1.3"
wait-timeout = "0.2.1" wait-timeout = "0.2.1"
tempfile = "3.16.0" tempfile = "3.27.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
[target.'cfg(unix)'.dev-dependencies] [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] [build-dependencies]
anyhow = "1.0.97" anyhow = "1.0.97"
indexmap = { version = "2.8.0", features = ["serde"] } indexmap = { version = "2.13.0", features = ["serde"] }
itertools = "0.14.0" itertools = "0.14.0"
once_cell = "1.20" once_cell = "1.20"
prettyplease = "0.2.35" prettyplease = "0.2.37"
proc-macro2 = "1.0.95" proc-macro2 = "1.0.106"
quote = "1.0.40" quote = "1.0.45"
regex = "1.10.6" regex = "1.12.2"
serde = "1.0" serde = "1.0"
serde_derive = "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"] } syn = { version = "2.0.104", features = ["full"] }
toml = { version = "0.9.1", features = ["preserve_order"] } toml = { version = "1.1.1", features = ["preserve_order"] }
walkdir = "2.5" walkdir = "2.5"
[build-dependencies.clap] [build-dependencies.clap]
version = "4.5.46" version = "4.5.60"
optional = true optional = true
features = ["wrap_help", "cargo"] features = ["wrap_help", "cargo"]
+51 -43
View File
@@ -19,26 +19,6 @@
[<a href="doc/README-ru.md">Русский</a>] [<a href="doc/README-ru.md">Русский</a>]
</p> </p>
### Sponsors
A special *thank you* goes to our biggest <a href="doc/sponsors.md">sponsors</a>:<br>
<p>
<a href="https://www.warp.dev/bat">
<img src="doc/sponsors/warp-logo.png" width="200" alt="Warp">
<br>
<strong>Warp, the intelligent terminal</strong>
<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 ### Syntax highlighting
`bat` supports syntax highlighting for a large number of programming and markup `bat` supports syntax highlighting for a large number of programming and markup
@@ -49,7 +29,7 @@ languages:
### Git integration ### Git integration
`bat` communicates with `git` to show modifications with respect to the index `bat` communicates with `git` to show modifications with respect to the index
(see left side bar): (see left sidebar):
![Git integration example](https://i.imgur.com/2lSW4RE.png) ![Git integration example](https://i.imgur.com/2lSW4RE.png)
@@ -201,30 +181,19 @@ bat main.cpp | xclip
`MANPAGER` environment variable: `MANPAGER` environment variable:
```bash ```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 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). 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. 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` #### `prettier` / `shfmt` / `rustfmt`
The [`prettybat`](https://github.com/eth-p/bat-extras/blob/master/doc/prettybat.md) script is a wrapper that will format code and print it with `bat`. The [`prettybat`](https://github.com/eth-p/bat-extras/blob/master/doc/prettybat.md) script is a wrapper that will format code and print it with `bat`.
#### `Warp`
<a href="https://app.warp.dev/drive/folder/-Bat-Warp-Pack-lxhe7HrEwgwpG17mvrFSz1">
<img src="doc/sponsors/warp-pack-header.png" alt="Warp">
</a>
#### Highlighting `--help` messages #### Highlighting `--help` messages
You can use `bat` to colorize help text: `$ cp --help | bat -plhelp` You can use `bat` to colorize help text: `$ cp --help | bat -plhelp`
@@ -257,6 +226,14 @@ abbr -a --position anywhere -- -h '-h | bat -plhelp'
This way, you can keep on using `cp --help`, but get colorized help pages. 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` 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. as a command argument you can prepend `\` to the argument (eg. `ls \-h`) to escape the aliasing defined above.
@@ -287,8 +264,8 @@ If your Ubuntu/Debian installation is new enough you can simply run:
sudo apt install bat 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 **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)). 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: 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 ``` bash
mkdir -p ~/.local/bin mkdir -p ~/.local/bin
ln -s /usr/bin/batcat ~/.local/bin/bat ln -s /usr/bin/batcat ~/.local/bin/bat
@@ -530,6 +507,30 @@ and line numbers but no grid and no file header. Set the `BAT_STYLE` environment
variable to make these changes permanent or use `bat`'s variable to make these changes permanent or use `bat`'s
[configuration file](#configuration-file). [configuration file](#configuration-file).
By default, `bat` enables `changes`, `grid`, `header-filename`, `numbers`, and `snip`.
The available pre-defined styles are:
| Style | Description |
|-------|-------------|
| `default` | Enables the recommended style components listed above. |
| `full` | Enables all available components. |
| `auto` | Same as `default`, unless the output is piped. |
| `plain` | Disables all available components. |
The available individual components are:
| Component | Description |
|-----------|-------------|
| `changes` | Show Git modification markers. |
| `header` | Alias for `header-filename`. |
| `header-filename` | Show filenames before the content. |
| `header-filesize` | Show file sizes before the content. |
| `grid` | Vertical/horizontal lines to separate the side bar and header from the content. |
| `rule` | Horizontal lines to delimit files. |
| `numbers` | Show line numbers in the side bar. |
| `snip` | Draw separation lines between distinct line ranges. |
>[!tip] >[!tip]
> If you specify a default style in `bat`'s config file, you can change which components > If you specify a default style in `bat`'s config file, you can change which components
> are displayed during a single run of `bat` using the `--style` command-line argument. > are displayed during a single run of `bat` using the `--style` command-line argument.
@@ -540,6 +541,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 > Or, if you want to override the styles completely, you use `--style=numbers` to
> only show the line numbers. > 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 ### Adding new syntaxes / language definitions
Should you find that a particular syntax is not available within `bat`, you can follow these Should you find that a particular syntax is not available within `bat`, you can follow these
@@ -586,7 +593,8 @@ syntax:
This works very similar to how we add new syntax definitions. This works very similar to how we add new syntax definitions.
> [!NOTE] > [!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: First, create a folder with the new syntax highlighting themes:
```bash ```bash
@@ -670,22 +678,22 @@ to improve the experience. Specifically, `-R`/`--RAW-CONTROL-CHARS`, `-F`/`--qui
> - The `--paging=always` argument is used. > - The `--paging=always` argument is used.
> - The `BAT_PAGING` environment is set to `always`. > - 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 the vertical size of the terminal. This is convenient for small files because you do not
have to press `q` to quit the pager. 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. 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 The `-X`/`--no-init` option is added to versions of `less` older than version 530 (older than 558 on Windows) to
of `less` older than version 530. Unfortunately, it also breaks mouse-wheel support in `less`. 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 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`. 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. 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. to truncate any lines larger than the terminal width.
### Indentation ### Indentation
BIN
View File
Binary file not shown.
+8 -4
View File
@@ -9,7 +9,7 @@ Register-ArgumentCompleter -Native -CommandName '{{PROJECT_EXECUTABLE}}' -Script
$ArrayCompletion = @('bash', 'fish', 'zsh', 'ps1') $ArrayCompletion = @('bash', 'fish', 'zsh', 'ps1')
$ArrayWhen = @('auto', 'never', 'always') $ArrayWhen = @('auto', 'never', 'always')
$ArrayYesNo = @('never', 'always') $ArrayYesNo = @('never', 'always')
$ArrayWrap = @('always', 'never', 'character') $ArrayWrap = @('always', 'never', 'character', 'word')
$ArrayBinary = @('no-printing', 'as-text') $ArrayBinary = @('no-printing', 'as-text')
$ArrayPrint = @('unicode', 'caret') $ArrayPrint = @('unicode', 'caret')
@@ -49,7 +49,10 @@ Register-ArgumentCompleter -Native -CommandName '{{PROJECT_EXECUTABLE}}' -Script
} }
'*;--language' { '*;--language' {
Get-MyLanguages | 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 break
} }
'*;--theme' { '*;--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('--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('--diff-context' , 'diff-context' , [CompletionResultType]::ParameterName, 'diff-context')
[CompletionResult]::new('--tabs' , 'tabs' , [CompletionResultType]::ParameterName, 'Set the tab width to T spaces.') [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('--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('--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*)') [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('-L' , 'L' , [CompletionResultType]::ParameterName, 'Display all supported languages.')
[CompletionResult]::new('--list-languages' , 'list-languages' , [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('-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('--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-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') [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('--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('--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('--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('-h' , 'h' , [CompletionResultType]::ParameterName, 'Print this help message.')
[CompletionResult]::new('--help' , 'help' , [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.') # [CompletionResult]::new('-V' , 'V' , [CompletionResultType]::ParameterName, 'Show version information.')
+34 -14
View File
@@ -14,18 +14,36 @@ __bat_escape_completions()
{ {
# Do not escape if completing a quoted value. # Do not escape if completing a quoted value.
[[ $cur == [\"\']* ]] && return 0 [[ $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 (( 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[0] == 4 && BASH_VERSINFO[1] > 0 BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] > 0
)); then )); 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 local i
for i in ${!COMPREPLY[*]}; do for i in ${!COMPREPLY[*]}; do
printf -v "COMPREPLY[i]" %q "${COMPREPLY[i]}" printf -v "COMPREPLY[i]" %q "${COMPREPLY[i]}"
done 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 else
compopt -o filenames compopt -o filenames
fi fi
@@ -66,7 +84,7 @@ _bat() {
printf "%s\n" "$lang" printf "%s\n" "$lang"
done done
)" -- "$cur")) )" -- "$cur"))
__bat_escape_completions __bat_escape_completions
return 0 return 0
;; ;;
-H | --highlight-line | \ -H | --highlight-line | \
@@ -82,6 +100,7 @@ _bat() {
--lessopen | \ --lessopen | \
--no-paging | \ --no-paging | \
--diagnostic | \ --diagnostic | \
--quiet-empty | \
--acknowledgements | \ --acknowledgements | \
-h | --help | \ -h | --help | \
-V | --version | \ -V | --version | \
@@ -98,7 +117,7 @@ _bat() {
return 0 return 0
;; ;;
--wrap) --wrap)
COMPREPLY=($(compgen -W "auto never character" -- "$cur")) COMPREPLY=($(compgen -W "auto never character word" -- "$cur"))
return 0 return 0
;; ;;
--binary) --binary)
@@ -130,16 +149,16 @@ _bat() {
return 0 return 0
;; ;;
--theme) --theme)
local IFS=$'\n' local IFS=$'\n'
COMPREPLY=($(compgen -W "auto${IFS}auto:always${IFS}auto:system${IFS}dark${IFS}light${IFS}$("$1" --list-themes)" -- "$cur")) COMPREPLY=($(compgen -W "auto${IFS}auto:always${IFS}auto:system${IFS}dark${IFS}light${IFS}$("$1" --list-themes)" -- "$cur"))
__bat_escape_completions __bat_escape_completions
return 0 return 0
;; ;;
--theme-dark | \ --theme-dark | \
--theme-light) --theme-light)
local IFS=$'\n' local IFS=$'\n'
COMPREPLY=($(compgen -W "$("$1" --list-themes)" -- "$cur")) COMPREPLY=($(compgen -W "$("$1" --list-themes)" -- "$cur"))
__bat_escape_completions __bat_escape_completions
return 0 return 0
;; ;;
--style) --style)
@@ -158,7 +177,7 @@ _bat() {
numbers numbers
snip snip
) )
# shellcheck disable=SC2016 # shellcheck disable=SC2016
if declare -F _comp_delimited >/dev/null 2>&1; then if declare -F _comp_delimited >/dev/null 2>&1; then
# bash-completion > 2.11 # bash-completion > 2.11
_comp_delimited , -W '"${styles[@]}"' _comp_delimited , -W '"${styles[@]}"'
@@ -171,8 +190,8 @@ _bat() {
$split && return 0 $split && return 0
if [[ $cur == -* ]]; then if [[ $cur == -* ]]; then
# --unbuffered excluded intentionally (no-op)
COMPREPLY=($(compgen -W " COMPREPLY=($(compgen -W "
--unbuffered
--show-all --show-all
--nonprintable-notation --nonprintable-notation
--binary --binary
@@ -209,6 +228,7 @@ _bat() {
--lessopen --lessopen
--completion --completion
--diagnostic --diagnostic
--quiet-empty
--acknowledgements --acknowledgements
--set-terminal-title --set-terminal-title
--help --help
+4 -2
View File
@@ -61,7 +61,7 @@ function __bat_no_excl_args
-s V -l version \ -s V -l version \
-l acknowledgements \ -l acknowledgements \
-l config-dir -l config-file \ -l config-dir -l config-file \
-l diagnostic \ -l diagnostic -l quiet-empty \
-l list-languages -l list-themes -l list-languages -l list-themes
end end
@@ -118,6 +118,7 @@ set -l wrap_opts '
auto\tdefault auto\tdefault
never\t never\t
character\t character\t
word\t
' '
# While --tabs theoretically takes any number, most people should be OK with these. # 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 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 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 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 -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 complete -c $bat -s V -l version -f -d "Show version information" -n __fish_is_first_arg
+4 -3
View File
@@ -34,7 +34,7 @@ _{{PROJECT_EXECUTABLE}}_main() {
'(-d --diff)'--diff'[only show lines that have been added/removed/modified]' '(-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' --diff-context='[specify lines of context around added/removed/modified lines when using `--diff`]:lines'
--tabs='[set the tab width]:tab width [4]' --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} '!(--wrap)'{-S,--chop-long-lines}
--terminal-width='[explicitly set the width of the terminal instead of determining it automatically]:width' --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]' '(-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' 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' \*{-r+,--line-range=}'[only print the specified line range]:start\:end'
'(* -)'{-L,--list-languages}'[display all supported languages]' '(* -)'{-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)' --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]' --set-terminal-title'[sets terminal title to filenames when using a pager]'
--diagnostic'[show diagnostic information for bug reports]' --diagnostic'[show diagnostic information for bug reports]'
--quiet-empty'[do not produce any output when the input is empty]'
-P'[disable paging]' -P'[disable paging]'
"--no-config[don't use the configuration file]" "--no-config[don't use the configuration file]"
"--no-custom-assets[don't load custom assets]" "--no-custom-assets[don't load custom assets]"
@@ -89,7 +90,7 @@ _{{PROJECT_EXECUTABLE}}_main() {
languages) languages)
local IFS=$'\n' local IFS=$'\n'
local -a languages local -a languages
languages=( $({{PROJECT_EXECUTABLE}} --list-languages | awk -F':|,' '{ for (i = 1; i <= NF; ++i) printf("%s:%s\n", $i, $1) }') ) languages=( ${(f)"$({{PROJECT_EXECUTABLE}} --list-languages | awk -F':|,' '{ for (i = 1; i <= NF; ++i) printf("%s:%s\n", $i, $1) }')"} )
_describe 'language' languages && ret=0 _describe 'language' languages && ret=0
;; ;;
+12 -4
View File
@@ -102,8 +102,10 @@ Set the tab width to T spaces. Use a width of 0 to pass tabs through directly
.HP .HP
\fB\-\-wrap\fR <mode> \fB\-\-wrap\fR <mode>
.IP .IP
Specify the text\-wrapping mode (*auto*, never, character). The '\-\-terminal\-width' option Specify the text\-wrapping mode (*auto*, never, character, word). The '\-\-terminal\-width' option
can be used in addition to control the output width. 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 .HP
\fB\-S\fR, \fB\-\-chop\-long\-lines\fR \fB\-S\fR, \fB\-\-chop\-long\-lines\fR
.IP .IP
@@ -263,8 +265,10 @@ Display a list of supported languages for syntax highlighting.
.HP .HP
\fB\-u\fR, \fB\-\-unbuffered\fR \fB\-u\fR, \fB\-\-unbuffered\fR
.IP .IP
This option exists for POSIX\-compliance reasons ('u' is for 'unbuffered'). The output is Enable unbuffered input reading. When this flag is set, bat will display data as soon as it
always unbuffered \- this option is simply ignored. 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 .HP
\fB\-\-no\-custom\-assets\fR \fB\-\-no\-custom\-assets\fR
.IP .IP
@@ -282,6 +286,10 @@ Show bat's cache directory.
.IP .IP
Show diagnostic information for bug reports. Show diagnostic information for bug reports.
.HP .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 \fB\-\-acknowledgements\fR
.IP .IP
Show acknowledgements. Show acknowledgements.
+9
View File
@@ -2,6 +2,15 @@ diff --git syntaxes/01_Packages/Python/Python.sublime-syntax syntaxes/01_Package
index 2acd86d8..86257f7b 100644 index 2acd86d8..86257f7b 100644
--- syntaxes/01_Packages/Python/Python.sublime-syntax --- syntaxes/01_Packages/Python/Python.sublime-syntax
+++ syntaxes/01_Packages/Python/Python.sublime-syntax +++ syntaxes/01_Packages/Python/Python.sublime-syntax
@@ -25,7 +31,7 @@ file_extensions:
- wscript
- bazel
- bzl
-first_line_match: ^#!\s*/.*\bpython(\d(\.\d)?)?\b
+first_line_match: ^#!\s*/.*\b(python(\d(\.\d)?)?|uv)\b
scope: source.python
variables:
@@ -988,10 +988,6 @@ contexts: @@ -988,10 +988,6 @@ contexts:
- match: \} - match: \}
scope: punctuation.section.mapping-or-set.end.python scope: punctuation.section.mapping-or-set.end.python
+1 -1
View File
@@ -14,7 +14,7 @@ index e973e319..07c170a7 100644
first_line_match: | first_line_match: |
(?x) (?x)
- ^\#! .* \b(bash|zsh|sh|tcsh|ash)\b - ^\#! .* \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 [^*]* -\*- | ^\# \s* -\*- [^*]* mode: \s* shell-script [^*]* -\*-
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
+12
View File
@@ -0,0 +1,12 @@
diff --git syntaxes/01_Packages/TCL/Tcl.sublime-syntax syntaxes/01_Packages/TCL/Tcl.sublime-syntax
index 1234567..abcdefg 100644
--- syntaxes/01_Packages/TCL/Tcl.sublime-syntax
+++ syntaxes/01_Packages/TCL/Tcl.sublime-syntax
@@ -3,6 +3,7 @@
# http://www.sublimetext.com/docs/3/syntax.html
name: Tcl
file_extensions:
- tcl
+first_line_match: ^\#!.*\b(tclsh|wish|expect)\b
scope: source.tcl
variables:
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -79,7 +79,7 @@ contexts:
double_quoted_string: double_quoted_string:
- meta_include_prototype: false - meta_include_prototype: false
- meta_scope: string.quoted.double.csv - meta_scope: meta.string.quoted.double.csv
- match: '""' - match: '""'
scope: constant.character.escape.csv scope: constant.character.escape.csv
- match: '"' - match: '"'
-398
View File
@@ -1,398 +0,0 @@
%YAML 1.2
---
# http://www.sublimetext.com/docs/3/syntax.html
name: Kotlin
file_extensions:
- kt
- kts
scope: source.Kotlin
contexts:
main:
- include: comments
- match: '^\s*(package)\b(?:\s*([^ ;$]+)\s*)?'
captures:
1: keyword.other.kotlin
2: entity.name.package.kotlin
- include: imports
- include: statements
classes:
- match: (?<!::)(?=\b(?:companion|class|object|interface)\b)
push:
- match: '(?=$|\})'
pop: true
- include: comments
- match: \b(companion\s*)?(class|object|interface)\b
captures:
1: storage.modifier.kotlin
2: storage.modifier.kotlin
push:
- match: '(?=<|\{|\(|:|$)'
pop: true
- include: comments
- match: \w+
scope: entity.name.type.class.kotlin
- match: <
push:
- match: ">"
pop: true
- include: generics
- match: \(
push:
- match: \)
pop: true
- include: parameters
- match: (:)
captures:
1: keyword.operator.declaration.kotlin
push:
- match: "(?={|$)"
pop: true
- match: \w+
scope: entity.other.inherited-class.kotlin
- match: \(
push:
- match: \)
pop: true
- include: expressions
- match: '\{'
push:
- match: '\}'
pop: true
- include: statements
comments:
- match: /\*
captures:
0: punctuation.definition.comment.kotlin
push:
- meta_scope: comment.block.kotlin
- match: \*/
captures:
0: punctuation.definition.comment.kotlin
pop: true
- match: \s*((//).*$\n?)
captures:
1: comment.line.double-slash.kotlin
2: punctuation.definition.comment.kotlin
constants:
- match: \b(true|false|null|this|super)\b
scope: constant.language.kotlin
- match: '\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)([LlFf])?\b'
scope: constant.numeric.kotlin
- match: '\b([A-Z][A-Z0-9_]+)\b'
scope: constant.other.kotlin
expressions:
- match: \(
push:
- match: \)
pop: true
- include: expressions
- include: types
- include: strings
- include: constants
- include: comments
- include: keywords
functions:
- match: (?=\s*\b(?:fun)\b)
push:
- match: '(?=$|\})'
pop: true
- match: \b(fun)\b
captures:
1: keyword.other.kotlin
push:
- match: (?=\()
pop: true
- match: <
push:
- match: ">"
pop: true
- include: generics
- match: '([\.<\?>\w]+\.)?(\w+)'
captures:
2: entity.name.function.kotlin
- match: \(
push:
- match: \)
pop: true
- include: parameters
- match: (:)
captures:
1: keyword.operator.declaration.kotlin
push:
- match: "(?={|=|$)"
pop: true
- include: types
- match: '\{'
push:
- match: '(?=\})'
pop: true
- include: statements
- match: (=)
captures:
1: keyword.operator.assignment.kotlin
push:
- match: (?=$)
pop: true
- include: expressions
generics:
- match: (:)
captures:
1: keyword.operator.declaration.kotlin
push:
- match: (?=,|>)
pop: true
- include: types
- include: keywords
- match: \w+
scope: storage.type.generic.kotlin
getters-and-setters:
- match: \b(get)\b\s*\(\s*\)
captures:
1: entity.name.function.kotlin
push:
- match: '\}|(?=\bset\b)|$'
pop: true
- match: (=)
captures:
1: keyword.operator.assignment.kotlin
push:
- match: (?=$|\bset\b)
pop: true
- include: expressions
- match: '\{'
push:
- match: '\}'
pop: true
- include: expressions
- match: \b(set)\b\s*(?=\()
captures:
1: entity.name.function.kotlin
push:
- match: '\}|(?=\bget\b)|$'
pop: true
- match: \(
push:
- match: \)
pop: true
- include: parameters
- match: (=)
captures:
1: keyword.operator.assignment.kotlin
push:
- match: (?=$|\bset\b)
pop: true
- include: expressions
- match: '\{'
push:
- match: '\}'
pop: true
- include: expressions
imports:
- match: '^\s*(import)\s+[^ $]+\s+(as)?'
captures:
1: keyword.other.kotlin
2: keyword.other.kotlin
keywords:
- match: \b(var|val|public|private|protected|abstract|final|sealed|enum|open|attribute|annotation|override|inline|vararg|in|out|internal|data|tailrec|operator|infix|const|yield|typealias|typeof|reified|suspend)\b
scope: storage.modifier.kotlin
- match: \b(try|catch|finally|throw)\b
scope: keyword.control.catch-exception.kotlin
- match: \b(if|else|while|for|do|return|when|where|break|continue)\b
scope: keyword.control.kotlin
- match: \b(in|is|!in|!is|as|as\?|assert)\b
scope: keyword.operator.kotlin
- match: (==|!=|===|!==|<=|>=|<|>)
scope: keyword.operator.comparison.kotlin
- match: (=)
scope: keyword.operator.assignment.kotlin
- match: (::)
scope: keyword.operator.kotlin
- match: (:)
scope: keyword.operator.declaration.kotlin
- match: \b(by)\b
scope: keyword.other.by.kotlin
- match: (\?\.)
scope: keyword.operator.safenav.kotlin
- match: (\.)
scope: keyword.operator.dot.kotlin
- match: (\?:)
scope: keyword.operator.elvis.kotlin
- match: (\-\-|\+\+)
scope: keyword.operator.increment-decrement.kotlin
- match: (\+=|\-=|\*=|\/=)
scope: keyword.operator.arithmetic.assign.kotlin
- match: (\.\.)
scope: keyword.operator.range.kotlin
- match: (\-|\+|\*|\/|%)
scope: keyword.operator.arithmetic.kotlin
- match: (!|&&|\|\|)
scope: keyword.operator.logical.kotlin
- match: (;)
scope: punctuation.terminator.kotlin
namespaces:
- match: \b(namespace)\b
scope: keyword.other.kotlin
- match: '\{'
push:
- match: '\}'
pop: true
- include: statements
parameters:
- match: (:)
captures:
1: keyword.operator.declaration.kotlin
push:
- match: (?=,|\)|=)
pop: true
- include: types
- match: (=)
captures:
1: keyword.operator.declaration.kotlin
push:
- match: (?=,|\))
pop: true
- include: expressions
- include: keywords
- match: \w+
scope: variable.parameter.function.kotlin
statements:
- include: namespaces
- include: typedefs
- include: classes
- include: functions
- include: variables
- include: getters-and-setters
- include: expressions
strings:
- match: '"""'
captures:
0: punctuation.definition.string.begin.kotlin
push:
- meta_scope: string.quoted.third.kotlin
- match: '"""'
captures:
0: punctuation.definition.string.end.kotlin
pop: true
- match: '(\$\w+|\$\{[^\}]+\})'
scope: variable.parameter.template.kotlin
- match: \\.
scope: constant.character.escape.kotlin
- match: '"'
captures:
0: punctuation.definition.string.begin.kotlin
push:
- meta_scope: string.quoted.double.kotlin
- match: '"'
captures:
0: punctuation.definition.string.end.kotlin
pop: true
- match: '(\$\w+|\$\{[^\}]+\})'
scope: variable.parameter.template.kotlin
- match: \\.
scope: constant.character.escape.kotlin
- match: "'"
captures:
0: punctuation.definition.string.begin.kotlin
push:
- meta_scope: string.quoted.single.kotlin
- match: "'"
captures:
0: punctuation.definition.string.end.kotlin
pop: true
- match: \\.
scope: constant.character.escape.kotlin
- match: "`"
captures:
0: punctuation.definition.string.begin.kotlin
push:
- meta_scope: string.quoted.single.kotlin
- match: "`"
captures:
0: punctuation.definition.string.end.kotlin
pop: true
typedefs:
- match: (?=\s*(?:type))
push:
- match: (?=$)
pop: true
- match: \b(type)\b
scope: keyword.other.kotlin
- match: <
push:
- match: ">"
pop: true
- include: generics
- include: expressions
types:
- match: \b(Nothing|Any|Unit|String|CharSequence|Int|Boolean|Char|Long|Double|Float|Short|Byte|dynamic)\b
scope: storage.type.buildin.kotlin
- match: \b(IntArray|BooleanArray|CharArray|LongArray|DoubleArray|FloatArray|ShortArray|ByteArray)\b
scope: storage.type.buildin.array.kotlin
- match: \b(Array|Collection|List|Map|Set|MutableList|MutableMap|MutableSet|Sequence)<\b
captures:
1: storage.type.buildin.collection.kotlin
push:
- match: ">"
pop: true
- include: types
- include: keywords
- match: \w+<
push:
- match: ">"
pop: true
- include: types
- include: keywords
- match: '\{'
push:
- match: '\}'
pop: true
- include: statements
- match: \(
push:
- match: \)
pop: true
- include: types
- match: (->)
scope: keyword.operator.declaration.kotlin
variables:
- match: (?=\s*\b(?:var|val)\b)
push:
- match: (?=:|=|(\b(by)\b)|$)
pop: true
- match: \b(var|val)\b
captures:
1: keyword.other.kotlin
push:
- match: (?=:|=|(\b(by)\b)|$)
pop: true
- match: <
push:
- match: ">"
pop: true
- include: generics
- match: '([\.<\?>\w]+\.)?(\w+)'
captures:
2: entity.name.variable.kotlin
- match: (:)
captures:
1: keyword.operator.declaration.kotlin
push:
- match: (?==|$)
pop: true
- include: types
- include: getters-and-setters
- match: \b(by)\b
captures:
1: keyword.other.kotlin
push:
- match: (?=$)
pop: true
- include: expressions
- match: (=)
captures:
1: keyword.operator.assignment.kotlin
push:
- match: (?=$)
pop: true
- include: expressions
- include: getters-and-setters
+2 -1
View File
@@ -9,6 +9,7 @@ scope: source.man
variables: variables:
section_heading: '^(?!#)\S.*$' section_heading: '^(?!#)\S.*$'
command_line_option: '(--?[A-Za-z0-9][_A-Za-z0-9-]*)' command_line_option: '(--?[A-Za-z0-9][_A-Za-z0-9-]*)'
ansi_escape_sequence: '\e\[[\?=]?(?:\d+;?)*[A-Za-z]'
contexts: contexts:
prototype: prototype:
@@ -69,7 +70,7 @@ contexts:
escape: '(?={{section_heading}})' escape: '(?={{section_heading}})'
function-call: 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: captures:
1: entity.name.function.man 1: entity.name.function.man
2: entity.name.function.man 2: entity.name.function.man
+118 -53
View File
@@ -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. /// A single matcher.
/// ///
/// Codegen converts this into a `Lazy<Option<GlobMatcher>>`. /// Codegen converts this into a `Lazy<Option<GlobMatcher>>`.
struct Matcher(Vec<MatcherSegment>); #[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize)]
/// Parse a matcher. #[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 /// Note that this implementation is rather strict: it will greedily interpret
/// every valid environment variable replacement as such, then immediately /// every valid environment variable replacement as such, then immediately
@@ -66,68 +103,95 @@ struct Matcher(Vec<MatcherSegment>);
/// ///
/// Revision history: /// Revision history:
/// - 2024-02-20: allow `{` and `}` (glob brace expansion) /// - 2024-02-20: allow `{` and `}` (glob brace expansion)
impl FromStr for Matcher { fn parse_glob(s: &str) -> Result<Vec<MatcherSegment>, anyhow::Error> {
type Err = anyhow::Error; use MatcherSegment as Seg;
fn from_str(s: &str) -> Result<Self, Self::Err> { static VAR_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\$\{([\w\d_]+)\}").unwrap());
use MatcherSegment as Seg;
static VAR_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\$\{([\w\d_]+)\}").unwrap());
let mut segments = vec![]; let mut segments = vec![];
let mut text_start = 0; let mut text_start = 0;
for capture in VAR_REGEX.captures_iter(s) { for capture in VAR_REGEX.captures_iter(s) {
let match_0 = capture.get(0).unwrap(); let match_0 = capture.get(0).unwrap();
// text before this var // text before this var
let text_end = match_0.start(); let text_end = match_0.start();
segments.push(Seg::Text(s[text_start..text_end].into())); segments.push(Seg::Text(s[text_start..text_end].into()));
text_start = match_0.end(); text_start = match_0.end();
// this var // this var
segments.push(Seg::Env(capture.get(1).unwrap().as_str().into())); 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 { impl ToTokens for Matcher {
fn to_tokens(&self, tokens: &mut TokenStream) { 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"), [] => unreachable!("0-length matcher should never be created"),
[MatcherSegment::Text(text)] => { [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 // 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); tokens.append_all(t);
} }
@@ -175,6 +239,7 @@ impl MatcherSegment {
/// A struct that models a single .toml file in /src/syntax_mapping/builtins/. /// A struct that models a single .toml file in /src/syntax_mapping/builtins/.
#[derive(Clone, Debug, Deserialize)] #[derive(Clone, Debug, Deserialize)]
struct MappingDefModel { struct MappingDefModel {
#[serde(default)]
mappings: IndexMap<MappingTarget, Vec<Matcher>>, mappings: IndexMap<MappingTarget, Vec<Matcher>>,
} }
impl MappingDefModel { impl MappingDefModel {
+3 -3
View File
@@ -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) | | | 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:&nbsp;[*](https://github.com/sharkdp/bat/issues/134) | :x: | :x: | (✔️) | :x: | :x:&nbsp;[*](https://github.com/rstacruz/hicat/issues/6) | :x: | :x: | :x: |
| Git integration | :heavy_check_mark: | :x: | :x: | :x: | :x: | :x: | :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: | | 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 | | 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: | | 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: | (:heavy_check_mark:) ? | :x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :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] (`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] (`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 | | Execution time [ms] (957 kB XML file) | 215 | 296 | 236 | 165 | 83 | 412 | 135 | 386 | 127 |
+21 -5
View File
@@ -37,6 +37,13 @@ Options:
name (like 'C++' or 'LaTeX') or possible file extension (like 'cpp', 'hpp' or 'md'). Use 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. '--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> -H, --highlight-line <N:M>
Highlight the specified line ranges with a different background color For example: Highlight the specified line ranges with a different background color For example:
'--highlight-line 40' highlights line 40 '--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 Set the tab width to T spaces. Use a width of 0 to pass tabs through directly
--wrap <mode> --wrap <mode>
Specify the text-wrapping mode (*auto*, never, character). The '--terminal-width' option Specify the text-wrapping mode (*auto*, never, character, word). The '--terminal-width'
can be used in addition to control the output width. option can be used in addition to control the output width.
-S, --chop-long-lines -S, --chop-long-lines
Truncate all lines longer than screen width. Alias for '--wrap=never'. Truncate all lines longer than screen width. Alias for '--wrap=never'.
@@ -70,7 +77,8 @@ Options:
--terminal-width <width> --terminal-width <width>
Explicitly set the width of the terminal instead of determining it automatically. If 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 prefixed with '+' or '-', the value will be treated as an offset to the actual terminal
width. See also: '--wrap'. width. This can also be configured via the BAT_WIDTH environment variable (e.g. export
BAT_WIDTH="100"). See also: '--wrap'.
-n, --number -n, --number
Only show line numbers, no other decorations. This is an alias for '--style=numbers' Only show line numbers, no other decorations. This is an alias for '--style=numbers'
@@ -194,6 +202,7 @@ Options:
'--line-range :40' prints lines 1 to 40 '--line-range :40' prints lines 1 to 40
'--line-range 40:' prints lines 40 to the end of the file '--line-range 40:' prints lines 40 to the end of the file
'--line-range 40' only prints line 40 '--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 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 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) '--line-range 30:40:2' prints lines 28 to 42 (range 30-40 with 2 lines of context)
@@ -202,8 +211,11 @@ Options:
Display a list of supported languages for syntax highlighting. Display a list of supported languages for syntax highlighting.
-u, --unbuffered -u, --unbuffered
This option exists for POSIX-compliance reasons ('u' is for 'unbuffered'). The output is Enable unbuffered input reading. When this flag is set, bat will display data as soon as
always unbuffered - this option is simply ignored. 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> --completion <SHELL>
Show shell completion for a certain shell. [possible values: bash, fish, zsh, ps1] Show shell completion for a certain shell. [possible values: bash, fish, zsh, ps1]
@@ -211,6 +223,10 @@ Options:
--diagnostic --diagnostic
Show diagnostic information for bug reports. 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 --acknowledgements
Show acknowledgements. Show acknowledgements.
+7 -1
View File
@@ -17,6 +17,8 @@ Options:
Show plain style (alias for '--style=plain'). Show plain style (alias for '--style=plain').
-l, --language <language> -l, --language <language>
Set the language for syntax highlighting. 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> -H, --highlight-line <N:M>
Highlight lines N through M. Highlight lines N through M.
--file-name <name> --file-name <name>
@@ -26,7 +28,7 @@ Options:
--tabs <T> --tabs <T>
Set the tab width to T spaces. Set the tab width to T spaces.
--wrap <mode> --wrap <mode>
Specify the text-wrapping mode (*auto*, never, character). Specify the text-wrapping mode (*auto*, never, character, word).
-S, --chop-long-lines -S, --chop-long-lines
Truncate all lines longer than screen width. Alias for '--wrap=never'. Truncate all lines longer than screen width. Alias for '--wrap=never'.
-n, --number -n, --number
@@ -58,8 +60,12 @@ Options:
Only print the lines from N to M. Only print the lines from N to M.
-L, --list-languages -L, --list-languages
Display all supported languages. Display all supported languages.
-u, --unbuffered
Enable unbuffered input reading for streaming use cases.
--completion <SHELL> --completion <SHELL>
Show shell completion for a certain shell. [possible values: bash, fish, zsh, ps1] 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 -h, --help
Print help (see more with '--help') Print help (see more with '--help')
-V, --version -V, --version
-2
View File
@@ -10,5 +10,3 @@ No issue will have a different priority based on sponsorship status of the
reporter. reporter.
Contributions from anybody are most welcomed, please see our [`CONTRIBUTING.md`](../CONTRIBUTING.md) guide. Contributions from anybody are most welcomed, please see our [`CONTRIBUTING.md`](../CONTRIBUTING.md) guide.
If you want to see our biggest sponsors, check the top of [`README.md`](../README.md#sponsors).
Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Generated
+27
View File
@@ -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
}
+40
View File
@@ -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
];
};
}
);
};
}
+154 -20
View File
@@ -158,7 +158,9 @@ impl HighlightingAssets {
let syntax_match = mapping.get_syntax_for(path); let syntax_match = mapping.get_syntax_for(path);
if let Some(MappingTarget::MapToUnknown) = syntax_match { if let Some(MappingTarget::MapToUnknown) = syntax_match {
return Err(Error::UndetectedSyntax(path.to_string_lossy().into())); return Err(Error::UndetectedSyntax(
crate::preprocessor::sanitize_for_terminal(&path.to_string_lossy()),
));
} }
if let Some(MappingTarget::MapTo(syntax_name)) = syntax_match { if let Some(MappingTarget::MapTo(syntax_name)) = syntax_match {
@@ -175,13 +177,17 @@ impl HighlightingAssets {
) { ) {
(Some(syntax), _) => Ok(syntax), (Some(syntax), _) => Ok(syntax),
(_, Some(MappingTarget::MapExtensionToUnknown)) => { (_, Some(MappingTarget::MapExtensionToUnknown)) => Err(Error::UndetectedSyntax(
Err(Error::UndetectedSyntax(path.to_string_lossy().into())) crate::preprocessor::sanitize_for_terminal(&path.to_string_lossy()),
} )),
_ => self _ => self
.get_syntax_for_file_extension(file_name, &mapping.ignored_suffixes)? .get_syntax_for_file_extension(file_name, &mapping.ignored_suffixes)?
.ok_or_else(|| Error::UndetectedSyntax(path.to_string_lossy().into())), .ok_or_else(|| {
Error::UndetectedSyntax(crate::preprocessor::sanitize_for_terminal(
&path.to_string_lossy(),
))
}),
} }
} }
@@ -210,6 +216,7 @@ impl HighlightingAssets {
pub(crate) fn get_syntax( pub(crate) fn get_syntax(
&self, &self,
language: Option<&str>, language: Option<&str>,
fallback_syntax: Option<&str>,
input: &mut OpenedInput, input: &mut OpenedInput,
mapping: &SyntaxMapping, mapping: &SyntaxMapping,
) -> Result<SyntaxReferenceInSet<'_>> { ) -> Result<SyntaxReferenceInSet<'_>> {
@@ -222,21 +229,50 @@ impl HighlightingAssets {
} }
let path = input.path(); let path = input.path();
let path_syntax = if let Some(path) = path { let absolute_path = path.and_then(|p| {
self.get_syntax_for_path( PathAbs::new(p)
PathAbs::new(path).map_or_else(|_| path.to_owned(), |p| p.as_path().to_path_buf()), .ok()
mapping, .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 { } else {
Err(Error::UndetectedSyntax("[unknown]".into())) 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 { match path_syntax {
// If a path wasn't provided, or if path based syntax detection Err(Error::UndetectedSyntax(path)) => {
// above failed, we fall back to first-line syntax detection. if let Some(syntax_in_set) = self.get_first_line_syntax(&mut input.reader)? {
Err(Error::UndetectedSyntax(path)) => self Ok(syntax_in_set)
.get_first_line_syntax(&mut input.reader)? } else if let Some(language) = fallback_syntax {
.ok_or(Error::UndetectedSyntax(path)), self.find_syntax_by_token(language)?
.ok_or_else(|| Error::UnknownSyntax(language.to_owned()))
} else {
Err(Error::UndetectedSyntax(path))
}
}
_ => path_syntax, _ => path_syntax,
} }
} }
@@ -262,6 +298,24 @@ impl HighlightingAssets {
.map(|syntax| SyntaxReferenceInSet { syntax, syntax_set })) .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<'_>>> { fn find_syntax_by_token(&self, token: &str) -> Result<Option<SyntaxReferenceInSet<'_>>> {
let syntax_set = self.get_syntax_set()?; let syntax_set = self.get_syntax_set()?;
Ok(syntax_set Ok(syntax_set
@@ -275,6 +329,9 @@ impl HighlightingAssets {
ignored_suffixes: &IgnoredSuffixes, ignored_suffixes: &IgnoredSuffixes,
) -> Result<Option<SyntaxReferenceInSet<'_>>> { ) -> Result<Option<SyntaxReferenceInSet<'_>>> {
let mut syntax = self.find_syntax_by_extension(Some(file_name))?; 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() { if syntax.is_none() {
syntax = syntax =
ignored_suffixes.try_with_stripped_suffix(file_name, |stripped_file_name| { ignored_suffixes.try_with_stripped_suffix(file_name, |stripped_file_name| {
@@ -395,11 +452,12 @@ mod tests {
fn get_syntax_name( fn get_syntax_name(
&self, &self,
language: Option<&str>, language: Option<&str>,
fallback_syntax: Option<&str>,
input: &mut OpenedInput, input: &mut OpenedInput,
mapping: &SyntaxMapping, mapping: &SyntaxMapping,
) -> String { ) -> String {
self.assets self.assets
.get_syntax(language, input, mapping) .get_syntax(language, fallback_syntax, input, mapping)
.map(|syntax_in_set| syntax_in_set.syntax.name.clone()) .map(|syntax_in_set| syntax_in_set.syntax.name.clone())
.unwrap_or_else(|_| "!no syntax!".to_owned()) .unwrap_or_else(|_| "!no syntax!".to_owned())
} }
@@ -419,7 +477,7 @@ mod tests {
let dummy_stdin: &[u8] = &[]; let dummy_stdin: &[u8] = &[];
let mut opened_input = input.open(dummy_stdin, None).unwrap(); 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 { fn syntax_for_file_with_content_os(&self, file_name: &OsStr, first_line: &str) -> String {
@@ -429,7 +487,7 @@ mod tests {
let dummy_stdin: &[u8] = &[]; let dummy_stdin: &[u8] = &[];
let mut opened_input = input.open(dummy_stdin, None).unwrap(); 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)] #[cfg(unix)]
@@ -449,7 +507,7 @@ mod tests {
let input = Input::stdin().with_name(Some(file_name)); let input = Input::stdin().with_name(Some(file_name));
let mut opened_input = input.open(content, None).unwrap(); 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 { fn syntax_is_same_for_inputkinds(&self, file_name: &str, content: &str) -> bool {
@@ -661,6 +719,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)] #[cfg(unix)]
#[test] #[test]
fn syntax_detection_for_symlinked_file() { fn syntax_detection_for_symlinked_file() {
@@ -682,8 +789,35 @@ mod tests {
let mut opened_input = input.open(dummy_stdin, None).unwrap(); let mut opened_input = input.open(dummy_stdin, None).unwrap();
assert_eq!( 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" "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"
);
}
} }
+245 -60
View File
@@ -38,9 +38,18 @@ pub fn env_no_color() -> bool {
env::var_os("NO_COLOR").is_some_and(|x| !x.is_empty()) env::var_os("NO_COLOR").is_some_and(|x| !x.is_empty())
} }
enum HelpType {
Short,
Long,
}
pub struct App { pub struct App {
pub matches: ArgMatches, pub matches: ArgMatches,
interactive_output: bool, 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,
} }
impl App { impl App {
@@ -50,65 +59,217 @@ impl App {
let interactive_output = std::io::stdout().is_terminal(); 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
});
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 { Ok(App {
matches: Self::matches(interactive_output)?, matches,
interactive_output, interactive_output,
number_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> { fn matches(interactive_output: bool) -> Result<ArgMatches> {
// Check if we should skip config file processing for special arguments // 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| { let should_skip_config = wild::args_os().any(|arg| {
matches!( matches!(
arg.to_str(), 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 // 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<_>>() if wild::args_os().any(|arg| arg == "--no-config") || should_skip_config {
} else if wild::args_os().any(|arg| arg == "--no-config") || should_skip_config {
// Skip the arguments in bats config file when --no-config is present // 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(); // Build arguments with config file
let mut args = if should_skip_config { let mut cli_args = wild::args_os();
// For special commands, don't even try to load env vars that might fail
vec![]
} else {
get_args_from_env_vars()
};
// Put the zero-th CLI argument (program name) first // Read arguments from bats config file
args.insert(0, cli_args.next().unwrap()); let config_args = match get_args_from_env_opts_var() {
Some(result) => result,
// .. and the rest at the end None => get_args_from_config_file(),
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
}; };
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<'_>> { pub fn config(&self, inputs: &[Input]) -> Result<Config<'_>> {
@@ -223,6 +384,10 @@ impl App {
None None
} }
}), }),
fallback_syntax: self
.matches
.get_one::<String>("fallback-syntax")
.map(|s| s.as_str()),
show_nonprintable: self.matches.get_flag("show-all"), show_nonprintable: self.matches.get_flag("show-all"),
nonprintable_notation: match self nonprintable_notation: match self
.matches .matches
@@ -238,27 +403,30 @@ impl App {
Some("no-printing") => BinaryBehavior::NoPrinting, Some("no-printing") => BinaryBehavior::NoPrinting,
_ => unreachable!("other values for --binary are not allowed"), _ => unreachable!("other values for --binary are not allowed"),
}, },
wrapping_mode: if self.interactive_output || maybe_term_width.is_some() { wrapping_mode: {
if !self.matches.get_flag("chop-long-lines") { if self.matches.get_flag("chop-long-lines") {
WrappingMode::NoWrapping(true)
} else {
match self.matches.get_one::<String>("wrap").map(|s| s.as_str()) { match self.matches.get_one::<String>("wrap").map(|s| s.as_str()) {
Some("character") => WrappingMode::Character, Some("character") => WrappingMode::Character,
Some("word") => WrappingMode::Word,
Some("never") => WrappingMode::NoWrapping(true), Some("never") => WrappingMode::NoWrapping(true),
Some("auto") | None => { Some("auto") | None => {
if style_components.plain() { if self.interactive_output || maybe_term_width.is_some() {
WrappingMode::NoWrapping(false) if style_components.plain() && maybe_term_width.is_none() {
WrappingMode::NoWrapping(false)
} else {
WrappingMode::Character
}
} else { } else {
WrappingMode::Character // 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"), _ => 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") colored_output: self.matches.get_flag("force-colorization")
|| match self.matches.get_one::<String>("color").map(|s| s.as_str()) { || match self.matches.get_one::<String>("color").map(|s| s.as_str()) {
@@ -277,8 +445,7 @@ impl App {
.map(|s| s.as_str()) .map(|s| s.as_str())
== Some("always") == Some("always")
|| self.matches.get_flag("force-colorization") || self.matches.get_flag("force-colorization")
|| self.matches.get_flag("number") || self.number_from_cli),
|| self.matches.contains_id("style") && !style_components.plain()),
tab_width: self tab_width: self
.matches .matches
.get_one::<String>("tabs") .get_one::<String>("tabs")
@@ -301,6 +468,8 @@ impl App {
Some("auto") => StripAnsiMode::Auto, Some("auto") => StripAnsiMode::Auto,
_ => unreachable!("other values for --strip-ansi are not allowed"), _ => 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(), theme: theme(self.theme_options()).to_string(),
visible_lines: match self.matches.try_contains_id("diff").unwrap_or_default() visible_lines: match self.matches.try_contains_id("diff").unwrap_or_default()
&& self.matches.get_flag("diff") && self.matches.get_flag("diff")
@@ -421,7 +590,17 @@ impl App {
// Plain if `--plain` is specified at least once. // Plain if `--plain` is specified at least once.
if self.matches.get_count("plain") > 0 { 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. // Default behavior.
@@ -458,21 +637,27 @@ impl App {
bat_warning!("Style 'rule' is a subset of style 'grid', 'rule' will not be visible."); 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) Ok(styled_components)
} }
fn theme_options(&self) -> ThemeOptions { pub(crate) fn theme_options(&self) -> ThemeOptions {
let theme = self Self::theme_options_from_matches(&self.matches)
.matches }
fn theme_options_from_matches(matches: &ArgMatches) -> ThemeOptions {
let theme = matches
.get_one::<String>("theme") .get_one::<String>("theme")
.map(|t| ThemePreference::from_str(t).unwrap()) .map(|t| ThemePreference::from_str(t).unwrap())
.unwrap_or_default(); .unwrap_or_default();
let theme_dark = self let theme_dark = matches
.matches
.get_one::<String>("theme-dark") .get_one::<String>("theme-dark")
.map(|t| ThemeName::from_str(t).unwrap()); .map(|t| ThemeName::from_str(t).unwrap());
let theme_light = self let theme_light = matches
.matches
.get_one::<String>("theme-light") .get_one::<String>("theme-light")
.map(|t| ThemeName::from_str(t).unwrap()); .map(|t| ThemeName::from_str(t).unwrap());
ThemeOptions { ThemeOptions {
+69 -17
View File
@@ -1,7 +1,5 @@
use bat::style::StyleComponentList; use bat::style::StyleComponentList;
use clap::{ use clap::{crate_name, crate_version, value_parser, Arg, ArgAction, ColorChoice, Command};
crate_name, crate_version, value_parser, Arg, ArgAction, ArgGroup, ColorChoice, Command,
};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::env; use std::env;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@@ -34,6 +32,8 @@ pub fn build_app(interactive_output: bool) -> Command {
.args_conflicts_with_subcommands(true) .args_conflicts_with_subcommands(true)
.allow_external_subcommands(true) .allow_external_subcommands(true)
.disable_help_subcommand(true) .disable_help_subcommand(true)
.disable_help_flag(true)
.disable_version_flag(true)
.max_term_width(100) .max_term_width(100)
.about("A cat(1) clone with wings.") .about("A cat(1) clone with wings.")
.long_about("A cat(1) clone with syntax highlighting and Git integration.") .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.", 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(
Arg::new("highlight-line") Arg::new("highlight-line")
.long("highlight-line") .long("highlight-line")
@@ -211,11 +222,11 @@ pub fn build_app(interactive_output: bool) -> Command {
.long("wrap") .long("wrap")
.overrides_with("wrap") .overrides_with("wrap")
.value_name("mode") .value_name("mode")
.value_parser(["auto", "never", "character"]) .value_parser(["auto", "never", "character", "word"])
.default_value("auto") .default_value("auto")
.hide_default_value(true) .hide_default_value(true)
.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). \ .long_help("Specify the text-wrapping mode (*auto*, never, character, word). \
The '--terminal-width' option can be used in addition to \ The '--terminal-width' option can be used in addition to \
control the output width."), control the output width."),
) )
@@ -229,6 +240,7 @@ pub fn build_app(interactive_output: bool) -> Command {
.arg( .arg(
Arg::new("terminal-width") Arg::new("terminal-width")
.long("terminal-width") .long("terminal-width")
.overrides_with("terminal-width")
.value_name("width") .value_name("width")
.hide_short_help(true) .hide_short_help(true)
.allow_hyphen_values(true) .allow_hyphen_values(true)
@@ -244,10 +256,13 @@ pub fn build_app(interactive_output: bool) -> Command {
}) })
.map_err(|e| e.to_string()) .map_err(|e| e.to_string())
}) })
.help( .help("Explicitly set the width of the terminal instead of determining it automatically.")
.long_help(
"Explicitly set the width of the terminal instead of determining it \ "Explicitly set the width of the terminal instead of determining it \
automatically. If prefixed with '+' or '-', the value will be treated \ automatically. If prefixed with '+' or '-', the value will be treated \
as an offset to the actual terminal width. See also: '--wrap'.", as an offset to the actual terminal width. This can also be configured \
via the BAT_WIDTH environment variable (e.g. export BAT_WIDTH=\"100\"). \
See also: '--wrap'.",
), ),
) )
.arg( .arg(
@@ -519,6 +534,7 @@ pub fn build_app(interactive_output: bool) -> Command {
.short('r') .short('r')
.action(ArgAction::Append) .action(ArgAction::Append)
.value_name("N:M") .value_name("N:M")
.allow_hyphen_values(true)
.help("Only print the lines from N to M.") .help("Only print the lines from N to M.")
.long_help( .long_help(
"Only print the specified range of lines for each file. \ "Only print the specified range of lines for each file. \
@@ -527,6 +543,7 @@ pub fn build_app(interactive_output: bool) -> Command {
'--line-range :40' prints lines 1 to 40\n \ '--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:' prints lines 40 to the end of the file\n \
'--line-range 40' only prints line 40\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 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 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)", '--line-range 30:40:2' prints lines 28 to 42 (range 30-40 with 2 lines of context)",
@@ -546,11 +563,14 @@ pub fn build_app(interactive_output: bool) -> Command {
.short('u') .short('u')
.long("unbuffered") .long("unbuffered")
.action(ArgAction::SetTrue) .action(ArgAction::SetTrue)
.hide_short_help(true) .help("Enable unbuffered input reading for streaming use cases.")
.long_help( .long_help(
"This option exists for POSIX-compliance reasons ('u' is for \ "Enable unbuffered input reading. When this flag is set, bat will \
'unbuffered'). The output is always unbuffered - this option \ display data as soon as it is available, without waiting for a \
is simply ignored.", 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( .arg(
@@ -639,6 +659,18 @@ pub fn build_app(interactive_output: bool) -> Command {
.hide_short_help(true) .hide_short_help(true)
.help("Show diagnostic information for bug reports."), .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(
Arg::new("acknowledgements") Arg::new("acknowledgements")
.long("acknowledgements") .long("acknowledgements")
@@ -652,6 +684,21 @@ pub fn build_app(interactive_output: bool) -> Command {
.action(ArgAction::SetTrue) .action(ArgAction::SetTrue)
.hide_short_help(true) .hide_short_help(true)
.help("Sets terminal title to filenames when using a pager."), .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, // Check if the current directory contains a file name cache. Otherwise,
@@ -663,11 +710,20 @@ pub fn build_app(interactive_output: bool) -> Command {
Command::new("cache") Command::new("cache")
.hide(true) .hide(true)
.about("Modify the syntax-definition and theme cache") .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(
Arg::new("build") Arg::new("build")
.long("build") .long("build")
.short('b') .short('b')
.action(ArgAction::SetTrue) .action(ArgAction::SetTrue)
.conflicts_with("clear")
.help("Initialize (or update) the syntax/theme cache.") .help("Initialize (or update) the syntax/theme cache.")
.long_help( .long_help(
"Initialize (or update) the syntax/theme cache by loading from \ "Initialize (or update) the syntax/theme cache by loading from \
@@ -679,13 +735,9 @@ pub fn build_app(interactive_output: bool) -> Command {
.long("clear") .long("clear")
.short('c') .short('c')
.action(ArgAction::SetTrue) .action(ArgAction::SetTrue)
.conflicts_with("build")
.help("Remove the cached syntax definitions and themes."), .help("Remove the cached syntax definitions and themes."),
) )
.group(
ArgGroup::new("cache-actions")
.args(["build", "clear"])
.required(true),
)
.arg( .arg(
Arg::new("source") Arg::new("source")
.long("source") .long("source")
+56 -4
View File
@@ -2,7 +2,7 @@ use std::env;
use std::ffi::OsString; use std::ffi::OsString;
use std::fs; use std::fs;
use std::io::{self, Write}; use std::io::{self, Write};
use std::path::PathBuf; use std::path::{Path, PathBuf};
use crate::directories::PROJECT_DIRS; 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> { pub fn get_args_from_config_file() -> Result<Vec<OsString>, shell_words::ParseError> {
let mut config = String::new(); 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_str(&c);
config.push('\n'); config.push('\n');
} }
if let Ok(c) = fs::read_to_string(config_file()) { // Skip the user config if it resolves to the same file as the system config,
config.push_str(&c); // 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) 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>> { 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)) env::var("BAT_OPTS").ok().map(|s| get_args_from_str(&s))
} }
@@ -139,6 +153,7 @@ fn get_args_from_str(content: &str) -> Result<Vec<OsString>, shell_words::ParseE
pub fn get_args_from_env_vars() -> Vec<OsString> { pub fn get_args_from_env_vars() -> Vec<OsString> {
[ [
("--tabs", "BAT_TABS"), ("--tabs", "BAT_TABS"),
("--terminal-width", "BAT_WIDTH"),
("--theme", bat::theme::env::BAT_THEME), ("--theme", bat::theme::env::BAT_THEME),
("--theme-dark", bat::theme::env::BAT_THEME_DARK), ("--theme-dark", bat::theme::env::BAT_THEME_DARK),
("--theme-light", bat::theme::env::BAT_THEME_LIGHT), ("--theme-light", bat::theme::env::BAT_THEME_LIGHT),
@@ -214,3 +229,40 @@ fn comments() {
get_args_from_str(config).unwrap() 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));
}
+51 -22
View File
@@ -16,8 +16,7 @@ use std::io::{BufReader, Write};
use std::path::Path; use std::path::Path;
use std::process; 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::Color::Green;
use nu_ansi_term::Style; use nu_ansi_term::Style;
@@ -39,7 +38,7 @@ use bat::{
error::*, error::*,
input::Input, input::Input,
style::{StyleComponent, StyleComponents}, style::{StyleComponent, StyleComponents},
theme::{color_scheme, default_theme, ColorScheme}, theme::{default_theme, theme, ColorScheme, ThemeOptions},
MappingTarget, PagingMode, MappingTarget, PagingMode,
}; };
@@ -197,7 +196,7 @@ pub fn list_themes(
cfg: &Config, cfg: &Config,
config_dir: &Path, config_dir: &Path,
cache_dir: &Path, cache_dir: &Path,
detect_color_scheme: DetectColorScheme, theme_options: ThemeOptions,
) -> Result<()> { ) -> Result<()> {
let assets = assets_from_cache_or_binary(cfg.use_custom_assets, cache_dir)?; let assets = assets_from_cache_or_binary(cfg.use_custom_assets, cache_dir)?;
let mut config = cfg.clone(); let mut config = cfg.clone();
@@ -206,11 +205,10 @@ pub fn list_themes(
config.language = Some("Rust"); config.language = Some("Rust");
config.style_components = StyleComponents(style); config.style_components = StyleComponents(style);
let mut output_type = let default_theme_name = theme(theme_options).to_string();
OutputType::from_mode(config.paging_mode, config.wrapping_mode, config.pager)?; let mut buf = String::new();
let mut writer = output_type.handle()?; let mut handle = OutputHandle::FmtWrite(&mut buf);
let default_theme_name = default_theme(color_scheme(detect_color_scheme).unwrap_or_default());
for theme in assets.themes() { for theme in assets.themes() {
let default_theme_info = if default_theme_name == theme { let default_theme_info = if default_theme_name == theme {
" (default)" " (default)"
@@ -221,42 +219,48 @@ pub fn list_themes(
} else { } else {
"" ""
}; };
if config.colored_output { if config.colored_output {
writeln!( handle.write_fmt(format_args!(
writer, "{}{default_theme_info}\n\n",
"Theme: {}{default_theme_info}\n",
Style::new().bold().paint(theme.to_string()), Style::new().bold().paint(theme.to_string()),
)?; ))?;
config.theme = theme.to_string(); config.theme = theme.to_string();
Controller::new(&config, &assets) Controller::new(&config, &assets)
.run(vec![theme_preview_file()], Some(&mut writer)) .run(vec![theme_preview_file()], Some(&mut handle))
.ok(); .ok();
writeln!(writer)?; handle.write_fmt(format_args!("\n"))?;
} else if config.loop_through { } else if config.loop_through {
writeln!(writer, "{theme}")?; handle.write_fmt(format_args!("{theme}\n"))?;
} else { } else {
writeln!(writer, "{theme}{default_theme_info}")?; handle.write_fmt(format_args!("{theme}{default_theme_info}\n"))?;
} }
} }
if config.colored_output { if config.colored_output {
writeln!( handle.write_fmt(format_args!(
writer,
"Further themes can be installed to '{}', \ "Further themes can be installed to '{}', \
and are added to the cache with `bat cache --build`. \ and are added to the cache with `bat cache --build`. \
For more information, see:\n\n \ For more information, see:\n\n \
https://github.com/sharkdp/bat#adding-new-themes", https://github.com/sharkdp/bat#adding-new-themes",
config_dir.join("themes").to_string_lossy() 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(()) Ok(())
} }
fn set_terminal_title_to(new_terminal_title: String) { fn set_terminal_title_to(new_terminal_title: String) {
let osc_command_for_setting_terminal_title = "\x1b]0;"; let osc_command_for_setting_terminal_title = "\x1b]0;";
let osc_end_command = "\x07"; let osc_end_command = "\x07";
print!("{osc_command_for_setting_terminal_title}{new_terminal_title}{osc_end_command}"); // Prevent BEL/ESC/C1 bytes in the title from terminating or nesting the OSC.
let safe_title = bat::sanitize_for_terminal(&new_terminal_title);
print!("{osc_command_for_setting_terminal_title}{safe_title}{osc_end_command}");
io::stdout().flush().unwrap(); io::stdout().flush().unwrap();
} }
@@ -282,7 +286,28 @@ fn run_controller(inputs: Vec<Input>, config: &Config, cache_dir: &Path) -> Resu
#[cfg(feature = "bugreport")] #[cfg(feature = "bugreport")]
fn invoke_bugreport(app: &App, cache_dir: &Path) { fn invoke_bugreport(app: &App, cache_dir: &Path) {
use bugreport::{bugreport, collector::*, format::Markdown}; use bugreport::{bugreport, collector::*, format::Markdown, report::ReportEntry};
struct ColorSchemeCollector;
impl Collector for ColorSchemeCollector {
fn description(&self) -> &str {
"Detected terminal color scheme"
}
fn collect(
&mut self,
_: &bugreport::CrateInfo,
) -> std::result::Result<ReportEntry, CollectionError> {
let color_scheme = bat::theme::color_scheme(bat::theme::DetectColorScheme::Always);
let text = match color_scheme {
Some(bat::theme::ColorScheme::Dark) => "dark",
Some(bat::theme::ColorScheme::Light) => "light",
None => "not detected",
};
Ok(ReportEntry::Text(text.to_string()))
}
}
let pager = bat::config::get_pager_executable( let pager = bat::config::get_pager_executable(
app.matches.get_one::<String>("pager").map(|s| s.as_str()), app.matches.get_one::<String>("pager").map(|s| s.as_str()),
) )
@@ -304,6 +329,9 @@ fn invoke_bugreport(app: &App, cache_dir: &Path) {
"BAT_STYLE", "BAT_STYLE",
"BAT_TABS", "BAT_TABS",
"BAT_THEME", "BAT_THEME",
"BAT_WIDTH",
bat::theme::env::BAT_THEME_DARK,
bat::theme::env::BAT_THEME_LIGHT,
"COLORTERM", "COLORTERM",
"LANG", "LANG",
"LC_ALL", "LC_ALL",
@@ -323,6 +351,7 @@ fn invoke_bugreport(app: &App, cache_dir: &Path) {
custom_assets_metadata, custom_assets_metadata,
)) ))
.info(DirectoryEntries::new("Custom assets", cache_dir)) .info(DirectoryEntries::new("Custom assets", cache_dir))
.info(ColorSchemeCollector)
.info(CompileTimeInformation::default()); .info(CompileTimeInformation::default());
#[cfg(feature = "paging")] #[cfg(feature = "paging")]
@@ -398,7 +427,7 @@ fn run() -> Result<bool> {
}; };
run_controller(inputs, &plain_config, cache_dir) run_controller(inputs, &plain_config, cache_dir)
} else if app.matches.get_flag("list-themes") { } else if app.matches.get_flag("list-themes") {
list_themes(&config, config_dir, cache_dir, DetectColorScheme::default())?; list_themes(&config, config_dir, cache_dir, app.theme_options())?;
Ok(true) Ok(true)
} else if app.matches.get_flag("config-file") { } else if app.matches.get_flag("config-file") {
println!("{}", config_file().to_string_lossy()); println!("{}", config_file().to_string_lossy());
+95 -3
View File
@@ -38,6 +38,9 @@ pub struct Config<'a> {
/// The explicitly configured language, if any /// The explicitly configured language, if any
pub language: Option<&'a str>, 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. /// Whether or not to show/replace non-printable characters like space, tab and newline.
pub show_nonprintable: bool, pub show_nonprintable: bool,
@@ -99,14 +102,20 @@ pub struct Config<'a> {
#[cfg(feature = "lessopen")] #[cfg(feature = "lessopen")]
pub use_lessopen: bool, 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, pub set_terminal_title: bool,
/// The maximum number of consecutive empty lines to display /// The maximum number of consecutive empty lines to display
pub squeeze_lines: Option<usize>, 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, 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"))] #[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) crate::pager::get_pager(config_pager)
.ok() .ok()
.flatten() .flatten()
.map(|pager| pager.bin) .and_then(|pager| {
if pager.kind != crate::pager::PagerKind::Builtin {
Some(pager.bin)
} else {
None
}
})
} }
#[test] #[test]
@@ -141,3 +156,80 @@ fn default_config_should_highlight_no_lines() {
RangeCheckResult::InRange 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
View File
@@ -50,10 +50,12 @@ impl Controller<'_> {
output_handle: Option<&mut OutputHandle<'_>>, output_handle: Option<&mut OutputHandle<'_>>,
mut handle_error: impl FnMut(&Error, &mut dyn Write), mut handle_error: impl FnMut(&Error, &mut dyn Write),
) -> Result<bool> { ) -> 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")] #[cfg(feature = "paging")]
{ if output_handle.is_none() {
use crate::input::InputKind; use crate::input::InputKind;
use std::path::Path; use std::path::Path;
@@ -74,25 +76,35 @@ impl Controller<'_> {
let wrapping_mode = self.config.wrapping_mode; 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"))] #[cfg(not(feature = "paging"))]
{ if output_handle.is_none() {
output_type = OutputType::stdout(); 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 { let stdout_identifier = if cfg!(windows) || attached_to_pager {
None None
} else { } else {
clircle::Identifier::stdout() clircle::Identifier::stdout()
}; };
let mut writer = match output_handle { let mut writer = match (output_handle, &mut output_type_opt) {
Some(OutputHandle::FmtWrite(w)) => OutputHandle::FmtWrite(w), (Some(OutputHandle::FmtWrite(w)), _) => OutputHandle::FmtWrite(w),
Some(OutputHandle::IoWrite(w)) => OutputHandle::IoWrite(w), (Some(OutputHandle::IoWrite(w)), _) => OutputHandle::IoWrite(w),
None => output_type.handle()?, (None, Some(ot)) => ot.handle()?,
(None, None) => unreachable!("No output handle and no output type available"),
}; };
let mut no_errors: bool = true; let mut no_errors: bool = true;
let stderr = io::stderr(); let stderr = io::stderr();
@@ -146,6 +158,7 @@ impl Controller<'_> {
#[cfg(not(feature = "lessopen"))] #[cfg(not(feature = "lessopen"))]
input.open(stdin, stdout_identifier)? input.open(stdin, stdout_identifier)?
}; };
opened_input.reader.unbuffered = self.config.unbuffered;
#[cfg(feature = "git")] #[cfg(feature = "git")]
let line_changes = if self.config.visible_lines.diff_mode() let line_changes = if self.config.visible_lines.diff_mode()
|| (!self.config.loop_through && self.config.style_components.changes()) || (!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)?; printer.print_line(false, writer, line_nr, &line, max_buffered_line_number)?;
if self.config.unbuffered {
writer.flush()?;
}
} }
RangeCheckResult::AfterLastRange => { RangeCheckResult::AfterLastRange => {
break; break;
+185 -23
View File
@@ -207,7 +207,7 @@ impl<'a> Input<'a> {
kind: OpenedInputKind::StdIn, kind: OpenedInputKind::StdIn,
description, description,
metadata: self.metadata, metadata: self.metadata,
reader: InputReader::new(stdin), reader: InputReader::try_new(stdin)?,
}) })
} }
@@ -216,34 +216,34 @@ impl<'a> Input<'a> {
description, description,
metadata: self.metadata, metadata: self.metadata,
reader: { reader: {
let mut file = File::open(&path) let path_display =
.map_err(|e| format!("'{}': {e}", path.to_string_lossy()))?; crate::preprocessor::sanitize_for_terminal(&path.to_string_lossy());
let mut file =
File::open(&path).map_err(|e| format!("'{path_display}': {e}"))?;
if file.metadata()?.is_dir() { if file.metadata()?.is_dir() {
return Err(format!("'{}' is a directory.", path.to_string_lossy()).into()); return Err(format!("'{path_display}' is a directory.").into());
} }
if let Some(stdout) = stdout_identifier { if let Some(stdout) = stdout_identifier {
let input_identifier = Identifier::try_from(file).map_err(|e| { let input_identifier = Identifier::try_from(file)
format!("{}: Error identifying file: {e}", path.to_string_lossy()) .map_err(|e| format!("{path_display}: Error identifying file: {e}"))?;
})?;
if stdout.surely_conflicts_with(&input_identifier) { if stdout.surely_conflicts_with(&input_identifier) {
return Err(format!( return Err(format!(
"IO circle detected. The input from '{}' is also an output. Aborting to avoid infinite loop.", "IO circle detected. The input from '{path_display}' is also an output. Aborting to avoid infinite loop.",
path.to_string_lossy()
) )
.into()); .into());
} }
file = input_identifier.into_inner().expect("The file was lost in the clircle::Identifier, this should not have happened..."); file = input_identifier.into_inner().expect("The file was lost in the clircle::Identifier, this should not have happened...");
} }
InputReader::new(BufReader::new(file)) InputReader::try_new(BufReader::new(file))?
}, },
}), }),
InputKind::CustomReader(reader) => Ok(OpenedInput { InputKind::CustomReader(reader) => Ok(OpenedInput {
description, description,
kind: OpenedInputKind::CustomReader, kind: OpenedInputKind::CustomReader,
metadata: self.metadata, metadata: self.metadata,
reader: InputReader::new(BufReader::new(reader)), reader: InputReader::try_new(BufReader::new(reader))?,
}), }),
} }
} }
@@ -253,30 +253,33 @@ pub(crate) struct InputReader<'a> {
inner: Box<dyn BufRead + 'a>, inner: Box<dyn BufRead + 'a>,
pub(crate) first_line: Vec<u8>, pub(crate) first_line: Vec<u8>,
pub(crate) content_type: Option<ContentType>, pub(crate) content_type: Option<ContentType>,
pub(crate) unbuffered: bool,
} }
impl<'a> InputReader<'a> { impl<'a> InputReader<'a> {
pub(crate) fn new<R: BufRead + 'a>(mut reader: R) -> InputReader<'a> { #[cfg(test)]
let mut first_line = vec![]; pub(crate) fn new<R: BufRead + 'a>(reader: R) -> InputReader<'a> {
reader.read_until(b'\n', &mut first_line).ok(); Self::try_new(reader).expect("reading the first line failed")
}
let content_type = if first_line.is_empty() { pub(crate) fn try_new<R: BufRead + 'a>(mut reader: R) -> io::Result<InputReader<'a>> {
None let mut first_line = vec![];
} else { reader.read_until(b'\n', &mut first_line)?;
Some(content_inspector::inspect(&first_line[..]))
}; let content_type = inspect_content_type(&first_line);
if content_type == Some(ContentType::UTF_16LE) { if content_type == Some(ContentType::UTF_16LE) {
read_utf16_line(&mut reader, &mut first_line, 0x00, 0x0A).ok(); read_utf16_line(&mut reader, &mut first_line, 0x00, 0x0A)?;
} else if content_type == Some(ContentType::UTF_16BE) { } else if content_type == Some(ContentType::UTF_16BE) {
read_utf16_line(&mut reader, &mut first_line, 0x0A, 0x00).ok(); read_utf16_line(&mut reader, &mut first_line, 0x0A, 0x00)?;
} }
InputReader { Ok(InputReader {
inner: Box::new(reader), inner: Box::new(reader),
first_line, first_line,
content_type, content_type,
} unbuffered: false,
})
} }
pub(crate) fn read_line(&mut self, buf: &mut Vec<u8>) -> io::Result<bool> { pub(crate) fn read_line(&mut self, buf: &mut Vec<u8>) -> io::Result<bool> {
@@ -292,9 +295,48 @@ impl<'a> InputReader<'a> {
return read_utf16_line(&mut self.inner, buf, 0x0A, 0x00); 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)?; let res = self.inner.read_until(b'\n', buf).map(|size| size > 0)?;
Ok(res) 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 inspect_content_type(first_line: &[u8]) -> Option<ContentType> {
if first_line.is_empty() {
return None;
}
let content_type = content_inspector::inspect(first_line);
if content_type == ContentType::UTF_8 && has_zip_signature(first_line) {
Some(ContentType::BINARY)
} else {
Some(content_type)
}
}
fn has_zip_signature(bytes: &[u8]) -> bool {
[b"PK\x03\x04", b"PK\x05\x06", b"PK\x07\x08"]
.into_iter()
.any(|signature| bytes.starts_with(signature))
} }
fn read_utf16_line<R: BufRead>( fn read_utf16_line<R: BufRead>(
@@ -352,6 +394,43 @@ fn basic() {
assert!(buffer.is_empty()); assert!(buffer.is_empty());
} }
#[test]
fn zip_magic_headers_are_treated_as_binary() {
for content in [b"PK\x03\x04hello", b"PK\x05\x06hello", b"PK\x07\x08hello"] {
let reader = InputReader::new(&content[..]);
assert_eq!(Some(ContentType::BINARY), reader.content_type);
}
}
#[test]
fn non_zip_pk_prefix_is_not_treated_as_binary() {
assert_eq!(
Some(ContentType::UTF_8),
inspect_content_type(b"PK\x03\x03hello")
);
}
#[test]
fn input_open_returns_initial_read_errors() {
struct FailingRead;
impl Read for FailingRead {
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
Err(io::Error::other("initial read failed"))
}
}
let input = Input::from_reader(Box::new(FailingRead));
let result = input.open(io::empty(), None);
assert!(result.is_err());
assert!(result
.err()
.unwrap()
.to_string()
.contains("initial read failed"));
}
#[test] #[test]
fn utf16le() { fn utf16le() {
let content = b"\xFF\xFE\x73\x00\x0A\x00\x64\x00"; let content = b"\xFF\xFE\x73\x00\x0A\x00\x64\x00";
@@ -381,6 +460,89 @@ fn utf16le() {
assert!(buffer.is_empty()); 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] #[test]
fn utf16le_issue3367() { fn utf16le_issue3367() {
let content = b"\xFF\xFE\x0A\x4E\x00\x4E\x0A\x4F\x00\x52\x0A\x00\ let content = b"\xFF\xFE\x0A\x4E\x00\x4E\x0A\x4F\x00\x52\x0A\x00\
+3 -3
View File
@@ -155,7 +155,7 @@ impl LessOpenPreprocessor {
Ok(OpenedInput { Ok(OpenedInput {
kind, kind,
reader: InputReader::new(BufReader::new( reader: InputReader::try_new(BufReader::new(
if matches!(self.kind, LessOpenKind::TempFile) { if matches!(self.kind, LessOpenKind::TempFile) {
let lessopen_string = match String::from_utf8(lessopen_stdout) { let lessopen_string = match String::from_utf8(lessopen_stdout) {
Ok(string) => string, Ok(string) => string,
@@ -192,7 +192,7 @@ impl LessOpenPreprocessor {
.map(|s| s.replacen("%s", &path_str, 1).replacen("%s", "-", 1)), .map(|s| s.replacen("%s", &path_str, 1).replacen("%s", "-", 1)),
} }
}, },
)), ))?,
metadata: input.metadata, metadata: input.metadata,
description: input.description, description: input.description,
}) })
@@ -261,7 +261,7 @@ impl Drop for Preprocessed {
} }
}; };
if lessclose_output.status.success() { if !lessclose_output.status.success() {
bat_warning!("$LESSCLOSE exited with nonzero exit code",) bat_warning!("$LESSCLOSE exited with nonzero exit code",)
}; };
} }
+1
View File
@@ -54,6 +54,7 @@ mod vscreen;
pub(crate) mod wrapping; pub(crate) mod wrapping;
pub use nonprintable_notation::{BinaryBehavior, NonprintableNotation}; pub use nonprintable_notation::{BinaryBehavior, NonprintableNotation};
pub use preprocessor::sanitize_for_terminal;
pub use preprocessor::StripAnsiMode; pub use preprocessor::StripAnsiMode;
pub use pretty_printer::{Input, PrettyPrinter, Syntax}; pub use pretty_printer::{Input, PrettyPrinter, Syntax};
pub use syntax_mapping::{MappingTarget, SyntaxMapping}; pub use syntax_mapping::{MappingTarget, SyntaxMapping};
+49 -13
View File
@@ -23,6 +23,18 @@ pub struct BuiltinPager {
impl BuiltinPager { impl BuiltinPager {
fn new() -> Self { fn new() -> Self {
let pager = minus::Pager::new(); 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 handle = {
let pager = pager.clone(); let pager = pager.clone();
Some(spawn(move || { Some(spawn(move || {
@@ -105,6 +117,10 @@ impl OutputType {
let resolved_path = match grep_cli::resolve_binary(&pager.bin) { let resolved_path = match grep_cli::resolve_binary(&pager.bin) {
Ok(path) => path, Ok(path) => path,
Err(_) => { Err(_) => {
crate::bat_warning!(
"Pager '{}' not found, outputting to stdout instead",
pager.bin
);
return Ok(OutputType::stdout()); return Ok(OutputType::stdout());
} }
}; };
@@ -131,8 +147,13 @@ impl OutputType {
p.arg("-S"); // Short version of --chop-long-lines for compatibility 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' // 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 // Passing '--no-init' fixes a bug with '--quit-if-one-screen' in older
// versions of 'less'. Unfortunately, it also breaks mouse-wheel support. // 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 // For newer versions (530 or 558 on Windows), we omit '--no-init' as it
// is not needed anymore. // is not needed anymore.
match retrieve_less_version(&pager.bin) { if single_screen_action == SingleScreenAction::Quit {
None => { match less_version {
p.arg("--no-init"); 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 { } else {
p.args(args); p.args(args);
@@ -167,7 +190,13 @@ impl OutputType {
Ok(p.stdin(Stdio::piped()) Ok(p.stdin(Stdio::piped())
.spawn() .spawn()
.map(OutputType::Pager) .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 { pub(crate) fn stdout() -> Self {
@@ -208,8 +237,8 @@ impl Drop for OutputType {
let _ = command.wait(); let _ = command.wait();
} }
OutputType::BuiltinPager(ref mut pager) => { OutputType::BuiltinPager(ref mut pager) => {
if pager.handle.is_some() { if let Some(handle) = pager.handle.take() {
let _ = pager.handle.take().unwrap().join().unwrap(); let _ = handle.join();
} }
} }
OutputType::Stdout(_) => (), OutputType::Stdout(_) => (),
@@ -229,4 +258,11 @@ impl OutputHandle<'_> {
Self::FmtWrite(handle) => handle.write_fmt(args).map_err(Into::into), 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(()),
}
}
} }
+116
View File
@@ -149,6 +149,64 @@ pub fn strip_ansi(line: &str) -> String {
buffer buffer
} }
/// Escape C0, DEL, and C1 control characters so a string from an untrusted
/// filename or path can be safely written to the terminal.
pub fn sanitize_for_terminal(input: &str) -> String {
if !input
.chars()
.any(|c| matches!(c, '\x00'..='\x08' | '\x0A'..='\x1F' | '\x7F'..='\u{9F}'))
{
return input.to_owned();
}
let mut out = String::with_capacity(input.len() + 8);
for c in input.chars() {
match c {
'\t' => out.push('\t'),
'\x00'..='\x1F' => {
out.push('^');
out.push(char::from_u32(0x40 + c as u32).unwrap_or('?'));
}
'\x7F' => out.push_str("^?"),
'\u{80}'..='\u{9F}' => {
use std::fmt::Write as _;
let _ = write!(out, "\\u{{{:x}}}", c as u32);
}
other => out.push(other),
}
}
out
}
/// 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)] #[derive(Debug, PartialEq, Clone, Copy, Default)]
pub enum StripAnsiMode { pub enum StripAnsiMode {
#[default] #[default]
@@ -211,3 +269,61 @@ fn test_strip_ansi() {
"multiple sequences" "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), "äöü");
}
#[test]
fn test_sanitize_for_terminal_passthrough() {
assert_eq!(sanitize_for_terminal(""), "");
assert_eq!(sanitize_for_terminal("hello.txt"), "hello.txt");
assert_eq!(sanitize_for_terminal("résumé.pdf"), "résumé.pdf");
assert_eq!(sanitize_for_terminal("日本語.md"), "日本語.md");
assert_eq!(
sanitize_for_terminal("path/with spaces/file.log"),
"path/with spaces/file.log"
);
assert_eq!(sanitize_for_terminal("a\tb"), "a\tb");
}
#[test]
fn test_sanitize_for_terminal_c0_controls() {
assert_eq!(
sanitize_for_terminal("\x1b[31mINJECTED\x1b[0m.txt"),
"^[[31mINJECTED^[[0m.txt"
);
assert_eq!(sanitize_for_terminal("bad\x07rest"), "bad^Grest");
assert_eq!(sanitize_for_terminal("\x00\x08\n\r\x7F"), "^@^H^J^M^?");
assert_eq!(sanitize_for_terminal("\u{9b}31m"), "\\u{9b}31m");
assert_eq!(
sanitize_for_terminal("\u{9d}0;pwned\x07"),
"\\u{9d}0;pwned^G"
);
}
#[test]
fn test_sanitize_for_terminal_idempotent_on_sanitized() {
let dirty = "\x1b]0;pwned\x07file.txt";
let clean = sanitize_for_terminal(dirty);
assert_eq!(sanitize_for_terminal(&clean), clean);
assert!(!clean.contains('\x1b'));
assert!(!clean.contains('\x07'));
}
+98 -12
View File
@@ -29,8 +29,9 @@ use crate::error::*;
use crate::input::OpenedInput; use crate::input::OpenedInput;
use crate::line_range::{MaxBufferedLineNumber, RangeCheckResult}; use crate::line_range::{MaxBufferedLineNumber, RangeCheckResult};
use crate::output::OutputHandle; use crate::output::OutputHandle;
use crate::preprocessor::strip_ansi; use crate::preprocessor::{
use crate::preprocessor::{expand_tabs, replace_nonprintable}; expand_tabs, replace_nonprintable, sanitize_for_terminal, strip_ansi, strip_overstrike,
};
use crate::style::StyleComponent; use crate::style::StyleComponent;
use crate::terminal::{as_terminal_escaped, to_ansi_color}; use crate::terminal::{as_terminal_escaped, to_ansi_color};
use crate::vscreen::{AnsiStyle, EscapeSequence, EscapeSequenceIterator}; use crate::vscreen::{AnsiStyle, EscapeSequence, EscapeSequenceIterator};
@@ -38,6 +39,16 @@ use crate::wrapping::WrappingMode;
use crate::BinaryBehavior; use crate::BinaryBehavior;
use crate::StripAnsiMode; 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 { const ANSI_UNDERLINE_ENABLE: EscapeSequence = EscapeSequence::CSI {
raw_sequence: "\x1B[4m", raw_sequence: "\x1B[4m",
parameters: "4", parameters: "4",
@@ -199,6 +210,7 @@ pub(crate) struct InteractivePrinter<'a> {
background_color_highlight: Option<Color>, background_color_highlight: Option<Color>,
consecutive_empty_lines: usize, consecutive_empty_lines: usize,
strip_ansi: bool, strip_ansi: bool,
strip_overstrike: bool,
} }
impl<'a> InteractivePrinter<'a> { impl<'a> InteractivePrinter<'a> {
@@ -263,17 +275,27 @@ impl<'a> InteractivePrinter<'a> {
|| matches!(config.binary, BinaryBehavior::AsText)) || matches!(config.binary, BinaryBehavior::AsText))
&& (config.colored_output || config.strip_ansi == StripAnsiMode::Auto); && (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 // Determine the type of syntax for highlighting
const PLAIN_TEXT_SYNTAX: &str = "Plain Text"; 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) => ( Ok(syntax_in_set) => (
syntax_in_set.syntax.name == PLAIN_TEXT_SYNTAX, 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)), Some(HighlighterFromSet::new(syntax_in_set, theme)),
), ),
Err(Error::UndetectedSyntax(_)) => ( Err(Error::UndetectedSyntax(_)) => (
true, true,
false,
Some( Some(
assets assets
.find_syntax_by_name(PLAIN_TEXT_SYNTAX)? .find_syntax_by_name(PLAIN_TEXT_SYNTAX)?
@@ -285,7 +307,7 @@ impl<'a> InteractivePrinter<'a> {
Err(e) => return Err(e), Err(e) => return Err(e),
} }
} else { } else {
(false, None) (false, false, None)
}; };
// Determine when to strip ANSI sequences // Determine when to strip ANSI sequences
@@ -310,6 +332,7 @@ impl<'a> InteractivePrinter<'a> {
background_color_highlight, background_color_highlight,
consecutive_empty_lines: 0, consecutive_empty_lines: 0,
strip_ansi, strip_ansi,
strip_overstrike,
}) })
} }
@@ -448,6 +471,11 @@ impl Printer for InteractivePrinter<'_> {
input: &OpenedInput, input: &OpenedInput,
add_header_padding: bool, add_header_padding: bool,
) -> Result<()> { ) -> 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() { if add_header_padding && self.config.style_components.rule() {
self.print_horizontal_line_term(handle, self.colors.rule)?; self.print_horizontal_line_term(handle, self.colors.rule)?;
} }
@@ -463,7 +491,7 @@ impl Printer for InteractivePrinter<'_> {
(but will be present if the output of 'bat' is piped). You can use 'bat -A' \ (but will be present if the output of 'bat' is piped). You can use 'bat -A' \
to show the binary file contents.", to show the binary file contents.",
Yellow.paint("[bat warning]"), Yellow.paint("[bat warning]"),
input.description.summary(), sanitize_for_terminal(&input.description.summary()),
)?; )?;
} else if self.config.style_components.grid() { } else if self.config.style_components.grid() {
self.print_horizontal_line(handle, '┬')?; self.print_horizontal_line(handle, '┬')?;
@@ -517,9 +545,11 @@ impl Printer for InteractivePrinter<'_> {
"{}{}{mode}", "{}{}{mode}",
description description
.kind() .kind()
.map(|kind| format!("{kind}: ")) .map(|kind| format!("{}: ", sanitize_for_terminal(kind)))
.unwrap_or_else(|| "".into()), .unwrap_or_else(|| "".into()),
self.colors.header_value.paint(description.title()), self.colors
.header_value
.paint(sanitize_for_terminal(description.title())),
); );
self.print_header_multiline_component(handle, &header_filename) self.print_header_multiline_component(handle, &header_filename)
} }
@@ -550,6 +580,11 @@ impl Printer for InteractivePrinter<'_> {
} }
fn print_footer(&mut self, handle: &mut OutputHandle, _input: &OpenedInput) -> Result<()> { 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() if self.config.style_components.grid()
&& (self.content_type.is_some_and(|c| c.is_text()) && (self.content_type.is_some_and(|c| c.is_text())
|| self.config.show_nonprintable || self.config.show_nonprintable
@@ -622,6 +657,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 ANSI escape sequences are supposed to be stripped, do it before syntax highlighting.
if self.strip_ansi { if self.strip_ansi {
line = strip_ansi(&line).into() line = strip_ansi(&line).into()
@@ -759,11 +800,21 @@ impl Printer for InteractivePrinter<'_> {
// Displayed width of line_buf // Displayed width of line_buf
let mut current_width = 0; 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() { for c in text.chars() {
// calculate the displayed width for next character // calculate the displayed width for next character
let cw = c.width().unwrap_or(0); let cw = char_width(c);
current_width += cw; 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, // if next character cannot be printed on this line,
// flush the buffer. // flush the buffer.
if current_width > max_width { if current_width > max_width {
@@ -785,13 +836,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. // It wraps.
write!( write!(
handle, handle,
"{}{}\n{}", "{}{}\n{}",
as_terminal_escaped( as_terminal_escaped(
style, style,
&format!("{}{line_buf}", self.ansi_style), &format!(
"{}{}",
self.ansi_style,
&line_buf[..emit_end]
),
self.config.true_color, self.config.true_color,
self.config.colored_output, self.config.colored_output,
self.config.use_italic_text, self.config.use_italic_text,
@@ -804,8 +879,19 @@ impl Printer for InteractivePrinter<'_> {
cursor = 0; cursor = 0;
max_width = cursor_max; max_width = cursor_max;
line_buf.clear(); if let Some(rs) = rest_start {
current_width = cw; // 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); line_buf.push(c);
+54 -3
View File
@@ -17,9 +17,16 @@ use ignored_suffixes::IgnoredSuffixes;
mod builtin; mod builtin;
pub mod ignored_suffixes; 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) let matcher = GlobBuilder::new(from)
.case_insensitive(true) .case_insensitive(matches!(case, Case::Insensitive))
.literal_separator(true) .literal_separator(true)
.build()? .build()?
.compile_matcher(); .compile_matcher();
@@ -97,7 +104,14 @@ impl<'a> SyntaxMapping<'a> {
} }
pub fn insert(&mut self, from: &str, to: MappingTarget<'a>) -> Result<()> { 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)); self.custom_mappings.push((matcher, to));
Ok(()) Ok(())
} }
@@ -261,4 +275,41 @@ mod tests {
Some(MappingTarget::MapTo("alpha")) 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)
);
}
} }
+5 -5
View File
@@ -3,7 +3,7 @@ use std::env;
use globset::GlobMatcher; use globset::GlobMatcher;
use once_cell::sync::Lazy; 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 // Static syntax mappings generated from /src/syntax_mapping/builtins/ by the
// build script (/build/syntax_mapping.rs). // build script (/build/syntax_mapping.rs).
@@ -53,8 +53,8 @@ include!(concat!(
/// A failure to compile is a fatal error. /// A failure to compile is a fatal error.
/// ///
/// Used internally by `Lazy<Option<GlobMatcher>>`'s lazy evaluation closure. /// Used internally by `Lazy<Option<GlobMatcher>>`'s lazy evaluation closure.
fn build_matcher_fixed(from: &str) -> GlobMatcher { fn build_matcher_fixed(from: &str, case: Case) -> GlobMatcher {
make_glob_matcher(from).expect("A builtin fixed glob matcher failed to compile") 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 /// 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. /// to compile.
/// ///
/// Used internally by `Lazy<Option<GlobMatcher>>`'s lazy evaluation closure. /// 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 // join segments
let mut buf = String::new(); let mut buf = String::new();
for seg in segs { for seg in segs {
@@ -77,7 +77,7 @@ fn build_matcher_dynamic(segs: &[MatcherSegment]) -> Option<GlobMatcher> {
} }
} }
// compile glob matcher // compile glob matcher
let matcher = make_glob_matcher(&buf).ok()?; let matcher = make_glob_matcher(&buf, case).ok()?;
Some(matcher) Some(matcher)
} }
+12 -5
View File
@@ -20,12 +20,10 @@ syntax mappings defined by all TOML files, and embed them into the binary.
## File syntax ## File syntax
Each TOML file should contain a single section named `mappings`, with each of Each TOML file should contain a single section named `mappings`, with each of its keys being a language
its keys being a language identifier (first column of `bat -L`; also referred to identifier (first column of `bat -L`; also referred to as "target").
as "target").
The value of each key should be an array of strings, with each item being a glob 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.
matcher. We will call each of these items a "rule".
For example, if `foo-application` uses both TOML and YAML configuration files, For example, if `foo-application` uses both TOML and YAML configuration files,
we could write something like this: 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 ## Ordering
At compile time, all TOML files applicable to the target are processed in At compile time, all TOML files applicable to the target are processed in
@@ -0,0 +1,2 @@
[mappings]
"Python" = [{ glob = "BUILD", case_sensitive = true }]
@@ -1,2 +1,2 @@
[mappings] [mappings]
"XML" = ["*.csproj", "*.vbproj", "*.props", "*.targets"] "XML" = ["*.csproj", "*.vbproj", "*.props", "*.targets", "*.slnx"]
@@ -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` # see `man quadlet`
[mappings] [mappings]
"INI" = [ "INI" = [
"**/containers/systemd/**/*.{container,volume,network,kube,image}", "**/containers/systemd/**/*.{container,volume,network,kube,image,build,pod,artifact}",
] ]
+1 -1
View File
@@ -288,7 +288,7 @@ fn color_scheme_from_system() -> Option<ColorScheme> {
const PREFERENCES_FILE: &str = "Library/Preferences/.GlobalPreferences.plist"; const PREFERENCES_FILE: &str = "Library/Preferences/.GlobalPreferences.plist";
const STYLE_KEY: &str = "AppleInterfaceStyle"; const STYLE_KEY: &str = "AppleInterfaceStyle";
let preferences_file = home::home_dir() let preferences_file = std::env::home_dir()
.map(|home| home.join(PREFERENCES_FILE)) .map(|home| home.join(PREFERENCES_FILE))
.expect("Could not get home directory"); .expect("Could not get home directory");
+1
View File
@@ -1,6 +1,7 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WrappingMode { pub enum WrappingMode {
Character, Character,
Word,
// The bool specifies whether wrapping has been explicitly disabled by the user via --wrap=never // The bool specifies whether wrapping has been explicitly disabled by the user via --wrap=never
NoWrapping(bool), NoWrapping(bool),
} }
-1
View File
@@ -34,7 +34,6 @@ fn all_themes_are_present() {
"Solarized (light)", "Solarized (light)",
"Sublime Snazzy", "Sublime Snazzy",
"TwoDark", "TwoDark",
"Visual Studio Dark+",
"ansi", "ansi",
"base16", "base16",
"base16-256", "base16-256",
+30
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
BBold tteexxtt and _u_n_d_e_r_l_i_n_e
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/expect -f
set timeout 30
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env tclsh
puts "Hello from tclsh"
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/wish
button .b -text "Click"
+3
View File
@@ -0,0 +1,3 @@
The quick brown fox jumps over the lazy dog and then runs away
superlongwordthatdefinitelyexceedstheterminalwidthandshouldfallbacktocharacterwrapping
short words here
File diff suppressed because it is too large Load Diff
Vendored Executable → Regular
+12 -10
View File
@@ -4,6 +4,7 @@ import itertools
import subprocess import subprocess
import pathlib import pathlib
import shutil import shutil
from typing import Iterable
def generate_snapshots(): def generate_snapshots():
@@ -19,22 +20,23 @@ def generate_snapshots():
def generate_style_snapshot(style): def generate_style_snapshot(style):
generate_snapshot(style.replace(",", "_"), "--style={}".format(style)) generate_snapshot(style.replace(",", "_"), ["--style={}".format(style)])
def generate_snapshot(name, arguments): def generate_snapshot(name: str, arguments: Iterable[str]):
command = "cargo run -- --paging=never --color=never --decorations=always " output_file = "output/{name}.snapshot.txt".format(name=name)
command += "{args} sample.rs > output/{name}.snapshot.txt".format( command = [
name=name, "cargo", "run", "--", "--paging=never", "--color=never",
args=arguments "--decorations=always", *arguments, "sample.rs"
) ]
print("generating snapshot for {}".format(name)) print("generating snapshot for {}".format(name))
subprocess.call(command, shell=True) with open(output_file, "w") as f:
subprocess.call(command, stdout=f)
def build_bat(): def build_bat():
print("building bat") print("building bat")
subprocess.call("cargo build", cwd="../..", shell=True) subprocess.call(["cargo", "build"], cwd="../..")
def prepare_output_dir(): def prepare_output_dir():
@@ -49,7 +51,7 @@ def modify_sample_file():
def undo_sample_file_modification(): def undo_sample_file_modification():
print("undoing sample.rs modifications") print("undoing sample.rs modifications")
subprocess.call("git checkout -- sample.rs", shell=True) subprocess.call(["git", "checkout", "--", "sample.rs"])
build_bat() build_bat()
+2 -2
View File
@@ -1,5 +1,5 @@
with Chests.Ring_Buffers; with Chests.Ring_Buffers;
with USB.Device.HID.Keyboard; with USB.Device.HID.Keyboard;
package body Click is package body Click is
 ----------------  ----------------
+2 -2
View File
@@ -1,5 +1,5 @@
with HAL.GPIO; with HAL.GPIO;
with USB.Device.HID.Keyboard; with USB.Device.HID.Keyboard;
generic generic
 Nb_Bounce : Natural;  Nb_Bounce : Natural;
@@ -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
View File
@@ -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
View File
@@ -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 "----------------------------------".
+5 -5
View File
@@ -1,7 +1,7 @@
first,last,address,city,zip first,last,address,city,zip
John,Doe,120 any st.,"Anytown, WW",08123 John,Doe,120 any st.,"Anytown, WW",08123
a,b a,b
1,"ha  1,"ha 
""ha""  ""ha"" 
ha",120 any st.,"Anytown, WW",08123 ha",120 any st.,"Anytown, WW",08123
3,4,120 any st.,"Anytown, WW",08123 3,4,120 any st.,"Anytown, WW",08123
Can't render this file because it contains an unexpected character in line 2 and column 177.
+1 -1
View File
@@ -1,3 +1,3 @@
foo bar baz|;, test hello world tsv foo bar baz|;, test hello world tsv
1,2 1,7 2,7 a b c "hello again" tsv 1,2 1,7 2,7 a b c "hello again" tsv
";|," ;|, baz test "hello world" tsv ";|," ;|, baz test "hello world" tsv
Can't render this file because it contains an unexpected character in line 2 and column 218.
+52 -52
View File
@@ -1,85 +1,85 @@
import kotlin.math.* import kotlin.math.*
data class Example( data class Example(
 val name: String,  val name: String,
 val numbers: List<Int?>  val numbers: List<Int?>
) )
fun interface JokeInterface { fun interface JokeInterface {
 fun isFunny(): Boolean  fun isFunny(): Boolean
} }
abstract class AbstractJoke : JokeInterface { abstract class AbstractJoke : JokeInterface {
 override fun isFunny() = false  override fun isFunny() = false
 abstract fun content(): String  abstract fun content(): String
} }
class Joke : AbstractJoke() { class Joke : AbstractJoke() {
 override fun isFunny(): Boolean {  override fun isFunny(): Boolean {
 return true  return true
 }  }
 override fun content(): String = "content of joke here, haha"  override fun content(): String = "content of joke here, haha"
} }
class DelegatedJoke(val joke: Joke) : JokeInterface by joke { class DelegatedJoke(val joke: Joke) : JokeInterface by joke {
 val number: Long = 123L  val number: Long = 123L
 companion object {  companion object {
 const val someConstant = "some constant text"  const val someConstant = "some constant text"
 }  }
} }
object SomeSingleton object SomeSingleton
sealed class Shape { sealed class Shape {
 abstract fun area(): Double  abstract fun area(): Double
} }
data class Square(val sideLength: Double) : Shape() { data class Square(val sideLength: Double) : Shape() {
 override fun area(): Double = sideLength.pow(2)  override fun area(): Double = sideLength.pow(2)
} }
object Point : Shape() { object Point : Shape() {
 override fun area() = .0  override fun area() = .0
} }
class Circle(val radius: Double) : Shape() { class Circle(val radius: Double) : Shape() {
 override fun area(): Double {  override fun area(): Double {
 return PI * radius * radius  return PI * radius * radius
 }  }
} }
fun String.extensionMethod() = "test" fun String.extensionMethod() = "test"
fun main() { fun main() {
 val name = """  val name = """
 multiline  multiline
 string  string
   
 some numbers: 123123 42  some numbers: 123123 42
 """.trimIndent()  """.trimIndent()
 val example = Example(name = name, numbers = listOf(512, 42, null, -1))  val example = Example(name = name, numbers = listOf(512, 42, null, -1))
 example.numbers  example.numbers
 .filterNotNull()  .filterNotNull()
 .forEach { println(it) }  .forEach { println(it) }
 setOf(Joke(), DelegatedJoke(Joke()).joke)  setOf(Joke(), DelegatedJoke(Joke()).joke)
 .filter(JokeInterface::isFunny)  .filter(JokeInterface::isFunny)
 .map(AbstractJoke::content)  .map(AbstractJoke::content)
 .forEachIndexed { index: Int, joke ->  .forEachIndexed { index: Int, joke ->
 println("I heard a funny joke(#${index + 1}): $joke")  println("I heard a funny joke(#${index + 1}): $joke")
 }  }
 listOf(Square(12.3), Point, Circle(5.2))  listOf(Square(12.3), Point, Circle(5.2))
 .associateWith(Shape::area)  .associateWith(Shape::area)
 .toList()  .toList()
 .sortedBy { it.second }  .sortedBy { it.second }
 .forEach {  .forEach {
 println("${it.first}: ${it.second}")  println("${it.first}: ${it.second}")
 }  }
 println("some string".extensionMethod())  println("some string".extensionMethod())
 require(SomeSingleton::class.simpleName == "SomeSingletonName") { "something does not seem right..." }  require(SomeSingleton::class.simpleName == "SomeSingletonName") { "something does not seem right..." }
} }
+399
View File
@@ -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)
+7
View File
@@ -0,0 +1,7 @@
#!/usr/bin/expect -f
# Expect script detected via expect shebang
set timeout 30
spawn ssh user@host
expect "password:"
send "secret\r"
expect eof
+7
View File
@@ -0,0 +1,7 @@
#!/usr/bin/env tclsh
# Tcl script detected via tclsh shebang
puts "Hello from tclsh"
set x 42
if {$x > 0} {
 puts "positive"
}
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/wish
# Tk script detected via wish shebang
package require Tk
button .b -text "Click" -command {puts "clicked"}
pack .b
+7
View File
@@ -0,0 +1,7 @@
<Solution>
 <Folder Name="/Build/">
 <File Path="Directory.Build.props" />
 <File Path="projectname.targets" />
 </Folder>
 <Project Path="console.csproj" />
</Solution>
+19
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+7
View File
@@ -0,0 +1,7 @@
#!/usr/bin/expect -f
# Expect script detected via expect shebang
set timeout 30
spawn ssh user@host
expect "password:"
send "secret\r"
expect eof
+7
View File
@@ -0,0 +1,7 @@
#!/usr/bin/env tclsh
# Tcl script detected via tclsh shebang
puts "Hello from tclsh"
set x 42
if {$x > 0} {
puts "positive"
}
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/wish
# Tk script detected via wish shebang
package require Tk
button .b -text "Click" -command {puts "clicked"}
pack .b
+7
View File
@@ -0,0 +1,7 @@
<Solution>
<Folder Name="/Build/">
<File Path="Directory.Build.props" />
<File Path="projectname.targets" />
</Folder>
<Project Path="console.csproj" />
</Solution>
+1 -10
View File
@@ -53,16 +53,7 @@ impl Default for BatTester {
fn default() -> Self { fn default() -> Self {
let temp_dir = create_sample_directory().expect("sample directory"); let temp_dir = create_sample_directory().expect("sample directory");
let root = env::current_exe() let exe = assert_cmd::cargo::cargo_bin!("bat").to_path_buf();
.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);
BatTester { temp_dir, exe } BatTester { temp_dir, exe }
} }

Some files were not shown because too many files have changed in this diff Show More