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