]> code.delx.au - gnu-emacs/blobdiff - lisp/help-mode.el
Update years in copyright notice; nfc.
[gnu-emacs] / lisp / help-mode.el
index 24ab7b62e367215309d100d6e0660f6e7ce64eae..eac7caad0cadbde38c9bcd88b088706572e4baa0 100644 (file)
@@ -1,7 +1,7 @@
 ;;; help-mode.el --- `help-mode' used by *Help* buffers
 
-;; Copyright (C) 1985, 1986, 1993, 1994, 1998, 1999, 2000, 2001
-;;   Free Software Foundation, Inc.
+;; Copyright (C) 1985, 1986, 1993, 1994, 1998, 1999, 2000, 2001, 2002,
+;;   2003, 2004, 2005 Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
 ;; Keywords: help, internal
@@ -20,8 +20,8 @@
 
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Commentary:
 
 ;;; Code:
 
 (require 'button)
-(eval-when-compile (require 'view))
+(require 'view)
 
 (defvar help-mode-map (make-sparse-keymap)
   "Keymap for help mode.")
 
 (set-keymap-parent help-mode-map button-buffer-map)
 
+(define-key help-mode-map [mouse-2] 'help-follow-mouse)
 (define-key help-mode-map "\C-c\C-b" 'help-go-back)
 (define-key help-mode-map "\C-c\C-c" 'help-follow)
 ;; Documentation only, since we use minor-mode-overriding-map-alist.
 (defvar help-xref-stack nil
   "A stack of ways by which to return to help buffers after following xrefs.
 Used by `help-follow' and `help-xref-go-back'.
-An element looks like (POSITION FUNCTION ARGS...), where POSITION is
-`(POINT . BUFFER-NAME)'.
-To use the element, do (apply FUNCTION ARGS) then goto the point in
-the named buffer.")
+An element looks like (POSITION FUNCTION ARGS...).
+To use the element, do (apply FUNCTION ARGS) then goto the point.")
 (put 'help-xref-stack 'permanent-local t)
+(make-variable-buffer-local 'help-xref-stack)
 
 (defvar help-xref-stack-item nil
   "An item for `help-follow' in this buffer to push onto `help-xref-stack'.
 The format is (FUNCTION ARGS...).")
 (put 'help-xref-stack-item 'permanent-local t)
