X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/0a812366d68919613124e903cec376101f77c57b..2d0ffc9aa8662ec6d931bcb0d755ceb36fc62020:/lisp/apropos.el diff --git a/lisp/apropos.el b/lisp/apropos.el index e220b92892..19a8aab206 100644 --- a/lisp/apropos.el +++ b/lisp/apropos.el @@ -3,7 +3,7 @@ ;; Copyright (C) 1989, 1994, 1995 Free Software Foundation, Inc. ;; Author: Joe Wells -;; Rewritten: Daniel.Pfeiffer@Informatik.START.dbp.de, fax (+49 69) 7588-2389 +;; Rewritten: Daniel Pfeiffer ;; Keywords: help ;; This file is part of GNU Emacs. @@ -19,8 +19,9 @@ ;; 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, 675 Mass Ave, Cambridge, MA 02139, USA. +;; 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. ;;; Commentary: @@ -56,50 +57,67 @@ ;;; Code: +(defgroup apropos nil + "Apropos commands for users and programmers" + :group 'help + :prefix "apropos") + ;; I see a degradation of maybe 10-20% only. -(defvar apropos-do-all nil +(defcustom apropos-do-all nil "*Whether the apropos commands should do more. -Slows them down more or less. Set this non-nil if you have a fast machine.") + +Slows them down more or less. Set this non-nil if you have a fast machine." + :group 'apropos + :type 'boolean) -(defvar apropos-symbol-face (if window-system 'bold) - "*Face for symbol name in apropos output or `nil'. -This looks good, but slows down the commands several times.") +(defcustom apropos-symbol-face 'bold + "*Face for symbol name in Apropos output, or nil for none." + :group 'apropos + :type 'face) -(defvar apropos-keybinding-face (if window-system 'underline) - "*Face for keybinding display in apropos output or `nil'. -This looks good, but slows down the commands several times.") +(defcustom apropos-keybinding-face 'underline + "*Face for lists of keybinding in Apropos output, or nil for none." + :group 'apropos + :type 'face) -(defvar apropos-label-face (if window-system 'italic) - "*Face for label (Command, Variable ...) in apropos output or `nil'. -If this is `nil' no mouse highlighting occurs. -This looks good, but slows down the commands several times. -When this is a face name, as it is initially, it gets transformed to a -text-property list for efficiency.") +(defcustom apropos-label-face 'italic + "*Face for label (`Command', `Variable' ...) in Apropos output. +A value of nil means don't use any special font for them, and also +turns off mouse highlighting." + :group 'apropos + :type 'face) -(defvar apropos-property-face (if window-system 'bold-italic) - "*Face for property name in apropos output or `nil'. -This looks good, but slows down the commands several times.") +(defcustom apropos-property-face 'bold-italic + "*Face for property name in apropos output, or nil for none." + :group 'apropos + :type 'face) -(defvar apropos-match-face (if window-system 'highlight) - "*Face for matching part in apropos-documentation/value output or `nil'. -This looks good, but slows down the commands several times.") +(defcustom apropos-match-face 'secondary-selection + "*Face for matching text in Apropos documentation/value, or nil for none. +This applies when you look for matches in the documentation or variable value +for the regexp; the part that matches gets displayed in this font." + :group 'apropos + :type 'face) -(defvar apropos-local-map +(defvar apropos-mode-map (let ((map (make-sparse-keymap))) (define-key map "\C-m" 'apropos-follow) + (define-key map " " 'scroll-up) + (define-key map "\177" 'scroll-down) + (define-key map "q" 'quit-window) (define-key map [mouse-2] 'apropos-mouse-follow) (define-key map [down-mouse-2] nil) map) - "Local map active when displaying apropos output.") + "Keymap used in Apropos mode.") (defvar apropos-regexp nil "Regexp used in current apropos run.") (defvar apropos-files-scanned () - "List of elc files already scanned in current run of `apropos-documentaion'.") + "List of elc files already scanned in current run of `apropos-documentation'.") (defvar apropos-accumulator () "Alist of symbols already found in current apropos run.") @@ -107,50 +125,86 @@ This looks good, but slows down the commands several times.") (defvar apropos-item () "Current item in or for apropos-accumulator.") +(defun apropos-mode () + "Major mode for following hyperlinks in output of apropos commands. + +\\{apropos-mode-map}" + (interactive) + (kill-all-local-variables) + (use-local-map apropos-mode-map) + (setq major-mode 'apropos-mode + mode-name "Apropos")) + +;;;###autoload +(defun apropos-variable (regexp &optional do-all) + "Show user variables that match REGEXP. +With optional prefix ARG or if `apropos-do-all' is non-nil, also show +normal variables." + (interactive (list (read-string + (concat "Apropos " + (if (or current-prefix-arg apropos-do-all) + "variable" + "user option") + " (regexp): ")) + current-prefix-arg)) + (apropos-command regexp nil + (if (or do-all apropos-do-all) + #'(lambda (symbol) + (and (boundp symbol) + (get symbol 'variable-documentation))) + 'user-variable-p))) + ;; For auld lang syne: ;;;###autoload (fset 'command-apropos 'apropos-command) ;;;###autoload -(defun apropos-command (apropos-regexp &optional do-all) - "Shows commands (interactively callable functions) that match REGEXP. -With optional prefix ARG or if `apropos-do-all' is non-nil, also show -variables." - (interactive (list (read-string (concat "Apropos command " - (if (or current-prefix-arg - apropos-do-all) - "or variable ") - "(regexp): ")) +(defun apropos-command (apropos-regexp &optional do-all var-predicate) + "Show commands (interactively callable functions) that match REGEXP. +With optional prefix ARG, or if `apropos-do-all' is non-nil, also show +noninteractive functions. + +If VAR-PREDICATE is non-nil, show only variables, and only those that +satisfy the predicate VAR-PREDICATE." + (interactive (list (read-string (concat + "Apropos command " + (if (or current-prefix-arg + apropos-do-all) + "or function ") + "(regexp): ")) current-prefix-arg)) (let ((message - (let ((standard-output (get-buffer-create "*Help*"))) + (let ((standard-output (get-buffer-create "*Apropos*"))) (print-help-return-message 'identity)))) (or do-all (setq do-all apropos-do-all)) (setq apropos-accumulator (apropos-internal apropos-regexp - (if do-all - (lambda (symbol) (or (commandp symbol) - (user-variable-p symbol))) - 'commandp))) - (if (apropos-print - t - (lambda (p) - (let (doc symbol) - (while p - (setcar p (list - (setq symbol (car p)) - (if (commandp symbol) - (if (setq doc (documentation symbol t)) - (substring doc 0 (string-match "\n" doc)) - "(not documented)")) - (and do-all - (user-variable-p symbol) - (if (setq doc (documentation-property - symbol 'variable-documentation t)) - (substring doc 0 - (string-match "\n" doc)))))) - (setq p (cdr p))))) - nil) - (and message (message message))))) + (or var-predicate + (if do-all 'functionp 'commandp)))) + (let ((tem apropos-accumulator)) + (while tem + (if (get (car tem) 'apropos-inhibit) + (setq apropos-accumulator (delq (car tem) apropos-accumulator))) + (setq tem (cdr tem)))) + (let ((p apropos-accumulator) + doc symbol) + (while p + (setcar p (list + (setq symbol (car p)) + (unless var-predicate + (if (functionp symbol) + (if (setq doc (documentation symbol t)) + (substring doc 0 (string-match "\n" doc)) + "(not documented)"))) + (and var-predicate + (funcall var-predicate symbol) + (if (setq doc (documentation-property + symbol 'variable-documentation t)) + (substring doc 0 + (string-match "\n" doc)))))) + (setq p (cdr p)))) + (and (apropos-print t nil) + message + (message message)))) ;;;###autoload @@ -167,29 +221,56 @@ Returns list of symbols and documentation found." (lambda (symbol) (or (fboundp symbol) (boundp symbol) + (facep symbol) (symbol-plist symbol)))))) + (let ((tem apropos-accumulator)) + (while tem + (if (get (car tem) 'apropos-inhibit) + (setq apropos-accumulator (delq (car tem) apropos-accumulator))) + (setq tem (cdr tem)))) + (let ((p apropos-accumulator) + symbol doc properties) + (while p + (setcar p (list + (setq symbol (car p)) + (when (fboundp symbol) + (if (setq doc (condition-case nil + (documentation symbol t) + (void-function + "(alias for undefined function)"))) + (substring doc 0 (string-match "\n" doc)) + "(not documented)")) + (when (boundp symbol) + (if (setq doc (documentation-property + symbol 'variable-documentation t)) + (substring doc 0 (string-match "\n" doc)) + "(not documented)")) + (when (setq properties (symbol-plist symbol)) + (setq doc (list (car properties))) + (while (setq properties (cdr (cdr properties))) + (setq doc (cons (car properties) doc))) + (mapconcat #'symbol-name (nreverse doc) " ")) + (when (get symbol 'widget-type) + (if (setq doc (documentation-property + symbol 'widget-documentation t)) + (substring doc 0 + (string-match "\n" doc)) + "(not documented)")) + (when (facep symbol) + (if (setq doc (documentation-property + symbol 'face-documentation t)) + (substring doc 0 + (string-match "\n" doc)) + "(not documented)")) + (when (get symbol 'custom-group) + (if (setq doc (documentation-property + symbol 'group-documentation t)) + (substring doc 0 + (string-match "\n" doc)) + "(not documented)")))) + (setq p (cdr p)))) (apropos-print (or do-all apropos-do-all) - (lambda (p) - (let (symbol doc) - (while p - (setcar p (list - (setq symbol (car p)) - (if (fboundp symbol) - (if (setq doc (documentation symbol t)) - (substring doc 0 (string-match "\n" doc)) - "(not documented)")) - (if (boundp symbol) - (if (setq doc (documentation-property - symbol 'variable-documentation t)) - (substring doc 0 - (string-match "\n" doc)) - "(not documented)")) - (if (setq doc (symbol-plist symbol)) - (if (eq (setq doc (/ (length doc) 2)) 1) - "1 property" - (concat doc " properties"))))) - (setq p (cdr p))))) nil)) @@ -215,12 +296,12 @@ Returns list of symbols and values found." (if (or f v p) (setq apropos-accumulator (cons (list symbol f v p) apropos-accumulator)))))) - (apropos-print nil nil t)) + (apropos-print nil t)) ;;;###autoload (defun apropos-documentation (apropos-regexp &optional do-all) - "Show symbols whose names or documentation contain matches for REGEXP. + "Show symbols whose documentation contain matches for REGEXP. With optional prefix ARG or if `apropos-do-all' is non-nil, also use documentation that is not stored in the documentation file and show key bindings. @@ -238,11 +319,10 @@ Returns list of symbols and documentation found." (mapatoms (lambda (symbol) (setq f (apropos-safe-documentation symbol) - v (get symbol 'variable-documentation) - v (if (integerp v) nil v)) - (or (string-match apropos-regexp (symbol-name symbol)) - (setq f (apropos-documentation-internal f) - v (apropos-documentation-internal v))) + v (get symbol 'variable-documentation)) + (if (integerp v) (setq v)) + (setq f (apropos-documentation-internal f) + v (apropos-documentation-internal v)) (if (or f v) (if (setq apropos-item (cdr (assq symbol apropos-accumulator))) @@ -254,7 +334,7 @@ Returns list of symbols and documentation found." (setq apropos-accumulator (cons (list symbol f v) apropos-accumulator))))))) - (apropos-print do-all nil t)) + (apropos-print nil t)) (kill-buffer standard-input)))) @@ -307,57 +387,64 @@ Returns list of symbols and documentation found." ;; Finds all documentation related to APROPOS-REGEXP in internal-doc-file-name. (defun apropos-documentation-check-doc-file () - (let (type symbol beg end) + (let (type symbol (sepa 2) sepb beg end) + (insert ?\^_) + (backward-char) (insert-file-contents (concat doc-directory internal-doc-file-name)) - (while (re-search-forward apropos-regexp nil t) - (setq beg (match-beginning 0) - end (point)) - (search-backward "\C-_") - (if (> (point) beg) - () - (or (setq type (if (eq ?F (char-after (1+ (point)))) - 1 ;function documentation - 2) ;variable documentation - symbol (prog2 - (forward-char 2) - (read)) - beg (- beg (point) 1) - end (- end (point) 1) - doc (buffer-substring - (1+ (point)) - (if (search-forward "\C-_" nil 'move) - (1- (point)) - (point))) - apropos-item (assq symbol apropos-accumulator)) - (setq apropos-item (list symbol nil nil) - apropos-accumulator (cons apropos-item apropos-accumulator))) - (and apropos-match-face - (>= beg 0) - (put-text-property beg end 'face apropos-match-face doc)) - (setcar (nthcdr type apropos-item) doc))))) + (forward-char) + (while (save-excursion + (setq sepb (search-forward "\^_")) + (not (eobp))) + (beginning-of-line 2) + (if (save-restriction + (narrow-to-region (point) (1- sepb)) + (re-search-forward apropos-regexp nil t)) + (progn + (setq beg (match-beginning 0) + end (point)) + (goto-char (1+ sepa)) + (or (setq type (if (eq ?F (preceding-char)) + 1 ; function documentation + 2) ; variable documentation + symbol (read) + beg (- beg (point) 1) + end (- end (point) 1) + doc (buffer-substring (1+ (point)) (1- sepb)) + apropos-item (assq symbol apropos-accumulator)) + (setq apropos-item (list symbol nil nil) + apropos-accumulator (cons apropos-item + apropos-accumulator))) + (if apropos-match-face + (put-text-property beg end 'face apropos-match-face doc)) + (setcar (nthcdr type apropos-item) doc))) + (setq sepa (goto-char sepb))))) (defun apropos-documentation-check-elc-file (file) (if (member file apropos-files-scanned) nil - (let (symbol doc beg end end1 this-is-a-variable) + (let (symbol doc beg end this-is-a-variable) (setq apropos-files-scanned (cons file apropos-files-scanned)) (erase-buffer) (insert-file-contents file) (while (search-forward "\n#@" nil t) ;; Read the comment length, and advance over it. (setq end (read) - beg (point) - end (+ (point) end 1)) - (if (re-search-forward apropos-regexp end t) + beg (1+ (point)) + end (+ (point) end -1)) + (forward-char) + (if (save-restriction + ;; match ^ and $ relative to doc string + (narrow-to-region beg end) + (re-search-forward apropos-regexp nil t)) (progn - (goto-char end) - (setq doc (buffer-substring (1+ beg) (- end 2)) - end1 (- (match-end 0) beg 1) - beg (- (match-beginning 0) beg 1) - this-is-a-variable (looking-at "(defvar\\|(defconst") + (goto-char (+ end 2)) + (setq doc (buffer-substring beg end) + end (- (match-end 0) beg) + beg (- (match-beginning 0) beg) + this-is-a-variable (looking-at "(def\\(var\\|const\\) ") symbol (progn (skip-chars-forward "(a-z") - (forward-char 1) + (forward-char) (read)) symbol (if (consp symbol) (nth 1 symbol) @@ -371,12 +458,11 @@ Returns list of symbols and documentation found." apropos-accumulator (cons apropos-item apropos-accumulator))) (if apropos-match-face - (put-text-property beg end1 'face apropos-match-face + (put-text-property beg end 'face apropos-match-face doc)) (setcar (nthcdr (if this-is-a-variable 2 1) apropos-item) - doc))))) - (goto-char end))))) + doc))))))))) @@ -404,81 +490,118 @@ Will return nil instead." -(defun apropos-print (do-keys doc-fn spacing) - "Output result of various apropos commands with `apropos-regexp'. -APROPOS-ACCUMULATOR is a list. Optional DOC-FN is called for each element -of apropos-accumulator and may modify it resulting in (symbol fn-doc -var-doc [plist-doc]). Returns sorted list of symbols and documentation -found." +(defvar apropos-label-properties nil + "List of face properties to use for a label. +Bound by `apropos-print' for use by `apropos-print-doc'.") + +(defun apropos-print (do-keys spacing) + "Output result of apropos searching into buffer `*Apropos*'. +The value of `apropos-accumulator' is the list of items to output. +Each element should have the format (SYMBOL FN-DOC VAR-DOC [PLIST-DOC]). +The return value is the list that was in `apropos-accumulator', sorted +alphabetically by symbol name; but this function also sets +`apropos-accumulator' to nil before returning." (if (null apropos-accumulator) (message "No apropos matches for `%s'" apropos-regexp) - (if doc-fn - (funcall doc-fn apropos-accumulator)) (setq apropos-accumulator (sort apropos-accumulator (lambda (a b) - (string-lessp (car a) (car b))))) - (and apropos-label-face - (symbolp apropos-label-face) - (setq apropos-label-face `(face ,apropos-label-face - mouse-face highlight))) + (string-lessp (car a) (car b))))) + (setq apropos-label-properties + (if (and apropos-label-face + (symbolp apropos-label-face)) + `(face ,apropos-label-face + mouse-face highlight))) (with-output-to-temp-buffer "*Apropos*" (let ((p apropos-accumulator) (old-buffer (current-buffer)) symbol item point1 point2) - (save-excursion - (set-buffer standard-output) - (if window-system - (insert (substitute-command-keys - "Click \\\\[apropos-mouse-follow] to get full documentation.\n"))) - (insert (substitute-command-keys - "In this buffer, type \\\\[apropos-follow] to get full documentation.\n\n")) - (use-local-map apropos-local-map) - (while (consp p) - (or (not spacing) (bobp) (terpri)) - (setq apropos-item (car p) - symbol (car apropos-item) - p (cdr p) - point1 (point)) - (princ symbol) ;print symbol name - (setq point2 (point)) - ;; don't calculate key-bindings unless needed - (and do-keys - (commandp symbol) - (indent-to 30 1) - (insert - (if (setq item (save-excursion - (set-buffer old-buffer) - (where-is-internal symbol))) - (mapconcat + (set-buffer standard-output) + (apropos-mode) + (if window-system + (insert "If you move the mouse over text that changes color,\n" + (substitute-command-keys + "you can click \\[apropos-mouse-follow] to get more information.\n"))) + (insert (substitute-command-keys + "In this buffer, type \\[apropos-follow] to get full documentation.\n\n")) + (while (consp p) + (or (not spacing) (bobp) (terpri)) + (setq apropos-item (car p) + symbol (car apropos-item) + p (cdr p) + point1 (point)) + (princ symbol) ; print symbol name + (setq point2 (point)) + ;; Calculate key-bindings if we want them. + (and do-keys + (commandp symbol) + (indent-to 30 1) + (if (let ((keys + (save-excursion + (set-buffer old-buffer) + (where-is-internal symbol))) + filtered) + ;; Copy over the list of key sequences, + ;; omitting any that contain a buffer or a frame. + (while keys + (let ((key (car keys)) + (i 0) + loser) + (while (< i (length key)) + (if (or (framep (aref key i)) + (bufferp (aref key i))) + (setq loser t)) + (setq i (1+ i))) + (or loser + (setq filtered (cons key filtered)))) + (setq keys (cdr keys))) + (setq item filtered)) + ;; Convert the remaining keys to a string and insert. + (insert + (mapconcat + (lambda (key) + (setq key (condition-case () + (key-description key) + (error))) (if apropos-keybinding-face - (lambda (key) - (setq key (key-description key)) - (put-text-property 0 (length key) - 'face apropos-keybinding-face - key) - key) - 'key-description) - item ", ") - "(not bound to any keys)"))) - (terpri) - ;; only now so we don't propagate text attributes all over - (put-text-property point1 point2 'item - (if (eval `(or ,@(cdr apropos-item))) - (car apropos-item) - apropos-item)) - (if apropos-symbol-face - (put-text-property point1 point2 'face apropos-symbol-face)) - (apropos-print-doc 'describe-function 1 - (if (commandp symbol) - "Command" - (if (apropos-macrop symbol) - "Macro" - "Function")) - do-keys) - (apropos-print-doc 'describe-variable 2 - "Variable" do-keys) - (apropos-print-doc 'apropos-describe-plist 3 - "Plist" nil)))))) + (put-text-property 0 (length key) + 'face apropos-keybinding-face + key)) + key) + item ", ")) + (insert "M-x") + (put-text-property (- (point) 3) (point) + 'face apropos-keybinding-face) + (insert " " (symbol-name symbol) " ") + (insert "RET") + (put-text-property (- (point) 3) (point) + 'face apropos-keybinding-face))) + (terpri) + ;; only now so we don't propagate text attributes all over + (put-text-property point1 point2 'item + (if (eval `(or ,@(cdr apropos-item))) + (car apropos-item) + apropos-item)) + (if apropos-symbol-face + (put-text-property point1 point2 'face apropos-symbol-face)) + (apropos-print-doc 'describe-function 1 + (if (commandp symbol) + "Command" + (if (apropos-macrop symbol) + "Macro" + "Function")) + t) + ;; We used to use customize-variable-other-window instead + ;; for a customizable variable, but that is slow. + ;; It is better to show an ordinary help buffer + ;; and let the user click on the customization button + ;; in that buffer, if he wants to. + (apropos-print-doc 'describe-variable 2 "Variable" t) + (apropos-print-doc 'customize-group-other-window 6 "Group" t) + (apropos-print-doc 'customize-face-other-window 5 "Face" t) + (apropos-print-doc 'widget-browse-other-window 4 "Widget" t) + (apropos-print-doc 'apropos-describe-plist 3 + "Plist" nil)) + (setq buffer-read-only t)))) (prog1 apropos-accumulator (setq apropos-accumulator ()))) ; permit gc @@ -501,46 +624,47 @@ found." (put-text-property (- (point) 2) (1- (point)) 'action action) (insert str ": ") - (if apropos-label-face + (if apropos-label-properties (add-text-properties (- (point) (length str) 2) (1- (point)) - apropos-label-face)) + apropos-label-properties)) (insert (if do-keys (substitute-command-keys i) i)) (or (bolp) (terpri))))) (defun apropos-mouse-follow (event) (interactive "e") - (let ((other (if (eq (current-buffer) (get-buffer "*Help*")) + (let ((other (if (eq (current-buffer) (get-buffer "*Apropos*")) () (current-buffer)))) - (set-buffer (window-buffer (posn-window (event-start event)))) - (goto-char (posn-point (event-start event))) - ;; somehow when clicking with the point in another window, undoes badly - (undo-boundary) - (apropos-follow other))) + (save-excursion + (set-buffer (window-buffer (posn-window (event-start event)))) + (goto-char (posn-point (event-start event))) + (or (and (not (eobp)) (get-text-property (point) 'mouse-face)) + (and (not (bobp)) (get-text-property (1- (point)) 'mouse-face)) + (error "There is nothing to follow here")) + (apropos-follow other)))) (defun apropos-follow (&optional other) (interactive) - (let ((point (point)) - (item - (or (and (not (eobp)) (get-text-property (point) 'item)) - (and (not (bobp)) (get-text-property (1- (point)) 'item)))) - action action-point) - (if (null item) + (let* (;; Properties are always found at the beginning of the line. + (bol (save-excursion (beginning-of-line) (point))) + ;; If there is no `item' property here, look behind us. + (item (get-text-property bol 'item)) + (item-at (if item nil (previous-single-property-change bol 'item))) + ;; Likewise, if there is no `action' property here, look in front. + (action (get-text-property bol 'action)) + (action-at (if action nil (next-single-property-change bol 'action)))) + (and (null item) item-at + (setq item (get-text-property (1- item-at) 'item))) + (and (null action) action-at + (setq action (get-text-property action-at 'action))) + (if (not (and item action)) (error "There is nothing to follow here")) - (if (consp item) - (error "There is nothing to follow in `%s'" (car item))) - (while (if (setq action-point - (next-single-property-change (point) 'action)) - (<= action-point point)) - (goto-char (1+ action-point)) - (setq action action-point)) - (funcall - (prog1 (get-text-property (or action action-point (point)) 'action) - (if other (set-buffer other))) - item))) + (if (consp item) (error "There is nothing to follow in `%s'" (car item))) + (if other (set-buffer other)) + (funcall action item))) @@ -554,6 +678,9 @@ found." (if apropos-symbol-face (put-text-property 8 (- (point) 14) 'face apropos-symbol-face)) (insert (apropos-format-plist symbol "\n ")) - (princ ")"))) + (princ ")") + (print-help-return-message))) + +(provide 'apropos) ;;; apropos.el ends here