1

Scenario: I want to detect all defcustom sexp that have a :set or :initialize keyword in all Emacs Lisp files inside a directory tree.

Searching for newline can be problematic depending of the regexp engine and the tool that uses it. Despite the following, I did not find something that works for several files:

  • [^\0] when back references and look-around are allowed. Unfortunately tools like deadgrep do not support them.
  • C-q C-j is another way but again several tools will not accept multiline regexp.
  • I have been able to use "defcustom [[:ascii:]]+:\(\(type\)\|\(initialize\)\)" with re-builder to validate that this works inside one file. It won't match text that includes non-ASCII characters, but that's probably OK. I guess I could write code that iterate across all files and use that expression or something like that.

I would like to find an Emacs-based tool that would allow me to write a regexp that support identifying text spread across multiple lines and search for that in all files inside a directory tree without having to write my own. Does such an Emacs-based tool exist?

10
  • Does project-find-regexp do the job?
    – aadcg
    Commented Sep 16, 2021 at 18:10
  • Did not know that one. Trying it. Looks like it needs to have the function to use defined. A simple search did not work so I don't have it setup correctly for the moment. Looking into it, thanks...
    – PRouleau
    Commented Sep 16, 2021 at 18:15
  • What do you mean by "needs to have the function to use defined"?
    – aadcg
    Commented Sep 16, 2021 at 18:16
  • 1
    Let us continue this discussion in chat.
    – PRouleau
    Commented Sep 16, 2021 at 18:25
  • 1
    This turns out to be a very interesting question. Eager to see what others have to say!
    – aadcg
    Commented Sep 16, 2021 at 19:23

1 Answer 1

1

I don't think you want to just use a single regexp to search for defcustoms that have either :set or :initialize, with (or without) any of those things possibly being on different lines. Why? Because a single regexp to find either :set or :initialize, or both, in either order, is complicated.

Instead, you want to use Lisp syntax: find (defcustom but search the text between the beginning of that match and the end of the defcustom sexp. Search it for :set, then search it for :initialize, without worrying about which might come first or what lines they might be on. No complicated regexp.

IOW, I'd suggest writing a function (command, if you like), that does something like this, in a loop for the file contents (starting from (point-min), with that loop being within a loop over the files you want to search (e.g. directory-files or whatever):

  1. search-forward for (defcustom.
  2. Go to beginning of the match ((match-beginning 0)), and record that position (e.g. in a let var).
  3. forward-sexp to get to the end of the (defcustom...) sexp, and record that position.
  4. save-restriction and narrow-to-region.
  5. search-forward from (point-min) for :set. If found, accumulate the region bounds as one of your search results, and loop for another search. If not found, go to step 6.
  6. Do step 5, but with :initialize in place of :set.

If you want to allow for whitespace between the ( and the defcustom, then use re-search-forward with an appropriate regexp that allows for it.

8
  • I'd really like to find a way to to a multi-file search of a pattern that spans multiple lines. I gave that example, but it's not the first time I wanted to do something similar. My guess is I will have to use PCRE and a backend tool that can support it. ripgrep and Ag both support PCRE. I used the regexp I described in my question that worked in re-builder and it's not that complicated. But re-builder works only on 1 buffer. I might end up writing a command but for that I'd prefer to use tools like deadgrep, allowing multiple searches, various criteria, etc...
    – PRouleau
    Commented Sep 17, 2021 at 2:07
  • And yes I could take advantage of the fact that I'm searching inside Lisp code, but I'm looking for a solution that would also work when searching for pattern crossing lines in multiple files written in other languages.
    – PRouleau
    Commented Sep 17, 2021 at 2:12
  • You're moving the goal post. Your question was about searching within contexts that are (defcustom...) sexps. I answered that. If you want to search within any kind of context then you can do that with Isearch+. You need to define the contexts, and it gives you multiple ways to do that. Then you can search within them, including across multiple buffers/files, and using any kind of isearching - regexp or otherwise. Icicles Search offers similar contextual searching.
    – Drew
    Commented Sep 17, 2021 at 4:37
  • 1
    You should post a separate question, rather than evolving and widening your question.
    – Drew
    Commented Sep 17, 2021 at 4:38
  • Perhaps., I thought the title and the last paragraph were stating the question, but probably I should not have identified a scenario that strict or added a wider scenario.
    – PRouleau
    Commented Sep 17, 2021 at 10:52

Your Answer

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

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