;;; finder.el --- topic & keyword-based code finder
;; Copyright (C) 1992, 1997, 1998, 1999, 2001, 2002, 2003, 2004, 2005,
-;; 2006, 2007, 2008 Free Software Foundation, Inc.
+;; 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
;; Author: Eric S. Raymond <esr@snark.thyrsus.com>
;; Created: 16 Jun 1992
;; This mode uses the Keywords library header to provide code-finding
;; services by keyword.
-;;
-;; Things to do:
-;; 1. Support multiple keywords per search. This could be extremely hairy;
-;; there doesn't seem to be any way to get completing-read to exit on
-;; an EOL with no substring pending, which is what we'd want to end the loop.
-;; 2. Search by string in synopsis line?
-;; 3. Function to check finder-package-info for unknown keywords.
;;; Code:
(tex . "supporting code for the TeX formatter")
(tools . "programming tools")
(unix . "front-ends/assistants for, or emulators of, UNIX-like features")
-;; Not a custom group and not currently useful.
-;; (vms . "support code for vms")
(wp . "word processing")
))
(defvar finder-mode-map
- (let ((map (make-sparse-keymap)))
+ (let ((map (make-sparse-keymap))
+ (menu-map (make-sparse-keymap "Finder")))
(define-key map " " 'finder-select)
(define-key map "f" 'finder-select)
(define-key map [follow-link] 'mouse-face)
(define-key map "p" 'previous-line)
(define-key map "q" 'finder-exit)
(define-key map "d" 'finder-list-keywords)
+
+ (define-key map [menu-bar finder-mode]
+ (cons "Finder" menu-map))
+ (define-key menu-map [finder-exit]
+ '(menu-item "Quit" finder-exit
+ :help "Exit Finder mode"))
+ (define-key menu-map [finder-summary]
+ '(menu-item "Summary" finder-summary
+ :help "Summary item on current line in a finder buffer"))
+ (define-key menu-map [finder-list-keywords]
+ '(menu-item "List keywords" finder-list-keywords
+ :help "Display descriptions of the keywords in the Finder buffer"))
+ (define-key menu-map [finder-select]
+ '(menu-item "Select" finder-select
+ :help "Select item on current line in a finder buffer"))
map))
(defvar finder-mode-syntax-table
"Syntax table used while in `finder-mode'.")
(defvar finder-font-lock-keywords
- '(("`\\([^']+\\)'" 1 font-lock-constant-face prepend))
+ '(("`\\([^'`]+\\)'" 1 font-lock-constant-face prepend))
"Font-lock keywords for Finder mode.")
(defvar finder-headmark nil
;; useful, and because in parallel builds of Emacs they may get
;; modified while we are trying to read them.
;; http://lists.gnu.org/archive/html/emacs-pretest-bug/2007-01/msg00469.html
-(defvar finder-no-scan-regexp "\\(^\\.#\\|\\(loaddefs\\|cus-load\\|\
-finder-inf\\|esh-groups\\|subdirs\\)\\.el$\\)"
+;; ldefs-boot is not auto-generated, but has nothing useful.
+(defvar finder-no-scan-regexp "\\(^\\.#\\|\\(loaddefs\\|ldefs-boot\\|\
+cus-load\\|finder-inf\\|esh-groups\\|subdirs\\)\\.el$\\)"
"Regexp matching file names not to scan for keywords.")
(autoload 'autoload-rubric "autoload")
Optional arguments DIRS are a list of Emacs Lisp directories to compile from;
no arguments compiles from `load-path'."
(save-excursion
- (let (processed summary keystart keywords)
- (find-file generated-finder-keywords-file)
- (setq buffer-undo-list t)
- (erase-buffer)
- (insert (autoload-rubric generated-finder-keywords-file
- "keyword-to-package mapping"))
- (search-backward "\f")
- (insert "(setq finder-package-info '(\n")
+ (find-file generated-finder-keywords-file)
+ (setq buffer-undo-list t)
+ (erase-buffer)
+ (insert (autoload-rubric generated-finder-keywords-file
+ "keyword-to-package mapping" t))
+ (search-backward "\f")
+ (insert "(setq finder-package-info '(\n")
+ (let (processed summary keywords)
(mapc
(lambda (d)
(when (file-exists-p (directory-file-name d))
(with-temp-buffer
(insert-file-contents (expand-file-name f d))
(setq summary (lm-synopsis)
- keywords (lm-keywords)))
+ keywords (lm-keywords-list)))
(insert
(format " (\"%s\"\n "
(if (string-match "\\.\\(gz\\|Z\\)$" f)
f)))
(prin1 summary (current-buffer))
(insert "\n ")
- (setq keystart (point))
- (insert (if keywords (format "(%s)" keywords) "nil")
- ")\n")
- (subst-char-in-region keystart (point) ?, ? )))
+ (prin1 (mapcar 'intern keywords) (current-buffer))
+ (insert ")\n")))
(directory-files d nil
;; Allow compressed files also. FIXME:
;; generalize this, especially for
;; MS-DOG-type filenames.
"^[^=].*\\.el\\(\\.\\(gz\\|Z\\)\\)?$"
))))
- (or dirs load-path))
- (insert " ))\n")
- (eval-buffer) ; so we get the new keyword list immediately
- (basic-save-buffer))))
+ (or dirs load-path)))
+ (insert " ))\n")
+ (eval-buffer) ; so we get the new keyword list immediately
+ (basic-save-buffer)))
(defun finder-compile-keywords-make-dist ()
"Regenerate `finder-inf.el' for the Emacs distribution."
"Put `mouse-face' and `help-echo' properties on the previous line."
(save-excursion
(forward-line -1)
+ ;; If finder-insert-at-column moved us to a new line, go back one more.
+ (if (looking-at "[ \t]") (forward-line -1))
(unless finder-help-echo
(setq finder-help-echo
(let* ((keys1 (where-is-internal 'finder-select
'(mouse-face highlight
help-echo finder-help-echo))))
+(defun finder-unknown-keywords ()
+ "Return an alist of unknown keywords and number of their occurences.
+Unknown are keywords that are present in `finder-package-info'
+but absent in `finder-known-keywords'."
+ (let ((unknown-keywords-hash (make-hash-table)))
+ ;; Prepare a hash where key is a keyword
+ ;; and value is the number of keyword occurences.
+ (mapc (lambda (package)
+ (mapc (lambda (keyword)
+ (unless (assq keyword finder-known-keywords)
+ (puthash keyword
+ (1+ (gethash keyword unknown-keywords-hash 0))
+ unknown-keywords-hash)))
+ (nth 2 package)))
+ finder-package-info)
+ ;; Make an alist from the hash and sort by the keyword name.
+ (sort (let (unknown-keywords-list)
+ (maphash (lambda (key value)
+ (push (cons key value) unknown-keywords-list))
+ unknown-keywords-hash)
+ unknown-keywords-list)
+ (lambda (a b) (string< (car a) (car b))))))
+
;;;###autoload
(defun finder-list-keywords ()
"Display descriptions of the keywords in the Finder buffer."
(setq finder-headmark (point))
(mapc
(lambda (x)
- (if (memq id (car (cdr (cdr x))))
- (progn
- (insert (car x))
- (finder-insert-at-column 16 (concat (nth 1 x) "\n"))
- (finder-mouse-face-on-line))))
+ (when (memq id (cadr (cdr x)))
+ (insert (car x))
+ (finder-insert-at-column 16 (concat (cadr x) "\n"))
+ (finder-mouse-face-on-line)))
finder-package-info)
(goto-char (point-min))
(forward-line)
(shrink-window-if-larger-than-buffer)
(finder-summary)))
+(define-button-type 'finder-xref 'action #'finder-goto-xref)
+
+(defun finder-goto-xref (button)
+ "Jump to a lisp file for the BUTTON at point."
+ (let* ((file (button-get button 'xref))
+ (lib (locate-library file)))
+ (if lib (finder-commentary lib)
+ (message "Unable to locate `%s'" file))))
+
;;;###autoload
(defun finder-commentary (file)
"Display FILE's commentary section.
(or str (error "Can't find any Commentary section"))
;; This used to use *Finder* but that would clobber the
;; directory of categories.
- (delete-other-windows)
(pop-to-buffer "*Finder-package*")
(setq buffer-read-only nil
buffer-undo-list t)
(while (re-search-forward "^;+ ?" nil t)
(replace-match "" nil nil))
(goto-char (point-min))
+ (while (re-search-forward "\\<\\([-[:alnum:]]+\\.el\\)\\>" nil t)
+ (if (locate-library (match-string 1))
+ (make-text-button (match-beginning 1) (match-end 1)
+ 'xref (match-string-no-properties 1)
+ 'help-echo "Read this file's commentary"
+ :type 'finder-xref)))
+ (goto-char (point-min))
(setq buffer-read-only t)
(set-buffer-modified-p nil)
(shrink-window-if-larger-than-buffer)
(defun finder-mouse-select (event)
"Select item in a finder buffer with the mouse."
(interactive "e")
- (save-excursion
- (set-buffer (window-buffer (posn-window (event-start event))))
+ (with-current-buffer (window-buffer (posn-window (event-start event)))
(goto-char (posn-point (event-start event)))
(finder-select)))