color: allow for user-configurable terminfo codes for effects
authorDanek Duvall <danek.duvall@oracle.com>
Thu, 13 Oct 2016 11:48:17 -0700
changeset 30173 f34a8cff51d9
parent 30172 90a6c18a7c1d
child 30174 5d777fe4615d
color: allow for user-configurable terminfo codes for effects If the entry in the terminfo database for your terminal is missing some attributes, it should be possible to create them on the fly without resorting to just making them a color. This change allows you to have [color] terminfo.<effect> = <code> where <effect> might be something like "dim" or "bold", and <code> is the escape sequence that would otherwise have come from a call to tigetstr(). If an escape character is needed, use "\E". Any such settings will override attributes that are present in the terminfo database.
hgext/color.py
tests/test-status-color.t
--- a/hgext/color.py	Tue Oct 04 04:06:48 2016 -0700
+++ b/hgext/color.py	Thu Oct 13 11:48:17 2016 -0700
@@ -196,9 +196,12 @@
     if mode not in ('auto', 'terminfo'):
         return
 
-    _terminfo_params.update((key[6:], (False, int(val)))
+    _terminfo_params.update((key[6:], (False, int(val), ''))
         for key, val in ui.configitems('color')
         if key.startswith('color.'))
+    _terminfo_params.update((key[9:], (True, '', val.replace('\\E', '\x1b')))
+        for key, val in ui.configitems('color')
+        if key.startswith('terminfo.'))
 
     try:
         curses.setupterm()
@@ -206,10 +209,10 @@
         _terminfo_params = {}
         return
 
-    for key, (b, e) in _terminfo_params.items():
+    for key, (b, e, c) in _terminfo_params.items():
         if not b:
             continue
-        if not curses.tigetstr(e):
+        if not c and not curses.tigetstr(e):
             # Most terminals don't support dim, invis, etc, so don't be
             # noisy and use ui.debug().
             ui.debug("no terminfo entry for %s\n" % e)
@@ -290,26 +293,26 @@
 
 try:
     import curses
-    # Mapping from effect name to terminfo attribute name or color number.
-    # This will also force-load the curses module.
-    _terminfo_params = {'none': (True, 'sgr0'),
-                        'standout': (True, 'smso'),
-                        'underline': (True, 'smul'),
-                        'reverse': (True, 'rev'),
-                        'inverse': (True, 'rev'),
-                        'blink': (True, 'blink'),
-                        'dim': (True, 'dim'),
-                        'bold': (True, 'bold'),
-                        'invisible': (True, 'invis'),
-                        'italic': (True, 'sitm'),
-                        'black': (False, curses.COLOR_BLACK),
-                        'red': (False, curses.COLOR_RED),
-                        'green': (False, curses.COLOR_GREEN),
-                        'yellow': (False, curses.COLOR_YELLOW),
-                        'blue': (False, curses.COLOR_BLUE),
-                        'magenta': (False, curses.COLOR_MAGENTA),
-                        'cyan': (False, curses.COLOR_CYAN),
-                        'white': (False, curses.COLOR_WHITE)}
+    # Mapping from effect name to terminfo attribute name (or raw code) or
+    # color number.  This will also force-load the curses module.
+    _terminfo_params = {'none': (True, 'sgr0', ''),
+                        'standout': (True, 'smso', ''),
+                        'underline': (True, 'smul', ''),
+                        'reverse': (True, 'rev', ''),
+                        'inverse': (True, 'rev', ''),
+                        'blink': (True, 'blink', ''),
+                        'dim': (True, 'dim', ''),
+                        'bold': (True, 'bold', ''),
+                        'invisible': (True, 'invis', ''),
+                        'italic': (True, 'sitm', ''),
+                        'black': (False, curses.COLOR_BLACK, ''),
+                        'red': (False, curses.COLOR_RED, ''),
+                        'green': (False, curses.COLOR_GREEN, ''),
+                        'yellow': (False, curses.COLOR_YELLOW, ''),
+                        'blue': (False, curses.COLOR_BLUE, ''),
+                        'magenta': (False, curses.COLOR_MAGENTA, ''),
+                        'cyan': (False, curses.COLOR_CYAN, ''),
+                        'white': (False, curses.COLOR_WHITE, '')}
 except ImportError:
     _terminfo_params = {}
 
@@ -375,9 +378,12 @@
     if effect.endswith('_background'):
         bg = True
         effect = effect[:-11]
-    attr, val = _terminfo_params[effect]
+    attr, val, termcode = _terminfo_params[effect]
     if attr:
-        return curses.tigetstr(val)
+        if termcode:
+            return termcode
+        else:
+            return curses.tigetstr(val)
     elif bg:
         return curses.tparm(curses.tigetstr('setab'), val)
     else:
@@ -412,7 +418,7 @@
 
 def configstyles(ui):
     for status, cfgeffects in ui.configitems('color'):
-        if '.' not in status or status.startswith('color.'):
+        if '.' not in status or status.startswith(('color.', 'terminfo.')):
             continue
         cfgeffects = ui.configlist('color', status)
         if cfgeffects:
--- a/tests/test-status-color.t	Tue Oct 04 04:06:48 2016 -0700
+++ b/tests/test-status-color.t	Thu Oct 13 11:48:17 2016 -0700
@@ -237,6 +237,25 @@
   \x1b[30m\x1b[30mC \x1b[30m\x1b[30m\x1b[30m.hgignore\x1b[30m (esc)
   \x1b[30m\x1b[30mC \x1b[30m\x1b[30m\x1b[30mmodified\x1b[30m (esc)
 
+The user can define effects with raw terminfo codes:
+
+  $ cat <<EOF >> $HGRCPATH
+  > # Completely bogus code for dim
+  > terminfo.dim = \E[88m
+  > # We can override what's in the terminfo database, too
+  > terminfo.bold = \E[2m
+  > EOF
+  $ TERM=hgterm TERMINFO="$TESTTMP/terminfo" hg status --config color.mode=terminfo --config color.status.clean=dim --color=always -A
+  \x1b[30m\x1b[32m\x1b[2mA \x1b[30m\x1b[30m\x1b[32m\x1b[2madded\x1b[30m (esc)
+  \x1b[30m\x1b[32m\x1b[2mA \x1b[30m\x1b[30m\x1b[32m\x1b[2mcopied\x1b[30m (esc)
+  \x1b[30m\x1b[30m  modified\x1b[30m (esc)
+  \x1b[30m\x1b[31m\x1b[2mR \x1b[30m\x1b[30m\x1b[31m\x1b[2mremoved\x1b[30m (esc)
+  \x1b[30m\x1b[36m\x1b[2m\x1b[4m! \x1b[30m\x1b[30m\x1b[36m\x1b[2m\x1b[4mdeleted\x1b[30m (esc)
+  \x1b[30m\x1b[35m\x1b[2m\x1b[4m? \x1b[30m\x1b[30m\x1b[35m\x1b[2m\x1b[4munknown\x1b[30m (esc)
+  \x1b[30m\x1b[30m\x1b[2mI \x1b[30m\x1b[30m\x1b[30m\x1b[2mignored\x1b[30m (esc)
+  \x1b[30m\x1b[88mC \x1b[30m\x1b[30m\x1b[88m.hgignore\x1b[30m (esc)
+  \x1b[30m\x1b[88mC \x1b[30m\x1b[30m\x1b[88mmodified\x1b[30m (esc)
+
 #endif