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