Mercurial > hg
comparison mercurial/ui.py @ 30992:61b4122019d3
pager: move pager-initiating code into core
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.
author | Augie Fackler <augie@google.com> |
---|---|
date | Wed, 15 Feb 2017 17:47:51 -0500 |
parents | 60b5db2ab586 |
children | 3ed6e43998df |
comparison
equal
deleted
inserted
replaced
30991:3844b3299a53 | 30992:61b4122019d3 |
---|---|
5 # This software may be used and distributed according to the terms of the | 5 # This software may be used and distributed according to the terms of the |
6 # GNU General Public License version 2 or any later version. | 6 # GNU General Public License version 2 or any later version. |
7 | 7 |
8 from __future__ import absolute_import | 8 from __future__ import absolute_import |
9 | 9 |
10 import atexit | |
10 import collections | 11 import collections |
11 import contextlib | 12 import contextlib |
12 import errno | 13 import errno |
13 import getpass | 14 import getpass |
14 import inspect | 15 import inspect |
15 import os | 16 import os |
16 import re | 17 import re |
18 import signal | |
17 import socket | 19 import socket |
20 import subprocess | |
18 import sys | 21 import sys |
19 import tempfile | 22 import tempfile |
20 import traceback | 23 import traceback |
21 | 24 |
22 from .i18n import _ | 25 from .i18n import _ |
113 return self._get_mgr().add_password(*args, **kwargs) | 116 return self._get_mgr().add_password(*args, **kwargs) |
114 | 117 |
115 def find_user_password(self, *args, **kwargs): | 118 def find_user_password(self, *args, **kwargs): |
116 return self._get_mgr().find_user_password(*args, **kwargs) | 119 return self._get_mgr().find_user_password(*args, **kwargs) |
117 | 120 |
121 def _catchterm(*args): | |
122 raise error.SignalInterrupt | |
118 | 123 |
119 class ui(object): | 124 class ui(object): |
120 def __init__(self, src=None): | 125 def __init__(self, src=None): |
121 """Create a fresh new ui object if no src given | 126 """Create a fresh new ui object if no src given |
122 | 127 |
147 | 152 |
148 if src: | 153 if src: |
149 self.fout = src.fout | 154 self.fout = src.fout |
150 self.ferr = src.ferr | 155 self.ferr = src.ferr |
151 self.fin = src.fin | 156 self.fin = src.fin |
157 self.pageractive = src.pageractive | |
152 | 158 |
153 self._tcfg = src._tcfg.copy() | 159 self._tcfg = src._tcfg.copy() |
154 self._ucfg = src._ucfg.copy() | 160 self._ucfg = src._ucfg.copy() |
155 self._ocfg = src._ocfg.copy() | 161 self._ocfg = src._ocfg.copy() |
156 self._trustusers = src._trustusers.copy() | 162 self._trustusers = src._trustusers.copy() |
164 self._blockedtimes = src._blockedtimes | 170 self._blockedtimes = src._blockedtimes |
165 else: | 171 else: |
166 self.fout = util.stdout | 172 self.fout = util.stdout |
167 self.ferr = util.stderr | 173 self.ferr = util.stderr |
168 self.fin = util.stdin | 174 self.fin = util.stdin |
175 self.pageractive = False | |
169 | 176 |
170 # shared read-only environment | 177 # shared read-only environment |
171 self.environ = encoding.environ | 178 self.environ = encoding.environ |
172 | 179 |
173 self.httppasswordmgrdb = httppasswordmgrdbproxy() | 180 self.httppasswordmgrdb = httppasswordmgrdbproxy() |
822 def _isatty(self, fh): | 829 def _isatty(self, fh): |
823 if self.configbool('ui', 'nontty', False): | 830 if self.configbool('ui', 'nontty', False): |
824 return False | 831 return False |
825 return util.isatty(fh) | 832 return util.isatty(fh) |
826 | 833 |
834 def pager(self, command): | |
835 """Start a pager for subsequent command output. | |
836 | |
837 Commands which produce a long stream of output should call | |
838 this function to activate the user's preferred pagination | |
839 mechanism (which may be no pager). Calling this function | |
840 precludes any future use of interactive functionality, such as | |
841 prompting the user or activating curses. | |
842 | |
843 Args: | |
844 command: The full, non-aliased name of the command. That is, "log" | |
845 not "history, "summary" not "summ", etc. | |
846 """ | |
847 if (self.pageractive | |
848 # TODO: if we want to allow HGPLAINEXCEPT=pager, | |
849 # formatted() will need some adjustment. | |
850 or not self.formatted() | |
851 or self.plain() | |
852 # TODO: expose debugger-enabled on the UI object | |
853 or '--debugger' in sys.argv): | |
854 # We only want to paginate if the ui appears to be | |
855 # interactive, the user didn't say HGPLAIN or | |
856 # HGPLAINEXCEPT=pager, and the user didn't specify --debug. | |
857 return | |
858 | |
859 # TODO: add a "system defaults" config section so this default | |
860 # of more(1) can be easily replaced with a global | |
861 # configuration file. For example, on OS X the sane default is | |
862 # less(1), not more(1), and on debian it's | |
863 # sensible-pager(1). We should probably also give the system | |
864 # default editor command similar treatment. | |
865 envpager = encoding.environ.get('PAGER', 'more') | |
866 pagercmd = self.config('pager', 'pager', envpager) | |
867 self.pageractive = True | |
868 # Preserve the formatted-ness of the UI. This is important | |
869 # because we mess with stdout, which might confuse | |
870 # auto-detection of things being formatted. | |
871 self.setconfig('ui', 'formatted', self.formatted(), 'pager') | |
872 self.setconfig('ui', 'interactive', False, 'pager') | |
873 if util.safehasattr(signal, "SIGPIPE"): | |
874 signal.signal(signal.SIGPIPE, _catchterm) | |
875 self._runpager(pagercmd) | |
876 | |
877 def _runpager(self, command): | |
878 """Actually start the pager and set up file descriptors. | |
879 | |
880 This is separate in part so that extensions (like chg) can | |
881 override how a pager is invoked. | |
882 """ | |
883 pager = subprocess.Popen(command, shell=True, bufsize=-1, | |
884 close_fds=util.closefds, stdin=subprocess.PIPE, | |
885 stdout=util.stdout, stderr=util.stderr) | |
886 | |
887 # back up original file descriptors | |
888 stdoutfd = os.dup(util.stdout.fileno()) | |
889 stderrfd = os.dup(util.stderr.fileno()) | |
890 | |
891 os.dup2(pager.stdin.fileno(), util.stdout.fileno()) | |
892 if self._isatty(util.stderr): | |
893 os.dup2(pager.stdin.fileno(), util.stderr.fileno()) | |
894 | |
895 @atexit.register | |
896 def killpager(): | |
897 if util.safehasattr(signal, "SIGINT"): | |
898 signal.signal(signal.SIGINT, signal.SIG_IGN) | |
899 # restore original fds, closing pager.stdin copies in the process | |
900 os.dup2(stdoutfd, util.stdout.fileno()) | |
901 os.dup2(stderrfd, util.stderr.fileno()) | |
902 pager.stdin.close() | |
903 pager.wait() | |
904 | |
827 def interface(self, feature): | 905 def interface(self, feature): |
828 """what interface to use for interactive console features? | 906 """what interface to use for interactive console features? |
829 | 907 |
830 The interface is controlled by the value of `ui.interface` but also by | 908 The interface is controlled by the value of `ui.interface` but also by |
831 the value of feature-specific configuration. For example: | 909 the value of feature-specific configuration. For example: |