mirror of
https://github.com/sharkdp/bat
synced 2026-08-04 19:01:44 +00:00
Compare commits
218 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2508323264 | |||
| 297afad337 | |||
| 1dddce3aa1 | |||
| 76be0d3933 | |||
| e5b8c4471a | |||
| b191691767 | |||
| 5c95b8803b | |||
| 5b421b455d | |||
| a6d6c28723 | |||
| 2966939e38 | |||
| d179693d1d | |||
| 268577c6f9 | |||
| 037861e588 | |||
| 94ccc646e8 | |||
| 0cc5d1dab6 | |||
| 28397b8f78 | |||
| c899849101 | |||
| bf53b64125 | |||
| c68aa0f424 | |||
| 6b57f4eebc | |||
| 0ddd388a29 | |||
| 34811b8161 | |||
| 8b92aae23f | |||
| 3a0941cabe | |||
| 52877c6d67 | |||
| 455de98a89 | |||
| 8c6a74a6c5 | |||
| 3f8b340ed8 | |||
| 65e6970907 | |||
| 90cc019482 | |||
| eaf0b99d25 | |||
| 3b1dcca5d0 | |||
| 26dbdf4fa0 | |||
| 6691786d82 | |||
| 50209bfe21 | |||
| c91511cca1 | |||
| c9f7a0c126 | |||
| 69c798eafd | |||
| 195d5e0e16 | |||
| 86c1777205 | |||
| 16874ab016 | |||
| bf5883f06f | |||
| e27bdfc7d6 | |||
| a7f4f202cb | |||
| 397223374a | |||
| 0c2da90fa4 | |||
| fe29da6e76 | |||
| 0eccfb50d9 | |||
| f74263e71d | |||
| 54f4896ced | |||
| 9d92350cbb | |||
| 26d409db65 | |||
| 06cd4b2ce8 | |||
| a348a9f68e | |||
| 39e2971e96 | |||
| a13eb60df3 | |||
| 9342d0bb24 | |||
| 36d7250af2 | |||
| 2ce8655167 | |||
| a452467e06 | |||
| 2ea3758c4b | |||
| 65bb4c7ee6 | |||
| 556149ad3a | |||
| 33bbf25468 | |||
| 5ee8913538 | |||
| 9b1c1c0271 | |||
| 2712d63a4b | |||
| b21fb6bca8 | |||
| ee43377a9c | |||
| 120b33a9db | |||
| 17b37fedcb | |||
| 1013cdd80a | |||
| be0a2d6048 | |||
| d1a1943998 | |||
| 2a9f5a24ed | |||
| a0ae089c4a | |||
| 96cc391f2d | |||
| a1e1170319 | |||
| 54a331d162 | |||
| 5e3b17e6f7 | |||
| 486e6a19cd | |||
| 247dfbee83 | |||
| 882931a77b | |||
| 12cb438aa4 | |||
| d0782ef954 | |||
| c826c2a438 | |||
| 9aa97b6c22 | |||
| f7890870ea | |||
| 145b99f01c | |||
| 9af1d2b891 | |||
| 2eee68599d | |||
| d569693dab | |||
| 900f61032d | |||
| d0ca566979 | |||
| a5a7dc14e4 | |||
| 54b33c8c21 | |||
| a5de77af4d | |||
| 870ff7b538 | |||
| 15b9acd12f | |||
| b327127f37 | |||
| 9214a4a4f0 | |||
| fcecd97224 | |||
| 870b3c0daf | |||
| b9c780c8c8 | |||
| ca865f9d26 | |||
| 3ee0e56315 | |||
| 091b0b6437 | |||
| 9f005d115d | |||
| b4cfc9633c | |||
| f7e055b6b7 | |||
| d4b438b9d3 | |||
| cd26d403a3 | |||
| f95a23f948 | |||
| 0886a24685 | |||
| 53eb9c57d6 | |||
| 026a9ebae3 | |||
| bdd10b8bec | |||
| fc160b0dcd | |||
| cb7b158172 | |||
| fcc36b1f79 | |||
| 4e4110bf50 | |||
| 22c8978fca | |||
| f711fb5006 | |||
| 64a9341b73 | |||
| ccf88fd5d8 | |||
| 6343535eb2 | |||
| e2ac6de783 | |||
| 25cee002f9 | |||
| 13a53d230b | |||
| ea27053a63 | |||
| b2b932f3ed | |||
| 747d074be1 | |||
| 61109ece15 | |||
| 389edd7239 | |||
| 23813cc08b | |||
| fdeb98e859 | |||
| 6a417886aa | |||
| 8b9991d780 | |||
| f59ddd1e36 | |||
| f90b9d1acc | |||
| f7af537018 | |||
| fd68af8031 | |||
| ebdef04c8f | |||
| a4150600af | |||
| 49223eed06 | |||
| 7dc7133b3c | |||
| e5e47716b0 | |||
| b4f8cd3bae | |||
| ee3a37f3fc | |||
| 4c60ab12cc | |||
| ac32dd17c9 | |||
| 4e3c57cc7c | |||
| 438a9a99ed | |||
| 9b0877102c | |||
| 67bed73e15 | |||
| ef7c39b159 | |||
| 8a399c8d7d | |||
| 9a0e444e09 | |||
| 438f3df345 | |||
| 7e2e0c82ac | |||
| da92154163 | |||
| 179bd4f525 | |||
| 53d67e2b6e | |||
| b625d07c34 | |||
| ec606e5dcc | |||
| c253821a5e | |||
| ccb1d78b22 | |||
| 9eb64b97fe | |||
| 15f0268bdc | |||
| 3fa70deaa7 | |||
| d4553c6b38 | |||
| 23d92d7641 | |||
| 92729430fc | |||
| 3eb79d63ce | |||
| 0483407775 | |||
| cbdf5c50c4 | |||
| 418b3c5ea1 | |||
| 9dca3126b3 | |||
| 7df9a5fe82 | |||
| 4bba08062c | |||
| 4e8ea815af | |||
| b7bc2d54ba | |||
| 21f9e2b0f7 | |||
| 92aa2b547e | |||
| 65f66d0bee | |||
| f5a9236a47 | |||
| edbe8f3e1a | |||
| ed09a80a73 | |||
| 802540ea08 | |||
| dbf84e77e0 | |||
| 8f18567f2d | |||
| e4d7e004ee | |||
| 51b57ccdb7 | |||
| 25f83610f9 | |||
| 7f070c9dcb | |||
| 64ef61b409 | |||
| 95a2079bcd | |||
| cc60ed6563 | |||
| 50be143c62 | |||
| 59fabd8ca5 | |||
| ced6801740 | |||
| f81e38618c | |||
| 1ff46aaaf8 | |||
| 74d023a7b1 | |||
| 36bbc770eb | |||
| f43409f473 | |||
| 203e4f9249 | |||
| 22e785ca49 | |||
| 57072597d6 | |||
| fb8ef40f3e | |||
| 0e8e39a59d | |||
| 64de2a44d5 | |||
| 716b261efd | |||
| 58ca1f66d6 | |||
| 86dcb3c165 | |||
| 7b7a5a32d6 | |||
| a7232a6ecc | |||
| 38762c34d9 |
+48
@@ -0,0 +1,48 @@
|
||||
[submodule "assets/syntaxes/Elixir"]
|
||||
path = assets/syntaxes/Elixir
|
||||
url = https://github.com/princemaple/elixir-sublime-syntax/
|
||||
[submodule "assets/syntaxes/Packages"]
|
||||
path = assets/syntaxes/Packages
|
||||
url = https://github.com/sublimehq/Packages/
|
||||
[submodule "assets/syntaxes/TOML"]
|
||||
path = assets/syntaxes/TOML
|
||||
url = https://github.com/sharkdp/sublime_toml_highlighting
|
||||
[submodule "assets/syntaxes/Julia"]
|
||||
path = assets/syntaxes/Julia
|
||||
url = https://github.com/JuliaEditorSupport/Julia-sublime
|
||||
[submodule "assets/themes/sublime-monokai-extended"]
|
||||
path = assets/themes/sublime-monokai-extended
|
||||
url = https://github.com/jonschlinkert/sublime-monokai-extended
|
||||
[submodule "assets/syntaxes/Docker"]
|
||||
path = assets/syntaxes/Docker
|
||||
url = https://github.com/asbjornenge/Docker.tmbundle
|
||||
[submodule "assets/syntaxes/VimL"]
|
||||
path = assets/syntaxes/VimL
|
||||
url = https://github.com/SalGnt/Sublime-VimL
|
||||
[submodule "assets/syntaxes/INI"]
|
||||
path = assets/syntaxes/INI
|
||||
url = https://github.com/clintberry/sublime-text-2-ini
|
||||
[submodule "assets/syntaxes/CMake"]
|
||||
path = assets/syntaxes/CMake
|
||||
url = https://github.com/zyxar/Sublime-CMakeLists
|
||||
[submodule "assets/syntaxes/LESS"]
|
||||
path = assets/syntaxes/LESS
|
||||
url = https://github.com/danro/LESS-sublime
|
||||
[submodule "assets/themes/DarkNeon"]
|
||||
path = assets/themes/DarkNeon
|
||||
url = https://github.com/RainyDayMedia/DarkNeon
|
||||
[submodule "assets/themes/github-sublime-theme"]
|
||||
path = assets/themes/github-sublime-theme
|
||||
url = https://github.com/AlexanderEkdahl/github-sublime-theme
|
||||
[submodule "assets/themes/1337-Scheme"]
|
||||
path = assets/themes/1337-Scheme
|
||||
url = https://github.com/MarkMichos/1337-Scheme
|
||||
[submodule "assets/themes/TwoDark"]
|
||||
path = assets/themes/TwoDark
|
||||
url = https://github.com/erremauro/TwoDark
|
||||
[submodule "assets/syntaxes/AWK"]
|
||||
path = assets/syntaxes/AWK
|
||||
url = https://github.com/JohnNilsson/awk-sublime
|
||||
[submodule "assets/syntaxes/Nix"]
|
||||
path = assets/syntaxes/Nix
|
||||
url = https://github.com/wmertens/sublime-nix
|
||||
+34
-12
@@ -6,25 +6,49 @@ matrix:
|
||||
- os: linux
|
||||
rust: stable
|
||||
env: TARGET=x86_64-unknown-linux-gnu
|
||||
- os: linux
|
||||
rust: stable
|
||||
env:
|
||||
- TARGET=x86_64-unknown-linux-musl
|
||||
- CC_x86_64_unknown_linux_musl=/usr/bin/musl-gcc
|
||||
- os: linux
|
||||
rust: stable
|
||||
env: TARGET=i686-unknown-linux-gnu
|
||||
- os: osx
|
||||
rust: stable
|
||||
env: TARGET=x86_64-apple-darwin
|
||||
- os: linux
|
||||
rust: stable
|
||||
env:
|
||||
- TARGET=arm-unknown-linux-gnueabihf
|
||||
- CC_arm_unknown_linux_gnueabihf=/usr/bin/arm-linux-gnueabihf-gcc-4.8
|
||||
- CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc-4.8
|
||||
|
||||
# Minimum Rust supported channel.
|
||||
- os: linux
|
||||
rust: 1.25.0
|
||||
rust: 1.26.0
|
||||
env: TARGET=x86_64-unknown-linux-gnu
|
||||
- os: linux
|
||||
rust: 1.26.0
|
||||
env:
|
||||
- TARGET=x86_64-unknown-linux-musl
|
||||
- CC_x86_64_unknown_linux_musl=/usr/bin/musl-gcc
|
||||
- os: osx
|
||||
rust: 1.25.0
|
||||
rust: 1.26.0
|
||||
env: TARGET=x86_64-apple-darwin
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
# needed for i686-unknown-linux-gnu target
|
||||
- gcc-multilib
|
||||
# needed to build deb packages
|
||||
- fakeroot
|
||||
# Code formatting check
|
||||
- os: linux
|
||||
rust: nightly
|
||||
# skip the global install step
|
||||
install:
|
||||
- cargo install --debug --force rustfmt-nightly
|
||||
script: cargo fmt -- --check
|
||||
|
||||
sudo: required
|
||||
|
||||
before_install:
|
||||
- ci/before_install.bash
|
||||
|
||||
env:
|
||||
global:
|
||||
@@ -39,9 +63,7 @@ install:
|
||||
- if [[ $TRAVIS_OS_NAME = linux && $HOST != $TARGET ]]; then rustup target add $TARGET; fi
|
||||
|
||||
script:
|
||||
# Incorporate TARGET env var to the build and test process
|
||||
- cargo build --target $TARGET --verbose
|
||||
- cargo test --target $TARGET --verbose
|
||||
- ci/script.bash
|
||||
|
||||
before_deploy:
|
||||
- bash ci/before_deploy.bash
|
||||
|
||||
Generated
+283
-335
File diff suppressed because it is too large
Load Diff
+26
-4
@@ -7,16 +7,38 @@ license = "MIT/Apache-2.0"
|
||||
name = "bat"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/sharkdp/bat"
|
||||
version = "0.1.0"
|
||||
version = "0.5.0"
|
||||
exclude = [
|
||||
"assets/syntaxes/*",
|
||||
"assets/themes/*",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
atty = "0.2.2"
|
||||
syntect = "2"
|
||||
ansi_term = "0.9"
|
||||
ansi_term = "0.11"
|
||||
console = "0.6"
|
||||
git2 = "0.6"
|
||||
directories = "1.0"
|
||||
lazy_static = "1.0"
|
||||
|
||||
[dependencies.git2]
|
||||
version = "0.7"
|
||||
default-features = false
|
||||
features = []
|
||||
|
||||
[dependencies.syntect]
|
||||
version = "2.1"
|
||||
default-features = false
|
||||
features = ["parsing", "yaml-load", "dump-load", "dump-create"]
|
||||
|
||||
[dependencies.clap]
|
||||
version = "2"
|
||||
default-features = false
|
||||
features = ["suggestions", "color", "wrap_help"]
|
||||
|
||||
[dependencies.error-chain]
|
||||
version = "0.12"
|
||||
default-features = false
|
||||
features = []
|
||||
|
||||
[dev-dependencies]
|
||||
tempdir = "0.3"
|
||||
|
||||
@@ -1,28 +1,227 @@
|
||||
# bat
|
||||
<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>
|
||||
<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>
|
||||
A <i>cat(1)</i> clone with syntax highlighting and Git integration.
|
||||
</p>
|
||||
|
||||
[](https://travis-ci.org/sharkdp/bat)
|
||||
[](https://crates.io/crates/bat)
|
||||
|
||||
*A cat(1) clone with wings.*
|
||||
|
||||
## Features
|
||||
<p align="center">
|
||||
<a href="#syntax-highlighting">Key Features</a> •
|
||||
<a href="#how-to-use">How To Use</a> •
|
||||
<a href="#installation">Installation</a> •
|
||||
<a href="#customization">Customization</a> •
|
||||
<a href="#project-goals-and-alternatives">Project goals, alternatives</a>
|
||||
</p>
|
||||
|
||||
### Syntax highlighting
|
||||
|
||||

|
||||
`bat` supports syntax highlighting for a large number of programming and markup
|
||||
languages:
|
||||
|
||||

|
||||

|
||||
|
||||
### Git integration
|
||||
|
||||

|
||||
`bat` communicates with `git` to show modifications with respect to the index
|
||||
(see left side bar):
|
||||
|
||||

|
||||
|
||||
### Automatic paging
|
||||
|
||||
`bat` can pipe its own output to `less` if the output is too large for one screen.
|
||||
|
||||
### File concatenation
|
||||
|
||||
Oh.. you can also use it to concatenate files :wink:. Whenever
|
||||
`bat` detects a non-interactive terminal, it will fall back to printing
|
||||
the plain file contents.
|
||||
|
||||
## How to use
|
||||
|
||||
Display a single file on the terminal
|
||||
|
||||
```bash
|
||||
> bat README.md
|
||||
```
|
||||
|
||||
Display multiple files at once
|
||||
|
||||
```bash
|
||||
> bat src/*.rs
|
||||
```
|
||||
|
||||
Read from stdin, explicitly specify the language
|
||||
|
||||
```bash
|
||||
> yaml2json .travis.yml | json_pp | bat -l json
|
||||
```
|
||||
|
||||
```bash
|
||||
> curl -s https://raw.githubusercontent.com/sharkdp/bat/master/src/main.rs | bat -l rs
|
||||
```
|
||||
|
||||
As a replacement for `cat`:
|
||||
|
||||
```bash
|
||||
bat > note.md # quickly create a new file
|
||||
|
||||
bat header.md content.md footer.md > document.md
|
||||
|
||||
bat -n main.rs # show line numbers (only)
|
||||
|
||||
bat f - g # output 'f', then stdin, then 'g'.
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
cargo install bat
|
||||
### From binaries
|
||||
|
||||
mkdir -p ~/.config/bat/themes
|
||||
cd ~/.config/bat/themes
|
||||
wget https://raw.githubusercontent.com/cj/sublime/master/Color%20Scheme%20-%20Default/Monokai.tmTheme
|
||||
Check out the [Release page](https://github.com/sharkdp/bat/releases) for
|
||||
binary builds and Debian packages.
|
||||
|
||||
#### On Arch Linux
|
||||
|
||||
You can install via Pacman:
|
||||
|
||||
```bash
|
||||
pacman -S bat
|
||||
```
|
||||
|
||||
#### On FreeBSD
|
||||
|
||||
You can install a precompiled [`bat` package](https://www.freshports.org/textproc/bat) with pkg:
|
||||
|
||||
```bash
|
||||
pkg install bat
|
||||
```
|
||||
|
||||
or build it on your own from the FreeBSD ports:
|
||||
|
||||
```bash
|
||||
cd /usr/ports/textproc/bat
|
||||
make install
|
||||
```
|
||||
|
||||
#### On macOS
|
||||
|
||||
You can install `bat` with [Homebrew](http://braumeister.org/formula/bat):
|
||||
|
||||
```bash
|
||||
brew install bat
|
||||
```
|
||||
|
||||
### From source
|
||||
|
||||
If you want to build to compile `bat` from source, you need Rust 1.26 or
|
||||
higher. You can then use `cargo` to build everything:
|
||||
|
||||
```bash
|
||||
cargo install bat
|
||||
```
|
||||
|
||||
On macOS, you might have to install `cmake` (`brew install cmake`) in order for
|
||||
some dependencies to be built.
|
||||
|
||||
## Customization
|
||||
|
||||
### Highlighting theme
|
||||
|
||||
Use `bat --list-themes` to get a list of all available themes for syntax
|
||||
highlighting. To select the `TwoDark` theme, call `bat` with the
|
||||
`--theme=TwoDark` option or set the `BAT_THEME` environment variable to
|
||||
`TwoDark`. Use `export BAT_THEME="TwoDark"` in your shells startup file to
|
||||
make the change permanent.
|
||||
|
||||
### Output style
|
||||
|
||||
You can use the `--style` option to control the appearance of `bat`s output.
|
||||
You can use `--style=numbers,changes`, for example, to show only Git changes
|
||||
and line numbers but no grid and no file header.
|
||||
|
||||
### Adding new syntaxes / language definitions
|
||||
|
||||
`bat` uses the excellent [`syntect`](https://github.com/trishume/syntect/)
|
||||
library for syntax highlighting. `syntect` can read any
|
||||
[Sublime Text `.sublime-syntax` file](https://www.sublimetext.com/docs/3/syntax.html)
|
||||
and theme. To add new syntax definitions, do the following.
|
||||
|
||||
Create a folder with syntax definition files:
|
||||
|
||||
```bash
|
||||
BAT_CONFIG_DIR="$(bat cache --config-dir)"
|
||||
|
||||
mkdir -p "$BAT_CONFIG_DIR/syntaxes"
|
||||
cd "$BAT_CONFIG_DIR/syntaxes"
|
||||
|
||||
# Put new '.sublime-syntax' language definition files
|
||||
# in this folder (or its subdirectories), for example:
|
||||
git clone https://github.com/tellnobody1/sublime-purescript-syntax
|
||||
```
|
||||
|
||||
Now use the following command to parse these files into a binary cache:
|
||||
|
||||
```bash
|
||||
bat cache --init
|
||||
```
|
||||
|
||||
Finally, use `bat --list-languages` to check if the new languages are available.
|
||||
|
||||
If you ever want to go back to the default settings, call:
|
||||
|
||||
```bash
|
||||
bat cache --clear
|
||||
```
|
||||
|
||||
### Adding new themes
|
||||
|
||||
This works very similar to how we add new syntax definitions.
|
||||
|
||||
First, create a folder with the new syntax highlighting themes:
|
||||
```bash
|
||||
BAT_CONFIG_DIR="$(bat cache --config-dir)"
|
||||
|
||||
mkdir -p "$BAT_CONFIG_DIR/themes"
|
||||
cd "$BAT_CONFIG_DIR/themes"
|
||||
|
||||
# Download a theme in '.tmTheme' format, for example:
|
||||
git clone https://github.com/greggb/sublime-snazzy
|
||||
|
||||
# Create a link to specify the new default theme
|
||||
ln -sf "sublime-snazzy/Sublime Snazzy.tmTheme" Default.tmTheme
|
||||
|
||||
# Update the binary cache
|
||||
bat cache --init
|
||||
```
|
||||
|
||||
Finally, use `bat --list-themes` to check if the new themes are available.
|
||||
|
||||
**Note:** Unlike for syntax definitions, adding custom themes currently *removes all default
|
||||
themes*. If you want to go back to the default themes, call `bat cache --clear`.
|
||||
|
||||
### Using a different pager
|
||||
|
||||
`bat` uses the pager that is specified in the `PAGER` environment variable. If this variable is not
|
||||
set, `less` is used by default. If you want to use a different pager, you can either modify the
|
||||
`PAGER` variable or set the `BAT_PAGER` environment variable to override what is specified in
|
||||
`PAGER`. If you want to pass command-line arguments to the pager, you need to create a small shell
|
||||
script as a wrapper, for example:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
less --tabs 4 -RF "$@"
|
||||
```
|
||||
|
||||
## Project goals and alternatives
|
||||
|
||||
`bat` tries to achieve the following goals:
|
||||
|
||||
- Provide beautiful, advanced syntax highlighting
|
||||
- Integrate with Git to show file modifications
|
||||
- Be a drop-in replacement for (POSIX) `cat`
|
||||
- Offer a user-friendly command-line interface
|
||||
|
||||
There are a lot of alternatives, if you are looking for similar programs. See
|
||||
[this document](doc/alternatives.md) for a comparison.
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
syntaxes/*
|
||||
themes/*
|
||||
@@ -0,0 +1,31 @@
|
||||
## Syntax Highlighting in bat
|
||||
|
||||
`bat` uses the [syntect](https://github.com/trishume/syntect) library to highlight source
|
||||
code. As a basis, syntect uses [Sublime Text](https://www.sublimetext.com/) syntax definitions
|
||||
in the `.sublime-syntax` format.
|
||||
|
||||
In order to add new syntaxes to `bat`, follow these steps:
|
||||
|
||||
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`).
|
||||
|
||||
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* ->
|
||||
*New Syntax from XXX.tmLanguage...*. Save the new file in the `assets/syntaxes` folder.
|
||||
|
||||
3. Run the `create.sh` script. It calls `bat cache --init` to parse all available
|
||||
`.sublime-syntax` files and serialize them to a `syntaxes.bin` file (in this folder).
|
||||
|
||||
4. Re-compile `bat`. At compilation time, the `syntaxes.bin` file will be stored inside the
|
||||
`bat` binary.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
Make sure that the local cache does not interfere with the internally stored syntaxes and
|
||||
themes (`bat cache --clear`).
|
||||
|
||||
### Manual modifications
|
||||
|
||||
The following files have been manually modified after converting from a `.tmLanguage` file:
|
||||
|
||||
* `VimL.sublime-syntax` => added `.vimrc` file type.
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
ASSET_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
bat cache --init --source="$ASSET_DIR" --target="$ASSET_DIR"
|
||||
Binary file not shown.
Submodule
+1
Submodule assets/syntaxes/AWK added at 1ce5f90d44
Submodule
+1
Submodule assets/syntaxes/CMake added at aba96a0862
Submodule
+1
Submodule assets/syntaxes/Docker added at 6e521ead6c
Submodule
+1
Submodule assets/syntaxes/Elixir added at 044d9af8a1
Submodule
+1
Submodule assets/syntaxes/INI added at 8dd3e316ad
@@ -0,0 +1,43 @@
|
||||
%YAML 1.2
|
||||
---
|
||||
# http://www.sublimetext.com/docs/3/syntax.html
|
||||
name: INI
|
||||
file_extensions:
|
||||
- ini
|
||||
- INI
|
||||
- inf
|
||||
- INF
|
||||
- reg
|
||||
- REG
|
||||
- lng
|
||||
- cfg
|
||||
- CFG
|
||||
- url
|
||||
- URL
|
||||
- .editorconfig
|
||||
scope: source.ini
|
||||
contexts:
|
||||
main:
|
||||
- match: ^\s*(;|#).*$\n?
|
||||
scope: comment.line.semicolon.ini
|
||||
captures:
|
||||
1: punctuation.definition.comment.ini
|
||||
- match: '^\s*(\[)(.*?)(\])'
|
||||
scope: meta.tag.section.ini
|
||||
captures:
|
||||
1: punctuation.definition.section.ini
|
||||
2: entity.section.ini
|
||||
3: punctuation.definition.section.ini
|
||||
- match: '^(\s*(["'']?)(.+?)(\2)\s*(=))?\s*((["'']?)(.*?)(\7))\s*(;.*)?$\n?'
|
||||
scope: meta.declaration.ini
|
||||
captures:
|
||||
1: meta.property.ini
|
||||
2: punctuation.definition.quote.ini
|
||||
3: keyword.name.ini
|
||||
4: punctuation.definition.quote.ini
|
||||
5: punctuation.definition.equals.ini
|
||||
6: meta.value.ini
|
||||
7: punctuation.definition.quote.ini
|
||||
8: string.name.value.ini
|
||||
9: punctuation.definition.quote.ini
|
||||
10: comment.declarationline.semicolon.ini
|
||||
Submodule
+1
Submodule assets/syntaxes/Julia added at 581805e47c
Submodule
+1
Submodule assets/syntaxes/LESS added at d3ddfe7b51
Submodule
+1
Submodule assets/syntaxes/Nix added at ebf06571f3
@@ -0,0 +1,609 @@
|
||||
%YAML 1.2
|
||||
---
|
||||
# http://www.sublimetext.com/docs/3/syntax.html
|
||||
name: Nix
|
||||
file_extensions:
|
||||
- nix
|
||||
scope: source.nix
|
||||
contexts:
|
||||
main:
|
||||
- include: expression
|
||||
comment:
|
||||
- match: '/\*([^*]|\*[^\/])*'
|
||||
push:
|
||||
- meta_scope: comment.block.nix
|
||||
- match: \*\/
|
||||
pop: true
|
||||
- include: comment-remark
|
||||
- match: '\#'
|
||||
push:
|
||||
- meta_scope: comment.line.number-sign.nix
|
||||
- match: $
|
||||
pop: true
|
||||
- include: comment-remark
|
||||
attribute-bind:
|
||||
- include: attribute-name
|
||||
- include: attribute-bind-from-equals
|
||||
attribute-bind-from-equals:
|
||||
- match: \=
|
||||
captures:
|
||||
0: keyword.operator.bind.nix
|
||||
push:
|
||||
- match: \;
|
||||
captures:
|
||||
0: punctuation.terminator.bind.nix
|
||||
pop: true
|
||||
- include: expression
|
||||
attribute-inherit:
|
||||
- match: \binherit\b
|
||||
captures:
|
||||
0: keyword.other.inherit.nix
|
||||
push:
|
||||
- match: \;
|
||||
captures:
|
||||
0: punctuation.terminator.inherit.nix
|
||||
pop: true
|
||||
- match: \(
|
||||
captures:
|
||||
0: punctuation.section.function.arguments.nix
|
||||
push:
|
||||
- match: (?=\;)
|
||||
pop: true
|
||||
- match: \)
|
||||
captures:
|
||||
0: punctuation.section.function.arguments.nix
|
||||
push:
|
||||
- match: (?=\;)
|
||||
pop: true
|
||||
- include: bad-reserved
|
||||
- include: attribute-name-single
|
||||
- include: others
|
||||
- include: expression
|
||||
- match: '(?=[a-zA-Z\_])'
|
||||
push:
|
||||
- match: (?=\;)
|
||||
pop: true
|
||||
- include: bad-reserved
|
||||
- include: attribute-name-single
|
||||
- include: others
|
||||
- include: others
|
||||
attribute-name:
|
||||
- match: '\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*'
|
||||
scope: entity.other.attribute-name.multipart.nix
|
||||
- match: \.
|
||||
- include: string-quoted
|
||||
- include: interpolation
|
||||
attribute-name-single:
|
||||
- match: '\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*'
|
||||
scope: entity.other.attribute-name.single.nix
|
||||
attrset-contents:
|
||||
- include: attribute-inherit
|
||||
- include: bad-reserved
|
||||
- include: attribute-bind
|
||||
- include: others
|
||||
attrset-definition:
|
||||
- match: '(?=\{)'
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- match: '(\{)'
|
||||
captures:
|
||||
0: punctuation.definition.attrset.nix
|
||||
push:
|
||||
- match: '(\})'
|
||||
captures:
|
||||
0: punctuation.definition.attrset.nix
|
||||
pop: true
|
||||
- include: attrset-contents
|
||||
- match: '(?<=\})'
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: expression-cont
|
||||
attrset-definition-brace-opened:
|
||||
- match: '(?<=\})'
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: expression-cont
|
||||
- match: (?=.?)
|
||||
push:
|
||||
- match: '\}'
|
||||
captures:
|
||||
0: punctuation.definition.attrset.nix
|
||||
pop: true
|
||||
- include: attrset-contents
|
||||
attrset-for-sure:
|
||||
- match: (?=\brec\b)
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- match: \brec\b
|
||||
captures:
|
||||
0: keyword.other.nix
|
||||
push:
|
||||
- match: '(?=\{)'
|
||||
pop: true
|
||||
- include: others
|
||||
- include: attrset-definition
|
||||
- include: others
|
||||
- match: '(?=\{\s*(\}|[^,?]*(=|;)))'
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: attrset-definition
|
||||
- include: others
|
||||
attrset-or-function:
|
||||
- match: '\{'
|
||||
captures:
|
||||
0: punctuation.definition.attrset-or-function.nix
|
||||
push:
|
||||
- match: '(?=([\])};]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- match: '(?=(\s*\}|\"|\binherit\b|\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*(\s*\.|\s*=[^=])))'
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: attrset-definition-brace-opened
|
||||
- match: '(?=(\.\.\.|\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*\s*[,?]))'
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: function-definition-brace-opened
|
||||
- include: bad-reserved
|
||||
- match: '\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*'
|
||||
captures:
|
||||
0: variable.parameter.function.maybe.nix
|
||||
push:
|
||||
- match: '(?=([\])};]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- match: (?=\.)
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: attrset-definition-brace-opened
|
||||
- match: \s*(\,)
|
||||
captures:
|
||||
1: keyword.operator.nix
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: function-definition-brace-opened
|
||||
- match: (?=\=)
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: attribute-bind-from-equals
|
||||
- include: attrset-definition-brace-opened
|
||||
- match: (?=\?)
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: function-parameter-default
|
||||
- match: \,
|
||||
captures:
|
||||
0: keyword.operator.nix
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: function-definition-brace-opened
|
||||
- include: others
|
||||
- include: others
|
||||
bad-reserved:
|
||||
- match: '(?<![\w''-])(if|then|else|assert|with|let|in|rec|inherit)(?![\w''-])'
|
||||
scope: invalid.illegal.reserved.nix
|
||||
comment-remark:
|
||||
- match: (TODO|FIXME|BUG|\!\!\!):?
|
||||
captures:
|
||||
1: markup.bold.comment.nix
|
||||
constants:
|
||||
- match: \b(builtins|true|false|null)\b
|
||||
captures:
|
||||
0: constant.language.nix
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: expression-cont
|
||||
- match: \b(scopedImport|import|isNull|abort|throw|baseNameOf|dirOf|removeAttrs|map|toString|derivationStrict|derivation)\b
|
||||
captures:
|
||||
0: support.function.nix
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: expression-cont
|
||||
- match: '\b[0-9]+\b'
|
||||
captures:
|
||||
0: constant.numeric.nix
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: expression-cont
|
||||
expression:
|
||||
- include: parens-and-cont
|
||||
- include: list-and-cont
|
||||
- include: string
|
||||
- include: interpolation
|
||||
- include: with-assert
|
||||
- include: function-for-sure
|
||||
- include: attrset-for-sure
|
||||
- include: attrset-or-function
|
||||
- include: let
|
||||
- include: if
|
||||
- include: operator-unary
|
||||
- include: constants
|
||||
- include: bad-reserved
|
||||
- include: parameter-name-and-cont
|
||||
- include: others
|
||||
expression-cont:
|
||||
- match: (?=.?)
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: parens
|
||||
- include: list
|
||||
- include: string
|
||||
- include: interpolation
|
||||
- include: function-for-sure
|
||||
- include: attrset-for-sure
|
||||
- include: attrset-or-function
|
||||
- match: '(\bor\b|\.|==|!=|!|\<\=|\<|\>\=|\>|&&|\|\||-\>|//|\?|\+\+|-|\*|/(?=([^*]|$))|\+)'
|
||||
scope: keyword.operator.nix
|
||||
- include: constants
|
||||
- include: bad-reserved
|
||||
- include: parameter-name
|
||||
- include: others
|
||||
function-body:
|
||||
- match: '(@\s*([a-zA-Z\_][a-zA-Z0-9\_\''\-]*)\s*)?(\:)'
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: expression
|
||||
function-body-from-colon:
|
||||
- match: (\:)
|
||||
captures:
|
||||
0: punctuation.definition.function.nix
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: expression
|
||||
function-contents:
|
||||
- include: bad-reserved
|
||||
- include: function-parameter
|
||||
- include: others
|
||||
function-definition:
|
||||
- match: (?=.?)
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: function-body-from-colon
|
||||
- match: (?=.?)
|
||||
push:
|
||||
- match: (?=\:)
|
||||
pop: true
|
||||
- match: '(\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*)'
|
||||
captures:
|
||||
0: variable.parameter.function.4.nix
|
||||
push:
|
||||
- match: (?=\:)
|
||||
pop: true
|
||||
- match: \@
|
||||
push:
|
||||
- match: (?=\:)
|
||||
pop: true
|
||||
- include: function-header-until-colon-no-arg
|
||||
- include: others
|
||||
- include: others
|
||||
- match: '(?=\{)'
|
||||
push:
|
||||
- match: (?=\:)
|
||||
pop: true
|
||||
- include: function-header-until-colon-with-arg
|
||||
- include: others
|
||||
function-definition-brace-opened:
|
||||
- match: (?=.?)
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: function-body-from-colon
|
||||
- match: (?=.?)
|
||||
push:
|
||||
- match: (?=\:)
|
||||
pop: true
|
||||
- include: function-header-close-brace-with-arg
|
||||
- match: (?=.?)
|
||||
push:
|
||||
- match: '(?=\})'
|
||||
pop: true
|
||||
- include: function-contents
|
||||
- include: others
|
||||
function-for-sure:
|
||||
- match: '(?=(\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*\s*[:@]|\{[^}]*\}\s*:|\{[^#}"''/=]*[,\?]))'
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: function-definition
|
||||
function-header-close-brace-no-arg:
|
||||
- match: '\}'
|
||||
captures:
|
||||
0: punctuation.definition.entity.function.nix
|
||||
push:
|
||||
- match: (?=\:)
|
||||
pop: true
|
||||
- include: others
|
||||
function-header-close-brace-with-arg:
|
||||
- match: '\}'
|
||||
captures:
|
||||
0: punctuation.definition.entity.function.nix
|
||||
push:
|
||||
- match: (?=\:)
|
||||
pop: true
|
||||
- include: function-header-terminal-arg
|
||||
- include: others
|
||||
function-header-open-brace:
|
||||
- match: '\{'
|
||||
captures:
|
||||
0: punctuation.definition.entity.function.2.nix
|
||||
push:
|
||||
- match: '(?=\})'
|
||||
pop: true
|
||||
- include: function-contents
|
||||
function-header-terminal-arg:
|
||||
- match: (?=@)
|
||||
push:
|
||||
- match: (?=\:)
|
||||
pop: true
|
||||
- match: \@
|
||||
push:
|
||||
- match: (?=\:)
|
||||
pop: true
|
||||
- match: '(\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*)'
|
||||
push:
|
||||
- meta_scope: variable.parameter.function.3.nix
|
||||
- match: (?=\:)
|
||||
pop: true
|
||||
- include: others
|
||||
- include: others
|
||||
function-header-until-colon-no-arg:
|
||||
- match: '(?=\{)'
|
||||
push:
|
||||
- match: (?=\:)
|
||||
pop: true
|
||||
- include: function-header-open-brace
|
||||
- include: function-header-close-brace-no-arg
|
||||
function-header-until-colon-with-arg:
|
||||
- match: '(?=\{)'
|
||||
push:
|
||||
- match: (?=\:)
|
||||
pop: true
|
||||
- include: function-header-open-brace
|
||||
- include: function-header-close-brace-with-arg
|
||||
function-parameter:
|
||||
- match: (\.\.\.)
|
||||
push:
|
||||
- meta_scope: keyword.operator.nix
|
||||
- match: '(,|(?=\}))'
|
||||
pop: true
|
||||
- include: others
|
||||
- match: '\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*'
|
||||
captures:
|
||||
0: variable.parameter.function.1.nix
|
||||
push:
|
||||
- match: '(,|(?=\}))'
|
||||
captures:
|
||||
0: keyword.operator.nix
|
||||
pop: true
|
||||
- include: whitespace
|
||||
- include: comment
|
||||
- include: function-parameter-default
|
||||
- include: expression
|
||||
- include: others
|
||||
function-parameter-default:
|
||||
- match: \?
|
||||
captures:
|
||||
0: keyword.operator.nix
|
||||
push:
|
||||
- match: "(?=[,}])"
|
||||
pop: true
|
||||
- include: expression
|
||||
if:
|
||||
- match: (?=\bif\b)
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- match: \bif\b
|
||||
captures:
|
||||
0: keyword.other.nix
|
||||
push:
|
||||
- match: \bth(?=en\b)
|
||||
captures:
|
||||
0: keyword.other.nix
|
||||
pop: true
|
||||
- include: expression
|
||||
- match: (?<=th)en\b
|
||||
captures:
|
||||
0: keyword.other.nix
|
||||
push:
|
||||
- match: \bel(?=se\b)
|
||||
captures:
|
||||
0: keyword.other.nix
|
||||
pop: true
|
||||
- include: expression
|
||||
- match: (?<=el)se\b
|
||||
captures:
|
||||
0: keyword.other.nix
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
captures:
|
||||
0: keyword.other.nix
|
||||
pop: true
|
||||
- include: expression
|
||||
illegal:
|
||||
- match: .
|
||||
scope: invalid.illegal
|
||||
interpolation:
|
||||
- match: '\$\{'
|
||||
captures:
|
||||
0: punctuation.section.embedded.begin.nix
|
||||
push:
|
||||
- meta_scope: markup.italic
|
||||
- match: '\}'
|
||||
captures:
|
||||
0: punctuation.section.embedded.end.nix
|
||||
pop: true
|
||||
- include: expression
|
||||
let:
|
||||
- match: (?=\blet\b)
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- match: \blet\b
|
||||
captures:
|
||||
0: keyword.other.nix
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(in|else|then)\b))'
|
||||
pop: true
|
||||
- match: '(?=\{)'
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- match: '\{'
|
||||
push:
|
||||
- match: '\}'
|
||||
pop: true
|
||||
- include: attrset-contents
|
||||
- match: '(^|(?<=\}))'
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: expression-cont
|
||||
- include: others
|
||||
- include: attrset-contents
|
||||
- include: others
|
||||
- match: \bin\b
|
||||
captures:
|
||||
0: keyword.other.nix
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: expression
|
||||
list:
|
||||
- match: '\['
|
||||
captures:
|
||||
0: punctuation.definition.list.nix
|
||||
push:
|
||||
- match: '\]'
|
||||
captures:
|
||||
0: punctuation.definition.list.nix
|
||||
pop: true
|
||||
- include: expression
|
||||
list-and-cont:
|
||||
- match: '(?=\[)'
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: list
|
||||
- include: expression-cont
|
||||
operator-unary:
|
||||
- match: (!|-)
|
||||
scope: keyword.operator.unary.nix
|
||||
others:
|
||||
- include: whitespace
|
||||
- include: comment
|
||||
- include: illegal
|
||||
parameter-name:
|
||||
- match: '\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*'
|
||||
captures:
|
||||
0: variable.parameter.name.nix
|
||||
parameter-name-and-cont:
|
||||
- match: '\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*'
|
||||
captures:
|
||||
0: variable.parameter.name.nix
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: expression-cont
|
||||
parens:
|
||||
- match: \(
|
||||
captures:
|
||||
0: punctuation.definition.expression.nix
|
||||
push:
|
||||
- match: \)
|
||||
captures:
|
||||
0: punctuation.definition.expression.nix
|
||||
pop: true
|
||||
- include: expression
|
||||
parens-and-cont:
|
||||
- match: (?=\()
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: parens
|
||||
- include: expression-cont
|
||||
string:
|
||||
- match: (?=\'\')
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- match: \'\'
|
||||
captures:
|
||||
0: punctuation.definition.string.other.start.nix
|
||||
push:
|
||||
- meta_scope: string.quoted.other.nix
|
||||
- match: \'\'(?!\$|\'|\\.)
|
||||
captures:
|
||||
0: punctuation.definition.string.other.end.nix
|
||||
pop: true
|
||||
- match: \'\'(\$|\'|\\.)
|
||||
scope: constant.character.escape.nix
|
||||
- include: interpolation
|
||||
- include: expression-cont
|
||||
- match: (?=\")
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: string-quoted
|
||||
- include: expression-cont
|
||||
- match: '([a-zA-Z0-9\.\_\-\+]*(\/[a-zA-Z0-9\.\_\-\+]+)+)'
|
||||
captures:
|
||||
0: string.unquoted.path.nix
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: expression-cont
|
||||
- match: '(\<[a-zA-Z0-9\.\_\-\+]+(\/[a-zA-Z0-9\.\_\-\+]+)*\>)'
|
||||
captures:
|
||||
0: string.unquoted.spath.nix
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: expression-cont
|
||||
- match: '([a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\'']+)'
|
||||
captures:
|
||||
0: string.unquoted.url.nix
|
||||
push:
|
||||
- match: '(?=([\])};,]|\b(else|then)\b))'
|
||||
pop: true
|
||||
- include: expression-cont
|
||||
string-quoted:
|
||||
- match: \"
|
||||
captures:
|
||||
0: punctuation.definition.string.double.start.nix
|
||||
push:
|
||||
- meta_scope: string.quoted.double.nix
|
||||
- match: \"
|
||||
captures:
|
||||
0: punctuation.definition.string.double.end.nix
|
||||
pop: true
|
||||
- match: \\.
|
||||
scope: constant.character.escape.nix
|
||||
- include: interpolation
|
||||
whitespace:
|
||||
- match: \s+
|
||||
with-assert:
|
||||
- match: '(?<![\w''-])(with|assert)(?![\w''-])'
|
||||
captures:
|
||||
0: keyword.other.nix
|
||||
push:
|
||||
- match: \;
|
||||
pop: true
|
||||
- include: expression
|
||||
Submodule
+1
Submodule assets/syntaxes/Packages added at 289782ff2e
Submodule
+1
Submodule assets/syntaxes/TOML added at b5e3064e99
Submodule
+1
Submodule assets/syntaxes/VimL added at b453aff6f7
@@ -0,0 +1,91 @@
|
||||
%YAML 1.2
|
||||
---
|
||||
# http://www.sublimetext.com/docs/3/syntax.html
|
||||
name: VimL
|
||||
file_extensions:
|
||||
- vim
|
||||
- .vimrc
|
||||
scope: source.viml
|
||||
contexts:
|
||||
main:
|
||||
- include: comment
|
||||
- include: string_quoted_double
|
||||
- include: string_quoted_single
|
||||
- include: string_regex
|
||||
- include: inline_comment
|
||||
- include: number_int
|
||||
- include: number_hex
|
||||
- include: keyword
|
||||
- include: support_function
|
||||
- include: support_variable
|
||||
- include: support_type
|
||||
- include: function_params
|
||||
- include: function_definition
|
||||
- include: function_call
|
||||
- include: function
|
||||
- include: variable
|
||||
- include: expr
|
||||
comment:
|
||||
- match: ^\s*".*$
|
||||
scope: comment.line.quotes.viml
|
||||
captures:
|
||||
1: punctuation.definition.comment.vim
|
||||
expr:
|
||||
- match: (\|\||&&|==(\?|#)?|(!|>|<)=(#|\?)?|(=|!)~(#|\?)?|(>|<)(#|\?)is|isnot|\.|\*|\\|%)
|
||||
scope: storage.function.viml
|
||||
function:
|
||||
- match: \b(fu(n|nction)?|end(f|fu|fun|function)?)\b
|
||||
scope: storage.function.viml
|
||||
function_call:
|
||||
- match: '(([sgbwtl]|)?:?[0-9a-zA-Z_#]+)(?=\()'
|
||||
scope: support.function.any-method
|
||||
function_definition:
|
||||
- match: '^\s*(function)\s*!?\s+(?=(s:)?[0-9a-zA-Z_#]+\s*\()'
|
||||
captures:
|
||||
1: storage.function.viml
|
||||
push:
|
||||
- meta_scope: meta.function.viml
|
||||
- match: (\()
|
||||
captures:
|
||||
1: punctuation.definition.parameters.begin.viml
|
||||
pop: true
|
||||
- match: "(s:)?[0-9a-zA-Z_#]+"
|
||||
scope: entity.name.function.viml
|
||||
function_params:
|
||||
- match: '-\w+='
|
||||
scope: support.type.viml
|
||||
inline_comment:
|
||||
- match: '(?!\$)(")(?!\{).*$\n?'
|
||||
scope: comment.line.quotes.viml
|
||||
captures:
|
||||
1: punctuation.definition.comment.vim
|
||||
keyword:
|
||||
- match: \b(if|while|for|try|return|throw|end(if|for|while|try)?|au(g|group)|else(if|)?|do|in|catch|finally|:)\b
|
||||
scope: keyword.control.viml
|
||||
number_hex:
|
||||
- match: "#[0-9a-f]{6}"
|
||||
scope: constant.numeric.hex
|
||||
number_int:
|
||||
- match: '-?\d+'
|
||||
scope: constant.numeric.integer
|
||||
string_quoted_double:
|
||||
- match: '"(\\\\|\\"|\n[^\S\n]*\\|[^\n"])*"'
|
||||
scope: string.quoted.double.viml
|
||||
string_quoted_single:
|
||||
- match: '''(''''|\n[^\S\n]*\\|[^\n''])*'''
|
||||
scope: string.quoted.single.viml
|
||||
string_regex:
|
||||
- match: '/(\\\\|\\/|\n[^\S\n]*\\|[^\n/])*/'
|
||||
scope: string.regexp.viml
|
||||
support_function:
|
||||
- match: \b(set(local|global)?|let|command|filetype|colorscheme|\w*map|\w*a(b|brev)?|syn|exe(c|cute)?|ec(ho|)?|au(tocmd|)?)\b
|
||||
scope: support.function.viml
|
||||
support_type:
|
||||
- match: <.*?>
|
||||
scope: support.type.viml
|
||||
support_variable:
|
||||
- match: '\b(am(enu|)?|(hl|inc)?search|[Bb]uf([Nn]ew[Ff]ile|[Rr]ead)?|[Ff]ile[Tt]ype)\b'
|
||||
scope: support.variable.viml
|
||||
variable:
|
||||
- match: '([sSgGbBwWlLaAvV]:|@|$|&(?!&))\w*'
|
||||
scope: variable.other.viml
|
||||
Binary file not shown.
Submodule
+1
Submodule assets/themes/1337-Scheme added at ca6a329cfd
Submodule
+1
Submodule assets/themes/DarkNeon added at 174bd535da
Symlink
+1
@@ -0,0 +1 @@
|
||||
sublime-monokai-extended/Monokai Extended.tmTheme
|
||||
Submodule
+1
Submodule assets/themes/TwoDark added at 8e0f6fa5b5
Submodule
+1
Submodule assets/themes/github-sublime-theme added at 508740b243
Submodule
+1
Submodule assets/themes/sublime-monokai-extended added at 1658f51c15
+10
-3
@@ -11,17 +11,24 @@ pack() {
|
||||
local tempdir
|
||||
local out_dir
|
||||
local package_name
|
||||
local gcc_prefix
|
||||
|
||||
tempdir=$(mktemp -d 2>/dev/null || mktemp -d -t tmp)
|
||||
out_dir=$(pwd)
|
||||
package_name="$PROJECT_NAME-$TRAVIS_TAG-$TARGET"
|
||||
|
||||
if [[ $TARGET == "arm-unknown-linux-gnueabihf" ]]; then
|
||||
gcc_prefix="arm-linux-gnueabihf-"
|
||||
else
|
||||
gcc_prefix=""
|
||||
fi
|
||||
|
||||
# create a "staging" directory
|
||||
mkdir "$tempdir/$package_name"
|
||||
|
||||
# copying the main binary
|
||||
cp "target/$TARGET/release/$PROJECT_NAME" "$tempdir/$package_name/"
|
||||
strip "$tempdir/$package_name/$PROJECT_NAME"
|
||||
"${gcc_prefix}"strip "$tempdir/$package_name/$PROJECT_NAME"
|
||||
|
||||
# readme and license
|
||||
cp README.md "$tempdir/$package_name"
|
||||
@@ -50,8 +57,8 @@ make_deb() {
|
||||
architecture=i386
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: unknown target" >&2
|
||||
return 1
|
||||
echo "make_deb: skipping target '${TARGET}'" >&2
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
version=${TRAVIS_TAG#v}
|
||||
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
if [ "$TRAVIS_OS_NAME" != linux ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
sudo apt-get update
|
||||
|
||||
# needed for musl targets
|
||||
sudo apt-get install -y musl-tools
|
||||
|
||||
# needed to build deb packages
|
||||
sudo apt-get install -y fakeroot
|
||||
|
||||
# needed for i686 linux gnu target
|
||||
if [[ $TARGET == i686-unknown-linux-gnu ]]; then
|
||||
sudo apt-get install -y gcc-multilib
|
||||
fi
|
||||
|
||||
# needed for cross-compiling for arm
|
||||
if [[ $TARGET == arm-unknown-linux-gnueabihf ]]; then
|
||||
sudo apt-get install -y \
|
||||
gcc-4.8-arm-linux-gnueabihf \
|
||||
binutils-arm-linux-gnueabihf \
|
||||
libc6-armhf-cross \
|
||||
libc6-dev-armhf-cross
|
||||
fi
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
# Incorporate TARGET env var to the build and test process
|
||||
cargo build --target "$TARGET" --verbose
|
||||
|
||||
# We cannot run arm executables on linux
|
||||
if [[ $TARGET != arm-unknown-linux-gnueabihf ]]; then
|
||||
cargo test --target "$TARGET" --verbose
|
||||
|
||||
# Run 'bat' on its own source code and the README
|
||||
cargo run --target "$TARGET" -- src/main.rs README.md --paging=never
|
||||
fi
|
||||
@@ -0,0 +1,20 @@
|
||||
# Alternatives
|
||||
|
||||
The following table tries to give an overview *from `bat`s perspective*, i.e. we only compare
|
||||
categories which are relevant for `bat`. Some of these projects have completely different goals and
|
||||
if you are not looking for a program like `bat`, this comparison might not be for you.
|
||||
|
||||
| | bat | [pygments](http://pygments.org/) | [highlight](http://www.andre-simon.de/doku/highlight/highlight.php) | [ccat](https://github.com/jingweno/ccat) | [source-highlight](https://www.gnu.org/software/src-highlite/) | [hicat](https://github.com/rstacruz/hicat) | [coderay](https://github.com/rubychan/coderay) | [rouge](https://github.com/jneen/rouge) |
|
||||
|----------------------------------------------|---------------------------------------------------------------------|----------------------------------|---------------------------------------------------------------------|------------------------------------------|----------------------------------------------------------------|-----------------------------------------------------|-----------------------------------------------------|-----------------------------------------------------|
|
||||
| Drop-in `cat` replacement | (:heavy_check_mark:) [*](https://github.com/sharkdp/bat/issues/134) | :x: | :x: | (:heavy_check_mark:) | :x: | :x: [*](https://github.com/rstacruz/hicat/issues/6) | :x: | :x: |
|
||||
| Git integration | :heavy_check_mark: | :x: | :x: | :x: | :x: | :x: | :x: | :x: |
|
||||
| Automatic paging | :heavy_check_mark: | :x: | :x: | :x: | :x: | :heavy_check_mark: | :x: | :x: |
|
||||
| Languages (circa) | 60 | 300 | 200 | 7 | 80 | 130 | 30 | 130 |
|
||||
| Extensible (languages, themes) | :heavy_check_mark: | (:heavy_check_mark:) | (:heavy_check_mark:) | :x: | (:heavy_check_mark:) | :x: | :x: | :x: |
|
||||
| Advanced highlighting (e.g. nested syntaxes) | :heavy_check_mark: | :heavy_check_mark: | (:heavy_check_mark:) ? | :x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Execution time [ms] (`jquery-3.3.1.js`) | 624 | 789 | 400 | 80 | 300 | 316 | 157 | 695 |
|
||||
| Execution time [ms] (`miniz.c`) | 66 | 656 | 26 | 8 | 53 | 141 | 75 | 254 |
|
||||
| Execution time [ms] (370 kB XML file) | 238 | 487 | 129 | 111 | 110 | 339 | 147 | 359 |
|
||||
|
||||
If you think that some entries in this table are outdated or wrong, please open a ticket or pull
|
||||
request.
|
||||
@@ -0,0 +1,326 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="1000"
|
||||
height="286.64285"
|
||||
viewBox="0 0 1000 286.64285"
|
||||
version="1.1"
|
||||
xml:space="preserve"
|
||||
style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41420996"
|
||||
id="svg1285"
|
||||
sodipodi:docname="logo-header.svg"
|
||||
inkscape:version="0.92.2 2405546, 2018-03-11"><metadata
|
||||
id="metadata1289"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1175"
|
||||
id="namedview1287"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="0.82573726"
|
||||
inkscape:cx="438.92362"
|
||||
inkscape:cy="320.87266"
|
||||
inkscape:window-x="1920"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg1285"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true" />
|
||||
<rect
|
||||
style="opacity:1;fill:#f2f2f2;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.14294769px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
|
||||
id="rect1929"
|
||||
width="1000"
|
||||
height="286.64285"
|
||||
x="0"
|
||||
y="0" /><g
|
||||
id="g929"
|
||||
transform="translate(0,13.321425)"><g
|
||||
transform="matrix(5.1207879,0,0,4.741146,45.483811,-7057.152)"
|
||||
id="g1168">
|
||||
<path
|
||||
d="m 77.792,1533.05 c 0,0.32 -0.064,0.61 -0.192,0.88 -0.128,0.27 -0.3,0.5 -0.516,0.7 -0.216,0.19 -0.468,0.34 -0.756,0.45 -0.288,0.11 -0.596,0.16 -0.924,0.16 h -4.02 v -8.52 h 4.104 c 0.296,0 0.564,0.07 0.804,0.19 0.24,0.13 0.444,0.3 0.612,0.5 0.168,0.21 0.298,0.44 0.39,0.7 0.092,0.26 0.138,0.52 0.138,0.78 0,0.44 -0.108,0.83 -0.324,1.19 -0.216,0.35 -0.516,0.62 -0.9,0.8 0.488,0.14 0.874,0.41 1.158,0.8 0.284,0.39 0.426,0.85 0.426,1.37 z m -0.84,-0.15 c 0,-0.21 -0.036,-0.41 -0.108,-0.6 -0.072,-0.2 -0.174,-0.37 -0.306,-0.52 -0.132,-0.15 -0.288,-0.27 -0.468,-0.36 -0.18,-0.09 -0.374,-0.14 -0.582,-0.14 h -3.264 v 3.23 h 3.18 c 0.216,0 0.418,-0.04 0.606,-0.13 0.188,-0.09 0.352,-0.21 0.492,-0.35 0.14,-0.15 0.25,-0.33 0.33,-0.52 0.08,-0.2 0.12,-0.4 0.12,-0.61 z m -4.728,-5.45 v 3.15 h 2.928 c 0.208,0 0.402,-0.04 0.582,-0.13 0.18,-0.09 0.334,-0.21 0.462,-0.35 0.128,-0.14 0.228,-0.31 0.3,-0.5 0.072,-0.19 0.108,-0.38 0.108,-0.59 0,-0.22 -0.034,-0.42 -0.102,-0.62 -0.068,-0.19 -0.164,-0.35 -0.288,-0.49 -0.124,-0.14 -0.27,-0.26 -0.438,-0.34 -0.168,-0.08 -0.352,-0.13 -0.552,-0.13 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1162"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 88.436,1526.72 h 0.708 l 3.54,8.52 h -0.888 l -1.104,-2.66 h -3.828 l -1.092,2.66 h -0.9 z m 2.064,5.2 -1.716,-4.21 -1.74,4.21 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1164"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 105.716,1527.47 h -3.036 v 7.77 h -0.84 v -7.77 h -3.036 v -0.75 h 6.912 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1166"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g><g
|
||||
transform="translate(1.9578e-5)"
|
||||
id="g6103"><g
|
||||
id="g1172"
|
||||
transform="matrix(0.31374232,0,0,0.31374232,-949.56013,-170.6326)">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path1170"
|
||||
style="fill:url(#_Linear1)"
|
||||
d="m 4399.57,773.907 c 0,0 35.33,20.718 42.25,24.779 13.1,7.683 47.96,27.832 64.24,37.232 1.77,1.027 2.87,2.923 2.87,4.975 0.01,2.053 -1.08,3.952 -2.85,4.986 -27.1,15.803 -110.2,64.254 -128.26,74.788 -2.16,1.254 -4.82,1.237 -6.95,-0.046 -13.57,-8.147 -62.93,-37.791 -75.02,-44.875 -16.93,-9.911 -37.27,-21.84 -37.27,-21.84 z" />
|
||||
</g><g
|
||||
id="g1176"
|
||||
transform="matrix(0.31374232,0,0,0.31374232,-949.56013,-170.6326)">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path1174"
|
||||
style="fill:#31369e"
|
||||
d="m 4269.48,860.305 c -54.5,-31.918 -88.34,-51.732 -117.18,-68.621 -3.98,-2.335 -6.43,-6.608 -6.44,-11.228 0,-4.619 2.45,-8.893 6.44,-11.229 l 319.41,-187.171 c 2.02,-1.186 4.52,-1.198 6.56,-0.032 2.03,1.166 3.29,3.333 3.29,5.678 0,29.472 0,107.932 0,138.732 0,7.842 -4.16,15.094 -10.94,19.049 -13.32,7.783 -54.43,31.465 -67.73,39.233 0,0 -133.3,75.654 -133.41,75.589 z" />
|
||||
</g><g
|
||||
id="g1180"
|
||||
transform="matrix(0.31374232,0,0,0.31374232,-949.56013,-170.6326)">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path1178"
|
||||
style="fill:#1d2167"
|
||||
d="m 4604.42,705.593 85.54,34.367 v -63.802 c 0,-2.628 -1.37,-5.066 -3.62,-6.431 -2.24,-1.365 -5.04,-1.459 -7.37,-0.247 -5.93,3.078 -74.55,36.113 -74.55,36.113 z" />
|
||||
</g><g
|
||||
id="g1184"
|
||||
transform="matrix(0.31374232,0,0,0.31374232,-949.56013,-170.6326)">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path1182"
|
||||
style="fill:url(#_Linear2)"
|
||||
d="m 4976.91,704.019 c 0,0 -8.53,-4.983 -8.56,-5 -79.92,-46.83 -170.16,-99.71 -199.6,-116.963 -2.02,-1.186 -4.52,-1.198 -6.56,-0.032 -2.04,1.166 -3.29,3.333 -3.29,5.678 0,29.472 0,107.932 0,138.732 0,7.842 4.16,15.094 10.93,19.049 13.33,7.783 68.33,39.913 68.33,39.913 z" />
|
||||
</g><g
|
||||
id="g1188"
|
||||
transform="matrix(0.31374232,0,0,0.31374232,-949.56013,-170.6326)">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path1186"
|
||||
style="fill:url(#_Linear3)"
|
||||
d="m 4968.44,699.073 c 0,0 -235.66,138.235 -263.83,154.762 -3.01,1.764 -6.73,1.783 -9.76,0.05 -3.02,-1.733 -4.89,-4.953 -4.89,-8.44 V 731.796 c 0,0 -111.16,-50.383 -128.47,-59.371 -2.33,-1.212 -4.9781,-0.84921 -7.37,0.247 -1.9592,0.89788 -3.62,3.803 -3.62,6.431 v 166.342 c 0,3.487 0.43,15.045 -12.02,22.226 -2.41,1.392 -130.21,75.278 -130.21,75.278 l 211.99,122.751 c 0,0 377.71,-221.201 467.9,-274.016 3.98,-2.335 6.43,-6.608 6.43,-11.228 0,-4.619 -2.45,-8.893 -6.43,-11.229 -24.64,-14.438 -119.72,-70.154 -119.72,-70.154"
|
||||
sodipodi:nodetypes="cccsccsssccccscc" />
|
||||
</g></g><g
|
||||
transform="matrix(0.18627124,0,0,0.22786575,340.43532,-286.36087)"
|
||||
id="g1262">
|
||||
<g
|
||||
transform="matrix(1.28,0,0,1,-97.8353,0)"
|
||||
id="g1192">
|
||||
<path
|
||||
d="m 374.487,2270.67 h 4.788 l 23.94,57.61 h -6.006 l -7.465,-18.01 h -25.887 l -7.385,18.01 h -6.086 z m 13.958,35.14 -11.604,-28.49 -11.767,28.49 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1190"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.28,0,0,1,-97.8353,0)"
|
||||
id="g1196">
|
||||
<path
|
||||
d="m 421.293,2307.02 c 0,-2.97 0.514,-5.78 1.542,-8.44 1.028,-2.65 2.475,-4.96 4.342,-6.94 1.866,-1.97 4.098,-3.52 6.695,-4.66 2.596,-1.14 5.464,-1.7 8.602,-1.7 4.003,0 7.492,0.9 10.468,2.71 2.975,1.82 5.221,4.26 6.735,7.35 l -5.356,1.7 c -1.19,-2.16 -2.853,-3.85 -4.99,-5.07 -2.137,-1.22 -4.504,-1.83 -7.101,-1.83 -2.164,0 -4.179,0.44 -6.046,1.3 -1.866,0.87 -3.489,2.05 -4.869,3.53 -1.379,1.49 -2.461,3.26 -3.246,5.32 -0.784,2.05 -1.176,4.3 -1.176,6.73 0,2.38 0.405,4.63 1.217,6.74 0.811,2.11 1.92,3.94 3.327,5.48 1.407,1.54 3.043,2.76 4.91,3.65 1.866,0.89 3.854,1.34 5.964,1.34 1.353,0 2.692,-0.19 4.017,-0.57 1.326,-0.38 2.556,-0.91 3.692,-1.58 1.137,-0.68 2.11,-1.45 2.922,-2.32 0.811,-0.86 1.379,-1.78 1.704,-2.75 l 5.437,1.62 c -0.595,1.51 -1.461,2.91 -2.597,4.18 -1.136,1.27 -2.475,2.38 -4.017,3.32 -1.541,0.95 -3.246,1.68 -5.112,2.2 -1.867,0.51 -3.801,0.77 -5.802,0.77 -3.084,0 -5.924,-0.59 -8.521,-1.75 -2.597,-1.16 -4.842,-2.76 -6.736,-4.79 -1.893,-2.03 -3.367,-4.36 -4.422,-7.02 -1.055,-2.65 -1.583,-5.49 -1.583,-8.52 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1194"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.28,0,0,1,-97.8353,0)"
|
||||
id="g1200">
|
||||
<path
|
||||
d="m 476.962,2329.1 c -2.001,0 -3.854,-0.34 -5.558,-1.02 -1.705,-0.67 -3.192,-1.61 -4.464,-2.8 -1.271,-1.19 -2.258,-2.58 -2.962,-4.18 -0.703,-1.59 -1.055,-3.31 -1.055,-5.15 0,-1.84 0.433,-3.54 1.299,-5.11 0.865,-1.57 2.083,-2.92 3.652,-4.06 1.568,-1.14 3.435,-2.02 5.599,-2.64 2.164,-0.62 4.544,-0.93 7.141,-0.93 2.164,0 4.355,0.19 6.573,0.57 2.218,0.38 4.193,0.92 5.924,1.62 v -3.81 c 0,-3.74 -1.055,-6.7 -3.165,-8.89 -2.11,-2.19 -5.031,-3.29 -8.764,-3.29 -2.164,0 -4.382,0.44 -6.654,1.3 -2.272,0.87 -4.572,2.11 -6.898,3.74 l -1.948,-3.66 c 5.465,-3.67 10.766,-5.51 15.906,-5.51 5.302,0 9.467,1.48 12.497,4.46 3.03,2.97 4.544,7.09 4.544,12.33 v 19.07 c 0,1.52 0.677,2.28 2.029,2.28 v 4.86 c -0.919,0.17 -1.623,0.25 -2.11,0.25 -1.406,0 -2.502,-0.35 -3.286,-1.06 -0.785,-0.7 -1.204,-1.67 -1.258,-2.92 l -0.162,-3.33 c -1.948,2.55 -4.396,4.5 -7.345,5.85 -2.948,1.35 -6.126,2.03 -9.535,2.03 z m 1.299,-4.22 c 2.867,0 5.504,-0.54 7.912,-1.63 2.407,-1.08 4.233,-2.51 5.478,-4.3 0.486,-0.48 0.852,-1.01 1.095,-1.58 0.244,-0.57 0.365,-1.09 0.365,-1.58 v -6.9 c -1.839,-0.7 -3.746,-1.24 -5.721,-1.62 -1.975,-0.38 -3.99,-0.57 -6.046,-0.57 -3.895,0 -7.06,0.81 -9.494,2.43 -2.435,1.63 -3.652,3.76 -3.652,6.41 0,1.3 0.257,2.52 0.771,3.66 0.514,1.13 1.217,2.12 2.11,2.96 0.893,0.84 1.961,1.5 3.205,1.99 1.245,0.48 2.57,0.73 3.977,0.73 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1198"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.28,0,0,1,-97.8353,0)"
|
||||
id="g1204">
|
||||
<path
|
||||
d="m 528.158,2326.26 c -0.325,0.16 -0.798,0.38 -1.42,0.65 -0.622,0.27 -1.339,0.54 -2.151,0.81 -0.811,0.27 -1.731,0.51 -2.759,0.73 -1.028,0.21 -2.11,0.32 -3.246,0.32 -1.19,0 -2.326,-0.16 -3.408,-0.49 -1.082,-0.32 -2.029,-0.82 -2.84,-1.5 -0.812,-0.67 -1.461,-1.52 -1.948,-2.55 -0.487,-1.03 -0.73,-2.22 -0.73,-3.57 v -30.27 h -5.843 v -4.38 h 5.843 v -14.29 h 5.518 v 14.29 h 9.738 v 4.38 h -9.738 v 28.73 c 0.108,1.56 0.663,2.71 1.664,3.44 1,0.73 2.15,1.1 3.448,1.1 1.569,0 2.949,-0.26 4.139,-0.77 1.19,-0.52 1.948,-0.88 2.272,-1.1 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1202"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.28,0,0,1,-97.8353,0)"
|
||||
id="g1208">
|
||||
<path
|
||||
d="m 547.959,2307.02 c 0,-2.97 0.514,-5.78 1.542,-8.44 1.027,-2.65 2.475,-4.96 4.341,-6.94 1.866,-1.97 4.098,-3.52 6.695,-4.66 2.597,-1.14 5.464,-1.7 8.602,-1.7 4.003,0 7.493,0.9 10.468,2.71 2.976,1.82 5.221,4.26 6.736,7.35 l -5.356,1.7 c -1.19,-2.16 -2.854,-3.85 -4.991,-5.07 -2.137,-1.22 -4.504,-1.83 -7.101,-1.83 -2.164,0 -4.179,0.44 -6.045,1.3 -1.867,0.87 -3.49,2.05 -4.869,3.53 -1.38,1.49 -2.462,3.26 -3.246,5.32 -0.785,2.05 -1.177,4.3 -1.177,6.73 0,2.38 0.406,4.63 1.217,6.74 0.812,2.11 1.921,3.94 3.327,5.48 1.407,1.54 3.044,2.76 4.91,3.65 1.866,0.89 3.855,1.34 5.965,1.34 1.352,0 2.691,-0.19 4.016,-0.57 1.326,-0.38 2.557,-0.91 3.693,-1.58 1.136,-0.68 2.11,-1.45 2.921,-2.32 0.812,-0.86 1.38,-1.78 1.704,-2.75 l 5.437,1.62 c -0.595,1.51 -1.46,2.91 -2.596,4.18 -1.136,1.27 -2.475,2.38 -4.017,3.32 -1.542,0.95 -3.246,1.68 -5.113,2.2 -1.866,0.51 -3.8,0.77 -5.802,0.77 -3.084,0 -5.924,-0.59 -8.521,-1.75 -2.597,-1.16 -4.842,-2.76 -6.735,-4.79 -1.894,-2.03 -3.368,-4.36 -4.423,-7.02 -1.055,-2.65 -1.582,-5.49 -1.582,-8.52 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1206"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.28,0,0,1,-97.8353,0)"
|
||||
id="g1212">
|
||||
<path
|
||||
d="m 593.24,2269.05 h 5.519 v 49.66 c 0,1.57 0.432,2.8 1.298,3.69 0.866,0.89 2.083,1.34 3.652,1.34 0.595,0 1.298,-0.08 2.11,-0.24 0.811,-0.16 1.569,-0.38 2.272,-0.65 l 0.974,4.46 c -1.028,0.43 -2.286,0.79 -3.774,1.06 -1.487,0.27 -2.772,0.4 -3.854,0.4 -2.543,0 -4.545,-0.72 -6.005,-2.15 -1.461,-1.43 -2.192,-3.45 -2.192,-6.04 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1210"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.28,0,0,1,-97.8353,0)"
|
||||
id="g1216">
|
||||
<path
|
||||
d="m 630.651,2329.1 c -3.084,0 -5.897,-0.59 -8.44,-1.75 -2.543,-1.16 -4.747,-2.74 -6.614,-4.75 -1.866,-2 -3.313,-4.32 -4.341,-6.97 -1.028,-2.66 -1.542,-5.44 -1.542,-8.36 0,-2.98 0.527,-5.79 1.582,-8.44 1.055,-2.65 2.516,-4.98 4.383,-6.98 1.866,-2 4.071,-3.6 6.613,-4.79 2.543,-1.19 5.329,-1.78 8.359,-1.78 3.029,0 5.829,0.59 8.399,1.78 2.57,1.19 4.788,2.79 6.654,4.79 1.867,2 3.327,4.33 4.382,6.98 1.055,2.65 1.583,5.46 1.583,8.44 0,2.92 -0.514,5.7 -1.542,8.36 -1.028,2.65 -2.475,4.97 -4.342,6.97 -1.866,2.01 -4.084,3.59 -6.654,4.75 -2.57,1.16 -5.397,1.75 -8.48,1.75 z m -15.338,-21.75 c 0,2.38 0.406,4.58 1.218,6.61 0.811,2.03 1.907,3.82 3.286,5.36 1.38,1.54 3.003,2.74 4.869,3.61 1.867,0.86 3.855,1.3 5.965,1.3 2.11,0 4.098,-0.45 5.964,-1.34 1.867,-0.89 3.503,-2.11 4.91,-3.65 1.406,-1.54 2.516,-3.34 3.327,-5.4 0.812,-2.06 1.217,-4.27 1.217,-6.65 0,-2.33 -0.405,-4.53 -1.217,-6.62 -0.811,-2.08 -1.921,-3.89 -3.327,-5.43 -1.407,-1.55 -3.043,-2.76 -4.91,-3.66 -1.866,-0.89 -3.854,-1.34 -5.964,-1.34 -2.11,0 -4.085,0.45 -5.924,1.34 -1.84,0.9 -3.463,2.13 -4.869,3.7 -1.407,1.56 -2.516,3.39 -3.327,5.47 -0.812,2.09 -1.218,4.32 -1.218,6.7 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1214"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.28,0,0,1,-97.8353,0)"
|
||||
id="g1220">
|
||||
<path
|
||||
d="m 694.029,2328.28 h -5.518 v -23.61 c 0,-5.03 -0.744,-8.68 -2.232,-10.95 -1.488,-2.28 -3.773,-3.41 -6.857,-3.41 -1.623,0 -3.246,0.29 -4.869,0.89 -1.623,0.59 -3.124,1.43 -4.504,2.52 -1.379,1.08 -2.61,2.36 -3.692,3.85 -1.082,1.49 -1.867,3.1 -2.354,4.83 v 25.88 h -5.518 v -42.27 h 5.032 v 9.57 c 1.731,-3.08 4.206,-5.57 7.425,-7.46 3.219,-1.9 6.722,-2.84 10.509,-2.84 2.326,0 4.301,0.43 5.924,1.29 1.623,0.87 2.921,2.09 3.895,3.65 0.974,1.57 1.677,3.5 2.11,5.77 0.433,2.27 0.649,4.79 0.649,7.54 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1218"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.28,0,0,1,-97.8353,0)"
|
||||
id="g1224">
|
||||
<path
|
||||
d="m 721.62,2329.1 c -3.084,0 -5.91,-0.59 -8.48,-1.75 -2.57,-1.16 -4.801,-2.76 -6.695,-4.79 -1.893,-2.03 -3.368,-4.36 -4.423,-7.02 -1.055,-2.65 -1.582,-5.49 -1.582,-8.52 0,-2.97 0.527,-5.77 1.582,-8.4 1.055,-2.62 2.516,-4.92 4.382,-6.89 1.867,-1.98 4.099,-3.55 6.695,-4.71 2.597,-1.16 5.41,-1.74 8.44,-1.74 3.084,0 5.91,0.58 8.48,1.74 2.57,1.16 4.775,2.74 6.614,4.75 1.839,2 3.273,4.3 4.301,6.89 1.028,2.6 1.542,5.36 1.542,8.28 0,0.44 0,0.87 0,1.3 0,0.43 -0.027,0.73 -0.081,0.89 h -36.193 c 0.162,2.28 0.689,4.37 1.582,6.29 0.893,1.92 2.042,3.59 3.449,4.99 1.407,1.41 3.016,2.5 4.828,3.29 1.813,0.78 3.747,1.18 5.803,1.18 1.352,0 2.705,-0.19 4.057,-0.57 1.353,-0.38 2.597,-0.89 3.733,-1.54 1.136,-0.65 2.164,-1.45 3.084,-2.4 0.919,-0.94 1.623,-1.99 2.11,-3.12 l 4.787,1.3 c -0.649,1.57 -1.568,2.99 -2.759,4.26 -1.19,1.27 -2.569,2.38 -4.138,3.32 -1.569,0.95 -3.3,1.68 -5.194,2.2 -1.893,0.51 -3.868,0.77 -5.924,0.77 z m 15.581,-24.19 c -0.162,-2.27 -0.69,-4.34 -1.582,-6.21 -0.893,-1.86 -2.029,-3.47 -3.409,-4.82 -1.379,-1.35 -2.975,-2.41 -4.788,-3.17 -1.812,-0.76 -3.746,-1.13 -5.802,-1.13 -2.056,0 -4.003,0.37 -5.843,1.13 -1.839,0.76 -3.449,1.82 -4.828,3.17 -1.38,1.35 -2.489,2.97 -3.327,4.87 -0.839,1.89 -1.339,3.95 -1.502,6.16 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1222"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.28,0,0,1,-97.8353,0)"
|
||||
id="g1228">
|
||||
<path
|
||||
d="m 818.344,2286.01 h 5.437 l -18.178,42.27 h -4.544 l -9.089,-21.58 -9.008,21.58 h -4.544 l -18.178,-42.27 h 5.356 l 15.418,36.59 8.034,-19.31 -7.222,-17.2 h 4.626 l 5.518,13.79 5.518,-13.79 h 4.707 l -7.223,17.2 8.034,19.31 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1226"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.28,0,0,1,-97.8353,0)"
|
||||
id="g1232">
|
||||
<path
|
||||
d="m 828.65,2328.28 v -42.27 h 5.518 v 42.27 z m 0,-51.12 v -8.11 h 5.518 v 8.11 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1230"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.28,0,0,1,-97.8353,0)"
|
||||
id="g1236">
|
||||
<path
|
||||
d="m 864.193,2326.26 c -0.324,0.16 -0.798,0.38 -1.42,0.65 -0.622,0.27 -1.339,0.54 -2.15,0.81 -0.812,0.27 -1.731,0.51 -2.759,0.73 -1.028,0.21 -2.11,0.32 -3.246,0.32 -1.19,0 -2.327,-0.16 -3.409,-0.49 -1.082,-0.32 -2.028,-0.82 -2.84,-1.5 -0.811,-0.67 -1.461,-1.52 -1.947,-2.55 -0.487,-1.03 -0.731,-2.22 -0.731,-3.57 v -30.27 h -5.843 v -4.38 h 5.843 v -14.29 h 5.518 v 14.29 h 9.738 v 4.38 h -9.738 v 28.73 c 0.109,1.56 0.663,2.71 1.664,3.44 1.001,0.73 2.15,1.1 3.449,1.1 1.569,0 2.948,-0.26 4.139,-0.77 1.19,-0.52 1.947,-0.88 2.272,-1.1 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1234"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.28,0,0,1,-97.8353,0)"
|
||||
id="g1240">
|
||||
<path
|
||||
d="m 904.201,2328.28 h -5.519 v -23.61 c 0,-4.81 -0.825,-8.41 -2.475,-10.79 -1.65,-2.38 -4.071,-3.57 -7.263,-3.57 -1.569,0 -3.124,0.31 -4.666,0.93 -1.542,0.62 -2.989,1.47 -4.341,2.56 -1.353,1.08 -2.53,2.35 -3.53,3.81 -1.001,1.46 -1.745,3.06 -2.232,4.79 v 25.88 h -5.518 v -59.23 h 5.518 v 26.53 c 1.677,-3.19 4.003,-5.71 6.979,-7.55 2.975,-1.83 6.194,-2.75 9.657,-2.75 2.434,0 4.49,0.44 6.167,1.33 1.677,0.9 3.057,2.14 4.139,3.74 1.082,1.59 1.866,3.51 2.353,5.76 0.487,2.24 0.731,4.72 0.731,7.42 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1238"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.28,0,0,1,-97.8353,0)"
|
||||
id="g1244">
|
||||
<path
|
||||
d="m 983.314,2286.01 h 5.437 l -18.177,42.27 h -4.545 l -9.088,-21.58 -9.008,21.58 h -4.545 l -18.177,-42.27 h 5.356 l 15.418,36.59 8.034,-19.31 -7.222,-17.2 h 4.625 l 5.519,13.79 5.518,-13.79 h 4.707 l -7.223,17.2 8.034,19.31 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1242"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.28,0,0,1,-97.8353,0)"
|
||||
id="g1248">
|
||||
<path
|
||||
d="m 993.62,2328.28 v -42.27 h 5.519 v 42.27 z m 0,-51.12 v -8.11 h 5.519 v 8.11 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1246"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.28,0,0,1,-97.8353,0)"
|
||||
id="g1252">
|
||||
<path
|
||||
d="m 1044.42,2328.28 h -5.52 v -23.61 c 0,-5.03 -0.74,-8.68 -2.23,-10.95 -1.49,-2.28 -3.77,-3.41 -6.86,-3.41 -1.62,0 -3.24,0.29 -4.87,0.89 -1.62,0.59 -3.12,1.43 -4.5,2.52 -1.38,1.08 -2.61,2.36 -3.69,3.85 -1.08,1.49 -1.87,3.1 -2.36,4.83 v 25.88 h -5.51 v -42.27 h 5.03 v 9.57 c 1.73,-3.08 4.2,-5.57 7.42,-7.46 3.22,-1.9 6.73,-2.84 10.51,-2.84 2.33,0 4.3,0.43 5.93,1.29 1.62,0.87 2.92,2.09 3.89,3.65 0.97,1.57 1.68,3.5 2.11,5.77 0.43,2.27 0.65,4.79 0.65,7.54 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1250"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.28,0,0,1,-97.8353,0)"
|
||||
id="g1256">
|
||||
<path
|
||||
d="m 1070.71,2328.85 c -2.92,0 -5.58,-0.61 -7.99,-1.82 -2.41,-1.22 -4.49,-2.83 -6.25,-4.83 -1.76,-2 -3.12,-4.3 -4.1,-6.9 -0.97,-2.6 -1.46,-5.27 -1.46,-8.03 0,-2.92 0.48,-5.72 1.42,-8.4 0.95,-2.68 2.29,-5.02 4.02,-7.02 1.73,-2 3.8,-3.6 6.21,-4.79 2.4,-1.19 5.07,-1.78 7.99,-1.78 3.63,0 6.76,0.9 9.41,2.71 2.66,1.82 4.93,4.1 6.82,6.86 v -8.84 h 4.95 v 42.44 c 0,2.92 -0.55,5.5 -1.66,7.75 -1.11,2.24 -2.6,4.12 -4.47,5.64 -1.86,1.51 -4.05,2.65 -6.57,3.4 -2.51,0.76 -5.18,1.14 -7.99,1.14 -4.87,0 -8.79,-0.86 -11.77,-2.6 -2.97,-1.73 -5.38,-4.05 -7.22,-6.97 l 3.41,-2.68 c 1.67,2.7 3.89,4.69 6.65,5.96 2.76,1.27 5.74,1.91 8.93,1.91 2.05,0 4,-0.28 5.84,-0.85 1.84,-0.57 3.45,-1.41 4.83,-2.52 1.38,-1.11 2.47,-2.51 3.29,-4.22 0.81,-1.7 1.21,-3.69 1.21,-5.96 v -8.36 c -1.62,2.7 -3.84,4.84 -6.65,6.41 -2.81,1.57 -5.76,2.35 -8.85,2.35 z m 1.55,-4.62 c 1.62,0 3.21,-0.3 4.78,-0.89 1.57,-0.6 2.99,-1.4 4.26,-2.4 1.28,-1 2.34,-2.12 3.21,-3.37 0.86,-1.24 1.43,-2.51 1.7,-3.81 v -13.07 c -0.59,-1.51 -1.4,-2.92 -2.43,-4.22 -1.03,-1.29 -2.19,-2.4 -3.49,-3.32 -1.3,-0.92 -2.69,-1.65 -4.18,-2.19 -1.49,-0.55 -2.99,-0.82 -4.5,-0.82 -2.39,0 -4.51,0.5 -6.37,1.5 -1.87,1.01 -3.45,2.32 -4.75,3.94 -1.3,1.62 -2.29,3.46 -2.96,5.52 -0.68,2.06 -1.02,4.14 -1.02,6.25 0,2.27 0.41,4.43 1.22,6.49 0.81,2.06 1.93,3.86 3.37,5.4 1.43,1.54 3.09,2.76 4.99,3.65 1.89,0.89 3.95,1.34 6.17,1.34 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1254"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.28,0,0,1,-97.8353,0)"
|
||||
id="g1260">
|
||||
<path
|
||||
d="m 1115.51,2329.1 c -3.3,0 -6.52,-0.55 -9.66,-1.63 -3.14,-1.08 -5.84,-2.65 -8.11,-4.7 l 2.51,-3.74 c 2.38,1.95 4.8,3.41 7.27,4.39 2.46,0.97 5.09,1.46 7.91,1.46 3.41,0 6.12,-0.69 8.15,-2.07 2.03,-1.38 3.05,-3.34 3.05,-5.89 0,-1.19 -0.27,-2.17 -0.82,-2.96 -0.54,-0.78 -1.33,-1.47 -2.39,-2.07 -1.05,-0.59 -2.38,-1.12 -3.98,-1.58 -1.59,-0.46 -3.44,-0.93 -5.56,-1.42 -2.43,-0.59 -4.54,-1.19 -6.32,-1.78 -1.79,-0.6 -3.26,-1.28 -4.43,-2.03 -1.16,-0.76 -2.03,-1.69 -2.59,-2.8 -0.57,-1.11 -0.86,-2.51 -0.86,-4.18 0,-2.11 0.42,-3.98 1.26,-5.6 0.84,-1.63 1.98,-2.96 3.41,-4.02 1.43,-1.05 3.11,-1.85 5.03,-2.39 1.92,-0.54 3.96,-0.81 6.13,-0.81 3.24,0 6.19,0.52 8.84,1.58 2.65,1.05 4.79,2.45 6.41,4.18 l -2.67,3.24 c -1.57,-1.62 -3.48,-2.84 -5.72,-3.65 -2.25,-0.81 -4.59,-1.22 -7.02,-1.22 -1.47,0 -2.82,0.15 -4.06,0.45 -1.25,0.3 -2.34,0.77 -3.29,1.42 -0.94,0.65 -1.69,1.47 -2.23,2.48 -0.54,1 -0.81,2.17 -0.81,3.52 0,1.14 0.19,2.06 0.57,2.76 0.37,0.71 1,1.32 1.86,1.83 0.87,0.51 1.96,0.97 3.29,1.38 1.33,0.41 2.94,0.83 4.83,1.26 2.7,0.65 5.08,1.3 7.14,1.94 2.05,0.66 3.77,1.41 5.15,2.28 1.38,0.86 2.42,1.92 3.13,3.16 0.7,1.25 1.05,2.76 1.05,4.55 0,3.89 -1.49,6.98 -4.46,9.25 -2.98,2.27 -6.98,3.41 -12.01,3.41 z"
|
||||
style="fill:#1d2167;fill-rule:nonzero"
|
||||
id="path1258"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g></g>
|
||||
<defs
|
||||
id="defs1283"><linearGradient
|
||||
id="_Linear1"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(-158.501,-265.604,265.604,-158.501,4436.06,964.047)"><stop
|
||||
offset="0"
|
||||
style="stop-color:rgb(49,54,158);stop-opacity:1"
|
||||
id="stop1266" /><stop
|
||||
offset="0.52"
|
||||
style="stop-color:rgb(38,42,122);stop-opacity:1"
|
||||
id="stop1268" /><stop
|
||||
offset="1"
|
||||
style="stop-color:black;stop-opacity:1"
|
||||
id="stop1270" /></linearGradient><linearGradient
|
||||
id="_Linear2"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(160.563,96.0376,-96.0376,160.563,4771.78,656.669)"><stop
|
||||
offset="0"
|
||||
style="stop-color:rgb(49,54,158);stop-opacity:1"
|
||||
id="stop1273" /><stop
|
||||
offset="1"
|
||||
style="stop-color:rgb(29,33,103);stop-opacity:1"
|
||||
id="stop1275" /></linearGradient><linearGradient
|
||||
id="_Linear3"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(76.53,136.554,-136.554,76.53,4737.26,847.244)"><stop
|
||||
offset="0"
|
||||
style="stop-color:rgb(49,54,158);stop-opacity:1"
|
||||
id="stop1278" /><stop
|
||||
offset="1"
|
||||
style="stop-color:rgb(29,33,103);stop-opacity:1"
|
||||
id="stop1280" /></linearGradient></defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 23 KiB |
+403
@@ -0,0 +1,403 @@
|
||||
use atty::{self, Stream};
|
||||
use clap::{App as ClapApp, AppSettings, Arg, ArgGroup, ArgMatches, SubCommand};
|
||||
use console::Term;
|
||||
use errors::*;
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
use style::{OutputComponent, OutputComponents, OutputWrap};
|
||||
|
||||
#[cfg(windows)]
|
||||
use ansi_term;
|
||||
|
||||
pub struct App {
|
||||
pub matches: ArgMatches<'static>,
|
||||
interactive_output: bool,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new() -> Self {
|
||||
let interactive_output = atty::is(Stream::Stdout);
|
||||
|
||||
#[cfg(windows)]
|
||||
let interactive_output = interactive_output && ansi_term::enable_ansi_support().is_ok();
|
||||
|
||||
App {
|
||||
matches: Self::matches(interactive_output),
|
||||
interactive_output,
|
||||
}
|
||||
}
|
||||
|
||||
fn matches(interactive_output: bool) -> ArgMatches<'static> {
|
||||
let clap_color_setting = if interactive_output {
|
||||
AppSettings::ColoredHelp
|
||||
} else {
|
||||
AppSettings::ColorNever
|
||||
};
|
||||
|
||||
ClapApp::new(crate_name!())
|
||||
.version(crate_version!())
|
||||
.global_setting(clap_color_setting)
|
||||
.global_setting(AppSettings::DeriveDisplayOrder)
|
||||
.global_setting(AppSettings::UnifiedHelpMessage)
|
||||
.global_setting(AppSettings::NextLineHelp)
|
||||
.setting(AppSettings::InferSubcommands)
|
||||
.setting(AppSettings::ArgsNegateSubcommands)
|
||||
.setting(AppSettings::DisableHelpSubcommand)
|
||||
.setting(AppSettings::VersionlessSubcommands)
|
||||
.max_term_width(90)
|
||||
.about(
|
||||
"A cat(1) clone with wings.\n\n\
|
||||
Use '--help' instead of '-h' to see a more detailed version of the help text.",
|
||||
).long_about("A cat(1) clone with syntax highlighting and Git integration.")
|
||||
.arg(
|
||||
Arg::with_name("language")
|
||||
.short("l")
|
||||
.long("language")
|
||||
.overrides_with("language")
|
||||
.help("Set the language for highlighting")
|
||||
.long_help(
|
||||
"Set the language for syntax highlighting. The language can be \
|
||||
specified as a name (like 'C++' or 'LaTeX') or possible file \
|
||||
extension (like 'cpp', 'hpp' or 'md'). Use '--list-languages' \
|
||||
to show all supported language names and file extensions",
|
||||
).takes_value(true),
|
||||
).arg(
|
||||
Arg::with_name("FILE")
|
||||
.help("File(s) to print / concatenate. Use '-' for standard input.")
|
||||
.long_help(
|
||||
"File(s) to print. Use no argument or '-' to read from standard \
|
||||
input",
|
||||
).multiple(true)
|
||||
.empty_values(false),
|
||||
).arg(
|
||||
Arg::with_name("style")
|
||||
.long("style")
|
||||
.value_name("style-components")
|
||||
.use_delimiter(true)
|
||||
.takes_value(true)
|
||||
.possible_values(&[
|
||||
"auto", "full", "plain", "changes", "header", "grid", "numbers",
|
||||
]).default_value("auto")
|
||||
.help("Comma-separated list of style elements to display")
|
||||
.long_help(
|
||||
"Configure which elements (line numbers, file headers, grid \
|
||||
borders, Git modifications, ..) to display in addition to the \
|
||||
file contents. The argument is a comma-separated list of \
|
||||
components to display (e.g. 'numbers,changes,grid') or a \
|
||||
pre-defined style ('full')",
|
||||
),
|
||||
).arg(
|
||||
Arg::with_name("color")
|
||||
.long("color")
|
||||
.overrides_with("color")
|
||||
.takes_value(true)
|
||||
.value_name("when")
|
||||
.possible_values(&["auto", "never", "always"])
|
||||
.default_value("auto")
|
||||
.help("When to use colors"),
|
||||
).arg(
|
||||
Arg::with_name("paging")
|
||||
.long("paging")
|
||||
.overrides_with("paging")
|
||||
.takes_value(true)
|
||||
.value_name("when")
|
||||
.possible_values(&["auto", "never", "always"])
|
||||
.default_value("auto")
|
||||
.help("When to use the pager")
|
||||
.long_help("Specify when to use the pager (less)"),
|
||||
).arg(
|
||||
Arg::with_name("wrap")
|
||||
.long("wrap")
|
||||
.overrides_with("wrap")
|
||||
.takes_value(true)
|
||||
.value_name("mode")
|
||||
.possible_values(&["character", "never"])
|
||||
.default_value("character")
|
||||
.help("When to wrap text"),
|
||||
).arg(
|
||||
Arg::with_name("list-languages")
|
||||
.long("list-languages")
|
||||
.help("Displays supported languages")
|
||||
.long_help("Display a list of supported languages"),
|
||||
).arg(
|
||||
Arg::with_name("theme")
|
||||
.long("theme")
|
||||
.overrides_with("theme")
|
||||
.takes_value(true)
|
||||
.help("Set the theme for highlighting")
|
||||
.long_help(
|
||||
"Set the theme for syntax highlighting. Use '--list-themes' to \
|
||||
see all available themes",
|
||||
),
|
||||
).arg(
|
||||
Arg::with_name("line-range")
|
||||
.long("line-range")
|
||||
.overrides_with("line-range")
|
||||
.takes_value(true)
|
||||
.value_name("n:m")
|
||||
.help("Only print the lines from n to m")
|
||||
.long_help(
|
||||
"Print a specified range or ranges of lines from the files. \
|
||||
For example: '--line-range 30:40' will print lines 30 to 40 \n\
|
||||
'--line-range :40' will print lines 1 to 40 \n\
|
||||
'--line-range 40:' will print lines 40 to the end of the file",
|
||||
),
|
||||
).arg(
|
||||
Arg::with_name("list-themes")
|
||||
.long("list-themes")
|
||||
.help("Displays supported themes")
|
||||
.help("Display a list of supported themes for syntax highlighting"),
|
||||
).arg(
|
||||
Arg::with_name("number")
|
||||
.long("number")
|
||||
.overrides_with("number")
|
||||
.short("n")
|
||||
.conflicts_with("style")
|
||||
.help("Show line numbers (alias for '--style=numbers')")
|
||||
.long_help(
|
||||
"Show line numbers (no other decorations). This is an alias for \
|
||||
'--style=numbers'",
|
||||
),
|
||||
).arg(
|
||||
Arg::with_name("unbuffered")
|
||||
.short("u")
|
||||
.help("(ignored)")
|
||||
// TODO: use '.hidden_short_help(true)' when the next clap version is released
|
||||
.long_help(
|
||||
"This option exists for POSIX-compliance reasons ('u' is for \
|
||||
'unbuffered'). The output is always unbuffered - this option \
|
||||
is simply ignored.",
|
||||
),
|
||||
).subcommand(
|
||||
SubCommand::with_name("cache")
|
||||
.about("Modify the syntax-definition and theme cache")
|
||||
.arg(
|
||||
Arg::with_name("init")
|
||||
.long("init")
|
||||
.short("i")
|
||||
.help("Initialize the syntax/theme cache")
|
||||
.long_help(
|
||||
"Initialize the syntax/theme cache by loading from the \
|
||||
source directory (default: the configuration directory)",
|
||||
),
|
||||
).arg(
|
||||
Arg::with_name("clear")
|
||||
.long("clear")
|
||||
.short("c")
|
||||
.help("Reset the cache"),
|
||||
).arg(
|
||||
Arg::with_name("config-dir")
|
||||
.long("config-dir")
|
||||
.short("d")
|
||||
.help("Show the configuration directory"),
|
||||
).group(
|
||||
ArgGroup::with_name("cache-actions")
|
||||
.args(&["init", "clear", "config-dir"])
|
||||
.required(true),
|
||||
).arg(
|
||||
Arg::with_name("source")
|
||||
.long("source")
|
||||
.requires("init")
|
||||
.takes_value(true)
|
||||
.value_name("dir")
|
||||
.help("Use a different source for loading syntaxes and themes from"),
|
||||
).arg(
|
||||
Arg::with_name("target")
|
||||
.long("target")
|
||||
.requires("init")
|
||||
.takes_value(true)
|
||||
.value_name("dir")
|
||||
.help(
|
||||
"Use a different source to store the cached syntax and theme set",
|
||||
),
|
||||
),
|
||||
).help_message("Print this help message.")
|
||||
.version_message("Show version information.")
|
||||
.get_matches()
|
||||
}
|
||||
|
||||
pub fn config(&self) -> Result<Config> {
|
||||
let files = self.files();
|
||||
|
||||
Ok(Config {
|
||||
true_color: is_truecolor_terminal(),
|
||||
output_components: self.output_components()?,
|
||||
language: self.matches.value_of("language"),
|
||||
output_wrap: if !self.interactive_output {
|
||||
// We don't have the tty width when piping to another program.
|
||||
// There's no point in wrapping when this is the case.
|
||||
OutputWrap::None
|
||||
} else {
|
||||
match self.matches.value_of("wrap") {
|
||||
Some("character") => OutputWrap::Character,
|
||||
Some("never") | _ => OutputWrap::None,
|
||||
}
|
||||
},
|
||||
colored_output: match self.matches.value_of("color") {
|
||||
Some("always") => true,
|
||||
Some("never") => false,
|
||||
Some("auto") | _ => self.interactive_output,
|
||||
},
|
||||
paging_mode: match self.matches.value_of("paging") {
|
||||
Some("always") => PagingMode::Always,
|
||||
Some("never") => PagingMode::Never,
|
||||
Some("auto") | _ => if files.contains(&None) {
|
||||
// If we are reading from stdin, only enable paging if we write to an
|
||||
// interactive terminal and if we do not *read* from an interactive
|
||||
// terminal.
|
||||
if self.interactive_output && !atty::is(Stream::Stdin) {
|
||||
PagingMode::QuitIfOneScreen
|
||||
} else {
|
||||
PagingMode::Never
|
||||
}
|
||||
} else {
|
||||
if self.interactive_output {
|
||||
PagingMode::QuitIfOneScreen
|
||||
} else {
|
||||
PagingMode::Never
|
||||
}
|
||||
},
|
||||
},
|
||||
term_width: Term::stdout().size().1 as usize,
|
||||
files,
|
||||
theme: self
|
||||
.matches
|
||||
.value_of("theme")
|
||||
.map(String::from)
|
||||
.or_else(|| env::var("BAT_THEME").ok())
|
||||
.unwrap_or(String::from("Default")),
|
||||
line_range: transpose(self.matches.value_of("line-range").map(LineRange::from))?,
|
||||
})
|
||||
}
|
||||
|
||||
fn files(&self) -> Vec<Option<&str>> {
|
||||
self.matches
|
||||
.values_of("FILE")
|
||||
.map(|values| {
|
||||
values
|
||||
.map(|filename| {
|
||||
if filename == "-" {
|
||||
None
|
||||
} else {
|
||||
Some(filename)
|
||||
}
|
||||
}).collect()
|
||||
}).unwrap_or_else(|| vec![None]) // read from stdin (None) if no args are given
|
||||
}
|
||||
|
||||
fn output_components(&self) -> Result<OutputComponents> {
|
||||
let matches = &self.matches;
|
||||
Ok(OutputComponents(if matches.is_present("number") {
|
||||
[OutputComponent::Numbers].iter().cloned().collect()
|
||||
} else {
|
||||
values_t!(matches.values_of("style"), OutputComponent)?
|
||||
.into_iter()
|
||||
.map(|style| style.components(self.interactive_output))
|
||||
.fold(HashSet::new(), |mut acc, components| {
|
||||
acc.extend(components.iter().cloned());
|
||||
acc
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum PagingMode {
|
||||
Always,
|
||||
QuitIfOneScreen,
|
||||
Never,
|
||||
}
|
||||
|
||||
pub struct Config<'a> {
|
||||
pub true_color: bool,
|
||||
pub output_wrap: OutputWrap,
|
||||
pub output_components: OutputComponents,
|
||||
pub language: Option<&'a str>,
|
||||
pub colored_output: bool,
|
||||
pub paging_mode: PagingMode,
|
||||
pub term_width: usize,
|
||||
pub files: Vec<Option<&'a str>>,
|
||||
pub theme: String,
|
||||
pub line_range: Option<LineRange>,
|
||||
}
|
||||
|
||||
fn is_truecolor_terminal() -> bool {
|
||||
env::var("COLORTERM")
|
||||
.map(|colorterm| colorterm == "truecolor" || colorterm == "24bit")
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub struct LineRange {
|
||||
pub lower: usize,
|
||||
pub upper: usize,
|
||||
}
|
||||
|
||||
impl LineRange {
|
||||
pub fn from(range_raw: &str) -> Result<LineRange> {
|
||||
LineRange::parse_range(range_raw)
|
||||
}
|
||||
|
||||
pub fn new() -> LineRange {
|
||||
LineRange {
|
||||
lower: usize::min_value(),
|
||||
upper: usize::max_value(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_range(range_raw: &str) -> Result<LineRange> {
|
||||
let mut new_range = LineRange::new();
|
||||
|
||||
if range_raw.bytes().nth(0).ok_or("Empty line range")? == b':' {
|
||||
new_range.upper = range_raw[1..].parse()?;
|
||||
return Ok(new_range);
|
||||
} else if range_raw.bytes().last().ok_or("Empty line range")? == b':' {
|
||||
new_range.lower = range_raw[..range_raw.len() - 1].parse()?;
|
||||
return Ok(new_range);
|
||||
}
|
||||
|
||||
let line_numbers: Vec<&str> = range_raw.split(':').collect();
|
||||
if line_numbers.len() == 2 {
|
||||
new_range.lower = line_numbers[0].parse()?;
|
||||
new_range.upper = line_numbers[1].parse()?;
|
||||
return Ok(new_range);
|
||||
}
|
||||
Err("expected single ':' character".into())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_line_range_full() {
|
||||
let range = LineRange::from("40:50").expect("Shouldn't fail on test!");
|
||||
assert_eq!(40, range.lower);
|
||||
assert_eq!(50, range.upper);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_line_range_partial_min() {
|
||||
let range = LineRange::from(":50").expect("Shouldn't fail on test!");
|
||||
assert_eq!(usize::min_value(), range.lower);
|
||||
assert_eq!(50, range.upper);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_line_range_partial_max() {
|
||||
let range = LineRange::from("40:").expect("Shouldn't fail on test!");
|
||||
assert_eq!(40, range.lower);
|
||||
assert_eq!(usize::max_value(), range.upper);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_line_range_fail() {
|
||||
let range = LineRange::from("40:50:80");
|
||||
assert!(range.is_err());
|
||||
let range = LineRange::from("40::80");
|
||||
assert!(range.is_err());
|
||||
let range = LineRange::from(":40:");
|
||||
assert!(range.is_err());
|
||||
let range = LineRange::from("40");
|
||||
assert!(range.is_err());
|
||||
}
|
||||
|
||||
fn transpose<T>(opt: Option<Result<T>>) -> Result<Option<T>> {
|
||||
opt.map_or(Ok(None), |res| res.map(Some))
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
use directories::ProjectDirs;
|
||||
use errors::*;
|
||||
use std::borrow::Cow;
|
||||
use std::fs::{self, File};
|
||||
use std::path::{Path, PathBuf};
|
||||
use syntect::dumps::{dump_to_file, from_binary, from_reader};
|
||||
use syntect::highlighting::{Theme, ThemeSet};
|
||||
use syntect::parsing::{SyntaxDefinition, SyntaxSet};
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::FileTypeExt;
|
||||
|
||||
lazy_static! {
|
||||
static ref PROJECT_DIRS: ProjectDirs =
|
||||
ProjectDirs::from("", "", crate_name!()).expect("Could not get home directory");
|
||||
}
|
||||
|
||||
pub struct HighlightingAssets {
|
||||
pub syntax_set: SyntaxSet,
|
||||
pub theme_set: ThemeSet,
|
||||
}
|
||||
|
||||
impl HighlightingAssets {
|
||||
pub fn new() -> Self {
|
||||
Self::from_cache().unwrap_or_else(|_| Self::from_binary())
|
||||
}
|
||||
|
||||
pub fn from_files(dir: Option<&Path>) -> Result<Self> {
|
||||
let source_dir = dir.unwrap_or_else(|| PROJECT_DIRS.config_dir());
|
||||
|
||||
let mut assets = Self::from_binary_unlinked();
|
||||
|
||||
let theme_dir = source_dir.join("themes");
|
||||
|
||||
if let Ok(theme_set) = ThemeSet::load_from_folder(&theme_dir) {
|
||||
// TODO: If syntect would support this, it would be great to
|
||||
// load the new themes in addition to the ones in the binary.
|
||||
assets.theme_set = theme_set;
|
||||
} else {
|
||||
println!(
|
||||
"No themes were found in '{}', using the default set",
|
||||
theme_dir.to_string_lossy()
|
||||
);
|
||||
}
|
||||
|
||||
let syntax_dir = source_dir.join("syntaxes");
|
||||
if syntax_dir.exists() {
|
||||
assets.syntax_set.load_syntaxes(syntax_dir, true)?;
|
||||
} else {
|
||||
println!(
|
||||
"No syntaxes were found in '{}', using the default set.",
|
||||
syntax_dir.to_string_lossy()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(assets)
|
||||
}
|
||||
|
||||
fn from_cache() -> Result<Self> {
|
||||
let theme_set_path = theme_set_path();
|
||||
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 mut syntax_set: SyntaxSet =
|
||||
from_reader(syntax_set_file).chain_err(|| "Could not parse cached syntax set")?;
|
||||
syntax_set.link_syntaxes();
|
||||
|
||||
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(theme_set_file).chain_err(|| "Could not parse cached theme set")?;
|
||||
|
||||
Ok(HighlightingAssets {
|
||||
syntax_set,
|
||||
theme_set,
|
||||
})
|
||||
}
|
||||
|
||||
fn from_binary_unlinked() -> Self {
|
||||
let syntax_set: SyntaxSet = from_binary(include_bytes!("../assets/syntaxes.bin"));
|
||||
let theme_set: ThemeSet = from_binary(include_bytes!("../assets/themes.bin"));
|
||||
|
||||
HighlightingAssets {
|
||||
syntax_set,
|
||||
theme_set,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_binary() -> Self {
|
||||
let mut assets = Self::from_binary_unlinked();
|
||||
assets.syntax_set.link_syntaxes();
|
||||
assets
|
||||
}
|
||||
|
||||
pub fn save(&self, dir: Option<&Path>) -> Result<()> {
|
||||
let target_dir = dir.unwrap_or_else(|| PROJECT_DIRS.cache_dir());
|
||||
let _ = fs::create_dir(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");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_theme(&self, theme: &str) -> &Theme {
|
||||
match self.theme_set.themes.get(theme) {
|
||||
Some(theme) => theme,
|
||||
None => {
|
||||
use ansi_term::Colour::Yellow;
|
||||
eprintln!(
|
||||
"{}: Unknown theme '{}', using default.",
|
||||
Yellow.paint("[bat warning]"),
|
||||
theme
|
||||
);
|
||||
&self.theme_set.themes["Default"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_syntax(&self, language: Option<&str>, filename: Option<&str>) -> &SyntaxDefinition {
|
||||
let syntax = match (language, filename) {
|
||||
(Some(language), _) => self.syntax_set.find_syntax_by_token(language),
|
||||
(None, Some(filename)) => {
|
||||
#[cfg(not(unix))]
|
||||
let may_read_from_file = true;
|
||||
|
||||
// Do not peek at the file (to determine the syntax) if it is a FIFO because they can
|
||||
// only be read once.
|
||||
#[cfg(unix)]
|
||||
let may_read_from_file = !fs::metadata(filename)
|
||||
.map(|m| m.file_type().is_fifo())
|
||||
.unwrap_or(false);
|
||||
|
||||
if may_read_from_file {
|
||||
self.syntax_set
|
||||
.find_syntax_for_file(filename)
|
||||
.unwrap_or(None)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
(None, None) => None,
|
||||
};
|
||||
|
||||
syntax.unwrap_or_else(|| self.syntax_set.find_syntax_plain_text())
|
||||
}
|
||||
}
|
||||
|
||||
fn theme_set_path() -> PathBuf {
|
||||
PROJECT_DIRS.cache_dir().join("themes.bin")
|
||||
}
|
||||
|
||||
fn syntax_set_path() -> PathBuf {
|
||||
PROJECT_DIRS.cache_dir().join("syntaxes.bin")
|
||||
}
|
||||
|
||||
pub fn config_dir() -> Cow<'static, str> {
|
||||
PROJECT_DIRS.config_dir().to_string_lossy()
|
||||
}
|
||||
|
||||
pub fn clear_assets() {
|
||||
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");
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
use ansi_term::Style;
|
||||
use diff::LineChange;
|
||||
use printer::{Colors, Printer};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DecorationText {
|
||||
pub width: usize,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
pub trait Decoration {
|
||||
fn generate(&self, line_number: usize, continuation: bool, printer: &Printer)
|
||||
-> DecorationText;
|
||||
fn width(&self) -> usize;
|
||||
}
|
||||
|
||||
// Line number decoration.
|
||||
pub struct LineNumberDecoration {
|
||||
color: Style,
|
||||
cached_wrap: DecorationText,
|
||||
cached_wrap_invalid_at: usize,
|
||||
}
|
||||
|
||||
impl LineNumberDecoration {
|
||||
pub fn new(colors: &Colors) -> Self {
|
||||
LineNumberDecoration {
|
||||
color: colors.line_number,
|
||||
cached_wrap_invalid_at: 10000,
|
||||
cached_wrap: DecorationText {
|
||||
text: colors.line_number.paint(" ".repeat(4)).to_string(),
|
||||
width: 4,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decoration for LineNumberDecoration {
|
||||
fn generate(
|
||||
&self,
|
||||
line_number: usize,
|
||||
continuation: bool,
|
||||
_printer: &Printer,
|
||||
) -> DecorationText {
|
||||
if continuation {
|
||||
if line_number > self.cached_wrap_invalid_at {
|
||||
let new_width = self.cached_wrap.width + 1;
|
||||
return DecorationText {
|
||||
text: self.color.paint(" ".repeat(new_width)).to_string(),
|
||||
width: new_width,
|
||||
};
|
||||
}
|
||||
|
||||
self.cached_wrap.clone()
|
||||
} else {
|
||||
let plain: String = format!("{:4}", line_number);
|
||||
DecorationText {
|
||||
width: plain.len(),
|
||||
text: self.color.paint(plain).to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn width(&self) -> usize {
|
||||
4
|
||||
}
|
||||
}
|
||||
|
||||
// Line changes decoration.
|
||||
pub struct LineChangesDecoration {
|
||||
cached_none: DecorationText,
|
||||
cached_added: DecorationText,
|
||||
cached_removed_above: DecorationText,
|
||||
cached_removed_below: DecorationText,
|
||||
cached_modified: DecorationText,
|
||||
}
|
||||
|
||||
impl LineChangesDecoration {
|
||||
#[inline]
|
||||
fn generate_cached(style: Style, text: &str) -> DecorationText {
|
||||
DecorationText {
|
||||
text: style.paint(text).to_string(),
|
||||
width: text.chars().count(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(colors: &Colors) -> Self {
|
||||
LineChangesDecoration {
|
||||
cached_none: Self::generate_cached(Style::default(), " "),
|
||||
cached_added: Self::generate_cached(colors.git_added, "+"),
|
||||
cached_removed_above: Self::generate_cached(colors.git_removed, "‾"),
|
||||
cached_removed_below: Self::generate_cached(colors.git_removed, "_"),
|
||||
cached_modified: Self::generate_cached(colors.git_modified, "~"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decoration for LineChangesDecoration {
|
||||
fn generate(
|
||||
&self,
|
||||
line_number: usize,
|
||||
continuation: bool,
|
||||
printer: &Printer,
|
||||
) -> DecorationText {
|
||||
if !continuation {
|
||||
if let Some(ref changes) = printer.line_changes {
|
||||
return match changes.get(&(line_number as u32)) {
|
||||
Some(&LineChange::Added) => self.cached_added.clone(),
|
||||
Some(&LineChange::RemovedAbove) => self.cached_removed_above.clone(),
|
||||
Some(&LineChange::RemovedBelow) => self.cached_removed_below.clone(),
|
||||
Some(&LineChange::Modified) => self.cached_modified.clone(),
|
||||
_ => self.cached_none.clone(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
self.cached_none.clone()
|
||||
}
|
||||
|
||||
fn width(&self) -> usize {
|
||||
self.cached_none.width
|
||||
}
|
||||
}
|
||||
|
||||
// Grid border decoration.
|
||||
pub struct GridBorderDecoration {
|
||||
cached: DecorationText,
|
||||
}
|
||||
|
||||
impl GridBorderDecoration {
|
||||
pub fn new(colors: &Colors) -> Self {
|
||||
GridBorderDecoration {
|
||||
cached: DecorationText {
|
||||
text: colors.grid.paint("│").to_string(),
|
||||
width: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decoration for GridBorderDecoration {
|
||||
fn generate(
|
||||
&self,
|
||||
_line_number: usize,
|
||||
_continuation: bool,
|
||||
_printer: &Printer,
|
||||
) -> DecorationText {
|
||||
self.cached.clone()
|
||||
}
|
||||
|
||||
fn width(&self) -> usize {
|
||||
self.cached.width
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
use git2::{DiffOptions, IntoCString, Repository};
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum LineChange {
|
||||
Added,
|
||||
RemovedAbove,
|
||||
RemovedBelow,
|
||||
Modified,
|
||||
}
|
||||
|
||||
pub type LineChanges = HashMap<u32, LineChange>;
|
||||
|
||||
pub fn get_git_diff(filename: &str) -> Option<LineChanges> {
|
||||
let repo = Repository::discover(&filename).ok()?;
|
||||
let path_absolute = fs::canonicalize(&filename).ok()?;
|
||||
let path_relative_to_repo = path_absolute.strip_prefix(repo.workdir()?).ok()?;
|
||||
|
||||
let mut diff_options = DiffOptions::new();
|
||||
let pathspec = path_relative_to_repo.into_c_string().ok()?;
|
||||
diff_options.pathspec(pathspec);
|
||||
diff_options.context_lines(0);
|
||||
|
||||
let diff = repo
|
||||
.diff_index_to_workdir(None, Some(&mut diff_options))
|
||||
.ok()?;
|
||||
|
||||
let mut line_changes: LineChanges = HashMap::new();
|
||||
|
||||
let mark_section =
|
||||
|line_changes: &mut LineChanges, start: u32, end: i32, change: LineChange| {
|
||||
for line in start..(end + 1) as u32 {
|
||||
line_changes.insert(line, change);
|
||||
}
|
||||
};
|
||||
|
||||
let _ = diff.foreach(
|
||||
&mut |_, _| true,
|
||||
None,
|
||||
Some(&mut |delta, hunk| {
|
||||
let path = delta.new_file().path().unwrap_or_else(|| Path::new(""));
|
||||
|
||||
if path_relative_to_repo != path {
|
||||
return false;
|
||||
}
|
||||
|
||||
let old_lines = hunk.old_lines();
|
||||
let new_start = hunk.new_start();
|
||||
let new_lines = hunk.new_lines();
|
||||
let new_end = (new_start + new_lines) as i32 - 1;
|
||||
|
||||
if old_lines == 0 && new_lines > 0 {
|
||||
mark_section(&mut line_changes, new_start, new_end, LineChange::Added);
|
||||
} else if new_lines == 0 && old_lines > 0 {
|
||||
if new_start == 0 {
|
||||
mark_section(&mut line_changes, 1, 1, LineChange::RemovedAbove);
|
||||
} else {
|
||||
mark_section(
|
||||
&mut line_changes,
|
||||
new_start,
|
||||
new_start as i32,
|
||||
LineChange::RemovedBelow,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
mark_section(&mut line_changes, new_start, new_end, LineChange::Modified);
|
||||
}
|
||||
|
||||
true
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
Some(line_changes)
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
use ansi_term::Colour::Green;
|
||||
use app::{Config, LineRange};
|
||||
use assets::HighlightingAssets;
|
||||
use diff::get_git_diff;
|
||||
use errors::*;
|
||||
use output::OutputType;
|
||||
use printer::Printer;
|
||||
use std::fs::File;
|
||||
use std::io::{self, BufRead, BufReader};
|
||||
use syntect::easy::HighlightLines;
|
||||
use syntect::highlighting::Theme;
|
||||
use syntect::parsing::SyntaxDefinition;
|
||||
|
||||
pub fn list_languages(assets: &HighlightingAssets, term_width: usize) {
|
||||
let mut languages = assets
|
||||
.syntax_set
|
||||
.syntaxes()
|
||||
.iter()
|
||||
.filter(|syntax| !syntax.hidden && !syntax.file_extensions.is_empty())
|
||||
.collect::<Vec<_>>();
|
||||
languages.sort_by_key(|lang| lang.name.to_uppercase());
|
||||
|
||||
let longest = languages
|
||||
.iter()
|
||||
.map(|syntax| syntax.name.len())
|
||||
.max()
|
||||
.unwrap_or(32); // Fallback width if they have no language definitions.
|
||||
|
||||
let comma_separator = ", ";
|
||||
let separator = " ";
|
||||
// Line-wrapping for the possible file extension overflow.
|
||||
let desired_width = term_width - longest - separator.len();
|
||||
|
||||
for lang in languages {
|
||||
print!("{:width$}{}", lang.name, separator, width = longest);
|
||||
|
||||
// Number of characters on this line so far, wrap before `desired_width`
|
||||
let mut num_chars = 0;
|
||||
|
||||
let mut extension = lang.file_extensions.iter().peekable();
|
||||
while let Some(word) = extension.next() {
|
||||
// If we can't fit this word in, then create a line break and align it in.
|
||||
let new_chars = word.len() + comma_separator.len();
|
||||
if num_chars + new_chars >= desired_width {
|
||||
num_chars = 0;
|
||||
print!("\n{:width$}{}", "", separator, width = longest);
|
||||
}
|
||||
|
||||
num_chars += new_chars;
|
||||
print!("{}", Green.paint(&word[..]));
|
||||
if extension.peek().is_some() {
|
||||
print!("{}", comma_separator);
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_files(assets: &HighlightingAssets, config: &Config) -> Result<bool> {
|
||||
let theme = assets.get_theme(&config.theme);
|
||||
|
||||
let mut output_type = OutputType::from_mode(config.paging_mode);
|
||||
let handle = output_type.handle()?;
|
||||
let mut printer = Printer::new(handle, &config, &theme);
|
||||
let mut no_errors: bool = true;
|
||||
|
||||
for file in &config.files {
|
||||
printer.ansi_prefix_sgr.clear();
|
||||
printer.line_changes = file.and_then(|filename| get_git_diff(filename));
|
||||
let syntax = assets.get_syntax(config.language, *file);
|
||||
|
||||
let result = print_file(theme, &syntax, &mut printer, *file);
|
||||
|
||||
if let Err(error) = result {
|
||||
handle_error(&error);
|
||||
no_errors = false;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(no_errors)
|
||||
}
|
||||
|
||||
fn print_file(
|
||||
theme: &Theme,
|
||||
syntax: &SyntaxDefinition,
|
||||
printer: &mut Printer,
|
||||
filename: Option<&str>,
|
||||
) -> Result<()> {
|
||||
let stdin = io::stdin(); // TODO: this is not always needed
|
||||
{
|
||||
let reader: Box<BufRead> = match filename {
|
||||
None => Box::new(stdin.lock()),
|
||||
Some(filename) => Box::new(BufReader::new(File::open(filename)?)),
|
||||
};
|
||||
|
||||
let highlighter = HighlightLines::new(syntax, theme);
|
||||
|
||||
printer.print_header(filename)?;
|
||||
print_file_ranges(printer, reader, highlighter, &printer.config.line_range)?;
|
||||
printer.print_footer()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_file_ranges<'a>(
|
||||
printer: &mut Printer,
|
||||
mut reader: Box<BufRead + 'a>,
|
||||
mut highlighter: HighlightLines,
|
||||
line_ranges: &Option<LineRange>,
|
||||
) -> Result<()> {
|
||||
let mut buffer = Vec::new();
|
||||
|
||||
while reader.read_until(b'\n', &mut buffer)? > 0 {
|
||||
{
|
||||
let line = String::from_utf8_lossy(&buffer);
|
||||
let regions = highlighter.highlight(line.as_ref());
|
||||
|
||||
match line_ranges {
|
||||
&Some(ref range) => {
|
||||
if printer.line_number + 1 < range.lower {
|
||||
// skip line
|
||||
printer.line_number += 1;
|
||||
} else if printer.line_number >= range.upper {
|
||||
// no more lines in range
|
||||
break;
|
||||
} else {
|
||||
printer.print_line(®ions)?;
|
||||
}
|
||||
}
|
||||
&None => {
|
||||
printer.print_line(®ions)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
buffer.clear();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
+104
-204
@@ -1,223 +1,123 @@
|
||||
extern crate ansi_term;
|
||||
extern crate atty;
|
||||
extern crate console;
|
||||
extern crate git2;
|
||||
extern crate syntect;
|
||||
// `error_chain!` can recurse deeply
|
||||
#![recursion_limit = "1024"]
|
||||
|
||||
#[macro_use]
|
||||
extern crate error_chain;
|
||||
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::io::{self, BufRead, ErrorKind, Result, StdoutLock, Write};
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
extern crate ansi_term;
|
||||
extern crate atty;
|
||||
extern crate console;
|
||||
extern crate directories;
|
||||
extern crate git2;
|
||||
extern crate syntect;
|
||||
|
||||
mod app;
|
||||
mod assets;
|
||||
mod decorations;
|
||||
mod diff;
|
||||
mod features;
|
||||
mod output;
|
||||
mod printer;
|
||||
mod style;
|
||||
mod terminal;
|
||||
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
|
||||
use ansi_term::Colour::{Fixed, Green, Red, White, Yellow};
|
||||
use ansi_term::Style;
|
||||
use atty::Stream;
|
||||
use clap::{App, AppSettings, Arg, ArgMatches};
|
||||
use console::Term;
|
||||
use git2::{DiffOptions, IntoCString, Repository};
|
||||
use app::App;
|
||||
use assets::{clear_assets, config_dir, HighlightingAssets};
|
||||
use features::{list_languages, print_files};
|
||||
|
||||
use syntect::easy::HighlightFile;
|
||||
use syntect::highlighting::{Theme, ThemeSet};
|
||||
use syntect::parsing::SyntaxSet;
|
||||
use syntect::util::as_24_bit_terminal_escaped;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum LineChange {
|
||||
Added,
|
||||
RemovedAbove,
|
||||
RemovedBelow,
|
||||
Modified,
|
||||
}
|
||||
|
||||
type LineChanges = HashMap<u32, LineChange>;
|
||||
|
||||
const PANEL_WIDTH: usize = 7;
|
||||
const GRID_COLOR: u8 = 238;
|
||||
|
||||
fn print_horizontal_line(
|
||||
handle: &mut StdoutLock,
|
||||
grid_char: char,
|
||||
term_width: usize,
|
||||
) -> io::Result<()> {
|
||||
let bar = "─".repeat(term_width - (PANEL_WIDTH + 1));
|
||||
let line = format!("{}{}{}", "─".repeat(PANEL_WIDTH), grid_char, bar);
|
||||
|
||||
write!(handle, "{}\n", Fixed(GRID_COLOR).paint(line))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_file<P: AsRef<Path>>(
|
||||
theme: &Theme,
|
||||
syntax_set: &SyntaxSet,
|
||||
filename: P,
|
||||
line_changes: Option<LineChanges>,
|
||||
) -> io::Result<()> {
|
||||
let mut highlighter = HighlightFile::new(filename.as_ref().clone(), syntax_set, theme)?;
|
||||
|
||||
let stdout = io::stdout();
|
||||
let mut handle = stdout.lock();
|
||||
|
||||
let term = Term::stdout();
|
||||
let (_, term_width) = term.size();
|
||||
let term_width = term_width as usize;
|
||||
|
||||
print_horizontal_line(&mut handle, '┬', term_width)?;
|
||||
|
||||
write!(
|
||||
handle,
|
||||
"{}{} {}\n",
|
||||
" ".repeat(PANEL_WIDTH),
|
||||
Fixed(GRID_COLOR).paint("│"),
|
||||
White.bold().paint(filename.as_ref().to_string_lossy())
|
||||
)?;
|
||||
|
||||
print_horizontal_line(&mut handle, '┼', term_width)?;
|
||||
|
||||
for (idx, maybe_line) in highlighter.reader.lines().enumerate() {
|
||||
let line_nr = idx + 1;
|
||||
let line = maybe_line.unwrap_or("<INVALID UTF-8>".into());
|
||||
let regions = highlighter.highlight_lines.highlight(&line);
|
||||
|
||||
let line_change = if let Some(ref changes) = line_changes {
|
||||
match changes.get(&(line_nr as u32)) {
|
||||
Some(&LineChange::Added) => Green.paint("+"),
|
||||
Some(&LineChange::RemovedAbove) => Red.paint("‾"),
|
||||
Some(&LineChange::RemovedBelow) => Red.paint("_"),
|
||||
Some(&LineChange::Modified) => Yellow.paint("~"),
|
||||
_ => Style::default().paint(" "),
|
||||
}
|
||||
} else {
|
||||
Style::default().paint(" ")
|
||||
};
|
||||
|
||||
write!(
|
||||
handle,
|
||||
"{} {} {} {}\n",
|
||||
Fixed(244).paint(format!("{:4}", line_nr)),
|
||||
line_change,
|
||||
Fixed(GRID_COLOR).paint("│"),
|
||||
as_24_bit_terminal_escaped(®ions, false)
|
||||
)?;
|
||||
}
|
||||
|
||||
print_horizontal_line(&mut handle, '┴', term_width)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_git_diff(filename: String) -> Option<LineChanges> {
|
||||
let repo = Repository::open_from_env().ok()?;
|
||||
|
||||
let mut diff_options = DiffOptions::new();
|
||||
diff_options.pathspec(filename.into_c_string().ok()?);
|
||||
diff_options.context_lines(0);
|
||||
|
||||
let diff = repo.diff_index_to_workdir(None, Some(&mut diff_options))
|
||||
.ok()?;
|
||||
|
||||
let mut line_changes: LineChanges = HashMap::new();
|
||||
|
||||
let mark_section =
|
||||
|line_changes: &mut LineChanges, start: u32, end: i32, change: LineChange| {
|
||||
for line in start..(end + 1) as u32 {
|
||||
line_changes.insert(line, change);
|
||||
}
|
||||
};
|
||||
|
||||
let _ = diff.foreach(
|
||||
&mut |_, _| true,
|
||||
None,
|
||||
Some(&mut |_, hunk| {
|
||||
let old_lines = hunk.old_lines();
|
||||
let new_start = hunk.new_start();
|
||||
let new_lines = hunk.new_lines();
|
||||
let new_end = (new_start + new_lines) as i32 - 1;
|
||||
|
||||
if old_lines == 0 && new_lines > 0 {
|
||||
mark_section(&mut line_changes, new_start, new_end, LineChange::Added);
|
||||
} else if new_lines == 0 && old_lines > 0 {
|
||||
if new_start <= 0 {
|
||||
mark_section(&mut line_changes, 1, 1, LineChange::RemovedAbove);
|
||||
} else {
|
||||
mark_section(
|
||||
&mut line_changes,
|
||||
new_start,
|
||||
new_start as i32,
|
||||
LineChange::RemovedBelow,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
mark_section(&mut line_changes, new_start, new_end, LineChange::Modified);
|
||||
}
|
||||
|
||||
true
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
Some(line_changes)
|
||||
}
|
||||
|
||||
fn run(matches: &ArgMatches) -> Result<()> {
|
||||
let home_dir = env::home_dir().ok_or(io::Error::new(
|
||||
ErrorKind::Other,
|
||||
"Could not get home directory",
|
||||
))?;
|
||||
|
||||
let theme_dir = home_dir.join(".config").join("bat").join("themes");
|
||||
let theme_set = ThemeSet::load_from_folder(theme_dir)
|
||||
.map_err(|_| io::Error::new(ErrorKind::Other, "Could not load themes"))?;
|
||||
let theme = &theme_set.themes["Monokai"];
|
||||
|
||||
let syntax_set = SyntaxSet::load_defaults_nonewlines();
|
||||
|
||||
if let Some(files) = matches.values_of("FILE") {
|
||||
for file in files {
|
||||
let line_changes = get_git_diff(file.to_string());
|
||||
print_file(theme, &syntax_set, file, line_changes)?;
|
||||
mod errors {
|
||||
error_chain! {
|
||||
foreign_links {
|
||||
Clap(::clap::Error);
|
||||
Io(::std::io::Error);
|
||||
SyntectError(::syntect::LoadingError);
|
||||
ParseIntError(::std::num::ParseIntError);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
pub fn handle_error(error: &Error) {
|
||||
match error {
|
||||
&Error(ErrorKind::Io(ref io_error), _)
|
||||
if io_error.kind() == super::io::ErrorKind::BrokenPipe =>
|
||||
{
|
||||
super::process::exit(0);
|
||||
}
|
||||
_ => {
|
||||
use ansi_term::Colour::Red;
|
||||
eprintln!("{}: {}", Red.paint("[bat error]"), error);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
use errors::*;
|
||||
|
||||
/// Returns `Err(..)` upon fatal errors. Otherwise, returns `Some(true)` on full success and
|
||||
/// `Some(false)` if any intermediate errors occurred (were printed).
|
||||
fn run() -> Result<bool> {
|
||||
let app = App::new();
|
||||
|
||||
match app.matches.subcommand() {
|
||||
("cache", Some(cache_matches)) => {
|
||||
if cache_matches.is_present("init") {
|
||||
let source_dir = cache_matches.value_of("source").map(Path::new);
|
||||
let target_dir = cache_matches.value_of("target").map(Path::new);
|
||||
|
||||
let assets = HighlightingAssets::from_files(source_dir)?;
|
||||
assets.save(target_dir)?;
|
||||
} else if cache_matches.is_present("clear") {
|
||||
clear_assets();
|
||||
} else if cache_matches.is_present("config-dir") {
|
||||
println!("{}", config_dir());
|
||||
}
|
||||
|
||||
return Ok(true);
|
||||
}
|
||||
_ => {
|
||||
let config = app.config()?;
|
||||
let assets = HighlightingAssets::new();
|
||||
|
||||
if app.matches.is_present("list-languages") {
|
||||
list_languages(&assets, config.term_width);
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
if app.matches.is_present("list-themes") {
|
||||
let themes = &assets.theme_set.themes;
|
||||
for (theme, _) in themes.iter() {
|
||||
println!("{}", theme);
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
print_files(&assets, &config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let clap_color_setting = if atty::is(Stream::Stdout) {
|
||||
AppSettings::ColoredHelp
|
||||
} else {
|
||||
AppSettings::ColorNever
|
||||
};
|
||||
let result = run();
|
||||
|
||||
let matches = App::new(crate_name!())
|
||||
.version(crate_version!())
|
||||
.setting(clap_color_setting)
|
||||
.setting(AppSettings::DeriveDisplayOrder)
|
||||
.setting(AppSettings::UnifiedHelpMessage)
|
||||
.setting(AppSettings::NextLineHelp)
|
||||
.setting(AppSettings::DisableVersion)
|
||||
.max_term_width(90)
|
||||
.about(crate_description!())
|
||||
.arg(
|
||||
Arg::with_name("FILE")
|
||||
.help("File(s) to print")
|
||||
.multiple(true)
|
||||
.empty_values(false),
|
||||
)
|
||||
.help_message("Print this help message.")
|
||||
.version_message("Show version information.")
|
||||
.get_matches();
|
||||
|
||||
let result = run(&matches);
|
||||
|
||||
if let Err(e) = result {
|
||||
if e.kind() != ErrorKind::BrokenPipe {
|
||||
eprintln!("{}: {}", Red.paint("[bat error]"), e);
|
||||
match result {
|
||||
Err(error) => {
|
||||
handle_error(&error);
|
||||
process::exit(1);
|
||||
}
|
||||
Ok(false) => {
|
||||
process::exit(1);
|
||||
}
|
||||
Ok(true) => {
|
||||
process::exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
use app::PagingMode;
|
||||
use errors::*;
|
||||
use std::env;
|
||||
use std::io::{self, Write};
|
||||
use std::process::{Child, Command, Stdio};
|
||||
|
||||
pub enum OutputType {
|
||||
Pager(Child),
|
||||
Stdout(io::Stdout),
|
||||
}
|
||||
|
||||
impl OutputType {
|
||||
pub fn from_mode(mode: PagingMode) -> Self {
|
||||
use self::PagingMode::*;
|
||||
match mode {
|
||||
Always => OutputType::try_pager(false),
|
||||
QuitIfOneScreen => OutputType::try_pager(true),
|
||||
_ => OutputType::stdout(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to launch the pager. Fall back to stdout in case of errors.
|
||||
fn try_pager(quit_if_one_screen: bool) -> Self {
|
||||
let pager = env::var("BAT_PAGER")
|
||||
.or_else(|_| env::var("PAGER"))
|
||||
.unwrap_or(String::from("less"));
|
||||
|
||||
let mut process = if pager == "less" {
|
||||
let mut args = vec!["--RAW-CONTROL-CHARS", "--no-init"];
|
||||
if quit_if_one_screen {
|
||||
args.push("--quit-if-one-screen");
|
||||
}
|
||||
|
||||
let mut p = Command::new("less");
|
||||
p.args(&args).env("LESSCHARSET", "UTF-8");
|
||||
p
|
||||
} else {
|
||||
Command::new(pager)
|
||||
};
|
||||
|
||||
process
|
||||
.stdin(Stdio::piped())
|
||||
.spawn()
|
||||
.map(OutputType::Pager)
|
||||
.unwrap_or_else(|_| OutputType::stdout())
|
||||
}
|
||||
|
||||
fn stdout() -> Self {
|
||||
OutputType::Stdout(io::stdout())
|
||||
}
|
||||
|
||||
pub fn handle(&mut self) -> Result<&mut Write> {
|
||||
Ok(match *self {
|
||||
OutputType::Pager(ref mut command) => command
|
||||
.stdin
|
||||
.as_mut()
|
||||
.chain_err(|| "Could not open stdin for pager")?,
|
||||
OutputType::Stdout(ref mut handle) => handle,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for OutputType {
|
||||
fn drop(&mut self) {
|
||||
if let OutputType::Pager(ref mut command) = *self {
|
||||
let _ = command.wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
+310
@@ -0,0 +1,310 @@
|
||||
use ansi_term::Colour::{Fixed, Green, Red, Yellow};
|
||||
use ansi_term::Style;
|
||||
use app::Config;
|
||||
use console::AnsiCodeIterator;
|
||||
use decorations::{Decoration, GridBorderDecoration, LineChangesDecoration, LineNumberDecoration};
|
||||
use diff::LineChanges;
|
||||
use errors::*;
|
||||
use std::boxed::Box;
|
||||
use std::io::Write;
|
||||
use std::vec::Vec;
|
||||
use style::OutputWrap;
|
||||
use syntect::highlighting::{self, Theme};
|
||||
use terminal::{as_terminal_escaped, to_ansi_color};
|
||||
|
||||
pub struct Printer<'a> {
|
||||
handle: &'a mut Write,
|
||||
colors: Colors,
|
||||
pub config: &'a Config<'a>,
|
||||
decorations: Vec<Box<Decoration>>,
|
||||
panel_width: usize,
|
||||
pub ansi_prefix_sgr: String,
|
||||
pub line_number: usize,
|
||||
pub line_changes: Option<LineChanges>,
|
||||
}
|
||||
|
||||
impl<'a> Printer<'a> {
|
||||
pub fn new(handle: &'a mut Write, config: &'a Config, theme: &Theme) -> Self {
|
||||
let colors = if config.colored_output {
|
||||
Colors::colored(theme, config.true_color)
|
||||
} else {
|
||||
Colors::plain()
|
||||
};
|
||||
|
||||
// Create decorations.
|
||||
let mut decorations: Vec<Box<Decoration>> = Vec::new();
|
||||
|
||||
if config.output_components.numbers() {
|
||||
decorations.push(Box::new(LineNumberDecoration::new(&colors)));
|
||||
}
|
||||
|
||||
if config.output_components.changes() {
|
||||
decorations.push(Box::new(LineChangesDecoration::new(&colors)));
|
||||
}
|
||||
|
||||
let mut panel_width: usize =
|
||||
decorations.len() + decorations.iter().fold(0, |a, x| a + x.width());
|
||||
|
||||
// The grid border decoration isn't added until after the panel_width calculation, since the
|
||||
// print_horizontal_line, print_header, and print_footer functions all assume the panel
|
||||
// width is without the grid border.
|
||||
if config.output_components.grid() && !decorations.is_empty() {
|
||||
decorations.push(Box::new(GridBorderDecoration::new(&colors)));
|
||||
}
|
||||
|
||||
// Disable the panel if the terminal is too small (i.e. can't fit 5 characters with the
|
||||
// panel showing).
|
||||
if config.term_width
|
||||
< (decorations.len() + decorations.iter().fold(0, |a, x| a + x.width())) + 5
|
||||
{
|
||||
decorations.clear();
|
||||
panel_width = 0;
|
||||
}
|
||||
|
||||
// Create printer.
|
||||
Printer {
|
||||
panel_width,
|
||||
handle,
|
||||
colors,
|
||||
config,
|
||||
decorations,
|
||||
ansi_prefix_sgr: String::new(),
|
||||
line_number: 0,
|
||||
line_changes: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_header(&mut self, filename: Option<&str>) -> Result<()> {
|
||||
self.line_number = 0;
|
||||
|
||||
if !self.config.output_components.header() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if self.config.output_components.grid() {
|
||||
self.print_horizontal_line('┬')?;
|
||||
|
||||
write!(
|
||||
self.handle,
|
||||
"{}{}",
|
||||
" ".repeat(self.panel_width),
|
||||
self.colors
|
||||
.grid
|
||||
.paint(if self.panel_width > 0 { "│ " } else { "" }),
|
||||
)?;
|
||||
} else {
|
||||
write!(self.handle, "{}", " ".repeat(self.panel_width))?;
|
||||
}
|
||||
|
||||
writeln!(
|
||||
self.handle,
|
||||
"{}{}",
|
||||
filename.map_or("", |_| "File: "),
|
||||
self.colors.filename.paint(filename.unwrap_or("STDIN"))
|
||||
)?;
|
||||
|
||||
if self.config.output_components.grid() {
|
||||
self.print_horizontal_line('┼')?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn print_footer(&mut self) -> Result<()> {
|
||||
if self.config.output_components.grid() {
|
||||
self.print_horizontal_line('┴')
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_line(&mut self, regions: &[(highlighting::Style, &str)]) -> Result<()> {
|
||||
self.line_number += 1;
|
||||
let mut cursor: usize = 0;
|
||||
let mut cursor_max: usize = self.config.term_width;
|
||||
let mut panel_wrap: Option<String> = None;
|
||||
|
||||
// Line decorations.
|
||||
if self.panel_width > 0 {
|
||||
let decorations = self
|
||||
.decorations
|
||||
.iter()
|
||||
.map(|ref d| d.generate(self.line_number, false, self))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for deco in decorations {
|
||||
write!(self.handle, "{} ", deco.text)?;
|
||||
cursor_max -= deco.width + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Line contents.
|
||||
if self.config.output_wrap == OutputWrap::None {
|
||||
let true_color = self.config.true_color;
|
||||
let colored_output = self.config.colored_output;
|
||||
|
||||
write!(
|
||||
self.handle,
|
||||
"{}",
|
||||
regions
|
||||
.iter()
|
||||
.map(|&(style, text)| as_terminal_escaped(
|
||||
style,
|
||||
text,
|
||||
true_color,
|
||||
colored_output,
|
||||
)).collect::<Vec<_>>()
|
||||
.join("")
|
||||
)?;
|
||||
} else {
|
||||
for &(style, region) in regions.iter() {
|
||||
let mut ansi_iterator = AnsiCodeIterator::new(region);
|
||||
let mut ansi_prefix: String = String::new();
|
||||
for chunk in ansi_iterator {
|
||||
match chunk {
|
||||
// ANSI escape passthrough.
|
||||
(text, true) => {
|
||||
if text.chars().last().map_or(false, |c| c == 'm') {
|
||||
ansi_prefix.push_str(text);
|
||||
if text == "\x1B[0m" {
|
||||
self.ansi_prefix_sgr = "\x1B[0m".to_owned();
|
||||
} else {
|
||||
self.ansi_prefix_sgr.push_str(text);
|
||||
}
|
||||
} else {
|
||||
ansi_prefix.push_str(text);
|
||||
}
|
||||
}
|
||||
|
||||
// Regular text.
|
||||
(text, false) => {
|
||||
let text = text.trim_right_matches(|c| c == '\r' || c == '\n');
|
||||
let mut chars = text.chars();
|
||||
let mut remaining = text.chars().count();
|
||||
|
||||
while remaining > 0 {
|
||||
let available = cursor_max - cursor;
|
||||
|
||||
// It fits.
|
||||
if remaining <= available {
|
||||
let text = chars.by_ref().take(remaining).collect::<String>();
|
||||
cursor += remaining;
|
||||
|
||||
write!(
|
||||
self.handle,
|
||||
"{}",
|
||||
as_terminal_escaped(
|
||||
style,
|
||||
&*format!(
|
||||
"{}{}{}",
|
||||
self.ansi_prefix_sgr, ansi_prefix, text
|
||||
),
|
||||
self.config.true_color,
|
||||
self.config.colored_output,
|
||||
)
|
||||
)?;
|
||||
break;
|
||||
}
|
||||
|
||||
// Generate wrap padding if not already generated.
|
||||
if panel_wrap.is_none() {
|
||||
panel_wrap = if self.panel_width > 0 {
|
||||
Some(format!(
|
||||
"{} ",
|
||||
self.decorations
|
||||
.iter()
|
||||
.map(|ref d| d
|
||||
.generate(self.line_number, true, self)
|
||||
.text).collect::<Vec<String>>()
|
||||
.join(" ")
|
||||
))
|
||||
} else {
|
||||
Some("".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
// It wraps.
|
||||
let text = chars.by_ref().take(available).collect::<String>();
|
||||
cursor = 0;
|
||||
remaining -= available;
|
||||
|
||||
write!(
|
||||
self.handle,
|
||||
"{}\n{}",
|
||||
as_terminal_escaped(
|
||||
style,
|
||||
&*format!(
|
||||
"{}{}{}",
|
||||
self.ansi_prefix_sgr, ansi_prefix, text
|
||||
),
|
||||
self.config.true_color,
|
||||
self.config.colored_output,
|
||||
),
|
||||
panel_wrap.clone().unwrap()
|
||||
)?;
|
||||
}
|
||||
|
||||
// Clear the ANSI prefix buffer.
|
||||
ansi_prefix.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
write!(self.handle, "\n")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_horizontal_line(&mut self, grid_char: char) -> Result<()> {
|
||||
if self.panel_width == 0 {
|
||||
writeln!(
|
||||
self.handle,
|
||||
"{}",
|
||||
self.colors.grid.paint("─".repeat(self.config.term_width))
|
||||
)?;
|
||||
} else {
|
||||
let hline = "─".repeat(self.config.term_width - (self.panel_width + 1));
|
||||
let hline = format!("{}{}{}", "─".repeat(self.panel_width), grid_char, hline);
|
||||
writeln!(self.handle, "{}", self.colors.grid.paint(hline))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_GUTTER_COLOR: u8 = 238;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Colors {
|
||||
pub grid: Style,
|
||||
pub filename: Style,
|
||||
pub git_added: Style,
|
||||
pub git_removed: Style,
|
||||
pub git_modified: Style,
|
||||
pub line_number: Style,
|
||||
}
|
||||
|
||||
impl Colors {
|
||||
fn plain() -> Self {
|
||||
Colors::default()
|
||||
}
|
||||
|
||||
fn colored(theme: &Theme, true_color: bool) -> Self {
|
||||
let gutter_color = theme
|
||||
.settings
|
||||
.gutter_foreground
|
||||
.map(|c| to_ansi_color(c, true_color))
|
||||
.unwrap_or(Fixed(DEFAULT_GUTTER_COLOR));
|
||||
|
||||
Colors {
|
||||
grid: gutter_color.normal(),
|
||||
filename: Style::new().bold(),
|
||||
git_added: Green.normal(),
|
||||
git_removed: Red.normal(),
|
||||
git_modified: Yellow.normal(),
|
||||
line_number: gutter_color.normal(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
use errors::*;
|
||||
use std::collections::HashSet;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
|
||||
pub enum OutputComponent {
|
||||
Auto,
|
||||
Changes,
|
||||
Grid,
|
||||
Header,
|
||||
Numbers,
|
||||
Full,
|
||||
Plain,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
|
||||
pub enum OutputWrap {
|
||||
Character,
|
||||
None,
|
||||
}
|
||||
|
||||
impl OutputComponent {
|
||||
pub fn components(&self, interactive_terminal: bool) -> &'static [OutputComponent] {
|
||||
match *self {
|
||||
OutputComponent::Auto => if interactive_terminal {
|
||||
OutputComponent::Full.components(interactive_terminal)
|
||||
} else {
|
||||
OutputComponent::Plain.components(interactive_terminal)
|
||||
},
|
||||
OutputComponent::Changes => &[OutputComponent::Changes],
|
||||
OutputComponent::Grid => &[OutputComponent::Grid],
|
||||
OutputComponent::Header => &[OutputComponent::Header],
|
||||
OutputComponent::Numbers => &[OutputComponent::Numbers],
|
||||
OutputComponent::Full => &[
|
||||
OutputComponent::Changes,
|
||||
OutputComponent::Grid,
|
||||
OutputComponent::Header,
|
||||
OutputComponent::Numbers,
|
||||
],
|
||||
OutputComponent::Plain => &[],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for OutputComponent {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
match s {
|
||||
"auto" => Ok(OutputComponent::Auto),
|
||||
"changes" => Ok(OutputComponent::Changes),
|
||||
"grid" => Ok(OutputComponent::Grid),
|
||||
"header" => Ok(OutputComponent::Header),
|
||||
"numbers" => Ok(OutputComponent::Numbers),
|
||||
"full" => Ok(OutputComponent::Full),
|
||||
"plain" | _ => Ok(OutputComponent::Plain),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OutputComponents(pub HashSet<OutputComponent>);
|
||||
|
||||
impl OutputComponents {
|
||||
pub fn changes(&self) -> bool {
|
||||
self.0.contains(&OutputComponent::Changes)
|
||||
}
|
||||
|
||||
pub fn grid(&self) -> bool {
|
||||
self.0.contains(&OutputComponent::Grid)
|
||||
}
|
||||
|
||||
pub fn header(&self) -> bool {
|
||||
self.0.contains(&OutputComponent::Header)
|
||||
}
|
||||
|
||||
pub fn numbers(&self) -> bool {
|
||||
self.0.contains(&OutputComponent::Numbers)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
use ansi_term::Colour::{Fixed, RGB};
|
||||
use ansi_term::{self, Style};
|
||||
use syntect::highlighting::{self, FontStyle};
|
||||
|
||||
/// Approximate a 24 bit color value by a 8 bit ANSI code
|
||||
fn rgb2ansi(r: u8, g: u8, b: u8) -> u8 {
|
||||
const BLACK: u8 = 16;
|
||||
const WHITE: u8 = 231;
|
||||
|
||||
if r == g && g == b {
|
||||
if r < 8 {
|
||||
BLACK
|
||||
} else if r > 248 {
|
||||
WHITE
|
||||
} else {
|
||||
((r - 8) as u16 * 24 / 247) as u8 + 232
|
||||
}
|
||||
} else {
|
||||
36 * (r / 51) + 6 * (g / 51) + (b / 51) + 16
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_ansi_color(color: highlighting::Color, true_color: bool) -> ansi_term::Colour {
|
||||
if true_color {
|
||||
RGB(color.r, color.g, color.b)
|
||||
} else {
|
||||
let ansi_code = rgb2ansi(color.r, color.g, color.b);
|
||||
Fixed(ansi_code)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_terminal_escaped(
|
||||
style: highlighting::Style,
|
||||
text: &str,
|
||||
true_color: bool,
|
||||
colored: bool,
|
||||
) -> String {
|
||||
let style = if !colored {
|
||||
Style::default()
|
||||
} else {
|
||||
let color = to_ansi_color(style.foreground, true_color);
|
||||
|
||||
if style.font_style.contains(FontStyle::BOLD) {
|
||||
color.bold()
|
||||
} else if style.font_style.contains(FontStyle::UNDERLINE) {
|
||||
color.underline()
|
||||
} else if style.font_style.contains(FontStyle::ITALIC) {
|
||||
color.italic()
|
||||
} else {
|
||||
color.normal()
|
||||
}
|
||||
};
|
||||
|
||||
style.paint(text).to_string()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rgb2ansi_black_white() {
|
||||
assert_eq!(16, rgb2ansi(0x00, 0x00, 0x00));
|
||||
assert_eq!(231, rgb2ansi(0xff, 0xff, 0xff));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rgb2ansi_gray() {
|
||||
assert_eq!(241, rgb2ansi(0x6c, 0x6c, 0x6c));
|
||||
assert_eq!(233, rgb2ansi(0x1c, 0x1c, 0x1c));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rgb2ansi_color() {
|
||||
assert_eq!(96, rgb2ansi(0x87, 0x5f, 0x87));
|
||||
assert_eq!(141, rgb2ansi(0xaf, 0x87, 0xff));
|
||||
assert_eq!(193, rgb2ansi(0xd7, 0xff, 0xaf));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rgb2ansi_approx() {
|
||||
assert_eq!(231, rgb2ansi(0xfe, 0xfe, 0xfe));
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
test-src/* linguist-vendored
|
||||
@@ -0,0 +1 @@
|
||||
test-src/*
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
if ! which 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
|
||||
fi
|
||||
|
||||
SRC="test-src/jquery-3.3.1.js"
|
||||
|
||||
|
||||
cmd_bat="bat --style=full --color=always --paging=never '$SRC'"
|
||||
cmd_pygmentize="pygmentize -g '$SRC'"
|
||||
cmd_highlight="highlight -O truecolor '$SRC'"
|
||||
cmd_ccat="ccat --color=always '$SRC'"
|
||||
cmd_source_highlight="source-highlight --failsafe --infer-lang -f esc -i '$SRC'"
|
||||
cmd_hicat="hicat '$SRC'"
|
||||
cmd_coderay="coderay '$SRC'"
|
||||
cmd_rouge="rougify '$SRC'"
|
||||
|
||||
hyperfine --warmup 3 \
|
||||
"$cmd_bat" \
|
||||
"$cmd_pygmentize" \
|
||||
"$cmd_highlight" \
|
||||
"$cmd_ccat" \
|
||||
"$cmd_source_highlight" \
|
||||
"$cmd_hicat" \
|
||||
"$cmd_coderay" \
|
||||
"$cmd_rouge" \
|
||||
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
if ! which 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
|
||||
fi
|
||||
|
||||
echo "### Startup time"
|
||||
echo
|
||||
|
||||
hyperfine --warmup 3 bat
|
||||
|
||||
echo
|
||||
echo "### Plain text"
|
||||
echo
|
||||
|
||||
hyperfine --warmup 3 "bat --language txt --paging=never 'test-src/jquery-3.3.1.js'"
|
||||
|
||||
echo
|
||||
echo "### Time to syntax-highlight large files"
|
||||
echo
|
||||
|
||||
for SRC in test-src/*; do
|
||||
hyperfine --warmup 3 "bat --style=full --color=always --paging=never '$SRC'"
|
||||
done
|
||||
+10364
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Vendored
+602
@@ -0,0 +1,602 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright 2013-2014 RAD Game Tools and Valve Software
|
||||
* Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
#include "miniz.h"
|
||||
|
||||
typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1];
|
||||
typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1];
|
||||
typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1];
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ------------------- zlib-style API's */
|
||||
|
||||
mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len)
|
||||
{
|
||||
mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16);
|
||||
size_t block_len = buf_len % 5552;
|
||||
if (!ptr)
|
||||
return MZ_ADLER32_INIT;
|
||||
while (buf_len)
|
||||
{
|
||||
for (i = 0; i + 7 < block_len; i += 8, ptr += 8)
|
||||
{
|
||||
s1 += ptr[0], s2 += s1;
|
||||
s1 += ptr[1], s2 += s1;
|
||||
s1 += ptr[2], s2 += s1;
|
||||
s1 += ptr[3], s2 += s1;
|
||||
s1 += ptr[4], s2 += s1;
|
||||
s1 += ptr[5], s2 += s1;
|
||||
s1 += ptr[6], s2 += s1;
|
||||
s1 += ptr[7], s2 += s1;
|
||||
}
|
||||
for (; i < block_len; ++i)
|
||||
s1 += *ptr++, s2 += s1;
|
||||
s1 %= 65521U, s2 %= 65521U;
|
||||
buf_len -= block_len;
|
||||
block_len = 5552;
|
||||
}
|
||||
return (s2 << 16) + s1;
|
||||
}
|
||||
|
||||
/* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ */
|
||||
#if 0
|
||||
mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len)
|
||||
{
|
||||
static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
|
||||
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c };
|
||||
mz_uint32 crcu32 = (mz_uint32)crc;
|
||||
if (!ptr)
|
||||
return MZ_CRC32_INIT;
|
||||
crcu32 = ~crcu32;
|
||||
while (buf_len--)
|
||||
{
|
||||
mz_uint8 b = *ptr++;
|
||||
crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)];
|
||||
crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)];
|
||||
}
|
||||
return ~crcu32;
|
||||
}
|
||||
#else
|
||||
/* Faster, but larger CPU cache footprint.
|
||||
*/
|
||||
mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len)
|
||||
{
|
||||
static const mz_uint32 s_crc_table[256] =
|
||||
{
|
||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535,
|
||||
0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD,
|
||||
0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D,
|
||||
0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
|
||||
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
|
||||
0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
|
||||
0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC,
|
||||
0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
|
||||
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB,
|
||||
0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F,
|
||||
0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB,
|
||||
0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
|
||||
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA,
|
||||
0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE,
|
||||
0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A,
|
||||
0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
|
||||
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409,
|
||||
0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
|
||||
0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739,
|
||||
0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
|
||||
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268,
|
||||
0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0,
|
||||
0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8,
|
||||
0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
|
||||
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF,
|
||||
0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703,
|
||||
0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7,
|
||||
0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
|
||||
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE,
|
||||
0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
|
||||
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6,
|
||||
0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
|
||||
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D,
|
||||
0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5,
|
||||
0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605,
|
||||
0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
|
||||
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
|
||||
};
|
||||
|
||||
mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF;
|
||||
const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr;
|
||||
|
||||
while (buf_len >= 4)
|
||||
{
|
||||
crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF];
|
||||
crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF];
|
||||
crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF];
|
||||
crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF];
|
||||
pByte_buf += 4;
|
||||
buf_len -= 4;
|
||||
}
|
||||
|
||||
while (buf_len)
|
||||
{
|
||||
crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF];
|
||||
++pByte_buf;
|
||||
--buf_len;
|
||||
}
|
||||
|
||||
return ~crc32;
|
||||
}
|
||||
#endif
|
||||
|
||||
void mz_free(void *p)
|
||||
{
|
||||
MZ_FREE(p);
|
||||
}
|
||||
|
||||
void *miniz_def_alloc_func(void *opaque, size_t items, size_t size)
|
||||
{
|
||||
(void)opaque, (void)items, (void)size;
|
||||
return MZ_MALLOC(items * size);
|
||||
}
|
||||
void miniz_def_free_func(void *opaque, void *address)
|
||||
{
|
||||
(void)opaque, (void)address;
|
||||
MZ_FREE(address);
|
||||
}
|
||||
void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size)
|
||||
{
|
||||
(void)opaque, (void)address, (void)items, (void)size;
|
||||
return MZ_REALLOC(address, items * size);
|
||||
}
|
||||
|
||||
const char *mz_version(void)
|
||||
{
|
||||
return MZ_VERSION;
|
||||
}
|
||||
|
||||
#ifndef MINIZ_NO_ZLIB_APIS
|
||||
|
||||
int mz_deflateInit(mz_streamp pStream, int level)
|
||||
{
|
||||
return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY);
|
||||
}
|
||||
|
||||
int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy)
|
||||
{
|
||||
tdefl_compressor *pComp;
|
||||
mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy);
|
||||
|
||||
if (!pStream)
|
||||
return MZ_STREAM_ERROR;
|
||||
if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)))
|
||||
return MZ_PARAM_ERROR;
|
||||
|
||||
pStream->data_type = 0;
|
||||
pStream->adler = MZ_ADLER32_INIT;
|
||||
pStream->msg = NULL;
|
||||
pStream->reserved = 0;
|
||||
pStream->total_in = 0;
|
||||
pStream->total_out = 0;
|
||||
if (!pStream->zalloc)
|
||||
pStream->zalloc = miniz_def_alloc_func;
|
||||
if (!pStream->zfree)
|
||||
pStream->zfree = miniz_def_free_func;
|
||||
|
||||
pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor));
|
||||
if (!pComp)
|
||||
return MZ_MEM_ERROR;
|
||||
|
||||
pStream->state = (struct mz_internal_state *)pComp;
|
||||
|
||||
if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY)
|
||||
{
|
||||
mz_deflateEnd(pStream);
|
||||
return MZ_PARAM_ERROR;
|
||||
}
|
||||
|
||||
return MZ_OK;
|
||||
}
|
||||
|
||||
int mz_deflateReset(mz_streamp pStream)
|
||||
{
|
||||
if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree))
|
||||
return MZ_STREAM_ERROR;
|
||||
pStream->total_in = pStream->total_out = 0;
|
||||
tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags);
|
||||
return MZ_OK;
|
||||
}
|
||||
|
||||
int mz_deflate(mz_streamp pStream, int flush)
|
||||
{
|
||||
size_t in_bytes, out_bytes;
|
||||
mz_ulong orig_total_in, orig_total_out;
|
||||
int mz_status = MZ_OK;
|
||||
|
||||
if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out))
|
||||
return MZ_STREAM_ERROR;
|
||||
if (!pStream->avail_out)
|
||||
return MZ_BUF_ERROR;
|
||||
|
||||
if (flush == MZ_PARTIAL_FLUSH)
|
||||
flush = MZ_SYNC_FLUSH;
|
||||
|
||||
if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE)
|
||||
return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR;
|
||||
|
||||
orig_total_in = pStream->total_in;
|
||||
orig_total_out = pStream->total_out;
|
||||
for (;;)
|
||||
{
|
||||
tdefl_status defl_status;
|
||||
in_bytes = pStream->avail_in;
|
||||
out_bytes = pStream->avail_out;
|
||||
|
||||
defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush);
|
||||
pStream->next_in += (mz_uint)in_bytes;
|
||||
pStream->avail_in -= (mz_uint)in_bytes;
|
||||
pStream->total_in += (mz_uint)in_bytes;
|
||||
pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state);
|
||||
|
||||
pStream->next_out += (mz_uint)out_bytes;
|
||||
pStream->avail_out -= (mz_uint)out_bytes;
|
||||
pStream->total_out += (mz_uint)out_bytes;
|
||||
|
||||
if (defl_status < 0)
|
||||
{
|
||||
mz_status = MZ_STREAM_ERROR;
|
||||
break;
|
||||
}
|
||||
else if (defl_status == TDEFL_STATUS_DONE)
|
||||
{
|
||||
mz_status = MZ_STREAM_END;
|
||||
break;
|
||||
}
|
||||
else if (!pStream->avail_out)
|
||||
break;
|
||||
else if ((!pStream->avail_in) && (flush != MZ_FINISH))
|
||||
{
|
||||
if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out))
|
||||
break;
|
||||
return MZ_BUF_ERROR; /* Can't make forward progress without some input.
|
||||
*/
|
||||
}
|
||||
}
|
||||
return mz_status;
|
||||
}
|
||||
|
||||
int mz_deflateEnd(mz_streamp pStream)
|
||||
{
|
||||
if (!pStream)
|
||||
return MZ_STREAM_ERROR;
|
||||
if (pStream->state)
|
||||
{
|
||||
pStream->zfree(pStream->opaque, pStream->state);
|
||||
pStream->state = NULL;
|
||||
}
|
||||
return MZ_OK;
|
||||
}
|
||||
|
||||
mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len)
|
||||
{
|
||||
(void)pStream;
|
||||
/* This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) */
|
||||
return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5);
|
||||
}
|
||||
|
||||
int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level)
|
||||
{
|
||||
int status;
|
||||
mz_stream stream;
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
|
||||
/* In case mz_ulong is 64-bits (argh I hate longs). */
|
||||
if ((source_len | *pDest_len) > 0xFFFFFFFFU)
|
||||
return MZ_PARAM_ERROR;
|
||||
|
||||
stream.next_in = pSource;
|
||||
stream.avail_in = (mz_uint32)source_len;
|
||||
stream.next_out = pDest;
|
||||
stream.avail_out = (mz_uint32)*pDest_len;
|
||||
|
||||
status = mz_deflateInit(&stream, level);
|
||||
if (status != MZ_OK)
|
||||
return status;
|
||||
|
||||
status = mz_deflate(&stream, MZ_FINISH);
|
||||
if (status != MZ_STREAM_END)
|
||||
{
|
||||
mz_deflateEnd(&stream);
|
||||
return (status == MZ_OK) ? MZ_BUF_ERROR : status;
|
||||
}
|
||||
|
||||
*pDest_len = stream.total_out;
|
||||
return mz_deflateEnd(&stream);
|
||||
}
|
||||
|
||||
int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)
|
||||
{
|
||||
return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION);
|
||||
}
|
||||
|
||||
mz_ulong mz_compressBound(mz_ulong source_len)
|
||||
{
|
||||
return mz_deflateBound(NULL, source_len);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tinfl_decompressor m_decomp;
|
||||
mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed;
|
||||
int m_window_bits;
|
||||
mz_uint8 m_dict[TINFL_LZ_DICT_SIZE];
|
||||
tinfl_status m_last_status;
|
||||
} inflate_state;
|
||||
|
||||
int mz_inflateInit2(mz_streamp pStream, int window_bits)
|
||||
{
|
||||
inflate_state *pDecomp;
|
||||
if (!pStream)
|
||||
return MZ_STREAM_ERROR;
|
||||
if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))
|
||||
return MZ_PARAM_ERROR;
|
||||
|
||||
pStream->data_type = 0;
|
||||
pStream->adler = 0;
|
||||
pStream->msg = NULL;
|
||||
pStream->total_in = 0;
|
||||
pStream->total_out = 0;
|
||||
pStream->reserved = 0;
|
||||
if (!pStream->zalloc)
|
||||
pStream->zalloc = miniz_def_alloc_func;
|
||||
if (!pStream->zfree)
|
||||
pStream->zfree = miniz_def_free_func;
|
||||
|
||||
pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state));
|
||||
if (!pDecomp)
|
||||
return MZ_MEM_ERROR;
|
||||
|
||||
pStream->state = (struct mz_internal_state *)pDecomp;
|
||||
|
||||
tinfl_init(&pDecomp->m_decomp);
|
||||
pDecomp->m_dict_ofs = 0;
|
||||
pDecomp->m_dict_avail = 0;
|
||||
pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT;
|
||||
pDecomp->m_first_call = 1;
|
||||
pDecomp->m_has_flushed = 0;
|
||||
pDecomp->m_window_bits = window_bits;
|
||||
|
||||
return MZ_OK;
|
||||
}
|
||||
|
||||
int mz_inflateInit(mz_streamp pStream)
|
||||
{
|
||||
return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS);
|
||||
}
|
||||
|
||||
int mz_inflate(mz_streamp pStream, int flush)
|
||||
{
|
||||
inflate_state *pState;
|
||||
mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32;
|
||||
size_t in_bytes, out_bytes, orig_avail_in;
|
||||
tinfl_status status;
|
||||
|
||||
if ((!pStream) || (!pStream->state))
|
||||
return MZ_STREAM_ERROR;
|
||||
if (flush == MZ_PARTIAL_FLUSH)
|
||||
flush = MZ_SYNC_FLUSH;
|
||||
if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH))
|
||||
return MZ_STREAM_ERROR;
|
||||
|
||||
pState = (inflate_state *)pStream->state;
|
||||
if (pState->m_window_bits > 0)
|
||||
decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER;
|
||||
orig_avail_in = pStream->avail_in;
|
||||
|
||||
first_call = pState->m_first_call;
|
||||
pState->m_first_call = 0;
|
||||
if (pState->m_last_status < 0)
|
||||
return MZ_DATA_ERROR;
|
||||
|
||||
if (pState->m_has_flushed && (flush != MZ_FINISH))
|
||||
return MZ_STREAM_ERROR;
|
||||
pState->m_has_flushed |= (flush == MZ_FINISH);
|
||||
|
||||
if ((flush == MZ_FINISH) && (first_call))
|
||||
{
|
||||
/* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */
|
||||
decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF;
|
||||
in_bytes = pStream->avail_in;
|
||||
out_bytes = pStream->avail_out;
|
||||
status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags);
|
||||
pState->m_last_status = status;
|
||||
pStream->next_in += (mz_uint)in_bytes;
|
||||
pStream->avail_in -= (mz_uint)in_bytes;
|
||||
pStream->total_in += (mz_uint)in_bytes;
|
||||
pStream->adler = tinfl_get_adler32(&pState->m_decomp);
|
||||
pStream->next_out += (mz_uint)out_bytes;
|
||||
pStream->avail_out -= (mz_uint)out_bytes;
|
||||
pStream->total_out += (mz_uint)out_bytes;
|
||||
|
||||
if (status < 0)
|
||||
return MZ_DATA_ERROR;
|
||||
else if (status != TINFL_STATUS_DONE)
|
||||
{
|
||||
pState->m_last_status = TINFL_STATUS_FAILED;
|
||||
return MZ_BUF_ERROR;
|
||||
}
|
||||
return MZ_STREAM_END;
|
||||
}
|
||||
/* flush != MZ_FINISH then we must assume there's more input. */
|
||||
if (flush != MZ_FINISH)
|
||||
decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT;
|
||||
|
||||
if (pState->m_dict_avail)
|
||||
{
|
||||
n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
|
||||
memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
|
||||
pStream->next_out += n;
|
||||
pStream->avail_out -= n;
|
||||
pStream->total_out += n;
|
||||
pState->m_dict_avail -= n;
|
||||
pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
|
||||
return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
in_bytes = pStream->avail_in;
|
||||
out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs;
|
||||
|
||||
status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags);
|
||||
pState->m_last_status = status;
|
||||
|
||||
pStream->next_in += (mz_uint)in_bytes;
|
||||
pStream->avail_in -= (mz_uint)in_bytes;
|
||||
pStream->total_in += (mz_uint)in_bytes;
|
||||
pStream->adler = tinfl_get_adler32(&pState->m_decomp);
|
||||
|
||||
pState->m_dict_avail = (mz_uint)out_bytes;
|
||||
|
||||
n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
|
||||
memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
|
||||
pStream->next_out += n;
|
||||
pStream->avail_out -= n;
|
||||
pStream->total_out += n;
|
||||
pState->m_dict_avail -= n;
|
||||
pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
|
||||
|
||||
if (status < 0)
|
||||
return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */
|
||||
else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in))
|
||||
return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */
|
||||
else if (flush == MZ_FINISH)
|
||||
{
|
||||
/* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */
|
||||
if (status == TINFL_STATUS_DONE)
|
||||
return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END;
|
||||
/* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */
|
||||
else if (!pStream->avail_out)
|
||||
return MZ_BUF_ERROR;
|
||||
}
|
||||
else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail))
|
||||
break;
|
||||
}
|
||||
|
||||
return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
|
||||
}
|
||||
|
||||
int mz_inflateEnd(mz_streamp pStream)
|
||||
{
|
||||
if (!pStream)
|
||||
return MZ_STREAM_ERROR;
|
||||
if (pStream->state)
|
||||
{
|
||||
pStream->zfree(pStream->opaque, pStream->state);
|
||||
pStream->state = NULL;
|
||||
}
|
||||
return MZ_OK;
|
||||
}
|
||||
|
||||
int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)
|
||||
{
|
||||
mz_stream stream;
|
||||
int status;
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
|
||||
/* In case mz_ulong is 64-bits (argh I hate longs). */
|
||||
if ((source_len | *pDest_len) > 0xFFFFFFFFU)
|
||||
return MZ_PARAM_ERROR;
|
||||
|
||||
stream.next_in = pSource;
|
||||
stream.avail_in = (mz_uint32)source_len;
|
||||
stream.next_out = pDest;
|
||||
stream.avail_out = (mz_uint32)*pDest_len;
|
||||
|
||||
status = mz_inflateInit(&stream);
|
||||
if (status != MZ_OK)
|
||||
return status;
|
||||
|
||||
status = mz_inflate(&stream, MZ_FINISH);
|
||||
if (status != MZ_STREAM_END)
|
||||
{
|
||||
mz_inflateEnd(&stream);
|
||||
return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status;
|
||||
}
|
||||
*pDest_len = stream.total_out;
|
||||
|
||||
return mz_inflateEnd(&stream);
|
||||
}
|
||||
|
||||
const char *mz_error(int err)
|
||||
{
|
||||
static struct
|
||||
{
|
||||
int m_err;
|
||||
const char *m_pDesc;
|
||||
} s_error_descs[] =
|
||||
{
|
||||
{ MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" }
|
||||
};
|
||||
mz_uint i;
|
||||
for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i)
|
||||
if (s_error_descs[i].m_err == err)
|
||||
return s_error_descs[i].m_pDesc;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /*MINIZ_NO_ZLIB_APIS */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
*/
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python3
|
||||
import itertools
|
||||
import subprocess
|
||||
import pathlib
|
||||
import shutil
|
||||
|
||||
def generate_snapshots():
|
||||
single_styles = ["changes", "grid", "header", "numbers"]
|
||||
collective_styles = ["full", "plain"]
|
||||
|
||||
for num in range(len(single_styles)):
|
||||
for grouped in itertools.combinations(single_styles, num + 1):
|
||||
generate_snapshot(",".join(grouped))
|
||||
|
||||
for style in collective_styles:
|
||||
generate_snapshot(style)
|
||||
|
||||
def generate_snapshot(option):
|
||||
command = "../../target/debug/bat --style={0} sample.rs > output/{0}.snapshot.txt".format(
|
||||
option
|
||||
)
|
||||
print("generating snapshot for {}".format(option))
|
||||
subprocess.call(command, shell=True)
|
||||
|
||||
def build_bat():
|
||||
print("building bat")
|
||||
subprocess.call("cargo build", cwd="../..", shell=True)
|
||||
|
||||
def prepare_output_dir():
|
||||
shutil.rmtree("output", ignore_errors=True)
|
||||
pathlib.Path("output").mkdir()
|
||||
|
||||
def modify_sample_file():
|
||||
print("modifying sample.rs to show changes")
|
||||
shutil.copyfile("sample.modified.rs", "sample.rs")
|
||||
|
||||
def undo_sample_file_modification():
|
||||
print("undoing sample.rs modifications")
|
||||
subprocess.call("git checkout -- sample.rs", shell=True)
|
||||
|
||||
build_bat()
|
||||
prepare_output_dir()
|
||||
modify_sample_file()
|
||||
generate_snapshots()
|
||||
undo_sample_file_modification()
|
||||
@@ -0,0 +1,26 @@
|
||||
───────┬────────────────────────────────────────────────────────────────────────
|
||||
│ 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: "�����"#;
|
||||
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 + │ }
|
||||
───────┴────────────────────────────────────────────────────────────────────────
|
||||
@@ -0,0 +1,26 @@
|
||||
──┬─────────────────────────────────────────────────────────────────────────────
|
||||
│ File: sample.rs
|
||||
──┼─────────────────────────────────────────────────────────────────────────────
|
||||
│ struct Rectangle {
|
||||
│ width: u32,
|
||||
│ height: u32,
|
||||
│ }
|
||||
│
|
||||
_ │ fn main() {
|
||||
│ let rect1 = Rectangle { width: 30, height: 50 };
|
||||
│
|
||||
│ println!(
|
||||
~ │ "The perimeter of the rectangle is {} pixels.",
|
||||
~ │ perimeter(&rect1)
|
||||
│ );
|
||||
+ │ println!(r#"This line contains invalid utf8: "�����"#;
|
||||
│ }
|
||||
│
|
||||
│ fn area(rectangle: &Rectangle) -> u32 {
|
||||
│ rectangle.width * rectangle.height
|
||||
│ }
|
||||
+ │
|
||||
+ │ fn perimeter(rectangle: &Rectangle) -> u32 {
|
||||
+ │ (rectangle.width + rectangle.height) * 2
|
||||
+ │ }
|
||||
──┴─────────────────────────────────────────────────────────────────────────────
|
||||
@@ -0,0 +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: "�����"#;
|
||||
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 + │ }
|
||||
───────┴────────────────────────────────────────────────────────────────────────
|
||||
@@ -0,0 +1,23 @@
|
||||
│ struct Rectangle {
|
||||
│ width: u32,
|
||||
│ height: u32,
|
||||
│ }
|
||||
│
|
||||
_ │ fn main() {
|
||||
│ let rect1 = Rectangle { width: 30, height: 50 };
|
||||
│
|
||||
│ println!(
|
||||
~ │ "The perimeter of the rectangle is {} pixels.",
|
||||
~ │ perimeter(&rect1)
|
||||
│ );
|
||||
+ │ println!(r#"This line contains invalid utf8: "�����"#;
|
||||
│ }
|
||||
│
|
||||
│ fn area(rectangle: &Rectangle) -> u32 {
|
||||
│ rectangle.width * rectangle.height
|
||||
│ }
|
||||
+ │
|
||||
+ │ fn perimeter(rectangle: &Rectangle) -> u32 {
|
||||
+ │ (rectangle.width + rectangle.height) * 2
|
||||
+ │ }
|
||||
──┴─────────────────────────────────────────────────────────────────────────────
|
||||
@@ -0,0 +1,23 @@
|
||||
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: "�����"#;
|
||||
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 + }
|
||||
@@ -0,0 +1,23 @@
|
||||
File: sample.rs
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
_ fn main() {
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
|
||||
println!(
|
||||
~ "The perimeter of the rectangle is {} pixels.",
|
||||
~ perimeter(&rect1)
|
||||
);
|
||||
+ println!(r#"This line contains invalid utf8: "�����"#;
|
||||
}
|
||||
|
||||
fn area(rectangle: &Rectangle) -> u32 {
|
||||
rectangle.width * rectangle.height
|
||||
}
|
||||
+
|
||||
+ fn perimeter(rectangle: &Rectangle) -> u32 {
|
||||
+ (rectangle.width + rectangle.height) * 2
|
||||
+ }
|
||||
@@ -0,0 +1,22 @@
|
||||
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: "�����"#;
|
||||
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 + }
|
||||
@@ -0,0 +1,22 @@
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
_ fn main() {
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
|
||||
println!(
|
||||
~ "The perimeter of the rectangle is {} pixels.",
|
||||
~ perimeter(&rect1)
|
||||
);
|
||||
+ println!(r#"This line contains invalid utf8: "�����"#;
|
||||
}
|
||||
|
||||
fn area(rectangle: &Rectangle) -> u32 {
|
||||
rectangle.width * rectangle.height
|
||||
}
|
||||
+
|
||||
+ fn perimeter(rectangle: &Rectangle) -> u32 {
|
||||
+ (rectangle.width + rectangle.height) * 2
|
||||
+ }
|
||||
@@ -0,0 +1,26 @@
|
||||
───────┬────────────────────────────────────────────────────────────────────────
|
||||
│ 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: "�����"#;
|
||||
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 + │ }
|
||||
───────┴────────────────────────────────────────────────────────────────────────
|
||||
@@ -0,0 +1,26 @@
|
||||
─────┬──────────────────────────────────────────────────────────────────────────
|
||||
│ 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: "�����"#;
|
||||
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 │ }
|
||||
─────┴──────────────────────────────────────────────────────────────────────────
|
||||
@@ -0,0 +1,26 @@
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
File: sample.rs
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
|
||||
println!(
|
||||
"The perimeter of the rectangle is {} pixels.",
|
||||
perimeter(&rect1)
|
||||
);
|
||||
println!(r#"This line contains invalid utf8: "�����"#;
|
||||
}
|
||||
|
||||
fn area(rectangle: &Rectangle) -> u32 {
|
||||
rectangle.width * rectangle.height
|
||||
}
|
||||
|
||||
fn perimeter(rectangle: &Rectangle) -> u32 {
|
||||
(rectangle.width + rectangle.height) * 2
|
||||
}
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
@@ -0,0 +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: "�����"#;
|
||||
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 │ }
|
||||
─────┴──────────────────────────────────────────────────────────────────────────
|
||||
@@ -0,0 +1,23 @@
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
|
||||
println!(
|
||||
"The perimeter of the rectangle is {} pixels.",
|
||||
perimeter(&rect1)
|
||||
);
|
||||
println!(r#"This line contains invalid utf8: "�����"#;
|
||||
}
|
||||
|
||||
fn area(rectangle: &Rectangle) -> u32 {
|
||||
rectangle.width * rectangle.height
|
||||
}
|
||||
|
||||
fn perimeter(rectangle: &Rectangle) -> u32 {
|
||||
(rectangle.width + rectangle.height) * 2
|
||||
}
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
@@ -0,0 +1,23 @@
|
||||
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: "�����"#;
|
||||
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 }
|
||||
@@ -0,0 +1,23 @@
|
||||
File: sample.rs
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
|
||||
println!(
|
||||
"The perimeter of the rectangle is {} pixels.",
|
||||
perimeter(&rect1)
|
||||
);
|
||||
println!(r#"This line contains invalid utf8: "�����"#;
|
||||
}
|
||||
|
||||
fn area(rectangle: &Rectangle) -> u32 {
|
||||
rectangle.width * rectangle.height
|
||||
}
|
||||
|
||||
fn perimeter(rectangle: &Rectangle) -> u32 {
|
||||
(rectangle.width + rectangle.height) * 2
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
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: "�����"#;
|
||||
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 }
|
||||
@@ -0,0 +1,22 @@
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
|
||||
println!(
|
||||
"The perimeter of the rectangle is {} pixels.",
|
||||
perimeter(&rect1)
|
||||
);
|
||||
println!(r#"This line contains invalid utf8: "�����"#;
|
||||
}
|
||||
|
||||
fn area(rectangle: &Rectangle) -> u32 {
|
||||
rectangle.width * rectangle.height
|
||||
}
|
||||
|
||||
fn perimeter(rectangle: &Rectangle) -> u32 {
|
||||
(rectangle.width + rectangle.height) * 2
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
|
||||
println!(
|
||||
"The perimeter of the rectangle is {} pixels.",
|
||||
perimeter(&rect1)
|
||||
);
|
||||
println!(r#"This line contains invalid utf8: "øˆ€€€"#;
|
||||
}
|
||||
|
||||
fn area(rectangle: &Rectangle) -> u32 {
|
||||
rectangle.width * rectangle.height
|
||||
}
|
||||
|
||||
fn perimeter(rectangle: &Rectangle) -> u32 {
|
||||
(rectangle.width + rectangle.height) * 2
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// width and height of a rectangle can be different
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
|
||||
println!(
|
||||
"The area of the rectangle is {} square pixels.",
|
||||
area(&rect1)
|
||||
);
|
||||
}
|
||||
|
||||
fn area(rectangle: &Rectangle) -> u32 {
|
||||
rectangle.width * rectangle.height
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
use std::env;
|
||||
use std::fs::{self, File};
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
extern crate tempdir;
|
||||
use self::tempdir::TempDir;
|
||||
|
||||
extern crate git2;
|
||||
use self::git2::build::CheckoutBuilder;
|
||||
use self::git2::Repository;
|
||||
use self::git2::Signature;
|
||||
|
||||
pub struct BatTester {
|
||||
/// Temporary working directory
|
||||
temp_dir: TempDir,
|
||||
|
||||
/// Path to the *bat* executable
|
||||
exe: PathBuf,
|
||||
}
|
||||
|
||||
impl BatTester {
|
||||
pub fn new() -> Self {
|
||||
let temp_dir = create_sample_directory().expect("sample directory");
|
||||
|
||||
let root = env::current_exe()
|
||||
.expect("tests executable")
|
||||
.parent()
|
||||
.expect("tests executable directory")
|
||||
.parent()
|
||||
.expect("bat executable directory")
|
||||
.to_path_buf();
|
||||
|
||||
let exe_name = if cfg!(windows) { "bat.exe" } else { "bat" };
|
||||
let exe = root.join(exe_name);
|
||||
|
||||
BatTester { temp_dir, exe }
|
||||
}
|
||||
|
||||
pub fn test_snapshot(&self, style: &str) {
|
||||
let output = Command::new(&self.exe)
|
||||
.current_dir(self.temp_dir.path())
|
||||
.args(&["sample.rs", &format!("--style={}", style)])
|
||||
.output()
|
||||
.expect("bat failed");
|
||||
|
||||
// have to do the replace because the filename in the header changes based on the current working directory
|
||||
let actual = String::from_utf8_lossy(&output.stdout)
|
||||
.as_ref()
|
||||
.replace("tests/snapshots/", "");
|
||||
|
||||
let mut expected = String::new();
|
||||
let mut file = File::open(format!("tests/snapshots/output/{}.snapshot.txt", style))
|
||||
.expect("snapshot file missing");
|
||||
file.read_to_string(&mut expected)
|
||||
.expect("could not read snapshot file");
|
||||
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
fn create_sample_directory() -> Result<TempDir, git2::Error> {
|
||||
// Create temp directory and initialize repository
|
||||
let temp_dir = TempDir::new("bat-tests").expect("Temp directory");
|
||||
let repo = Repository::init(&temp_dir)?;
|
||||
|
||||
// Copy over `sample.rs`
|
||||
let sample_path = temp_dir.path().join("sample.rs");
|
||||
println!("{:?}", &sample_path);
|
||||
fs::copy("tests/snapshots/sample.rs", &sample_path).expect("successful copy");
|
||||
|
||||
// Commit
|
||||
let mut index = repo.index()?;
|
||||
index.add_path(Path::new("sample.rs"))?;
|
||||
|
||||
let oid = index.write_tree()?;
|
||||
let signature = Signature::now("bat test runner", "bat@test.runner")?;
|
||||
let tree = repo.find_tree(oid)?;
|
||||
let _ = repo.commit(
|
||||
Some("HEAD"), // point HEAD to our new commit
|
||||
&signature, // author
|
||||
&signature, // committer
|
||||
"initial commit",
|
||||
&tree,
|
||||
&[],
|
||||
);
|
||||
let mut opts = CheckoutBuilder::new();
|
||||
repo.checkout_head(Some(opts.force()))?;
|
||||
|
||||
fs::copy("tests/snapshots/sample.modified.rs", &sample_path).expect("successful copy");
|
||||
|
||||
Ok(temp_dir)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
mod tester;
|
||||
|
||||
use tester::BatTester;
|
||||
|
||||
static STYLES: &'static [&'static str] = &[
|
||||
"changes",
|
||||
"grid",
|
||||
"header",
|
||||
"numbers",
|
||||
"changes,grid",
|
||||
"changes,header",
|
||||
"changes,numbers",
|
||||
"grid,header",
|
||||
"grid,numbers",
|
||||
"header,numbers",
|
||||
"changes,grid,header",
|
||||
"changes,grid,numbers",
|
||||
"changes,header,numbers",
|
||||
"grid,header,numbers",
|
||||
"changes,grid,header,numbers",
|
||||
"full",
|
||||
"plain",
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_snapshots() {
|
||||
let bat_tester = BatTester::new();
|
||||
|
||||
for style in STYLES {
|
||||
println!("testing {}", style);
|
||||
bat_tester.test_snapshot(&*style);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user