1
0
mirror of https://github.com/sharkdp/bat.git synced 2025-09-01 10:52:24 +01:00

Compare commits

..

1 Commits

Author SHA1 Message Date
David Peter
8244eb8ef8 Experiment: remove CARGO_TEST_OPTIONS
In principle, this *could* work. `cargo cross` runs the tests via QEMU.
For integration tests, the only difficulty is that we run `bat` from
within the tests. But maybe this is handled by assert_cmd.
2021-08-22 16:31:41 +02:00
98 changed files with 1247 additions and 2235 deletions

View File

@@ -1,7 +1,7 @@
name: CICD
env:
MIN_SUPPORTED_RUST_VERSION: "1.46.0"
MIN_SUPPORTED_RUST_VERSION: "1.45.0"
CICD_INTERMEDIATES_DIR: "_cicd-intermediates"
on:
@@ -14,19 +14,6 @@ on:
- '*'
jobs:
ensure_cargo_fmt:
name: Ensure 'cargo fmt' has been run
runs-on: ubuntu-20.04
steps:
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
default: true
profile: minimal
components: rustfmt
- uses: actions/checkout@v2
- run: cargo fmt -- --check
min_version:
name: Minimum supported rust version
runs-on: ubuntu-20.04
@@ -40,12 +27,17 @@ jobs:
toolchain: ${{ env.MIN_SUPPORTED_RUST_VERSION }}
default: true
profile: minimal # minimal component installation (ie, no documentation)
components: clippy
components: clippy, rustfmt
- name: Ensure `cargo fmt` has been run
uses: actions-rs/cargo@v1
with:
command: fmt
args: -- --check
- name: Run clippy (on minimum supported rust version to prevent warnings we can't fix)
uses: actions-rs/cargo@v1
with:
command: clippy
args: --locked --all-targets --all-features -- --allow clippy::unknown_clippy_lints
args: --locked --all-targets --all-features
- name: Run tests
uses: actions-rs/cargo@v1
with:
@@ -94,21 +86,6 @@ jobs:
run: bat --list-languages
- name: List of themes
run: bat --list-themes
- name: Test custom assets
run: tests/syntax-tests/test_custom_assets.sh
documentation:
name: Documentation
runs-on: ubuntu-20.04
steps:
- name: Git checkout
uses: actions/checkout@v2
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
default: true
profile: minimal
- name: Check documentation
env:
RUSTDOCFLAGS: -D warnings
@@ -118,23 +95,24 @@ jobs:
args: --locked --no-deps --document-private-items --all-features
build:
name: ${{ matrix.job.target }} (${{ matrix.job.os }})
name: ${{ matrix.job.os }} (${{ matrix.job.target }})
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job:
- { target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
- { target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true }
- { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true }
- { target: i686-pc-windows-msvc , os: windows-2019 }
- { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
- { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
- { target: x86_64-apple-darwin , os: macos-10.15 }
- { target: x86_64-pc-windows-gnu , os: windows-2019 }
- { target: x86_64-pc-windows-msvc , os: windows-2019 }
- { target: x86_64-unknown-linux-gnu , os: ubuntu-20.04 }
- { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
- { os: ubuntu-20.04, target: arm-unknown-linux-gnueabihf , use-cross: true }
- { os: ubuntu-20.04, target: arm-unknown-linux-musleabihf, use-cross: true }
- { os: ubuntu-20.04, target: aarch64-unknown-linux-gnu , use-cross: true }
- { os: ubuntu-20.04, target: i686-unknown-linux-gnu , use-cross: true }
- { os: ubuntu-20.04, target: i686-unknown-linux-musl , use-cross: true }
- { os: ubuntu-20.04, target: x86_64-unknown-linux-gnu }
- { os: ubuntu-20.04, target: x86_64-unknown-linux-musl , use-cross: true }
- { os: macos-10.15 , target: x86_64-apple-darwin }
# - { os: windows-2019, target: i686-pc-windows-gnu } ## disabled; error: linker `i686-w64-mingw32-gcc` not found
- { os: windows-2019, target: i686-pc-windows-msvc }
- { os: windows-2019, target: x86_64-pc-windows-gnu }
- { os: windows-2019, target: x86_64-pc-windows-msvc }
steps:
- name: Checkout source code
uses: actions/checkout@v2
@@ -216,21 +194,12 @@ jobs:
echo ::set-output name=BIN_PATH::${BIN_PATH}
echo ::set-output name=BIN_NAME::${BIN_NAME}
- name: Set testing options
id: test-options
shell: bash
run: |
# test only library unit tests and binary for arm-type targets
unset CARGO_TEST_OPTIONS
unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-* | aarch64-*) CARGO_TEST_OPTIONS="--lib --bin ${PROJECT_NAME}" ;; esac;
echo ::set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS}
- name: Run tests
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.job.use-cross }}
command: test
args: --locked --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}}
args: --locked --target=${{ matrix.job.target }}
- name: Run bat
uses: actions-rs/cargo@v1
@@ -307,7 +276,6 @@ jobs:
# Autocompletion files
cp 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'-*/out/assets/completions/bat.bash "$ARCHIVE_DIR/autocomplete/${{ env.PROJECT_NAME }}.bash"
cp 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'-*/out/assets/completions/bat.fish "$ARCHIVE_DIR/autocomplete/${{ env.PROJECT_NAME }}.fish"
cp 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'-*/out/assets/completions/_bat.ps1 "$ARCHIVE_DIR/autocomplete/_${{ env.PROJECT_NAME }}.ps1"
cp 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'-*/out/assets/completions/bat.zsh "$ARCHIVE_DIR/autocomplete/${{ env.PROJECT_NAME }}.zsh"
# base compressed package
@@ -356,7 +324,6 @@ jobs:
gzip -n --best "${DPKG_DIR}/usr/share/man/man1/${{ env.PROJECT_NAME }}.1"
# Autocompletion files
install -Dm644 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'-*/out/assets/completions/bat.bash "${DPKG_DIR}/usr/share/bash-completion/completions/${{ env.PROJECT_NAME }}"
install -Dm644 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'-*/out/assets/completions/bat.fish "${DPKG_DIR}/usr/share/fish/vendor_completions.d/${{ env.PROJECT_NAME }}.fish"
install -Dm644 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'-*/out/assets/completions/bat.zsh "${DPKG_DIR}/usr/share/zsh/vendor-completions/_${{ env.PROJECT_NAME }}"

3
.gitmodules vendored
View File

@@ -230,6 +230,3 @@
[submodule "assets/syntaxes/02_Extra/Slim"]
path = assets/syntaxes/02_Extra/Slim
url = https://github.com/slim-template/ruby-slim.tmbundle.git
[submodule "assets/syntaxes/02_Extra/Racket"]
path = assets/syntaxes/02_Extra/Racket
url = https://github.com/follesoe/sublime-racket.git

View File

@@ -3,23 +3,16 @@
## Features
- `$BAT_CONFIG_DIR` is now a recognized environment variable. It has precedence over `$XDG_CONFIG_HOME`, see #1727 (@billrisher)
- Support for `x:+delta` syntax in line ranges (e.g. `20:+10`). See #1810 (@bojan88)
## Bugfixes
- Python syntax highlighting no longer suffers from abysmal performance in specific scenarios. See #1688 (@keith-hall)
- First line not shown in diff context. See #1891 (@divagant-martian)
## Performance
- Load cached assets as fast as integrated assets, see #1753 (@Enselic)
- Greatly reduce startup time in loop-through mode, e.g. when redirecting output. Instead of *50 ms* - *100 ms*, startup takes *5 ms* - *10 ms*. See #1747 (@Enselic)
- Reduce startup time by approximately 80% for 91 out of 168 syntaxes when using `--language`. See #1787 (@Enselic)
## Other
- Add PowerShell completion, see #1826 (@rashil2000)
- Minimum supported Rust version (MSRV) bumped to 1.46
- Load cached assets as fast as integrated assets, see #1753 (@Enselic)
- Greatly reduce startup time in loop-through mode, e.g. when redirecting output. Instead of *50 ms* - *100 ms*, startup takes *5 ms* - *10 ms*. See #1747 (@Enselic)
## Syntaxes
@@ -29,26 +22,16 @@
- Highlight for `vimrc` and `gvimrc` files, see #1763 (@SuperSandro2000)
- Syslog highlighting improvements, see #1793 (@scop)
- Added support for `slim` syntax, see #1693 (@mfinelli)
- Racket, see #1884 (@jubnzv)
## New themes
## `bat` as a library
- Deprecate `HighlightingAssets::syntaxes()` and `HighlightingAssets::syntax_for_file_name()`. Use `HighlightingAssets::get_syntaxes()` and `HighlightingAssets::get_syntax_for_path()` instead. They return a `Result` which is needed for upcoming lazy-loading work to improve startup performance. They also return which `SyntaxSet` the returned `SyntaxReference` belongs to. See #1747, #1755, #1776, #1862 (@Enselic)
- Remove `HighlightingAssets::from_files` and `HighlightingAssets::save_to_cache`. Instead of calling the former and then the latter you now make a single call to `bat::assets::build`. See #1802 (@Enselic)
- Replace the `error::Error(error::ErrorKind, _)` struct and enum with an `error::Error` enum. `Error(ErrorKind::UnknownSyntax, _)` becomes `Error::UnknownSyntax`, etc. Also remove the `error::ResultExt` trait. These changes stem from replacing `error-chain` with `thiserror`. See #1820 (@Enselic)
- Deprecate `HighlightingAssets::syntaxes()` and `HighlightingAssets::syntax_for_file_name()`. Use `HighlightingAssets::get_syntaxes()` and `HighlightingAssets::get_syntax_for_file_name()` instead. They return a `Result` which is needed for upcoming lazy-loading work to improve startup performance. They also return what `SyntaxSet` the returned `SyntaxReference` belongs to. See #1747, #1755 and #1776 (@Enselic)
# v0.18.3
## Bugfixes
- Bump `git2` dependency to fix build with Rust 1.54, see #1761
# v0.18.2
## Features

83
Cargo.lock generated
View File

