ui: add ui.write() output labeling API
authorBrodie Rao <brodie@bitheap.org>
Fri, 02 Apr 2010 15:22:00 -0500
changeset 10815 32b213b9b22c
parent 10814 cd0c49bdbfd9
child 10816 635d601e8f21
ui: add ui.write() output labeling API This adds output labeling support with the following methods: - ui.write(..., label='topic.name topic2.name2 ...') - ui.write_err(.., label=...) - ui.popbuffer(labeled=False) - ui.label(msg, label) By adding an API to label output directly, the color extension can forgo parsing command output and instead override the above methods to insert ANSI color codes. GUI tools can also override the above methods and use the labels to do GUI-specific styling. popbuffer gains a labeled argument that, when set to True, returns its buffered output with labels handled. In the case of the color extension, this would return output with color codes embedded. For existing users that use this method to capture and parse output, labels are discarded and output returned as normal when labeled is False (the default). Existing wrappers of ui.write() and ui.write_err() should make sure to accept its new **opts argument.
hgext/progress.py
mercurial/ui.py
--- a/hgext/progress.py	Thu Apr 01 17:51:59 2010 -0500
+++ b/hgext/progress.py	Fri Apr 02 15:22:00 2010 -0500
@@ -165,10 +165,10 @@
                 self.show(topic, pos, item, unit, total)
         return orig(topic, pos, item=item, unit=unit, total=total)
 
-    def write(self, orig, *args):
+    def write(self, orig, *args, **opts):
         if self.printed:
             self.clear()
-        return orig(*args)
+        return orig(*args, **opts)
 
 sharedprog = None
 
--- a/mercurial/ui.py	Thu Apr 01 17:51:59 2010 -0500
+++ b/mercurial/ui.py	Fri Apr 02 15:22:00 2010 -0500
@@ -239,17 +239,42 @@
     def pushbuffer(self):
         self._buffers.append([])
 
-    def popbuffer(self):
+    def popbuffer(self, labeled=False):
+        '''pop the last buffer and return the buffered output
+
+        If labeled is True, any labels associated with buffered
+        output will be handled. By default, this has no effect
+        on the output returned, but extensions and GUI tools may
+        handle this argument and returned styled output. If output
+        is being buffered so it can be captured and parsed or
+        processed, labeled should not be set to True.
+        '''
         return "".join(self._buffers.pop())
 
-    def write(self, *args):
+    def write(self, *args, **opts):
+        '''write args to output
+
+        By default, this method simply writes to the buffer or stdout,
+        but extensions or GUI tools may override this method,
+        write_err(), popbuffer(), and label() to style output from
+        various parts of hg.
+
+        An optional keyword argument, "label", can be passed in.
+        This should be a string containing label names separated by
+        space. Label names take the form of "topic.type". For example,
+        ui.debug() issues a label of "ui.debug".
+
+        When labeling output for a specific command, a label of
+        "cmdname.type" is recommended. For example, status issues
+        a label of "status.modified" for modified files.
+        '''
         if self._buffers:
             self._buffers[-1].extend([str(a) for a in args])
         else:
             for a in args:
                 sys.stdout.write(str(a))
 
-    def write_err(self, *args):
+    def write_err(self, *args, **opts):
         try:
             if not getattr(sys.stdout, 'closed', False):
                 sys.stdout.flush()
@@ -335,17 +360,37 @@
             return getpass.getpass(prompt or _('password: '))
         except EOFError:
             raise util.Abort(_('response expected'))
-    def status(self, *msg):
+    def status(self, *msg, **opts):
+        '''write status message to output (if ui.quiet is False)
+
+        This adds an output label of "ui.status".
+        '''
         if not self.quiet:
-            self.write(*msg)
-    def warn(self, *msg):
-        self.write_err(*msg)
-    def note(self, *msg):
+            opts['label'] = opts.get('label', '') + ' ui.status'
+            self.write(*msg, **opts)
+    def warn(self, *msg, **opts):
+        '''write warning message to output (stderr)
+
+        This adds an output label of "ui.warning".
+        '''
+        opts['label'] = opts.get('label', '') + ' ui.warning'
+        self.write_err(*msg, **opts)
+    def note(self, *msg, **opts):
+        '''write note to output (if ui.verbose is True)
+
+        This adds an output label of "ui.note".
+        '''
         if self.verbose:
-            self.write(*msg)
-    def debug(self, *msg):
+            opts['label'] = opts.get('label', '') + ' ui.note'
+            self.write(*msg, **opts)
+    def debug(self, *msg, **opts):
+        '''write debug message to output (if ui.debugflag is True)
+
+        This adds an output label of "ui.debug".
+        '''
         if self.debugflag:
-            self.write(*msg)
+            opts['label'] = opts.get('label', '') + ' ui.debug'
+            self.write(*msg, **opts)
     def edit(self, text, user):
         (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
                                       text=True)
@@ -417,3 +462,15 @@
                      % (topic, item, pos, total, unit, pct))
         else:
             self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
+
+    def label(self, msg, label):
+        '''style msg based on supplied label
+
+        Like ui.write(), this just returns msg unchanged, but extensions
+        and GUI tools can override it to allow styling output without
+        writing it.
+
+        ui.write(s, 'label') is equivalent to
+        ui.write(ui.label(s, 'label')).
+        '''
+        return msg