From 3a9d574770301f874af55b19b39d2c618966f135 Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Wed, 15 May 2024 14:31:25 +0800 Subject: [PATCH 01/91] Add syntax mapping for `/etc/pacman.conf` --- src/syntax_mapping/builtins/linux/50-pacman.toml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/syntax_mapping/builtins/linux/50-pacman.toml b/src/syntax_mapping/builtins/linux/50-pacman.toml index 655118c5..2f4ee71f 100644 --- a/src/syntax_mapping/builtins/linux/50-pacman.toml +++ b/src/syntax_mapping/builtins/linux/50-pacman.toml @@ -1,3 +1,8 @@ [mappings] -# pacman hooks -"INI" = ["/usr/share/libalpm/hooks/*.hook", "/etc/pacman.d/hooks/*.hook"] +"INI" = [ + # config + "/etc/pacman.conf", + # hooks + "/usr/share/libalpm/hooks/*.hook", + "/etc/pacman.d/hooks/*.hook", +] From f82487daf8d24285c6206af49809992a7371ff18 Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Wed, 15 May 2024 14:35:47 +0800 Subject: [PATCH 02/91] Write changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28e56e34..5a7177c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ - Added auto detect syntax for `.jsonc` #2795 (@mxaddict) - Added auto detect syntax for `.aws/{config,credentials}` #2795 (@mxaddict) - Add syntax mapping for Wireguard config #2874 (@cyqsimon) +- Add syntax mapping for `/etc/pacman.conf` #2961 (@cyqsimon) ## Themes From de697f771775fd91d2352640b60429f124b11818 Mon Sep 17 00:00:00 2001 From: Brenton Bostick Date: Wed, 17 Jul 2024 15:46:38 -0400 Subject: [PATCH 03/91] add CFML syntax --- .gitmodules | 3 ++ assets/syntaxes/02_Extra/CFML | 1 + tests/syntax-tests/highlighted/CFML/test.cfml | 54 +++++++++++++++++++ tests/syntax-tests/source/CFML/test.cfml | 54 +++++++++++++++++++ 4 files changed, 112 insertions(+) create mode 160000 assets/syntaxes/02_Extra/CFML create mode 100644 tests/syntax-tests/highlighted/CFML/test.cfml create mode 100644 tests/syntax-tests/source/CFML/test.cfml diff --git a/.gitmodules b/.gitmodules index 7c8a7724..fe159da8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -260,3 +260,6 @@ [submodule "assets/syntaxes/02_Extra/vscode-wgsl"] path = assets/syntaxes/02_Extra/vscode-wgsl url = https://github.com/PolyMeilex/vscode-wgsl.git +[submodule "assets/syntaxes/02_Extra/CFML"] + path = assets/syntaxes/02_Extra/CFML + url = https://github.com/jcberquist/sublimetext-cfml.git diff --git a/assets/syntaxes/02_Extra/CFML b/assets/syntaxes/02_Extra/CFML new file mode 160000 index 00000000..b91c44a3 --- /dev/null +++ b/assets/syntaxes/02_Extra/CFML @@ -0,0 +1 @@ +Subproject commit b91c44a32e251c20c6359a8d9232287e1b408e6c diff --git a/tests/syntax-tests/highlighted/CFML/test.cfml b/tests/syntax-tests/highlighted/CFML/test.cfml new file mode 100644 index 00000000..4aa8ecdc --- /dev/null +++ b/tests/syntax-tests/highlighted/CFML/test.cfml @@ -0,0 +1,54 @@ +<head>  +<title>Add New Employees  +  +<body>  +<h1>Add New Employees  +  +  +<cfparam name="Form.firstname" default="">  +<cfparam name="Form.lastname" default="">  +<cfparam name="Form.email" default="">  +<cfparam name="Form.phone" default="">  +<cfparam name="Form.department" default="">  +  +<cfif #Form.firstname# eq "">  +<p>Please fill out the form.  +<cfelse>  +<cfoutput>  +<cfscript>  +employee=StructNew();  +employee.firstname = Form.firstname;  +employee.lastname = Form.lastname;  +employee.email = Form.email;  +employee.phone = Form.phone;  +employee.department = Form.department;  +  +  +First name is #StructFind(employee, "firstname")#<br>  +Last name is #StructFind(employee, "lastname")#<br>  +EMail is #StructFind(employee, "email")#<br>  +Phone is #StructFind(employee, "phone")#<br>  +Department is #StructFind(employee, "department")#<br>  +  +  +<cf_addemployee empinfo="#employee#">  +  +  +<hr>  +<form action="newemployee.cfm" method="Post">  +First Name:&nbsp;  +<input name="firstname" type="text" hspace="30" maxlength="30"><br>  +Last Name:&nbsp;  +<input name="lastname" type="text" hspace="30" maxlength="30"><br>  +EMail:&nbsp;  +<input name="email" type="text" hspace="30" maxlength="30"><br>  +Phone:&nbsp;  +<input name="phone" type="text" hspace="20" maxlength="20"><br>  +Department:&nbsp;  +<input name="department" type="text" hspace="30" maxlength="30"><br>  +<input type="Submit" value="OK">  +  +<br>  +  + diff --git a/tests/syntax-tests/source/CFML/test.cfml b/tests/syntax-tests/source/CFML/test.cfml new file mode 100644 index 00000000..f119af76 --- /dev/null +++ b/tests/syntax-tests/source/CFML/test.cfml @@ -0,0 +1,54 @@ + +Add New Employees + + +

Add New Employees

+ + + + + + + + + +

Please fill out the form.

