;;; help-mode.el --- `help-mode' used by *Help* buffers
;; Copyright (C) 1985, 1986, 1993, 1994, 1998, 1999, 2000, 2001, 2002,
-;; 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+;; 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
;; Maintainer: FSF
;; Keywords: help, internal
;; This file is part of GNU Emacs.
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; GNU General Public License for more details.
;; 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., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
(require 'button)
(require 'view)
+(eval-when-compile (require 'easymenu))
(defvar help-mode-map (make-sparse-keymap)
"Keymap for help mode.")
(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-f" 'help-go-forward)
(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)
+(easy-menu-define help-mode-menu help-mode-map
+ "Menu for Help Mode."
+ '("Help-Mode"
+ ["Show Help for Symbol" help-follow-symbol
+ :help "Show the docs for the symbol at point"]
+ ["Previous Topic" help-go-back
+ :help "Go back to previous topic in this help buffer"]
+ ["Next Topic" help-go-forward
+ :help "Go back to next topic in this help buffer"]
+ ["Move to Previous Button" backward-button
+ :help "Move to the Next Button in the help buffer"]
+ ["Move to Next Button" forward-button
+ :help "Move to the Next Button in the help buffer"]))
+
(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'.
(put 'help-xref-stack 'permanent-local t)
(make-variable-buffer-local 'help-xref-stack)
+(defvar help-xref-forward-stack nil
+ "A stack used to navigate help forwards after using the back button.
+Used by `help-follow' and `help-xref-go-forward'.
+An element looks like (POSITION FUNCTION ARGS...).
+To use the element, do (apply FUNCTION ARGS) then goto the point.")
+(put 'help-xref-forward-stack 'permanent-local t)
+(make-variable-buffer-local 'help-xref-forward-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)
+(defvar help-xref-stack-forward-item nil
+ "An item for `help-go-back' to push onto `help-xref-forward-stack'.
+The format is (FUNCTION ARGS...).")
+(put 'help-xref-stack-forward-item 'permanent-local t)
+(make-variable-buffer-local 'help-xref-stack-forward-item)
+
(setq-default help-xref-stack nil help-xref-stack-item nil)
+(setq-default help-xref-forward-stack nil help-xref-forward-stack-item nil)
(defcustom help-mode-hook nil
"Hook run by `help-mode'."
'help-function #'help-xref-go-back
'help-echo (purecopy "mouse-2, RET: go back to previous help buffer"))
+(define-button-type 'help-forward
+ :supertype 'help-xref
+ 'help-function #'help-xref-go-forward
+ 'help-echo (purecopy "mouse-2, RET: move forward to next help buffer"))
+
+(define-button-type 'help-info-variable
+ :supertype 'help-xref
+ ;; the name of the variable is put before the argument to Info
+ 'help-function (lambda (a v) (info v))
+ 'help-echo (purecopy "mouse-2, RET: read this Info node"))
+
(define-button-type 'help-info
:supertype 'help-xref
'help-function #'info
(message "Unable to find location in file"))))
'help-echo (purecopy "mouse-2, RET: find function's definition"))
+(define-button-type 'help-function-cmacro
+ :supertype 'help-xref
+ 'help-function (lambda (fun file)
+ (setq file (locate-library file t))
+ (if (and file (file-readable-p file))
+ (progn
+ (pop-to-buffer (find-file-noselect file))
+ (goto-char (point-min))
+ (if (re-search-forward
+ (format "^[ \t]*(define-compiler-macro[ \t]+%s"
+ (regexp-quote (symbol-name fun))) nil t)
+ (forward-line 0)
+ (message "Unable to find location in file")))
+ (message "Unable to find file")))
+ 'help-echo (purecopy "mouse-2, RET: find function's compiler macro"))
+
(define-button-type 'help-variable-def
:supertype 'help-xref
'help-function (lambda (var &optional file)
(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-exit-action (lambda (buffer)
- (or (window-minibuffer-p (selected-window))
- (one-window-p t)
- (delete-window))))
+ (set (make-local-variable 'view-no-disable-on-exit) t)
+ ;; With Emacs 22 `view-exit-action' could delete the selected window
+ ;; disregarding whether the help buffer was shown in that window at
+ ;; all. Since `view-exit-action' is called with the help buffer as
+ ;; argument it seems more appropriate to have it work on the buffer
+ ;; only and leave it to `view-mode-exit' to delete any associated
+ ;; window(s).
+ (setq view-exit-action
+ (lambda (buffer)
+ ;; Use `with-current-buffer' to make sure that `bury-buffer'
+ ;; also removes BUFFER from the selected window.
+ (with-current-buffer buffer
+ (bury-buffer))))
+
(run-mode-hooks 'help-mode-hook))
;;;###autoload
;;;###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))))
+ (if (eq help-window t)
+ ;; If `help-window' is t, `view-return-to-alist' is handled by
+ ;; `with-help-window'. In this case set `help-window' to the
+ ;; selected window since now is the only moment where we can
+ ;; unambiguously identify it.
+ (setq help-window (selected-window))
+ (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.
(defvar help-back-label (purecopy "[back]")
"Label to use by `help-make-xrefs' for the go-back reference.")
+(defvar help-forward-label (purecopy "[forward]")
+ "Label to use by `help-make-xrefs' for the go-forward reference.")
+
(defconst help-xref-symbol-regexp
(purecopy (concat "\\(\\<\\(\\(variable\\|option\\)\\|" ; Link to var
"\\(function\\|command\\)\\|" ; Link to function
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))
+ (push (cons (point) help-xref-stack-item) help-xref-stack)
+ (setq help-xref-forward-stack nil))
(when interactive-p
(let ((tail (nthcdr 10 help-xref-stack)))
;; Truncate the stack.
(defvar help-xref-following nil
"Non-nil when following a help cross-reference.")
+;;;###autoload
(defun help-buffer ()
+ "Return the name of a buffer for inserting help.
+If `help-xref-following' is non-nil, this is the name of the
+current buffer.
+Otherwise, it is *Help*; if no buffer with that name currently
+exists, it is created."
(buffer-name ;for with-output-to-temp-buffer
(if help-xref-following
(current-buffer)
help buffers. Variable `help-back-label' specifies the text for
that."
(interactive "b")
- (save-excursion
- (set-buffer (or buffer (current-buffer)))
- (goto-char (point-min))
- ;; Skip the header-type info, though it might be useful to parse
- ;; it at some stage (e.g. "function in `library'").
- (forward-paragraph)
- (let ((old-modified (buffer-modified-p)))
- (let ((stab (syntax-table))
- (case-fold-search t)
- (inhibit-read-only t))
- (set-syntax-table emacs-lisp-mode-syntax-table)
- ;; The following should probably be abstracted out.
- (unwind-protect
- (progn
- ;; Info references
- (save-excursion
- (while (re-search-forward help-xref-info-regexp nil t)
- (let ((data (match-string 2)))
- (save-match-data
- (unless (string-match "^([^)]+)" data)
- (setq data (concat "(emacs)" 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.
- (if help-xref-mule-regexp
- (save-excursion
- (while (re-search-forward help-xref-mule-regexp nil t)
- (let* ((data (match-string 7))
- (sym (intern-soft data)))
- (cond
- ((match-string 3) ; coding system
- (and sym (coding-system-p sym)
- (help-xref-button 6 'help-coding-system sym)))
- ((match-string 4) ; input method
- (and (assoc data input-method-alist)
- (help-xref-button 7 'help-input-method data)))
- ((or (match-string 5) (match-string 6)) ; charset
- (and sym (charsetp sym)
- (help-xref-button 7 'help-character-set sym)))
- ((assoc data input-method-alist)
- (help-xref-button 7 'help-character-set data))
- ((and sym (coding-system-p sym))
- (help-xref-button 7 'help-coding-system sym))
- ((and sym (charsetp sym))
- (help-xref-button 7 'help-character-set sym)))))))
- ;; Quoted symbols
- (save-excursion
- (while (re-search-forward help-xref-symbol-regexp nil t)
- (let* ((data (match-string 8))
- (sym (intern-soft data)))
- (if sym
- (cond
- ((match-string 3) ; `variable' &c
- (and (or (boundp sym) ; `variable' doesn't ensure
+ (with-current-buffer (or buffer (current-buffer))
+ (save-excursion
+ (goto-char (point-min))
+ ;; Skip the header-type info, though it might be useful to parse
+ ;; it at some stage (e.g. "function in `library'").
+ (forward-paragraph)
+ (let ((old-modified (buffer-modified-p)))
+ (let ((stab (syntax-table))
+ (case-fold-search t)
+ (inhibit-read-only t))
+ (set-syntax-table emacs-lisp-mode-syntax-table)
+ ;; The following should probably be abstracted out.
+ (unwind-protect
+ (progn
+ ;; Info references
+ (save-excursion
+ (while (re-search-forward help-xref-info-regexp nil t)
+ (let ((data (match-string 2)))
+ (save-match-data
+ (unless (string-match "^([^)]+)" data)
+ (setq data (concat "(emacs)" 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.
+ (if help-xref-mule-regexp
+ (save-excursion
+ (while (re-search-forward help-xref-mule-regexp nil t)
+ (let* ((data (match-string 7))
+ (sym (intern-soft data)))
+ (cond
+ ((match-string 3) ; coding system
+ (and sym (coding-system-p sym)
+ (help-xref-button 6 'help-coding-system sym)))
+ ((match-string 4) ; input method
+ (and (assoc data input-method-alist)
+ (help-xref-button 7 'help-input-method data)))
+ ((or (match-string 5) (match-string 6)) ; charset
+ (and sym (charsetp sym)
+ (help-xref-button 7 'help-character-set sym)))
+ ((assoc data input-method-alist)
+ (help-xref-button 7 'help-character-set data))
+ ((and sym (coding-system-p sym))
+ (help-xref-button 7 'help-coding-system sym))
+ ((and sym (charsetp sym))
+ (help-xref-button 7 'help-character-set sym)))))))
+ ;; Quoted symbols
+ (save-excursion
+ (while (re-search-forward help-xref-symbol-regexp nil t)
+ (let* ((data (match-string 8))
+ (sym (intern-soft data)))
+ (if sym
+ (cond
+ ((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
- (and (fboundp sym) ; similarly
- (help-xref-button 8 'help-function sym)))
- ((match-string 5) ; `face'
- (and (facep sym)
- (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))))
- (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 (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))
- ((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)))))))
- ;; An obvious case of a key substitution:
- (save-excursion
- (while (re-search-forward
- ;; Assume command name is only word and symbol
- ;; characters to get things like `use M-x foo->bar'.
- ;; Command required to end with word constituent
- ;; to avoid `.' at end of a sentence.
- "\\<M-x\\s-+\\(\\sw\\(\\sw\\|\\s_\\)*\\sw\\)" nil t)
- (let ((sym (intern-soft (match-string 1))))
- (if (fboundp sym)
- (help-xref-button 1 'help-function sym)))))
- ;; Look for commands in whole keymap substitutions:
- (save-excursion
- ;; Make sure to find the first keymap.
- (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
- (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\\|\\s_\\)+$"))
- (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")
- (help-insert-xref-button help-back-label 'help-back
- (current-buffer))
- (insert "\n")))
- ;; View mode steals RET from us.
- (set (make-local-variable 'minor-mode-overriding-map-alist)
- (list (cons 'view-mode help-xref-override-view-map)))
- (set-buffer-modified-p old-modified))))
+ (get sym 'variable-documentation))
+ (help-xref-button 8 'help-variable sym)))
+ ((match-string 4) ; `function' &c
+ (and (fboundp sym) ; similarly
+ (help-xref-button 8 'help-function sym)))
+ ((match-string 5) ; `face'
+ (and (facep sym)
+ (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))))
+ (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 (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))
+ ((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)))))))
+ ;; An obvious case of a key substitution:
+ (save-excursion
+ (while (re-search-forward
+ ;; Assume command name is only word and symbol
+ ;; characters to get things like `use M-x foo->bar'.
+ ;; Command required to end with word constituent
+ ;; to avoid `.' at end of a sentence.
+ "\\<M-x\\s-+\\(\\sw\\(\\sw\\|\\s_\\)*\\sw\\)" nil t)
+ (let ((sym (intern-soft (match-string 1))))
+ (if (fboundp sym)
+ (help-xref-button 1 'help-function sym)))))
+ ;; Look for commands in whole keymap substitutions:
+ (save-excursion
+ ;; Make sure to find the first keymap.
+ (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
+ (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\\|\\s_\\)+$"))
+ (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")
+ (when (or help-xref-stack help-xref-forward-stack)
+ (insert "\n"))
+ ;; Make a back-reference in this buffer if appropriate.
+ (when help-xref-stack
+ (help-insert-xref-button help-back-label 'help-back
+ (current-buffer)))
+ ;; Make a forward-reference in this buffer if appropriate.
+ (when help-xref-forward-stack
+ (when help-xref-stack
+ (insert "\t"))
+ (help-insert-xref-button help-forward-label 'help-forward
+ (current-buffer)))
+ (when (or help-xref-stack help-xref-forward-stack)
+ (insert "\n")))
+ ;; View mode steals RET from us.
+ (set (make-local-variable 'minor-mode-overriding-map-alist)
+ (list (cons 'view-mode help-xref-override-view-map)))
+ (set-buffer-modified-p old-modified)))))
;;;###autoload
(defun help-xref-button (match-number type &rest args)
"From BUFFER, go back to previous help buffer text using `help-xref-stack'."
(let (item position method args)
(with-current-buffer buffer
+ (push (cons (point) help-xref-stack-item) help-xref-forward-stack)
(when help-xref-stack
(setq item (pop help-xref-stack)
;; Clear the current item so that it won't get pushed
(set-window-point (get-buffer-window buffer) position)
(goto-char position)))))
+(defun help-xref-go-forward (buffer)
+ "From BUFFER, go forward to next help buffer."
+ (let (item position method args)
+ (with-current-buffer buffer
+ (push (cons (point) help-xref-stack-item) help-xref-stack)
+ (when help-xref-forward-stack
+ (setq item (pop help-xref-forward-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)
+ (with-current-buffer buffer
+ (if (get-buffer-window buffer)
+ (set-window-point (get-buffer-window buffer) position)
+ (goto-char position)))))
+
(defun help-go-back ()
"Go back to previous topic in this help buffer."
(interactive)
(help-xref-go-back (current-buffer))
(error "No previous help buffer")))
+(defun help-go-forward ()
+ "Go back to next topic in this help buffer."
+ (interactive)
+ (if help-xref-forward-stack
+ (help-xref-go-forward (current-buffer))
+ (error "No next 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