diff --git a/.github/ISSUE_TEMPLATE/syntax_request.md b/.github/ISSUE_TEMPLATE/syntax_request.md index d7e01653..a3919764 100644 --- a/.github/ISSUE_TEMPLATE/syntax_request.md +++ b/.github/ISSUE_TEMPLATE/syntax_request.md @@ -26,4 +26,4 @@ guidelines for adding new syntaxes: [Name or description of the syntax/language here] **Guideline Criteria:** -[packagecontro.io link here] +[packagecontrol.io link here] diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 1ab0f2e3..68515f3d 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -163,6 +163,7 @@ jobs: fail-fast: false matrix: job: + - { target: aarch64-unknown-linux-musl , os: ubuntu-20.04, dpkg_arch: arm64, use-cross: true } - { target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, dpkg_arch: arm64, use-cross: true } - { target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, dpkg_arch: armhf, use-cross: true } - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, dpkg_arch: musl-linux-armhf, use-cross: true } @@ -170,6 +171,7 @@ jobs: - { target: i686-unknown-linux-gnu , os: ubuntu-20.04, dpkg_arch: i686, use-cross: true } - { target: i686-unknown-linux-musl , os: ubuntu-20.04, dpkg_arch: musl-linux-i686, use-cross: true } - { target: x86_64-apple-darwin , os: macos-12, } + - { target: aarch64-apple-darwin , os: macos-14, } - { target: x86_64-pc-windows-gnu , os: windows-2019, } - { target: x86_64-pc-windows-msvc , os: windows-2019, } - { target: x86_64-unknown-linux-gnu , os: ubuntu-20.04, dpkg_arch: amd64, use-cross: true } @@ -444,7 +446,7 @@ jobs: echo "IS_RELEASE=${IS_RELEASE}" >> $GITHUB_OUTPUT - name: Publish archives and packages - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 if: steps.is-release.outputs.IS_RELEASE with: files: | diff --git a/.gitmodules b/.gitmodules index 7c8a7724..fe159da8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -260,3 +260,6 @@ [submodule "assets/syntaxes/02_Extra/vscode-wgsl"] path = assets/syntaxes/02_Extra/vscode-wgsl url = https://github.com/PolyMeilex/vscode-wgsl.git +[submodule "assets/syntaxes/02_Extra/CFML"] + path = assets/syntaxes/02_Extra/CFML + url = https://github.com/jcberquist/sublimetext-cfml.git diff --git a/CHANGELOG.md b/CHANGELOG.md index 835f43e9..09a46b22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ - `bat --squeeze-blank`/`bat -s` will now squeeze consecutive empty lines, see #1441 (@eth-p) and #2665 (@einfachIrgendwer0815) - `bat --squeeze-limit` to set the maximum number of empty consecutive when using `--squeeze-blank`, see #1441 (@eth-p) and #2665 (@einfachIrgendwer0815) - `PrettyPrinter::squeeze_empty_lines` to support line squeezing for bat as a library, see #1441 (@eth-p) and #2665 (@einfachIrgendwer0815) +- Syntax highlighting for JavaScript files that start with `#!/usr/bin/env bun` #2913 (@sharunkumar) +- `bat --strip-ansi={never,always,auto}` to remove ANSI escape sequences from bat's input, see #2999 (@eth-p) +- Add or remove individual style components without replacing all styles #2929 (@eth-p) - Support negative relative line ranges, e.g. `bat -r :-10` / `bat -r='-10:'`, see #3068 (@ajesipow) ## Bugfixes @@ -15,6 +18,7 @@ - Fix handling of inputs with OSC ANSI escape sequences, see #2541 and #2544 (@eth-p) - Fix handling of inputs with combined ANSI color and attribute sequences, see #2185 and #2856 (@eth-p) - Fix panel width when line 10000 wraps, see #2854 (@eth-p) +- Fix compile issue of `time` dependency caused by standard library regression #3045 (@cyqsimon) ## Other @@ -34,6 +38,13 @@ - Relax syntax mapping rule restrictions to allow brace expansion #2865 (@cyqsimon) - Apply clippy fixes #2864 (@cyqsimon) - Faster startup by offloading glob matcher building to a worker thread #2868 (@cyqsimon) +- Display which theme is the default one in basic output (no colors), see #2937 (@sblondon) +- Display which theme is the default one in colored output, see #2838 (@sblondon) +- Add aarch64-apple-darwin ("Apple Silicon") binary tarballs to releases, see #2967 (@someposer) +- Update the Lisp syntax, see #2970 (@ccqpein) +- Use bat's ANSI iterator during tab expansion, see #2998 (@eth-p) +- Support 'statically linked binary' for aarch64 in 'Release' page, see #2992 (@tzq0301) +- Update options in shell completions and the man page of `bat`, see #2995 (@akinomyoga) ## Syntaxes @@ -41,10 +52,13 @@ - Upgrade JQ syntax, see #2820 (@dependabot[bot]) - Add syntax mapping for quadman quadlets #2866 (@cyqsimon) - Map containers .conf files to TOML syntax #2867 (@cyqsimon) -- Associate `xsh` files with `xonsh` syntax that is Python, see #2840 (@anki-code). -- Added auto detect syntax for `.jsonc` #2795 (@mxaddict) -- Added auto detect syntax for `.aws/{config,credentials}` #2795 (@mxaddict) -- Add syntax mapping for Wireguard config #2874 (@cyqsimon) +- Associate `.xsh` files with `xonsh` syntax that is Python, see #2840 (@anki-code) +- Associate JSON with Comments `.jsonc` with `json` syntax, see #2795 (@mxaddict) +- Associate JSON-LD `.jsonld` files with `json` syntax, see #3037 (@vorburger) +- Associate `.textproto` files with `ProtoBuf` syntax, see #3038 (@vorburger) +- Associate `.aws/{config,credentials}`, see #2795 (@mxaddict) +- Associate Wireguard config `/etc/wireguard/*.conf`, see #2874 (@cyqsimon) +- Add support for [CFML](https://www.adobe.com/products/coldfusion-family.html), see #3031 (@brenton-at-pieces) ## Themes diff --git a/Cargo.lock b/Cargo.lock index 04df6b85..7eb287b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ dependencies = [ [[package]] name = "ansi_colours" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a1558bd2075d341b9ca698ec8eb6fcc55a746b1fc4255585aad5b141d918a80" +checksum = "14eec43e0298190790f41679fe69ef7a829d2a2ddd78c8c00339e84710e435fe" dependencies = [ "rgb", ] @@ -76,9 +76,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.78" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca87830a3e3fb156dc96cfbd31cb620265dd053be734723f22b760d6cc3c3051" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "assert_cmd" @@ -130,8 +130,7 @@ dependencies = [ "grep-cli", "home", "indexmap", - "itertools 0.12.1", - "itertools 0.13.0", + "itertools", "nix", "nu-ansi-term", "once_cell", @@ -293,15 +292,15 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "console" -version = "0.15.7" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" dependencies = [ "encode_unicode", "lazy_static", "libc", "unicode-width", - "windows-sys 0.45.0", + "windows-sys 0.52.0", ] [[package]] @@ -417,9 +416,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -464,9 +463,9 @@ dependencies = [ [[package]] name = "expect-test" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d9eafeadd538e68fb28016364c9732d78e420b9ff8853fa5e4058861e9f8d3" +checksum = "9e0be0a561335815e06dab7c62e50353134c796e7a6155402a64bcff66b6a5e0" dependencies = [ "dissimilar", "once_cell", @@ -490,9 +489,9 @@ checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -565,9 +564,9 @@ dependencies = [ [[package]] name = "git2" -version = "0.18.2" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b3ba52851e73b46a4c3df1d89343741112003f0f6f13beb0dfac9e457c3fdcd" +checksum = "232e6a7bfe35766bf715e55a88b39a700596c0ccfd88cd3680b4cdb40d66ef70" dependencies = [ "bitflags 2.4.0", "libc", @@ -648,9 +647,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.2" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" +checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", "hashbrown 0.14.1", @@ -659,18 +658,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.11.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -858,9 +848,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.6.1" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" +checksum = "7ac44c994af577c799b1b4bd80dc214701e349873ad894d6cdf96f4f7526e0b9" dependencies = [ "memchr", ] @@ -937,14 +927,13 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "predicates" -version = "3.0.4" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dfc28575c2e3f19cb3c73b93af36460ae898d426eba6fc15b9bd2a5220758a0" +checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" dependencies = [ "anstyle", "difflib", "float-cmp", - "itertools 0.11.0", "normalize-line-endings", "predicates-core", "regex", @@ -968,9 +957,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -986,9 +975,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -1130,24 +1119,24 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.193" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" dependencies = [ "proc-macro2", "quote", @@ -1176,9 +1165,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.6.1" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15d167997bd841ec232f5b2b8e0e26606df2e7caa4c31b95ea9ca52b200bd270" +checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" dependencies = [ "serde", "serde_derive", @@ -1187,9 +1176,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.6.1" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "865f9743393e638991566a8b7a479043c2c8da94a33e0a31f18214c9cae0a64d" +checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" dependencies = [ "darling", "proc-macro2", @@ -1265,9 +1254,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "2.0.32" +version = "2.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" +checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35" dependencies = [ "proc-macro2", "quote", @@ -1347,18 +1336,18 @@ checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" [[package]] name = "thiserror" -version = "1.0.53" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2cd5904763bad08ad5513ddbb12cf2ae273ca53fa9f68e843e236ec6dfccc09" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.53" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcf4a824cce0aeacd6f38ae6f24234c8e80d68632338ebaa1443b5df9e29e19" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", @@ -1367,9 +1356,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", @@ -1388,9 +1377,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", @@ -1469,9 +1458,9 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unsafe-libyaml" @@ -1513,9 +1502,9 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -1529,9 +1518,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wild" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10d01931a94d5a115a53f95292f51d316856b68a035618eb831bbba593a30b67" +checksum = "a3131afc8c575281e1e80f36ed6a092aa502c08b18ed7524e86fbbb12bb410e1" dependencies = [ "glob", ] diff --git a/Cargo.toml b/Cargo.toml index 2581ce30..76234d5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,14 +44,14 @@ regex-fancy = ["syntect/regex-fancy"] # Use the rust-only "fancy-regex" engine nu-ansi-term = "0.50.0" ansi_colours = "^1.2" bincode = "1.0" -console = "0.15.7" +console = "0.15.8" flate2 = "1.0" once_cell = "1.19" thiserror = "1.0" wild = { version = "2.2", optional = true } content_inspector = "0.2.4" shell-words = { version = "1.1.0", optional = true } -unicode-width = "0.1.11" +unicode-width = "0.1.13" globset = "0.4" serde = "1.0" serde_derive = "1.0" @@ -63,10 +63,10 @@ bugreport = { version = "0.5.0", optional = true } etcetera = { version = "0.8.0", optional = true } grep-cli = { version = "0.1.10", optional = true } regex = { version = "1.10.2", optional = true } -walkdir = { version = "2.4", optional = true } +walkdir = { version = "2.5", optional = true } bytesize = { version = "1.3.0" } -encoding_rs = "0.8.33" -os_str_bytes = { version = "~6.6", optional = true } +encoding_rs = "0.8.34" +os_str_bytes = { version = "~7.0", optional = true } run_script = { version = "^0.10.1", optional = true} itertools = "0.13.0" @@ -91,9 +91,9 @@ plist = "1.6.0" [dev-dependencies] assert_cmd = "2.0.12" -expect-test = "1.4.1" +expect-test = "1.5.0" serial_test = { version = "2.0.0", default-features = false } -predicates = "3.0.4" +predicates = "3.1.0" wait-timeout = "0.2.0" tempfile = "3.8.1" serde = { version = "1.0", features = ["derive"] } @@ -102,16 +102,16 @@ serde = { version = "1.0", features = ["derive"] } nix = { version = "0.26.4", default-features = false, features = ["term"] } [build-dependencies] -anyhow = "1.0.78" -indexmap = { version = "2.2.2", features = ["serde"] } -itertools = "0.12.1" +anyhow = "1.0.86" +indexmap = { version = "2.3.0", features = ["serde"] } +itertools = "0.13.0" once_cell = "1.18" regex = "1.10.2" serde = "1.0" serde_derive = "1.0" -serde_with = { version = "3.6.1", default-features = false, features = ["macros"] } +serde_with = { version = "3.8.1", default-features = false, features = ["macros"] } toml = { version = "0.8.9", features = ["preserve_order"] } -walkdir = "2.4" +walkdir = "2.5" [build-dependencies.clap] version = "4.4.12" diff --git a/README.md b/README.md index 57baf2b0..016fe834 100644 --- a/README.md +++ b/README.md @@ -373,6 +373,14 @@ You can install `bat` using the [nix package manager](https://nixos.org/nix): nix-env -i bat ``` +### Via flox + +You can install `bat` using [Flox](https://flox.dev) + +```bash +flox install bat +``` + ### On openSUSE You can install `bat` with zypper: @@ -507,6 +515,16 @@ and line numbers but no grid and no file header. Set the `BAT_STYLE` environment variable to make these changes permanent or use `bat`s [configuration file](https://github.com/sharkdp/bat#configuration-file). +>[!tip] +> If you specify a default style in `bat`'s config file, you can change which components +> are displayed during a single run of `bat` using the `--style` command-line argument. +> By prefixing a component with `+` or `-`, it can be added or removed from the current style. +> +> For example, if your config contains `--style=full,-snip`, you can run bat with +> `--style=-grid,+snip` to remove the grid and add back the `snip` component. +> Or, if you want to override the styles completely, you use `--style=numbers` to +> only show the line numbers. + ### Adding new syntaxes / language definitions Should you find that a particular syntax is not available within `bat`, you can follow these @@ -729,7 +747,7 @@ your `PATH` or [define an environment variable](#using-a-different-pager). The [ Windows 10 natively supports colors 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/). +[Cmder](http://cmder.app/), which includes [ConEmu](https://conemu.github.io/). **Note:** Old versions of `less` do not correctly interpret colors on Windows. To fix this, you can add the optional Unix tools to your PATH when installing Git. If you don’t have any other pagers installed, you can disable paging entirely by passing `--paging=never` or by setting `BAT_PAGER` to an empty string. @@ -759,9 +777,14 @@ bat() { If an input file contains color codes or other ANSI escape sequences or control characters, `bat` will have problems performing syntax highlighting and text wrapping, and thus the output can become garbled. -When displaying such files it is recommended to disable both syntax highlighting and wrapping by + +If your version of `bat` supports the `--strip-ansi=auto` option, it can be used to remove such sequences +before syntax highlighting. Alternatively, you may disable both syntax highlighting and wrapping by passing the `--color=never --wrap=never` options to `bat`. +> [!NOTE] +> The `auto` option of `--strip-ansi` avoids removing escape sequences when the syntax is plain text. + ### Terminals & colors `bat` handles terminals *with* and *without* truecolor support. However, the colors in most syntax diff --git a/assets/completions/bat.bash.in b/assets/completions/bat.bash.in index de8651a8..f314bb25 100644 --- a/assets/completions/bat.bash.in +++ b/assets/completions/bat.bash.in @@ -76,6 +76,7 @@ _bat() { -m | --map-syntax | \ --ignored-suffix | \ --list-themes | \ + --squeeze-limit | \ --line-range | \ -L | --list-languages | \ --lessopen | \ @@ -157,6 +158,7 @@ _bat() { --diff-context --tabs --wrap + --chop-long-lines --terminal-width --number --color @@ -169,18 +171,24 @@ _bat() { --ignored-suffix --theme --list-themes + --squeeze-blank + --squeeze-limit --style --line-range --list-languages --lessopen --diagnostic --acknowledgements + --set-terminal-title --help --version --cache-dir --config-dir --config-file --generate-config-file + --no-config + --no-custom-assets + --no-lessopen " -- "$cur")) return 0 fi diff --git a/assets/completions/bat.fish.in b/assets/completions/bat.fish.in index d86ebfe6..788f71b0 100644 --- a/assets/completions/bat.fish.in +++ b/assets/completions/bat.fish.in @@ -133,6 +133,8 @@ set -l tabs_opts ' complete -c $bat -l acknowledgements -d "Print acknowledgements" -n __fish_is_first_arg +complete -c $bat -l cache-dir -f -d "Show bat's cache directory" -n __fish_is_first_arg + complete -c $bat -l color -x -a "$color_opts" -d "When to use colored output" -n __bat_no_excl_args complete -c $bat -l config-dir -f -d "Display location of configuration directory" -n __fish_is_first_arg diff --git a/assets/completions/bat.zsh.in b/assets/completions/bat.zsh.in index 69caceed..7d03abb3 100644 --- a/assets/completions/bat.zsh.in +++ b/assets/completions/bat.zsh.in @@ -48,6 +48,7 @@ _{{PROJECT_EXECUTABLE}}_main() { default auto full plain changes header header-filename header-filesize grid rule numbers snip' \*{-r+,--line-range=}'[only print the specified line range]:start\:end' '(* -)'{-L,--list-languages}'[display all supported languages]' + -P'[disable paging]' "--no-config[don't use the configuration file]" "--no-custom-assets[don't load custom assets]" '(--no-lessopen)'--lessopen'[enable the $LESSOPEN preprocessor]' diff --git a/assets/manual/bat.1.in b/assets/manual/bat.1.in index b85520da..2bc0a3a5 100644 --- a/assets/manual/bat.1.in +++ b/assets/manual/bat.1.in @@ -87,6 +87,10 @@ Set the tab width to T spaces. Use a width of 0 to pass tabs through directly Specify the text\-wrapping mode (*auto*, never, character). The '\-\-terminal\-width' option can be used in addition to control the output width. .HP +\fB\-S\fR, \fB\-\-chop\-long\-lines\fR +.IP +Truncate all lines longer than screen width. Alias for '\-\-wrap=never'. +.HP \fB\-\-terminal\-width\fR .IP Explicitly set the width of the terminal instead of determining it automatically. If @@ -141,6 +145,11 @@ use -m '*.build:Python'. To highlight files named '.myignore' with the Git Ignor syntax, use -m '.myignore:Git Ignore'. Note that the right-hand side is the *name* of the syntax, not a file extension. .HP +\fB\-\-ignored\-suffix\fR +.IP +Ignore extension. For example: 'bat \-\-ignored-suffix ".dev" my_file.json.dev' +will use JSON syntax, and ignore '.dev' +.HP \fB\-\-theme\fR .IP Set the theme for syntax highlighting. Use '\-\-list\-themes' to see all available themes. @@ -151,6 +160,14 @@ export the BAT_THEME environment variable (e.g.: export BAT_THEME="..."). .IP Display a list of supported themes for syntax highlighting. .HP +\fB\-s\fR, \fB\-\-squeeze\-blank\fR +.IP +Squeeze consecutive empty lines into a single empty line. +.HP +\fB\-\-squeeze\-limit\fR +.IP +Set the maximum number of consecutive empty lines to be printed. +.HP \fB\-\-style\fR .IP Configure which elements (line numbers, file headers, grid borders, Git modifications, @@ -184,6 +201,30 @@ Display a list of supported languages for syntax highlighting. This option exists for POSIX\-compliance reasons ('u' is for 'unbuffered'). The output is always unbuffered \- this option is simply ignored. .HP +\fB\-\-no\-custom\-assets\fR +.IP +Do not load custom assets. +.HP +\fB\-\-config\-dir\fR +.IP +Show bat's configuration directory. +.HP +\fB\-\-cache\-dir\fR +.IP +Show bat's cache directory. +.HP +\fB\-\-diagnostic\fR +.IP +Show diagnostic information for bug reports. +.HP +\fB\-\-acknowledgements\fR +.IP +Show acknowledgements. +.HP +\fB\-\-set\-terminal\-title\fR +.IP +Sets terminal title to filenames when using a pager. +.HP \fB\-h\fR, \fB\-\-help\fR .IP Print this help message. @@ -212,6 +253,20 @@ location of the configuration file. To generate a default configuration file, call: \fB{{PROJECT_EXECUTABLE}} --generate-config-file\fR + +These are related options: +.HP +\fB\-\-config\-file\fR +.IP +Show path to the configuration file. +.HP +\fB\-\-generate-config\-file\fR +.IP +Generates a default configuration file. +.HP +\fB\-\-no\-config\fR +.IP +Do not use the configuration file. .SH "ADDING CUSTOM LANGUAGES" {{PROJECT_EXECUTABLE}} supports Sublime Text \fB.sublime-syntax\fR language files, and can be customized to add additional languages to your local installation. To do this, add the \fB.sublime-syntax\fR language @@ -258,6 +313,16 @@ To temporarily disable the preprocessor if it is enabled by default, call: \fB{{PROJECT_EXECUTABLE}} --no-lessopen\fR +These are related options: +.HP +\fB\-\-lessopen\fR +.IP +Enable the $LESSOPEN preprocessor. +.HP +\fB\-\-no\-lessopen\fR +.IP +Disable the $LESSOPEN preprocessor if enabled (overrides --lessopen) +.PP For more information, see the "INPUT PREPROCESSOR" section of less(1). .SH "MORE INFORMATION" diff --git a/assets/patches/JavaScript.sublime-syntax.patch b/assets/patches/JavaScript.sublime-syntax.patch new file mode 100644 index 00000000..9ab89d1d --- /dev/null +++ b/assets/patches/JavaScript.sublime-syntax.patch @@ -0,0 +1,14 @@ +Submodule assets/syntaxes/01_Packages contains modified content +diff --git syntaxes/01_Packages/JavaScript/JavaScript.sublime-syntax syntaxes/01_Packages/JavaScript/JavaScript.sublime-syntax +index 05a4fed6..78a7bf55 100644 +--- syntaxes/01_Packages/JavaScript/JavaScript.sublime-syntax ++++ syntaxes/01_Packages/JavaScript/JavaScript.sublime-syntax +@@ -5,7 +5,7 @@ name: JavaScript + file_extensions: + - js + - htc +-first_line_match: ^#!\s*/.*\b(node|js)\b ++first_line_match: ^#!\s*/.*\b(node|bun|js)\b + scope: source.js + variables: + bin_digit: '[01_]' diff --git a/assets/patches/Lisp.sublime-syntax.patch b/assets/patches/Lisp.sublime-syntax.patch new file mode 100644 index 00000000..09a84a38 --- /dev/null +++ b/assets/patches/Lisp.sublime-syntax.patch @@ -0,0 +1,2365 @@ +diff --git syntaxes/01_Packages/Lisp/Lisp.sublime-syntax syntaxes/01_Packages/Lisp/Lisp.sublime-syntax +index 50e5dad3..44a9795d 100644 +--- syntaxes/01_Packages/Lisp/Lisp.sublime-syntax ++++ syntaxes/01_Packages/Lisp/Lisp.sublime-syntax +@@ -1,11 +1,14 @@ + %YAML 1.2 + --- +-# http://www.sublimetext.com/docs/3/syntax.html ++# https://www.sublimetext.com/docs/syntax.html + # + # `Common Lisp` Language Reference: +-# http://www.cs.cmu.edu/Groups/AI/html/cltl/clm/index.html +-# ++# https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/index.html ++# https://www.lispworks.com/documentation/lw70/CLHS/Front/Contents.htm + name: Lisp ++scope: source.lisp ++version: 2 ++ + file_extensions: + - lisp + - cl +@@ -17,108 +20,2268 @@ file_extensions: + - ss + - lsp + - fasl # Scheme dialect of Lisp +-scope: source.lisp ++ - sld # Scheme r7rs library ++ ++first_line_match: |- ++ (?xi: ++ ^ \s* ; .*? -\*- .*? \blisp\b .*? -\*- # editorconfig ++ ) ++ ++############################################################################### ++ + contexts: + main: +- - include: comments ++ - include: statements ++ ++ statements: + - include: parens ++ - include: expressions ++ - match: \) ++ scope: invalid.illegal.stray-bracket-end.lisp + + expressions: + - include: comments + - include: numbers +- - include: constants + - include: strings +- - include: variables ++ - include: annotations ++ - include: keywords ++ - include: quotes ++ - include: macros ++ - include: modifiers ++ - include: identifiers ++ ++###[ COMMENTS ]################################################################ ++ ++ comments: ++ - include: line-comments ++ - include: block-comments ++ - match: \|# ++ scope: invalid.illegal.stray-comment-end.lisp ++ ++ block-comments: ++ - match: '#\|' ++ scope: punctuation.definition.comment.begin.lisp ++ push: block-comment-body ++ ++ block-comment-body: ++ - meta_scope: comment.block.lisp ++ - match: '\|#' ++ scope: punctuation.definition.comment.end.lisp ++ pop: 1 ++ - include: block-comments ++ ++ line-comments: ++ - match: ;+ ++ scope: punctuation.definition.comment.lisp ++ push: line-comment-body ++ ++ line-comment-body: ++ - meta_scope: comment.line.semicolon.lisp ++ - match: $\n? ++ pop: 1 ++ ++###[ PARENTHESES ]############################################################# ++ ++ parens: ++ - match: \( ++ scope: punctuation.section.parens.begin.lisp ++ push: ++ - paren-body ++ - paren-begin ++ ++ paren-begin: ++ - include: class-declaration ++ - include: function-declaration ++ - include: macro-declaration ++ - include: struct-declaration ++ - include: type-declaration ++ - include: variable-declaration ++ - include: other-declaration ++ - include: declare + - include: control +- - include: functions +- - include: operators ++ - include: loop ++ - include: operator ++ - include: quote ++ - include: namespaces ++ - include: builtin-function ++ - include: other-function ++ - include: comments ++ - include: else-pop + +- variables: +- - match: (\*)(?i:trace-output|terminal-io|suppress-series-warnings|standard-output|standard-input|readtable|read-suppress|read-eval|read-default-float-format|read-base|random-state|query-io|print-right-margin|print-readably|print-radix|print-pretty|print-pprint-dispatch|print-miser-width|print-lines|print-level|print-length|print-gensym|print-escape|print-circle|print-case|print-base|print-array|package|modules|macroexpand-hook|load-verbose|load-truename|load-print|load-pathname|gensym-counter|features|evalhook|error-output|default-pathname-defaults|debugger-hook|debug-io|compile-verbose|compile-print|compile-file-truename|compile-file-pathname|break-on-warnings|break-on-signals|applyhook)(\*) +- scope: variable.language.lisp ++ paren-body: ++ - meta_scope: meta.parens.lisp ++ - include: paren-end ++ - include: parens ++ - include: expressions ++ ++ paren-end: ++ - meta_include_prototype: false ++ - meta_scope: meta.parens.lisp ++ - match: \) ++ scope: punctuation.section.parens.end.lisp ++ pop: 1 ++ ++###[ LISTS ]################################################################### ++ ++ expect-list: ++ # a list expression ++ # skips function-like parentheses on next level ++ - meta_include_prototype: false ++ - match: \( ++ scope: punctuation.section.parens.begin.lisp ++ set: paren-body ++ - include: comments ++ - include: else-pop ++ ++ expect-lists-list: ++ # a list of list expressions ++ # skips function-like parentheses on next 2 levels ++ - meta_include_prototype: false ++ - match: \( ++ scope: punctuation.section.parens.begin.lisp ++ set: lists-list-body ++ - include: comments ++ - include: else-pop ++ ++ lists-list-body: ++ - meta_scope: meta.parens.lisp ++ - include: paren-end ++ - include: lists ++ - include: expressions ++ ++ lists: ++ - match: \( ++ scope: punctuation.section.parens.begin.lisp ++ push: paren-body ++ ++###[ QUOTED EXPRESSIONS ]###################################################### ++ ++ quotes: ++ # abbreviation of (quote ...) function ++ - match: \' ++ scope: punctuation.definition.quoted.lisp ++ push: quoted-content ++ ++ quoted-content: ++ - meta_include_prototype: false ++ - match: \( ++ scope: punctuation.section.parens.begin.lisp ++ set: quoted-paren-body ++ # A quoted symbol evaluates to the symbol. ++ - match: '{{identifier}}' ++ scope: constant.other.symbol.lisp + captures: +- 1: punctuation.definition.variable.begin.lisp +- 2: punctuation.definition.variable.end.lisp +- - match: (\*)(\S*)(\*) +- scope: variable.other.global.lisp ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp ++ pop: 1 ++ - include: immediately-pop ++ ++ quoted-parens: ++ - match: \( ++ scope: punctuation.section.parens.begin.lisp ++ push: quoted-paren-body ++ ++ quoted-paren-body: ++ - meta_scope: meta.parens.lisp ++ - include: paren-end ++ - include: quoted-parens ++ - include: expressions ++ ++###[ CLASS DECLARATIONS ]###################################################### ++ ++ class-declaration: ++ # https://www.lispworks.com/documentation/lw70/CLHS/Body/m_defcla.htm ++ - match: (?i:defclass|define-class){{break}} ++ scope: meta.class.lisp keyword.declaration.class.lisp ++ set: ++ - class-declaration-slots ++ - class-declaration-parent-list ++ - class-declaration-name ++ ++ class-declaration-name: ++ - match: '{{identifier}}' ++ scope: entity.name.class.lisp + captures: +- 1: punctuation.definition.variable.begin.lisp +- 3: punctuation.definition.variable.end.lisp ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp ++ pop: 1 ++ - include: comments ++ - include: else-pop ++ ++ class-declaration-parent-list: ++ - meta_include_prototype: false ++ - match: \( ++ scope: punctuation.section.parens.begin.lisp ++ set: class-declaration-parent-list-body ++ - include: comments ++ - include: else-pop ++ ++ class-declaration-parent-list-body: ++ - meta_scope: meta.parens.lisp ++ - include: namespaces ++ - match: '{{identifier}}' ++ scope: entity.other.inherited-class.lisp ++ captures: ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp ++ - include: comments ++ - include: paren-end ++ ++ class-declaration-slots: ++ - meta_content_scope: meta.class.lisp ++ - include: paren-pop ++ - include: lists ++ - include: expressions ++ ++###[ FUNCTION DECLARATIONS ]################################################### ++ ++ function-declaration: ++ # https://www.lispworks.com/documentation/HyperSpec/Body/s_lambda.htm ++ - match: (?i:lambda){{break}} ++ scope: meta.function.lisp keyword.declaration.function.inline.lisp ++ set: ++ - function-body ++ - function-parameter-list ++ # https://www.lispworks.com/documentation/HyperSpec/Body/m_defun.htm ++ - match: (?i:def(?:[a-z]+f)?un|defgeneric|define-(?:command(?:-global)?|parenscript)){{break}} ++ scope: meta.function.lisp keyword.declaration.function.lisp ++ set: ++ - function-body ++ - function-parameter-list ++ - function-declaration-name ++ # https://www.lispworks.com/documentation/HyperSpec/Body/m_defmet.htm ++ - match: (?i:defmethod){{break}} ++ scope: meta.function.lisp keyword.declaration.function.lisp ++ set: ++ - function-body ++ - function-parameter-list ++ - method-qualifier ++ - function-declaration-name ++ # https://www.lispworks.com/documentation/HyperSpec/Body/s_flet_.htm ++ - match: (?i:flet|labels){{break}} ++ scope: keyword.declaration.function.lisp ++ set: function-declaration-list ++ ++ function-declaration-list: ++ - meta_include_prototype: false ++ - match: \( ++ scope: punctuation.section.parens.begin.lisp ++ set: function-declaration-list-body ++ - include: comments ++ - include: else-pop ++ ++ function-declaration-list-body: ++ - meta_scope: meta.parens.lisp ++ - match: \( ++ scope: punctuation.section.parens.begin.lisp ++ push: ++ - paren-end ++ - function-body ++ - function-parameter-list ++ - function-declaration-name ++ - include: comments ++ - include: paren-end ++ ++ function-declaration-name: ++ - include: namespaces ++ - match: '{{identifier}}' ++ scope: entity.name.function.lisp ++ captures: ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp ++ pop: 1 ++ - include: comments ++ - include: else-pop ++ ++ method-qualifier: ++ # qualifiers may be anything but lists ++ - include: expressions ++ - include: else-pop ++ ++ function-parameter-list: ++ - meta_include_prototype: false ++ - match: \( ++ scope: punctuation.section.parameters.begin.lisp ++ set: function-parameter-list-body ++ - include: comments ++ - include: else-pop ++ ++ function-parameter-list-body: ++ - clear_scopes: 1 ++ - meta_scope: meta.function.parameters.lisp ++ - match: \) ++ scope: punctuation.section.parameters.end.lisp ++ pop: 1 ++ - include: comments ++ - include: annotations ++ - include: keywords ++ - include: modifiers ++ - include: parameters ++ ++ function-body: ++ - meta_content_scope: meta.function.lisp ++ - include: paren-pop ++ - include: parens ++ - include: expressions ++ ++###[ MACRO DECLARATIONS ]###################################################### ++ ++ macro-declaration: ++ # https://www.lispworks.com/documentation/HyperSpec/Body/m_defmac.htm ++ - match: (?i:defmacro){{break}} ++ scope: meta.macro.lisp keyword.declaration.macro.lisp ++ set: ++ - macro-body ++ - macro-parameter-list ++ - macro-declaration-name ++ # https://www.lispworks.com/documentation/HyperSpec/Body/s_flet_.htm ++ - match: (?i:macrolet){{break}} ++ scope: keyword.declaration.macro.lisp ++ set: macro-declaration-list ++ ++ macro-declaration-list: ++ - meta_include_prototype: false ++ - match: \( ++ scope: punctuation.section.parens.begin.lisp ++ set: macro-declaration-list-body ++ - include: comments ++ - include: else-pop ++ ++ macro-declaration-list-body: ++ - meta_scope: meta.parens.lisp ++ - match: \( ++ scope: punctuation.section.parens.begin.lisp ++ push: ++ - paren-end ++ - macro-body ++ - macro-parameter-list ++ - macro-declaration-name ++ - include: comments ++ - include: paren-end ++ ++ macro-declaration-name: ++ - include: namespaces ++ - match: '{{identifier}}' ++ scope: entity.name.macro.lisp ++ captures: ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp ++ pop: 1 ++ - include: comments ++ - include: else-pop ++ ++ macro-parameter-list: ++ - meta_include_prototype: false ++ - match: \( ++ scope: punctuation.section.parameters.begin.lisp ++ set: macro-parameter-list-body ++ - include: comments ++ - include: else-pop ++ ++ macro-parameter-list-body: ++ - clear_scopes: 1 ++ - meta_scope: meta.macro.parameters.lisp ++ - match: \) ++ scope: punctuation.section.parameters.end.lisp ++ pop: 1 ++ - include: comments ++ - include: annotations ++ - include: keywords ++ - include: modifiers ++ - include: parameters ++ ++ macro-body: ++ - meta_content_scope: meta.macro.lisp ++ - include: paren-pop ++ - include: parens ++ - include: expressions ++ ++###[ STRUCT DECLARATIONS ]##################################################### ++ ++ struct-declaration: ++ # https://www.lispworks.com/documentation/lw70/CLHS/Body/m_defstr.htm ++ - match: (?i:defstruct|define-struct){{break}} ++ scope: meta.struct.lisp keyword.declaration.struct.lisp ++ set: ++ - struct-declaration-body ++ - struct-declaration-name ++ ++ struct-declaration-name: ++ - meta_include_prototype: false ++ - match: \( ++ scope: punctuation.section.parens.begin.lisp ++ set: ++ - paren-body ++ - struct-declaration-qualified-name ++ - include: struct-declaration-qualified-name ++ ++ struct-declaration-qualified-name: ++ - include: namespaces ++ - match: '{{identifier}}' ++ scope: entity.name.struct.lisp ++ captures: ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp ++ pop: 1 ++ - include: comments ++ - include: else-pop ++ ++ struct-declaration-body: ++ - meta_content_scope: meta.struct.lisp ++ - include: paren-pop ++ - include: lists ++ - include: expressions ++ ++###[ TYPE DECLARATIONS ]####################################################### ++ ++ type-declaration: ++ # https://www.lispworks.com/documentation/lw70/CLHS/Body/m_deftp.htm ++ - match: (?i:deftype|define-type){{break}} ++ scope: meta.type.lisp keyword.declaration.type.lisp ++ set: ++ - type-body ++ - type-parameter-list ++ - type-declaration-name ++ ++ type-declaration-name: ++ - include: namespaces ++ - match: '{{identifier}}' ++ scope: entity.name.type.lisp ++ captures: ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp ++ pop: 1 ++ - include: comments ++ - include: else-pop ++ ++ type-parameter-list: ++ - meta_include_prototype: false ++ - match: \( ++ scope: punctuation.section.parameters.begin.lisp ++ set: type-parameter-list-body ++ - include: comments ++ - include: else-pop ++ ++ type-parameter-list-body: ++ - clear_scopes: 1 ++ - meta_scope: meta.type.parameters.lisp ++ - match: \) ++ scope: punctuation.section.parameters.end.lisp ++ pop: 1 ++ - include: comments ++ - include: annotations ++ - include: keywords ++ - include: modifiers ++ - include: parameters ++ ++ type-body: ++ - meta_content_scope: meta.type.lisp ++ - include: paren-pop ++ - include: parens ++ - include: expressions ++ ++###[ VARIABLE DECLARATIONS ]################################################### ++ ++ variable-declaration: ++ # https://www.lispworks.com/documentation/HyperSpec/Body/s_let_l.htm ++ - match: (?i:let\*?){{break}} ++ scope: keyword.declaration.variable.lisp ++ set: variable-declaration-list ++ # https://www.lispworks.com/documentation/lw445/CLHS/Body/m_defcon.htm ++ - match: (?i:defconstant){{break}} ++ scope: meta.declaration.lisp keyword.declaration.constant.lisp ++ set: constant-declaration-name ++ # https://www.lispworks.com/documentation/HyperSpec/Body/m_defpar.htm ++ - match: (?i:defparameter|defvar){{break}} ++ scope: meta.declaration.lisp keyword.declaration.variable.lisp ++ set: variable-declaration-name ++ ++ variable-declaration-list: ++ - meta_include_prototype: false ++ - match: \( ++ scope: punctuation.section.parens.begin.lisp ++ set: variable-declaration-list-body ++ - include: comments ++ - include: else-pop ++ ++ variable-declaration-list-body: ++ - meta_scope: meta.parens.lisp ++ - match: \( ++ scope: punctuation.section.parens.begin.lisp ++ push: ++ - paren-body ++ - variable-declaration-name ++ - match: '{{identifier}}' ++ scope: meta.declaration.lisp variable.other.lisp ++ captures: ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp ++ - include: comments ++ - include: paren-end ++ ++ constant-declaration-name: ++ - meta_content_scope: meta.declaration.lisp ++ - match: '{{identifier}}' ++ scope: meta.declaration.lisp constant.other.lisp ++ captures: ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp ++ pop: 1 ++ - include: comments ++ - include: else-pop ++ ++ variable-declaration-name: ++ - meta_content_scope: meta.declaration.lisp ++ - match: '{{identifier}}' ++ scope: meta.declaration.lisp variable.other.lisp ++ captures: ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp ++ pop: 1 ++ - include: comments ++ - include: else-pop ++ ++###[ OTHER DECLARATIONS ]###################################################### ++ ++ other-declaration: ++ - match: '{{declaration_keywords}}' ++ scope: keyword.declaration.lisp ++ pop: 1 ++ ++###[ DECLARE STATEMENTS ]###################################################### ++ ++ declare: ++ # https://www.lispworks.com/documentation/HyperSpec/Body/s_declar.htm ++ - match: (?i:declare){{break}} ++ scope: keyword.declaration.declare.lisp ++ set: declare-specifier-list ++ ++ declare-specifier-list: ++ - match: \( ++ scope: punctuation.section.parens.begin.lisp ++ push: ++ - paren-body ++ - declare-identifier ++ - include: comments ++ - include: else-pop ++ ++ declare-identifier: ++ - match: '{{declaration_identifiers}}' ++ scope: storage.modifier.lisp ++ pop: 1 ++ - include: comments ++ - include: else-pop ++ ++###[ CONTROL FLOW STATEMENTS ]################################################# + + control: +- - match: \b(?i:with|while|when|unless|typecase|to|thereis|then|return-from name|return|repeat|prog*|prog|never|named|maplist|mapl|mapcon|mapcar|mapcan|mapc|loop|let|initially|if|from|for|finally|etypecase|else|dotimes|dolist|doing|do*|do|ctypecase|cond|case|block|as|always)\b ++ # Conditional ++ - match: (?i:if|case|cond|otherwise|unless|when|[ce]?typecase){{break}} ++ scope: keyword.control.conditional.lisp ++ pop: 1 ++ # Flow ++ - match: (?i:return(?:-from)?){{break}} ++ scope: keyword.control.flow.return.lisp ++ pop: 1 ++ # Loop ++ # https://www.lispworks.com/documentation/lw70/CLHS/Body/m_dolist.htm ++ # https://www.lispworks.com/documentation/lw70/CLHS/Body/m_dotime.htm ++ - match: (?i:dolist|dotimes){{break}} ++ scope: keyword.control.loop.lisp ++ set: expect-list ++ # https://www.lispworks.com/documentation/lw70/CLHS/Body/m_do_do.htm ++ - match: (?i:do\*?|do-(?:all-|external-)?symbols|iterate){{break}} ++ scope: keyword.control.loop.lisp ++ set: expect-lists-list ++ # Other ++ - match: (?xi:block|map(?:can|car|con|c|list|l)){{break}} + scope: keyword.control.lisp ++ pop: 1 ++ ++###[ LOOP STATEMENTS ]######################################################### ++ ++ loop: ++ # https://www.lispworks.com/documentation/lw70/CLHS/Body/06_a.htm ++ # https://www.lispworks.com/documentation/lw70/CLHS/Body/m_loop.htm ++ - match: (?i:loop){{break}} ++ scope: keyword.control.loop.lisp ++ set: loop-body ++ ++ loop-body: ++ - meta_scope: meta.loop.lisp ++ - include: paren-pop ++ ++ # declaration keywords ++ - match: (?i:named){{break}} ++ scope: keyword.declaration.name.lisp ++ push: loop-name ++ - match: (?i:with){{break}} ++ scope: keyword.declaration.variable.lisp ++ ++ # loop termination test keywords ++ - match: (?i:always|as|do|doing|for|never|repeat|thereis|until|while){{break}} ++ scope: keyword.control.loop.lisp ++ # conditional keywords ++ - match: (?i:end|else|if|then|unless|when){{break}} ++ scope: keyword.control.conditional.lisp ++ # control flow keywords ++ - match: (?i:finally|initially){{break}} ++ scope: keyword.control.flow.lisp ++ - match: (?i:return(?:-from)?){{break}} ++ scope: keyword.control.flow.return.lisp + +- functions: +- - match: \b(?i:(defun|defmethod|defmacro))\b\s+([\w\-!?<>]*) +- scope: meta.function.lisp ++ # operator keywords ++ - match: ={{break}} # can be both, assignment and comparison ++ scope: keyword.operator.lisp ++ - match: (?i:into){{break}} ++ scope: keyword.operator.assignment.lisp ++ - match: (?i:and){{break}} ++ scope: keyword.operator.logical.lisp ++ - match: |- ++ (?xi: across | above | being | below | by | each | in | of | on | the ++ | (?:down|up)?to | (?:up)?from | using ){{break}} ++ scope: keyword.other.loop.lisp ++ ++ # expressions ++ - include: comments ++ - include: parens ++ - include: numbers ++ - include: strings ++ - include: annotations ++ - include: keywords ++ - include: quotes ++ - include: macros ++ - include: modifiers ++ - match: (?={{identifier}}) ++ push: loop-identifier ++ ++ loop-identifier: ++ - include: builtin-function ++ - include: identifier ++ ++ loop-name: ++ - match: '{{identifier}}' ++ scope: entity.name.loop.lisp ++ captures: ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp ++ pop: 1 ++ - include: comments ++ - include: else-pop ++ ++###[ QUOTE STATEMENTS ]######################################################## ++ ++ quote: ++ - match: (?i:quote){{break}} ++ scope: meta.function-call.lisp support.function.lisp ++ set: quote-args ++ ++ quote-args: ++ - include: paren-pop ++ - include: quoted-parens ++ - include: expressions ++ ++###[ FUNCTIONS ]############################################################### ++ ++ builtin-function: ++ - match: '{{builtin_functions}}' ++ scope: meta.function-call.lisp support.function.lisp ++ pop: 1 ++ ++ other-function: ++ # functions starting with `make-` are most likely used as constructors ++ - match: make-{{identifier}} ++ scope: meta.function-call.lisp variable.function.constructor.lisp ++ captures: ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp ++ pop: 1 ++ # mutating functions changing data in-place end with ! ++ - match: (?![+*#`'&]\S){{identifier}}\!{{break}} ++ scope: meta.function-call.lisp variable.function.mutating.lisp ++ captures: ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp ++ pop: 1 ++ # predicate functions returning boolean values end with ? or -p ++ - match: (?![+*#`'&]\S){{identifier}}(?:\?|-p){{break}} ++ scope: meta.function-call.lisp variable.function.predicate.lisp ++ captures: ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp ++ pop: 1 ++ # anything not looking like global variable, constant or macro ++ - match: (?![+*#`'&]\S|{{builtin_constants}}|{{builtin_types}}){{identifier}} ++ scope: meta.function-call.lisp variable.function.lisp ++ captures: ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp ++ pop: 1 ++ ++###[ MACROS ]################################################################## ++ ++ macros: ++ # https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node191.html ++ - include: macro-arrays ++ - include: macro-characters ++ - include: macro-conditionals ++ - include: macro-functions ++ - include: macro-numbers ++ - include: macro-pathnames ++ - include: macro-structures ++ - include: macro-throw-exceptions ++ - include: macro-variables ++ - include: macro-vectors ++ ++ macro-conditionals: ++ # Feature-flags ++ - match: (#[-+])({{identifier}})? ++ captures: ++ 1: keyword.control.conditional.lisp ++ 2: constant.other.feature.lisp ++ 3: punctuation.definition.symbol.begin.lisp ++ 4: punctuation.definition.symbol.end.lisp ++ ++ macro-arrays: ++ # https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node29.html ++ - match: (#\d*[aA])(\() ++ captures: ++ 1: punctuation.definition.array.lisp ++ 2: meta.parens.lisp punctuation.section.parens.begin.lisp ++ push: macro-array-body ++ - match: (#\d*[aA])({{identifier}}) ++ captures: ++ 1: punctuation.definition.array.lisp ++ 2: variable.other.lisp ++ 3: punctuation.definition.symbol.begin.lisp ++ 4: punctuation.definition.symbol.end.lisp ++ ++ macro-array-body: ++ - meta_scope: meta.array.lisp ++ - meta_content_scope: meta.parens.lisp ++ - match: \) ++ scope: meta.parens.lisp punctuation.section.parens.end.lisp ++ pop: 1 ++ - include: parens ++ - include: expressions ++ ++ macro-characters: ++ # Standard Characters ++ # https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node22.html ++ # Non-Standard Characters ++ # https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node24.html ++ # Character Attributes ++ # https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node25.html ++ - match: (#(\d*)\\){{char_attributes}}?(?i:Space|NewLine){{break}} ++ scope: constant.character.standard.lisp ++ captures: ++ 1: punctuation.definition.constant.lisp ++ 2: storage.modifier.attributes.lisp ++ - match: (#(\d*)\\){{char_attributes}}?(?i:Backspace|Tab|Linefeed|Page|Return|RubOut){{break}} ++ scope: constant.character.semi-standard.lisp ++ captures: ++ 1: punctuation.definition.constant.lisp ++ 2: storage.modifier.attributes.lisp ++ - match: (#(\d*)\\){{char_attributes}}?{{standard_char}}{{break}} ++ scope: constant.character.standard.lisp + captures: +- 1: storage.type.function-type.lisp +- 2: entity.name.function.lisp +- - match: \b(?i:zerop|yes-or-no-p|y-or-n-p|write-to-string|write-string|write-char|write-byte|write|with-standard-io-syntax|with-slots|with-simple-restart|with-package-iterator|with-output-to-string|with-open-stream|with-open-file|with-input-from-string|with-hash-table-iterator|with-condition-restarts|with-compilation-unit|with-accessors|wild-pathname-p|warn|vectorp|vector-push-extend|vector-push|vector-pop|vector|variable-information|values-list|values|user-homedir-pathname|use-value|use-package|upper-case-p|upgraded-complex-part-type|upgraded-array-element-type|update-instance-for-redefined-class|update-instance-for-different-class|unuse-package|untrace|until-if|until|unread-char|union|unintern|unexport|typep|type-of|type-error-expected-type|type-error-datum|two-way-stream-output-stream|two-way-stream-input-stream|truncate|truename|tree-equal|translate-pathname|translate-logical-pathname|trace|to-alter|time|third|terpri|terminate-producing|tenth|tanh|tan|tailp|synonym-stream-symbol|symbolp|symbol-value|symbol-plist|symbol-package|symbol-name|symbol-function|sxhash|svref|summing|sum|subtypep|substitute-if-not|substitute-if|substitute|subst-if-not|subst-if|subst|subsetp|subseries|subseq|sublis|stringp|string>=|string>|string=|string<=|string<|string/=|string-upcase|string-trim|string-right-trim|string-not-lessp|string-not-greaterp|string-not-equal|string-lessp|string-left-trim|string-greaterp|string-equal|string-downcase|string-char-p|string-capitalize|string|streamp|stream-external-format|stream-error-stream|stream-element-type|store-value|step|standard-char-p|stable-sort|sqrt|split-if|split|special-form-p|sort|some|software-version|software-type|slot-value|slot-unbound|slot-missing|slot-makunbound|slot-exists-p|slot-boundp|sleep|sixth|sinh|sin|simple-vector-p|simple-string-p|simple-condition-format-string|simple-condition-format-arguments|simple-bit-vector-p|signum|signal|short-site-name|shiftf|shared-initialize|shadowing-import|shadow|seventh|setq|setf|set-syntax-from-char|set-pprint-dispatch|set-macro-character|set-exclusive-or|set-dispatch-macro-character|set-difference|set-char-bit|set|series|second|search|schar|scan-symbols|scan-sublists|scan-range|scan-plist|scan-multiple|scan-lists-of-lists-fringe|scan-lists-of-lists|scan-hash|scan-fn-inclusive|scan-fn|scan-file|scan-alist|scan|scale-float|sbit|rplacd|rplaca|row-major-aref|round|rotatef|room|reverse|revappend|result-of|restart-name|restart-case|restart-bind|rest|require|replace|rename-package|rename-file|remprop|remove-method|remove-duplicates|remove|remhash|remf|reinitialize-instance|reduce|realpart|realp|readtablep|readtable-case|read-preserving-whitespace|read-line|read-from-string|read-delimited-list|read-char-no-hang|read-char|read-byte|read|rationalp|rationalize|rational|rassoc-if-not|rassoc-if|rassoc|random-state-p|random|quote|pushnew|push|psetq|psetf|provide|progn|prog2|prog1|producing|proclaim|probe-file|print-unreadable-object|print-object|print|prin1|previous|pprint-tabular|pprint-tab|pprint-pop|pprint-newline|pprint-logical-block|pprint-linear|pprint-indent|pprint-fill|pprint-exit-if-list-exhausted|pprint-dispatch|positions|position-if-not|position-if|position|pop|plusp|phase|peek-char|pathnamep|pathname-version|pathname-type|pathname-name|pathname-match-p|pathname-host|pathname-directory|pathname-device|pathname|parse-namestring|parse-macro|parse-integer|pairlis|packagep|package-used-by-list|package-use-list|package-shadowing-symbols|package-nicknames|package-name|package-error-package|output-stream-p|open-stream-p|open|oddp|nunion|numerator|numberp|nthcdr|nth-value|nth|nsubstitute-if-not|nsubstitute-if|nsubstitute|nsubst-if-not|nsubst-if|nsubst|nsublis|nstring-upcase|nstring-downcase|nstring-capitalize|nset-exclusive-or|nset-difference|nreverse|nreconc|notevery|notany|no-next-method|no-applicable-method|ninth|nintersection|next-out|next-method-p|next-in|nconcing|nconc|nbutlast|namestring|name-char|multiple-value-setq|multiple-value-list|multiple-value-bind|muffle-warning|mismatch|minusp|minimizing|minimize|mingle|method-qualifiers|method-combination-error|merge-pathnames|merge|memberp|member-if-not|member-if|member|maximizing|maximize|mask-field|mask|mapping|maphash|map-into|map-fn|map|makunbound|make-two-way-stream|make-synonym-stream|make-symbol|make-string-output-stream|make-string-input-stream|make-string|make-sequence|make-random-state|make-pathname|make-package|make-load-form-saving-slots|make-load-form|make-list|make-instances-obsolete|make-instance|make-hash-table|make-echo-stream|make-dispatch-macro-character|make-condition|make-concatenated-stream|make-char|make-broadcast-stream|make-array|macroexpand-1|macroexpand|macro-function|machine-version|machine-type|machine-instance|lower-case-p|loop-finish|long-site-name|logtest|logorc2|logorc1|lognot|lognand|logical-pathname-translations|logical-pathname|logcount|logbitp|logandc2|logandc1|log|locally|load-logical-pathname-translations|load|listp|listen|list-length|list-all-packages|list*|list|lisp-implementation-version|lisp-implementation-type|length|ldiff|ldb-test|ldb|lcm|latch|last|lambda|keywordp|iterate|isqrt|invoke-restart|invoke-debugger|invalid-method-error|intersection|intern|interactive-stream-p|integerp|integer-length|integer-decode-float|int-char|inspect|input-stream-p|initialize-instance|in-package|import|imagpart|ignore-errors|identity|host-namestring|hash-table-test|hash-table-size|hash-table-rehash-threshold|hash-table-rehash-size|hash-table-p|hash-table-count|handler-case|handler-bind|graphic-char-p|gethash|getf|get-universal-time|get-setf-method-multiple-value|get-setf-method|get-properties|get-output-stream-string|get-internal-run-time|get-internal-real-time|get-decoded-time|get|gentemp|gensym|generic-function|generator|gcd|gathering|gatherer|functionp|function-lambda-expression|function-keywords|function-information|funcall|fourth|formatter|format|floor|floatp|float-sign|float-radix|float-precision|float-digits|float|first|finish-output|find-symbol|find-restart|find-package|find-method|find-if-not|find-if|find-class|find-all-symbols|find|fill-pointer|fill|file-write-date|file-string-length|file-position|file-namestring|file-length|file-error-pathname|file-author|fifth|ffloor|fdefinition|fboundp|f|expt|export|expand|exp|every|evenp|evalhook|eval|error|ensure-generic-function|enough-namestring|endp|encode-universal-time|enclose|encapsulated|elt|eighth|ed|echo-stream-output-stream|echo-stream-input-stream|ecase|dribble|dpb|documentation|do-symbols|do-external-symbols|do-all-symbols|disassemble|directory-namestring|directory|digit-char-p|digit-char|destructuring-bind|describe-object|describe|deposit-field|denominator|delete-package|delete-if-not|delete-if|delete-file|delete-duplicates|delete|defvar|deftype|defstruct|defpackage|define-setf-method|define-modify-macro|define-method-combination|define-declaration|define-condition|define-compiler-macro|defgeneric|defclass|decode-universal-time|decode-float|declaration-information|declaim|counting|count-if-not|count-if|count|cotruncate|cosh|cos|copy-tree|copy-symbol|copy-seq|copy-readtable|copy-pprint-dispatch|copy-list|copy-alist|continue|constantp|consp|cons|conjugate|concatenated-stream-streams|concatenate|compute-restarts|compute-applicable-methods|complexp|complex|complement|compiler-macroexpand-1|compiler-macroexpand|compiler-macro-function|compiler-let|compiled-function-p|compile-file-pathname|compile-file|compile|commonp|collecting-fn|collecting|collect-sum|collect-plist|collect-or|collect-nth|collect-nconc|collect-min|collect-max|collect-length|collect-last|collect-hash|collect-fn|collect-first|collect-file|collect-append|collect-and|collect-alist|collect|coerce|code-char|clrhash|close|clear-input|class-of|class-name|cis|chunk|choose-if|choose|check-type|characterp|character|char>=|char>|char=|char<=|char<|char/=|char-upcase|char-not-lessp|char-not-greaterp|char-not-equal|char-name|char-lessp|char-int|char-greaterp|char-font|char-equal|char-downcase|char-code|char-bits|char-bit|char|change-class|cerror|cell-error-name|ceiling|cdr|cddr|cdddr|cddddr|cdddar|cddar|cddadr|cddaar|cdar|cdadr|cdaddr|cdadar|cdaar|cdaadr|cdaaar|ccase|catenate|car|call-next-method|call-method|cadr|caddr|cadddr|caddar|cadar|cadadr|cadaar|caar|caadr|caaddr|caadar|caaar|caaadr|caaaar|byte-size|byte-position|byte|butlast|broadcast-stream-streams|break|boundp|both-case-p|boole|bit-xor|bit-vector-p|bit-orc2|bit-orc1|bit-not|bit-nor|bit-nand|bit-ior|bit-eqv|bit-andc2|bit-andc1|bit-and|bit|augment-environment|atom|atanh|atan|assoc-if-not|assoc-if|assoc|assert|asinh|asin|ash|arrayp|array-total-size|array-row-major-index|array-rank|array-in-bounds-p|array-has-fill-pointer-p|array-element-type|array-dimensions|array-dimension|arithmetic-error-operation|arithmetic-error-operands|aref|apropos-list|apropos|applyhook|apply|appending|append|alter|alphanumericp|alpha-char-p|adjustable-array-p|adjust-array|adjoin|add-method|acosh|acos|acons|abs|abort)\b +- scope: support.function.lisp ++ 1: punctuation.definition.constant.lisp ++ 2: storage.modifier.attributes.lisp ++ - match: (#(\d*)\\){{char_attributes}}?{{identifier_char}}+{{break}} ++ scope: constant.character.non-standard.lisp ++ captures: ++ 1: punctuation.definition.constant.lisp ++ 2: storage.modifier.attributes.lisp ++ ++ macro-functions: ++ # Function reference ++ - match: \#[Mm'] ++ scope: punctuation.definition.function.lisp ++ push: macro-function-name ++ ++ macro-function-name: ++ - include: namespaces ++ - include: operator ++ - include: builtin-function ++ - include: other-function ++ - include: immediately-pop ++ ++ macro-numbers: ++ # binary rational numbers ++ - match: |- ++ (?xi) ++ ( \#(?: b | 2r ) ) ++ ( [-+]? ) ++ ( [01]+ (/) [01]+ ) ++ {{break}} ++ scope: meta.number.rational.binary.lisp ++ captures: ++ 1: constant.numeric.base.lisp ++ 2: keyword.operator.arithmetic.lisp ++ 3: constant.numeric.value.lisp ++ 4: keyword.operator.arithmetic.lisp ++ # binary integer numbers ++ - match: |- ++ (?xi) ++ ( \#(?: b | 2r ) ) ++ ( [-+]? ) ++ ( [01]+ (?: (\.)(?![01]) | {{break}} ) ) ++ scope: meta.number.integer.binary.lisp ++ captures: ++ 1: constant.numeric.base.lisp ++ 2: keyword.operator.arithmetic.lisp ++ 3: constant.numeric.value.lisp ++ 4: punctuation.separator.decimal.lisp ++ # octal rational numbers ++ - match: |- ++ (?xi) ++ ( \#(?: o | 8r ) ) ++ ( [-+]? ) ++ ( [0-7]+ (/) [0-7]+ ) ++ {{break}} ++ scope: meta.number.rational.octal.lisp ++ captures: ++ 1: constant.numeric.base.lisp ++ 2: keyword.operator.arithmetic.lisp ++ 3: constant.numeric.value.lisp ++ 4: keyword.operator.arithmetic.lisp ++ # octal integer numbers ++ - match: |- ++ (?xi) ++ ( \#(?: o | 8r ) ) ++ ( [-+]? ) ++ ( [0-7]+ (?: (\.)(?![0-7]) | {{break}} ) ) ++ scope: meta.number.integer.octal.lisp ++ captures: ++ 1: constant.numeric.base.lisp ++ 2: keyword.operator.arithmetic.lisp ++ 3: constant.numeric.value.lisp ++ 4: punctuation.separator.decimal.lisp ++ # hexadecimal rational numbers ++ - match: |- ++ (?xi) ++ ( \#(?: x | 16r ) ) ++ ( [-+]? ) ++ ( \h+ (/) \h+ ) ++ {{break}} ++ scope: meta.number.rational.hexadecimal.lisp ++ captures: ++ 1: constant.numeric.base.lisp ++ 2: keyword.operator.arithmetic.lisp ++ 3: constant.numeric.value.lisp ++ 4: keyword.operator.arithmetic.lisp ++ # hexadecimal integer numbers ++ - match: |- ++ (?xi) ++ ( \#(?: x | 16r ) ) ++ ( [-+]? ) ++ ( \h+ (?: (\.)(?!\h) | {{break}} ) ) ++ scope: meta.number.integer.hexadecimal.lisp ++ captures: ++ 1: constant.numeric.base.lisp ++ 2: keyword.operator.arithmetic.lisp ++ 3: constant.numeric.value.lisp ++ 4: punctuation.separator.decimal.lisp ++ # radix rational numbers ++ - match: |- ++ (?xi) ++ ( \#\d+r ) ++ ( [-+]? ) ++ ( [[:alnum:]]+ (/) [[:alnum:]]+ ) ++ {{break}} ++ scope: meta.number.rational.other.lisp ++ captures: ++ 1: constant.numeric.base.lisp ++ 2: keyword.operator.arithmetic.lisp ++ 3: constant.numeric.value.lisp ++ 4: keyword.operator.arithmetic.lisp ++ # radix integer numbers ++ - match: |- ++ (?xi) ++ ( \#\d+r ) ++ ( [-+]? ) ++ ( [[:alnum:]]+ (?: (\.)(?![[:alnum:]]) | {{break}} ) ) ++ scope: meta.number.integer.other.lisp ++ captures: ++ 1: constant.numeric.base.lisp ++ 2: keyword.operator.arithmetic.lisp ++ 3: constant.numeric.value.lisp ++ 4: punctuation.separator.decimal.lisp ++ # complex numbers ++ - match: (#[cC])(\() ++ captures: ++ 1: punctuation.definition.complex.lisp ++ 2: meta.parens.lisp punctuation.section.parens.begin.lisp ++ push: macro-numbers-complex-body ++ ++ macro-numbers-complex-body: ++ - meta_scope: meta.number.complex.lisp ++ - meta_content_scope: meta.parens.lisp ++ - match: \) ++ scope: meta.parens.lisp punctuation.section.parens.end.lisp ++ pop: 1 ++ - include: parens ++ - include: expressions + +- operators: +- - match: '\/\=|\>\=|\<\=|\=|\>|\<|\b(?i:max|min|eq|neq|eql|equalp|equal)\b' ++ macro-pathnames: ++ - match: (#\d*[pP])(\") ++ captures: ++ 1: punctuation.definition.pathname.lisp ++ 2: string.quoted.double.lisp punctuation.definition.string.begin.lisp ++ push: macro-pathname-body ++ ++ macro-pathname-body: ++ - meta_scope: meta.path.lisp ++ - meta_content_scope: string.quoted.double.lisp ++ - match: \" ++ scope: string.quoted.double.lisp punctuation.definition.string.end.lisp ++ pop: 1 ++ - match: \\. ++ scope: constant.character.escape.lisp ++ ++ macro-structures: ++ # Structures ++ # https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node39.html ++ - match: (#\d*[sS])(\() ++ captures: ++ 1: punctuation.definition.struct.lisp ++ 2: meta.parens.lisp punctuation.section.parens.begin.lisp ++ push: ++ - macro-struct-body ++ - macro-struct-name ++ ++ macro-struct-name: ++ - match: '{{identifier}}' ++ scope: entity.name.struct.lisp ++ captures: ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp ++ pop: 1 ++ - include: else-pop ++ ++ macro-struct-body: ++ - meta_scope: meta.struct.lisp ++ - meta_content_scope: meta.parens.lisp ++ - match: \) ++ scope: meta.parens.lisp punctuation.section.parens.end.lisp ++ pop: 1 ++ - include: lists ++ - include: expressions ++ ++ macro-vectors: ++ # Vector ++ # https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node30.html ++ - match: (#\d*)(\() ++ captures: ++ 1: punctuation.definition.vector.lisp ++ 2: meta.parens.lisp punctuation.section.parens.begin.lisp ++ push: macro-vector-body ++ # Bit vector ++ # https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node32.html ++ - match: (#\d*\*)([01]*){{break}} ++ scope: meta.vector.lisp ++ captures: ++ 1: punctuation.definition.vector.lisp ++ 2: meta.number.integer.binary constant.numeric.value.lisp ++ ++ macro-vector-body: ++ - meta_scope: meta.vector.lisp ++ - meta_content_scope: meta.parens.lisp ++ - match: \) ++ scope: meta.parens.lisp punctuation.section.parens.end.lisp ++ pop: 1 ++ - include: parens ++ - include: expressions ++ ++ macro-throw-exceptions: ++ - match: '#[ \t\n\r)<]' ++ scope: keyword.control.exception.throw.lisp ++ ++ macro-variables: ++ # Inline runtime/loadtime eval ++ - match: \#[.,:] ++ scope: punctuation.definition.variable.lisp ++ # Local assignment ++ - match: ((#)\d+)\s*(=) ++ captures: ++ 1: variable.other.local.lisp ++ 2: punctuation.definition.variable.lisp ++ 3: keyword.operator.assignment.lisp ++ - match: (#)(\d+)(#) ++ scope: variable.other.lisp ++ captures: ++ 1: punctuation.definition.variable.begin.lisp ++ 2: variable.other.local.lisp ++ 3: punctuation.definition.variable.end.lisp ++ ++###[ OPERATORS ]############################################################### ++ ++ modifiers: ++ # https://www.lispworks.com/documentation/lw70/CLHS/Body/02_df.htm ++ - match: '[`'']' ++ scope: punctuation.definition.quoted.lisp ++ - match: ',@?' ++ scope: punctuation.definition.variable.lisp ++ ++ operator: ++ # Contains 'Standardized Compound Type Specifier Names' ++ # https://www.lispworks.com/documentation/lw70/CLHS/Body/04_bc.htm ++ - match: (?:(?:/=|>=|<=|=|>|<)|(?i:max|min|eq|neq|eql|equalp|equal)){{break}} + scope: keyword.operator.comparison.lisp +- - match: '\+|\-|\*|\/|\b(?i:mod|rem|incf|decf)\b' ++ pop: 1 ++ - match: ={{break}} ++ scope: keyword.operator.assignment.lisp ++ pop: 1 ++ - match: (?:[-+*/]|(?i:mod|rem|incf|decf)){{break}} + scope: keyword.operator.arithmetic.lisp +- - match: \b(?i:and|or|not)\b ++ pop: 1 ++ - match: (?i:and|or|not|satisfies){{break}} + scope: keyword.operator.logical.lisp +- - match: \b(?i:logand|logior|logxor|lognor|logeqv)\b ++ pop: 1 ++ - match: (?i:logand|logior|logxor|lognor|logeqv){{break}} + scope: keyword.operator.bitwise.lisp ++ pop: 1 + +- parens: +- - match: '\(' +- scope: punctuation.definition.group.begin.lisp +- push: +- - meta_scope: meta.group.lisp +- - match: '\)' +- scope: punctuation.definition.group.end.lisp +- pop: true +- - include: expressions +- - include: parens +- - match: '\)' +- scope: invalid.illegal.stray-bracket-end.lisp +- +- constants: +- - match: \b(?i:null|t|single-float-negative-epsilon|single-float-epsilon|short-float-negative-epsilon|short-float-epsilon|pi|nil|multiple-values-limit|most-positive-single-float|most-positive-short-float|most-positive-long-float|most-positive-fixnum|most-positive-double-float|most-negative-single-float|most-negative-short-float|most-negative-long-float|most-negative-fixnum|most-negative-double-float|long-float-negative-epsilon|long-float-epsilon|least-positive-single-float|least-positive-short-float|least-positive-normalized-single-float|least-positive-normalized-short-float|least-positive-normalized-long-float|least-positive-normalized-double-float|least-positive-long-float|least-positive-double-float|least-negative-single-float|least-negative-short-float|least-negative-normalized-single-float|least-negative-normalized-short-float|least-negative-normalized-long-float|least-negative-normalized-double-float|least-negative-long-float|least-negative-double-float|lambda-parameters-limit|lambda-list-keywords|internal-time-units-per-second|double-float-negative-epsilon|double-float-epsilon|char-super-bit|char-meta-bit|char-hyper-bit|char-font-limit|char-control-bit|char-code-limit|char-bits-limit|call-arguments-limit|array-total-size-limit|array-rank-limit|array-dimension-limit)\b +- scope: constant.language.lisp +- - match: '(#)(\w|[\\+-=<>''"&#])+' +- scope: constant.character.lisp +- captures: +- 1: punctuation.definition.constant.lisp ++###[ LITERALS ]################################################################ + + numbers: +- - match: '\b((0(x|X)\h*)|(([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)(?i:l|ul|u|f|ll|ull)?\b' +- scope: constant.numeric.lisp ++ # https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node16.html ++ # https://www.lispworks.com/documentation/lw70/CLHS/Body/02_ca.htm ++ # decimal floating point ++ - match: |- ++ (?x) ++ ( [-+]? ) ++ ( \d* (?: (\.) \d+ ) {{exponent}}? ++ | \d+ (?: (\.) \d* )? {{exponent}}) ++ {{break}} ++ scope: meta.number.float.decimal.lisp ++ captures: ++ 1: keyword.operator.arithmetic.lisp ++ 2: constant.numeric.value.lisp ++ 3: punctuation.separator.decimal.lisp ++ 4: punctuation.separator.decimal.lisp ++ # decimal rational numbers ++ - match: |- ++ (?x) ++ ( [-+]? ) ++ ( \d+ (/) \d+ ) ++ {{break}} ++ scope: meta.number.rational.decimal.lisp ++ captures: ++ 1: keyword.operator.arithmetic.lisp ++ 2: constant.numeric.value.lisp ++ 3: keyword.operator.arithmetic.lisp ++ # decimal integer numbers ++ - match: |- ++ (?x) ++ ( [-+]? ) ++ ( \d+ (?: (\.)(?!\d) | {{break}} ) ) ++ scope: meta.number.integer.decimal.lisp ++ captures: ++ 1: keyword.operator.arithmetic.lisp ++ 2: constant.numeric.value.lisp ++ 3: punctuation.separator.decimal.lisp + + strings: +- - match: '"' ++ # https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node31.html ++ - match: \" + scope: punctuation.definition.string.begin.lisp ++ push: string-body ++ ++ string-body: ++ - meta_include_prototype: false ++ - meta_scope: meta.string.lisp string.quoted.double.lisp ++ - match: \" ++ scope: punctuation.definition.string.end.lisp ++ pop: 1 ++ - match: \\. ++ scope: constant.character.escape.lisp ++ - include: string-format-mini-language ++ ++ string-format-mini-language: ++ # https://www.hexstreamsoft.com/articles/common-lisp-format-reference/clhs-summary/#subsections-summary-table ++ - match: (~)(\d*)[%&|~] ++ scope: constant.character.escape.lisp ++ captures: ++ 1: punctuation.definition.placeholder.lisp ++ # 2: constant.numeric.integer.decimal.lisp ++ - match: (~):?@?[CR] ++ scope: constant.other.placeholder.lisp ++ captures: ++ 1: punctuation.definition.placeholder.lisp ++ # ~R ++ - match: |- ++ (?x: ++ (~) ++ (?:(\d*) # Radix ++ (?:(,)\d* # mincol ++ (?:(,)(?:'.)? # padchar ++ (?:(,)(?:'.)? # comma-char ++ (?:(,)\d*)? # comma-interval ++ )?)?)?) ++ ):?@?[rR] ++ scope: constant.other.placeholder.lisp ++ captures: ++ 1: punctuation.definition.placeholder.lisp ++ 2: punctuation.definition.numeric.base.lisp ++ 3: punctuation.separator.sequence.lisp ++ 4: punctuation.separator.sequence.lisp ++ 5: punctuation.separator.sequence.lisp ++ 6: punctuation.separator.sequence.lisp ++ # ~b, ~o, ~d, ~x ++ - match: |- ++ (?x: ++ (~) ++ (?:\d* # mincol ++ (?:(,)(?:'.)? # padchar ++ (?:(,)(?:'.)? # comma-char ++ (?:(,)\d*)? # comma-interval ++ )?)?)? ++ ):?@?[bBoOdDxX] ++ scope: constant.other.placeholder.lisp ++ captures: ++ 1: punctuation.definition.placeholder.lisp ++ 2: punctuation.separator.sequence.lisp ++ 3: punctuation.separator.sequence.lisp ++ 4: punctuation.separator.sequence.lisp ++ # ~f ++ - match: |- ++ (?x: ++ (~) ++ (?:\d* # width ++ (?:(,)\d* # decimals ++ (?:(,)\d* # scale ++ (?:(,)(?:'.)? # overflowchar ++ (?:(,)(?:'.)? # padchar ++ )?)?)?)?)? ++ )@?[fF] ++ scope: constant.other.placeholder.lisp ++ captures: ++ 1: punctuation.definition.placeholder.lisp ++ 2: punctuation.separator.sequence.lisp ++ 3: punctuation.separator.sequence.lisp ++ 4: punctuation.separator.sequence.lisp ++ 5: punctuation.separator.sequence.lisp ++ # ~e, ~g ++ - match: |- ++ (?x: ++ (~) ++ (?:\d* # width ++ (?:(,)\d* # decimals ++ (?:(,)\d* # exponent-width ++ (?:(,)\d* # scale ++ (?:(,)(?:'.)? # overflowchar ++ (?:(,)(?:'.)? # padchar ++ (?:(,)(?:'.)? # exponentchar ++ )?)?)?)?)?)?)? ++ )@?[eEgG] ++ scope: constant.other.placeholder.lisp ++ captures: ++ 1: punctuation.definition.placeholder.lisp ++ 2: punctuation.separator.sequence.lisp ++ 3: punctuation.separator.sequence.lisp ++ 4: punctuation.separator.sequence.lisp ++ 5: punctuation.separator.sequence.lisp ++ 6: punctuation.separator.sequence.lisp ++ 7: punctuation.separator.sequence.lisp ++ # ~$ ++ - match: |- ++ (?x: ++ (~) ++ (?:\d* # decimals ++ (?:(,)\d* # min-units ++ (?:(,)\d* # min-width ++ (?:(,)(?:'.)? # padchar ++ )?)?)?)? ++ ):?@?\$ ++ scope: constant.other.placeholder.lisp ++ captures: ++ 1: punctuation.definition.placeholder.lisp ++ 2: punctuation.separator.sequence.lisp ++ 3: punctuation.separator.sequence.lisp ++ 4: punctuation.separator.sequence.lisp ++ # ~a, ~s ++ - match: |- ++ (?x: ++ (~) ++ (?:\d* # min-col ++ (?:(,)\d* # col-inc ++ (?:(,)\d* # min-pad ++ (?:(,)(?:'.)? # padchar ++ )?)?)?)? ++ ):?@?[aAsS] ++ scope: constant.other.placeholder.lisp ++ captures: ++ 1: punctuation.definition.placeholder.lisp ++ 2: punctuation.separator.sequence.lisp ++ 3: punctuation.separator.sequence.lisp ++ 4: punctuation.separator.sequence.lisp ++ ++###[ SYMBOLS ]################################################################# ++ ++ annotations: ++ - match: (&){{identifier}} ++ scope: variable.annotation.lisp ++ captures: ++ 1: punctuation.definition.annotation.lisp ++ 2: punctuation.definition.symbol.begin.lisp ++ 3: punctuation.definition.symbol.end.lisp ++ ++ keywords: ++ # The colon : is a package marker. ++ # If the package name is missing, the symbol is in the KEYWORD package. ++ - match: (::?)({{identifier}}) ++ captures: ++ 1: punctuation.accessor.lisp ++ 2: keyword.other.symbol.lisp ++ 3: punctuation.definition.symbol.begin.lisp ++ 4: punctuation.definition.symbol.end.lisp ++ - match: \.{{break}} ++ scope: keyword.control.lisp ++ ++ identifiers: ++ # https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node27.html ++ # Pushing a dedicated context reduces syntax cache size ++ # and increases parsing performance, significantly. ++ - match: (?={{identifier}}) ++ push: identifier ++ ++ identifier: ++ - include: namespaces ++ # types ++ - match: '{{builtin_types}}' ++ scope: storage.type.lisp ++ pop: 1 ++ # constants ++ - match: (?i:true|false){{break}} ++ scope: constant.language.boolean.lisp ++ pop: 1 ++ - match: (?i:nil|null){{break}} ++ scope: constant.language.null.lisp ++ pop: 1 ++ - match: '{{builtin_constants}}' ++ scope: constant.language.lisp ++ pop: 1 ++ - match: \+\S+\+{{break}} ++ scope: constant.other.lisp ++ pop: 1 ++ # variables ++ - match: '{{builtin_variables}}' ++ scope: variable.language.lisp ++ pop: 1 ++ - match: '{{identifier}}' ++ scope: variable.other.lisp ++ captures: ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp ++ pop: 1 ++ - include: immediately-pop ++ ++ parameters: ++ # parameter with initial value ++ - match: \( ++ scope: punctuation.section.parens.begin.lisp + push: +- - meta_scope: string.quoted.double.lisp +- - match: '"' +- scope: punctuation.definition.string.end.lisp +- pop: true +- - match: \\. +- scope: constant.character.escape.lisp +- +- block-comment: +- - match: '#\|' +- scope: punctuation.definition.comment.begin.lisp +- push: +- - meta_scope: comment.block.lisp +- - include: block-comment +- - match: '\|#' +- scope: punctuation.definition.comment.end.lisp +- pop: true ++ - paren-body ++ - parameter-name ++ # parameter without initial value ++ - match: '{{identifier}}' ++ scope: variable.parameter.lisp ++ captures: ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp + +- comments: +- - include: block-comment +- - match: '\|#' +- scope: invalid.illegal.stray-comment-end.lisp +- - match: (;).*$\n? +- scope: comment.line.semicolon.lisp ++ parameter-name: ++ - include: annotations ++ - include: keywords ++ - include: modifiers ++ - match: '{{identifier}}' ++ scope: variable.parameter.lisp ++ captures: ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp ++ pop: 1 ++ - include: comments ++ - include: else-pop ++ ++ namespaces: ++ # built-in namespace indicating keyword symbols ++ # note: accessor is consumed by `keywords` context ++ - match: (\|?)keyword(\|?)(?=:) ++ scope: variable.namespace.lisp ++ captures: ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp ++ # normal namespaces but not an escaped char #\: ++ - match: ((?!#\\?){{identifier}})(::?) ++ captures: ++ 1: variable.namespace.lisp ++ 2: punctuation.definition.symbol.begin.lisp ++ 3: punctuation.definition.symbol.end.lisp ++ 4: punctuation.accessor.lisp ++ ++ variables: ++ # Symbols evaluate to their values ++ # https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node27.html ++ - match: '{{identifier}}' ++ scope: variable.other.lisp + captures: +- 1: punctuation.definition.comment.lisp ++ 1: punctuation.definition.symbol.begin.lisp ++ 2: punctuation.definition.symbol.end.lisp ++ ++###[ PROTOTYPES ]############################################################## ++ ++ else-pop: ++ - match: (?=\S) ++ pop: 1 ++ ++ immediately-pop: ++ - match: '' ++ pop: 1 ++ ++ paren-pop: ++ - match: (?=\)) ++ pop: 1 ++ ++############################################################################### ++ ++variables: ++ ++ # variables / keyword-symbols ++ identifier: (?:{{identifier_char}}*{{identifier_must_have}}{{identifier_char}}*|{{quoted_identifier}}) ++ identifier_char: (?:\\.|[^{{break_char}}]) ++ identifier_must_have: (?:\\.|[^\d.{{break_char}}]) # May not be only digits or periods ++ quoted_identifier: (\|)(?:\\.|[^|])*(\|) ++ ++ break: (?={{break_char}}|$) ++ break_char: '[\s()"'',:;|]' ++ ++ # caracters ++ standard_char: '[0-9A-Za-z!"#$%&''()*+,\-./:;<=>?@\\\[\]^_`{|}~]' ++ char_attributes: (?:(?:[[:alnum:]_]+-)+\\?) ++ ++ # numbers ++ exponent: (?:[esfdlESFDL][-+]?\d+) ++ ++ declaration_keywords: |- ++ (?xi: ++ declaim ++ | define-(?: compiler-macro | condition | declaration | method-combination | modify-macro | setf-method ) ++ | def(?: class | constant | generic | macro | method | package | parameter | setf | struct | type | un | var ) ++ | generic-(?: flet | function | labels ) ++ | flet ++ | labels ++ | let\*? ++ | locally ++ | macrolet ++ | multiple-value-bind ++ | proclaim ++ | prog\*? ++ | with-(?: accessors | added-methods | condition-restarts | input-from-string | open-file | open-stream | output-to-string | standard-io-syntax ) ++ ){{break}} ++ ++ declaration_identifiers: |- ++ (?xi: ++ dynamic-extent ++ | ftype ++ | ignorable ++ | ignore ++ | inline ++ | notinline ++ | optimize ++ | special ++ | type ++ ){{break}} ++ ++ builtin_constants: |- ++ (?xi: ++ t | true | false | null | nil | pi ++ | array-(?: rank | dimension | total-size )-limit ++ | call-arguments-limit ++ | char-(?: (?: super | hyper | meta | control )-bit | (?: font | code | bits )-limit ) ++ | internal-time-units-per-second ++ | lambda-(?: parameters-limit | list-keywords ) ++ | least-(?: positive | negative )-normalized-(?: single | short | long | double )-float ++ | most-(?: positive | negative )-fixnum ++ | (?: most | least )-(?: positive | negative )-(?: single | short | long | double )-float ++ | multiple-values-limit ++ | (?: single | short | long | double )-float-(?: negative- )?epsilon ++ ){{break}} ++ ++ builtin_functions: |- ++ (?xi: ++ abort ++ | abs ++ | acons ++ | acos ++ | acosh ++ | add-method ++ | adjoin ++ | adjust-array ++ | adjustable-array-p ++ | alpha-char-p ++ | alphanumericp ++ | alter ++ | append ++ | appending ++ | apply ++ | applyhook ++ | apropos ++ | apropos-list ++ | aref ++ | arithmetic-error-operands ++ | arithmetic-error-operation ++ | array-dimension ++ | array-dimensions ++ | array-element-type ++ | array-has-fill-pointer-p ++ | array-in-bounds-p ++ | array-rank ++ | array-row-major-index ++ | array-total-size ++ | arrayp ++ | ash ++ | asin ++ | asinh ++ | assert ++ | assoc ++ | assoc-if ++ | assoc-if-not ++ | atan ++ | atanh ++ | atom ++ | augment-environment ++ | bit ++ | bit-and ++ | bit-andc1 ++ | bit-andc2 ++ | bit-eqv ++ | bit-ior ++ | bit-nand ++ | bit-nor ++ | bit-not ++ | bit-orc1 ++ | bit-orc2 ++ | bit-vector-p ++ | bit-xor ++ | boole ++ | both-case-p ++ | boundp ++ | break ++ | broadcast-stream-streams ++ | butlast ++ | byte ++ | byte-position ++ | byte-size ++ | caaaar ++ | caaadr ++ | caaar ++ | caadar ++ | caaddr ++ | caadr ++ | caar ++ | cadaar ++ | cadadr ++ | cadar ++ | caddar ++ | cadddr ++ | caddr ++ | cadr ++ | call-method ++ | call-next-method ++ | car ++ | catenate ++ | ccase ++ | cdaaar ++ | cdaadr ++ | cdaar ++ | cdadar ++ | cdaddr ++ | cdadr ++ | cdar ++ | cddaar ++ | cddadr ++ | cddar ++ | cdddar ++ | cddddr ++ | cdddr ++ | cddr ++ | cdr ++ | ceiling ++ | cell-error-name ++ | cerror ++ | change-class ++ | char ++ | char-bit ++ | char-bits ++ | char-code ++ | char-downcase ++ | char-equal ++ | char-font ++ | char-greaterp ++ | char-int ++ | char-lessp ++ | char-name ++ | char-not-equal ++ | char-not-greaterp ++ | char-not-lessp ++ | char-upcase ++ | char/= ++ | char< ++ | char<= ++ | char= ++ | char> ++ | char>= ++ | character ++ | characterp ++ | check-type ++ | choose ++ | choose-if ++ | chunk ++ | cis ++ | class-name ++ | class-of ++ | clear-input ++ | close ++ | clrhash ++ | code-char ++ | coerce ++ | collect ++ | collect-alist ++ | collect-and ++ | collect-append ++ | collect-file ++ | collect-first ++ | collect-fn ++ | collect-hash ++ | collect-last ++ | collect-length ++ | collect-max ++ | collect-min ++ | collect-nconc ++ | collect-nth ++ | collect-or ++ | collect-plist ++ | collect-sum ++ | collecting ++ | collecting-fn ++ | commonp ++ | compile ++ | compile-file ++ | compile-file-pathname ++ | compiled-function-p ++ | compiler-let ++ | compiler-macro-function ++ | compiler-macroexpand ++ | compiler-macroexpand-1 ++ | complement ++ | complex ++ | complexp ++ | compute-applicable-methods ++ | compute-restarts ++ | concatenate ++ | concatenated-stream-streams ++ | conjugate ++ | cons ++ | consp ++ | constantp ++ | continue ++ | copy-alist ++ | copy-list ++ | copy-pprint-dispatch ++ | copy-readtable ++ | copy-seq ++ | copy-symbol ++ | copy-tree ++ | cos ++ | cosh ++ | cotruncate ++ | count ++ | count-if ++ | count-if-not ++ | counting ++ | declaim ++ | declaration-information ++ | decode-float ++ | decode-universal-time ++ | delete ++ | delete-duplicates ++ | delete-file ++ | delete-if ++ | delete-if-not ++ | delete-package ++ | denominator ++ | deposit-field ++ | describe ++ | describe-object ++ | destructuring-bind ++ | digit-char ++ | digit-char-p ++ | directory ++ | directory-namestring ++ | disassemble ++ | do-all-keyword-symbols ++ | do-external-keyword-symbols ++ | do-keyword-symbols ++ | documentation ++ | dolist ++ | dpb ++ | dribble ++ | ecase ++ | echo-stream-input-stream ++ | echo-stream-output-stream ++ | ed ++ | eighth ++ | elt ++ | encapsulated ++ | enclose ++ | encode-universal-time ++ | endp ++ | enough-namestring ++ | ensure-generic-function ++ | error ++ | eval ++ | evalhook ++ | evenp ++ | every ++ | exp ++ | expand ++ | export ++ | expt ++ | f ++ | fboundp ++ | fdefinition ++ | ffloor ++ | fifth ++ | file-author ++ | file-error-pathname ++ | file-length ++ | file-namestring ++ | file-position ++ | file-string-length ++ | file-write-date ++ | fill ++ | fill-pointer ++ | find ++ | find-all-keyword-symbols ++ | find-class ++ | find-if ++ | find-if-not ++ | find-method ++ | find-package ++ | find-restart ++ | find-symbol ++ | finish-output ++ | first ++ | float ++ | float-digits ++ | float-precision ++ | float-radix ++ | float-sign ++ | floatp ++ | floor ++ | format ++ | formatter ++ | fourth ++ | funcall ++ | function-information ++ | function-keywords ++ | function-lambda-expression ++ | functionp ++ | gatherer ++ | gathering ++ | gcd ++ | generator ++ | generic-function ++ | gensym ++ | gentemp ++ | get ++ | get-decoded-time ++ | get-internal-real-time ++ | get-internal-run-time ++ | get-output-stream-string ++ | get-properties ++ | get-setf-method ++ | get-setf-method-multiple-value ++ | get-universal-time ++ | getf ++ | gethash ++ | graphic-char-p ++ | handler-bind ++ | handler-case ++ | hash-table-count ++ | hash-table-p ++ | hash-table-rehash-size ++ | hash-table-rehash-threshold ++ | hash-table-size ++ | hash-table-test ++ | host-namestring ++ | identity ++ | ignore-errors ++ | imagpart ++ | import ++ | in-package ++ | initialize-instance ++ | input-stream-p ++ | inspect ++ | int-char ++ | integer-decode-float ++ | integer-length ++ | integerp ++ | interactive-stream-p ++ | intern ++ | intersection ++ | invalid-method-error ++ | invoke-debugger ++ | invoke-restart ++ | isqrt ++ | iterate ++ | keywordp ++ | last ++ | latch ++ | lcm ++ | ldb ++ | ldb-test ++ | ldiff ++ | length ++ | lisp-implementation-type ++ | lisp-implementation-version ++ | list ++ | list-all-packages ++ | list-length ++ | list\* ++ | listen ++ | listp ++ | load ++ | load-logical-pathname-translations ++ | locally ++ | log ++ | logandc1 ++ | logandc2 ++ | logbitp ++ | logcount ++ | logical-pathname ++ | logical-pathname-translations ++ | lognand ++ | lognot ++ | logorc1 ++ | logorc2 ++ | logtest ++ | long-site-name ++ | loop-finish ++ | lower-case-p ++ | machine-instance ++ | machine-type ++ | machine-version ++ | macro-function ++ | macroexpand ++ | macroexpand-1 ++ | make-array ++ | make-broadcast-stream ++ | make-char ++ | make-concatenated-stream ++ | make-condition ++ | make-dispatch-macro-character ++ | make-echo-stream ++ | make-hash-table ++ | make-instance ++ | make-instances-obsolete ++ | make-list ++ | make-load-form ++ | make-load-form-saving-slots ++ | make-package ++ | make-pathname ++ | make-random-state ++ | make-sequence ++ | make-string ++ | make-string-input-stream ++ | make-string-output-stream ++ | make-symbol ++ | make-synonym-stream ++ | make-two-way-stream ++ | makunbound ++ | map ++ | map-fn ++ | map-into ++ | maphash ++ | mapping ++ | mask ++ | mask-field ++ | maximize ++ | maximizing ++ | member ++ | member-if ++ | member-if-not ++ | memberp ++ | merge ++ | merge-pathnames ++ | method-combination-error ++ | method-qualifiers ++ | mingle ++ | minimize ++ | minimizing ++ | minusp ++ | mismatch ++ | muffle-warning ++ | multiple-value-bind ++ | multiple-value-list ++ | multiple-value-setq ++ | name-char ++ | namestring ++ | nbutlast ++ | nconc ++ | nconcing ++ | next-in ++ | next-method-p ++ | next-out ++ | nintersection ++ | ninth ++ | no-applicable-method ++ | no-next-method ++ | notany ++ | notevery ++ | nreconc ++ | nreverse ++ | nset-difference ++ | nset-exclusive-or ++ | nstring-capitalize ++ | nstring-downcase ++ | nstring-upcase ++ | nsublis ++ | nsubst ++ | nsubst-if ++ | nsubst-if-not ++ | nsubstitute ++ | nsubstitute-if ++ | nsubstitute-if-not ++ | nth ++ | nth-value ++ | nthcdr ++ | numberp ++ | numerator ++ | nunion ++ | oddp ++ | open ++ | open-stream-p ++ | output-stream-p ++ | package-error-package ++ | package-name ++ | package-nicknames ++ | package-shadowing-keyword-symbols ++ | package-use-list ++ | package-used-by-list ++ | packagep ++ | pairlis ++ | parse-integer ++ | parse-macro ++ | parse-namestring ++ | pathname ++ | pathname-device ++ | pathname-directory ++ | pathname-host ++ | pathname-match-p ++ | pathname-name ++ | pathname-type ++ | pathname-version ++ | pathnamep ++ | peek-char ++ | phase ++ | plusp ++ | pop ++ | position ++ | position-if ++ | position-if-not ++ | positions ++ | pprint-dispatch ++ | pprint-exit-if-list-exhausted ++ | pprint-fill ++ | pprint-indent ++ | pprint-linear ++ | pprint-logical-block ++ | pprint-newline ++ | pprint-pop ++ | pprint-tab ++ | pprint-tabular ++ | previous ++ | prin1 ++ | print ++ | print-object ++ | print-unreadable-object ++ | probe-file ++ | producing ++ | prog1 ++ | prog2 ++ | progn ++ | provide ++ | psetf ++ | psetq ++ | push ++ | pushnew ++ | quote ++ | random ++ | random-state-p ++ | rassoc ++ | rassoc-if ++ | rassoc-if-not ++ | rational ++ | rationalize ++ | rationalp ++ | read ++ | read-byte ++ | read-char ++ | read-char-no-hang ++ | read-delimited-list ++ | read-from-string ++ | read-line ++ | read-preserving-whitespace ++ | readtable-case ++ | readtablep ++ | realp ++ | realpart ++ | reduce ++ | reinitialize-instance ++ | remf ++ | remhash ++ | remove ++ | remove-duplicates ++ | remove-method ++ | remprop ++ | rename-file ++ | rename-package ++ | replace ++ | require ++ | rest ++ | restart-bind ++ | restart-case ++ | restart-name ++ | result-of ++ | revappend ++ | reverse ++ | room ++ | rotatef ++ | round ++ | row-major-aref ++ | rplaca ++ | rplacd ++ | sbit ++ | scale-float ++ | scan ++ | scan-alist ++ | scan-file ++ | scan-fn ++ | scan-fn-inclusive ++ | scan-hash ++ | scan-lists-of-lists ++ | scan-lists-of-lists-fringe ++ | scan-multiple ++ | scan-plist ++ | scan-range ++ | scan-sublists ++ | scan-symbols ++ | schar ++ | search ++ | second ++ | series ++ | set ++ | set-char-bit ++ | set-difference ++ | set-dispatch-macro-character ++ | set-exclusive-or ++ | set-macro-character ++ | set-pprint-dispatch ++ | set-syntax-from-char ++ | setf ++ | setq ++ | seventh ++ | shadow ++ | shadowing-import ++ | shared-initialize ++ | shiftf ++ | short-site-name ++ | signal ++ | signum ++ | simple-bit-vector-p ++ | simple-condition-format-arguments ++ | simple-condition-format-string ++ | simple-string-p ++ | simple-vector-p ++ | sin ++ | sinh ++ | sixth ++ | sleep ++ | slot-boundp ++ | slot-exists-p ++ | slot-makunbound ++ | slot-missing ++ | slot-unbound ++ | slot-value ++ | software-type ++ | software-version ++ | some ++ | sort ++ | special-form-p ++ | split ++ | split-if ++ | sqrt ++ | stable-sort ++ | standard-char-p ++ | step ++ | store-value ++ | stream-element-type ++ | stream-error-stream ++ | stream-external-format ++ | streamp ++ | string ++ | string-capitalize ++ | string-downcase ++ | string-equal ++ | string-greaterp ++ | string-left-trim ++ | string-lessp ++ | string-not-equal ++ | string-not-greaterp ++ | string-not-lessp ++ | string-right-trim ++ | string-trim ++ | string-upcase ++ | string/= ++ | string< ++ | string<= ++ | string= ++ | string> ++ | string>= ++ | stringp ++ | sublis ++ | subseq ++ | subseries ++ | subsetp ++ | subst ++ | subst-if ++ | subst-if-not ++ | substitute ++ | substitute-if ++ | substitute-if-not ++ | subtypep ++ | sum ++ | summing ++ | svref ++ | sxhash ++ | symbol-function ++ | symbol-name ++ | symbol-package ++ | symbol-plist ++ | symbol-value ++ | symbolp ++ | synonym-stream-symbol ++ | tailp ++ | tan ++ | tanh ++ | tenth ++ | terminate-producing ++ | terpri ++ | third ++ | time ++ | to-alter ++ | trace ++ | translate-logical-pathname ++ | translate-pathname ++ | tree-equal ++ | truename ++ | truncate ++ | two-way-stream-input-stream ++ | two-way-stream-output-stream ++ | type-error-datum ++ | type-error-expected-type ++ | type-of ++ | typep ++ | unexport ++ | unintern ++ | union ++ | unread-char ++ | untrace ++ | unuse-package ++ | update-instance-for-different-class ++ | update-instance-for-redefined-class ++ | upgraded-array-element-type ++ | upgraded-complex-part-type ++ | upper-case-p ++ | use-package ++ | use-value ++ | user-homedir-pathname ++ | values ++ | values-list ++ | variable-information ++ | vector ++ | vector-pop ++ | vector-push ++ | vector-push-extend ++ | vectorp ++ | warn ++ | wild-pathname-p ++ | with-compilation-unit ++ | with-hash-table-iterator ++ | with-package-iterator ++ | with-simple-restart ++ | with-standard-io-syntax ++ | write ++ | write-byte ++ | write-char ++ | write-string ++ | write-to-string ++ | y-or-n-p ++ | yes-or-no-p ++ | zerop ++ ){{break}} ++ ++ # Built-in atomic type specifiers ++ # https://www.lispworks.com/documentation/lw70/CLHS/Body/04_bc.htm ++ # Note: Some of them are scoped function when appearing as first list item. ++ builtin_types: |- ++ (?xi: ++ arithmetic-error ++ | array ++ | atom ++ | base-char ++ | base-string ++ | bignum ++ | bit ++ | bit-vector ++ | broadcast-stream ++ | built-in-class ++ | cell-error ++ | character ++ | class ++ | compiled-function ++ | complex ++ | concatenated-stream ++ | condition ++ | cons ++ | control-error ++ | division-by-zero ++ | double-float ++ | echo-stream ++ | end-of-file ++ | error ++ | extended-char ++ | file-error ++ | file-stream ++ | fixnum ++ | float ++ | floating-point-inexact ++ | floating-point-invalid-operation ++ | floating-point-overflow ++ | floating-point-underflow ++ | function ++ | generic-function ++ | hash-table ++ | integer ++ | keyword ++ | list ++ | logical-pathname ++ | long-float ++ | method ++ | method-combination ++ | number ++ | package ++ | package-error ++ | parse-error ++ | pathname ++ | print-not-readable ++ | program-error ++ | random-state ++ | ratio ++ | rational ++ | reader-error ++ | readtable ++ | real ++ | restart ++ | sequence ++ | serious-condition ++ | short-float ++ | signed-byte ++ | simple-array ++ | simple-base-string ++ | simple-bit-vector ++ | simple-condition ++ | simple-error ++ | simple-string ++ | simple-type-error ++ | simple-vector ++ | simple-warning ++ | single-float ++ | standard-char ++ | standard-class ++ | standard-generic-function ++ | standard-method ++ | standard-object ++ | storage-condition ++ | stream ++ | stream-error ++ | string ++ | string-stream ++ | structure-class ++ | structure-object ++ | style-warning ++ | symbol ++ | synonym-stream ++ | two-way-stream ++ | type-error ++ | unbound-slot ++ | unbound-variable ++ | undefined-function ++ | unsigned-byte ++ | vector ++ | warning ++ ){{break}} ++ ++ builtin_variables: |- ++ \*(?xi: ++ applyhook ++ | break-on-signals ++ | break-on-warnings ++ | compile-file-pathname ++ | compile-file-truename ++ | compile-print ++ | compile-verbose ++ | debug-io ++ | debugger-hook ++ | default-pathname-defaults ++ | error-output ++ | evalhook ++ | features ++ | gensym-counter ++ | load-pathname ++ | load-print ++ | load-truename ++ | load-verbose ++ | macroexpand-hook ++ | modules ++ | package ++ | print-array ++ | print-base ++ | print-case ++ | print-circle ++ | print-escape ++ | print-gensym ++ | print-length ++ | print-level ++ | print-lines ++ | print-miser-width ++ | print-pprint-dispatch ++ | print-pretty ++ | print-radix ++ | print-readably ++ | print-right-margin ++ | query-io ++ | random-state ++ | read-base ++ | read-default-float-format ++ | read-eval ++ | read-suppress ++ | readtable ++ | standard-input ++ | standard-output ++ | suppress-series-warnings ++ | terminal-io ++ | trace-output ++ )\*{{break}} diff --git a/assets/syntaxes/02_Extra/CFML b/assets/syntaxes/02_Extra/CFML new file mode 160000 index 00000000..b91c44a3 --- /dev/null +++ b/assets/syntaxes/02_Extra/CFML @@ -0,0 +1 @@ +Subproject commit b91c44a32e251c20c6359a8d9232287e1b408e6c diff --git a/assets/syntaxes/02_Extra/Julia b/assets/syntaxes/02_Extra/Julia index 98233f96..3366b10b 160000 --- a/assets/syntaxes/02_Extra/Julia +++ b/assets/syntaxes/02_Extra/Julia @@ -1 +1 @@ -Subproject commit 98233f96d4827a1a576c0b8bf87a68b9c97e4306 +Subproject commit 3366b10be91aaab7a61ae0bc0a5af5cc375e58d1 diff --git a/assets/syntaxes/02_Extra/Protobuf b/assets/syntaxes/02_Extra/Protobuf index 726e21d7..13653315 160000 --- a/assets/syntaxes/02_Extra/Protobuf +++ b/assets/syntaxes/02_Extra/Protobuf @@ -1 +1 @@ -Subproject commit 726e21d74dac23cbb036f2fbbd626decdc954060 +Subproject commit 1365331580b0e4bb86f74d0c599dccc87e7bdacb diff --git a/doc/README-zh.md b/doc/README-zh.md index 1fea0577..007afb3e 100644 --- a/doc/README-zh.md +++ b/doc/README-zh.md @@ -412,7 +412,7 @@ bat --list-themes | fzf --preview="bat --theme={} --color=always /path/to/file" ### 输出样式 -你可以用`--style`参数来控制`bat`输出的样式。使用`--style=numbers,chanegs`可以只开启 Git 修改和行号显示而不添加其他内容。`BAT_STYLE`环境变量具有相同功能。 +你可以用`--style`参数来控制`bat`输出的样式。使用`--style=numbers,changes`可以只开启 Git 修改和行号显示而不添加其他内容。`BAT_STYLE`环境变量具有相同功能。 ### 添加新的语言和语法 diff --git a/doc/long-help.txt b/doc/long-help.txt index a6ffe962..2b03490f 100644 --- a/doc/long-help.txt +++ b/doc/long-help.txt @@ -122,6 +122,11 @@ Options: --squeeze-limit Set the maximum number of consecutive empty lines to be printed. + --strip-ansi + Specify when to strip ANSI escape sequences from the input. The automatic mode will remove + escape sequences unless the syntax highlighting language is plain text. Possible values: + auto, always, *never*. + --style 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 @@ -129,6 +134,12 @@ Options: set a default style, add the '--style=".."' option to the configuration file or export the BAT_STYLE environment variable (e.g.: export BAT_STYLE=".."). + When styles are specified in multiple places, the "nearest" set of styles take precedence. + The command-line arguments are the highest priority, followed by the BAT_STYLE environment + variable, and then the configuration file. If any set of styles consists entirely of + components prefixed with "+" or "-", it will modify the previous set of styles instead of + replacing them. + By default, the following components are enabled: changes, grid, header-filename, numbers, snip diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index 6fc85321..d6628668 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -2,11 +2,14 @@ use std::collections::HashSet; use std::env; use std::io::IsTerminal; use std::path::{Path, PathBuf}; +use std::str::FromStr; use crate::{ clap_app, config::{get_args_from_config_file, get_args_from_env_opts_var, get_args_from_env_vars}, }; +use bat::style::StyleComponentList; +use bat::StripAnsiMode; use clap::ArgMatches; use console::Term; @@ -85,7 +88,6 @@ impl App { // .. and the rest at the end cli_args.for_each(|a| args.push(a)); - args }; @@ -242,6 +244,16 @@ impl App { 4 }, ), + strip_ansi: match self + .matches + .get_one::("strip-ansi") + .map(|s| s.as_str()) + { + Some("never") => StripAnsiMode::Never, + Some("always") => StripAnsiMode::Always, + Some("auto") => StripAnsiMode::Auto, + _ => unreachable!("other values for --strip-ansi are not allowed"), + }, theme: self .matches .get_one::("theme") @@ -353,34 +365,57 @@ impl App { Ok(file_input) } + fn forced_style_components(&self) -> Option { + // No components if `--decorations=never``. + if self + .matches + .get_one::("decorations") + .map(|s| s.as_str()) + == Some("never") + { + return Some(StyleComponents(HashSet::new())); + } + + // Only line numbers if `--number`. + if self.matches.get_flag("number") { + return Some(StyleComponents(HashSet::from([ + StyleComponent::LineNumbers, + ]))); + } + + // Plain if `--plain` is specified at least once. + if self.matches.get_count("plain") > 0 { + return Some(StyleComponents(HashSet::from([StyleComponent::Plain]))); + } + + // Default behavior. + None + } + fn style_components(&self) -> Result { let matches = &self.matches; - let mut styled_components = StyleComponents( - if matches.get_one::("decorations").map(|s| s.as_str()) == Some("never") { - HashSet::new() - } else if matches.get_flag("number") { - [StyleComponent::LineNumbers].iter().cloned().collect() - } else if 0 < matches.get_count("plain") { - [StyleComponent::Plain].iter().cloned().collect() - } else { - matches - .get_one::("style") - .map(|styles| { - styles - .split(',') - .map(|style| style.parse::()) - .filter_map(|style| style.ok()) - .collect::>() - }) - .unwrap_or_else(|| vec![StyleComponent::Default]) + let mut styled_components = match self.forced_style_components() { + Some(forced_components) => forced_components, + + // Parse the `--style` arguments and merge them. + None if matches.contains_id("style") => { + let lists = matches + .get_many::("style") + .expect("styles present") + .map(|v| StyleComponentList::from_str(v)) + .collect::>>()?; + + StyleComponentList::to_components(lists, self.interactive_output, true) + } + + // Use the default. + None => StyleComponents(HashSet::from_iter( + StyleComponent::Default + .components(self.interactive_output) .into_iter() - .map(|style| style.components(self.interactive_output)) - .fold(HashSet::new(), |mut acc, components| { - acc.extend(components.iter().cloned()); - acc - }) - }, - ); + .cloned(), + )), + }; // If `grid` is set, remove `rule` as it is a subset of `grid`, and print a warning. if styled_components.grid() && styled_components.0.remove(&StyleComponent::Rule) { diff --git a/src/bin/bat/clap_app.rs b/src/bin/bat/clap_app.rs index b82762b6..33dde980 100644 --- a/src/bin/bat/clap_app.rs +++ b/src/bin/bat/clap_app.rs @@ -1,9 +1,11 @@ +use bat::style::StyleComponentList; use clap::{ crate_name, crate_version, value_parser, Arg, ArgAction, ArgGroup, ColorChoice, Command, }; use once_cell::sync::Lazy; use std::env; use std::path::{Path, PathBuf}; +use std::str::FromStr; static VERSION: Lazy = Lazy::new(|| { #[cfg(feature = "bugreport")] @@ -402,37 +404,30 @@ pub fn build_app(interactive_output: bool) -> Command { .long_help("Set the maximum number of consecutive empty lines to be printed.") .hide_short_help(true) ) + .arg( + Arg::new("strip-ansi") + .long("strip-ansi") + .overrides_with("strip-ansi") + .value_name("when") + .value_parser(["auto", "always", "never"]) + .default_value("never") + .hide_default_value(true) + .help("Strip colors from the input (auto, always, *never*)") + .long_help("Specify when to strip ANSI escape sequences from the input. \ + The automatic mode will remove escape sequences unless the syntax highlighting \ + language is plain text. Possible values: auto, always, *never*.") + .hide_short_help(true) + ) .arg( Arg::new("style") .long("style") + .action(ArgAction::Append) .value_name("components") - .overrides_with("style") - .overrides_with("plain") - .overrides_with("number") // Cannot use claps built in validation because we have to turn off clap's delimiters .value_parser(|val: &str| { - let mut invalid_vals = val.split(',').filter(|style| { - !&[ - "auto", - "full", - "default", - "plain", - "header", - "header-filename", - "header-filesize", - "grid", - "rule", - "numbers", - "snip", - #[cfg(feature = "git")] - "changes", - ].contains(style) - }); - - if let Some(invalid) = invalid_vals.next() { - Err(format!("Unknown style, '{invalid}'")) - } else { - Ok(val.to_owned()) + match StyleComponentList::from_str(val) { + Err(err) => Err(err), + Ok(_) => Ok(val.to_owned()), } }) .help( @@ -447,6 +442,12 @@ pub fn build_app(interactive_output: bool) -> Command { pre-defined style ('full'). To set a default style, add the \ '--style=\"..\"' option to the configuration file or export the \ BAT_STYLE environment variable (e.g.: export BAT_STYLE=\"..\").\n\n\ + When styles are specified in multiple places, the \"nearest\" set \ + of styles take precedence. The command-line arguments are the highest \ + priority, followed by the BAT_STYLE environment variable, and then \ + the configuration file. If any set of styles consists entirely of \ + components prefixed with \"+\" or \"-\", it will modify the \ + previous set of styles instead of replacing them.\n\n\ By default, the following components are enabled:\n \ changes, grid, header-filename, numbers, snip\n\n\ Possible values:\n\n \ diff --git a/src/bin/bat/config.rs b/src/bin/bat/config.rs index 9e38dfa4..6fa18f09 100644 --- a/src/bin/bat/config.rs +++ b/src/bin/bat/config.rs @@ -146,8 +146,11 @@ pub fn get_args_from_env_vars() -> Vec { ("--style", "BAT_STYLE"), ] .iter() - .filter_map(|(flag, key)| env::var(key).ok().map(|var| [flag.to_string(), var])) - .flatten() + .filter_map(|(flag, key)| { + env::var(key) + .ok() + .map(|var| [flag.to_string(), var].join("=")) + }) .map(|a| a.into()) .collect() } diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs index 38595721..4528a60b 100644 --- a/src/bin/bat/main.rs +++ b/src/bin/bat/main.rs @@ -30,6 +30,7 @@ use directories::PROJECT_DIRS; use globset::GlobMatcher; use bat::{ + assets::HighlightingAssets, config::Config, controller::Controller, error::*, @@ -199,19 +200,31 @@ pub fn list_themes(cfg: &Config, config_dir: &Path, cache_dir: &Path) -> Result< let stdout = io::stdout(); let mut stdout = stdout.lock(); - if config.colored_output { - for theme in assets.themes() { + let default_theme = HighlightingAssets::default_theme(); + for theme in assets.themes() { + let default_theme_info = if default_theme == theme { + " (default)" + } else { + "" + }; + if config.colored_output { writeln!( stdout, - "Theme: {}\n", - Style::new().bold().paint(theme.to_string()) + "Theme: {}{}\n", + Style::new().bold().paint(theme.to_string()), + default_theme_info )?; config.theme = theme.to_string(); Controller::new(&config, &assets) .run(vec![theme_preview_file()], None) .ok(); writeln!(stdout)?; + } else { + writeln!(stdout, "{theme}{default_theme_info}")?; } + } + + if config.colored_output { writeln!( stdout, "Further themes can be installed to '{}', \ @@ -220,10 +233,6 @@ pub fn list_themes(cfg: &Config, config_dir: &Path, cache_dir: &Path) -> Result< https://github.com/sharkdp/bat#adding-new-themes", config_dir.join("themes").to_string_lossy() )?; - } else { - for theme in assets.themes() { - writeln!(stdout, "{theme}")?; - } } Ok(()) @@ -272,24 +281,25 @@ fn invoke_bugreport(app: &App, cache_dir: &Path) { .info(OperatingSystem::default()) .info(CommandLine::default()) .info(EnvironmentVariables::list(&[ - "SHELL", - "PAGER", - "LESS", - "LANG", - "LC_ALL", - "BAT_PAGER", - "BAT_PAGING", "BAT_CACHE_PATH", "BAT_CONFIG_PATH", "BAT_OPTS", + "BAT_PAGER", + "BAT_PAGING", "BAT_STYLE", "BAT_TABS", "BAT_THEME", - "XDG_CONFIG_HOME", - "XDG_CACHE_HOME", "COLORTERM", - "NO_COLOR", + "LANG", + "LC_ALL", + "LESS", "MANPAGER", + "NO_COLOR", + "PAGER", + "SHELL", + "TERM", + "XDG_CACHE_HOME", + "XDG_CONFIG_HOME", ])) .info(FileContent::new("System Config file", system_config_file())) .info(FileContent::new("Config file", config_file())) diff --git a/src/config.rs b/src/config.rs index 0752191b..8ff49ec1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,6 +5,7 @@ use crate::paging::PagingMode; use crate::style::StyleComponents; use crate::syntax_mapping::SyntaxMapping; use crate::wrapping::WrappingMode; +use crate::StripAnsiMode; #[derive(Debug, Clone)] pub enum VisibleLines { @@ -100,6 +101,9 @@ pub struct Config<'a> { /// The maximum number of consecutive empty lines to display pub squeeze_lines: Option, + + // Weather or not to set terminal title when using a pager + pub strip_ansi: StripAnsiMode, } #[cfg(all(feature = "minimal-application", feature = "paging"))] diff --git a/src/lib.rs b/src/lib.rs index 0296ad32..23c4a800 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,6 +53,7 @@ mod vscreen; pub(crate) mod wrapping; pub use nonprintable_notation::NonprintableNotation; +pub use preprocessor::StripAnsiMode; pub use pretty_printer::{Input, PrettyPrinter, Syntax}; pub use syntax_mapping::{MappingTarget, SyntaxMapping}; pub use wrapping::WrappingMode; diff --git a/src/preprocessor.rs b/src/preprocessor.rs index 3328f3b6..dc2aa66e 100644 --- a/src/preprocessor.rs +++ b/src/preprocessor.rs @@ -1,17 +1,18 @@ use std::fmt::Write; -use console::AnsiCodeIterator; - -use crate::nonprintable_notation::NonprintableNotation; +use crate::{ + nonprintable_notation::NonprintableNotation, + vscreen::{EscapeSequenceOffsets, EscapeSequenceOffsetsIterator}, +}; /// Expand tabs like an ANSI-enabled expand(1). pub fn expand_tabs(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) => { + for seq in EscapeSequenceOffsetsIterator::new(line) { + match seq { + EscapeSequenceOffsets::Text { .. } => { + let mut text = &line[seq.index_of_start()..seq.index_past_end()]; while let Some(index) = text.find('\t') { // Add previous text. if index > 0 { @@ -31,6 +32,10 @@ pub fn expand_tabs(line: &str, width: usize, cursor: &mut usize) -> String { *cursor += text.len(); buffer.push_str(text); } + _ => { + // Copy the ANSI escape sequence. + buffer.push_str(&line[seq.index_of_start()..seq.index_past_end()]) + } } } @@ -131,6 +136,27 @@ pub fn replace_nonprintable( output } +/// Strips ANSI escape sequences from the input. +pub fn strip_ansi(line: &str) -> String { + let mut buffer = String::with_capacity(line.len()); + + for seq in EscapeSequenceOffsetsIterator::new(line) { + if let EscapeSequenceOffsets::Text { .. } = seq { + buffer.push_str(&line[seq.index_of_start()..seq.index_past_end()]); + } + } + + buffer +} + +#[derive(Debug, PartialEq, Clone, Copy, Default)] +pub enum StripAnsiMode { + #[default] + Never, + Always, + Auto, +} + #[test] fn test_try_parse_utf8_char() { assert_eq!(try_parse_utf8_char(&[0x20]), Some((' ', 1))); @@ -174,3 +200,14 @@ fn test_try_parse_utf8_char() { assert_eq!(try_parse_utf8_char(&[0xef, 0x20]), None); assert_eq!(try_parse_utf8_char(&[0xf0, 0xf0]), None); } + +#[test] +fn test_strip_ansi() { + // The sequence detection is covered by the tests in the vscreen module. + assert_eq!(strip_ansi("no ansi"), "no ansi"); + assert_eq!(strip_ansi("\x1B[33mone"), "one"); + assert_eq!( + strip_ansi("\x1B]1\x07multiple\x1B[J sequences"), + "multiple sequences" + ); +} diff --git a/src/pretty_printer.rs b/src/pretty_printer.rs index c6203aa9..eb123ea3 100644 --- a/src/pretty_printer.rs +++ b/src/pretty_printer.rs @@ -11,7 +11,7 @@ use crate::{ input, line_range::{HighlightedLineRanges, LineRange, LineRanges}, style::StyleComponent, - SyntaxMapping, WrappingMode, + StripAnsiMode, SyntaxMapping, WrappingMode, }; #[cfg(feature = "paging")] @@ -182,6 +182,15 @@ impl<'a> PrettyPrinter<'a> { self } + /// Whether to remove ANSI escape sequences from the input (default: never) + /// + /// If `Auto` is used, escape sequences will only be removed when the input + /// is not plain text. + pub fn strip_ansi(&mut self, mode: StripAnsiMode) -> &mut Self { + self.config.strip_ansi = mode; + self + } + /// Text wrapping mode (default: do not wrap) pub fn wrapping_mode(&mut self, mode: WrappingMode) -> &mut Self { self.config.wrapping_mode = mode; diff --git a/src/printer.rs b/src/printer.rs index 38c264f8..64e89e76 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -29,11 +29,13 @@ use crate::diff::LineChanges; use crate::error::*; use crate::input::OpenedInput; use crate::line_range::{MaxBufferedLineNumber, RangeCheckResult}; +use crate::preprocessor::strip_ansi; use crate::preprocessor::{expand_tabs, replace_nonprintable}; use crate::style::StyleComponent; use crate::terminal::{as_terminal_escaped, to_ansi_color}; use crate::vscreen::{AnsiStyle, EscapeSequence, EscapeSequenceIterator}; use crate::wrapping::WrappingMode; +use crate::StripAnsiMode; const ANSI_UNDERLINE_ENABLE: EscapeSequence = EscapeSequence::CSI { raw_sequence: "\x1B[4m", @@ -209,6 +211,7 @@ pub(crate) struct InteractivePrinter<'a> { highlighter_from_set: Option>, background_color_highlight: Option, consecutive_empty_lines: usize, + strip_ansi: bool, } impl<'a> InteractivePrinter<'a> { @@ -267,20 +270,41 @@ impl<'a> InteractivePrinter<'a> { .content_type .map_or(false, |c| c.is_binary() && !config.show_nonprintable); - let highlighter_from_set = if is_printing_binary || !config.colored_output { - None - } else { - // Determine the type of syntax for highlighting - let syntax_in_set = - match assets.get_syntax(config.language, input, &config.syntax_mapping) { - Ok(syntax_in_set) => syntax_in_set, - Err(Error::UndetectedSyntax(_)) => assets - .find_syntax_by_name("Plain Text")? - .expect("A plain text syntax is available"), - Err(e) => return Err(e), - }; + let needs_to_match_syntax = !is_printing_binary + && (config.colored_output || config.strip_ansi == StripAnsiMode::Auto); - Some(HighlighterFromSet::new(syntax_in_set, theme)) + let (is_plain_text, highlighter_from_set) = if needs_to_match_syntax { + // Determine the type of syntax for highlighting + const PLAIN_TEXT_SYNTAX: &str = "Plain Text"; + match assets.get_syntax(config.language, input, &config.syntax_mapping) { + Ok(syntax_in_set) => ( + syntax_in_set.syntax.name == PLAIN_TEXT_SYNTAX, + Some(HighlighterFromSet::new(syntax_in_set, theme)), + ), + + Err(Error::UndetectedSyntax(_)) => ( + true, + Some( + assets + .find_syntax_by_name(PLAIN_TEXT_SYNTAX)? + .map(|s| HighlighterFromSet::new(s, theme)) + .expect("A plain text syntax is available"), + ), + ), + + Err(e) => return Err(e), + } + } else { + (false, None) + }; + + // Determine when to strip ANSI sequences + let strip_ansi = match config.strip_ansi { + _ if config.show_nonprintable => false, + StripAnsiMode::Always => true, + StripAnsiMode::Auto if is_plain_text => false, // Plain text may already contain escape sequences. + StripAnsiMode::Auto => true, + _ => false, }; Ok(InteractivePrinter { @@ -295,6 +319,7 @@ impl<'a> InteractivePrinter<'a> { highlighter_from_set, background_color_highlight, consecutive_empty_lines: 0, + strip_ansi, }) } @@ -576,7 +601,7 @@ impl<'a> Printer for InteractivePrinter<'a> { ) .into() } else { - match self.content_type { + let mut line = match self.content_type { Some(ContentType::BINARY) | None => { return Ok(()); } @@ -593,7 +618,14 @@ impl<'a> Printer for InteractivePrinter<'a> { line } } + }; + + // If ANSI escape sequences are supposed to be stripped, do it before syntax highlighting. + if self.strip_ansi { + line = strip_ansi(&line).into() } + + line }; let regions = self.highlight_regions_for_line(&line)?; diff --git a/src/style.rs b/src/style.rs index 652b3743..b8d8b09f 100644 --- a/src/style.rs +++ b/src/style.rs @@ -138,3 +138,227 @@ impl StyleComponents { self.0.clear(); } } + +#[derive(Debug, PartialEq)] +enum ComponentAction { + Override, + Add, + Remove, +} + +impl ComponentAction { + fn extract_from_str(string: &str) -> (ComponentAction, &str) { + match string.chars().next() { + Some('-') => (ComponentAction::Remove, string.strip_prefix('-').unwrap()), + Some('+') => (ComponentAction::Add, string.strip_prefix('+').unwrap()), + _ => (ComponentAction::Override, string), + } + } +} + +/// A list of [StyleComponent] that can be parsed from a string. +pub struct StyleComponentList(Vec<(ComponentAction, StyleComponent)>); + +impl StyleComponentList { + fn expand_into(&self, components: &mut HashSet, interactive_terminal: bool) { + for (action, component) in self.0.iter() { + let subcomponents = component.components(interactive_terminal); + + use ComponentAction::*; + match action { + Override | Add => components.extend(subcomponents), + Remove => components.retain(|c| !subcomponents.contains(c)), + } + } + } + + /// Returns `true` if any component in the list was not prefixed with `+` or `-`. + fn contains_override(&self) -> bool { + self.0.iter().any(|(a, _)| *a == ComponentAction::Override) + } + + /// Combines multiple [StyleComponentList]s into a single [StyleComponents] set. + /// + /// ## Precedence + /// The most recent list will take precedence and override all previous lists + /// unless it only contains components prefixed with `-` or `+`. When this + /// happens, the list's components will be merged into the previous list. + /// + /// ## Example + /// ```text + /// [numbers,grid] + [header,changes] -> [header,changes] + /// [numbers,grid] + [+header,-grid] -> [numbers,header] + /// ``` + /// + /// ## Parameters + /// - `with_default`: If true, the styles lists will build upon the StyleComponent::Auto style. + pub fn to_components( + lists: impl IntoIterator, + interactive_terminal: bool, + with_default: bool, + ) -> StyleComponents { + let mut components: HashSet = HashSet::new(); + if with_default { + components.extend(StyleComponent::Auto.components(interactive_terminal)) + } + + StyleComponents(lists.into_iter().fold(components, |mut components, list| { + if list.contains_override() { + components.clear(); + } + + list.expand_into(&mut components, interactive_terminal); + components + })) + } +} + +impl Default for StyleComponentList { + fn default() -> Self { + StyleComponentList(vec![(ComponentAction::Override, StyleComponent::Default)]) + } +} + +impl FromStr for StyleComponentList { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(StyleComponentList( + s.split(",") + .map(|s| ComponentAction::extract_from_str(s)) // If the component starts with "-", it's meant to be removed + .map(|(a, s)| Ok((a, StyleComponent::from_str(s)?))) + .collect::>>()?, + )) + } +} + +#[cfg(test)] +mod test { + use std::collections::HashSet; + use std::str::FromStr; + + use super::ComponentAction::*; + use super::StyleComponent; + use super::StyleComponent::*; + use super::StyleComponentList; + + #[test] + pub fn style_component_list_parse() { + assert_eq!( + StyleComponentList::from_str("grid,+numbers,snip,-snip,header") + .expect("no error") + .0, + vec![ + (Override, Grid), + (Add, LineNumbers), + (Override, Snip), + (Remove, Snip), + (Override, Header), + ] + ); + + assert!(StyleComponentList::from_str("not-a-component").is_err()); + assert!(StyleComponentList::from_str("grid,not-a-component").is_err()); + assert!(StyleComponentList::from_str("numbers,-not-a-component").is_err()); + } + + #[test] + pub fn style_component_list_to_components() { + assert_eq!( + StyleComponentList::to_components( + vec![StyleComponentList::from_str("grid,numbers").expect("no error")], + false, + false + ) + .0, + HashSet::from([Grid, LineNumbers]) + ); + } + + #[test] + pub fn style_component_list_to_components_removes_negated() { + assert_eq!( + StyleComponentList::to_components( + vec![StyleComponentList::from_str("grid,numbers,-grid").expect("no error")], + false, + false + ) + .0, + HashSet::from([LineNumbers]) + ); + } + + #[test] + pub fn style_component_list_to_components_expands_subcomponents() { + assert_eq!( + StyleComponentList::to_components( + vec![StyleComponentList::from_str("full").expect("no error")], + false, + false + ) + .0, + HashSet::from_iter(Full.components(true).to_owned()) + ); + } + + #[test] + pub fn style_component_list_expand_negates_subcomponents() { + assert!(!StyleComponentList::to_components( + vec![StyleComponentList::from_str("full,-numbers").expect("no error")], + true, + false + ) + .numbers()); + } + + #[test] + pub fn style_component_list_to_components_precedence_overrides_previous_lists() { + assert_eq!( + StyleComponentList::to_components( + vec![ + StyleComponentList::from_str("grid").expect("no error"), + StyleComponentList::from_str("numbers").expect("no error"), + ], + false, + false + ) + .0, + HashSet::from([LineNumbers]) + ); + } + + #[test] + pub fn style_component_list_to_components_precedence_merges_previous_lists() { + assert_eq!( + StyleComponentList::to_components( + vec![ + StyleComponentList::from_str("grid,header").expect("no error"), + StyleComponentList::from_str("-grid").expect("no error"), + StyleComponentList::from_str("+numbers").expect("no error"), + ], + false, + false + ) + .0, + HashSet::from([HeaderFilename, LineNumbers]) + ); + } + + #[test] + pub fn style_component_list_default_builds_on_auto() { + assert_eq!( + StyleComponentList::to_components( + vec![StyleComponentList::from_str("-numbers").expect("no error"),], + true, + true + ) + .0, + { + let mut expected: HashSet = HashSet::new(); + expected.extend(Auto.components(true)); + expected.remove(&LineNumbers); + expected + } + ); + } +} diff --git a/src/syntax_mapping/builtins/common/50-json.toml b/src/syntax_mapping/builtins/common/50-json.toml index e604868a..7d33b6fe 100644 --- a/src/syntax_mapping/builtins/common/50-json.toml +++ b/src/syntax_mapping/builtins/common/50-json.toml @@ -1,3 +1,3 @@ # JSON Lines is a simple variation of JSON #2535 [mappings] -"JSON" = ["*.jsonl", "*.jsonc"] +"JSON" = ["*.jsonl", "*.jsonc", "*.jsonld"] diff --git a/src/vscreen.rs b/src/vscreen.rs index e211c2aa..9e29f9cc 100644 --- a/src/vscreen.rs +++ b/src/vscreen.rs @@ -285,7 +285,7 @@ fn join( /// A range of indices for a raw ANSI escape sequence. #[derive(Debug, PartialEq)] -enum EscapeSequenceOffsets { +pub enum EscapeSequenceOffsets { Text { start: usize, end: usize, @@ -320,6 +320,32 @@ enum EscapeSequenceOffsets { }, } +impl EscapeSequenceOffsets { + /// Returns the byte-index of the first character in the escape sequence. + pub fn index_of_start(&self) -> usize { + use EscapeSequenceOffsets::*; + match self { + Text { start, .. } => *start, + Unknown { start, .. } => *start, + NF { start_sequence, .. } => *start_sequence, + OSC { start_sequence, .. } => *start_sequence, + CSI { start_sequence, .. } => *start_sequence, + } + } + + /// Returns the byte-index past the last character in the escape sequence. + pub fn index_past_end(&self) -> usize { + use EscapeSequenceOffsets::*; + match self { + Text { end, .. } => *end, + Unknown { end, .. } => *end, + NF { end, .. } => *end, + OSC { end, .. } => *end, + CSI { end, .. } => *end, + } + } +} + /// An iterator over the offests of ANSI/VT escape sequences within a string. /// /// ## Example @@ -327,7 +353,7 @@ enum EscapeSequenceOffsets { /// ```ignore /// let iter = EscapeSequenceOffsetsIterator::new("\x1B[33mThis is yellow text.\x1B[m"); /// ``` -struct EscapeSequenceOffsetsIterator<'a> { +pub struct EscapeSequenceOffsetsIterator<'a> { text: &'a str, chars: Peekable>, } diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 354273fa..1502c7c1 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -312,6 +312,41 @@ fn squeeze_limit_line_numbers() { .stdout(" 1 line 1\n 2 \n 3 \n 4 \n 5 line 5\n 6 \n 7 \n 8 \n 9 \n 10 \n 20 line 20\n 21 line 21\n 22 \n 23 \n 24 line 24\n 25 \n 26 line 26\n 27 \n 28 \n 29 \n 30 line 30\n"); } +#[test] +fn list_themes_with_colors() { + #[cfg(target_os = "macos")] + let default_theme_chunk = "Monokai Extended Light\x1B[0m (default)"; + + #[cfg(not(target_os = "macos"))] + let default_theme_chunk = "Monokai Extended\x1B[0m (default)"; + + bat() + .arg("--color=always") + .arg("--list-themes") + .assert() + .success() + .stdout(predicate::str::contains("DarkNeon").normalize()) + .stdout(predicate::str::contains(default_theme_chunk).normalize()) + .stdout(predicate::str::contains("Output the square of a number.").normalize()); +} + +#[test] +fn list_themes_without_colors() { + #[cfg(target_os = "macos")] + let default_theme_chunk = "Monokai Extended Light (default)"; + + #[cfg(not(target_os = "macos"))] + let default_theme_chunk = "Monokai Extended (default)"; + + bat() + .arg("--color=never") + .arg("--list-themes") + .assert() + .success() + .stdout(predicate::str::contains("DarkNeon").normalize()) + .stdout(predicate::str::contains(default_theme_chunk).normalize()); +} + #[test] #[cfg_attr(any(not(feature = "git"), target_os = "windows"), ignore)] fn short_help() { @@ -2671,3 +2706,215 @@ fn highlighting_independant_from_map_syntax_case() { .stdout(expected) .stderr(""); } + +#[test] +fn strip_ansi_always_strips_ansi() { + bat() + .arg("--style=plain") + .arg("--decorations=always") + .arg("--color=never") + .arg("--strip-ansi=always") + .write_stdin("\x1B[33mYellow\x1B[m") + .assert() + .success() + .stdout("Yellow"); +} + +#[test] +fn strip_ansi_never_does_not_strip_ansi() { + let output = String::from_utf8( + bat() + .arg("--style=plain") + .arg("--decorations=always") + .arg("--color=never") + .arg("--strip-ansi=never") + .write_stdin("\x1B[33mYellow\x1B[m") + .assert() + .success() + .get_output() + .stdout + .clone(), + ) + .expect("valid utf8"); + + assert!(output.contains("\x1B[33mYellow")) +} + +#[test] +fn strip_ansi_does_not_affect_simple_printer() { + let output = String::from_utf8( + bat() + .arg("--style=plain") + .arg("--decorations=never") + .arg("--color=never") + .arg("--strip-ansi=always") + .write_stdin("\x1B[33mYellow\x1B[m") + .assert() + .success() + .get_output() + .stdout + .clone(), + ) + .expect("valid utf8"); + + assert!(output.contains("\x1B[33mYellow")) +} + +#[test] +fn strip_ansi_does_not_strip_when_show_nonprintable() { + let output = String::from_utf8( + bat() + .arg("--style=plain") + .arg("--decorations=never") + .arg("--color=always") + .arg("--strip-ansi=always") + .arg("--show-nonprintable") + .write_stdin("\x1B[33mY") + .assert() + .success() + .get_output() + .stdout + .clone(), + ) + .expect("valid utf8"); + + assert!(output.contains("␛")) +} + +#[test] +fn strip_ansi_auto_strips_ansi_when_detected_syntax_by_filename() { + bat() + .arg("--style=plain") + .arg("--decorations=always") + .arg("--color=never") + .arg("--strip-ansi=auto") + .arg("--file-name=test.rs") + .write_stdin("fn \x1B[33mYellow\x1B[m() -> () {}") + .assert() + .success() + .stdout("fn Yellow() -> () {}"); +} + +#[test] +fn strip_ansi_auto_strips_ansi_when_provided_syntax_by_option() { + bat() + .arg("--style=plain") + .arg("--decorations=always") + .arg("--color=never") + .arg("--strip-ansi=auto") + .arg("--language=rust") + .write_stdin("fn \x1B[33mYellow\x1B[m() -> () {}") + .assert() + .success() + .stdout("fn Yellow() -> () {}"); +} + +#[test] +fn strip_ansi_auto_does_not_strip_when_plain_text_by_filename() { + let output = String::from_utf8( + bat() + .arg("--style=plain") + .arg("--decorations=always") + .arg("--color=never") + .arg("--strip-ansi=auto") + .arg("--file-name=ansi.txt") + .write_stdin("\x1B[33mYellow\x1B[m") + .assert() + .success() + .get_output() + .stdout + .clone(), + ) + .expect("valid utf8"); + + assert!(output.contains("\x1B[33mYellow")) +} + +#[test] +fn strip_ansi_auto_does_not_strip_ansi_when_plain_text_by_option() { + let output = String::from_utf8( + bat() + .arg("--style=plain") + .arg("--decorations=always") + .arg("--color=never") + .arg("--strip-ansi=auto") + .arg("--language=txt") + .write_stdin("\x1B[33mYellow\x1B[m") + .assert() + .success() + .get_output() + .stdout + .clone(), + ) + .expect("valid utf8"); + + assert!(output.contains("\x1B[33mYellow")) +} + +// Tests that style components can be removed with `-component`. +#[test] +fn style_components_can_be_removed() { + bat() + .arg({ + #[cfg(not(feature = "git"))] + { + "--style=full,-grid" + } + #[cfg(feature = "git")] + { + "--style=full,-grid,-changes" + } + }) + .arg("--decorations=always") + .arg("--color=never") + .write_stdin("test") + .assert() + .success() + .stdout(" STDIN\n Size: -\n 1 test\n") + .stderr(""); +} + +// Tests that style components are chosen based on the rightmost `--style` argument. +#[test] +fn style_components_can_be_overidden() { + bat() + .arg("--style=full") + .arg("--style=header,numbers") + .arg("--decorations=always") + .arg("--color=never") + .write_stdin("test") + .assert() + .success() + .stdout(" STDIN\n 1 test\n") + .stderr(""); +} + +// Tests that style components can be merged across multiple `--style` arguments. +#[test] +fn style_components_will_merge() { + bat() + .arg("--style=header,grid") + .arg("--style=-grid,+numbers") + .arg("--decorations=always") + .arg("--color=never") + .write_stdin("test") + .assert() + .success() + .stdout(" STDIN\n 1 test\n") + .stderr(""); +} + +// Tests that style components can be merged with the `BAT_STYLE` environment variable. +#[test] +fn style_components_will_merge_with_env_var() { + bat() + .env("BAT_STYLE", "header,grid") + .arg("--style=-grid,+numbers") + .arg("--decorations=always") + .arg("--color=never") + .write_stdin("test") + .assert() + .success() + .stdout(" STDIN\n 1 test\n") + .stderr(""); +} diff --git a/tests/syntax-tests/highlighted/CFML/test.cfml b/tests/syntax-tests/highlighted/CFML/test.cfml new file mode 100644 index 00000000..4aa8ecdc --- /dev/null +++ b/tests/syntax-tests/highlighted/CFML/test.cfml @@ -0,0 +1,54 @@ +<head>  +<title>Add New Employees  +  +<body>  +<h1>Add New Employees  +  +  +<cfparam name="Form.firstname" default="">  +<cfparam name="Form.lastname" default="">  +<cfparam name="Form.email" default="">  +<cfparam name="Form.phone" default="">  +<cfparam name="Form.department" default="">  +  +<cfif #Form.firstname# eq "">  +<p>Please fill out the form.  +<cfelse>  +<cfoutput>  +<cfscript>  +employee=StructNew();  +employee.firstname = Form.firstname;  +employee.lastname = Form.lastname;  +employee.email = Form.email;  +employee.phone = Form.phone;  +employee.department = Form.department;  +  +  +First name is #StructFind(employee, "firstname")#<br>  +Last name is #StructFind(employee, "lastname")#<br>  +EMail is #StructFind(employee, "email")#<br>  +Phone is #StructFind(employee, "phone")#<br>  +Department is #StructFind(employee, "department")#<br>  +  +  +<cf_addemployee empinfo="#employee#">  +  +  +<hr>  +<form action="newemployee.cfm" method="Post">  +First Name:&nbsp;  +<input name="firstname" type="text" hspace="30" maxlength="30"><br>  +Last Name:&nbsp;  +<input name="lastname" type="text" hspace="30" maxlength="30"><br>  +EMail:&nbsp;  +<input name="email" type="text" hspace="30" maxlength="30"><br>  +Phone:&nbsp;  +<input name="phone" type="text" hspace="20" maxlength="20"><br>  +Department:&nbsp;  +<input name="department" type="text" hspace="30" maxlength="30"><br>  +<input type="Submit" value="OK">  +  +<br>  +  + diff --git a/tests/syntax-tests/highlighted/Lisp/utils.lisp b/tests/syntax-tests/highlighted/Lisp/utils.lisp index ef61a820..1b12c557 100644 --- a/tests/syntax-tests/highlighted/Lisp/utils.lisp +++ b/tests/syntax-tests/highlighted/Lisp/utils.lisp @@ -1,80 +1,80 @@ -(cl:defpackage :chillax.utils - (:use :cl :alexandria) - (:export - :fun :mkhash :hashget :strcat :dequote :at)) -(in-package :chillax.utils) +(cl:defpackage :chillax.utils + (:use :cl :alexandria) + (:export + :fun :mkhash :hashget :strcat :dequote :at)) +(in-package :chillax.utils) -;;; Functions -(defmacro fun (&body body) +;;; Functions +(defmacro fun (&body body)  "This macro puts the FUN back in FUNCTION." - `(lambda (&optional _) (declare (ignorable _)) ,@body)) + `(lambda (&optional _) (declare (ignorable _)) ,@body)) -;;; Hash tables -(defun mkhash (&rest keys-and-values &aux (table (make-hash-table :test #'equal))) +;;; Hash tables +(defun mkhash (&rest keys-and-values &aux (table (make-hash-table :test #'equal)))  "Convenience function for `literal' hash table definition." - (loop for (key val) on keys-and-values by #'cddr do (setf (gethash key table) val) - finally (return table))) + (loop for (key val) on keys-and-values by #'cddr do (setf (gethash key table) val) + finally (return table))) -(defun hashget (hash &rest keys) +(defun hashget (hash &rest keys)  "Convenience function for recursively accessing hash tables." - (reduce (lambda (h k) (gethash k h)) keys :initial-value hash)) + (reduce (lambda (h k) (gethash k h)) keys :initial-value hash)) -(define-compiler-macro hashget (hash &rest keys) - (if (null keys) hash - (let ((hash-sym (make-symbol "HASH")) - (key-syms (loop for i below (length keys) - collect (make-symbol (format nil "~:@(~:R~)-KEY" i))))) - `(let ((,hash-sym ,hash) - ,@(loop for key in keys for sym in key-syms - collect `(,sym ,key))) - ,(reduce (lambda (hash key) `(gethash ,key ,hash)) - key-syms :initial-value hash-sym))))) +(define-compiler-macro hashget (hash &rest keys) + (if (null keys) hash + (let ((hash-sym (make-symbol "HASH")) + (key-syms (loop for i below (length keys) + collect (make-symbol (format nil "~:@(~:R~)-KEY" i))))) + `(let ((,hash-sym ,hash) + ,@(loop for key in keys for sym in key-syms + collect `(,sym ,key))) + ,(reduce (lambda (hash key) `(gethash ,key ,hash)) + key-syms :initial-value hash-sym))))) -(defun (setf hashget) (new-value hash key &rest more-keys) +(defun (setf hashget) (new-value hash key &rest more-keys)  "Uses the last key given to hashget to insert NEW-VALUE into the hash table returned by the second-to-last key. tl;dr: DWIM SETF function for HASHGET." - (if more-keys - (setf (gethash (car (last more-keys)) - (apply #'hashget hash key (butlast more-keys))) - new-value) - (setf (gethash key hash) new-value))) + (if more-keys + (setf (gethash (car (last more-keys)) + (apply #'hashget hash key (butlast more-keys))) + new-value) + (setf (gethash key hash) new-value))) -;;; Strings -(defun strcat (string &rest more-strings) - (apply #'concatenate 'string string more-strings)) +;;; Strings +(defun strcat (string &rest more-strings) + (apply #'concatenate 'string string more-strings)) -(defun dequote (string) - (let ((len (length string))) - (if (and (> len 1) (starts-with #\" string) (ends-with #\" string)) - (subseq string 1 (- len 1)) - string))) +(defun dequote (string) + (let ((len (length string))) + (if (and (> len 1) (starts-with #\" string) (ends-with #\" string)) + (subseq string 1 (- len 1)) + string))) -;;; -;;; At -;;; -(defgeneric at (doc &rest keys)) -(defgeneric (setf at) (new-value doc key &rest more-keys)) +;;; +;;; At +;;; +(defgeneric at (doc &rest keys)) +(defgeneric (setf at) (new-value doc key &rest more-keys)) -(defmethod at ((doc hash-table) &rest keys) - (apply #'hashget doc keys)) -(defmethod (setf at) (new-value (doc hash-table) key &rest more-keys) - (apply #'(setf hashget) new-value doc key more-keys)) +(defmethod at ((doc hash-table) &rest keys) + (apply #'hashget doc keys)) +(defmethod (setf at) (new-value (doc hash-table) key &rest more-keys) + (apply #'(setf hashget) new-value doc key more-keys)) -(defmethod at ((doc list) &rest keys) - (reduce (lambda (alist key) - (cdr (assoc key alist :test #'equal))) - keys :initial-value doc)) -(defmethod (setf at) (new-value (doc list) key &rest more-keys) - (if more-keys - (setf (cdr (assoc (car (last more-keys)) - (apply #'at doc key (butlast more-keys)) - :test #'equal)) - new-value) - (setf (cdr (assoc key doc :test #'equal)) new-value))) +(defmethod at ((doc list) &rest keys) + (reduce (lambda (alist key) + (cdr (assoc key alist :test #'equal))) + keys :initial-value doc)) +(defmethod (setf at) (new-value (doc list) key &rest more-keys) + (if more-keys + (setf (cdr (assoc (car (last more-keys)) + (apply #'at doc key (butlast more-keys)) + :test #'equal)) + new-value) + (setf (cdr (assoc key doc :test #'equal)) new-value))) -;; A playful alias. -(defun @ (doc &rest keys) - (apply #'at doc keys)) -(defun (setf @) (new-value doc key &rest more-keys) - (apply #'(setf at) new-value doc key more-keys)) +;; A playful alias. +(defun @ (doc &rest keys) + (apply #'at doc keys)) +(defun (setf @) (new-value doc key &rest more-keys) + (apply #'(setf at) new-value doc key more-keys)) diff --git a/tests/syntax-tests/source/CFML/test.cfml b/tests/syntax-tests/source/CFML/test.cfml new file mode 100644 index 00000000..f119af76 --- /dev/null +++ b/tests/syntax-tests/source/CFML/test.cfml @@ -0,0 +1,54 @@ + +Add New Employees + + +

Add New Employees

+ + + + + + + + + +

Please fill out the form.

+ + + +employee=StructNew(); +employee.firstname = Form.firstname; +employee.lastname = Form.lastname; +employee.email = Form.email; +employee.phone = Form.phone; +employee.department = Form.department; + + +First name is #StructFind(employee, "firstname")#
+Last name is #StructFind(employee, "lastname")#
+EMail is #StructFind(employee, "email")#
+Phone is #StructFind(employee, "phone")#
+Department is #StructFind(employee, "department")#
+
+ + +
+ +
+
+First Name:  +
+Last Name:  +
+EMail:  +
+Phone:  +
+Department:  +
+ +
+
+ +