Add copyright and license notice.
[bpt/emacs.git] / lisp / ehelp.el
CommitLineData
c0274f38
ER
1;;; ehelp.el --- bindings for electric-help mode
2
0d30b337 3;; Copyright (C) 1986, 1995, 2000, 2001, 2002, 2003, 2004,
409cc4a3 4;; 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
3a801d0c 5
e5167999 6;; Maintainer: FSF
fd7fa35a 7;; Keywords: help, extensions
e5167999 8
a2535589
JA
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
b4aa6026 13;; the Free Software Foundation; either version 3, or (at your option)
a2535589
JA
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
b578f267 22;; along with GNU Emacs; see the file COPYING. If not, write to the
086add15
LK
23;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24;; Boston, MA 02110-1301, USA.
a2535589 25
e41b2db1
ER
26;;; Commentary:
27
28;; This package provides a pre-packaged `Electric Help Mode' for
29;; browsing on-line help screens. There is one entry point,
9dd2b34e 30;; `with-electric-help'; all you have to give it is a no-argument
1b3250ab 31;; function that generates the actual text of the help into the current
e41b2db1
ER
32;; buffer.
33
bbe2ecf6
RS
34;; To make this the default, you must do
35;; (require 'ehelp)
36;; (define-key global-map "\C-h" 'ehelp-command)
37;; (define-key global-map [help] 'ehelp-command)
38;; (define-key global-map [f1] 'ehelp-command)
39
e5167999
ER
40;;; Code:
41
a2535589 42(require 'electric)
a2535589 43(defvar electric-help-map ()
d5b9d1d8 44 "Keymap defining commands available in `electric-help-mode'.")
a2535589 45
db30b383
RS
46(defvar electric-help-form-to-execute nil)
47
d05c87ac
GM
48(defgroup electric-help ()
49 "Electric help facility."
50 :version "21.1"
51 :group 'help)
52
53(defcustom electric-help-shrink-window t
54 "If set, adjust help window sizes to buffer sizes when displaying help."
55 :type 'boolean
56 :group 'electric-help)
57
d1842b2b
GM
58(defcustom electric-help-mode-hook nil
59 "Hook run by `with-electric-help' after initializing the buffer."
60 :type 'hook
61 :group 'electric-help)
62
a2535589
JA
63(put 'electric-help-undefined 'suppress-keymap t)
64(if electric-help-map
65 ()
66 (let ((map (make-keymap)))
f98e758c
RS
67 ;; allow all non-self-inserting keys - search, scroll, etc, but
68 ;; let M-x and C-x exit ehelp mode and retain buffer:
e27ef4db 69 (suppress-keymap map)
f98e758c
RS
70 (define-key map "\C-u" 'electric-help-undefined)
71 (define-key map [?\C-0] 'electric-help-undefined)
72 (define-key map [?\C-1] 'electric-help-undefined)
73 (define-key map [?\C-2] 'electric-help-undefined)
74 (define-key map [?\C-3] 'electric-help-undefined)
75 (define-key map [?\C-4] 'electric-help-undefined)
76 (define-key map [?\C-5] 'electric-help-undefined)
77 (define-key map [?\C-6] 'electric-help-undefined)
78 (define-key map [?\C-7] 'electric-help-undefined)
79 (define-key map [?\C-8] 'electric-help-undefined)
80 (define-key map [?\C-9] 'electric-help-undefined)
a2535589
JA
81 (define-key map (char-to-string help-char) 'electric-help-help)
82 (define-key map "?" 'electric-help-help)
83 (define-key map " " 'scroll-up)
84 (define-key map "\^?" 'scroll-down)
85 (define-key map "." 'beginning-of-buffer)
86 (define-key map "<" 'beginning-of-buffer)
87 (define-key map ">" 'end-of-buffer)
88 ;(define-key map "\C-g" 'electric-help-exit)
a2535589 89 (define-key map "Q" 'electric-help-exit)
ad8d994c 90 (define-key map "q" 'electric-help-exit)
a2535589 91 ;;a better key than this?
e27ef4db 92 (define-key map "R" 'electric-help-retain)
ad8d994c 93 (define-key map "r" 'electric-help-retain)
f98e758c
RS
94 (define-key map "\ex" 'electric-help-execute-extended)
95 (define-key map "\C-x" 'electric-help-ctrl-x-prefix)
a2535589
JA
96
97 (setq electric-help-map map)))
f98e758c 98
a2535589 99(defun electric-help-mode ()
d5b9d1d8
JB
100 "`with-electric-help' temporarily places its buffer in this mode.
101\(On exit from `with-electric-help', the buffer is put in `default-major-mode'.)"
a2535589
JA
102 (setq buffer-read-only t)
103 (setq mode-name "Help")
104 (setq major-mode 'help)
105 (setq mode-line-buffer-identification '(" Help: %b"))
106 (use-local-map electric-help-map)
2e8174d2 107 (add-hook 'mouse-leave-buffer-hook 'electric-help-retain)
db30b383 108 (view-mode -1)
a2535589
JA
109 ;; this is done below in with-electric-help
110 ;(run-hooks 'electric-help-mode-hook)
111 )
112
9dd2b34e 113;;;###autoload
483a5ec0 114(defun with-electric-help (thunk &optional buffer noerase minheight)
bbe2ecf6 115 "Pop up an \"electric\" help buffer.
f98e758c
RS
116THUNK is a function of no arguments which is called to initialize the
117contents of BUFFER. BUFFER defaults to `*Help*'. BUFFER will be
118erased before THUNK is called unless NOERASE is non-nil. THUNK will
119be called while BUFFER is current and with `standard-output' bound to
bbe2ecf6
RS
120the buffer specified by BUFFER.
121
122If THUNK returns nil, we display BUFFER starting at the top, and
123shrink the window to fit. If THUNK returns non-nil, we don't do those things.
a2535589
JA
124
125After THUNK has been called, this function \"electrically\" pops up a window
126in which BUFFER is displayed and allows the user to scroll through that buffer
24501f03 127in `electric-help-mode'. The window's height will be at least MINHEIGHT if
f98e758c
RS
128this value is non-nil.
129
130If THUNK returns nil, we display BUFFER starting at the top, and
d05c87ac
GM
131shrink the window to fit if `electric-help-shrink-window' is non-nil.
132If THUNK returns non-nil, we don't do those things.
f98e758c 133
f55e4a7e
EZ
134When the user exits (with `electric-help-exit', or otherwise), the help
135buffer's window disappears (i.e., we use `save-window-excursion'), and
24501f03 136BUFFER is put into `default-major-mode' (or `fundamental-mode')."
a2535589
JA
137 (setq buffer (get-buffer-create (or buffer "*Help*")))
138 (let ((one (one-window-p t))
88c269cb 139 (config (current-window-configuration))
f98e758c 140 (bury nil)
db30b383 141 (electric-help-form-to-execute nil))
88c269cb 142 (unwind-protect
143 (save-excursion
d05c87ac
GM
144 (when one
145 (goto-char (window-start (selected-window))))
88c269cb 146 (let ((pop-up-windows t))
147 (pop-to-buffer buffer))
148 (save-excursion
149 (set-buffer buffer)
d05c87ac
GM
150 (when (and minheight (< (window-height) minheight))
151 (enlarge-window (- minheight (window-height))))
88c269cb 152 (electric-help-mode)
e024a2f4 153 (setq buffer-read-only nil)
d05c87ac
GM
154 (unless noerase
155 (erase-buffer)))
88c269cb 156 (let ((standard-output buffer))
d05c87ac
GM
157 (unless (funcall thunk)
158 (set-buffer buffer)
159 (set-buffer-modified-p nil)
160 (goto-char (point-min))
161 (when (and one electric-help-shrink-window)
162 (shrink-window-if-larger-than-buffer))))
88c269cb 163 (set-buffer buffer)
164 (run-hooks 'electric-help-mode-hook)
e024a2f4 165 (setq buffer-read-only t)
d05c87ac 166 (if (eq (car-safe (electric-help-command-loop)) 'retain)
88c269cb 167 (setq config (current-window-configuration))
d05c87ac 168 (setq bury t))
9201fa06 169 ;; Remove the hook.
d05c87ac
GM
170 (when (memq 'electric-help-retain mouse-leave-buffer-hook)
171 (remove-hook 'mouse-leave-buffer-hook 'electric-help-retain)))
88c269cb 172 (message "")
173 (set-buffer buffer)
174 (setq buffer-read-only nil)
d05c87ac
GM
175
176 ;; We should really get a usable *Help* buffer when retaining
177 ;; the electric one with `r'. The problem is that a simple
f55e4a7e 178 ;; call to help-mode won't cut it; at least RET is bound wrong
d05c87ac
GM
179 ;; afterwards. It's also not clear that `help-mode' is always
180 ;; the right thing, maybe we should add an optional parameter.
88c269cb 181 (condition-case ()
182 (funcall (or default-major-mode 'fundamental-mode))
183 (error nil))
71296446 184
88c269cb 185 (set-window-configuration config)
d05c87ac 186 (when bury
f55e4a7e 187 ;;>> Perhaps this shouldn't be done,
d05c87ac
GM
188 ;; so that when we say "Press space to bury" we mean it
189 (replace-buffer-in-windows buffer)
190 ;; must do this outside of save-window-excursion
191 (bury-buffer buffer))
db30b383 192 (eval electric-help-form-to-execute))))
a2535589
JA
193
194(defun electric-help-command-loop ()
195 (catch 'exit
196 (if (pos-visible-in-window-p (point-max))
7bccdfbc 197 (progn (message "%s" (substitute-command-keys "<<< Press Space to bury the help buffer, Press \\[electric-help-retain] to retain it >>>"))
70447f8c 198 (if (equal (setq unread-command-events (list (read-event)))
3d5b307e 199 '(?\s))
dbc4e1c1 200 (progn (setq unread-command-events nil)
a2535589
JA
201 (throw 'exit t)))))
202 (let (up down both neither
e0db3d3f 203 (standard (and (eq (key-binding " " nil t)
a2535589 204 'scroll-up)
e0db3d3f 205 (eq (key-binding "\^?" nil t)
a2535589 206 'scroll-down)
e0db3d3f 207 (eq (key-binding "q" nil t)
e27ef4db 208 'electric-help-exit)
e0db3d3f 209 (eq (key-binding "r" nil t)
e27ef4db 210 'electric-help-retain))))
a2535589
JA
211 (Electric-command-loop
212 'exit
213 (function (lambda ()
71296446
JB
214 (sit-for 0) ;necessary if last command was end-of-buffer or
215 ;beginning-of-buffer - otherwise pos-visible-in-window-p
f98e758c 216 ;will yield a wrong result.
a2535589 217 (let ((min (pos-visible-in-window-p (point-min)))
1d4c1257 218 (max (pos-visible-in-window-p (1- (point-max)))))
f98e758c
RS
219 (cond (isearch-mode 'noprompt)
220 ((and min max)
e27ef4db 221 (cond (standard "Press q to exit, r to retain ")
a2535589 222 (neither)
e27ef4db 223 (t (setq neither (substitute-command-keys "Press \\[electric-help-exit] to exit, \\[electric-help-retain] to retain ")))))
a2535589 224 (min
e27ef4db 225 (cond (standard "Press SPC to scroll, q to exit, r to retain ")
a2535589 226 (up)
e27ef4db 227 (t (setq up (substitute-command-keys "Press \\[scroll-up] to scroll, \\[electric-help-exit] to exit, \\[electric-help-retain] to retain ")))))
a2535589 228 (max
f98e758c 229 (cond (standard "Press DEL to scroll back, q to exit, r to retain ")
a2535589 230 (down)
e27ef4db 231 (t (setq down (substitute-command-keys "Press \\[scroll-down] to scroll back, \\[electric-help-exit] to exit, \\[electric-help-retain] to retain ")))))
a2535589 232 (t
f98e758c 233 (cond (standard "Press SPC to scroll, DEL to scroll back, q to exit, r to retain ")
a2535589 234 (both)
e27ef4db 235 (t (setq both (substitute-command-keys "Press \\[scroll-up] to scroll, \\[scroll-down] to scroll back, \\[electric-help-exit] to exit, \\[electric-help-retain] to retain ")))))))))
a2535589
JA
236 t))))
237
238
239\f
240;(defun electric-help-scroll-up (arg)
241; ">>>Doc"
242; (interactive "P")
243; (if (and (null arg) (pos-visible-in-window-p (point-max)))
244; (electric-help-exit)
245; (scroll-up arg)))
246
247(defun electric-help-exit ()
24501f03 248 "Exit `with-electric-help', restoring the previous window/buffer configuration.
9201fa06 249\(The *Help* buffer will be buried.)"
a2535589 250 (interactive)
9201fa06
RS
251 ;; Make sure that we don't throw twice, even if two events cause
252 ;; calling this function:
253 (if (memq 'electric-help-retain mouse-leave-buffer-hook)
254 (progn
255 (remove-hook 'mouse-leave-buffer-hook 'electric-help-retain)
256 (throw 'exit t))))
a2535589
JA
257
258(defun electric-help-retain ()
24501f03 259 "Exit `with-electric-help', retaining the current window/buffer configuration.
a2535589
JA
260\(The *Help* buffer will not be selected, but \\[switch-to-buffer-other-window] RET
261will select it.)"
262 (interactive)
f98e758c
RS
263 ;; Make sure that we don't throw twice, even if two events cause
264 ;; calling this function:
2e8174d2
RS
265 (if (memq 'electric-help-retain mouse-leave-buffer-hook)
266 (progn
267 (remove-hook 'mouse-leave-buffer-hook 'electric-help-retain)
268 (throw 'exit '(retain)))))
a2535589
JA
269
270
a2535589
JA
271(defun electric-help-undefined ()
272 (interactive)
273 (error "%s is undefined -- Press %s to exit"
274 (mapconcat 'single-key-description (this-command-keys) " ")
e0db3d3f 275 (if (eq (key-binding "q" nil t) 'electric-help-exit)
f98e758c 276 "q"
a2535589
JA
277 (substitute-command-keys "\\[electric-help-exit]"))))
278
279
280;>>> this needs to be hairified (recursive help, anybody?)
281(defun electric-help-help ()
282 (interactive)
e0db3d3f
JB
283 (if (and (eq (key-binding "q" nil t) 'electric-help-exit)
284 (eq (key-binding " " nil t) 'scroll-up)
285 (eq (key-binding "\^?" nil t) 'scroll-down)
286 (eq (key-binding "r" nil t) 'electric-help-retain))
e27ef4db
RS
287 (message "SPC scrolls up, DEL scrolls down, q exits burying help buffer, r exits")
288 (message "%s" (substitute-command-keys "\\[scroll-up] scrolls up, \\[scroll-down] scrolls down, \\[electric-help-exit] exits burying help buffer, \\[electric-help-retain] exits")))
a2535589
JA
289 (sit-for 2))
290
291\f
9dd2b34e 292;;;###autoload
1676b298
RS
293(defun electric-helpify (fun &optional name)
294 (let ((name (or name "*Help*")))
a2535589
JA
295 (if (save-window-excursion
296 ;; kludge-o-rama
297 (let* ((p (symbol-function 'print-help-return-message))
298 (b (get-buffer name))
299 (m (buffer-modified-p b)))
300 (and b (not (get-buffer-window b))
301 (setq b nil))
302 (unwind-protect
303 (progn
304 (message "%s..." (capitalize (symbol-name fun)))
305 ;; with-output-to-temp-buffer marks the buffer as unmodified.
306 ;; kludging excessively and relying on that as some sort
307 ;; of indication leads to the following abomination...
308 ;;>> This would be doable without such icky kludges if either
309 ;;>> (a) there were a function to read the interactive
310 ;;>> args for a command and return a list of those args.
311 ;;>> (To which one would then just apply the command)
312 ;;>> (The only problem with this is that interactive-p
313 ;;>> would break, but that is such a misfeature in
314 ;;>> any case that I don't care)
315 ;;>> It is easy to do this for emacs-lisp functions;
316 ;;>> the only problem is getting the interactive spec
317 ;;>> for subrs
318 ;;>> (b) there were a function which returned a
319 ;;>> modification-tick for a buffer. One could tell
320 ;;>> whether a buffer had changed by whether the
321 ;;>> modification-tick were different.
322 ;;>> (Presumably there would have to be a way to either
323 ;;>> restore the tick to some previous value, or to
324 ;;>> suspend updating of the tick in order to allow
325 ;;>> things like momentary-string-display)
326 (and b
327 (save-excursion
328 (set-buffer b)
329 (set-buffer-modified-p t)))
330 (fset 'print-help-return-message 'ignore)
331 (call-interactively fun)
332 (and (get-buffer name)
333 (get-buffer-window (get-buffer name))
334 (or (not b)
335 (not (eq b (get-buffer name)))
336 (not (buffer-modified-p b)))))
337 (fset 'print-help-return-message p)
338 (and b (buffer-name b)
339 (save-excursion
340 (set-buffer b)
341 (set-buffer-modified-p m))))))
342 (with-electric-help 'ignore name t))))
343
344\f
f98e758c 345
71296446 346;; This is to be bound to M-x in ehelp mode. Retains ehelp buffer and then
f98e758c
RS
347;; continues with execute-extended-command.
348(defun electric-help-execute-extended (prefixarg)
349 (interactive "p")
db30b383 350 (setq electric-help-form-to-execute '(execute-extended-command nil))
f98e758c
RS
351 (electric-help-retain))
352
353;; This is to be buond to C-x in ehelp mode. Retains ehelp buffer and then
354;; continues with ctrl-x prefix.
355(defun electric-help-ctrl-x-prefix (prefixarg)
356 (interactive "p")
db30b383 357 (setq electric-help-form-to-execute '(progn (message nil) (setq unread-command-char ?\C-x)))
f98e758c
RS
358 (electric-help-retain))
359
360\f
a2535589
JA
361(defun electric-describe-key ()
362 (interactive)
363 (electric-helpify 'describe-key))
364
365(defun electric-describe-mode ()
366 (interactive)
367 (electric-helpify 'describe-mode))
368
369(defun electric-view-lossage ()
370 (interactive)
371 (electric-helpify 'view-lossage))
372
373;(defun electric-help-for-help ()
374; "See help-for-help"
375; (interactive)
376; )
377
378(defun electric-describe-function ()
379 (interactive)
380 (electric-helpify 'describe-function))
381
382(defun electric-describe-variable ()
383 (interactive)
384 (electric-helpify 'describe-variable))
385
386(defun electric-describe-bindings ()
387 (interactive)
388 (electric-helpify 'describe-bindings))
389
390(defun electric-describe-syntax ()
391 (interactive)
392 (electric-helpify 'describe-syntax))
393
394(defun electric-command-apropos ()
395 (interactive)
1676b298 396 (electric-helpify 'command-apropos "*Apropos*"))
a2535589
JA
397
398;(define-key help-map "a" 'electric-command-apropos)
399
e27ef4db
RS
400(defun electric-apropos ()
401 (interactive)
402 (electric-helpify 'apropos))
a2535589 403
a2535589
JA
404\f
405;;;; ehelp-map
406
407(defvar ehelp-map ())
408(if ehelp-map
409 nil
71296446 410 (let ((map (copy-keymap help-map)))
f98e758c 411 (substitute-key-definition 'apropos 'electric-apropos map)
e27ef4db 412 (substitute-key-definition 'command-apropos 'electric-command-apropos map)
a2535589
JA
413 (substitute-key-definition 'describe-key 'electric-describe-key map)
414 (substitute-key-definition 'describe-mode 'electric-describe-mode map)
415 (substitute-key-definition 'view-lossage 'electric-view-lossage map)
416 (substitute-key-definition 'describe-function 'electric-describe-function map)
417 (substitute-key-definition 'describe-variable 'electric-describe-variable map)
418 (substitute-key-definition 'describe-bindings 'electric-describe-bindings map)
419 (substitute-key-definition 'describe-syntax 'electric-describe-syntax map)
420
6e5cbb63
RS
421 (setq ehelp-map map)))
422
423;;;###(autoload 'ehelp-command "ehelp" "Prefix command for ehelp." t 'keymap)
424(defalias 'ehelp-command ehelp-map)
425(put 'ehelp-command 'documentation "Prefix command for ehelp.")
a2535589 426
71296446 427(provide 'ehelp)
49116ac0 428
ab5796a9 429;;; arch-tag: e0e3037f-42c0-433e-ba18-322c5d951f46
c0274f38 430;;; ehelp.el ends here