[PATCH 3 of 4] color: automatically define 16 and 256 colors if supported

Gregory Szorc gregory.szorc at gmail.com
Mon Jul 10 20:26:51 EDT 2017


On Mon, Jul 10, 2017 at 4:32 PM, Kyle Lippincott <spectral at pewpew.net>
wrote:

>
>
> On Sun, Jul 9, 2017 at 4:46 PM, Gregory Szorc <gregory.szorc at gmail.com>
> wrote:
>
>> # HG changeset patch
>> # User Gregory Szorc <gregory.szorc at gmail.com>
>> # Date 1499641339 25200
>> #      Sun Jul 09 16:02:19 2017 -0700
>> # Node ID fa6223b9e2a0d9fbfa81329b83c0512417cee713
>> # Parent  98c54db9407a7e0ba94a632aafdbbec3fc76fa8b
>> color: automatically define 16 and 256 colors if supported
>>
>> The "colors" terminfo capability returns the number of colors
>> supported by the terminal profile.
>>
>> But with terminals, nothing is straightforward. The "colors"
>> capability only returns what the current terminal profile (likely
>> defined by $TERM) supports. The current terminal type or its profile
>> may be wrong (most likely the former). So, any consumer of this
>> capability needs to take this scenario into account.
>>
>> This commit adds code for querying the "colors" terminfo capability
>> and for defining additional colors if 16 or 256 color support is
>> present. Because of the aforementioned limitations with capability
>> accuracy, a config option is introduced that allows the color count
>> to be limited. If the extra colors cause any problems, once can simply
>> set this option to restore existing 8 color behavior.
>>
>> As part of this feature, a dictionary containing cherry-picked colors
>> from the 256 color spectrum was created. Choices are very subjective
>> and can and should be adjusted, as appropriate.
>>
>> The help documentation for color support has been significantly
>> overhauled as part of this commit.
>>
>> diff --git a/mercurial/color.py b/mercurial/color.py
>> --- a/mercurial/color.py
>> +++ b/mercurial/color.py
>> @@ -45,10 +45,43 @@ try:
>>          'white': curses.COLOR_WHITE,
>>      }
>>
>> +    # Bright versions of the built-in colors are colors 8-15, in the
>> same order.
>> +    TERMINFO_COLOR_16 = {
>> +        'brightblack': 8,
>> +        'brightred': 9,
>> +        'brightgreen': 10,
>> +        'brightyellow': 11,
>> +        'brightblue': 12,
>> +        'brightmagenta': 13,
>> +        'brightcyan': 14,
>> +        'brightwhite': 15,
>> +    }
>> +
>> +    # 6x6x6 color cube is 16-231. We define some common ones.
>> +    TERMINFO_COLOR_256 = {
>> +        'darkgreen': 28,
>> +        'turquoise': 45,
>> +        'lightgreen': 70,
>> +        'bluegreen': 73,
>> +        'deeppurple': 89,
>> +        'purple': 129,
>> +        'lightpurple': 177,
>> +        'lightorange': 178,
>> +        'lightyellow': 191,
>> +        'pink': 201,
>> +        'darkorange': 208,
>> +    }
>> +
>> +    # 232-255 is a continuous spectrum of grey.
>> +    for i, value in enumerate(range(232, 256)):
>> +        TERMINFO_COLOR_256['grey%02d' % (i + 1)] = value
>> +
>>  except ImportError:
>>      curses = None
>>      _baseterminfoparams = {}
>>      TERMINFO_COLOR_8 = {}
>> +    TERMINFO_COLOR_16 = {}
>> +    TERMINFO_COLOR_256 = {}
>>
>>  _enabledbydefault = True
>>
>> @@ -146,6 +179,43 @@ def _terminfocolors(ui):
>>      for color, value in TERMINFO_COLOR_8.items():
>>          colors[color] = (value, False)
>>
>> +    # Add 16 and 256 bit colors if supported and allowed by config
>> policy.
>> +    #
>> +    # There's no reliable way to detect if the extra colors will
>> actually work.
>> +    # The terminfo database just reports what the current terminal is
>> +    # advertising. Some terminals support querying the value of a color
>> via
>> +    # e.g. \e]4;%d;?\a. However, this isn't universally supported. So set
>> +    # colors based purely on terminfo and provide users a way to disable
>> in
>> +    # case it doesn't work.
>> +
>> +    # Will likely return -1 on failure.
>> +    termcolors = curses.tigetnum('colors')
>> +    if termcolors < 8:
>> +        termcolors = 8
>> +
>> +    # Default is to allow as many colors as the terminal supports.
>> +    colorlimit = ui.configint('color', 'colorlimit', 256)
>> +
>> +    if colorlimit not in (8, 16, 256):
>> +        ui.warn(_('unsupported color.colorlimit value %d; use 8, 16, '
>> +                  'or 256\n') % colorlimit)
>> +        colorlimit = 8
>> +
>> +    usecolors = min(termcolors, colorlimit)
>>
>
> Considering how often TERM is incorrect and/or terminfo is incorrect or
> minimizing/lowest-common-denominator (great example for both: TERM=xterm
> vs xterm-256color), are we sure we want to min()?  Can we have some config
> option to override the colors setting?
>
> Every single terminal I support handles \033[38;5;##m colors where ## is
> max(88) or max(256), regardless of what their terminfo says.  I'm fine with
> ignoring the few 88-color rxvt-unicode users and telling them to use the
> 256-color version.  It would be nice to be able to put something like:
>
> [color]
> mode=terminfo
> colorlimit=256   # Ignore terminfo's stated limit
>
> into our config file to get this all corrected.
>
>
I was also considering a config option (either the same or a different one)
to force setting color support. After all, inaccurate TERM+terminfo is a
thing. My worry about that is it may need to be conditional depending on
the terminal type. If you e.g. have a shared hgrc that is accessed by
different terminals, a forced setting could be bad. It's definitely safer
to use TERM+terminfo to drive the setting.

Terminals are scary and I'm really hesitant to make *any* change that uses
more powerful terminal features that can't easily be reverted via simple
config file muckery.


> +
>> +    if usecolors >= 16:
>> +        for color, value in TERMINFO_COLOR_16.items():
>> +            colors[color] = (value, False)
>> +
>> +    if usecolors >= 256:
>> +        for color, value in TERMINFO_COLOR_256.items():
>> +            colors[color] = (value, False)
>> +
>> +    # TODO consider filtering color values by usecolors. This has 2
>> +    # implications: 1) users can't force colors beyond the supported
>> +    # color range (this arguably makes sense but is BC) 2) configstyles()
>> +    # emits a warning if there is a reference to this discarded color
>> +    # (it may be desirable to suppress that warning).
>>      for key, value in ui.configitems('color'):
>>          if key.startswith('color.'):
>>              colors[key[6:]] = (int(value), True)
>> @@ -186,6 +256,7 @@ def _terminfosetup(ui, mode):
>>              # noisy and use ui.debug().
>>              ui.debug("no terminfo entry for %s\n" % e)
>>              del ui._terminfoparams[key]
>> +
>>      if not curses.tigetstr('setaf') or not curses.tigetstr('setab'):
>>          # Only warn about missing terminfo entries if we explicitly
>> asked for
>>          # terminfo mode.
>> diff --git a/mercurial/help/color.txt b/mercurial/help/color.txt
>> --- a/mercurial/help/color.txt
>> +++ b/mercurial/help/color.txt
>> @@ -131,19 +131,38 @@ effects may be overridden from your conf
>>
>>    histedit.remaining = red bold
>>
>> +Supported Colors
>> +================
>> +
>> +There are 8 standard colors: ``black``, ``red``, ``green``, ``yellow``,
>> +``blue``, ``magenta``, ``cyan``, and ``white``.
>> +
>> +If the ``terminfo`` color mode is being used and the terminfo database
>> +reports that the current terminal supports more colors, additional colors
>> +will be defined.
>> +
>> +If 16 colors are supported, the 8 additional colors are the 8 standard
>> +colors prefixed with "bright". e.g. ``brightred`` and ``brightyellow``.
>> +
>> +If 256 colors are supported, an assortment of additional colors are
>> +available. These include ``darkgreen``, ``turquoise``, ``purple``, and
>> +``pink``. For the list of all defined colors, run :hg:`debugcolor`.
>> +
>> +In some cases, not all colors will render properly. See
>> +:hg:`help config.color.colorlimit` for how to limit Mercurial to a
>> smaller
>> +set of colors.
>> +
>>  Custom colors
>>  =============
>>
>> -Because there are only eight standard colors, Mercurial allows you
>> -to define color names for other color slots which might be available
>> -for your terminal type, assuming terminfo mode.  For instance::
>> +When using ``terminfo`` mode, Mercurial supports defining colors via
>> +config options. You can either declare new colors or change the value
>> +for a built-in color. e.g.::
>>
>> -  color.brightblue = 12
>> +  [color]
>> +  color.mydarkblue = 18
>>    color.pink = 207
>>    color.orange = 202
>>
>> -to set 'brightblue' to color slot 12 (useful for 16 color terminals
>> -that have brighter colors defined in the upper eight) and, 'pink' and
>> -'orange' to colors in 256-color xterm's default color cube.  These
>> -defined colors may then be used as any of the pre-defined eight,
>> -including appending '_background' to set the background to that color.
>> +To see what color values are supported and how they render, perform
>> +an Internet search for "xterm color cube."
>> diff --git a/mercurial/help/config.txt b/mercurial/help/config.txt
>> --- a/mercurial/help/config.txt
>> +++ b/mercurial/help/config.txt
>> @@ -432,6 +432,22 @@ effect and style see :hg:`help color`.
>>      On some systems (such as MSYS in Windows), the terminal may support
>>      a different color mode than the pager program.
>>
>> +``colorlimit``
>> +    Limits how many colors will be defined and used.
>> +
>> +    Most terminals support 8 standard colors. Mercurial will detect
>> +    and use additional colors if the terminal indicates support for them.
>> +
>> +    In some cases, the terminal advertises support for additional colors
>> +    but doesn't actually support them. This can lead to poor formatting
>> +    or even gibberish being printed.
>> +
>> +    Setting this option to a value of ``8``, ``16``, or ``256`` will
>> +    explicitly limit Mercurial to a maximum of that many colors.
>> +
>> +    This option has no effect unless the ``terminfo`` color mode is being
>> +    used.
>> +
>>  ``commands``
>>  ------------
>>
>> diff --git a/tests/hgterm.ti b/tests/hgterm16.ti
>> copy from tests/hgterm.ti
>> copy to tests/hgterm16.ti
>> --- a/tests/hgterm.ti
>> +++ b/tests/hgterm16.ti
>> @@ -1,6 +1,6 @@
>> -hgterm,
>> +hgterm-16color,
>>         am, km, mir, msgr, xenl,
>> -       colors#8, cols#80, it#8, lines#24, pairs#64,
>> +       colors#16, cols#80, it#8, lines#24, pairs#64,
>>         acsc=``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
>>         bel=^G, bold=\E[1m, clear=\E[H\E[2J, cr=\r,
>>         csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=\b,
>> diff --git a/tests/hgterm.ti b/tests/hgterm256.ti
>> copy from tests/hgterm.ti
>> copy to tests/hgterm256.ti
>> --- a/tests/hgterm.ti
>> +++ b/tests/hgterm256.ti
>> @@ -1,6 +1,6 @@
>> -hgterm,
>> +hgterm-256color,
>>         am, km, mir, msgr, xenl,
>> -       colors#8, cols#80, it#8, lines#24, pairs#64,
>> +       colors#256, cols#80, it#8, lines#24, pairs#64,
>>         acsc=``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
>>         bel=^G, bold=\E[1m, clear=\E[H\E[2J, cr=\r,
>>         csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=\b,
>> diff --git a/tests/test-status-color.t b/tests/test-status-color.t
>> --- a/tests/test-status-color.t
>> +++ b/tests/test-status-color.t
>> @@ -226,6 +226,9 @@ hg status -A (with terminfo color):
>>
>>    $ mkdir "$TESTTMP/terminfo"
>>    $ TERMINFO="$TESTTMP/terminfo" tic "$TESTDIR/hgterm.ti"
>> +  $ TERMINFO="$TESTTMP/terminfo" tic "$TESTDIR/hgterm16.ti"
>> +  $ TERMINFO="$TESTTMP/terminfo" tic "$TESTDIR/hgterm256.ti"
>> +
>>    $ TERM=hgterm TERMINFO="$TESTTMP/terminfo" hg status --config
>> color.mode=terminfo -A
>>    \x1b[30m\x1b[32m\x1b[1mA \x1b[30m\x1b[30m\x1b[32m\x1b[1madded\x1b[30m
>> (esc)
>>    \x1b[30m\x1b[32m\x1b[1mA \x1b[30m\x1b[30m\x1b[32m\x1b[1mcopied\x1b[30m
>> (esc)
>> @@ -256,6 +259,58 @@ The user can define effects with raw ter
>>    \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)
>>
>> +A 16 and 256 colors won't be used if the terminal doesn't advertise
>> support
>> +
>> +  $ TERM=hgterm TERMINFO="$TESTTMP/terminfo" hg status -a --config
>> color.mode=terminfo --config color.status.added=brightred
>> +  ignoring unknown color/effect 'brightred' (configured in
>> color.status.added)
>> +  ignoring unknown color/effect 'brightred' (configured in
>> color.status.added)
>> +  ignoring unknown color/effect 'brightred' (configured in
>> color.status.added)
>> +  A added
>> +  A copied
>> +
>> +  $ TERM=hgterm TERMINFO="$TESTTMP/terminfo" hg status -a --config
>> color.mode=terminfo --config color.status.added=purple
>> +  ignoring unknown color/effect 'purple' (configured in
>> color.status.added)
>> +  ignoring unknown color/effect 'purple' (configured in
>> color.status.added)
>> +  ignoring unknown color/effect 'purple' (configured in
>> color.status.added)
>> +  A added
>> +  A copied
>> +
>> +A 16 color can be used if terminal advertises 16 color support
>> +
>> +  $ TERM=hgterm-16color TERMINFO="$TESTTMP/terminfo" hg status -a
>> --config color.mode=terminfo --config color.status.added=brightred
>> +  \x1b[30m\x1b[39mA \x1b[30m\x1b[30m\x1b[39madded\x1b[30m (esc)
>> +  \x1b[30m\x1b[39mA \x1b[30m\x1b[30m\x1b[39mcopied\x1b[30m (esc)
>> +
>> +A 256 color can be used if terminal advertises 256 color support
>> +
>> +  $ TERM=hgterm-256color TERMINFO="$TESTTMP/terminfo" hg status -a
>> --config color.mode=terminfo --config color.status.added=purple
>> +  \x1b[30m\x1b[3129mA \x1b[30m\x1b[30m\x1b[3129madded\x1b[30m (esc)
>> +  \x1b[30m\x1b[3129mA \x1b[30m\x1b[30m\x1b[3129mcopied\x1b[30m (esc)
>> +
>> +color.colorlimit can narrow allowed color range
>> +
>> +  $ TERM=hgterm-256color TERMINFO="$TESTTMP/terminfo" hg status -a
>> --config color.mode=terminfo --config color.status.added=purple --config
>> color.colorlimit=8
>> +  ignoring unknown color/effect 'purple' (configured in
>> color.status.added)
>> +  ignoring unknown color/effect 'purple' (configured in
>> color.status.added)
>> +  ignoring unknown color/effect 'purple' (configured in
>> color.status.added)
>> +  A added
>> +  A copied
>> +  $ TERM=hgterm-256color TERMINFO="$TESTTMP/terminfo" hg status -a
>> --config color.mode=terminfo --config color.status.added=purple --config
>> color.colorlimit=16
>> +  ignoring unknown color/effect 'purple' (configured in
>> color.status.added)
>> +  ignoring unknown color/effect 'purple' (configured in
>> color.status.added)
>> +  ignoring unknown color/effect 'purple' (configured in
>> color.status.added)
>> +  A added
>> +  A copied
>> +
>> +Invalid color.colorlimit is normalized to 8
>> +
>> +  $ TERM=hgterm-256color TERMINFO="$TESTTMP/terminfo" hg status -a
>> --config color.mode=terminfo --config color.colorlimit=128
>> +  unsupported color.colorlimit value 128; use 8, 16, or 256
>> +  unsupported color.colorlimit value 128; use 8, 16, or 256
>> +  unsupported color.colorlimit value 128; use 8, 16, or 256
>> +  \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)
>> +
>>  #endif
>>
>>
>> _______________________________________________
>> Mercurial-devel mailing list
>> Mercurial-devel at mercurial-scm.org
>> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.mercurial-scm.org/pipermail/mercurial-devel/attachments/20170710/17a7d227/attachment.html>


More information about the Mercurial-devel mailing list