*** empty log message ***
[bpt/emacs.git] / lisp / apropos.el
CommitLineData
e8af40ee 1;;; apropos.el --- apropos commands for users and programmers
c0274f38 2
0d30b337 3;; Copyright (C) 1989, 1994, 1995, 2001, 2002, 2003, 2004,
409cc4a3 4;; 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
9750e079 5
e5167999 6;; Author: Joe Wells <jbw@bigbird.bu.edu>
ae212837 7;; Rewritten: Daniel Pfeiffer <occitan@esperanto.org>
e9571d2a 8;; Keywords: help
e5167999 9
6f8e447f
RS
10;; This file is part of GNU Emacs.
11
eb3fa2cf 12;; GNU Emacs is free software: you can redistribute it and/or modify
6f8e447f 13;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
6f8e447f
RS
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
eb3fa2cf 23;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
6f8e447f 24
e5167999 25;;; Commentary:
6f8e447f
RS
26
27;; The ideas for this package were derived from the C code in
28;; src/keymap.c and elsewhere. The functions in this file should
29;; always be byte-compiled for speed. Someone should rewrite this in
30;; C (as part of src/keymap.c) for speed.
31
32;; The idea for super-apropos is based on the original implementation
33;; by Lynn Slater <lrs@esl.com>.
34
35;; History:
36;; Fixed bug, current-local-map can return nil.
37;; Change, doesn't calculate key-bindings unless needed.
38;; Added super-apropos capability, changed print functions.
3925e76d
KH
39;;; Made fast-apropos and super-apropos share code.
40;;; Sped up fast-apropos again.
6f8e447f 41;; Added apropos-do-all option.
3925e76d 42;;; Added fast-command-apropos.
6f8e447f 43;; Changed doc strings to comments for helping functions.
3925e76d 44;;; Made doc file buffer read-only, buried it.
6f8e447f
RS
45;; Only call substitute-command-keys if do-all set.
46
645c4f6a
KH
47;; Optionally use configurable faces to make the output more legible.
48;; Differentiate between command, function and macro.
3925e76d
KH
49;; Apropos-command (ex command-apropos) does cmd and optionally user var.
50;; Apropos shows all 3 aspects of symbols (fn, var and plist)
51;; Apropos-documentation (ex super-apropos) now finds all it should.
52;; New apropos-value snoops through all values and optionally plists.
53;; Reading DOC file doesn't load nroff.
54;; Added hypertext following of documentation, mouse-2 on variable gives value
55;; from buffer in active window.
56
e5167999
ER
57;;; Code:
58
851befd4 59(require 'button)
caa8e7aa 60(eval-when-compile (require 'cl))
851befd4 61
ddd2f740 62(defgroup apropos nil
32463e4d 63 "Apropos commands for users and programmers."
c906e3ab 64 :group 'help
ddd2f740
RS
65 :prefix "apropos")
66
3925e76d 67;; I see a degradation of maybe 10-20% only.
ddd2f740 68(defcustom apropos-do-all nil
671c04d9 69 "Whether the apropos commands should do more.
ddd2f740
RS
70
71Slows them down more or less. Set this non-nil if you have a fast machine."
72 :group 'apropos
73 :type 'boolean)
3925e76d
KH
74
75
07eeca5d 76(defcustom apropos-symbol-face 'bold
671c04d9 77 "Face for symbol name in Apropos output, or nil for none."
ddd2f740
RS
78 :group 'apropos
79 :type 'face)
645c4f6a 80
07eeca5d 81(defcustom apropos-keybinding-face 'underline
671c04d9 82 "Face for lists of keybinding in Apropos output, or nil for none."
ddd2f740
RS
83 :group 'apropos
84 :type 'face)
645c4f6a 85
671c04d9
SM
86(defcustom apropos-label-face '(italic variable-pitch)
87 "Face for label (`Command', `Variable' ...) in Apropos output.
07eeca5d
RS
88A value of nil means don't use any special font for them, and also
89turns off mouse highlighting."
ddd2f740
RS
90 :group 'apropos
91 :type 'face)
645c4f6a 92
07eeca5d 93(defcustom apropos-property-face 'bold-italic
671c04d9 94 "Face for property name in apropos output, or nil for none."
ddd2f740
RS
95 :group 'apropos
96 :type 'face)
645c4f6a 97
5248b3e3 98(defcustom apropos-match-face 'match
671c04d9 99 "Face for matching text in Apropos documentation/value, or nil for none.
07eeca5d 100This applies when you look for matches in the documentation or variable value
0820b753 101for the pattern; the part that matches gets displayed in this font."
ddd2f740
RS
102 :group 'apropos
103 :type 'face)
3925e76d 104
ab765ff7 105(defcustom apropos-sort-by-scores nil
671c04d9 106 "Non-nil means sort matches by scores; best match is shown first.
0820b753
KS
107This applies to all `apropos' commands except `apropos-documentation'.
108If value is `verbose', the computed score is shown for each match."
30aab741 109 :group 'apropos
0820b753
KS
110 :type '(choice (const :tag "off" nil)
111 (const :tag "on" t)
112 (const :tag "show scores" verbose)))
113
114(defcustom apropos-documentation-sort-by-scores t
115 "*Non-nil means sort matches by scores; best match is shown first.
116This applies to `apropos-documentation' only.
117If value is `verbose', the computed score is shown for each match."
118 :group 'apropos
119 :type '(choice (const :tag "off" nil)
120 (const :tag "on" t)
121 (const :tag "show scores" verbose)))
6f8e447f 122
26a4a227 123(defvar apropos-mode-map
3925e76d 124 (let ((map (make-sparse-keymap)))
e517f56d
MB
125 (set-keymap-parent map button-buffer-map)
126 ;; Use `apropos-follow' instead of just using the button
127 ;; definition of RET, so that users can use it anywhere in an
128 ;; apropos item, not just on top of a button.
3925e76d 129 (define-key map "\C-m" 'apropos-follow)
5b23b5e4
RS
130 (define-key map " " 'scroll-up)
131 (define-key map "\177" 'scroll-down)
132 (define-key map "q" 'quit-window)
3925e76d 133 map)
26a4a227 134 "Keymap used in Apropos mode.")
4de5599d 135
45f485a6 136(defvar apropos-mode-hook nil
671c04d9 137 "Hook run when mode is turned on.")
3925e76d 138
fe8bc3fa 139(defvar apropos-pattern nil
0820b753
KS
140 "Apropos pattern as entered by user.")
141
142(defvar apropos-pattern-quoted nil
c6b19225 143 "Apropos pattern passed through `regexp-quote'.")
0820b753
KS
144
145(defvar apropos-words ()
146 "Current list of apropos words extracted from `apropos-pattern'.")
645c4f6a 147
0820b753
KS
148(defvar apropos-all-words ()
149 "Current list of words and synonyms.")
7dbffb1c 150
0820b753
KS
151(defvar apropos-regexp nil
152 "Regexp used in current apropos run.")
153
154(defvar apropos-all-words-regexp nil
7dbffb1c
KS
155 "Regexp matching apropos-all-words.")
156
645c4f6a 157(defvar apropos-files-scanned ()
1259a080 158 "List of elc files already scanned in current run of `apropos-documentation'.")
645c4f6a
KH
159
160(defvar apropos-accumulator ()
161 "Alist of symbols already found in current apropos run.")
3925e76d 162
645c4f6a 163(defvar apropos-item ()
7a5348db 164 "Current item in or for `apropos-accumulator'.")
e517f56d 165
7dbffb1c
KS
166(defvar apropos-synonyms '(
167 ("find" "open" "edit")
168 ("kill" "cut")
f4517e51
KS
169 ("yank" "paste")
170 ("region" "selection"))
7dbffb1c 171 "List of synonyms known by apropos.
5e24ee14 172Each element is a list of words where the first word is the standard Emacs
7dbffb1c
KS
173term, and the rest of the words are alternative terms.")
174
e517f56d
MB
175\f
176;;; Button types used by apropos
177
178(define-button-type 'apropos-symbol
179 'face apropos-symbol-face
894e460c 180 'help-echo "mouse-2, RET: Display more help on this symbol"
d8fac801 181 'follow-link t
894e460c
MB
182 'action #'apropos-symbol-button-display-help
183 'skip t)
e517f56d
MB
184
185(defun apropos-symbol-button-display-help (button)
186 "Display further help for the `apropos-symbol' button BUTTON."
187 (button-activate
188 (or (apropos-next-label-button (button-start button))
189 (error "There is nothing to follow for `%s'" (button-label button)))))
190
894e460c
MB
191(define-button-type 'apropos-function
192 'apropos-label "Function"
d8fac801
KS
193 'help-echo "mouse-2, RET: Display more help on this function"
194 'follow-link t
894e460c 195 'action (lambda (button)
d8fac801
KS
196 (describe-function (button-get button 'apropos-symbol))))
197
894e460c
MB
198(define-button-type 'apropos-macro
199 'apropos-label "Macro"
d8fac801
KS
200 'help-echo "mouse-2, RET: Display more help on this macro"
201 'follow-link t
894e460c 202 'action (lambda (button)
d8fac801
KS
203 (describe-function (button-get button 'apropos-symbol))))
204
894e460c
MB
205(define-button-type 'apropos-command
206 'apropos-label "Command"
d8fac801
KS
207 'help-echo "mouse-2, RET: Display more help on this command"
208 'follow-link t
894e460c 209 'action (lambda (button)
d8fac801 210 (describe-function (button-get button 'apropos-symbol))))
7cfedc97 211
894e460c
MB
212;; We used to use `customize-variable-other-window' instead for a
213;; customizable variable, but that is slow. It is better to show an
214;; ordinary help buffer and let the user click on the customization
215;; button in that buffer, if he wants to.
216;; Likewise for `customize-face-other-window'.
217(define-button-type 'apropos-variable
218 'apropos-label "Variable"
219 'help-echo "mouse-2, RET: Display more help on this variable"
d8fac801 220 'follow-link t
894e460c
MB
221 'action (lambda (button)
222 (describe-variable (button-get button 'apropos-symbol))))
223
224(define-button-type 'apropos-face
225 'apropos-label "Face"
226 'help-echo "mouse-2, RET: Display more help on this face"
d8fac801 227 'follow-link t
894e460c
MB
228 'action (lambda (button)
229 (describe-face (button-get button 'apropos-symbol))))
230
231(define-button-type 'apropos-group
232 'apropos-label "Group"
233 'help-echo "mouse-2, RET: Display more help on this group"
d8fac801 234 'follow-link t
894e460c 235 'action (lambda (button)
2d37b91e 236 (customize-group-other-window
894e460c
MB
237 (button-get button 'apropos-symbol))))
238
239(define-button-type 'apropos-widget
240 'apropos-label "Widget"
241 'help-echo "mouse-2, RET: Display more help on this widget"
d8fac801 242 'follow-link t
894e460c
MB
243 'action (lambda (button)
244 (widget-browse-other-window (button-get button 'apropos-symbol))))
245
246(define-button-type 'apropos-plist
247 'apropos-label "Plist"
248 'help-echo "mouse-2, RET: Display more help on this plist"
d8fac801 249 'follow-link t
894e460c
MB
250 'action (lambda (button)
251 (apropos-describe-plist (button-get button 'apropos-symbol))))
e517f56d 252
2e3d43ac
SM
253(define-button-type 'apropos-library
254 'help-echo "mouse-2, RET: Display more help on this library"
255 'follow-link t
256 'action (lambda (button)
257 (apropos-library (button-get button 'apropos-symbol))))
258
e517f56d 259(defun apropos-next-label-button (pos)
a11a4e9f 260 "Return the next apropos label button after POS, or nil if there's none.
e517f56d
MB
261Will also return nil if more than one `apropos-symbol' button is encountered
262before finding a label."
a101302b 263 (let* ((button (next-button pos t))
e517f56d 264 (already-hit-symbol nil)
3b8c60f1
MB
265 (label (and button (button-get button 'apropos-label)))
266 (type (and button (button-get button 'type))))
e517f56d 267 (while (and button
3b8c60f1
MB
268 (not label)
269 (or (not (eq type 'apropos-symbol))
e517f56d 270 (not already-hit-symbol)))
3b8c60f1 271 (when (eq type 'apropos-symbol)
e517f56d
MB
272 (setq already-hit-symbol t))
273 (setq button (next-button (button-start button)))
274 (when button
3b8c60f1
MB
275 (setq label (button-get button 'apropos-label))
276 (setq type (button-get button 'type))))
277 (and label button)))
e517f56d 278
645c4f6a 279\f
7dbffb1c
KS
280(defun apropos-words-to-regexp (words wild)
281 "Make regexp matching any two of the words in WORDS."
282 (concat "\\("
283 (mapconcat 'identity words "\\|")
80f5d2ef 284 "\\)"
71296446 285 (if (cdr words)
80f5d2ef
AS
286 (concat wild
287 "\\("
7dbffb1c
KS
288 (mapconcat 'identity words "\\|")
289 "\\)")
290 "")))
291
0820b753
KS
292;;;###autoload
293(defun apropos-read-pattern (subject)
294 "Read an apropos pattern, either a word list or a regexp.
295Returns the user pattern, either a list of words which are matched
296literally, or a string which is used as a regexp to search for.
297
298SUBJECT is a string that is included in the prompt to identify what
299kind of objects to search."
300 (let ((pattern
301 (read-string (concat "Apropos " subject " (word list or regexp): "))))
302 (if (string-equal (regexp-quote pattern) pattern)
303 ;; Split into words
304 (split-string pattern "[ \t]+")
305 pattern)))
306
307(defun apropos-parse-pattern (pattern)
308 "Rewrite a list of words to a regexp matching all permutations.
3fefda51
KS
309If PATTERN is a string, that means it is already a regexp.
310This updates variables `apropos-pattern', `apropos-pattern-quoted',
311`apropos-regexp', `apropos-words', and `apropos-all-words-regexp'."
0820b753
KS
312 (setq apropos-words nil
313 apropos-all-words nil)
314 (if (consp pattern)
7dbffb1c
KS
315 ;; We don't actually make a regexp matching all permutations.
316 ;; Instead, for e.g. "a b c", we make a regexp matching
317 ;; any combination of two or more words like this:
318 ;; (a|b|c).*(a|b|c) which may give some false matches,
319 ;; but as long as it also gives the right ones, that's ok.
0820b753
KS
320 (let ((words pattern))
321 (setq apropos-pattern (mapconcat 'identity pattern " ")
322 apropos-pattern-quoted (regexp-quote apropos-pattern))
7dbffb1c
KS
323 (dolist (word words)
324 (let ((syn apropos-synonyms) (s word) (a word))
325 (while syn
326 (if (member word (car syn))
327 (progn
328 (setq a (mapconcat 'identity (car syn) "\\|"))
329 (if (member word (cdr (car syn)))
330 (setq s a))
331 (setq syn nil))
332 (setq syn (cdr syn))))
333 (setq apropos-words (cons s apropos-words)
334 apropos-all-words (cons a apropos-all-words))))
3fefda51
KS
335 (setq apropos-all-words-regexp
336 (apropos-words-to-regexp apropos-all-words ".+"))
337 (setq apropos-regexp
338 (apropos-words-to-regexp apropos-words ".*?")))
0820b753
KS
339 (setq apropos-pattern-quoted (regexp-quote pattern)
340 apropos-all-words-regexp pattern
3fefda51
KS
341 apropos-pattern pattern
342 apropos-regexp pattern)))
0820b753 343
7dbffb1c
KS
344
345(defun apropos-calc-scores (str words)
346 "Return apropos scores for string STR matching WORDS.
347Value is a list of offsets of the words into the string."
0820b753 348 (let (scores i)
7dbffb1c
KS
349 (if words
350 (dolist (word words scores)
351 (if (setq i (string-match word str))
352 (setq scores (cons i scores))))
353 ;; Return list of start and end position of regexp
347a20b8 354 (and (string-match apropos-pattern str)
0820b753 355 (list (match-beginning 0) (match-end 0))))))
7dbffb1c
KS
356
357(defun apropos-score-str (str)
358 "Return apropos score for string STR."
359 (if str
0820b753
KS
360 (let* ((l (length str))
361 (score (- (/ l 10))))
7dbffb1c 362 (dolist (s (apropos-calc-scores str apropos-all-words) score)
d5857a96 363 (setq score (+ score 1000 (/ (* (- l s) 1000) l)))))
7dbffb1c
KS
364 0))
365
366(defun apropos-score-doc (doc)
367 "Return apropos score for documentation string DOC."
b7a2a696
LK
368 (let ((l (length doc)))
369 (if (> l 0)
0820b753
KS
370 (let ((score 0) i)
371 (when (setq i (string-match apropos-pattern-quoted doc))
372 (setq score 10000))
b7a2a696
LK
373 (dolist (s (apropos-calc-scores doc apropos-all-words) score)
374 (setq score (+ score 50 (/ (* (- l s) 50) l)))))
375 0)))
71296446 376
7dbffb1c
KS
377(defun apropos-score-symbol (symbol &optional weight)
378 "Return apropos score for SYMBOL."
379 (setq symbol (symbol-name symbol))
380 (let ((score 0)
0820b753 381 (l (length symbol)))
7dbffb1c
KS
382 (dolist (s (apropos-calc-scores symbol apropos-words) (* score (or weight 3)))
383 (setq score (+ score (- 60 l) (/ (* (- l s) 60) l))))))
384
d2b30292
KS
385(defun apropos-true-hit (str words)
386 "Return t if STR is a genuine hit.
387This may fail if only one of the keywords is matched more than once.
388This requires that at least 2 keywords (unless only one was given)."
389 (or (not str)
390 (not words)
391 (not (cdr words))
392 (> (length (apropos-calc-scores str words)) 1)))
393
394(defun apropos-false-hit-symbol (symbol)
395 "Return t if SYMBOL is not really matched by the current keywords."
396 (not (apropos-true-hit (symbol-name symbol) apropos-words)))
397
398(defun apropos-false-hit-str (str)
399 "Return t if STR is not really matched by the current keywords."
400 (not (apropos-true-hit str apropos-words)))
401
402(defun apropos-true-hit-doc (doc)
403 "Return t if DOC is really matched by the current keywords."
404 (apropos-true-hit doc apropos-all-words))
405
38ab866c 406(define-derived-mode apropos-mode fundamental-mode "Apropos"
26a4a227
KH
407 "Major mode for following hyperlinks in output of apropos commands.
408
38ab866c 409\\{apropos-mode-map}")
26a4a227 410
f38fd610 411;;;###autoload
0820b753
KS
412(defun apropos-variable (pattern &optional do-all)
413 "Show user variables that match PATTERN.
414PATTERN can be a word, a list of words (separated by spaces),
415or a regexp (using some regexp special characters). If it is a word,
416search for matches for that word as a substring. If it is a list of words,
417search for matches for any two (or more) of those words.
418
419With \\[universal-argument] prefix, or if `apropos-do-all' is non-nil, also show
05942d06 420normal variables."
0820b753
KS
421 (interactive (list (apropos-read-pattern
422 (if (or current-prefix-arg apropos-do-all)
423 "variable" "user option"))
05942d06 424 current-prefix-arg))
0820b753 425 (apropos-command pattern nil
cd00fd36 426 (if (or do-all apropos-do-all)
05942d06
RS
427 #'(lambda (symbol)
428 (and (boundp symbol)
429 (get symbol 'variable-documentation)))
430 'user-variable-p)))
26a4a227 431
645c4f6a
KH
432;; For auld lang syne:
433;;;###autoload
82e736c1 434(defalias 'command-apropos 'apropos-command)
6f8e447f 435;;;###autoload
0820b753
KS
436(defun apropos-command (pattern &optional do-all var-predicate)
437 "Show commands (interactively callable functions) that match PATTERN.
438PATTERN can be a word, a list of words (separated by spaces),
fe8bc3fa
RS
439or a regexp (using some regexp special characters). If it is a word,
440search for matches for that word as a substring. If it is a list of words,
441search for matches for any two (or more) of those words.
442
0820b753 443With \\[universal-argument] prefix, or if `apropos-do-all' is non-nil, also show
9a909b3c 444noninteractive functions.
05942d06 445
9a909b3c 446If VAR-PREDICATE is non-nil, show only variables, and only those that
0820b753
KS
447satisfy the predicate VAR-PREDICATE.
448
449When called from a Lisp program, a string PATTERN is used as a regexp,
450while a list of strings is used as a word list."
451 (interactive (list (apropos-read-pattern
452 (if (or current-prefix-arg apropos-do-all)
453 "command or function" "command"))
645c4f6a 454 current-prefix-arg))
3fefda51 455 (apropos-parse-pattern pattern)
c851bcec 456 (let ((message
26a4a227 457 (let ((standard-output (get-buffer-create "*Apropos*")))
3925e76d 458 (print-help-return-message 'identity))))
645c4f6a
KH
459 (or do-all (setq do-all apropos-do-all))
460 (setq apropos-accumulator
0820b753 461 (apropos-internal apropos-regexp
cd00fd36 462 (or var-predicate
554fde6e
SM
463 ;; We used to use `functionp' here, but this
464 ;; rules out macros. `fboundp' rules in
465 ;; keymaps, but it seems harmless.
466 (if do-all 'fboundp 'commandp))))
dea22c45
RS
467 (let ((tem apropos-accumulator))
468 (while tem
d2b30292
KS
469 (if (or (get (car tem) 'apropos-inhibit)
470 (apropos-false-hit-symbol (car tem)))
dea22c45
RS
471 (setq apropos-accumulator (delq (car tem) apropos-accumulator)))
472 (setq tem (cdr tem))))
a9155e87 473 (let ((p apropos-accumulator)
7dbffb1c 474 doc symbol score)
a9155e87
KH
475 (while p
476 (setcar p (list
477 (setq symbol (car p))
7dbffb1c 478 (setq score (apropos-score-symbol symbol))
a9155e87 479 (unless var-predicate
554fde6e 480 (if (fboundp symbol)
a9155e87 481 (if (setq doc (documentation symbol t))
7dbffb1c 482 (progn
71296446 483 (setq score (+ score (apropos-score-doc doc)))
7dbffb1c 484 (substring doc 0 (string-match "\n" doc)))
a9155e87
KH
485 "(not documented)")))
486 (and var-predicate
487 (funcall var-predicate symbol)
488 (if (setq doc (documentation-property
489 symbol 'variable-documentation t))
7dbffb1c
KS
490 (progn
491 (setq score (+ score (apropos-score-doc doc)))
492 (substring doc 0
493 (string-match "\n" doc)))))))
494 (setcar (cdr (car p)) score)
a9155e87 495 (setq p (cdr p))))
0820b753 496 (and (apropos-print t nil nil t)
a9155e87 497 message
8a26c165 498 (message "%s" message))))
3925e76d
KH
499
500
3925e76d 501;;;###autoload
914b40da
RS
502(defun apropos-documentation-property (symbol property raw)
503 "Like (documentation-property SYMBOL PROPERTY RAW) but handle errors."
504 (condition-case ()
505 (let ((doc (documentation-property symbol property raw)))
506 (if doc (substring doc 0 (string-match "\n" doc))
507 "(not documented)"))
508 (error "(error retrieving documentation)")))
509
5760219d
JPW
510
511;;;###autoload
0820b753 512(defun apropos (pattern &optional do-all)
2a4ec7e1
RS
513 "Show all meaningful Lisp symbols whose names match PATTERN.
514Symbols are shown if they are defined as functions, variables, or
515faces, or if they have nonempty property lists.
516
0820b753 517PATTERN can be a word, a list of words (separated by spaces),
fe8bc3fa
RS
518or a regexp (using some regexp special characters). If it is a word,
519search for matches for that word as a substring. If it is a list of words,
520search for matches for any two (or more) of those words.
521
543e570f
RS
522With \\[universal-argument] prefix, or if `apropos-do-all' is non-nil,
523consider all symbols (if they match PATTERN).
524
525Returns list of symbols and documentation found."
0820b753
KS
526 (interactive (list (apropos-read-pattern "symbol")
527 current-prefix-arg))
3fefda51 528 (apropos-parse-pattern pattern)
caa8e7aa 529 (apropos-symbols-internal
0820b753 530 (apropos-internal apropos-regexp
543e570f
RS
531 (and (not do-all)
532 (not apropos-do-all)
533 (lambda (symbol)
534 (or (fboundp symbol)
535 (boundp symbol)
536 (facep symbol)
537 (symbol-plist symbol)))))
caa8e7aa
SM
538 (or do-all apropos-do-all)))
539
2e3d43ac
SM
540(defun apropos-library-button (sym)
541 (if (null sym)
542 "<nothing>"
543 (let ((name (copy-sequence (symbol-name sym))))
544 (make-text-button name nil
545 'type 'apropos-library
546 'face apropos-symbol-face
547 'apropos-symbol name)
548 name)))
549
550;;;###autoload
551(defun apropos-library (file)
552 "List the variables and functions defined by library FILE.
553FILE should be one of the libraries currently loaded and should
554thus be found in `load-history'."
555 (interactive
556 (let ((libs
557 (nconc (delq nil
558 (mapcar
559 (lambda (l)
560 (setq l (file-name-nondirectory l))
561 (while
562 (not (equal (setq l (file-name-sans-extension l))
563 l)))
564 l)
565 (mapcar 'car load-history)))
566 (mapcar 'car load-history))))
567 (list (completing-read "Describe library: " libs nil t))))
568 (let ((symbols nil)
569 ;; (autoloads nil)
570 (provides nil)
571 (requires nil)
572 (lh-entry (assoc file load-history)))
573 (unless lh-entry
574 ;; `file' may be the "shortname".
575 (let ((lh load-history)
576 (re (concat "\\(?:\\`\\|[\\/]\\)" (regexp-quote file)
577 "\\(\\.\\|\\'\\)")))
578 (while (and lh (null lh-entry))
579 (if (string-match re (caar lh))
580 (setq lh-entry (car lh))
581 (setq lh (cdr lh)))))
582 (unless lh-entry (error "Unknown library `%s'" file)))
583 (dolist (x (cdr lh-entry))
584 (case (car-safe x)
585 ;; (autoload (push (cdr x) autoloads))
586 (require (push (cdr x) requires))
587 (provide (push (cdr x) provides))
588 (t (push (or (cdr-safe x) x) symbols))))
589 (let ((apropos-pattern "")) ;Dummy binding for apropos-symbols-internal.
590 (apropos-symbols-internal
591 symbols apropos-do-all
592 (concat
593 (format "Library `%s' provides: %s\nand requires: %s"
594 file
595 (mapconcat 'apropos-library-button
596 (or provides '(nil)) " and ")
597 (mapconcat 'apropos-library-button
598 (or requires '(nil)) " and ")))))))
599
caa8e7aa
SM
600(defun apropos-symbols-internal (symbols keys &optional text)
601 ;; Filter out entries that are marked as apropos-inhibit.
602 (let ((all nil))
603 (dolist (symbol symbols)
604 (unless (get symbol 'apropos-inhibit)
605 (push symbol all)))
606 (setq symbols all))
607 (let ((apropos-accumulator
608 (mapcar
609 (lambda (symbol)
610 (let (doc properties)
611 (list
612 symbol
613 (apropos-score-symbol symbol)
614 (when (fboundp symbol)
615 (if (setq doc (condition-case nil
616 (documentation symbol t)
617 (void-function
618 "(alias for undefined function)")
619 (error
620 "(can't retrieve function documentation)")))
621 (substring doc 0 (string-match "\n" doc))
622 "(not documented)"))
623 (when (boundp symbol)
624 (apropos-documentation-property
914b40da 625 symbol 'variable-documentation t))
a9155e87
KH
626 (when (setq properties (symbol-plist symbol))
627 (setq doc (list (car properties)))
628 (while (setq properties (cdr (cdr properties)))
629 (setq doc (cons (car properties) doc)))
630 (mapconcat #'symbol-name (nreverse doc) " "))
631 (when (get symbol 'widget-type)
914b40da
RS
632 (apropos-documentation-property
633 symbol 'widget-documentation t))
caa8e7aa
SM
634 (when (facep symbol)
635 (apropos-documentation-property
636 symbol 'face-documentation t))
637 (when (get symbol 'custom-group)
914b40da 638 (apropos-documentation-property
caa8e7aa
SM
639 symbol 'group-documentation t)))))
640 symbols)))
641 (apropos-print keys nil text)))
3925e76d
KH
642
643
6f8e447f 644;;;###autoload
0820b753 645(defun apropos-value (pattern &optional do-all)
2a4ec7e1 646 "Show all symbols whose value's printed representation matches PATTERN.
0820b753 647PATTERN can be a word, a list of words (separated by spaces),
fe8bc3fa
RS
648or a regexp (using some regexp special characters). If it is a word,
649search for matches for that word as a substring. If it is a list of words,
650search for matches for any two (or more) of those words.
651
0820b753 652With \\[universal-argument] prefix, or if `apropos-do-all' is non-nil, also looks
3925e76d 653at the function and at the names and values of properties.
645c4f6a 654Returns list of symbols and values found."
0820b753
KS
655 (interactive (list (apropos-read-pattern "value")
656 current-prefix-arg))
3fefda51 657 (apropos-parse-pattern pattern)
645c4f6a
KH
658 (or do-all (setq do-all apropos-do-all))
659 (setq apropos-accumulator ())
660 (let (f v p)
3925e76d
KH
661 (mapatoms
662 (lambda (symbol)
663 (setq f nil v nil p nil)
0820b753
KS
664 (or (memq symbol '(apropos-regexp
665 apropos-pattern apropos-all-words-regexp
7dbffb1c
KS
666 apropos-words apropos-all-words
667 do-all apropos-accumulator
668 symbol f v p))
645c4f6a 669 (setq v (apropos-value-internal 'boundp symbol 'symbol-value)))
3925e76d 670 (if do-all
645c4f6a
KH
671 (setq f (apropos-value-internal 'fboundp symbol 'symbol-function)
672 p (apropos-format-plist symbol "\n " t)))
d2b30292
KS
673 (if (apropos-false-hit-str v)
674 (setq v nil))
675 (if (apropos-false-hit-str f)
676 (setq f nil))
677 (if (apropos-false-hit-str p)
678 (setq p nil))
3925e76d 679 (if (or f v p)
71296446 680 (setq apropos-accumulator (cons (list symbol
7dbffb1c
KS
681 (+ (apropos-score-str f)
682 (apropos-score-str v)
683 (apropos-score-str p))
684 f v p)
645c4f6a 685 apropos-accumulator))))))
9cc84e31 686 (apropos-print nil "\n----------------\n"))
3925e76d
KH
687
688
645c4f6a 689;;;###autoload
0820b753 690(defun apropos-documentation (pattern &optional do-all)
2a4ec7e1 691 "Show symbols whose documentation contains matches for PATTERN.
0820b753 692PATTERN can be a word, a list of words (separated by spaces),
fe8bc3fa
RS
693or a regexp (using some regexp special characters). If it is a word,
694search for matches for that word as a substring. If it is a list of words,
695search for matches for any two (or more) of those words.
696
0820b753 697With \\[universal-argument] prefix, or if `apropos-do-all' is non-nil, also use
645c4f6a
KH
698documentation that is not stored in the documentation file and show key
699bindings.
700Returns list of symbols and documentation found."
0820b753
KS
701 (interactive (list (apropos-read-pattern "documentation")
702 current-prefix-arg))
3fefda51 703 (apropos-parse-pattern pattern)
645c4f6a
KH
704 (or do-all (setq do-all apropos-do-all))
705 (setq apropos-accumulator () apropos-files-scanned ())
706 (let ((standard-input (get-buffer-create " apropos-temp"))
0820b753 707 (apropos-sort-by-scores apropos-documentation-sort-by-scores)
7dbffb1c 708 f v sf sv)
645c4f6a
KH
709 (unwind-protect
710 (save-excursion
711 (set-buffer standard-input)
712 (apropos-documentation-check-doc-file)
713 (if do-all
714 (mapatoms
715 (lambda (symbol)
716 (setq f (apropos-safe-documentation symbol)
26a4a227
KH
717 v (get symbol 'variable-documentation))
718 (if (integerp v) (setq v))
719 (setq f (apropos-documentation-internal f)
720 v (apropos-documentation-internal v))
7dbffb1c
KS
721 (setq sf (apropos-score-doc f)
722 sv (apropos-score-doc v))
645c4f6a
KH
723 (if (or f v)
724 (if (setq apropos-item
725 (cdr (assq symbol apropos-accumulator)))
726 (progn
727 (if f
7dbffb1c
KS
728 (progn
729 (setcar (nthcdr 1 apropos-item) f)
730 (setcar apropos-item (+ (car apropos-item) sf))))
645c4f6a 731 (if v
7dbffb1c
KS
732 (progn
733 (setcar (nthcdr 2 apropos-item) v)
734 (setcar apropos-item (+ (car apropos-item) sv)))))
645c4f6a 735 (setq apropos-accumulator
71296446 736 (cons (list symbol
7dbffb1c
KS
737 (+ (apropos-score-symbol symbol 2) sf sv)
738 f v)
645c4f6a 739 apropos-accumulator)))))))
0820b753 740 (apropos-print nil "\n----------------\n" nil t))
645c4f6a
KH
741 (kill-buffer standard-input))))
742
743\f
744(defun apropos-value-internal (predicate symbol function)
745 (if (funcall predicate symbol)
746 (progn
747 (setq symbol (prin1-to-string (funcall function symbol)))
0820b753 748 (if (string-match apropos-regexp symbol)
645c4f6a
KH
749 (progn
750 (if apropos-match-face
751 (put-text-property (match-beginning 0) (match-end 0)
752 'face apropos-match-face
753 symbol))
754 symbol)))))
755
756(defun apropos-documentation-internal (doc)
757 (if (consp doc)
758 (apropos-documentation-check-elc-file (car doc))
0820b753
KS
759 (if (and doc
760 (string-match apropos-all-words-regexp doc)
761 (apropos-true-hit-doc doc))
762 (when apropos-match-face
763 (setq doc (substitute-command-keys (copy-sequence doc)))
764 (if (or (string-match apropos-pattern-quoted doc)
765 (string-match apropos-all-words-regexp doc))
766 (put-text-property (match-beginning 0)
767 (match-end 0)
768 'face apropos-match-face doc))
769 doc))))
645c4f6a
KH
770
771(defun apropos-format-plist (pl sep &optional compare)
3925e76d
KH
772 (setq pl (symbol-plist pl))
773 (let (p p-out)
774 (while pl
775 (setq p (format "%s %S" (car pl) (nth 1 pl)))
0820b753 776 (if (or (not compare) (string-match apropos-regexp p))
645c4f6a 777 (if apropos-property-face
3925e76d 778 (put-text-property 0 (length (symbol-name (car pl)))
645c4f6a 779 'face apropos-property-face p))
3925e76d 780 (setq p nil))
645c4f6a
KH
781 (if p
782 (progn
783 (and compare apropos-match-face
784 (put-text-property (match-beginning 0) (match-end 0)
785 'face apropos-match-face
786 p))
787 (setq p-out (concat p-out (if p-out sep) p))))
3925e76d
KH
788 (setq pl (nthcdr 2 pl)))
789 p-out))
790
6f8e447f 791
0820b753 792;; Finds all documentation related to APROPOS-REGEXP in internal-doc-file-name.
3925e76d 793
645c4f6a 794(defun apropos-documentation-check-doc-file ()
0820b753 795 (let (type symbol (sepa 2) sepb)
26a4a227
KH
796 (insert ?\^_)
797 (backward-char)
645c4f6a 798 (insert-file-contents (concat doc-directory internal-doc-file-name))
26a4a227
KH
799 (forward-char)
800 (while (save-excursion
801 (setq sepb (search-forward "\^_"))
802 (not (eobp)))
803 (beginning-of-line 2)
804 (if (save-restriction
805 (narrow-to-region (point) (1- sepb))
0820b753 806 (re-search-forward apropos-all-words-regexp nil t))
26a4a227 807 (progn
26a4a227 808 (goto-char (1+ sepa))
d2b30292
KS
809 (setq type (if (eq ?F (preceding-char))
810 2 ; function documentation
811 3) ; variable documentation
812 symbol (read)
d2b30292
KS
813 doc (buffer-substring (1+ (point)) (1- sepb)))
814 (when (apropos-true-hit-doc doc)
815 (or (and (setq apropos-item (assq symbol apropos-accumulator))
816 (setcar (cdr apropos-item)
0820b753 817 (apropos-score-doc doc)))
71296446 818 (setq apropos-item (list symbol
d2b30292
KS
819 (+ (apropos-score-symbol symbol 2)
820 (apropos-score-doc doc))
821 nil nil)
822 apropos-accumulator (cons apropos-item
823 apropos-accumulator)))
0820b753
KS
824 (when apropos-match-face
825 (setq doc (substitute-command-keys doc))
826 (if (or (string-match apropos-pattern-quoted doc)
827 (string-match apropos-all-words-regexp doc))
828 (put-text-property (match-beginning 0)
829 (match-end 0)
830 'face apropos-match-face doc)))
d2b30292 831 (setcar (nthcdr type apropos-item) doc))))
26a4a227 832 (setq sepa (goto-char sepb)))))
645c4f6a
KH
833
834(defun apropos-documentation-check-elc-file (file)
835 (if (member file apropos-files-scanned)
836 nil
26a4a227 837 (let (symbol doc beg end this-is-a-variable)
645c4f6a
KH
838 (setq apropos-files-scanned (cons file apropos-files-scanned))
839 (erase-buffer)
840 (insert-file-contents file)
841 (while (search-forward "\n#@" nil t)
842 ;; Read the comment length, and advance over it.
843 (setq end (read)
26a4a227
KH
844 beg (1+ (point))
845 end (+ (point) end -1))
846 (forward-char)
847 (if (save-restriction
848 ;; match ^ and $ relative to doc string
849 (narrow-to-region beg end)
0820b753 850 (re-search-forward apropos-all-words-regexp nil t))
645c4f6a 851 (progn
26a4a227
KH
852 (goto-char (+ end 2))
853 (setq doc (buffer-substring beg end)
854 end (- (match-end 0) beg)
d2b30292
KS
855 beg (- (match-beginning 0) beg))
856 (when (apropos-true-hit-doc doc)
857 (setq this-is-a-variable (looking-at "(def\\(var\\|const\\) ")
858 symbol (progn
859 (skip-chars-forward "(a-z")
860 (forward-char)
861 (read))
862 symbol (if (consp symbol)
863 (nth 1 symbol)
864 symbol))
865 (if (if this-is-a-variable
866 (get symbol 'variable-documentation)
867 (and (fboundp symbol) (apropos-safe-documentation symbol)))
868 (progn
869 (or (and (setq apropos-item (assq symbol apropos-accumulator))
870 (setcar (cdr apropos-item)
871 (+ (cadr apropos-item) (apropos-score-doc doc))))
872 (setq apropos-item (list symbol
873 (+ (apropos-score-symbol symbol 2)
874 (apropos-score-doc doc))
875 nil nil)
876 apropos-accumulator (cons apropos-item
877 apropos-accumulator)))
0820b753
KS
878 (when apropos-match-face
879 (setq doc (substitute-command-keys doc))
880 (if (or (string-match apropos-pattern-quoted doc)
881 (string-match apropos-all-words-regexp doc))
882 (put-text-property (match-beginning 0)
883 (match-end 0)
884 'face apropos-match-face doc)))
d2b30292
KS
885 (setcar (nthcdr (if this-is-a-variable 3 2)
886 apropos-item)
887 doc))))))))))
645c4f6a
KH
888
889
890
891(defun apropos-safe-documentation (function)
7a5348db 892 "Like `documentation', except it avoids calling `get_doc_string'.
6f8e447f 893Will return nil instead."
3925e76d 894 (while (and function (symbolp function))
6f8e447f 895 (setq function (if (fboundp function)
3925e76d 896 (symbol-function function))))
d2e1218f
RS
897 (if (eq (car-safe function) 'macro)
898 (setq function (cdr function)))
3925e76d 899 (setq function (if (byte-code-function-p function)
645c4f6a
KH
900 (if (> (length function) 4)
901 (aref function 4))
902 (if (eq (car-safe function) 'autoload)
903 (nth 2 function)
904 (if (eq (car-safe function) 'lambda)
905 (if (stringp (nth 2 function))
906 (nth 2 function)
907 (if (stringp (nth 3 function))
908 (nth 3 function)))))))
909 (if (integerp function)
910 nil
911 function))
912
913
0820b753 914(defun apropos-print (do-keys spacing &optional text nosubst)
a9155e87
KH
915 "Output result of apropos searching into buffer `*Apropos*'.
916The value of `apropos-accumulator' is the list of items to output.
71296446 917Each element should have the format
7dbffb1c 918 (SYMBOL SCORE FN-DOC VAR-DOC [PLIST-DOC WIDGET-DOC FACE-DOC GROUP-DOC]).
a9155e87
KH
919The return value is the list that was in `apropos-accumulator', sorted
920alphabetically by symbol name; but this function also sets
9cc84e31
RS
921`apropos-accumulator' to nil before returning.
922
caa8e7aa
SM
923If SPACING is non-nil, it should be a string; separate items with that string.
924If non-nil TEXT is a string that will be printed as a heading."
645c4f6a 925 (if (null apropos-accumulator)
0820b753 926 (message "No apropos matches for `%s'" apropos-pattern)
645c4f6a 927 (setq apropos-accumulator
30aab741
RS
928 (sort apropos-accumulator
929 (lambda (a b)
930 ;; Don't sort by score if user can't see the score.
931 ;; It would be confusing. -- rms.
ab765ff7 932 (if apropos-sort-by-scores
30aab741
RS
933 (or (> (cadr a) (cadr b))
934 (and (= (cadr a) (cadr b))
935 (string-lessp (car a) (car b))))
936 (string-lessp (car a) (car b))))))
09e32aaf 937 (with-output-to-temp-buffer "*Apropos*"
645c4f6a 938 (let ((p apropos-accumulator)
3925e76d 939 (old-buffer (current-buffer))
e517f56d 940 symbol item)
26a4a227
KH
941 (set-buffer standard-output)
942 (apropos-mode)
a2ee7919 943 (if (display-mouse-p)
8d33699b
EZ
944 (insert
945 "If moving the mouse over text changes the text's color, "
946 "you can click\n"
947 "mouse-2 (second button from right) on that text to "
948 "get more information.\n"))
53a7d078 949 (insert "In this buffer, go to the name of the command, or function,"
d94d5b5a
EZ
950 " or variable,\n"
951 (substitute-command-keys
952 "and type \\[apropos-follow] to get full documentation.\n\n"))
caa8e7aa 953 (if text (insert text "\n\n"))
671c04d9 954 (dolist (apropos-item p)
9cc84e31
RS
955 (when (and spacing (not (bobp)))
956 (princ spacing))
671c04d9 957 (setq symbol (car apropos-item))
0820b753
KS
958 ;; Insert dummy score element for backwards compatibility with 21.x
959 ;; apropos-item format.
960 (if (not (numberp (cadr apropos-item)))
961 (setq apropos-item
962 (cons (car apropos-item)
963 (cons nil (cdr apropos-item)))))
e517f56d
MB
964 (insert-text-button (symbol-name symbol)
965 'type 'apropos-symbol
966 ;; Can't use default, since user may have
967 ;; changed the variable!
968 ;; Just say `no' to variables containing faces!
969 'face apropos-symbol-face)
0820b753
KS
970 (if (and (eq apropos-sort-by-scores 'verbose)
971 (cadr apropos-item))
7dbffb1c 972 (insert " (" (number-to-string (cadr apropos-item)) ") "))
98ce2330 973 ;; Calculate key-bindings if we want them.
671c04d9
SM
974 (and do-keys
975 (commandp symbol)
976 (not (eq symbol 'self-insert-command))
977 (indent-to 30 1)
978 (if (let ((keys
979 (with-current-buffer old-buffer
980 (where-is-internal symbol)))
981 filtered)
982 ;; Copy over the list of key sequences,
983 ;; omitting any that contain a buffer or a frame.
984 ;; FIXME: Why omit keys that contain buffers and
985 ;; frames? This looks like a bad workaround rather
986 ;; than a proper fix. Does anybod know what problem
987 ;; this is trying to address? --Stef
988 (dolist (key keys)
989 (let ((i 0)
990 loser)
991 (while (< i (length key))
992 (if (or (framep (aref key i))
993 (bufferp (aref key i)))
994 (setq loser t))
995 (setq i (1+ i)))
996 (or loser
997 (push key filtered))))
998 (setq item filtered))
999 ;; Convert the remaining keys to a string and insert.
1000 (insert
1001 (mapconcat
1002 (lambda (key)
1003 (setq key (condition-case ()
1004 (key-description key)
1005 (error)))
1006 (if apropos-keybinding-face
1007 (put-text-property 0 (length key)
1008 'face apropos-keybinding-face
1009 key))
1010 key)
1011 item ", "))
1012 (insert "M-x ... RET")
1013 (when apropos-keybinding-face
1014 (put-text-property (- (point) 11) (- (point) 8)
1015 'face apropos-keybinding-face)
1016 (put-text-property (- (point) 3) (point)
1017 'face apropos-keybinding-face))))
1018 (terpri)
7dbffb1c 1019 (apropos-print-doc 2
26a4a227 1020 (if (commandp symbol)
894e460c 1021 'apropos-command
26a4a227 1022 (if (apropos-macrop symbol)
894e460c
MB
1023 'apropos-macro
1024 'apropos-function))
0820b753
KS
1025 (not nosubst))
1026 (apropos-print-doc 3 'apropos-variable (not nosubst))
7dbffb1c
KS
1027 (apropos-print-doc 7 'apropos-group t)
1028 (apropos-print-doc 6 'apropos-face t)
1029 (apropos-print-doc 5 'apropos-widget t)
1030 (apropos-print-doc 4 'apropos-plist nil))
a9155e87 1031 (setq buffer-read-only t))))
645c4f6a
KH
1032 (prog1 apropos-accumulator
1033 (setq apropos-accumulator ()))) ; permit gc
1034
645c4f6a 1035(defun apropos-macrop (symbol)
b965722b 1036 "Return t if SYMBOL is a Lisp macro."
645c4f6a
KH
1037 (and (fboundp symbol)
1038 (consp (setq symbol
1039 (symbol-function symbol)))
1040 (or (eq (car symbol) 'macro)
1041 (if (eq (car symbol) 'autoload)
1042 (memq (nth 4 symbol)
1043 '(macro t))))))
3925e76d 1044
645c4f6a 1045
894e460c 1046(defun apropos-print-doc (i type do-keys)
671c04d9
SM
1047 (when (stringp (setq i (nth i apropos-item)))
1048 (insert " ")
1049 (insert-text-button (button-type-get type 'apropos-label)
1050 'type type
1051 ;; Can't use the default button face, since
1052 ;; user may have changed the variable!
1053 ;; Just say `no' to variables containing faces!
1054 'face apropos-label-face
1055 'apropos-symbol (car apropos-item))
1056 (insert ": ")
1057 (insert (if do-keys (substitute-command-keys i) i))
1058 (or (bolp) (terpri))))
3925e76d
KH
1059
1060
e517f56d
MB
1061(defun apropos-follow ()
1062 "Invokes any button at point, otherwise invokes the nearest label button."
3925e76d 1063 (interactive)
e517f56d
MB
1064 (button-activate
1065 (or (apropos-next-label-button (line-beginning-position))
1066 (error "There is nothing to follow here"))))
3925e76d
KH
1067
1068
1069(defun apropos-describe-plist (symbol)
1070 "Display a pretty listing of SYMBOL's plist."
caa8e7aa 1071 (help-setup-xref (list 'apropos-describe-plist symbol) (interactive-p))
c6808785 1072 (with-help-window (help-buffer)
3925e76d
KH
1073 (set-buffer standard-output)
1074 (princ "Symbol ")
1075 (prin1 symbol)
1076 (princ "'s plist is\n (")
645c4f6a 1077 (if apropos-symbol-face
caa8e7aa
SM
1078 (put-text-property (+ (point-min) 7) (- (point) 14)
1079 'face apropos-symbol-face))
3925e76d 1080 (insert (apropos-format-plist symbol "\n "))
c6808785 1081 (princ ")")))
6f8e447f 1082
e517f56d 1083
896546cd
RS
1084(provide 'apropos)
1085
cbee283d 1086;; arch-tag: d56fa2ac-e56b-4ce3-84ff-852f9c0dc66e
c0274f38 1087;;; apropos.el ends here