[PATCH] Color command output using ui buffers

kevin.christen at gmail.com kevin.christen at gmail.com
Sat Dec 29 17:21:27 CST 2007


# HG changeset patch
# User Kevin Christen <kevin.christen at gmail.com>
# Date 1198945152 21600
# Node ID 7ebe8ea4d38e57bb27821ef5bd389662f20196f3
# Parent  bc475d1f74caf65df21b267b5b9ab58770c3a903
Color command output using ui buffers

diff -r bc475d1f74ca -r 7ebe8ea4d38e hgext/color.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/color.py	Sat Dec 29 10:19:12 2007 -0600
@@ -0,0 +1,196 @@
+# color.py color output for the status and qseries commands
+#
+# Copyright (C) 2007 Kevin Christen <kevin.christen at gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program.  If not, see <http://www.gnu.org/licenses/>.  This
+# software may be used and distributed according to the terms of the GNU
+# General Public License, incorporated herein by reference.
+
+'''add color output to the status and qseries commands
+
+This extension provides the cstatus command, which adds color to the output
+of the status command to reflect file status, and the cqseries command,
+which adds color to the output of the qseries command to reflect patch
+status (applied, unapplied, missing).  Other effects in addition to color
+are availble, like bold and underlined text.  Effects are rendered with the
+ECMA-48 SGR control function (aka ANSI escape codes).  This module also
+provides the render_text function, which can be used to add effects to any
+text.
+
+To enable this extension, add this to your .hgrc file:
+[extensions]
+color =
+
+Default effects my be overriden from the .hgrc file:
+
+[color]
+cstatus.modified = blue bold underline red_background
+cstatus.added = green bold
+cstatus.removed = red bold blue_background
+cstatus.deleted = cyan bold underline
+cstatus.unknown = magenta bold underline
+cstatus.ignored = black bold
+
+ 'none' turns off all effects
+cstatus.clean = none
+cstatus.copied = none
+
+cqseries.applied = blue bold underline
+cqseries.unapplied = black bold
+cqseries.missing = red bold
+'''
+
+import re, sys
+
+from mercurial import commands, cmdutil, ui
+from mercurial.i18n import _
+from hgext import mq
+
+_effect_commands = { 'none': (0, 0),
+                     'black': (30, 39),
+                     'red': (31, 39),
+                     'green': (32, 39),
+                     'yellow': (33, 39),
+                     'blue': (34, 39),
+                     'magenta': (35, 39),
+                     'cyan': (36, 39),
+                     'white': (37, 39),
+                     'bold': (1, 22),
+                     'italic': (3, 23),
+                     'underline': (4, 24),
+                     'inverse': (7, 27),
+                     'black_background': (40, 49),
+                     'red_background': (41, 49),
+                     'green_background': (42, 49),
+                     'yellow_background': (43, 49),
+                     'blue_background': (44, 49),
+                     'purple_background': (45, 49),
+                     'cyan_background': (46, 49),
+                     'white_background': (47, 49), }
+
+def render_effects(text, *effects):
+    'Wrap text in commands to turn on each effect.'
+    start = []
+    stop = []
+    for effect in effects:
+        start.append(str(_effect_commands[effect][0]))
+        stop.append(str(_effect_commands[effect][1]))
+    start = '\033[' + ';'.join(start) + 'm'
+    stop = '\033[' + ';'.join(stop) + 'm'
+    return start + text + stop
+
+_status_abbreviations = { 'M': 'modified',
+                          'A': 'added',
+                          'R': 'removed',
+                          'D': 'deleted',
+                          '?': 'unknown',
+                          'I': 'ignored',
+                          'C': 'clean',
+                          ' ': 'copied', }
+
+_status_effects = { 'modified': ('blue', 'bold'),
+                    'added': ('green', 'bold'),
+                    'removed': ('red', 'bold'),
+                    'deleted': ('cyan', 'bold', 'underline'),
+                    'unknown': ('magenta', 'bold', 'underline'),
+                    'ignored': ('black', 'bold'),
+                    'clean': ('none', ),
+                    'copied': ('none', ), }
+
+def colorstatus(ui, repo, *pats, **opts):
+    '''run the status command with colored output
+
+    Usage is identical to the status command.
+    '''
+
+    delimiter = opts['print0'] and '\0' or '\n'
+
+    # run status and capture it's output
+    ui.pushbuffer()
+    commands.status(ui, repo, *pats, **opts)
+    # filter out empty strings
+    lines = filter(lambda l: l, ui.popbuffer().split(delimiter))
+
+    if opts['no_status']:
+        # if --no-status, run the command again without that option to get
+        # output with status abbreviations
+        opts['no_status'] = False
+        ui.pushbuffer()
+        commands.status(ui, repo, *pats, **opts)
+        lines_with_status = filter(lambda l: l,
+                                   ui.popbuffer().split(delimiter))
+    else:
+        lines_with_status = lines
+
+    # apply color to output and display it
+    for i in xrange(0, len(lines)):
+        status = _status_abbreviations[lines_with_status[i][0]]
+        effects = _status_effects[status]
+        if effects:
+            lines[i] = render_effects(lines[i], *effects)
+            sys.stdout.write(lines[i] + delimiter)
+
+_patch_effects = { 'applied': ('blue', 'bold', 'underline'),
+                   'missing': ('red', 'bold'),
+                   'unapplied': ('black', 'bold'), }
+
+def colorqseries(ui, repo, **opts):
+    '''run the qseries command with colored output
+
+    Usage is identical to the qseries command.
+    '''
+    ui.pushbuffer()
+    mq.series(ui, repo, **opts)
+    patches = ui.popbuffer().splitlines()
+    for patch in patches:
+        if opts['missing']:
+            effects = _patch_effects['missing']
+        elif filter(lambda n: patch.startswith(n.name), repo.mq.applied):
+            effects = _patch_effects['applied']
+        else:
+            effects = _patch_effects['unapplied']
+        sys.stdout.write(render_effects(patch, *effects) + '\n')
+
+def _copy_options(ui, fromcmd, fromtable, tocmd):
+    '''Copy fromcmd options into the local command table for tocmd.'''
+    toentry = cmdutil.findcmd(ui, tocmd, cmdtable)
+    fromentry = cmdutil.findcmd(ui, fromcmd, fromtable)
+    toentry[1][1].extend(fromentry[1][1])
+
+def _configeffects(ui):
+    '''Override default effects with those from .hgrc'''
+    for filestatus in _status_effects:
+        effects = ui.config('color', 'cstatus.' + filestatus)
+        if effects:
+            _status_effects[filestatus] = re.split('\W+', effects)
+    for patchstatus in _patch_effects:
+        effects = ui.config('color', 'cqseries.' + patchstatus)
+        if effects:
+            _patch_effects[patchstatus] = re.split('\W+', effects)
+
+def uisetup(ui):
+    '''Initialize the extension.'''
+    _copy_options(ui, 'status', commands.table, 'cstatus')
+    _configeffects(ui)
+    if ui.config('extensions', 'hgext.mq', default=None) is not None:
+        cmdtable['cqseries'] = (colorqseries,
+                                [],
+                                _('hg cqseries [-ms]'))
+        _copy_options(ui, 'qseries', mq.cmdtable, 'cqseries')
+
+cmdtable = {
+    "cstatus|cst": (colorstatus,
+                    [], # filled in from status command's options
+                    _('hg cstatus [OPTION]... [FILE]...')),
+}


More information about the Mercurial-devel mailing list