mirror of
https://github.com/sharkdp/bat
synced 2026-08-04 19:01:44 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 410e284322 |
@@ -1,11 +0,0 @@
|
||||
# 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"]
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
name: Extract crate metadata
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v6
|
||||
- name: Extract crate information
|
||||
id: crate_metadata
|
||||
run: |
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt,clippy
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v6
|
||||
- run: cargo fmt -- --check
|
||||
- run: cargo clippy --locked --all-targets --all-features -- -D warnings
|
||||
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
needs: crate_metadata
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
- name: Install rust toolchain (v${{ needs.crate_metadata.outputs.msrv }})
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
@@ -80,7 +80,7 @@ jobs:
|
||||
name: License checks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: true # we especially want to perform license checks on submodules
|
||||
- run: tests/scripts/license-checks.sh
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Git checkout
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: true # we need all syntax and theme submodules
|
||||
- name: Install Rust toolchain
|
||||
@@ -119,7 +119,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Git checkout
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
- name: Prepare environment variables
|
||||
run: |
|
||||
echo "BAT_SYSTEM_CONFIG_PREFIX=$GITHUB_WORKSPACE/tests/examples/system_config" >> $GITHUB_ENV
|
||||
@@ -135,7 +135,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Git checkout
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
- name: Check documentation
|
||||
@@ -150,7 +150,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: cargo install cargo-audit --locked
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v6
|
||||
- run: cargo audit
|
||||
|
||||
build:
|
||||
@@ -178,7 +178,7 @@ jobs:
|
||||
BUILD_CMD: cargo
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install prerequisites
|
||||
shell: bash
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
PR_BASE: ${{ github.base_ref }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v6
|
||||
- name: Fetch PR base
|
||||
run: git fetch --no-tags --prune --depth=1 origin
|
||||
|
||||
|
||||
+3
-6
@@ -49,6 +49,9 @@
|
||||
[submodule "assets/themes/zenburn"]
|
||||
path = assets/themes/zenburn
|
||||
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"]
|
||||
path = assets/syntaxes/02_Extra/Elm
|
||||
url = https://github.com/elm-community/SublimeElmLanguageSupport
|
||||
@@ -278,9 +281,3 @@
|
||||
[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
|
||||
[submodule "assets/syntaxes/02_Extra/Caddy"]
|
||||
path = assets/syntaxes/02_Extra/Caddy
|
||||
url = https://github.com/caddyserver/sublimetext.git
|
||||
|
||||
@@ -4,33 +4,20 @@
|
||||
## Other
|
||||
|
||||
- Add instructions for removing fish help abbreviations to README, see #3655 (@claw-explorer). Closes #3536
|
||||
- Add .NET slnx extension, see #3682 (@ltrzesniewski)
|
||||
|
||||
## Features
|
||||
|
||||
- Map justfile, Justfile, .justfile, and *.justfile to Makefile syntax highlighting, see #3623 (@zachvalenta)
|
||||
- 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 `--ignored-suffix` not falling back to first-line/shebang detection when the ignored suffix is also a registered extension (e.g. `--ignored-suffix .txt` on a shebang script), see #2745 and #3816 (@adnrivera)
|
||||
- Fix `capacity overflow` panic when printing a snip separator at `--terminal-width=1` with multiple line ranges. Closes #3803, see #3804 (@leeewee)
|
||||
- Pass `--no-paging` to `bat` invocations inside the bash / zsh / fish / PowerShell shell completion scripts so that shell-level pager wiring (e.g. `LESSOPEN='|-bat -f -pp %s'`) cannot inject ANSI escape sequences into the completion candidates. Closes #3760 (@mvanhorn)
|
||||
- Quote filenames before substituting them into `$LESSOPEN` / `$LESSCLOSE` templates, preventing shell injection when a filename contains shell metacharacters, see #3726 (@curious-rabbit)
|
||||
- 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)
|
||||
@@ -45,33 +32,21 @@
|
||||
- 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)
|
||||
- Fix zsh tab completion offering invalid `-l` arguments (file globs, paths, hidden filenames) sourced from the second column of `--list-languages`. Closes #3735, see #3737 (@truffle-dev)
|
||||
- Fix `usize` underflow in `--list-languages` when `--terminal-width` is smaller than the longest language name, see #3812 (@greymoth-jp)
|
||||
|
||||
## 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)
|
||||
- Replace `libgit2` with a pure Rust implementation of git called `gitoxide`, see PR #3703 (@blinxen)
|
||||
|
||||
## 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)
|
||||
- Include subdirectories in SSH Config syntax mapping, see #3758 (@injust)
|
||||
- Add Ghostty syntax mapping, see #3759 (@injust)
|
||||
- Add syntax highlighting for `Caddyfile` #3789 (@CosmicHorrorDev)
|
||||
- Include `.code-workspace` as a JSON extension #3809 (@dhruvkb)
|
||||
- Add syntax mapping for DNF repo configuration files, see #3814 (@injust)
|
||||
|
||||
## Themes
|
||||
|
||||
|
||||
Generated
+655
-1569
File diff suppressed because it is too large
Load Diff
+18
-18
@@ -31,7 +31,8 @@ minimal-application = [
|
||||
"regex-onig",
|
||||
"wild",
|
||||
]
|
||||
git = ["gix"] # Support indicating git modifications
|
||||
git = ["git2"] # Support indicating git modifications
|
||||
vendored-libgit2 = ["git2/vendored-libgit2"]
|
||||
paging = [ "shell-words", "grep-cli", "minus"] # Support applying a pager on the output
|
||||
lessopen = ["execute"] # Support $LESSOPEN preprocessor
|
||||
build-assets = ["syntect/yaml-load", "syntect/plist-load", "regex", "walkdir"]
|
||||
@@ -51,11 +52,11 @@ thiserror = "2.0"
|
||||
wild = { version = "2.2", optional = true }
|
||||
content_inspector = "0.2.4"
|
||||
shell-words = { version = "1.1.1", optional = true }
|
||||
minus = { version = "5.7", optional = true, features = [
|
||||
minus = { version = "5.6", optional = true, features = [
|
||||
"dynamic_output",
|
||||
"search",
|
||||
] }
|
||||
unicode-width = "0.2.2"
|
||||
unicode-width = "0.2.1"
|
||||
globset = "0.4"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
@@ -63,23 +64,22 @@ serde_yaml = "0.9.28"
|
||||
semver = "1.0"
|
||||
path_abs = { version = "0.5", default-features = false }
|
||||
clircle = { version = "0.6.1", default-features = false }
|
||||
bugreport = { version = "0.6.0", optional = true }
|
||||
bugreport = { version = "0.5.0", optional = true }
|
||||
etcetera = { version = "0.11.0", optional = true }
|
||||
grep-cli = { version = "0.1.12", optional = true }
|
||||
regex = { version = "1.12.3", optional = true }
|
||||
regex = { version = "1.12.2", optional = true }
|
||||
walkdir = { version = "2.5", optional = true }
|
||||
bytesize = { version = "2.3.1" }
|
||||
encoding_rs = "0.8.35"
|
||||
execute = { version = "0.2.15", optional = true }
|
||||
terminal-colorsaurus = "1.0"
|
||||
unicode-segmentation = "1.13.2"
|
||||
unicode-segmentation = "1.12.0"
|
||||
itertools = "0.14.0"
|
||||
|
||||
[dependencies.gix]
|
||||
version = "0.85"
|
||||
[dependencies.git2]
|
||||
version = "0.20"
|
||||
optional = true
|
||||
default-features = false
|
||||
features = ["sha1", "blob-diff"]
|
||||
|
||||
[dependencies.syntect]
|
||||
version = "5.3.0"
|
||||
@@ -87,20 +87,20 @@ default-features = false
|
||||
features = ["parsing"]
|
||||
|
||||
[dependencies.clap]
|
||||
version = "4.6.1"
|
||||
version = "4.5.60"
|
||||
optional = true
|
||||
features = ["wrap_help", "cargo"]
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
plist = "1.9.0"
|
||||
plist = "1.7.0"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = "2.0.16"
|
||||
expect-test = "1.5.0"
|
||||
serial_test = { version = "2.0.0", default-features = false }
|
||||
predicates = "3.1.4"
|
||||
predicates = "3.1.3"
|
||||
wait-timeout = "0.2.1"
|
||||
tempfile = "3.27.0"
|
||||
tempfile = "3.23.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
[target.'cfg(unix)'.dev-dependencies]
|
||||
@@ -108,22 +108,22 @@ nix = { version = "0.31", default-features = false, features = ["term"] }
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1.0.97"
|
||||
indexmap = { version = "2.14.0", features = ["serde"] }
|
||||
indexmap = { version = "2.13.0", features = ["serde"] }
|
||||
itertools = "0.14.0"
|
||||
once_cell = "1.20"
|
||||
prettyplease = "0.2.37"
|
||||
proc-macro2 = "1.0.106"
|
||||
quote = "1.0.45"
|
||||
regex = "1.12.3"
|
||||
quote = "1.0.40"
|
||||
regex = "1.12.2"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_with = { version = "3.17.0", default-features = false, features = ["macros"] }
|
||||
syn = { version = "2.0.104", features = ["full"] }
|
||||
toml = { version = "1.1.1", features = ["preserve_order"] }
|
||||
toml = { version = "0.9.8", features = ["preserve_order"] }
|
||||
walkdir = "2.5"
|
||||
|
||||
[build-dependencies.clap]
|
||||
version = "4.6.1"
|
||||
version = "4.5.60"
|
||||
optional = true
|
||||
features = ["wrap_help", "cargo"]
|
||||
|
||||
|
||||
@@ -19,6 +19,20 @@
|
||||
[<a href="doc/README-ru.md">Русский</a>]
|
||||
</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>
|
||||
|
||||
### Syntax highlighting
|
||||
|
||||
`bat` supports syntax highlighting for a large number of programming and markup
|
||||
@@ -194,6 +208,12 @@ Note that the [Manpage syntax](assets/syntaxes/02_Extra/Manpage.sublime-syntax)
|
||||
|
||||
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
|
||||
|
||||
You can use `bat` to colorize help text: `$ cp --help | bat -plhelp`
|
||||
@@ -307,7 +327,7 @@ pacman -S bat
|
||||
|
||||
### On Fedora
|
||||
|
||||
You can install [the `bat` package](https://packages.fedoraproject.org/pkgs/rust-bat/bat/) from the official sources:
|
||||
You can install [the `bat` package](https://koji.fedoraproject.org/koji/packageinfo?packageID=27506) from the official [Fedora Modular](https://docs.fedoraproject.org/en-US/modularity/using-modules/) repository.
|
||||
|
||||
```bash
|
||||
dnf install bat
|
||||
@@ -507,30 +527,6 @@ 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
|
||||
[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]
|
||||
> 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.
|
||||
|
||||
Vendored
+2
-2
@@ -14,12 +14,12 @@ Register-ArgumentCompleter -Native -CommandName '{{PROJECT_EXECUTABLE}}' -Script
|
||||
$ArrayPrint = @('unicode', 'caret')
|
||||
|
||||
function Get-MyThemes(){
|
||||
$themes = {{PROJECT_EXECUTABLE}} --no-paging --list-themes | ForEach-Object {$_ -replace "^(.*)$", '''$1'''} | select-object
|
||||
$themes = {{PROJECT_EXECUTABLE}} --list-themes | ForEach-Object {$_ -replace "^(.*)$", '''$1'''} | select-object
|
||||
return $themes
|
||||
}
|
||||
|
||||
function Get-MyLanguages(){
|
||||
$themes = {{PROJECT_EXECUTABLE}} --no-paging --list-languages | ForEach-Object{[pscustomobject]@{MyParameter=$_.Substring(0,$_.IndexOf(":")).Trim();MyDescription=$_.Substring($_.IndexOf(":")+1)}} | select-object
|
||||
$themes = {{PROJECT_EXECUTABLE}} --list-languages | ForEach-Object{[pscustomobject]@{MyParameter=$_.Substring(0,$_.IndexOf(":")).Trim();MyDescription=$_.Substring($_.IndexOf(":")+1)}} | select-object
|
||||
return $themes
|
||||
}
|
||||
|
||||
|
||||
Vendored
+3
-3
@@ -80,7 +80,7 @@ _bat() {
|
||||
-l | --language)
|
||||
local IFS=$'\n'
|
||||
COMPREPLY=($(compgen -W "$(
|
||||
"$1" --no-paging --list-languages | while IFS=: read -r lang _; do
|
||||
"$1" --list-languages | while IFS=: read -r lang _; do
|
||||
printf "%s\n" "$lang"
|
||||
done
|
||||
)" -- "$cur"))
|
||||
@@ -150,14 +150,14 @@ _bat() {
|
||||
;;
|
||||
--theme)
|
||||
local IFS=$'\n'
|
||||
COMPREPLY=($(compgen -W "auto${IFS}auto:always${IFS}auto:system${IFS}dark${IFS}light${IFS}$("$1" --no-paging --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
|
||||
return 0
|
||||
;;
|
||||
--theme-dark | \
|
||||
--theme-light)
|
||||
local IFS=$'\n'
|
||||
COMPREPLY=($(compgen -W "$("$1" --no-paging --list-themes)" -- "$cur"))
|
||||
COMPREPLY=($(compgen -W "$("$1" --list-themes)" -- "$cur"))
|
||||
__bat_escape_completions
|
||||
return 0
|
||||
;;
|
||||
|
||||
Vendored
+5
-5
@@ -15,11 +15,11 @@ function __bat_complete_files -a token
|
||||
end
|
||||
|
||||
function __bat_complete_one_language -a comp
|
||||
command $bat --no-paging --list-languages | string split -f1 : | string match -e "$comp"
|
||||
command $bat --list-languages | string split -f1 : | string match -e "$comp"
|
||||
end
|
||||
|
||||
function __bat_complete_list_languages
|
||||
for spec in (command $bat --no-paging --list-languages)
|
||||
for spec in (command $bat --list-languages)
|
||||
set -l name (string split -f1 : $spec)
|
||||
for ext in (string split -f2 : $spec | string split ,)
|
||||
test -n "$ext"; or continue
|
||||
@@ -234,11 +234,11 @@ complete -c $bat -l tabs -x -a "$tabs_opts" -d "Set tab width" -n __bat_no_excl_
|
||||
|
||||
complete -c $bat -l terminal-width -x -d "Set terminal <width>, +<offset>, or -<offset>" -n __bat_no_excl_args
|
||||
|
||||
complete -c $bat -l theme -x -a "$special_themes(command $bat --no-paging --list-themes | command cat)" -d "Set the syntax highlighting theme" -n __bat_no_excl_args
|
||||
complete -c $bat -l theme -x -a "$special_themes(command $bat --list-themes | command cat)" -d "Set the syntax highlighting theme" -n __bat_no_excl_args
|
||||
|
||||
complete -c $bat -l theme-dark -x -a "(command $bat --no-paging --list-themes | command cat)" -d "Set the syntax highlighting theme for dark backgrounds" -n __bat_no_excl_args
|
||||
complete -c $bat -l theme-dark -x -a "(command $bat --list-themes | command cat)" -d "Set the syntax highlighting theme for dark backgrounds" -n __bat_no_excl_args
|
||||
|
||||
complete -c $bat -l theme-light -x -a "(command $bat --no-paging --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 "Enable unbuffered input reading for streaming use cases" -n __bat_no_excl_args
|
||||
|
||||
|
||||
Vendored
+3
-8
@@ -90,25 +90,20 @@ _{{PROJECT_EXECUTABLE}}_main() {
|
||||
languages)
|
||||
local IFS=$'\n'
|
||||
local -a languages
|
||||
# `--list-languages` emits one `name:matchers` line per language,
|
||||
# which `_describe` parses as `value:description`. Only the
|
||||
# language name is offered as the completion value; the matchers
|
||||
# show up as the menu description. See
|
||||
# https://github.com/sharkdp/bat/issues/3735.
|
||||
languages=( ${(f)"$({{PROJECT_EXECUTABLE}} --no-paging --color=never --decorations=never --list-languages)"} )
|
||||
languages=( $({{PROJECT_EXECUTABLE}} --list-languages | awk -F':|,' '{ for (i = 1; i <= NF; ++i) printf("%s:%s\n", $i, $1) }') )
|
||||
|
||||
_describe 'language' languages && ret=0
|
||||
;;
|
||||
|
||||
themes)
|
||||
local -a themes expl
|
||||
themes=(${(f)"$(_call_program themes {{PROJECT_EXECUTABLE}} --no-paging --color=never --decorations=never --list-themes)"} )
|
||||
themes=(${(f)"$(_call_program themes {{PROJECT_EXECUTABLE}} --list-themes)"} )
|
||||
|
||||
_wanted themes expl 'theme' compadd -a themes && ret=0
|
||||
;;
|
||||
theme_preferences)
|
||||
local -a themes expl
|
||||
themes=(auto dark light auto:always auto:system ${(f)"$(_call_program themes {{PROJECT_EXECUTABLE}} --no-paging --color=never --decorations=never --list-themes)"} )
|
||||
themes=(auto dark light auto:always auto:system ${(f)"$(_call_program themes {{PROJECT_EXECUTABLE}} --list-themes)"} )
|
||||
|
||||
_wanted themes expl 'theme' compadd -a themes && ret=0
|
||||
;;
|
||||
|
||||
-9
@@ -2,15 +2,6 @@ diff --git syntaxes/01_Packages/Python/Python.sublime-syntax syntaxes/01_Package
|
||||
index 2acd86d8..86257f7b 100644
|
||||
--- 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:
|
||||
- match: \}
|
||||
scope: punctuation.section.mapping-or-set.end.python
|
||||
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
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:
|
||||
Vendored
-1
Submodule assets/syntaxes/02_Extra/Caddy deleted from cd7d132da3
Vendored
+1
-1
Submodule assets/syntaxes/02_Extra/Idris2 updated: 4d8eb35a38...bbfe50e023
Vendored
+1
-1
Submodule assets/syntaxes/02_Extra/Kotlin updated: c353694169...aeeed2780b
+398
@@ -0,0 +1,398 @@
|
||||
%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
|
||||
Vendored
+1
-1
Submodule assets/syntaxes/02_Extra/PureScript updated: 1773f4fddb...5acebc1850
+1
-1
Submodule assets/syntaxes/02_Extra/typst-syntax-highlight updated: 5f71d12fa1...363f0e767c
Vendored
+1
-1
Submodule assets/syntaxes/02_Extra/vscode-wgsl updated: a285c38f74...acf26718d7
+1
-1
@@ -97,7 +97,7 @@ The following files have been manually modified after converting from a `.tmLang
|
||||
as it is not kept in a standalone repository. The file is generated from
|
||||
https://github.com/open-policy-agent/opa/blob/master/misc/syntax/textmate/Rego.tmLanguage
|
||||
* `SML.sublime_syntax` has been added manually from
|
||||
https://github.com/seanjames777/SML-Language-Definition as it is not
|
||||
https://github.com/seanjames777/SML-Language-Definitiona as it is not
|
||||
kept in a standalone repository. The file generated is from
|
||||
https://github.com/seanjames777/SML-Language-Definition/blob/master/sml.tmLanguage
|
||||
* `Cabal.sublime_syntax` has been added manually from
|
||||
|
||||
+1
-2
@@ -77,8 +77,7 @@ Options:
|
||||
--terminal-width <width>
|
||||
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. This can also be configured via the BAT_WIDTH environment variable (e.g. export
|
||||
BAT_WIDTH="100"). See also: '--wrap'.
|
||||
width. See also: '--wrap'.
|
||||
|
||||
-n, --number
|
||||
Only show line numbers, no other decorations. This is an alias for '--style=numbers'
|
||||
|
||||
@@ -10,3 +10,5 @@ No issue will have a different priority based on sponsorship status of the
|
||||
reporter.
|
||||
|
||||
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.
|
After Width: | Height: | Size: 28 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
+13
-61
@@ -158,9 +158,7 @@ impl HighlightingAssets {
|
||||
let syntax_match = mapping.get_syntax_for(path);
|
||||
|
||||
if let Some(MappingTarget::MapToUnknown) = syntax_match {
|
||||
return Err(Error::UndetectedSyntax(
|
||||
crate::preprocessor::sanitize_for_terminal(&path.to_string_lossy()),
|
||||
));
|
||||
return Err(Error::UndetectedSyntax(path.to_string_lossy().into()));
|
||||
}
|
||||
|
||||
if let Some(MappingTarget::MapTo(syntax_name)) = syntax_match {
|
||||
@@ -177,17 +175,13 @@ impl HighlightingAssets {
|
||||
) {
|
||||
(Some(syntax), _) => Ok(syntax),
|
||||
|
||||
(_, Some(MappingTarget::MapExtensionToUnknown)) => Err(Error::UndetectedSyntax(
|
||||
crate::preprocessor::sanitize_for_terminal(&path.to_string_lossy()),
|
||||
)),
|
||||
(_, Some(MappingTarget::MapExtensionToUnknown)) => {
|
||||
Err(Error::UndetectedSyntax(path.to_string_lossy().into()))
|
||||
}
|
||||
|
||||
_ => self
|
||||
.get_syntax_for_file_extension(file_name, &mapping.ignored_suffixes)?
|
||||
.ok_or_else(|| {
|
||||
Error::UndetectedSyntax(crate::preprocessor::sanitize_for_terminal(
|
||||
&path.to_string_lossy(),
|
||||
))
|
||||
}),
|
||||
.ok_or_else(|| Error::UndetectedSyntax(path.to_string_lossy().into())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,15 +341,15 @@ impl HighlightingAssets {
|
||||
file_name: &OsStr,
|
||||
ignored_suffixes: &IgnoredSuffixes,
|
||||
) -> Result<Option<SyntaxReferenceInSet<'_>>> {
|
||||
let stripped =
|
||||
ignored_suffixes.try_with_stripped_suffix(file_name, |stripped_file_name| {
|
||||
self.get_syntax_for_file_extension(stripped_file_name, ignored_suffixes)
|
||||
.map(Some)
|
||||
})?;
|
||||
match stripped {
|
||||
Some(syntax) => Ok(syntax),
|
||||
None => self.find_syntax_by_extension(Path::new(file_name).extension()),
|
||||
let mut syntax = self.find_syntax_by_extension(Path::new(file_name).extension())?;
|
||||
if syntax.is_none() {
|
||||
syntax =
|
||||
ignored_suffixes.try_with_stripped_suffix(file_name, |stripped_file_name| {
|
||||
// Note: recursion
|
||||
self.get_syntax_for_file_extension(stripped_file_name, ignored_suffixes)
|
||||
})?;
|
||||
}
|
||||
Ok(syntax)
|
||||
}
|
||||
|
||||
fn get_first_line_syntax(
|
||||
@@ -686,48 +680,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn syntax_detection_ignored_suffix_falls_back_to_first_line() {
|
||||
let mut test = SyntaxDetectionTest::new();
|
||||
|
||||
// By default a `.txt` file uses Plain Text, even with a shebang: the
|
||||
// `.txt` extension wins and first-line detection is not reached.
|
||||
assert_eq!(
|
||||
test.syntax_for_file_with_content("test.txt", "#!/usr/bin/env bash"),
|
||||
"Plain Text"
|
||||
);
|
||||
|
||||
// Once `.txt` is an ignored suffix it is stripped before detection. The
|
||||
// stripped name (`test`) has no extension, so detection falls back to the
|
||||
// first line -- even though `.txt` is itself a registered extension that
|
||||
// would otherwise match as Plain Text. See #2745.
|
||||
test.syntax_mapping.insert_ignored_suffix(".txt");
|
||||
assert_eq!(
|
||||
test.syntax_for_file_with_content("test.txt", "#!/usr/bin/env bash"),
|
||||
"Bourne Again Shell (bash)"
|
||||
);
|
||||
assert_eq!(
|
||||
test.syntax_for_file_with_content("test.txt", "<?php"),
|
||||
"PHP"
|
||||
);
|
||||
|
||||
// A `.txt` file without a recognizable first line keeps no syntax (the
|
||||
// caller renders it as plain text); we must not resurrect the shadowed
|
||||
// `.txt` -> Plain Text match here.
|
||||
assert_eq!(
|
||||
test.syntax_for_file_with_content("notes.txt", "just some prose"),
|
||||
"!no syntax!"
|
||||
);
|
||||
|
||||
// Stripping that exposes a real extension still works: `.dev` is ignored,
|
||||
// and the remaining `.json` extension is detected as usual.
|
||||
test.syntax_mapping.insert_ignored_suffix(".dev");
|
||||
assert_eq!(
|
||||
test.syntax_for_file_with_content("config.json.dev", ""),
|
||||
"JSON"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn syntax_detection_is_case_insensitive() {
|
||||
let mut test = SyntaxDetectionTest::new();
|
||||
|
||||
+28
-2
@@ -50,6 +50,11 @@ pub struct App {
|
||||
/// (not from config file or environment variables).
|
||||
/// This is used to honor the flag when piping output, similar to `cat -n`.
|
||||
number_from_cli: bool,
|
||||
/// True if --wrap=character was passed on the command line
|
||||
/// (not from config file or environment variables).
|
||||
/// When piping output (non-interactive), --wrap=character is ignored unless
|
||||
/// it was explicitly provided on the command line.
|
||||
wrap_character_from_cli: bool,
|
||||
}
|
||||
|
||||
impl App {
|
||||
@@ -90,6 +95,18 @@ impl App {
|
||||
false
|
||||
});
|
||||
|
||||
// Check if --wrap=character was passed on the command line
|
||||
// (before merging with config file and environment variables).
|
||||
// This is needed to honor --wrap=character when piping output, while
|
||||
// ignoring it when it comes only from the config file or BAT_OPTS.
|
||||
let cli_args_vec: Vec<_> = wild::args_os().collect();
|
||||
let wrap_character_from_cli = cli_args_vec
|
||||
.iter()
|
||||
.any(|arg| arg.to_string_lossy() == "--wrap=character")
|
||||
|| cli_args_vec.windows(2).any(|pair| {
|
||||
pair[0].to_string_lossy() == "--wrap" && pair[1].to_string_lossy() == "character"
|
||||
});
|
||||
|
||||
let matches = Self::matches(interactive_output)?;
|
||||
|
||||
if matches.get_flag("help") {
|
||||
@@ -130,6 +147,7 @@ impl App {
|
||||
matches,
|
||||
interactive_output,
|
||||
number_from_cli,
|
||||
wrap_character_from_cli,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -408,7 +426,15 @@ impl App {
|
||||
WrappingMode::NoWrapping(true)
|
||||
} else {
|
||||
match self.matches.get_one::<String>("wrap").map(|s| s.as_str()) {
|
||||
Some("character") => WrappingMode::Character,
|
||||
Some("character") => {
|
||||
if !self.interactive_output && !self.wrap_character_from_cli {
|
||||
// When piping output (non-interactive), ignore --wrap=character
|
||||
// unless it was explicitly provided on the command line.
|
||||
WrappingMode::NoWrapping(false)
|
||||
} else {
|
||||
WrappingMode::Character
|
||||
}
|
||||
}
|
||||
Some("word") => WrappingMode::Word,
|
||||
Some("never") => WrappingMode::NoWrapping(true),
|
||||
Some("auto") | None => {
|
||||
@@ -645,7 +671,7 @@ impl App {
|
||||
Ok(styled_components)
|
||||
}
|
||||
|
||||
pub(crate) fn theme_options(&self) -> ThemeOptions {
|
||||
fn theme_options(&self) -> ThemeOptions {
|
||||
Self::theme_options_from_matches(&self.matches)
|
||||
}
|
||||
|
||||
|
||||
@@ -240,7 +240,6 @@ pub fn build_app(interactive_output: bool) -> Command {
|
||||
.arg(
|
||||
Arg::new("terminal-width")
|
||||
.long("terminal-width")
|
||||
.overrides_with("terminal-width")
|
||||
.value_name("width")
|
||||
.hide_short_help(true)
|
||||
.allow_hyphen_values(true)
|
||||
@@ -256,13 +255,10 @@ pub fn build_app(interactive_output: bool) -> Command {
|
||||
})
|
||||
.map_err(|e| e.to_string())
|
||||
})
|
||||
.help("Explicitly set the width of the terminal instead of determining it automatically.")
|
||||
.long_help(
|
||||
.help(
|
||||
"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. This can also be configured \
|
||||
via the BAT_WIDTH environment variable (e.g. export BAT_WIDTH=\"100\"). \
|
||||
See also: '--wrap'.",
|
||||
as an offset to the actual terminal width. See also: '--wrap'.",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
|
||||
@@ -153,7 +153,6 @@ fn get_args_from_str(content: &str) -> Result<Vec<OsString>, shell_words::ParseE
|
||||
pub fn get_args_from_env_vars() -> Vec<OsString> {
|
||||
[
|
||||
("--tabs", "BAT_TABS"),
|
||||
("--terminal-width", "BAT_WIDTH"),
|
||||
("--theme", bat::theme::env::BAT_THEME),
|
||||
("--theme-dark", bat::theme::env::BAT_THEME_DARK),
|
||||
("--theme-light", bat::theme::env::BAT_THEME_LIGHT),
|
||||
|
||||
+8
-41
@@ -17,6 +17,7 @@ use std::path::Path;
|
||||
use std::process;
|
||||
|
||||
use bat::output::{OutputHandle, OutputType};
|
||||
use bat::theme::DetectColorScheme;
|
||||
use nu_ansi_term::Color::Green;
|
||||
use nu_ansi_term::Style;
|
||||
|
||||
@@ -38,7 +39,7 @@ use bat::{
|
||||
error::*,
|
||||
input::Input,
|
||||
style::{StyleComponent, StyleComponents},
|
||||
theme::{default_theme, theme, ColorScheme, ThemeOptions},
|
||||
theme::{color_scheme, default_theme, ColorScheme},
|
||||
MappingTarget, PagingMode,
|
||||
};
|
||||
|
||||
@@ -152,14 +153,7 @@ pub fn get_languages(config: &Config, cache_dir: &Path) -> Result<String> {
|
||||
let comma_separator = ", ";
|
||||
let separator = " ";
|
||||
// Line-wrapping for the possible file extension overflow.
|
||||
// Clamp instead of subtracting: a tiny `--terminal-width` (smaller than the
|
||||
// longest language name) would otherwise underflow `usize` and wrap to a huge
|
||||
// value, silently disabling wrapping (and panicking in debug/overflow-checked
|
||||
// builds). Mirrors the `saturating_sub` fix applied to `print_snip` in #3804.
|
||||
let desired_width = config
|
||||
.term_width
|
||||
.saturating_sub(longest)
|
||||
.saturating_sub(separator.len());
|
||||
let desired_width = config.term_width - longest - separator.len();
|
||||
|
||||
let style = if config.colored_output {
|
||||
Green.normal()
|
||||
@@ -203,7 +197,7 @@ pub fn list_themes(
|
||||
cfg: &Config,
|
||||
config_dir: &Path,
|
||||
cache_dir: &Path,
|
||||
theme_options: ThemeOptions,
|
||||
detect_color_scheme: DetectColorScheme,
|
||||
) -> Result<()> {
|
||||
let assets = assets_from_cache_or_binary(cfg.use_custom_assets, cache_dir)?;
|
||||
let mut config = cfg.clone();
|
||||
@@ -212,7 +206,7 @@ pub fn list_themes(
|
||||
config.language = Some("Rust");
|
||||
config.style_components = StyleComponents(style);
|
||||
|
||||
let default_theme_name = theme(theme_options).to_string();
|
||||
let default_theme_name = default_theme(color_scheme(detect_color_scheme).unwrap_or_default());
|
||||
let mut buf = String::new();
|
||||
let mut handle = OutputHandle::FmtWrite(&mut buf);
|
||||
|
||||
@@ -265,9 +259,7 @@ pub fn list_themes(
|
||||
fn set_terminal_title_to(new_terminal_title: String) {
|
||||
let osc_command_for_setting_terminal_title = "\x1b]0;";
|
||||
let osc_end_command = "\x07";
|
||||
// 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}");
|
||||
print!("{osc_command_for_setting_terminal_title}{new_terminal_title}{osc_end_command}");
|
||||
io::stdout().flush().unwrap();
|
||||
}
|
||||
|
||||
@@ -293,28 +285,7 @@ fn run_controller(inputs: Vec<Input>, config: &Config, cache_dir: &Path) -> Resu
|
||||
|
||||
#[cfg(feature = "bugreport")]
|
||||
fn invoke_bugreport(app: &App, cache_dir: &Path) {
|
||||
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()))
|
||||
}
|
||||
}
|
||||
use bugreport::{bugreport, collector::*, format::Markdown};
|
||||
let pager = bat::config::get_pager_executable(
|
||||
app.matches.get_one::<String>("pager").map(|s| s.as_str()),
|
||||
)
|
||||
@@ -336,9 +307,6 @@ fn invoke_bugreport(app: &App, cache_dir: &Path) {
|
||||
"BAT_STYLE",
|
||||
"BAT_TABS",
|
||||
"BAT_THEME",
|
||||
"BAT_WIDTH",
|
||||
bat::theme::env::BAT_THEME_DARK,
|
||||
bat::theme::env::BAT_THEME_LIGHT,
|
||||
"COLORTERM",
|
||||
"LANG",
|
||||
"LC_ALL",
|
||||
@@ -358,7 +326,6 @@ fn invoke_bugreport(app: &App, cache_dir: &Path) {
|
||||
custom_assets_metadata,
|
||||
))
|
||||
.info(DirectoryEntries::new("Custom assets", cache_dir))
|
||||
.info(ColorSchemeCollector)
|
||||
.info(CompileTimeInformation::default());
|
||||
|
||||
#[cfg(feature = "paging")]
|
||||
@@ -434,7 +401,7 @@ fn run() -> Result<bool> {
|
||||
};
|
||||
run_controller(inputs, &plain_config, cache_dir)
|
||||
} else if app.matches.get_flag("list-themes") {
|
||||
list_themes(&config, config_dir, cache_dir, app.theme_options())?;
|
||||
list_themes(&config, config_dir, cache_dir, DetectColorScheme::default())?;
|
||||
Ok(true)
|
||||
} else if app.matches.get_flag("config-file") {
|
||||
println!("{}", config_file().to_string_lossy());
|
||||
|
||||
+64
-178
@@ -1,13 +1,12 @@
|
||||
#![cfg(feature = "git")]
|
||||
|
||||
use gix::diff::blob::pipeline::{Mode, WorktreeRoots};
|
||||
use gix::diff::blob::{Algorithm, HunkIter, ResourceKind};
|
||||
use gix::object::tree::EntryKind;
|
||||
use path_abs::PathInfo;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
use git2::{DiffOptions, IntoCString, Repository};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum LineChange {
|
||||
Added,
|
||||
RemovedAbove,
|
||||
@@ -17,181 +16,68 @@ pub enum LineChange {
|
||||
|
||||
pub type LineChanges = HashMap<u32, LineChange>;
|
||||
|
||||
fn collect_changes_from_hunks(hunks: HunkIter) -> Option<LineChanges> {
|
||||
let mut changes: LineChanges = HashMap::new();
|
||||
for hunk in hunks {
|
||||
if hunk.before.is_empty() && !hunk.after.is_empty() {
|
||||
for line in hunk.after {
|
||||
changes.insert(line + 1, LineChange::Added);
|
||||
}
|
||||
} else if hunk.after.is_empty() && !hunk.before.is_empty() {
|
||||
if hunk.after.start == 0 {
|
||||
changes.insert(1, LineChange::RemovedAbove);
|
||||
} else {
|
||||
changes.insert(hunk.after.start, LineChange::RemovedBelow);
|
||||
}
|
||||
} else {
|
||||
for line in hunk.after {
|
||||
changes.insert(line + 1, LineChange::Modified);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(changes)
|
||||
}
|
||||
|
||||
pub fn get_git_diff(filename: &Path) -> Option<LineChanges> {
|
||||
let filepath_absolute = filename.canonicalize().ok()?;
|
||||
let repository = gix::discover(filepath_absolute.parent().ok()?).ok()?;
|
||||
let repo_path_absolute = repository.workdir()?.canonicalize().ok()?;
|
||||
let filepath_relative_to_repo = gix::path::to_unix_separators_on_windows(gix::path::into_bstr(
|
||||
filepath_absolute.strip_prefix(&repo_path_absolute).ok()?,
|
||||
));
|
||||
let index = repository.index_or_load_from_head_or_empty().ok()?;
|
||||
let index_entry = index.entry_by_path(filepath_relative_to_repo.as_ref())?;
|
||||
let mut cache = repository
|
||||
.diff_resource_cache(
|
||||
Mode::ToGit,
|
||||
WorktreeRoots {
|
||||
old_root: None,
|
||||
new_root: repository.workdir().map(Path::to_path_buf),
|
||||
},
|
||||
)
|
||||
let repo = Repository::discover(filename).ok()?;
|
||||
|
||||
let repo_path_absolute = fs::canonicalize(repo.workdir()?).ok()?;
|
||||
|
||||
let filepath_absolute = fs::canonicalize(filename).ok()?;
|
||||
let filepath_relative_to_repo = filepath_absolute.strip_prefix(&repo_path_absolute).ok()?;
|
||||
|
||||
let mut diff_options = DiffOptions::new();
|
||||
let pathspec = filepath_relative_to_repo.into_c_string().ok()?;
|
||||
diff_options.pathspec(pathspec);
|
||||
diff_options.context_lines(0);
|
||||
|
||||
let diff = repo
|
||||
.diff_index_to_workdir(None, Some(&mut diff_options))
|
||||
.ok()?;
|
||||
cache
|
||||
.set_resource(
|
||||
index_entry.id,
|
||||
index_entry.mode.to_tree_entry_mode()?.kind(),
|
||||
filepath_relative_to_repo.as_ref(),
|
||||
ResourceKind::OldOrSource,
|
||||
&repository,
|
||||
)
|
||||
.ok()?;
|
||||
cache
|
||||
.set_resource(
|
||||
repository.object_hash().null(),
|
||||
EntryKind::Blob,
|
||||
filepath_relative_to_repo.as_ref(),
|
||||
ResourceKind::NewOrDestination,
|
||||
&repository,
|
||||
)
|
||||
.ok()?;
|
||||
let diff = gix::diff::blob::diff_with_slider_heuristics(
|
||||
Algorithm::Histogram,
|
||||
&cache.prepare_diff().ok()?.interned_input(),
|
||||
|
||||
let mut line_changes: LineChanges = HashMap::new();
|
||||
|
||||
let mark_section =
|
||||
|line_changes: &mut LineChanges, start: u32, end: i32, change: LineChange| {
|
||||
for line in start..=end as u32 {
|
||||
line_changes.insert(line, change);
|
||||
}
|
||||
};
|
||||
|
||||
let _ = diff.foreach(
|
||||
&mut |_, _| true,
|
||||
None,
|
||||
Some(&mut |delta, hunk| {
|
||||
let path = delta.new_file().path().unwrap_or_else(|| Path::new(""));
|
||||
|
||||
if filepath_relative_to_repo != path {
|
||||
return false;
|
||||
}
|
||||
|
||||
let old_lines = hunk.old_lines();
|
||||
let new_start = hunk.new_start();
|
||||
let new_lines = hunk.new_lines();
|
||||
let new_end = (new_start + new_lines) as i32 - 1;
|
||||
|
||||
if old_lines == 0 && new_lines > 0 {
|
||||
mark_section(&mut line_changes, new_start, new_end, LineChange::Added);
|
||||
} else if new_lines == 0 && old_lines > 0 {
|
||||
if new_start == 0 {
|
||||
mark_section(&mut line_changes, 1, 1, LineChange::RemovedAbove);
|
||||
} else {
|
||||
mark_section(
|
||||
&mut line_changes,
|
||||
new_start,
|
||||
new_start as i32,
|
||||
LineChange::RemovedBelow,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
mark_section(&mut line_changes, new_start, new_end, LineChange::Modified);
|
||||
}
|
||||
|
||||
true
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
collect_changes_from_hunks(diff.hunks())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{get_git_diff, LineChange};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
fn git(repo: &Path, args: &[&str]) {
|
||||
let output = Command::new("git")
|
||||
.args(args)
|
||||
.current_dir(repo)
|
||||
.output()
|
||||
.expect("git command can run");
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"git {args:?} failed\nstdout: {}\nstderr: {}",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
|
||||
fn setup_repo() -> tempfile::TempDir {
|
||||
let dir = tempfile::tempdir().expect("can create temporary directory");
|
||||
let repo = dir.path();
|
||||
git(repo, &["init"]);
|
||||
git(repo, &["config", "user.email", "test@test.test"]);
|
||||
git(repo, &["config", "user.name", "Test"]);
|
||||
dir
|
||||
}
|
||||
|
||||
fn create_and_track_file(repo: &tempfile::TempDir, filename: &str) -> PathBuf {
|
||||
let filepath = repo.path().join(filename);
|
||||
std::fs::write(&filepath, "file\n").expect("can write file");
|
||||
git(repo.path(), &["add", filename]);
|
||||
git(repo.path(), &["commit", "-m", "initial"]);
|
||||
|
||||
filepath.to_owned()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_a_git_repository() {
|
||||
let dir = tempfile::tempdir().expect("can create temporary directory");
|
||||
let file = dir.path().join("file.txt");
|
||||
std::fs::write(&file, "line 1\n").expect("can write file");
|
||||
|
||||
assert_eq!(get_git_diff(&file), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_is_calculated_against_index() {
|
||||
let repo = setup_repo();
|
||||
let file = create_and_track_file(&repo, "file.txt");
|
||||
|
||||
std::fs::write(&file, "line 1\nline 2 modified\n").expect("can write file");
|
||||
// Add modified file to index -> should find 0 changes but not none
|
||||
git(repo.path(), &["add", "file.txt"]);
|
||||
|
||||
assert_eq!(get_git_diff(&file).expect("empty map").len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_is_calculated_against_index_2() {
|
||||
let repo = setup_repo();
|
||||
let file = create_and_track_file(&repo, "file.txt");
|
||||
|
||||
std::fs::write(&file, "line 1\nline 2 modified\n").expect("can write file");
|
||||
git(repo.path(), &["add", "file.txt"]);
|
||||
// modify the second line again which should show a single change
|
||||
std::fs::write(&file, "line 1\nline 2 modified again\n").expect("can write file");
|
||||
|
||||
assert_eq!(get_git_diff(&file).expect("one change").len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_is_calculated_correctly() {
|
||||
let repo = setup_repo();
|
||||
create_and_track_file(&repo, "committed.txt");
|
||||
let filename = "committed2.txt";
|
||||
let file = create_and_track_file(&repo, filename);
|
||||
|
||||
std::fs::write(&file, "file\nline 2 added\n").expect("can write file");
|
||||
let mut changes = get_git_diff(&file).expect("multiple changes");
|
||||
assert_eq!(changes.get(&2), Some(&LineChange::Added));
|
||||
git(repo.path(), &["add", filename]);
|
||||
|
||||
std::fs::write(&file, "file\nline 2 modified\n").expect("can write file");
|
||||
changes = get_git_diff(&file).expect("multiple changes");
|
||||
assert_eq!(changes.get(&2), Some(&LineChange::Modified));
|
||||
git(repo.path(), &["add", filename]);
|
||||
|
||||
std::fs::write(&file, "line 2 modified\nline 3 added").expect("can write file");
|
||||
changes = get_git_diff(&file).expect("multiple changes");
|
||||
assert_eq!(changes.get(&1), Some(&LineChange::RemovedAbove));
|
||||
assert_eq!(changes.get(&2), Some(&LineChange::Added));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn faulty_git_version_does_not_panic() {
|
||||
let repo = setup_repo();
|
||||
let file = create_and_track_file(&repo, "file.txt");
|
||||
std::fs::write(&file, "line 1\nline 2 modified\n").expect("can write file");
|
||||
// changes are detected
|
||||
assert_eq!(get_git_diff(&file).expect("one change").len(), 2);
|
||||
// write invalid repositoryformatversion
|
||||
git(
|
||||
repo.path(),
|
||||
&["config", "core.repositoryformatversion", "one"],
|
||||
);
|
||||
// changes are no longer detected
|
||||
assert_eq!(get_git_diff(&file), None);
|
||||
}
|
||||
Some(line_changes)
|
||||
}
|
||||
|
||||
+22
-79
@@ -207,7 +207,7 @@ impl<'a> Input<'a> {
|
||||
kind: OpenedInputKind::StdIn,
|
||||
description,
|
||||
metadata: self.metadata,
|
||||
reader: InputReader::try_new(stdin)?,
|
||||
reader: InputReader::new(stdin),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -216,34 +216,34 @@ impl<'a> Input<'a> {
|
||||
description,
|
||||
metadata: self.metadata,
|
||||
reader: {
|
||||
let path_display =
|
||||
crate::preprocessor::sanitize_for_terminal(&path.to_string_lossy());
|
||||
let mut file =
|
||||
File::open(&path).map_err(|e| format!("'{path_display}': {e}"))?;
|
||||
let mut file = File::open(&path)
|
||||
.map_err(|e| format!("'{}': {e}", path.to_string_lossy()))?;
|
||||
if file.metadata()?.is_dir() {
|
||||
return Err(format!("'{path_display}' is a directory.").into());
|
||||
return Err(format!("'{}' is a directory.", path.to_string_lossy()).into());
|
||||
}
|
||||
|
||||
if let Some(stdout) = stdout_identifier {
|
||||
let input_identifier = Identifier::try_from(file)
|
||||
.map_err(|e| format!("{path_display}: Error identifying file: {e}"))?;
|
||||
let input_identifier = Identifier::try_from(file).map_err(|e| {
|
||||
format!("{}: Error identifying file: {e}", path.to_string_lossy())
|
||||
})?;
|
||||
if stdout.surely_conflicts_with(&input_identifier) {
|
||||
return Err(format!(
|
||||
"IO circle detected. The input from '{path_display}' is also an output. Aborting to avoid infinite loop.",
|
||||
"IO circle detected. The input from '{}' is also an output. Aborting to avoid infinite loop.",
|
||||
path.to_string_lossy()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
file = input_identifier.into_inner().expect("The file was lost in the clircle::Identifier, this should not have happened...");
|
||||
}
|
||||
|
||||
InputReader::try_new(BufReader::new(file))?
|
||||
InputReader::new(BufReader::new(file))
|
||||
},
|
||||
}),
|
||||
InputKind::CustomReader(reader) => Ok(OpenedInput {
|
||||
description,
|
||||
kind: OpenedInputKind::CustomReader,
|
||||
metadata: self.metadata,
|
||||
reader: InputReader::try_new(BufReader::new(reader))?,
|
||||
reader: InputReader::new(BufReader::new(reader)),
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -257,29 +257,28 @@ pub(crate) struct InputReader<'a> {
|
||||
}
|
||||
|
||||
impl<'a> InputReader<'a> {
|
||||
#[cfg(test)]
|
||||
pub(crate) fn new<R: BufRead + 'a>(reader: R) -> InputReader<'a> {
|
||||
Self::try_new(reader).expect("reading the first line failed")
|
||||
}
|
||||
|
||||
pub(crate) fn try_new<R: BufRead + 'a>(mut reader: R) -> io::Result<InputReader<'a>> {
|
||||
pub(crate) fn new<R: BufRead + 'a>(mut reader: R) -> InputReader<'a> {
|
||||
let mut first_line = vec![];
|
||||
reader.read_until(b'\n', &mut first_line)?;
|
||||
reader.read_until(b'\n', &mut first_line).ok();
|
||||
|
||||
let content_type = inspect_content_type(&first_line);
|
||||
let content_type = if first_line.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(content_inspector::inspect(&first_line[..]))
|
||||
};
|
||||
|
||||
if content_type == Some(ContentType::UTF_16LE) {
|
||||
read_utf16_line(&mut reader, &mut first_line, 0x00, 0x0A)?;
|
||||
read_utf16_line(&mut reader, &mut first_line, 0x00, 0x0A).ok();
|
||||
} else if content_type == Some(ContentType::UTF_16BE) {
|
||||
read_utf16_line(&mut reader, &mut first_line, 0x0A, 0x00)?;
|
||||
read_utf16_line(&mut reader, &mut first_line, 0x0A, 0x00).ok();
|
||||
}
|
||||
|
||||
Ok(InputReader {
|
||||
InputReader {
|
||||
inner: Box::new(reader),
|
||||
first_line,
|
||||
content_type,
|
||||
unbuffered: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn read_line(&mut self, buf: &mut Vec<u8>) -> io::Result<bool> {
|
||||
@@ -320,25 +319,6 @@ impl<'a> InputReader<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
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>(
|
||||
reader: &mut R,
|
||||
buf: &mut Vec<u8>,
|
||||
@@ -394,43 +374,6 @@ fn basic() {
|
||||
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]
|
||||
fn utf16le() {
|
||||
let content = b"\xFF\xFE\x73\x00\x0A\x00\x64\x00";
|
||||
|
||||
+7
-115
@@ -14,53 +14,6 @@ use crate::{
|
||||
input::{Input, InputKind, InputReader, OpenedInput, OpenedInputKind},
|
||||
};
|
||||
|
||||
/// Wrap `s` in POSIX single quotes. An embedded `'` is encoded as `'\''`
|
||||
/// (close, escaped quote, reopen) since `'...'` has no escape character.
|
||||
fn shell_quote(s: &str) -> String {
|
||||
if !s.contains('\'') {
|
||||
let mut quoted = String::with_capacity(s.len() + 2);
|
||||
quoted.push('\'');
|
||||
quoted.push_str(s);
|
||||
quoted.push('\'');
|
||||
return quoted;
|
||||
}
|
||||
let mut quoted = String::with_capacity(s.len() + 2);
|
||||
quoted.push('\'');
|
||||
for c in s.chars() {
|
||||
if c == '\'' {
|
||||
quoted.push_str("'\\''");
|
||||
} else {
|
||||
quoted.push(c);
|
||||
}
|
||||
}
|
||||
quoted.push('\'');
|
||||
quoted
|
||||
}
|
||||
|
||||
/// Substitute the first `%s` in a $LESSOPEN/$LESSCLOSE template with a quoted value.
|
||||
fn shell_substitute(template: &str, replacement: &str) -> String {
|
||||
template.replacen("%s", &shell_quote(replacement), 1)
|
||||
}
|
||||
|
||||
/// Substitute the first two `%s` occurrences positionally in one pass; chaining
|
||||
/// `shell_substitute` twice would mis-target if `first` itself contains `%s`.
|
||||
fn shell_substitute_two(template: &str, first: &str, second: &str) -> String {
|
||||
let mut out = String::with_capacity(template.len() + first.len() + second.len() + 4);
|
||||
let mut remaining = template;
|
||||
let mut to_substitute = [Some(first), Some(second)];
|
||||
for slot in &mut to_substitute {
|
||||
if let Some(idx) = remaining.find("%s") {
|
||||
out.push_str(&remaining[..idx]);
|
||||
out.push_str(&shell_quote(slot.take().unwrap()));
|
||||
remaining = &remaining[idx + 2..];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
out.push_str(remaining);
|
||||
out
|
||||
}
|
||||
|
||||
/// Preprocess files and/or stdin using $LESSOPEN and $LESSCLOSE
|
||||
pub(crate) struct LessOpenPreprocessor {
|
||||
lessopen: String,
|
||||
@@ -134,7 +87,7 @@ impl LessOpenPreprocessor {
|
||||
None => return input.open(stdin, stdout_identifier),
|
||||
};
|
||||
|
||||
let mut lessopen_command = shell(shell_substitute(&self.lessopen, path_str));
|
||||
let mut lessopen_command = shell(self.lessopen.replacen("%s", path_str, 1));
|
||||
lessopen_command.stdout(Stdio::piped());
|
||||
|
||||
let lessopen_output = match lessopen_command.execute_output() {
|
||||
@@ -169,7 +122,7 @@ impl LessOpenPreprocessor {
|
||||
let mut stdin_buffer = Vec::new();
|
||||
stdin.read_to_end(&mut stdin_buffer)?;
|
||||
|
||||
let mut lessopen_command = shell(shell_substitute(&self.lessopen, "-"));
|
||||
let mut lessopen_command = shell(self.lessopen.replacen("%s", "-", 1));
|
||||
lessopen_command.stdout(Stdio::piped());
|
||||
|
||||
let lessopen_output = match lessopen_command.execute_input_output(&stdin_buffer)
|
||||
@@ -202,7 +155,7 @@ impl LessOpenPreprocessor {
|
||||
|
||||
Ok(OpenedInput {
|
||||
kind,
|
||||
reader: InputReader::try_new(BufReader::new(
|
||||
reader: InputReader::new(BufReader::new(
|
||||
if matches!(self.kind, LessOpenKind::TempFile) {
|
||||
let lessopen_string = match String::from_utf8(lessopen_stdout) {
|
||||
Ok(string) => string,
|
||||
@@ -228,7 +181,7 @@ impl LessOpenPreprocessor {
|
||||
lessclose: self
|
||||
.lessclose
|
||||
.as_ref()
|
||||
.map(|s| shell_substitute_two(s, &path_str, &stdout)),
|
||||
.map(|s| s.replacen("%s", &path_str, 1).replacen("%s", &stdout, 1)),
|
||||
}
|
||||
} else {
|
||||
Preprocessed {
|
||||
@@ -236,10 +189,10 @@ impl LessOpenPreprocessor {
|
||||
lessclose: self
|
||||
.lessclose
|
||||
.as_ref()
|
||||
.map(|s| shell_substitute_two(s, &path_str, "-")),
|
||||
.map(|s| s.replacen("%s", &path_str, 1).replacen("%s", "-", 1)),
|
||||
}
|
||||
},
|
||||
))?,
|
||||
)),
|
||||
metadata: input.metadata,
|
||||
description: input.description,
|
||||
})
|
||||
@@ -308,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",)
|
||||
};
|
||||
}
|
||||
@@ -431,65 +384,4 @@ mod tests {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shell_quote_plain_filename() {
|
||||
assert_eq!(super::shell_quote("file.txt"), "'file.txt'");
|
||||
assert_eq!(super::shell_quote("with space.txt"), "'with space.txt'");
|
||||
assert_eq!(super::shell_quote(""), "''");
|
||||
assert_eq!(super::shell_quote("-"), "'-'");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shell_quote_metacharacters_neutralised() {
|
||||
assert_eq!(super::shell_quote("; rm -rf ~ ;"), "'; rm -rf ~ ;'");
|
||||
assert_eq!(super::shell_quote("$(payload)"), "'$(payload)'");
|
||||
assert_eq!(super::shell_quote("`payload`"), "'`payload`'");
|
||||
assert_eq!(super::shell_quote("a|b&c;d"), "'a|b&c;d'");
|
||||
assert_eq!(super::shell_quote("..>/dev/null"), "'..>/dev/null'");
|
||||
assert_eq!(super::shell_quote("\n\t"), "'\n\t'");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shell_quote_embedded_single_quote() {
|
||||
assert_eq!(super::shell_quote("it's"), "'it'\\''s'");
|
||||
assert_eq!(super::shell_quote("'"), "''\\'''");
|
||||
assert_eq!(super::shell_quote("a'b'c"), "'a'\\''b'\\''c'");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shell_substitute_only_replaces_first_percent_s() {
|
||||
assert_eq!(super::shell_substitute("echo %s", "x"), "echo 'x'");
|
||||
assert_eq!(super::shell_substitute("echo %s %s", "x"), "echo 'x' %s");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shell_substitute_protects_against_filename_injection() {
|
||||
let template = "|preproc %s";
|
||||
let attacker_filename = "; rm -rf ~ ;";
|
||||
let result = super::shell_substitute(template, attacker_filename);
|
||||
assert_eq!(result, "|preproc '; rm -rf ~ ;'");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shell_substitute_two_replaces_first_two_placeholders() {
|
||||
assert_eq!(
|
||||
super::shell_substitute_two("echo %s and %s done", "a", "b"),
|
||||
"echo 'a' and 'b' done"
|
||||
);
|
||||
// A third %s is left alone.
|
||||
assert_eq!(
|
||||
super::shell_substitute_two("echo %s %s %s", "a", "b"),
|
||||
"echo 'a' 'b' %s"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shell_substitute_two_handles_percent_s_in_first_argument() {
|
||||
// The chained-substitute approach would replace the injected `%s` here.
|
||||
assert_eq!(
|
||||
super::shell_substitute_two("a %s b %s c", "%s", "second"),
|
||||
"a '%s' b 'second' c"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,6 @@ mod vscreen;
|
||||
pub(crate) mod wrapping;
|
||||
|
||||
pub use nonprintable_notation::{BinaryBehavior, NonprintableNotation};
|
||||
pub use preprocessor::sanitize_for_terminal;
|
||||
pub use preprocessor::StripAnsiMode;
|
||||
pub use pretty_printer::{Input, PrettyPrinter, Syntax};
|
||||
pub use syntax_mapping::{MappingTarget, SyntaxMapping};
|
||||
|
||||
@@ -149,35 +149,6 @@ pub fn strip_ansi(line: &str) -> String {
|
||||
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:
|
||||
@@ -290,40 +261,3 @@ fn test_strip_overstrike() {
|
||||
// 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'));
|
||||
}
|
||||
|
||||
+7
-23
@@ -29,9 +29,7 @@ use crate::error::*;
|
||||
use crate::input::OpenedInput;
|
||||
use crate::line_range::{MaxBufferedLineNumber, RangeCheckResult};
|
||||
use crate::output::OutputHandle;
|
||||
use crate::preprocessor::{
|
||||
expand_tabs, replace_nonprintable, sanitize_for_terminal, strip_ansi, strip_overstrike,
|
||||
};
|
||||
use crate::preprocessor::{expand_tabs, replace_nonprintable, strip_ansi, strip_overstrike};
|
||||
use crate::style::StyleComponent;
|
||||
use crate::terminal::{as_terminal_escaped, to_ansi_color};
|
||||
use crate::vscreen::{AnsiStyle, EscapeSequence, EscapeSequenceIterator};
|
||||
@@ -491,7 +489,7 @@ impl Printer for InteractivePrinter<'_> {
|
||||
(but will be present if the output of 'bat' is piped). You can use 'bat -A' \
|
||||
to show the binary file contents.",
|
||||
Yellow.paint("[bat warning]"),
|
||||
sanitize_for_terminal(&input.description.summary()),
|
||||
input.description.summary(),
|
||||
)?;
|
||||
} else if self.config.style_components.grid() {
|
||||
self.print_horizontal_line(handle, '┬')?;
|
||||
@@ -545,11 +543,9 @@ impl Printer for InteractivePrinter<'_> {
|
||||
"{}{}{mode}",
|
||||
description
|
||||
.kind()
|
||||
.map(|kind| format!("{}: ", sanitize_for_terminal(kind)))
|
||||
.map(|kind| format!("{kind}: "))
|
||||
.unwrap_or_else(|| "".into()),
|
||||
self.colors
|
||||
.header_value
|
||||
.paint(sanitize_for_terminal(description.title())),
|
||||
self.colors.header_value.paint(description.title()),
|
||||
);
|
||||
self.print_header_multiline_component(handle, &header_filename)
|
||||
}
|
||||
@@ -603,23 +599,11 @@ impl Printer for InteractivePrinter<'_> {
|
||||
let title = "8<";
|
||||
let title_count = title.chars().count();
|
||||
|
||||
let snip_left = "─ ".repeat(
|
||||
self.config
|
||||
.term_width
|
||||
.saturating_sub(panel_count)
|
||||
.saturating_sub(title_count / 2)
|
||||
/ 4,
|
||||
);
|
||||
let snip_left = "─ ".repeat((self.config.term_width - panel_count - (title_count / 2)) / 4);
|
||||
let snip_left_count = snip_left.chars().count(); // Can't use .len() with Unicode.
|
||||
|
||||
let snip_right = " ─".repeat(
|
||||
self.config
|
||||
.term_width
|
||||
.saturating_sub(panel_count)
|
||||
.saturating_sub(snip_left_count)
|
||||
.saturating_sub(title_count)
|
||||
/ 2,
|
||||
);
|
||||
let snip_right =
|
||||
" ─".repeat((self.config.term_width - panel_count - snip_left_count - title_count) / 2);
|
||||
|
||||
writeln!(
|
||||
handle,
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
[mappings]
|
||||
"XML" = ["*.csproj", "*.vbproj", "*.props", "*.targets", "*.slnx"]
|
||||
"XML" = ["*.csproj", "*.vbproj", "*.props", "*.targets"]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# JSON Lines is a simple variation of JSON #2535
|
||||
[mappings]
|
||||
"JSON" = ["*.jsonl", "*.jsonc", "*.jsonld", "*.geojson", "*.ndjson", "*.code-workspace"]
|
||||
"JSON" = ["*.jsonl", "*.jsonc", "*.jsonld", "*.geojson", "*.ndjson"]
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
[mappings]
|
||||
"Makefile" = ["justfile", "Justfile", ".justfile", "*.justfile"]
|
||||
@@ -1,2 +1,2 @@
|
||||
[mappings]
|
||||
"SSH Config" = ["**/.ssh/**/config"]
|
||||
"SSH Config" = ["**/.ssh/config"]
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
[mappings]
|
||||
"INI" = ["/etc/dnf/dnf.conf", "/etc/yum.repos.d/*.repo"]
|
||||
@@ -1,2 +0,0 @@
|
||||
[mappings]
|
||||
"INI" = ["**/ghostty/**/*.ghostty", "**/ghostty/themes/*"]
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
NAME="${0##*/}"
|
||||
for i in {1..3}; do
|
||||
echo "hello $NAME $i"
|
||||
done
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/usr/bin/expect -f
|
||||
set timeout 30
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env tclsh
|
||||
puts "Hello from tclsh"
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/usr/bin/wish
|
||||
button .b -text "Click"
|
||||
+47
-150
@@ -305,19 +305,6 @@ fn line_range_multiple() {
|
||||
.stdout("line 1\nline 2\nline 4\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn snip_at_terminal_width_one_does_not_panic() {
|
||||
bat()
|
||||
.arg("multiline.txt")
|
||||
.arg("--style=snip")
|
||||
.arg("--color=always")
|
||||
.arg("--terminal-width=1")
|
||||
.arg("--line-range=1:2")
|
||||
.arg("--line-range=4:4")
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn line_range_multiple_with_context() {
|
||||
bat()
|
||||
@@ -1060,57 +1047,6 @@ fn tabs_4_arg_overrides_env_noconfig() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn terminal_width_env_var_is_respected() {
|
||||
let tmp_dir = tempdir().expect("can create temporary directory");
|
||||
let tmp_path = tmp_dir.path().join("long.txt");
|
||||
std::fs::write(
|
||||
&tmp_path,
|
||||
"0123456789abcdef0123456789abcdef0123456789abcdef\n",
|
||||
)
|
||||
.expect("can write temporary file");
|
||||
|
||||
bat()
|
||||
.env("BAT_WIDTH", "20")
|
||||
.arg(&tmp_path)
|
||||
.arg("--paging=never")
|
||||
.arg("--color=never")
|
||||
.arg("--style=numbers")
|
||||
.arg("--decorations=always")
|
||||
.arg("--wrap=character")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(" 1 0123456789abcde\n f0123456789abcd\n ef0123456789abc\n def\n")
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn terminal_width_arg_overrides_env() {
|
||||
let tmp_dir = tempdir().expect("can create temporary directory");
|
||||
let tmp_path = tmp_dir.path().join("long.txt");
|
||||
std::fs::write(
|
||||
&tmp_path,
|
||||
"0123456789abcdef0123456789abcdef0123456789abcdef\n",
|
||||
)
|
||||
.expect("can write temporary file");
|
||||
|
||||
bat()
|
||||
.env("BAT_WIDTH", "20")
|
||||
.arg(&tmp_path)
|
||||
.arg("--paging=never")
|
||||
.arg("--color=never")
|
||||
.arg("--style=numbers")
|
||||
.arg("--decorations=always")
|
||||
.arg("--wrap=character")
|
||||
.arg("--terminal-width=10")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(
|
||||
" 1 01234\n 56789\n abcde\n f0123\n 45678\n 9abcd\n ef012\n 34567\n 89abc\n def\n",
|
||||
)
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fail_non_existing() {
|
||||
bat().arg("non-existing-file").assert().failure();
|
||||
@@ -1538,7 +1474,6 @@ fn diagnostic_sanity_check() {
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("BAT_PAGER="))
|
||||
.stdout(predicate::str::contains("BAT_WIDTH="))
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
@@ -2158,24 +2093,6 @@ fn header_binary() {
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn header_zip_file_is_binary() {
|
||||
let tmp_dir = tempdir().expect("can create temporary directory");
|
||||
let tmp_path = tmp_dir.path().join("test.zip");
|
||||
std::fs::write(&tmp_path, b"PK\x03\x04hello").expect("can write temporary file");
|
||||
|
||||
bat()
|
||||
.arg(&tmp_path)
|
||||
.arg("--decorations=always")
|
||||
.arg("--style=header")
|
||||
.arg("-r=0:0")
|
||||
.arg("--file-name=test.zip")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("File: test.zip <BINARY>\n")
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn header_full_binary() {
|
||||
bat()
|
||||
@@ -3117,6 +3034,53 @@ fn no_wrapping_with_chop_long_lines() {
|
||||
wrapping_test("--chop-long-lines", false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn line_wrapping_when_set_to_character() {
|
||||
wrapping_test("--wrap=character", true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_line_wrapping_when_character_wrap_from_config() {
|
||||
// --wrap=character in a config file should be ignored when output is not interactive
|
||||
// (e.g. when piping), since wrapping is only useful with a pager.
|
||||
let tmp_dir = tempdir().expect("can create temporary directory");
|
||||
let tmp_config_path = tmp_dir.path().join("wrap-character.conf");
|
||||
std::fs::write(&tmp_config_path, "--wrap=character").expect("can write config file");
|
||||
|
||||
let expected = "abcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyz\n";
|
||||
|
||||
bat_with_config()
|
||||
.env("BAT_CONFIG_PATH", tmp_config_path.to_str().unwrap())
|
||||
.arg("--style=rule")
|
||||
.arg("--color=never")
|
||||
.arg("--decorations=always")
|
||||
.arg("--terminal-width=80")
|
||||
.arg("long-single-line.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(expected)
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_line_wrapping_when_character_wrap_from_bat_opts() {
|
||||
// --wrap=character in BAT_OPTS should be ignored when output is not interactive,
|
||||
// since it was not explicitly provided on the command line.
|
||||
let expected = "abcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyzabcdefghigklmnopqrstuvxyz\n";
|
||||
|
||||
bat_with_config()
|
||||
.env("BAT_OPTS", "--wrap=character")
|
||||
.arg("--style=rule")
|
||||
.arg("--color=never")
|
||||
.arg("--decorations=always")
|
||||
.arg("--terminal-width=80")
|
||||
.arg("long-single-line.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(expected)
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn wrap_never_flag_respected_with_paging_always() {
|
||||
@@ -4215,70 +4179,3 @@ fn plain_without_diff_still_works() {
|
||||
.success()
|
||||
.stdout("line 1\nline 2 modified\nline 3\nline 4 added\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tcl_shebang_detection_tclsh() {
|
||||
bat()
|
||||
.arg("--color=always")
|
||||
.arg("--style=plain")
|
||||
.arg("--decorations=always")
|
||||
.arg("regression_tests/issue_3647_tclsh")
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tcl_shebang_detection_wish() {
|
||||
bat()
|
||||
.arg("--color=always")
|
||||
.arg("--style=plain")
|
||||
.arg("--decorations=always")
|
||||
.arg("regression_tests/issue_3647_wish")
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tcl_shebang_detection_expect() {
|
||||
bat()
|
||||
.arg("--color=always")
|
||||
.arg("--style=plain")
|
||||
.arg("--decorations=always")
|
||||
.arg("regression_tests/issue_3647_expect")
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignored_suffix_enables_first_line_detection() {
|
||||
// A shebang shell script saved with a `.txt` extension is Plain Text by
|
||||
// default (the extension wins). With `--ignored-suffix .txt` the suffix is
|
||||
// stripped before detection, so it falls back to the first line and is
|
||||
// highlighted exactly as if its language were forced to bash. See #2745.
|
||||
let fixture = "regression_tests/issue_2745.txt";
|
||||
let common = ["--color=always", "--decorations=never", "--style=plain"];
|
||||
|
||||
let stdout = |args: &[&str]| -> Vec<u8> {
|
||||
let assert = bat()
|
||||
.args(common)
|
||||
.args(args)
|
||||
.arg(fixture)
|
||||
.assert()
|
||||
.success();
|
||||
assert.get_output().stdout.clone()
|
||||
};
|
||||
|
||||
let forced_bash = stdout(&["--language", "bash"]);
|
||||
let with_ignored_suffix = stdout(&["--ignored-suffix", ".txt"]);
|
||||
let default = stdout(&[]);
|
||||
|
||||
// The fixture really is being highlighted (forcing bash is not a no-op).
|
||||
assert!(
|
||||
forced_bash.windows(2).any(|w| w == b"\x1b["),
|
||||
"forced-bash output should contain ANSI color codes"
|
||||
);
|
||||
// With the ignored suffix, detection matches forced bash highlighting...
|
||||
assert_eq!(with_ignored_suffix, forced_bash);
|
||||
// ...while the default (extension wins) stays plain and differs.
|
||||
assert_ne!(default, forced_bash);
|
||||
}
|
||||
|
||||
+10
-12
@@ -4,7 +4,6 @@ import itertools
|
||||
import subprocess
|
||||
import pathlib
|
||||
import shutil
|
||||
from typing import Iterable
|
||||
|
||||
|
||||
def generate_snapshots():
|
||||
@@ -20,23 +19,22 @@ def generate_snapshots():
|
||||
|
||||
|
||||
def generate_style_snapshot(style):
|
||||
generate_snapshot(style.replace(",", "_"), ["--style={}".format(style)])
|
||||
generate_snapshot(style.replace(",", "_"), "--style={}".format(style))
|
||||
|
||||
|
||||
def generate_snapshot(name: str, arguments: Iterable[str]):
|
||||
output_file = "output/{name}.snapshot.txt".format(name=name)
|
||||
command = [
|
||||
"cargo", "run", "--", "--paging=never", "--color=never",
|
||||
"--decorations=always", *arguments, "sample.rs"
|
||||
]
|
||||
def generate_snapshot(name, arguments):
|
||||
command = "cargo run -- --paging=never --color=never --decorations=always "
|
||||
command += "{args} sample.rs > output/{name}.snapshot.txt".format(
|
||||
name=name,
|
||||
args=arguments
|
||||
)
|
||||
print("generating snapshot for {}".format(name))
|
||||
with open(output_file, "w") as f:
|
||||
subprocess.call(command, stdout=f)
|
||||
subprocess.call(command, shell=True)
|
||||
|
||||
|
||||
def build_bat():
|
||||
print("building bat")
|
||||
subprocess.call(["cargo", "build"], cwd="../..")
|
||||
subprocess.call("cargo build", cwd="../..", shell=True)
|
||||
|
||||
|
||||
def prepare_output_dir():
|
||||
@@ -51,7 +49,7 @@ def modify_sample_file():
|
||||
|
||||
def undo_sample_file_modification():
|
||||
print("undoing sample.rs modifications")
|
||||
subprocess.call(["git", "checkout", "--", "sample.rs"])
|
||||
subprocess.call("git checkout -- sample.rs", shell=True)
|
||||
|
||||
|
||||
build_bat()
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
[38;2;248;248;242m([0m[38;2;255;255;255mlogging[0m[38;2;248;248;242m)[0m[38;2;248;248;242m {[0m
|
||||
[38;2;248;248;242m [0m[38;2;102;217;239mlog[0m[38;2;248;248;242m {[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114moutput[0m[38;2;248;248;242m file /var/log/caddy.log[0m
|
||||
[38;2;248;248;242m }[0m
|
||||
[38;2;248;248;242m}[0m
|
||||
|
||||
[38;2;117;113;94m# a comment[0m
|
||||
[38;2;255;255;255mlocalhost:8080[0m[38;2;249;38;114m,[0m[38;2;248;248;242m [0m[38;2;255;255;255mexample.com[0m[38;2;248;248;242m {[0m
|
||||
[38;2;248;248;242m [0m[38;2;102;217;239mroot[0m[38;2;248;248;242m * /var/www/site[0m
|
||||
[38;2;248;248;242m [0m[38;2;102;217;239mfile_server[0m
|
||||
[38;2;248;248;242m [0m[38;2;102;217;239mreverse_proxy[0m[38;2;248;248;242m /.well-known/matrix/* localhost:8008[0m[38;2;248;248;242m {[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mheader_up[0m[38;2;248;248;242m Host [0m[38;2;190;132;255m{upstream_hostport}[0m
|
||||
[38;2;248;248;242m }[0m
|
||||
[38;2;248;248;242m [0m[38;2;102;217;239mimport[0m[38;2;248;248;242m logging[0m
|
||||
[38;2;248;248;242m}[0m
|
||||
|
||||
[38;2;255;255;255mwww.example.com[0m[38;2;248;248;242m {[0m
|
||||
[38;2;248;248;242m [0m[38;2;102;217;239mredir[0m[38;2;248;248;242m https://example.com[0m[38;2;190;132;255m{uri}[0m[38;2;248;248;242m permanent[0m
|
||||
[38;2;248;248;242m [0m[38;2;102;217;239mimport[0m[38;2;248;248;242m logging[0m
|
||||
[38;2;248;248;242m}[0m
|
||||
|
||||
[38;2;255;255;255mstatus.example.com[0m[38;2;248;248;242m {[0m
|
||||
[38;2;248;248;242m [0m[38;2;102;217;239mreverse_proxy[0m[38;2;248;248;242m localhost:3002[0m
|
||||
[38;2;248;248;242m [0m[38;2;102;217;239mimport[0m[38;2;248;248;242m logging[0m
|
||||
[38;2;248;248;242m}[0m
|
||||
+52
-52
@@ -1,85 +1,85 @@
|
||||
[38;2;249;38;114mimport[0m[38;2;248;248;242m [0m[38;2;248;248;242mkotlin[0m[38;2;248;248;242m.[0m[38;2;248;248;242mmath[0m[38;2;248;248;242m.[0m[38;2;249;38;114m*[0m
|
||||
[38;2;249;38;114mimport[0m[38;2;248;248;242m kotlin.math.*[0m
|
||||
|
||||
[38;2;249;38;114mdata[0m[38;2;248;248;242m [0m[38;2;249;38;114mclass[0m[38;2;248;248;242m [0m[4;38;2;102;217;239mExample[0m[38;2;248;248;242m([0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mval[0m[38;2;248;248;242m [0m[3;38;2;253;151;31mname[0m[38;2;248;248;242m:[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mString[0m[38;2;248;248;242m,[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mval[0m[38;2;248;248;242m [0m[3;38;2;253;151;31mnumbers[0m[38;2;248;248;242m:[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mList[0m[38;2;248;248;242m<[0m[3;38;2;166;226;46mInt[0m[38;2;249;38;114m?[0m[38;2;248;248;242m>[0m
|
||||
[38;2;249;38;114mdata[0m[38;2;248;248;242m [0m[38;2;249;38;114mclass[0m[38;2;248;248;242m [0m[38;2;166;226;46mExample[0m[38;2;248;248;242m([0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mval[0m[38;2;248;248;242m [0m[3;38;2;253;151;31mname[0m[38;2;249;38;114m:[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mString[0m[38;2;248;248;242m,[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mval[0m[38;2;248;248;242m [0m[3;38;2;253;151;31mnumbers[0m[38;2;249;38;114m:[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mList[0m[38;2;248;248;242m<[0m[3;38;2;102;217;239mInt[0m[38;2;248;248;242m?>[0m
|
||||
[38;2;248;248;242m)[0m
|
||||
|
||||
[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46minterface[0m[38;2;248;248;242m [0m[38;2;248;248;242mJokeInterface[0m[38;2;248;248;242m [0m[38;2;248;248;242m{[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46misFunny[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m[38;2;248;248;242m:[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mBoolean[0m
|
||||
[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46minterface[0m[38;2;248;248;242m [0m[38;2;166;226;46mJokeInterface[0m[38;2;248;248;242m {[0m
|
||||
[38;2;248;248;242m [0m[38;2;166;226;46mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46misFunny[0m[38;2;248;248;242m()[0m[38;2;249;38;114m:[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mBoolean[0m
|
||||
[38;2;248;248;242m}[0m
|
||||
|
||||
[38;2;249;38;114mabstract[0m[38;2;248;248;242m [0m[38;2;249;38;114mclass[0m[38;2;248;248;242m [0m[4;38;2;102;217;239mAbstractJoke[0m[38;2;248;248;242m [0m[38;2;248;248;242m:[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mJokeInterface[0m[38;2;248;248;242m [0m[38;2;248;248;242m{[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114moverride[0m[38;2;248;248;242m [0m[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46misFunny[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;190;132;255mfalse[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mabstract[0m[38;2;248;248;242m [0m[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46mcontent[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m[38;2;248;248;242m:[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mString[0m
|
||||
[38;2;249;38;114mabstract[0m[38;2;248;248;242m [0m[38;2;249;38;114mclass[0m[38;2;248;248;242m [0m[38;2;166;226;46mAbstractJoke[0m[38;2;248;248;242m [0m[38;2;249;38;114m:[0m[38;2;248;248;242m [0m[3;4;38;2;166;226;46mJokeInterface[0m[38;2;248;248;242m {[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114moverride[0m[38;2;248;248;242m [0m[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46misFunny[0m[38;2;248;248;242m() [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;190;132;255mfalse[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mabstract[0m[38;2;248;248;242m [0m[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46mcontent[0m[38;2;248;248;242m()[0m[38;2;249;38;114m:[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mString[0m
|
||||
[38;2;248;248;242m}[0m
|
||||
|
||||
[38;2;249;38;114mclass[0m[38;2;248;248;242m [0m[4;38;2;102;217;239mJoke[0m[38;2;248;248;242m [0m[38;2;248;248;242m:[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mAbstractJoke[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m[38;2;248;248;242m [0m[38;2;248;248;242m{[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114moverride[0m[38;2;248;248;242m [0m[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46misFunny[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m[38;2;248;248;242m:[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mBoolean[0m[38;2;248;248;242m [0m[38;2;248;248;242m{[0m
|
||||
[38;2;249;38;114mclass[0m[38;2;248;248;242m [0m[38;2;166;226;46mJoke[0m[38;2;248;248;242m [0m[38;2;249;38;114m:[0m[38;2;248;248;242m [0m[3;4;38;2;166;226;46mAbstractJoke[0m[38;2;248;248;242m() {[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114moverride[0m[38;2;248;248;242m [0m[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46misFunny[0m[38;2;248;248;242m()[0m[38;2;249;38;114m:[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mBoolean[0m[38;2;248;248;242m {[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mreturn[0m[38;2;248;248;242m [0m[38;2;190;132;255mtrue[0m
|
||||
[38;2;248;248;242m [0m[38;2;248;248;242m}[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114moverride[0m[38;2;248;248;242m [0m[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46mcontent[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m[38;2;248;248;242m:[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mString[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mcontent of joke here, haha[0m[38;2;230;219;116m"[0m
|
||||
[38;2;248;248;242m }[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114moverride[0m[38;2;248;248;242m [0m[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46mcontent[0m[38;2;248;248;242m()[0m[38;2;249;38;114m:[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mString[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mcontent of joke here, haha[0m[38;2;230;219;116m"[0m
|
||||
[38;2;248;248;242m}[0m
|
||||
|
||||
[38;2;249;38;114mclass[0m[38;2;248;248;242m [0m[4;38;2;102;217;239mDelegatedJoke[0m[38;2;248;248;242m([0m[38;2;249;38;114mval[0m[38;2;248;248;242m [0m[3;38;2;253;151;31mjoke[0m[38;2;248;248;242m:[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mJoke[0m[38;2;248;248;242m)[0m[38;2;248;248;242m [0m[38;2;248;248;242m:[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mJokeInterface[0m[38;2;248;248;242m [0m[38;2;249;38;114mby[0m[38;2;248;248;242m [0m[38;2;255;255;255mjoke[0m[38;2;248;248;242m [0m[38;2;248;248;242m{[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mval[0m[38;2;248;248;242m [0m[38;2;255;255;255mnumber[0m[38;2;248;248;242m:[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mLong[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;190;132;255m123L[0m
|
||||
[38;2;249;38;114mclass[0m[38;2;248;248;242m [0m[38;2;166;226;46mDelegatedJoke[0m[38;2;248;248;242m([0m[38;2;249;38;114mval[0m[38;2;248;248;242m [0m[3;38;2;253;151;31mjoke[0m[38;2;249;38;114m:[0m[38;2;248;248;242m Joke) [0m[38;2;249;38;114m:[0m[38;2;248;248;242m [0m[3;4;38;2;166;226;46mJokeInterface[0m[38;2;248;248;242m [0m[3;4;38;2;166;226;46mby[0m[38;2;248;248;242m [0m[3;4;38;2;166;226;46mjoke[0m[38;2;248;248;242m {[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mval[0m[38;2;248;248;242m [0m[38;2;166;226;46mnumber[0m[38;2;249;38;114m:[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mLong[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;190;132;255m123L[0m
|
||||
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mcompanion[0m[38;2;248;248;242m [0m[38;2;249;38;114mobject[0m[38;2;248;248;242m [0m[38;2;248;248;242m{[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mconst[0m[38;2;248;248;242m [0m[38;2;249;38;114mval[0m[38;2;248;248;242m [0m[38;2;255;255;255msomeConstant[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116msome constant text[0m[38;2;230;219;116m"[0m
|
||||
[38;2;248;248;242m [0m[38;2;248;248;242m}[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mcompanion [0m[38;2;249;38;114mobject[0m[38;2;248;248;242m {[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mconst[0m[38;2;248;248;242m [0m[38;2;249;38;114mval[0m[38;2;248;248;242m [0m[38;2;166;226;46msomeConstant[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116msome constant text[0m[38;2;230;219;116m"[0m
|
||||
[38;2;248;248;242m }[0m
|
||||
[38;2;248;248;242m}[0m
|
||||
|
||||
[38;2;249;38;114mobject[0m[38;2;248;248;242m [0m[4;38;2;102;217;239mSomeSingleton[0m
|
||||
[38;2;249;38;114mobject[0m[38;2;248;248;242m [0m[38;2;166;226;46mSomeSingleton[0m
|
||||
|
||||
[38;2;249;38;114msealed[0m[38;2;248;248;242m [0m[38;2;249;38;114mclass[0m[38;2;248;248;242m [0m[4;38;2;102;217;239mShape[0m[38;2;248;248;242m [0m[38;2;248;248;242m{[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mabstract[0m[38;2;248;248;242m [0m[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46marea[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m[38;2;248;248;242m:[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mDouble[0m
|
||||
[38;2;249;38;114msealed[0m[38;2;248;248;242m [0m[38;2;249;38;114mclass[0m[38;2;248;248;242m [0m[38;2;166;226;46mShape[0m[38;2;248;248;242m {[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mabstract[0m[38;2;248;248;242m [0m[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46marea[0m[38;2;248;248;242m()[0m[38;2;249;38;114m:[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mDouble[0m
|
||||
[38;2;248;248;242m}[0m
|
||||
|
||||
[38;2;249;38;114mdata[0m[38;2;248;248;242m [0m[38;2;249;38;114mclass[0m[38;2;248;248;242m [0m[4;38;2;102;217;239mSquare[0m[38;2;248;248;242m([0m[38;2;249;38;114mval[0m[38;2;248;248;242m [0m[3;38;2;253;151;31msideLength[0m[38;2;248;248;242m:[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mDouble[0m[38;2;248;248;242m)[0m[38;2;248;248;242m [0m[38;2;248;248;242m:[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mShape[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m[38;2;248;248;242m [0m[38;2;248;248;242m{[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114moverride[0m[38;2;248;248;242m [0m[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46marea[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m[38;2;248;248;242m:[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mDouble[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;255;255;255msideLength[0m[38;2;248;248;242m.[0m[38;2;248;248;242mpow[0m[38;2;248;248;242m([0m[38;2;190;132;255m2[0m[38;2;248;248;242m)[0m
|
||||
[38;2;249;38;114mdata[0m[38;2;248;248;242m [0m[38;2;249;38;114mclass[0m[38;2;248;248;242m [0m[38;2;166;226;46mSquare[0m[38;2;248;248;242m([0m[38;2;249;38;114mval[0m[38;2;248;248;242m [0m[3;38;2;253;151;31msideLength[0m[38;2;249;38;114m:[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mDouble[0m[38;2;248;248;242m) [0m[38;2;249;38;114m:[0m[38;2;248;248;242m [0m[3;4;38;2;166;226;46mShape[0m[38;2;248;248;242m() {[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114moverride[0m[38;2;248;248;242m [0m[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46marea[0m[38;2;248;248;242m()[0m[38;2;249;38;114m:[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mDouble[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m sideLength[0m[38;2;249;38;114m.[0m[38;2;248;248;242mpow([0m[38;2;190;132;255m2[0m[38;2;248;248;242m)[0m
|
||||
[38;2;248;248;242m}[0m
|
||||
|
||||
[38;2;249;38;114mobject[0m[38;2;248;248;242m [0m[4;38;2;102;217;239mPoint[0m[38;2;248;248;242m [0m[38;2;248;248;242m:[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mShape[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m[38;2;248;248;242m [0m[38;2;248;248;242m{[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114moverride[0m[38;2;248;248;242m [0m[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46marea[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;190;132;255m.0[0m
|
||||
[38;2;249;38;114mobject[0m[38;2;248;248;242m [0m[38;2;166;226;46mPoint[0m[38;2;248;248;242m [0m[38;2;249;38;114m:[0m[38;2;248;248;242m [0m[3;4;38;2;166;226;46mShape[0m[38;2;248;248;242m() {[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114moverride[0m[38;2;248;248;242m [0m[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46marea[0m[38;2;248;248;242m() [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;249;38;114m.[0m[38;2;190;132;255m0[0m
|
||||
[38;2;248;248;242m}[0m
|
||||
|
||||
[38;2;249;38;114mclass[0m[38;2;248;248;242m [0m[4;38;2;102;217;239mCircle[0m[38;2;248;248;242m([0m[38;2;249;38;114mval[0m[38;2;248;248;242m [0m[3;38;2;253;151;31mradius[0m[38;2;248;248;242m:[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mDouble[0m[38;2;248;248;242m)[0m[38;2;248;248;242m [0m[38;2;248;248;242m:[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mShape[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m[38;2;248;248;242m [0m[38;2;248;248;242m{[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114moverride[0m[38;2;248;248;242m [0m[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46marea[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m[38;2;248;248;242m:[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mDouble[0m[38;2;248;248;242m [0m[38;2;248;248;242m{[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mreturn[0m[38;2;248;248;242m [0m[38;2;255;255;255mPI[0m[38;2;248;248;242m [0m[38;2;249;38;114m*[0m[38;2;248;248;242m [0m[38;2;255;255;255mradius[0m[38;2;248;248;242m [0m[38;2;249;38;114m*[0m[38;2;248;248;242m [0m[38;2;255;255;255mradius[0m
|
||||
[38;2;248;248;242m [0m[38;2;248;248;242m}[0m
|
||||
[38;2;249;38;114mclass[0m[38;2;248;248;242m [0m[38;2;166;226;46mCircle[0m[38;2;248;248;242m([0m[38;2;249;38;114mval[0m[38;2;248;248;242m [0m[3;38;2;253;151;31mradius[0m[38;2;249;38;114m:[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mDouble[0m[38;2;248;248;242m) [0m[38;2;249;38;114m:[0m[38;2;248;248;242m [0m[3;4;38;2;166;226;46mShape[0m[38;2;248;248;242m() {[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114moverride[0m[38;2;248;248;242m [0m[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46marea[0m[38;2;248;248;242m()[0m[38;2;249;38;114m:[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mDouble[0m[38;2;248;248;242m {[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mreturn[0m[38;2;248;248;242m [0m[38;2;190;132;255mPI[0m[38;2;248;248;242m [0m[38;2;249;38;114m*[0m[38;2;248;248;242m radius [0m[38;2;249;38;114m*[0m[38;2;248;248;242m radius[0m
|
||||
[38;2;248;248;242m }[0m
|
||||
[38;2;248;248;242m}[0m
|
||||
|
||||
[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mString[0m[38;2;248;248;242m.[0m[38;2;166;226;46mextensionMethod[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mtest[0m[38;2;230;219;116m"[0m
|
||||
[38;2;249;38;114mfun[0m[38;2;248;248;242m String.[0m[38;2;166;226;46mextensionMethod[0m[38;2;248;248;242m() [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mtest[0m[38;2;230;219;116m"[0m
|
||||
|
||||
[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46mmain[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m[38;2;248;248;242m [0m[38;2;248;248;242m{[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mval[0m[38;2;248;248;242m [0m[38;2;255;255;255mname[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;230;219;116m"""[0m
|
||||
[38;2;249;38;114mfun[0m[38;2;248;248;242m [0m[38;2;166;226;46mmain[0m[38;2;248;248;242m() {[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mval[0m[38;2;248;248;242m [0m[38;2;166;226;46mname[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;230;219;116m"""[0m
|
||||
[38;2;230;219;116m multiline[0m
|
||||
[38;2;230;219;116m string[0m
|
||||
[38;2;230;219;116m [0m
|
||||
[38;2;230;219;116m some numbers: 123123 42[0m
|
||||
[38;2;230;219;116m [0m[38;2;230;219;116m"""[0m[38;2;248;248;242m.[0m[38;2;248;248;242mtrimIndent[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mval[0m[38;2;248;248;242m [0m[38;2;255;255;255mexample[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;248;248;242mExample[0m[38;2;248;248;242m([0m[38;2;255;255;255mname[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;255;255;255mname[0m[38;2;248;248;242m,[0m[38;2;248;248;242m [0m[38;2;255;255;255mnumbers[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m [0m[38;2;248;248;242mlistOf[0m[38;2;248;248;242m([0m[38;2;190;132;255m512[0m[38;2;248;248;242m,[0m[38;2;248;248;242m [0m[38;2;190;132;255m42[0m[38;2;248;248;242m,[0m[38;2;248;248;242m [0m[38;2;190;132;255mnull[0m[38;2;248;248;242m,[0m[38;2;248;248;242m [0m[38;2;249;38;114m-[0m[38;2;190;132;255m1[0m[38;2;248;248;242m)[0m[38;2;248;248;242m)[0m
|
||||
[38;2;230;219;116m [0m[38;2;230;219;116m"""[0m[38;2;249;38;114m.[0m[38;2;248;248;242mtrimIndent()[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mval[0m[38;2;248;248;242m [0m[38;2;166;226;46mexample[0m[38;2;248;248;242m [0m[38;2;249;38;114m=[0m[38;2;248;248;242m Example(name [0m[38;2;249;38;114m=[0m[38;2;248;248;242m name, numbers [0m[38;2;249;38;114m=[0m[38;2;248;248;242m listOf([0m[38;2;190;132;255m512[0m[38;2;248;248;242m, [0m[38;2;190;132;255m42[0m[38;2;248;248;242m, [0m[38;2;190;132;255mnull[0m[38;2;248;248;242m, [0m[38;2;249;38;114m-[0m[38;2;190;132;255m1[0m[38;2;248;248;242m))[0m
|
||||
|
||||
[38;2;248;248;242m [0m[38;2;255;255;255mexample[0m[38;2;248;248;242m.[0m[38;2;255;255;255mnumbers[0m
|
||||
[38;2;248;248;242m [0m[38;2;248;248;242m.[0m[38;2;248;248;242mfilterNotNull[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m
|
||||
[38;2;248;248;242m [0m[38;2;248;248;242m.[0m[38;2;248;248;242mforEach[0m[38;2;248;248;242m [0m[38;2;248;248;242m{[0m[38;2;248;248;242m [0m[38;2;248;248;242mprintln[0m[38;2;248;248;242m([0m[38;2;255;255;255mit[0m[38;2;248;248;242m)[0m[38;2;248;248;242m [0m[38;2;248;248;242m}[0m
|
||||
[38;2;248;248;242m example[0m[38;2;249;38;114m.[0m[38;2;248;248;242mnumbers[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114m.[0m[38;2;248;248;242mfilterNotNull()[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114m.[0m[38;2;248;248;242mforEach { println(it) }[0m
|
||||
|
||||
[38;2;248;248;242m [0m[38;2;248;248;242msetOf[0m[38;2;248;248;242m([0m[38;2;248;248;242mJoke[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m[38;2;248;248;242m,[0m[38;2;248;248;242m [0m[38;2;248;248;242mDelegatedJoke[0m[38;2;248;248;242m([0m[38;2;248;248;242mJoke[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m[38;2;248;248;242m)[0m[38;2;248;248;242m.[0m[38;2;255;255;255mjoke[0m[38;2;248;248;242m)[0m
|
||||
[38;2;248;248;242m [0m[38;2;248;248;242m.[0m[38;2;248;248;242mfilter[0m[38;2;248;248;242m([0m[38;2;255;255;255mJokeInterface[0m[38;2;248;248;242m::[0m[38;2;248;248;242misFunny[0m[38;2;248;248;242m)[0m
|
||||
[38;2;248;248;242m [0m[38;2;248;248;242m.[0m[38;2;248;248;242mmap[0m[38;2;248;248;242m([0m[38;2;255;255;255mAbstractJoke[0m[38;2;248;248;242m::[0m[38;2;248;248;242mcontent[0m[38;2;248;248;242m)[0m
|
||||
[38;2;248;248;242m [0m[38;2;248;248;242m.[0m[38;2;248;248;242mforEachIndexed[0m[38;2;248;248;242m [0m[38;2;248;248;242m{[0m[38;2;248;248;242m [0m[38;2;255;255;255mindex[0m[38;2;248;248;242m:[0m[38;2;248;248;242m [0m[3;38;2;166;226;46mInt[0m[38;2;248;248;242m,[0m[38;2;248;248;242m [0m[3;38;2;253;151;31mjoke[0m[38;2;248;248;242m [0m[38;2;249;38;114m->[0m
|
||||
[38;2;248;248;242m [0m[38;2;248;248;242mprintln[0m[38;2;248;248;242m([0m[38;2;230;219;116m"[0m[38;2;230;219;116mI heard a funny joke(#[0m[38;2;248;248;242m${[0m[38;2;255;255;255mindex[0m[38;2;248;248;242m [0m[38;2;249;38;114m+[0m[38;2;248;248;242m [0m[38;2;190;132;255m1[0m[38;2;248;248;242m}[0m[38;2;230;219;116m): [0m[38;2;255;255;255m$joke[0m[38;2;230;219;116m"[0m[38;2;248;248;242m)[0m
|
||||
[38;2;248;248;242m [0m[38;2;248;248;242m}[0m
|
||||
[38;2;248;248;242m setOf(Joke(), DelegatedJoke(Joke())[0m[38;2;249;38;114m.[0m[38;2;248;248;242mjoke)[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114m.[0m[38;2;248;248;242mfilter(JokeInterface[0m[38;2;249;38;114m::[0m[38;2;248;248;242misFunny)[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114m.[0m[38;2;248;248;242mmap(AbstractJoke[0m[38;2;249;38;114m::[0m[38;2;248;248;242mcontent)[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114m.[0m[38;2;248;248;242mforEachIndexed { index[0m[38;2;249;38;114m:[0m[38;2;248;248;242m [0m[3;38;2;102;217;239mInt[0m[38;2;248;248;242m, joke [0m[38;2;249;38;114m->[0m
|
||||
[38;2;248;248;242m println([0m[38;2;230;219;116m"[0m[38;2;230;219;116mI heard a funny joke(#[0m[3;38;2;253;151;31m${index + 1}[0m[38;2;230;219;116m): [0m[3;38;2;253;151;31m$joke[0m[38;2;230;219;116m"[0m[38;2;248;248;242m)[0m
|
||||
[38;2;248;248;242m }[0m
|
||||
|
||||
[38;2;248;248;242m [0m[38;2;248;248;242mlistOf[0m[38;2;248;248;242m([0m[38;2;248;248;242mSquare[0m[38;2;248;248;242m([0m[38;2;190;132;255m12.3[0m[38;2;248;248;242m)[0m[38;2;248;248;242m,[0m[38;2;248;248;242m [0m[38;2;255;255;255mPoint[0m[38;2;248;248;242m,[0m[38;2;248;248;242m [0m[38;2;248;248;242mCircle[0m[38;2;248;248;242m([0m[38;2;190;132;255m5.2[0m[38;2;248;248;242m)[0m[38;2;248;248;242m)[0m
|
||||
[38;2;248;248;242m [0m[38;2;248;248;242m.[0m[38;2;248;248;242massociateWith[0m[38;2;248;248;242m([0m[38;2;255;255;255mShape[0m[38;2;248;248;242m::[0m[38;2;248;248;242marea[0m[38;2;248;248;242m)[0m
|
||||
[38;2;248;248;242m [0m[38;2;248;248;242m.[0m[38;2;248;248;242mtoList[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m
|
||||
[38;2;248;248;242m [0m[38;2;248;248;242m.[0m[38;2;248;248;242msortedBy[0m[38;2;248;248;242m [0m[38;2;248;248;242m{[0m[38;2;248;248;242m [0m[38;2;255;255;255mit[0m[38;2;248;248;242m.[0m[38;2;255;255;255msecond[0m[38;2;248;248;242m [0m[38;2;248;248;242m}[0m
|
||||
[38;2;248;248;242m [0m[38;2;248;248;242m.[0m[38;2;248;248;242mforEach[0m[38;2;248;248;242m [0m[38;2;248;248;242m{[0m
|
||||
[38;2;248;248;242m [0m[38;2;248;248;242mprintln[0m[38;2;248;248;242m([0m[38;2;230;219;116m"[0m[38;2;248;248;242m${[0m[38;2;255;255;255mit[0m[38;2;248;248;242m.[0m[38;2;255;255;255mfirst[0m[38;2;248;248;242m}[0m[38;2;230;219;116m: [0m[38;2;248;248;242m${[0m[38;2;255;255;255mit[0m[38;2;248;248;242m.[0m[38;2;255;255;255msecond[0m[38;2;248;248;242m}[0m[38;2;230;219;116m"[0m[38;2;248;248;242m)[0m
|
||||
[38;2;248;248;242m [0m[38;2;248;248;242m}[0m
|
||||
[38;2;248;248;242m listOf(Square([0m[38;2;190;132;255m12.3[0m[38;2;248;248;242m), Point, Circle([0m[38;2;190;132;255m5.2[0m[38;2;248;248;242m))[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114m.[0m[38;2;248;248;242massociateWith(Shape[0m[38;2;249;38;114m::[0m[38;2;248;248;242marea)[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114m.[0m[38;2;248;248;242mtoList()[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114m.[0m[38;2;248;248;242msortedBy { it[0m[38;2;249;38;114m.[0m[38;2;248;248;242msecond }[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114m.[0m[38;2;248;248;242mforEach {[0m
|
||||
[38;2;248;248;242m println([0m[38;2;230;219;116m"[0m[3;38;2;253;151;31m${it.first}[0m[38;2;230;219;116m: [0m[3;38;2;253;151;31m${it.second}[0m[38;2;230;219;116m"[0m[38;2;248;248;242m)[0m
|
||||
[38;2;248;248;242m }[0m
|
||||
|
||||
[38;2;248;248;242m [0m[38;2;248;248;242mprintln[0m[38;2;248;248;242m([0m[38;2;230;219;116m"[0m[38;2;230;219;116msome string[0m[38;2;230;219;116m"[0m[38;2;248;248;242m.[0m[38;2;248;248;242mextensionMethod[0m[38;2;248;248;242m([0m[38;2;248;248;242m)[0m[38;2;248;248;242m)[0m
|
||||
[38;2;248;248;242m println([0m[38;2;230;219;116m"[0m[38;2;230;219;116msome string[0m[38;2;230;219;116m"[0m[38;2;249;38;114m.[0m[38;2;248;248;242mextensionMethod())[0m
|
||||
|
||||
[38;2;248;248;242m [0m[38;2;248;248;242mrequire[0m[38;2;248;248;242m([0m[38;2;255;255;255mSomeSingleton[0m[38;2;248;248;242m::[0m[38;2;249;38;114mclass[0m[38;2;248;248;242m.[0m[38;2;255;255;255msimpleName[0m[38;2;248;248;242m [0m[38;2;249;38;114m==[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mSomeSingletonName[0m[38;2;230;219;116m"[0m[38;2;248;248;242m)[0m[38;2;248;248;242m [0m[38;2;248;248;242m{[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116msomething does not seem right...[0m[38;2;230;219;116m"[0m[38;2;248;248;242m [0m[38;2;248;248;242m}[0m
|
||||
[38;2;248;248;242m require(SomeSingleton[0m[38;2;249;38;114m::[0m[38;2;248;248;242mclass[0m[38;2;249;38;114m.[0m[38;2;248;248;242msimpleName [0m[38;2;249;38;114m==[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mSomeSingletonName[0m[38;2;230;219;116m"[0m[38;2;248;248;242m) { [0m[38;2;230;219;116m"[0m[38;2;230;219;116msomething does not seem right...[0m[38;2;230;219;116m"[0m[38;2;248;248;242m }[0m
|
||||
[38;2;248;248;242m}[0m
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
[38;2;117;113;94m#[0m[38;2;117;113;94m!/usr/bin/expect -f[0m
|
||||
[38;2;117;113;94m#[0m[38;2;117;113;94m Expect script detected via expect shebang[0m
|
||||
[38;2;249;38;114mset[0m[38;2;248;248;242m timeout [0m[38;2;190;132;255m30[0m
|
||||
[38;2;248;248;242mspawn[0m[38;2;248;248;242m ssh user@host[0m
|
||||
[38;2;248;248;242mexpect[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mpassword:[0m[38;2;230;219;116m"[0m
|
||||
[38;2;248;248;242msend[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116msecret[0m[38;2;190;132;255m\r[0m[38;2;230;219;116m"[0m
|
||||
[38;2;248;248;242mexpect[0m[38;2;248;248;242m [0m[38;2;249;38;114meof[0m
|
||||
@@ -1,7 +0,0 @@
|
||||
[38;2;117;113;94m#[0m[38;2;117;113;94m!/usr/bin/env tclsh[0m
|
||||
[38;2;117;113;94m#[0m[38;2;117;113;94m Tcl script detected via tclsh shebang[0m
|
||||
[38;2;249;38;114mputs[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mHello from tclsh[0m[38;2;230;219;116m"[0m
|
||||
[38;2;249;38;114mset[0m[38;2;248;248;242m x [0m[38;2;190;132;255m42[0m
|
||||
[38;2;249;38;114mif[0m[38;2;248;248;242m [0m[38;2;248;248;242m{[0m[38;2;255;255;255m$[0m[38;2;255;255;255mx[0m[38;2;248;248;242m [0m[38;2;249;38;114m>[0m[38;2;248;248;242m [0m[38;2;190;132;255m0[0m[38;2;248;248;242m}[0m[38;2;248;248;242m [0m[38;2;248;248;242m{[0m
|
||||
[38;2;248;248;242m [0m[38;2;249;38;114mputs[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mpositive[0m[38;2;230;219;116m"[0m
|
||||
[38;2;248;248;242m}[0m
|
||||
@@ -1,5 +0,0 @@
|
||||
[38;2;117;113;94m#[0m[38;2;117;113;94m!/usr/bin/wish[0m
|
||||
[38;2;117;113;94m#[0m[38;2;117;113;94m Tk script detected via wish shebang[0m
|
||||
[38;2;249;38;114mpackage[0m[38;2;248;248;242m require Tk[0m
|
||||
[38;2;248;248;242mbutton[0m[38;2;248;248;242m .b [0m[38;2;249;38;114m-[0m[38;2;248;248;242mtext [0m[38;2;230;219;116m"[0m[38;2;230;219;116mClick[0m[38;2;230;219;116m"[0m[38;2;248;248;242m [0m[38;2;249;38;114m-[0m[38;2;248;248;242mcommand [0m[38;2;248;248;242m{[0m[38;2;249;38;114mputs[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mclicked[0m[38;2;230;219;116m"[0m[38;2;248;248;242m}[0m
|
||||
[38;2;248;248;242mpack[0m[38;2;248;248;242m .b[0m
|
||||
@@ -1,7 +0,0 @@
|
||||
[38;2;255;255;255m<[0m[38;2;249;38;114mSolution[0m[38;2;255;255;255m>[0m
|
||||
[38;2;248;248;242m [0m[38;2;255;255;255m<[0m[38;2;249;38;114mFolder[0m[38;2;248;248;242m [0m[38;2;166;226;46mName[0m[38;2;248;248;242m=[0m[38;2;230;219;116m"[0m[38;2;230;219;116m/Build/[0m[38;2;230;219;116m"[0m[38;2;255;255;255m>[0m
|
||||
[38;2;248;248;242m [0m[38;2;255;255;255m<[0m[38;2;249;38;114mFile[0m[38;2;248;248;242m [0m[38;2;166;226;46mPath[0m[38;2;248;248;242m=[0m[38;2;230;219;116m"[0m[38;2;230;219;116mDirectory.Build.props[0m[38;2;230;219;116m"[0m[38;2;248;248;242m [0m[38;2;255;255;255m/>[0m
|
||||
[38;2;248;248;242m [0m[38;2;255;255;255m<[0m[38;2;249;38;114mFile[0m[38;2;248;248;242m [0m[38;2;166;226;46mPath[0m[38;2;248;248;242m=[0m[38;2;230;219;116m"[0m[38;2;230;219;116mprojectname.targets[0m[38;2;230;219;116m"[0m[38;2;248;248;242m [0m[38;2;255;255;255m/>[0m
|
||||
[38;2;248;248;242m [0m[38;2;255;255;255m</[0m[38;2;249;38;114mFolder[0m[38;2;255;255;255m>[0m
|
||||
[38;2;248;248;242m [0m[38;2;255;255;255m<[0m[38;2;249;38;114mProject[0m[38;2;248;248;242m [0m[38;2;166;226;46mPath[0m[38;2;248;248;242m=[0m[38;2;230;219;116m"[0m[38;2;230;219;116mconsole.csproj[0m[38;2;230;219;116m"[0m[38;2;248;248;242m [0m[38;2;255;255;255m/>[0m
|
||||
[38;2;255;255;255m</[0m[38;2;249;38;114mSolution[0m[38;2;255;255;255m>[0m
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
(logging) {
|
||||
log {
|
||||
output file /var/log/caddy.log
|
||||
}
|
||||
}
|
||||
|
||||
# a comment
|
||||
localhost:8080, example.com {
|
||||
root * /var/www/site
|
||||
file_server
|
||||
reverse_proxy /.well-known/matrix/* localhost:8008 {
|
||||
header_up Host {upstream_hostport}
|
||||
}
|
||||
import logging
|
||||
}
|
||||
|
||||
www.example.com {
|
||||
redir https://example.com{uri} permanent
|
||||
import logging
|
||||
}
|
||||
|
||||
status.example.com {
|
||||
reverse_proxy localhost:3002
|
||||
import logging
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/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
@@ -1,7 +0,0 @@
|
||||
#!/usr/bin/env tclsh
|
||||
# Tcl script detected via tclsh shebang
|
||||
puts "Hello from tclsh"
|
||||
set x 42
|
||||
if {$x > 0} {
|
||||
puts "positive"
|
||||
}
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
#!/usr/bin/wish
|
||||
# Tk script detected via wish shebang
|
||||
package require Tk
|
||||
button .b -text "Click" -command {puts "clicked"}
|
||||
pack .b
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
<Solution>
|
||||
<Folder Name="/Build/">
|
||||
<File Path="Directory.Build.props" />
|
||||
<File Path="projectname.targets" />
|
||||
</Folder>
|
||||
<Project Path="console.csproj" />
|
||||
</Solution>
|
||||
+28
-37
@@ -1,17 +1,15 @@
|
||||
use gix::actor::SignatureRef;
|
||||
use gix::bstr::BString;
|
||||
use gix::bstr::ByteSlice;
|
||||
use gix::date::time::Format;
|
||||
use gix::date::Time;
|
||||
use gix::objs::tree;
|
||||
use std::env;
|
||||
use std::fs::{self, File};
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
use tempfile::TempDir;
|
||||
|
||||
use git2::build::CheckoutBuilder;
|
||||
use git2::Repository;
|
||||
use git2::Signature;
|
||||
|
||||
pub struct BatTester {
|
||||
/// Temporary working directory
|
||||
temp_dir: TempDir,
|
||||
@@ -61,42 +59,35 @@ impl Default for BatTester {
|
||||
}
|
||||
}
|
||||
|
||||
fn create_sample_directory() -> Result<TempDir, Box<dyn std::error::Error>> {
|
||||
fn create_sample_directory() -> Result<TempDir, git2::Error> {
|
||||
// Create temp directory and initialize repository
|
||||
let temp_dir = TempDir::new().expect("Temp directory");
|
||||
let repo = gix::init(&temp_dir)?;
|
||||
let mut tree = gix::objs::Tree::empty();
|
||||
let repo = Repository::init(&temp_dir)?;
|
||||
|
||||
// Create sample.rs from snapshot file
|
||||
let blob_id = repo.write_blob_stream(File::open("tests/snapshots/sample.rs")?)?;
|
||||
let entry = tree::Entry {
|
||||
mode: tree::EntryMode::from(tree::EntryKind::Blob),
|
||||
oid: blob_id.object()?.id,
|
||||
filename: BString::from("sample.rs"),
|
||||
};
|
||||
tree.entries.push(entry);
|
||||
let tree_id = repo.write_object(tree)?;
|
||||
// Copy over `sample.rs`
|
||||
let sample_path = temp_dir.path().join("sample.rs");
|
||||
println!("{sample_path:?}");
|
||||
fs::copy("tests/snapshots/sample.rs", &sample_path).expect("successful copy");
|
||||
|
||||
let author = SignatureRef {
|
||||
name: "test".as_bytes().as_bstr(),
|
||||
email: "test@test.test".as_bytes().as_bstr(),
|
||||
time: &Time::now_local_or_utc().format_or_unix(Format::Raw),
|
||||
};
|
||||
let commit_id = repo.commit_as(
|
||||
author,
|
||||
author,
|
||||
"HEAD",
|
||||
// Commit
|
||||
let mut index = repo.index()?;
|
||||
index.add_path(Path::new("sample.rs"))?;
|
||||
|
||||
let oid = index.write_tree()?;
|
||||
let signature = Signature::now("bat test runner", "bat@test.runner")?;
|
||||
let tree = repo.find_tree(oid)?;
|
||||
let _ = repo.commit(
|
||||
Some("HEAD"), // point HEAD to our new commit
|
||||
&signature, // author
|
||||
&signature, // committer
|
||||
"initial commit",
|
||||
tree_id,
|
||||
gix::commit::NO_PARENT_IDS,
|
||||
)?;
|
||||
assert_eq!(commit_id, repo.head_id()?);
|
||||
&tree,
|
||||
&[],
|
||||
);
|
||||
let mut opts = CheckoutBuilder::new();
|
||||
repo.checkout_head(Some(opts.force()))?;
|
||||
|
||||
fs::copy(
|
||||
"tests/snapshots/sample.modified.rs",
|
||||
temp_dir.path().join("sample.rs"),
|
||||
)
|
||||
.expect("successful copy");
|
||||
fs::copy("tests/snapshots/sample.modified.rs", &sample_path).expect("successful copy");
|
||||
|
||||
Ok(temp_dir)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user