scheme interaction mode
[bpt/emacs.git] / lisp / ehelp.el
... / ...
CommitLineData
1;;; ehelp.el --- bindings for electric-help mode -*- lexical-binding: t -*-
2
3;; Copyright (C) 1986, 1995, 2000-2014 Free Software Foundation, Inc.
4
5;; Author: Richard Mlynarik
6;; (according to ack.texi and authors.el)
7;; Maintainer: emacs-devel@gnu.org
8;; Keywords: help, extensions
9
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software: you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
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
23;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24
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,
29;; `with-electric-help'; all you have to give it is a no-argument
30;; function that generates the actual text of the help into the current
31;; buffer.
32
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
39;;; Code:
40
41(require 'electric)
42
43(defvar electric-help-form-to-execute nil)
44
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
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
60(put 'electric-help-undefined 'suppress-keymap t)
61
62(defvar electric-help-map
63 (let ((map (make-keymap)))
64 ;; FIXME fragile. Should derive from help-mode-map in a smarter way.
65 (set-keymap-parent map button-buffer-map)
66 ;; allow all non-self-inserting keys - search, scroll, etc, but
67 ;; let M-x and C-x exit ehelp mode and retain buffer:
68 (suppress-keymap map)
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)
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 [?\S-\ ] 'scroll-down)
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 map)
97 "Keymap defining commands available in `electric-help-mode'.")
98
99(defvar electric-help-orig-major-mode nil)
100(make-variable-buffer-local 'electric-help-orig-major-mode)
101
102(defun electric-help-mode ()
103 "`with-electric-help' temporarily places its buffer in this mode.
104\(On exit from `with-electric-help', the original `major-mode' is restored.)"
105 (setq buffer-read-only t)
106 (setq electric-help-orig-major-mode major-mode)
107 (setq mode-name "Help")
108 (setq major-mode 'help-mode)
109 (setq mode-line-buffer-identification '(" Help: %b"))
110 (use-local-map electric-help-map)
111 (add-hook 'mouse-leave-buffer-hook 'electric-help-retain)
112 (view-mode -1)
113 ;; this is done below in with-electric-help
114 ;(run-hooks 'electric-help-mode-hook)
115 )
116
117;;;###autoload
118(defun with-electric-help (thunk &optional buffer noerase minheight)
119 "Pop up an \"electric\" help buffer.
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
124the buffer specified by BUFFER.
125
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.
128
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.
133
134If THUNK returns nil, we display BUFFER starting at the top, and
135shrink the window to fit if `electric-help-shrink-window' is non-nil.
136If THUNK returns non-nil, we don't do those things.
137
138When the user exits (with `electric-help-exit', or otherwise), the help
139buffer's window disappears (i.e., we use `save-window-excursion'), and
140BUFFER is put back into its original major mode."
141 (setq buffer (get-buffer-create (or buffer "*Help*")))
142 (let ((one (one-window-p t))
143 (config (current-window-configuration))
144 (bury nil)
145 (electric-help-form-to-execute nil))
146 (unwind-protect
147 (save-excursion
148 (when one
149 (goto-char (window-start)))
150 (let ((pop-up-windows t))
151 (pop-to-buffer buffer))
152 (with-current-buffer buffer
153 (when (and minheight (< (window-height) minheight))
154 (enlarge-window (- minheight (window-height))))
155 (electric-help-mode)
156 (setq buffer-read-only nil)
157 (unless noerase
158 (erase-buffer)))
159 (let ((standard-output buffer))
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))))
166 (set-buffer buffer)
167 (run-hooks 'electric-help-mode-hook)
168 (setq buffer-read-only t)
169 (if (eq (car-safe (electric-help-command-loop)) 'retain)
170 (setq config (current-window-configuration))
171 (setq bury t))
172 ;; Remove the hook.
173 (when (memq 'electric-help-retain mouse-leave-buffer-hook)
174 (remove-hook 'mouse-leave-buffer-hook 'electric-help-retain)))
175 (message "")
176 (set-buffer buffer)
177 (setq buffer-read-only nil)
178
179 ;; Restore the original major mode saved by `electric-help-mode'.
180 ;; We should really get a usable *Help* buffer when retaining
181 ;; the electric one with `r'. The problem is that a simple
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.
188 (condition-case ()
189 (funcall (or electric-help-orig-major-mode 'fundamental-mode))
190 (error nil))
191
192 (set-window-configuration config)
193 (when bury
194 ;;>> Perhaps this shouldn't be done,
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))
199 (if (functionp electric-help-form-to-execute)
200 (funcall electric-help-form-to-execute)
201 (eval electric-help-form-to-execute)))))
202
203(defun electric-help-command-loop ()
204 (catch 'exit
205 (if (pos-visible-in-window-p (point-max))
206 (progn (message "%s" (substitute-command-keys "<<< Press Space to bury the help buffer, Press \\[electric-help-retain] to retain it >>>"))
207 (if (equal (setq unread-command-events (list (read-event)))
208 '(?\s))
209 (progn (setq unread-command-events nil)
210 (throw 'exit t)))))
211 (let (up down both neither
212 (standard (and (eq (key-binding " " nil t)
213 'scroll-up)
214 (eq (key-binding "\^?" nil t)
215 'scroll-down)
216 (eq (key-binding "q" nil t)
217 'electric-help-exit)
218 (eq (key-binding "r" nil t)
219 'electric-help-retain))))
220 (Electric-command-loop
221 'exit
222 (function (lambda ()
223 (sit-for 0) ;necessary if last command was end-of-buffer or
224 ;beginning-of-buffer - otherwise pos-visible-in-window-p
225 ;will yield a wrong result.
226 (let ((min (pos-visible-in-window-p (point-min)))
227 (max (pos-visible-in-window-p (1- (point-max)))))
228 (cond (isearch-mode 'noprompt)
229 ((and min max)
230 (cond (standard "Press q to exit, r to retain ")
231 (neither)
232 (t (setq neither (substitute-command-keys "Press \\[electric-help-exit] to exit, \\[electric-help-retain] to retain ")))))
233 (min
234 (cond (standard "Press SPC to scroll, q to exit, r to retain ")
235 (up)
236 (t (setq up (substitute-command-keys "Press \\[scroll-up] to scroll, \\[electric-help-exit] to exit, \\[electric-help-retain] to retain ")))))
237 (max
238 (cond (standard "Press DEL to scroll back, q to exit, r to retain ")
239 (down)
240 (t (setq down (substitute-command-keys "Press \\[scroll-down] to scroll back, \\[electric-help-exit] to exit, \\[electric-help-retain] to retain ")))))
241 (t
242 (cond (standard "Press SPC to scroll, DEL to scroll back, q to exit, r to retain ")
243 (both)
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 ")))))))))
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 ()
257 "Exit `with-electric-help', restoring the previous window/buffer configuration.
258\(The *Help* buffer will be buried.)"
259 (interactive)
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))))
266
267(defun electric-help-retain ()
268 "Exit `with-electric-help', retaining the current window/buffer configuration.
269\(The *Help* buffer will not be selected, but \\[switch-to-buffer-other-window] RET
270will select it.)"
271 (interactive)
272 ;; Make sure that we don't throw twice, even if two events cause
273 ;; calling this function:
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)))))
278
279
280(defun electric-help-undefined ()
281 (interactive)
282 (error "%s is undefined -- Press %s to exit"
283 (mapconcat 'single-key-description (this-command-keys) " ")
284 (if (eq (key-binding "q" nil t) 'electric-help-exit)
285 "q"
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)
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))
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")))
298 (sit-for 2))
299
300\f
301;;;###autoload
302(defun electric-helpify (fun &optional name)
303 (let ((name (or name "*Help*")))
304 (if (save-window-excursion
305 ;; kludge-o-rama
306 (let* ((p (symbol-function 'help-print-return-message))
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
336 (with-current-buffer b
337 (set-buffer-modified-p t)))
338 (fset 'help-print-return-message 'ignore)
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)))))
345 (fset 'help-print-return-message p)
346 (and b (buffer-name b)
347 (with-current-buffer b
348 (set-buffer-modified-p m))))))
349 (with-electric-help 'ignore name t))))
350
351\f
352
353;; This is to be bound to M-x in ehelp mode. Retains ehelp buffer and then
354;; continues with execute-extended-command.
355(defun electric-help-execute-extended (_prefixarg)
356 (interactive "p")
357 (setq electric-help-form-to-execute
358 (lambda () (execute-extended-command nil)))
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.
363(defun electric-help-ctrl-x-prefix (_prefixarg)
364 (interactive "p")
365 (setq electric-help-form-to-execute
366 (lambda ()
367 (message nil)
368 (setq unread-command-events
369 (append unread-command-events '(?\C-x)))))
370 (electric-help-retain))
371
372\f
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)
408 (electric-helpify 'command-apropos "*Apropos*"))
409
410;(define-key help-map "a" 'electric-command-apropos)
411
412(defun electric-apropos ()
413 (interactive)
414 (electric-helpify 'apropos))
415
416\f
417;;;; ehelp-map
418
419(defvar ehelp-map
420 (let ((map (copy-keymap help-map)))
421 (substitute-key-definition 'apropos 'electric-apropos map)
422 (substitute-key-definition 'command-apropos 'electric-command-apropos map)
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)
430 map))
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.")
435
436(provide 'ehelp)
437
438;;; ehelp.el ends here