MS-Windows followup for 2013-02-11T23:37:18Z!eggert@cs.ucla.edu.
[bpt/emacs.git] / lisp / ehelp.el
CommitLineData
2fbc1934 1;;; ehelp.el --- bindings for electric-help mode -*- lexical-binding: t -*-
c0274f38 2
ab422c4d 3;; Copyright (C) 1986, 1995, 2000-2013 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)))
32939005
GM
64 ;; FIXME fragile. Should derive from help-mode-map in a smarter way.
65 (set-keymap-parent map button-buffer-map)
f98e758c
RS
66 ;; allow all non-self-inserting keys - search, scroll, etc, but
67 ;; let M-x and C-x exit ehelp mode and retain buffer:
e27ef4db 68 (suppress-keymap map)
f98e758c
RS
69 (define-key map "\C-u" 'electric-help-undefined)
70 (define-key map [?\C-0] 'electric-help-undefined)
71 (define-key map [?\C-1] 'electric-help-undefined)
72 (define-key map [?\C-2] 'electric-help-undefined)
73 (define-key map [?\C-3] 'electric-help-undefined)
74 (define-key map [?\C-4] 'electric-help-undefined)
75 (define-key map [?\C-5] 'electric-help-undefined)
76 (define-key map [?\C-6] 'electric-help-undefined)
77 (define-key map [?\C-7] 'electric-help-undefined)
78 (define-key map [?\C-8] 'electric-help-undefined)
79 (define-key map [?\C-9] 'electric-help-undefined)
a2535589
JA
80 (define-key map (char-to-string help-char) 'electric-help-help)
81 (define-key map "?" 'electric-help-help)
82 (define-key map " " 'scroll-up)
83 (define-key map "\^?" 'scroll-down)
84 (define-key map "." 'beginning-of-buffer)
85 (define-key map "<" 'beginning-of-buffer)
86 (define-key map ">" 'end-of-buffer)
87 ;(define-key map "\C-g" 'electric-help-exit)
a2535589 88 (define-key map "Q" 'electric-help-exit)
ad8d994c 89 (define-key map "q" 'electric-help-exit)
a2535589 90 ;;a better key than this?
e27ef4db 91 (define-key map "R" 'electric-help-retain)
ad8d994c 92 (define-key map "r" 'electric-help-retain)
f98e758c
RS
93 (define-key map "\ex" 'electric-help-execute-extended)
94 (define-key map "\C-x" 'electric-help-ctrl-x-prefix)
3fd29551
JB
95 map)
96 "Keymap defining commands available in `electric-help-mode'.")
f98e758c 97
9c13a46e
JL
98(defvar electric-help-orig-major-mode nil)
99(make-variable-buffer-local 'electric-help-orig-major-mode)
100
a2535589 101(defun electric-help-mode ()
d5b9d1d8 102 "`with-electric-help' temporarily places its buffer in this mode.
9c13a46e 103\(On exit from `with-electric-help', the original `major-mode' is restored.)"
a2535589 104 (setq buffer-read-only t)
9c13a46e 105 (setq electric-help-orig-major-mode major-mode)
a2535589 106 (setq mode-name "Help")
addc11ed 107 (setq major-mode 'help-mode)
a2535589
JA
108 (setq mode-line-buffer-identification '(" Help: %b"))
109 (use-local-map electric-help-map)
2e8174d2 110 (add-hook 'mouse-leave-buffer-hook 'electric-help-retain)
db30b383 111 (view-mode -1)
a2535589
JA
112 ;; this is done below in with-electric-help
113 ;(run-hooks 'electric-help-mode-hook)
114 )
115
9dd2b34e 116;;;###autoload
483a5ec0 117(defun with-electric-help (thunk &optional buffer noerase minheight)
bbe2ecf6 118 "Pop up an \"electric\" help buffer.
f98e758c
RS
119THUNK is a function of no arguments which is called to initialize the
120contents of BUFFER. BUFFER defaults to `*Help*'. BUFFER will be
121erased before THUNK is called unless NOERASE is non-nil. THUNK will
122be called while BUFFER is current and with `standard-output' bound to
bbe2ecf6
RS
123the buffer specified by BUFFER.
124
3fd29551
JB
125If THUNK returns nil, we display BUFFER starting at the top, and shrink
126the window to fit. If THUNK returns non-nil, we don't do those things.
a2535589 127
3fd29551
JB
128After THUNK has been called, this function \"electrically\" pops up a
129window in which BUFFER is displayed and allows the user to scroll
130through that buffer in `electric-help-mode'. The window's height will
131be at least MINHEIGHT if this value is non-nil.
f98e758c
RS
132
133If THUNK returns nil, we display BUFFER starting at the top, and
d05c87ac
GM
134shrink the window to fit if `electric-help-shrink-window' is non-nil.
135If THUNK returns non-nil, we don't do those things.
f98e758c 136
f55e4a7e
EZ
137When the user exits (with `electric-help-exit', or otherwise), the help
138buffer's window disappears (i.e., we use `save-window-excursion'), and
9c13a46e 139BUFFER is put back into its original major mode."
a2535589
JA
140 (setq buffer (get-buffer-create (or buffer "*Help*")))
141 (let ((one (one-window-p t))
88c269cb 142 (config (current-window-configuration))
f98e758c 143 (bury nil)
db30b383 144 (electric-help-form-to-execute nil))
88c269cb 145 (unwind-protect
146 (save-excursion
d05c87ac
GM
147 (when one
148 (goto-char (window-start (selected-window))))
88c269cb 149 (let ((pop-up-windows t))
150 (pop-to-buffer buffer))
14acf2f5 151 (with-current-buffer buffer
d05c87ac
GM
152 (when (and minheight (< (window-height) minheight))
153 (enlarge-window (- minheight (window-height))))
88c269cb 154 (electric-help-mode)
e024a2f4 155 (setq buffer-read-only nil)
d05c87ac
GM
156 (unless noerase
157 (erase-buffer)))
88c269cb 158 (let ((standard-output buffer))
d05c87ac
GM
159 (unless (funcall thunk)
160 (set-buffer buffer)
161 (set-buffer-modified-p nil)
162 (goto-char (point-min))
163 (when (and one electric-help-shrink-window)
164 (shrink-window-if-larger-than-buffer))))
88c269cb 165 (set-buffer buffer)
166 (run-hooks 'electric-help-mode-hook)
e024a2f4 167 (setq buffer-read-only t)
d05c87ac 168 (if (eq (car-safe (electric-help-command-loop)) 'retain)
88c269cb 169 (setq config (current-window-configuration))
d05c87ac 170 (setq bury t))
9201fa06 171 ;; Remove the hook.
d05c87ac
GM
172 (when (memq 'electric-help-retain mouse-leave-buffer-hook)
173 (remove-hook 'mouse-leave-buffer-hook 'electric-help-retain)))
88c269cb 174 (message "")
175 (set-buffer buffer)
176 (setq buffer-read-only nil)
d05c87ac 177
9c13a46e 178 ;; Restore the original major mode saved by `electric-help-mode'.
d05c87ac
GM
179 ;; We should really get a usable *Help* buffer when retaining
180 ;; the electric one with `r'. The problem is that a simple
9c13a46e
JL
181 ;; call to `help-mode' won't cut it; e.g. RET is bound wrong
182 ;; afterwards (`View-scroll-line-forward' instead of `help-follow').
183 ;; That's because Help mode should be set with `with-help-window'
184 ;; instead of the direct call to `help-mode'. But at least
185 ;; RET works correctly on links after using `help-mode'.
186 ;; This is satisfactory enough.
88c269cb 187 (condition-case ()
9c13a46e 188 (funcall (or electric-help-orig-major-mode 'fundamental-mode))
88c269cb 189 (error nil))
71296446 190
88c269cb 191 (set-window-configuration config)
d05c87ac 192 (when bury
f55e4a7e 193 ;;>> Perhaps this shouldn't be done,
d05c87ac
GM
194 ;; so that when we say "Press space to bury" we mean it
195 (replace-buffer-in-windows buffer)
196 ;; must do this outside of save-window-excursion
197 (bury-buffer buffer))
bfeae2cf
SM
198 (if (functionp electric-help-form-to-execute)
199 (funcall electric-help-form-to-execute)
200 (eval electric-help-form-to-execute)))))
a2535589
JA
201
202(defun electric-help-command-loop ()
203 (catch 'exit
204 (if (pos-visible-in-window-p (point-max))
7bccdfbc 205 (progn (message "%s" (substitute-command-keys "<<< Press Space to bury the help buffer, Press \\[electric-help-retain] to retain it >>>"))
70447f8c 206 (if (equal (setq unread-command-events (list (read-event)))
3d5b307e 207 '(?\s))
dbc4e1c1 208 (progn (setq unread-command-events nil)
a2535589
JA
209 (throw 'exit t)))))
210 (let (up down both neither
e0db3d3f 211 (standard (and (eq (key-binding " " nil t)
a2535589 212 'scroll-up)
e0db3d3f 213 (eq (key-binding "\^?" nil t)
a2535589 214 'scroll-down)
e0db3d3f 215 (eq (key-binding "q" nil t)
e27ef4db 216 'electric-help-exit)
e0db3d3f 217 (eq (key-binding "r" nil t)
e27ef4db 218 'electric-help-retain))))
a2535589
JA
219 (Electric-command-loop
220 'exit
221 (function (lambda ()
71296446
JB
222 (sit-for 0) ;necessary if last command was end-of-buffer or
223 ;beginning-of-buffer - otherwise pos-visible-in-window-p
f98e758c 224 ;will yield a wrong result.
a2535589 225 (let ((min (pos-visible-in-window-p (point-min)))
1d4c1257 226 (max (pos-visible-in-window-p (1- (point-max)))))
f98e758c
RS
227 (cond (isearch-mode 'noprompt)
228 ((and min max)
e27ef4db 229 (cond (standard "Press q to exit, r to retain ")
a2535589 230 (neither)
e27ef4db 231 (t (setq neither (substitute-command-keys "Press \\[electric-help-exit] to exit, \\[electric-help-retain] to retain ")))))
a2535589 232 (min
e27ef4db 233 (cond (standard "Press SPC to scroll, q to exit, r to retain ")
a2535589 234 (up)
e27ef4db 235 (t (setq up (substitute-command-keys "Press \\[scroll-up] to scroll, \\[electric-help-exit] to exit, \\[electric-help-retain] to retain ")))))
a2535589 236 (max
f98e758c 237 (cond (standard "Press DEL to scroll back, q to exit, r to retain ")
a2535589 238 (down)
e27ef4db 239 (t (setq down (substitute-command-keys "Press \\[scroll-down] to scroll back, \\[electric-help-exit] to exit, \\[electric-help-retain] to retain ")))))
a2535589 240 (t
f98e758c 241 (cond (standard "Press SPC to scroll, DEL to scroll back, q to exit, r to retain ")
a2535589 242 (both)
e27ef4db 243 (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
244 t))))
245
246
247\f
248;(defun electric-help-scroll-up (arg)
249; ">>>Doc"
250; (interactive "P")
251; (if (and (null arg) (pos-visible-in-window-p (point-max)))
252; (electric-help-exit)
253; (scroll-up arg)))
254
255(defun electric-help-exit ()
24501f03 256 "Exit `with-electric-help', restoring the previous window/buffer configuration.
9201fa06 257\(The *Help* buffer will be buried.)"
a2535589 258 (interactive)
9201fa06
RS
259 ;; Make sure that we don't throw twice, even if two events cause
260 ;; calling this function:
261 (if (memq 'electric-help-retain mouse-leave-buffer-hook)
262 (progn
263 (remove-hook 'mouse-leave-buffer-hook 'electric-help-retain)
264 (throw 'exit t))))
a2535589
JA
265
266(defun electric-help-retain ()
24501f03 267 "Exit `with-electric-help', retaining the current window/buffer configuration.
a2535589
JA
268\(The *Help* buffer will not be selected, but \\[switch-to-buffer-other-window] RET
269will select it.)"
270 (interactive)
f98e758c
RS
271 ;; Make sure that we don't throw twice, even if two events cause
272 ;; calling this function:
2e8174d2
RS
273 (if (memq 'electric-help-retain mouse-leave-buffer-hook)
274 (progn
275 (remove-hook 'mouse-leave-buffer-hook 'electric-help-retain)
276 (throw 'exit '(retain)))))
a2535589
JA
277
278
a2535589
JA
279(defun electric-help-undefined ()
280 (interactive)
281 (error "%s is undefined -- Press %s to exit"
282 (mapconcat 'single-key-description (this-command-keys) " ")
e0db3d3f 283 (if (eq (key-binding "q" nil t) 'electric-help-exit)
f98e758c 284 "q"
a2535589
JA
285 (substitute-command-keys "\\[electric-help-exit]"))))
286
287
288;>>> this needs to be hairified (recursive help, anybody?)
289(defun electric-help-help ()
290 (interactive)
e0db3d3f
JB
291 (if (and (eq (key-binding "q" nil t) 'electric-help-exit)
292 (eq (key-binding " " nil t) 'scroll-up)
293 (eq (key-binding "\^?" nil t) 'scroll-down)
294 (eq (key-binding "r" nil t) 'electric-help-retain))
e27ef4db
RS
295 (message "SPC scrolls up, DEL scrolls down, q exits burying help buffer, r exits")
296 (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
297 (sit-for 2))
298
299\f
9dd2b34e 300;;;###autoload
1676b298
RS
301(defun electric-helpify (fun &optional name)
302 (let ((name (or name "*Help*")))
a2535589
JA
303 (if (save-window-excursion
304 ;; kludge-o-rama
d5d105e8 305 (let* ((p (symbol-function 'help-print-return-message))
a2535589
JA
306 (b (get-buffer name))
307 (m (buffer-modified-p b)))
308 (and b (not (get-buffer-window b))
309 (setq b nil))
310 (unwind-protect
311 (progn
312 (message "%s..." (capitalize (symbol-name fun)))
313 ;; with-output-to-temp-buffer marks the buffer as unmodified.
314 ;; kludging excessively and relying on that as some sort
315 ;; of indication leads to the following abomination...
316 ;;>> This would be doable without such icky kludges if either
317 ;;>> (a) there were a function to read the interactive
318 ;;>> args for a command and return a list of those args.
319 ;;>> (To which one would then just apply the command)
320 ;;>> (The only problem with this is that interactive-p
321 ;;>> would break, but that is such a misfeature in
322 ;;>> any case that I don't care)
323 ;;>> It is easy to do this for emacs-lisp functions;
324 ;;>> the only problem is getting the interactive spec
325 ;;>> for subrs
326 ;;>> (b) there were a function which returned a
327 ;;>> modification-tick for a buffer. One could tell
328 ;;>> whether a buffer had changed by whether the
329 ;;>> modification-tick were different.
330 ;;>> (Presumably there would have to be a way to either
331 ;;>> restore the tick to some previous value, or to
332 ;;>> suspend updating of the tick in order to allow
333 ;;>> things like momentary-string-display)
334 (and b
7fdbcd83 335 (with-current-buffer b
a2535589 336 (set-buffer-modified-p t)))
d5d105e8 337 (fset 'help-print-return-message 'ignore)
a2535589
JA
338 (call-interactively fun)
339 (and (get-buffer name)
340 (get-buffer-window (get-buffer name))
341 (or (not b)
342 (not (eq b (get-buffer name)))
343 (not (buffer-modified-p b)))))
d5d105e8 344 (fset 'help-print-return-message p)
a2535589 345 (and b (buffer-name b)
7fdbcd83 346 (with-current-buffer b
a2535589
JA
347 (set-buffer-modified-p m))))))
348 (with-electric-help 'ignore name t))))
349
350\f
f98e758c 351
71296446 352;; This is to be bound to M-x in ehelp mode. Retains ehelp buffer and then
f98e758c 353;; continues with execute-extended-command.
2fbc1934 354(defun electric-help-execute-extended (_prefixarg)
f98e758c 355 (interactive "p")
bfeae2cf
SM
356 (setq electric-help-form-to-execute
357 (lambda () (execute-extended-command nil)))
f98e758c
RS
358 (electric-help-retain))
359
360;; This is to be buond to C-x in ehelp mode. Retains ehelp buffer and then
361;; continues with ctrl-x prefix.
2fbc1934 362(defun electric-help-ctrl-x-prefix (_prefixarg)
f98e758c 363 (interactive "p")
bfeae2cf
SM
364 (setq electric-help-form-to-execute
365 (lambda ()
366 (message nil)
367 (setq unread-command-events
368 (append unread-command-events '(?\C-x)))))
f98e758c
RS
369 (electric-help-retain))
370
371\f
a2535589
JA
372(defun electric-describe-key ()
373 (interactive)
374 (electric-helpify 'describe-key))
375
376(defun electric-describe-mode ()
377 (interactive)
378 (electric-helpify 'describe-mode))
379
380(defun electric-view-lossage ()
381 (interactive)
382 (electric-helpify 'view-lossage))
383
384;(defun electric-help-for-help ()
385; "See help-for-help"
386; (interactive)
387; )
388
389(defun electric-describe-function ()
390 (interactive)
391 (electric-helpify 'describe-function))
392
393(defun electric-describe-variable ()
394 (interactive)
395 (electric-helpify 'describe-variable))
396
397(defun electric-describe-bindings ()
398 (interactive)
399 (electric-helpify 'describe-bindings))
400
401(defun electric-describe-syntax ()
402 (interactive)
403 (electric-helpify 'describe-syntax))
404
405(defun electric-command-apropos ()
406 (interactive)
1676b298 407 (electric-helpify 'command-apropos "*Apropos*"))
a2535589
JA
408
409;(define-key help-map "a" 'electric-command-apropos)
410
e27ef4db
RS
411(defun electric-apropos ()
412 (interactive)
413 (electric-helpify 'apropos))
a2535589 414
a2535589
JA
415\f
416;;;; ehelp-map
417
3fd29551 418(defvar ehelp-map
71296446 419 (let ((map (copy-keymap help-map)))
f98e758c 420 (substitute-key-definition 'apropos 'electric-apropos map)
e27ef4db 421 (substitute-key-definition 'command-apropos 'electric-command-apropos map)
a2535589
JA
422 (substitute-key-definition 'describe-key 'electric-describe-key map)
423 (substitute-key-definition 'describe-mode 'electric-describe-mode map)
424 (substitute-key-definition 'view-lossage 'electric-view-lossage map)
425 (substitute-key-definition 'describe-function 'electric-describe-function map)
426 (substitute-key-definition 'describe-variable 'electric-describe-variable map)
427 (substitute-key-definition 'describe-bindings 'electric-describe-bindings map)
428 (substitute-key-definition 'describe-syntax 'electric-describe-syntax map)
3fd29551 429 map))
6e5cbb63
RS
430
431;;;###(autoload 'ehelp-command "ehelp" "Prefix command for ehelp." t 'keymap)
432(defalias 'ehelp-command ehelp-map)
433(put 'ehelp-command 'documentation "Prefix command for ehelp.")
a2535589 434
71296446 435(provide 'ehelp)
49116ac0 436
c0274f38 437;;; ehelp.el ends here