diff --git a/src/cli.rs b/src/cli.rs index 544b25ceb..ad445d552 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -96,13 +96,16 @@ pub struct Opt { /// See: pub blame_timestamp_output_format: Option, - #[arg(long = "color-only")] + #[arg(long = "color-only", overrides_with = "no_color_only")] /// Do not alter the input structurally in any way. /// /// But color and highlight hunk lines according to your delta configuration. This is mainly /// intended for other tools that use delta. pub color_only: bool, + #[arg(long = "no-color-only", overrides_with = "color_only", hide = true)] + pub no_color_only: bool, + #[arg(long = "config", default_value = "", value_name = "PATH", value_hint = ValueHint::FilePath)] /// Load the config file at PATH instead of ~/.gitconfig. pub config: String, @@ -184,18 +187,32 @@ pub struct Opt { /// doesn't support it, then delta will fall back to `diff` instead of `git diff`. pub diff_args: String, - #[arg(long = "diff-highlight")] + #[arg(long = "diff-highlight", overrides_with = "no_diff_highlight")] /// Emulate diff-highlight. /// /// pub diff_highlight: bool, - #[arg(long = "diff-so-fancy")] + #[arg( + long = "no-diff-highlight", + overrides_with = "diff_highlight", + hide = true + )] + pub no_diff_highlight: bool, + + #[arg(long = "diff-so-fancy", overrides_with = "no_diff_so_fancy")] /// Emulate diff-so-fancy. /// /// pub diff_so_fancy: bool, + #[arg( + long = "no-diff-so-fancy", + overrides_with = "diff_so_fancy", + hide = true + )] + pub no_diff_so_fancy: bool, + #[arg(long = "diff-stat-align-width", default_value = "48", value_name = "N")] /// Width allocated for file paths in a diff stat section. /// @@ -406,7 +423,7 @@ pub struct Opt { /// Used in the default value of navigate-regex. pub hunk_label: String, - #[arg(long = "hyperlinks")] + #[arg(long = "hyperlinks", overrides_with = "no_hyperlinks")] /// Render commit hashes, file names, and line numbers as hyperlinks. /// /// Following the hyperlink spec for terminal emulators: @@ -420,6 +437,9 @@ pub struct Opt { /// ). pub hyperlinks: bool, + #[arg(long = "no-hyperlinks", overrides_with = "hyperlinks", hide = true)] + pub no_hyperlinks: bool, + #[arg(long = "hyperlinks-commit-link-format", value_name = "FMT")] /// Format string for commit hyperlinks (requires --hyperlinks). /// @@ -466,13 +486,23 @@ pub struct Opt { /// Git's --color-moved feature. Set this to "false" to disable this behavior. pub inspect_raw_lines: String, - #[arg(long = "keep-plus-minus-markers")] + #[arg( + long = "keep-plus-minus-markers", + overrides_with = "no_keep_plus_minus_markers" + )] /// Prefix added/removed lines with a +/- character, as git does. /// /// By default, delta does not emit any prefix, so code can be copied directly from delta's /// output. pub keep_plus_minus_markers: bool, + #[arg( + long = "no-keep-plus-minus-markers", + overrides_with = "keep_plus_minus_markers", + hide = true + )] + pub no_keep_plus_minus_markers: bool, + #[arg(long = "light")] /// Use default colors appropriate for a light terminal background. /// @@ -497,12 +527,15 @@ pub struct Opt { /// --width=variable is given. pub line_fill_method: Option, - #[arg(short = 'n', long = "line-numbers")] + #[arg(short = 'n', long = "line-numbers", overrides_with = "no_line_numbers")] /// Display line numbers next to the diff. /// /// See LINE NUMBERS section. pub line_numbers: bool, + #[arg(long = "no-line-numbers", overrides_with = "line_numbers", hide = true)] + pub no_line_numbers: bool, + #[arg( long = "line-numbers-left-format", default_value = "{nm:^4}⋮", @@ -722,13 +755,16 @@ pub struct Opt { /// See STYLES section. pub minus_style: String, - #[arg(long = "navigate")] + #[arg(long = "navigate", overrides_with = "no_navigate")] /// Activate diff navigation. /// /// Use n to jump forwards and N to jump backwards. To change the file labels used see /// --file-added-label, --file-copied-label, --file-modified-label, --file-removed-label, --file-renamed-label. pub navigate: bool, + #[arg(long = "no-navigate", overrides_with = "navigate", hide = true)] + pub no_navigate: bool, + #[arg(long = "navigate-regex", value_name = "REGEX")] /// Regular expression defining navigation stop points. pub navigate_regex: Option, @@ -805,18 +841,28 @@ pub struct Opt { /// See STYLES section. pub plus_style: String, - #[arg(long = "raw")] + #[arg(long = "raw", overrides_with = "no_raw")] /// Do not alter the input in any way. /// /// This is mainly intended for testing delta. pub raw: bool, - #[arg(long = "relative-paths")] + #[arg(long = "no-raw", overrides_with = "raw", hide = true)] + pub no_raw: bool, + + #[arg(long = "relative-paths", overrides_with = "no_relative_paths")] /// Output all file paths relative to the current directory. /// /// This means that they will resolve correctly when clicked on or used in shell commands. pub relative_paths: bool, + #[arg( + long = "no-relative-paths", + overrides_with = "relative_paths", + hide = true + )] + pub no_relative_paths: bool, + #[arg(long = "right-arrow", default_value = "⟶ ", value_name = "STRING")] /// Text to display with a changed file path. /// @@ -856,10 +902,13 @@ pub struct Opt { /// shown, use --dark or --light, or both, on the command line together with this option. pub show_themes: bool, - #[arg(short = 's', long = "side-by-side")] + #[arg(short = 's', long = "side-by-side", overrides_with = "no_side_by_side")] /// Display diffs in side-by-side layout. pub side_by_side: bool, + #[arg(long = "no-side-by-side", overrides_with = "side_by_side", hide = true)] + pub no_side_by_side: bool, + #[arg(long = "syntax-theme", value_name = "SYNTAX_THEME")] /// The syntax-highlighting theme to use. /// diff --git a/src/handlers/blame.rs b/src/handlers/blame.rs index b09ba100c..e018a6e20 100644 --- a/src/handlers/blame.rs +++ b/src/handlers/blame.rs @@ -313,8 +313,9 @@ pub fn format_blame_line_number( let mut result = String::new(); // depends on defaults being set when parsing arguments - let line_number = if let Some(width) = format.width { - format::pad(line_number, width, format.alignment_spec.unwrap(), None) + let line_number = if let (Some(width), Some(alignment)) = (format.width, format.alignment_spec) + { + format::pad(line_number, width, alignment, None) } else { String::new() }; diff --git a/src/options/set.rs b/src/options/set.rs index 6d26d15df..40f770fb1 100644 --- a/src/options/set.rs +++ b/src/options/set.rs @@ -50,6 +50,17 @@ macro_rules! set_options { "dark", "light", "syntax-theme", + // --no-* negation flags (handled in apply_no_flag_overrides) + "no-color-only", + "no-diff-highlight", + "no-diff-so-fancy", + "no-hyperlinks", + "no-keep-plus-minus-markers", + "no-line-numbers", + "no-navigate", + "no-raw", + "no-relative-paths", + "no-side-by-side", ]); let expected_option_names: HashSet<_> = $expected_option_name_map .values() @@ -234,6 +245,10 @@ pub fn set_options( true ); + // Apply --no-* flag overrides. When a --no-* flag is user-supplied, it forces + // the corresponding option to false, overriding any gitconfig value. + apply_no_flag_overrides(opt, arg_matches); + // Setting ComputedValues set_widths_and_isatty(opt); set_true_color(opt); @@ -253,6 +268,26 @@ pub fn set_options( } } +fn apply_no_flag_overrides(opt: &mut cli::Opt, arg_matches: &clap::ArgMatches) { + macro_rules! apply_no_flag { + ($no_field:ident, $field:ident) => { + if config::user_supplied_option(stringify!($no_field), arg_matches) { + opt.$field = false; + } + }; + } + apply_no_flag!(no_color_only, color_only); + apply_no_flag!(no_diff_highlight, diff_highlight); + apply_no_flag!(no_diff_so_fancy, diff_so_fancy); + apply_no_flag!(no_hyperlinks, hyperlinks); + apply_no_flag!(no_keep_plus_minus_markers, keep_plus_minus_markers); + apply_no_flag!(no_line_numbers, line_numbers); + apply_no_flag!(no_navigate, navigate); + apply_no_flag!(no_raw, raw); + apply_no_flag!(no_relative_paths, relative_paths); + apply_no_flag!(no_side_by_side, side_by_side); +} + #[allow(non_snake_case)] fn set__light__dark__syntax_theme__options( opt: &mut cli::Opt, @@ -373,28 +408,28 @@ fn gather_features( // Gather builtin feature flags supplied on command line. // TODO: Iterate over programmatically-obtained names of builtin features. - if opt.raw { + if opt.raw && !opt.no_raw { gather_builtin_features_recursively("raw", &mut features, builtin_features, opt); } - if opt.color_only { + if opt.color_only && !opt.no_color_only { gather_builtin_features_recursively("color-only", &mut features, builtin_features, opt); } - if opt.diff_highlight { + if opt.diff_highlight && !opt.no_diff_highlight { gather_builtin_features_recursively("diff-highlight", &mut features, builtin_features, opt); } - if opt.diff_so_fancy { + if opt.diff_so_fancy && !opt.no_diff_so_fancy { gather_builtin_features_recursively("diff-so-fancy", &mut features, builtin_features, opt); } - if opt.hyperlinks { + if opt.hyperlinks && !opt.no_hyperlinks { gather_builtin_features_recursively("hyperlinks", &mut features, builtin_features, opt); } - if opt.line_numbers { + if opt.line_numbers && !opt.no_line_numbers { gather_builtin_features_recursively("line-numbers", &mut features, builtin_features, opt); } - if opt.navigate { + if opt.navigate && !opt.no_navigate { gather_builtin_features_recursively("navigate", &mut features, builtin_features, opt); } - if opt.side_by_side { + if opt.side_by_side && !opt.no_side_by_side { gather_builtin_features_recursively("side-by-side", &mut features, builtin_features, opt); } @@ -806,6 +841,64 @@ pub mod tests { remove_file(git_config_path).unwrap(); } + #[test] + fn test_no_flags_override_gitconfig() { + let git_config_contents = b" +[delta] + side-by-side = true + line-numbers = true + navigate = true + hyperlinks = true + keep-plus-minus-markers = true + raw = true + relative-paths = true +"; + let git_config_path = "delta__test_no_flags_override_gitconfig.gitconfig"; + + let opt = integration_test_utils::make_options_from_args_and_git_config( + &[ + "--no-side-by-side", + "--no-line-numbers", + "--no-navigate", + "--no-hyperlinks", + "--no-keep-plus-minus-markers", + "--no-raw", + "--no-relative-paths", + ], + Some(git_config_contents), + Some(git_config_path), + ); + + assert!(!opt.side_by_side); + assert!(!opt.line_numbers); + assert!(!opt.navigate); + assert!(!opt.hyperlinks); + assert!(!opt.keep_plus_minus_markers); + assert!(!opt.raw); + assert!(!opt.relative_paths); + + remove_file(git_config_path).unwrap(); + } + + #[test] + fn test_no_flags_last_flag_wins() { + // --no-side-by-side followed by --side-by-side should enable it + let opt = integration_test_utils::make_options_from_args_and_git_config( + &["--no-side-by-side", "--side-by-side"], + None, + None, + ); + assert!(opt.side_by_side); + + // --side-by-side followed by --no-side-by-side should disable it + let opt = integration_test_utils::make_options_from_args_and_git_config( + &["--side-by-side", "--no-side-by-side"], + None, + None, + ); + assert!(!opt.side_by_side); + } + #[test] fn test_width_in_git_config_is_honored() { let git_config_contents = b"