]> code.delx.au - gnu-emacs/blobdiff - lisp/help-mode.el
(Abbrevs): A @node line without explicit Prev, Next, and Up links.
[gnu-emacs] / lisp / help-mode.el
index b2b7690101a198eac2d66c572c4e82976f7fc0c5..0c6e0f47453b94f2eae83650bc52d835eb710e49 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, 2006 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:
 
@@ -31,7 +31,7 @@
 ;;; Code:
 
 (require 'button)
-(eval-when-compile (require 'view))
+(require 'view)
 
 (defvar help-mode-map (make-sparse-keymap)
   "Keymap for help mode.")
@@ -40,7 +40,7 @@
 
 (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)
+(define-key help-mode-map "\C-c\C-c" 'help-follow-symbol)
 ;; Documentation only, since we use minor-mode-overriding-map-alist.
 (define-key help-mode-map "\r" 'help-follow)
 
@@ -60,89 +60,148 @@ The format is (FUNCTION ARGS...).")
 
 (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-url
+  :supertype 'help-xref
+  'help-function #'browse-url
+  'help-echo (purecopy "mouse-2, RET: view this URL in a browser"))
 
 (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")
-  'action #'help-button-action)
+  '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)
+  (setq view-exit-action (lambda (buffer)
+                          (or (window-minibuffer-p (selected-window))
+                              (one-window-p t)
+                              (delete-window))))
+  (run-mode-hooks 'help-mode-hook))
 
 ;;;###autoload
 (defun help-mode-setup ()
@@ -151,46 +210,39 @@ 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.")
 
 (defconst help-xref-symbol-regexp
-  (purecopy (concat "\\(\\<\\(\\(variable\\|option\\)\\|"
-                   "\\(function\\|command\\)\\|"
-                   "\\(face\\)\\|"
-                   "\\(symbol\\)\\|"
-                   "\\(source \\(?:code \\)?\\(?:of\\|for\\)\\)\\)\\s-+\\)?"
+  (purecopy (concat "\\(\\<\\(\\(variable\\|option\\)\\|"  ; Link to var
+                   "\\(function\\|command\\)\\|"          ; Link to function
+                   "\\(face\\)\\|"                        ; Link to face
+                   "\\(symbol\\|program\\)\\|"            ; Don't link
+                   "\\(source \\(?:code \\)?\\(?:of\\|for\\)\\)\\)"
+                   "[ \t\n]+\\)?"
                    ;; Note starting with word-syntax character:
                    "`\\(\\sw\\(\\sw\\|\\s_\\)+\\)'"))
   "Regexp matching doc string references to symbols.
@@ -198,7 +250,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
@@ -207,9 +259,13 @@ 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.")
 
+(defconst help-xref-url-regexp
+  (purecopy "\\<[Uu][Rr][Ll][ \t\n]+`\\([^']+\\)'")
+  "Regexp matching doc string references to a URL.")
+
 ;;;###autoload
 (defun help-setup-xref (item interactive-p)
   "Invoked from commands using the \"*Help*\" buffer to install some xref info.
@@ -223,11 +279,12 @@ 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)
-    (if interactive-p
-       ;; Why do we want to prevent the user from going back ??  -stef
-       (setq help-xref-stack nil)
-      (when help-xref-stack-item
-       (push (cons (point) help-xref-stack-item) help-xref-stack)))
+    (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
@@ -239,16 +296,25 @@ restore it properly when going back."
        (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
@@ -276,11 +342,16 @@ 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))))
+              ;; URLs
+              (save-excursion
+                (while (re-search-forward help-xref-url-regexp nil t)
+                  (let ((data (match-string 1)))
+                   (help-xref-button 1 'help-url data))))
              ;; Mule related keywords.  Do this before trying
              ;; `help-xref-symbol-regexp' because some of Mule
              ;; keywords have variable or function definitions.
@@ -312,11 +383,12 @@ that."
                          (sym (intern-soft data)))
                     (if sym
                         (cond
-                         ((match-string 3) ; `variable' &c
-                          (and (boundp sym) ; `variable' doesn't ensure
+                         ((match-string 3)  ; `variable' &c
+                          (and (or (boundp sym) ; `variable' doesn't ensure
                                         ; it's actually bound
+                                  (get sym 'variable-documentation))
                                (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'
@@ -324,23 +396,37 @@ 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 (boundp sym) (fboundp sym))
+                        ((and
+                           (facep sym)
+                           (save-match-data (looking-at "[ \t\n]+face\\W")))
+                          (help-xref-button 8 'help-face sym))
+                         ((and (or (boundp sym)
+                                  (get sym 'variable-documentation))
+                              (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
+                          (or (boundp sym)
+                              (get sym 'variable-documentation))
+                          (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
@@ -356,40 +442,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 help-xref-stack
-         (insert "\n\n")
+         (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
@@ -417,29 +505,32 @@ 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
+                                          (or (boundp sym)
+                                              (get sym
+                                                   'variable-documentation)))
+                                     '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.
@@ -458,7 +549,8 @@ help buffer."
                  ;; Don't record the current entry in the stack.
                  (setq help-xref-stack-item nil)
                  (describe-function symbol)))
-         (sdoc (when (boundp symbol)
+         (sdoc (when (or (boundp symbol)
+                         (get symbol 'variable-documentation))
                  ;; Don't record the current entry in the stack.
                  (setq help-xref-stack-item nil)
                  (describe-variable symbol))))
@@ -466,13 +558,13 @@ help buffer."
        (sdoc
        ;; We now have a help buffer on the variable.
        ;; Insert the function and face text before it.
-      (when (or fdoc facedoc)
+       (when (or fdoc facedoc)
          (goto-char (point-min))
          (let ((inhibit-read-only t))
            (when fdoc
              (insert fdoc "\n\n")
-           (when facedoc
-             (insert (make-string 30 ?-) "\n\n" (symbol-name symbol)
+             (when facedoc
+               (insert (make-string 30 ?-) "\n\n" (symbol-name symbol)
                        " is also a " "face." "\n\n")))
            (when facedoc
              (insert facedoc "\n\n"))
@@ -494,16 +586,7 @@ help buffer."
          (help-setup-xref (list #'help-xref-interned symbol) nil)))))))
 
 \f
-;;; 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))))
+;; Navigation/hyperlinking with xrefs
 
 (defun help-xref-go-back (buffer)
   "From BUFFER, go back to previous help buffer text using `help-xref-stack'."
@@ -519,16 +602,17 @@ help buffer."
              method (cadr item)
              args (cddr item))))
     (apply method args)
-    ;; FIXME: are we sure we're in the right buffer ?
-    (goto-char 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.
@@ -538,28 +622,50 @@ a proper [back] button."
   (let ((help-xref-following t))
     (apply function args)))
 
-(defun help-follow (&optional pos)
-  "Follow cross-reference at POS, defaulting to point.
+;; The doc string is meant to explain what buttons do.
+(defun help-follow-mouse ()
+  "Follow the cross-reference that you click on."
+  (interactive)
+  (error "No cross-reference here"))
+
+;; The doc string is meant to explain what buttons do.
+(defun help-follow ()
+  "Follow cross-reference at point.
 
 For the cross-reference format, see `help-make-xrefs'."
+  (interactive)
+  (error "No cross-reference here"))
+
+(defun help-follow-symbol (&optional pos)
+  "In help buffer, show docs for symbol at POS, defaulting to point.
+Show all docs for that symbol as either a variable, function or face."
   (interactive "d")
   (unless pos
     (setq pos (point)))
-  (unless (push-button pos)
-    ;; check if the symbol under point is a function or variable
-    (let ((sym
-          (intern
-           (save-excursion
-             (goto-char pos) (skip-syntax-backward "w_")
-             (buffer-substring (point)
-                               (progn (skip-syntax-forward "w_")
-                                      (point)))))))
-      (when (or (boundp sym) (fboundp sym) (facep sym))
-       (help-do-xref pos #'help-xref-interned (list sym))))))
-
+  ;; check if the symbol under point is a function, variable or face
+  (let ((sym
+        (intern
+         (save-excursion
+           (goto-char pos) (skip-syntax-backward "w_")
+           (buffer-substring (point)
+                             (progn (skip-syntax-forward "w_")
+                                    (point)))))))
+    (when (or (boundp sym)
+             (get sym 'variable-documentation)
+             (fboundp sym) (facep sym))
+      (help-do-xref pos #'help-xref-interned (list sym)))))
+
+(defun help-insert-string (string)
+  "Insert STRING to the help buffer and install xref info for it.
+This function can be used to restore the old contents of the help buffer
+when going back to the previous topic in the xref stack.  It is needed
+in case when it is impossible to recompute the old contents of the
+help buffer by other means."
+  (setq help-xref-stack-item (list #'help-insert-string string))
+  (with-output-to-temp-buffer (help-buffer)
+    (insert string)))
 
 (provide 'help-mode)
 
+;; arch-tag: 850954ae-3725-4cb4-8e91-0bf6d52d6b0b
 ;;; help-mode.el ends here
-
-