]> git.eshelyaron.com Git - emacs.git/commitdiff
diff-hunk-kill independent of point inside headers
authorTino Calancha <tino.calancha@gmail.com>
Sat, 21 Jan 2017 02:54:55 +0000 (11:54 +0900)
committerTino Calancha <tino.calancha@gmail.com>
Sat, 21 Jan 2017 02:54:55 +0000 (11:54 +0900)
Make diff-apply-hunk and diff-hunk-kill independent of the point
position in a diff header (Bug#17544).
This change allows to apply hunks in order.  It also makes possible to
press M-k repeatedly to kill hunks in the order they appear in the buffer.
See discussion on #Bug25105.
* lisp/vc/diff-mode.el (diff-file-junk-re):
Move definition before it's used.
(diff--at-diff-header-p): New predicate; return non-nil when point
is inside a hunk header, a file header, or within a line
matching diff-file-junk-re.
(diff-beginning-of-hunk): Use it.
Check if the point is inside a diff header, in the middle of a hunk,
or before the first hunk.
(diff-apply-hunk): Call diff-beginning-of-hunk with non-nil arg
before apply the hunk.
(diff-hunk-kill, diff-file-kill):
Call diff-beginning-of-hunk with non-nil arg after kill the hunks.
(diff-post-command-hook): Call diff-beginning-of-hunk with non-nil argument.

lisp/vc/diff-mode.el

index 4e878c404cd2fe10313c9011c9e6e3f7af67cab2..b7ad8e8ebd8eea709385ffb36a826d2d06b66f45 100644 (file)
@@ -498,22 +498,55 @@ See http://lists.gnu.org/archive/html/emacs-devel/2007-11/msg01990.html")
     ;; The return value is used by easy-mmode-define-navigation.
     (goto-char (or end (point-max)))))
 
+;; "index ", "old mode", "new mode", "new file mode" and
+;; "deleted file mode" are output by git-diff.
+(defconst diff-file-junk-re
+  "diff \\|index \\|\\(?:deleted file\\|new\\(?: file\\)?\\|old\\) mode\\|=== modified file")
+
+;; If point is in a diff header, then return beginning
+;; of hunk position otherwise return nil.
+(defun diff--at-diff-header-p ()
+  "Return non-nil if point is inside a diff header."
+  (let ((regexp-hunk diff-hunk-header-re)
+        (regexp-file diff-file-header-re)
+        (regexp-junk diff-file-junk-re)
+        (orig (point)))
+    (catch 'headerp
+      (save-excursion
+        (forward-line 0)
+        (when (looking-at regexp-hunk) ; Hunk header.
+          (throw 'headerp (point)))
+        (forward-line -1)
+        (when (re-search-forward regexp-file (point-at-eol 4) t) ; File header.
+          (forward-line 0)
+          (throw 'headerp (point)))
+        (goto-char orig)
+        (forward-line 0)
+        (when (looking-at regexp-junk) ; Git diff junk.
+          (while (and (looking-at regexp-junk)
+                      (not (bobp)))
+            (forward-line -1))
+          (re-search-forward regexp-file nil t)
+          (forward-line 0)
+          (throw 'headerp (point)))) nil)))
+
 (defun diff-beginning-of-hunk (&optional try-harder)
   "Move back to the previous hunk beginning, and return its position.
 If point is in a file header rather than a hunk, advance to the
 next hunk if TRY-HARDER is non-nil; otherwise signal an error."
   (beginning-of-line)
-  (if (looking-at diff-hunk-header-re)
+  (if (looking-at diff-hunk-header-re) ; At hunk header.
       (point)
-    (forward-line 1)
-    (condition-case ()
-       (re-search-backward diff-hunk-header-re)
-      (error
-       (unless try-harder
-        (error "Can't find the beginning of the hunk"))
-       (diff-beginning-of-file-and-junk)
-       (diff-hunk-next)
-       (point)))))
+    (let ((pos (diff--at-diff-header-p))
+          (regexp diff-hunk-header-re))
+      (cond (pos ; At junk diff header.
+             (if try-harder
+                 (goto-char pos)
+               (error "Can't find the beginning of the hunk")))
+            ((re-search-backward regexp nil t)) ; In the middle of a hunk.
+            ((re-search-forward regexp nil t) ; At first hunk header.
+             (forward-line 0))
+            (t (error "Can't find the beginning of the hunk"))))))
 
 (defun diff-unified-hunk-p ()
   (save-excursion
@@ -632,12 +665,8 @@ If the prefix ARG is given, restrict the view to the current file instead."
                   hunk-bounds))
         (inhibit-read-only t))
     (apply 'kill-region bounds)
-    (goto-char (car bounds))))
-
-;; "index ", "old mode", "new mode", "new file mode" and
-;; "deleted file mode" are output by git-diff.
-(defconst diff-file-junk-re
-  "diff \\|index \\|\\(?:deleted file\\|new\\(?: file\\)?\\|old\\) mode\\|=== modified file")
+    (goto-char (car bounds))
+    (diff-beginning-of-hunk t)))
 
 (defun diff-beginning-of-file-and-junk ()
   "Go to the beginning of file-related diff-info.
@@ -690,7 +719,8 @@ data such as \"Index: ...\" and such."
   "Kill current file's hunks."
   (interactive)
   (let ((inhibit-read-only t))
-    (apply 'kill-region (diff-bounds-of-file))))
+    (apply 'kill-region (diff-bounds-of-file)))
+  (diff-beginning-of-hunk t))
 
 (defun diff-kill-junk ()
   "Kill spurious empty diffs."
@@ -1274,7 +1304,7 @@ See `after-change-functions' for the meaning of BEG, END and LEN."
        ;; it's safer not to do it on big changes, e.g. when yanking a big
        ;; diff, or when the user edits the header, since we might then
        ;; screw up perfectly correct values.  --Stef
-       (diff-beginning-of-hunk)
+       (diff-beginning-of-hunk t)
         (let* ((style (if (looking-at "\\*\\*\\*") 'context))
                (start (line-beginning-position (if (eq style 'context) 3 2)))
                (mid (if (eq style 'context)
@@ -1738,6 +1768,7 @@ the value of this variable when given an appropriate prefix argument).
 
 With a prefix argument, REVERSE the hunk."
   (interactive "P")
+  (diff-beginning-of-hunk t)
   (pcase-let ((`(,buf ,line-offset ,pos ,old ,new ,switched)
                ;; Sometimes we'd like to have the following behavior: if
                ;; REVERSE go to the new file, otherwise go to the old.