mirror of
https://github.com/sharkdp/bat
synced 2026-08-03 18:51:44 +00:00
Compare commits
160 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2596bfe7a7 | |||
| 406bffa8c8 | |||
| 48441b99ef | |||
| ce4717144a | |||
| f59d00d4c7 | |||
| dcfdbf82dd | |||
| 0cb884d501 | |||
| 7bc4020a92 | |||
| 0792195156 | |||
| 83f24ffcfb | |||
| 74ec390770 | |||
| 9014ffcfd8 | |||
| 81af375289 | |||
| d707eab95c | |||
| d64c55ef11 | |||
| 2b1d53fb27 | |||
| bf696f58c8 | |||
| 296bad8ffd | |||
| 94310af30c | |||
| 38c096bf79 | |||
| 696d1b3ed3 | |||
| 17d98724a5 | |||
| 7d7ee1b594 | |||
| 682b4209f2 | |||
| 41ddcbd240 | |||
| 48a7ce3bf2 | |||
| 3bcc4d0d55 | |||
| 8821bca656 | |||
| 56111aa20d | |||
| 5fe8a8342b | |||
| 989109145a | |||
| 23afc8e90c | |||
| 8e18786556 | |||
| 81488adf8b | |||
| e37e9c1214 | |||
| a4828387c1 | |||
| abeac8b12e | |||
| 82e7786e74 | |||
| 0064321323 | |||
| 9e622a1a5f | |||
| 59ca933aee | |||
| 793af6b911 | |||
| c8abe3f235 | |||
| 5d71056209 | |||
| eb7d4d002a | |||
| 62b32bd848 | |||
| 8961f7aef8 | |||
| 17f3a3b95d | |||
| 261a7ea154 | |||
| 53a973e9dd | |||
| cba9df746e | |||
| 12eee0c590 | |||
| b76f5e72d4 | |||
| 702cb198da | |||
| 5f826419d1 | |||
| 13f671b499 | |||
| 49f4322054 | |||
| 0c9e044e41 | |||
| 74d4377ed2 | |||
| 6a124591df | |||
| 7a87315b94 | |||
| 0cde4e9121 | |||
| 36dde9275a | |||
| a8f759c080 | |||
| f034be71e7 | |||
| eee35e54e9 | |||
| ec0ce05455 | |||
| 13e6b3fac7 | |||
| 26c951fec4 | |||
| 590960f7f5 | |||
| b4d54106fe | |||
| f3b90ddb38 | |||
| 3bacfc5184 | |||
| f8d0956893 | |||
| 1dc328ad49 | |||
| 5e5cb89da6 | |||
| 057e4eced1 | |||
| 27974616bf | |||
| 319ab779ee | |||
| 37831cdcb3 | |||
| 70480ee9d4 | |||
| 371c929ede | |||
| 864656bd11 | |||
| 2e9cf63a5f | |||
| 886b22e0ee | |||
| 72618db179 | |||
| c8273369cc | |||
| 95a56a2795 | |||
| 859ff50766 | |||
| ce583eb9bf | |||
| 3a195be14e | |||
| 5f6e310152 | |||
| 5449472f15 | |||
| 82e20bfe14 | |||
| 47abb192bc | |||
| 03a7d6be92 | |||
| a927815292 | |||
| 3447a03433 | |||
| 03c2281828 | |||
| c386cb35fb | |||
| 97ea3dd4dc | |||
| a6d9d1551f | |||
| 34619a2e89 | |||
| 83d408bab3 | |||
| 8067fd9eda | |||
| 98ba9e0101 | |||
| 1b8ce60054 | |||
| d5a31dc2ec | |||
| 051dd4ba87 | |||
| 2ad1848859 | |||
| 90e7d2fe33 | |||
| 04fa84aea7 | |||
| a3f8140fbe | |||
| 96aedf6240 | |||
| 4cc2989fe9 | |||
| 0ecc94956b | |||
| 61e47e0c13 | |||
| f5145ef130 | |||
| edd27645d0 | |||
| fc6f4f31dd | |||
| f2cef702a0 | |||
| 8b3d54ce29 | |||
| 47a3721890 | |||
| 495fab24a5 | |||
| fccbe4f4f2 | |||
| 9793bb3938 | |||
| 496dd29cb9 | |||
| 116d76a00f | |||
| dd336e6fb2 | |||
| c33af3581a | |||
| 2f4ecf8fc5 | |||
| e037afeaca | |||
| 7b87af1748 | |||
| 0eee26fb09 | |||
| 3355aeba22 | |||
| 42e3825daf | |||
| 014d754588 | |||
| 4e11abdf9b | |||
| 570805bc98 | |||
| e7e1967bb0 | |||
| 9a050cd87f | |||
| d39507889a | |||
| 3e8d444c78 | |||
| 522ab7a83c | |||
| 0c5b4fcd4a | |||
| 37b3b8730d | |||
| 7c50fe5fec | |||
| 40a827ebcb | |||
| 376c556862 | |||
| 83772bd2cf | |||
| 4aef8c180a | |||
| 131d113ff5 | |||
| 38178fedf4 | |||
| 59f2e2d58d | |||
| 8d3136eb6f | |||
| 136a745c5d | |||
| fb3c775c8b | |||
| 8adce9fae8 | |||
| cfa2cb6ec7 | |||
| 517be5c7bc |
@@ -4,3 +4,4 @@
|
||||
# Generated files
|
||||
/assets/completions/bat.fish
|
||||
/assets/manual/bat.1
|
||||
/assets/metadata.yaml
|
||||
|
||||
+15
-3
@@ -166,9 +166,6 @@
|
||||
[submodule "assets/themes/Nord-sublime"]
|
||||
path = assets/themes/Nord-sublime
|
||||
url = https://github.com/crabique/Nord-plist.git
|
||||
[submodule "assets/themes/solarized-sublime"]
|
||||
path = assets/themes/solarized-sublime
|
||||
url = https://github.com/paulcpederson/solarized-sublime.git
|
||||
[submodule "assets/syntaxes/Vue"]
|
||||
path = assets/syntaxes/02_Extra/Vue
|
||||
url = https://github.com/vuejs/vue-syntax-highlight.git
|
||||
@@ -176,3 +173,18 @@
|
||||
[submodule "assets/syntaxes/CoffeeScript"]
|
||||
path = assets/syntaxes/02_Extra/CoffeeScript
|
||||
url = https://github.com/sustained/CoffeeScript-Sublime-Plugin
|
||||
[submodule "assets/syntaxes/02_Extra/Stylus"]
|
||||
path = assets/syntaxes/02_Extra/Stylus
|
||||
url = https://github.com/billymoon/Stylus
|
||||
[submodule "assets/themes/Solarized"]
|
||||
path = assets/themes/Solarized
|
||||
url = https://github.com/braver/Solarized
|
||||
[submodule "assets/syntaxes/02_Extra/Fortran"]
|
||||
path = assets/syntaxes/02_Extra/Fortran
|
||||
url = https://github.com/315234/SublimeFortran
|
||||
[submodule "assets/syntaxes/02_Extra/Email"]
|
||||
path = assets/syntaxes/02_Extra/Email
|
||||
url = https://github.com/mariozaizar/email.sublime-syntax.git
|
||||
[submodule "assets/syntaxes/02_Extra/QML"]
|
||||
path = assets/syntaxes/02_Extra/QML
|
||||
url = https://github.com/skozlovf/Sublime-QML
|
||||
|
||||
+3
-4
@@ -1,5 +1,4 @@
|
||||
language: rust
|
||||
cache: cargo
|
||||
|
||||
matrix:
|
||||
include:
|
||||
@@ -33,15 +32,15 @@ matrix:
|
||||
|
||||
# Minimum Rust supported channel.
|
||||
- os: linux
|
||||
rust: 1.36.0
|
||||
rust: 1.40.0
|
||||
env: TARGET=x86_64-unknown-linux-gnu
|
||||
- os: linux
|
||||
rust: 1.36.0
|
||||
rust: 1.40.0
|
||||
env:
|
||||
- TARGET=x86_64-unknown-linux-musl
|
||||
- CC_x86_64_unknown_linux_musl=/usr/bin/musl-gcc
|
||||
- os: osx
|
||||
rust: 1.36.0
|
||||
rust: 1.40.0
|
||||
env: TARGET=x86_64-apple-darwin
|
||||
|
||||
# Disable nightly for now
|
||||
|
||||
+663
@@ -0,0 +1,663 @@
|
||||
# unreleased
|
||||
|
||||
## Features
|
||||
## Bugfixes
|
||||
## Other
|
||||
## New syntaxes
|
||||
## New themes
|
||||
## `bat` as a library
|
||||
## Packaging
|
||||
|
||||
# v0.15.1
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Fix highlighting of Markdown files, see #963 and #977
|
||||
- Fix `base16` theme (was broken since in v0.14), see #972, #934 and #979 (@mk12).
|
||||
Users suffering from #865 ("no color for bat in ssh from a Windows client") can use the `ansi-dark` and `ansi-light` themes from now on.
|
||||
|
||||
## New syntaxes
|
||||
|
||||
- Fortran, see #957
|
||||
- Email (@mariozaizar)
|
||||
- QML, see #962 (@pylipp)
|
||||
|
||||
# v0.15.0
|
||||
|
||||
## Features
|
||||
|
||||
- Add a new `--diff`/`-d` option that can be used to only show lines surrounding
|
||||
Git changes, i.e. added, removed or modified lines. The amount of additional
|
||||
context can be controlled with `--diff-context=N`. See #23 and #940
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Error message printed in the middle of the output for another file, see #946
|
||||
- Performance improvements when using custom caches (via `bat cache --build`): the `bat` startup time should now be twice as fast (@lzutao).
|
||||
|
||||
## Themes
|
||||
|
||||
- Updated version of the Solarized dark/light themes, see #941
|
||||
|
||||
## `bat` as a library
|
||||
|
||||
- There are a few changes in the "low level" API (the `Config` struct has changed and
|
||||
the error handler needs a new `&mut dyn Write` argument). The high-level API is not
|
||||
affected.
|
||||
|
||||
# v0.14.0
|
||||
|
||||
## Features
|
||||
|
||||
- Added a new `--file-name <name>…` option to overwrite the displayed filename(s)
|
||||
in the header. This is useful when piping input into `bat`. See #654 and #892 (@neuronull).
|
||||
- Added a new `--generate-config-file` option to create an initial configuration file
|
||||
at the right place. See #870 (@jmick414)
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Performance problems with C# source code have been fixed, see #677 (@keith-hall)
|
||||
- Performance problems with Makefiles have been fixed, see #750 (@keith-hall)
|
||||
- Fix bug when highlighting Ruby files with unindented heredocs, see #914 (@keith-hall)
|
||||
- A highlighting problem with Rust source code has been fixed, see #924 (@keith-hall)
|
||||
- Windows: short files that do not require paging are displayed and then lost, see #887
|
||||
- `--highlight-line` did not work correctly in combination with `--tabs=0` and `--wrap=never`,
|
||||
see #937
|
||||
|
||||
## Other
|
||||
|
||||
- When saving/reading user-provided syntaxes or themes, `bat` will now maintain a
|
||||
`metadata.yaml` file which includes information about the `bat` version which was
|
||||
used to create the cached files. When loading cached files, we now print an error
|
||||
if they have been created with an incompatible version. See #882
|
||||
- Updated `liquid` dependency to 0.20, see #880 (@ignatenkobrain)
|
||||
|
||||
## `bat` as a library
|
||||
|
||||
- A completely new "high level" API has been added that is much more convenient
|
||||
to use. See the `examples` folder for the updated code. The older "low level"
|
||||
API is still available (basically everything that is not in the root `bat`
|
||||
module), but has been refactored quite a bit. It is recommended to only use
|
||||
the new "high level" API, if possible. This will be much easier to keep stable.
|
||||
Note that this should still be considered a "beta" release of `bat`-as-a-library.
|
||||
For more details and some screenshots of the example programs, see #936.
|
||||
- Stripped out a lot of binary-only dependencies, see #895 and #899 (@dtolnay)
|
||||
|
||||
This introduces a `features = ["application"]` which is enabled by default and pulls in
|
||||
everything required by `bat` the application. When depending on bat as a library, downstream
|
||||
`Cargo.toml` should disable this feature to cut out inapplicable heavy dependencies:
|
||||
``` toml
|
||||
[dependencies]
|
||||
bat = { version = "0.14", default-features = false }
|
||||
```
|
||||
Other optional functionality has also been put behind features: `paging` and `git` support.
|
||||
- Allow using the library with older syntect, see #896 and #898 (@dtolnay)
|
||||
|
||||
## New syntaxes
|
||||
|
||||
- Rego, see #872 (@patrick-east)
|
||||
- Stylo, see #917
|
||||
|
||||
|
||||
# v0.13.0
|
||||
|
||||
## `bat` as a library
|
||||
|
||||
Beginning with this release, `bat` can be used as a library (#423).
|
||||
|
||||
This was a huge effort and I want to thank all people who made this possible: @DrSensor, @mitsuhiko, @mre, @eth-p!
|
||||
|
||||
- Initial attempt in #469 (@mitsuhiko)
|
||||
- Second attempt, complete restructuring of the `bat` crate, see #679 (@DrSensor)
|
||||
- Updates to example, public API, error handling, further refactoring: #693 #873 #875 (@sharkdp)
|
||||
|
||||
I want to stress that this is the very first release of the library. Things are very likely to change. A lot of things are still missing (including the documentation).
|
||||
|
||||
That being said, you can start using it! See the example programs in [`examples/`](https://github.com/sharkdp/bat/tree/master/examples).
|
||||
|
||||
You can see the API documentation here: https://docs.rs/bat/
|
||||
|
||||
## Features
|
||||
|
||||
- (**Breaking change**) Glob-based syntax mapping, see #877 and #592. With this change,
|
||||
users need to update their bat config files (`bat --config-file`), if they have any `--map-syntax` settings
|
||||
present.
|
||||
|
||||
The option now works like this:
|
||||
```bash
|
||||
--map-syntax <glob-pattern>:<syntax-name>
|
||||
```
|
||||
|
||||
For more information, see the `--help` text, the man page or the README.
|
||||
|
||||
This new feature allows us to properly highlight files like:
|
||||
* `/etc/profile`
|
||||
* `~/.ssh/config`
|
||||
|
||||
- `--highlight-line` now accepts line ranges, see #809 (@lkalir)
|
||||
- Proper wrapping support for output with wide Unicode characters, see #811 #787 and #815 (@Kogia-sima)
|
||||
- A lot of updates to existing syntaxes via #644 (@benwaffle, @keith-hall)
|
||||
- `BAT_CACHE_PATH` can be used to place cached `bat` assets in a non-standard path, see #829 (@neuronull)
|
||||
- Support combination of multiple styles at the same time, see #857 (@aslpavel)
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Do not pass '--no-init' on newer less versions, see #749 and #786 (@sharkdp)
|
||||
- 'bat cache' still takes precedence over existing files, see #666 (@sharkdp)
|
||||
- `.sc` files should be treated as scala files, see #443 (@benwaffle)
|
||||
- Allow underscores and dashes in page names, see #670 (@LunarLambda)
|
||||
- Keep empty lines empty, see #601 (@mbarbar)
|
||||
- Wrapping does not work when piping, see #758 (@fusillicode, @allevo, @gildo)
|
||||
- Allow for non-unicode filenames, see #225 (@sharkdp)
|
||||
- Empty file without header produces incomplete grid, see #798 (@eth-p)
|
||||
- Files named `build` don't respect shebang lines, see #685 (@sharkdp)
|
||||
|
||||
## Other
|
||||
|
||||
- Parametrizable names for man page and shell completion files, see #659 #673 #656 (@eth-p)
|
||||
- Enabled LTO, making `bat` about 10% faster, see #719 (@bolinfest, @sharkdp)
|
||||
- Suggestions non how to configure `bat` for MacOS dark mode, see README (@jerguslejko)
|
||||
- Extended ["Integration with other tools"](https://github.com/sharkdp/bat#integration-with-other-tools) section (@eth-p)
|
||||
- Updated [instrutions on how to use `bat` as a `man`-pager](https://github.com/sharkdp/bat#man), see #652, see #667 (@sharkdp)
|
||||
- Add section concerning file encodings, see #688 and #568 (@sharkdp)
|
||||
- Updated sort order of command-line options in `--help` text and manpage, see #653 and #700 (@hrlmartins)
|
||||
- Updates to the man page syntax, see #718 (@sharkdp)
|
||||
- Japanese documentation updates, see #863 (@k-ta-yamada, @sorairolake and @wt-l00)
|
||||
- Accept "default" as a theme, see #757 (@fvictorio)
|
||||
- Updated Windows installation instructions, see #852 (@sorenbug)
|
||||
- Updated man page, see #573 (@sharkdp)
|
||||
|
||||
## New syntaxes
|
||||
|
||||
- Jinja2, see #648 (@Martin819)
|
||||
- SaltStack SLS, see #658 (@Martin819)
|
||||
- `/etc/fstab`, see #696 (@flopp and @eth-p)
|
||||
- `/etc/group` and `/etc/passwd`, see #698 (@argentite)
|
||||
- `/proc/cpuinfo` and `/proc/meminfo`, see #593 (@sharkdp)
|
||||
- Nim, see #542 (@sharkdp)
|
||||
- Vue, see #826 (@chaaaaarlotte)
|
||||
- CoffeScript, see #833 (@sharkdp)
|
||||
|
||||
## New themes
|
||||
|
||||
- Dracula, see #687 (@clarfon)
|
||||
- Nord, see #760 (@crabique)
|
||||
- Solarized light and dark, see #768 (@hakamadare)
|
||||
|
||||
## Packaging
|
||||
|
||||
- `bat` is now in the official Ubuntu and Debian repositories, see #323 and #705 (@MarcoFalke)
|
||||
- `bat` can now be installed via MacPorts, see #675 (@bn3t)
|
||||
- Install fish completions into 'vendor_completions.d', see #651 (@sharkdp)
|
||||
|
||||
## Thanks
|
||||
|
||||
- To @eth-p for joining me as a maintainer! I'm very grateful for all the work you put into
|
||||
managing and responding to issues, improving our deployment, adding PR/issue templates (#837) as
|
||||
well as fixing bugs and implementing new features.
|
||||
|
||||
# v0.12.1
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Fixes a bug for older Windows versions (*"The procedure entry point `CreateFile2` could not be located"*), see #643 (@rivy)
|
||||
|
||||
# v0.12.0
|
||||
|
||||
## Features
|
||||
|
||||
- Binary file content can now be viewed with `bat -A`, see #623, #640 (@pjsier and @sharkdp)
|
||||
- `bat` can now be used as a man pager. Take a look at the README and #523 for more details.
|
||||
- Add new style component to separate multiple `--line-range`s, see #570 (@eth-p)
|
||||
- Added `-L` as an alias for `--list-languages`
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Output looks unbalanced when using '--style=grid,numbers' without 'header', see #571 (@eth-p)
|
||||
- issues with filenames starting with "cache", see #584
|
||||
- Can't build cache with new theme without creating cache dir, see #576 (@frm)
|
||||
- `--terminal-width -10` is parsed incorrectly, see #611
|
||||
|
||||
## Other
|
||||
|
||||
- Added fish completions to DEB package, see #554
|
||||
|
||||
## New syntaxes
|
||||
|
||||
- Emacs Org mode, see #36 (@bricewge)
|
||||
- `requirements.txt`
|
||||
- DotENV `.env`
|
||||
- SSH config syntax (`-l ssh_config`), see #582 (@issmirnov)
|
||||
- `/etc/hosts`, see #583 (@issmirnov)
|
||||
- GraphQL, see #625 (@dandavison)
|
||||
- Verilog, see #616
|
||||
- SCSS and Sass, see #637
|
||||
- `strace` syntax, see #599
|
||||
|
||||
## Packaging
|
||||
|
||||
- `bat` is now in the official Gentoo repositories, see #588 (@toku-sa-n)
|
||||
- `bat` is now in the official Alpine Linux repositories, see #586 (@5paceToast)
|
||||
- `bat` is in the official Fedora repositories, see #610 (@ignatenkobrain)
|
||||
|
||||
# v0.11.0
|
||||
|
||||
## Features
|
||||
|
||||
- Three new special color themes are available: `ansi-light`, `ansi-dark` and `base16`. These
|
||||
are useful for people that often switch from dark to light themes in their terminal emulator
|
||||
or for people that want the colors to match their terminal theme colors. For more details,
|
||||
see #543 and #490 (@mk12, implementation idea by @trishume)
|
||||
- Hand-written auto completion script for Fish shell, see #524 and #555 (@ev-dev and @eth-p)
|
||||
- The `-p`/`--plain` option can now be used twice (typically `-pp`). The first `-p` switches the
|
||||
`--style` to "plain". The second `-p` disables the pager. See #560 and #552 (@eth-p)
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Do not replace arguments to `less` when using `--pager`, see #509
|
||||
- Binary files will now be indicated by a warning in interactive mode, see #530 #466 #550 (@maxfilov)
|
||||
- Empty files are (once again) printed with a single header line, see #504 and #566 (@reidwagner
|
||||
and @sharkdp)
|
||||
- `--terminal-width=0` is now disallowed, see #559 (@eth-p)
|
||||
- Accidental printing of files named `cache`, see #557
|
||||
|
||||
## Other
|
||||
|
||||
- New integration tests, see #500 and #502 (@reidwagner and @sharkdp)
|
||||
- New ["Integration with other tools"](https://github.com/sharkdp/bat#integration-with-other-tools) section in the README.
|
||||
- Migrated to Rust 2018 (@expobrain)
|
||||
|
||||
## New syntaxes
|
||||
|
||||
- F# syntax has been updated, see #531 (@stroborobo)
|
||||
- Fish shell, see #548 (@sanga)
|
||||
|
||||
## Packaging
|
||||
|
||||
- `bat` is now available on Chocolatey, see #541 (@rasmuskriest)
|
||||
|
||||
# v0.10.0
|
||||
|
||||
## Features
|
||||
|
||||
- Added new `--highlight-line <N>` option, see #453, #346 and #175 (@tskinn and @sharkdp)
|
||||
|
||||
## Changes
|
||||
|
||||
- **Change the default configuration directory on macOS** to `~/.config/bat`, see #442 (@lavifb). If you are on macOS, you need to copy your configuration directory from the previous place (`~/Library/Preferences/bat`) to the new place (`~/.config/bat`).
|
||||
- Completely disabled the generation of shell completion files, see #372
|
||||
- Properly set arguments to `less` if `PAGER` environment variable contains something like `less -F` (which is missing the `-R` option), see #430 (@majecty)
|
||||
- Report the name of missing files, see #444 (@ufuji1984)
|
||||
- Don't start pager if file doesn't exist, see #387
|
||||
- Rename `bat cache --init` to `bat cache --build`, see #498
|
||||
- Move the `--config-dir` and `--cache-dir` options from `bat cache` to `bat` and hide them from the help text.
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Blank line at the end of output when using `--style=plain`, see #379
|
||||
- EOF must be sent twice on stdin if no other input is sent, see #477 (@reidwagner)
|
||||
|
||||
## New syntaxes
|
||||
|
||||
- Twig (@ahmedelgabri)
|
||||
- `.desktop` files (@jleclanche)
|
||||
- AsciiDoc (@markusthoemmes)
|
||||
- Assembly (x86_64 and ARM)
|
||||
- Log files (@caos21)
|
||||
- Protobuf and ProtobufText (@caos21)
|
||||
- Terraform (@caos21)
|
||||
- Jsonnet (@hfm)
|
||||
- Varlink (@haraldh)
|
||||
|
||||
## Other
|
||||
|
||||
- Added Japanese version of README (@sh-tech and @object1037)
|
||||
- Code improvements (@barskern)
|
||||
|
||||
# v0.9.0
|
||||
|
||||
## Features
|
||||
|
||||
- A new `-A`/`--show-all` option has been added to show and highlight non-printable characters (in analogy to GNU `cat`s option):
|
||||
|
||||

|
||||
|
||||
see #395 and #381 for more details.
|
||||
|
||||
- Added `--pager` option (to configure the pager from the configuration file), see #362 (@majecty)
|
||||
|
||||
- Added `BAT_CONFIG_PATH` environment variable to set a non-default path for `bat`s configuration file, see #375 (@deg4uss3r)
|
||||
|
||||
- Allow for multiple occurences of `--style` to allow for the configuration
|
||||
of styles from the config file, see #367 (@sindreij)
|
||||
|
||||
- Allow for multiple `--line-range` arguments, see #23
|
||||
|
||||
- The `--terminal-width` option can now also accept offsets, see #376
|
||||
|
||||
## Changes
|
||||
|
||||
- Use of italics is now *disabled by default* (see #389 for details). They can be
|
||||
re-enabled by adding `--italic-text=always` to your configuration file.
|
||||
|
||||
- The default tab-width has been set to 4.
|
||||
|
||||
- Added new "Sublime Snazzy" theme.
|
||||
|
||||
- Shell completions are currently *not* shipped anymore, see #372 for details.
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Avoid endless recursion when `PAGER="bat"`, see #383 (@rodorgas)
|
||||
|
||||
## Other
|
||||
|
||||
- `bat` is now available on openSUSE, see #405 (@dmarcoux)
|
||||
|
||||
- Added section about the new configuration file in the README (@deg4uss3r)
|
||||
|
||||
- Chinese translation of README (@chinanf-boy)
|
||||
|
||||
- Re-written tests for `--tabs` (@choznerol)
|
||||
|
||||
- Speed up integration tests, see #394
|
||||
|
||||
# v0.8.0
|
||||
|
||||
## Features
|
||||
|
||||
- Support for a configuration file with the following simple format:
|
||||
|
||||
```bash
|
||||
--tabs=4
|
||||
--theme="Sublime Snazzy"
|
||||
|
||||
# A line-comment
|
||||
--map-syntax .ignore:.gitignore
|
||||
--map-syntax PKGBUILD:bash
|
||||
--map-syntax Podfile:ruby
|
||||
|
||||
# Flags and options can also be on a single line:
|
||||
--wrap=never --paging=never
|
||||
```
|
||||
|
||||
The configuration file path can be accessed via `bat --config-file`. On Linux,
|
||||
it is stored in `~/.config/bat/config`.
|
||||
|
||||
- Support for the `BAT_OPTS` environment variable with the same format as specified
|
||||
above (in a single line). This takes precedence over the configuration file.
|
||||
|
||||
See also #310.
|
||||
|
||||
- Support for custom syntax mappings via the `-m`/`--max-syntax` option.
|
||||
|
||||
This allows users to (re)map certain file extensions or file names to an existing syntax:
|
||||
|
||||
``` bash
|
||||
bat --map-syntax .config:json ...
|
||||
```
|
||||
|
||||
The option can be use multiple times. Note that you can easily make these mappings permanent by using bats new configuration file.
|
||||
|
||||
See #169
|
||||
|
||||
- Support pager command-line arguments in `PAGER` and `BAT_PAGER`, see #352 (@Foxboron)
|
||||
|
||||
- Add support for wildcards in Windows CMD, see #309 (@zxey)
|
||||
|
||||
- First-line syntax detection for all input types, see #205
|
||||
|
||||
- Encoding support for UTF-16LE and UTF-16BE, see #285
|
||||
|
||||
- New syntaxes: Robot framework (@sanga)
|
||||
|
||||
## Changes
|
||||
|
||||
- Binary files are now detected and not displayed when the output goes to an interactive terminal, see #205
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- JavaDoc comments break syntax highlighting in .java files, see #81
|
||||
|
||||
- Bat Panics on Haskell Source Code, see #314
|
||||
|
||||
## Other
|
||||
|
||||
- Better `-h` and `--help` texts.
|
||||
|
||||
- Updated documentation on how to configure `bat`s pager
|
||||
|
||||
- Updated documentation for light backgrounds, see #328 (@russtaylor)
|
||||
|
||||
- Generate shell completions during build, see #115 (@davideGiovannini)
|
||||
|
||||
- A lot of new tests have been written
|
||||
|
||||
- `bat` is now available via [Termux](https://termux.com/), see #341 (@fornwall)
|
||||
|
||||
- `bat` is now available via [nix](https://nixos.org/nix), see #344 (@mgttlinger)
|
||||
|
||||
- `bat` is now available via [Docker](https://hub.docker.com/r/danlynn/bat/), see #331 (@danlynn)
|
||||
|
||||
# v0.7.1
|
||||
|
||||
## Features
|
||||
|
||||
- Use the `ansi_colours` package by @mina86 for better true-color approximation on 8 bit color terminals, see #319 and #202.
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Bat Panics on Haskell Source Code, see #314
|
||||
- Disable wrapping when `--style=plain`/`-p` is used, see #289
|
||||
|
||||
## Other
|
||||
|
||||
- Added Ansible install instructions (@aeimer)
|
||||
- Added section about Cygwin to the README (@eth-p)
|
||||
|
||||
# v0.7.0
|
||||
|
||||
## Features
|
||||
|
||||
- Tabs are now (optionally) expanded to spaces. This can be controlled with the new
|
||||
`--tabs` command-line option or the `BAT_TABS` environment variable. The
|
||||
new feature also closes two bugs #166 and #184. For more information, see #302 (@eth-p).
|
||||
|
||||
- Added support for the `BAT_STYLE` environment variable, see #208 (@ms2300)
|
||||
|
||||
- Added `OneHalf` theme for terminals with a light-gray background, see #256
|
||||
|
||||
- Added new syntaxes for CSV, JSX in JavaScript and TypeScript, Cabal, Dart,
|
||||
F#, PureScript, Swift, Crystal, PowerShell (Many Thanks to @tobenna and @mimadrid)
|
||||
|
||||
## Changes
|
||||
|
||||
- Query `git diff` only when needed, see #303 (@ShikChen)
|
||||
|
||||
- Disable wrapping when `--plain` is used, see #289 (@eth-p)
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Can read files named `cache`, see #275 (@BK1603)
|
||||
|
||||
- A lot of bugfixes for Windows, see #252, #264
|
||||
|
||||
- Detect `less` reliably and in a portable way, see #271 and #290 (@Aankhen)
|
||||
|
||||
- last decoration line is not formatted properly with `--wrap never`, see #299 (@Rogach)
|
||||
|
||||
- Do not show file header for directories, see #292
|
||||
|
||||
## Other
|
||||
|
||||
- Enabled a new `aarch64` build target, see #258 (@rachitchokshi)
|
||||
|
||||
- Provide Debian packages for `armhf`, see #280 (@rachitchokshi)
|
||||
|
||||
- Added README section about "`bat` on Windows" (@Aankhen)
|
||||
|
||||
- Windows installation via scoop (@meltinglava)
|
||||
|
||||
# v0.6.1
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Fixed panic when running `bat --list-languages | head`, see #232 (@mchlrhw)
|
||||
- Respect `--color` settings for `--list-themes` and `--list-languages`, see #233
|
||||
- Git modifications now work on Windows
|
||||
|
||||
## Other
|
||||
|
||||
- There will be auto-generated Windows releases, starting with this version (@anykao)
|
||||
|
||||
# v0.6.0
|
||||
|
||||
## Features
|
||||
|
||||
- The `--list-themes` option now shows a preview for each highlighting theme (@ms2300)
|
||||
- Added `-p`/`--plain` as an alias for `--style=plain`, see #212 (@ms2300)
|
||||
- Major refactorings, enabling some progress on #150. In non-interactive mode, `bat` will now copy input bytes 1:1.
|
||||
- New languages: Elm, Kotlin, Puppet, TypeScript, see #215 #216 #217 #218
|
||||
- New syntax highlighting theme: zenburn (@colindean)
|
||||
|
||||
## Changes
|
||||
|
||||
- New themes in `$BAT_CONFIG_DIR/themes` are now loaded *in addition* to
|
||||
the default themes (they may also override), see #172
|
||||
- The `Default.tmTheme` symlink is not necessary anymore.
|
||||
|
||||
## Bugfixes
|
||||
|
||||
* Using `bat cache --init` leads to duplicated syntaxes, see #206
|
||||
|
||||
## Other
|
||||
|
||||
* Extended and cleaned-up `--help` text.
|
||||
* Added initial version of a man page, see #52
|
||||
* New README sections: *Development* and *Troubleshooting*, see #220
|
||||
|
||||
# v0.5.0
|
||||
|
||||
## Features
|
||||
|
||||
- Added `--line-range n:m` option to print a range of lines, see #159 (@tskinn)
|
||||
- The syntax highlighting theme can now be controlled by the `BAT_THEME` environment variable, see [README](https://github.com/sharkdp/bat#highlighting-theme) and #177 (@mandx)
|
||||
- The `PAGER` and `BAT_PAGER` environment variables can be used to control the pager that `bat` uses, see #158 and the [new README section](https://github.com/sharkdp/bat#using-a-different-pager)
|
||||
- Added syntax highlighting for Nix, see #180
|
||||
- Added syntax highlighting for AWK (Gert Hulselmans)
|
||||
|
||||
## Changes
|
||||
|
||||
- The customization of syntax sets and theme sets is now separated. Syntax definitions are now loaded *in addition* to the ones that are stored in the `bat` binary by default. Please refer to these new sections in the README: [Adding new syntaxes](https://github.com/sharkdp/bat#adding-new-syntaxes--language-definitions), [Adding new themes](https://github.com/sharkdp/bat#adding-new-themes), also see #172
|
||||
- The color for the filename is now the default foreground color. The colors for the grid and the line numbers is now determined from the syntax highlighting theme, which now also works for light backgrounds, see #178.
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Escape Sequences get partially stripped, see #182 (@eth-p)
|
||||
- Use separate Git repository for snapshot testing, see #165 and #161
|
||||
- Markdown breaking on JavaScript, see #183
|
||||
|
||||
## Other
|
||||
|
||||
- Binaries for armv7 are now provided, see #196
|
||||
- `bat` is now in the official [Arch package repositories](https://www.archlinux.org/packages/community/x86_64/bat/).
|
||||
- Optimizations in the RGB => 8-bit conversion (@mina86)
|
||||
|
||||
# v0.4.1
|
||||
|
||||
(this is just a small bugfix release, see 0.4.0 for all features and changes)
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Fix problem with `cargo test` when `bat` is not checked out in a Git repository, see #161
|
||||
|
||||
# v0.4.0
|
||||
|
||||
## Features
|
||||
|
||||
* Support for line-wrapping, see #54 and #102 (@eth-p)
|
||||
* New and updated `--style` parameter, see #74 and README (@pitkley)
|
||||
* Added `--theme` and `--list-themes` options, see #89 (@rleungx)
|
||||
* Added syntax highlighting for: Julia (@iamed2), Dockerfiles, VimL, CMake, INI, Less
|
||||
* Added a few popular Sublime Text highlighting themes, see #133
|
||||
* Support for bold, italic and underline font styles, see #96
|
||||
* Support for 32bit systems is now available, see #84
|
||||
* Added `-u` and `-n` options, see #134
|
||||
* ANSI color support on Windows 10
|
||||
|
||||
## Changes
|
||||
|
||||
* The customization folder for own syntaxes has been renamed from `syntax` to `syntaxes`, see README.
|
||||
* Changed Markdown syntax to the default Sublime Text syntax, see #157
|
||||
* Sorted language listing (@rleungx)
|
||||
* Command line arguments like `--theme` or `--color` can now override themselves.
|
||||
* Improved `--help` text.
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Fixed crash for (really) small terminal sizes, see #117 (@eth-p)
|
||||
- Syntax detection for `.bashrc`, `CMakeLists.txt` etc., see #100
|
||||
- Properly handle lines with invalid UTF-8, see #7 (@BrainMaestro)
|
||||
- Better error handling, see #17 (@rleungx and @sharkdp)
|
||||
- Proper handling of UTF-8 characters in `less`, see #98 (@ghuls)
|
||||
- Build fix on nightly, see #148 (@tathanhdinh)
|
||||
|
||||
## Other
|
||||
|
||||
- [Comparison with alternative projects](https://github.com/sharkdp/bat/blob/master/doc/alternatives.md).
|
||||
- New "bat" logo in the README, see #119 (@jraulhernandezi)
|
||||
- Output test cases (@BrainMaestro)
|
||||
- Lots of great refactoring work (@BrainMaestro)
|
||||
|
||||
# v0.3.0
|
||||
|
||||
## Features
|
||||
|
||||
* Automatic paging by integrating with `less`, see #29 (@BrainMaestro)
|
||||
* Added support for reading from standard input, see #2
|
||||
* Added support for writing to non-interactive terminals (pipes, files, ..); new
|
||||
`--color=auto/always/never` option, see #26 (@BrainMaestro)
|
||||
* Added `--list-languages` option to print all available syntaxes, see #69 (@connorkuehl)
|
||||
* New option to specify the syntax via `-l`/`--language`, see #19 (@BrainMaestro)
|
||||
* New option to control the output style (`--style`), see #5 (@nakulcg)
|
||||
* Added syntax highlighting support for TOML files, see #37
|
||||
|
||||
## Changes
|
||||
|
||||
* The `init-cache` sub-command has been removed. The cache can now be controlled via
|
||||
`bat cache`. See `bat cache -h` for all available commands.
|
||||
|
||||
## Bug fixes
|
||||
|
||||
* Get git repository from file path instead of current directory, see #22 (@nakulcg)
|
||||
* Process substitution can now be used with bat (`bat <(echo a) <(echo b)`), see #80
|
||||
|
||||
## Thanks
|
||||
|
||||
I'd like to say a big THANK YOU to all contributors and everyone that has given us
|
||||
some form of feedback.
|
||||
|
||||
Special thanks go to @BrainMaestro for his huge support with new features, bug reports
|
||||
and code reviews!
|
||||
|
||||
# v0.2.3
|
||||
|
||||
- Added a new statically linked version of bat (`..-musl-..`)
|
||||
|
||||
# v0.2.2
|
||||
|
||||
- Remove openssl dependency completely, see #30.
|
||||
|
||||
# v0.2.1
|
||||
|
||||
- Added Elixir syntax, see #25.
|
||||
- Use libcurl-openssl instead of libcurl-gnutls, see #30.
|
||||
|
||||
# v0.2.0
|
||||
|
||||
- Support for custom syntaxes, add 'Markdown extended' theme
|
||||
- Bugfix: Git modifications not shown from child folder
|
||||
|
||||
# v0.1.0
|
||||
|
||||
Initial release
|
||||
Generated
+258
-218
File diff suppressed because it is too large
Load Diff
+37
-14
@@ -7,7 +7,7 @@ license = "MIT/Apache-2.0"
|
||||
name = "bat"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/sharkdp/bat"
|
||||
version = "0.13.0"
|
||||
version = "0.15.1"
|
||||
exclude = [
|
||||
"assets/syntaxes/*",
|
||||
"assets/themes/*",
|
||||
@@ -15,48 +15,71 @@ exclude = [
|
||||
build = "build.rs"
|
||||
edition = '2018'
|
||||
|
||||
[features]
|
||||
default = ["application"]
|
||||
# Feature required for bat the application. Should be disabled when depending on
|
||||
# bat as a library.
|
||||
application = [
|
||||
"atty",
|
||||
"clap",
|
||||
"dirs",
|
||||
"git",
|
||||
"lazy_static",
|
||||
"liquid",
|
||||
"paging",
|
||||
"wild",
|
||||
"regex-onig",
|
||||
]
|
||||
git = ["git2"] # Support indicating git modifications
|
||||
paging = ["shell-words"] # Support applying a pager on the output
|
||||
regex-onig = ["syntect/regex-onig"] # Use the "oniguruma" regex engine
|
||||
regex-fancy = ["syntect/regex-fancy"] # Use the rust-only "fancy-regex" engine
|
||||
|
||||
[dependencies]
|
||||
atty = "0.2.14"
|
||||
atty = { version = "0.2.14", optional = true }
|
||||
ansi_term = "^0.12.1"
|
||||
ansi_colours = "^1.0"
|
||||
console = "0.10"
|
||||
dirs = "2.0"
|
||||
lazy_static = "1.4"
|
||||
wild = "2.0"
|
||||
console = "0.11.2"
|
||||
dirs = { version = "2.0", optional = true }
|
||||
lazy_static = { version = "1.4", optional = true }
|
||||
wild = { version = "2.0", optional = true }
|
||||
content_inspector = "0.2.4"
|
||||
encoding = "0.2"
|
||||
shell-words = "0.1.0"
|
||||
shell-words = { version = "0.1.0", optional = true }
|
||||
unicode-width = "0.1.7"
|
||||
globset = "0.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_yaml = "0.8"
|
||||
semver = "0.9"
|
||||
|
||||
[dependencies.git2]
|
||||
version = "0.13"
|
||||
optional = true
|
||||
default-features = false
|
||||
features = []
|
||||
|
||||
[dependencies.syntect]
|
||||
version = "3.3.0"
|
||||
version = "4.1.0"
|
||||
default-features = false
|
||||
features = ["parsing", "yaml-load", "dump-load", "dump-create"]
|
||||
|
||||
[dependencies.clap]
|
||||
version = "2.33"
|
||||
optional = true
|
||||
default-features = false
|
||||
features = ["suggestions", "color", "wrap_help"]
|
||||
|
||||
[dependencies.error-chain]
|
||||
version = "0.12"
|
||||
default-features = false
|
||||
features = []
|
||||
|
||||
[dev-dependencies]
|
||||
tempdir = "0.3"
|
||||
assert_cmd = "0.12.0"
|
||||
assert_cmd = "1.0.1"
|
||||
|
||||
[build-dependencies]
|
||||
clap = "2.33"
|
||||
liquid = "0.19"
|
||||
lazy_static = "1.4"
|
||||
clap = { version = "2.33", optional = true }
|
||||
liquid = { version = "0.20", optional = true }
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
|
||||
@@ -157,7 +157,7 @@ formatting problems.
|
||||
|
||||
If you prefer to have this bundled in a new command, you can also use [`batman`](https://github.com/eth-p/bat-extras/blob/master/doc/batman.md).
|
||||
|
||||
Note that the [Manpage syntax](assets/syntaxes/Manpage.sublime-syntax) is developed in this repository and still needs some work.
|
||||
Note that the [Manpage syntax](assets/syntaxes/02_Extra/Manpage.sublime-syntax) is developed in this repository and still needs some work.
|
||||
|
||||
#### `prettier` / `shfmt` / `rustfmt`
|
||||
|
||||
@@ -171,16 +171,23 @@ The [`prettybat`](https://github.com/eth-p/bat-extras/blob/master/doc/prettybat.
|
||||
### On Ubuntu
|
||||
*... and other Debian-based Linux distributions.*
|
||||
|
||||
You can install [the Ubuntu `bat` package](https://packages.ubuntu.com/eoan/bat) or [the Debian `bat` package](https://packages.debian.org/sid/bat) since Ubuntu Eoan 19.10 or Debian unstable sid.
|
||||
`bat` is making its way through the [Ubuntu](https://packages.ubuntu.com/eoan/bat) and
|
||||
[Debian](https://packages.debian.org/sid/bat) package release process, and is available
|
||||
for Ubuntu as of Eoan 19.10. On Debian `bat` is currently only available on the unstable
|
||||
"Sid" branch.
|
||||
|
||||
If your Ubuntu/Debian installation is new enough you can simply run:
|
||||
|
||||
```bash
|
||||
apt install bat
|
||||
```
|
||||
|
||||
If you want to run the latest release of bat or if you are on older versions of Ubuntu/Debian, download the latest `.deb` package from the [release page](https://github.com/sharkdp/bat/releases)
|
||||
and install it via:
|
||||
If the package has not yet been promoted to your Ubuntu/Debian installation, or you want
|
||||
the most recent release of `bat`, download the latest `.deb` package from the
|
||||
[release page](https://github.com/sharkdp/bat/releases) and install it via:
|
||||
|
||||
```bash
|
||||
sudo dpkg -i bat_0.13.0_amd64.deb # adapt version number and architecture
|
||||
sudo dpkg -i bat_0.15.1_amd64.deb # adapt version number and architecture
|
||||
```
|
||||
|
||||
### On Alpine Linux
|
||||
@@ -341,11 +348,11 @@ binaries are also available: look for archives with `musl` in the file name.
|
||||
|
||||
### From source
|
||||
|
||||
If you want to build `bat` from source, you need Rust 1.36 or
|
||||
If you want to build `bat` from source, you need Rust 1.40 or
|
||||
higher. You can then use `cargo` to build everything:
|
||||
|
||||
```bash
|
||||
cargo install bat
|
||||
cargo install --locked bat
|
||||
```
|
||||
|
||||
On some platforms, you might need to install `llvm` and/or `libclang-dev`.
|
||||
@@ -464,8 +471,8 @@ it should work out of the box.
|
||||
### Dark mode
|
||||
|
||||
If you make use of the dark mode feature in macOS, you might want to configure `bat` to use a different
|
||||
theme based on the OS theme. The following snippet uses the `default` theme when in the light mode
|
||||
and the `GitHub` theme when in the dark mode.
|
||||
theme based on the OS theme. The following snippet uses the `default` theme when in the _dark mode_
|
||||
and the `GitHub` theme when in the _light mode_.
|
||||
|
||||
```bash
|
||||
alias cat="bat --theme=\$(defaults read -globalDomain AppleInterfaceStyle &> /dev/null && echo default || echo GitHub)"
|
||||
@@ -485,6 +492,11 @@ non-default location of the configuration file:
|
||||
export BAT_CONFIG_PATH="/path/to/bat.conf"
|
||||
```
|
||||
|
||||
A default configuration file can be created with the `--generate-config-file` option.
|
||||
```bash
|
||||
bat --generate-config-file
|
||||
```
|
||||
|
||||
### Format
|
||||
|
||||
The configuration file is a simple list of command line arguments. Use `bat --help` to see a full list of possible options and values. In addition, you can add comments by prepending a line with the `#` character.
|
||||
@@ -594,11 +606,11 @@ cargo build --bins
|
||||
cargo test
|
||||
|
||||
# Install (release version)
|
||||
cargo install
|
||||
cargo install --locked
|
||||
|
||||
# Build a bat binary with modified syntaxes and themes
|
||||
bash assets/create.sh
|
||||
cargo install -f
|
||||
cargo install --locked --force
|
||||
```
|
||||
|
||||
## Maintainers
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ environment:
|
||||
|
||||
install:
|
||||
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
||||
- rustup-init.exe -y --default-host %TARGET%
|
||||
- rustup-init.exe -y --default-host %TARGET% --profile minimal
|
||||
- set PATH=%PATH%;C:\cargo\bin
|
||||
- rustc -Vv
|
||||
- cargo -V
|
||||
|
||||
Vendored
+3
@@ -39,3 +39,6 @@ The following files have been manually modified after converting from a `.tmLang
|
||||
|
||||
* `Assembly (x86_64)` has been manually added from https://github.com/13xforever/x86-assembly-textmate-bundle due to `git clone` recursion problems
|
||||
* `Nim.sublime-syntax` has been added manually from https://github.com/getzola/zola/blob/master/sublime_syntaxes/Nim.sublime-syntax as there was no suitable Git repository for it. The original syntax seems to originate from https://github.com/Varriount/NimLime
|
||||
* `Rego.sublime-syntax` has been added manually from https://github.com/open-policy-agent/opa/blob/master/misc/syntax/sublime/rego.sublime-syntax
|
||||
as it is not kept in a standalone repository. The file is generated from
|
||||
https://github.com/open-policy-agent/opa/blob/master/misc/syntax/textmate/Rego.tmLanguage
|
||||
|
||||
Vendored
+16
-6
@@ -42,12 +42,22 @@ fi
|
||||
# Always remove the local cache to avoid any confusion
|
||||
bat cache --clear
|
||||
|
||||
# TODO: Remove this (and the reverse part below) when
|
||||
# https://github.com/trishume/syntect/issues/222 has been fixed
|
||||
JAVADOC_FILE="${ASSET_DIR}/syntaxes/01_Packages/Java/JavaDoc.sublime-syntax"
|
||||
JAVADOC_PATCH="${ASSET_DIR}/JavaDoc.sublime-syntax.patch"
|
||||
patch "$JAVADOC_FILE" "$JAVADOC_PATCH"
|
||||
# TODO:
|
||||
# - Remove the JavaDoc patch once https://github.com/trishume/syntect/issues/222 has been fixed
|
||||
# - Remove the C# patch once https://github.com/sublimehq/Packages/pull/2331 has been merged
|
||||
|
||||
(
|
||||
cd "$ASSET_DIR"
|
||||
for patch in patches/*.patch; do
|
||||
patch --strip=0 < "$patch"
|
||||
done
|
||||
)
|
||||
|
||||
bat cache --build --blank --source="$ASSET_DIR" --target="$ASSET_DIR"
|
||||
|
||||
patch -R "$JAVADOC_FILE" "$JAVADOC_PATCH"
|
||||
(
|
||||
cd "$ASSET_DIR"
|
||||
for patch in patches/*.patch; do
|
||||
patch --strip=0 --reverse < "$patch"
|
||||
done
|
||||
)
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
diff --git syntaxes/01_Packages/C#/C#.sublime-syntax syntaxes/01_Packages/C#/C#.sublime-syntax
|
||||
index ed494f8b..01b710e8 100644
|
||||
--- syntaxes/01_Packages/C#/C#.sublime-syntax
|
||||
+++ syntaxes/01_Packages/C#/C#.sublime-syntax
|
||||
@@ -1312,7 +1312,7 @@ contexts:
|
||||
2: punctuation.separator.cs
|
||||
3: punctuation.section.brackets.end.cs
|
||||
4: keyword.operator.pointer.cs
|
||||
- - match: \((?=(?:[^,)(]*|\([^\)]*\))*,)
|
||||
+ - match: \((?=(?:[^,)(]|\([^\)]*\))*,)
|
||||
scope: punctuation.section.group.begin.cs
|
||||
push:
|
||||
- meta_scope: meta.group.tuple.cs
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
diff --git syntaxes/01_Packages/Makefile/Makefile.sublime-syntax syntaxes/01_Packages/Makefile/Makefile.sublime-syntax
|
||||
index 3cc3a97e..0c7a3f24 100644
|
||||
--- syntaxes/01_Packages/Makefile/Makefile.sublime-syntax
|
||||
+++ syntaxes/01_Packages/Makefile/Makefile.sublime-syntax
|
||||
@@ -44,64 +44,50 @@ variables:
|
||||
# variable substitutions anywhere. We try to remedy this by hacking in a
|
||||
# regex that matches up to four levels of nested parentheses, and ignores
|
||||
# whatever's inside the parentheses.
|
||||
- nps: '[^()]*'
|
||||
- open: '(?:\('
|
||||
- close: '\))?' # ignore this invalid.illegal
|
||||
+ nps_unnested: '[^()]*'
|
||||
+ nps: '[^()]*(?=[()])'
|
||||
+ open: '(?:{{nps}}\('
|
||||
+ close: '\){{nps_unnested}})?' # ignore this invalid.illegal
|
||||
just_eat: | # WARNING: INSANITY FOLLOWS!
|
||||
- (?x) # ignore whitespace in this regex
|
||||
- {{nps}} # level 0
|
||||
+ (?x)(?: # ignore whitespace in this regex
|
||||
{{open}} # start level 1 __
|
||||
- {{nps}} # level 1 _______ /*_>-<
|
||||
{{open}} # start level 2 ___/ _____ \__/ /
|
||||
- {{nps}} # level 2 <____/ \____/
|
||||
{{open}} # start level 3 is like snek... (by Valerie Haecky)
|
||||
- {{nps}} # level 3
|
||||
{{open}} # start level 4
|
||||
{{nps}} # level 4
|
||||
{{close}} # end level 4
|
||||
- {{nps}} # level 3
|
||||
{{close}} # end level 3
|
||||
- {{nps}} # level 2
|
||||
{{open}} # start level 3
|
||||
- {{nps}} # level 3
|
||||
{{open}} # start level 4
|
||||
{{nps}} # level 4
|
||||
{{close}} # end level 4
|
||||
- {{nps}} # level 3
|
||||
{{close}} # end level 3
|
||||
- {{nps}} # level 2
|
||||
+ {{nps}}
|
||||
{{close}} # end level 2
|
||||
- {{nps}} # level 1
|
||||
{{open}} # start level 2
|
||||
- {{nps}} # level 2
|
||||
{{open}} # start level 3
|
||||
- {{nps}} # level 3
|
||||
{{open}} # start level 4
|
||||
{{nps}} # level 4
|
||||
{{close}} # end level 4
|
||||
- {{nps}} # level 3
|
||||
+ {{nps}}
|
||||
{{close}} # end level 3
|
||||
- {{nps}} # level 2
|
||||
{{open}} # start level 3
|
||||
- {{nps}} # level 3
|
||||
{{open}} # start level 4
|
||||
{{nps}} # level 4
|
||||
{{close}} # end level 4
|
||||
- {{nps}} # level 3
|
||||
+ {{nps}}
|
||||
{{close}} # end level 3
|
||||
- {{nps}} # level 2
|
||||
{{open}} # start level 3
|
||||
- {{nps}} # level 3
|
||||
{{open}} # start level 4
|
||||
{{nps}} # level 4
|
||||
{{close}} # end level 4
|
||||
- {{nps}} # level 3
|
||||
+ {{nps}}
|
||||
{{close}} # end level 3
|
||||
- {{nps}} # level 2
|
||||
+ {{nps}}
|
||||
{{close}} # end level 2
|
||||
- {{nps}} # level 1
|
||||
+ {{nps}}
|
||||
{{close}} # end level 1
|
||||
- {{nps}} # level 0
|
||||
+ |{{nps_unnested}})
|
||||
rule_lookahead: '{{just_eat}}{{ruleassign}}{{just_eat}}'
|
||||
|
||||
var_lookahead_base: '{{just_eat}}({{varassign}}|{{shellassign}}){{just_eat}}'
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
diff --git syntaxes/01_Packages/Markdown/Markdown.sublime-syntax syntaxes/01_Packages/Markdown/Markdown.sublime-syntax
|
||||
index 19dc685d..6afd87ae 100644
|
||||
--- syntaxes/01_Packages/Markdown/Markdown.sublime-syntax
|
||||
+++ syntaxes/01_Packages/Markdown/Markdown.sublime-syntax
|
||||
@@ -24,7 +24,6 @@ variables:
|
||||
)
|
||||
[ \t]*$ # followed by any number of tabs or spaces, followed by the end of the line
|
||||
)
|
||||
- setext_escape: ^(?=\s{0,3}(?:---+|===+)\s*$)
|
||||
block_quote: (?:[ ]{,3}>(?:.|$)) # between 0 and 3 spaces, followed by a greater than sign, followed by any character or the end of the line
|
||||
atx_heading: (?:[#]{1,6}\s*) # between 1 and 6 hashes, followed by any amount of whitespace
|
||||
indented_code_block: (?:[ ]{4}|\t) # 4 spaces or a tab
|
||||
@@ -277,69 +276,40 @@ contexts:
|
||||
8: markup.underline.link.markdown
|
||||
push: [link-ref-def, after-link-title, link-title]
|
||||
- match: '^(?=\S)(?![=-]{3,}\s*$)'
|
||||
- branch_point: heading2-branch
|
||||
- branch:
|
||||
- - not-heading2
|
||||
- - heading2
|
||||
-
|
||||
- not-paragraph:
|
||||
- - match: |-
|
||||
- (?x) # pop out of this context when one of the following conditions are met:
|
||||
- ^(?:
|
||||
- \s*$ # the line is blank (or only contains whitespace)
|
||||
- | (?=
|
||||
- {{block_quote}} # a block quote begins the line
|
||||
- | [ ]{,3}[*+-][ ] # an unordered list item begins the line
|
||||
- | [ ]{,3}1[.][ ] # an ordered list item with number "1" begins the line
|
||||
- | \# # an ATX heading begins the line
|
||||
- | [ ]{,3}<( # all types of HTML blocks except type 7 may interrupt a paragraph
|
||||
- {{html_tag_block_end_at_close_tag}} # 1
|
||||
- | !-- # 2
|
||||
- | \? # 3
|
||||
- | ![A-Z] # 4
|
||||
- | !\[CDATA\[ # 5
|
||||
- | {{html_tag_block_end_at_blank_line}} # 6
|
||||
+ push:
|
||||
+ - meta_scope: meta.paragraph.markdown
|
||||
+ - match: |-
|
||||
+ (?x) # pop out of this context when one of the following conditions are met:
|
||||
+ ^(?:
|
||||
+ \s*$ # the line is blank (or only contains whitespace)
|
||||
+ | (?=
|
||||
+ {{block_quote}} # a block quote begins the line
|
||||
+ | [ ]{,3}[*+-][ ] # an unordered list item begins the line
|
||||
+ | [ ]{,3}1[.][ ] # an ordered list item with number "1" begins the line
|
||||
+ | \# # an ATX heading begins the line
|
||||
+ | [ ]{,3}<( # all types of HTML blocks except type 7 may interrupt a paragraph
|
||||
+ {{html_tag_block_end_at_close_tag}} # 1
|
||||
+ | !-- # 2
|
||||
+ | \? # 3
|
||||
+ | ![A-Z] # 4
|
||||
+ | !\[CDATA\[ # 5
|
||||
+ | {{html_tag_block_end_at_blank_line}} # 6
|
||||
+ )
|
||||
)
|
||||
)
|
||||
- )
|
||||
- pop: true
|
||||
-
|
||||
- not-heading2:
|
||||
- - include: not-paragraph
|
||||
- - match: (?=\S)
|
||||
- branch_point: heading1-branch
|
||||
- branch:
|
||||
- - paragraph
|
||||
- - heading1
|
||||
- - match: ''
|
||||
- pop: true
|
||||
-
|
||||
- paragraph:
|
||||
- - meta_scope: meta.paragraph.markdown
|
||||
- - match: ^\s{0,3}===+\s*$
|
||||
- fail: heading1-branch
|
||||
- - match: ^\s{0,3}---+\s*$
|
||||
- fail: heading2-branch
|
||||
- - include: not-paragraph
|
||||
- - include: inline-bold-italic-linebreak
|
||||
- - include: scope:text.html.basic
|
||||
-
|
||||
- heading1:
|
||||
- - meta_scope: markup.heading.1.markdown
|
||||
- - include: inline-bold-italic-linebreak
|
||||
- - match: '^[ \t]{0,3}(={3,})(?=[ \t]*$)'
|
||||
- captures:
|
||||
- 1: markup.heading.1.setext.markdown punctuation.definition.heading.setext.markdown
|
||||
- pop: true
|
||||
-
|
||||
- heading2:
|
||||
- - meta_scope: markup.heading.2.markdown
|
||||
- - include: inline-bold-italic-linebreak
|
||||
- - match: '^[ \t]{0,3}(-{3,})(?=[ \t]*$)'
|
||||
- captures:
|
||||
- 1: markup.heading.2.setext.markdown punctuation.definition.heading.setext.markdown
|
||||
- pop: true
|
||||
-
|
||||
+ pop: true
|
||||
+ - include: inline-bold-italic-linebreak
|
||||
+ - include: scope:text.html.basic
|
||||
+ - match: '^(={3,})(?=[ \t]*$)'
|
||||
+ scope: markup.heading.1.setext.markdown
|
||||
+ captures:
|
||||
+ 1: punctuation.definition.heading.setext.markdown
|
||||
+ pop: true
|
||||
+ - match: '^(-{3,})(?=[ \t]*$)'
|
||||
+ scope: markup.heading.2.setext.markdown
|
||||
+ captures:
|
||||
+ 1: punctuation.definition.heading.setext.markdown
|
||||
+ pop: true
|
||||
link-ref-def:
|
||||
- meta_scope: meta.link.reference.def.markdown
|
||||
- match: ''
|
||||
@@ -430,8 +400,6 @@ contexts:
|
||||
push:
|
||||
- meta_scope: markup.bold.markdown
|
||||
- meta_content_scope: markup.italic.markdown
|
||||
- - match: '{{setext_escape}}'
|
||||
- pop: true
|
||||
- match: |-
|
||||
(?x)
|
||||
[ \t]*\*{4,} # if there are more than 3 its not applicable to be bold or italic
|
||||
@@ -446,8 +414,6 @@ contexts:
|
||||
scope: punctuation.definition.bold.end.markdown
|
||||
set:
|
||||
- meta_content_scope: markup.italic.markdown
|
||||
- - match: '{{setext_escape}}'
|
||||
- pop: true
|
||||
- match: |-
|
||||
(?x)
|
||||
[ \t]*\*{3,} # if there are more than 3 its not applicable to be bold or italic
|
||||
@@ -463,8 +429,6 @@ contexts:
|
||||
scope: punctuation.definition.italic.end.markdown
|
||||
set:
|
||||
- meta_content_scope: markup.bold.markdown
|
||||
- - match: '{{setext_escape}}'
|
||||
- pop: true
|
||||
- match: |-
|
||||
(?x)
|
||||
[ \t]*\*{3,} # if there are more than 3 its not applicable to be bold or italic
|
||||
@@ -727,8 +691,6 @@ contexts:
|
||||
scope: punctuation.definition.italic.begin.markdown
|
||||
push:
|
||||
- meta_scope: markup.italic.markdown
|
||||
- - match: '{{setext_escape}}'
|
||||
- pop: true
|
||||
- match: |-
|
||||
(?x)
|
||||
[ \t]*\*{4,} # if there are more than 3 its not applicable to be bold or italic
|
||||
@@ -745,8 +707,6 @@ contexts:
|
||||
scope: punctuation.definition.italic.begin.markdown
|
||||
push:
|
||||
- meta_scope: markup.italic.markdown
|
||||
- - match: '{{setext_escape}}'
|
||||
- pop: true
|
||||
- match: |-
|
||||
(?x)
|
||||
[ \t]*_{4,} # if there are more than 3 its not applicable to be bold or italic
|
||||
@@ -773,8 +733,6 @@ contexts:
|
||||
- include: bold-italic-trailing
|
||||
bold-italic-trailing:
|
||||
- include: scope:text.html.basic
|
||||
- - match: '{{setext_escape}}'
|
||||
- pop: true
|
||||
- match: ^\s*$\n?
|
||||
scope: invalid.illegal.non-terminated.bold-italic.markdown
|
||||
pop: true
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
diff --git syntaxes/01_Packages/Rust/Rust.sublime-syntax syntaxes/01_Packages/Rust/Rust.sublime-syntax
|
||||
index 3c354486..24727a4e 100644
|
||||
--- syntaxes/01_Packages/Rust/Rust.sublime-syntax
|
||||
+++ syntaxes/01_Packages/Rust/Rust.sublime-syntax
|
||||
@@ -907,6 +907,8 @@ contexts:
|
||||
- include: type-any-identifier
|
||||
- match: ':'
|
||||
scope: punctuation.separator.rust
|
||||
+ - match: (?=;)
|
||||
+ pop: true
|
||||
|
||||
fn-body:
|
||||
- meta_scope: meta.function.rust
|
||||
Vendored
BIN
Binary file not shown.
+1
Submodule assets/syntaxes/02_Extra/Email added at fc034fb85a
+1
Submodule assets/syntaxes/02_Extra/Fortran added at dcf4f24f1c
+1
Submodule assets/syntaxes/02_Extra/QML added at e3e058a21f
+103
@@ -0,0 +1,103 @@
|
||||
%YAML 1.2
|
||||
---
|
||||
# http://www.sublimetext.com/docs/3/syntax.html
|
||||
name: QML
|
||||
file_extensions:
|
||||
- qml
|
||||
- qmlproject
|
||||
scope: source.qml
|
||||
contexts:
|
||||
main:
|
||||
- match: /\*(?!/)
|
||||
comment: Block comment.
|
||||
push:
|
||||
- meta_scope: comment.block.documentation.qml
|
||||
- match: \*/
|
||||
pop: true
|
||||
- match: //.*$
|
||||
comment: Line comment.
|
||||
scope: comment.line.double-slash.qml
|
||||
- match: \b(import)\s+
|
||||
comment: import statement.
|
||||
captures:
|
||||
1: keyword.other.import.qml
|
||||
push:
|
||||
- meta_scope: meta.import.qml
|
||||
- match: $
|
||||
pop: true
|
||||
- match: '([\w\d\.]+)\s+(\d+\.\d+)(?:\s+(as)\s+([A-Z][\w\d]*))?'
|
||||
comment: "import Namespace VersionMajor.VersionMinor [as SingletonTypeIdentifier]"
|
||||
scope: meta.import.namespace.qml
|
||||
captures:
|
||||
1: entity.name.class.qml
|
||||
2: constant.numeric.qml
|
||||
3: keyword.other.import.qml
|
||||
4: entity.name.class.qml
|
||||
- match: '(\"[^\"]+\")(?:\s+(as)\s+([A-Z][\w\d]*))?'
|
||||
comment: "import <string> [as Script]"
|
||||
scope: meta.import.dirjs.qml
|
||||
captures:
|
||||
1: string.quoted.double.qml
|
||||
2: keyword.other.import.qml
|
||||
3: entity.name.class.qml
|
||||
- match: '\b[A-Z]\w*\b'
|
||||
comment: Capitalized word (class or enum).
|
||||
scope: support.class.qml
|
||||
- match: '(((^|\{)\s*)|\b)on[A-Z]\w*\b'
|
||||
comment: onSomething - handler.
|
||||
scope: support.class.qml
|
||||
- match: '(?:^|\{)\s*(id)\s*\:\s*([^;\s]+)\b'
|
||||
comment: "id: <something>"
|
||||
scope: meta.id.qml
|
||||
captures:
|
||||
1: keyword.other.qml
|
||||
2: storage.modifier.qml
|
||||
- match: '^\s*(?:(default|readonly)\s+)?(property)\s+(?:(alias)|([\w\<\>]+))\s+(\w+)'
|
||||
comment: property definition.
|
||||
scope: meta.propertydef.qml
|
||||
captures:
|
||||
1: keyword.other.qml
|
||||
2: keyword.other.qml
|
||||
3: keyword.other.qml
|
||||
4: storage.type.qml
|
||||
5: entity.other.attribute-name.qml
|
||||
- match: \b(signal)\s+(\w+)\s*
|
||||
comment: "signal <signalName>[([<type> <parameter>[, ...]])]"
|
||||
captures:
|
||||
1: keyword.other.qml
|
||||
2: support.function.qml
|
||||
push:
|
||||
- meta_scope: meta.signal.qml
|
||||
- match: ;|(?=/)|$
|
||||
pop: true
|
||||
- match: (\w+)\s+(\w+)
|
||||
scope: meta.signal.parameters.qml
|
||||
captures:
|
||||
1: storage.type.qml
|
||||
2: variable.parameter.qml
|
||||
- match: (?:\b|\s+)(?:(true|false|null|undefined)|(var|void)|(on|as|enum|connect|break|case|catch|continue|debugger|default|delete|do|else|finally|for|if|in|instanceof|new|return|switch|this|throw|try|typeof|while|with))\b
|
||||
comment: js keywords.
|
||||
scope: meta.keyword.qml
|
||||
captures:
|
||||
1: constant.language.qml
|
||||
2: storage.type.qml
|
||||
3: keyword.control.qml
|
||||
- match: '\b(function)\s+([\w_]+)\s*(?=\()'
|
||||
comment: function definition.
|
||||
scope: meta.function.qml
|
||||
captures:
|
||||
1: storage.type.qml
|
||||
2: entity.name.function.untitled
|
||||
- match: '\b[\w_]+\s*(?=\()'
|
||||
comment: function call.
|
||||
scope: support.function.qml
|
||||
- match: '(?:^|\{|;)\s*[a-z][\w\.]*\s*(?=\:)'
|
||||
comment: "property (property: <something>)."
|
||||
scope: entity.other.attribute-name.qml
|
||||
- match: (?<=\.)\b\w*
|
||||
comment: property of the variable (name.property).
|
||||
scope: entity.other.attribute-name.qml
|
||||
- match: '\b([a-z_]\w*)\b'
|
||||
comment: All non colored words are assumed to be variables.
|
||||
scope: variable.parameter
|
||||
- include: scope:source.js
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
%YAML 1.2
|
||||
---
|
||||
# http://www.sublimetext.com/docs/3/syntax.html
|
||||
name: Rego
|
||||
file_extensions:
|
||||
- rego
|
||||
scope: source.rego
|
||||
contexts:
|
||||
main:
|
||||
- include: comment
|
||||
- include: keyword
|
||||
- include: operator
|
||||
- include: head
|
||||
- include: term
|
||||
comment:
|
||||
- match: (#).*$\n?
|
||||
scope: comment.line.number-sign.rego
|
||||
captures:
|
||||
1: punctuation.definition.comment.rego
|
||||
call:
|
||||
- match: '([a-zA-Z_][a-zA-Z0-9_]*)\('
|
||||
scope: meta.function-call.rego
|
||||
captures:
|
||||
1: support.function.any-method.rego
|
||||
constant:
|
||||
- match: \b(?:true|false|null)\b
|
||||
scope: constant.language.rego
|
||||
head:
|
||||
- match: "^([[:alpha:]_][[:alnum:]_]*)"
|
||||
captures:
|
||||
1: entity.name.function.declaration
|
||||
push:
|
||||
- meta_scope: meta.function.rego
|
||||
- match: '(=|{|\n)'
|
||||
pop: true
|
||||
- include: term
|
||||
keyword:
|
||||
- match: (^|\s+)(?:(default|not|package|import|as|with|else|some))\s+
|
||||
scope: keyword.other.rego
|
||||
number:
|
||||
- match: |-
|
||||
(?x: # turn on extended mode
|
||||
-? # an optional minus
|
||||
(?:
|
||||
0 # a zero
|
||||
| # ...or...
|
||||
[1-9] # a 1-9 character
|
||||
\d* # followed by zero or more digits
|
||||
)
|
||||
(?:
|
||||
(?:
|
||||
\. # a period
|
||||
\d+ # followed by one or more digits
|
||||
)?
|
||||
(?:
|
||||
[eE] # an e character
|
||||
[+-]? # followed by an option +/-
|
||||
\d+ # followed by one or more digits
|
||||
)? # make exponent optional
|
||||
)? # make decimal portion optional
|
||||
)
|
||||
scope: constant.numeric.rego
|
||||
operator:
|
||||
- match: \=|\!\=|>|<|<\=|>\=|\+|-|\*|%|/|\||&|:\=
|
||||
scope: keyword.operator.comparison.rego
|
||||
string:
|
||||
- match: '"'
|
||||
captures:
|
||||
0: punctuation.definition.string.begin.rego
|
||||
push:
|
||||
- meta_scope: string.quoted.double.rego
|
||||
- match: '"'
|
||||
captures:
|
||||
0: punctuation.definition.string.end.rego
|
||||
pop: true
|
||||
- match: |-
|
||||
(?x: # turn on extended mode
|
||||
\\ # a literal backslash
|
||||
(?: # ...followed by...
|
||||
["\\/bfnrt] # one of these characters
|
||||
| # ...or...
|
||||
u # a u
|
||||
[0-9a-fA-F]{4} # and four hex digits
|
||||
)
|
||||
)
|
||||
scope: constant.character.escape.rego
|
||||
- match: \\.
|
||||
scope: invalid.illegal.unrecognized-string-escape.rego
|
||||
term:
|
||||
- include: constant
|
||||
- include: string
|
||||
- include: number
|
||||
- include: call
|
||||
- include: variable
|
||||
variable:
|
||||
- match: '\b[[:alpha:]_][[:alnum:]_]*\b'
|
||||
scope: meta.identifier.rego
|
||||
+1
Submodule assets/syntaxes/02_Extra/Stylus added at 30908e3b57
+711
@@ -0,0 +1,711 @@
|
||||
%YAML 1.2
|
||||
---
|
||||
# http://www.sublimetext.com/docs/3/syntax.html
|
||||
name: Stylus
|
||||
file_extensions:
|
||||
- styl
|
||||
- stylus
|
||||
scope: source.stylus
|
||||
contexts:
|
||||
main:
|
||||
- include: comments
|
||||
- match: '^\s*(@(?:import|charset|css|font-face|(?:-webkit-)?keyframes)(?:\s+([\w-]+))?)\b'
|
||||
captures:
|
||||
1: keyword.control.at-rule.other.stylus
|
||||
2: variable.other.animation-name.stylus
|
||||
push:
|
||||
- match: '$|;|(?=\{)'
|
||||
pop: true
|
||||
- include: string-quoted
|
||||
- match: ^\s*(@media)\s*
|
||||
captures:
|
||||
1: keyword.control.at-rule.media.stylus
|
||||
push:
|
||||
- match: '$|(?=\{)'
|
||||
pop: true
|
||||
- include: media-query
|
||||
- match: |-
|
||||
(?x)
|
||||
(?<=^|;|})
|
||||
\s*
|
||||
(?=
|
||||
[\[\]'".\w$-]+
|
||||
\s*
|
||||
([?:]?=)
|
||||
(?![^\[]*\])
|
||||
)
|
||||
push:
|
||||
- match: $|;
|
||||
pop: true
|
||||
- include: expression
|
||||
- include: iteration
|
||||
- include: conditionals
|
||||
- include: return
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
|
||||
^(\s*) # starts at the beginning of line
|
||||
([\w$-]+) # identifier (name)
|
||||
(\() # start of argument list
|
||||
(?=
|
||||
.*?
|
||||
\)\s*\{ # we see a curly brace afterwards
|
||||
) # which means this is a function definition
|
||||
captures:
|
||||
2: entity.name.function.stylus
|
||||
3: punctuation.definition.parameters.start.stylus
|
||||
push:
|
||||
- meta_scope: meta.function-call.stylus
|
||||
- match: (\))
|
||||
captures:
|
||||
1: punctuation.definition.parameters.end.stylus
|
||||
pop: true
|
||||
- include: expression
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
(
|
||||
|
||||
(^|;) # starts at the beginning of line or at a ;
|
||||
\s*
|
||||
(\+?\s* # for block mixins
|
||||
[\w$-]+) # identifier (name)
|
||||
(\() # start of argument list
|
||||
(?=
|
||||
.*?
|
||||
\)\s*;?\s* # if there are only spaces and semicolons
|
||||
$|; # then this a
|
||||
)
|
||||
)
|
||||
captures:
|
||||
3: entity.other.attribute-name.mixin.stylus
|
||||
4: punctuation.definition.parameters.start.stylus
|
||||
push:
|
||||
- meta_scope: meta.function-call.stylus
|
||||
- match: (\))
|
||||
captures:
|
||||
1: punctuation.definition.parameters.end.stylus
|
||||
pop: true
|
||||
- include: expression
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
(^|(?<=\*/|\}))\s*
|
||||
(?=
|
||||
font(?!
|
||||
\s*:\s
|
||||
|
|
||||
-
|
||||
|
|
||||
.*?
|
||||
(?:
|
||||
\/|normal|bold|light(er?)|serif|sans|monospace|
|
||||
\b\d+(?:\b|px|r?em|%)|
|
||||
var\s*\(|
|
||||
['"][^\]]*$
|
||||
)
|
||||
) | # we need to distinguish between tag and property `cursor`
|
||||
cursor(?!
|
||||
\s*[:;]\s
|
||||
|
|
||||
-
|
||||
|
|
||||
.*?
|
||||
(?:
|
||||
(?:url\s*\()|
|
||||
(?:-moz-|-webkit-|-ms-)?
|
||||
(?:auto|default|none|context-menu|help|pointer|progress|
|
||||
wait|cell|crosshair|text|vertical-text|alias|copy|
|
||||
move|no-drop|not-allowed|e-resize|n-resize|ne-resize|
|
||||
nw-resize|s-resize|se-resize|sw-resize|w-resize|
|
||||
ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|
|
||||
row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing
|
||||
normal|bold|light(er?)|serif|sans|monospace)
|
||||
)
|
||||
) | (
|
||||
(
|
||||
altGlyph|altGlyphDef|altGlyphItem|animate|animateColor|
|
||||
animateMotion|animateTransform|circle|clipPath|color-profile|
|
||||
defs|desc|ellipse|feBlend|feColorMatrix|
|
||||
feComponentTransfer|feComposite|feConvolveMatrix|
|
||||
feDiffuseLighting|feDisplacementMap|feDistantLight|feFlood|
|
||||
feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feImage|feMerge|
|
||||
feMergeNode|feMorphology|feOffset|fePointLight|
|
||||
feSpecularLighting|feSpotLight|feTile|feTurbulence|filter|
|
||||
font-face|font-face-format|font-face-name|font-face-src|
|
||||
font-face-uri|foreignObject|g|glyph|glyphRef|hkern|image|line|
|
||||
linearGradient|marker|mask|metadata|missing-glyph|mpath|path|
|
||||
pattern|polygon|polyline|radialGradient|rect|set|stop|svg|
|
||||
switch|symbol|text|textPath|tref|tspan|use|view|vkern|
|
||||
a|abbr|acronym|address|applet|area|article|aside|audio|b|base|
|
||||
basefont|bdi|bdo|bgsound|big|blink|blockquote|body|br|button|
|
||||
canvas|caption|center|cite|code|col|colgroup|data|
|
||||
datalist|dd|decorator|del|details|dfn|dir|div|dl|dt|element|
|
||||
em|embed|fieldset|figcaption|figure|footer|form|frame|
|
||||
frameset|h1|h2|h3|h4|h5|h6|head|header|hgroup|hr|html|i|iframe|
|
||||
img|input|ins|isindex|kbd|keygen|label|legend|li|link|listing|
|
||||
main|map|mark|marquee|menu|menuitem|meta|meter|nav|nobr|
|
||||
noframes|noscript|object|ol|optgroup|option|output|p|param|
|
||||
plaintext|pre|progress|q|rp|rt|ruby|s|samp|script|section|
|
||||
select|shadow|small|source|spacer|span|strike|strong|style|
|
||||
sub|summary|sup|table|tbody|td|template|textarea|tfoot|th|
|
||||
thead|time|title|tr|track|tt|u|ul|var|video|wbr|xmp)
|
||||
|
||||
\s*([\s,.#\[]|:[^\s]|(?=\{|$))
|
||||
|
||||
) | (
|
||||
[:~>\[*\/] # symbols but they are valid for selector
|
||||
|
||||
) | (
|
||||
|
||||
\+\s*[\w$-]+\b\s* # are an identifier starting with $
|
||||
(?!\() # and they can't have anything besides
|
||||
|
||||
) | ( # for animtions
|
||||
|
||||
\d+(\.\d+)?%|(from|to)\b
|
||||
|
||||
) | ( # Placeholder selectors
|
||||
|
||||
\$[\w$-]+\b\s* # are an identifier starting with $
|
||||
(?=$|\{) # and they can't have anything besides
|
||||
|
||||
) | ( # CSS class
|
||||
|
||||
\.[a-zA-Z0-9_-]+
|
||||
|
||||
) | ( # CSS id
|
||||
|
||||
\#[a-zA-Z0-9_-]+
|
||||
|
||||
) | ( # Reference to parent
|
||||
|
||||
([\w\d_-]+)? # matching any word right before &
|
||||
(&) # & itself, escaped because of plist
|
||||
([\w\d_-]+)? # matching any word right after &
|
||||
)
|
||||
)
|
||||
push:
|
||||
- meta_scope: meta.selector.stylus
|
||||
- match: |-
|
||||
|
||||
|$|(?=\{\s*\}.*$)|(?=\{.*?[:;])|(?=\{)(?!.+\}.*$)
|
||||
pop: true
|
||||
- include: comma
|
||||
- match: \d+(\.\d+)?%|from|to
|
||||
scope: entity.other.animation-keyframe.stylus
|
||||
- include: selector-components
|
||||
- match: .
|
||||
scope: entity.other.attribute-name.stylus
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
(?<=^|;|{)\s* # starts after begining of line, '{' or ';''
|
||||
(?= # lookahead for
|
||||
(
|
||||
[a-zA-Z0-9_-] # then a letter
|
||||
| # or
|
||||
(\{(.*?)\}) # interpolation
|
||||
| # or
|
||||
(/\*.*?\*/) # comment
|
||||
)+
|
||||
|
||||
\s*[:\s]\s* # value is separted by colon or space
|
||||
|
||||
(?!(\s*\{)) # if there are only spaces afterwards
|
||||
|
||||
(?!
|
||||
[^}]*? # checking for an unclosed curly braces on this
|
||||
\{ # line because if one exists it means that
|
||||
[^}]* # this is a selector and not a property
|
||||
($|\})
|
||||
)
|
||||
)
|
||||
push:
|
||||
- match: '(?=\}|;)|(?<!,)\s*\n'
|
||||
pop: true
|
||||
- include: comments
|
||||
- include: interpolation
|
||||
- match: '(?<!^|;|{)\s*(?:(:)|\s)'
|
||||
captures:
|
||||
1: punctuation.separator.key-value.stylus
|
||||
push:
|
||||
- match: '(;)|(?=\})|(?=(?<!\,)\s*\n)'
|
||||
captures:
|
||||
1: punctuation.terminator.rule.stylus
|
||||
pop: true
|
||||
- include: comments
|
||||
- include: expression
|
||||
- match: "-(moz|o|ms|webkit|khtml)-"
|
||||
scope: support.type.vendor-prefix.stylus
|
||||
- match: .
|
||||
scope: meta.property-name.stylus support.type.property-name.stylus
|
||||
- match: '@extends?\s'
|
||||
captures:
|
||||
0: keyword.language.stylus
|
||||
push:
|
||||
- match: (?=$|;)
|
||||
pop: true
|
||||
- include: selector-components
|
||||
- include: string-quoted
|
||||
- include: escape
|
||||
- include: language-constants
|
||||
- include: language-operators
|
||||
- include: language-keywords
|
||||
- include: property-reference
|
||||
- include: function-call
|
||||
- match: '\{'
|
||||
scope: punctuation.section.start.stylus
|
||||
- match: '\}'
|
||||
scope: punctuation.section.end.stylus
|
||||
attribute-selector:
|
||||
- match: '\[(?=[^\]]*\])'
|
||||
captures:
|
||||
0: punctuation.definition.entity.start.stylus
|
||||
push:
|
||||
- meta_scope: meta.attribute-selector.stylus
|
||||
- match: '\]'
|
||||
captures:
|
||||
0: punctuation.definition.entity.end.stylus
|
||||
pop: true
|
||||
- match: '(?<=\[)|(?<=\{)'
|
||||
push:
|
||||
- match: '(?=[|~=\]\s])'
|
||||
pop: true
|
||||
- include: interpolation
|
||||
- match: .
|
||||
captures:
|
||||
0: entity.other.attribute-name.stylus
|
||||
- include: interpolation
|
||||
- match: "([|~]?=)"
|
||||
captures:
|
||||
1: keyword.operator.stylus
|
||||
- include: string-quoted
|
||||
- match: .
|
||||
captures:
|
||||
0: string.unquoted.stylus
|
||||
color-values:
|
||||
- match: \b(aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow)\b
|
||||
scope: constant.color.w3c-standard-color-name.stylus
|
||||
- match: (hsla?|rgba?)\s*(\()
|
||||
captures:
|
||||
1: keyword.language.function.misc.stylus
|
||||
2: punctuation.definition.parameters.start.stylus
|
||||
push:
|
||||
- match: \)
|
||||
captures:
|
||||
0: punctuation.definition.parameters.end.stylus
|
||||
pop: true
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
\b
|
||||
(?:0*((?:1?[0-9]{1,2})|(?:2(?:[0-4][0-9]|5[0-5])))\s*(,)\s*)
|
||||
(?:0*((?:1?[0-9]{1,2})|(?:2(?:[0-4][0-9]|5[0-5])))\s*(,)\s*)
|
||||
(?:0*((?:1?[0-9]{1,2})|(?:2(?:[0-4][0-9]|5[0-5])))\b)
|
||||
captures:
|
||||
1: constant.other.color.rgb-value.stylus constant.other.color.rgb-value.red.stylus
|
||||
2: punctuation.delimiter.comma.stylus
|
||||
3: constant.other.color.rgb-value.stylus constant.other.color.rgb-value.green.stylus
|
||||
4: punctuation.delimiter.comma.stylus
|
||||
5: constant.other.color.rgb-value.stylus constant.other.color.rgb-value.blue.stylus
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
\b
|
||||
((?:[0-9]{1,2}|100)%)(,) # red
|
||||
\s*
|
||||
((?:[0-9]{1,2}|100)%)(,) # green
|
||||
\s*
|
||||
((?:[0-9]{1,2}|100)%) # blue
|
||||
captures:
|
||||
1: constant.other.color.rgb-value.stylus constant.other.color.rgb-value.red.stylus
|
||||
2: punctuation.delimiter.comma.stylus
|
||||
3: constant.other.color.rgb-value.stylus constant.other.color.rgb-value.green.stylus
|
||||
4: punctuation.delimiter.comma.stylus
|
||||
5: constant.other.color.rgb-value.stylus constant.other.color.rgb-value.blue.stylus
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
(?:\s*(,)\s*((0?\.[0-9]+)|[0-1]))?
|
||||
captures:
|
||||
1: punctuation.delimiter.comma.stylus
|
||||
2: constant.other.color.rgb-value.stylus constant.other.color.rgb-value.alpha.stylus
|
||||
- include: numeric-values
|
||||
- include: numeric-values
|
||||
comma:
|
||||
- match: \s*,\s*
|
||||
scope: punctuation.delimiter.comma.stylus
|
||||
comments:
|
||||
- include: single-line-comment
|
||||
- match: \/\*
|
||||
captures:
|
||||
0: punctuation.definition.comment.stylus
|
||||
push:
|
||||
- meta_scope: comment.block.stylus
|
||||
- match: \*\/
|
||||
captures:
|
||||
0: punctuation.definition.comment.stylus
|
||||
pop: true
|
||||
conditionals:
|
||||
- match: '(^\s*|\s+)(if|unless|else)(?=[\s({]|$)\s*'
|
||||
captures:
|
||||
2: keyword.control.stylus
|
||||
push:
|
||||
- match: '(?=$|\{)'
|
||||
pop: true
|
||||
- include: expression
|
||||
escape:
|
||||
- match: \\.
|
||||
scope: constant.character.escape.stylus
|
||||
expression:
|
||||
- include: single-line-comment
|
||||
- include: comma
|
||||
- include: iteration
|
||||
- include: conditionals
|
||||
- include: language-operators
|
||||
- include: language-keywords
|
||||
- include: hash-definition
|
||||
- include: color-values
|
||||
- include: url
|
||||
- include: function-call
|
||||
- include: string-quoted
|
||||
- include: escape
|
||||
- include: hash-access
|
||||
- include: language-constants
|
||||
- include: language-property-value-constants
|
||||
- include: property-reference
|
||||
- include: variable
|
||||
function-call:
|
||||
- match: '([\w$-]+)(\()'
|
||||
captures:
|
||||
1: entity.function-name.stylus
|
||||
2: punctuation.definition.parameters.start.stylus
|
||||
push:
|
||||
- meta_scope: meta.function-call.stylus
|
||||
- match: (\))
|
||||
captures:
|
||||
1: punctuation.definition.parameters.end.stylus
|
||||
pop: true
|
||||
- include: expression
|
||||
hash-access:
|
||||
- match: '(?=[\w$-]+(?:\.|\[[^\]=]*\]))'
|
||||
push:
|
||||
- meta_scope: meta.hash-access.stylus
|
||||
- match: '(?=[^''''""\[\]\w.$-]|\s|$)'
|
||||
pop: true
|
||||
- match: \.
|
||||
scope: punctuation.delimiter.hash.stylus
|
||||
- match: '\['
|
||||
scope: punctuation.definition.entity.start.stylus
|
||||
- match: '\]'
|
||||
scope: punctuation.definition.entity.end.stylus
|
||||
- include: string-quoted
|
||||
- include: variable
|
||||
hash-definition:
|
||||
- match: '\{'
|
||||
captures:
|
||||
0: punctuation.section.embedded.start.stylus
|
||||
push:
|
||||
- meta_scope: meta.hash.stylus
|
||||
- match: '\}'
|
||||
captures:
|
||||
0: punctuation.section.embedded.end.stylus
|
||||
pop: true
|
||||
- include: single-line-comment
|
||||
- match: |-
|
||||
(?x)
|
||||
(?:
|
||||
([\w$-]+)
|
||||
|
|
||||
('[^']*')
|
||||
|
|
||||
("[^"]*")
|
||||
)
|
||||
\s*
|
||||
(:)
|
||||
\s*
|
||||
captures:
|
||||
1: support.type.property-name.stylus
|
||||
2: string.quoted.single.stylus
|
||||
3: string.quoted.double.stylus
|
||||
4: punctuation.separator.key-value.stylus
|
||||
push:
|
||||
- match: '(;)|(?=\}|$)'
|
||||
captures:
|
||||
1: punctuation.terminator.statement.stylus
|
||||
pop: true
|
||||
- include: expression
|
||||
interpolation:
|
||||
- match: '\{'
|
||||
captures:
|
||||
0: punctuation.section.embedded.start.stylus
|
||||
push:
|
||||
- meta_scope: stylus.embedded.source
|
||||
- match: '\}'
|
||||
captures:
|
||||
0: punctuation.section.embedded.end.stylus
|
||||
pop: true
|
||||
- include: expression
|
||||
iteration:
|
||||
- match: (^\s*|\s+)(for)\s+(?=.*?\s+in\s+)
|
||||
captures:
|
||||
2: keyword.control.stylus
|
||||
push:
|
||||
- match: '$|\{'
|
||||
pop: true
|
||||
- include: expression
|
||||
language-constants:
|
||||
- match: \b(true|false|null)\b
|
||||
scope: constant.language.stylus
|
||||
language-keywords:
|
||||
- match: (\b|\s)(return|else)\b
|
||||
scope: keyword.control.stylus
|
||||
- match: (\b|\s)(!important|in|is defined|is a)\b
|
||||
scope: keyword.other.stylus
|
||||
- match: \barguments\b
|
||||
scope: variable.language.stylus
|
||||
language-operators:
|
||||
- match: ((?:\?|:|!|~|\+|-|(?:\*)?\*|\/|%|(\.)?\.\.|<|>|(?:=|:|\?|\+|-|\*|\/|%|<|>)?=|!=)|\b(?:in|is(?:nt)?|(?<!:)not)\b)
|
||||
scope: keyword.operator.stylus
|
||||
language-property-value-constants:
|
||||
- match: \b(absolute|all(-scroll)?|always|armenian|auto|avoid|baseline|below|bidi-override|block|bold(er)?|(border|content|padding)-box|both|bottom|break-all|break-word|capitalize|center|char|circle|cjk-ideographic|col-resize|collapse|crosshair|cursive|dashed|decimal-leading-zero|decimal|default|disabled|disc|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ellipsis|fantasy|fixed|geometricPrecision|georgian|groove|hand|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|inactive|inherit|inline-block|inline|inset|inside|inter-ideograph|inter-word|italic|justify|katakana-iroha|katakana|keep-all|left|lighter|line-edge|line-through|line|list-item|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|medium|middle|move|monospace|n-resize|ne-resize|newspaper|no-drop|no-repeat|nw-resize|none|normal|not-allowed|nowrap|oblique|optimize(Legibility|Quality|Speed)|outset|outside|overline|pointer|pre(-(wrap|line))?|progress|relative|repeat-x|repeat-y|repeat|right|ridge|row-resize|rtl|(sans-)?serif|s-resize|scroll|se-resize|separate|small-caps|solid|square|static|strict|sub|super|sw-resize|table(-(row|cell|footer-group|header-group))?|tb-rl|text-bottom|text-top|text|thick|thin|top|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|vertical(-(ideographic|text))?|visible(Painted|Fill|Stroke)?|w-resize|wait|whitespace|zero|smaller|larger|((xx?-)?(small(er)?|large(r)?))|painted|fill|stroke)\b
|
||||
scope: constant.property-value.stylus
|
||||
media-query:
|
||||
- match: '\s*(?![{;]|$)'
|
||||
push:
|
||||
- meta_scope: meta.at-rule.media.stylus
|
||||
- match: '\s*(?=[{;]|$)'
|
||||
pop: true
|
||||
- match: '(?i)\s*(only|not)?\s*(all|aural|braille|embossed|handheld|print|projection|screen|tty|tv)?(?![\w\d$-]+)'
|
||||
captures:
|
||||
1: keyword.operator.logic.media.stylus
|
||||
2: support.constant.media.stylus
|
||||
push:
|
||||
- match: '\s*(?:(,)|(?=[{;]|$))'
|
||||
pop: true
|
||||
- include: media-query-list
|
||||
- include: variable
|
||||
media-query-list:
|
||||
- match: \s*(and)?\s*(\()\s*
|
||||
captures:
|
||||
1: keyword.operator.logic.media.stylus
|
||||
2: punctuation.definition.parameters.start.stylus
|
||||
push:
|
||||
- match: \)
|
||||
captures:
|
||||
0: punctuation.definition.parameters.end.stylus
|
||||
pop: true
|
||||
- include: media-query-properties
|
||||
- include: numeric-values
|
||||
media-query-properties:
|
||||
- match: \s*:\s*
|
||||
captures:
|
||||
0: punctuation.separator.key-value.stylus
|
||||
- match: |-
|
||||
(?x)
|
||||
(
|
||||
((min|max)-)?
|
||||
(
|
||||
((device-)?(height|width|aspect-ratio))|
|
||||
(color(-index)?)|monochrome|resolution
|
||||
)
|
||||
)|grid|scan|orientation
|
||||
captures:
|
||||
0: support.type.property-name.media.stylus
|
||||
- match: \b(portrait|landscape|progressive|interlace)\b
|
||||
captures:
|
||||
1: support.constant.property-value.stylus
|
||||
numeric-values:
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
(\#)(?:
|
||||
([0-9a-fA-F])
|
||||
([0-9a-fA-F])
|
||||
([0-9a-fA-F])
|
||||
([0-9a-fA-F])?
|
||||
| ([0-9a-fA-F]{2})
|
||||
([0-9a-fA-F]{2})
|
||||
([0-9a-fA-F]{2})
|
||||
([0-9a-fA-F]{2})?
|
||||
)\b
|
||||
scope: constant.other.color.rgb-value.stylus
|
||||
captures:
|
||||
1: punctuation.definition.constant.stylus
|
||||
2: constant.other.color.rgb-value.red.stylus
|
||||
3: constant.other.color.rgb-value.green.stylus
|
||||
4: constant.other.color.rgb-value.blue.stylus
|
||||
5: constant.other.color.rgb-value.alpha.stylus
|
||||
6: constant.other.color.rgb-value.red.stylus
|
||||
7: constant.other.color.rgb-value.green.stylus
|
||||
8: constant.other.color.rgb-value.blue.stylus
|
||||
9: constant.other.color.rgb-value.alpha.stylus
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
(?:-|\+)? # negative / positive
|
||||
(?:
|
||||
(?:
|
||||
[0-9]+ # integer part
|
||||
(?:\.[0-9]+)? # fraction
|
||||
) |
|
||||
(?:\.[0-9]+) # fraction without leading zero
|
||||
)
|
||||
((?: # units
|
||||
px|pt|ch|cm|mm|in|
|
||||
r?em|ex|pc|vw|vh|vmin|vmax|deg|
|
||||
g?rad|turn|dpi|dpcm|dppx|m?s|k?Hz
|
||||
)\b|%)?
|
||||
scope: constant.numeric.stylus
|
||||
captures:
|
||||
1: keyword.other.unit.stylus
|
||||
property-reference:
|
||||
- match: "@[a-z-]+"
|
||||
scope: variable.other.property.stylus
|
||||
pseudo:
|
||||
- match: (:)(active|checked|default|disabled|empty|enabled|first-child|first-of-type|first|fullscreen|focus|hover|indeterminate|in-range|invalid|last-child|last-of-type|left|link|only-child|only-of-type|optional|out-of-range|read-only|read-write|required|right|root|scope|target|valid|visited)\b
|
||||
scope: entity.other.attribute-name.pseudo-class.stylus
|
||||
captures:
|
||||
1: puncutation.definition.entity.stylus
|
||||
- match: (:?:)(before|after)\b
|
||||
scope: entity.other.attribute-name.pseudo-element.stylus
|
||||
captures:
|
||||
1: puncutation.definition.entity.stylus
|
||||
- match: (::)(first-letter|first-number|selection)\b
|
||||
scope: entity.other.attribute-name.pseudo-element.stylus
|
||||
captures:
|
||||
1: puncutation.definition.entity.stylus
|
||||
- match: ((:)dir)\s*(?:(\()(ltr|rtl)?(\)))?
|
||||
captures:
|
||||
1: entity.other.attribute-name.pseudo-element.stylus
|
||||
2: puncutation.definition.entity.stylus
|
||||
3: puncutation.definition.parameters.start.stylus
|
||||
4: constant.language.stylus
|
||||
5: puncutation.definition.parameters.end.stylus
|
||||
- match: ((:)lang)\s*(?:(\()(\w+(-\w+)?)?(\)))?
|
||||
captures:
|
||||
1: entity.other.attribute-name.pseudo-element.stylus
|
||||
2: puncutation.definition.entity.stylus
|
||||
3: puncutation.definition.parameters.start.stylus
|
||||
4: constant.language.stylus
|
||||
5: puncutation.definition.parameters.end.stylus
|
||||
- include: pseudo-nth
|
||||
- include: pseudo-not
|
||||
pseudo-not:
|
||||
- match: ((:)not)\s*(\()
|
||||
captures:
|
||||
1: entity.other.attribute-name.pseudo-element.stylus
|
||||
2: puncutation.definition.entity.stylus
|
||||
3: puncutation.definition.parameters.start.stylus
|
||||
push:
|
||||
- match: \)
|
||||
captures:
|
||||
0: puncutation.definition.parameters.end.stylus
|
||||
pop: true
|
||||
- include: selector-components
|
||||
pseudo-nth:
|
||||
- match: ((:)(?:nth-child|nth-last-child|nth-of-type|nth-last-of-type|nth-match|nth-last-match|nth-column|nth-last-column))\s*(\()
|
||||
captures:
|
||||
1: entity.other.attribute-name.pseudo-class.stylus
|
||||
2: puncutation.definition.entity.stylus
|
||||
3: puncutation.definition.parameters.start.stylus
|
||||
push:
|
||||
- match: \)
|
||||
captures:
|
||||
0: puncutation.definition.parameters.end.stylus
|
||||
pop: true
|
||||
- include: language-operators
|
||||
- include: interpolation
|
||||
- match: \b(odd|even)\b
|
||||
scope: constant.language.stylus
|
||||
- match: \b(\d+)?n\b
|
||||
scope: variable.language.stylus
|
||||
captures:
|
||||
1: constant.numeric.stylus
|
||||
- match: \d+
|
||||
scope: constant.numeric.stylus
|
||||
return:
|
||||
- match: ^\s*(return)
|
||||
captures:
|
||||
1: keyword.control.stylus
|
||||
push:
|
||||
- match: (;)|(?=$)
|
||||
captures:
|
||||
1: punctuation.terminator.statement.stylus
|
||||
pop: true
|
||||
- include: expression
|
||||
selector-components:
|
||||
- include: comments
|
||||
- include: interpolation
|
||||
- include: attribute-selector
|
||||
- include: pseudo
|
||||
- match: '\$[\w$-]+\b'
|
||||
scope: entity.other.placeholder.stylus
|
||||
- match: "[:~>]"
|
||||
scope: keyword.operator.selector.stylus
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
\b(
|
||||
altGlyph|altGlyphDef|altGlyphItem|animate|animateColor|
|
||||
animateMotion|animateTransform|circle|clipPath|color-profile|
|
||||
defs|desc|ellipse|feBlend|feColorMatrix|
|
||||
feComponentTransfer|feComposite|feConvolveMatrix|
|
||||
feDiffuseLighting|feDisplacementMap|feDistantLight|feFlood|
|
||||
feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feImage|feMerge|
|
||||
feMergeNode|feMorphology|feOffset|fePointLight|
|
||||
feSpecularLighting|feSpotLight|feTile|feTurbulence|filter|
|
||||
font-face|font-face-format|font-face-name|font-face-src|
|
||||
font-face-uri|foreignObject|g|glyph|glyphRef|hkern|image|line|
|
||||
linearGradient|marker|mask|metadata|missing-glyph|mpath|path|
|
||||
pattern|polygon|polyline|radialGradient|rect|set|stop|svg|
|
||||
switch|symbol|text|textPath|tref|tspan|use|view|vkern|
|
||||
a|abbr|acronym|address|applet|area|article|aside|audio|b|base|
|
||||
basefont|bdi|bdo|bgsound|big|blink|blockquote|body|br|button|
|
||||
canvas|caption|center|cite|code|col|colgroup|content|data|
|
||||
datalist|dd|decorator|del|details|dfn|dir|div|dl|dt|element|
|
||||
em|embed|fieldset|figcaption|figure|font|footer|form|frame|
|
||||
frameset|h1|h2|h3|h4|h5|h6|head|header|hgroup|hr|html|i|iframe|
|
||||
img|input|ins|isindex|kbd|keygen|label|legend|li|link|listing|
|
||||
main|map|mark|marquee|menu|menuitem|meta|meter|nav|nobr|
|
||||
noframes|noscript|object|ol|optgroup|option|output|p|param|
|
||||
plaintext|pre|progress|q|rp|rt|ruby|s|samp|script|section|
|
||||
select|shadow|small|source|spacer|span|strike|strong|style|
|
||||
sub|summary|sup|table|tbody|td|template|textarea|tfoot|th|
|
||||
thead|time|title|tr|track|tt|u|ul|var|video|wbr|xmp
|
||||
)\b
|
||||
scope: entity.name.tag.stylus
|
||||
- match: '\.[a-zA-Z0-9_-]+'
|
||||
scope: entity.other.attribute-name.class.stylus
|
||||
- match: "#[a-zA-Z0-9_-]+"
|
||||
scope: entity.other.attribute-name.id.stylus
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
([\w\d_-]+)? # matching any word right before &
|
||||
(&) # & itself, escaped because of plist
|
||||
([\w\d_-]+)? # matching any word right after &
|
||||
captures:
|
||||
1: entity.other.attribute-name.stylus
|
||||
2: variable.language.stylus
|
||||
3: entity.other.attribute-name.stylus
|
||||
single-line-comment:
|
||||
- match: (\/\/).*$
|
||||
scope: comment.line.stylus
|
||||
captures:
|
||||
1: punctuation.definition.comment.stylus
|
||||
string-quoted:
|
||||
- match: "'[^']*'"
|
||||
scope: string.quoted.single.stylus
|
||||
- match: '"[^"]*"'
|
||||
scope: string.quoted.double.stylus
|
||||
url:
|
||||
- match: (url)\s*(\()
|
||||
captures:
|
||||
1: entity.function-name.stylus
|
||||
2: punctuation.definition.parameters.start.stylus
|
||||
push:
|
||||
- meta_scope: meta.function-call.stylus
|
||||
- match: (\))
|
||||
captures:
|
||||
1: punctuation.definition.parameters.end.stylus
|
||||
pop: true
|
||||
- include: string-quoted
|
||||
- include: language-constants
|
||||
- include: language-property-value-constants
|
||||
- include: property-reference
|
||||
- include: variable
|
||||
variable:
|
||||
- match: '([\w$-]+\b)'
|
||||
scope: variable.other.stylus
|
||||
Vendored
BIN
Binary file not shown.
+1
Submodule assets/themes/Solarized added at 87e01090cf
Vendored
-1
Submodule assets/themes/solarized-sublime deleted from 1394cf0f5f
@@ -1,66 +1,47 @@
|
||||
// TODO: Re-enable generation of shell completion files (below) when clap 3 is out.
|
||||
// For more details, see https://github.com/sharkdp/bat/issues/372
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate liquid;
|
||||
// For bat-as-a-library, no build script is required. The build script is for
|
||||
// the manpage and completions, which are only relevant to the bat application.
|
||||
#[cfg(not(feature = "application"))]
|
||||
fn main() {}
|
||||
|
||||
use std::error::Error;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
#[cfg(feature = "application")]
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
use std::error::Error;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
// Read environment variables.
|
||||
lazy_static! {
|
||||
pub static ref PROJECT_NAME: &'static str = option_env!("PROJECT_NAME").unwrap_or("bat");
|
||||
pub static ref PROJECT_VERSION: &'static str = option_env!("CARGO_PKG_VERSION").unwrap();
|
||||
pub static ref EXECUTABLE_NAME: &'static str = option_env!("PROJECT_EXECUTABLE")
|
||||
.or(option_env!("PROJECT_NAME"))
|
||||
.unwrap_or("bat");
|
||||
}
|
||||
// Read environment variables.
|
||||
let project_name = option_env!("PROJECT_NAME").unwrap_or("bat");
|
||||
let executable_name = option_env!("PROJECT_EXECUTABLE").unwrap_or(project_name);
|
||||
static PROJECT_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
fn init_template() -> liquid::value::Object {
|
||||
let mut globals = liquid::value::Object::new();
|
||||
/// Generates a file from a liquid template.
|
||||
fn template(
|
||||
variables: &liquid::Object,
|
||||
in_file: &str,
|
||||
out_file: impl AsRef<Path>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let template = liquid::ParserBuilder::with_stdlib()
|
||||
.build()?
|
||||
.parse(&fs::read_to_string(in_file)?)?;
|
||||
|
||||
globals.insert(
|
||||
"PROJECT_NAME".into(),
|
||||
liquid::value::Value::scalar(PROJECT_NAME.to_owned()),
|
||||
);
|
||||
fs::write(out_file, template.render(variables)?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
globals.insert(
|
||||
"PROJECT_EXECUTABLE".into(),
|
||||
liquid::value::Value::scalar(EXECUTABLE_NAME.to_owned()),
|
||||
);
|
||||
|
||||
globals.insert(
|
||||
"PROJECT_VERSION".into(),
|
||||
liquid::value::Value::scalar(PROJECT_VERSION.to_owned()),
|
||||
);
|
||||
|
||||
globals
|
||||
}
|
||||
|
||||
/// Generates a file from a liquid template.
|
||||
fn template(
|
||||
variables: &liquid::value::Object,
|
||||
in_file: &str,
|
||||
out_file: impl AsRef<Path>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let template = liquid::ParserBuilder::with_liquid()
|
||||
.build()?
|
||||
.parse(&fs::read_to_string(in_file)?)?;
|
||||
|
||||
fs::write(out_file, template.render(variables)?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let variables = init_template();
|
||||
let variables = liquid::object!({
|
||||
"PROJECT_NAME": project_name,
|
||||
"PROJECT_EXECUTABLE": executable_name,
|
||||
"PROJECT_VERSION": PROJECT_VERSION,
|
||||
});
|
||||
|
||||
let out_dir_env = std::env::var_os("OUT_DIR").expect("OUT_DIR to be set in build.rs");
|
||||
let out_dir = Path::new(&out_dir_env);
|
||||
|
||||
std::fs::create_dir_all(out_dir.join("assets/manual")).unwrap();
|
||||
std::fs::create_dir_all(out_dir.join("assets/completions")).unwrap();
|
||||
fs::create_dir_all(out_dir.join("assets/manual")).unwrap();
|
||||
fs::create_dir_all(out_dir.join("assets/completions")).unwrap();
|
||||
|
||||
template(
|
||||
&variables,
|
||||
|
||||
Vendored
+2
-1
@@ -164,7 +164,8 @@ Package: $dpkgname
|
||||
Version: $version
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Maintainer: David Peter <mail@david-peter.de>
|
||||
Maintainer: $maintainer
|
||||
Homepage: $homepage
|
||||
Architecture: $architecture
|
||||
Provides: $PROJECT_NAME
|
||||
Conflicts: $conflictname
|
||||
|
||||
Vendored
+6
@@ -12,3 +12,9 @@ if [[ $TARGET != arm-unknown-linux-gnueabihf ]] && [[ $TARGET != aarch64-unknown
|
||||
# Run 'bat' on its own source code and the README
|
||||
cargo run --target "$TARGET" -- src/bin/bat/main.rs README.md --paging=never
|
||||
fi
|
||||
|
||||
# Check bat-as-a-library, which has a smaller set of dependencies
|
||||
cargo check --target "$TARGET" --verbose --lib --no-default-features --features regex-onig
|
||||
cargo check --target "$TARGET" --verbose --lib --no-default-features --features regex-onig,git
|
||||
cargo check --target "$TARGET" --verbose --lib --no-default-features --features regex-onig,paging
|
||||
cargo check --target "$TARGET" --verbose --lib --no-default-features --features regex-onig,git,paging
|
||||
|
||||
+102
-37
@@ -2,6 +2,24 @@
|
||||
_modules=('system' 'bat' 'bat_config' 'bat_wrapper' 'bat_wrapper_function' 'tool')
|
||||
_modules_consented=()
|
||||
|
||||
set -o pipefail
|
||||
|
||||
export LC_ALL=C
|
||||
export LANG=C
|
||||
|
||||
BAT="bat"
|
||||
if ! command -v bat &>/dev/null; then
|
||||
if command -v batcat &> /dev/null; then
|
||||
BAT="batcat"
|
||||
else
|
||||
tput setaf 1
|
||||
printf "%s\n%s\n" \
|
||||
"Unable to find a bat executable on your PATH." \
|
||||
"Please ensure that 'bat' exists and is not named something else."
|
||||
tput sgr0
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Modules:
|
||||
@@ -9,6 +27,7 @@ _modules_consented=()
|
||||
|
||||
_bat_:description() {
|
||||
_collects "Version information for 'bat'."
|
||||
_collects "Custom syntaxes and themes for 'bat'."
|
||||
}
|
||||
|
||||
_bat_config_:description() {
|
||||
@@ -33,59 +52,92 @@ _tool_:description() {
|
||||
_collects "Version information for 'less'."
|
||||
}
|
||||
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
_bat_:run() {
|
||||
_out bat --version
|
||||
_out "$BAT" --version
|
||||
_out env | grep '^BAT_\|^PAGER='
|
||||
|
||||
local cache_dir="$(bat --cache-dir)"
|
||||
if [[ -f "${cache_dir}/syntaxes.bin" ]]; then
|
||||
_print_command "$BAT" "--list-languages"
|
||||
echo "Found custom syntax set."
|
||||
fi
|
||||
|
||||
if [[ -f "${cache_dir}/themes.bin" ]]; then
|
||||
_print_command "$BAT" "--list-themes"
|
||||
echo "Found custom theme set."
|
||||
fi
|
||||
}
|
||||
|
||||
_bat_config_:run() {
|
||||
if [[ -f "$(bat --config-file)" ]]; then
|
||||
_out_fence cat "$(bat --config-file)";
|
||||
if [[ -f "$("$BAT" --config-file)" ]]; then
|
||||
_out_fence cat "$("$BAT" --config-file)"
|
||||
fi
|
||||
}
|
||||
|
||||
_bat_wrapper_:run() {
|
||||
if file "$(which bat)" | grep "text executable" &>/dev/null; then
|
||||
_out_fence cat "$(which bat)"
|
||||
return
|
||||
_bat_wrapper_:detect_wrapper() {
|
||||
local bat="$1"
|
||||
if file "$(which "${bat}")" | grep "text executable" &> /dev/null; then
|
||||
_out_fence cat "$(which "${bat}")"
|
||||
return
|
||||
fi
|
||||
|
||||
printf "\nNo wrapper script for '%s'.\n" "${bat}"
|
||||
}
|
||||
|
||||
_bat_wrapper_:detect_wrapper bat
|
||||
if [[ "$BAT" != "bat" ]]; then
|
||||
_bat_wrapper_:detect_wrapper "$BAT"
|
||||
fi
|
||||
printf "\nNo wrapper script.\n"
|
||||
}
|
||||
|
||||
_bat_wrapper_function_:run() {
|
||||
case "$("$SHELL" --version | head -n 1)" in
|
||||
*fish*)
|
||||
if "$SHELL" --login -c 'type bat' 2>&1 | grep 'function' &>/dev/null; then
|
||||
_out_fence "$SHELL" --login -c 'functions bat'
|
||||
fi ;;
|
||||
_bat_wrapper_function_:detect_wrapper() {
|
||||
local command="$1"
|
||||
case "$("$SHELL" --version | head -n 1)" in
|
||||
*fish*)
|
||||
if "$SHELL" --login -i -c "type ${command}" 2>&1 | grep 'function' &> /dev/null; then
|
||||
_out_fence "$SHELL" --login -i -c "functions ${command}"
|
||||
return
|
||||
fi ;;
|
||||
|
||||
*bash*)
|
||||
if "$SHELL" --login -c 'type bat' 2>&1 | grep 'function' &>/dev/null; then
|
||||
_out_fence "$SHELL" --login -c 'declare -f bat'
|
||||
fi ;;
|
||||
*bash* | *zsh*)
|
||||
local type="$("$SHELL" --login -i -c "type ${command}" 2>&1)"
|
||||
if grep 'function' <<< "$type" &> /dev/null; then
|
||||
_out_fence "$SHELL" --login -i -c "declare -f ${command}"
|
||||
return
|
||||
elif grep 'alias' <<< "$type" &> /dev/null; then
|
||||
_out_fence "$SHELL" --login -i -c "type ${command}"
|
||||
return
|
||||
fi ;;
|
||||
|
||||
*)
|
||||
echo "Unable to determine if a wrapper function is set."
|
||||
return ;;
|
||||
esac
|
||||
printf "\nNo wrapper function.\n"
|
||||
*)
|
||||
echo "Unable to determine if a wrapper function for '${command}' is set."
|
||||
return ;;
|
||||
esac
|
||||
printf "\nNo wrapper function for '%s'.\n" "${command}"
|
||||
}
|
||||
|
||||
_bat_wrapper_function_:detect_wrapper bat
|
||||
_bat_wrapper_function_:detect_wrapper cat
|
||||
if [[ "$BAT" != "bat" ]]; then
|
||||
_bat_wrapper_function_:detect_wrapper "$BAT"
|
||||
fi
|
||||
}
|
||||
|
||||
_system_:run() {
|
||||
_out uname -srm
|
||||
|
||||
if command -v "sw_vers" &>/dev/null; then _out sw_vers; fi
|
||||
if command -v "lsb_release" &>/dev/null; then _out lsb_release -a; fi
|
||||
if command -v "sw_vers" &> /dev/null; then _out sw_vers; fi
|
||||
if command -v "lsb_release" &> /dev/null; then _out lsb_release -a; fi
|
||||
}
|
||||
|
||||
_tool_:run() {
|
||||
_out less --version | head -n1
|
||||
}
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Functions:
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -109,7 +161,7 @@ _out_fence() {
|
||||
}
|
||||
|
||||
_tput() {
|
||||
tput "$@" 1>&2 2>/dev/null
|
||||
tput "$@" 1>&2 2> /dev/null
|
||||
}
|
||||
|
||||
_collects() {
|
||||
@@ -120,7 +172,7 @@ _ask_module() {
|
||||
_tput clear
|
||||
_tput cup 0 0
|
||||
|
||||
cat 1>&2 <<EOF
|
||||
cat 1>&2 << EOF
|
||||
--------------------------------------------------------------------------------
|
||||
This script runs some harmless commands to collect information about your
|
||||
system and bat configuration. It will give you a small preview of the commands
|
||||
@@ -135,7 +187,7 @@ EOF
|
||||
_tput sgr0
|
||||
"_$1_:description"
|
||||
_tput sgr0
|
||||
|
||||
|
||||
# Print preview.
|
||||
_tput setaf 3
|
||||
printf "\nThe following commands will be run:\n" 1>&2
|
||||
@@ -143,19 +195,20 @@ EOF
|
||||
declare -f "_$1_:run" \
|
||||
| sed 's/^ *//; s/;$//' \
|
||||
| grep '^_out[^ ]* ' \
|
||||
| sed 's/^_out[^ ]* //' 1>&2
|
||||
| sed 's/^_out[^ ]* //' \
|
||||
| sed "s/\"\$BAT\"/$BAT/" 1>&2
|
||||
|
||||
# Prompt
|
||||
printf "\n" 1>&2
|
||||
local response
|
||||
while true; do
|
||||
_tput cup "$(( $(tput lines || echo 22) - 2 ))"
|
||||
_tput cup "$(($( tput lines || echo 22) - 2))"
|
||||
_tput el
|
||||
read -er -p "Collect $(sed 's/_/ /' <<< "$1") data? [Y/n] " response
|
||||
case "$response" in
|
||||
Y|y|yes|'') return 0 ;;
|
||||
N|n|no) return 1 ;;
|
||||
*) continue
|
||||
Y | y | yes | '') return 0 ;;
|
||||
N | n | no) return 1 ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
@@ -166,13 +219,27 @@ _run_module() {
|
||||
"_$1_:run"
|
||||
}
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Functions:
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Tell the user if their executable isn't named "bat".
|
||||
if [[ "$BAT" != "bat" ]] && [[ "$1" != '-y' ]]; then
|
||||
trap '_tput rmcup; exit 1' INT
|
||||
_tput smcup
|
||||
_tput clear
|
||||
_tput cup 0 0
|
||||
_tput setaf 1
|
||||
printf "The %s executable on your system is named '%s'.\n%s\n" "bat" "$BAT" \
|
||||
"If your issue is related to installation, please check that this isn't the issue."
|
||||
_tput sgr0
|
||||
printf "Press any key to continue...\n"
|
||||
read -rsn1
|
||||
_tput rmcup
|
||||
fi
|
||||
|
||||
# Ask for consent.
|
||||
if [[ "$1" = '-y' ]]; then
|
||||
if [[ "$1" == '-y' ]]; then
|
||||
_modules_consented=("${_modules[@]}")
|
||||
else
|
||||
trap '_tput rmcup; exit 1' INT
|
||||
@@ -190,5 +257,3 @@ for _module in "${_modules_consented[@]}"; do
|
||||
_run_module "$_module" 2>&1
|
||||
printf "\n"
|
||||
done
|
||||
|
||||
|
||||
|
||||
+4
-4
@@ -180,7 +180,7 @@ apt install bat
|
||||
batの最新リリースを実行する場合、または Ubuntu/Debian の古いバージョンを使用している場合は、[release page](https://github.com/sharkdp/bat/releases) から最新の `.deb` パッケージをダウンロードし、
|
||||
次の方法でインストールします:
|
||||
```bash
|
||||
sudo dpkg -i bat_0.13.0_amd64.deb # adapt version number and architecture
|
||||
sudo dpkg -i bat_0.15.1_amd64.deb # adapt version number and architecture
|
||||
```
|
||||
|
||||
### On Alpine Linux
|
||||
@@ -345,7 +345,7 @@ ansible-galaxy install aeimer.install_bat
|
||||
そして `cargo` をビルドに対して使用します:
|
||||
|
||||
```bash
|
||||
cargo install bat
|
||||
cargo install --locked bat
|
||||
```
|
||||
|
||||
一部のプラットフォームでは `llvm` および/または `libclang-dev` のインストールが必要になる場合があります。
|
||||
@@ -594,11 +594,11 @@ cargo build --bins
|
||||
cargo test
|
||||
|
||||
# Install (release version)
|
||||
cargo install
|
||||
cargo install --locked
|
||||
|
||||
# Build a bat binary with modified syntaxes and themes
|
||||
bash assets/create.sh
|
||||
cargo install -f
|
||||
cargo install --locked --force
|
||||
```
|
||||
|
||||
## Maintainers
|
||||
|
||||
+4
-4
@@ -170,7 +170,7 @@ apt install bat
|
||||
만약 최근 릴리즈된 bat을 사용을 원하거나 buntu/Debian 예전 버전을 사용하는 경우, [릴리즈 페이지](https://github.com/sharkdp/bat/releases)에서 다음과 같이 `.deb` 패키지를 받아 설치 할 수도 있습니다:
|
||||
|
||||
```bash
|
||||
sudo dpkg -i bat_0.13.0_amd64.deb # adapt version number and architecture
|
||||
sudo dpkg -i bat_0.15.1_amd64.deb # adapt version number and architecture
|
||||
```
|
||||
|
||||
### On Alpine Linux
|
||||
@@ -327,7 +327,7 @@ ansible-galaxy install aeimer.install_bat
|
||||
`bat`의 소스를 직접 빌드하기 위해서는, Rust 1.36 이상이 필요하며 `cargo`를 이용해 빌드할 수 있습니다.
|
||||
|
||||
```bash
|
||||
cargo install bat
|
||||
cargo install --locked bat
|
||||
```
|
||||
|
||||
일부 플랫폼에서는 `llvm` 그리고/또는 `libclang-dev` 설치가 필요할 수도 있습니다.
|
||||
@@ -528,11 +528,11 @@ cargo build --bins
|
||||
cargo test
|
||||
|
||||
# Install (release version)
|
||||
cargo install
|
||||
cargo install --locked
|
||||
|
||||
# Build a bat binary with modified syntaxes and themes
|
||||
bash assets/create.sh
|
||||
cargo install -f
|
||||
cargo install --locked --force
|
||||
```
|
||||
|
||||
## 메인테이너들
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/// A program that prints its own source code using the bat library
|
||||
use bat::{PagingMode, PrettyPrinter, WrappingMode};
|
||||
|
||||
fn main() {
|
||||
PrettyPrinter::new()
|
||||
.header(true)
|
||||
.grid(true)
|
||||
.line_numbers(true)
|
||||
.use_italics(true)
|
||||
// The following line will be highlighted in the output:
|
||||
.highlight(line!() as usize)
|
||||
.theme("1337")
|
||||
.wrapping_mode(WrappingMode::Character)
|
||||
.paging_mode(PagingMode::QuitIfOneScreen)
|
||||
.input_file(file!())
|
||||
.print()
|
||||
.unwrap();
|
||||
}
|
||||
+8
-28
@@ -1,33 +1,13 @@
|
||||
/// A very simple colorized `cat` clone, using `bat` as a library.
|
||||
/// See `src/bin/bat` for the full `bat` application.
|
||||
use bat::{
|
||||
config::{Config, InputFile, StyleComponent, StyleComponents},
|
||||
Controller, HighlightingAssets,
|
||||
};
|
||||
use console::Term;
|
||||
use std::process;
|
||||
use bat::PrettyPrinter;
|
||||
|
||||
fn main() {
|
||||
let files = std::env::args_os().skip(1).collect::<Vec<_>>();
|
||||
|
||||
if files.is_empty() {
|
||||
eprintln!("No input files specified");
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
let config = Config {
|
||||
term_width: Term::stdout().size().1 as usize,
|
||||
colored_output: true,
|
||||
true_color: true,
|
||||
style_components: StyleComponents::new(&[
|
||||
StyleComponent::Header,
|
||||
StyleComponent::Grid,
|
||||
StyleComponent::Numbers,
|
||||
]),
|
||||
files: files.iter().map(|file| InputFile::Ordinary(file)).collect(),
|
||||
..Default::default()
|
||||
};
|
||||
let assets = HighlightingAssets::from_binary();
|
||||
|
||||
Controller::new(&config, &assets).run().expect("no errors");
|
||||
PrettyPrinter::new()
|
||||
.header(true)
|
||||
.grid(true)
|
||||
.line_numbers(true)
|
||||
.input_files(std::env::args_os().skip(1))
|
||||
.print()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/// A simple program that prints its own source code using the bat library
|
||||
use bat::PrettyPrinter;
|
||||
|
||||
fn main() {
|
||||
let printer = PrettyPrinter::new();
|
||||
|
||||
println!("Syntaxes:");
|
||||
for syntax in printer.syntaxes() {
|
||||
println!("- {} ({})", syntax.name, syntax.file_extensions.join(", "));
|
||||
}
|
||||
|
||||
println!();
|
||||
|
||||
println!("Themes:");
|
||||
for theme in printer.themes() {
|
||||
println!("- {}", theme);
|
||||
}
|
||||
}
|
||||
+2
-16
@@ -1,20 +1,6 @@
|
||||
/// A simple program that prints its own source code using the bat library
|
||||
use bat::{
|
||||
config::{Config, InputFile},
|
||||
Controller, HighlightingAssets,
|
||||
};
|
||||
use std::ffi::OsStr;
|
||||
use bat::PrettyPrinter;
|
||||
|
||||
fn main() {
|
||||
let path_to_this_file = OsStr::new(file!());
|
||||
|
||||
let config = Config {
|
||||
files: vec![InputFile::Ordinary(path_to_this_file)],
|
||||
colored_output: true,
|
||||
true_color: true,
|
||||
..Default::default()
|
||||
};
|
||||
let assets = HighlightingAssets::from_binary();
|
||||
|
||||
Controller::new(&config, &assets).run().expect("no errors");
|
||||
PrettyPrinter::new().input_file(file!()).print().unwrap();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/// A program that serializes a Rust structure to YAML and pretty-prints the result
|
||||
use bat::PrettyPrinter;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Person {
|
||||
name: String,
|
||||
height: f64,
|
||||
adult: bool,
|
||||
children: Vec<Person>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let person = Person {
|
||||
name: String::from("Anne Mustermann"),
|
||||
height: 1.76f64,
|
||||
adult: true,
|
||||
children: vec![Person {
|
||||
name: String::from("Max Mustermann"),
|
||||
height: 1.32f64,
|
||||
adult: false,
|
||||
children: vec![],
|
||||
}],
|
||||
};
|
||||
|
||||
let bytes = serde_yaml::to_vec(&person).unwrap();
|
||||
PrettyPrinter::new()
|
||||
.language("yaml")
|
||||
.line_numbers(true)
|
||||
.grid(true)
|
||||
.header(true)
|
||||
.input_from_bytes_with_name(&bytes, "person.yaml")
|
||||
.print()
|
||||
.unwrap();
|
||||
}
|
||||
+131
-53
@@ -1,4 +1,5 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::{self, File};
|
||||
use std::io::BufReader;
|
||||
use std::path::Path;
|
||||
@@ -7,8 +8,9 @@ use syntect::dumps::{dump_to_file, from_binary, from_reader};
|
||||
use syntect::highlighting::{Theme, ThemeSet};
|
||||
use syntect::parsing::{SyntaxReference, SyntaxSet, SyntaxSetBuilder};
|
||||
|
||||
use crate::errors::*;
|
||||
use crate::inputfile::{InputFile, InputFileReader};
|
||||
use crate::assets_metadata::AssetsMetadata;
|
||||
use crate::error::*;
|
||||
use crate::input::{InputReader, OpenedInput, OpenedInputKind};
|
||||
use crate::syntax_mapping::{MappingTarget, SyntaxMapping};
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -67,8 +69,11 @@ impl HighlightingAssets {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_cache(theme_set_path: &Path, syntax_set_path: &Path) -> Result<Self> {
|
||||
let syntax_set_file = File::open(syntax_set_path).chain_err(|| {
|
||||
pub fn from_cache(cache_path: &Path) -> Result<Self> {
|
||||
let syntax_set_path = cache_path.join("syntaxes.bin");
|
||||
let theme_set_path = cache_path.join("themes.bin");
|
||||
|
||||
let syntax_set_file = File::open(&syntax_set_path).chain_err(|| {
|
||||
format!(
|
||||
"Could not load cached syntax set '{}'",
|
||||
syntax_set_path.to_string_lossy()
|
||||
@@ -112,7 +117,7 @@ impl HighlightingAssets {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save_to_cache(&self, target_dir: &Path) -> Result<()> {
|
||||
pub fn save_to_cache(&self, target_dir: &Path, current_version: &str) -> Result<()> {
|
||||
let _ = fs::create_dir_all(target_dir);
|
||||
let theme_set_path = target_dir.join("themes.bin");
|
||||
let syntax_set_path = target_dir.join("syntaxes.bin");
|
||||
@@ -141,6 +146,13 @@ impl HighlightingAssets {
|
||||
})?;
|
||||
println!("okay");
|
||||
|
||||
print!(
|
||||
"Writing metadata to folder {} ... ",
|
||||
target_dir.to_string_lossy()
|
||||
);
|
||||
AssetsMetadata::new(current_version).save_to_folder(target_dir)?;
|
||||
println!("okay");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -152,8 +164,8 @@ impl HighlightingAssets {
|
||||
self.syntax_set.syntaxes()
|
||||
}
|
||||
|
||||
pub fn themes(&self) -> impl Iterator<Item = &String> {
|
||||
self.theme_set.themes.keys()
|
||||
pub fn themes(&self) -> impl Iterator<Item = &str> {
|
||||
self.theme_set.themes.keys().map(|s| s.as_ref())
|
||||
}
|
||||
|
||||
pub(crate) fn get_theme(&self, theme: &str) -> &Theme {
|
||||
@@ -168,7 +180,7 @@ impl HighlightingAssets {
|
||||
theme
|
||||
);
|
||||
}
|
||||
&self.theme_set.themes[self.fallback_theme.unwrap_or(Self::default_theme())]
|
||||
&self.theme_set.themes[self.fallback_theme.unwrap_or_else(|| Self::default_theme())]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -176,59 +188,82 @@ impl HighlightingAssets {
|
||||
pub(crate) fn get_syntax(
|
||||
&self,
|
||||
language: Option<&str>,
|
||||
filename: InputFile,
|
||||
reader: &mut InputFileReader,
|
||||
input: &mut OpenedInput,
|
||||
mapping: &SyntaxMapping,
|
||||
) -> &SyntaxReference {
|
||||
let syntax = match (language, filename) {
|
||||
(Some(language), _) => self.syntax_set.find_syntax_by_token(language),
|
||||
(None, InputFile::Ordinary(filename)) => {
|
||||
let path = Path::new(filename);
|
||||
let syntax = if let Some(language) = language {
|
||||
self.syntax_set.find_syntax_by_token(language)
|
||||
} else {
|
||||
match input.kind {
|
||||
OpenedInputKind::OrdinaryFile(ref actual_path) => {
|
||||
let path_str = input
|
||||
.metadata
|
||||
.user_provided_name
|
||||
.as_ref()
|
||||
.unwrap_or(actual_path);
|
||||
let path = Path::new(path_str);
|
||||
let line_syntax = self.get_first_line_syntax(&mut input.reader);
|
||||
|
||||
let file_name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
|
||||
let extension = path.extension().and_then(|x| x.to_str()).unwrap_or("");
|
||||
|
||||
let ext_syntax = self
|
||||
.syntax_set
|
||||
.find_syntax_by_extension(&file_name)
|
||||
.or_else(|| self.syntax_set.find_syntax_by_extension(&extension));
|
||||
let line_syntax = String::from_utf8(reader.first_line.clone())
|
||||
.ok()
|
||||
.and_then(|l| self.syntax_set.find_syntax_by_first_line(&l));
|
||||
|
||||
let absolute_path = path.canonicalize().ok().unwrap_or(path.to_owned());
|
||||
match mapping.get_syntax_for(absolute_path) {
|
||||
Some(MappingTarget::MapTo(syntax_name)) => {
|
||||
// TODO: we should probably return an error here if this syntax can not be
|
||||
// found. Currently, we just fall back to 'plain'.
|
||||
self.syntax_set.find_syntax_by_name(syntax_name)
|
||||
let absolute_path = path.canonicalize().ok().unwrap_or_else(|| path.to_owned());
|
||||
match mapping.get_syntax_for(absolute_path) {
|
||||
Some(MappingTarget::MapTo(syntax_name)) => {
|
||||
// TODO: we should probably return an error here if this syntax can not be
|
||||
// found. Currently, we just fall back to 'plain'.
|
||||
self.syntax_set.find_syntax_by_name(syntax_name)
|
||||
}
|
||||
Some(MappingTarget::MapToUnknown) => line_syntax,
|
||||
None => {
|
||||
let file_name = path.file_name().unwrap_or_default();
|
||||
self.get_extension_syntax(file_name).or(line_syntax)
|
||||
}
|
||||
}
|
||||
Some(MappingTarget::MapToUnknown) => line_syntax,
|
||||
None => ext_syntax.or(line_syntax),
|
||||
}
|
||||
OpenedInputKind::StdIn | OpenedInputKind::CustomReader => {
|
||||
if let Some(ref name) = input.metadata.user_provided_name {
|
||||
self.get_extension_syntax(&name)
|
||||
.or_else(|| self.get_first_line_syntax(&mut input.reader))
|
||||
} else {
|
||||
self.get_first_line_syntax(&mut input.reader)
|
||||
}
|
||||
}
|
||||
OpenedInputKind::ThemePreviewFile => self.syntax_set.find_syntax_by_name("Rust"),
|
||||
}
|
||||
(None, InputFile::StdIn) => String::from_utf8(reader.first_line.clone())
|
||||
.ok()
|
||||
.and_then(|l| self.syntax_set.find_syntax_by_first_line(&l)),
|
||||
(_, InputFile::ThemePreviewFile) => self.syntax_set.find_syntax_by_name("Rust"),
|
||||
};
|
||||
|
||||
syntax.unwrap_or_else(|| self.syntax_set.find_syntax_plain_text())
|
||||
}
|
||||
|
||||
fn get_extension_syntax(&self, file_name: &OsStr) -> Option<&SyntaxReference> {
|
||||
self.syntax_set
|
||||
.find_syntax_by_extension(file_name.to_str().unwrap_or_default())
|
||||
.or_else(|| {
|
||||
self.syntax_set.find_syntax_by_extension(
|
||||
Path::new(file_name)
|
||||
.extension()
|
||||
.and_then(|x| x.to_str())
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn get_first_line_syntax(&self, reader: &mut InputReader) -> Option<&SyntaxReference> {
|
||||
String::from_utf8(reader.first_line.clone())
|
||||
.ok()
|
||||
.and_then(|l| self.syntax_set.find_syntax_by_first_line(&l))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
|
||||
use tempdir::TempDir;
|
||||
|
||||
use crate::assets::HighlightingAssets;
|
||||
use crate::inputfile::InputFile;
|
||||
use crate::syntax_mapping::{MappingTarget, SyntaxMapping};
|
||||
use crate::input::Input;
|
||||
|
||||
struct SyntaxDetectionTest<'a> {
|
||||
assets: HighlightingAssets,
|
||||
@@ -246,26 +281,43 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn synax_for_file_with_content(&self, file_name: &str, first_line: &str) -> String {
|
||||
fn syntax_for_file_with_content_os(&self, file_name: &OsStr, first_line: &str) -> String {
|
||||
let file_path = self.temp_dir.path().join(file_name);
|
||||
{
|
||||
let mut temp_file = File::create(&file_path).unwrap();
|
||||
writeln!(temp_file, "{}", first_line).unwrap();
|
||||
}
|
||||
|
||||
let input_file = InputFile::Ordinary(OsStr::new(&file_path));
|
||||
let syntax = self.assets.get_syntax(
|
||||
None,
|
||||
input_file,
|
||||
&mut input_file.get_reader(&io::stdin()).unwrap(),
|
||||
&self.syntax_mapping,
|
||||
);
|
||||
let input = Input::ordinary_file(file_path.as_os_str());
|
||||
let dummy_stdin: &[u8] = &[];
|
||||
let mut opened_input = input.open(dummy_stdin).unwrap();
|
||||
let syntax = self
|
||||
.assets
|
||||
.get_syntax(None, &mut opened_input, &self.syntax_mapping);
|
||||
|
||||
syntax.name.clone()
|
||||
}
|
||||
|
||||
fn syntax_for_file_os(&self, file_name: &OsStr) -> String {
|
||||
self.syntax_for_file_with_content_os(file_name, "")
|
||||
}
|
||||
|
||||
fn syntax_for_file_with_content(&self, file_name: &str, first_line: &str) -> String {
|
||||
self.syntax_for_file_with_content_os(OsStr::new(file_name), first_line)
|
||||
}
|
||||
|
||||
fn syntax_for_file(&self, file_name: &str) -> String {
|
||||
self.synax_for_file_with_content(file_name, "")
|
||||
self.syntax_for_file_with_content(file_name, "")
|
||||
}
|
||||
|
||||
fn syntax_for_stdin_with_content(&self, file_name: &str, content: &[u8]) -> String {
|
||||
let input = Input::stdin().with_name(Some(OsStr::new(file_name)));
|
||||
let mut opened_input = input.open(content).unwrap();
|
||||
|
||||
let syntax = self
|
||||
.assets
|
||||
.get_syntax(None, &mut opened_input, &self.syntax_mapping);
|
||||
syntax.name.clone()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,6 +336,19 @@ mod tests {
|
||||
assert_eq!(test.syntax_for_file("Makefile"), "Makefile");
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn syntax_detection_invalid_utf8() {
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
let test = SyntaxDetectionTest::new();
|
||||
|
||||
assert_eq!(
|
||||
test.syntax_for_file_os(OsStr::from_bytes(b"invalid_\xFEutf8_filename.rs")),
|
||||
"Rust"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn syntax_detection_well_defined_mapping_for_duplicate_extensions() {
|
||||
let test = SyntaxDetectionTest::new();
|
||||
@@ -299,15 +364,15 @@ mod tests {
|
||||
let test = SyntaxDetectionTest::new();
|
||||
|
||||
assert_eq!(
|
||||
test.synax_for_file_with_content("my_script", "#!/bin/bash"),
|
||||
test.syntax_for_file_with_content("my_script", "#!/bin/bash"),
|
||||
"Bourne Again Shell (bash)"
|
||||
);
|
||||
assert_eq!(
|
||||
test.synax_for_file_with_content("build", "#!/bin/bash"),
|
||||
test.syntax_for_file_with_content("build", "#!/bin/bash"),
|
||||
"Bourne Again Shell (bash)"
|
||||
);
|
||||
assert_eq!(
|
||||
test.synax_for_file_with_content("my_script", "<?php"),
|
||||
test.syntax_for_file_with_content("my_script", "<?php"),
|
||||
"PHP"
|
||||
);
|
||||
}
|
||||
@@ -333,4 +398,17 @@ mod tests {
|
||||
.ok();
|
||||
assert_eq!(test.syntax_for_file("README.MD"), "Markdown");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn syntax_detection_stdin_filename() {
|
||||
let test = SyntaxDetectionTest::new();
|
||||
|
||||
// from file extension
|
||||
assert_eq!(test.syntax_for_stdin_with_content("test.cpp", b"a"), "C++");
|
||||
// from first line (fallback)
|
||||
assert_eq!(
|
||||
test.syntax_for_stdin_with_content("my_script", b"#!/bin/bash"),
|
||||
"Bourne Again Shell (bash)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use semver::Version;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Default, Serialize, Deserialize)]
|
||||
pub struct AssetsMetadata {
|
||||
bat_version: Option<String>,
|
||||
creation_time: Option<SystemTime>,
|
||||
}
|
||||
|
||||
const FILENAME: &str = "metadata.yaml";
|
||||
|
||||
impl AssetsMetadata {
|
||||
pub(crate) fn new(current_version: &str) -> AssetsMetadata {
|
||||
AssetsMetadata {
|
||||
bat_version: Some(current_version.to_owned()),
|
||||
creation_time: Some(SystemTime::now()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn save_to_folder(&self, path: &Path) -> Result<()> {
|
||||
let file = File::create(path.join(FILENAME))?;
|
||||
serde_yaml::to_writer(file, self)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_load_from_folder(path: &Path) -> Result<Self> {
|
||||
let file = File::open(path.join(FILENAME))?;
|
||||
Ok(serde_yaml::from_reader(file)?)
|
||||
}
|
||||
|
||||
/// Load metadata about the stored cache file from the given folder.
|
||||
///
|
||||
/// There are several possibilities:
|
||||
/// - We find a metadata.yaml file and are able to parse it
|
||||
/// => return the contained information
|
||||
/// - We find a metadata.yaml file and but are not able to parse it
|
||||
/// => return a SerdeYamlError
|
||||
/// - We do not find a metadata.yaml file but a syntaxes.bin or themes.bin file
|
||||
/// => assume that these were created by an old version of bat and return
|
||||
/// AssetsMetadata::default() without version information
|
||||
/// - We do not find a metadata.yaml file and no cached assets
|
||||
/// => no user provided assets are available, return None
|
||||
pub fn load_from_folder(path: &Path) -> Result<Option<Self>> {
|
||||
match Self::try_load_from_folder(path) {
|
||||
Ok(metadata) => Ok(Some(metadata)),
|
||||
Err(e) => match e.kind() {
|
||||
ErrorKind::SerdeYamlError(_) => Err(e),
|
||||
_ => {
|
||||
if path.join("syntaxes.bin").exists() || path.join("themes.bin").exists() {
|
||||
Ok(Some(Self::default()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_compatible_with(&self, current_version: &str) -> bool {
|
||||
let current_version =
|
||||
Version::parse(current_version).expect("bat follows semantic versioning");
|
||||
let stored_version = self
|
||||
.bat_version
|
||||
.as_ref()
|
||||
.and_then(|ver| Version::parse(ver).ok());
|
||||
|
||||
if let Some(stored_version) = stored_version {
|
||||
current_version.major == stored_version.major
|
||||
&& current_version.minor == stored_version.minor
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
+80
-45
@@ -1,5 +1,6 @@
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::str::FromStr;
|
||||
|
||||
use atty::{self, Stream};
|
||||
@@ -9,20 +10,17 @@ use crate::{
|
||||
config::{get_args_from_config_file, get_args_from_env_var},
|
||||
};
|
||||
use clap::ArgMatches;
|
||||
use wild;
|
||||
|
||||
use console::Term;
|
||||
|
||||
#[cfg(windows)]
|
||||
use ansi_term;
|
||||
|
||||
use bat::{
|
||||
config::{
|
||||
Config, HighlightedLineRanges, InputFile, LineRange, LineRanges, MappingTarget, OutputWrap,
|
||||
PagingMode, StyleComponent, StyleComponents, SyntaxMapping,
|
||||
},
|
||||
errors::*,
|
||||
HighlightingAssets,
|
||||
assets::HighlightingAssets,
|
||||
config::{Config, VisibleLines},
|
||||
error::*,
|
||||
input::Input,
|
||||
line_range::{HighlightedLineRanges, LineRange, LineRanges},
|
||||
style::{StyleComponent, StyleComponents},
|
||||
MappingTarget, PagingMode, SyntaxMapping, WrappingMode,
|
||||
};
|
||||
|
||||
fn is_truecolor_terminal() -> bool {
|
||||
@@ -76,18 +74,17 @@ impl App {
|
||||
Ok(clap_app::build_app(interactive_output).get_matches_from(args))
|
||||
}
|
||||
|
||||
pub fn config(&self) -> Result<Config> {
|
||||
let files = self.files();
|
||||
pub fn config(&self, inputs: &[Input]) -> Result<Config> {
|
||||
let style_components = self.style_components()?;
|
||||
|
||||
let paging_mode = match self.matches.value_of("paging") {
|
||||
Some("always") => PagingMode::Always,
|
||||
Some("never") => PagingMode::Never,
|
||||
Some("auto") | _ => {
|
||||
Some("auto") | None => {
|
||||
if self.matches.occurrences_of("plain") > 1 {
|
||||
// If we have -pp as an option when in auto mode, the pager should be disabled.
|
||||
PagingMode::Never
|
||||
} else if files.contains(&InputFile::StdIn) {
|
||||
} else if inputs.iter().any(Input::is_stdin) {
|
||||
// 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.
|
||||
@@ -102,6 +99,7 @@ impl App {
|
||||
PagingMode::Never
|
||||
}
|
||||
}
|
||||
_ => unreachable!("other values for --paging are not allowed"),
|
||||
};
|
||||
|
||||
let mut syntax_mapping = SyntaxMapping::builtin();
|
||||
@@ -146,22 +144,23 @@ impl App {
|
||||
}
|
||||
}),
|
||||
show_nonprintable: self.matches.is_present("show-all"),
|
||||
output_wrap: if self.interactive_output || maybe_term_width.is_some() {
|
||||
wrapping_mode: if self.interactive_output || maybe_term_width.is_some() {
|
||||
match self.matches.value_of("wrap") {
|
||||
Some("character") => OutputWrap::Character,
|
||||
Some("never") => OutputWrap::None,
|
||||
Some("auto") | _ => {
|
||||
Some("character") => WrappingMode::Character,
|
||||
Some("never") => WrappingMode::NoWrapping,
|
||||
Some("auto") | None => {
|
||||
if style_components.plain() {
|
||||
OutputWrap::None
|
||||
WrappingMode::NoWrapping
|
||||
} else {
|
||||
OutputWrap::Character
|
||||
WrappingMode::Character
|
||||
}
|
||||
}
|
||||
_ => unreachable!("other values for --paging are not allowed"),
|
||||
}
|
||||
} else {
|
||||
// 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
|
||||
WrappingMode::NoWrapping
|
||||
},
|
||||
colored_output: match self.matches.value_of("color") {
|
||||
Some("always") => true,
|
||||
@@ -173,7 +172,6 @@ impl App {
|
||||
loop_through: !(self.interactive_output
|
||||
|| self.matches.value_of("color") == Some("always")
|
||||
|| self.matches.value_of("decorations") == Some("always")),
|
||||
files,
|
||||
tab_width: self
|
||||
.matches
|
||||
.value_of("tabs")
|
||||
@@ -200,13 +198,23 @@ impl App {
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| String::from(HighlightingAssets::default_theme())),
|
||||
line_ranges: self
|
||||
.matches
|
||||
.values_of("line-range")
|
||||
.map(|vs| vs.map(LineRange::from).collect())
|
||||
.transpose()?
|
||||
.map(LineRanges::from)
|
||||
.unwrap_or_default(),
|
||||
visible_lines: if self.matches.is_present("diff") {
|
||||
VisibleLines::DiffContext(
|
||||
self.matches
|
||||
.value_of("diff-context")
|
||||
.and_then(|t| t.parse().ok())
|
||||
.unwrap_or(2),
|
||||
)
|
||||
} else {
|
||||
VisibleLines::Ranges(
|
||||
self.matches
|
||||
.values_of("line-range")
|
||||
.map(|vs| vs.map(LineRange::from).collect())
|
||||
.transpose()?
|
||||
.map(LineRanges::from)
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
},
|
||||
style_components,
|
||||
syntax_mapping,
|
||||
pager: self.matches.value_of("pager"),
|
||||
@@ -225,21 +233,48 @@ impl App {
|
||||
})
|
||||
}
|
||||
|
||||
fn files(&self) -> Vec<InputFile> {
|
||||
self.matches
|
||||
.values_of_os("FILE")
|
||||
.map(|values| {
|
||||
values
|
||||
.map(|filename| {
|
||||
if filename == "-" {
|
||||
InputFile::StdIn
|
||||
} else {
|
||||
InputFile::Ordinary(filename)
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_else(|| vec![InputFile::StdIn])
|
||||
pub fn inputs(&self) -> Result<Vec<Input>> {
|
||||
// verify equal length of file-names and input FILEs
|
||||
match self.matches.values_of("file-name") {
|
||||
Some(ref filenames)
|
||||
if self.matches.values_of_os("FILE").is_some()
|
||||
&& filenames.len() != self.matches.values_of_os("FILE").unwrap().len() =>
|
||||
{
|
||||
return Err("Must be one file name per input type.".into());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let filenames: Option<Vec<&str>> = self
|
||||
.matches
|
||||
.values_of("file-name")
|
||||
.map(|values| values.collect());
|
||||
|
||||
let mut filenames_or_none: Box<dyn Iterator<Item = _>> = match filenames {
|
||||
Some(ref filenames) => Box::new(filenames.iter().map(|name| Some(OsStr::new(*name)))),
|
||||
None => Box::new(std::iter::repeat(None)),
|
||||
};
|
||||
let files: Option<Vec<&OsStr>> = self.matches.values_of_os("FILE").map(|vs| vs.collect());
|
||||
|
||||
if files.is_none() {
|
||||
let input = Input::stdin().with_name(filenames_or_none.next().unwrap_or(None));
|
||||
return Ok(vec![input]);
|
||||
}
|
||||
let files_or_none: Box<dyn Iterator<Item = _>> = match files {
|
||||
Some(ref files) => Box::new(files.iter().map(|name| Some(*name))),
|
||||
None => Box::new(std::iter::repeat(None)),
|
||||
};
|
||||
|
||||
let mut file_input = Vec::new();
|
||||
for (filepath, provided_name) in files_or_none.zip(filenames_or_none) {
|
||||
if let Some(filepath) = filepath {
|
||||
if filepath.to_str().unwrap_or_default() == "-" {
|
||||
file_input.push(Input::stdin().with_name(provided_name));
|
||||
} else {
|
||||
file_input.push(Input::ordinary_file(filepath).with_name(provided_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(file_input)
|
||||
}
|
||||
|
||||
fn style_components(&self) -> Result<StyleComponents> {
|
||||
@@ -248,7 +283,7 @@ impl App {
|
||||
if matches.value_of("decorations") == Some("never") {
|
||||
HashSet::new()
|
||||
} else if matches.is_present("number") {
|
||||
[StyleComponent::Numbers].iter().cloned().collect()
|
||||
[StyleComponent::LineNumbers].iter().cloned().collect()
|
||||
} else if matches.is_present("plain") {
|
||||
[StyleComponent::Plain].iter().cloned().collect()
|
||||
} else {
|
||||
|
||||
+35
-15
@@ -1,18 +1,13 @@
|
||||
use std::borrow::Cow;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::crate_version;
|
||||
|
||||
use crate::directories::PROJECT_DIRS;
|
||||
|
||||
use bat::HighlightingAssets;
|
||||
|
||||
fn theme_set_path() -> PathBuf {
|
||||
PROJECT_DIRS.cache_dir().join("themes.bin")
|
||||
}
|
||||
|
||||
fn syntax_set_path() -> PathBuf {
|
||||
PROJECT_DIRS.cache_dir().join("syntaxes.bin")
|
||||
}
|
||||
use bat::assets::HighlightingAssets;
|
||||
use bat::assets_metadata::AssetsMetadata;
|
||||
use bat::error::*;
|
||||
|
||||
pub fn config_dir() -> Cow<'static, str> {
|
||||
PROJECT_DIRS.config_dir().to_string_lossy()
|
||||
@@ -23,16 +18,41 @@ pub fn cache_dir() -> Cow<'static, str> {
|
||||
}
|
||||
|
||||
pub fn clear_assets() {
|
||||
let theme_set_path = PROJECT_DIRS.cache_dir().join("themes.bin");
|
||||
let syntax_set_path = PROJECT_DIRS.cache_dir().join("syntaxes.bin");
|
||||
let metadata_file = PROJECT_DIRS.cache_dir().join("metadata.yaml");
|
||||
|
||||
print!("Clearing theme set cache ... ");
|
||||
fs::remove_file(theme_set_path()).ok();
|
||||
fs::remove_file(theme_set_path).ok();
|
||||
println!("okay");
|
||||
|
||||
print!("Clearing syntax set cache ... ");
|
||||
fs::remove_file(syntax_set_path()).ok();
|
||||
fs::remove_file(syntax_set_path).ok();
|
||||
println!("okay");
|
||||
|
||||
print!("Clearing metadata file ... ");
|
||||
fs::remove_file(metadata_file).ok();
|
||||
println!("okay");
|
||||
}
|
||||
|
||||
pub fn assets_from_cache_or_binary() -> HighlightingAssets {
|
||||
HighlightingAssets::from_cache(&theme_set_path(), &syntax_set_path())
|
||||
.unwrap_or(HighlightingAssets::from_binary())
|
||||
pub fn assets_from_cache_or_binary() -> Result<HighlightingAssets> {
|
||||
let cache_dir = PROJECT_DIRS.cache_dir();
|
||||
if let Some(metadata) = AssetsMetadata::load_from_folder(&cache_dir)? {
|
||||
if !metadata.is_compatible_with(crate_version!()) {
|
||||
return Err(format!(
|
||||
"The binary caches for the user-customized syntaxes and themes \
|
||||
in '{}' are not compatible with this version of bat ({}). To solve this, \
|
||||
either rebuild the cache (bat cache --build) or remove \
|
||||
the custom syntaxes/themes (bat cache --clear).\n\
|
||||
For more information, see:\n\n \
|
||||
https://github.com/sharkdp/bat#adding-new-syntaxes--language-definitions",
|
||||
cache_dir.to_string_lossy(),
|
||||
crate_version!()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(HighlightingAssets::from_cache(&cache_dir)
|
||||
.unwrap_or_else(|_| HighlightingAssets::from_binary()))
|
||||
}
|
||||
|
||||
+52
-2
@@ -1,4 +1,4 @@
|
||||
use clap::{App as ClapApp, AppSettings, Arg, ArgGroup, SubCommand};
|
||||
use clap::{crate_name, crate_version, App as ClapApp, AppSettings, Arg, ArgGroup, SubCommand};
|
||||
use std::path::Path;
|
||||
|
||||
pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
|
||||
@@ -93,6 +93,47 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
|
||||
'--highlight-line 40:' highlights lines 40 to the end of the file"
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("file-name")
|
||||
.long("file-name")
|
||||
.takes_value(true)
|
||||
.number_of_values(1)
|
||||
.multiple(true)
|
||||
.value_name("name")
|
||||
.help("Specify the name to display for a file.")
|
||||
.long_help("Specify the name to display for a file. Useful when piping \
|
||||
data to bat from STDIN when bat does not otherwise know \
|
||||
the filename."),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("diff")
|
||||
.long("diff")
|
||||
.short("d")
|
||||
.help("Only show lines that have been added/removed/modified.")
|
||||
.long_help(
|
||||
"Only show lines that have been added/removed/modified with respect \
|
||||
to the Git index. Use --diff-context=N to control how much context you want to see.",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("diff-context")
|
||||
.long("diff-context")
|
||||
.overrides_with("diff-context")
|
||||
.takes_value(true)
|
||||
.value_name("N")
|
||||
.validator(
|
||||
|n| {
|
||||
n.parse::<usize>()
|
||||
.map_err(|_| "must be a number")
|
||||
.map(|_| ()) // Convert to Result<(), &str>
|
||||
.map_err(|e| e.to_string())
|
||||
}, // Convert to Result<(), String>
|
||||
)
|
||||
.hidden_short_help(true)
|
||||
.long_help(
|
||||
"Include N lines of context around added/removed/modified lines when using '--diff'.",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("tabs")
|
||||
.long("tabs")
|
||||
@@ -140,7 +181,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
|
||||
t.parse::<i32>()
|
||||
.map_err(|_e| "must be an offset or number")
|
||||
.and_then(|v| if v == 0 && !is_offset {
|
||||
Err("terminal width cannot be zero".into())
|
||||
Err("terminal width cannot be zero")
|
||||
} else {
|
||||
Ok(())
|
||||
})
|
||||
@@ -327,6 +368,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
|
||||
.takes_value(true)
|
||||
.number_of_values(1)
|
||||
.value_name("N:M")
|
||||
.conflicts_with("diff")
|
||||
.help("Only print the lines from N to M.")
|
||||
.long_help(
|
||||
"Only print the specified range of lines for each file. \
|
||||
@@ -370,6 +412,14 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
|
||||
.hidden(true)
|
||||
.help("Show path to the configuration file."),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("generate-config-file")
|
||||
.long("generate-config-file")
|
||||
.conflicts_with("list-languages")
|
||||
.conflicts_with("list-themes")
|
||||
.hidden(true)
|
||||
.help("Generates a default configuration file."),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("config-dir")
|
||||
.long("config-dir")
|
||||
|
||||
+68
-2
@@ -1,10 +1,9 @@
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::io::{self, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use shell_words;
|
||||
|
||||
use crate::directories::PROJECT_DIRS;
|
||||
|
||||
pub fn config_file() -> PathBuf {
|
||||
@@ -15,6 +14,73 @@ pub fn config_file() -> PathBuf {
|
||||
.unwrap_or_else(|| PROJECT_DIRS.config_dir().join("config"))
|
||||
}
|
||||
|
||||
pub fn generate_config_file() -> bat::error::Result<()> {
|
||||
let config_file = config_file();
|
||||
if config_file.exists() {
|
||||
println!(
|
||||
"A config file already exists at: {}",
|
||||
config_file.to_string_lossy()
|
||||
);
|
||||
|
||||
print!("Overwrite? (y/N): ");
|
||||
io::stdout().flush()?;
|
||||
let mut decision = String::new();
|
||||
io::stdin().read_line(&mut decision)?;
|
||||
|
||||
if !decision.trim().eq_ignore_ascii_case("Y") {
|
||||
return Ok(());
|
||||
}
|
||||
} else {
|
||||
let config_dir = config_file.parent();
|
||||
match config_dir {
|
||||
Some(path) => fs::create_dir_all(path)?,
|
||||
None => {
|
||||
return Err(format!(
|
||||
"Unable to write config file to: {}",
|
||||
config_file.to_string_lossy()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let default_config =
|
||||
r#"# This is `bat`s configuration file. Each line either contains a comment or
|
||||
# a command-line option that you want to pass to `bat` by default. You can
|
||||
# run `bat --help` to get a list of all possible configuration options.
|
||||
|
||||
# Specify desired highlighting theme (e.g. "TwoDark"). Run `bat --list-themes`
|
||||
# for a list of all available themes
|
||||
#--theme="TwoDark"
|
||||
|
||||
# Enable this to use italic text on the terminal. This is not supported on all
|
||||
# terminal emulators (like tmux, by default):
|
||||
#--italic-text=always
|
||||
|
||||
# Uncomment the following line to disable automatic paging:
|
||||
#--paging=never
|
||||
|
||||
# Uncomment the following line if you are using less version >= 551 and want to
|
||||
# enable mouse scrolling support in `bat` when running inside tmux. This might
|
||||
# disable text selection, unless you press shift.
|
||||
#--pager="--RAW-CONTROL-CHARS --quit-if-one-screen --mouse"
|
||||
|
||||
# Syntax mappings: map a certain filename pattern to a language.
|
||||
# Example 1: use the C++ syntax for .ino files
|
||||
# Example 2: Use ".gitignore"-style highlighting for ".ignore" files
|
||||
#--map-syntax "*.ino:C++"
|
||||
#--map-syntax ".ignore:Git Ignore"
|
||||
"#;
|
||||
|
||||
fs::write(&config_file, default_config)?;
|
||||
println!(
|
||||
"Success! Config file written to {}",
|
||||
config_file.to_string_lossy()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_args_from_config_file() -> Result<Vec<OsString>, shell_words::ParseError> {
|
||||
Ok(fs::read_to_string(config_file())
|
||||
.ok()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use dirs;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
/// Wrapper for 'dirs' that treats MacOS more like Linux, by following the XDG specification.
|
||||
@@ -33,7 +32,7 @@ impl BatProjectDirs {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_cache_dir() -> Option<PathBuf> {
|
||||
fn get_cache_dir() -> Option<PathBuf> {
|
||||
// on all OS prefer BAT_CACHE_PATH if set
|
||||
let cache_dir_op = env::var_os("BAT_CACHE_PATH").map(PathBuf::from);
|
||||
if cache_dir_op.is_some() {
|
||||
|
||||
+32
-28
@@ -1,11 +1,6 @@
|
||||
// `error_chain!` can recurse deeply
|
||||
#![recursion_limit = "1024"]
|
||||
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
|
||||
extern crate dirs as dirs_rs;
|
||||
|
||||
mod app;
|
||||
mod assets;
|
||||
mod clap_app;
|
||||
@@ -22,15 +17,21 @@ use std::process;
|
||||
use ansi_term::Colour::Green;
|
||||
use ansi_term::Style;
|
||||
|
||||
use crate::{app::App, config::config_file};
|
||||
use crate::{
|
||||
app::App,
|
||||
config::{config_file, generate_config_file},
|
||||
};
|
||||
use assets::{assets_from_cache_or_binary, cache_dir, clear_assets, config_dir};
|
||||
use bat::Controller;
|
||||
use clap::crate_version;
|
||||
use directories::PROJECT_DIRS;
|
||||
|
||||
use bat::{
|
||||
config::{Config, InputFile, StyleComponent, StyleComponents},
|
||||
errors::*,
|
||||
HighlightingAssets,
|
||||
assets::HighlightingAssets,
|
||||
config::Config,
|
||||
controller::Controller,
|
||||
error::*,
|
||||
input::Input,
|
||||
style::{StyleComponent, StyleComponents},
|
||||
};
|
||||
|
||||
fn run_cache_subcommand(matches: &clap::ArgMatches) -> Result<()> {
|
||||
@@ -47,7 +48,7 @@ fn run_cache_subcommand(matches: &clap::ArgMatches) -> Result<()> {
|
||||
let blank = matches.is_present("blank");
|
||||
|
||||
let assets = HighlightingAssets::from_files(source_dir, !blank)?;
|
||||
assets.save_to_cache(target_dir)?;
|
||||
assets.save_to_cache(target_dir, crate_version!())?;
|
||||
} else if matches.is_present("clear") {
|
||||
clear_assets();
|
||||
}
|
||||
@@ -56,7 +57,7 @@ fn run_cache_subcommand(matches: &clap::ArgMatches) -> Result<()> {
|
||||
}
|
||||
|
||||
pub fn list_languages(config: &Config) -> Result<()> {
|
||||
let assets = assets_from_cache_or_binary();
|
||||
let assets = assets_from_cache_or_binary()?;
|
||||
let mut languages = assets
|
||||
.syntaxes()
|
||||
.iter()
|
||||
@@ -69,7 +70,7 @@ pub fn list_languages(config: &Config) -> Result<()> {
|
||||
|
||||
if config.loop_through {
|
||||
for lang in languages {
|
||||
write!(stdout, "{}:{}\n", lang.name, lang.file_extensions.join(","))?;
|
||||
writeln!(stdout, "{}:{}", lang.name, lang.file_extensions.join(","))?;
|
||||
}
|
||||
} else {
|
||||
let longest = languages
|
||||
@@ -118,11 +119,10 @@ pub fn list_languages(config: &Config) -> Result<()> {
|
||||
}
|
||||
|
||||
pub fn list_themes(cfg: &Config) -> Result<()> {
|
||||
let assets = assets_from_cache_or_binary();
|
||||
let assets = assets_from_cache_or_binary()?;
|
||||
let mut config = cfg.clone();
|
||||
let mut style = HashSet::new();
|
||||
style.insert(StyleComponent::Plain);
|
||||
config.files = vec![InputFile::ThemePreviewFile];
|
||||
config.style_components = StyleComponents(style);
|
||||
|
||||
let stdout = io::stdout();
|
||||
@@ -136,7 +136,9 @@ pub fn list_themes(cfg: &Config) -> Result<()> {
|
||||
Style::new().bold().paint(theme.to_string())
|
||||
)?;
|
||||
config.theme = theme.to_string();
|
||||
let _controller = Controller::new(&config, &assets).run();
|
||||
Controller::new(&config, &assets)
|
||||
.run(vec![Input::theme_preview_file()])
|
||||
.ok();
|
||||
writeln!(stdout)?;
|
||||
}
|
||||
} else {
|
||||
@@ -148,10 +150,10 @@ pub fn list_themes(cfg: &Config) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_controller(config: &Config) -> Result<bool> {
|
||||
let assets = assets_from_cache_or_binary();
|
||||
fn run_controller(inputs: Vec<Input>, config: &Config) -> Result<bool> {
|
||||
let assets = assets_from_cache_or_binary()?;
|
||||
let controller = Controller::new(&config, &assets);
|
||||
controller.run()
|
||||
controller.run(inputs)
|
||||
}
|
||||
|
||||
/// Returns `Err(..)` upon fatal errors. Otherwise, returns `Ok(true)` on full success and
|
||||
@@ -168,26 +170,27 @@ fn run() -> Result<bool> {
|
||||
run_cache_subcommand(cache_matches)?;
|
||||
Ok(true)
|
||||
} else {
|
||||
let mut config = app.config()?;
|
||||
config.files = vec![InputFile::Ordinary(OsStr::new("cache"))];
|
||||
let inputs = vec![Input::ordinary_file(OsStr::new("cache"))];
|
||||
let config = app.config(&inputs)?;
|
||||
|
||||
run_controller(&config)
|
||||
run_controller(inputs, &config)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let config = app.config()?;
|
||||
let inputs = app.inputs()?;
|
||||
let config = app.config(&inputs)?;
|
||||
|
||||
if app.matches.is_present("list-languages") {
|
||||
list_languages(&config)?;
|
||||
|
||||
Ok(true)
|
||||
} else if app.matches.is_present("list-themes") {
|
||||
list_themes(&config)?;
|
||||
|
||||
Ok(true)
|
||||
} else if app.matches.is_present("config-file") {
|
||||
println!("{}", config_file().to_string_lossy());
|
||||
|
||||
Ok(true)
|
||||
} else if app.matches.is_present("generate-config-file") {
|
||||
generate_config_file()?;
|
||||
Ok(true)
|
||||
} else if app.matches.is_present("config-dir") {
|
||||
writeln!(io::stdout(), "{}", config_dir())?;
|
||||
@@ -196,7 +199,7 @@ fn run() -> Result<bool> {
|
||||
writeln!(io::stdout(), "{}", cache_dir())?;
|
||||
Ok(true)
|
||||
} else {
|
||||
run_controller(&config)
|
||||
run_controller(inputs, &config)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,7 +210,8 @@ fn main() {
|
||||
|
||||
match result {
|
||||
Err(error) => {
|
||||
default_error_handler(&error);
|
||||
let stderr = std::io::stderr();
|
||||
default_error_handler(&error, &mut stderr.lock());
|
||||
process::exit(1);
|
||||
}
|
||||
Ok(false) => {
|
||||
|
||||
+32
-23
@@ -1,27 +1,38 @@
|
||||
pub use crate::inputfile::InputFile;
|
||||
pub use crate::line_range::{HighlightedLineRanges, LineRange, LineRanges};
|
||||
pub use crate::style::{StyleComponent, StyleComponents};
|
||||
pub use crate::syntax_mapping::{MappingTarget, SyntaxMapping};
|
||||
pub use crate::wrap::OutputWrap;
|
||||
use crate::line_range::{HighlightedLineRanges, LineRanges};
|
||||
#[cfg(feature = "paging")]
|
||||
use crate::paging::PagingMode;
|
||||
use crate::style::StyleComponents;
|
||||
use crate::syntax_mapping::SyntaxMapping;
|
||||
use crate::wrapping::WrappingMode;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum PagingMode {
|
||||
Always,
|
||||
QuitIfOneScreen,
|
||||
Never,
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum VisibleLines {
|
||||
/// Show all lines which are included in the line ranges
|
||||
Ranges(LineRanges),
|
||||
|
||||
#[cfg(feature = "git")]
|
||||
/// Only show lines surrounding added/deleted/modified lines
|
||||
DiffContext(usize),
|
||||
}
|
||||
|
||||
impl Default for PagingMode {
|
||||
impl VisibleLines {
|
||||
pub fn diff_mode(&self) -> bool {
|
||||
match self {
|
||||
Self::Ranges(_) => false,
|
||||
#[cfg(feature = "git")]
|
||||
Self::DiffContext(_) => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VisibleLines {
|
||||
fn default() -> Self {
|
||||
PagingMode::Never
|
||||
VisibleLines::Ranges(LineRanges::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Config<'a> {
|
||||
/// List of files to print
|
||||
pub files: Vec<InputFile<'a>>,
|
||||
|
||||
/// The explicitly configured language, if any
|
||||
pub language: Option<&'a str>,
|
||||
|
||||
@@ -47,14 +58,15 @@ pub struct Config<'a> {
|
||||
/// Style elements (grid, line numbers, ...)
|
||||
pub style_components: StyleComponents,
|
||||
|
||||
/// Text wrapping mode
|
||||
pub output_wrap: OutputWrap,
|
||||
/// If and how text should be wrapped
|
||||
pub wrapping_mode: WrappingMode,
|
||||
|
||||
/// Pager or STDOUT
|
||||
#[cfg(feature = "paging")]
|
||||
pub paging_mode: PagingMode,
|
||||
|
||||
/// Specifies the lines that should be printed
|
||||
pub line_ranges: LineRanges,
|
||||
/// Specifies which lines should be printed
|
||||
pub visible_lines: VisibleLines,
|
||||
|
||||
/// The syntax highlighting theme
|
||||
pub theme: String,
|
||||
@@ -76,10 +88,7 @@ pub struct Config<'a> {
|
||||
fn default_config_should_include_all_lines() {
|
||||
use crate::line_range::RangeCheckResult;
|
||||
|
||||
assert_eq!(
|
||||
Config::default().line_ranges.check(17),
|
||||
RangeCheckResult::InRange
|
||||
);
|
||||
assert_eq!(LineRanges::default().check(17), RangeCheckResult::InRange);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+131
-47
@@ -1,12 +1,17 @@
|
||||
use std::io::{self, Write};
|
||||
use std::path::Path;
|
||||
|
||||
use crate::assets::HighlightingAssets;
|
||||
use crate::config::{Config, PagingMode};
|
||||
use crate::errors::*;
|
||||
use crate::inputfile::{InputFile, InputFileReader};
|
||||
use crate::config::{Config, VisibleLines};
|
||||
#[cfg(feature = "git")]
|
||||
use crate::diff::{get_git_diff, LineChanges};
|
||||
use crate::error::*;
|
||||
use crate::input::{Input, InputReader, OpenedInput};
|
||||
#[cfg(feature = "git")]
|
||||
use crate::line_range::LineRange;
|
||||
use crate::line_range::{LineRanges, RangeCheckResult};
|
||||
use crate::output::OutputType;
|
||||
#[cfg(feature = "paging")]
|
||||
use crate::paging::PagingMode;
|
||||
use crate::printer::{InteractivePrinter, Printer, SimplePrinter};
|
||||
|
||||
pub struct Controller<'a> {
|
||||
@@ -19,54 +24,116 @@ impl<'b> Controller<'b> {
|
||||
Controller { config, assets }
|
||||
}
|
||||
|
||||
pub fn run(&self) -> Result<bool> {
|
||||
self.run_with_error_handler(default_error_handler)
|
||||
pub fn run(&self, inputs: Vec<Input>) -> Result<bool> {
|
||||
self.run_with_error_handler(inputs, default_error_handler)
|
||||
}
|
||||
|
||||
pub fn run_with_error_handler(&self, handle_error: impl Fn(&Error)) -> Result<bool> {
|
||||
// Do not launch the pager if NONE of the input files exist
|
||||
let mut paging_mode = self.config.paging_mode;
|
||||
if self.config.paging_mode != PagingMode::Never {
|
||||
let call_pager = self.config.files.iter().any(|file| {
|
||||
if let InputFile::Ordinary(path) = file {
|
||||
return Path::new(path).exists();
|
||||
} else {
|
||||
return true;
|
||||
pub fn run_with_error_handler(
|
||||
&self,
|
||||
inputs: Vec<Input>,
|
||||
handle_error: impl Fn(&Error, &mut dyn Write),
|
||||
) -> Result<bool> {
|
||||
let mut output_type;
|
||||
|
||||
#[cfg(feature = "paging")]
|
||||
{
|
||||
use crate::input::InputKind;
|
||||
use std::path::Path;
|
||||
|
||||
// Do not launch the pager if NONE of the input files exist
|
||||
let mut paging_mode = self.config.paging_mode;
|
||||
if self.config.paging_mode != PagingMode::Never {
|
||||
let call_pager = inputs.iter().any(|ref input| {
|
||||
if let InputKind::OrdinaryFile(ref path) = input.kind {
|
||||
Path::new(path).exists()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
if !call_pager {
|
||||
paging_mode = PagingMode::Never;
|
||||
}
|
||||
});
|
||||
if !call_pager {
|
||||
paging_mode = PagingMode::Never;
|
||||
}
|
||||
output_type = OutputType::from_mode(paging_mode, self.config.pager)?;
|
||||
}
|
||||
|
||||
let mut output_type = OutputType::from_mode(paging_mode, self.config.pager)?;
|
||||
#[cfg(not(feature = "paging"))]
|
||||
{
|
||||
output_type = OutputType::stdout();
|
||||
}
|
||||
|
||||
let attached_to_pager = output_type.is_pager();
|
||||
let writer = output_type.handle()?;
|
||||
let mut no_errors: bool = true;
|
||||
|
||||
let stdin = io::stdin();
|
||||
let stderr = io::stderr();
|
||||
let print_error = |error: &Error, write: &mut dyn Write| {
|
||||
if attached_to_pager {
|
||||
handle_error(error, write);
|
||||
} else {
|
||||
handle_error(error, &mut stderr.lock());
|
||||
}
|
||||
};
|
||||
|
||||
for input_file in &self.config.files {
|
||||
match input_file.get_reader(&stdin) {
|
||||
for input in inputs.into_iter() {
|
||||
match input.open(io::stdin().lock()) {
|
||||
Err(error) => {
|
||||
handle_error(&error);
|
||||
print_error(&error, writer);
|
||||
no_errors = false;
|
||||
}
|
||||
Ok(mut reader) => {
|
||||
let result = if self.config.loop_through {
|
||||
let mut printer = SimplePrinter::new();
|
||||
self.print_file(reader, &mut printer, writer, *input_file)
|
||||
Ok(mut opened_input) => {
|
||||
#[cfg(feature = "git")]
|
||||
let line_changes = if self.config.visible_lines.diff_mode()
|
||||
|| (!self.config.loop_through && self.config.style_components.changes())
|
||||
{
|
||||
match opened_input.kind {
|
||||
crate::input::OpenedInputKind::OrdinaryFile(ref path) => {
|
||||
let diff = get_git_diff(path);
|
||||
|
||||
// Skip files without Git modifications
|
||||
if self.config.visible_lines.diff_mode()
|
||||
&& diff
|
||||
.as_ref()
|
||||
.map(|changes| changes.is_empty())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
diff
|
||||
}
|
||||
_ if self.config.visible_lines.diff_mode() => {
|
||||
// Skip non-file inputs in diff mode
|
||||
continue;
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
let mut printer = InteractivePrinter::new(
|
||||
&self.config,
|
||||
&self.assets,
|
||||
*input_file,
|
||||
&mut reader,
|
||||
);
|
||||
self.print_file(reader, &mut printer, writer, *input_file)
|
||||
None
|
||||
};
|
||||
|
||||
let mut printer: Box<dyn Printer> = if self.config.loop_through {
|
||||
Box::new(SimplePrinter::new())
|
||||
} else {
|
||||
Box::new(InteractivePrinter::new(
|
||||
&self.config,
|
||||
&self.assets,
|
||||
&mut opened_input,
|
||||
#[cfg(feature = "git")]
|
||||
&line_changes,
|
||||
))
|
||||
};
|
||||
|
||||
let result = self.print_file(
|
||||
&mut *printer,
|
||||
writer,
|
||||
&mut opened_input,
|
||||
#[cfg(feature = "git")]
|
||||
&line_changes,
|
||||
);
|
||||
|
||||
if let Err(error) = result {
|
||||
handle_error(&error);
|
||||
print_error(&error, writer);
|
||||
no_errors = false;
|
||||
}
|
||||
}
|
||||
@@ -76,30 +143,47 @@ impl<'b> Controller<'b> {
|
||||
Ok(no_errors)
|
||||
}
|
||||
|
||||
fn print_file<'a, P: Printer>(
|
||||
fn print_file<'a>(
|
||||
&self,
|
||||
reader: InputFileReader,
|
||||
printer: &mut P,
|
||||
printer: &mut dyn Printer,
|
||||
writer: &mut dyn Write,
|
||||
input_file: InputFile<'a>,
|
||||
input: &mut OpenedInput,
|
||||
#[cfg(feature = "git")] line_changes: &Option<LineChanges>,
|
||||
) -> Result<()> {
|
||||
if !reader.first_line.is_empty() || self.config.style_components.header() {
|
||||
printer.print_header(writer, input_file)?;
|
||||
if !input.reader.first_line.is_empty() || self.config.style_components.header() {
|
||||
printer.print_header(writer, input)?;
|
||||
}
|
||||
|
||||
if !reader.first_line.is_empty() {
|
||||
self.print_file_ranges(printer, writer, reader, &self.config.line_ranges)?;
|
||||
if !input.reader.first_line.is_empty() {
|
||||
let line_ranges = match self.config.visible_lines {
|
||||
VisibleLines::Ranges(ref line_ranges) => line_ranges.clone(),
|
||||
#[cfg(feature = "git")]
|
||||
VisibleLines::DiffContext(context) => {
|
||||
let mut line_ranges: Vec<LineRange> = vec![];
|
||||
|
||||
if let Some(line_changes) = line_changes {
|
||||
for line in line_changes.keys() {
|
||||
let line = *line as usize;
|
||||
line_ranges.push(LineRange::new(line - context, line + context));
|
||||
}
|
||||
}
|
||||
|
||||
LineRanges::from(line_ranges)
|
||||
}
|
||||
};
|
||||
|
||||
self.print_file_ranges(printer, writer, &mut input.reader, &line_ranges)?;
|
||||
}
|
||||
printer.print_footer(writer)?;
|
||||
printer.print_footer(writer, input)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_file_ranges<P: Printer>(
|
||||
fn print_file_ranges(
|
||||
&self,
|
||||
printer: &mut P,
|
||||
printer: &mut dyn Printer,
|
||||
writer: &mut dyn Write,
|
||||
mut reader: InputFileReader,
|
||||
reader: &mut InputReader,
|
||||
line_ranges: &LineRanges,
|
||||
) -> Result<()> {
|
||||
let mut line_buffer = Vec::new();
|
||||
|
||||
+12
-8
@@ -1,14 +1,15 @@
|
||||
#[cfg(feature = "git")]
|
||||
use crate::diff::LineChange;
|
||||
use crate::printer::{Colors, InteractivePrinter};
|
||||
use ansi_term::Style;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DecorationText {
|
||||
pub(crate) struct DecorationText {
|
||||
pub width: usize,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
pub trait Decoration {
|
||||
pub(crate) trait Decoration {
|
||||
fn generate(
|
||||
&self,
|
||||
line_number: usize,
|
||||
@@ -18,14 +19,14 @@ pub trait Decoration {
|
||||
fn width(&self) -> usize;
|
||||
}
|
||||
|
||||
pub struct LineNumberDecoration {
|
||||
pub(crate) struct LineNumberDecoration {
|
||||
color: Style,
|
||||
cached_wrap: DecorationText,
|
||||
cached_wrap_invalid_at: usize,
|
||||
}
|
||||
|
||||
impl LineNumberDecoration {
|
||||
pub fn new(colors: &Colors) -> Self {
|
||||
pub(crate) fn new(colors: &Colors) -> Self {
|
||||
LineNumberDecoration {
|
||||
color: colors.line_number,
|
||||
cached_wrap_invalid_at: 10000,
|
||||
@@ -68,7 +69,8 @@ impl Decoration for LineNumberDecoration {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LineChangesDecoration {
|
||||
#[cfg(feature = "git")]
|
||||
pub(crate) struct LineChangesDecoration {
|
||||
cached_none: DecorationText,
|
||||
cached_added: DecorationText,
|
||||
cached_removed_above: DecorationText,
|
||||
@@ -76,6 +78,7 @@ pub struct LineChangesDecoration {
|
||||
cached_modified: DecorationText,
|
||||
}
|
||||
|
||||
#[cfg(feature = "git")]
|
||||
impl LineChangesDecoration {
|
||||
#[inline]
|
||||
fn generate_cached(style: Style, text: &str) -> DecorationText {
|
||||
@@ -85,7 +88,7 @@ impl LineChangesDecoration {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(colors: &Colors) -> Self {
|
||||
pub(crate) fn new(colors: &Colors) -> Self {
|
||||
LineChangesDecoration {
|
||||
cached_none: Self::generate_cached(Style::default(), " "),
|
||||
cached_added: Self::generate_cached(colors.git_added, "+"),
|
||||
@@ -96,6 +99,7 @@ impl LineChangesDecoration {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "git")]
|
||||
impl Decoration for LineChangesDecoration {
|
||||
fn generate(
|
||||
&self,
|
||||
@@ -123,12 +127,12 @@ impl Decoration for LineChangesDecoration {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GridBorderDecoration {
|
||||
pub(crate) struct GridBorderDecoration {
|
||||
cached: DecorationText,
|
||||
}
|
||||
|
||||
impl GridBorderDecoration {
|
||||
pub fn new(colors: &Colors) -> Self {
|
||||
pub(crate) fn new(colors: &Colors) -> Self {
|
||||
GridBorderDecoration {
|
||||
cached: DecorationText {
|
||||
text: colors.grid.paint("│").to_string(),
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![cfg(feature = "git")]
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
use error_chain::error_chain;
|
||||
use std::io::Write;
|
||||
|
||||
error_chain! {
|
||||
foreign_links {
|
||||
Clap(::clap::Error) #[cfg(feature = "application")];
|
||||
Io(::std::io::Error);
|
||||
SyntectError(::syntect::LoadingError);
|
||||
ParseIntError(::std::num::ParseIntError);
|
||||
GlobParsingError(::globset::Error);
|
||||
SerdeYamlError(::serde_yaml::Error);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_error_handler(error: &Error, output: &mut dyn Write) {
|
||||
use ansi_term::Colour::Red;
|
||||
|
||||
match error {
|
||||
Error(ErrorKind::Io(ref io_error), _)
|
||||
if io_error.kind() == ::std::io::ErrorKind::BrokenPipe =>
|
||||
{
|
||||
::std::process::exit(0);
|
||||
}
|
||||
Error(ErrorKind::SerdeYamlError(_), _) => {
|
||||
writeln!(
|
||||
output,
|
||||
"{}: Error while parsing metadata.yaml file: {}",
|
||||
Red.paint("[bat error]"),
|
||||
error
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
_ => {
|
||||
writeln!(output, "{}: {}", Red.paint("[bat error]"), error).ok();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
use error_chain::error_chain;
|
||||
|
||||
error_chain! {
|
||||
foreign_links {
|
||||
Clap(::clap::Error);
|
||||
Io(::std::io::Error);
|
||||
SyntectError(::syntect::LoadingError);
|
||||
ParseIntError(::std::num::ParseIntError);
|
||||
GlobParsingError(::globset::Error);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_error_handler(error: &Error) {
|
||||
match error {
|
||||
Error(ErrorKind::Io(ref io_error), _)
|
||||
if io_error.kind() == ::std::io::ErrorKind::BrokenPipe =>
|
||||
{
|
||||
::std::process::exit(0);
|
||||
}
|
||||
_ => {
|
||||
use ansi_term::Colour::Red;
|
||||
eprintln!("{}: {}", Red.paint("[bat error]"), error);
|
||||
}
|
||||
};
|
||||
}
|
||||
+258
@@ -0,0 +1,258 @@
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::fs::File;
|
||||
use std::io::{self, BufRead, BufReader, Read};
|
||||
|
||||
use content_inspector::{self, ContentType};
|
||||
|
||||
use crate::error::*;
|
||||
|
||||
const THEME_PREVIEW_FILE: &[u8] = include_bytes!("../assets/theme_preview.rs");
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct InputDescription {
|
||||
pub full: String,
|
||||
pub prefix: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
pub(crate) enum InputKind<'a> {
|
||||
OrdinaryFile(OsString),
|
||||
StdIn,
|
||||
ThemePreviewFile,
|
||||
CustomReader(Box<dyn Read + 'a>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub(crate) struct InputMetadata {
|
||||
pub(crate) user_provided_name: Option<OsString>,
|
||||
}
|
||||
|
||||
pub struct Input<'a> {
|
||||
pub(crate) kind: InputKind<'a>,
|
||||
pub(crate) metadata: InputMetadata,
|
||||
}
|
||||
|
||||
pub(crate) enum OpenedInputKind {
|
||||
OrdinaryFile(OsString),
|
||||
StdIn,
|
||||
ThemePreviewFile,
|
||||
CustomReader,
|
||||
}
|
||||
|
||||
pub(crate) struct OpenedInput<'a> {
|
||||
pub(crate) kind: OpenedInputKind,
|
||||
pub(crate) metadata: InputMetadata,
|
||||
pub(crate) reader: InputReader<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Input<'a> {
|
||||
pub fn ordinary_file(path: &OsStr) -> Self {
|
||||
Input {
|
||||
kind: InputKind::OrdinaryFile(path.to_os_string()),
|
||||
metadata: InputMetadata::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stdin() -> Self {
|
||||
Input {
|
||||
kind: InputKind::StdIn,
|
||||
metadata: InputMetadata::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn theme_preview_file() -> Self {
|
||||
Input {
|
||||
kind: InputKind::ThemePreviewFile,
|
||||
metadata: InputMetadata::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_reader(reader: Box<dyn Read + 'a>) -> Self {
|
||||
Input {
|
||||
kind: InputKind::CustomReader(reader),
|
||||
metadata: InputMetadata::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_stdin(&self) -> bool {
|
||||
if let InputKind::StdIn = self.kind {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_name(mut self, provided_name: Option<&OsStr>) -> Self {
|
||||
self.metadata.user_provided_name = provided_name.map(|n| n.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn open<R: BufRead + 'a>(self, stdin: R) -> Result<OpenedInput<'a>> {
|
||||
match self.kind {
|
||||
InputKind::StdIn => Ok(OpenedInput {
|
||||
kind: OpenedInputKind::StdIn,
|
||||
metadata: self.metadata,
|
||||
reader: InputReader::new(stdin),
|
||||
}),
|
||||
InputKind::OrdinaryFile(path) => Ok(OpenedInput {
|
||||
kind: OpenedInputKind::OrdinaryFile(path.clone()),
|
||||
metadata: self.metadata,
|
||||
reader: {
|
||||
let file = File::open(&path)
|
||||
.map_err(|e| format!("'{}': {}", path.to_string_lossy(), e))?;
|
||||
if file.metadata()?.is_dir() {
|
||||
return Err(format!("'{}' is a directory.", path.to_string_lossy()).into());
|
||||
}
|
||||
InputReader::new(BufReader::new(file))
|
||||
},
|
||||
}),
|
||||
InputKind::ThemePreviewFile => Ok(OpenedInput {
|
||||
kind: OpenedInputKind::ThemePreviewFile,
|
||||
metadata: self.metadata,
|
||||
reader: InputReader::new(THEME_PREVIEW_FILE),
|
||||
}),
|
||||
InputKind::CustomReader(reader) => Ok(OpenedInput {
|
||||
kind: OpenedInputKind::CustomReader,
|
||||
metadata: self.metadata,
|
||||
reader: InputReader::new(BufReader::new(reader)),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> OpenedInput<'a> {
|
||||
pub fn description(&self) -> InputDescription {
|
||||
if let Some(ref name) = self.metadata.user_provided_name {
|
||||
InputDescription {
|
||||
full: format!("file '{}'", name.to_string_lossy()),
|
||||
prefix: "File: ".to_owned(),
|
||||
name: name.to_string_lossy().into_owned(),
|
||||
}
|
||||
} else {
|
||||
match self.kind {
|
||||
OpenedInputKind::OrdinaryFile(ref path) => InputDescription {
|
||||
full: format!("file '{}'", path.to_string_lossy()),
|
||||
prefix: "File: ".to_owned(),
|
||||
name: path.to_string_lossy().into_owned(),
|
||||
},
|
||||
OpenedInputKind::StdIn => InputDescription {
|
||||
full: "STDIN".to_owned(),
|
||||
prefix: "".to_owned(),
|
||||
name: "STDIN".to_owned(),
|
||||
},
|
||||
OpenedInputKind::ThemePreviewFile => InputDescription {
|
||||
full: "".to_owned(),
|
||||
prefix: "".to_owned(),
|
||||
name: "".to_owned(),
|
||||
},
|
||||
OpenedInputKind::CustomReader => InputDescription {
|
||||
full: "reader".to_owned(),
|
||||
prefix: "".to_owned(),
|
||||
name: "READER".into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct InputReader<'a> {
|
||||
inner: Box<dyn BufRead + 'a>,
|
||||
pub(crate) first_line: Vec<u8>,
|
||||
pub(crate) content_type: Option<ContentType>,
|
||||
}
|
||||
|
||||
impl<'a> InputReader<'a> {
|
||||
fn new<R: BufRead + 'a>(mut reader: R) -> InputReader<'a> {
|
||||
let mut first_line = vec![];
|
||||
reader.read_until(b'\n', &mut first_line).ok();
|
||||
|
||||
let content_type = if first_line.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(content_inspector::inspect(&first_line[..]))
|
||||
};
|
||||
|
||||
if content_type == Some(ContentType::UTF_16LE) {
|
||||
reader.read_until(0x00, &mut first_line).ok();
|
||||
}
|
||||
|
||||
InputReader {
|
||||
inner: Box::new(reader),
|
||||
first_line,
|
||||
content_type,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn read_line(&mut self, buf: &mut Vec<u8>) -> io::Result<bool> {
|
||||
if self.first_line.is_empty() {
|
||||
let res = self.inner.read_until(b'\n', buf).map(|size| size > 0)?;
|
||||
|
||||
if self.content_type == Some(ContentType::UTF_16LE) {
|
||||
self.inner.read_until(0x00, buf).ok();
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
} else {
|
||||
buf.append(&mut self.first_line);
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic() {
|
||||
let content = b"#!/bin/bash\necho hello";
|
||||
let mut reader = InputReader::new(&content[..]);
|
||||
|
||||
assert_eq!(b"#!/bin/bash\n", &reader.first_line[..]);
|
||||
|
||||
let mut buffer = vec![];
|
||||
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(true, res.unwrap());
|
||||
assert_eq!(b"#!/bin/bash\n", &buffer[..]);
|
||||
|
||||
buffer.clear();
|
||||
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(true, res.unwrap());
|
||||
assert_eq!(b"echo hello", &buffer[..]);
|
||||
|
||||
buffer.clear();
|
||||
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(false, res.unwrap());
|
||||
assert!(buffer.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn utf16le() {
|
||||
let content = b"\xFF\xFE\x73\x00\x0A\x00\x64\x00";
|
||||
let mut reader = InputReader::new(&content[..]);
|
||||
|
||||
assert_eq!(b"\xFF\xFE\x73\x00\x0A\x00", &reader.first_line[..]);
|
||||
|
||||
let mut buffer = vec![];
|
||||
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(true, res.unwrap());
|
||||
assert_eq!(b"\xFF\xFE\x73\x00\x0A\x00", &buffer[..]);
|
||||
|
||||
buffer.clear();
|
||||
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(true, res.unwrap());
|
||||
assert_eq!(b"\x64\x00", &buffer[..]);
|
||||
|
||||
buffer.clear();
|
||||
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(false, res.unwrap());
|
||||
assert!(buffer.is_empty());
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::File;
|
||||
use std::io::{self, BufRead, BufReader};
|
||||
|
||||
use content_inspector::{self, ContentType};
|
||||
|
||||
use crate::errors::*;
|
||||
|
||||
const THEME_PREVIEW_FILE: &[u8] = include_bytes!("../assets/theme_preview.rs");
|
||||
|
||||
pub struct InputFileReader<'a> {
|
||||
inner: Box<dyn BufRead + 'a>,
|
||||
pub(crate) first_line: Vec<u8>,
|
||||
pub(crate) content_type: Option<ContentType>,
|
||||
}
|
||||
|
||||
impl<'a> InputFileReader<'a> {
|
||||
fn new<R: BufRead + 'a>(mut reader: R) -> InputFileReader<'a> {
|
||||
let mut first_line = vec![];
|
||||
reader.read_until(b'\n', &mut first_line).ok();
|
||||
|
||||
let content_type = if first_line.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(content_inspector::inspect(&first_line[..]))
|
||||
};
|
||||
|
||||
if content_type == Some(ContentType::UTF_16LE) {
|
||||
reader.read_until(0x00, &mut first_line).ok();
|
||||
}
|
||||
|
||||
InputFileReader {
|
||||
inner: Box::new(reader),
|
||||
first_line,
|
||||
content_type,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn read_line(&mut self, buf: &mut Vec<u8>) -> io::Result<bool> {
|
||||
if self.first_line.is_empty() {
|
||||
let res = self.inner.read_until(b'\n', buf).map(|size| size > 0)?;
|
||||
|
||||
if self.content_type == Some(ContentType::UTF_16LE) {
|
||||
self.inner.read_until(0x00, buf).ok();
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
} else {
|
||||
buf.append(&mut self.first_line);
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum InputFile<'a> {
|
||||
StdIn,
|
||||
Ordinary(&'a OsStr),
|
||||
ThemePreviewFile,
|
||||
}
|
||||
|
||||
impl<'a> InputFile<'a> {
|
||||
pub(crate) fn get_reader(&self, stdin: &'a io::Stdin) -> Result<InputFileReader> {
|
||||
match self {
|
||||
InputFile::StdIn => Ok(InputFileReader::new(stdin.lock())),
|
||||
InputFile::Ordinary(filename) => {
|
||||
let file = File::open(filename)
|
||||
.map_err(|e| format!("'{}': {}", filename.to_string_lossy(), e))?;
|
||||
|
||||
if file.metadata()?.is_dir() {
|
||||
return Err(format!("'{}' is a directory.", filename.to_string_lossy()).into());
|
||||
}
|
||||
|
||||
Ok(InputFileReader::new(BufReader::new(file)))
|
||||
}
|
||||
InputFile::ThemePreviewFile => Ok(InputFileReader::new(THEME_PREVIEW_FILE)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic() {
|
||||
let content = b"#!/bin/bash\necho hello";
|
||||
let mut reader = InputFileReader::new(&content[..]);
|
||||
|
||||
assert_eq!(b"#!/bin/bash\n", &reader.first_line[..]);
|
||||
|
||||
let mut buffer = vec![];
|
||||
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(true, res.unwrap());
|
||||
assert_eq!(b"#!/bin/bash\n", &buffer[..]);
|
||||
|
||||
buffer.clear();
|
||||
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(true, res.unwrap());
|
||||
assert_eq!(b"echo hello", &buffer[..]);
|
||||
|
||||
buffer.clear();
|
||||
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(false, res.unwrap());
|
||||
assert!(buffer.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn utf16le() {
|
||||
let content = b"\xFF\xFE\x73\x00\x0A\x00\x64\x00";
|
||||
let mut reader = InputFileReader::new(&content[..]);
|
||||
|
||||
assert_eq!(b"\xFF\xFE\x73\x00\x0A\x00", &reader.first_line[..]);
|
||||
|
||||
let mut buffer = vec![];
|
||||
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(true, res.unwrap());
|
||||
assert_eq!(b"\xFF\xFE\x73\x00\x0A\x00", &buffer[..]);
|
||||
|
||||
buffer.clear();
|
||||
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(true, res.unwrap());
|
||||
assert_eq!(b"\x64\x00", &buffer[..]);
|
||||
|
||||
buffer.clear();
|
||||
|
||||
let res = reader.read_line(&mut buffer);
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(false, res.unwrap());
|
||||
assert!(buffer.is_empty());
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
#![cfg(feature = "paging")]
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
pub fn retrieve_less_version() -> Option<usize> {
|
||||
|
||||
+37
-23
@@ -1,34 +1,48 @@
|
||||
// `error_chain!` can recurse deeply
|
||||
#![recursion_limit = "1024"]
|
||||
//! `bat` is a library to print syntax highlighted content.
|
||||
//!
|
||||
//! The main struct of this crate is `PrettyPrinter` which can be used to
|
||||
//! configure and run the syntax highlighting.
|
||||
//!
|
||||
//! If you need more control, you can also use the structs in the submodules
|
||||
//! (start with `controller::Controller`), but note that the API of these
|
||||
//! internal modules is much more likely to change. Some or all of these
|
||||
//! modules might be removed in the future.
|
||||
//!
|
||||
//! "Hello world" example:
|
||||
//! ```
|
||||
//! use bat::PrettyPrinter;
|
||||
//!
|
||||
//! PrettyPrinter::new()
|
||||
//! .input_from_bytes(b"<span style=\"color: #ff00cc\">Hello world!</span>\n")
|
||||
//! .language("html")
|
||||
//! .print()
|
||||
//! .unwrap();
|
||||
//! ```
|
||||
|
||||
extern crate ansi_term;
|
||||
extern crate atty;
|
||||
extern crate console;
|
||||
extern crate content_inspector;
|
||||
extern crate dirs as dirs_rs;
|
||||
extern crate encoding;
|
||||
extern crate git2;
|
||||
extern crate shell_words;
|
||||
extern crate syntect;
|
||||
extern crate wild;
|
||||
|
||||
pub(crate) mod assets;
|
||||
pub mod assets;
|
||||
pub mod assets_metadata;
|
||||
pub mod config;
|
||||
pub(crate) mod controller;
|
||||
pub mod controller;
|
||||
mod decorations;
|
||||
mod diff;
|
||||
pub mod errors;
|
||||
pub(crate) mod inputfile;
|
||||
pub mod error;
|
||||
pub mod input;
|
||||
mod less;
|
||||
pub(crate) mod line_range;
|
||||
pub mod line_range;
|
||||
mod output;
|
||||
#[cfg(feature = "paging")]
|
||||
pub(crate) mod paging;
|
||||
mod preprocessor;
|
||||
mod pretty_printer;
|
||||
pub(crate) mod printer;
|
||||
pub(crate) mod style;
|
||||
pub mod style;
|
||||
pub(crate) mod syntax_mapping;
|
||||
mod terminal;
|
||||
pub(crate) mod wrap;
|
||||
pub(crate) mod wrapping;
|
||||
|
||||
pub use assets::HighlightingAssets;
|
||||
pub use controller::Controller;
|
||||
pub use printer::{InteractivePrinter, Printer, SimplePrinter};
|
||||
pub use pretty_printer::PrettyPrinter;
|
||||
pub use syntax_mapping::{MappingTarget, SyntaxMapping};
|
||||
pub use wrapping::WrappingMode;
|
||||
|
||||
#[cfg(feature = "paging")]
|
||||
pub use paging::PagingMode;
|
||||
|
||||
+11
-4
@@ -1,9 +1,9 @@
|
||||
use crate::errors::*;
|
||||
use crate::error::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LineRange {
|
||||
pub lower: usize,
|
||||
pub upper: usize,
|
||||
lower: usize,
|
||||
upper: usize,
|
||||
}
|
||||
|
||||
impl Default for LineRange {
|
||||
@@ -16,6 +16,13 @@ impl Default for LineRange {
|
||||
}
|
||||
|
||||
impl LineRange {
|
||||
pub fn new(from: usize, to: usize) -> Self {
|
||||
LineRange {
|
||||
lower: from,
|
||||
upper: to,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from(range_raw: &str) -> Result<LineRange> {
|
||||
LineRange::parse_range(range_raw)
|
||||
}
|
||||
@@ -23,7 +30,7 @@ impl LineRange {
|
||||
fn parse_range(range_raw: &str) -> Result<LineRange> {
|
||||
let mut new_range = LineRange::default();
|
||||
|
||||
if range_raw.bytes().nth(0).ok_or("Empty line range")? == b':' {
|
||||
if range_raw.bytes().next().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':' {
|
||||
|
||||
+37
-10
@@ -1,22 +1,22 @@
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::io::{self, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Child, Command, Stdio};
|
||||
#[cfg(feature = "paging")]
|
||||
use std::process::Child;
|
||||
|
||||
use shell_words;
|
||||
|
||||
use crate::config::PagingMode;
|
||||
use crate::errors::*;
|
||||
use crate::error::*;
|
||||
#[cfg(feature = "paging")]
|
||||
use crate::less::retrieve_less_version;
|
||||
#[cfg(feature = "paging")]
|
||||
use crate::paging::PagingMode;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum OutputType {
|
||||
#[cfg(feature = "paging")]
|
||||
Pager(Child),
|
||||
Stdout(io::Stdout),
|
||||
}
|
||||
|
||||
impl OutputType {
|
||||
#[cfg(feature = "paging")]
|
||||
pub fn from_mode(mode: PagingMode, pager: Option<&str>) -> Result<Self> {
|
||||
use self::PagingMode::*;
|
||||
Ok(match mode {
|
||||
@@ -27,7 +27,13 @@ impl OutputType {
|
||||
}
|
||||
|
||||
/// Try to launch the pager. Fall back to stdout in case of errors.
|
||||
#[cfg(feature = "paging")]
|
||||
fn try_pager(quit_if_one_screen: bool, pager_from_config: Option<&str>) -> Result<Self> {
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
let mut replace_arguments_to_less = false;
|
||||
|
||||
let pager_from_env = match (env::var("BAT_PAGER"), env::var("PAGER")) {
|
||||
@@ -80,11 +86,16 @@ impl OutputType {
|
||||
// versions of 'less'. Unfortunately, it also breaks mouse-wheel support.
|
||||
//
|
||||
// See: http://www.greenwoodsoftware.com/less/news.530.html
|
||||
//
|
||||
// For newer versions (530 or 558 on Windows), we omit '--no-init' as it
|
||||
// is not needed anymore.
|
||||
match retrieve_less_version() {
|
||||
None => {
|
||||
p.arg("--no-init");
|
||||
}
|
||||
Some(version) if version < 530 => {
|
||||
Some(version)
|
||||
if (version < 530 || (cfg!(windows) && version < 558)) =>
|
||||
{
|
||||
p.arg("--no-init");
|
||||
}
|
||||
_ => {}
|
||||
@@ -110,12 +121,27 @@ impl OutputType {
|
||||
}
|
||||
}
|
||||
|
||||
fn stdout() -> Self {
|
||||
pub(crate) fn stdout() -> Self {
|
||||
OutputType::Stdout(io::stdout())
|
||||
}
|
||||
|
||||
#[cfg(feature = "paging")]
|
||||
pub(crate) fn is_pager(&self) -> bool {
|
||||
if let OutputType::Pager(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "paging"))]
|
||||
pub(crate) fn is_pager(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn handle(&mut self) -> Result<&mut dyn Write> {
|
||||
Ok(match *self {
|
||||
#[cfg(feature = "paging")]
|
||||
OutputType::Pager(ref mut command) => command
|
||||
.stdin
|
||||
.as_mut()
|
||||
@@ -125,6 +151,7 @@ impl OutputType {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "paging")]
|
||||
impl Drop for OutputType {
|
||||
fn drop(&mut self) {
|
||||
if let OutputType::Pager(ref mut command) = *self {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum PagingMode {
|
||||
Always,
|
||||
QuitIfOneScreen,
|
||||
Never,
|
||||
}
|
||||
|
||||
impl Default for PagingMode {
|
||||
fn default() -> Self {
|
||||
PagingMode::Never
|
||||
}
|
||||
}
|
||||
+8
-8
@@ -36,15 +36,15 @@ pub fn expand_tabs(line: &str, width: usize, cursor: &mut usize) -> String {
|
||||
fn try_parse_utf8_char(input: &[u8]) -> Option<(char, usize)> {
|
||||
let str_from_utf8 = |seq| std::str::from_utf8(seq).ok();
|
||||
|
||||
let decoded = None
|
||||
.or(input.get(0..1).and_then(str_from_utf8).map(|c| (c, 1)))
|
||||
.or(input.get(0..2).and_then(str_from_utf8).map(|c| (c, 2)))
|
||||
.or(input.get(0..3).and_then(str_from_utf8).map(|c| (c, 3)))
|
||||
.or(input.get(0..4).and_then(str_from_utf8).map(|c| (c, 4)));
|
||||
let decoded = input
|
||||
.get(0..1)
|
||||
.and_then(str_from_utf8)
|
||||
.map(|c| (c, 1))
|
||||
.or_else(|| input.get(0..2).and_then(str_from_utf8).map(|c| (c, 2)))
|
||||
.or_else(|| input.get(0..3).and_then(str_from_utf8).map(|c| (c, 3)))
|
||||
.or_else(|| input.get(0..4).and_then(str_from_utf8).map(|c| (c, 4)));
|
||||
|
||||
let decoded_char = decoded.map(|(seq, n)| (seq.chars().next().unwrap(), n));
|
||||
|
||||
decoded_char
|
||||
decoded.map(|(seq, n)| (seq.chars().next().unwrap(), n))
|
||||
}
|
||||
|
||||
pub fn replace_nonprintable(input: &[u8], tab_width: usize) -> String {
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
use std::ffi::OsStr;
|
||||
use std::io::Read;
|
||||
|
||||
use console::Term;
|
||||
use syntect::parsing::SyntaxReference;
|
||||
|
||||
use crate::{
|
||||
assets::HighlightingAssets,
|
||||
config::{Config, VisibleLines},
|
||||
controller::Controller,
|
||||
error::Result,
|
||||
input::Input,
|
||||
line_range::{HighlightedLineRanges, LineRange, LineRanges},
|
||||
style::{StyleComponent, StyleComponents},
|
||||
SyntaxMapping, WrappingMode,
|
||||
};
|
||||
|
||||
#[cfg(feature = "paging")]
|
||||
use crate::paging::PagingMode;
|
||||
|
||||
#[derive(Default)]
|
||||
struct ActiveStyleComponents {
|
||||
header: bool,
|
||||
vcs_modification_markers: bool,
|
||||
grid: bool,
|
||||
line_numbers: bool,
|
||||
snip: bool,
|
||||
}
|
||||
|
||||
pub struct PrettyPrinter<'a> {
|
||||
inputs: Vec<Input<'a>>,
|
||||
config: Config<'a>,
|
||||
assets: HighlightingAssets,
|
||||
|
||||
highlighted_lines: Vec<LineRange>,
|
||||
term_width: Option<usize>,
|
||||
active_style_components: ActiveStyleComponents,
|
||||
}
|
||||
|
||||
impl<'a> PrettyPrinter<'a> {
|
||||
pub fn new() -> Self {
|
||||
let mut config = Config::default();
|
||||
|
||||
config.colored_output = true;
|
||||
config.true_color = true;
|
||||
|
||||
PrettyPrinter {
|
||||
inputs: vec![],
|
||||
config,
|
||||
assets: HighlightingAssets::from_binary(),
|
||||
|
||||
highlighted_lines: vec![],
|
||||
term_width: None,
|
||||
active_style_components: ActiveStyleComponents::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a file which should be pretty-printed
|
||||
pub fn input_file(&mut self, path: impl AsRef<OsStr>) -> &mut Self {
|
||||
self.inputs.push(Input::ordinary_file(path.as_ref()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add multiple files which should be pretty-printed
|
||||
pub fn input_files<I, P>(&mut self, paths: I) -> &mut Self
|
||||
where
|
||||
I: IntoIterator<Item = P>,
|
||||
P: AsRef<OsStr>,
|
||||
{
|
||||
for path in paths {
|
||||
self.inputs.push(Input::ordinary_file(path.as_ref()));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Add STDIN as an input
|
||||
pub fn input_stdin(&mut self) -> &mut Self {
|
||||
self.inputs.push(Input::stdin());
|
||||
self
|
||||
}
|
||||
|
||||
/// Add STDIN as an input (with customized name)
|
||||
pub fn input_stdin_with_name(&mut self, name: impl AsRef<OsStr>) -> &mut Self {
|
||||
self.inputs
|
||||
.push(Input::stdin().with_name(Some(name.as_ref())));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a byte string as an input
|
||||
pub fn input_from_bytes(&mut self, content: &'a [u8]) -> &mut Self {
|
||||
self.input_from_reader(content)
|
||||
}
|
||||
|
||||
/// Add a byte string as an input (with customized name)
|
||||
pub fn input_from_bytes_with_name(
|
||||
&mut self,
|
||||
content: &'a [u8],
|
||||
name: impl AsRef<OsStr>,
|
||||
) -> &mut Self {
|
||||
self.input_from_reader_with_name(content, name)
|
||||
}
|
||||
|
||||
/// Add a custom reader as an input
|
||||
pub fn input_from_reader<R: Read + 'a>(&mut self, reader: R) -> &mut Self {
|
||||
self.inputs.push(Input::from_reader(Box::new(reader)));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a custom reader as an input (with customized name)
|
||||
pub fn input_from_reader_with_name<R: Read + 'a>(
|
||||
&mut self,
|
||||
reader: R,
|
||||
name: impl AsRef<OsStr>,
|
||||
) -> &mut Self {
|
||||
self.inputs
|
||||
.push(Input::from_reader(Box::new(reader)).with_name(Some(name.as_ref())));
|
||||
self
|
||||
}
|
||||
|
||||
/// Specify the syntax file which should be used (default: auto-detect)
|
||||
pub fn language(&mut self, language: &'a str) -> &mut Self {
|
||||
self.config.language = Some(language);
|
||||
self
|
||||
}
|
||||
|
||||
/// The character width of the terminal (default: autodetect)
|
||||
pub fn term_width(&mut self, width: usize) -> &mut Self {
|
||||
self.term_width = Some(width);
|
||||
self
|
||||
}
|
||||
|
||||
/// The width of tab characters (default: None - do not turn tabs to spaces)
|
||||
pub fn tab_width(&mut self, tab_width: Option<usize>) -> &mut Self {
|
||||
self.config.tab_width = tab_width.unwrap_or(0);
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether or not the output should be colorized (default: true)
|
||||
pub fn colored_output(&mut self, yes: bool) -> &mut Self {
|
||||
self.config.colored_output = yes;
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether or not to output 24bit colors (default: true)
|
||||
pub fn true_color(&mut self, yes: bool) -> &mut Self {
|
||||
self.config.true_color = yes;
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether to show a header with the file name
|
||||
pub fn header(&mut self, yes: bool) -> &mut Self {
|
||||
self.active_style_components.header = yes;
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether to show line numbers
|
||||
pub fn line_numbers(&mut self, yes: bool) -> &mut Self {
|
||||
self.active_style_components.line_numbers = yes;
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether to paint a grid, separating line numbers, git changes and the code
|
||||
pub fn grid(&mut self, yes: bool) -> &mut Self {
|
||||
self.active_style_components.grid = yes;
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether to show modification markers for VCS changes
|
||||
pub fn vcs_modification_markers(&mut self, yes: bool) -> &mut Self {
|
||||
self.active_style_components.vcs_modification_markers = yes;
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether to show "snip" markers between visible line ranges (default: no)
|
||||
pub fn snip(&mut self, yes: bool) -> &mut Self {
|
||||
self.active_style_components.snip = yes;
|
||||
self
|
||||
}
|
||||
|
||||
/// Text wrapping mode (default: do not wrap)
|
||||
pub fn wrapping_mode(&mut self, mode: WrappingMode) -> &mut Self {
|
||||
self.config.wrapping_mode = mode;
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether or not to use ANSI italics (default: off)
|
||||
pub fn use_italics(&mut self, yes: bool) -> &mut Self {
|
||||
self.config.use_italic_text = yes;
|
||||
self
|
||||
}
|
||||
|
||||
/// If and how to use a pager (default: no paging)
|
||||
#[cfg(feature = "paging")]
|
||||
pub fn paging_mode(&mut self, mode: PagingMode) -> &mut Self {
|
||||
self.config.paging_mode = mode;
|
||||
self
|
||||
}
|
||||
|
||||
/// Specify the command to start the pager (default: use "less")
|
||||
#[cfg(feature = "paging")]
|
||||
pub fn pager(&mut self, cmd: &'a str) -> &mut Self {
|
||||
self.config.pager = Some(cmd);
|
||||
self
|
||||
}
|
||||
|
||||
/// Specify the lines that should be printed (default: all)
|
||||
pub fn line_ranges(&mut self, ranges: LineRanges) -> &mut Self {
|
||||
self.config.visible_lines = VisibleLines::Ranges(ranges);
|
||||
self
|
||||
}
|
||||
|
||||
/// Specify a line that should be highlighted (default: none).
|
||||
/// This can be called multiple times to highlight more than one
|
||||
/// line. See also: highlight_range.
|
||||
pub fn highlight(&mut self, line: usize) -> &mut Self {
|
||||
self.highlighted_lines.push(LineRange::new(line, line));
|
||||
self
|
||||
}
|
||||
|
||||
/// Specify a range of lines that should be highlighted (default: none).
|
||||
/// This can be called multiple times to highlight more than one range
|
||||
/// of lines.
|
||||
pub fn highlight_range(&mut self, from: usize, to: usize) -> &mut Self {
|
||||
self.highlighted_lines.push(LineRange::new(from, to));
|
||||
self
|
||||
}
|
||||
|
||||
/// Specify the highlighting theme
|
||||
pub fn theme(&mut self, theme: impl AsRef<str>) -> &mut Self {
|
||||
self.config.theme = theme.as_ref().to_owned();
|
||||
self
|
||||
}
|
||||
|
||||
/// Specify custom file extension / file name to syntax mappings
|
||||
pub fn syntax_mapping(&mut self, mapping: SyntaxMapping<'a>) -> &mut Self {
|
||||
self.config.syntax_mapping = mapping;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn themes(&self) -> impl Iterator<Item = &str> {
|
||||
self.assets.themes()
|
||||
}
|
||||
|
||||
pub fn syntaxes(&self) -> impl Iterator<Item = &SyntaxReference> {
|
||||
self.assets.syntaxes().iter()
|
||||
}
|
||||
|
||||
/// Pretty-print all specified inputs. This method will "use" all stored inputs.
|
||||
/// If you want to call 'print' multiple times, you have to call the appropriate
|
||||
/// input_* methods again.
|
||||
pub fn print(&mut self) -> Result<bool> {
|
||||
self.config.highlighted_lines =
|
||||
HighlightedLineRanges(LineRanges::from(self.highlighted_lines.clone()));
|
||||
self.config.term_width = self
|
||||
.term_width
|
||||
.unwrap_or_else(|| Term::stdout().size().1 as usize);
|
||||
|
||||
let mut style_components = vec![];
|
||||
if self.active_style_components.grid {
|
||||
style_components.push(StyleComponent::Grid);
|
||||
}
|
||||
if self.active_style_components.header {
|
||||
style_components.push(StyleComponent::Header);
|
||||
}
|
||||
if self.active_style_components.line_numbers {
|
||||
style_components.push(StyleComponent::LineNumbers);
|
||||
}
|
||||
if self.active_style_components.snip {
|
||||
style_components.push(StyleComponent::Snip);
|
||||
}
|
||||
if self.active_style_components.vcs_modification_markers {
|
||||
style_components.push(StyleComponent::Changes);
|
||||
}
|
||||
self.config.style_components = StyleComponents::new(&style_components);
|
||||
|
||||
let mut inputs: Vec<Input> = vec![];
|
||||
std::mem::swap(&mut inputs, &mut self.inputs);
|
||||
|
||||
let controller = Controller::new(&self.config, &self.assets);
|
||||
controller.run(inputs)
|
||||
}
|
||||
}
|
||||
+43
-60
@@ -1,4 +1,3 @@
|
||||
use std::borrow::Cow;
|
||||
use std::io::Write;
|
||||
use std::vec::Vec;
|
||||
|
||||
@@ -21,21 +20,21 @@ use unicode_width::UnicodeWidthChar;
|
||||
|
||||
use crate::assets::HighlightingAssets;
|
||||
use crate::config::Config;
|
||||
use crate::decorations::{
|
||||
Decoration, GridBorderDecoration, LineChangesDecoration, LineNumberDecoration,
|
||||
};
|
||||
use crate::diff::get_git_diff;
|
||||
#[cfg(feature = "git")]
|
||||
use crate::decorations::LineChangesDecoration;
|
||||
use crate::decorations::{Decoration, GridBorderDecoration, LineNumberDecoration};
|
||||
#[cfg(feature = "git")]
|
||||
use crate::diff::LineChanges;
|
||||
use crate::errors::*;
|
||||
use crate::inputfile::{InputFile, InputFileReader};
|
||||
use crate::error::*;
|
||||
use crate::input::OpenedInput;
|
||||
use crate::line_range::RangeCheckResult;
|
||||
use crate::preprocessor::{expand_tabs, replace_nonprintable};
|
||||
use crate::terminal::{as_terminal_escaped, to_ansi_color};
|
||||
use crate::wrap::OutputWrap;
|
||||
use crate::wrapping::WrappingMode;
|
||||
|
||||
pub trait Printer {
|
||||
fn print_header(&mut self, handle: &mut dyn Write, file: InputFile) -> Result<()>;
|
||||
fn print_footer(&mut self, handle: &mut dyn Write) -> Result<()>;
|
||||
pub(crate) trait Printer {
|
||||
fn print_header(&mut self, handle: &mut dyn Write, input: &OpenedInput) -> Result<()>;
|
||||
fn print_footer(&mut self, handle: &mut dyn Write, input: &OpenedInput) -> Result<()>;
|
||||
|
||||
fn print_snip(&mut self, handle: &mut dyn Write) -> Result<()>;
|
||||
|
||||
@@ -57,11 +56,11 @@ impl SimplePrinter {
|
||||
}
|
||||
|
||||
impl Printer for SimplePrinter {
|
||||
fn print_header(&mut self, _handle: &mut dyn Write, _file: InputFile) -> Result<()> {
|
||||
fn print_header(&mut self, _handle: &mut dyn Write, _input: &OpenedInput) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_footer(&mut self, _handle: &mut dyn Write) -> Result<()> {
|
||||
fn print_footer(&mut self, _handle: &mut dyn Write, _input: &OpenedInput) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -83,25 +82,27 @@ impl Printer for SimplePrinter {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InteractivePrinter<'a> {
|
||||
pub(crate) struct InteractivePrinter<'a> {
|
||||
colors: Colors,
|
||||
config: &'a Config<'a>,
|
||||
decorations: Vec<Box<dyn Decoration>>,
|
||||
panel_width: usize,
|
||||
ansi_prefix_sgr: String,
|
||||
content_type: Option<ContentType>,
|
||||
pub line_changes: Option<LineChanges>,
|
||||
#[cfg(feature = "git")]
|
||||
pub line_changes: &'a Option<LineChanges>,
|
||||
highlighter: Option<HighlightLines<'a>>,
|
||||
syntax_set: &'a SyntaxSet,
|
||||
background_color_highlight: Option<Color>,
|
||||
}
|
||||
|
||||
impl<'a> InteractivePrinter<'a> {
|
||||
pub fn new(
|
||||
pub(crate) fn new(
|
||||
config: &'a Config,
|
||||
assets: &'a HighlightingAssets,
|
||||
file: InputFile,
|
||||
reader: &mut InputFileReader,
|
||||
input: &mut OpenedInput,
|
||||
#[cfg(feature = "git")]
|
||||
line_changes: &'a Option<LineChanges>,
|
||||
) -> Self {
|
||||
let theme = assets.get_theme(&config.theme);
|
||||
|
||||
@@ -120,8 +121,11 @@ impl<'a> InteractivePrinter<'a> {
|
||||
decorations.push(Box::new(LineNumberDecoration::new(&colors)));
|
||||
}
|
||||
|
||||
if config.style_components.changes() {
|
||||
decorations.push(Box::new(LineChangesDecoration::new(&colors)));
|
||||
#[cfg(feature = "git")]
|
||||
{
|
||||
if config.style_components.changes() {
|
||||
decorations.push(Box::new(LineChangesDecoration::new(&colors)));
|
||||
}
|
||||
}
|
||||
|
||||
let mut panel_width: usize =
|
||||
@@ -143,26 +147,15 @@ impl<'a> InteractivePrinter<'a> {
|
||||
panel_width = 0;
|
||||
}
|
||||
|
||||
let mut line_changes = None;
|
||||
|
||||
let highlighter = if reader
|
||||
let highlighter = if input
|
||||
.reader
|
||||
.content_type
|
||||
.map_or(false, |c| c.is_binary() && !config.show_nonprintable)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
// Get the Git modifications
|
||||
line_changes = if config.style_components.changes() {
|
||||
match file {
|
||||
InputFile::Ordinary(filename) => get_git_diff(filename),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Determine the type of syntax for highlighting
|
||||
let syntax = assets.get_syntax(config.language, file, reader, &config.syntax_mapping);
|
||||
let syntax = assets.get_syntax(config.language, input, &config.syntax_mapping);
|
||||
Some(HighlightLines::new(syntax, theme))
|
||||
};
|
||||
|
||||
@@ -171,8 +164,9 @@ impl<'a> InteractivePrinter<'a> {
|
||||
colors,
|
||||
config,
|
||||
decorations,
|
||||
content_type: reader.content_type,
|
||||
content_type: input.reader.content_type,
|
||||
ansi_prefix_sgr: String::new(),
|
||||
#[cfg(feature = "git")]
|
||||
line_changes,
|
||||
highlighter,
|
||||
syntax_set: &assets.syntax_set,
|
||||
@@ -209,7 +203,7 @@ impl<'a> InteractivePrinter<'a> {
|
||||
if self.config.style_components.grid() {
|
||||
format!("{} │ ", text_filled)
|
||||
} else {
|
||||
format!("{}", text_filled)
|
||||
text_filled
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,34 +212,26 @@ impl<'a> InteractivePrinter<'a> {
|
||||
if self.config.tab_width > 0 {
|
||||
expand_tabs(text, self.config.tab_width, cursor)
|
||||
} else {
|
||||
*cursor += text.len();
|
||||
text.to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Printer for InteractivePrinter<'a> {
|
||||
fn print_header(&mut self, handle: &mut dyn Write, file: InputFile) -> Result<()> {
|
||||
fn print_header(&mut self, handle: &mut dyn Write, input: &OpenedInput) -> Result<()> {
|
||||
if !self.config.style_components.header() {
|
||||
if Some(ContentType::BINARY) == self.content_type && !self.config.show_nonprintable {
|
||||
let input = match file {
|
||||
InputFile::Ordinary(filename) => {
|
||||
format!("file '{}'", filename.to_string_lossy())
|
||||
}
|
||||
_ => "STDIN".into(),
|
||||
};
|
||||
|
||||
writeln!(
|
||||
handle,
|
||||
"{}: Binary content from {} will not be printed to the terminal \
|
||||
(but will be present if the output of 'bat' is piped). You can use 'bat -A' \
|
||||
to show the binary file contents.",
|
||||
Yellow.paint("[bat warning]"),
|
||||
input
|
||||
input.description().full,
|
||||
)?;
|
||||
} else {
|
||||
if self.config.style_components.grid() {
|
||||
self.print_horizontal_line(handle, '┬')?;
|
||||
}
|
||||
} else if self.config.style_components.grid() {
|
||||
self.print_horizontal_line(handle, '┬')?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
@@ -265,11 +251,6 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
||||
write!(handle, "{}", " ".repeat(self.panel_width))?;
|
||||
}
|
||||
|
||||
let (prefix, name) = match file {
|
||||
InputFile::Ordinary(filename) => ("File: ", filename.to_string_lossy()),
|
||||
_ => ("", Cow::from("STDIN")),
|
||||
};
|
||||
|
||||
let mode = match self.content_type {
|
||||
Some(ContentType::BINARY) => " <BINARY>",
|
||||
Some(ContentType::UTF_16LE) => " <UTF-16LE>",
|
||||
@@ -278,11 +259,13 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
||||
_ => "",
|
||||
};
|
||||
|
||||
let description = input.description();
|
||||
|
||||
writeln!(
|
||||
handle,
|
||||
"{}{}{}",
|
||||
prefix,
|
||||
self.colors.filename.paint(name),
|
||||
description.prefix,
|
||||
self.colors.filename.paint(&description.name),
|
||||
mode
|
||||
)?;
|
||||
|
||||
@@ -297,7 +280,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_footer(&mut self, handle: &mut dyn Write) -> Result<()> {
|
||||
fn print_footer(&mut self, handle: &mut dyn Write, _input: &OpenedInput) -> Result<()> {
|
||||
if self.config.style_components.grid()
|
||||
&& (self.content_type.map_or(false, |c| c.is_text()) || self.config.show_nonprintable)
|
||||
{
|
||||
@@ -320,9 +303,9 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
||||
let snip_right =
|
||||
" ─".repeat((self.config.term_width - panel_count - snip_left_count - title_count) / 2);
|
||||
|
||||
write!(
|
||||
writeln!(
|
||||
handle,
|
||||
"{}\n",
|
||||
"{}",
|
||||
self.colors
|
||||
.grid
|
||||
.paint(format!("{}{}{}{}", panel, snip_left, title, snip_right))
|
||||
@@ -397,7 +380,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
||||
}
|
||||
|
||||
// Line contents.
|
||||
if self.config.output_wrap == OutputWrap::None {
|
||||
if self.config.wrapping_mode == WrappingMode::NoWrapping {
|
||||
let true_color = self.config.true_color;
|
||||
let colored_output = self.config.colored_output;
|
||||
let italics = self.config.use_italic_text;
|
||||
|
||||
+7
-6
@@ -1,7 +1,7 @@
|
||||
use std::collections::HashSet;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::errors::*;
|
||||
use crate::error::*;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
|
||||
pub enum StyleComponent {
|
||||
@@ -9,7 +9,7 @@ pub enum StyleComponent {
|
||||
Changes,
|
||||
Grid,
|
||||
Header,
|
||||
Numbers,
|
||||
LineNumbers,
|
||||
Snip,
|
||||
Full,
|
||||
Plain,
|
||||
@@ -28,13 +28,13 @@ impl StyleComponent {
|
||||
StyleComponent::Changes => &[StyleComponent::Changes],
|
||||
StyleComponent::Grid => &[StyleComponent::Grid],
|
||||
StyleComponent::Header => &[StyleComponent::Header],
|
||||
StyleComponent::Numbers => &[StyleComponent::Numbers],
|
||||
StyleComponent::LineNumbers => &[StyleComponent::LineNumbers],
|
||||
StyleComponent::Snip => &[StyleComponent::Snip],
|
||||
StyleComponent::Full => &[
|
||||
StyleComponent::Changes,
|
||||
StyleComponent::Grid,
|
||||
StyleComponent::Header,
|
||||
StyleComponent::Numbers,
|
||||
StyleComponent::LineNumbers,
|
||||
StyleComponent::Snip,
|
||||
],
|
||||
StyleComponent::Plain => &[],
|
||||
@@ -51,7 +51,7 @@ impl FromStr for StyleComponent {
|
||||
"changes" => Ok(StyleComponent::Changes),
|
||||
"grid" => Ok(StyleComponent::Grid),
|
||||
"header" => Ok(StyleComponent::Header),
|
||||
"numbers" => Ok(StyleComponent::Numbers),
|
||||
"numbers" => Ok(StyleComponent::LineNumbers),
|
||||
"snip" => Ok(StyleComponent::Snip),
|
||||
"full" => Ok(StyleComponent::Full),
|
||||
"plain" => Ok(StyleComponent::Plain),
|
||||
@@ -68,6 +68,7 @@ impl StyleComponents {
|
||||
StyleComponents(components.iter().cloned().collect())
|
||||
}
|
||||
|
||||
#[cfg(feature = "git")]
|
||||
pub fn changes(&self) -> bool {
|
||||
self.0.contains(&StyleComponent::Changes)
|
||||
}
|
||||
@@ -81,7 +82,7 @@ impl StyleComponents {
|
||||
}
|
||||
|
||||
pub fn numbers(&self) -> bool {
|
||||
self.0.contains(&StyleComponent::Numbers)
|
||||
self.0.contains(&StyleComponent::LineNumbers)
|
||||
}
|
||||
|
||||
pub fn snip(&self) -> bool {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::path::Path;
|
||||
|
||||
use crate::errors::Result;
|
||||
use crate::error::Result;
|
||||
|
||||
use globset::{Candidate, GlobBuilder, GlobMatcher};
|
||||
|
||||
@@ -29,6 +29,12 @@ impl<'a> SyntaxMapping<'a> {
|
||||
mapping
|
||||
.insert("**/.ssh/config", MappingTarget::MapTo("SSH Config"))
|
||||
.unwrap();
|
||||
mapping
|
||||
.insert(
|
||||
"**/bat/config",
|
||||
MappingTarget::MapTo("Bourne Again Shell (bash)"),
|
||||
)
|
||||
.unwrap();
|
||||
mapping
|
||||
.insert(
|
||||
"/etc/profile",
|
||||
|
||||
+26
-6
@@ -1,17 +1,37 @@
|
||||
extern crate ansi_colours;
|
||||
|
||||
use ansi_term::Colour::{Fixed, RGB};
|
||||
use ansi_term::Color::{self, Fixed, RGB};
|
||||
use ansi_term::{self, Style};
|
||||
|
||||
use syntect::highlighting::{self, FontStyle};
|
||||
|
||||
pub fn to_ansi_color(color: highlighting::Color, true_color: bool) -> ansi_term::Colour {
|
||||
pub fn to_ansi_color(color: highlighting::Color, true_color: bool) -> ansi_term::Color {
|
||||
if color.a == 0 {
|
||||
// Themes can specify one of the user-configurable terminal colors by
|
||||
// encoding them as #RRGGBBAA with AA set to 00 (transparent) and RR set
|
||||
// to the color palette number. The built-in themes ansi-light,
|
||||
// to the 8-bit color palette number. The built-in themes ansi-light,
|
||||
// ansi-dark, and base16 use this.
|
||||
Fixed(color.r)
|
||||
match color.r {
|
||||
// For the first 8 colors, use the Color enum to produce ANSI escape
|
||||
// sequences using codes 30-37 (foreground) and 40-47 (background).
|
||||
// For example, red foreground is \x1b[31m. This works on terminals
|
||||
// without 256-color support.
|
||||
0x00 => Color::Black,
|
||||
0x01 => Color::Red,
|
||||
0x02 => Color::Green,
|
||||
0x03 => Color::Yellow,
|
||||
0x04 => Color::Blue,
|
||||
0x05 => Color::Purple,
|
||||
0x06 => Color::Cyan,
|
||||
0x07 => Color::White,
|
||||
// For all other colors, use Fixed to produce escape sequences using
|
||||
// codes 38;5 (foreground) and 48;5 (background). For example,
|
||||
// bright red foreground is \x1b[38;5;9m. This only works on
|
||||
// terminals with 256-color support.
|
||||
//
|
||||
// TODO: When ansi_term adds support for bright variants using codes
|
||||
// 90-97 (foreground) and 100-107 (background), we should use those
|
||||
// for values 0x08 to 0x0f and only use Fixed for 0x10 to 0xff.
|
||||
n => Fixed(n),
|
||||
}
|
||||
} else if true_color {
|
||||
RGB(color.r, color.g, color.b)
|
||||
} else {
|
||||
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
|
||||
pub enum OutputWrap {
|
||||
Character,
|
||||
None,
|
||||
}
|
||||
|
||||
impl Default for OutputWrap {
|
||||
fn default() -> Self {
|
||||
OutputWrap::None
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum WrappingMode {
|
||||
Character,
|
||||
NoWrapping,
|
||||
}
|
||||
|
||||
impl Default for WrappingMode {
|
||||
fn default() -> Self {
|
||||
WrappingMode::NoWrapping
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
```javascript
|
||||
var test = "boom";
|
||||
```
|
||||
@@ -0,0 +1,4 @@
|
||||
Test
|
||||
|
||||
<div>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
module Main where
|
||||
|
||||
main :: IO ()
|
||||
main = putStrLn "Please show my file :c"
|
||||
@@ -0,0 +1,3 @@
|
||||
<<END_DESC
|
||||
Test
|
||||
END_DESC
|
||||
@@ -0,0 +1,2 @@
|
||||
<style lang="stylus">
|
||||
</style>
|
||||
Vendored
BIN
Binary file not shown.
@@ -541,3 +541,132 @@ fn empty_file_leads_to_empty_output_with_grid_enabled() {
|
||||
.success()
|
||||
.stdout("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filename_basic() {
|
||||
bat()
|
||||
.arg("test.txt")
|
||||
.arg("--decorations=always")
|
||||
.arg("--style=header")
|
||||
.arg("-r=0:0")
|
||||
.arg("--file-name=foo")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("File: foo\n")
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filename_binary() {
|
||||
bat()
|
||||
.arg("test.binary")
|
||||
.arg("--decorations=always")
|
||||
.arg("--style=header")
|
||||
.arg("-r=0:0")
|
||||
.arg("--file-name=foo")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("File: foo <BINARY>\n")
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filename_stdin() {
|
||||
bat()
|
||||
.arg("--decorations=always")
|
||||
.arg("--style=header")
|
||||
.arg("-r=0:0")
|
||||
.arg("-")
|
||||
.write_stdin("stdin\n")
|
||||
.arg("--file-name=foo")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("File: foo\n")
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filename_stdin_binary() {
|
||||
let vec = vec![0; 1];
|
||||
bat_with_config()
|
||||
.arg("--decorations=always")
|
||||
.arg("--style=header")
|
||||
.write_stdin(vec)
|
||||
.arg("--file-name=foo")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("File: foo <BINARY>\n")
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filename_multiple_ok() {
|
||||
bat()
|
||||
.arg("--decorations=always")
|
||||
.arg("--style=header")
|
||||
.arg("-r=0:0")
|
||||
.arg("test.txt")
|
||||
.arg("--file-name=foo")
|
||||
.arg("single-line.txt")
|
||||
.arg("--file-name=bar")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("File: foo\nFile: bar\n")
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filename_multiple_err() {
|
||||
bat()
|
||||
.arg("--decorations=always")
|
||||
.arg("--style=header")
|
||||
.arg("-r=0:0")
|
||||
.arg("test.txt")
|
||||
.arg("--file-name=foo")
|
||||
.arg("single-line.txt")
|
||||
.assert()
|
||||
.failure();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn file_with_invalid_utf8_filename() {
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
use tempdir::TempDir;
|
||||
|
||||
let tmp_dir = TempDir::new("bat_test").expect("can create temporary directory");
|
||||
let file_path = tmp_dir
|
||||
.path()
|
||||
.join(OsStr::from_bytes(b"test-invalid-utf8-\xC3(.rs"));
|
||||
{
|
||||
let mut file = File::create(&file_path).expect("can create temporary file");
|
||||
writeln!(file, "dummy content").expect("can write to file");
|
||||
}
|
||||
|
||||
bat()
|
||||
.arg(file_path.as_os_str())
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("dummy content\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn do_not_panic_regression_tests() {
|
||||
for filename in &[
|
||||
"issue_28.md",
|
||||
"issue_190.md",
|
||||
"issue_314.hs",
|
||||
"issue_914.rb",
|
||||
"issue_915.vue",
|
||||
] {
|
||||
bat()
|
||||
.arg("--color=always")
|
||||
.arg(&format!("regression_tests/{}", filename))
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use bat::HighlightingAssets;
|
||||
use bat::assets::HighlightingAssets;
|
||||
|
||||
#[test]
|
||||
fn no_duplicate_extensions() {
|
||||
const KNOWN_EXCEPTIONS: &[&'static str] = &[
|
||||
const KNOWN_EXCEPTIONS: &[&str] = &[
|
||||
// The '.h' extension currently appears in multiple syntaxes: C, C++, Objective C,
|
||||
// Objective C++
|
||||
"h",
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# This script goes through all languages that are supported by 'bat'. For each
|
||||
# language, it loops over the correspoinding file extensions and searches a
|
||||
# given folder for matching files. It calls 'bat' for each of these files and
|
||||
# measures the highlighting speed (number of characters per second). The script
|
||||
# reports files which lead to slow highlighting speeds or errors during the
|
||||
# execution of 'bat'.
|
||||
#
|
||||
# Requirements (external programs):
|
||||
# - bat (in the $PATH)
|
||||
# - fd (https://github.com/sharkdp/fd)
|
||||
# - wc
|
||||
|
||||
import sys
|
||||
import time
|
||||
import os
|
||||
import subprocess as sp
|
||||
|
||||
|
||||
# Threshold speed, characters per second
|
||||
THRESHOLD_SPEED = 20000
|
||||
|
||||
# Maximum time we allow `bat` to run
|
||||
BAT_TIMEOUT_SEC = 10
|
||||
|
||||
# Maximum number of files to measure
|
||||
MAX_NUM_FILES = 100
|
||||
|
||||
# Root folder for the search
|
||||
SEARCH_ROOT = os.getenv("HOME")
|
||||
|
||||
|
||||
def find_slow_files(startup_time, glob_pattern, language=None):
|
||||
out = sp.check_output(
|
||||
[
|
||||
"fd",
|
||||
"--hidden",
|
||||
"--no-ignore",
|
||||
"--type=file",
|
||||
"--max-results",
|
||||
str(MAX_NUM_FILES),
|
||||
"--glob",
|
||||
glob_pattern,
|
||||
SEARCH_ROOT,
|
||||
]
|
||||
)
|
||||
|
||||
paths = out.split(b"\n")[:-1]
|
||||
language_text = f"Language {language}, " if language else ""
|
||||
print(f"{language_text}glob pattern: {glob_pattern} ({len(paths)} matches)")
|
||||
|
||||
for path in paths:
|
||||
num_chars = int(sp.check_output(["wc", "-c", path]).split(b" ")[0].decode())
|
||||
|
||||
if num_chars < 500:
|
||||
# It is hard to measure the exact speed for short files
|
||||
continue
|
||||
|
||||
try:
|
||||
start = time.time()
|
||||
sp.check_output(["bat", "--color=always", path], timeout=BAT_TIMEOUT_SEC)
|
||||
duration = time.time() - start - startup_time
|
||||
|
||||
if duration <= 0:
|
||||
continue
|
||||
|
||||
highlighting_speed = num_chars / duration
|
||||
|
||||
if highlighting_speed < THRESHOLD_SPEED:
|
||||
print(f" {highlighting_speed:10.0f} chars/s: {path.decode()}")
|
||||
|
||||
except sp.CalledProcessError:
|
||||
print(f" Error while highlighting file '{path.decode()}'.")
|
||||
|
||||
except sp.TimeoutExpired:
|
||||
if num_chars < THRESHOLD_SPEED * BAT_TIMEOUT_SEC:
|
||||
print(f" Error: bat timed out on file '{path.decode()}'.")
|
||||
else:
|
||||
print(
|
||||
f" Warning: bat timed out on file '{path.decode()} (but the file is large)."
|
||||
)
|
||||
|
||||
|
||||
def measure_bat_startup_speed():
|
||||
min_duration = None
|
||||
for _ in range(20):
|
||||
start = time.time()
|
||||
p = sp.Popen(
|
||||
["bat", "--color=always", "--language=py"], stdin=sp.PIPE, stdout=sp.PIPE
|
||||
)
|
||||
p.communicate(input=b"test")
|
||||
duration = time.time() - start
|
||||
|
||||
if not min_duration or duration < min_duration:
|
||||
min_duration = duration
|
||||
|
||||
return min_duration
|
||||
|
||||
|
||||
def traverse_all_languages(startup_time):
|
||||
output = sp.check_output(["bat", "--list-languages"]).decode()
|
||||
|
||||
for line in output.strip().split("\n"):
|
||||
language, extensions = line.split(":")
|
||||
for ext in extensions.split(","):
|
||||
find_slow_files(startup_time, ext, language)
|
||||
if not ext.startswith("."):
|
||||
find_slow_files(startup_time, f"*.{ext}", language)
|
||||
|
||||
|
||||
def main():
|
||||
print("Measuring 'bat' startup speed ... ", flush=True, end="")
|
||||
startup_time = measure_bat_startup_speed()
|
||||
print(f"{startup_time * 1000:.1f} ms")
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
traverse_all_languages(startup_time)
|
||||
else:
|
||||
pattern = sys.argv[1]
|
||||
find_slow_files(startup_time, pattern)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+4
-6
@@ -4,13 +4,11 @@ use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
extern crate tempdir;
|
||||
use self::tempdir::TempDir;
|
||||
use tempdir::TempDir;
|
||||
|
||||
extern crate git2;
|
||||
use self::git2::build::CheckoutBuilder;
|
||||
use self::git2::Repository;
|
||||
use self::git2::Signature;
|
||||
use git2::build::CheckoutBuilder;
|
||||
use git2::Repository;
|
||||
use git2::Signature;
|
||||
|
||||
pub struct BatTester {
|
||||
/// Temporary working directory
|
||||
|
||||
Reference in New Issue
Block a user