mirror of
https://github.com/sharkdp/bat.git
synced 2025-09-01 10:52:24 +01:00
Compare commits
154 Commits
release-v0
...
create_hig
Author | SHA1 | Date | |
---|---|---|---|
|
271842d87c | ||
|
ed3246c423 | ||
|
2339d78bf4 | ||
|
3a3cd0acba | ||
|
ce4ddc0911 | ||
|
aed4ea144f | ||
|
eea061c1d9 | ||
|
10288e309e | ||
|
ebdb00d4fc | ||
|
6fc7ebf37a | ||
|
8f6a0cd9e2 | ||
|
994c21a5e1 | ||
|
554374667e | ||
|
043f3381b0 | ||
|
d04a83de7b | ||
|
b622a4d890 | ||
|
b551d28a2f | ||
|
e6caa04209 | ||
|
adadedeab1 | ||
|
a6cf5235aa | ||
|
1477338106 | ||
|
418fce5683 | ||
|
7a15ba3796 | ||
|
0f002a5b06 | ||
|
5344a32d34 | ||
|
c9645693a4 | ||
|
f607263bdc | ||
|
f309d2fbd2 | ||
|
47283f226a | ||
|
aefc8fd824 | ||
|
405a80f3ee | ||
|
ad98d35a48 | ||
|
b69ab219d7 | ||
|
dc8225f682 | ||
|
9d9b266f54 | ||
|
b9d01c1a61 | ||
|
122cae7902 | ||
|
44a332c1c4 | ||
|
5143f3ad43 | ||
|
a6dc25a216 | ||
|
f04d2a9d6a | ||
|
eb3b3b9f8d | ||
|
0994f3783f | ||
|
974dec38e3 | ||
|
a0c363647f | ||
|
d989224a8a | ||
|
82f439e715 | ||
|
b034879eae | ||
|
9ed9a6fc3d | ||
|
e84b702309 | ||
|
6226eba52a | ||
|
9e0ea06435 | ||
|
863d9cacd0 | ||
|
4baa346aae | ||
|
7956485e37 | ||
|
372e42f350 | ||
|
9124271eaf | ||
|
156dec2737 | ||
|
27f046ec03 | ||
|
74ae3dee91 | ||
|
87978e7755 | ||
|
d935ea1cda | ||
|
9602195910 | ||
|
8ca852c728 | ||
|
df067f7d1f | ||
|
a8a81e99d2 | ||
|
65e7c531de | ||
|
7c41bd72da | ||
|
355a82db54 | ||
|
b3e17bde82 | ||
|
4b38e7b1d7 | ||
|
43afae34be | ||
|
27fa55d274 | ||
|
19c3e82abf | ||
|
f1c0fd7343 | ||
|
12dfbdc400 | ||
|
c86a179412 | ||
|
0748783404 | ||
|
b3247d9364 | ||
|
ba8a694314 | ||
|
ff70a80741 | ||
|
ecdb17148d | ||
|
11bd523f7e | ||
|
01fbedc246 | ||
|
05e4e1f2f2 | ||
|
20223ad77c | ||
|
51edacb5eb | ||
|
5197ef9048 | ||
|
19678527e5 | ||
|
5d319dee94 | ||
|
43e1a11ad8 | ||
|
ed09f90e5e | ||
|
cbd96237fd | ||
|
f5c1cb2dff | ||
|
5eb93a6eae | ||
|
25fa577cd0 | ||
|
deddc81426 | ||
|
133b06e945 | ||
|
699f1e65cc | ||
|
9ef87dab27 | ||
|
5125e9c941 | ||
|
6c62ed5608 | ||
|
bf78288e9e | ||
|
f8498b260b | ||
|
79f08588c6 | ||
|
2d92a4dbb3 | ||
|
f508ddf66d | ||
|
02218c916c | ||
|
89217e0d58 | ||
|
cb4973987b | ||
|
905902d811 | ||
|
c83e382eac | ||
|
f6975e2acd | ||
|
d8b813c0bf | ||
|
5236ed135e | ||
|
47d955a2ab | ||
|
bd797c75a4 | ||
|
05c11964fc | ||
|
8ecd23eab4 | ||
|
1ef0206f24 | ||
|
6694aa369e | ||
|
0331d28ee4 | ||
|
51c7eb7ac1 | ||
|
5516bcb839 | ||
|
056b966501 | ||
|
28eca6a2be | ||
|
b7fd55242e | ||
|
8161955cc7 | ||
|
3b020fd95a | ||
|
697d106bd4 | ||
|
50e1c6074f | ||
|
a610987ef7 | ||
|
a7fd9f4b1b | ||
|
5f5b77cdda | ||
|
83808a63be | ||
|
6d5ff671e7 | ||
|
ffdf349a96 | ||
|
f3d53b79a2 | ||
|
6acec2c074 | ||
|
1bac3750df | ||
|
b040efff79 | ||
|
a81009607a | ||
|
c0e09662b4 | ||
|
ccf4563573 | ||
|
fb1ab09e3e | ||
|
f464b1ba39 | ||
|
2ea6348b85 | ||
|
6e536ab06d | ||
|
7537e309d8 | ||
|
84e2a2e5d1 | ||
|
f6fc826dc6 | ||
|
375d55aa5d | ||
|
6ef2bb3283 | ||
|
fc0794a83d |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
github: [sharkdp, keith-hall, Enselic]
|
1
.github/ISSUE_TEMPLATE/config.yml
vendored
1
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,2 +1 @@
|
||||
blank_issues_enabled: true
|
||||
|
||||
|
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -7,4 +7,3 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
1
.github/ISSUE_TEMPLATE/question.md
vendored
1
.github/ISSUE_TEMPLATE/question.md
vendored
@@ -7,4 +7,3 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
96
.github/workflows/CICD.yml
vendored
96
.github/workflows/CICD.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: CICD
|
||||
|
||||
env:
|
||||
MIN_SUPPORTED_RUST_VERSION: "1.45.0"
|
||||
MIN_SUPPORTED_RUST_VERSION: "1.46.0"
|
||||
CICD_INTERMEDIATES_DIR: "_cicd-intermediates"
|
||||
|
||||
on:
|
||||
@@ -14,9 +14,22 @@ 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-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
@@ -32,15 +45,16 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --all-targets --all-features
|
||||
args: --locked --all-targets --all-features -- --allow clippy::unknown_clippy_lints
|
||||
- name: Run tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --locked
|
||||
|
||||
test_with_new_syntaxes_and_themes:
|
||||
name: Run tests with updated syntaxes and themes
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Git checkout
|
||||
uses: actions/checkout@v2
|
||||
@@ -68,44 +82,59 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --release
|
||||
args: --locked --release
|
||||
- name: Run ignored-by-default unit tests with new syntaxes and themes
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --release -- --ignored
|
||||
args: --locked --release -- --ignored
|
||||
- name: Syntax highlighting regression test
|
||||
run: tests/syntax-tests/regression_test.sh
|
||||
- name: List of languages
|
||||
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
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: doc
|
||||
args: --no-deps --document-private-items --all-features
|
||||
args: --locked --no-deps --document-private-items --all-features
|
||||
|
||||
build:
|
||||
name: ${{ matrix.job.os }} (${{ matrix.job.target }})
|
||||
name: ${{ matrix.job.target }} (${{ matrix.job.os }})
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- { os: ubuntu-18.04, target: arm-unknown-linux-gnueabihf , use-cross: true }
|
||||
- { os: ubuntu-18.04, target: aarch64-unknown-linux-gnu , use-cross: true }
|
||||
- { os: ubuntu-18.04, target: i686-unknown-linux-gnu , use-cross: true }
|
||||
- { os: ubuntu-18.04, target: i686-unknown-linux-musl , use-cross: true }
|
||||
- { os: ubuntu-18.04, target: x86_64-unknown-linux-gnu }
|
||||
- { os: ubuntu-18.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 }
|
||||
- { 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 }
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
@@ -114,7 +143,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
case ${{ matrix.job.target }} in
|
||||
arm-unknown-linux-gnueabihf) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
|
||||
arm-unknown-linux-*) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
|
||||
aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;;
|
||||
esac
|
||||
|
||||
@@ -149,7 +178,7 @@ jobs:
|
||||
with:
|
||||
use-cross: ${{ matrix.job.use-cross }}
|
||||
command: build
|
||||
args: --release --target=${{ matrix.job.target }}
|
||||
args: --locked --release --target=${{ matrix.job.target }}
|
||||
|
||||
- name: Strip debug information from executable
|
||||
id: strip
|
||||
@@ -164,7 +193,7 @@ jobs:
|
||||
# Figure out what strip tool to use if any
|
||||
STRIP="strip"
|
||||
case ${{ matrix.job.target }} in
|
||||
arm-unknown-linux-gnueabihf) STRIP="arm-linux-gnueabihf-strip" ;;
|
||||
arm-unknown-linux-*) STRIP="arm-linux-gnueabihf-strip" ;;
|
||||
aarch64-unknown-linux-gnu) STRIP="aarch64-linux-gnu-strip" ;;
|
||||
*-pc-windows-msvc) STRIP="" ;;
|
||||
esac;
|
||||
@@ -201,49 +230,56 @@ jobs:
|
||||
with:
|
||||
use-cross: ${{ matrix.job.use-cross }}
|
||||
command: test
|
||||
args: --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}}
|
||||
args: --locked --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}}
|
||||
|
||||
- name: Run bat
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.job.use-cross }}
|
||||
command: run
|
||||
args: --target=${{ matrix.job.target }} -- --paging=never --color=always --theme=ansi Cargo.toml src/config.rs
|
||||
args: --locked --target=${{ matrix.job.target }} -- --paging=never --color=always --theme=ansi Cargo.toml src/config.rs
|
||||
|
||||
- name: Show diagnostics (bat --diagnostic)
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.job.use-cross }}
|
||||
command: run
|
||||
args: --target=${{ matrix.job.target }} -- --paging=never --color=always --theme=ansi Cargo.toml src/config.rs --diagnostic
|
||||
args: --locked --target=${{ matrix.job.target }} -- --paging=never --color=always --theme=ansi Cargo.toml src/config.rs --diagnostic
|
||||
|
||||
- name: "Feature check: regex-onig"
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.job.use-cross }}
|
||||
command: check
|
||||
args: --target=${{ matrix.job.target }} --verbose --lib --no-default-features --features regex-onig
|
||||
args: --locked --target=${{ matrix.job.target }} --verbose --lib --no-default-features --features regex-onig
|
||||
|
||||
- name: "Feature check: regex-onig,git"
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.job.use-cross }}
|
||||
command: check
|
||||
args: --target=${{ matrix.job.target }} --verbose --lib --no-default-features --features regex-onig,git
|
||||
args: --locked --target=${{ matrix.job.target }} --verbose --lib --no-default-features --features regex-onig,git
|
||||
|
||||
- name: "Feature check: regex-onig,paging"
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.job.use-cross }}
|
||||
command: check
|
||||
args: --target=${{ matrix.job.target }} --verbose --lib --no-default-features --features regex-onig,paging
|
||||
args: --locked --target=${{ matrix.job.target }} --verbose --lib --no-default-features --features regex-onig,paging
|
||||
|
||||
- name: "Feature check: regex-onig,git,paging"
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.job.use-cross }}
|
||||
command: check
|
||||
args: --target=${{ matrix.job.target }} --verbose --lib --no-default-features --features regex-onig,git,paging
|
||||
args: --locked --target=${{ matrix.job.target }} --verbose --lib --no-default-features --features regex-onig,git,paging
|
||||
|
||||
- name: "Feature check: minimal-application"
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.job.use-cross }}
|
||||
command: check
|
||||
args: --locked --target=${{ matrix.job.target }} --verbose --no-default-features --features minimal-application
|
||||
|
||||
- name: Create tarball
|
||||
id: package
|
||||
@@ -271,6 +307,7 @@ 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
|
||||
@@ -319,6 +356,7 @@ 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 }}"
|
||||
|
||||
|
17
.gitmodules
vendored
17
.gitmodules
vendored
@@ -217,4 +217,19 @@
|
||||
url = https://github.com/vidann1/visual-studio-dark-plus.git
|
||||
[submodule "assets/syntaxes/02_Extra/SublimeEthereum"]
|
||||
path = assets/syntaxes/02_Extra/SublimeEthereum
|
||||
url = https://github.com/davidhq/SublimeEthereum.git
|
||||
url = https://github.com/davidhq/SublimeEthereum.git
|
||||
[submodule "assets/syntaxes/02_Extra/Groff"]
|
||||
path = assets/syntaxes/02_Extra/Groff
|
||||
url = https://github.com/carsonoid/sublime_man_page_support
|
||||
[submodule "assets/syntaxes/02_Extra/http-request-response"]
|
||||
path = assets/syntaxes/02_Extra/http-request-response
|
||||
url = https://github.com/keith-hall/http-request-response-syntax.git
|
||||
[submodule "assets/syntaxes/02_Extra/LLVM"]
|
||||
path = assets/syntaxes/02_Extra/LLVM
|
||||
url = https://github.com/ioncodes/LLVM.tmBundle
|
||||
[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
|
||||
|
22
CHANGELOG.md
22
CHANGELOG.md
@@ -2,21 +2,43 @@
|
||||
|
||||
## 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
|
||||
|
||||
## Syntaxes
|
||||
|
||||
- Groff, see #1685 (@scop)
|
||||
- HTTP Requests and Responses, see #1748 (@keith-hall)
|
||||
- LLVM, see #1777 (@ioncodes)
|
||||
- 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)
|
||||
|
||||
|
||||
|
||||
|
149
Cargo.lock
generated
149
Cargo.lock
generated
@@ -46,9 +46,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "assert_cmd"
|
||||
version = "1.0.5"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a88b6bd5df287567ffdf4ddf4d33060048e1068308e5f62d81c6f9824a045a48"
|
||||
checksum = "b800c4403e8105d959595e1f88119e78bc12bc874c4336973658b648a746ba93"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"doc-comment",
|
||||
@@ -89,6 +89,7 @@ dependencies = [
|
||||
"ansi_term 0.12.1",
|
||||
"assert_cmd",
|
||||
"atty",
|
||||
"bincode",
|
||||
"bugreport",
|
||||
"clap",
|
||||
"clircle",
|
||||
@@ -96,11 +97,12 @@ dependencies = [
|
||||
"content_inspector",
|
||||
"dirs-next",
|
||||
"encoding",
|
||||
"error-chain",
|
||||
"flate2",
|
||||
"git2",
|
||||
"globset",
|
||||
"grep-cli",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"nix",
|
||||
"path_abs",
|
||||
"predicates",
|
||||
@@ -111,6 +113,7 @@ dependencies = [
|
||||
"shell-words",
|
||||
"syntect",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"unicode-width",
|
||||
"wait-timeout",
|
||||
"wild",
|
||||
@@ -142,9 +145,9 @@ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
@@ -261,10 +264,10 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "difference"
|
||||
version = "2.0.0"
|
||||
name = "difflib"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
|
||||
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
||||
|
||||
[[package]]
|
||||
name = "dirs-next"
|
||||
@@ -299,6 +302,12 @@ version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.6"
|
||||
@@ -369,20 +378,11 @@ 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.3.5"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae91abf6555234338687bb47913978d275539235fcb77ba9863b779090b42b14"
|
||||
checksum = "9d6b8560a05112eb52f04b00e5d3790c0dd75d9d980eb8a122fb23b92a623ccf"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"regex",
|
||||
@@ -390,9 +390,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.20"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0"
|
||||
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crc32fast",
|
||||
@@ -402,9 +402,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "float-cmp"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4"
|
||||
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
@@ -552,6 +552,15 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.7"
|
||||
@@ -581,9 +590,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.95"
|
||||
version = "0.2.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36"
|
||||
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
@@ -675,9 +684,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.21.0"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c3728fec49d363a50a8828a190b379a446cc5cf085c06259bbbeb34447e4ec7"
|
||||
checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
@@ -773,15 +782,6 @@ version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
|
||||
dependencies = [
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.19"
|
||||
@@ -810,12 +810,13 @@ checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
|
||||
[[package]]
|
||||
name = "predicates"
|
||||
version = "1.0.8"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f49cfaf7fdaa3bfacc6fa3e7054e65148878354a5cfddcf661df4c851f8021df"
|
||||
checksum = "c143348f141cc87aab5b950021bac6145d0e5ae754b0591de23244cee42c9308"
|
||||
dependencies = [
|
||||
"difference",
|
||||
"difflib",
|
||||
"float-cmp",
|
||||
"itertools",
|
||||
"normalize-line-endings",
|
||||
"predicates-core",
|
||||
"regex",
|
||||
@@ -984,36 +985,24 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.11.0"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
|
||||
dependencies = [
|
||||
"pest",
|
||||
]
|
||||
checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.126"
|
||||
version = "1.0.130"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
|
||||
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.126"
|
||||
version = "1.0.130"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
|
||||
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1033,12 +1022,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.8.17"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23"
|
||||
checksum = "d8c608a35705a5d3cdc9fbe403147647ff34b921f8e833e49306df898f9b20af"
|
||||
dependencies = [
|
||||
"dtoa",
|
||||
"linked-hash-map",
|
||||
"indexmap",
|
||||
"serde",
|
||||
"yaml-rust",
|
||||
]
|
||||
@@ -1108,9 +1097,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syntect"
|
||||
version = "4.5.0"
|
||||
version = "4.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bfac2b23b4d049dc9a89353b4e06bbc85a8f42020cccbe5409a115cf19031e5"
|
||||
checksum = "8b20815bbe80ee0be06e6957450a841185fcf690fe0178f14d77a05ce2caa031"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bitflags",
|
||||
@@ -1192,6 +1181,26 @@ 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"
|
||||
@@ -1213,12 +1222,6 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41"
|
||||
|
||||
[[package]]
|
||||
name = "ucd-trie"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.5"
|
||||
@@ -1239,9 +1242,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
@@ -1273,12 +1276,6 @@ 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"
|
||||
|
41
Cargo.toml
41
Cargo.toml
@@ -16,17 +16,26 @@ default = ["application"]
|
||||
# Feature required for bat the application. Should be disabled when depending on
|
||||
# bat as a library.
|
||||
application = [
|
||||
"bugreport",
|
||||
"build-assets",
|
||||
"git",
|
||||
"minimal-application",
|
||||
]
|
||||
# Mainly for developers that want to iterate quickly
|
||||
# Be aware that the included features might change in the future
|
||||
minimal-application = [
|
||||
"atty",
|
||||
"clap",
|
||||
"dirs-next",
|
||||
"git",
|
||||
"lazy_static",
|
||||
"paging",
|
||||
"wild",
|
||||
"regex-onig",
|
||||
"wild",
|
||||
]
|
||||
git = ["git2"] # Support indicating git modifications
|
||||
paging = ["shell-words"] # Support applying a pager on the output
|
||||
paging = ["shell-words", "grep-cli"] # 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"]
|
||||
|
||||
# You need to use one of these if you depend on bat as a library:
|
||||
regex-onig = ["syntect/regex-onig"] # Use the "oniguruma" regex engine
|
||||
@@ -36,22 +45,26 @@ 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.8"
|
||||
unicode-width = "0.1.9"
|
||||
globset = "0.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_yaml = "0.8"
|
||||
semver = "0.11"
|
||||
semver = "1.0"
|
||||
path_abs = { version = "0.5", default-features = false }
|
||||
clircle = "0.3"
|
||||
bugreport = "0.4"
|
||||
bugreport = { version = "0.4", optional = true }
|
||||
dirs-next = { version = "2.0.0", optional = true }
|
||||
grep-cli = "0.1.6"
|
||||
grep-cli = { version = "0.1.6", optional = true }
|
||||
|
||||
[dependencies.git2]
|
||||
version = "0.13"
|
||||
@@ -59,9 +72,9 @@ optional = true
|
||||
default-features = false
|
||||
|
||||
[dependencies.syntect]
|
||||
version = "4.5.0"
|
||||
version = "4.6.0"
|
||||
default-features = false
|
||||
features = ["parsing", "yaml-load", "dump-load", "dump-create"]
|
||||
features = ["parsing"]
|
||||
|
||||
[dependencies.clap]
|
||||
version = "2.33"
|
||||
@@ -69,19 +82,15 @@ optional = true
|
||||
default-features = false
|
||||
features = ["suggestions", "color", "wrap_help"]
|
||||
|
||||
[dependencies.error-chain]
|
||||
version = "0.12"
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = "1.0.5"
|
||||
assert_cmd = "2.0.1"
|
||||
serial_test = "0.5.1"
|
||||
predicates = "1.0.7"
|
||||
predicates = "2.0.2"
|
||||
wait-timeout = "0.2.0"
|
||||
tempfile = "3.2.0"
|
||||
|
||||
[target.'cfg(unix)'.dev-dependencies]
|
||||
nix = "0.21.0"
|
||||
nix = "0.23.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "2.33", optional = true }
|
||||
|
31
README.md
31
README.md
@@ -12,7 +12,11 @@
|
||||
<a href="#installation">Installation</a> •
|
||||
<a href="#customization">Customization</a> •
|
||||
<a href="#project-goals-and-alternatives">Project goals, alternatives</a><br>
|
||||
[<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>]
|
||||
[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>]
|
||||
</p>
|
||||
|
||||
### Syntax highlighting
|
||||
@@ -196,10 +200,7 @@ 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 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.
|
||||
`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).
|
||||
|
||||
If your Ubuntu/Debian installation is new enough you can simply run:
|
||||
|
||||
@@ -251,6 +252,14 @@ 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)
|
||||
@@ -289,6 +298,14 @@ 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):
|
||||
@@ -363,7 +380,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.45 or
|
||||
If you want to build `bat` from source, you need Rust 1.46 or
|
||||
higher. You can then use `cargo` to build everything:
|
||||
|
||||
```bash
|
||||
@@ -598,7 +615,7 @@ Example configuration file:
|
||||
# Use italic text on the terminal (not supported on all terminals)
|
||||
--italic-text=always
|
||||
|
||||
# Use C++ syntax for .ino files
|
||||
# Use C++ syntax for Arduino .ino files
|
||||
--map-syntax "*.ino:C++"
|
||||
```
|
||||
|
||||
|
92
assets/completions/_bat.ps1.in
vendored
Normal file
92
assets/completions/_bat.ps1.in
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
|
||||
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
|
||||
}
|
4
assets/completions/bat.fish.in
vendored
4
assets/completions/bat.fish.in
vendored
@@ -34,7 +34,7 @@ complete -c {{PROJECT_EXECUTABLE}} -s H -l highlight-line -x -d "<N> Highlight t
|
||||
|
||||
complete -c {{PROJECT_EXECUTABLE}} -l italic-text -xka "always never" -d "Specify when to use ANSI sequences for italic text (default: never)" -n "not __fish_seen_subcommand_from cache"
|
||||
|
||||
complete -c {{PROJECT_EXECUTABLE}} -s l -l language -d "Set the language for syntax highlighting" -n "not __fish_seen_subcommand_from cache" -xa "(__{{PROJECT_EXECUTABLE}}_autocomplete_languages)"
|
||||
complete -c {{PROJECT_EXECUTABLE}} -s l -l language -d "Set the language for syntax highlighting" -n "not __fish_seen_subcommand_from cache" -xa "(__{{PROJECT_EXECUTABLE}}_autocomplete_languages)"
|
||||
|
||||
complete -c {{PROJECT_EXECUTABLE}} -s r -l line-range -x -d "<N:M> Only print the specified range of lines for each file" -n "not __fish_seen_subcommand_from cache"
|
||||
|
||||
@@ -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 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 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 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"
|
||||
|
||||
|
3
assets/completions/bat.zsh.in
vendored
3
assets/completions/bat.zsh.in
vendored
@@ -45,6 +45,7 @@ _{{PROJECT_EXECUTABLE}}_main() {
|
||||
'(-r --line-range)'{-r+,--line-range=}'[Only print the lines from N to M]:<N\:M>...'
|
||||
'(: --list-themes --list-languages -L)'{-L,--list-languages}'[Display all supported languages]'
|
||||
'(: --no-config)'--no-config'[Do not use the configuration file]'
|
||||
'(: --no-custom-assets)'--no-custom-assets'[Do not load custom assets]'
|
||||
'(: --config-dir)'--config-dir'[Show bat'"'"'s configuration directory]'
|
||||
'(: --config-file)'--config-file'[Show path to the configuration file]'
|
||||
'(: --generate-config-file)'--generate-config-file'[Generates a default configuration file]'
|
||||
@@ -74,7 +75,7 @@ _{{PROJECT_EXECUTABLE}}_main() {
|
||||
;;
|
||||
|
||||
style)
|
||||
_values -s , 'style' auto full plain changes header grid numbers snip
|
||||
_values -s , 'style' auto full plain changes header grid rule numbers snip
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
2
assets/create.sh
vendored
2
assets/create.sh
vendored
@@ -5,7 +5,7 @@ ASSET_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
REPO_DIR="$ASSET_DIR/.."
|
||||
|
||||
# Ensure submodules are initialized.
|
||||
function update_submodules() {
|
||||
update_submodules() {
|
||||
local submodule
|
||||
local submodule_prompt=unspecified
|
||||
local submodule_path
|
||||
|
8
assets/manual/bat.1.in
vendored
8
assets/manual/bat.1.in
vendored
@@ -50,6 +50,8 @@ 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>...
|
||||
@@ -142,7 +144,7 @@ Configure which elements (line numbers, file headers, grid borders, Git modifica
|
||||
of components to display (e.g. 'numbers,changes,grid') or a pre\-defined style ('full').
|
||||
To set a default style, add the '\-\-style=".."' option to the configuration file or
|
||||
export the BAT_STYLE environment variable (e.g.: export BAT_STYLE=".."). Possible
|
||||
values: *auto*, full, plain, changes, header, grid, rule, numbers, snip.
|
||||
values: *full*, auto, plain, changes, header, grid, rule, numbers, snip.
|
||||
.HP
|
||||
\fB\-r\fR, \fB\-\-line\-range\fR <N:M>...
|
||||
.IP
|
||||
@@ -154,6 +156,8 @@ 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
|
||||
@@ -218,7 +222,7 @@ git clone https://github.com/tellnobody1/sublime-purescript-syntax
|
||||
|
||||
Once the cache is built, the new language will be visible in `\fB{{PROJECT_EXECUTABLE}} --list-languages\fR`.
|
||||
.br
|
||||
If you ever want to remove the custom languages, you can clear the cache with `\fB{{PROJECT_EXECUTABLE}} cache --clear\fR`.
|
||||
If you ever want to remove the custom languages, you can clear the cache with `\fB{{PROJECT_EXECUTABLE}} cache --clear\fR`.
|
||||
|
||||
.SH "ADDING CUSTOM THEMES"
|
||||
Similarly to custom languages, {{PROJECT_EXECUTABLE}} supports Sublime Text \fB.tmTheme\fR themes.
|
||||
|
BIN
assets/minimal_syntaxes.bin
vendored
Normal file
BIN
assets/minimal_syntaxes.bin
vendored
Normal file
Binary file not shown.
16
assets/patches/Groff.sublime-syntax.patch
vendored
Normal file
16
assets/patches/Groff.sublime-syntax.patch
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
diff --git syntaxes/02_Extra/Groff/Man Page/Man Page.sublime-syntax syntaxes/02_Extra/Groff/Man Page/Man Page.sublime-syntax
|
||||
index 57834af..6648664 100644
|
||||
--- syntaxes/02_Extra/Groff/Man Page/Man Page.sublime-syntax
|
||||
+++ syntaxes/02_Extra/Groff/Man Page/Man Page.sublime-syntax
|
||||
@@ -4,9 +4,9 @@
|
||||
# - man-pages(7)
|
||||
# - groff(7)
|
||||
---
|
||||
-name: Man Page (groff/troff)
|
||||
+name: Groff/troff
|
||||
scope: text.groff
|
||||
-file_extensions: [man, groff, troff, '1', '2', '3', '4', '5', '6', '7']
|
||||
+file_extensions: [groff, troff, '1', '2', '3', '4', '5', '6', '7', '8', '9']
|
||||
|
||||
contexts:
|
||||
main:
|
11
assets/patches/Markdown.sublime-syntax.patch
vendored
11
assets/patches/Markdown.sublime-syntax.patch
vendored
@@ -1,5 +1,5 @@
|
||||
diff --git syntaxes/01_Packages/Markdown/Markdown.sublime-syntax syntaxes/01_Packages/Markdown/Markdown.sublime-syntax
|
||||
index 19dc685d..6afd87ae 100644
|
||||
index 19dc685d..44440c7f 100644
|
||||
--- syntaxes/01_Packages/Markdown/Markdown.sublime-syntax
|
||||
+++ syntaxes/01_Packages/Markdown/Markdown.sublime-syntax
|
||||
@@ -24,7 +24,6 @@ variables:
|
||||
@@ -166,3 +166,12 @@ index 19dc685d..6afd87ae 100644
|
||||
- match: ^\s*$\n?
|
||||
scope: invalid.illegal.non-terminated.bold-italic.markdown
|
||||
pop: true
|
||||
@@ -1152,7 +1110,7 @@ contexts:
|
||||
- match: |-
|
||||
(?x)
|
||||
{{fenced_code_block_start}}
|
||||
- ((?i:rust))
|
||||
+ ((?i:rust|rs))
|
||||
{{fenced_code_block_trailing_infostring_characters}}
|
||||
captures:
|
||||
0: meta.code-fence.definition.begin.rust.markdown-gfm
|
||||
|
15
assets/patches/Python.sublime-syntax.patch
vendored
Normal file
15
assets/patches/Python.sublime-syntax.patch
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
diff --git syntaxes/01_Packages/Python/Python.sublime-syntax syntaxes/01_Packages/Python/Python.sublime-syntax
|
||||
index 2acd86d8..86257f7b 100644
|
||||
--- syntaxes/01_Packages/Python/Python.sublime-syntax
|
||||
+++ syntaxes/01_Packages/Python/Python.sublime-syntax
|
||||
@@ -988,10 +988,6 @@ contexts:
|
||||
- match: \}
|
||||
scope: punctuation.section.mapping-or-set.end.python
|
||||
set: after-expression
|
||||
- - match: (?={{simple_expression}}:|\s*\*\*)
|
||||
- set: inside-dictionary
|
||||
- - match: (?={{simple_expression}}[,}]|\s*\*)
|
||||
- set: inside-set
|
||||
- match: ','
|
||||
scope: punctuation.separator.set.python
|
||||
set: inside-set
|
1
assets/syntaxes/02_Extra/Groff
vendored
Submodule
1
assets/syntaxes/02_Extra/Groff
vendored
Submodule
Submodule assets/syntaxes/02_Extra/Groff added at 651091c7e2
2
assets/syntaxes/02_Extra/Julia
vendored
2
assets/syntaxes/02_Extra/Julia
vendored
Submodule assets/syntaxes/02_Extra/Julia updated: 48639e1dbf...1e55f3211b
1
assets/syntaxes/02_Extra/LLVM
vendored
Submodule
1
assets/syntaxes/02_Extra/LLVM
vendored
Submodule
Submodule assets/syntaxes/02_Extra/LLVM added at 274126faa9
1
assets/syntaxes/02_Extra/Racket
vendored
Submodule
1
assets/syntaxes/02_Extra/Racket
vendored
Submodule
Submodule assets/syntaxes/02_Extra/Racket added at 7df4479c07
52
assets/syntaxes/02_Extra/Racket.sublime-syntax
vendored
Normal file
52
assets/syntaxes/02_Extra/Racket.sublime-syntax
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
%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
|
1
assets/syntaxes/02_Extra/Slim
vendored
Submodule
1
assets/syntaxes/02_Extra/Slim
vendored
Submodule
Submodule assets/syntaxes/02_Extra/Slim added at 3b1441f89f
306
assets/syntaxes/02_Extra/Slim.sublime-syntax
vendored
Normal file
306
assets/syntaxes/02_Extra/Slim.sublime-syntax
vendored
Normal file
@@ -0,0 +1,306 @@
|
||||
%YAML 1.2
|
||||
---
|
||||
# http://www.sublimetext.com/docs/syntax.html
|
||||
name: Ruby Slim
|
||||
file_extensions:
|
||||
- slim
|
||||
- skim
|
||||
scope: text.slim
|
||||
contexts:
|
||||
main:
|
||||
- match: ^(\s*)(ruby):$
|
||||
captures:
|
||||
2: constant.language.name.ruby.filter.slim
|
||||
push:
|
||||
- meta_scope: text.ruby.filter.slim
|
||||
- match: ^(?!(\1\s)|\s*$)
|
||||
pop: true
|
||||
- include: scope:source.ruby
|
||||
- match: ^(\s*)(javascript):$
|
||||
captures:
|
||||
2: constant.language.name.javascript.filter.slim
|
||||
push:
|
||||
- meta_scope: source.js.filter.slim
|
||||
- match: ^(?!(\1\s)|\s*$)
|
||||
pop: true
|
||||
- include: scope:source.js
|
||||
- match: ^(---)\s*\n
|
||||
captures:
|
||||
1: storage.frontmatter.slim
|
||||
push:
|
||||
- meta_scope: source.yaml.meta.slim
|
||||
- match: ^(---)\s*\n
|
||||
captures:
|
||||
1: storage.frontmatter.slim
|
||||
pop: true
|
||||
- include: scope:source.yaml
|
||||
- match: ^(\s*)(coffee):$
|
||||
captures:
|
||||
2: constant.language.name.coffeescript.filter.slim
|
||||
push:
|
||||
- meta_scope: text.coffeescript.filter.slim
|
||||
- match: ^(?!(\1\s)|\s*$)
|
||||
pop: true
|
||||
- include: scope:source.coffee
|
||||
- match: ^(\s*)(markdown):$
|
||||
captures:
|
||||
2: constant.language.name.markdown.filter.slim
|
||||
push:
|
||||
- meta_scope: text.markdown.filter.slim
|
||||
- match: ^(?!(\1\s)|\s*$)
|
||||
pop: true
|
||||
- include: scope:text.html.markdown
|
||||
- match: ^(\s*)(css):$
|
||||
captures:
|
||||
2: constant.language.name.css.filter.slim
|
||||
push:
|
||||
- meta_scope: text.css.filter.slim
|
||||
- match: ^(?!(\1\s)|\s*$)
|
||||
pop: true
|
||||
- include: scope:source.css
|
||||
- match: ^(\s*)(sass):$
|
||||
captures:
|
||||
2: constant.language.name.sass.filter.slim
|
||||
push:
|
||||
- meta_scope: text.sass.filter.slim
|
||||
- match: ^(?!(\1\s)|\s*$)
|
||||
pop: true
|
||||
- include: scope:source.sass
|
||||
- match: ^(\s*)(scss):$
|
||||
captures:
|
||||
2: constant.language.name.scss.filter.slim
|
||||
push:
|
||||
- meta_scope: text.scss.filter.slim
|
||||
- match: ^(?!(\1\s)|\s*$)
|
||||
pop: true
|
||||
- include: scope:source.scss
|
||||
- match: ^(\s*)(less):$
|
||||
captures:
|
||||
2: constant.language.name.less.filter.slim
|
||||
push:
|
||||
- meta_scope: text.less.filter.slim
|
||||
- match: ^(?!(\1\s)|\s*$)
|
||||
pop: true
|
||||
- include: scope:source.less
|
||||
- match: ^(\s*)(erb):$
|
||||
captures:
|
||||
2: constant.language.name.erb.filter.slim
|
||||
push:
|
||||
- meta_scope: text.erb.filter.slim
|
||||
- match: ^(?!(\1\s)|\s*$)
|
||||
pop: true
|
||||
- include: scope:source.erb
|
||||
- match: ^(! )($|\s.*)
|
||||
scope: meta.prolog.slim
|
||||
captures:
|
||||
1: punctuation.definition.prolog.slim
|
||||
- match: ^(\s*)(/)\s*.*$
|
||||
captures:
|
||||
2: comment.line.slash.slim
|
||||
push:
|
||||
- meta_scope: comment.block.slim
|
||||
- match: ^(?!(\1\s)|\s*$)
|
||||
pop: true
|
||||
- match: ^\s*(?=-)
|
||||
push:
|
||||
- match: $
|
||||
pop: true
|
||||
- include: rubyline
|
||||
- match: (?==+|~)
|
||||
push:
|
||||
- match: $
|
||||
pop: true
|
||||
- include: rubyline
|
||||
- include: tag-attribute
|
||||
- include: embedded-ruby
|
||||
- match: ^(\s*)(\||')\s*
|
||||
comment: Verbatim text (can include HTML tags and copied lines)
|
||||
push:
|
||||
- match: ^(?!(\1\s)|\s*$)
|
||||
pop: true
|
||||
- include: scope:text.html.basic
|
||||
- include: embedded-ruby
|
||||
- match: '^\s*(\.|#|[-a-zA-Z0-9]+)([\w-]+)?'
|
||||
comment: '1 - dot OR hash OR any combination of word, number; 2 - OPTIONAL any combination of word, number, dash or underscore (following a . or'
|
||||
captures:
|
||||
1: entity.name.tag.slim
|
||||
2: entity.other.attribute-name.event.slim
|
||||
push:
|
||||
- meta_scope: meta.tag
|
||||
- match: '$|(?!\.|#|:|-|~|/|\}|\]|\*|\s?[\*\{])'
|
||||
captures:
|
||||
1: entity.name.tag.slim
|
||||
2: entity.other.attribute-name.event.slim
|
||||
pop: true
|
||||
- match: '(:[\w\d]+)+'
|
||||
comment: XML
|
||||
push:
|
||||
- meta_scope: entity.name.tag.slim
|
||||
- match: $|\s
|
||||
pop: true
|
||||
- match: '(:\s)(\.|#|[a-zA-Z0-9]+)([\w-]+)?'
|
||||
comment: Inline HTML / 1 - colon; 2 - dot OR hash OR any combination of word, number; 3 - OPTIONAL any combination of word, number, dash or underscore (following a . or
|
||||
captures:
|
||||
1: punctuation.definition.tag.end.slim
|
||||
2: entity.name.tag.slim
|
||||
3: entity.other.attribute-name.event.slim
|
||||
push:
|
||||
- match: '$|(?!\.|#|=|-|~|/|\}|\]|\*|\s?[\*\{])'
|
||||
captures:
|
||||
1: punctuation.definition.tag.end.slim
|
||||
2: entity.name.tag.slim
|
||||
3: entity.other.attribute-name.event.slim
|
||||
pop: true
|
||||
- include: root-class-id-tag
|
||||
- include: tag-attribute
|
||||
- match: '(\*\{)(?=.*\}|.*\|\s*$)'
|
||||
comment: Splat attributes
|
||||
captures:
|
||||
1: punctuation.section.embedded.ruby
|
||||
push:
|
||||
- meta_scope: source.ruby.embedded.slim
|
||||
- match: '(\})|$|^(?!.*\|\s*$)'
|
||||
captures:
|
||||
1: punctuation.section.embedded.ruby
|
||||
pop: true
|
||||
- include: embedded-ruby
|
||||
- include: root-class-id-tag
|
||||
- include: rubyline
|
||||
- match: /
|
||||
scope: punctuation.terminator.tag.slim
|
||||
- match: ^\s*(\\.)
|
||||
captures:
|
||||
1: meta.escape.slim
|
||||
- match: ^\s*(?=\||')
|
||||
push:
|
||||
- match: $
|
||||
pop: true
|
||||
- include: embedded-ruby
|
||||
- include: scope:text.html.basic
|
||||
- match: '(?=<[\w\d\:]+)'
|
||||
comment: Inline and root-level HTML tags
|
||||
push:
|
||||
- match: $|\/\>
|
||||
pop: true
|
||||
- include: scope:text.html.basic
|
||||
continuation:
|
||||
- match: '([\\,])\s*\n'
|
||||
captures:
|
||||
1: punctuation.separator.continuation.slim
|
||||
delimited-ruby-a:
|
||||
- match: '=\('
|
||||
push:
|
||||
- meta_scope: source.ruby.embedded.slim
|
||||
- match: \)(?=( \w|$))
|
||||
pop: true
|
||||
- include: scope:source.ruby.rails
|
||||
delimited-ruby-b:
|
||||
- match: '=\['
|
||||
push:
|
||||
- meta_scope: source.ruby.embedded.slim
|
||||
- match: '\](?=( \w|$))'
|
||||
pop: true
|
||||
- include: scope:source.ruby.rails
|
||||
delimited-ruby-c:
|
||||
- match: '=\{'
|
||||
push:
|
||||
- meta_scope: source.ruby.embedded.slim
|
||||
- match: '\}(?=( \w|$))'
|
||||
pop: true
|
||||
- include: scope:source.ruby.rails
|
||||
embedded-ruby:
|
||||
- match: '(?<!\\)#\{{1,2}'
|
||||
captures:
|
||||
0: punctuation.section.embedded.ruby
|
||||
push:
|
||||
- meta_scope: source.ruby.embedded.html
|
||||
- match: '\}{1,2}'
|
||||
captures:
|
||||
0: punctuation.section.embedded.ruby
|
||||
pop: true
|
||||
- include: scope:source.ruby.rails
|
||||
entities:
|
||||
- match: '(&)([a-zA-Z0-9]+|#[0-9]+|#x[0-9a-fA-F]+)(;)'
|
||||
scope: constant.character.entity.html
|
||||
captures:
|
||||
1: punctuation.definition.entity.html
|
||||
3: punctuation.definition.entity.html
|
||||
- match: '&'
|
||||
scope: invalid.illegal.bad-ampersand.html
|
||||
interpolated-ruby:
|
||||
- match: '=(?=\b)'
|
||||
push:
|
||||
- meta_scope: source.ruby.embedded.html
|
||||
- match: \s|\w$
|
||||
pop: true
|
||||
root-class-id-tag:
|
||||
- match: '(\.|#)([\w\d\-]+)'
|
||||
captures:
|
||||
1: punctuation.separator.key-value.html
|
||||
2: entity.other.attribute-name.html
|
||||
rubyline:
|
||||
- match: (==|=)(<>|><|<'|'<|<|>)?|-
|
||||
comment: Hack to thwart Sublime's Ruby highlighter. It thinks do without a variable continues the next line (this can be muted with a / at the end of the line). For things like yields, do is unnecessary without an argument, so this hack will suffice
|
||||
push:
|
||||
- meta_scope: meta.line.ruby.slim
|
||||
- meta_content_scope: source.ruby.embedded.slim
|
||||
- match: (do\s*\n$)|(?<!\\|,|,\n|\\\n)$
|
||||
captures:
|
||||
1: keyword.control.start-block.ruby
|
||||
pop: true
|
||||
- match: '#.*$'
|
||||
comment: Hack to let ruby comments work in this context properly
|
||||
scope: comment.line.number-sign.ruby
|
||||
- include: continuation
|
||||
- include: scope:source.ruby.rails
|
||||
string-double-quoted:
|
||||
- match: (")(?=.*")
|
||||
captures:
|
||||
0: punctuation.definition.string.begin.html
|
||||
push:
|
||||
- meta_scope: string.quoted.double.html
|
||||
- meta_content_scope: meta.toc-list.id.html
|
||||
- match: '"'
|
||||
captures:
|
||||
0: punctuation.definition.string.end.html
|
||||
pop: true
|
||||
- include: embedded-ruby
|
||||
- include: entities
|
||||
string-single-quoted:
|
||||
- match: (')(?=.*')
|
||||
captures:
|
||||
0: punctuation.definition.string.begin.html
|
||||
push:
|
||||
- meta_scope: string.quoted.single.html
|
||||
- meta_content_scope: meta.toc-list.id.html
|
||||
- match: "'"
|
||||
captures:
|
||||
0: punctuation.definition.string.end.html
|
||||
pop: true
|
||||
- include: embedded-ruby
|
||||
- include: entities
|
||||
tag-attribute:
|
||||
- match: '([\w.#_-]+)(=)(?!\s)(true|false|nil)?(\s*\(|\{)?'
|
||||
captures:
|
||||
1: entity.other.attribute-name.event.slim
|
||||
2: punctuation.separator.key-value.html
|
||||
3: constant.language.slim
|
||||
push:
|
||||
- meta_scope: meta.attribute-with-value.slim
|
||||
- match: '\}|\)|$'
|
||||
captures:
|
||||
1: entity.other.attribute-name.event.slim
|
||||
2: punctuation.separator.key-value.html
|
||||
3: constant.language.slim
|
||||
pop: true
|
||||
- include: tag-stuff
|
||||
- include: string-double-quoted
|
||||
- include: string-single-quoted
|
||||
tag-stuff:
|
||||
- include: tag-attribute
|
||||
- include: interpolated-ruby
|
||||
- include: delimited-ruby-a
|
||||
- include: delimited-ruby-b
|
||||
- include: delimited-ruby-c
|
||||
- include: rubyline
|
||||
- include: embedded-ruby
|
2
assets/syntaxes/02_Extra/VimL
vendored
2
assets/syntaxes/02_Extra/VimL
vendored
Submodule assets/syntaxes/02_Extra/VimL updated: 7ebcaa1d98...c91fe3ab02
1
assets/syntaxes/02_Extra/http-request-response
vendored
Submodule
1
assets/syntaxes/02_Extra/http-request-response
vendored
Submodule
Submodule assets/syntaxes/02_Extra/http-request-response added at 93b93261bc
@@ -16,11 +16,11 @@ contexts:
|
||||
- match: ^
|
||||
push: text
|
||||
loghost:
|
||||
- match: '[\w-]+'
|
||||
- match: '[\w:.-]+'
|
||||
scope: entity.other.attribute-name.loghost.syslog
|
||||
set: process
|
||||
process:
|
||||
- match: ([\w-]+)(?:(\[)(\d+)(\]))?(:)
|
||||
- match: ([\w.-]+)(?:(\[)(\d+)(\]))?([ :])
|
||||
captures:
|
||||
1: support.function.process.syslog
|
||||
2: punctuation.separator.pid.begin.syslog
|
||||
|
5
build.rs
5
build.rs
@@ -64,6 +64,11 @@ 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",
|
||||
|
@@ -1,7 +1,6 @@
|
||||
<p align="center">
|
||||
<img src="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?svg=true"></a>
|
||||
<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>
|
||||
シンタックスハイライトとGitとの連携機能付きの <i>cat(1)</i> クローン。
|
||||
@@ -12,8 +11,12 @@
|
||||
<a href="#使い方">使い方</a> •
|
||||
<a href="#インストール">インストール</a> •
|
||||
<a href="#カスタマイズ">カスタマイズ</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>]
|
||||
<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>]
|
||||
</p>
|
||||
|
||||
### シンタックスハイライト
|
||||
@@ -522,7 +525,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 .ino files
|
||||
# Use C++ syntax for Arduino .ino files
|
||||
--map-syntax "*.ino:C++"
|
||||
|
||||
# Use ".gitignore"-style highlighting for ".ignore" files
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<p align="center">
|
||||
<img src="../doc/logo-header.svg" alt="bat - a cat clone with wings"><br>
|
||||
<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="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,8 +11,12 @@
|
||||
<a href="#사용법">사용법</a> •
|
||||
<a href="#설치">설치</a> •
|
||||
<a href="#사용자화">사용자화</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>]
|
||||
<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>]
|
||||
</p>
|
||||
|
||||
### 문법 강조
|
||||
@@ -279,6 +283,14 @@ 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에서
|
||||
|
||||
공식 소스를 통해
|
||||
@@ -404,7 +416,7 @@ scoop install bat
|
||||
|
||||
### 소스에서
|
||||
|
||||
`bat`의 소스를 빌드하기 위해서는, Rust 1.45 이상이 필요합니다.
|
||||
`bat`의 소스를 빌드하기 위해서는, Rust 1.46 이상이 필요합니다.
|
||||
`cargo`를 이용해 전부 빌드할 수 있습니다:
|
||||
|
||||
```bash
|
||||
@@ -663,7 +675,7 @@ bat --generate-config-file
|
||||
# 터미널에서 이탤릭체 쓰기 (일부 터미널에서 미지원)
|
||||
--italic-text=always
|
||||
|
||||
# .ino 파일에 C++ 문법 쓰기
|
||||
# Arduino .ino 파일에 C++ 문법 쓰기
|
||||
--map-syntax "*.ino:C++"
|
||||
```
|
||||
|
||||
@@ -797,6 +809,10 @@ cargo install --locked --force
|
||||
- [keith-hall](https://github.com/keith-hall)
|
||||
- [Enselic](https://github.com/Enselic)
|
||||
|
||||
## 보안 취약점
|
||||
|
||||
만약 `bat`의 취약점을 발견하였다면, [David Peter](https://david-peter.de/)에게 메일로 연락주시기 바랍니다.
|
||||
|
||||
## 프로젝트 목표와 대안들
|
||||
|
||||
`bat`은 다음과 같은 목표를 달성하려고 합니다:
|
||||
|
@@ -1,7 +1,6 @@
|
||||
<p align="center">
|
||||
<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="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>
|
||||
Клон утилиты <i>cat(1)</i> с поддержкой выделения синтаксиса и Git
|
||||
@@ -12,8 +11,12 @@
|
||||
<a href="#как-использовать">Использование</a> •
|
||||
<a href="#установка">Установка</a> •
|
||||
<a href="#кастомизация">Кастомизация</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>]
|
||||
<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>]
|
||||
[Русский]
|
||||
</p>
|
||||
|
||||
### Выделение синтаксиса
|
||||
@@ -341,7 +344,7 @@ ansible-galaxy install aeimer.install_bat
|
||||
|
||||
### Из исходников
|
||||
|
||||
Если вы желаете установить `bat` из исходников, вам понадобится Rust 1.45 или выше. После этого используйте `cargo`, чтобы все скомпилировать:
|
||||
Если вы желаете установить `bat` из исходников, вам понадобится Rust 1.46 или выше. После этого используйте `cargo`, чтобы все скомпилировать:
|
||||
|
||||
```bash
|
||||
cargo install --locked bat
|
||||
@@ -434,11 +437,11 @@ export BAT_PAGER="less -RF"
|
||||
`-R`/`--RAW-CONTROL-CHARS`,
|
||||
`-F`/`--quit-if-one-screen` и `-X`/`--no-init`. Последний флаг(`-X`) используется только для `less`, чья версия раньше 530.
|
||||
|
||||
Флаг `-R` нужен чтобы корректно воспроизвести ANSI цвета. Второй флаг (`-F`) говорит
|
||||
Флаг `-R` нужен чтобы корректно воспроизвести ANSI цвета. Второй флаг (`-F`) говорит
|
||||
`less` чтобы тот сразу же завершился, если размер вывода меньше чем вертикальный размер терминала.
|
||||
Это удобно для небольших файлов, так как вам не надо каждый раз нажимать `q`, чтобы выйти из пейджера. Третий флаг (`-X`) нужен для того, чтобы исправить баг с `--quit-if-one-screen` в старых версиях `less`. К сожалению, это блокирует возможность использования колеса мышки.
|
||||
|
||||
Если вы хотите все же его включить, вы можете добавить флаг `-R`.
|
||||
Если вы хотите все же его включить, вы можете добавить флаг `-R`.
|
||||
Для `less` новее чем 530 оно должно работать из коробки.
|
||||
|
||||
### Темная тема
|
||||
@@ -481,7 +484,7 @@ bat --generate-config-file
|
||||
# Использовать курсив (поддерживается не всеми терминалами)
|
||||
--italic-text=always
|
||||
|
||||
# Использовать синтаксис C++ для всех .ino файлов
|
||||
# Использовать синтаксис C++ для всех Arduino .ino файлов
|
||||
--map-syntax "*.ino:C++"
|
||||
|
||||
# Использовать синтаксик Git Ignore для всех файлов .ignore
|
||||
@@ -499,7 +502,7 @@ Windows поддерживает только очень простой пейд
|
||||
### Цвета
|
||||
|
||||
Windows 10 поддерживает цвета и в `conhost.exe` (Command Prompt), и в PowerShell начиная с версии Windows
|
||||
[v1511](https://ru.wikipedia.org/wiki/Windows_10#Обновления и поддержка), так же как и в bash. На ранних версиях Windows вы можете использовать
|
||||
[v1511](https://ru.wikipedia.org/wiki/Windows_10#Обновления и поддержка), так же как и в bash. На ранних версиях Windows вы можете использовать
|
||||
[Cmder](http://cmder.net/), в котором есть [ConEmu](https://conemu.github.io/).
|
||||
|
||||
**Внимание:** Версия `less` в Git и MSYS2 воспроизводит цвета некорректно. Если у вас нет других пейджеров, мы можете отключить использование пейджеров с помощью флага `--paging=never`
|
||||
|
@@ -9,7 +9,9 @@ in the `.sublime-syntax` format.
|
||||
**Important:** Before proceeding, verify that the syntax you wish to add meets the [criteria for inclusion](#Criteria-for-inclusion-of-new-syntaxes).
|
||||
|
||||
1. Find a Sublime Text syntax for the given language, preferably in a separate Git repository
|
||||
which can be included as a submodule (under `assets/syntaxes`).
|
||||
which can be included as a submodule (under `assets/syntaxes`) using
|
||||
`git submodule add <https github link> ./assets/syntaxes/02_Extra/<repo name>`, replacing
|
||||
the contents of the angle brackets as appropriate.
|
||||
|
||||
2. If the Sublime Text syntax is only available as a `.tmLanguage` file, open the file in
|
||||
Sublime Text and convert it to a `.sublime-syntax` file via *Tools* -> *Developer* ->
|
||||
@@ -26,7 +28,8 @@ in the `.sublime-syntax` format.
|
||||
6. Add a syntax test for the new language. See [below](#Syntax-tests) for details.
|
||||
|
||||
7. If you send a pull request with your changes, please do *not* include the changed `syntaxes.bin`
|
||||
file. A new binary cache file will be created once before every new release of `bat`.
|
||||
file. A new binary cache file will be created once before every new release of `bat`. This
|
||||
avoids bloating the repository size unnecessarily.
|
||||
|
||||
### Syntax tests
|
||||
|
||||
@@ -68,7 +71,7 @@ themes (`bat cache --clear`).
|
||||
|
||||
## Criteria for inclusion of new syntaxes
|
||||
|
||||
* More than 10,000 downloads on packagecontrol.io/
|
||||
* More than 10,000 downloads at [Package Control](https://packagecontrol.io)
|
||||
|
||||
### Manual modifications
|
||||
|
||||
|
58
doc/release-checklist.md
Normal file
58
doc/release-checklist.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# Release checklist
|
||||
|
||||
## Dependencies
|
||||
|
||||
See this page for a good overview: https://deps.rs/repo/github/sharkdp/bat
|
||||
|
||||
- [ ] Optional: update dependencies with `cargo update`. This is also done by
|
||||
dependabot, so it is not strictly necessary.
|
||||
- [ ] Check for outdated dependencies (`cargo outdated`) and decide for each of
|
||||
them whether we want to (manually) upgrade. This will require changes to
|
||||
`Cargo.toml`.
|
||||
|
||||
## Version bump
|
||||
|
||||
- [ ] Update version in `Cargo.toml`. Run `cargo build` to update `Cargo.lock`.
|
||||
Make sure to `git add` the `Cargo.lock` changes as well.
|
||||
- [ ] Find the current min. supported Rust version by running
|
||||
`grep '^\s*MIN_SUPPORTED_RUST_VERSION' .github/workflows/CICD.yml`.
|
||||
- [ ] Update the version and the min. supported Rust version in `README.md` and
|
||||
`doc/README-*.md`.
|
||||
- [ ] Update `CHANGELOG.md`. Introduce a section for the new release and
|
||||
prepare a new (empty) "unreleased" section at the top.
|
||||
|
||||
## Update syntaxes and themes (build assets)
|
||||
|
||||
- [ ] Install the latest master version (`cargo install -f --path .`) and make
|
||||
sure that it is available on the `PATH` (`bat --version` should show the
|
||||
new version).
|
||||
- [ ] Run `assets/create.sh` and check in the binary asset files.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [ ] Review the `-h` and `--help` texts
|
||||
- [ ] Review the `man` page
|
||||
|
||||
## Pre-release checks
|
||||
|
||||
- [ ] Push all changes and wait for CI to succeed (before continuing with the
|
||||
next section).
|
||||
- [ ] Optional: manually test the new features and command-line options. To do
|
||||
this, install the latest `bat` version again (to include the new synaxes
|
||||
and themes).
|
||||
- [ ] Run `cargo publish --dry-run --allow-dirty` to make sure that it will
|
||||
succeed later (after creating the GitHub release).
|
||||
|
||||
## Release
|
||||
|
||||
- [ ] Create a tag and push it: `git tag vX.Y.Z; git push origin tag vX.Y.Z`.
|
||||
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
|
||||
release notes, copy the corresponding section from `CHANGELOG.md` and
|
||||
possibly add additional remarks for package maintainers.
|
||||
Publish the release.
|
||||
- [ ] Check if the binary deployment works (archives and Debian packages should
|
||||
appear when the CI run for the Git tag has finished).
|
||||
- [ ] Publish to crates.io by running `cargo publish` in a *clean* repository.
|
||||
The safest way to do this is to clone a fresh copy.
|
541
src/assets.rs
541
src/assets.rs
@@ -1,217 +1,201 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::{self, File};
|
||||
use std::io::BufReader;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
use syntect::dumps::{dump_to_file, from_binary, from_reader};
|
||||
use lazycell::LazyCell;
|
||||
|
||||
use syntect::highlighting::{Theme, ThemeSet};
|
||||
use syntect::parsing::{SyntaxReference, SyntaxSet, SyntaxSetBuilder};
|
||||
use syntect::parsing::{SyntaxReference, SyntaxSet};
|
||||
|
||||
use path_abs::PathAbs;
|
||||
|
||||
use crate::assets_metadata::AssetsMetadata;
|
||||
use crate::bat_warning;
|
||||
use crate::error::*;
|
||||
use crate::input::{InputReader, OpenedInput, OpenedInputKind};
|
||||
use crate::input::{InputReader, OpenedInput};
|
||||
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 {
|
||||
pub(crate) syntax_set: SyntaxSet,
|
||||
pub(crate) theme_set: ThemeSet,
|
||||
syntax_set_cell: LazyCell<SyntaxSet>,
|
||||
serialized_syntax_set: SerializedSyntaxSet,
|
||||
|
||||
minimal_assets: MinimalAssets,
|
||||
|
||||
theme_set: ThemeSet,
|
||||
fallback_theme: Option<&'static str>,
|
||||
}
|
||||
|
||||
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",
|
||||
];
|
||||
#[derive(Debug)]
|
||||
pub struct SyntaxReferenceInSet<'a> {
|
||||
pub syntax: &'a SyntaxReference,
|
||||
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;
|
||||
|
||||
impl HighlightingAssets {
|
||||
fn new(
|
||||
serialized_syntax_set: SerializedSyntaxSet,
|
||||
minimal_syntaxes: MinimalSyntaxes,
|
||||
theme_set: ThemeSet,
|
||||
) -> Self {
|
||||
HighlightingAssets {
|
||||
syntax_set_cell: LazyCell::new(),
|
||||
serialized_syntax_set,
|
||||
minimal_assets: MinimalAssets::new(minimal_syntaxes),
|
||||
theme_set,
|
||||
fallback_theme: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_theme() -> &'static str {
|
||||
"Monokai Extended"
|
||||
}
|
||||
|
||||
pub fn from_files(source_dir: &Path, include_integrated_assets: bool) -> Result<Self> {
|
||||
let mut theme_set = if include_integrated_assets {
|
||||
Self::get_integrated_themeset()
|
||||
} else {
|
||||
ThemeSet {
|
||||
themes: BTreeMap::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 = SyntaxSetBuilder::new();
|
||||
builder.add_plain_text_syntax();
|
||||
builder
|
||||
} else {
|
||||
Self::get_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()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(HighlightingAssets {
|
||||
syntax_set: syntax_set_builder.build(),
|
||||
theme_set,
|
||||
fallback_theme: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_cache(cache_path: &Path) -> Result<Self> {
|
||||
let syntax_set_path = cache_path.join("syntaxes.bin");
|
||||
let theme_set_path = cache_path.join("themes.bin");
|
||||
|
||||
let syntax_set_file = File::open(&syntax_set_path).chain_err(|| {
|
||||
format!(
|
||||
"Could not load cached syntax set '{}'",
|
||||
syntax_set_path.to_string_lossy()
|
||||
)
|
||||
})?;
|
||||
let syntax_set: SyntaxSet = from_reader(BufReader::new(syntax_set_file))
|
||||
.chain_err(|| "Could not parse cached syntax set")?;
|
||||
|
||||
let theme_set_file = File::open(&theme_set_path).chain_err(|| {
|
||||
format!(
|
||||
"Could not load cached theme set '{}'",
|
||||
theme_set_path.to_string_lossy()
|
||||
)
|
||||
})?;
|
||||
let theme_set: ThemeSet = from_reader(BufReader::new(theme_set_file))
|
||||
.chain_err(|| "Could not parse cached theme set")?;
|
||||
|
||||
Ok(HighlightingAssets {
|
||||
syntax_set,
|
||||
theme_set,
|
||||
fallback_theme: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_integrated_syntaxset() -> SyntaxSet {
|
||||
from_binary(include_bytes!("../assets/syntaxes.bin"))
|
||||
}
|
||||
|
||||
fn get_integrated_themeset() -> ThemeSet {
|
||||
from_binary(include_bytes!("../assets/themes.bin"))
|
||||
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)?,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn from_binary() -> Self {
|
||||
let syntax_set = Self::get_integrated_syntaxset();
|
||||
let theme_set = Self::get_integrated_themeset();
|
||||
|
||||
HighlightingAssets {
|
||||
syntax_set,
|
||||
theme_set,
|
||||
fallback_theme: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save_to_cache(&self, target_dir: &Path, current_version: &str) -> Result<()> {
|
||||
let _ = fs::create_dir_all(target_dir);
|
||||
let theme_set_path = target_dir.join("themes.bin");
|
||||
let syntax_set_path = target_dir.join("syntaxes.bin");
|
||||
|
||||
print!(
|
||||
"Writing theme set to {} ... ",
|
||||
theme_set_path.to_string_lossy()
|
||||
);
|
||||
dump_to_file(&self.theme_set, &theme_set_path).chain_err(|| {
|
||||
format!(
|
||||
"Could not save theme set to {}",
|
||||
theme_set_path.to_string_lossy()
|
||||
)
|
||||
})?;
|
||||
println!("okay");
|
||||
|
||||
print!(
|
||||
"Writing syntax set to {} ... ",
|
||||
syntax_set_path.to_string_lossy()
|
||||
);
|
||||
dump_to_file(&self.syntax_set, &syntax_set_path).chain_err(|| {
|
||||
format!(
|
||||
"Could not save syntax set to {}",
|
||||
syntax_set_path.to_string_lossy()
|
||||
)
|
||||
})?;
|
||||
println!("okay");
|
||||
|
||||
print!(
|
||||
"Writing metadata to folder {} ... ",
|
||||
target_dir.to_string_lossy()
|
||||
);
|
||||
AssetsMetadata::new(current_version).save_to_folder(target_dir)?;
|
||||
println!("okay");
|
||||
|
||||
Ok(())
|
||||
HighlightingAssets::new(
|
||||
SerializedSyntaxSet::FromBinary(get_serialized_integrated_syntaxset()),
|
||||
get_integrated_minimal_syntaxes(),
|
||||
get_integrated_themeset(),
|
||||
)
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
/// Use [Self::get_syntaxes] instead
|
||||
#[deprecated]
|
||||
pub fn syntaxes(&self) -> &[SyntaxReference] {
|
||||
self.syntax_set.syntaxes()
|
||||
self.get_syntax_set()
|
||||
.expect(".syntaxes() is deprecated, use .get_syntaxes() instead")
|
||||
.syntaxes()
|
||||
}
|
||||
|
||||
pub fn get_syntaxes(&self) -> Result<&[SyntaxReference]> {
|
||||
Ok(self.get_syntax_set()?.syntaxes())
|
||||
}
|
||||
|
||||
fn get_theme_set(&self) -> &ThemeSet {
|
||||
&self.theme_set
|
||||
}
|
||||
|
||||
pub fn themes(&self) -> impl Iterator<Item = &str> {
|
||||
self.theme_set.themes.keys().map(|s| s.as_ref())
|
||||
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
|
||||
#[deprecated]
|
||||
pub fn syntax_for_file_name(
|
||||
&self,
|
||||
file_name: impl AsRef<Path>,
|
||||
mapping: &SyntaxMapping,
|
||||
) -> Option<&SyntaxReference> {
|
||||
let file_name = file_name.as_ref();
|
||||
match mapping.get_syntax_for(file_name) {
|
||||
Some(MappingTarget::MapToUnknown) => None,
|
||||
Some(MappingTarget::MapTo(syntax_name)) => {
|
||||
self.syntax_set.find_syntax_by_name(syntax_name)
|
||||
self.get_syntax_for_path(file_name, mapping)
|
||||
.ok()
|
||||
.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(
|
||||
&self,
|
||||
path: 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()))
|
||||
}
|
||||
|
||||
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 {
|
||||
match self.theme_set.themes.get(theme) {
|
||||
match self.get_theme_set().themes.get(theme) {
|
||||
Some(theme) => theme,
|
||||
None => {
|
||||
if theme == "ansi-light" || theme == "ansi-dark" {
|
||||
@@ -221,7 +205,8 @@ impl HighlightingAssets {
|
||||
if !theme.is_empty() {
|
||||
bat_warning!("Unknown theme '{}', using default.", theme)
|
||||
}
|
||||
&self.theme_set.themes[self.fallback_theme.unwrap_or_else(|| Self::default_theme())]
|
||||
&self.get_theme_set().themes
|
||||
[self.fallback_theme.unwrap_or_else(|| Self::default_theme())]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -231,92 +216,125 @@ impl HighlightingAssets {
|
||||
language: Option<&str>,
|
||||
input: &mut OpenedInput,
|
||||
mapping: &SyntaxMapping,
|
||||
) -> Result<&SyntaxReference> {
|
||||
) -> Result<SyntaxReferenceInSet> {
|
||||
if let Some(language) = language {
|
||||
self.syntax_set
|
||||
let syntax_set = self.get_syntax_set_by_name(language)?;
|
||||
return syntax_set
|
||||
.find_syntax_by_token(language)
|
||||
.ok_or_else(|| ErrorKind::UnknownSyntax(language.to_owned()).into())
|
||||
.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,
|
||||
)
|
||||
} else {
|
||||
let line_syntax = self.get_first_line_syntax(&mut input.reader);
|
||||
Err(Error::UndetectedSyntax("[unknown]".into()))
|
||||
};
|
||||
|
||||
// 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)) => self
|
||||
.syntax_set
|
||||
.find_syntax_by_name(syntax_name)
|
||||
.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())
|
||||
}
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_extension_syntax(&self, file_name: &OsStr) -> Option<&SyntaxReference> {
|
||||
self.syntax_set
|
||||
.find_syntax_by_extension(file_name.to_str().unwrap_or_default())
|
||||
.or_else(|| {
|
||||
let file_path = Path::new(file_name);
|
||||
self.syntax_set
|
||||
.find_syntax_by_extension(
|
||||
file_path
|
||||
.extension()
|
||||
.and_then(|x| x.to_str())
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
.or_else(|| {
|
||||
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) {
|
||||
return self
|
||||
.get_extension_syntax(OsStr::new(stripped_filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
})
|
||||
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 get_first_line_syntax(&self, reader: &mut InputReader) -> Option<&SyntaxReference> {
|
||||
String::from_utf8(reader.first_line.clone())
|
||||
.ok()
|
||||
.and_then(|l| self.syntax_set.find_syntax_by_first_line(&l))
|
||||
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))?;
|
||||
if syntax.is_none() {
|
||||
syntax = self.find_syntax_by_extension(Path::new(file_name).extension())?;
|
||||
}
|
||||
if syntax.is_none() {
|
||||
syntax = try_with_stripped_suffix(file_name, |stripped_file_name| {
|
||||
self.get_extension_syntax(stripped_file_name) // Note: recursion
|
||||
})?;
|
||||
}
|
||||
Ok(syntax)
|
||||
}
|
||||
|
||||
fn get_first_line_syntax(
|
||||
&self,
|
||||
reader: &mut InputReader,
|
||||
) -> Result<Option<SyntaxReferenceInSet>> {
|
||||
let syntax_set = self.get_syntax_set()?;
|
||||
Ok(String::from_utf8(reader.first_line.clone())
|
||||
.ok()
|
||||
.and_then(|l| syntax_set.find_syntax_by_first_line(&l))
|
||||
.map(|syntax| SyntaxReferenceInSet { syntax, syntax_set }))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) 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_minimal_syntaxes() -> MinimalSyntaxes {
|
||||
from_binary(
|
||||
include_bytes!("../assets/minimal_syntaxes.bin"),
|
||||
COMPRESS_MINIMAL_SYNTAXES,
|
||||
)
|
||||
}
|
||||
|
||||
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(|_| {
|
||||
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())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -326,7 +344,7 @@ mod tests {
|
||||
use std::ffi::OsStr;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::io::{BufReader, Write};
|
||||
use tempfile::TempDir;
|
||||
|
||||
use crate::input::Input;
|
||||
@@ -346,6 +364,18 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_syntax_name(
|
||||
&self,
|
||||
language: Option<&str>,
|
||||
input: &mut OpenedInput,
|
||||
mapping: &SyntaxMapping,
|
||||
) -> String {
|
||||
self.assets
|
||||
.get_syntax(language, input, mapping)
|
||||
.map(|syntax_in_set| syntax_in_set.syntax.name.clone())
|
||||
.unwrap_or_else(|_| "!no syntax!".to_owned())
|
||||
}
|
||||
|
||||
fn syntax_for_real_file_with_content_os(
|
||||
&self,
|
||||
file_name: &OsStr,
|
||||
@@ -361,11 +391,7 @@ mod tests {
|
||||
let dummy_stdin: &[u8] = &[];
|
||||
let mut opened_input = input.open(dummy_stdin, None).unwrap();
|
||||
|
||||
self.assets
|
||||
.get_syntax(None, &mut opened_input, &self.syntax_mapping)
|
||||
.unwrap_or_else(|_| self.assets.syntax_set.find_syntax_plain_text())
|
||||
.name
|
||||
.clone()
|
||||
self.get_syntax_name(None, &mut opened_input, &self.syntax_mapping)
|
||||
}
|
||||
|
||||
fn syntax_for_file_with_content_os(&self, file_name: &OsStr, first_line: &str) -> String {
|
||||
@@ -375,11 +401,7 @@ mod tests {
|
||||
let dummy_stdin: &[u8] = &[];
|
||||
let mut opened_input = input.open(dummy_stdin, None).unwrap();
|
||||
|
||||
self.assets
|
||||
.get_syntax(None, &mut opened_input, &self.syntax_mapping)
|
||||
.unwrap_or_else(|_| self.assets.syntax_set.find_syntax_plain_text())
|
||||
.name
|
||||
.clone()
|
||||
self.get_syntax_name(None, &mut opened_input, &self.syntax_mapping)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -399,11 +421,7 @@ mod tests {
|
||||
let input = Input::stdin().with_name(Some(file_name));
|
||||
let mut opened_input = input.open(content, None).unwrap();
|
||||
|
||||
self.assets
|
||||
.get_syntax(None, &mut opened_input, &self.syntax_mapping)
|
||||
.unwrap_or_else(|_| self.assets.syntax_set.find_syntax_plain_text())
|
||||
.name
|
||||
.clone()
|
||||
self.get_syntax_name(None, &mut opened_input, &self.syntax_mapping)
|
||||
}
|
||||
|
||||
fn syntax_is_same_for_inputkinds(&self, file_name: &str, content: &str) -> bool {
|
||||
@@ -557,10 +575,7 @@ mod tests {
|
||||
let mut opened_input = input.open(dummy_stdin, None).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
test.assets
|
||||
.get_syntax(None, &mut opened_input, &test.syntax_mapping)
|
||||
.unwrap_or_else(|_| test.assets.syntax_set.find_syntax_plain_text())
|
||||
.name,
|
||||
test.get_syntax_name(None, &mut opened_input, &test.syntax_mapping),
|
||||
"SSH Config"
|
||||
);
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ pub struct AssetsMetadata {
|
||||
const FILENAME: &str = "metadata.yaml";
|
||||
|
||||
impl AssetsMetadata {
|
||||
#[cfg(feature = "build-assets")]
|
||||
pub(crate) fn new(current_version: &str) -> AssetsMetadata {
|
||||
AssetsMetadata {
|
||||
bat_version: Some(current_version.to_owned()),
|
||||
@@ -23,6 +24,7 @@ impl AssetsMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "build-assets")]
|
||||
pub(crate) fn save_to_folder(&self, path: &Path) -> Result<()> {
|
||||
let file = File::create(path.join(FILENAME))?;
|
||||
serde_yaml::to_writer(file, self)?;
|
||||
@@ -50,16 +52,15 @@ 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) => 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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
500
src/assets/build_assets.rs
Normal file
500
src/assets/build_assets.rs
Normal file
@@ -0,0 +1,500 @@
|
||||
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);
|
||||
}
|
||||
}
|
41
src/assets/build_assets/graphviz_utils.rs
Normal file
41
src/assets/build_assets/graphviz_utils.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
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(())
|
||||
}
|
42
src/assets/ignored_suffixes.rs
Normal file
42
src/assets/ignored_suffixes.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
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)
|
||||
}
|
72
src/assets/minimal_assets.rs
Normal file
72
src/assets/minimal_assets.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
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()
|
||||
})
|
||||
}
|
||||
}
|
27
src/assets/serialized_syntax_set.rs
Normal file
27
src/assets/serialized_syntax_set.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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)
|
||||
.chain_err(|| "Could not parse configuration file")?;
|
||||
.map_err(|_| "Could not parse configuration file")?;
|
||||
|
||||
// Put the zero-th CLI argument (program name) first
|
||||
args.insert(0, cli_args.next().unwrap());
|
||||
@@ -158,7 +158,7 @@ impl App {
|
||||
WrappingMode::Character
|
||||
}
|
||||
}
|
||||
_ => unreachable!("other values for --paging are not allowed"),
|
||||
_ => unreachable!("other values for --wrap are not allowed"),
|
||||
}
|
||||
} else {
|
||||
// We don't have the tty width when piping to another program.
|
||||
@@ -234,6 +234,7 @@ impl App {
|
||||
.map(LineRanges::from)
|
||||
.map(HighlightedLineRanges)
|
||||
.unwrap_or_default(),
|
||||
use_custom_assets: !self.matches.is_present("no-custom-assets"),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -300,7 +301,7 @@ impl App {
|
||||
.map(|style_str| {
|
||||
style_str
|
||||
.split(',')
|
||||
.map(|x| StyleComponent::from_str(&x))
|
||||
.map(|x| StyleComponent::from_str(x))
|
||||
.collect::<Result<Vec<StyleComponent>>>()
|
||||
})
|
||||
.transpose()?;
|
||||
|
@@ -18,26 +18,15 @@ pub fn cache_dir() -> Cow<'static, str> {
|
||||
}
|
||||
|
||||
pub fn clear_assets() {
|
||||
let theme_set_path = PROJECT_DIRS.cache_dir().join("themes.bin");
|
||||
let syntax_set_path = PROJECT_DIRS.cache_dir().join("syntaxes.bin");
|
||||
let metadata_file = PROJECT_DIRS.cache_dir().join("metadata.yaml");
|
||||
|
||||
print!("Clearing theme set cache ... ");
|
||||
fs::remove_file(theme_set_path).ok();
|
||||
println!("okay");
|
||||
|
||||
print!("Clearing syntax set cache ... ");
|
||||
fs::remove_file(syntax_set_path).ok();
|
||||
println!("okay");
|
||||
|
||||
print!("Clearing metadata file ... ");
|
||||
fs::remove_file(metadata_file).ok();
|
||||
println!("okay");
|
||||
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");
|
||||
}
|
||||
|
||||
pub fn assets_from_cache_or_binary() -> Result<HighlightingAssets> {
|
||||
pub fn assets_from_cache_or_binary(use_custom_assets: bool) -> Result<HighlightingAssets> {
|
||||
let cache_dir = PROJECT_DIRS.cache_dir();
|
||||
if let Some(metadata) = AssetsMetadata::load_from_folder(&cache_dir)? {
|
||||
if let Some(metadata) = AssetsMetadata::load_from_folder(cache_dir)? {
|
||||
if !metadata.is_compatible_with(crate_version!()) {
|
||||
return Err(format!(
|
||||
"The binary caches for the user-customized syntaxes and themes \
|
||||
@@ -53,6 +42,16 @@ pub fn assets_from_cache_or_binary() -> Result<HighlightingAssets> {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(HighlightingAssets::from_cache(&cache_dir)
|
||||
.unwrap_or_else(|_| HighlightingAssets::from_binary()))
|
||||
let custom_assets = if use_custom_assets {
|
||||
HighlightingAssets::from_cache(cache_dir).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(custom_assets.unwrap_or_else(HighlightingAssets::from_binary))
|
||||
}
|
||||
|
||||
fn clear_asset(filename: &str, description: &str) {
|
||||
print!("Clearing {} ... ", description);
|
||||
fs::remove_file(PROJECT_DIRS.cache_dir().join(filename)).ok();
|
||||
println!("okay");
|
||||
}
|
||||
|
@@ -95,7 +95,8 @@ 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",
|
||||
'--highlight-line 40:' highlights lines 40 to the end of the file\n \
|
||||
'--highlight-line 30:+10' highlights lines 30 to 40",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
@@ -294,6 +295,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
|
||||
.arg(
|
||||
Arg::with_name("no-paging")
|
||||
.short("P")
|
||||
.long("no-paging")
|
||||
.alias("no-pager")
|
||||
.overrides_with("no-paging")
|
||||
.hidden(true)
|
||||
@@ -394,8 +396,8 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
|
||||
'--style=\"..\"' option to the configuration file or export the \
|
||||
BAT_STYLE environment variable (e.g.: export BAT_STYLE=\"..\").\n\n\
|
||||
Possible values:\n\n \
|
||||
* full: enables all available components.\n \
|
||||
* auto: same as 'full', unless the output is piped (default).\n \
|
||||
* full: enables all available components (default).\n \
|
||||
* auto: same as 'full', unless the output is piped.\n \
|
||||
* plain: disables all available components.\n \
|
||||
* changes: show Git modification markers.\n \
|
||||
* header: show filenames before the content.\n \
|
||||
@@ -422,7 +424,8 @@ 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",
|
||||
'--line-range 40' only prints line 40\n \
|
||||
'--line-range 30:+10' prints lines 30 to 40",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
@@ -450,6 +453,12 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
|
||||
.hidden(true)
|
||||
.help("Do not use the configuration file"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("no-custom-assets")
|
||||
.long("no-custom-assets")
|
||||
.hidden(true)
|
||||
.help("Do not load custom assets"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("config-file")
|
||||
.long("config-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 .ino files
|
||||
# Example 1: use the C++ syntax for Arduino .ino files
|
||||
# Example 2: Use ".gitignore"-style highlighting for ".ignore" files
|
||||
#--map-syntax "*.ino:C++"
|
||||
#--map-syntax ".ignore:Git Ignore"
|
||||
|
@@ -4,8 +4,9 @@ use std::path::{Path, PathBuf};
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
/// Wrapper for 'dirs' that treats MacOS more like Linux, by following the XDG specification.
|
||||
/// This means that the `XDG_CACHE_HOME` and `XDG_CONFIG_HOME` environment variables are
|
||||
/// checked first. The fallback directories are `~/.cache/bat` and `~/.config/bat`, respectively.
|
||||
/// The `XDG_CACHE_HOME` environment variable is checked first. `BAT_CONFIG_DIR`
|
||||
/// is then checked before the `XDG_CONFIG_HOME` environment variable.
|
||||
/// The fallback directories are `~/.cache/bat` and `~/.config/bat`, respectively.
|
||||
pub struct BatProjectDirs {
|
||||
cache_dir: PathBuf,
|
||||
config_dir: PathBuf,
|
||||
@@ -15,16 +16,23 @@ impl BatProjectDirs {
|
||||
fn new() -> Option<BatProjectDirs> {
|
||||
let cache_dir = BatProjectDirs::get_cache_dir()?;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
let config_dir_op = env::var_os("XDG_CONFIG_HOME")
|
||||
.map(PathBuf::from)
|
||||
.filter(|p| p.is_absolute())
|
||||
.or_else(|| dirs_next::home_dir().map(|d| d.join(".config")));
|
||||
// Checks whether or not $BAT_CONFIG_DIR exists. If it doesn't, set our config dir
|
||||
// to our system's default configuration home.
|
||||
let config_dir =
|
||||
if let Some(config_dir_op) = env::var_os("BAT_CONFIG_DIR").map(PathBuf::from) {
|
||||
config_dir_op
|
||||
} else {
|
||||
#[cfg(target_os = "macos")]
|
||||
let config_dir_op = env::var_os("XDG_CONFIG_HOME")
|
||||
.map(PathBuf::from)
|
||||
.filter(|p| p.is_absolute())
|
||||
.or_else(|| dirs_next::home_dir().map(|d| d.join(".config")));
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
let config_dir_op = dirs_next::config_dir();
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
let config_dir_op = dirs_next::config_dir();
|
||||
|
||||
let config_dir = config_dir_op.map(|d| d.join("bat"))?;
|
||||
config_dir_op.map(|d| d.join("bat"))?
|
||||
};
|
||||
|
||||
Some(BatProjectDirs {
|
||||
cache_dir,
|
||||
|
@@ -1,5 +1,4 @@
|
||||
// `error_chain!` can recurse deeply
|
||||
#![recursion_limit = "1024"]
|
||||
#![deny(unsafe_code)]
|
||||
|
||||
mod app;
|
||||
mod assets;
|
||||
@@ -23,12 +22,10 @@ use crate::{
|
||||
};
|
||||
|
||||
use assets::{assets_from_cache_or_binary, cache_dir, clear_assets, config_dir};
|
||||
use clap::crate_version;
|
||||
use directories::PROJECT_DIRS;
|
||||
use globset::GlobMatcher;
|
||||
|
||||
use bat::{
|
||||
assets::HighlightingAssets,
|
||||
config::Config,
|
||||
controller::Controller,
|
||||
error::*,
|
||||
@@ -39,21 +36,28 @@ use bat::{
|
||||
|
||||
const THEME_PREVIEW_DATA: &[u8] = include_bytes!("../../../assets/theme_preview.rs");
|
||||
|
||||
#[cfg(feature = "build-assets")]
|
||||
fn build_assets(matches: &clap::ArgMatches) -> Result<()> {
|
||||
let source_dir = matches
|
||||
.value_of("source")
|
||||
.map(Path::new)
|
||||
.unwrap_or_else(|| PROJECT_DIRS.config_dir());
|
||||
let target_dir = matches
|
||||
.value_of("target")
|
||||
.map(Path::new)
|
||||
.unwrap_or_else(|| PROJECT_DIRS.cache_dir());
|
||||
|
||||
let blank = matches.is_present("blank");
|
||||
|
||||
bat::assets::build(source_dir, !blank, target_dir, clap::crate_version!())
|
||||
}
|
||||
|
||||
fn run_cache_subcommand(matches: &clap::ArgMatches) -> Result<()> {
|
||||
if matches.is_present("build") {
|
||||
let source_dir = matches
|
||||
.value_of("source")
|
||||
.map(Path::new)
|
||||
.unwrap_or_else(|| PROJECT_DIRS.config_dir());
|
||||
let target_dir = matches
|
||||
.value_of("target")
|
||||
.map(Path::new)
|
||||
.unwrap_or_else(|| PROJECT_DIRS.cache_dir());
|
||||
|
||||
let blank = matches.is_present("blank");
|
||||
|
||||
let assets = HighlightingAssets::from_files(source_dir, !blank)?;
|
||||
assets.save_to_cache(target_dir, crate_version!())?;
|
||||
#[cfg(feature = "build-assets")]
|
||||
build_assets(matches)?;
|
||||
#[cfg(not(feature = "build-assets"))]
|
||||
println!("bat has been built without the 'build-assets' feature. The 'cache --build' option is not available.");
|
||||
} else if matches.is_present("clear") {
|
||||
clear_assets();
|
||||
}
|
||||
@@ -80,16 +84,16 @@ fn get_syntax_mapping_to_paths<'a>(
|
||||
pub fn get_languages(config: &Config) -> Result<String> {
|
||||
let mut result: String = String::new();
|
||||
|
||||
let assets = assets_from_cache_or_binary()?;
|
||||
let assets = assets_from_cache_or_binary(config.use_custom_assets)?;
|
||||
let mut languages = assets
|
||||
.syntaxes()
|
||||
.get_syntaxes()?
|
||||
.iter()
|
||||
.filter(|syntax| !syntax.hidden && !syntax.file_extensions.is_empty())
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Handling of file-extension conflicts, see issue #1076
|
||||
for lang in languages.iter_mut() {
|
||||
for lang in &mut languages {
|
||||
let lang_name = lang.name.clone();
|
||||
lang.file_extensions.retain(|extension| {
|
||||
// The 'extension' variable is not certainly a real extension.
|
||||
@@ -98,14 +102,12 @@ 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() {
|
||||
true
|
||||
} else {
|
||||
let test_file = Path::new("test").with_extension(extension);
|
||||
match assets.syntax_for_file_name(test_file, &config.syntax_mapping) {
|
||||
Some(syntax) => syntax.name == lang_name,
|
||||
None => false,
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -113,7 +115,7 @@ pub fn get_languages(config: &Config) -> Result<String> {
|
||||
|
||||
let configured_languages = get_syntax_mapping_to_paths(config.syntax_mapping.mappings());
|
||||
|
||||
for lang in languages.iter_mut() {
|
||||
for lang in &mut languages {
|
||||
if let Some(additional_paths) = configured_languages.get(lang.name.as_str()) {
|
||||
lang.file_extensions
|
||||
.extend(additional_paths.iter().cloned());
|
||||
@@ -175,7 +177,7 @@ fn theme_preview_file<'a>() -> Input<'a> {
|
||||
}
|
||||
|
||||
pub fn list_themes(cfg: &Config) -> Result<()> {
|
||||
let assets = assets_from_cache_or_binary()?;
|
||||
let assets = assets_from_cache_or_binary(cfg.use_custom_assets)?;
|
||||
let mut config = cfg.clone();
|
||||
let mut style = HashSet::new();
|
||||
style.insert(StyleComponent::Plain);
|
||||
@@ -216,57 +218,65 @@ pub fn list_themes(cfg: &Config) -> Result<()> {
|
||||
}
|
||||
|
||||
fn run_controller(inputs: Vec<Input>, config: &Config) -> Result<bool> {
|
||||
let assets = assets_from_cache_or_binary()?;
|
||||
let controller = Controller::new(&config, &assets);
|
||||
let assets = assets_from_cache_or_binary(config.use_custom_assets)?;
|
||||
let controller = Controller::new(config, &assets);
|
||||
controller.run(inputs)
|
||||
}
|
||||
|
||||
#[cfg(feature = "bugreport")]
|
||||
fn invoke_bugreport(app: &App) {
|
||||
use bugreport::{bugreport, collector::*, format::Markdown};
|
||||
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!()
|
||||
.info(SoftwareVersion::default())
|
||||
.info(OperatingSystem::default())
|
||||
.info(CommandLine::default())
|
||||
.info(EnvironmentVariables::list(&[
|
||||
"SHELL",
|
||||
"PAGER",
|
||||
"LESS",
|
||||
"LANG",
|
||||
"LC_ALL",
|
||||
"BAT_PAGER",
|
||||
"BAT_CACHE_PATH",
|
||||
"BAT_CONFIG_PATH",
|
||||
"BAT_OPTS",
|
||||
"BAT_STYLE",
|
||||
"BAT_TABS",
|
||||
"BAT_THEME",
|
||||
"XDG_CONFIG_HOME",
|
||||
"XDG_CACHE_HOME",
|
||||
"COLORTERM",
|
||||
"NO_COLOR",
|
||||
"MANPAGER",
|
||||
]))
|
||||
.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(
|
||||
"Less version",
|
||||
resolved_path,
|
||||
&["--version"],
|
||||
))
|
||||
};
|
||||
|
||||
report.print::<Markdown>();
|
||||
}
|
||||
|
||||
/// Returns `Err(..)` upon fatal errors. Otherwise, returns `Ok(true)` on full success and
|
||||
/// `Ok(false)` if any intermediate errors occurred (were printed).
|
||||
fn run() -> Result<bool> {
|
||||
let app = App::new()?;
|
||||
|
||||
if app.matches.is_present("diagnostic") {
|
||||
use bugreport::{bugreport, collector::*, format::Markdown};
|
||||
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 report = bugreport!()
|
||||
.info(SoftwareVersion::default())
|
||||
.info(OperatingSystem::default())
|
||||
.info(CommandLine::default())
|
||||
.info(EnvironmentVariables::list(&[
|
||||
"SHELL",
|
||||
"PAGER",
|
||||
"LESS",
|
||||
"BAT_PAGER",
|
||||
"BAT_CACHE_PATH",
|
||||
"BAT_CONFIG_PATH",
|
||||
"BAT_OPTS",
|
||||
"BAT_STYLE",
|
||||
"BAT_TABS",
|
||||
"BAT_THEME",
|
||||
"XDG_CONFIG_HOME",
|
||||
"XDG_CACHE_HOME",
|
||||
"COLORTERM",
|
||||
"NO_COLOR",
|
||||
"MANPAGER",
|
||||
]))
|
||||
.info(FileContent::new("Config file", config_file()))
|
||||
.info(CompileTimeInformation::default());
|
||||
|
||||
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>();
|
||||
|
||||
#[cfg(feature = "bugreport")]
|
||||
invoke_bugreport(&app);
|
||||
#[cfg(not(feature = "bugreport"))]
|
||||
println!("bat has been built without the 'bugreport' feature. The '--diagnostic' option is not available.");
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
|
@@ -82,15 +82,18 @@ pub struct Config<'a> {
|
||||
|
||||
/// Ranges of lines which should be highlighted with a special background color
|
||||
pub highlighted_lines: HighlightedLineRanges,
|
||||
|
||||
/// Whether or not to allow custom assets. If this is false or if custom assets (a.k.a.
|
||||
/// cached assets) are not available, assets from the binary will be used instead.
|
||||
pub use_custom_assets: bool,
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "application", feature = "paging"))]
|
||||
#[cfg(all(feature = "minimal-application", feature = "paging"))]
|
||||
pub fn get_pager_executable(config_pager: Option<&str>) -> Option<String> {
|
||||
if let Ok(Some(pager)) = crate::pager::get_pager(config_pager) {
|
||||
Some(pager.bin)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
crate::pager::get_pager(config_pager)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|pager| pager.bin)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@@ -45,7 +45,7 @@ impl<'b> Controller<'b> {
|
||||
// Do not launch the pager if NONE of the input files exist
|
||||
let mut paging_mode = self.config.paging_mode;
|
||||
if self.config.paging_mode != PagingMode::Never {
|
||||
let call_pager = inputs.iter().any(|ref input| {
|
||||
let call_pager = inputs.iter().any(|input| {
|
||||
if let InputKind::OrdinaryFile(ref path) = input.kind {
|
||||
Path::new(path).exists()
|
||||
} else {
|
||||
@@ -124,11 +124,11 @@ impl<'b> Controller<'b> {
|
||||
};
|
||||
|
||||
let mut printer: Box<dyn Printer> = if self.config.loop_through {
|
||||
Box::new(SimplePrinter::new(&self.config))
|
||||
Box::new(SimplePrinter::new(self.config))
|
||||
} else {
|
||||
Box::new(InteractivePrinter::new(
|
||||
&self.config,
|
||||
&self.assets,
|
||||
self.config,
|
||||
self.assets,
|
||||
&mut opened_input,
|
||||
#[cfg(feature = "git")]
|
||||
&line_changes,
|
||||
@@ -175,9 +175,10 @@ 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 - context, line + context));
|
||||
for &line in line_changes.keys() {
|
||||
let line = line as usize;
|
||||
line_ranges
|
||||
.push(LineRange::new(line.saturating_sub(context), line + context));
|
||||
}
|
||||
}
|
||||
|
||||
|
64
src/error.rs
64
src/error.rs
@@ -1,42 +1,52 @@
|
||||
use error_chain::error_chain;
|
||||
use std::io::Write;
|
||||
use thiserror::Error;
|
||||
|
||||
error_chain! {
|
||||
foreign_links {
|
||||
Clap(::clap::Error) #[cfg(feature = "application")];
|
||||
Io(::std::io::Error);
|
||||
SyntectError(::syntect::LoadingError);
|
||||
ParseIntError(::std::num::ParseIntError);
|
||||
GlobParsingError(::globset::Error);
|
||||
SerdeYamlError(::serde_yaml::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),
|
||||
}
|
||||
|
||||
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<&'static str> for Error {
|
||||
fn from(s: &'static str) -> Self {
|
||||
Error::Msg(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
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(ErrorKind::Io(ref io_error), _)
|
||||
if io_error.kind() == ::std::io::ErrorKind::BrokenPipe =>
|
||||
{
|
||||
Error::Io(ref io_error) if io_error.kind() == ::std::io::ErrorKind::BrokenPipe => {
|
||||
::std::process::exit(0);
|
||||
}
|
||||
Error(ErrorKind::SerdeYamlError(_), _) => {
|
||||
Error::SerdeYamlError(_) => {
|
||||
writeln!(
|
||||
output,
|
||||
"{}: Error while parsing metadata.yaml file: {}",
|
||||
|
51
src/input.rs
51
src/input.rs
@@ -50,8 +50,8 @@ impl InputDescription {
|
||||
}
|
||||
|
||||
pub fn title(&self) -> &String {
|
||||
match self.title.as_ref() {
|
||||
Some(ref title) => title,
|
||||
match &self.title {
|
||||
Some(title) => title,
|
||||
None => &self.name,
|
||||
}
|
||||
}
|
||||
@@ -108,6 +108,21 @@ 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())
|
||||
@@ -256,18 +271,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() {
|
||||
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 {
|
||||
if !self.first_line.is_empty() {
|
||||
buf.append(&mut self.first_line);
|
||||
Ok(true)
|
||||
return 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,21 +297,21 @@ fn basic() {
|
||||
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(true, res.unwrap());
|
||||
assert!(res.unwrap());
|
||||
assert_eq!(b"#!/bin/bash\n", &buffer[..]);
|
||||
|
||||
buffer.clear();
|
||||
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(true, res.unwrap());
|
||||
assert!(res.unwrap());
|
||||
assert_eq!(b"echo hello", &buffer[..]);
|
||||
|
||||
buffer.clear();
|
||||
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(false, res.unwrap());
|
||||
assert!(!res.unwrap());
|
||||
assert!(buffer.is_empty());
|
||||
}
|
||||
|
||||
@@ -311,20 +326,20 @@ fn utf16le() {
|
||||
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(true, res.unwrap());
|
||||
assert!(res.unwrap());
|
||||
assert_eq!(b"\xFF\xFE\x73\x00\x0A\x00", &buffer[..]);
|
||||
|
||||
buffer.clear();
|
||||
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(true, res.unwrap());
|
||||
assert!(res.unwrap());
|
||||
assert_eq!(b"\x64\x00", &buffer[..]);
|
||||
|
||||
buffer.clear();
|
||||
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(false, res.unwrap());
|
||||
assert!(!res.unwrap());
|
||||
assert!(buffer.is_empty());
|
||||
}
|
||||
|
12
src/less.rs
12
src/less.rs
@@ -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 ") {
|
||||
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
|
||||
if !output.starts_with(b"less ") {
|
||||
return 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]
|
||||
|
@@ -19,10 +19,14 @@
|
||||
//! .unwrap();
|
||||
//! ```
|
||||
|
||||
#![deny(unsafe_code)]
|
||||
|
||||
mod macros;
|
||||
|
||||
pub mod assets;
|
||||
pub mod assets_metadata;
|
||||
pub mod assets_metadata {
|
||||
pub use super::assets::assets_metadata::*;
|
||||
}
|
||||
pub mod config;
|
||||
pub mod controller;
|
||||
mod decorations;
|
||||
|
@@ -47,7 +47,16 @@ impl LineRange {
|
||||
}
|
||||
2 => {
|
||||
new_range.lower = line_numbers[0].parse()?;
|
||||
new_range.upper = line_numbers[1].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()?
|
||||
};
|
||||
|
||||
Ok(new_range)
|
||||
}
|
||||
_ => Err(
|
||||
@@ -100,6 +109,23 @@ 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
|
||||
|
@@ -52,7 +52,7 @@ impl OutputType {
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
let pager_opt =
|
||||
pager::get_pager(pager_from_config).chain_err(|| "Could not parse pager command.")?;
|
||||
pager::get_pager(pager_from_config).map_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(ErrorKind::InvalidPagerValueBat.into());
|
||||
return Err(Error::InvalidPagerValueBat);
|
||||
}
|
||||
|
||||
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()
|
||||
.chain_err(|| "Could not open stdin for pager")?,
|
||||
.ok_or("Could not open stdin for pager")?,
|
||||
OutputType::Stdout(ref mut handle) => handle,
|
||||
})
|
||||
}
|
||||
|
@@ -235,7 +235,9 @@ impl<'a> PrettyPrinter<'a> {
|
||||
}
|
||||
|
||||
pub fn syntaxes(&self) -> impl Iterator<Item = &SyntaxReference> {
|
||||
self.assets.syntaxes().iter()
|
||||
// We always use assets from the binary, which are guaranteed to always
|
||||
// be valid, so get_syntaxes() can never fail here
|
||||
self.assets.get_syntaxes().unwrap().iter()
|
||||
}
|
||||
|
||||
/// Pretty-print all specified inputs. This method will "use" all stored inputs.
|
||||
|
@@ -18,7 +18,7 @@ use encoding::{DecoderTrap, Encoding};
|
||||
|
||||
use unicode_width::UnicodeWidthChar;
|
||||
|
||||
use crate::assets::HighlightingAssets;
|
||||
use crate::assets::{HighlightingAssets, SyntaxReferenceInSet};
|
||||
use crate::config::Config;
|
||||
#[cfg(feature = "git")]
|
||||
use crate::decorations::LineChangesDecoration;
|
||||
@@ -99,6 +99,20 @@ 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>,
|
||||
@@ -108,8 +122,7 @@ pub(crate) struct InteractivePrinter<'a> {
|
||||
content_type: Option<ContentType>,
|
||||
#[cfg(feature = "git")]
|
||||
pub line_changes: &'a Option<LineChanges>,
|
||||
highlighter: Option<HighlightLines<'a>>,
|
||||
syntax_set: &'a SyntaxSet,
|
||||
highlighter_from_set: Option<HighlighterFromSet<'a>>,
|
||||
background_color_highlight: Option<Color>,
|
||||
}
|
||||
|
||||
@@ -163,7 +176,7 @@ impl<'a> InteractivePrinter<'a> {
|
||||
panel_width = 0;
|
||||
}
|
||||
|
||||
let highlighter = if input
|
||||
let highlighter_from_set = if input
|
||||
.reader
|
||||
.content_type
|
||||
.map_or(false, |c| c.is_binary() && !config.show_nonprintable)
|
||||
@@ -171,15 +184,16 @@ impl<'a> InteractivePrinter<'a> {
|
||||
None
|
||||
} else {
|
||||
// Determine the type of syntax for highlighting
|
||||
let syntax = match assets.get_syntax(config.language, input, &config.syntax_mapping) {
|
||||
Ok(syntax) => syntax,
|
||||
Err(Error(ErrorKind::UndetectedSyntax(_), _)) => {
|
||||
assets.syntax_set.find_syntax_plain_text()
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
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(e) => return Err(e),
|
||||
};
|
||||
|
||||
Some(HighlightLines::new(syntax, theme))
|
||||
Some(HighlighterFromSet::new(syntax_in_set, theme))
|
||||
};
|
||||
|
||||
Ok(InteractivePrinter {
|
||||
@@ -191,8 +205,7 @@ impl<'a> InteractivePrinter<'a> {
|
||||
ansi_prefix_sgr: String::new(),
|
||||
#[cfg(feature = "git")]
|
||||
line_changes,
|
||||
highlighter,
|
||||
syntax_set: &assets.syntax_set,
|
||||
highlighter_from_set,
|
||||
background_color_highlight,
|
||||
})
|
||||
}
|
||||
@@ -220,29 +233,29 @@ impl<'a> InteractivePrinter<'a> {
|
||||
|
||||
fn create_fake_panel(&self, text: &str) -> String {
|
||||
if self.panel_width == 0 {
|
||||
"".to_string()
|
||||
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)
|
||||
} else {
|
||||
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
|
||||
}
|
||||
text_filled
|
||||
}
|
||||
}
|
||||
|
||||
fn preprocess(&self, text: &str, cursor: &mut usize) -> String {
|
||||
if self.config.tab_width > 0 {
|
||||
expand_tabs(text, self.config.tab_width, cursor)
|
||||
} else {
|
||||
*cursor += text.len();
|
||||
text.to_string()
|
||||
return expand_tabs(text, self.config.tab_width, cursor);
|
||||
}
|
||||
|
||||
*cursor += text.len();
|
||||
text.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,30 +379,32 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
||||
line_buffer: &[u8],
|
||||
) -> Result<()> {
|
||||
let line = if self.config.show_nonprintable {
|
||||
replace_nonprintable(&line_buffer, self.config.tab_width)
|
||||
replace_nonprintable(line_buffer, self.config.tab_width)
|
||||
} else {
|
||||
match self.content_type {
|
||||
Some(ContentType::BINARY) | None => {
|
||||
return Ok(());
|
||||
}
|
||||
Some(ContentType::UTF_16LE) => UTF_16LE
|
||||
.decode(&line_buffer, DecoderTrap::Replace)
|
||||
.decode(line_buffer, DecoderTrap::Replace)
|
||||
.map_err(|_| "Invalid UTF-16LE")?,
|
||||
Some(ContentType::UTF_16BE) => UTF_16BE
|
||||
.decode(&line_buffer, DecoderTrap::Replace)
|
||||
.decode(line_buffer, DecoderTrap::Replace)
|
||||
.map_err(|_| "Invalid UTF-16BE")?,
|
||||
_ => String::from_utf8_lossy(&line_buffer).to_string(),
|
||||
_ => String::from_utf8_lossy(line_buffer).to_string(),
|
||||
}
|
||||
};
|
||||
|
||||
let regions = {
|
||||
let highlighter = match self.highlighter {
|
||||
Some(ref mut highlighter) => highlighter,
|
||||
let highlighter_from_set = match self.highlighter_from_set {
|
||||
Some(ref mut highlighter_from_set) => highlighter_from_set,
|
||||
_ => {
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
highlighter.highlight(line.as_ref(), self.syntax_set)
|
||||
highlighter_from_set
|
||||
.highlighter
|
||||
.highlight(&line, highlighter_from_set.syntax_set)
|
||||
};
|
||||
|
||||
if out_of_range {
|
||||
@@ -414,8 +429,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
||||
let decorations = self
|
||||
.decorations
|
||||
.iter()
|
||||
.map(|ref d| d.generate(line_number, false, self))
|
||||
.collect::<Vec<_>>();
|
||||
.map(|d| d.generate(line_number, false, self));
|
||||
|
||||
for deco in decorations {
|
||||
write!(handle, "{} ", deco.text)?;
|
||||
@@ -429,7 +443,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.iter() {
|
||||
for &(style, region) in ®ions {
|
||||
let text = &*self.preprocess(region, &mut cursor_total);
|
||||
let text_trimmed = text.trim_end_matches(|c| c == '\r' || c == '\n');
|
||||
write!(
|
||||
@@ -466,7 +480,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
||||
writeln!(handle)?;
|
||||
}
|
||||
} else {
|
||||
for &(style, region) in regions.iter() {
|
||||
for &(style, region) in ®ions {
|
||||
let ansi_iterator = AnsiCodeIterator::new(region);
|
||||
let mut ansi_prefix: String = String::new();
|
||||
for chunk in ansi_iterator {
|
||||
@@ -525,7 +539,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
||||
"{} ",
|
||||
self.decorations
|
||||
.iter()
|
||||
.map(|ref d| d
|
||||
.map(|d| d
|
||||
.generate(line_number, true, self)
|
||||
.text)
|
||||
.collect::<Vec<String>>()
|
||||
|
@@ -81,7 +81,7 @@ impl<'a> SyntaxMapping<'a> {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for glob in [
|
||||
for glob in &[
|
||||
"**/systemd/**/*.conf",
|
||||
"**/systemd/**/*.example",
|
||||
"*.automount",
|
||||
@@ -100,9 +100,7 @@ impl<'a> SyntaxMapping<'a> {
|
||||
"*.swap",
|
||||
"*.target",
|
||||
"*.timer",
|
||||
]
|
||||
.iter()
|
||||
{
|
||||
] {
|
||||
mapping.insert(glob, MappingTarget::MapTo("INI")).unwrap();
|
||||
}
|
||||
|
||||
@@ -153,7 +151,7 @@ impl<'a> SyntaxMapping<'a> {
|
||||
}
|
||||
|
||||
pub(crate) fn get_syntax_for(&self, path: impl AsRef<Path>) -> Option<MappingTarget<'a>> {
|
||||
let candidate = Candidate::new(path.as_ref());
|
||||
let candidate = Candidate::new(&path);
|
||||
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)
|
||||
|
10
tests/benchmarks/run-benchmarks.sh
vendored
10
tests/benchmarks/run-benchmarks.sh
vendored
@@ -3,9 +3,9 @@ cd "$(dirname "${BASH_SOURCE[0]}")" || exit
|
||||
|
||||
# Check that Hyperfine is installed.
|
||||
if ! command -v hyperfine > /dev/null 2>&1; then
|
||||
echo "'hyperfine' does not seem to be installed."
|
||||
echo "You can get it here: https://github.com/sharkdp/hyperfine"
|
||||
exit 1
|
||||
echo "'hyperfine' does not seem to be installed."
|
||||
echo "You can get it here: https://github.com/sharkdp/hyperfine"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Determine the target directories.
|
||||
@@ -15,7 +15,7 @@ get_target_dir() {
|
||||
| sed 's/^[[:space:]]*target-dir[[:space:]]*=//; s/^[[:space:]]*"//; s/"[[:space:]]*$//' \
|
||||
&& return 0
|
||||
fi
|
||||
|
||||
|
||||
echo "../../target"
|
||||
}
|
||||
|
||||
@@ -68,5 +68,5 @@ echo "### Time to syntax-highlight large files"
|
||||
echo
|
||||
|
||||
for SRC in test-src/*; do
|
||||
hyperfine --warmup 3 "$(printf "%q" "$BAT") --style=full --color=always --paging=never $(printf "%q" "$SRC")"
|
||||
hyperfine --warmup 3 "$(printf "%q" "$BAT") --style=full --color=always --paging=never $(printf "%q" "$SRC")"
|
||||
done
|
||||
|
2
tests/examples/regression_tests/first_line_fallback.invalid-syntax
vendored
Normal file
2
tests/examples/regression_tests/first_line_fallback.invalid-syntax
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env bash
|
||||
echo "Hello"
|
@@ -34,12 +34,17 @@ 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("PAGER");
|
||||
cmd.env_remove("BAT_PAGER");
|
||||
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_THEME");
|
||||
cmd.env_remove("BAT_TABS");
|
||||
cmd.env_remove("BAT_THEME");
|
||||
cmd.env_remove("COLORTERM");
|
||||
cmd.env_remove("NO_COLOR");
|
||||
cmd.env_remove("PAGER");
|
||||
cmd
|
||||
}
|
||||
|
||||
@@ -700,6 +705,16 @@ fn pager_failed_to_parse() {
|
||||
.stderr(predicate::str::contains("Could not parse pager command"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diagnostic_sanity_check() {
|
||||
bat()
|
||||
.arg("--diagnostic")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("BAT_PAGER="))
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_location_test() {
|
||||
bat_with_config()
|
||||
@@ -737,6 +752,16 @@ fn config_location_when_generating() {
|
||||
assert!(tmp_config_path.exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_location_from_bat_config_dir_variable() {
|
||||
bat_with_config()
|
||||
.env("BAT_CONFIG_DIR", "conf/")
|
||||
.arg("--config-file")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::is_match("conf/config\n").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_read_arguments_from_file() {
|
||||
bat_with_config()
|
||||
@@ -795,6 +820,17 @@ fn does_not_print_unwanted_file_named_cache() {
|
||||
bat_with_config().arg("cach").assert().failure();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accepts_no_custom_assets_arg() {
|
||||
// Just make sure --no-custom-assets is considered a valid arg
|
||||
// Don't bother to actually verify that it works
|
||||
bat()
|
||||
.arg("--no-custom-assets")
|
||||
.arg("test.txt")
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unicode_wrap() {
|
||||
bat_with_config()
|
||||
@@ -1091,6 +1127,21 @@ 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()
|
||||
@@ -1101,6 +1152,42 @@ fn show_all_mode() {
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_paging_arg() {
|
||||
bat()
|
||||
.arg("--no-paging")
|
||||
.arg("--color=never")
|
||||
.arg("--decorations=never")
|
||||
.arg("single-line.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("Single Line");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_paging_short_arg() {
|
||||
bat()
|
||||
.arg("-P")
|
||||
.arg("--color=never")
|
||||
.arg("--decorations=never")
|
||||
.arg("single-line.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("Single Line");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_pager_arg() {
|
||||
bat()
|
||||
.arg("--no-pager")
|
||||
.arg("--color=never")
|
||||
.arg("--decorations=never")
|
||||
.arg("single-line.txt")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("Single Line");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plain_mode_does_not_add_nonexisting_newline() {
|
||||
bat()
|
||||
|
@@ -26,7 +26,7 @@ fn no_duplicate_extensions() {
|
||||
|
||||
let mut extensions = HashSet::new();
|
||||
|
||||
for syntax in assets.syntaxes() {
|
||||
for syntax in assets.get_syntaxes().expect("this is a #[test]") {
|
||||
for extension in &syntax.file_extensions {
|
||||
assert!(
|
||||
KNOWN_EXCEPTIONS.contains(&extension.as_str()) || extensions.insert(extension),
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# This script goes through all languages that are supported by 'bat'. For each
|
||||
# language, it loops over the correspoinding file extensions and searches a
|
||||
# language, it loops over the corresponding file extensions and searches a
|
||||
# given folder for matching files. It calls 'bat' for each of these files and
|
||||
# measures the highlighting speed (number of characters per second). The script
|
||||
# reports files which lead to slow highlighting speeds or errors during the
|
||||
|
1
tests/snapshots/output/changes.snapshot.txt
vendored
1
tests/snapshots/output/changes.snapshot.txt
vendored
@@ -1,3 +1,4 @@
|
||||
+ /// A rectangle. First line is changed to prevent a regression of #1869
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
@@ -1,4 +1,5 @@
|
||||
──┬─────────────────────────────────────────────────────────────────────────────
|
||||
+ │ /// A rectangle. First line is changed to prevent a regression of #1869
|
||||
│ struct Rectangle {
|
||||
│ width: u32,
|
||||
│ height: u32,
|
||||
|
@@ -1,6 +1,7 @@
|
||||
──┬─────────────────────────────────────────────────────────────────────────────
|
||||
│ File: sample.rs
|
||||
──┼─────────────────────────────────────────────────────────────────────────────
|
||||
+ │ /// A rectangle. First line is changed to prevent a regression of #1869
|
||||
│ struct Rectangle {
|
||||
│ width: u32,
|
||||
│ height: u32,
|
||||
|
@@ -1,26 +1,27 @@
|
||||
───────┬────────────────────────────────────────────────────────────────────────
|
||||
│ File: sample.rs
|
||||
───────┼────────────────────────────────────────────────────────────────────────
|
||||
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 + │ }
|
||||
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,26 +1,27 @@
|
||||
───────┬────────────────────────────────────────────────────────────────────────
|
||||
│ File: sample.rs
|
||||
───────┼────────────────────────────────────────────────────────────────────────
|
||||
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 + │ }
|
||||
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,6 +1,7 @@
|
||||
──┬─────────────────────────────────────────────────────────────────────────────
|
||||
│ File: sample.rs
|
||||
──┼─────────────────────────────────────────────────────────────────────────────
|
||||
+ │ /// A rectangle. First line is changed to prevent a regression of #1869
|
||||
│ struct Rectangle {
|
||||
│ width: u32,
|
||||
│ height: u32,
|
||||
|
@@ -1,24 +1,25 @@
|
||||
───────┬────────────────────────────────────────────────────────────────────────
|
||||
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 + │ }
|
||||
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,24 +1,25 @@
|
||||
───────┬────────────────────────────────────────────────────────────────────────
|
||||
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 + │ }
|
||||
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,4 +1,5 @@
|
||||
──┬─────────────────────────────────────────────────────────────────────────────
|
||||
+ │ /// A rectangle. First line is changed to prevent a regression of #1869
|
||||
│ struct Rectangle {
|
||||
│ width: u32,
|
||||
│ height: u32,
|
||||
|
@@ -1,4 +1,5 @@
|
||||
File: sample.rs
|
||||
+ /// A rectangle. First line is changed to prevent a regression of #1869
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
@@ -1,23 +1,24 @@
|
||||
File: sample.rs
|
||||
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 + }
|
||||
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,23 +1,24 @@
|
||||
File: sample.rs
|
||||
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 + }
|
||||
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,4 +1,5 @@
|
||||
File: sample.rs
|
||||
+ /// A rectangle. First line is changed to prevent a regression of #1869
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
@@ -1,22 +1,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 + }
|
||||
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,22 +1,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 + }
|
||||
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,3 +1,4 @@
|
||||
+ /// A rectangle. First line is changed to prevent a regression of #1869
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
45
tests/snapshots/output/full.snapshot.txt
vendored
45
tests/snapshots/output/full.snapshot.txt
vendored
@@ -1,26 +1,27 @@
|
||||
───────┬────────────────────────────────────────────────────────────────────────
|
||||
│ File: sample.rs
|
||||
───────┼────────────────────────────────────────────────────────────────────────
|
||||
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 + │ }
|
||||
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
tests/snapshots/output/grid.snapshot.txt
vendored
1
tests/snapshots/output/grid.snapshot.txt
vendored
@@ -1,4 +1,5 @@
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
/// A rectangle. First line is changed to prevent a regression of #1869
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
@@ -1,6 +1,7 @@
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
File: sample.rs
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
/// A rectangle. First line is changed to prevent a regression of #1869
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
@@ -1,26 +1,27 @@
|
||||
─────┬──────────────────────────────────────────────────────────────────────────
|
||||
│ File: sample.rs
|
||||
─────┼──────────────────────────────────────────────────────────────────────────
|
||||
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 │ }
|
||||
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,26 +1,27 @@
|
||||
─────┬──────────────────────────────────────────────────────────────────────────
|
||||
│ File: sample.rs
|
||||
─────┼──────────────────────────────────────────────────────────────────────────
|
||||
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 │ }
|
||||
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,6 +1,7 @@
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
File: sample.rs
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
/// A rectangle. First line is changed to prevent a regression of #1869
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
45
tests/snapshots/output/grid_numbers.snapshot.txt
vendored
45
tests/snapshots/output/grid_numbers.snapshot.txt
vendored
@@ -1,24 +1,25 @@
|
||||
─────┬──────────────────────────────────────────────────────────────────────────
|
||||
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 │ }
|
||||
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,24 +1,25 @@
|
||||
─────┬──────────────────────────────────────────────────────────────────────────
|
||||
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 │ }
|
||||
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,4 +1,5 @@
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
/// A rectangle. First line is changed to prevent a regression of #1869
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
1
tests/snapshots/output/header.snapshot.txt
vendored
1
tests/snapshots/output/header.snapshot.txt
vendored
@@ -1,4 +1,5 @@
|
||||
File: sample.rs
|
||||
/// A rectangle. First line is changed to prevent a regression of #1869
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
@@ -1,23 +1,24 @@
|
||||
File: sample.rs
|
||||
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 }
|
||||
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,23 +1,24 @@
|
||||
File: sample.rs
|
||||
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 }
|
||||
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,4 +1,5 @@
|
||||
File: sample.rs
|
||||
/// A rectangle. First line is changed to prevent a regression of #1869
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
45
tests/snapshots/output/numbers.snapshot.txt
vendored
45
tests/snapshots/output/numbers.snapshot.txt
vendored
@@ -1,22 +1,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 }
|
||||
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 }
|
||||
|
45
tests/snapshots/output/numbers_rule.snapshot.txt
vendored
45
tests/snapshots/output/numbers_rule.snapshot.txt
vendored
@@ -1,22 +1,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 }
|
||||
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
tests/snapshots/output/plain.snapshot.txt
vendored
1
tests/snapshots/output/plain.snapshot.txt
vendored
@@ -1,3 +1,4 @@
|
||||
/// A rectangle. First line is changed to prevent a regression of #1869
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
1
tests/snapshots/output/rule.snapshot.txt
vendored
1
tests/snapshots/output/rule.snapshot.txt
vendored
@@ -1,3 +1,4 @@
|
||||
/// A rectangle. First line is changed to prevent a regression of #1869
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
1
tests/snapshots/sample.modified.rs
vendored
1
tests/snapshots/sample.modified.rs
vendored
@@ -1,3 +1,4 @@
|
||||
/// A rectangle. First line is changed to prevent a regression of #1869
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
21
tests/syntax-tests/BatTestCustomAssets.sublime-syntax
Normal file
21
tests/syntax-tests/BatTestCustomAssets.sublime-syntax
Normal file
@@ -0,0 +1,21 @@
|
||||
%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
|
@@ -6,7 +6,7 @@ import sys
|
||||
import os.path as path
|
||||
import os
|
||||
import argparse
|
||||
|
||||
from multiprocessing import Pool
|
||||
|
||||
BAT_OPTIONS = [
|
||||
"--no-config",
|
||||
@@ -38,64 +38,79 @@ 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__))
|
||||
sources = path.join(root, "source", "*")
|
||||
source_paths = path.join(root, "source", "*")
|
||||
|
||||
for source in glob.glob(path.join(sources, "*")) + glob.glob(
|
||||
path.join(sources, ".*")
|
||||
sources = []
|
||||
for source in glob.glob(path.join(source_paths, "*")) + glob.glob(
|
||||
path.join(source_paths, ".*")
|
||||
):
|
||||
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
|
||||
sources.append((output_basepath, source))
|
||||
|
||||
source_dirname = path.basename(path.dirname(source))
|
||||
source_filename = path.basename(source)
|
||||
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
|
||||
|
||||
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)
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -114,4 +129,5 @@ if __name__ == "__main__":
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
create_highlighted_versions(output_basepath=args.output)
|
||||
if not create_highlighted_versions(output_basepath=args.output):
|
||||
sys.exit(1)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user