Compare commits

...

22 Commits

Author SHA1 Message Date
Adam Johnson
98aeecee17
Merge aabfcfe0ef into c384eed937 2024-09-18 11:12:40 -04:00
dvermd
c384eed937 Do not double panic in FakeParentArgs::drop
Signed-off-by: dvermd <315743+dvermd@users.noreply.github.com>
2024-09-15 10:42:35 +02:00
Dan Davison
7dd279284b Update release Makefile 2024-09-11 08:51:11 -04:00
Dan Davison
9b80e68904 Link to new binaries 2024-09-11 08:49:53 -04:00
Dan Davison
a589ff9deb Bump version 2024-09-11 08:29:53 -04:00
Thomas Otto
3f092c7e74 Upgrade and pin unicode-width to v0.1.12
v0.1.13 calculates the width of some characters differently, which
causes delta to panic

See unicode-rs/unicode-width#55 and unicode-rs/unicode-width#66
2024-09-11 08:24:33 -04:00
Tau Gärtli
3ccdd2d21f Consolidate doc comments 2024-09-08 23:57:24 +02:00
Tau Gärtli
df43b77fa5 Remove fallback to bat theme env var
The fallback is already handled in `set_options`
2024-09-08 23:57:24 +02:00
Tau Gärtli
c5696757c0 Replace "light mode" bool with a dedicated enum 2024-09-08 23:57:24 +02:00
Tau Gärtli
8220cefb3c Make docs proper module-level docs 2024-09-08 23:57:24 +02:00
Tau Gärtli
46c72682d5 Make use top-level 2024-09-08 23:57:24 +02:00
Tau Gärtli
a66ac8e182 Merge get_is_light* functions together 2024-09-08 23:57:24 +02:00
Tau Gärtli
cba999a99f Allow --dark to override a light syntax theme 2024-09-08 23:57:24 +02:00
Tau Gärtli
e1f3e618b7 Add missing test combinations 2024-09-08 23:57:24 +02:00
Tau Gärtli
5363030e96 Add test for dark theme 2024-09-08 23:57:24 +02:00
Tau Gärtli
9830f595b4 Do fallback outside switch 2024-09-08 23:57:24 +02:00
Tau Gärtli
b595a2fe01 De-duplicate test data 2024-09-08 23:57:24 +02:00
Tau Gärtli
3a9c7ad296 Prevent line breaks 2024-09-08 23:57:24 +02:00
Dan Davison
2d7015c774 Fix link
Closes #1848
2024-09-06 19:30:08 -04:00
Dan Davison
329ab6376e Delete unused Makefile target 2024-09-06 19:30:08 -04:00
Tau Gärtli
ba0bfaf9ec
Fix clippy warnings (#1851)
* Fix non-portable doc comments warning

A line starting with > might be interpreted as a block quote.
In regular markdown this could be prevented by escaping the `>`
using a backslash. However, since the doc comments are used by
clap for the long help more or less verbatim, the `\` would be visible
hence the shuffling around of words.

* Use `.contains()`
2024-09-06 08:39:52 -04:00
Adam Johnson
aabfcfe0ef Recommend zdiff3 merge.conflictStyle 2022-12-14 11:04:41 +00:00
21 changed files with 222 additions and 239 deletions

6
Cargo.lock generated
View File

@ -591,7 +591,7 @@ dependencies = [
[[package]]
name = "git-delta"
version = "0.18.1"
version = "0.18.2"
dependencies = [
"ansi_colours",
"ansi_term",
@ -1559,9 +1559,9 @@ checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-width"
version = "0.1.11"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
[[package]]
name = "unsafe-libyaml"

View File

@ -8,7 +8,7 @@ edition = "2018"
homepage = "https://github.com/dandavison/delta"
license = "MIT"
repository = "https://github.com/dandavison/delta"
version = "0.18.1"
version = "0.18.2"
[[bin]]
name = "delta"
@ -55,7 +55,7 @@ syntect = "5.0.0"
sysinfo = { version = "0.29.0", default-features = false, features = [] }
terminal-colorsaurus = "0.4.1"
unicode-segmentation = "1.10.1"
unicode-width = "0.1.10"
unicode-width = "=0.1.12"
xdg = "2.4.1"
[lints.rust]

View File

@ -26,12 +26,6 @@ release:
version:
@grep version Cargo.toml | head -n1 | sed -E 's,.*version = "([^"]+)",\1,'
hash:
@version=$$(make version) && \
printf "$$version-tar.gz %s\n" $$(curl -sL https://github.com/dandavison/delta/archive/$$version.tar.gz | sha256sum -) && \
printf "delta-$$version-x86_64-apple-darwin.tar.gz %s\n" $$(curl -sL https://github.com/dandavison/delta/releases/download/$$version/delta-$$version-x86_64-apple-darwin.tar.gz | sha256sum -) && \
printf "delta-$$version-x86_64-unknown-linux-musl.tar.gz %s\n" $$(curl -sL https://github.com/dandavison/delta/releases/download/$$version/delta-$$version-x86_64-unknown-linux-musl.tar.gz | sha256sum -)
BENCHMARK_INPUT_FILE = /tmp/delta-benchmark-input.gitdiff
BENCHMARK_COMMAND = git log -p 23c292d3f25c67082a2ba315a187268be1a9b0ab
benchmark: build
@ -47,4 +41,4 @@ flamegraph: build
chronologer:
chronologer etc/performance/chronologer.yaml
.PHONY: build format lint test unit-test end-to-end-test release shell-completion version hash benchmark flamegraph chronologer
.PHONY: build format lint test unit-test end-to-end-test release shell-completion version benchmark flamegraph chronologer

View File

@ -32,7 +32,7 @@
# light = true
[merge]
conflictstyle = diff3
conflictStyle = zdiff3
[diff]
colorMoved = default

View File

@ -48,10 +48,9 @@ BUMP_VERSION_IN_DOCUMENTATION_LINKS_SENTINEL=.make-sentinels/bump-version-in-doc
bump-version-in-documentation-links: $(BUMP_VERSION_IN_DOCUMENTATION_LINKS_SENTINEL)
$(BUMP_VERSION_IN_DOCUMENTATION_LINKS_SENTINEL):
sed -i -E "s,$$DELTA_OLD_VERSION,$$DELTA_NEW_VERSION,g" manual/src/full---help-output.md manual/src/installation.md
rg -qF "$$DELTA_NEW_VERSION" manual/src/full---help-output.md
rg -qF "$$DELTA_NEW_VERSION" manual/src/installation.md
git add manual/src/full---help-output.md manual/src/installation.md
git commit -m "Bump version in links to executables"
git commit -m "Link to new binaries"
touch $(BUMP_VERSION_IN_DOCUMENTATION_LINKS_SENTINEL)

View File

@ -13,7 +13,7 @@
navigate = true
[merge]
conflictstyle = diff3
conflictStyle = zdiff3
[diff]
colorMoved = default

View File

@ -1,13 +1,13 @@
# Installation
You can download an executable for your system:
[Linux (glibc)](https://github.com/dandavison/delta/releases/download/0.18.1/delta-0.18.1-x86_64-unknown-linux-gnu.tar.gz)
[Linux (glibc)](https://github.com/dandavison/delta/releases/download/0.18.2/delta-0.18.2-x86_64-unknown-linux-gnu.tar.gz)
|
[Linux (musl)](https://github.com/dandavison/delta/releases/download/0.18.1/delta-0.18.1-x86_64-unknown-linux-musl.tar.gz)
[Linux (musl)](https://github.com/dandavison/delta/releases/download/0.18.2/delta-0.18.2-x86_64-unknown-linux-musl.tar.gz)
|
[MacOS](https://github.com/dandavison/delta/releases/download/0.18.1/delta-0.18.1-x86_64-apple-darwin.tar.gz)
[MacOS](https://github.com/dandavison/delta/releases/download/0.18.2/delta-0.18.2-x86_64-apple-darwin.tar.gz)
|
[Windows](https://github.com/dandavison/delta/releases/download/0.18.1/delta-0.18.1-x86_64-pc-windows-msvc.zip)
[Windows](https://github.com/dandavison/delta/releases/download/0.18.2/delta-0.18.2-x86_64-pc-windows-msvc.zip)
|
[All](https://github.com/dandavison/delta/releases)

View File

@ -1,13 +1,14 @@
# Merge conflicts
Consider setting
Consider setting [`merge.conflictStyle`](https://git-scm.com/docs/git-config#Documentation/git-config.txt-mergeconflictStyle) to `zdiff3`:
```gitconfig
[merge]
conflictstyle = diff3
conflictStyle = zdiff3
```
With that setting, when a merge conflict is encountered, delta will display diffs between the ancestral commit and each of the two merge parents:
With that setting, when a merge conflict is encountered, Git will display merge conflicts with the contents of the merge base as well.
delta will then display this as two diffs, from the ancestor to each side of the conflict:
<table><tr><td><img width=500px src="https://user-images.githubusercontent.com/52205/144783121-bb549100-69d8-41b8-ac62-1704f1f7b43e.png" alt="image" /></td></tr></table>

View File

@ -12,4 +12,4 @@ To format file links for opening in VSCode from other terminal emulators, use th
(To use VSCode Insiders, change that to `vscode-insiders://file/{path}:{line}`).
See [hyperlinks](./hyperlinks.md).
See [hyperlinks](../hyperlinks.md).

View File

@ -11,6 +11,7 @@ use syntect::highlighting::Theme as SyntaxTheme;
use syntect::parsing::SyntaxSet;
use crate::ansi::{ANSI_SGR_BOLD, ANSI_SGR_RESET, ANSI_SGR_UNDERLINE};
use crate::color::ColorMode;
use crate::config::delta_unreachable;
use crate::env::DeltaEnv;
use crate::git_config::GitConfig;
@ -411,9 +412,9 @@ pub struct Opt {
/// and line numbers link to the local file using a file URL, whereas commit hashes link to the
/// commit in GitHub, if the remote repository is hosted by GitHub. See
/// --hyperlinks-file-link-format for full control over the file URLs emitted. Hyperlinks are
/// supported by several common terminal emulators. To make them work, you must use less version
/// >= 581 with the -R flag (or use -r with older less versions, but this will break e.g.
/// --navigate). If you use tmux, then you will also need a patched fork of tmux (see
/// supported by several common terminal emulators. To make them work, you must use less
/// version >= 581 with the -R flag (or use -r with older less versions, but this will break
/// e.g. --navigate). If you use tmux, then you will also need a patched fork of tmux (see
/// <https://github.com/dandavison/tmux>).
pub hyperlinks: bool,
@ -1180,7 +1181,7 @@ pub struct ComputedValues {
pub background_color_extends_to_terminal_width: bool,
pub decorations_width: Width,
pub inspect_raw_lines: InspectRawLines,
pub is_light_mode: bool,
pub color_mode: ColorMode,
pub paging_mode: PagingMode,
pub syntax_set: SyntaxSet,
pub syntax_theme: Option<SyntaxTheme>,

View File

@ -8,6 +8,7 @@ use syntect::highlighting::Color as SyntectColor;
use crate::fatal;
use crate::git_config::GitConfig;
use crate::utils;
use ColorMode::*;
pub fn parse_color(s: &str, true_color: bool, git_config: Option<&GitConfig>) -> Option<Color> {
if s == "normal" {
@ -105,39 +106,50 @@ fn ansi_16_color_number_to_name(n: u8) -> Option<&'static str> {
None
}
pub fn get_minus_background_color_default(is_light_mode: bool, is_true_color: bool) -> Color {
match (is_light_mode, is_true_color) {
(true, true) => LIGHT_THEME_MINUS_COLOR,
(true, false) => LIGHT_THEME_MINUS_COLOR_256,
(false, true) => DARK_THEME_MINUS_COLOR,
(false, false) => DARK_THEME_MINUS_COLOR_256,
/// The color mode determines some default color choices
/// such as the diff background color or the palette used for blame.
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
pub enum ColorMode {
#[default]
/// Dark background with light text.
Dark,
/// Light background with dark text.
Light,
}
pub fn get_minus_background_color_default(mode: ColorMode, is_true_color: bool) -> Color {
match (mode, is_true_color) {
(Light, true) => LIGHT_THEME_MINUS_COLOR,
(Light, false) => LIGHT_THEME_MINUS_COLOR_256,
(Dark, true) => DARK_THEME_MINUS_COLOR,
(Dark, false) => DARK_THEME_MINUS_COLOR_256,
}
}
pub fn get_minus_emph_background_color_default(is_light_mode: bool, is_true_color: bool) -> Color {
match (is_light_mode, is_true_color) {
(true, true) => LIGHT_THEME_MINUS_EMPH_COLOR,
(true, false) => LIGHT_THEME_MINUS_EMPH_COLOR_256,
(false, true) => DARK_THEME_MINUS_EMPH_COLOR,
(false, false) => DARK_THEME_MINUS_EMPH_COLOR_256,
pub fn get_minus_emph_background_color_default(mode: ColorMode, is_true_color: bool) -> Color {
match (mode, is_true_color) {
(Light, true) => LIGHT_THEME_MINUS_EMPH_COLOR,
(Light, false) => LIGHT_THEME_MINUS_EMPH_COLOR_256,
(Dark, true) => DARK_THEME_MINUS_EMPH_COLOR,
(Dark, false) => DARK_THEME_MINUS_EMPH_COLOR_256,
}
}
pub fn get_plus_background_color_default(is_light_mode: bool, is_true_color: bool) -> Color {
match (is_light_mode, is_true_color) {
(true, true) => LIGHT_THEME_PLUS_COLOR,
(true, false) => LIGHT_THEME_PLUS_COLOR_256,
(false, true) => DARK_THEME_PLUS_COLOR,
(false, false) => DARK_THEME_PLUS_COLOR_256,
pub fn get_plus_background_color_default(mode: ColorMode, is_true_color: bool) -> Color {
match (mode, is_true_color) {
(Light, true) => LIGHT_THEME_PLUS_COLOR,
(Light, false) => LIGHT_THEME_PLUS_COLOR_256,
(Dark, true) => DARK_THEME_PLUS_COLOR,
(Dark, false) => DARK_THEME_PLUS_COLOR_256,
}
}
pub fn get_plus_emph_background_color_default(is_light_mode: bool, is_true_color: bool) -> Color {
match (is_light_mode, is_true_color) {
(true, true) => LIGHT_THEME_PLUS_EMPH_COLOR,
(true, false) => LIGHT_THEME_PLUS_EMPH_COLOR_256,
(false, true) => DARK_THEME_PLUS_EMPH_COLOR,
(false, false) => DARK_THEME_PLUS_EMPH_COLOR_256,
pub fn get_plus_emph_background_color_default(mode: ColorMode, is_true_color: bool) -> Color {
match (mode, is_true_color) {
(Light, true) => LIGHT_THEME_PLUS_EMPH_COLOR,
(Light, false) => LIGHT_THEME_PLUS_EMPH_COLOR_256,
(Dark, true) => DARK_THEME_PLUS_EMPH_COLOR,
(Dark, false) => DARK_THEME_PLUS_EMPH_COLOR_256,
}
}

View File

@ -9,7 +9,7 @@ use syntect::parsing::SyntaxSet;
use crate::ansi;
use crate::cli;
use crate::color;
use crate::color::{self, ColorMode};
use crate::delta::State;
use crate::fatal;
use crate::features::navigate;
@ -214,7 +214,7 @@ impl From<cli::Opt> for Config {
));
});
let blame_palette = make_blame_palette(opt.blame_palette, opt.computed.is_light_mode);
let blame_palette = make_blame_palette(opt.blame_palette, opt.computed.color_mode);
if blame_palette.is_empty() {
fatal("Option 'blame-palette' must not be empty.")
@ -437,17 +437,17 @@ impl From<cli::Opt> for Config {
}
}
fn make_blame_palette(blame_palette: Option<String>, is_light_mode: bool) -> Vec<String> {
match (blame_palette, is_light_mode) {
fn make_blame_palette(blame_palette: Option<String>, mode: ColorMode) -> Vec<String> {
match (blame_palette, mode) {
(Some(string), _) => string
.split_whitespace()
.map(|s| s.to_owned())
.collect::<Vec<String>>(),
(None, true) => color::LIGHT_THEME_BLAME_PALETTE
(None, ColorMode::Light) => color::LIGHT_THEME_BLAME_PALETTE
.iter()
.map(|s| s.to_string())
.collect::<Vec<String>>(),
(None, false) => color::DARK_THEME_BLAME_PALETTE
(None, ColorMode::Dark) => color::DARK_THEME_BLAME_PALETTE
.iter()
.map(|s| s.to_string())
.collect::<Vec<String>>(),

View File

@ -3,6 +3,7 @@ use std::cmp::max;
use lazy_static::lazy_static;
use regex::Regex;
use crate::color::ColorMode::*;
use crate::config;
use crate::delta::State;
use crate::features::hyperlinks;
@ -38,26 +39,27 @@ pub fn make_feature() -> Vec<(String, OptionValueFunction)> {
"line-numbers-minus-style",
String,
None,
opt => if opt.computed.is_light_mode {
"red".to_string()
} else {
"88".to_string()
opt => match opt.computed.color_mode {
Light => "red",
Dark => "88",
}
),
(
"line-numbers-zero-style",
String,
None,
opt => if opt.computed.is_light_mode {"#dddddd"} else {"#444444"}
opt => match opt.computed.color_mode {
Light => "#dddddd",
Dark => "#444444",
}
),
(
"line-numbers-plus-style",
String,
None,
opt => if opt.computed.is_light_mode {
"green".to_string()
} else {
"28".to_string()
opt => match opt.computed.color_mode {
Light => "green",
Dark => "28",
}
)
])

View File

@ -105,7 +105,7 @@ fn run_app() -> std::io::Result<i32> {
Some(subcommands::show_themes::show_themes(
opt.dark,
opt.light,
opt.computed.is_light_mode,
opt.computed.color_mode,
))
} else if opt.show_colors {
Some(subcommands::show_colors::show_colors())

View File

@ -237,7 +237,7 @@ pub fn set_options(
// Setting ComputedValues
set_widths_and_isatty(opt);
set_true_color(opt);
theme::set__is_light_mode__syntax_theme__syntax_set(opt, assets);
theme::set__color_mode__syntax_theme__syntax_set(opt, assets);
opt.computed.inspect_raw_lines =
cli::InspectRawLines::from_str(&opt.inspect_raw_lines).unwrap();
opt.computed.paging_mode = parse_paging_mode(&opt.paging_mode);

View File

@ -1,30 +1,33 @@
//! Delta doesn't have a formal concept of a "theme". What it has is
//!
//! 1. The choice of "theme". This is the language syntax highlighting theme; you have to make this
//! choice when using `bat` also.
//! 2. The choice of "light vs dark mode". This determines whether the background colors should be
//! chosen for a light or dark terminal background. (`bat` has no equivalent.)
//!
//! Basically:
//! 1. The theme is specified by the `--syntax-theme` option. If this isn't supplied then it is specified
//! by the `BAT_THEME` environment variable.
//! 2. Light vs dark mode is specified by the `--light` or `--dark` options. If these aren't
//! supplied then it detected from the terminal. If this fails it is inferred from the chosen theme.
//!
//! In the absence of other factors, the default assumes a dark terminal background.
use std::io::{stdout, IsTerminal};
/// Delta doesn't have a formal concept of a "theme". What it has is
/// (a) the choice of syntax-highlighting theme
/// (b) the choice of light-background-mode vs dark-background-mode, which determine certain
/// default color choices
/// This module sets those options. If the light/dark background mode choice is not made explicitly
/// by the user, it is determined by the classification of the syntax theme into light-background
/// vs dark-background syntax themes. If the user didn't choose a syntax theme, a dark-background
/// default is selected.
use bat;
use bat::assets::HighlightingAssets;
#[cfg(not(test))]
use terminal_colorsaurus::{color_scheme, QueryOptions};
use crate::cli::{self, DetectDarkLight};
use crate::color::{ColorMode, ColorMode::*};
#[allow(non_snake_case)]
pub fn set__is_light_mode__syntax_theme__syntax_set(
opt: &mut cli::Opt,
assets: HighlightingAssets,
) {
let syntax_theme_name_from_bat_theme = &opt.env.bat_theme;
let (is_light_mode, syntax_theme_name) = get_is_light_mode_and_syntax_theme_name(
opt.syntax_theme.as_ref(),
syntax_theme_name_from_bat_theme.as_ref(),
get_is_light(opt),
);
opt.computed.is_light_mode = is_light_mode;
pub fn set__color_mode__syntax_theme__syntax_set(opt: &mut cli::Opt, assets: HighlightingAssets) {
let (color_mode, syntax_theme_name) =
get_color_mode_and_syntax_theme_name(opt.syntax_theme.as_ref(), get_color_mode(opt));
opt.computed.color_mode = color_mode;
opt.computed.syntax_theme = if is_no_syntax_highlighting_syntax_theme_name(&syntax_theme_name) {
None
@ -38,6 +41,14 @@ pub fn is_light_syntax_theme(theme: &str) -> bool {
LIGHT_SYNTAX_THEMES.contains(&theme) || theme.to_lowercase().contains("light")
}
pub fn color_mode_from_syntax_theme(theme: &str) -> ColorMode {
if is_light_syntax_theme(theme) {
ColorMode::Light
} else {
ColorMode::Dark
}
}
const LIGHT_SYNTAX_THEMES: [&str; 7] = [
"Catppuccin Latte",
"GitHub",
@ -55,69 +66,34 @@ fn is_no_syntax_highlighting_syntax_theme_name(theme_name: &str) -> bool {
theme_name.to_lowercase() == "none"
}
/// Return a (theme_name, is_light_mode) tuple.
/// Return a (theme_name, color_mode) tuple.
/// theme_name == None in return value means syntax highlighting is disabled.
///
/// There are two types of color choices that have to be made:
/// 1. The choice of "theme". This is the language syntax highlighting theme; you have to make this
/// choice when using `bat` also.
/// 2. The choice of "light vs dark mode". This determines whether the background colors should be
/// chosen for a light or dark terminal background. (`bat` has no equivalent.)
///
/// Basically:
/// 1. The theme is specified by the `--syntax-theme` option. If this isn't supplied then it is specified
/// by the `BAT_THEME` environment variable.
/// 2. Light vs dark mode is specified by the `--light` or `--dark` options. If these aren't
/// supplied then it is inferred from the chosen theme.
///
/// In the absence of other factors, the default assumes a dark terminal background.
///
/// Specifically, the rules are as follows:
///
/// | --theme | $BAT_THEME | --light/--dark | Behavior |
/// |------------|------------|----------------|----------------------------------------------------------------------------|
/// | - | - | - | default dark theme, dark mode |
/// | some_theme | (IGNORED) | - | some_theme with light/dark mode inferred accordingly |
/// | - | BAT_THEME | - | BAT_THEME, with light/dark mode inferred accordingly |
/// | - | - | yes | default light/dark theme, light/dark mode |
/// | some_theme | (IGNORED) | yes | some_theme, light/dark mode (even if some_theme conflicts with light/dark) |
/// | - | BAT_THEME | yes | BAT_THEME, light/dark mode (even if BAT_THEME conflicts with light/dark) |
fn get_is_light_mode_and_syntax_theme_name(
theme_arg: Option<&String>,
bat_theme_env_var: Option<&String>,
light_mode: bool,
) -> (bool, String) {
match (theme_arg, bat_theme_env_var, light_mode) {
(None, None, false) => (false, DEFAULT_DARK_SYNTAX_THEME.to_string()),
(Some(theme_name), _, false) => (is_light_syntax_theme(theme_name), theme_name.to_string()),
(None, Some(theme_name), false) => {
(is_light_syntax_theme(theme_name), theme_name.to_string())
}
(None, None, true) => (true, DEFAULT_LIGHT_SYNTAX_THEME.to_string()),
(Some(theme_name), _, is_light_mode) => (is_light_mode, theme_name.to_string()),
(None, Some(theme_name), is_light_mode) => (is_light_mode, theme_name.to_string()),
fn get_color_mode_and_syntax_theme_name(
syntax_theme: Option<&String>,
mode: Option<ColorMode>,
) -> (ColorMode, String) {
match (syntax_theme, mode) {
(Some(theme), None) => (color_mode_from_syntax_theme(theme), theme.to_string()),
(Some(theme), Some(mode)) => (mode, theme.to_string()),
(None, None | Some(Dark)) => (Dark, DEFAULT_DARK_SYNTAX_THEME.to_string()),
(None, Some(Light)) => (Light, DEFAULT_LIGHT_SYNTAX_THEME.to_string()),
}
}
fn get_is_light(opt: &cli::Opt) -> bool {
get_is_light_opt(opt)
.or_else(|| should_detect_dark_light(opt).then(detect_light_mode))
.unwrap_or_default()
}
fn get_is_light_opt(opt: &cli::Opt) -> Option<bool> {
fn get_color_mode(opt: &cli::Opt) -> Option<ColorMode> {
if opt.light {
Some(true)
Some(Light)
} else if opt.dark {
Some(false)
Some(Dark)
} else if should_detect_color_mode(opt) {
detect_color_mode()
} else {
None
}
}
/// See [`cli::Opt::detect_dark_light`] for a detailed explanation.
fn should_detect_dark_light(opt: &cli::Opt) -> bool {
fn should_detect_color_mode(opt: &cli::Opt) -> bool {
match opt.detect_dark_light {
DetectDarkLight::Auto => opt.color_only || stdout().is_terminal(),
DetectDarkLight::Always => true,
@ -126,19 +102,26 @@ fn should_detect_dark_light(opt: &cli::Opt) -> bool {
}
#[cfg(not(test))]
fn detect_light_mode() -> bool {
use terminal_colorsaurus::{color_scheme, ColorScheme, QueryOptions};
color_scheme(QueryOptions::default()).unwrap_or_default() == ColorScheme::Light
fn detect_color_mode() -> Option<ColorMode> {
color_scheme(QueryOptions::default())
.ok()
.map(ColorMode::from)
}
impl From<terminal_colorsaurus::ColorScheme> for ColorMode {
fn from(value: terminal_colorsaurus::ColorScheme) -> Self {
match value {
terminal_colorsaurus::ColorScheme::Dark => ColorMode::Dark,
terminal_colorsaurus::ColorScheme::Light => ColorMode::Light,
}
}
}
#[cfg(test)]
fn detect_light_mode() -> bool {
LIGHT_MODE_IN_TESTS
fn detect_color_mode() -> Option<ColorMode> {
None
}
#[cfg(test)]
pub(crate) const LIGHT_MODE_IN_TESTS: bool = false;
#[cfg(test)]
mod tests {
use super::*;
@ -148,41 +131,24 @@ mod tests {
// TODO: Test influence of BAT_THEME env var. E.g. see utils::process::tests::FakeParentArgs.
#[test]
fn test_syntax_theme_selection() {
#[derive(PartialEq)]
enum Mode {
Light,
Dark,
}
for (
syntax_theme,
mode, // (--light, --dark)
expected_syntax_theme,
expected_mode,
) in vec![
(None, None, DEFAULT_DARK_SYNTAX_THEME, Mode::Dark),
(Some("GitHub"), None, "GitHub", Mode::Light),
(Some("GitHub"), None, "GitHub", Mode::Light),
(
None,
Some(Mode::Light),
DEFAULT_LIGHT_SYNTAX_THEME,
Mode::Light,
),
(
None,
Some(Mode::Dark),
DEFAULT_DARK_SYNTAX_THEME,
Mode::Dark,
),
(
None,
Some(Mode::Light),
DEFAULT_LIGHT_SYNTAX_THEME,
Mode::Light,
),
(None, Some(Mode::Light), "GitHub", Mode::Light),
(Some("none"), None, "none", Mode::Dark),
(Some("None"), Some(Mode::Light), "none", Mode::Light),
(None, None, DEFAULT_DARK_SYNTAX_THEME, Dark),
(Some("GitHub"), None, "GitHub", Light),
(Some("Nord"), None, "Nord", Dark),
(None, Some(Dark), DEFAULT_DARK_SYNTAX_THEME, Dark),
(None, Some(Light), DEFAULT_LIGHT_SYNTAX_THEME, Light),
(Some("GitHub"), Some(Light), "GitHub", Light),
(Some("GitHub"), Some(Dark), "GitHub", Dark),
(Some("Nord"), Some(Light), "Nord", Light),
(Some("Nord"), Some(Dark), "Nord", Dark),
(Some("none"), None, "none", Dark),
(Some("none"), Some(Dark), "none", Dark),
(Some("None"), Some(Light), "none", Light),
] {
let mut args = vec![];
if let Some(syntax_theme) = syntax_theme {
@ -198,10 +164,10 @@ mod tests {
args.push("never");
}
match mode {
Some(Mode::Light) => {
Some(Light) => {
args.push("--light");
}
Some(Mode::Dark) => {
Some(Dark) => {
args.push("--dark");
}
None => {}
@ -225,31 +191,19 @@ mod tests {
}
assert_eq!(
config.minus_style.ansi_term_style.background.unwrap(),
color::get_minus_background_color_default(
expected_mode == Mode::Light,
is_true_color
)
color::get_minus_background_color_default(expected_mode, is_true_color)
);
assert_eq!(
config.minus_emph_style.ansi_term_style.background.unwrap(),
color::get_minus_emph_background_color_default(
expected_mode == Mode::Light,
is_true_color
)
color::get_minus_emph_background_color_default(expected_mode, is_true_color)
);
assert_eq!(
config.plus_style.ansi_term_style.background.unwrap(),
color::get_plus_background_color_default(
expected_mode == Mode::Light,
is_true_color
)
color::get_plus_background_color_default(expected_mode, is_true_color)
);
assert_eq!(
config.plus_emph_style.ansi_term_style.background.unwrap(),
color::get_plus_emph_background_color_default(
expected_mode == Mode::Light,
is_true_color
)
color::get_plus_emph_background_color_default(expected_mode, is_true_color)
);
}
}

View File

@ -115,15 +115,14 @@ fn parse_as_reference_to_git_config(style_string: &str, opt: &cli::Opt) -> Style
}
fn make_hunk_styles<'a>(opt: &'a cli::Opt, styles: &'a mut HashMap<&str, StyleReference>) {
let is_light_mode = opt.computed.is_light_mode;
let color_mode = opt.computed.color_mode;
let true_color = opt.computed.true_color;
let minus_style = style_from_str(
&opt.minus_style,
Some(Style::from_colors(
None,
Some(color::get_minus_background_color_default(
is_light_mode,
true_color,
color_mode, true_color,
)),
)),
None,
@ -136,8 +135,7 @@ fn make_hunk_styles<'a>(opt: &'a cli::Opt, styles: &'a mut HashMap<&str, StyleRe
Some(Style::from_colors(
None,
Some(color::get_minus_emph_background_color_default(
is_light_mode,
true_color,
color_mode, true_color,
)),
)),
None,
@ -160,8 +158,7 @@ fn make_hunk_styles<'a>(opt: &'a cli::Opt, styles: &'a mut HashMap<&str, StyleRe
Some(Style::from_colors(
None,
Some(color::get_minus_background_color_default(
is_light_mode,
true_color,
color_mode, true_color,
)),
)),
None,
@ -176,8 +173,7 @@ fn make_hunk_styles<'a>(opt: &'a cli::Opt, styles: &'a mut HashMap<&str, StyleRe
Some(Style::from_colors(
None,
Some(color::get_plus_background_color_default(
is_light_mode,
true_color,
color_mode, true_color,
)),
)),
None,
@ -190,8 +186,7 @@ fn make_hunk_styles<'a>(opt: &'a cli::Opt, styles: &'a mut HashMap<&str, StyleRe
Some(Style::from_colors(
None,
Some(color::get_plus_emph_background_color_default(
is_light_mode,
true_color,
color_mode, true_color,
)),
)),
None,
@ -214,8 +209,7 @@ fn make_hunk_styles<'a>(opt: &'a cli::Opt, styles: &'a mut HashMap<&str, StyleRe
Some(Style::from_colors(
None,
Some(color::get_plus_background_color_default(
is_light_mode,
true_color,
color_mode, true_color,
)),
)),
None,

View File

@ -1,8 +1,9 @@
use crate::cli;
use crate::color::{ColorMode, ColorMode::*};
use crate::config;
use crate::delta;
use crate::env::DeltaEnv;
use crate::options::theme::is_light_syntax_theme;
use crate::options::theme::color_mode_from_syntax_theme;
use crate::utils;
use crate::utils::bat::output::{OutputType, PagingMode};
use clap::Parser;
@ -41,19 +42,19 @@ pub fn show_syntax_themes() -> std::io::Result<()> {
let opt = make_opt();
if !(opt.dark || opt.light) {
_show_syntax_themes(opt, false, &mut writer, stdin_data.as_ref())?;
_show_syntax_themes(make_opt(), true, &mut writer, stdin_data.as_ref())?;
_show_syntax_themes(opt, Dark, &mut writer, stdin_data.as_ref())?;
_show_syntax_themes(make_opt(), Light, &mut writer, stdin_data.as_ref())?;
} else if opt.light {
_show_syntax_themes(opt, true, &mut writer, stdin_data.as_ref())?;
_show_syntax_themes(opt, Light, &mut writer, stdin_data.as_ref())?;
} else {
_show_syntax_themes(opt, false, &mut writer, stdin_data.as_ref())?
_show_syntax_themes(opt, Dark, &mut writer, stdin_data.as_ref())?
};
Ok(())
}
fn _show_syntax_themes(
mut opt: cli::Opt,
is_light_mode: bool,
color_mode: ColorMode,
writer: &mut dyn Write,
stdin: Option<&Vec<u8>>,
) -> std::io::Result<()> {
@ -80,14 +81,14 @@ index f38589a..0f1bb83 100644
}
};
opt.computed.is_light_mode = is_light_mode;
opt.computed.color_mode = color_mode;
let mut config = config::Config::from(opt);
let title_style = ansi_term::Style::new().bold();
let assets = utils::bat::assets::load_highlighting_assets();
for syntax_theme in assets
.themes()
.filter(|t| is_light_syntax_theme(t) == is_light_mode)
.filter(|t| color_mode_from_syntax_theme(t) == color_mode)
{
writeln!(
writer,
@ -121,7 +122,7 @@ mod tests {
let opt = integration_test_utils::make_options_from_args(&[]);
let mut writer = Cursor::new(vec![0; 1024]);
_show_syntax_themes(opt, true, &mut writer, None).unwrap();
_show_syntax_themes(opt, Light, &mut writer, None).unwrap();
let mut s = String::new();
writer.rewind().unwrap();
writer.read_to_string(&mut s).unwrap();

View File

@ -1,6 +1,7 @@
use std::io::{self, ErrorKind, IsTerminal, Read};
use crate::cli;
use crate::color::ColorMode;
use crate::config;
use crate::delta;
use crate::env::DeltaEnv;
@ -8,7 +9,7 @@ use crate::git_config;
use crate::options::get::get_themes;
use crate::utils::bat::output::{OutputType, PagingMode};
pub fn show_themes(dark: bool, light: bool, computed_theme_is_light: bool) -> std::io::Result<()> {
pub fn show_themes(dark: bool, light: bool, color_mode: ColorMode) -> std::io::Result<()> {
use std::io::BufReader;
use bytelines::ByteLines;
@ -58,8 +59,8 @@ pub fn show_themes(dark: bool, light: bool, computed_theme_is_light: bool) -> st
let is_light_theme = opt.light;
let config = config::Config::from(opt);
if (!computed_theme_is_light && is_dark_theme)
|| (computed_theme_is_light && is_light_theme)
if (color_mode == ColorMode::Dark && is_dark_theme)
|| (color_mode == ColorMode::Light && is_light_theme)
|| (dark && light)
{
writeln!(writer, "\n\nTheme: {}\n", title_style.paint(theme))?;

View File

@ -103,7 +103,7 @@ pub fn wrap(text: &str, width: usize, indent_with: &str, no_indent: &str, no_wra
}
#[cfg(test)]
if result.find("no-sanity").is_none() {
if !result.contains("no-sanity") {
// sanity check
let stripped_input = text
.replace(" ", "")

View File

@ -598,6 +598,7 @@ pub mod tests {
With(usize, Rc<Vec<T>>),
None,
Invalid,
ErrorAlreadyHandled,
}
// When calling `FakeParentArgs::get()`, it can return `Some(values)` which were set earlier
@ -648,24 +649,39 @@ pub mod tests {
TlsState::With(n, args) => TlsState::With(*n + 1, Rc::clone(args)),
TlsState::None => TlsState::None,
TlsState::Invalid => TlsState::Invalid,
TlsState::ErrorAlreadyHandled => TlsState::ErrorAlreadyHandled,
});
match old_value {
TlsState::Once(args) | TlsState::Scope(args) => Some(args),
TlsState::With(n, args) if n < args.len() => Some(args[n].clone()),
TlsState::None => None,
TlsState::Invalid | TlsState::With(_, _) => Self::error("get"),
TlsState::Invalid | TlsState::With(_, _) | TlsState::ErrorAlreadyHandled => {
Self::error("get");
None
}
}
})
}
pub fn are_set() -> bool {
FAKE_ARGS.with(|a| *a.borrow() != TlsState::None)
FAKE_ARGS.with(|a| {
*a.borrow() != TlsState::None && *a.borrow() != TlsState::ErrorAlreadyHandled
})
}
fn error(where_: &str) -> ! {
panic!(
"test logic error (in {}): wrong FakeParentArgs scope?",
where_
);
fn error(where_: &str) {
FAKE_ARGS.with(|a| {
let old_value = a.replace(TlsState::ErrorAlreadyHandled);
match old_value {
TlsState::ErrorAlreadyHandled => (),
_ => {
panic!(
"test logic error (in {}): wrong FakeParentArgs scope?",
where_
);
}
}
});
}
}
impl Drop for FakeParentArgs {
@ -680,7 +696,7 @@ pub mod tests {
}
}
TlsState::Once(_) | TlsState::None => Self::error("drop"),
TlsState::Scope(_) | TlsState::Invalid => {}
TlsState::Scope(_) | TlsState::Invalid | TlsState::ErrorAlreadyHandled => {}
}
});
}
@ -838,7 +854,7 @@ pub mod tests {
}
#[test]
#[should_panic]
#[should_panic(expected = "test logic error (in get): wrong FakeParentArgs scope?")]
fn test_process_testing_assert() {
let _args = FakeParentArgs::once("git blame do.not.panic");
assert_eq!(
@ -850,13 +866,23 @@ pub mod tests {
}
#[test]
#[should_panic]
fn test_process_testing_assert_never_used() {
#[should_panic(expected = "test logic error (in drop): wrong FakeParentArgs scope?")]
fn test_process_testing_assert_once_never_used() {
let _args = FakeParentArgs::once("never used");
}
// causes a panic while panicking, so can't test:
// let _args = FakeParentArgs::for_scope(&"never used");
// let _args = FakeParentArgs::once(&"never used");
#[test]
#[should_panic(expected = "test logic error (in once): wrong FakeParentArgs scope?")]
fn test_process_testing_assert_for_scope_never_used() {
let _args = FakeParentArgs::for_scope(&"never used");
let _args = FakeParentArgs::once(&"never used");
}
#[test]
#[should_panic(expected = "test logic error (in for_scope): wrong FakeParentArgs scope?")]
fn test_process_testing_assert_once_never_used2() {
let _args = FakeParentArgs::once(&"never used");
let _args = FakeParentArgs::for_scope(&"never used");
}
#[test]
@ -879,13 +905,13 @@ pub mod tests {
}
#[test]
#[should_panic]
#[should_panic(expected = "test logic error (in drop with): wrong FakeParentArgs scope?")]
fn test_process_testing_n_times_unused() {
let _args = FakeParentArgs::with(&["git blame once", "git blame twice"]);
}
#[test]
#[should_panic]
#[should_panic(expected = "test logic error (in drop with): wrong FakeParentArgs scope?")]
fn test_process_testing_n_times_underused() {
let _args = FakeParentArgs::with(&["git blame once", "git blame twice"]);
assert_eq!(
@ -895,15 +921,13 @@ pub mod tests {
}
#[test]
#[should_panic]
#[ignore]
#[should_panic(expected = "test logic error (in get): wrong FakeParentArgs scope?")]
fn test_process_testing_n_times_overused() {
let _args = FakeParentArgs::with(&["git blame once"]);
assert_eq!(
calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename),
Some("once".into())
);
// ignored: dropping causes a panic while panicking, so can't test
calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename);
}