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