Add ansi theme to replace ansi-light and ansi-dark

This combines ansi-light and ansi-dark into a single theme that works
with both light and dark backgrounds. Instead of specifying white/black,
the ansi theme uses the terminal's default foreground/background color
by setting alpha=01, i.e. #00000001. This is in addition to the alpha=00
encoding where red contains an ANSI color palette number.

Now, `--theme ansi-light` and `--theme ansi-dark` will print a
deprecation notice and use ansi instead (unless the user has a custom
theme named ansi-light or ansi-dark, which would take precedence).
This commit is contained in:
Mitchell Kember 2020-11-29 17:16:54 -05:00 committed by David Peter
parent 19e7763f35
commit 3099f51ba7
7 changed files with 62 additions and 614 deletions

View File

@ -18,6 +18,8 @@
## New themes ## New themes
- `ansi` replaces `ansi-dark` and `ansi-light`, see #1104 and #1412 (@mk12)
## `bat` as a library ## `bat` as a library
## Packaging ## Packaging

View File

@ -390,12 +390,11 @@ You can also use a custom theme by following the
### 8-bit themes ### 8-bit themes
`bat` has four themes that always use [8-bit colors](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors), `bat` has three themes that always use [8-bit colors](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors),
even when truecolor support is available: even when truecolor support is available:
- `ansi-dark` looks decent on any terminal with a dark background. It uses 3-bit colors: black, red, - `ansi` looks decent on any terminal. It uses 3-bit colors: black, red, green,
green, yellow, blue, magenta, cyan, and white. yellow, blue, magenta, cyan, and white.
- `ansi-light` is like `ansi-dark`, but for terminals with a light background.
- `base16` is designed for [base16](https://github.com/chriskempson/base16) terminal themes. It uses - `base16` is designed for [base16](https://github.com/chriskempson/base16) terminal themes. It uses
4-bit colors (3-bit colors plus bright variants) in accordance with the 4-bit colors (3-bit colors plus bright variants) in accordance with the
[base16 styling guidelines](https://github.com/chriskempson/base16/blob/master/styling.md). [base16 styling guidelines](https://github.com/chriskempson/base16/blob/master/styling.md).

View File

@ -1,504 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!--
The colors in this theme are encoded as #RRGGBBAA where RR is an ANSI
palette number from 00 to 0f, and AA is the special value 00 to indicate
that this encoding is being used.
-->
<key>author</key>
<string>Template: Chris Kempson, Scheme: Mitchell Kember</string>
<key>name</key>
<string>ANSI Light</string>
<key>colorSpaceName</key>
<string>sRGB</string>
<key>settings</key>
<array>
<dict>
<key>settings</key>
<dict>
<key>background</key>
<string>#07000000</string>
<key>caret</key>
<string>#00000000</string>
<key>foreground</key>
<string>#00000000</string>
<key>invisibles</key>
<string>#00000000</string>
<key>lineHighlight</key>
<string>#00000000</string>
<key>selection</key>
<string>#00000000</string>
<key>gutter</key>
<string>#07000000</string>
<key>gutterForeground</key>
<string>#00000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Text</string>
<key>scope</key>
<string>variable.parameter.function</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#00000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Comments</string>
<key>scope</key>
<string>comment, punctuation.definition.comment</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#02000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Punctuation</string>
<key>scope</key>
<string>punctuation.definition.string, punctuation.definition.variable, punctuation.definition.string, punctuation.definition.parameters, punctuation.definition.string, punctuation.definition.array</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#00000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Delimiters</string>
<key>scope</key>
<string>none</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#00000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Operators</string>
<key>scope</key>
<string>keyword.operator</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#00000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Keywords</string>
<key>scope</key>
<string>keyword</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#05000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Variables</string>
<key>scope</key>
<string>variable</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#00000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Functions</string>
<key>scope</key>
<string>entity.name.function, meta.require, support.function.any-method</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#04000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Labels</string>
<key>scope</key>
<string>entity.name.label</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#06000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Classes</string>
<key>scope</key>
<string>support.class, entity.name.class, entity.name.type.class</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#03000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Classes</string>
<key>scope</key>
<string>meta.class</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#00000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Methods</string>
<key>scope</key>
<string>keyword.other.special-method</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#04000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Storage</string>
<key>scope</key>
<string>storage</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#05000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Support</string>
<key>scope</key>
<string>support.function</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#06000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Strings, Inherited Class</string>
<key>scope</key>
<string>string, constant.other.symbol, entity.other.inherited-class</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#02000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Integers</string>
<key>scope</key>
<string>constant.numeric</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#03000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Floats</string>
<key>scope</key>
<string>none</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#03000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Boolean</string>
<key>scope</key>
<string>none</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#03000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Constants</string>
<key>scope</key>
<string>constant</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#03000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Tags</string>
<key>scope</key>
<string>entity.name.tag</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#01000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Attributes</string>
<key>scope</key>
<string>entity.other.attribute-name</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#03000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Attribute IDs</string>
<key>scope</key>
<string>entity.other.attribute-name.id, punctuation.definition.entity</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#04000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Selector</string>
<key>scope</key>
<string>meta.selector</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#05000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Values</string>
<key>scope</key>
<string>none</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#03000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Headings</string>
<key>scope</key>
<string>markup.heading punctuation.definition.heading, entity.name.section</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#04000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Units</string>
<key>scope</key>
<string>keyword.other.unit</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#03000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Bold</string>
<key>scope</key>
<string>markup.bold, punctuation.definition.bold</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>bold</string>
<key>foreground</key>
<string>#03000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Italic</string>
<key>scope</key>
<string>markup.italic, punctuation.definition.italic</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>italic</string>
<key>foreground</key>
<string>#05000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Code</string>
<key>scope</key>
<string>markup.raw.inline</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#02000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Link Text</string>
<key>scope</key>
<string>string.other.link, punctuation.definition.string.end.markdown, punctuation.definition.string.begin.markdown</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#01000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Link Url</string>
<key>scope</key>
<string>meta.link</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#03000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Quotes</string>
<key>scope</key>
<string>markup.quote</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#03000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Inserted</string>
<key>scope</key>
<string>markup.inserted</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#02000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Deleted</string>
<key>scope</key>
<string>markup.deleted</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#01000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Changed</string>
<key>scope</key>
<string>markup.changed</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#05000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Colors</string>
<key>scope</key>
<string>constant.other.color</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#06000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Regular Expressions</string>
<key>scope</key>
<string>string.regexp</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#06000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Escape Characters</string>
<key>scope</key>
<string>constant.character.escape</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#06000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Embedded</string>
<key>scope</key>
<string>punctuation.section.embedded, variable.interpolation</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#05000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Illegal</string>
<key>scope</key>
<string>invalid.illegal</string>
<key>settings</key>
<dict>
<key>background</key>
<string>#01000000</string>
<key>foreground</key>
<string>#00000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Broken</string>
<key>scope</key>
<string>invalid.broken</string>
<key>settings</key>
<dict>
<key>background</key>
<string>#03000000</string>
<key>foreground</key>
<string>#07000000</string>
</dict>
</dict>
</array>
<key>uuid</key>
<string>uuid</string>
</dict>
</plist>

View File

@ -3,14 +3,14 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<!-- <!--
The colors in this theme are encoded as #RRGGBBAA where RR is an ANSI The colors in this theme are encoded as #RRGGBBAA where:
palette number from 00 to 0f, and AA is the special value 00 to indicate * If AA is 00, then RR is an ANSI palette number from 00 to 07.
that this encoding is being used. * If AA is 01, the terminal's default fg/bg color is used.
--> -->
<key>author</key> <key>author</key>
<string>Template: Chris Kempson, Scheme: Mitchell Kember</string> <string>Template: Chris Kempson, Scheme: Mitchell Kember</string>
<key>name</key> <key>name</key>
<string>ANSI Dark</string> <string>ANSI</string>
<key>colorSpaceName</key> <key>colorSpaceName</key>
<string>sRGB</string> <string>sRGB</string>
<key>settings</key> <key>settings</key>
@ -19,32 +19,17 @@
<key>settings</key> <key>settings</key>
<dict> <dict>
<key>background</key> <key>background</key>
<string>#00000000</string> <string>#00000001</string>
<key>caret</key>
<string>#07000000</string>
<key>foreground</key> <key>foreground</key>
<string>#07000000</string> <string>#00000001</string>
<key>invisibles</key> <!--
<string>#07000000</string> Explicitly set the gutter color since bat falls back to a
<key>lineHighlight</key> hardcoded DEFAULT_GUTTER_COLOR otherwise.
<string>#07000000</string> -->
<key>selection</key>
<string>#07000000</string>
<key>gutter</key> <key>gutter</key>
<string>#00000000</string> <string>#00000001</string>
<key>gutterForeground</key> <key>gutterForeground</key>
<string>#07000000</string> <string>#00000001</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Text</string>
<key>scope</key>
<string>variable.parameter.function</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#07000000</string>
</dict> </dict>
</dict> </dict>
<dict> <dict>
@ -58,39 +43,6 @@
<string>#02000000</string> <string>#02000000</string>
</dict> </dict>
</dict> </dict>
<dict>
<key>name</key>
<string>Punctuation</string>
<key>scope</key>
<string>punctuation.definition.string, punctuation.definition.variable, punctuation.definition.string, punctuation.definition.parameters, punctuation.definition.string, punctuation.definition.array</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#07000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Delimiters</string>
<key>scope</key>
<string>none</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#07000000</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Operators</string>
<key>scope</key>
<string>keyword.operator</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#07000000</string>
</dict>
</dict>
<dict> <dict>
<key>name</key> <key>name</key>
<string>Keywords</string> <string>Keywords</string>
@ -102,17 +54,6 @@
<string>#05000000</string> <string>#05000000</string>
</dict> </dict>
</dict> </dict>
<dict>
<key>name</key>
<string>Variables</string>
<key>scope</key>
<string>variable</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#07000000</string>
</dict>
</dict>
<dict> <dict>
<key>name</key> <key>name</key>
<string>Functions</string> <string>Functions</string>
@ -146,17 +87,6 @@
<string>#03000000</string> <string>#03000000</string>
</dict> </dict>
</dict> </dict>
<dict>
<key>name</key>
<string>Classes</string>
<key>scope</key>
<string>meta.class</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#07000000</string>
</dict>
</dict>
<dict> <dict>
<key>name</key> <key>name</key>
<string>Methods</string> <string>Methods</string>
@ -480,8 +410,6 @@
<dict> <dict>
<key>background</key> <key>background</key>
<string>#01000000</string> <string>#01000000</string>
<key>foreground</key>
<string>#07000000</string>
</dict> </dict>
</dict> </dict>
<dict> <dict>
@ -493,8 +421,6 @@
<dict> <dict>
<key>background</key> <key>background</key>
<string>#03000000</string> <string>#03000000</string>
<key>foreground</key>
<string>#00000000</string>
</dict> </dict>
</dict> </dict>
</array> </array>

View File

@ -189,6 +189,15 @@ impl HighlightingAssets {
match self.theme_set.themes.get(theme) { match self.theme_set.themes.get(theme) {
Some(theme) => theme, Some(theme) => theme,
None => { None => {
if theme == "ansi-light" || theme == "ansi-dark" {
use ansi_term::Colour::Yellow;
eprintln!(
"{}: Theme '{}' is deprecated, using 'ansi' instead.",
Yellow.paint("[bat warning]"),
theme
);
return self.get_theme("ansi");
}
if theme != "" { if theme != "" {
use ansi_term::Colour::Yellow; use ansi_term::Colour::Yellow;
eprintln!( eprintln!(

View File

@ -448,7 +448,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
if text.len() != text_trimmed.len() { if text.len() != text_trimmed.len() {
if let Some(background_color) = background_color { if let Some(background_color) = background_color {
let mut ansi_style = Style::default(); let mut ansi_style = Style::default();
ansi_style.background = Some(to_ansi_color(background_color, true_color)); ansi_style.background = to_ansi_color(background_color, true_color);
let width = if cursor_total <= cursor_max { let width = if cursor_total <= cursor_max {
cursor_max - cursor_total + 1 cursor_max - cursor_total + 1
} else { } else {
@ -589,8 +589,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
if let Some(background_color) = background_color { if let Some(background_color) = background_color {
let mut ansi_style = Style::default(); let mut ansi_style = Style::default();
ansi_style.background = ansi_style.background = to_ansi_color(background_color, self.config.true_color);
Some(to_ansi_color(background_color, self.config.true_color));
write!( write!(
handle, handle,
@ -624,20 +623,27 @@ impl Colors {
} }
fn colored(theme: &Theme, true_color: bool) -> Self { fn colored(theme: &Theme, true_color: bool) -> Self {
let gutter_color = theme let gutter_style = Style {
.settings foreground: match theme.settings.gutter_foreground {
.gutter_foreground // If the theme provides a gutter foreground color, use it.
.map(|c| to_ansi_color(c, true_color)) // Note: It might be the special value #00000001, in which case
.unwrap_or(Fixed(DEFAULT_GUTTER_COLOR)); // to_ansi_color returns None and we use an empty Style
// (resulting in the terminal's default foreground color).
Some(c) => to_ansi_color(c, true_color),
// Otherwise, use a specific fallback color.
None => Some(Fixed(DEFAULT_GUTTER_COLOR)),
},
..Style::default()
};
Colors { Colors {
grid: gutter_color.normal(), grid: gutter_style,
rule: gutter_color.normal(), rule: gutter_style,
filename: Style::new().bold(), filename: Style::new().bold(),
git_added: Green.normal(), git_added: Green.normal(),
git_removed: Red.normal(), git_removed: Red.normal(),
git_modified: Yellow.normal(), git_modified: Yellow.normal(),
line_number: gutter_color.normal(), line_number: gutter_style,
} }
} }
} }

View File

@ -3,13 +3,13 @@ use ansi_term::{self, Style};
use syntect::highlighting::{self, FontStyle}; use syntect::highlighting::{self, FontStyle};
pub fn to_ansi_color(color: highlighting::Color, true_color: bool) -> ansi_term::Color { pub fn to_ansi_color(color: highlighting::Color, true_color: bool) -> Option<ansi_term::Color> {
if color.a == 0 { if color.a == 0 {
// Themes can specify one of the user-configurable terminal colors by // Themes can specify one of the user-configurable terminal colors by
// encoding them as #RRGGBBAA with AA set to 00 (transparent) and RR set // encoding them as #RRGGBBAA with AA set to 00 (transparent) and RR set
// to the 8-bit color palette number. The built-in themes ansi-light, // to the 8-bit color palette number. The built-in themes ansi, base16,
// ansi-dark, base16, and base16-256 use this. // and base16-256 use this.
match color.r { Some(match color.r {
// For the first 8 colors, use the Color enum to produce ANSI escape // For the first 8 colors, use the Color enum to produce ANSI escape
// sequences using codes 30-37 (foreground) and 40-47 (background). // sequences using codes 30-37 (foreground) and 40-47 (background).
// For example, red foreground is \x1b[31m. This works on terminals // For example, red foreground is \x1b[31m. This works on terminals
@ -31,11 +31,18 @@ pub fn to_ansi_color(color: highlighting::Color, true_color: bool) -> ansi_term:
// 90-97 (foreground) and 100-107 (background), we should use those // 90-97 (foreground) and 100-107 (background), we should use those
// for values 0x08 to 0x0f and only use Fixed for 0x10 to 0xff. // for values 0x08 to 0x0f and only use Fixed for 0x10 to 0xff.
n => Fixed(n), n => Fixed(n),
} })
} else if color.a == 1 {
// Themes can specify the terminal's default foreground/background color
// (i.e. no escape sequence) using the encoding #RRGGBBAA with AA set to
// 01. The built-in theme ansi uses this.
None
} else if true_color { } else if true_color {
RGB(color.r, color.g, color.b) Some(RGB(color.r, color.g, color.b))
} else { } else {
Fixed(ansi_colours::ansi256_from_rgb((color.r, color.g, color.b))) Some(Fixed(ansi_colours::ansi256_from_rgb((
color.r, color.g, color.b,
))))
} }
} }
@ -54,7 +61,10 @@ pub fn as_terminal_escaped(
let mut style = if !colored { let mut style = if !colored {
Style::default() Style::default()
} else { } else {
let mut color = Style::from(to_ansi_color(style.foreground, true_color)); let mut color = Style {
foreground: to_ansi_color(style.foreground, true_color),
..Style::default()
};
if style.font_style.contains(FontStyle::BOLD) { if style.font_style.contains(FontStyle::BOLD) {
color = color.bold(); color = color.bold();
} }
@ -67,6 +77,6 @@ pub fn as_terminal_escaped(
color color
}; };
style.background = background_color.map(|c| to_ansi_color(c, true_color)); style.background = background_color.and_then(|c| to_ansi_color(c, true_color));
style.paint(text).to_string() style.paint(text).to_string()
} }