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