Let users help diff-mode find files to patch

View: New views
1 Messages — Rating Filter:   Alert me  

Let users help diff-mode find files to patch

by Jim Blandy-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

When using Mercurial Queues, if some patch named
ROOT/.hg/patches/fix-blurgh.patch contains hunks for some file
a/urgh/blurgh.cpp, then those hunks always apply to
ROOT/urgh/blurgh.cpp.

However, Emacs's diff-mode isn't smart enough to figure that out,
meaning that commands like `diff-goto-source' or `diff-apply-hunk'
prompt for the filename in an annoying way.

The attached patch adds a -function to diff-mode that users can
customize to tell diff-mode how to do the right thing.

[diff-find-file-name-functions.patch]

diff --git a/diff-mode.el b/diff-mode.el
--- a/diff-mode.el
+++ b/diff-mode.el
@@ -754,16 +754,29 @@ If the OLD prefix arg is passed, tell th
        (list (match-string 1)))
      header-files
      (when (re-search-backward
     "^diff \\(-\\S-+ +\\)*\\(\\S-+\\)\\( +\\(\\S-+\\)\\)?"
     nil t)
        (list (if old (match-string 2) (match-string 4))
      (if old (match-string 4) (match-string 2)))))))))
 
+(defvar diff-find-file-name-functions ()
+  "A list of functions to call to find the file to which a hunk applies.
+Each function in this list should take one argument, FILENAMES, a
+list of possible filenames extracted from the patch's file
+header. If the function can determine what real file the patch
+should be applied to, the function should return that real file's
+name. Otherwise, it should return nil.
+
+diff-mode's commands use a number of strategies to try find the
+file to which a patch applies. If none succeed, each of the
+functions in this list are tried in turn; if they all return nil,
+diff-mode prompts the user for the filename.")
+
 (defun diff-find-file-name (&optional old noprompt prefix)
   "Return the file corresponding to the current patch.
 Non-nil OLD means that we want the old file.
 Non-nil NOPROMPT means to prefer returning nil than to prompt the user.
 PREFIX is only used internally: don't use it."
   (unless (equal diff-remembered-defdir default-directory)
     ;; Flush diff-remembered-files-alist if the default-directory is changed.
     (set (make-local-variable 'diff-remembered-defdir) default-directory)
@@ -799,16 +812,19 @@ PREFIX is only used internally: don't us
       (when (file-exists-p file) file)))
        ;; If we haven't found the file, maybe it's because we haven't paid
        ;; attention to the PCL-CVS hint.
        (and (not prefix)
     (boundp 'cvs-pcl-cvs-dirchange-re)
     (save-excursion
       (re-search-backward cvs-pcl-cvs-dirchange-re nil t))
     (diff-find-file-name old noprompt (match-string 1)))
+       ;; If the user has provided their own functions for finding the
+       ;; name of the patch's target, call them.
+       (run-hook-with-args-until-success 'diff-find-file-name-functions fs)
        ;; if all else fails, ask the user
        (unless noprompt
          (let ((file (read-file-name (format "Use file %s: "
                                              (or (first fs) ""))
                                      nil (first fs) t (first fs))))
            (set (make-local-variable 'diff-remembered-files-alist)
                 (cons (cons fs file) diff-remembered-files-alist))
            file))))))