1
0
mirror of https://github.com/sharkdp/bat.git synced 2025-09-01 10:52:24 +01:00

Compare commits

..

84 Commits

Author SHA1 Message Date
Martin Nordholts
271842d87c Add bat panic regression test when LiveScript is missing 2021-10-23 10:40:27 +02:00
rhysd
ed3246c423 Make grep-cli optional dependency 2021-10-17 21:22:57 +02:00
Diva M
2339d78bf4 update snapshot tests 2021-10-17 21:21:23 +02:00
Diva M
3a3cd0acba changelog 2021-10-17 21:21:23 +02:00
Diva M
ce4ddc0911 use saturating substraction to calculate Line ranges 2021-10-17 21:21:23 +02:00
i-ky
aed4ea144f Sync README header across translations (#1905)
* Fix links in Russian translation.
* Simplify links in Korean translation.
* Fix build status badge in Japanese and Russian translations.
* Add link from Japanese to Russian translation.
* Add line break between "topics" row and "translations" row.
* Remove "translations" from translations row in Japanese and Russian.
* Add consistent spacing between translation links.
* Add backreferences to English README.
* Remove self references.
* Split translations row across multiple lines for easier maintenance.

Co-authored-by: David Peter <sharkdp@users.noreply.github.com>
2021-10-17 21:15:23 +02:00
Raf Czlonka
eea061c1d9 Add install instructions on OpenBSD 2021-10-17 21:11:07 +02:00
Ikko Ashimine
10288e309e Add Русский link 2021-10-17 20:44:15 +02:00
mac.mini
ebdb00d4fc add security vulnerabilities in ko doc 2021-10-16 21:05:02 +02:00
NotWearingPants
6fc7ebf37a Add missing style values in fish & zsh completions (#1890) 2021-10-11 19:13:16 +02:00
USER
8f6a0cd9e2 update Korean readme 2021-10-10 10:24:04 +02:00
Martin Nordholts
994c21a5e1 syntax-tests: Make CpuInfo test actually work (#1887)
* syntax-tests: Make CpuInfo test actually work

File extension matching is case-sensitive, so extension needs to be .cpuinfo for
the syntax to actually be used.

* Also fix MemInfo
2021-10-06 06:50:11 +02:00
Martin Nordholts
554374667e Deny unsafe code in lib and bin
The deny also applies recursively to submodules.
2021-10-04 08:08:33 +02:00
Martin Nordholts
043f3381b0 CICD: Make the 'cargo fmt' check a toplevel job (#1883)
Mainly to make it easier to see what went wrong when it fails.

If this ever gets of out sync with a particular Rust version, we can most likely
save the situation by introducing a `rustfmt.toml` file.
2021-10-04 08:08:14 +02:00
Georgy Komarov
d04a83de7b Add Racket syntax 2021-10-03 19:17:26 +02:00
invakid404
b622a4d890 chore: add funtoo linux instructions 2021-10-03 09:15:36 +02:00
Roneo.Org
b551d28a2f List available Ubuntu packages more precisely.
Ref: https://github.com/sharkdp/bat/pull/1865#issuecomment-931709001
2021-10-03 09:12:18 +02:00
Roneo.Org
e6caa04209 Minor rephrasing 2021-10-03 09:12:18 +02:00
Roneo.Org
adadedeab1 Update the doc for Ubuntu and Debian 2021-10-03 09:12:18 +02:00
dependabot[bot]
a6cf5235aa Bump assert_cmd from 2.0.0 to 2.0.1
Bumps [assert_cmd](https://github.com/assert-rs/assert_cmd) from 2.0.0 to 2.0.1.
- [Release notes](https://github.com/assert-rs/assert_cmd/releases)
- [Changelog](https://github.com/assert-rs/assert_cmd/blob/master/CHANGELOG.md)
- [Commits](https://github.com/assert-rs/assert_cmd/compare/v2.0.0...v2.0.1)

---
updated-dependencies:
- dependency-name: assert_cmd
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-03 08:13:06 +02:00
dependabot[bot]
1477338106 Bump nix from 0.22.1 to 0.23.0
Bumps [nix](https://github.com/nix-rust/nix) from 0.22.1 to 0.23.0.
- [Release notes](https://github.com/nix-rust/nix/releases)
- [Changelog](https://github.com/nix-rust/nix/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nix-rust/nix/compare/v0.22.1...v0.23.0)

---
updated-dependencies:
- dependency-name: nix
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-03 08:10:26 +02:00
Martin Nordholts
418fce5683 Bump MSRV to 1.46
See #1874 and #1872.
2021-10-03 07:51:37 +02:00
dependabot[bot]
7a15ba3796 Bump assets/syntaxes/02_Extra/Julia from 48639e1 to 1e55f32
Bumps [assets/syntaxes/02_Extra/Julia](https://github.com/JuliaEditorSupport/Julia-sublime) from `48639e1` to `1e55f32`.
- [Release notes](https://github.com/JuliaEditorSupport/Julia-sublime/releases)
- [Commits](48639e1dbf...1e55f3211b)

---
updated-dependencies:
- dependency-name: assets/syntaxes/02_Extra/Julia
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-02 13:25:57 +02:00
dependabot[bot]
0f002a5b06 Bump serde_yaml from 0.8.20 to 0.8.21
Bumps [serde_yaml](https://github.com/dtolnay/serde-yaml) from 0.8.20 to 0.8.21.
- [Release notes](https://github.com/dtolnay/serde-yaml/releases)
- [Commits](https://github.com/dtolnay/serde-yaml/compare/0.8.20...0.8.21)

---
updated-dependencies:
- dependency-name: serde_yaml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-02 13:20:53 +02:00
dependabot[bot]
5344a32d34 Bump unicode-width from 0.1.8 to 0.1.9
Bumps [unicode-width](https://github.com/unicode-rs/unicode-width) from 0.1.8 to 0.1.9.
- [Release notes](https://github.com/unicode-rs/unicode-width/releases)
- [Commits](https://github.com/unicode-rs/unicode-width/compare/v0.1.8...v0.1.9)

---
updated-dependencies:
- dependency-name: unicode-width
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-02 12:18:05 +02:00
dependabot[bot]
c9645693a4 Bump flate2 from 1.0.20 to 1.0.22
Bumps [flate2](https://github.com/rust-lang/flate2-rs) from 1.0.20 to 1.0.22.
- [Release notes](https://github.com/rust-lang/flate2-rs/releases)
- [Commits](https://github.com/rust-lang/flate2-rs/compare/1.0.20...1.0.22)

---
updated-dependencies:
- dependency-name: flate2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-02 11:17:00 +02:00
dependabot[bot]
f607263bdc Bump thiserror from 1.0.28 to 1.0.29
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.28 to 1.0.29.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.28...1.0.29)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-02 11:16:08 +02:00
dependabot[bot]
f309d2fbd2 Bump serde from 1.0.127 to 1.0.130
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.127 to 1.0.130.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.127...v1.0.130)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-02 11:15:50 +02:00
Kuan-Yi Li
47283f226a Add bash completion to deb package
From the discussion in #1734, instead of using `env.PROJECT_NAME`,
`steps.strip.outputs.BIN_NAME` could potentially be a better choice as
the filename needs to match the called executable for bash-completion's
on demand loading to work. This `BIN_NAME` needs to stay in sync with
`env.PROJECT_EXECUTABLE` which is replaced in the template, but not
available for the deb build at this stage.

Follow the same route as for the fish/zsh completions for now for the
sake of consistency.

Closes #1733
2021-10-02 11:13:19 +02:00
Martin Nordholts
aefc8fd824 src/printer.rs: Simplify Plain Text fallback code
By forwarding the task to find the `Plain Text` syntax to `assets`. Not only does
the code become simpler; we also get rid of a call to `self.get_syntax_set()`
which is beneficial to the long term goal of replacing `syntaxes.bin` with
`minimal_syntaxes.bin`.

Note that the use of `.expect()` is not a regression in error handling. It was
previously hidden in `.find_syntax_plain_text()`.
2021-09-29 17:54:45 +02:00
Martin Nordholts
405a80f3ee HighlightingAssets: Turn get_syntax_for_path() into public API 2021-09-28 07:26:20 +02:00
Martin Nordholts
ad98d35a48 HighlightingAssets: Implement get_syntax_for_file_name() with get_syntax_for_path()
We can do this since the file_name() of a file_name is file_name.
2021-09-28 07:26:20 +02:00
Martin Nordholts
b69ab219d7 HighlightingAssets: Extract get_syntax_for_path() method
To make the code easier to refactor further.
2021-09-28 07:26:20 +02:00
Martin Nordholts
dc8225f682 src/assets.rs: Extract helper method OpenedInput::path() 2021-09-27 08:00:58 +02:00
Martin Nordholts
9d9b266f54 build_assets.rs: Enable dump of syntax dependencies to Graphviz dot file 2021-09-24 20:07:55 +02:00
Martin Nordholts
b9d01c1a61 build_assets.rs: Ignore explicit contexts when tracking dependencies 2021-09-24 20:07:55 +02:00
Martin Nordholts
122cae7902 build_assets.rs: Sort first to make dependencies.dedup() actually useful 2021-09-24 20:07:55 +02:00
David Peter
44a332c1c4 Parallelize syntax regression tests
The syntax highlighting regression tests can be trivially parallelized.
On my notebook (8 core), this results in a 3.9x speedup.
2021-09-22 22:18:01 +02:00
Martin Nordholts
5143f3ad43 build_assets.rs: Add code to track dependents of each syntax
This information is useful when you want to build several SyntaxSets, but
without having to duplicate SyntaxDefinitions. For example:

"Rust" has no dependencies. But "Markdown" depends on "Rust". With the data
structures this code adds, we know that "Rust" is a dependent syntax for
"Markdown", and can construct a SyntaxSet that takes that into account.

Note that code has a temporary environment flag to ignore any information about
dependents when constructing SyntaxSets. Code that makes use of the new data
structure will be added later.
2021-09-22 09:14:19 +02:00
Martin Nordholts
a6dc25a216 build_assets.rs: Make OtherSyntaxLookup come before SyntaxToDependencies
It makes more sense structurally when we later introduce SyntaxToDependents.
2021-09-22 09:14:19 +02:00
Martin Nordholts
f04d2a9d6a build_assets.rs: Rename 'Dependency' to 'OtherSyntax'
So that we later can use it for not only information about dependencies, but
also for information about dependents.
2021-09-22 09:14:19 +02:00
Martin Nordholts
eb3b3b9f8d src/printer.rs: Add HighlighterFromSet helper
The main benefit is that we get rid of a call to `assets.get_syntax_set()`,
which in turn makes it easier to later get rid of `syntaxes.bin`.
2021-09-22 06:11:32 +02:00
Martin Nordholts
0994f3783f HighlightingAssets: Move common get_extension_syntax() code into find_syntax_by_extension() 2021-09-22 06:03:24 +02:00
Martin Nordholts
974dec38e3 HighlightingAssets: Inline find_syntax_by_file_name() and find_syntax_by_file_name_extension()
There is no benefit to keeping separate functions.
2021-09-22 06:03:24 +02:00
Martin Nordholts
a0c363647f src/assets.rs: Use /// not // for COMPRESS_* consts 2021-09-18 07:05:04 +02:00
Martin Nordholts
d989224a8a HighlightingAssets: Inline absolute_path
Its name is confusing, because it does not always hold an absolute path. Get rid
of this problem by inlining it.
2021-09-18 06:19:52 +02:00
Martin Nordholts
82f439e715 HighlightingAssets: Simplify absolute_path with .map_or_else() 2021-09-18 06:19:52 +02:00
Martin Nordholts
b034879eae HighlightingAssets: No need for both path and path_str 2021-09-18 06:19:52 +02:00
Martin Nordholts
9ed9a6fc3d Simplify HighlightingAssets::get_syntax() first_line logic (#1852)
And make self.get_first_line_syntax() be called lazily.
2021-09-16 17:01:12 +02:00
Martin Nordholts
e84b702309 Extract some private submodules from 'bat::assets' (#1850) 2021-09-15 07:59:33 +02:00
Martin Nordholts
6226eba52a HighlightingAssets: Add find_syntax_by_extension() helper 2021-09-14 07:38:36 +02:00
Martin Nordholts
9e0ea06435 HighlightingAssets: Add find_syntax_by_name() helper 2021-09-14 07:38:36 +02:00
Marcin Puc
863d9cacd0 Add various other code refactorings 2021-09-12 15:50:10 +02:00
Marcin Puc
4baa346aae Use deref coercion to simplify some argument passing 2021-09-12 15:50:10 +02:00
Marcin Puc
7956485e37 Improve iterator usage 2021-09-12 15:50:10 +02:00
Marcin Puc
372e42f350 Reduce nesting in if blocks by short circuiting 2021-09-12 15:50:10 +02:00
Martin Nordholts
9124271eaf Load independent and minimal syntax sets when using --language (#1787)
This significantly speeds up the startup time of bat, since only a single
linked SyntaxDefinition is loaded for each file. The size increase of the
binary is just ~400 kB.

In order for startup time to be improved, the --language arg must be used, and
it must match one of the following names:

"Plain Text", "ActionScript", "AppleScript", "Batch File", "NAnt Build File",
"C#", "C", "CSS", "D", "Diff", "Erlang", "Go", "Haskell", "JSON", "Java
Properties", "BibTeX", "LaTeX Log", "TeX", "Lisp", "Lua", "MATLAB", "Pascal",
"R", "Regular Expression", "Rust", "SQL", "Scala", "Tcl", "XML", "YAML", "Apache
Conf", "ARM Assembly", "Assembly (x86_64)", "CMakeCache", "Comma Separated
Values", "Cabal", "CoffeeScript", "CpuInfo", "Dart Analysis Output", "Dart",
"Dockerfile", "DotENV", "F#", "Friendly Interactive Shell (fish)", "Fortran
(Fixed Form)", "Fortran (Modern)", "Fortran Namelist", "fstab", "GLSL",
"GraphQL", "Groff/troff", "group", "hosts", "INI", "Jinja2", "jsonnet",
"Kotlin", "Less", "LLVM", "Lean", "MemInfo", "Nim", "Ninja", "Nix", "passwd",
"PowerShell", "Protocol Buffer (TEXT)", "Puppet", "Rego", "resolv", "Robot
Framework", "SML", "Strace", "Stylus", "Solidity", "Vyper", "Swift",
"SystemVerilog", "TOML", "Terraform", "TypeScript", "TypeScriptReact",
"Verilog", "VimL", "Zig", "gnuplot", "log", "requirements.txt", "Highlight
non-printables", "Private Key", "varlink"

Later commits will improve startup time for more code paths.

* fix some typos and misspellings

* CHANGELOG.md: Add Performance section (preliminary)

* Add a CHANGELOG.md entry for this PR
2021-09-09 20:52:33 +02:00
Kevin John Mulligan
156dec2737 Add context to .ino configuration
This allows a user to search the README or the entire repository for "Arduino" to find the necessary configuration.
2021-09-07 20:21:12 +02:00
David Peter
27f046ec03 Consolidate environment variable lists
We want to make sure that all of our test environments are clean from
possible outside modification. This consolidates the list of used
environment variables in Rust-based and Python-based integration tests.

Note that there is also a similar list in `src/bin/bat/main.rs` which
is even more exhaustive (for bug report collection). However, some
of these variables can not possibly have an effect on test environments.
2021-09-07 20:17:16 +02:00
dependabot[bot]
74ae3dee91 Bump serde_yaml from 0.8.17 to 0.8.20
Bumps [serde_yaml](https://github.com/dtolnay/serde-yaml) from 0.8.17 to 0.8.20.
- [Release notes](https://github.com/dtolnay/serde-yaml/releases)
- [Commits](https://github.com/dtolnay/serde-yaml/compare/0.8.17...0.8.20)

---
updated-dependencies:
- dependency-name: serde_yaml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-07 18:38:43 +02:00
Martin Nordholts
87978e7755 Make asset compression optional at compile time (#1825)
This will be needed to later support zero-copy deserialization of independent
syntax sets, but is interesting and useful on its own.

Instead of deferring serialization and deserialization to syntect, we implement it
ourselves in the same way, but make compression optional.
2021-09-07 17:21:48 +02:00
Martin Nordholts
d935ea1cda Add regression testing for the custom assets functionality (#1829)
The test is following the same steps regular users are instructed to follow:
https://github.com/sharkdp/bat/blob/master/README.md#adding-new-syntaxes--language-definitions
2021-09-07 17:01:15 +02:00
dependabot[bot]
9602195910 Bump assert_cmd from 1.0.8 to 2.0.0
Bumps [assert_cmd](https://github.com/assert-rs/assert_cmd) from 1.0.8 to 2.0.0.
- [Release notes](https://github.com/assert-rs/assert_cmd/releases)
- [Changelog](https://github.com/assert-rs/assert_cmd/blob/master/CHANGELOG.md)
- [Commits](https://github.com/assert-rs/assert_cmd/compare/v1.0.8...v2.0.0)

---
updated-dependencies:
- dependency-name: assert_cmd
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-03 19:31:02 +02:00
Martin Nordholts
8ca852c728 CICD: Put documentation testing in its own job (#1831)
For increased parallelization and thus a faster CICD pipeline.
2021-09-03 18:03:52 +02:00
dependabot[bot]
df067f7d1f Bump predicates from 2.0.1 to 2.0.2
Bumps [predicates](https://github.com/assert-rs/predicates-rs) from 2.0.1 to 2.0.2.
- [Release notes](https://github.com/assert-rs/predicates-rs/releases)
- [Changelog](https://github.com/assert-rs/predicates-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/assert-rs/predicates-rs/compare/v2.0.1...v2.0.2)

---
updated-dependencies:
- dependency-name: predicates
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-02 17:11:06 +02:00
dependabot[bot]
a8a81e99d2 Bump nix from 0.22.0 to 0.22.1
Bumps [nix](https://github.com/nix-rust/nix) from 0.22.0 to 0.22.1.
- [Release notes](https://github.com/nix-rust/nix/releases)
- [Changelog](https://github.com/nix-rust/nix/blob/v0.22.1/CHANGELOG.md)
- [Commits](https://github.com/nix-rust/nix/compare/v0.22.0...v0.22.1)

---
updated-dependencies:
- dependency-name: nix
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-02 17:09:12 +02:00
dependabot[bot]
65e7c531de Bump thiserror from 1.0.26 to 1.0.28
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.26 to 1.0.28.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.26...1.0.28)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-02 17:05:15 +02:00
Ville Skyttä
7c41bd72da assets: add Debian ucf backups to ignored suffixes
Refs https://manpages.debian.org/bullseye/ucf/ucf.1.en.html
2021-08-29 19:55:00 +02:00
Martin Nordholts
355a82db54 CICD: Sort build matrix by target 2021-08-29 15:39:25 +02:00
Martin Nordholts
b3e17bde82 CICD: Remove disabled windows-2019 i686-pc-windows-gnu from build matrix
It just messes up alignment. Nothing gets better by keeing it around.
2021-08-29 15:39:25 +02:00
Martin Nordholts
4b38e7b1d7 CICD: Swap target and os in matrix job name
The full name can frequently not be shown, and target is more relevant than os,
so show target first.
2021-08-29 15:39:25 +02:00
Rashil Gandhi
43afae34be Add PowerShell completion file (#1826) 2021-08-28 13:39:17 +02:00
Martin Nordholts
27fa55d274 src/build_assets.rs: Refactor into smaller functions (#1822)
To make the code easier to work with.
2021-08-27 09:56:20 +02:00
Martin Nordholts
19c3e82abf Replace deprecated 'error-chain' with 'thiserror' (#1820)
We can't use #[from] on Error::Msg(String) because String does not implement Error.
(Which it shouldn't; see e.g. https://internals.rust-lang.org/t/impl-error-for-string/8881.)
So we implement From manually for Error::Msg, since our current code was written
in that way for error-chain.
2021-08-26 13:12:21 +02:00
Martin Nordholts
f1c0fd7343 Don't take a HighlightingAssets detour to build assets (#1802)
Move code to build assets to its own file. That results in better modularity and flexibility.

It also allows us to simplify HighlightingAssets a lot, since it will now always
be initialized with a SerializedSyntaxSet.
2021-08-24 07:58:03 +02:00
Martin Nordholts
12dfbdc400 doc/release-checklist.md: Recommend git push origin tag vX.Y.Z (#1815) 2021-08-23 20:08:04 +02:00
Bojan Durdevic
c86a179412 PR comments addressed for line range +delta syntax 2021-08-23 19:55:41 +02:00
Bojan Durdevic
0748783404 Support for line range plus syntax 2021-08-23 19:55:41 +02:00
Martin Nordholts
b3247d9364 Merge the v0.18.3 hotfix release into master
The v0.18.3 release was a hotfix release to make bat build again with Rust 1.54.
This merge commit brings in all changes from the hotfix release into the master
branch.

In practice, this only brings in one new commit, namely b146958ec. The other
three commits in the release were cherry-picks from master.
2021-08-22 19:22:20 +02:00
Martin Nordholts
ba8a694314 Add LANG and LC_ALL to --diagnostics output
To make it easier to debug problems like #1806 in the future.
2021-08-22 18:31:39 +02:00
Martin Nordholts
b146958ecb Bump version to v0.18.3 2021-08-22 17:47:12 +02:00
Martin Nordholts
15c701b196 Bump bugreport to 0.4.1
(cherry picked from commit f6975e2acd)
2021-08-21 20:03:39 +02:00
David Peter
d8339808a1 Update git2 dependency to fix incompatibility with Rust 1.54
(cherry picked from commit f3d53b79a2)
2021-08-21 20:02:56 +02:00
bl-ue
3b263f0917 Fix typo in README
(cherry picked from commit fc0794a83d)
2021-08-21 20:02:23 +02:00
98 changed files with 2235 additions and 1247 deletions

View File

@@ -1,7 +1,7 @@
name: CICD
env:
MIN_SUPPORTED_RUST_VERSION: "1.45.0"
MIN_SUPPORTED_RUST_VERSION: "1.46.0"
CICD_INTERMEDIATES_DIR: "_cicd-intermediates"
on:
@@ -14,6 +14,19 @@ on:
- '*'
jobs:
ensure_cargo_fmt:
name: Ensure 'cargo fmt' has been run
runs-on: ubuntu-20.04
steps:
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
default: true
profile: minimal
components: rustfmt
- uses: actions/checkout@v2
- run: cargo fmt -- --check
min_version:
name: Minimum supported rust version
runs-on: ubuntu-20.04
@@ -27,17 +40,12 @@ jobs:
toolchain: ${{ env.MIN_SUPPORTED_RUST_VERSION }}
default: true
profile: minimal # minimal component installation (ie, no documentation)
components: clippy, rustfmt
- name: Ensure `cargo fmt` has been run
uses: actions-rs/cargo@v1
with:
command: fmt
args: -- --check
components: clippy
- name: Run clippy (on minimum supported rust version to prevent warnings we can't fix)
uses: actions-rs/cargo@v1
with:
command: clippy
args: --locked --all-targets --all-features
args: --locked --all-targets --all-features -- --allow clippy::unknown_clippy_lints
- name: Run tests
uses: actions-rs/cargo@v1
with:
@@ -86,6 +94,21 @@ jobs:
run: bat --list-languages
- name: List of themes
run: bat --list-themes
- name: Test custom assets
run: tests/syntax-tests/test_custom_assets.sh
documentation:
name: Documentation
runs-on: ubuntu-20.04
steps:
- name: Git checkout
uses: actions/checkout@v2
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
default: true
profile: minimal
- name: Check documentation
env:
RUSTDOCFLAGS: -D warnings
@@ -95,24 +118,23 @@ jobs:
args: --locked --no-deps --document-private-items --all-features
build:
name: ${{ matrix.job.os }} (${{ matrix.job.target }})
name: ${{ matrix.job.target }} (${{ matrix.job.os }})
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job:
- { os: ubuntu-20.04, target: arm-unknown-linux-gnueabihf , use-cross: true }
- { os: ubuntu-20.04, target: arm-unknown-linux-musleabihf, use-cross: true }
- { os: ubuntu-20.04, target: aarch64-unknown-linux-gnu , use-cross: true }
- { os: ubuntu-20.04, target: i686-unknown-linux-gnu , use-cross: true }
- { os: ubuntu-20.04, target: i686-unknown-linux-musl , use-cross: true }
- { os: ubuntu-20.04, target: x86_64-unknown-linux-gnu }
- { os: ubuntu-20.04, target: x86_64-unknown-linux-musl , use-cross: true }
- { os: macos-10.15 , target: x86_64-apple-darwin }
# - { os: windows-2019, target: i686-pc-windows-gnu } ## disabled; error: linker `i686-w64-mingw32-gcc` not found
- { os: windows-2019, target: i686-pc-windows-msvc }
- { os: windows-2019, target: x86_64-pc-windows-gnu }
- { os: windows-2019, target: x86_64-pc-windows-msvc }
- { target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
- { target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true }
- { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true }
- { target: i686-pc-windows-msvc , os: windows-2019 }
- { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
- { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
- { target: x86_64-apple-darwin , os: macos-10.15 }
- { 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 }
- { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
steps:
- name: Checkout source code
uses: actions/checkout@v2
@@ -194,12 +216,21 @@ jobs:
echo ::set-output name=BIN_PATH::${BIN_PATH}
echo ::set-output name=BIN_NAME::${BIN_NAME}
- name: Set testing options
id: test-options
shell: bash
run: |
# test only library unit tests and binary for arm-type targets
unset CARGO_TEST_OPTIONS
unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-* | aarch64-*) CARGO_TEST_OPTIONS="--lib --bin ${PROJECT_NAME}" ;; esac;
echo ::set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS}
- name: Run tests
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.job.use-cross }}
command: test
args: --locked --target=${{ matrix.job.target }}
args: --locked --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}}
- name: Run bat
uses: actions-rs/cargo@v1
@@ -276,6 +307,7 @@ jobs:
# Autocompletion files
cp 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'-*/out/assets/completions/bat.bash "$ARCHIVE_DIR/autocomplete/${{ env.PROJECT_NAME }}.bash"
cp 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'-*/out/assets/completions/bat.fish "$ARCHIVE_DIR/autocomplete/${{ env.PROJECT_NAME }}.fish"
cp 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'-*/out/assets/completions/_bat.ps1 "$ARCHIVE_DIR/autocomplete/_${{ env.PROJECT_NAME }}.ps1"
cp 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'-*/out/assets/completions/bat.zsh "$ARCHIVE_DIR/autocomplete/${{ env.PROJECT_NAME }}.zsh"
# base compressed package
@@ -324,6 +356,7 @@ jobs:
gzip -n --best "${DPKG_DIR}/usr/share/man/man1/${{ env.PROJECT_NAME }}.1"
# Autocompletion files
install -Dm644 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'-*/out/assets/completions/bat.bash "${DPKG_DIR}/usr/share/bash-completion/completions/${{ env.PROJECT_NAME }}"
install -Dm644 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'-*/out/assets/completions/bat.fish "${DPKG_DIR}/usr/share/fish/vendor_completions.d/${{ env.PROJECT_NAME }}.fish"
install -Dm644 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'-*/out/assets/completions/bat.zsh "${DPKG_DIR}/usr/share/zsh/vendor-completions/_${{ env.PROJECT_NAME }}"

3
.gitmodules vendored
View File

@@ -230,3 +230,6 @@
[submodule "assets/syntaxes/02_Extra/Slim"]
path = assets/syntaxes/02_Extra/Slim
url = https://github.com/slim-template/ruby-slim.tmbundle.git
[submodule "assets/syntaxes/02_Extra/Racket"]
path = assets/syntaxes/02_Extra/Racket
url = https://github.com/follesoe/sublime-racket.git

View File

@@ -3,16 +3,23 @@
## Features
- `$BAT_CONFIG_DIR` is now a recognized environment variable. It has precedence over `$XDG_CONFIG_HOME`, see #1727 (@billrisher)
- Support for `x:+delta` syntax in line ranges (e.g. `20:+10`). See #1810 (@bojan88)
## Bugfixes
- Python syntax highlighting no longer suffers from abysmal performance in specific scenarios. See #1688 (@keith-hall)
- First line not shown in diff context. See #1891 (@divagant-martian)
## Other
## Performance
- Load cached assets as fast as integrated assets, see #1753 (@Enselic)
- Greatly reduce startup time in loop-through mode, e.g. when redirecting output. Instead of *50 ms* - *100 ms*, startup takes *5 ms* - *10 ms*. See #1747 (@Enselic)
- Reduce startup time by approximately 80% for 91 out of 168 syntaxes when using `--language`. See #1787 (@Enselic)
## Other
- Add PowerShell completion, see #1826 (@rashil2000)
- Minimum supported Rust version (MSRV) bumped to 1.46
## Syntaxes
@@ -22,16 +29,26 @@
- Highlight for `vimrc` and `gvimrc` files, see #1763 (@SuperSandro2000)
- Syslog highlighting improvements, see #1793 (@scop)
- Added support for `slim` syntax, see #1693 (@mfinelli)
- Racket, see #1884 (@jubnzv)
## New themes
## `bat` as a library
- Deprecate `HighlightingAssets::syntaxes()` and `HighlightingAssets::syntax_for_file_name()`. Use `HighlightingAssets::get_syntaxes()` and `HighlightingAssets::get_syntax_for_file_name()` instead. They return a `Result` which is needed for upcoming lazy-loading work to improve startup performance. They also return what `SyntaxSet` the returned `SyntaxReference` belongs to. See #1747, #1755 and #1776 (@Enselic)
- Deprecate `HighlightingAssets::syntaxes()` and `HighlightingAssets::syntax_for_file_name()`. Use `HighlightingAssets::get_syntaxes()` and `HighlightingAssets::get_syntax_for_path()` instead. They return a `Result` which is needed for upcoming lazy-loading work to improve startup performance. They also return which `SyntaxSet` the returned `SyntaxReference` belongs to. See #1747, #1755, #1776, #1862 (@Enselic)
- Remove `HighlightingAssets::from_files` and `HighlightingAssets::save_to_cache`. Instead of calling the former and then the latter you now make a single call to `bat::assets::build`. See #1802 (@Enselic)
- Replace the `error::Error(error::ErrorKind, _)` struct and enum with an `error::Error` enum. `Error(ErrorKind::UnknownSyntax, _)` becomes `Error::UnknownSyntax`, etc. Also remove the `error::ResultExt` trait. These changes stem from replacing `error-chain` with `thiserror`. See #1820 (@Enselic)
# v0.18.3
## Bugfixes
- Bump `git2` dependency to fix build with Rust 1.54, see #1761
# v0.18.2
## Features

83
Cargo.lock generated
View File

@@ -46,9 +46,9 @@ dependencies = [
[[package]]
name = "assert_cmd"
version = "1.0.8"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c98233c6673d8601ab23e77eb38f999c51100d46c5703b17288c57fddf3a1ffe"
checksum = "b800c4403e8105d959595e1f88119e78bc12bc874c4336973658b648a746ba93"
dependencies = [
"bstr",
"doc-comment",
@@ -83,12 +83,13 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "bat"
version = "0.18.2"
version = "0.18.3"
dependencies = [
"ansi_colours",
"ansi_term 0.12.1",
"assert_cmd",
"atty",
"bincode",
"bugreport",
"clap",
"clircle",
@@ -96,7 +97,7 @@ dependencies = [
"content_inspector",
"dirs-next",
"encoding",
"error-chain",
"flate2",
"git2",
"globset",
"grep-cli",
@@ -112,6 +113,7 @@ dependencies = [
"shell-words",
"syntect",
"tempfile",
"thiserror",
"unicode-width",
"wait-timeout",
"wild",
@@ -143,9 +145,9 @@ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bitflags"
version = "1.2.1"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bstr"
@@ -376,15 +378,6 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569"
[[package]]
name = "error-chain"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
dependencies = [
"version_check",
]
[[package]]
name = "fancy-regex"
version = "0.7.1"
@@ -397,9 +390,9 @@ dependencies = [
[[package]]
name = "flate2"
version = "1.0.20"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0"
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
dependencies = [
"cfg-if",
"crc32fast",
@@ -597,9 +590,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.98"
version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
[[package]]
name = "libgit2-sys"
@@ -691,9 +684,9 @@ dependencies = [
[[package]]
name = "nix"
version = "0.22.0"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1e25ee6b412c2a1e3fcb6a4499a5c1bfe7f43e014bdce9a6b6666e5aa2d187"
checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188"
dependencies = [
"bitflags",
"cc",
@@ -817,9 +810,9 @@ checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "predicates"
version = "2.0.1"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc3d91237f5de3bcd9d927e24d03b495adb6135097b001cea7403e2d573d00a9"
checksum = "c143348f141cc87aab5b950021bac6145d0e5ae754b0591de23244cee42c9308"
dependencies = [
"difflib",
"float-cmp",
@@ -998,18 +991,18 @@ checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
[[package]]
name = "serde"
version = "1.0.127"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.127"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
dependencies = [
"proc-macro2",
"quote",
@@ -1029,12 +1022,12 @@ dependencies = [
[[package]]
name = "serde_yaml"
version = "0.8.17"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23"
checksum = "d8c608a35705a5d3cdc9fbe403147647ff34b921f8e833e49306df898f9b20af"
dependencies = [
"dtoa",
"linked-hash-map",
"indexmap",
"serde",
"yaml-rust",
]
@@ -1188,6 +1181,26 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tinyvec"
version = "1.2.0"
@@ -1229,9 +1242,9 @@ dependencies = [
[[package]]
name = "unicode-width"
version = "0.1.8"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
@@ -1263,12 +1276,6 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "wait-timeout"
version = "0.2.0"

View File

@@ -6,7 +6,7 @@ homepage = "https://github.com/sharkdp/bat"
license = "MIT/Apache-2.0"
name = "bat"
repository = "https://github.com/sharkdp/bat"
version = "0.18.2"
version = "0.18.3"
exclude = ["assets/syntaxes/*", "assets/themes/*"]
build = "build.rs"
edition = '2018'
@@ -33,7 +33,7 @@ minimal-application = [
"wild",
]
git = ["git2"] # Support indicating git modifications
paging = ["shell-words"] # Support applying a pager on the output
paging = ["shell-words", "grep-cli"] # Support applying a pager on the output
# Add "syntect/plist-load" when https://github.com/trishume/syntect/pull/345 reaches us
build-assets = ["syntect/yaml-load", "syntect/dump-create"]
@@ -45,14 +45,17 @@ regex-fancy = ["syntect/regex-fancy"] # Use the rust-only "fancy-regex" engine
atty = { version = "0.2.14", optional = true }
ansi_term = "^0.12.1"
ansi_colours = "^1.0"
bincode = "1.0"
console = "0.14.1"
flate2 = "1.0"
lazy_static = { version = "1.4", optional = true }
lazycell = "1.0"
thiserror = "1.0"
wild = { version = "2.0", optional = true }
content_inspector = "0.2.4"
encoding = "0.2"
shell-words = { version = "1.0.0", optional = true }
unicode-width = "0.1.8"
unicode-width = "0.1.9"
globset = "0.4"
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.8"
@@ -61,7 +64,7 @@ path_abs = { version = "0.5", default-features = false }
clircle = "0.3"
bugreport = { version = "0.4", optional = true }
dirs-next = { version = "2.0.0", optional = true }
grep-cli = "0.1.6"
grep-cli = { version = "0.1.6", optional = true }
[dependencies.git2]
version = "0.13"
@@ -71,7 +74,7 @@ default-features = false
[dependencies.syntect]
version = "4.6.0"
default-features = false
features = ["parsing", "dump-load"]
features = ["parsing"]
[dependencies.clap]
version = "2.33"
@@ -79,19 +82,15 @@ optional = true
default-features = false
features = ["suggestions", "color", "wrap_help"]
[dependencies.error-chain]
version = "0.12"
default-features = false
[dev-dependencies]
assert_cmd = "1.0.8"
assert_cmd = "2.0.1"
serial_test = "0.5.1"
predicates = "2.0.1"
predicates = "2.0.2"
wait-timeout = "0.2.0"
tempfile = "3.2.0"
[target.'cfg(unix)'.dev-dependencies]
nix = "0.22.0"
nix = "0.23.0"
[build-dependencies]
clap = { version = "2.33", optional = true }

View File

@@ -12,7 +12,11 @@
<a href="#installation">Installation</a>
<a href="#customization">Customization</a>
<a href="#project-goals-and-alternatives">Project goals, alternatives</a><br>
[<a href="https://github.com/chinanf-boy/bat-zh">中文</a>] [<a href="doc/README-ja.md">日本語</a>] [<a href="doc/README-ko.md">한국어</a>] [<a href="doc/README-ru.md">Русский</a>]
[English]
[<a href="https://github.com/chinanf-boy/bat-zh">中文</a>]
[<a href="doc/README-ja.md">日本語</a>]
[<a href="doc/README-ko.md">한국어</a>]
[<a href="doc/README-ru.md">Русский</a>]
</p>
### Syntax highlighting
@@ -196,10 +200,7 @@ The [`prettybat`](https://github.com/eth-p/bat-extras/blob/master/doc/prettybat.
### On Ubuntu (using `apt`)
*... and other Debian-based Linux distributions.*
`bat` is making its way through the [Ubuntu](https://packages.ubuntu.com/eoan/bat) and
[Debian](https://packages.debian.org/testing/bat) package release process, and is available
for Ubuntu as of Eoan 19.10. On Debian `bat` is currently available on the unstable
"Sid" branch and on the testing branch.
`bat` is available on [Ubuntu since 20.04 ("Focal")](https://packages.ubuntu.com/search?keywords=bat&exact=1) and [Debian since August 2021 (Debian 11 - "Bullseye")](https://packages.debian.org/bullseye/bat).
If your Ubuntu/Debian installation is new enough you can simply run:
@@ -222,7 +223,7 @@ the most recent release of `bat`, download the latest `.deb` package from the
[release page](https://github.com/sharkdp/bat/releases) and install it via:
```bash
sudo dpkg -i bat_0.18.2_amd64.deb # adapt version number and architecture
sudo dpkg -i bat_0.18.3_amd64.deb # adapt version number and architecture
```
### On Alpine Linux
@@ -251,6 +252,14 @@ You can install [the `bat` package](https://koji.fedoraproject.org/koji/packagei
dnf install bat
```
### On Funtoo Linux
You can install [the `bat` package](https://github.com/funtoo/dev-kit/tree/1.4-release/sys-apps/bat) from dev-kit.
```bash
emerge sys-apps/bat
```
### On Gentoo Linux
You can install [the `bat` package](https://packages.gentoo.org/packages/sys-apps/bat)
@@ -289,6 +298,14 @@ cd /usr/ports/textproc/bat
make install
```
### On OpenBSD
You can install `bat` package using [`pkg_add(1)`](https://man.openbsd.org/pkg_add.1):
```bash
pkg_add bat
```
### Via nix
You can install `bat` using the [nix package manager](https://nixos.org/nix):
@@ -363,7 +380,7 @@ binaries are also available: look for archives with `musl` in the file name.
### From source
If you want to build `bat` from source, you need Rust 1.45 or
If you want to build `bat` from source, you need Rust 1.46 or
higher. You can then use `cargo` to build everything:
```bash
@@ -598,7 +615,7 @@ Example configuration file:
# Use italic text on the terminal (not supported on all terminals)
--italic-text=always
# Use C++ syntax for .ino files
# Use C++ syntax for Arduino .ino files
--map-syntax "*.ino:C++"
```

92
assets/completions/_bat.ps1.in vendored Normal file
View File

@@ -0,0 +1,92 @@
using namespace System.Management.Automation
using namespace System.Management.Automation.Language
Register-ArgumentCompleter -Native -CommandName '{{PROJECT_EXECUTABLE}}' -ScriptBlock {
param($wordToComplete, $commandAst, $cursorPosition)
$commandElements = $commandAst.CommandElements
$command = @(
'{{PROJECT_EXECUTABLE}}'
for ($i = 1; $i -lt $commandElements.Count; $i++) {
$element = $commandElements[$i]
if ($element -isnot [StringConstantExpressionAst] -or
$element.StringConstantType -ne [StringConstantType]::BareWord -or
$element.Value.StartsWith('-')) {
break
}
$element.Value
}) -join ';'
$completions = @(switch ($command) {
'{{PROJECT_EXECUTABLE}}' {
[CompletionResult]::new('-l', 'l', [CompletionResultType]::ParameterName, 'Set the language for syntax highlighting.')
[CompletionResult]::new('--language', 'language', [CompletionResultType]::ParameterName, 'Set the language for syntax highlighting.')
[CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'Highlight lines N through M.')
[CompletionResult]::new('--highlight-line', 'highlight-line', [CompletionResultType]::ParameterName, 'Highlight lines N through M.')
[CompletionResult]::new('--file-name', 'file-name', [CompletionResultType]::ParameterName, 'Specify the name to display for a file.')
[CompletionResult]::new('--diff-context', 'diff-context', [CompletionResultType]::ParameterName, 'diff-context')
[CompletionResult]::new('--tabs', 'tabs', [CompletionResultType]::ParameterName, 'Set the tab width to T spaces.')
[CompletionResult]::new('--wrap', 'wrap', [CompletionResultType]::ParameterName, 'Specify the text-wrapping mode (*auto*, never, character).')
[CompletionResult]::new('--terminal-width', 'terminal-width', [CompletionResultType]::ParameterName, 'Explicitly set the width of the terminal instead of determining it automatically. If prefixed with ''+'' or ''-'', the value will be treated as an offset to the actual terminal width. See also: ''--wrap''.')
[CompletionResult]::new('--color', 'color', [CompletionResultType]::ParameterName, 'When to use colors (*auto*, never, always).')
[CompletionResult]::new('--italic-text', 'italic-text', [CompletionResultType]::ParameterName, 'Use italics in output (always, *never*)')
[CompletionResult]::new('--decorations', 'decorations', [CompletionResultType]::ParameterName, 'When to show the decorations (*auto*, never, always).')
[CompletionResult]::new('--paging', 'paging', [CompletionResultType]::ParameterName, 'Specify when to use the pager, or use `-P` to disable (*auto*, never, always).')
[CompletionResult]::new('--pager', 'pager', [CompletionResultType]::ParameterName, 'Determine which pager to use.')
[CompletionResult]::new('-m', 'm', [CompletionResultType]::ParameterName, 'Use the specified syntax for files matching the glob pattern (''*.cpp:C++'').')
[CompletionResult]::new('--map-syntax', 'map-syntax', [CompletionResultType]::ParameterName, 'Use the specified syntax for files matching the glob pattern (''*.cpp:C++'').')
[CompletionResult]::new('--theme', 'theme', [CompletionResultType]::ParameterName, 'Set the color theme for syntax highlighting.')
[CompletionResult]::new('--style', 'style', [CompletionResultType]::ParameterName, 'Comma-separated list of style elements to display (*auto*, full, plain, changes, header, grid, rule, numbers, snip).')
[CompletionResult]::new('-r', 'r', [CompletionResultType]::ParameterName, 'Only print the lines from N to M.')
[CompletionResult]::new('--line-range', 'line-range', [CompletionResultType]::ParameterName, 'Only print the lines from N to M.')
[CompletionResult]::new('-A', 'A', [CompletionResultType]::ParameterName, 'Show non-printable characters (space, tab, newline, ..).')
[CompletionResult]::new('--show-all', 'show-all', [CompletionResultType]::ParameterName, 'Show non-printable characters (space, tab, newline, ..).')
[CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Show plain style (alias for ''--style=plain'').')
[CompletionResult]::new('--plain', 'plain', [CompletionResultType]::ParameterName, 'Show plain style (alias for ''--style=plain'').')
[CompletionResult]::new('-d', 'd', [CompletionResultType]::ParameterName, 'Only show lines that have been added/removed/modified.')
[CompletionResult]::new('--diff', 'diff', [CompletionResultType]::ParameterName, 'Only show lines that have been added/removed/modified.')
[CompletionResult]::new('-n', 'n', [CompletionResultType]::ParameterName, 'Show line numbers (alias for ''--style=numbers'').')
[CompletionResult]::new('--number', 'number', [CompletionResultType]::ParameterName, 'Show line numbers (alias for ''--style=numbers'').')
[CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'f')
[CompletionResult]::new('--force-colorization', 'force-colorization', [CompletionResultType]::ParameterName, 'force-colorization')
[CompletionResult]::new('-P', 'P', [CompletionResultType]::ParameterName, 'Alias for ''--paging=never''')
[CompletionResult]::new('--no-paging', 'no-paging', [CompletionResultType]::ParameterName, 'Alias for ''--paging=never''')
[CompletionResult]::new('--list-themes', 'list-themes', [CompletionResultType]::ParameterName, 'Display all supported highlighting themes.')
[CompletionResult]::new('-L', 'L', [CompletionResultType]::ParameterName, 'Display all supported languages.')
[CompletionResult]::new('--list-languages', 'list-languages', [CompletionResultType]::ParameterName, 'Display all supported languages.')
[CompletionResult]::new('-u', 'u', [CompletionResultType]::ParameterName, 'u')
[CompletionResult]::new('--unbuffered', 'unbuffered', [CompletionResultType]::ParameterName, 'unbuffered')
[CompletionResult]::new('--no-config', 'no-config', [CompletionResultType]::ParameterName, 'Do not use the configuration file')
[CompletionResult]::new('--no-custom-assets', 'no-custom-assets', [CompletionResultType]::ParameterName, 'Do not load custom assets')
[CompletionResult]::new('--config-file', 'config-file', [CompletionResultType]::ParameterName, 'Show path to the configuration file.')
[CompletionResult]::new('--generate-config-file', 'generate-config-file', [CompletionResultType]::ParameterName, 'Generates a default configuration file.')
[CompletionResult]::new('--config-dir', 'config-dir', [CompletionResultType]::ParameterName, 'Show bat''s configuration directory.')
[CompletionResult]::new('--cache-dir', 'cache-dir', [CompletionResultType]::ParameterName, 'Show bat''s cache directory.')
[CompletionResult]::new('--diagnostic', 'diagnostic', [CompletionResultType]::ParameterName, 'Show diagnostic information for bug reports.')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print this help message.')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print this help message.')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Show version information.')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Show version information.')
[CompletionResult]::new('cache', 'cache', [CompletionResultType]::ParameterValue, 'Modify the syntax-definition and theme cache')
break
}
'{{PROJECT_EXECUTABLE}};cache' {
[CompletionResult]::new('--source', 'source', [CompletionResultType]::ParameterName, 'Use a different directory to load syntaxes and themes from.')
[CompletionResult]::new('--target', 'target', [CompletionResultType]::ParameterName, 'Use a different directory to store the cached syntax and theme set.')
[CompletionResult]::new('-b', 'b', [CompletionResultType]::ParameterName, 'Initialize (or update) the syntax/theme cache.')
[CompletionResult]::new('--build', 'build', [CompletionResultType]::ParameterName, 'Initialize (or update) the syntax/theme cache.')
[CompletionResult]::new('-c', 'c', [CompletionResultType]::ParameterName, 'Remove the cached syntax definitions and themes.')
[CompletionResult]::new('--clear', 'clear', [CompletionResultType]::ParameterName, 'Remove the cached syntax definitions and themes.')
[CompletionResult]::new('--blank', 'blank', [CompletionResultType]::ParameterName, 'Create completely new syntax and theme sets (instead of appending to the default sets).')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
break
}
})
$completions.Where{ $_.CompletionText -like "$wordToComplete*" } |
Sort-Object -Property ListItemText
}

View File

@@ -56,7 +56,7 @@ complete -c {{PROJECT_EXECUTABLE}} -s P -d "Disable paging. Alias for '--paging=
complete -c {{PROJECT_EXECUTABLE}} -s A -l show-all -d "Show non-printable characters like space/tab/newline" -n "not __fish_seen_subcommand_from cache"
complete -c {{PROJECT_EXECUTABLE}} -l style -xka "auto full plain changes header grid numbers" -d "Comma-separated list of style elements or presets to display with file contents" -n "not __fish_seen_subcommand_from cache"
complete -c {{PROJECT_EXECUTABLE}} -l style -xka "auto full plain changes header grid rule numbers snip" -d "Comma-separated list of style elements or presets to display with file contents" -n "not __fish_seen_subcommand_from cache"
complete -c {{PROJECT_EXECUTABLE}} -l tabs -x -d "<T> Set the tab width to T spaces (width of 0 passes tabs through directly)" -n "not __fish_seen_subcommand_from cache"

View File

@@ -75,7 +75,7 @@ _{{PROJECT_EXECUTABLE}}_main() {
;;
style)
_values -s , 'style' auto full plain changes header grid numbers snip
_values -s , 'style' auto full plain changes header grid rule numbers snip
;;
esac
}

View File

@@ -50,6 +50,8 @@ highlights lines 30 to 40
highlights lines 1 to 40
.IP "\-\-highlight\-line 40:"
highlights lines 40 to the end of the file
.IP "\-\-highlight\-line 30:+10"
highlights lines 30 to 40
.RE
.HP
\fB\-\-file\-name\fR <name>...
@@ -154,6 +156,8 @@ prints lines 30 to 40
prints lines 1 to 40
.IP "\-\-line\-range 40:"
prints lines 40 to the end of the file
.IP "\-\-line\-range 30:+10"
prints lines 30 to 40
.RE
.HP
\fB\-L\fR, \fB\-\-list\-languages\fR

BIN
assets/minimal_syntaxes.bin vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,52 @@
%YAML 1.2
---
# http://www.sublimetext.com/docs/3/syntax.html
name: Racket
file_extensions:
- rkt
scope: source.racket
contexts:
main:
- match: '[^\\](\"[^\"]*\")'
captures:
1: string.quoted.double.source.racket
- match: '\((define)\s+([a-zA-Z0-9_\-?\+^]+)\s*'
scope: meta.variable.source.racket
captures:
1: keyword.source.racket
2: entity.name.variable.source.racket
- match: '\((define)\s+\(([a-zA-Z0-9_\-?\+^]+)\s*'
scope: meta.function.source.racket
captures:
1: keyword.source.racket
2: entity.name.function
- match: '\((struct)\s+([a-zA-Z0-9_\-?\+^]+)\s+'
scope: meta.struct.source.racket
captures:
1: keyword.source.racket
2: entity.name.type
- match: '[\s\(](if|lambda|cond|define|type-case|let|letrec|let!|\#lang|require|test|else|first|rest|define-type|define-type-alias|define-struct|not|local|error|lang)[\s\)]'
scope: meta.keywords.source.racket
captures:
1: keyword.source.racket
- match: '[\s\(](true|false|empty|null)[\s\)]'
captures:
1: constant.language.source.racket
- match: '[\s\(\[\{](#t|#true|#f|#false)[\s\)\]\}]'
captures:
1: constant.language.source.racket
- match: '(#\\[a-zA-Z0-9_\-?\+\.\!\"]+)'
captures:
1: constant.language.source.racket
- match: '\b(0|([1-9][0-9_]*))\b'
scope: constant.numeric.integer.source.racket
- match: ;
push:
- meta_scope: comment.line.documentation.source.racket
- match: $\n
pop: true
- match: '#\|'
push:
- meta_scope: comment.block.source.racket
- match: '\|#'
pop: true

View File

@@ -64,6 +64,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
"assets/completions/bat.fish.in",
out_dir.join("assets/completions/bat.fish"),
)?;
template(
&variables,
"assets/completions/_bat.ps1.in",
out_dir.join("assets/completions/_bat.ps1"),
)?;
template(
&variables,
"assets/completions/bat.zsh.in",

View File

@@ -1,7 +1,6 @@
<p align="center">
<img src="logo-header.svg" alt="bat - a cat clone with wings"><br>
<a href="https://travis-ci.org/sharkdp/bat"><img src="https://travis-ci.org/sharkdp/bat.svg?branch=master" alt="Build Status"></a>
<a href="https://ci.appveyor.com/project/sharkdp/bat"><img src="https://ci.appveyor.com/api/projects/status/cptsmtbiwbnr2vhf?svg=true"></a>
<a href="https://github.com/sharkdp/bat/actions?query=workflow%3ACICD"><img src="https://github.com/sharkdp/bat/workflows/CICD/badge.svg" alt="Build Status"></a>
<img src="https://img.shields.io/crates/l/bat.svg" alt="license">
<a href="https://crates.io/crates/bat"><img src="https://img.shields.io/crates/v/bat.svg?colorB=319e8c" alt="Version info"></a><br>
シンタックスハイライトとGitとの連携機能付きの <i>cat(1)</i> クローン。
@@ -12,8 +11,12 @@
<a href="#使い方">使い方</a>
<a href="#インストール">インストール</a>
<a href="#カスタマイズ">カスタマイズ</a>
<a href="#プロジェクトの目標と既存の類似したOSS">プロジェクトの目標と既存の類似したOSS</a>
翻訳 [<a href="https://github.com/chinanf-boy/bat-zh">中文</a>][<a href="README-ja.md">日本語</a>][<a href="README-ko.md">한국어</a>]
<a href="#プロジェクトの目標と既存の類似したOSS">プロジェクトの目標と既存の類似したOSS</a><br>
[<a href="../README.md">English</a>]
[<a href="https://github.com/chinanf-boy/bat-zh">中文</a>]
[日本語]
[<a href="README-ko.md">한국어</a>]
[<a href="README-ru.md">Русский</a>]
</p>
### シンタックスハイライト
@@ -202,7 +205,7 @@ ln -s /usr/bin/batcat ~/.local/bin/bat
batの最新リリースを実行する場合、または Ubuntu/Debian の古いバージョンを使用している場合は、[release page](https://github.com/sharkdp/bat/releases) から最新の `.deb` パッケージをダウンロードし、
次の方法でインストールします:
```bash
sudo dpkg -i bat_0.18.2_amd64.deb # adapt version number and architecture
sudo dpkg -i bat_0.18.3_amd64.deb # adapt version number and architecture
```
### On Alpine Linux
@@ -522,7 +525,7 @@ export BAT_CONFIG_PATH="/path/to/bat.conf"
# Use italic text on the terminal (not supported on all terminals)
--italic-text=always
# Use C++ syntax for .ino files
# Use C++ syntax for Arduino .ino files
--map-syntax "*.ino:C++"
# Use ".gitignore"-style highlighting for ".ignore" files

View File

@@ -1,5 +1,5 @@
<p align="center">
<img src="../doc/logo-header.svg" alt="bat - a cat clone with wings"><br>
<img src="logo-header.svg" alt="bat - a cat clone with wings"><br>
<a href="https://github.com/sharkdp/bat/actions?query=workflow%3ACICD"><img src="https://github.com/sharkdp/bat/workflows/CICD/badge.svg" alt="Build Status"></a>
<img src="https://img.shields.io/crates/l/bat.svg" alt="license">
<a href="https://crates.io/crates/bat"><img src="https://img.shields.io/crates/v/bat.svg?colorB=319e8c" alt="Version info"></a><br>
@@ -11,8 +11,12 @@
<a href="#사용법">사용법</a>
<a href="#설치">설치</a>
<a href="#사용자화">사용자화</a>
<a href="#프로젝트-목표와-대안들">프로젝트 목표와 대안들</a>
[<a href="https://github.com/chinanf-boy/bat-zh">中文</a>] [<a href="../doc/README-ja.md">日本語</a>] [<a href="../doc/README-ko.md">한국어</a>] [<a href="../doc/README-ru.md">Русский</a>]
<a href="#프로젝트-목표와-대안들">프로젝트 목표와 대안들</a><br>
[<a href="../README.md">English</a>]
[<a href="https://github.com/chinanf-boy/bat-zh">中文</a>]
[<a href="README-ja.md">日本語</a>]
[한국어]
[<a href="README-ru.md">Русский</a>]
</p>
### 문법 강조
@@ -244,7 +248,7 @@ ln -s /usr/bin/batcat ~/.local/bin/bat
다음과 같이 `.deb` 패키지를 받아 설치하세요:
```bash
sudo dpkg -i bat_0.18.2_amd64.deb # adapt version number and architecture
sudo dpkg -i bat_0.18.3_amd64.deb # adapt version number and architecture
```
### Alpine Linux에서
@@ -279,6 +283,14 @@ pacman -S bat
dnf install bat
```
### Funtoo Linux에서
dev-kit을 통해 [`bat` 패키지](https://github.com/funtoo/dev-kit/tree/1.4-release/sys-apps/bat)를 설치할 수 있습니다:
```bash
emerge sys-apps/bat
```
### Gentoo Linux에서
공식 소스를 통해
@@ -404,7 +416,7 @@ scoop install bat
### 소스에서
`bat`의 소스를 빌드하기 위해서는, Rust 1.45 이상이 필요합니다.
`bat`의 소스를 빌드하기 위해서는, Rust 1.46 이상이 필요합니다.
`cargo`를 이용해 전부 빌드할 수 있습니다:
```bash
@@ -663,7 +675,7 @@ bat --generate-config-file
# 터미널에서 이탤릭체 쓰기 (일부 터미널에서 미지원)
--italic-text=always
# .ino 파일에 C++ 문법 쓰기
# Arduino .ino 파일에 C++ 문법 쓰기
--map-syntax "*.ino:C++"
```
@@ -797,6 +809,10 @@ cargo install --locked --force
- [keith-hall](https://github.com/keith-hall)
- [Enselic](https://github.com/Enselic)
## 보안 취약점
만약 `bat`의 취약점을 발견하였다면, [David Peter](https://david-peter.de/)에게 메일로 연락주시기 바랍니다.
## 프로젝트 목표와 대안들
`bat`은 다음과 같은 목표를 달성하려고 합니다:

View File

@@ -1,7 +1,6 @@
<p align="center">
<img src="doc/logo-header.svg" alt="bat - a cat clone with wings"><br>
<a href="https://travis-ci.org/sharkdp/bat"><img src="https://travis-ci.org/sharkdp/bat.svg?branch=master" alt="Build Status"></a>
<a href="https://ci.appveyor.com/project/sharkdp/bat"><img src="https://ci.appveyor.com/api/projects/status/cptsmtbiwbnr2vhf/branch/master?svg=true"></a>
<img src="logo-header.svg" alt="bat - a cat clone with wings"><br>
<a href="https://github.com/sharkdp/bat/actions?query=workflow%3ACICD"><img src="https://github.com/sharkdp/bat/workflows/CICD/badge.svg" alt="Build Status"></a>
<img src="https://img.shields.io/crates/l/bat.svg" alt="license">
<a href="https://crates.io/crates/bat"><img src="https://img.shields.io/crates/v/bat.svg?colorB=319e8c" alt="Version info"></a><br>
Клон утилиты <i>cat(1)</i> с поддержкой выделения синтаксиса и Git
@@ -12,8 +11,12 @@
<a href="#как-использовать">Использование</a>
<a href="#установка">Установка</a>
<a href="#кастомизация">Кастомизация</a>
<a href="#цели-и-альтернативы">Цели и альтернативы </a>
Перевод [<a href="https://github.com/chinanf-boy/bat-zh">中文</a>][<a href="doc/README-ja.md">日本語</a>][<a href="doc/README-ko.md">한국어</a>][<a href="doc/README-ru.md">Русский</a>]
<a href="#цели-и-альтернативы">Цели и альтернативы </a><br>
[<a href="../README.md">English]
[<a href="https://github.com/chinanf-boy/bat-zh">中文</a>]
[<a href="README-ja.md">日本語</a>]
[<a href="README-ko.md">한국어</a>]
[Русский]
</p>
### Выделение синтаксиса
@@ -185,7 +188,7 @@ ln -s /usr/bin/batcat ~/.local/bin/bat
[release page](https://github.com/sharkdp/bat/releases) и установить так:
```bash
sudo dpkg -i bat_0.18.2_amd64.deb # измените архитектуру и версию
sudo dpkg -i bat_0.18.3_amd64.deb # измените архитектуру и версию
```
### Alpine Linux
@@ -341,7 +344,7 @@ ansible-galaxy install aeimer.install_bat
### Из исходников
Если вы желаете установить `bat` из исходников, вам понадобится Rust 1.45 или выше. После этого используйте `cargo`, чтобы все скомпилировать:
Если вы желаете установить `bat` из исходников, вам понадобится Rust 1.46 или выше. После этого используйте `cargo`, чтобы все скомпилировать:
```bash
cargo install --locked bat
@@ -481,7 +484,7 @@ bat --generate-config-file
# Использовать курсив (поддерживается не всеми терминалами)
--italic-text=always
# Использовать синтаксис C++ для всех .ino файлов
# Использовать синтаксис C++ для всех Arduino .ino файлов
--map-syntax "*.ino:C++"
# Использовать синтаксик Git Ignore для всех файлов .ignore

View File

@@ -45,7 +45,7 @@ See this page for a good overview: https://deps.rs/repo/github/sharkdp/bat
## Release
- [ ] Create a tag and push it to the remote `git tag vX.Y.Z; git push --tags`.
- [ ] Create a tag and push it: `git tag vX.Y.Z; git push origin tag vX.Y.Z`.
This will trigger the deployment via GitHub Actions.
- [ ] Go to https://github.com/sharkdp/bat/releases/new to create the new
release. Select the new tag and also use it as the release title. For the

View File

@@ -1,10 +1,9 @@
use std::ffi::OsStr;
use std::fs;
use std::path::{Path, PathBuf};
use std::path::Path;
use lazycell::LazyCell;
use syntect::dumps::{from_binary, from_reader};
use syntect::highlighting::{Theme, ThemeSet};
use syntect::parsing::{SyntaxReference, SyntaxSet};
@@ -12,13 +11,30 @@ use path_abs::PathAbs;
use crate::bat_warning;
use crate::error::*;
use crate::input::{InputReader, OpenedInput, OpenedInputKind};
use crate::input::{InputReader, OpenedInput};
use crate::syntax_mapping::{MappingTarget, SyntaxMapping};
use ignored_suffixes::*;
use minimal_assets::*;
use serialized_syntax_set::*;
#[cfg(feature = "build-assets")]
pub use crate::assets::build_assets::*;
pub(crate) mod assets_metadata;
#[cfg(feature = "build-assets")]
mod build_assets;
mod ignored_suffixes;
mod minimal_assets;
mod serialized_syntax_set;
#[derive(Debug)]
pub struct HighlightingAssets {
syntax_set_cell: LazyCell<SyntaxSet>,
serialized_syntax_set: Option<SerializedSyntaxSet>,
serialized_syntax_set: SerializedSyntaxSet,
minimal_assets: MinimalAssets,
theme_set: ThemeSet,
fallback_theme: Option<&'static str>,
}
@@ -29,39 +45,33 @@ pub struct SyntaxReferenceInSet<'a> {
pub syntax_set: &'a SyntaxSet,
}
const IGNORED_SUFFIXES: [&str; 10] = [
// Editor etc backups
"~",
".bak",
".old",
".orig",
// Debian and derivatives apt/dpkg backups
".dpkg-dist",
".dpkg-old",
// Red Hat and derivatives rpm backups
".rpmnew",
".rpmorig",
".rpmsave",
// Build system input/template files
".in",
];
/// Compress for size of ~700 kB instead of ~4600 kB at the cost of ~30% longer deserialization time
pub(crate) const COMPRESS_SYNTAXES: bool = true;
/// Compress for size of ~20 kB instead of ~200 kB at the cost of ~30% longer deserialization time
pub(crate) const COMPRESS_THEMES: bool = true;
/// Compress for size of ~400 kB instead of ~2100 kB at the cost of ~30% longer deserialization time
pub(crate) const COMPRESS_SERIALIZED_MINIMAL_SYNTAXES: bool = true;
/// Whether or not to compress the serialized form of [MinimalSyntaxes]. Shall
/// always be `false`, because the data in
/// [MinimalSyntaxes.serialized_syntax_sets] has already been compressed
/// (assuming [COMPRESS_SERIALIZED_MINIMAL_SYNTAXES] is `true`). The "outer" data
/// structures like `by_name` are tiny. If we compress, deserialization can't do
/// efficient byte-by-byte copy of `serialized_syntax_sets`.
pub(crate) const COMPRESS_MINIMAL_SYNTAXES: bool = false;
impl HighlightingAssets {
fn new(
syntax_set: Option<SyntaxSet>,
serialized_syntax_set: Option<SerializedSyntaxSet>,
serialized_syntax_set: SerializedSyntaxSet,
minimal_syntaxes: MinimalSyntaxes,
theme_set: ThemeSet,
) -> Self {
assert!(syntax_set.is_some() || serialized_syntax_set.is_some());
let syntax_set_cell = LazyCell::new();
if let Some(syntax_set) = syntax_set {
syntax_set_cell.fill(syntax_set).expect("can never fail");
}
HighlightingAssets {
syntax_set_cell,
syntax_set_cell: LazyCell::new(),
serialized_syntax_set,
minimal_assets: MinimalAssets::new(minimal_syntaxes),
theme_set,
fallback_theme: None,
}
@@ -71,127 +81,33 @@ impl HighlightingAssets {
"Monokai Extended"
}
#[cfg(feature = "build-assets")]
pub fn from_files(source_dir: &Path, include_integrated_assets: bool) -> Result<Self> {
let mut theme_set = if include_integrated_assets {
get_integrated_themeset()
} else {
ThemeSet::new()
};
let theme_dir = source_dir.join("themes");
if theme_dir.exists() {
let res = theme_set.add_from_folder(&theme_dir);
if let Err(err) = res {
println!(
"Failed to load one or more themes from '{}' (reason: '{}')",
theme_dir.to_string_lossy(),
err,
);
}
} else {
println!(
"No themes were found in '{}', using the default set",
theme_dir.to_string_lossy()
);
}
let mut syntax_set_builder = if !include_integrated_assets {
let mut builder = syntect::parsing::SyntaxSetBuilder::new();
builder.add_plain_text_syntax();
builder
} else {
from_binary::<SyntaxSet>(get_serialized_integrated_syntaxset()).into_builder()
};
let syntax_dir = source_dir.join("syntaxes");
if syntax_dir.exists() {
syntax_set_builder.add_from_folder(syntax_dir, true)?;
} else {
println!(
"No syntaxes were found in '{}', using the default set.",
syntax_dir.to_string_lossy()
);
}
if std::env::var("BAT_PRINT_SYNTAX_DEPENDENCIES").is_ok() {
// To trigger this code, run:
// BAT_PRINT_SYNTAX_DEPENDENCIES=1 cargo run -- cache --build --source assets --blank --target /tmp
crate::syntax_dependencies::print_syntax_dependencies(&syntax_set_builder);
}
let syntax_set = syntax_set_builder.build();
let missing_contexts = syntax_set.find_unlinked_contexts();
if !missing_contexts.is_empty() {
println!("Some referenced contexts could not be found!");
for context in missing_contexts {
println!("- {}", context);
}
}
Ok(HighlightingAssets::new(Some(syntax_set), None, theme_set))
}
pub fn from_cache(cache_path: &Path) -> Result<Self> {
Ok(HighlightingAssets::new(
None,
Some(SerializedSyntaxSet::FromFile(
cache_path.join("syntaxes.bin"),
)),
asset_from_cache(&cache_path.join("themes.bin"), "theme set")?,
SerializedSyntaxSet::FromFile(cache_path.join("syntaxes.bin")),
asset_from_cache(
&cache_path.join("minimal_syntaxes.bin"),
"minimal syntax sets",
COMPRESS_MINIMAL_SYNTAXES,
)?,
asset_from_cache(&cache_path.join("themes.bin"), "theme set", COMPRESS_THEMES)?,
))
}
pub fn from_binary() -> Self {
HighlightingAssets::new(
None,
Some(SerializedSyntaxSet::FromBinary(
get_serialized_integrated_syntaxset(),
)),
SerializedSyntaxSet::FromBinary(get_serialized_integrated_syntaxset()),
get_integrated_minimal_syntaxes(),
get_integrated_themeset(),
)
}
#[cfg(feature = "build-assets")]
pub fn save_to_cache(&self, target_dir: &Path, current_version: &str) -> Result<()> {
let _ = fs::create_dir_all(target_dir);
asset_to_cache(
self.get_theme_set(),
&target_dir.join("themes.bin"),
"theme set",
)?;
asset_to_cache(
self.get_syntax_set()?,
&target_dir.join("syntaxes.bin"),
"syntax set",
)?;
print!(
"Writing metadata to folder {} ... ",
target_dir.to_string_lossy()
);
crate::assets_metadata::AssetsMetadata::new(current_version).save_to_folder(target_dir)?;
println!("okay");
Ok(())
}
pub fn set_fallback_theme(&mut self, theme: &'static str) {
self.fallback_theme = Some(theme);
}
pub(crate) fn get_syntax_set(&self) -> Result<&SyntaxSet> {
if !self.syntax_set_cell.filled() {
self.syntax_set_cell.fill(
self.serialized_syntax_set
.as_ref()
.expect("a dev forgot to setup serialized_syntax_set, please report to https://github.com/sharkdp/bat/issues")
.deserialize()?
).unwrap();
}
// It is safe to .unwrap() because we just made sure it was .filled()
Ok(self.syntax_set_cell.borrow().unwrap())
self.syntax_set_cell
.try_borrow_with(|| self.serialized_syntax_set.deserialize())
}
/// Use [Self::get_syntaxes] instead
@@ -214,36 +130,68 @@ impl HighlightingAssets {
self.get_theme_set().themes.keys().map(|s| s.as_ref())
}
/// Use [Self::get_syntax_for_file_name] instead
/// Finds a [SyntaxSet] that contains a [SyntaxReference] by its name. First
/// tries to find a minimal [SyntaxSet]. If none is found, returns the
/// [SyntaxSet] that contains all syntaxes.
fn get_syntax_set_by_name(&self, name: &str) -> Result<&SyntaxSet> {
match self.minimal_assets.get_syntax_set_by_name(name) {
Some(syntax_set) => Ok(syntax_set),
None => self.get_syntax_set(),
}
}
/// Use [Self::get_syntax_for_path] instead
#[deprecated]
pub fn syntax_for_file_name(
&self,
file_name: impl AsRef<Path>,
mapping: &SyntaxMapping,
) -> Option<&SyntaxReference> {
self.get_syntax_for_file_name(file_name, mapping)
.expect(
".syntax_for_file_name() is deprecated, use .get_syntax_for_file_name() instead",
)
self.get_syntax_for_path(file_name, mapping)
.ok()
.map(|syntax_in_set| syntax_in_set.syntax)
}
pub fn get_syntax_for_file_name(
/// Detect the syntax based on, in order:
/// 1. Syntax mappings (e.g. `/etc/profile` -> `Bourne Again Shell (bash)`)
/// 2. The file name (e.g. `Dockerfile`)
/// 3. The file name extension (e.g. `.rs`)
///
/// When detecting syntax based on syntax mappings, the full path is taken
/// into account. When detecting syntax based on file name, no regard is
/// taken to the path of the file. Only the file name itself matters. When
/// detecting syntax based on file name extension, only the file name
/// extension itself matters.
///
/// Returns [Error::UndetectedSyntax] if it was not possible detect syntax
/// based on path/file name/extension (or if the path was mapped to
/// [MappingTarget::MapToUnknown]). In this case it is appropriate to fall
/// back to other methods to detect syntax. Such as using the contents of
/// the first line of the file.
///
/// Returns [Error::UnknownSyntax] if a syntax mapping exist, but the mapped
/// syntax does not exist.
pub fn get_syntax_for_path(
&self,
file_name: impl AsRef<Path>,
path: impl AsRef<Path>,
mapping: &SyntaxMapping,
) -> Result<Option<SyntaxReferenceInSet>> {
let file_name = file_name.as_ref();
Ok(match mapping.get_syntax_for(file_name) {
Some(MappingTarget::MapToUnknown) => None,
Some(MappingTarget::MapTo(syntax_name)) => {
let syntax_set = self.get_syntax_set()?;
syntax_set
.find_syntax_by_name(syntax_name)
.map(|syntax| SyntaxReferenceInSet { syntax, syntax_set })
) -> Result<SyntaxReferenceInSet> {
let path = path.as_ref();
match mapping.get_syntax_for(path) {
Some(MappingTarget::MapToUnknown) => {
Err(Error::UndetectedSyntax(path.to_string_lossy().into()))
}
None => self.get_extension_syntax(file_name.as_os_str())?,
})
Some(MappingTarget::MapTo(syntax_name)) => self
.find_syntax_by_name(syntax_name)?
.ok_or_else(|| Error::UnknownSyntax(syntax_name.to_owned())),
None => {
let file_name = path.file_name().unwrap_or_default();
self.get_extension_syntax(file_name)?
.ok_or_else(|| Error::UndetectedSyntax(path.to_string_lossy().into()))
}
}
}
pub(crate) fn get_theme(&self, theme: &str) -> &Theme {
@@ -270,113 +218,60 @@ impl HighlightingAssets {
mapping: &SyntaxMapping,
) -> Result<SyntaxReferenceInSet> {
if let Some(language) = language {
let syntax_set = self.get_syntax_set()?;
syntax_set
let syntax_set = self.get_syntax_set_by_name(language)?;
return syntax_set
.find_syntax_by_token(language)
.map(|syntax| SyntaxReferenceInSet { syntax, syntax_set })
.ok_or_else(|| ErrorKind::UnknownSyntax(language.to_owned()).into())
} else {
let line_syntax = self.get_first_line_syntax(&mut input.reader)?;
// Get the path of the file:
// If this was set by the metadata, that will take priority.
// If it wasn't, it will use the real file path (if available).
let path_str =
input
.metadata
.user_provided_name
.as_ref()
.or_else(|| match input.kind {
OpenedInputKind::OrdinaryFile(ref path) => Some(path),
_ => None,
});
if let Some(path_str) = path_str {
// If a path was provided, we try and detect the syntax based on extension mappings.
let path = Path::new(path_str);
let absolute_path = PathAbs::new(path)
.ok()
.map(|p| p.as_path().to_path_buf())
.unwrap_or_else(|| path.to_owned());
match mapping.get_syntax_for(absolute_path) {
Some(MappingTarget::MapToUnknown) => line_syntax.ok_or_else(|| {
ErrorKind::UndetectedSyntax(path.to_string_lossy().into()).into()
}),
Some(MappingTarget::MapTo(syntax_name)) => {
let syntax_set = self.get_syntax_set()?;
syntax_set
.find_syntax_by_name(syntax_name)
.map(|syntax| SyntaxReferenceInSet { syntax, syntax_set })
.ok_or_else(|| ErrorKind::UnknownSyntax(syntax_name.to_owned()).into())
}
None => {
let file_name = path.file_name().unwrap_or_default();
self.get_extension_syntax(file_name)?
.or(line_syntax)
.ok_or_else(|| {
ErrorKind::UndetectedSyntax(path.to_string_lossy().into()).into()
})
}
}
} else {
// If a path wasn't provided, we fall back to the detect first-line syntax.
line_syntax.ok_or_else(|| ErrorKind::UndetectedSyntax("[unknown]".into()).into())
}
.ok_or_else(|| Error::UnknownSyntax(language.to_owned()));
}
let path = input.path();
let path_syntax = if let Some(path) = path {
self.get_syntax_for_path(
PathAbs::new(path).map_or_else(|_| path.to_owned(), |p| p.as_path().to_path_buf()),
mapping,
)
} else {
Err(Error::UndetectedSyntax("[unknown]".into()))
};
match path_syntax {
// If a path wasn't provided, or if path based syntax detection
// above failed, we fall back to first-line syntax detection.
Err(Error::UndetectedSyntax(path)) => self
.get_first_line_syntax(&mut input.reader)?
.ok_or(Error::UndetectedSyntax(path)),
_ => path_syntax,
}
}
pub(crate) fn find_syntax_by_name(
&self,
syntax_name: &str,
) -> Result<Option<SyntaxReferenceInSet>> {
let syntax_set = self.get_syntax_set()?;
Ok(syntax_set
.find_syntax_by_name(syntax_name)
.map(|syntax| SyntaxReferenceInSet { syntax, syntax_set }))
}
fn find_syntax_by_extension(&self, e: Option<&OsStr>) -> Result<Option<SyntaxReferenceInSet>> {
let syntax_set = self.get_syntax_set()?;
let extension = e.and_then(|x| x.to_str()).unwrap_or_default();
Ok(syntax_set
.find_syntax_by_extension(extension)
.map(|syntax| SyntaxReferenceInSet { syntax, syntax_set }))
}
fn get_extension_syntax(&self, file_name: &OsStr) -> Result<Option<SyntaxReferenceInSet>> {
let mut syntax = self.find_syntax_by_file_name(file_name)?;
let mut syntax = self.find_syntax_by_extension(Some(file_name))?;
if syntax.is_none() {
syntax = self.find_syntax_by_file_name_extension(file_name)?;
syntax = self.find_syntax_by_extension(Path::new(file_name).extension())?;
}
if syntax.is_none() {
syntax = self.get_extension_syntax_with_stripped_suffix(file_name)?;
}
Ok(syntax)
}
fn find_syntax_by_file_name(&self, file_name: &OsStr) -> Result<Option<SyntaxReferenceInSet>> {
let syntax_set = self.get_syntax_set()?;
Ok(syntax_set
.find_syntax_by_extension(file_name.to_str().unwrap_or_default())
.map(|syntax| SyntaxReferenceInSet { syntax, syntax_set }))
}
fn find_syntax_by_file_name_extension(
&self,
file_name: &OsStr,
) -> Result<Option<SyntaxReferenceInSet>> {
let file_path = Path::new(file_name);
let syntax_set = self.get_syntax_set()?;
Ok(syntax_set
.find_syntax_by_extension(
file_path
.extension()
.and_then(|x| x.to_str())
.unwrap_or_default(),
)
.map(|syntax| SyntaxReferenceInSet { syntax, syntax_set }))
}
/// If we find an ignored suffix on the file name, e.g. '~', we strip it and
/// then try again to find a syntax without it. Note that we do this recursively.
fn get_extension_syntax_with_stripped_suffix(
&self,
file_name: &OsStr,
) -> Result<Option<SyntaxReferenceInSet>> {
let file_path = Path::new(file_name);
let mut syntax = None;
if let Some(file_str) = file_path.to_str() {
for suffix in IGNORED_SUFFIXES.iter() {
if let Some(stripped_filename) = file_str.strip_suffix(suffix) {
syntax = self.get_extension_syntax(OsStr::new(stripped_filename))?;
break;
}
}
syntax = try_with_stripped_suffix(file_name, |stripped_file_name| {
self.get_extension_syntax(stripped_file_name) // Note: recursion
})?;
}
Ok(syntax)
}
@@ -393,57 +288,53 @@ impl HighlightingAssets {
}
}
/// A SyntaxSet in serialized form, i.e. bincoded and flate2 compressed.
/// We keep it in this format since we want to load it lazily.
#[derive(Debug)]
enum SerializedSyntaxSet {
/// The data comes from a user-generated cache file.
FromFile(PathBuf),
/// The data to use is embedded into the bat binary.
FromBinary(&'static [u8]),
}
impl SerializedSyntaxSet {
fn deserialize(&self) -> Result<SyntaxSet> {
match self {
SerializedSyntaxSet::FromBinary(data) => Ok(from_binary(data)),
SerializedSyntaxSet::FromFile(ref path) => asset_from_cache(path, "syntax set"),
}
}
}
fn get_serialized_integrated_syntaxset() -> &'static [u8] {
pub(crate) fn get_serialized_integrated_syntaxset() -> &'static [u8] {
include_bytes!("../assets/syntaxes.bin")
}
fn get_integrated_themeset() -> ThemeSet {
from_binary(include_bytes!("../assets/themes.bin"))
pub(crate) fn get_integrated_themeset() -> ThemeSet {
from_binary(include_bytes!("../assets/themes.bin"), COMPRESS_THEMES)
}
#[cfg(feature = "build-assets")]
fn asset_to_cache<T: serde::Serialize>(asset: &T, path: &Path, description: &str) -> Result<()> {
print!("Writing {} to {} ... ", description, path.to_string_lossy());
syntect::dumps::dump_to_file(asset, &path).chain_err(|| {
format!(
"Could not save {} to {}",
description,
path.to_string_lossy()
)
})?;
println!("okay");
Ok(())
fn get_integrated_minimal_syntaxes() -> MinimalSyntaxes {
from_binary(
include_bytes!("../assets/minimal_syntaxes.bin"),
COMPRESS_MINIMAL_SYNTAXES,
)
}
fn asset_from_cache<T: serde::de::DeserializeOwned>(path: &Path, description: &str) -> Result<T> {
let contents = fs::read(path).chain_err(|| {
pub(crate) fn from_binary<T: serde::de::DeserializeOwned>(v: &[u8], compressed: bool) -> T {
asset_from_contents(v, "n/a", compressed)
.expect("data integrated in binary is never faulty, but make sure `compressed` is in sync!")
}
fn asset_from_contents<T: serde::de::DeserializeOwned>(
contents: &[u8],
description: &str,
compressed: bool,
) -> Result<T> {
if compressed {
bincode::deserialize_from(flate2::read::ZlibDecoder::new(contents))
} else {
bincode::deserialize_from(contents)
}
.map_err(|_| format!("Could not parse {}", description).into())
}
fn asset_from_cache<T: serde::de::DeserializeOwned>(
path: &Path,
description: &str,
compressed: bool,
) -> Result<T> {
let contents = fs::read(path).map_err(|_| {
format!(
"Could not load cached {} '{}'",
description,
path.to_string_lossy()
)
})?;
from_reader(&contents[..]).chain_err(|| format!("Could not parse cached {}", description))
asset_from_contents(&contents[..], description, compressed)
.map_err(|_| format!("Could not parse cached {}", description).into())
}
#[cfg(test)]

View File

@@ -52,16 +52,15 @@ impl AssetsMetadata {
pub fn load_from_folder(path: &Path) -> Result<Option<Self>> {
match Self::try_load_from_folder(path) {
Ok(metadata) => Ok(Some(metadata)),
Err(e) => match e.kind() {
ErrorKind::SerdeYamlError(_) => Err(e),
_ => {
if path.join("syntaxes.bin").exists() || path.join("themes.bin").exists() {
Ok(Some(Self::default()))
} else {
Ok(None)
}
Err(e) => {
if let Error::SerdeYamlError(_) = e {
Err(e)
} else if path.join("syntaxes.bin").exists() || path.join("themes.bin").exists() {
Ok(Some(Self::default()))
} else {
Ok(None)
}
},
}
}
}

500
src/assets/build_assets.rs Normal file
View File

@@ -0,0 +1,500 @@
use std::collections::HashMap;
use std::path::Path;
use syntect::highlighting::ThemeSet;
use syntect::parsing::syntax_definition::{
ContextReference, MatchOperation, MatchPattern, Pattern, SyntaxDefinition,
};
use syntect::parsing::{Scope, SyntaxSet, SyntaxSetBuilder};
use crate::assets::*;
mod graphviz_utils;
type SyntaxName = String;
/// Used to look up which [SyntaxDefinition] corresponds to a given [OtherSyntax]
type OtherSyntaxLookup<'a> = HashMap<OtherSyntax, &'a SyntaxDefinition>;
/// Used to look up what dependencies a given [SyntaxDefinition] has
type SyntaxToDependencies = HashMap<SyntaxName, Vec<OtherSyntax>>;
/// Used to look up what other [SyntaxDefinition]s depend on a given [SyntaxDefinition]
type SyntaxToDependents<'a> = HashMap<SyntaxName, Vec<OtherSyntax>>;
/// Represents some other `*.sublime-syntax` file, i.e. another [SyntaxDefinition].
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Clone, Hash)]
pub(crate) enum OtherSyntax {
/// By name. Example YAML: `include: C.sublime-syntax` (name is `"C"`)
ByName(String),
/// By scope. Example YAML: `embed: scope:source.c` (scope is `"source.c"`)
ByScope(Scope),
}
pub fn build(
source_dir: &Path,
include_integrated_assets: bool,
target_dir: &Path,
current_version: &str,
) -> Result<()> {
let theme_set = build_theme_set(source_dir, include_integrated_assets);
let syntax_set_builder = build_syntax_set_builder(source_dir, include_integrated_assets)?;
let minimal_syntaxes = build_minimal_syntaxes(&syntax_set_builder, include_integrated_assets)?;
let syntax_set = syntax_set_builder.build();
print_unlinked_contexts(&syntax_set);
write_assets(
&theme_set,
&syntax_set,
&minimal_syntaxes,
target_dir,
current_version,
)
}
fn build_theme_set(source_dir: &Path, include_integrated_assets: bool) -> ThemeSet {
let mut theme_set = if include_integrated_assets {
crate::assets::get_integrated_themeset()
} else {
ThemeSet::new()
};
let theme_dir = source_dir.join("themes");
if theme_dir.exists() {
let res = theme_set.add_from_folder(&theme_dir);
if let Err(err) = res {
println!(
"Failed to load one or more themes from '{}' (reason: '{}')",
theme_dir.to_string_lossy(),
err,
);
}
} else {
println!(
"No themes were found in '{}', using the default set",
theme_dir.to_string_lossy()
);
}
theme_set
}
fn build_syntax_set_builder(
source_dir: &Path,
include_integrated_assets: bool,
) -> Result<SyntaxSetBuilder> {
let mut syntax_set_builder = if !include_integrated_assets {
let mut builder = syntect::parsing::SyntaxSetBuilder::new();
builder.add_plain_text_syntax();
builder
} else {
from_binary::<SyntaxSet>(get_serialized_integrated_syntaxset(), COMPRESS_SYNTAXES)
.into_builder()
};
let syntax_dir = source_dir.join("syntaxes");
if syntax_dir.exists() {
syntax_set_builder.add_from_folder(syntax_dir, true)?;
} else {
println!(
"No syntaxes were found in '{}', using the default set.",
syntax_dir.to_string_lossy()
);
}
Ok(syntax_set_builder)
}
fn print_unlinked_contexts(syntax_set: &SyntaxSet) {
let missing_contexts = syntax_set.find_unlinked_contexts();
if !missing_contexts.is_empty() {
println!("Some referenced contexts could not be found!");
for context in missing_contexts {
println!("- {}", context);
}
}
}
fn write_assets(
theme_set: &ThemeSet,
syntax_set: &SyntaxSet,
minimal_syntaxes: &MinimalSyntaxes,
target_dir: &Path,
current_version: &str,
) -> Result<()> {
let _ = std::fs::create_dir_all(target_dir);
asset_to_cache(
theme_set,
&target_dir.join("themes.bin"),
"theme set",
COMPRESS_THEMES,
)?;
asset_to_cache(
syntax_set,
&target_dir.join("syntaxes.bin"),
"syntax set",
COMPRESS_SYNTAXES,
)?;
asset_to_cache(
minimal_syntaxes,
&target_dir.join("minimal_syntaxes.bin"),
"minimal syntax sets",
COMPRESS_MINIMAL_SYNTAXES,
)?;
print!(
"Writing metadata to folder {} ... ",
target_dir.to_string_lossy()
);
crate::assets_metadata::AssetsMetadata::new(current_version).save_to_folder(target_dir)?;
println!("okay");
Ok(())
}
fn print_syntax_set_names(syntax_set: &SyntaxSet) {
let names = syntax_set
.syntaxes()
.iter()
.map(|syntax| &syntax.name)
.collect::<Vec<_>>();
println!("{:?}", names);
}
fn build_minimal_syntaxes(
syntax_set_builder: &'_ SyntaxSetBuilder,
include_integrated_assets: bool,
) -> Result<MinimalSyntaxes> {
let mut minimal_syntaxes = MinimalSyntaxes {
by_name: HashMap::new(),
serialized_syntax_sets: vec![],
};
if include_integrated_assets {
// Dependency info is not present in integrated assets, so we can't
// calculate minimal syntax sets. Return early without any data filled
// in. This means that no minimal syntax sets will be available to use, and
// the full, slow-to-deserialize, fallback syntax set will be used instead.
return Ok(minimal_syntaxes);
}
let minimal_syntax_sets_to_serialize = build_minimal_syntax_sets(syntax_set_builder)
// For now, only store syntax sets with one syntax, otherwise
// the binary grows by several megs
.filter(|syntax_set| syntax_set.syntaxes().len() == 1);
for minimal_syntax_set in minimal_syntax_sets_to_serialize {
// Remember what index it is found at
let current_index = minimal_syntaxes.serialized_syntax_sets.len();
for syntax in minimal_syntax_set.syntaxes() {
minimal_syntaxes
.by_name
.insert(syntax.name.to_ascii_lowercase().clone(), current_index);
}
let serialized_syntax_set = asset_to_contents(
&minimal_syntax_set,
&format!("failed to serialize minimal syntax set {}", current_index),
COMPRESS_SERIALIZED_MINIMAL_SYNTAXES,
)?;
// Add last so that it ends up at `current_index`
minimal_syntaxes
.serialized_syntax_sets
.push(serialized_syntax_set);
}
Ok(minimal_syntaxes)
}
/// Analyzes dependencies between syntaxes in a [SyntaxSetBuilder].
/// From that, it builds minimal [SyntaxSet]s.
fn build_minimal_syntax_sets(
syntax_set_builder: &'_ SyntaxSetBuilder,
) -> impl Iterator<Item = SyntaxSet> + '_ {
let syntaxes = syntax_set_builder.syntaxes();
// Build the data structures we need for dependency resolution
let (other_syntax_lookup, syntax_to_dependencies, syntax_to_dependents) =
generate_maps(syntaxes);
maybe_write_syntax_dependencies_to_graphviz_dot_file(
&other_syntax_lookup,
&syntax_to_dependencies,
);
// Create one minimal SyntaxSet from each (non-hidden) SyntaxDefinition
syntaxes.iter().filter_map(move |syntax| {
if syntax.hidden {
return None;
}
let mut builder = SyntaxSetDependencyBuilder::new();
builder.add_with_dependencies(
syntax,
&other_syntax_lookup,
&syntax_to_dependencies,
&syntax_to_dependents,
);
let syntax_set = builder.build();
if std::env::var("BAT_PRINT_SYNTAX_DEPENDENCIES").is_ok() {
// To trigger this code, run:
// BAT_PRINT_SYNTAX_DEPENDENCIES=1 cargo run -- cache --build --source assets --blank --target /tmp
print_syntax_set_names(&syntax_set);
}
Some(syntax_set)
})
}
/// In order to analyze dependencies, we need three key pieces of data.
///
/// * When we have a [OtherSyntax], we need to know what [SyntaxDefinition]
/// that corresponds to
/// * When we have a [SyntaxDefinition], we need to know what dependencies it
/// has
/// * When we have a [SyntaxDefinition], we need to know what other syntaxes
/// depend on it
///
/// This functions generates that data for each syntax.
fn generate_maps(
syntaxes: &[SyntaxDefinition],
) -> (OtherSyntaxLookup, SyntaxToDependencies, SyntaxToDependents) {
let mut other_syntax_lookup = HashMap::new();
let mut syntax_to_dependencies = HashMap::new();
let mut syntax_to_dependents = HashMap::new();
for syntax in syntaxes {
other_syntax_lookup.insert(OtherSyntax::ByName(syntax.name.clone()), syntax);
other_syntax_lookup.insert(OtherSyntax::ByScope(syntax.scope), syntax);
}
for syntax in syntaxes {
let dependencies = dependencies_for_syntax(syntax);
for dependency in &dependencies {
if let Some(dependency) = other_syntax_lookup.get(dependency) {
syntax_to_dependents
.entry(dependency.name.clone())
.or_insert_with(Vec::new)
.push(OtherSyntax::ByName(syntax.name.clone()));
}
}
syntax_to_dependencies.insert(syntax.name.clone(), dependencies);
}
(
other_syntax_lookup,
syntax_to_dependencies,
syntax_to_dependents,
)
}
/// Gets what external dependencies a given [SyntaxDefinition] has.
/// An external dependency is another `.sublime-syntax` file.
/// It does that by looking for variants of the following YAML patterns:
/// - `include: C.sublime-syntax`
/// - `embed: scope:source.c`
fn dependencies_for_syntax(syntax: &SyntaxDefinition) -> Vec<OtherSyntax> {
let mut dependencies: Vec<OtherSyntax> = syntax
.contexts
.values()
.flat_map(|context| &context.patterns)
.flat_map(dependencies_from_pattern)
.collect();
// No need to track a dependency more than once
dependencies.sort();
dependencies.dedup();
dependencies
}
fn dependencies_from_pattern(pattern: &Pattern) -> Vec<OtherSyntax> {
match *pattern {
Pattern::Match(MatchPattern {
operation: MatchOperation::Push(ref context_references),
..
}) => context_references
.iter()
.map(dependency_from_context_reference)
.collect(),
Pattern::Include(ref context_reference) => {
vec![dependency_from_context_reference(context_reference)]
}
_ => vec![],
}
.into_iter()
.flatten()
.collect()
}
/// To generate a Graphviz dot file of syntax dependencies, do this:
/// ```bash
/// sudo apt install graphviz
/// BAT_SYNTAX_DEPENDENCIES_TO_GRAPHVIZ_DOT_FILE=/tmp/bat-syntax-dependencies.dot cargo run -- cache --build --source assets --blank --target /tmp
/// dot /tmp/bat-syntax-dependencies.dot -Tpng -o /tmp/bat-syntax-dependencies.png
/// open /tmp/bat-syntax-dependencies.png
/// ```
fn maybe_write_syntax_dependencies_to_graphviz_dot_file(
other_syntax_lookup: &OtherSyntaxLookup,
syntax_to_dependencies: &SyntaxToDependencies,
) {
if let Ok(dot_file_path) = std::env::var("BAT_SYNTAX_DEPENDENCIES_TO_GRAPHVIZ_DOT_FILE") {
graphviz_utils::try_syntax_dependencies_to_graphviz_dot_file(
other_syntax_lookup,
syntax_to_dependencies,
&dot_file_path,
);
}
}
/// Removes any context name from the syntax reference.
///
/// When we track dependencies between syntaxes, we are not interested in
/// dependencies on specific contexts inside other syntaxes. We only care about
/// the dependency on the syntax itself.
///
/// For example, if a syntax includes another syntax like this:
/// ```yaml
/// - include: scope:source.c++#unique-variables
/// ```
/// we only want to track the dependency on `source.c++`.
fn remove_explicit_context(scope: Scope) -> Scope {
if let Some(without_context) = scope.build_string().split('#').next() {
Scope::new(without_context).expect("removing context reference must never fail")
} else {
scope
}
}
fn dependency_from_context_reference(context_reference: &ContextReference) -> Option<OtherSyntax> {
match &context_reference {
ContextReference::File { ref name, .. } => Some(OtherSyntax::ByName(name.clone())),
ContextReference::ByScope { ref scope, .. } => {
Some(OtherSyntax::ByScope(remove_explicit_context(*scope)))
}
_ => None,
}
}
/// Helper to construct a [SyntaxSetBuilder] that contains only [SyntaxDefinition]s
/// that have dependencies among them.
struct SyntaxSetDependencyBuilder {
syntax_set_builder: SyntaxSetBuilder,
}
impl SyntaxSetDependencyBuilder {
fn new() -> Self {
SyntaxSetDependencyBuilder {
syntax_set_builder: SyntaxSetBuilder::new(),
}
}
/// Add a [SyntaxDefinition] to the underlying [SyntaxSetBuilder].
/// Also resolve any dependencies it has and add those [SyntaxDefinition]s too.
/// This is a recursive process.
fn add_with_dependencies(
&mut self,
syntax: &SyntaxDefinition,
other_syntax_lookup: &OtherSyntaxLookup,
syntax_to_dependencies: &SyntaxToDependencies,
syntax_to_dependents: &SyntaxToDependents,
) {
let name = &syntax.name;
if self.is_syntax_already_added(name) {
return;
}
self.syntax_set_builder.add(syntax.clone());
let mut syntaxes_to_add = vec![];
if let Some(dependencies) = syntax_to_dependencies.get(name) {
syntaxes_to_add.extend(dependencies);
}
if let Some(dependents) = syntax_to_dependents.get(name) {
// This will later be enabled intelligently
if std::env::var("BAT_INCLUDE_SYNTAX_DEPENDENTS").is_ok() {
syntaxes_to_add.extend(dependents);
}
}
for syntax_to_add in syntaxes_to_add {
if let Some(syntax_to_add) = other_syntax_lookup.get(syntax_to_add) {
self.add_with_dependencies(
syntax_to_add,
other_syntax_lookup,
syntax_to_dependencies,
syntax_to_dependents,
)
}
}
}
fn is_syntax_already_added(&self, name: &str) -> bool {
self.syntax_set_builder
.syntaxes()
.iter()
.any(|syntax| syntax.name == name)
}
fn build(self) -> SyntaxSet {
self.syntax_set_builder.build()
}
}
fn asset_to_contents<T: serde::Serialize>(
asset: &T,
description: &str,
compressed: bool,
) -> Result<Vec<u8>> {
let mut contents = vec![];
if compressed {
bincode::serialize_into(
flate2::write::ZlibEncoder::new(&mut contents, flate2::Compression::best()),
asset,
)
} else {
bincode::serialize_into(&mut contents, asset)
}
.map_err(|_| format!("Could not serialize {}", description))?;
Ok(contents)
}
fn asset_to_cache<T: serde::Serialize>(
asset: &T,
path: &Path,
description: &str,
compressed: bool,
) -> Result<()> {
print!("Writing {} to {} ... ", description, path.to_string_lossy());
let contents = asset_to_contents(asset, description, compressed)?;
std::fs::write(path, &contents[..]).map_err(|_| {
format!(
"Could not save {} to {}",
description,
path.to_string_lossy()
)
})?;
println!("okay");
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn remove_explicit_context_sanity() {
// Example from Objective-C++.sublime-syntax
let scope = Scope::new("source.c++#unique-variables").unwrap();
let expected = Scope::new("source.c++").unwrap();
assert_eq!(remove_explicit_context(scope), expected);
}
}

View File

@@ -0,0 +1,41 @@
use super::*;
pub(crate) fn try_syntax_dependencies_to_graphviz_dot_file(
other_syntax_lookup: &OtherSyntaxLookup,
syntax_to_dependencies: &SyntaxToDependencies,
dot_file_path: &str,
) {
match syntax_dependencies_to_graphviz_dot_file(
other_syntax_lookup,
syntax_to_dependencies,
dot_file_path,
) {
Ok(_) => println!("Wrote graphviz dot file to {}", dot_file_path),
Err(e) => eprintln!(
"Failed to write graphviz dot file to {}: {}",
dot_file_path, e
),
};
}
fn syntax_dependencies_to_graphviz_dot_file(
other_syntax_lookup: &OtherSyntaxLookup,
syntax_to_dependencies: &SyntaxToDependencies,
dot_file_path: &str,
) -> Result<()> {
use std::io::Write;
let mut dot_file = std::fs::File::create(dot_file_path)?;
writeln!(dot_file, "digraph BatSyntaxDependencies {{")?;
for (key, dependencies) in syntax_to_dependencies {
for dependency in dependencies {
if let Some(dep) = other_syntax_lookup.get(dependency) {
writeln!(dot_file, " \"{}\" -> \"{}\"", key, dep.name)?;
}
}
}
writeln!(dot_file, "}}")?;
Ok(())
}

View File

@@ -0,0 +1,42 @@
use std::ffi::OsStr;
use std::path::Path;
use crate::error::*;
const IGNORED_SUFFIXES: [&str; 13] = [
// Editor etc backups
"~",
".bak",
".old",
".orig",
// Debian and derivatives apt/dpkg/ucf backups
".dpkg-dist",
".dpkg-old",
".ucf-dist",
".ucf-new",
".ucf-old",
// Red Hat and derivatives rpm backups
".rpmnew",
".rpmorig",
".rpmsave",
// Build system input/template files
".in",
];
/// If we find an ignored suffix on the file name, e.g. '~', we strip it and
/// then try again without it.
pub fn try_with_stripped_suffix<T, F>(file_name: &OsStr, func: F) -> Result<Option<T>>
where
F: Fn(&OsStr) -> Result<Option<T>>,
{
let mut from_stripped = None;
if let Some(file_str) = Path::new(file_name).to_str() {
for suffix in &IGNORED_SUFFIXES {
if let Some(stripped_filename) = file_str.strip_suffix(suffix) {
from_stripped = func(OsStr::new(stripped_filename))?;
break;
}
}
}
Ok(from_stripped)
}

View File

@@ -0,0 +1,72 @@
use std::collections::HashMap;
use lazycell::LazyCell;
use syntect::parsing::SyntaxSet;
use super::*;
#[derive(Debug)]
pub(crate) struct MinimalAssets {
minimal_syntaxes: MinimalSyntaxes,
/// Lazily load serialized [SyntaxSet]s from [Self.minimal_syntaxes]. The
/// index in this vec matches the index in
/// [Self.minimal_syntaxes.serialized_syntax_sets]
deserialized_minimal_syntaxes: Vec<LazyCell<SyntaxSet>>,
}
/// Stores and allows lookup of minimal [SyntaxSet]s. The [SyntaxSet]s are
/// stored in serialized form, and are deserialized on-demand. This gives good
/// startup performance since only the necessary [SyntaxReference]s needs to be
/// deserialized.
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub(crate) struct MinimalSyntaxes {
/// Lookup the index into `serialized_syntax_sets` of a [SyntaxSet] by the
/// name of any [SyntaxReference] inside the [SyntaxSet]
/// (We will later add `by_extension`, `by_first_line`, etc.)
pub(crate) by_name: HashMap<String, usize>,
/// Serialized [SyntaxSet]s. Whether or not this data is compressed is
/// decided by [COMPRESS_SERIALIZED_MINIMAL_SYNTAXES]
pub(crate) serialized_syntax_sets: Vec<Vec<u8>>,
}
impl MinimalAssets {
pub(crate) fn new(minimal_syntaxes: MinimalSyntaxes) -> Self {
// Prepare so we can lazily load minimal syntaxes without a mut reference
let deserialized_minimal_syntaxes =
vec![LazyCell::new(); minimal_syntaxes.serialized_syntax_sets.len()];
Self {
minimal_syntaxes,
deserialized_minimal_syntaxes,
}
}
pub fn get_syntax_set_by_name(&self, name: &str) -> Option<&SyntaxSet> {
self.minimal_syntaxes
.by_name
.get(&name.to_ascii_lowercase())
.and_then(|index| self.get_minimal_syntax_set_with_index(*index))
}
fn load_minimal_syntax_set_with_index(&self, index: usize) -> Result<SyntaxSet> {
let serialized_syntax_set = &self.minimal_syntaxes.serialized_syntax_sets[index];
asset_from_contents(
&serialized_syntax_set[..],
&format!("minimal syntax set {}", index),
COMPRESS_SERIALIZED_MINIMAL_SYNTAXES,
)
.map_err(|_| format!("Could not parse minimal syntax set {}", index).into())
}
fn get_minimal_syntax_set_with_index(&self, index: usize) -> Option<&SyntaxSet> {
self.deserialized_minimal_syntaxes
.get(index)
.and_then(|cell| {
cell.try_borrow_with(|| self.load_minimal_syntax_set_with_index(index))
.ok()
})
}
}

View File

@@ -0,0 +1,27 @@
use std::path::PathBuf;
use syntect::parsing::SyntaxSet;
use super::*;
/// A SyntaxSet in serialized form, i.e. bincoded and flate2 compressed.
/// We keep it in this format since we want to load it lazily.
#[derive(Debug)]
pub enum SerializedSyntaxSet {
/// The data comes from a user-generated cache file.
FromFile(PathBuf),
/// The data to use is embedded into the bat binary.
FromBinary(&'static [u8]),
}
impl SerializedSyntaxSet {
pub fn deserialize(&self) -> Result<SyntaxSet> {
match self {
SerializedSyntaxSet::FromBinary(data) => Ok(from_binary(data, COMPRESS_SYNTAXES)),
SerializedSyntaxSet::FromFile(ref path) => {
asset_from_cache(path, "syntax set", COMPRESS_SYNTAXES)
}
}
}
}

View File

@@ -62,7 +62,7 @@ impl App {
// Read arguments from bats config file
let mut args = get_args_from_env_var()
.unwrap_or_else(get_args_from_config_file)
.chain_err(|| "Could not parse configuration file")?;
.map_err(|_| "Could not parse configuration file")?;
// Put the zero-th CLI argument (program name) first
args.insert(0, cli_args.next().unwrap());

View File

@@ -20,6 +20,7 @@ pub fn cache_dir() -> Cow<'static, str> {
pub fn clear_assets() {
clear_asset("themes.bin", "theme set cache");
clear_asset("syntaxes.bin", "syntax set cache");
clear_asset("minimal_syntaxes.bin", "minimal syntax sets cache");
clear_asset("metadata.yaml", "metadata file");
}

View File

@@ -95,7 +95,8 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
'--highlight-line 40' highlights line 40\n \
'--highlight-line 30:40' highlights lines 30 to 40\n \
'--highlight-line :40' highlights lines 1 to 40\n \
'--highlight-line 40:' highlights lines 40 to the end of the file",
'--highlight-line 40:' highlights lines 40 to the end of the file\n \
'--highlight-line 30:+10' highlights lines 30 to 40",
),
)
.arg(
@@ -423,7 +424,8 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
'--line-range 30:40' prints lines 30 to 40\n \
'--line-range :40' prints lines 1 to 40\n \
'--line-range 40:' prints lines 40 to the end of the file\n \
'--line-range 40' only prints line 40",
'--line-range 40' only prints line 40\n \
'--line-range 30:+10' prints lines 30 to 40",
),
)
.arg(

View File

@@ -64,7 +64,7 @@ pub fn generate_config_file() -> bat::error::Result<()> {
#--pager="less --RAW-CONTROL-CHARS --quit-if-one-screen --mouse"
# Syntax mappings: map a certain filename pattern to a language.
# Example 1: use the C++ syntax for .ino files
# Example 1: use the C++ syntax for Arduino .ino files
# Example 2: Use ".gitignore"-style highlighting for ".ignore" files
#--map-syntax "*.ino:C++"
#--map-syntax ".ignore:Git Ignore"

View File

@@ -1,5 +1,4 @@
// `error_chain!` can recurse deeply
#![recursion_limit = "1024"]
#![deny(unsafe_code)]
mod app;
mod assets;
@@ -50,8 +49,7 @@ fn build_assets(matches: &clap::ArgMatches) -> Result<()> {
let blank = matches.is_present("blank");
let assets = bat::assets::HighlightingAssets::from_files(source_dir, !blank)?;
assets.save_to_cache(target_dir, clap::crate_version!())
bat::assets::build(source_dir, !blank, target_dir, clap::crate_version!())
}
fn run_cache_subcommand(matches: &clap::ArgMatches) -> Result<()> {
@@ -95,7 +93,7 @@ pub fn get_languages(config: &Config) -> Result<String> {
.collect::<Vec<_>>();
// Handling of file-extension conflicts, see issue #1076
for lang in languages.iter_mut() {
for lang in &mut languages {
let lang_name = lang.name.clone();
lang.file_extensions.retain(|extension| {
// The 'extension' variable is not certainly a real extension.
@@ -104,17 +102,12 @@ pub fn get_languages(config: &Config) -> Result<String> {
// Also skip if the 'extension' contains another real extension, likely
// that is a full match file name like 'CMakeLists.txt' and 'Cargo.lock'
if extension.starts_with('.') || Path::new(extension).extension().is_some() {
true
} else {
let test_file = Path::new("test").with_extension(extension);
let syntax_in_set = assets
.get_syntax_for_file_name(test_file, &config.syntax_mapping)
.unwrap(); // safe since .get_syntaxes() above worked
match syntax_in_set {
Some(syntax_in_set) => syntax_in_set.syntax.name == lang_name,
None => false,
}
return true;
}
let test_file = Path::new("test").with_extension(extension);
let syntax_in_set = assets.get_syntax_for_path(test_file, &config.syntax_mapping);
matches!(syntax_in_set, Ok(syntax_in_set) if syntax_in_set.syntax.name == lang_name)
});
}
@@ -122,7 +115,7 @@ pub fn get_languages(config: &Config) -> Result<String> {
let configured_languages = get_syntax_mapping_to_paths(config.syntax_mapping.mappings());
for lang in languages.iter_mut() {
for lang in &mut languages {
if let Some(additional_paths) = configured_languages.get(lang.name.as_str()) {
lang.file_extensions
.extend(additional_paths.iter().cloned());
@@ -236,7 +229,7 @@ fn invoke_bugreport(app: &App) {
let pager = bat::config::get_pager_executable(app.matches.value_of("pager"))
.unwrap_or_else(|| "less".to_owned()); // FIXME: Avoid non-canonical path to "less".
let report = bugreport!()
let mut report = bugreport!()
.info(SoftwareVersion::default())
.info(OperatingSystem::default())
.info(CommandLine::default())
@@ -244,6 +237,8 @@ fn invoke_bugreport(app: &App) {
"SHELL",
"PAGER",
"LESS",
"LANG",
"LC_ALL",
"BAT_PAGER",
"BAT_CACHE_PATH",
"BAT_CONFIG_PATH",
@@ -260,14 +255,13 @@ fn invoke_bugreport(app: &App) {
.info(FileContent::new("Config file", config_file()))
.info(CompileTimeInformation::default());
let mut report = if let Ok(resolved_path) = grep_cli::resolve_binary(pager) {
report.info(CommandOutput::new(
#[cfg(feature = "paging")]
if let Ok(resolved_path) = grep_cli::resolve_binary(pager) {
report = report.info(CommandOutput::new(
"Less version",
resolved_path,
&["--version"],
))
} else {
report
};
report.print::<Markdown>();

View File

@@ -90,11 +90,10 @@ pub struct Config<'a> {
#[cfg(all(feature = "minimal-application", feature = "paging"))]
pub fn get_pager_executable(config_pager: Option<&str>) -> Option<String> {
if let Ok(Some(pager)) = crate::pager::get_pager(config_pager) {
Some(pager.bin)
} else {
None
}
crate::pager::get_pager(config_pager)
.ok()
.flatten()
.map(|pager| pager.bin)
}
#[test]

View File

@@ -175,9 +175,10 @@ impl<'b> Controller<'b> {
let mut line_ranges: Vec<LineRange> = vec![];
if let Some(line_changes) = line_changes {
for line in line_changes.keys() {
let line = *line as usize;
line_ranges.push(LineRange::new(line - context, line + context));
for &line in line_changes.keys() {
let line = line as usize;
line_ranges
.push(LineRange::new(line.saturating_sub(context), line + context));
}
}

View File

@@ -1,42 +1,52 @@
use error_chain::error_chain;
use std::io::Write;
use thiserror::Error;
error_chain! {
foreign_links {
Clap(::clap::Error) #[cfg(feature = "minimal-application")];
Io(::std::io::Error);
SyntectError(::syntect::LoadingError);
ParseIntError(::std::num::ParseIntError);
GlobParsingError(::globset::Error);
SerdeYamlError(::serde_yaml::Error);
}
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
Io(#[from] ::std::io::Error),
#[error(transparent)]
SyntectError(#[from] ::syntect::LoadingError),
#[error(transparent)]
ParseIntError(#[from] ::std::num::ParseIntError),
#[error(transparent)]
GlobParsingError(#[from] ::globset::Error),
#[error(transparent)]
SerdeYamlError(#[from] ::serde_yaml::Error),
#[error("unable to detect syntax for {0}")]
UndetectedSyntax(String),
#[error("unknown syntax: '{0}'")]
UnknownSyntax(String),
#[error("Unknown style '{0}'")]
UnknownStyle(String),
#[error("Use of bat as a pager is disallowed in order to avoid infinite recursion problems")]
InvalidPagerValueBat,
#[error("{0}")]
Msg(String),
}
errors {
UndetectedSyntax(input: String) {
description("unable to detect syntax"),
display("unable to detect syntax for {}", input)
}
UnknownSyntax(name: String) {
description("unknown syntax"),
display("unknown syntax: '{}'", name)
}
InvalidPagerValueBat {
description("invalid value `bat` for pager property"),
display("Use of bat as a pager is disallowed in order to avoid infinite recursion problems")
}
impl From<&'static str> for Error {
fn from(s: &'static str) -> Self {
Error::Msg(s.to_owned())
}
}
impl From<String> for Error {
fn from(s: String) -> Self {
Error::Msg(s)
}
}
pub type Result<T> = std::result::Result<T, Error>;
pub fn default_error_handler(error: &Error, output: &mut dyn Write) {
use ansi_term::Colour::Red;
match error {
Error(ErrorKind::Io(ref io_error), _)
if io_error.kind() == ::std::io::ErrorKind::BrokenPipe =>
{
Error::Io(ref io_error) if io_error.kind() == ::std::io::ErrorKind::BrokenPipe => {
::std::process::exit(0);
}
Error(ErrorKind::SerdeYamlError(_), _) => {
Error::SerdeYamlError(_) => {
writeln!(
output,
"{}: Error while parsing metadata.yaml file: {}",

View File

@@ -50,7 +50,7 @@ impl InputDescription {
}
pub fn title(&self) -> &String {
match self.title.as_ref() {
match &self.title {
Some(title) => title,
None => &self.name,
}
@@ -108,6 +108,21 @@ pub(crate) struct OpenedInput<'a> {
pub(crate) description: InputDescription,
}
impl OpenedInput<'_> {
/// Get the path of the file:
/// If this was set by the metadata, that will take priority.
/// If it wasn't, it will use the real file path (if available).
pub(crate) fn path(&self) -> Option<&PathBuf> {
self.metadata
.user_provided_name
.as_ref()
.or_else(|| match self.kind {
OpenedInputKind::OrdinaryFile(ref path) => Some(path),
_ => None,
})
}
}
impl<'a> Input<'a> {
pub fn ordinary_file(path: impl AsRef<Path>) -> Self {
Self::_ordinary_file(path.as_ref())
@@ -256,18 +271,18 @@ impl<'a> InputReader<'a> {
}
pub(crate) fn read_line(&mut self, buf: &mut Vec<u8>) -> io::Result<bool> {
if self.first_line.is_empty() {
let res = self.inner.read_until(b'\n', buf).map(|size| size > 0)?;
if self.content_type == Some(ContentType::UTF_16LE) {
self.inner.read_until(0x00, buf).ok();
}
Ok(res)
} else {
if !self.first_line.is_empty() {
buf.append(&mut self.first_line);
Ok(true)
return Ok(true);
}
let res = self.inner.read_until(b'\n', buf).map(|size| size > 0)?;
if self.content_type == Some(ContentType::UTF_16LE) {
let _ = self.inner.read_until(0x00, buf);
}
Ok(res)
}
}

View File

@@ -11,13 +11,13 @@ pub fn retrieve_less_version(less_path: &dyn AsRef<OsStr>) -> Option<usize> {
}
fn parse_less_version(output: &[u8]) -> Option<usize> {
if output.starts_with(b"less ") {
let version = std::str::from_utf8(&output[5..]).ok()?;
let end = version.find(|c: char| !c.is_ascii_digit())?;
version[..end].parse::<usize>().ok()
} else {
None
if !output.starts_with(b"less ") {
return None;
}
let version = std::str::from_utf8(&output[5..]).ok()?;
let end = version.find(|c: char| !c.is_ascii_digit())?;
version[..end].parse::<usize>().ok()
}
#[test]

View File

@@ -19,10 +19,14 @@
//! .unwrap();
//! ```
#![deny(unsafe_code)]
mod macros;
pub mod assets;
pub mod assets_metadata;
pub mod assets_metadata {
pub use super::assets::assets_metadata::*;
}
pub mod config;
pub mod controller;
mod decorations;
@@ -40,8 +44,6 @@ mod preprocessor;
mod pretty_printer;
pub(crate) mod printer;
pub mod style;
#[cfg(feature = "build-assets")]
mod syntax_dependencies;
pub(crate) mod syntax_mapping;
mod terminal;
pub(crate) mod wrapping;

View File

@@ -47,7 +47,16 @@ impl LineRange {
}
2 => {
new_range.lower = line_numbers[0].parse()?;
new_range.upper = line_numbers[1].parse()?;
new_range.upper = if line_numbers[1].bytes().next() == Some(b'+') {
let more_lines = &line_numbers[1][1..]
.parse()
.map_err(|_| "Invalid character after +")?;
new_range.lower + more_lines
} else {
line_numbers[1].parse()?
};
Ok(new_range)
}
_ => Err(
@@ -100,6 +109,23 @@ fn test_parse_fail() {
assert!(range.is_err());
}
#[test]
fn test_parse_plus() {
let range = LineRange::from("40:+10").expect("Shouldn't fail on test!");
assert_eq!(40, range.lower);
assert_eq!(50, range.upper);
}
#[test]
fn test_parse_plus_fail() {
let range = LineRange::from("40:+z");
assert!(range.is_err());
let range = LineRange::from("40:+-10");
assert!(range.is_err());
let range = LineRange::from("40:+");
assert!(range.is_err());
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum RangeCheckResult {
// Within one of the given ranges

View File

@@ -52,7 +52,7 @@ impl OutputType {
use std::process::{Command, Stdio};
let pager_opt =
pager::get_pager(pager_from_config).chain_err(|| "Could not parse pager command.")?;
pager::get_pager(pager_from_config).map_err(|_| "Could not parse pager command.")?;
let pager = match pager_opt {
Some(pager) => pager,
@@ -60,7 +60,7 @@ impl OutputType {
};
if pager.kind == PagerKind::Bat {
return Err(ErrorKind::InvalidPagerValueBat.into());
return Err(Error::InvalidPagerValueBat);
}
let resolved_path = match grep_cli::resolve_binary(&pager.bin) {
@@ -142,7 +142,7 @@ impl OutputType {
OutputType::Pager(ref mut command) => command
.stdin
.as_mut()
.chain_err(|| "Could not open stdin for pager")?,
.ok_or("Could not open stdin for pager")?,
OutputType::Stdout(ref mut handle) => handle,
})
}

View File

@@ -99,6 +99,20 @@ impl<'a> Printer for SimplePrinter<'a> {
}
}
struct HighlighterFromSet<'a> {
highlighter: HighlightLines<'a>,
syntax_set: &'a SyntaxSet,
}
impl<'a> HighlighterFromSet<'a> {
fn new(syntax_in_set: SyntaxReferenceInSet<'a>, theme: &'a Theme) -> Self {
Self {
highlighter: HighlightLines::new(syntax_in_set.syntax, theme),
syntax_set: syntax_in_set.syntax_set,
}
}
}
pub(crate) struct InteractivePrinter<'a> {
colors: Colors,
config: &'a Config<'a>,
@@ -108,8 +122,7 @@ pub(crate) struct InteractivePrinter<'a> {
content_type: Option<ContentType>,
#[cfg(feature = "git")]
pub line_changes: &'a Option<LineChanges>,
highlighter: Option<HighlightLines<'a>>,
syntax_set: &'a SyntaxSet,
highlighter_from_set: Option<HighlighterFromSet<'a>>,
background_color_highlight: Option<Color>,
}
@@ -163,29 +176,24 @@ impl<'a> InteractivePrinter<'a> {
panel_width = 0;
}
let (highlighter, syntax_set) = if input
let highlighter_from_set = if input
.reader
.content_type
.map_or(false, |c| c.is_binary() && !config.show_nonprintable)
{
(None, assets.get_syntax_set()?)
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(ErrorKind::UndetectedSyntax(_), _)) => {
let syntax_set = assets.get_syntax_set()?;
let syntax = syntax_set.find_syntax_plain_text();
SyntaxReferenceInSet { syntax, syntax_set }
}
Err(Error::UndetectedSyntax(_)) => assets
.find_syntax_by_name("Plain Text")?
.expect("A plain text syntax is available"),
Err(e) => return Err(e),
};
(
Some(HighlightLines::new(syntax_in_set.syntax, theme)),
syntax_in_set.syntax_set,
)
Some(HighlighterFromSet::new(syntax_in_set, theme))
};
Ok(InteractivePrinter {
@@ -197,8 +205,7 @@ impl<'a> InteractivePrinter<'a> {
ansi_prefix_sgr: String::new(),
#[cfg(feature = "git")]
line_changes,
highlighter,
syntax_set,
highlighter_from_set,
background_color_highlight,
})
}
@@ -226,29 +233,29 @@ impl<'a> InteractivePrinter<'a> {
fn create_fake_panel(&self, text: &str) -> String {
if self.panel_width == 0 {
"".to_string()
return "".to_string();
}
let text_truncated: String = text.chars().take(self.panel_width - 1).collect();
let text_filled: String = format!(
"{}{}",
text_truncated,
" ".repeat(self.panel_width - 1 - text_truncated.len())
);
if self.config.style_components.grid() {
format!("{}", text_filled)
} else {
let text_truncated: String = text.chars().take(self.panel_width - 1).collect();
let text_filled: String = format!(
"{}{}",
text_truncated,
" ".repeat(self.panel_width - 1 - text_truncated.len())
);
if self.config.style_components.grid() {
format!("{}", text_filled)
} else {
text_filled
}
text_filled
}
}
fn preprocess(&self, text: &str, cursor: &mut usize) -> String {
if self.config.tab_width > 0 {
expand_tabs(text, self.config.tab_width, cursor)
} else {
*cursor += text.len();
text.to_string()
return expand_tabs(text, self.config.tab_width, cursor);
}
*cursor += text.len();
text.to_string()
}
}
@@ -389,13 +396,15 @@ impl<'a> Printer for InteractivePrinter<'a> {
};
let regions = {
let highlighter = match self.highlighter {
Some(ref mut highlighter) => highlighter,
let highlighter_from_set = match self.highlighter_from_set {
Some(ref mut highlighter_from_set) => highlighter_from_set,
_ => {
return Ok(());
}
};
highlighter.highlight(line.as_ref(), self.syntax_set)
highlighter_from_set
.highlighter
.highlight(&line, highlighter_from_set.syntax_set)
};
if out_of_range {
@@ -420,8 +429,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
let decorations = self
.decorations
.iter()
.map(|d| d.generate(line_number, false, self))
.collect::<Vec<_>>();
.map(|d| d.generate(line_number, false, self));
for deco in decorations {
write!(handle, "{} ", deco.text)?;
@@ -435,7 +443,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
let colored_output = self.config.colored_output;
let italics = self.config.use_italic_text;
for &(style, region) in regions.iter() {
for &(style, region) in &regions {
let text = &*self.preprocess(region, &mut cursor_total);
let text_trimmed = text.trim_end_matches(|c| c == '\r' || c == '\n');
write!(
@@ -472,7 +480,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
writeln!(handle)?;
}
} else {
for &(style, region) in regions.iter() {
for &(style, region) in &regions {
let ansi_iterator = AnsiCodeIterator::new(region);
let mut ansi_prefix: String = String::new();
for chunk in ansi_iterator {

View File

@@ -1,184 +0,0 @@
use std::collections::HashMap;
use syntect::parsing::syntax_definition::{
ContextReference, MatchOperation, MatchPattern, Pattern, SyntaxDefinition,
};
use syntect::parsing::{Scope, SyntaxSet, SyntaxSetBuilder};
type SyntaxName = String;
/// Used to look up what dependencies a given [SyntaxDefinition] has
type SyntaxToDependencies = HashMap<SyntaxName, Vec<Dependency>>;
/// Used to look up which [SyntaxDefinition] corresponds to a given [Dependency]
type DependencyToSyntax<'a> = HashMap<Dependency, &'a SyntaxDefinition>;
/// Represents a dependency on an external `.sublime-syntax` file.
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
enum Dependency {
/// By name. Example YAML: `include: C.sublime-syntax`
ByName(String),
/// By scope. Example YAML: `embed: scope:source.c`
ByScope(Scope),
}
/// Generates independent [SyntaxSet]s after analyzing dependencies between syntaxes
/// in a [SyntaxSetBuilder], and then prints the reults.
pub(crate) fn print_syntax_dependencies(syntax_set_builder: &SyntaxSetBuilder) {
println!("Constructing independent SyntaxSets...");
let independent_syntax_sets = build_independent_syntax_sets(syntax_set_builder);
println!("Independent SyntaxSets:");
for syntax_set in independent_syntax_sets {
let names = syntax_set
.syntaxes()
.iter()
.map(|syntax| &syntax.name)
.collect::<Vec<_>>();
println!("{:?}", names);
}
}
/// Analyzes dependencies between syntaxes in a [SyntaxSetBuilder].
/// From that, it builds independent [SyntaxSet]s.
fn build_independent_syntax_sets(
syntax_set_builder: &'_ SyntaxSetBuilder,
) -> impl Iterator<Item = SyntaxSet> + '_ {
let syntaxes = syntax_set_builder.syntaxes();
// Build the data structures we need for dependency resolution
let (syntax_to_dependencies, dependency_to_syntax) = generate_maps(syntaxes);
// Create one independent SyntaxSet from each (non-hidden) SyntaxDefinition
syntaxes.iter().filter_map(move |syntax| {
if syntax.hidden {
return None;
}
let mut builder = SyntaxSetDependencyBuilder::new();
builder.add_with_dependencies(syntax, &syntax_to_dependencies, &dependency_to_syntax);
Some(builder.build())
})
}
/// In order to analyze dependencies, we need two key pieces of data.
/// First, when we have a [Dependency], we need to know what [SyntaxDefinition] that
/// corresponds to. Second, when we have a [SyntaxDefinition], we need to know
/// what dependencies it has. This functions generates that data for each syntax.
fn generate_maps(syntaxes: &[SyntaxDefinition]) -> (SyntaxToDependencies, DependencyToSyntax) {
let mut syntax_to_dependencies = HashMap::new();
let mut dependency_to_syntax = HashMap::new();
for syntax in syntaxes {
syntax_to_dependencies.insert(syntax.name.clone(), dependencies_for_syntax(syntax));
dependency_to_syntax.insert(Dependency::ByName(syntax.name.clone()), syntax);
dependency_to_syntax.insert(Dependency::ByScope(syntax.scope), syntax);
}
(syntax_to_dependencies, dependency_to_syntax)
}
/// Gets what external dependencies a given [SyntaxDefinition] has.
/// An external dependency is another `.sublime-syntax` file.
/// It does that by looking for variants of the following YAML patterns:
/// - `include: C.sublime-syntax`
/// - `embed: scope:source.c`
fn dependencies_for_syntax(syntax: &SyntaxDefinition) -> Vec<Dependency> {
let mut dependencies: Vec<Dependency> = syntax
.contexts
.values()
.flat_map(|context| &context.patterns)
.flat_map(dependencies_from_pattern)
.collect();
// No need to track a dependency more than once
dependencies.dedup();
dependencies
}
fn dependencies_from_pattern(pattern: &Pattern) -> Vec<Dependency> {
match *pattern {
Pattern::Match(MatchPattern {
operation: MatchOperation::Push(ref context_references),
..
}) => context_references
.iter()
.map(dependency_from_context_reference)
.collect(),
Pattern::Include(ref context_reference) => {
vec![dependency_from_context_reference(context_reference)]
}
_ => vec![],
}
.into_iter()
.flatten()
.collect()
}
fn dependency_from_context_reference(context_reference: &ContextReference) -> Option<Dependency> {
match &context_reference {
ContextReference::File { ref name, .. } => Some(Dependency::ByName(name.clone())),
ContextReference::ByScope { ref scope, .. } => Some(Dependency::ByScope(*scope)),
_ => None,
}
}
/// Helper to construct a [SyntaxSetBuilder] that contains only [SyntaxDefinition]s
/// that have dependencies among them.
struct SyntaxSetDependencyBuilder {
syntax_set_builder: SyntaxSetBuilder,
}
impl SyntaxSetDependencyBuilder {
fn new() -> Self {
SyntaxSetDependencyBuilder {
syntax_set_builder: SyntaxSetBuilder::new(),
}
}
/// Add a [SyntaxDefinition] to the underlying [SyntaxSetBuilder].
/// Also resolve any dependencies it has and add those [SyntaxDefinition]s too.
/// This is a recursive process.
fn add_with_dependencies(
&mut self,
syntax: &SyntaxDefinition,
syntax_to_dependencies: &SyntaxToDependencies,
dependency_to_syntax: &DependencyToSyntax,
) {
let name = &syntax.name;
if self.is_syntax_already_added(name) {
return;
}
self.syntax_set_builder.add(syntax.clone());
let dependencies = syntax_to_dependencies.get(name);
if dependencies.is_none() {
eprintln!("ERROR: Unknown dependencies for {}", name);
return;
}
for dependency in dependencies.unwrap() {
if let Some(syntax_definition_dependency) = dependency_to_syntax.get(dependency) {
self.add_with_dependencies(
syntax_definition_dependency,
syntax_to_dependencies,
dependency_to_syntax,
)
}
}
}
fn is_syntax_already_added(&self, name: &str) -> bool {
self.syntax_set_builder
.syntaxes()
.iter()
.any(|syntax| syntax.name == name)
}
fn build(self) -> SyntaxSet {
self.syntax_set_builder.build()
}
}

View File

@@ -81,7 +81,7 @@ impl<'a> SyntaxMapping<'a> {
.unwrap();
}
for glob in [
for glob in &[
"**/systemd/**/*.conf",
"**/systemd/**/*.example",
"*.automount",
@@ -100,9 +100,7 @@ impl<'a> SyntaxMapping<'a> {
"*.swap",
"*.target",
"*.timer",
]
.iter()
{
] {
mapping.insert(glob, MappingTarget::MapTo("INI")).unwrap();
}
@@ -153,7 +151,7 @@ impl<'a> SyntaxMapping<'a> {
}
pub(crate) fn get_syntax_for(&self, path: impl AsRef<Path>) -> Option<MappingTarget<'a>> {
let candidate = Candidate::new(path.as_ref());
let candidate = Candidate::new(&path);
let candidate_filename = path.as_ref().file_name().map(Candidate::new);
for (ref glob, ref syntax) in self.mappings.iter().rev() {
if glob.is_match_candidate(&candidate)

View File

@@ -0,0 +1,2 @@
#!/usr/bin/env bash
echo "Hello"

View File

@@ -34,13 +34,17 @@ const EXAMPLES_DIR: &str = "tests/examples";
fn bat_raw_command_with_config() -> Command {
let mut cmd = Command::cargo_bin("bat").unwrap();
cmd.current_dir("tests/examples");
cmd.env_remove("PAGER");
cmd.env_remove("BAT_PAGER");
cmd.env_remove("BAT_CONFIG_PATH");
cmd.env_remove("BAT_STYLE");
cmd.env_remove("BAT_THEME");
cmd.env_remove("BAT_TABS");
cmd.env_remove("BAT_CACHE_PATH");
cmd.env_remove("BAT_CONFIG_DIR");
cmd.env_remove("BAT_CONFIG_PATH");
cmd.env_remove("BAT_OPTS");
cmd.env_remove("BAT_PAGER");
cmd.env_remove("BAT_STYLE");
cmd.env_remove("BAT_TABS");
cmd.env_remove("BAT_THEME");
cmd.env_remove("COLORTERM");
cmd.env_remove("NO_COLOR");
cmd.env_remove("PAGER");
cmd
}
@@ -1123,6 +1127,21 @@ fn do_not_detect_different_syntax_for_stdin_and_files() {
);
}
#[test]
fn no_first_line_fallback_when_mapping_to_invalid_syntax() {
let file = "regression_tests/first_line_fallback.invalid-syntax";
bat()
.arg("--color=always")
.arg("--map-syntax=*.invalid-syntax:InvalidSyntax")
.arg(&format!("--file-name={}", file))
.arg("--style=plain")
.arg(file)
.assert()
.failure()
.stderr(predicate::str::contains("unknown syntax: 'InvalidSyntax'"));
}
#[test]
fn show_all_mode() {
bat()

View File

@@ -1,3 +1,4 @@
+ /// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,4 +1,5 @@
──┬─────────────────────────────────────────────────────────────────────────────
+ │ /// A rectangle. First line is changed to prevent a regression of #1869
│ struct Rectangle {
│ width: u32,
│ height: u32,

View File

@@ -1,6 +1,7 @@
──┬─────────────────────────────────────────────────────────────────────────────
│ File: sample.rs
──┼─────────────────────────────────────────────────────────────────────────────
+ │ /// A rectangle. First line is changed to prevent a regression of #1869
│ struct Rectangle {
│ width: u32,
│ height: u32,

View File

@@ -1,26 +1,27 @@
───────┬────────────────────────────────────────────────────────────────────────
│ File: sample.rs
───────┼────────────────────────────────────────────────────────────────────────
1 │ struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 _fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 ~ "The perimeter of the rectangle is {} pixels.",
11 ~ │ perimeter(&rect1)
12 │ );
13 +println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 +
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
1 + │ /// A rectangle. First line is changed to prevent a regression of #1869
2 │ struct Rectangle {
3 │ width: u32,
4 │ height: u32,
5 │ }
6
7 _fn main() {
8 │ let rect1 = Rectangle { width: 30, height: 50 };
9 │
10 println!(
11 ~ │ "The perimeter of the rectangle is {} pixels.",
12 ~ perimeter(&rect1)
13 );
14 + │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 │ }
16 │
17 │ fn area(rectangle: &Rectangle) -> u32 {
18 │ rectangle.width * rectangle.height
19 }
20 + │
21 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
22 + │ (rectangle.width + rectangle.height) * 2
23 + │ }
───────┴────────────────────────────────────────────────────────────────────────

View File

@@ -1,26 +1,27 @@
───────┬────────────────────────────────────────────────────────────────────────
│ File: sample.rs
───────┼────────────────────────────────────────────────────────────────────────
1 │ struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 _fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 ~ "The perimeter of the rectangle is {} pixels.",
11 ~ │ perimeter(&rect1)
12 │ );
13 +println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 +
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
1 + │ /// A rectangle. First line is changed to prevent a regression of #1869
2 │ struct Rectangle {
3 │ width: u32,
4 │ height: u32,
5 │ }
6
7 _fn main() {
8 │ let rect1 = Rectangle { width: 30, height: 50 };
9 │
10 println!(
11 ~ │ "The perimeter of the rectangle is {} pixels.",
12 ~ perimeter(&rect1)
13 );
14 + │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 │ }
16 │
17 │ fn area(rectangle: &Rectangle) -> u32 {
18 │ rectangle.width * rectangle.height
19 }
20 + │
21 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
22 + │ (rectangle.width + rectangle.height) * 2
23 + │ }
───────┴────────────────────────────────────────────────────────────────────────

View File

@@ -1,6 +1,7 @@
──┬─────────────────────────────────────────────────────────────────────────────
│ File: sample.rs
──┼─────────────────────────────────────────────────────────────────────────────
+ │ /// A rectangle. First line is changed to prevent a regression of #1869
│ struct Rectangle {
│ width: u32,
│ height: u32,

View File

@@ -1,24 +1,25 @@
───────┬────────────────────────────────────────────────────────────────────────
1 │ struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 _fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 ~ "The perimeter of the rectangle is {} pixels.",
11 ~ │ perimeter(&rect1)
12 │ );
13 +println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 +
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
1 + │ /// A rectangle. First line is changed to prevent a regression of #1869
2 │ struct Rectangle {
3 │ width: u32,
4 │ height: u32,
5 │ }
6
7 _fn main() {
8 │ let rect1 = Rectangle { width: 30, height: 50 };
9 │
10 println!(
11 ~ │ "The perimeter of the rectangle is {} pixels.",
12 ~ perimeter(&rect1)
13 );
14 + │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 │ }
16 │
17 │ fn area(rectangle: &Rectangle) -> u32 {
18 │ rectangle.width * rectangle.height
19 }
20 + │
21 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
22 + │ (rectangle.width + rectangle.height) * 2
23 + │ }
───────┴────────────────────────────────────────────────────────────────────────

View File

@@ -1,24 +1,25 @@
───────┬────────────────────────────────────────────────────────────────────────
1 │ struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 _fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 ~ "The perimeter of the rectangle is {} pixels.",
11 ~ │ perimeter(&rect1)
12 │ );
13 +println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 +
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
1 + │ /// A rectangle. First line is changed to prevent a regression of #1869
2 │ struct Rectangle {
3 │ width: u32,
4 │ height: u32,
5 │ }
6
7 _fn main() {
8 │ let rect1 = Rectangle { width: 30, height: 50 };
9 │
10 println!(
11 ~ │ "The perimeter of the rectangle is {} pixels.",
12 ~ perimeter(&rect1)
13 );
14 + │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 │ }
16 │
17 │ fn area(rectangle: &Rectangle) -> u32 {
18 │ rectangle.width * rectangle.height
19 }
20 + │
21 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
22 + │ (rectangle.width + rectangle.height) * 2
23 + │ }
───────┴────────────────────────────────────────────────────────────────────────

View File

@@ -1,4 +1,5 @@
──┬─────────────────────────────────────────────────────────────────────────────
+ │ /// A rectangle. First line is changed to prevent a regression of #1869
│ struct Rectangle {
│ width: u32,
│ height: u32,

View File

@@ -1,4 +1,5 @@
File: sample.rs
+ /// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,23 +1,24 @@
File: sample.rs
1 struct Rectangle {
2 width: u32,
3 height: u32,
4 }
5
6 _ fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8
9 println!(
10 ~ "The perimeter of the rectangle is {} pixels.",
11 ~ perimeter(&rect1)
12 );
13 + println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 }
15
16 fn area(rectangle: &Rectangle) -> u32 {
17 rectangle.width * rectangle.height
18 }
19 +
20 + fn perimeter(rectangle: &Rectangle) -> u32 {
21 + (rectangle.width + rectangle.height) * 2
22 + }
1 + /// A rectangle. First line is changed to prevent a regression of #1869
2 struct Rectangle {
3 width: u32,
4 height: u32,
5 }
6
7 _ fn main() {
8 let rect1 = Rectangle { width: 30, height: 50 };
9
10 println!(
11 ~ "The perimeter of the rectangle is {} pixels.",
12 ~ perimeter(&rect1)
13 );
14 + println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 }
16
17 fn area(rectangle: &Rectangle) -> u32 {
18 rectangle.width * rectangle.height
19 }
20 +
21 + fn perimeter(rectangle: &Rectangle) -> u32 {
22 + (rectangle.width + rectangle.height) * 2
23 + }

View File

@@ -1,23 +1,24 @@
File: sample.rs
1 struct Rectangle {
2 width: u32,
3 height: u32,
4 }
5
6 _ fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8
9 println!(
10 ~ "The perimeter of the rectangle is {} pixels.",
11 ~ perimeter(&rect1)
12 );
13 + println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 }
15
16 fn area(rectangle: &Rectangle) -> u32 {
17 rectangle.width * rectangle.height
18 }
19 +
20 + fn perimeter(rectangle: &Rectangle) -> u32 {
21 + (rectangle.width + rectangle.height) * 2
22 + }
1 + /// A rectangle. First line is changed to prevent a regression of #1869
2 struct Rectangle {
3 width: u32,
4 height: u32,
5 }
6
7 _ fn main() {
8 let rect1 = Rectangle { width: 30, height: 50 };
9
10 println!(
11 ~ "The perimeter of the rectangle is {} pixels.",
12 ~ perimeter(&rect1)
13 );
14 + println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 }
16
17 fn area(rectangle: &Rectangle) -> u32 {
18 rectangle.width * rectangle.height
19 }
20 +
21 + fn perimeter(rectangle: &Rectangle) -> u32 {
22 + (rectangle.width + rectangle.height) * 2
23 + }

View File

@@ -1,4 +1,5 @@
File: sample.rs
+ /// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,22 +1,23 @@
1 struct Rectangle {
2 width: u32,
3 height: u32,
4 }
5
6 _ fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8
9 println!(
10 ~ "The perimeter of the rectangle is {} pixels.",
11 ~ perimeter(&rect1)
12 );
13 + println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 }
15
16 fn area(rectangle: &Rectangle) -> u32 {
17 rectangle.width * rectangle.height
18 }
19 +
20 + fn perimeter(rectangle: &Rectangle) -> u32 {
21 + (rectangle.width + rectangle.height) * 2
22 + }
1 + /// A rectangle. First line is changed to prevent a regression of #1869
2 struct Rectangle {
3 width: u32,
4 height: u32,
5 }
6
7 _ fn main() {
8 let rect1 = Rectangle { width: 30, height: 50 };
9
10 println!(
11 ~ "The perimeter of the rectangle is {} pixels.",
12 ~ perimeter(&rect1)
13 );
14 + println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 }
16
17 fn area(rectangle: &Rectangle) -> u32 {
18 rectangle.width * rectangle.height
19 }
20 +
21 + fn perimeter(rectangle: &Rectangle) -> u32 {
22 + (rectangle.width + rectangle.height) * 2
23 + }

View File

@@ -1,22 +1,23 @@
1 struct Rectangle {
2 width: u32,
3 height: u32,
4 }
5
6 _ fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8
9 println!(
10 ~ "The perimeter of the rectangle is {} pixels.",
11 ~ perimeter(&rect1)
12 );
13 + println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 }
15
16 fn area(rectangle: &Rectangle) -> u32 {
17 rectangle.width * rectangle.height
18 }
19 +
20 + fn perimeter(rectangle: &Rectangle) -> u32 {
21 + (rectangle.width + rectangle.height) * 2
22 + }
1 + /// A rectangle. First line is changed to prevent a regression of #1869
2 struct Rectangle {
3 width: u32,
4 height: u32,
5 }
6
7 _ fn main() {
8 let rect1 = Rectangle { width: 30, height: 50 };
9
10 println!(
11 ~ "The perimeter of the rectangle is {} pixels.",
12 ~ perimeter(&rect1)
13 );
14 + println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 }
16
17 fn area(rectangle: &Rectangle) -> u32 {
18 rectangle.width * rectangle.height
19 }
20 +
21 + fn perimeter(rectangle: &Rectangle) -> u32 {
22 + (rectangle.width + rectangle.height) * 2
23 + }

View File

@@ -1,3 +1,4 @@
+ /// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,26 +1,27 @@
───────┬────────────────────────────────────────────────────────────────────────
│ File: sample.rs
───────┼────────────────────────────────────────────────────────────────────────
1 │ struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 _fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 ~ "The perimeter of the rectangle is {} pixels.",
11 ~ │ perimeter(&rect1)
12 │ );
13 +println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 +
20 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 + │ (rectangle.width + rectangle.height) * 2
22 + │ }
1 + │ /// A rectangle. First line is changed to prevent a regression of #1869
2 │ struct Rectangle {
3 │ width: u32,
4 │ height: u32,
5 │ }
6
7 _fn main() {
8 │ let rect1 = Rectangle { width: 30, height: 50 };
9 │
10 println!(
11 ~ │ "The perimeter of the rectangle is {} pixels.",
12 ~ perimeter(&rect1)
13 );
14 + │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 │ }
16 │
17 │ fn area(rectangle: &Rectangle) -> u32 {
18 │ rectangle.width * rectangle.height
19 }
20 + │
21 + │ fn perimeter(rectangle: &Rectangle) -> u32 {
22 + │ (rectangle.width + rectangle.height) * 2
23 + │ }
───────┴────────────────────────────────────────────────────────────────────────

View File

@@ -1,4 +1,5 @@
────────────────────────────────────────────────────────────────────────────────
/// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,6 +1,7 @@
────────────────────────────────────────────────────────────────────────────────
File: sample.rs
────────────────────────────────────────────────────────────────────────────────
/// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,26 +1,27 @@
─────┬──────────────────────────────────────────────────────────────────────────
│ File: sample.rs
─────┼──────────────────────────────────────────────────────────────────────────
1 │ struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 │ fn main() {
7 │ let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 │ "The perimeter of the rectangle is {} pixels.",
11 │ perimeter(&rect1)
12 │ );
13 │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 │
20 │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 │ (rectangle.width + rectangle.height) * 2
22 │ }
1 │ /// A rectangle. First line is changed to prevent a regression of #1869
2 │ struct Rectangle {
3 │ width: u32,
4 │ height: u32,
5 │ }
6 │
7 │ fn main() {
8 │ let rect1 = Rectangle { width: 30, height: 50 };
9 │
10 │ println!(
11 │ "The perimeter of the rectangle is {} pixels.",
12 │ perimeter(&rect1)
13 │ );
14 │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 │ }
16 │
17 │ fn area(rectangle: &Rectangle) -> u32 {
18 │ rectangle.width * rectangle.height
19 │ }
20 │
21 │ fn perimeter(rectangle: &Rectangle) -> u32 {
22 │ (rectangle.width + rectangle.height) * 2
23 │ }
─────┴──────────────────────────────────────────────────────────────────────────

View File

@@ -1,26 +1,27 @@
─────┬──────────────────────────────────────────────────────────────────────────
│ File: sample.rs
─────┼──────────────────────────────────────────────────────────────────────────
1 │ struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 │ fn main() {
7 │ let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 │ "The perimeter of the rectangle is {} pixels.",
11 │ perimeter(&rect1)
12 │ );
13 │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 │
20 │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 │ (rectangle.width + rectangle.height) * 2
22 │ }
1 │ /// A rectangle. First line is changed to prevent a regression of #1869
2 │ struct Rectangle {
3 │ width: u32,
4 │ height: u32,
5 │ }
6 │
7 │ fn main() {
8 │ let rect1 = Rectangle { width: 30, height: 50 };
9 │
10 │ println!(
11 │ "The perimeter of the rectangle is {} pixels.",
12 │ perimeter(&rect1)
13 │ );
14 │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 │ }
16 │
17 │ fn area(rectangle: &Rectangle) -> u32 {
18 │ rectangle.width * rectangle.height
19 │ }
20 │
21 │ fn perimeter(rectangle: &Rectangle) -> u32 {
22 │ (rectangle.width + rectangle.height) * 2
23 │ }
─────┴──────────────────────────────────────────────────────────────────────────

View File

@@ -1,6 +1,7 @@
────────────────────────────────────────────────────────────────────────────────
File: sample.rs
────────────────────────────────────────────────────────────────────────────────
/// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,24 +1,25 @@
─────┬──────────────────────────────────────────────────────────────────────────
1 │ struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 │ fn main() {
7 │ let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 │ "The perimeter of the rectangle is {} pixels.",
11 │ perimeter(&rect1)
12 │ );
13 │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 │
20 │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 │ (rectangle.width + rectangle.height) * 2
22 │ }
1 │ /// A rectangle. First line is changed to prevent a regression of #1869
2 │ struct Rectangle {
3 │ width: u32,
4 │ height: u32,
5 │ }
6 │
7 │ fn main() {
8 │ let rect1 = Rectangle { width: 30, height: 50 };
9 │
10 │ println!(
11 │ "The perimeter of the rectangle is {} pixels.",
12 │ perimeter(&rect1)
13 │ );
14 │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 │ }
16 │
17 │ fn area(rectangle: &Rectangle) -> u32 {
18 │ rectangle.width * rectangle.height
19 │ }
20 │
21 │ fn perimeter(rectangle: &Rectangle) -> u32 {
22 │ (rectangle.width + rectangle.height) * 2
23 │ }
─────┴──────────────────────────────────────────────────────────────────────────

View File

@@ -1,24 +1,25 @@
─────┬──────────────────────────────────────────────────────────────────────────
1 │ struct Rectangle {
2 │ width: u32,
3 │ height: u32,
4 │ }
5 │
6 │ fn main() {
7 │ let rect1 = Rectangle { width: 30, height: 50 };
8 │
9 │ println!(
10 │ "The perimeter of the rectangle is {} pixels.",
11 │ perimeter(&rect1)
12 │ );
13 │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 │ }
15 │
16 │ fn area(rectangle: &Rectangle) -> u32 {
17 │ rectangle.width * rectangle.height
18 │ }
19 │
20 │ fn perimeter(rectangle: &Rectangle) -> u32 {
21 │ (rectangle.width + rectangle.height) * 2
22 │ }
1 │ /// A rectangle. First line is changed to prevent a regression of #1869
2 │ struct Rectangle {
3 │ width: u32,
4 │ height: u32,
5 │ }
6 │
7 │ fn main() {
8 │ let rect1 = Rectangle { width: 30, height: 50 };
9 │
10 │ println!(
11 │ "The perimeter of the rectangle is {} pixels.",
12 │ perimeter(&rect1)
13 │ );
14 │ println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 │ }
16 │
17 │ fn area(rectangle: &Rectangle) -> u32 {
18 │ rectangle.width * rectangle.height
19 │ }
20 │
21 │ fn perimeter(rectangle: &Rectangle) -> u32 {
22 │ (rectangle.width + rectangle.height) * 2
23 │ }
─────┴──────────────────────────────────────────────────────────────────────────

View File

@@ -1,4 +1,5 @@
────────────────────────────────────────────────────────────────────────────────
/// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,4 +1,5 @@
File: sample.rs
/// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,23 +1,24 @@
File: sample.rs
1 struct Rectangle {
2 width: u32,
3 height: u32,
4 }
5
6 fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8
9 println!(
10 "The perimeter of the rectangle is {} pixels.",
11 perimeter(&rect1)
12 );
13 println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 }
15
16 fn area(rectangle: &Rectangle) -> u32 {
17 rectangle.width * rectangle.height
18 }
19
20 fn perimeter(rectangle: &Rectangle) -> u32 {
21 (rectangle.width + rectangle.height) * 2
22 }
1 /// A rectangle. First line is changed to prevent a regression of #1869
2 struct Rectangle {
3 width: u32,
4 height: u32,
5 }
6
7 fn main() {
8 let rect1 = Rectangle { width: 30, height: 50 };
9
10 println!(
11 "The perimeter of the rectangle is {} pixels.",
12 perimeter(&rect1)
13 );
14 println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 }
16
17 fn area(rectangle: &Rectangle) -> u32 {
18 rectangle.width * rectangle.height
19 }
20
21 fn perimeter(rectangle: &Rectangle) -> u32 {
22 (rectangle.width + rectangle.height) * 2
23 }

View File

@@ -1,23 +1,24 @@
File: sample.rs
1 struct Rectangle {
2 width: u32,
3 height: u32,
4 }
5
6 fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8
9 println!(
10 "The perimeter of the rectangle is {} pixels.",
11 perimeter(&rect1)
12 );
13 println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 }
15
16 fn area(rectangle: &Rectangle) -> u32 {
17 rectangle.width * rectangle.height
18 }
19
20 fn perimeter(rectangle: &Rectangle) -> u32 {
21 (rectangle.width + rectangle.height) * 2
22 }
1 /// A rectangle. First line is changed to prevent a regression of #1869
2 struct Rectangle {
3 width: u32,
4 height: u32,
5 }
6
7 fn main() {
8 let rect1 = Rectangle { width: 30, height: 50 };
9
10 println!(
11 "The perimeter of the rectangle is {} pixels.",
12 perimeter(&rect1)
13 );
14 println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 }
16
17 fn area(rectangle: &Rectangle) -> u32 {
18 rectangle.width * rectangle.height
19 }
20
21 fn perimeter(rectangle: &Rectangle) -> u32 {
22 (rectangle.width + rectangle.height) * 2
23 }

View File

@@ -1,4 +1,5 @@
File: sample.rs
/// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,22 +1,23 @@
1 struct Rectangle {
2 width: u32,
3 height: u32,
4 }
5
6 fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8
9 println!(
10 "The perimeter of the rectangle is {} pixels.",
11 perimeter(&rect1)
12 );
13 println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 }
15
16 fn area(rectangle: &Rectangle) -> u32 {
17 rectangle.width * rectangle.height
18 }
19
20 fn perimeter(rectangle: &Rectangle) -> u32 {
21 (rectangle.width + rectangle.height) * 2
22 }
1 /// A rectangle. First line is changed to prevent a regression of #1869
2 struct Rectangle {
3 width: u32,
4 height: u32,
5 }
6
7 fn main() {
8 let rect1 = Rectangle { width: 30, height: 50 };
9
10 println!(
11 "The perimeter of the rectangle is {} pixels.",
12 perimeter(&rect1)
13 );
14 println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 }
16
17 fn area(rectangle: &Rectangle) -> u32 {
18 rectangle.width * rectangle.height
19 }
20
21 fn perimeter(rectangle: &Rectangle) -> u32 {
22 (rectangle.width + rectangle.height) * 2
23 }

View File

@@ -1,22 +1,23 @@
1 struct Rectangle {
2 width: u32,
3 height: u32,
4 }
5
6 fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8
9 println!(
10 "The perimeter of the rectangle is {} pixels.",
11 perimeter(&rect1)
12 );
13 println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
14 }
15
16 fn area(rectangle: &Rectangle) -> u32 {
17 rectangle.width * rectangle.height
18 }
19
20 fn perimeter(rectangle: &Rectangle) -> u32 {
21 (rectangle.width + rectangle.height) * 2
22 }
1 /// A rectangle. First line is changed to prevent a regression of #1869
2 struct Rectangle {
3 width: u32,
4 height: u32,
5 }
6
7 fn main() {
8 let rect1 = Rectangle { width: 30, height: 50 };
9
10 println!(
11 "The perimeter of the rectangle is {} pixels.",
12 perimeter(&rect1)
13 );
14 println!(r#"This line contains invalid utf8: "<22><><EFBFBD><EFBFBD><EFBFBD>"#;
15 }
16
17 fn area(rectangle: &Rectangle) -> u32 {
18 rectangle.width * rectangle.height
19 }
20
21 fn perimeter(rectangle: &Rectangle) -> u32 {
22 (rectangle.width + rectangle.height) * 2
23 }

View File

@@ -1,3 +1,4 @@
/// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,3 +1,4 @@
/// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -1,3 +1,4 @@
/// A rectangle. First line is changed to prevent a regression of #1869
struct Rectangle {
width: u32,
height: u32,

View File

@@ -0,0 +1,21 @@
%YAML 1.2
---
# http://www.sublimetext.com/docs/3/syntax.html
name: BatTestCustomAssets
file_extensions:
- battestcustomassets
scope: source.battestcustomassets
# This syntax is used to test if custom assets work with bat.
# The way it works is that this syntax is only allowed to be included with
# custom assets. That way it is easy to test if custom assets are used (and works)
# or not.
#
# This syntax is based on CpuInfo.sublime-syntax.
contexts:
main:
- match: '^([^:]+)\w*:\w*(.*)$'
captures:
1: keyword.other.battestcustomassets-key
2: string.other.battestcustomassets-value

View File

@@ -6,7 +6,7 @@ import sys
import os.path as path
import os
import argparse
from multiprocessing import Pool
BAT_OPTIONS = [
"--no-config",
@@ -38,64 +38,79 @@ def get_options(source):
return options
def create_highlighted_version(args):
output_basepath, source = args
env = os.environ.copy()
env.pop("BAT_CACHE_PATH", None)
env.pop("BAT_CONFIG_DIR", None)
env.pop("BAT_CONFIG_PATH", None)
env.pop("BAT_OPTS", None)
env.pop("BAT_PAGER", None)
env.pop("BAT_STYLE", None)
env.pop("BAT_TABS", None)
env.pop("BAT_THEME", None)
env.pop("NO_COLOR", None)
env.pop("PAGER", None)
env["COLORTERM"] = "truecolor" # make sure to output 24bit colors
source_dirname = path.basename(path.dirname(source))
source_filename = path.basename(source)
if source_filename in SKIP_FILENAMES:
return
bat_output = subprocess.check_output(
["bat"] + get_options(source) + [source],
stderr=subprocess.PIPE,
env=env,
)
output_dir = path.join(output_basepath, source_dirname)
output_path = path.join(output_dir, source_filename)
os.makedirs(output_dir, exist_ok=True)
with open(output_path, "wb") as output_file:
output_file.write(bat_output)
print("Created '{}'".format(output_path))
def create_highlighted_versions(output_basepath):
root = os.path.dirname(os.path.abspath(__file__))
sources = path.join(root, "source", "*")
source_paths = path.join(root, "source", "*")
for source in glob.glob(path.join(sources, "*")) + glob.glob(
path.join(sources, ".*")
sources = []
for source in glob.glob(path.join(source_paths, "*")) + glob.glob(
path.join(source_paths, ".*")
):
try:
env = os.environ.copy()
env.pop("PAGER", None)
env.pop("BAT_PAGER", None)
env.pop("BAT_CONFIG_PATH", None)
env.pop("BAT_STYLE", None)
env.pop("BAT_THEME", None)
env.pop("BAT_TABS", None)
env["COLORTERM"] = "truecolor" # make sure to output 24bit colors
sources.append((output_basepath, source))
source_dirname = path.basename(path.dirname(source))
source_filename = path.basename(source)
try:
with Pool() as p:
p.map(create_highlighted_version, sources)
except subprocess.CalledProcessError as err:
print(
"=== Error: Could not highlight source file '{}".format(source),
file=sys.stderr,
)
print(
"=== bat stdout:\n{}".format(err.stdout.decode("utf-8")),
file=sys.stderr,
)
print(
"=== bat stderr:\n{}".format(err.stderr.decode("utf-8")),
file=sys.stderr,
)
return False
except FileNotFoundError:
print(
"Error: Could not execute 'bat'. Please make sure that the executable "
"is available on the PATH."
)
return False
if source_filename in SKIP_FILENAMES:
continue
bat_output = subprocess.check_output(
["bat"] + get_options(source) + [source],
stderr=subprocess.PIPE,
env=env,
)
output_dir = path.join(output_basepath, source_dirname)
output_path = path.join(output_dir, source_filename)
os.makedirs(output_dir, exist_ok=True)
with open(output_path, "wb") as output_file:
output_file.write(bat_output)
print("Created '{}'".format(output_path))
except subprocess.CalledProcessError as err:
print(
"=== Error: Could not highlight source file '{}".format(source),
file=sys.stderr,
)
print(
"=== bat stdout:\n{}".format(err.stdout.decode("utf-8")),
file=sys.stderr,
)
print(
"=== bat stderr:\n{}".format(err.stderr.decode("utf-8")),
file=sys.stderr,
)
sys.exit(1)
except FileNotFoundError:
print(
"Error: Could not execute 'bat'. Please make sure that the executable "
"is available on the PATH."
)
sys.exit(1)
return True
if __name__ == "__main__":
@@ -114,4 +129,5 @@ if __name__ == "__main__":
args = parser.parse_args()
create_highlighted_versions(output_basepath=args.output)
if not create_highlighted_versions(output_basepath=args.output):
sys.exit(1)

View File

@@ -0,0 +1,2 @@
custom assets : 0
are : the best

View File

@@ -1,45 +0,0 @@
processor : 0
model name : ARMv7 Processor rev 3 (v7l)
BogoMIPS : 270.00
Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3
processor : 1
model name : ARMv7 Processor rev 3 (v7l)
BogoMIPS : 270.00
Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3
processor : 2
model name : ARMv7 Processor rev 3 (v7l)
BogoMIPS : 270.00
Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3
processor : 3
model name : ARMv7 Processor rev 3 (v7l)
BogoMIPS : 270.00
Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3
Hardware : BCM2711
Revision : b03111
Serial : 1000000095fd9fc5
Model : Raspberry Pi 4 Model B Rev 1.1

View File

@@ -0,0 +1,45 @@
processor : 0
model name : ARMv7 Processor rev 3 (v7l)
BogoMIPS : 270.00
Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3
processor : 1
model name : ARMv7 Processor rev 3 (v7l)
BogoMIPS : 270.00
Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3
processor : 2
model name : ARMv7 Processor rev 3 (v7l)
BogoMIPS : 270.00
Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3
processor : 3
model name : ARMv7 Processor rev 3 (v7l)
BogoMIPS : 270.00
Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3
Hardware : BCM2711
Revision : b03111
Serial : 1000000095fd9fc5
Model : Raspberry Pi 4 Model B Rev 1.1

View File

@@ -0,0 +1 @@
// foo.ucf-dist (Debian ucf backup) should highlight same as foo

View File

@@ -0,0 +1 @@
// foo.ucf-new (Debian ucf backup) should highlight same as foo

View File

@@ -0,0 +1 @@
// foo.ucf-old (Debian ucf backup) should highlight same as foo

View File

@@ -1,53 +0,0 @@
MemTotal: 1004892 kB
MemFree: 109424 kB
MemAvailable: 498032 kB
Buffers: 66360 kB
Cached: 448344 kB
SwapCached: 0 kB
Active: 547076 kB
Inactive: 196864 kB
Active(anon): 249956 kB
Inactive(anon): 7328 kB
Active(file): 297120 kB
Inactive(file): 189536 kB
Unevictable: 18516 kB
Mlocked: 18516 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 276 kB
Writeback: 0 kB
AnonPages: 247780 kB
Mapped: 168472 kB
Shmem: 19860 kB
KReclaimable: 59128 kB
Slab: 108616 kB
SReclaimable: 59128 kB
SUnreclaim: 49488 kB
KernelStack: 2060 kB
PageTables: 4232 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 502444 kB
Committed_AS: 678300 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 10756 kB
VmallocChunk: 0 kB
Percpu: 784 kB
HardwareCorrupted: 0 kB
AnonHugePages: 0 kB
ShmemHugePages: 0 kB
ShmemPmdMapped: 0 kB
FileHugePages: 0 kB
FilePmdMapped: 0 kB
CmaTotal: 0 kB
CmaFree: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 0 kB
DirectMap4k: 118764 kB
DirectMap2M: 929792 kB
DirectMap1G: 0 kB

View File

@@ -0,0 +1,53 @@
MemTotal: 1004892 kB
MemFree: 109424 kB
MemAvailable: 498032 kB
Buffers: 66360 kB
Cached: 448344 kB
SwapCached: 0 kB
Active: 547076 kB
Inactive: 196864 kB
Active(anon): 249956 kB
Inactive(anon): 7328 kB
Active(file): 297120 kB
Inactive(file): 189536 kB
Unevictable: 18516 kB
Mlocked: 18516 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 276 kB
Writeback: 0 kB
AnonPages: 247780 kB
Mapped: 168472 kB
Shmem: 19860 kB
KReclaimable: 59128 kB
Slab: 108616 kB
SReclaimable: 59128 kB
SUnreclaim: 49488 kB
KernelStack: 2060 kB
PageTables: 4232 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 502444 kB
Committed_AS: 678300 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 10756 kB
VmallocChunk: 0 kB
Percpu: 784 kB
HardwareCorrupted: 0 kB
AnonHugePages: 0 kB
ShmemHugePages: 0 kB
ShmemPmdMapped: 0 kB
FileHugePages: 0 kB
FilePmdMapped: 0 kB
CmaTotal: 0 kB
CmaFree: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 0 kB
DirectMap4k: 118764 kB
DirectMap2M: 929792 kB
DirectMap1G: 0 kB

View File

@@ -0,0 +1,56 @@
#lang racket
(require "main.rkt" rackunit)
;; Helper for test cases with multiple outputs
;; See: https://stackoverflow.com/questions/41081395/unit-testing-in-racket-with-multiple-outputs
(define-syntax check-values-equal?
 (syntax-rules ()
 [(_ a b) (check-equal? (call-with-values (thunk a) list) b)]))
;; Named POSIX semaphores
(test-begin
 (define test-sem-name "/test-nix-1")
 ;; Unlink if already exists
 (sem-unlink test-sem-name)
 ;; Open and unlink
 (define test-sem-p (sem-open test-sem-name (+ O_CREAT O_EXCL)))
 (check-not-false test-sem-p)
 (check-not-equal? test-sem-p (void))
 (check-exn exn:fail?
 (lambda () (sem-open test-sem-name (+ O_CREAT O_EXCL)))
 "Permission denied")
 (check-exn exn:fail?
 (lambda () (sem-open test-sem-name (+ O_CREAT O_EXCL))))
 ;; Change values
 (check-equal? (sem-getvalue test-sem-p) 0)
 (sem-post test-sem-p)
 (check-equal? (sem-getvalue test-sem-p) 1)
 (sem-wait test-sem-p)
 (check-equal? (sem-getvalue test-sem-p) 0)
 (sem-post test-sem-p)
 (check-equal? (sem-getvalue test-sem-p) 1)
 (sem-post test-sem-p)
 (check-equal? (sem-getvalue test-sem-p) 2)
 (sem-trywait test-sem-p)
 (check-equal? (sem-getvalue test-sem-p) 2)
 ;; Can't unlink twice
 (check-not-false (sem-unlink test-sem-name))
 (check-false (sem-unlink test-sem-name)))
;; Named POSIX shared memory
(test-begin
 (define test-shm-name "test-nix-mem-1")
 ;; Open and unlink
 (shm-unlink test-shm-name)
 (define test-shm-fd (shm-open test-shm-name (+ O_EXCL O_CREAT O_RDWR) #o644))
 (check > test-shm-fd 0)
 (check-not-false (shm-unlink test-shm-name))
 (check-false (shm-unlink test-shm-name)))

View File

@@ -0,0 +1,2 @@
custom assets : 0
are : the best

View File

@@ -0,0 +1 @@
// foo.ucf-dist (Debian ucf backup) should highlight same as foo

View File

@@ -0,0 +1 @@
// foo.ucf-new (Debian ucf backup) should highlight same as foo

View File

@@ -0,0 +1 @@
// foo.ucf-old (Debian ucf backup) should highlight same as foo

View File

@@ -0,0 +1,56 @@
#lang racket
(require "main.rkt" rackunit)
;; Helper for test cases with multiple outputs
;; See: https://stackoverflow.com/questions/41081395/unit-testing-in-racket-with-multiple-outputs
(define-syntax check-values-equal?
(syntax-rules ()
[(_ a b) (check-equal? (call-with-values (thunk a) list) b)]))
;; Named POSIX semaphores
(test-begin
(define test-sem-name "/test-nix-1")
;; Unlink if already exists
(sem-unlink test-sem-name)
;; Open and unlink
(define test-sem-p (sem-open test-sem-name (+ O_CREAT O_EXCL)))
(check-not-false test-sem-p)
(check-not-equal? test-sem-p (void))
(check-exn exn:fail?
(lambda () (sem-open test-sem-name (+ O_CREAT O_EXCL)))
"Permission denied")
(check-exn exn:fail?
(lambda () (sem-open test-sem-name (+ O_CREAT O_EXCL))))
;; Change values
(check-equal? (sem-getvalue test-sem-p) 0)
(sem-post test-sem-p)
(check-equal? (sem-getvalue test-sem-p) 1)
(sem-wait test-sem-p)
(check-equal? (sem-getvalue test-sem-p) 0)
(sem-post test-sem-p)
(check-equal? (sem-getvalue test-sem-p) 1)
(sem-post test-sem-p)
(check-equal? (sem-getvalue test-sem-p) 2)
(sem-trywait test-sem-p)
(check-equal? (sem-getvalue test-sem-p) 2)
;; Can't unlink twice
(check-not-false (sem-unlink test-sem-name))
(check-false (sem-unlink test-sem-name)))
;; Named POSIX shared memory
(test-begin
(define test-shm-name "test-nix-mem-1")
;; Open and unlink
(shm-unlink test-shm-name)
(define test-shm-fd (shm-open test-shm-name (+ O_EXCL O_CREAT O_RDWR) #o644))
(check > test-shm-fd 0)
(check-not-false (shm-unlink test-shm-name))
(check-false (shm-unlink test-shm-name)))

View File

@@ -26,6 +26,10 @@
onMount(hashchange);
</script>
<script type="text/livescript">
// This block is a regression test for a bat panic when a LiveScript syntax definition is missing
</script>
<style>
main {
position: relative;

View File

@@ -0,0 +1,74 @@
#!/usr/bin/env bash
set -o errexit -o nounset -o pipefail
### ENVIRONMENT
BAT_CONFIG_DIR=$(mktemp -d)
export BAT_CONFIG_DIR
BAT_CACHE_PATH=$(mktemp -d)
export BAT_CACHE_PATH
echo "
BAT_CONFIG_DIR = ${BAT_CONFIG_DIR}
BAT_CACHE_PATH = ${BAT_CACHE_PATH}
"
### HELPER VARS
custom_syntax_args=(
"--language=BatTestCustomAssets"
"tests/syntax-tests/source/BatTestCustomAssets/NoColorsUnlessCustomAssetsAreUsed.battestcustomassets"
)
integrated_syntax_args=(
"--language=Rust"
"examples/simple.rs"
)
### HELPER FUNCTIONS
echo_step() {
echo -e "\n== $1 =="
}
fail_test() {
echo -e "FAIL: $1"
exit 1
}
### TEST STEPS
echo_step "TEST: Make sure 'BatTestCustomAssets' is not part of integrated syntaxes"
bat -f "${custom_syntax_args[@]}" &&
fail_test "EXPECTED: 'unknown syntax' error ACTUAL: no error occured"
echo_step "PREPARE: Install custom syntax 'BatTestCustomAssets'"
custom_syntaxes_dir="$(bat --config-dir)/syntaxes"
mkdir -p "${custom_syntaxes_dir}"
cp -v "tests/syntax-tests/BatTestCustomAssets.sublime-syntax" \
"${custom_syntaxes_dir}/BatTestCustomAssets.sublime-syntax"
echo_step "PREPARE: Build custom assets to enable 'BatTestCustomAssets' syntax"
bat cache --build
echo_step "TEST: 'BatTestCustomAssets' is a known syntax"
bat -f "${custom_syntax_args[@]}" ||
fail_test "EXPECTED: syntax highlighting to work ACTUAL: there was an error"
echo_step "TEST: The 'Rust' syntax is still available"
bat -f "${integrated_syntax_args[@]}" ||
fail_test "EXPECTED: syntax highlighting still works with integrated assets ACTUAL: there was an error"
echo_step "TEST: 'BatTestCustomAssets' is an unknown syntax with --no-custom-assets"
bat -f --no-custom-assets "${custom_syntax_args[@]}" &&
fail_test "EXPECTED: 'unknown syntax' error because of --no-custom-assets ACTUAL: no error occured"
echo_step "TEST: 'bat cache --clear' removes all files"
bat cache --clear
remaining_files=$(ls -A "${BAT_CACHE_PATH}")
[ -z "${remaining_files}" ] ||
fail_test "EXPECTED: no files remain ACTUAL: some files remain:\n${remaining_files}"
echo_step "CLEAN"
rm -rv "${BAT_CONFIG_DIR}" "${BAT_CACHE_PATH}"