1
0
mirror of https://github.com/sharkdp/bat synced 2026-08-04 19:01:44 +00:00

Compare commits

...

163 Commits

Author SHA1 Message Date
sharkdp 732750c274 Bump version 2018-09-12 22:03:48 +02:00
sharkdp 6b6baa3118 Update dependencies 2018-09-12 22:01:27 +02:00
sharkdp 54c434934e Bump version, update assets 2018-09-12 22:01:01 +02:00
sharkdp ad6e0a2581 Update man page 2018-09-12 22:00:28 +02:00
sharkdp bd619db3ca Mention BAT_STYLE in README.md 2018-09-12 21:57:54 +02:00
sharkdp e098eb43a2 Do not show file header for directories
This also adds a better error message.

closes #292
2018-09-12 21:53:41 +02:00
David Peter ea369ee17f Merge pull request #298 from ms2300/bat_style
#208 Added BAT_STYLE env variable functionality
2018-09-12 21:48:34 +02:00
sharkdp 63d32bc818 Merge remote-tracking branch 'upstream/master' into bat_style 2018-09-12 21:40:41 +02:00
sharkdp dccf8d8221 Use a more streamlined version of style-component collection 2018-09-12 21:35:23 +02:00
sharkdp 97129ab9d8 Add PowerShell syntax, closes #306 2018-09-12 20:53:49 +02:00
David Peter 52d0d6ca30 Merge pull request #302 from eth-p/feature-tabs
Tab expansion support.

