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