##// END OF EJS Templates
pager: wrap ui._runpager...
Jun Wu -
r30722:117e15c3 default
parent child Browse files
Show More
@@ -1,176 +1,182 b''
1 # pager.py - display output using a pager
1 # pager.py - display output using a pager
2 #
2 #
3 # Copyright 2008 David Soria Parra <dsp@php.net>
3 # Copyright 2008 David Soria Parra <dsp@php.net>
4 #
4 #
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 # To load the extension, add it to your configuration file:
8 # To load the extension, add it to your configuration file:
9 #
9 #
10 # [extension]
10 # [extension]
11 # pager =
11 # pager =
12 #
12 #
13 # Run 'hg help pager' to get info on configuration.
13 # Run 'hg help pager' to get info on configuration.
14
14
15 '''browse command output with an external pager
15 '''browse command output with an external pager
16
16
17 To set the pager that should be used, set the application variable::
17 To set the pager that should be used, set the application variable::
18
18
19 [pager]
19 [pager]
20 pager = less -FRX
20 pager = less -FRX
21
21
22 If no pager is set, the pager extensions uses the environment variable
22 If no pager is set, the pager extensions uses the environment variable
23 $PAGER. If neither pager.pager, nor $PAGER is set, no pager is used.
23 $PAGER. If neither pager.pager, nor $PAGER is set, no pager is used.
24
24
25 You can disable the pager for certain commands by adding them to the
25 You can disable the pager for certain commands by adding them to the
26 pager.ignore list::
26 pager.ignore list::
27
27
28 [pager]
28 [pager]
29 ignore = version, help, update
29 ignore = version, help, update
30
30
31 You can also enable the pager only for certain commands using
31 You can also enable the pager only for certain commands using
32 pager.attend. Below is the default list of commands to be paged::
32 pager.attend. Below is the default list of commands to be paged::
33
33
34 [pager]
34 [pager]
35 attend = annotate, cat, diff, export, glog, log, qdiff
35 attend = annotate, cat, diff, export, glog, log, qdiff
36
36
37 Setting pager.attend to an empty value will cause all commands to be
37 Setting pager.attend to an empty value will cause all commands to be
38 paged.
38 paged.
39
39
40 If pager.attend is present, pager.ignore will be ignored.
40 If pager.attend is present, pager.ignore will be ignored.
41
41
42 Lastly, you can enable and disable paging for individual commands with
42 Lastly, you can enable and disable paging for individual commands with
43 the attend-<command> option. This setting takes precedence over
43 the attend-<command> option. This setting takes precedence over
44 existing attend and ignore options and defaults::
44 existing attend and ignore options and defaults::
45
45
46 [pager]
46 [pager]
47 attend-cat = false
47 attend-cat = false
48
48
49 To ignore global commands like :hg:`version` or :hg:`help`, you have
49 To ignore global commands like :hg:`version` or :hg:`help`, you have
50 to specify them in your user configuration file.
50 to specify them in your user configuration file.
51
51
52 To control whether the pager is used at all for an individual command,
52 To control whether the pager is used at all for an individual command,
53 you can use --pager=<value>::
53 you can use --pager=<value>::
54
54
55 - use as needed: `auto`.
55 - use as needed: `auto`.
56 - require the pager: `yes` or `on`.
56 - require the pager: `yes` or `on`.
57 - suppress the pager: `no` or `off` (any unrecognized value
57 - suppress the pager: `no` or `off` (any unrecognized value
58 will also work).
58 will also work).
59
59
60 '''
60 '''
61 from __future__ import absolute_import
61 from __future__ import absolute_import
62
62
63 import atexit
63 import atexit
64 import os
64 import os
65 import signal
65 import signal
66 import subprocess
66 import subprocess
67 import sys
67 import sys
68
68
69 from mercurial.i18n import _
69 from mercurial.i18n import _
70 from mercurial import (
70 from mercurial import (
71 cmdutil,
71 cmdutil,
72 commands,
72 commands,
73 dispatch,
73 dispatch,
74 encoding,
74 encoding,
75 extensions,
75 extensions,
76 util,
76 util,
77 )
77 )
78
78
79 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
79 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
80 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
80 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
81 # be specifying the version(s) of Mercurial they are tested with, or
81 # be specifying the version(s) of Mercurial they are tested with, or
82 # leave the attribute unspecified.
82 # leave the attribute unspecified.
83 testedwith = 'ships-with-hg-core'
83 testedwith = 'ships-with-hg-core'
84
84
85 def _runpager(ui, p):
85 def _runpager(ui, p):
86 pager = subprocess.Popen(p, shell=True, bufsize=-1,
86 pager = subprocess.Popen(p, shell=True, bufsize=-1,
87 close_fds=util.closefds, stdin=subprocess.PIPE,
87 close_fds=util.closefds, stdin=subprocess.PIPE,
88 stdout=util.stdout, stderr=util.stderr)
88 stdout=util.stdout, stderr=util.stderr)
89
89
90 # back up original file objects and descriptors
90 # back up original file objects and descriptors
91 olduifout = ui.fout
91 olduifout = ui.fout
92 oldstdout = util.stdout
92 oldstdout = util.stdout
93 stdoutfd = os.dup(util.stdout.fileno())
93 stdoutfd = os.dup(util.stdout.fileno())
94 stderrfd = os.dup(util.stderr.fileno())
94 stderrfd = os.dup(util.stderr.fileno())
95
95
96 # create new line-buffered stdout so that output can show up immediately
96 # create new line-buffered stdout so that output can show up immediately
97 ui.fout = util.stdout = newstdout = os.fdopen(util.stdout.fileno(), 'wb', 1)
97 ui.fout = util.stdout = newstdout = os.fdopen(util.stdout.fileno(), 'wb', 1)
98 os.dup2(pager.stdin.fileno(), util.stdout.fileno())
98 os.dup2(pager.stdin.fileno(), util.stdout.fileno())
99 if ui._isatty(util.stderr):
99 if ui._isatty(util.stderr):
100 os.dup2(pager.stdin.fileno(), util.stderr.fileno())
100 os.dup2(pager.stdin.fileno(), util.stderr.fileno())
101
101
102 @atexit.register
102 @atexit.register
103 def killpager():
103 def killpager():
104 if util.safehasattr(signal, "SIGINT"):
104 if util.safehasattr(signal, "SIGINT"):
105 signal.signal(signal.SIGINT, signal.SIG_IGN)
105 signal.signal(signal.SIGINT, signal.SIG_IGN)
106 pager.stdin.close()
106 pager.stdin.close()
107 ui.fout = olduifout
107 ui.fout = olduifout
108 util.stdout = oldstdout
108 util.stdout = oldstdout
109 # close new stdout while it's associated with pager; otherwise stdout
109 # close new stdout while it's associated with pager; otherwise stdout
110 # fd would be closed when newstdout is deleted
110 # fd would be closed when newstdout is deleted
111 newstdout.close()
111 newstdout.close()
112 # restore original fds: stdout is open again
112 # restore original fds: stdout is open again
113 os.dup2(stdoutfd, util.stdout.fileno())
113 os.dup2(stdoutfd, util.stdout.fileno())
114 os.dup2(stderrfd, util.stderr.fileno())
114 os.dup2(stderrfd, util.stderr.fileno())
115 pager.wait()
115 pager.wait()
116
116
117 def uisetup(ui):
117 def uisetup(ui):
118 if '--debugger' in sys.argv or not ui.formatted():
118 if '--debugger' in sys.argv or not ui.formatted():
119 return
119 return
120
120
121 class pagerui(ui.__class__):
122 def _runpager(self, pagercmd):
123 _runpager(self, pagercmd)
124
125 ui.__class__ = pagerui
126
121 # chg has its own pager implementation
127 # chg has its own pager implementation
122 argv = sys.argv[:]
128 argv = sys.argv[:]
123 if 'chgunix' in dispatch._earlygetopt(['--cmdserver'], argv):
129 if 'chgunix' in dispatch._earlygetopt(['--cmdserver'], argv):
124 return
130 return
125
131
126 def pagecmd(orig, ui, options, cmd, cmdfunc):
132 def pagecmd(orig, ui, options, cmd, cmdfunc):
127 p = ui.config("pager", "pager", encoding.environ.get("PAGER"))
133 p = ui.config("pager", "pager", encoding.environ.get("PAGER"))
128 usepager = False
134 usepager = False
129 always = util.parsebool(options['pager'])
135 always = util.parsebool(options['pager'])
130 auto = options['pager'] == 'auto'
136 auto = options['pager'] == 'auto'
131
137
132 if not p:
138 if not p:
133 pass
139 pass
134 elif always:
140 elif always:
135 usepager = True
141 usepager = True
136 elif not auto:
142 elif not auto:
137 usepager = False
143 usepager = False
138 else:
144 else:
139 attend = ui.configlist('pager', 'attend', attended)
145 attend = ui.configlist('pager', 'attend', attended)
140 ignore = ui.configlist('pager', 'ignore')
146 ignore = ui.configlist('pager', 'ignore')
141 cmds, _ = cmdutil.findcmd(cmd, commands.table)
147 cmds, _ = cmdutil.findcmd(cmd, commands.table)
142
148
143 for cmd in cmds:
149 for cmd in cmds:
144 var = 'attend-%s' % cmd
150 var = 'attend-%s' % cmd
145 if ui.config('pager', var):
151 if ui.config('pager', var):
146 usepager = ui.configbool('pager', var)
152 usepager = ui.configbool('pager', var)
147 break
153 break
148 if (cmd in attend or
154 if (cmd in attend or
149 (cmd not in ignore and not attend)):
155 (cmd not in ignore and not attend)):
150 usepager = True
156 usepager = True
151 break
157 break
152
158
153 setattr(ui, 'pageractive', usepager)
159 setattr(ui, 'pageractive', usepager)
154
160
155 if usepager:
161 if usepager:
156 ui.setconfig('ui', 'formatted', ui.formatted(), 'pager')
162 ui.setconfig('ui', 'formatted', ui.formatted(), 'pager')
157 ui.setconfig('ui', 'interactive', False, 'pager')
163 ui.setconfig('ui', 'interactive', False, 'pager')
158 if util.safehasattr(signal, "SIGPIPE"):
164 if util.safehasattr(signal, "SIGPIPE"):
159 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
165 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
160 _runpager(ui, p)
166 ui._runpager(p)
161 return orig(ui, options, cmd, cmdfunc)
167 return orig(ui, options, cmd, cmdfunc)
162
168
163 # Wrap dispatch._runcommand after color is loaded so color can see
169 # Wrap dispatch._runcommand after color is loaded so color can see
164 # ui.pageractive. Otherwise, if we loaded first, color's wrapped
170 # ui.pageractive. Otherwise, if we loaded first, color's wrapped
165 # dispatch._runcommand would run without having access to ui.pageractive.
171 # dispatch._runcommand would run without having access to ui.pageractive.
166 def afterloaded(loaded):
172 def afterloaded(loaded):
167 extensions.wrapfunction(dispatch, '_runcommand', pagecmd)
173 extensions.wrapfunction(dispatch, '_runcommand', pagecmd)
168 extensions.afterloaded('color', afterloaded)
174 extensions.afterloaded('color', afterloaded)
169
175
170 def extsetup(ui):
176 def extsetup(ui):
171 commands.globalopts.append(
177 commands.globalopts.append(
172 ('', 'pager', 'auto',
178 ('', 'pager', 'auto',
173 _("when to paginate (boolean, always, auto, or never)"),
179 _("when to paginate (boolean, always, auto, or never)"),
174 _('TYPE')))
180 _('TYPE')))
175
181
176 attended = ['annotate', 'cat', 'diff', 'export', 'glog', 'log', 'qdiff']
182 attended = ['annotate', 'cat', 'diff', 'export', 'glog', 'log', 'qdiff']
General Comments 0
You need to be logged in to leave comments. Login now