5

Whenever I'm using M-x rgrep to search inside my project arborescence, the interactive prompts suggest the following default arguments for the function:

Search for (default: <current word>) (OK, always what I want)

Search for <current word> in files (default: *.<extension of the current file>) (mostly OK, not always what I want but I can live with that)

Base directory: <current directory> (not OK, I never want it to use the current directory)

I like the first proposed argument, but I'd like to change the second one, and especially the third one, to use other defaults (such as "parent directory" instead of "current directory").

How can I modify rgrep so that (1) either it remains interactive, but proposes different default values, or (2) it becomes non-interactive, but uses the word currently under the cursor as first argument?

  • As always in questions like this, look at the definition of the command, to see what arguments it expects. Then write your own command that provides the arguments you want to the original command, called within your command body as a function. Your function's interactive spec can do anything you like, and it can copy some of its code from the original interactive spec, or otherwise be inspired from it. @Francesco's answer shows you examples of this. – Drew Aug 21 '15 at 15:44
  • I had seen some examples using interactive such as the EmacsWiki RecursiveGrep page, but I had failed to find one that showed how to change the default arguments while still allowing the user to modify them. His example is indeed what I wanted. – anol Aug 21 '15 at 16:15
1

Fully interactive method

One way to do that consists in calling rgrep interactively in a tweaked context so that deduced defaults correspond to what you want. For example, let rgrep believe you called it from a file named "foo.cxx" in the parent directory.

The advantage of this is that you keep the intelligence rgrep puts in default parameters deduction (to pursue the example, you not only have "*.cxx" as a files pattern, but also "*.cc", "*.cpp" & co).

The disadvantage of such a method is that changing buffer-file-name and default-directory could have unintended side effects (but I didn't see any in this example).

(defun my-rgrep ()
  (interactive)
  (let ((buffer-file-name "foo.cxx")
        (default-directory (file-name-directory default-directory)))
    (call-interactively #'rgrep)))

Semi-interactive method

You can also devise a command that interactively allows you to manage the pattern part and non-interactively sets values for other arguments. It then non-interactively calls rgrep:

(defun my-rgrep (&optional confirm)
  (interactive "P")
  (rgrep (grep-read-regexp)
         "*.cxx"
         (file-name-directory default-directory)
         confirm))
0

Here is another approach, it will allow you to call rgrep with a numeric prefix argument of 0 to prompt you with the (git) project directory. This approach can be used for other commands as well. See my corresponding blog post:

(setq lexical-binding t)

(defun projectize-1 (cmd f &rest args)
  (if (eq cmd real-this-command)
      (let* ((proot
              (and (= 0
                      (prefix-numeric-value
                       current-prefix-arg))
                   (locate-dominating-file "." ".git")))
             (current-prefix-arg
              (if proot nil current-prefix-arg))
             (default-directory
               (or proot default-directory)))
        (call-interactively f))
    (apply f args)))


(defun projectize (&rest cmds)
  (dolist (cmd cmds)
    (advice-add cmd :around
                (lambda (f &rest args)
                  (interactive)
                  (apply #'projectize-1 cmd f args)))))


(projectize #'rgrep)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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