]> code.delx.au - gnu-emacs/blobdiff - lisp/finder.el
move notifications.el to net/
[gnu-emacs] / lisp / finder.el
index 2ff4dc9c30bb4fa884e719985f20ca4d8fb42e00..2de8e2e01a725c60e16626557534cff728fa112a 100644 (file)
@@ -1,7 +1,7 @@
 ;;; 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")
@@ -138,14 +146,14 @@ finder-inf\\|esh-groups\\|subdirs\\)\\.el$\\)"
 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))
@@ -159,7 +167,7 @@ no arguments compiles from `load-path'."
                 (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)
@@ -167,20 +175,18 @@ no arguments compiles from `load-path'."
                            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."
@@ -201,6 +207,8 @@ no arguments compiles from `load-path'."
   "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
@@ -215,6 +223,29 @@ no arguments compiles from `load-path'."
      '(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."
@@ -252,11 +283,10 @@ no arguments compiles from `load-path'."
     (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)
@@ -265,6 +295,15 @@ no arguments compiles from `load-path'."
     (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.
@@ -279,7 +318,6 @@ FILE should be in a form suitable for passing to `locate-library'."
     (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)
@@ -293,6 +331,13 @@ FILE should be in a form suitable for passing to `locate-library'."
     (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)
@@ -319,8 +364,7 @@ FILE should be in a form suitable for passing to `locate-library'."
 (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)))