color: don't disable colors if HGPLAINEXCEPT=color

Summary:
With `HGPLAINEXCEPT=color`, colors should still be enabled if the terminal is
capable of supporting them and output is to the terminal.

Currently this doesn't work if `--color=auto` (the default), as
`color._modesetup` uses `ui.formatted` to check if the output is a terminal,
and thus has colors available, but this is `False` for all `HGPLAIN` modes.

Instead, add a new method to `ui` that checks whether  `fout` is a terminal, and
use that for color autodetection.

This function also allows us to add `HGPLAINEXCEPT=pager` as we can use
that in the same way.

Reviewed By: farnz

Differential Revision: D21617170

fbshipit-source-id: 7ee4eaa8963f3d6eb7ed8044a678a4804b9a98f0
This commit is contained in:
Mark Thomas 2020-05-19 06:11:56 -07:00 committed by Facebook GitHub Bot
parent 9e3a321235
commit 513a4f8426
6 changed files with 37 additions and 17 deletions

View File

@ -178,14 +178,16 @@ def _modesetup(ui):
if not always and not auto:
return None
formatted = always or (encoding.environ.get("TERM") != "dumb" and ui.formatted)
havecolors = always or (
encoding.environ.get("TERM") != "dumb" and ui.terminaloutput()
)
if pycompat.iswindows:
from . import win32
if not (util.istest() or win32.enablevtmode()):
if not ui.pageractive:
if formatted:
if havecolors:
ui.warn(
_(
"couldn't enable VT mode for your terminal, disabling colors\n"
@ -194,7 +196,7 @@ def _modesetup(ui):
if not always:
return None
if always or (auto and formatted):
if always or (auto and havecolors):
return "ansi"
return None

View File

@ -456,6 +456,7 @@ coreconfigitem("ui", "allowemptycommit", default=False)
coreconfigitem("ui", "allowmerge", default=True)
coreconfigitem("ui", "archivemeta", default=True)
coreconfigitem("ui", "askusername", default=False)
coreconfigitem("ui", "assume-tty", default=False)
coreconfigitem("ui", "autopullcommits", default=True)
coreconfigitem("ui", "clonebundlefallback", default=False)
coreconfigitem("ui", "clonebundleprefers", default=list)

View File

@ -180,6 +180,8 @@ class ui(object):
# color mode: see mercurial/color.py for possible value
self._colormode = None
self._styles = {}
# Whether the output stream is known to be a terminal.
self._terminaloutput = None
if src:
self._uiconfig = src._uiconfig.copy()
@ -191,6 +193,7 @@ class ui(object):
self._disablepager = src._disablepager
self._tweaked = src._tweaked
self._outputui = src._outputui
self._terminaloutput = src._terminaloutput
self.environ = src.environ
self.callhooks = src.callhooks
@ -755,6 +758,8 @@ class ui(object):
def _isatty(self, fh):
if self.configbool("ui", "nontty"):
return False
if self.configbool("ui", "assume-tty"):
return True
return util.isatty(fh)
def disablepager(self):
@ -783,10 +788,8 @@ class ui(object):
command in self.configlist("pager", "ignore")
or not self.configbool("ui", "paginate")
or not self.configbool("pager", "attend-" + command, True)
# TODO: if we want to allow HGPLAINEXCEPT=pager,
# formatted() will need some adjustment.
or not self.formatted
or self.plain()
or not self.terminaloutput()
or self.plain("pager")
or self._buffers
# TODO: expose debugger-enabled on the UI object
or "--debugger" in pycompat.sysargv
@ -809,6 +812,7 @@ class ui(object):
self.flush()
wasformatted = self.formatted
wasterminaloutput = self.terminaloutput()
if util.safehasattr(signal, "SIGPIPE"):
signal.signal(signal.SIGPIPE, _catchterm)
if pagercmd == "internal:streampager":
@ -821,6 +825,7 @@ class ui(object):
self.setconfig("ui", "formatted", wasformatted, "pager")
util.clearcachedproperty(self, "formatted")
self.setconfig("ui", "interactive", False, "pager")
self._terminaloutput = wasterminaloutput
# If pager encoding is set, update the output encoding
pagerencoding = self.config("pager", "encoding")
@ -847,6 +852,9 @@ class ui(object):
# The Rust pager wants utf-8 unconditionally.
encoding.outputencoding = "utf-8"
# Pass through whether output was a terminal
istty = self.terminaloutput()
# Replace stream with write functions from the Rust pager.
class stream(object):
def __init__(self, writefunc):
@ -861,6 +869,9 @@ class ui(object):
def close(self):
pass
def isatty(self):
return istty
assert isinstance(self.fout, util.refcell)
assert isinstance(self.ferr, util.refcell)
origfout = self.fout.swap(stream(pager.write))
@ -1055,6 +1066,13 @@ class ui(object):
pass
return scmutil.termsize(self)[0]
def terminaloutput(self):
"""is output to a terminal?"""
istty = self._terminaloutput
if istty is None:
return self._isatty(self.fout)
return istty
@util.propertycache
def formatted(self):
"""should formatted output be used?

View File

@ -2,7 +2,7 @@
Setup
$ setconfig ui.color=yes ui.formatted=always ui.paginate=never
$ setconfig ui.color=always ui.paginate=never
$ setconfig color.mode=ansi
$ hg init repo
$ cd repo
@ -49,7 +49,7 @@ default context
(check that 'ui.color=yes' match '--color=auto')
$ hg diff --nodates --config ui.formatted=no
$ hg diff --nodates --config ui.color=yes
diff -r cf9f4ba66af2 a
--- a/a
+++ b/a
@ -65,7 +65,7 @@ default context
(check that 'ui.color=no' disable color)
$ hg diff --nodates --config ui.formatted=yes --config ui.color=no
$ hg diff --nodates --config ui.color=no
diff -r cf9f4ba66af2 a
--- a/a
+++ b/a
@ -81,7 +81,7 @@ default context
(check that 'ui.color=always' force color)
$ hg diff --nodates --config ui.formatted=no --config ui.color=always
$ hg diff --nodates --config ui.color=always
\x1b[0;1mdiff -r cf9f4ba66af2 a\x1b[0m (esc)
\x1b[0;31;1m--- a/a\x1b[0m (esc)
\x1b[0;32;1m+++ b/a\x1b[0m (esc)

View File

@ -8,10 +8,9 @@
> sys.stdout.write('paged empty output!\n')
> EOF
Enable ui.formatted because pager won't fire without it, and set up
pager and tell it to use our fake pager that lets us see when the
pager was running.
$ setconfig ui.formatted=yes ui.color=no
Enable ui.assume-tty so that the pager will start, and set the pager to our
fake pager that lets us see when the pager was running.
$ setconfig ui.assume-tty=yes ui.color=no
$ cat >>$HGRCPATH <<EOF
> [pager]
> pager = $PYTHON $TESTTMP/fakepager.py
@ -147,7 +146,7 @@ Attend for an abbreviated command does not work
Pager should not start if stdout is not a tty.
$ hg log -l1 -q --config ui.formatted=False
$ hg log -l1 -q --config ui.assume-tty=no
10:46106edeeb38
Pager should be disabled if pager.pager is empty (otherwise the output would

View File

@ -21,7 +21,7 @@ hgrc.write("color=\n")
hgrc.close()
ui_ = uimod.ui.load()
ui_.setconfig("ui", "formatted", "True")
ui_.setconfig("ui", "assume-tty", "True")
# we're not interested in the output, so write that to devnull
ui_.fout = open(os.devnull, "wb")