pager.py
175 lines
| 5.6 KiB
| text/x-python
|
PythonLexer
/ hgext / pager.py
David Soria Parra
|
r6323 | # pager.py - display output using a pager | ||
# | ||||
# Copyright 2008 David Soria Parra <dsp@php.net> | ||||
# | ||||
Martin Geisler
|
r8225 | # This software may be used and distributed according to the terms of the | ||
Matt Mackall
|
r10263 | # GNU General Public License version 2 or any later version. | ||
David Soria Parra
|
r6323 | # | ||
Brodie Rao
|
r12083 | # To load the extension, add it to your configuration file: | ||
David Soria Parra
|
r6323 | # | ||
# [extension] | ||||
Martin Geisler
|
r10112 | # pager = | ||
David Soria Parra
|
r6323 | # | ||
timeless
|
r29967 | # Run 'hg help pager' to get info on configuration. | ||
Christian Ebert
|
r6462 | |||
Cédric Duval
|
r8894 | '''browse command output with an external pager | ||
Christian Ebert
|
r6462 | |||
Martin Geisler
|
r9212 | To set the pager that should be used, set the application variable:: | ||
Christian Ebert
|
r6462 | |||
[pager] | ||||
Thomas Arendsen Hein
|
r17305 | pager = less -FRX | ||
Christian Ebert
|
r6462 | |||
Martin Geisler
|
r9267 | 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. | ||||
Christian Ebert
|
r6462 | |||
You can disable the pager for certain commands by adding them to the | ||||
Martin Geisler
|
r9212 | pager.ignore list:: | ||
Christian Ebert
|
r6462 | |||
[pager] | ||||
ignore = version, help, update | ||||
Martin Geisler
|
r9267 | You can also enable the pager only for certain commands using | ||
Brodie Rao
|
r9841 | pager.attend. Below is the default list of commands to be paged:: | ||
Christian Ebert
|
r6462 | |||
[pager] | ||||
Brodie Rao
|
r9841 | attend = annotate, cat, diff, export, glog, log, qdiff | ||
Setting pager.attend to an empty value will cause all commands to be | ||||
paged. | ||||
Christian Ebert
|
r6462 | |||
If pager.attend is present, pager.ignore will be ignored. | ||||
Matt Mackall
|
r21281 | Lastly, you can enable and disable paging for individual commands with | ||
the attend-<command> option. This setting takes precedence over | ||||
existing attend and ignore options and defaults:: | ||||
[pager] | ||||
attend-cat = false | ||||
Martin Geisler
|
r10973 | To ignore global commands like :hg:`version` or :hg:`help`, you have | ||
Brodie Rao
|
r12083 | to specify them in your user configuration file. | ||
Brodie Rao
|
r12694 | |||
timeless
|
r27128 | To control whether the pager is used at all for an individual command, | ||
you can use --pager=<value>:: | ||||
- use as needed: `auto`. | ||||
- require the pager: `yes` or `on`. | ||||
- suppress the pager: `no` or `off` (any unrecognized value | ||||
will also work). | ||||
Matt Mackall
|
r21281 | |||
Christian Ebert
|
r6462 | ''' | ||
Augie Fackler
|
r28320 | from __future__ import absolute_import | ||
David Soria Parra
|
r6323 | |||
Augie Fackler
|
r28320 | import atexit | ||
import os | ||||
import signal | ||||
import subprocess | ||||
import sys | ||||
Yuya Nishihara
|
r29205 | from mercurial.i18n import _ | ||
Augie Fackler
|
r28320 | from mercurial import ( | ||
cmdutil, | ||||
commands, | ||||
dispatch, | ||||
extensions, | ||||
util, | ||||
) | ||||
David Soria Parra
|
r6323 | |||
Augie Fackler
|
r29841 | # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for | ||
Augie Fackler
|
r25186 | # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should | ||
# be specifying the version(s) of Mercurial they are tested with, or | ||||
# leave the attribute unspecified. | ||||
Augie Fackler
|
r29841 | testedwith = 'ships-with-hg-core' | ||
Augie Fackler
|
r16743 | |||
Yuya Nishihara
|
r26452 | def _runpager(ui, p): | ||
Brodie Rao
|
r16631 | pager = subprocess.Popen(p, shell=True, bufsize=-1, | ||
close_fds=util.closefds, stdin=subprocess.PIPE, | ||||
Yuya Nishihara
|
r30473 | stdout=util.stdout, stderr=util.stderr) | ||
Brodie Rao
|
r16631 | |||
Yuya Nishihara
|
r26454 | # back up original file objects and descriptors | ||
olduifout = ui.fout | ||||
Yuya Nishihara
|
r30473 | oldstdout = util.stdout | ||
stdoutfd = os.dup(util.stdout.fileno()) | ||||
stderrfd = os.dup(util.stderr.fileno()) | ||||
Yuya Nishihara
|
r26454 | |||
# create new line-buffered stdout so that output can show up immediately | ||||
Yuya Nishihara
|
r30473 | ui.fout = util.stdout = newstdout = os.fdopen(util.stdout.fileno(), 'wb', 1) | ||
os.dup2(pager.stdin.fileno(), util.stdout.fileno()) | ||||
if ui._isatty(util.stderr): | ||||
os.dup2(pager.stdin.fileno(), util.stderr.fileno()) | ||||
Brodie Rao
|
r16631 | |||
@atexit.register | ||||
def killpager(): | ||||
Matt Mackall
|
r18717 | if util.safehasattr(signal, "SIGINT"): | ||
signal.signal(signal.SIGINT, signal.SIG_IGN) | ||||
Brodie Rao
|
r16631 | pager.stdin.close() | ||
Yuya Nishihara
|
r26454 | ui.fout = olduifout | ||
Yuya Nishihara
|
r30473 | util.stdout = oldstdout | ||
Yuya Nishihara
|
r26454 | # close new stdout while it's associated with pager; otherwise stdout | ||
# fd would be closed when newstdout is deleted | ||||
newstdout.close() | ||||
# restore original fds: stdout is open again | ||||
Yuya Nishihara
|
r30473 | os.dup2(stdoutfd, util.stdout.fileno()) | ||
os.dup2(stderrfd, util.stderr.fileno()) | ||||
Brodie Rao
|
r16631 | pager.wait() | ||
Brodie Rao
|
r11182 | |||
David Soria Parra
|
r6323 | def uisetup(ui): | ||
Matt Mackall
|
r16752 | if '--debugger' in sys.argv or not ui.formatted(): | ||
Yuya Nishihara
|
r11414 | return | ||
Jun Wu
|
r28554 | # chg has its own pager implementation | ||
argv = sys.argv[:] | ||||
if 'chgunix' in dispatch._earlygetopt(['--cmdserver'], argv): | ||||
return | ||||
Matt Mackall
|
r7216 | def pagecmd(orig, ui, options, cmd, cmdfunc): | ||
David Soria Parra <dsp <at> php.net>
|
r6417 | p = ui.config("pager", "pager", os.environ.get("PAGER")) | ||
Matt Mackall
|
r21277 | usepager = False | ||
Matt Mackall
|
r21278 | always = util.parsebool(options['pager']) | ||
Matt Mackall
|
r21279 | auto = options['pager'] == 'auto' | ||
Idan Kamara
|
r14515 | |||
Matt Mackall
|
r21277 | if not p: | ||
pass | ||||
Matt Mackall
|
r21278 | elif always: | ||
usepager = True | ||||
Matt Mackall
|
r21279 | elif not auto: | ||
usepager = False | ||||
Matt Mackall
|
r21277 | else: | ||
Brodie Rao
|
r9841 | attend = ui.configlist('pager', 'attend', attended) | ||
Matt Mackall
|
r21280 | ignore = ui.configlist('pager', 'ignore') | ||
David Soria Parra
|
r19940 | cmds, _ = cmdutil.findcmd(cmd, commands.table) | ||
for cmd in cmds: | ||||
Matt Mackall
|
r21281 | var = 'attend-%s' % cmd | ||
if ui.config('pager', var): | ||||
usepager = ui.configbool('pager', var) | ||||
break | ||||
Matt Mackall
|
r21279 | if (cmd in attend or | ||
(cmd not in ignore and not attend)): | ||||
Matt Mackall
|
r21277 | usepager = True | ||
David Soria Parra
|
r19940 | break | ||
Matt Mackall
|
r21277 | |||
Gregory Szorc
|
r24066 | setattr(ui, 'pageractive', usepager) | ||
Matt Mackall
|
r21277 | if usepager: | ||
ui.setconfig('ui', 'formatted', ui.formatted(), 'pager') | ||||
ui.setconfig('ui', 'interactive', False, 'pager') | ||||
if util.safehasattr(signal, "SIGPIPE"): | ||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL) | ||||
_runpager(ui, p) | ||||
Matt Mackall
|
r7216 | return orig(ui, options, cmd, cmdfunc) | ||
David Soria Parra <dsp <at> php.net>
|
r6417 | |||
Gregory Szorc
|
r24067 | # Wrap dispatch._runcommand after color is loaded so color can see | ||
# ui.pageractive. Otherwise, if we loaded first, color's wrapped | ||||
# dispatch._runcommand would run without having access to ui.pageractive. | ||||
def afterloaded(loaded): | ||||
extensions.wrapfunction(dispatch, '_runcommand', pagecmd) | ||||
extensions.afterloaded('color', afterloaded) | ||||
Brodie Rao
|
r9841 | |||
Brodie Rao
|
r12694 | def extsetup(ui): | ||
commands.globalopts.append( | ||||
('', 'pager', 'auto', | ||||
_("when to paginate (boolean, always, auto, or never)"), | ||||
_('TYPE'))) | ||||
Brodie Rao
|
r9841 | attended = ['annotate', 'cat', 'diff', 'export', 'glog', 'log', 'qdiff'] | ||