+ + + +employee=StructNew(); +employee.firstname = Form.firstname; +employee.lastname = Form.lastname; +employee.email = Form.email; +employee.phone = Form.phone; +employee.department = Form.department; + + +First name is #StructFind(employee, "firstname")#
+Last name is #StructFind(employee, "lastname")#
+EMail is #StructFind(employee, "email")#
+Phone is #StructFind(employee, "phone")#
+Department is #StructFind(employee, "department")#
+
+ + +
+ +
+
+First Name:  +
+Last Name:  +
+EMail:  +
+Phone:  +
+Department:  +
+ +
+
+ + From 50b01ea8dab019641f1233241d919e002ceab272 Mon Sep 17 00:00:00 2001 From: Brenton Bostick Date: Wed, 17 Jul 2024 16:17:41 -0400 Subject: [PATCH 04/91] update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e361ddb..fb7231b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ - Added auto detect syntax for `.jsonc` #2795 (@mxaddict) - Added auto detect syntax for `.aws/{config,credentials}` #2795 (@mxaddict) - Add syntax mapping for Wireguard config #2874 (@cyqsimon) +- Add support for [CFML](https://www.adobe.com/products/coldfusion-family.html), see #3031 (@brenton-at-pieces) ## Themes From ff71e32c0f1cf70a404329e64a964acee1c084cc Mon Sep 17 00:00:00 2001 From: Michael Vorburger Date: Sun, 21 Jul 2024 23:09:49 +0200 Subject: [PATCH 05/91] Update the Protobuf sobmodule (fixes #2542) --- CHANGELOG.md | 1 + assets/syntaxes/02_Extra/Protobuf | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e361ddb..8bb7a579 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ - Added auto detect syntax for `.jsonc` #2795 (@mxaddict) - Added auto detect syntax for `.aws/{config,credentials}` #2795 (@mxaddict) - Add syntax mapping for Wireguard config #2874 (@cyqsimon) +- Associate `.textproto` files with `ProtoBuf` syntax, see #3038 (@vorburger). ## Themes diff --git a/assets/syntaxes/02_Extra/Protobuf b/assets/syntaxes/02_Extra/Protobuf index 726e21d7..13653315 160000 --- a/assets/syntaxes/02_Extra/Protobuf +++ b/assets/syntaxes/02_Extra/Protobuf @@ -1 +1 @@ -Subproject commit 726e21d74dac23cbb036f2fbbd626decdc954060 +Subproject commit 1365331580b0e4bb86f74d0c599dccc87e7bdacb From c34bdb5e66015e65ceecaa637b9e7316a5db92c0 Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Fri, 26 Jul 2024 16:10:17 +0800 Subject: [PATCH 06/91] Bump time to 0.3.36 (fixes #3043) --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5fc3e1fd..c49ff655 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1347,9 +1347,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", @@ -1368,9 +1368,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", From 1a11ba11e32ab4cdeee3fd9a3afa19a69d210514 Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Fri, 26 Jul 2024 16:18:16 +0800 Subject: [PATCH 07/91] Write changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bb7a579..27a2d806 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Fix handling of inputs with OSC ANSI escape sequences, see #2541 and #2544 (@eth-p) - Fix handling of inputs with combined ANSI color and attribute sequences, see #2185 and #2856 (@eth-p) - Fix panel width when line 10000 wraps, see #2854 (@eth-p) +- Fix compile issue of `time` dependency caused by standard library regression #3045 (@cyqsimon) ## Other From 7bd6cdbebc232585e09bea563c5ee0cdaa50f0f9 Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Fri, 26 Jul 2024 15:37:48 +0800 Subject: [PATCH 08/91] Add syntax mapping for kubernetes config files --- src/syntax_mapping/builtins/linux/50-kubernetes.toml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/syntax_mapping/builtins/linux/50-kubernetes.toml diff --git a/src/syntax_mapping/builtins/linux/50-kubernetes.toml b/src/syntax_mapping/builtins/linux/50-kubernetes.toml new file mode 100644 index 00000000..6a81a35a --- /dev/null +++ b/src/syntax_mapping/builtins/linux/50-kubernetes.toml @@ -0,0 +1,2 @@ +[mappings] +"YAML" = ["/etc/kubernetes/*.conf"] From 319b8868fc93c1fd026a67b58c4192fa535e015c Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Fri, 26 Jul 2024 23:39:27 +0800 Subject: [PATCH 09/91] Write changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27a2d806..a4a6ed16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ - Added auto detect syntax for `.aws/{config,credentials}` #2795 (@mxaddict) - Add syntax mapping for Wireguard config #2874 (@cyqsimon) - Associate `.textproto` files with `ProtoBuf` syntax, see #3038 (@vorburger). +- Add syntax mapping for kubernetes config files #3049 (@cyqsimon) ## Themes From 23fd20048272922c87ea4759f5b7a52ceb8e6d1a Mon Sep 17 00:00:00 2001 From: Michael Vorburger Date: Sun, 21 Jul 2024 22:56:16 +0200 Subject: [PATCH 10/91] Associate JSON-LD `.jsonld` files with `json` syntax (fixes #3036) Use PR instead of Issue # number in CHANGELOG. Clean up (unify) CHANGELOG. --- CHANGELOG.md | 11 ++++++----- src/syntax_mapping/builtins/common/50-json.toml | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36dd079b..aa4c8de0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,11 +49,12 @@ - Upgrade JQ syntax, see #2820 (@dependabot[bot]) - Add syntax mapping for quadman quadlets #2866 (@cyqsimon) - Map containers .conf files to TOML syntax #2867 (@cyqsimon) -- Associate `xsh` files with `xonsh` syntax that is Python, see #2840 (@anki-code). -- Added auto detect syntax for `.jsonc` #2795 (@mxaddict) -- Added auto detect syntax for `.aws/{config,credentials}` #2795 (@mxaddict) -- Add syntax mapping for Wireguard config #2874 (@cyqsimon) -- Associate `.textproto` files with `ProtoBuf` syntax, see #3038 (@vorburger). +- Associate `.xsh` files with `xonsh` syntax that is Python, see #2840 (@anki-code) +- Associate JSON with Comments `.jsonc` with `json` syntax, see #2795 (@mxaddict) +- Associate JSON-LD `.jsonld` files with `json` syntax, see #3037 (@vorburger) +- Associate `.textproto` files with `ProtoBuf` syntax, see #3038 (@vorburger) +- Associate `.aws/{config,credentials}`, see #2795 (@mxaddict) +- Associate Wireguard config `/etc/wireguard/*.conf`, see #2874 (@cyqsimon) - Add support for [CFML](https://www.adobe.com/products/coldfusion-family.html), see #3031 (@brenton-at-pieces) ## Themes diff --git a/src/syntax_mapping/builtins/common/50-json.toml b/src/syntax_mapping/builtins/common/50-json.toml index e604868a..7d33b6fe 100644 --- a/src/syntax_mapping/builtins/common/50-json.toml +++ b/src/syntax_mapping/builtins/common/50-json.toml @@ -1,3 +1,3 @@ # JSON Lines is a simple variation of JSON #2535 [mappings] -"JSON" = ["*.jsonl", "*.jsonc"] +"JSON" = ["*.jsonl", "*.jsonc", "*.jsonld"] From 44ca1e7df15a514ce9afba5bbc2b103042b369b5 Mon Sep 17 00:00:00 2001 From: Koichi Murase Date: Sun, 9 Jun 2024 20:24:00 +0900 Subject: [PATCH 11/91] Add options present in --help to the man page --- assets/manual/bat.1.in | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/assets/manual/bat.1.in b/assets/manual/bat.1.in index b85520da..6d952814 100644 --- a/assets/manual/bat.1.in +++ b/assets/manual/bat.1.in @@ -87,6 +87,10 @@ Set the tab width to T spaces. Use a width of 0 to pass tabs through directly Specify the text\-wrapping mode (*auto*, never, character). The '\-\-terminal\-width' option can be used in addition to control the output width. .HP +\fB\-S\fR, \fB\-\-chop\-long\-lines\fR +.IP +Truncate all lines longer than screen width. Alias for '\-\-wrap=never'. +.HP \fB\-\-terminal\-width\fR .IP Explicitly set the width of the terminal instead of determining it automatically. If @@ -141,6 +145,11 @@ use -m '*.build:Python'. To highlight files named '.myignore' with the Git Ignor syntax, use -m '.myignore:Git Ignore'. Note that the right-hand side is the *name* of the syntax, not a file extension. .HP +\fB\-\-ignored\-suffix\fR +.IP +Ignore extension. For example: 'bat \-\-ignored-suffix ".dev" my_file.json.dev' +will use JSON syntax, and ignore '.dev' +.HP \fB\-\-theme\fR .IP Set the theme for syntax highlighting. Use '\-\-list\-themes' to see all available themes. @@ -151,6 +160,14 @@ export the BAT_THEME environment variable (e.g.: export BAT_THEME="..."). .IP Display a list of supported themes for syntax highlighting. .HP +\fB\-s\fR, \fB\-\-squeeze\-blank\fR +.IP +Squeeze consecutive empty lines into a single empty line. +.HP +\fB\-\-squeeze\-limit\fR +.IP +Set the maximum number of consecutive empty lines to be printed. +.HP \fB\-\-style\fR .IP Configure which elements (line numbers, file headers, grid borders, Git modifications, @@ -184,6 +201,18 @@ Display a list of supported languages for syntax highlighting. This option exists for POSIX\-compliance reasons ('u' is for 'unbuffered'). The output is always unbuffered \- this option is simply ignored. .HP +\fB\-\-diagnostic\fR +.IP +Show diagnostic information for bug reports. +.HP +\fB\-\-acknowledgements\fR +.IP +Show acknowledgements. +.HP +\fB\-\-set\-terminal\-title\fR +.IP +Sets terminal title to filenames when using a pager. +.HP \fB\-h\fR, \fB\-\-help\fR .IP Print this help message. From 43a77a42f81de49b1bce1525140ef1fdd842f961 Mon Sep 17 00:00:00 2001 From: Koichi Murase Date: Sun, 9 Jun 2024 20:40:56 +0900 Subject: [PATCH 12/91] Add options present in --help to Bash completion --- assets/completions/bat.bash.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/assets/completions/bat.bash.in b/assets/completions/bat.bash.in index de8651a8..3ae993c3 100644 --- a/assets/completions/bat.bash.in +++ b/assets/completions/bat.bash.in @@ -76,6 +76,7 @@ _bat() { -m | --map-syntax | \ --ignored-suffix | \ --list-themes | \ + --squeeze-limit | \ --line-range | \ -L | --list-languages | \ --lessopen | \ @@ -157,6 +158,7 @@ _bat() { --diff-context --tabs --wrap + --chop-long-lines --terminal-width --number --color @@ -169,12 +171,15 @@ _bat() { --ignored-suffix --theme --list-themes + --squeeze-blank + --squeeze-limit --style --line-range --list-languages --lessopen --diagnostic --acknowledgements + --set-terminal-title --help --version --cache-dir From 83a15ac05fd60327984a785375dab8d9e7453883 Mon Sep 17 00:00:00 2001 From: Koichi Murase Date: Mon, 10 Jun 2024 07:48:23 +0900 Subject: [PATCH 13/91] Add --cache-dir to Fish completion --- assets/completions/bat.fish.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assets/completions/bat.fish.in b/assets/completions/bat.fish.in index d86ebfe6..788f71b0 100644 --- a/assets/completions/bat.fish.in +++ b/assets/completions/bat.fish.in @@ -133,6 +133,8 @@ set -l tabs_opts ' complete -c $bat -l acknowledgements -d "Print acknowledgements" -n __fish_is_first_arg +complete -c $bat -l cache-dir -f -d "Show bat's cache directory" -n __fish_is_first_arg + complete -c $bat -l color -x -a "$color_opts" -d "When to use colored output" -n __bat_no_excl_args complete -c $bat -l config-dir -f -d "Display location of configuration directory" -n __fish_is_first_arg From b1cdc06430a4c99e09d04870cda01a98799917f5 Mon Sep 17 00:00:00 2001 From: Koichi Murase Date: Mon, 10 Jun 2024 07:52:42 +0900 Subject: [PATCH 14/91] Add --no-* options to Bash/Zsh completions --- assets/completions/bat.bash.in | 3 +++ assets/completions/bat.zsh.in | 1 + 2 files changed, 4 insertions(+) diff --git a/assets/completions/bat.bash.in b/assets/completions/bat.bash.in index 3ae993c3..f314bb25 100644 --- a/assets/completions/bat.bash.in +++ b/assets/completions/bat.bash.in @@ -186,6 +186,9 @@ _bat() { --config-dir --config-file --generate-config-file + --no-config + --no-custom-assets + --no-lessopen " -- "$cur")) return 0 fi diff --git a/assets/completions/bat.zsh.in b/assets/completions/bat.zsh.in index 69caceed..7d03abb3 100644 --- a/assets/completions/bat.zsh.in +++ b/assets/completions/bat.zsh.in @@ -48,6 +48,7 @@ _{{PROJECT_EXECUTABLE}}_main() { default auto full plain changes header header-filename header-filesize grid rule numbers snip' \*{-r+,--line-range=}'[only print the specified line range]:start\:end' '(* -)'{-L,--list-languages}'[display all supported languages]' + -P'[disable paging]' "--no-config[don't use the configuration file]" "--no-custom-assets[don't load custom assets]" '(--no-lessopen)'--lessopen'[enable the $LESSOPEN preprocessor]' From d5aa9d8e0512128e6175897ad3f81d580ef9e007 Mon Sep 17 00:00:00 2001 From: Koichi Murase Date: Mon, 10 Jun 2024 08:13:23 +0900 Subject: [PATCH 15/91] Add options generated by completions to the man page --- assets/manual/bat.1.in | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/assets/manual/bat.1.in b/assets/manual/bat.1.in index 6d952814..2bc0a3a5 100644 --- a/assets/manual/bat.1.in +++ b/assets/manual/bat.1.in @@ -201,6 +201,18 @@ Display a list of supported languages for syntax highlighting. This option exists for POSIX\-compliance reasons ('u' is for 'unbuffered'). The output is always unbuffered \- this option is simply ignored. .HP +\fB\-\-no\-custom\-assets\fR +.IP +Do not load custom assets. +.HP +\fB\-\-config\-dir\fR +.IP +Show bat's configuration directory. +.HP +\fB\-\-cache\-dir\fR +.IP +Show bat's cache directory. +.HP \fB\-\-diagnostic\fR .IP Show diagnostic information for bug reports. @@ -241,6 +253,20 @@ location of the configuration file. To generate a default configuration file, call: \fB{{PROJECT_EXECUTABLE}} --generate-config-file\fR + +These are related options: +.HP +\fB\-\-config\-file\fR +.IP +Show path to the configuration file. +.HP +\fB\-\-generate-config\-file\fR +.IP +Generates a default configuration file. +.HP +\fB\-\-no\-config\fR +.IP +Do not use the configuration file. .SH "ADDING CUSTOM LANGUAGES" {{PROJECT_EXECUTABLE}} supports Sublime Text \fB.sublime-syntax\fR language files, and can be customized to add additional languages to your local installation. To do this, add the \fB.sublime-syntax\fR language @@ -287,6 +313,16 @@ To temporarily disable the preprocessor if it is enabled by default, call: \fB{{PROJECT_EXECUTABLE}} --no-lessopen\fR +These are related options: +.HP +\fB\-\-lessopen\fR +.IP +Enable the $LESSOPEN preprocessor. +.HP +\fB\-\-no\-lessopen\fR +.IP +Disable the $LESSOPEN preprocessor if enabled (overrides --lessopen) +.PP For more information, see the "INPUT PREPROCESSOR" section of less(1). .SH "MORE INFORMATION" From 9a650e827915c7e310e73ffe1822247a5bda7064 Mon Sep 17 00:00:00 2001 From: Koichi Murase Date: Sun, 9 Jun 2024 21:16:31 +0900 Subject: [PATCH 16/91] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa4c8de0..610af60a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ - Update the Lisp syntax, see #2970 (@ccqpein) - Use bat's ANSI iterator during tab expansion, see #2998 (@eth-p) - Support 'statically linked binary' for aarch64 in 'Release' page, see #2992 (@tzq0301) +- Update options in shell completions and the man page of `bat`, see #2995 (@akinomyoga) ## Syntaxes From b74c125c43bb28e0b2b8f5188dac5d7348deae95 Mon Sep 17 00:00:00 2001 From: "Ethan P." Date: Sat, 6 Apr 2024 14:54:10 -0700 Subject: [PATCH 17/91] Support merging `--style` arguments The `overrides_with` clap builder option was removed because it interfered with the matcher's ability to retain all occurrences of `--style`. The behavior it covered is expressed within the new `forced_style_components` function. --- src/bin/bat/app.rs | 76 ++++++++++------ src/bin/bat/clap_app.rs | 31 ++----- src/style.rs | 194 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 250 insertions(+), 51 deletions(-) diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index 0d2600b2..c95c2bd1 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -2,11 +2,13 @@ use std::collections::HashSet; use std::env; use std::io::IsTerminal; use std::path::{Path, PathBuf}; +use std::str::FromStr; use crate::{ clap_app, config::{get_args_from_config_file, get_args_from_env_opts_var, get_args_from_env_vars}, }; +use bat::style::StyleComponentList; use bat::StripAnsiMode; use clap::ArgMatches; @@ -86,7 +88,6 @@ impl App { // .. and the rest at the end cli_args.for_each(|a| args.push(a)); - args }; @@ -364,34 +365,57 @@ impl App { Ok(file_input) } + fn forced_style_components(&self) -> Option { + // No components if `--decorations=never``. + if self + .matches + .get_one::("decorations") + .map(|s| s.as_str()) + == Some("never") + { + return Some(StyleComponents(HashSet::new())); + } + + // Only line numbers if `--number`. + if self.matches.get_flag("number") { + return Some(StyleComponents(HashSet::from([ + StyleComponent::LineNumbers, + ]))); + } + + // Plain if `--plain` is specified at least once. + if self.matches.get_count("plain") > 0 { + return Some(StyleComponents(HashSet::from([StyleComponent::Plain]))); + } + + // Default behavior. + None + } + fn style_components(&self) -> Result { let matches = &self.matches; - let mut styled_components = StyleComponents( - if matches.get_one::("decorations").map(|s| s.as_str()) == Some("never") { - HashSet::new() - } else if matches.get_flag("number") { - [StyleComponent::LineNumbers].iter().cloned().collect() - } else if 0 < matches.get_count("plain") { - [StyleComponent::Plain].iter().cloned().collect() - } else { - matches - .get_one::("style") - .map(|styles| { - styles - .split(',') - .map(|style| style.parse::()) - .filter_map(|style| style.ok()) - .collect::>() - }) - .unwrap_or_else(|| vec![StyleComponent::Default]) + let mut styled_components = match self.forced_style_components() { + Some(forced_components) => forced_components, + + // Parse the `--style` arguments and merge them. + None if matches.contains_id("style") => { + let lists = matches + .get_many::("style") + .expect("styles present") + .map(|v| StyleComponentList::from_str(v)) + .collect::>>()?; + + StyleComponentList::to_components(lists, self.interactive_output) + } + + // Use the default. + None => StyleComponents(HashSet::from_iter( + StyleComponent::Default + .components(self.interactive_output) .into_iter() - .map(|style| style.components(self.interactive_output)) - .fold(HashSet::new(), |mut acc, components| { - acc.extend(components.iter().cloned()); - acc - }) - }, - ); + .cloned(), + )), + }; // If `grid` is set, remove `rule` as it is a subset of `grid`, and print a warning. if styled_components.grid() && styled_components.0.remove(&StyleComponent::Rule) { diff --git a/src/bin/bat/clap_app.rs b/src/bin/bat/clap_app.rs index e70b1a5b..8e79174b 100644 --- a/src/bin/bat/clap_app.rs +++ b/src/bin/bat/clap_app.rs @@ -1,9 +1,11 @@ +use bat::style::StyleComponentList; use clap::{ crate_name, crate_version, value_parser, Arg, ArgAction, ArgGroup, ColorChoice, Command, }; use once_cell::sync::Lazy; use std::env; use std::path::{Path, PathBuf}; +use std::str::FromStr; static VERSION: Lazy = Lazy::new(|| { #[cfg(feature = "bugreport")] @@ -419,34 +421,13 @@ pub fn build_app(interactive_output: bool) -> Command { .arg( Arg::new("style") .long("style") + .action(ArgAction::Append) .value_name("components") - .overrides_with("style") - .overrides_with("plain") - .overrides_with("number") // Cannot use claps built in validation because we have to turn off clap's delimiters .value_parser(|val: &str| { - let mut invalid_vals = val.split(',').filter(|style| { - !&[ - "auto", - "full", - "default", - "plain", - "header", - "header-filename", - "header-filesize", - "grid", - "rule", - "numbers", - "snip", - #[cfg(feature = "git")] - "changes", - ].contains(style) - }); - - if let Some(invalid) = invalid_vals.next() { - Err(format!("Unknown style, '{invalid}'")) - } else { - Ok(val.to_owned()) + match StyleComponentList::from_str(val) { + Err(err) => Err(err), + Ok(_) => Ok(val.to_owned()), } }) .help( diff --git a/src/style.rs b/src/style.rs index 652b3743..8c2cc05d 100644 --- a/src/style.rs +++ b/src/style.rs @@ -138,3 +138,197 @@ impl StyleComponents { self.0.clear(); } } + +#[derive(Debug, PartialEq)] +enum ComponentAction { + Override, + Add, + Remove, +} + +impl ComponentAction { + fn extract_from_str(string: &str) -> (ComponentAction, &str) { + match string.chars().next() { + Some('-') => (ComponentAction::Remove, string.strip_prefix('-').unwrap()), + Some('+') => (ComponentAction::Add, string.strip_prefix('+').unwrap()), + _ => (ComponentAction::Override, string), + } + } +} + +/// A list of [StyleComponent] that can be parsed from a string. +pub struct StyleComponentList(Vec<(ComponentAction, StyleComponent)>); + +impl StyleComponentList { + fn expand_into(&self, components: &mut HashSet, interactive_terminal: bool) { + for (action, component) in self.0.iter() { + let subcomponents = component.components(interactive_terminal); + + use ComponentAction::*; + match action { + Override | Add => components.extend(subcomponents), + Remove => components.retain(|c| !subcomponents.contains(c)), + } + } + } + + /// Returns `true` if any component in the list was not prefixed with `+` or `-`. + fn contains_override(&self) -> bool { + self.0.iter().any(|(a, _)| *a == ComponentAction::Override) + } + + /// Combines multiple [StyleComponentList]s into a single [StyleComponents] set. + /// + /// ## Precedence + /// The most recent list will take precedence and override all previous lists + /// unless it only contains components prefixed with `-` or `+`. When this + /// happens, the list's components will be merged into the previous list. + /// + /// ## Example + /// ```text + /// [numbers,grid] + [header,changes] -> [header,changes] + /// [numbers,grid] + [+header,-grid] -> [numbers,header] + /// ``` + pub fn to_components( + lists: impl IntoIterator, + interactive_terminal: bool, + ) -> StyleComponents { + StyleComponents( + lists + .into_iter() + .fold(HashSet::new(), |mut components, list| { + if list.contains_override() { + components.clear(); + } + + list.expand_into(&mut components, interactive_terminal); + components + }), + ) + } +} + +impl Default for StyleComponentList { + fn default() -> Self { + StyleComponentList(vec![(ComponentAction::Override, StyleComponent::Default)]) + } +} + +impl FromStr for StyleComponentList { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(StyleComponentList( + s.split(",") + .map(|s| ComponentAction::extract_from_str(s)) // If the component starts with "-", it's meant to be removed + .map(|(a, s)| Ok((a, StyleComponent::from_str(s)?))) + .collect::>>()?, + )) + } +} + +#[cfg(test)] +mod test { + use std::collections::HashSet; + use std::str::FromStr; + + use super::ComponentAction::*; + use super::StyleComponent::*; + use super::StyleComponentList; + + #[test] + pub fn style_component_list_parse() { + assert_eq!( + StyleComponentList::from_str("grid,+numbers,snip,-snip,header") + .expect("no error") + .0, + vec![ + (Override, Grid), + (Add, LineNumbers), + (Override, Snip), + (Remove, Snip), + (Override, Header), + ] + ); + + assert!(StyleComponentList::from_str("not-a-component").is_err()); + assert!(StyleComponentList::from_str("grid,not-a-component").is_err()); + assert!(StyleComponentList::from_str("numbers,-not-a-component").is_err()); + } + + #[test] + pub fn style_component_list_to_components() { + assert_eq!( + StyleComponentList::to_components( + vec![StyleComponentList::from_str("grid,numbers").expect("no error")], + false + ) + .0, + HashSet::from([Grid, LineNumbers]) + ); + } + + #[test] + pub fn style_component_list_to_components_removes_negated() { + assert_eq!( + StyleComponentList::to_components( + vec![StyleComponentList::from_str("grid,numbers,-grid").expect("no error")], + false + ) + .0, + HashSet::from([LineNumbers]) + ); + } + + #[test] + pub fn style_component_list_to_components_expands_subcomponents() { + assert_eq!( + StyleComponentList::to_components( + vec![StyleComponentList::from_str("full").expect("no error")], + false + ) + .0, + HashSet::from_iter(Full.components(true).to_owned()) + ); + } + + #[test] + pub fn style_component_list_expand_negates_subcomponents() { + assert!(!StyleComponentList::to_components( + vec![StyleComponentList::from_str("full,-numbers").expect("no error")], + true + ) + .numbers()); + } + + #[test] + pub fn style_component_list_to_components_precedence_overrides_previous_lists() { + assert_eq!( + StyleComponentList::to_components( + vec![ + StyleComponentList::from_str("grid").expect("no error"), + StyleComponentList::from_str("numbers").expect("no error"), + ], + false + ) + .0, + HashSet::from([LineNumbers]) + ); + } + + #[test] + pub fn style_component_list_to_components_precedence_merges_previous_lists() { + assert_eq!( + StyleComponentList::to_components( + vec![ + StyleComponentList::from_str("grid,header").expect("no error"), + StyleComponentList::from_str("-grid").expect("no error"), + StyleComponentList::from_str("+numbers").expect("no error"), + ], + false + ) + .0, + HashSet::from([HeaderFilename, LineNumbers]) + ); + } +} From 6e91ba83b7663d22f88788d89595513e55b4ec0f Mon Sep 17 00:00:00 2001 From: "Ethan P." Date: Sat, 6 Apr 2024 15:04:20 -0700 Subject: [PATCH 18/91] Update clap/docs for merging `--style` arguments --- README.md | 10 ++++++++++ doc/long-help.txt | 6 ++++++ src/bin/bat/clap_app.rs | 6 ++++++ 3 files changed, 22 insertions(+) diff --git a/README.md b/README.md index 1e42e501..016fe834 100644 --- a/README.md +++ b/README.md @@ -515,6 +515,16 @@ and line numbers but no grid and no file header. Set the `BAT_STYLE` environment variable to make these changes permanent or use `bat`s [configuration file](https://github.com/sharkdp/bat#configuration-file). +>[!tip] +> If you specify a default style in `bat`'s config file, you can change which components +> are displayed during a single run of `bat` using the `--style` command-line argument. +> By prefixing a component with `+` or `-`, it can be added or removed from the current style. +> +> For example, if your config contains `--style=full,-snip`, you can run bat with +> `--style=-grid,+snip` to remove the grid and add back the `snip` component. +> Or, if you want to override the styles completely, you use `--style=numbers` to +> only show the line numbers. + ### Adding new syntaxes / language definitions Should you find that a particular syntax is not available within `bat`, you can follow these diff --git a/doc/long-help.txt b/doc/long-help.txt index d9cdce39..2b03490f 100644 --- a/doc/long-help.txt +++ b/doc/long-help.txt @@ -134,6 +134,12 @@ Options: set a default style, add the '--style=".."' option to the configuration file or export the BAT_STYLE environment variable (e.g.: export BAT_STYLE=".."). + When styles are specified in multiple places, the "nearest" set of styles take precedence. + The command-line arguments are the highest priority, followed by the BAT_STYLE environment + variable, and then the configuration file. If any set of styles consists entirely of + components prefixed with "+" or "-", it will modify the previous set of styles instead of + replacing them. + By default, the following components are enabled: changes, grid, header-filename, numbers, snip diff --git a/src/bin/bat/clap_app.rs b/src/bin/bat/clap_app.rs index 8e79174b..33dde980 100644 --- a/src/bin/bat/clap_app.rs +++ b/src/bin/bat/clap_app.rs @@ -442,6 +442,12 @@ pub fn build_app(interactive_output: bool) -> Command { pre-defined style ('full'). To set a default style, add the \ '--style=\"..\"' option to the configuration file or export the \ BAT_STYLE environment variable (e.g.: export BAT_STYLE=\"..\").\n\n\ + When styles are specified in multiple places, the \"nearest\" set \ + of styles take precedence. The command-line arguments are the highest \ + priority, followed by the BAT_STYLE environment variable, and then \ + the configuration file. If any set of styles consists entirely of \ + components prefixed with \"+\" or \"-\", it will modify the \ + previous set of styles instead of replacing them.\n\n\ By default, the following components are enabled:\n \ changes, grid, header-filename, numbers, snip\n\n\ Possible values:\n\n \ From 93b25d75a0db2bc5fef4e147a92af55373781046 Mon Sep 17 00:00:00 2001 From: "Ethan P." Date: Sat, 6 Apr 2024 17:05:46 -0700 Subject: [PATCH 19/91] Join env var options with "=" instead of " " Joining them with a space was causing certain styles (e.g. `-grid`) to be misinterpreted as a separate option. --- src/bin/bat/config.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/bin/bat/config.rs b/src/bin/bat/config.rs index 9e38dfa4..6fa18f09 100644 --- a/src/bin/bat/config.rs +++ b/src/bin/bat/config.rs @@ -146,8 +146,11 @@ pub fn get_args_from_env_vars() -> Vec { ("--style", "BAT_STYLE"), ] .iter() - .filter_map(|(flag, key)| env::var(key).ok().map(|var| [flag.to_string(), var])) - .flatten() + .filter_map(|(flag, key)| { + env::var(key) + .ok() + .map(|var| [flag.to_string(), var].join("=")) + }) .map(|a| a.into()) .collect() } From 180a77ee99e8b5bc9f4cdb017788deb595968d5f Mon Sep 17 00:00:00 2001 From: "Ethan P." Date: Sat, 6 Apr 2024 17:21:03 -0700 Subject: [PATCH 20/91] Add integration tests for merging styles A huge thanks to @einfachIrgendwer0815 for helping me make sure these tests work under the MSRV CI job. --- tests/integration_tests.rs | 68 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index d6009361..8df4327c 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -2810,3 +2810,71 @@ fn strip_ansi_auto_does_not_strip_ansi_when_plain_text_by_option() { assert!(output.contains("\x1B[33mYellow")) } + +// Tests that style components can be removed with `-component`. +#[test] +fn style_components_can_be_removed() { + bat() + .arg({ + #[cfg(not(feature = "git"))] + { + "--style=full,-grid" + } + #[cfg(feature = "git")] + { + "--style=full,-grid,-changes" + } + }) + .arg("--decorations=always") + .arg("--color=never") + .write_stdin("test") + .assert() + .success() + .stdout(" STDIN\n Size: -\n 1 test\n") + .stderr(""); +} + +// Tests that style components are chosen based on the rightmost `--style` argument. +#[test] +fn style_components_can_be_overidden() { + bat() + .arg("--style=full") + .arg("--style=header,numbers") + .arg("--decorations=always") + .arg("--color=never") + .write_stdin("test") + .assert() + .success() + .stdout(" STDIN\n 1 test\n") + .stderr(""); +} + +// Tests that style components can be merged across multiple `--style` arguments. +#[test] +fn style_components_will_merge() { + bat() + .arg("--style=header,grid") + .arg("--style=-grid,+numbers") + .arg("--decorations=always") + .arg("--color=never") + .write_stdin("test") + .assert() + .success() + .stdout(" STDIN\n 1 test\n") + .stderr(""); +} + +// Tests that style components can be merged with the `BAT_STYLE` environment variable. +#[test] +fn style_components_will_merge_with_env_var() { + bat() + .env("BAT_STYLE", "header,grid") + .arg("--style=-grid,+numbers") + .arg("--decorations=always") + .arg("--color=never") + .write_stdin("test") + .assert() + .success() + .stdout(" STDIN\n 1 test\n") + .stderr(""); +} From aa3ec109b74eb6e23d1fe15ade3087ca8430eeb2 Mon Sep 17 00:00:00 2001 From: "Ethan P." Date: Mon, 10 Jun 2024 17:39:55 -0700 Subject: [PATCH 21/91] First StyleComponentList should remove from 'auto' style. This happens when there are no `--style` arguments other than the one passed in as a command line argument. Prior to this change, removing a style component (e.g. `--style=-numbers`) would remove the component from an empty style component set, resulting in no styles at all. That behaviour was less intuitive than the new behaviour, which starts out with the default components and removes the line numbers. --- src/bin/bat/app.rs | 2 +- src/style.rs | 54 +++++++++++++++++++++++++++++++++++----------- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index c95c2bd1..d6628668 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -405,7 +405,7 @@ impl App { .map(|v| StyleComponentList::from_str(v)) .collect::>>()?; - StyleComponentList::to_components(lists, self.interactive_output) + StyleComponentList::to_components(lists, self.interactive_output, true) } // Use the default. diff --git a/src/style.rs b/src/style.rs index 8c2cc05d..b8d8b09f 100644 --- a/src/style.rs +++ b/src/style.rs @@ -189,22 +189,27 @@ impl StyleComponentList { /// [numbers,grid] + [header,changes] -> [header,changes] /// [numbers,grid] + [+header,-grid] -> [numbers,header] /// ``` + /// + /// ## Parameters + /// - `with_default`: If true, the styles lists will build upon the StyleComponent::Auto style. pub fn to_components( lists: impl IntoIterator, interactive_terminal: bool, + with_default: bool, ) -> StyleComponents { - StyleComponents( - lists - .into_iter() - .fold(HashSet::new(), |mut components, list| { - if list.contains_override() { - components.clear(); - } + let mut components: HashSet = HashSet::new(); + if with_default { + components.extend(StyleComponent::Auto.components(interactive_terminal)) + } - list.expand_into(&mut components, interactive_terminal); - components - }), - ) + StyleComponents(lists.into_iter().fold(components, |mut components, list| { + if list.contains_override() { + components.clear(); + } + + list.expand_into(&mut components, interactive_terminal); + components + })) } } @@ -233,6 +238,7 @@ mod test { use std::str::FromStr; use super::ComponentAction::*; + use super::StyleComponent; use super::StyleComponent::*; use super::StyleComponentList; @@ -261,6 +267,7 @@ mod test { assert_eq!( StyleComponentList::to_components( vec![StyleComponentList::from_str("grid,numbers").expect("no error")], + false, false ) .0, @@ -273,6 +280,7 @@ mod test { assert_eq!( StyleComponentList::to_components( vec![StyleComponentList::from_str("grid,numbers,-grid").expect("no error")], + false, false ) .0, @@ -285,6 +293,7 @@ mod test { assert_eq!( StyleComponentList::to_components( vec![StyleComponentList::from_str("full").expect("no error")], + false, false ) .0, @@ -296,7 +305,8 @@ mod test { pub fn style_component_list_expand_negates_subcomponents() { assert!(!StyleComponentList::to_components( vec![StyleComponentList::from_str("full,-numbers").expect("no error")], - true + true, + false ) .numbers()); } @@ -309,6 +319,7 @@ mod test { StyleComponentList::from_str("grid").expect("no error"), StyleComponentList::from_str("numbers").expect("no error"), ], + false, false ) .0, @@ -325,10 +336,29 @@ mod test { StyleComponentList::from_str("-grid").expect("no error"), StyleComponentList::from_str("+numbers").expect("no error"), ], + false, false ) .0, HashSet::from([HeaderFilename, LineNumbers]) ); } + + #[test] + pub fn style_component_list_default_builds_on_auto() { + assert_eq!( + StyleComponentList::to_components( + vec![StyleComponentList::from_str("-numbers").expect("no error"),], + true, + true + ) + .0, + { + let mut expected: HashSet = HashSet::new(); + expected.extend(Auto.components(true)); + expected.remove(&LineNumbers); + expected + } + ); + } } From 39684b85ad6c14819a7780c2feef37a888ac7f43 Mon Sep 17 00:00:00 2001 From: "Ethan P." Date: Sat, 6 Apr 2024 18:10:01 -0700 Subject: [PATCH 22/91] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 610af60a..ea47b58f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - `PrettyPrinter::squeeze_empty_lines` to support line squeezing for bat as a library, see #1441 (@eth-p) and #2665 (@einfachIrgendwer0815) - Syntax highlighting for JavaScript files that start with `#!/usr/bin/env bun` #2913 (@sharunkumar) - `bat --strip-ansi={never,always,auto}` to remove ANSI escape sequences from bat's input, see #2999 (@eth-p) +- Add or remove individual style components without replacing all styles #2929 (@eth-p) ## Bugfixes From 13317b0a3b3814aebdb7bda14ee7a6d35d3edb45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 02:47:46 +0000 Subject: [PATCH 23/91] Bump expect-test from 1.4.1 to 1.5.0 (#3055) Bumps [expect-test](https://github.com/rust-analyzer/expect-test) from 1.4.1 to 1.5.0. - [Changelog](https://github.com/rust-analyzer/expect-test/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-analyzer/expect-test/compare/v1.4.1...v1.5.0) --- updated-dependencies: - dependency-name: expect-test dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c49ff655..e260d57e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -463,9 +463,9 @@ dependencies = [ [[package]] name = "expect-test" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d9eafeadd538e68fb28016364c9732d78e420b9ff8853fa5e4058861e9f8d3" +checksum = "9e0be0a561335815e06dab7c62e50353134c796e7a6155402a64bcff66b6a5e0" dependencies = [ "dissimilar", "once_cell", diff --git a/Cargo.toml b/Cargo.toml index 6170905a..c939c500 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,7 +90,7 @@ plist = "1.6.0" [dev-dependencies] assert_cmd = "2.0.12" -expect-test = "1.4.1" +expect-test = "1.5.0" serial_test = { version = "2.0.0", default-features = false } predicates = "3.1.0" wait-timeout = "0.2.0" From d8d0b77568fe903b2d3e92e58b74b0f70d7fd636 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 03:14:26 +0000 Subject: [PATCH 24/91] Bump indexmap from 2.2.6 to 2.3.0 (#3057) Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.2.6 to 2.3.0. - [Changelog](https://github.com/indexmap-rs/indexmap/blob/master/RELEASES.md) - [Commits](https://github.com/indexmap-rs/indexmap/compare/2.2.6...2.3.0) --- updated-dependencies: - dependency-name: indexmap dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e260d57e..5a4b616d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -647,9 +647,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", "hashbrown 0.14.1", diff --git a/Cargo.toml b/Cargo.toml index c939c500..720e629b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -102,7 +102,7 @@ nix = { version = "0.26.4", default-features = false, features = ["term"] } [build-dependencies] anyhow = "1.0.86" -indexmap = { version = "2.2.6", features = ["serde"] } +indexmap = { version = "2.3.0", features = ["serde"] } itertools = "0.13.0" once_cell = "1.18" regex = "1.10.2" From b662fec214daaa77a67f08d8fbd2e81915d6e0bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 03:28:12 +0000 Subject: [PATCH 25/91] Bump ansi_colours from 1.2.2 to 1.2.3 (#3058) Bumps [ansi_colours](https://github.com/mina86/ansi_colours) from 1.2.2 to 1.2.3. - [Commits](https://github.com/mina86/ansi_colours/commits) --- updated-dependencies: - dependency-name: ansi_colours dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5a4b616d..1168501a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ dependencies = [ [[package]] name = "ansi_colours" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a1558bd2075d341b9ca698ec8eb6fcc55a746b1fc4255585aad5b141d918a80" +checksum = "14eec43e0298190790f41679fe69ef7a829d2a2ddd78c8c00339e84710e435fe" dependencies = [ "rgb", ] From 1423dd944066f060aa814817b2867097f2bac4d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Tue, 16 Apr 2024 14:43:16 +0200 Subject: [PATCH 26/91] Choose theme based on the terminal's color scheme --- src/lib.rs | 1 + src/theme.rs | 334 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 335 insertions(+) create mode 100644 src/theme.rs diff --git a/src/lib.rs b/src/lib.rs index 23c4a800..5699d87a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,6 +49,7 @@ pub(crate) mod printer; pub mod style; pub(crate) mod syntax_mapping; mod terminal; +pub mod theme; mod vscreen; pub(crate) mod wrapping; diff --git a/src/theme.rs b/src/theme.rs new file mode 100644 index 00000000..b1607140 --- /dev/null +++ b/src/theme.rs @@ -0,0 +1,334 @@ +use std::convert::Infallible; +use std::str::FromStr; + +/// Chooses an appropriate theme or falls back to a default theme +/// based on the user-provided options and the color scheme of the terminal. +pub fn theme(options: ThemeOptions, detector: &dyn ColorSchemeDetector) -> String { + // Implementation note: This function is mostly pure (i.e. it has no side effects) for the sake of testing. + // All the side effects (e.g. querying the terminal for its colors) are performed in the detector. + if let Some(theme) = options.theme { + theme.into_theme(ColorScheme::default()) + } else { + let color_scheme = detect(options.detect_color_scheme, detector).unwrap_or_default(); + choose_theme(options, color_scheme) + .map(|t| t.into_theme(color_scheme)) + .unwrap_or_else(|| default_theme(color_scheme).to_owned()) + } +} + +fn choose_theme(options: ThemeOptions, color_scheme: ColorScheme) -> Option { + match color_scheme { + ColorScheme::Dark => options.dark_theme, + ColorScheme::Light => options.light_theme, + } +} + +fn detect(when: DetectColorScheme, detector: &dyn ColorSchemeDetector) -> Option { + let should_detect = match when { + DetectColorScheme::Auto => detector.should_detect(), + DetectColorScheme::Always => true, + DetectColorScheme::Never => false, + }; + should_detect.then(|| detector.detect()).flatten() +} + +const fn default_theme(color_scheme: ColorScheme) -> &'static str { + match color_scheme { + ColorScheme::Dark => "Monokai Extended", + ColorScheme::Light => "Monokai Extended Light", + } +} + +/// Options for configuring the theme used for syntax highlighting. +#[derive(Debug, Default)] +pub struct ThemeOptions { + /// Always use this theme regardless of the terminal's background color. + pub theme: Option, + /// The theme to use in case the terminal uses a dark background with light text. + pub dark_theme: Option, + /// The theme to use in case the terminal uses a light background with dark text. + pub light_theme: Option, + /// Detect whether or not the terminal is dark or light by querying for its colors. + pub detect_color_scheme: DetectColorScheme, +} + +/// The name of a theme or the default theme. +#[derive(Debug)] +pub enum ThemeRequest { + Named(String), + Default, +} + +impl FromStr for ThemeRequest { + type Err = Infallible; + + fn from_str(s: &str) -> Result { + if s == "default" { + Ok(ThemeRequest::Default) + } else { + Ok(ThemeRequest::Named(s.to_owned())) + } + } +} + +impl ThemeRequest { + fn into_theme(self, color_scheme: ColorScheme) -> String { + match self { + ThemeRequest::Named(t) => t, + ThemeRequest::Default => default_theme(color_scheme).to_owned(), + } + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub enum DetectColorScheme { + /// Only query the terminal for its colors when appropriate (e.g. when the the output is not redirected). + #[default] + Auto, + /// Always query the terminal for its colors. + Always, + /// Never query the terminal for its colors. + Never, +} + +/// The color scheme used to pick a fitting theme. Defaults to [`ColorScheme::Dark`]. +#[derive(Default, Copy, Clone)] +pub enum ColorScheme { + #[default] + Dark, + Light, +} + +pub trait ColorSchemeDetector { + fn should_detect(&self) -> bool; + + fn detect(&self) -> Option; +} + +#[cfg(test)] +impl ColorSchemeDetector for Option { + fn should_detect(&self) -> bool { + true + } + + fn detect(&self) -> Option { + *self + } +} + +#[cfg(test)] +mod tests { + use super::ColorScheme::*; + use super::DetectColorScheme::*; + use super::*; + use std::cell::Cell; + use std::iter; + + mod color_scheme_detection { + use super::*; + + #[test] + fn not_called_for_never() { + let detector = DetectorStub::should_detect(Some(Dark)); + let options = ThemeOptions { + detect_color_scheme: Never, + ..Default::default() + }; + _ = theme(options, &detector); + assert!(!detector.was_called.get()); + } + + #[test] + fn called_for_always() { + let detectors = [ + DetectorStub::should_detect(Some(Dark)), + DetectorStub::should_not_detect(), + ]; + for detector in detectors { + let options = ThemeOptions { + detect_color_scheme: Always, + ..Default::default() + }; + _ = theme(options, &detector); + assert!(detector.was_called.get()); + } + } + + #[test] + fn called_for_auto_if_should_detect() { + let detector = DetectorStub::should_detect(Some(Dark)); + _ = theme(ThemeOptions::default(), &detector); + assert!(detector.was_called.get()); + } + + #[test] + fn not_called_for_auto_if_not_should_detect() { + let detector = DetectorStub::should_not_detect(); + _ = theme(ThemeOptions::default(), &detector); + assert!(!detector.was_called.get()); + } + } + + mod precedence { + use super::*; + + #[test] + fn theme_is_preferred_over_light_or_dark_themes() { + for color_scheme in optional(color_schemes()) { + for options in [ + ThemeOptions { + theme: Some(ThemeRequest::Named("Theme".to_string())), + ..Default::default() + }, + ThemeOptions { + theme: Some(ThemeRequest::Named("Theme".to_string())), + dark_theme: Some(ThemeRequest::Named("Dark Theme".to_string())), + light_theme: Some(ThemeRequest::Named("Light Theme".to_string())), + ..Default::default() + }, + ] { + let detector = ConstantDetector(color_scheme); + assert_eq!("Theme", theme(options, &detector)); + } + } + } + + #[test] + fn detector_is_not_called_if_theme_is_present() { + let options = ThemeOptions { + theme: Some(ThemeRequest::Named("Theme".to_string())), + ..Default::default() + }; + let detector = DetectorStub::should_detect(Some(Dark)); + _ = theme(options, &detector); + assert!(!detector.was_called.get()); + } + } + + mod default_theme { + use super::*; + + #[test] + fn dark_if_unable_to_detect_color_scheme() { + let detector = ConstantDetector(None); + assert_eq!( + default_theme(ColorScheme::Dark), + theme(ThemeOptions::default(), &detector) + ); + } + + // For backwards compatibility, if the default theme is requested + // explicitly through BAT_THEME, we always pick the default dark theme. + #[test] + fn dark_if_requested_explicitly_through_theme() { + for color_scheme in optional(color_schemes()) { + let options = ThemeOptions { + theme: Some(ThemeRequest::Default), + ..Default::default() + }; + let detector = ConstantDetector(color_scheme); + assert_eq!(default_theme(ColorScheme::Dark), theme(options, &detector)); + } + } + + #[test] + fn varies_depending_on_color_scheme() { + for color_scheme in color_schemes() { + for options in [ + ThemeOptions::default(), + ThemeOptions { + dark_theme: Some(ThemeRequest::Default), + light_theme: Some(ThemeRequest::Default), + ..Default::default() + }, + ] { + let detector = ConstantDetector(Some(color_scheme)); + assert_eq!(default_theme(color_scheme), theme(options, &detector)); + } + } + } + } + + mod choosing { + use super::*; + + #[test] + fn chooses_dark_theme_if_dark_or_unknown() { + for color_scheme in [Some(Dark), None] { + let options = ThemeOptions { + dark_theme: Some(ThemeRequest::Named("Dark".to_string())), + light_theme: Some(ThemeRequest::Named("Light".to_string())), + ..Default::default() + }; + let detector = ConstantDetector(color_scheme); + assert_eq!("Dark", theme(options, &detector)); + } + } + + #[test] + fn chooses_light_theme_if_light() { + let options = ThemeOptions { + dark_theme: Some(ThemeRequest::Named("Dark".to_string())), + light_theme: Some(ThemeRequest::Named("Light".to_string())), + ..Default::default() + }; + let detector = ConstantDetector(Some(ColorScheme::Light)); + assert_eq!("Light", theme(options, &detector)); + } + } + + struct DetectorStub { + should_detect: bool, + color_scheme: Option, + was_called: Cell, + } + + impl DetectorStub { + fn should_detect(color_scheme: Option) -> Self { + DetectorStub { + should_detect: true, + color_scheme, + was_called: Cell::default(), + } + } + + fn should_not_detect() -> Self { + DetectorStub { + should_detect: false, + color_scheme: None, + was_called: Cell::default(), + } + } + } + + impl ColorSchemeDetector for DetectorStub { + fn should_detect(&self) -> bool { + self.should_detect + } + + fn detect(&self) -> Option { + self.was_called.set(true); + self.color_scheme + } + } + + struct ConstantDetector(Option); + + impl ColorSchemeDetector for ConstantDetector { + fn should_detect(&self) -> bool { + true + } + + fn detect(&self) -> Option { + self.0 + } + } + + fn optional(value: impl Iterator) -> impl Iterator> { + value.map(Some).chain(iter::once(None)) + } + + fn color_schemes() -> impl Iterator { + [Dark, Light].into_iter() + } +} From de796392cfba07a86d48ca9099e053b176ac916b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Tue, 16 Apr 2024 14:43:18 +0200 Subject: [PATCH 27/91] Deprecate old `default_theme` function --- src/assets.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/assets.rs b/src/assets.rs index 9655553d..53414366 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -90,6 +90,7 @@ impl HighlightingAssets { /// /// See and /// for more context. + #[deprecated(note = "use bat::theme::theme instead")] pub fn default_theme() -> &'static str { #[cfg(not(target_os = "macos"))] { From cda363a3f742fe4ef112ffcd4a61bb756332c16c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Tue, 16 Apr 2024 14:43:20 +0200 Subject: [PATCH 28/91] Use `default_theme()` function from theme module --- src/assets.rs | 27 ++++++++------------------- src/theme.rs | 2 +- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/assets.rs b/src/assets.rs index 53414366..857f416b 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -13,6 +13,7 @@ use crate::error::*; use crate::input::{InputReader, OpenedInput}; use crate::syntax_mapping::ignored_suffixes::IgnoredSuffixes; use crate::syntax_mapping::MappingTarget; +use crate::theme::{default_theme, ColorScheme}; use crate::{bat_warning, SyntaxMapping}; use lazy_theme_set::LazyThemeSet; @@ -94,33 +95,18 @@ impl HighlightingAssets { pub fn default_theme() -> &'static str { #[cfg(not(target_os = "macos"))] { - Self::default_dark_theme() + default_theme(ColorScheme::Dark) } #[cfg(target_os = "macos")] { if macos_dark_mode_active() { - Self::default_dark_theme() + default_theme(ColorScheme::Dark) } else { - Self::default_light_theme() + default_theme(ColorScheme::Light) } } } - /** - * The default theme that looks good on a dark background. - */ - fn default_dark_theme() -> &'static str { - "Monokai Extended" - } - - /** - * The default theme that looks good on a light background. - */ - #[cfg(target_os = "macos")] - fn default_light_theme() -> &'static str { - "Monokai Extended Light" - } - pub fn from_cache(cache_path: &Path) -> Result { Ok(HighlightingAssets::new( SerializedSyntaxSet::FromFile(cache_path.join("syntaxes.bin")), @@ -249,7 +235,10 @@ impl HighlightingAssets { bat_warning!("Unknown theme '{}', using default.", theme) } self.get_theme_set() - .get(self.fallback_theme.unwrap_or_else(Self::default_theme)) + .get( + self.fallback_theme + .unwrap_or_else(|| default_theme(ColorScheme::Dark)), + ) .expect("something is very wrong if the default theme is missing") } } diff --git a/src/theme.rs b/src/theme.rs index b1607140..8ce75e1f 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -32,7 +32,7 @@ fn detect(when: DetectColorScheme, detector: &dyn ColorSchemeDetector) -> Option should_detect.then(|| detector.detect()).flatten() } -const fn default_theme(color_scheme: ColorScheme) -> &'static str { +pub(crate) const fn default_theme(color_scheme: ColorScheme) -> &'static str { match color_scheme { ColorScheme::Dark => "Monokai Extended", ColorScheme::Light => "Monokai Extended Light", From cea45e05f3e3f2fea3eed6cbd8e2ac4bd1036fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Thu, 18 Jul 2024 15:38:28 +0200 Subject: [PATCH 29/91] Expose new theme selection in CLI --- Cargo.lock | 51 +++++++++++++++++++++++-- Cargo.toml | 3 ++ doc/long-help.txt | 36 +++++++++++++++-- doc/short-help.txt | 6 +++ src/bin/bat/app.rs | 85 ++++++++++++++++++++++++++++++++++------- src/bin/bat/clap_app.rs | 53 ++++++++++++++++++++++++- src/bin/bat/config.rs | 2 + src/theme.rs | 24 ++++++------ 8 files changed, 227 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1168501a..6d413de6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,6 +149,7 @@ dependencies = [ "shell-words", "syntect", "tempfile", + "terminal-colorsaurus", "thiserror", "toml", "unicode-width", @@ -620,6 +621,12 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + [[package]] name = "home" version = "0.5.9" @@ -688,9 +695,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.149" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libgit2-sys" @@ -755,9 +762,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" @@ -768,6 +775,18 @@ dependencies = [ "adler", ] +[[package]] +name = "mio" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4929e1f84c5e54c3ec6141cd5d8b5a5c055f031f80cf78f2072920173cb4d880" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys 0.52.0", +] + [[package]] name = "nix" version = "0.26.4" @@ -1309,6 +1328,30 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal-colorsaurus" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f99bb1dc5cde9eada5a8f466641240f9d5b9f55291d675df4160b097fbfa42e" +dependencies = [ + "cfg-if", + "libc", + "memchr", + "mio", + "terminal-trx", +] + +[[package]] +name = "terminal-trx" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d4c86910e10c782a02d3b7606de43cf7ebd80e1fafdca8e49a0db2b0d4611f0" +dependencies = [ + "cfg-if", + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "terminal_size" version = "0.3.0" diff --git a/Cargo.toml b/Cargo.toml index 720e629b..affce17f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ application = [ "bugreport", "build-assets", "git", + "detect-color-scheme", "minimal-application", ] # Mainly for developers that want to iterate quickly @@ -35,6 +36,7 @@ git = ["git2"] # Support indicating git modifications paging = ["shell-words", "grep-cli"] # Support applying a pager on the output lessopen = ["run_script", "os_str_bytes"] # Support $LESSOPEN preprocessor build-assets = ["syntect/yaml-load", "syntect/plist-load", "regex", "walkdir"] +detect-color-scheme = ["dep:terminal-colorsaurus"] # You need to use one of these if you depend on bat as a library: regex-onig = ["syntect/regex-onig"] # Use the "oniguruma" regex engine @@ -68,6 +70,7 @@ bytesize = { version = "1.3.0" } encoding_rs = "0.8.34" os_str_bytes = { version = "~7.0", optional = true } run_script = { version = "^0.10.1", optional = true} +terminal-colorsaurus = { version = "0.4", optional = true } [dependencies.git2] version = "0.18" diff --git a/doc/long-help.txt b/doc/long-help.txt index 2b03490f..86e9a532 100644 --- a/doc/long-help.txt +++ b/doc/long-help.txt @@ -109,9 +109,39 @@ Options: 'bat --ignored-suffix ".dev" my_file.json.dev' will use JSON syntax, and ignore '.dev' --theme - Set the theme for syntax highlighting. Use '--list-themes' to see all available themes. To - set a default theme, add the '--theme="..."' option to the configuration file or export - the BAT_THEME environment variable (e.g.: export BAT_THEME="..."). + Set the theme for syntax highlighting. Note that this option overrides '--theme-dark' and + '--theme-light'. Use '--list-themes' to see all available themes. To set a default theme, + add the '--theme="..."' option to the configuration file or export the BAT_THEME + environment variable (e.g.: export BAT_THEME="..."). + + --detect-color-scheme + Specify when to query the terminal for its colors in order to pick an appropriate syntax + highlighting theme. Use '--theme-light' and '--theme-dark' (or the environment variables + BAT_THEME_LIGHT and BAT_THEME_DARK) to configure which themes are picked. You may also use + '--theme' to set a theme that is used regardless of the terminal's colors. + + Possible values: + * auto (default): + Only query the terminals colors if the output is not redirected. This is to prevent + race conditions with pagers such as less. + * never + Never query the terminal for its colors and assume that the terminal has a dark + background. + * always + Always query the terminal for its colors, regardless of whether or not the output is + redirected. + + --theme-light + Sets the theme name for syntax highlighting used when the terminal uses a light + background. Use '--list-themes' to see all available themes. To set a default theme, add + the '--theme-light="..." option to the configuration file or export the BAT_THEME_LIGHT + environment variable (e.g. export BAT_THEME_LIGHT="..."). + + --theme-dark + Sets the theme name for syntax highlighting used when the terminal uses a dark background. + Use '--list-themes' to see all available themes. To set a default theme, add the + '--theme-dark="..." option to the configuration file or export the BAT_THEME_DARK + environment variable (e.g. export BAT_THEME_DARK="..."). --list-themes Display a list of supported themes for syntax highlighting. diff --git a/doc/short-help.txt b/doc/short-help.txt index 305bbf3d..3e369229 100644 --- a/doc/short-help.txt +++ b/doc/short-help.txt @@ -41,6 +41,12 @@ Options: Use the specified syntax for files matching the glob pattern ('*.cpp:C++'). --theme Set the color theme for syntax highlighting. + --detect-color-scheme + Specify when to query the terminal for its colors. + --theme-light + Sets the color theme for syntax highlighting used for light backgrounds. + --theme-dark + Sets the color theme for syntax highlighting used for dark backgrounds. --list-themes Display all supported highlighting themes. -s, --squeeze-blank diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index d6628668..d9e1662b 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -9,6 +9,9 @@ use crate::{ config::{get_args_from_config_file, get_args_from_env_opts_var, get_args_from_env_vars}, }; use bat::style::StyleComponentList; +use bat::theme::{ + theme, ColorScheme, ColorSchemeDetector, DetectColorScheme, ThemeOptions, ThemeRequest, +}; use bat::StripAnsiMode; use clap::ArgMatches; @@ -16,7 +19,6 @@ use console::Term; use crate::input::{new_file_input, new_stdin_input}; use bat::{ - assets::HighlightingAssets, bat_warning, config::{Config, VisibleLines}, error::*, @@ -254,18 +256,7 @@ impl App { Some("auto") => StripAnsiMode::Auto, _ => unreachable!("other values for --strip-ansi are not allowed"), }, - theme: self - .matches - .get_one::("theme") - .map(String::from) - .map(|s| { - if s == "default" { - String::from(HighlightingAssets::default_theme()) - } else { - s - } - }) - .unwrap_or_else(|| String::from(HighlightingAssets::default_theme())), + theme: theme(self.theme_options(), &TerminalColorSchemeDetector), visible_lines: match self.matches.try_contains_id("diff").unwrap_or_default() && self.matches.get_flag("diff") { @@ -424,4 +415,72 @@ impl App { Ok(styled_components) } + + fn theme_options(&self) -> ThemeOptions { + let theme = self + .matches + .get_one::("theme") + .map(|t| ThemeRequest::from_str(t).unwrap()); + let theme_dark = self + .matches + .get_one::("theme-dark") + .map(|t| ThemeRequest::from_str(t).unwrap()); + let theme_light = self + .matches + .get_one::("theme-light") + .map(|t| ThemeRequest::from_str(t).unwrap()); + let detect_color_scheme = match self + .matches + .get_one::("detect-color-scheme") + .map(|s| s.as_str()) + { + Some("auto") => DetectColorScheme::Auto, + Some("never") => DetectColorScheme::Never, + Some("always") => DetectColorScheme::Always, + _ => unreachable!("other values for --detect-color-scheme are not allowed"), + }; + ThemeOptions { + theme, + theme_dark, + theme_light, + detect_color_scheme, + } + } +} + +struct TerminalColorSchemeDetector; + +#[cfg(feature = "detect-color-scheme")] +impl ColorSchemeDetector for TerminalColorSchemeDetector { + fn should_detect(&self) -> bool { + // Querying the terminal for its colors via OSC 10 / OSC 11 requires "exclusive" access + // since we read/write from the terminal and enable/disable raw mode. + // This causes race conditions with pagers such as less when they are attached to the + // same terminal as us. + // + // This is usually only an issue when the output is manually piped to a pager. + // For example: `bat Cargo.toml | less`. + // Otherwise, if we start the pager ourselves, then there's no race condition + // since the pager is started *after* the color is detected. + std::io::stdout().is_terminal() + } + + fn detect(&self) -> Option { + use terminal_colorsaurus::{color_scheme, ColorScheme as ColorsaurusScheme, QueryOptions}; + match color_scheme(QueryOptions::default()).ok()? { + ColorsaurusScheme::Dark => Some(ColorScheme::Dark), + ColorsaurusScheme::Light => Some(ColorScheme::Light), + } + } +} + +#[cfg(not(feature = "detect-color-scheme"))] +impl ColorSchemeDetector for TerminalColorSchemeDetector { + fn should_detect(&self) -> bool { + false + } + + fn detect(&self) -> Option { + None + } } diff --git a/src/bin/bat/clap_app.rs b/src/bin/bat/clap_app.rs index 33dde980..6abdab9c 100644 --- a/src/bin/bat/clap_app.rs +++ b/src/bin/bat/clap_app.rs @@ -375,13 +375,64 @@ pub fn build_app(interactive_output: bool) -> Command { .overrides_with("theme") .help("Set the color theme for syntax highlighting.") .long_help( - "Set the theme for syntax highlighting. Use '--list-themes' to \ + "Set the theme for syntax highlighting. Note that this option overrides \ + '--theme-dark' and '--theme-light'. Use '--list-themes' to \ see all available themes. To set a default theme, add the \ '--theme=\"...\"' option to the configuration file or export the \ BAT_THEME environment variable (e.g.: export \ BAT_THEME=\"...\").", ), ) + .arg( + Arg::new("detect-color-scheme") + .long("detect-color-scheme") + .overrides_with("detect-color-scheme") + .value_name("when") + .value_parser(["auto", "never", "always"]) + .default_value("auto") + .hide_default_value(true) + .help("Specify when to query the terminal for its colors.") + .long_help( + "Specify when to query the terminal for its colors \ + in order to pick an appropriate syntax highlighting theme. \ + Use '--theme-light' and '--theme-dark' (or the environment variables \ + BAT_THEME_LIGHT and BAT_THEME_DARK) to configure which themes are picked. \ + You may also use '--theme' to set a theme that is used regardless of the terminal's colors.\n\n\ + Possible values:\n\ + * auto (default):\n \ + Only query the terminals colors if the output is not redirected. \ + This is to prevent race conditions with pagers such as less.\n\ + * never\n \ + Never query the terminal for its colors \ + and assume that the terminal has a dark background.\n\ + * always\n \ + Always query the terminal for its colors, \ + regardless of whether or not the output is redirected."), + ) + .arg( + Arg::new("theme-light") + .long("theme-light") + .overrides_with("theme-light") + .value_name("theme") + .help("Sets the color theme for syntax highlighting used for light backgrounds.") + .long_help( + "Sets the theme name for syntax highlighting used when the terminal uses a light background. \ + Use '--list-themes' to see all available themes. To set a default theme, add the \ + '--theme-light=\"...\" option to the configuration file or export the BAT_THEME_LIGHT \ + environment variable (e.g. export BAT_THEME_LIGHT=\"...\")."), + ) + .arg( + Arg::new("theme-dark") + .long("theme-dark") + .overrides_with("theme-dark") + .value_name("theme") + .help("Sets the color theme for syntax highlighting used for dark backgrounds.") + .long_help( + "Sets the theme name for syntax highlighting used when the terminal uses a dark background. \ + Use '--list-themes' to see all available themes. To set a default theme, add the \ + '--theme-dark=\"...\" option to the configuration file or export the BAT_THEME_DARK \ + environment variable (e.g. export BAT_THEME_DARK=\"...\")."), + ) .arg( Arg::new("list-themes") .long("list-themes") diff --git a/src/bin/bat/config.rs b/src/bin/bat/config.rs index 6fa18f09..f1ec3d53 100644 --- a/src/bin/bat/config.rs +++ b/src/bin/bat/config.rs @@ -141,6 +141,8 @@ pub fn get_args_from_env_vars() -> Vec { [ ("--tabs", "BAT_TABS"), ("--theme", "BAT_THEME"), + ("--theme-dark", "BAT_THEME_DARK"), + ("--theme-light", "BAT_THEME_LIGHT"), ("--pager", "BAT_PAGER"), ("--paging", "BAT_PAGING"), ("--style", "BAT_STYLE"), diff --git a/src/theme.rs b/src/theme.rs index 8ce75e1f..ec3e3d34 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -18,8 +18,8 @@ pub fn theme(options: ThemeOptions, detector: &dyn ColorSchemeDetector) -> Strin fn choose_theme(options: ThemeOptions, color_scheme: ColorScheme) -> Option { match color_scheme { - ColorScheme::Dark => options.dark_theme, - ColorScheme::Light => options.light_theme, + ColorScheme::Dark => options.theme_dark, + ColorScheme::Light => options.theme_light, } } @@ -45,9 +45,9 @@ pub struct ThemeOptions { /// Always use this theme regardless of the terminal's background color. pub theme: Option, /// The theme to use in case the terminal uses a dark background with light text. - pub dark_theme: Option, + pub theme_dark: Option, /// The theme to use in case the terminal uses a light background with dark text. - pub light_theme: Option, + pub theme_light: Option, /// Detect whether or not the terminal is dark or light by querying for its colors. pub detect_color_scheme: DetectColorScheme, } @@ -182,8 +182,8 @@ mod tests { }, ThemeOptions { theme: Some(ThemeRequest::Named("Theme".to_string())), - dark_theme: Some(ThemeRequest::Named("Dark Theme".to_string())), - light_theme: Some(ThemeRequest::Named("Light Theme".to_string())), + theme_dark: Some(ThemeRequest::Named("Dark Theme".to_string())), + theme_light: Some(ThemeRequest::Named("Light Theme".to_string())), ..Default::default() }, ] { @@ -237,8 +237,8 @@ mod tests { for options in [ ThemeOptions::default(), ThemeOptions { - dark_theme: Some(ThemeRequest::Default), - light_theme: Some(ThemeRequest::Default), + theme_dark: Some(ThemeRequest::Default), + theme_light: Some(ThemeRequest::Default), ..Default::default() }, ] { @@ -256,8 +256,8 @@ mod tests { fn chooses_dark_theme_if_dark_or_unknown() { for color_scheme in [Some(Dark), None] { let options = ThemeOptions { - dark_theme: Some(ThemeRequest::Named("Dark".to_string())), - light_theme: Some(ThemeRequest::Named("Light".to_string())), + theme_dark: Some(ThemeRequest::Named("Dark".to_string())), + theme_light: Some(ThemeRequest::Named("Light".to_string())), ..Default::default() }; let detector = ConstantDetector(color_scheme); @@ -268,8 +268,8 @@ mod tests { #[test] fn chooses_light_theme_if_light() { let options = ThemeOptions { - dark_theme: Some(ThemeRequest::Named("Dark".to_string())), - light_theme: Some(ThemeRequest::Named("Light".to_string())), + theme_dark: Some(ThemeRequest::Named("Dark".to_string())), + theme_light: Some(ThemeRequest::Named("Light".to_string())), ..Default::default() }; let detector = ConstantDetector(Some(ColorScheme::Light)); From 9a1bfe946dafcd095895737b7b395dd636114737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Tue, 16 Apr 2024 14:43:24 +0200 Subject: [PATCH 30/91] Update completions and man page --- assets/completions/_bat.ps1.in | 3 +++ assets/completions/bat.bash.in | 9 ++++++-- assets/completions/bat.fish.in | 7 +++++++ assets/completions/bat.zsh.in | 3 +++ assets/manual/bat.1.in | 38 +++++++++++++++++++++++++++++++--- 5 files changed, 55 insertions(+), 5 deletions(-) diff --git a/assets/completions/_bat.ps1.in b/assets/completions/_bat.ps1.in index c0c151e1..5635dea2 100644 --- a/assets/completions/_bat.ps1.in +++ b/assets/completions/_bat.ps1.in @@ -32,11 +32,14 @@ Register-ArgumentCompleter -Native -CommandName '{{PROJECT_EXECUTABLE}}' -Script [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('--detect-color-scheme', 'detect-color-scheme', [CompletionResultType]::ParameterName, 'When to detect the terminal''s color scheme (*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('--theme-dark', 'theme', [CompletionResultType]::ParameterName, 'Set the color theme for syntax highlighting for dark backgrounds.') + [CompletionResult]::new('--theme-light', 'theme', [CompletionResultType]::ParameterName, 'Set the color theme for syntax highlighting for light backgrounds.') [CompletionResult]::new('--style', 'style', [CompletionResultType]::ParameterName, 'Comma-separated list of style elements to display (*default*, auto, full, plain, changes, header, header-filename, header-filesize, 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.') diff --git a/assets/completions/bat.bash.in b/assets/completions/bat.bash.in index f314bb25..a02c5a04 100644 --- a/assets/completions/bat.bash.in +++ b/assets/completions/bat.bash.in @@ -100,7 +100,7 @@ _bat() { COMPREPLY=($(compgen -W "auto never character" -- "$cur")) return 0 ;; - --color | --decorations | --paging) + --color | --decorations | --paging | --detect-color-scheme) COMPREPLY=($(compgen -W "auto never always" -- "$cur")) return 0 ;; @@ -112,7 +112,9 @@ _bat() { COMPREPLY=($(compgen -c -- "$cur")) return 0 ;; - --theme) + --theme | \ + --theme-dark | \ + --theme-light) local IFS=$'\n' COMPREPLY=($(compgen -W "$("$1" --list-themes)" -- "$cur")) __bat_escape_completions @@ -164,12 +166,15 @@ _bat() { --color --italic-text --decorations + --detect-color-scheme --force-colorization --paging --pager --map-syntax --ignored-suffix --theme + --theme-dark + --theme-light --list-themes --squeeze-blank --squeeze-limit diff --git a/assets/completions/bat.fish.in b/assets/completions/bat.fish.in index 788f71b0..7bd0ebca 100644 --- a/assets/completions/bat.fish.in +++ b/assets/completions/bat.fish.in @@ -99,6 +99,7 @@ set -l color_opts ' ' set -l decorations_opts $color_opts set -l paging_opts $color_opts +set -l detect_color_scheme_opts $color_opts # Include some examples so we can indicate the default. set -l pager_opts ' @@ -143,6 +144,8 @@ complete -c $bat -l config-file -f -d "Display location of configuration file" - complete -c $bat -l decorations -x -a "$decorations_opts" -d "When to use --style decorations" -n __bat_no_excl_args +complete -c $bat -l detect-color-scheme -x -a "$detect_color_scheme_opts" -d "When to detect the terminal's color scheme" -n __bat_no_excl_args + complete -c $bat -l diagnostic -d "Print diagnostic info for bug reports" -n __fish_is_first_arg complete -c $bat -s d -l diff -d "Only show lines with Git changes" -n __bat_no_excl_args @@ -205,6 +208,10 @@ complete -c $bat -l terminal-width -x -d "Set terminal , +, or -< complete -c $bat -l theme -x -a "(command $bat --list-themes | command cat)" -d "Set the syntax highlighting theme" -n __bat_no_excl_args +complete -c $bat -l theme-dark -x -a "(command $bat --list-themes | command cat)" -d "Set the syntax highlighting theme for dark backgrounds" -n __bat_no_excl_args + +complete -c $bat -l theme-light -x -a "(command $bat --list-themes | command cat)" -d "Set the syntax highlighting theme for light backgrounds" -n __bat_no_excl_args + complete -c $bat -s V -l version -f -d "Show version information" -n __fish_is_first_arg complete -c $bat -l wrap -x -a "$wrap_opts" -d "Text-wrapping mode" -n __bat_no_excl_args diff --git a/assets/completions/bat.zsh.in b/assets/completions/bat.zsh.in index 7d03abb3..4a598437 100644 --- a/assets/completions/bat.zsh.in +++ b/assets/completions/bat.zsh.in @@ -40,9 +40,12 @@ _{{PROJECT_EXECUTABLE}}_main() { --color='[specify when to use colors]:when:(auto never always)' --italic-text='[use italics in output]:when:(always never)' --decorations='[specify when to show the decorations]:when:(auto never always)' + --detect-color-scheme="[specify when to detect the terminal's color scheme]:when:(auto never always)" --paging='[specify when to use the pager]:when:(auto never always)' '(-m --map-syntax)'{-m+,--map-syntax=}'[map a glob pattern to an existing syntax name]: :->syntax-maps' '(--theme)'--theme='[set the color theme for syntax highlighting]:theme:->themes' + '(--theme-dark)'--theme-dark='[set the color theme for syntax highlighting for dark backgrounds]:theme:->themes' + '(--theme-light)'--theme-light='[set the color theme for syntax highlighting for light backgrounds]:theme:->themes' '(: --list-themes --list-languages -L)'--list-themes'[show all supported highlighting themes]' --style='[comma-separated list of style elements to display]: : _values "style [default]" default auto full plain changes header header-filename header-filesize grid rule numbers snip' diff --git a/assets/manual/bat.1.in b/assets/manual/bat.1.in index 2bc0a3a5..5dcabe2d 100644 --- a/assets/manual/bat.1.in +++ b/assets/manual/bat.1.in @@ -117,6 +117,24 @@ Specify when to use the decorations that have been specified via '\-\-style'. Th automatic mode only enables decorations if an interactive terminal is detected. Possible values: *auto*, never, always. .HP +\fB\-\-detect\-color\-scheme\fR +.IP +Specify when to query the terminal for its colors in order to pick an appropriate syntax +highlighting theme. Use \fB\-\-theme-light\fP and \fB\-\-theme-dark\fP (or the environment variables +\fBBAT_THEME_LIGHT\fP and \fBBAT_THEME_DARK\fP) to configure which themes are picked. You can also use +\fP\-\-theme\fP to set a theme that is used regardless of the terminal's colors. +.IP +\fI\fP can be one of: +.RS +.IP "\fBauto\fP" +Only query the terminals colors if the output is not redirected. This is to prevent +race conditions with pagers such as less. +.IP "never" +Never query the terminal for its colors and assume that the terminal has a dark background. +.IP "always" +Always query the terminal for its colors, regardless of whether or not the output is redirected. +.RE +.HP \fB\-f\fR, \fB\-\-force\-colorization\fR .IP Alias for '--decorations=always --color=always'. This is useful \ @@ -152,9 +170,23 @@ will use JSON syntax, and ignore '.dev' .HP \fB\-\-theme\fR .IP -Set the theme for syntax highlighting. Use '\-\-list\-themes' to see all available themes. -To set a default theme, add the '\-\-theme="..."' option to the configuration file or -export the BAT_THEME environment variable (e.g.: export BAT_THEME="..."). +Set the theme for syntax highlighting. Use \fB\-\-list\-themes\fP to see all available themes. +To set a default theme, add the \fB\-\-theme="..."\fP option to the configuration file or +export the \fBBAT_THEME\fP environment variable (e.g.: \fBexport BAT_THEME="..."\fP). +.HP +\fB\-\-theme\-dark\fR +.IP +Sets the theme name for syntax highlighting used when the terminal uses a dark background. +To set a default theme, add the \fB\-\-theme-dark="..."\fP option to the configuration file or +export the \fBBAT_THEME_DARK\fP environment variable (e.g. \fBexport BAT_THEME_DARK="..."\fP). +This option is ignored if \fB\-\-theme\fP option is set. +.HP +\fB\-\-theme\-light\fR +.IP +Sets the theme name for syntax highlighting used when the terminal uses a dark background. +To set a default theme, add the \fB\-\-theme-dark="..."\fP option to the configuration file or +export the \fBBAT_THEME_LIGHT\fP environment variable (e.g. \fBexport BAT_THEME_LIGHT="..."\fP). +This option is ignored if \fB\-\-theme\fP option is set. .HP \fB\-\-list\-themes\fR .IP From 14ce668a1d043b9ecdbdf99e6832923ba3576031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Tue, 16 Apr 2024 14:43:27 +0200 Subject: [PATCH 31/91] Add generated powershell completion to ignore list --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a3ea8cff..fbfe6ac6 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ **/*.rs.bk # Generated files +/assets/completions/_bat.ps1 /assets/completions/bat.bash /assets/completions/bat.fish /assets/completions/bat.zsh From ff81cfd584ca873fcad186193247af51c4f71627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Tue, 16 Apr 2024 14:43:31 +0200 Subject: [PATCH 32/91] Move actual detection into library --- Cargo.toml | 3 +- src/bin/bat/app.rs | 43 +-------------- src/theme.rs | 133 +++++++++++++++++++++++++++++++-------------- 3 files changed, 95 insertions(+), 84 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index affce17f..62ae693c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,14 +13,13 @@ edition = '2021' rust-version = "1.70" [features] -default = ["application"] +default = ["application", "detect-color-scheme"] # Feature required for bat the application. Should be disabled when depending on # bat as a library. application = [ "bugreport", "build-assets", "git", - "detect-color-scheme", "minimal-application", ] # Mainly for developers that want to iterate quickly diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index d9e1662b..711ab1c6 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -9,9 +9,7 @@ use crate::{ config::{get_args_from_config_file, get_args_from_env_opts_var, get_args_from_env_vars}, }; use bat::style::StyleComponentList; -use bat::theme::{ - theme, ColorScheme, ColorSchemeDetector, DetectColorScheme, ThemeOptions, ThemeRequest, -}; +use bat::theme::{theme, DetectColorScheme, ThemeOptions, ThemeRequest}; use bat::StripAnsiMode; use clap::ArgMatches; @@ -256,7 +254,7 @@ impl App { Some("auto") => StripAnsiMode::Auto, _ => unreachable!("other values for --strip-ansi are not allowed"), }, - theme: theme(self.theme_options(), &TerminalColorSchemeDetector), + theme: theme(self.theme_options()), visible_lines: match self.matches.try_contains_id("diff").unwrap_or_default() && self.matches.get_flag("diff") { @@ -447,40 +445,3 @@ impl App { } } } - -struct TerminalColorSchemeDetector; - -#[cfg(feature = "detect-color-scheme")] -impl ColorSchemeDetector for TerminalColorSchemeDetector { - fn should_detect(&self) -> bool { - // Querying the terminal for its colors via OSC 10 / OSC 11 requires "exclusive" access - // since we read/write from the terminal and enable/disable raw mode. - // This causes race conditions with pagers such as less when they are attached to the - // same terminal as us. - // - // This is usually only an issue when the output is manually piped to a pager. - // For example: `bat Cargo.toml | less`. - // Otherwise, if we start the pager ourselves, then there's no race condition - // since the pager is started *after* the color is detected. - std::io::stdout().is_terminal() - } - - fn detect(&self) -> Option { - use terminal_colorsaurus::{color_scheme, ColorScheme as ColorsaurusScheme, QueryOptions}; - match color_scheme(QueryOptions::default()).ok()? { - ColorsaurusScheme::Dark => Some(ColorScheme::Dark), - ColorsaurusScheme::Light => Some(ColorScheme::Light), - } - } -} - -#[cfg(not(feature = "detect-color-scheme"))] -impl ColorSchemeDetector for TerminalColorSchemeDetector { - fn should_detect(&self) -> bool { - false - } - - fn detect(&self) -> Option { - None - } -} diff --git a/src/theme.rs b/src/theme.rs index ec3e3d34..2491f9bd 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -1,35 +1,13 @@ +//! Utilities for choosing an appropriate theme for syntax highlighting. + use std::convert::Infallible; +use std::io::IsTerminal as _; use std::str::FromStr; /// Chooses an appropriate theme or falls back to a default theme /// based on the user-provided options and the color scheme of the terminal. -pub fn theme(options: ThemeOptions, detector: &dyn ColorSchemeDetector) -> String { - // Implementation note: This function is mostly pure (i.e. it has no side effects) for the sake of testing. - // All the side effects (e.g. querying the terminal for its colors) are performed in the detector. - if let Some(theme) = options.theme { - theme.into_theme(ColorScheme::default()) - } else { - let color_scheme = detect(options.detect_color_scheme, detector).unwrap_or_default(); - choose_theme(options, color_scheme) - .map(|t| t.into_theme(color_scheme)) - .unwrap_or_else(|| default_theme(color_scheme).to_owned()) - } -} - -fn choose_theme(options: ThemeOptions, color_scheme: ColorScheme) -> Option { - match color_scheme { - ColorScheme::Dark => options.theme_dark, - ColorScheme::Light => options.theme_light, - } -} - -fn detect(when: DetectColorScheme, detector: &dyn ColorSchemeDetector) -> Option { - let should_detect = match when { - DetectColorScheme::Auto => detector.should_detect(), - DetectColorScheme::Always => true, - DetectColorScheme::Never => false, - }; - should_detect.then(|| detector.detect()).flatten() +pub fn theme(options: ThemeOptions) -> String { + theme_from_detector(options, &TerminalColorSchemeDetector) } pub(crate) const fn default_theme(color_scheme: ColorScheme) -> &'static str { @@ -40,6 +18,7 @@ pub(crate) const fn default_theme(color_scheme: ColorScheme) -> &'static str { } /// Options for configuring the theme used for syntax highlighting. +/// Used together with [`theme`]. #[derive(Debug, Default)] pub struct ThemeOptions { /// Always use this theme regardless of the terminal's background color. @@ -48,7 +27,7 @@ pub struct ThemeOptions { pub theme_dark: Option, /// The theme to use in case the terminal uses a light background with dark text. pub theme_light: Option, - /// Detect whether or not the terminal is dark or light by querying for its colors. + /// Whether or not to test if the terminal is dark or light by querying for its colors. pub detect_color_scheme: DetectColorScheme, } @@ -82,7 +61,7 @@ impl ThemeRequest { #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum DetectColorScheme { - /// Only query the terminal for its colors when appropriate (e.g. when the the output is not redirected). + /// Only query the terminal for its colors when appropriate (i.e. when the the output is not redirected). #[default] Auto, /// Always query the terminal for its colors. @@ -99,12 +78,78 @@ pub enum ColorScheme { Light, } -pub trait ColorSchemeDetector { +fn theme_from_detector(options: ThemeOptions, detector: &dyn ColorSchemeDetector) -> String { + // Implementation note: This function is mostly pure (i.e. it has no side effects) for the sake of testing. + // All the side effects (e.g. querying the terminal for its colors) are performed in the detector. + if let Some(theme) = options.theme { + theme.into_theme(ColorScheme::default()) + } else { + let color_scheme = detect(options.detect_color_scheme, detector).unwrap_or_default(); + choose_theme(options, color_scheme) + .map(|t| t.into_theme(color_scheme)) + .unwrap_or_else(|| default_theme(color_scheme).to_owned()) + } +} + +fn choose_theme(options: ThemeOptions, color_scheme: ColorScheme) -> Option { + match color_scheme { + ColorScheme::Dark => options.theme_dark, + ColorScheme::Light => options.theme_light, + } +} + +fn detect(when: DetectColorScheme, detector: &dyn ColorSchemeDetector) -> Option { + let should_detect = match when { + DetectColorScheme::Auto => detector.should_detect(), + DetectColorScheme::Always => true, + DetectColorScheme::Never => false, + }; + should_detect.then(|| detector.detect()).flatten() +} + +trait ColorSchemeDetector { fn should_detect(&self) -> bool; fn detect(&self) -> Option; } +struct TerminalColorSchemeDetector; + +#[cfg(feature = "detect-color-scheme")] +impl ColorSchemeDetector for TerminalColorSchemeDetector { + fn should_detect(&self) -> bool { + // Querying the terminal for its colors via OSC 10 / OSC 11 requires "exclusive" access + // since we read/write from the terminal and enable/disable raw mode. + // This causes race conditions with pagers such as less when they are attached to the + // same terminal as us. + // + // This is usually only an issue when the output is manually piped to a pager. + // For example: `bat Cargo.toml | less`. + // Otherwise, if we start the pager ourselves, then there's no race condition + // since the pager is started *after* the color is detected. + std::io::stdout().is_terminal() + } + + fn detect(&self) -> Option { + use terminal_colorsaurus::{color_scheme, ColorScheme as ColorsaurusScheme, QueryOptions}; + match color_scheme(QueryOptions::default()).ok()? { + ColorsaurusScheme::Dark => Some(ColorScheme::Dark), + ColorsaurusScheme::Light => Some(ColorScheme::Light), + } + } +} + +#[cfg(not(feature = "detect-color-scheme"))] +impl ColorSchemeDetector for TerminalColorSchemeDetector { + fn should_detect(&self) -> bool { + false + } + + fn detect(&self) -> Option { + None + } +} + #[cfg(test)] impl ColorSchemeDetector for Option { fn should_detect(&self) -> bool { @@ -134,7 +179,7 @@ mod tests { detect_color_scheme: Never, ..Default::default() }; - _ = theme(options, &detector); + _ = theme_from_detector(options, &detector); assert!(!detector.was_called.get()); } @@ -149,7 +194,7 @@ mod tests { detect_color_scheme: Always, ..Default::default() }; - _ = theme(options, &detector); + _ = theme_from_detector(options, &detector); assert!(detector.was_called.get()); } } @@ -157,14 +202,14 @@ mod tests { #[test] fn called_for_auto_if_should_detect() { let detector = DetectorStub::should_detect(Some(Dark)); - _ = theme(ThemeOptions::default(), &detector); + _ = theme_from_detector(ThemeOptions::default(), &detector); assert!(detector.was_called.get()); } #[test] fn not_called_for_auto_if_not_should_detect() { let detector = DetectorStub::should_not_detect(); - _ = theme(ThemeOptions::default(), &detector); + _ = theme_from_detector(ThemeOptions::default(), &detector); assert!(!detector.was_called.get()); } } @@ -188,7 +233,7 @@ mod tests { }, ] { let detector = ConstantDetector(color_scheme); - assert_eq!("Theme", theme(options, &detector)); + assert_eq!("Theme", theme_from_detector(options, &detector)); } } } @@ -200,7 +245,7 @@ mod tests { ..Default::default() }; let detector = DetectorStub::should_detect(Some(Dark)); - _ = theme(options, &detector); + _ = theme_from_detector(options, &detector); assert!(!detector.was_called.get()); } } @@ -213,7 +258,7 @@ mod tests { let detector = ConstantDetector(None); assert_eq!( default_theme(ColorScheme::Dark), - theme(ThemeOptions::default(), &detector) + theme_from_detector(ThemeOptions::default(), &detector) ); } @@ -227,7 +272,10 @@ mod tests { ..Default::default() }; let detector = ConstantDetector(color_scheme); - assert_eq!(default_theme(ColorScheme::Dark), theme(options, &detector)); + assert_eq!( + default_theme(ColorScheme::Dark), + theme_from_detector(options, &detector) + ); } } @@ -243,7 +291,10 @@ mod tests { }, ] { let detector = ConstantDetector(Some(color_scheme)); - assert_eq!(default_theme(color_scheme), theme(options, &detector)); + assert_eq!( + default_theme(color_scheme), + theme_from_detector(options, &detector) + ); } } } @@ -261,7 +312,7 @@ mod tests { ..Default::default() }; let detector = ConstantDetector(color_scheme); - assert_eq!("Dark", theme(options, &detector)); + assert_eq!("Dark", theme_from_detector(options, &detector)); } } @@ -273,7 +324,7 @@ mod tests { ..Default::default() }; let detector = ConstantDetector(Some(ColorScheme::Light)); - assert_eq!("Light", theme(options, &detector)); + assert_eq!("Light", theme_from_detector(options, &detector)); } } From 30b0143ccf93edac1310fca522a89550d5566676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Thu, 18 Jul 2024 15:44:33 +0200 Subject: [PATCH 33/91] Make default_theme pub --- src/theme.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/theme.rs b/src/theme.rs index 2491f9bd..b60184fb 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -10,7 +10,9 @@ pub fn theme(options: ThemeOptions) -> String { theme_from_detector(options, &TerminalColorSchemeDetector) } -pub(crate) const fn default_theme(color_scheme: ColorScheme) -> &'static str { +/// The default theme, suitable for the given color scheme. +/// Use [`theme`], if you want to automatically detect the color scheme from the terminal. +pub const fn default_theme(color_scheme: ColorScheme) -> &'static str { match color_scheme { ColorScheme::Dark => "Monokai Extended", ColorScheme::Light => "Monokai Extended Light", From 6498615f5fb047bef7d84072747676a74fa89c4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Tue, 16 Apr 2024 14:43:33 +0200 Subject: [PATCH 34/91] Improve upon the documentation --- src/pretty_printer.rs | 4 +++- src/theme.rs | 17 ++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/pretty_printer.rs b/src/pretty_printer.rs index eb123ea3..51c9af80 100644 --- a/src/pretty_printer.rs +++ b/src/pretty_printer.rs @@ -245,7 +245,9 @@ impl<'a> PrettyPrinter<'a> { self } - /// Specify the highlighting theme + /// Specify the highlighting theme. + /// You can use [`crate::theme::theme`] to pick a theme based on user preferences + /// and the terminal's background color. pub fn theme(&mut self, theme: impl AsRef) -> &mut Self { self.config.theme = theme.as_ref().to_owned(); self diff --git a/src/theme.rs b/src/theme.rs index b60184fb..54db307b 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -11,7 +11,7 @@ pub fn theme(options: ThemeOptions) -> String { } /// The default theme, suitable for the given color scheme. -/// Use [`theme`], if you want to automatically detect the color scheme from the terminal. +/// Use [`theme`] if you want to automatically detect the color scheme from the terminal. pub const fn default_theme(color_scheme: ColorScheme) -> &'static str { match color_scheme { ColorScheme::Dark => "Monokai Extended", @@ -21,7 +21,7 @@ pub const fn default_theme(color_scheme: ColorScheme) -> &'static str { /// Options for configuring the theme used for syntax highlighting. /// Used together with [`theme`]. -#[derive(Debug, Default)] +#[derive(Debug, Default, PartialEq, Eq)] pub struct ThemeOptions { /// Always use this theme regardless of the terminal's background color. pub theme: Option, @@ -34,7 +34,14 @@ pub struct ThemeOptions { } /// The name of a theme or the default theme. -#[derive(Debug)] +/// +/// ``` +/// # use bat::theme::ThemeRequest; +/// # use std::str::FromStr as _; +/// assert_eq!(ThemeRequest::Default, ThemeRequest::from_str("default").unwrap()); +/// assert_eq!(ThemeRequest::Named("example".to_string()), ThemeRequest::from_str("example").unwrap()); +/// ``` +#[derive(Debug, PartialEq, Eq, Hash)] pub enum ThemeRequest { Named(String), Default, @@ -61,7 +68,7 @@ impl ThemeRequest { } } -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] pub enum DetectColorScheme { /// Only query the terminal for its colors when appropriate (i.e. when the the output is not redirected). #[default] @@ -73,7 +80,7 @@ pub enum DetectColorScheme { } /// The color scheme used to pick a fitting theme. Defaults to [`ColorScheme::Dark`]. -#[derive(Default, Copy, Clone)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] pub enum ColorScheme { #[default] Dark, From e8ca6ec7c31d5cbb07334fc6a814d5df442bbe91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Tue, 16 Apr 2024 14:43:35 +0200 Subject: [PATCH 35/91] Remove cargo feature --- Cargo.toml | 5 ++--- src/theme.rs | 12 ------------ 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 62ae693c..47600d0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ edition = '2021' rust-version = "1.70" [features] -default = ["application", "detect-color-scheme"] +default = ["application"] # Feature required for bat the application. Should be disabled when depending on # bat as a library. application = [ @@ -35,7 +35,6 @@ git = ["git2"] # Support indicating git modifications paging = ["shell-words", "grep-cli"] # Support applying a pager on the output lessopen = ["run_script", "os_str_bytes"] # Support $LESSOPEN preprocessor build-assets = ["syntect/yaml-load", "syntect/plist-load", "regex", "walkdir"] -detect-color-scheme = ["dep:terminal-colorsaurus"] # You need to use one of these if you depend on bat as a library: regex-onig = ["syntect/regex-onig"] # Use the "oniguruma" regex engine @@ -69,7 +68,7 @@ bytesize = { version = "1.3.0" } encoding_rs = "0.8.34" os_str_bytes = { version = "~7.0", optional = true } run_script = { version = "^0.10.1", optional = true} -terminal-colorsaurus = { version = "0.4", optional = true } +terminal-colorsaurus = "0.4" [dependencies.git2] version = "0.18" diff --git a/src/theme.rs b/src/theme.rs index 54db307b..d518e0b2 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -124,7 +124,6 @@ trait ColorSchemeDetector { struct TerminalColorSchemeDetector; -#[cfg(feature = "detect-color-scheme")] impl ColorSchemeDetector for TerminalColorSchemeDetector { fn should_detect(&self) -> bool { // Querying the terminal for its colors via OSC 10 / OSC 11 requires "exclusive" access @@ -148,17 +147,6 @@ impl ColorSchemeDetector for TerminalColorSchemeDetector { } } -#[cfg(not(feature = "detect-color-scheme"))] -impl ColorSchemeDetector for TerminalColorSchemeDetector { - fn should_detect(&self) -> bool { - false - } - - fn detect(&self) -> Option { - None - } -} - #[cfg(test)] impl ColorSchemeDetector for Option { fn should_detect(&self) -> bool { From 594b1417f1854fbb5c1bdb9cdd27d880b0ca38f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Tue, 16 Apr 2024 14:43:39 +0200 Subject: [PATCH 36/91] Update readme --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 016fe834..81ce2202 100644 --- a/README.md +++ b/README.md @@ -482,8 +482,10 @@ the following command (you need [`fzf`](https://github.com/junegunn/fzf) for thi bat --list-themes | fzf --preview="bat --theme={} --color=always /path/to/file" ``` -`bat` looks good on a dark background by default. However, if your terminal uses a -light background, some themes like `GitHub` or `OneHalfLight` will work better for you. +`bat` automatically picks a fitting theme depending on your terminal's background color. +You can use the `--theme-light` / `--theme-light` options or the `BAT_THEME_DARK` / `BAT_THEME_LIGHT` environment variables +to customize the themes used. This is especially useful if you frequently switch between dark and light mode. + You can also use a custom theme by following the ['Adding new themes' section below](https://github.com/sharkdp/bat#adding-new-themes). From c3b190d45b50a4ab05663de1680dba3cd74d7ecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Tue, 16 Apr 2024 14:43:42 +0200 Subject: [PATCH 37/91] Disable color detection in test --- tests/integration_tests.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 8df4327c..0381f48c 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -405,6 +405,7 @@ fn no_args_doesnt_break() { // as the slave end of a pseudo terminal. Although both point to the same "file", bat should // not exit, because in this case it is safe to read and write to the same fd, which is why // this test exists. + let OpenptyResult { master, slave } = openpty(None, None).expect("Couldn't open pty."); let mut master = unsafe { File::from_raw_fd(master) }; let stdin_file = unsafe { File::from_raw_fd(slave) }; @@ -415,6 +416,7 @@ fn no_args_doesnt_break() { let mut child = bat_raw_command() .stdin(stdin) .stdout(stdout) + .env("TERM", "dumb") // Suppresses color detection .spawn() .expect("Failed to start."); From 06b645435a497e77e28ad6d7d3531b5a5415c695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Tue, 16 Apr 2024 14:43:44 +0200 Subject: [PATCH 38/91] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea47b58f..0aa1112a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Syntax highlighting for JavaScript files that start with `#!/usr/bin/env bun` #2913 (@sharunkumar) - `bat --strip-ansi={never,always,auto}` to remove ANSI escape sequences from bat's input, see #2999 (@eth-p) - Add or remove individual style components without replacing all styles #2929 (@eth-p) +- Automatically choose theme based on the terminal's color scheme, see #2896 (@bash) ## Bugfixes From 1b0a6da4be97c7ab2b0e9b53db5a14ac4b1fad44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Tue, 16 Apr 2024 14:43:50 +0200 Subject: [PATCH 39/91] Use new `default_theme` fn for --list-themes --- src/bin/bat/main.rs | 10 +++++++--- src/theme.rs | 5 +++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs index 4528a60b..4dfb8124 100644 --- a/src/bin/bat/main.rs +++ b/src/bin/bat/main.rs @@ -30,12 +30,12 @@ use directories::PROJECT_DIRS; use globset::GlobMatcher; use bat::{ - assets::HighlightingAssets, config::Config, controller::Controller, error::*, input::Input, style::{StyleComponent, StyleComponents}, + theme::{color_scheme, default_theme, ColorScheme, DetectColorScheme}, MappingTarget, PagingMode, }; @@ -200,10 +200,14 @@ pub fn list_themes(cfg: &Config, config_dir: &Path, cache_dir: &Path) -> Result< let stdout = io::stdout(); let mut stdout = stdout.lock(); - let default_theme = HighlightingAssets::default_theme(); + let default_theme_name = default_theme(color_scheme(DetectColorScheme::Auto)); for theme in assets.themes() { - let default_theme_info = if default_theme == theme { + let default_theme_info = if default_theme_name == theme { " (default)" + } else if default_theme(ColorScheme::Dark) == theme { + " (default dark)" + } else if default_theme(ColorScheme::Light) == theme { + " (default light)" } else { "" }; diff --git a/src/theme.rs b/src/theme.rs index d518e0b2..ef5a8ae2 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -19,6 +19,11 @@ pub const fn default_theme(color_scheme: ColorScheme) -> &'static str { } } +/// Detects the color scheme from the terminal. +pub fn color_scheme(when: DetectColorScheme) -> ColorScheme { + detect(when, &TerminalColorSchemeDetector).unwrap_or_default() +} + /// Options for configuring the theme used for syntax highlighting. /// Used together with [`theme`]. #[derive(Debug, Default, PartialEq, Eq)] From 5c6974703e7db3a432be64f301cd08bd35135ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Wed, 1 May 2024 08:27:31 +0200 Subject: [PATCH 40/91] Respect --detect-color-scheme flag when listing themes --- src/bin/bat/app.rs | 17 ++++++++++------- src/bin/bat/main.rs | 11 ++++++++--- tests/integration_tests.rs | 16 +++++++--------- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index 711ab1c6..606132f7 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -427,7 +427,16 @@ impl App { .matches .get_one::("theme-light") .map(|t| ThemeRequest::from_str(t).unwrap()); - let detect_color_scheme = match self + ThemeOptions { + theme, + theme_dark, + theme_light, + detect_color_scheme: self.detect_color_scheme(), + } + } + + pub(crate) fn detect_color_scheme(&self) -> DetectColorScheme { + match self .matches .get_one::("detect-color-scheme") .map(|s| s.as_str()) @@ -436,12 +445,6 @@ impl App { Some("never") => DetectColorScheme::Never, Some("always") => DetectColorScheme::Always, _ => unreachable!("other values for --detect-color-scheme are not allowed"), - }; - ThemeOptions { - theme, - theme_dark, - theme_light, - detect_color_scheme, } } } diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs index 4dfb8124..95493a6d 100644 --- a/src/bin/bat/main.rs +++ b/src/bin/bat/main.rs @@ -189,7 +189,12 @@ fn theme_preview_file<'a>() -> Input<'a> { Input::from_reader(Box::new(BufReader::new(THEME_PREVIEW_DATA))) } -pub fn list_themes(cfg: &Config, config_dir: &Path, cache_dir: &Path) -> Result<()> { +pub fn list_themes( + cfg: &Config, + config_dir: &Path, + cache_dir: &Path, + detect_color_scheme: DetectColorScheme, +) -> Result<()> { let assets = assets_from_cache_or_binary(cfg.use_custom_assets, cache_dir)?; let mut config = cfg.clone(); let mut style = HashSet::new(); @@ -200,7 +205,7 @@ pub fn list_themes(cfg: &Config, config_dir: &Path, cache_dir: &Path) -> Result< let stdout = io::stdout(); let mut stdout = stdout.lock(); - let default_theme_name = default_theme(color_scheme(DetectColorScheme::Auto)); + let default_theme_name = default_theme(color_scheme(detect_color_scheme)); for theme in assets.themes() { let default_theme_info = if default_theme_name == theme { " (default)" @@ -375,7 +380,7 @@ fn run() -> Result { }; run_controller(inputs, &plain_config, cache_dir) } else if app.matches.get_flag("list-themes") { - list_themes(&config, config_dir, cache_dir)?; + list_themes(&config, config_dir, cache_dir, app.detect_color_scheme())?; Ok(true) } else if app.matches.get_flag("config-file") { println!("{}", config_file().to_string_lossy()); diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 0381f48c..23aed5bc 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -274,37 +274,35 @@ fn squeeze_limit_line_numbers() { #[test] fn list_themes_with_colors() { - #[cfg(target_os = "macos")] - let default_theme_chunk = "Monokai Extended Light\x1B[0m (default)"; - - #[cfg(not(target_os = "macos"))] let default_theme_chunk = "Monokai Extended\x1B[0m (default)"; + let default_light_theme_chunk = "Monokai Extended Light\x1B[0m (default light)"; bat() .arg("--color=always") + .arg("--detect-color-scheme=never") .arg("--list-themes") .assert() .success() .stdout(predicate::str::contains("DarkNeon").normalize()) .stdout(predicate::str::contains(default_theme_chunk).normalize()) + .stdout(predicate::str::contains(default_light_theme_chunk).normalize()) .stdout(predicate::str::contains("Output the square of a number.").normalize()); } #[test] fn list_themes_without_colors() { - #[cfg(target_os = "macos")] - let default_theme_chunk = "Monokai Extended Light (default)"; - - #[cfg(not(target_os = "macos"))] let default_theme_chunk = "Monokai Extended (default)"; + let default_light_theme_chunk = "Monokai Extended Light (default light)"; bat() .arg("--color=never") + .arg("--detect-color-scheme=never") .arg("--list-themes") .assert() .success() .stdout(predicate::str::contains("DarkNeon").normalize()) - .stdout(predicate::str::contains(default_theme_chunk).normalize()); + .stdout(predicate::str::contains(default_theme_chunk).normalize()) + .stdout(predicate::str::contains(default_light_theme_chunk).normalize()); } #[test] From abf9dada04b715494ae323ffdc51e1bb39854221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Thu, 18 Jul 2024 15:49:25 +0200 Subject: [PATCH 41/91] Remove `HighlightingAssets::default_theme()` --- CHANGELOG.md | 3 +++ src/assets.rs | 57 --------------------------------------------------- 2 files changed, 3 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0aa1112a..0cd76031 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,9 @@ - [BREAKING] `SyntaxMapping::mappings` is replaced by `SyntaxMapping::{builtin,custom,all}_mappings` - Make `Controller::run_with_error_handler`'s error handler `FnMut`, see #2831 (@rhysd) - Improve compile time by 20%, see #2815 (@dtolnay) +- Add `theme::theme` for choosing an appropriate theme based on the + terminal's color scheme, see #2896 (@bash) + - [BREAKING] Remove `HighlightingAssets::default_theme`. Use `theme::default_theme` instead. # v0.24.0 diff --git a/src/assets.rs b/src/assets.rs index 857f416b..d32ccbd4 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -70,43 +70,6 @@ impl HighlightingAssets { } } - /// The default theme. - /// - /// ### Windows and Linux - /// - /// Windows and most Linux distributions has a dark terminal theme by - /// default. On these platforms, this function always returns a theme that - /// looks good on a dark background. - /// - /// ### macOS - /// - /// On macOS the default terminal background is light, but it is common that - /// Dark Mode is active, which makes the terminal background dark. On this - /// platform, the default theme depends on - /// ```bash - /// defaults read -globalDomain AppleInterfaceStyle - /// ``` - /// To avoid the overhead of the check on macOS, simply specify a theme - /// explicitly via `--theme`, `BAT_THEME`, or `~/.config/bat`. - /// - /// See and - /// for more context. - #[deprecated(note = "use bat::theme::theme instead")] - pub fn default_theme() -> &'static str { - #[cfg(not(target_os = "macos"))] - { - default_theme(ColorScheme::Dark) - } - #[cfg(target_os = "macos")] - { - if macos_dark_mode_active() { - default_theme(ColorScheme::Dark) - } else { - default_theme(ColorScheme::Light) - } - } - } - pub fn from_cache(cache_path: &Path) -> Result { Ok(HighlightingAssets::new( SerializedSyntaxSet::FromFile(cache_path.join("syntaxes.bin")), @@ -389,26 +352,6 @@ fn asset_from_cache( .map_err(|_| format!("Could not parse cached {description}").into()) } -#[cfg(target_os = "macos")] -fn macos_dark_mode_active() -> bool { - const PREFERENCES_FILE: &str = "Library/Preferences/.GlobalPreferences.plist"; - const STYLE_KEY: &str = "AppleInterfaceStyle"; - - let preferences_file = home::home_dir() - .map(|home| home.join(PREFERENCES_FILE)) - .expect("Could not get home directory"); - - match plist::Value::from_file(preferences_file).map(|file| file.into_dictionary()) { - Ok(Some(preferences)) => match preferences.get(STYLE_KEY).and_then(|val| val.as_string()) { - Some(value) => value == "Dark", - // If the key does not exist, then light theme is currently in use. - None => false, - }, - // Unreachable, in theory. All macOS users have a home directory and preferences file setup. - Ok(None) | Err(_) => true, - } -} - #[cfg(test)] mod tests { use super::*; From b9b981f6572c612cce443a8fff0b5fb9c24d3868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Thu, 18 Jul 2024 17:36:57 +0200 Subject: [PATCH 42/91] Generalize --detect-color-scheme to --color-scheme --- assets/completions/_bat.ps1.in | 2 +- assets/completions/bat.bash.in | 6 ++- assets/completions/bat.fish.in | 10 +++- assets/completions/bat.zsh.in | 2 +- doc/long-help.txt | 26 +++++----- doc/short-help.txt | 4 +- src/bin/bat/app.rs | 18 ++++--- src/bin/bat/clap_app.rs | 34 ++++++------- src/bin/bat/main.rs | 13 +++-- src/theme.rs | 90 +++++++++++++++++++++++++++------- tests/integration_tests.rs | 4 +- 11 files changed, 140 insertions(+), 69 deletions(-) diff --git a/assets/completions/_bat.ps1.in b/assets/completions/_bat.ps1.in index 5635dea2..ac66ccc8 100644 --- a/assets/completions/_bat.ps1.in +++ b/assets/completions/_bat.ps1.in @@ -32,7 +32,7 @@ Register-ArgumentCompleter -Native -CommandName '{{PROJECT_EXECUTABLE}}' -Script [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('--detect-color-scheme', 'detect-color-scheme', [CompletionResultType]::ParameterName, 'When to detect the terminal''s color scheme (*auto*, never, always).') + [CompletionResult]::new('--color-scheme', 'color-scheme', [CompletionResultType]::ParameterName, 'Whether to choose a dark or light syntax highlighting theme (*auto*, auto:always, dark, light, system).') [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++'').') diff --git a/assets/completions/bat.bash.in b/assets/completions/bat.bash.in index a02c5a04..0a01a054 100644 --- a/assets/completions/bat.bash.in +++ b/assets/completions/bat.bash.in @@ -100,10 +100,12 @@ _bat() { COMPREPLY=($(compgen -W "auto never character" -- "$cur")) return 0 ;; - --color | --decorations | --paging | --detect-color-scheme) + --color | --decorations | --paging) COMPREPLY=($(compgen -W "auto never always" -- "$cur")) return 0 ;; + --color-scheme) + COMPREPLY=($(compgen -W "auto auto:always dark light system" -- "$cur")) --italic-text) COMPREPLY=($(compgen -W "always never" -- "$cur")) return 0 @@ -166,7 +168,6 @@ _bat() { --color --italic-text --decorations - --detect-color-scheme --force-colorization --paging --pager @@ -175,6 +176,7 @@ _bat() { --theme --theme-dark --theme-light + --color-scheme --list-themes --squeeze-blank --squeeze-limit diff --git a/assets/completions/bat.fish.in b/assets/completions/bat.fish.in index 7bd0ebca..33cf8264 100644 --- a/assets/completions/bat.fish.in +++ b/assets/completions/bat.fish.in @@ -99,7 +99,13 @@ set -l color_opts ' ' set -l decorations_opts $color_opts set -l paging_opts $color_opts -set -l detect_color_scheme_opts $color_opts +set -l color_scheme_opts " + auto\t'Use the terminal\'s color scheme if the output is not redirected (default)' + auto:always\t'Always use the terminal\'s color scheme' + dark\t'Use a dark syntax highlighting theme' + light\t'Use a light syntax highlighting theme' + system\t'Query the OS for its color scheme (macOS only)' +" # Include some examples so we can indicate the default. set -l pager_opts ' @@ -144,7 +150,7 @@ complete -c $bat -l config-file -f -d "Display location of configuration file" - complete -c $bat -l decorations -x -a "$decorations_opts" -d "When to use --style decorations" -n __bat_no_excl_args -complete -c $bat -l detect-color-scheme -x -a "$detect_color_scheme_opts" -d "When to detect the terminal's color scheme" -n __bat_no_excl_args +complete -c $bat -l color-scheme -x -a "$color_scheme_opts" -d "Whether to choose a dark or light syntax highlighting theme" -n __bat_no_excl_args complete -c $bat -l diagnostic -d "Print diagnostic info for bug reports" -n __fish_is_first_arg diff --git a/assets/completions/bat.zsh.in b/assets/completions/bat.zsh.in index 4a598437..4bcae11c 100644 --- a/assets/completions/bat.zsh.in +++ b/assets/completions/bat.zsh.in @@ -40,7 +40,7 @@ _{{PROJECT_EXECUTABLE}}_main() { --color='[specify when to use colors]:when:(auto never always)' --italic-text='[use italics in output]:when:(always never)' --decorations='[specify when to show the decorations]:when:(auto never always)' - --detect-color-scheme="[specify when to detect the terminal's color scheme]:when:(auto never always)" + --color-scheme="[whether to choose a dark or light syntax highlighting theme]:scheme:(auto auto:always dark light system)" --paging='[specify when to use the pager]:when:(auto never always)' '(-m --map-syntax)'{-m+,--map-syntax=}'[map a glob pattern to an existing syntax name]: :->syntax-maps' '(--theme)'--theme='[set the color theme for syntax highlighting]:theme:->themes' diff --git a/doc/long-help.txt b/doc/long-help.txt index 86e9a532..c374a039 100644 --- a/doc/long-help.txt +++ b/doc/long-help.txt @@ -114,22 +114,22 @@ Options: add the '--theme="..."' option to the configuration file or export the BAT_THEME environment variable (e.g.: export BAT_THEME="..."). - --detect-color-scheme - Specify when to query the terminal for its colors in order to pick an appropriate syntax - highlighting theme. Use '--theme-light' and '--theme-dark' (or the environment variables - BAT_THEME_LIGHT and BAT_THEME_DARK) to configure which themes are picked. You may also use - '--theme' to set a theme that is used regardless of the terminal's colors. + --color-scheme + Specify whether to choose a dark or light syntax highlighting theme. Use '--theme-light' + and '--theme-dark' (or the environment variables BAT_THEME_LIGHT and BAT_THEME_DARK) to + configure which themes are picked. You may also use '--theme' to set a theme that is used + regardless of this choice. Possible values: * auto (default): - Only query the terminals colors if the output is not redirected. This is to prevent - race conditions with pagers such as less. - * never - Never query the terminal for its colors and assume that the terminal has a dark - background. - * always - Always query the terminal for its colors, regardless of whether or not the output is - redirected. + Query the terminals for its color scheme if the output is not redirected. This is to + prevent race conditions with pagers such as less. + * 'auto:always': + Always query the terminal for its color scheme, regardless of whether or not the + output is redirected. + * dark: Use a dark syntax highlighting theme. + * light: Use a light syntax highlighting theme. + * system: Query the OS for its color scheme. Only works on macOS. --theme-light Sets the theme name for syntax highlighting used when the terminal uses a light diff --git a/doc/short-help.txt b/doc/short-help.txt index 3e369229..f17a6d9d 100644 --- a/doc/short-help.txt +++ b/doc/short-help.txt @@ -41,8 +41,8 @@ Options: Use the specified syntax for files matching the glob pattern ('*.cpp:C++'). --theme Set the color theme for syntax highlighting. - --detect-color-scheme - Specify when to query the terminal for its colors. + --color-scheme + Specify whether to choose a dark or light theme. --theme-light Sets the color theme for syntax highlighting used for light backgrounds. --theme-dark diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index 606132f7..d2e5b4db 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -9,7 +9,7 @@ use crate::{ config::{get_args_from_config_file, get_args_from_env_opts_var, get_args_from_env_vars}, }; use bat::style::StyleComponentList; -use bat::theme::{theme, DetectColorScheme, ThemeOptions, ThemeRequest}; +use bat::theme::{theme, ColorSchemePreference, DetectColorScheme, ThemeOptions, ThemeRequest}; use bat::StripAnsiMode; use clap::ArgMatches; @@ -431,20 +431,22 @@ impl App { theme, theme_dark, theme_light, - detect_color_scheme: self.detect_color_scheme(), + color_scheme: self.color_scheme_preference(), } } - pub(crate) fn detect_color_scheme(&self) -> DetectColorScheme { + pub(crate) fn color_scheme_preference(&self) -> ColorSchemePreference { match self .matches - .get_one::("detect-color-scheme") + .get_one::("color-scheme") .map(|s| s.as_str()) { - Some("auto") => DetectColorScheme::Auto, - Some("never") => DetectColorScheme::Never, - Some("always") => DetectColorScheme::Always, - _ => unreachable!("other values for --detect-color-scheme are not allowed"), + Some("auto") => ColorSchemePreference::Auto(DetectColorScheme::Auto), + Some("auto:always") => ColorSchemePreference::Auto(DetectColorScheme::Always), + Some("dark") => ColorSchemePreference::Dark, + Some("light") => ColorSchemePreference::Light, + Some("system") => ColorSchemePreference::System, + _ => unreachable!("other values for --color-scheme are not allowed"), } } } diff --git a/src/bin/bat/clap_app.rs b/src/bin/bat/clap_app.rs index 6abdab9c..0857dba4 100644 --- a/src/bin/bat/clap_app.rs +++ b/src/bin/bat/clap_app.rs @@ -384,30 +384,30 @@ pub fn build_app(interactive_output: bool) -> Command { ), ) .arg( - Arg::new("detect-color-scheme") - .long("detect-color-scheme") - .overrides_with("detect-color-scheme") - .value_name("when") - .value_parser(["auto", "never", "always"]) + Arg::new("color-scheme") + .long("color-scheme") + .overrides_with("color-scheme") + .value_name("scheme") + .value_parser(["auto", "auto:always", "dark", "light", "system"]) .default_value("auto") .hide_default_value(true) - .help("Specify when to query the terminal for its colors.") + .help("Specify whether to choose a dark or light theme.") .long_help( - "Specify when to query the terminal for its colors \ - in order to pick an appropriate syntax highlighting theme. \ + "Specify whether to choose a dark or light syntax highlighting theme. \ Use '--theme-light' and '--theme-dark' (or the environment variables \ BAT_THEME_LIGHT and BAT_THEME_DARK) to configure which themes are picked. \ - You may also use '--theme' to set a theme that is used regardless of the terminal's colors.\n\n\ + You may also use '--theme' to set a theme that is used regardless of this choice.\n\n\ Possible values:\n\ - * auto (default):\n \ - Only query the terminals colors if the output is not redirected. \ + * auto (default):\n \ + Query the terminals for its color scheme if the output is not redirected. \ This is to prevent race conditions with pagers such as less.\n\ - * never\n \ - Never query the terminal for its colors \ - and assume that the terminal has a dark background.\n\ - * always\n \ - Always query the terminal for its colors, \ - regardless of whether or not the output is redirected."), + * 'auto:always':\n \ + Always query the terminal for its color scheme, \ + regardless of whether or not the output is redirected.\n\ + * dark: Use a dark syntax highlighting theme.\n\ + * light: Use a light syntax highlighting theme.\n\ + * system: Query the OS for its color scheme. Only works on macOS.\n\ + "), ) .arg( Arg::new("theme-light") diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs index 95493a6d..891390c1 100644 --- a/src/bin/bat/main.rs +++ b/src/bin/bat/main.rs @@ -35,7 +35,7 @@ use bat::{ error::*, input::Input, style::{StyleComponent, StyleComponents}, - theme::{color_scheme, default_theme, ColorScheme, DetectColorScheme}, + theme::{color_scheme, default_theme, ColorScheme, ColorSchemePreference}, MappingTarget, PagingMode, }; @@ -193,7 +193,7 @@ pub fn list_themes( cfg: &Config, config_dir: &Path, cache_dir: &Path, - detect_color_scheme: DetectColorScheme, + color_scheme_pref: ColorSchemePreference, ) -> Result<()> { let assets = assets_from_cache_or_binary(cfg.use_custom_assets, cache_dir)?; let mut config = cfg.clone(); @@ -205,7 +205,7 @@ pub fn list_themes( let stdout = io::stdout(); let mut stdout = stdout.lock(); - let default_theme_name = default_theme(color_scheme(detect_color_scheme)); + let default_theme_name = default_theme(color_scheme(color_scheme_pref)); for theme in assets.themes() { let default_theme_info = if default_theme_name == theme { " (default)" @@ -380,7 +380,12 @@ fn run() -> Result { }; run_controller(inputs, &plain_config, cache_dir) } else if app.matches.get_flag("list-themes") { - list_themes(&config, config_dir, cache_dir, app.detect_color_scheme())?; + list_themes( + &config, + config_dir, + cache_dir, + app.color_scheme_preference(), + )?; Ok(true) } else if app.matches.get_flag("config-file") { println!("{}", config_file().to_string_lossy()); diff --git a/src/theme.rs b/src/theme.rs index ef5a8ae2..1c6d7e55 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -20,8 +20,8 @@ pub const fn default_theme(color_scheme: ColorScheme) -> &'static str { } /// Detects the color scheme from the terminal. -pub fn color_scheme(when: DetectColorScheme) -> ColorScheme { - detect(when, &TerminalColorSchemeDetector).unwrap_or_default() +pub fn color_scheme(preference: ColorSchemePreference) -> ColorScheme { + color_scheme_impl(preference, &TerminalColorSchemeDetector) } /// Options for configuring the theme used for syntax highlighting. @@ -34,8 +34,8 @@ pub struct ThemeOptions { pub theme_dark: Option, /// The theme to use in case the terminal uses a light background with dark text. pub theme_light: Option, - /// Whether or not to test if the terminal is dark or light by querying for its colors. - pub detect_color_scheme: DetectColorScheme, + /// How to choose between dark and light. + pub color_scheme: ColorSchemePreference, } /// The name of a theme or the default theme. @@ -73,6 +73,25 @@ impl ThemeRequest { } } +/// How to choose between dark and light. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ColorSchemePreference { + /// Detect the color scheme from the terminal. + Auto(DetectColorScheme), + /// Use a dark theme. + Dark, + /// Use a light theme. + Light, + /// Detect the color scheme from the OS instead (macOS only). + System, +} + +impl Default for ColorSchemePreference { + fn default() -> Self { + Self::Auto(DetectColorScheme::default()) + } +} + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] pub enum DetectColorScheme { /// Only query the terminal for its colors when appropriate (i.e. when the the output is not redirected). @@ -80,8 +99,6 @@ pub enum DetectColorScheme { Auto, /// Always query the terminal for its colors. Always, - /// Never query the terminal for its colors. - Never, } /// The color scheme used to pick a fitting theme. Defaults to [`ColorScheme::Dark`]. @@ -92,13 +109,25 @@ pub enum ColorScheme { Light, } +fn color_scheme_impl( + pref: ColorSchemePreference, + detector: &dyn ColorSchemeDetector, +) -> ColorScheme { + match pref { + ColorSchemePreference::Auto(when) => detect(when, detector).unwrap_or_default(), + ColorSchemePreference::Dark => ColorScheme::Dark, + ColorSchemePreference::Light => ColorScheme::Light, + ColorSchemePreference::System => color_scheme_from_system().unwrap_or_default(), + } +} + fn theme_from_detector(options: ThemeOptions, detector: &dyn ColorSchemeDetector) -> String { // Implementation note: This function is mostly pure (i.e. it has no side effects) for the sake of testing. // All the side effects (e.g. querying the terminal for its colors) are performed in the detector. if let Some(theme) = options.theme { theme.into_theme(ColorScheme::default()) } else { - let color_scheme = detect(options.detect_color_scheme, detector).unwrap_or_default(); + let color_scheme = color_scheme_impl(options.color_scheme, detector); choose_theme(options, color_scheme) .map(|t| t.into_theme(color_scheme)) .unwrap_or_else(|| default_theme(color_scheme).to_owned()) @@ -116,7 +145,6 @@ fn detect(when: DetectColorScheme, detector: &dyn ColorSchemeDetector) -> Option let should_detect = match when { DetectColorScheme::Auto => detector.should_detect(), DetectColorScheme::Always => true, - DetectColorScheme::Never => false, }; should_detect.then(|| detector.detect()).flatten() } @@ -152,6 +180,31 @@ impl ColorSchemeDetector for TerminalColorSchemeDetector { } } +#[cfg(not(target_os = "macos"))] +fn color_scheme_from_system() -> Option { + None +} + +#[cfg(target_os = "macos")] +fn color_scheme_from_system() -> Option { + const PREFERENCES_FILE: &str = "Library/Preferences/.GlobalPreferences.plist"; + const STYLE_KEY: &str = "AppleInterfaceStyle"; + + let preferences_file = home::home_dir() + .map(|home| home.join(PREFERENCES_FILE)) + .expect("Could not get home directory"); + + match plist::Value::from_file(preferences_file).map(|file| file.into_dictionary()) { + Ok(Some(preferences)) => match preferences.get(STYLE_KEY).and_then(|val| val.as_string()) { + Some(value) if value == "Dark" => Some(ColorScheme::Dark), + // If the key does not exist, then light theme is currently in use. + Some(_) | None => Some(ColorScheme::Light), + }, + // Unreachable, in theory. All macOS users have a home directory and preferences file setup. + Ok(None) | Err(_) => None, + } +} + #[cfg(test)] impl ColorSchemeDetector for Option { fn should_detect(&self) -> bool { @@ -166,6 +219,7 @@ impl ColorSchemeDetector for Option { #[cfg(test)] mod tests { use super::ColorScheme::*; + use super::ColorSchemePreference as Pref; use super::DetectColorScheme::*; use super::*; use std::cell::Cell; @@ -175,14 +229,16 @@ mod tests { use super::*; #[test] - fn not_called_for_never() { - let detector = DetectorStub::should_detect(Some(Dark)); - let options = ThemeOptions { - detect_color_scheme: Never, - ..Default::default() - }; - _ = theme_from_detector(options, &detector); - assert!(!detector.was_called.get()); + fn not_called_for_dark_or_light() { + for pref in [Pref::Dark, Pref::Light] { + let detector = DetectorStub::should_detect(Some(Dark)); + let options = ThemeOptions { + color_scheme: pref, + ..Default::default() + }; + _ = theme_from_detector(options, &detector); + assert!(!detector.was_called.get()); + } } #[test] @@ -193,7 +249,7 @@ mod tests { ]; for detector in detectors { let options = ThemeOptions { - detect_color_scheme: Always, + color_scheme: Pref::Auto(Always), ..Default::default() }; _ = theme_from_detector(options, &detector); diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 23aed5bc..e4b73c59 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -279,7 +279,7 @@ fn list_themes_with_colors() { bat() .arg("--color=always") - .arg("--detect-color-scheme=never") + .arg("--color-scheme=dark") .arg("--list-themes") .assert() .success() @@ -296,7 +296,7 @@ fn list_themes_without_colors() { bat() .arg("--color=never") - .arg("--detect-color-scheme=never") + .arg("--color-scheme=dark") .arg("--list-themes") .assert() .success() From 7f089ead62508675749fe3da5f9d009ba38224cd Mon Sep 17 00:00:00 2001 From: einfachIrgendwer0815 <85333734+einfachIrgendwer0815@users.noreply.github.com> Date: Fri, 31 May 2024 18:20:16 +0200 Subject: [PATCH 43/91] Add option `--binary` `--binary` allows to specify how to deal with binary content. Current options are not printing anything or treating the binary data as text. --- doc/long-help.txt | 7 +++++++ doc/short-help.txt | 2 ++ src/bin/bat/app.rs | 6 ++++++ src/bin/bat/clap_app.rs | 16 ++++++++++++++++ src/config.rs | 5 ++++- src/lib.rs | 2 +- src/nonprintable_notation.rs | 12 ++++++++++++ src/printer.rs | 19 +++++++++++++++---- 8 files changed, 63 insertions(+), 6 deletions(-) diff --git a/doc/long-help.txt b/doc/long-help.txt index 2b03490f..87fb5d96 100644 --- a/doc/long-help.txt +++ b/doc/long-help.txt @@ -20,6 +20,13 @@ Options: * unicode (␇, ␊, ␀, ..) * caret (^G, ^J, ^@, ..) + --binary + How to treat binary content. (default: no-printing) + + Possible values: + * no-printing: do not print any binary content + * as-text: treat binary content as normal text + -p, --plain... Only show plain style, no decorations. This is an alias for '--style=plain'. When '-p' is used twice ('-pp'), it also disables automatic paging (alias for '--style=plain diff --git a/doc/short-help.txt b/doc/short-help.txt index 305bbf3d..16b9eb05 100644 --- a/doc/short-help.txt +++ b/doc/short-help.txt @@ -11,6 +11,8 @@ Options: Show non-printable characters (space, tab, newline, ..). --nonprintable-notation Set notation for non-printable characters. + --binary + How to treat binary content. (default: no-printing) -p, --plain... Show plain style (alias for '--style=plain'). -l, --language diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index d6628668..4d52c73d 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -9,6 +9,7 @@ use crate::{ config::{get_args_from_config_file, get_args_from_env_opts_var, get_args_from_env_vars}, }; use bat::style::StyleComponentList; +use bat::BinaryBehavior; use bat::StripAnsiMode; use clap::ArgMatches; @@ -193,6 +194,11 @@ impl App { Some("caret") => NonprintableNotation::Caret, _ => unreachable!("other values for --nonprintable-notation are not allowed"), }, + binary: match self.matches.get_one::("binary").map(|s| s.as_str()) { + Some("as-text") => BinaryBehavior::AsText, + Some("no-printing") => BinaryBehavior::NoPrinting, + _ => unreachable!("other values for --binary are not allowed"), + }, wrapping_mode: if self.interactive_output || maybe_term_width.is_some() { if !self.matches.get_flag("chop-long-lines") { match self.matches.get_one::("wrap").map(|s| s.as_str()) { diff --git a/src/bin/bat/clap_app.rs b/src/bin/bat/clap_app.rs index 33dde980..4327fa15 100644 --- a/src/bin/bat/clap_app.rs +++ b/src/bin/bat/clap_app.rs @@ -77,6 +77,22 @@ pub fn build_app(interactive_output: bool) -> Command { * caret (^G, ^J, ^@, ..)", ), ) + .arg( + Arg::new("binary") + .long("binary") + .action(ArgAction::Set) + .default_value("no-printing") + .value_parser(["no-printing", "as-text"]) + .value_name("behavior") + .hide_default_value(true) + .help("How to treat binary content. (default: no-printing)") + .long_help( + "How to treat binary content. (default: no-printing)\n\n\ + Possible values:\n \ + * no-printing: do not print any binary content\n \ + * as-text: treat binary content as normal text", + ), + ) .arg( Arg::new("plain") .overrides_with("plain") diff --git a/src/config.rs b/src/config.rs index eb7df8ee..eb44281c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,5 @@ use crate::line_range::{HighlightedLineRanges, LineRanges}; -use crate::nonprintable_notation::NonprintableNotation; +use crate::nonprintable_notation::{BinaryBehavior, NonprintableNotation}; #[cfg(feature = "paging")] use crate::paging::PagingMode; use crate::style::StyleComponents; @@ -44,6 +44,9 @@ pub struct Config<'a> { /// The configured notation for non-printable characters pub nonprintable_notation: NonprintableNotation, + /// How to treat binary content + pub binary: BinaryBehavior, + /// The character width of the terminal pub term_width: usize, diff --git a/src/lib.rs b/src/lib.rs index 23c4a800..a015913e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,7 +52,7 @@ mod terminal; mod vscreen; pub(crate) mod wrapping; -pub use nonprintable_notation::NonprintableNotation; +pub use nonprintable_notation::{BinaryBehavior, NonprintableNotation}; pub use preprocessor::StripAnsiMode; pub use pretty_printer::{Input, PrettyPrinter, Syntax}; pub use syntax_mapping::{MappingTarget, SyntaxMapping}; diff --git a/src/nonprintable_notation.rs b/src/nonprintable_notation.rs index ff09aca6..9f8d7cb8 100644 --- a/src/nonprintable_notation.rs +++ b/src/nonprintable_notation.rs @@ -10,3 +10,15 @@ pub enum NonprintableNotation { #[default] Unicode, } + +/// How to treat binary content +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +pub enum BinaryBehavior { + /// Do not print any binary content + #[default] + NoPrinting, + + /// Treat binary content as normal text + AsText, +} diff --git a/src/printer.rs b/src/printer.rs index e9bea3fd..3398b217 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -35,6 +35,7 @@ use crate::style::StyleComponent; use crate::terminal::{as_terminal_escaped, to_ansi_color}; use crate::vscreen::{AnsiStyle, EscapeSequence, EscapeSequenceIterator}; use crate::wrapping::WrappingMode; +use crate::BinaryBehavior; use crate::StripAnsiMode; const ANSI_UNDERLINE_ENABLE: EscapeSequence = EscapeSequence::CSI { @@ -458,7 +459,10 @@ impl<'a> Printer for InteractivePrinter<'a> { } if !self.config.style_components.header() { - if Some(ContentType::BINARY) == self.content_type && !self.config.show_nonprintable { + if Some(ContentType::BINARY) == self.content_type + && !self.config.show_nonprintable + && !matches!(self.config.binary, BinaryBehavior::AsText) + { writeln!( handle, "{}: Binary content from {} will not be printed to the terminal \ @@ -539,7 +543,10 @@ impl<'a> Printer for InteractivePrinter<'a> { })?; if self.config.style_components.grid() { - if self.content_type.map_or(false, |c| c.is_text()) || self.config.show_nonprintable { + if self.content_type.map_or(false, |c| c.is_text()) + || self.config.show_nonprintable + || matches!(self.config.binary, BinaryBehavior::AsText) + { self.print_horizontal_line(handle, '┼')?; } else { self.print_horizontal_line(handle, '┴')?; @@ -551,7 +558,9 @@ impl<'a> Printer for InteractivePrinter<'a> { fn print_footer(&mut self, handle: &mut OutputHandle, _input: &OpenedInput) -> Result<()> { if self.config.style_components.grid() - && (self.content_type.map_or(false, |c| c.is_text()) || self.config.show_nonprintable) + && (self.content_type.map_or(false, |c| c.is_text()) + || self.config.show_nonprintable + || matches!(self.config.binary, BinaryBehavior::AsText)) { self.print_horizontal_line(handle, '┴') } else { @@ -599,7 +608,9 @@ impl<'a> Printer for InteractivePrinter<'a> { .into() } else { let mut line = match self.content_type { - Some(ContentType::BINARY) | None => { + Some(ContentType::BINARY) | None + if !matches!(self.config.binary, BinaryBehavior::AsText) => + { return Ok(()); } Some(ContentType::UTF_16LE) => UTF_16LE.decode_with_bom_removal(line_buffer).0, From 7cc231b82b7f3ddd8395a783a0bf59e35a6b5b44 Mon Sep 17 00:00:00 2001 From: einfachIrgendwer0815 <85333734+einfachIrgendwer0815@users.noreply.github.com> Date: Fri, 31 May 2024 18:32:28 +0200 Subject: [PATCH 44/91] Test `--binary=as-text` --- tests/integration_tests.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 8df4327c..2cc4eb8b 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1929,6 +1929,16 @@ fn show_all_with_unicode() { .stderr(""); } +#[test] +fn binary_as_text() { + bat() + .arg("--binary=as-text") + .arg("control_characters.txt") + .assert() + .stdout("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x7F") + .stderr(""); +} + #[test] fn no_paging_arg() { bat() From a769a3d8137f863634a14ae5741d7ee98299367f Mon Sep 17 00:00:00 2001 From: einfachIrgendwer0815 <85333734+einfachIrgendwer0815@users.noreply.github.com> Date: Fri, 31 May 2024 20:58:25 +0200 Subject: [PATCH 45/91] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea47b58f..ec662247 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Syntax highlighting for JavaScript files that start with `#!/usr/bin/env bun` #2913 (@sharunkumar) - `bat --strip-ansi={never,always,auto}` to remove ANSI escape sequences from bat's input, see #2999 (@eth-p) - Add or remove individual style components without replacing all styles #2929 (@eth-p) +- Add option `--binary=as-text` for printing binary content, see issue #2974 and PR #2976 (@einfachIrgendwer0815) ## Bugfixes From 0c3b22e0f0ed1a371923b2e4df130f646c564772 Mon Sep 17 00:00:00 2001 From: einfachIrgendwer0815 <85333734+einfachIrgendwer0815@users.noreply.github.com> Date: Sat, 1 Jun 2024 00:13:03 +0200 Subject: [PATCH 46/91] Run syntax highlighting when `--binary=as-text` --- src/printer.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/printer.rs b/src/printer.rs index 3398b217..95017188 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -269,7 +269,8 @@ impl<'a> InteractivePrinter<'a> { .content_type .map_or(false, |c| c.is_binary() && !config.show_nonprintable); - let needs_to_match_syntax = !is_printing_binary + let needs_to_match_syntax = (!is_printing_binary + || matches!(config.binary, BinaryBehavior::AsText)) && (config.colored_output || config.strip_ansi == StripAnsiMode::Auto); let (is_plain_text, highlighter_from_set) = if needs_to_match_syntax { From bc42149a726b73e5e4cd6b8afdeb340a3ae23c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Sun, 18 Aug 2024 14:59:14 +0200 Subject: [PATCH 47/91] Merge color scheme options into theme / BAT_THEME --- assets/completions/_bat.ps1.in | 1 - assets/completions/bat.bash.in | 3 - assets/completions/bat.fish.in | 9 --- assets/completions/bat.zsh.in | 1 - assets/manual/bat.1.in | 26 ++------ doc/long-help.txt | 17 ----- doc/short-help.txt | 2 - src/bin/bat/app.rs | 25 ++------ src/bin/bat/clap_app.rs | 26 -------- src/bin/bat/main.rs | 2 +- src/theme.rs | 111 ++++++++++++++++++++++----------- tests/integration_tests.rs | 2 - 12 files changed, 83 insertions(+), 142 deletions(-) diff --git a/assets/completions/_bat.ps1.in b/assets/completions/_bat.ps1.in index ac66ccc8..b6f62aae 100644 --- a/assets/completions/_bat.ps1.in +++ b/assets/completions/_bat.ps1.in @@ -32,7 +32,6 @@ Register-ArgumentCompleter -Native -CommandName '{{PROJECT_EXECUTABLE}}' -Script [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('--color-scheme', 'color-scheme', [CompletionResultType]::ParameterName, 'Whether to choose a dark or light syntax highlighting theme (*auto*, auto:always, dark, light, system).') [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++'').') diff --git a/assets/completions/bat.bash.in b/assets/completions/bat.bash.in index 0a01a054..162d1c53 100644 --- a/assets/completions/bat.bash.in +++ b/assets/completions/bat.bash.in @@ -104,8 +104,6 @@ _bat() { COMPREPLY=($(compgen -W "auto never always" -- "$cur")) return 0 ;; - --color-scheme) - COMPREPLY=($(compgen -W "auto auto:always dark light system" -- "$cur")) --italic-text) COMPREPLY=($(compgen -W "always never" -- "$cur")) return 0 @@ -176,7 +174,6 @@ _bat() { --theme --theme-dark --theme-light - --color-scheme --list-themes --squeeze-blank --squeeze-limit diff --git a/assets/completions/bat.fish.in b/assets/completions/bat.fish.in index 33cf8264..9907718f 100644 --- a/assets/completions/bat.fish.in +++ b/assets/completions/bat.fish.in @@ -99,13 +99,6 @@ set -l color_opts ' ' set -l decorations_opts $color_opts set -l paging_opts $color_opts -set -l color_scheme_opts " - auto\t'Use the terminal\'s color scheme if the output is not redirected (default)' - auto:always\t'Always use the terminal\'s color scheme' - dark\t'Use a dark syntax highlighting theme' - light\t'Use a light syntax highlighting theme' - system\t'Query the OS for its color scheme (macOS only)' -" # Include some examples so we can indicate the default. set -l pager_opts ' @@ -150,8 +143,6 @@ complete -c $bat -l config-file -f -d "Display location of configuration file" - complete -c $bat -l decorations -x -a "$decorations_opts" -d "When to use --style decorations" -n __bat_no_excl_args -complete -c $bat -l color-scheme -x -a "$color_scheme_opts" -d "Whether to choose a dark or light syntax highlighting theme" -n __bat_no_excl_args - complete -c $bat -l diagnostic -d "Print diagnostic info for bug reports" -n __fish_is_first_arg complete -c $bat -s d -l diff -d "Only show lines with Git changes" -n __bat_no_excl_args diff --git a/assets/completions/bat.zsh.in b/assets/completions/bat.zsh.in index 4bcae11c..bec0d3c9 100644 --- a/assets/completions/bat.zsh.in +++ b/assets/completions/bat.zsh.in @@ -40,7 +40,6 @@ _{{PROJECT_EXECUTABLE}}_main() { --color='[specify when to use colors]:when:(auto never always)' --italic-text='[use italics in output]:when:(always never)' --decorations='[specify when to show the decorations]:when:(auto never always)' - --color-scheme="[whether to choose a dark or light syntax highlighting theme]:scheme:(auto auto:always dark light system)" --paging='[specify when to use the pager]:when:(auto never always)' '(-m --map-syntax)'{-m+,--map-syntax=}'[map a glob pattern to an existing syntax name]: :->syntax-maps' '(--theme)'--theme='[set the color theme for syntax highlighting]:theme:->themes' diff --git a/assets/manual/bat.1.in b/assets/manual/bat.1.in index 5dcabe2d..05239d50 100644 --- a/assets/manual/bat.1.in +++ b/assets/manual/bat.1.in @@ -117,24 +117,6 @@ Specify when to use the decorations that have been specified via '\-\-style'. Th automatic mode only enables decorations if an interactive terminal is detected. Possible values: *auto*, never, always. .HP -\fB\-\-detect\-color\-scheme\fR -.IP -Specify when to query the terminal for its colors in order to pick an appropriate syntax -highlighting theme. Use \fB\-\-theme-light\fP and \fB\-\-theme-dark\fP (or the environment variables -\fBBAT_THEME_LIGHT\fP and \fBBAT_THEME_DARK\fP) to configure which themes are picked. You can also use -\fP\-\-theme\fP to set a theme that is used regardless of the terminal's colors. -.IP -\fI\fP can be one of: -.RS -.IP "\fBauto\fP" -Only query the terminals colors if the output is not redirected. This is to prevent -race conditions with pagers such as less. -.IP "never" -Never query the terminal for its colors and assume that the terminal has a dark background. -.IP "always" -Always query the terminal for its colors, regardless of whether or not the output is redirected. -.RE -.HP \fB\-f\fR, \fB\-\-force\-colorization\fR .IP Alias for '--decorations=always --color=always'. This is useful \ @@ -177,14 +159,14 @@ export the \fBBAT_THEME\fP environment variable (e.g.: \fBexport BAT_THEME="..." \fB\-\-theme\-dark\fR .IP Sets the theme name for syntax highlighting used when the terminal uses a dark background. -To set a default theme, add the \fB\-\-theme-dark="..."\fP option to the configuration file or +To set a default theme, add the \fB\-\-theme-dark="..."\fP option to the configuration file or export the \fBBAT_THEME_DARK\fP environment variable (e.g. \fBexport BAT_THEME_DARK="..."\fP). This option is ignored if \fB\-\-theme\fP option is set. .HP \fB\-\-theme\-light\fR .IP Sets the theme name for syntax highlighting used when the terminal uses a dark background. -To set a default theme, add the \fB\-\-theme-dark="..."\fP option to the configuration file or +To set a default theme, add the \fB\-\-theme-dark="..."\fP option to the configuration file or export the \fBBAT_THEME_LIGHT\fP environment variable (e.g. \fBexport BAT_THEME_LIGHT="..."\fP). This option is ignored if \fB\-\-theme\fP option is set. .HP @@ -339,7 +321,7 @@ To use the preprocessor, call: \fB{{PROJECT_EXECUTABLE}} --lessopen\fR -Alternatively, the preprocessor may be enabled by default by adding the '\-\-lessopen' option to the configuration file. +Alternatively, the preprocessor may be enabled by default by adding the '\-\-lessopen' option to the configuration file. To temporarily disable the preprocessor if it is enabled by default, call: @@ -355,7 +337,7 @@ Enable the $LESSOPEN preprocessor. .IP Disable the $LESSOPEN preprocessor if enabled (overrides --lessopen) .PP -For more information, see the "INPUT PREPROCESSOR" section of less(1). +For more information, see the "INPUT PREPROCESSOR" section of less(1). .SH "MORE INFORMATION" diff --git a/doc/long-help.txt b/doc/long-help.txt index c374a039..8d5a3a5e 100644 --- a/doc/long-help.txt +++ b/doc/long-help.txt @@ -114,23 +114,6 @@ Options: add the '--theme="..."' option to the configuration file or export the BAT_THEME environment variable (e.g.: export BAT_THEME="..."). - --color-scheme - Specify whether to choose a dark or light syntax highlighting theme. Use '--theme-light' - and '--theme-dark' (or the environment variables BAT_THEME_LIGHT and BAT_THEME_DARK) to - configure which themes are picked. You may also use '--theme' to set a theme that is used - regardless of this choice. - - Possible values: - * auto (default): - Query the terminals for its color scheme if the output is not redirected. This is to - prevent race conditions with pagers such as less. - * 'auto:always': - Always query the terminal for its color scheme, regardless of whether or not the - output is redirected. - * dark: Use a dark syntax highlighting theme. - * light: Use a light syntax highlighting theme. - * system: Query the OS for its color scheme. Only works on macOS. - --theme-light Sets the theme name for syntax highlighting used when the terminal uses a light background. Use '--list-themes' to see all available themes. To set a default theme, add diff --git a/doc/short-help.txt b/doc/short-help.txt index f17a6d9d..d5c35059 100644 --- a/doc/short-help.txt +++ b/doc/short-help.txt @@ -41,8 +41,6 @@ Options: Use the specified syntax for files matching the glob pattern ('*.cpp:C++'). --theme Set the color theme for syntax highlighting. - --color-scheme - Specify whether to choose a dark or light theme. --theme-light Sets the color theme for syntax highlighting used for light backgrounds. --theme-dark diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index d2e5b4db..6d83c9c9 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -9,7 +9,7 @@ use crate::{ config::{get_args_from_config_file, get_args_from_env_opts_var, get_args_from_env_vars}, }; use bat::style::StyleComponentList; -use bat::theme::{theme, ColorSchemePreference, DetectColorScheme, ThemeOptions, ThemeRequest}; +use bat::theme::{theme, ThemeName, ThemeOptions, ThemePreference}; use bat::StripAnsiMode; use clap::ArgMatches; @@ -418,35 +418,20 @@ impl App { let theme = self .matches .get_one::("theme") - .map(|t| ThemeRequest::from_str(t).unwrap()); + .map(|t| ThemePreference::from_str(t).unwrap()) + .unwrap_or_default(); let theme_dark = self .matches .get_one::("theme-dark") - .map(|t| ThemeRequest::from_str(t).unwrap()); + .map(|t| ThemeName::from_str(t).unwrap()); let theme_light = self .matches .get_one::("theme-light") - .map(|t| ThemeRequest::from_str(t).unwrap()); + .map(|t| ThemeName::from_str(t).unwrap()); ThemeOptions { theme, theme_dark, theme_light, - color_scheme: self.color_scheme_preference(), - } - } - - pub(crate) fn color_scheme_preference(&self) -> ColorSchemePreference { - match self - .matches - .get_one::("color-scheme") - .map(|s| s.as_str()) - { - Some("auto") => ColorSchemePreference::Auto(DetectColorScheme::Auto), - Some("auto:always") => ColorSchemePreference::Auto(DetectColorScheme::Always), - Some("dark") => ColorSchemePreference::Dark, - Some("light") => ColorSchemePreference::Light, - Some("system") => ColorSchemePreference::System, - _ => unreachable!("other values for --color-scheme are not allowed"), } } } diff --git a/src/bin/bat/clap_app.rs b/src/bin/bat/clap_app.rs index 0857dba4..e8056f3a 100644 --- a/src/bin/bat/clap_app.rs +++ b/src/bin/bat/clap_app.rs @@ -383,32 +383,6 @@ pub fn build_app(interactive_output: bool) -> Command { BAT_THEME=\"...\").", ), ) - .arg( - Arg::new("color-scheme") - .long("color-scheme") - .overrides_with("color-scheme") - .value_name("scheme") - .value_parser(["auto", "auto:always", "dark", "light", "system"]) - .default_value("auto") - .hide_default_value(true) - .help("Specify whether to choose a dark or light theme.") - .long_help( - "Specify whether to choose a dark or light syntax highlighting theme. \ - Use '--theme-light' and '--theme-dark' (or the environment variables \ - BAT_THEME_LIGHT and BAT_THEME_DARK) to configure which themes are picked. \ - You may also use '--theme' to set a theme that is used regardless of this choice.\n\n\ - Possible values:\n\ - * auto (default):\n \ - Query the terminals for its color scheme if the output is not redirected. \ - This is to prevent race conditions with pagers such as less.\n\ - * 'auto:always':\n \ - Always query the terminal for its color scheme, \ - regardless of whether or not the output is redirected.\n\ - * dark: Use a dark syntax highlighting theme.\n\ - * light: Use a light syntax highlighting theme.\n\ - * system: Query the OS for its color scheme. Only works on macOS.\n\ - "), - ) .arg( Arg::new("theme-light") .long("theme-light") diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs index 891390c1..09470504 100644 --- a/src/bin/bat/main.rs +++ b/src/bin/bat/main.rs @@ -384,7 +384,7 @@ fn run() -> Result { &config, config_dir, cache_dir, - app.color_scheme_preference(), + ColorSchemePreference::default(), )?; Ok(true) } else if app.matches.get_flag("config-file") { diff --git a/src/theme.rs b/src/theme.rs index 1c6d7e55..5f6460cc 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -29,46 +29,79 @@ pub fn color_scheme(preference: ColorSchemePreference) -> ColorScheme { #[derive(Debug, Default, PartialEq, Eq)] pub struct ThemeOptions { /// Always use this theme regardless of the terminal's background color. - pub theme: Option, + /// This corresponds with the `BAT_THEME` environment variable and the `--theme` option. + pub theme: ThemePreference, /// The theme to use in case the terminal uses a dark background with light text. - pub theme_dark: Option, + /// This corresponds with the `BAT_THEME_DARK` environment variable and the `--theme-dark` option. + pub theme_dark: Option, /// The theme to use in case the terminal uses a light background with dark text. - pub theme_light: Option, - /// How to choose between dark and light. - pub color_scheme: ColorSchemePreference, + /// This corresponds with the `BAT_THEME_LIGHT` environment variable and the `--theme-light` option. + pub theme_light: Option, +} + +/// What theme should `bat` use? +#[derive(Debug, PartialEq, Eq, Hash)] +pub enum ThemePreference { + /// Choose between [`ThemeOptions::theme_dark`] and [`ThemeOptions::theme_light`] + /// based on the terminal's (or the OS') color scheme. + Auto(ColorSchemePreference), + /// Always use the same theme regardless of the terminal's color scheme. + Fixed(ThemeName), +} + +impl Default for ThemePreference { + fn default() -> Self { + ThemePreference::Auto(ColorSchemePreference::default()) + } +} + +impl FromStr for ThemePreference { + type Err = Infallible; + + fn from_str(s: &str) -> Result { + use ThemePreference::*; + match s { + "auto" => Ok(Auto(ColorSchemePreference::default())), + "auto:always" => Ok(Auto(ColorSchemePreference::Auto(DetectColorScheme::Always))), + "auto:system" => Ok(Auto(ColorSchemePreference::System)), + "dark" => Ok(Auto(ColorSchemePreference::Dark)), + "light" => Ok(Auto(ColorSchemePreference::Light)), + _ => ThemeName::from_str(s).map(Fixed), + } + } } /// The name of a theme or the default theme. /// /// ``` -/// # use bat::theme::ThemeRequest; +/// # use bat::theme::ThemeName; /// # use std::str::FromStr as _; -/// assert_eq!(ThemeRequest::Default, ThemeRequest::from_str("default").unwrap()); -/// assert_eq!(ThemeRequest::Named("example".to_string()), ThemeRequest::from_str("example").unwrap()); +/// assert_eq!(ThemeName::Default, ThemeName::from_str("default").unwrap()); +/// assert_eq!(ThemeName::Named("example".to_string()), ThemeName::from_str("example").unwrap()); /// ``` #[derive(Debug, PartialEq, Eq, Hash)] -pub enum ThemeRequest { +pub enum ThemeName { Named(String), Default, } -impl FromStr for ThemeRequest { +impl FromStr for ThemeName { type Err = Infallible; fn from_str(s: &str) -> Result { if s == "default" { - Ok(ThemeRequest::Default) + Ok(ThemeName::Default) } else { - Ok(ThemeRequest::Named(s.to_owned())) + Ok(ThemeName::Named(s.to_owned())) } } } -impl ThemeRequest { +impl ThemeName { fn into_theme(self, color_scheme: ColorScheme) -> String { match self { - ThemeRequest::Named(t) => t, - ThemeRequest::Default => default_theme(color_scheme).to_owned(), + ThemeName::Named(t) => t, + ThemeName::Default => default_theme(color_scheme).to_owned(), } } } @@ -124,17 +157,18 @@ fn color_scheme_impl( fn theme_from_detector(options: ThemeOptions, detector: &dyn ColorSchemeDetector) -> String { // Implementation note: This function is mostly pure (i.e. it has no side effects) for the sake of testing. // All the side effects (e.g. querying the terminal for its colors) are performed in the detector. - if let Some(theme) = options.theme { - theme.into_theme(ColorScheme::default()) - } else { - let color_scheme = color_scheme_impl(options.color_scheme, detector); - choose_theme(options, color_scheme) - .map(|t| t.into_theme(color_scheme)) - .unwrap_or_else(|| default_theme(color_scheme).to_owned()) + match options.theme { + ThemePreference::Fixed(theme_name) => theme_name.into_theme(ColorScheme::default()), + ThemePreference::Auto(color_scheme_preference) => { + let color_scheme = color_scheme_impl(color_scheme_preference, detector); + choose_theme(options, color_scheme) + .map(|t| t.into_theme(color_scheme)) + .unwrap_or_else(|| default_theme(color_scheme).to_owned()) + } } } -fn choose_theme(options: ThemeOptions, color_scheme: ColorScheme) -> Option { +fn choose_theme(options: ThemeOptions, color_scheme: ColorScheme) -> Option { match color_scheme { ColorScheme::Dark => options.theme_dark, ColorScheme::Light => options.theme_light, @@ -220,7 +254,6 @@ impl ColorSchemeDetector for Option { mod tests { use super::ColorScheme::*; use super::ColorSchemePreference as Pref; - use super::DetectColorScheme::*; use super::*; use std::cell::Cell; use std::iter; @@ -233,7 +266,7 @@ mod tests { for pref in [Pref::Dark, Pref::Light] { let detector = DetectorStub::should_detect(Some(Dark)); let options = ThemeOptions { - color_scheme: pref, + theme: ThemePreference::Auto(pref), ..Default::default() }; _ = theme_from_detector(options, &detector); @@ -249,7 +282,9 @@ mod tests { ]; for detector in detectors { let options = ThemeOptions { - color_scheme: Pref::Auto(Always), + theme: ThemePreference::Auto(ColorSchemePreference::Auto( + DetectColorScheme::Always, + )), ..Default::default() }; _ = theme_from_detector(options, &detector); @@ -280,13 +315,13 @@ mod tests { for color_scheme in optional(color_schemes()) { for options in [ ThemeOptions { - theme: Some(ThemeRequest::Named("Theme".to_string())), + theme: ThemePreference::Fixed(ThemeName::Named("Theme".to_string())), ..Default::default() }, ThemeOptions { - theme: Some(ThemeRequest::Named("Theme".to_string())), - theme_dark: Some(ThemeRequest::Named("Dark Theme".to_string())), - theme_light: Some(ThemeRequest::Named("Light Theme".to_string())), + theme: ThemePreference::Fixed(ThemeName::Named("Theme".to_string())), + theme_dark: Some(ThemeName::Named("Dark Theme".to_string())), + theme_light: Some(ThemeName::Named("Light Theme".to_string())), ..Default::default() }, ] { @@ -299,7 +334,7 @@ mod tests { #[test] fn detector_is_not_called_if_theme_is_present() { let options = ThemeOptions { - theme: Some(ThemeRequest::Named("Theme".to_string())), + theme: ThemePreference::Fixed(ThemeName::Named("Theme".to_string())), ..Default::default() }; let detector = DetectorStub::should_detect(Some(Dark)); @@ -326,7 +361,7 @@ mod tests { fn dark_if_requested_explicitly_through_theme() { for color_scheme in optional(color_schemes()) { let options = ThemeOptions { - theme: Some(ThemeRequest::Default), + theme: ThemePreference::Fixed(ThemeName::Default), ..Default::default() }; let detector = ConstantDetector(color_scheme); @@ -343,8 +378,8 @@ mod tests { for options in [ ThemeOptions::default(), ThemeOptions { - theme_dark: Some(ThemeRequest::Default), - theme_light: Some(ThemeRequest::Default), + theme_dark: Some(ThemeName::Default), + theme_light: Some(ThemeName::Default), ..Default::default() }, ] { @@ -365,8 +400,8 @@ mod tests { fn chooses_dark_theme_if_dark_or_unknown() { for color_scheme in [Some(Dark), None] { let options = ThemeOptions { - theme_dark: Some(ThemeRequest::Named("Dark".to_string())), - theme_light: Some(ThemeRequest::Named("Light".to_string())), + theme_dark: Some(ThemeName::Named("Dark".to_string())), + theme_light: Some(ThemeName::Named("Light".to_string())), ..Default::default() }; let detector = ConstantDetector(color_scheme); @@ -377,8 +412,8 @@ mod tests { #[test] fn chooses_light_theme_if_light() { let options = ThemeOptions { - theme_dark: Some(ThemeRequest::Named("Dark".to_string())), - theme_light: Some(ThemeRequest::Named("Light".to_string())), + theme_dark: Some(ThemeName::Named("Dark".to_string())), + theme_light: Some(ThemeName::Named("Light".to_string())), ..Default::default() }; let detector = ConstantDetector(Some(ColorScheme::Light)); diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index e4b73c59..582b255d 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -279,7 +279,6 @@ fn list_themes_with_colors() { bat() .arg("--color=always") - .arg("--color-scheme=dark") .arg("--list-themes") .assert() .success() @@ -296,7 +295,6 @@ fn list_themes_without_colors() { bat() .arg("--color=never") - .arg("--color-scheme=dark") .arg("--list-themes") .assert() .success() From 89ce06018340fda343072022000891e03a3b372b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Sun, 18 Aug 2024 20:32:23 +0200 Subject: [PATCH 48/91] Update help, man page and completions --- assets/completions/bat.bash.in | 7 ++++++- assets/completions/bat.fish.in | 10 +++++++++- assets/completions/bat.zsh.in | 10 ++++++++-- assets/manual/bat.1.in | 19 +++++++++++++++++-- doc/long-help.txt | 16 ++++++++++++---- src/bin/bat/clap_app.rs | 12 +++++++++--- src/theme.rs | 4 ++++ 7 files changed, 65 insertions(+), 13 deletions(-) diff --git a/assets/completions/bat.bash.in b/assets/completions/bat.bash.in index 162d1c53..90931f24 100644 --- a/assets/completions/bat.bash.in +++ b/assets/completions/bat.bash.in @@ -112,7 +112,12 @@ _bat() { COMPREPLY=($(compgen -c -- "$cur")) return 0 ;; - --theme | \ + --theme) + local IFS=$'\n' + COMPREPLY=($(compgen -W "auto${IFS}auto:always${IFS}auto:system${IFS}dark${IFS}light${IFS}$("$1" --list-themes)" -- "$cur")) + __bat_escape_completions + return 0 + ;; --theme-dark | \ --theme-light) local IFS=$'\n' diff --git a/assets/completions/bat.fish.in b/assets/completions/bat.fish.in index 9907718f..e2712706 100644 --- a/assets/completions/bat.fish.in +++ b/assets/completions/bat.fish.in @@ -129,6 +129,14 @@ set -l tabs_opts ' 8\t ' +set -l special_themes ' + auto\tdefault,\ Choose\ a\ theme\ based\ on\ dark\ or\ light\ mode + auto:always\tChoose\ a\ theme\ based\ on\ dark\ or\ light\ mode + auto:system\tChoose\ a\ theme\ based\ on\ dark\ or\ light\ mode + dark\tUse\ the\ theme\ specified\ by\ --theme-dark + light\tUse\ the\ theme\ specified\ by\ --theme-light +' + # Completions: complete -c $bat -l acknowledgements -d "Print acknowledgements" -n __fish_is_first_arg @@ -203,7 +211,7 @@ complete -c $bat -l tabs -x -a "$tabs_opts" -d "Set tab width" -n __bat_no_excl_ complete -c $bat -l terminal-width -x -d "Set terminal , +, or -" -n __bat_no_excl_args -complete -c $bat -l theme -x -a "(command $bat --list-themes | command cat)" -d "Set the syntax highlighting theme" -n __bat_no_excl_args +complete -c $bat -l theme -x -a "$special_themes(command $bat --list-themes | command cat)" -d "Set the syntax highlighting theme" -n __bat_no_excl_args complete -c $bat -l theme-dark -x -a "(command $bat --list-themes | command cat)" -d "Set the syntax highlighting theme for dark backgrounds" -n __bat_no_excl_args diff --git a/assets/completions/bat.zsh.in b/assets/completions/bat.zsh.in index bec0d3c9..76b981b6 100644 --- a/assets/completions/bat.zsh.in +++ b/assets/completions/bat.zsh.in @@ -42,7 +42,7 @@ _{{PROJECT_EXECUTABLE}}_main() { --decorations='[specify when to show the decorations]:when:(auto never always)' --paging='[specify when to use the pager]:when:(auto never always)' '(-m --map-syntax)'{-m+,--map-syntax=}'[map a glob pattern to an existing syntax name]: :->syntax-maps' - '(--theme)'--theme='[set the color theme for syntax highlighting]:theme:->themes' + '(--theme)'--theme='[set the color theme for syntax highlighting]:theme:->theme_preferences' '(--theme-dark)'--theme-dark='[set the color theme for syntax highlighting for dark backgrounds]:theme:->themes' '(--theme-light)'--theme-light='[set the color theme for syntax highlighting for light backgrounds]:theme:->themes' '(: --list-themes --list-languages -L)'--list-themes'[show all supported highlighting themes]' @@ -84,7 +84,13 @@ _{{PROJECT_EXECUTABLE}}_main() { themes) local -a themes expl - themes=( ${(f)"$(_call_program themes {{PROJECT_EXECUTABLE}} --list-themes)"} ) + themes=(${(f)"$(_call_program themes {{PROJECT_EXECUTABLE}} --list-themes)"} ) + + _wanted themes expl 'theme' compadd -a themes && ret=0 + ;; + theme_preferences) + local -a themes expl + themes=(auto dark light auto:always auto:system ${(f)"$(_call_program themes {{PROJECT_EXECUTABLE}} --list-themes)"} ) _wanted themes expl 'theme' compadd -a themes && ret=0 ;; diff --git a/assets/manual/bat.1.in b/assets/manual/bat.1.in index 05239d50..ccc70629 100644 --- a/assets/manual/bat.1.in +++ b/assets/manual/bat.1.in @@ -155,20 +155,35 @@ will use JSON syntax, and ignore '.dev' Set the theme for syntax highlighting. Use \fB\-\-list\-themes\fP to see all available themes. To set a default theme, add the \fB\-\-theme="..."\fP option to the configuration file or export the \fBBAT_THEME\fP environment variable (e.g.: \fBexport BAT_THEME="..."\fP). + +Special values: +.RS +.IP "auto (\fIdefault\fR)" +Picks a dark or light theme depending on the terminal's colors. +Use \fB-\-theme\-light\fR and \fB-\-theme\-dark\fR to customize the selected theme. +.IP "auto:always" +Variation of \fBauto\fR where where the terminal's colors are detected even when the output is redirected. +.IP "auto:system (macOS only)" +Variation of \fBauto\fR where the color scheme is detected from the system-wide preference instead. +.IP "dark" +Use the dark theme specified by \fB-\-theme-dark\fR. +.IP "light" +Use the light theme specified by \fB-\-theme-light\fR. +.RE .HP \fB\-\-theme\-dark\fR .IP Sets the theme name for syntax highlighting used when the terminal uses a dark background. To set a default theme, add the \fB\-\-theme-dark="..."\fP option to the configuration file or export the \fBBAT_THEME_DARK\fP environment variable (e.g. \fBexport BAT_THEME_DARK="..."\fP). -This option is ignored if \fB\-\-theme\fP option is set. +This option only has an effect when \fB\-\-theme\fP option is set to \fBauto\fR or \fBdark\fR. .HP \fB\-\-theme\-light\fR .IP Sets the theme name for syntax highlighting used when the terminal uses a dark background. To set a default theme, add the \fB\-\-theme-dark="..."\fP option to the configuration file or export the \fBBAT_THEME_LIGHT\fP environment variable (e.g. \fBexport BAT_THEME_LIGHT="..."\fP). -This option is ignored if \fB\-\-theme\fP option is set. +This option only has an effect when \fB\-\-theme\fP option is set to \fBauto\fR or \fBlight\fR. .HP \fB\-\-list\-themes\fR .IP diff --git a/doc/long-help.txt b/doc/long-help.txt index 8d5a3a5e..e93c8a2d 100644 --- a/doc/long-help.txt +++ b/doc/long-help.txt @@ -109,10 +109,18 @@ Options: 'bat --ignored-suffix ".dev" my_file.json.dev' will use JSON syntax, and ignore '.dev' --theme - Set the theme for syntax highlighting. Note that this option overrides '--theme-dark' and - '--theme-light'. Use '--list-themes' to see all available themes. To set a default theme, - add the '--theme="..."' option to the configuration file or export the BAT_THEME - environment variable (e.g.: export BAT_THEME="..."). + Set the theme for syntax highlighting. Use '--list-themes' to see all available themes. To + set a default theme, add the '--theme="..."' option to the configuration file or export + the BAT_THEME environment variable (e.g.: export BAT_THEME="..."). + + Special values: + + * auto: Picks a dark or light theme depending on the terminal's colors (default). + Use '--theme-light' and '--theme-dark' to customize the selected theme. + * auto:always: Detect the terminal's colors even when the output is redirected. + * auto:system: Detect the color scheme from the system-wide preference (macOS only). + * dark: Use the dark theme specified by '--theme-dark'. + * light: Use the light theme specified by '--theme-light'. --theme-light Sets the theme name for syntax highlighting used when the terminal uses a light diff --git a/src/bin/bat/clap_app.rs b/src/bin/bat/clap_app.rs index e8056f3a..0fb34748 100644 --- a/src/bin/bat/clap_app.rs +++ b/src/bin/bat/clap_app.rs @@ -375,12 +375,18 @@ pub fn build_app(interactive_output: bool) -> Command { .overrides_with("theme") .help("Set the color theme for syntax highlighting.") .long_help( - "Set the theme for syntax highlighting. Note that this option overrides \ - '--theme-dark' and '--theme-light'. Use '--list-themes' to \ + "Set the theme for syntax highlighting. Use '--list-themes' to \ see all available themes. To set a default theme, add the \ '--theme=\"...\"' option to the configuration file or export the \ BAT_THEME environment variable (e.g.: export \ - BAT_THEME=\"...\").", + BAT_THEME=\"...\").\n\n\ + Special values:\n\n \ + * auto: Picks a dark or light theme depending on the terminal's colors (default).\n \ + Use '--theme-light' and '--theme-dark' to customize the selected theme.\n \ + * auto:always: Detect the terminal's colors even when the output is redirected.\n \ + * auto:system: Detect the color scheme from the system-wide preference (macOS only).\n \ + * dark: Use the dark theme specified by '--theme-dark'.\n \ + * light: Use the light theme specified by '--theme-light'.", ), ) .arg( diff --git a/src/theme.rs b/src/theme.rs index 5f6460cc..dcac4e6c 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -216,6 +216,10 @@ impl ColorSchemeDetector for TerminalColorSchemeDetector { #[cfg(not(target_os = "macos"))] fn color_scheme_from_system() -> Option { + crate::bat_warning!( + "Theme 'auto:system' is only supported on macOS, \ + using default." + ); None } From 50958472e5b4bdc7451a609b877dc8d5f6a4860e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Fri, 23 Aug 2024 18:03:07 +0200 Subject: [PATCH 49/91] Return theme alongside detected color scheme --- src/bin/bat/app.rs | 2 +- src/theme.rs | 92 ++++++++++++++++++++++++++++++---------------- 2 files changed, 61 insertions(+), 33 deletions(-) diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index 6d83c9c9..f58d4965 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -254,7 +254,7 @@ impl App { Some("auto") => StripAnsiMode::Auto, _ => unreachable!("other values for --strip-ansi are not allowed"), }, - theme: theme(self.theme_options()), + theme: theme(self.theme_options()).to_string(), visible_lines: match self.matches.try_contains_id("diff").unwrap_or_default() && self.matches.get_flag("diff") { diff --git a/src/theme.rs b/src/theme.rs index dcac4e6c..ae36b6f9 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -1,13 +1,18 @@ //! Utilities for choosing an appropriate theme for syntax highlighting. use std::convert::Infallible; +use std::fmt; use std::io::IsTerminal as _; use std::str::FromStr; /// Chooses an appropriate theme or falls back to a default theme /// based on the user-provided options and the color scheme of the terminal. -pub fn theme(options: ThemeOptions) -> String { - theme_from_detector(options, &TerminalColorSchemeDetector) +/// +/// Intentionally returns a [`ThemeResult`] instead of a simple string so +/// that downstream consumers such as `delta` can easily apply their own +/// default theme and can use the detected color scheme elsewhere. +pub fn theme(options: ThemeOptions) -> ThemeResult { + theme_impl(options, &TerminalColorSchemeDetector) } /// The default theme, suitable for the given color scheme. @@ -26,7 +31,7 @@ pub fn color_scheme(preference: ColorSchemePreference) -> ColorScheme { /// Options for configuring the theme used for syntax highlighting. /// Used together with [`theme`]. -#[derive(Debug, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct ThemeOptions { /// Always use this theme regardless of the terminal's background color. /// This corresponds with the `BAT_THEME` environment variable and the `--theme` option. @@ -40,7 +45,14 @@ pub struct ThemeOptions { } /// What theme should `bat` use? -#[derive(Debug, PartialEq, Eq, Hash)] +/// +/// The easiest way to construct this is from a string: +/// ``` +/// # use bat::theme::ThemePreference; +/// # use std::str::FromStr as _; +/// let preference = ThemePreference::from_str("auto:system").unwrap(); +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ThemePreference { /// Choose between [`ThemeOptions::theme_dark`] and [`ThemeOptions::theme_light`] /// based on the terminal's (or the OS') color scheme. @@ -79,7 +91,7 @@ impl FromStr for ThemePreference { /// assert_eq!(ThemeName::Default, ThemeName::from_str("default").unwrap()); /// assert_eq!(ThemeName::Named("example".to_string()), ThemeName::from_str("example").unwrap()); /// ``` -#[derive(Debug, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ThemeName { Named(String), Default, @@ -97,15 +109,6 @@ impl FromStr for ThemeName { } } -impl ThemeName { - fn into_theme(self, color_scheme: ColorScheme) -> String { - match self { - ThemeName::Named(t) => t, - ThemeName::Default => default_theme(color_scheme).to_owned(), - } - } -} - /// How to choose between dark and light. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ColorSchemePreference { @@ -142,6 +145,26 @@ pub enum ColorScheme { Light, } +/// The resolved theme and the color scheme as determined from +/// the terminal, OS or fallback. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ThemeResult { + /// The theme selected according to the [`ThemeOptions`]. + pub theme: ThemeName, + /// Either the user's chosen color scheme, the terminal's color scheme, the OS's + /// color scheme or `None` if the color scheme was not detected because the user chose a fixed theme. + pub color_scheme: Option, +} + +impl fmt::Display for ThemeResult { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.theme { + ThemeName::Named(name) => f.write_str(name), + ThemeName::Default => f.write_str(default_theme(self.color_scheme.unwrap_or_default())), + } + } +} + fn color_scheme_impl( pref: ColorSchemePreference, detector: &dyn ColorSchemeDetector, @@ -154,16 +177,21 @@ fn color_scheme_impl( } } -fn theme_from_detector(options: ThemeOptions, detector: &dyn ColorSchemeDetector) -> String { +fn theme_impl(options: ThemeOptions, detector: &dyn ColorSchemeDetector) -> ThemeResult { // Implementation note: This function is mostly pure (i.e. it has no side effects) for the sake of testing. // All the side effects (e.g. querying the terminal for its colors) are performed in the detector. match options.theme { - ThemePreference::Fixed(theme_name) => theme_name.into_theme(ColorScheme::default()), - ThemePreference::Auto(color_scheme_preference) => { - let color_scheme = color_scheme_impl(color_scheme_preference, detector); - choose_theme(options, color_scheme) - .map(|t| t.into_theme(color_scheme)) - .unwrap_or_else(|| default_theme(color_scheme).to_owned()) + ThemePreference::Fixed(theme) => ThemeResult { + theme, + color_scheme: None, + }, + ThemePreference::Auto(pref) => { + let color_scheme = color_scheme_impl(pref, detector); + let theme = choose_theme(options, color_scheme).unwrap_or(ThemeName::Default); + ThemeResult { + theme, + color_scheme: Some(color_scheme), + } } } } @@ -273,7 +301,7 @@ mod tests { theme: ThemePreference::Auto(pref), ..Default::default() }; - _ = theme_from_detector(options, &detector); + _ = theme_impl(options, &detector); assert!(!detector.was_called.get()); } } @@ -291,7 +319,7 @@ mod tests { )), ..Default::default() }; - _ = theme_from_detector(options, &detector); + _ = theme_impl(options, &detector); assert!(detector.was_called.get()); } } @@ -299,14 +327,14 @@ mod tests { #[test] fn called_for_auto_if_should_detect() { let detector = DetectorStub::should_detect(Some(Dark)); - _ = theme_from_detector(ThemeOptions::default(), &detector); + _ = theme_impl(ThemeOptions::default(), &detector); assert!(detector.was_called.get()); } #[test] fn not_called_for_auto_if_not_should_detect() { let detector = DetectorStub::should_not_detect(); - _ = theme_from_detector(ThemeOptions::default(), &detector); + _ = theme_impl(ThemeOptions::default(), &detector); assert!(!detector.was_called.get()); } } @@ -330,7 +358,7 @@ mod tests { }, ] { let detector = ConstantDetector(color_scheme); - assert_eq!("Theme", theme_from_detector(options, &detector)); + assert_eq!("Theme", theme_impl(options, &detector).to_string()); } } } @@ -342,7 +370,7 @@ mod tests { ..Default::default() }; let detector = DetectorStub::should_detect(Some(Dark)); - _ = theme_from_detector(options, &detector); + _ = theme_impl(options, &detector); assert!(!detector.was_called.get()); } } @@ -355,7 +383,7 @@ mod tests { let detector = ConstantDetector(None); assert_eq!( default_theme(ColorScheme::Dark), - theme_from_detector(ThemeOptions::default(), &detector) + theme_impl(ThemeOptions::default(), &detector).to_string() ); } @@ -371,7 +399,7 @@ mod tests { let detector = ConstantDetector(color_scheme); assert_eq!( default_theme(ColorScheme::Dark), - theme_from_detector(options, &detector) + theme_impl(options, &detector).to_string() ); } } @@ -390,7 +418,7 @@ mod tests { let detector = ConstantDetector(Some(color_scheme)); assert_eq!( default_theme(color_scheme), - theme_from_detector(options, &detector) + theme_impl(options, &detector).to_string() ); } } @@ -409,7 +437,7 @@ mod tests { ..Default::default() }; let detector = ConstantDetector(color_scheme); - assert_eq!("Dark", theme_from_detector(options, &detector)); + assert_eq!("Dark", theme_impl(options, &detector).to_string()); } } @@ -421,7 +449,7 @@ mod tests { ..Default::default() }; let detector = ConstantDetector(Some(ColorScheme::Light)); - assert_eq!("Light", theme_from_detector(options, &detector)); + assert_eq!("Light", theme_impl(options, &detector).to_string()); } } From 0603f0b844707d3f9de7a9a7f110fd5a4f2743a9 Mon Sep 17 00:00:00 2001 From: einfachIrgendwer0815 <85333734+einfachIrgendwer0815@users.noreply.github.com> Date: Sun, 25 Aug 2024 10:54:46 +0200 Subject: [PATCH 50/91] Don't output default theme info to piped stdout --- src/bin/bat/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs index 4528a60b..3b74ec75 100644 --- a/src/bin/bat/main.rs +++ b/src/bin/bat/main.rs @@ -202,7 +202,7 @@ pub fn list_themes(cfg: &Config, config_dir: &Path, cache_dir: &Path) -> Result< let default_theme = HighlightingAssets::default_theme(); for theme in assets.themes() { - let default_theme_info = if default_theme == theme { + let default_theme_info = if !config.loop_through && default_theme == theme { " (default)" } else { "" From 5be9d195003c00754885b2e3bb00cf13b6e707d2 Mon Sep 17 00:00:00 2001 From: einfachIrgendwer0815 <85333734+einfachIrgendwer0815@users.noreply.github.com> Date: Sun, 25 Aug 2024 11:07:29 +0200 Subject: [PATCH 51/91] Reinforce tests for `--list-themes` --- tests/integration_tests.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 8df4327c..c083a941 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -300,6 +300,7 @@ fn list_themes_without_colors() { bat() .arg("--color=never") + .arg("--decorations=always") // trick bat into setting `Config::loop_through` to false .arg("--list-themes") .assert() .success() @@ -307,6 +308,15 @@ fn list_themes_without_colors() { .stdout(predicate::str::contains(default_theme_chunk).normalize()); } +#[test] +fn list_themes_to_piped_output() { + bat() + .arg("--list-themes") + .assert() + .success() + .stdout(predicate::str::contains("(default)").not()); +} + #[test] #[cfg_attr(any(not(feature = "git"), target_os = "windows"), ignore)] fn short_help() { From c1772b7793f94c50fba2efc2b6b8091654cff010 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 02:45:53 +0000 Subject: [PATCH 52/91] Bump plist from 1.6.0 to 1.7.0 (#3076) Bumps [plist](https://github.com/ebarnard/rust-plist) from 1.6.0 to 1.7.0. - [Release notes](https://github.com/ebarnard/rust-plist/releases) - [Commits](https://github.com/ebarnard/rust-plist/compare/v1.6.0...v1.7.0) --- updated-dependencies: - dependency-name: plist dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 28 ++++++---------------------- Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1168501a..f185750b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -103,9 +103,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base64" -version = "0.21.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bat" @@ -716,15 +716,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "line-wrap" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" -dependencies = [ - "safemem", -] - [[package]] name = "linked-hash-map" version = "0.5.6" @@ -892,13 +883,12 @@ checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" [[package]] name = "plist" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef" +checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" dependencies = [ "base64", "indexmap", - "line-wrap", "quick-xml", "serde", "time", @@ -957,9 +947,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" dependencies = [ "memchr", ] @@ -1087,12 +1077,6 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" -[[package]] -name = "safemem" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" - [[package]] name = "same-file" version = "1.0.6" diff --git a/Cargo.toml b/Cargo.toml index 720e629b..0740b291 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,7 +86,7 @@ features = ["wrap_help", "cargo"] [target.'cfg(target_os = "macos")'.dependencies] home = "0.5.9" -plist = "1.6.0" +plist = "1.7.0" [dev-dependencies] assert_cmd = "2.0.12" From ffa74a7f499bc1d3ceb62bff3f8e1453a87f4ca6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 03:05:26 +0000 Subject: [PATCH 53/91] Bump serde from 1.0.199 to 1.0.209 (#3077) Bumps [serde](https://github.com/serde-rs/serde) from 1.0.199 to 1.0.209. - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.199...v1.0.209) --- updated-dependencies: - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f185750b..4fcb870b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1100,18 +1100,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.199" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.199" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", From 16d9b99f6cbdf4529f8e638f22cd1561ee95160e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Wed, 4 Sep 2024 21:18:29 +0200 Subject: [PATCH 54/91] Flatten preference enum --- src/bin/bat/main.rs | 14 ++--- src/theme.rs | 121 ++++++++++++++++++++------------------------ 2 files changed, 60 insertions(+), 75 deletions(-) diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs index 09470504..2b27eff4 100644 --- a/src/bin/bat/main.rs +++ b/src/bin/bat/main.rs @@ -14,6 +14,7 @@ use std::io::{BufReader, Write}; use std::path::Path; use std::process; +use bat::theme::DetectColorScheme; use nu_ansi_term::Color::Green; use nu_ansi_term::Style; @@ -35,7 +36,7 @@ use bat::{ error::*, input::Input, style::{StyleComponent, StyleComponents}, - theme::{color_scheme, default_theme, ColorScheme, ColorSchemePreference}, + theme::{color_scheme, default_theme, ColorScheme}, MappingTarget, PagingMode, }; @@ -193,7 +194,7 @@ pub fn list_themes( cfg: &Config, config_dir: &Path, cache_dir: &Path, - color_scheme_pref: ColorSchemePreference, + detect_color_scheme: DetectColorScheme, ) -> Result<()> { let assets = assets_from_cache_or_binary(cfg.use_custom_assets, cache_dir)?; let mut config = cfg.clone(); @@ -205,7 +206,7 @@ pub fn list_themes( let stdout = io::stdout(); let mut stdout = stdout.lock(); - let default_theme_name = default_theme(color_scheme(color_scheme_pref)); + let default_theme_name = default_theme(color_scheme(detect_color_scheme).unwrap_or_default()); for theme in assets.themes() { let default_theme_info = if default_theme_name == theme { " (default)" @@ -380,12 +381,7 @@ fn run() -> Result { }; run_controller(inputs, &plain_config, cache_dir) } else if app.matches.get_flag("list-themes") { - list_themes( - &config, - config_dir, - cache_dir, - ColorSchemePreference::default(), - )?; + list_themes(&config, config_dir, cache_dir, DetectColorScheme::default())?; Ok(true) } else if app.matches.get_flag("config-file") { println!("{}", config_file().to_string_lossy()); diff --git a/src/theme.rs b/src/theme.rs index ae36b6f9..7b41ff4a 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -25,8 +25,8 @@ pub const fn default_theme(color_scheme: ColorScheme) -> &'static str { } /// Detects the color scheme from the terminal. -pub fn color_scheme(preference: ColorSchemePreference) -> ColorScheme { - color_scheme_impl(preference, &TerminalColorSchemeDetector) +pub fn color_scheme(when: DetectColorScheme) -> Option { + detect(when, &TerminalColorSchemeDetector) } /// Options for configuring the theme used for syntax highlighting. @@ -55,15 +55,19 @@ pub struct ThemeOptions { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ThemePreference { /// Choose between [`ThemeOptions::theme_dark`] and [`ThemeOptions::theme_light`] - /// based on the terminal's (or the OS') color scheme. - Auto(ColorSchemePreference), + /// based on the terminal's color scheme. + Auto(DetectColorScheme), /// Always use the same theme regardless of the terminal's color scheme. Fixed(ThemeName), + /// Use a dark theme. + Dark, + /// Use a light theme. + Light, } impl Default for ThemePreference { fn default() -> Self { - ThemePreference::Auto(ColorSchemePreference::default()) + ThemePreference::Auto(Default::default()) } } @@ -73,11 +77,11 @@ impl FromStr for ThemePreference { fn from_str(s: &str) -> Result { use ThemePreference::*; match s { - "auto" => Ok(Auto(ColorSchemePreference::default())), - "auto:always" => Ok(Auto(ColorSchemePreference::Auto(DetectColorScheme::Always))), - "auto:system" => Ok(Auto(ColorSchemePreference::System)), - "dark" => Ok(Auto(ColorSchemePreference::Dark)), - "light" => Ok(Auto(ColorSchemePreference::Light)), + "auto" => Ok(Auto(Default::default())), + "auto:always" => Ok(Auto(DetectColorScheme::Always)), + "auto:system" => Ok(Auto(DetectColorScheme::System)), + "dark" => Ok(Dark), + "light" => Ok(Light), _ => ThemeName::from_str(s).map(Fixed), } } @@ -109,25 +113,6 @@ impl FromStr for ThemeName { } } -/// How to choose between dark and light. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum ColorSchemePreference { - /// Detect the color scheme from the terminal. - Auto(DetectColorScheme), - /// Use a dark theme. - Dark, - /// Use a light theme. - Light, - /// Detect the color scheme from the OS instead (macOS only). - System, -} - -impl Default for ColorSchemePreference { - fn default() -> Self { - Self::Auto(DetectColorScheme::default()) - } -} - #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] pub enum DetectColorScheme { /// Only query the terminal for its colors when appropriate (i.e. when the the output is not redirected). @@ -135,6 +120,8 @@ pub enum DetectColorScheme { Auto, /// Always query the terminal for its colors. Always, + /// Detect the system-wide dark/light preference (macOS only). + System, } /// The color scheme used to pick a fitting theme. Defaults to [`ColorScheme::Dark`]. @@ -165,18 +152,6 @@ impl fmt::Display for ThemeResult { } } -fn color_scheme_impl( - pref: ColorSchemePreference, - detector: &dyn ColorSchemeDetector, -) -> ColorScheme { - match pref { - ColorSchemePreference::Auto(when) => detect(when, detector).unwrap_or_default(), - ColorSchemePreference::Dark => ColorScheme::Dark, - ColorSchemePreference::Light => ColorScheme::Light, - ColorSchemePreference::System => color_scheme_from_system().unwrap_or_default(), - } -} - fn theme_impl(options: ThemeOptions, detector: &dyn ColorSchemeDetector) -> ThemeResult { // Implementation note: This function is mostly pure (i.e. it has no side effects) for the sake of testing. // All the side effects (e.g. querying the terminal for its colors) are performed in the detector. @@ -185,14 +160,18 @@ fn theme_impl(options: ThemeOptions, detector: &dyn ColorSchemeDetector) -> Them theme, color_scheme: None, }, - ThemePreference::Auto(pref) => { - let color_scheme = color_scheme_impl(pref, detector); - let theme = choose_theme(options, color_scheme).unwrap_or(ThemeName::Default); - ThemeResult { - theme, - color_scheme: Some(color_scheme), - } - } + ThemePreference::Dark => choose_theme_opt(Some(ColorScheme::Dark), options), + ThemePreference::Light => choose_theme_opt(Some(ColorScheme::Light), options), + ThemePreference::Auto(when) => choose_theme_opt(detect(when, detector), options), + } +} + +fn choose_theme_opt(color_scheme: Option, options: ThemeOptions) -> ThemeResult { + ThemeResult { + color_scheme, + theme: color_scheme + .and_then(|c| choose_theme(options, c)) + .unwrap_or(ThemeName::Default), } } @@ -207,6 +186,7 @@ fn detect(when: DetectColorScheme, detector: &dyn ColorSchemeDetector) -> Option let should_detect = match when { DetectColorScheme::Auto => detector.should_detect(), DetectColorScheme::Always => true, + DetectColorScheme::System => return color_scheme_from_system(), }; should_detect.then(|| detector.detect()).flatten() } @@ -285,7 +265,6 @@ impl ColorSchemeDetector for Option { #[cfg(test)] mod tests { use super::ColorScheme::*; - use super::ColorSchemePreference as Pref; use super::*; use std::cell::Cell; use std::iter; @@ -295,10 +274,10 @@ mod tests { #[test] fn not_called_for_dark_or_light() { - for pref in [Pref::Dark, Pref::Light] { + for theme in [ThemePreference::Dark, ThemePreference::Light] { let detector = DetectorStub::should_detect(Some(Dark)); let options = ThemeOptions { - theme: ThemePreference::Auto(pref), + theme, ..Default::default() }; _ = theme_impl(options, &detector); @@ -314,9 +293,7 @@ mod tests { ]; for detector in detectors { let options = ThemeOptions { - theme: ThemePreference::Auto(ColorSchemePreference::Auto( - DetectColorScheme::Always, - )), + theme: ThemePreference::Auto(DetectColorScheme::Always), ..Default::default() }; _ = theme_impl(options, &detector); @@ -379,7 +356,7 @@ mod tests { use super::*; #[test] - fn dark_if_unable_to_detect_color_scheme() { + fn default_dark_if_unable_to_detect_color_scheme() { let detector = ConstantDetector(None); assert_eq!( default_theme(ColorScheme::Dark), @@ -390,7 +367,7 @@ mod tests { // For backwards compatibility, if the default theme is requested // explicitly through BAT_THEME, we always pick the default dark theme. #[test] - fn dark_if_requested_explicitly_through_theme() { + fn default_dark_if_requested_explicitly_through_theme() { for color_scheme in optional(color_schemes()) { let options = ThemeOptions { theme: ThemePreference::Fixed(ThemeName::Default), @@ -428,17 +405,29 @@ mod tests { mod choosing { use super::*; + #[test] + fn chooses_default_theme_if_unknown() { + let options = ThemeOptions { + theme_dark: Some(ThemeName::Named("Dark".to_string())), + theme_light: Some(ThemeName::Named("Light".to_string())), + ..Default::default() + }; + let detector = ConstantDetector(None); + assert_eq!( + default_theme(ColorScheme::default()), + theme_impl(options, &detector).to_string() + ); + } + #[test] fn chooses_dark_theme_if_dark_or_unknown() { - for color_scheme in [Some(Dark), None] { - let options = ThemeOptions { - theme_dark: Some(ThemeName::Named("Dark".to_string())), - theme_light: Some(ThemeName::Named("Light".to_string())), - ..Default::default() - }; - let detector = ConstantDetector(color_scheme); - assert_eq!("Dark", theme_impl(options, &detector).to_string()); - } + let options = ThemeOptions { + theme_dark: Some(ThemeName::Named("Dark".to_string())), + theme_light: Some(ThemeName::Named("Light".to_string())), + ..Default::default() + }; + let detector = ConstantDetector(Some(ColorScheme::Dark)); + assert_eq!("Dark", theme_impl(options, &detector).to_string()); } #[test] From e075fee5bf2fe6b40e16bb4ac02f88bcfe1fe782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Sat, 7 Sep 2024 20:24:57 +0200 Subject: [PATCH 55/91] Add infallible constructor --- src/theme.rs | 60 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/src/theme.rs b/src/theme.rs index 7b41ff4a..41ce84e1 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -48,9 +48,9 @@ pub struct ThemeOptions { /// /// The easiest way to construct this is from a string: /// ``` -/// # use bat::theme::ThemePreference; -/// # use std::str::FromStr as _; -/// let preference = ThemePreference::from_str("auto:system").unwrap(); +/// # use bat::theme::{ThemePreference, DetectColorScheme}; +/// let preference = ThemePreference::new("auto:system"); +/// assert_eq!(ThemePreference::Auto(DetectColorScheme::System), preference); /// ``` #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ThemePreference { @@ -71,19 +71,26 @@ impl Default for ThemePreference { } } +impl ThemePreference { + /// Creates a theme preference from a string. + pub fn new(s: &str) -> Self { + use ThemePreference::*; + match s { + "auto" => Auto(Default::default()), + "auto:always" => Auto(DetectColorScheme::Always), + "auto:system" => Auto(DetectColorScheme::System), + "dark" => Dark, + "light" => Light, + _ => Fixed(ThemeName::new(s)), + } + } +} + impl FromStr for ThemePreference { type Err = Infallible; fn from_str(s: &str) -> Result { - use ThemePreference::*; - match s { - "auto" => Ok(Auto(Default::default())), - "auto:always" => Ok(Auto(DetectColorScheme::Always)), - "auto:system" => Ok(Auto(DetectColorScheme::System)), - "dark" => Ok(Dark), - "light" => Ok(Light), - _ => ThemeName::from_str(s).map(Fixed), - } + Ok(ThemePreference::new(s)) } } @@ -91,9 +98,8 @@ impl FromStr for ThemePreference { /// /// ``` /// # use bat::theme::ThemeName; -/// # use std::str::FromStr as _; -/// assert_eq!(ThemeName::Default, ThemeName::from_str("default").unwrap()); -/// assert_eq!(ThemeName::Named("example".to_string()), ThemeName::from_str("example").unwrap()); +/// assert_eq!(ThemeName::Default, ThemeName::new("default")); +/// assert_eq!(ThemeName::Named("example".to_string()), ThemeName::new("example")); /// ``` #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ThemeName { @@ -101,14 +107,30 @@ pub enum ThemeName { Default, } +impl ThemeName { + /// Creates a theme name from a string. + pub fn new(s: &str) -> Self { + if s == "default" { + ThemeName::Default + } else { + ThemeName::Named(s.to_owned()) + } + } +} + impl FromStr for ThemeName { type Err = Infallible; fn from_str(s: &str) -> Result { - if s == "default" { - Ok(ThemeName::Default) - } else { - Ok(ThemeName::Named(s.to_owned())) + Ok(ThemeName::new(s)) + } +} + +impl fmt::Display for ThemeName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ThemeName::Named(t) => f.write_str(t), + ThemeName::Default => f.write_str("default"), } } } From 60e402733241f2154688722fbe75225fc4c06c6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Sat, 7 Sep 2024 21:34:05 +0200 Subject: [PATCH 56/91] Expose theme env vars --- src/bin/bat/config.rs | 6 +++--- src/theme.rs | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/bin/bat/config.rs b/src/bin/bat/config.rs index f1ec3d53..a0ee7ba3 100644 --- a/src/bin/bat/config.rs +++ b/src/bin/bat/config.rs @@ -140,9 +140,9 @@ fn get_args_from_str(content: &str) -> Result, shell_words::ParseE pub fn get_args_from_env_vars() -> Vec { [ ("--tabs", "BAT_TABS"), - ("--theme", "BAT_THEME"), - ("--theme-dark", "BAT_THEME_DARK"), - ("--theme-light", "BAT_THEME_LIGHT"), + ("--theme", bat::theme::env::BAT_THEME), + ("--theme-dark", bat::theme::env::BAT_THEME_DARK), + ("--theme-light", bat::theme::env::BAT_THEME_LIGHT), ("--pager", "BAT_PAGER"), ("--paging", "BAT_PAGING"), ("--style", "BAT_STYLE"), diff --git a/src/theme.rs b/src/theme.rs index 41ce84e1..0276e5e0 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -5,6 +5,16 @@ use std::fmt; use std::io::IsTerminal as _; use std::str::FromStr; +/// Environment variable names. +pub mod env { + /// See [`crate::theme::ThemeOptions::theme`]. + pub const BAT_THEME: &str = "BAT_THEME"; + /// See [`crate::theme::ThemeOptions::theme_dark`]. + pub const BAT_THEME_DARK: &str = "BAT_THEME"; + /// See [`crate::theme::ThemeOptions::theme_light`]. + pub const BAT_THEME_LIGHT: &str = "BAT_THEME"; +} + /// Chooses an appropriate theme or falls back to a default theme /// based on the user-provided options and the color scheme of the terminal. /// From 10e823c4b760870eac4569e42d07704cffd3e79e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Sat, 7 Sep 2024 21:34:44 +0200 Subject: [PATCH 57/91] Rename internal function --- src/theme.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/theme.rs b/src/theme.rs index 0276e5e0..71c09e98 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -36,7 +36,7 @@ pub const fn default_theme(color_scheme: ColorScheme) -> &'static str { /// Detects the color scheme from the terminal. pub fn color_scheme(when: DetectColorScheme) -> Option { - detect(when, &TerminalColorSchemeDetector) + color_scheme_impl(when, &TerminalColorSchemeDetector) } /// Options for configuring the theme used for syntax highlighting. @@ -194,7 +194,7 @@ fn theme_impl(options: ThemeOptions, detector: &dyn ColorSchemeDetector) -> Them }, ThemePreference::Dark => choose_theme_opt(Some(ColorScheme::Dark), options), ThemePreference::Light => choose_theme_opt(Some(ColorScheme::Light), options), - ThemePreference::Auto(when) => choose_theme_opt(detect(when, detector), options), + ThemePreference::Auto(when) => choose_theme_opt(color_scheme_impl(when, detector), options), } } @@ -214,7 +214,10 @@ fn choose_theme(options: ThemeOptions, color_scheme: ColorScheme) -> Option Option { +fn color_scheme_impl( + when: DetectColorScheme, + detector: &dyn ColorSchemeDetector, +) -> Option { let should_detect = match when { DetectColorScheme::Auto => detector.should_detect(), DetectColorScheme::Always => true, From f6cbee9e270b18681c2b39ad507bb128b98a35a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Sat, 7 Sep 2024 21:35:37 +0200 Subject: [PATCH 58/91] Update docs --- src/theme.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/theme.rs b/src/theme.rs index 71c09e98..b8c4c67b 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -43,7 +43,8 @@ pub fn color_scheme(when: DetectColorScheme) -> Option { /// Used together with [`theme`]. #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct ThemeOptions { - /// Always use this theme regardless of the terminal's background color. + /// Configures how the theme is chosen. If set to a [`ThemePreference::Fixed`] value, + /// then the given theme is used regardless of the terminal's background color. /// This corresponds with the `BAT_THEME` environment variable and the `--theme` option. pub theme: ThemePreference, /// The theme to use in case the terminal uses a dark background with light text. From 0ebb9cbfe2651127d22cea12ca359db5e26c8595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Sat, 7 Sep 2024 21:57:27 +0200 Subject: [PATCH 59/91] Add Display impl --- src/theme.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/theme.rs b/src/theme.rs index b8c4c67b..5fed4faa 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -105,6 +105,20 @@ impl FromStr for ThemePreference { } } +impl fmt::Display for ThemePreference { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use ThemePreference::*; + match self { + Auto(DetectColorScheme::Auto) => f.write_str("auto"), + Auto(DetectColorScheme::Always) => f.write_str("auto:always"), + Auto(DetectColorScheme::System) => f.write_str("auto:system"), + Fixed(theme) => theme.fmt(f), + Dark => f.write_str("dark"), + Light => f.write_str("light"), + } + } +} + /// The name of a theme or the default theme. /// /// ``` @@ -478,6 +492,26 @@ mod tests { } } + mod theme_preference { + use super::*; + + #[test] + fn values_roundtrip_via_display() { + let prefs = [ + ThemePreference::Auto(DetectColorScheme::Auto), + ThemePreference::Auto(DetectColorScheme::Always), + ThemePreference::Auto(DetectColorScheme::System), + ThemePreference::Fixed(ThemeName::Default), + ThemePreference::Fixed(ThemeName::new("foo")), + ThemePreference::Dark, + ThemePreference::Light, + ]; + for pref in prefs { + assert_eq!(pref, ThemePreference::new(&pref.to_string())); + } + } + } + struct DetectorStub { should_detect: bool, color_scheme: Option, From 02ae6ef348d19dd5c117c0d53733c4df076ec87b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Sat, 7 Sep 2024 22:59:27 +0200 Subject: [PATCH 60/91] Remove redundant guard --- src/theme.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/theme.rs b/src/theme.rs index 5fed4faa..dcc6ee42 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -292,7 +292,7 @@ fn color_scheme_from_system() -> Option { match plist::Value::from_file(preferences_file).map(|file| file.into_dictionary()) { Ok(Some(preferences)) => match preferences.get(STYLE_KEY).and_then(|val| val.as_string()) { - Some(value) if value == "Dark" => Some(ColorScheme::Dark), + Some("Dark") => Some(ColorScheme::Dark), // If the key does not exist, then light theme is currently in use. Some(_) | None => Some(ColorScheme::Light), }, From b7471847889ee2f0faab7e9f9d12df70d351edd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Sun, 8 Sep 2024 17:10:46 +0200 Subject: [PATCH 61/91] Accept `impl Into` to avoid cloning strings --- src/theme.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/theme.rs b/src/theme.rs index dcc6ee42..9fbef238 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -84,9 +84,10 @@ impl Default for ThemePreference { impl ThemePreference { /// Creates a theme preference from a string. - pub fn new(s: &str) -> Self { + pub fn new(s: impl Into) -> Self { use ThemePreference::*; - match s { + let s = s.into(); + match s.as_str() { "auto" => Auto(Default::default()), "auto:always" => Auto(DetectColorScheme::Always), "auto:system" => Auto(DetectColorScheme::System), @@ -134,11 +135,12 @@ pub enum ThemeName { impl ThemeName { /// Creates a theme name from a string. - pub fn new(s: &str) -> Self { + pub fn new(s: impl Into) -> Self { + let s = s.into(); if s == "default" { ThemeName::Default } else { - ThemeName::Named(s.to_owned()) + ThemeName::Named(s) } } } From ed7789d9b75cf55a6b598b2157bf634cfccb8e1c Mon Sep 17 00:00:00 2001 From: Mikael Vaaltola Date: Wed, 18 Sep 2024 12:51:43 +0300 Subject: [PATCH 62/91] Associate GeoJSON `.geojson` files with `json` syntax (fixes #3083) --- CHANGELOG.md | 1 + src/syntax_mapping/builtins/common/50-json.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea47b58f..243f7449 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ - Associate JSON with Comments `.jsonc` with `json` syntax, see #2795 (@mxaddict) - Associate JSON-LD `.jsonld` files with `json` syntax, see #3037 (@vorburger) - Associate `.textproto` files with `ProtoBuf` syntax, see #3038 (@vorburger) +- Associate GeoJSON `.geojson` files with `json` syntax, see #3084 (@mvaaltola) - Associate `.aws/{config,credentials}`, see #2795 (@mxaddict) - Associate Wireguard config `/etc/wireguard/*.conf`, see #2874 (@cyqsimon) - Add support for [CFML](https://www.adobe.com/products/coldfusion-family.html), see #3031 (@brenton-at-pieces) diff --git a/src/syntax_mapping/builtins/common/50-json.toml b/src/syntax_mapping/builtins/common/50-json.toml index 7d33b6fe..3198c4f3 100644 --- a/src/syntax_mapping/builtins/common/50-json.toml +++ b/src/syntax_mapping/builtins/common/50-json.toml @@ -1,3 +1,3 @@ # JSON Lines is a simple variation of JSON #2535 [mappings] -"JSON" = ["*.jsonl", "*.jsonc", "*.jsonld"] +"JSON" = ["*.jsonl", "*.jsonc", "*.jsonld", "*.geojson"] From a0226f5670506c8e691cf05f3a752d8a5b2eb0a5 Mon Sep 17 00:00:00 2001 From: flytam Date: Thu, 8 Aug 2024 20:34:48 +0800 Subject: [PATCH 63/91] Update Chinese version translation of README.md --- doc/README-zh.md | 50 ++++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/doc/README-zh.md b/doc/README-zh.md index 007afb3e..9f2d202e 100644 --- a/doc/README-zh.md +++ b/doc/README-zh.md @@ -616,63 +616,59 @@ iconv -f ISO-8859-1 -t UTF-8 my-file.php | bat 注意: 当`bat`无法识别语言时你可能会需要`-l`/`--language`参数。 -## Development +## 开发 ```bash -# Recursive clone to retrieve all submodules +# 递归 clone 以获取所有子模块 git clone --recursive https://github.com/sharkdp/bat -# Build (debug version) +# 构建(调试版本) cd bat cargo build --bins -# Run unit tests and integration tests +# 运行单元测试和集成测试 cargo test -# Install (release version) +# 安装(发布版本) cargo install --path . --locked -# Build a bat binary with modified syntaxes and themes +# 使用修改后的语法和主题构建一个 bat 二进制文件 bash assets/create.sh cargo install --path . --locked --force ``` -If you want to build an application that uses `bat`s pretty-printing -features as a library, check out the [the API documentation](https://docs.rs/bat/). -Note that you have to use either `regex-onig` or `regex-fancy` as a feature -when you depend on `bat` as a library. +如果你想构建一个使用 `bat` 美化打印功能的应用程序,请查看 [API 文档](https://docs.rs/bat/)。请注意,当你依赖 `bat` 作为库时,必须使用 `regex-onig` 或 `regex-fancy` 作为特性。 -## Contributing +## 贡献指南 -Take a look at the [`CONTRIBUTING.md`](CONTRIBUTING.md) guide. +请查看 [`CONTRIBUTING.md`](CONTRIBUTING.md) 指南。 -## Maintainers +## 维护者 - [sharkdp](https://github.com/sharkdp) - [eth-p](https://github.com/eth-p) - [keith-hall](https://github.com/keith-hall) - [Enselic](https://github.com/Enselic) -## Security vulnerabilities +## 安全漏洞 -Please contact [David Peter](https://david-peter.de/) via email if you want to report a vulnerability in `bat`. +如果你想报告 `bat` 中的漏洞,请通过邮件联系 [David Peter](https://david-peter.de/)。 -## Project goals and alternatives +## 项目目标和替代方案 -`bat` tries to achieve the following goals: +`bat` 试图实现以下目标: -- Provide beautiful, advanced syntax highlighting -- Integrate with Git to show file modifications -- Be a drop-in replacement for (POSIX) `cat` -- Offer a user-friendly command-line interface +- 提供美观的高级语法高亮 +- 与 Git 集成以显示文件修改 +- 成为 (POSIX) `cat` 的替代品 +- 提供用户友好的命令行界面 -There are a lot of alternatives, if you are looking for similar programs. See -[this document](doc/alternatives.md) for a comparison. +如果你在寻找类似的程序,有很多替代方案。请参阅[本文档](doc/alternatives.md)进行比较。 -## License +## 许可证 -Copyright (c) 2018-2021 [bat-developers](https://github.com/sharkdp/bat). +版权所有 (c) 2018-2021 [bat-developers](https://github.com/sharkdp/bat)。 -`bat` is made available under the terms of either the MIT License or the Apache License 2.0, at your option. +`bat` 可根据 MIT 许可证或 Apache 许可证 2.0 的条款使用,任选其一。 -See the [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) files for license details. +有关许可证的详细信息,请参阅 [LICENSE-APACHE](LICENSE-APACHE) 和 [LICENSE-MIT](LICENSE-MIT) 文件。 From bc69ffb0f0732c64af5d7751e074e6bc17342c69 Mon Sep 17 00:00:00 2001 From: einfachIrgendwer0815 <85333734+einfachIrgendwer0815@users.noreply.github.com> Date: Thu, 26 Sep 2024 21:06:07 +0200 Subject: [PATCH 64/91] Map `*.mkd` files to `Markdown` syntax (#3061) --- CHANGELOG.md | 1 + src/syntax_mapping/builtins/common/50-markdown.toml | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 src/syntax_mapping/builtins/common/50-markdown.toml diff --git a/CHANGELOG.md b/CHANGELOG.md index 243f7449..d2ec2da9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ - Associate `.aws/{config,credentials}`, see #2795 (@mxaddict) - Associate Wireguard config `/etc/wireguard/*.conf`, see #2874 (@cyqsimon) - Add support for [CFML](https://www.adobe.com/products/coldfusion-family.html), see #3031 (@brenton-at-pieces) +- Map `*.mkd` files to `Markdown` syntax, see issue #3060 and PR #3061 (@einfachIrgendwer0815) ## Themes diff --git a/src/syntax_mapping/builtins/common/50-markdown.toml b/src/syntax_mapping/builtins/common/50-markdown.toml new file mode 100644 index 00000000..aa0531d2 --- /dev/null +++ b/src/syntax_mapping/builtins/common/50-markdown.toml @@ -0,0 +1,2 @@ +[mappings] +"Markdown" = ["*.mkd"] From 9d635627dc27cddf3414b75b6e11abc3024a3950 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 02:40:45 +0000 Subject: [PATCH 65/91] Bump globset from 0.4.14 to 0.4.15 (#3090) Bumps [globset](https://github.com/BurntSushi/ripgrep) from 0.4.14 to 0.4.15. - [Release notes](https://github.com/BurntSushi/ripgrep/releases) - [Changelog](https://github.com/BurntSushi/ripgrep/blob/master/CHANGELOG.md) - [Commits](https://github.com/BurntSushi/ripgrep/compare/globset-0.4.14...ignore-0.4.15) --- updated-dependencies: - dependency-name: globset dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4fcb870b..7b992e72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -583,9 +583,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" dependencies = [ "aho-corasick", "bstr", From 16a79d38c2ca82e4412d453ddfc44cdfe6367ebe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 03:02:02 +0000 Subject: [PATCH 66/91] Bump once_cell from 1.19.0 to 1.20.1 (#3091) Bumps [once_cell](https://github.com/matklad/once_cell) from 1.19.0 to 1.20.1. - [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md) - [Commits](https://github.com/matklad/once_cell/compare/v1.19.0...v1.20.1) --- updated-dependencies: - dependency-name: once_cell dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 13 +++++++++++-- Cargo.toml | 4 ++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7b992e72..ff10f09c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -802,9 +802,12 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] [[package]] name = "onig" @@ -894,6 +897,12 @@ dependencies = [ "time", ] +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + [[package]] name = "powerfmt" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 0740b291..cbb84a6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ ansi_colours = "^1.2" bincode = "1.0" console = "0.15.8" flate2 = "1.0" -once_cell = "1.19" +once_cell = "1.20" thiserror = "1.0" wild = { version = "2.2", optional = true } content_inspector = "0.2.4" @@ -104,7 +104,7 @@ nix = { version = "0.26.4", default-features = false, features = ["term"] } anyhow = "1.0.86" indexmap = { version = "2.3.0", features = ["serde"] } itertools = "0.13.0" -once_cell = "1.18" +once_cell = "1.20" regex = "1.10.2" serde = "1.0" serde_derive = "1.0" From c59dad0cae45d7aa84ad87583d6b6904b30839b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 03:19:34 +0000 Subject: [PATCH 67/91] Bump git2 from 0.18.3 to 0.19.0 (#3092) Bumps [git2](https://github.com/rust-lang/git2-rs) from 0.18.3 to 0.19.0. - [Changelog](https://github.com/rust-lang/git2-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/git2-rs/compare/git2-0.18.3...git2-0.19.0) --- updated-dependencies: - dependency-name: git2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ff10f09c..e2f6ef7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -564,9 +564,9 @@ dependencies = [ [[package]] name = "git2" -version = "0.18.3" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "232e6a7bfe35766bf715e55a88b39a700596c0ccfd88cd3680b4cdb40d66ef70" +checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724" dependencies = [ "bitflags 2.4.0", "libc", @@ -694,9 +694,9 @@ checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libgit2-sys" -version = "0.16.2+1.7.2" +version = "0.17.0+1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4126d8b4ee5c9d9ea891dd875cfdc1e9d0950437179104b183d7d8a74d24e8" +checksum = "10472326a8a6477c3c20a64547b0059e4b0d086869eee31e6d7da728a8eb7224" dependencies = [ "cc", "libc", diff --git a/Cargo.toml b/Cargo.toml index cbb84a6c..f5ef6af9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,7 +70,7 @@ os_str_bytes = { version = "~7.0", optional = true } run_script = { version = "^0.10.1", optional = true} [dependencies.git2] -version = "0.18" +version = "0.19" optional = true default-features = false From 61c9f312c9d10103b33b7d8069401304ca938f06 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 03:36:57 +0000 Subject: [PATCH 68/91] Bump flate2 from 1.0.30 to 1.0.34 (#3093) Bumps [flate2](https://github.com/rust-lang/flate2-rs) from 1.0.30 to 1.0.34. - [Release notes](https://github.com/rust-lang/flate2-rs/releases) - [Changelog](https://github.com/rust-lang/flate2-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/rust-lang/flate2-rs/compare/1.0.30...1.0.34) --- updated-dependencies: - dependency-name: flate2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e2f6ef7c..42db6b5f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,10 +3,10 @@ version = 3 [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aho-corasick" @@ -489,9 +489,9 @@ checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", "miniz_oxide", @@ -752,11 +752,11 @@ checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] From 3e5f4266ce4c7245650f1fc1cdb465b7c0b80700 Mon Sep 17 00:00:00 2001 From: Matt Helsley Date: Thu, 3 Oct 2024 16:08:07 -0700 Subject: [PATCH 69/91] Fix lessopen feature RawOsString API changed in os_str_bytes 7.0.0 and bat no longer builds with the lessopen feature because: - ::from_string() is deprecated and says to use new(). The documentation says that new(), like from_string() no longer needs to copy the string. - ::assert_from_raw_vec() is no longer merely deprecated and now requires selecting the "conversions" feature of os_str_bytes. Replaces PR#2938 --- Cargo.toml | 2 +- src/lessopen.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f5ef6af9..d5b61566 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ minimal-application = [ ] git = ["git2"] # Support indicating git modifications paging = ["shell-words", "grep-cli"] # Support applying a pager on the output -lessopen = ["run_script", "os_str_bytes"] # Support $LESSOPEN preprocessor +lessopen = ["run_script", "os_str_bytes/conversions"] # Support $LESSOPEN preprocessor build-assets = ["syntect/yaml-load", "syntect/plist-load", "regex", "walkdir"] # You need to use one of these if you depend on bat as a library: diff --git a/src/lessopen.rs b/src/lessopen.rs index c8f5225d..4d7e0ece 100644 --- a/src/lessopen.rs +++ b/src/lessopen.rs @@ -112,7 +112,7 @@ impl LessOpenPreprocessor { } ( - RawOsString::from_string(lessopen_stdout), + RawOsString::new(lessopen_stdout), path_str.to_string(), OpenedInputKind::OrdinaryFile(path.to_path_buf()), ) From 9a816c9c68728b077f342147482fc3242bf50238 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaras=C5=82a=C5=AD=20Viktor=C4=8Dyk?= Date: Sun, 13 Oct 2024 11:46:53 +0200 Subject: [PATCH 70/91] Add syntax mapping for CITATION.cff --- CHANGELOG.md | 1 + src/syntax_mapping/builtins/common/50-citation.toml | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 src/syntax_mapping/builtins/common/50-citation.toml diff --git a/CHANGELOG.md b/CHANGELOG.md index d2ec2da9..20aea9ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,7 @@ - Associate Wireguard config `/etc/wireguard/*.conf`, see #2874 (@cyqsimon) - Add support for [CFML](https://www.adobe.com/products/coldfusion-family.html), see #3031 (@brenton-at-pieces) - Map `*.mkd` files to `Markdown` syntax, see issue #3060 and PR #3061 (@einfachIrgendwer0815) +- Add syntax mapping for CITATION.cff, see #3103 (@Ugzuzg) ## Themes diff --git a/src/syntax_mapping/builtins/common/50-citation.toml b/src/syntax_mapping/builtins/common/50-citation.toml new file mode 100644 index 00000000..aa06b5b9 --- /dev/null +++ b/src/syntax_mapping/builtins/common/50-citation.toml @@ -0,0 +1,2 @@ +[mappings] +"YAML" = ["CITATION.cff"] From fd6c7637e49d6ff1f7f475943685c1b3aab5c76a Mon Sep 17 00:00:00 2001 From: einfachIrgendwer0815 <85333734+einfachIrgendwer0815@users.noreply.github.com> Date: Sat, 21 Oct 2023 21:58:57 +0200 Subject: [PATCH 71/91] Partially revert "Make -pp override --paging and vice versa when passed as a later argument. (#2660)" This partially reverts commit e2bf85e749d87459e3fced697af5cd9cc96eeb8c. --- src/bin/bat/clap_app.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bin/bat/clap_app.rs b/src/bin/bat/clap_app.rs index 33dde980..c0669c0f 100644 --- a/src/bin/bat/clap_app.rs +++ b/src/bin/bat/clap_app.rs @@ -81,7 +81,6 @@ pub fn build_app(interactive_output: bool) -> Command { Arg::new("plain") .overrides_with("plain") .overrides_with("number") - .overrides_with("paging") .short('p') .long("plain") .action(ArgAction::Count) @@ -306,7 +305,6 @@ pub fn build_app(interactive_output: bool) -> Command { .long("paging") .overrides_with("paging") .overrides_with("no-paging") - .overrides_with("plain") .value_name("when") .value_parser(["auto", "never", "always"]) .default_value("auto") From e667415deff1a8150db6cc1fc5e8db701827261d Mon Sep 17 00:00:00 2001 From: einfachIrgendwer0815 <85333734+einfachIrgendwer0815@users.noreply.github.com> Date: Sat, 21 Oct 2023 22:16:59 +0200 Subject: [PATCH 72/91] Add tests against issue #2731 --- tests/integration_tests.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index c083a941..2a192cdb 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1020,6 +1020,31 @@ fn enable_pager_if_pp_flag_comes_before_paging() { .stdout(predicate::eq("pager-output\n").normalize()); } +#[test] +fn paging_does_not_override_simple_plain() { + bat() + .env("PAGER", "echo pager-output") + .arg("--decorations=always") + .arg("--plain") + .arg("--paging=never") + .arg("test.txt") + .assert() + .success() + .stdout(predicate::eq("hello world\n")); +} + +#[test] +fn simple_plain_does_not_override_paging() { + bat() + .env("PAGER", "echo pager-output") + .arg("--paging=always") + .arg("--plain") + .arg("test.txt") + .assert() + .success() + .stdout(predicate::eq("pager-output\n")); +} + #[test] fn pager_failed_to_parse() { bat() From c9fd0f3cf0e042c1c070e01e06bc8249b35a644a Mon Sep 17 00:00:00 2001 From: einfachIrgendwer0815 <85333734+einfachIrgendwer0815@users.noreply.github.com> Date: Fri, 18 Oct 2024 11:43:33 +0200 Subject: [PATCH 73/91] Add partial override of -pp and --paging --- src/bin/bat/app.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index d6628668..9a78c949 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -97,12 +97,30 @@ impl App { pub fn config(&self, inputs: &[Input]) -> Result { let style_components = self.style_components()?; + let extra_plain = self.matches.get_count("plain") > 1; + let plain_last_index = self + .matches + .indices_of("plain") + .and_then(Iterator::max) + .unwrap_or_default(); + let paging_last_index = self + .matches + .indices_of("paging") + .and_then(Iterator::max) + .unwrap_or_default(); + let paging_mode = match self.matches.get_one::("paging").map(|s| s.as_str()) { - Some("always") => PagingMode::Always, + Some("always") => { + // Disable paging if the second -p (or -pp) is specified after --paging=always + if extra_plain && plain_last_index > paging_last_index { + PagingMode::Never + } else { + PagingMode::Always + } + } Some("never") => PagingMode::Never, Some("auto") | None => { // If we have -pp as an option when in auto mode, the pager should be disabled. - let extra_plain = self.matches.get_count("plain") > 1; if extra_plain || self.matches.get_flag("no-paging") { PagingMode::Never } else if inputs.iter().any(Input::is_stdin) { From ac082ab64b3536d463ec922c838474f8979f1726 Mon Sep 17 00:00:00 2001 From: einfachIrgendwer0815 <85333734+einfachIrgendwer0815@users.noreply.github.com> Date: Fri, 18 Oct 2024 12:44:53 +0200 Subject: [PATCH 74/91] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2ec2da9..a0e46c13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Fix handling of inputs with combined ANSI color and attribute sequences, see #2185 and #2856 (@eth-p) - Fix panel width when line 10000 wraps, see #2854 (@eth-p) - Fix compile issue of `time` dependency caused by standard library regression #3045 (@cyqsimon) +- Fix override behavior of --plain and --paging, see issue #2731 and PR #3108 (@einfachIrgendwer0815) ## Other From b93a41a9a3c1af60bdf7f2dcd56d0d5d14aab429 Mon Sep 17 00:00:00 2001 From: kojix2 <2xijok@gmail.com> Date: Sun, 11 Aug 2024 00:26:33 +0900 Subject: [PATCH 75/91] Add print_with_writer to PrettyPrint --- src/pretty_printer.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/pretty_printer.rs b/src/pretty_printer.rs index eb123ea3..aa06a78b 100644 --- a/src/pretty_printer.rs +++ b/src/pretty_printer.rs @@ -279,6 +279,11 @@ impl<'a> PrettyPrinter<'a> { /// If you want to call 'print' multiple times, you have to call the appropriate /// input_* methods again. pub fn print(&mut self) -> Result { + self.print_with_writer(None::<&mut dyn std::fmt::Write>) + } + + /// Pretty-print all specified inputs to a specified writer. + pub fn print_with_writer(&mut self, writer: Option) -> Result { let highlight_lines = std::mem::take(&mut self.highlighted_lines); self.config.highlighted_lines = HighlightedLineRanges(LineRanges::from(highlight_lines)); self.config.term_width = self @@ -315,7 +320,13 @@ impl<'a> PrettyPrinter<'a> { // Run the controller let controller = Controller::new(&self.config, &self.assets); - controller.run(inputs.into_iter().map(|i| i.into()).collect(), None) + + // If writer is provided, pass it to the controller, otherwise pass None + if let Some(mut w) = writer { + controller.run(inputs.into_iter().map(|i| i.into()).collect(), Some(&mut w)) + } else { + controller.run(inputs.into_iter().map(|i| i.into()).collect(), None) + } } } From c533a4aa146f3d623d7f8f113b13e08abae849d9 Mon Sep 17 00:00:00 2001 From: kojix2 <2xijok@gmail.com> Date: Wed, 23 Oct 2024 21:42:02 +0900 Subject: [PATCH 76/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c404042f..67c9ee19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,6 +72,7 @@ - [BREAKING] `SyntaxMapping::mappings` is replaced by `SyntaxMapping::{builtin,custom,all}_mappings` - Make `Controller::run_with_error_handler`'s error handler `FnMut`, see #2831 (@rhysd) - Improve compile time by 20%, see #2815 (@dtolnay) +- Add `PrettyPrinter::print_with_writer` for custom output destinations, see #3070 (@kojix2) # v0.24.0 From 649fb05c5859a2dca61bed165ba9965c0ac5a667 Mon Sep 17 00:00:00 2001 From: Fabio Valentini Date: Thu, 24 Oct 2024 01:17:17 +0200 Subject: [PATCH 77/91] Bump nix from 0.26.4 to 0.29 --- CHANGELOG.md | 1 + Cargo.lock | 17 ++++++++++++----- Cargo.toml | 2 +- tests/integration_tests.rs | 5 ++--- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c404042f..55879dce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ - Use bat's ANSI iterator during tab expansion, see #2998 (@eth-p) - Support 'statically linked binary' for aarch64 in 'Release' page, see #2992 (@tzq0301) - Update options in shell completions and the man page of `bat`, see #2995 (@akinomyoga) +- Update nix dev-dependency to v0.29.0, see #3112 (@decathorpe) ## Syntaxes diff --git a/Cargo.lock b/Cargo.lock index 42db6b5f..0e3b3418 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -243,6 +243,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "clap" version = "4.4.12" @@ -688,9 +694,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.149" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libgit2-sys" @@ -761,12 +767,13 @@ dependencies = [ [[package]] name = "nix" -version = "0.26.4" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "cfg-if", + "cfg_aliases", "libc", ] diff --git a/Cargo.toml b/Cargo.toml index f5ef6af9..b8fcbcbe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -98,7 +98,7 @@ tempfile = "3.8.1" serde = { version = "1.0", features = ["derive"] } [target.'cfg(unix)'.dev-dependencies] -nix = { version = "0.26.4", default-features = false, features = ["term"] } +nix = { version = "0.29", default-features = false, features = ["term"] } [build-dependencies] anyhow = "1.0.86" diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index c083a941..13b35718 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -9,7 +9,6 @@ use tempfile::tempdir; mod unix { pub use std::fs::File; pub use std::io::{self, Write}; - pub use std::os::unix::io::FromRawFd; pub use std::path::PathBuf; pub use std::process::Stdio; pub use std::thread; @@ -416,8 +415,8 @@ fn no_args_doesnt_break() { // not exit, because in this case it is safe to read and write to the same fd, which is why // this test exists. let OpenptyResult { master, slave } = openpty(None, None).expect("Couldn't open pty."); - let mut master = unsafe { File::from_raw_fd(master) }; - let stdin_file = unsafe { File::from_raw_fd(slave) }; + let mut master = File::from(master); + let stdin_file = File::from(slave); let stdout_file = stdin_file.try_clone().unwrap(); let stdin = Stdio::from(stdin_file); let stdout = Stdio::from(stdout_file); From 50428443423a22685ec9f3ba8e4586a4ab419315 Mon Sep 17 00:00:00 2001 From: Pratik Munot Date: Tue, 29 Oct 2024 21:34:47 -0700 Subject: [PATCH 78/91] Adding pipe delimeter support for csv files --- assets/syntaxes/02_Extra/CSV.sublime-syntax | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/assets/syntaxes/02_Extra/CSV.sublime-syntax b/assets/syntaxes/02_Extra/CSV.sublime-syntax index cca7cd2c..0ad17834 100644 --- a/assets/syntaxes/02_Extra/CSV.sublime-syntax +++ b/assets/syntaxes/02_Extra/CSV.sublime-syntax @@ -7,14 +7,14 @@ file_extensions: - tsv scope: text.csv variables: - field_separator: (?:[,;\t]) + field_separator: (?:[,;|\t]) record_separator: (?:$\n?) contexts: prototype: - match: (?={{record_separator}}) pop: true fields: - - match: '' + - match: "" push: - field_or_record_separator - field4 @@ -26,15 +26,15 @@ contexts: - field1 main: - meta_include_prototype: false - - match: '^' + - match: "^" set: fields field_or_record_separator: - meta_include_prototype: false - - match: '{{record_separator}}' + - match: "{{record_separator}}" scope: punctuation.terminator.record.csv pop: true - - match: '{{field_separator}}' + - match: "{{field_separator}}" scope: punctuation.separator.sequence.csv pop: true @@ -56,23 +56,22 @@ contexts: pop: true field1: - - match: '' + - match: "" set: - meta_content_scope: meta.field-1.csv support.type - include: field_contents field2: - - match: '' + - match: "" set: - meta_content_scope: meta.field-2.csv support.function - include: field_contents field3: - - match: '' + - match: "" set: - meta_content_scope: meta.field-3.csv constant.numeric - include: field_contents field4: - - match: '' + - match: "" set: - meta_content_scope: meta.field-4.csv keyword.operator - include: field_contents - From 1942d40863472ea78603cf21ffa69ef49b335ad2 Mon Sep 17 00:00:00 2001 From: Pratik Munot Date: Tue, 29 Oct 2024 22:36:43 -0700 Subject: [PATCH 79/91] changelog updates --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55879dce..633af5ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ - Add support for [CFML](https://www.adobe.com/products/coldfusion-family.html), see #3031 (@brenton-at-pieces) - Map `*.mkd` files to `Markdown` syntax, see issue #3060 and PR #3061 (@einfachIrgendwer0815) - Add syntax mapping for kubernetes config files #3049 (@cyqsimon) +- Adds support for pipe delimiter for CSV #3115 (@pratik-m) ## Themes From 9090fb75e0ae7772aa09b698a10a4f5fb04a4143 Mon Sep 17 00:00:00 2001 From: Jacek Generowicz Date: Fri, 26 Apr 2024 16:19:07 +0200 Subject: [PATCH 80/91] Make .debdiff imply .diff using syntax_mapping toml Closes #2940 --- src/syntax_mapping/builtins/unix-family/50-diff.toml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/syntax_mapping/builtins/unix-family/50-diff.toml diff --git a/src/syntax_mapping/builtins/unix-family/50-diff.toml b/src/syntax_mapping/builtins/unix-family/50-diff.toml new file mode 100644 index 00000000..2998d9c5 --- /dev/null +++ b/src/syntax_mapping/builtins/unix-family/50-diff.toml @@ -0,0 +1,3 @@ +# .debdiff is the extension used for diffs in Debian packaging +[mappings] +"Diff" = ["*.debdiff"] From d0ae2e6826caf0e7701be7751989827b371b4bba Mon Sep 17 00:00:00 2001 From: Jacek Generowicz Date: Fri, 26 Apr 2024 16:30:39 +0200 Subject: [PATCH 81/91] Add CHANGELOG entry for .debdiff => diff --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e45f1bb1..5326de59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -114,6 +114,7 @@ - Update `Julia` syntax, see #2553 (@dependabot) - add `NSIS` support, see #2577 (@idleberg) - Update `ssh-config`, see #2697 (@mrmeszaros) +- Add syntax mapping `*.debdiff` => `diff`, see #2947 (@jacg) ## `bat` as a library From 0a8bfc52a6337828eb0d58a49006437d44ac5647 Mon Sep 17 00:00:00 2001 From: Jacek Generowicz Date: Wed, 15 May 2024 09:07:44 +0200 Subject: [PATCH 82/91] Move from unix-family to common --- src/syntax_mapping/builtins/{unix-family => common}/50-diff.toml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/syntax_mapping/builtins/{unix-family => common}/50-diff.toml (100%) diff --git a/src/syntax_mapping/builtins/unix-family/50-diff.toml b/src/syntax_mapping/builtins/common/50-diff.toml similarity index 100% rename from src/syntax_mapping/builtins/unix-family/50-diff.toml rename to src/syntax_mapping/builtins/common/50-diff.toml From c627526f8c18ba1a1e630770b7627c159b545962 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 03:57:52 +0000 Subject: [PATCH 83/91] Bump clircle from 0.5.0 to 0.6.0 (#3117) Bumps [clircle](https://github.com/niklasmohrin/clircle) from 0.5.0 to 0.6.0. - [Release notes](https://github.com/niklasmohrin/clircle/releases) - [Commits](https://github.com/niklasmohrin/clircle/compare/v0.5.0...v0.6.0) --- updated-dependencies: - dependency-name: clircle dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 5 ++--- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e3b3418..a9b6f7dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -279,12 +279,11 @@ checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "clircle" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0b92245ea62a7a751db4b0e4a583f8978e508077ef6de24fcc0d0dc5311a8d" +checksum = "e136d50bd652710f1d86259a8977263d46bef0ab782a8bfc3887e44338517015" dependencies = [ "cfg-if", - "libc", "serde", "serde_derive", "winapi", diff --git a/Cargo.toml b/Cargo.toml index b8fcbcbe..a6c410b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,7 @@ serde_derive = "1.0" serde_yaml = "0.9.28" semver = "1.0" path_abs = { version = "0.5", default-features = false } -clircle = "0.5" +clircle = "0.6" bugreport = { version = "0.5.0", optional = true } etcetera = { version = "0.8.0", optional = true } grep-cli = { version = "0.1.10", optional = true } From e7bef716c9ade4641f2b92e992b56837bc1d019d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 04:23:23 +0000 Subject: [PATCH 84/91] Bump encoding_rs from 0.8.34 to 0.8.35 (#3119) Bumps [encoding_rs](https://github.com/hsivonen/encoding_rs) from 0.8.34 to 0.8.35. - [Commits](https://github.com/hsivonen/encoding_rs/compare/v0.8.34...v0.8.35) --- updated-dependencies: - dependency-name: encoding_rs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a9b6f7dd..f8356339 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -421,9 +421,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] diff --git a/Cargo.toml b/Cargo.toml index a6c410b9..ccc4ced7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,7 +65,7 @@ grep-cli = { version = "0.1.10", optional = true } regex = { version = "1.10.2", optional = true } walkdir = { version = "2.5", optional = true } bytesize = { version = "1.3.0" } -encoding_rs = "0.8.34" +encoding_rs = "0.8.35" os_str_bytes = { version = "~7.0", optional = true } run_script = { version = "^0.10.1", optional = true} From 6b2c5645d251d2f3c5f58f889b8a63cf4c7ffa56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 04:46:28 +0000 Subject: [PATCH 85/91] Bump toml from 0.8.9 to 0.8.19 (#3121) Bumps [toml](https://github.com/toml-rs/toml) from 0.8.9 to 0.8.19. - [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.9...toml-v0.8.19) --- updated-dependencies: - dependency-name: toml dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f8356339..f6fedcbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1146,9 +1146,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -1392,9 +1392,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "toml" -version = "0.8.9" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6a4b9e8023eb94392d3dca65d717c53abc5dad49c07cb65bb8fcd87115fa325" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "indexmap", "serde", @@ -1405,18 +1405,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.21.1" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "serde", @@ -1746,9 +1746,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176b6138793677221d420fd2f0aeeced263f197688b36484660da767bca2fa32" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index ccc4ced7..6714e6c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -109,7 +109,7 @@ regex = "1.10.2" serde = "1.0" serde_derive = "1.0" serde_with = { version = "3.8.1", default-features = false, features = ["macros"] } -toml = { version = "0.8.9", features = ["preserve_order"] } +toml = { version = "0.8.19", features = ["preserve_order"] } walkdir = "2.5" [build-dependencies.clap] From dbaa0a6d9eea7a594b56b535574be30b077a7a12 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 05:02:22 +0000 Subject: [PATCH 86/91] Bump grep-cli from 0.1.10 to 0.1.11 (#3120) Bumps [grep-cli](https://github.com/BurntSushi/ripgrep) from 0.1.10 to 0.1.11. - [Release notes](https://github.com/BurntSushi/ripgrep/releases) - [Changelog](https://github.com/BurntSushi/ripgrep/blob/master/CHANGELOG.md) - [Commits](https://github.com/BurntSushi/ripgrep/compare/grep-cli-0.1.10...0.1.11) --- updated-dependencies: - dependency-name: grep-cli dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f6fedcbf..0f242845 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -601,9 +601,9 @@ dependencies = [ [[package]] name = "grep-cli" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea40788c059ab8b622c4d074732750bfb3bd2912e2dd58eabc11798a4d5ad725" +checksum = "47f1288f0e06f279f84926fa4c17e3fcd2a22b357927a82f2777f7be26e4cec0" dependencies = [ "bstr", "globset", diff --git a/Cargo.toml b/Cargo.toml index 6714e6c4..67327460 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ path_abs = { version = "0.5", default-features = false } clircle = "0.6" bugreport = { version = "0.5.0", optional = true } etcetera = { version = "0.8.0", optional = true } -grep-cli = { version = "0.1.10", optional = true } +grep-cli = { version = "0.1.11", optional = true } regex = { version = "1.10.2", optional = true } walkdir = { version = "2.5", optional = true } bytesize = { version = "1.3.0" } From f8f12a7db579ede8bd4598d384fde768bf055377 Mon Sep 17 00:00:00 2001 From: David Peter Date: Sun, 10 Nov 2024 20:02:58 +0100 Subject: [PATCH 87/91] Update sponsorship information --- README.md | 6 +++--- doc/sponsors/warp-logo.png | Bin 80535 -> 132621 bytes 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 016fe834..4ffd7fe9 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,11 @@ A special *thank you* goes to our biggest sponsors Warp
- Warp is a modern, Rust-based terminal with AI built in
so you and your team can build great software, faster.
+ Warp, the intelligent terminal
- Feel more productive on the command line with parameterized commands, + Run commands like a power user with AI and your dev team’s
- autosuggestions, and an IDE-like text editor. + knowledge in one fast, intuitive terminal. For MacOS or Linux.
### Syntax highlighting diff --git a/doc/sponsors/warp-logo.png b/doc/sponsors/warp-logo.png index 4795a2b97a7ece92c764e94edfcb4dc30273e5d3..f99dd38ceea805656daea5cd80c3525dbd307b71 100644 GIT binary patch literal 132621 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 k$@@B)IBe@Pl;6bAZ|msXLgl`skZAEg!r?*IS* From 15f2553a3d7fdb1d7c2a6be7d3bed175b6dd1c8d Mon Sep 17 00:00:00 2001 From: volklord <20864876+volklord@users.noreply.github.com> Date: Wed, 7 Aug 2024 15:31:56 +0200 Subject: [PATCH 88/91] bat.zsh.in: Remove duplicate word Remove the duplicate word `disable disable` --- assets/completions/bat.zsh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/completions/bat.zsh.in b/assets/completions/bat.zsh.in index 7d03abb3..e98dbbe4 100644 --- a/assets/completions/bat.zsh.in +++ b/assets/completions/bat.zsh.in @@ -26,7 +26,7 @@ _{{PROJECT_EXECUTABLE}}_main() { args=( '(-A --show-all)'{-A,--show-all}'[show non-printable characters (space, tab, newline, ..)]' --nonprintable-notation='[specify how to display non-printable characters when using --show-all]:notation:(caret unicode)' - \*{-p,--plain}'[show plain style (alias for `--style=plain`), repeat twice to disable disable automatic paging (alias for `--paging=never`)]' + \*{-p,--plain}'[show plain style (alias for `--style=plain`), repeat twice to disable automatic paging (alias for `--paging=never`)]' '(-l --language)'{-l+,--language=}'[set the language for syntax highlighting]:language:->languages' \*{-H+,--highlight-line=}'[highlight specified block of lines]:start\:end' \*--file-name='[specify the name to display for a file]:name:_files' From 2caeaef14bec10565a62451d92dd92362b483275 Mon Sep 17 00:00:00 2001 From: Adam Perkowski Date: Sun, 6 Oct 2024 18:20:43 +0200 Subject: [PATCH 89/91] =?UTF-8?q?=F0=9F=93=94=20docs:=20mention=20`BAT=5FC?= =?UTF-8?q?ONFIG=5FDIR`=20in=20the=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4ffd7fe9..14c264a8 100644 --- a/README.md +++ b/README.md @@ -693,10 +693,11 @@ on your operating system. To get the default path for your system, call bat --config-file ``` -Alternatively, you can use the `BAT_CONFIG_PATH` environment variable to point `bat` to a -non-default location of the configuration file: +Alternatively, you can use `BAT_CONFIG_PATH` or `BAT_CONFIG_DIR` environment variables to point `bat` +to a non-default location of the configuration file or the configuration directory respectively: ```bash -export BAT_CONFIG_PATH="/path/to/bat.conf" +export BAT_CONFIG_PATH="/path/to/bat/bat.conf" +export BAT_CONFIG_DIR="/path/to/bat" ``` A default configuration file can be created with the `--generate-config-file` option. From 57a8b1088306acfbe300e43a254beef3114abadd Mon Sep 17 00:00:00 2001 From: Cosmic Horror Date: Sun, 30 Jun 2024 16:03:17 -0600 Subject: [PATCH 90/91] Bump themes/Nord-submlime from `0d655b2` `bf92a9e` --- assets/themes/Nord-sublime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/themes/Nord-sublime b/assets/themes/Nord-sublime index 0d655b23..bf92a9e4 160000 --- a/assets/themes/Nord-sublime +++ b/assets/themes/Nord-sublime @@ -1 +1 @@ -Subproject commit 0d655b23d6b300e691676d9b90a68d92b267f7ec +Subproject commit bf92a9e4457dc2f97efebc59bbeac95933ec6515 From d27064cea5797f4d4a1e9299674ad56dd7846f91 Mon Sep 17 00:00:00 2001 From: binchengqu Date: Thu, 21 Nov 2024 11:55:43 +0800 Subject: [PATCH 91/91] chore: remove redundant word in comment Signed-off-by: binchengqu --- src/theme.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/theme.rs b/src/theme.rs index 9fbef238..5863bf85 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -164,7 +164,7 @@ impl fmt::Display for ThemeName { #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] pub enum DetectColorScheme { - /// Only query the terminal for its colors when appropriate (i.e. when the the output is not redirected). + /// Only query the terminal for its colors when appropriate (i.e. when the output is not redirected). #[default] Auto, /// Always query the terminal for its colors.