|
1 # pager.py - display output using a pager |
|
2 # |
|
3 # Copyright 2008 David Soria Parra <dsp@php.net> |
|
4 # |
|
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. |
|
7 # |
|
8 # To load the extension, add it to your configuration file: |
|
9 # |
|
10 # [extension] |
|
11 # pager = |
|
12 # |
|
13 # Run "hg help pager" to get info on configuration. |
|
14 |
|
15 '''browse command output with an external pager |
|
16 |
|
17 To set the pager that should be used, set the application variable:: |
|
18 |
|
19 [pager] |
|
20 pager = less -FRSX |
|
21 |
|
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. |
|
24 |
|
25 If you notice "BROKEN PIPE" error messages, you can disable them by |
|
26 setting:: |
|
27 |
|
28 [pager] |
|
29 quiet = True |
|
30 |
|
31 You can disable the pager for certain commands by adding them to the |
|
32 pager.ignore list:: |
|
33 |
|
34 [pager] |
|
35 ignore = version, help, update |
|
36 |
|
37 You can also enable the pager only for certain commands using |
|
38 pager.attend. Below is the default list of commands to be paged:: |
|
39 |
|
40 [pager] |
|
41 attend = annotate, cat, diff, export, glog, log, qdiff |
|
42 |
|
43 Setting pager.attend to an empty value will cause all commands to be |
|
44 paged. |
|
45 |
|
46 If pager.attend is present, pager.ignore will be ignored. |
|
47 |
|
48 To ignore global commands like :hg:`version` or :hg:`help`, you have |
|
49 to specify them in your user configuration file. |
|
50 |
|
51 The --pager=... option can also be used to control when the pager is |
|
52 used. Use a boolean value like yes, no, on, off, or use auto for |
|
53 normal behavior. |
|
54 ''' |
|
55 |
|
56 import sys, os, signal, shlex, errno |
|
57 from mercurial import commands, dispatch, util, extensions |
|
58 from mercurial.i18n import _ |
|
59 |
|
60 def _runpager(p): |
|
61 if not hasattr(os, 'fork'): |
|
62 sys.stderr = sys.stdout = util.popen(p, 'wb') |
|
63 return |
|
64 fdin, fdout = os.pipe() |
|
65 pid = os.fork() |
|
66 if pid == 0: |
|
67 os.close(fdin) |
|
68 os.dup2(fdout, sys.stdout.fileno()) |
|
69 os.dup2(fdout, sys.stderr.fileno()) |
|
70 os.close(fdout) |
|
71 return |
|
72 os.dup2(fdin, sys.stdin.fileno()) |
|
73 os.close(fdin) |
|
74 os.close(fdout) |
|
75 try: |
|
76 os.execvp('/bin/sh', ['/bin/sh', '-c', p]) |
|
77 except OSError, e: |
|
78 if e.errno == errno.ENOENT: |
|
79 # no /bin/sh, try executing the pager directly |
|
80 args = shlex.split(p) |
|
81 os.execvp(args[0], args) |
|
82 else: |
|
83 raise |
|
84 |
|
85 def uisetup(ui): |
|
86 if ui.plain(): |
|
87 return |
|
88 |
|
89 def pagecmd(orig, ui, options, cmd, cmdfunc): |
|
90 p = ui.config("pager", "pager", os.environ.get("PAGER")) |
|
91 if p and sys.stdout.isatty() and '--debugger' not in sys.argv: |
|
92 attend = ui.configlist('pager', 'attend', attended) |
|
93 auto = options['pager'] == 'auto' |
|
94 always = util.parsebool(options['pager']) |
|
95 if (always or auto and |
|
96 (cmd in attend or |
|
97 (cmd not in ui.configlist('pager', 'ignore') and not attend))): |
|
98 ui.setconfig('ui', 'formatted', ui.formatted()) |
|
99 ui.setconfig('ui', 'interactive', False) |
|
100 _runpager(p) |
|
101 if ui.configbool('pager', 'quiet'): |
|
102 signal.signal(signal.SIGPIPE, signal.SIG_DFL) |
|
103 return orig(ui, options, cmd, cmdfunc) |
|
104 |
|
105 extensions.wrapfunction(dispatch, '_runcommand', pagecmd) |
|
106 |
|
107 def extsetup(ui): |
|
108 commands.globalopts.append( |
|
109 ('', 'pager', 'auto', |
|
110 _("when to paginate (boolean, always, auto, or never)"), |
|
111 _('TYPE'))) |
|
112 |
|
113 attended = ['annotate', 'cat', 'diff', 'export', 'glog', 'log', 'qdiff'] |