(Vertical Scrolling): Document what does nil mean as the first arg of
[bpt/emacs.git] / lisp / help-mode.el
CommitLineData
f4ed5b19
MB
1;;; help-mode.el --- `help-mode' used by *Help* buffers
2
3;; Copyright (C) 1985, 1986, 1993, 1994, 1998, 1999, 2000, 2001
4;; Free Software Foundation, Inc.
5
6;; Maintainer: FSF
7;; Keywords: help, internal
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., 59 Temple Place - Suite 330,
24;; Boston, MA 02111-1307, USA.
25
26;;; Commentary:
27
28;; Defines `help-mode', which is the mode used by *Help* buffers, and
29;; associated support machinery, such as adding hyperlinks, etc.,
30
31;;; Code:
32
33(require 'button)
34(eval-when-compile (require 'view))
35
36(defvar help-mode-map (make-sparse-keymap)
37 "Keymap for help mode.")
38
39(set-keymap-parent help-mode-map button-buffer-map)
40
89f5b33f 41(define-key help-mode-map [mouse-2] 'help-follow-mouse)
f4ed5b19
MB
42(define-key help-mode-map "\C-c\C-b" 'help-go-back)
43(define-key help-mode-map "\C-c\C-c" 'help-follow)
44;; Documentation only, since we use minor-mode-overriding-map-alist.
45(define-key help-mode-map "\r" 'help-follow)
46
47(defvar help-xref-stack nil
48 "A stack of ways by which to return to help buffers after following xrefs.
49Used by `help-follow' and `help-xref-go-back'.
89f5b33f
SM
50An element looks like (POSITION FUNCTION ARGS...).
51To use the element, do (apply FUNCTION ARGS) then goto the point.")
f4ed5b19 52(put 'help-xref-stack 'permanent-local t)
89f5b33f 53(make-variable-buffer-local 'help-xref-stack)
f4ed5b19
MB
54
55(defvar help-xref-stack-item nil
56 "An item for `help-follow' in this buffer to push onto `help-xref-stack'.
57The format is (FUNCTION ARGS...).")
58(put 'help-xref-stack-item 'permanent-local t)
89f5b33f 59(make-variable-buffer-local 'help-xref-stack-item)
f4ed5b19
MB
60
61(setq-default help-xref-stack nil help-xref-stack-item nil)
62
1700f41d
RS
63(defcustom help-mode-hook nil
64 "Hook run by `help-mode'."
65 :type 'hook
66 :group 'help)
f4ed5b19
MB
67\f
68;; Button types used by help
69
6e881567
MB
70(define-button-type 'help-xref
71 'action #'help-button-action)
72
73(defun help-button-action (button)
74 "Call BUTTON's help function."
75 (help-do-xref (button-start button)
76 (button-get button 'help-function)
77 (button-get button 'help-args)))
78
f4ed5b19
MB
79;; Make some button types that all use the same naming conventions
80(dolist (help-type '("function" "variable" "face"
81 "coding-system" "input-method" "character-set"))
82 (define-button-type (intern (purecopy (concat "help-" help-type)))
6e881567 83 :supertype 'help-xref
f4ed5b19 84 'help-function (intern (concat "describe-" help-type))
6e881567 85 'help-echo (purecopy (concat "mouse-2, RET: describe this " help-type))))
f4ed5b19
MB
86
87;; make some more ideosyncratic button types
88
89(define-button-type 'help-symbol
6e881567 90 :supertype 'help-xref
f4ed5b19 91 'help-function #'help-xref-interned
6e881567 92 'help-echo (purecopy "mouse-2, RET: describe this symbol"))
f4ed5b19
MB
93
94(define-button-type 'help-back
6e881567 95 :supertype 'help-xref
f4ed5b19 96 'help-function #'help-xref-go-back
6e881567 97 'help-echo (purecopy "mouse-2, RET: go back to previous help buffer"))
f4ed5b19
MB
98
99(define-button-type 'help-info
6e881567 100 :supertype 'help-xref
f4ed5b19 101 'help-function #'info
6e881567 102 'help-echo (purecopy"mouse-2, RET: read this Info node"))
f4ed5b19
MB
103
104(define-button-type 'help-customize-variable
6e881567 105 :supertype 'help-xref
f4ed5b19
MB
106 'help-function (lambda (v)
107 (if help-xref-stack
108 (pop help-xref-stack))
109 (customize-variable v))
6e881567 110 'help-echo (purecopy "mouse-2, RET: customize variable"))
f4ed5b19 111
07f904a3 112(define-button-type 'help-customize-face
6e881567 113 :supertype 'help-xref
07f904a3
MB
114 'help-function (lambda (v)
115 (if help-xref-stack
116 (pop help-xref-stack))
117 (customize-face v))
6e881567 118 'help-echo (purecopy "mouse-2, RET: customize face"))
07f904a3 119
f4ed5b19 120(define-button-type 'help-function-def
6e881567 121 :supertype 'help-xref
f4ed5b19
MB
122 'help-function (lambda (fun file)
123 (require 'find-func)
124 ;; Don't use find-function-noselect because it follows
125 ;; aliases (which fails for built-in functions).
126 (let* ((location (find-function-search-for-symbol
127 fun nil file)))
128 (pop-to-buffer (car location))
129 (goto-char (cdr location))))
6e881567 130 'help-echo (purecopy "mouse-2, RET: find function's definition"))
f4ed5b19
MB
131
132(define-button-type 'help-variable-def
6e881567 133 :supertype 'help-xref
f4ed5b19
MB
134 'help-function (lambda (var &optional file)
135 (let ((location
136 (find-variable-noselect var file)))
137 (pop-to-buffer (car location))
138 (goto-char (cdr location))))
6e881567 139 'help-echo (purecopy"mouse-2, RET: find variable's definition"))
f4ed5b19
MB
140
141\f
142;;;###autoload
1700f41d 143(defun help-mode ()
f4ed5b19
MB
144 "Major mode for viewing help text and navigating references in it.
145Entry to this mode runs the normal hook `help-mode-hook'.
146Commands:
147\\{help-mode-map}"
1700f41d
RS
148 (interactive)
149 (kill-all-local-variables)
150 (use-local-map help-mode-map)
151 (setq mode-name "Help")
152 (setq major-mode 'help-mode)
153 (make-local-variable 'font-lock-defaults)
f4ed5b19
MB
154 (setq font-lock-defaults nil) ; font-lock would defeat xref
155 (view-mode)
156 (make-local-variable 'view-no-disable-on-exit)
1700f41d
RS
157 (setq view-no-disable-on-exit t)
158 (run-hooks 'help-mode-hook))
f4ed5b19
MB
159
160;;;###autoload
161(defun help-mode-setup ()
162 (help-mode)
163 (setq buffer-read-only nil))
164
165;;;###autoload
166(defun help-mode-finish ()
167 (when (eq major-mode 'help-mode)
168 ;; View mode's read-only status of existing *Help* buffer is lost
169 ;; by with-output-to-temp-buffer.
170 (toggle-read-only 1)
171 (help-make-xrefs (current-buffer)))
172 (setq view-return-to-alist
173 (list (cons (selected-window) help-return-method))))
174
175\f
176;;; Grokking cross-reference information in doc strings and
177;;; hyperlinking it.
178
179;; This may have some scope for extension and the same or something
180;; similar should be done for widget doc strings, which currently use
181;; another mechanism.
182
183(defcustom help-highlight-p t
184 "*If non-nil, `help-make-xrefs' highlight cross-references.
185Under a window system it highlights them with face defined by
186`help-highlight-face'."
187 :group 'help
188 :version "20.3"
189 :type 'boolean)
190
191(defcustom help-highlight-face 'underline
192 "Face used by `help-make-xrefs' to highlight cross-references.
193Must be previously-defined."
194 :group 'help
195 :version "20.3"
196 :type 'face)
197
198(defvar help-back-label (purecopy "[back]")
199 "Label to use by `help-make-xrefs' for the go-back reference.")
200
201(defconst help-xref-symbol-regexp
202 (purecopy (concat "\\(\\<\\(\\(variable\\|option\\)\\|"
203 "\\(function\\|command\\)\\|"
204 "\\(face\\)\\|"
205 "\\(symbol\\)\\|"
206 "\\(source \\(?:code \\)?\\(?:of\\|for\\)\\)\\)\\s-+\\)?"
207 ;; Note starting with word-syntax character:
208 "`\\(\\sw\\(\\sw\\|\\s_\\)+\\)'"))
209 "Regexp matching doc string references to symbols.
210
211The words preceding the quoted symbol can be used in doc strings to
212distinguish references to variables, functions and symbols.")
213
214(defconst help-xref-mule-regexp nil
215 "Regexp matching doc string references to MULE-related keywords.
216
217It is usually nil, and is temporarily bound to an appropriate regexp
218when help commands related to multilingual environment (e.g.,
219`describe-coding-system') are invoked.")
220
221
222(defconst help-xref-info-regexp
223 (purecopy "\\<[Ii]nfo[ \t\n]+node[ \t\n]+`\\([^']+\\)'")
224 "Regexp matching doc string references to an Info node.")
225
226;;;###autoload
227(defun help-setup-xref (item interactive-p)
228 "Invoked from commands using the \"*Help*\" buffer to install some xref info.
229
230ITEM is a (FUNCTION . ARGS) pair appropriate for recreating the help
231buffer after following a reference. INTERACTIVE-P is non-nil if the
232calling command was invoked interactively. In this case the stack of
89f5b33f
SM
233items for help buffer \"back\" buttons is cleared.
234
235This should be called very early, before the output buffer is cleared,
236because we want to record the \"previous\" position of point so we can
237restore it properly when going back."
238 (with-current-buffer (help-buffer)
239 (if interactive-p
240 ;; Why do we want to prevent the user from going back ?? -stef
241 (setq help-xref-stack nil)
242 (when help-xref-stack-item
243 (push (cons (point) help-xref-stack-item) help-xref-stack)))
244 (setq help-xref-stack-item item)))
f4ed5b19
MB
245
246(defvar help-xref-following nil
247 "Non-nil when following a help cross-reference.")
248
89f5b33f 249(defun help-buffer ()
89f5b33f
SM
250 (buffer-name ;for with-output-to-temp-buffer
251 (if help-xref-following
252 (current-buffer)
253 (get-buffer-create "*Help*"))))
254
f4ed5b19
MB
255;;;###autoload
256(defun help-make-xrefs (&optional buffer)
257 "Parse and hyperlink documentation cross-references in the given BUFFER.
258
259Find cross-reference information in a buffer and, if
260`help-highlight-p' is non-nil, highlight it with face defined by
261`help-highlight-face'; activate such cross references for selection
262with `help-follow'. Cross-references have the canonical form `...'
263and the type of reference may be disambiguated by the preceding
264word(s) used in `help-xref-symbol-regexp'.
265
266If the variable `help-xref-mule-regexp' is non-nil, find also
267cross-reference information related to multilingual environment
268\(e.g., coding-systems). This variable is also used to disambiguate
269the type of reference as the same way as `help-xref-symbol-regexp'.
270
271A special reference `back' is made to return back through a stack of
272help buffers. Variable `help-back-label' specifies the text for
273that."
274 (interactive "b")
275 (save-excursion
276 (set-buffer (or buffer (current-buffer)))
277 (goto-char (point-min))
278 ;; Skip the header-type info, though it might be useful to parse
279 ;; it at some stage (e.g. "function in `library'").
280 (forward-paragraph)
281 (let ((old-modified (buffer-modified-p)))
282 (let ((stab (syntax-table))
283 (case-fold-search t)
284 (inhibit-read-only t))
285 (set-syntax-table emacs-lisp-mode-syntax-table)
286 ;; The following should probably be abstracted out.
287 (unwind-protect
288 (progn
289 ;; Info references
290 (save-excursion
291 (while (re-search-forward help-xref-info-regexp nil t)
292 (let ((data (match-string 1)))
293 (save-match-data
294 (unless (string-match "^([^)]+)" data)
295 (setq data (concat "(emacs)" data))))
296 (help-xref-button 1 'help-info data))))
297 ;; Mule related keywords. Do this before trying
298 ;; `help-xref-symbol-regexp' because some of Mule
299 ;; keywords have variable or function definitions.
300 (if help-xref-mule-regexp
301 (save-excursion
302 (while (re-search-forward help-xref-mule-regexp nil t)
303 (let* ((data (match-string 7))
304 (sym (intern-soft data)))
305 (cond
306 ((match-string 3) ; coding system
307 (and sym (coding-system-p sym)
308 (help-xref-button 6 'help-coding-system sym)))
309 ((match-string 4) ; input method
310 (and (assoc data input-method-alist)
311 (help-xref-button 7 'help-input-method data)))
312 ((or (match-string 5) (match-string 6)) ; charset
313 (and sym (charsetp sym)
314 (help-xref-button 7 'help-character-set sym)))
315 ((assoc data input-method-alist)
316 (help-xref-button 7 'help-character-set data))
317 ((and sym (coding-system-p sym))
318 (help-xref-button 7 'help-coding-system sym))
319 ((and sym (charsetp sym))
320 (help-xref-button 7 'help-character-set sym)))))))
321 ;; Quoted symbols
322 (save-excursion
323 (while (re-search-forward help-xref-symbol-regexp nil t)
324 (let* ((data (match-string 8))
325 (sym (intern-soft data)))
326 (if sym
327 (cond
328 ((match-string 3) ; `variable' &c
329 (and (boundp sym) ; `variable' doesn't ensure
330 ; it's actually bound
331 (help-xref-button 8 'help-variable sym)))
332 ((match-string 4) ; `function' &c
333 (and (fboundp sym) ; similarly
334 (help-xref-button 8 'help-function sym)))
335 ((match-string 5) ; `face'
336 (and (facep sym)
337 (help-xref-button 8 'help-face sym)))
338 ((match-string 6)) ; nothing for `symbol'
339 ((match-string 7)
340;; this used:
341;; #'(lambda (arg)
342;; (let ((location
343;; (find-function-noselect arg)))
344;; (pop-to-buffer (car location))
345;; (goto-char (cdr location))))
346 (help-xref-button 8 'help-function-def sym))
347 ((and (boundp sym) (fboundp sym))
348 ;; We can't intuit whether to use the
349 ;; variable or function doc -- supply both.
350 (help-xref-button 8 'help-symbol sym))
351 ((boundp sym)
352 (help-xref-button 8 'help-variable sym))
353 ((fboundp sym)
354 (help-xref-button 8 'help-function sym))
355 ((facep sym)
356 (help-xref-button 8 'help-face sym)))))))
357 ;; An obvious case of a key substitution:
358 (save-excursion
359 (while (re-search-forward
360 ;; Assume command name is only word characters
361 ;; and dashes to get things like `use M-x foo.'.
362 "\\<M-x\\s-+\\(\\sw\\(\\sw\\|-\\)+\\)" nil t)
363 (let ((sym (intern-soft (match-string 1))))
364 (if (fboundp sym)
365 (help-xref-button 1 'help-function sym)))))
366 ;; Look for commands in whole keymap substitutions:
367 (save-excursion
368 ;; Make sure to find the first keymap.
369 (goto-char (point-min))
370 ;; Find a header and the column at which the command
371 ;; name will be found.
372 (while (re-search-forward "^key +binding\n\\(-+ +\\)-+\n\n"
373 nil t)
374 (let ((col (- (match-end 1) (match-beginning 1))))
375 (while
376 ;; Ignore single blank lines in table, but not
377 ;; double ones, which should terminate it.
378 (and (not (looking-at "\n\\s-*\n"))
379 (progn
380 (and (eolp) (forward-line))
381 (end-of-line)
382 (skip-chars-backward "^\t\n")
383 (if (and (>= (current-column) col)
384 (looking-at "\\(\\sw\\|-\\)+$"))
385 (let ((sym (intern-soft (match-string 0))))
386 (if (fboundp sym)
387 (help-xref-button 0 'help-function sym))))
388 (zerop (forward-line)))))))))
389 (set-syntax-table stab))
390 ;; Delete extraneous newlines at the end of the docstring
391 (goto-char (point-max))
392 (while (and (not (bobp)) (bolp))
393 (delete-char -1))
394 ;; Make a back-reference in this buffer if appropriate.
89f5b33f 395 (when help-xref-stack
f4ed5b19
MB
396 (insert "\n\n")
397 (help-insert-xref-button help-back-label 'help-back
398 (current-buffer))))
399 ;; View mode steals RET from us.
400 (set (make-local-variable 'minor-mode-overriding-map-alist)
401 (list (cons 'view-mode
402 (let ((map (make-sparse-keymap)))
403 (set-keymap-parent map view-mode-map)
404 (define-key map "\r" 'help-follow)
405 map))))
406 (set-buffer-modified-p old-modified))))
407
408;;;###autoload
409(defun help-xref-button (match-number type &rest args)
410 "Make a hyperlink for cross-reference text previously matched.
411MATCH-NUMBER is the subexpression of interest in the last matched
412regexp. TYPE is the type of button to use. Any remaining arguments are
413passed to the button's help-function when it is invoked.
414See `help-make-xrefs'."
415 ;; Don't mung properties we've added specially in some instances.
416 (unless (button-at (match-beginning match-number))
417 (make-text-button (match-beginning match-number)
418 (match-end match-number)
419 'type type 'help-args args)))
420
421;;;###autoload
422(defun help-insert-xref-button (string type &rest args)
423 "Insert STRING and make a hyperlink from cross-reference text on it.
424TYPE is the type of button to use. Any remaining arguments are passed
425to the button's help-function when it is invoked.
426See `help-make-xrefs'."
427 (unless (button-at (point))
428 (insert-text-button string 'type type 'help-args args)))
429
430;;;###autoload
431(defun help-xref-on-pp (from to)
432 "Add xrefs for symbols in `pp's output between FROM and TO."
433 (let ((ost (syntax-table)))
434 (unwind-protect
435 (save-excursion
436 (save-restriction
437 (set-syntax-table emacs-lisp-mode-syntax-table)
438 (narrow-to-region from to)
439 (goto-char (point-min))
440 (while (not (eobp))
441 (cond
442 ((looking-at "\"") (forward-sexp 1))
443 ((looking-at "#<") (search-forward ">" nil 'move))
444 ((looking-at "\\(\\(\\sw\\|\\s_\\)+\\)")
445 (let* ((sym (intern-soft (match-string 1)))
446 (type (cond ((fboundp sym) 'help-function)
447 ((or (memq sym '(t nil))
448 (keywordp sym))
449 nil)
450 ((and sym (boundp sym))
451 'help-variable))))
452 (when type (help-xref-button 1 type sym)))
453 (goto-char (match-end 1)))
454 (t (forward-char 1))))))
455 (set-syntax-table ost))))
456
457\f
458;; Additional functions for (re-)creating types of help buffers.
459(defun help-xref-interned (symbol)
460 "Follow a hyperlink which appeared to be an arbitrary interned SYMBOL.
89f5b33f 461Both variable, function and face documentation are extracted into a single
f4ed5b19 462help buffer."
89f5b33f
SM
463 (with-current-buffer (help-buffer)
464 ;; Push the previous item on the stack before clobbering the output buffer.
465 (help-setup-xref nil nil)
466 (let ((facedoc (when (facep symbol)
467 ;; Don't record the current entry in the stack.
468 (setq help-xref-stack-item nil)
469 (describe-face symbol)))
470 (fdoc (when (fboundp symbol)
471 ;; Don't record the current entry in the stack.
472 (setq help-xref-stack-item nil)
473 (describe-function symbol)))
474 (sdoc (when (boundp symbol)
475 ;; Don't record the current entry in the stack.
476 (setq help-xref-stack-item nil)
477 (describe-variable symbol))))
478 (cond
479 (sdoc
480 ;; We now have a help buffer on the variable.
481 ;; Insert the function and face text before it.
f4ed5b19 482 (when (or fdoc facedoc)
f4ed5b19
MB
483 (goto-char (point-min))
484 (let ((inhibit-read-only t))
485 (when fdoc
89f5b33f 486 (insert fdoc "\n\n")
f4ed5b19
MB
487 (when facedoc
488 (insert (make-string 30 ?-) "\n\n" (symbol-name symbol)
89f5b33f
SM
489 " is also a " "face." "\n\n")))
490 (when facedoc
491 (insert facedoc "\n\n"))
f4ed5b19
MB
492 (insert (make-string 30 ?-) "\n\n" (symbol-name symbol)
493 " is also a " "variable." "\n\n"))
89f5b33f
SM
494 ;; Don't record the `describe-variable' item in the stack.
495 (setq help-xref-stack-item nil)
496 (help-setup-xref (list #'help-xref-interned symbol) nil)))
497 (fdoc
498 ;; We now have a help buffer on the function.
499 ;; Insert face text before it.
500 (when facedoc
501 (goto-char (point-max))
502 (let ((inhibit-read-only t))
503 (insert "\n\n" (make-string 30 ?-) "\n\n" (symbol-name symbol)
504 " is also a " "face." "\n\n" facedoc))
505 ;; Don't record the `describe-function' item in the stack.
506 (setq help-xref-stack-item nil)
507 (help-setup-xref (list #'help-xref-interned symbol) nil)))))))
f4ed5b19
MB
508
509\f
510;;; Navigation/hyperlinking with xrefs
511
89f5b33f
SM
512(defun help-follow-mouse (click)
513 "Follow the cross-reference that you CLICK on."
514 (interactive "e")
515 (let* ((start (event-start click))
516 (window (car start))
517 (pos (car (cdr start))))
518 (with-current-buffer (window-buffer window)
519 (help-follow pos))))
520
f4ed5b19
MB
521(defun help-xref-go-back (buffer)
522 "From BUFFER, go back to previous help buffer text using `help-xref-stack'."
523 (let (item position method args)
524 (with-current-buffer buffer
525 (when help-xref-stack
f4ed5b19 526 (setq item (pop help-xref-stack)
89f5b33f
SM
527 ;; Clear the current item so that it won't get pushed
528 ;; by the function we're about to call. TODO: We could also
529 ;; push it onto a "forward" stack and add a `forw' button.
530 help-xref-stack-item nil
f4ed5b19
MB
531 position (car item)
532 method (cadr item)
533 args (cddr item))))
534 (apply method args)
89f5b33f
SM
535 ;; FIXME: are we sure we're in the right buffer ?
536 (goto-char position)))
f4ed5b19
MB
537
538(defun help-go-back ()
539 "Invoke the [back] button (if any) in the Help mode buffer."
540 (interactive)
541 (let ((back-button (button-at (1- (point-max)))))
542 (if back-button
543 (button-activate back-button)
544 (error "No [back] button"))))
545
546(defun help-do-xref (pos function args)
547 "Call the help cross-reference function FUNCTION with args ARGS.
548Things are set up properly so that the resulting help-buffer has
549a proper [back] button."
f4ed5b19
MB
550 ;; There is a reference at point. Follow it.
551 (let ((help-xref-following t))
552 (apply function args)))
553
554(defun help-follow (&optional pos)
555 "Follow cross-reference at POS, defaulting to point.
556
557For the cross-reference format, see `help-make-xrefs'."
558 (interactive "d")
559 (unless pos
560 (setq pos (point)))
561 (unless (push-button pos)
562 ;; check if the symbol under point is a function or variable
563 (let ((sym
564 (intern
565 (save-excursion
566 (goto-char pos) (skip-syntax-backward "w_")
567 (buffer-substring (point)
568 (progn (skip-syntax-forward "w_")
569 (point)))))))
89f5b33f 570 (when (or (boundp sym) (fboundp sym) (facep sym))
f4ed5b19
MB
571 (help-do-xref pos #'help-xref-interned (list sym))))))
572
573
574(provide 'help-mode)
575
576;;; help-mode.el ends here
577
578