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