home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / elisp / packages / filec.el < prev    next >
Encoding:
Text File  |  1990-07-22  |  10.8 KB  |  281 lines

  1. ;From ark1!uakari.primate.wisc.edu!samsung!uunet!talos!kjones Fri Mar 23 15:52:52 1990
  2. ;Article 1609 of comp.emacs:
  3. ;Path: ark1!uakari.primate.wisc.edu!samsung!uunet!talos!kjones
  4. ;>From elves@magic-tree.keebler.com (The Keebler Elves)
  5. ;Newsgroups: comp.emacs
  6. ;Subject: filec.el (improved)
  7. ;Summary: filename completion for normally non-completing types of
  8. ;         minibuffer input.
  9. ;Message-ID: <1990Mar21.160311.23399@talos.pm.com>
  10. ;Date: 21 Mar 90 16:03:11 GMT
  11. ;Sender: kjones@talos.pm.com (Kyle Jones)
  12. ;Reply-To: kyle@cs.odu.edu
  13. ;Lines: 266
  14. ;
  15. ;Two new variables: completion-use-environment set non-nil support expansion of
  16. ;references to enviromental variables, and completion-slashify-directories set
  17. ;non-nil causes the automatic appending of a slash to unambiguously completed
  18. ;directory names.  There is a tiny bug fix to minibuffer-completion-message.
  19. ;
  20. ;Installation instructions are in the comments at the top of the file.
  21. ;
  22. ;Scream at the peacocks,
  23. ;
  24. ;kyle jones   <kjones@talos.pm.com>   ...!uunet!talos!kjones
  25. ;------------------------------------------------------------
  26. ;;; Filename completion in the minibuffer
  27. ;;; Copyright (C) 1990 Kyle E. Jones
  28. ;;;
  29. ;;; This program is free software; you can redistribute it and/or modify
  30. ;;; it under the terms of the GNU General Public License as published by
  31. ;;; the Free Software Foundation; either version 1, or (at your option)
  32. ;;; any later version.
  33. ;;;
  34. ;;; This program is distributed in the hope that it will be useful,
  35. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  36. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  37. ;;; GNU General Public License for more details.
  38. ;;;
  39. ;;; A copy of the GNU General Public License can be obtained from this
  40. ;;; program's author (send electronic mail to kyle@cs.odu.edu) or from
  41. ;;; the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
  42. ;;; 02139, USA.
  43. ;;;
  44. ;;; Send bug reports to kyle@cs.odu.edu.
  45.  
  46. ;; This package provides filename completion for the normally
  47. ;; non-completing types of minibuffer input.  The central function is
  48. ;; minibuffer-complete-file.  This function should be bound to a key in
  49. ;; minibuffer-local-map that you want to use to invoke filename
  50. ;; completion.  The installtion instructions below assumes your choice
  51. ;; will be TAB; change the define-key call to suit your own tastes.
  52. ;;
  53. ;; To use this package, put it in a file called "filec.el" in a Lisp
  54. ;; directory that Emacs knows about, and byte-compile it.
  55. ;;
  56. ;; At this point you can either:
  57. ;;
  58. ;;  1. Put the lines:
  59. ;;       (require 'filec)
  60. ;;       (define-key minibuffer-local-map "\t" 'minibuffer-complete-file)
  61. ;;     in your .emacs file.
  62. ;;
  63. ;;  2. Put the lines:
  64. ;;       (autoload 'minibuffer-complete-file "filec" nil t)
  65. ;;       (define-key minibuffer-local-map "\t" 'minibuffer-complete-file)
  66. ;;     in your .emacs file.
  67.  
  68. (provide 'filec)
  69.  
  70. (defvar completion-auto-correct nil
  71.   "*Non-nil means that minibuffer-complete-file should aggressively erase
  72. the trailing part of a word that caused completion to fail, and retry
  73. the completion with the resulting word.")
  74.  
  75. (defvar completion-use-environment nil
  76.   "*Non-nil value means that minibuffer-complete-file should expand
  77. references to environmental variables.  Such references should appear as
  78. $var, where var is an environmental variable.  To get a $ interpreted
  79. normally in a filename when this variable is set non-nil, you must type $$.")
  80.  
  81. (defvar completion-slashify-directories nil
  82.   "*Non-nil value means that minibuffer-complete-file should automatically
  83. append a slash to directory names that complete unambiguously.")
  84.  
  85. (defun minibuffer-complete-file ()
  86.   "Interpret the word under or before the cursor as a filename and complete
  87. it as far as possible."
  88.   (interactive)
  89.   (let ((opoint (point)) beg end unexpanded-word word completion c-list
  90.     directory file-regexp)
  91.     ;; find the beginning and end of the word we're trying to complete
  92.     (if (or (eobp) (memq (following-char) '(?\t ?\n ?\ )))
  93.     (progn
  94.       (skip-chars-backward " \t\n")   
  95.       (and (not (eobp)) (forward-char))
  96.       (setq end (point)))
  97.       (skip-chars-forward "^ \t\n")
  98.       (setq end (point)))
  99.     (skip-chars-backward "^ \t\n")
  100.     (setq beg (point))
  101.     (goto-char opoint)
  102.     ;; copy the word into a string
  103.     (setq word (buffer-substring beg end))
  104.     ;; expand environmental variables if the user requested it.
  105.     (and completion-use-environment
  106.      (not (eq word (setq word (substitute-in-file-name word))))
  107.      (progn
  108.        (delete-region beg end)
  109.        (insert word)
  110.        (setq end (+ beg (length word)))))
  111.     (setq unexpanded-word word)
  112.     ;; expand the filename fully so we can compare to the full pathname.
  113.     ;; expand-file-name "resolves" . and .., so we have to shield them.
  114.     (if (and (string-match "\\(^\\|/\\)?\.\.?$" word)
  115.          (file-directory-p word))
  116.     (setq word
  117.           (concat (expand-file-name
  118.                (or (file-name-directory word)
  119.                default-directory))
  120.               (file-name-nondirectory word)))
  121.       (while (not (eq word (setq word (expand-file-name word))))))
  122.     ;; extract the directory information from the word
  123.     (setq directory (file-name-directory word))
  124.     ;; extract the file part of the word and convert it to a regular
  125.     ;; expression that matches itself and any other string prefixed by
  126.     ;; it.
  127.     (setq file-regexp
  128.       (concat "^" (regexp-quote (file-name-nondirectory word))))
  129.     ;; Generate a completion list consisting of the filenames in the
  130.     ;; specified directory (see above), taking into account
  131.     ;; completion-ignored-extensions.
  132.     (setq c-list (directory-files directory t file-regexp)
  133.       c-list (or (delete-matching-strings
  134.               (concat (mapconcat 'regexp-quote
  135.                      completion-ignored-extensions
  136.                      "\\|")
  137.                   "$")
  138.               c-list)
  139.              c-list)
  140.       c-list (mapcar 'list c-list))
  141.     ;; Try the word against the completion list.
  142.     (and c-list (setq completion (try-completion word c-list)))
  143.     ;; If completion is nil, figure out what prefix of the word would prefix
  144.     ;; something in the completion list... but only if the user is interested.
  145.     (if (and (null completion) completion-auto-correct)
  146.     (let ((c-list (mapcar 'list (directory-files directory t nil)))
  147.           (i -1))
  148.       (while (null (setq completion
  149.                  (try-completion (substring word 0 i) c-list)))
  150.         (setq i (1- i)))
  151.       (setq completion (substring word 0 i))))
  152.     ;; If completion is t, we had a perfect match already.
  153.     (if (eq completion t)
  154.     (cond ((cdr c-list)
  155.            (minibuffer-completion-message "[Complete, but not unique]"))
  156.           ((and completion-slashify-directories
  157.             (file-directory-p word)
  158.             (/= ?/ (substring word -1 (length word))))
  159.            (goto-char end)
  160.            (insert "/"))
  161.           (t
  162.            (minibuffer-completion-message "[Sole completion]")))
  163.       ;; Compute the difference in length between the completion and the
  164.       ;; word.  A negative difference means no match and the magnitude
  165.       ;; indicates the number of chars that need to be shaved off the end
  166.       ;; before a match will occur.  A positive difference means a match
  167.       ;; occurred and the magnitude specifies the number of new chars that
  168.       ;; can be appended to the word as a completion.
  169.       ;;
  170.       ;; Because of file name expansion, the magnitude of a negative
  171.       ;; difference can be greater than the length of the unexpanded word.
  172.       ;; Therefore the floor value is limited by negative length of the word.
  173.       ;;
  174.       ;; `completion' can be nil here, but the code works anyway because
  175.       ;; (length nil) still equals 0!
  176.       (setq diff (max (- beg end) (- (length completion) (length word))))
  177.       (cond
  178.        ;; We have some completion chars.  Insert them.
  179.        ((> diff 0)
  180.     (goto-char end)
  181.     (insert (substring completion (- diff)))
  182.     (if (and completion-slashify-directories
  183.          (null (cdr c-list))
  184.          (file-directory-p completion))
  185.         (insert "/")))
  186.        ;; The word prefixed more than one string, but we can't complete
  187.        ;; any further.  Either give help or say "Ambiguous".
  188.        ((zerop diff)
  189.     (if (assoc word c-list)
  190.         (minibuffer-completion-message "[Complete, but not unique]")
  191.       (if (not completion-auto-help)
  192.           (minibuffer-completion-message "[Ambiguous]")
  193.         (minibuffer-show-completions
  194.          (sort
  195.           (directory-files
  196.            directory nil
  197.            (concat "^" (regexp-quote (file-name-nondirectory word))))
  198.           'string-lessp)))))
  199.        ;; The word didn't prefix anything... if completion-auto-correct is
  200.        ;; non-nil strip the offending characters and try again.
  201.        (completion-auto-correct
  202.     (goto-char end)
  203.     (delete-char diff)
  204.     (minibuffer-complete-file))
  205.        ;; completion utterly failed, tell the user so.
  206.        (t
  207.     (minibuffer-completion-message "[No match]"))))))
  208.  
  209. (defun minibuffer-completion-message (string &optional seconds)
  210.   "Briefly display STRING to the right of the current minibuffer input.
  211. Optional second arg SECONDS specifies how long to keep the message visible;
  212. the default is 2 seconds.
  213.  
  214. A keypress causes the immediate erasure of the STRING, and return of control
  215. to the calling program."
  216.   (let (omax (inhibit-quit t))
  217.     (save-excursion
  218.       (goto-char (point-max))
  219.       (setq omax (point))
  220.       (insert " " string))
  221.     (sit-for (or seconds 2))
  222.     (delete-region omax (point-max))))
  223.  
  224. (defun minibuffer-show-completions (list)
  225.   "Display LIST in a multi-column listing in the \" *Completions*\" buffer.
  226. LIST should be a list of strings."
  227.   (save-excursion
  228.     (let (longest rows columns list-length q i)
  229.       (set-buffer (get-buffer-create " *Completions*"))
  230.       (erase-buffer)
  231.       (insert "Possible completions are:\n")
  232.       (setq q list
  233.         list-length 0
  234.         longest 0)
  235.       (while q
  236.     (setq longest (max longest (length (car q)))
  237.           list-length (1+ list-length)
  238.           q (cdr q)))
  239.       ;; provide for separation between columns
  240.       (setq longest (+ 3 longest))
  241.       (setq columns (/ (- (screen-width) 2) longest)
  242.         rows (/ list-length columns)
  243.         rows
  244.         (+ (if (zerop (% list-length columns)) 0 1)
  245.            rows))
  246.       (setq i columns
  247.         tab-stop-list nil)
  248.       (while (not (zerop i))
  249.     (setq tab-stop-list (cons (* longest i) tab-stop-list)
  250.           i (1- i)))
  251.       (setq q list
  252.         i 0)
  253.       (while q
  254.     (insert (car q))
  255.     (setq i (1+ i)
  256.           q (cdr q))
  257.     (if (zerop (% i columns))
  258.         (insert "\n")
  259.       (tab-to-tab-stop)))
  260.       (goto-char (point-min))
  261.       (display-buffer " *Completions*"))))
  262.  
  263. (defun delete-matching-strings (regexp list &optional destructively)
  264.   "Delete strings matching REGEXP from LIST.
  265. Optional third arg non-nil means to destructively alter LIST, instead of
  266. working on a copy.
  267.  
  268. The new version of the list, minus the deleted strings, is returned."
  269.   (or destructively (setq list (copy-sequence list)))
  270.   (let ((curr list) (prev nil))
  271.     (while curr
  272.       (if (not (string-match regexp (car curr)))
  273.       (setq prev curr
  274.         curr (cdr curr))
  275.     (if (null prev)
  276.         (setq list (cdr list)
  277.           curr list)
  278.       (setcdr prev (cdr curr))
  279.       (setq curr (cdr curr)))))
  280.     list ))
  281.