> On Feb 15, 2017, at 9:12 PM, Augie Fackler <r...@durin42.com> wrote: > > # HG changeset patch > # User Augie Fackler <au...@google.com> > # Date 1487198871 18000 > # Wed Feb 15 17:47:51 2017 -0500 > # Node ID 675643abfdb6adbdfd8bddfbc263701c9caca539 > # Parent e5363cb96233861fc99f7e9b85d7884d3121558c > pager: move pager-initiating code into core
These 9 patches represent most of the functionally interesting bits of my pager-in-core series. The rest of the stack is visible at https://hg.durin42.com/hg-wip/graph/pager - the majority of the patches beyond this point are one-line “turn on pager” commits. I expect to send the remaining 21 patches in two batches: one wave of 19 trivial “turn it on” patches, and then 2 patches that rearrange some docs and mark the pager extension as deprecated. > > No functionality change. > > A previous version of this API had a category argument on > ui.pager(). As I migrated the commands in core, I couldn't come up > with good enough consistency in any categorization scheme so I just > scrapped the whole idea. It may be worth revisiting in the future. > > diff --git a/hgext/pager.py b/hgext/pager.py > --- a/hgext/pager.py > +++ b/hgext/pager.py > @@ -60,19 +60,11 @@ you can use --pager=<value>:: > ''' > 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, > - error, > extensions, > util, > ) > @@ -83,48 +75,14 @@ from mercurial import ( > # 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 descriptors > - stdoutfd = os.dup(util.stdout.fileno()) > - stderrfd = os.dup(util.stderr.fileno()) > - > - 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) > - # restore original fds, closing pager.stdin copies in the process > - os.dup2(stdoutfd, util.stdout.fileno()) > - os.dup2(stderrfd, util.stderr.fileno()) > - pager.stdin.close() > - pager.wait() > - > -def catchterm(*args): > - raise error.SignalInterrupt > - > 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: > + if always: > usepager = True > elif not auto: > usepager = False > @@ -143,14 +101,8 @@ def uisetup(ui): > 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, catchterm) > - ui._runpager(p) > + ui.pager('extension-via-attend-' + cmd) > return orig(ui, options, cmd, cmdfunc) > > # Wrap dispatch._runcommand after color is loaded so color can see > 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 > @@ -143,6 +146,7 @@ class ui(object): > self.fout = src.fout > self.ferr = src.ferr > self.fin = src.fin > + self.pageractive = src.pageractive > > self._tcfg = src._tcfg.copy() > self._ucfg = src._ucfg.copy() > @@ -159,6 +163,7 @@ class ui(object): > self.fout = util.stdout > self.ferr = util.stderr > self.fin = util.stdin > + self.pageractive = False > > # shared read-only environment > self.environ = encoding.environ > @@ -792,6 +797,75 @@ class ui(object): > return False > return util.isatty(fh) > > + def pager(self, command): > + """Start a pager for subsequent command output. > + > + Commands which produce a long stream of output should call > + this function to activate the user's preferred pagination > + mechanism (which may be no pager). Calling this function > + precludes any future use of interactive functionality, such as > + prompting the user or activating curses. > + > + Args: > + command: The full, non-aliased name of the command. That is, "log" > + not "history, "summary" not "summ", etc. > + """ > + if (self.pageractive > + # TODO: if we want to allow HGPLAINEXCEPT=pager, > + # formatted() will need some adjustment. > + or not self.formatted() > + or self.plain() > + # TODO: expose debugger-enabled on the UI object > + or '--debugger' in sys.argv): > + # We only want to paginate if the ui appears to be > + # interactive, the user didn't say HGPLAIN or > + # HGPLAINEXCEPT=pager, and the user didn't specify --debug. > + return > + > + # TODO: add a "system defaults" config section so this default > + # of more(1) can be easily replaced with a global > + # configuration file. For example, on OS X the sane default is > + # less(1), not more(1), and on debian it's > + # sensible-pager(1). We should probably also give the system > + # default editor command similar treatment. > + envpager = encoding.environ.get('PAGER', 'more') > + pagercmd = self.config('pager', 'pager', envpager) > + self.pageractive = True > + # Preserve the formatted-ness of the UI. This is important > + # because we mess with stdout, which might confuse > + # auto-detection of things being formatted. > + self.setconfig('ui', 'formatted', self.formatted(), 'pager') > + self.setconfig('ui', 'interactive', False, 'pager') > + self._runpager(pagercmd) > + > + def _runpager(self, command): > + """Actually start the pager and set up file descriptors. > + > + This is separate in part so that extensions (like chg) can > + override how a pager is invoked. > + """ > + pager = subprocess.Popen(command, shell=True, bufsize=-1, > + close_fds=util.closefds, > stdin=subprocess.PIPE, > + stdout=util.stdout, stderr=util.stderr) > + > + # back up original file descriptors > + stdoutfd = os.dup(util.stdout.fileno()) > + stderrfd = os.dup(util.stderr.fileno()) > + > + 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) > + # restore original fds, closing pager.stdin copies in the process > + os.dup2(stdoutfd, util.stdout.fileno()) > + os.dup2(stderrfd, util.stderr.fileno()) > + pager.stdin.close() > + pager.wait() > + > def interface(self, feature): > """what interface to use for interactive console features? > > _______________________________________________ > Mercurial-devel mailing list > Mercurial-devel@mercurial-scm.org > https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel _______________________________________________ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel