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