From 4ef255815ede838419d6cbf83867fdd0dc8f9a10 Mon Sep 17 00:00:00 2001 From: Thomas Otto Date: Mon, 5 Jun 2023 22:21:07 +0200 Subject: [PATCH] wrap --help output Set clap option `max_term_width`, so the help output is by default as wide as the terminal or this max value. Then manually wrap the `after_long_help()` text (only on demand) to the same width using `textwrap`. Also use matching ansi codes in this section. --- Cargo.lock | 16 +++ Cargo.toml | 1 + src/ansi/mod.rs | 2 + src/cli.rs | 372 +++++++++++++++++++++++++++--------------------- 4 files changed, 231 insertions(+), 160 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 90c7af888..d52512024 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -584,6 +584,7 @@ dependencies = [ "smol_str", "syntect", "sysinfo", + "textwrap", "unicode-segmentation", "unicode-width", "vte", @@ -1245,6 +1246,12 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" +[[package]] +name = "smawk" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" + [[package]] name = "smol_str" version = "0.1.24" @@ -1350,6 +1357,15 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "textwrap" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" +dependencies = [ + "smawk", +] + [[package]] name = "thiserror" version = "1.0.38" diff --git a/Cargo.toml b/Cargo.toml index a5ea944d0..8d0d9b23e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,7 @@ unicode-segmentation = "1.10.1" unicode-width = "0.1.10" vte = "0.11.0" xdg = "2.4.1" +textwrap = { version = "0.16.0", default-features = false, features = ["smawk"] } [dependencies.git2] version = "0.17.2" diff --git a/src/ansi/mod.rs b/src/ansi/mod.rs index a0f7f9733..bbb2867ba 100644 --- a/src/ansi/mod.rs +++ b/src/ansi/mod.rs @@ -12,8 +12,10 @@ use iterator::{AnsiElementIterator, Element}; pub const ANSI_CSI_CLEAR_TO_EOL: &str = "\x1b[0K"; pub const ANSI_CSI_CLEAR_TO_BOL: &str = "\x1b[1K"; +pub const ANSI_SGR_BOLD: &str = "\x1b[1m"; pub const ANSI_SGR_RESET: &str = "\x1b[0m"; pub const ANSI_SGR_REVERSE: &str = "\x1b[7m"; +pub const ANSI_SGR_UNDERLINE: &str = "\x1b[4m"; pub fn strip_ansi_codes(s: &str) -> String { strip_ansi_codes_from_strings_iterator(ansi_strings_iterator(s)) diff --git a/src/cli.rs b/src/cli.rs index e1e47503d..abe7dda53 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -3,11 +3,13 @@ use std::ffi::OsString; use std::path::{Path, PathBuf}; use bat::assets::HighlightingAssets; -use clap::{ColorChoice, CommandFactory, FromArgMatches, Parser}; +use clap::{ArgMatches, ColorChoice, CommandFactory, FromArgMatches, Parser}; +use console::Term; use lazy_static::lazy_static; use syntect::highlighting::Theme as SyntaxTheme; use syntect::parsing::SyntaxSet; +use crate::ansi::{ANSI_SGR_BOLD, ANSI_SGR_RESET, ANSI_SGR_UNDERLINE}; use crate::config::delta_unreachable; use crate::env::DeltaEnv; use crate::git_config::GitConfig; @@ -15,170 +17,15 @@ use crate::options; use crate::utils; use crate::utils::bat::output::PagingMode; +const TERM_FALLBACK_WIDTH: usize = 99; + #[derive(Parser)] #[command( name = "delta", about = "A viewer for git and diff output", version, color = ColorChoice::Always, - term_width(0), - after_long_help = "\ -GIT CONFIG ----------- - -By default, delta takes settings from a section named \"delta\" in git config files, if one is present. The git config file to use for delta options will usually be ~/.gitconfig, but delta follows the rules given in https://git-scm.com/docs/git-config#FILES. Most delta options can be given in a git config file, using the usual option names but without the initial '--'. An example is - -[delta] - line-numbers = true - zero-style = dim syntax - -FEATURES --------- -A feature is a named collection of delta options in git config. An example is: - -[delta \"my-delta-feature\"] - syntax-theme = Dracula - plus-style = bold syntax \"#002800\" - -To activate those options, you would use: - -delta --features my-delta-feature - -A feature name may not contain whitespace. You can activate multiple features: - -[delta] - features = my-highlight-styles-colors-feature my-line-number-styles-feature - -If more than one feature sets the same option, the last one wins. - -If an option is present in the [delta] section, then features are not considered at all. - -If you want an option to be fully overridable by a feature and also have a non default value when no features are used, then you need to define a \"default\" feature and include it in the main delta configuration. - -For instance: - -[delta] -feature = default-feature - -[delta \"default-feature\"] -width = 123 - -At this point, you can override features set in the command line or in the environment variables and the \"last one wins\" rules will apply as expected. - -STYLES ------- - -All options that have a name like --*-style work the same way. It is very similar to how colors/styles are specified in a gitconfig file: https://git-scm.com/docs/git-config#Documentation/git-config.txt-color - -Here is an example: - ---minus-style 'red bold ul \"#ffeeee\"' - -That means: For removed lines, set the foreground (text) color to 'red', make it bold and underlined, and set the background color to '#ffeeee'. - -See the COLORS section below for how to specify a color. In addition to real colors, there are 4 special color names: 'auto', 'normal', 'raw', and 'syntax'. - -Here is an example of using special color names together with a single attribute: - ---minus-style 'syntax bold auto' - -That means: For removed lines, syntax-highlight the text, and make it bold, and do whatever delta normally does for the background. - -The available attributes are: 'blink', 'bold', 'dim', 'hidden', 'italic', 'reverse', 'strike', and 'ul' (or 'underline'). - -The attribute 'omit' is supported by commit-style, file-style, and hunk-header-style, meaning to remove the element entirely from the output. - -A complete description of the style string syntax follows: - -- If the input that delta is receiving already has colors, and you want delta to output those colors unchanged, then use the special style string 'raw'. Otherwise, delta will strip any colors from its input. - -- A style string consists of 0, 1, or 2 colors, together with an arbitrary number of style attributes, all separated by spaces. - -- The first color is the foreground (text) color. The second color is the background color. Attributes can go in any position. - -- This means that in order to specify a background color you must also specify a foreground (text) color. - -- If you want delta to choose one of the colors automatically, then use the special color 'auto'. This can be used for both foreground and background. - -- If you want the foreground/background color to be your terminal's foreground/background color, then use the special color 'normal'. - -- If you want the foreground text to be syntax-highlighted according to its language, then use the special foreground color 'syntax'. This can only be used for the foreground (text). - -- The minimal style specification is the empty string ''. This means: do not apply any colors or styling to the element in question. - -COLORS ------- - -There are four ways to specify a color (this section applies to foreground and background colors within a style string): - -1. CSS color name - - Any of the 140 color names used in CSS: https://www.w3schools.com/colors/colors_groups.asp - -2. RGB hex code - - An example of using an RGB hex code is: - --file-style=\"#0e7c0e\" - -3. ANSI color name - - There are 8 ANSI color names: - black, red, green, yellow, blue, magenta, cyan, white. - - In addition, all of them have a bright form: - brightblack, brightred, brightgreen, brightyellow, brightblue, brightmagenta, brightcyan, brightwhite. - - An example of using an ANSI color name is: - --file-style=\"green\" - - Unlike RGB hex codes, ANSI color names are just names: you can choose the exact color that each name corresponds to in the settings of your terminal application (the application you use to enter commands at a shell prompt). This means that if you use ANSI color names, and you change the color theme used by your terminal, then delta's colors will respond automatically, without needing to change the delta command line. - - \"purple\" is accepted as a synonym for \"magenta\". Color names and codes are case-insensitive. - -4. ANSI color number - - An example of using an ANSI color number is: - --file-style=28 - - There are 256 ANSI color numbers: 0-255. The first 16 are the same as the colors described in the \"ANSI color name\" section above. See https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit. Specifying colors like this is useful if your terminal only supports 256 colors (i.e. doesn\'t support 24-bit color). - - -LINE NUMBERS ------------- - -To display line numbers, use --line-numbers. - -Line numbers are displayed in two columns. Here's what it looks like by default: - - 1 ⋮ 1 │ unchanged line - 2 ⋮ │ removed line - ⋮ 2 │ added line - -In that output, the line numbers for the old (minus) version of the file appear in the left column, and the line numbers for the new (plus) version of the file appear in the right column. In an unchanged (zero) line, both columns contain a line number. - -The following options allow the line number display to be customized: - ---line-numbers-left-format: Change the contents of the left column ---line-numbers-right-format: Change the contents of the right column ---line-numbers-left-style: Change the style applied to the left column ---line-numbers-right-style: Change the style applied to the right column ---line-numbers-minus-style: Change the style applied to line numbers in minus lines ---line-numbers-zero-style: Change the style applied to line numbers in unchanged lines ---line-numbers-plus-style: Change the style applied to line numbers in plus lines - -Options --line-numbers-left-format and --line-numbers-right-format allow you to change the contents of the line number columns. Their values are arbitrary format strings, which are allowed to contain the placeholders {nm} for the line number associated with the old version of the file and {np} for the line number associated with the new version of the file. The placeholders support a subset of the string formatting syntax documented here: https://doc.rust-lang.org/std/fmt/#formatting-parameters. Specifically, you can use the alignment and width syntax. - -For example, the default value of --line-numbers-left-format is '{nm:^4}⋮'. This means that the left column should display the minus line number (nm), center-aligned, padded with spaces to a width of 4 characters, followed by a unicode dividing-line character (⋮). - -Similarly, the default value of --line-numbers-right-format is '{np:^4}│'. This means that the right column should display the plus line number (np), center-aligned, padded with spaces to a width of 4 characters, followed by a unicode dividing-line character (│). - -Use '<' for left-align, '^' for center-align, and '>' for right-align. - - -If something isn't working correctly, or you have a feature request, please open an issue at https://github.com/dandavison/delta/issues. - -For a short help summary, please use delta -h. -" + max_term_width(TERM_FALLBACK_WIDTH), )] pub struct Opt { #[arg(long = "blame-code-style", value_name = "STYLE")] @@ -1117,13 +964,40 @@ pub enum InspectRawLines { } impl Opt { + fn handle_help_and_version(args: &[OsString]) -> ArgMatches { + match Self::command().try_get_matches_from(args) { + Err(ref e) if e.kind() == clap::error::ErrorKind::DisplayVersion => { + let version = Self::command().render_version(); + println!("{version}"); + std::process::exit(0); + } + Err(ref e) if e.kind() == clap::error::ErrorKind::DisplayHelp => { + let help = if args.iter().any(|arg| arg == "-h") { + Self::command().render_help() + } else { + // generate long help on demand: + Self::command() + .after_long_help(get_after_long_help()) + .render_long_help() + }; + print!("{}", help.ansi()); + std::process::exit(0); + } + Err(e) => { + e.exit(); + } + Ok(matches) => matches, + } + } pub fn from_args_and_git_config( env: DeltaEnv, git_config: Option, assets: HighlightingAssets, ) -> Self { + let args = std::env::args_os().collect::>(); + + let matches = Self::handle_help_and_version(&args); let mut final_config = git_config; - let matches = Self::command().get_matches(); if let Some(path) = matches.get_one::("config") { if !path.is_empty() { @@ -1202,3 +1076,181 @@ lazy_static! { .into_iter() .collect(); } + +#[allow(non_snake_case)] +fn get_after_long_help() -> String { + let term_stdout = Term::stdout(); + let term_width = if term_stdout.is_term() { + crate::utils::workarounds::windows_msys2_width_fix(term_stdout.size(), &term_stdout) + } else { + TERM_FALLBACK_WIDTH + }; + + let wrap = + textwrap::Options::new(term_width).word_separator(textwrap::WordSeparator::AsciiSpace).word_splitter(textwrap::WordSplitter::NoHyphenation); + + // header + let H_ = format!("{ANSI_SGR_BOLD}{ANSI_SGR_UNDERLINE}"); + let _H = ANSI_SGR_RESET; + + let after_long_help = format!( + r##" +{H_}Git config{_H} + +By default, delta takes settings from a section named "delta" in git config files, if one is present. The git config file to use for delta options will usually be ~/.gitconfig, but delta follows the rules given in https://git-scm.com/docs/git-config#FILES. Most delta options can be given in a git config file, using the usual option names but without the initial '--'. An example is + +[delta] + line-numbers = true + zero-style = dim syntax + + +{H_}Features{_H} + +A feature is a named collection of delta options in git config. An example is: + +[delta "my-delta-feature"] + syntax-theme = Dracula + plus-style = bold syntax "#002800" + +To activate those options, you would use: + +delta --features my-delta-feature + +A feature name may not contain whitespace. You can activate multiple features: + +[delta] + features = my-highlight-styles-colors-feature my-line-number-styles-feature + +If more than one feature sets the same option, the last one wins. + +If an option is present in the [delta] section, then features are not considered at all. + +If you want an option to be fully overridable by a feature and also have a non default value when no features are used, then you need to define a "default" feature and include it in the main delta configuration. + +For instance: + +[delta] + feature = default-feature + +[delta "default-feature"] + width = 123 + +At this point, you can override features set in the command line or in the environment variables and the "last one wins" rules will apply as expected. + + +{H_}Styles{_H} + +All options that have a name like --*-style work the same way. It is very similar to how colors/styles are specified in a gitconfig file: https://git-scm.com/docs/git-config#Documentation/git-config.txt-color + +Here is an example: + +--minus-style 'red bold ul "#ffeeee"' + +That means: For removed lines, set the foreground (text) color to 'red', make it bold and underlined, and set the background color to '#ffeeee'. + +See the COLORS section below for how to specify a color. In addition to real colors, there are 4 special color names: 'auto', 'normal', 'raw', and 'syntax'. + +Here is an example of using special color names together with a single attribute: + +--minus-style 'syntax bold auto' + +That means: For removed lines, syntax-highlight the text, and make it bold, and do whatever delta normally does for the background. + +The available attributes are: 'blink', 'bold', 'dim', 'hidden', 'italic', 'reverse', 'strike', and 'ul' (or 'underline'). + +The attribute 'omit' is supported by commit-style, file-style, and hunk-header-style, meaning to remove the element entirely from the output. + +A complete description of the style string syntax follows: + +- If the input that delta is receiving already has colors, and you want delta to output those colors unchanged, then use the special style string 'raw'. Otherwise, delta will strip any colors from its input. + +- A style string consists of 0, 1, or 2 colors, together with an arbitrary number of style attributes, all separated by spaces. + +- The first color is the foreground (text) color. The second color is the background color. Attributes can go in any position. + +- This means that in order to specify a background color you must also specify a foreground (text) color. + +- If you want delta to choose one of the colors automatically, then use the special color 'auto'. This can be used for both foreground and background. + +- If you want the foreground/background color to be your terminal's foreground/background color, then use the special color 'normal'. + +- If you want the foreground text to be syntax-highlighted according to its language, then use the special foreground color 'syntax'. This can only be used for the foreground (text). + +- The minimal style specification is the empty string ''. This means: do not apply any colors or styling to the element in question. + + +{H_}Colors{_H} + +There are four ways to specify a color (this section applies to foreground and background colors within a style string): + +1. CSS color name + + Any of the 140 color names used in CSS: https://www.w3schools.com/colors/colors_groups.asp + +2. RGB hex code + + An example of using an RGB hex code is: + --file-style="#0e7c0e" + +3. ANSI color name + + There are 8 ANSI color names: + black, red, green, yellow, blue, magenta, cyan, white. + + In addition, all of them have a bright form: + brightblack, brightred, brightgreen, brightyellow, brightblue, brightmagenta, brightcyan, brightwhite. + + An example of using an ANSI color name is: + --file-style="green" + + Unlike RGB hex codes, ANSI color names are just names: you can choose the exact color that each name corresponds to in the settings of your terminal application (the application you use to enter commands at a shell prompt). This means that if you use ANSI color names, and you change the color theme used by your terminal, then delta's colors will respond automatically, without needing to change the delta command line. + + "purple" is accepted as a synonym for "magenta". Color names and codes are case-insensitive. + +4. ANSI color number + + An example of using an ANSI color number is: + --file-style=28 + + There are 256 ANSI color numbers: 0-255. The first 16 are the same as the colors described in the "ANSI color name" section above. See https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit. Specifying colors like this is useful if your terminal only supports 256 colors (i.e. doesn't support 24-bit color). + + +{H_}Line Numbers{_H} + +To display line numbers, use --line-numbers. + +Line numbers are displayed in two columns. Here's what it looks like by default: + + 1 ⋮ 1 │ unchanged line + 2 ⋮ │ removed line + ⋮ 2 │ added line + +In that output, the line numbers for the old (minus) version of the file appear in the left column, and the line numbers for the new (plus) version of the file appear in the right column. In an unchanged (zero) line, both columns contain a line number. + +The following options allow the line number display to be customized: + +--line-numbers-left-format: Change the contents of the left column +--line-numbers-right-format: Change the contents of the right column +--line-numbers-left-style: Change the style applied to the left column +--line-numbers-right-style: Change the style applied to the right column +--line-numbers-minus-style: Change the style applied to line numbers in minus lines +--line-numbers-zero-style: Change the style applied to line numbers in unchanged lines +--line-numbers-plus-style: Change the style applied to line numbers in plus lines + +Options --line-numbers-left-format and --line-numbers-right-format allow you to change the contents of the line number columns. Their values are arbitrary format strings, which are allowed to contain the placeholders {{nm}} for the line number associated with the old version of the file and {{np}} for the line number associated with the new version of the file. The placeholders support a subset of the string formatting syntax documented here: https://doc.rust-lang.org/std/fmt/#formatting-parameters. Specifically, you can use the alignment and width syntax. + +For example, the default value of --line-numbers-left-format is '{{nm:^4}}⋮'. This means that the left column should display the minus line number (nm), center-aligned, padded with spaces to a width of 4 characters, followed by a unicode dividing-line character (⋮). + +Similarly, the default value of --line-numbers-right-format is '{{np:^4}}│'. This means that the right column should display the plus line number (np), center-aligned, padded with spaces to a width of 4 characters, followed by a unicode dividing-line character (│). + +Use '<' for left-align, '^' for center-align, and '>' for right-align. + + +If something isn't working correctly, or you have a feature request, please open an issue at https://github.com/dandavison/delta/issues. + +For a short help summary, please use delta -h. +"## + ); + + textwrap::wrap(&after_long_help, wrap).join("\n") +}