1 ;;; em-smart --- smart display of output
3 ;; Copyright (C) 1999, 2000 Free Software Foundation
5 ;; This file is part of GNU Emacs.
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)
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.
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.
24 (eval-when-compile (require 'esh-maint
))
26 (defgroup eshell-smart nil
27 "This module combines the facility of normal, modern shells with
28 some of the edit/review concepts inherent in the design of Plan 9's
29 9term. See the docs for more details.
31 Most likely you will have to turn this option on and play around with
32 it to get a real sense of how it works."
33 :tag
"Smart display of output"
34 :link
'(info-link "(eshell)Smart display of output")
35 :group
'eshell-module
)
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.
44 ;; @ When you invoke a command, it is assumed that you want to read
45 ;; the output of that command.
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
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.
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
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
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.
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,
77 ;; @ Like I said, it's not really comprehensible until you try it! ;)
81 (defcustom eshell-smart-load-hook
'(eshell-smart-initialize)
82 "*A list of functions to call when loading `eshell-smart'."
86 (defcustom eshell-smart-unload-hook
90 (remove-hook 'window-configuration-change-hook
91 'eshell-refresh-windows
))))
92 "*A hook that gets run when `eshell-smart' is unloaded."
96 (defcustom eshell-review-quick-commands nil
97 "*If t, always review commands.
98 Reviewing means keeping point on the text of the command that was just
99 invoked, to allow corrections to be made easily.
101 If set to nil, quick commands won't be reviewed. A quick command is a
102 command that produces no output, and exits successfully.
104 If set to `not-even-short-output', then the definition of \"quick
105 command\" is extended to include commands that produce output, iff
106 that output can be presented in its entirely in the Eshell window."
107 :type
'(choice (const :tag
"No" nil
)
109 (const :tag
"Not even short output"
110 not-even-short-output
))
111 :group
'eshell-smart
)
113 (defcustom eshell-smart-display-navigate-list
120 "*A list of commands which cause Eshell to jump to the end of buffer."
121 :type
'(repeat function
)
122 :group
'eshell-smart
)
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.
126 That is, if a command is running and the user presses SPACE at a time
127 when the end of the buffer is visible, point will go to the end of the
128 buffer and smart-display will be turned off (that is, subsequently
129 pressing backspace will not cause the buffer to scroll down).
131 This feature is provided to make it very easy to watch the output of a
132 long-running command, such as make, where it's more desirable to see
133 the output go by than to review it afterward.
135 Setting this variable to nil means that space and backspace will
136 always have a consistent behavior, which is to move back and forth
137 through displayed output. But it also means that enabling output
138 tracking requires the user to manually move point to the end of the
139 buffer using \\[end-of-buffer]."
141 :group
'eshell-smart
)
143 (defcustom eshell-where-to-jump
'begin
144 "*This variable indicates where point should jump to after a command.
145 The 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
)
151 ;;; Internal Variables:
153 (defvar eshell-smart-displayed nil
)
154 (defvar eshell-smart-command-done nil
)
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
)
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
)
171 (make-local-hook 'eshell-output-filter-functions
)
172 (add-hook 'eshell-output-filter-functions
'eshell-refresh-windows t t
)
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
)
179 (make-local-hook 'eshell-input-filter-functions
)
180 (add-hook 'eshell-input-filter-functions
181 'eshell-smart-display-setup nil t
)
183 (make-local-variable 'eshell-smart-command-done
)
184 (make-local-hook 'eshell-post-command-hook
)
185 (add-hook 'eshell-post-command-hook
188 (setq eshell-smart-command-done t
))) t t
)
190 (unless (eq eshell-review-quick-commands t
)
191 (add-hook 'eshell-post-command-hook
192 'eshell-smart-maybe-jump-to-end nil t
))))
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
))
200 (save-selected-window
202 (eshell-smart-redisplay))))))
204 (defun eshell-refresh-windows (&optional frame
)
205 "Refresh all visible Eshell buffers."
210 (with-current-buffer (window-buffer wind
)
212 (let (window-scroll-functions)
213 (eshell-smart-scroll-window wind
(window-start))
214 (setq affected t
))))))
217 (let (window-scroll-functions)
218 (eshell-redisplay)))))
220 (defun eshell-smart-display-setup ()
221 "Set the point to somewhere in the beginning of the last command."
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))
230 ((eq eshell-where-to-jump
'end
)
231 (goto-char (1- eshell-last-input-end
)))
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))
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
)))
244 (defun eshell-smart-maybe-jump-to-end ()
245 "Jump to the end of the input buffer.
246 This is done whenever a command exits sucessfully and both the command
247 and the end of the buffer are still visible."
248 (when (and (= eshell-last-command-status
0)
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)))
254 (goto-char (point-max))
255 (remove-hook 'pre-command-hook
'eshell-smart-display-move t
)))
257 (defun eshell-smart-redisplay ()
258 "Display as much output as possible, smartly."
261 (let ((top-point (point)))
262 (and (memq 'eshell-smart-display-move pre-command-hook
)
263 (>= (point) eshell-last-input-start
)
264 (< (point) eshell-last-input-end
)
265 (set-window-start (selected-window)
266 (line-beginning-position) t
))
267 (if (pos-visible-in-window-p (point-max))
269 (goto-char (point-max))
271 (unless (pos-visible-in-window-p top-point
)
272 (goto-char top-point
)
273 (set-window-start (selected-window)
274 (line-beginning-position) t
)))))))
276 (defun eshell-smart-goto-end ()
277 "Like `end-of-buffer', but do not push a mark."
279 (goto-char (point-max)))
281 (defun eshell-smart-display-move ()
282 "Handle self-inserting or movement commands intelligently."
284 (if (or current-prefix-arg
285 (and (> (point) eshell-last-input-start
)
286 (< (point) eshell-last-input-end
))
287 (>= (point) eshell-last-output-end
))
290 ((eq this-command
'self-insert-command
)
291 (if (eq last-command-char ?
)
292 (if (and eshell-smart-space-goes-to-end
293 eshell-current-command
)
294 (if (not (pos-visible-in-window-p (point-max)))
295 (setq this-command
'scroll-up
)
296 (setq this-command
'eshell-smart-goto-end
))
297 (setq this-command
'scroll-up
))
299 (goto-char (point-max))))
300 ((eq this-command
'delete-backward-char
)
301 (setq this-command
'ignore
)
302 (if (< (point) eshell-last-input-start
)
304 (if (pos-visible-in-window-p eshell-last-input-start
)
308 (eshell-show-output))
310 (if (pos-visible-in-window-p eshell-last-input-end
)
311 (eshell-show-output)))))
312 ((or (memq this-command eshell-smart-display-navigate-list
)
313 (and (eq this-command
'eshell-send-input
)
314 (not (and (>= (point) eshell-last-input-start
)
315 (< (point) eshell-last-input-end
)))))
317 (goto-char (point-max)))))
319 (remove-hook 'pre-command-hook
'eshell-smart-display-move t
))))
323 ;;; em-smart.el ends here