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