mirror of
https://github.com/sharkdp/bat
synced 2026-08-04 19:01:44 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f070c9dcb | |||
| 64ef61b409 | |||
| 95a2079bcd | |||
| cc60ed6563 | |||
| 50be143c62 | |||
| 59fabd8ca5 | |||
| ced6801740 | |||
| f81e38618c | |||
| 1ff46aaaf8 | |||
| 74d023a7b1 | |||
| 36bbc770eb | |||
| f43409f473 | |||
| 203e4f9249 | |||
| 22e785ca49 | |||
| 57072597d6 | |||
| fb8ef40f3e | |||
| 0e8e39a59d | |||
| 64de2a44d5 | |||
| 716b261efd | |||
| 58ca1f66d6 | |||
| 86dcb3c165 | |||
| 7b7a5a32d6 | |||
| a7232a6ecc | |||
| 38762c34d9 |
+2
-2
@@ -12,10 +12,10 @@ matrix:
|
||||
|
||||
# Minimum Rust supported channel.
|
||||
- os: linux
|
||||
rust: 1.25.0
|
||||
rust: 1.22.1
|
||||
env: TARGET=x86_64-unknown-linux-gnu
|
||||
- os: osx
|
||||
rust: 1.25.0
|
||||
rust: 1.22.1
|
||||
env: TARGET=x86_64-apple-darwin
|
||||
|
||||
addons:
|
||||
|
||||
Generated
+61
-10
@@ -29,6 +29,27 @@ dependencies = [
|
||||
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace-sys"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.8.0"
|
||||
@@ -40,19 +61,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bat"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"console 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"directories 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"git2 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntect 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntect 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "0.9.2"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -156,6 +181,14 @@ dependencies = [
|
||||
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "directories"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dtoa"
|
||||
version = "0.4.2"
|
||||
@@ -172,6 +205,14 @@ dependencies = [
|
||||
"shared_child 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error-chain"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"backtrace 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.1"
|
||||
@@ -348,7 +389,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "onig"
|
||||
version = "3.2.1"
|
||||
version = "3.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -508,6 +549,11 @@ dependencies = [
|
||||
"ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "safemem"
|
||||
version = "0.2.0"
|
||||
@@ -594,15 +640,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syntect"
|
||||
version = "2.0.0"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bincode 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"onig 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"onig 3.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plist 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -788,8 +834,10 @@ dependencies = [
|
||||
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
"checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6"
|
||||
"checksum atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "af80143d6f7608d746df1520709e5d141c96f240b0e62b0aa41bdfb53374d9d4"
|
||||
"checksum backtrace 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbe525f66f42d207968308ee86bc2dd60aa5fab535b22e616323a173d097d8e"
|
||||
"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661"
|
||||
"checksum base64 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c4a342b450b268e1be8036311e2c613d7f8a7ed31214dff1cc3b60852a3168d"
|
||||
"checksum bincode 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9a6301db0b49fb63551bc15b5ae348147101cdf323242b93ec7546d5002ff1af"
|
||||
"checksum bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bda13183df33055cbb84b847becce220d392df502ebe7a4a78d7021771ed94d0"
|
||||
"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
|
||||
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
|
||||
"checksum byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87"
|
||||
@@ -801,8 +849,10 @@ dependencies = [
|
||||
"checksum cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "56d741ea7a69e577f6d06b36b7dff4738f680593dc27a701ffa8506b73ce28bb"
|
||||
"checksum console 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7649ca90478264b9686aac8d269fcb014f14c2bed7c79a7e51b9f6afd4d783eb"
|
||||
"checksum curl-sys 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f3f7738d877ec81040305d5bb91976ac594f564f5e455dc02a29a23c1d00fe6f"
|
||||
"checksum directories 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "52bdea76de17dee3b166076dc4eeaf1d3df65fc094918a16776c2675b79ced50"
|
||||
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
|
||||
"checksum duct 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "166298c17c5b4fe5997b962c2f22e887c7c5adc44308eb9103ce5b66af45a423"
|
||||
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
|
||||
"checksum flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fac2277e84e5e858483756647a9d0aa8d9a2b7cba517fd84325a0aaa69a0909"
|
||||
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
|
||||
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
|
||||
@@ -825,7 +875,7 @@ dependencies = [
|
||||
"checksum nix 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2c5afeb0198ec7be8569d666644b574345aad2e95a53baf3a532da3e0f3fb32"
|
||||
"checksum num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f8d26da319fb45674985c78f1d1caf99aa4941f785d384a2ae36d0740bc3e2fe"
|
||||
"checksum num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dee092fcdf725aee04dd7da1d21debff559237d49ef1cb3e69bcb8ece44c7364"
|
||||
"checksum onig 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be6b3b3411efa38b2cff84a1925df1bb8b048d4c1d60656617ed7aca9a966dd9"
|
||||
"checksum onig 3.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f5eeb268a4620c74ea5768c6d2ccd492d60a47a8754666b91a46bfc35cd4d1ba"
|
||||
"checksum onig_sys 68.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c6be7c4f985508684e54f18dd37f71e66f3e1ad9318336a520d7e42f0d3ea8e"
|
||||
"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
|
||||
"checksum openssl-sys 0.9.28 (registry+https://github.com/rust-lang/crates.io-index)" = "0bbd90640b148b46305c1691eed6039b5c8509bed16991e3562a01eeb76902a3"
|
||||
@@ -844,6 +894,7 @@ dependencies = [
|
||||
"checksum regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "aec3f58d903a7d2a9dc2bf0e41a746f4530e0cab6b615494e058f67a3ef947fb"
|
||||
"checksum regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e"
|
||||
"checksum regex-syntax 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bd90079345f4a4c3409214734ae220fd773c6f2e8a543d07370c6c1c369cfbfb"
|
||||
"checksum rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11fb43a206a04116ffd7cfcf9bcb941f8eb6cc7ff667272246b0a1c74259a3cb"
|
||||
"checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f"
|
||||
"checksum same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfb6eded0b06a0b512c8ddbcf04089138c9b4362c2f696f3c3d76039d68f3637"
|
||||
"checksum serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "d3bcee660dcde8f52c3765dd9ca5ee36b4bf35470a738eb0bd5a8752b0389645"
|
||||
@@ -855,7 +906,7 @@ dependencies = [
|
||||
"checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b"
|
||||
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
|
||||
"checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59"
|
||||
"checksum syntect 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db9fffcb25a761118df53811bd1cfcd54cf57fcbc51e1ea3167ae263477129ad"
|
||||
"checksum syntect 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eceb461f684c9e8e00f7a50055698af62c76d5b86e4ad50b4311acd5f655d606"
|
||||
"checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327"
|
||||
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
|
||||
"checksum termios 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d9cf598a6d7ce700a4e6a9199da127e6819a61e64b68609683cc9a01b5683a"
|
||||
|
||||
+10
-2
@@ -7,14 +7,22 @@ license = "MIT/Apache-2.0"
|
||||
name = "bat"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/sharkdp/bat"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
|
||||
[dependencies]
|
||||
atty = "0.2.2"
|
||||
syntect = "2"
|
||||
ansi_term = "0.9"
|
||||
bincode = "1.0"
|
||||
console = "0.6"
|
||||
git2 = "0.6"
|
||||
error-chain = "0.11"
|
||||
directories = "0.9"
|
||||
lazy_static = "1.0"
|
||||
|
||||
[dependencies.syntect]
|
||||
version = "2"
|
||||
default-features = false
|
||||
features = ["parsing", "yaml-load", "dump-load", "dump-create"]
|
||||
|
||||
[dependencies.clap]
|
||||
version = "2"
|
||||
|
||||
@@ -3,26 +3,67 @@
|
||||
[](https://travis-ci.org/sharkdp/bat)
|
||||
[](https://crates.io/crates/bat)
|
||||
|
||||
*A cat(1) clone with wings.*
|
||||
A *cat(1)* clone with syntax highlighting and Git integration.
|
||||
|
||||
## Features
|
||||
|
||||
### 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):
|
||||
|
||||

|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
cargo install bat
|
||||
Check out the [Release page](https://github.com/sharkdp/bat/releases) for binary builds and Debian packages.
|
||||
|
||||
### From source
|
||||
|
||||
If you want to build to compile `bat` from source, you need Rust 1.22 or higher.
|
||||
Make sure that you have the devel-version of libopenssl installed (see instructions
|
||||
[here](https://github.com/sfackler/rust-openssl)). You can then use `cargo` to build everything:
|
||||
|
||||
``` bash
|
||||
cargo install bat
|
||||
```
|
||||
|
||||
## Customization
|
||||
|
||||
`bat` uses the excellent [`syntect`](https://github.com/trishume/syntect/) library for syntax highlighting. `syntect` can read any Sublime Text language definition and theme.
|
||||
|
||||
To build your own language-set and theme, follow these steps:
|
||||
|
||||
Create a folder with a syntax highlighting theme:
|
||||
``` bash
|
||||
mkdir -p ~/.config/bat/themes
|
||||
cd ~/.config/bat/themes
|
||||
wget https://raw.githubusercontent.com/cj/sublime/master/Color%20Scheme%20-%20Default/Monokai.tmTheme
|
||||
|
||||
# Download a theme, for example:
|
||||
git clone https://github.com/jonschlinkert/sublime-monokai-extended
|
||||
|
||||
# Create a 'Default.tmTheme' link
|
||||
ln -s "sublime-monokai-extended/Monokai Extended.tmTheme" Default.tmTheme
|
||||
```
|
||||
|
||||
Create a folder with language definition files:
|
||||
``` bash
|
||||
mkdir -p ~/.config/bat/syntax
|
||||
cd ~/.config/bat/syntax
|
||||
|
||||
# Download some language definition files, for example:
|
||||
git clone https://github.com/sublimehq/Packages/
|
||||
rm -rf Packages/Markdown
|
||||
git clone https://github.com/jonschlinkert/sublime-markdown-extended
|
||||
```
|
||||
|
||||
Finally, use the following command to parse all these files into a binary
|
||||
cache:
|
||||
``` bash
|
||||
bat init-cache
|
||||
```
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
set -e
|
||||
|
||||
THEME_FOLDER="$HOME/.config/bat/themes"
|
||||
SYNTAX_FOLDER="$HOME/.config/bat/syntax"
|
||||
|
||||
if [ ! -e "$THEME_FOLDER" ]; then
|
||||
mkdir -p "$THEME_FOLDER"
|
||||
(
|
||||
cd "$THEME_FOLDER"
|
||||
git clone https://github.com/jonschlinkert/sublime-monokai-extended
|
||||
ln -s "sublime-monokai-extended/Monokai Extended.tmTheme" Default.tmTheme
|
||||
)
|
||||
fi
|
||||
|
||||
if [ ! -e "$SYNTAX_FOLDER" ]; then
|
||||
mkdir -p "$SYNTAX_FOLDER"
|
||||
(
|
||||
cd "$SYNTAX_FOLDER"
|
||||
git clone https://github.com/sublimehq/Packages/
|
||||
rm -rf Packages/Markdown
|
||||
git clone https://github.com/jonschlinkert/sublime-markdown-extended
|
||||
)
|
||||
fi
|
||||
|
||||
bat init-cache
|
||||
|
||||
cp "$HOME/.cache/bat"/* .
|
||||
Binary file not shown.
Binary file not shown.
+238
-54
@@ -1,29 +1,63 @@
|
||||
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;
|
||||
|
||||
#[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 terminal;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::io::{self, BufRead, ErrorKind, Result, StdoutLock, Write};
|
||||
use std::fs::{self, File};
|
||||
use std::io::{self, BufRead, StdoutLock, Write};
|
||||
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 clap::{App, AppSettings, Arg, SubCommand};
|
||||
use console::Term;
|
||||
use directories::ProjectDirs;
|
||||
use git2::{DiffOptions, IntoCString, Repository};
|
||||
|
||||
use syntect::dumps::{dump_to_file, from_binary, from_reader};
|
||||
use syntect::easy::HighlightFile;
|
||||
use syntect::highlighting::{Theme, ThemeSet};
|
||||
use syntect::parsing::SyntaxSet;
|
||||
use syntect::util::as_24_bit_terminal_escaped;
|
||||
|
||||
use terminal::as_terminal_escaped;
|
||||
|
||||
lazy_static! {
|
||||
static ref PROJECT_DIRS: ProjectDirs = ProjectDirs::from("", "", crate_name!());
|
||||
}
|
||||
|
||||
mod errors {
|
||||
error_chain!{
|
||||
foreign_links {
|
||||
Io(::std::io::Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use errors::*;
|
||||
|
||||
struct Options {
|
||||
true_color: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum LineChange {
|
||||
@@ -42,22 +76,23 @@ 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);
|
||||
) -> Result<()> {
|
||||
let hline = "─".repeat(term_width - (PANEL_WIDTH + 1));
|
||||
let hline = format!("{}{}{}", "─".repeat(PANEL_WIDTH), grid_char, hline);
|
||||
|
||||
write!(handle, "{}\n", Fixed(GRID_COLOR).paint(line))?;
|
||||
writeln!(handle, "{}", Fixed(GRID_COLOR).paint(hline))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_file<P: AsRef<Path>>(
|
||||
options: &Options,
|
||||
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)?;
|
||||
line_changes: &Option<LineChanges>,
|
||||
) -> Result<()> {
|
||||
let mut highlighter = HighlightFile::new(filename.as_ref(), syntax_set, theme)?;
|
||||
|
||||
let stdout = io::stdout();
|
||||
let mut handle = stdout.lock();
|
||||
@@ -68,9 +103,9 @@ fn print_file<P: AsRef<Path>>(
|
||||
|
||||
print_horizontal_line(&mut handle, '┬', term_width)?;
|
||||
|
||||
write!(
|
||||
writeln!(
|
||||
handle,
|
||||
"{}{} {}\n",
|
||||
"{}{} File {}",
|
||||
" ".repeat(PANEL_WIDTH),
|
||||
Fixed(GRID_COLOR).paint("│"),
|
||||
White.bold().paint(filename.as_ref().to_string_lossy())
|
||||
@@ -80,10 +115,10 @@ fn print_file<P: AsRef<Path>>(
|
||||
|
||||
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 line = maybe_line.unwrap_or_else(|_| "<INVALID UTF-8>".into());
|
||||
let regions = highlighter.highlight_lines.highlight(&line);
|
||||
|
||||
let line_change = if let Some(ref changes) = line_changes {
|
||||
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("‾"),
|
||||
@@ -95,13 +130,13 @@ fn print_file<P: AsRef<Path>>(
|
||||
Style::default().paint(" ")
|
||||
};
|
||||
|
||||
write!(
|
||||
writeln!(
|
||||
handle,
|
||||
"{} {} {} {}\n",
|
||||
"{} {} {} {}",
|
||||
Fixed(244).paint(format!("{:4}", line_nr)),
|
||||
line_change,
|
||||
Fixed(GRID_COLOR).paint("│"),
|
||||
as_24_bit_terminal_escaped(®ions, false)
|
||||
as_terminal_escaped(®ions, options.true_color)
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -110,11 +145,15 @@ fn print_file<P: AsRef<Path>>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_git_diff(filename: String) -> Option<LineChanges> {
|
||||
fn get_git_diff(filename: &str) -> Option<LineChanges> {
|
||||
let repo = Repository::open_from_env().ok()?;
|
||||
let workdir = repo.workdir()?;
|
||||
let current_dir = env::current_dir().ok()?;
|
||||
let filepath = current_dir.join(Path::new(&filename));
|
||||
|
||||
let mut diff_options = DiffOptions::new();
|
||||
diff_options.pathspec(filename.into_c_string().ok()?);
|
||||
let pathspec = format!("*{}", filename).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))
|
||||
@@ -132,7 +171,13 @@ fn get_git_diff(filename: String) -> Option<LineChanges> {
|
||||
let _ = diff.foreach(
|
||||
&mut |_, _| true,
|
||||
None,
|
||||
Some(&mut |_, hunk| {
|
||||
Some(&mut |delta, hunk| {
|
||||
let path = delta.new_file().path().unwrap_or_else(|| Path::new(""));
|
||||
|
||||
if filepath != workdir.join(path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let old_lines = hunk.old_lines();
|
||||
let new_start = hunk.new_start();
|
||||
let new_lines = hunk.new_lines();
|
||||
@@ -141,7 +186,7 @@ fn get_git_diff(filename: String) -> Option<LineChanges> {
|
||||
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 {
|
||||
if new_start == 0 {
|
||||
mark_section(&mut line_changes, 1, 1, LineChange::RemovedAbove);
|
||||
} else {
|
||||
mark_section(
|
||||
@@ -163,37 +208,134 @@ fn get_git_diff(filename: String) -> Option<LineChanges> {
|
||||
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)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
fn is_truecolor_terminal() -> bool {
|
||||
env::var("COLORTERM")
|
||||
.map(|colorterm| colorterm == "truecolor" || colorterm == "24bit")
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
struct HighlightingAssets {
|
||||
pub syntax_set: SyntaxSet,
|
||||
pub theme_set: ThemeSet,
|
||||
}
|
||||
|
||||
impl HighlightingAssets {
|
||||
fn from_files() -> Result<Self> {
|
||||
let config_dir = PROJECT_DIRS.config_dir();
|
||||
|
||||
let theme_dir = config_dir.join("themes");
|
||||
|
||||
let theme_set = ThemeSet::load_from_folder(&theme_dir).map_err(|_| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!(
|
||||
"Could not load themes from '{}'",
|
||||
theme_dir.to_string_lossy()
|
||||
),
|
||||
)
|
||||
})?;
|
||||
|
||||
let mut syntax_set = SyntaxSet::new();
|
||||
let syntax_dir = config_dir.join("syntax");
|
||||
let _ = syntax_set.load_syntaxes(syntax_dir, false);
|
||||
syntax_set.load_plain_text_syntax();
|
||||
|
||||
Ok(HighlightingAssets {
|
||||
syntax_set,
|
||||
theme_set,
|
||||
})
|
||||
}
|
||||
|
||||
fn save(&self) -> Result<()> {
|
||||
let cache_dir = PROJECT_DIRS.cache_dir();
|
||||
let theme_set_path = cache_dir.join("theme_set");
|
||||
let syntax_set_path = cache_dir.join("syntax_set");
|
||||
|
||||
let _ = fs::create_dir(cache_dir);
|
||||
|
||||
dump_to_file(&self.theme_set, &theme_set_path).map_err(|_| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!(
|
||||
"Could not save theme set to {}",
|
||||
theme_set_path.to_string_lossy()
|
||||
),
|
||||
)
|
||||
})?;
|
||||
println!("Wrote theme set to {}", theme_set_path.to_string_lossy());
|
||||
|
||||
dump_to_file(&self.syntax_set, &syntax_set_path).map_err(|_| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!(
|
||||
"Could not save syntax set to {}",
|
||||
syntax_set_path.to_string_lossy()
|
||||
),
|
||||
)
|
||||
})?;
|
||||
println!("Wrote syntax set to {}", syntax_set_path.to_string_lossy());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn from_cache() -> Result<Self> {
|
||||
let cache_dir = PROJECT_DIRS.cache_dir();
|
||||
let theme_set_path = cache_dir.join("theme_set");
|
||||
let syntax_set_path = cache_dir.join("syntax_set");
|
||||
|
||||
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).map_err(|_| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("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).map_err(|_| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("Could not parse cached theme set"),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(HighlightingAssets {
|
||||
syntax_set,
|
||||
theme_set,
|
||||
})
|
||||
}
|
||||
|
||||
fn from_binary() -> Self {
|
||||
let mut syntax_set: SyntaxSet = from_binary(include_bytes!("../assets/syntax_set"));
|
||||
syntax_set.link_syntaxes();
|
||||
let theme_set: ThemeSet = from_binary(include_bytes!("../assets/theme_set"));
|
||||
|
||||
HighlightingAssets {
|
||||
syntax_set,
|
||||
theme_set,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run() -> Result<()> {
|
||||
let clap_color_setting = if atty::is(Stream::Stdout) {
|
||||
AppSettings::ColoredHelp
|
||||
} else {
|
||||
AppSettings::ColorNever
|
||||
};
|
||||
|
||||
let matches = App::new(crate_name!())
|
||||
let app_matches = App::new(crate_name!())
|
||||
.version(crate_version!())
|
||||
.setting(clap_color_setting)
|
||||
.setting(AppSettings::DeriveDisplayOrder)
|
||||
@@ -208,16 +350,58 @@ fn main() {
|
||||
.multiple(true)
|
||||
.empty_values(false),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("init-cache")
|
||||
.about("Load syntax definitions and themes into cache"),
|
||||
)
|
||||
.help_message("Print this help message.")
|
||||
.version_message("Show version information.")
|
||||
.get_matches();
|
||||
|
||||
let result = run(&matches);
|
||||
match app_matches.subcommand() {
|
||||
("init-cache", Some(_)) => {
|
||||
let assets = HighlightingAssets::from_files()?;
|
||||
assets.save()?;
|
||||
}
|
||||
_ => {
|
||||
let options = Options {
|
||||
true_color: is_truecolor_terminal(),
|
||||
};
|
||||
|
||||
if let Err(e) = result {
|
||||
if e.kind() != ErrorKind::BrokenPipe {
|
||||
eprintln!("{}: {}", Red.paint("[bat error]"), e);
|
||||
process::exit(1);
|
||||
let assets =
|
||||
HighlightingAssets::from_cache().unwrap_or(HighlightingAssets::from_binary());
|
||||
|
||||
let theme = assets.theme_set.themes.get("Default").ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("Could not find 'Default' theme"),
|
||||
)
|
||||
})?;
|
||||
|
||||
if let Some(files) = app_matches.values_of("FILE") {
|
||||
for file in files {
|
||||
let line_changes = get_git_diff(&file.to_string());
|
||||
print_file(&options, theme, &assets.syntax_set, file, &line_changes)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let result = run();
|
||||
|
||||
if let Err(error) = result {
|
||||
match error {
|
||||
Error(ErrorKind::Io(ref io_error), _)
|
||||
if io_error.kind() == io::ErrorKind::BrokenPipe => {}
|
||||
_ => {
|
||||
eprintln!("{}: {}", Red.paint("[bat error]"), error);
|
||||
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
use std::fmt::Write;
|
||||
|
||||
use ansi_term::Colour::{Fixed, RGB};
|
||||
use syntect::highlighting;
|
||||
|
||||
/// 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 {
|
||||
let fr = f32::from(r);
|
||||
(((fr - 8.) / 247.) * 24.) as u8 + 232
|
||||
}
|
||||
} else {
|
||||
let fr = f32::from(r);
|
||||
let fg = f32::from(g);
|
||||
let fb = f32::from(b);
|
||||
16 + (36 * (fr / 255. * 5.) as u8) + (6 * (fg / 255. * 5.) as u8) + (fb / 255. * 5.) as u8
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_terminal_escaped(v: &[(highlighting::Style, &str)], true_color: bool) -> String {
|
||||
let mut s: String = String::new();
|
||||
for &(ref style, text) in v.iter() {
|
||||
let style = if true_color {
|
||||
RGB(style.foreground.r, style.foreground.g, style.foreground.b)
|
||||
} else {
|
||||
let ansi = rgb2ansi(style.foreground.r, style.foreground.g, style.foreground.b);
|
||||
Fixed(ansi)
|
||||
};
|
||||
|
||||
write!(s, "{}", style.paint(text)).unwrap();
|
||||
}
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
#[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));
|
||||
}
|
||||
Reference in New Issue
Block a user