2

I am trying to write an interactive command that will search a set of files for a regexp. The files are selected programmatically, for example, as a list of files that match some date criteria, or recently modified criteria and are in a list of paths that has been computed. These paths are likely to be in different directories, and not to match a regular pattern. The command will compute this list, e.g. for files in the last week, or last month, etc.

I want to search this list of files for a regexp. I have been able to get an approach like this to work with grep. Here, assume that the list of files is computed elsewhere

 (grep
  (format "grep -nH -i %s %s" 
      regexp
      (mapconcat 'identity files " "))))

But I wondered if there is a better way to let-bind a variable to a file list, or to use occur/swiper, etc on this list. So far I have only found ways to search files that match a regexp, for example, and not to search a set of files.

Also I am concerned about what would happen if you had hundreds of files and if the length of that command got very long, so it might be better to search in buffers?

Something like swiper-multi would be great, but I can't figure out how to specify the list of buffers/files for it.

4
  • will this do what you need? unix.stackexchange.com/a/178859/6096
    – Tyler
    Jan 27, 2019 at 16:27
  • 1
    There's % g in dired and M-x grep.
    – user12563
    Jan 27, 2019 at 16:44
  • @Tyler that is ok at the command line, but I was hoping for a solution in Emacs. I found a way to use M-x grep as suggested above where you concat all the files into a string and put it in the grep command. It works ok, but I thought there might be a way to do it with moccur, or something. Jan 27, 2019 at 17:34
  • Do you want to do this interactively or just from Lisp? If (also) interactively, do you want the user to choose the files interactively? If so, one-by-one or, e.g. by pasting a Lisp list of file names at a prompt? Please specify the request a bit more. Thx.
    – Drew
    Jan 27, 2019 at 20:25

3 Answers 3

0

After your update to the question, which says you want to supply the list of files by program -- here are some other possibilities.

  1. Do what dired-do-find-regexp does, but just provide the list of files as an arg.
    (defun my-dired-do-find-regexp (regexp files)
      "Find all matches for REGEXP in FILES.
    But skip files matching `grep-find-ignored-files' and file
    in subdirectories matching `grep-find-ignored-directories'.
    REGEXP should use constructs supported by your local `grep' command."
      (interactive "sSearch files (regexp): ")
      (require 'grep)
      (defvar grep-find-ignored-files)
      (defvar grep-find-ignored-directories)
      (let ((ignores (nconc (mapcar (lambda (s) (concat s "/"))
                                    grep-find-ignored-directories)
                            grep-find-ignored-files))
              (xrefs (mapcan (lambda (file)
                               (xref-collect-matches regexp
                                  "*"
                                  file
                                  (and (file-directory-p file)  ignores)))
                             files)))
        (unless xrefs (user-error "No matches for: %s" regexp))
        (xref--show-xrefs xrefs nil t)))
  1. Do what dired-do-search does, but just provide the list of files as an arg.
    (defun dired-do-search (regexp files)
      "Search for REGEXP through FILES.
    Stops when a match is found.
    To continue searching for next match, use command \\[tags-loop-continue]."
      (interactive "sSearch files (regexp): ")
      (tags-search regexp files))
1
  • Thanks, these are great suggestions. I like 1. best as it finds all matches. 2. stops at the first one (as the docstring suggests). Jan 28, 2019 at 0:12
0
  1. As @DoMiNeLa10 suggested in a comment, in Dired % g marks files in the current Dired buffer (which can include subdir listings at any number of levels) whose contents match a regexp.

    Follow that with any command that searches the marked files -- for instance, A, which searches for regexp matches in all marked files.

  2. If you use Dired+ then you can do the same thing, but also descend recursively through all marked subdirectories that have Dired buffers:

    Use M-+ % g, which is like % g but which marks not only files in the current Dired buffer but also those in any Dired buffers for its marked subdirs, and so on, recursively.

    Follow that with any command that searches marked files recursively -- for instance, M-+ A, which searches for regexp matches in all marked files in the current Dired buffer and in its marked subdirs, and so on, recursively.

    (With a prefix arg (non-positive for M-+ % g), such commands descend recursively in all subdirectories, not just those that are marked.)

1
  • Thanks. This is complementary to what I am trying to do, and an relatively unexplored part of Emacs for me. It looks really helpful though! Jan 27, 2019 at 22:47
0

Here is what I finally worked out for something like swiper-multi:

(defun swiper-files (&rest files)
  (let* ((swiper-multi-buffers nil)
     (swiper-multi-candidates nil)
     (this-command 'ivy-done))
    (mapc 'swiper-multi-action-1 (mapcar
                  (lambda (f)
                    (buffer-name (find-file-noselect f)))
                  files))
    (ivy-read "Swiper: " swiper-multi-candidates
              :action #'swiper-multi-action-2
              :unwind #'swiper--cleanup
              :caller 'swiper-multi)))

I use it like this:

(swiper-files "f1.org" "f2.org")

where those two files are computed somehow.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.