+(make-variable-buffer-local 'help-xref-stack-item)
 
 (setq-default help-xref-stack nil help-xref-stack-item nil)
 
-
+(defcustom help-mode-hook nil
+  "Hook run by `help-mode'."
+  :type 'hook
+  :group 'help)
 \f
 ;; Button types used by help
 
-;; Make some button types that all use the same naming conventions
-(dolist (help-type '("function" "variable" "face"
-                    "coding-system" "input-method" "character-set"))
-  (define-button-type (intern (purecopy (concat "help-" help-type)))
-    'help-function (intern (concat "describe-" help-type))
-    'help-echo (purecopy (concat "mouse-2, RET: describe this " help-type))
-    'action #'help-button-action))
+(define-button-type 'help-xref
+  'follow-link t
+  'action #'help-button-action)
+
+(defun help-button-action (button)
+  "Call BUTTON's help function."
+  (help-do-xref (button-start button)
+               (button-get button 'help-function)
+               (button-get button 'help-args)))
+
+;; These 6 calls to define-button-type were generated in a dolist
+;; loop, but that is bad because it means these button types don't
+;; have an easily found definition.
+
+(define-button-type 'help-function
+  :supertype 'help-xref
+  'help-function 'describe-function
+  'help-echo (purecopy "mouse-2, RET: describe this function"))
+
+(define-button-type 'help-variable
+  :supertype 'help-xref
+  'help-function 'describe-variable
+  'help-echo (purecopy "mouse-2, RET: describe this variable"))
+
+(define-button-type 'help-face
+  :supertype 'help-xref
+  'help-function 'describe-face
+  'help-echo (purecopy "mouse-2, RET: describe this face"))
+
+(define-button-type 'help-coding-system
+  :supertype 'help-xref
+  'help-function 'describe-coding-system
+  'help-echo (purecopy "mouse-2, RET: describe this coding system"))
+
+(define-button-type 'help-input-method
+  :supertype 'help-xref
+  'help-function 'describe-input-method
+  'help-echo (purecopy "mouse-2, RET: describe this input method"))
+
+(define-button-type 'help-character-set
+  :supertype 'help-xref
+  'help-function 'describe-character-set
+  'help-echo (purecopy "mouse-2, RET: describe this character set"))
 
 ;; make some more ideosyncratic button types
 
 (define-button-type 'help-symbol
+  :supertype 'help-xref
   'help-function #'help-xref-interned
-  'help-echo (purecopy "mouse-2, RET: describe this symbol")
-  'action #'help-button-action)
+  'help-echo (purecopy "mouse-2, RET: describe this symbol"))
 
 (define-button-type 'help-back
+  :supertype 'help-xref
   'help-function #'help-xref-go-back
-  'help-echo (purecopy "mouse-2, RET: go back to previous help buffer")
-  'action #'help-button-action)
+  'help-echo (purecopy "mouse-2, RET: go back to previous help buffer"))
 
 (define-button-type 'help-info
+  :supertype 'help-xref
   'help-function #'info
-  'help-echo (purecopy"mouse-2, RET: read this Info node")
-  'action #'help-button-action)
+  'help-echo (purecopy"mouse-2, RET: read this Info node"))
 
 (define-button-type 'help-customize-variable
+  :supertype 'help-xref
   'help-function (lambda (v)
                   (if help-xref-stack
                       (pop help-xref-stack))
                   (customize-variable v))
-  'help-echo (purecopy "mouse-2, RET: customize variable")
-  'action #'help-button-action)
+  'help-echo (purecopy "mouse-2, RET: customize variable"))
+
+(define-button-type 'help-customize-face
+  :supertype 'help-xref
+  'help-function (lambda (v)
+                  (if help-xref-stack
+                      (pop help-xref-stack))
+                  (customize-face v))
+  'help-echo (purecopy "mouse-2, RET: customize face"))
 
 (define-button-type 'help-function-def
+  :supertype 'help-xref
   'help-function (lambda (fun file)
                   (require 'find-func)
-                 ;; Don't use find-function-noselect because it follows
+                  (when (eq file 'C-source)
+                    (setq file
+                          (help-C-file-name (indirect-function fun) 'fun)))
+                  ;; Don't use find-function-noselect because it follows
                   ;; aliases (which fails for built-in functions).
-                  (let* ((location (find-function-search-for-symbol
-                                    fun nil file)))
+                  (let ((location
+                         (find-function-search-for-symbol fun nil file)))
                     (pop-to-buffer (car location))
                     (goto-char (cdr location))))
-  'help-echo (purecopy "mouse-2, RET: find function's definition")
-  'action #'help-button-action)
+  'help-echo (purecopy "mouse-2, RET: find function's definition"))
 
 (define-button-type 'help-variable-def
+  :supertype 'help-xref
   'help-function (lambda (var &optional file)
-                  (let ((location
-                         (find-variable-noselect var file)))
+                  (when (eq file 'C-source)
+                    (setq file (help-C-file-name var 'var)))
+                  (let ((location (find-variable-noselect var file)))
                     (pop-to-buffer (car location))
                     (goto-char (cdr location))))
-  'help-echo (purecopy"mouse-2, RET: find variable's definition")
-  'action #'help-button-action)
+  'help-echo (purecopy"mouse-2, RET: find variable's definition"))
 
-(defun help-button-action (button)
-  "Call BUTTON's help function."
-  (help-do-xref (button-start button)
-               (button-get button 'help-function)
-               (button-get button 'help-args)))
+(define-button-type 'help-face-def
+  :supertype 'help-xref
+  'help-function (lambda (fun file)
+                  (require 'find-func)
+                  ;; Don't use find-function-noselect because it follows
+                  ;; aliases (which fails for built-in functions).
+                  (let ((location
+                         (find-function-search-for-symbol fun 'defface file)))
+                    (pop-to-buffer (car location))
+                    (goto-char (cdr location))))
+  'help-echo (purecopy "mouse-2, RET: find face's definition"))
 
 \f
 ;;;###autoload
-(define-derived-mode help-mode nil "Help"
+(defun help-mode ()
   "Major mode for viewing help text and navigating references in it.
 Entry to this mode runs the normal hook `help-mode-hook'.
 Commands:
 \\{help-mode-map}"
-  (setq font-lock-defaults nil)         ; font-lock would defeat xref
+  (interactive)
+  (kill-all-local-variables)
+  (use-local-map help-mode-map)
+  (setq mode-name "Help")
+  (setq major-mode 'help-mode)
   (view-mode)
   (make-local-variable 'view-no-disable-on-exit)
-  (setq view-no-disable-on-exit t))
+  (setq view-no-disable-on-exit t)
+  (run-mode-hooks 'help-mode-hook))
 
 ;;;###autoload
 (defun help-mode-setup ()
@@ -142,37 +205,29 @@ Commands:
 
 ;;;###autoload
 (defun help-mode-finish ()
+  (let ((entry (assq (selected-window) view-return-to-alist)))
+       (if entry
+           ;; When entering Help mode from the Help window,
+           ;; such as by following a link, preserve the same
+           ;; meaning for the q command.
+           ;; (setcdr entry (cons (selected-window) help-return-method))
+           nil
+         (setq view-return-to-alist
+               (cons (cons (selected-window) help-return-method)
+                     view-return-to-alist))))
   (when (eq major-mode 'help-mode)
     ;; View mode's read-only status of existing *Help* buffer is lost
     ;; by with-output-to-temp-buffer.
     (toggle-read-only 1)
-    (help-make-xrefs (current-buffer)))
-  (setq view-return-to-alist
-       (list (cons (selected-window) help-return-method))))
-
+    (help-make-xrefs (current-buffer))))
 \f
-;;; Grokking cross-reference information in doc strings and
-;;; hyperlinking it.
+;; Grokking cross-reference information in doc strings and
+;; hyperlinking it.
 
 ;; This may have some scope for extension and the same or something
 ;; similar should be done for widget doc strings, which currently use
 ;; another mechanism.
 
-(defcustom help-highlight-p t
-  "*If non-nil, `help-make-xrefs' highlight cross-references.
-Under a window system it highlights them with face defined by
-`help-highlight-face'."
- :group 'help
- :version "20.3"
- :type 'boolean)
-
-(defcustom help-highlight-face 'underline
-  "Face used by `help-make-xrefs' to highlight cross-references.
-Must be previously-defined."
-  :group 'help
-  :version "20.3"
-  :type 'face)
-
 (defvar help-back-label (purecopy "[back]")
   "Label to use by `help-make-xrefs' for the go-back reference.")
 
@@ -181,7 +236,8 @@ Must be previously-defined."
                    "\\(function\\|command\\)\\|"
                    "\\(face\\)\\|"
                    "\\(symbol\\)\\|"
-                   "\\(source \\(?:code \\)?\\(?:of\\|for\\)\\)\\)\\s-+\\)?"
+                   "\\(source \\(?:code \\)?\\(?:of\\|for\\)\\)\\)"
+                   "[ \t\n]+\\)?"
                    ;; Note starting with word-syntax character:
                    "`\\(\\sw\\(\\sw\\|\\s_\\)+\\)'"))
   "Regexp matching doc string references to symbols.
@@ -189,7 +245,7 @@ Must be previously-defined."
 The words preceding the quoted symbol can be used in doc strings to
 distinguish references to variables, functions and symbols.")
 
-(defconst help-xref-mule-regexp nil
+(defvar help-xref-mule-regexp nil
   "Regexp matching doc string references to MULE-related keywords.
 
 It is usually nil, and is temporarily bound to an appropriate regexp
@@ -198,7 +254,7 @@ when help commands related to multilingual environment (e.g.,
 
 
 (defconst help-xref-info-regexp
-  (purecopy "\\<[Ii]nfo[ \t\n]+node[ \t\n]+`\\([^']+\\)'")
+  (purecopy "\\<[Ii]nfo[ \t\n]+\\(node\\|anchor\\)[ \t\n]+`\\([^']+\\)'")
   "Regexp matching doc string references to an Info node.")
 
 ;;;###autoload
@@ -208,24 +264,48 @@ when help commands related to multilingual environment (e.g.,
 ITEM is a (FUNCTION . ARGS) pair appropriate for recreating the help
 buffer after following a reference.  INTERACTIVE-P is non-nil if the
 calling command was invoked interactively.  In this case the stack of
-items for help buffer \"back\" buttons is cleared."
-  (if interactive-p
-      (setq help-xref-stack nil))
-  (setq help-xref-stack-item item))
+items for help buffer \"back\" buttons is cleared.
+
+This should be called very early, before the output buffer is cleared,
+because we want to record the \"previous\" position of point so we can
+restore it properly when going back."
+  (with-current-buffer (help-buffer)
+    (when help-xref-stack-item
+      (push (cons (point) help-xref-stack-item) help-xref-stack))
+    (when interactive-p
+      (let ((tail (nthcdr 10 help-xref-stack)))
+       ;; Truncate the stack.
+       (if tail (setcdr tail nil))))
+    (setq help-xref-stack-item item)))
 
 (defvar help-xref-following nil
   "Non-nil when following a help cross-reference.")
 
+(defun help-buffer ()
+  (buffer-name                         ;for with-output-to-temp-buffer
+   (if help-xref-following
+       (current-buffer)
+     (get-buffer-create "*Help*"))))
+
+(defvar help-xref-override-view-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map view-mode-map)
+    (define-key map "\r" nil)
+    map)
+  "Replacement keymap for `view-mode' in help buffers.")
+
 ;;;###autoload
 (defun help-make-xrefs (&optional buffer)
   "Parse and hyperlink documentation cross-references in the given BUFFER.
 
-Find cross-reference information in a buffer and, if
-`help-highlight-p' is non-nil, highlight it with face defined by
-`help-highlight-face'; activate such cross references for selection
-with `help-follow'.  Cross-references have the canonical form `...'
-and the type of reference may be disambiguated by the preceding
-word(s) used in `help-xref-symbol-regexp'.
+Find cross-reference information in a buffer and activate such cross
+references for selection with `help-follow'.  Cross-references have
+the canonical form `...'  and the type of reference may be
+disambiguated by the preceding word(s) used in
+`help-xref-symbol-regexp'.  Faces only get cross-referenced if
+preceded or followed by the word `face'.  Variables without
+variable documentation do not get cross-referenced, unless
+preceded by the word `variable' or `option'.
 
 If the variable `help-xref-mule-regexp' is non-nil, find also
 cross-reference information related to multilingual environment
@@ -253,11 +333,11 @@ that."
               ;; Info references
               (save-excursion
                 (while (re-search-forward help-xref-info-regexp nil t)
-                  (let ((data (match-string 1)))
+                  (let ((data (match-string 2)))
                    (save-match-data
                      (unless (string-match "^([^)]+)" data)
                        (setq data (concat "(emacs)" data))))
-                   (help-xref-button 1 'help-info data))))
+                   (help-xref-button 2 'help-info data))))
              ;; Mule related keywords.  Do this before trying
              ;; `help-xref-symbol-regexp' because some of Mule
              ;; keywords have variable or function definitions.
@@ -289,11 +369,11 @@ that."
                          (sym (intern-soft data)))
                     (if sym
                         (cond
-                         ((match-string 3) ; `variable' &c
+                         ((match-string 3)  ; `variable' &c
                           (and (boundp sym) ; `variable' doesn't ensure
                                         ; it's actually bound
                                (help-xref-button 8 'help-variable sym)))
-                         ((match-string 4) ; `function' &c
+                         ((match-string 4)   ; `function' &c
                           (and (fboundp sym) ; similarly
                                (help-xref-button 8 'help-function sym)))
                         ((match-string 5) ; `face'
@@ -301,23 +381,34 @@ that."
                               (help-xref-button 8 'help-face sym)))
                          ((match-string 6)) ; nothing for `symbol'
                         ((match-string 7)
-;; this used:
-;;                        #'(lambda (arg)
-;;                            (let ((location
-;;                                   (find-function-noselect arg)))
-;;                              (pop-to-buffer (car location))
-;;                              (goto-char (cdr location))))
+;; this used:
+;;;                      #'(lambda (arg)
+;;;                          (let ((location
+;;;                                 (find-function-noselect arg)))
+;;;                            (pop-to-buffer (car location))
+;;;                            (goto-char (cdr location))))
                          (help-xref-button 8 'help-function-def sym))
+                        ((and
+                           (facep sym)
+                           (save-match-data (looking-at "[ \t\n]+face\\W")))
+                          (help-xref-button 8 'help-face sym))
                          ((and (boundp sym) (fboundp sym))
                           ;; We can't intuit whether to use the
                           ;; variable or function doc -- supply both.
                           (help-xref-button 8 'help-symbol sym))
-                         ((boundp sym)
+                         ((and
+                          (boundp sym)
+                          (or
+                           (documentation-property
+                            sym 'variable-documentation)
+                           (condition-case nil
+                               (documentation-property
+                                (indirect-variable sym)
+                                'variable-documentation)
+                             (cyclic-variable-indirection nil))))
                          (help-xref-button 8 'help-variable sym))
                         ((fboundp sym)
-                         (help-xref-button 8 'help-function sym))
-                        ((facep sym)
-                         (help-xref-button 8 'help-face sym)))))))
+                         (help-xref-button 8 'help-function sym)))))))
               ;; An obvious case of a key substitution:
               (save-excursion
                 (while (re-search-forward
@@ -333,40 +424,42 @@ that."
                (goto-char (point-min))
                 ;; Find a header and the column at which the command
                 ;; name will be found.
+
+               ;; If the keymap substitution isn't the last thing in
+               ;; the doc string, and if there is anything on the
+               ;; same line after it, this code won't recognize the end of it.
                 (while (re-search-forward "^key +binding\n\\(-+ +\\)-+\n\n"
                                           nil t)
                   (let ((col (- (match-end 1) (match-beginning 1))))
                     (while
-                        ;; Ignore single blank lines in table, but not
-                        ;; double ones, which should terminate it.
-                        (and (not (looking-at "\n\\s-*\n"))
-                             (progn
-                              (and (eolp) (forward-line))
-                              (end-of-line)
-                              (skip-chars-backward "^\t\n")
-                               (if (and (>= (current-column) col)
-                                       (looking-at "\\(\\sw\\|-\\)+$"))
-                                   (let ((sym (intern-soft (match-string 0))))
-                                     (if (fboundp sym)
-                                         (help-xref-button 0 'help-function sym))))
-                              (zerop (forward-line)))))))))
-          (set-syntax-table stab))
+                        (and (not (eobp))
+                            ;; Stop at a pair of blank lines.
+                            (not (looking-at "\n\\s-*\n")))
+                     ;; Skip a single blank line.
+                     (and (eolp) (forward-line))
+                     (end-of-line)
+                     (skip-chars-backward "^\t\n")
+                     (if (and (>= (current-column) col)
+                              (looking-at "\\(\\sw\\|-\\)+$"))
+                         (let ((sym (intern-soft (match-string 0))))
+                           (if (fboundp sym)
+                               (help-xref-button 0 'help-function sym))))
+                     (forward-line))))))
+         (set-syntax-table stab))
        ;; Delete extraneous newlines at the end of the docstring
        (goto-char (point-max))
        (while (and (not (bobp)) (bolp))
          (delete-char -1))
+        (insert "\n")
         ;; Make a back-reference in this buffer if appropriate.
-        (when (and help-xref-following help-xref-stack)
-         (insert "\n\n")
+        (when help-xref-stack
+         (insert "\n")
          (help-insert-xref-button help-back-label 'help-back
-                                  (current-buffer))))
+                                  (current-buffer))
+          (insert "\n")))
       ;; View mode steals RET from us.
       (set (make-local-variable 'minor-mode-overriding-map-alist)
-           (list (cons 'view-mode
-                       (let ((map (make-sparse-keymap)))
-                         (set-keymap-parent map view-mode-map)
-                         (define-key map "\r" 'help-follow)
-                         map))))
+           (list (cons 'view-mode help-xref-override-view-map)))
       (set-buffer-modified-p old-modified))))
 
 ;;;###autoload
@@ -394,92 +487,124 @@ See `help-make-xrefs'."
 ;;;###autoload
 (defun help-xref-on-pp (from to)
   "Add xrefs for symbols in `pp's output between FROM and TO."
-  (let ((ost (syntax-table)))
-    (unwind-protect
-       (save-excursion
-         (save-restriction
-           (set-syntax-table emacs-lisp-mode-syntax-table)
-           (narrow-to-region from to)
-           (goto-char (point-min))
-           (while (not (eobp))
-             (cond
-              ((looking-at "\"") (forward-sexp 1))
-              ((looking-at "#<") (search-forward ">" nil 'move))
-              ((looking-at "\\(\\(\\sw\\|\\s_\\)+\\)")
-               (let* ((sym (intern-soft (match-string 1)))
-                      (type (cond ((fboundp sym) 'help-function)
-                                ((or (memq sym '(t nil))
-                                     (keywordp sym))
-                                 nil)
-                                ((and sym (boundp sym))
-                                 'help-variable))))
-                 (when type (help-xref-button 1 type sym)))
-               (goto-char (match-end 1)))
-              (t (forward-char 1))))))
-      (set-syntax-table ost))))
+  (if (> (- to from) 5000) nil
+    (with-syntax-table emacs-lisp-mode-syntax-table
+      (save-excursion
+       (save-restriction
+         (narrow-to-region from to)
+         (goto-char (point-min))
+         (condition-case nil
+             (while (not (eobp))
+               (cond
+                ((looking-at "\"") (forward-sexp 1))
+                ((looking-at "#<") (search-forward ">" nil 'move))
+                ((looking-at "\\(\\(\\sw\\|\\s_\\)+\\)")
+                 (let* ((sym (intern-soft (match-string 1)))
+                        (type (cond ((fboundp sym) 'help-function)
+                                    ((or (memq sym '(t nil))
+                                         (keywordp sym))
+                                     nil)
+                                    ((and sym (boundp sym))
+                                     'help-variable))))
+                   (when type (help-xref-button 1 type sym)))
+                 (goto-char (match-end 1)))
+                (t (forward-char 1))))
+           (error nil)))))))
 
 \f
 ;; Additional functions for (re-)creating types of help buffers.
 (defun help-xref-interned (symbol)
   "Follow a hyperlink which appeared to be an arbitrary interned SYMBOL.
-
-Both variable and function documentation are extracted into a single
+Both variable, function and face documentation are extracted into a single
 help buffer."
-  (let ((fdoc (when (fboundp symbol) (describe-function symbol)))
-       (facedoc (when (facep symbol) (describe-face symbol))))
-    (when (or (boundp symbol) (not fdoc))
-      (describe-variable symbol)
-      ;; We now have a help buffer on the variable.  Insert the function
-      ;; text before it.
-      (when (or fdoc facedoc)
-       (with-current-buffer "*Help*"
+  (with-current-buffer (help-buffer)
+    ;; Push the previous item on the stack before clobbering the output buffer.
+    (help-setup-xref nil nil)
+    (let ((facedoc (when (facep symbol)
+                    ;; Don't record the current entry in the stack.
+                    (setq help-xref-stack-item nil)
+                    (describe-face symbol)))
+         (fdoc (when (fboundp symbol)
+                 ;; Don't record the current entry in the stack.
+                 (setq help-xref-stack-item nil)
+                 (describe-function symbol)))
+         (sdoc (when (boundp symbol)
+                 ;; Don't record the current entry in the stack.
+                 (setq help-xref-stack-item nil)
+                 (describe-variable symbol))))
+      (cond
+       (sdoc
+       ;; We now have a help buffer on the variable.
+       ;; Insert the function and face text before it.
+       (when (or fdoc facedoc)
          (goto-char (point-min))
          (let ((inhibit-read-only t))
            (when fdoc
-             (insert fdoc "\n\n"))
+             (insert fdoc "\n\n")
+             (when facedoc
+               (insert (make-string 30 ?-) "\n\n" (symbol-name symbol)
+                       " is also a " "face." "\n\n")))
            (when facedoc
-             (insert (make-string 30 ?-) "\n\n" (symbol-name symbol)
-                     " is also a " "face." "\n\n" facedoc "\n\n"))
+             (insert facedoc "\n\n"))
            (insert (make-string 30 ?-) "\n\n" (symbol-name symbol)
                    " is also a " "variable." "\n\n"))
-         (help-setup-xref (list #'help-xref-interned symbol) nil))))))
+         ;; Don't record the `describe-variable' item in the stack.
+         (setq help-xref-stack-item nil)
+         (help-setup-xref (list #'help-xref-interned symbol) nil)))
+       (fdoc
+       ;; We now have a help buffer on the function.
+       ;; Insert face text before it.
+       (when facedoc
+         (goto-char (point-max))
+         (let ((inhibit-read-only t))
+           (insert "\n\n" (make-string 30 ?-) "\n\n" (symbol-name symbol)
+                   " is also a " "face." "\n\n" facedoc))
+         ;; Don't record the `describe-function' item in the stack.
+         (setq help-xref-stack-item nil)
+         (help-setup-xref (list #'help-xref-interned symbol) nil)))))))
 
 \f
-;;; Navigation/hyperlinking with xrefs
+;; Navigation/hyperlinking with xrefs
+
+(defun help-follow-mouse (click)
+  "Follow the cross-reference that you CLICK on."
+  (interactive "e")
+  (let* ((start (event-start click))
+        (window (car start))
+        (pos (car (cdr start))))
+    (with-current-buffer (window-buffer window)
+      (help-follow pos))))
 
 (defun help-xref-go-back (buffer)
   "From BUFFER, go back to previous help buffer text using `help-xref-stack'."
   (let (item position method args)
     (with-current-buffer buffer
       (when help-xref-stack
-       (setq help-xref-stack (cdr help-xref-stack)) ; due to help-follow
        (setq item (pop help-xref-stack)
+             ;; Clear the current item so that it won't get pushed
+             ;; by the function we're about to call.  TODO: We could also
+             ;; push it onto a "forward" stack and add a `forw' button.
+             help-xref-stack-item nil
              position (car item)
              method (cadr item)
              args (cddr item))))
     (apply method args)
-    ;; We assume that the buffer we just recreated has the saved name,
-    ;; which might not always be true.
-    (when (get-buffer (cdr position))
-      (with-current-buffer (cdr position)
-       (goto-char (car position))))))
+    (with-current-buffer buffer
+      (if (get-buffer-window buffer)
+         (set-window-point (get-buffer-window buffer) position)
+       (goto-char position)))))
 
 (defun help-go-back ()
-  "Invoke the [back] button (if any) in the Help mode buffer."
+  "Go back to previous topic in this help buffer."
   (interactive)
-  (let ((back-button (button-at (1- (point-max)))))
-    (if back-button
-       (button-activate back-button)
-      (error "No [back] button"))))
+  (if help-xref-stack
+      (help-xref-go-back (current-buffer))
+    (error "No previous help buffer")))
 
 (defun help-do-xref (pos function args)
   "Call the help cross-reference function FUNCTION with args ARGS.
 Things are set up properly so that the resulting help-buffer has
 a proper [back] button."
-  (setq help-xref-stack (cons (cons (cons pos (buffer-name))
-                                   help-xref-stack-item)
-                             help-xref-stack))
-  (setq help-xref-stack-item nil)
   ;; There is a reference at point.  Follow it.
   (let ((help-xref-following t))
     (apply function args)))
@@ -500,12 +625,11 @@ For the cross-reference format, see `help-make-xrefs'."
              (buffer-substring (point)
                                (progn (skip-syntax-forward "w_")
                                       (point)))))))
-      (when (or (boundp sym) (fboundp sym))
+      (when (or (boundp sym) (fboundp sym) (facep sym))
        (help-do-xref pos #'help-xref-interned (list sym))))))
 
 
 (provide 'help-mode)
 
+;; arch-tag: 850954ae-3725-4cb4-8e91-0bf6d52d6b0b
 ;;; help-mode.el ends here
-
-