(defgroup reftex): Update home page url-link.
[bpt/emacs.git] / lisp / ehelp.el
... / ...
CommitLineData
1;;; ehelp.el --- bindings for electric-help mode
2
3;; Copyright (C) 1986, 1995, 2000, 2001, 2002, 2003, 2004,
4;; 2005, 2006 Free Software Foundation, Inc.
5
6;; Maintainer: FSF
7;; Keywords: help, extensions
8
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
13;; the Free Software Foundation; either version 2, or (at your option)
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
22;; along with GNU Emacs; see the file COPYING. If not, write to the
23;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24;; Boston, MA 02110-1301, USA.
25
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,
30;; `with-electric-help'; all you have to give it is a no-argument
31;; function that generates the actual text of the help into the current
32;; buffer.
33
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
40;;; Code:
41
42(require 'electric)
43(defvar electric-help-map ()
44 "Keymap defining commands available in `electric-help-mode'.")
45
46(defvar electric-help-form-to-execute nil)
47
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
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
63(put 'electric-help-undefined 'suppress-keymap t)
64(if electric-help-map
65 ()
66 (let ((map (make-keymap)))
67 ;; allow all non-self-inserting keys - search, scroll, etc, but
68 ;; let M-x and C-x exit ehelp mode and retain buffer:
69 (suppress-keymap map)
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)
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)
89 (define-key map "Q" 'electric-help-exit)
90 (define-key map "q" 'electric-help-exit)
91 ;;a better key than this?
92 (define-key map "R" 'electric-help-retain)
93 (define-key map "r" 'electric-help-retain)
94 (define-key map "\ex" 'electric-help-execute-extended)
95 (define-key map "\C-x" 'electric-help-ctrl-x-prefix)
96
97 (setq electric-help-map map)))
98
99(defun electric-help-mode ()
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'.)"
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)
107 (add-hook 'mouse-leave-buffer-hook 'electric-help-retain)
108 (view-mode -1)
109 ;; this is done below in with-electric-help
110 ;(run-hooks 'electric-help-mode-hook)
111 )
112
113;;;###autoload
114(defun with-electric-help (thunk &optional buffer noerase minheight)
115 "Pop up an \"electric\" help buffer.
116The arguments are THUNK &optional BUFFER NOERASE MINHEIGHT.
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
121the buffer specified by BUFFER.
122
123If THUNK returns nil, we display BUFFER starting at the top, and
124shrink the window to fit. If THUNK returns non-nil, we don't do those things.
125
126After THUNK has been called, this function \"electrically\" pops up a window
127in which BUFFER is displayed and allows the user to scroll through that buffer
128in electric-help-mode. The window's height will be at least MINHEIGHT if
129this value is non-nil.
130
131If THUNK returns nil, we display BUFFER starting at the top, and
132shrink the window to fit if `electric-help-shrink-window' is non-nil.
133If THUNK returns non-nil, we don't do those things.
134
135When the user exits (with `electric-help-exit', or otherwise), the help
136buffer's window disappears (i.e., we use `save-window-excursion'), and
137BUFFER is put into `default-major-mode' (or `fundamental-mode') when we exit."
138 (setq buffer (get-buffer-create (or buffer "*Help*")))
139 (let ((one (one-window-p t))
140 (config (current-window-configuration))
141 (bury nil)
142 (electric-help-form-to-execute nil))
143 (unwind-protect
144 (save-excursion
145 (when one
146 (goto-char (window-start (selected-window))))
147 (let ((pop-up-windows t))
148 (pop-to-buffer buffer))
149 (save-excursion
150 (set-buffer buffer)
151 (when (and minheight (< (window-height) minheight))
152 (enlarge-window (- minheight (window-height))))
153 (electric-help-mode)
154 (setq buffer-read-only nil)
155 (unless noerase
156 (erase-buffer)))
157 (let ((standard-output buffer))
158 (unless (funcall thunk)
159 (set-buffer buffer)
160 (set-buffer-modified-p nil)
161 (goto-char (point-min))
162 (when (and one electric-help-shrink-window)
163 (shrink-window-if-larger-than-buffer))))
164 (set-buffer buffer)
165 (run-hooks 'electric-help-mode-hook)
166 (setq buffer-read-only t)
167 (if (eq (car-safe (electric-help-command-loop)) 'retain)
168 (setq config (current-window-configuration))
169 (setq bury t))
170 ;; Remove the hook.
171 (when (memq 'electric-help-retain mouse-leave-buffer-hook)
172 (remove-hook 'mouse-leave-buffer-hook 'electric-help-retain)))
173 (message "")
174 (set-buffer buffer)
175 (setq buffer-read-only nil)
176
177 ;; We should really get a usable *Help* buffer when retaining
178 ;; the electric one with `r'. The problem is that a simple
179 ;; call to help-mode won't cut it; at least RET is bound wrong
180 ;; afterwards. It's also not clear that `help-mode' is always
181 ;; the right thing, maybe we should add an optional parameter.
182 (condition-case ()
183 (funcall (or default-major-mode 'fundamental-mode))
184 (error nil))
185
186 (set-window-configuration config)
187 (when bury
188 ;;>> Perhaps this shouldn't be done,
189 ;; so that when we say "Press space to bury" we mean it
190 (replace-buffer-in-windows buffer)
191 ;; must do this outside of save-window-excursion
192 (bury-buffer buffer))
193 (eval electric-help-form-to-execute))))
194
195(defun electric-help-command-loop ()
196 (catch 'exit
197 (if (pos-visible-in-window-p (point-max))
198 (progn (message "%s" (substitute-command-keys "<<< Press Space to bury the help buffer, Press \\[electric-help-retain] to retain it >>>"))
199 (if (equal (setq unread-command-events (list (read-event)))
200 '(?\ ))
201 (progn (setq unread-command-events nil)
202 (throw 'exit t)))))
203 (let (up down both neither
204 (standard (and (eq (key-binding " " nil t)
205 'scroll-up)
206 (eq (key-binding "\^?" nil t)
207 'scroll-down)
208 (eq (key-binding "q" nil t)
209 'electric-help-exit)
210 (eq (key-binding "r" nil t)
211 'electric-help-retain))))
212 (Electric-command-loop
213 'exit
214 (function (lambda ()
215 (sit-for 0) ;necessary if last command was end-of-buffer or
216 ;beginning-of-buffer - otherwise pos-visible-in-window-p
217 ;will yield a wrong result.
218 (let ((min (pos-visible-in-window-p (point-min)))
219 (max (pos-visible-in-window-p (1- (point-max)))))
220 (cond (isearch-mode 'noprompt)
221 ((and min max)
222 (cond (standard "Press q to exit, r to retain ")
223 (neither)
224 (t (setq neither (substitute-command-keys "Press \\[electric-help-exit] to exit, \\[electric-help-retain] to retain ")))))
225 (min
226 (cond (standard "Press SPC to scroll, q to exit, r to retain ")
227 (up)
228 (t (setq up (substitute-command-keys "Press \\[scroll-up] to scroll, \\[electric-help-exit] to exit, \\[electric-help-retain] to retain ")))))
229 (max
230 (cond (standard "Press DEL to scroll back, q to exit, r to retain ")
231 (down)
232 (t (setq down (substitute-command-keys "Press \\[scroll-down] to scroll back, \\[electric-help-exit] to exit, \\[electric-help-retain] to retain ")))))
233 (t
234 (cond (standard "Press SPC to scroll, DEL to scroll back, q to exit, r to retain ")
235 (both)
236 (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 ")))))))))
237 t))))
238
239
240\f
241;(defun electric-help-scroll-up (arg)
242; ">>>Doc"
243; (interactive "P")
244; (if (and (null arg) (pos-visible-in-window-p (point-max)))
245; (electric-help-exit)
246; (scroll-up arg)))
247
248(defun electric-help-exit ()
249 "Exit `electric-help', restoring the previous window/buffer configuration.
250\(The *Help* buffer will be buried.)"
251 (interactive)
252 ;; Make sure that we don't throw twice, even if two events cause
253 ;; calling this function:
254 (if (memq 'electric-help-retain mouse-leave-buffer-hook)
255 (progn
256 (remove-hook 'mouse-leave-buffer-hook 'electric-help-retain)
257 (throw 'exit t))))
258
259(defun electric-help-retain ()
260 "Exit `electric-help', retaining the current window/buffer configuration.
261\(The *Help* buffer will not be selected, but \\[switch-to-buffer-other-window] RET
262will select it.)"
263 (interactive)
264 ;; Make sure that we don't throw twice, even if two events cause
265 ;; calling this function:
266 (if (memq 'electric-help-retain mouse-leave-buffer-hook)
267 (progn
268 (remove-hook 'mouse-leave-buffer-hook 'electric-help-retain)
269 (throw 'exit '(retain)))))
270
271
272(defun electric-help-undefined ()
273 (interactive)
274 (error "%s is undefined -- Press %s to exit"
275 (mapconcat 'single-key-description (this-command-keys) " ")
276 (if (eq (key-binding "q" nil t) 'electric-help-exit)
277 "q"
278 (substitute-command-keys "\\[electric-help-exit]"))))
279
280
281;>>> this needs to be hairified (recursive help, anybody?)
282(defun electric-help-help ()
283 (interactive)
284 (if (and (eq (key-binding "q" nil t) 'electric-help-exit)
285 (eq (key-binding " " nil t) 'scroll-up)
286 (eq (key-binding "\^?" nil t) 'scroll-down)
287 (eq (key-binding "r" nil t) 'electric-help-retain))
288 (message "SPC scrolls up, DEL scrolls down, q exits burying help buffer, r exits")
289 (message "%s" (substitute-command-keys "\\[scroll-up] scrolls up, \\[scroll-down] scrolls down, \\[electric-help-exit] exits burying help buffer, \\[electric-help-retain] exits")))
290 (sit-for 2))
291
292\f
293;;;###autoload
294(defun electric-helpify (fun &optional name)
295 (let ((name (or name "*Help*")))
296 (if (save-window-excursion
297 ;; kludge-o-rama
298 (let* ((p (symbol-function 'print-help-return-message))
299 (b (get-buffer name))
300 (m (buffer-modified-p b)))
301 (and b (not (get-buffer-window b))
302 (setq b nil))
303 (unwind-protect
304 (progn
305 (message "%s..." (capitalize (symbol-name fun)))
306 ;; with-output-to-temp-buffer marks the buffer as unmodified.
307 ;; kludging excessively and relying on that as some sort
308 ;; of indication leads to the following abomination...
309 ;;>> This would be doable without such icky kludges if either
310 ;;>> (a) there were a function to read the interactive
311 ;;>> args for a command and return a list of those args.
312 ;;>> (To which one would then just apply the command)
313 ;;>> (The only problem with this is that interactive-p
314 ;;>> would break, but that is such a misfeature in
315 ;;>> any case that I don't care)
316 ;;>> It is easy to do this for emacs-lisp functions;
317 ;;>> the only problem is getting the interactive spec
318 ;;>> for subrs
319 ;;>> (b) there were a function which returned a
320 ;;>> modification-tick for a buffer. One could tell
321 ;;>> whether a buffer had changed by whether the
322 ;;>> modification-tick were different.
323 ;;>> (Presumably there would have to be a way to either
324 ;;>> restore the tick to some previous value, or to
325 ;;>> suspend updating of the tick in order to allow
326 ;;>> things like momentary-string-display)
327 (and b
328 (save-excursion
329 (set-buffer b)
330 (set-buffer-modified-p t)))
331 (fset 'print-help-return-message 'ignore)
332 (call-interactively fun)
333 (and (get-buffer name)
334 (get-buffer-window (get-buffer name))
335 (or (not b)
336 (not (eq b (get-buffer name)))
337 (not (buffer-modified-p b)))))
338 (fset 'print-help-return-message p)
339 (and b (buffer-name b)
340 (save-excursion
341 (set-buffer b)
342 (set-buffer-modified-p m))))))
343 (with-electric-help 'ignore name t))))
344
345\f
346
347;; This is to be bound to M-x in ehelp mode. Retains ehelp buffer and then
348;; continues with execute-extended-command.
349(defun electric-help-execute-extended (prefixarg)
350 (interactive "p")
351 (setq electric-help-form-to-execute '(execute-extended-command nil))
352 (electric-help-retain))
353
354;; This is to be buond to C-x in ehelp mode. Retains ehelp buffer and then
355;; continues with ctrl-x prefix.
356(defun electric-help-ctrl-x-prefix (prefixarg)
357 (interactive "p")
358 (setq electric-help-form-to-execute '(progn (message nil) (setq unread-command-char ?\C-x)))
359 (electric-help-retain))
360
361\f
362(defun electric-describe-key ()
363 (interactive)
364 (electric-helpify 'describe-key))
365
366(defun electric-describe-mode ()
367 (interactive)
368 (electric-helpify 'describe-mode))
369
370(defun electric-view-lossage ()
371 (interactive)
372 (electric-helpify 'view-lossage))
373
374;(defun electric-help-for-help ()
375; "See help-for-help"
376; (interactive)
377; )
378
379(defun electric-describe-function ()
380 (interactive)
381 (electric-helpify 'describe-function))
382
383(defun electric-describe-variable ()
384 (interactive)
385 (electric-helpify 'describe-variable))
386
387(defun electric-describe-bindings ()
388 (interactive)
389 (electric-helpify 'describe-bindings))
390
391(defun electric-describe-syntax ()
392 (interactive)
393 (electric-helpify 'describe-syntax))
394
395(defun electric-command-apropos ()
396 (interactive)
397 (electric-helpify 'command-apropos "*Apropos*"))
398
399;(define-key help-map "a" 'electric-command-apropos)
400
401(defun electric-apropos ()
402 (interactive)
403 (electric-helpify 'apropos))
404
405\f
406;;;; ehelp-map
407
408(defvar ehelp-map ())
409(if ehelp-map
410 nil
411 (let ((map (copy-keymap help-map)))
412 (substitute-key-definition 'apropos 'electric-apropos map)
413 (substitute-key-definition 'command-apropos 'electric-command-apropos map)
414 (substitute-key-definition 'describe-key 'electric-describe-key map)
415 (substitute-key-definition 'describe-mode 'electric-describe-mode map)
416 (substitute-key-definition 'view-lossage 'electric-view-lossage map)
417 (substitute-key-definition 'describe-function 'electric-describe-function map)
418 (substitute-key-definition 'describe-variable 'electric-describe-variable map)
419 (substitute-key-definition 'describe-bindings 'electric-describe-bindings map)
420 (substitute-key-definition 'describe-syntax 'electric-describe-syntax map)
421
422 (setq ehelp-map map)))
423
424;;;###(autoload 'ehelp-command "ehelp" "Prefix command for ehelp." t 'keymap)
425(defalias 'ehelp-command ehelp-map)
426(put 'ehelp-command 'documentation "Prefix command for ehelp.")
427
428(provide 'ehelp)
429
430;;; arch-tag: e0e3037f-42c0-433e-ba18-322c5d951f46
431;;; ehelp.el ends here