* net/dbus.el (dbus-call-method): Use timeout for `read-event'.
[bpt/emacs.git] / lisp / help-mode.el
CommitLineData
f4ed5b19
MB
1;;; help-mode.el --- `help-mode' used by *Help* buffers
2
acaf905b 3;; Copyright (C) 1985-1986, 1993-1994, 1998-2012
95df8112 4;; Free Software Foundation, Inc.
f4ed5b19
MB
5
6;; Maintainer: FSF
7;; Keywords: help, internal
bd78fa1d 8;; Package: emacs
f4ed5b19
MB
9
10;; This file is part of GNU Emacs.
11
eb3fa2cf 12;; GNU Emacs is free software: you can redistribute it and/or modify
f4ed5b19 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.
f4ed5b19
MB
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/>.
f4ed5b19
MB
24
25;;; Commentary:
26
27;; Defines `help-mode', which is the mode used by *Help* buffers, and
28;; associated support machinery, such as adding hyperlinks, etc.,
29
30;;; Code:
31
32(require 'button)
410e58b5 33(require 'view)
6f300323 34(eval-when-compile (require 'easymenu))
f4ed5b19 35
b016851c
SM
36(defvar help-mode-map
37 (let ((map (make-sparse-keymap)))
09ac1c2a
CS
38 (set-keymap-parent map (make-composed-keymap button-buffer-map
39 special-mode-map))
b016851c
SM
40 (define-key map [mouse-2] 'help-follow-mouse)
41 (define-key map "\C-c\C-b" 'help-go-back)
42 (define-key map "\C-c\C-f" 'help-go-forward)
3d10db47
SS
43 (define-key map [XF86Back] 'help-go-back)
44 (define-key map [XF86Forward] 'help-go-forward)
b016851c 45 (define-key map "\C-c\C-c" 'help-follow-symbol)
b016851c
SM
46 (define-key map "\r" 'help-follow)
47 map)
f4ed5b19
MB
48 "Keymap for help mode.")
49
58245a58
DN
50(easy-menu-define help-mode-menu help-mode-map
51 "Menu for Help Mode."
52 '("Help-Mode"
53 ["Show Help for Symbol" help-follow-symbol
54 :help "Show the docs for the symbol at point"]
55 ["Previous Topic" help-go-back
56 :help "Go back to previous topic in this help buffer"]
57 ["Next Topic" help-go-forward
58 :help "Go back to next topic in this help buffer"]
59 ["Move to Previous Button" backward-button
60 :help "Move to the Next Button in the help buffer"]
61 ["Move to Next Button" forward-button
62 :help "Move to the Next Button in the help buffer"]))
63
f4ed5b19
MB
64(defvar help-xref-stack nil
65 "A stack of ways by which to return to help buffers after following xrefs.
66Used by `help-follow' and `help-xref-go-back'.
89f5b33f
SM
67An element looks like (POSITION FUNCTION ARGS...).
68To use the element, do (apply FUNCTION ARGS) then goto the point.")
f4ed5b19 69(put 'help-xref-stack 'permanent-local t)
89f5b33f 70(make-variable-buffer-local 'help-xref-stack)
f4ed5b19 71
95f731db 72(defvar help-xref-forward-stack nil
da2cfeef 73 "A stack used to navigate help forwards after using the back button.
95f731db
NR
74Used by `help-follow' and `help-xref-go-forward'.
75An element looks like (POSITION FUNCTION ARGS...).
76To use the element, do (apply FUNCTION ARGS) then goto the point.")
77(put 'help-xref-forward-stack 'permanent-local t)
78(make-variable-buffer-local 'help-xref-forward-stack)
79
f4ed5b19
MB
80(defvar help-xref-stack-item nil
81 "An item for `help-follow' in this buffer to push onto `help-xref-stack'.
82The format is (FUNCTION ARGS...).")
83(put 'help-xref-stack-item 'permanent-local t)
89f5b33f 84(make-variable-buffer-local 'help-xref-stack-item)
f4ed5b19 85
95f731db
NR
86(defvar help-xref-stack-forward-item nil
87 "An item for `help-go-back' to push onto `help-xref-forward-stack'.
88The format is (FUNCTION ARGS...).")
89(put 'help-xref-stack-forward-item 'permanent-local t)
90(make-variable-buffer-local 'help-xref-stack-forward-item)
91
f4ed5b19 92(setq-default help-xref-stack nil help-xref-stack-item nil)
95f731db 93(setq-default help-xref-forward-stack nil help-xref-forward-stack-item nil)
f4ed5b19 94
1700f41d
RS
95(defcustom help-mode-hook nil
96 "Hook run by `help-mode'."
97 :type 'hook
98 :group 'help)
f4ed5b19
MB
99\f
100;; Button types used by help
101
6e881567 102(define-button-type 'help-xref
54d761b3 103 'follow-link t
6e881567
MB
104 'action #'help-button-action)
105
106(defun help-button-action (button)
107 "Call BUTTON's help function."
108 (help-do-xref (button-start button)
109 (button-get button 'help-function)
110 (button-get button 'help-args)))
111
7e2a83df
RS
112;; These 6 calls to define-button-type were generated in a dolist
113;; loop, but that is bad because it means these button types don't
114;; have an easily found definition.
115
116(define-button-type 'help-function
117 :supertype 'help-xref
118 'help-function 'describe-function
119 'help-echo (purecopy "mouse-2, RET: describe this function"))
120
121(define-button-type 'help-variable
122 :supertype 'help-xref
123 'help-function 'describe-variable
124 'help-echo (purecopy "mouse-2, RET: describe this variable"))
125
126(define-button-type 'help-face
127 :supertype 'help-xref
128 'help-function 'describe-face
129 'help-echo (purecopy "mouse-2, RET: describe this face"))
130
131(define-button-type 'help-coding-system
132 :supertype 'help-xref
133 'help-function 'describe-coding-system
134 'help-echo (purecopy "mouse-2, RET: describe this coding system"))
135
136(define-button-type 'help-input-method
137 :supertype 'help-xref
138 'help-function 'describe-input-method
139 'help-echo (purecopy "mouse-2, RET: describe this input method"))
140
141(define-button-type 'help-character-set
142 :supertype 'help-xref
143 'help-function 'describe-character-set
144 'help-echo (purecopy "mouse-2, RET: describe this character set"))
f4ed5b19 145
53964682 146;; Make some more idiosyncratic button types.
f4ed5b19
MB
147
148(define-button-type 'help-symbol
6e881567 149 :supertype 'help-xref
f4ed5b19 150 'help-function #'help-xref-interned
6e881567 151 'help-echo (purecopy "mouse-2, RET: describe this symbol"))
f4ed5b19
MB
152
153(define-button-type 'help-back
6e881567 154 :supertype 'help-xref
f4ed5b19 155 'help-function #'help-xref-go-back
6e881567 156 'help-echo (purecopy "mouse-2, RET: go back to previous help buffer"))
f4ed5b19 157
95f731db
NR
158(define-button-type 'help-forward
159 :supertype 'help-xref
160 'help-function #'help-xref-go-forward
161 'help-echo (purecopy "mouse-2, RET: move forward to next help buffer"))
162
7f9629ce
RC
163(define-button-type 'help-info-variable
164 :supertype 'help-xref
165 ;; the name of the variable is put before the argument to Info
06b60517 166 'help-function (lambda (_a v) (info v))
7f9629ce
RC
167 'help-echo (purecopy "mouse-2, RET: read this Info node"))
168
f4ed5b19 169(define-button-type 'help-info
6e881567 170 :supertype 'help-xref
f4ed5b19 171 'help-function #'info
5c825567
BW
172 'help-echo (purecopy "mouse-2, RET: read this Info node"))
173
174(define-button-type 'help-url
175 :supertype 'help-xref
176 'help-function #'browse-url
177 'help-echo (purecopy "mouse-2, RET: view this URL in a browser"))
f4ed5b19
MB
178
179(define-button-type 'help-customize-variable
6e881567 180 :supertype 'help-xref
f4ed5b19 181 'help-function (lambda (v)
f4ed5b19 182 (customize-variable v))
6e881567 183 'help-echo (purecopy "mouse-2, RET: customize variable"))
f4ed5b19 184
07f904a3 185(define-button-type 'help-customize-face
6e881567 186 :supertype 'help-xref
07f904a3 187 'help-function (lambda (v)
07f904a3 188 (customize-face v))
6e881567 189 'help-echo (purecopy "mouse-2, RET: customize face"))
07f904a3 190
f4ed5b19 191(define-button-type 'help-function-def
6e881567 192 :supertype 'help-xref
f4ed5b19
MB
193 'help-function (lambda (fun file)
194 (require 'find-func)
2ea0f8fd
SM
195 (when (eq file 'C-source)
196 (setq file
197 (help-C-file-name (indirect-function fun) 'fun)))
410e58b5 198 ;; Don't use find-function-noselect because it follows
f4ed5b19 199 ;; aliases (which fails for built-in functions).
410e58b5 200 (let ((location
2ea0f8fd 201 (find-function-search-for-symbol fun nil file)))
f4ed5b19 202 (pop-to-buffer (car location))
46ab7691
NR
203 (if (cdr location)
204 (goto-char (cdr location))
205 (message "Unable to find location in file"))))
6e881567 206 'help-echo (purecopy "mouse-2, RET: find function's definition"))
f4ed5b19 207
57c8e7b4
GM
208(define-button-type 'help-function-cmacro
209 :supertype 'help-xref
210 'help-function (lambda (fun file)
211 (setq file (locate-library file t))
212 (if (and file (file-readable-p file))
213 (progn
214 (pop-to-buffer (find-file-noselect file))
215 (goto-char (point-min))
216 (if (re-search-forward
217 (format "^[ \t]*(define-compiler-macro[ \t]+%s"
218 (regexp-quote (symbol-name fun))) nil t)
219 (forward-line 0)
220 (message "Unable to find location in file")))
221 (message "Unable to find file")))
222 'help-echo (purecopy "mouse-2, RET: find function's compiler macro"))
223
f4ed5b19 224(define-button-type 'help-variable-def
6e881567 225 :supertype 'help-xref
f4ed5b19 226 'help-function (lambda (var &optional file)
2ea0f8fd
SM
227 (when (eq file 'C-source)
228 (setq file (help-C-file-name var 'var)))
229 (let ((location (find-variable-noselect var file)))
f4ed5b19 230 (pop-to-buffer (car location))
46ab7691
NR
231 (if (cdr location)
232 (goto-char (cdr location))
233 (message "Unable to find location in file"))))
27313250 234 'help-echo (purecopy "mouse-2, RET: find variable's definition"))
f4ed5b19 235
12b42b71
RS
236(define-button-type 'help-face-def
237 :supertype 'help-xref
238 'help-function (lambda (fun file)
239 (require 'find-func)
240 ;; Don't use find-function-noselect because it follows
241 ;; aliases (which fails for built-in functions).
242 (let ((location
243 (find-function-search-for-symbol fun 'defface file)))
244 (pop-to-buffer (car location))
46ab7691
NR
245 (if (cdr location)
246 (goto-char (cdr location))
247 (message "Unable to find location in file"))))
12b42b71
RS
248 'help-echo (purecopy "mouse-2, RET: find face's definition"))
249
8adb4c33
CY
250(define-button-type 'help-package
251 :supertype 'help-xref
252 'help-function 'describe-package
253 'help-echo (purecopy "mouse-2, RET: Describe package"))
254
cced7584
CY
255(define-button-type 'help-package-def
256 :supertype 'help-xref
257 'help-function (lambda (file) (dired file))
258 'help-echo (purecopy "mouse-2, RET: visit package directory"))
259
6b09b5d1
CY
260(define-button-type 'help-theme-def
261 :supertype 'help-xref
262 'help-function 'find-file
263 'help-echo (purecopy "mouse-2, RET: visit theme file"))
264
265(define-button-type 'help-theme-edit
266 :supertype 'help-xref
267 'help-function 'customize-create-theme
268 'help-echo (purecopy "mouse-2, RET: edit this theme file"))
f4ed5b19 269\f
0c9e42b5
DA
270(defvar bookmark-make-record-function)
271
4feb6e73 272;;;###autoload
09ac1c2a 273(define-derived-mode help-mode special-mode "Help"
f4ed5b19
MB
274 "Major mode for viewing help text and navigating references in it.
275Entry to this mode runs the normal hook `help-mode-hook'.
276Commands:
277\\{help-mode-map}"
dbd6da78 278 (set (make-local-variable 'revert-buffer-function)
0c9e42b5
DA
279 'help-mode-revert-buffer)
280 (set (make-local-variable 'bookmark-make-record-function)
281 'help-bookmark-make-record))
f4ed5b19
MB
282
283;;;###autoload
284(defun help-mode-setup ()
285 (help-mode)
286 (setq buffer-read-only nil))
287
288;;;###autoload
289(defun help-mode-finish ()
290 (when (eq major-mode 'help-mode)
291 ;; View mode's read-only status of existing *Help* buffer is lost
292 ;; by with-output-to-temp-buffer.
293 (toggle-read-only 1)
b1ea593c
LL
294
295 (save-excursion
296 (goto-char (point-min))
297 (let ((inhibit-read-only t))
def71b5e 298 (when (re-search-forward "^This [^[:space:]]+ is advised.$" nil t)
b1ea593c
LL
299 (put-text-property (match-beginning 0)
300 (match-end 0)
301 'face 'font-lock-warning-face))))
302
69e73dd3 303 (help-make-xrefs (current-buffer))))
f4ed5b19 304\f
410e58b5
SM
305;; Grokking cross-reference information in doc strings and
306;; hyperlinking it.
f4ed5b19
MB
307
308;; This may have some scope for extension and the same or something
309;; similar should be done for widget doc strings, which currently use
310;; another mechanism.
311
f4ed5b19
MB
312(defvar help-back-label (purecopy "[back]")
313 "Label to use by `help-make-xrefs' for the go-back reference.")
314
95f731db
NR
315(defvar help-forward-label (purecopy "[forward]")
316 "Label to use by `help-make-xrefs' for the go-forward reference.")
317
f4ed5b19 318(defconst help-xref-symbol-regexp
8a31f813 319 (purecopy (concat "\\(\\<\\(\\(variable\\|option\\)\\|" ; Link to var
a79d3474
NR
320 "\\(function\\|command\\)\\|" ; Link to function
321 "\\(face\\)\\|" ; Link to face
322 "\\(symbol\\|program\\|property\\)\\|" ; Don't link
040b2fa3
LT
323 "\\(source \\(?:code \\)?\\(?:of\\|for\\)\\)\\)"
324 "[ \t\n]+\\)?"
f4ed5b19
MB
325 ;; Note starting with word-syntax character:
326 "`\\(\\sw\\(\\sw\\|\\s_\\)+\\)'"))
327 "Regexp matching doc string references to symbols.
328
329The words preceding the quoted symbol can be used in doc strings to
330distinguish references to variables, functions and symbols.")
331
410e58b5 332(defvar help-xref-mule-regexp nil
f4ed5b19
MB
333 "Regexp matching doc string references to MULE-related keywords.
334
335It is usually nil, and is temporarily bound to an appropriate regexp
336when help commands related to multilingual environment (e.g.,
337`describe-coding-system') are invoked.")
338
339
340(defconst help-xref-info-regexp
2e10efeb 341 (purecopy "\\<[Ii]nfo[ \t\n]+\\(node\\|anchor\\)[ \t\n]+`\\([^']+\\)'")
f4ed5b19
MB
342 "Regexp matching doc string references to an Info node.")
343
5c825567
BW
344(defconst help-xref-url-regexp
345 (purecopy "\\<[Uu][Rr][Ll][ \t\n]+`\\([^']+\\)'")
346 "Regexp matching doc string references to a URL.")
347
f4ed5b19
MB
348;;;###autoload
349(defun help-setup-xref (item interactive-p)
350 "Invoked from commands using the \"*Help*\" buffer to install some xref info.
351
352ITEM is a (FUNCTION . ARGS) pair appropriate for recreating the help
353buffer after following a reference. INTERACTIVE-P is non-nil if the
354calling command was invoked interactively. In this case the stack of
89f5b33f
SM
355items for help buffer \"back\" buttons is cleared.
356
357This should be called very early, before the output buffer is cleared,
358because we want to record the \"previous\" position of point so we can
359restore it properly when going back."
360 (with-current-buffer (help-buffer)
410e58b5 361 (when help-xref-stack-item
95f731db
NR
362 (push (cons (point) help-xref-stack-item) help-xref-stack)
363 (setq help-xref-forward-stack nil))
410e58b5
SM
364 (when interactive-p
365 (let ((tail (nthcdr 10 help-xref-stack)))
366 ;; Truncate the stack.
367 (if tail (setcdr tail nil))))
89f5b33f 368 (setq help-xref-stack-item item)))
f4ed5b19
MB
369
370(defvar help-xref-following nil
371 "Non-nil when following a help cross-reference.")
372
91dc07f3 373;;;###autoload
89f5b33f 374(defun help-buffer ()
f4227998
CY
375 "Return the name of a buffer for inserting help.
376If `help-xref-following' is non-nil, this is the name of the
d23ae2b0
CY
377current buffer. Signal an error if this buffer is not derived
378from `help-mode'.
379Otherwise, return \"*Help*\", creating a buffer with that name if
380it does not already exist."
89f5b33f 381 (buffer-name ;for with-output-to-temp-buffer
d23ae2b0
CY
382 (if (not help-xref-following)
383 (get-buffer-create "*Help*")
384 (unless (derived-mode-p 'help-mode)
385 (error "Current buffer is not in Help mode"))
386 (current-buffer))))
89f5b33f 387
f4ed5b19
MB
388;;;###autoload
389(defun help-make-xrefs (&optional buffer)
390 "Parse and hyperlink documentation cross-references in the given BUFFER.
391
d1282401
CW
392Find cross-reference information in a buffer and activate such cross
393references for selection with `help-follow'. Cross-references have
394the canonical form `...' and the type of reference may be
395disambiguated by the preceding word(s) used in
040b2fa3
LT
396`help-xref-symbol-regexp'. Faces only get cross-referenced if
397preceded or followed by the word `face'. Variables without
398variable documentation do not get cross-referenced, unless
9315fc34 399preceded by the word `variable' or `option'.
f4ed5b19
MB
400
401If the variable `help-xref-mule-regexp' is non-nil, find also
402cross-reference information related to multilingual environment
403\(e.g., coding-systems). This variable is also used to disambiguate
404the type of reference as the same way as `help-xref-symbol-regexp'.
405
406A special reference `back' is made to return back through a stack of
407help buffers. Variable `help-back-label' specifies the text for
408that."
409 (interactive "b")
589888fe
SM
410 (with-current-buffer (or buffer (current-buffer))
411 (save-excursion
412 (goto-char (point-min))
413 ;; Skip the header-type info, though it might be useful to parse
414 ;; it at some stage (e.g. "function in `library'").
415 (forward-paragraph)
416 (let ((old-modified (buffer-modified-p)))
417 (let ((stab (syntax-table))
418 (case-fold-search t)
419 (inhibit-read-only t))
420 (set-syntax-table emacs-lisp-mode-syntax-table)
421 ;; The following should probably be abstracted out.
422 (unwind-protect
423 (progn
424 ;; Info references
425 (save-excursion
426 (while (re-search-forward help-xref-info-regexp nil t)
427 (let ((data (match-string 2)))
428 (save-match-data
429 (unless (string-match "^([^)]+)" data)
a50878fa
KR
430 (setq data (concat "(emacs)" data)))
431 (setq data ;; possible newlines if para filled
432 (replace-regexp-in-string "[ \t\n]+" " " data t t)))
589888fe
SM
433 (help-xref-button 2 'help-info data))))
434 ;; URLs
435 (save-excursion
436 (while (re-search-forward help-xref-url-regexp nil t)
437 (let ((data (match-string 1)))
438 (help-xref-button 1 'help-url data))))
439 ;; Mule related keywords. Do this before trying
440 ;; `help-xref-symbol-regexp' because some of Mule
441 ;; keywords have variable or function definitions.
442 (if help-xref-mule-regexp
443 (save-excursion
444 (while (re-search-forward help-xref-mule-regexp nil t)
445 (let* ((data (match-string 7))
446 (sym (intern-soft data)))
447 (cond
448 ((match-string 3) ; coding system
449 (and sym (coding-system-p sym)
450 (help-xref-button 6 'help-coding-system sym)))
451 ((match-string 4) ; input method
452 (and (assoc data input-method-alist)
453 (help-xref-button 7 'help-input-method data)))
454 ((or (match-string 5) (match-string 6)) ; charset
455 (and sym (charsetp sym)
456 (help-xref-button 7 'help-character-set sym)))
457 ((assoc data input-method-alist)
458 (help-xref-button 7 'help-character-set data))
459 ((and sym (coding-system-p sym))
460 (help-xref-button 7 'help-coding-system sym))
461 ((and sym (charsetp sym))
462 (help-xref-button 7 'help-character-set sym)))))))
463 ;; Quoted symbols
464 (save-excursion
465 (while (re-search-forward help-xref-symbol-regexp nil t)
466 (let* ((data (match-string 8))
467 (sym (intern-soft data)))
468 (if sym
469 (cond
470 ((match-string 3) ; `variable' &c
471 (and (or (boundp sym) ; `variable' doesn't ensure
f4ed5b19 472 ; it's actually bound
589888fe
SM
473 (get sym 'variable-documentation))
474 (help-xref-button 8 'help-variable sym)))
475 ((match-string 4) ; `function' &c
476 (and (fboundp sym) ; similarly
477 (help-xref-button 8 'help-function sym)))
478 ((match-string 5) ; `face'
479 (and (facep sym)
480 (help-xref-button 8 'help-face sym)))
481 ((match-string 6)) ; nothing for `symbol'
482 ((match-string 7)
483 ;; this used:
484 ;; #'(lambda (arg)
485 ;; (let ((location
486 ;; (find-function-noselect arg)))
487 ;; (pop-to-buffer (car location))
488 ;; (goto-char (cdr location))))
489 (help-xref-button 8 'help-function-def sym))
490 ((and
491 (facep sym)
492 (save-match-data (looking-at "[ \t\n]+face\\W")))
493 (help-xref-button 8 'help-face sym))
494 ((and (or (boundp sym)
495 (get sym 'variable-documentation))
496 (fboundp sym))
497 ;; We can't intuit whether to use the
498 ;; variable or function doc -- supply both.
499 (help-xref-button 8 'help-symbol sym))
500 ((and
501 (or (boundp sym)
502 (get sym 'variable-documentation))
503 (or
504 (documentation-property
505 sym 'variable-documentation)
506 (condition-case nil
507 (documentation-property
508 (indirect-variable sym)
509 'variable-documentation)
510 (cyclic-variable-indirection nil))))
511 (help-xref-button 8 'help-variable sym))
512 ((fboundp sym)
513 (help-xref-button 8 'help-function sym)))))))
514 ;; An obvious case of a key substitution:
515 (save-excursion
516 (while (re-search-forward
517 ;; Assume command name is only word and symbol
518 ;; characters to get things like `use M-x foo->bar'.
519 ;; Command required to end with word constituent
520 ;; to avoid `.' at end of a sentence.
521 "\\<M-x\\s-+\\(\\sw\\(\\sw\\|\\s_\\)*\\sw\\)" nil t)
522 (let ((sym (intern-soft (match-string 1))))
523 (if (fboundp sym)
524 (help-xref-button 1 'help-function sym)))))
525 ;; Look for commands in whole keymap substitutions:
526 (save-excursion
527 ;; Make sure to find the first keymap.
528 (goto-char (point-min))
529 ;; Find a header and the column at which the command
530 ;; name will be found.
531
532 ;; If the keymap substitution isn't the last thing in
533 ;; the doc string, and if there is anything on the same
534 ;; line after it, this code won't recognize the end of it.
535 (while (re-search-forward "^key +binding\n\\(-+ +\\)-+\n\n"
536 nil t)
537 (let ((col (- (match-end 1) (match-beginning 1))))
538 (while
539 (and (not (eobp))
540 ;; Stop at a pair of blank lines.
541 (not (looking-at "\n\\s-*\n")))
542 ;; Skip a single blank line.
543 (and (eolp) (forward-line))
544 (end-of-line)
545 (skip-chars-backward "^ \t\n")
546 (if (and (>= (current-column) col)
547 (looking-at "\\(\\sw\\|\\s_\\)+$"))
548 (let ((sym (intern-soft (match-string 0))))
549 (if (fboundp sym)
550 (help-xref-button 0 'help-function sym))))
551 (forward-line))))))
552 (set-syntax-table stab))
553 ;; Delete extraneous newlines at the end of the docstring
554 (goto-char (point-max))
555 (while (and (not (bobp)) (bolp))
556 (delete-char -1))
557 (insert "\n")
558 (when (or help-xref-stack help-xref-forward-stack)
559 (insert "\n"))
560 ;; Make a back-reference in this buffer if appropriate.
561 (when help-xref-stack
562 (help-insert-xref-button help-back-label 'help-back
563 (current-buffer)))
564 ;; Make a forward-reference in this buffer if appropriate.
565 (when help-xref-forward-stack
566 (when help-xref-stack
567 (insert "\t"))
568 (help-insert-xref-button help-forward-label 'help-forward
569 (current-buffer)))
570 (when (or help-xref-stack help-xref-forward-stack)
571 (insert "\n")))
589888fe 572 (set-buffer-modified-p old-modified)))))
f4ed5b19
MB
573
574;;;###autoload
575(defun help-xref-button (match-number type &rest args)
576 "Make a hyperlink for cross-reference text previously matched.
577MATCH-NUMBER is the subexpression of interest in the last matched
578regexp. TYPE is the type of button to use. Any remaining arguments are
579passed to the button's help-function when it is invoked.
580See `help-make-xrefs'."
581 ;; Don't mung properties we've added specially in some instances.
582 (unless (button-at (match-beginning match-number))
583 (make-text-button (match-beginning match-number)
584 (match-end match-number)
585 'type type 'help-args args)))
586
587;;;###autoload
588(defun help-insert-xref-button (string type &rest args)
589 "Insert STRING and make a hyperlink from cross-reference text on it.
590TYPE is the type of button to use. Any remaining arguments are passed
591to the button's help-function when it is invoked.
592See `help-make-xrefs'."
593 (unless (button-at (point))
594 (insert-text-button string 'type type 'help-args args)))
595
596;;;###autoload
597(defun help-xref-on-pp (from to)
598 "Add xrefs for symbols in `pp's output between FROM and TO."
22f5d492
SM
599 (if (> (- to from) 5000) nil
600 (with-syntax-table emacs-lisp-mode-syntax-table
601 (save-excursion
602 (save-restriction
603 (narrow-to-region from to)
604 (goto-char (point-min))
605 (condition-case nil
606 (while (not (eobp))
607 (cond
608 ((looking-at "\"") (forward-sexp 1))
609 ((looking-at "#<") (search-forward ">" nil 'move))
610 ((looking-at "\\(\\(\\sw\\|\\s_\\)+\\)")
611 (let* ((sym (intern-soft (match-string 1)))
612 (type (cond ((fboundp sym) 'help-function)
613 ((or (memq sym '(t nil))
614 (keywordp sym))
615 nil)
e715d9b4
LT
616 ((and sym
617 (or (boundp sym)
618 (get sym
619 'variable-documentation)))
22f5d492
SM
620 'help-variable))))
621 (when type (help-xref-button 1 type sym)))
622 (goto-char (match-end 1)))
623 (t (forward-char 1))))
624 (error nil)))))))
f4ed5b19
MB
625
626\f
627;; Additional functions for (re-)creating types of help buffers.
628(defun help-xref-interned (symbol)
629 "Follow a hyperlink which appeared to be an arbitrary interned SYMBOL.
89f5b33f 630Both variable, function and face documentation are extracted into a single
f4ed5b19 631help buffer."
89f5b33f
SM
632 (with-current-buffer (help-buffer)
633 ;; Push the previous item on the stack before clobbering the output buffer.
774784f6 634 (help-setup-xref nil nil)
89f5b33f
SM
635 (let ((facedoc (when (facep symbol)
636 ;; Don't record the current entry in the stack.
637 (setq help-xref-stack-item nil)
638 (describe-face symbol)))
639 (fdoc (when (fboundp symbol)
640 ;; Don't record the current entry in the stack.
641 (setq help-xref-stack-item nil)
642 (describe-function symbol)))
e715d9b4
LT
643 (sdoc (when (or (boundp symbol)
644 (get symbol 'variable-documentation))
89f5b33f
SM
645 ;; Don't record the current entry in the stack.
646 (setq help-xref-stack-item nil)
647 (describe-variable symbol))))
648 (cond
649 (sdoc
650 ;; We now have a help buffer on the variable.
651 ;; Insert the function and face text before it.
ea127bf4 652 (when (or fdoc facedoc)
f4ed5b19
MB
653 (goto-char (point-min))
654 (let ((inhibit-read-only t))
655 (when fdoc
89f5b33f 656 (insert fdoc "\n\n")
ea127bf4
RS
657 (when facedoc
658 (insert (make-string 30 ?-) "\n\n" (symbol-name symbol)
89f5b33f
SM
659 " is also a " "face." "\n\n")))
660 (when facedoc
661 (insert facedoc "\n\n"))
f4ed5b19
MB
662 (insert (make-string 30 ?-) "\n\n" (symbol-name symbol)
663 " is also a " "variable." "\n\n"))
89f5b33f
SM
664 ;; Don't record the `describe-variable' item in the stack.
665 (setq help-xref-stack-item nil)
666 (help-setup-xref (list #'help-xref-interned symbol) nil)))
667 (fdoc
668 ;; We now have a help buffer on the function.
669 ;; Insert face text before it.
670 (when facedoc
671 (goto-char (point-max))
672 (let ((inhibit-read-only t))
673 (insert "\n\n" (make-string 30 ?-) "\n\n" (symbol-name symbol)
674 " is also a " "face." "\n\n" facedoc))
675 ;; Don't record the `describe-function' item in the stack.
676 (setq help-xref-stack-item nil)
677 (help-setup-xref (list #'help-xref-interned symbol) nil)))))))
f4ed5b19
MB
678
679\f
410e58b5 680;; Navigation/hyperlinking with xrefs
f4ed5b19
MB
681
682(defun help-xref-go-back (buffer)
683 "From BUFFER, go back to previous help buffer text using `help-xref-stack'."
684 (let (item position method args)
685 (with-current-buffer buffer
95f731db 686 (push (cons (point) help-xref-stack-item) help-xref-forward-stack)
f4ed5b19 687 (when help-xref-stack
f4ed5b19 688 (setq item (pop help-xref-stack)
89f5b33f
SM
689 ;; Clear the current item so that it won't get pushed
690 ;; by the function we're about to call. TODO: We could also
691 ;; push it onto a "forward" stack and add a `forw' button.
692 help-xref-stack-item nil
f4ed5b19
MB
693 position (car item)
694 method (cadr item)
695 args (cddr item))))
696 (apply method args)
77144ebc
RS
697 (with-current-buffer buffer
698 (if (get-buffer-window buffer)
699 (set-window-point (get-buffer-window buffer) position)
700 (goto-char position)))))
f4ed5b19 701
95f731db
NR
702(defun help-xref-go-forward (buffer)
703 "From BUFFER, go forward to next help buffer."
704 (let (item position method args)
705 (with-current-buffer buffer
706 (push (cons (point) help-xref-stack-item) help-xref-stack)
707 (when help-xref-forward-stack
708 (setq item (pop help-xref-forward-stack)
709 ;; Clear the current item so that it won't get pushed
710 ;; by the function we're about to call. TODO: We could also
711 ;; push it onto a "forward" stack and add a `forw' button.
712 help-xref-stack-item nil
713 position (car item)
714 method (cadr item)
715 args (cddr item))))
716 (apply method args)
717 (with-current-buffer buffer
718 (if (get-buffer-window buffer)
719 (set-window-point (get-buffer-window buffer) position)
720 (goto-char position)))))
91dc07f3 721
f4ed5b19 722(defun help-go-back ()
933cd61e 723 "Go back to previous topic in this help buffer."
f4ed5b19 724 (interactive)
933cd61e
SM
725 (if help-xref-stack
726 (help-xref-go-back (current-buffer))
fdeadcd1 727 (error "No previous help buffer")))
91dc07f3 728
95f731db
NR
729(defun help-go-forward ()
730 "Go back to next topic in this help buffer."
731 (interactive)
732 (if help-xref-forward-stack
733 (help-xref-go-forward (current-buffer))
734 (error "No next help buffer")))
f4ed5b19 735
06b60517 736(defun help-do-xref (_pos function args)
f4ed5b19
MB
737 "Call the help cross-reference function FUNCTION with args ARGS.
738Things are set up properly so that the resulting help-buffer has
739a proper [back] button."
f4ed5b19
MB
740 ;; There is a reference at point. Follow it.
741 (let ((help-xref-following t))
742 (apply function args)))
743
8a31f813
LT
744;; The doc string is meant to explain what buttons do.
745(defun help-follow-mouse ()
746 "Follow the cross-reference that you click on."
747 (interactive)
748 (error "No cross-reference here"))
749
750;; The doc string is meant to explain what buttons do.
751(defun help-follow ()
752 "Follow cross-reference at point.
f4ed5b19
MB
753
754For the cross-reference format, see `help-make-xrefs'."
8a31f813
LT
755 (interactive)
756 (error "No cross-reference here"))
757
758(defun help-follow-symbol (&optional pos)
759 "In help buffer, show docs for symbol at POS, defaulting to point.
760Show all docs for that symbol as either a variable, function or face."
f4ed5b19
MB
761 (interactive "d")
762 (unless pos
763 (setq pos (point)))
8a31f813
LT
764 ;; check if the symbol under point is a function, variable or face
765 (let ((sym
766 (intern
767 (save-excursion
768 (goto-char pos) (skip-syntax-backward "w_")
769 (buffer-substring (point)
770 (progn (skip-syntax-forward "w_")
771 (point)))))))
772 (when (or (boundp sym)
773 (get sym 'variable-documentation)
774 (fboundp sym) (facep sym))
775 (help-do-xref pos #'help-xref-interned (list sym)))))
f4ed5b19 776
06b60517 777(defun help-mode-revert-buffer (_ignore-auto noconfirm)
dbd6da78
JL
778 (when (or noconfirm (yes-or-no-p "Revert help buffer? "))
779 (let ((pos (point))
780 (item help-xref-stack-item)
781 ;; Pretend there is no current item to add to the history.
782 (help-xref-stack-item nil)
783 ;; Use the current buffer.
784 (help-xref-following t))
785 (apply (car item) (cdr item))
786 (goto-char pos))))
787
ee90fe92
NR
788(defun help-insert-string (string)
789 "Insert STRING to the help buffer and install xref info for it.
790This function can be used to restore the old contents of the help buffer
791when going back to the previous topic in the xref stack. It is needed
792in case when it is impossible to recompute the old contents of the
793help buffer by other means."
794 (setq help-xref-stack-item (list #'help-insert-string string))
795 (with-output-to-temp-buffer (help-buffer)
796 (insert string)))
f4ed5b19 797
0c9e42b5
DA
798\f
799;; Bookmark support
800
801(declare-function bookmark-prop-get "bookmark" (bookmark prop))
773e1f08
JB
802(declare-function bookmark-make-record-default "bookmark"
803 (&optional no-file no-context posn))
0c9e42b5
DA
804
805(defun help-bookmark-make-record ()
806 "Create and return a help-mode bookmark record.
807Implements `bookmark-make-record-function' for help-mode buffers."
808 (unless (car help-xref-stack-item)
809 (error "Cannot create bookmark - help command not known"))
810 `(,@(bookmark-make-record-default 'NO-FILE 'NO-CONTEXT)
811 (buffer-name . "*Help*")
812 (help-fn . ,(car help-xref-stack-item))
813 (help-arg . ,(cadr help-xref-stack-item))
814 (position . ,(point))
815 (handler . help-bookmark-jump)))
816
817;;;###autoload
818(defun help-bookmark-jump (bookmark)
819 "Jump to help-mode bookmark BOOKMARK.
820Handler function for record returned by `help-bookmark-make-record'.
821BOOKMARK is a bookmark name or a bookmark record."
822 (let ((help-fn (bookmark-prop-get bookmark 'help-fn))
823 (help-arg (bookmark-prop-get bookmark 'help-arg))
824 (position (bookmark-prop-get bookmark 'position)))
825 (funcall help-fn help-arg)
826 (pop-to-buffer "*Help*")
827 (goto-char position)))
828
829
f4ed5b19
MB
830(provide 'help-mode)
831
832;;; help-mode.el ends here