Mercurial > hg > xemacs-beta
view lisp/packages/igrep.el @ 24:4103f0995bd7 r19-15b95
Import from CVS: tag r19-15b95
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:51:03 +0200 |
parents | |
children | 34a5b81f86ba |
line wrap: on
line source
;;;; igrep.el --- An improved interface to `grep`. ;;; Description: ;;; ;;; Define the \[igrep] command, which is like \[grep] except that it ;;; takes three required arguments (PROGRAM, EXPRESSION, and FILES) and ;;; an optional argument (OPTIONS) instead of just one argument (COMMAND). ;;; Also define the analogous \[egrep] and \[fgrep] commands for convenience. ;;; ;;; Define the \[igrep-find] command, which is like \[igrep] except that ;;; it uses `find` to recursively `grep` a directory. Also define the ;;; analogous \[egrep-find] and \[fgrep-find] commands for convenience. ;;; ;;; \[igrep] and \[igrep-find] (and their analogues) provide defaults ;;; for the EXPRESSION and FILES arguments when called interactively, ;;; and there are global variables that control the syntax of the `grep` ;;; and `find` shell commands that are executed. A user option controls ;;; whether the corresponding GNU (gzip) "zPROGRAM" script is used, to ;;; `grep` compressed files. ;;; ;;; \[agrep] and \[agrep-find] are also defined as convenient interfaces ;;; to the approximate `grep` utility, which is distributed with the ;;; `glimpse' indexing and query tool (available from ;;; <URL:http://glimpse.cs.arizona.edu:1994/>). ;;; ;;; \[grep] itself is advised to provide the \[igrep] interface when ;;; called interactively (when called programmatically, it still uses ;;; the original argument list). \[grep-find] is defined as an alias ;;; for \[igrep-find]. ;;; ;;; When run interactively from Dired mode, the various \[igrep] ;;; commands provide defaults for the EXPRESSION and FILES arguments ;;; that are based on the visited directory (including any inserted ;;; subdirectories) and the current file. In addition, the ;;; \[dired-do-igrep] and \[dired-do-igrep-find] commands are defined ;;; that respect the `dired-do-*' command calling conventions: a prefix ;;; argument is interpreted as the number of succeeding files to `grep`, ;;; otherwise all the marked files are `grep`ed. \[dired-do-grep] and ;;; \[dired-do-grep-find] are defined as aliases for \[dired-do-igrep] ;;; and \[dired-do-igrep-find]. ;;; Copyright: ;;; ;;; Copyright © 1994,1995,1996,1997 Kevin Rodgers ;;; ;;; This program is free software; you can redistribute it and/or modify ;;; it under the terms of the GNU General Public License as published by ;;; the Free Software Foundation; either version 2 of the License, or ;;; at your option) any later version. ;;; ;;; This program is distributed in the hope that it will be useful, ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;;; GNU General Public License for more details. ;;; ;;; You should have received a copy of the GNU General Public License ;;; along with this program; if not, write to the Free Software ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ;;; ;;; Neither my former nor current employer (Martin Marietta and ;;; Information Handling Services, respectively) has disclaimed any ;;; copyright interest in igrep.el. ;;; ;;; Kevin Rodgers <kevinr@ihs.com> Project Engineer ;;; Information Handling Services Electronic Systems Development ;;; 15 Inverness Way East, M/S A201 ;;; Englewood CO 80112 USA (303)397-2807[voice]/-2779[fax] ;;; Installation: ;;; ;;; 1. Put this file in a directory that is a member of load-path, and ;;; byte-compile it (e.g. with `M-x byte-compile-file') for better ;;; performance. You can ignore any warnings about references to free ;;; variables and "not known to be defined" functions. ;;; 2. Put these forms in default.el or ~/.emacs: ;;; (autoload (function igrep) "igrep" ;;; "*Run `grep` PROGRAM to match EXPRESSION in FILES..." t) ;;; (autoload (function igrep-find) "igrep" ;;; "*Run `grep` via `find`..." t) ;;; (autoload (function dired-do-igrep) "igrep" ;;; "*Run `grep` on the marked (or next prefix ARG) files." t) ;;; (autoload (function dired-do-igrep-find) "igrep" ;;; "*Run `grep` via `find` on the marked (or next prefix ARG) directories." t) ;;; 2. a. For completeness, you can add these forms as well: ;;; (autoload (function grep) "igrep" ;;; "*Run `grep` PROGRAM to match EXPRESSION in FILES..." t) ;;; (autoload (function egrep) "igrep" ;;; "*Run `egrep`..." t) ;;; (autoload (function fgrep) "igrep" ;;; "*Run `fgrep`..." t) ;;; (autoload (function agrep) "igrep" ;;; "*Run `agrep`..." t) ;;; (autoload (function grep-find) "igrep" ;;; "*Run `grep` via `find`..." t) ;;; (autoload (function egrep-find) "igrep" ;;; "*Run `egrep` via `find`..." t) ;;; (autoload (function fgrep-find) "igrep" ;;; "*Run `fgrep` via `find`..." t) ;;; (autoload (function agrep-find) "igrep" ;;; "*Run `agrep` via `find`..." t) ;;; (autoload (function dired-do-grep) "igrep" ;;; "*Run `grep` on the marked (or next prefix ARG) files." t) ;;; (autoload (function dired-do-grep-find) "igrep" ;;; "*Run `grep` via `find` on the marked (or next prefix ARG) directories." t) ;;; 3. If you are running Windows 95/NT, you should install findutils ;;; and grep from release 17.1 (or higher) of the Cygnus GNU-Win32 ;;; distribution. See <URL:http://www.cygnus.com/misc/gnu-win32/>. ;;; Usage: ;;; ;;; M-x igrep M-x igrep-find ;;; M-x grep M-x grep-find ;;; M-x egrep M-x egrep-find ;;; M-x fgrep M-x fgrep-find ;;; M-x agrep M-x agrep-find ;;; M-x dired-do-igrep M-x dired-do-igrep-find ;;; M-x dired-do-grep M-x dired-do-grep-find ;;; (Each of the 10 igrep commands accepts 1, 2, or 3 `C-u' prefix arguments. ;;; The 2 Dired commands interpret a prefix argument like the regular `dired-do' ;;; commands.) ;;; C-x ` (M-x next-error) C-c C-c (M-x compile-goto-error) [in *igrep*] ;;; Customization examples: ;;; ;;; To ignore case by default: ;;; (setq igrep-options "-i") ;;; To search subdirectories by default: ;;; (setq igrep-find t) ;;; To search files with the GNU (gzip) zgrep script: ;;; (setq igrep-use-zgrep t) ;;; or define new igrep commands (this works for zegrep and zfgrep as well): ;;; (igrep-define zgrep) ; M-x zgrep ;;; (igrep-find-define zgrep) ; M-x zgrep-find ;;; To avoid exceeding some shells' limit on command argument length ;;; (this only works when grep'ing files in the current directory): ;;; (setq igrep-find t ;;; igrep-find-prune-option "\\! -name .") ;;; To do: ;;; ;;; 1. Delete the *-program variables (except for igrep-program) and ;;; replace igrep-options with a table that maps igrep-program to the ;;; appropriate options. ;;; 2. Generalize support for the -prune find clause (e.g. -fstype nfs). ;;; 3. Provide support for `glimpse`. ;;; 4. Add a menu interface. ;;; LCD Archive Entry: ;;; ;;; igrep|Kevin Rodgers|kevinr@ihs.com| ;;; An improved interface to grep/egrep/fgrep; plus recursive `find` versions.| ;;; 97/02/10|2.56|~/misc/igrep.el.Z| ;;; Package interface: (provide 'igrep) (require 'backquote) ; igrep-with-default-in-history (require 'compile) ; compile-internal and grep-regexp- ; alist (defconst igrep-version "2.56" "Version of igrep.el") ;;; User options: (defvar igrep-options nil "*The options passed by \\[igrep] to `igrep-program', or nil. `-n' will automatically be passed to `igrep-program', to generate the output expected by \\[next-error] and \\[compile-goto-error]. `-e' will automatically be passed to `igrep-program', if it supports that option.") (defvar igrep-read-options nil "*If non-nil, `\\[igrep]' always prompts for options; otherwise, it only prompts when 1 or 3 `C-u's are given as a prefix arg.") (defvar igrep-read-multiple-files nil "*If non-nil, `\\[igrep]' always prompts for multiple-files; otherwise, it only prompts when 2 or 3 `C-u's are given as a prefix arg.") (defvar igrep-verbose-prompts t "*If t, \\[igrep] prompts for arguments verbosely; if not t but non-nil, \\[igrep] prompts for arguments semi-verbosely; if nil, \\[igrep] prompts for arguments tersely.") (defvar igrep-save-buffers 'query "*If t, \\[igrep] first saves each modified file buffer; if not t but non-nil, \\[igrep] offers to save each modified file buffer.") (defvar igrep-program-table ; referenced by igrep-use-zgrep (let ((exec-directories exec-path) (program-obarray (make-vector 11 0))) (while exec-directories (if (and (car exec-directories) (file-directory-p (car exec-directories))) (let ((grep-programs (directory-files (car exec-directories) nil "grep\\(\\.exe\\)?\\'"))) (while grep-programs ;; Check `(file-executable-p (car grep-programs))'? (if (save-match-data (string-match "\\.exe\\'" (car grep-programs))) (intern (substring (car grep-programs) 0 -4) program-obarray) (intern (car grep-programs) program-obarray)) (setq grep-programs (cdr grep-programs))))) (setq exec-directories (cdr exec-directories))) program-obarray) "An obarray of available `grep` programs, passed by `igrep-read-program' to `completing-read' when `igrep-program' is nil.") (defvar igrep-use-zgrep (if (intern-soft "zgrep" igrep-program-table) 'files) "If t, \\[igrep] searches files using the GNU (gzip) `zPROGRAM` script; If not t but non-nil, \\[igrep] searches compressed FILES using `zPROGRAM`; if nil, \\[igrep] searches files with `PROGRAM`.") ;;; User variables: (defvar igrep-program "grep" "The default shell program run by \\[igrep] and \\[igrep-find]. It must take a `grep` expression argument and one or more file names. If nil, \\[igrep] prompts for the program to run.") (defvar igrep-expression-quote-char (if (memq system-type '(ms-dos windows-95 windows-nt emx)) ?\" ?') "The character used to delimit the EXPRESSION argument to \\[igrep], to protect it from shell filename expansion.") (defvar igrep-expression-option (if (or (eq system-type 'berkeley-unix) (save-match-data (string-match "-sco" system-configuration))) "-e") "If non-nil, the option used to specify the EXPRESSION argument to \\[igrep], to protect an initial `-' from option processing.") (defvar igrep-parenthesis-escape-char (if (memq system-type '(ms-dos windows-95 windows-nt emx)) nil ?\\) "If non-nil, the character used by \\[igrep] to escape parentheses, to protect them from shell interpretation.") (defvar igrep-find nil "If non-nil, \\[igrep] searches directories using `find`. See \\[igrep-find].") (defvar igrep-find-prune-options (if (not (save-match-data (string-match "-sco" system-configuration))) "-name SCCS -o -name RCS") "The `find` clause used to prune directories, or nil; see \\[igrep-find].") (defvar igrep-find-file-options "-type f -o -type l" "The `find` clause used to filter files passed to `grep`, or nil; see \\[igrep-find].") (defvar igrep-find-use-xargs (if (equal (call-process "find" nil nil nil (if (boundp 'grep-null-device) grep-null-device "/dev/null") "-print0") 0) 'gnu) "If `gnu', \\[igrep-find] executes `find ... -print0 | xargs -0 -e grep ...`; if not `gnu' but non-nil, \\[igrep-find] executes `find ... -print | xargs -e grep ...`; if nil, \\[igrep-find] executes `find ... -exec grep ...`.") (defvar igrep-program-default nil "The default `grep` program, passed by `igrep-read-program' to `completing-read' when `igrep-program' is nil.") (defvar igrep-expression-history '() "The minibuffer history list for \\[igrep]'s EXPRESSION argument.") (defvar igrep-files-history '() "The minibuffer history list for \\[igrep]'s FILES argument.") ;;; Commands: ;; ;;;###autoload Not ready to replace compile.el's grep yet. -sb (defadvice grep (around igrep-interface first (&rest grep-args) activate) "If called interactively, use the \\[igrep] interface instead, where GREP-ARGS is (PROGRAM EXPRESSION FILES OPTIONS); if called programmatically, GREP-ARGS is still (COMMAND)." (interactive (igrep-read-args)) (if (interactive-p) (apply (function igrep) grep-args) ad-do-it)) (defalias 'grep-find 'igrep-find) ;;;###autoload (defun igrep (program expression files &optional options) "*Run `grep` PROGRAM to match EXPRESSION in FILES. The output is displayed in the *igrep* buffer, which \\[next-error] and \\[compile-goto-error] parse to find each line of matched text. PROGRAM may be nil, in which case it defaults to `igrep-program'. EXPRESSION is automatically delimited by `igrep-expression-quote-char'. FILES is either a file name pattern (expanded by the shell named by `shell-file-name') or a list of file name patterns. Optional OPTIONS is also passed to PROGRAM; it defaults to `igrep-options'. If a prefix argument \ \(\\[universal-argument]\) \ is given when called interactively, or if `igrep-read-options' is set, OPTIONS is read from the minibuffer. If two prefix arguments \ \(\\[universal-argument] \\[universal-argument]\) \ are given when called interactively, or if `igrep-read-multiple-files' is set, FILES is read from the minibuffer multiple times. If three prefix arguments \ \(\\[universal-argument] \\[universal-argument] \\[universal-argument]\) \ are given when called interactively, or if `igrep-read-options' and `igrep-read-multiple-files' are set, OPTIONS is read and FILES is read multiple times. If `igrep-find' is non-nil, the directory or directories containing FILES is recursively searched for files whose name matches the file name component of FILES \(and whose contents match EXPRESSION\)." (interactive (igrep-read-args)) (if (null program) (setq program (or igrep-program "grep"))) (if (null options) (setq options igrep-options)) (if (not (listp files)) ; (stringp files) (setq files (list files))) (if (string-match "^[rj]?sh$" (file-name-nondirectory shell-file-name)) ;; (restricted, job-control, or standard) Bourne shell doesn't expand ~: (setq files (mapcar 'expand-file-name files))) (let* ((win32-quote-process-args nil) ; work around NT Emacs hack (use-zgrep (cond ((eq igrep-use-zgrep t)) (igrep-use-zgrep (let ((files files) (compressed-p nil)) (while (and files (not compressed-p)) (if (save-match-data (string-match "\\.g?[zZ]\\'" (car files))) (setq compressed-p t)) (setq files (cdr files))) compressed-p)) (t nil))) (command (format "%s -n %s %s %c%s%c %s %s" (if (and use-zgrep (save-match-data (not (string-match "\\`z" program)))) (setq program (concat "z" program)) program) (or options "") (or igrep-expression-option (progn (if (save-match-data (string-match "\\`-" expression)) (setq expression (concat "\\" expression))) "")) igrep-expression-quote-char expression igrep-expression-quote-char (if igrep-find (if igrep-find-use-xargs "" "\"{}\"") (mapconcat (function identity) files " ")) (if (boundp 'grep-null-device) grep-null-device "/dev/null")))) (if igrep-find (setq command (igrep-format-find-command command files))) (cond ((eq igrep-save-buffers t) (save-some-buffers t)) (igrep-save-buffers (save-some-buffers))) (compile-internal command (format "No more %c%s%c matches" igrep-expression-quote-char program igrep-expression-quote-char) "igrep" nil grep-regexp-alist))) ;; Analogue commands: ;;;###autoload (defmacro igrep-define (analogue-command &rest igrep-bindings) "Define ANALOGUE-COMMAND as an `igrep' analogue command. Optional (VARIABLE VALUE) arguments specify temporary bindings for the command." ;;; (interactive "SCommand: ") ; C-u => read bindings? (let ((analogue-program (symbol-name analogue-command))) (` (defun (, analogue-command) (&rest igrep-args) (, (format "*Run `%s` via \\[igrep]. All arguments \(including prefix arguments, when called interactively\) are handled by `igrep'." analogue-program)) (interactive (let ((igrep-program (if igrep-program (, analogue-program))) (igrep-program-default (, analogue-program))) (igrep-read-args))) (let ( (,@ igrep-bindings)) (apply (function igrep) (cond ((interactive-p) (car igrep-args)) ((car igrep-args)) (t (, analogue-program))) (cdr igrep-args))))))) (igrep-define egrep) (igrep-define fgrep) (igrep-define agrep (igrep-use-zgrep nil) (igrep-expression-option "-e")) ;; Recursive (`find`) commands: ;;;###autoload (defun igrep-find (&rest igrep-args) "*Run `grep` via `find`; see \\[igrep] and `igrep-find'. All arguments \(including prefix arguments, when called interactively\) are handled by `igrep'." (interactive (let ((igrep-find t)) (igrep-read-args))) (let ((igrep-find t)) (apply (function igrep) igrep-args))) ;; Analogue recursive (`find`) commands: ;;;###autoload (defmacro igrep-find-define (analogue-command &rest igrep-bindings) "Define ANALOGUE-COMMAND-find as an `igrep' analogue `find` command. Optional (VARIABLE VALUE) arguments specify temporary bindings for the command." ;;; (interactive "SCommand: ") ; C-u => read bindings? (let ((analogue-program (symbol-name analogue-command))) (setq analogue-command (intern (format "%s-find" analogue-command))) (` (defun (, analogue-command) (&rest igrep-args) (, (format "*Run `%s` via \\[igrep-find]. All arguments \(including prefix arguments, when called interactively\) are handled by `igrep'." analogue-program)) (interactive (let ((igrep-program (if igrep-program (, analogue-program))) (igrep-program-default (, analogue-program)) (igrep-find t)) (igrep-read-args))) (let ( (,@ igrep-bindings)) (apply (function igrep-find) (cond ((interactive-p) (car igrep-args)) ((car igrep-args)) (t (, analogue-program))) (cdr igrep-args))))))) (igrep-find-define egrep) (igrep-find-define fgrep) (igrep-find-define agrep (igrep-use-zgrep nil) (igrep-expression-option "-e")) ;; Dired commands: ;;;###autoload (defun dired-do-igrep (program expression &optional options arg) "*Run `grep` PROGRAM to match EXPRESSION (with optional OPTIONS) on the marked (or next prefix ARG) files." (interactive (let* ((current-prefix-arg nil) (igrep-args (igrep-read-args t))) ;; Delete FILES: (setcdr (nthcdr 1 igrep-args) (nthcdr 3 igrep-args)) ;; Append ARG: (nconc igrep-args (list current-prefix-arg)))) (igrep program expression (funcall (cond ((fboundp 'dired-get-marked-files) ; GNU Emacs 'dired-get-marked-files) ((fboundp 'dired-mark-get-files) ; XEmacs 'dired-mark-get-files)) t arg) options)) ;;;###autoload (defalias 'dired-do-grep 'dired-do-igrep) ;; Dired recursive (`find`) commands: ;;;###autoload (defun dired-do-igrep-find (program expression &optional options arg) "*Run `grep` PROGRAM to match EXPRESSION (with optional OPTIONS) on the marked (or next prefix ARG) directories." (interactive (let* ((current-prefix-arg nil) (igrep-find t) (igrep-args (igrep-read-args t))) ;; Delete FILES: (setcdr (nthcdr 1 igrep-args) (nthcdr 3 igrep-args)) ;; Append ARG: (nconc igrep-args (list current-prefix-arg)))) (let ((igrep-find t)) (dired-do-igrep program expression options arg))) ;;;###autoload (defalias 'dired-do-grep-find 'dired-do-igrep-find) ;;; Utilities: (defsubst igrep-file-directory (name) ;; Return the directory component of NAME, or "." if it has no ;; directory component. (directory-file-name (or (file-name-directory name) (file-name-as-directory ".")))) (defsubst igrep-file-pattern (name) ;; Return the file component of NAME, or "*" if it has no file ;; component. (let ((pattern (file-name-nondirectory name))) (if (string= pattern "") "*" pattern))) (defun igrep-format-find-command (command files) ;; Format `grep` COMMAND to be invoked via `find` on FILES. (let ((directories '()) (patterns '())) (while files (let ((dir (igrep-file-directory (car files))) (pat (igrep-file-pattern (car files)))) (if (and (not (string= dir ".")) (file-symlink-p dir)) (setq dir (concat dir "/."))) (if (not (member dir directories)) (setq directories (cons dir directories))) (cond ((equal pat "*") (setq patterns t)) ((and (listp patterns) (not (member pat patterns))) (setq patterns (cons pat patterns))))) (setq files (cdr files))) (format (cond ((eq igrep-find-use-xargs 'gnu) ;; | \\\n "find %s %s %s %s -print0 | xargs -0 -e %s") (igrep-find-use-xargs ;; | \\\n "find %s %s %s %s -print | xargs -e %s") ;;; ((memq system-type '(ms-dos windows-95 windows-nt emx)) ;;; "find %s %s %s %s -exec %s ;") (t "find %s %s %s %s -exec %s \\;")) (mapconcat (function identity) (nreverse directories) " ") (if igrep-find-prune-options (format "-type d %c( %s %c) -prune -o" (or igrep-parenthesis-escape-char ? ) igrep-find-prune-options (or igrep-parenthesis-escape-char ? )) "") (if igrep-find-file-options (format "%c( %s %c)" (or igrep-parenthesis-escape-char ? ) igrep-find-file-options (or igrep-parenthesis-escape-char ? )) "") (if (listp patterns) (if (cdr patterns) ; (> (length patterns) 1) (format "%c( %s %c)" (or igrep-parenthesis-escape-char " ") (mapconcat (function (lambda (pat) (format "-name \"%s\"" pat))) (nreverse patterns) " -o ") (or igrep-parenthesis-escape-char " ")) (format "-name \"%s\"" (car patterns))) "") command))) (defun igrep-read-args (&optional no-files) ;; Read and return a list: (PROGRAM EXPRESSION FILES OPTIONS). ;; If NO-FILES is non-nil, then FILES is not read and nil is returned ;; in its place. (let* ((program (igrep-read-program (if igrep-verbose-prompts (if igrep-find "[find] ")))) (prompt-prefix (if igrep-verbose-prompts (apply (function concat) (if igrep-find "[find] ") (if (eq igrep-verbose-prompts t) (list program " "))))) (options (igrep-read-options prompt-prefix))) (if (eq igrep-verbose-prompts t) (setq prompt-prefix (concat prompt-prefix options " "))) (list program (igrep-read-expression prompt-prefix) (if (not no-files) (igrep-read-files prompt-prefix)) options))) (defsubst igrep-prefix (prefix string) ;; If PREFIX is non-nil, concatenate it and STRING; otherwise, return STRING. (if prefix (concat prefix string) string)) (defun igrep-read-program (&optional prompt-prefix) ;; If igrep-program is nil, read and return a program name from the ;; minibuffer; otherwise, return igrep-program. ;; Optional PROMPT-PREFIX is prepended to the "Program: " prompt. (or igrep-program (let ((prompt "Program: ")) (completing-read (igrep-prefix prompt-prefix prompt) igrep-program-table nil t (or igrep-program-default "grep"))))) (defun igrep-read-options (&optional prompt-prefix) ;; If current-prefix-arg is '(4) or '(64), read and return an options ;; string from the minibuffer; otherwise, return igrep-options. ;; Optional PROMPT-PREFIX is prepended to the "Options: " prompt. (if (or igrep-read-options (and (consp current-prefix-arg) (memq (prefix-numeric-value current-prefix-arg) '(4 64)))) (let ((prompt "Options: ")) (read-string (igrep-prefix prompt-prefix prompt) (or igrep-options "-"))) igrep-options)) (defun igrep-read-expression (&optional prompt-prefix) ;; Read and return a `grep` expression string from the minibuffer. ;; Optional PROMPT-PREFIX is prepended to the "Expression: " prompt. (let ((default-expression (igrep-default-expression))) (if (string= default-expression "") (read-from-minibuffer (igrep-prefix prompt-prefix "Expression: ") nil nil nil 'igrep-expression-history) (let ((expression (igrep-read-string-with-default-in-history (igrep-prefix prompt-prefix (format "Expression (default %s): " default-expression)) default-expression 'igrep-expression-history))) (if (string= expression "") default-expression expression))))) (defun igrep-default-expression () (if (eq major-mode 'dired-mode) (let ((dired-file (dired-get-filename nil t))) (save-excursion (set-buffer (or (and dired-file (get-file-buffer dired-file)) (other-buffer (current-buffer) t))) (current-word))) (current-word))) (defsubst igrep-default-key () ;; Return the key bound to `exit-minibuffer', preferably "\r". (if (eq (lookup-key minibuffer-local-completion-map "\r") (function exit-minibuffer)) "\r" (where-is-internal (function exit-minibuffer) minibuffer-local-completion-map t))) (defun igrep-read-files (&optional prompt-prefix) ;; Read and return a file name pattern from the minibuffer. If ;; current-prefix-arg is '(16) or '(64), read multiple file name ;; patterns and return them in a list. Optional PROMPT-PREFIX is ;; prepended to the "File(s): " prompt. (let* ((dired-subdirectory (if (eq major-mode 'dired-mode) (dired-current-directory t))) (default-files (concat dired-subdirectory (igrep-default-file-pattern))) (prompt (format "File(s) (default %s): " default-files)) (insert-default-directory nil) ; use relative path names (file (igrep-read-file-name-with-default-in-history (igrep-prefix prompt-prefix prompt) default-files nil 'igrep-files-history))) (if (or igrep-read-multiple-files (and (consp current-prefix-arg) (memq (prefix-numeric-value current-prefix-arg) '(16 64)))) (let ((files (list file))) (setq prompt (igrep-prefix prompt-prefix (if igrep-verbose-prompts (format "File(s): [Type `%s' when done] " (key-description (igrep-default-key))) "File(s): "))) (while (not (string= (setq file (igrep-read-file-name prompt nil "" nil nil 'igrep-files-history)) "")) (setq files (cons file files))) (nreverse files)) file))) (defmacro igrep-with-default-in-history (default history &rest body) ;; Temporarily append DEFAULT to HISTORY, and execute BODY forms. (` (progn ;; Append default to history: (set history (cons (, default) (if (boundp (, history)) (symbol-value (, history)) '()))) (unwind-protect ; Make sure the history is restored. ;; Execute body: (progn (,@ body)) ;; Delete default from history (undo the append above): (setcdr (symbol-value (, history)) (nthcdr 2 (symbol-value (, history)))))))) (defun igrep-read-string-with-default-in-history (prompt default history) ;; Read a string from the minibuffer, prompting with string PROMPT. ;; DEFAULT can be inserted into the minibuffer with `previous- ;; history-element'; HISTORY is a symbol whose value (if bound) is a ;; list of previous results, most recent first. (let ((string (igrep-with-default-in-history default history (read-from-minibuffer prompt nil nil nil history)))) ;; Replace empty string in history with default: (if (string= string "") (setcar (symbol-value history) default)) string)) (defun igrep-read-file-name-with-default-in-history (prompt &optional default initial history) ;; Read a file name from the minibuffer, prompting with string PROMPT. ;; DEFAULT can be inserted into the minibuffer with `previous- ;; history-element'; HISTORY is a symbol whose value (if any) is a ;; list of previous results, most recent first. (igrep-with-default-in-history default history (igrep-read-file-name prompt nil default nil initial history))) (defun igrep-read-file-name (prompt &optional directory default existing initial history) ;; Just like read-file-name, but with optional HISTORY. ;; Also: convert DIRECTORY to DIRECTORY/* file name pattern. (let ((file-name (if history (let ((file-name-history (symbol-value history))) (prog1 (read-file-name prompt directory default existing initial) (set history file-name-history))) (read-file-name prompt directory default existing initial)))) (if (and (not (string-equal file-name "")) (file-directory-p file-name)) (expand-file-name "*" file-name) file-name))) (defun igrep-default-file-pattern () ;; Return a shell file name pattern that matches files with the same ;; extension as the file being visited in the current buffer. ;; (Based on other-possibly-interesting-files in ~/as-is/unix.el, by ;; Wolfgang Rupprecht <wolfgang@mgm.mit.edu>.) (if (eq major-mode 'dired-mode) (cond ((stringp dired-directory) (if (file-directory-p dired-directory) "*" (file-name-nondirectory dired-directory))) ; wildcard ((consp dired-directory) ; (DIR FILE ...) (mapconcat 'identity (cdr dired-directory) " "))) (if buffer-file-name (let ((file-name (file-name-nondirectory buffer-file-name))) (concat "*" (save-match-data (if (string-match "\\.[^.]+\\(\\.g?[zZ]\\)?$" file-name) (substring file-name (match-beginning 0) (match-end 0)))))) "*"))) ;;; Local Variables: ;;; eval: (put 'igrep-with-default-in-history 'lisp-indent-hook 2) ;;; eval: (put 'igrep-define 'lisp-indent-hook 1) ;;; eval: (put 'igrep-find-define 'lisp-indent-hook 1) ;;; End: ;;;; igrep.el ends here