Skip to main content

Projectize commands using numeric arguments

Today it occurred to me that numeric arguments 0 and 1 could be utilized for different purposes. For most commands they are meaningless which allows us to assign them a specific meaning ourselves. Here is one way, which uses numeric prefix 0 to projectize commands:

(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
              (and (not (= 0 (prefix-numeric-value
                              current-prefix-arg)))
                   current-prefix-arg))
             (default-directory
               (or proot default-directory))
             (buffer-file-name
              (or proot buffer-file-name)))
        ;; Update: simplified interactive call
        ;; thanks to /u/SlowValue
        (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)))))


(global-set-key (kbd "C-c f") 'find-name-dired)
(global-set-key (kbd "C-c r") 'rgrep)
(global-set-key (kbd "C-c C-c") 'compile)

(projectize #'find-name-dired #'rgrep #'compile)

Using this you can call C-0 C-c r or C-0 C-c f and will be prompted with the (git) project root (if it exists). This also works when you use C-0 M-x rgrep to call rgrep. The hack above should work for many other commands which prompt you for a directory.