From e4f0ce8fc10cbd4c21d528e62c3eec8fade8ec33 Mon Sep 17 00:00:00 2001 From: Martin von Zweigbergk Date: Sat, 30 Mar 2024 10:18:56 -0700 Subject: [PATCH] cli: add a global `--quiet` flag, which silences status messages --- CHANGELOG.md | 2 ++ cli/src/cli_util.rs | 13 +++++++++++++ cli/src/formatter.rs | 26 ++++++++++++++++++++++++++ cli/src/ui.rs | 12 ++++++++++-- cli/tests/cli-reference@.md.snap | 4 ++++ cli/tests/test_global_opts.rs | 16 ++++++++++++++++ 6 files changed, 71 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a45ad9c24b3..5daf50af63c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * `jj duplicate` and `jj abandon` can now take more than a single `-r` argument, for consistency with other commands. +* There is a new global `--quiet` flag to silence commands' non-primary output. + ### Fixed bugs ## [0.15.1] - 2024-03-06 diff --git a/cli/src/cli_util.rs b/cli/src/cli_util.rs index 7752199dd99..dd5d15809eb 100644 --- a/cli/src/cli_util.rs +++ b/cli/src/cli_util.rs @@ -2122,6 +2122,16 @@ pub struct EarlyArgs { /// When to colorize output (always, never, auto) #[arg(long, value_name = "WHEN", global = true)] pub color: Option, + /// Silence non-primary command output + /// + /// For example, `jj files` will still list files, but it won't tell you if + /// the working copy was snapshotted or if descendants were rebased. + /// + /// Warnings and errors will still be printed. + #[arg(long, global = true, action = ArgAction::SetTrue)] + // Parsing with ignore_errors will crash if this is bool, so use + // Option. + pub quiet: Option, /// Disable the pager #[arg(long, value_name = "WHEN", global = true, action = ArgAction::SetTrue)] // Parsing with ignore_errors will crash if this is bool, so use @@ -2291,6 +2301,9 @@ fn handle_early_args( if let Some(choice) = args.color { args.config_toml.push(format!(r#"ui.color="{choice}""#)); } + if args.quiet.unwrap_or_default() { + args.config_toml.push(format!(r#"ui.quiet=true"#)); + } if args.no_pager.unwrap_or_default() { args.config_toml.push(r#"ui.paginate="never""#.to_owned()); } diff --git a/cli/src/formatter.rs b/cli/src/formatter.rs index 965b3d48e4a..1663e73ab90 100644 --- a/cli/src/formatter.rs +++ b/cli/src/formatter.rs @@ -166,6 +166,32 @@ impl FormatterFactory { } } +pub struct SilentFormatter; + +impl Write for SilentFormatter { + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Formatter for SilentFormatter { + fn raw(&mut self) -> &mut dyn Write { + self + } + + fn push_label(&mut self, _label: &str) -> io::Result<()> { + Ok(()) + } + + fn pop_label(&mut self) -> io::Result<()> { + Ok(()) + } +} + pub struct PlainTextFormatter { output: W, } diff --git a/cli/src/ui.rs b/cli/src/ui.rs index 14fde6b940d..cee768d85c4 100644 --- a/cli/src/ui.rs +++ b/cli/src/ui.rs @@ -23,7 +23,7 @@ use tracing::instrument; use crate::command_error::{config_error_with_message, CommandError}; use crate::config::CommandNameAndArgs; -use crate::formatter::{Formatter, FormatterFactory, HeadingLabeledWriter, LabeledWriter}; +use crate::formatter::{Formatter, FormatterFactory, HeadingLabeledWriter, LabeledWriter, SilentFormatter}; const BUILTIN_PAGER_NAME: &str = ":builtin"; @@ -163,6 +163,7 @@ impl Write for UiStderr<'_> { pub struct Ui { color: bool, + quiet: bool, pager_cmd: CommandNameAndArgs, paginate: PaginationChoice, progress_indicator: bool, @@ -245,6 +246,7 @@ fn pager_setting(config: &config::Config) -> Result Result { let color = use_color(color_setting(config)); + let quiet = config.get_bool("ui.quiet").unwrap_or_default(); // 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(); @@ -252,6 +254,7 @@ impl Ui { let progress_indicator = progress_indicator_setting(config); Ok(Ui { color, + quiet, formatter_factory, pager_cmd: pager_setting(config)?, paginate: pagination_setting(config)?, @@ -376,7 +379,12 @@ impl Ui { /// Writes a message that's a status update not part of the command's main /// output. pub fn status(&self) -> Box { - self.stderr_formatter() + if self.quiet { + Box::new(SilentFormatter) + } + else { + self.stderr_formatter() + } } /// Writer to print hint with the default "Hint: " heading. diff --git a/cli/tests/cli-reference@.md.snap b/cli/tests/cli-reference@.md.snap index dc7d797e9f3..268c4c645e4 100644 --- a/cli/tests/cli-reference@.md.snap +++ b/cli/tests/cli-reference@.md.snap @@ -159,6 +159,10 @@ To get started, see the tutorial at https://github.com/martinvonz/jj/blob/main/d Possible values: `true`, `false` * `--color ` — When to colorize output (always, never, auto) +* `--quiet` — Silence non-primary command output + + Possible values: `true`, `false` + * `--no-pager` — Disable the pager Possible values: `true`, `false` diff --git a/cli/tests/test_global_opts.rs b/cli/tests/test_global_opts.rs index 0ad86b77094..54add1989aa 100644 --- a/cli/tests/test_global_opts.rs +++ b/cli/tests/test_global_opts.rs @@ -441,6 +441,21 @@ fn test_color_ui_messages() { "###); } +#[test] +fn test_quiet() { + let test_env = TestEnvironment::default(); + test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]); + let repo_path = test_env.env_root().join("repo"); + + // Can skip message about new working copy with `--quiet` + std::fs::write(repo_path.join("file1"), "contents").unwrap(); + let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["--quiet", "describe", "-m=new description"]); + insta::assert_snapshot!(stderr, @r###" + Working copy now at: qpvuntsm 09bef04e new description + Parent commit : zzzzzzzz 00000000 (empty) (no description set) + "###); +} + #[test] fn test_early_args() { // Test that help output parses early args @@ -535,6 +550,7 @@ fn test_help() { --at-operation Operation to load the repo at [default: @] [aliases: at-op] --debug Enable debug logging --color When to colorize output (always, never, auto) + --quiet Silence non-primary command output --no-pager Disable the pager --config-toml Additional configuration options (can be repeated) "###);