pager: don't run pager if nothing is written to stdout/stderr
This decides when to run the pager based on the first call to
ui.write() and ui.write_err(). This has the side effect of not the
output of subprocesses that write output before hg does.
# pager.py - display output using a pager
#
# Copyright 2008 David Soria Parra <dsp@php.net>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
#
# To load the extension, add it to your configuration file:
#
# [extension]
# pager =
#
# Run "hg help pager" to get info on configuration.
'''browse command output with an external pager
To set the pager that should be used, set the application variable::
[pager]
pager = LESS='FSRX' less
If no pager is set, the pager extensions uses the environment variable
$PAGER. If neither pager.pager, nor $PAGER is set, no pager is used.
By default, the pager is only executed if a command has output. To
force the pager to run even if a command prints nothing, set::
[pager]
force = True
If you notice "BROKEN PIPE" error messages, you can disable them by
setting::
[pager]
quiet = True
You can disable the pager for certain commands by adding them to the
pager.ignore list::
[pager]
ignore = version, help, update
You can also enable the pager only for certain commands using
pager.attend. Below is the default list of commands to be paged::
[pager]
attend = annotate, cat, diff, export, glog, log, qdiff
Setting pager.attend to an empty value will cause all commands to be
paged.
If pager.attend is present, pager.ignore will be ignored.
To ignore global commands like :hg:`version` or :hg:`help`, you have
to specify them in your user configuration file.
The --pager=... option can also be used to control when the pager is
used. Use a boolean value like yes, no, on, off, or use auto for
normal behavior.
'''
import sys, os, signal, shlex, errno
from mercurial import commands, dispatch, util, extensions
from mercurial.i18n import _
def _runpager(p, sigpipe=False):
if not hasattr(os, 'fork'):
sys.stderr = sys.stdout = util.popen(p, 'wb')
return
fdin, fdout = os.pipe()
pid = os.fork()
if pid == 0:
os.close(fdin)
os.dup2(fdout, sys.stdout.fileno())
os.dup2(fdout, sys.stderr.fileno())
os.close(fdout)
if sigpipe:
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
return
os.dup2(fdin, sys.stdin.fileno())
os.close(fdin)
os.close(fdout)
try:
os.execvp('/bin/sh', ['/bin/sh', '-c', p])
except OSError, e:
if e.errno == errno.ENOENT:
# no /bin/sh, try executing the pager directly
args = shlex.split(p)
os.execvp(args[0], args)
else:
raise
def uisetup(ui):
if ui.plain():
return
class pagerui(ui.__class__):
_pager = None
_pagerstarted = False
def write(self, *args, **opts):
if self._pager and not self._pagerstarted:
self._pagerstarted = True
self._pager()
return super(pagerui, self).write(*args, **opts)
def write_err(self, *args, **opts):
if self._pager and not self._pagerstarted:
self._pagerstarted = True
self._pager()
return super(pagerui, self).write(*args, **opts)
ui.__class__ = pagerui
def pagecmd(orig, ui, options, cmd, cmdfunc):
p = ui.config("pager", "pager", os.environ.get("PAGER"))
if p and sys.stdout.isatty() and '--debugger' not in sys.argv:
attend = ui.configlist('pager', 'attend', attended)
auto = options['pager'] == 'auto'
always = util.parsebool(options['pager'])
if (always or auto and
(cmd in attend or
(cmd not in ui.configlist('pager', 'ignore') and not attend))):
ui.setconfig('ui', 'formatted', ui.formatted())
ui.setconfig('ui', 'interactive', False)
sigpipe = ui.configbool('pager', 'quiet')
if ui.configbool('pager', 'force'):
_runpager(p, sigpipe)
else:
ui._pager = lambda: _runpager(p, sigpipe)
return orig(ui, options, cmd, cmdfunc)
extensions.wrapfunction(dispatch, '_runcommand', pagecmd)
def extsetup(ui):
commands.globalopts.append(
('', 'pager', 'auto',
_("when to paginate (boolean, always, auto, or never)"),
_('TYPE')))
attended = ['annotate', 'cat', 'diff', 'export', 'glog', 'log', 'qdiff']