closes #166 
closes #184
2018-09-12 20:50:05 +02:00
sharkdp a05494e9d9 Make tests more robust 2018-09-12 20:32:49 +02:00
sharkdp 84ac92efbb Fix test for --style=plain 2018-09-12 20:32:33 +02:00
sharkdp b4c6e412dc Make generate_snapshots script more robust 2018-09-12 20:28:42 +02:00
sharkdp 3e21d69a92 Re-generate files again 2018-09-12 20:28:30 +02:00
sharkdp b7690d4bb1 Undo change in sample.modified.rs 2018-09-12 20:28:21 +02:00
eth-p d404139ff7 Added validation for --tabs.
Also added a comment to explain what 0 represents.
2018-09-12 10:08:58 -07:00
eth-p 9ef1e9f792 Update snapshots. 2018-09-11 15:04:44 -07:00
eth-p 503fe0b641 Fix sample.modified.rs 2018-09-11 15:03:13 -07:00
eth-p 1807f9653c Merge remote-tracking branch 'upstream/master' into feature-tabs
# Conflicts:
#	src/printer.rs
2018-09-11 14:03:47 -07:00
eth-p 1e74f0e2a9 Ran cargo fmt. 2018-09-11 13:47:35 -07:00
eth-p c1e1f753cf Added three new snapshot tests for --tabs and --wrap. 2018-09-11 13:47:01 -07:00
eth-p d90797f8e9 Fixed tab expansion not working in --wrap=never mode. 2018-09-11 13:45:49 -07:00
eth-p b4096e5627 Moved tab expansion to happen after syntax highlighting. 2018-09-11 13:02:22 -07:00
shik Chen 4e3ab4d399 Query git diff only when needed
In large git repo like chromium, it takes too long time to query git
diff.
2018-09-11 21:09:38 +02:00
rachitchokshi f0d936763c provide armhf deb packages (#301)
* Enabled deb packaging for armhf architecture

closes #280.
2018-09-11 21:03:53 +02:00
ms2300 7897260bf0 BAT_STYLE accounts for multiple styles (ie numbers,header) 2018-09-10 21:58:19 -06:00
eth-p eb6e43b9a9 Disabled tab expansion when decorations and pager are not used. 2018-09-10 20:12:13 -07:00
eth-p 7cdcdbb31d Updated snapshots. 2018-09-10 19:20:09 -07:00
eth-p 9159341714 Modified snapshot tests to support tab expansion.
This changes how the files are named (to allow for snapshots that aren't
directly related to the --style argument) and fixes the
generate_snapshots.py script to work with the latest version of bat.

Three new tests are also introduced:
- tabs_4 - Tab expansion with a width of 4.
- tabs_8 - Tab expansion with a width of 8.
- tabs_passthrough - No tab expansion.
2018-09-10 19:19:43 -07:00
eth-p b23ff24ebc Added tab expansion preprocessing step. 2018-09-10 18:11:59 -07:00
Rogach e1ecc17f69 print additional newline if last line in input file was not terminated with a newline (fixes #299) 2018-09-10 22:18:16 +02:00
rachitchokshi 0fe3badf19 adding deb packaging for arm64 (#1) 2018-09-10 21:24:15 +02:00
rachitchokshi 89d4cb951a Updated before_deploy.bash to call version of strip specific to aarch64 target 2018-09-10 21:24:15 +02:00
rachitchokshi e9681bf22d Testing build by removing dependency 'libz-sys = "1.0.20"' 2018-09-10 21:24:15 +02:00
rachitchokshi 1b6df8a480 Enabled build target aarch64-unknown-linux-gnu for arm64 architecture 2018-09-10 21:24:15 +02:00
ms2300 cf24986edb Run formatting for bat_style changes 2018-09-10 00:07:46 -06:00
ms2300 b9c556a0b6 Updated long help to reflect BAT_STYLE changes 2018-09-09 18:35:40 -06:00
ms2300 9d6bde48fa Added BAT_STYLE env variable functionality 2018-09-09 18:28:04 -06:00
Aankhen d6c8fee044 README.md: Add Using bat on Windows 2018-09-09 12:22:05 +02:00
sharkdp 8b4abb03db Run 'cargo fmt' 2018-09-08 09:52:21 +02:00
eth-p 375cf76e15 Formatted for newest rustfmt. 2018-09-08 09:52:21 +02:00
eth-p 1a4acc435e Typo 2018-09-08 09:52:21 +02:00
eth-p d8030626f8 Automatically disable wrapping when style is plain. 2018-09-08 09:52:21 +02:00
eth-p fb61aa4f60 Wrapping disabled when --plain is used. (#289) 2018-09-08 09:52:21 +02:00
Aankhen 76df41fa97 src/output.rs: Handle less in a portable way. 2018-09-07 21:47:31 +02:00
Miguel Madrid Mencía 97efd75cee Add Crystal syntax 2018-09-05 23:45:09 +02:00
Shreyansh Chouhan 53d0c1deca Can read files named "cache" now, cache subcommand working too (#275)
Now if a cache file exists in the current directory, and the user passes
no arguments to the cache command, the cache file would be displayed.
If however the user uses cache command with arguments, the cache command
would be executed as normal regardless of whether the file cache exists
in the current directory or not.

Though now there won't be an error message displayed if the user uses the cache sub command without arguments in any directory that contains a file named cache.
2018-09-05 23:22:12 +02:00
David Peter 155179a07a Update alternatives.md 2018-09-05 22:01:42 +02:00
sharkdp ba00caf9c3 Add Swift syntax 2018-09-05 21:56:49 +02:00
sharkdp ac418ba21c Add PureScript syntax 2018-09-05 21:50:16 +02:00
sharkdp acde83af31 Add note about 'lesspipe', closes #242 2018-09-05 21:44:53 +02:00
sharkdp 021a68eb21 Add F# syntax, closes #284 2018-09-05 21:38:35 +02:00
David Peter 566aab3b05 Merge pull request #279 from tobenna/add-jsx-syntax
Add support for JSX in javascript and typescript
2018-09-05 21:33:50 +02:00
sharkdp f88eba6476 Add section concerning missing zlib-devel
closes #229
2018-09-05 21:13:43 +02:00
sharkdp 1d9e7ac4c2 Support "(/usr)/bin/less" as PAGER too
closes #271
2018-09-05 21:10:11 +02:00
David Peter 80ae2ecbf8 Merge branch 'master' into add-jsx-syntax 2018-09-05 20:51:03 +02:00
David Peter 96d4492d27 Merge pull request #282 from tobenna/support-cabal-syntax
Syntax highlighting for .cabal
2018-09-05 20:44:25 +02:00
David Peter c90f0b8e21 Merge branch 'master' into support-cabal-syntax 2018-09-05 20:43:50 +02:00
David Peter 4b866ab6af Merge branch 'master' into add-jsx-syntax 2018-09-05 20:36:31 +02:00
Tobenna 2dbd91e543 Add Dart syntax 2018-09-05 20:33:16 +02:00
Tobenna Ndu 785da3d9df Add support for cabal syntax 2018-09-05 14:42:27 +01:00
Tobenna d455cb7c32 Add support for JSX in javascript and typescript 2018-09-05 08:49:33 +01:00
sharkdp d2b4766f92 Small README update 2018-09-04 08:25:24 +02:00
Roald 5cf92b4f94 Windows download with scoop 2018-09-04 08:23:38 +02:00
sharkdp 7b803a50af Ignore enable_ansi_support errors
- Call `enable_ansi_support` only once.
- Ignore errors that appear when trying to enable ANSI support. This
  will hopefully fix #252 and #264
2018-09-03 22:15:38 +02:00
sharkdp 67ec5fe2b7 Add OneHalf themes, closes #256 2018-09-03 22:12:41 +02:00
sharkdp 3447ed4def Add CSV syntax, closes #254 2018-09-03 21:49:26 +02:00
David Peter 532fb921a6 Update README.md 2018-09-01 00:59:30 +02:00
sharkdp ba4d5a2e7d Bump version number 2018-09-01 00:30:43 +02:00
sharkdp 026273a05c Enable ANSI support on Windows 2018-09-01 00:23:15 +02:00
sharkdp 8903b64830 Fix for Rust 1.26 2018-08-31 23:57:27 +02:00
sharkdp 194155f062 Fix relative-path computation for Windows 2018-08-31 23:57:27 +02:00
sharkdp a6ff3b900d Fix terminal width to 80 characters in tests 2018-08-31 23:57:27 +02:00
sharkdp 8a52bcf92d Add hidden --terminal-width option 2018-08-31 23:57:27 +02:00
sharkdp 0b1535f63b Split snapshot tests into individual tests 2018-08-31 23:57:27 +02:00
sharkdp 0a5b3eaf3e Re-enable tests on windows 2018-08-31 23:57:27 +02:00
sharkdp 11294249f8 Add new key for AppVeyor 2018-08-31 23:57:27 +02:00
anykao 35a1236ad0 Update tests.rs 2018-08-31 23:57:27 +02:00
anykao af8da50c2c Update appveyor.yml 2018-08-31 23:57:27 +02:00
anykao 649d1e3df2 Update appveyor.yml 2018-08-31 23:57:27 +02:00
anykao 199cfd4519 Update appveyor.yml 2018-08-31 23:57:27 +02:00
David Peter c7cce0752b Add AppVeyor badge 2018-08-31 23:57:27 +02:00
anykao 3a89486189 Create before_deploy.ps1 2018-08-31 23:57:27 +02:00
anykao 720a2fb7a5 Create appveyor.yml 2018-08-31 23:57:27 +02:00
sharkdp e3c990f11e Respect color settings for list-themes and list-languages 2018-08-31 21:57:18 +02:00
sharkdp 90c7d0c365 Use stdout locks 2018-08-31 21:48:26 +02:00
mchlrhw 56002267d2 Fix panic when running bat --list-languages | head
Fixes: https://github.com/sharkdp/bat/issues/232
2018-08-31 21:43:49 +02:00
David Peter 84734eac9d Update alternatives.md 2018-08-31 00:39:26 +02:00
sharkdp acdea97e71 Add --plain 2018-08-28 23:37:49 +02:00
sharkdp 839e4ee611 Bump version 2018-08-28 23:21:35 +02:00
sharkdp c7afcdebf8 Move theme_preview file to constant 2018-08-28 22:16:42 +02:00
sharkdp 08e6db1581 Add Troubleshooting section
see #220
2018-08-28 22:08:34 +02:00
sharkdp 4d08ecd828 Fix headline levels 2018-08-28 21:04:41 +02:00
sharkdp 28e1d0e613 Add development section to README 2018-08-28 21:03:03 +02:00
sharkdp d11839ad03 Update install instructions 2018-08-28 20:55:15 +02:00
sharkdp 8cacd9b432 Include theme_preview file in binary 2018-08-28 20:24:13 +02:00
sharkdp 6f67444c99 Run cargo fmt 2018-08-28 20:24:13 +02:00
sharkdp fd2b376ea0 Small style changes to --list-themes 2018-08-28 20:24:13 +02:00
sharkdp cda54e4f61 Fix build for Rust 1.26 2018-08-28 20:24:13 +02:00
ms2300 3b90ada735 Mutation done correctly 2018-08-28 20:24:13 +02:00
ms2300 1837d364fe Added bold to theme titles 2018-08-28 20:24:13 +02:00
ms2300 4ea3e6bbb7 Changed preview code 2018-08-28 20:24:13 +02:00
ms2300 38544ed735 Changed to plain style 2018-08-28 20:24:13 +02:00
ms2300 5444b0dfed Fixes mut issue 2018-08-28 20:24:13 +02:00
ms2300 f3dde0185d Modifying changing config for efficiency 2018-08-28 20:24:13 +02:00
ms2300 2e3784aa30 #213 : output hello world for each theme when --list-theme is flagged 2018-08-28 20:24:13 +02:00
sharkdp 1088455702 Add syntaxes for Elm, Kotlin, Puppet, TypeScript
closes #215
closes #216
closes #217
closes #218
2018-08-28 19:36:31 +02:00
Colin Dean 4f812c32ab Adds zenburn theme submodule
Work towards #224 but @sharkdp is going to rebuild themes.bin
2018-08-28 07:37:27 +02:00
sharkdp b6c59a5bd4 Add man page to releases 2018-08-27 23:10:14 +02:00
sharkdp b682679776 Add initial version of man page, see #52 2018-08-27 22:57:47 +02:00
ms2300 81771432fb #212 : added -p as an alias for --style=plain 2018-08-27 17:56:50 +02:00
sharkdp 627181bcb6 Always call the syntax highlighter 2018-08-23 23:35:57 +02:00
sharkdp 226d9a573a Add simple loop-through mode
Use a loop-through mode that simply copies input to output if a
non-interactive terminal is detected.

see #150
2018-08-23 23:13:24 +02:00
sharkdp 246cf79dbd Split into Controller and Printer 2018-08-23 22:37:27 +02:00
sharkdp ea955c734d Refactoring, introduce Printer trait 2018-08-23 19:44:14 +02:00
sharkdp 6223ad6d52 Document Config struct 2018-08-22 22:36:59 +02:00
sharkdp 31a9335bd5 Document Config struct 2018-08-22 22:36:37 +02:00
sharkdp 9316f2a758 Major refactoring and cleanup 2018-08-22 22:29:12 +02:00
sharkdp c884c3cc12 Re-order options in help texts 2018-08-22 19:34:30 +02:00
sharkdp 08850f0474 Update the --help text 2018-08-22 19:27:11 +02:00
Tim Sampson 8e6c5bc562 doc: mention BAT_THEME in help output 2018-08-22 18:14:01 +02:00
sharkdp 052425b12f Load customized themes in addition to defaults
- New themes in `$BAT_CONFIG_DIR/themes` are now loaded *in addition* to
  the default themes (they may also override).
- The `Default.tmTheme` symlink is not necessary anymore.

This relates to #172
2018-08-20 21:39:21 +02:00
sharkdp 2df3305b94 Add new '--blank' option for 'bat cache --init'
closes #206
2018-08-20 21:34:08 +02:00
sharkdp 6882fc1512 Clean up help messages 2018-08-20 21:34:08 +02:00
sharkdp 2508323264 Extract grid and line-number color from theme
This changes the output color of the grid and the line numbers to use
the "gutter" foreground color defined in the Sublime `.tmTheme` files.

Sublime Text does the same.

Note: we could go one step further and also extract the "GitGutter"
colors from the themes. These could be used instead of red/green/yellow
to signify Git modifications. The problem is that they are quite a bit
harder to extract from the syntect `Theme` object.

closes #178
2018-08-19 12:32:35 +02:00
sharkdp 297afad337 Bump version 2018-08-19 12:31:35 +02:00
sharkdp 1dddce3aa1 Separate syntax set and theme set
This commit separates the handling of syntax sets and theme sets. It
also changes the way how new syntax definitions are loaded from `bat`'s
configuration folder. New syntax definitions are now loaded *in
addition* to the ones that are stored in the `bat` binary by default.

This fixes #172
2018-08-19 11:08:23 +02:00
sharkdp 76be0d3933 Add arm as a compile target 2018-08-19 09:50:24 +02:00
sharkdp e5b8c4471a Bump minimum required rust version 2018-08-18 21:30:12 +02:00
sharkdp b191691767 Revert "replace trait object BufRead by generic"
This reverts commit 037861e588.
2018-08-18 21:25:58 +02:00
Michal Nazarewicz 5c95b8803b Avoid floating point arithmetic in RGB→8-bit ANSI approximation 2018-08-18 20:58:03 +02:00
sharkdp 5b421b455d Update dependencies 2018-08-18 20:44:25 +02:00
sharkdp a6d6c28723 Add documentation on new pager-customization 2018-08-18 20:22:11 +02:00
sharkdp 2966939e38 Update submodules 2018-08-18 19:30:35 +02:00
sharkdp d179693d1d Use BAT_PAGER and PAGER environment variables, closes #158 2018-08-18 18:48:22 +02:00
sharkdp 268577c6f9 Run 'cargo fmt' 2018-08-18 17:54:26 +02:00
TA Thanh Dinh 037861e588 replace trait object BufRead by generic 2018-08-18 17:54:26 +02:00
sharkdp 94ccc646e8 Update README 2018-07-23 21:58:06 +02:00
sharkdp 0cc5d1dab6 Update error-chain to fix compiler warnings 2018-07-23 21:54:30 +02:00
sharkdp 28397b8f78 Always show a warning when theme is unknown 2018-07-23 21:51:49 +02:00
sharkdp c899849101 Use map instead of and_then(..Some(..)) 2018-07-23 21:51:49 +02:00
sharkdp bf53b64125 Small style fix 2018-07-23 21:51:49 +02:00
Armando Perez c68aa0f424 Allow specifying the theme via the BAT_THEME environment variable
The `--theme` command line option stills takes precedence and this
change preserves how errors are handled when it's used: If a theme name
that doesn't exist is specified using the argument, this error is fatal.
However, if a theme that doesn't exist is specified using the environment
variable, the error is logged to `stderr` and the "Default" theme is
loaded as a fallback.
2018-07-23 21:51:49 +02:00
sharkdp 6b57f4eebc Style changes 2018-07-18 00:06:24 +02:00
eth-p 0ddd388a29 Improve logic for SGR sequence passthrough 2018-07-18 00:06:24 +02:00
eth-p 34811b8161 Fix #182 2018-07-18 00:06:24 +02:00
David Peter 8b92aae23f Fix Travis link 2018-07-17 23:37:03 +02:00
sharkdp 3a0941cabe Update syntaxes.bin 2018-07-17 23:28:44 +02:00
sharkdp 52877c6d67 Add Nix syntax, closes #180 2018-07-17 23:28:31 +02:00
sharkdp 455de98a89 Update sublimehq/Packages, closes #183 2018-07-17 23:27:19 +02:00
Stéphane Campinas 8c6a74a6c5 typo 2018-07-06 19:09:53 +02:00
Gert Hulselmans 3f8b340ed8 Add AWK syntax. 2018-06-15 16:41:51 +02:00
sharkdp 65e6970907 Change help text for --line-range 2018-06-12 08:18:44 +02:00
sharkdp 90cc019482 Run 'cargo fmt' 2018-06-12 08:18:44 +02:00
Taylor eaf0b99d25 update 2018-06-12 08:18:44 +02:00
Taylor Skinner 3b1dcca5d0 use map() 2018-06-12 08:18:44 +02:00
Taylor Skinner 26dbdf4fa0 update line-ranges 2018-06-12 08:18:44 +02:00
Taylor Skinner 6691786d82 add line-range 2018-06-12 08:18:44 +02:00
Conrad Kleinespel 50209bfe21 updates archlinux install instructions 2018-06-08 16:15:27 +02:00
sharkdp c91511cca1 Always run snapshot test 2018-06-02 19:03:38 +02:00
sharkdp c9f7a0c126 Use separate Git repository for snapshot testing
closes #161
2018-06-02 19:00:05 +02:00
Ezinwa Okpoechi 69c798eafd Fix command typo 2018-06-01 17:01:28 +02:00
92 changed files with 13683 additions and 1017 deletions
+52 -1
View File
@@ -24,7 +24,7 @@
url = https://github.com/clintberry/sublime-text-2-ini
[submodule "assets/syntaxes/CMake"]
path = assets/syntaxes/CMake
url = https://github.com/sharkdp/Sublime-CMakeLists
url = https://github.com/zyxar/Sublime-CMakeLists
[submodule "assets/syntaxes/LESS"]
path = assets/syntaxes/LESS
url = https://github.com/danro/LESS-sublime
@@ -40,3 +40,54 @@
[submodule "assets/themes/TwoDark"]
path = assets/themes/TwoDark
url = https://github.com/erremauro/TwoDark
[submodule "assets/syntaxes/AWK"]
path = assets/syntaxes/AWK
url = https://github.com/JohnNilsson/awk-sublime
[submodule "assets/syntaxes/Nix"]
path = assets/syntaxes/Nix
url = https://github.com/wmertens/sublime-nix
[submodule "assets/themes/zenburn"]
path = assets/themes/zenburn
url = https://github.com/colinta/zenburn.git
[submodule "assets/syntaxes/Kotlin"]
path = assets/syntaxes/Kotlin
url = https://github.com/vkostyukov/kotlin-sublime-package
[submodule "assets/syntaxes/Elm"]
path = assets/syntaxes/Elm
url = https://github.com/elm-community/SublimeElmLanguageSupport
[submodule "assets/syntaxes/TypeScript"]
path = assets/syntaxes/TypeScript
url = https://github.com/Microsoft/TypeScript-Sublime-Plugin
[submodule "assets/syntaxes/Puppet"]
path = assets/syntaxes/Puppet
url = https://github.com/russCloak/SublimePuppet
[submodule "assets/syntaxes/CSV"]
path = assets/syntaxes/CSV
url = https://github.com/wadetb/Sublime-Text-Advanced-CSV
[submodule "assets/themes/onehalf"]
path = assets/themes/onehalf
url = https://github.com/sonph/onehalf
[submodule "assets/syntaxes/JavaScript (Babel)"]
path = assets/syntaxes/JavaScript (Babel)
url = https://github.com/babel/babel-sublime
[submodule "assets/syntaxes/Cabal"]
path = assets/syntaxes/Cabal
url = https://github.com/SublimeHaskell/SublimeHaskell
[submodule "assets/syntaxes/Dart"]
path = assets/syntaxes/Dart
url = https://github.com/guillermooo/dart-sublime-bundle
[submodule "assets/syntaxes/FSharp"]
path = assets/syntaxes/FSharp
url = https://github.com/hoest/sublimetext-fsharp
[submodule "assets/syntaxes/PureScript"]
path = assets/syntaxes/PureScript
url = https://github.com/tellnobody1/sublime-purescript-syntax
[submodule "assets/syntaxes/Swift"]
path = assets/syntaxes/Swift
url = https://github.com/quiqueg/Swift-Sublime-Package
[submodule "assets/syntaxes/Crystal"]
path = assets/syntaxes/Crystal
url = https://github.com/crystal-lang-tools/sublime-crystal.git
[submodule "assets/syntaxes/PowerShell"]
path = assets/syntaxes/PowerShell
url = https://github.com/PowerShell/EditorSyntax
+21 -18
View File
@@ -17,18 +17,30 @@ matrix:
- os: osx
rust: stable
env: TARGET=x86_64-apple-darwin
- os: linux
rust: stable
env:
- TARGET=arm-unknown-linux-gnueabihf
- CC_arm_unknown_linux_gnueabihf=/usr/bin/arm-linux-gnueabihf-gcc-4.8
- CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc-4.8
- os: linux
rust: stable
env:
- TARGET=aarch64-unknown-linux-gnu
- CC_aarch64-unknown-linux-gnu=/usr/bin/aarch64-linux-gnu-gcc-4.8
- CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc-4.8
# Minimum Rust supported channel.
- os: linux
rust: 1.24.0
rust: 1.26.0
env: TARGET=x86_64-unknown-linux-gnu
- os: linux
rust: 1.24.0
rust: 1.26.0
env:
- TARGET=x86_64-unknown-linux-musl
- CC_x86_64_unknown_linux_musl=/usr/bin/musl-gcc
- os: osx
rust: 1.24.0
rust: 1.26.0
env: TARGET=x86_64-apple-darwin
# Code formatting check
@@ -39,15 +51,10 @@ matrix:
- cargo install --debug --force rustfmt-nightly
script: cargo fmt -- --check
addons:
apt:
packages:
# needed for i686-unknown-linux-gnu target
- gcc-multilib
# needed for musl targets
- musl-tools
# needed to build deb packages
- fakeroot
sudo: required
before_install:
- ci/before_install.bash
env:
global:
@@ -62,11 +69,7 @@ install:
- if [[ $TRAVIS_OS_NAME = linux && $HOST != $TARGET ]]; then rustup target add $TARGET; fi
script:
# Incorporate TARGET env var to the build and test process
- cargo build --target $TARGET --verbose
- cargo test --target $TARGET --verbose
# Run 'bat' on its own source code and the README
- cargo run --target $TARGET -- src/main.rs README.md --paging=never
- ci/script.bash
before_deploy:
- bash ci/before_deploy.bash
Generated
+343 -317
View File
File diff suppressed because it is too large Load Diff
+8 -5
View File
@@ -7,7 +7,7 @@ license = "MIT/Apache-2.0"
name = "bat"
readme = "README.md"
repository = "https://github.com/sharkdp/bat"
version = "0.4.1"
version = "0.7.0"
exclude = [
"assets/syntaxes/*",
"assets/themes/*",
@@ -15,9 +15,9 @@ exclude = [
[dependencies]
atty = "0.2.2"
ansi_term = "0.10"
ansi_term = "0.11"
console = "0.6"
directories = "0.10"
directories = "1.0"
lazy_static = "1.0"
[dependencies.git2]
@@ -31,11 +31,14 @@ default-features = false
features = ["parsing", "yaml-load", "dump-load", "dump-create"]
[dependencies.clap]
version = "2"
version = "2.32"
default-features = false
features = ["suggestions", "color", "wrap_help"]
[dependencies.error-chain]
version = "0.11"
version = "0.12"
default-features = false
features = []
[dev-dependencies]
tempdir = "0.3"
+161 -50
View File
@@ -1,6 +1,7 @@
<p align="center">
<img src="doc/logo-header.svg" alt="bat - a cat clone with wings"><br>
<a href="Version info"><img src="https://travis-ci.org/sharkdp/bat.svg?branch=master" alt="Build Status"></a>
<a href="https://travis-ci.org/sharkdp/bat"><img src="https://travis-ci.org/sharkdp/bat.svg?branch=master" alt="Build Status"></a>
<a href="https://ci.appveyor.com/project/sharkdp/bat"><img src="https://ci.appveyor.com/api/projects/status/cptsmtbiwbnr2vhf?svg=true"></a>
<img src="https://img.shields.io/crates/l/bat.svg" alt="license">
<a href="https://crates.io/crates/bat"><img src="https://img.shields.io/crates/v/bat.svg?colorB=319e8c" alt="Version info"></a><br>
A <i>cat(1)</i> clone with syntax highlighting and Git integration.
@@ -71,28 +72,37 @@ bat header.md content.md footer.md > document.md
bat -n main.rs # show line numbers (only)
cat f - g # output 'f', then stdin, then 'g'.
bat f - g # output 'f', then stdin, then 'g'.
```
## Installation
### From binaries
### On Ubuntu
*... and other Debian-based Linux distributions.*
Check out the [Release page](https://github.com/sharkdp/bat/releases) for
binary builds and Debian packages.
#### On Arch Linux
You can install [the AUR package](https://aur.archlinux.org/packages/bat/)
via yaourt, or manually:
```bash
git clone https://aur.archlinux.org/bat.git
cd bat
makepkg -si
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.7.0_amd64.deb # adapt version number and architecture
```
#### On FreeBSD
### On Arch Linux
You can install [the `bat` package](https://www.archlinux.org/packages/community/x86_64/bat/)
from the official sources:
```bash
pacman -S bat
```
### On Void Linux
You can install `bat` via xbps-install:
```
xbps-install -S bat
```
### On FreeBSD
You can install a precompiled [`bat` package](https://www.freshports.org/textproc/bat) with pkg:
@@ -107,7 +117,7 @@ cd /usr/ports/textproc/bat
make install
```
#### On macOS
### On macOS
You can install `bat` with [Homebrew](http://braumeister.org/formula/bat):
@@ -115,9 +125,25 @@ You can install `bat` with [Homebrew](http://braumeister.org/formula/bat):
brew install bat
```
### On Windows
You can download pre-built binaries from the [Release page](https://github.com/sharkdp/bat/releases),
or install it with [scoop](https://scoop.sh/):
```bash
scoop install bat
```
[See below](#using-bat-on-windows) for notes.
### From binaries
Check out the [Release page](https://github.com/sharkdp/bat/releases) for
prebuilt versions of `bat` for many different architectures.
### From source
If you want to build to compile `bat` from source, you need Rust 1.24 or
If you want to build `bat` from source, you need Rust 1.26 or
higher. You can then use `cargo` to build everything:
```bash
@@ -132,65 +158,150 @@ some dependencies to be built.
### Highlighting theme
Use `bat --list-themes` to get a list of all available themes for syntax
highlighting. To select the `TwoDark` theme, for example, call `bat` with
the `--theme=TwoDark` option. Use `alias bat="bat --theme=TwoDark"` in your
shells startup file to make the change permanent.
highlighting. To select the `TwoDark` theme, call `bat` with the
`--theme=TwoDark` option or set the `BAT_THEME` environment variable to
`TwoDark`. Use `export BAT_THEME="TwoDark"` in your shells startup file to
make the change permanent.
### Output style
You can use the `--style` option to control the appearance of `bat`s output.
You can use `--style=numbers,changes`, for example, to show only Git changes
and line numbers but no grid and no file header.
and line numbers but no grid and no file header. Use the `BAT_STYLE` environment
variable to make these changes permanent.
### Add new syntaxes and highlighting themes
### Adding new syntaxes / language definitions
`bat` uses the excellent [`syntect`](https://github.com/trishume/syntect/)
library for syntax highlighting. `syntect` can read any
[Sublime Text `.sublime-syntax` file](https://www.sublimetext.com/docs/3/syntax.html)
and theme.
and theme. To add new syntax definitions, do the following.
To build your own language-set and theme, follow these steps:
Create a folder with syntax definition files:
Create a folder with a syntax highlighting theme:
```bash
BAT_CONFIG_DIR="$(bat cache --config-dir)"
mkdir -p "$BAT_CONFIG_DIR/syntaxes"
cd "$BAT_CONFIG_DIR/syntaxes"
# Put new '.sublime-syntax' language definition files
# in this folder (or its subdirectories), for example:
git clone https://github.com/tellnobody1/sublime-purescript-syntax
```
Now use the following command to parse these files into a binary cache:
```bash
bat cache --init
```
Finally, use `bat --list-languages` to check if the new languages are available.
If you ever want to go back to the default settings, call:
```bash
bat cache --clear
```
### Adding new themes
This works very similar to how we add new syntax definitions.
First, create a folder with the new syntax highlighting themes:
```bash
BAT_CONFIG_DIR="$(bat cache --config-dir)"
mkdir -p "$BAT_CONFIG_DIR/themes"
cd "$BAT_CONFIG_DIR/themes"
# Download a theme, for example:
# Download a theme in '.tmTheme' format, for example:
git clone https://github.com/greggb/sublime-snazzy
# Create a link for the default theme
ln -sf "sublime-snazzy/Sublime Snazzy.tmTheme" Default.tmTheme
```
Create a folder with language definition files:
```bash
mkdir -p "$BAT_CONFIG_DIR/syntaxes"
cd "$BAT_CONFIG_DIR/syntaxes"
# Download some language definition files, for example:
git clone https://github.com/sublimehq/Packages
git clone https://github.com/danro/LESS-sublime
```
Finally, use the following command to parse all these files into a binary
cache:
```bash
# Update the binary cache
bat cache --init
```
Use `bat --list-languages` and `bat --list-themes` to check if all languages and themes are
available.
Finally, use `bat --list-themes` to check if the new themes are available.
If you ever want to go back to the default settings, call:
### Using a different pager
`bat` uses the pager that is specified in the `PAGER` environment variable. If this variable is not
set, `less` is used by default. If you want to use a different pager, you can either modify the
`PAGER` variable or set the `BAT_PAGER` environment variable to override what is specified in
`PAGER`. If you want to pass command-line arguments to the pager, you need to create a small shell
script as a wrapper, for example:
```bash
bat cache --clear
#!/bin/bash
less --tabs 4 -RF "$@"
```
## Using `bat` on Windows
`bat` mostly works out-of-the-box on Windows, but a few features may need extra configuration.
### Paging
Windows only includes a very limited pager in the form of `more`. You can download a Windows binary
for `less` [from its homepage](http://www.greenwoodsoftware.com/less/download.html) or [through
Chocolatey](https://chocolatey.org/packages/Less). To use it, place the binary in a directory in
your `PATH` or [define an environment variable](#using-a-different-pager).
### Colours
Windows 10 natively supports colours in both `conhost.exe` (Command Prompt) and PowerShell since
[v1511](https://en.wikipedia.org/wiki/Windows_10_version_history#Version_1511_(November_Update)), as
well as in newer versions of bash. On earlier versions of Windows, you can use
[Cmder](http://cmder.net/), which includes [ConEmu](https://conemu.github.io/).
**Note:** The Git and MSYS versions of `less` do not correctly interpret colours on Windows. If you
dont have any other pagers installed, you can disable paging entirely by passing `--paging=never`
or by setting `BAT_PAGER` to an empty string.
## Troubleshooting
### Terminals & colors
`bat` handles terminals *with* and *without* truecolor support. However, the colors in the syntax
highlighting themes are not optimized for 8-bit colors and it is therefore strongly recommended
that you use a terminal with 24-bit truecolor support (`terminator`, `konsole`, `iTerm2`, ...).
See [this article](https://gist.github.com/XVilka/8346728) for more details and a full list of
terminals with truecolor support.
Make sure that your truecolor terminal sets the `COLORTERM` variable to either `truecolor` or
`24bit`. Otherwise, `bat` will not be able to determine whether or not 24-bit escape sequences
are supported (and fall back to 8-bit colors).
### Line numbers and grid are hardly visible
Please try a different theme (see `bat --list-themes` for a list). The `OneHalfDark` and
`OneHalfLight` themes provide grid and line colors that are brighter.
### Error when compiling: "cannot find -lz"
Please install the `zlib-devel` package and try again.
## Development
```bash
# Recursive clone to retrieve all submodules
git clone --recursive https://github.com/sharkdp/bat
# Build (debug version)
cd bat
cargo build
# Run unit tests and integration tests
cargo test
# Install (release version)
cargo install
# Build a bat binary with modified syntaxes and themes
bash assets/create.sh
cargo install -f
```
## Project goals and alternatives
+49
View File
@@ -0,0 +1,49 @@
environment:
global:
TARGET: x86_64-pc-windows-msvc
RUST_VERSION: stable
CRATE_NAME: bat
CARGO_HOME: "c:\\cargo"
RUSTUP_HOME: "c:\\rustup"
install:
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
- rustup-init.exe -y --default-host %TARGET%
- set PATH=%PATH%;C:\cargo\bin
- rustc -Vv
- cargo -V
build: false
test_script:
- cargo test --target %TARGET% --verbose
- cargo run --target %TARGET% -- src/main.rs README.md --paging=never
before_deploy:
# Generate artifacts for release
- cargo build --release --verbose
- ps: ci\before_deploy.ps1
deploy:
description: 'Automatically deployed release'
artifact: /.*\.zip/
# Here's how:
# - Go to 'https://github.com/settings/tokens/new' and generate a Token with only the
# `public_repo` scope enabled
# - Then go to 'https://ci.appveyor.com/tools/encrypt' and enter the newly generated token.
# - Enter the "encrypted value" below
auth_token:
secure: oO4/J+hcFXgXcEqEc8gzlQNqLG0IxU78s4EyH+uczGdsyWajb3yipxPR6nXUvmoj
provider: GitHub
on:
RUST_VERSION: stable
appveyor_repo_tag: true
cache:
- C:\Users\appveyor\.cargo\registry
- target
notifications:
- provider: Email
on_build_success: false
on_build_failure: false
on_build_status_changed: false
+1
View File
@@ -29,3 +29,4 @@ themes (`bat cache --clear`).
The following files have been manually modified after converting from a `.tmLanguage` file:
* `VimL.sublime-syntax` => added `.vimrc` file type.
* `Dart.sublime-syntax` => removed `#regex.dart` include,
+1 -1
View File
@@ -2,4 +2,4 @@
ASSET_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
bat cache --init --source="$ASSET_DIR" --target="$ASSET_DIR"
bat cache --init --blank --source="$ASSET_DIR" --target="$ASSET_DIR"
Binary file not shown.
Submodule assets/syntaxes/AWK added at 1ce5f90d44
Submodule assets/syntaxes/CSV added at 4786d037a7
+46
View File
@@ -0,0 +1,46 @@
%YAML 1.2
---
# http://www.sublimetext.com/docs/3/syntax.html
name: Advanced CSV
file_extensions:
- csv
- tsv
scope: text.advanced_csv
contexts:
main:
- match: (\")
captures:
1: string.quoted.double.advanced_csv
push:
- meta_scope: meta.quoted.advanced_csv
- match: (\")
captures:
1: string.quoted.double.advanced_csv
pop: true
- include: main
- match: '(\[([+-]?\d*)(\:)?([+-]?\d*)(\,)?([+-]?\d*)(\:)?([+-]?\d*)\])?\s*([<>v^])?\s*(=)'
captures:
1: keyword.operator.advanced_csv
2: constant.numeric.formula.advanced_csv
4: constant.numeric.formula.advanced_csv
6: constant.numeric.formula.advanced_csv
8: constant.numeric.formula.advanced_csv
9: keyword.operator.advanced_csv
10: keyword.operator.advanced_csv
push:
- meta_scope: meta.range.advanced_csv
- match: (?=(\")|$)
pop: true
- include: scope:source.python
- match: '(?<=^|,|\s|\")([0-9.eE+-]+)(?=$|,|\s|\")'
scope: meta.number.advanced_csv
captures:
1: constant.numeric.advanced_csv
- match: '(?<=^|,|\s|\")([^, \t\"]+)(?=$|,|\s|\")'
scope: meta.nonnumber.advanced_csv
captures:
1: storage.type.advanced_csv
- match: (\,)
scope: meta.delimiter.advanced_csv
captures:
1: keyword.operator.advanced_csv
+47
View File
@@ -0,0 +1,47 @@
%YAML 1.2
---
# http://www.sublimetext.com/docs/3/syntax.html
name: Cabal
file_extensions:
- cabal
scope: source.cabal
contexts:
main:
- match: ^(\s*)(exposed-modules):$
captures:
2: constant.other.cabal
push:
- meta_scope: exposed.modules.cabal
- match: ^(?!\1\s)
pop: true
- include: module_name
- match: ^(\s*)(build-depends):$
captures:
2: constant.other.cabal
push:
- meta_scope: exposed.modules.cabal
- match: ^(?!\1\s)
pop: true
- match: "([<>=]+)|([&|]+)"
scope: keyword.operator.haskell
- match: ((\d+|\*)\.)*(\d+|\*)
scope: constant.numeric.haskell
- match: '([\w\-]+)'
scope: support.function.haskell
- match: '^\s*([a-zA-Z_-]+)(:)\s+'
scope: entity.cabal
captures:
1: constant.other.cabal
2: punctuation.entity.cabal
- match: '^(?i)(executable|library|test-suite|benchmark|flag|source-repository)\s+([^\s,]+)\s*$'
scope: entity.cabal
captures:
1: keyword.entity.cabal
2: string.cabal
- match: ^(?i)library\s*$
scope: keyword.entity.cabal
- match: "--.*$"
scope: comment.cabal
module_name:
- match: '([A-Z][A-Za-z_''0-9]*)(\.[A-Z][A-Za-z_''0-9]*)*'
scope: storage.module.haskell
File diff suppressed because it is too large Load Diff
+369
View File
@@ -0,0 +1,369 @@
%YAML 1.2
---
# http://www.sublimetext.com/docs/3/syntax.html
name: Dart
file_extensions:
- dart
scope: source.dart
contexts:
main:
- match: ^(#!.*)$
scope: meta.preprocessor.script.dart
- match: ^\s*\b(library|import|export|part of|part)\b
captures:
0: keyword.other.import.dart
push:
- meta_scope: meta.declaration.dart
- match: ;
captures:
0: punctuation.terminator.dart
pop: true
- include: strings
- match: \b(as|show|hide|deferred)\b
scope: keyword.other.import.dart
- include: comments
- include: constants-and-special-vars
- include: annotations
- include: decl-typedef
- include: decl-class
- include: decl-enum
- include: decl-function
- include: keywords
- include: strings
annotations:
- match: '^(?:\s*)((@)([a-zA-Z0-9_]+))'
captures:
1: annotation.dart
2: entity.name.function.annotation.dart
3: support.type.dart
comments:
- match: /\*\*/
scope: comment.block.empty.dart
captures:
0: punctuation.definition.comment.dart
- include: comments-inline
comments-inline:
- match: /\*
push:
- meta_scope: comment.block.dart
- match: \*/
pop: true
- include: scope:text.dart-doccomments
- match: (///)
captures:
1: marker.dart
push:
- meta_scope: comment.line.triple-slash.dart
- match: $
pop: true
- include: scope:text.dart-doccomments
- match: (//)
captures:
1: marker.dart
push:
- meta_scope: comment.line.double-slash.dart
- match: $
pop: true
- include: scope:text.dart-doccomments
constants-and-special-vars:
- match: \b(true|false|null)\b
scope: constant.language.dart
- match: \b(this|super)\b
scope: variable.language.dart
- match: '\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)\b'
scope: constant.numeric.dart
decl-class:
- match: \bclass\b
captures:
0: keyword.control.new.dart
push:
- meta_scope: meta.declaration.class.dart
- match: "(?={)"
pop: true
- include: keywords
- match: "[A-Za-z_][A-Za-z0-9_]*"
scope: class.name.dart
decl-enum:
- match: \benum\b
captures:
0: keyword.declaration.dart
push:
- meta_scope: meta.declaration.enum.dart
- match: "(?={)"
pop: true
- include: keywords
- match: "[A-Za-z_][A-Za-z0-9_]*"
scope: enum.name.dart
decl-function:
- match: ^\s*(?:\b(void|bool|num|int|double|dynamic|var|String|List|Map)\b)\s+(get)\s+(\w+)\s+(?==>)
comment: A getter with a primitive return type.
scope: meta.declaration.function.dart
captures:
1: storage.type.primitive.dart
2: keyword.declaration.dart
3: function.name.dart
- match: ^\s*(?:\b(\w+)\b\s+)?(get)\s+(\w+)\s+(?==>)
comment: A getter with a user-defined return type or no return type.
scope: meta.declaration.function.dart
captures:
1: type.user-defined.dart
2: keyword.declaration.dart
3: function.name.dart
- match: ^\s*(set)\s+(\w+)(?=\()
comment: A setter.
captures:
1: keyword.declaration.dart
2: function.name.dart
push:
- meta_scope: meta.declaration.function.dart
- match: \)
pop: true
- include: comments-inline
- include: decl-function-parameter
- include: strings
- include: keywords
- match: ^\s*(?:\b(void|bool|num|int|double|dynamic|var|String|List|Map)\b)\s+(\w+)(?=\()
comment: A function with a primitive return type.
captures:
1: storage.type.primitive.dart
2: function.name.dart
push:
- meta_scope: meta.declaration.function.dart
- match: \)
pop: true
- include: comments-inline
- include: decl-function-parameter
- include: strings
- include: keywords
- match: ^\s*(?:\b(return)\b)\s+(\w+)(?=\()
comment: A function invocation after 'return'
captures:
1: keyword.control.dart
2: function.name.dart
push:
- meta_scope: meta.invocation.function.dart
- match: \)
pop: true
- include: comments-inline
- include: decl-function-parameter
- include: strings
- include: keywords
- match: ^\s*\b(new)\b\s+(\w+)(?=\()
comment: A class instantiation after 'new'
captures:
1: keyword.declaration.dart
2: function.name.dart
push:
- meta_scope: meta.invocation.function.dart
- match: \)
pop: true
- include: comments-inline
- include: decl-function-parameter
- include: strings
- include: keywords
decl-function-parameter:
- include: constants-and-special-vars
- match: (?:\b(void|bool|num|int|double|dynamic|var|String|List|Map)\b)\s+(\w+)(?=\()
comment: A function with a primitive return type.
captures:
1: storage.type.primitive.dart
2: function.name.dart
push:
- meta_scope: meta.parameter.function.dart
- match: \)
pop: true
- include: decl-function-parameter
- include: strings
- include: keywords
- match: \b(new)\b\s+(\w+)(?=\()
comment: A class instantiation after 'new'
captures:
1: keyword.declaration.dart
2: function.name.dart
push:
- meta_scope: meta.invocation.function.dart
- match: \)
pop: true
- include: decl-function-parameter
- include: strings
- include: keywords
- match: (?:\b(\w+)\b)\s+(\w+)(?=\()
comment: A function with a user-defined return type.
captures:
1: type.user-defined.dart
2: function.name.dart
push:
- meta_scope: meta.parameter.function.dart
- match: \)
pop: true
- include: decl-function-parameter
- include: strings
- include: keywords
- match: (\w+)(?=\()
comment: A function with no return type.
captures:
1: function.name.dart
push:
- meta_scope: meta.parameter.function.dart
- match: \)
pop: true
- include: decl-function-parameter
- include: strings
- include: keywords
decl-typedef:
- match: typedef
captures:
0: keyword.control.new.dart
push:
- meta_scope: meta.declaration.typedef.dart
- match: ;
captures:
0: punctuation.terminator.dart
pop: true
- match: '(?:\b(void|bool|num|int|double|dynamic|var|String|List|Map)\b|([a-zA-Z_][a-zA-Z0-9_]*))\s+([a-zA-Z_][a-zA-Z0-9_]+)'
captures:
1: storage.type.primitive.dart
2: typedef.return.dart
3: typedef.name.dart
- match: \(
push:
- meta_scope: typedef.params.dart
- match: \)
pop: true
- include: keywords
keywords:
- match: \bassert\b
scope: keyword.control.assert.dart
- match: \bas\b
scope: keyword.cast.dart
- match: \b(try|catch|finally|throw|on|rethrow)\b
scope: keyword.control.catch-exception.dart
- match: \s+\?\s+|\s+:\s+
scope: keyword.control.ternary.dart
- match: \b(break|case|continue|default|do|else|for|if|in|return|switch|while)\b
scope: keyword.control.dart
- match: \b(async\*|async|await\*|await|yield)\b
scope: keyword.control.async.dart
- match: \b(new)\b
scope: keyword.control.new.dart
- match: \b(abstract|extends|external|factory|implements|with|interface|get|native|operator|set|typedef)\b
scope: keyword.declaration.dart
- match: \b(is\!?)\b
scope: keyword.operator.dart
- match: (<<|>>>?|~|\^|\||&)
scope: keyword.operator.bitwise.dart
- match: ((&|\^|\||<<|>>>?)=)
scope: keyword.operator.assignment.bitwise.dart
- match: (===?|!==?|<=?|>=?)
scope: keyword.operator.comparison.dart
- match: '(([+*/%-]|\~)=)'
scope: keyword.operator.assignment.arithmetic.dart
- match: (=)
scope: keyword.operator.assignment.dart
- match: (\-\-|\+\+)
scope: keyword.operator.increment-decrement.dart
- match: (\-|\+|\*|\/|\~\/|%)
scope: keyword.operator.arithmetic.dart
- match: (!|&&|\|\|)
scope: keyword.operator.logical.dart
- match: ;
scope: punctuation.terminator.dart
- match: \b(static|final|const)\b
scope: storage.modifier.dart
- match: \b(?:void|bool|num|int|double|dynamic|var|String|List|Map)\b
scope: storage.type.primitive.dart
regexp:
- match: '\\[^''"]'
scope: constant.character.escaped.regex.dart
- match: \(
push:
- meta_content_scope: meta.capture.regex.dart
- match: \)
pop: true
- match: \?(:|=|!)
scope: ignore.capture.regex.dart
- match: \*|\+|\?|\.|\|
scope: keyword.other.regex.dart
- match: \^|\$
scope: keyword.other.regex.dart
- match: \.
scope: constant.other.regex.dart
- match: '\[(\^)?'
captures:
1: keyword.other.negation.regex.dart
push:
- meta_scope: constant.character.range.regex.dart
- match: '\]'
pop: true
- match: '\\[^"'']'
scope: constant.character.escaped.regex.dart
- match: '\{(?:\d+)?,(?:\d+)?\}'
scope: keyword.other.regex.dart
string-interp:
- match: '(\$)(\{)'
captures:
1: keyword.other.dart
2: keyword.other.dart
push:
- meta_scope: interpolation.dart
- meta_content_scope: source.dart
- match: '(\})'
captures:
1: keyword.other.dart
pop: true
- include: main
- match: (\$)(\w+)
captures:
1: keyword.other.dart
2: variable.parameter.dart
- match: \\.
scope: constant.character.escape.dart
strings:
- match: (?<!r)"""
push:
- meta_scope: string.interpolated.triple.double.dart
- match: '"""(?!")'
pop: true
- include: string-interp
- match: (?<!r)'''
push:
- meta_scope: string.interpolated.triple.single.dart
- match: "'''(?!')"
pop: true
- include: string-interp
- match: r"""
push:
- meta_scope: string.quoted.triple.double.dart
- match: '"""(?!")'
pop: true
- match: r'''
push:
- meta_scope: string.quoted.triple.single.dart
- match: "'''(?!')"
pop: true
- match: (?<!\\|r)"
push:
- meta_scope: string.interpolated.double.dart
- match: '"'
pop: true
- match: \n
scope: invalid.string.newline
- include: string-interp
- match: r"
push:
- meta_scope: string.quoted.double.dart
- match: '"'
pop: true
- include: regexp
- match: (?<!\|r)'
push:
- meta_scope: string.interpolated.single.dart
- match: "'"
pop: true
- include: string-interp
- match: r'
push:
- meta_scope: string.quoted.single.dart
- match: "'"
pop: true
- include: regexp
Submodule assets/syntaxes/Elm added at dfbb8bdc9a
+234
View File
@@ -0,0 +1,234 @@
%YAML 1.2
---
# http://www.sublimetext.com/docs/3/syntax.html
name: "F#"
file_extensions:
- fs
- fsi
- fsx
scope: source.fsharp
contexts:
main:
- include: comments
- include: constants
- include: structure
- include: attributes
- include: strings
- include: definition
- include: method_calls
- include: modules
- include: anonymous_functions
- include: keywords
anonymous_functions:
- match: \b(fun)\b
captures:
1: keyword.other.function-definition.fsharp
push:
- meta_scope: meta.function.anonymous
- match: (->)
captures:
1: keyword.other.fsharp
pop: true
- include: variables
attributes:
- match: '\[\<'
push:
- meta_scope: support.function.attribute.fsharp
- match: '\>\]'
pop: true
- include: main
characters:
- match: (')
captures:
1: punctuation.definition.string.begin.fsharp
push:
- meta_scope: string.quoted.single.fsharp
- match: (')
captures:
1: punctuation.definition.string.end.fsharp
pop: true
- match: '\\$[ \t]*'
scope: punctuation.separator.string.ignore-eol.fsharp
- match: '\\([\\""ntbr]|u[a-fA-F0-9]{4}|u[a-fA-F0-9]{8})'
scope: constant.character.string.escape.fsharp
- match: '\\(?![\\''ntbr]|u[a-fA-F0-9]{4}|u[a-fA-F0-9]{8}).'
scope: invalid.illeagal.character.string.fsharp
comments:
- match: \(\*\*?(\*)\)
scope: comment.block.fsharp
captures:
1: comment.block.empty.fsharp
- match: '\(\*[^\)]'
push:
- meta_scope: comment.block.fsharp
- match: \*\)
pop: true
- include: comments
- match: //.*$
scope: comment.line.double-slash.fsharp
constants:
- match: \(\)
scope: constant.language.unit.fsharp
- match: '\b-?[0-9][0-9_]*((\.([0-9][0-9_]*([eE][+-]??[0-9][0-9_]*)?)?)|([eE][+-]??[0-9][0-9_]*))'
scope: constant.numeric.floating-point.fsharp
- match: '\b(-?((0(x|X)[0-9a-fA-F][0-9a-fA-F_]*)|(0(o|O)[0-7][0-7_]*)|(0(b|B)[01][01_]*)|([0-9][0-9_]*)))'
scope: constant.numeric.integer.nativeint.fsharp
definition:
- match: '\b(val|val|let|and|member|override|use)\s?(rec|inline|mutable)?(\s+\(?([a-zA-Z.\|_][a-zA-Z0-9.|_]*)\)?\w*)\b'
captures:
1: keyword.other.binding.fsharp
2: keyword.other.function-recursive.fsharp
3: variable.other.binding.fsharp
push:
- meta_scope: meta.binding.fsharp
- match: "=|$"
captures:
1: punctuation.separator.function.type-constraint.fsharp
pop: true
- include: variables
- match: \b(let)\s+((\(\))|(_))\s+=
scope: meta.expression.fsharp
captures:
1: keyword.other.binding.fsharp
2: keyword.other.function-recursive.fsharp
4: constant.language.unit.fsharp
5: constant.language.ignored.fsharp
keywords:
- match: ^\s*#\s*(light|if|else|endif|r|I|load|time|help|quit)\b
push:
- meta_scope: meta.preprocessor.fsharp
- match: (\s|$)
pop: true
- match: \b(new|in|as|if|then|else|elif|for|begin|end|match|with|type|inherit|true|false|null|do)\b
scope: keyword.other.fsharp
- match: '(\|>|\|?>|\->|\<\-|:>|:|\[|\]|\;|\||_)'
scope: entity.name
method_calls:
- match: '(?<!\w)([a-z]\w*)(\.)'
push:
- meta_scope: meta.method-call.fsharp
- match: '[A-Z]\w*(\.)'
scope: meta.method.fsharp
captures:
1: punctuation.separator.method-call.fsharp
- match: (?=.)
pop: true
modules:
- match: '\b(namespace|module)\s+([a-zA-Z][a-zA-Z0-9''_.]*)'
captures:
1: keyword.other.fsharp
2: entity.name.section.fsharp
push:
- meta_scope: meta.module.namespace.fsharp
- match: (\s|$)
pop: true
- match: '(\.)([A-Z][a-zA-Z0-9''_]*)'
scope: support.other.module.fsharp
captures:
1: punctuation.separator.module-reference.fsharp
2: support.other.module.fsharp
- match: '\b(open)\s+([A-Z][a-zA-Z0-9''_]*)(?=(\.[A-Z][a-zA-Z0-9_]*)*)'
captures:
1: keyword.other.fsharp
2: support.other.module.fsharp
push:
- meta_scope: meta.module.open.fsharp
- match: (\s|$)
pop: true
- match: '(\.)([A-Z][a-zA-Z0-9''_]*)'
scope: support.other.module.fsharp
captures:
1: punctuation.separator.module-reference.fsharp
2: support.other.module.fsharp
- match: '^\s*(module)\s+([A-Z][a-zA-Z0-9''_]*)\s*(=)\s*([A-Z][a-zA-Z0-9''_]*)'
captures:
1: keyword.other.module-definition.fsharp
2: entity.name.type.module.fsharp
3: punctuation.separator.module-definition.fsharp
4: support.other.module.fsharp
push:
- meta_scope: meta.module.alias.fsharp
- match: (\s|$)
pop: true
- match: '(\.)([A-Z][a-zA-Z0-9''_]*)'
scope: support.other.module.fsharp
captures:
1: punctuation.separator.module-reference.fsharp
2: support.other.module.fsharp
- match: '(?<!\w)([A-Z][a-zA-Z0-9_]*)(\.)'
captures:
1: support.other.module.fsharp
2: punctuation.separator.module-reference.fsharp
push:
- meta_scope: meta.module.reference.fsharp
- match: '[A-Z][a-zA-Z0-9_]+(\.)'
scope: support.other.module.fsharp
captures:
1: punctuation.separator.module-reference.fsharp
- match: (?=.)
pop: true
strings:
- match: '(?=[^\\])(""")'
captures:
1: punctuation.definition.string.begin.fsharp
push:
- meta_scope: string.quoted.double.fsharp
- match: (""")
captures:
1: punctuation.definition.string.end.fsharp
pop: true
- match: '\\$[ \t]*'
scope: punctuation.separator.string.ignore-eol.fsharp
- match: '(?=[^\\])(@")'
captures:
1: punctuation.definition.string.begin.fsharp
push:
- meta_scope: string.quoted.double.fsharp
- match: (")
captures:
1: punctuation.definition.string.end.fsharp
pop: true
- match: '\\$[ \t]*'
scope: punctuation.separator.string.ignore-eol.fsharp
- match: '(?=[^\\])(")'
captures:
1: punctuation.definition.string.begin.fsharp
push:
- meta_scope: string.quoted.double.fsharp
- match: (")
captures:
1: punctuation.definition.string.end.fsharp
pop: true
- match: '\\$[ \t]*'
scope: punctuation.separator.string.ignore-eol.fsharp
- match: '\\([\\''ntbr]|u[a-fA-F0-9]{4}|u[a-fA-F0-9]{8})'
scope: constant.character.string.escape.fsharp
- match: '\\(?![\\''ntbr]|u[a-fA-F0-9]{4}|u[a-fA-F0-9]{8}).'
scope: invalid.illeagal.character.string.fsharp
- match: '(?=[^\\])('')'
captures:
1: punctuation.definition.string.begin.fsharp
push:
- meta_scope: string.quoted.double.fsharp
- match: (')
captures:
1: punctuation.definition.string.end.fsharp
pop: true
- match: '\\$[ \t]*'
scope: punctuation.separator.string.ignore-eol.fsharp
- match: '\\([\\"ntbr]|u[a-fA-F0-9]{4}|u[a-fA-F0-9]{8})'
scope: constant.character.string.escape.fsharp
- match: '\\(?![\\"ntbr]|u[a-fA-F0-9]{4}|u[a-fA-F0-9]{8}).'
scope: invalid.illeagal.character.string.fsharp
structure:
- match: \(
push:
- meta_scope: meta.paren-group.fsharp
- match: \)
pop: true
- include: main
variables:
- match: \(\)
scope: variable.parameter.unit.fsharp
- match: '[a-zA-Z]\w*'
scope: variable.parameter.fsharp
File diff suppressed because it is too large Load Diff
+398
View File
@@ -0,0 +1,398 @@
%YAML 1.2
---
# http://www.sublimetext.com/docs/3/syntax.html
name: Kotlin
file_extensions:
- kt
- kts
scope: source.Kotlin
contexts:
main:
- include: comments
- match: '^\s*(package)\b(?:\s*([^ ;$]+)\s*)?'
captures:
1: keyword.other.kotlin
2: entity.name.package.kotlin
- include: imports
- include: statements
classes:
- match: (?<!::)(?=\b(?:companion|class|object|interface)\b)
push:
- match: '(?=$|\})'
pop: true
- include: comments
- match: \b(companion\s*)?(class|object|interface)\b
captures:
1: storage.modifier.kotlin
2: storage.modifier.kotlin
push:
- match: '(?=<|\{|\(|:|$)'
pop: true
- include: comments
- match: \w+
scope: entity.name.type.class.kotlin
- match: <
push:
- match: ">"
pop: true
- include: generics
- match: \(
push:
- match: \)
pop: true
- include: parameters
- match: (:)
captures:
1: keyword.operator.declaration.kotlin
push:
- match: "(?={|$)"
pop: true
- match: \w+
scope: entity.other.inherited-class.kotlin
- match: \(
push:
- match: \)
pop: true
- include: expressions
- match: '\{'
push:
- match: '\}'
pop: true
- include: statements
comments:
- match: /\*
captures:
0: punctuation.definition.comment.kotlin
push:
- meta_scope: comment.block.kotlin
- match: \*/
captures:
0: punctuation.definition.comment.kotlin
pop: true
- match: \s*((//).*$\n?)
captures:
1: comment.line.double-slash.kotlin
2: punctuation.definition.comment.kotlin
constants:
- match: \b(true|false|null|this|super)\b
scope: constant.language.kotlin
- match: '\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)([LlFf])?\b'
scope: constant.numeric.kotlin
- match: '\b([A-Z][A-Z0-9_]+)\b'
scope: constant.other.kotlin
expressions:
- match: \(
push:
- match: \)
pop: true
- include: expressions
- include: types
- include: strings
- include: constants
- include: comments
- include: keywords
functions:
- match: (?=\s*\b(?:fun)\b)
push:
- match: '(?=$|\})'
pop: true
- match: \b(fun)\b
captures:
1: keyword.other.kotlin
push:
- match: (?=\()
pop: true
- match: <
push:
- match: ">"
pop: true
- include: generics
- match: '([\.<\?>\w]+\.)?(\w+)'
captures:
2: entity.name.function.kotlin
- match: \(
push:
- match: \)
pop: true
- include: parameters
- match: (:)
captures:
1: keyword.operator.declaration.kotlin
push:
- match: "(?={|=|$)"
pop: true
- include: types
- match: '\{'
push:
- match: '(?=\})'
pop: true
- include: statements
- match: (=)
captures:
1: keyword.operator.assignment.kotlin
push:
- match: (?=$)
pop: true
- include: expressions
generics:
- match: (:)
captures:
1: keyword.operator.declaration.kotlin
push:
- match: (?=,|>)
pop: true
- include: types
- include: keywords
- match: \w+
scope: storage.type.generic.kotlin
getters-and-setters:
- match: \b(get)\b\s*\(\s*\)
captures:
1: entity.name.function.kotlin
push:
- match: '\}|(?=\bset\b)|$'
pop: true
- match: (=)
captures:
1: keyword.operator.assignment.kotlin
push:
- match: (?=$|\bset\b)
pop: true
- include: expressions
- match: '\{'
push:
- match: '\}'
pop: true
- include: expressions
- match: \b(set)\b\s*(?=\()
captures:
1: entity.name.function.kotlin
push:
- match: '\}|(?=\bget\b)|$'
pop: true
- match: \(
push:
- match: \)
pop: true
- include: parameters
- match: (=)
captures:
1: keyword.operator.assignment.kotlin
push:
- match: (?=$|\bset\b)
pop: true
- include: expressions
- match: '\{'
push:
- match: '\}'
pop: true
- include: expressions
imports:
- match: '^\s*(import)\s+[^ $]+\s+(as)?'
captures:
1: keyword.other.kotlin
2: keyword.other.kotlin
keywords:
- match: \b(var|val|public|private|protected|abstract|final|sealed|enum|open|attribute|annotation|override|inline|vararg|in|out|internal|data|tailrec|operator|infix|const|yield|typealias|typeof|reified|suspend)\b
scope: storage.modifier.kotlin
- match: \b(try|catch|finally|throw)\b
scope: keyword.control.catch-exception.kotlin
- match: \b(if|else|while|for|do|return|when|where|break|continue)\b
scope: keyword.control.kotlin
- match: \b(in|is|!in|!is|as|as\?|assert)\b
scope: keyword.operator.kotlin
- match: (==|!=|===|!==|<=|>=|<|>)
scope: keyword.operator.comparison.kotlin
- match: (=)
scope: keyword.operator.assignment.kotlin
- match: (::)
scope: keyword.operator.kotlin
- match: (:)
scope: keyword.operator.declaration.kotlin
- match: \b(by)\b
scope: keyword.other.by.kotlin
- match: (\?\.)
scope: keyword.operator.safenav.kotlin
- match: (\.)
scope: keyword.operator.dot.kotlin
- match: (\?:)
scope: keyword.operator.elvis.kotlin
- match: (\-\-|\+\+)
scope: keyword.operator.increment-decrement.kotlin
- match: (\+=|\-=|\*=|\/=)
scope: keyword.operator.arithmetic.assign.kotlin
- match: (\.\.)
scope: keyword.operator.range.kotlin
- match: (\-|\+|\*|\/|%)
scope: keyword.operator.arithmetic.kotlin
- match: (!|&&|\|\|)
scope: keyword.operator.logical.kotlin
- match: (;)
scope: punctuation.terminator.kotlin
namespaces:
- match: \b(namespace)\b
scope: keyword.other.kotlin
- match: '\{'
push:
- match: '\}'
pop: true
- include: statements
parameters:
- match: (:)
captures:
1: keyword.operator.declaration.kotlin
push:
- match: (?=,|\)|=)
pop: true
- include: types
- match: (=)
captures:
1: keyword.operator.declaration.kotlin
push:
- match: (?=,|\))
pop: true
- include: expressions
- include: keywords
- match: \w+
scope: variable.parameter.function.kotlin
statements:
- include: namespaces
- include: typedefs
- include: classes
- include: functions
- include: variables
- include: getters-and-setters
- include: expressions
strings:
- match: '"""'
captures:
0: punctuation.definition.string.begin.kotlin
push:
- meta_scope: string.quoted.third.kotlin
- match: '"""'
captures:
0: punctuation.definition.string.end.kotlin
pop: true
- match: '(\$\w+|\$\{[^\}]+\})'
scope: variable.parameter.template.kotlin
- match: \\.
scope: constant.character.escape.kotlin
- match: '"'
captures:
0: punctuation.definition.string.begin.kotlin
push:
- meta_scope: string.quoted.double.kotlin
- match: '"'
captures:
0: punctuation.definition.string.end.kotlin
pop: true
- match: '(\$\w+|\$\{[^\}]+\})'
scope: variable.parameter.template.kotlin
- match: \\.
scope: constant.character.escape.kotlin
- match: "'"
captures:
0: punctuation.definition.string.begin.kotlin
push:
- meta_scope: string.quoted.single.kotlin
- match: "'"
captures:
0: punctuation.definition.string.end.kotlin
pop: true
- match: \\.
scope: constant.character.escape.kotlin
- match: "`"
captures:
0: punctuation.definition.string.begin.kotlin
push:
- meta_scope: string.quoted.single.kotlin
- match: "`"
captures:
0: punctuation.definition.string.end.kotlin
pop: true
typedefs:
- match: (?=\s*(?:type))
push:
- match: (?=$)
pop: true
- match: \b(type)\b
scope: keyword.other.kotlin
- match: <
push:
- match: ">"
pop: true
- include: generics
- include: expressions
types:
- match: \b(Nothing|Any|Unit|String|CharSequence|Int|Boolean|Char|Long|Double|Float|Short|Byte|dynamic)\b
scope: storage.type.buildin.kotlin
- match: \b(IntArray|BooleanArray|CharArray|LongArray|DoubleArray|FloatArray|ShortArray|ByteArray)\b
scope: storage.type.buildin.array.kotlin
- match: \b(Array|Collection|List|Map|Set|MutableList|MutableMap|MutableSet|Sequence)<\b
captures:
1: storage.type.buildin.collection.kotlin
push:
- match: ">"
pop: true
- include: types
- include: keywords
- match: \w+<
push:
- match: ">"
pop: true
- include: types
- include: keywords
- match: '\{'
push:
- match: '\}'
pop: true
- include: statements
- match: \(
push:
- match: \)
pop: true
- include: types
- match: (->)
scope: keyword.operator.declaration.kotlin
variables:
- match: (?=\s*\b(?:var|val)\b)
push:
- match: (?=:|=|(\b(by)\b)|$)
pop: true
- match: \b(var|val)\b
captures:
1: keyword.other.kotlin
push:
- match: (?=:|=|(\b(by)\b)|$)
pop: true
- match: <
push:
- match: ">"
pop: true
- include: generics
- match: '([\.<\?>\w]+\.)?(\w+)'
captures:
2: entity.name.variable.kotlin
- match: (:)
captures:
1: keyword.operator.declaration.kotlin
push:
- match: (?==|$)
pop: true
- include: types
- include: getters-and-setters
- match: \b(by)\b
captures:
1: keyword.other.kotlin
push:
- match: (?=$)
pop: true
- include: expressions
- match: (=)
captures:
1: keyword.operator.assignment.kotlin
push:
- match: (?=$)
pop: true
- include: expressions
- include: getters-and-setters
Submodule assets/syntaxes/Nix added at ebf06571f3
+609
View File
@@ -0,0 +1,609 @@
%YAML 1.2
---
# http://www.sublimetext.com/docs/3/syntax.html
name: Nix
file_extensions:
- nix
scope: source.nix
contexts:
main:
- include: expression
comment:
- match: '/\*([^*]|\*[^\/])*'
push:
- meta_scope: comment.block.nix
- match: \*\/
pop: true
- include: comment-remark
- match: '\#'
push:
- meta_scope: comment.line.number-sign.nix
- match: $
pop: true
- include: comment-remark
attribute-bind:
- include: attribute-name
- include: attribute-bind-from-equals
attribute-bind-from-equals:
- match: \=
captures:
0: keyword.operator.bind.nix
push:
- match: \;
captures:
0: punctuation.terminator.bind.nix
pop: true
- include: expression
attribute-inherit:
- match: \binherit\b
captures:
0: keyword.other.inherit.nix
push:
- match: \;
captures:
0: punctuation.terminator.inherit.nix
pop: true
- match: \(
captures:
0: punctuation.section.function.arguments.nix
push:
- match: (?=\;)
pop: true
- match: \)
captures:
0: punctuation.section.function.arguments.nix
push:
- match: (?=\;)
pop: true
- include: bad-reserved
- include: attribute-name-single
- include: others
- include: expression
- match: '(?=[a-zA-Z\_])'
push:
- match: (?=\;)
pop: true
- include: bad-reserved
- include: attribute-name-single
- include: others
- include: others
attribute-name:
- match: '\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*'
scope: entity.other.attribute-name.multipart.nix
- match: \.
- include: string-quoted
- include: interpolation
attribute-name-single:
- match: '\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*'
scope: entity.other.attribute-name.single.nix
attrset-contents:
- include: attribute-inherit
- include: bad-reserved
- include: attribute-bind
- include: others
attrset-definition:
- match: '(?=\{)'
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- match: '(\{)'
captures:
0: punctuation.definition.attrset.nix
push:
- match: '(\})'
captures:
0: punctuation.definition.attrset.nix
pop: true
- include: attrset-contents
- match: '(?<=\})'
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: expression-cont
attrset-definition-brace-opened:
- match: '(?<=\})'
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: expression-cont
- match: (?=.?)
push:
- match: '\}'
captures:
0: punctuation.definition.attrset.nix
pop: true
- include: attrset-contents
attrset-for-sure:
- match: (?=\brec\b)
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- match: \brec\b
captures:
0: keyword.other.nix
push:
- match: '(?=\{)'
pop: true
- include: others
- include: attrset-definition
- include: others
- match: '(?=\{\s*(\}|[^,?]*(=|;)))'
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: attrset-definition
- include: others
attrset-or-function:
- match: '\{'
captures:
0: punctuation.definition.attrset-or-function.nix
push:
- match: '(?=([\])};]|\b(else|then)\b))'
pop: true
- match: '(?=(\s*\}|\"|\binherit\b|\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*(\s*\.|\s*=[^=])))'
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: attrset-definition-brace-opened
- match: '(?=(\.\.\.|\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*\s*[,?]))'
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: function-definition-brace-opened
- include: bad-reserved
- match: '\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*'
captures:
0: variable.parameter.function.maybe.nix
push:
- match: '(?=([\])};]|\b(else|then)\b))'
pop: true
- match: (?=\.)
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: attrset-definition-brace-opened
- match: \s*(\,)
captures:
1: keyword.operator.nix
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: function-definition-brace-opened
- match: (?=\=)
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: attribute-bind-from-equals
- include: attrset-definition-brace-opened
- match: (?=\?)
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: function-parameter-default
- match: \,
captures:
0: keyword.operator.nix
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: function-definition-brace-opened
- include: others
- include: others
bad-reserved:
- match: '(?<![\w''-])(if|then|else|assert|with|let|in|rec|inherit)(?![\w''-])'
scope: invalid.illegal.reserved.nix
comment-remark:
- match: (TODO|FIXME|BUG|\!\!\!):?
captures:
1: markup.bold.comment.nix
constants:
- match: \b(builtins|true|false|null)\b
captures:
0: constant.language.nix
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: expression-cont
- match: \b(scopedImport|import|isNull|abort|throw|baseNameOf|dirOf|removeAttrs|map|toString|derivationStrict|derivation)\b
captures:
0: support.function.nix
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: expression-cont
- match: '\b[0-9]+\b'
captures:
0: constant.numeric.nix
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: expression-cont
expression:
- include: parens-and-cont
- include: list-and-cont
- include: string
- include: interpolation
- include: with-assert
- include: function-for-sure
- include: attrset-for-sure
- include: attrset-or-function
- include: let
- include: if
- include: operator-unary
- include: constants
- include: bad-reserved
- include: parameter-name-and-cont
- include: others
expression-cont:
- match: (?=.?)
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: parens
- include: list
- include: string
- include: interpolation
- include: function-for-sure
- include: attrset-for-sure
- include: attrset-or-function
- match: '(\bor\b|\.|==|!=|!|\<\=|\<|\>\=|\>|&&|\|\||-\>|//|\?|\+\+|-|\*|/(?=([^*]|$))|\+)'
scope: keyword.operator.nix
- include: constants
- include: bad-reserved
- include: parameter-name
- include: others
function-body:
- match: '(@\s*([a-zA-Z\_][a-zA-Z0-9\_\''\-]*)\s*)?(\:)'
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: expression
function-body-from-colon:
- match: (\:)
captures:
0: punctuation.definition.function.nix
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: expression
function-contents:
- include: bad-reserved
- include: function-parameter
- include: others
function-definition:
- match: (?=.?)
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: function-body-from-colon
- match: (?=.?)
push:
- match: (?=\:)
pop: true
- match: '(\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*)'
captures:
0: variable.parameter.function.4.nix
push:
- match: (?=\:)
pop: true
- match: \@
push:
- match: (?=\:)
pop: true
- include: function-header-until-colon-no-arg
- include: others
- include: others
- match: '(?=\{)'
push:
- match: (?=\:)
pop: true
- include: function-header-until-colon-with-arg
- include: others
function-definition-brace-opened:
- match: (?=.?)
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: function-body-from-colon
- match: (?=.?)
push:
- match: (?=\:)
pop: true
- include: function-header-close-brace-with-arg
- match: (?=.?)
push:
- match: '(?=\})'
pop: true
- include: function-contents
- include: others
function-for-sure:
- match: '(?=(\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*\s*[:@]|\{[^}]*\}\s*:|\{[^#}"''/=]*[,\?]))'
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: function-definition
function-header-close-brace-no-arg:
- match: '\}'
captures:
0: punctuation.definition.entity.function.nix
push:
- match: (?=\:)
pop: true
- include: others
function-header-close-brace-with-arg:
- match: '\}'
captures:
0: punctuation.definition.entity.function.nix
push:
- match: (?=\:)
pop: true
- include: function-header-terminal-arg
- include: others
function-header-open-brace:
- match: '\{'
captures:
0: punctuation.definition.entity.function.2.nix
push:
- match: '(?=\})'
pop: true
- include: function-contents
function-header-terminal-arg:
- match: (?=@)
push:
- match: (?=\:)
pop: true
- match: \@
push:
- match: (?=\:)
pop: true
- match: '(\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*)'
push:
- meta_scope: variable.parameter.function.3.nix
- match: (?=\:)
pop: true
- include: others
- include: others
function-header-until-colon-no-arg:
- match: '(?=\{)'
push:
- match: (?=\:)
pop: true
- include: function-header-open-brace
- include: function-header-close-brace-no-arg
function-header-until-colon-with-arg:
- match: '(?=\{)'
push:
- match: (?=\:)
pop: true
- include: function-header-open-brace
- include: function-header-close-brace-with-arg
function-parameter:
- match: (\.\.\.)
push:
- meta_scope: keyword.operator.nix
- match: '(,|(?=\}))'
pop: true
- include: others
- match: '\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*'
captures:
0: variable.parameter.function.1.nix
push:
- match: '(,|(?=\}))'
captures:
0: keyword.operator.nix
pop: true
- include: whitespace
- include: comment
- include: function-parameter-default
- include: expression
- include: others
function-parameter-default:
- match: \?
captures:
0: keyword.operator.nix
push:
- match: "(?=[,}])"
pop: true
- include: expression
if:
- match: (?=\bif\b)
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- match: \bif\b
captures:
0: keyword.other.nix
push:
- match: \bth(?=en\b)
captures:
0: keyword.other.nix
pop: true
- include: expression
- match: (?<=th)en\b
captures:
0: keyword.other.nix
push:
- match: \bel(?=se\b)
captures:
0: keyword.other.nix
pop: true
- include: expression
- match: (?<=el)se\b
captures:
0: keyword.other.nix
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
captures:
0: keyword.other.nix
pop: true
- include: expression
illegal:
- match: .
scope: invalid.illegal
interpolation:
- match: '\$\{'
captures:
0: punctuation.section.embedded.begin.nix
push:
- meta_scope: markup.italic
- match: '\}'
captures:
0: punctuation.section.embedded.end.nix
pop: true
- include: expression
let:
- match: (?=\blet\b)
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- match: \blet\b
captures:
0: keyword.other.nix
push:
- match: '(?=([\])};,]|\b(in|else|then)\b))'
pop: true
- match: '(?=\{)'
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- match: '\{'
push:
- match: '\}'
pop: true
- include: attrset-contents
- match: '(^|(?<=\}))'
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: expression-cont
- include: others
- include: attrset-contents
- include: others
- match: \bin\b
captures:
0: keyword.other.nix
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: expression
list:
- match: '\['
captures:
0: punctuation.definition.list.nix
push:
- match: '\]'
captures:
0: punctuation.definition.list.nix
pop: true
- include: expression
list-and-cont:
- match: '(?=\[)'
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: list
- include: expression-cont
operator-unary:
- match: (!|-)
scope: keyword.operator.unary.nix
others:
- include: whitespace
- include: comment
- include: illegal
parameter-name:
- match: '\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*'
captures:
0: variable.parameter.name.nix
parameter-name-and-cont:
- match: '\b[a-zA-Z\_][a-zA-Z0-9\_\''\-]*'
captures:
0: variable.parameter.name.nix
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: expression-cont
parens:
- match: \(
captures:
0: punctuation.definition.expression.nix
push:
- match: \)
captures:
0: punctuation.definition.expression.nix
pop: true
- include: expression
parens-and-cont:
- match: (?=\()
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: parens
- include: expression-cont
string:
- match: (?=\'\')
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- match: \'\'
captures:
0: punctuation.definition.string.other.start.nix
push:
- meta_scope: string.quoted.other.nix
- match: \'\'(?!\$|\'|\\.)
captures:
0: punctuation.definition.string.other.end.nix
pop: true
- match: \'\'(\$|\'|\\.)
scope: constant.character.escape.nix
- include: interpolation
- include: expression-cont
- match: (?=\")
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: string-quoted
- include: expression-cont
- match: '([a-zA-Z0-9\.\_\-\+]*(\/[a-zA-Z0-9\.\_\-\+]+)+)'
captures:
0: string.unquoted.path.nix
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: expression-cont
- match: '(\<[a-zA-Z0-9\.\_\-\+]+(\/[a-zA-Z0-9\.\_\-\+]+)*\>)'
captures:
0: string.unquoted.spath.nix
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: expression-cont
- match: '([a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\'']+)'
captures:
0: string.unquoted.url.nix
push:
- match: '(?=([\])};,]|\b(else|then)\b))'
pop: true
- include: expression-cont
string-quoted:
- match: \"
captures:
0: punctuation.definition.string.double.start.nix
push:
- meta_scope: string.quoted.double.nix
- match: \"
captures:
0: punctuation.definition.string.double.end.nix
pop: true
- match: \\.
scope: constant.character.escape.nix
- include: interpolation
whitespace:
- match: \s+
with-assert:
- match: '(?<![\w''-])(with|assert)(?![\w''-])'
captures:
0: keyword.other.nix
push:
- match: \;
pop: true
- include: expression
+466
View File
@@ -0,0 +1,466 @@
%YAML 1.2
---
# http://www.sublimetext.com/docs/3/syntax.html
name: PowerShell
file_extensions:
- ps1
- psm1
- psd1
scope: source.powershell
contexts:
main:
- match: "<#"
captures:
0: punctuation.definition.comment.block.begin.powershell
push:
- meta_scope: comment.block.powershell
- match: "#>"
captures:
0: punctuation.definition.comment.block.end.powershell
pop: true
- include: commentEmbeddedDocs
- match: '[2-6]>&1|>>|>|<<|<|>|>\||[1-6]>|[1-6]>>'
scope: keyword.operator.redirection.powershell
- include: commands
- include: commentLine
- include: variable
- include: interpolatedStringContent
- include: function
- include: attribute
- include: UsingDirective
- include: type
- include: hashtable
- include: doubleQuotedString
- include: scriptblock
- include: doubleQuotedStringEscapes
- match: (?<!')'
captures:
0: punctuation.definition.string.begin.powershell
push:
- meta_scope: string.quoted.single.powershell
- match: "'(?!')"
captures:
0: punctuation.definition.string.end.powershell
pop: true
- match: "''"
scope: constant.character.escape.powershell
- match: \@"(?=$)
push:
- meta_scope: string.quoted.double.heredoc.powershell
- match: ^"@
pop: true
- include: variableNoProperty
- include: doubleQuotedStringEscapes
- include: interpolation
- match: \@'(?=$)
push:
- meta_scope: string.quoted.single.heredoc.powershell
- match: ^'@
pop: true
- match: "''"
scope: constant.character.escape.powershell
- include: numericConstant
- match: (@)(\()
captures:
1: keyword.other.array.begin.powershell
2: punctuation.section.group.begin.powershell
push:
- meta_scope: meta.group.array-expression.powershell
- match: \)
captures:
0: punctuation.section.group.end.powershell
pop: true
- include: main
- match: (\$)(\()
comment: "TODO: move to repo; make recursive."
captures:
1: punctuation.definition.variable.powershell
2: punctuation.section.group.begin.powershell
push:
- meta_scope: meta.group.complex.subexpression.powershell
- match: \)
captures:
0: punctuation.section.group.end.powershell
pop: true
- include: main
- match: '(\b(([A-Za-z0-9\-_\.]+)\.(?i:exe|com|cmd|bat))\b)'
scope: support.function.powershell
- match: (?<!\w|-|\.)((?i:begin|break|catch|continue|data|default|define|do|dynamicparam|else|elseif|end|exit|finally|for|from|if|in|inlinescript|parallel|param|process|return|sequence|switch|throw|trap|try|until|var|while)|%|\?)(?!\w)
scope: keyword.control.powershell
- match: '(?<!\w|-|[^\)]\.)((?i:(foreach|where)(?!-object))|%|\?)(?!\w)'
scope: keyword.control.powershell
- match: (?<!\w)(--%)(?!\w)
comment: This should be moved to the repository at some point.
captures:
1: keyword.control.powershell
push:
- match: $
pop: true
- match: .+
scope: string.unquoted.powershell
- match: (?<!\w)((?i:hidden|static))(?!\w)
comment: This should only be relevant inside a class but will require a rework of how classes are matched. This is a temp fix.
scope: storage.modifier.powershell
- match: '(?<!\w|-)((?i:class)|%|\?)(?:\s)+((?:\p{L}|\d|_|-|)+)\b'
comment: capture should be entity.name.type, but it doesn't provide a good color in the default schema.
captures:
1: storage.type.powershell
2: entity.name.function
- match: (?<!\w)-(?i:is(?:not)?|as)\b
scope: keyword.operator.comparison.powershell
- match: '(?<!\w)-(?i:[ic]?(?:eq|ne|[gl][te]|(?:not)?(?:like|match|contains|in)|replace))(?!\p{L})'
scope: keyword.operator.comparison.powershell
- match: '(?<!\w)-(?i:join|split)(?!\p{L})|!'
scope: keyword.operator.unary.powershell
- match: '(?<!\w)-(?i:and|or|not|xor)(?!\p{L})|!'
scope: keyword.operator.logical.powershell
- match: '(?<!\w)-(?i:band|bor|bnot|bxor|shl|shr)(?!\p{L})'
scope: keyword.operator.bitwise.powershell
- match: '(?<!\w)-(?i:f)(?!\p{L})'
scope: keyword.operator.string-format.powershell
- match: "[+%*/-]?=|[+/*%-]"
scope: keyword.operator.assignment.powershell
- match: '\|{2}|&{2}|;'
scope: punctuation.terminator.statement.powershell
- match: '&|(?<!\w)\.(?= )|`|,|\|'
scope: keyword.operator.other.powershell
- match: (?<!\s|^)\.\.(?=\-?\d|\(|\$)
comment: This is very imprecise, is there a syntax for 'must come after...'
scope: keyword.operator.range.powershell
RequiresDirective:
- match: (?<=#)(?i:(requires))\s
captures:
0: keyword.control.requires.powershell
push:
- meta_scope: meta.requires.powershell
- match: $
pop: true
- match: \-(?i:Modules|PSSnapin|RunAsAdministrator|ShellId|Version)
scope: keyword.other.powershell
- match: '(?<!-)\b\p{L}+|\d+(?:\.\d+)*'
scope: variable.parameter.powershell
- include: hashtable
UsingDirective:
- match: (?<!\w)(?i:(using))\s+(?i:(namespace|module))\s+(?i:((?:\w+(?:\.)?)+))
captures:
1: keyword.control.using.powershell
2: keyword.other.powershell
3: variable.parameter.powershell
attribute:
- match: '(\[)\s*\b(?i)(cmdletbinding|alias|outputtype|parameter|validatenotnull|validatenotnullorempty|validatecount|validateset|allownull|allowemptycollection|allowemptystring|validatescript|validaterange|validatepattern|validatelength)\b'
captures:
1: punctuation.section.bracket.begin.powershell
2: support.function.attribute.powershell
push:
- meta_scope: meta.attribute.powershell
- match: '(\])'
captures:
1: punctuation.section.bracket.end.powershell
pop: true
- match: \(
captures:
0: punctuation.section.group.begin.powershell
push:
- match: \)
captures:
0: punctuation.section.group.end.powershell
pop: true
- include: variable
- include: variableNoProperty
- include: hashtable
- include: scriptblock
- include: doubleQuotedStringEscapes
- include: doubleQuotedString
- include: type
- include: numericConstant
- include: doubleQuotedString
- include: main
- match: (?i)\b(mandatory|valuefrompipeline|valuefrompipelinebypropertyname|valuefromremainingarguments|position|parametersetname|defaultparametersetname|supportsshouldprocess|supportspaging|positionalbinding|helpuri|confirmimpact|helpmessage)\b(?:\s+)?(=)?
captures:
1: variable.parameter.attribute.powershell
2: keyword.operator.assignment.powershell
- match: (?<!')'
captures:
0: punctuation.definition.string.begin.powershell
push:
- meta_scope: string.quoted.single.powershell
- match: "'(?!')"
captures:
0: punctuation.definition.string.end.powershell
pop: true
- match: "''"
scope: constant.character.escape.powershell
commands:
- match: '(?:(\p{L}|\d|_|-|\\|\:)*\\)?\b(?i:Add|Approve|Assert|Backup|Block|Build|Checkpoint|Clear|Close|Compare|Complete|Compress|Confirm|Connect|Convert|ConvertFrom|ConvertTo|Copy|Debug|Deny|Deploy|Disable|Disconnect|Dismount|Edit|Enable|Enter|Exit|Expand|Export|Find|Format|Get|Grant|Group|Hide|Import|Initialize|Install|Invoke|Join|Limit|Lock|Measure|Merge|Mount|Move|New|Open|Optimize|Out|Ping|Pop|Protect|Publish|Push|Read|Receive|Redo|Register|Remove|Rename|Repair|Request|Reset|Resize|Resolve|Restart|Restore|Resume|Revoke|Save|Search|Select|Send|Set|Show|Skip|Split|Start|Step|Stop|Submit|Suspend|Switch|Sync|Test|Trace|Unblock|Undo|Uninstall|Unlock|Unprotect|Unpublish|Unregister|Update|Use|Wait|Watch|Write)\-.+?(?:\.(?i:exe|cmd|bat|ps1))?\b'
comment: "Verb-Noun pattern:"
scope: support.function.powershell
- match: (?<!\w)(?i:foreach-object)(?!\w)
comment: Builtin cmdlets with reserved verbs
scope: support.function.powershell
- match: (?<!\w)(?i:where-object)(?!\w)
comment: Builtin cmdlets with reserved verbs
scope: support.function.powershell
- match: (?<!\w)(?i:sort-object)(?!\w)
comment: Builtin cmdlets with reserved verbs
scope: support.function.powershell
- match: (?<!\w)(?i:tee-object)(?!\w)
comment: Builtin cmdlets with reserved verbs
scope: support.function.powershell
commentEmbeddedDocs:
- match: ^(?i:(?:\s?|#)+(\.)(COMPONENT|DESCRIPTION|EXAMPLE|EXTERNALHELP|FORWARDHELPCATEGORY|FORWARDHELPTARGETNAME|FUNCTIONALITY|INPUTS|LINK|NOTES|OUTPUTS|REMOTEHELPRUNSPACE|ROLE|SYNOPSIS))
scope: comment.documentation.embedded.powershell
captures:
1: constant.string.documentation.powershell
2: keyword.operator.documentation.powershell
- match: '(?i:\s?(\.)(PARAMETER|FORWARDHELPTARGETNAME|FORWARDHELPCATEGORY|REMOTEHELPRUNSPACE|EXTERNALHELP)\s+([a-z0-9-_]+))'
scope: comment.documentation.embedded.powershell
captures:
1: constant.string.documentation.powershell
2: keyword.operator.documentation.powershell
3: keyword.operator.documentation.powershell
commentLine:
- match: '(?<![`\\-])#'
captures:
0: punctuation.definition.comment.powershell
push:
- meta_scope: comment.line.powershell
- match: $\n?
captures:
0: punctuation.definition.comment.powershell
pop: true
- include: commentEmbeddedDocs
- include: RequiresDirective
doubleQuotedString:
- match: (?<!(?<!`)")"
captures:
0: punctuation.definition.string.begin.powershell
push:
- meta_scope: string.quoted.double.powershell
- match: '"(?!")'
captures:
0: punctuation.definition.string.end.powershell
pop: true
- match: '(?i)\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,64}\b'
- include: variableNoProperty
- include: variable
- include: doubleQuotedStringEscapes
- include: interpolation
- match: '`\s*$'
scope: keyword.other.powershell
doubleQuotedStringEscapes:
- match: '`[0abnfrvt"''$`]'
scope: constant.character.escape.powershell
- match: '""'
scope: constant.character.escape.powershell
function:
- match: '^(?:\s*+)(?i)(function|filter|configuration|workflow)\s+(?:(global|local|script|private):)?((?:\p{L}|\d|_|-|\.)+)'
captures:
0: meta.function.powershell
1: storage.type.powershell
2: storage.modifier.scope.powershell
3: entity.name.function.powershell
push:
- match: '(?=\{|\()'
pop: true
- include: commentLine
hashtable:
- match: '(@)(\{)'
captures:
1: keyword.other.hashtable.begin.powershell
2: punctuation.section.braces.begin.powershell
push:
- meta_scope: meta.hashtable.powershell
- match: '(\})'
captures:
1: punctuation.section.braces.end.powershell
pop: true
- match: \b((?:\'|\")?)(\w+)((?:\'|\")?)(?:\s+)?(=)(?:\s+)?
scope: meta.hashtable.assignment.powershell
captures:
1: punctuation.definition.string.begin.powershell
2: variable.other.readwrite.powershell
3: punctuation.definition.string.end.powershell
4: keyword.operator.assignment.powershell
- include: scriptblock
- include: main
interpolatedStringContent:
- match: \(
captures:
0: punctuation.section.group.begin.powershell
push:
- meta_content_scope: interpolated.simple.source.powershell
- match: \)
captures:
0: punctuation.section.group.end.powershell
pop: true
- include: main
- include: interpolation
- include: interpolatedStringContent
interpolation:
- match: (\$)(\()
captures:
1: punctuation.definition.variable.powershell
2: punctuation.section.group.begin.powershell
push:
- meta_content_scope: interpolated.complex.source.powershell
- match: \)
captures:
0: punctuation.section.group.end.powershell
pop: true
- include: main
- include: interpolation
- include: interpolatedStringContent
numericConstant:
- match: '(?<!\w)([-+]?0(?:x|X)[0-9a-fA-F_]+(?:U|u|L|l|UL|Ul|uL|ul|LU|Lu|lU|lu)?)((?i:[kmgtp]b)?)\b'
captures:
1: constant.numeric.hex.powershell
2: keyword.other.powershell
- match: '(?<!\w)([-+]?(?:[0-9_]+)?\.[0-9_]+(?:(?:e|E)[0-9]+)?(?:F|f|D|d|M|m)?)((?i:[kmgtp]b)?)\b'
captures:
1: constant.numeric.integer.powershell
2: keyword.other.powershell
- match: '(?<!\w)([-+]?0(?:b|B)[01_]+(?:U|u|L|l|UL|Ul|uL|ul|LU|Lu|lU|lu)?)((?i:[kmgtp]b)?)\b'
captures:
1: constant.numeric.octal.powershell
2: keyword.other.powershell
- match: '(?<!\w)([-+]?[0-9_]+(?:e|E)(?:[0-9_])?+(?:F|f|D|d|M|m)?)((?i:[kmgtp]b)?)\b'
captures:
1: constant.numeric.integer.powershell
2: keyword.other.powershell
- match: '(?<!\w)([-+]?[0-9_]+\.(?:e|E)(?:[0-9_])?+(?:F|f|D|d|M|m)?)((?i:[kmgtp]b)?)\b'
captures:
1: constant.numeric.integer.powershell
2: keyword.other.powershell
- match: '(?<!\w)([-+]?[0-9_]+[\.]?(?:F|f|D|d|M|m))((?i:[kmgtp]b)?)\b'
captures:
1: constant.numeric.integer.powershell
2: keyword.other.powershell
- match: '(?<!\w)([-+]?[0-9_]+[\.]?(?:U|u|L|l|UL|Ul|uL|ul|LU|Lu|lU|lu)?)((?i:[kmgtp]b)?)\b'
captures:
1: constant.numeric.integer.powershell
2: keyword.other.powershell
scriptblock:
- match: '\{'
captures:
0: punctuation.section.braces.begin.powershell
push:
- meta_scope: meta.scriptblock.powershell
- match: '\}'
captures:
0: punctuation.section.braces.end.powershell
pop: true
- include: main
type:
- match: '\['
captures:
0: punctuation.section.bracket.begin.powershell
push:
- match: '\]'
captures:
0: punctuation.section.bracket.end.powershell
pop: true
- match: '(?!\d+|\.)(?:\p{L}|\p{N}|\.)+'
scope: storage.type.powershell
- include: main
variable:
- match: (\$)(?i:(False|Null|True))\b
comment: These are special constants.
captures:
0: constant.language.powershell
1: punctuation.definition.variable.powershell
- match: '(\$)(?i:(Error|ExecutionContext|Host|Home|PID|PsHome|PsVersionTable|ShellID))((?:\.(?:\p{L}|\d|_)+)*\b)?\b'
comment: These are the other built-in constants.
captures:
0: support.constant.variable.powershell
1: punctuation.definition.variable.powershell
3: variable.other.member.powershell
- match: '(\$)(?i:(\$|\^|\?|_|Args|ConsoleFileName|Event|EventArgs|EventSubscriber|ForEach|Input|LastExitCode|Matches|MyInvocation|NestedPromptLevel|Profile|PSBoundParameters|PsCmdlet|PsCulture|PSDebugContext|PSItem|PSCommandPath|PSScriptRoot|PsUICulture|Pwd|Sender|SourceArgs|SourceEventArgs|StackTrace|Switch|This))((?:\.(?:\p{L}|\d|_)+)*\b)?\b'
comment: Automatic variables are not constants, but they are read-only. In monokai (default) color schema support.variable doesn't have color, so we use constant.
captures:
0: support.constant.automatic.powershell
1: punctuation.definition.variable.powershell
3: variable.other.member.powershell
- match: '(\$)(?i:(ConfirmPreference|DebugPreference|ErrorActionPreference|ErrorView|FormatEnumerationLimit|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount|MaximumHistoryCount|MaximumVariableCount|OFS|OutputEncoding|ProgressPreference|PsCulture|PSDebugContext|PSDefaultParameterValues|PSEmailServer|PSItem|PSModuleAutoloadingPreference|PSSenderInfo|PSSessionApplicationName|PSSessionConfigurationName|PSSessionOption|VerbosePreference|WarningPreference|WhatIfPreference))((?:\.(?:\p{L}|\d|_)+)*\b)?\b'
comment: Style preference variables as language variables so that they stand out.
captures:
0: variable.language.powershell
1: punctuation.definition.variable.powershell
3: variable.other.member.powershell
- match: '(?i:(\$|@)(global|local|private|script|using|workflow):((?:\p{L}|\d|_)+))((?:\.(?:\p{L}|\d|_)+)*\b)?'
captures:
0: variable.other.readwrite.powershell
1: punctuation.definition.variable.powershell
2: storage.modifier.scope.powershell
4: variable.other.member.powershell
- match: '(?i:(\$)(\{)(global|local|private|script|using|workflow):([^}]*[^}`])(\}))((?:\.(?:\p{L}|\d|_)+)*\b)?'
captures:
0: variable.other.readwrite.powershell
1: punctuation.definition.variable.powershell
2: punctuation.section.braces.begin.powershell
3: storage.modifier.scope.powershell
5: punctuation.section.braces.end.powershell
6: variable.other.member.powershell
- match: '(?i:(\$|@)((?:\p{L}|\d|_)+:)?((?:\p{L}|\d|_)+))((?:\.(?:\p{L}|\d|_)+)*\b)?'
captures:
0: variable.other.readwrite.powershell
1: punctuation.definition.variable.powershell
2: support.variable.drive.powershell
4: variable.other.member.powershell
- match: '(?i:(\$)(\{)((?:\p{L}|\d|_)+:)?([^}]*[^}`])(\}))((?:\.(?:\p{L}|\d|_)+)*\b)?'
captures:
0: variable.other.readwrite.powershell
1: punctuation.definition.variable.powershell
2: punctuation.section.braces.begin.powershell
3: support.variable.drive.powershell
5: punctuation.section.braces.end.powershell
6: variable.other.member.powershell
variableNoProperty:
- match: (\$)(?i:(False|Null|True))\b
comment: These are special constants.
captures:
0: constant.language.powershell
1: punctuation.definition.variable.powershell
- match: (\$)(?i:(Error|ExecutionContext|Host|Home|PID|PsHome|PsVersionTable|ShellID))\b
comment: These are the other built-in constants.
captures:
0: support.constant.variable.powershell
1: punctuation.definition.variable.powershell
3: variable.other.member.powershell
- match: (\$)(?i:(\$|\^|\?|_|Args|ConsoleFileName|Event|EventArgs|EventSubscriber|ForEach|Input|LastExitCode|Matches|MyInvocation|NestedPromptLevel|Profile|PSBoundParameters|PsCmdlet|PsCulture|PSDebugContext|PSItem|PSCommandPath|PSScriptRoot|PsUICulture|Pwd|Sender|SourceArgs|SourceEventArgs|StackTrace|Switch|This))\b
comment: Automatic variables are not constants, but they are read-only...
captures:
0: support.variable.automatic.powershell
1: punctuation.definition.variable.powershell
3: variable.other.member.powershell
- match: (\$)(?i:(ConfirmPreference|DebugPreference|ErrorActionPreference|ErrorView|FormatEnumerationLimit|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount|MaximumHistoryCount|MaximumVariableCount|OFS|OutputEncoding|ProgressPreference|PsCulture|PSDebugContext|PSDefaultParameterValues|PSEmailServer|PSItem|PSModuleAutoloadingPreference|PSSenderInfo|PSSessionApplicationName|PSSessionConfigurationName|PSSessionOption|VerbosePreference|WarningPreference|WhatIfPreference))\b
comment: Style preference variables as language variables so that they stand out.
captures:
0: variable.language.powershell
1: punctuation.definition.variable.powershell
3: variable.other.member.powershell
- match: '(?i:(\$|@)(global|local|private|script|using|workflow):((?:\p{L}|\d|_)+))'
captures:
0: variable.other.readwrite.powershell
1: punctuation.definition.variable.powershell
2: storage.modifier.scope.powershell
4: variable.other.member.powershell
- match: '(?i:(\$)(\{)(global|local|private|script|using|workflow):([^}]*[^}`])(\}))'
captures:
0: variable.other.readwrite.powershell
1: punctuation.definition.variable.powershell
2: storage.modifier.scope.powershell
4: keyword.other.powershell
5: variable.other.member.powershell
- match: '(?i:(\$)((?:\p{L}|\d|_)+:)?((?:\p{L}|\d|_)+))'
captures:
0: variable.other.readwrite.powershell
1: punctuation.definition.variable.powershell
2: support.variable.drive.powershell
4: variable.other.member.powershell
- match: '(?i:(\$)(\{)((?:\p{L}|\d|_)+:)?([^}]*[^}`])(\}))'
captures:
0: variable.other.readwrite.powershell
1: punctuation.definition.variable.powershell
2: punctuation.section.braces.begin
3: support.variable.drive.powershell
5: punctuation.section.braces.end
+371
View File
@@ -0,0 +1,371 @@
%YAML 1.2
---
# http://www.sublimetext.com/docs/3/syntax.html
name: Swift
file_extensions:
- swift
first_line_match: ^#!/.*\bswift
scope: source.swift
contexts:
main:
- include: shebang-line
- include: comment
- include: attribute
- include: literal
- include: operator
- include: declaration
- include: storage-type
- include: keyword
- include: type
- include: boolean
comment:
- include: documentation-comment
- include: block-comment
- include: in-line-comment
access-level-modifier:
- match: \b(open|public|internal|fileprivate|private)\b(?:\(set\))?
comment: access-level-modifier
scope: keyword.other.access-level-modifier.swift
arithmetic-operator:
- match: '(?<![/=\-+!*%<>&|\^~.])(\+|\-|\*|\/)(?![/=\-+!*%<>&|\^~.])'
scope: keyword.operator.arithmetic.swift
array-type:
- match: \b(Array)(<)
captures:
1: support.type.array.swift
2: punctuation.array.begin.swift
push:
- meta_scope: meta.array.swift
- match: (>)
captures:
1: punctuation.array.end.swift
pop: true
- include: main
assignment-operator:
- match: '(?<![/=\-+!*%<>&|\^~.])(\+|\-|\*|\/|%|<<|>>|&|\^|\||&&|\|\|)?=(?![/=\-+!*%<>&|\^~.])'
scope: keyword.operator.assignment.swift
attribute:
- match: '((@)(\B\$[0-9]+|\b[\w^\d][\w\d]*\b|\B`[\w^\d][\w\d]*`\B))(\()'
captures:
1: storage.modifier.attribute.swift
2: punctuation.definition.attribute.swift
3: punctuation.definition.attribute-arguments.begin.swift
push:
- meta_content_scope: meta.attribute.arguments.swift
- match: \)
captures:
0: punctuation.definition.attribute-arguments.end.swift
pop: true
- include: main
- match: '((@)(\B\$[0-9]+|\b[\w^\d][\w\d]*\b|\B`[\w^\d][\w\d]*`\B))'
captures:
1: storage.modifier.attribute.swift
2: punctuation.definition.attribute.swift
bitwise-operator:
- match: '(?<![/=\-+!*%<>&|\^~.])(&|\||\^|<<|>>)(?![/=\-+!*%<>&|\^~.])'
scope: keyword.operator.bitwise.swift
block-comment:
- match: /\*
comment: Block comment
captures:
0: punctuation.definition.comment.block.begin.swift
push:
- meta_scope: comment.block.swift
- match: \*/
captures:
0: punctuation.definition.comment.block.end.swift
pop: true
boolean:
- match: \b(true|false)\b
scope: keyword.constant.boolean.swift
branch-statement-keyword:
- include: if-statement-keyword
- include: switch-statement-keyword
catch-statement-keyword:
- match: \b(catch|do)\b
comment: catch-statement
scope: kewyord.control.catch.swift
code-block:
- match: '(\{)'
comment: code-block
captures:
1: punctuation.definition.code-block.begin.swift
push:
- match: '(\})'
captures:
1: punctuation.definition.code-block.end.swift
pop: true
- include: main
collection-type:
- include: array-type
- include: dictionary-type
- match: \b(Array|Dictionary)\b
scope: support.type.swift
comparative-operator:
- match: '(?<![/=\-+!*%<>&|\^~.])((=|!)==?|(<|>)=?|~=)(?![/=\-+!*%<>&|\^~.])'
scope: keyword.operator.comparative.swift
control-transfer-statement-keyword:
- match: \b(continue|break|fallthrough|return)\b
comment: control-transfer-statement
scope: keyword.control.transfer.swift
custom-operator:
- match: '(?<=[\s(\[{,;:])([/=\-+!*%<>&|\^~.]++)(?![\s)\]},;:])'
scope: keyword.operator.custom.prefix.unary.swift
- match: '(?<![\s(\[{,;:])([/=\-+!*%<>&|\^~.]++)(?![\s)\]},;:\.])'
scope: keyword.operator.custom.postfix.unary.swift
- match: '(?<=[\s(\[{,;:])([/=\-+!*%<>&|\^~.]++)(?=[\s)\]},;:])'
scope: keyword.operator.custom.binary.swift
declaration:
- include: import-declaration
- include: function-declaration
declaration-modifier:
- match: \b(class|convenience|dynamic|final|lazy|(non)?mutating|optional|override|required|static|unowned((un)?safe)?|weak)\b
comment: declaration-modifier
scope: keyword.other.declaration-modifier.swift
dictionary-type:
- match: \b(Dictionary)(<)
captures:
1: support.type.dictionary.swift
2: punctuation.dictionary.begin.swift
push:
- meta_scope: meta.dictionary.swift
- match: (>)
captures:
1: punctuation.dictionary.end.swift
pop: true
- include: main
documentation-comment:
- match: /\*\*
comment: Documentation comment
captures:
0: punctuation.definition.comment.block.documentation.begin.swift
push:
- meta_scope: comment.block.documentation.swift
- match: \*/
captures:
0: punctuation.definition.comment.block.documentation.end.swift
pop: true
floating-point-literal:
- match: '\b([0-9][0-9_]*)(\.([0-9][0-9_]*))?([eE][+\-]?([0-9][0-9_]*))?\b'
comment: floating-point-literal -> (decimal-literal)(decimal-fraction)?(decimal-exponent)?
- match: '\b(0x\h[\h_]*)(\.(0x\h[\h_]*))?([pP][+\-]?(0x\h[\h_]*))\b'
comment: floating-point-literal -> (hexadecimal-literal)(hexadecimal-fraction)?(hexadecimal-exponent)
function-body:
- include: code-block
function-declaration:
- match: '\b(func)\s+(\B\$[0-9]+|\b[\w^\d][\w\d]*\b|\B`[\w^\d][\w\d]*`\B|[/=\-+!*%<>&|\^~.]+)\s*(?=\(|<)'
comment: function-declaration
captures:
1: storage.type.function.swift
2: entity.type.function.swift
push:
- meta_scope: meta.function-declaration.swift
- match: '(?<=\})'
pop: true
- include: generic-parameter-clause
- include: parameter-clause
- include: function-result
- include: function-body
function-result:
- match: '(?<![/=\-+!*%<>&|\^~.])(\->)(?![/=\-+!*%<>&|\^~.])\s*'
comment: function-result
captures:
1: keyword.operator.function-result.swift
push:
- meta_scope: meta.function-result.swift
- match: '\s*(?=\{)'
pop: true
- include: type
generic-parameter-clause:
- match: (<)
comment: generic-parameter-clause
captures:
1: punctuation.definition.generic-parameter-clause.begin.swift
push:
- meta_scope: meta.generic-parameter-clause.swift
- match: (>)
captures:
1: punctuation.definition.generic-parameter-clause.end.swift
pop: true
- include: main
identifier:
- match: '(\B\$[0-9]+|\b[\w^\d][\w\d]*\b|\B`[\w^\d][\w\d]*`\B)'
comment: identifier
scope: meta.identifier.swift
if-statement-keyword:
- match: \b(if|else)\b
comment: if-statement
scope: keyword.control.if.swift
import-declaration:
- match: '\b(import)\s+(?:(typealias|struct|class|enum|protocol|var|func)\s+)?((?:\B\$[0-9]+|\b[\w^\d][\w\d]*\b|\B`[\w^\d][\w\d]*`\B|[/=\-+!*%<>&|\^~.]+)(?:\.(?:\B\$[0-9]+|\b[\w^\d][\w\d]*\b|\B`[\w^\d][\w\d]*`\B|[/=\-+!*%<>&|\^~.]+))*)'
comment: import-declaration
scope: meta.import.swift
captures:
1: keyword.other.import.swift
2: storage.modifier.swift
3: support.type.module.import.swift
in-line-comment:
- match: (//).*
comment: In-line comment
scope: comment.line.double-slash.swift
captures:
1: punctuation.definition.comment.line.double-slash.swift
increment-decrement-operator:
- match: '(?<![/=\-+!*%<>&|\^~.])(\+\+|\-\-)(?![/=\-+!*%<>&|\^~.])'
scope: keyword.operator.increment-or-decrement.swift
integer-literal:
- match: '(\B\-|\b)(0b[01][01_]*)\b'
comment: binary-literal
scope: constant.numeric.integer.binary.swift
- match: '(\B\-|\b)(0o[0-7][0-7_]*)\b'
comment: octal-literal
scope: constant.numeric.integer.octal.swift
- match: '(\B\-|\b)([0-9][0-9_]*)\b'
comment: decimal-literal
scope: constant.numeric.integer.decimal.swift
- match: '(\B\-|\b)(0x\h[\h_]*)\b'
comment: hexadecimal-literal
scope: constant.numeric.integer.hexadecimal.swift
integer-type:
- match: \bU?Int(8|16|32|64)?\b
comment: Int types
scope: support.type.swift
keyword:
- include: branch-statement-keyword
- include: control-transfer-statement-keyword
- include: loop-statement-keyword
- include: catch-statement-keyword
- include: operator-declaration-modifier
- include: declaration-modifier
- include: access-level-modifier
- match: \b(class|deinit|enum|extension|func|import|init|let|protocol|static|struct|subscript|typealias|var|throws|rethrows)\b
comment: declaration keyword
scope: keyword.declaration.swift
- match: \b(break|case|continue|default|do|else|fallthrough|if|in|for|return|switch|where|while|repeat|catch|guard|defer|try|throw)\b
comment: statement keyword
scope: keyword.statement.swift
- match: \b(as|dynamicType|is|new|super|self|Self|Type)\b
comment: expression and type keyword
scope: keyword.other.statement.swift
- match: \b(associativity|didSet|get|infix|inout|left|mutating|none|nonmutating|operator|override|postfix|precedence|prefix|right|set|unowned((un)?safe)?|weak|willSet)\b
comment: other keyword
scope: keyword.other.swift
literal:
- include: integer-literal
- include: floating-point-literal
- include: nil-literal
- include: string-literal
- include: special-literal
logical-operator:
- match: '(?<![/=\-+!*%<>&|\^~.])(!|&&|\|\|)(?![/=\-+!*%<>&|\^~.])'
scope: keyword.operator.logical.swift
loop-statement-keyword:
- match: \b(while|repeat|for|in)\b
comment: loop-statement
scope: keyword.control.loop.swift
nil-literal:
- match: \bnil\b
comment: nil-literal
scope: constant.nil.swift
operator:
- include: comparative-operator
- include: assignment-operator
- include: logical-operator
- include: remainder-operator
- include: increment-decrement-operator
- include: overflow-operator
- include: range-operator
- include: bitwise-operator
- include: arithmetic-operator
- include: ternary-operator
- include: type-casting-operator
- include: custom-operator
operator-declaration-modifier:
- match: \b(operator|prefix|infix|postfix)\b
comment: operator-declaration
scope: keyword.other.operator.swift
optional-type:
- match: \b(Optional)(<)
scope: meta.optional.swift
overflow-operator:
- match: '(?<![/=\-+!*%<>&|\^~.])\&(\+|\-|\*|\/|%)(?![/=\-+!*%<>&|\^~.])'
scope: keyword.operator.overflow.swift
parameter-clause:
- match: (\()
comment: parameter-clause
captures:
1: punctuation.definition.function-arguments.begin.swift
push:
- meta_scope: meta.parameter-clause.swift
- match: (\))
captures:
1: punctuation.definition.function-arguments.end.swift
pop: true
- include: main
primitive-type:
- match: \b(Int|Float|Double|String|Bool|Character|Void)\b
comment: Primitive types
scope: support.type.swift
protocol-composition-type:
- match: \b(protocol)(<)
scope: meta.protocol.swift
range-operator:
- match: '(?<![/=\-+!*%<>&|\^~.])\.\.(?:\.)?(?![/=\-+!*%<>&|\^~.])'
scope: keyword.operator.range.swift
remainder-operator:
- match: '(?<![/=\-+!*%<>&|\^~.])\%(?![/=\-+!*%<>&|\^~.])'
scope: keyword.operator.remainder.swift
shebang-line:
- match: ^(#!).*$
comment: Shebang line
scope: comment.line.shebang.swift
captures:
1: punctuation.definition.comment.line.shebang.swift
special-literal:
- match: \b__(FILE|LINE|COLUMN|FUNCTION)__\b
scope: keyword.other.literal.swift
storage-type:
- match: \b(var|func|let|class|enum|struct|protocol|extension|typealias)\b
scope: storage.type.swift
string-literal:
- match: \"
captures:
0: string.quoted.double.swift
push:
- meta_scope: meta.literal.string.swift
- match: \"
captures:
0: string.quoted.double.swift
pop: true
- match: '\\([0tnr\"\''\\]|x\h{2}|u\h{4}|U\h{8})'
scope: constant.character.escape.swift
- match: (\\\()
captures:
1: support.punctuation.expression.begin.swift
push:
- meta_content_scope: meta.expression.swift
- match: (\))
captures:
1: support.punctuation.expression.end.swift
pop: true
- include: scope:source.swift
- match: (\"|\\)
scope: invalid.illegal.swift
- match: (.)
scope: string.quoted.double.swift
switch-statement-keyword:
- match: \b(switch|case|default|where)\b
comment: switch-statement
scope: keyword.control.switch.swift
ternary-operator:
- match: '(?<=[\s(\[{,;:])(\?|:)(?=[\s)\]},;:])'
scope: keyword.operator.ternary.swift
type:
- include: primitive-type
- include: integer-type
- include: collection-type
- include: optional-type
- include: protocol-composition-type
type-casting-operator:
- match: \b(is\b|as(\?\B|\b))
scope: keyword.operator.type-casting.swift
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+5
View File
@@ -0,0 +1,5 @@
// Output the square of a number.
fn print_square(num: f64) {
let result = f64::powf(num, 2.0);
println!("The square of {:.2} is {:.2}.", num, result);
}
BIN
View File
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
sublime-monokai-extended/Monokai Extended.tmTheme
+31 -5
View File
@@ -11,19 +11,29 @@ pack() {
local tempdir
local out_dir
local package_name
local gcc_prefix
tempdir=$(mktemp -d 2>/dev/null || mktemp -d -t tmp)
out_dir=$(pwd)
package_name="$PROJECT_NAME-$TRAVIS_TAG-$TARGET"
if [[ $TARGET == "arm-unknown-linux-gnueabihf" ]]; then
gcc_prefix="arm-linux-gnueabihf-"
elif [[ $TARGET == "aarch64-unknown-linux-gnu" ]]; then
gcc_prefix="aarch64-linux-gnu-"
else
gcc_prefix=""
fi
# create a "staging" directory
mkdir "$tempdir/$package_name"
# copying the main binary
cp "target/$TARGET/release/$PROJECT_NAME" "$tempdir/$package_name/"
strip "$tempdir/$package_name/$PROJECT_NAME"
"${gcc_prefix}"strip "$tempdir/$package_name/$PROJECT_NAME"
# readme and license
# manpage, readme and license
cp "doc/$PROJECT_NAME.1" "$tempdir/$package_name"
cp README.md "$tempdir/$package_name"
cp LICENSE-MIT "$tempdir/$package_name"
cp LICENSE-APACHE "$tempdir/$package_name"
@@ -41,17 +51,28 @@ make_deb() {
local version
local dpkgname
local conflictname
local gcc_prefix
case $TARGET in
x86_64*)
architecture=amd64
gcc_prefix=""
;;
i686*)
architecture=i386
gcc_prefix=""
;;
aarch64*)
architecture=arm64
gcc_prefix="aarch64-linux-gnu-"
;;
arm*hf)
architecture=armhf
gcc_prefix="arm-linux-gnueabihf-"
;;
*)
echo "ERROR: unknown target" >&2
return 1
echo "make_deb: skipping target '${TARGET}'" >&2
return 0
;;
esac
version=${TRAVIS_TAG#v}
@@ -67,7 +88,11 @@ make_deb() {
# copy the main binary
install -Dm755 "target/$TARGET/release/$PROJECT_NAME" "$tempdir/usr/bin/$PROJECT_NAME"
strip "$tempdir/usr/bin/$PROJECT_NAME"
"${gcc_prefix}"strip "$tempdir/usr/bin/$PROJECT_NAME"
# manpage
install -Dm644 "doc/$PROJECT_NAME.1" "$tempdir/usr/share/man/man1/$PROJECT_NAME.1"
gzip --best "$tempdir/usr/share/man/man1/$PROJECT_NAME.1"
# readme and license
install -Dm644 README.md "$tempdir/usr/share/doc/$PROJECT_NAME/README.md"
@@ -86,6 +111,7 @@ Architecture: $architecture
Provides: $PROJECT_NAME
Conflicts: $conflictname
Description: A cat(1) clone with wings.
A cat(1) clone with syntax highlighting and Git integration.
EOF
fakeroot dpkg-deb --build "$tempdir" "${dpkgname}_${version}_${architecture}.deb"
+26
View File
@@ -0,0 +1,26 @@
# This script takes care of packaging the build artifacts that will go in the
# release zipfile
$SRC_DIR = $PWD.Path
$STAGE = [System.Guid]::NewGuid().ToString()
Set-Location $ENV:Temp
New-Item -Type Directory -Name $STAGE
Set-Location $STAGE
$ZIP = "$SRC_DIR\$($Env:CRATE_NAME)-$($Env:APPVEYOR_REPO_TAG_NAME)-$($Env:TARGET).zip"
Copy-Item "$SRC_DIR\target\release\bat.exe" '.\'
# readme and license
Copy-Item $SRC_DIR\README.md '.\'
Copy-Item $SRC_DIR\LICENSE-MIT '.\'
Copy-Item $SRC_DIR\LICENSE-APACHE '.\'
7z a "$ZIP" *
Push-AppveyorArtifact "$ZIP"
Set-Location ..
Remove-Item $STAGE -Force -Recurse
Set-Location $SRC_DIR
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
set -ex
if [ "$TRAVIS_OS_NAME" != linux ]; then
exit 0
fi
sudo apt-get update
# needed for musl targets
sudo apt-get install -y musl-tools
# needed to build deb packages
sudo apt-get install -y fakeroot
# needed for i686 linux gnu target
if [[ $TARGET == i686-unknown-linux-gnu ]]; then
sudo apt-get install -y gcc-multilib
fi
# needed for cross-compiling for arm
if [[ $TARGET == arm-unknown-linux-gnueabihf ]]; then
sudo apt-get install -y \
gcc-4.8-arm-linux-gnueabihf \
binutils-arm-linux-gnueabihf \
libc6-armhf-cross \
libc6-dev-armhf-cross
fi
# needed for cross-compiling for arm64
if [[ $TARGET == aarch64-unknown-linux-gnu ]]; then
sudo apt-get install -y \
gcc-4.8-aarch64-linux-gnu \
binutils-aarch64-linux-gnu \
gcc-aarch64-linux-gnu
fi
Executable
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -ex
# Incorporate TARGET env var to the build and test process
cargo build --target "$TARGET" --verbose
# We cannot run arm executables on linux
if [[ $TARGET != arm-unknown-linux-gnueabihf ]] && [[ $TARGET != aarch64-unknown-linux-gnu ]]; then
cargo test --target "$TARGET" --verbose
# Run 'bat' on its own source code and the README
cargo run --target "$TARGET" -- src/main.rs README.md --paging=never
fi
+5 -2
View File
@@ -6,10 +6,10 @@ if you are not looking for a program like `bat`, this comparison might not be fo
| | bat | [pygments](http://pygments.org/) | [highlight](http://www.andre-simon.de/doku/highlight/highlight.php) | [ccat](https://github.com/jingweno/ccat) | [source-highlight](https://www.gnu.org/software/src-highlite/) | [hicat](https://github.com/rstacruz/hicat) | [coderay](https://github.com/rubychan/coderay) | [rouge](https://github.com/jneen/rouge) |
|----------------------------------------------|---------------------------------------------------------------------|----------------------------------|---------------------------------------------------------------------|------------------------------------------|----------------------------------------------------------------|-----------------------------------------------------|-----------------------------------------------------|-----------------------------------------------------|
| Drop-in `cat` replacement | (:heavy_check_mark:) [*](https://github.com/sharkdp/bat/issues/134) | :x: | :x: | (:heavy_check_mark:) | :x: | :x: [*](https://github.com/rstacruz/hicat/issues/6) | :x: | :x: |
| Drop-in `cat` replacement | :heavy_check_mark: [*](https://github.com/sharkdp/bat/issues/134) | :x: | :x: | (:heavy_check_mark:) | :x: | :x: [*](https://github.com/rstacruz/hicat/issues/6) | :x: | :x: |
| Git integration | :heavy_check_mark: | :x: | :x: | :x: | :x: | :x: | :x: | :x: |
| Automatic paging | :heavy_check_mark: | :x: | :x: | :x: | :x: | :heavy_check_mark: | :x: | :x: |
| Languages (circa) | 60 | 300 | 200 | 7 | 80 | 130 | 30 | 130 |
| Languages (circa) | 100 | 300 | 200 | 7 | 80 | 130 | 30 | 130 |
| Extensible (languages, themes) | :heavy_check_mark: | (:heavy_check_mark:) | (:heavy_check_mark:) | :x: | (:heavy_check_mark:) | :x: | :x: | :x: |
| Advanced highlighting (e.g. nested syntaxes) | :heavy_check_mark: | :heavy_check_mark: | (:heavy_check_mark:) ? | :x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Execution time [ms] (`jquery-3.3.1.js`) | 624 | 789 | 400 | 80 | 300 | 316 | 157 | 695 |
@@ -18,3 +18,6 @@ if you are not looking for a program like `bat`, this comparison might not be fo
If you think that some entries in this table are outdated or wrong, please open a ticket or pull
request.
Some other alternatives that are also related, but not yet included in the table:
- [lesspipe](https://github.com/wofr06/lesspipe)
+117
View File
@@ -0,0 +1,117 @@
.TH BAT "1"
.SH NAME
bat \- manual page for bat
.SH DESCRIPTION
bat - a cat(1) clone with syntax highlighting and Git integration.
.SH "USAGE"
.IP
bat [OPTIONS] [FILE]...
.IP
bat <SUBCOMMAND>
.SH "OPTIONS"
.HP
\fB\-l\fR, \fB\-\-language\fR <language>
.IP
Explicitly set the language for syntax highlighting. The language can be
specified as a name (like 'C++' or 'LaTeX') or possible file extension (like
\&'cpp', 'hpp' or 'md'). Use '\-\-list\-languages' to show all supported language
names and file extensions.
.HP
\fB\-\-list\-languages\fR
.IP
Display a list of supported languages for syntax highlighting.
.HP
\fB\-\-theme\fR <theme>
.IP
Set the theme for syntax highlighting. Use '\-\-list\-themes' to see all
available themes. To set a default theme, export the BAT_THEME environment
variable (e.g.: export BAT_THEME="TwoDark").
.HP
\fB\-\-list\-themes\fR
.IP
Display a list of supported themes for syntax highlighting.
.HP
\fB\-\-style\fR <style\-components>
.IP
Configure which elements (line numbers, file headers, grid borders, Git
modifications, ..) to display in addition to the file contents. The argument
is a comma\-separated list of components to display (e.g.
\&'numbers,changes,grid') or a pre\-defined style ('full'). To set a default
theme, export the BAT_STYLE environment variable (e.g.: export
BAT_STYLE="numbers"). [possible values: auto, full, plain, changes, header,
grid, numbers]
.HP
\fB\-p\fR
.IP
Only show plain style, no decorations. This is an alias for '\-\-style=plain'
.HP
\fB\-n\fR, \fB\-\-number\fR
.IP
Only show line numbers, no other decorations. This is an alias for
\&'\-\-style=numbers'
.HP
\fB\-\-line\-range\fR <N:M>
.IP
Only print the specified range of lines for each file. For example:
.RS
.IP "\-\-line\-range 30:40"
prints lines 30 to 40
.IP "\-\-line\-range :40"
prints lines 1 to 40
.IP "\-\-line\-range 40:"
prints lines 40 to the end of the file
.RE
.HP
\fB\-\-color\fR <when>
.IP
Specify when to use colored output. The automatic mode only enables colors if
an interactive terminal is detected. [default: auto] [possible values: auto,
never, always]
.HP
\fB\-\-decorations\fR <when>
.IP
Specify when to use the decorations that have been specified via '\-\-style'.
The automatic mode only enables decorations if an interactive terminal is
detected. [default: auto] [possible values: auto, never, always]
.HP
\fB\-\-paging\fR <when>
.IP
Specify when to use the pager. To control which pager is used, set the PAGER
or BAT_PAGER environment variables (the latter takes precedence). The default
pager is 'less'. To disable the pager permanently, set BAT_PAGER to an empty
string. [default: auto] [possible values: auto, never, always]
.HP
\fB\-\-wrap\fR <mode>
.TP
Specify the text\-wrapping mode. [default: auto]
[possible values: auto,
.IP
never, character]
.HP
\fB\-u\fR
.IP
This option exists for POSIX\-compliance reasons ('u' is for 'unbuffered'). The
output is always unbuffered \- this option is simply ignored.
.HP
\fB\-\-tabs\fR <tabs>
.IP
Sets the tab width. Use a width of 0 to pass tabs through directly
.HP
\fB\-h\fR, \fB\-\-help\fR
.IP
Print this help message.
.HP
\fB\-V\fR, \fB\-\-version\fR
.IP
Show version information.
.SH "ARGS"
.IP
<FILE>...
.IP
File(s) to print / concatenate. Use a dash ('\-') or no argument at all to read
from standard input.
.SH "SUBCOMMANDS"
.IP
cache
.IP
Modify the syntax\-definition and theme cache. See "bat cache --help" for more information
+391 -234
View File
@@ -1,14 +1,88 @@
use atty::{self, Stream};
use clap::{App as ClapApp, AppSettings, Arg, ArgGroup, ArgMatches, SubCommand};
use console::Term;
use errors::*;
use std::collections::HashSet;
use std::env;
use style::{OutputComponent, OutputComponents, OutputWrap};
use std::path::Path;
use std::str::FromStr;
use atty::{self, Stream};
use clap::{App as ClapApp, AppSettings, Arg, ArgGroup, ArgMatches, SubCommand};
use console::Term;
#[cfg(windows)]
use ansi_term;
use assets::BAT_THEME_DEFAULT;
use errors::*;
use line_range::LineRange;
use style::{OutputComponent, OutputComponents, OutputWrap};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PagingMode {
Always,
QuitIfOneScreen,
Never,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum InputFile<'a> {
StdIn,
Ordinary(&'a str),
ThemePreviewFile,
}
#[derive(Clone)]
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>,
/// The character width of the terminal
pub term_width: usize,
/// The width of tab characters.
/// Currently, a value of 0 will cause tabs to be passed through without expanding them.
pub tab_width: usize,
/// Whether or not to simply loop through all input (`cat` mode)
pub loop_through: bool,
/// Whether or not the output should be colorized
pub colored_output: bool,
/// Whether or not the output terminal supports true color
pub true_color: bool,
/// Style elements (grid, line numbers, ...)
pub output_components: OutputComponents,
/// Text wrapping mode
pub output_wrap: OutputWrap,
/// Pager or STDOUT
pub paging_mode: PagingMode,
/// The range lines that should be printed, if specified
pub line_range: Option<LineRange>,
/// The syntax highlighting theme
pub theme: String,
}
fn is_truecolor_terminal() -> bool {
env::var("COLORTERM")
.map(|colorterm| colorterm == "truecolor" || colorterm == "24bit")
.unwrap_or(false)
}
/// Helper function that should might appear in Rust stable at some point
/// (https://doc.rust-lang.org/stable/std/option/enum.Option.html#method.transpose)
fn transpose<T>(opt: Option<Result<T>>) -> Result<Option<T>> {
opt.map_or(Ok(None), |res| res.map(Some))
}
pub struct App {
pub matches: ArgMatches<'static>,
interactive_output: bool,
@@ -16,10 +90,10 @@ pub struct App {
impl App {
pub fn new() -> Self {
let interactive_output = atty::is(Stream::Stdout);
#[cfg(windows)]
let interactive_output = interactive_output && ansi_term::enable_ansi_support().is_ok();
let _ = ansi_term::enable_ansi_support();
let interactive_output = atty::is(Stream::Stdout);
App {
matches: Self::matches(interactive_output),
@@ -34,6 +108,10 @@ impl App {
AppSettings::ColorNever
};
// Check if the current directory contains a file name cache, if it does
// do not make the arguements for subcommand 'cache' required.
let arg_group_required = !Path::new("cache").exists();
ClapApp::new(crate_name!())
.version(crate_version!())
.global_setting(clap_color_setting)
@@ -48,188 +126,264 @@ impl App {
.about(
"A cat(1) clone with wings.\n\n\
Use '--help' instead of '-h' to see a more detailed version of the help text.",
).long_about("A cat(1) clone with syntax highlighting and Git integration.")
.arg(
Arg::with_name("FILE")
.help("File(s) to print / concatenate. Use '-' for standard input.")
.long_help(
"File(s) to print / concatenate. Use a dash ('-') or no argument at all \
to read from standard input.",
).multiple(true)
.empty_values(false),
)
.long_about("A cat(1) clone with syntax highlighting and Git integration.")
.arg(
Arg::with_name("language")
.short("l")
.long("language")
.overrides_with("language")
.help("Set the language for highlighting")
.help("Set the language for syntax highlighting.")
.long_help(
"Set the language for syntax highlighting. The language can be \
specified as a name (like 'C++' or 'LaTeX') or possible file \
extension (like 'cpp', 'hpp' or 'md'). Use '--list-languages' \
to show all supported language names and file extensions",
)
.takes_value(true),
)
.arg(
Arg::with_name("FILE")
.help("File(s) to print / concatenate. Use '-' for standard input.")
.long_help(
"File(s) to print. Use no argument or '-' to read from standard \
input",
)
.multiple(true)
.empty_values(false),
)
.arg(
Arg::with_name("style")
.long("style")
.value_name("style-components")
.use_delimiter(true)
.takes_value(true)
.possible_values(&[
"auto", "full", "plain", "changes", "header", "grid", "numbers",
])
.default_value("auto")
.help("Comma-separated list of style elements to display")
.long_help(
"Configure which elements (line numbers, file headers, grid \
"Explicitly set the language for syntax highlighting. The language can be \
specified as a name (like 'C++' or 'LaTeX') or possible file extension \
(like 'cpp', 'hpp' or 'md'). Use '--list-languages' to show all supported \
language names and file extensions."
).takes_value(true),
).arg(
Arg::with_name("list-languages")
.long("list-languages")
.conflicts_with("list-themes")
.help("Display all supported languages.")
.long_help("Display a list of supported languages for syntax highlighting."),
).arg(
Arg::with_name("theme")
.long("theme")
.overrides_with("theme")
.takes_value(true)
.help("Set the color theme for syntax highlighting.")
.long_help(
"Set the theme for syntax highlighting. Use '--list-themes' to \
see all available themes. To set a default theme, export the \
BAT_THEME environment variable (e.g.: export \
BAT_THEME=\"TwoDark\").",
),
).arg(
Arg::with_name("list-themes")
.long("list-themes")
.help("Display all supported highlighting themes.")
.long_help("Display a list of supported themes for syntax highlighting."),
).arg(
Arg::with_name("style")
.long("style")
.value_name("style-components")
.use_delimiter(true)
.takes_value(true)
.possible_values(&[
"auto", "full", "plain", "changes", "header", "grid", "numbers",
])
.help("Comma-separated list of style elements to display.")
.long_help(
"Configure which elements (line numbers, file headers, grid \
borders, Git modifications, ..) to display in addition to the \
file contents. The argument is a comma-separated list of \
components to display (e.g. 'numbers,changes,grid') or a \
pre-defined style ('full')",
),
)
.arg(
Arg::with_name("color")
.long("color")
.overrides_with("color")
.takes_value(true)
.value_name("when")
.possible_values(&["auto", "never", "always"])
.default_value("auto")
.help("When to use colors"),
)
.arg(
Arg::with_name("paging")
.long("paging")
.overrides_with("paging")
.takes_value(true)
.value_name("when")
.possible_values(&["auto", "never", "always"])
.default_value("auto")
.help("When to use the pager")
.long_help("Specify when to use the pager (less)"),
)
.arg(
Arg::with_name("wrap")
.long("wrap")
.overrides_with("wrap")
.takes_value(true)
.value_name("mode")
.possible_values(&["character", "never"])
.default_value("character")
.help("When to wrap text"),
)
.arg(
Arg::with_name("list-languages")
.long("list-languages")
.help("Displays supported languages")
.long_help("Display a list of supported languages"),
)
.arg(
Arg::with_name("theme")
.long("theme")
.overrides_with("theme")
.takes_value(true)
.help("Set the theme for highlighting")
.long_help(
"Set the theme for syntax highlighting. Use '--list-themes' to \
see all available themes",
),
)
.arg(
Arg::with_name("list-themes")
.long("list-themes")
.help("Displays supported themes")
.help("Display a list of supported themes for syntax highlighting"),
)
.arg(
Arg::with_name("number")
.long("number")
.overrides_with("number")
.short("n")
.conflicts_with("style")
.help("Show line numbers (alias for '--style=numbers')")
.long_help(
"Show line numbers (no other decorations). This is an alias for \
pre-defined style ('full'). To set a default theme, export the \
BAT_STYLE environment variable (e.g.: export BAT_STYLE=\"numbers\").",
),
).arg(
Arg::with_name("plain")
.overrides_with("plain")
.short("p")
.long("plain")
.conflicts_with("style")
.conflicts_with("number")
.help("Show plain style (alias for '--style=plain').")
.long_help(
"Only show plain style, no decorations. This is an alias for \
'--style=plain'",
),
).arg(
Arg::with_name("number")
.long("number")
.overrides_with("number")
.short("n")
.conflicts_with("style")
.help("Show line numbers (alias for '--style=numbers').")
.long_help(
"Only show line numbers, no other decorations. This is an alias for \
'--style=numbers'",
),
)
.arg(
Arg::with_name("unbuffered")
.short("u")
.help("(ignored)")
// TODO: use '.hidden_short_help(true)' when the next clap version is released
.long_help(
"This option exists for POSIX-compliance reasons ('u' is for \
),
).arg(
Arg::with_name("line-range")
.long("line-range")
.overrides_with("line-range")
.takes_value(true)
.value_name("N:M")
.help("Only print the lines from N to M.")
.long_help(
"Only print the specified range of lines for each file. \
For example:\n \
'--line-range 30:40' prints lines 30 to 40\n \
'--line-range :40' prints lines 1 to 40\n \
'--line-range 40:' prints lines 40 to the end of the file",
),
).arg(
Arg::with_name("color")
.long("color")
.overrides_with("color")
.takes_value(true)
.value_name("when")
.possible_values(&["auto", "never", "always"])
.default_value("auto")
.help("When to use colors.")
.long_help("Specify when to use colored output. The automatic mode \
only enables colors if an interactive terminal is detected."),
).arg(
Arg::with_name("decorations")
.long("decorations")
.overrides_with("decorations")
.takes_value(true)
.value_name("when")
.possible_values(&["auto", "never", "always"])
.default_value("auto")
.help("When to show the decorations specified by '--style'.")
.long_help("Specify when to use the decorations that have been specified \
via '--style'. The automatic mode only enables decorations if \
an interactive terminal is detected."),
).arg(
Arg::with_name("paging")
.long("paging")
.overrides_with("paging")
.takes_value(true)
.value_name("when")
.possible_values(&["auto", "never", "always"])
.default_value("auto")
.help("Specify when to use the pager.")
.long_help("Specify when to use the pager. To control which pager \
is used, set the PAGER or BAT_PAGER environment \
variables (the latter takes precedence). The default \
pager is 'less'. To disable the pager permanently, set \
BAT_PAGER to an empty string."),
).arg(
Arg::with_name("wrap")
.long("wrap")
.overrides_with("wrap")
.takes_value(true)
.value_name("mode")
.possible_values(&["auto", "never", "character"])
.default_value("auto")
.help("Specify the text-wrapping mode.")
.long_help("Specify the text-wrapping mode."),
).arg(
Arg::with_name("unbuffered")
.short("u")
.hidden_short_help(true)
.long_help(
"This option exists for POSIX-compliance reasons ('u' is for \
'unbuffered'). The output is always unbuffered - this option \
it simply ignored.",
is simply ignored.",
),
).arg(
Arg::with_name("tabs")
.long("tabs")
.takes_value(true)
.value_name("tabs")
.validator(
|t| t.parse::<u32>()
.map_err(|_t| "must be a number")
.map(|_t| ()) // Convert to Result<(), &str>
.map_err(|e| e.to_string()) // Convert to Result<(), String>
)
.help("Sets the tab width.")
.long_help("Sets the tab width. Use a width of 0 to pass tabs through \
directly"),
).arg(
Arg::with_name("terminal-width")
.long("terminal-width")
.takes_value(true)
.value_name("width")
.hidden(true)
.help("Set the width of the terminal"),
).subcommand(
SubCommand::with_name("cache")
.about("Modify the syntax-definition and theme cache")
.arg(
Arg::with_name("init")
.long("init")
.short("i")
.help("Initialize the syntax/theme cache.")
.long_help(
"Initialize the syntax/theme cache by loading from the \
source directory (default: the configuration directory).",
),
).arg(
Arg::with_name("clear")
.long("clear")
.short("c")
.help("Remove the cached syntax definitions and themes."),
).arg(
Arg::with_name("config-dir")
.long("config-dir")
.short("d")
.help("Show bat's configuration directory."),
).group(
ArgGroup::with_name("cache-actions")
.args(&["init", "clear", "config-dir"])
.required(arg_group_required),
).arg(
Arg::with_name("source")
.long("source")
.requires("init")
.takes_value(true)
.value_name("dir")
.help("Use a different directory to load syntaxes and themes from."),
).arg(
Arg::with_name("target")
.long("target")
.requires("init")
.takes_value(true)
.value_name("dir")
.help(
"Use a different directory to store the cached syntax and theme set.",
),
)
.subcommand(
SubCommand::with_name("cache")
.about("Modify the syntax-definition and theme cache")
.arg(
Arg::with_name("init")
.long("init")
.short("i")
.help("Initialize the syntax/theme cache")
.long_help(
"Initialize the syntax/theme cache by loading from the \
source directory (default: the configuration directory)",
),
)
.arg(
Arg::with_name("clear")
.long("clear")
.short("c")
.help("Reset the cache"),
)
.arg(
Arg::with_name("config-dir")
.long("config-dir")
.short("d")
.help("Show the configuration directory"),
)
.group(
ArgGroup::with_name("cache-actions")
.args(&["init", "clear", "config-dir"])
.required(true),
)
.arg(
Arg::with_name("source")
.long("source")
.requires("init")
.takes_value(true)
.value_name("dir")
.help("Use a different source for loading syntaxes and themes from"),
)
.arg(
Arg::with_name("target")
.long("target")
.requires("init")
.takes_value(true)
.value_name("dir")
.help(
"Use a different source to store the cached syntax and theme set",
),
),
)
.help_message("Print this help message.")
).arg(
Arg::with_name("blank")
.long("blank")
.requires("init")
.help("Create completely new syntax and theme sets \
(instead of appending to the default sets).")
),
).help_message("Print this help message.")
.version_message("Show version information.")
.get_matches()
}
pub fn config(&self) -> Result<Config> {
let files = self.files();
let output_components = self.output_components()?;
let paging_mode = match self.matches.value_of("paging") {
Some("always") => PagingMode::Always,
Some("never") => PagingMode::Never,
Some("auto") | _ => if files.contains(&InputFile::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.
if self.interactive_output && !atty::is(Stream::Stdin) {
PagingMode::QuitIfOneScreen
} else {
PagingMode::Never
}
} else {
if self.interactive_output {
PagingMode::QuitIfOneScreen
} else {
PagingMode::Never
}
},
};
Ok(Config {
true_color: is_truecolor_terminal(),
output_components: self.output_components()?,
language: self.matches.value_of("language"),
output_wrap: if !self.interactive_output {
// We don't have the tty width when piping to another program.
@@ -238,7 +392,12 @@ impl App {
} else {
match self.matches.value_of("wrap") {
Some("character") => OutputWrap::Character,
Some("never") | _ => OutputWrap::None,
Some("never") => OutputWrap::None,
Some("auto") | _ => if output_components.plain() {
OutputWrap::None
} else {
OutputWrap::Character
},
}
},
colored_output: match self.matches.value_of("color") {
@@ -246,86 +405,84 @@ impl App {
Some("never") => false,
Some("auto") | _ => self.interactive_output,
},
paging_mode: match self.matches.value_of("paging") {
Some("always") => PagingMode::Always,
Some("never") => PagingMode::Never,
Some("auto") | _ => if files.contains(&None) {
// If we are reading from stdin, only enable paging if we write to an
// interactive terminal and if we do not *read* from an interactive
// terminal.
if self.interactive_output && !atty::is(Stream::Stdin) {
PagingMode::QuitIfOneScreen
} else {
PagingMode::Never
}
} else {
if self.interactive_output {
PagingMode::QuitIfOneScreen
} else {
PagingMode::Never
}
},
},
term_width: Term::stdout().size().1 as usize,
paging_mode,
term_width: self
.matches
.value_of("terminal-width")
.and_then(|w| w.parse().ok())
.unwrap_or(Term::stdout().size().1 as usize),
loop_through: !(self.interactive_output
|| self.matches.value_of("color") == Some("always")
|| self.matches.value_of("decorations") == Some("always")),
files,
theme: self.matches.value_of("theme"),
tab_width: self
.matches
.value_of("tabs")
.map(String::from)
.or_else(|| env::var("BAT_TABS").ok())
.and_then(|t| t.parse().ok())
.unwrap_or(
if output_components.plain() && paging_mode == PagingMode::Never {
0
} else {
8
},
),
theme: self
.matches
.value_of("theme")
.map(String::from)
.or_else(|| env::var("BAT_THEME").ok())
.unwrap_or(String::from(BAT_THEME_DEFAULT)),
line_range: transpose(self.matches.value_of("line-range").map(LineRange::from))?,
output_components,
})
}
fn files(&self) -> Vec<Option<&str>> {
fn files(&self) -> Vec<InputFile> {
self.matches
.values_of("FILE")
.map(|values| {
values
.map(|filename| {
if filename == "-" {
None
InputFile::StdIn
} else {
Some(filename)
InputFile::Ordinary(filename)
}
})
.collect()
})
.unwrap_or_else(|| vec![None]) // read from stdin (None) if no args are given
}).collect()
}).unwrap_or_else(|| vec![InputFile::StdIn])
}
fn output_components(&self) -> Result<OutputComponents> {
let matches = &self.matches;
Ok(OutputComponents(if matches.is_present("number") {
[OutputComponent::Numbers].iter().cloned().collect()
} else {
values_t!(matches.values_of("style"), OutputComponent)?
.into_iter()
.map(|style| style.components(self.interactive_output))
.fold(HashSet::new(), |mut acc, components| {
acc.extend(components.iter().cloned());
acc
})
}))
Ok(OutputComponents(
if matches.value_of("decorations") == Some("never") {
HashSet::new()
} else if matches.is_present("number") {
[OutputComponent::Numbers].iter().cloned().collect()
} else if matches.is_present("plain") {
[OutputComponent::Plain].iter().cloned().collect()
} else {
let env_style_components: Option<Vec<OutputComponent>> =
transpose(env::var("BAT_STYLE").ok().map(|style_str| {
style_str
.split(",")
.map(|x| OutputComponent::from_str(&x))
.collect::<Result<Vec<OutputComponent>>>()
}))?;
values_t!(matches.values_of("style"), OutputComponent)
.ok()
.or(env_style_components)
.unwrap_or(vec![OutputComponent::Full])
.into_iter()
.map(|style| style.components(self.interactive_output))
.fold(HashSet::new(), |mut acc, components| {
acc.extend(components.iter().cloned());
acc
})
},
))
}
}
#[derive(Debug, Clone, Copy)]
pub enum PagingMode {
Always,
QuitIfOneScreen,
Never,
}
pub struct Config<'a> {
pub true_color: bool,
pub output_wrap: OutputWrap,
pub output_components: OutputComponents,
pub language: Option<&'a str>,
pub colored_output: bool,
pub paging_mode: PagingMode,
pub term_width: usize,
pub files: Vec<Option<&'a str>>,
pub theme: Option<&'a str>,
}
fn is_truecolor_terminal() -> bool {
env::var("COLORTERM")
.map(|colorterm| colorterm == "truecolor" || colorterm == "24bit")
.unwrap_or(false)
}
+85 -35
View File
@@ -1,8 +1,8 @@
use directories::ProjectDirs;
use errors::*;
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::fs::{self, File};
use std::io;
use std::path::{Path, PathBuf};
use syntect::dumps::{dump_to_file, from_binary, from_reader};
use syntect::highlighting::{Theme, ThemeSet};
@@ -11,10 +11,15 @@ use syntect::parsing::{SyntaxDefinition, SyntaxSet};
#[cfg(unix)]
use std::os::unix::fs::FileTypeExt;
use app::InputFile;
lazy_static! {
static ref PROJECT_DIRS: ProjectDirs = ProjectDirs::from("", "", crate_name!());
static ref PROJECT_DIRS: ProjectDirs =
ProjectDirs::from("", "", crate_name!()).expect("Could not get home directory");
}
pub const BAT_THEME_DEFAULT: &str = "Monokai Extended";
pub struct HighlightingAssets {
pub syntax_set: SyntaxSet,
pub theme_set: ThemeSet,
@@ -25,31 +30,49 @@ impl HighlightingAssets {
Self::from_cache().unwrap_or_else(|_| Self::from_binary())
}
pub fn from_files(dir: Option<&Path>) -> Result<Self> {
let source_dir = dir.unwrap_or_else(|| PROJECT_DIRS.config_dir());
let theme_dir = source_dir.join("themes");
let theme_set = ThemeSet::load_from_folder(&theme_dir).chain_err(|| {
format!(
"Could not load themes from '{}'",
theme_dir.to_string_lossy()
)
})?;
fn empty() -> Self {
let mut syntax_set = SyntaxSet::new();
let syntax_dir = source_dir.join("syntaxes");
if !syntax_dir.exists() {
return Err(format!(
"Could not load syntaxes from '{}'",
syntax_dir.to_string_lossy()
).into());
}
syntax_set.load_syntaxes(syntax_dir, true)?;
syntax_set.load_plain_text_syntax();
Ok(HighlightingAssets {
let theme_set = ThemeSet {
themes: BTreeMap::new(),
};
HighlightingAssets {
syntax_set,
theme_set,
})
}
}
pub fn from_files(dir: Option<&Path>, start_empty: bool) -> Result<Self> {
let source_dir = dir.unwrap_or_else(|| PROJECT_DIRS.config_dir());
let mut assets = if start_empty {
Self::empty()
} else {
Self::from_binary_unlinked()
};
let theme_dir = source_dir.join("themes");
let res = extend_theme_set(&mut assets.theme_set, &theme_dir);
if !res.is_ok() {
println!(
"No themes were found in '{}', using the default set",
theme_dir.to_string_lossy()
);
}
let syntax_dir = source_dir.join("syntaxes");
if syntax_dir.exists() {
assets.syntax_set.load_syntaxes(syntax_dir, true)?;
} else {
println!(
"No syntaxes were found in '{}', using the default set.",
syntax_dir.to_string_lossy()
);
}
Ok(assets)
}
fn from_cache() -> Result<Self> {
@@ -79,9 +102,8 @@ impl HighlightingAssets {
})
}
fn from_binary() -> Self {
let mut syntax_set: SyntaxSet = from_binary(include_bytes!("../assets/syntaxes.bin"));
syntax_set.link_syntaxes();
fn from_binary_unlinked() -> Self {
let syntax_set: SyntaxSet = from_binary(include_bytes!("../assets/syntaxes.bin"));
let theme_set: ThemeSet = from_binary(include_bytes!("../assets/themes.bin"));
HighlightingAssets {
@@ -90,6 +112,12 @@ impl HighlightingAssets {
}
}
fn from_binary() -> Self {
let mut assets = Self::from_binary_unlinked();
assets.syntax_set.link_syntaxes();
assets
}
pub fn save(&self, dir: Option<&Path>) -> Result<()> {
let target_dir = dir.unwrap_or_else(|| PROJECT_DIRS.cache_dir());
let _ = fs::create_dir(target_dir);
@@ -123,19 +151,25 @@ impl HighlightingAssets {
Ok(())
}
pub fn get_theme(&self, theme: &str) -> Result<&Theme> {
Ok(self.theme_set.themes.get(theme).ok_or_else(|| {
io::Error::new(
io::ErrorKind::Other,
format!("Could not find '{}' theme", theme),
)
})?)
pub fn get_theme(&self, theme: &str) -> &Theme {
match self.theme_set.themes.get(theme) {
Some(theme) => theme,
None => {
use ansi_term::Colour::Yellow;
eprintln!(
"{}: Unknown theme '{}', using default.",
Yellow.paint("[bat warning]"),
theme
);
&self.theme_set.themes[BAT_THEME_DEFAULT]
}
}
}
pub fn get_syntax(&self, language: Option<&str>, filename: Option<&str>) -> &SyntaxDefinition {
pub fn get_syntax(&self, language: Option<&str>, filename: InputFile) -> &SyntaxDefinition {
let syntax = match (language, filename) {
(Some(language), _) => self.syntax_set.find_syntax_by_token(language),
(None, Some(filename)) => {
(None, InputFile::Ordinary(filename)) => {
#[cfg(not(unix))]
let may_read_from_file = true;
@@ -154,13 +188,29 @@ impl HighlightingAssets {
None
}
}
(None, None) => None,
(None, InputFile::StdIn) => None,
(_, InputFile::ThemePreviewFile) => self.syntax_set.find_syntax_by_name("Rust"),
};
syntax.unwrap_or_else(|| self.syntax_set.find_syntax_plain_text())
}
}
// TODO: this function will soon be part of syntect's `ThemeSet`.
fn extend_theme_set<P: AsRef<Path>>(theme_set: &mut ThemeSet, folder: P) -> Result<()> {
let paths = ThemeSet::discover_theme_paths(folder)?;
for p in &paths {
let theme = ThemeSet::get_theme(p)?;
let basename = p
.file_stem()
.and_then(|x| x.to_str())
.ok_or("Could not get theme basename")?;
theme_set.themes.insert(basename.to_owned(), theme);
}
Ok(())
}
fn theme_set_path() -> PathBuf {
PROJECT_DIRS.cache_dir().join("themes.bin")
}
+112
View File
@@ -0,0 +1,112 @@
use std::fs::File;
use std::io::{self, BufRead, BufReader, Write};
use app::{Config, InputFile};
use assets::HighlightingAssets;
use errors::*;
use line_range::LineRange;
use output::OutputType;
use printer::{InteractivePrinter, Printer, SimplePrinter};
const THEME_PREVIEW_FILE: &[u8] = include_bytes!("../assets/theme_preview.rs");
pub struct Controller<'a> {
config: &'a Config<'a>,
assets: &'a HighlightingAssets,
}
impl<'b> Controller<'b> {
pub fn new<'a>(config: &'a Config, assets: &'a HighlightingAssets) -> Controller<'a> {
Controller { config, assets }
}
pub fn run(&self) -> Result<bool> {
let mut output_type = OutputType::from_mode(self.config.paging_mode);
let writer = output_type.handle()?;
let mut no_errors: bool = true;
for filename in &self.config.files {
let result = if self.config.loop_through {
let mut printer = SimplePrinter::new();
self.print_file(&mut printer, writer, *filename)
} else {
let mut printer = InteractivePrinter::new(&self.config, &self.assets, *filename);
self.print_file(&mut printer, writer, *filename)
};
if let Err(error) = result {
handle_error(&error);
no_errors = false;
}
}
Ok(no_errors)
}
fn print_file<'a, P: Printer>(
&self,
printer: &mut P,
writer: &mut Write,
filename: InputFile<'a>,
) -> Result<()> {
let stdin = io::stdin();
{
let reader: Box<BufRead> = match filename {
InputFile::StdIn => Box::new(stdin.lock()),
InputFile::Ordinary(filename) => {
let file = File::open(filename)?;
if file.metadata()?.is_dir() {
return Err(format!("'{}' is a directory.", filename).into());
}
Box::new(BufReader::new(file))
}
InputFile::ThemePreviewFile => Box::new(THEME_PREVIEW_FILE),
};
printer.print_header(writer, filename)?;
self.print_file_ranges(printer, writer, reader, &self.config.line_range)?;
printer.print_footer(writer)?;
}
Ok(())
}
fn print_file_ranges<'a, P: Printer>(
&self,
printer: &mut P,
writer: &mut Write,
mut reader: Box<BufRead + 'a>,
line_ranges: &Option<LineRange>,
) -> Result<()> {
let mut line_buffer = Vec::new();
let mut line_number: usize = 1;
while reader.read_until(b'\n', &mut line_buffer)? > 0 {
{
match line_ranges {
&Some(ref range) => {
if line_number < range.lower {
// Call the printer in case we need to call the syntax highlighter
// for this line. However, set `out_of_range` to `true`.
printer.print_line(true, writer, line_number, &line_buffer)?;
} else if line_number > range.upper {
// no more lines in range, exit early
break;
} else {
printer.print_line(false, writer, line_number, &line_buffer)?;
}
}
&None => {
printer.print_line(false, writer, line_number, &line_buffer)?;
}
}
line_number += 1;
}
line_buffer.clear();
}
Ok(())
}
}
+10 -9
View File
@@ -1,6 +1,6 @@
use ansi_term::Style;
use diff::LineChange;
use printer::{Colors, Printer};
use printer::{Colors, InteractivePrinter};
#[derive(Clone)]
pub struct DecorationText {
@@ -9,12 +9,15 @@ pub struct DecorationText {
}
pub trait Decoration {
fn generate(&self, line_number: usize, continuation: bool, printer: &Printer)
-> DecorationText;
fn generate(
&self,
line_number: usize,
continuation: bool,
printer: &InteractivePrinter,
) -> DecorationText;
fn width(&self) -> usize;
}
// Line number decoration.
pub struct LineNumberDecoration {
color: Style,
cached_wrap: DecorationText,
@@ -39,7 +42,7 @@ impl Decoration for LineNumberDecoration {
&self,
line_number: usize,
continuation: bool,
_printer: &Printer,
_printer: &InteractivePrinter,
) -> DecorationText {
if continuation {
if line_number > self.cached_wrap_invalid_at {
@@ -65,7 +68,6 @@ impl Decoration for LineNumberDecoration {
}
}
// Line changes decoration.
pub struct LineChangesDecoration {
cached_none: DecorationText,
cached_added: DecorationText,
@@ -99,7 +101,7 @@ impl Decoration for LineChangesDecoration {
&self,
line_number: usize,
continuation: bool,
printer: &Printer,
printer: &InteractivePrinter,
) -> DecorationText {
if !continuation {
if let Some(ref changes) = printer.line_changes {
@@ -121,7 +123,6 @@ impl Decoration for LineChangesDecoration {
}
}
// Grid border decoration.
pub struct GridBorderDecoration {
cached: DecorationText,
}
@@ -142,7 +143,7 @@ impl Decoration for GridBorderDecoration {
&self,
_line_number: usize,
_continuation: bool,
_printer: &Printer,
_printer: &InteractivePrinter,
) -> DecorationText {
self.cached.clone()
}
+7 -4
View File
@@ -15,11 +15,14 @@ pub type LineChanges = HashMap<u32, LineChange>;
pub fn get_git_diff(filename: &str) -> Option<LineChanges> {
let repo = Repository::discover(&filename).ok()?;
let path_absolute = fs::canonicalize(&filename).ok()?;
let path_relative_to_repo = path_absolute.strip_prefix(repo.workdir()?).ok()?;
let repo_path_absolute = fs::canonicalize(repo.workdir()?).ok()?;
let filepath_absolute = fs::canonicalize(&filename).ok()?;
let filepath_relative_to_repo = filepath_absolute.strip_prefix(&repo_path_absolute).ok()?;
let mut diff_options = DiffOptions::new();
let pathspec = path_relative_to_repo.into_c_string().ok()?;
let pathspec = filepath_relative_to_repo.into_c_string().ok()?;
diff_options.pathspec(pathspec);
diff_options.context_lines(0);
@@ -42,7 +45,7 @@ pub fn get_git_diff(filename: &str) -> Option<LineChanges> {
Some(&mut |delta, hunk| {
let path = delta.new_file().path().unwrap_or_else(|| Path::new(""));
if path_relative_to_repo != path {
if filepath_relative_to_repo != path {
return false;
}
-112
View File
@@ -1,112 +0,0 @@
use ansi_term::Colour::Green;
use app::Config;
use assets::HighlightingAssets;
use diff::get_git_diff;
use errors::*;
use output::OutputType;
use printer::Printer;
use std::fs::File;
use std::io::{self, BufRead, BufReader};
use syntect::easy::HighlightLines;
use syntect::highlighting::Theme;
use syntect::parsing::SyntaxDefinition;
pub fn list_languages(assets: &HighlightingAssets, term_width: usize) {
let mut languages = assets
.syntax_set
.syntaxes()
.iter()
.filter(|syntax| !syntax.hidden && !syntax.file_extensions.is_empty())
.collect::<Vec<_>>();
languages.sort_by_key(|lang| lang.name.to_uppercase());
let longest = languages
.iter()
.map(|syntax| syntax.name.len())
.max()
.unwrap_or(32); // Fallback width if they have no language definitions.
let comma_separator = ", ";
let separator = " ";
// Line-wrapping for the possible file extension overflow.
let desired_width = term_width - longest - separator.len();
for lang in languages {
print!("{:width$}{}", lang.name, separator, width = longest);
// Number of characters on this line so far, wrap before `desired_width`
let mut num_chars = 0;
let mut extension = lang.file_extensions.iter().peekable();
while let Some(word) = extension.next() {
// If we can't fit this word in, then create a line break and align it in.
let new_chars = word.len() + comma_separator.len();
if num_chars + new_chars >= desired_width {
num_chars = 0;
print!("\n{:width$}{}", "", separator, width = longest);
}
num_chars += new_chars;
print!("{}", Green.paint(&word[..]));
if extension.peek().is_some() {
print!("{}", comma_separator);
}
}
println!();
}
}
pub fn print_files(assets: &HighlightingAssets, config: &Config) -> Result<bool> {
let theme = assets.get_theme(config.theme.unwrap_or("Default"))?;
let mut output_type = OutputType::from_mode(config.paging_mode);
let handle = output_type.handle()?;
let mut printer = Printer::new(handle, &config);
let mut no_errors: bool = true;
for file in &config.files {
printer.line_changes = file.and_then(|filename| get_git_diff(filename));
let syntax = assets.get_syntax(config.language, *file);
let result = print_file(theme, &syntax, &mut printer, *file);
if let Err(error) = result {
handle_error(&error);
no_errors = false;
}
}
Ok(no_errors)
}
fn print_file(
theme: &Theme,
syntax: &SyntaxDefinition,
printer: &mut Printer,
filename: Option<&str>,
) -> Result<()> {
let stdin = io::stdin(); // TODO: this is not always needed
let mut reader: Box<BufRead> = match filename {
None => Box::new(stdin.lock()),
Some(filename) => Box::new(BufReader::new(File::open(filename)?)),
};
let mut highlighter = HighlightLines::new(syntax, theme);
printer.print_header(filename)?;
let mut buffer = Vec::new();
while reader.read_until(b'\n', &mut buffer)? > 0 {
{
let line = String::from_utf8_lossy(&buffer);
let regions = highlighter.highlight(line.as_ref());
printer.print_line(&regions)?;
}
buffer.clear();
}
printer.print_footer()?;
Ok(())
}
+74
View File
@@ -0,0 +1,74 @@
use errors::*;
#[derive(Clone)]
pub struct LineRange {
pub lower: usize,
pub upper: usize,
}
impl LineRange {
pub fn from(range_raw: &str) -> Result<LineRange> {
LineRange::parse_range(range_raw)
}
pub fn new() -> LineRange {
LineRange {
lower: usize::min_value(),
upper: usize::max_value(),
}
}
pub fn parse_range(range_raw: &str) -> Result<LineRange> {
let mut new_range = LineRange::new();
if range_raw.bytes().nth(0).ok_or("Empty line range")? == b':' {
new_range.upper = range_raw[1..].parse()?;
return Ok(new_range);
} else if range_raw.bytes().last().ok_or("Empty line range")? == b':' {
new_range.lower = range_raw[..range_raw.len() - 1].parse()?;
return Ok(new_range);
}
let line_numbers: Vec<&str> = range_raw.split(':').collect();
if line_numbers.len() == 2 {
new_range.lower = line_numbers[0].parse()?;
new_range.upper = line_numbers[1].parse()?;
return Ok(new_range);
}
Err("expected single ':' character".into())
}
}
#[test]
fn test_parse_full() {
let range = LineRange::from("40:50").expect("Shouldn't fail on test!");
assert_eq!(40, range.lower);
assert_eq!(50, range.upper);
}
#[test]
fn test_parse_partial_min() {
let range = LineRange::from(":50").expect("Shouldn't fail on test!");
assert_eq!(usize::min_value(), range.lower);
assert_eq!(50, range.upper);
}
#[test]
fn test_parse_partial_max() {
let range = LineRange::from("40:").expect("Shouldn't fail on test!");
assert_eq!(40, range.lower);
assert_eq!(usize::max_value(), range.upper);
}
#[test]
fn test_parse_fail() {
let range = LineRange::from("40:50:80");
assert!(range.is_err());
let range = LineRange::from("40::80");
assert!(range.is_err());
let range = LineRange::from(":40:");
assert!(range.is_err());
let range = LineRange::from("40");
assert!(range.is_err());
}
+143 -26
View File
@@ -19,21 +19,29 @@ extern crate syntect;
mod app;
mod assets;
mod controller;
mod decorations;
mod diff;
mod features;
mod line_range;
mod output;
mod preprocessor;
mod printer;
mod style;
mod terminal;
use std::collections::HashSet;
use std::io;
use std::io::Write;
use std::path::Path;
use std::process;
use app::App;
use ansi_term::Colour::Green;
use ansi_term::Style;
use app::{App, Config, InputFile};
use assets::{clear_assets, config_dir, HighlightingAssets};
use features::{list_languages, print_files};
use controller::Controller;
use style::{OutputComponent, OutputComponents};
mod errors {
error_chain! {
@@ -41,6 +49,7 @@ mod errors {
Clap(::clap::Error);
Io(::std::io::Error);
SyntectError(::syntect::LoadingError);
ParseIntError(::std::num::ParseIntError);
}
}
@@ -61,6 +70,119 @@ mod errors {
use errors::*;
fn run_cache_subcommand(matches: &clap::ArgMatches) -> Result<()> {
if matches.is_present("init") {
let source_dir = matches.value_of("source").map(Path::new);
let target_dir = matches.value_of("target").map(Path::new);
let blank = matches.is_present("blank");
let assets = HighlightingAssets::from_files(source_dir, blank)?;
assets.save(target_dir)?;
} else if matches.is_present("clear") {
clear_assets();
} else if matches.is_present("config-dir") {
writeln!(io::stdout(), "{}", config_dir())?;
}
Ok(())
}
pub fn list_languages(config: &Config) -> Result<()> {
let assets = HighlightingAssets::new();
let mut languages = assets
.syntax_set
.syntaxes()
.iter()
.filter(|syntax| !syntax.hidden && !syntax.file_extensions.is_empty())
.collect::<Vec<_>>();
languages.sort_by_key(|lang| lang.name.to_uppercase());
let longest = languages
.iter()
.map(|syntax| syntax.name.len())
.max()
.unwrap_or(32); // Fallback width if they have no language definitions.
let comma_separator = ", ";
let separator = " ";
// Line-wrapping for the possible file extension overflow.
let desired_width = config.term_width - longest - separator.len();
let stdout = io::stdout();
let mut stdout = stdout.lock();
let style = if config.colored_output {
Green.normal()
} else {
Style::default()
};
for lang in languages {
write!(stdout, "{:width$}{}", lang.name, separator, width = longest)?;
// Number of characters on this line so far, wrap before `desired_width`
let mut num_chars = 0;
let mut extension = lang.file_extensions.iter().peekable();
while let Some(word) = extension.next() {
// If we can't fit this word in, then create a line break and align it in.
let new_chars = word.len() + comma_separator.len();
if num_chars + new_chars >= desired_width {
num_chars = 0;
write!(stdout, "\n{:width$}{}", "", separator, width = longest)?;
}
num_chars += new_chars;
write!(stdout, "{}", style.paint(&word[..]))?;
if extension.peek().is_some() {
write!(stdout, "{}", comma_separator)?;
}
}
writeln!(stdout)?;
}
Ok(())
}
pub fn list_themes(cfg: &Config) -> Result<()> {
let assets = HighlightingAssets::new();
let themes = &assets.theme_set.themes;
let mut config = cfg.clone();
let mut style = HashSet::new();
style.insert(OutputComponent::Plain);
config.files = vec![InputFile::ThemePreviewFile];
config.output_components = OutputComponents(style);
let stdout = io::stdout();
let mut stdout = stdout.lock();
if config.colored_output {
for (theme, _) in themes.iter() {
writeln!(
stdout,
"Theme: {}\n",
Style::new().bold().paint(theme.to_string())
)?;
config.theme = theme.to_string();
let _controller = Controller::new(&config, &assets).run();
writeln!(stdout)?;
}
} else {
for (theme, _) in themes.iter() {
writeln!(stdout, "{}", theme)?;
}
}
Ok(())
}
fn run_controller(config: &Config) -> Result<bool> {
let assets = HighlightingAssets::new();
let controller = Controller::new(&config, &assets);
controller.run()
}
/// Returns `Err(..)` upon fatal errors. Otherwise, returns `Some(true)` on full success and
/// `Some(false)` if any intermediate errors occurred (were printed).
fn run() -> Result<bool> {
@@ -68,38 +190,33 @@ fn run() -> Result<bool> {
match app.matches.subcommand() {
("cache", Some(cache_matches)) => {
if cache_matches.is_present("init") {
let source_dir = cache_matches.value_of("source").map(Path::new);
let target_dir = cache_matches.value_of("target").map(Path::new);
// If there is a file named 'cache' in the current working directory,
// arguments for subcommand 'cache' are not mandatory.
// If there are non-zero arguments, execute the subcommand cache, else, open the file cache.
if !cache_matches.args.is_empty() {
run_cache_subcommand(cache_matches)?;
Ok(true)
} else {
let mut config = app.config()?;
config.files = vec![InputFile::Ordinary(&"cache")];
let assets = HighlightingAssets::from_files(source_dir)?;
assets.save(target_dir)?;
} else if cache_matches.is_present("clear") {
clear_assets();
} else if cache_matches.is_present("config-dir") {
println!("{}", config_dir());
run_controller(&config)
}
return Ok(true);
}
_ => {
let config = app.config()?;
let assets = HighlightingAssets::new();
if app.matches.is_present("list-languages") {
list_languages(&assets, config.term_width);
return Ok(true);
}
list_languages(&config)?;
if app.matches.is_present("list-themes") {
let themes = &assets.theme_set.themes;
for (theme, _) in themes.iter() {
println!("{}", theme);
}
return Ok(true);
}
Ok(true)
} else if app.matches.is_present("list-themes") {
list_themes(&config)?;
print_files(&assets, &config)
Ok(true)
} else {
run_controller(&config)
}
}
}
}
+27 -9
View File
@@ -1,7 +1,11 @@
use std::env;
use std::ffi::OsString;
use std::io::{self, Write};
use std::path::PathBuf;
use std::process::{Child, Command, Stdio};
use app::PagingMode;
use errors::*;
use std::io::{self, Write};
use std::process::{Child, Command, Stdio};
pub enum OutputType {
Pager(Child),
@@ -20,13 +24,27 @@ impl OutputType {
/// Try to launch the pager. Fall back to stdout in case of errors.
fn try_pager(quit_if_one_screen: bool) -> Self {
let mut args = vec!["--RAW-CONTROL-CHARS", "--no-init"];
if quit_if_one_screen {
args.push("--quit-if-one-screen");
}
Command::new("less")
.args(&args)
.env("LESSCHARSET", "UTF-8")
let pager = env::var("BAT_PAGER")
.or_else(|_| env::var("PAGER"))
.unwrap_or(String::from("less"));
let less_path = PathBuf::from(&pager);
let is_less = less_path.file_stem() == Some(&OsString::from("less"));
let mut process = if is_less {
let mut args = vec!["--RAW-CONTROL-CHARS", "--no-init"];
if quit_if_one_screen {
args.push("--quit-if-one-screen");
}
let mut p = Command::new(&less_path);
p.args(&args).env("LESSCHARSET", "UTF-8");
p
} else {
Command::new(pager)
};
process
.stdin(Stdio::piped())
.spawn()
.map(OutputType::Pager)
+34
View File
@@ -0,0 +1,34 @@
use console::AnsiCodeIterator;
/// Expand tabs like an ANSI-enabled expand(1).
pub fn expand(line: &str, width: usize, cursor: &mut usize) -> String {
let mut buffer = String::with_capacity(line.len() * 2);
for chunk in AnsiCodeIterator::new(line) {
match chunk {
(text, true) => buffer.push_str(text),
(mut text, false) => {
while let Some(index) = text.find('\t') {
// Add previous text.
if index > 0 {
*cursor += index;
buffer.push_str(&text[0..index]);
}
// Add tab.
let spaces = width - (*cursor % width);
*cursor += spaces;
buffer.push_str(&*" ".repeat(spaces));
// Next.
text = &text[index + 1..text.len()];
}
*cursor += text.len();
buffer.push_str(text);
}
}
}
buffer
}
+197 -86
View File
@@ -1,31 +1,84 @@
use ansi_term::Colour::{Fixed, Green, Red, White, Yellow};
use ansi_term::Style;
use app::Config;
use console::AnsiCodeIterator;
use decorations::{Decoration, GridBorderDecoration, LineChangesDecoration, LineNumberDecoration};
use diff::LineChanges;
use errors::*;
use std::boxed::Box;
use std::io::Write;
use std::vec::Vec;
use style::OutputWrap;
use syntect::highlighting;
use terminal::as_terminal_escaped;
pub struct Printer<'a> {
handle: &'a mut Write,
use ansi_term::Colour::{Fixed, Green, Red, Yellow};
use ansi_term::Style;
use console::AnsiCodeIterator;
use syntect::easy::HighlightLines;
use syntect::highlighting::Theme;
use app::{Config, InputFile};
use assets::HighlightingAssets;
use decorations::{Decoration, GridBorderDecoration, LineChangesDecoration, LineNumberDecoration};
use diff::get_git_diff;
use diff::LineChanges;
use errors::*;
use preprocessor::expand;
use style::OutputWrap;
use terminal::{as_terminal_escaped, to_ansi_color};
pub trait Printer {
fn print_header(&mut self, handle: &mut Write, file: InputFile) -> Result<()>;
fn print_footer(&mut self, handle: &mut Write) -> Result<()>;
fn print_line(
&mut self,
out_of_range: bool,
handle: &mut Write,
line_number: usize,
line_buffer: &[u8],
) -> Result<()>;
}
pub struct SimplePrinter;
impl SimplePrinter {
pub fn new() -> Self {
SimplePrinter {}
}
}
impl Printer for SimplePrinter {
fn print_header(&mut self, _handle: &mut Write, _file: InputFile) -> Result<()> {
Ok(())
}
fn print_footer(&mut self, _handle: &mut Write) -> Result<()> {
Ok(())
}
fn print_line(
&mut self,
out_of_range: bool,
handle: &mut Write,
_line_number: usize,
line_buffer: &[u8],
) -> Result<()> {
if !out_of_range {
handle.write(line_buffer)?;
}
Ok(())
}
}
pub struct InteractivePrinter<'a> {
colors: Colors,
config: &'a Config<'a>,
decorations: Vec<Box<Decoration>>,
panel_width: usize,
line_number: usize,
ansi_prefix_sgr: String,
pub line_changes: Option<LineChanges>,
highlighter: HighlightLines<'a>,
}
impl<'a> Printer<'a> {
pub fn new(handle: &'a mut Write, config: &'a Config) -> Self {
impl<'a> InteractivePrinter<'a> {
pub fn new(config: &'a Config, assets: &'a HighlightingAssets, file: InputFile) -> Self {
let theme = assets.get_theme(&config.theme);
let colors = if config.colored_output {
Colors::colored()
Colors::colored(theme, config.true_color)
} else {
Colors::plain()
};
@@ -60,30 +113,67 @@ impl<'a> Printer<'a> {
panel_width = 0;
}
// Create printer.
Printer {
// Get the Git modifications
let line_changes = if config.output_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);
let highlighter = HighlightLines::new(syntax, theme);
InteractivePrinter {
panel_width,
handle,
colors,
config,
decorations,
line_number: 0,
line_changes: None,
ansi_prefix_sgr: String::new(),
line_changes,
highlighter,
}
}
pub fn print_header(&mut self, filename: Option<&str>) -> Result<()> {
self.line_number = 0;
fn print_horizontal_line(&mut self, handle: &mut Write, grid_char: char) -> Result<()> {
if self.panel_width == 0 {
writeln!(
handle,
"{}",
self.colors.grid.paint("".repeat(self.config.term_width))
)?;
} else {
let hline = "".repeat(self.config.term_width - (self.panel_width + 1));
let hline = format!("{}{}{}", "".repeat(self.panel_width), grid_char, hline);
writeln!(handle, "{}", self.colors.grid.paint(hline))?;
}
Ok(())
}
fn preprocess(&self, text: &str, cursor: &mut usize) -> String {
if self.config.tab_width > 0 {
expand(text, self.config.tab_width, cursor)
} else {
text.to_string()
}
}
}
impl<'a> Printer for InteractivePrinter<'a> {
fn print_header(&mut self, handle: &mut Write, file: InputFile) -> Result<()> {
if !self.config.output_components.header() {
return Ok(());
}
if self.config.output_components.grid() {
self.print_horizontal_line('┬')?;
self.print_horizontal_line(handle, '┬')?;
write!(
self.handle,
handle,
"{}{}",
" ".repeat(self.panel_width),
self.colors
@@ -91,35 +181,48 @@ impl<'a> Printer<'a> {
.paint(if self.panel_width > 0 { "" } else { "" }),
)?;
} else {
write!(self.handle, "{}", " ".repeat(self.panel_width))?;
write!(handle, "{}", " ".repeat(self.panel_width))?;
}
writeln!(
self.handle,
"{}{}",
filename.map_or("", |_| "File: "),
self.colors.filename.paint(filename.unwrap_or("STDIN"))
)?;
let (prefix, name) = match file {
InputFile::Ordinary(filename) => ("File: ", filename),
_ => ("", "STDIN"),
};
writeln!(handle, "{}{}", prefix, self.colors.filename.paint(name))?;
if self.config.output_components.grid() {
self.print_horizontal_line('┼')?;
self.print_horizontal_line(handle, '┼')?;
}
Ok(())
}
pub fn print_footer(&mut self) -> Result<()> {
fn print_footer(&mut self, handle: &mut Write) -> Result<()> {
if self.config.output_components.grid() {
self.print_horizontal_line('┴')
self.print_horizontal_line(handle, '┴')
} else {
Ok(())
}
}
pub fn print_line(&mut self, regions: &[(highlighting::Style, &str)]) -> Result<()> {
self.line_number += 1;
fn print_line(
&mut self,
out_of_range: bool,
handle: &mut Write,
line_number: usize,
line_buffer: &[u8],
) -> Result<()> {
let line = String::from_utf8_lossy(&line_buffer).to_string();
let regions = self.highlighter.highlight(line.as_ref());
if out_of_range {
return Ok(());
}
let mut cursor: usize = 0;
let mut cursor_max: usize = self.config.term_width;
let mut cursor_total: usize = 0;
let mut panel_wrap: Option<String> = None;
// Line decorations.
@@ -127,11 +230,11 @@ impl<'a> Printer<'a> {
let decorations = self
.decorations
.iter()
.map(|ref d| d.generate(self.line_number, false, self))
.map(|ref d| d.generate(line_number, false, self))
.collect::<Vec<_>>();
for deco in decorations {
write!(self.handle, "{} ", deco.text)?;
write!(handle, "{} ", deco.text)?;
cursor_max -= deco.width + 1;
}
}
@@ -141,34 +244,45 @@ impl<'a> Printer<'a> {
let true_color = self.config.true_color;
let colored_output = self.config.colored_output;
write!(
self.handle,
"{}",
regions
.iter()
.map(|&(style, text)| as_terminal_escaped(
style,
text,
true_color,
colored_output,
))
.collect::<Vec<_>>()
.join("")
)?;
for &(style, region) in regions.iter() {
let text = &*self.preprocess(region, &mut cursor_total);
write!(
handle,
"{}",
as_terminal_escaped(style, &*text, true_color, colored_output,)
)?;
}
if line.bytes().next_back() != Some(b'\n') {
write!(handle, "\n")?;
}
} else {
for &(style, region) in regions.iter() {
let mut ansi_iterator = AnsiCodeIterator::new(region);
let mut ansi_prefix = "";
let mut ansi_prefix: String = String::new();
for chunk in ansi_iterator {
match chunk {
// ANSI escape passthrough.
(text, true) => {
ansi_prefix = text;
if text.chars().last().map_or(false, |c| c == 'm') {
ansi_prefix.push_str(text);
if text == "\x1B[0m" {
self.ansi_prefix_sgr = "\x1B[0m".to_owned();
} else {
self.ansi_prefix_sgr.push_str(text);
}
} else {
ansi_prefix.push_str(text);
}
}
// Regular text.
(text, false) => {
let text = text.trim_right_matches(|c| c == '\r' || c == '\n');
let text = self.preprocess(
text.trim_right_matches(|c| c == '\r' || c == '\n'),
&mut cursor_total,
);
let mut chars = text.chars();
let mut remaining = text.chars().count();
@@ -181,11 +295,14 @@ impl<'a> Printer<'a> {
cursor += remaining;
write!(
self.handle,
handle,
"{}",
as_terminal_escaped(
style,
&*format!("{}{}", ansi_prefix, text),
&*format!(
"{}{}{}",
self.ansi_prefix_sgr, ansi_prefix, text
),
self.config.true_color,
self.config.colored_output,
)
@@ -201,9 +318,8 @@ impl<'a> Printer<'a> {
self.decorations
.iter()
.map(|ref d| d
.generate(self.line_number, true, self)
.text)
.collect::<Vec<String>>()
.generate(line_number, true, self)
.text).collect::<Vec<String>>()
.join(" ")
))
} else {
@@ -217,47 +333,36 @@ impl<'a> Printer<'a> {
remaining -= available;
write!(
self.handle,
handle,
"{}\n{}",
as_terminal_escaped(
style,
&*format!("{}{}", ansi_prefix, text),
&*format!(
"{}{}{}",
self.ansi_prefix_sgr, ansi_prefix, text
),
self.config.true_color,
self.config.colored_output,
),
panel_wrap.clone().unwrap()
)?;
}
// Clear the ANSI prefix buffer.
ansi_prefix.clear();
}
}
}
}
write!(self.handle, "\n")?;
}
Ok(())
}
fn print_horizontal_line(&mut self, grid_char: char) -> Result<()> {
if self.panel_width == 0 {
writeln!(
self.handle,
"{}",
self.colors.grid.paint("".repeat(self.config.term_width))
)?;
} else {
let hline = "".repeat(self.config.term_width - (self.panel_width + 1));
let hline = format!("{}{}{}", "".repeat(self.panel_width), grid_char, hline);
writeln!(self.handle, "{}", self.colors.grid.paint(hline))?;
write!(handle, "\n")?;
}
Ok(())
}
}
const GRID_COLOR: u8 = 238;
const LINE_NUMBER_COLOR: u8 = 244;
const DEFAULT_GUTTER_COLOR: u8 = 238;
#[derive(Default)]
pub struct Colors {
@@ -274,14 +379,20 @@ impl Colors {
Colors::default()
}
fn colored() -> Self {
fn colored(theme: &Theme, true_color: bool) -> Self {
let gutter_color = theme
.settings
.gutter_foreground
.map(|c| to_ansi_color(c, true_color))
.unwrap_or(Fixed(DEFAULT_GUTTER_COLOR));
Colors {
grid: Fixed(GRID_COLOR).normal(),
filename: White.bold(),
grid: gutter_color.normal(),
filename: Style::new().bold(),
git_added: Green.normal(),
git_removed: Red.normal(),
git_modified: Yellow.normal(),
line_number: Fixed(LINE_NUMBER_COLOR).normal(),
line_number: gutter_color.normal(),
}
}
}
+9 -2
View File
@@ -1,7 +1,8 @@
use errors::*;
use std::collections::HashSet;
use std::str::FromStr;
use errors::*;
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
pub enum OutputComponent {
Auto,
@@ -53,11 +54,13 @@ impl FromStr for OutputComponent {
"header" => Ok(OutputComponent::Header),
"numbers" => Ok(OutputComponent::Numbers),
"full" => Ok(OutputComponent::Full),
"plain" | _ => Ok(OutputComponent::Plain),
"plain" => Ok(OutputComponent::Plain),
_ => Err(format!("Unknown style '{}'", s).into()),
}
}
}
#[derive(Clone)]
pub struct OutputComponents(pub HashSet<OutputComponent>);
impl OutputComponents {
@@ -76,4 +79,8 @@ impl OutputComponents {
pub fn numbers(&self) -> bool {
self.0.contains(&OutputComponent::Numbers)
}
pub fn plain(&self) -> bool {
self.0.is_empty()
}
}
+14 -13
View File
@@ -1,5 +1,6 @@
use ansi_term::Colour::{Fixed, RGB};
use ansi_term::Style;
use ansi_term::{self, Style};
use syntect::highlighting::{self, FontStyle};
/// Approximate a 24 bit color value by a 8 bit ANSI code
@@ -13,14 +14,19 @@ fn rgb2ansi(r: u8, g: u8, b: u8) -> u8 {
} else if r > 248 {
WHITE
} else {
let fr = f32::from(r);
(((fr - 8.) / 247.) * 24.) as u8 + 232
((r - 8) as u16 * 24 / 247) as u8 + 232
}
} else {
let fr = f32::from(r);
let fg = f32::from(g);
let fb = f32::from(b);
16 + (36 * (fr / 255. * 5.) as u8) + (6 * (fg / 255. * 5.) as u8) + (fb / 255. * 5.) as u8
36 * (r / 51) + 6 * (g / 51) + (b / 51) + 16
}
}
pub fn to_ansi_color(color: highlighting::Color, true_color: bool) -> ansi_term::Colour {
if true_color {
RGB(color.r, color.g, color.b)
} else {
let ansi_code = rgb2ansi(color.r, color.g, color.b);
Fixed(ansi_code)
}
}
@@ -33,12 +39,7 @@ pub fn as_terminal_escaped(
let style = if !colored {
Style::default()
} else {
let color = if true_color {
RGB(style.foreground.r, style.foreground.g, style.foreground.b)
} else {
let ansi = rgb2ansi(style.foreground.r, style.foreground.g, style.foreground.b);
Fixed(ansi)
};
let color = to_ansi_color(style.foreground, true_color);
if style.font_style.contains(FontStyle::BOLD) {
color.bold()
+27 -6
View File
@@ -1,43 +1,64 @@
#!/usr/bin/env python3
import itertools
import subprocess
import pathlib
import shutil
def generate_snapshots():
single_styles = ["changes", "grid", "header", "numbers"]
collective_styles = ["full", "plain"]
for num in range(len(single_styles)):
for grouped in itertools.combinations(single_styles, num + 1):
generate_snapshot(",".join(grouped))
generate_style_snapshot(",".join(grouped))
for style in collective_styles:
generate_snapshot(style)
generate_style_snapshot(style)
def generate_snapshot(option):
command = "../../target/debug/bat --style={0} sample.rs > output/{0}.snapshot.txt".format(
option
generate_snapshot("tabs_passthrough", "--tabs=0 --style=full --wrap=never")
generate_snapshot("tabs_passthrough_wrapped", "--tabs=0 --style=full --wrap=character")
generate_snapshot("tabs_4", "--tabs=4 --style=full --wrap=never")
generate_snapshot("tabs_4_wrapped", "--tabs=4 --style=full --wrap=character")
generate_snapshot("tabs_8", "--tabs=8 --style=full --wrap=never")
generate_snapshot("tabs_8_wrapped", "--tabs=8 --style=full --wrap=character")
def generate_style_snapshot(style):
generate_snapshot(style.replace(",", "_"), "--style={}".format(style))
def generate_snapshot(name, arguments):
command = "cargo run -- --paging=never --color=never --decorations=always "
command += "{args} sample.rs > output/{name}.snapshot.txt".format(
name=name,
args=arguments
)
print("generating snapshot for {}".format(option))
print("generating snapshot for {}".format(name))
subprocess.call(command, shell=True)
def build_bat():
print("building bat")
subprocess.call("cargo build", cwd="../..", shell=True)
def prepare_output_dir():
shutil.rmtree("output", ignore_errors=True)
pathlib.Path("output").mkdir()
def modify_sample_file():
print("modifying sample.rs to show changes")
shutil.copyfile("sample.modified.rs", "sample.rs")
def undo_sample_file_modification():
print("undoing sample.rs modifications")
subprocess.call("git checkout -- sample.rs", shell=True)
build_bat()
prepare_output_dir()
modify_sample_file()
+15 -1
View File
@@ -16,7 +16,21 @@ _ fn main() {
fn area(rectangle: &Rectangle) -> u32 {
rectangle.width * rectangle.height
}
+
+ fn perimeter(rectangle: &Rectangle) -> u32 {
+ (rectangle.width + rectangle.height) * 2
+ }
+
// Tab alignment:
/*
Indent
1 2 3 4
1 ?
22 ?
333 ?
4444 ?
55555 ?
666666 ?
7777777 ?
88888888 ?
~ */
@@ -16,8 +16,22 @@ _ │ fn main() {
│ fn area(rectangle: &Rectangle) -> u32 {
│ rectangle.width * rectangle.height
│ }
+
+ │ fn perimeter(rectangle: &Rectangle) -> u32 {
+ │ (rectangle.width + rectangle.height) * 2
+ │ }
+ │
│ // Tab alignment:
│ /*
│ Indent
│ 1 2 3 4
│ 1 ?
│ 22 ?
│ 333 ?
│ 4444 ?
│ 55555 ?
│ 666666 ?
│ 7777777 ?
│ 88888888 ?
~ │ */
──┴─────────────────────────────────────────────────────────────────────────────
@@ -19,8 +19,22 @@ _ │ fn main() {
│ fn area(rectangle: &Rectangle) -> u32 {
│ rectangle.width * rectangle.height
│ }
+
+ │ fn perimeter(rectangle: &Rectangle) -> u32 {
+ │ (rectangle.width + rectangle.height) * 2
+ │ }
+ │
│ // Tab alignment:
│ /*
│ Indent
│ 1 2 3 4
│ 1 ?
│ 22 ?
│ 333 ?
│ 4444 ?
│ 55555 ?
│ 666666 ?
│ 7777777 ?
│ 88888888 ?
~ │ */
──┴─────────────────────────────────────────────────────────────────────────────
@@ -0,0 +1,40 @@
───────┬────────────────────────────────────────────────────────────────────────
│ File: sample.rs
───────┼────────────────────────────────────────────────────────────────────────
1 │ struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 _ │ fn main() {
7 │ let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 ~ │ "The perimeter of the rectangle is {} pixels.",
11 ~ │ perimeter(&rect1)
12 │ );
13 + │ println!(r#"This line contains invalid utf8: ""#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 │
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
23 + │
24 │ // Tab alignment:
25 │ /*
26 │ Indent
27 │ 1 2 3 4
28 │ 1 ?
29 │ 22 ?
30 │ 333 ?
31 │ 4444 ?
32 │ 55555 ?
33 │ 666666 ?
34 │ 7777777 ?
35 │ 88888888 ?
36 ~ │ */
───────┴────────────────────────────────────────────────────────────────────────
@@ -16,8 +16,22 @@
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 +
19
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
23 + │
24 │ // Tab alignment:
25 │ /*
26 │ Indent
27 │ 1 2 3 4
28 │ 1 ?
29 │ 22 ?
30 │ 333 ?
31 │ 4444 ?
32 │ 55555 ?
33 │ 666666 ?
34 │ 7777777 ?
35 │ 88888888 ?
36 ~ │ */
───────┴────────────────────────────────────────────────────────────────────────
@@ -17,7 +17,21 @@ _ fn main() {
fn area(rectangle: &Rectangle) -> u32 {
rectangle.width * rectangle.height
}
+
+ fn perimeter(rectangle: &Rectangle) -> u32 {
+ (rectangle.width + rectangle.height) * 2
+ }
+
// Tab alignment:
/*
Indent
1 2 3 4
1 ?
22 ?
333 ?
4444 ?
55555 ?
666666 ?
7777777 ?
88888888 ?
~ */
@@ -17,7 +17,21 @@
16 fn area(rectangle: &Rectangle) -> u32 {
17 rectangle.width * rectangle.height
18 }
19 +
19
20 + fn perimeter(rectangle: &Rectangle) -> u32 {
21 + (rectangle.width + rectangle.height) * 2
22 + }
23 +
24 // Tab alignment:
25 /*
26 Indent
27 1 2 3 4
28 1 ?
29 22 ?
30 333 ?
31 4444 ?
32 55555 ?
33 666666 ?
34 7777777 ?
35 88888888 ?
36 ~ */
@@ -16,7 +16,21 @@
16 fn area(rectangle: &Rectangle) -> u32 {
17 rectangle.width * rectangle.height
18 }
19 +
19
20 + fn perimeter(rectangle: &Rectangle) -> u32 {
21 + (rectangle.width + rectangle.height) * 2
22 + }
23 +
24 // Tab alignment:
25 /*
26 Indent
27 1 2 3 4
28 1 ?
29 22 ?
30 333 ?
31 4444 ?
32 55555 ?
33 666666 ?
34 7777777 ?
35 88888888 ?
36 ~ */
+15 -1
View File
@@ -19,8 +19,22 @@
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 +
19
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
23 + │
24 │ // Tab alignment:
25 │ /*
26 │ Indent
27 │ 1 2 3 4
28 │ 1 ?
29 │ 22 ?
30 │ 333 ?
31 │ 4444 ?
32 │ 55555 ?
33 │ 666666 ?
34 │ 7777777 ?
35 │ 88888888 ?
36 ~ │ */
───────┴────────────────────────────────────────────────────────────────────────
+14
View File
@@ -20,4 +20,18 @@ fn area(rectangle: &Rectangle) -> u32 {
fn perimeter(rectangle: &Rectangle) -> u32 {
(rectangle.width + rectangle.height) * 2
}
// Tab alignment:
/*
Indent
1 2 3 4
1 ?
22 ?
333 ?
4444 ?
55555 ?
666666 ?
7777777 ?
88888888 ?
*/
────────────────────────────────────────────────────────────────────────────────
@@ -23,4 +23,18 @@ fn area(rectangle: &Rectangle) -> u32 {
fn perimeter(rectangle: &Rectangle) -> u32 {
(rectangle.width + rectangle.height) * 2
}
// Tab alignment:
/*
Indent
1 2 3 4
1 ?
22 ?
333 ?
4444 ?
55555 ?
666666 ?
7777777 ?
88888888 ?
*/
────────────────────────────────────────────────────────────────────────────────
@@ -23,4 +23,18 @@
20 │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 │ (rectangle.width + rectangle.height) * 2
22 │ }
23 │
24 │ // Tab alignment:
25 │ /*
26 │ Indent
27 │ 1 2 3 4
28 │ 1 ?
29 │ 22 ?
30 │ 333 ?
31 │ 4444 ?
32 │ 55555 ?
33 │ 666666 ?
34 │ 7777777 ?
35 │ 88888888 ?
36 │ */
─────┴──────────────────────────────────────────────────────────────────────────
@@ -20,4 +20,18 @@
20 │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 │ (rectangle.width + rectangle.height) * 2
22 │ }
23 │
24 │ // Tab alignment:
25 │ /*
26 │ Indent
27 │ 1 2 3 4
28 │ 1 ?
29 │ 22 ?
30 │ 333 ?
31 │ 4444 ?
32 │ 55555 ?
33 │ 666666 ?
34 │ 7777777 ?
35 │ 88888888 ?
36 │ */
─────┴──────────────────────────────────────────────────────────────────────────
@@ -21,3 +21,17 @@ fn area(rectangle: &Rectangle) -> u32 {
fn perimeter(rectangle: &Rectangle) -> u32 {
(rectangle.width + rectangle.height) * 2
}
// Tab alignment:
/*
Indent
1 2 3 4
1 ?
22 ?
333 ?
4444 ?
55555 ?
666666 ?
7777777 ?
88888888 ?
*/
@@ -21,3 +21,17 @@
20 fn perimeter(rectangle: &Rectangle) -> u32 {
21 (rectangle.width + rectangle.height) * 2
22 }
23
24 // Tab alignment:
25 /*
26 Indent
27 1 2 3 4
28 1 ?
29 22 ?
30 333 ?
31 4444 ?
32 55555 ?
33 666666 ?
34 7777777 ?
35 88888888 ?
36 */
@@ -20,3 +20,17 @@
20 fn perimeter(rectangle: &Rectangle) -> u32 {
21 (rectangle.width + rectangle.height) * 2
22 }
23
24 // Tab alignment:
25 /*
26 Indent
27 1 2 3 4
28 1 ?
29 22 ?
30 333 ?
31 4444 ?
32 55555 ?
33 666666 ?
34 7777777 ?
35 88888888 ?
36 */
+14
View File
@@ -20,3 +20,17 @@ fn area(rectangle: &Rectangle) -> u32 {
fn perimeter(rectangle: &Rectangle) -> u32 {
(rectangle.width + rectangle.height) * 2
}
// Tab alignment:
/*
Indent
1 2 3 4
1 ?
22 ?
333 ?
4444 ?
55555 ?
666666 ?
7777777 ?
88888888 ?
*/
@@ -0,0 +1,40 @@
───────┬────────────────────────────────────────────────────────────────────────
│ File: sample.rs
───────┼────────────────────────────────────────────────────────────────────────
1 │ struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 _ │ fn main() {
7 │ let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 ~ │ "The perimeter of the rectangle is {} pixels.",
11 ~ │ perimeter(&rect1)
12 │ );
13 + │ println!(r#"This line contains invalid utf8: ""#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 │
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
23 + │
24 │ // Tab alignment:
25 │ /*
26 │ Indent
27 │ 1 2 3 4
28 │ 1 ?
29 │ 22 ?
30 │ 333 ?
31 │ 4444 ?
32 │ 55555 ?
33 │ 666666 ?
34 │ 7777777 ?
35 │ 88888888 ?
36 ~ │ */
───────┴────────────────────────────────────────────────────────────────────────
@@ -0,0 +1,40 @@
───────┬────────────────────────────────────────────────────────────────────────
│ File: sample.rs
───────┼────────────────────────────────────────────────────────────────────────
1 │ struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 _ │ fn main() {
7 │ let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 ~ │ "The perimeter of the rectangle is {} pixels.",
11 ~ │ perimeter(&rect1)
12 │ );
13 + │ println!(r#"This line contains invalid utf8: ""#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 │
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
23 + │
24 │ // Tab alignment:
25 │ /*
26 │ Indent
27 │ 1 2 3 4
28 │ 1 ?
29 │ 22 ?
30 │ 333 ?
31 │ 4444 ?
32 │ 55555 ?
33 │ 666666 ?
34 │ 7777777 ?
35 │ 88888888 ?
36 ~ │ */
───────┴────────────────────────────────────────────────────────────────────────
@@ -0,0 +1,40 @@
───────┬────────────────────────────────────────────────────────────────────────
│ File: sample.rs
───────┼────────────────────────────────────────────────────────────────────────
1 │ struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 _ │ fn main() {
7 │ let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 ~ │ "The perimeter of the rectangle is {} pixels.",
11 ~ │ perimeter(&rect1)
12 │ );
13 + │ println!(r#"This line contains invalid utf8: ""#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 │
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
23 + │
24 │ // Tab alignment:
25 │ /*
26 │ Indent
27 │ 1 2 3 4
28 │ 1 ?
29 │ 22 ?
30 │ 333 ?
31 │ 4444 ?
32 │ 55555 ?
33 │ 666666 ?
34 │ 7777777 ?
35 │ 88888888 ?
36 ~ │ */
───────┴────────────────────────────────────────────────────────────────────────
@@ -0,0 +1,40 @@
───────┬────────────────────────────────────────────────────────────────────────
│ File: sample.rs
───────┼────────────────────────────────────────────────────────────────────────
1 │ struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 _ │ fn main() {
7 │ let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 ~ │ "The perimeter of the rectangle is {} pixels.",
11 ~ │ perimeter(&rect1)
12 │ );
13 + │ println!(r#"This line contains invalid utf8: ""#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 │
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
23 + │
24 │ // Tab alignment:
25 │ /*
26 │ Indent
27 │ 1 2 3 4
28 │ 1 ?
29 │ 22 ?
30 │ 333 ?
31 │ 4444 ?
32 │ 55555 ?
33 │ 666666 ?
34 │ 7777777 ?
35 │ 88888888 ?
36 ~ │ */
───────┴────────────────────────────────────────────────────────────────────────
@@ -19,8 +19,22 @@
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 +
19
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
23 + │
24 │ // Tab alignment:
25 │ /*
26 │ Indent
27 │ 1 2 3 4
28 │ 1 ?
29 │ 22 ?
30 │ 333 ?
31 │ 4444 ?
32 │ 55555 ?
33 │ 666666 ?
34 │ 7777777 ?
35 │ 88888888 ?
36 ~ │ */
───────┴────────────────────────────────────────────────────────────────────────
@@ -0,0 +1,40 @@
───────┬────────────────────────────────────────────────────────────────────────
│ File: sample.rs
───────┼────────────────────────────────────────────────────────────────────────
1 │ struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 _ │ fn main() {
7 │ let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 ~ │ "The perimeter of the rectangle is {} pixels.",
11 ~ │ perimeter(&rect1)
12 │ );
13 + │ println!(r#"This line contains invalid utf8: ""#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 │
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
23 + │
24 │ // Tab alignment:
25 │ /*
26 │ Indent
27 │ 1 2 3 4
28 │ 1 ?
29 │ 22 ?
30 │ 333 ?
31 │ 4444 ?
32 │ 55555 ?
33 │ 666666 ?
34 │ 7777777 ?
35 │ 88888888 ?
36 ~ │ */
───────┴────────────────────────────────────────────────────────────────────────
+14
View File
@@ -20,3 +20,17 @@ fn area(rectangle: &Rectangle) -> u32 {
fn perimeter(rectangle: &Rectangle) -> u32 {
(rectangle.width + rectangle.height) * 2
}
// Tab alignment:
/*
Indent
1 2 3 4
1 ?
22 ?
333 ?
4444 ?
55555 ?
666666 ?
7777777 ?
88888888 ?
*/
+14
View File
@@ -16,3 +16,17 @@ fn main() {
fn area(rectangle: &Rectangle) -> u32 {
rectangle.width * rectangle.height
}
// Tab alignment:
/*
Indent
1 2 3 4
1 ?
22 ?
333 ?
4444 ?
55555 ?
666666 ?
7777777 ?
88888888 ?
*/
+58 -28
View File
@@ -1,16 +1,28 @@
use std::env;
use std::fs::{self, File};
use std::io::Read;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::process::Command;
extern crate tempdir;
use self::tempdir::TempDir;
extern crate git2;
use self::git2::build::CheckoutBuilder;
use self::git2::Repository;
use self::git2::Signature;
pub struct BatTester {
/// Temporary working directory
temp_dir: TempDir,
/// Path to the *bat* executable
exe: PathBuf,
}
impl BatTester {
pub fn new() -> Self {
modify_sample_file();
let temp_dir = create_sample_directory().expect("sample directory");
let root = env::current_exe()
.expect("tests executable")
@@ -21,24 +33,33 @@ impl BatTester {
.to_path_buf();
let exe_name = if cfg!(windows) { "bat.exe" } else { "bat" };
let exe = root.join(exe_name);
BatTester {
exe: root.join(exe_name),
}
BatTester { temp_dir, exe }
}
pub fn test_snapshot(&self, style: &str) {
pub fn test_snapshot(&self, name: &str, style: &str, tab_width: u32, wrap: bool) {
let output = Command::new(&self.exe)
.args(&["tests/snapshots/sample.rs", &format!("--style={}", style)])
.output()
.current_dir(self.temp_dir.path())
.args(&[
"sample.rs",
"--paging=never",
"--color=never",
"--decorations=always",
"--terminal-width=80",
&format!("--wrap={}", if wrap { "character" } else { "never" }),
&format!("--tabs={}", tab_width),
&format!("--style={}", style),
]).output()
.expect("bat failed");
// have to do the replace because the filename in the header changes based on the current working directory
let actual = String::from_utf8_lossy(&output.stdout)
.as_ref()
.replace("tests/snapshots/", "");
let mut expected = String::new();
let mut file = File::open(format!("tests/snapshots/output/{}.snapshot.txt", style))
let mut file = File::open(format!("tests/snapshots/output/{}.snapshot.txt", name))
.expect("snapshot file missing");
file.read_to_string(&mut expected)
.expect("could not read snapshot file");
@@ -47,26 +68,35 @@ impl BatTester {
}
}
impl Drop for BatTester {
fn drop(&mut self) {
undo_sample_file_modification();
}
}
fn create_sample_directory() -> Result<TempDir, git2::Error> {
// Create temp directory and initialize repository
let temp_dir = TempDir::new("bat-tests").expect("Temp directory");
let repo = Repository::init(&temp_dir)?;
fn modify_sample_file() {
fs::copy(
"tests/snapshots/sample.modified.rs",
"tests/snapshots/sample.rs",
).expect("generating modified sample file failed");
}
// Copy over `sample.rs`
let sample_path = temp_dir.path().join("sample.rs");
println!("{:?}", &sample_path);
fs::copy("tests/snapshots/sample.rs", &sample_path).expect("successful copy");
fn undo_sample_file_modification() {
let output = Command::new("git")
.args(&["checkout", "--", "tests/snapshots/sample.rs"])
.output()
.expect("git checkout failed");
// Commit
let mut index = repo.index()?;
index.add_path(Path::new("sample.rs"))?;
if !output.status.success() {
panic!("undoing modified sample changes failed")
}
let oid = index.write_tree()?;
let signature = Signature::now("bat test runner", "bat@test.runner")?;
let tree = repo.find_tree(oid)?;
let _ = repo.commit(
Some("HEAD"), // point HEAD to our new commit
&signature, // author
&signature, // committer
"initial commit",
&tree,
&[],
);
let mut opts = CheckoutBuilder::new();
repo.checkout_head(Some(opts.force()))?;
fs::copy("tests/snapshots/sample.modified.rs", &sample_path).expect("successful copy");
Ok(temp_dir)
}
+36 -40
View File
@@ -1,45 +1,41 @@
mod tester;
use std::process::{Command, Stdio};
use tester::BatTester;
static STYLES: &'static [&'static str] = &[
"changes",
"grid",
"header",
"numbers",
"changes,grid",
"changes,header",
"changes,numbers",
"grid,header",
"grid,numbers",
"header,numbers",
"changes,grid,header",
"changes,grid,numbers",
"changes,header,numbers",
"grid,header,numbers",
"changes,grid,header,numbers",
"full",
"plain",
];
#[test]
fn test_snapshots() {
let status = Command::new("git")
.arg("rev-parse")
.stdout(Stdio::null())
.stderr(Stdio::null())
.status();
if !status.map(|s| s.success()).unwrap_or(false) {
// Git not available or not a git repository. Skipping snapshot test.
return;
}
let bat_tester = BatTester::new();
for style in STYLES {
println!("testing {}", style);
bat_tester.test_snapshot(&*style);
}
macro_rules! snapshot_tests {
($($test_name: ident: $style: expr => [wrap: $wrap:expr, tabs: $tabs:expr],)*) => {
$(
#[test]
fn $test_name() {
let bat_tester = BatTester::new();
bat_tester.test_snapshot(stringify!($test_name), $style, $tabs, $wrap);
}
)*
};
}
snapshot_tests! {
changes: "changes" => [wrap: false, tabs: 8],
grid: "grid" => [wrap: false, tabs: 8],
header: "header" => [wrap: false, tabs: 8],
numbers: "numbers" => [wrap: false, tabs: 8],
changes_grid: "changes,grid" => [wrap: false, tabs: 8],
changes_header: "changes,header" => [wrap: false, tabs: 8],
changes_numbers: "changes,numbers" => [wrap: false, tabs: 8],
grid_header: "grid,header" => [wrap: false, tabs: 8],
grid_numbers: "grid,numbers" => [wrap: false, tabs: 8],
header_numbers: "header,numbers" => [wrap: false, tabs: 8],
changes_grid_header: "changes,grid,header" => [wrap: false, tabs: 8],
changes_grid_numbers: "changes,grid,numbers" => [wrap: false, tabs: 8],
changes_header_numbers: "changes,header,numbers" => [wrap: false, tabs: 8],
grid_header_numbers: "grid,header,numbers" => [wrap: false, tabs: 8],
changes_grid_header_numbers: "changes,grid,header,numbers" => [wrap: false, tabs: 8],
full: "full" => [wrap: false, tabs: 8],
plain: "plain" => [wrap: false, tabs: 0],
tabs_passthrough_wrapped: "full" => [wrap: true, tabs: 0],
tabs_4_wrapped: "full" => [wrap: true, tabs: 4],
tabs_8_wrapped: "full" => [wrap: true, tabs: 8],
tabs_passthrough: "full" => [wrap: false, tabs: 0],
tabs_4: "full" => [wrap: false, tabs: 4],
tabs_8: "full" => [wrap: false, tabs: 8],
}