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