jbw%bucsf.BU.EDU@BU-CS.BU.EDU (Joe Wells) (03/24/89)
I got annoyed today at how long M-x apropos took on the simple request "window". It took MINUTES! So I rewrote it. This version is anywhere from 2 to 30 times faster than regular apropos, even though it is in lisp and the original was in C. The original version of apropos traversed the entire key-binding tree for each symbol that your request found. This takes a LONG TIME. My version traverses the key-binding tree just once, for all of the symbols for which it wants to find key bindings. It should complete any request in a few seconds. Have fun, and of course, send bugs to me ... -- Joe Wells INTERNET: jbw%bucsf.bu.edu@bu-it.bu.edu IP: [128.197.10.201] UUCP: ...!harvard!bu-cs!bucsf!jbw ---------------------------------------------------------------------- ;; Faster apropos command. ;; Copyright (C) 1989 Free Software Foundation, Inc. ;; This file is not officially part of GNU Emacs, but is being donated ;; to the Free Software Foundation. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY. No author or distributor ;; accepts responsibility to anyone for the consequences of using it ;; or for whether it serves any particular purpose or works at all, ;; unless he says so in writing. Refer to the GNU Emacs General Public ;; License for full details. ;; Everyone is granted permission to copy, modify and redistribute ;; GNU Emacs, but only under the conditions described in the ;; GNU Emacs General Public License. A copy of this license is ;; supposed to have been given to you along with GNU Emacs so you ;; can know your rights and responsibilities. It should be in a ;; file named COPYING. Among other things, the copyright notice ;; and this notice must be preserved on all copies. ;; Author: Joe Wells ;; jbw%bucsf.bu.edu@bu-it.bu.edu (school year) ;; joew%uswest@boulder.colorado.edu (summer) ;; The ideas for this package were derived from the C code in ;; src/keymap.c and elsewhere. The functions in this file should ;; always be byte-compiled for speed. Someone should rewrite this in ;; C (as part of src/keymap.c) for speed. (defun fast-apropos (string &optional pred noprint) "Show all symbols whose names contain match for REGEXP. If optional arg PRED is non-nil, (funcall PRED SYM) is done for each symbol and a symbol is mentioned if that returns non-nil. Returns list of symbols found; if third arg NOPRINT is non-nil, does not display them, just returns the list." (interactive "sFast-Apropos: ") (let ((list (apropos string pred t))) (or noprint (with-output-to-temp-buffer "*Help*" (fast-apropos1 string pred list))) list)) (defun fast-apropos1 (string pred list) "Helping function for fast-apropos. Equivalent to apropos1 in src/keymap.c except that it's a lot faster and it's written in lisp. Using REGEXP and PRED, displays LIST of symbols in the buffer pointed to by standard-output, along with neat info like function and variable documentation and key bindings. Should only be called by fast-apropos." (let ((keys (match-all-keys string pred)) ;; (current-local-map (current-local-map)) symbol tem) (save-excursion (set-buffer standard-output) (while (consp list) (setq symbol (car list) list (cdr list)) (prin1 symbol nil) (cond ((commandp symbol) (indent-to 30 1) ;; (setq tem (where-is-internal symbol current-local-map nil)) (setq tem (assq symbol keys)) (if tem (princ (mapconcat 'key-description ;; tem (cdr tem) ", ") nil) (insert "(not bound to any keys)")))) (terpri nil) (cond ((fboundp symbol) (setq tem (documentation symbol)) (if (stringp tem) (insert " Function: " (substring tem 0 (string-match "\n" tem)) "\n")))) (setq tem (documentation-property symbol 'variable-documentation)) (if (stringp tem) (insert " Variable: " (substring tem 0 (string-match "\n" tem)) "\n"))))) nil) (defun match-all-keys (regexp pred) "Find key bindings for symbols matching REGEXP and passing PRED. If PRED is non-nil, (funcall PRED SYMBOL) is done before a symbol is included in the list. The return value is in the form ((SYMBOL KEYS ...) ...)." (let* ((current-local-map (current-local-map)) (maps (nconc (accessible-keymaps current-local-map) (accessible-keymaps (current-global-map)))) map ;map we are now inspecting sequence ;key sequence to reach map i ;index into vector map found ;key bindings found so far command ;what is bound to current keys key ;last key to reach command local ;local binding for sequence + key item) ;key binding in found list ;; examine all reachable keymaps (while (consp maps) (setq map (cdr (car maps)) sequence (car (car maps)) ;keys to reach this map maps (cdr maps)) (cond ((consp map) (setq map (cdr map)))) ;skip keymap symbol (setq i 0) (while (and map (< i 128)) ;vector keymaps have 128 entries (cond ((consp map) (setq command (cdr (car map)) key (car (car map)) map (cdr map))) ((vectorp map) (setq command (aref map i) key i i (1+ i)))) ;; if is a symbol, and matches regexp, and passes pred, and is ;; not shadowed by a different local binding, record it (and (symbolp command) (string-match regexp (symbol-name command)) (if pred (funcall pred symbol) t) (setq key (concat sequence (char-to-string key))) ;; checking if shadowed by local binding ;; either no local binding, or runs off the binding tree ;; (number), or is the same binding (or (not (setq local (lookup-key current-local-map key))) (numberp local) (eq command local)) (setq key (cons key nil)) ;; if this command is already in found, just add the key ;; sequence, else add the (command keys) item (if (setq item (assq command found)) (nconc item key) (setq found (cons (cons command key) found)))))) found))