home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / elisp / misc / lib-complete.el < prev    next >
Encoding:
Text File  |  1991-06-19  |  12.2 KB  |  320 lines

  1. ; Path: dg-rtp!rock.concert.net!mcnc!stanford.edu!agate!spool.mu.edu!olivea!uunet!comp.vuw.ac.nz!waikato.ac.nz!aukuni.ac.nz!mike-w
  2. ; From: mike-w@cs.aukuni.ac.nz (Mike Williams)
  3. ; Newsgroups: gnu.emacs.sources
  4. ; Subject: UPDATE: lib-complete.el
  5. ; Date: 20 Jun 91 16:41:53 GMT
  6. ; Organization: University of Auckland, New Zealand.
  7. ; Nntp-Posting-Host: cs17.cs.aukuni.ac.nz
  8. ; X-News-Software: GNUS 3.13
  9. ;   Here's an update of the recently posted lib-complete.el, with improved
  10. ;   caching, several bug-fixes, and a generally better outlook on life :-)
  11. ;   It incorporates several ideas from Hallvard Furuseth's load-library.el.
  12. ;; ========================================================================
  13. ;; lib-complete.el --  Completion on a search path
  14. ;; Author          : Mike Williams <mike-w@cs.aukuni.ac.nz>
  15. ;; Created On      : Sat Apr 20 17:47:21 1991
  16. ;; Last Modified By: Mike Williams
  17. ;; Last Modified On: Tue Jun 18 12:53:08 1991
  18. ;; RCS Info        : $Revision: 1.7 $ $Locker:  $
  19. ;; ========================================================================
  20. ;; NOTE: this file must be recompiled if changed.
  21. ;;
  22. ;; Copyright (C) Mike Williams <mike-w@cs.aukuni.ac.nz> 1991
  23. ;;
  24. ;; LCD Archive Entry:
  25. ;; lib-complete|Mike Williams|mike-w@cs.aukuni.ac.nz
  26. ;; |Completion on a search path
  27. ;; |91-06-20|1.7|~/misc/lib-complete.el.Z
  28. ;;
  29. ;; This file is not part of GNU Emacs, but is made available under the
  30. ;; same conditions.
  31. ;;
  32. ;; GNU Emacs is distributed in the hope that it will be useful,
  33. ;; but WITHOUT ANY WARRANTY.  No author or distributor
  34. ;; accepts responsibility to anyone for the consequences of using it
  35. ;; or for whether it serves any particular purpose or works at all,
  36. ;; unless he says so in writing.  Refer to the GNU Emacs General Public
  37. ;; License for full details.
  38. ;;
  39. ;; Everyone is granted permission to copy, modify and redistribute
  40. ;; GNU Emacs, but only under the conditions described in the
  41. ;; GNU Emacs General Public License.   A copy of this license is
  42. ;; supposed to have been given to you along with GNU Emacs so you
  43. ;; can know your rights and responsibilities.  It should be in a
  44. ;; file named COPYING.  Among other things, the copyright notice
  45. ;; and this notice must be preserved on all copies.
  46.  
  47. ;; Many thanks to Hallvard Furuseth <hallvard@ifi.uio.no> for his
  48. ;; helpful suggestions.
  49.  
  50. (provide 'lib-complete)
  51.  
  52. ;; LCD Archive Entry:
  53. ;; lib-complete|Mike Williams|mike-w@cs.aukuni.ac.nz
  54. ;; |Completion on a search path
  55. ;; |91-04-20|$Revision: 1.7 $|
  56.  
  57. ;;=== Usage ===============================================================
  58. ;; 
  59. ;; (autoload (fmakunbound 'load-library) "lib-complete" nil t)
  60. ;; (autoload 'locate-file "lib-complete" nil t)
  61. ;; (autoload 'library-all-completions "lib-complete")
  62. ;; (autoload 'read-library "lib-complete")
  63.  
  64. ;;=== Locate a file in a search path ======================================
  65.  
  66. (defun locate-file (FILE SEARCH-PATH &optional SUFFIX-LIST PRED)
  67.   "Search for FILE on SEARCH-PATH (list).  If optional SUFFIX-LIST is
  68. provided, allow file to be followed by one of the suffixes.
  69. Optional second argument PRED restricts the number of files which
  70. may match.  The default is file-exists-p."
  71.   (if (not SUFFIX-LIST) (setq SUFFIX-LIST '("")))
  72.   (if (not PRED) (setq PRED 'file-exists-p))
  73.   (if (file-name-absolute-p FILE) (setq SEARCH-PATH '(nil)))
  74.   (if (equal FILE "") (error "Empty filename"))
  75.   (let ((filelist 
  76.      (mapcar 
  77.       (function (lambda (ext) (concat FILE ext)))
  78.       SUFFIX-LIST)))
  79.     ;; Search SEARCH-PATH for a readable file in filelist
  80.     (catch 'found
  81.       (while SEARCH-PATH
  82.     (let ((filelist filelist))
  83.       (while filelist
  84.         (let ((filepath (expand-file-name (car filelist) 
  85.                           (car SEARCH-PATH))))
  86.           (if (funcall PRED filepath)
  87.           (throw 'found filepath)))
  88.         (setq filelist (cdr filelist))))
  89.     (setq SEARCH-PATH (cdr SEARCH-PATH))))
  90.     ))
  91.  
  92. ;;=== Determine completions for filename in search path ===================
  93.  
  94. (defun library-all-completions (FILE SEARCH-PATH &optional FULL FAST)
  95.   "Return all completions for FILE in any directory on SEARCH-PATH.
  96. If optional third argument FULL is non-nil, returned pathnames should be 
  97.   absolute rather than relative to some directory on the SEARCH-PATH.
  98. If optional fourth argument FAST is non-nil, don't sort the completions,
  99.   or remove duplicates."
  100.   (setq FILE (or FILE ""))
  101.   (if (file-name-absolute-p FILE)
  102.       ;; It's an absolute file name, so don't need SEARCH-PATH
  103.       (progn
  104.     (setq FILE (expand-file-name FILE))
  105.     (file-name-all-completions 
  106.      (file-name-nondirectory FILE) (file-name-directory FILE)))
  107.     (let ((subdir (file-name-directory FILE))
  108.       (file (file-name-nondirectory FILE))
  109.       all-completions)
  110.       ;; Make list of completions in each directory on SEARCH-PATH
  111.       (while SEARCH-PATH
  112.     (let* ((dir (concat (file-name-as-directory 
  113.                  (expand-file-name (car SEARCH-PATH)))
  114.                 subdir))
  115.            (dir-prefix (if FULL dir subdir)))
  116.       (if (file-directory-p dir)
  117.           (let ((subdir-completions 
  118.              (file-name-all-completions file dir)))
  119.         (while subdir-completions
  120.           (setq all-completions 
  121.             (cons (concat dir-prefix (car subdir-completions))
  122.                   all-completions))
  123.           (setq subdir-completions (cdr subdir-completions))))))
  124.     (setq SEARCH-PATH (cdr SEARCH-PATH)))   
  125.       (if FAST all-completions
  126.     (let ((sorted (nreverse (sort all-completions 'string<)))
  127.           compressed)
  128.       (while sorted
  129.         (if (equal (car sorted) (car compressed)) nil
  130.           (setq compressed (cons (car sorted) compressed)))
  131.         (setq sorted (cdr sorted)))
  132.       compressed)))))
  133.  
  134. ;;=== Utilities ===========================================================
  135.  
  136. (defmacro progn-with-message (MESSAGE &rest FORMS)
  137.   "(progn-with-message MESSAGE FORMS ...)
  138. Display MESSAGE and evaluate FORMS, returning value of the last one."
  139.   ;; based on Hallvard Furuseth's funcall-with-message
  140.   (` 
  141.    (if (eq (selected-window) (minibuffer-window))
  142.        (save-excursion
  143.      (goto-char (point-max))
  144.      (let ((orig-pmax (point-max)))
  145.        (unwind-protect
  146.            (progn
  147.          (insert " " (, MESSAGE)) (goto-char orig-pmax)
  148.          (sit-for 0)        ; Redisplay
  149.          (,@ FORMS))
  150.          (delete-region orig-pmax (point-max)))))
  151.      (prog2
  152.       (message "%s" (, MESSAGE))
  153.       (progn (,@ FORMS))
  154.       (message "")))))
  155.  
  156. (put 'progn-with-message 'lisp-indent-hook 1)
  157.  
  158. ;;=== Completion caching ==================================================
  159.  
  160. (defconst lib-complete:cache nil
  161.   "Used within read-library and read-library-internal to prevent 
  162. costly repeated calls to library-all-completions.
  163. Format is a list of lists of the form
  164.  
  165.     ([<path> <subdir>] <cache-record> <cache-record> ...)
  166.  
  167. where each <cache-record> has the form
  168.  
  169.    (<root> <modtimes> <completion-table>)")
  170.  
  171. (defun lib-complete:better-root (ROOT1 ROOT2)
  172.   "Return non-nil if ROOT1 is a superset of ROOT2."
  173.   (and (equal (file-name-directory ROOT1) (file-name-directory ROOT2))
  174.        (string-match
  175.     (concat "^" (regexp-quote (file-name-nondirectory ROOT1)))
  176.     ROOT2)))
  177.  
  178. (defun lib-complete:get-completion-table (FILE PATH FILTER)
  179.   (let* ((subdir (file-name-directory FILE))
  180.      (root (file-name-nondirectory FILE))
  181.      (PATH 
  182.       (mapcar 
  183.        (function (lambda (dir) (file-name-as-directory
  184.                     (expand-file-name (or dir "")))))
  185.        PATH))
  186.      (key (vector PATH subdir FILTER))
  187.      (real-dirs 
  188.       (if subdir
  189.           (mapcar (function (lambda (dir) (concat dir subdir))) PATH)
  190.         PATH))
  191.      (path-modtimes
  192.       (mapcar 
  193.        (function (lambda (fn) (if fn (nth 5 (file-attributes fn))))) 
  194.        real-dirs))
  195.      (cache-entry (assoc key lib-complete:cache))
  196.      (cache-records (cdr cache-entry)))
  197.     ;; Look for cached entry
  198.     (catch 'table
  199.       (while cache-records
  200.     (if (and 
  201.          (lib-complete:better-root (nth 0 (car cache-records)) root)
  202.          (equal (nth 1 (car cache-records)) path-modtimes))
  203.         (throw 'table (nth 2 (car cache-records))))
  204.     (setq cache-records (cdr cache-records)))
  205.       ;; Otherwise build completions
  206.       (let ((completion-list 
  207.          (progn-with-message "(building completion table...)"
  208.            (library-all-completions FILE PATH nil 'fast)))
  209.         (completion-table (make-vector 127 0)))
  210.     (while completion-list
  211.       (let ((completion
  212.          (if (or (not FILTER) 
  213.              (file-directory-p (car completion-list))) 
  214.              (car completion-list)
  215.            (funcall FILTER (car completion-list)))))
  216.         (if completion
  217.         (intern completion completion-table)))
  218.       (setq completion-list (cdr completion-list)))
  219.     ;; Cache the completions
  220.     (lib-complete:cache-completions key root 
  221.                     path-modtimes completion-table)
  222.     completion-table))))
  223.  
  224. (defvar lib-complete:max-cache-size 20 
  225.   "*Maximum number of search paths which are cached.")
  226.  
  227. (defun lib-complete:cache-completions (KEY ROOT MODTIMES TABLE)
  228.   (let ((cache-entry (assoc key lib-complete:cache))
  229.     (cache-records (cdr cache-entry))
  230.     (new-cache-records (list (list ROOT MODTIMES TABLE))))
  231.     (if (not cache-entry) nil
  232.       ;; Remove old cache entry
  233.       (setq lib-complete:cache (delq cache-entry lib-complete:cache))
  234.       ;; Copy non-redundant entries from old cache entry
  235.       (while cache-records
  236.     (if (or (equal ROOT (nth 0 (car cache-records)))
  237.         (lib-complete:better-root ROOT (nth 0 (car cache-records))))
  238.         nil
  239.       (setq new-cache-records 
  240.         (cons (car cache-records) new-cache-records)))
  241.     (setq cache-records (cdr cache-records))))
  242.     ;; Add entry to front of cache
  243.     (setq lib-complete:cache
  244.       (cons (cons KEY (nreverse new-cache-records)) lib-complete:cache))
  245.     ;; Trim cache
  246.     (let ((tail (nthcdr lib-complete:max-cache-size lib-complete:cache)))
  247.       (if tail (setcdr tail nil)))))
  248.  
  249. ;;=== Read a filename, with completion in a search path ===================
  250.  
  251. (defun read-library-internal (FILE FILTER FLAG)
  252.   "Don't call this."
  253.   ;; Relies on read-library-internal-search-path being let-bound
  254.   (let ((completion-table
  255.      (lib-complete:get-completion-table
  256.       FILE read-library-internal-search-path FILTER)))
  257.     (cond
  258.      ((not completion-table) nil)
  259.      ;; Completion table is filtered before use, so the PREDICATE
  260.      ;; argument is redundant.
  261.      ((eq FLAG nil) (try-completion FILE completion-table nil))
  262.      ((eq FLAG t) (all-completions FILE completion-table nil))
  263.      ((eq FLAG 'lambda) (and (intern-soft FILE completion-table) t))
  264.      )))
  265.  
  266. (defun read-library (PROMPT SEARCH-PATH &optional DEFAULT MUST-MATCH 
  267.                 FULL FILTER)
  268.   "Read library name, prompting with PROMPT and completing in directories
  269. from SEARCH-PATH.  A nil in the search path represents the current
  270. directory.  Completions for a given search-path are cached, with the
  271. cache being invalidated whenever one of the directories on the path changes.
  272. Default to DEFAULT if user enters a null string.
  273. Optional fourth arg MUST-MATCH non-nil means require existing file's name.
  274.   Non-nil and non-t means also require confirmation after completion.
  275. Optional fifth argument FULL non-nil causes a full pathname, rather than a 
  276.   relative pathname, to be returned.  Note that FULL implies MUST-MATCH.
  277. Optional sixth argument FILTER can be used to provide a function to
  278.   filter the completions.  This function is passed the filename, and should
  279.   return a transformed filename (possibly a null transformation) or nil, 
  280.   indicating that the filename should not be included in the completions."
  281.   (let* ((read-library-internal-search-path SEARCH-PATH)
  282.      (library (completing-read PROMPT 'read-library-internal 
  283.                    FILTER (or MUST-MATCH FULL) nil)))
  284.     (cond 
  285.      ((equal library "") DEFAULT)
  286.      (FULL (locate-file library read-library-internal-search-path))
  287.      (t library))))
  288.  
  289. ;; NOTE: as a special case, read-library may be used to read a filename
  290. ;; relative to the current directory, returning a *relative* pathname
  291. ;; (read-file-name returns a full pathname).
  292. ;;
  293. ;; eg. (read-library "Local header: " '(nil) nil)
  294.  
  295. ;;=== Replacement for load-library with completion ========================
  296.  
  297. (defun load-library (LIBRARY)
  298.   "Load the library named LIBRARY."
  299.   (interactive 
  300.    (list 
  301.     (read-library "Load Library: " load-path nil nil nil
  302.           (function (lambda (fn) 
  303.                   (cond 
  304.                    ((string-match "\\.elc?$" fn)
  305.                 (substring fn 0 (match-beginning 0))))))
  306.           )))
  307.   (load LIBRARY))
  308.  
  309. ;;=== END of lib-complete.el ==============================================
  310. --
  311.     /-------------------- mike-w@cs.aukuni.ac.nz ---------------------\
  312.     | Mike Williams, Computer Science, Auckland University, Aotearoa. |
  313.     \-------------- I have a lovely bunch of coconuts. ---------------/
  314.     Disclaimer: I wasn't even there.
  315. -- 
  316. New administrater uofa.
  317.