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