update.
[chise/xemacs-chise.git.1] / lisp / finder.el
1 ;;; finder.el --- topic & keyword-based code finder
2
3 ;; Copyright (C) 1992 Free Software Foundation, Inc.
4
5 ;; Author: Eric S. Raymond <esr@snark.thyrsus.com>
6 ;; Created: 16 Jun 1992
7 ;; Version: 1.0
8 ;; Keywords: help
9 ;; X-Modified-by: Bob Weiner <weiner@mot.com>, 4/18/95, to include Lisp
10 ;;      library directory names in finder-program-info, for fast display of
11 ;;      Lisp libraries and associated commentaries.  Added {v}, finder-view,
12 ;;      and {e}, finder-edit commands for displaying libraries.
13 ;;      
14 ;;      Added user variable, 'finder-abbreviate-directory-list', used to
15 ;;      abbreviate directories before they are saved to finder-program-info.
16 ;;      Such relative directories can be portable from one Emacs installation
17 ;;      to another.  Default value is based upon the value of Emacs'
18 ;;      data-directory variable.
19
20 ;; This file is part of XEmacs.
21
22 ;; XEmacs is free software; you can redistribute it and/or modify it
23 ;; under the terms of the GNU General Public License as published by
24 ;; the Free Software Foundation; either version 2, or (at your option)
25 ;; any later version.
26
27 ;; XEmacs is distributed in the hope that it will be useful, but
28 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
29 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
30 ;; General Public License for more details.
31
32 ;; You should have received a copy of the GNU General Public License
33 ;; along with XEmacs; see the file COPYING.  If not, write to the Free
34 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
35 ;; 02111-1307, USA.
36
37 ;;; Synched up with: FSF 19.34.
38
39 ;;; Commentary:
40
41 ;; This mode uses the Keywords library header to provide code-finding
42 ;; services by keyword.
43 ;;
44 ;; Things to do:
45 ;;    1. Support multiple keywords per search.  This could be extremely hairy;
46 ;; there doesn't seem to be any way to get completing-read to exit on
47 ;; an EOL with no substring pending, which is what we'd want to end the loop.
48 ;;    2. Search by string in synopsis line?
49 ;;    3. Function to check finder-package-info for unknown keywords.
50
51 ;;; Code:
52
53 (require 'lisp-mnt)
54 (condition-case nil
55     (require 'finder-inf)
56   (t nil))
57 ;; XEmacs addition
58 (require 'picture)
59 (require 'mode-motion)
60
61 (defvar finder-emacs-root-directory
62   (file-name-directory (directory-file-name data-directory))
63   "Root directory of current emacs tree.")
64
65 (defvar finder-abbreviate-directory-list
66   (list finder-emacs-root-directory)
67   "*List of directory roots to remove from finder-package-info directory entries.
68 The first element in the list is used when expanding relative package
69 directories to view or extract information from package source code.")
70
71 (defvar finder-file-regexp "\\.el$"
72   "Regexp which matches file names but not Emacs Lisp finder keywords.")
73
74 ;; Local variable in finder buffer.
75 (defvar finder-headmark)
76
77 (defvar finder-known-keywords
78   `(
79     (abbrev     . "abbreviation handling, typing shortcuts, macros")
80     (bib        . "code related to the `bib' bibliography processor")
81     (build      . "code used to build XEmacs")
82     (c          . "C, C++, and Objective-C language support")
83     (calendar   . "calendar and time management support")
84     (comm       . "communications, networking, remote access to files")
85     (content    . "contains content (menu/dialog box descs, text, images, &c)")
86     (data       . "support for editing files of data")
87     (docs       . "support for XEmacs documentation")
88     (dumped     . "files preloaded into XEmacs")
89     (emulations . "emulations of other editors")
90     (extensions . "Emacs Lisp language extensions")
91     (faces      . "support for multiple fonts")
92     (frames     . "support for XEmacs frames and window systems")
93     (games      . "games, jokes and amusements")
94     (gui        . "support for menubars, dialog boxes, and other GUI features")
95     (hardware   . "support for interfacing with exotic hardware")
96     (help       . "support for on-line help systems")
97     (hypermedia . "support for links between text or other media types")
98     (i18n       . "internationalization and alternate character-set support")
99     (internal   . "code implementing core functionality in XEmacs")
100     (languages  . "specialized modes for editing programming languages")
101     (lisp       . "Lisp support, including Emacs Lisp")
102     (local      . "code local to your site")
103     (mail       . "modes for electronic-mail handling")
104     (maint      . "maintenance aids for the Emacs development group")
105     (matching   . "various sorts of searching and matching")
106     (mouse      . "mouse support")
107     (mswin      . "support for anything running on MS Windows")
108     ,(when (featurep 'mule)
109        (cons 'mule "multi-language extensions"))
110     (news       . "support for netnews reading and posting")
111     (oop        . "support for object-oriented programming")
112     (outlines   . "support for hierarchical outlining")
113     (processes  . "process, subshell, compilation, and job control support")
114     (services   . "provides services for use by other programs (cf `user')")
115     (terminals  . "support for terminal types")
116     (tex        . "code related to the TeX formatter")
117     (tools      . "programming tools")
118     (unix       . "front-ends/assistants for, or emulators of, UNIX features")
119     (user       . "program interacts directly with the user (cf `services'")
120     (vms        . "support code for vms")
121     (wp         . "word processing")
122     (www        . "support for the Web (WWW, the World Wide Web)")
123     ))
124
125 (defvar finder-mode-map nil)
126 (or finder-mode-map
127     (let ((map (make-sparse-keymap)))
128       (define-key map " "       'finder-select)
129       (define-key map "f"       'finder-select)
130       (define-key map "\C-m"    'finder-select)
131       ;; XEmacs changes
132       (define-key map "e"       'finder-edit)
133       (define-key map "v"       'finder-view)
134       (define-key map "?"       'finder-summary)
135       (define-key map "q"       'finder-exit)
136       (define-key map "d"       'finder-list-keywords)
137       ;; XEmacs change
138       (define-key map [button2] 'finder-mouse-select)
139       (setq finder-mode-map map)))
140
141
142 ;;; Code for regenerating the keyword list.
143
144 (defvar finder-package-info nil
145   "Assoc list mapping file names to description & keyword lists.")
146
147 (defvar finder-compile-keywords-quiet nil
148   "If non-nil finder-compile-keywords will not print any messages.")
149
150 (defun finder-compile-keywords (&rest dirs)
151   "Regenerate the keywords association list into the file `finder-inf.el'.
152 Optional arguments are a list of Emacs Lisp directories to compile from; no
153 arguments compiles from `load-path'."
154   (save-excursion
155     ;; XEmacs change
156     (find-file "finder-inf.el")
157     (let ((processed nil)
158           (directory-abbrev-alist
159            (append
160            (mapcar (function (lambda (dir)
161                                (cons (concat "^" (regexp-quote dir))
162                                      "")))
163                     finder-abbreviate-directory-list)
164             directory-abbrev-alist))
165           (using-load-path))
166       (or dirs (setq dirs load-path))
167       (setq using-load-path (equal dirs load-path))
168       (erase-buffer)
169       (insert ";;; finder-inf.el --- keyword-to-package mapping\n")
170       (insert ";; Keywords: help\n")
171       (insert ";;; Commentary:\n")
172       (insert ";; Don't edit this file.  It's generated by finder.el\n\n")
173       (insert ";;; Code:\n")
174       (insert "\n(defconst finder-package-info '(\n")
175       (mapcar
176        (lambda (d)
177          (mapcar
178           (lambda (f) 
179             (when (and (not (member f processed)) (file-readable-p f))
180               (let (summary keystart keywords)
181                 (setq processed (cons f processed))
182                 (if (not finder-compile-keywords-quiet)
183                     (message "Processing %s ..." f))
184                 (save-excursion
185                   (set-buffer (get-buffer-create "*finder-scratch*"))
186                   (buffer-disable-undo (current-buffer))
187                   (erase-buffer)
188                   (insert-file-contents (expand-file-name f d))
189                   (condition-case err
190                       (setq summary  (lm-synopsis)
191                             keywords (lm-keywords))
192                     (t (message "finder: error processing %s %S" f err))))
193                 (when summary
194                   (insert (format "    (\"%s\"\n        " f))
195                   (prin1 summary (current-buffer))
196                   (insert "\n        ")
197                   (setq keystart (point))
198                   (insert (if keywords (format "(%s)" keywords) "nil"))
199                   (subst-char-in-region keystart (point) ?, ? )
200                   (insert "\n        ")
201                   (prin1 (abbreviate-file-name d) (current-buffer))
202                   (insert ")\n")))))
203           ;;
204           ;; Skip null, non-existent or relative pathnames, e.g. "./", if
205           ;; using load-path, so that they do not interfere with a scan of
206           ;; library directories only.
207           (if (and using-load-path
208                    (not (and d (file-name-absolute-p d) (file-exists-p d))))
209               nil
210             (setq d (file-name-as-directory (or d ".")))
211             (directory-files d nil "^[^=].*\\.el$"))))
212        dirs)
213       (insert "))\n\n(provide 'finder-inf)\n\n;;; finder-inf.el ends here\n")
214       (kill-buffer "*finder-scratch*")
215       (unless noninteractive
216         (eval-current-buffer)) ; So we get the new keyword list immediately
217       (basic-save-buffer))))
218
219 (defun finder-compile-keywords-make-dist ()
220   "Regenerate `finder-inf.el' for the Emacs distribution."
221   (finder-compile-keywords default-directory))
222
223 ;;; Now the retrieval code
224
225 (defun finder-insert-at-column (column &rest strings)
226   "Insert list of STRINGS, at column COLUMN."
227   (if (>= (current-column) column) (insert "\n"))
228   (move-to-column column)
229   (let ((col (current-column)))
230     (if (< col column)
231         (indent-to column)
232       (if (and (/= col column)
233                (= (preceding-char) ?\t))
234           (let (indent-tabs-mode)
235             (delete-char -1)
236             (indent-to col)
237             (move-to-column column)))))
238   (apply 'insert strings))
239
240 (defun finder-list-keywords ()
241   "Display descriptions of the keywords in the Finder buffer."
242   (interactive)
243   (setq buffer-read-only nil)
244   (erase-buffer)
245   (mapcar
246    (lambda (assoc)
247      (let ((keyword (car assoc)))
248        (insert (symbol-name keyword))
249        (finder-insert-at-column 14 (concat (cdr assoc) "\n"))
250        (cons (symbol-name keyword) keyword)))
251    finder-known-keywords)
252   (goto-char (point-min))
253   (setq finder-headmark (point))
254   (setq buffer-read-only t)
255   (set-buffer-modified-p nil)
256   ;; XEmacs change
257   (if (not (one-window-p))
258       (balance-windows))
259   (finder-summary))
260
261 (defun finder-list-matches (key)
262   (setq buffer-read-only nil)
263   (erase-buffer)
264   (let ((id (intern key)))
265     (insert
266      "The following packages match the keyword `" key "':\n\n")
267     (setq finder-headmark (point))
268     (mapcar
269      (lambda (x)
270        (if (memq id (car (cdr (cdr x))))
271            (progn
272              (insert (car x))
273              (finder-insert-at-column 16 (concat (car (cdr x)) "\n")))))
274      finder-package-info)
275     (goto-char (point-min))
276     (forward-line)
277     (setq buffer-read-only t)
278     (set-buffer-modified-p nil)
279     (shrink-window-if-larger-than-buffer)
280     (finder-summary)))
281
282 ;; Search for a file named FILE the same way `load' would search.
283 (defun finder-find-library (file)
284   (if (file-name-absolute-p file)
285       file
286     (let ((dirs load-path)
287           found)
288       (while (and dirs (not found))
289         (if (file-exists-p (expand-file-name (concat file ".el") (car dirs)))
290             (setq found (expand-file-name (concat file ".el") (car dirs)))
291           (if (file-exists-p (expand-file-name file (car dirs)))
292               (setq found (expand-file-name file (car dirs)))))
293         (setq dirs (cdr dirs)))
294       found)))
295
296 ;;;###autoload
297 (defun finder-commentary (file)
298   "Display FILE's commentary section.
299 FILE should be in a form suitable for passing to `locate-library'."
300   (interactive "sLibrary name: ")
301   (let* ((str (lm-commentary (or (finder-find-library file)
302                                  (finder-find-library (concat file ".el"))
303                                  (error "Can't find library %s" file)))))
304     (if (null str)
305         (error "Can't find any Commentary section"))
306     (pop-to-buffer "*Finder*")
307     ;; XEmacs change
308     (setq buffer-read-only nil
309           mode-motion-hook 'mode-motion-highlight-line)
310     (erase-buffer)
311     (insert str)
312     (goto-char (point-min))
313     (delete-blank-lines)
314     (goto-char (point-max))
315     (delete-blank-lines)
316     (goto-char (point-min))
317     (while (re-search-forward "^;+ ?" nil t)
318       (replace-match "" nil nil))
319     (goto-char (point-min))
320     (setq buffer-read-only t)
321     (set-buffer-modified-p nil)
322     (shrink-window-if-larger-than-buffer)
323     (finder-summary)))
324
325 (defun finder-current-item ()
326   (if (and finder-headmark (< (point) finder-headmark))
327       (error "No keyword or filename on this line")
328     (save-excursion
329       (beginning-of-line)
330       (current-word))))
331
332 ;; XEmacs change
333 (defun finder-edit ()
334   (interactive)
335   (let ((entry (finder-current-item)))
336     (if (string-match finder-file-regexp entry)
337         (let ((path (finder-find-library entry)))
338           (if path
339               (find-file-other-window path)
340             (error "Can't find Emacs Lisp library: '%s'" entry)))
341       ;; a finder keyword
342       (error "Finder-edit works on Emacs Lisp libraries only"))))
343
344 ;; XEmacs change
345 (defun finder-view ()
346   (interactive)
347   (let ((entry (finder-current-item)))
348     (if (string-match finder-file-regexp entry)
349         (let ((path (finder-find-library entry)))
350           (if path
351               (view-file-other-window path)
352             (error "Can't find Emacs Lisp library: '%s'" entry)))
353       ;; a finder keyword
354       (error "Finder-view works on Emacs Lisp libraries only"))))
355
356 (defun finder-select ()
357   (interactive)
358   (let ((key (finder-current-item)))
359     ;; XEmacs change
360     (if (string-match finder-file-regexp key)
361         (finder-commentary key)
362       (finder-list-matches key))))
363
364 ;; XEmacs change
365 (defun finder-mouse-select (ev)
366   (interactive "e")
367   (goto-char (event-point ev))
368   (finder-select))
369
370 ;; XEmacs change
371 ;;;###autoload
372 (defun finder-by-keyword ()
373   "Find packages matching a given keyword."
374   (interactive)
375   (finder-mode)
376   (finder-list-keywords))
377
378 (defun finder-mode ()
379   "Major mode for browsing package documentation.
380 \\<finder-mode-map>
381 \\[finder-select]       more help for the item on the current line
382 \\[finder-edit] edit Lisp library in another window
383 \\[finder-view] view Lisp library in another window
384 \\[finder-exit] exit Finder mode and kill the Finder buffer.
385 "
386   (interactive)
387   (pop-to-buffer "*Finder*")
388   ;; XEmacs change
389   (setq buffer-read-only nil
390         mode-motion-hook 'mode-motion-highlight-line)
391   (erase-buffer)
392   (use-local-map finder-mode-map)
393   (set-syntax-table emacs-lisp-mode-syntax-table)
394   (setq mode-name "Finder")
395   (setq major-mode 'finder-mode)
396   (make-local-variable 'finder-headmark)
397   (setq finder-headmark nil))
398
399 (defun finder-summary ()
400   "Summarize basic Finder commands."
401   (interactive)
402   (message "%s"
403    (substitute-command-keys
404     ;; XEmacs change
405     "\\<finder-mode-map>\\[finder-select] = select, \\[finder-list-keywords] = keywords, \\[finder-edit] = edit, \\[finder-view] = view, \\[finder-exit] = quit, \\[finder-summary] = help")))
406
407 (defun finder-exit ()
408   "Exit Finder mode and kill the buffer."
409   (interactive)
410   ;; XEmacs change
411   (or (one-window-p t 0)
412       (delete-window))
413   (kill-buffer "*Finder*"))
414
415 (provide 'finder)
416
417 ;;; finder.el ends here