Convert consecutive FSF copyright years to ranges.
[bpt/emacs.git] / lisp / eshell / em-smart.el
1 ;;; em-smart.el --- smart display of output
2
3 ;; Copyright (C) 1999-2011 Free Software Foundation, Inc.
4
5 ;; Author: John Wiegley <johnw@gnu.org>
6
7 ;; This file is part of GNU Emacs.
8
9 ;; GNU Emacs is free software: you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation, either version 3 of the License, or
12 ;; (at your option) any later version.
13
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
21
22 ;;; Commentary:
23
24 ;; The best way to get a sense of what this code is trying to do is by
25 ;; using it. Basically, the philosophy represents a blend between the
26 ;; ease of use of modern day shells, and the review-before-you-proceed
27 ;; mentality of Plan 9's 9term.
28 ;;
29 ;; @ When you invoke a command, it is assumed that you want to read
30 ;; the output of that command.
31 ;;
32 ;; @ If the output is not what you wanted, it is assumed that you will
33 ;; want to edit, and then resubmit a refined version of that
34 ;; command.
35 ;;
36 ;; @ If the output is valid, pressing any self-inserting character key
37 ;; will jump to end of the buffer and insert that character, in
38 ;; order to begin entry of a new command.
39 ;;
40 ;; @ If you show an intention to edit the previous command -- by
41 ;; moving around within it -- then the next self-inserting
42 ;; characters will insert *there*, instead of at the bottom of the
43 ;; buffer.
44 ;;
45 ;; @ If you show an intention to review old commands, such as M-p or
46 ;; M-r, point will jump to the bottom of the buffer before invoking
47 ;; that command.
48 ;;
49 ;; @ If none of the above has happened yet (i.e., your point is just
50 ;; sitting on the previous command), you can use SPACE and BACKSPACE
51 ;; (or DELETE) to page forward and backward *through the output of
52 ;; the last command only*. It will constrain the movement of the
53 ;; point and window so that the maximum amount of output is always
54 ;; displayed at all times.
55 ;;
56 ;; @ While output is being generated from a command, the window will
57 ;; be constantly reconfigured (until it would otherwise make no
58 ;; difference) in order to always show you the most output from the
59 ;; command possible. This happens if you change window sizes,
60 ;; scroll, etc.
61 ;;
62 ;; @ Like I said, it's not really comprehensible until you try it! ;)
63 ;;
64 ;; One disadvantage of this module is that it increases Eshell's
65 ;; memory consumption by a factor of two or more. With small commands
66 ;; (such as pwd), where the screen is mostly full, consumption can
67 ;; increase by orders of magnitude.
68
69 ;;; Code:
70
71 (eval-when-compile (require 'eshell))
72
73 ;;;###autoload
74 (eshell-defgroup eshell-smart nil
75 "This module combines the facility of normal, modern shells with
76 some of the edit/review concepts inherent in the design of Plan 9's
77 9term. See the docs for more details.
78
79 Most likely you will have to turn this option on and play around with
80 it to get a real sense of how it works."
81 :tag "Smart display of output"
82 ;; :link '(info-link "(eshell)Smart display of output")
83 :group 'eshell-module)
84
85 ;;; User Variables:
86
87 (defcustom eshell-smart-load-hook '(eshell-smart-initialize)
88 "A list of functions to call when loading `eshell-smart'."
89 :type 'hook
90 :group 'eshell-smart)
91
92 (defcustom eshell-smart-unload-hook
93 (list
94 (function
95 (lambda ()
96 (remove-hook 'window-configuration-change-hook
97 'eshell-refresh-windows))))
98 "A hook that gets run when `eshell-smart' is unloaded."
99 :type 'hook
100 :group 'eshell-smart)
101
102 (defcustom eshell-review-quick-commands nil
103 "If t, always review commands.
104 Reviewing means keeping point on the text of the command that was just
105 invoked, to allow corrections to be made easily.
106
107 If set to nil, quick commands won't be reviewed. A quick command is a
108 command that produces no output, and exits successfully.
109
110 If set to `not-even-short-output', then the definition of \"quick
111 command\" is extended to include commands that produce output, if and
112 only if that output can be presented in its entirely in the Eshell window."
113 :type '(choice (const :tag "No" nil)
114 (const :tag "Yes" t)
115 (const :tag "Not even short output"
116 not-even-short-output))
117 :group 'eshell-smart)
118
119 (defcustom eshell-smart-display-navigate-list
120 '(insert-parentheses
121 mouse-yank-at-click
122 mouse-yank-secondary
123 yank-pop
124 yank-rectangle
125 yank)
126 "A list of commands which cause Eshell to jump to the end of buffer."
127 :type '(repeat function)
128 :group 'eshell-smart)
129
130 (defcustom eshell-smart-space-goes-to-end t
131 "If non-nil, space will go to end of buffer when point-max is visible.
132 That is, if a command is running and the user presses SPACE at a time
133 when the end of the buffer is visible, point will go to the end of the
134 buffer and smart-display will be turned off (that is, subsequently
135 pressing backspace will not cause the buffer to scroll down).
136
137 This feature is provided to make it very easy to watch the output of a
138 long-running command, such as make, where it's more desirable to see
139 the output go by than to review it afterward.
140
141 Setting this variable to nil means that space and backspace will
142 always have a consistent behavior, which is to move back and forth
143 through displayed output. But it also means that enabling output
144 tracking requires the user to manually move point to the end of the
145 buffer using \\[end-of-buffer]."
146 :type 'boolean
147 :group 'eshell-smart)
148
149 (defcustom eshell-where-to-jump 'begin
150 "This variable indicates where point should jump to after a command.
151 The options are `begin', `after' or `end'."
152 :type '(radio (const :tag "Beginning of command" begin)
153 (const :tag "After command word" after)
154 (const :tag "End of command" end))
155 :group 'eshell-smart)
156
157 ;;; Internal Variables:
158
159 (defvar eshell-smart-displayed nil)
160 (defvar eshell-smart-command-done nil)
161 (defvar eshell-currently-handling-window nil)
162
163 ;;; Functions:
164
165 (defun eshell-smart-initialize ()
166 "Setup Eshell smart display."
167 (unless eshell-non-interactive-p
168 ;; override a few variables, since they would interfere with the
169 ;; smart display functionality.
170 (set (make-local-variable 'eshell-scroll-to-bottom-on-output) nil)
171 (set (make-local-variable 'eshell-scroll-to-bottom-on-input) nil)
172 (set (make-local-variable 'eshell-scroll-show-maximum-output) t)
173
174 (add-hook 'window-scroll-functions 'eshell-smart-scroll-window nil t)
175 (add-hook 'window-configuration-change-hook 'eshell-refresh-windows)
176
177 (add-hook 'eshell-output-filter-functions 'eshell-refresh-windows t t)
178
179 (add-hook 'after-change-functions 'eshell-disable-after-change nil t)
180
181 (add-hook 'eshell-input-filter-functions 'eshell-smart-display-setup nil t)
182
183 (make-local-variable 'eshell-smart-command-done)
184 (add-hook 'eshell-post-command-hook
185 (function
186 (lambda ()
187 (setq eshell-smart-command-done t))) t t)
188
189 (unless (eq eshell-review-quick-commands t)
190 (add-hook 'eshell-post-command-hook
191 'eshell-smart-maybe-jump-to-end nil t))))
192
193 (defun eshell-smart-scroll-window (wind start)
194 "Scroll the given Eshell window accordingly."
195 (unless eshell-currently-handling-window
196 (let ((inhibit-point-motion-hooks t)
197 (eshell-currently-handling-window t))
198 (save-selected-window
199 (select-window wind)
200 (eshell-smart-redisplay)))))
201
202 (defun eshell-refresh-windows (&optional frame)
203 "Refresh all visible Eshell buffers."
204 (let (affected)
205 (walk-windows
206 (function
207 (lambda (wind)
208 (with-current-buffer (window-buffer wind)
209 (if eshell-mode
210 (let (window-scroll-functions)
211 (eshell-smart-scroll-window wind (window-start))
212 (setq affected t))))))
213 0 frame)
214 (if affected
215 (let (window-scroll-functions)
216 (eshell-redisplay)))))
217
218 (defun eshell-smart-display-setup ()
219 "Set the point to somewhere in the beginning of the last command."
220 (cond
221 ((eq eshell-where-to-jump 'begin)
222 (goto-char eshell-last-input-start))
223 ((eq eshell-where-to-jump 'after)
224 (goto-char (next-single-property-change
225 eshell-last-input-start 'arg-end))
226 (if (= (point) (- eshell-last-input-end 2))
227 (forward-char)))
228 ((eq eshell-where-to-jump 'end)
229 (goto-char (1- eshell-last-input-end)))
230 (t
231 (error "Invalid value for `eshell-where-to-jump'")))
232 (setq eshell-smart-command-done nil)
233 (add-hook 'pre-command-hook 'eshell-smart-display-move nil t)
234 (eshell-refresh-windows))
235
236 (defun eshell-disable-after-change (b e l)
237 "Disable smart display mode if the buffer changes in any way."
238 (when eshell-smart-command-done
239 (remove-hook 'pre-command-hook 'eshell-smart-display-move t)
240 (setq eshell-smart-command-done nil)))
241
242 (defun eshell-smart-maybe-jump-to-end ()
243 "Jump to the end of the input buffer.
244 This is done whenever a command exits successfully and both the command
245 and the end of the buffer are still visible."
246 (when (and (= eshell-last-command-status 0)
247 (if (eq eshell-review-quick-commands 'not-even-short-output)
248 (and (pos-visible-in-window-p (point-max))
249 (pos-visible-in-window-p eshell-last-input-start))
250 (= (count-lines eshell-last-input-end
251 eshell-last-output-end) 0)))
252 (goto-char (point-max))
253 (remove-hook 'pre-command-hook 'eshell-smart-display-move t)))
254
255 (defun eshell-smart-redisplay ()
256 "Display as much output as possible, smartly."
257 (if (eobp)
258 (save-excursion
259 (recenter -1)
260 ;; trigger the redisplay now, so that we catch any attempted
261 ;; point motion; this is to cover for a redisplay bug
262 (eshell-redisplay))
263 (let ((top-point (point)))
264 (and (memq 'eshell-smart-display-move pre-command-hook)
265 (>= (point) eshell-last-input-start)
266 (< (point) eshell-last-input-end)
267 (set-window-start (selected-window)
268 (line-beginning-position) t))
269 (if (pos-visible-in-window-p (point-max))
270 (save-excursion
271 (goto-char (point-max))
272 (recenter -1)
273 (unless (pos-visible-in-window-p top-point)
274 (goto-char top-point)
275 (set-window-start (selected-window)
276 (line-beginning-position) t)))))))
277
278 (defun eshell-smart-goto-end ()
279 "Like `end-of-buffer', but do not push a mark."
280 (interactive)
281 (goto-char (point-max)))
282
283 (defun eshell-smart-display-move ()
284 "Handle self-inserting or movement commands intelligently."
285 (let (clear)
286 (if (or current-prefix-arg
287 (and (> (point) eshell-last-input-start)
288 (< (point) eshell-last-input-end))
289 (>= (point) eshell-last-output-end))
290 (setq clear t)
291 (cond
292 ((eq this-command 'self-insert-command)
293 (if (eq last-command-event ? )
294 (if (and eshell-smart-space-goes-to-end
295 eshell-current-command)
296 (if (not (pos-visible-in-window-p (point-max)))
297 (setq this-command 'scroll-up)
298 (setq this-command 'eshell-smart-goto-end))
299 (setq this-command 'scroll-up))
300 (setq clear t)
301 (goto-char (point-max))))
302 ((eq this-command 'delete-backward-char)
303 (setq this-command 'ignore)
304 (if (< (point) eshell-last-input-start)
305 (eshell-show-output)
306 (if (pos-visible-in-window-p eshell-last-input-start)
307 (progn
308 (ignore-errors
309 (scroll-down))
310 (eshell-show-output))
311 (scroll-down)
312 (if (pos-visible-in-window-p eshell-last-input-end)
313 (eshell-show-output)))))
314 ((or (memq this-command eshell-smart-display-navigate-list)
315 (and (eq this-command 'eshell-send-input)
316 (not (and (>= (point) eshell-last-input-start)
317 (< (point) eshell-last-input-end)))))
318 (setq clear t)
319 (goto-char (point-max)))))
320 (if clear
321 (remove-hook 'pre-command-hook 'eshell-smart-display-move t))))
322
323 (provide 'em-smart)
324
325 ;; Local Variables:
326 ;; generated-autoload-file: "esh-groups.el"
327 ;; End:
328
329 ;;; em-smart.el ends here