[PATCH] pager: migrate heavily-used extension into core
Bryan O'Sullivan
bos at serpentine.com
Fri Feb 3 22:28:37 UTC 2017
# HG changeset patch
# User Bryan O'Sullivan <bryano at fb.com>
# Date 1486160890 28800
# Fri Feb 03 14:28:10 2017 -0800
# Node ID a4d9e5c20bd72caefcc8efe29a46b6a31a816606
# Parent abf029200e198878a4576a87e095bd8d77d9cea9
pager: migrate heavily-used extension into core
No default behaviours were harmed during the making of this change.
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -107,6 +107,8 @@ globalopts = [
('', 'version', None, _('output version information and exit')),
('h', 'help', None, _('display help and exit')),
('', 'hidden', False, _('consider hidden changesets')),
+ ('', 'pager', 'auto',
+ _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
]
dryrunopts = [('n', 'dry-run', None,
diff --git a/mercurial/dispatch.py b/mercurial/dispatch.py
--- a/mercurial/dispatch.py
+++ b/mercurial/dispatch.py
@@ -816,6 +816,39 @@ def _dispatch(req):
def _runcommand(ui, options, cmd, cmdfunc):
"""Run a command function, possibly with profiling enabled."""
try:
+ p = ui.config("pager", "pager", encoding.environ.get("PAGER"))
+ usepager = False
+ always = util.parsebool(options['pager'])
+ auto = options['pager'] == 'auto'
+
+ if not p or '--debugger' in sys.argv or not ui.formatted():
+ pass
+ elif always:
+ usepager = True
+ elif not auto:
+ usepager = False
+ else:
+ attend = ui.configlist('pager', 'attend', ui.attended)
+ ignore = ui.configlist('pager', 'ignore')
+ cmds, _ = cmdutil.findcmd(cmd, commands.table)
+
+ for cmd in cmds:
+ var = 'attend-%s' % cmd
+ if ui.config('pager', var):
+ usepager = ui.configbool('pager', var)
+ break
+ if cmd in attend or (cmd not in ignore and not attend):
+ usepager = True
+ break
+
+ ui.pageractive = usepager
+
+ 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)
+ ui._runpager(p)
return cmdfunc()
except error.SignatureError:
raise error.CommandError(cmd, _('invalid arguments'))
diff --git a/mercurial/extensions.py b/mercurial/extensions.py
--- a/mercurial/extensions.py
+++ b/mercurial/extensions.py
@@ -27,7 +27,7 @@ from . import (
_aftercallbacks = {}
_order = []
_builtin = set(['hbisect', 'bookmarks', 'parentrevspec', 'progress', 'interhg',
- 'inotify', 'hgcia'])
+ 'inotify', 'hgcia', 'pager'])
def extensions(ui=None):
if ui:
diff --git a/mercurial/help.py b/mercurial/help.py
--- a/mercurial/help.py
+++ b/mercurial/help.py
@@ -230,6 +230,7 @@ helptable = sorted([
loaddoc('scripting')),
(['internals'], _("Technical implementation topics"),
internalshelp),
+ (["pager"], _("Using an External Pager"), loaddoc('pager')),
])
# Maps topics with sub-topics to a list of their sub-topics.
diff --git a/hgext/pager.py b/mercurial/help/pager.txt
rename from hgext/pager.py
rename to mercurial/help/pager.txt
--- a/hgext/pager.py
+++ b/mercurial/help/pager.txt
@@ -1,19 +1,3 @@
-# pager.py - display output using a pager
-#
-# Copyright 2008 David Soria Parra <dsp at 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]
@@ -56,119 +40,3 @@ you can use --pager=<value>::
- require the pager: `yes` or `on`.
- suppress the pager: `no` or `off` (any unrecognized value
will also work).
-
-'''
-from __future__ import absolute_import
-
-import atexit
-import os
-import signal
-import subprocess
-import sys
-
-from mercurial.i18n import _
-from mercurial import (
- cmdutil,
- commands,
- dispatch,
- encoding,
- extensions,
- util,
- )
-
-# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
-# 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.
-testedwith = 'ships-with-hg-core'
-
-def _runpager(ui, p):
- pager = subprocess.Popen(p, shell=True, bufsize=-1,
- close_fds=util.closefds, stdin=subprocess.PIPE,
- stdout=util.stdout, stderr=util.stderr)
-
- # back up original file objects and descriptors
- olduifout = ui.fout
- oldstdout = util.stdout
- stdoutfd = os.dup(util.stdout.fileno())
- stderrfd = os.dup(util.stderr.fileno())
-
- # create new line-buffered stdout so that output can show up immediately
- 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())
-
- @atexit.register
- def killpager():
- if util.safehasattr(signal, "SIGINT"):
- signal.signal(signal.SIGINT, signal.SIG_IGN)
- pager.stdin.close()
- ui.fout = olduifout
- util.stdout = oldstdout
- # 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
- os.dup2(stdoutfd, util.stdout.fileno())
- os.dup2(stderrfd, util.stderr.fileno())
- pager.wait()
-
-def uisetup(ui):
- class pagerui(ui.__class__):
- def _runpager(self, pagercmd):
- _runpager(self, pagercmd)
-
- ui.__class__ = pagerui
-
- def pagecmd(orig, ui, options, cmd, cmdfunc):
- p = ui.config("pager", "pager", encoding.environ.get("PAGER"))
- usepager = False
- always = util.parsebool(options['pager'])
- auto = options['pager'] == 'auto'
-
- if not p or '--debugger' in sys.argv or not ui.formatted():
- pass
- elif always:
- usepager = True
- elif not auto:
- usepager = False
- else:
- attend = ui.configlist('pager', 'attend', attended)
- ignore = ui.configlist('pager', 'ignore')
- cmds, _ = cmdutil.findcmd(cmd, commands.table)
-
- for cmd in cmds:
- var = 'attend-%s' % cmd
- if ui.config('pager', var):
- usepager = ui.configbool('pager', var)
- break
- if (cmd in attend or
- (cmd not in ignore and not attend)):
- usepager = True
- break
-
- setattr(ui, 'pageractive', usepager)
-
- 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)
- ui._runpager(p)
- return orig(ui, options, cmd, cmdfunc)
-
- # 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)
-
-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']
diff --git a/mercurial/statprof.py b/mercurial/statprof.py
--- a/mercurial/statprof.py
+++ b/mercurial/statprof.py
@@ -130,7 +130,7 @@ skips = set(["util.py:check", "extension
"color.py:colorcmd", "dispatch.py:checkargs",
"dispatch.py:<lambda>", "dispatch.py:_runcatch",
"dispatch.py:_dispatch", "dispatch.py:_runcommand",
- "pager.py:pagecmd", "dispatch.py:run",
+ "dispatch.py:run",
"dispatch.py:dispatch", "dispatch.py:runcommand",
"hg.py:<module>", "evolve.py:warnobserrors",
])
diff --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -7,13 +7,16 @@
from __future__ import absolute_import
+import atexit
import contextlib
import errno
import getpass
import inspect
import os
import re
+import signal
import socket
+import subprocess
import sys
import tempfile
import traceback
@@ -94,6 +97,10 @@ default = %s
# pager =""",
}
+# commands whose output may be run through the pager
+# (this is a top-level global in case extensions need to add to it)
+pagedcommands = ['annotate', 'cat', 'diff', 'export', 'glog', 'log', 'qdiff']
+
class ui(object):
def __init__(self, src=None):
"""Create a fresh new ui object if no src given
@@ -102,6 +109,7 @@ class ui(object):
In most cases, you should use ui.copy() to create a copy of an existing
ui object.
"""
+ self.pageractive = False
# _buffers: used for temporary capture of output
self._buffers = []
# 3-tuple describing how each buffer in the stack behaves.
@@ -1251,6 +1259,39 @@ class ui(object):
if ('ui', 'quiet') in overrides:
self.fixconfig(section='ui')
+ def _runpager(self, p):
+ pager = subprocess.Popen(p, shell=True, bufsize=-1,
+ close_fds=util.closefds, stdin=subprocess.PIPE,
+ stdout=util.stdout, stderr=util.stderr)
+
+ # back up original file objects and descriptors
+ olduifout = self.fout
+ oldstdout = util.stdout
+ stdoutfd = os.dup(util.stdout.fileno())
+ stderrfd = os.dup(util.stderr.fileno())
+
+ # create new line-buffered stdout so that output can show up immediately
+ self.fout = util.stdout = newstdout = os.fdopen(util.stdout.fileno(),
+ 'wb', 1)
+ os.dup2(pager.stdin.fileno(), util.stdout.fileno())
+ if self._isatty(util.stderr):
+ os.dup2(pager.stdin.fileno(), util.stderr.fileno())
+
+ @atexit.register
+ def killpager():
+ if util.safehasattr(signal, "SIGINT"):
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
+ pager.stdin.close()
+ self.fout = olduifout
+ util.stdout = oldstdout
+ # 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
+ os.dup2(stdoutfd, util.stdout.fileno())
+ os.dup2(stderrfd, util.stderr.fileno())
+ pager.wait()
+
class paths(dict):
"""Represents a collection of paths and their configs.
diff --git a/tests/test-chg.t b/tests/test-chg.t
--- a/tests/test-chg.t
+++ b/tests/test-chg.t
@@ -41,13 +41,11 @@ pager
> sys.stdout.write('paged! %r\n' % line)
> EOF
-enable pager extension globally, but spawns the master server with no tty:
+enable pager globally, but spawn the master server with no tty:
$ chg init pager
$ cd pager
$ cat >> $HGRCPATH <<EOF
- > [extensions]
- > pager =
> [pager]
> pager = python $TESTTMP/fakepager.py
> EOF
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -138,6 +138,7 @@ Show the global options
--help
--hidden
--noninteractive
+ --pager
--profile
--quiet
--repository
@@ -171,6 +172,7 @@ Show the options for the "serve" command
--ipv6
--name
--noninteractive
+ --pager
--pid-file
--port
--prefix
diff --git a/tests/test-extension.t b/tests/test-extension.t
--- a/tests/test-extension.t
+++ b/tests/test-extension.t
@@ -543,6 +543,8 @@ hide outer repo
--version output version information and exit
-h --help display help and exit
--hidden consider hidden changesets
+ --pager TYPE when to paginate (boolean, always, auto, or never)
+ (default: auto)
@@ -578,6 +580,8 @@ hide outer repo
--version output version information and exit
-h --help display help and exit
--hidden consider hidden changesets
+ --pager TYPE when to paginate (boolean, always, auto, or never)
+ (default: auto)
@@ -856,6 +860,8 @@ extension help itself
--version output version information and exit
-h --help display help and exit
--hidden consider hidden changesets
+ --pager TYPE when to paginate (boolean, always, auto, or never)
+ (default: auto)
Make sure that single '-v' option shows help and built-ins only for 'dodo' command
$ hg help -v dodo
@@ -889,6 +895,8 @@ Make sure that single '-v' option shows
--version output version information and exit
-h --help display help and exit
--hidden consider hidden changesets
+ --pager TYPE when to paginate (boolean, always, auto, or never)
+ (default: auto)
In case when extension name doesn't match any of its commands,
help message should ask for '-v' to get list of built-in aliases
@@ -960,6 +968,8 @@ help options '-v' and '-v -e' should be
--version output version information and exit
-h --help display help and exit
--hidden consider hidden changesets
+ --pager TYPE when to paginate (boolean, always, auto, or never)
+ (default: auto)
$ hg help -v -e dudu
dudu extension -
@@ -992,6 +1002,8 @@ help options '-v' and '-v -e' should be
--version output version information and exit
-h --help display help and exit
--hidden consider hidden changesets
+ --pager TYPE when to paginate (boolean, always, auto, or never)
+ (default: auto)
Disabled extension commands:
diff --git a/tests/test-globalopts.t b/tests/test-globalopts.t
--- a/tests/test-globalopts.t
+++ b/tests/test-globalopts.t
@@ -351,6 +351,7 @@ Testing -h/--help:
hgweb Configuring hgweb
internals Technical implementation topics
merge-tools Merge Tools
+ pager Using an External Pager
patterns File Name Patterns
phases Working with Phases
revisions Specifying Revisions
@@ -432,6 +433,7 @@ Testing -h/--help:
hgweb Configuring hgweb
internals Technical implementation topics
merge-tools Merge Tools
+ pager Using an External Pager
patterns File Name Patterns
phases Working with Phases
revisions Specifying Revisions
diff --git a/tests/test-help.t b/tests/test-help.t
--- a/tests/test-help.t
+++ b/tests/test-help.t
@@ -113,6 +113,7 @@ Short help:
hgweb Configuring hgweb
internals Technical implementation topics
merge-tools Merge Tools
+ pager Using an External Pager
patterns File Name Patterns
phases Working with Phases
revisions Specifying Revisions
@@ -188,6 +189,7 @@ Short help:
hgweb Configuring hgweb
internals Technical implementation topics
merge-tools Merge Tools
+ pager Using an External Pager
patterns File Name Patterns
phases Working with Phases
revisions Specifying Revisions
@@ -262,7 +264,6 @@ Test extension help:
largefiles track large binary files
mq manage a stack of patches
notify hooks for sending email push notifications
- pager browse command output with an external pager
patchbomb command to send changesets as (a series of) patch emails
purge command to delete untracked files from the working
directory
@@ -326,6 +327,8 @@ Test short command list with verbose opt
--version output version information and exit
-h --help display help and exit
--hidden consider hidden changesets
+ --pager TYPE when to paginate (boolean, always, auto, or never)
+ (default: auto)
(use 'hg help' for the full list of commands)
@@ -422,6 +425,8 @@ Verbose help for add
--version output version information and exit
-h --help display help and exit
--hidden consider hidden changesets
+ --pager TYPE when to paginate (boolean, always, auto, or never)
+ (default: auto)
Test the textwidth config option
@@ -827,6 +832,7 @@ Test that default list of commands omits
hgweb Configuring hgweb
internals Technical implementation topics
merge-tools Merge Tools
+ pager Using an External Pager
patterns File Name Patterns
phases Working with Phases
revisions Specifying Revisions
@@ -1914,6 +1920,13 @@ Dish up an empty repo; serve it cold.
Merge Tools
</td></tr>
<tr><td>
+ <a href="/help/pager">
+ pager
+ </a>
+ </td><td>
+ Using an External Pager
+ </td></tr>
+ <tr><td>
<a href="/help/patterns">
patterns
</a>
@@ -2523,6 +2536,9 @@ Dish up an empty repo; serve it cold.
<tr><td></td>
<td>--hidden</td>
<td>consider hidden changesets</td></tr>
+ <tr><td></td>
+ <td>--pager TYPE</td>
+ <td>when to paginate (boolean, always, auto, or never) (default: auto)</td></tr>
</table>
</div>
@@ -2718,6 +2734,9 @@ Dish up an empty repo; serve it cold.
<tr><td></td>
<td>--hidden</td>
<td>consider hidden changesets</td></tr>
+ <tr><td></td>
+ <td>--pager TYPE</td>
+ <td>when to paginate (boolean, always, auto, or never) (default: auto)</td></tr>
</table>
</div>
diff --git a/tests/test-hgweb-json.t b/tests/test-hgweb-json.t
--- a/tests/test-hgweb-json.t
+++ b/tests/test-hgweb-json.t
@@ -1593,6 +1593,10 @@ help/ shows help topics
"topic": "merge-tools"
},
{
+ "summary": "Using an External Pager",
+ "topic": "pager"
+ },
+ {
"summary": "File Name Patterns",
"topic": "patterns"
},
diff --git a/tests/test-install.t b/tests/test-install.t
--- a/tests/test-install.t
+++ b/tests/test-install.t
@@ -166,6 +166,7 @@ path variables are expanded (~ is the sa
help/hg.1.txt
help/hgignore.5.txt
help/hgrc.5.txt
+ help/pager.txt
Not tracked:
$ python wixxml.py templates
diff --git a/tests/test-pager.t b/tests/test-pager.t
--- a/tests/test-pager.t
+++ b/tests/test-pager.t
@@ -10,8 +10,6 @@ pager was running.
$ cat >> $HGRCPATH <<EOF
> [ui]
> formatted = yes
- > [extensions]
- > pager=
> [pager]
> pager = python $TESTTMP/fakepager.py
> EOF
More information about the Mercurial-devel
mailing list