Skip to content

Commit

Permalink
cli: add ui.color = "debug"
Browse files Browse the repository at this point in the history
When using `ui.color = "debug"`, changes in the output style
additionally include delimiters << and >>, as well as all active labels
at this point separated by ::. The output is otherwise unformatted and
the delimiters and labels inherit the style of the content they apply
to.
  • Loading branch information
tingerrr committed May 6, 2024
1 parent 20af8c7 commit da54563
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 13 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Conflict markers now include an explanation of what each part of the conflict
represents.

* `ui.color = "debug"` prints active labels alongside the regular colored output.

### Fixed bugs

## [0.17.0] - 2024-05-01
Expand Down
2 changes: 1 addition & 1 deletion cli/src/cli_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2407,7 +2407,7 @@ pub struct GlobalArgs {

#[derive(clap::Args, Clone, Debug)]
pub struct EarlyArgs {
/// When to colorize output (always, never, auto)
/// When to colorize output (always, never, debug, auto)
#[arg(long, value_name = "WHEN", global = true)]
pub color: Option<ColorChoice>,
/// Silence non-primary command output
Expand Down
1 change: 1 addition & 0 deletions cli/src/config-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"enum": [
"always",
"never",
"debug",
"auto"
],
"default": "auto"
Expand Down
85 changes: 81 additions & 4 deletions cli/src/formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,18 +132,19 @@ pub struct FormatterFactory {
enum FormatterFactoryKind {
PlainText,
Sanitized,
Color { rules: Arc<Rules> },
Color { rules: Arc<Rules>, debug: bool },
}

impl FormatterFactory {
pub fn prepare(
config: &config::Config,
debug: bool,
color: bool,
sanitized: bool,
) -> Result<Self, config::ConfigError> {
let kind = if color {
let rules = Arc::new(rules_from_config(config)?);
FormatterFactoryKind::Color { rules }
FormatterFactoryKind::Color { rules, debug }
} else if sanitized {
FormatterFactoryKind::Sanitized
} else {
Expand All @@ -159,8 +160,12 @@ impl FormatterFactory {
match &self.kind {
FormatterFactoryKind::PlainText => Box::new(PlainTextFormatter::new(output)),
FormatterFactoryKind::Sanitized => Box::new(SanitizingFormatter::new(output)),
FormatterFactoryKind::Color { rules } => {
Box::new(ColorFormatter::new(output, rules.clone()))
FormatterFactoryKind::Color { rules, debug } => {
if *debug {
Box::new(DebugFormatter::new(output, rules.clone()))
} else {
Box::new(ColorFormatter::new(output, rules.clone()))
}
}
}
}
Expand Down Expand Up @@ -522,6 +527,58 @@ impl<W: Write> Drop for ColorFormatter<W> {
}
}

pub struct DebugFormatter<W: Write> {
formatter: ColorFormatter<W>,
}

impl<W: Write> DebugFormatter<W> {
pub fn new(output: W, rules: Arc<Rules>) -> Self {
Self {
formatter: ColorFormatter::new(output, rules),
}
}

pub fn for_config(output: W, config: &config::Config) -> Result<Self, config::ConfigError> {
let rules = rules_from_config(config)?;
Ok(Self::new(output, Arc::new(rules)))
}
}

impl<W: Write> Write for DebugFormatter<W> {
fn write(&mut self, data: &[u8]) -> Result<usize, Error> {
let labels = self.formatter.labels.clone();
write!(&mut self.formatter, "<<")?;
for (idx, label) in labels.iter().enumerate() {
if idx != 0 {
write!(&mut self.formatter, " ")?;
}
write!(&mut self.formatter, "{}", label)?;
}
write!(&mut self.formatter, "::")?;
let res = self.formatter.write(data)?;
write!(&mut self.formatter, ">>")?;
Ok(res)
}

fn flush(&mut self) -> Result<(), Error> {
self.formatter.flush()
}
}

impl<W: Write> Formatter for DebugFormatter<W> {
fn raw(&mut self) -> &mut dyn Write {
self.formatter.raw()
}

fn push_label(&mut self, label: &str) -> io::Result<()> {
self.formatter.push_label(label)
}

fn pop_label(&mut self) -> io::Result<()> {
self.formatter.pop_label()
}
}

/// Like buffered formatter, but records `push`/`pop_label()` calls.
///
/// This allows you to manipulate the recorded data without losing labels.
Expand Down Expand Up @@ -1095,6 +1152,26 @@ mod tests {
insta::assert_snapshot!(String::from_utf8(output).unwrap(), @" inside ");
}

#[test]
fn test_debug_formatter() {
// Behaves like the color formatter, but surrounds each write with <<...>>,
// adding the active labels before the actual content separated by a ::.
let config = config_from_string(
r#"
colors.outer = "green"
"#,
);
let mut output: Vec<u8> = vec![];
let mut formatter = DebugFormatter::for_config(&mut output, &config).unwrap();
formatter.push_label("outer").unwrap();
formatter.push_label("inner").unwrap();
write!(formatter, " inside ").unwrap();
formatter.pop_label().unwrap();
formatter.pop_label().unwrap();
drop(formatter);
insta::assert_snapshot!(String::from_utf8(output).unwrap(), @"<<outer inner:: inside >>");
}

#[test]
fn test_heading_labeled_writer() {
let config = config_from_string(
Expand Down
23 changes: 19 additions & 4 deletions cli/src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ impl Write for UiStderr<'_> {
}

pub struct Ui {
debug: bool,
color: bool,
quiet: bool,
pager_cmd: CommandNameAndArgs,
Expand All @@ -179,6 +180,7 @@ fn progress_indicator_setting(config: &config::Config) -> bool {
pub enum ColorChoice {
Always,
Never,
Debug,
#[default]
Auto,
}
Expand All @@ -190,6 +192,7 @@ impl FromStr for ColorChoice {
match s {
"always" => Ok(ColorChoice::Always),
"never" => Ok(ColorChoice::Never),
"debug" => Ok(ColorChoice::Debug),
"auto" => Ok(ColorChoice::Auto),
_ => Err("must be one of always, never, or auto"),
}
Expand All @@ -201,6 +204,7 @@ impl fmt::Display for ColorChoice {
let s = match self {
ColorChoice::Always => "always",
ColorChoice::Never => "never",
ColorChoice::Debug => "debug",
ColorChoice::Auto => "auto",
};
write!(f, "{s}")
Expand All @@ -215,10 +219,15 @@ fn color_setting(config: &config::Config) -> ColorChoice {
.unwrap_or_default()
}

fn debug_color(choice: ColorChoice) -> bool {
matches!(choice, ColorChoice::Debug)
}

fn use_color(choice: ColorChoice) -> bool {
match choice {
ColorChoice::Always => true,
ColorChoice::Never => false,
ColorChoice::Debug => true,
ColorChoice::Auto => io::stdout().is_terminal(),
}
}
Expand Down Expand Up @@ -249,14 +258,17 @@ fn pager_setting(config: &config::Config) -> Result<CommandNameAndArgs, CommandE

impl Ui {
pub fn with_config(config: &config::Config) -> Result<Ui, CommandError> {
let color = use_color(color_setting(config));
let color = color_setting(config);
let debug = debug_color(color);
let color = use_color(color);
let quiet = be_quiet(config);
// Sanitize ANSI escape codes if we're printing to a terminal. Doesn't affect
// ANSI escape codes that originate from the formatter itself.
let sanitize = io::stdout().is_terminal();
let formatter_factory = FormatterFactory::prepare(config, color, sanitize)?;
let formatter_factory = FormatterFactory::prepare(config, debug, color, sanitize)?;
let progress_indicator = progress_indicator_setting(config);
Ok(Ui {
debug,
color,
quiet,
formatter_factory,
Expand All @@ -268,13 +280,16 @@ impl Ui {
}

pub fn reset(&mut self, config: &config::Config) -> Result<(), CommandError> {
self.color = use_color(color_setting(config));
let color = color_setting(config);
self.debug = debug_color(color);
self.color = use_color(color);
self.quiet = be_quiet(config);
self.paginate = pagination_setting(config)?;
self.pager_cmd = pager_setting(config)?;
self.progress_indicator = progress_indicator_setting(config);
let sanitize = io::stdout().is_terminal();
self.formatter_factory = FormatterFactory::prepare(config, self.color, sanitize)?;
self.formatter_factory =
FormatterFactory::prepare(config, self.debug, self.color, sanitize)?;
Ok(())
}

Expand Down
2 changes: 1 addition & 1 deletion cli/tests/[email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ To get started, see the tutorial at https://github.com/martinvonz/jj/blob/main/d
Possible values: `true`, `false`
* `--color <WHEN>` — When to colorize output (always, never, auto)
* `--color <WHEN>` — When to colorize output (always, never, debug, auto)
* `--quiet` — Silence non-primary command output
Possible values: `true`, `false`
Expand Down
2 changes: 1 addition & 1 deletion cli/tests/test_global_opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,7 @@ fn test_help() {
--ignore-immutable Allow rewriting immutable commits
--at-operation <AT_OPERATION> Operation to load the repo at [default: @] [aliases: at-op]
--debug Enable debug logging
--color <WHEN> When to colorize output (always, never, auto)
--color <WHEN> When to colorize output (always, never, debug, auto)
--quiet Silence non-primary command output
--no-pager Disable the pager
--config-toml <TOML> Additional configuration options (can be repeated)
Expand Down
5 changes: 3 additions & 2 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,9 @@ Don't forget to change these to your own details!

### Colorizing output

Possible values are `always`, `never` and `auto` (default: `auto`).
`auto` will use color only when writing to a terminal.
Possible values are `always`, `never`, `debug` and `auto` (default: `auto`).
`auto` will use color only when writing to a terminal. `debug` will print the
active labels alongside the regular colorized output.

This setting overrides the `NO_COLOR` environment variable (if set).

Expand Down

0 comments on commit da54563

Please sign in to comment.