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