* lisp/subr.el (macrop): New function.
[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
KS
343(defun apropos-words-to-regexp (words wild)
344 "Make regexp matching any two of the words in WORDS."
345 (concat "\\("
346 (mapconcat 'identity words "\\|")
80f5d2ef 347 "\\)"
71296446 348 (if (cdr words)
80f5d2ef
AS
349 (concat wild
350 "\\("
7dbffb1c
KS
351 (mapconcat 'identity words "\\|")
352 "\\)")
353 "")))
354
0820b753
KS
355;;;###autoload
356(defun apropos-read-pattern (subject)
357 "Read an apropos pattern, either a word list or a regexp.
358Returns the user pattern, either a list of words which are matched
359literally, or a string which is used as a regexp to search for.
360
361SUBJECT is a string that is included in the prompt to identify what
362kind of objects to search."
363 (let ((pattern
775c916b 364 (read-string (concat "Search for " subject " (word list or regexp): "))))
0820b753
KS
365 (if (string-equal (regexp-quote pattern) pattern)
366 ;; Split into words
922d37d3 367 (split-string pattern "[ \t]+" t)
0820b753
KS
368 pattern)))
369
370(defun apropos-parse-pattern (pattern)
371 "Rewrite a list of words to a regexp matching all permutations.
3fefda51
KS
372If PATTERN is a string, that means it is already a regexp.
373This updates variables `apropos-pattern', `apropos-pattern-quoted',
374`apropos-regexp', `apropos-words', and `apropos-all-words-regexp'."
0820b753
KS
375 (setq apropos-words nil
376 apropos-all-words nil)
377 (if (consp pattern)
7dbffb1c
KS
378 ;; We don't actually make a regexp matching all permutations.
379 ;; Instead, for e.g. "a b c", we make a regexp matching
380 ;; any combination of two or more words like this:
381 ;; (a|b|c).*(a|b|c) which may give some false matches,
382 ;; but as long as it also gives the right ones, that's ok.
0820b753
KS
383 (let ((words pattern))
384 (setq apropos-pattern (mapconcat 'identity pattern " ")
385 apropos-pattern-quoted (regexp-quote apropos-pattern))
7dbffb1c
KS
386 (dolist (word words)
387 (let ((syn apropos-synonyms) (s word) (a word))
388 (while syn
389 (if (member word (car syn))
390 (progn
391 (setq a (mapconcat 'identity (car syn) "\\|"))
392 (if (member word (cdr (car syn)))
393 (setq s a))
394 (setq syn nil))
395 (setq syn (cdr syn))))
396 (setq apropos-words (cons s apropos-words)
397 apropos-all-words (cons a apropos-all-words))))
3fefda51
KS
398 (setq apropos-all-words-regexp
399 (apropos-words-to-regexp apropos-all-words ".+"))
400 (setq apropos-regexp
401 (apropos-words-to-regexp apropos-words ".*?")))
0820b753
KS
402 (setq apropos-pattern-quoted (regexp-quote pattern)
403 apropos-all-words-regexp pattern
3fefda51
KS
404 apropos-pattern pattern
405 apropos-regexp pattern)))
0820b753 406
7dbffb1c
KS
407
408(defun apropos-calc-scores (str words)
409 "Return apropos scores for string STR matching WORDS.
410Value is a list of offsets of the words into the string."
0820b753 411 (let (scores i)
7dbffb1c
KS
412 (if words
413 (dolist (word words scores)
414 (if (setq i (string-match word str))
415 (setq scores (cons i scores))))
416 ;; Return list of start and end position of regexp
347a20b8 417 (and (string-match apropos-pattern str)
0820b753 418 (list (match-beginning 0) (match-end 0))))))
7dbffb1c
KS
419
420(defun apropos-score-str (str)
421 "Return apropos score for string STR."
422 (if str
0820b753
KS
423 (let* ((l (length str))
424 (score (- (/ l 10))))
7dbffb1c 425 (dolist (s (apropos-calc-scores str apropos-all-words) score)
d5857a96 426 (setq score (+ score 1000 (/ (* (- l s) 1000) l)))))
7dbffb1c
KS
427 0))
428
429(defun apropos-score-doc (doc)
430 "Return apropos score for documentation string DOC."
b7a2a696
LK
431 (let ((l (length doc)))
432 (if (> l 0)
06b60517
JB
433 (let ((score 0))
434 (when (string-match apropos-pattern-quoted doc)
0820b753 435 (setq score 10000))
b7a2a696
LK
436 (dolist (s (apropos-calc-scores doc apropos-all-words) score)
437 (setq score (+ score 50 (/ (* (- l s) 50) l)))))
438 0)))
71296446 439
7dbffb1c
KS
440(defun apropos-score-symbol (symbol &optional weight)
441 "Return apropos score for SYMBOL."
442 (setq symbol (symbol-name symbol))
443 (let ((score 0)
0820b753 444 (l (length symbol)))
7dbffb1c
KS
445 (dolist (s (apropos-calc-scores symbol apropos-words) (* score (or weight 3)))
446 (setq score (+ score (- 60 l) (/ (* (- l s) 60) l))))))
447
d2b30292
KS
448(defun apropos-true-hit (str words)
449 "Return t if STR is a genuine hit.
450This may fail if only one of the keywords is matched more than once.
451This requires that at least 2 keywords (unless only one was given)."
452 (or (not str)
453 (not words)
454 (not (cdr words))
455 (> (length (apropos-calc-scores str words)) 1)))
456
457(defun apropos-false-hit-symbol (symbol)
458 "Return t if SYMBOL is not really matched by the current keywords."
459 (not (apropos-true-hit (symbol-name symbol) apropos-words)))
460
461(defun apropos-false-hit-str (str)
462 "Return t if STR is not really matched by the current keywords."
463 (not (apropos-true-hit str apropos-words)))
464
465(defun apropos-true-hit-doc (doc)
466 "Return t if DOC is really matched by the current keywords."
467 (apropos-true-hit doc apropos-all-words))
468
abef340a 469(define-derived-mode apropos-mode special-mode "Apropos"
26a4a227
KH
470 "Major mode for following hyperlinks in output of apropos commands.
471
38ab866c 472\\{apropos-mode-map}")
26a4a227 473
1d69bd9b
SM
474(defvar apropos-multi-type t
475 "If non-nil, this apropos query concerns multiple types.
476This is used to decide whether to print the result's type or not.")
477
f38fd610 478;;;###autoload
acfe10b7
BG
479(defun apropos-user-option (pattern &optional do-all)
480 "Show user options that match PATTERN.
0820b753
KS
481PATTERN can be a word, a list of words (separated by spaces),
482or a regexp (using some regexp special characters). If it is a word,
483search for matches for that word as a substring. If it is a list of words,
484search for matches for any two (or more) of those words.
485
486With \\[universal-argument] prefix, or if `apropos-do-all' is non-nil, also show
acfe10b7 487variables, not just user options."
0820b753
KS
488 (interactive (list (apropos-read-pattern
489 (if (or current-prefix-arg apropos-do-all)
490 "variable" "user option"))
05942d06 491 current-prefix-arg))
0820b753 492 (apropos-command pattern nil
cd00fd36 493 (if (or do-all apropos-do-all)
05942d06
RS
494 #'(lambda (symbol)
495 (and (boundp symbol)
496 (get symbol 'variable-documentation)))
b4d3bc10 497 'custom-variable-p)))
26a4a227 498
acfe10b7
BG
499;;;###autoload
500(defun apropos-variable (pattern &optional do-not-all)
501 "Show variables that match PATTERN.
502When DO-NOT-ALL is not-nil, show user options only, i.e. behave
503like `apropos-user-option'."
504 (interactive (list (apropos-read-pattern
505 (if current-prefix-arg "user option" "variable"))
506 current-prefix-arg))
507 (let ((apropos-do-all (if do-not-all nil t)))
508 (apropos-user-option pattern)))
509
645c4f6a
KH
510;; For auld lang syne:
511;;;###autoload
82e736c1 512(defalias 'command-apropos 'apropos-command)
6f8e447f 513;;;###autoload
0820b753
KS
514(defun apropos-command (pattern &optional do-all var-predicate)
515 "Show commands (interactively callable functions) that match PATTERN.
516PATTERN can be a word, a list of words (separated by spaces),
fe8bc3fa
RS
517or a regexp (using some regexp special characters). If it is a word,
518search for matches for that word as a substring. If it is a list of words,
519search for matches for any two (or more) of those words.
520
0820b753 521With \\[universal-argument] prefix, or if `apropos-do-all' is non-nil, also show
9a909b3c 522noninteractive functions.
05942d06 523
9a909b3c 524If VAR-PREDICATE is non-nil, show only variables, and only those that
0820b753
KS
525satisfy the predicate VAR-PREDICATE.
526
527When called from a Lisp program, a string PATTERN is used as a regexp,
528while a list of strings is used as a word list."
529 (interactive (list (apropos-read-pattern
530 (if (or current-prefix-arg apropos-do-all)
531 "command or function" "command"))
645c4f6a 532 current-prefix-arg))
3fefda51 533 (apropos-parse-pattern pattern)
c851bcec 534 (let ((message
26a4a227 535 (let ((standard-output (get-buffer-create "*Apropos*")))
d5d105e8 536 (help-print-return-message 'identity))))
645c4f6a
KH
537 (or do-all (setq do-all apropos-do-all))
538 (setq apropos-accumulator
0820b753 539 (apropos-internal apropos-regexp
cd00fd36 540 (or var-predicate
554fde6e
SM
541 ;; We used to use `functionp' here, but this
542 ;; rules out macros. `fboundp' rules in
543 ;; keymaps, but it seems harmless.
544 (if do-all 'fboundp 'commandp))))
dea22c45
RS
545 (let ((tem apropos-accumulator))
546 (while tem
d2b30292
KS
547 (if (or (get (car tem) 'apropos-inhibit)
548 (apropos-false-hit-symbol (car tem)))
dea22c45
RS
549 (setq apropos-accumulator (delq (car tem) apropos-accumulator)))
550 (setq tem (cdr tem))))
a9155e87 551 (let ((p apropos-accumulator)
7dbffb1c 552 doc symbol score)
a9155e87
KH
553 (while p
554 (setcar p (list
555 (setq symbol (car p))
7dbffb1c 556 (setq score (apropos-score-symbol symbol))
a9155e87 557 (unless var-predicate
554fde6e 558 (if (fboundp symbol)
9da97cf0
GM
559 (if (setq doc (condition-case nil
560 (documentation symbol t)
561 (error 'error)))
562 ;; Eg alias to undefined function.
563 (if (eq doc 'error)
564 "(documentation error)"
71296446 565 (setq score (+ score (apropos-score-doc doc)))
7dbffb1c 566 (substring doc 0 (string-match "\n" doc)))
a9155e87
KH
567 "(not documented)")))
568 (and var-predicate
569 (funcall var-predicate symbol)
570 (if (setq doc (documentation-property
571 symbol 'variable-documentation t))
7dbffb1c
KS
572 (progn
573 (setq score (+ score (apropos-score-doc doc)))
574 (substring doc 0
575 (string-match "\n" doc)))))))
576 (setcar (cdr (car p)) score)
a9155e87 577 (setq p (cdr p))))
1d69bd9b
SM
578 (and (let ((apropos-multi-type do-all))
579 (apropos-print t nil nil t))
a9155e87 580 message
8a26c165 581 (message "%s" message))))
3925e76d
KH
582
583
3925e76d 584;;;###autoload
914b40da
RS
585(defun apropos-documentation-property (symbol property raw)
586 "Like (documentation-property SYMBOL PROPERTY RAW) but handle errors."
587 (condition-case ()
588 (let ((doc (documentation-property symbol property raw)))
589 (if doc (substring doc 0 (string-match "\n" doc))
590 "(not documented)"))
591 (error "(error retrieving documentation)")))
592
5760219d
JPW
593
594;;;###autoload
0820b753 595(defun apropos (pattern &optional do-all)
2a4ec7e1
RS
596 "Show all meaningful Lisp symbols whose names match PATTERN.
597Symbols are shown if they are defined as functions, variables, or
598faces, or if they have nonempty property lists.
599
0820b753 600PATTERN can be a word, a list of words (separated by spaces),
fe8bc3fa
RS
601or a regexp (using some regexp special characters). If it is a word,
602search for matches for that word as a substring. If it is a list of words,
603search for matches for any two (or more) of those words.
604
543e570f
RS
605With \\[universal-argument] prefix, or if `apropos-do-all' is non-nil,
606consider all symbols (if they match PATTERN).
607
608Returns list of symbols and documentation found."
0820b753
KS
609 (interactive (list (apropos-read-pattern "symbol")
610 current-prefix-arg))
3fefda51 611 (apropos-parse-pattern pattern)
caa8e7aa 612 (apropos-symbols-internal
0820b753 613 (apropos-internal apropos-regexp
543e570f
RS
614 (and (not do-all)
615 (not apropos-do-all)
616 (lambda (symbol)
617 (or (fboundp symbol)
618 (boundp symbol)
619 (facep symbol)
620 (symbol-plist symbol)))))
caa8e7aa
SM
621 (or do-all apropos-do-all)))
622
2e3d43ac
SM
623(defun apropos-library-button (sym)
624 (if (null sym)
625 "<nothing>"
626 (let ((name (copy-sequence (symbol-name sym))))
627 (make-text-button name nil
628 'type 'apropos-library
46c71e23 629 'face 'apropos-symbol
2e3d43ac
SM
630 'apropos-symbol name)
631 name)))
632
633;;;###autoload
634(defun apropos-library (file)
635 "List the variables and functions defined by library FILE.
636FILE should be one of the libraries currently loaded and should
98282f6f
GM
637thus be found in `load-history'. If `apropos-do-all' is non-nil,
638the output includes key-bindings of commands."
2e3d43ac 639 (interactive
47529322
GM
640 (let* ((libs (delq nil (mapcar 'car load-history)))
641 (libs
642 (nconc (delq nil
643 (mapcar
644 (lambda (l)
645 (setq l (file-name-nondirectory l))
646 (while
647 (not (equal (setq l (file-name-sans-extension l))
648 l)))
649 l)
650 libs))
651 libs)))
2e3d43ac
SM
652 (list (completing-read "Describe library: " libs nil t))))
653 (let ((symbols nil)
654 ;; (autoloads nil)
655 (provides nil)
656 (requires nil)
657 (lh-entry (assoc file load-history)))
658 (unless lh-entry
659 ;; `file' may be the "shortname".
660 (let ((lh load-history)
661 (re (concat "\\(?:\\`\\|[\\/]\\)" (regexp-quote file)
662 "\\(\\.\\|\\'\\)")))
663 (while (and lh (null lh-entry))
7aad296a 664 (if (and (caar lh) (string-match re (caar lh)))
2e3d43ac
SM
665 (setq lh-entry (car lh))
666 (setq lh (cdr lh)))))
667 (unless lh-entry (error "Unknown library `%s'" file)))
668 (dolist (x (cdr lh-entry))
f58e0fd5 669 (pcase (car-safe x)
2e3d43ac 670 ;; (autoload (push (cdr x) autoloads))
f58e0fd5
SM
671 (`require (push (cdr x) requires))
672 (`provide (push (cdr x) provides))
673 (_ (push (or (cdr-safe x) x) symbols))))
2e3d43ac
SM
674 (let ((apropos-pattern "")) ;Dummy binding for apropos-symbols-internal.
675 (apropos-symbols-internal
676 symbols apropos-do-all
677 (concat
678 (format "Library `%s' provides: %s\nand requires: %s"
679 file
680 (mapconcat 'apropos-library-button
681 (or provides '(nil)) " and ")
682 (mapconcat 'apropos-library-button
683 (or requires '(nil)) " and ")))))))
684
caa8e7aa
SM
685(defun apropos-symbols-internal (symbols keys &optional text)
686 ;; Filter out entries that are marked as apropos-inhibit.
687 (let ((all nil))
688 (dolist (symbol symbols)
689 (unless (get symbol 'apropos-inhibit)
690 (push symbol all)))
691 (setq symbols all))
692 (let ((apropos-accumulator
693 (mapcar
694 (lambda (symbol)
695 (let (doc properties)
696 (list
697 symbol
698 (apropos-score-symbol symbol)
699 (when (fboundp symbol)
700 (if (setq doc (condition-case nil
701 (documentation symbol t)
702 (void-function
703 "(alias for undefined function)")
704 (error
705 "(can't retrieve function documentation)")))
706 (substring doc 0 (string-match "\n" doc))
707 "(not documented)"))
708 (when (boundp symbol)
709 (apropos-documentation-property
4ef177aa
CY
710 symbol 'variable-documentation t))
711 (when (setq properties (symbol-plist symbol))
712 (setq doc (list (car properties)))
713 (while (setq properties (cdr (cdr properties)))
714 (setq doc (cons (car properties) doc)))
715 (mapconcat #'symbol-name (nreverse doc) " "))
716 (when (get symbol 'widget-type)
717 (apropos-documentation-property
718 symbol 'widget-documentation t))
caa8e7aa 719 (when (facep symbol)
19b72ab7
GM
720 (let ((alias (get symbol 'face-alias)))
721 (if alias
722 (if (facep alias)
723 (format "%slias for the face `%s'."
724 (if (get symbol 'obsolete-face)
725 "Obsolete a"
726 "A")
727 alias)
728 ;; Never happens in practice because fails
729 ;; (facep symbol) test.
730 "(alias for undefined face)")
731 (apropos-documentation-property
732 symbol 'face-documentation t))))
caa8e7aa 733 (when (get symbol 'custom-group)
4ef177aa
CY
734 (apropos-documentation-property
735 symbol 'group-documentation t)))))
caa8e7aa
SM
736 symbols)))
737 (apropos-print keys nil text)))
3925e76d
KH
738
739
6f8e447f 740;;;###autoload
0820b753 741(defun apropos-value (pattern &optional do-all)
2a4ec7e1 742 "Show all symbols whose value's printed representation matches PATTERN.
0820b753 743PATTERN can be a word, a list of words (separated by spaces),
fe8bc3fa
RS
744or a regexp (using some regexp special characters). If it is a word,
745search for matches for that word as a substring. If it is a list of words,
746search for matches for any two (or more) of those words.
747
0820b753 748With \\[universal-argument] prefix, or if `apropos-do-all' is non-nil, also looks
98282f6f
GM
749at function definitions (arguments, documentation and body) and at the
750names and values of properties.
751
645c4f6a 752Returns list of symbols and values found."
0820b753
KS
753 (interactive (list (apropos-read-pattern "value")
754 current-prefix-arg))
3fefda51 755 (apropos-parse-pattern pattern)
645c4f6a
KH
756 (or do-all (setq do-all apropos-do-all))
757 (setq apropos-accumulator ())
758 (let (f v p)
3925e76d
KH
759 (mapatoms
760 (lambda (symbol)
761 (setq f nil v nil p nil)
0820b753
KS
762 (or (memq symbol '(apropos-regexp
763 apropos-pattern apropos-all-words-regexp
7dbffb1c
KS
764 apropos-words apropos-all-words
765 do-all apropos-accumulator
766 symbol f v p))
645c4f6a 767 (setq v (apropos-value-internal 'boundp symbol 'symbol-value)))
3925e76d 768 (if do-all
645c4f6a
KH
769 (setq f (apropos-value-internal 'fboundp symbol 'symbol-function)
770 p (apropos-format-plist symbol "\n " t)))
d2b30292
KS
771 (if (apropos-false-hit-str v)
772 (setq v nil))
773 (if (apropos-false-hit-str f)
774 (setq f nil))
775 (if (apropos-false-hit-str p)
776 (setq p nil))
3925e76d 777 (if (or f v p)
71296446 778 (setq apropos-accumulator (cons (list symbol
7dbffb1c
KS
779 (+ (apropos-score-str f)
780 (apropos-score-str v)
781 (apropos-score-str p))
782 f v p)
645c4f6a 783 apropos-accumulator))))))
1d69bd9b
SM
784 (let ((apropos-multi-type do-all))
785 (apropos-print nil "\n----------------\n")))
3925e76d
KH
786
787
645c4f6a 788;;;###autoload
0820b753 789(defun apropos-documentation (pattern &optional do-all)
2a4ec7e1 790 "Show symbols whose documentation contains matches for PATTERN.
0820b753 791PATTERN can be a word, a list of words (separated by spaces),
fe8bc3fa
RS
792or a regexp (using some regexp special characters). If it is a word,
793search for matches for that word as a substring. If it is a list of words,
794search for matches for any two (or more) of those words.
795
98282f6f
GM
796Note that by default this command only searches in the file specified by
797`internal-doc-file-name'; i.e., the etc/DOC file. With \\[universal-argument] prefix,
798or if `apropos-do-all' is non-nil, it searches all currently defined
799documentation strings.
800
645c4f6a 801Returns list of symbols and documentation found."
98282f6f
GM
802 ;; The doc used to say that DO-ALL includes key-bindings info in the
803 ;; output, but I cannot see that that is true.
0820b753
KS
804 (interactive (list (apropos-read-pattern "documentation")
805 current-prefix-arg))
3fefda51 806 (apropos-parse-pattern pattern)
645c4f6a
KH
807 (or do-all (setq do-all apropos-do-all))
808 (setq apropos-accumulator () apropos-files-scanned ())
809 (let ((standard-input (get-buffer-create " apropos-temp"))
0820b753 810 (apropos-sort-by-scores apropos-documentation-sort-by-scores)
7dbffb1c 811 f v sf sv)
645c4f6a 812 (unwind-protect
7fdbcd83 813 (with-current-buffer standard-input
645c4f6a
KH
814 (apropos-documentation-check-doc-file)
815 (if do-all
816 (mapatoms
817 (lambda (symbol)
818 (setq f (apropos-safe-documentation symbol)
26a4a227
KH
819 v (get symbol 'variable-documentation))
820 (if (integerp v) (setq v))
821 (setq f (apropos-documentation-internal f)
822 v (apropos-documentation-internal v))
7dbffb1c
KS
823 (setq sf (apropos-score-doc f)
824 sv (apropos-score-doc v))
645c4f6a
KH
825 (if (or f v)
826 (if (setq apropos-item
827 (cdr (assq symbol apropos-accumulator)))
828 (progn
829 (if f
7dbffb1c
KS
830 (progn
831 (setcar (nthcdr 1 apropos-item) f)
832 (setcar apropos-item (+ (car apropos-item) sf))))
645c4f6a 833 (if v
7dbffb1c
KS
834 (progn
835 (setcar (nthcdr 2 apropos-item) v)
836 (setcar apropos-item (+ (car apropos-item) sv)))))
645c4f6a 837 (setq apropos-accumulator
71296446 838 (cons (list symbol
7dbffb1c
KS
839 (+ (apropos-score-symbol symbol 2) sf sv)
840 f v)
645c4f6a 841 apropos-accumulator)))))))
0820b753 842 (apropos-print nil "\n----------------\n" nil t))
645c4f6a
KH
843 (kill-buffer standard-input))))
844
845\f
846(defun apropos-value-internal (predicate symbol function)
847 (if (funcall predicate symbol)
848 (progn
849 (setq symbol (prin1-to-string (funcall function symbol)))
0820b753 850 (if (string-match apropos-regexp symbol)
645c4f6a
KH
851 (progn
852 (if apropos-match-face
853 (put-text-property (match-beginning 0) (match-end 0)
854 'face apropos-match-face
855 symbol))
856 symbol)))))
857
858(defun apropos-documentation-internal (doc)
859 (if (consp doc)
860 (apropos-documentation-check-elc-file (car doc))
0820b753
KS
861 (if (and doc
862 (string-match apropos-all-words-regexp doc)
863 (apropos-true-hit-doc doc))
864 (when apropos-match-face
865 (setq doc (substitute-command-keys (copy-sequence doc)))
866 (if (or (string-match apropos-pattern-quoted doc)
867 (string-match apropos-all-words-regexp doc))
868 (put-text-property (match-beginning 0)
869 (match-end 0)
870 'face apropos-match-face doc))
871 doc))))
645c4f6a
KH
872
873(defun apropos-format-plist (pl sep &optional compare)
3925e76d
KH
874 (setq pl (symbol-plist pl))
875 (let (p p-out)
876 (while pl
877 (setq p (format "%s %S" (car pl) (nth 1 pl)))
0820b753 878 (if (or (not compare) (string-match apropos-regexp p))
46c71e23
CY
879 (put-text-property 0 (length (symbol-name (car pl)))
880 'face 'apropos-property p)
3925e76d 881 (setq p nil))
645c4f6a
KH
882 (if p
883 (progn
884 (and compare apropos-match-face
885 (put-text-property (match-beginning 0) (match-end 0)
886 'face apropos-match-face
887 p))
888 (setq p-out (concat p-out (if p-out sep) p))))
3925e76d
KH
889 (setq pl (nthcdr 2 pl)))
890 p-out))
891
6f8e447f 892
0820b753 893;; Finds all documentation related to APROPOS-REGEXP in internal-doc-file-name.
3925e76d 894
645c4f6a 895(defun apropos-documentation-check-doc-file ()
a62f564f 896 (let (type symbol (sepa 2) sepb doc)
26a4a227
KH
897 (insert ?\^_)
898 (backward-char)
645c4f6a 899 (insert-file-contents (concat doc-directory internal-doc-file-name))
26a4a227
KH
900 (forward-char)
901 (while (save-excursion
902 (setq sepb (search-forward "\^_"))
903 (not (eobp)))
904 (beginning-of-line 2)
905 (if (save-restriction
906 (narrow-to-region (point) (1- sepb))
0820b753 907 (re-search-forward apropos-all-words-regexp nil t))
26a4a227 908 (progn
26a4a227 909 (goto-char (1+ sepa))
d2b30292
KS
910 (setq type (if (eq ?F (preceding-char))
911 2 ; function documentation
912 3) ; variable documentation
913 symbol (read)
d2b30292 914 doc (buffer-substring (1+ (point)) (1- sepb)))
4a6c9bec
GM
915 (when (and (apropos-true-hit-doc doc)
916 ;; The DOC file lists all built-in funcs and vars.
917 ;; If any are not currently bound, they can
918 ;; only be platform-specific stuff (eg NS) not
919 ;; in use on the current platform.
920 ;; So we exclude them.
921 (cond ((= 3 type) (boundp symbol))
922 ((= 2 type) (fboundp symbol))))
d2b30292
KS
923 (or (and (setq apropos-item (assq symbol apropos-accumulator))
924 (setcar (cdr apropos-item)
0820b753 925 (apropos-score-doc doc)))
71296446 926 (setq apropos-item (list symbol
d2b30292
KS
927 (+ (apropos-score-symbol symbol 2)
928 (apropos-score-doc doc))
929 nil nil)
930 apropos-accumulator (cons apropos-item
931 apropos-accumulator)))
0820b753
KS
932 (when apropos-match-face
933 (setq doc (substitute-command-keys doc))
934 (if (or (string-match apropos-pattern-quoted doc)
935 (string-match apropos-all-words-regexp doc))
936 (put-text-property (match-beginning 0)
937 (match-end 0)
938 'face apropos-match-face doc)))
d2b30292 939 (setcar (nthcdr type apropos-item) doc))))
26a4a227 940 (setq sepa (goto-char sepb)))))
645c4f6a
KH
941
942(defun apropos-documentation-check-elc-file (file)
943 (if (member file apropos-files-scanned)
944 nil
26a4a227 945 (let (symbol doc beg end this-is-a-variable)
645c4f6a
KH
946 (setq apropos-files-scanned (cons file apropos-files-scanned))
947 (erase-buffer)
948 (insert-file-contents file)
949 (while (search-forward "\n#@" nil t)
950 ;; Read the comment length, and advance over it.
951 (setq end (read)
26a4a227
KH
952 beg (1+ (point))
953 end (+ (point) end -1))
954 (forward-char)
955 (if (save-restriction
956 ;; match ^ and $ relative to doc string
957 (narrow-to-region beg end)
0820b753 958 (re-search-forward apropos-all-words-regexp nil t))
645c4f6a 959 (progn
26a4a227
KH
960 (goto-char (+ end 2))
961 (setq doc (buffer-substring beg end)
962 end (- (match-end 0) beg)
d2b30292
KS
963 beg (- (match-beginning 0) beg))
964 (when (apropos-true-hit-doc doc)
965 (setq this-is-a-variable (looking-at "(def\\(var\\|const\\) ")
966 symbol (progn
967 (skip-chars-forward "(a-z")
968 (forward-char)
969 (read))
970 symbol (if (consp symbol)
971 (nth 1 symbol)
972 symbol))
973 (if (if this-is-a-variable
974 (get symbol 'variable-documentation)
975 (and (fboundp symbol) (apropos-safe-documentation symbol)))
976 (progn
977 (or (and (setq apropos-item (assq symbol apropos-accumulator))
978 (setcar (cdr apropos-item)
979 (+ (cadr apropos-item) (apropos-score-doc doc))))
980 (setq apropos-item (list symbol
981 (+ (apropos-score-symbol symbol 2)
982 (apropos-score-doc doc))
983 nil nil)
984 apropos-accumulator (cons apropos-item
985 apropos-accumulator)))
0820b753
KS
986 (when apropos-match-face
987 (setq doc (substitute-command-keys doc))
988 (if (or (string-match apropos-pattern-quoted doc)
989 (string-match apropos-all-words-regexp doc))
990 (put-text-property (match-beginning 0)
991 (match-end 0)
992 'face apropos-match-face doc)))
d2b30292
KS
993 (setcar (nthcdr (if this-is-a-variable 3 2)
994 apropos-item)
995 doc))))))))))
645c4f6a
KH
996
997
998
999(defun apropos-safe-documentation (function)
7a5348db 1000 "Like `documentation', except it avoids calling `get_doc_string'.
6f8e447f 1001Will return nil instead."
3925e76d 1002 (while (and function (symbolp function))
6f8e447f 1003 (setq function (if (fboundp function)
3925e76d 1004 (symbol-function function))))
d2e1218f
RS
1005 (if (eq (car-safe function) 'macro)
1006 (setq function (cdr function)))
3925e76d 1007 (setq function (if (byte-code-function-p function)
645c4f6a
KH
1008 (if (> (length function) 4)
1009 (aref function 4))
7abaf5cc 1010 (if (autoloadp function)
645c4f6a
KH
1011 (nth 2 function)
1012 (if (eq (car-safe function) 'lambda)
1013 (if (stringp (nth 2 function))
1014 (nth 2 function)
1015 (if (stringp (nth 3 function))
1016 (nth 3 function)))))))
1017 (if (integerp function)
1018 nil
1019 function))
1020
1d69bd9b
SM
1021(defcustom apropos-compact-layout nil
1022 "If non-nil, use a single line per binding."
1023 :type 'boolean)
645c4f6a 1024
0820b753 1025(defun apropos-print (do-keys spacing &optional text nosubst)
a9155e87
KH
1026 "Output result of apropos searching into buffer `*Apropos*'.
1027The value of `apropos-accumulator' is the list of items to output.
71296446 1028Each element should have the format
7dbffb1c 1029 (SYMBOL SCORE FN-DOC VAR-DOC [PLIST-DOC WIDGET-DOC FACE-DOC GROUP-DOC]).
a9155e87
KH
1030The return value is the list that was in `apropos-accumulator', sorted
1031alphabetically by symbol name; but this function also sets
9cc84e31
RS
1032`apropos-accumulator' to nil before returning.
1033
caa8e7aa
SM
1034If SPACING is non-nil, it should be a string; separate items with that string.
1035If non-nil TEXT is a string that will be printed as a heading."
645c4f6a 1036 (if (null apropos-accumulator)
0820b753 1037 (message "No apropos matches for `%s'" apropos-pattern)
645c4f6a 1038 (setq apropos-accumulator
30aab741
RS
1039 (sort apropos-accumulator
1040 (lambda (a b)
1041 ;; Don't sort by score if user can't see the score.
1042 ;; It would be confusing. -- rms.
ab765ff7 1043 (if apropos-sort-by-scores
30aab741
RS
1044 (or (> (cadr a) (cadr b))
1045 (and (= (cadr a) (cadr b))
1046 (string-lessp (car a) (car b))))
1047 (string-lessp (car a) (car b))))))
09e32aaf 1048 (with-output-to-temp-buffer "*Apropos*"
645c4f6a 1049 (let ((p apropos-accumulator)
3925e76d 1050 (old-buffer (current-buffer))
abd20d91 1051 (inhibit-read-only t)
e517f56d 1052 symbol item)
26a4a227 1053 (set-buffer standard-output)
abd20d91 1054 (apropos-mode)
4ef177aa
CY
1055 (insert (substitute-command-keys "Type \\[apropos-follow] on ")
1056 (if apropos-multi-type "a type label" "an entry")
1057 " to view its full documentation.\n\n")
caa8e7aa 1058 (if text (insert text "\n\n"))
671c04d9 1059 (dolist (apropos-item p)
9cc84e31
RS
1060 (when (and spacing (not (bobp)))
1061 (princ spacing))
671c04d9 1062 (setq symbol (car apropos-item))
0820b753
KS
1063 ;; Insert dummy score element for backwards compatibility with 21.x
1064 ;; apropos-item format.
1065 (if (not (numberp (cadr apropos-item)))
1066 (setq apropos-item
1067 (cons (car apropos-item)
1068 (cons nil (cdr apropos-item)))))
e517f56d
MB
1069 (insert-text-button (symbol-name symbol)
1070 'type 'apropos-symbol
60f4c0c8 1071 'skip apropos-multi-type
46c71e23 1072 'face 'apropos-symbol)
0820b753
KS
1073 (if (and (eq apropos-sort-by-scores 'verbose)
1074 (cadr apropos-item))
7dbffb1c 1075 (insert " (" (number-to-string (cadr apropos-item)) ") "))
98ce2330 1076 ;; Calculate key-bindings if we want them.
1d69bd9b
SM
1077 (unless apropos-compact-layout
1078 (and do-keys
1079 (commandp symbol)
1080 (not (eq symbol 'self-insert-command))
1081 (indent-to 30 1)
1082 (if (let ((keys
1083 (with-current-buffer old-buffer
1084 (where-is-internal symbol)))
1085 filtered)
1086 ;; Copy over the list of key sequences,
1087 ;; omitting any that contain a buffer or a frame.
1088 ;; FIXME: Why omit keys that contain buffers and
1089 ;; frames? This looks like a bad workaround rather
91af3942 1090 ;; than a proper fix. Does anybody know what problem
1d69bd9b
SM
1091 ;; this is trying to address? --Stef
1092 (dolist (key keys)
1093 (let ((i 0)
1094 loser)
1095 (while (< i (length key))
1096 (if (or (framep (aref key i))
1097 (bufferp (aref key i)))
1098 (setq loser t))
1099 (setq i (1+ i)))
1100 (or loser
1101 (push key filtered))))
1102 (setq item filtered))
1103 ;; Convert the remaining keys to a string and insert.
1104 (insert
1105 (mapconcat
1106 (lambda (key)
1107 (setq key (condition-case ()
1108 (key-description key)
1109 (error)))
46c71e23
CY
1110 (put-text-property 0 (length key)
1111 'face 'apropos-keybinding
1112 key)
1d69bd9b
SM
1113 key)
1114 item ", "))
1115 (insert "M-x ... RET")
46c71e23
CY
1116 (put-text-property (- (point) 11) (- (point) 8)
1117 'face 'apropos-keybinding)
1118 (put-text-property (- (point) 3) (point)
1119 'face 'apropos-keybinding)))
1d69bd9b 1120 (terpri))
7dbffb1c 1121 (apropos-print-doc 2
26a4a227 1122 (if (commandp symbol)
894e460c 1123 'apropos-command
671d5c16 1124 (if (macrop symbol)
894e460c
MB
1125 'apropos-macro
1126 'apropos-function))
0820b753 1127 (not nosubst))
acfe10b7
BG
1128 (apropos-print-doc 3
1129 (if (custom-variable-p symbol)
1130 'apropos-user-option
1131 'apropos-variable)
1132 (not nosubst))
7dbffb1c
KS
1133 (apropos-print-doc 7 'apropos-group t)
1134 (apropos-print-doc 6 'apropos-face t)
1135 (apropos-print-doc 5 'apropos-widget t)
1136 (apropos-print-doc 4 'apropos-plist nil))
1d69bd9b 1137 (set (make-local-variable 'truncate-partial-width-windows) t)
abd20d91 1138 (set (make-local-variable 'truncate-lines) t))))
645c4f6a
KH
1139 (prog1 apropos-accumulator
1140 (setq apropos-accumulator ()))) ; permit gc
1141
894e460c 1142(defun apropos-print-doc (i type do-keys)
4ef177aa
CY
1143 (let ((doc (nth i apropos-item)))
1144 (when (stringp doc)
1145 (if apropos-compact-layout
1146 (insert (propertize "\t" 'display '(space :align-to 32)) " ")
1147 (insert " "))
1148 (if apropos-multi-type
1149 (let ((button-face (button-type-get type 'face)))
1150 (unless (consp button-face)
1151 (setq button-face (list button-face)))
1152 (insert-text-button
1153 (if apropos-compact-layout
1154 (format "<%s>" (button-type-get type 'apropos-short-label))
1155 (button-type-get type 'apropos-label))
1156 'type type
4ef177aa
CY
1157 'apropos-symbol (car apropos-item))
1158 (insert (if apropos-compact-layout " " ": ")))
1159
1160 ;; If the query is only for a single type, there's no point
1161 ;; writing it over and over again. Insert a blank button, and
1162 ;; put the 'apropos-label property there (needed by
1163 ;; apropos-symbol-button-display-help).
1164 (insert-text-button
60f4c0c8 1165 " " 'type type 'skip t
4ef177aa
CY
1166 'face 'default 'apropos-symbol (car apropos-item)))
1167
1168 (let ((opoint (point))
1169 (ocol (current-column)))
1170 (cond ((equal doc "")
1171 (setq doc "(not documented)"))
1172 (do-keys
1173 (setq doc (substitute-command-keys doc))))
1174 (insert doc)
1175 (if (equal doc "(not documented)")
1176 (put-text-property opoint (point) 'font-lock-face 'shadow))
1177 ;; The labeling buttons might make the line too long, so fill it if
1178 ;; necessary.
275b59b0
NF
1179 (let ((fill-column (+ 5 (if (integerp emacs-lisp-docstring-fill-column)
1180 emacs-lisp-docstring-fill-column
1181 fill-column)))
4ef177aa
CY
1182 (fill-prefix (make-string ocol ?\s)))
1183 (fill-region opoint (point) nil t)))
1184 (or (bolp) (terpri)))))
3925e76d 1185
e517f56d
MB
1186(defun apropos-follow ()
1187 "Invokes any button at point, otherwise invokes the nearest label button."
3925e76d 1188 (interactive)
e517f56d
MB
1189 (button-activate
1190 (or (apropos-next-label-button (line-beginning-position))
1191 (error "There is nothing to follow here"))))
3925e76d
KH
1192
1193
1194(defun apropos-describe-plist (symbol)
1195 "Display a pretty listing of SYMBOL's plist."
32226619
JB
1196 (help-setup-xref (list 'apropos-describe-plist symbol)
1197 (called-interactively-p 'interactive))
c6808785 1198 (with-help-window (help-buffer)
3925e76d
KH
1199 (set-buffer standard-output)
1200 (princ "Symbol ")
1201 (prin1 symbol)
1202 (princ "'s plist is\n (")
46c71e23
CY
1203 (put-text-property (+ (point-min) 7) (- (point) 14)
1204 'face 'apropos-symbol)
3925e76d 1205 (insert (apropos-format-plist symbol "\n "))
c6808785 1206 (princ ")")))
6f8e447f 1207
e517f56d 1208
896546cd
RS
1209(provide 'apropos)
1210
c0274f38 1211;;; apropos.el ends here