rhg: Colorize `rhg status` output when appropriate
authorSimon Sapin <simon.sapin@octobus.net>
Tue, 08 Feb 2022 14:20:58 +0100
changeset 48734 3e2b4bb286e7
parent 48733 39c447e03dbc
child 48735 29eb80d190b2
rhg: Colorize `rhg status` output when appropriate Differential Revision: https://phab.mercurial-scm.org/D12168
rust/rhg/src/commands/status.rs
rust/rhg/src/main.rs
rust/rhg/src/ui.rs
tests/test-status-color.t
--- a/rust/rhg/src/commands/status.rs	Thu Feb 10 12:59:32 2022 +0100
+++ b/rust/rhg/src/commands/status.rs	Tue Feb 08 14:20:58 2022 +0100
@@ -326,25 +326,25 @@
         },
     };
     if display_states.modified {
-        output.display(b"M", ds_status.modified)?;
+        output.display(b"M ", "status.modified", ds_status.modified)?;
     }
     if display_states.added {
-        output.display(b"A", ds_status.added)?;
+        output.display(b"A ", "status.added", ds_status.added)?;
     }
     if display_states.removed {
-        output.display(b"R", ds_status.removed)?;
+        output.display(b"R ", "status.removed", ds_status.removed)?;
     }
     if display_states.deleted {
-        output.display(b"!", ds_status.deleted)?;
+        output.display(b"! ", "status.deleted", ds_status.deleted)?;
     }
     if display_states.unknown {
-        output.display(b"?", ds_status.unknown)?;
+        output.display(b"? ", "status.unknown", ds_status.unknown)?;
     }
     if display_states.ignored {
-        output.display(b"I", ds_status.ignored)?;
+        output.display(b"I ", "status.ignored", ds_status.ignored)?;
     }
     if display_states.clean {
-        output.display(b"C", ds_status.clean)?;
+        output.display(b"C ", "status.clean", ds_status.clean)?;
     }
 
     let mut dirstate_write_needed = ds_status.dirty;
@@ -448,9 +448,11 @@
     fn display(
         &self,
         status_prefix: &[u8],
+        label: &'static str,
         mut paths: Vec<StatusPath<'_>>,
     ) -> Result<(), CommandError> {
         paths.sort_unstable();
+        // TODO: get the stdout lock once for the whole loop instead of in each write
         for StatusPath { path, copy_source } in paths {
             let relative;
             let path = if let Some(relativize) = &self.relativize {
@@ -459,22 +461,20 @@
             } else {
                 path.as_bytes()
             };
-            // TODO optim, probably lots of unneeded copies here, especially
-            // if out stream is buffered
-            if self.no_status {
-                self.ui.write_stdout(&format_bytes!(b"{}\n", path))?
-            } else {
-                self.ui.write_stdout(&format_bytes!(
-                    b"{} {}\n",
-                    status_prefix,
-                    path
-                ))?
+            // TODO: Add a way to use `write_bytes!` instead of `format_bytes!`
+            // in order to stream to stdout instead of allocating an
+            // itermediate `Vec<u8>`.
+            if !self.no_status {
+                self.ui.write_stdout_labelled(status_prefix, label)?
             }
+            self.ui
+                .write_stdout_labelled(&format_bytes!(b"{}\n", path), label)?;
             if let Some(source) = copy_source {
-                self.ui.write_stdout(&format_bytes!(
-                    b"  {}\n",
-                    source.as_bytes()
-                ))?
+                let label = "status.copied";
+                self.ui.write_stdout_labelled(
+                    &format_bytes!(b"  {}\n", source.as_bytes()),
+                    label,
+                )?
             }
         }
         Ok(())
--- a/rust/rhg/src/main.rs	Thu Feb 10 12:59:32 2022 +0100
+++ b/rust/rhg/src/main.rs	Tue Feb 08 14:20:58 2022 +0100
@@ -30,7 +30,7 @@
     repo: Result<&Repo, &NoRepoInCwdError>,
     config: &Config,
 ) -> Result<(), CommandError> {
-    check_unsupported(config, repo, ui)?;
+    check_unsupported(config, repo)?;
 
     let app = App::new("rhg")
         .global_setting(AppSettings::AllowInvalidUtf8)
@@ -679,7 +679,6 @@
 fn check_unsupported(
     config: &Config,
     repo: Result<&Repo, &NoRepoInCwdError>,
-    ui: &ui::Ui,
 ) -> Result<(), CommandError> {
     check_extensions(config)?;
 
@@ -703,13 +702,5 @@
         Err(CommandError::unsupported("[decode] config"))?
     }
 
-    if let Some(color) = config.get(b"ui", b"color") {
-        if (color == b"always" || color == b"debug")
-            && !ui.plain(Some("color"))
-        {
-            Err(CommandError::unsupported("colored output"))?
-        }
-    }
-
     Ok(())
 }
--- a/rust/rhg/src/ui.rs	Thu Feb 10 12:59:32 2022 +0100
+++ b/rust/rhg/src/ui.rs	Tue Feb 08 14:20:58 2022 +0100
@@ -57,11 +57,6 @@
 
     /// Write bytes to stdout
     pub fn write_stdout(&self, bytes: &[u8]) -> Result<(), UiError> {
-        // Hack to silence "unused" warnings
-        if false {
-            return self.write_stdout_labelled(bytes, "");
-        }
-
         let mut stdout = self.stdout.lock();
 
         stdout.write_all(bytes).or_else(handle_stdout_error)?;
--- a/tests/test-status-color.t	Thu Feb 10 12:59:32 2022 +0100
+++ b/tests/test-status-color.t	Tue Feb 08 14:20:58 2022 +0100
@@ -311,9 +311,8 @@
 
   $ hg --config color.status.modified=periwinkle status
   ignoring unknown color/effect 'periwinkle' (configured in color.status.modified)
-  ignoring unknown color/effect 'periwinkle' (configured in color.status.modified)
-  ignoring unknown color/effect 'periwinkle' (configured in color.status.modified)
-  ignoring unknown color/effect 'periwinkle' (configured in color.status.modified) (rhg !)
+  ignoring unknown color/effect 'periwinkle' (configured in color.status.modified) (no-rhg !)
+  ignoring unknown color/effect 'periwinkle' (configured in color.status.modified) (no-rhg !)
   M modified
   \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
   \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mcopied\x1b[0m (esc)