@@ -46,9 +46,9 @@ dependencies = [
[[package]]
name = "assert_cmd"
version = "2.0.1"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b800c4403e8105d959595e1f88119e78bc12bc874c4336973658b648a746ba93"
checksum = "c98233c6673d8601ab23e77eb38f999c51100d46c5703b17288c57fddf3a1ffe"
dependencies = [
"bstr",
"doc-comment",
@@ -83,13 +83,12 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "bat"
version = "0.18.3"
version = "0.18.2"
dependencies = [
"ansi_colours",
"ansi_term 0.12.1",
"assert_cmd",
"atty",
"bincode",
"bugreport",
"clap",
"clircle",
@@ -97,7 +96,7 @@ dependencies = [
"content_inspector",
"dirs-next",
"encoding",
"flate2",
"error-chain",
"git2",
"globset",
"grep-cli",
@@ -113,7 +112,6 @@ dependencies = [
"shell-words",
"syntect",
"tempfile",
"thiserror",
"unicode-width",
"wait-timeout",
"wild",
@@ -145,9 +143,9 @@ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bitflags"
version = "1.3.2"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "bstr"
@@ -378,6 +376,15 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569"
[[package]]
name = "error-chain"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
dependencies = [
"version_check",
]
[[package]]
name = "fancy-regex"
version = "0.7.1"
@@ -390,9 +397,9 @@ dependencies = [
[[package]]
name = "flate2"
version = "1.0.22"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0"
dependencies = [
"cfg-if",
"crc32fast",
@@ -590,9 +597,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.103"
version = "0.2.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
[[package]]
name = "libgit2-sys"
@@ -684,9 +691,9 @@ dependencies = [
[[package]]
name = "nix"
version = "0.23.0"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188"
checksum = "cf1e25ee6b412c2a1e3fcb6a4499a5c1bfe7f43e014bdce9a6b6666e5aa2d187"
dependencies = [
"bitflags",
"cc",
@@ -810,9 +817,9 @@ checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "predicates"
version = "2.0.2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c143348f141cc87aab5b950021bac6145d0e5ae754b0591de23244cee42c9308"
checksum = "bc3d91237f5de3bcd9d927e24d03b495adb6135097b001cea7403e2d573d00a9"
dependencies = [
"difflib",
"float-cmp",
@@ -991,18 +998,18 @@ checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
[[package]]
name = "serde"
version = "1.0.130"
version = "1.0.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.130"
version = "1.0.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc"
dependencies = [
"proc-macro2",
"quote",
@@ -1022,12 +1029,12 @@ dependencies = [
[[package]]
name = "serde_yaml"
version = "0.8.21"
version = "0.8.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8c608a35705a5d3cdc9fbe403147647ff34b921f8e833e49306df898f9b20af"
checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23"
dependencies = [
"dtoa",
"indexmap",
"linked-hash-map",
"serde",
"yaml-rust",
]
@@ -1181,26 +1188,6 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tinyvec"
version = "1.2.0"
@@ -1242,9 +1229,9 @@ dependencies = [
[[package]]
name = "unicode-width"
version = "0.1.9"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]]
name = "unicode-xid"
@@ -1276,6 +1263,12 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "wait-timeout"
version = "0.2.0"

View File

@@ -6,7 +6,7 @@ homepage = "https://github.com/sharkdp/bat"
license = "MIT/Apache-2.0"
name = "bat"
repository = "https://github.com/sharkdp/bat"
version = "0.18.3"
version = "0.18.2"
exclude = ["assets/syntaxes/*", "assets/themes/*"]
build = "build.rs"
edition = '2018'
@@ -33,7 +33,7 @@ minimal-application = [
"wild",
]
git = ["git2"] # Support indicating git modifications
paging = ["shell-words", "grep-cli"] # Support applying a pager on the output
paging = ["shell-words"] # Support applying a pager on the output
# Add "syntect/plist-load" when https://github.com/trishume/syntect/pull/345 reaches us
build-assets = ["syntect/yaml-load", "syntect/dump-create"]
@@ -45,17 +45,14 @@ regex-fancy = ["syntect/regex-fancy"] # Use the rust-only "fancy-regex" engine
atty = { version = "0.2.14", optional = true }
ansi_term = "^0.12.1"
ansi_colours = "^1.0"
bincode = "1.0"
console = "0.14.1"
flate2 = "1.0"
lazy_static = { version = "1.4", optional = true }
lazycell = "1.0"
thiserror = "1.0"
wild = { version = "2.0", optional = true }
content_inspector = "0.2.4"
encoding = "0.2"
shell-words = { version = "1.0.0", optional = true }
unicode-width = "0.1.9"
unicode-width = "0.1.8"
globset = "0.4"
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.8"
@@ -64,7 +61,7 @@ path_abs = { version = "0.5", default-features = false }
clircle = "0.3"
bugreport = { version = "0.4", optional = true }
dirs-next = { version = "2.0.0", optional = true }
grep-cli = { version = "0.1.6", optional = true }
grep-cli = "0.1.6"
[dependencies.git2]
version = "0.13"
@@ -74,7 +71,7 @@ default-features = false
[dependencies.syntect]
version = "4.6.0"
default-features = false
features = ["parsing"]
features = ["parsing", "dump-load"]
[dependencies.clap]
version = "2.33"
@@ -82,15 +79,19 @@ optional = true
default-features = false
features = ["suggestions", "color", "wrap_help"]
[dependencies.error-chain]
version = "0.12"
default-features = false
[dev-dependencies]
assert_cmd = "2.0.1"
assert_cmd = "1.0.8"
serial_test = "0.5.1"
predicates = "2.0.2"
predicates = "2.0.1"
wait-timeout = "0.2.0"
tempfile = "3.2.0"
[target.'cfg(unix)'.dev-dependencies]
nix = "0.23.0"
nix = "0.22.0"
[build-dependencies]
clap = { version = "2.33", optional = true }

View File

@@ -12,11 +12,7 @@
<a href="#installation">Installation</a>
<a href="#customization">Customization</a>
<a href="#project-goals-and-alternatives">Project goals, alternatives</a><br>
[English]
[<a href="https://github.com/chinanf-boy/bat-zh">中文</a>]
[<a href="doc/README-ja.md">日本語</a>]
[<a href="doc/README-ko.md">한국어</a>]
[<a href="doc/README-ru.md">Русский</a>]
[<a href="https://github.com/chinanf-boy/bat-zh">中文</a>] [<a href="doc/README-ja.md">日本語</a>] [<a href="doc/README-ko.md">한국어</a>] [<a href="doc/README-ru.md">Русский</a>]
</p>
### Syntax highlighting
@@ -200,7 +196,10 @@ The [`prettybat`](https://github.com/eth-p/bat-extras/blob/master/doc/prettybat.
### On Ubuntu (using `apt`)
*... and other Debian-based Linux distributions.*
`bat` is available on [Ubuntu since 20.04 ("Focal")](https://packages.ubuntu.com/search?keywords=bat&exact=1) and [Debian since August 2021 (Debian 11 - "Bullseye")](https://packages.debian.org/bullseye/bat).
`bat` is making its way through the [Ubuntu](https://packages.ubuntu.com/eoan/bat) and
[Debian](https://packages.debian.org/testing/bat) package release process, and is available
for Ubuntu as of Eoan 19.10. On Debian `bat` is currently available on the unstable
"Sid" branch and on the testing branch.
If your Ubuntu/Debian installation is new enough you can simply run:
@@ -223,7 +222,7 @@ the most recent release of `bat`, download the latest `.deb` package from the
[release page](https://github.com/sharkdp/bat/releases) and install it via:
```bash
sudo dpkg -i bat_0.18.3_amd64.deb # adapt version number and architecture
sudo dpkg -i bat_0.18.2_amd64.deb # adapt version number and architecture
```
### On Alpine Linux
@@ -252,14 +251,6 @@ You can install [the `bat` package](https://koji.fedoraproject.org/koji/packagei
dnf install bat
```
### On Funtoo Linux
You can install [the `bat` package](https://github.com/funtoo/dev-kit/tree/1.4-release/sys-apps/bat) from dev-kit.
```bash
emerge sys-apps/bat
```
### On Gentoo Linux
You can install [the `bat` package](https://packages.gentoo.org/packages/sys-apps/bat)
@@ -298,14 +289,6 @@ cd /usr/ports/textproc/bat
make install
```
### On OpenBSD
You can install `bat` package using [`pkg_add(1)`](https://man.openbsd.org/pkg_add.1):
```bash
pkg_add bat
```
### Via nix
You can install `bat` using the [nix package manager](https://nixos.org/nix):
@@ -380,7 +363,7 @@ binaries are also available: look for archives with `musl` in the file name.
### From source
If you want to build `bat` from source, you need Rust 1.46 or
If you want to build `bat` from source, you need Rust 1.45 or
higher. You can then use `cargo` to build everything:
```bash
@@ -615,7 +598,7 @@ Example configuration file:
# Use italic text on the terminal (not supported on all terminals)
--italic-text=always
# Use C++ syntax for Arduino .ino files
# Use C++ syntax for .ino files
--map-syntax "*.ino:C++"
```

View File

@@ -1,92 +0,0 @@
using namespace System.Management.Automation
using namespace System.Management.Automation.Language
Register-ArgumentCompleter -Native -CommandName '{{PROJECT_EXECUTABLE}}' -ScriptBlock {
param($wordToComplete, $commandAst, $cursorPosition)
$commandElements = $commandAst.CommandElements
$command = @(
'{{PROJECT_EXECUTABLE}}'
for ($i = 1; $i -lt $commandElements.Count; $i++) {
$element = $commandElements[$i]
if ($element -isnot [StringConstantExpressionAst] -or
$element.StringConstantType -ne [StringConstantType]::BareWord -or
$element.Value.StartsWith('-')) {
break
}
$element.Value
}) -join ';'
$completions = @(switch ($command) {
'{{PROJECT_EXECUTABLE}}' {
[CompletionResult]::new('-l', 'l', [CompletionResultType]::ParameterName, 'Set the language for syntax highlighting.')
[CompletionResult]::new('--language', 'language', [CompletionResultType]::ParameterName, 'Set the language for syntax highlighting.')
[CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'Highlight lines N through M.')
[CompletionResult]::new('--highlight-line', 'highlight-line', [CompletionResultType]::ParameterName, 'Highlight lines N through M.')
[CompletionResult]::new('--file-name', 'file-name', [CompletionResultType]::ParameterName, 'Specify the name to display for a file.')
[CompletionResult]::new('--diff-context', 'diff-context', [CompletionResultType]::ParameterName, 'diff-context')
[CompletionResult]::new('--tabs', 'tabs', [CompletionResultType]::ParameterName, 'Set the tab width to T spaces.')
[CompletionResult]::new('--wrap', 'wrap', [CompletionResultType]::ParameterName, 'Specify the text-wrapping mode (*auto*, never, character).')
[CompletionResult]::new('--terminal-width', 'terminal-width', [CompletionResultType]::ParameterName, 'Explicitly set the width of the terminal instead of determining it automatically. If prefixed with ''+'' or ''-'', the value will be treated as an offset to the actual terminal width. See also: ''--wrap''.')
[CompletionResult]::new('--color', 'color', [CompletionResultType]::ParameterName, 'When to use colors (*auto*, never, always).')
[CompletionResult]::new('--italic-text', 'italic-text', [CompletionResultType]::ParameterName, 'Use italics in output (always, *never*)')
[CompletionResult]::new('--decorations', 'decorations', [CompletionResultType]::ParameterName, 'When to show the decorations (*auto*, never, always).')
[CompletionResult]::new('--paging', 'paging', [CompletionResultType]::ParameterName, 'Specify when to use the pager, or use `-P` to disable (*auto*, never, always).')
[CompletionResult]::new('--pager', 'pager', [CompletionResultType]::ParameterName, 'Determine which pager to use.')
[CompletionResult]::new('-m', 'm', [CompletionResultType]::ParameterName, 'Use the specified syntax for files matching the glob pattern (''*.cpp:C++'').')
[CompletionResult]::new('--map-syntax', 'map-syntax', [CompletionResultType]::ParameterName, 'Use the specified syntax for files matching the glob pattern (''*.cpp:C++'').')
[CompletionResult]::new('--theme', 'theme', [CompletionResultType]::ParameterName, 'Set the color theme for syntax highlighting.')
[CompletionResult]::new('--style', 'style', [CompletionResultType]::ParameterName, 'Comma-separated list of style elements to display (*auto*, full, plain, changes, header, grid, rule, numbers, snip).')
[CompletionResult]::new('-r', 'r', [CompletionResultType]::ParameterName, 'Only print the lines from N to M.')
[CompletionResult]::new('--line-range', 'line-range', [CompletionResultType]::ParameterName, 'Only print the lines from N to M.')
[CompletionResult]::new('-A', 'A', [CompletionResultType]::ParameterName, 'Show non-printable characters (space, tab, newline, ..).')
[CompletionResult]::new('--show-all', 'show-all', [CompletionResultType]::ParameterName, 'Show non-printable characters (space, tab, newline, ..).')
[CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Show plain style (alias for ''--style=plain'').')
[CompletionResult]::new('--plain', 'plain', [CompletionResultType]::ParameterName, 'Show plain style (alias for ''--style=plain'').')
[CompletionResult]::new('-d', 'd', [CompletionResultType]::ParameterName, 'Only show lines that have been added/removed/modified.')
[CompletionResult]::new('--diff', 'diff', [CompletionResultType]::ParameterName, 'Only show lines that have been added/removed/modified.')
[CompletionResult]::new('-n', 'n', [CompletionResultType]::ParameterName, 'Show line numbers (alias for ''--style=numbers'').')
[CompletionResult]::new('--number', 'number', [CompletionResultType]::ParameterName, 'Show line numbers (alias for ''--style=numbers'').')
[CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'f')
[CompletionResult]::new('--force-colorization', 'force-colorization', [CompletionResultType]::ParameterName, 'force-colorization')
[CompletionResult]::new('-P', 'P', [CompletionResultType]::ParameterName, 'Alias for ''--paging=never''')
[CompletionResult]::new('--no-paging', 'no-paging', [CompletionResultType]::ParameterName, 'Alias for ''--paging=never''')
[CompletionResult]::new('--list-themes', 'list-themes', [CompletionResultType]::ParameterName, 'Display all supported highlighting themes.')
[CompletionResult]::new('-L', 'L', [CompletionResultType]::ParameterName, 'Display all supported languages.')
[CompletionResult]::new('--list-languages', 'list-languages', [CompletionResultType]::ParameterName, 'Display all supported languages.')
[CompletionResult]::new('-u', 'u', [CompletionResultType]::ParameterName, 'u')
[CompletionResult]::new('--unbuffered', 'unbuffered', [CompletionResultType]::ParameterName, 'unbuffered')
[CompletionResult]::new('--no-config', 'no-config', [CompletionResultType]::ParameterName, 'Do not use the configuration file')
[CompletionResult]::new('--no-custom-assets', 'no-custom-assets', [CompletionResultType]::ParameterName, 'Do not load custom assets')
[CompletionResult]::new('--config-file', 'config-file', [CompletionResultType]::ParameterName, 'Show path to the configuration file.')
[CompletionResult]::new('--generate-config-file', 'generate-config-file', [CompletionResultType]::ParameterName, 'Generates a default configuration file.')
[CompletionResult]::new('--config-dir', 'config-dir', [CompletionResultType]::ParameterName, 'Show bat''s configuration directory.')
[CompletionResult]::new('--cache-dir', 'cache-dir', [CompletionResultType]::ParameterName, 'Show bat''s cache directory.')
[CompletionResult]::new('--diagnostic', 'diagnostic', [CompletionResultType]::ParameterName, 'Show diagnostic information for bug reports.')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print this help message.')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print this help message.')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Show version information.')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Show version information.')
[CompletionResult]::new('cache', 'cache', [CompletionResultType]::ParameterValue, 'Modify the syntax-definition and theme cache')
break
}
'{{PROJECT_EXECUTABLE}};cache' {
[CompletionResult]::new('--source', 'source', [CompletionResultType]::ParameterName, 'Use a different directory to load syntaxes and themes from.')
[CompletionResult]::new('--target', 'target', [CompletionResultType]::ParameterName, 'Use a different directory to store the cached syntax and theme set.')
[CompletionResult]::new('-b', 'b', [CompletionResultType]::ParameterName, 'Initialize (or update) the syntax/theme cache.')
[CompletionResult]::new('--build', 'build', [CompletionResultType]::ParameterName, 'Initialize (or update) the syntax/theme cache.')
[CompletionResult]::new('-c', 'c', [CompletionResultType]::ParameterName, 'Remove the cached syntax definitions and themes.')
[CompletionResult]::new('--clear', 'clear', [CompletionResultType]::ParameterName, 'Remove the cached syntax definitions and themes.')
[CompletionResult]::new('--blank', 'blank', [CompletionResultType]::ParameterName, 'Create completely new syntax and theme sets (instead of appending to the default sets).')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
break
}
})
$completions.Where{ $_.CompletionText -like "$wordToComplete*" } |
Sort-Object -Property ListItemText
}

View File

@@ -56,7 +56,7 @@ complete -c {{PROJECT_EXECUTABLE}} -s P -d "Disable paging. Alias for '--paging=
complete -c {{PROJECT_EXECUTABLE}} -s A -l show-all -d "Show non-printable characters like space/tab/newline" -n "not __fish_seen_subcommand_from cache"
complete -c {{PROJECT_EXECUTABLE}} -l style -xka "auto full plain changes header grid rule numbers snip" -d "Comma-separated list of style elements or presets to display with file contents" -n "not __fish_seen_subcommand_from cache"
complete -c {{PROJECT_EXECUTABLE}} -l style -xka "auto full plain changes header grid numbers" -d "Comma-separated list of style elements or presets to display with file contents" -n "not __fish_seen_subcommand_from cache"
complete -c {{PROJECT_EXECUTABLE}} -l tabs -x -d "<T> Set the tab width to T spaces (width of 0 passes tabs through directly)" -n "not __fish_seen_subcommand_from cache"

View File

@@ -75,7 +75,7 @@ _{{PROJECT_EXECUTABLE}}_main() {
;;
style)
_values -s , 'style' auto full plain changes header grid rule numbers snip
_values -s , 'style' auto full plain changes header grid numbers snip
;;
esac
}

View File

@@ -50,8 +50,6 @@ highlights lines 30 to 40
highlights lines 1 to 40
.IP "\-\-highlight\-line 40:"
highlights lines 40 to the end of the file
.IP "\-\-highlight\-line 30:+10"
highlights lines 30 to 40
.RE
.HP
\fB\-\-file\-name\fR <name>...
@@ -156,8 +154,6 @@ prints lines 30 to 40
prints lines 1 to 40
.IP "\-\-line\-range 40:"
prints lines 40 to the end of the file
.IP "\-\-line\-range 30:+10"
prints lines 30 to 40
.RE
.HP
\fB\-L\fR, \fB\-\-list\-languages\fR

Binary file not shown.

View File

@@ -1,52 +0,0 @@
%YAML 1.2
---
# http://www.sublimetext.com/docs/3/syntax.html
name: Racket
file_extensions:
- rkt
scope: source.racket
contexts:
main:
- match: '[^\\](\"[^\"]*\")'
captures:
1: string.quoted.double.source.racket
- match: '\((define)\s+([a-zA-Z0-9_\-?\+^]+)\s*'
scope: meta.variable.source.racket
captures:
1: keyword.source.racket
2: entity.name.variable.source.racket
- match: '\((define)\s+\(([a-zA-Z0-9_\-?\+^]+)\s*'
scope: meta.function.source.racket
captures:
1: keyword.source.racket
2: entity.name.function
- match: '\((struct)\s+([a-zA-Z0-9_\-?\+^]+)\s+'
scope: meta.struct.source.racket
captures:
1: keyword.source.racket
2: entity.name.type
- match: '[\s\(](if|lambda|cond|define|type-case|let|letrec|let!|\#lang|require|test|else|first|rest|define-type|define-type-alias|define-struct|not|local|error|lang)[\s\)]'
scope: meta.keywords.source.racket
captures:
1: keyword.source.racket
- match: '[\s\(](true|false|empty|null)[\s\)]'
captures:
1: constant.language.source.racket
- match: '[\s\(\[\{](#t|#true|#f|#false)[\s\)\]\}]'
captures:
1: constant.language.source.racket
- match: '(#\\[a-zA-Z0-9_\-?\+\.\!\"]+)'
captures:
1: constant.language.source.racket
- match: '\b(0|([1-9][0-9_]*))\b'
scope: constant.numeric.integer.source.racket
- match: ;
push:
- meta_scope: comment.line.documentation.source.racket
- match: $\n
pop: true
- match: '#\|'
push:
- meta_scope: comment.block.source.racket
- match: '\|#'
pop: true

View File

@@ -64,11 +64,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
"assets/completions/bat.fish.in",
out_dir.join("assets/completions/bat.fish"),
)?;
template(
&variables,
"assets/completions/_bat.ps1.in",
out_dir.join("assets/completions/_bat.ps1"),
)?;
template(
&variables,
"assets/completions/bat.zsh.in",

View File

@@ -1,6 +1,7 @@
<p align="center">
<img src="logo-header.svg" alt="bat - a cat clone with wings"><br>
<a href="https://github.com/sharkdp/bat/actions?query=workflow%3ACICD"><img src="https://github.com/sharkdp/bat/workflows/CICD/badge.svg" alt="Build Status"></a>
<a href="https://travis-ci.org/sharkdp/bat"><img src="https://travis-ci.org/sharkdp/bat.svg?branch=master" alt="Build Status"></a>
<a href="https://ci.appveyor.com/project/sharkdp/bat"><img src="https://ci.appveyor.com/api/projects/status/cptsmtbiwbnr2vhf?svg=true"></a>
<img src="https://img.shields.io/crates/l/bat.svg" alt="license">
<a href="https://crates.io/crates/bat"><img src="https://img.shields.io/crates/v/bat.svg?colorB=319e8c" alt="Version info"></a><br>
シンタックスハイライトとGitとの連携機能付きの <i>cat(1)</i> クローン。
@@ -11,12 +12,8 @@
<a href="#使い方">使い方</a>
<a href="#インストール">インストール</a>
<a href="#カスタマイズ">カスタマイズ</a>
<a href="#プロジェクトの目標と既存の類似したOSS">プロジェクトの目標と既存の類似したOSS</a><br>
[<a href="../README.md">English</a>]
[<a href="https://github.com/chinanf-boy/bat-zh">中文</a>]
[日本語]
[<a href="README-ko.md">한국어</a>]
[<a href="README-ru.md">Русский</a>]
<a href="#プロジェクトの目標と既存の類似したOSS">プロジェクトの目標と既存の類似したOSS</a>
翻訳 [<a href="https://github.com/chinanf-boy/bat-zh">中文</a>][<a href="README-ja.md">日本語</a>][<a href="README-ko.md">한국어</a>]
</p>
### シンタックスハイライト
@@ -205,7 +202,7 @@ ln -s /usr/bin/batcat ~/.local/bin/bat
batの最新リリースを実行する場合、または Ubuntu/Debian の古いバージョンを使用している場合は、[release page](https://github.com/sharkdp/bat/releases) から最新の `.deb` パッケージをダウンロードし、
次の方法でインストールします:
```bash
sudo dpkg -i bat_0.18.3_amd64.deb # adapt version number and architecture
sudo dpkg -i bat_0.18.2_amd64.deb # adapt version number and architecture
```
### On Alpine Linux
@@ -525,7 +522,7 @@ export BAT_CONFIG_PATH="/path/to/bat.conf"
# Use italic text on the terminal (not supported on all terminals)
--italic-text=always
# Use C++ syntax for Arduino .ino files
# Use C++ syntax for .ino files
--map-syntax "*.ino:C++"
# Use ".gitignore"-style highlighting for ".ignore" files

View File

@@ -1,5 +1,5 @@
<p align="center">
<img src="logo-header.svg" alt="bat - a cat clone with wings"><br>
<img src="../doc/logo-header.svg" alt="bat - a cat clone with wings"><br>
<a href="https://github.com/sharkdp/bat/actions?query=workflow%3ACICD"><img src="https://github.com/sharkdp/bat/workflows/CICD/badge.svg" alt="Build Status"></a>
<img src="https://img.shields.io/crates/l/bat.svg" alt="license">
<a href="https://crates.io/crates/bat"><img src="https://img.shields.io/crates/v/bat.svg?colorB=319e8c" alt="Version info"></a><br>
@@ -11,12 +11,8 @@
<a href="#사용법">사용법</a>
<a href="#설치">설치</a>
<a href="#사용자화">사용자화</a>
<a href="#프로젝트-목표와-대안들">프로젝트 목표와 대안들</a><br>
[<a href="../README.md">English</a>]
[<a href="https://github.com/chinanf-boy/bat-zh">中文</a>]
[<a href="README-ja.md">日本語</a>]
[한국어]
[<a href="README-ru.md">Русский</a>]
<a href="#프로젝트-목표와-대안들">프로젝트 목표와 대안들</a>
[<a href="https://github.com/chinanf-boy/bat-zh">中文</a>] [<a href="../doc/README-ja.md">日本語</a>] [<a href="../doc/README-ko.md">한국어</a>] [<a href="../doc/README-ru.md">Русский</a>]
</p>
### 문법 강조
@@ -248,7 +244,7 @@ ln -s /usr/bin/batcat ~/.local/bin/bat
다음과 같이 `.deb` 패키지를 받아 설치하세요:
```bash
sudo dpkg -i bat_0.18.3_amd64.deb # adapt version number and architecture
sudo dpkg -i bat_0.18.2_amd64.deb # adapt version number and architecture
```
### Alpine Linux에서
@@ -283,14 +279,6 @@ pacman -S bat
dnf install bat
```
### Funtoo Linux에서
dev-kit을 통해 [`bat` 패키지](https://github.com/funtoo/dev-kit/tree/1.4-release/sys-apps/bat)를 설치할 수 있습니다:
```bash
emerge sys-apps/bat
```
### Gentoo Linux에서
공식 소스를 통해
@@ -416,7 +404,7 @@ scoop install bat
### 소스에서
`bat`의 소스를 빌드하기 위해서는, Rust 1.46 이상이 필요합니다.
`bat`의 소스를 빌드하기 위해서는, Rust 1.45 이상이 필요합니다.
`cargo`를 이용해 전부 빌드할 수 있습니다:
```bash
@@ -675,7 +663,7 @@ bat --generate-config-file
# 터미널에서 이탤릭체 쓰기 (일부 터미널에서 미지원)
--italic-text=always
# Arduino .ino 파일에 C++ 문법 쓰기
# .ino 파일에 C++ 문법 쓰기
--map-syntax "*.ino:C++"
```
@@ -809,10 +797,6 @@ cargo install --locked --force
- [keith-hall](https://github.com/keith-hall)
- [Enselic](https://github.com/Enselic)
## 보안 취약점
만약 `bat`의 취약점을 발견하였다면, [David Peter](https://david-peter.de/)에게 메일로 연락주시기 바랍니다.
## 프로젝트 목표와 대안들
`bat`은 다음과 같은 목표를 달성하려고 합니다:

View File

@@ -1,6 +1,7 @@
<p align="center">
<img src="logo-header.svg" alt="bat - a cat clone with wings"><br>
<a href="https://github.com/sharkdp/bat/actions?query=workflow%3ACICD"><img src="https://github.com/sharkdp/bat/workflows/CICD/badge.svg" alt="Build Status"></a>
<img src="doc/logo-header.svg" alt="bat - a cat clone with wings"><br>
<a href="https://travis-ci.org/sharkdp/bat"><img src="https://travis-ci.org/sharkdp/bat.svg?branch=master" alt="Build Status"></a>
<a href="https://ci.appveyor.com/project/sharkdp/bat"><img src="https://ci.appveyor.com/api/projects/status/cptsmtbiwbnr2vhf/branch/master?svg=true"></a>
<img src="https://img.shields.io/crates/l/bat.svg" alt="license">
<a href="https://crates.io/crates/bat"><img src="https://img.shields.io/crates/v/bat.svg?colorB=319e8c" alt="Version info"></a><br>
Клон утилиты <i>cat(1)</i> с поддержкой выделения синтаксиса и Git
@@ -11,12 +12,8 @@
<a href="#как-использовать">Использование</a>
<a href="#установка">Установка</a>
<a href="#кастомизация">Кастомизация</a>
<a href="#цели-и-альтернативы">Цели и альтернативы </a><br>
[<a href="../README.md">English]
[<a href="https://github.com/chinanf-boy/bat-zh">中文</a>]
[<a href="README-ja.md">日本語</a>]
[<a href="README-ko.md">한국어</a>]
[Русский]
<a href="#цели-и-альтернативы">Цели и альтернативы </a>
Перевод [<a href="https://github.com/chinanf-boy/bat-zh">中文</a>][<a href="doc/README-ja.md">日本語</a>][<a href="doc/README-ko.md">한국어</a>][<a href="doc/README-ru.md">Русский</a>]
</p>
### Выделение синтаксиса
@@ -188,7 +185,7 @@ ln -s /usr/bin/batcat ~/.local/bin/bat
[release page](https://github.com/sharkdp/bat/releases) и установить так:
```bash
sudo dpkg -i bat_0.18.3_amd64.deb # измените архитектуру и версию
sudo dpkg -i bat_0.18.2_amd64.deb # измените архитектуру и версию
```
### Alpine Linux
@@ -344,7 +341,7 @@ ansible-galaxy install aeimer.install_bat
### Из исходников
Если вы желаете установить `bat` из исходников, вам понадобится Rust 1.46 или выше. После этого используйте `cargo`, чтобы все скомпилировать:
Если вы желаете установить `bat` из исходников, вам понадобится Rust 1.45 или выше. После этого используйте `cargo`, чтобы все скомпилировать:
```bash
cargo install --locked bat
@@ -484,7 +481,7 @@ bat --generate-config-file
# Использовать курсив (поддерживается не всеми терминалами)
--italic-text=always
# Использовать синтаксис C++ для всех Arduino .ino файлов
# Использовать синтаксис C++ для всех .ino файлов
--map-syntax "*.ino:C++"
# Использовать синтаксик Git Ignore для всех файлов .ignore

View File

@@ -45,7 +45,7 @@ See this page for a good overview: https://deps.rs/repo/github/sharkdp/bat
## Release
- [ ] Create a tag and push it: `git tag vX.Y.Z; git push origin tag vX.Y.Z`.
- [ ] Create a tag and push it to the remote `git tag vX.Y.Z; git push --tags`.
This will trigger the deployment via GitHub Actions.
- [ ] Go to https://github.com/sharkdp/bat/releases/new to create the new
release. Select the new tag and also use it as the release title. For the

View File

@@ -1,9 +1,10 @@
use std::ffi::OsStr;
use std::fs;
use std::path::Path;
use std::path::{Path, PathBuf};
use lazycell::LazyCell;
use syntect::dumps::{from_binary, from_reader};
use syntect::highlighting::{Theme, ThemeSet};
use syntect::parsing::{SyntaxReference, SyntaxSet};
@@ -11,30 +12,13 @@ use path_abs::PathAbs;
use crate::bat_warning;
use crate::error::*;
use crate::input::{InputReader, OpenedInput};
use crate::input::{InputReader, OpenedInput, OpenedInputKind};
use crate::syntax_mapping::{MappingTarget, SyntaxMapping};
use ignored_suffixes::*;
use minimal_assets::*;
use serialized_syntax_set::*;
#[cfg(feature = "build-assets")]
pub use crate::assets::build_assets::*;
pub(crate) mod assets_metadata;
#[cfg(feature = "build-assets")]
mod build_assets;
mod ignored_suffixes;
mod minimal_assets;
mod serialized_syntax_set;
#[derive(Debug)]
pub struct HighlightingAssets {
syntax_set_cell: LazyCell<SyntaxSet>,
serialized_syntax_set: SerializedSyntaxSet,
minimal_assets: MinimalAssets,
serialized_syntax_set: Option<SerializedSyntaxSet>,
theme_set: ThemeSet,
fallback_theme: Option<&'static str>,
}
@@ -45,33 +29,39 @@ pub struct SyntaxReferenceInSet<'a> {
pub syntax_set: &'a SyntaxSet,
}
/// Compress for size of ~700 kB instead of ~4600 kB at the cost of ~30% longer deserialization time
pub(crate) const COMPRESS_SYNTAXES: bool = true;
/// Compress for size of ~20 kB instead of ~200 kB at the cost of ~30% longer deserialization time
pub(crate) const COMPRESS_THEMES: bool = true;
/// Compress for size of ~400 kB instead of ~2100 kB at the cost of ~30% longer deserialization time
pub(crate) const COMPRESS_SERIALIZED_MINIMAL_SYNTAXES: bool = true;
/// Whether or not to compress the serialized form of [MinimalSyntaxes]. Shall
/// always be `false`, because the data in
/// [MinimalSyntaxes.serialized_syntax_sets] has already been compressed
/// (assuming [COMPRESS_SERIALIZED_MINIMAL_SYNTAXES] is `true`). The "outer" data
/// structures like `by_name` are tiny. If we compress, deserialization can't do
/// efficient byte-by-byte copy of `serialized_syntax_sets`.
pub(crate) const COMPRESS_MINIMAL_SYNTAXES: bool = false;
const IGNORED_SUFFIXES: [&str; 10] = [
// Editor etc backups
"~",
".bak",
".old",
".orig",
// Debian and derivatives apt/dpkg backups
".dpkg-dist",
".dpkg-old",
// Red Hat and derivatives rpm backups
".rpmnew",
".rpmorig",
".rpmsave",
// Build system input/template files
".in",
];
impl HighlightingAssets {
fn new(
serialized_syntax_set: SerializedSyntaxSet,
minimal_syntaxes: MinimalSyntaxes,
syntax_set: Option<SyntaxSet>,
serialized_syntax_set: Option<SerializedSyntaxSet>,
theme_set: ThemeSet,
) -> Self {
assert!(syntax_set.is_some() || serialized_syntax_set.is_some());
let syntax_set_cell = LazyCell::new();
if let Some(syntax_set) = syntax_set {
syntax_set_cell.fill(syntax_set).expect("can never fail");
}
HighlightingAssets {
syntax_set_cell: LazyCell::new(),
syntax_set_cell,
serialized_syntax_set,
minimal_assets: MinimalAssets::new(minimal_syntaxes),
theme_set,
fallback_theme: None,
}
@@ -81,33 +71,127 @@ impl HighlightingAssets {
"Monokai Extended"
}
#[cfg(feature = "build-assets")]
pub fn from_files(source_dir: &Path, include_integrated_assets: bool) -> Result<Self> {
let mut theme_set = if include_integrated_assets {
get_integrated_themeset()
} else {
ThemeSet::new()
};
let theme_dir = source_dir.join("themes");
if theme_dir.exists() {
let res = theme_set.add_from_folder(&theme_dir);
if let Err(err) = res {
println!(
"Failed to load one or more themes from '{}' (reason: '{}')",
theme_dir.to_string_lossy(),
err,
);
}
} else {
println!(
"No themes were found in '{}', using the default set",
theme_dir.to_string_lossy()
);
}
let mut syntax_set_builder = if !include_integrated_assets {
let mut builder = syntect::parsing::SyntaxSetBuilder::new();
builder.add_plain_text_syntax();
builder
} else {
from_binary::<SyntaxSet>(get_serialized_integrated_syntaxset()).into_builder()
};
let syntax_dir = source_dir.join("syntaxes");
if syntax_dir.exists() {
syntax_set_builder.add_from_folder(syntax_dir, true)?;
} else {
println!(
"No syntaxes were found in '{}', using the default set.",
syntax_dir.to_string_lossy()
);
}
if std::env::var("BAT_PRINT_SYNTAX_DEPENDENCIES").is_ok() {
// To trigger this code, run:
// BAT_PRINT_SYNTAX_DEPENDENCIES=1 cargo run -- cache --build --source assets --blank --target /tmp
crate::syntax_dependencies::print_syntax_dependencies(&syntax_set_builder);
}
let syntax_set = syntax_set_builder.build();
let missing_contexts = syntax_set.find_unlinked_contexts();
if !missing_contexts.is_empty() {
println!("Some referenced contexts could not be found!");
for context in missing_contexts {
println!("- {}", context);
}
}
Ok(HighlightingAssets::new(Some(syntax_set), None, theme_set))
}
pub fn from_cache(cache_path: &Path) -> Result<Self> {
Ok(HighlightingAssets::new(
SerializedSyntaxSet::FromFile(cache_path.join("syntaxes.bin")),
asset_from_cache(
&cache_path.join("minimal_syntaxes.bin"),
"minimal syntax sets",
COMPRESS_MINIMAL_SYNTAXES,
)?,
asset_from_cache(&cache_path.join("themes.bin"), "theme set", COMPRESS_THEMES)?,
None,
Some(SerializedSyntaxSet::FromFile(
cache_path.join("syntaxes.bin"),
)),
asset_from_cache(&cache_path.join("themes.bin"), "theme set")?,
))
}
pub fn from_binary() -> Self {
HighlightingAssets::new(
SerializedSyntaxSet::FromBinary(get_serialized_integrated_syntaxset()),
get_integrated_minimal_syntaxes(),
None,
Some(SerializedSyntaxSet::FromBinary(
get_serialized_integrated_syntaxset(),
)),
get_integrated_themeset(),
)
}
#[cfg(feature = "build-assets")]
pub fn save_to_cache(&self, target_dir: &Path, current_version: &str) -> Result<()> {
let _ = fs::create_dir_all(target_dir);
asset_to_cache(
self.get_theme_set(),
&target_dir.join("themes.bin"),
"theme set",
)?;
asset_to_cache(
self.get_syntax_set()?,
&target_dir.join("syntaxes.bin"),
"syntax set",
)?;
print!(
"Writing metadata to folder {} ... ",
target_dir.to_string_lossy()
);
crate::assets_metadata::AssetsMetadata::new(current_version).save_to_folder(target_dir)?;
println!("okay");
Ok(())
}
pub fn set_fallback_theme(&mut self, theme: &'static str) {
self.fallback_theme = Some(theme);
}
pub(crate) fn get_syntax_set(&self) -> Result<&SyntaxSet> {
self.syntax_set_cell
.try_borrow_with(|| self.serialized_syntax_set.deserialize())
if !self.syntax_set_cell.filled() {
self.syntax_set_cell.fill(
self.serialized_syntax_set
.as_ref()
.expect("a dev forgot to setup serialized_syntax_set, please report to https://github.com/sharkdp/bat/issues")
.deserialize()?
).unwrap();
}
// It is safe to .unwrap() because we just made sure it was .filled()
Ok(self.syntax_set_cell.borrow().unwrap())
}
/// Use [Self::get_syntaxes] instead
@@ -130,68 +214,36 @@ impl HighlightingAssets {
self.get_theme_set().themes.keys().map(|s| s.as_ref())
}
/// Finds a [SyntaxSet] that contains a [SyntaxReference] by its name. First
/// tries to find a minimal [SyntaxSet]. If none is found, returns the
/// [SyntaxSet] that contains all syntaxes.
fn get_syntax_set_by_name(&self, name: &str) -> Result<&SyntaxSet> {
match self.minimal_assets.get_syntax_set_by_name(name) {
Some(syntax_set) => Ok(syntax_set),
None => self.get_syntax_set(),
}
}
/// Use [Self::get_syntax_for_path] instead
/// Use [Self::get_syntax_for_file_name] instead
#[deprecated]
pub fn syntax_for_file_name(
&self,
file_name: impl AsRef<Path>,
mapping: &SyntaxMapping,
) -> Option<&SyntaxReference> {
self.get_syntax_for_path(file_name, mapping)
.ok()
self.get_syntax_for_file_name(file_name, mapping)
.expect(
".syntax_for_file_name() is deprecated, use .get_syntax_for_file_name() instead",
)
.map(|syntax_in_set| syntax_in_set.syntax)
}
/// Detect the syntax based on, in order:
/// 1. Syntax mappings (e.g. `/etc/profile` -> `Bourne Again Shell (bash)`)
/// 2. The file name (e.g. `Dockerfile`)
/// 3. The file name extension (e.g. `.rs`)
///
/// When detecting syntax based on syntax mappings, the full path is taken
/// into account. When detecting syntax based on file name, no regard is
/// taken to the path of the file. Only the file name itself matters. When
/// detecting syntax based on file name extension, only the file name
/// extension itself matters.
///
/// Returns [Error::UndetectedSyntax] if it was not possible detect syntax
/// based on path/file name/extension (or if the path was mapped to
/// [MappingTarget::MapToUnknown]). In this case it is appropriate to fall
/// back to other methods to detect syntax. Such as using the contents of
/// the first line of the file.
///
/// Returns [Error::UnknownSyntax] if a syntax mapping exist, but the mapped
/// syntax does not exist.
pub fn get_syntax_for_path(
pub fn get_syntax_for_file_name(
&self,
path: impl AsRef<Path>,
file_name: impl AsRef<Path>,
mapping: &SyntaxMapping,
) -> Result<SyntaxReferenceInSet> {
let path = path.as_ref();
match mapping.get_syntax_for(path) {
Some(MappingTarget::MapToUnknown) => {
Err(Error::UndetectedSyntax(path.to_string_lossy().into()))
) -> Result<Option<SyntaxReferenceInSet>> {
let file_name = file_name.as_ref();
Ok(match mapping.get_syntax_for(file_name) {
Some(MappingTarget::MapToUnknown) => None,
Some(MappingTarget::MapTo(syntax_name)) => {
let syntax_set = self.get_syntax_set()?;
syntax_set
.find_syntax_by_name(syntax_name)
.map(|syntax| SyntaxReferenceInSet { syntax, syntax_set })
}
Some(MappingTarget::MapTo(syntax_name)) => self
.find_syntax_by_name(syntax_name)?
.ok_or_else(|| Error::UnknownSyntax(syntax_name.to_owned())),
None => {
let file_name = path.file_name().unwrap_or_default();
self.get_extension_syntax(file_name)?
.ok_or_else(|| Error::UndetectedSyntax(path.to_string_lossy().into()))
}
}
None => self.get_extension_syntax(file_name.as_os_str())?,
})
}
pub(crate) fn get_theme(&self, theme: &str) -> &Theme {
@@ -218,60 +270,113 @@ impl HighlightingAssets {
mapping: &SyntaxMapping,
) -> Result<SyntaxReferenceInSet> {
if let Some(language) = language {
let syntax_set = self.get_syntax_set_by_name(language)?;
return syntax_set
let syntax_set = self.get_syntax_set()?;
syntax_set
.find_syntax_by_token(language)
.map(|syntax| SyntaxReferenceInSet { syntax, syntax_set })
.ok_or_else(|| Error::UnknownSyntax(language.to_owned()));
}
let path = input.path();
let path_syntax = if let Some(path) = path {
self.get_syntax_for_path(
PathAbs::new(path).map_or_else(|_| path.to_owned(), |p| p.as_path().to_path_buf()),
mapping,
)
.ok_or_else(|| ErrorKind::UnknownSyntax(language.to_owned()).into())
} else {
Err(Error::UndetectedSyntax("[unknown]".into()))
};
let line_syntax = self.get_first_line_syntax(&mut input.reader)?;
match path_syntax {
// If a path wasn't provided, or if path based syntax detection
// above failed, we fall back to first-line syntax detection.
Err(Error::UndetectedSyntax(path)) => self
.get_first_line_syntax(&mut input.reader)?
.ok_or(Error::UndetectedSyntax(path)),
_ => path_syntax,
// Get the path of the file:
// If this was set by the metadata, that will take priority.
// If it wasn't, it will use the real file path (if available).
let path_str =
input
.metadata
.user_provided_name
.as_ref()
.or_else(|| match input.kind {
OpenedInputKind::OrdinaryFile(ref path) => Some(path),
_ => None,
});
if let Some(path_str) = path_str {
// If a path was provided, we try and detect the syntax based on extension mappings.
let path = Path::new(path_str);
let absolute_path = PathAbs::new(path)
.ok()
.map(|p| p.as_path().to_path_buf())
.unwrap_or_else(|| path.to_owned());
match mapping.get_syntax_for(absolute_path) {
Some(MappingTarget::MapToUnknown) => line_syntax.ok_or_else(|| {
ErrorKind::UndetectedSyntax(path.to_string_lossy().into()).into()
}),
Some(MappingTarget::MapTo(syntax_name)) => {
let syntax_set = self.get_syntax_set()?;
syntax_set
.find_syntax_by_name(syntax_name)
.map(|syntax| SyntaxReferenceInSet { syntax, syntax_set })
.ok_or_else(|| ErrorKind::UnknownSyntax(syntax_name.to_owned()).into())
}
None => {
let file_name = path.file_name().unwrap_or_default();
self.get_extension_syntax(file_name)?
.or(line_syntax)
.ok_or_else(|| {
ErrorKind::UndetectedSyntax(path.to_string_lossy().into()).into()
})
}
}
} else {
// If a path wasn't provided, we fall back to the detect first-line syntax.
line_syntax.ok_or_else(|| ErrorKind::UndetectedSyntax("[unknown]".into()).into())
}
}
}
pub(crate) fn find_syntax_by_name(
&self,
syntax_name: &str,
) -> Result<Option<SyntaxReferenceInSet>> {
let syntax_set = self.get_syntax_set()?;
Ok(syntax_set
.find_syntax_by_name(syntax_name)
.map(|syntax| SyntaxReferenceInSet { syntax, syntax_set }))
}
fn find_syntax_by_extension(&self, e: Option<&OsStr>) -> Result<Option<SyntaxReferenceInSet>> {
let syntax_set = self.get_syntax_set()?;
let extension = e.and_then(|x| x.to_str()).unwrap_or_default();
Ok(syntax_set
.find_syntax_by_extension(extension)
.map(|syntax| SyntaxReferenceInSet { syntax, syntax_set }))
}
fn get_extension_syntax(&self, file_name: &OsStr) -> Result<Option<SyntaxReferenceInSet>> {
let mut syntax = self.find_syntax_by_extension(Some(file_name))?;
let mut syntax = self.find_syntax_by_file_name(file_name)?;
if syntax.is_none() {
syntax = self.find_syntax_by_extension(Path::new(file_name).extension())?;
syntax = self.find_syntax_by_file_name_extension(file_name)?;
}
if syntax.is_none() {
syntax = try_with_stripped_suffix(file_name, |stripped_file_name| {
self.get_extension_syntax(stripped_file_name) // Note: recursion
})?;
syntax = self.get_extension_syntax_with_stripped_suffix(file_name)?;
}
Ok(syntax)
}
fn find_syntax_by_file_name(&self, file_name: &OsStr) -> Result<Option<SyntaxReferenceInSet>> {
let syntax_set = self.get_syntax_set()?;
Ok(syntax_set
.find_syntax_by_extension(file_name.to_str().unwrap_or_default())
.map(|syntax| SyntaxReferenceInSet { syntax, syntax_set }))
}
fn find_syntax_by_file_name_extension(
&self,
file_name: &OsStr,
) -> Result<Option<SyntaxReferenceInSet>> {
let file_path = Path::new(file_name);
let syntax_set = self.get_syntax_set()?;
Ok(syntax_set
.find_syntax_by_extension(
file_path
.extension()
.and_then(|x| x.to_str())
.unwrap_or_default(),
)
.map(|syntax| SyntaxReferenceInSet { syntax, syntax_set }))
}
/// If we find an ignored suffix on the file name, e.g. '~', we strip it and
/// then try again to find a syntax without it. Note that we do this recursively.
fn get_extension_syntax_with_stripped_suffix(
&self,
file_name: &OsStr,
) -> Result<Option<SyntaxReferenceInSet>> {
let file_path = Path::new(file_name);
let mut syntax = None;
if let Some(file_str) = file_path.to_str() {
for suffix in IGNORED_SUFFIXES.iter() {
if let Some(stripped_filename) = file_str.strip_suffix(suffix) {
syntax = self.get_extension_syntax(OsStr::new(stripped_filename))?;
break;
}
}
}
Ok(syntax)
}
@@ -288,53 +393,57 @@ impl HighlightingAssets {
}
}
pub(crate) fn get_serialized_integrated_syntaxset() -> &'static [u8] {
/// A SyntaxSet in serialized form, i.e. bincoded and flate2 compressed.
/// We keep it in this format since we want to load it lazily.
#[derive(Debug)]
enum SerializedSyntaxSet {
/// The data comes from a user-generated cache file.
FromFile(PathBuf),
/// The data to use is embedded into the bat binary.
FromBinary(&'static [u8]),
}
impl SerializedSyntaxSet {
fn deserialize(&self) -> Result<SyntaxSet> {
match self {
SerializedSyntaxSet::FromBinary(data) => Ok(from_binary(data)),
SerializedSyntaxSet::FromFile(ref path) => asset_from_cache(path, "syntax set"),
}
}
}
fn get_serialized_integrated_syntaxset() -> &'static [u8] {
include_bytes!("../assets/syntaxes.bin")
}
pub(crate) fn get_integrated_themeset() -> ThemeSet {
from_binary(include_bytes!("../assets/themes.bin"), COMPRESS_THEMES)
fn get_integrated_themeset() -> ThemeSet {
from_binary(include_bytes!("../assets/themes.bin"))
}
fn get_integrated_minimal_syntaxes() -> MinimalSyntaxes {
from_binary(
include_bytes!("../assets/minimal_syntaxes.bin"),
COMPRESS_MINIMAL_SYNTAXES,
)
#[cfg(feature = "build-assets")]
fn asset_to_cache<T: serde::Serialize>(asset: &T, path: &Path, description: &str) -> Result<()> {
print!("Writing {} to {} ... ", description, path.to_string_lossy());
syntect::dumps::dump_to_file(asset, &path).chain_err(|| {
format!(
"Could not save {} to {}",
description,
path.to_string_lossy()
)
})?;
println!("okay");
Ok(())
}
pub(crate) fn from_binary<T: serde::de::DeserializeOwned>(v: &[u8], compressed: bool) -> T {
asset_from_contents(v, "n/a", compressed)
.expect("data integrated in binary is never faulty, but make sure `compressed` is in sync!")
}
fn asset_from_contents<T: serde::de::DeserializeOwned>(
contents: &[u8],
description: &str,
compressed: bool,
) -> Result<T> {
if compressed {
bincode::deserialize_from(flate2::read::ZlibDecoder::new(contents))
} else {
bincode::deserialize_from(contents)
}
.map_err(|_| format!("Could not parse {}", description).into())
}
fn asset_from_cache<T: serde::de::DeserializeOwned>(
path: &Path,
description: &str,
compressed: bool,
) -> Result<T> {
let contents = fs::read(path).map_err(|_| {
fn asset_from_cache<T: serde::de::DeserializeOwned>(path: &Path, description: &str) -> Result<T> {
let contents = fs::read(path).chain_err(|| {
format!(
"Could not load cached {} '{}'",
description,
path.to_string_lossy()
)
})?;
asset_from_contents(&contents[..], description, compressed)
.map_err(|_| format!("Could not parse cached {}", description).into())
from_reader(&contents[..]).chain_err(|| format!("Could not parse cached {}", description))
}
#[cfg(test)]

View File

@@ -1,500 +0,0 @@
use std::collections::HashMap;
use std::path::Path;
use syntect::highlighting::ThemeSet;
use syntect::parsing::syntax_definition::{
ContextReference, MatchOperation, MatchPattern, Pattern, SyntaxDefinition,
};
use syntect::parsing::{Scope, SyntaxSet, SyntaxSetBuilder};
use crate::assets::*;
mod graphviz_utils;
type SyntaxName = String;
/// Used to look up which [SyntaxDefinition] corresponds to a given [OtherSyntax]
type OtherSyntaxLookup<'a> = HashMap<OtherSyntax, &'a SyntaxDefinition>;
/// Used to look up what dependencies a given [SyntaxDefinition] has
type SyntaxToDependencies = HashMap<SyntaxName, Vec<OtherSyntax>>;
/// Used to look up what other [SyntaxDefinition]s depend on a given [SyntaxDefinition]
type SyntaxToDependents<'a> = HashMap<SyntaxName, Vec<OtherSyntax>>;
/// Represents some other `*.sublime-syntax` file, i.e. another [SyntaxDefinition].
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Clone, Hash)]
pub(crate) enum OtherSyntax {
/// By name. Example YAML: `include: C.sublime-syntax` (name is `"C"`)
ByName(String),
/// By scope. Example YAML: `embed: scope:source.c` (scope is `"source.c"`)
ByScope(Scope),
}
pub fn build(
source_dir: &Path,
include_integrated_assets: bool,
target_dir: &Path,
current_version: &str,
) -> Result<()> {
let theme_set = build_theme_set(source_dir, include_integrated_assets);
let syntax_set_builder = build_syntax_set_builder(source_dir, include_integrated_assets)?;
let minimal_syntaxes = build_minimal_syntaxes(&syntax_set_builder, include_integrated_assets)?;
let syntax_set = syntax_set_builder.build();
print_unlinked_contexts(&syntax_set);
write_assets(
&theme_set,
&syntax_set,
&minimal_syntaxes,
target_dir,
current_version,
)
}
fn build_theme_set(source_dir: &Path, include_integrated_assets: bool) -> ThemeSet {
let mut theme_set = if include_integrated_assets {
crate::assets::get_integrated_themeset()
} else {
ThemeSet::new()
};
let theme_dir = source_dir.join("themes");
if theme_dir.exists() {
let res = theme_set.add_from_folder(&theme_dir);
if let Err(err) = res {
println!(
"Failed to load one or more themes from '{}' (reason: '{}')",
theme_dir.to_string_lossy(),
err,
);
}
} else {
println!(
"No themes were found in '{}', using the default set",
theme_dir.to_string_lossy()
);
}
theme_set
}
fn build_syntax_set_builder(
source_dir: &Path,
include_integrated_assets: bool,
) -> Result<SyntaxSetBuilder> {
let mut syntax_set_builder = if !include_integrated_assets {
let mut builder = syntect::parsing::SyntaxSetBuilder::new();
builder.add_plain_text_syntax();
builder
} else {
from_binary::<SyntaxSet>(get_serialized_integrated_syntaxset(), COMPRESS_SYNTAXES)
.into_builder()
};
let syntax_dir = source_dir.join("syntaxes");
if syntax_dir.exists() {
syntax_set_builder.add_from_folder(syntax_dir, true)?;
} else {
println!(
"No syntaxes were found in '{}', using the default set.",
syntax_dir.to_string_lossy()
);
}
Ok(syntax_set_builder)
}
fn print_unlinked_contexts(syntax_set: &SyntaxSet) {
let missing_contexts = syntax_set.find_unlinked_contexts();
if !missing_contexts.is_empty() {
println!("Some referenced contexts could not be found!");
for context in missing_contexts {
println!("- {}", context);
}
}
}
fn write_assets(
theme_set: &ThemeSet,
syntax_set: &SyntaxSet,
minimal_syntaxes: &MinimalSyntaxes,
target_dir: &Path,
current_version: &str,
) -> Result<()> {
let _ = std::fs::create_dir_all(target_dir);
asset_to_cache(
theme_set,
&target_dir.join("themes.bin"),
"theme set",
COMPRESS_THEMES,
)?;
asset_to_cache(
syntax_set,
&target_dir.join("syntaxes.bin"),
"syntax set",
COMPRESS_SYNTAXES,
)?;
asset_to_cache(
minimal_syntaxes,
&target_dir.join("minimal_syntaxes.bin"),
"minimal syntax sets",
COMPRESS_MINIMAL_SYNTAXES,
)?;
print!(
"Writing metadata to folder {} ... ",
target_dir.to_string_lossy()
);
crate::assets_metadata::AssetsMetadata::new(current_version).save_to_folder(target_dir)?;
println!("okay");
Ok(())
}
fn print_syntax_set_names(syntax_set: &SyntaxSet) {
let names = syntax_set
.syntaxes()
.iter()
.map(|syntax| &syntax.name)
.collect::<Vec<_>>();
println!("{:?}", names);
}
fn build_minimal_syntaxes(
syntax_set_builder: &'_ SyntaxSetBuilder,
include_integrated_assets: bool,
) -> Result<MinimalSyntaxes> {
let mut minimal_syntaxes = MinimalSyntaxes {
by_name: HashMap::new(),
serialized_syntax_sets: vec![],
};
if include_integrated_assets {
// Dependency info is not present in integrated assets, so we can't
// calculate minimal syntax sets. Return early without any data filled
// in. This means that no minimal syntax sets will be available to use, and
// the full, slow-to-deserialize, fallback syntax set will be used instead.
return Ok(minimal_syntaxes);
}
let minimal_syntax_sets_to_serialize = build_minimal_syntax_sets(syntax_set_builder)
// For now, only store syntax sets with one syntax, otherwise
// the binary grows by several megs
.filter(|syntax_set| syntax_set.syntaxes().len() == 1);
for minimal_syntax_set in minimal_syntax_sets_to_serialize {
// Remember what index it is found at
let current_index = minimal_syntaxes.serialized_syntax_sets.len();
for syntax in minimal_syntax_set.syntaxes() {
minimal_syntaxes
.by_name
.insert(syntax.name.to_ascii_lowercase().clone(), current_index);
}
let serialized_syntax_set = asset_to_contents(
&minimal_syntax_set,
&format!("failed to serialize minimal syntax set {}", current_index),
COMPRESS_SERIALIZED_MINIMAL_SYNTAXES,
)?;
// Add last so that it ends up at `current_index`
minimal_syntaxes
.serialized_syntax_sets
.push(serialized_syntax_set);
}
Ok(minimal_syntaxes)
}
/// Analyzes dependencies between syntaxes in a [SyntaxSetBuilder].
/// From that, it builds minimal [SyntaxSet]s.
fn build_minimal_syntax_sets(
syntax_set_builder: &'_ SyntaxSetBuilder,
) -> impl Iterator<Item = SyntaxSet> + '_ {
let syntaxes = syntax_set_builder.syntaxes();
// Build the data structures we need for dependency resolution
let (other_syntax_lookup, syntax_to_dependencies, syntax_to_dependents) =
generate_maps(syntaxes);
maybe_write_syntax_dependencies_to_graphviz_dot_file(
&other_syntax_lookup,
&syntax_to_dependencies,
);
// Create one minimal SyntaxSet from each (non-hidden) SyntaxDefinition
syntaxes.iter().filter_map(move |syntax| {
if syntax.hidden {
return None;
}
let mut builder = SyntaxSetDependencyBuilder::new();
builder.add_with_dependencies(
syntax,
&other_syntax_lookup,
&syntax_to_dependencies,
&syntax_to_dependents,
);
let syntax_set = builder.build();
if std::env::var("BAT_PRINT_SYNTAX_DEPENDENCIES").is_ok() {
// To trigger this code, run:
// BAT_PRINT_SYNTAX_DEPENDENCIES=1 cargo run -- cache --build --source assets --blank --target /tmp
print_syntax_set_names(&syntax_set);
}
Some(syntax_set)
})
}
/// In order to analyze dependencies, we need three key pieces of data.
///
/// * When we have a [OtherSyntax], we need to know what [SyntaxDefinition]
/// that corresponds to
/// * When we have a [SyntaxDefinition], we need to know what dependencies it
/// has
/// * When we have a [SyntaxDefinition], we need to know what other syntaxes
/// depend on it
///
/// This functions generates that data for each syntax.
fn generate_maps(
syntaxes: &[SyntaxDefinition],
) -> (OtherSyntaxLookup, SyntaxToDependencies, SyntaxToDependents) {
let mut other_syntax_lookup = HashMap::new();
let mut syntax_to_dependencies = HashMap::new();
let mut syntax_to_dependents = HashMap::new();
for syntax in syntaxes {
other_syntax_lookup.insert(OtherSyntax::ByName(syntax.name.clone()), syntax);
other_syntax_lookup.insert(OtherSyntax::ByScope(syntax.scope), syntax);
}
for syntax in syntaxes {
let dependencies = dependencies_for_syntax(syntax);
for dependency in &dependencies {
if let Some(dependency) = other_syntax_lookup.get(dependency) {
syntax_to_dependents
.entry(dependency.name.clone())
.or_insert_with(Vec::new)
.push(OtherSyntax::ByName(syntax.name.clone()));
}
}
syntax_to_dependencies.insert(syntax.name.clone(), dependencies);
}
(
other_syntax_lookup,
syntax_to_dependencies,
syntax_to_dependents,
)
}
/// Gets what external dependencies a given [SyntaxDefinition] has.
/// An external dependency is another `.sublime-syntax` file.
/// It does that by looking for variants of the following YAML patterns:
/// - `include: C.sublime-syntax`
/// - `embed: scope:source.c`
fn dependencies_for_syntax(syntax: &SyntaxDefinition) -> Vec<OtherSyntax> {
let mut dependencies: Vec<OtherSyntax> = syntax
.contexts
.values()
.flat_map(|context| &context.patterns)
.flat_map(dependencies_from_pattern)
.collect();
// No need to track a dependency more than once
dependencies.sort();
dependencies.dedup();
dependencies
}
fn dependencies_from_pattern(pattern: &Pattern) -> Vec<OtherSyntax> {
match *pattern {
Pattern::Match(MatchPattern {
operation: MatchOperation::Push(ref context_references),
..
}) => context_references
.iter()
.map(dependency_from_context_reference)
.collect(),
Pattern::Include(ref context_reference) => {
vec![dependency_from_context_reference(context_reference)]
}
_ => vec![],
}
.into_iter()
.flatten()
.collect()
}
/// To generate a Graphviz dot file of syntax dependencies, do this:
/// ```bash
/// sudo apt install graphviz
/// BAT_SYNTAX_DEPENDENCIES_TO_GRAPHVIZ_DOT_FILE=/tmp/bat-syntax-dependencies.dot cargo run -- cache --build --source assets --blank --target /tmp
/// dot /tmp/bat-syntax-dependencies.dot -Tpng -o /tmp/bat-syntax-dependencies.png
/// open /tmp/bat-syntax-dependencies.png
/// ```
fn maybe_write_syntax_dependencies_to_graphviz_dot_file(
other_syntax_lookup: &OtherSyntaxLookup,
syntax_to_dependencies: &SyntaxToDependencies,
) {
if let Ok(dot_file_path) = std::env::var("BAT_SYNTAX_DEPENDENCIES_TO_GRAPHVIZ_DOT_FILE") {
graphviz_utils::try_syntax_dependencies_to_graphviz_dot_file(
other_syntax_lookup,
syntax_to_dependencies,
&dot_file_path,
);
}
}
/// Removes any context name from the syntax reference.
///
/// When we track dependencies between syntaxes, we are not interested in
/// dependencies on specific contexts inside other syntaxes. We only care about
/// the dependency on the syntax itself.
///
/// For example, if a syntax includes another syntax like this:
/// ```yaml
/// - include: scope:source.c++#unique-variables
/// ```
/// we only want to track the dependency on `source.c++`.
fn remove_explicit_context(scope: Scope) -> Scope {
if let Some(without_context) = scope.build_string().split('#').next() {
Scope::new(without_context).expect("removing context reference must never fail")
} else {
scope
}
}
fn dependency_from_context_reference(context_reference: &ContextReference) -> Option<OtherSyntax> {
match &context_reference {
ContextReference::File { ref name, .. } => Some(OtherSyntax::ByName(name.clone())),
ContextReference::ByScope { ref scope, .. } => {
Some(OtherSyntax::ByScope(remove_explicit_context(*scope)))
}
_ => None,
}
}
/// Helper to construct a [SyntaxSetBuilder] that contains only [SyntaxDefinition]s
/// that have dependencies among them.
struct SyntaxSetDependencyBuilder {
syntax_set_builder: SyntaxSetBuilder,
}
impl SyntaxSetDependencyBuilder {
fn new() -> Self {
SyntaxSetDependencyBuilder {
syntax_set_builder: SyntaxSetBuilder::new(),
}
}
/// Add a [SyntaxDefinition] to the underlying [SyntaxSetBuilder].
/// Also resolve any dependencies it has and add those [SyntaxDefinition]s too.
/// This is a recursive process.
fn add_with_dependencies(
&mut self,
syntax: &SyntaxDefinition,
other_syntax_lookup: &OtherSyntaxLookup,
syntax_to_dependencies: &SyntaxToDependencies,
syntax_to_dependents: &SyntaxToDependents,
) {
let name = &syntax.name;
if self.is_syntax_already_added(name) {
return;
}
self.syntax_set_builder.add(syntax.clone());
let mut syntaxes_to_add = vec![];
if let Some(dependencies) = syntax_to_dependencies.get(name) {
syntaxes_to_add.extend(dependencies);
}
if let Some(dependents) = syntax_to_dependents.get(name) {
// This will later be enabled intelligently
if std::env::var("BAT_INCLUDE_SYNTAX_DEPENDENTS").is_ok() {
syntaxes_to_add.extend(dependents);
}
}
for syntax_to_add in syntaxes_to_add {
if let Some(syntax_to_add) = other_syntax_lookup.get(syntax_to_add) {
self.add_with_dependencies(
syntax_to_add,
other_syntax_lookup,
syntax_to_dependencies,
syntax_to_dependents,
)
}
}
}
fn is_syntax_already_added(&self, name: &str) -> bool {
self.syntax_set_builder
.syntaxes()
.iter()
.any(|syntax| syntax.name == name)
}
fn build(self) -> SyntaxSet {
self.syntax_set_builder.build()
}
}
fn asset_to_contents<T: serde::Serialize>(
asset: &T,
description: &str,
compressed: bool,
) -> Result<Vec<u8>> {
let mut contents = vec![];
if compressed {
bincode::serialize_into(
flate2::write::ZlibEncoder::new(&mut contents, flate2::Compression::best()),
asset,
)
} else {
bincode::serialize_into(&mut contents, asset)
}
.map_err(|_| format!("Could not serialize {}", description))?;
Ok(contents)
}
fn asset_to_cache<T: serde::Serialize>(
asset: &T,
path: &Path,
description: &str,
compressed: bool,
) -> Result<()> {
print!("Writing {} to {} ... ", description, path.to_string_lossy());
let contents = asset_to_contents(asset, description, compressed)?;
std::fs::write(path, &contents[..]).map_err(|_| {
format!(
"Could not save {} to {}",
description,
path.to_string_lossy()
)
})?;
println!("okay");
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn remove_explicit_context_sanity() {
// Example from Objective-C++.sublime-syntax
let scope = Scope::new("source.c++#unique-variables").unwrap();
let expected = Scope::new("source.c++").unwrap();
assert_eq!(remove_explicit_context(scope), expected);
}
}

View File

@@ -1,41 +0,0 @@
use super::*;
pub(crate) fn try_syntax_dependencies_to_graphviz_dot_file(
other_syntax_lookup: &OtherSyntaxLookup,
syntax_to_dependencies: &SyntaxToDependencies,
dot_file_path: &str,
) {
match syntax_dependencies_to_graphviz_dot_file(
other_syntax_lookup,
syntax_to_dependencies,
dot_file_path,
) {
Ok(_) => println!("Wrote graphviz dot file to {}", dot_file_path),
Err(e) => eprintln!(
"Failed to write graphviz dot file to {}: {}",
dot_file_path, e
),
};
}
fn syntax_dependencies_to_graphviz_dot_file(
other_syntax_lookup: &OtherSyntaxLookup,
syntax_to_dependencies: &SyntaxToDependencies,
dot_file_path: &str,
) -> Result<()> {
use std::io::Write;
let mut dot_file = std::fs::File::create(dot_file_path)?;
writeln!(dot_file, "digraph BatSyntaxDependencies {{")?;
for (key, dependencies) in syntax_to_dependencies {
for dependency in dependencies {
if let Some(dep) = other_syntax_lookup.get(dependency) {
writeln!(dot_file, " \"{}\" -> \"{}\"", key, dep.name)?;
}
}
}
writeln!(dot_file, "}}")?;
Ok(())
}

View File

@@ -1,42 +0,0 @@
use std::ffi::OsStr;
use std::path::Path;
use crate::error::*;
const IGNORED_SUFFIXES: [&str; 13] = [
// Editor etc backups
"~",
".bak",
".old",
".orig",
// Debian and derivatives apt/dpkg/ucf backups
".dpkg-dist",
".dpkg-old",
".ucf-dist",
".ucf-new",
".ucf-old",
// Red Hat and derivatives rpm backups
".rpmnew",
".rpmorig",
".rpmsave",
// Build system input/template files
".in",
];
/// If we find an ignored suffix on the file name, e.g. '~', we strip it and
/// then try again without it.
pub fn try_with_stripped_suffix<T, F>(file_name: &OsStr, func: F) -> Result<Option<T>>
where
F: Fn(&OsStr) -> Result<Option<T>>,
{
let mut from_stripped = None;
if let Some(file_str) = Path::new(file_name).to_str() {
for suffix in &IGNORED_SUFFIXES {
if let Some(stripped_filename) = file_str.strip_suffix(suffix) {
from_stripped = func(OsStr::new(stripped_filename))?;
break;
}
}
}
Ok(from_stripped)
}

View File

@@ -1,72 +0,0 @@
use std::collections::HashMap;
use lazycell::LazyCell;
use syntect::parsing::SyntaxSet;
use super::*;
#[derive(Debug)]
pub(crate) struct MinimalAssets {
minimal_syntaxes: MinimalSyntaxes,
/// Lazily load serialized [SyntaxSet]s from [Self.minimal_syntaxes]. The
/// index in this vec matches the index in
/// [Self.minimal_syntaxes.serialized_syntax_sets]
deserialized_minimal_syntaxes: Vec<LazyCell<SyntaxSet>>,
}
/// Stores and allows lookup of minimal [SyntaxSet]s. The [SyntaxSet]s are
/// stored in serialized form, and are deserialized on-demand. This gives good
/// startup performance since only the necessary [SyntaxReference]s needs to be
/// deserialized.
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub(crate) struct MinimalSyntaxes {
/// Lookup the index into `serialized_syntax_sets` of a [SyntaxSet] by the
/// name of any [SyntaxReference] inside the [SyntaxSet]
/// (We will later add `by_extension`, `by_first_line`, etc.)
pub(crate) by_name: HashMap<String, usize>,
/// Serialized [SyntaxSet]s. Whether or not this data is compressed is
/// decided by [COMPRESS_SERIALIZED_MINIMAL_SYNTAXES]
pub(crate) serialized_syntax_sets: Vec<Vec<u8>>,
}
impl MinimalAssets {
pub(crate) fn new(minimal_syntaxes: MinimalSyntaxes) -> Self {
// Prepare so we can lazily load minimal syntaxes without a mut reference
let deserialized_minimal_syntaxes =
vec![LazyCell::new(); minimal_syntaxes.serialized_syntax_sets.len()];
Self {
minimal_syntaxes,
deserialized_minimal_syntaxes,
}
}
pub fn get_syntax_set_by_name(&self, name: &str) -> Option<&SyntaxSet> {
self.minimal_syntaxes
.by_name
.get(&name.to_ascii_lowercase())
.and_then(|index| self.get_minimal_syntax_set_with_index(*index))
}
fn load_minimal_syntax_set_with_index(&self, index: usize) -> Result<SyntaxSet> {
let serialized_syntax_set = &self.minimal_syntaxes.serialized_syntax_sets[index];
asset_from_contents(
&serialized_syntax_set[..],
&format!("minimal syntax set {}", index),
COMPRESS_SERIALIZED_MINIMAL_SYNTAXES,
)
.map_err(|_| format!("Could not parse minimal syntax set {}", index).into())
}
fn get_minimal_syntax_set_with_index(&self, index: usize) -> Option<&SyntaxSet> {
self.deserialized_minimal_syntaxes
.get(index)
.and_then(|cell| {
cell.try_borrow_with(|| self.load_minimal_syntax_set_with_index(index))
.ok()
})
}
}

View File

@@ -1,27 +0,0 @@
use std::path::PathBuf;
use syntect::parsing::SyntaxSet;
use super::*;
/// A SyntaxSet in serialized form, i.e. bincoded and flate2 compressed.
/// We keep it in this format since we want to load it lazily.
#[derive(Debug)]
pub enum SerializedSyntaxSet {
/// The data comes from a user-generated cache file.
FromFile(PathBuf),
/// The data to use is embedded into the bat binary.
FromBinary(&'static [u8]),
}
impl SerializedSyntaxSet {
pub fn deserialize(&self) -> Result<SyntaxSet> {
match self {
SerializedSyntaxSet::FromBinary(data) => Ok(from_binary(data, COMPRESS_SYNTAXES)),
SerializedSyntaxSet::FromFile(ref path) => {
asset_from_cache(path, "syntax set", COMPRESS_SYNTAXES)
}
}
}
}

View File

@@ -52,15 +52,16 @@ impl AssetsMetadata {
pub fn load_from_folder(path: &Path) -> Result<Option<Self>> {
match Self::try_load_from_folder(path) {
Ok(metadata) => Ok(Some(metadata)),
Err(e) => {
if let Error::SerdeYamlError(_) = e {
Err(e)
} else if path.join("syntaxes.bin").exists() || path.join("themes.bin").exists() {
Ok(Some(Self::default()))
} else {
Ok(None)
Err(e) => match e.kind() {
ErrorKind::SerdeYamlError(_) => Err(e),
_ => {
if path.join("syntaxes.bin").exists() || path.join("themes.bin").exists() {
Ok(Some(Self::default()))
} else {
Ok(None)
}
}
}
},
}
}

View File

@@ -62,7 +62,7 @@ impl App {
// Read arguments from bats config file
let mut args = get_args_from_env_var()
.unwrap_or_else(get_args_from_config_file)
.map_err(|_| "Could not parse configuration file")?;
.chain_err(|| "Could not parse configuration file")?;
// Put the zero-th CLI argument (program name) first
args.insert(0, cli_args.next().unwrap());

View File

@@ -20,7 +20,6 @@ pub fn cache_dir() -> Cow<'static, str> {
pub fn clear_assets() {
clear_asset("themes.bin", "theme set cache");
clear_asset("syntaxes.bin", "syntax set cache");
clear_asset("minimal_syntaxes.bin", "minimal syntax sets cache");
clear_asset("metadata.yaml", "metadata file");
}

View File

@@ -95,8 +95,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
'--highlight-line 40' highlights line 40\n \
'--highlight-line 30:40' highlights lines 30 to 40\n \
'--highlight-line :40' highlights lines 1 to 40\n \
'--highlight-line 40:' highlights lines 40 to the end of the file\n \
'--highlight-line 30:+10' highlights lines 30 to 40",
'--highlight-line 40:' highlights lines 40 to the end of the file",
),
)
.arg(
@@ -424,8 +423,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
'--line-range 30:40' prints lines 30 to 40\n \
'--line-range :40' prints lines 1 to 40\n \
'--line-range 40:' prints lines 40 to the end of the file\n \
'--line-range 40' only prints line 40\n \
'--line-range 30:+10' prints lines 30 to 40",
'--line-range 40' only prints line 40",
),
)
.arg(

View File

@@ -64,7 +64,7 @@ pub fn generate_config_file() -> bat::error::Result<()> {
#--pager="less --RAW-CONTROL-CHARS --quit-if-one-screen --mouse"
# Syntax mappings: map a certain filename pattern to a language.
# Example 1: use the C++ syntax for Arduino .ino files
# Example 1: use the C++ syntax for .ino files
# Example 2: Use ".gitignore"-style highlighting for ".ignore" files
#--map-syntax "*.ino:C++"
#--map-syntax ".ignore:Git Ignore"

View File

@@ -1,4 +1,5 @@
#![deny(unsafe_code)]
// `error_chain!` can recurse deeply
#![recursion_limit = "1024"]
mod app;
mod assets;
@@ -49,7 +50,8 @@ fn build_assets(matches: &clap::ArgMatches) -> Result<()> {
let blank = matches.is_present("blank");
bat::assets::build(source_dir, !blank, target_dir, clap::crate_version!())
let assets = bat::assets::HighlightingAssets::from_files(source_dir, !blank)?;
assets.save_to_cache(target_dir, clap::crate_version!())
}
fn run_cache_subcommand(matches: &clap::ArgMatches) -> Result<()> {
@@ -93,7 +95,7 @@ pub fn get_languages(config: &Config) -> Result<String> {
.collect::<Vec<_>>();
// Handling of file-extension conflicts, see issue #1076
for lang in &mut languages {
for lang in languages.iter_mut() {
let lang_name = lang.name.clone();
lang.file_extensions.retain(|extension| {
// The 'extension' variable is not certainly a real extension.
@@ -102,12 +104,17 @@ pub fn get_languages(config: &Config) -> Result<String> {
// Also skip if the 'extension' contains another real extension, likely
// that is a full match file name like 'CMakeLists.txt' and 'Cargo.lock'
if extension.starts_with('.') || Path::new(extension).extension().is_some() {
return true;
true
} else {
let test_file = Path::new("test").with_extension(extension);
let syntax_in_set = assets
.get_syntax_for_file_name(test_file, &config.syntax_mapping)
.unwrap(); // safe since .get_syntaxes() above worked
match syntax_in_set {
Some(syntax_in_set) => syntax_in_set.syntax.name == lang_name,
None => false,
}
}
let test_file = Path::new("test").with_extension(extension);
let syntax_in_set = assets.get_syntax_for_path(test_file, &config.syntax_mapping);
matches!(syntax_in_set, Ok(syntax_in_set) if syntax_in_set.syntax.name == lang_name)
});
}
@@ -115,7 +122,7 @@ pub fn get_languages(config: &Config) -> Result<String> {
let configured_languages = get_syntax_mapping_to_paths(config.syntax_mapping.mappings());
for lang in &mut languages {
for lang in languages.iter_mut() {
if let Some(additional_paths) = configured_languages.get(lang.name.as_str()) {
lang.file_extensions
.extend(additional_paths.iter().cloned());
@@ -229,7 +236,7 @@ fn invoke_bugreport(app: &App) {
let pager = bat::config::get_pager_executable(app.matches.value_of("pager"))
.unwrap_or_else(|| "less".to_owned()); // FIXME: Avoid non-canonical path to "less".
let mut report = bugreport!()
let report = bugreport!()
.info(SoftwareVersion::default())
.info(OperatingSystem::default())
.info(CommandLine::default())
@@ -237,8 +244,6 @@ fn invoke_bugreport(app: &App) {
"SHELL",
"PAGER",
"LESS",
"LANG",
"LC_ALL",
"BAT_PAGER",
"BAT_CACHE_PATH",
"BAT_CONFIG_PATH",
@@ -255,13 +260,14 @@ fn invoke_bugreport(app: &App) {
.info(FileContent::new("Config file", config_file()))
.info(CompileTimeInformation::default());
#[cfg(feature = "paging")]
if let Ok(resolved_path) = grep_cli::resolve_binary(pager) {
report = report.info(CommandOutput::new(
let mut report = if let Ok(resolved_path) = grep_cli::resolve_binary(pager) {
report.info(CommandOutput::new(
"Less version",
resolved_path,
&["--version"],
))
} else {
report
};
report.print::<Markdown>();

View File

@@ -90,10 +90,11 @@ pub struct Config<'a> {
#[cfg(all(feature = "minimal-application", feature = "paging"))]
pub fn get_pager_executable(config_pager: Option<&str>) -> Option<String> {
crate::pager::get_pager(config_pager)
.ok()
.flatten()
.map(|pager| pager.bin)
if let Ok(Some(pager)) = crate::pager::get_pager(config_pager) {
Some(pager.bin)
} else {
None
}
}
#[test]

View File

@@ -175,10 +175,9 @@ impl<'b> Controller<'b> {
let mut line_ranges: Vec<LineRange> = vec![];
if let Some(line_changes) = line_changes {
for &line in line_changes.keys() {
let line = line as usize;
line_ranges
.push(LineRange::new(line.saturating_sub(context), line + context));
for line in line_changes.keys() {
let line = *line as usize;
line_ranges.push(LineRange::new(line - context, line + context));
}
}

View File

@@ -1,52 +1,42 @@
use error_chain::error_chain;
use std::io::Write;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
Io(#[from] ::std::io::Error),
#[error(transparent)]
SyntectError(#[from] ::syntect::LoadingError),
#[error(transparent)]
ParseIntError(#[from] ::std::num::ParseIntError),
#[error(transparent)]
GlobParsingError(#[from] ::globset::Error),
#[error(transparent)]
SerdeYamlError(#[from] ::serde_yaml::Error),
#[error("unable to detect syntax for {0}")]
UndetectedSyntax(String),
#[error("unknown syntax: '{0}'")]
UnknownSyntax(String),
#[error("Unknown style '{0}'")]
UnknownStyle(String),
#[error("Use of bat as a pager is disallowed in order to avoid infinite recursion problems")]
InvalidPagerValueBat,
#[error("{0}")]
Msg(String),
}
error_chain! {
foreign_links {
Clap(::clap::Error) #[cfg(feature = "minimal-application")];
Io(::std::io::Error);
SyntectError(::syntect::LoadingError);
ParseIntError(::std::num::ParseIntError);
GlobParsingError(::globset::Error);
SerdeYamlError(::serde_yaml::Error);
}
impl From<&'static str> for Error {
fn from(s: &'static str) -> Self {
Error::Msg(s.to_owned())
errors {
UndetectedSyntax(input: String) {
description("unable to detect syntax"),
display("unable to detect syntax for {}", input)
}
UnknownSyntax(name: String) {
description("unknown syntax"),
display("unknown syntax: '{}'", name)
}
InvalidPagerValueBat {
description("invalid value `bat` for pager property"),
display("Use of bat as a pager is disallowed in order to avoid infinite recursion problems")
}
}
}
impl From<String> for Error {
fn from(s: String) -> Self {
Error::Msg(s)
}
}
pub type Result<T> = std::result::Result<T, Error>;
pub fn default_error_handler(error: &Error, output: &mut dyn Write) {
use ansi_term::Colour::Red;
match error {
Error::Io(ref io_error) if io_error.kind() == ::std::io::ErrorKind::BrokenPipe => {
Error(ErrorKind::Io(ref io_error), _)
if io_error.kind() == ::std::io::ErrorKind::BrokenPipe =>
{
::std::process::exit(0);
}
Error::SerdeYamlError(_) => {
Error(ErrorKind::SerdeYamlError(_), _) => {
writeln!(
output,
"{}: Error while parsing metadata.yaml file: {}",

View File

@@ -50,7 +50,7 @@ impl InputDescription {
}
pub fn title(&self) -> &String {
match &self.title {
match self.title.as_ref() {
Some(title) => title,
None => &self.name,
}
@@ -108,21 +108,6 @@ pub(crate) struct OpenedInput<'a> {
pub(crate) description: InputDescription,
}
impl OpenedInput<'_> {
/// Get the path of the file:
/// If this was set by the metadata, that will take priority.
/// If it wasn't, it will use the real file path (if available).
pub(crate) fn path(&self) -> Option<&PathBuf> {
self.metadata
.user_provided_name
.as_ref()
.or_else(|| match self.kind {
OpenedInputKind::OrdinaryFile(ref path) => Some(path),
_ => None,
})
}
}
impl<'a> Input<'a> {
pub fn ordinary_file(path: impl AsRef<Path>) -> Self {
Self::_ordinary_file(path.as_ref())
@@ -271,18 +256,18 @@ impl<'a> InputReader<'a> {
}
pub(crate) fn read_line(&mut self, buf: &mut Vec<u8>) -> io::Result<bool> {
if !self.first_line.is_empty() {
if self.first_line.is_empty() {
let res = self.inner.read_until(b'\n', buf).map(|size| size > 0)?;
if self.content_type == Some(ContentType::UTF_16LE) {
self.inner.read_until(0x00, buf).ok();
}
Ok(res)
} else {
buf.append(&mut self.first_line);
return Ok(true);
Ok(true)
}
let res = self.inner.read_until(b'\n', buf).map(|size| size > 0)?;
if self.content_type == Some(ContentType::UTF_16LE) {
let _ = self.inner.read_until(0x00, buf);
}
Ok(res)
}
}

View File

@@ -11,13 +11,13 @@ pub fn retrieve_less_version(less_path: &dyn AsRef<OsStr>) -> Option<usize> {
}
fn parse_less_version(output: &[u8]) -> Option<usize> {
if !output.starts_with(b"less ") {
return None;
if output.starts_with(b"less ") {
let version = std::str::from_utf8(&output[5..]).ok()?;
let end = version.find(|c: char| !c.is_ascii_digit())?;
version[..end].parse::<usize>().ok()
} else {
None
}
let version = std::str::from_utf8(&output[5..]).ok()?;
let end = version.find(|c: char| !c.is_ascii_digit())?;
version[..end].parse::<usize>().ok()
}
#[test]

View File

@@ -19,14 +19,10 @@
//! .unwrap();
//! ```
#![deny(unsafe_code)]
mod macros;
pub mod assets;
pub mod assets_metadata {
pub use super::assets::assets_metadata::*;
}
pub mod assets_metadata;
pub mod config;
pub mod controller;
mod decorations;
@@ -44,6 +40,8 @@ mod preprocessor;
mod pretty_printer;
pub(crate) mod printer;
pub mod style;
#[cfg(feature = "build-assets")]
mod syntax_dependencies;
pub(crate) mod syntax_mapping;
mod terminal;
pub(crate) mod wrapping;

View File

@@ -47,16 +47,7 @@ impl LineRange {
}
2 => {
new_range.lower = line_numbers[0].parse()?;
new_range.upper = if line_numbers[1].bytes().next() == Some(b'+') {
let more_lines = &line_numbers[1][1..]
.parse()
.map_err(|_| "Invalid character after +")?;
new_range.lower + more_lines
} else {
line_numbers[1].parse()?
};
new_range.upper = line_numbers[1].parse()?;
Ok(new_range)
}
_ => Err(
@@ -109,23 +100,6 @@ fn test_parse_fail() {
assert!(range.is_err());
}
#[test]
fn test_parse_plus() {
let range = LineRange::from("40:+10").expect("Shouldn't fail on test!");
assert_eq!(40, range.lower);
assert_eq!(50, range.upper);
}
#[test]
fn test_parse_plus_fail() {
let range = LineRange::from("40:+z");
assert!(range.is_err());
let range = LineRange::from("40:+-10");
assert!(range.is_err());
let range = LineRange::from("40:+");
assert!(range.is_err());
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum RangeCheckResult {
// Within one of the given ranges

View File

@@ -52,7 +52,7 @@ impl OutputType {
use std::process::{Command, Stdio};
let pager_opt =
pager::get_pager(pager_from_config).map_err(|_| "Could not parse pager command.")?;
pager::get_pager(pager_from_config).chain_err(|| "Could not parse pager command.")?;
let pager = match pager_opt {
Some(pager) => pager,
@@ -60,7 +60,7 @@ impl OutputType {
};
if pager.kind == PagerKind::Bat {
return Err(Error::InvalidPagerValueBat);
return Err(ErrorKind::InvalidPagerValueBat.into());
}
let resolved_path = match grep_cli::resolve_binary(&pager.bin) {
@@ -142,7 +142,7 @@ impl OutputType {
OutputType::Pager(ref mut command) => command
.stdin
.as_mut()
.ok_or("Could not open stdin for pager")?,
.chain_err(|| "Could not open stdin for pager")?,
OutputType::Stdout(ref mut handle) => handle,
})
}

View File

@@ -99,20 +99,6 @@ impl<'a> Printer for SimplePrinter<'a> {
}
}
struct HighlighterFromSet<'a> {
highlighter: HighlightLines<'a>,
syntax_set: &'a SyntaxSet,
}
impl<'a> HighlighterFromSet<'a> {
fn new(syntax_in_set: SyntaxReferenceInSet<'a>, theme: &'a Theme) -> Self {
Self {
highlighter: HighlightLines::new(syntax_in_set.syntax, theme),
syntax_set: syntax_in_set.syntax_set,
}
}
}
pub(crate) struct InteractivePrinter<'a> {
colors: Colors,
config: &'a Config<'a>,
@@ -122,7 +108,8 @@ pub(crate) struct InteractivePrinter<'a> {
content_type: Option<ContentType>,
#[cfg(feature = "git")]
pub line_changes: &'a Option<LineChanges>,
highlighter_from_set: Option<HighlighterFromSet<'a>>,
highlighter: Option<HighlightLines<'a>>,
syntax_set: &'a SyntaxSet,
background_color_highlight: Option<Color>,
}
@@ -176,24 +163,29 @@ impl<'a> InteractivePrinter<'a> {
panel_width = 0;
}
let highlighter_from_set = if input
let (highlighter, syntax_set) = if input
.reader
.content_type
.map_or(false, |c| c.is_binary() && !config.show_nonprintable)
{
None
(None, assets.get_syntax_set()?)
} else {
// Determine the type of syntax for highlighting
let syntax_in_set =
match assets.get_syntax(config.language, input, &config.syntax_mapping) {
Ok(syntax_in_set) => syntax_in_set,
Err(Error::UndetectedSyntax(_)) => assets
.find_syntax_by_name("Plain Text")?
.expect("A plain text syntax is available"),
Err(Error(ErrorKind::UndetectedSyntax(_), _)) => {
let syntax_set = assets.get_syntax_set()?;
let syntax = syntax_set.find_syntax_plain_text();
SyntaxReferenceInSet { syntax, syntax_set }
}
Err(e) => return Err(e),
};
Some(HighlighterFromSet::new(syntax_in_set, theme))
(
Some(HighlightLines::new(syntax_in_set.syntax, theme)),
syntax_in_set.syntax_set,
)
};
Ok(InteractivePrinter {
@@ -205,7 +197,8 @@ impl<'a> InteractivePrinter<'a> {
ansi_prefix_sgr: String::new(),
#[cfg(feature = "git")]
line_changes,
highlighter_from_set,
highlighter,
syntax_set,
background_color_highlight,
})
}
@@ -233,29 +226,29 @@ impl<'a> InteractivePrinter<'a> {
fn create_fake_panel(&self, text: &str) -> String {
if self.panel_width == 0 {
return "".to_string();
}
let text_truncated: String = text.chars().take(self.panel_width - 1).collect();
let text_filled: String = format!(
"{}{}",
text_truncated,
" ".repeat(self.panel_width - 1 - text_truncated.len())
);
if self.config.style_components.grid() {
format!("{}", text_filled)
"".to_string()
} else {
text_filled
let text_truncated: String = text.chars().take(self.panel_width - 1).collect();
let text_filled: String = format!(
"{}{}",
text_truncated,
" ".repeat(self.panel_width - 1 - text_truncated.len())
);
if self.config.style_components.grid() {
format!("{}", text_filled)
} else {
text_filled
}
}
}
fn preprocess(&self, text: &str, cursor: &mut usize) -> String {
if self.config.tab_width > 0 {
return expand_tabs(text, self.config.tab_width, cursor);
expand_tabs(text, self.config.tab_width, cursor)
} else {
*cursor += text.len();
text.to_string()
}
*cursor += text.len();
text.to_string()
}
}
@@ -396,15 +389,13 @@ impl<'a> Printer for InteractivePrinter<'a> {
};
let regions = {
let highlighter_from_set = match self.highlighter_from_set {
Some(ref mut highlighter_from_set) => highlighter_from_set,
let highlighter = match self.highlighter {
Some(ref mut highlighter) => highlighter,
_ => {
return Ok(());
}
};
highlighter_from_set
.highlighter
.highlight(&line, highlighter_from_set.syntax_set)
highlighter.highlight(line.as_ref(), self.syntax_set)
};
if out_of_range {
@@ -429,7 +420,8 @@ impl<'a> Printer for InteractivePrinter<'a> {
let decorations = self
.decorations
.iter()
.map(|d| d.generate(line_number, false, self));
.map(|d| d.generate(line_number, false, self))
.collect::<Vec<_>>();
for deco in decorations {
write!(handle, "{} ", deco.text)?;
@@ -443,7 +435,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
let colored_output = self.config.colored_output;
let italics = self.config.use_italic_text;
for &(style, region) in &regions {
for &(style, region) in regions.iter() {
let text = &*self.preprocess(region, &mut cursor_total);
let text_trimmed = text.trim_end_matches(|c| c == '\r' || c == '\n');
write!(
@@ -480,7 +472,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
writeln!(handle)?;
}
} else {
for &(style, region) in &regions {
for &(style, region) in regions.iter() {
let ansi_iterator = AnsiCodeIterator::new(region);
let mut ansi_prefix: String = String::new();
for chunk in ansi_iterator {

184
src/syntax_dependencies.rs Normal file
View File

@@ -0,0 +1,184 @@
use std::collections::HashMap;
use syntect::parsing::syntax_definition::{
ContextReference, MatchOperation, MatchPattern, Pattern, SyntaxDefinition,
};
use syntect::parsing::{Scope, SyntaxSet, SyntaxSetBuilder};
type SyntaxName = String;
/// Used to look up what dependencies a given [SyntaxDefinition] has
type SyntaxToDependencies = HashMap<SyntaxName, Vec<Dependency>>;
/// Used to look up which [SyntaxDefinition] corresponds to a given [Dependency]
type DependencyToSyntax<'a> = HashMap<Dependency, &'a SyntaxDefinition>;
/// Represents a dependency on an external `.sublime-syntax` file.
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
enum Dependency {
/// By name. Example YAML: `include: C.sublime-syntax`
ByName(String),
/// By scope. Example YAML: `embed: scope:source.c`
ByScope(Scope),
}
/// Generates independent [SyntaxSet]s after analyzing dependencies between syntaxes
/// in a [SyntaxSetBuilder], and then prints the reults.
pub(crate) fn print_syntax_dependencies(syntax_set_builder: &SyntaxSetBuilder) {
println!("Constructing independent SyntaxSets...");
let independent_syntax_sets = build_independent_syntax_sets(syntax_set_builder);
println!("Independent SyntaxSets:");
for syntax_set in independent_syntax_sets {
let names = syntax_set
.syntaxes()
.iter()
.map(|syntax| &syntax.name)
.collect::<Vec<_>>();
println!("{:?}", names);
}
}
/// Analyzes dependencies between syntaxes in a [SyntaxSetBuilder].
/// From that, it builds independent [SyntaxSet]s.
fn build_independent_syntax_sets(
syntax_set_builder: &'_ SyntaxSetBuilder,
) -> impl Iterator<Item = SyntaxSet> + '_ {
let syntaxes = syntax_set_builder.syntaxes();
// Build the data structures we need for dependency resolution
let (syntax_to_dependencies, dependency_to_syntax) = generate_maps(syntaxes);
// Create one independent SyntaxSet from each (non-hidden) SyntaxDefinition
syntaxes.iter().filter_map(move |syntax| {
if syntax.hidden {
return None;
}
let mut builder = SyntaxSetDependencyBuilder::new();
builder.add_with_dependencies(syntax, &syntax_to_dependencies, &dependency_to_syntax);
Some(builder.build())
})
}
/// In order to analyze dependencies, we need two key pieces of data.
/// First, when we have a [Dependency], we need to know what [SyntaxDefinition] that
/// corresponds to. Second, when we have a [SyntaxDefinition], we need to know
/// what dependencies it has. This functions generates that data for each syntax.
fn generate_maps(syntaxes: &[SyntaxDefinition]) -> (SyntaxToDependencies, DependencyToSyntax) {
let mut syntax_to_dependencies = HashMap::new();
let mut dependency_to_syntax = HashMap::new();
for syntax in syntaxes {
syntax_to_dependencies.insert(syntax.name.clone(), dependencies_for_syntax(syntax));
dependency_to_syntax.insert(Dependency::ByName(syntax.name.clone()), syntax);
dependency_to_syntax.insert(Dependency::ByScope(syntax.scope), syntax);
}
(syntax_to_dependencies, dependency_to_syntax)
}
/// Gets what external dependencies a given [SyntaxDefinition] has.
/// An external dependency is another `.sublime-syntax` file.
/// It does that by looking for variants of the following YAML patterns:
/// - `include: C.sublime-syntax`
/// - `embed: scope:source.c`
fn dependencies_for_syntax(syntax: &SyntaxDefinition) -> Vec<Dependency> {
let mut dependencies: Vec<Dependency> = syntax
.contexts
.values()
.flat_map(|context| &context.patterns)
.flat_map(dependencies_from_pattern)
.collect();
// No need to track a dependency more than once
dependencies.dedup();
dependencies
}
fn dependencies_from_pattern(pattern: &Pattern) -> Vec<Dependency> {
match *pattern {
Pattern::Match(MatchPattern {
operation: MatchOperation::Push(ref context_references),
..
}) => context_references
.iter()
.map(dependency_from_context_reference)
.collect(),
Pattern::Include(ref context_reference) => {
vec![dependency_from_context_reference(context_reference)]
}
_ => vec![],
}
.into_iter()
.flatten()
.collect()
}
fn dependency_from_context_reference(context_reference: &ContextReference) -> Option<Dependency> {
match &context_reference {
ContextReference::File { ref name, .. } => Some(Dependency::ByName(name.clone())),
ContextReference::ByScope { ref scope, .. } => Some(Dependency::ByScope(*scope)),
_ => None,
}
}
/// Helper to construct a [SyntaxSetBuilder] that contains only [SyntaxDefinition]s
/// that have dependencies among them.
struct SyntaxSetDependencyBuilder {
syntax_set_builder: SyntaxSetBuilder,
}
impl SyntaxSetDependencyBuilder {
fn new() -> Self {
SyntaxSetDependencyBuilder {
syntax_set_builder: SyntaxSetBuilder::new(),
}
}
/// Add a [SyntaxDefinition] to the underlying [SyntaxSetBuilder].
/// Also resolve any dependencies it has and add those [SyntaxDefinition]s too.
/// This is a recursive process.
fn add_with_dependencies(
&mut self,
syntax: &SyntaxDefinition,
syntax_to_dependencies: &SyntaxToDependencies,
dependency_to_syntax: &DependencyToSyntax,
) {
let name = &syntax.name;
if self.is_syntax_already_added(name) {
return;
}
self.syntax_set_builder.add(syntax.clone());
let dependencies = syntax_to_dependencies.get(name);
if dependencies.is_none() {
eprintln!("ERROR: Unknown dependencies for {}", name);
return;
}
for dependency in dependencies.unwrap() {
if let Some(syntax_definition_dependency) = dependency_to_syntax.get(dependency) {
self.add_with_dependencies(
syntax_definition_dependency,
syntax_to_dependencies,
dependency_to_syntax,
)
}
}
}
fn is_syntax_already_added(&self, name: &str) -> bool {
self.syntax_set_builder
.syntaxes()
.iter()
.any(|syntax| syntax.name == name)
}
fn build(self) -> SyntaxSet {
self.syntax_set_builder.build()
}
}

View File

@@ -81,7 +81,7 @@ impl<'a> SyntaxMapping<'a> {
.unwrap();
}
for glob in &[
for glob in [
"**/systemd/**/*.conf",
"**/systemd/**/*.example",
"*.automount",
@@ -100,7 +100,9 @@ impl<'a> SyntaxMapping<'a> {
"*.swap",
"*.target",
"*.timer",
] {
]
.iter()
{
mapping.insert(glob, MappingTarget::MapTo("INI")).unwrap();
}
@@ -151,7 +153,7 @@ impl<'a> SyntaxMapping<'a> {
}
pub(crate) fn get_syntax_for(&self, path: impl AsRef<Path>) -> Option<MappingTarget<'a>> {
let candidate = Candidate::new(&path);
let candidate = Candidate::new(path.as_ref());
let candidate_filename = path.as_ref().file_name().map(Candidate::new);
for (ref glob, ref syntax) in self.mappings.iter().rev() {
if glob.is_match_candidate(&candidate)

View File

@@ -1,2 +0,0 @@
#!/usr/bin/env bash
echo "Hello"

View File

@@ -34,17 +34,13 @@ const EXAMPLES_DIR: &str = "tests/examples";
fn bat_raw_command_with_config() -> Command {
let mut cmd = Command::cargo_bin("bat").unwrap();
cmd.current_dir("tests/examples");
cmd.env_remove("BAT_CACHE_PATH");
cmd.env_remove("BAT_CONFIG_DIR");
cmd.env_remove("BAT_CONFIG_PATH");
cmd.env_remove("BAT_OPTS");
cmd.env_remove("BAT_PAGER");
cmd.env_remove("BAT_STYLE");
cmd.env_remove("BAT_TABS");
cmd.env_remove("BAT_THEME");
cmd.env_remove("COLORTERM");
cmd.env_remove("NO_COLOR");
cmd.env_remove("PAGER");
cmd.env_remove("BAT_PAGER");
cmd.env_remove("BAT_CONFIG_PATH");
cmd.env_remove("BAT_STYLE");
cmd.env_remove("BAT_THEME");
cmd.env_remove("BAT_TABS");
cmd.env_remove("BAT_CONFIG_DIR");
cmd
}
@@ -1127,21 +1123,6 @@ fn do_not_detect_different_syntax_for_stdin_and_files() {
);
}
#[test]
fn no_first_line_fallback_when_mapping_to_invalid_syntax() {
let file = "regression_tests/first_line_fallback.invalid-syntax";
bat()
.arg("--color=always")
.arg("--map-syntax=*.invalid-syntax:InvalidSyntax")
.arg(&format!("--file-name={}", file))
.arg("--style=plain")
.arg(file)
.assert()
.failure()
.stderr(predicate::str::contains("unknown syntax: 'InvalidSyntax'"));
}
#[test]
fn show_all_mode() {
bat()

View File

@@ -1,4 +1,3 @@
+ /// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,5 +1,4 @@
──┬─────────────────────────────────────────────────────────────────────────────
+ │ /// A rectangle. First line is changed to prevent a regression of #1869
│ struct Rectangle {
│ width: u32,
│ height: u32,

View File

@@ -1,7 +1,6 @@
──┬─────────────────────────────────────────────────────────────────────────────
│ File: sample.rs
──┼─────────────────────────────────────────────────────────────────────────────
+ │ /// A rectangle. First line is changed to prevent a regression of #1869
│ struct Rectangle {
│ width: u32,
│ height: u32,

View File

@@ -1,27 +1,26 @@
───────┬────────────────────────────────────────────────────────────────────────
│ File: sample.rs
───────┼────────────────────────────────────────────────────────────────────────
1 +/// A rectangle. First line is changed to prevent a regression of #1869
2 │ struct Rectangle {
3 │ width: u32,
4 │ height: u32,
5 │ }
6
7 _fn main() {
8 │ let rect1 = Rectangle { width: 30, height: 50 };
9 │
10 println!(
11 ~ │ "The perimeter of the rectangle is {} pixels.",
12 ~ │ perimeter(&rect1)
13 );
14 + │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 │ }
16 │
17 │ fn area(rectangle: &Rectangle) -> u32 {
18 │ rectangle.width * rectangle.height
19 }
20 + │
21 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
22 + │ (rectangle.width + rectangle.height) * 2
23 + │ }
1 struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 _ │ fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 ~ "The perimeter of the rectangle is {} pixels.",
11 ~ │ perimeter(&rect1)
12 │ );
13 +println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 +
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
───────┴────────────────────────────────────────────────────────────────────────

View File

@@ -1,27 +1,26 @@
───────┬────────────────────────────────────────────────────────────────────────
│ File: sample.rs
───────┼────────────────────────────────────────────────────────────────────────
1 +/// A rectangle. First line is changed to prevent a regression of #1869
2 │ struct Rectangle {
3 │ width: u32,
4 │ height: u32,
5 │ }
6
7 _fn main() {
8 │ let rect1 = Rectangle { width: 30, height: 50 };
9 │
10 println!(
11 ~ │ "The perimeter of the rectangle is {} pixels.",
12 ~ │ perimeter(&rect1)
13 );
14 + │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 │ }
16 │
17 │ fn area(rectangle: &Rectangle) -> u32 {
18 │ rectangle.width * rectangle.height
19 }
20 + │
21 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
22 + │ (rectangle.width + rectangle.height) * 2
23 + │ }
1 struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 _ │ fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 ~ "The perimeter of the rectangle is {} pixels.",
11 ~ │ perimeter(&rect1)
12 │ );
13 +println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 +
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
───────┴────────────────────────────────────────────────────────────────────────

View File

@@ -1,7 +1,6 @@
──┬─────────────────────────────────────────────────────────────────────────────
│ File: sample.rs
──┼─────────────────────────────────────────────────────────────────────────────
+ │ /// A rectangle. First line is changed to prevent a regression of #1869
│ struct Rectangle {
│ width: u32,
│ height: u32,

View File

@@ -1,25 +1,24 @@
───────┬────────────────────────────────────────────────────────────────────────
1 +/// A rectangle. First line is changed to prevent a regression of #1869
2 │ struct Rectangle {
3 │ width: u32,
4 │ height: u32,
5 │ }
6
7 _fn main() {
8 │ let rect1 = Rectangle { width: 30, height: 50 };
9 │
10 println!(
11 ~ │ "The perimeter of the rectangle is {} pixels.",
12 ~ │ perimeter(&rect1)
13 );
14 + │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 │ }
16 │
17 │ fn area(rectangle: &Rectangle) -> u32 {
18 │ rectangle.width * rectangle.height
19 }
20 + │
21 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
22 + │ (rectangle.width + rectangle.height) * 2
23 + │ }
1 struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 _ │ fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 ~ "The perimeter of the rectangle is {} pixels.",
11 ~ │ perimeter(&rect1)
12 │ );
13 +println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 +
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
───────┴────────────────────────────────────────────────────────────────────────

View File

@@ -1,25 +1,24 @@
───────┬────────────────────────────────────────────────────────────────────────
1 +/// A rectangle. First line is changed to prevent a regression of #1869
2 │ struct Rectangle {
3 │ width: u32,
4 │ height: u32,
5 │ }
6
7 _fn main() {
8 │ let rect1 = Rectangle { width: 30, height: 50 };
9 │
10 println!(
11 ~ │ "The perimeter of the rectangle is {} pixels.",
12 ~ │ perimeter(&rect1)
13 );
14 + │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 │ }
16 │
17 │ fn area(rectangle: &Rectangle) -> u32 {
18 │ rectangle.width * rectangle.height
19 }
20 + │
21 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
22 + │ (rectangle.width + rectangle.height) * 2
23 + │ }
1 struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 _ │ fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 ~ "The perimeter of the rectangle is {} pixels.",
11 ~ │ perimeter(&rect1)
12 │ );
13 +println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 +
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
───────┴────────────────────────────────────────────────────────────────────────

View File

@@ -1,5 +1,4 @@
──┬─────────────────────────────────────────────────────────────────────────────
+ │ /// A rectangle. First line is changed to prevent a regression of #1869
│ struct Rectangle {
│ width: u32,
│ height: u32,

View File

@@ -1,5 +1,4 @@
File: sample.rs
+ /// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,24 +1,23 @@
File: sample.rs
1 + /// A rectangle. First line is changed to prevent a regression of #1869
2 struct Rectangle {
3 width: u32,
4 height: u32,
5 }
6
7 _ fn main() {
8 let rect1 = Rectangle { width: 30, height: 50 };
9
10 println!(
11 ~ "The perimeter of the rectangle is {} pixels.",
12 ~ perimeter(&rect1)
13 );
14 + println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 }
16
17 fn area(rectangle: &Rectangle) -> u32 {
18 rectangle.width * rectangle.height
19 }
20 +
21 + fn perimeter(rectangle: &Rectangle) -> u32 {
22 + (rectangle.width + rectangle.height) * 2
23 + }
1 struct Rectangle {
2 width: u32,
3 height: u32,
4 }
5
6 _ fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8
9 println!(
10 ~ "The perimeter of the rectangle is {} pixels.",
11 ~ perimeter(&rect1)
12 );
13 + println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 }
15
16 fn area(rectangle: &Rectangle) -> u32 {
17 rectangle.width * rectangle.height
18 }
19 +
20 + fn perimeter(rectangle: &Rectangle) -> u32 {
21 + (rectangle.width + rectangle.height) * 2
22 + }

View File

@@ -1,24 +1,23 @@
File: sample.rs
1 + /// A rectangle. First line is changed to prevent a regression of #1869
2 struct Rectangle {
3 width: u32,
4 height: u32,
5 }
6
7 _ fn main() {
8 let rect1 = Rectangle { width: 30, height: 50 };
9
10 println!(
11 ~ "The perimeter of the rectangle is {} pixels.",
12 ~ perimeter(&rect1)
13 );
14 + println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 }
16
17 fn area(rectangle: &Rectangle) -> u32 {
18 rectangle.width * rectangle.height
19 }
20 +
21 + fn perimeter(rectangle: &Rectangle) -> u32 {
22 + (rectangle.width + rectangle.height) * 2
23 + }
1 struct Rectangle {
2 width: u32,
3 height: u32,
4 }
5
6 _ fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8
9 println!(
10 ~ "The perimeter of the rectangle is {} pixels.",
11 ~ perimeter(&rect1)
12 );
13 + println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 }
15
16 fn area(rectangle: &Rectangle) -> u32 {
17 rectangle.width * rectangle.height
18 }
19 +
20 + fn perimeter(rectangle: &Rectangle) -> u32 {
21 + (rectangle.width + rectangle.height) * 2
22 + }

View File

@@ -1,5 +1,4 @@
File: sample.rs
+ /// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,23 +1,22 @@
1 + /// A rectangle. First line is changed to prevent a regression of #1869
2 struct Rectangle {
3 width: u32,
4 height: u32,
5 }
6
7 _ fn main() {
8 let rect1 = Rectangle { width: 30, height: 50 };
9
10 println!(
11 ~ "The perimeter of the rectangle is {} pixels.",
12 ~ perimeter(&rect1)
13 );
14 + println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 }
16
17 fn area(rectangle: &Rectangle) -> u32 {
18 rectangle.width * rectangle.height
19 }
20 +
21 + fn perimeter(rectangle: &Rectangle) -> u32 {
22 + (rectangle.width + rectangle.height) * 2
23 + }
1 struct Rectangle {
2 width: u32,
3 height: u32,
4 }
5
6 _ fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8
9 println!(
10 ~ "The perimeter of the rectangle is {} pixels.",
11 ~ perimeter(&rect1)
12 );
13 + println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 }
15
16 fn area(rectangle: &Rectangle) -> u32 {
17 rectangle.width * rectangle.height
18 }
19 +
20 + fn perimeter(rectangle: &Rectangle) -> u32 {
21 + (rectangle.width + rectangle.height) * 2
22 + }

View File

@@ -1,23 +1,22 @@
1 + /// A rectangle. First line is changed to prevent a regression of #1869
2 struct Rectangle {
3 width: u32,
4 height: u32,
5 }
6
7 _ fn main() {
8 let rect1 = Rectangle { width: 30, height: 50 };
9
10 println!(
11 ~ "The perimeter of the rectangle is {} pixels.",
12 ~ perimeter(&rect1)
13 );
14 + println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 }
16
17 fn area(rectangle: &Rectangle) -> u32 {
18 rectangle.width * rectangle.height
19 }
20 +
21 + fn perimeter(rectangle: &Rectangle) -> u32 {
22 + (rectangle.width + rectangle.height) * 2
23 + }
1 struct Rectangle {
2 width: u32,
3 height: u32,
4 }
5
6 _ fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8
9 println!(
10 ~ "The perimeter of the rectangle is {} pixels.",
11 ~ perimeter(&rect1)
12 );
13 + println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 }
15
16 fn area(rectangle: &Rectangle) -> u32 {
17 rectangle.width * rectangle.height
18 }
19 +
20 + fn perimeter(rectangle: &Rectangle) -> u32 {
21 + (rectangle.width + rectangle.height) * 2
22 + }

View File

@@ -1,4 +1,3 @@
+ /// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,27 +1,26 @@
───────┬────────────────────────────────────────────────────────────────────────
│ File: sample.rs
───────┼────────────────────────────────────────────────────────────────────────
1 +/// A rectangle. First line is changed to prevent a regression of #1869
2 │ struct Rectangle {
3 │ width: u32,
4 │ height: u32,
5 │ }
6
7 _fn main() {
8 │ let rect1 = Rectangle { width: 30, height: 50 };
9 │
10 println!(
11 ~ │ "The perimeter of the rectangle is {} pixels.",
12 ~ │ perimeter(&rect1)
13 );
14 + │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 │ }
16 │
17 │ fn area(rectangle: &Rectangle) -> u32 {
18 │ rectangle.width * rectangle.height
19 }
20 + │
21 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
22 + │ (rectangle.width + rectangle.height) * 2
23 + │ }
1 struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 _ │ fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 ~ "The perimeter of the rectangle is {} pixels.",
11 ~ │ perimeter(&rect1)
12 │ );
13 +println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 +
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
───────┴────────────────────────────────────────────────────────────────────────

View File

@@ -1,5 +1,4 @@
────────────────────────────────────────────────────────────────────────────────
/// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,7 +1,6 @@
────────────────────────────────────────────────────────────────────────────────
File: sample.rs
────────────────────────────────────────────────────────────────────────────────
/// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,27 +1,26 @@
─────┬──────────────────────────────────────────────────────────────────────────
│ File: sample.rs
─────┼──────────────────────────────────────────────────────────────────────────
1 │ /// A rectangle. First line is changed to prevent a regression of #1869
2 │ struct Rectangle {
3 │ width: u32,
4 │ height: u32,
5 │ }
6 │
7 │ fn main() {
8 │ let rect1 = Rectangle { width: 30, height: 50 };
9 │
10 │ println!(
11 │ "The perimeter of the rectangle is {} pixels.",
12 │ perimeter(&rect1)
13 │ );
14 │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 │ }
16 │
17 │ fn area(rectangle: &Rectangle) -> u32 {
18 │ rectangle.width * rectangle.height
19 │ }
20 │
21 │ fn perimeter(rectangle: &Rectangle) -> u32 {
22 │ (rectangle.width + rectangle.height) * 2
23 │ }
1 │ struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 │ fn main() {
7 │ let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 │ "The perimeter of the rectangle is {} pixels.",
11 │ perimeter(&rect1)
12 │ );
13 │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 │
20 │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 │ (rectangle.width + rectangle.height) * 2
22 │ }
─────┴──────────────────────────────────────────────────────────────────────────

View File

@@ -1,27 +1,26 @@
─────┬──────────────────────────────────────────────────────────────────────────
│ File: sample.rs
─────┼──────────────────────────────────────────────────────────────────────────
1 │ /// A rectangle. First line is changed to prevent a regression of #1869
2 │ struct Rectangle {
3 │ width: u32,
4 │ height: u32,
5 │ }
6 │
7 │ fn main() {
8 │ let rect1 = Rectangle { width: 30, height: 50 };
9 │
10 │ println!(
11 │ "The perimeter of the rectangle is {} pixels.",
12 │ perimeter(&rect1)
13 │ );
14 │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 │ }
16 │
17 │ fn area(rectangle: &Rectangle) -> u32 {
18 │ rectangle.width * rectangle.height
19 │ }
20 │
21 │ fn perimeter(rectangle: &Rectangle) -> u32 {
22 │ (rectangle.width + rectangle.height) * 2
23 │ }
1 │ struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 │ fn main() {
7 │ let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 │ "The perimeter of the rectangle is {} pixels.",
11 │ perimeter(&rect1)
12 │ );
13 │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 │
20 │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 │ (rectangle.width + rectangle.height) * 2
22 │ }
─────┴──────────────────────────────────────────────────────────────────────────

View File

@@ -1,7 +1,6 @@
────────────────────────────────────────────────────────────────────────────────
File: sample.rs
────────────────────────────────────────────────────────────────────────────────
/// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,25 +1,24 @@
─────┬──────────────────────────────────────────────────────────────────────────
1 │ /// A rectangle. First line is changed to prevent a regression of #1869
2 │ struct Rectangle {
3 │ width: u32,
4 │ height: u32,
5 │ }
6 │
7 │ fn main() {
8 │ let rect1 = Rectangle { width: 30, height: 50 };
9 │
10 │ println!(
11 │ "The perimeter of the rectangle is {} pixels.",
12 │ perimeter(&rect1)
13 │ );
14 │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 │ }
16 │
17 │ fn area(rectangle: &Rectangle) -> u32 {
18 │ rectangle.width * rectangle.height
19 │ }
20 │
21 │ fn perimeter(rectangle: &Rectangle) -> u32 {
22 │ (rectangle.width + rectangle.height) * 2
23 │ }
1 │ struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 │ fn main() {
7 │ let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 │ "The perimeter of the rectangle is {} pixels.",
11 │ perimeter(&rect1)
12 │ );
13 │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 │
20 │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 │ (rectangle.width + rectangle.height) * 2
22 │ }
─────┴──────────────────────────────────────────────────────────────────────────

View File

@@ -1,25 +1,24 @@
─────┬──────────────────────────────────────────────────────────────────────────
1 │ /// A rectangle. First line is changed to prevent a regression of #1869
2 │ struct Rectangle {
3 │ width: u32,
4 │ height: u32,
5 │ }
6 │
7 │ fn main() {
8 │ let rect1 = Rectangle { width: 30, height: 50 };
9 │
10 │ println!(
11 │ "The perimeter of the rectangle is {} pixels.",
12 │ perimeter(&rect1)
13 │ );
14 │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 │ }
16 │
17 │ fn area(rectangle: &Rectangle) -> u32 {
18 │ rectangle.width * rectangle.height
19 │ }
20 │
21 │ fn perimeter(rectangle: &Rectangle) -> u32 {
22 │ (rectangle.width + rectangle.height) * 2
23 │ }
1 │ struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 │ fn main() {
7 │ let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 │ "The perimeter of the rectangle is {} pixels.",
11 │ perimeter(&rect1)
12 │ );
13 │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 │
20 │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 │ (rectangle.width + rectangle.height) * 2
22 │ }
─────┴──────────────────────────────────────────────────────────────────────────

View File

@@ -1,5 +1,4 @@
────────────────────────────────────────────────────────────────────────────────
/// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,5 +1,4 @@
File: sample.rs
/// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,24 +1,23 @@
File: sample.rs
1 /// A rectangle. First line is changed to prevent a regression of #1869
2 struct Rectangle {
3 width: u32,
4 height: u32,
5 }
6
7 fn main() {
8 let rect1 = Rectangle { width: 30, height: 50 };
9
10 println!(
11 "The perimeter of the rectangle is {} pixels.",
12 perimeter(&rect1)
13 );
14 println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 }
16
17 fn area(rectangle: &Rectangle) -> u32 {
18 rectangle.width * rectangle.height
19 }
20
21 fn perimeter(rectangle: &Rectangle) -> u32 {
22 (rectangle.width + rectangle.height) * 2
23 }
1 struct Rectangle {
2 width: u32,
3 height: u32,
4 }
5
6 fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8
9 println!(
10 "The perimeter of the rectangle is {} pixels.",
11 perimeter(&rect1)
12 );
13 println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 }
15
16 fn area(rectangle: &Rectangle) -> u32 {
17 rectangle.width * rectangle.height
18 }
19
20 fn perimeter(rectangle: &Rectangle) -> u32 {
21 (rectangle.width + rectangle.height) * 2
22 }

View File

@@ -1,24 +1,23 @@
File: sample.rs
1 /// A rectangle. First line is changed to prevent a regression of #1869
2 struct Rectangle {
3 width: u32,
4 height: u32,
5 }
6
7 fn main() {
8 let rect1 = Rectangle { width: 30, height: 50 };
9
10 println!(
11 "The perimeter of the rectangle is {} pixels.",
12 perimeter(&rect1)
13 );
14 println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 }
16
17 fn area(rectangle: &Rectangle) -> u32 {
18 rectangle.width * rectangle.height
19 }
20
21 fn perimeter(rectangle: &Rectangle) -> u32 {
22 (rectangle.width + rectangle.height) * 2
23 }
1 struct Rectangle {
2 width: u32,
3 height: u32,
4 }
5
6 fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8
9 println!(
10 "The perimeter of the rectangle is {} pixels.",
11 perimeter(&rect1)
12 );
13 println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 }
15
16 fn area(rectangle: &Rectangle) -> u32 {
17 rectangle.width * rectangle.height
18 }
19
20 fn perimeter(rectangle: &Rectangle) -> u32 {
21 (rectangle.width + rectangle.height) * 2
22 }

View File

@@ -1,5 +1,4 @@
File: sample.rs
/// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,23 +1,22 @@
1 /// A rectangle. First line is changed to prevent a regression of #1869
2 struct Rectangle {
3 width: u32,
4 height: u32,
5 }
6
7 fn main() {
8 let rect1 = Rectangle { width: 30, height: 50 };
9
10 println!(
11 "The perimeter of the rectangle is {} pixels.",
12 perimeter(&rect1)
13 );
14 println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 }
16
17 fn area(rectangle: &Rectangle) -> u32 {
18 rectangle.width * rectangle.height
19 }
20
21 fn perimeter(rectangle: &Rectangle) -> u32 {
22 (rectangle.width + rectangle.height) * 2
23 }
1 struct Rectangle {
2 width: u32,
3 height: u32,
4 }
5
6 fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8
9 println!(
10 "The perimeter of the rectangle is {} pixels.",
11 perimeter(&rect1)
12 );
13 println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 }
15
16 fn area(rectangle: &Rectangle) -> u32 {
17 rectangle.width * rectangle.height
18 }
19
20 fn perimeter(rectangle: &Rectangle) -> u32 {
21 (rectangle.width + rectangle.height) * 2
22 }

View File

@@ -1,23 +1,22 @@
1 /// A rectangle. First line is changed to prevent a regression of #1869
2 struct Rectangle {
3 width: u32,
4 height: u32,
5 }
6
7 fn main() {
8 let rect1 = Rectangle { width: 30, height: 50 };
9
10 println!(
11 "The perimeter of the rectangle is {} pixels.",
12 perimeter(&rect1)
13 );
14 println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 }
16
17 fn area(rectangle: &Rectangle) -> u32 {
18 rectangle.width * rectangle.height
19 }
20
21 fn perimeter(rectangle: &Rectangle) -> u32 {
22 (rectangle.width + rectangle.height) * 2
23 }
1 struct Rectangle {
2 width: u32,
3 height: u32,
4 }
5
6 fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8
9 println!(
10 "The perimeter of the rectangle is {} pixels.",
11 perimeter(&rect1)
12 );
13 println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 }
15
16 fn area(rectangle: &Rectangle) -> u32 {
17 rectangle.width * rectangle.height
18 }
19
20 fn perimeter(rectangle: &Rectangle) -> u32 {
21 (rectangle.width + rectangle.height) * 2
22 }

View File

@@ -1,4 +1,3 @@
/// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,4 +1,3 @@
/// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,4 +1,3 @@
/// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,21 +0,0 @@
%YAML 1.2
---
# http://www.sublimetext.com/docs/3/syntax.html
name: BatTestCustomAssets
file_extensions:
- battestcustomassets
scope: source.battestcustomassets
# This syntax is used to test if custom assets work with bat.
# The way it works is that this syntax is only allowed to be included with
# custom assets. That way it is easy to test if custom assets are used (and works)
# or not.
#
# This syntax is based on CpuInfo.sublime-syntax.
contexts:
main:
- match: '^([^:]+)\w*:\w*(.*)$'
captures:
1: keyword.other.battestcustomassets-key
2: string.other.battestcustomassets-value

View File

@@ -6,7 +6,7 @@ import sys
import os.path as path
import os
import argparse
from multiprocessing import Pool
BAT_OPTIONS = [
"--no-config",
@@ -38,79 +38,64 @@ def get_options(source):
return options
def create_highlighted_version(args):
output_basepath, source = args
env = os.environ.copy()
env.pop("BAT_CACHE_PATH", None)
env.pop("BAT_CONFIG_DIR", None)
env.pop("BAT_CONFIG_PATH", None)
env.pop("BAT_OPTS", None)
env.pop("BAT_PAGER", None)
env.pop("BAT_STYLE", None)
env.pop("BAT_TABS", None)
env.pop("BAT_THEME", None)
env.pop("NO_COLOR", None)
env.pop("PAGER", None)
env["COLORTERM"] = "truecolor" # make sure to output 24bit colors
source_dirname = path.basename(path.dirname(source))
source_filename = path.basename(source)
if source_filename in SKIP_FILENAMES:
return
bat_output = subprocess.check_output(
["bat"] + get_options(source) + [source],
stderr=subprocess.PIPE,
env=env,
)
output_dir = path.join(output_basepath, source_dirname)
output_path = path.join(output_dir, source_filename)
os.makedirs(output_dir, exist_ok=True)
with open(output_path, "wb") as output_file:
output_file.write(bat_output)
print("Created '{}'".format(output_path))
def create_highlighted_versions(output_basepath):
root = os.path.dirname(os.path.abspath(__file__))
source_paths = path.join(root, "source", "*")
sources = path.join(root, "source", "*")
sources = []
for source in glob.glob(path.join(source_paths, "*")) + glob.glob(
path.join(source_paths, ".*")
for source in glob.glob(path.join(sources, "*")) + glob.glob(
path.join(sources, ".*")
):
sources.append((output_basepath, source))
try:
env = os.environ.copy()
env.pop("PAGER", None)
env.pop("BAT_PAGER", None)
env.pop("BAT_CONFIG_PATH", None)
env.pop("BAT_STYLE", None)
env.pop("BAT_THEME", None)
env.pop("BAT_TABS", None)
env["COLORTERM"] = "truecolor" # make sure to output 24bit colors
try:
with Pool() as p:
p.map(create_highlighted_version, sources)
except subprocess.CalledProcessError as err:
print(
"=== Error: Could not highlight source file '{}".format(source),
file=sys.stderr,
)
print(
"=== bat stdout:\n{}".format(err.stdout.decode("utf-8")),
file=sys.stderr,
)
print(
"=== bat stderr:\n{}".format(err.stderr.decode("utf-8")),
file=sys.stderr,
)
return False
except FileNotFoundError:
print(
"Error: Could not execute 'bat'. Please make sure that the executable "
"is available on the PATH."
)
return False
source_dirname = path.basename(path.dirname(source))
source_filename = path.basename(source)
return True
if source_filename in SKIP_FILENAMES:
continue
bat_output = subprocess.check_output(
["bat"] + get_options(source) + [source],
stderr=subprocess.PIPE,
env=env,
)
output_dir = path.join(output_basepath, source_dirname)
output_path = path.join(output_dir, source_filename)
os.makedirs(output_dir, exist_ok=True)
with open(output_path, "wb") as output_file:
output_file.write(bat_output)
print("Created '{}'".format(output_path))
except subprocess.CalledProcessError as err:
print(
"=== Error: Could not highlight source file '{}".format(source),
file=sys.stderr,
)
print(
"=== bat stdout:\n{}".format(err.stdout.decode("utf-8")),
file=sys.stderr,
)
print(
"=== bat stderr:\n{}".format(err.stderr.decode("utf-8")),
file=sys.stderr,
)
sys.exit(1)
except FileNotFoundError:
print(
"Error: Could not execute 'bat'. Please make sure that the executable "
"is available on the PATH."
)
sys.exit(1)
if __name__ == "__main__":
@@ -129,5 +114,4 @@ if __name__ == "__main__":
args = parser.parse_args()
if not create_highlighted_versions(output_basepath=args.output):
sys.exit(1)
create_highlighted_versions(output_basepath=args.output)

View File

@@ -1,2 +0,0 @@
custom assets : 0
are : the best

View File

@@ -0,0 +1,45 @@
processor : 0
model name : ARMv7 Processor rev 3 (v7l)
BogoMIPS : 270.00
Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3
processor : 1
model name : ARMv7 Processor rev 3 (v7l)
BogoMIPS : 270.00
Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3
processor : 2
model name : ARMv7 Processor rev 3 (v7l)
BogoMIPS : 270.00
Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3
processor : 3
model name : ARMv7 Processor rev 3 (v7l)
BogoMIPS : 270.00
Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3
Hardware : BCM2711
Revision : b03111
Serial : 1000000095fd9fc5
Model : Raspberry Pi 4 Model B Rev 1.1

View File

@@ -1,45 +0,0 @@
processor : 0
model name : ARMv7 Processor rev 3 (v7l)
BogoMIPS : 270.00
Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3
processor : 1
model name : ARMv7 Processor rev 3 (v7l)
BogoMIPS : 270.00
Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3
processor : 2
model name : ARMv7 Processor rev 3 (v7l)
BogoMIPS : 270.00
Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3
processor : 3
model name : ARMv7 Processor rev 3 (v7l)
BogoMIPS : 270.00
Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3
Hardware : BCM2711
Revision : b03111
Serial : 1000000095fd9fc5
Model : Raspberry Pi 4 Model B Rev 1.1

View File

@@ -1 +0,0 @@
// foo.ucf-dist (Debian ucf backup) should highlight same as foo

View File

@@ -1 +0,0 @@
// foo.ucf-new (Debian ucf backup) should highlight same as foo

View File

@@ -1 +0,0 @@
// foo.ucf-old (Debian ucf backup) should highlight same as foo

View File

@@ -0,0 +1,53 @@
MemTotal: 1004892 kB
MemFree: 109424 kB
MemAvailable: 498032 kB
Buffers: 66360 kB
Cached: 448344 kB
SwapCached: 0 kB
Active: 547076 kB
Inactive: 196864 kB
Active(anon): 249956 kB
Inactive(anon): 7328 kB
Active(file): 297120 kB
Inactive(file): 189536 kB
Unevictable: 18516 kB
Mlocked: 18516 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 276 kB
Writeback: 0 kB
AnonPages: 247780 kB
Mapped: 168472 kB
Shmem: 19860 kB
KReclaimable: 59128 kB
Slab: 108616 kB
SReclaimable: 59128 kB
SUnreclaim: 49488 kB
KernelStack: 2060 kB
PageTables: 4232 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 502444 kB
Committed_AS: 678300 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 10756 kB
VmallocChunk: 0 kB
Percpu: 784 kB
HardwareCorrupted: 0 kB
AnonHugePages: 0 kB
ShmemHugePages: 0 kB
ShmemPmdMapped: 0 kB
FileHugePages: 0 kB
FilePmdMapped: 0 kB
CmaTotal: 0 kB
CmaFree: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 0 kB
DirectMap4k: 118764 kB
DirectMap2M: 929792 kB
DirectMap1G: 0 kB

View File

@@ -1,53 +0,0 @@
MemTotal: 1004892 kB
MemFree: 109424 kB
MemAvailable: 498032 kB
Buffers: 66360 kB
Cached: 448344 kB
SwapCached: 0 kB
Active: 547076 kB
Inactive: 196864 kB
Active(anon): 249956 kB
Inactive(anon): 7328 kB
Active(file): 297120 kB
Inactive(file): 189536 kB
Unevictable: 18516 kB
Mlocked: 18516 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 276 kB
Writeback: 0 kB
AnonPages: 247780 kB
Mapped: 168472 kB
Shmem: 19860 kB
KReclaimable: 59128 kB
Slab: 108616 kB
SReclaimable: 59128 kB
SUnreclaim: 49488 kB
KernelStack: 2060 kB
PageTables: 4232 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 502444 kB
Committed_AS: 678300 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 10756 kB
VmallocChunk: 0 kB
Percpu: 784 kB
HardwareCorrupted: 0 kB
AnonHugePages: 0 kB
ShmemHugePages: 0 kB
ShmemPmdMapped: 0 kB
FileHugePages: 0 kB
FilePmdMapped: 0 kB
CmaTotal: 0 kB
CmaFree: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 0 kB
DirectMap4k: 118764 kB
DirectMap2M: 929792 kB
DirectMap1G: 0 kB

View File

@@ -1,56 +0,0 @@
#lang racket
(require "main.rkt" rackunit)
;; Helper for test cases with multiple outputs
;; See: https://stackoverflow.com/questions/41081395/unit-testing-in-racket-with-multiple-outputs
(define-syntax check-values-equal?
 (syntax-rules ()
 [(_ a b) (check-equal? (call-with-values (thunk a) list) b)]))
;; Named POSIX semaphores
(test-begin
 (define test-sem-name "/test-nix-1")
 ;; Unlink if already exists
 (sem-unlink test-sem-name)
 ;; Open and unlink
 (define test-sem-p (sem-open test-sem-name (+ O_CREAT O_EXCL)))
 (check-not-false test-sem-p)
 (check-not-equal? test-sem-p (void))
 (check-exn exn:fail?
 (lambda () (sem-open test-sem-name (+ O_CREAT O_EXCL)))
 "Permission denied")
 (check-exn exn:fail?
 (lambda () (sem-open test-sem-name (+ O_CREAT O_EXCL))))
 ;; Change values
 (check-equal? (sem-getvalue test-sem-p) 0)
 (sem-post test-sem-p)
 (check-equal? (sem-getvalue test-sem-p) 1)
 (sem-wait test-sem-p)
 (check-equal? (sem-getvalue test-sem-p) 0)
 (sem-post test-sem-p)
 (check-equal? (sem-getvalue test-sem-p) 1)
 (sem-post test-sem-p)
 (check-equal? (sem-getvalue test-sem-p) 2)
 (sem-trywait test-sem-p)
 (check-equal? (sem-getvalue test-sem-p) 2)
 ;; Can't unlink twice
 (check-not-false (sem-unlink test-sem-name))
 (check-false (sem-unlink test-sem-name)))
;; Named POSIX shared memory
(test-begin
 (define test-shm-name "test-nix-mem-1")
 ;; Open and unlink
 (shm-unlink test-shm-name)
 (define test-shm-fd (shm-open test-shm-name (+ O_EXCL O_CREAT O_RDWR) #o644))
 (check > test-shm-fd 0)
 (check-not-false (shm-unlink test-shm-name))
 (check-false (shm-unlink test-shm-name)))

View File

@@ -1,2 +0,0 @@
custom assets : 0
are : the best

View File

@@ -1 +0,0 @@
// foo.ucf-dist (Debian ucf backup) should highlight same as foo

View File

@@ -1 +0,0 @@
// foo.ucf-new (Debian ucf backup) should highlight same as foo

View File

@@ -1 +0,0 @@
// foo.ucf-old (Debian ucf backup) should highlight same as foo

View File

@@ -1,56 +0,0 @@
#lang racket
(require "main.rkt" rackunit)
;; Helper for test cases with multiple outputs
;; See: https://stackoverflow.com/questions/41081395/unit-testing-in-racket-with-multiple-outputs
(define-syntax check-values-equal?
(syntax-rules ()
[(_ a b) (check-equal? (call-with-values (thunk a) list) b)]))
;; Named POSIX semaphores
(test-begin
(define test-sem-name "/test-nix-1")
;; Unlink if already exists
(sem-unlink test-sem-name)
;; Open and unlink
(define test-sem-p (sem-open test-sem-name (+ O_CREAT O_EXCL)))
(check-not-false test-sem-p)
(check-not-equal? test-sem-p (void))
(check-exn exn:fail?
(lambda () (sem-open test-sem-name (+ O_CREAT O_EXCL)))
"Permission denied")
(check-exn exn:fail?
(lambda () (sem-open test-sem-name (+ O_CREAT O_EXCL))))
;; Change values
(check-equal? (sem-getvalue test-sem-p) 0)
(sem-post test-sem-p)
(check-equal? (sem-getvalue test-sem-p) 1)
(sem-wait test-sem-p)
(check-equal? (sem-getvalue test-sem-p) 0)
(sem-post test-sem-p)
(check-equal? (sem-getvalue test-sem-p) 1)
(sem-post test-sem-p)
(check-equal? (sem-getvalue test-sem-p) 2)
(sem-trywait test-sem-p)
(check-equal? (sem-getvalue test-sem-p) 2)
;; Can't unlink twice
(check-not-false (sem-unlink test-sem-name))
(check-false (sem-unlink test-sem-name)))
;; Named POSIX shared memory
(test-begin
(define test-shm-name "test-nix-mem-1")
;; Open and unlink
(shm-unlink test-shm-name)
(define test-shm-fd (shm-open test-shm-name (+ O_EXCL O_CREAT O_RDWR) #o644))
(check > test-shm-fd 0)
(check-not-false (shm-unlink test-shm-name))
(check-false (shm-unlink test-shm-name)))

View File

@@ -26,10 +26,6 @@
onMount(hashchange);
</script>
<script type="text/livescript">
// This block is a regression test for a bat panic when a LiveScript syntax definition is missing
</script>
<style>
main {
position: relative;

View File

@@ -1,74 +0,0 @@
#!/usr/bin/env bash
set -o errexit -o nounset -o pipefail
### ENVIRONMENT
BAT_CONFIG_DIR=$(mktemp -d)
export BAT_CONFIG_DIR
BAT_CACHE_PATH=$(mktemp -d)
export BAT_CACHE_PATH
echo "
BAT_CONFIG_DIR = ${BAT_CONFIG_DIR}
BAT_CACHE_PATH = ${BAT_CACHE_PATH}
"
### HELPER VARS
custom_syntax_args=(
"--language=BatTestCustomAssets"
"tests/syntax-tests/source/BatTestCustomAssets/NoColorsUnlessCustomAssetsAreUsed.battestcustomassets"
)
integrated_syntax_args=(
"--language=Rust"
"examples/simple.rs"
)
### HELPER FUNCTIONS
echo_step() {
echo -e "\n== $1 =="
}
fail_test() {
echo -e "FAIL: $1"
exit 1
}
### TEST STEPS
echo_step "TEST: Make sure 'BatTestCustomAssets' is not part of integrated syntaxes"
bat -f "${custom_syntax_args[@]}" &&
fail_test "EXPECTED: 'unknown syntax' error ACTUAL: no error occured"
echo_step "PREPARE: Install custom syntax 'BatTestCustomAssets'"
custom_syntaxes_dir="$(bat --config-dir)/syntaxes"
mkdir -p "${custom_syntaxes_dir}"
cp -v "tests/syntax-tests/BatTestCustomAssets.sublime-syntax" \
"${custom_syntaxes_dir}/BatTestCustomAssets.sublime-syntax"
echo_step "PREPARE: Build custom assets to enable 'BatTestCustomAssets' syntax"
bat cache --build
echo_step "TEST: 'BatTestCustomAssets' is a known syntax"
bat -f "${custom_syntax_args[@]}" ||
fail_test "EXPECTED: syntax highlighting to work ACTUAL: there was an error"
echo_step "TEST: The 'Rust' syntax is still available"
bat -f "${integrated_syntax_args[@]}" ||
fail_test "EXPECTED: syntax highlighting still works with integrated assets ACTUAL: there was an error"
echo_step "TEST: 'BatTestCustomAssets' is an unknown syntax with --no-custom-assets"
bat -f --no-custom-assets "${custom_syntax_args[@]}" &&
fail_test "EXPECTED: 'unknown syntax' error because of --no-custom-assets ACTUAL: no error occured"
echo_step "TEST: 'bat cache --clear' removes all files"
bat cache --clear
remaining_files=$(ls -A "${BAT_CACHE_PATH}")
[ -z "${remaining_files}" ] ||
fail_test "EXPECTED: no files remain ACTUAL: some files remain:\n${remaining_files}"
echo_step "CLEAN"
rm -rv "${BAT_CONFIG_DIR}" "${BAT_CACHE_PATH}"