Misc custom group fixes
[bpt/emacs.git] / lisp / eshell / em-smart.el
CommitLineData
60370d40 1;;; em-smart.el --- smart display of output
affbf647 2
ab422c4d 3;; Copyright (C) 1999-2013 Free Software Foundation, Inc.
affbf647 4
7de5b421
GM
5;; Author: John Wiegley <johnw@gnu.org>
6
affbf647
GM
7;; This file is part of GNU Emacs.
8
4ee57b2a 9;; GNU Emacs is free software: you can redistribute it and/or modify
affbf647 10;; it under the terms of the GNU General Public License as published by
4ee57b2a
GM
11;; the Free Software Foundation, either version 3 of the License, or
12;; (at your option) any later version.
affbf647
GM
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
4ee57b2a 20;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
affbf647 21
affbf647
GM
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! ;)
dace60cf
JW
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.
affbf647 68
dbba8a04
GM
69;;; Code:
70
71(eval-when-compile (require 'eshell))
72
3146b070 73;;;###autoload
35ff222c
GM
74(progn
75(defgroup eshell-smart nil
dbba8a04
GM
76 "This module combines the facility of normal, modern shells with
77some of the edit/review concepts inherent in the design of Plan 9's
789term. See the docs for more details.
79
80Most likely you will have to turn this option on and play around with
81it to get a real sense of how it works."
82 :tag "Smart display of output"
83 ;; :link '(info-link "(eshell)Smart display of output")
35ff222c 84 :group 'eshell-module))
dbba8a04 85
affbf647
GM
86;;; User Variables:
87
d783d303 88(defcustom eshell-smart-load-hook nil
ec60da52 89 "A list of functions to call when loading `eshell-smart'."
d783d303 90 :version "24.1" ; removed eshell-smart-initialize
affbf647
GM
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))))
ec60da52 100 "A hook that gets run when `eshell-smart' is unloaded."
affbf647
GM
101 :type 'hook
102 :group 'eshell-smart)
103
104(defcustom eshell-review-quick-commands nil
ec60da52 105 "If t, always review commands.
b4bd214e
JW
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
bbd44006
JB
113command\" is extended to include commands that produce output, if and
114only if that output can be presented in its entirely in the Eshell window."
b4bd214e
JW
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
ddd63a1e 124 mouse-yank-primary
affbf647
GM
125 mouse-yank-secondary
126 yank-pop
127 yank-rectangle
128 yank)
ec60da52 129 "A list of commands which cause Eshell to jump to the end of buffer."
affbf647
GM
130 :type '(repeat function)
131 :group 'eshell-smart)
132
133(defcustom eshell-smart-space-goes-to-end t
ec60da52 134 "If non-nil, space will go to end of buffer when point-max is visible.
affbf647
GM
135That is, if a command is running and the user presses SPACE at a time
136when the end of the buffer is visible, point will go to the end of the
137buffer and smart-display will be turned off (that is, subsequently
138pressing backspace will not cause the buffer to scroll down).
139
140This feature is provided to make it very easy to watch the output of a
141long-running command, such as make, where it's more desirable to see
142the output go by than to review it afterward.
143
144Setting this variable to nil means that space and backspace will
145always have a consistent behavior, which is to move back and forth
146through displayed output. But it also means that enabling output
147tracking requires the user to manually move point to the end of the
148buffer using \\[end-of-buffer]."
149 :type 'boolean
150 :group 'eshell-smart)
151
152(defcustom eshell-where-to-jump 'begin
ec60da52 153 "This variable indicates where point should jump to after a command.
affbf647
GM
154The options are `begin', `after' or `end'."
155 :type '(radio (const :tag "Beginning of command" begin)
156 (const :tag "After command word" after)
157 (const :tag "End of command" end))
158 :group 'eshell-smart)
159
160;;; Internal Variables:
161
162(defvar eshell-smart-displayed nil)
163(defvar eshell-smart-command-done nil)
dace60cf 164(defvar eshell-currently-handling-window nil)
affbf647
GM
165
166;;; Functions:
167
168(defun eshell-smart-initialize ()
169 "Setup Eshell smart display."
170 (unless eshell-non-interactive-p
171 ;; override a few variables, since they would interfere with the
172 ;; smart display functionality.
173 (set (make-local-variable 'eshell-scroll-to-bottom-on-output) nil)
174 (set (make-local-variable 'eshell-scroll-to-bottom-on-input) nil)
175 (set (make-local-variable 'eshell-scroll-show-maximum-output) t)
176
affbf647
GM
177 (add-hook 'window-scroll-functions 'eshell-smart-scroll-window nil t)
178 (add-hook 'window-configuration-change-hook 'eshell-refresh-windows)
179
affbf647
GM
180 (add-hook 'eshell-output-filter-functions 'eshell-refresh-windows t t)
181
dace60cf 182 (add-hook 'after-change-functions 'eshell-disable-after-change nil t)
affbf647 183
dace60cf 184 (add-hook 'eshell-input-filter-functions 'eshell-smart-display-setup nil t)
affbf647
GM
185
186 (make-local-variable 'eshell-smart-command-done)
dace60cf
JW
187 (add-hook 'eshell-post-command-hook
188 (function
189 (lambda ()
190 (setq eshell-smart-command-done t))) t t)
affbf647 191
b4bd214e 192 (unless (eq eshell-review-quick-commands t)
affbf647
GM
193 (add-hook 'eshell-post-command-hook
194 'eshell-smart-maybe-jump-to-end nil t))))
195
196(defun eshell-smart-scroll-window (wind start)
197 "Scroll the given Eshell window accordingly."
198 (unless eshell-currently-handling-window
199 (let ((inhibit-point-motion-hooks t)
200 (eshell-currently-handling-window t))
dace60cf
JW
201 (save-selected-window
202 (select-window wind)
203 (eshell-smart-redisplay)))))
affbf647
GM
204
205(defun eshell-refresh-windows (&optional frame)
206 "Refresh all visible Eshell buffers."
207 (let (affected)
208 (walk-windows
209 (function
210 (lambda (wind)
211 (with-current-buffer (window-buffer wind)
dace60cf
JW
212 (if eshell-mode
213 (let (window-scroll-functions)
214 (eshell-smart-scroll-window wind (window-start))
215 (setq affected t))))))
affbf647
GM
216 0 frame)
217 (if affected
218 (let (window-scroll-functions)
219 (eshell-redisplay)))))
220
221(defun eshell-smart-display-setup ()
222 "Set the point to somewhere in the beginning of the last command."
223 (cond
224 ((eq eshell-where-to-jump 'begin)
225 (goto-char eshell-last-input-start))
226 ((eq eshell-where-to-jump 'after)
227 (goto-char (next-single-property-change
228 eshell-last-input-start 'arg-end))
229 (if (= (point) (- eshell-last-input-end 2))
230 (forward-char)))
231 ((eq eshell-where-to-jump 'end)
232 (goto-char (1- eshell-last-input-end)))
233 (t
234 (error "Invalid value for `eshell-where-to-jump'")))
235 (setq eshell-smart-command-done nil)
236 (add-hook 'pre-command-hook 'eshell-smart-display-move nil t)
237 (eshell-refresh-windows))
238
239(defun eshell-disable-after-change (b e l)
240 "Disable smart display mode if the buffer changes in any way."
241 (when eshell-smart-command-done
242 (remove-hook 'pre-command-hook 'eshell-smart-display-move t)
243 (setq eshell-smart-command-done nil)))
244
245(defun eshell-smart-maybe-jump-to-end ()
246 "Jump to the end of the input buffer.
45a87863 247This is done whenever a command exits successfully and both the command
b4bd214e 248and the end of the buffer are still visible."
affbf647 249 (when (and (= eshell-last-command-status 0)
b4bd214e
JW
250 (if (eq eshell-review-quick-commands 'not-even-short-output)
251 (and (pos-visible-in-window-p (point-max))
252 (pos-visible-in-window-p eshell-last-input-start))
253 (= (count-lines eshell-last-input-end
254 eshell-last-output-end) 0)))
affbf647
GM
255 (goto-char (point-max))
256 (remove-hook 'pre-command-hook 'eshell-smart-display-move t)))
257
258(defun eshell-smart-redisplay ()
259 "Display as much output as possible, smartly."
260 (if (eobp)
157975e3
JW
261 (save-excursion
262 (recenter -1)
263 ;; trigger the redisplay now, so that we catch any attempted
264 ;; point motion; this is to cover for a redisplay bug
265 (eshell-redisplay))
ca7aae91
JW
266 (let ((top-point (point)))
267 (and (memq 'eshell-smart-display-move pre-command-hook)
268 (>= (point) eshell-last-input-start)
269 (< (point) eshell-last-input-end)
270 (set-window-start (selected-window)
271 (line-beginning-position) t))
272 (if (pos-visible-in-window-p (point-max))
273 (save-excursion
274 (goto-char (point-max))
275 (recenter -1)
276 (unless (pos-visible-in-window-p top-point)
277 (goto-char top-point)
278 (set-window-start (selected-window)
279 (line-beginning-position) t)))))))
affbf647
GM
280
281(defun eshell-smart-goto-end ()
282 "Like `end-of-buffer', but do not push a mark."
283 (interactive)
284 (goto-char (point-max)))
285
286(defun eshell-smart-display-move ()
287 "Handle self-inserting or movement commands intelligently."
288 (let (clear)
289 (if (or current-prefix-arg
290 (and (> (point) eshell-last-input-start)
291 (< (point) eshell-last-input-end))
292 (>= (point) eshell-last-output-end))
293 (setq clear t)
294 (cond
295 ((eq this-command 'self-insert-command)
1ba983e8 296 (if (eq last-command-event ? )
affbf647
GM
297 (if (and eshell-smart-space-goes-to-end
298 eshell-current-command)
299 (if (not (pos-visible-in-window-p (point-max)))
300 (setq this-command 'scroll-up)
301 (setq this-command 'eshell-smart-goto-end))
302 (setq this-command 'scroll-up))
303 (setq clear t)
304 (goto-char (point-max))))
305 ((eq this-command 'delete-backward-char)
306 (setq this-command 'ignore)
307 (if (< (point) eshell-last-input-start)
308 (eshell-show-output)
309 (if (pos-visible-in-window-p eshell-last-input-start)
310 (progn
311 (ignore-errors
312 (scroll-down))
313 (eshell-show-output))
314 (scroll-down)
315 (if (pos-visible-in-window-p eshell-last-input-end)
316 (eshell-show-output)))))
317 ((or (memq this-command eshell-smart-display-navigate-list)
318 (and (eq this-command 'eshell-send-input)
319 (not (and (>= (point) eshell-last-input-start)
320 (< (point) eshell-last-input-end)))))
321 (setq clear t)
322 (goto-char (point-max)))))
323 (if clear
324 (remove-hook 'pre-command-hook 'eshell-smart-display-move t))))
325
dbba8a04 326(provide 'em-smart)
affbf647 327
3146b070
GM
328;; Local Variables:
329;; generated-autoload-file: "esh-groups.el"
330;; End:
331
affbf647 332;;; em-smart.el ends here