X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/f5f727f8ffa2e674601875b2f2fe7ce3f0030c93..8e5c7b90ee28002fa6b4b860d3c69719f865f19a:/lisp/filecache.el diff --git a/lisp/filecache.el b/lisp/filecache.el index a580ee67ba..442f729dd1 100644 --- a/lisp/filecache.el +++ b/lisp/filecache.el @@ -1,11 +1,11 @@ -;;; filecache.el --- Find files using a pre-loaded cache +;;; filecache.el --- find files using a pre-loaded cache ;; ;; Author: Peter Breton ;; Created: Sun Nov 10 1996 ;; Keywords: convenience -;; Time-stamp: <1998-04-29 22:38:56 pbreton> ;; -;; Copyright (C) 1996 Free Software Foundation, Inc. +;; Copyright (C) 1996, 2000, 2002, 2003, 2004, +;; 2005 Free Software Foundation, Inc. ;; This file is part of GNU Emacs. @@ -21,8 +21,8 @@ ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, -;; Boston, MA 02111-1307, USA. +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; @@ -43,14 +43,14 @@ ;; ADDING FILES TO THE CACHE: ;; ;; Use the following functions to add items to the file cache: -;; +;; ;; * `file-cache-add-file': Adds a single file to the cache ;; ;; * `file-cache-add-file-list': Adds a list of files to the cache ;; ;; The following functions use the regular expressions in ;; `file-cache-delete-regexps' to eliminate unwanted files: -;; +;; ;; * `file-cache-add-directory': Adds the files in a directory to the ;; cache. You can also specify a regular expression to match the files ;; which should be added. @@ -64,6 +64,9 @@ ;; * `file-cache-add-directory-using-locate': Uses the `locate' command to ;; add files matching a pattern to the cache. ;; +;; * `file-cache-add-directory-recursively': Uses the find-lisp package to +;; add all files matching a pattern to the cache. +;; ;; Use the function `file-cache-clear-cache' to remove all items from the ;; cache. There are a number of `file-cache-delete' functions provided ;; as well, but in general it is probably better to not worry too much @@ -88,7 +91,7 @@ ;; ;; 4) When you have found a unique completion, the minibuffer contents ;; will change to the full name of that file. -;; +;; ;; If there are a number of directories which contain the completion, ;; invoking `file-cache-minibuffer-complete' repeatedly will cycle through ;; them. @@ -102,7 +105,7 @@ ;; For maximum utility, you should probably define an `eval-after-load' ;; form which loads your favorite files: ;; -;; (eval-after-load +;; (eval-after-load ;; "filecache" ;; '(progn ;; (message "Loading file cache...") @@ -115,10 +118,10 @@ ;; If you clear and reload the cache frequently, it is probably easiest ;; to put your initializations in a function: ;; -;; (eval-after-load +;; (eval-after-load ;; "filecache" ;; '(my-file-cache-initialize)) -;; +;; ;; (defun my-file-cache-initialize () ;; (interactive) ;; (message "Loading file cache...") @@ -132,13 +135,16 @@ ;; Lisp functions. ;; ;; RELATED WORK: -;; +;; ;; This package is a distant relative of Noah Friedman's fff utilities. ;; Our goal is pretty similar, but the implementation strategies are ;; different. ;;; Code: +(eval-when-compile + (require 'find-lisp)) + (defgroup file-cache nil "Find files using a pre-loaded cache." :group 'files @@ -150,12 +156,12 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; User-modifiable variables -(defcustom file-cache-filter-regexps - (list "~$" "\\.o$" "\\.exe$" "\\.a$" "\\.elc$" ",v$" "\\.output$" - "\\.$" "#$") +(defcustom file-cache-filter-regexps + (list "~$" "\\.o$" "\\.exe$" "\\.a$" "\\.elc$" ",v$" "\\.output$" + "\\.$" "#$" "\\.class$") "*List of regular expressions used as filters by the file cache. File names which match these expressions will not be added to the cache. -Note that the functions `file-cache-add-file' and `file-cache-add-file-list' +Note that the functions `file-cache-add-file' and `file-cache-add-file-list' do not use this variable." :type '(repeat regexp) :group 'file-cache) @@ -165,6 +171,19 @@ do not use this variable." :type 'string :group 'file-cache) +(defcustom file-cache-find-command-posix-flag 'not-defined + "*Set to t, if `file-cache-find-command' handles wildcards POSIX style. +This variable is automatically set to nil or non-nil +if it has the initial value `not-defined' whenever you first +call the `file-cache-add-directory-using-find'. + +Under Windows operating system where Cygwin is available, this value +should be t." + :type '(choice (const :tag "Yes" t) + (const :tag "No" nil) + (const :tag "Unknown" not-defined)) + :group 'file-cache) + (defcustom file-cache-locate-command "locate" "*External program used by `file-cache-add-directory-using-locate'." :type 'string @@ -187,6 +206,34 @@ do not use this variable." :type 'string :group 'file-cache) +(defcustom file-cache-completion-ignore-case + (if (memq system-type (list 'ms-dos 'windows-nt 'cygwin)) + t + completion-ignore-case) + "If non-nil, file-cache completion should ignore case. +Defaults to the value of `completion-ignore-case'." + :type 'sexp + :group 'file-cache + ) + +(defcustom file-cache-case-fold-search + (if (memq system-type (list 'ms-dos 'windows-nt 'cygwin)) + t + case-fold-search) + "If non-nil, file-cache completion should ignore case. +Defaults to the value of `case-fold-search'." + :type 'sexp + :group 'file-cache + ) + +(defcustom file-cache-ignore-case + (memq system-type (list 'ms-dos 'windows-nt 'cygwin)) + "Non-nil means ignore case when checking completions in the file cache. +Defaults to nil on DOS and Windows, and t on other systems." + :type 'sexp + :group 'file-cache + ) + (defvar file-cache-multiple-directory-message nil) ;; Internal variables @@ -197,7 +244,7 @@ do not use this variable." :type 'string :group 'file-cache) -(defcustom file-cache-buffer "*File Cache*" +(defcustom file-cache-buffer "*File Cache*" "Buffer to hold the cache of file names." :type 'string :group 'file-cache) @@ -221,7 +268,7 @@ do not use this variable." (defun file-cache-add-directory (directory &optional regexp) "Add DIRECTORY to the file cache. -If the optional REGEXP argument is non-nil, only files which match it will +If the optional REGEXP argument is non-nil, only files which match it will be added to the cache." (interactive "DAdd files from directory: ") ;; Not an error, because otherwise we can't use load-paths that @@ -234,21 +281,23 @@ be added to the cache." ;; Filter out files we don't want to see (mapcar '(lambda (file) - (mapcar - '(lambda (regexp) - (if (string-match regexp file) - (setq dir-files (delq file dir-files)))) - file-cache-filter-regexps)) + (if (file-directory-p file) + (setq dir-files (delq file dir-files)) + (mapcar + '(lambda (regexp) + (if (string-match regexp file) + (setq dir-files (delq file dir-files)))) + file-cache-filter-regexps))) dir-files) (file-cache-add-file-list dir-files)))) (defun file-cache-add-directory-list (directory-list &optional regexp) "Add DIRECTORY-LIST (a list of directory names) to the file cache. -If the optional REGEXP argument is non-nil, only files which match it -will be added to the cache. Note that the REGEXP is applied to the files +If the optional REGEXP argument is non-nil, only files which match it +will be added to the cache. Note that the REGEXP is applied to the files in each directory, not to the directory list itself." (interactive "XAdd files from directory list: ") - (mapcar + (mapcar '(lambda (dir) (file-cache-add-directory dir regexp)) directory-list)) @@ -262,10 +311,12 @@ in each directory, not to the directory list itself." "Add FILE to the file cache." (interactive "fAdd File: ") (if (not (file-exists-p file)) - (message "File %s does not exist" file) + (message "Filecache: file %s does not exist" file) (let* ((file-name (file-name-nondirectory file)) (dir-name (file-name-directory file)) - (the-entry (assoc file-name file-cache-alist)) + (the-entry (assoc-string + file-name file-cache-alist + file-cache-ignore-case)) ) ;; Does the entry exist already? (if the-entry @@ -278,22 +329,29 @@ in each directory, not to the directory list itself." ) ;; If not, add it to the cache (setq file-cache-alist - (cons (cons file-name (list dir-name)) + (cons (cons file-name (list dir-name)) file-cache-alist))) ))) - + (defun file-cache-add-directory-using-find (directory) "Use the `find' command to add files to the file cache. Find is run in DIRECTORY." (interactive "DAdd files under directory: ") (let ((dir (expand-file-name directory))) + (when (memq system-type '(windows-nt cygwin)) + (if (eq file-cache-find-command-posix-flag 'not-defined) + (setq file-cache-find-command-posix-flag + (executable-command-find-posix-p file-cache-find-command)))) (set-buffer (get-buffer-create file-cache-buffer)) (erase-buffer) - (call-process file-cache-find-command nil + (call-process file-cache-find-command nil (get-buffer file-cache-buffer) nil - dir "-name" - (if (memq system-type - (list 'windows-nt 'ms-dos)) "'*'" "*") + dir "-name" + (if (memq system-type '(windows-nt cygwin)) + (if file-cache-find-command-posix-flag + "\\*" + "'*'") + "*") "-print") (file-cache-add-from-file-cache-buffer))) @@ -303,17 +361,41 @@ STRING is passed as an argument to the locate command." (interactive "sAdd files using locate string: ") (set-buffer (get-buffer-create file-cache-buffer)) (erase-buffer) - (call-process file-cache-locate-command nil + (call-process file-cache-locate-command nil (get-buffer file-cache-buffer) nil string) (file-cache-add-from-file-cache-buffer)) +(defun file-cache-add-directory-recursively (dir &optional regexp) + "Adds DIR and any subdirectories to the file-cache. +This function does not use any external programs +If the optional REGEXP argument is non-nil, only files which match it +will be added to the cache. Note that the REGEXP is applied to the files +in each directory, not to the directory list itself." + (interactive "DAdd directory: ") + (require 'find-lisp) + (mapcar + (function + (lambda(file) + (or (file-directory-p file) + (let (filtered) + (mapcar + (function + (lambda(regexp) + (and (string-match regexp file) + (setq filtered t)) + )) + file-cache-filter-regexps) + filtered) + (file-cache-add-file file)))) + (find-lisp-find-files dir (if regexp regexp "^")))) + (defun file-cache-add-from-file-cache-buffer (&optional regexp) "Add any entries found in the file cache buffer. Each entry matches the regular expression `file-cache-buffer-default-regexp' or the optional REGEXP argument." (set-buffer file-cache-buffer) - (mapcar + (mapcar (function (lambda (elt) (goto-char (point-min)) (delete-matching-lines elt))) @@ -321,10 +403,10 @@ or the optional REGEXP argument." (goto-char (point-min)) (let ((full-filename)) (while (re-search-forward - (or regexp file-cache-buffer-default-regexp) + (or regexp file-cache-buffer-default-regexp) (point-max) t) (setq full-filename (buffer-substring-no-properties - (match-beginning 0) (match-end 0))) + (match-beginning 0) (match-end 0))) (file-cache-add-file full-filename)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -341,8 +423,9 @@ or the optional REGEXP argument." "Delete FILE from the file cache." (interactive (list (completing-read "Delete file from cache: " file-cache-alist))) - (setq file-cache-alist - (delq (assoc file file-cache-alist) file-cache-alist))) + (setq file-cache-alist + (delq (assoc-string file file-cache-alist file-cache-ignore-case) + file-cache-alist))) (defun file-cache-delete-file-list (file-list) "Delete FILE-LIST (a list of files) from the file cache." @@ -353,26 +436,27 @@ or the optional REGEXP argument." "Delete files matching REGEXP from the file cache." (interactive "sRegexp: ") (let ((delete-list)) - (mapcar '(lambda (elt) + (mapcar '(lambda (elt) (and (string-match regexp (car elt)) (setq delete-list (cons (car elt) delete-list)))) file-cache-alist) (file-cache-delete-file-list delete-list) - (message "Deleted %d files from file cache" (length delete-list)))) + (message "Filecache: deleted %d files from file cache" + (length delete-list)))) (defun file-cache-delete-directory (directory) "Delete DIRECTORY from the file cache." (interactive "DDelete directory from file cache: ") (let ((dir (expand-file-name directory)) (result 0)) - (mapcar - '(lambda (entry) + (mapcar + '(lambda (entry) (if (file-cache-do-delete-directory dir entry) (setq result (1+ result)))) file-cache-alist) (if (zerop result) - (error "No entries containing %s found in cache" directory) - (message "Deleted %d entries" result)))) + (error "Filecache: no entries containing %s found in cache" directory) + (message "Filecache: deleted %d entries" result)))) (defun file-cache-do-delete-directory (dir entry) (let ((directory-list (cdr entry)) @@ -380,7 +464,7 @@ or the optional REGEXP argument." ) (and (member directory directory-list) (if (equal 1 (length directory-list)) - (setq file-cache-alist + (setq file-cache-alist (delq entry file-cache-alist)) (setcdr entry (delete directory directory-list))) ) @@ -397,30 +481,32 @@ or the optional REGEXP argument." ;; Returns the name of a directory for a file in the cache (defun file-cache-directory-name (file) - (let* ((directory-list (cdr (assoc file file-cache-alist))) + (let* ((directory-list (cdr (assoc-string + file file-cache-alist + file-cache-ignore-case))) (len (length directory-list)) (directory) (num) ) (if (not (listp directory-list)) - (error "Unknown type in file-cache-alist for key %s" file)) - (cond + (error "Filecache: unknown type in file-cache-alist for key %s" file)) + (cond ;; Single element ((eq 1 len) (setq directory (elt directory-list 0))) ;; No elements ((eq 0 len) - (error "No directory found for key %s" file)) + (error "Filecache: no directory found for key %s" file)) ;; Multiple elements (t - (let* ((minibuffer-dir (file-name-directory (buffer-string))) + (let* ((minibuffer-dir (file-name-directory (minibuffer-contents))) (dir-list (member minibuffer-dir directory-list)) ) (setq directory ;; If the directory is in the list, return the next element ;; Otherwise, return the first element - (if dir-list - (or (elt directory-list + (if dir-list + (or (elt directory-list (setq num (1+ (- len (length dir-list))))) (elt directory-list (setq num 0))) (elt directory-list (setq num 0)))) @@ -436,7 +522,7 @@ or the optional REGEXP argument." (defun file-cache-file-name (file) (let ((directory (file-cache-directory-name file))) (concat directory file))) - + ;; Return a canonical directory for comparison purposes. ;; Such a directory ends with a forward slash. (defun file-cache-canonical-directory (dir) @@ -451,10 +537,10 @@ or the optional REGEXP argument." ;; The prefix argument works around a bug in the minibuffer completion. ;; The completion function doesn't distinguish between the states: -;; +;; ;; "Multiple completions of name" (eg, Makefile, Makefile.in) ;; "Name available in multiple directories" (/tmp/Makefile, ~me/Makefile) -;; +;; ;; The default is to do the former; a prefix arg forces the latter. ;;;###autoload @@ -462,30 +548,30 @@ or the optional REGEXP argument." "Complete a filename in the minibuffer using a preloaded cache. Filecache does two kinds of substitution: it completes on names in the cache, and, once it has found a unique name, it cycles through -the directories that the name is available in. With a prefix argument, -the name is considered already unique; only the second substitution +the directories that the name is available in. With a prefix argument, +the name is considered already unique; only the second substitution \(directories) is done." - (interactive "P") - (let* + (interactive "P") + (let* ( - (completion-ignore-case nil) - (case-fold-search nil) - (string (file-name-nondirectory (buffer-string))) + (completion-ignore-case file-cache-completion-ignore-case) + (case-fold-search file-cache-case-fold-search) + (string (file-name-nondirectory (minibuffer-contents))) (completion-string (try-completion string file-cache-alist)) (completion-list) (len) (file-cache-string) ) - (cond + (cond ;; If it's the only match, replace the original contents ((or arg (eq completion-string t)) (setq file-cache-string (file-cache-file-name string)) - (if (string= file-cache-string (buffer-string)) + (if (string= file-cache-string (minibuffer-contents)) (file-cache-temp-minibuffer-message file-cache-sole-match-message) - (erase-buffer) - (insert-string file-cache-string) + (delete-minibuffer-contents) + (insert file-cache-string) (if file-cache-multiple-directory-message - (file-cache-temp-minibuffer-message + (file-cache-temp-minibuffer-message file-cache-multiple-directory-message)) )) @@ -494,12 +580,13 @@ the name is considered already unique; only the second substitution ;; If we've already inserted a unique string, see if the user ;; wants to use that one (if (and (string= string completion-string) - (assoc string file-cache-alist)) + (assoc-string string file-cache-alist + file-cache-ignore-case)) (if (and (eq last-command this-command) (string= file-cache-last-completion completion-string)) - (progn - (erase-buffer) - (insert-string (file-cache-file-name completion-string)) + (progn + (delete-minibuffer-contents) + (insert (file-cache-file-name completion-string)) (setq file-cache-last-completion nil) ) (file-cache-temp-minibuffer-message file-cache-non-unique-message) @@ -511,29 +598,29 @@ the name is considered already unique; only the second substitution (if (> len 1) (progn (goto-char (point-max)) - (insert-string + (insert (substring completion-string (length string))) ;; Add our own setup function to the Completions Buffer (let ((completion-setup-hook - (reverse + (reverse (append (list 'file-cache-completion-setup-function) completion-setup-hook))) ) (with-output-to-temp-buffer file-cache-completions-buffer - (display-completion-list completion-list)) + (display-completion-list completion-list string)) ) ) (setq file-cache-string (file-cache-file-name completion-string)) - (if (string= file-cache-string (buffer-string)) - (file-cache-temp-minibuffer-message + (if (string= file-cache-string (minibuffer-contents)) + (file-cache-temp-minibuffer-message file-cache-sole-match-message) - (erase-buffer) - (insert-string file-cache-string) + (delete-minibuffer-contents) + (insert file-cache-string) (if file-cache-multiple-directory-message - (file-cache-temp-minibuffer-message + (file-cache-temp-minibuffer-message file-cache-multiple-directory-message))) ))) - + ;; No match ((eq completion-string nil) (file-cache-temp-minibuffer-message file-cache-no-match-message)) @@ -563,11 +650,11 @@ the name is considered already unique; only the second substitution (if file-cache-completions-keymap nil - (setq file-cache-completions-keymap + (setq file-cache-completions-keymap (copy-keymap completion-list-mode-map)) - (define-key file-cache-completions-keymap [mouse-2] - 'file-cache-mouse-choose-completion) - (define-key file-cache-completions-keymap "\C-m" + (define-key file-cache-completions-keymap [mouse-2] + 'file-cache-mouse-choose-completion) + (define-key file-cache-completions-keymap "\C-m" 'file-cache-choose-completion)) (use-local-map file-cache-completions-keymap) @@ -593,17 +680,95 @@ the name is considered already unique; only the second substitution ) ) +(defun file-cache-complete () + "Complete the word at point, using the filecache." + (interactive) + (let (start pattern completion all) + (save-excursion + (skip-syntax-backward "^\"") + (setq start (point))) + (setq pattern (buffer-substring-no-properties start (point))) + (setq completion (try-completion pattern file-cache-alist)) + (setq all (all-completions pattern file-cache-alist nil)) + (cond ((eq completion t)) + ((null completion) + (message "Can't find completion for \"%s\"" pattern) + (ding)) + ((not (string= pattern completion)) + (delete-region start (point)) + (insert completion) + ) + (t + (with-output-to-temp-buffer "*Completions*" + (display-completion-list all pattern)) + )) + )) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Show parts of the cache +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defun file-cache-files-matching-internal (regexp) + "Output a list of files whose names (not including directories) +match REGEXP." + (let ((results)) + (mapcar + (function + (lambda(cache-element) + (and (string-match regexp + (elt cache-element 0)) + (if results + (nconc results (list (elt cache-element 0))) + (setq results (list (elt cache-element 0))))))) + file-cache-alist) + results)) + +(defun file-cache-files-matching (regexp) + "Output a list of files whose names (not including directories) +match REGEXP." + (interactive "sFind files matching regexp: ") + (let ((results + (file-cache-files-matching-internal regexp)) + buf) + (set-buffer + (setq buf (get-buffer-create + "*File Cache Files Matching*"))) + (erase-buffer) + (insert + (mapconcat + 'identity + results + "\n")) + (goto-char (point-min)) + (display-buffer buf))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Debugging functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun file-cache-debug-read-from-minibuffer (file) "Debugging function." - (interactive + (interactive (list (completing-read "File Cache: " file-cache-alist))) - (message "%s" (assoc file file-cache-alist)) + (message "%s" (assoc-string file file-cache-alist + file-cache-ignore-case)) ) +(defun file-cache-display () + "Display the file cache." + (interactive) + (let ((buf "*File Cache Contents*")) + (with-current-buffer + (get-buffer-create buf) + (erase-buffer) + (mapcar + (function + (lambda(item) + (insert (nth 1 item) (nth 0 item) "\n"))) + file-cache-alist) + (pop-to-buffer buf) + ))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Keybindings ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -614,4 +779,5 @@ the name is considered already unique; only the second substitution (provide 'filecache) +;;; arch-tag: 433d3ca4-4af2-47ce-b2cf-1f727460f538 ;;; filecache.el ends here