Compare commits

...

20 Commits

Author SHA1 Message Date
LUCIANO FURTADO
fd5dcab06b
Merge 9c4f98aef5 into 0dd38d43f7 2024-09-22 20:27:42 +02:00
dvermd
0dd38d43f7
Improve blame file type detection (#1829)
Move blame to CallingProcess

Signed-off-by: dvermd <315743+dvermd@users.noreply.github.com>
2024-09-22 20:25:44 +02: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
LUCIANO FURTADO
9c4f98aef5
avoid negative repeat_count fixes #1760 2024-08-01 08:59:37 -05:00
18 changed files with 469 additions and 389 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

@ -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

@ -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

@ -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;
@ -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

@ -14,7 +14,7 @@ use crate::format::{self, FormatStringSimple, Placeholder};
use crate::format::{make_placeholder_regex, parse_line_number_format};
use crate::paint::{self, BgShouldFill, StyleSectionSpecifier};
use crate::style::Style;
use crate::utils;
use crate::utils::process;
#[derive(Clone, Debug)]
pub enum BlameLineNumbers {
@ -76,9 +76,8 @@ impl<'a> StateMachine<'a> {
)?;
// Emit syntax-highlighted code
if matches!(self.state, State::Unknown) {
self.painter
.set_syntax(utils::process::git_blame_filename().as_deref());
if self.state == State::Unknown {
self.painter.set_syntax(self.get_filename().as_deref());
self.painter.set_highlighter();
}
self.state = State::Blame(key);
@ -94,6 +93,13 @@ impl<'a> StateMachine<'a> {
Ok(handled_line)
}
fn get_filename(&self) -> Option<String> {
match &*process::calling_process() {
process::CallingProcess::GitBlame(command_line) => command_line.last_arg.clone(),
_ => None,
}
}
fn blame_metadata_style(
&mut self,
key: &str,

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

@ -261,7 +261,9 @@ impl<'p> Painter<'p> {
line.push_str(
#[allow(clippy::unnecessary_to_owned)]
&fill_style
.paint(" ".repeat(config.available_terminal_width - text_width))
.paint(
" ".repeat(config.available_terminal_width.saturating_sub(text_width)),
)
.to_string(),
);
} else if line_is_empty {

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

@ -2137,6 +2137,31 @@ src/align.rs:71: impl<'a> Alignment<'a> { │
"###);
}
#[test] #[ignore]
fn test_long_line_underflow() {
let output = DeltaTest::with_args(&["--width" , "111", "--zero-style", "syntax #FFFFFF"]).with_input(GIT_DIFF_LONG_LINE).skip_header();
assert_snapshot!(output, @r###"
foobar
14: function UNDERFLOW_EXEC() {
XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
}
echo "=== VALIDATE UNDERFLOW"
/usr/local/bin/check_underflow
# this is an example of the underflow error
echo "=== UNDERFLOW_EXEC"
UNDERFLOW_EXEC -i foobar.yaml
"###);
}
const GIT_DIFF_SINGLE_HUNK: &str = "\
commit 94907c0f136f46dc46ffae2dc92dca9af7eb7c2e
Author: Dan Davison <dandavison7@gmail.com>
@ -3125,4 +3150,30 @@ index 53f98b6..14d6caa 100644
æäöøÆÄÖØß
";
const GIT_DIFF_LONG_LINE: &str = r#"\
commit XXXXXXXXXXXXXXXXXXXXXXXXX
Author: LUCIANO FURTADO <lrfurtado@gmail.com>
Date: Tue Jun 25 14:07:31 2024 -0500
Ref: https://github.com/dandavison/delta/issues/1760
diff --git a/foobar b/foobar
index 185a33cd5b..123f893935 100755
--- a/foobar
+++ b/foobar
@@ -14,6 +14,11 @@ function UNDERFLOW_EXEC() {
XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
}
+
+
+echo "=== VALIDATE UNDERFLOW"
+/usr/local/bin/check_underflow
+
# this is an example of the underflow error
echo "=== UNDERFLOW_EXEC"
UNDERFLOW_EXEC -i foobar.yaml
"#;
}

View File

@ -13,6 +13,7 @@ pub enum CallingProcess {
GitShow(CommandLine, Option<String>), // element 2 is filename
GitLog(CommandLine),
GitReflog(CommandLine),
GitBlame(CommandLine),
GitGrep(CommandLine),
OtherGrep, // rg, grep, ag, ack, etc
None, // no matching process could be found
@ -26,7 +27,9 @@ impl CallingProcess {
CallingProcess::GitDiff(cmd) if cmd.long_options.contains("--relative") => true,
CallingProcess::GitShow(cmd, _) if cmd.long_options.contains("--relative") => true,
CallingProcess::GitLog(cmd) if cmd.long_options.contains("--relative") => true,
CallingProcess::GitGrep(_) | CallingProcess::OtherGrep => true,
CallingProcess::GitBlame(_)
| CallingProcess::GitGrep(_)
| CallingProcess::OtherGrep => true,
_ => false,
}
}
@ -36,7 +39,7 @@ impl CallingProcess {
pub struct CommandLine {
pub long_options: HashSet<String>,
pub short_options: HashSet<String>,
last_arg: Option<String>,
pub last_arg: Option<String>,
}
lazy_static! {
@ -110,36 +113,6 @@ pub enum ProcessArgs<T> {
OtherProcess,
}
pub fn git_blame_filename() -> Option<String> {
calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename)
}
pub fn guess_git_blame_filename(args: &[String]) -> ProcessArgs<String> {
let all_args = args.iter().map(|s| s.as_str());
// See git(1) and git-blame(1). Some arguments separate their parameter with space or '=', e.g.
// --date 2015 or --date=2015.
let git_blame_options_with_parameter =
"-C -c -L --since --ignore-rev --ignore-revs-file --contents --reverse --date";
let selected_args =
skip_uninteresting_args(all_args, git_blame_options_with_parameter.split(' '));
match selected_args.as_slice() {
[git, "blame", .., last_arg] if is_git_binary(git) => {
match Path::new(last_arg)
.file_name()
.map(|filename| filename.to_string_lossy().to_string())
{
Some(filename) => ProcessArgs::Args(filename),
None => ProcessArgs::ArgError,
}
}
[git, "blame"] if is_git_binary(git) => ProcessArgs::ArgError,
_ => ProcessArgs::OtherProcess,
}
}
pub fn describe_calling_process(args: &[String]) -> ProcessArgs<CallingProcess> {
let mut args = args.iter().map(|s| s.as_str());
@ -155,7 +128,12 @@ pub fn describe_calling_process(args: &[String]) -> ProcessArgs<CallingProcess>
Some(command) => match Path::new(command).file_stem() {
Some(s) if s.to_str().map(is_git_binary).unwrap_or(false) => {
let mut args = args.skip_while(|s| {
*s != "diff" && *s != "show" && *s != "log" && *s != "reflog" && *s != "grep"
*s != "diff"
&& *s != "show"
&& *s != "log"
&& *s != "reflog"
&& *s != "grep"
&& *s != "blame"
});
match args.next() {
Some("diff") => {
@ -184,6 +162,9 @@ pub fn describe_calling_process(args: &[String]) -> ProcessArgs<CallingProcess>
Some("grep") => {
ProcessArgs::Args(CallingProcess::GitGrep(parse_command_line(args)))
}
Some("blame") => {
ProcessArgs::Args(CallingProcess::GitBlame(parse_command_line(args)))
}
_ => {
// It's git, but not a subcommand that we parse. Don't
// look at any more processes.
@ -223,49 +204,19 @@ fn is_git_binary(git: &str) -> bool {
.unwrap_or(false)
}
// Skip all arguments starting with '-' from `args_it`. Also skip all arguments listed in
// `skip_this_plus_parameter` plus their respective next argument.
// Keep all arguments once a '--' is encountered.
// (Note that some arguments work with and without '=': '--foo' 'bar' / '--foo=bar')
fn skip_uninteresting_args<'a, 'b, ArgsI, SkipI>(
mut args_it: ArgsI,
skip_this_plus_parameter: SkipI,
) -> Vec<&'a str>
where
ArgsI: Iterator<Item = &'a str>,
SkipI: Iterator<Item = &'b str>,
{
let arg_follows_space: HashSet<&'b str> = skip_this_plus_parameter.into_iter().collect();
let mut result = Vec::new();
loop {
match args_it.next() {
None => break result,
Some("--") => {
result.extend(args_it);
break result;
}
Some(arg) if arg_follows_space.contains(arg) => {
let _skip_parameter = args_it.next();
}
Some(arg) if !arg.starts_with('-') => {
result.push(arg);
}
Some(_) => { /* skip: --these -and --also=this */ }
}
}
}
// Given `--aa val -bc -d val e f -- ...` return
// ({"--aa"}, {"-b", "-c", "-d"})
fn parse_command_line<'a>(args: impl Iterator<Item = &'a str>) -> CommandLine {
let mut long_options = HashSet::new();
let mut short_options = HashSet::new();
let mut last_arg = None;
let mut after_double_dash = false;
for s in args {
if s == "--" {
break;
if after_double_dash {
last_arg = Some(s);
} else if s == "--" {
after_double_dash = true;
} else if s.starts_with("--") {
long_options.insert(s.split('=').next().unwrap().to_owned());
} else if let Some(suffix) = s.strip_prefix('-') {
@ -598,6 +549,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 +600,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,59 +647,12 @@ pub mod tests {
}
}
TlsState::Once(_) | TlsState::None => Self::error("drop"),
TlsState::Scope(_) | TlsState::Invalid => {}
TlsState::Scope(_) | TlsState::Invalid | TlsState::ErrorAlreadyHandled => {}
}
});
}
}
#[test]
fn test_guess_git_blame_filename() {
use ProcessArgs::Args;
fn make_string_vec(args: &[&str]) -> Vec<String> {
args.iter().map(|&x| x.to_owned()).collect::<Vec<String>>()
}
let args = make_string_vec(&["git", "blame", "hello", "world.txt"]);
assert_eq!(guess_git_blame_filename(&args), Args("world.txt".into()));
let args = make_string_vec(&[
"git",
"blame",
"-s",
"-f",
"hello.txt",
"--date=2015",
"--date",
"now",
]);
assert_eq!(guess_git_blame_filename(&args), Args("hello.txt".into()));
let args = make_string_vec(&["git", "blame", "-s", "-f", "--", "hello.txt"]);
assert_eq!(guess_git_blame_filename(&args), Args("hello.txt".into()));
let args = make_string_vec(&["git", "blame", "--", "--not.an.argument"]);
assert_eq!(
guess_git_blame_filename(&args),
Args("--not.an.argument".into())
);
let args = make_string_vec(&["foo", "bar", "-a", "--123", "not.git"]);
assert_eq!(guess_git_blame_filename(&args), ProcessArgs::OtherProcess);
let args = make_string_vec(&["git", "blame", "--help.txt"]);
assert_eq!(guess_git_blame_filename(&args), ProcessArgs::ArgError);
let args = make_string_vec(&["git", "-c", "a=b", "blame", "main.rs"]);
assert_eq!(guess_git_blame_filename(&args), Args("main.rs".into()));
let args = make_string_vec(&["git", "blame", "README"]);
assert_eq!(guess_git_blame_filename(&args), Args("README".into()));
let args = make_string_vec(&["git", "blame", ""]);
assert_eq!(guess_git_blame_filename(&args), ProcessArgs::ArgError);
}
#[derive(Debug, Default)]
struct FakeProc {
#[allow(dead_code)]
@ -817,46 +737,71 @@ pub mod tests {
{
let _args = FakeParentArgs::once("git blame hello");
assert_eq!(
calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename),
Some("hello".into())
calling_process_cmdline(ProcInfo::new(), describe_calling_process),
Some(CallingProcess::GitBlame(CommandLine {
long_options: [].into(),
short_options: [].into(),
last_arg: Some("hello".into())
}))
);
}
{
let _args = FakeParentArgs::once("git blame world.txt");
assert_eq!(
calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename),
Some("world.txt".into())
calling_process_cmdline(ProcInfo::new(), describe_calling_process),
Some(CallingProcess::GitBlame(CommandLine {
long_options: [].into(),
short_options: [].into(),
last_arg: Some("world.txt".into())
}))
);
}
{
let _args = FakeParentArgs::for_scope("git blame hello world.txt");
assert_eq!(
calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename),
Some("world.txt".into())
calling_process_cmdline(ProcInfo::new(), describe_calling_process),
Some(CallingProcess::GitBlame(CommandLine {
long_options: [].into(),
short_options: [].into(),
last_arg: Some("world.txt".into())
}))
);
}
}
#[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!(
calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename),
Some("do.not.panic".into())
calling_process_cmdline(ProcInfo::new(), describe_calling_process),
Some(CallingProcess::GitBlame(CommandLine {
long_options: [].into(),
short_options: [].into(),
last_arg: Some("do.not.panic".into())
}))
);
calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename);
calling_process_cmdline(ProcInfo::new(), describe_calling_process);
}
#[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]
@ -865,50 +810,70 @@ pub mod tests {
}
#[test]
fn test_process_testing_n_times_panic() {
fn test_process_testing_n_times() {
let _args = FakeParentArgs::with(&["git blame once", "git blame twice"]);
assert_eq!(
calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename),
Some("once".into())
calling_process_cmdline(ProcInfo::new(), describe_calling_process),
Some(CallingProcess::GitBlame(CommandLine {
long_options: [].into(),
short_options: [].into(),
last_arg: Some("once".into())
}))
);
assert_eq!(
calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename),
Some("twice".into())
calling_process_cmdline(ProcInfo::new(), describe_calling_process),
Some(CallingProcess::GitBlame(CommandLine {
long_options: [].into(),
short_options: [].into(),
last_arg: Some("twice".into())
}))
);
}
#[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!(
calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename),
Some("once".into())
calling_process_cmdline(ProcInfo::new(), describe_calling_process),
Some(CallingProcess::GitBlame(CommandLine {
long_options: [].into(),
short_options: [].into(),
last_arg: Some("once".into())
}))
);
}
#[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())
calling_process_cmdline(ProcInfo::new(), describe_calling_process),
Some(CallingProcess::GitBlame(CommandLine {
long_options: [].into(),
short_options: [].into(),
last_arg: Some("once".into())
}))
);
// ignored: dropping causes a panic while panicking, so can't test
calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename);
calling_process_cmdline(ProcInfo::new(), describe_calling_process);
}
#[test]
fn test_process_blame_no_parent_found() {
fn test_describe_calling_process_blame() {
let no_processes = MockProcInfo::with(&[]);
assert_eq!(
calling_process_cmdline(no_processes, describe_calling_process),
None
);
let two_trees = MockProcInfo::with(&[
(2, 100, "-shell", None),
(3, 100, "git blame src/main.rs", Some(2)),
@ -916,43 +881,119 @@ pub mod tests {
(5, 100, "delta", Some(4)),
]);
assert_eq!(
calling_process_cmdline(two_trees, guess_git_blame_filename),
None
);
}
#[test]
fn test_process_blame_info_with_parent() {
let no_processes = MockProcInfo::with(&[]);
assert_eq!(
calling_process_cmdline(no_processes, guess_git_blame_filename),
calling_process_cmdline(two_trees, describe_calling_process),
None
);
let no_options_command_line = CommandLine {
long_options: [].into(),
short_options: [].into(),
last_arg: Some("hello.txt".to_string()),
};
let parent = MockProcInfo::with(&[
(2, 100, "-shell", None),
(3, 100, "git blame hello.txt", Some(2)),
(4, 100, "delta", Some(3)),
]);
assert_eq!(
calling_process_cmdline(parent, guess_git_blame_filename),
Some("hello.txt".into())
calling_process_cmdline(parent, describe_calling_process),
Some(CallingProcess::GitBlame(no_options_command_line.clone()))
);
let parent = MockProcInfo::with(&[
(2, 100, "-shell", None),
(3, 100, "git blame -- hello.txt", Some(2)),
(4, 100, "delta", Some(3)),
]);
assert_eq!(
calling_process_cmdline(parent, describe_calling_process),
Some(CallingProcess::GitBlame(no_options_command_line.clone()))
);
let parent = MockProcInfo::with(&[
(2, 100, "-shell", None),
(3, 100, "git blame -- --not.an.argument", Some(2)),
(4, 100, "delta", Some(3)),
]);
assert_eq!(
calling_process_cmdline(parent, describe_calling_process),
Some(CallingProcess::GitBlame(CommandLine {
long_options: [].into(),
short_options: [].into(),
last_arg: Some("--not.an.argument".to_string()),
}))
);
let parent = MockProcInfo::with(&[
(2, 100, "-shell", None),
(3, 100, "git blame --help.txt", Some(2)),
(4, 100, "delta", Some(3)),
]);
assert_eq!(
calling_process_cmdline(parent, describe_calling_process),
Some(CallingProcess::GitBlame(CommandLine {
long_options: ["--help.txt".into()].into(),
short_options: [].into(),
last_arg: None,
}))
);
let parent = MockProcInfo::with(&[
(2, 100, "-shell", None),
(3, 100, "git blame --", Some(2)),
(4, 100, "delta", Some(3)),
]);
assert_eq!(
calling_process_cmdline(parent, describe_calling_process),
Some(CallingProcess::GitBlame(CommandLine {
long_options: [].into(),
short_options: [].into(),
last_arg: None,
}))
);
let parent = MockProcInfo::with(&[
(2, 100, "-shell", None),
(3, 100, "Git.exe blame hello.txt", Some(2)),
(4, 100, "delta", Some(3)),
]);
assert_eq!(
calling_process_cmdline(parent, describe_calling_process),
Some(CallingProcess::GitBlame(no_options_command_line.clone()))
);
let git_blame_command =
"git -c a=b blame -fnb --incremental -t --color-by-age -M --since=3.weeks --contents annotation.txt -C -C2 hello.txt";
// here -C2 is parsed as -C and -2. It doesn't really matters because we only use last_arg from options
// to determine the file type.
let expected_result = Some(CallingProcess::GitBlame(CommandLine {
long_options: set(&["--incremental", "--color-by-age", "--since", "--contents"]),
short_options: set(&["-f", "-n", "-b", "-t", "-M", "-C", "-2"]),
last_arg: Some("hello.txt".to_string()),
}));
let parent = MockProcInfo::with(&[
(2, 100, "-shell", None),
(3, 100, git_blame_command, Some(2)),
(4, 100, "delta", Some(3)),
]);
assert_eq!(
calling_process_cmdline(parent, describe_calling_process),
expected_result
);
let grandparent = MockProcInfo::with(&[
(2, 100, "-shell", None),
(3, 100, "git blame src/main.rs", Some(2)),
(3, 100, git_blame_command, Some(2)),
(4, 100, "call_delta.sh", Some(3)),
(5, 100, "delta", Some(4)),
]);
assert_eq!(
calling_process_cmdline(grandparent, guess_git_blame_filename),
Some("main.rs".into())
calling_process_cmdline(grandparent, describe_calling_process),
expected_result
);
}
#[test]
fn test_process_blame_info_with_sibling() {
let sibling = MockProcInfo::with(&[
(2, 100, "-xterm", None),
(3, 100, "-shell", Some(2)),
@ -960,8 +1001,12 @@ pub mod tests {
(5, 100, "delta", Some(3)),
]);
assert_eq!(
calling_process_cmdline(sibling, guess_git_blame_filename),
Some("main.rs".into())
calling_process_cmdline(sibling, describe_calling_process),
Some(CallingProcess::GitBlame(CommandLine {
long_options: [].into(),
short_options: [].into(),
last_arg: Some("src/main.rs".into())
}))
);
let indirect_sibling = MockProcInfo::with(&[
@ -978,8 +1023,12 @@ pub mod tests {
(20, 100, "delta", Some(5)),
]);
assert_eq!(
calling_process_cmdline(indirect_sibling, guess_git_blame_filename),
Some("main.abc".into())
calling_process_cmdline(indirect_sibling, describe_calling_process),
Some(CallingProcess::GitBlame(CommandLine {
long_options: set(&["--correct"]),
short_options: [].into(),
last_arg: Some("src/main.abc".into())
}))
);
let indirect_sibling2 = MockProcInfo::with(&[
@ -991,8 +1040,12 @@ pub mod tests {
(20, 100, "delta", Some(5)),
]);
assert_eq!(
calling_process_cmdline(indirect_sibling2, guess_git_blame_filename),
Some("main.def".into())
calling_process_cmdline(indirect_sibling2, describe_calling_process),
Some(CallingProcess::GitBlame(CommandLine {
long_options: [].into(),
short_options: [].into(),
last_arg: Some("src/main.def".into())
}))
);
// 3 blame processes, 2 with matching start times, pick the one with lower
@ -1010,8 +1063,12 @@ pub mod tests {
(20, 100, "delta", Some(5)),
]);
assert_eq!(
calling_process_cmdline(indirect_sibling_start_times, guess_git_blame_filename),
Some("main.this".into())
calling_process_cmdline(indirect_sibling_start_times, describe_calling_process),
Some(CallingProcess::GitBlame(CommandLine {
long_options: [].into(),
short_options: [].into(),
last_arg: Some("src/main.this".into())
}))
);
}