diff --git a/hgext/progress.py b/hgext/progress.py
--- a/hgext/progress.py
+++ b/hgext/progress.py
@@ -165,10 +165,10 @@ class progbar(object):
                 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
 
diff --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -239,17 +239,42 @@ class ui(object):
     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 @@ class ui(object):
             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 @@ class ui(object):
                      % (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