From 3099f51ba7a19596f084699eeb1b69305a88b9e4 Mon Sep 17 00:00:00 2001 From: Mitchell Kember <mk12360@gmail.com> Date: Sun, 29 Nov 2020 17:16:54 -0500 Subject: [PATCH] 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). --- CHANGELOG.md | 2 + README.md | 7 +- assets/themes/ansi-light.tmTheme | 504 ------------------ .../{ansi-dark.tmTheme => ansi.tmTheme} | 98 +--- src/assets.rs | 9 + src/printer.rs | 28 +- src/terminal.rs | 28 +- 7 files changed, 62 insertions(+), 614 deletions(-) delete mode 100644 assets/themes/ansi-light.tmTheme rename assets/themes/{ansi-dark.tmTheme => ansi.tmTheme} (81%) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab7e262c..07b8ce95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ ## New themes +- `ansi` replaces `ansi-dark` and `ansi-light`, see #1104 and #1412 (@mk12) + ## `bat` as a library ## Packaging diff --git a/README.md b/README.md index f84a2b5f..cbd83731 100644 --- a/README.md +++ b/README.md @@ -390,12 +390,11 @@ You can also use a custom theme by following the ### 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: -- `ansi-dark` looks decent on any terminal with a dark background. It uses 3-bit colors: black, red, - green, yellow, blue, magenta, cyan, and white. -- `ansi-light` is like `ansi-dark`, but for terminals with a light background. +- `ansi` looks decent on any terminal. It uses 3-bit colors: black, red, green, + yellow, blue, magenta, cyan, and white. - `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 [base16 styling guidelines](https://github.com/chriskempson/base16/blob/master/styling.md). diff --git a/assets/themes/ansi-light.tmTheme b/assets/themes/ansi-light.tmTheme deleted file mode 100644 index 5dfb94e8..00000000 --- a/assets/themes/ansi-light.tmTheme +++ /dev/null @@ -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> diff --git a/assets/themes/ansi-dark.tmTheme b/assets/themes/ansi.tmTheme similarity index 81% rename from assets/themes/ansi-dark.tmTheme rename to assets/themes/ansi.tmTheme index a7976951..957f42bb 100644 --- a/assets/themes/ansi-dark.tmTheme +++ b/assets/themes/ansi.tmTheme @@ -3,14 +3,14 @@ <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. + The colors in this theme are encoded as #RRGGBBAA where: + * If AA is 00, then RR is an ANSI palette number from 00 to 07. + * If AA is 01, the terminal's default fg/bg color is used. --> <key>author</key> <string>Template: Chris Kempson, Scheme: Mitchell Kember</string> <key>name</key> - <string>ANSI Dark</string> + <string>ANSI</string> <key>colorSpaceName</key> <string>sRGB</string> <key>settings</key> @@ -19,32 +19,17 @@ <key>settings</key> <dict> <key>background</key> - <string>#00000000</string> - <key>caret</key> - <string>#07000000</string> + <string>#00000001</string> <key>foreground</key> - <string>#07000000</string> - <key>invisibles</key> - <string>#07000000</string> - <key>lineHighlight</key> - <string>#07000000</string> - <key>selection</key> - <string>#07000000</string> + <string>#00000001</string> + <!-- + Explicitly set the gutter color since bat falls back to a + hardcoded DEFAULT_GUTTER_COLOR otherwise. + --> <key>gutter</key> - <string>#00000000</string> + <string>#00000001</string> <key>gutterForeground</key> - <string>#07000000</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> + <string>#00000001</string> </dict> </dict> <dict> @@ -58,39 +43,6 @@ <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>#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> <key>name</key> <string>Keywords</string> @@ -102,17 +54,6 @@ <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>#07000000</string> - </dict> - </dict> <dict> <key>name</key> <string>Functions</string> @@ -146,17 +87,6 @@ <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>#07000000</string> - </dict> - </dict> <dict> <key>name</key> <string>Methods</string> @@ -480,8 +410,6 @@ <dict> <key>background</key> <string>#01000000</string> - <key>foreground</key> - <string>#07000000</string> </dict> </dict> <dict> @@ -493,8 +421,6 @@ <dict> <key>background</key> <string>#03000000</string> - <key>foreground</key> - <string>#00000000</string> </dict> </dict> </array> diff --git a/src/assets.rs b/src/assets.rs index eecb0693..734c6d20 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -189,6 +189,15 @@ impl HighlightingAssets { match self.theme_set.themes.get(theme) { Some(theme) => theme, 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 != "" { use ansi_term::Colour::Yellow; eprintln!( diff --git a/src/printer.rs b/src/printer.rs index d588083e..a4b143d4 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -448,7 +448,7 @@ impl<'a> Printer for InteractivePrinter<'a> { if text.len() != text_trimmed.len() { if let Some(background_color) = background_color { 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 { cursor_max - cursor_total + 1 } else { @@ -589,8 +589,7 @@ impl<'a> Printer for InteractivePrinter<'a> { if let Some(background_color) = background_color { let mut ansi_style = Style::default(); - ansi_style.background = - Some(to_ansi_color(background_color, self.config.true_color)); + ansi_style.background = to_ansi_color(background_color, self.config.true_color); write!( handle, @@ -624,20 +623,27 @@ impl Colors { } fn colored(theme: &Theme, true_color: bool) -> Self { - let gutter_color = theme - .settings - .gutter_foreground - .map(|c| to_ansi_color(c, true_color)) - .unwrap_or(Fixed(DEFAULT_GUTTER_COLOR)); + let gutter_style = Style { + foreground: match theme.settings.gutter_foreground { + // If the theme provides a gutter foreground color, use it. + // Note: It might be the special value #00000001, in which case + // 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 { - grid: gutter_color.normal(), - rule: gutter_color.normal(), + grid: gutter_style, + rule: gutter_style, filename: Style::new().bold(), git_added: Green.normal(), git_removed: Red.normal(), git_modified: Yellow.normal(), - line_number: gutter_color.normal(), + line_number: gutter_style, } } } diff --git a/src/terminal.rs b/src/terminal.rs index b744ba9a..b7159347 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -3,13 +3,13 @@ use ansi_term::{self, Style}; 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 { // Themes can specify one of the user-configurable terminal colors by // 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, - // ansi-dark, base16, and base16-256 use this. - match color.r { + // to the 8-bit color palette number. The built-in themes ansi, base16, + // and base16-256 use this. + Some(match color.r { // For the first 8 colors, use the Color enum to produce ANSI escape // sequences using codes 30-37 (foreground) and 40-47 (background). // 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 // for values 0x08 to 0x0f and only use Fixed for 0x10 to 0xff. 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 { - RGB(color.r, color.g, color.b) + Some(RGB(color.r, color.g, color.b)) } 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 { Style::default() } 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) { color = color.bold(); } @@ -67,6 +77,6 @@ pub fn as_terminal_escaped( 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() }